1use super::{
29 BattleAction, BattleProvider, BattleRng, BattleState, BattlerRef, BattlerState as Battler,
30 EffectResult, MoveGate, OrderKey,
31};
32use std::fmt;
33
34pub enum TurnEvent<P: BattleProvider + ?Sized> {
39 MoveUsed {
41 who: BattlerRef,
43 move_: P::Move,
45 },
46 ActionPrevented {
49 who: BattlerRef,
51 reason: EffectResult,
53 },
54 Missed {
57 who: BattlerRef,
59 target: BattlerRef,
61 },
62 Damage {
64 who: BattlerRef,
66 target: BattlerRef,
68 amount: u16,
70 critical: bool,
72 effectiveness: f32,
74 },
75 Faint {
77 who: BattlerRef,
79 },
80 Switched {
82 side: u8,
84 to_slot: usize,
86 },
87 Residual {
89 result: EffectResult,
91 },
92 Effect {
95 result: EffectResult,
97 },
98}
99
100impl<P: BattleProvider + ?Sized> fmt::Debug for TurnEvent<P> {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 match self {
103 TurnEvent::MoveUsed { who, move_ } => f
104 .debug_struct("MoveUsed")
105 .field("who", who)
106 .field("move_", move_)
107 .finish(),
108 TurnEvent::ActionPrevented { who, reason } => f
109 .debug_struct("ActionPrevented")
110 .field("who", who)
111 .field("reason", reason)
112 .finish(),
113 TurnEvent::Missed { who, target } => f
114 .debug_struct("Missed")
115 .field("who", who)
116 .field("target", target)
117 .finish(),
118 TurnEvent::Damage {
119 who,
120 target,
121 amount,
122 critical,
123 effectiveness,
124 } => f
125 .debug_struct("Damage")
126 .field("who", who)
127 .field("target", target)
128 .field("amount", amount)
129 .field("critical", critical)
130 .field("effectiveness", effectiveness)
131 .finish(),
132 TurnEvent::Faint { who } => f.debug_struct("Faint").field("who", who).finish(),
133 TurnEvent::Switched { side, to_slot } => f
134 .debug_struct("Switched")
135 .field("side", side)
136 .field("to_slot", to_slot)
137 .finish(),
138 TurnEvent::Residual { result } => {
139 f.debug_struct("Residual").field("result", result).finish()
140 }
141 TurnEvent::Effect { result } => {
142 f.debug_struct("Effect").field("result", result).finish()
143 }
144 }
145 }
146}
147
148impl<P: BattleProvider + ?Sized> PartialEq for TurnEvent<P>
149where
150 P::Move: PartialEq,
151{
152 fn eq(&self, other: &Self) -> bool {
153 use TurnEvent::*;
154 match (self, other) {
155 (MoveUsed { who: a, move_: ma }, MoveUsed { who: b, move_: mb }) => a == b && ma == mb,
156 (ActionPrevented { who: a, reason: ra }, ActionPrevented { who: b, reason: rb }) => {
157 a == b && ra == rb
158 }
159 (Missed { who: a, target: ta }, Missed { who: b, target: tb }) => a == b && ta == tb,
160 (
161 Damage {
162 who: a,
163 target: ta,
164 amount: am,
165 critical: ca,
166 effectiveness: ea,
167 },
168 Damage {
169 who: b,
170 target: tb,
171 amount: bm,
172 critical: cb,
173 effectiveness: eb,
174 },
175 ) => a == b && ta == tb && am == bm && ca == cb && ea == eb,
176 (Faint { who: a }, Faint { who: b }) => a == b,
177 (Switched { side: a, to_slot: ta }, Switched { side: b, to_slot: tb }) => {
178 a == b && ta == tb
179 }
180 (Residual { result: a }, Residual { result: b }) => a == b,
181 (Effect { result: a }, Effect { result: b }) => a == b,
182 _ => false,
183 }
184 }
185}
186
187#[derive(Clone, Copy, Debug, PartialEq, Eq)]
189pub enum BattleEnd {
190 PlayerWin,
192 PlayerLoss,
194 Fled,
196 Caught,
198}
199
200pub struct TurnOutcome<P: BattleProvider + ?Sized> {
202 pub events: Vec<TurnEvent<P>>,
204 pub battle_over: Option<BattleEnd>,
206}
207
208impl<P: BattleProvider + ?Sized> fmt::Debug for TurnOutcome<P> {
209 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210 f.debug_struct("TurnOutcome")
211 .field("events", &self.events)
212 .field("battle_over", &self.battle_over)
213 .finish()
214 }
215}
216
217pub struct BattleDriver;
219
220impl BattleDriver {
221 pub fn execute_turn<P: BattleProvider>(
227 provider: &P,
228 state: &mut BattleState<P>,
229 actions: [BattleAction<P>; 2],
230 rng: &mut dyn BattleRng,
231 ) -> TurnOutcome<P> {
232 let mut events: Vec<TurnEvent<P>> = Vec::new();
233
234 let [player_action, opponent_action] = actions;
237 let actors = [
238 (BattlerRef::PLAYER, player_action),
239 (BattlerRef::OPPONENT, opponent_action),
240 ];
241
242 let mut keyed: Vec<(OrderKey, usize)> = actors
246 .iter()
247 .enumerate()
248 .map(|(idx, (who, action))| (provider.turn_order_key(state, *who, action, rng), idx))
249 .collect();
250 keyed.sort_by(|a, b| a.0.cmp(&b.0));
252
253 for (_key, idx) in keyed {
255 let (who, action) = &actors[idx];
256 if Self::side_all_fainted(state, who.side) {
259 continue;
260 }
261 Self::resolve_action(provider, state, *who, action, rng, &mut events);
262
263 if let Some(battle_over) = Self::detect_end(state) {
266 return TurnOutcome {
267 events,
268 battle_over: Some(battle_over),
269 };
270 }
271 }
272
273 for result in provider.end_of_turn(state, rng) {
275 events.push(TurnEvent::Residual { result });
276 }
277 Self::push_faints(state, &mut events);
279
280 state.turn_count = state.turn_count.saturating_add(1);
282 let battle_over = Self::detect_end(state);
283
284 TurnOutcome {
285 events,
286 battle_over,
287 }
288 }
289
290 fn resolve_action<P: BattleProvider>(
292 provider: &P,
293 state: &mut BattleState<P>,
294 who: BattlerRef,
295 action: &BattleAction<P>,
296 rng: &mut dyn BattleRng,
297 events: &mut Vec<TurnEvent<P>>,
298 ) {
299 let gate = provider.before_move(state, who, action, rng);
301 let effective_action: BattleAction<P> = match gate {
302 MoveGate::Acts => action.clone(),
303 MoveGate::Prevented(reason) => {
304 events.push(TurnEvent::ActionPrevented { who, reason });
305 return;
306 }
307 MoveGate::ForcedAction(forced) => forced,
308 };
309
310 match effective_action {
311 BattleAction::Fight { move_ } => {
312 Self::resolve_fight(provider, state, who, move_, rng, events);
313 }
314 BattleAction::Switch { to_slot } => {
315 if Self::apply_switch(state, who.side, to_slot) {
316 events.push(TurnEvent::Switched {
317 side: who.side,
318 to_slot,
319 });
320 }
321 }
322 BattleAction::UseItem { .. } | BattleAction::Run | BattleAction::Nothing => {}
326 }
327 }
328
329 fn resolve_fight<P: BattleProvider>(
331 provider: &P,
332 state: &mut BattleState<P>,
333 who: BattlerRef,
334 move_: P::Move,
335 rng: &mut dyn BattleRng,
336 events: &mut Vec<TurnEvent<P>>,
337 ) {
338 let target = Self::opposing(who);
339
340 events.push(TurnEvent::MoveUsed {
341 who,
342 move_: move_.clone(),
343 });
344
345 if !provider.accuracy_check(state, who, target, &move_, rng) {
347 events.push(TurnEvent::Missed { who, target });
348 return;
349 }
350
351 let critical = provider.roll_critical(state, who, target, &move_, rng);
357
358 let random = rng.next_u8();
363 let (Some(attacker), Some(defender)) = (
364 Self::battler(state, who).cloned(),
365 Self::battler(state, target).cloned(),
366 ) else {
367 return;
368 };
369 let dmg = provider.calculate_damage(&move_, &attacker, &defender, random, critical);
370
371 if dmg.is_miss {
372 events.push(TurnEvent::Missed { who, target });
373 return;
374 }
375
376 if let Some(def_mut) = Self::battler_mut(state, target) {
377 def_mut.take_damage(dmg.damage);
378 }
379 events.push(TurnEvent::Damage {
380 who,
381 target,
382 amount: dmg.damage,
383 critical,
384 effectiveness: dmg.effectiveness,
385 });
386
387 if let Some(def) = Self::battler(state, target) {
388 if def.hp == 0 {
389 events.push(TurnEvent::Faint { who: target });
390 }
391 }
392 }
393
394 pub fn apply_switch<P: BattleProvider>(
397 state: &mut BattleState<P>,
398 side: u8,
399 to_slot: usize,
400 ) -> bool {
401 let party = match side {
402 0 => &mut state.player_battlers,
403 _ => &mut state.opponent_battlers,
404 };
405 if to_slot == 0 || to_slot >= party.len() || party[to_slot].hp == 0 {
406 return false;
407 }
408 party.swap(0, to_slot);
409 true
410 }
411
412 fn opposing(who: BattlerRef) -> BattlerRef {
415 BattlerRef::new(if who.side == 0 { 1 } else { 0 }, who.slot)
416 }
417
418 fn battler<P: BattleProvider>(
419 state: &BattleState<P>,
420 who: BattlerRef,
421 ) -> Option<&Battler<P>> {
422 let party = if who.side == 0 {
423 &state.player_battlers
424 } else {
425 &state.opponent_battlers
426 };
427 party.get(who.slot as usize)
428 }
429
430 fn battler_mut<P: BattleProvider>(
431 state: &mut BattleState<P>,
432 who: BattlerRef,
433 ) -> Option<&mut Battler<P>> {
434 let party = if who.side == 0 {
435 &mut state.player_battlers
436 } else {
437 &mut state.opponent_battlers
438 };
439 party.get_mut(who.slot as usize)
440 }
441
442 fn side_all_fainted<P: BattleProvider>(state: &BattleState<P>, side: u8) -> bool {
444 let party = if side == 0 {
445 &state.player_battlers
446 } else {
447 &state.opponent_battlers
448 };
449 party.is_empty() || party.iter().all(|b| b.hp == 0)
450 }
451
452 fn push_faints<P: BattleProvider>(state: &BattleState<P>, events: &mut Vec<TurnEvent<P>>) {
454 for who in [BattlerRef::PLAYER, BattlerRef::OPPONENT] {
455 if let Some(b) = Self::battler(state, who) {
456 if b.hp == 0 {
457 let already = events
458 .iter()
459 .any(|e| matches!(e, TurnEvent::Faint { who: w } if *w == who));
460 if !already {
461 events.push(TurnEvent::Faint { who });
462 }
463 }
464 }
465 }
466 }
467
468 fn detect_end<P: BattleProvider>(state: &BattleState<P>) -> Option<BattleEnd> {
470 let player_wiped =
471 !state.player_battlers.is_empty() && state.player_battlers.iter().all(|b| b.hp == 0);
472 let opponent_wiped = !state.opponent_battlers.is_empty()
473 && state.opponent_battlers.iter().all(|b| b.hp == 0);
474 match (player_wiped, opponent_wiped) {
475 (_, true) => Some(BattleEnd::PlayerWin),
476 (true, false) => Some(BattleEnd::PlayerLoss),
477 (false, false) => None,
478 }
479 }
480}