use crate::battle::{Battle, BattleRules};
use crate::character::{verify_get_character, CharacterRules};
use crate::entity::EntityId;
use crate::error::{WeaselError, WeaselResult};
use crate::event::{
Event, EventId, EventKind, EventProcessor, EventQueue, EventTrigger, LinkedQueue,
};
use crate::fight::FightRules;
use crate::util::Id;
#[cfg(feature = "serialization")]
use serde::{Deserialize, Serialize};
use std::any::Any;
use std::fmt::{Debug, Formatter, Result};
pub type Status<R> = <<R as BattleRules>::CR as CharacterRules<R>>::Status;
pub type StatusId<R> = <Status<R> as Id>::Id;
pub type Potency<R> = <<R as BattleRules>::FR as FightRules<R>>::Potency;
pub type StatusesAlteration<R> = <<R as BattleRules>::CR as CharacterRules<R>>::StatusesAlteration;
pub type StatusDuration = EventId;
pub struct AppliedStatus<R: BattleRules> {
status: Status<R>,
origin: Option<EventId>,
duration: StatusDuration,
}
impl<R: BattleRules> AppliedStatus<R> {
pub fn new(status: Status<R>) -> Self {
Self {
status,
origin: None,
duration: 0,
}
}
pub fn with_origin(status: Status<R>, origin: EventId) -> Self {
Self {
status,
origin: Some(origin),
duration: 0,
}
}
pub fn status(&self) -> &Status<R> {
&self.status
}
pub fn status_mut(&mut self) -> &mut Status<R> {
&mut self.status
}
pub fn origin(&self) -> Option<EventId> {
self.origin
}
pub fn duration(&self) -> StatusDuration {
self.duration
}
pub(crate) fn update(&mut self) {
self.duration += 1;
}
}
impl<R: BattleRules> std::ops::Deref for AppliedStatus<R> {
type Target = Status<R>;
fn deref(&self) -> &Self::Target {
&self.status
}
}
impl<R: BattleRules> std::ops::DerefMut for AppliedStatus<R> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.status
}
}
pub type NewStatus<R> = AppliedStatus<R>;
pub type OldStatus<R> = AppliedStatus<R>;
pub enum Application<'a, R: BattleRules> {
New(&'a NewStatus<R>),
Replacement(&'a OldStatus<R>, &'a NewStatus<R>),
}
pub(crate) fn update_statuses<R: BattleRules + 'static>(
id: &EntityId<R>,
battle: &mut Battle<R>,
event_queue: &mut Option<EventQueue<R>>,
) -> WeaselResult<(), R> {
let character = battle
.state
.entities
.character_mut(id)
.ok_or_else(|| WeaselError::EntityNotFound(id.clone()))?;
for status in character.statuses_mut() {
status.update();
}
let character = battle
.state
.entities
.character(id)
.ok_or_else(|| WeaselError::EntityNotFound(id.clone()))?;
for status in character.statuses() {
let terminated = battle.rules.fight_rules().update_status(
&battle.state,
character,
status,
&mut event_queue
.as_mut()
.map(|queue| LinkedQueue::new(queue, status.origin())),
&mut battle.entropy,
&mut battle.metrics.write_handle(),
);
if terminated {
ClearStatus::trigger(
event_queue,
character.entity_id().clone(),
status.id().clone(),
)
.fire();
}
}
Ok(())
}
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub struct InflictStatus<R: BattleRules> {
#[cfg_attr(
feature = "serialization",
serde(bound(
serialize = "EntityId<R>: Serialize",
deserialize = "EntityId<R>: Deserialize<'de>"
))
)]
entity_id: EntityId<R>,
#[cfg_attr(
feature = "serialization",
serde(bound(
serialize = "StatusId<R>: Serialize",
deserialize = "StatusId<R>: Deserialize<'de>"
))
)]
status_id: StatusId<R>,
#[cfg_attr(
feature = "serialization",
serde(bound(
serialize = "Option<Potency<R>>: Serialize",
deserialize = "Option<Potency<R>>: Deserialize<'de>"
))
)]
potency: Option<Potency<R>>,
}
impl<R: BattleRules> InflictStatus<R> {
pub fn trigger<'a, P: EventProcessor<R>>(
processor: &'a mut P,
entity_id: EntityId<R>,
status_id: StatusId<R>,
) -> InflictStatusTrigger<'a, R, P> {
InflictStatusTrigger {
processor,
entity_id,
status_id,
potency: None,
}
}
pub fn entity_id(&self) -> &EntityId<R> {
&self.entity_id
}
pub fn status_id(&self) -> &StatusId<R> {
&self.status_id
}
pub fn potency(&self) -> &Option<Potency<R>> {
&self.potency
}
}
impl<R: BattleRules> Debug for InflictStatus<R> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!(
f,
"InflictStatus {{ entity_id: {:?}, status_id: {:?}, potency: {:?} }}",
self.entity_id, self.status_id, self.potency
)
}
}
impl<R: BattleRules> Clone for InflictStatus<R> {
fn clone(&self) -> Self {
Self {
entity_id: self.entity_id.clone(),
status_id: self.status_id.clone(),
potency: self.potency.clone(),
}
}
}
impl<R: BattleRules + 'static> Event<R> for InflictStatus<R> {
fn verify(&self, battle: &Battle<R>) -> WeaselResult<(), R> {
verify_get_character(battle.entities(), &self.entity_id).map(|_| ())
}
fn apply(&self, battle: &mut Battle<R>, event_queue: &mut Option<EventQueue<R>>) {
let character = battle
.state
.entities
.character_mut(&self.entity_id)
.unwrap_or_else(|| {
panic!(
"constraint violated: character {:?} not found",
self.entity_id
)
});
let status = battle.rules.character_rules().generate_status(
character,
&self.status_id,
&self.potency,
&mut battle.entropy,
&mut battle.metrics.write_handle(),
);
if let Some(status) = status {
let origin = battle.history.next_id();
let old_status = character.add_status(AppliedStatus::with_origin(status, origin));
let character = battle
.state
.entities
.character(&self.entity_id)
.unwrap_or_else(|| {
panic!(
"constraint violated: character {:?} not found",
self.entity_id
)
});
let status = character.status(&self.status_id).unwrap_or_else(|| {
panic!(
"constraint violated: status {:?} not found in {:?}",
self.status_id, self.entity_id
)
});
let application = if let Some(old) = old_status.as_ref() {
Application::Replacement(old, status)
} else {
Application::New(status)
};
battle.rules.fight_rules().apply_status(
&battle.state,
character,
application,
event_queue,
&mut battle.entropy,
&mut battle.metrics.write_handle(),
);
}
}
fn kind(&self) -> EventKind {
EventKind::InflictStatus
}
fn box_clone(&self) -> Box<dyn Event<R> + Send> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub struct InflictStatusTrigger<'a, R, P>
where
R: BattleRules,
P: EventProcessor<R>,
{
processor: &'a mut P,
entity_id: EntityId<R>,
status_id: StatusId<R>,
potency: Option<Potency<R>>,
}
impl<'a, R, P> InflictStatusTrigger<'a, R, P>
where
R: BattleRules + 'static,
P: EventProcessor<R>,
{
pub fn potency(&'a mut self, potency: Potency<R>) -> &'a mut Self {
self.potency = Some(potency);
self
}
}
impl<'a, R, P> EventTrigger<'a, R, P> for InflictStatusTrigger<'a, R, P>
where
R: BattleRules + 'static,
P: EventProcessor<R>,
{
fn processor(&'a mut self) -> &'a mut P {
self.processor
}
fn event(&self) -> Box<dyn Event<R> + Send> {
Box::new(InflictStatus {
entity_id: self.entity_id.clone(),
status_id: self.status_id.clone(),
potency: self.potency.clone(),
})
}
}
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub struct ClearStatus<R: BattleRules> {
#[cfg_attr(
feature = "serialization",
serde(bound(
serialize = "EntityId<R>: Serialize",
deserialize = "EntityId<R>: Deserialize<'de>"
))
)]
entity_id: EntityId<R>,
#[cfg_attr(
feature = "serialization",
serde(bound(
serialize = "StatusId<R>: Serialize",
deserialize = "StatusId<R>: Deserialize<'de>"
))
)]
status_id: StatusId<R>,
}
impl<R: BattleRules> ClearStatus<R> {
pub fn trigger<'a, P: EventProcessor<R>>(
processor: &'a mut P,
entity_id: EntityId<R>,
status_id: StatusId<R>,
) -> ClearStatusTrigger<'a, R, P> {
ClearStatusTrigger {
processor,
entity_id,
status_id,
}
}
pub fn entity_id(&self) -> &EntityId<R> {
&self.entity_id
}
pub fn status_id(&self) -> &StatusId<R> {
&self.status_id
}
}
impl<R: BattleRules> Debug for ClearStatus<R> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!(
f,
"ClearStatus {{ entity_id: {:?}, status_id: {:?} }}",
self.entity_id, self.status_id
)
}
}
impl<R: BattleRules> Clone for ClearStatus<R> {
fn clone(&self) -> Self {
Self {
entity_id: self.entity_id.clone(),
status_id: self.status_id.clone(),
}
}
}
impl<R: BattleRules + 'static> Event<R> for ClearStatus<R> {
fn verify(&self, battle: &Battle<R>) -> WeaselResult<(), R> {
let character = verify_get_character(battle.entities(), &self.entity_id)?;
if character.status(&self.status_id).is_none() {
Err(WeaselError::StatusNotPresent(
self.entity_id.clone(),
self.status_id.clone(),
))
} else {
Ok(())
}
}
fn apply(&self, battle: &mut Battle<R>, event_queue: &mut Option<EventQueue<R>>) {
let character = battle
.state
.entities
.character(&self.entity_id)
.unwrap_or_else(|| {
panic!(
"constraint violated: character {:?} not found",
self.entity_id
)
});
let status = character.status(&self.status_id).unwrap_or_else(|| {
panic!(
"constraint violated: status {:?} not found in {:?}",
self.status_id, self.entity_id
)
});
battle.rules.fight_rules().delete_status(
&battle.state,
character,
status,
event_queue,
&mut battle.entropy,
&mut battle.metrics.write_handle(),
);
let character = battle
.state
.entities
.character_mut(&self.entity_id)
.unwrap_or_else(|| {
panic!(
"constraint violated: character {:?} not found",
self.entity_id
)
});
character.remove_status(&self.status_id);
}
fn kind(&self) -> EventKind {
EventKind::ClearStatus
}
fn box_clone(&self) -> Box<dyn Event<R> + Send> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub struct ClearStatusTrigger<'a, R, P>
where
R: BattleRules,
P: EventProcessor<R>,
{
processor: &'a mut P,
entity_id: EntityId<R>,
status_id: StatusId<R>,
}
impl<'a, R, P> EventTrigger<'a, R, P> for ClearStatusTrigger<'a, R, P>
where
R: BattleRules + 'static,
P: EventProcessor<R>,
{
fn processor(&'a mut self) -> &'a mut P {
self.processor
}
fn event(&self) -> Box<dyn Event<R> + Send> {
Box::new(ClearStatus {
entity_id: self.entity_id.clone(),
status_id: self.status_id.clone(),
})
}
}
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub struct AlterStatuses<R: BattleRules> {
#[cfg_attr(
feature = "serialization",
serde(bound(
serialize = "EntityId<R>: Serialize",
deserialize = "EntityId<R>: Deserialize<'de>"
))
)]
id: EntityId<R>,
#[cfg_attr(
feature = "serialization",
serde(bound(
serialize = "StatusesAlteration<R>: Serialize",
deserialize = "StatusesAlteration<R>: Deserialize<'de>"
))
)]
alteration: StatusesAlteration<R>,
}
impl<R: BattleRules> AlterStatuses<R> {
pub fn trigger<'a, P: EventProcessor<R>>(
processor: &'a mut P,
id: EntityId<R>,
alteration: StatusesAlteration<R>,
) -> AlterStatusesTrigger<'a, R, P> {
AlterStatusesTrigger {
processor,
id,
alteration,
}
}
pub fn id(&self) -> &EntityId<R> {
&self.id
}
pub fn alteration(&self) -> &StatusesAlteration<R> {
&self.alteration
}
}
impl<R: BattleRules> Debug for AlterStatuses<R> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!(
f,
"AlterStatuses {{ id: {:?}, alteration: {:?} }}",
self.id, self.alteration
)
}
}
impl<R: BattleRules> Clone for AlterStatuses<R> {
fn clone(&self) -> Self {
Self {
id: self.id.clone(),
alteration: self.alteration.clone(),
}
}
}
impl<R: BattleRules + 'static> Event<R> for AlterStatuses<R> {
fn verify(&self, battle: &Battle<R>) -> WeaselResult<(), R> {
verify_get_character(battle.entities(), &self.id).map(|_| ())
}
fn apply(&self, battle: &mut Battle<R>, _: &mut Option<EventQueue<R>>) {
let character = battle
.state
.entities
.character_mut(&self.id)
.unwrap_or_else(|| panic!("constraint violated: character {:?} not found", self.id));
battle.rules.character_rules().alter_statuses(
character,
&self.alteration,
&mut battle.entropy,
&mut battle.metrics.write_handle(),
);
}
fn kind(&self) -> EventKind {
EventKind::AlterStatuses
}
fn box_clone(&self) -> Box<dyn Event<R> + Send> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub struct AlterStatusesTrigger<'a, R, P>
where
R: BattleRules,
P: EventProcessor<R>,
{
processor: &'a mut P,
id: EntityId<R>,
alteration: StatusesAlteration<R>,
}
impl<'a, R, P> EventTrigger<'a, R, P> for AlterStatusesTrigger<'a, R, P>
where
R: BattleRules + 'static,
P: EventProcessor<R>,
{
fn processor(&'a mut self) -> &'a mut P {
self.processor
}
fn event(&self) -> Box<dyn Event<R> + Send> {
Box::new(AlterStatuses {
id: self.id.clone(),
alteration: self.alteration.clone(),
})
}
}