only tick nonwaiting objects
This commit is contained in:
@@ -383,6 +383,37 @@ fn a_later_object_sees_an_earlier_objects_move_this_tick() {
|
||||
assert_eq!(log_texts(&game), vec!["blocked"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tick_is_gated_on_an_empty_queue() {
|
||||
// `tick` fires only when the object's output queue is empty — so an *unguarded*
|
||||
// tick that just `move`s east paces itself one step per drained move instead of
|
||||
// piling up. `move` enqueues a Move plus a 0.25s Delay; while that Delay is still
|
||||
// draining the engine skips re-running `tick`. With 100 ms frames the object steps
|
||||
// once (x=1) then waits ~0.25s (the pending Delay) before the next call fires.
|
||||
// Under the old every-frame model an unguarded tick would enqueue a fresh move each
|
||||
// frame, racing the object east far faster.
|
||||
let obj = scripted_object(0, 0, "m");
|
||||
let board = open_board(6, 1, (5, 0), vec![obj]);
|
||||
let mut game = GameState::with_scripts(
|
||||
board,
|
||||
// Note: no `if m.queue.length == 0` guard — the engine provides it.
|
||||
scripts_from(&[("m", "fn tick(m, dt) { move(East); }")]),
|
||||
);
|
||||
game.run_init();
|
||||
|
||||
// Over 0.3s only the first move has resolved; the pending Delay suppresses the
|
||||
// re-tick, so the object is still at x=1 rather than having stacked more moves.
|
||||
for _ in 0..3 {
|
||||
game.tick(Duration::from_millis(100));
|
||||
}
|
||||
assert_eq!(game.board().objects[&1].x, 1);
|
||||
|
||||
// Once the Delay fully drains the queue empties, so the next frame calls `tick`
|
||||
// again and the object takes its second step.
|
||||
game.tick(Duration::from_millis(100));
|
||||
assert_eq!(game.board().objects[&1].x, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_send_cycle_terminates_via_the_called_guard() {
|
||||
// Two objects send "go" to each other in a cycle. Without the per-invocation
|
||||
|
||||
Reference in New Issue
Block a user