use super::{EvolutionProvider, EvolutionTrigger, ExpProvider, MonsterProvider, StatSet};
use crate::battle::{BattleProvider, BattlerState, EnumMap};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MonsterStatus {
Healthy,
Sleep(u8),
Poison,
Burn,
Freeze,
Paralysis,
}
impl Default for MonsterStatus {
fn default() -> Self {
MonsterStatus::Healthy
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MoveSlot<P: MonsterProvider> {
pub move_id: P::MoveId,
pub pp: u8,
pub pp_up: u8,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct LevelUp<P: MonsterProvider> {
pub levels_gained: u8,
pub old_level: u8,
pub new_level: u8,
pub learned_moves: Vec<P::MoveId>,
}
impl<P: MonsterProvider> Default for LevelUp<P> {
fn default() -> Self {
LevelUp {
levels_gained: 0,
old_level: 0,
new_level: 0,
learned_moves: Vec::new(),
}
}
}
impl<P: MonsterProvider> LevelUp<P> {
pub fn gained(&self) -> bool {
self.levels_gained > 0
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MonsterInstance<P: MonsterProvider> {
pub species: P::SpeciesId,
pub level: u8,
pub exp: u32,
pub genetics: P::Genetics,
pub training: P::Training,
pub stats: StatSet<P>,
pub current_hp: u16,
pub status: MonsterStatus,
pub moves: Vec<MoveSlot<P>>,
}
impl<P: MonsterProvider> MonsterInstance<P> {
pub fn new(
provider: &P,
species: P::SpeciesId,
level: u8,
genetics: P::Genetics,
training: P::Training,
) -> Self {
let mut inst = Self {
species,
level,
exp: 0,
genetics,
training,
stats: StatSet::zeroed(provider),
current_hp: 0,
status: MonsterStatus::Healthy,
moves: Vec::new(),
};
inst.recalc_stats(provider);
inst.current_hp = inst.max_hp(provider);
inst
}
pub fn recalc_stats(&mut self, provider: &P) {
for &stat in provider.stats() {
let value =
provider.calc_stat(self.species, stat, self.level, &self.genetics, &self.training);
self.stats.set(stat, value);
}
let max = self.max_hp(provider);
if self.current_hp > max {
self.current_hp = max;
}
}
pub fn max_hp(&self, provider: &P) -> u16 {
self.stats.get(provider.hp_stat())
}
pub fn is_fainted(&self) -> bool {
self.current_hp == 0
}
}
impl<P: ExpProvider> MonsterInstance<P> {
pub fn gain_exp(&mut self, provider: &P, amount: u32) -> LevelUp<P> {
let old_level = self.level;
self.exp = self.exp.saturating_add(amount);
let max_level = provider.max_level();
let max_exp = provider.exp_for_level(self.species, max_level);
if self.exp > max_exp {
self.exp = max_exp;
}
let mut learned_moves: Vec<P::MoveId> = Vec::new();
while self.level < max_level {
let next = self.level + 1;
let needed = provider.exp_for_level(self.species, next);
if self.exp >= needed {
let old_max_hp = self.max_hp(provider);
self.level = next;
self.recalc_stats(provider);
let new_max_hp = self.max_hp(provider);
let new_hp = provider.levelup_current_hp(old_max_hp, new_max_hp, self.current_hp);
self.current_hp = new_hp.min(new_max_hp);
let mut gained =
provider.learn_moves_on_levelup(self.species, next, &mut self.moves);
learned_moves.append(&mut gained);
} else {
break;
}
}
LevelUp {
levels_gained: self.level - old_level,
old_level,
new_level: self.level,
learned_moves,
}
}
}
impl<P: EvolutionProvider> MonsterInstance<P> {
pub fn try_evolve(
&mut self,
provider: &P,
trigger: EvolutionTrigger<P::EvoItem>,
) -> Option<P::SpeciesId> {
let target = provider.evolution_target(self, trigger)?;
self.species = target;
self.recalc_stats(provider);
Some(target)
}
}
impl<P: MonsterProvider> MonsterInstance<P> {
pub fn to_battler<B, FS, FM, FST>(
&self,
provider: &P,
mut map_species: FS,
mut map_move: FM,
mut map_stat: FST,
) -> BattlerState<B>
where
B: BattleProvider,
B::Stat: Copy + PartialEq,
FS: FnMut(P::SpeciesId) -> B::Species,
FM: FnMut(P::MoveId) -> B::Move,
FST: FnMut(P::Stat) -> B::Stat,
{
let mut stats: EnumMap<B::Stat, u16> = EnumMap::new();
for (stat, value) in self.stats.iter() {
stats.set(map_stat(stat), value);
}
let moves: Vec<B::Move> = self.moves.iter().map(|m| map_move(m.move_id)).collect();
let species = map_species(self.species);
let max_hp = self.max_hp(provider);
BattlerState::new(species, self.current_hp, max_hp, stats, moves)
}
}