builtin scripts

This commit is contained in:
2026-06-16 14:34:42 -05:00
parent 7608fc959f
commit 6464e47747
13 changed files with 317 additions and 119 deletions
+1
View File
@@ -3,6 +3,7 @@ mod grid_errors;
mod object_placement;
mod player_placement;
mod portal_placement;
mod pushers;
mod round_trip;
use crate::board::Board;
+132
View File
@@ -0,0 +1,132 @@
//! Pushers are now scripted objects (the `pusher_*` archetypes expand into objects
//! carrying the embedded `pusher.rhai` plus a `BUILTIN_pusher_<dir>` tag).
use super::{layer, load_board, map};
use crate::archetype::Archetype;
use crate::game::GameState;
use crate::map_file::MapFile;
use crate::object_def::ObjectDef;
use std::time::Duration;
/// Finds the pusher object on a board (by its built-in tag).
fn pusher<'a>(board: &'a crate::board::Board, id: &mut u32) -> &'a ObjectDef {
let (&oid, obj) = board
.objects
.iter()
.find(|(_, o)| o.tags.contains("BUILTIN_pusher_east"))
.expect("pusher object");
*id = oid;
obj
}
#[test]
fn pusher_loads_as_a_tagged_scripted_solid_object() {
let board = load_board(&map(
3,
1,
&[layer(
"P @",
&[("P", "kind = \"pusher_east\""), ("@", "kind = \"player\"")],
)],
));
let mut id = 0;
let p = pusher(&board, &mut id);
assert_eq!((p.x, p.y), (0, 0));
assert!(
p.builtin_script.is_some(),
"carries the embedded pusher script"
);
assert!(p.script_name.is_none());
assert!(!board.is_passable(0, 0), "pusher is solid");
}
#[test]
fn pusher_advances_and_shoves_a_crate() {
// Pusher at (0,0), crate at (1,0), the player parked at the east edge.
let board = load_board(&map(
5,
1,
&[layer(
"Po @",
&[
("P", "kind = \"pusher_east\""),
("o", "kind = \"crate\""),
("@", "kind = \"player\""),
],
)],
));
let mut pid = 0;
pusher(&board, &mut pid);
let mut game = GameState::new(board);
game.run_init();
for _ in 0..12 {
game.tick(Duration::from_secs_f64(0.3));
}
let b = game.board();
assert!(b.objects[&pid].x > 0, "pusher advanced east");
assert_eq!(
b.get(0, 1, 0).1,
Archetype::Empty,
"crate left its start cell"
);
assert!(
(2..b.width).any(|x| b.get(0, x, 0).1 == Archetype::Crate),
"crate was shoved east"
);
}
#[test]
fn pusher_blocked_by_wall_stays_put() {
let board = load_board(&map(
4,
1,
&[layer(
"P# @",
&[
("P", "kind = \"pusher_east\""),
("#", "kind = \"wall\""),
("@", "kind = \"player\""),
],
)],
));
let mut pid = 0;
pusher(&board, &mut pid);
let mut game = GameState::new(board);
game.run_init();
for _ in 0..12 {
game.tick(Duration::from_secs_f64(0.3));
}
assert_eq!(
game.board().objects[&pid].x,
0,
"a wall-blocked pusher does not move"
);
}
#[test]
fn pusher_round_trips_to_its_keyword() {
let board = load_board(&map(
3,
1,
&[layer(
"P @",
&[("P", "kind = \"pusher_east\""), ("@", "kind = \"player\"")],
)],
));
// Save collapses the expanded object back into the `pusher_east` keyword.
let toml_out = toml::to_string_pretty(&MapFile::from(&board)).unwrap();
assert!(
toml_out.contains("pusher_east"),
"pusher should save as its archetype keyword, got:\n{toml_out}"
);
// Reload: it is a working pusher object again.
let board2 = load_board(&toml_out);
let mut id = 0;
let p = pusher(&board2, &mut id);
assert_eq!((p.x, p.y), (0, 0));
assert!(p.builtin_script.is_some());
}