use bevy::prelude::*;
use super::components::{ActionError, ActionPoints};
use super::events::{
ActionConsumedHook, ActionConsumedMessage, ActionsDepletedHook, ActionsResetHook,
ActionsResetMessage, CheckTurnEndMessage, ConsumeActionMessage,
};
use crate::plugins::time::{AdvanceTimeRequested, DayChanged};
pub fn handle_action_consume(
mut commands: Commands,
mut messages: MessageReader<ConsumeActionMessage>,
mut action_query: Query<&mut ActionPoints>,
mut consumed_messages: MessageWriter<ActionConsumedMessage>,
) {
for message in messages.read() {
if let Ok(mut action_points) = action_query.get_mut(message.entity) {
match action_points.consume_with(&message.context) {
Ok(consumed) => {
consumed_messages.write(ActionConsumedMessage {
entity: message.entity,
context: consumed.context.clone(),
remaining: consumed.remaining,
depleted: consumed.depleted,
});
commands.trigger(ActionConsumedHook {
entity: message.entity,
context: consumed.context,
remaining: consumed.remaining,
depleted: consumed.depleted,
});
if consumed.depleted {
commands.trigger(ActionsDepletedHook {
entity: message.entity,
});
}
}
Err(ActionError::Depleted) => {
warn!(
"Entity {:?} attempted to consume action but depleted: {}",
message.entity, message.context
);
}
}
} else {
warn!(
"Entity {:?} does not exist or has no ActionPoints component",
message.entity
);
}
}
}
pub fn handle_action_reset(
mut commands: Commands,
mut messages: MessageReader<DayChanged>,
mut action_query: Query<(Entity, &mut ActionPoints)>,
mut reset_messages: MessageWriter<ActionsResetMessage>,
) {
let day_changed = messages.read().next().is_some();
if day_changed {
for (entity, mut action_points) in action_query.iter_mut() {
action_points.reset();
let new_count = action_points.available;
reset_messages.write(ActionsResetMessage { entity, new_count });
commands.trigger(ActionsResetHook { entity, new_count });
}
}
}
pub fn on_actions_depleted_check_turn_end(
_trigger: On<ActionsDepletedHook>,
mut commands: Commands,
) {
commands.write_message(CheckTurnEndMessage);
}
pub fn check_turn_end_all_players(
mut messages: MessageReader<CheckTurnEndMessage>,
mut commands: Commands,
action_query: Query<&ActionPoints>,
) {
if messages.read().next().is_none() {
return;
}
let all_depleted = action_query.iter().all(|points| points.is_depleted());
if all_depleted {
info!("All entities depleted action points, advancing turn");
commands.write_message(AdvanceTimeRequested);
} else {
debug!("Some entities still have action points, not advancing turn");
}
}
#[cfg(test)]
mod tests {
use super::*;
use bevy::app::App;
#[test]
fn test_handle_action_consume_success() {
let mut app = App::new();
app.add_plugins(bevy::MinimalPlugins)
.add_message::<ConsumeActionMessage>()
.add_message::<ActionConsumedMessage>()
.add_systems(Update, handle_action_consume);
let entity = app.world_mut().spawn(ActionPoints::new(3)).id();
app.world_mut().write_message(ConsumeActionMessage {
entity,
context: "Test action".to_string(),
});
app.update();
let mut consumed_msgs = app
.world_mut()
.resource_mut::<Messages<ActionConsumedMessage>>();
let msgs: Vec<_> = consumed_msgs.drain().collect();
assert_eq!(msgs.len(), 1);
assert_eq!(msgs[0].entity, entity);
assert_eq!(msgs[0].context, "Test action");
assert_eq!(msgs[0].remaining, 2);
assert!(!msgs[0].depleted);
let points = app.world().get::<ActionPoints>(entity).unwrap();
assert_eq!(points.available, 2);
}
#[test]
fn test_handle_action_consume_depleted() {
let mut app = App::new();
app.add_plugins(bevy::MinimalPlugins)
.add_message::<ConsumeActionMessage>()
.add_message::<ActionConsumedMessage>()
.add_systems(Update, handle_action_consume);
let entity = app.world_mut().spawn(ActionPoints::new(1)).id();
app.world_mut().write_message(ConsumeActionMessage {
entity,
context: "Final action".to_string(),
});
app.update();
let mut consumed_msgs = app
.world_mut()
.resource_mut::<Messages<ActionConsumedMessage>>();
let msgs: Vec<_> = consumed_msgs.drain().collect();
assert_eq!(msgs.len(), 1);
assert_eq!(msgs[0].remaining, 0);
assert!(msgs[0].depleted);
let points = app.world().get::<ActionPoints>(entity).unwrap();
assert!(points.is_depleted());
}
#[test]
fn test_handle_action_reset() {
let mut app = App::new();
app.add_plugins(bevy::MinimalPlugins)
.add_message::<DayChanged>()
.add_message::<ActionsResetMessage>()
.add_systems(Update, handle_action_reset);
let entity1 = app.world_mut().spawn(ActionPoints::new(3)).id();
let entity2 = app.world_mut().spawn(ActionPoints::new(5)).id();
{
let mut points1 = app.world_mut().get_mut::<ActionPoints>(entity1).unwrap();
points1.consume();
points1.consume();
}
{
let mut points2 = app.world_mut().get_mut::<ActionPoints>(entity2).unwrap();
points2.consume();
}
app.world_mut().write_message(DayChanged { day: 2 });
app.update();
let mut reset_msgs = app
.world_mut()
.resource_mut::<Messages<ActionsResetMessage>>();
let msgs: Vec<_> = reset_msgs.drain().collect();
assert_eq!(msgs.len(), 2);
let points1 = app.world().get::<ActionPoints>(entity1).unwrap();
assert_eq!(points1.available, 3);
let points2 = app.world().get::<ActionPoints>(entity2).unwrap();
assert_eq!(points2.available, 5);
}
#[test]
fn test_check_turn_end_all_depleted() {
let mut app = App::new();
app.add_plugins(bevy::MinimalPlugins)
.add_message::<CheckTurnEndMessage>()
.add_message::<AdvanceTimeRequested>()
.add_systems(Update, check_turn_end_all_players);
app.world_mut().spawn(ActionPoints::new(0));
app.world_mut().spawn(ActionPoints::new(0));
app.world_mut().write_message(CheckTurnEndMessage);
app.update();
let mut advance_msgs = app
.world_mut()
.resource_mut::<Messages<AdvanceTimeRequested>>();
let msgs: Vec<_> = advance_msgs.drain().collect();
assert_eq!(msgs.len(), 1);
}
#[test]
fn test_check_turn_end_not_all_depleted() {
let mut app = App::new();
app.add_plugins(bevy::MinimalPlugins)
.add_message::<CheckTurnEndMessage>()
.add_message::<AdvanceTimeRequested>()
.add_systems(Update, check_turn_end_all_players);
app.world_mut().spawn(ActionPoints::new(0)); app.world_mut().spawn(ActionPoints::new(2));
app.world_mut().write_message(CheckTurnEndMessage);
app.update();
let mut advance_msgs = app
.world_mut()
.resource_mut::<Messages<AdvanceTimeRequested>>();
let msgs: Vec<_> = advance_msgs.drain().collect();
assert_eq!(msgs.len(), 0);
}
}