firecore_battle/host/
moves.rs

1use core::{cmp::Reverse, hash::Hash};
2use rand::Rng;
3use std::collections::BTreeMap;
4
5use pokedex::{
6    moves::{Priority},
7    pokemon::{
8        stat::{BaseStat, StatType},
9    },
10};
11
12use crate::{
13    moves::BattleMove,
14    pokemon::{Indexed, PokemonIdentifier},
15};
16
17use super::{collections::BattleMap, party::BattleParty, player::BattlePlayer};
18
19#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
20pub enum MovePriority<ID: Ord> {
21    First(ID, usize),
22    Second(Reverse<Priority>, Reverse<BaseStat>, Option<u16>),
23}
24
25pub fn move_queue<ID: Clone + Ord + Hash, R: Rng, T>(
26    players: &mut BattleMap<ID, BattlePlayer<ID, T>>,
27    random: &mut R,
28) -> Vec<Indexed<ID, BattleMove<ID>>> {
29    let mut queue = BTreeMap::new();
30
31    for mut player in players.values_mut() {
32        queue_player(&mut queue, &mut player.party, random)
33    }
34
35    queue.into_values().collect()
36}
37
38fn queue_player<ID: Clone + Ord, R: Rng, T>(
39    queue: &mut BTreeMap<MovePriority<ID>, Indexed<ID, BattleMove<ID>>>,
40    party: &mut BattleParty<ID, T>,
41    random: &mut R,
42) {
43    for index in 0..party.active.len() {
44        if let Some(pokemon) = party.active.get_mut(index).and_then(Option::as_mut) {
45            if let Some(action) = pokemon.queued_move.take() {
46                if let Some(instance) = party.active(index) {
47                    let pokemon = PokemonIdentifier(party.id().clone(), index);
48
49                    let mut priority = match action {
50                        BattleMove::Move(index, ..) => MovePriority::Second(
51                            Reverse(
52                                instance
53                                    .moves
54                                    .get(index)
55                                    .map(|i| i.0.priority)
56                                    .unwrap_or_default(),
57                            ),
58                            Reverse(instance.stat(StatType::Speed)),
59                            None,
60                        ),
61                        _ => MovePriority::First(party.id().clone(), index),
62                    };
63
64                    fn tie_break<ID: Ord, R: Rng>(
65                        queue: &mut BTreeMap<MovePriority<ID>, Indexed<ID, BattleMove<ID>>>,
66                        random: &mut R,
67                        priority: &mut MovePriority<ID>,
68                    ) {
69                        if let MovePriority::Second(.., shift) = priority {
70                            *shift = Some(random.gen());
71                        }
72                        if queue.contains_key(priority) {
73                            tie_break(queue, random, priority);
74                        }
75                    }
76
77                    if queue.contains_key(&priority) {
78                        tie_break(queue, random, &mut priority);
79                    }
80
81                    queue.insert(priority, Indexed(pokemon, action));
82                }
83            }
84        }
85    }
86}