multi board

This commit is contained in:
2026-06-13 01:27:33 -05:00
parent e5c6affc41
commit 73c8a9bd3e
13 changed files with 456 additions and 445 deletions
+17 -24
View File
@@ -1,4 +1,4 @@
use std::collections::{BTreeMap, HashMap};
use std::collections::BTreeMap;
use crate::archetype::Archetype;
use crate::archetype::Archetype::Empty;
use crate::font::FontSpec;
@@ -61,16 +61,11 @@ pub struct Board {
pub font: Option<FontSpec>,
/// Integer scale factor applied to tiles when rendering this board. 1 = natural size.
pub zoom: u32,
/// Named Rhai scripts available on this board: script name → source text.
///
/// All script source lives here; [`ObjectDef`]s and [`Board::board_script_name`]
/// reference entries by name. This means multiple objects can share the same
/// source while each running instance gets its own Rhai scope at runtime.
pub scripts: HashMap<String, String>,
/// Name of the board-level script in [`Board::scripts`], if any.
/// Name of the board-level script in the world script pool, if any.
///
/// A board script runs on the board as a whole (e.g. `on_enter`, `on_tick`)
/// rather than being tied to a specific object cell.
/// rather than being tied to a specific object cell. Scripts live in
/// [`World::scripts`](crate::world::World) and are looked up by this name.
pub board_script_name: Option<String>,
/// Nonfatal problems collected while loading this map (e.g. unknown
/// archetypes, dropped objects, recovered placement chars), as red-on-black
@@ -313,7 +308,7 @@ impl Board {
#[cfg(test)]
pub(crate) mod tests {
use std::collections::{BTreeMap, HashMap};
use std::collections::BTreeMap;
use color::Rgba8;
use crate::archetype::Archetype;
use crate::glyph::Glyph;
@@ -322,14 +317,13 @@ pub(crate) mod tests {
use crate::utils::{ObjectId, Player, Solid};
use super::Board;
/// Builds an all-empty `w×h` board with the given player position, objects,
/// and `(name, source)` scripts. Assigns sequential ids (1..=n) to objects.
/// Builds an all-empty `w×h` board with the given player position and objects.
/// Assigns sequential ids (1..=n) to objects.
pub(crate) fn open_board(
w: usize,
h: usize,
player: (i32, i32),
objects: Vec<ObjectDef>,
scripts: &[(&str, &str)],
) -> Board {
let mut object_map: BTreeMap<ObjectId, ObjectDef> = BTreeMap::new();
let mut next_object_id: ObjectId = 1;
@@ -351,7 +345,6 @@ pub(crate) mod tests {
portals: Vec::new(),
font: None,
zoom: 1,
scripts: scripts.iter().map(|(k, v)| (k.to_string(), v.to_string())).collect::<HashMap<_,_>>(),
board_script_name: None,
load_errors: Vec::new(),
}
@@ -374,7 +367,7 @@ pub(crate) mod tests {
#[test]
fn solid_at_reports_wall_object_and_empty() {
let mut board = open_board(4, 1, (3, 0), vec![], &[]);
let mut board = open_board(4, 1, (3, 0), vec![]);
board.cells[1] = (Archetype::Wall.default_glyph(), Archetype::Wall);
board.add_object(ObjectDef::new(2, 0));
@@ -398,14 +391,14 @@ pub(crate) mod tests {
fn non_solid_object_does_not_block() {
let mut obj = ObjectDef::new(1, 0);
obj.solid = false;
let board = open_board(3, 1, (0, 0), vec![obj], &[]);
let board = open_board(3, 1, (0, 0), vec![obj]);
assert!(board.solid_at(1, 0).is_none());
assert!(board.is_passable(1, 0));
}
#[test]
fn solid_at_reports_player() {
let board = open_board(3, 1, (1, 0), vec![], &[]);
let board = open_board(3, 1, (1, 0), vec![]);
match board.solid_at(1, 0) {
Some(Solid::Player) => {}
_ => panic!("expected Solid::Player at the player's cell"),
@@ -415,7 +408,7 @@ pub(crate) mod tests {
#[test]
fn in_bounds_checks_grid_boundaries() {
let board = open_board(3, 2, (0, 0), vec![], &[]);
let board = open_board(3, 2, (0, 0), vec![]);
assert!(board.in_bounds((0, 0)));
assert!(board.in_bounds((2, 1)));
assert!(!board.in_bounds((-1, 0)));
@@ -426,13 +419,13 @@ pub(crate) mod tests {
#[test]
fn can_push_is_read_only_and_correct() {
let mut board = open_board(3, 1, (0, 0), vec![], &[]);
let mut board = open_board(3, 1, (0, 0), vec![]);
crate_at(&mut board, 1, 0);
assert!(board.can_push(1, 0, Direction::East));
assert_eq!(board.get(1, 0).1, Archetype::Crate); // no mutation
assert_eq!(board.get(2, 0).1, Archetype::Empty);
let mut board = open_board(3, 1, (0, 0), vec![], &[]);
let mut board = open_board(3, 1, (0, 0), vec![]);
crate_at(&mut board, 1, 0);
wall_at(&mut board, 2, 0);
assert!(!board.can_push(1, 0, Direction::East));
@@ -441,7 +434,7 @@ pub(crate) mod tests {
#[test]
fn push_into_player_pushes_player() {
// Crate shoved east into the player slides the player along into open space.
let mut board = open_board(4, 1, (2, 0), vec![], &[]);
let mut board = open_board(4, 1, (2, 0), vec![]);
crate_at(&mut board, 1, 0);
assert!(board.can_push(1, 0, Direction::East));
board.push(1, 0, Direction::East);
@@ -453,7 +446,7 @@ pub(crate) mod tests {
#[test]
fn push_into_player_blocked_by_wall() {
// Player backed against a wall: push has nowhere to go, nothing moves.
let mut board = open_board(4, 1, (2, 0), vec![], &[]);
let mut board = open_board(4, 1, (2, 0), vec![]);
crate_at(&mut board, 1, 0);
wall_at(&mut board, 3, 0);
assert!(!board.can_push(1, 0, Direction::East));
@@ -465,7 +458,7 @@ pub(crate) mod tests {
#[test]
fn glyph_at_uses_floor_for_empty_and_grid_for_solid() {
// Player parked at (2,0) so it doesn't overlap either asserted cell.
let mut board = open_board(3, 1, (2, 0), vec![], &[]);
let mut board = open_board(3, 1, (2, 0), vec![]);
let floor_glyph = Glyph {
tile: '.' as u32,
fg: Rgba8 { r: 10, g: 20, b: 30, a: 255 },
@@ -479,7 +472,7 @@ pub(crate) mod tests {
#[test]
fn fresh_board_is_valid_and_reports_errors() {
let mut board = open_board(1, 1, (0, 0), vec![], &[]);
let mut board = open_board(1, 1, (0, 0), vec![]);
assert!(board.is_valid());
board.report_error("something went wrong");
assert!(!board.is_valid());