use alloc::{
borrow::ToOwned,
boxed::Box,
string::String,
vec::Vec,
};
use core::fmt::Debug;
use anyhow::Result;
use battler_data::Fraction;
use hashbrown::HashMap;
use crate::{
battle::{
Context,
CoreBattle,
Mon,
MonHandle,
},
effect::{
AppliedEffectLocation,
EffectHandle,
fxlang::Value,
},
error::{
WrapOptionError,
integer_overflow_error,
},
};
#[derive(Debug, Default, Clone, PartialEq, Eq)]
enum EffectActivationState {
Starting,
Started,
Ending,
#[default]
Ended,
}
#[derive(Debug, Default, Clone)]
pub struct EffectState {
initialized: bool,
activation_state: EffectActivationState,
values: HashMap<String, Value>,
linked_id: Option<u32>,
linked_to: Vec<u32>,
effect_order: u32,
}
impl EffectState {
const DURATION: &'static str = "duration";
const TARGET: &'static str = "target";
const SOURCE_EFFECT: &'static str = "source_effect";
const SOURCE: &'static str = "source";
const SOURCE_SIDE: &'static str = "source_side";
const SOURCE_POSITION: &'static str = "source_position";
pub fn initial_effect_state(
context: &mut Context,
source_effect: Option<&EffectHandle>,
target: Option<MonHandle>,
source: Option<MonHandle>,
) -> Result<Self> {
let mut effect_state = Self::default();
effect_state.initialize(context, source_effect, target, source)?;
Ok(effect_state)
}
pub fn initialize(
&mut self,
context: &mut Context,
source_effect: Option<&EffectHandle>,
target: Option<MonHandle>,
source: Option<MonHandle>,
) -> Result<()> {
self.activation_state = EffectActivationState::Ended;
self.effect_order = context.battle_mut().next_effect_order();
if let Some(source_effect) = source_effect {
self.set_source_effect(source_effect.stable_effect_handle(context)?);
}
if let Some(target_handle) = target {
self.set_target(target_handle);
}
if let Some(source_handle) = source {
self.set_source(source_handle);
let mut context = context.mon_context(source_handle)?;
self.set_source_side(context.mon().side);
if let Some(source_position) = Mon::position_on_side(&mut context) {
self.set_source_position(source_position)?;
}
}
self.initialized = true;
Ok(())
}
pub fn initialized(&self) -> bool {
self.initialized
}
pub fn get(&self, key: &str) -> Option<&Value> {
self.values.get(key)
}
pub fn get_mut(&mut self, key: &str) -> &mut Value {
self.values
.entry(key.to_owned())
.or_insert(Value::Undefined)
}
pub fn duration(&self) -> Option<u8> {
self.get(Self::DURATION)?.clone().integer_u8().ok()
}
pub fn set_duration(&mut self, duration: u8) {
self.values
.insert(Self::DURATION.to_owned(), Value::UFraction(duration.into()));
}
pub fn target(&self) -> Option<MonHandle> {
self.get(Self::TARGET)?.clone().mon_handle().ok()
}
pub fn set_target(&mut self, target: MonHandle) {
self.values
.insert(Self::TARGET.to_owned(), Value::Mon(target));
}
pub fn source_effect(&self) -> Option<EffectHandle> {
self.get(Self::SOURCE_EFFECT)?.clone().effect_handle().ok()
}
pub fn set_source_effect(&mut self, source_effect: EffectHandle) {
self.values.insert(
Self::SOURCE_EFFECT.to_owned(),
Value::Effect(source_effect.clone()),
);
}
pub fn source(&self) -> Option<MonHandle> {
self.get(Self::SOURCE)?.clone().mon_handle().ok()
}
pub fn set_source(&mut self, source: MonHandle) {
self.values
.insert(Self::SOURCE.to_owned(), Value::Mon(source));
}
pub fn source_side(&self) -> Option<usize> {
self.get(Self::SOURCE_SIDE)?.clone().side_index().ok()
}
pub fn set_source_side(&mut self, source_side: usize) {
self.values
.insert(Self::SOURCE_SIDE.to_owned(), Value::Side(source_side));
}
pub fn source_position(&self) -> Option<usize> {
self.get(Self::SOURCE_POSITION)?
.clone()
.integer_usize()
.ok()
}
pub fn set_source_position(&mut self, source_position: usize) -> Result<()> {
self.values.insert(
Self::SOURCE_POSITION.to_owned(),
Value::UFraction(Fraction::from(
TryInto::<u32>::try_into(source_position).map_err(integer_overflow_error)?,
)),
);
Ok(())
}
fn set_activation_state(&mut self, activation_state: EffectActivationState) {
self.activation_state = activation_state;
*self.get_mut("started") = Value::Boolean(self.started());
*self.get_mut("ending") = Value::Boolean(self.ending());
}
pub fn started(&self) -> bool {
self.activation_state == EffectActivationState::Started
}
pub fn ending(&self) -> bool {
self.activation_state == EffectActivationState::Ending
}
pub fn linked_id(&self) -> Option<u32> {
self.linked_id
}
pub fn set_linked_id(&mut self, linked_id: u32) {
self.linked_id = Some(linked_id);
}
pub fn linked_to(&self) -> impl Iterator<Item = &u32> {
self.linked_to.iter()
}
pub fn add_link(&mut self, linked_id: u32) {
self.linked_to.push(linked_id);
}
pub fn remove_link(&mut self, linked_id: u32) {
self.linked_to.retain(|id| *id != linked_id);
}
pub fn effect_order(&self) -> u32 {
self.effect_order
}
pub fn set_effect_order(&mut self, effect_order: u32) {
self.effect_order = effect_order;
}
}
pub trait EffectStateConnector: Debug + Send {
fn exists(&self, context: &mut Context) -> Result<bool>;
fn get_mut<'a>(&self, context: &'a mut Context) -> Result<Option<&'a mut EffectState>>;
fn applied_effect_location(&self) -> AppliedEffectLocation;
fn make_dynamic(&self) -> DynamicEffectStateConnector;
}
#[derive(Debug)]
pub struct DynamicEffectStateConnector(Box<dyn EffectStateConnector>);
impl DynamicEffectStateConnector {
pub fn new<T>(connector: T) -> Self
where
T: EffectStateConnector + 'static,
{
Self(Box::new(connector))
}
pub fn exists(&self, context: &mut Context) -> Result<bool> {
self.0.exists(context)
}
pub fn get_mut<'a>(&self, context: &'a mut Context) -> Result<&'a mut EffectState> {
Ok(self
.0
.get_mut(context)?
.wrap_expectation("effect state is not defined")?)
}
pub fn applied_effect_location(&self) -> AppliedEffectLocation {
self.0.applied_effect_location()
}
fn set_activation_state(
&self,
context: &mut Context,
state: EffectActivationState,
) -> Result<()> {
self.get_mut(context)?.set_activation_state(state);
CoreBattle::invalidate_effect_caches(context)?;
Ok(())
}
pub fn set_starting(&self, context: &mut Context) -> Result<()> {
self.set_activation_state(context, EffectActivationState::Starting)
}
pub fn set_started(&self, context: &mut Context) -> Result<()> {
self.set_activation_state(context, EffectActivationState::Started)
}
pub fn set_ending(&self, context: &mut Context) -> Result<()> {
self.set_activation_state(context, EffectActivationState::Ending)
}
pub fn set_ended(&self, context: &mut Context) -> Result<()> {
self.set_activation_state(context, EffectActivationState::Ended)
}
}
impl Clone for DynamicEffectStateConnector {
fn clone(&self) -> Self {
self.0.make_dynamic()
}
}