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 (
178 Switched {
179 side: a,
180 to_slot: ta,
181 },
182 Switched {
183 side: b,
184 to_slot: tb,
185 },
186 ) => a == b && ta == tb,
187 (Residual { result: a }, Residual { result: b }) => a == b,
188 (Effect { result: a }, Effect { result: b }) => a == b,
189 _ => false,
190 }
191 }
192}
193
194#[derive(Clone, Copy, Debug, PartialEq, Eq)]
196pub enum BattleEnd {
197 PlayerWin,
199 PlayerLoss,
201 Fled,
203 Caught,
205}
206
207pub struct TurnOutcome<P: BattleProvider + ?Sized> {
209 pub events: Vec<TurnEvent<P>>,
211 pub battle_over: Option<BattleEnd>,
213}
214
215impl<P: BattleProvider + ?Sized> fmt::Debug for TurnOutcome<P> {
216 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217 f.debug_struct("TurnOutcome")
218 .field("events", &self.events)
219 .field("battle_over", &self.battle_over)
220 .finish()
221 }
222}
223
224pub struct BattleDriver;
226
227impl BattleDriver {
228 pub fn execute_turn<P: BattleProvider>(
234 provider: &P,
235 state: &mut BattleState<P>,
236 actions: [BattleAction<P>; 2],
237 rng: &mut dyn BattleRng,
238 ) -> TurnOutcome<P> {
239 let mut events: Vec<TurnEvent<P>> = Vec::new();
240
241 let [player_action, opponent_action] = actions;
244 let actors = [
245 (BattlerRef::PLAYER, player_action),
246 (BattlerRef::OPPONENT, opponent_action),
247 ];
248
249 let mut keyed: Vec<(OrderKey, usize)> = actors
253 .iter()
254 .enumerate()
255 .map(|(idx, (who, action))| (provider.turn_order_key(state, *who, action, rng), idx))
256 .collect();
257 keyed.sort_by(|a, b| a.0.cmp(&b.0));
259
260 for (_key, idx) in keyed {
262 let (who, action) = &actors[idx];
263 if Self::side_all_fainted(state, who.side) {
266 continue;
267 }
268 Self::resolve_action(provider, state, *who, action, rng, &mut events);
269
270 if let Some(battle_over) = Self::detect_end(state) {
273 return TurnOutcome {
274 events,
275 battle_over: Some(battle_over),
276 };
277 }
278 }
279
280 for result in provider.end_of_turn(state, rng) {
282 events.push(TurnEvent::Residual { result });
283 }
284 Self::push_faints(state, &mut events);
286
287 state.turn_count = state.turn_count.saturating_add(1);
289 let battle_over = Self::detect_end(state);
290
291 TurnOutcome {
292 events,
293 battle_over,
294 }
295 }
296
297 fn resolve_action<P: BattleProvider>(
299 provider: &P,
300 state: &mut BattleState<P>,
301 who: BattlerRef,
302 action: &BattleAction<P>,
303 rng: &mut dyn BattleRng,
304 events: &mut Vec<TurnEvent<P>>,
305 ) {
306 let gate = provider.before_move(state, who, action, rng);
308 let effective_action: BattleAction<P> = match gate {
309 MoveGate::Acts => action.clone(),
310 MoveGate::Prevented(reason) => {
311 events.push(TurnEvent::ActionPrevented { who, reason });
312 return;
313 }
314 MoveGate::ForcedAction(forced) => forced,
315 };
316
317 match effective_action {
318 BattleAction::Fight { move_ } => {
319 Self::resolve_fight(provider, state, who, move_, rng, events);
320 }
321 BattleAction::Switch { to_slot } => {
322 if Self::apply_switch(state, who.side, to_slot) {
323 events.push(TurnEvent::Switched {
324 side: who.side,
325 to_slot,
326 });
327 }
328 }
329 BattleAction::UseItem { .. } | BattleAction::Run | BattleAction::Nothing => {}
333 }
334 }
335
336 fn resolve_fight<P: BattleProvider>(
338 provider: &P,
339 state: &mut BattleState<P>,
340 who: BattlerRef,
341 move_: P::Move,
342 rng: &mut dyn BattleRng,
343 events: &mut Vec<TurnEvent<P>>,
344 ) {
345 let target = Self::opposing(who);
346
347 events.push(TurnEvent::MoveUsed {
348 who,
349 move_: move_.clone(),
350 });
351
352 if !provider.accuracy_check(state, who, target, &move_, rng) {
354 events.push(TurnEvent::Missed { who, target });
355 return;
356 }
357
358 let critical = provider.roll_critical(state, who, target, &move_, rng);
364
365 let random = rng.next_u8();
370 let (Some(attacker), Some(defender)) = (
371 Self::battler(state, who).cloned(),
372 Self::battler(state, target).cloned(),
373 ) else {
374 return;
375 };
376 let dmg = provider.calculate_damage(&move_, &attacker, &defender, random, critical);
377
378 if dmg.is_miss {
379 events.push(TurnEvent::Missed { who, target });
380 return;
381 }
382
383 if let Some(def_mut) = Self::battler_mut(state, target) {
384 def_mut.take_damage(dmg.damage);
385 }
386 events.push(TurnEvent::Damage {
387 who,
388 target,
389 amount: dmg.damage,
390 critical,
391 effectiveness: dmg.effectiveness,
392 });
393
394 if let Some(def) = Self::battler(state, target) {
395 if def.hp == 0 {
396 events.push(TurnEvent::Faint { who: target });
397 }
398 }
399 }
400
401 pub fn apply_switch<P: BattleProvider>(
404 state: &mut BattleState<P>,
405 side: u8,
406 to_slot: usize,
407 ) -> bool {
408 let party = match side {
409 0 => &mut state.player_battlers,
410 _ => &mut state.opponent_battlers,
411 };
412 if to_slot == 0 || to_slot >= party.len() || party[to_slot].hp == 0 {
413 return false;
414 }
415 party.swap(0, to_slot);
416 true
417 }
418
419 fn opposing(who: BattlerRef) -> BattlerRef {
422 BattlerRef::new(if who.side == 0 { 1 } else { 0 }, who.slot)
423 }
424
425 fn battler<P: BattleProvider>(state: &BattleState<P>, who: BattlerRef) -> Option<&Battler<P>> {
426 let party = if who.side == 0 {
427 &state.player_battlers
428 } else {
429 &state.opponent_battlers
430 };
431 party.get(who.slot as usize)
432 }
433
434 fn battler_mut<P: BattleProvider>(
435 state: &mut BattleState<P>,
436 who: BattlerRef,
437 ) -> Option<&mut Battler<P>> {
438 let party = if who.side == 0 {
439 &mut state.player_battlers
440 } else {
441 &mut state.opponent_battlers
442 };
443 party.get_mut(who.slot as usize)
444 }
445
446 fn side_all_fainted<P: BattleProvider>(state: &BattleState<P>, side: u8) -> bool {
448 let party = if side == 0 {
449 &state.player_battlers
450 } else {
451 &state.opponent_battlers
452 };
453 party.is_empty() || party.iter().all(|b| b.hp == 0)
454 }
455
456 fn push_faints<P: BattleProvider>(state: &BattleState<P>, events: &mut Vec<TurnEvent<P>>) {
458 for who in [BattlerRef::PLAYER, BattlerRef::OPPONENT] {
459 if let Some(b) = Self::battler(state, who) {
460 if b.hp == 0 {
461 let already = events
462 .iter()
463 .any(|e| matches!(e, TurnEvent::Faint { who: w } if *w == who));
464 if !already {
465 events.push(TurnEvent::Faint { who });
466 }
467 }
468 }
469 }
470 }
471
472 fn detect_end<P: BattleProvider>(state: &BattleState<P>) -> Option<BattleEnd> {
474 let player_wiped =
475 !state.player_battlers.is_empty() && state.player_battlers.iter().all(|b| b.hp == 0);
476 let opponent_wiped = !state.opponent_battlers.is_empty()
477 && state.opponent_battlers.iter().all(|b| b.hp == 0);
478 match (player_wiped, opponent_wiped) {
479 (_, true) => Some(BattleEnd::PlayerWin),
480 (true, false) => Some(BattleEnd::PlayerLoss),
481 (false, false) => None,
482 }
483 }
484}