use alloc::{
borrow::ToOwned,
format,
string::{
String,
ToString,
},
};
use core::ops::{
Deref,
DerefMut,
};
use anyhow::Result;
use battler_data::{
Id,
Identifiable,
};
use zone_alloc::{
ElementRef,
ElementRefMut,
};
use crate::{
abilities::Ability,
battle::{
Context,
MoveHandle,
},
common::split_once_optional,
conditions::Condition,
config::Clause,
effect::fxlang,
items::Item,
mons::Species,
moves::{
Move,
MoveHitEffectType,
},
};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct NonExistentEffect {
pub name: String,
pub id: Id,
}
impl NonExistentEffect {
pub fn new(id: Id) -> Self {
Self {
name: id.to_string(),
id,
}
}
}
impl Identifiable for NonExistentEffect {
fn id(&self) -> &Id {
&self.id
}
}
#[allow(dead_code)]
pub enum MaybeElementRef<'a, T> {
Owned(ElementRefMut<'a, T>),
Unowned(&'a mut T),
}
impl<T> Deref for MaybeElementRef<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match self {
Self::Owned(val) => val.deref(),
Self::Unowned(val) => val,
}
}
}
impl<T> DerefMut for MaybeElementRef<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
Self::Owned(val) => val.deref_mut(),
Self::Unowned(val) => val,
}
}
}
impl<T> AsMut<T> for MaybeElementRef<'_, T> {
fn as_mut(&mut self) -> &mut T {
self.deref_mut()
}
}
impl<'a, T> From<ElementRefMut<'a, T>> for MaybeElementRef<'a, T> {
fn from(value: ElementRefMut<'a, T>) -> Self {
Self::Owned(value)
}
}
impl<'a, T> From<&'a mut T> for MaybeElementRef<'a, T> {
fn from(value: &'a mut T) -> Self {
Self::Unowned(value)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum EffectType {
Move,
Ability,
Condition,
MoveCondition,
AbilityCondition,
Item,
ItemCondition,
Clause,
Species,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum EffectHandle {
ActiveMove(MoveHandle, MoveHitEffectType),
MoveCondition(Id),
InactiveMove(Id),
Ability(Id),
AbilityCondition(Id),
Condition(Id),
Item(Id),
ItemCondition(Id),
Clause(Id),
Species(Id),
OutsideCondition(Id),
NonExistent(NonExistentEffect),
}
impl EffectHandle {
pub fn from_fxlang_id(fxlang_id: &str) -> Self {
let (effect_type, id) = split_once_optional(fxlang_id, ':');
let (effect_type, id) = match id {
Some(id) => (effect_type, id),
None => ("condition", fxlang_id),
};
let id = Id::from(id);
match effect_type {
"ability" => Self::Ability(id),
"abilitycondition" => Self::AbilityCondition(id),
"clause" => Self::Clause(id),
"item" => Self::Item(id),
"itemcondition" => Self::ItemCondition(id),
"move" => Self::InactiveMove(id),
"movecondition" => Self::MoveCondition(id),
"species" => Self::Species(id),
_ => Self::Condition(id),
}
}
pub fn is_ability(&self) -> bool {
match self {
Self::Ability(_) | Self::AbilityCondition(_) => true,
_ => false,
}
}
pub fn is_active_move(&self) -> bool {
match self {
Self::ActiveMove(_, _) => true,
_ => false,
}
}
pub fn is_active_move_secondary(&self) -> bool {
match self {
Self::ActiveMove(_, MoveHitEffectType::SecondaryEffect(_, _, _)) => true,
_ => false,
}
}
pub fn is_item(&self) -> bool {
match self {
Self::Item(_) | Self::ItemCondition(_) => true,
_ => false,
}
}
pub fn try_id(&self) -> Option<&Id> {
match self {
Self::ActiveMove(_, _) => None,
Self::MoveCondition(id) => Some(&id),
Self::InactiveMove(id) => Some(&id),
Self::Ability(id) => Some(&id),
Self::AbilityCondition(id) => Some(&id),
Self::Condition(id) => Some(&id),
Self::Item(id) => Some(&id),
Self::ItemCondition(id) => Some(&id),
Self::Clause(id) => Some(&id),
Self::Species(id) => Some(&id),
Self::OutsideCondition(id) => Some(&id),
Self::NonExistent(effect) => Some(&effect.id),
}
}
pub fn stable_effect_handle(&self, context: &Context) -> Result<EffectHandle> {
match self {
Self::ActiveMove(active_move_handle, _) => Ok(EffectHandle::InactiveMove(
context.active_move(*active_move_handle)?.id().clone(),
)),
val @ _ => Ok(val.clone()),
}
}
pub fn condition_handle(&self, context: &Context) -> Result<Option<EffectHandle>> {
match self {
Self::ActiveMove(active_move_handle, _) => Ok(Some(EffectHandle::MoveCondition(
context.active_move(*active_move_handle)?.id().clone(),
))),
Self::InactiveMove(id) => Ok(Some(EffectHandle::MoveCondition(id.clone()))),
Self::Ability(id) => Ok(Some(EffectHandle::AbilityCondition(id.clone()))),
Self::Item(id) => Ok(Some(EffectHandle::ItemCondition(id.clone()))),
_ => Ok(None),
}
}
pub fn non_condition_handle(&self) -> Option<EffectHandle> {
match self {
EffectHandle::MoveCondition(id) => Some(EffectHandle::InactiveMove(id.clone())),
EffectHandle::AbilityCondition(id) => Some(EffectHandle::Ability(id.clone())),
EffectHandle::ItemCondition(id) => Some(EffectHandle::Item(id.clone())),
EffectHandle::Condition(_) => None,
handle @ _ => Some(handle.clone()),
}
}
pub fn unlinked_fxlang_id(&self) -> Option<String> {
match self {
Self::ActiveMove(active_move_handle, _) => {
Some(format!("activemove:{active_move_handle}"))
}
_ => None,
}
}
}
impl From<MoveHandle> for EffectHandle {
fn from(value: MoveHandle) -> Self {
Self::ActiveMove(value, MoveHitEffectType::PrimaryEffect)
}
}
pub enum Effect<'borrow> {
ActiveMove(&'borrow mut Move, MoveHitEffectType),
MoveCondition(ElementRef<'borrow, Move>),
InactiveMove(ElementRef<'borrow, Move>),
Ability(ElementRef<'borrow, Ability>),
AbilityCondition(ElementRef<'borrow, Ability>),
Condition(ElementRef<'borrow, Condition>),
Item(ElementRef<'borrow, Item>),
ItemCondition(ElementRef<'borrow, Item>),
Clause(ElementRef<'borrow, Clause>),
Species(ElementRef<'borrow, Species>),
NonExistent(NonExistentEffect),
}
impl<'borrow> Effect<'borrow> {
pub fn for_active_move(
active_move: &'borrow mut Move,
hit_effect_type: MoveHitEffectType,
) -> Self {
Self::ActiveMove(active_move, hit_effect_type)
}
pub fn for_ability(ability: ElementRef<'borrow, Ability>) -> Self {
Self::Ability(ability)
}
pub fn for_ability_condition(ability: ElementRef<'borrow, Ability>) -> Self {
Self::AbilityCondition(ability)
}
pub fn for_condition(condition: ElementRef<'borrow, Condition>) -> Self {
Self::Condition(condition)
}
pub fn for_move_condition(mov: ElementRef<'borrow, Move>) -> Self {
Self::MoveCondition(mov)
}
pub fn for_item(item: ElementRef<'borrow, Item>) -> Self {
Self::Item(item)
}
pub fn for_item_condition(item: ElementRef<'borrow, Item>) -> Self {
Self::ItemCondition(item)
}
pub fn for_clause(clause: ElementRef<'borrow, Clause>) -> Self {
Self::Clause(clause)
}
pub fn for_species(species: ElementRef<'borrow, Species>) -> Self {
Self::Species(species)
}
pub fn for_inactive_move(mov: ElementRef<'borrow, Move>) -> Self {
Self::InactiveMove(mov)
}
pub fn for_non_existent(effect: NonExistentEffect) -> Self {
Self::NonExistent(effect)
}
pub fn name(&self) -> &str {
match self {
Self::ActiveMove(active_move, _) => &active_move.data.name,
Self::MoveCondition(mov) | Self::InactiveMove(mov) => &mov.data.name,
Self::Ability(ability) | Self::AbilityCondition(ability) => &ability.data.name,
Self::Condition(condition) => &condition.data.name,
Self::Item(item) | Self::ItemCondition(item) => &item.data.name,
Self::Clause(clause) => &clause.data.name,
Self::Species(species) => &species.data.name,
Self::NonExistent(effect) => &effect.name,
}
}
pub fn effect_type(&self) -> EffectType {
match self {
Self::ActiveMove(_, _) => EffectType::Move,
Self::MoveCondition(_) => EffectType::MoveCondition,
Self::InactiveMove(_) => EffectType::Move,
Self::Ability(_) => EffectType::Ability,
Self::AbilityCondition(_) => EffectType::AbilityCondition,
Self::Condition(_) => EffectType::Condition,
Self::Item(_) => EffectType::Item,
Self::ItemCondition(_) => EffectType::ItemCondition,
Self::Clause(_) => EffectType::Clause,
Self::Species(_) => EffectType::Species,
Self::NonExistent(_) => EffectType::Condition,
}
}
fn effect_type_name(&self) -> &str {
match self {
Self::ActiveMove(_, _) | Self::MoveCondition(_) | Self::InactiveMove(_) => "move",
Self::Ability(_) | Self::AbilityCondition(_) => "ability",
Self::Condition(condition) => condition.condition_type_name(),
Self::Item(_) | Self::ItemCondition(_) => "item",
Self::Clause(_) => "clause",
Self::Species(_) => "species",
Self::NonExistent(_) => "",
}
}
pub fn full_name(&self) -> String {
match self.effect_type_name() {
"" => self.name().to_owned(),
prefix => format!("{prefix}:{}", self.name()),
}
}
fn fxlang_id_effect_type_name(&self) -> String {
match self {
Self::ActiveMove(_, hit_effect_type) => match hit_effect_type.secondary_index() {
None => "move".to_owned(),
Some((target, hit, secondary_index)) => {
format!("movesecondary-{hit}-{target}-{secondary_index}")
}
},
Self::MoveCondition(_) => "movecondition".to_owned(),
Self::InactiveMove(_) => "move".to_owned(),
Self::Ability(_) => "ability".to_owned(),
Self::AbilityCondition(_) => "abilitycondition".to_owned(),
Self::Condition(condition) => condition.condition_type_name().to_owned(),
Self::Item(_) => "item".to_owned(),
Self::ItemCondition(_) => "itemcondition".to_owned(),
Self::Clause(_) => "clause".to_owned(),
Self::Species(_) => "species".to_owned(),
Self::NonExistent(_) => "condition".to_owned(),
}
}
pub fn fxlang_id(&self) -> String {
match self.fxlang_id_effect_type_name().as_str() {
"" => format!("{}", self.id()),
prefix => format!("{prefix}:{}", self.id()),
}
}
pub fn move_effect<'effect>(&'effect self) -> Option<&'effect Move> {
match self {
Self::ActiveMove(active_move, _) => Some(active_move.deref()),
Self::InactiveMove(mov) => Some(mov),
Self::MoveCondition(mov) => Some(mov),
_ => None,
}
}
pub fn condition<'effect>(&'effect self) -> Option<&'effect Condition> {
match self {
Self::Condition(condition) => Some(condition.deref()),
_ => None,
}
}
pub fn fxlang_effect<'effect>(&'effect self) -> Option<&'effect fxlang::Effect> {
match self {
Self::ActiveMove(active_move, hit_effect_type) => {
active_move.fxlang_effect(*hit_effect_type)
}
Self::MoveCondition(mov) => Some(&mov.condition),
Self::InactiveMove(mov) => Some(&mov.effect),
Self::Ability(ability) => Some(&ability.effect),
Self::AbilityCondition(ability) => Some(&ability.condition),
Self::Condition(condition) => Some(&condition.condition),
Self::Item(item) => Some(&item.effect),
Self::ItemCondition(item) => Some(&item.condition),
Self::Clause(clause) => Some(&clause.effect),
Self::Species(species) => Some(&species.effect),
Self::NonExistent(_) => None,
}
}
pub fn unlinked(&self) -> bool {
match self {
Self::ActiveMove(active_move, _) => active_move.unlinked,
_ => false,
}
}
}
impl Identifiable for Effect<'_> {
fn id(&self) -> &Id {
match self {
Self::ActiveMove(active_move, _) => active_move.id(),
Self::MoveCondition(mov) | Self::InactiveMove(mov) => mov.id(),
Self::Ability(ability) | Self::AbilityCondition(ability) => ability.id(),
Self::Condition(condition) => condition.id(),
Self::Item(item) | Self::ItemCondition(item) => item.id(),
Self::Clause(clause) => clause.id(),
Self::Species(species) => species.id(),
Self::NonExistent(effect) => effect.id(),
}
}
}