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