use crate::battle::rng::BattleRng;
use crate::battle::{BattleAction, BattleProvider, BattleState, BattlerRef, BattlerState};
use super::event::{Effect, EffectId};
pub trait EffectProvider: BattleProvider + 'static {
type EffectStateKind: Clone;
fn effect_for_move(&self, m: &Self::Move) -> Option<&'static Effect<Self>>
where
Self: Sized;
fn effect_for_status(&self, s: &Self::Status) -> Option<&'static Effect<Self>>
where
Self: Sized;
fn effect_for_volatile(&self, kind: &Self::EffectStateKind) -> Option<&'static Effect<Self>>
where
Self: Sized,
{
let _ = kind;
None
}
fn turn_order_rank(
&self,
state: &BattleState<Self>,
who: BattlerRef,
action: &<Self as BattleProvider>::Move,
) -> (i32, i32)
where
Self: Sized;
fn forced_action(
&self,
effects: &[EffectState<Self>],
actor: BattlerRef,
chosen: &BattleAction<Self>,
) -> Option<BattleAction<Self>>
where
Self: Sized,
{
let _ = (effects, actor, chosen);
None
}
fn effect_for_ability(&self, b: &BattlerState<Self>) -> Option<&'static Effect<Self>>
where
Self: Sized,
{
let _ = b;
None
}
fn effect_for_item(&self, b: &BattlerState<Self>) -> Option<&'static Effect<Self>>
where
Self: Sized,
{
let _ = b;
None
}
fn side_effects(&self, ctx: &BattleCtx<'_, Self>, side: u8) -> &[&'static Effect<Self>]
where
Self: Sized,
{
let _ = (ctx, side);
&[]
}
fn field_effects(&self, ctx: &BattleCtx<'_, Self>) -> &[&'static Effect<Self>]
where
Self: Sized,
{
let _ = ctx;
&[]
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum EffectHost {
Battler(BattlerRef),
Side(u8),
Field,
}
impl From<BattlerRef> for EffectHost {
fn from(r: BattlerRef) -> Self {
EffectHost::Battler(r)
}
}
impl PartialEq<BattlerRef> for EffectHost {
fn eq(&self, other: &BattlerRef) -> bool {
matches!(self, EffectHost::Battler(r) if r == other)
}
}
impl PartialEq<EffectHost> for BattlerRef {
fn eq(&self, other: &EffectHost) -> bool {
matches!(other, EffectHost::Battler(r) if r == self)
}
}
pub struct EffectState<P: EffectProvider + ?Sized> {
pub id: EffectId,
pub host: BattlerRef,
pub effect_order: u64,
pub kind: P::EffectStateKind,
}
impl<P: EffectProvider + ?Sized> EffectState<P> {
pub fn host_scope(&self) -> EffectHost {
EffectHost::Battler(self.host)
}
}
impl<P: EffectProvider + ?Sized> Clone for EffectState<P> {
fn clone(&self) -> Self {
Self {
id: self.id,
host: self.host,
effect_order: self.effect_order,
kind: self.kind.clone(),
}
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct MoveContext {
pub is_critical: bool,
pub damage: u16,
pub move_missed: bool,
pub last_damage: u16,
}
pub struct BattleCtx<'a, P: EffectProvider + ?Sized> {
pub state: &'a mut BattleState<P>,
pub effects: &'a mut Vec<EffectState<P>>,
pub mv: &'a mut MoveContext,
pub rng: &'a mut dyn BattleRng,
}
impl<'a, P: EffectProvider + ?Sized> BattleCtx<'a, P> {
pub fn battler_mut(&mut self, r: BattlerRef) -> &mut BattlerState<P> {
match r.side {
0 => &mut self.state.player_battlers[r.slot as usize],
_ => &mut self.state.opponent_battlers[r.slot as usize],
}
}
pub fn battler(&self, r: BattlerRef) -> &BattlerState<P> {
match r.side {
0 => &self.state.player_battlers[r.slot as usize],
_ => &self.state.opponent_battlers[r.slot as usize],
}
}
pub fn pair_mut(
&mut self,
a: BattlerRef,
b: BattlerRef,
) -> (&mut BattlerState<P>, &mut BattlerState<P>) {
debug_assert!(a != b, "pair_mut requires two distinct battlers");
if a.side != b.side {
let pa: *mut BattlerState<P> = self.battler_mut(a);
let pb: *mut BattlerState<P> = self.battler_mut(b);
unsafe { (&mut *pa, &mut *pb) }
} else {
let v = if a.side == 0 {
&mut self.state.player_battlers
} else {
&mut self.state.opponent_battlers
};
let (lo, hi) = (a.slot.min(b.slot) as usize, a.slot.max(b.slot) as usize);
let (left, right) = v.split_at_mut(hi);
let (first, second) = (&mut left[lo], &mut right[0]);
if a.slot < b.slot {
(first, second)
} else {
(second, first)
}
}
}
pub fn effect_mut(&mut self, id: EffectId) -> Option<&mut EffectState<P>> {
match self.effects.binary_search_by(|e| e.id.cmp(&id)) {
Ok(idx) => Some(&mut self.effects[idx]),
Err(_) => None,
}
}
pub fn effect(&self, id: EffectId) -> Option<&EffectState<P>> {
match self.effects.binary_search_by(|e| e.id.cmp(&id)) {
Ok(idx) => Some(&self.effects[idx]),
Err(_) => None,
}
}
pub fn install_effect(&mut self, host: BattlerRef, kind: P::EffectStateKind) -> EffectId {
let id = EffectId(self.effects.iter().map(|e| e.id.0).max().unwrap_or(0) + 1);
self.effects.push(EffectState {
id,
host,
effect_order: id.0 as u64,
kind,
});
self.effects.sort_by_key(|e| e.id.0);
id
}
}