1use crate::abilities::Abilities;
2use serde::{Deserialize, Serialize};
3use std::cell::RefCell;
4use std::collections::HashMap;
5use std::hash::Hash;
6use std::rc::Rc;
7
8#[cfg(feature = "serde")]
10fn default_abilities_modifiers() -> Rc<RefCell<Abilities>> {
11 Rc::new(RefCell::new(Abilities::default()))
12}
13
14#[derive(Debug, Clone)]
15#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
16#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
17#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
18pub enum ClassSpellCasting {
19 KnowledgePrepared {
25 spells_index: Vec<Vec<String>>,
27 spells_prepared_index: Vec<Vec<String>>,
29 pending_preparation: bool,
31 },
32 AlreadyKnowPrepared {
40 spells_prepared_index: Vec<Vec<String>>,
42 pending_preparation: bool,
44 },
45 KnowledgeAlreadyPrepared {
48 spells_index: Vec<Vec<String>>,
50 usable_slots: UsableSlots,
51 },
52}
53
54#[derive(Debug, Default, Clone)]
55#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
56#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
57#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
58pub struct UsableSlots {
59 pub cantrip_slots: u8,
60 pub level_1: u8,
61 pub level_2: u8,
62 pub level_3: u8,
63 pub level_4: u8,
64 pub level_5: u8,
65 pub level_6: u8,
66 pub level_7: u8,
67 pub level_8: u8,
68 pub level_9: u8,
69}
70
71#[derive(Debug, Default, Clone)]
72#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
73#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
74#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
75pub struct ClassProperties {
76 pub level: u8,
78 pub subclass: Option<String>,
80 pub spell_casting: Option<ClassSpellCasting>,
82 pub fighting_style: Option<String>,
83 pub hunters_prey: Option<String>,
84 pub defensive_tactics: Option<String>,
85 pub additional_fighting_style: Option<String>,
86 pub multiattack: Option<String>,
87 pub superior_hunters_defense: Option<String>,
88 pub natural_explorer_terrain_type: Option<Vec<String>>,
89 pub ranger_favored_enemy_type: Option<Vec<String>>,
90 pub sorcerer_metamagic: Option<Vec<String>>,
91 pub warlock_eldritch_invocation: Option<Vec<String>>,
92 pub sorcerer_dragon_ancestor: Option<String>,
93 #[cfg_attr(feature = "serde", serde(skip_serializing, skip_deserializing, default = "default_abilities_modifiers"))]
94 #[cfg_attr(feature = "utoipa", schema(ignore))]
95 pub abilities_modifiers: Rc<RefCell<Abilities>>,
96}
97
98#[derive(Debug)]
100#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
101#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
102#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
103pub struct Class(String, pub ClassProperties);
104
105impl Hash for Class {
106 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
107 self.0.hash(state);
108 }
109}
110
111impl PartialEq for Class {
112 fn eq(&self, other: &Self) -> bool {
113 self.0 == other.0
114 }
115}
116
117impl Eq for Class {}
118
119impl Class {
120 pub fn index(&self) -> &str {
121 &self.0
122 }
123
124 pub fn hit_dice(&self) -> u8 {
125 match self.index() {
126 "barbarian" => 12,
127 "bard" => 8,
128 "cleric" => 8,
129 "druid" => 8,
130 "fighter" => 10,
131 "monk" => 8,
132 "paladin" => 10,
133 "ranger" => 10,
134 "rogue" => 8,
135 "sorcerer" => 6,
136 "warlock" => 8,
137 "wizard" => 6,
138 _ => 6,
140 }
141 }
142}
143
144#[derive(Default, Debug)]
145#[cfg_attr(feature = "serde", derive(Serialize))]
146#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
147#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
148pub struct Classes(pub HashMap<String, Class>);
149
150#[cfg(feature = "serde")]
151impl<'de> Deserialize<'de> for Classes {
152 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
153 where
154 D: serde::Deserializer<'de>
155 {
156 let map = HashMap::<String, Class>::deserialize(deserializer)?;
159 Ok(Classes(map))
160 }
161}
162
163impl Classes {
164 #[cfg(feature = "serde")]
165 pub fn deserialize_with_abilities(
166 value: serde_json::Value,
167 shared_abilities: Rc<RefCell<Abilities>>,
168 ) -> Result<Self, serde_json::Error> {
169 let mut result = Classes::default();
170
171 let class_map = value.as_object()
173 .ok_or_else(|| serde::de::Error::custom("Expected object for Classes"))?;
174
175 for (key, value) in class_map {
176 let mut class_properties: ClassProperties = serde_json::from_value(
178 value.get(1).cloned().unwrap_or(serde_json::Value::Null)
179 )?;
180
181 class_properties.abilities_modifiers = shared_abilities.clone();
183
184 let index = key.clone();
186 let class = Class(index, class_properties);
187
188 result.0.insert(key.clone(), class);
190 }
191
192 Ok(result)
193 }
194
195 pub fn new(class_index: String) -> Self {
196 let mut classes = Self::default();
197
198 let spell_casting = match class_index.as_str() {
199 "cleric" | "paladin" | "druid" | "wizard" => {
200 Some(ClassSpellCasting::AlreadyKnowPrepared {
201 spells_prepared_index: Vec::new(),
202 pending_preparation: true,
203 })
204 }
205 "ranger" | "bard" | "warlock" | "sorcerer" => {
206 Some(ClassSpellCasting::KnowledgeAlreadyPrepared {
207 spells_index: Vec::new(),
208 usable_slots: UsableSlots::default(),
209 })
210 }
211 _ => None,
212 };
213
214 let class_properties = ClassProperties {
215 spell_casting,
216 ..ClassProperties::default()
217 };
218
219 classes
222 .0
223 .insert(class_index.clone(), Class(class_index, class_properties));
224 classes
225 }
226}