enter hook
This commit is contained in:
@@ -59,6 +59,14 @@ fn scripted_object(x: usize, y: usize, script: &str) -> ObjectDef {
|
||||
o
|
||||
}
|
||||
|
||||
/// Returns a **non-solid** `ObjectDef` at `(x, y)` bound to the named script — the
|
||||
/// kind that receives an `enter` hook when a solid relocates onto its cell.
|
||||
fn nonsolid_object(x: usize, y: usize, script: &str) -> ObjectDef {
|
||||
let mut o = scripted_object(x, y, script);
|
||||
o.solid = false;
|
||||
o
|
||||
}
|
||||
|
||||
/// Flattens each log line into a single string for easy assertions.
|
||||
fn log_texts(game: &GameState) -> Vec<String> {
|
||||
game.log
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::{board_with_object, log_texts, scripted_object, scripts_from};
|
||||
use crate::board::tests::open_board;
|
||||
use super::{board_with_object, log_texts, nonsolid_object, scripted_object, scripts_from};
|
||||
use crate::board::tests::{crate_at, open_board};
|
||||
use crate::game::{GameState, ScrollLine};
|
||||
use crate::utils::Direction;
|
||||
use std::time::Duration;
|
||||
@@ -406,3 +406,112 @@ fn a_send_cycle_terminates_via_the_called_guard() {
|
||||
// A.poke's re-send to B.poke is a repeat key and is skipped, so the cascade stops.
|
||||
assert_eq!(log_texts(&game), vec!["B", "A"]);
|
||||
}
|
||||
|
||||
// ── enter hook ──────────────────────────────────────────────────────────────
|
||||
// `enter(me, dir)` fires on a non-solid object when a solid relocates onto its
|
||||
// cell. `dir` is the side the entrant came from (opposite the travel direction
|
||||
// for a cardinal move/push; best-effort for teleport/shift).
|
||||
|
||||
#[test]
|
||||
fn player_walking_onto_a_nonsolid_fires_enter_from_the_travel_side() {
|
||||
// The player walks East onto a non-solid trigger; it entered from the West.
|
||||
let board = open_board(3, 1, (0, 0), vec![nonsolid_object(1, 0, "e")]);
|
||||
let mut game = GameState::with_scripts(
|
||||
board,
|
||||
scripts_from(&[("e", "fn enter(m, dir) { log(`entered from ${dir}`); }")]),
|
||||
);
|
||||
game.run_init();
|
||||
game.try_move(Direction::East);
|
||||
// The player is not blocked by a non-solid — it moves onto the cell.
|
||||
assert_eq!((game.board().player.x, game.board().player.y), (1, 0));
|
||||
assert!(log_texts(&game).iter().any(|t| t == "entered from West"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn object_moving_onto_a_nonsolid_fires_enter() {
|
||||
// A solid mover ticks East onto a non-solid trigger sharing the destination.
|
||||
let mover = scripted_object(0, 0, "m");
|
||||
let trigger = nonsolid_object(1, 0, "e");
|
||||
let board = open_board(3, 1, (2, 0), vec![mover, trigger]);
|
||||
let mut game = GameState::with_scripts(
|
||||
board,
|
||||
scripts_from(&[
|
||||
("m", "fn tick(m, dt) { if m.queue.length == 0 { move(East); } }"),
|
||||
("e", "fn enter(m, dir) { log(`entered from ${dir}`); }"),
|
||||
]),
|
||||
);
|
||||
game.run_init();
|
||||
game.tick(Duration::from_millis(16));
|
||||
assert!(log_texts(&game).iter().any(|t| t == "entered from West"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pushing_a_crate_onto_a_nonsolid_fires_enter() {
|
||||
// Player pushes a crate East onto a non-solid trigger's cell; the crate is the
|
||||
// solid entrant, so `enter` fires (from West).
|
||||
let mut board = open_board(4, 1, (0, 0), vec![nonsolid_object(2, 0, "e")]);
|
||||
crate_at(&mut board, 1, 0);
|
||||
let mut game = GameState::with_scripts(
|
||||
board,
|
||||
scripts_from(&[("e", "fn enter(m, dir) { log(`entered from ${dir}`); }")]),
|
||||
);
|
||||
game.run_init();
|
||||
game.try_move(Direction::East);
|
||||
assert!(log_texts(&game).iter().any(|t| t == "entered from West"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleporting_onto_a_nonsolid_fires_enter() {
|
||||
// A solid object teleports itself from (0,0) onto the non-solid trigger at (1,0).
|
||||
let mover = scripted_object(0, 0, "t");
|
||||
let trigger = nonsolid_object(1, 0, "e");
|
||||
let board = open_board(3, 1, (2, 0), vec![mover, trigger]);
|
||||
let mut game = GameState::with_scripts(
|
||||
board,
|
||||
scripts_from(&[
|
||||
("t", "fn init(m) { teleport(m.id, 1, 0); }"),
|
||||
("e", "fn enter(m, dir) { log(`entered from ${dir}`); }"),
|
||||
]),
|
||||
);
|
||||
game.run_init();
|
||||
// Best-effort direction: the jump was one cell east, so it entered from West.
|
||||
assert!(log_texts(&game).iter().any(|t| t == "entered from West"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shifting_a_crate_onto_a_nonsolid_fires_enter() {
|
||||
// An object shifts the ring [(1,0),(2,0)]: the crate at (1,0) rotates onto the
|
||||
// non-solid trigger at (2,0), firing `enter` (best-effort West).
|
||||
let shifter = nonsolid_object(0, 0, "s");
|
||||
let trigger = nonsolid_object(2, 0, "e");
|
||||
let mut board = open_board(4, 1, (3, 0), vec![shifter, trigger]);
|
||||
crate_at(&mut board, 1, 0);
|
||||
let mut game = GameState::with_scripts(
|
||||
board,
|
||||
scripts_from(&[
|
||||
("s", "fn init(m) { shift([[1, 0], [2, 0]]); }"),
|
||||
("e", "fn enter(m, dir) { log(`entered from ${dir}`); }"),
|
||||
]),
|
||||
);
|
||||
game.run_init();
|
||||
assert!(log_texts(&game).iter().any(|t| t == "entered from West"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_nonsolid_mover_onto_a_nonsolid_does_not_fire_enter() {
|
||||
// Only *solid* entrants trigger enter: a non-solid mover walking onto another
|
||||
// non-solid must NOT fire it.
|
||||
let mover = nonsolid_object(0, 0, "m");
|
||||
let trigger = nonsolid_object(1, 0, "e");
|
||||
let board = open_board(3, 1, (2, 0), vec![mover, trigger]);
|
||||
let mut game = GameState::with_scripts(
|
||||
board,
|
||||
scripts_from(&[
|
||||
("m", "fn tick(m, dt) { if m.queue.length == 0 { move(East); } }"),
|
||||
("e", "fn enter(m, dir) { log(`entered from ${dir}`); }"),
|
||||
]),
|
||||
);
|
||||
game.run_init();
|
||||
game.tick(Duration::from_millis(16));
|
||||
assert!(!log_texts(&game).iter().any(|t| t.starts_with("entered")));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user