1use core::fmt;
2
3use serde::{Deserialize, Serialize};
4
5use crate::{DamageType, DieStat};
6
7#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
8pub enum Attack {
9 MeleeWeaponAttack {
10 name: String,
11 modifier: i32,
12 reach: Option<i32>,
13 target_type: TargetType,
14 damage: DieStat,
15 damage_type: DamageType,
16 description: String,
17 },
18 RangedWeaponAttack {
19 name: String,
20 modifier: i32,
21 range: Option<Range>,
22 target_type: TargetType,
23 damage: DieStat,
24 damage_type: DamageType,
25 description: String,
26 },
27 MeleeOrRangedWeaponAttack {
28 name: String,
29 modifier: i32,
30 reach: Option<i32>,
31 range: Option<Range>,
32 target_type: TargetType,
33 damage: DieStat,
34 damage_type: DamageType,
35 description: String,
36 },
37 MeleeSpellAttack {
38 name: String,
39 modifier: i32,
40 reach: Option<i32>,
41 target_type: TargetType,
42 damage: DieStat,
43 damage_type: DamageType,
44 description: String,
45 },
46 RangedSpellAttack {
47 name: String,
48 modifier: i32,
49 range: Option<Range>,
50 target_type: TargetType,
51 damage: DieStat,
52 damage_type: DamageType,
53 description: String,
54 },
55}
56
57impl fmt::Display for Attack {
58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 match &self {
60 Attack::MeleeWeaponAttack {
61 name,
62 modifier,
63 reach,
64 target_type,
65 damage,
66 damage_type,
67 description,
68 } => {
69 write!(
70 f,
71 "{}. {}: {} to hit, reach {}, {}. Hit: {} {}. {}",
72 name,
73 "Melee Weapon Attack",
74 modifier,
75 if let Some(x) = reach {
76 x.to_string()
77 } else {
78 String::new()
79 },
80 target_type.to_string(),
81 damage.to_string(),
82 damage_type,
83 description
84 )
85 }
86 Attack::RangedWeaponAttack {
87 name,
88 modifier,
89 range,
90 target_type,
91 damage,
92 damage_type,
93 description,
94 } => {
95 write!(
96 f,
97 "{}. {}: {} to hit, range {}, {}. Hit: {} {}. {}",
98 name,
99 "Ranged Weapon Attack",
100 modifier,
101 if let Some(x) = range {
102 format!("{}/{} ft.", x.close_range, x.long_range)
103 } else {
104 String::from("")
105 },
106 target_type.to_string(),
107 damage.to_string(),
108 damage_type,
109 description
110 )
111 }
112 Attack::MeleeOrRangedWeaponAttack {
113 name,
114 modifier,
115 reach,
116 range,
117 target_type,
118 damage,
119 damage_type,
120 description,
121 } => {
122 write!(
123 f,
124 "{}. {}: {} to hit, reach {} or range {}, {}. Hit: {} {}. {}",
125 name,
126 "",
127 modifier,
128 if let Some(x) = reach {
129 x.to_string()
130 } else {
131 String::new()
132 },
133 if let Some(x) = range {
134 format!("{}/{} ft.", x.close_range, x.long_range)
135 } else {
136 String::new()
137 },
138 target_type.to_string(),
139 damage.to_string(),
140 damage_type,
141 description
142 )
143 }
144 Attack::MeleeSpellAttack {
145 name,
146 modifier,
147 reach,
148 target_type,
149 damage,
150 damage_type,
151 description,
152 } => {
153 write!(
154 f,
155 "{}. {}: {} to hit, reach {}, {}. Hit: {} {}. {}",
156 name,
157 "Melee Spell Attack",
158 modifier,
159 if let Some(x) = reach {
160 x.to_string()
161 } else {
162 String::new()
163 },
164 target_type.to_string(),
165 damage.to_string(),
166 damage_type,
167 description
168 )
169 }
170 Attack::RangedSpellAttack {
171 name,
172 modifier,
173 range,
174 target_type,
175 damage,
176 damage_type,
177 description,
178 } => {
179 write!(
180 f,
181 "{}. {}: {} to hit, range {}, {}. Hit: {} {}. {}",
182 name,
183 "Ranged Spell Attack",
184 modifier,
185 if let Some(x) = range {
186 format!("{}/{} ft.", x.close_range, x.long_range)
187 } else {
188 String::new()
189 },
190 target_type.to_string(),
191 damage.to_string(),
192 damage_type,
193 description
194 )
195 }
196 }
197 }
198}
199
200#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
201pub enum TargetType {
202 OneTarget,
203 MultipleTargets(i32),
204 Cone(i32),
205 Line(i32),
206 Cube(i32),
207 Sphere(i32),
208}
209
210impl fmt::Display for TargetType {
211 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
212 match &self {
213 TargetType::OneTarget => write!(f, "one target"),
214 TargetType::MultipleTargets(x) => write!(
215 f,
216 "{} targets",
217 num_to_words::integer_to_en_us(*x as i64).expect("Number of Targets")
218 ),
219 TargetType::Cone(x) => write!(
220 f,
221 "{} ft. Cone",
222 num_to_words::integer_to_en_us(*x as i64).expect("Number of Feet")
223 ),
224 TargetType::Line(x) => write!(
225 f,
226 "{} ft. Line",
227 num_to_words::integer_to_en_us(*x as i64).expect("Number of Feet")
228 ),
229 TargetType::Cube(x) => write!(
230 f,
231 "{} ft. Cube",
232 num_to_words::integer_to_en_us(*x as i64).expect("Number of Feet")
233 ),
234 TargetType::Sphere(x) => write!(
235 f,
236 "{} ft. Sphere",
237 num_to_words::integer_to_en_us(*x as i64).expect("Number of Feet")
238 ),
239 }
240 }
241}
242
243#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
244pub struct Range {
245 pub close_range: i32,
246 pub long_range: i32,
247}
248
249impl fmt::Display for Range {
250 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
251 write!(f, "{} ft. / {} ft.", self.close_range, self.long_range)
252 }
253}