battler 0.8.0

Pokémon battle engine for Rust.
Documentation
use alloc::string::String;

use battler_data::Id;
use hashbrown::HashMap;
use serde::{
    Deserialize,
    Serialize,
};
use serde_string_enum::{
    DeserializeLabeledStringEnum,
    SerializeLabeledStringEnum,
};

use crate::{
    battle::{
        Context,
        core_battle_effects,
    },
    effect::fxlang,
};

/// The environment of the battle field.
#[derive(
    Debug,
    Default,
    Clone,
    Copy,
    PartialEq,
    Eq,
    SerializeLabeledStringEnum,
    DeserializeLabeledStringEnum,
)]
pub enum FieldEnvironment {
    #[default]
    #[string = "Normal"]
    Normal,
    #[string = "Cave"]
    Cave,
    #[string = "Sand"]
    Sand,
    #[string = "Water"]
    Water,
    #[string = "Ice"]
    Ice,
    #[string = "Sky"]
    Sky,
    #[string = "Grass"]
    Grass,
    #[string = "Volcano"]
    Volcano,
}

/// The time of day of the battle field.
#[derive(
    Debug,
    Default,
    Clone,
    Copy,
    PartialEq,
    Eq,
    SerializeLabeledStringEnum,
    DeserializeLabeledStringEnum,
)]
pub enum TimeOfDay {
    #[default]
    #[string = "Day"]
    Day,
    #[string = "Morning"]
    Morning,
    #[string = "Evening"]
    Evening,
    #[string = "Night"]
    Night,
}

/// Data for the field of a battle.
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct FieldData {
    /// The default weather on the field.
    pub weather: Option<String>,
    /// The default terrain on the field.
    pub terrain: Option<String>,
    /// The environment of the field.
    #[serde(default)]
    pub environment: FieldEnvironment,
    /// The time of day of the field.
    #[serde(default)]
    pub time: TimeOfDay,
}

/// Cache for field effects.
#[derive(Debug, Default, Clone)]
pub struct FieldEffectCache {
    pub effective_weather: Option<Option<Id>>,
    pub effective_terrain: Option<Option<Id>>,
}

/// The battle field, which represents the shared environment that all Mons (from both sides) battle
/// on.
///
/// Effects can be applied to the entire field, which will affect all Mons.
pub struct Field {
    pub default_weather: Option<Id>,
    pub default_terrain: Option<Id>,
    pub environment: FieldEnvironment,
    pub time: TimeOfDay,
    pub weather: Option<Id>,
    pub weather_state: fxlang::EffectState,
    pub terrain: Option<Id>,
    pub terrain_state: fxlang::EffectState,
    pub pseudo_weathers: HashMap<Id, fxlang::EffectState>,
    pub effect_cache: FieldEffectCache,
}

impl Field {
    /// Creates a new field.
    pub fn new(data: FieldData) -> Self {
        Self {
            default_weather: data.weather.map(|weather| Id::from(weather)),
            default_terrain: data.terrain.map(|terrain| Id::from(terrain)),
            environment: data.environment,
            time: data.time,
            weather: None,
            weather_state: fxlang::EffectState::default(),
            terrain: None,
            terrain_state: fxlang::EffectState::default(),
            pseudo_weathers: HashMap::default(),
            effect_cache: FieldEffectCache::default(),
        }
    }

    /// The effective terrain for the field.
    ///
    /// Terrain can be suppressed for the entire field by abilities.
    pub fn effective_terrain(context: &mut Context) -> Option<Id> {
        if let Some(effective_terrain) = context
            .battle()
            .field
            .effect_cache
            .effective_terrain
            .clone()
        {
            return effective_terrain;
        }
        let effective_terrain = {
            if core_battle_effects::run_event_for_battle_expecting_bool_quick_return(
                context,
                fxlang::BattleEvent::SuppressFieldTerrain,
            ) {
                None
            } else {
                context.battle().field.terrain.clone()
            }
        };
        context.battle_mut().field.effect_cache.effective_terrain = Some(effective_terrain.clone());
        effective_terrain
    }

    /// The effective weather for the field.
    ///
    /// Weather can be suppressed for the entire field by abilities.
    pub fn effective_weather(context: &mut Context) -> Option<Id> {
        if let Some(effective_weather) = context
            .battle()
            .field
            .effect_cache
            .effective_weather
            .clone()
        {
            return effective_weather;
        }
        let effective_weather = {
            if core_battle_effects::run_event_for_battle_expecting_bool_quick_return(
                context,
                fxlang::BattleEvent::SuppressFieldWeather,
            ) {
                None
            } else {
                context.battle().field.weather.clone()
            }
        };
        context.battle_mut().field.effect_cache.effective_weather = Some(effective_weather.clone());
        effective_weather
    }
}