use crate::actor::ActorRules;
use crate::character::CharacterRules;
use crate::entity::Entities;
use crate::entropy::{Entropy, EntropyRules};
use crate::error::{WeaselError, WeaselResult};
use crate::event::{
ClientEventPrototype, Event, EventKind, EventProcessor, EventPrototype, EventQueue,
EventTrigger, EventWrapper, Prioritized, VersionedEventWrapper,
};
use crate::fight::FightRules;
use crate::history::History;
use crate::metric::{Metrics, ReadMetrics, WriteMetrics};
use crate::player::{Rights, RightsHandle, RightsHandleMut};
use crate::round::{Rounds, RoundsRules};
use crate::space::{Space, SpaceRules};
use crate::team::{ConcludeObjectives, TeamId, TeamRules};
use crate::user::UserRules;
use crate::util::Id;
#[cfg(feature = "serialization")]
use serde::{Deserialize, Serialize};
use std::any::Any;
use std::fmt::Debug;
use std::marker::PhantomData;
use std::ops::Range;
pub type EventCallback<R> =
Box<dyn FnMut(&EventWrapper<R>, &BattleState<R>, &mut Option<EventQueue<R>>) + Send>;
pub struct Battle<R: BattleRules> {
pub(crate) state: BattleState<R>,
pub(crate) entropy: Entropy<R>,
pub(crate) history: History<R>,
pub(crate) rules: R,
pub(crate) event_callback: Option<EventCallback<R>>,
pub(crate) metrics: Metrics<R>,
rights: Rights<R>,
}
impl<R: BattleRules + 'static> Battle<R> {
pub fn builder(rules: R) -> BattleBuilder<R> {
BattleBuilder {
rules,
event_callback: None,
}
}
pub(crate) fn verify_event(&self, event: &(dyn Event<R> + Send)) -> WeaselResult<(), R> {
if self.phase() == BattlePhase::Ended {
Err(WeaselError::BattleEnded)
} else {
event.verify(&self)
}
}
pub(crate) fn verify_prototype(&self, event: &EventPrototype<R>) -> WeaselResult<(), R> {
if let Some(condition) = event.condition() {
if !condition(&self.state) {
return Err(WeaselError::ConditionUnsatisfied);
}
}
self.verify_event(&***event)
}
pub(crate) fn verify_wrapper(&self, event: &VersionedEventWrapper<R>) -> WeaselResult<(), R> {
let version = self.rules.version();
if event.version() != version {
return Err(WeaselError::IncompatibleVersions(
version.clone(),
event.version().clone(),
));
}
self.history.verify_event(event.wrapper())?;
self.verify_event(&****event)
}
pub(crate) fn verify_client(&self, event: &ClientEventPrototype<R>) -> WeaselResult<(), R> {
let version = self.rules.version();
if event.version() != version {
return Err(WeaselError::IncompatibleVersions(
event.version().clone(),
version.clone(),
));
}
self.verify_event(&***event)
}
pub(crate) fn promote(&self, event: EventPrototype<R>) -> EventWrapper<R> {
event.promote(self.history.next_id())
}
pub(crate) fn apply(&mut self, event: &EventWrapper<R>, queue: &mut Option<EventQueue<R>>) {
event.apply(self, queue);
self.history.archive(event);
Battle::check_objectives(
&self.state,
&self.rules.team_rules(),
&self.metrics.read_handle(),
&mut queue.as_mut().map(|queue| Prioritized::new(queue)),
Checkpoint::EventEnd,
);
if let Some(cb) = &mut self.event_callback {
cb(event, &self.state, queue);
}
}
pub(crate) fn end(&mut self) {
self.state.phase = BattlePhase::Ended;
}
pub fn phase(&self) -> BattlePhase {
self.state.phase
}
pub fn entities(&self) -> &Entities<R> {
&self.state.entities
}
pub fn entities_mut(&mut self) -> &mut Entities<R> {
&mut self.state.entities
}
pub fn history(&self) -> &History<R> {
&self.history
}
pub fn rules(&self) -> &R {
&self.rules
}
pub fn rules_mut(&mut self) -> &mut R {
&mut self.rules
}
pub fn space(&self) -> &Space<R> {
&self.state.space
}
pub fn space_mut(&mut self) -> &mut Space<R> {
&mut self.state.space
}
pub fn entropy(&self) -> &Entropy<R> {
&self.entropy
}
pub fn entropy_mut(&mut self) -> &mut Entropy<R> {
&mut self.entropy
}
pub fn rounds(&self) -> &Rounds<R> {
&self.state.rounds
}
pub fn rounds_mut(&mut self) -> &mut Rounds<R> {
&mut self.state.rounds
}
pub fn metrics(&self) -> ReadMetrics<R> {
self.metrics.read_handle()
}
pub fn metrics_mut(&mut self) -> WriteMetrics<R> {
self.metrics.write_handle()
}
pub(crate) fn rights(&self) -> RightsHandle<R> {
RightsHandle::new(&self.rights)
}
pub(crate) fn rights_mut<'a>(
&'a mut self,
) -> RightsHandleMut<R, impl Iterator<Item = &'a TeamId<R>>> {
RightsHandleMut::new(
&mut self.rights,
self.state.entities().teams().map(|team| team.id()),
)
}
pub fn versioned_events<'a>(
&'a self,
range: Range<usize>,
) -> impl Iterator<Item = VersionedEventWrapper<R>> + 'a {
self.history().events()[range]
.iter()
.map(move |e| e.clone().version(self.rules().version().clone()))
}
pub(crate) fn check_objectives<P>(
state: &BattleState<R>,
rules: &R::TR,
metrics: &ReadMetrics<R>,
processor: &mut P,
checkpoint: Checkpoint,
) where
P: EventProcessor<R>,
{
macro_rules! run_check {
($function: ident) => {{
for team in state
.entities
.teams()
.filter(|team| team.conclusion().is_none())
{
if let Some(conclusion) = rules.$function(state, team, metrics) {
ConcludeObjectives::trigger(processor, team.id().clone(), conclusion)
.fire();
}
}
}};
}
match checkpoint {
Checkpoint::TurnEnd => {
run_check!(check_objectives_on_turn);
}
Checkpoint::EventEnd => {
run_check!(check_objectives_on_event);
}
}
}
}
pub(crate) enum Checkpoint {
TurnEnd,
EventEnd,
}
pub struct BattleState<R: BattleRules> {
pub(crate) entities: Entities<R>,
pub(crate) space: Space<R>,
pub(crate) rounds: Rounds<R>,
pub(crate) phase: BattlePhase,
}
impl<R: BattleRules> BattleState<R> {
pub fn entities(&self) -> &Entities<R> {
&self.entities
}
pub fn space(&self) -> &Space<R> {
&self.space
}
pub fn rounds(&self) -> &Rounds<R> {
&self.rounds
}
pub fn phase(&self) -> BattlePhase {
self.phase
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum BattlePhase {
Started,
Ended,
}
pub trait BattleRules: Sized + Send {
type TR: TeamRules<Self>;
type CR: CharacterRules<Self>;
type AR: ActorRules<Self>;
type FR: FightRules<Self>;
type UR: UserRules<Self>;
type SR: SpaceRules<Self>;
type RR: RoundsRules<Self>;
type ER: EntropyRules;
#[cfg(not(feature = "serialization"))]
type Version: PartialEq + Debug + Clone + Send;
#[cfg(feature = "serialization")]
type Version: PartialEq + Debug + Clone + Send + Serialize + for<'a> Deserialize<'a>;
fn team_rules(&self) -> &Self::TR;
fn character_rules(&self) -> &Self::CR;
fn actor_rules(&self) -> &Self::AR;
fn fight_rules(&self) -> &Self::FR;
fn user_rules(&self) -> &Self::UR;
fn space_rules(&mut self) -> Self::SR;
fn rounds_rules(&mut self) -> Self::RR;
fn entropy_rules(&mut self) -> Self::ER;
fn version(&self) -> &Self::Version;
}
pub type Version<R> = <R as BattleRules>::Version;
pub trait BattleController<R: BattleRules> {
fn battle(&self) -> &Battle<R>;
fn event_callback(&self) -> &Option<EventCallback<R>>;
fn set_event_callback(&mut self, callback: Option<EventCallback<R>>);
}
pub struct BattleBuilder<R: BattleRules> {
rules: R,
event_callback: Option<EventCallback<R>>,
}
impl<R: BattleRules> BattleBuilder<R> {
pub fn event_callback(mut self, event_callback: EventCallback<R>) -> Self {
self.event_callback = Some(event_callback);
self
}
pub fn build(mut self) -> Battle<R> {
Battle {
state: BattleState {
entities: Entities::new(),
space: Space::new(None, self.rules.space_rules()),
rounds: Rounds::new(None, self.rules.rounds_rules()),
phase: BattlePhase::Started,
},
entropy: Entropy::new(None, self.rules.entropy_rules()),
history: History::new(),
rules: self.rules,
event_callback: self.event_callback,
metrics: Metrics::new(),
rights: Rights::new(),
}
}
}
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub struct EndBattle<R> {
#[cfg_attr(feature = "serialization", serde(skip))]
_phantom: PhantomData<R>,
}
impl<R: BattleRules> EndBattle<R> {
pub fn trigger<P: EventProcessor<R>>(processor: &mut P) -> EndBattleTrigger<R, P> {
EndBattleTrigger {
processor,
_phantom: PhantomData,
}
}
}
impl<R> std::fmt::Debug for EndBattle<R> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "EndBattle {{ }}")
}
}
impl<R> Clone for EndBattle<R> {
fn clone(&self) -> Self {
Self {
_phantom: PhantomData,
}
}
}
impl<R: BattleRules + 'static> Event<R> for EndBattle<R> {
fn verify(&self, _battle: &Battle<R>) -> WeaselResult<(), R> {
Ok(())
}
fn apply(&self, battle: &mut Battle<R>, _: &mut Option<EventQueue<R>>) {
battle.end();
}
fn kind(&self) -> EventKind {
EventKind::EndBattle
}
fn box_clone(&self) -> Box<dyn Event<R> + Send> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub struct EndBattleTrigger<'a, R, P>
where
R: BattleRules,
P: EventProcessor<R>,
{
processor: &'a mut P,
_phantom: PhantomData<R>,
}
impl<'a, R, P> EventTrigger<'a, R, P> for EndBattleTrigger<'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(EndBattle {
_phantom: self._phantom,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::event::EventKind;
use crate::server::Server;
use crate::team::CreateTeam;
use crate::util::tests::{dummy, team};
use crate::{battle_rules, rules::empty::*};
battle_rules! {}
fn cb(
event: &EventWrapper<CustomRules>,
_: &BattleState<CustomRules>,
event_queue: &mut Option<EventQueue<CustomRules>>,
) {
if let EventKind::CreateTeam = event.kind() {
let create_team: &CreateTeam<CustomRules> =
match event.as_any().downcast_ref::<CreateTeam<CustomRules>>() {
Some(b) => b,
None => panic!("incorrect cast!"),
};
assert_eq!(*create_team.id(), 1);
dummy(event_queue);
}
}
#[test]
fn event_callback() {
let battle = Battle::builder(CustomRules::new())
.event_callback(Box::new(cb))
.build();
let mut server = Server::builder(battle).build();
team(&mut server, 1);
assert_eq!(
server.battle().history().events()[1].kind(),
EventKind::DummyEvent
);
}
}