1use std::collections::{HashMap};
2use std::hash::Hash;
3use serde::{Deserialize, Serialize};
4use crate::abilities::Abilities;
5
6#[derive(Debug)]
7#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
8#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
9#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
10pub enum ClassSpellCasting {
11 KnowledgePrepared {
14 spells_index: Vec<Vec<String>>,
16 spells_prepared_index: Vec<Vec<String>>,
18 pending_preparation: bool,
20 },
21 AlreadyKnowPrepared {
24 spells_prepared_index: Vec<Vec<String>>,
26 pending_preparation: bool,
28 },
29 KnowledgeAlreadyPrepared {
32 spells_index: Vec<Vec<String>>,
34 usable_slots: UsableSlots,
35 },
36}
37
38#[derive(Debug, Default)]
39#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
40#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
41#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
42pub struct UsableSlots {
43 pub level_1: u8,
44 pub level_2: u8,
45 pub level_3: u8,
46 pub level_4: u8,
47 pub level_5: u8,
48 pub level_6: u8,
49 pub level_7: u8,
50 pub level_8: u8,
51 pub level_9: u8,
52}
53
54#[derive(Debug, Default)]
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 ClassProperties {
59 pub level: u8,
61 pub subclass: Option<String>,
63 pub spell_casting: Option<ClassSpellCasting>,
65 pub fighting_style: Option<String>,
66 pub additional_fighting_style: Option<String>,
67 pub abilities_modifiers: Abilities,
68}
69
70#[derive(Debug)]
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 Class(String, pub ClassProperties);
76
77impl Hash for Class {
78 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
79 self.0.hash(state);
80 }
81}
82
83impl PartialEq for Class {
84 fn eq(&self, other: &Self) -> bool {
85 self.0 == other.0
86 }
87}
88
89impl Eq for Class {}
90
91impl Class {
92 pub fn index(&self) -> &str {
93 &self.0
94 }
95
96 pub fn hit_dice(&self) -> u8 {
97 match self.index() {
98 "barbarian" => 12,
99 "bard" => 8,
100 "cleric" => 8,
101 "druid" => 8,
102 "fighter" => 10,
103 "monk" => 8,
104 "paladin" => 10,
105 "ranger" => 10,
106 "rogue" => 8,
107 "sorcerer" => 6,
108 "warlock" => 8,
109 "wizard" => 6,
110 _ => 6,
112 }
113 }
114}
115
116#[derive(Default, Debug)]
117#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
118#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
119#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
120pub struct Classes(pub HashMap<String, Class>);
121
122
123impl Classes {
124 pub fn new(class_index: String) -> Self {
125 let mut classes = Self::default();
126
127 let spell_casting = match class_index.as_str() {
128 "cleric" | "paladin" | "druid" => {
129 Some(ClassSpellCasting::AlreadyKnowPrepared {
130 spells_prepared_index: Vec::new(),
131 pending_preparation: true,
132 })
133 }
134 _ => {
135 None
136 }
137 };
138
139 let class_properties = ClassProperties {
140 spell_casting,
141 ..ClassProperties::default()
142 };
143
144 classes.0.insert(class_index.clone(), Class(class_index, class_properties));
145 classes
146 }
147}