Board layers
This commit is contained in:
+216
-111
@@ -1,11 +1,11 @@
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use crate::archetype::Archetype;
|
||||
use crate::archetype::Archetype::Empty;
|
||||
use crate::glyph::Glyph;
|
||||
use crate::layer::Layer;
|
||||
use crate::log::LogLine;
|
||||
use crate::object_def::ObjectDef;
|
||||
use crate::utils::Direction;
|
||||
use crate::utils::{ObjectId, Player, PortalDef, RegistryValue, Solid};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
|
||||
/// The complete state of one game board (a single room or screen).
|
||||
///
|
||||
@@ -31,19 +31,12 @@ pub struct Board {
|
||||
pub width: usize,
|
||||
/// Height of the board in cells.
|
||||
pub height: usize,
|
||||
/// Row-major grid of `(Glyph, Archetype)` pairs. Use [`Board::get`] to
|
||||
/// access by `(x, y)` coordinates.
|
||||
pub(crate) cells: Vec<(Glyph, Archetype)>,
|
||||
/// Row-major cache of the visual floor layer: the glyph drawn for a cell when
|
||||
/// it would otherwise render as [`Archetype::Empty`]. Computed once at load
|
||||
/// from [`floor_spec`](Board::floor_spec) (see [`crate::floor::build_floor`]).
|
||||
/// When a map declares no `[floor]`, every entry is black-on-black space (the
|
||||
/// historical look of an empty cell). Read it via [`Board::glyph_at`].
|
||||
pub(crate) floor: Vec<Glyph>,
|
||||
/// The raw `[floor]` declaration, kept so [`crate::map_file::save`] can
|
||||
/// round-trip it. `None` when the map declared no floor. (Generators
|
||||
/// re-randomize on reload; literal glyph grids are preserved exactly.)
|
||||
pub(crate) floor_spec: Option<crate::floor::FloorSpec>,
|
||||
/// Ordered draw stack of [`Layer`]s, bottom (index 0) to top. Each layer holds
|
||||
/// a row-major grid of `(Glyph, Archetype)` cells; a transparent cell lets the
|
||||
/// layer beneath show through. Drawing ([`Board::glyph_at`]) walks the stack
|
||||
/// top-down; solidity ([`Board::solid_at`]) scans every layer. Access a single
|
||||
/// cell with [`Board::get`]/[`Board::get_mut`] by `(z, x, y)`.
|
||||
pub(crate) layers: Vec<Layer>,
|
||||
/// Current player position. See [`Player`] for caveats about its future.
|
||||
pub player: Player,
|
||||
/// Scripted objects on this board, keyed by stable [`ObjectId`]. A `BTreeMap`
|
||||
@@ -75,61 +68,85 @@ pub struct Board {
|
||||
}
|
||||
|
||||
impl Board {
|
||||
/// Returns a reference to the cell at `(x, y)`.
|
||||
/// Number of draw layers on this board (≥ 1 for a loaded board).
|
||||
pub fn layer_count(&self) -> usize {
|
||||
self.layers.len()
|
||||
}
|
||||
|
||||
/// Returns a reference to the cell at `(x, y)` on layer `z`.
|
||||
///
|
||||
/// The cell is a `(Glyph, Archetype)` tuple. Panics if `x` or `y` are
|
||||
/// The cell is a `(Glyph, Archetype)` tuple. Panics if `z`, `x`, or `y` are
|
||||
/// out of bounds.
|
||||
pub fn get(&self, x: usize, y: usize) -> &(Glyph, Archetype) {
|
||||
&self.cells[y * self.width + x]
|
||||
pub fn get(&self, z: usize, x: usize, y: usize) -> &(Glyph, Archetype) {
|
||||
&self.layers[z].cells[y * self.width + x]
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the cell at `(x, y)`.
|
||||
/// Returns a mutable reference to the cell at `(x, y)` on layer `z`.
|
||||
///
|
||||
/// Panics if `x` or `y` are out of bounds.
|
||||
pub fn get_mut(&mut self, x: usize, y: usize) -> &mut (Glyph, Archetype) {
|
||||
&mut self.cells[y * self.width + x]
|
||||
/// Panics if `z`, `x`, or `y` are out of bounds.
|
||||
pub fn get_mut(&mut self, z: usize, x: usize, y: usize) -> &mut (Glyph, Archetype) {
|
||||
let w = self.width;
|
||||
&mut self.layers[z].cells[y * w + x]
|
||||
}
|
||||
|
||||
/// Returns the glyph we should display for a given coordinate:
|
||||
/// Returns the glyph to display at `(x, y)`, honoring layer draw order.
|
||||
///
|
||||
/// - If the coord contains a [`Solid`], use the appropriate glyph for that
|
||||
/// - If it contains at least one non-solid object with a glyph other than 0, use one (arbitrarily)
|
||||
/// - If it contains any non-Empty archetype, use that glyph
|
||||
/// - Fall back to the [`floor`](Board::floor) glyph.
|
||||
/// The player is always drawn on top (it is not part of the layer stack yet).
|
||||
/// Otherwise the layers are walked **top-down**; the first thing that draws on
|
||||
/// a layer wins:
|
||||
///
|
||||
/// An `Empty` cell's *own* glyph is intentionally ignored — the floor layer
|
||||
/// supersedes it (so a cell vacated by a pushed crate reveals the floor, not
|
||||
/// black). Panics if out of bounds.
|
||||
/// 1. an object on that layer (a solid object always; otherwise a non-solid
|
||||
/// object whose glyph is not transparent, i.e. `tile != 0`),
|
||||
/// 2. a portal on that layer,
|
||||
/// 3. the layer's terrain cell — a solid always draws, and a non-solid draws
|
||||
/// only when not transparent (`tile != 0`).
|
||||
///
|
||||
/// If no layer contributes anything, the canonical black `Empty` glyph is used.
|
||||
/// Panics if out of bounds.
|
||||
pub fn glyph_at(&self, x: usize, y: usize) -> Glyph {
|
||||
if let Some(solid) = self.solid_at(x, y) {
|
||||
// Use the solid
|
||||
return match solid {
|
||||
Solid::Player => Glyph::player(),
|
||||
Solid::Cell(_) => self.get(x, y).0,
|
||||
Solid::Object(id) => self.objects[&id].glyph
|
||||
// The player is rendered above the whole stack (see the `Player` notes).
|
||||
if self.player.x == x as i32 && self.player.y == y as i32 {
|
||||
return Glyph::player();
|
||||
}
|
||||
|
||||
for z in (0..self.layers.len()).rev() {
|
||||
// Objects on this layer: a solid object always draws; otherwise the
|
||||
// first non-transparent non-solid object (lets invisible objects exist).
|
||||
let mut nonsolid: Option<Glyph> = None;
|
||||
for o in self
|
||||
.objects
|
||||
.values()
|
||||
.filter(|o| o.x == x && o.y == y && o.z == z)
|
||||
{
|
||||
if o.solid {
|
||||
return o.glyph;
|
||||
}
|
||||
if nonsolid.is_none() && o.glyph.tile != 0 {
|
||||
nonsolid = Some(o.glyph);
|
||||
}
|
||||
}
|
||||
if let Some(g) = nonsolid {
|
||||
return g;
|
||||
}
|
||||
|
||||
// A portal on this layer draws above its (transparent) terrain cell.
|
||||
if self
|
||||
.portals
|
||||
.iter()
|
||||
.any(|p| p.x == x && p.y == y && p.z == z)
|
||||
{
|
||||
return PortalDef::default_glyph();
|
||||
}
|
||||
|
||||
// The terrain cell: a solid always draws; a non-solid only if visible.
|
||||
let (glyph, arch) = self.get(z, x, y);
|
||||
if arch.behavior().solid || glyph.tile != 0 {
|
||||
return *glyph;
|
||||
}
|
||||
}
|
||||
|
||||
let (glyph, arch) = *self.get(x, y);
|
||||
let ids = self.object_ids_at(x, y); // IDs of non-solid (ethereal?) objects
|
||||
|
||||
// Objects with nonzero (nonblank) glyphs take precedence. This allows us to use invisible
|
||||
// nonsolid objects on the board.
|
||||
if let Some(glyph) = ids.iter().find(|id| self.objects[id].glyph.tile != 0 ).map(|id| self.objects[id].glyph) {
|
||||
return glyph
|
||||
}
|
||||
|
||||
// Is there a normal archetype?
|
||||
if arch != Empty {
|
||||
return glyph
|
||||
}
|
||||
|
||||
// Portal: passable and non-opaque, but visible above the floor.
|
||||
if self.portals.iter().any(|p| p.x == x && p.y == y) {
|
||||
return PortalDef::default_glyph();
|
||||
}
|
||||
|
||||
self.floor[y * self.width + x]
|
||||
// Nothing on any layer: the canonical black empty cell.
|
||||
Archetype::Empty.default_glyph()
|
||||
}
|
||||
|
||||
/// Returns `true` if `(x, y)` is a valid cell coordinate on this board.
|
||||
@@ -153,7 +170,7 @@ impl Board {
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.load_errors.is_empty()
|
||||
}
|
||||
|
||||
|
||||
/// Returns the single solid entity occupying `(x, y)`, if any.
|
||||
///
|
||||
/// Checks player first, then objects, then the grid archetype. Because at most one solid
|
||||
@@ -165,19 +182,38 @@ impl Board {
|
||||
if self.player.x == x as i32 && self.player.y == y as i32 {
|
||||
return Some(Solid::Player);
|
||||
}
|
||||
// A solid object shadows the grid cell it sits on.
|
||||
if let Some(id) = self.solid_object_id_at(x, y)
|
||||
{
|
||||
// A solid object shadows the cell it sits on.
|
||||
if let Some(id) = self.solid_object_id_at(x, y) {
|
||||
return Some(Solid::Object(id));
|
||||
}
|
||||
// Otherwise the grid archetype itself may be solid (e.g. a wall).
|
||||
let arch = self.get(x, y).1;
|
||||
if arch.behavior().solid {
|
||||
return Some(Solid::Cell(arch));
|
||||
// Otherwise some layer's terrain archetype may be solid (e.g. a wall).
|
||||
if let Some(z) = self.solid_cell_layer(x, y) {
|
||||
return Some(Solid::Cell(self.get(z, x, y).1));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns the index of the layer whose terrain cell at `(x, y)` is solid, if
|
||||
/// any. By the one-solid-per-cell invariant there is at most one such layer.
|
||||
fn solid_cell_layer(&self, x: usize, y: usize) -> Option<usize> {
|
||||
(0..self.layers.len()).find(|&z| self.get(z, x, y).1.behavior().solid)
|
||||
}
|
||||
|
||||
/// Returns the coordinates of every [`Archetype::Pusher`] terrain cell on the
|
||||
/// board (scanning all layers). Used by the pusher heartbeat in
|
||||
/// [`GameState::tick`](crate::game::GameState::tick).
|
||||
pub fn pusher_cells(&self) -> Vec<(usize, usize)> {
|
||||
let mut cells = Vec::new();
|
||||
for y in 0..self.height {
|
||||
for x in 0..self.width {
|
||||
if matches!(self.solid_at(x, y), Some(Solid::Cell(Archetype::Pusher(_)))) {
|
||||
cells.push((x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
cells
|
||||
}
|
||||
|
||||
/// Returns `true` if a mover can enter `(x, y)` — i.e. no solid occupies it.
|
||||
///
|
||||
/// Convenience inverse of [`solid_at`](Board::solid_at).
|
||||
@@ -261,7 +297,12 @@ impl Board {
|
||||
/// - If the target has a pushable chain: pushes it first, then slides in.
|
||||
/// - If blocked or out of bounds: no-op, returns `false`.
|
||||
pub(crate) fn advance_pusher(&mut self, x: usize, y: usize) -> bool {
|
||||
let Archetype::Pusher(dir) = self.get(x, y).1 else { return false; };
|
||||
let Some(z) = self.solid_cell_layer(x, y) else {
|
||||
return false;
|
||||
};
|
||||
let Archetype::Pusher(dir) = self.get(z, x, y).1 else {
|
||||
return false;
|
||||
};
|
||||
let (dx, dy): (i32, i32) = dir.into();
|
||||
let tx = x as i32 + dx;
|
||||
let ty = y as i32 + dy;
|
||||
@@ -283,24 +324,24 @@ impl Board {
|
||||
|
||||
/// Moves the single solid occupant of `(x, y)` one step by `(dx, dy)`.
|
||||
///
|
||||
/// A solid object is relocated; otherwise the grid archetype (a crate) is
|
||||
/// moved, leaving `Empty` floor behind (the grid has no separate floor layer).
|
||||
/// A solid object is relocated (keeping its layer); otherwise the solid
|
||||
/// terrain archetype (a crate) is moved within its own layer, leaving a
|
||||
/// transparent cell behind so the layer beneath (e.g. floor) shows through.
|
||||
/// The caller guarantees the destination is already clear.
|
||||
fn shift_solid(&mut self, x: usize, y: usize, dx: i32, dy: i32) {
|
||||
let (tx, ty) = ((x as i32 + dx) as usize, (y as i32 + dy) as usize);
|
||||
// The player owns its cell, so move it before considering objects/grid.
|
||||
// The player owns its cell, so move it before considering objects/terrain.
|
||||
if self.player.x == x as i32 && self.player.y == y as i32 {
|
||||
self.player.x = tx as i32;
|
||||
self.player.y = ty as i32;
|
||||
} else if let Some(id) = self.solid_object_id_at(x, y)
|
||||
{
|
||||
} else if let Some(id) = self.solid_object_id_at(x, y) {
|
||||
let obj = self.objects.get_mut(&id).expect("id from object_id_at");
|
||||
obj.x = tx;
|
||||
obj.y = ty;
|
||||
} else {
|
||||
let moved = *self.get(x, y);
|
||||
*self.get_mut(tx, ty) = moved;
|
||||
*self.get_mut(x, y) = (Archetype::Empty.default_glyph(), Archetype::Empty);
|
||||
} else if let Some(z) = self.solid_cell_layer(x, y) {
|
||||
let moved = *self.get(z, x, y);
|
||||
*self.get_mut(z, tx, ty) = moved;
|
||||
*self.get_mut(z, x, y) = (Glyph::transparent(), Archetype::Empty);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,20 +350,19 @@ impl Board {
|
||||
self.objects
|
||||
.iter()
|
||||
.filter(|(_, o)| o.x == x && o.y == y)
|
||||
.map(|(&id, _)| id).collect()
|
||||
.map(|(&id, _)| id)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Returns a borrow of the actual object at `(x, y)` if any
|
||||
pub fn solid_object_id_at(&self, x: usize, y: usize) -> Option<ObjectId> {
|
||||
self.objects
|
||||
.iter()
|
||||
.find_map(|(&id, o)| {
|
||||
if o.x == x && o.y == y && o.solid {
|
||||
Some(id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
self.objects.iter().find_map(|(&id, o)| {
|
||||
if o.x == x && o.y == y && o.solid {
|
||||
Some(id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Inserts `object`, assigning it the next free [`ObjectId`], and returns that id.
|
||||
@@ -340,17 +380,22 @@ impl Board {
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use color::Rgba8;
|
||||
use super::Board;
|
||||
use crate::archetype::Archetype;
|
||||
use crate::glyph::Glyph;
|
||||
use crate::layer::Layer;
|
||||
use crate::object_def::ObjectDef;
|
||||
use crate::utils::Direction;
|
||||
use crate::utils::{ObjectId, Player, Solid};
|
||||
use super::Board;
|
||||
use color::Rgba8;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
|
||||
/// Builds an all-empty `w×h` board with the given player position and objects.
|
||||
/// Assigns sequential ids (1..=n) to objects.
|
||||
/// Builds an all-empty `w×h` single-layer board with the given player position
|
||||
/// and objects (all on layer 0). Assigns sequential ids (1..=n) to objects.
|
||||
///
|
||||
/// The single layer is fully transparent, so terrain stamped via [`crate_at`]
|
||||
/// etc. always lands on the board's top layer. Use [`add_floor`] to slip a
|
||||
/// visible floor layer underneath.
|
||||
pub(crate) fn open_board(
|
||||
w: usize,
|
||||
h: usize,
|
||||
@@ -368,10 +413,13 @@ pub(crate) mod tests {
|
||||
name: "test".into(),
|
||||
width: w,
|
||||
height: h,
|
||||
cells: vec![(Archetype::Empty.default_glyph(), Archetype::Empty); w * h],
|
||||
floor: vec![Archetype::Empty.default_glyph(); w * h],
|
||||
floor_spec: None,
|
||||
player: Player { x: player.0, y: player.1 },
|
||||
layers: vec![Layer {
|
||||
cells: vec![(Glyph::transparent(), Archetype::Empty); w * h],
|
||||
}],
|
||||
player: Player {
|
||||
x: player.0,
|
||||
y: player.1,
|
||||
},
|
||||
objects: object_map,
|
||||
next_object_id,
|
||||
portals: Vec::new(),
|
||||
@@ -381,25 +429,48 @@ pub(crate) mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// Stamps a crate cell onto a board.
|
||||
/// Inserts a visible floor layer (filled with `glyph`) below everything,
|
||||
/// bumping existing terrain and objects up one layer.
|
||||
pub(crate) fn add_floor(board: &mut Board, glyph: Glyph) {
|
||||
let count = board.width * board.height;
|
||||
board.layers.insert(
|
||||
0,
|
||||
Layer {
|
||||
cells: vec![(glyph, Archetype::Empty); count],
|
||||
},
|
||||
);
|
||||
for o in board.objects.values_mut() {
|
||||
o.z += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// The index of the board's top (terrain) layer, where stamps are written.
|
||||
fn top(board: &Board) -> usize {
|
||||
board.layers.len() - 1
|
||||
}
|
||||
|
||||
/// Stamps a crate cell onto the board's top layer.
|
||||
pub(crate) fn crate_at(board: &mut Board, x: usize, y: usize) {
|
||||
*board.get_mut(x, y) = (Archetype::Crate.default_glyph(), Archetype::Crate);
|
||||
let z = top(board);
|
||||
*board.get_mut(z, x, y) = (Archetype::Crate.default_glyph(), Archetype::Crate);
|
||||
}
|
||||
|
||||
/// Stamps a wall cell onto a board.
|
||||
/// Stamps a wall cell onto the board's top layer.
|
||||
pub(crate) fn wall_at(board: &mut Board, x: usize, y: usize) {
|
||||
*board.get_mut(x, y) = (Archetype::Wall.default_glyph(), Archetype::Wall);
|
||||
let z = top(board);
|
||||
*board.get_mut(z, x, y) = (Archetype::Wall.default_glyph(), Archetype::Wall);
|
||||
}
|
||||
|
||||
/// Stamps an arbitrary archetype cell onto a board.
|
||||
/// Stamps an arbitrary archetype cell onto the board's top layer.
|
||||
pub(crate) fn stamp(board: &mut Board, x: usize, y: usize, arch: Archetype) {
|
||||
*board.get_mut(x, y) = (arch.default_glyph(), arch);
|
||||
let z = top(board);
|
||||
*board.get_mut(z, x, y) = (arch.default_glyph(), arch);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn solid_at_reports_wall_object_and_empty() {
|
||||
let mut board = open_board(4, 1, (3, 0), vec![]);
|
||||
board.cells[1] = (Archetype::Wall.default_glyph(), Archetype::Wall);
|
||||
wall_at(&mut board, 1, 0);
|
||||
board.add_object(ObjectDef::new(2, 0));
|
||||
|
||||
assert!(board.solid_at(0, 0).is_none());
|
||||
@@ -412,7 +483,9 @@ pub(crate) mod tests {
|
||||
assert!(!board.is_passable(1, 0));
|
||||
|
||||
match board.solid_at(2, 0) {
|
||||
Some(Solid::Object(id)) => assert_eq!((board.objects[&id].x, board.objects[&id].y), (2, 0)),
|
||||
Some(Solid::Object(id)) => {
|
||||
assert_eq!((board.objects[&id].x, board.objects[&id].y), (2, 0))
|
||||
}
|
||||
_ => panic!("expected Solid::Object"),
|
||||
}
|
||||
assert!(!board.is_passable(2, 0));
|
||||
@@ -453,8 +526,8 @@ pub(crate) mod tests {
|
||||
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);
|
||||
assert_eq!(board.get(0, 1, 0).1, Archetype::Crate); // no mutation
|
||||
assert_eq!(board.get(0, 2, 0).1, Archetype::Empty);
|
||||
|
||||
let mut board = open_board(3, 1, (0, 0), vec![]);
|
||||
crate_at(&mut board, 1, 0);
|
||||
@@ -469,8 +542,8 @@ pub(crate) mod tests {
|
||||
crate_at(&mut board, 1, 0);
|
||||
assert!(board.can_push(1, 0, Direction::East));
|
||||
board.push(1, 0, Direction::East);
|
||||
assert_eq!(board.get(1, 0).1, Archetype::Empty);
|
||||
assert_eq!(board.get(2, 0).1, Archetype::Crate);
|
||||
assert_eq!(board.get(0, 1, 0).1, Archetype::Empty);
|
||||
assert_eq!(board.get(0, 2, 0).1, Archetype::Crate);
|
||||
assert_eq!((board.player.x, board.player.y), (3, 0));
|
||||
}
|
||||
|
||||
@@ -482,7 +555,7 @@ pub(crate) mod tests {
|
||||
wall_at(&mut board, 3, 0);
|
||||
assert!(!board.can_push(1, 0, Direction::East));
|
||||
board.push(1, 0, Direction::East); // no-op
|
||||
assert_eq!(board.get(1, 0).1, Archetype::Crate);
|
||||
assert_eq!(board.get(0, 1, 0).1, Archetype::Crate);
|
||||
assert_eq!((board.player.x, board.player.y), (2, 0));
|
||||
}
|
||||
|
||||
@@ -492,15 +565,47 @@ pub(crate) mod tests {
|
||||
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 },
|
||||
bg: Rgba8 { r: 1, g: 2, b: 3, a: 255 },
|
||||
fg: Rgba8 {
|
||||
r: 10,
|
||||
g: 20,
|
||||
b: 30,
|
||||
a: 255,
|
||||
},
|
||||
bg: Rgba8 {
|
||||
r: 1,
|
||||
g: 2,
|
||||
b: 3,
|
||||
a: 255,
|
||||
},
|
||||
};
|
||||
board.floor = vec![floor_glyph; 3];
|
||||
// Floor on a lower layer, a wall on the top (terrain) layer at (0,0).
|
||||
add_floor(&mut board, floor_glyph);
|
||||
wall_at(&mut board, 0, 0);
|
||||
// The wall (solid) draws over the floor; the empty cell reveals the floor.
|
||||
assert_eq!(board.glyph_at(0, 0), Archetype::Wall.default_glyph());
|
||||
assert_eq!(board.glyph_at(1, 0), floor_glyph);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn glyph_at_draws_higher_layer_over_lower() {
|
||||
// A non-solid object on an upper layer renders above a wall on a lower one.
|
||||
let mut board = open_board(2, 1, (1, 0), vec![]);
|
||||
wall_at(&mut board, 0, 0); // wall on layer 0
|
||||
// Add an upper layer holding a visible, non-solid object at (0,0).
|
||||
board.layers.push(Layer {
|
||||
cells: vec![(Glyph::transparent(), Archetype::Empty); 2],
|
||||
});
|
||||
let mut obj = ObjectDef::new(0, 0);
|
||||
obj.z = 1;
|
||||
obj.solid = false;
|
||||
obj.glyph = Glyph {
|
||||
tile: '*' as u32,
|
||||
..Glyph::transparent()
|
||||
};
|
||||
board.add_object(obj);
|
||||
assert_eq!(board.glyph_at(0, 0).tile, '*' as u32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fresh_board_is_valid_and_reports_errors() {
|
||||
let mut board = open_board(1, 1, (0, 0), vec![]);
|
||||
@@ -509,4 +614,4 @@ pub(crate) mod tests {
|
||||
assert!(!board.is_valid());
|
||||
assert_eq!(board.load_errors.len(), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user