battler/battle/
core_battle.rs

1use std::{
2    cmp::Ordering,
3    collections::VecDeque,
4    marker::PhantomPinned,
5    mem,
6    time::{
7        SystemTime,
8        UNIX_EPOCH,
9    },
10};
11
12use ahash::HashMapExt;
13use itertools::Itertools;
14use zone_alloc::{
15    ElementRef,
16    ElementRefMut,
17};
18
19use crate::{
20    battle::{
21        core_battle_actions,
22        core_battle_effects,
23        core_battle_logs,
24        speed_sort,
25        Action,
26        BattleQueue,
27        BattleRegistry,
28        Context,
29        CoreBattleEngineOptions,
30        CoreBattleEngineRandomizeBaseDamage,
31        CoreBattleOptions,
32        EndAction,
33        Field,
34        LearnMoveRequest,
35        Mon,
36        MonContext,
37        MonExitType,
38        MonHandle,
39        MoveHandle,
40        Player,
41        PlayerBattleData,
42        PlayerContext,
43        Request,
44        RequestType,
45        Side,
46        SpeedOrderable,
47        SwitchRequest,
48        TeamPreviewRequest,
49        TurnRequest,
50    },
51    common::{
52        FastHashMap,
53        Id,
54        Identifiable,
55    },
56    config::Format,
57    dex::{
58        DataStore,
59        Dex,
60    },
61    effect::{
62        fxlang,
63        Effect,
64        EffectHandle,
65        EffectManager,
66        LinkedEffectsManager,
67    },
68    error::{
69        general_error,
70        Error,
71        WrapOptionError,
72        WrapResultError,
73    },
74    items::ItemTarget,
75    log::{
76        Event,
77        EventLog,
78        EventLogEntryMut,
79    },
80    log_event,
81    mons::{
82        Type,
83        TypeEffectiveness,
84    },
85    moves::{
86        Move,
87        MoveTarget,
88        SwitchType,
89    },
90    rng::{
91        rand_util,
92        PseudoRandomNumberGenerator,
93    },
94};
95
96/// The public interface for a [`CoreBattle`].
97///
98/// Intended to separate public methods used by library users (a.k.a., battle operators) and public
99/// methods used by internal battle logic.
100pub struct PublicCoreBattle<'d> {
101    /// The internal [`CoreBattle`], which contains all battle objects and logic.
102    pub internal: CoreBattle<'d>,
103}
104
105impl<'d> PublicCoreBattle<'d> {
106    /// Constructs a new [`PublicCoreBattle`] from a
107    /// [`BattleBuilder`][`crate::battle::BattleBuilder`].
108    pub(crate) fn from_builder(
109        options: CoreBattleOptions,
110        dex: Dex<'d>,
111        format: Format,
112        engine_options: CoreBattleEngineOptions,
113    ) -> Result<Self, Error> {
114        let internal = CoreBattle::from_builder(options, dex, format, engine_options)?;
115        Ok(Self { internal })
116    }
117
118    /// Creates a new battle.
119    pub fn new(
120        options: CoreBattleOptions,
121        data: &'d dyn DataStore,
122        engine_options: CoreBattleEngineOptions,
123    ) -> Result<Self, Error> {
124        let internal = CoreBattle::new(options, data, engine_options)?;
125        Ok(Self { internal })
126    }
127
128    /// Has the battle started?
129    pub fn started(&self) -> bool {
130        self.internal.started
131    }
132
133    /// Has the battle ended?
134    pub fn ended(&self) -> bool {
135        self.internal.ended
136    }
137
138    /// Does the battle have new battle logs since the last call to [`Self::new_logs`]?
139    pub fn has_new_logs(&self) -> bool {
140        self.internal.has_new_logs()
141    }
142
143    /// Returns all battle logs.
144    pub fn all_logs(&self) -> impl Iterator<Item = &str> {
145        self.internal.all_logs()
146    }
147
148    /// Returns new battle logs since the last call to [`Self::new_logs`].
149    pub fn new_logs(&mut self) -> impl Iterator<Item = &str> {
150        self.internal.new_logs()
151    }
152
153    /// Starts the battle.
154    pub fn start(&mut self) -> Result<(), Error> {
155        self.internal.start()
156    }
157
158    /// Is the battle ready to continue?
159    pub fn ready_to_continue(&mut self) -> Result<bool, Error> {
160        self.internal.ready_to_continue()
161    }
162
163    /// Continues the battle.
164    ///
165    /// [`Self::ready_to_continue`] should return `Ok(true)` before this method
166    /// is called.
167    pub fn continue_battle(&mut self) -> Result<(), Error> {
168        self.internal.continue_battle()
169    }
170
171    /// Returns the player data for the battle by player ID.
172    ///
173    /// Individual requests to players also contain this data, but this method can be useful for
174    /// viewing for the player's team at other points in the battle and even after the battle ends.
175    pub fn player_data(&mut self, player: &str) -> Result<PlayerBattleData, Error> {
176        self.internal.player_data(player)
177    }
178
179    /// Returns all active requests for the battle, indexed by player ID.
180    pub fn active_requests<'b>(&'b self) -> impl Iterator<Item = (String, Request)> + 'b {
181        self.internal.active_requests()
182    }
183
184    /// Returns the active request for the player ID.
185    pub fn request_for_player(&self, player: &str) -> Option<Request> {
186        self.internal.request_for_player(player)
187    }
188
189    /// Sets the player's choice for their active request.
190    pub fn set_player_choice(&mut self, player_id: &str, input: &str) -> Result<(), Error> {
191        self.internal.set_player_choice(player_id, input)
192    }
193}
194
195/// An entry in the faint queue.
196pub struct FaintEntry {
197    pub target: MonHandle,
198    pub source: Option<MonHandle>,
199    pub effect: Option<EffectHandle>,
200}
201
202/// An entry in the catch queue.
203pub struct CatchEntry {
204    pub target: MonHandle,
205    pub player: usize,
206    pub item: Id,
207    pub shakes: u8,
208    pub critical: bool,
209}
210
211/// An instance of a battle.
212///
213/// A battle has the following properties:
214/// - Takes place on a single [`Field`][`crate::battle::Field`].
215/// - Takes place between two [`Side`][`crate::battle::Side`]s.
216/// - Receives input for a single [`Player`][`crate::battle::Player`].
217/// - Features [`Mon`][`crate::battle::Mon`]s attacking one another in a turn-based manner.
218/// - Adheres to a [`Format`][`crate::config::Format`].
219///
220/// All of the core battle logic runs through this object.
221pub struct CoreBattle<'d> {
222    log: EventLog,
223
224    // SAFETY: None of the objects below should be overwritten or destroyed for the lifetime of the
225    // battle.
226    //
227    // We could PinBox these, but that would complicate our code quite a bit.
228    pub prng: Box<dyn PseudoRandomNumberGenerator>,
229    pub dex: Dex<'d>,
230    pub queue: BattleQueue,
231    pub faint_queue: VecDeque<FaintEntry>,
232    pub catch_queue: VecDeque<CatchEntry>,
233    pub engine_options: CoreBattleEngineOptions,
234    pub format: Format,
235    pub field: Field,
236    pub sides: [Side; 2],
237    pub players: Vec<Player>,
238    pub effect_manager: EffectManager,
239    pub linked_effects_manager: LinkedEffectsManager,
240
241    registry: BattleRegistry,
242    player_ids: FastHashMap<String, usize>,
243    effect_handle_cache: FastHashMap<Id, EffectHandle>,
244
245    turn: u64,
246    request: Option<RequestType>,
247    mid_turn: bool,
248    started: bool,
249    in_pre_battle: bool,
250    ending: bool,
251    ended: bool,
252    next_ability_order: u32,
253    next_forfeit_order: u32,
254    last_move_log: Option<usize>,
255    last_exited: Option<MonHandle>,
256
257    input_log: FastHashMap<usize, FastHashMap<u64, String>>,
258
259    _pin: PhantomPinned,
260}
261
262// Block for constructors.
263impl<'d> CoreBattle<'d> {
264    fn new(
265        mut options: CoreBattleOptions,
266        data: &'d dyn DataStore,
267        engine_options: CoreBattleEngineOptions,
268    ) -> Result<Self, Error> {
269        options
270            .validate()
271            .wrap_error_with_message("battle options are invalid")?;
272        let dex = Dex::new(data)?;
273        let format_data = mem::replace(&mut options.format, None);
274        let format = Format::new(
275            format_data.wrap_expectation("missing format field for new battle")?,
276            &dex,
277        )?;
278        Self::from_builder(options, dex, format, engine_options)
279    }
280
281    fn from_builder(
282        options: CoreBattleOptions,
283        dex: Dex<'d>,
284        format: Format,
285        engine_options: CoreBattleEngineOptions,
286    ) -> Result<Self, Error> {
287        let prng = (engine_options.rng_factory)(options.seed);
288        let log = EventLog::new();
289        let registry = BattleRegistry::new();
290        let queue = BattleQueue::new();
291        let faint_queue = VecDeque::new();
292        let catch_queue = VecDeque::new();
293        let field = Field::new(options.field);
294        let (side_1, mut players) =
295            Side::new(options.side_1, 0, &format.battle_type, &dex, &registry)?;
296        let (side_2, side_2_players) =
297            Side::new(options.side_2, 1, &format.battle_type, &dex, &registry)?;
298        players.extend(side_2_players);
299
300        let player_ids = players
301            .iter()
302            .enumerate()
303            .map(move |(player_index, player)| (player.id.to_owned(), player_index))
304            .collect();
305        let input_log = FastHashMap::from_iter(
306            players
307                .iter()
308                .enumerate()
309                .map(|(index, _)| (index, FastHashMap::new())),
310        );
311
312        let effect_manager = EffectManager::new();
313        let linked_effects_manager = LinkedEffectsManager::new();
314
315        let mut battle = Self {
316            log,
317            prng,
318            dex,
319            queue,
320            faint_queue,
321            catch_queue,
322            engine_options,
323            format,
324            field,
325            sides: [side_1, side_2],
326            players,
327            effect_manager,
328            linked_effects_manager,
329            registry,
330            player_ids,
331            effect_handle_cache: FastHashMap::new(),
332            turn: 0,
333            request: None,
334            mid_turn: false,
335            started: false,
336            in_pre_battle: false,
337            ending: false,
338            ended: false,
339            next_ability_order: 0,
340            next_forfeit_order: 0,
341            last_move_log: None,
342            last_exited: None,
343            input_log,
344            _pin: PhantomPinned,
345        };
346        Self::initialize(&mut battle.context())?;
347        Ok(battle)
348    }
349}
350
351// Block for all basic getters.
352impl<'d> CoreBattle<'d> {
353    pub fn context<'b>(&'b mut self) -> Context<'b, 'd> {
354        Context::new(self)
355    }
356
357    pub fn side_indices(&self) -> impl Iterator<Item = usize> {
358        0..self.sides.len()
359    }
360
361    pub fn sides(&self) -> impl Iterator<Item = &Side> {
362        self.sides.iter()
363    }
364
365    pub fn sides_mut(&mut self) -> impl Iterator<Item = &mut Side> {
366        self.sides.iter_mut()
367    }
368
369    pub fn side(&self, side: usize) -> Result<&Side, Error> {
370        self.sides
371            .get(side)
372            .wrap_not_found_error_with_format(format_args!("side {side}"))
373    }
374
375    pub fn side_mut(&mut self, side: usize) -> Result<&mut Side, Error> {
376        self.sides
377            .get_mut(side)
378            .wrap_not_found_error_with_format(format_args!("side {side}"))
379    }
380
381    pub fn player_indices(&self) -> impl Iterator<Item = usize> {
382        0..self.players.len()
383    }
384
385    pub fn players(&self) -> impl Iterator<Item = &Player> {
386        self.players.iter()
387    }
388
389    pub fn players_mut(&mut self) -> impl Iterator<Item = &mut Player> {
390        self.players.iter_mut()
391    }
392
393    pub fn player(&self, player: usize) -> Result<&Player, Error> {
394        self.players
395            .get(player)
396            .wrap_not_found_error_with_format(format_args!("player {player}"))
397    }
398
399    pub fn player_mut(&mut self, player: usize) -> Result<&mut Player, Error> {
400        self.players
401            .get_mut(player)
402            .wrap_not_found_error_with_format(format_args!("player {player}"))
403    }
404
405    pub fn player_indices_on_side<'b>(&'b self, side: usize) -> impl Iterator<Item = usize> + 'b {
406        (0..self.players.len()).filter(move |player| {
407            self.players
408                .get(*player)
409                .is_some_and(|player| player.side == side)
410        })
411    }
412
413    pub fn players_on_side(&self, side: usize) -> impl Iterator<Item = &Player> {
414        self.players().filter(move |player| player.side == side)
415    }
416
417    fn player_index_by_id(&self, player_id: &str) -> Result<usize, Error> {
418        self.player_ids
419            .get(player_id)
420            .wrap_not_found_error_with_format(format_args!("player {player_id}"))
421            .cloned()
422    }
423
424    pub unsafe fn mon<'b>(&'b self, mon_handle: MonHandle) -> Result<ElementRef<'b, Mon>, Error> {
425        self.registry.mon(mon_handle)
426    }
427
428    pub unsafe fn mon_mut<'b>(
429        &'b self,
430        mon_handle: MonHandle,
431    ) -> Result<ElementRefMut<'b, Mon>, Error> {
432        self.registry.mon_mut(mon_handle)
433    }
434
435    pub unsafe fn active_move<'b>(
436        &'b self,
437        move_handle: MoveHandle,
438    ) -> Result<ElementRef<'b, Move>, Error> {
439        self.registry.active_move(move_handle)
440    }
441
442    pub unsafe fn active_move_mut<'b>(
443        &'b self,
444        move_handle: MoveHandle,
445    ) -> Result<ElementRefMut<'b, Move>, Error> {
446        self.registry.active_move_mut(move_handle)
447    }
448
449    pub fn all_mon_handles<'b>(&'b self) -> impl Iterator<Item = MonHandle> + 'b {
450        self.sides()
451            .map(|side| self.all_mon_handles_on_side(side.index))
452            .flatten()
453    }
454
455    pub fn all_mon_handles_on_side<'b>(
456        &'b self,
457        side: usize,
458    ) -> impl Iterator<Item = MonHandle> + 'b {
459        self.players_on_side(side)
460            .map(|player| player.mons.iter())
461            .flatten()
462            .cloned()
463    }
464
465    fn active_positions_on_side<'b>(
466        &'b self,
467        side: usize,
468    ) -> impl Iterator<Item = Option<MonHandle>> + 'b {
469        self.players_on_side(side)
470            .map(|player| player.field_positions().map(|(_, pos)| pos.cloned()))
471            .flatten()
472    }
473
474    pub fn active_mon_handles_on_side<'b>(
475        &'b self,
476        side: usize,
477    ) -> impl Iterator<Item = MonHandle> + 'b {
478        self.active_positions_on_side(side)
479            .filter_map(|position| position)
480    }
481
482    pub fn all_active_mon_handles<'b>(&'b self) -> impl Iterator<Item = MonHandle> + 'b {
483        self.side_indices()
484            .map(|side| self.active_mon_handles_on_side(side))
485            .flatten()
486    }
487
488    pub fn all_active_mon_handles_in_speed_order(
489        context: &mut Context,
490    ) -> Result<Vec<MonHandle>, Error> {
491        let active_mons = context
492            .battle()
493            .all_active_mon_handles()
494            .collect::<Vec<_>>();
495        let mut active_mons_with_speed = Vec::with_capacity(active_mons.len());
496        for mon in active_mons {
497            active_mons_with_speed.push(Mon::speed_orderable(&context.mon_context(mon)?));
498        }
499        Self::speed_sort(context, active_mons_with_speed.as_mut_slice());
500        Ok(active_mons_with_speed
501            .into_iter()
502            .map(|mon| mon.mon_handle)
503            .collect())
504    }
505
506    pub fn next_ability_order(&mut self) -> u32 {
507        let next = self.next_ability_order;
508        self.next_ability_order += 1;
509        next
510    }
511
512    pub fn next_forfeit_order(&mut self) -> u32 {
513        let next = self.next_forfeit_order;
514        self.next_forfeit_order += 1;
515        next
516    }
517
518    pub fn side_length(&self, side: &Side) -> usize {
519        self.players_on_side(side.index).count() * self.format.battle_type.active_per_player()
520    }
521
522    // A.k.a., `mons_per_side`.
523    pub fn max_side_length(&self) -> usize {
524        self.sides()
525            .map(|side| self.side_length(side))
526            .max()
527            .unwrap_or(0)
528    }
529
530    pub fn turn(&self) -> u64 {
531        self.turn
532    }
533}
534
535// Block for methods that are only called from the public interface.
536impl<'d> CoreBattle<'d> {
537    fn has_new_logs(&self) -> bool {
538        self.log.has_new_messages()
539    }
540
541    fn all_logs(&self) -> impl Iterator<Item = &str> {
542        self.log.logs()
543    }
544
545    fn new_logs(&mut self) -> impl Iterator<Item = &str> {
546        self.log.read_out()
547    }
548
549    fn start(&mut self) -> Result<(), Error> {
550        Self::start_internal(&mut self.context())
551    }
552
553    fn ready_to_continue(&mut self) -> Result<bool, Error> {
554        Self::all_player_choices_done(&mut self.context())
555    }
556
557    fn continue_battle(&mut self) -> Result<(), Error> {
558        if !self.ready_to_continue()? {
559            return Err(general_error("battle is not ready to continue"));
560        }
561        Self::continue_battle_internal(&mut self.context())
562    }
563
564    fn player_data(&mut self, player: &str) -> Result<PlayerBattleData, Error> {
565        let player = self.player_index_by_id(player)?;
566        Player::request_data(&mut self.context().player_context(player)?)
567    }
568
569    fn active_requests<'b>(&'b self) -> impl Iterator<Item = (String, Request)> + 'b {
570        self.players().filter_map(|player| {
571            player
572                .active_request()
573                .map(|request| (player.id.to_owned(), request))
574        })
575    }
576
577    fn request_for_player(&self, player: &str) -> Option<Request> {
578        let player = self.player_index_by_id(player).ok()?;
579        self.player(player).ok()?.active_request()
580    }
581
582    fn set_player_choice(&mut self, player_id: &str, input: &str) -> Result<(), Error> {
583        Self::set_player_choice_internal(&mut self.context(), player_id, input)
584    }
585}
586
587impl<'d> CoreBattle<'d> {
588    pub fn log_private_public(&mut self, side: usize, private: Event, public: Event) {
589        self.log
590            .push_extend([log_event!("split", ("side", side)), private, public])
591    }
592
593    pub fn log(&mut self, event: Event) {
594        self.log.push(event)
595    }
596
597    pub fn log_many<I>(&mut self, events: I)
598    where
599        I: IntoIterator<Item = Event>,
600    {
601        self.log.push_extend(events)
602    }
603
604    pub fn log_move(&mut self, event: Event) {
605        self.last_move_log = Some(self.log.len());
606        self.log(event)
607    }
608
609    pub fn add_attribute_to_last_move(&mut self, attribute: &str) {
610        if let Some(EventLogEntryMut::Uncommitted(event)) =
611            self.last_move_log.and_then(|index| self.log.get_mut(index))
612        {
613            event.add_flag(attribute);
614            if attribute == "noanim" {
615                event.remove("target");
616                event.remove("spread");
617            }
618        }
619    }
620
621    pub fn add_attribute_value_to_last_move(&mut self, attribute: &str, value: String) {
622        if let Some(EventLogEntryMut::Uncommitted(event)) =
623            self.last_move_log.and_then(|index| self.log.get_mut(index))
624        {
625            event.set(attribute, value);
626        }
627    }
628
629    pub fn remove_attribute_from_last_move(&mut self, attribute: &str) {
630        if let Some(EventLogEntryMut::Uncommitted(event)) =
631            self.last_move_log.and_then(|index| self.log.get_mut(index))
632        {
633            event.remove(attribute);
634        }
635    }
636
637    pub fn started(&self) -> bool {
638        self.started
639    }
640
641    pub fn ending(&self) -> bool {
642        self.ending
643    }
644
645    pub fn ended(&self) -> bool {
646        self.ended
647    }
648
649    fn initialize(context: &mut Context) -> Result<(), Error> {
650        for player in 0..context.battle().players.len() {
651            let mut context = context.player_context(player)?;
652            Player::set_index(&mut context, player)?;
653        }
654        let mon_handles = context.battle().all_mon_handles().collect::<Vec<_>>();
655        for mon_handle in mon_handles {
656            let mut context = context.mon_context(mon_handle)?;
657            Mon::initialize(&mut context)?;
658        }
659        Ok(())
660    }
661
662    fn start_internal(context: &mut Context) -> Result<(), Error> {
663        if context.battle().started {
664            return Err(general_error("battle already started"));
665        }
666        context.battle_mut().started = true;
667        context.battle_mut().in_pre_battle = true;
668
669        let battle_type_event =
670            log_event!("info", ("battletype", &context.battle().format.battle_type));
671        context.battle_mut().log(battle_type_event);
672
673        // Extract and sort all rule logs.
674        //
675        // We sort to keep the battle log stable.
676        let mut rule_logs = context
677            .battle()
678            .format
679            .rules
680            .clauses(&context.battle().dex)
681            .filter_map(|clause| {
682                clause.data.rule_log.as_ref().map(|rule_log| {
683                    let value = context
684                        .battle()
685                        .format
686                        .rules
687                        .value(clause.id())
688                        .wrap_expectation_with_format(format_args!(
689                            "expected {} to be present in ruleset",
690                            clause.data.name
691                        ))?;
692                    let rule_log = rule_log.replace("{}", value);
693                    Ok(rule_log)
694                })
695            })
696            .collect::<Result<Vec<_>, Error>>()?;
697        rule_logs.sort();
698        context.battle_mut().log_many(
699            rule_logs
700                .into_iter()
701                .map(|rule_log| log_event!("info", ("rule", rule_log))),
702        );
703
704        let side_logs = context
705            .battle()
706            .sides()
707            .map(|side| log_event!("side", ("id", side.index), ("name", &side.name)))
708            .collect::<Vec<_>>();
709        context.battle_mut().log_many(side_logs);
710
711        if context.battle().format.battle_type.can_have_uneven_sides() {
712            let event = log_event!(
713                "maxsidelength",
714                ("length", context.battle().max_side_length())
715            );
716            context.battle_mut().log(event);
717        }
718
719        // Before reporting player positions, shift players to try and center them as appropriate.
720        for side in context.battle().side_indices() {
721            let mut context = context.side_context(side)?;
722            let players_on_side = context
723                .battle()
724                .players_on_side(context.side().index)
725                .count();
726            let players_on_foe_side = context
727                .battle()
728                .players_on_side(context.foe_side().index)
729                .count();
730            if players_on_foe_side > players_on_side {
731                let shift_players_right_by = (players_on_foe_side - players_on_side) / 2;
732                if shift_players_right_by > 0 {
733                    for player in context
734                        .battle()
735                        .player_indices_on_side(context.side().index)
736                        .collect::<Vec<_>>()
737                    {
738                        context
739                            .as_battle_context_mut()
740                            .player_context(player)?
741                            .player_mut()
742                            .position += shift_players_right_by;
743                    }
744                }
745            }
746        }
747
748        let player_logs = context
749            .battle()
750            .players()
751            .map(|player| {
752                log_event!(
753                    "player",
754                    ("id", &player.id),
755                    ("name", &player.name),
756                    ("side", player.side),
757                    ("position", player.position),
758                )
759            })
760            .collect::<Vec<_>>();
761        context.battle_mut().log_many(player_logs);
762
763        if context.battle().has_team_preview() {
764            context.battle_mut().log_team_sizes();
765            Self::start_team_preview(context)?;
766        }
767
768        BattleQueue::add_action(context, Action::Start)?;
769        context.battle_mut().mid_turn = true;
770
771        if context.battle().request.is_none() && context.battle().engine_options.auto_continue {
772            Self::continue_battle_internal(context)?;
773        }
774
775        Ok(())
776    }
777
778    fn time_now(&self) -> u128 {
779        SystemTime::now()
780            .duration_since(UNIX_EPOCH)
781            .unwrap()
782            .as_millis()
783    }
784
785    fn log_current_time(&mut self) {
786        self.log(log_event!("time", ("value", self.time_now())));
787    }
788
789    fn log_team_sizes(&mut self) {
790        let team_size_events = self
791            .players()
792            .filter(|player| !player.player_type.wild())
793            .map(|player| {
794                log_event!(
795                    "teamsize",
796                    ("player", &player.id),
797                    ("size", player.mons.len()),
798                )
799            })
800            .collect::<Vec<_>>();
801        self.log_many(team_size_events);
802    }
803
804    fn has_team_preview(&self) -> bool {
805        self.format.rules.has_rule(&Id::from_known("teampreview"))
806    }
807
808    fn start_team_preview(context: &mut Context) -> Result<(), Error> {
809        context.battle_mut().log(log_event!("teampreviewstart"));
810        let events = context
811            .battle()
812            .all_mon_handles()
813            .collect::<Vec<_>>()
814            .into_iter()
815            .map(|mon_handle| {
816                let context = context.mon_context(mon_handle)?;
817                Ok(log_event!(
818                    "mon",
819                    ("player", &context.player().id),
820                    Mon::public_details(&context)?,
821                ))
822            })
823            .collect::<Result<Vec<_>, Error>>()?;
824        context.battle_mut().log_many(events);
825        match context.battle().format.rules.numeric_rules.picked_team_size {
826            Some(picked_team_size) => context
827                .battle_mut()
828                .log(log_event!("teampreview", ("pick", picked_team_size))),
829            None => context.battle_mut().log(log_event!("teampreview")),
830        }
831        Self::make_request(context, RequestType::TeamPreview)?;
832        Ok(())
833    }
834
835    fn get_request_for_player(
836        context: &mut Context,
837        player: usize,
838        request_type: RequestType,
839    ) -> Result<Option<Request>, Error> {
840        match request_type {
841            RequestType::TeamPreview => {
842                let max_team_size = context
843                    .battle()
844                    .format
845                    .rules
846                    .numeric_rules
847                    .picked_team_size
848                    .map(|size| size as usize);
849                Ok(Some(Request::TeamPreview(TeamPreviewRequest {
850                    max_team_size,
851                })))
852            }
853            RequestType::Turn => {
854                let mut context = context.player_context(player)?;
855                let active = context
856                    .player()
857                    .active_mon_handles()
858                    .cloned()
859                    .collect::<Vec<_>>()
860                    .into_iter()
861                    .map(|mon| {
862                        let mut context = context.mon_context(mon)?;
863                        Mon::move_request(&mut context)
864                    })
865                    .collect::<Result<Vec<_>, _>>()?;
866                let ally_indices = context
867                    .battle()
868                    .player_indices_on_side(context.side().index)
869                    .filter(|player| *player != context.player().index)
870                    .collect::<Vec<_>>();
871                let mut allies = Vec::with_capacity(ally_indices.len());
872                for player in ally_indices {
873                    let mut context = context.as_battle_context_mut().player_context(player)?;
874                    allies.push(Player::request_data(&mut context)?);
875                }
876                Ok(Some(Request::Turn(TurnRequest { active, allies })))
877            }
878            RequestType::Switch => {
879                // We only make a request if there are Mons that need to switch out.
880                let context = context.player_context(player)?;
881                if Player::mons_left(&context)? == 0 {
882                    return Ok(None);
883                }
884                let mut needs_switch = Vec::new();
885                for (slot, mon) in context.player().field_positions_with_active_or_exited_mon() {
886                    if context.mon(*mon)?.needs_switch.is_some() {
887                        needs_switch.push(slot);
888                    }
889                }
890                if !needs_switch.is_empty() {
891                    Ok(Some(Request::Switch(SwitchRequest { needs_switch })))
892                } else {
893                    Ok(None)
894                }
895            }
896            RequestType::LearnMove => {
897                let mut context = context.player_context(player)?;
898                let mut learn_move_request = None;
899                for mon in context.player().mon_handles().cloned().collect::<Vec<_>>() {
900                    if let Some(request) = Mon::learn_move_request(&mut context.mon_context(mon)?)?
901                    {
902                        learn_move_request = Some(request);
903                        break;
904                    }
905                }
906                match learn_move_request {
907                    Some(request) => Ok(Some(Request::LearnMove(LearnMoveRequest {
908                        can_learn_move: request,
909                    }))),
910                    None => Ok(None),
911                }
912            }
913        }
914    }
915
916    fn set_player_choice_internal(
917        context: &mut Context,
918        player_id: &str,
919        input: &str,
920    ) -> Result<(), Error> {
921        let player = context.battle().player_index_by_id(player_id)?;
922        Player::make_choice(&mut context.player_context(player)?, input)?;
923        let turn = context.battle().turn;
924        context
925            .battle_mut()
926            .input_log
927            .get_mut(&player)
928            .wrap_not_found_error_with_format(format_args!("input_log for player {player}"))?
929            .insert(turn, input.to_owned());
930
931        if context.battle().engine_options.auto_continue && Self::all_player_choices_done(context)?
932        {
933            Self::commit_choices(context)?;
934        }
935
936        Ok(())
937    }
938
939    fn make_request(context: &mut Context, request_type: RequestType) -> Result<(), Error> {
940        context.battle_mut().log.commit();
941        Self::clear_requests(context)?;
942        context.battle_mut().request = Some(request_type);
943
944        for player in 0..context.battle().players.len() {
945            if let Some(request) = Self::get_request_for_player(context, player, request_type)? {
946                let mut context = context.player_context(player)?;
947                context.player_mut().make_request(request);
948            }
949        }
950        Ok(())
951    }
952
953    fn all_player_choices_done(context: &mut Context) -> Result<bool, Error> {
954        for player in 0..context.battle().players.len() {
955            let mut context = context.player_context(player)?;
956            if !Player::choice_done(&mut context)? {
957                return Ok(false);
958            }
959        }
960        Ok(true)
961    }
962
963    fn clear_requests(context: &mut Context) -> Result<(), Error> {
964        context.battle_mut().request = None;
965        for player in 0..context.battle().players.len() {
966            let mut context = context.player_context(player)?;
967            context.player_mut().clear_request();
968            Player::clear_choice(&mut context);
969        }
970        Ok(())
971    }
972
973    fn commit_choices(context: &mut Context) -> Result<(), Error> {
974        // Take all player actions and insert them into the battle queue.
975        let choices = context
976            .battle_mut()
977            .players_mut()
978            .map(|player| player.take_choice())
979            .collect::<Vec<_>>();
980        for choice in choices {
981            BattleQueue::add_actions(context, choice.actions.into_iter())?;
982        }
983        Self::clear_requests(context)?;
984
985        if context.battle().engine_options.auto_continue {
986            Self::continue_battle_internal(context)?;
987        }
988        Ok(())
989    }
990
991    fn continue_battle_internal(context: &mut Context) -> Result<(), Error> {
992        if !context.battle().engine_options.auto_continue {
993            if !Self::all_player_choices_done(context)? {
994                return Err(general_error(
995                    "cannot continue: all players have not made their choice",
996                ));
997            }
998            Self::commit_choices(context)?;
999        }
1000
1001        context.battle_mut().log_current_time();
1002
1003        context.battle_mut().request = None;
1004
1005        if !context.battle().mid_turn {
1006            BattleQueue::add_action(context, Action::BeforeTurn)?;
1007            BattleQueue::add_action(context, Action::Residual)?;
1008            context.battle_mut().mid_turn = true;
1009        }
1010
1011        // Sort the new actions and continue the battle.
1012        BattleQueue::sort(context);
1013
1014        // Run actions as long as possible.
1015        while let Some(action) = context.battle_mut().queue.pop_front() {
1016            Self::run_action(context, action)?;
1017            // This action initiated some request or ended the battle.
1018            if context.battle().request.is_some() || context.battle().ended {
1019                return Ok(());
1020            }
1021        }
1022
1023        // We must drop all borrowed state before moving to the next turn.
1024        context.clear_context_cache();
1025        Self::next_turn(context)?;
1026        context.battle_mut().mid_turn = false;
1027        Ok(())
1028    }
1029
1030    fn run_action(context: &mut Context, action: Action) -> Result<(), Error> {
1031        // Actions don't matter anymore if the battle ended.
1032        if context.battle().ended {
1033            return Ok(());
1034        }
1035
1036        match &action {
1037            Action::Start => {
1038                context.battle_mut().log_team_sizes();
1039                context.battle_mut().in_pre_battle = false;
1040
1041                context.battle_mut().log(log_event!("start"));
1042
1043                let player_switch_in_orders = context
1044                    .battle()
1045                    .players()
1046                    .sorted_by(|a, b| match (a.player_type.wild(), b.player_type.wild()) {
1047                        (true, false) => Ordering::Less,
1048                        (false, true) => Ordering::Greater,
1049                        _ => Ordering::Equal,
1050                    })
1051                    .map(|player| player.index)
1052                    .collect::<Vec<_>>();
1053                for player in player_switch_in_orders {
1054                    let mut context = context.player_context(player)?;
1055                    let field_positions = context
1056                        .player()
1057                        .field_positions()
1058                        .map(|(pos, _)| pos)
1059                        .collect::<Vec<_>>();
1060                    for (mon, position) in Player::switchable_mon_handles(&context)
1061                        .cloned()
1062                        .zip(field_positions.into_iter())
1063                        .collect::<Vec<_>>()
1064                    {
1065                        let mut context = context.mon_context(mon)?;
1066                        core_battle_actions::switch_in(&mut context, position, None, false)?;
1067                    }
1068                }
1069
1070                // TODO: Start event for species. Some forms changes happen at the very beginning of
1071                // the battle.
1072
1073                // Clears the weather, which then sets the default weather.
1074                core_battle_actions::clear_weather(&mut context.field_effect_context(
1075                    EffectHandle::Condition(Id::from_known("start")),
1076                    None,
1077                    None,
1078                )?)?;
1079
1080                // Clears the terrain, which then sets the default terrain.
1081                core_battle_actions::clear_terrain(&mut context.field_effect_context(
1082                    EffectHandle::Condition(Id::from_known("start")),
1083                    None,
1084                    None,
1085                )?)?;
1086
1087                context.battle_mut().mid_turn = true;
1088            }
1089            Action::End(action) => {
1090                core_battle_effects::run_event_for_each_active_mon(
1091                    context,
1092                    fxlang::BattleEvent::EndBattle,
1093                )?;
1094                Self::win(context, action.winning_side)?;
1095            }
1096            Action::Team(action) => {
1097                let mut context = context.mon_context(action.mon_action.mon)?;
1098                if action.index == 0 {
1099                    context.player_mut().mons.clear();
1100                }
1101                context.mon_mut().team_position = action.index;
1102                context.player_mut().mons.push(action.mon_action.mon);
1103            }
1104            Action::Switch(action) => {
1105                let mut context = context.mon_context(action.mon_action.mon)?;
1106                core_battle_actions::switch_in(&mut context, action.position, None, false)?;
1107            }
1108            Action::SwitchEvents(action) => {
1109                let mut context = context.mon_context(action.mon_action.mon)?;
1110                core_battle_actions::run_switch_in_events(&mut context)?;
1111            }
1112            Action::Move(action) => {
1113                let mut context = context.mon_context(action.mon_action.mon)?;
1114                if !context.mon().active || !context.mon().active {
1115                    return Ok(());
1116                }
1117                core_battle_actions::do_move(
1118                    &mut context,
1119                    action
1120                        .active_move_handle
1121                        .wrap_expectation("expected move action to have an active move")?,
1122                    action.target,
1123                    action.original_target,
1124                )?;
1125            }
1126            Action::BeforeTurnMove(action) => {
1127                let mut context = context.mon_context(action.mon_action.mon)?;
1128                if !context.mon().active || !context.mon().active {
1129                    return Ok(());
1130                }
1131                core_battle_effects::run_applying_effect_event(
1132                    &mut context.applying_effect_context(
1133                        EffectHandle::InactiveMove(action.id.clone()),
1134                        None,
1135                        None,
1136                    )?,
1137                    fxlang::BattleEvent::BeforeTurn,
1138                    fxlang::VariableInput::default(),
1139                );
1140            }
1141            Action::PriorityChargeMove(action) => {
1142                let mut context = context.mon_context(action.mon_action.mon)?;
1143                if !context.mon().active || !context.mon().active {
1144                    return Ok(());
1145                }
1146                core_battle_effects::run_applying_effect_event(
1147                    &mut context.applying_effect_context(
1148                        EffectHandle::InactiveMove(action.id.clone()),
1149                        None,
1150                        None,
1151                    )?,
1152                    fxlang::BattleEvent::PriorityChargeMove,
1153                    fxlang::VariableInput::default(),
1154                );
1155            }
1156            Action::MegaEvo(_) => todo!("mega evolution is not implemented"),
1157            Action::Pass => (),
1158            Action::BeforeTurn => {
1159                for mon_handle in context
1160                    .battle()
1161                    .all_active_mon_handles()
1162                    .collect::<Vec<_>>()
1163                {
1164                    let foe_side = context.mon_context(mon_handle)?.foe_side().index;
1165                    for foe_handle in context
1166                        .battle()
1167                        .active_mon_handles_on_side(foe_side)
1168                        .collect::<Vec<_>>()
1169                    {
1170                        context
1171                            .mon_mut(foe_handle)?
1172                            .foes_fought_while_active
1173                            .insert(mon_handle);
1174                    }
1175                }
1176            }
1177            Action::Residual => {
1178                Self::clear_all_active_moves(context)?;
1179                Self::update_speed(context)?;
1180                core_battle_effects::run_event_for_residual(context, fxlang::BattleEvent::Residual);
1181                context.battle_mut().log(log_event!("residual"));
1182            }
1183            Action::Experience(action) => {
1184                core_battle_actions::gain_experience(
1185                    &mut context.mon_context(action.mon)?,
1186                    action.exp,
1187                )?;
1188            }
1189            Action::LevelUp(action) => {
1190                let mut context = context.mon_context(action.mon)?;
1191                let target_level = action.level.unwrap_or(context.mon().level + 1);
1192                core_battle_actions::level_up(&mut context, target_level)?;
1193            }
1194            Action::LearnMove(action) => {
1195                let mut context = context.mon_context(action.mon)?;
1196                let request = Mon::learn_move_request(&mut context)?.wrap_expectation_with_format(format_args!("mon {} has no move to learn, even though we allowed the player to choose to learn a move", context.mon().name))?;
1197                Mon::learn_move(&mut context, &request.id, action.forget_move_slot)?;
1198            }
1199            Action::Escape(action) => {
1200                core_battle_actions::try_escape(
1201                    &mut context.mon_context(action.mon_action.mon)?,
1202                    false,
1203                )?;
1204            }
1205            Action::Forfeit(action) => {
1206                core_battle_actions::forfeit(&mut context.player_context(action.player)?)?;
1207            }
1208            Action::Item(action) => {
1209                core_battle_actions::player_use_item(
1210                    &mut context.mon_context(action.mon_action.mon)?,
1211                    &action.item,
1212                    action.target,
1213                    core_battle_actions::PlayerUseItemInput {
1214                        move_slot: action.move_slot.clone(),
1215                    },
1216                )?;
1217            }
1218        }
1219
1220        Self::after_action(context)?;
1221        Ok(())
1222    }
1223
1224    fn after_action(context: &mut Context) -> Result<(), Error> {
1225        Self::faint_messages(context)?;
1226        Self::catch_messages(context)?;
1227
1228        let mut some_move_can_be_learned = false;
1229        for mon in context.battle().all_mon_handles().collect::<Vec<_>>() {
1230            let mon = context.mon(mon)?;
1231            if !mon.learnable_moves.is_empty() {
1232                some_move_can_be_learned = true;
1233                break;
1234            }
1235        }
1236        if some_move_can_be_learned {
1237            Self::make_request(context, RequestType::LearnMove)?;
1238            return Ok(());
1239        }
1240
1241        // Everything after this point does not matter if the battle is ending.
1242        if context.battle().ending {
1243            return Ok(());
1244        }
1245
1246        // Drag out any Mons in the place of force switches.
1247        let mons = context
1248            .battle()
1249            .all_active_mon_handles()
1250            .collect::<Vec<_>>();
1251        for mon in mons {
1252            let mut context = context.mon_context(mon)?;
1253            if context.mon().force_switch.is_some() && context.mon().hp > 0 {
1254                if let Some(position) = context.mon().active_position {
1255                    core_battle_actions::drag_in(context.as_player_context_mut(), position)?;
1256                }
1257            }
1258            context.mon_mut().force_switch = None;
1259        }
1260
1261        if context.battle().queue.is_empty() {
1262            // This sets that exited Mons must be switched out.
1263            //
1264            // We only do this at the end of the turn.
1265            Self::check_for_exited_mons(context)?;
1266        } else if let Some(Action::Switch(switch)) = context.battle().queue.peek() {
1267            // Instant switches should happen... instantly.
1268            if switch.instant {
1269                return Ok(());
1270            }
1271        }
1272
1273        // TODO: Update speed dynamically, if we wish to support it like gen 8 does.
1274
1275        Self::update(context)?;
1276
1277        let mut some_switch_needed = false;
1278        for player in context.battle().player_indices() {
1279            let mut context = context.player_context(player)?;
1280            let needs_switch = Player::needs_switch(&context)?;
1281            let can_switch = Player::can_switch(&context);
1282            if needs_switch {
1283                if !can_switch {
1284                    // Switch can't happen, so unset the switch flag.
1285                    for mon in context
1286                        .player()
1287                        .active_or_exited_mon_handles()
1288                        .cloned()
1289                        .collect::<Vec<_>>()
1290                        .into_iter()
1291                    {
1292                        context.mon_mut(mon)?.needs_switch = None;
1293                    }
1294                } else {
1295                    // Switch out will occur mid turn.
1296                    //
1297                    // Run BeforeSwitchOut event now. This will make sure actions like Pursuit
1298                    // trigger on the same turn, rather than the next turn.
1299                    for mon in context
1300                        .player()
1301                        .active_or_exited_mon_handles()
1302                        .cloned()
1303                        .collect::<Vec<_>>()
1304                    {
1305                        let mut context = context.mon_context(mon)?;
1306                        if context.mon().needs_switch.is_some() {
1307                            if !context.mon().skip_before_switch_out {
1308                                core_battle_effects::run_event_for_mon(
1309                                    &mut context,
1310                                    fxlang::BattleEvent::BeforeSwitchOut,
1311                                    fxlang::VariableInput::default(),
1312                                );
1313                            }
1314
1315                            context.mon_mut().skip_before_switch_out = true;
1316
1317                            // Mon may have fainted here.
1318                            Self::faint_messages(context.as_battle_context_mut())?;
1319                            if context.battle().ending {
1320                                return Ok(());
1321                            }
1322                        }
1323                    }
1324
1325                    // At this point, maybe the Mon that was going to switch fainted, so we should
1326                    // double check if the player still needs a switch.
1327                    some_switch_needed = some_switch_needed || Player::needs_switch(&context)?;
1328                }
1329            }
1330        }
1331
1332        if some_switch_needed {
1333            Self::make_request(context, RequestType::Switch)?;
1334            return Ok(());
1335        }
1336
1337        Ok(())
1338    }
1339
1340    fn check_for_exited_mons(context: &mut Context) -> Result<(), Error> {
1341        for player in context.battle().player_indices() {
1342            let mut context = context.player_context(player)?;
1343            for mon in context
1344                .player()
1345                .active_or_exited_mon_handles()
1346                .cloned()
1347                .collect::<Vec<_>>()
1348            {
1349                let mut context = context.mon_context(mon)?;
1350                if context.mon().exited.is_some() {
1351                    context.mon_mut().needs_switch = Some(SwitchType::Normal);
1352                }
1353            }
1354        }
1355        Ok(())
1356    }
1357
1358    fn next_turn(context: &mut Context) -> Result<(), Error> {
1359        context.battle_mut().turn += 1;
1360
1361        if context.battle().turn >= 1000 {
1362            context.battle_mut().log(log_event!("turnlimit"));
1363            Self::schedule_tie(context)?;
1364            return Ok(());
1365        }
1366
1367        for mon_handle in context
1368            .battle()
1369            .all_active_mon_handles()
1370            .collect::<Vec<_>>()
1371        {
1372            let mut context = context.mon_context(mon_handle)?;
1373            Mon::reset_state_for_next_turn(&mut context)?;
1374
1375            if let Some(last_move) = context.mon().last_move {
1376                context
1377                    .battle()
1378                    .registry
1379                    .save_active_move_from_next_turn(last_move)?;
1380            }
1381            if let Some(last_move_used) = context.mon().last_move_used {
1382                context
1383                    .battle()
1384                    .registry
1385                    .save_active_move_from_next_turn(last_move_used)?;
1386            }
1387        }
1388
1389        context.battle_mut().registry.next_turn()?;
1390
1391        // TODO: Endless battle clause.
1392
1393        let turn_event = log_event!("turn", ("turn", context.battle().turn));
1394        context.battle_mut().log(turn_event);
1395
1396        Self::make_request(context, RequestType::Turn)?;
1397        Ok(())
1398    }
1399
1400    fn schedule_tie(context: &mut Context) -> Result<(), Error> {
1401        Self::schedule_win(context, None)
1402    }
1403
1404    fn schedule_win(context: &mut Context, mut side: Option<usize>) -> Result<(), Error> {
1405        if context.battle().ending {
1406            return Ok(());
1407        }
1408
1409        if side.is_none() {
1410            // Resolve ties, if possible, using the last Mon that exited.
1411            if let Some(last_exited) = context.battle().last_exited {
1412                side = Some(context.mon(last_exited)?.side);
1413            }
1414        }
1415        BattleQueue::insert_action_into_sorted_position(
1416            context,
1417            Action::End(EndAction { winning_side: side }),
1418        )?;
1419
1420        context.battle_mut().ending = true;
1421        Ok(())
1422    }
1423
1424    fn win(context: &mut Context, side: Option<usize>) -> Result<(), Error> {
1425        match side {
1426            Some(side) => {
1427                context.battle_mut().log(log_event!("win", ("side", side)));
1428            }
1429            None => {
1430                context.battle_mut().log(log_event!("tie"));
1431            }
1432        }
1433        context.battle_mut().ended = true;
1434        context.battle_mut().log.commit();
1435        Self::clear_requests(context)?;
1436        Ok(())
1437    }
1438
1439    fn calculate_action_priority(context: &mut Context, action: &mut Action) -> Result<(), Error> {
1440        if let Action::Move(action) = action {
1441            let mov = context.battle().dex.moves.get_by_id(&action.id)?;
1442            let priority = mov.data.priority as i32;
1443
1444            let mut context = context.mon_context(action.mon_action.mon)?;
1445            let mut context =
1446                context.active_move_context(action.active_move_handle.wrap_expectation(
1447                    "expected active move to exist on action for priority calculation",
1448                )?)?;
1449            let mut context = context.user_applying_effect_context(None)?;
1450
1451            let priority = core_battle_effects::run_event_for_applying_effect_expecting_i32(
1452                &mut context,
1453                fxlang::BattleEvent::ModifyPriority,
1454                priority,
1455            );
1456            action.priority = priority;
1457
1458            action.sub_priority = core_battle_effects::run_event_for_applying_effect_expecting_i32(
1459                &mut context,
1460                fxlang::BattleEvent::SubPriority,
1461                0,
1462            );
1463        }
1464        if let Action::Switch(action) = action {
1465            // The priority of switch actions are determined by the speed of the Mon switching out.
1466            let mut context = context.mon_context(action.switching_out)?;
1467            action.mon_action.speed = Mon::action_speed(&mut context)? as u32;
1468        } else if let Some(mon_action) = action.mon_action_mut() {
1469            let mut context = context.mon_context(mon_action.mon)?;
1470            mon_action.speed = Mon::action_speed(&mut context)? as u32;
1471        }
1472        Ok(())
1473    }
1474
1475    pub fn register_active_move(
1476        context: &mut Context,
1477        active_move: Move,
1478    ) -> Result<MoveHandle, Error> {
1479        let active_move_handle = context.battle_mut().register_move(active_move);
1480        Ok(active_move_handle)
1481    }
1482
1483    pub fn register_active_move_by_id(
1484        context: &mut Context,
1485        move_id: &Id,
1486    ) -> Result<MoveHandle, Error> {
1487        let active_move = (*context.battle_mut().dex.moves.get_by_id(move_id)?).clone();
1488        Self::register_active_move(context, active_move)
1489    }
1490
1491    /// Resolves the given action by calculating its priority in the context of the battle.
1492    pub fn resolve_action(context: &mut Context, action: &mut Action) -> Result<(), Error> {
1493        if let Action::Move(action) = action {
1494            let mut context = context.mon_context(action.mon_action.mon)?;
1495            if let Some(target) = action.target {
1496                action.original_target = Mon::get_target(&mut context, target)?;
1497            }
1498            action.active_move_handle = Some(Self::register_active_move_by_id(
1499                context.as_battle_context_mut(),
1500                &action.id,
1501            )?);
1502        }
1503        Self::calculate_action_priority(context, action)?;
1504        Ok(())
1505    }
1506
1507    /// Selects a random switchable Mon from the player.
1508    pub fn random_switchable(
1509        context: &mut Context,
1510        player: usize,
1511    ) -> Result<Option<MonHandle>, Error> {
1512        let prng = context.battle_mut().prng.as_mut();
1513        // SAFETY: PRNG is completely disjoint from the iterator created below.
1514        let prng = unsafe { mem::transmute(prng) };
1515        Ok(rand_util::sample_iter(
1516            prng,
1517            Player::switchable_mon_handles(&context.player_context(player)?),
1518        )
1519        .cloned())
1520    }
1521
1522    /// Selects a random target for the move.
1523    pub fn random_target(
1524        context: &mut Context,
1525        mon: MonHandle,
1526        move_target: MoveTarget,
1527    ) -> Result<Option<MonHandle>, Error> {
1528        if move_target.can_target_user() {
1529            // Target the user if possible.
1530            return Ok(Some(mon));
1531        }
1532
1533        let mut context = context.mon_context(mon)?;
1534        let mons = if !move_target.can_target_foes() {
1535            // Cannot target foes, so only consider allies.
1536            Mon::adjacent_allies(&mut context)?.collect::<Vec<_>>()
1537        } else if move_target.is_adjacent_only() {
1538            // Consider adjacent foes. Allies are excluded, so that a move will never randomly
1539            // target an ally if it doesn't need to.
1540            Mon::adjacent_foes(&mut context)?.collect::<Vec<_>>()
1541        } else {
1542            // Consider all foes.
1543            Mon::active_foes(&mut context).collect::<Vec<_>>()
1544        };
1545
1546        let mons = mons
1547            .into_iter()
1548            .filter(|mon| {
1549                context
1550                    .as_battle_context()
1551                    .mon(*mon)
1552                    .is_ok_and(|mon| mon.hp > 0)
1553            })
1554            .collect::<Vec<_>>();
1555
1556        Ok(
1557            rand_util::sample_slice(context.battle_mut().prng.as_mut(), &mons)
1558                .cloned()
1559                .map(|mon| Some(mon))
1560                .unwrap_or(None),
1561        )
1562    }
1563
1564    /// Gets the selected target of the move.
1565    pub fn get_target(
1566        context: &mut Context,
1567        mon: MonHandle,
1568        move_id: &Id,
1569        target: Option<isize>,
1570        original_target: Option<MonHandle>,
1571    ) -> Result<Option<MonHandle>, Error> {
1572        let mov = context.battle().dex.moves.get_by_id(move_id)?;
1573        let tracks_target = mov.data.tracks_target;
1574        let move_target = mov.data.target.clone();
1575
1576        if tracks_target {
1577            if let Some(original_target) = original_target {
1578                let context = context.mon_context(original_target)?;
1579                if context.mon().active {
1580                    // Move's original target is on the field.
1581                    return Ok(Some(original_target));
1582                }
1583            }
1584        }
1585
1586        if let Some(target) = target {
1587            let mut context = context.mon_context(mon)?;
1588            if !move_target.is_random()
1589                && Self::valid_target(&mut context, move_target.clone(), target)?
1590            {
1591                if let Some(target_mon_handle) = Mon::get_target(&mut context, target)? {
1592                    let target_context = context
1593                        .as_battle_context_mut()
1594                        .mon_context(target_mon_handle)?;
1595                    if target_context.mon().active
1596                        || target_context
1597                            .mon()
1598                            .is_ally(target_context.as_battle_context().mon(mon)?)
1599                    {
1600                        // Target is unfainted or an ally, so the chosen target is still valid.
1601                        return Ok(Some(target_mon_handle));
1602                    }
1603                }
1604            }
1605        }
1606
1607        // The chosen target is not valid.
1608        if !move_target.requires_target() {
1609            Ok(None)
1610        } else {
1611            Self::random_target(context, mon, move_target)
1612        }
1613    }
1614
1615    /// Gets the selected target of the move.
1616    pub fn get_item_target(
1617        context: &mut MonContext,
1618        item: &Id,
1619        target: Option<isize>,
1620    ) -> Result<Option<MonHandle>, Error> {
1621        let item = context.battle().dex.items.get_by_id(item)?;
1622        let item_target = item.data.target.wrap_expectation("item is not usable")?;
1623
1624        match item_target {
1625            ItemTarget::Active => {
1626                return Ok(Some(context.mon_handle()));
1627            }
1628            ItemTarget::IsolatedFoe => {
1629                let foes = context
1630                    .battle()
1631                    .active_mon_handles_on_side(context.foe_side().index)
1632                    .collect::<Vec<_>>();
1633                if foes.len() != 1 {
1634                    return Ok(None);
1635                }
1636                return Ok(foes.first().cloned());
1637            }
1638            _ => (),
1639        }
1640
1641        if let Some(target) = target {
1642            if Self::valid_item_target(context.as_player_context_mut(), item_target, target)? {
1643                if let Some(target_mon_handle) =
1644                    Player::get_item_target(context.as_player_context_mut(), target)?
1645                {
1646                    return Ok(Some(target_mon_handle));
1647                }
1648            }
1649        }
1650
1651        Ok(None)
1652    }
1653
1654    /// Checks if the selected target position is valid for the move.
1655    pub fn valid_target(
1656        context: &mut MonContext,
1657        move_target: MoveTarget,
1658        target_location: isize,
1659    ) -> Result<bool, Error> {
1660        if target_location == 0 {
1661            return Err(general_error("target position cannot be 0"));
1662        }
1663        let target_side = if target_location > 0 {
1664            context.foe_side().index
1665        } else {
1666            context.side().index
1667        };
1668        let target_location = target_location.abs() as usize;
1669        let target_location = target_location - 1;
1670        if !Mon::relative_location_of_target(&context, target_side, target_location).map_or(
1671            false,
1672            |relative_location| {
1673                move_target.valid_target(
1674                    relative_location,
1675                    context.battle().format.options.adjacency_reach,
1676                )
1677            },
1678        ) {
1679            return Ok(false);
1680        }
1681        Ok(true)
1682    }
1683
1684    /// Checks if the selected target position is valid for the move.
1685    pub fn valid_item_target(
1686        context: &mut PlayerContext,
1687        item_target: ItemTarget,
1688        target_location: isize,
1689    ) -> Result<bool, Error> {
1690        match item_target {
1691            ItemTarget::Party => {
1692                if target_location >= 0 {
1693                    return Ok(false);
1694                }
1695                let team_slot = (-target_location) as usize;
1696                let team_slot = team_slot - 1;
1697                Ok(team_slot < context.player().mons.len())
1698            }
1699            ItemTarget::Active => Ok(false),
1700            ItemTarget::Foe => {
1701                if target_location <= 0 {
1702                    return Ok(false);
1703                }
1704                Ok(Side::mon_in_position(
1705                    &mut context.foe_side_context()?,
1706                    target_location as usize,
1707                )?
1708                .is_some())
1709            }
1710            ItemTarget::IsolatedFoe => {
1711                if target_location <= 0 {
1712                    return Ok(false);
1713                }
1714                if Side::active_mons_count(&mut context.foe_side_context()?) != 0 {
1715                    return Ok(false);
1716                }
1717                Ok(Side::mon_in_position(
1718                    &mut context.foe_side_context()?,
1719                    target_location as usize,
1720                )?
1721                .is_some())
1722            }
1723        }
1724    }
1725
1726    /// Registers a new active move, returning its handle.
1727    pub fn register_move(&mut self, mov: Move) -> MoveHandle {
1728        self.registry.register_move(mov)
1729    }
1730
1731    /// Clears all active moves for all Mons.
1732    pub fn clear_all_active_moves(context: &mut Context) -> Result<(), Error> {
1733        for mon in context
1734            .battle()
1735            .all_active_mon_handles()
1736            .collect::<Vec<_>>()
1737        {
1738            let mon = context.mon_mut(mon)?;
1739            mon.clear_active_move();
1740        }
1741        Ok(())
1742    }
1743
1744    /// Updates the speed of all Mons.
1745    pub fn update_speed(context: &mut Context) -> Result<(), Error> {
1746        for mon_handle in context
1747            .battle()
1748            .all_active_mon_handles()
1749            .collect::<Vec<_>>()
1750        {
1751            Mon::update_speed(&mut context.mon_context(mon_handle)?)?;
1752        }
1753        Ok(())
1754    }
1755
1756    /// Checks type immunity for several defensive types against an offensive type.
1757    pub fn check_type_immunity(&self, offense: Type, defense: &[Type]) -> bool {
1758        defense
1759            .iter()
1760            .map(|defense| {
1761                self.dex
1762                    .type_chart()
1763                    .types
1764                    .get(&offense)
1765                    .and_then(|row| row.get(&defense))
1766                    .unwrap_or(&TypeEffectiveness::Normal)
1767            })
1768            .any(|effectiveness| effectiveness == &TypeEffectiveness::None)
1769    }
1770
1771    /// Checks the type effectiveness of an offensive type against a defensive type.
1772    pub fn check_type_effectiveness(&self, offense: Type, defense: Type) -> i8 {
1773        match self
1774            .dex
1775            .type_chart()
1776            .types
1777            .get(&offense)
1778            .and_then(|row| row.get(&defense))
1779            .unwrap_or(&TypeEffectiveness::Normal)
1780        {
1781            TypeEffectiveness::Strong => 1,
1782            TypeEffectiveness::Weak => -1,
1783            _ => 0,
1784        }
1785    }
1786
1787    /// Randomizes damage, as part of the damage calculation formula.
1788    pub fn randomize_base_damage(&mut self, base_damage: u32) -> u32 {
1789        let random_factor = match self.engine_options.randomize_base_damage {
1790            CoreBattleEngineRandomizeBaseDamage::Randomize => {
1791                rand_util::range(self.prng.as_mut(), 0, 16) as u32
1792            }
1793            CoreBattleEngineRandomizeBaseDamage::Max => 0,
1794            CoreBattleEngineRandomizeBaseDamage::Min => 15,
1795        };
1796        base_damage * (100 - random_factor) / 100
1797    }
1798
1799    /// Logs all faint messages.
1800    ///
1801    /// A Mon is considered truly fainted only after this method runs.
1802    pub fn faint_messages(context: &mut Context) -> Result<(), Error> {
1803        if context.battle().ending {
1804            return Ok(());
1805        }
1806
1807        while let Some(entry) = context.battle_mut().faint_queue.pop_front() {
1808            let mut context = context.mon_context(entry.target)?;
1809            if !context.mon().active {
1810                continue;
1811            }
1812
1813            // TODO: BeforeFaint event.
1814            core_battle_logs::faint(&mut context)?;
1815
1816            let mon_handle = context.mon_handle();
1817            core_battle_actions::give_out_experience(context.as_battle_context_mut(), mon_handle)?;
1818
1819            match entry.effect.clone() {
1820                Some(effect) => core_battle_effects::run_event_for_applying_effect(
1821                    &mut context.applying_effect_context(effect, entry.source, None)?,
1822                    fxlang::BattleEvent::Faint,
1823                    fxlang::VariableInput::default(),
1824                ),
1825                None => core_battle_effects::run_event_for_mon(
1826                    &mut context,
1827                    fxlang::BattleEvent::Faint,
1828                    fxlang::VariableInput::default(),
1829                ),
1830            };
1831
1832            core_battle_effects::run_event_for_mon(
1833                &mut context,
1834                fxlang::BattleEvent::Exit,
1835                fxlang::VariableInput::default(),
1836            );
1837
1838            Mon::clear_state_on_exit(&mut context, MonExitType::Fainted)?;
1839            context.battle_mut().last_exited = Some(context.mon_handle());
1840        }
1841
1842        Self::check_win(context)?;
1843
1844        Ok(())
1845    }
1846
1847    /// Logs all catch messages.
1848    ///
1849    /// A Mon is considered truly caught only after this method runs.
1850    pub fn catch_messages(context: &mut Context) -> Result<(), Error> {
1851        while let Some(entry) = context.battle_mut().catch_queue.pop_front() {
1852            let mut context = context.mon_context(entry.target)?;
1853
1854            core_battle_logs::catch(
1855                &mut context
1856                    .as_battle_context_mut()
1857                    .player_context(entry.player)?,
1858                entry.target,
1859                &entry.item,
1860                entry.shakes,
1861                entry.critical,
1862            )?;
1863
1864            let mon_handle = context.mon_handle();
1865            core_battle_actions::give_out_experience(context.as_battle_context_mut(), mon_handle)?;
1866
1867            core_battle_effects::run_event_for_mon(
1868                &mut context,
1869                fxlang::BattleEvent::Exit,
1870                fxlang::VariableInput::default(),
1871            );
1872
1873            Mon::clear_state_on_exit(&mut context, MonExitType::Caught)?;
1874            context.battle_mut().last_exited = Some(context.mon_handle());
1875
1876            context.mon_mut().ball = context
1877                .battle()
1878                .dex
1879                .items
1880                .get_by_id(&entry.item)?
1881                .data
1882                .name
1883                .clone();
1884
1885            context
1886                .as_battle_context_mut()
1887                .player_context(entry.player)?
1888                .player_mut()
1889                .caught
1890                .push(entry.target);
1891        }
1892
1893        Self::check_win(context)?;
1894
1895        Ok(())
1896    }
1897
1898    /// Checks if anyone has won the battle.
1899    pub fn check_win(context: &mut Context) -> Result<(), Error> {
1900        if context.battle().in_pre_battle {
1901            return Ok(());
1902        }
1903
1904        let mut winner = None;
1905        for side in context.battle().side_indices() {
1906            let mut context = context.side_context(side)?;
1907            let mons_left = Side::mons_left(&mut context)?;
1908            if mons_left > 0 {
1909                if winner.is_some() {
1910                    return Ok(());
1911                }
1912                winner = Some(side);
1913            }
1914        }
1915        Self::schedule_win(context, winner)
1916    }
1917
1918    /// Gets an [`EffectHandle`] by name.
1919    pub fn get_effect_handle(&mut self, name: &str) -> Result<&EffectHandle, Error> {
1920        self.get_effect_handle_by_id(&Id::from(name))
1921    }
1922
1923    /// Gets an [`EffectHandle`] by ID.
1924    ///
1925    /// An [`Effect`] has many variants. An ID is not enough on its own to lookup a generic effect.
1926    /// For the duration of a battle, an ID will map to a single [`EffectHandle`]. This method
1927    /// handles the caching of this translation.
1928    pub fn get_effect_handle_by_id(&mut self, id: &Id) -> Result<&EffectHandle, Error> {
1929        if self.effect_handle_cache.contains_key(id) {
1930            return self
1931                .effect_handle_cache
1932                .get(id)
1933                .wrap_expectation("effect handle not found in cache after its key was found");
1934        }
1935
1936        let effect_handle = Self::lookup_effect_in_dex(self, id.clone());
1937        self.effect_handle_cache.insert(id.clone(), effect_handle);
1938        self.effect_handle_cache
1939            .get(id)
1940            .wrap_expectation("effect handle not found in cache after insertion")
1941    }
1942
1943    fn lookup_effect_in_dex(&self, id: Id) -> EffectHandle {
1944        if self.dex.conditions.get_by_id(&id).is_ok() {
1945            return EffectHandle::Condition(id);
1946        }
1947        if self.dex.moves.get_by_id(&id).is_ok() {
1948            return EffectHandle::MoveCondition(id);
1949        }
1950        if self.dex.abilities.get_by_id(&id).is_ok() {
1951            return EffectHandle::AbilityCondition(id);
1952        }
1953        if self.dex.items.get_by_id(&id).is_ok() {
1954            return EffectHandle::ItemCondition(id);
1955        }
1956        EffectHandle::NonExistent(id)
1957    }
1958
1959    /// Gets an [`Effect`] by handle.
1960    ///
1961    /// [`EffectHandle`] is considered a stable way to look up any effect in the dex.
1962    pub fn get_effect_by_handle<'context>(
1963        context: &'context Context,
1964        effect_handle: &EffectHandle,
1965    ) -> Result<Effect<'context>, Error> {
1966        match effect_handle {
1967            EffectHandle::ActiveMove(active_move_handle, hit_effect_type) => {
1968                Ok(Effect::for_active_move(
1969                    context.active_move_mut(*active_move_handle)?,
1970                    *hit_effect_type,
1971                ))
1972            }
1973            EffectHandle::MoveCondition(id) => Ok(Effect::for_move_condition(
1974                context.battle().dex.moves.get_by_id(id)?,
1975            )),
1976            EffectHandle::InactiveMove(id) => Ok(Effect::for_inactive_move(
1977                context.battle().dex.moves.get_by_id(id)?,
1978            )),
1979            EffectHandle::Ability(id) => Ok(Effect::for_ability(
1980                context.battle().dex.abilities.get_by_id(id)?,
1981            )),
1982            EffectHandle::AbilityCondition(id) => Ok(Effect::for_ability_condition(
1983                context.battle().dex.abilities.get_by_id(id)?,
1984            )),
1985            EffectHandle::Condition(id) => Ok(Effect::for_condition(
1986                context.battle().dex.conditions.get_by_id(id)?,
1987            )),
1988            EffectHandle::Item(id) => {
1989                Ok(Effect::for_item(context.battle().dex.items.get_by_id(id)?))
1990            }
1991            EffectHandle::ItemCondition(id) => Ok(Effect::for_item_condition(
1992                context.battle().dex.items.get_by_id(id)?,
1993            )),
1994            EffectHandle::NonExistent(id) => Ok(Effect::for_non_existent(id.clone())),
1995        }
1996    }
1997
1998    /// Gets an [`Effect`] by ID.
1999    pub fn get_effect_by_id<'context>(
2000        context: &'context mut Context,
2001        id: &Id,
2002    ) -> Result<Effect<'context>, Error> {
2003        let effect_handle = context.battle_mut().get_effect_handle_by_id(id)?.clone();
2004        Self::get_effect_by_handle(context, &effect_handle)
2005    }
2006
2007    /// Sorts the given items by speed.
2008    pub fn speed_sort<T>(context: &mut Context, items: &mut [T])
2009    where
2010        for<'a> &'a T: SpeedOrderable,
2011    {
2012        let prng = context.battle_mut().prng.as_mut();
2013        // SAFETY: PRNG and sorting are completely disjoint.
2014        let prng = unsafe { mem::transmute(prng) };
2015        let tie_resolution = context.battle().engine_options.speed_sort_tie_resolution;
2016        speed_sort(items, prng, tie_resolution);
2017    }
2018
2019    /// Updates the battle, triggering any miscellaneous effects on Mons that could activate.
2020    pub fn update(context: &mut Context) -> Result<(), Error> {
2021        core_battle_effects::run_event_for_each_active_mon_with_effect(
2022            &mut context.effect_context(EffectHandle::Condition(Id::from_known("update")), None)?,
2023            fxlang::BattleEvent::Update,
2024        )
2025    }
2026}