use alloc::{
string::String,
vec::Vec,
};
use anyhow::Result;
use battler_data::{
DefaultTrueBool,
Id,
MoveFlag,
Type,
};
use crate::{
battle::{
ActiveMoveContext,
EffectiveAbility,
Field,
MonContext,
MonHandle,
core_battle_effects,
},
effect::fxlang,
};
pub fn effective_types(context: &mut MonContext) -> Vec<Type> {
if let Some(effective_types) = context
.mon()
.volatile_state
.effect_cache
.effective_types
.clone()
{
return effective_types;
}
let effective_types = {
let mut types = effective_types_no_added_type(context);
if let Some(added_type) = context.mon().volatile_state.added_type {
types.push(added_type);
}
types
};
context
.mon_mut()
.volatile_state
.effect_cache
.effective_types = Some(effective_types.clone());
effective_types
}
pub fn effective_types_no_added_type(context: &mut MonContext) -> Vec<Type> {
if let Some(effective_types_no_added_type) = context
.mon()
.volatile_state
.effect_cache
.effective_types_no_added_type
.clone()
{
return effective_types_no_added_type;
}
let effective_types_no_added_type = {
let types = core_battle_effects::run_event_with_relay::<_, Vec<Type>>(
context,
fxlang::BattleEvent::ForceTypes,
Vec::default(),
);
if !types.is_empty() {
types
} else {
effective_types_before_forced_types(context, false)
}
};
context
.mon_mut()
.volatile_state
.effect_cache
.effective_types_no_added_type = Some(effective_types_no_added_type.clone());
effective_types_no_added_type
}
fn effective_types_before_forced_types(context: &mut MonContext, added_type: bool) -> Vec<Type> {
if let Some(effective_types_before_forced_types) = context
.mon()
.volatile_state
.effect_cache
.effective_types_before_forced_types
.clone()
{
return effective_types_before_forced_types;
}
let mut types = context.mon().volatile_state.types.clone();
if added_type && let Some(added_type) = context.mon().volatile_state.added_type {
types.push(added_type);
}
let effective_types_before_forced_types = {
let types = core_battle_effects::run_event_with_relay::<_, Vec<Type>>(
context,
fxlang::BattleEvent::Types,
types,
);
if !types.is_empty() {
types
} else {
Vec::from_iter([Type::None])
}
};
context
.mon_mut()
.volatile_state
.effect_cache
.effective_types_before_forced_types = Some(effective_types_before_forced_types.clone());
effective_types_before_forced_types
}
pub fn has_type(context: &mut MonContext, typ: Type) -> bool {
let types = effective_types(context);
types.contains(&typ)
}
pub fn has_type_before_forced_types(context: &mut MonContext, typ: Type) -> bool {
let types = effective_types_before_forced_types(context, true);
types.contains(&typ)
}
pub fn berry_eating_health(context: &mut MonContext) -> u16 {
if let Some(berry_eating_health) = context
.mon()
.volatile_state
.effect_cache
.berry_eating_health
{
return berry_eating_health;
}
let berry_eating_health = {
let health = context.mon().max_hp / 4;
core_battle_effects::run_event_with_relay::<_, u16>(
context,
fxlang::BattleEvent::BerryEatingHealth,
health,
)
};
context
.mon_mut()
.volatile_state
.effect_cache
.berry_eating_health = Some(berry_eating_health);
berry_eating_health
}
pub fn can_heal(context: &mut MonContext) -> bool {
if let Some(can_heal) = context.mon().volatile_state.effect_cache.can_heal {
return can_heal;
}
let can_heal = *core_battle_effects::run_event_with_options::<_, _, DefaultTrueBool>(
context,
fxlang::BattleEvent::CanHeal,
(),
core_battle_effects::RunEventOptions {
return_first_value: true,
..Default::default()
},
);
context.mon_mut().volatile_state.effect_cache.can_heal = Some(can_heal);
can_heal
}
pub fn is_asleep(context: &mut MonContext) -> bool {
if let Some(is_asleep) = context.mon().volatile_state.effect_cache.is_asleep {
return is_asleep;
}
let is_asleep = core_battle_effects::run_event_with_options::<_, _, bool>(
context,
fxlang::BattleEvent::IsAsleep,
(),
core_battle_effects::RunEventOptions {
return_first_value: true,
..Default::default()
},
);
context.mon_mut().volatile_state.effect_cache.is_asleep = Some(is_asleep);
is_asleep
}
pub fn is_away_from_field(context: &mut MonContext) -> bool {
if let Some(is_away_from_field) = context.mon().volatile_state.effect_cache.is_away_from_field {
return is_away_from_field;
}
let is_away_from_field = core_battle_effects::run_event_with_options::<_, _, bool>(
context,
fxlang::BattleEvent::IsAwayFromField,
(),
core_battle_effects::RunEventOptions {
return_first_value: true,
..Default::default()
},
);
context
.mon_mut()
.volatile_state
.effect_cache
.is_away_from_field = Some(is_away_from_field);
is_away_from_field
}
pub fn is_behind_substitute(context: &mut MonContext) -> bool {
if let Some(is_behind_substitute) = context
.mon()
.volatile_state
.effect_cache
.is_behind_substitute
{
return is_behind_substitute;
}
let is_behind_substitute = core_battle_effects::run_event_with_options::<_, _, bool>(
context,
fxlang::BattleEvent::IsBehindSubstitute,
(),
core_battle_effects::RunEventOptions {
return_first_value: true,
..Default::default()
},
);
context
.mon_mut()
.volatile_state
.effect_cache
.is_behind_substitute = Some(is_behind_substitute);
is_behind_substitute
}
pub fn is_choice_locked(context: &mut MonContext) -> bool {
if let Some(is_choice_locked) = context.mon().volatile_state.effect_cache.is_choice_locked {
return is_choice_locked;
}
let is_choice_locked = core_battle_effects::run_event_with_options::<_, _, bool>(
context,
fxlang::BattleEvent::IsChoiceLocked,
(),
core_battle_effects::RunEventOptions {
return_first_value: true,
..Default::default()
},
);
context
.mon_mut()
.volatile_state
.effect_cache
.is_choice_locked = Some(is_choice_locked);
is_choice_locked
}
pub fn is_contact_proof(context: &mut MonContext) -> bool {
if let Some(is_contact_proof) = context.mon().volatile_state.effect_cache.is_contact_proof {
return is_contact_proof;
}
let is_contact_proof = core_battle_effects::run_event_with_options::<_, _, bool>(
context,
fxlang::BattleEvent::IsContactProof,
(),
core_battle_effects::RunEventOptions {
return_first_value: true,
..Default::default()
},
);
context
.mon_mut()
.volatile_state
.effect_cache
.is_contact_proof = Some(is_contact_proof);
is_contact_proof
}
pub fn is_grounded(context: &mut MonContext) -> bool {
if let Some(is_grounded) = context.mon().volatile_state.effect_cache.is_grounded {
return is_grounded;
}
let is_grounded = *core_battle_effects::run_event_with_options::<_, _, DefaultTrueBool>(
context,
fxlang::BattleEvent::IsGrounded,
(),
core_battle_effects::RunEventOptions {
return_first_value: true,
..Default::default()
},
);
context.mon_mut().volatile_state.effect_cache.is_grounded = Some(is_grounded);
is_grounded
}
pub fn is_immune_to_entry_hazards(context: &mut MonContext) -> bool {
if let Some(is_immune_to_entry_hazards) = context
.mon()
.volatile_state
.effect_cache
.is_immune_to_entry_hazards
{
return is_immune_to_entry_hazards;
}
let is_immune_to_entry_hazards = core_battle_effects::run_event_with_options::<_, _, bool>(
context,
fxlang::BattleEvent::IsImmuneToEntryHazards,
(),
core_battle_effects::RunEventOptions {
return_first_value: true,
..Default::default()
},
);
context
.mon_mut()
.volatile_state
.effect_cache
.is_immune_to_entry_hazards = Some(is_immune_to_entry_hazards);
is_immune_to_entry_hazards
}
pub fn is_soundproof(context: &mut MonContext) -> bool {
if let Some(is_soundproof) = context.mon().volatile_state.effect_cache.is_soundproof {
return is_soundproof;
}
let is_soundproof = core_battle_effects::run_event_with_options::<_, _, bool>(
context,
fxlang::BattleEvent::IsSoundproof,
(),
core_battle_effects::RunEventOptions {
return_first_value: true,
..Default::default()
},
);
context.mon_mut().volatile_state.effect_cache.is_soundproof = Some(is_soundproof);
is_soundproof
}
pub fn is_semi_invulnerable(context: &mut MonContext) -> bool {
if let Some(is_semi_invulnerable) = context
.mon()
.volatile_state
.effect_cache
.is_semi_invulnerable
{
return is_semi_invulnerable;
}
let is_semi_invulnerable = core_battle_effects::run_event_with_options::<_, _, bool>(
context,
fxlang::BattleEvent::IsSemiInvulnerable,
(),
core_battle_effects::RunEventOptions {
return_first_value: true,
..Default::default()
},
);
context
.mon_mut()
.volatile_state
.effect_cache
.is_semi_invulnerable = Some(is_semi_invulnerable);
is_semi_invulnerable
}
pub fn override_weather(context: &mut MonContext) -> Option<Id> {
if let Some(override_weather) = context
.mon()
.volatile_state
.effect_cache
.override_weather
.clone()
{
return override_weather;
}
let override_weather = {
if let Some(weather) = core_battle_effects::run_event::<_, Option<String>>(
context,
fxlang::BattleEvent::OverrideWeather,
) {
Some(Id::from(weather))
} else {
None
}
};
context
.mon_mut()
.volatile_state
.effect_cache
.override_weather = Some(override_weather.clone());
override_weather
}
pub fn effective_weather(
context: &mut MonContext,
origin: Option<MonHandle>,
) -> Result<Option<Id>> {
let origin = origin.unwrap_or(context.mon_handle());
if let Some(override_weather) =
override_weather(&mut context.as_battle_context_mut().mon_context(origin)?)
{
return Ok(Some(override_weather));
}
Ok(effective_weather_no_override(context))
}
fn effective_weather_no_override(context: &mut MonContext) -> Option<Id> {
if let Some(effective_weather) = context
.mon()
.volatile_state
.effect_cache
.effective_weather
.clone()
{
return effective_weather;
}
let effective_weather = {
if core_battle_effects::run_event_with_options::<_, _, bool>(
context,
fxlang::BattleEvent::SuppressMonWeather,
(),
core_battle_effects::RunEventOptions {
return_first_value: true,
..Default::default()
},
) {
None
} else {
Field::effective_weather(context.as_battle_context_mut())
}
};
context
.mon_mut()
.volatile_state
.effect_cache
.effective_weather = Some(effective_weather.clone());
effective_weather
}
pub fn effective_terrain(context: &mut MonContext) -> Option<Id> {
if let Some(effective_terrain) = context
.mon()
.volatile_state
.effect_cache
.effective_terrain
.clone()
{
return effective_terrain;
}
let terrain = Field::effective_terrain(context.as_battle_context_mut()).clone()?;
let effective_terrain = {
if !is_grounded(context) || is_semi_invulnerable(context) {
None
} else if core_battle_effects::run_event_with_options::<_, _, bool>(
context,
fxlang::BattleEvent::SuppressMonTerrain,
(),
core_battle_effects::RunEventOptions {
return_first_value: true,
..Default::default()
},
) {
None
} else {
Some(terrain)
}
};
context
.mon_mut()
.volatile_state
.effect_cache
.effective_terrain = Some(effective_terrain.clone());
effective_terrain
}
fn check_ability_suppression(context: &mut MonContext) -> (Option<EffectiveAbility>, bool) {
if let Some(effective_ability) = context
.mon()
.volatile_state
.effect_cache
.effective_ability
.clone()
&& let Some(can_suppress_ability) = context
.mon()
.volatile_state
.effect_cache
.can_suppress_ability
{
return (effective_ability, can_suppress_ability);
}
let ability = context.mon().volatile_state.ability_slot.ability.clone();
let (effective_ability, can_suppress_ability) = {
let suppress_ability = core_battle_effects::run_event_with_options::<_, _, Option<bool>>(
context,
fxlang::BattleEvent::SuppressMonAbility,
(),
core_battle_effects::RunEventOptions {
return_first_value: true,
..Default::default()
},
);
match suppress_ability {
Some(true) => (None, false),
Some(false) => (Some(ability), false),
None => (Some(ability), true),
}
};
context
.mon_mut()
.volatile_state
.effect_cache
.can_suppress_ability = Some(can_suppress_ability);
context
.mon_mut()
.volatile_state
.effect_cache
.effective_ability = Some(effective_ability.clone());
(effective_ability, can_suppress_ability)
}
pub fn can_suppress_ability(context: &mut MonContext) -> bool {
check_ability_suppression(context).1
}
pub fn effective_ability(context: &mut MonContext) -> Option<EffectiveAbility> {
check_ability_suppression(context).0
}
fn check_item_suppression(context: &mut MonContext) -> (Option<Id>, bool) {
if let Some(effective_item) = context
.mon()
.volatile_state
.effect_cache
.effective_item
.clone()
&& let Some(can_suppress_item) = context.mon().volatile_state.effect_cache.can_suppress_item
{
return (effective_item, can_suppress_item);
}
let item = match context.mon().item.clone() {
Some(item) => item,
None => return (None, false),
};
let (effective_item, can_suppress_item) = {
let suppress_item = core_battle_effects::run_event_with_options::<_, _, Option<bool>>(
context,
fxlang::BattleEvent::SuppressMonItem,
(),
core_battle_effects::RunEventOptions {
return_first_value: true,
..Default::default()
},
);
match suppress_item {
Some(true) => (None, false),
Some(false) => (Some(item), false),
None => (Some(item), true),
}
};
context
.mon_mut()
.volatile_state
.effect_cache
.can_suppress_item = Some(can_suppress_item);
context.mon_mut().volatile_state.effect_cache.effective_item = Some(effective_item.clone());
(effective_item, can_suppress_item)
}
pub fn can_suppress_item(context: &mut MonContext) -> bool {
check_item_suppression(context).1
}
pub fn effective_item(context: &mut MonContext) -> Option<Id> {
check_item_suppression(context).0
}
pub fn move_makes_contact(context: &mut ActiveMoveContext) -> bool {
if !context
.active_move()
.data
.flags
.contains(&MoveFlag::Contact)
{
return false;
}
return !is_contact_proof(context.as_mon_context_mut());
}
pub fn effective_tera_type(context: &mut MonContext) -> Type {
if let Some(effective_tera_type) = context
.mon()
.volatile_state
.effect_cache
.effective_tera_type
.clone()
{
return effective_tera_type;
}
let effective_tera_type = core_battle_effects::run_event_with_relay::<_, Type>(
context,
fxlang::BattleEvent::ForceTeraType,
context.mon().tera_type,
);
context
.mon_mut()
.volatile_state
.effect_cache
.effective_tera_type = Some(effective_tera_type);
effective_tera_type
}