use crate::battle::{Battle, BattleRules};
use crate::error::{WeaselError, WeaselResult};
use crate::event::{Event, EventKind, EventProcessor, EventQueue, EventRights, EventTrigger};
use crate::round::TurnStateType;
use crate::team::{Call, TeamId, TeamRules};
use crate::util::Id;
#[cfg(feature = "serialization")]
use serde::{Deserialize, Serialize};
use std::any::Any;
pub type Power<R> = <<R as BattleRules>::TR as TeamRules<R>>::Power;
pub type PowerId<R> = <Power<R> as Id>::Id;
pub type PowersSeed<R> = <<R as BattleRules>::TR as TeamRules<R>>::PowersSeed;
pub type Invocation<R> = <<R as BattleRules>::TR as TeamRules<R>>::Invocation;
pub type PowersAlteration<R> = <<R as BattleRules>::TR as TeamRules<R>>::PowersAlteration;
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub struct InvokePower<R: BattleRules> {
#[cfg_attr(
feature = "serialization",
serde(bound(
serialize = "TeamId<R>: Serialize",
deserialize = "TeamId<R>: Deserialize<'de>"
))
)]
team_id: TeamId<R>,
#[cfg_attr(
feature = "serialization",
serde(bound(
serialize = "PowerId<R>: Serialize",
deserialize = "PowerId<R>: Deserialize<'de>"
))
)]
power_id: PowerId<R>,
#[cfg_attr(
feature = "serialization",
serde(bound(
serialize = "Option<Invocation<R>>: Serialize",
deserialize = "Option<Invocation<R>>: Deserialize<'de>"
))
)]
invocation: Option<Invocation<R>>,
}
impl<R: BattleRules> InvokePower<R> {
pub fn trigger<P: EventProcessor<R>>(
processor: &mut P,
team_id: TeamId<R>,
power_id: PowerId<R>,
) -> InvokePowerTrigger<R, P> {
InvokePowerTrigger {
processor,
team_id,
power_id,
invocation: None,
}
}
pub fn team_id(&self) -> &TeamId<R> {
&self.team_id
}
pub fn power_id(&self) -> &PowerId<R> {
&self.power_id
}
pub fn invocation(&self) -> &Option<Invocation<R>> {
&self.invocation
}
}
impl<R: BattleRules> std::fmt::Debug for InvokePower<R> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"InvokePower {{ team_id: {:?}, power_id: {:?}, invocation: {:?} }}",
self.team_id, self.power_id, self.invocation
)
}
}
impl<R: BattleRules> Clone for InvokePower<R> {
fn clone(&self) -> Self {
InvokePower {
team_id: self.team_id.clone(),
power_id: self.power_id.clone(),
invocation: self.invocation.clone(),
}
}
}
impl<R: BattleRules + 'static> Event<R> for InvokePower<R> {
fn verify(&self, battle: &Battle<R>) -> WeaselResult<(), R> {
if let Some(team) = battle.entities().team(&self.team_id) {
let ready = match battle.state.rounds.state() {
TurnStateType::Ready => true,
TurnStateType::Started(entities) => entities.iter().any(|e| {
battle
.state
.entities
.actor(&e)
.map(|a| *a.team_id() == self.team_id)
.unwrap_or(false)
}),
};
if !ready {
return Err(WeaselError::TeamNotReady(self.team_id.clone()));
}
if let Some(power) = team.power(&self.power_id) {
battle
.rules
.team_rules()
.invocable(&battle.state, Call::new(team, power, &self.invocation))
.map_err(|err| {
WeaselError::PowerNotInvocable(
self.team_id.clone(),
self.power_id.clone(),
Box::new(err),
)
})
} else {
Err(WeaselError::PowerNotKnown(
self.team_id.clone(),
self.power_id.clone(),
))
}
} else {
Err(WeaselError::TeamNotFound(self.team_id.clone()))
}
}
fn apply(&self, battle: &mut Battle<R>, event_queue: &mut Option<EventQueue<R>>) {
let team = battle
.state
.entities
.team(&self.team_id)
.unwrap_or_else(|| panic!("constraint violated: team {:?} not found", self.team_id));
let power = team.power(&self.power_id).unwrap_or_else(|| {
panic!(
"constraint violated: power {:?} not found in team {:?}",
self.power_id, self.team_id
)
});
battle.rules.team_rules().invoke(
&battle.state,
Call::new(team, power, &self.invocation),
event_queue,
&mut battle.entropy,
&mut battle.metrics.write_handle(),
);
}
fn kind(&self) -> EventKind {
EventKind::InvokePower
}
fn box_clone(&self) -> Box<dyn Event<R> + Send> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn rights<'a>(&'a self, _: &'a Battle<R>) -> EventRights<'a, R> {
EventRights::Team(&self.team_id)
}
}
pub struct InvokePowerTrigger<'a, R, P>
where
R: BattleRules,
P: EventProcessor<R>,
{
processor: &'a mut P,
team_id: TeamId<R>,
power_id: PowerId<R>,
invocation: Option<Invocation<R>>,
}
impl<'a, R, P> InvokePowerTrigger<'a, R, P>
where
R: BattleRules + 'static,
P: EventProcessor<R>,
{
pub fn invocation(&'a mut self, invocation: Invocation<R>) -> &'a mut Self {
self.invocation = Some(invocation);
self
}
}
impl<'a, R, P> EventTrigger<'a, R, P> for InvokePowerTrigger<'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(InvokePower {
team_id: self.team_id.clone(),
power_id: self.power_id.clone(),
invocation: self.invocation.clone(),
})
}
}