2021-06-07 21:58:05 +00:00
|
|
|
#![warn(clippy::pedantic)]
|
|
|
|
//#![warn(clippy::nursery)]
|
2021-10-17 04:28:39 +00:00
|
|
|
use crate::comp::skillset::{skills::Skill, SkillGroupKind, SkillSet};
|
2021-06-07 21:58:05 +00:00
|
|
|
|
|
|
|
use crate::assets::{self, AssetExt};
|
2021-03-21 05:53:39 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
2021-01-07 19:47:29 +00:00
|
|
|
use tracing::warn;
|
|
|
|
|
2021-06-14 10:55:25 +00:00
|
|
|
/// `SkillSetBuilder` preset. Consider using loading from assets, when possible.
|
|
|
|
/// When you're adding new enum variant,
|
|
|
|
/// handle it in [`with_preset`](SkillSetBuilder::with_preset) method
|
2021-03-21 05:53:39 +00:00
|
|
|
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, Debug)]
|
2021-06-14 10:55:25 +00:00
|
|
|
pub enum Preset {}
|
2021-06-07 21:58:05 +00:00
|
|
|
|
|
|
|
#[derive(Debug, Deserialize, Clone)]
|
|
|
|
struct SkillSetTree(Vec<SkillNode>);
|
|
|
|
impl assets::Asset for SkillSetTree {
|
|
|
|
type Loader = assets::RonLoader;
|
|
|
|
|
|
|
|
const EXTENSION: &'static str = "ron";
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize, Clone)]
|
|
|
|
enum SkillNode {
|
|
|
|
Tree(String),
|
|
|
|
Skill((Skill, Option<u16>)),
|
|
|
|
Group(SkillGroupKind),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[must_use]
|
|
|
|
fn skills_from_asset_expect(asset_specifier: &str) -> Vec<(Skill, Option<u16>)> {
|
2021-06-25 16:47:03 +00:00
|
|
|
let nodes = SkillSetTree::load_expect(asset_specifier).read();
|
2021-06-07 21:58:05 +00:00
|
|
|
|
2021-06-25 16:47:03 +00:00
|
|
|
skills_from_nodes(&nodes.0)
|
2021-06-07 21:58:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[must_use]
|
2021-06-25 16:47:03 +00:00
|
|
|
fn skills_from_nodes(nodes: &[SkillNode]) -> Vec<(Skill, Option<u16>)> {
|
2021-06-07 21:58:05 +00:00
|
|
|
let mut skills = Vec::new();
|
|
|
|
for node in nodes {
|
|
|
|
match node {
|
|
|
|
SkillNode::Tree(asset) => {
|
2021-07-05 15:43:30 +00:00
|
|
|
skills.append(&mut skills_from_asset_expect(asset));
|
2021-06-07 21:58:05 +00:00
|
|
|
},
|
|
|
|
SkillNode::Skill(req) => {
|
2021-06-25 16:47:03 +00:00
|
|
|
skills.push(*req);
|
2021-06-07 21:58:05 +00:00
|
|
|
},
|
|
|
|
SkillNode::Group(group) => {
|
2021-06-25 16:47:03 +00:00
|
|
|
skills.push((Skill::UnlockGroup(*group), None));
|
2021-06-07 21:58:05 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
skills
|
2021-01-07 19:47:29 +00:00
|
|
|
}
|
|
|
|
|
2021-09-24 21:11:05 +00:00
|
|
|
#[derive(Default)]
|
2021-01-07 19:47:29 +00:00
|
|
|
pub struct SkillSetBuilder(SkillSet);
|
|
|
|
|
|
|
|
impl SkillSetBuilder {
|
2021-06-07 21:58:05 +00:00
|
|
|
/// Creates `SkillSetBuilder` from `asset_specifier`
|
|
|
|
#[must_use]
|
|
|
|
pub fn from_asset_expect(asset_specifier: &str) -> Self {
|
|
|
|
let builder = Self::default();
|
2021-01-08 20:53:52 +00:00
|
|
|
|
2021-06-07 21:58:05 +00:00
|
|
|
builder.with_asset_expect(asset_specifier)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Applies `asset_specifier` with needed skill tree
|
|
|
|
#[must_use]
|
|
|
|
pub fn with_asset_expect(mut self, asset_specifier: &str) -> Self {
|
|
|
|
let tree = skills_from_asset_expect(asset_specifier);
|
|
|
|
for (skill, level) in tree {
|
|
|
|
self = self.with_skill(skill, level);
|
|
|
|
}
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Creates `SkillSetBuilder` for given preset
|
|
|
|
#[must_use]
|
|
|
|
pub fn from_preset(preset: Preset) -> Self {
|
|
|
|
let builder = Self::default();
|
|
|
|
|
|
|
|
builder.with_preset(preset)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Applies preset
|
|
|
|
#[must_use]
|
2021-06-14 10:55:25 +00:00
|
|
|
pub const fn with_preset(self, _preset: Preset) -> Self { self }
|
2021-01-07 19:47:29 +00:00
|
|
|
|
2021-06-07 11:38:08 +00:00
|
|
|
#[must_use]
|
|
|
|
/// # Panics
|
|
|
|
/// will panic only in tests
|
|
|
|
/// 1) If added skill doesn't have any group
|
|
|
|
/// 2) If added skill already applied
|
|
|
|
/// 3) If added skill wasn't applied at the end
|
2021-01-21 19:52:07 +00:00
|
|
|
pub fn with_skill(mut self, skill: Skill, level: Option<u16>) -> Self {
|
2021-06-07 11:38:08 +00:00
|
|
|
let group = if let Some(skill_group) = skill.skill_group_kind() {
|
|
|
|
skill_group
|
2021-01-10 01:05:13 +00:00
|
|
|
} else {
|
2021-06-07 11:38:08 +00:00
|
|
|
let err = format!(
|
2021-01-10 01:05:13 +00:00
|
|
|
"Tried to add skill: {:?} which does not have an associated skill group.",
|
|
|
|
skill
|
|
|
|
);
|
2021-06-07 21:58:05 +00:00
|
|
|
common_base::dev_panic!(err, or return self);
|
2021-06-07 11:38:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let SkillSetBuilder(ref mut skill_set) = self;
|
|
|
|
if skill_is_applied(skill_set, skill, level) {
|
|
|
|
let err = format!(
|
|
|
|
"Tried to add skill: {:?} with level {:?} which is already applied",
|
|
|
|
skill, level,
|
|
|
|
);
|
2021-06-07 21:58:05 +00:00
|
|
|
common_base::dev_panic!(err, or return self);
|
2021-06-07 11:38:08 +00:00
|
|
|
}
|
|
|
|
for _ in 0..level.unwrap_or(1) {
|
|
|
|
skill_set.add_skill_points(group, skill_set.skill_cost(skill));
|
2021-10-22 15:47:06 +00:00
|
|
|
if let Err(err) = skill_set.unlock_skill(skill) {
|
|
|
|
let err_msg = format!("Failed to add skill: {:?}. Error: {:?}", skill, err);
|
|
|
|
common_base::dev_panic!(err_msg);
|
|
|
|
}
|
2021-06-07 11:38:08 +00:00
|
|
|
}
|
|
|
|
if !skill_is_applied(skill_set, skill, level) {
|
|
|
|
let err = format!(
|
|
|
|
"Failed to add skill: {:?}. Verify that it has the appropriate skill group \
|
|
|
|
available and meets all prerequisite skills.",
|
|
|
|
skill
|
|
|
|
);
|
2021-06-07 21:58:05 +00:00
|
|
|
common_base::dev_panic!(err);
|
2021-01-07 19:47:29 +00:00
|
|
|
}
|
2021-01-10 01:05:13 +00:00
|
|
|
self
|
2021-01-07 19:47:29 +00:00
|
|
|
}
|
|
|
|
|
2021-06-07 21:58:05 +00:00
|
|
|
#[must_use]
|
2021-01-07 19:47:29 +00:00
|
|
|
pub fn build(self) -> SkillSet { self.0 }
|
|
|
|
}
|
2021-06-07 11:38:08 +00:00
|
|
|
|
2021-06-07 21:58:05 +00:00
|
|
|
#[must_use]
|
2021-06-07 11:38:08 +00:00
|
|
|
fn skill_is_applied(skill_set: &SkillSet, skill: Skill, level: Option<u16>) -> bool {
|
|
|
|
if let Ok(applied_level) = skill_set.skill_level(skill) {
|
|
|
|
applied_level == level
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
2021-06-07 21:58:05 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_all_skillset_assets() {
|
2021-07-05 15:43:30 +00:00
|
|
|
let skillsets = assets::read_expect_dir::<SkillSetTree>("common.skillset", true);
|
|
|
|
for skillset in skillsets {
|
2021-06-07 21:58:05 +00:00
|
|
|
std::mem::drop({
|
|
|
|
let mut skillset_builder = SkillSetBuilder::default();
|
2021-07-05 15:43:30 +00:00
|
|
|
let nodes = &*skillset.0;
|
2021-06-07 21:58:05 +00:00
|
|
|
let tree = skills_from_nodes(nodes);
|
|
|
|
for (skill, level) in tree {
|
|
|
|
skillset_builder = skillset_builder.with_skill(skill, level);
|
|
|
|
}
|
|
|
|
|
|
|
|
skillset_builder
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|