firecore_battle/default_engine/moves/
execution.rs

1use rand::Rng;
2
3use serde::{Deserialize, Serialize};
4
5use pokedex::{
6    ailment::{Ailment, AilmentLength},
7    moves::Move,
8};
9
10use crate::{
11    engine::{BattlePokemon, MoveResult},
12    moves::{damage::DamageKind, MoveCancel, Percent},
13    pokemon::{
14        stat::{BattleStatType, Stage},
15        Indexed,
16    },
17};
18
19#[derive(Debug, Clone, Deserialize, Serialize)]
20pub enum MoveExecution {
21    /// Load a vector of actions
22    Actions(Vec<MoveUse>),
23    /// Use a script defined in the instance of the object that uses this
24    Script,
25    /// Placeholder to show that object does not have a defined use yet.
26    None,
27}
28
29#[derive(Debug, Clone, Deserialize, Serialize)]
30#[serde(deny_unknown_fields)]
31pub enum MoveUse {
32    Damage(DamageKind),
33    Ailment(Option<(Ailment, AilmentLength)>, Percent),
34    Drain(DamageKind, i8),
35    Stat(BattleStatType, Stage),
36    Flinch,
37    Chance(Vec<Self>, Percent),
38}
39
40impl MoveExecution {
41    pub fn size(&self) -> usize {
42        match self {
43            Self::Actions(actions) => actions.iter().map(MoveUse::size).sum(),
44            Self::Script | Self::None => 1,
45        }
46    }
47}
48
49impl MoveUse {
50    pub fn size(&self) -> usize {
51        match self {
52            Self::Chance(uses, ..) => uses.iter().map(Self::size).sum(),
53            Self::Drain(..) => 2,
54            _ => 1,
55        }
56    }
57}
58
59pub fn move_usage<ID: Clone, R: Rng>(
60    user: &Indexed<ID, &BattlePokemon>,
61    random: &mut R,
62    results: &mut Vec<Indexed<ID, MoveResult>>,
63    actions: &[MoveUse],
64    m: &Move,
65    Indexed(target_id, target): Indexed<ID, &BattlePokemon>,
66) {
67    for action in actions {
68        match action {
69            MoveUse::Damage(kind) => {
70                results.push(Indexed(
71                    target_id.clone(),
72                    MoveResult::Damage(user.1.damage_kind(
73                        random,
74                        target,
75                        *kind,
76                        m.category,
77                        m.pokemon_type,
78                        m.crit_rate,
79                    )),
80                ));
81            }
82            MoveUse::Ailment(effect, chance) => {
83                if random.gen_bool(*chance as f64 / 100.0) {
84                    match effect {
85                        Some((ailment, length)) => {
86                            if target.ailment.is_none() {
87                                results.push(Indexed(
88                                    target_id.clone(),
89                                    MoveResult::Ailment(Some(length.init(*ailment, random))),
90                                ));
91                            }
92                        }
93                        None => {
94                            if target.ailment.is_some() {
95                                results.push(Indexed(target_id.clone(), MoveResult::Ailment(None)))
96                            }
97                        }
98                    }
99                }
100            }
101            MoveUse::Drain(kind, percent) => {
102                let result = user.1.damage_kind(
103                    random,
104                    target,
105                    *kind,
106                    m.category,
107                    m.pokemon_type,
108                    m.crit_rate,
109                );
110
111                let healing = (result.damage as f32 * *percent as f32 / 100.0) as i16;
112
113                results.push(Indexed(target_id.clone(), MoveResult::Damage(result)));
114                results.push(Indexed(user.0.clone(), MoveResult::Heal(healing)))
115            }
116            MoveUse::Stat(stat, stage) => {
117                if target.stages.can_change(*stat, *stage) {
118                    results.push(Indexed(target_id.clone(), MoveResult::Stat(*stat, *stage)));
119                }
120            }
121            // MoveUseType::Linger(..) => {
122            // 	results.insert(target.instance, Some(MoveAction::Todo));
123            // }
124            MoveUse::Flinch => results.push(Indexed(
125                target_id.clone(),
126                MoveResult::Cancel(MoveCancel::Flinch),
127            )),
128            MoveUse::Chance(actions, chance) => {
129                if random.gen_range(0..=100) < *chance {
130                    move_usage(
131                        user,
132                        random,
133                        results,
134                        actions,
135                        m,
136                        Indexed(target_id.clone(), target),
137                    );
138                }
139            }
140        }
141    }
142}