This commit is contained in:
2026-07-14 23:48:50 -05:00
parent 03185c9c68
commit 5146cc9bcc
25 changed files with 380 additions and 272 deletions
+20 -20
View File
@@ -313,7 +313,7 @@ impl GameState {
}
Action::SetLight(radius) => {
if let Some(obj) = board.objects.get_mut(&ba.source) {
obj.light = radius;
obj.behavior.glow = radius;
}
}
Action::SetTag {
@@ -405,7 +405,7 @@ impl GameState {
"teleport({target},{x},{y}): no such object"
)),
Some(obj) => {
let source_solid = obj.solid;
let source_solid = obj.behavior.solid;
let (old_x, old_y) = (obj.x as i64, obj.y as i64);
let blocked = source_solid
&& matches!(
@@ -742,7 +742,7 @@ fn step_object(board: &mut Board, id: ObjectId, dir: Direction) -> StepOutcome {
entered: Vec::new(),
};
let (dx, dy): (i64, i64) = dir.into();
let Some((ox, oy, solid)) = board.objects.get(&id).map(|o| (o.x, o.y, o.solid)) else {
let Some((ox, oy, solid)) = board.objects.get(&id).map(|o| (o.x, o.y, o.behavior.solid)) else {
return out;
};
let target = (ox as i64 + dx, oy as i64 + dy);
@@ -805,7 +805,7 @@ mod tests {
// ordinary solid here: the chain can't move (the player is backed against
// the board edge), so nothing happens and the gem is not collected.
let mut sobj = ObjectDef::new(0, 0);
sobj.solid = false;
sobj.behavior.solid = false;
sobj.script_name = Some("s".to_string());
let mut board = open_board(3, 1, (2, 0), vec![sobj]);
stamp(&mut board, 1, 0, Archetype::Builtin(Builtin::Gem, "gem"));
@@ -822,14 +822,14 @@ mod tests {
// No grab: the gem is untouched and the player never moved.
assert_eq!(game.player.borrow().gems, 0);
assert!(game.board().objects.values().any(|o| o.grab));
assert!(game.board().objects.values().any(|o| o.behavior.grab));
assert_eq!((game.board().player.x, game.board().player.y), (2, 0));
}
/// Builds a `GameState` with one non-solid object running `src` as its script.
fn game_with_object_script(board_w: usize, src: &str) -> GameState {
let mut obj = ObjectDef::new(0, 0);
obj.solid = false;
obj.behavior.solid = false;
obj.script_name = Some("s".to_string());
let mut board = open_board(board_w, 1, (board_w as i64 - 1, 0), vec![obj]);
crate_at(&mut board, 2, 0);
@@ -850,7 +850,7 @@ mod tests {
game.tick(Duration::from_millis(16));
let b = game.board();
assert_eq!(b.get(2, 0).1, Archetype::Empty);
assert_eq!(b.get(3, 0).1, Archetype::Crate);
assert_eq!(b.get(3, 0).1, Archetype::Builtin(Builtin::Crate, "crate"));
}
#[test]
@@ -859,7 +859,7 @@ mod tests {
// object sits at (0,0); the player at (3,0). passable should report only the
// genuinely empty in-bounds cell.
let mut obj = ObjectDef::new(0, 0);
obj.solid = false;
obj.behavior.solid = false;
obj.script_name = Some("s".to_string());
let mut board = open_board(4, 1, (3, 0), vec![obj]);
crate_at(&mut board, 2, 0);
@@ -886,7 +886,7 @@ mod tests {
// right after run_init (dt = 0) — under the old queued-Action behavior it
// would have been stuck behind the delay and absent here.
let mut obj = ObjectDef::new(0, 0);
obj.solid = false;
obj.behavior.solid = false;
obj.script_name = Some("s".to_string());
let board = open_board(4, 1, (3, 0), vec![obj]);
let src = "fn init(m) { delay(5.0); log(\"immediate\"); }";
@@ -916,7 +916,7 @@ mod tests {
ccw: bool,
) -> GameState {
let mut obj = ObjectDef::new(1, 1);
obj.solid = false;
obj.behavior.solid = false;
obj.script_name = Some("spinner".to_string());
if ccw {
obj.tags.insert("BUILTIN_spinner_ccw".to_string());
@@ -945,9 +945,9 @@ mod tests {
let mut game = spinner_board(&[(1, 0), (2, 0)], &[(2, 1)]);
game.tick(Duration::from_millis(16));
let b = game.board();
assert_eq!(b.get(1, 0).1, Archetype::Crate); // N kept
assert_eq!(b.get(2, 0).1, Archetype::Crate); // NE kept (not destroyed)
assert_eq!(b.get(2, 1).1, Archetype::Wall); // wall kept
assert_eq!(b.get(1, 0).1, Archetype::Builtin(Builtin::Crate, "crate")); // N kept
assert_eq!(b.get(2, 0).1, Archetype::Builtin(Builtin::Crate, "crate")); // NE kept (not destroyed)
assert_eq!(b.get(2, 1).1, Archetype::Builtin(Builtin::Wall, "wall")); // wall kept
}
#[test]
@@ -957,9 +957,9 @@ mod tests {
let mut game = spinner_board(&[(0, 1), (0, 0), (1, 0)], &[]);
game.tick(Duration::from_millis(16));
let b = game.board();
assert_eq!(b.get(2, 0).1, Archetype::Crate); // NE: filled by N
assert_eq!(b.get(1, 0).1, Archetype::Crate); // N: filled by NW
assert_eq!(b.get(0, 0).1, Archetype::Crate); // NW: filled by W
assert_eq!(b.get(2, 0).1, Archetype::Builtin(Builtin::Crate, "crate")); // NE: filled by N
assert_eq!(b.get(1, 0).1, Archetype::Builtin(Builtin::Crate, "crate")); // N: filled by NW
assert_eq!(b.get(0, 0).1, Archetype::Builtin(Builtin::Crate, "crate")); // NW: filled by W
assert_eq!(b.get(0, 1).1, Archetype::Empty); // W: vacated (hole moved here)
}
@@ -970,7 +970,7 @@ mod tests {
let mut game = spinner_board_dir(&[(1, 0)], &[], true);
game.tick(Duration::from_millis(16));
let b = game.board();
assert_eq!(b.get(0, 0).1, Archetype::Crate); // NW: crate rotated counter-clockwise
assert_eq!(b.get(0, 0).1, Archetype::Builtin(Builtin::Crate, "crate")); // NW: crate rotated counter-clockwise
assert_eq!(b.get(2, 0).1, Archetype::Empty); // NE: untouched
assert_eq!(b.get(1, 0).1, Archetype::Empty); // N: vacated
}
@@ -999,7 +999,7 @@ mod tests {
#[test]
fn set_key_gives_and_takes_keys() {
let mut sobj = ObjectDef::new(0, 0);
sobj.solid = false;
sobj.behavior.solid = false;
sobj.script_name = Some("s".to_string());
let board = open_board(2, 1, (1, 0), vec![sobj]);
let scripts = HashMap::from([(
@@ -1016,7 +1016,7 @@ mod tests {
// A second script can take a key.
let mut sobj2 = ObjectDef::new(0, 0);
sobj2.solid = false;
sobj2.behavior.solid = false;
sobj2.script_name = Some("t".to_string());
let board2 = open_board(2, 1, (1, 0), vec![sobj2]);
let scripts2 = HashMap::from([(
@@ -1032,7 +1032,7 @@ mod tests {
#[test]
fn set_key_unknown_color_logs_error() {
let mut sobj = ObjectDef::new(0, 0);
sobj.solid = false;
sobj.behavior.solid = false;
sobj.script_name = Some("s".to_string());
let board = open_board(2, 1, (1, 0), vec![sobj]);
let scripts = HashMap::from([(