veloren_common/
skillset_builder.rs1#![warn(clippy::pedantic)]
2use crate::comp::skillset::{SkillGroupKind, SkillSet, skills::Skill};
4
5use crate::assets::{self, AssetExt};
6use serde::{Deserialize, Serialize};
7use tracing::warn;
8
9#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
13pub enum Preset {
14 Rank1,
15 Rank2,
16 Rank3,
17 Rank4,
18 Rank5,
19}
20
21#[derive(Debug, Deserialize, Clone)]
22struct SkillSetTree(Vec<SkillNode>);
23impl assets::Asset for SkillSetTree {
24 type Loader = assets::RonLoader;
25
26 const EXTENSION: &'static str = "ron";
27}
28
29#[derive(Debug, Deserialize, Clone)]
30enum SkillNode {
31 Tree(String),
32 Skill((Skill, u16)),
33 Group(SkillGroupKind),
34}
35
36#[must_use]
37fn skills_from_asset_expect(asset_specifier: &str) -> Vec<(Skill, u16)> {
38 let nodes = SkillSetTree::load_expect(asset_specifier).read();
39
40 skills_from_nodes(&nodes.0)
41}
42
43#[must_use]
44fn skills_from_nodes(nodes: &[SkillNode]) -> Vec<(Skill, u16)> {
45 let mut skills = Vec::new();
46 for node in nodes {
47 match node {
48 SkillNode::Tree(asset) => {
49 skills.append(&mut skills_from_asset_expect(asset));
50 },
51 SkillNode::Skill(req) => {
52 skills.push(*req);
53 },
54 SkillNode::Group(group) => {
55 skills.push((Skill::UnlockGroup(*group), 1));
56 },
57 }
58 }
59
60 skills
61}
62
63#[derive(Default)]
64pub struct SkillSetBuilder(SkillSet);
65
66impl SkillSetBuilder {
67 #[must_use]
69 pub fn from_asset_expect(asset_specifier: &str) -> Self {
70 let builder = Self::default();
71
72 builder.with_asset_expect(asset_specifier)
73 }
74
75 #[must_use]
77 pub fn with_asset_expect(mut self, asset_specifier: &str) -> Self {
78 let tree = skills_from_asset_expect(asset_specifier);
79 for (skill, level) in tree {
80 self = self.with_skill(skill, level);
81 }
82
83 self
84 }
85
86 #[must_use]
88 pub fn from_preset(preset: Preset) -> Self {
89 let builder = Self::default();
90
91 builder.with_preset(preset)
92 }
93
94 #[must_use]
96 pub fn with_preset(self, preset: Preset) -> Self {
97 match preset {
98 Preset::Rank1 => self.with_asset_expect("common.skillset.preset.rank1.fullskill"),
99 Preset::Rank2 => self.with_asset_expect("common.skillset.preset.rank2.fullskill"),
100 Preset::Rank3 => self.with_asset_expect("common.skillset.preset.rank3.fullskill"),
101 Preset::Rank4 => self.with_asset_expect("common.skillset.preset.rank4.fullskill"),
102 Preset::Rank5 => self.with_asset_expect("common.skillset.preset.rank5.fullskill"),
103 }
104 }
105
106 #[must_use]
107 pub fn with_skill(mut self, skill: Skill, level: u16) -> Self {
113 let Some(group) = skill.skill_group_kind() else {
114 let err = format!(
115 "Tried to add skill: {skill:?} which does not have an associated skill group."
116 );
117 common_base::dev_panic!(err, or return self);
118 };
119
120 let SkillSetBuilder(ref mut skill_set) = self;
121 if skill_is_applied(skill_set, skill, level) {
122 let err = format!(
123 "Tried to add skill: {skill:?} with level {level:?} which is already applied"
124 );
125 common_base::dev_panic!(err, or return self);
126 }
127 for _ in 0..level {
128 skill_set.add_skill_points(group, skill_set.skill_cost(skill));
129 if let Err(err) = skill_set.unlock_skill(skill) {
130 let err_msg = format!("Failed to add skill: {skill:?}. Error: {err:?}");
131 common_base::dev_panic!(err_msg);
132 }
133 }
134 if !skill_is_applied(skill_set, skill, level) {
135 let err = format!(
136 "Failed to add skill: {skill:?}. Verify that it has the appropriate skill group \
137 available and meets all prerequisite skills."
138 );
139 common_base::dev_panic!(err);
140 }
141 self
142 }
143
144 #[must_use]
145 pub fn build(self) -> SkillSet { self.0 }
146}
147
148#[must_use]
149fn skill_is_applied(skill_set: &SkillSet, skill: Skill, level: u16) -> bool {
150 if let Ok(applied_level) = skill_set.skill_level(skill) {
151 applied_level == level
152 } else {
153 false
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
162 fn test_all_skillset_assets() {
163 let skillsets =
164 assets::load_rec_dir::<SkillSetTree>("common.skillset").expect("load skillsets");
165 for skillset in skillsets.read().ids() {
166 let skillset = SkillSetTree::load_expect(skillset).read();
167
168 drop({
169 let mut skillset_builder = SkillSetBuilder::default();
170 let nodes = &*skillset.0;
171 let tree = skills_from_nodes(nodes);
172 for (skill, level) in tree {
173 skillset_builder = skillset_builder.with_skill(skill, level);
174 }
175
176 skillset_builder
177 });
178 }
179 }
180}