2026-06-13 16:24:29 -05:00
|
|
|
|
use crate::archetype::Archetype;
|
|
|
|
|
|
use crate::board::Board;
|
2026-07-10 23:16:28 -05:00
|
|
|
|
use crate::floor::Floor;
|
2026-06-13 16:24:29 -05:00
|
|
|
|
use crate::game::GameState;
|
2026-06-15 23:35:18 -05:00
|
|
|
|
use crate::glyph::Glyph;
|
2026-06-25 00:04:42 -05:00
|
|
|
|
use crate::utils::{Direction, PlayerPos, PortalDef};
|
2026-06-13 16:24:29 -05:00
|
|
|
|
use crate::world::World;
|
2026-06-15 23:35:18 -05:00
|
|
|
|
use std::cell::RefCell;
|
|
|
|
|
|
use std::collections::{BTreeMap, HashMap};
|
|
|
|
|
|
use std::rc::Rc;
|
2026-06-13 16:24:29 -05:00
|
|
|
|
|
|
|
|
|
|
/// Builds a 3×3 board with the player at `(px, py)` and the given portals.
|
2026-06-28 00:12:52 -05:00
|
|
|
|
fn make_board(px: i64, py: i64, portals: Vec<PortalDef>) -> Board {
|
2026-06-13 16:24:29 -05:00
|
|
|
|
Board {
|
|
|
|
|
|
name: "test".into(),
|
|
|
|
|
|
width: 3,
|
|
|
|
|
|
height: 3,
|
2026-07-10 23:16:28 -05:00
|
|
|
|
grid: vec![(Glyph::transparent(), Archetype::Empty); 9],
|
|
|
|
|
|
floor: Floor::Blank,
|
|
|
|
|
|
decorations: Vec::new(),
|
2026-06-25 00:04:42 -05:00
|
|
|
|
player: PlayerPos { x: px, y: py },
|
2026-06-13 16:24:29 -05:00
|
|
|
|
objects: BTreeMap::new(),
|
|
|
|
|
|
next_object_id: 1,
|
|
|
|
|
|
portals,
|
|
|
|
|
|
board_script_name: None,
|
|
|
|
|
|
load_errors: Vec::new(),
|
2026-06-13 17:58:04 -05:00
|
|
|
|
registry: HashMap::new(),
|
2026-06-13 16:24:29 -05:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Two-board world used by all tests in this module.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// - `"b1"`: player at `(0, 0)`, portal `"to_b2"` at `(2, 0)` → `"b2"` / `"from_b1"`
|
|
|
|
|
|
/// - `"b2"`: player at `(0, 0)`, portal `"from_b1"` at `(1, 1)` → `"b1"` / `"to_b2"`
|
|
|
|
|
|
fn two_board_world() -> World {
|
2026-06-15 23:35:18 -05:00
|
|
|
|
let b1 = make_board(
|
|
|
|
|
|
0,
|
|
|
|
|
|
0,
|
|
|
|
|
|
vec![PortalDef {
|
|
|
|
|
|
name: "to_b2".into(),
|
|
|
|
|
|
x: 2,
|
|
|
|
|
|
y: 0,
|
|
|
|
|
|
target_map: "b2".into(),
|
|
|
|
|
|
target_entry: "from_b1".into(),
|
|
|
|
|
|
}],
|
|
|
|
|
|
);
|
|
|
|
|
|
let b2 = make_board(
|
|
|
|
|
|
0,
|
|
|
|
|
|
0,
|
|
|
|
|
|
vec![PortalDef {
|
|
|
|
|
|
name: "from_b1".into(),
|
|
|
|
|
|
x: 1,
|
|
|
|
|
|
y: 1,
|
|
|
|
|
|
target_map: "b1".into(),
|
|
|
|
|
|
target_entry: "to_b2".into(),
|
|
|
|
|
|
}],
|
|
|
|
|
|
);
|
2026-06-13 16:24:29 -05:00
|
|
|
|
World {
|
|
|
|
|
|
name: "test".into(),
|
|
|
|
|
|
start: "b1".into(),
|
|
|
|
|
|
scripts: HashMap::new(),
|
|
|
|
|
|
boards: HashMap::from([
|
|
|
|
|
|
("b1".to_string(), Rc::new(RefCell::new(b1))),
|
|
|
|
|
|
("b2".to_string(), Rc::new(RefCell::new(b2))),
|
|
|
|
|
|
]),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn enter_board_switches_board() {
|
|
|
|
|
|
let mut game = GameState::from_world(two_board_world());
|
|
|
|
|
|
game.enter_board("b2", "from_b1");
|
|
|
|
|
|
assert_eq!(game.current_board_name(), "b2");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn enter_board_places_player_at_arrival_portal() {
|
|
|
|
|
|
let mut game = GameState::from_world(two_board_world());
|
|
|
|
|
|
game.enter_board("b2", "from_b1");
|
|
|
|
|
|
let board = game.board();
|
|
|
|
|
|
// Arrival portal "from_b1" is at (1, 1) on b2.
|
|
|
|
|
|
assert_eq!(board.player.x, 1);
|
|
|
|
|
|
assert_eq!(board.player.y, 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn enter_board_sets_board_transition() {
|
|
|
|
|
|
let mut game = GameState::from_world(two_board_world());
|
|
|
|
|
|
game.enter_board("b2", "from_b1");
|
|
|
|
|
|
assert_eq!(game.board_transition, Some(1.0));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn enter_board_unknown_board_logs_error() {
|
|
|
|
|
|
let mut game = GameState::from_world(two_board_world());
|
|
|
|
|
|
game.enter_board("nope", "entry");
|
|
|
|
|
|
assert_eq!(game.current_board_name(), "b1", "board should not change");
|
|
|
|
|
|
assert!(!game.log.is_empty(), "an error should be logged");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn enter_board_unknown_entry_logs_error() {
|
|
|
|
|
|
let mut game = GameState::from_world(two_board_world());
|
|
|
|
|
|
game.enter_board("b2", "nope");
|
|
|
|
|
|
assert_eq!(game.current_board_name(), "b1", "board should not change");
|
|
|
|
|
|
assert!(!game.log.is_empty(), "an error should be logged");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn try_move_onto_portal_switches_board() {
|
|
|
|
|
|
let world = two_board_world();
|
|
|
|
|
|
// Start the player one cell west of the portal at (2, 0).
|
2026-06-25 00:04:42 -05:00
|
|
|
|
world.boards["b1"].borrow_mut().player = PlayerPos { x: 1, y: 0 };
|
2026-06-13 16:24:29 -05:00
|
|
|
|
let mut game = GameState::from_world(world);
|
|
|
|
|
|
game.try_move(Direction::East);
|
|
|
|
|
|
assert_eq!(game.current_board_name(), "b2");
|
|
|
|
|
|
}
|