feat(simulation): add tick rate scaling system
Replace binary paused flag with TickRate enum (Full/Half/Paused) per D-052. Full rate advances every frame, Half every 2 frames via fractional accumulation, Paused blocks all advances. Add SetTickRate player action for client-driven rate changes. Implements ticket #406. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
use crate::bridge::types::{PlayerAction, PlayerInput};
|
||||
use crate::perception::vision_cone::{facing_from_delta, Facing};
|
||||
use crate::simulation::movement::{MoveIntent, PlayerCharacter, TilePosition};
|
||||
use crate::simulation::time::SimulationTime;
|
||||
use crate::simulation::time::{SimulationTime, TickRate};
|
||||
use bevy_ecs::prelude::*;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
@@ -75,13 +75,17 @@ pub fn process_player_input(
|
||||
PlayerAction::MoveSoutheast => apply_move(&player_query, &mut commands, 1, 1),
|
||||
PlayerAction::MoveSouthwest => apply_move(&player_query, &mut commands, -1, 1),
|
||||
PlayerAction::Pause => {
|
||||
time.paused = true;
|
||||
time.tick_rate = TickRate::Paused;
|
||||
tracing::debug!("Simulation paused by player input");
|
||||
}
|
||||
PlayerAction::Unpause => {
|
||||
time.paused = false;
|
||||
time.tick_rate = TickRate::Full;
|
||||
tracing::debug!("Simulation unpaused by player input");
|
||||
}
|
||||
PlayerAction::SetTickRate(rate) => {
|
||||
time.tick_rate = rate;
|
||||
tracing::debug!("Tick rate set to {:?} by player input", rate);
|
||||
}
|
||||
PlayerAction::Interact => {
|
||||
tracing::trace!("Interact action — no-op for Sprint 1");
|
||||
}
|
||||
@@ -160,10 +164,7 @@ mod tests {
|
||||
fn process_input_move_creates_intent() {
|
||||
let mut world = bevy_ecs::world::World::new();
|
||||
world.insert_resource(InputQueue::default());
|
||||
world.insert_resource(SimulationTime {
|
||||
tick: 0,
|
||||
paused: false,
|
||||
});
|
||||
world.insert_resource(SimulationTime::default());
|
||||
|
||||
let player = world
|
||||
.spawn((PlayerCharacter, TilePosition::new(5, 5, 0)))
|
||||
@@ -183,13 +184,10 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn process_input_pause_toggles() {
|
||||
fn process_input_pause_sets_paused() {
|
||||
let mut world = bevy_ecs::world::World::new();
|
||||
world.insert_resource(InputQueue::default());
|
||||
world.insert_resource(SimulationTime {
|
||||
tick: 0,
|
||||
paused: false,
|
||||
});
|
||||
world.insert_resource(SimulationTime::default());
|
||||
|
||||
world.resource_mut::<InputQueue>().push(PlayerInput {
|
||||
tick: 0,
|
||||
@@ -200,17 +198,32 @@ mod tests {
|
||||
schedule.add_systems(process_player_input);
|
||||
schedule.run(&mut world);
|
||||
|
||||
assert!(world.resource::<SimulationTime>().paused);
|
||||
assert_eq!(world.resource::<SimulationTime>().tick_rate, TickRate::Paused);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn process_input_set_tick_rate() {
|
||||
let mut world = bevy_ecs::world::World::new();
|
||||
world.insert_resource(InputQueue::default());
|
||||
world.insert_resource(SimulationTime::default());
|
||||
|
||||
world.resource_mut::<InputQueue>().push(PlayerInput {
|
||||
tick: 0,
|
||||
action: PlayerAction::SetTickRate(TickRate::Half),
|
||||
});
|
||||
|
||||
let mut schedule = bevy_ecs::schedule::Schedule::default();
|
||||
schedule.add_systems(process_player_input);
|
||||
schedule.run(&mut world);
|
||||
|
||||
assert_eq!(world.resource::<SimulationTime>().tick_rate, TickRate::Half);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn process_input_no_player_no_panic() {
|
||||
let mut world = bevy_ecs::world::World::new();
|
||||
world.insert_resource(InputQueue::default());
|
||||
world.insert_resource(SimulationTime {
|
||||
tick: 0,
|
||||
paused: false,
|
||||
});
|
||||
world.insert_resource(SimulationTime::default());
|
||||
|
||||
world.resource_mut::<InputQueue>().push(PlayerInput {
|
||||
tick: 0,
|
||||
@@ -227,10 +240,7 @@ mod tests {
|
||||
fn process_input_future_tick_ignored() {
|
||||
let mut world = bevy_ecs::world::World::new();
|
||||
world.insert_resource(InputQueue::default());
|
||||
world.insert_resource(SimulationTime {
|
||||
tick: 0,
|
||||
paused: false,
|
||||
});
|
||||
world.insert_resource(SimulationTime::default());
|
||||
|
||||
let player = world
|
||||
.spawn((PlayerCharacter, TilePosition::new(5, 5, 0)))
|
||||
|
||||
Reference in New Issue
Block a user