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);
118
119 return self;
120 };
121
122 let SkillSetBuilder(ref mut skill_set) = self;
123 if skill_is_applied(skill_set, skill, level) {
124 let err = format!(
125 "Tried to add skill: {skill:?} with level {level:?} which is already applied"
126 );
127 common_base::dev_panic!(err);
128
129 return self;
130 }
131 for _ in 0..level {
132 skill_set.add_skill_points(group, skill_set.skill_cost(skill));
133 if let Err(err) = skill_set.unlock_skill(skill) {
134 let err_msg = format!("Failed to add skill: {skill:?}. Error: {err:?}");
135 common_base::dev_panic!(err_msg);
136 }
137 }
138 if !skill_is_applied(skill_set, skill, level) {
139 let err = format!(
140 "Failed to add skill: {skill:?}. Verify that it has the appropriate skill group \
141 available and meets all prerequisite skills."
142 );
143 common_base::dev_panic!(err);
144 }
145 self
146 }
147
148 #[must_use]
149 pub fn build(self) -> SkillSet { self.0 }
150}
151
152#[must_use]
153fn skill_is_applied(skill_set: &SkillSet, skill: Skill, level: u16) -> bool {
154 if let Ok(applied_level) = skill_set.skill_level(skill) {
155 applied_level == level
156 } else {
157 false
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164
165 #[test]
166 fn test_all_skillset_assets() {
167 let skillsets =
168 assets::load_rec_dir::<SkillSetTree>("common.skillset").expect("load skillsets");
169 for skillset in skillsets.read().ids() {
170 let skillset = SkillSetTree::load_expect(skillset).read();
171
172 drop({
173 let mut skillset_builder = SkillSetBuilder::default();
174 let nodes = &*skillset.0;
175 let tree = skills_from_nodes(nodes);
176 for (skill, level) in tree {
177 skillset_builder = skillset_builder.with_skill(skill, level);
178 }
179
180 skillset_builder
181 });
182 }
183 }
184}