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
96pub struct PublicCoreBattle<'d> {
101 pub internal: CoreBattle<'d>,
103}
104
105impl<'d> PublicCoreBattle<'d> {
106 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 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 pub fn started(&self) -> bool {
130 self.internal.started
131 }
132
133 pub fn ended(&self) -> bool {
135 self.internal.ended
136 }
137
138 pub fn has_new_logs(&self) -> bool {
140 self.internal.has_new_logs()
141 }
142
143 pub fn all_logs(&self) -> impl Iterator<Item = &str> {
145 self.internal.all_logs()
146 }
147
148 pub fn new_logs(&mut self) -> impl Iterator<Item = &str> {
150 self.internal.new_logs()
151 }
152
153 pub fn start(&mut self) -> Result<(), Error> {
155 self.internal.start()
156 }
157
158 pub fn ready_to_continue(&mut self) -> Result<bool, Error> {
160 self.internal.ready_to_continue()
161 }
162
163 pub fn continue_battle(&mut self) -> Result<(), Error> {
168 self.internal.continue_battle()
169 }
170
171 pub fn player_data(&mut self, player: &str) -> Result<PlayerBattleData, Error> {
176 self.internal.player_data(player)
177 }
178
179 pub fn active_requests<'b>(&'b self) -> impl Iterator<Item = (String, Request)> + 'b {
181 self.internal.active_requests()
182 }
183
184 pub fn request_for_player(&self, player: &str) -> Option<Request> {
186 self.internal.request_for_player(player)
187 }
188
189 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
195pub struct FaintEntry {
197 pub target: MonHandle,
198 pub source: Option<MonHandle>,
199 pub effect: Option<EffectHandle>,
200}
201
202pub struct CatchEntry {
204 pub target: MonHandle,
205 pub player: usize,
206 pub item: Id,
207 pub shakes: u8,
208 pub critical: bool,
209}
210
211pub struct CoreBattle<'d> {
222 log: EventLog,
223
224 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
262impl<'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, ®istry)?;
296 let (side_2, side_2_players) =
297 Side::new(options.side_2, 1, &format.battle_type, &dex, ®istry)?;
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
351impl<'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 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
535impl<'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 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 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 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 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 BattleQueue::sort(context);
1013
1014 while let Some(action) = context.battle_mut().queue.pop_front() {
1016 Self::run_action(context, action)?;
1017 if context.battle().request.is_some() || context.battle().ended {
1019 return Ok(());
1020 }
1021 }
1022
1023 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 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 core_battle_actions::clear_weather(&mut context.field_effect_context(
1075 EffectHandle::Condition(Id::from_known("start")),
1076 None,
1077 None,
1078 )?)?;
1079
1080 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 if context.battle().ending {
1243 return Ok(());
1244 }
1245
1246 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 Self::check_for_exited_mons(context)?;
1266 } else if let Some(Action::Switch(switch)) = context.battle().queue.peek() {
1267 if switch.instant {
1269 return Ok(());
1270 }
1271 }
1272
1273 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 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 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 Self::faint_messages(context.as_battle_context_mut())?;
1319 if context.battle().ending {
1320 return Ok(());
1321 }
1322 }
1323 }
1324
1325 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 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 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 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 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 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 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 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 return Ok(Some(mon));
1531 }
1532
1533 let mut context = context.mon_context(mon)?;
1534 let mons = if !move_target.can_target_foes() {
1535 Mon::adjacent_allies(&mut context)?.collect::<Vec<_>>()
1537 } else if move_target.is_adjacent_only() {
1538 Mon::adjacent_foes(&mut context)?.collect::<Vec<_>>()
1541 } else {
1542 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 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 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 return Ok(Some(target_mon_handle));
1602 }
1603 }
1604 }
1605 }
1606
1607 if !move_target.requires_target() {
1609 Ok(None)
1610 } else {
1611 Self::random_target(context, mon, move_target)
1612 }
1613 }
1614
1615 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 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 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 pub fn register_move(&mut self, mov: Move) -> MoveHandle {
1728 self.registry.register_move(mov)
1729 }
1730
1731 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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}