use crate::battle::{Battle, BattleRules, BattleState};
use crate::entity::{transmute_entity, Entities, Entity, EntityId, Transmutation};
use crate::entropy::Entropy;
use crate::error::{WeaselError, WeaselResult};
use crate::event::{Event, EventKind, EventProcessor, EventQueue, EventTrigger, Prioritized};
use crate::metric::WriteMetrics;
use crate::status::{AppliedStatus, Potency, Status, StatusId};
use crate::util::Id;
#[cfg(feature = "serialization")]
use serde::{Deserialize, Serialize};
use std::any::Any;
use std::fmt::{Debug, Formatter, Result};
use std::hash::Hash;
pub trait CharacterRules<R: BattleRules> {
#[cfg(not(feature = "serialization"))]
type CreatureId: Hash + Eq + Clone + Debug + Send;
#[cfg(feature = "serialization")]
type CreatureId: Hash + Eq + Clone + Debug + Send + Serialize + for<'a> Deserialize<'a>;
#[cfg(not(feature = "serialization"))]
type ObjectId: Hash + Eq + Clone + Debug + Send;
#[cfg(feature = "serialization")]
type ObjectId: Hash + Eq + Clone + Debug + Send + Serialize + for<'a> Deserialize<'a>;
type Statistic: Id + 'static;
#[cfg(not(feature = "serialization"))]
type StatisticsSeed: Clone + Debug + Send;
#[cfg(feature = "serialization")]
type StatisticsSeed: Clone + Debug + Send + Serialize + for<'a> Deserialize<'a>;
#[cfg(not(feature = "serialization"))]
type StatisticsAlteration: Clone + Debug + Send;
#[cfg(feature = "serialization")]
type StatisticsAlteration: Clone + Debug + Send + Serialize + for<'a> Deserialize<'a>;
type Status: Id + 'static;
#[cfg(not(feature = "serialization"))]
type StatusesAlteration: Clone + Debug + Send;
#[cfg(feature = "serialization")]
type StatusesAlteration: Clone + Debug + Send + Serialize + for<'a> Deserialize<'a>;
fn generate_statistics(
&self,
_seed: &Option<Self::StatisticsSeed>,
_entropy: &mut Entropy<R>,
_metrics: &mut WriteMetrics<R>,
) -> Box<dyn Iterator<Item = Self::Statistic>> {
Box::new(std::iter::empty())
}
fn alter_statistics(
&self,
_character: &mut dyn Character<R>,
_alteration: &Self::StatisticsAlteration,
_entropy: &mut Entropy<R>,
_metrics: &mut WriteMetrics<R>,
) -> Option<Transmutation> {
None
}
fn generate_status(
&self,
_character: &dyn Character<R>,
_status_id: &StatusId<R>,
_potency: &Option<Potency<R>>,
_entropy: &mut Entropy<R>,
_metrics: &mut WriteMetrics<R>,
) -> Option<Status<R>> {
None
}
fn alter_statuses(
&self,
_character: &mut dyn Character<R>,
_alteration: &Self::StatusesAlteration,
_entropy: &mut Entropy<R>,
_metrics: &mut WriteMetrics<R>,
) {
}
fn on_character_added(
&self,
_state: &BattleState<R>,
_character: &dyn Character<R>,
_event_queue: &mut Option<EventQueue<R>>,
_entropy: &mut Entropy<R>,
_metrics: &mut WriteMetrics<R>,
) {
}
fn on_character_transmuted(
&self,
_state: &BattleState<R>,
_character: &dyn Character<R>,
_transmutation: Transmutation,
_event_queue: &mut Option<EventQueue<R>>,
_entropy: &mut Entropy<R>,
_metrics: &mut WriteMetrics<R>,
) {
}
}
pub type Statistic<R> = <<R as BattleRules>::CR as CharacterRules<R>>::Statistic;
pub type StatisticId<R> = <Statistic<R> as Id>::Id;
pub type StatisticsSeed<R> = <<R as BattleRules>::CR as CharacterRules<R>>::StatisticsSeed;
pub type StatisticsAlteration<R> =
<<R as BattleRules>::CR as CharacterRules<R>>::StatisticsAlteration;
pub trait Character<R: BattleRules>: Entity<R> {
fn statistics<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Statistic<R>> + 'a>;
fn statistics_mut<'a>(&'a mut self) -> Box<dyn Iterator<Item = &'a mut Statistic<R>> + 'a>;
fn statistic(&self, id: &StatisticId<R>) -> Option<&Statistic<R>>;
fn statistic_mut(&mut self, id: &StatisticId<R>) -> Option<&mut Statistic<R>>;
fn add_statistic(&mut self, statistic: Statistic<R>) -> Option<Statistic<R>>;
fn remove_statistic(&mut self, id: &StatisticId<R>) -> Option<Statistic<R>>;
fn statuses<'a>(&'a self) -> Box<dyn Iterator<Item = &'a AppliedStatus<R>> + 'a>;
fn statuses_mut<'a>(&'a mut self) -> Box<dyn Iterator<Item = &'a mut AppliedStatus<R>> + 'a>;
fn status(&self, id: &StatusId<R>) -> Option<&AppliedStatus<R>>;
fn status_mut(&mut self, id: &StatusId<R>) -> Option<&mut AppliedStatus<R>>;
fn add_status(&mut self, status: AppliedStatus<R>) -> Option<AppliedStatus<R>>;
fn remove_status(&mut self, id: &StatusId<R>) -> Option<AppliedStatus<R>>;
}
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub struct AlterStatistics<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 = "StatisticsAlteration<R>: Serialize",
deserialize = "StatisticsAlteration<R>: Deserialize<'de>"
))
)]
alteration: StatisticsAlteration<R>,
}
impl<R: BattleRules> AlterStatistics<R> {
pub fn trigger<'a, P: EventProcessor<R>>(
processor: &'a mut P,
id: EntityId<R>,
alteration: StatisticsAlteration<R>,
) -> AlterStatisticsTrigger<'a, R, P> {
AlterStatisticsTrigger {
processor,
id,
alteration,
}
}
pub fn id(&self) -> &EntityId<R> {
&self.id
}
pub fn alteration(&self) -> &StatisticsAlteration<R> {
&self.alteration
}
}
impl<R: BattleRules> Debug for AlterStatistics<R> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!(
f,
"AlterStatistics {{ id: {:?}, alteration: {:?} }}",
self.id, self.alteration
)
}
}
impl<R: BattleRules> Clone for AlterStatistics<R> {
fn clone(&self) -> Self {
Self {
id: self.id.clone(),
alteration: self.alteration.clone(),
}
}
}
impl<R: BattleRules + 'static> Event<R> for AlterStatistics<R> {
fn verify(&self, battle: &Battle<R>) -> WeaselResult<(), R> {
verify_get_character(battle.entities(), &self.id).map(|_| ())
}
fn apply(&self, battle: &mut Battle<R>, event_queue: &mut Option<EventQueue<R>>) {
let character = battle
.state
.entities
.character_mut(&self.id)
.unwrap_or_else(|| panic!("constraint violated: character {:?} not found", self.id));
let transmutation = battle.rules.character_rules().alter_statistics(
character,
&self.alteration,
&mut battle.entropy,
&mut battle.metrics.write_handle(),
);
if let Some(transmutation) = transmutation {
transmute_entity(
&self.id,
transmutation,
&mut event_queue.as_mut().map(|queue| Prioritized::new(queue)),
);
}
}
fn kind(&self) -> EventKind {
EventKind::AlterStatistics
}
fn box_clone(&self) -> Box<dyn Event<R> + Send> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub struct AlterStatisticsTrigger<'a, R, P>
where
R: BattleRules,
P: EventProcessor<R>,
{
processor: &'a mut P,
id: EntityId<R>,
alteration: StatisticsAlteration<R>,
}
impl<'a, R, P> EventTrigger<'a, R, P> for AlterStatisticsTrigger<'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(AlterStatistics {
id: self.id.clone(),
alteration: self.alteration.clone(),
})
}
}
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub struct RegenerateStatistics<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 = "Option<StatisticsSeed<R>>: Serialize",
deserialize = "Option<StatisticsSeed<R>>: Deserialize<'de>"
))
)]
seed: Option<StatisticsSeed<R>>,
}
impl<R: BattleRules> RegenerateStatistics<R> {
pub fn trigger<P: EventProcessor<R>>(
processor: &'_ mut P,
id: EntityId<R>,
) -> RegenerateStatisticsTrigger<'_, R, P> {
RegenerateStatisticsTrigger {
processor,
id,
seed: None,
}
}
pub fn id(&self) -> &EntityId<R> {
&self.id
}
pub fn seed(&self) -> &Option<StatisticsSeed<R>> {
&self.seed
}
}
impl<R: BattleRules> Debug for RegenerateStatistics<R> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!(
f,
"RegenerateStatistics {{ id: {:?}, seed: {:?} }}",
self.id, self.seed
)
}
}
impl<R: BattleRules> Clone for RegenerateStatistics<R> {
fn clone(&self) -> Self {
Self {
id: self.id.clone(),
seed: self.seed.clone(),
}
}
}
impl<R: BattleRules + 'static> Event<R> for RegenerateStatistics<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));
let statistics: Vec<_> = battle
.rules
.character_rules()
.generate_statistics(
&self.seed,
&mut battle.entropy,
&mut battle.metrics.write_handle(),
)
.collect();
let mut to_remove = Vec::new();
for statistic in character.statistics() {
if statistics
.iter()
.find(|e| e.id() == statistic.id())
.is_none()
{
to_remove.push(statistic.id().clone());
}
}
for statistic_id in to_remove {
character.remove_statistic(&statistic_id);
}
for statistic in statistics {
if character.statistic(statistic.id()).is_none() {
character.add_statistic(statistic);
}
}
}
fn kind(&self) -> EventKind {
EventKind::RegenerateStatistics
}
fn box_clone(&self) -> Box<dyn Event<R> + Send> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub struct RegenerateStatisticsTrigger<'a, R, P>
where
R: BattleRules,
P: EventProcessor<R>,
{
processor: &'a mut P,
id: EntityId<R>,
seed: Option<StatisticsSeed<R>>,
}
impl<'a, R, P> RegenerateStatisticsTrigger<'a, R, P>
where
R: BattleRules + 'static,
P: EventProcessor<R>,
{
pub fn seed(&'a mut self, seed: StatisticsSeed<R>) -> &'a mut Self {
self.seed = Some(seed);
self
}
}
impl<'a, R, P> EventTrigger<'a, R, P> for RegenerateStatisticsTrigger<'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(RegenerateStatistics {
id: self.id.clone(),
seed: self.seed.clone(),
})
}
}
pub(crate) fn verify_get_character<'a, R>(
entities: &'a Entities<R>,
id: &EntityId<R>,
) -> WeaselResult<&'a dyn Character<R>, R>
where
R: BattleRules,
{
if !id.is_character() {
return Err(WeaselError::NotACharacter(id.clone()));
}
let character = entities
.character(id)
.ok_or_else(|| WeaselError::EntityNotFound(id.clone()))?;
Ok(character)
}