1use crate::abilities::Abilities;
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::hash::Hash;
5
6#[derive(Debug, Clone)]
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 {
17 spells_index: Vec<Vec<String>>,
19 spells_prepared_index: Vec<Vec<String>>,
21 pending_preparation: bool,
23 },
24 AlreadyKnowPrepared {
31 spells_prepared_index: Vec<Vec<String>>,
33 pending_preparation: bool,
35 },
36 KnowledgeAlreadyPrepared {
39 spells_index: Vec<Vec<String>>,
41 usable_slots: UsableSlots,
42 },
43}
44
45#[derive(Debug, Default, Clone)]
46#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
47#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
48#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
49pub struct UsableSlots {
50 pub cantrip_slots: u8,
51 pub level_1: u8,
52 pub level_2: u8,
53 pub level_3: u8,
54 pub level_4: u8,
55 pub level_5: u8,
56 pub level_6: u8,
57 pub level_7: u8,
58 pub level_8: u8,
59 pub level_9: u8,
60}
61
62#[derive(Debug, Default, Clone)]
63#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
64#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
65#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
66pub struct ClassProperties {
67 pub level: u8,
69 pub subclass: Option<String>,
71 pub spell_casting: Option<ClassSpellCasting>,
73 pub fighting_style: Option<String>,
74 pub hunters_prey: Option<String>,
75 pub defensive_tactics: Option<String>,
76 pub additional_fighting_style: Option<String>,
77 pub multiattack: Option<String>,
78 pub superior_hunters_defense: Option<String>,
79 pub abilities_modifiers: Abilities,
80}
81
82#[derive(Debug)]
84#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
85#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
86#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
87pub struct Class(String, pub ClassProperties);
88
89impl Hash for Class {
90 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
91 self.0.hash(state);
92 }
93}
94
95impl PartialEq for Class {
96 fn eq(&self, other: &Self) -> bool {
97 self.0 == other.0
98 }
99}
100
101impl Eq for Class {}
102
103impl Class {
104 pub fn index(&self) -> &str {
105 &self.0
106 }
107
108 pub fn hit_dice(&self) -> u8 {
109 match self.index() {
110 "barbarian" => 12,
111 "bard" => 8,
112 "cleric" => 8,
113 "druid" => 8,
114 "fighter" => 10,
115 "monk" => 8,
116 "paladin" => 10,
117 "ranger" => 10,
118 "rogue" => 8,
119 "sorcerer" => 6,
120 "warlock" => 8,
121 "wizard" => 6,
122 _ => 6,
124 }
125 }
126}
127
128#[derive(Default, Debug)]
129#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
130#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
131#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
132pub struct Classes(pub HashMap<String, Class>);
133
134impl Classes {
135 pub fn new(class_index: String) -> Self {
136 let mut classes = Self::default();
137
138 let spell_casting = match class_index.as_str() {
139 "cleric" | "paladin" | "druid" => Some(ClassSpellCasting::AlreadyKnowPrepared {
140 spells_prepared_index: Vec::new(),
141 pending_preparation: true,
142 }),
143 "ranger" | "bard" | "warlock" => Some(ClassSpellCasting::KnowledgeAlreadyPrepared {
144 spells_index: Vec::new(),
145 usable_slots: UsableSlots::default(),
146 }),
147 _ => None,
148 };
149
150 let class_properties = ClassProperties {
151 spell_casting,
152 ..ClassProperties::default()
153 };
154
155 classes
156 .0
157 .insert(class_index.clone(), Class(class_index, class_properties));
158 classes
159 }
160}