1use std::ops::{
2 Deref,
3 DerefMut,
4};
5
6use anyhow::Result;
7use battler_data::{
8 Id,
9 Identifiable,
10};
11use zone_alloc::{
12 ElementRef,
13 ElementRefMut,
14};
15
16use crate::{
17 abilities::Ability,
18 battle::{
19 Context,
20 MoveHandle,
21 },
22 common::split_once_optional,
23 conditions::Condition,
24 config::Clause,
25 effect::fxlang,
26 items::Item,
27 mons::Species,
28 moves::{
29 Move,
30 MoveHitEffectType,
31 },
32};
33
34#[allow(dead_code)]
40pub enum MaybeElementRef<'a, T> {
41 Owned(ElementRefMut<'a, T>),
42 Unowned(&'a mut T),
43}
44
45impl<T> Deref for MaybeElementRef<'_, T> {
46 type Target = T;
47 fn deref(&self) -> &Self::Target {
48 match self {
49 Self::Owned(val) => val.deref(),
50 Self::Unowned(val) => val,
51 }
52 }
53}
54
55impl<T> DerefMut for MaybeElementRef<'_, T> {
56 fn deref_mut(&mut self) -> &mut Self::Target {
57 match self {
58 Self::Owned(val) => val.deref_mut(),
59 Self::Unowned(val) => val,
60 }
61 }
62}
63
64impl<T> AsMut<T> for MaybeElementRef<'_, T> {
65 fn as_mut(&mut self) -> &mut T {
66 self.deref_mut()
67 }
68}
69
70impl<'a, T> From<ElementRefMut<'a, T>> for MaybeElementRef<'a, T> {
71 fn from(value: ElementRefMut<'a, T>) -> Self {
72 Self::Owned(value)
73 }
74}
75
76impl<'a, T> From<&'a mut T> for MaybeElementRef<'a, T> {
77 fn from(value: &'a mut T) -> Self {
78 Self::Unowned(value)
79 }
80}
81
82#[derive(Clone, Copy, Debug, PartialEq, Eq)]
84pub enum EffectType {
85 Move,
86 Ability,
87 Condition,
88 MoveCondition,
89 AbilityCondition,
90 Item,
91 ItemCondition,
92 Clause,
93 Species,
94}
95
96#[derive(Clone, Debug, PartialEq, Eq, Hash)]
100pub enum EffectHandle {
101 ActiveMove(MoveHandle, MoveHitEffectType),
103 MoveCondition(Id),
105 InactiveMove(Id),
107 Ability(Id),
109 AbilityCondition(Id),
111 Condition(Id),
113 Item(Id),
115 ItemCondition(Id),
117 Clause(Id),
119 Species(Id),
121 NonExistent(Id),
123}
124
125impl EffectHandle {
126 pub fn from_fxlang_id(fxlang_id: &str) -> Self {
128 let (effect_type, id) = split_once_optional(fxlang_id, ':');
129 let (effect_type, id) = match id {
130 Some(id) => (effect_type, id),
131 None => ("condition", fxlang_id),
132 };
133 let id = Id::from(id);
134 match effect_type {
135 "ability" => Self::Ability(id),
136 "abilitycondition" => Self::AbilityCondition(id),
137 "clause" => Self::Clause(id),
138 "item" => Self::Item(id),
139 "itemcondition" => Self::ItemCondition(id),
140 "move" => Self::InactiveMove(id),
141 "movecondition" => Self::MoveCondition(id),
142 "species" => Self::Species(id),
143 _ => Self::Condition(id),
144 }
145 }
146
147 pub fn is_ability(&self) -> bool {
149 match self {
150 Self::Ability(_) | Self::AbilityCondition(_) => true,
151 _ => false,
152 }
153 }
154
155 pub fn is_active_move(&self) -> bool {
157 match self {
158 Self::ActiveMove(_, _) => true,
159 _ => false,
160 }
161 }
162
163 pub fn is_active_move_secondary(&self) -> bool {
165 match self {
166 Self::ActiveMove(_, MoveHitEffectType::SecondaryEffect(_, _, _)) => true,
167 _ => false,
168 }
169 }
170 pub fn is_item(&self) -> bool {
172 match self {
173 Self::Item(_) | Self::ItemCondition(_) => true,
174 _ => false,
175 }
176 }
177
178 pub fn try_id(&self) -> Option<&Id> {
180 match self {
181 Self::ActiveMove(_, _) => None,
182 Self::MoveCondition(id) => Some(&id),
183 Self::InactiveMove(id) => Some(&id),
184 Self::Ability(id) => Some(&id),
185 Self::AbilityCondition(id) => Some(&id),
186 Self::Condition(id) => Some(&id),
187 Self::Item(id) => Some(&id),
188 Self::ItemCondition(id) => Some(&id),
189 Self::Clause(id) => Some(&id),
190 Self::Species(id) => Some(&id),
191 Self::NonExistent(id) => Some(&id),
192 }
193 }
194
195 pub fn stable_effect_handle(&self, context: &Context) -> Result<EffectHandle> {
200 match self {
201 Self::ActiveMove(active_move_handle, _) => Ok(EffectHandle::InactiveMove(
202 context.active_move(*active_move_handle)?.id().clone(),
203 )),
204 val @ _ => Ok(val.clone()),
205 }
206 }
207
208 pub fn condition_handle(&self, context: &Context) -> Result<Option<EffectHandle>> {
210 match self {
211 Self::ActiveMove(active_move_handle, _) => Ok(Some(EffectHandle::MoveCondition(
212 context.active_move(*active_move_handle)?.id().clone(),
213 ))),
214 Self::InactiveMove(id) => Ok(Some(EffectHandle::MoveCondition(id.clone()))),
215 Self::Ability(id) => Ok(Some(EffectHandle::AbilityCondition(id.clone()))),
216 Self::Item(id) => Ok(Some(EffectHandle::ItemCondition(id.clone()))),
217 _ => Ok(None),
218 }
219 }
220
221 pub fn non_condition_handle(&self) -> Option<EffectHandle> {
225 match self {
226 EffectHandle::MoveCondition(id) => Some(EffectHandle::InactiveMove(id.clone())),
227 EffectHandle::AbilityCondition(id) => Some(EffectHandle::Ability(id.clone())),
228 EffectHandle::ItemCondition(id) => Some(EffectHandle::Item(id.clone())),
229 EffectHandle::Condition(_) => None,
230 handle @ _ => Some(handle.clone()),
231 }
232 }
233
234 pub fn unlinked_fxlang_id(&self) -> Option<String> {
241 match self {
242 Self::ActiveMove(active_move_handle, _) => {
243 Some(format!("activemove:{active_move_handle}"))
244 }
245 _ => None,
246 }
247 }
248}
249
250pub enum Effect<'borrow> {
254 ActiveMove(&'borrow mut Move, MoveHitEffectType),
256 MoveCondition(ElementRef<'borrow, Move>),
258 InactiveMove(ElementRef<'borrow, Move>),
260 Ability(ElementRef<'borrow, Ability>),
262 AbilityCondition(ElementRef<'borrow, Ability>),
264 Condition(ElementRef<'borrow, Condition>),
266 Item(ElementRef<'borrow, Item>),
268 ItemCondition(ElementRef<'borrow, Item>),
270 Clause(ElementRef<'borrow, Clause>),
272 Species(ElementRef<'borrow, Species>),
274 NonExistent(Id),
276}
277
278impl<'borrow> Effect<'borrow> {
279 pub fn for_active_move(
281 active_move: &'borrow mut Move,
282 hit_effect_type: MoveHitEffectType,
283 ) -> Self {
284 Self::ActiveMove(active_move, hit_effect_type)
285 }
286
287 pub fn for_ability(ability: ElementRef<'borrow, Ability>) -> Self {
289 Self::Ability(ability)
290 }
291
292 pub fn for_ability_condition(ability: ElementRef<'borrow, Ability>) -> Self {
294 Self::AbilityCondition(ability)
295 }
296
297 pub fn for_condition(condition: ElementRef<'borrow, Condition>) -> Self {
299 Self::Condition(condition)
300 }
301
302 pub fn for_move_condition(mov: ElementRef<'borrow, Move>) -> Self {
304 Self::MoveCondition(mov)
305 }
306
307 pub fn for_item(item: ElementRef<'borrow, Item>) -> Self {
309 Self::Item(item)
310 }
311
312 pub fn for_item_condition(item: ElementRef<'borrow, Item>) -> Self {
314 Self::ItemCondition(item)
315 }
316
317 pub fn for_clause(clause: ElementRef<'borrow, Clause>) -> Self {
319 Self::Clause(clause)
320 }
321
322 pub fn for_species(species: ElementRef<'borrow, Species>) -> Self {
324 Self::Species(species)
325 }
326
327 pub fn for_inactive_move(mov: ElementRef<'borrow, Move>) -> Self {
329 Self::InactiveMove(mov)
330 }
331
332 pub fn for_non_existent(id: Id) -> Self {
334 Self::NonExistent(id)
335 }
336
337 pub fn name(&self) -> &str {
339 match self {
340 Self::ActiveMove(active_move, _) => &active_move.data.name,
341 Self::MoveCondition(mov) | Self::InactiveMove(mov) => &mov.data.name,
342 Self::Ability(ability) | Self::AbilityCondition(ability) => &ability.data.name,
343 Self::Condition(condition) => &condition.data.name,
344 Self::Item(item) | Self::ItemCondition(item) => &item.data.name,
345 Self::Clause(clause) => &clause.data.name,
346 Self::Species(species) => &species.data.name,
347 Self::NonExistent(id) => id.as_ref(),
348 }
349 }
350
351 pub fn effect_type(&self) -> EffectType {
353 match self {
354 Self::ActiveMove(_, _) => EffectType::Move,
355 Self::MoveCondition(_) => EffectType::MoveCondition,
356 Self::InactiveMove(_) => EffectType::Move,
357 Self::Ability(_) => EffectType::Ability,
358 Self::AbilityCondition(_) => EffectType::AbilityCondition,
359 Self::Condition(_) => EffectType::Condition,
360 Self::Item(_) => EffectType::Item,
361 Self::ItemCondition(_) => EffectType::ItemCondition,
362 Self::Clause(_) => EffectType::Clause,
363 Self::Species(_) => EffectType::Species,
364 Self::NonExistent(_) => EffectType::Condition,
365 }
366 }
367
368 fn effect_type_name(&self) -> &str {
369 match self {
370 Self::ActiveMove(_, _) | Self::MoveCondition(_) | Self::InactiveMove(_) => "move",
371 Self::Ability(_) | Self::AbilityCondition(_) => "ability",
372 Self::Condition(condition) => condition.condition_type_name(),
373 Self::Item(_) | Self::ItemCondition(_) => "item",
374 Self::Clause(_) => "clause",
375 Self::Species(_) => "species",
376 Self::NonExistent(_) => "",
377 }
378 }
379
380 pub fn full_name(&self) -> String {
382 match self.effect_type_name() {
383 "" => self.name().to_owned(),
384 prefix => format!("{prefix}:{}", self.name()),
385 }
386 }
387
388 fn fxlang_id_effect_type_name(&self) -> String {
389 match self {
390 Self::ActiveMove(_, hit_effect_type) => match hit_effect_type.secondary_index() {
391 None => "move".to_owned(),
392 Some((target, hit, secondary_index)) => {
393 format!("movesecondary-{hit}-{target}-{secondary_index}")
394 }
395 },
396 Self::MoveCondition(_) => "movecondition".to_owned(),
397 Self::InactiveMove(_) => "move".to_owned(),
398 Self::Ability(_) => "ability".to_owned(),
399 Self::AbilityCondition(_) => "abilitycondition".to_owned(),
400 Self::Condition(condition) => condition.condition_type_name().to_owned(),
401 Self::Item(_) => "item".to_owned(),
402 Self::ItemCondition(_) => "itemcondition".to_owned(),
403 Self::Clause(_) => "clause".to_owned(),
404 Self::Species(_) => "species".to_owned(),
405 Self::NonExistent(_) => "condition".to_owned(),
406 }
407 }
408
409 pub fn fxlang_id(&self) -> String {
411 match self.fxlang_id_effect_type_name().as_str() {
412 "" => format!("{}", self.id()),
413 prefix => format!("{prefix}:{}", self.id()),
414 }
415 }
416
417 pub fn move_effect<'effect>(&'effect self) -> Option<&'effect Move> {
419 match self {
420 Self::ActiveMove(active_move, _) => Some(active_move.deref()),
421 Self::InactiveMove(mov) => Some(mov),
422 Self::MoveCondition(mov) => Some(mov),
423 _ => None,
424 }
425 }
426
427 pub fn condition<'effect>(&'effect self) -> Option<&'effect Condition> {
429 match self {
430 Self::Condition(condition) => Some(condition.deref()),
431 _ => None,
432 }
433 }
434
435 pub fn fxlang_effect<'effect>(&'effect self) -> Option<&'effect fxlang::Effect> {
437 match self {
438 Self::ActiveMove(active_move, hit_effect_type) => {
439 active_move.fxlang_effect(*hit_effect_type)
440 }
441 Self::MoveCondition(mov) => Some(&mov.condition),
442 Self::InactiveMove(mov) => Some(&mov.effect),
443 Self::Ability(ability) => Some(&ability.effect),
444 Self::AbilityCondition(ability) => Some(&ability.condition),
445 Self::Condition(condition) => Some(&condition.condition),
446 Self::Item(item) => Some(&item.effect),
447 Self::ItemCondition(item) => Some(&item.condition),
448 Self::Clause(clause) => Some(&clause.effect),
449 Self::Species(species) => Some(&species.effect),
450 Self::NonExistent(_) => None,
451 }
452 }
453
454 pub fn unlinked(&self) -> bool {
456 match self {
457 Self::ActiveMove(active_move, _) => active_move.unlinked,
458 _ => false,
459 }
460 }
461}
462
463impl Identifiable for Effect<'_> {
464 fn id(&self) -> &Id {
465 match self {
466 Self::ActiveMove(active_move, _) => active_move.id(),
467 Self::MoveCondition(mov) | Self::InactiveMove(mov) => mov.id(),
468 Self::Ability(ability) | Self::AbilityCondition(ability) => ability.id(),
469 Self::Condition(condition) => condition.id(),
470 Self::Item(item) | Self::ItemCondition(item) => item.id(),
471 Self::Clause(clause) => clause.id(),
472 Self::Species(species) => species.id(),
473 Self::NonExistent(id) => id,
474 }
475 }
476}