use crate::context::{ResourceContext, ServiceContext};
use crate::event::EventBus;
use crate::plugin::time::{AdvanceTimeRequested, DayChanged};
use crate::system::System;
use async_trait::async_trait;
use std::any::Any;
use std::sync::Arc;
use super::events::{ActionConsumedEvent, ActionsResetEvent};
use super::hook::ActionHook;
use super::resources::{ActionConsumed, ActionPoints};
pub struct ActionSystem {
hook: Arc<dyn ActionHook>,
}
impl ActionSystem {
pub fn new(hook: Arc<dyn ActionHook>) -> Self {
Self { hook }
}
pub async fn process_events(
&mut self,
_services: &ServiceContext,
resources: &mut ResourceContext,
) {
let events = {
if let Some(mut bus) = resources.get_mut::<EventBus>().await {
let reader = bus.reader::<ActionConsumedEvent>();
reader.iter().cloned().collect::<Vec<_>>()
} else {
Vec::new()
}
};
for event in events {
let consumed = ActionConsumed {
context: event.context,
remaining: event.remaining,
depleted: event.depleted,
};
self.hook.on_action_consumed(&consumed, resources).await;
if consumed.depleted {
let should_advance = self.hook.on_actions_depleted(resources).await;
if should_advance {
if let Some(mut bus) = resources.get_mut::<EventBus>().await {
bus.publish(AdvanceTimeRequested);
}
}
}
}
}
}
#[async_trait]
impl System for ActionSystem {
fn name(&self) -> &'static str {
"action_system"
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
pub struct ActionResetSystem {
hook: Arc<dyn ActionHook>,
}
impl ActionResetSystem {
pub fn new(hook: Arc<dyn ActionHook>) -> Self {
Self { hook }
}
pub async fn update(&mut self, _services: &ServiceContext, resources: &mut ResourceContext) {
let day_changed = {
if let Some(mut bus) = resources.get_mut::<EventBus>().await {
let reader = bus.reader::<DayChanged>();
!reader.iter().collect::<Vec<_>>().is_empty()
} else {
false
}
};
if !day_changed {
return;
}
let new_count = {
if let Some(mut points) = resources.get_mut::<ActionPoints>().await {
points.reset();
points.available
} else {
return;
}
};
self.hook.on_actions_reset(new_count, resources).await;
if let Some(mut bus) = resources.get_mut::<EventBus>().await {
bus.publish(ActionsResetEvent { new_count });
}
}
}
#[async_trait]
impl System for ActionResetSystem {
fn name(&self) -> &'static str {
"action_reset"
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::event::EventBus;
use crate::plugin::action::DefaultActionHook;
#[tokio::test]
async fn test_action_reset_system() {
let mut resources = ResourceContext::new();
resources.insert(ActionPoints::new(3));
resources.insert(EventBus::new());
let services = ServiceContext::new();
let hook = Arc::new(DefaultActionHook);
let mut system = ActionResetSystem::new(hook);
{
let mut points = resources.get_mut::<ActionPoints>().await.unwrap();
points.consume();
points.consume();
assert_eq!(points.available, 1);
}
{
let mut bus = resources.get_mut::<EventBus>().await.unwrap();
bus.publish(DayChanged { day: 2 });
bus.dispatch(); }
system.update(&services, &mut resources).await;
let points = resources.get::<ActionPoints>().await.unwrap();
assert_eq!(points.available, 3);
{
let mut bus = resources.get_mut::<EventBus>().await.unwrap();
bus.dispatch();
}
let mut bus = resources.get_mut::<EventBus>().await.unwrap();
let reader = bus.reader::<ActionsResetEvent>();
let events: Vec<_> = reader.iter().collect();
assert_eq!(events.len(), 1);
assert_eq!(events[0].new_count, 3);
}
#[tokio::test]
async fn test_action_reset_system_no_event() {
let mut resources = ResourceContext::new();
resources.insert(ActionPoints::new(3));
resources.insert(EventBus::new());
let services = ServiceContext::new();
let hook = Arc::new(DefaultActionHook);
let mut system = ActionResetSystem::new(hook);
{
let mut points = resources.get_mut::<ActionPoints>().await.unwrap();
points.consume();
assert_eq!(points.available, 2);
}
system.update(&services, &mut resources).await;
let points = resources.get::<ActionPoints>().await.unwrap();
assert_eq!(points.available, 2);
}
#[tokio::test]
async fn test_action_system_with_hook() {
let mut resources = ResourceContext::new();
resources.insert(EventBus::new());
let services = ServiceContext::new();
let hook = Arc::new(DefaultActionHook);
let mut system = ActionSystem::new(hook);
{
let mut bus = resources.get_mut::<EventBus>().await.unwrap();
bus.publish(ActionConsumedEvent {
context: "test action".to_string(),
remaining: 0,
depleted: true,
});
bus.dispatch();
}
system.process_events(&services, &mut resources).await;
{
let mut bus = resources.get_mut::<EventBus>().await.unwrap();
bus.dispatch();
}
let mut bus = resources.get_mut::<EventBus>().await.unwrap();
let reader = bus.reader::<AdvanceTimeRequested>();
let events: Vec<_> = reader.iter().collect();
assert_eq!(events.len(), 1);
}
}