use bevy::prelude::*;
use super::components::AnimationLock;
use super::events::{AdvanceTimeRequested, DayChanged, TickAdvanced};
use super::resources::{GameDate, NextTurnPhase};
use super::states::TurnPhase;
pub fn handle_advance_time(
mut commands: Commands,
mut messages: MessageReader<AdvanceTimeRequested>,
mut date: ResMut<GameDate>,
) {
if messages.read().next().is_some() {
messages.read().for_each(drop);
let new_day = date.increment_day();
commands.write_message(DayChanged { day: new_day });
}
}
pub fn tick_system(mut commands: Commands, mut date: ResMut<GameDate>) {
date.tick();
commands.write_message(TickAdvanced { tick: date.tick });
}
pub fn update_animation_locks(
mut commands: Commands,
time: Res<Time>,
mut locks: Query<(Entity, &mut AnimationLock)>,
) {
for (entity, mut lock) in locks.iter_mut() {
lock.timer.tick(time.delta());
if lock.timer.is_finished() {
commands.entity(entity).despawn();
}
}
}
pub fn check_animation_locks(
locks: Query<&AnimationLock>,
current_state: Res<State<TurnPhase>>,
mut next_state: ResMut<NextState<TurnPhase>>,
mut next_phase: ResMut<NextTurnPhase>,
) {
if *current_state.get() != TurnPhase::Visuals {
return;
}
if !locks.is_empty() {
return; }
if let Some(phase) = next_phase.get() {
next_state.set(phase);
next_phase.clear(); } else {
next_state.set(TurnPhase::PlayerInput);
}
}
#[cfg(test)]
mod tests {
use super::*;
fn setup_test_app() -> App {
let mut app = App::new();
app.add_plugins(MinimalPlugins);
app.add_plugins(bevy::state::app::StatesPlugin); app.init_state::<TurnPhase>();
app.insert_resource(GameDate::new());
app.insert_resource(NextTurnPhase::default());
app.add_message::<AdvanceTimeRequested>();
app.add_message::<DayChanged>();
app.add_message::<TickAdvanced>();
app.add_systems(
Update,
(
handle_advance_time,
tick_system,
update_animation_locks,
check_animation_locks,
),
);
app
}
#[test]
fn test_handle_advance_time() {
let mut app = setup_test_app();
app.world_mut().write_message(AdvanceTimeRequested);
app.update();
let date = app.world().resource::<GameDate>();
assert_eq!(date.day, 2);
let mut messages = app.world_mut().resource_mut::<Messages<DayChanged>>();
let events: Vec<_> = messages.drain().collect();
assert_eq!(events.len(), 1);
assert_eq!(events[0].day, 2);
}
#[test]
fn test_tick_system() {
let mut app = setup_test_app();
let initial_tick = app.world().resource::<GameDate>().tick;
app.update();
let date = app.world().resource::<GameDate>();
assert_eq!(date.tick, initial_tick + 1);
let mut messages = app.world_mut().resource_mut::<Messages<TickAdvanced>>();
let events: Vec<_> = messages.drain().collect();
assert_eq!(events.len(), 1);
assert_eq!(events[0].tick, initial_tick + 1);
}
#[test]
fn test_animation_lock_auto_despawn() {
let mut app = setup_test_app();
app.world_mut().spawn(AnimationLock::new(0.01, "test"));
let lock_count = app
.world_mut()
.query::<&AnimationLock>()
.iter(app.world())
.count();
assert_eq!(lock_count, 1);
for _ in 0..10 {
app.update();
}
}
#[test]
fn test_check_animation_locks_blocks_transition() {
let mut app = setup_test_app();
app.world_mut().spawn(AnimationLock::new(1.0, "test"));
app.world_mut()
.resource_mut::<NextState<TurnPhase>>()
.set(TurnPhase::Visuals);
app.update();
let state = app.world().resource::<State<TurnPhase>>();
assert_eq!(*state.get(), TurnPhase::Visuals);
app.update();
let state = app.world().resource::<State<TurnPhase>>();
assert_eq!(*state.get(), TurnPhase::Visuals);
}
#[test]
fn test_check_animation_locks_allows_transition_when_empty() {
let mut app = setup_test_app();
app.world_mut()
.resource_mut::<NextState<TurnPhase>>()
.set(TurnPhase::Visuals);
app.update();
app.update();
let state = app.world().resource::<State<TurnPhase>>();
assert_eq!(*state.get(), TurnPhase::PlayerInput);
}
#[test]
fn test_next_turn_phase_reservation() {
let mut app = setup_test_app();
app.world_mut()
.resource_mut::<NextTurnPhase>()
.reserve(TurnPhase::EnemyTurn);
app.world_mut()
.resource_mut::<NextState<TurnPhase>>()
.set(TurnPhase::Visuals);
app.update();
app.update();
let state = app.world().resource::<State<TurnPhase>>();
assert_eq!(*state.get(), TurnPhase::EnemyTurn);
let next_phase = app.world().resource::<NextTurnPhase>();
assert!(!next_phase.is_reserved());
}
}