wip 2 pointifying
This commit is contained in:
+30
-142
@@ -129,7 +129,7 @@ impl Board {
|
||||
let p = p.into();
|
||||
let grid_glyph = self.get(p).as_ref().map(Tile::glyph);
|
||||
|
||||
let sensors = self.sensors.iter().filter(|&s| s.x == p.ux() && s.y == p.uy());
|
||||
let sensors = self.sensors.iter().filter(|&s| s.location == p);
|
||||
|
||||
// Is there a sensor above the grid?
|
||||
if let Some(above) = sensors.clone().find(|s| s.draw_layer == DrawLayer::Above && s.scripting.glyph.is_visible()) {
|
||||
@@ -148,7 +148,7 @@ impl Board {
|
||||
|
||||
// Otherwise the floor, or the canonical black empty cell.
|
||||
self.floor
|
||||
.glyph_at(p.ux(), p.uy(), self.width)
|
||||
.glyph_at(p, self.width)
|
||||
.unwrap_or_else(|| Glyph::transparent())
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ impl Board {
|
||||
/// Panics if `x` or `y` are out of bounds.
|
||||
pub fn is_opaque_at<P: Into<Point>>(&self, p: P) -> bool {
|
||||
let p = p.into();
|
||||
if self.sensors.iter().any(|s| s.x == p.ux() && s.y == p.uy() && s.scripting.optics.opaque) {
|
||||
if self.sensors.iter().any(|s| s.location == p && s.scripting.optics.opaque) {
|
||||
true
|
||||
} else {
|
||||
match self.get(p) {
|
||||
@@ -240,110 +240,13 @@ impl Board {
|
||||
// Glowing sensors
|
||||
for s in self.sensors.iter() {
|
||||
if s.optics().glow > 0 {
|
||||
add_source(&mut lighting, s.x, s.y, s.optics().glow, color_to_rgb(s.scripting.glyph.fg));
|
||||
add_source(&mut lighting, s.location.ux(), s.location.uy(), s.optics().glow, color_to_rgb(s.scripting.glyph.fg));
|
||||
}
|
||||
}
|
||||
|
||||
Some(lighting)
|
||||
}
|
||||
|
||||
// /// Whether the solid at `(x, y)` can be **shifted** one step in `dir`: it is
|
||||
// /// itself a pushable solid *and* the next cell is either empty or holds another
|
||||
// /// pushable solid.
|
||||
// ///
|
||||
// /// Unlike [`can_push`](Board::can_push) this inspects only the single cell
|
||||
// /// ahead — it does **not** verify the whole chain ends in open space. It is the
|
||||
// /// right test for a simultaneous rotation/shift (applied via
|
||||
// /// [`apply_swap`](Board::apply_swap)), where a destination is occupied by
|
||||
// /// another pushable that is itself moving the same frame. Returns `false` if
|
||||
// /// `(x, y)` holds no pushable, or the cell ahead runs off the board.
|
||||
// ///
|
||||
// /// The **player** is always a blocker: a shift can't relocate the player, and
|
||||
// /// `apply_swap` refuses to overwrite it, so a cell holding the player is never
|
||||
// /// an acceptable shift destination.
|
||||
// pub fn can_shift(&self, x: usize, y: usize, dir: Direction) -> bool {
|
||||
// // The source must hold a solid pushable in `dir`.
|
||||
// if !self.is_pushable(x, y, dir) {
|
||||
// return false;
|
||||
// }
|
||||
// let (dx, dy): (i64, i64) = dir.into();
|
||||
// let next = (x as i64 + dx, y as i64 + dy);
|
||||
// if !self.in_bounds(next) {
|
||||
// return false; // nothing to shift into off the board
|
||||
// }
|
||||
// let (nx, ny) = (next.0 as usize, next.1 as usize);
|
||||
// // The player always blocks a shift: a shift can't relocate it and
|
||||
// // `apply_swap` refuses to overwrite it. (The player reads as pushable, so it
|
||||
// // must be excluded explicitly before the cell-ahead test below.)
|
||||
// if self.get(nx, ny).as_ref().is_some_and(Tile::player) {
|
||||
// return false;
|
||||
// }
|
||||
// // The cell ahead is acceptable if it is empty or another pushable solid.
|
||||
// self.is_passable(nx, ny) || self.is_pushable(nx, ny, dir)
|
||||
// }
|
||||
|
||||
// /// Shoves the chain of pushable solids starting at `(x, y)` one step in `dir`,
|
||||
// /// leaving `Empty` floor behind each moved cell.
|
||||
// ///
|
||||
// /// No-op when the chain can't move (it self-checks via [`can_push`](Board::can_push)),
|
||||
// /// so it is safe to call unconditionally.
|
||||
// /// Returns the cells the shoved solids moved **into** (each chain cell stepped
|
||||
// /// one cell in `dir`), so the caller can fire `enter` on any non-solid object a
|
||||
// /// pushed solid landed on. Empty when nothing moved.
|
||||
// pub fn push(&mut self, x: usize, y: usize, dir: Direction) -> Vec<(usize, usize)> {
|
||||
// if !self.can_push(x, y, dir) {
|
||||
// return Vec::new();
|
||||
// }
|
||||
// let (dx, dy): (i64, i64) = dir.into();
|
||||
// // can_push guaranteed the chain ends at an in-bounds passable cell, so
|
||||
// // re-walk it (no bounds checks needed) and shift the far end first, which
|
||||
// // keeps each destination cell vacated before its occupant arrives.
|
||||
// let mut chain: Vec<(usize, usize)> = Vec::new();
|
||||
// let (mut cx, mut cy) = (x, y);
|
||||
// while !self.is_passable(cx, cy) {
|
||||
// chain.push((cx, cy));
|
||||
// cx = (cx as i64 + dx) as usize;
|
||||
// cy = (cy as i64 + dy) as usize;
|
||||
// }
|
||||
// for &(px, py) in chain.iter().rev() {
|
||||
// self.shift_solid(px, py, dx, dy);
|
||||
// }
|
||||
// // Each solid ended up one step along `dir`; those destination cells are
|
||||
// // where an `enter` may need to fire.
|
||||
// chain
|
||||
// .iter()
|
||||
// .map(|&(px, py)| ((px as i64 + dx) as usize, (py as i64 + dy) as usize))
|
||||
// .collect()
|
||||
// }
|
||||
|
||||
// /// Tries to walk the object in x,y in the given direction. Pushes the cell in that direction
|
||||
// /// first. This won't move anything if:
|
||||
// /// - the cell x,y is empty; no-op
|
||||
// /// - x,y is out of bounds, no-op
|
||||
// /// - the target cell can't be pushed in that direction, either because it's `Block` or
|
||||
// /// something in its chain is
|
||||
// pub fn move_object<P: Into<Point>>(&mut self, p: P, dir: Direction) {
|
||||
// let p = p.into();
|
||||
// // Is this even a cell?
|
||||
// if self.in_bounds(p) {
|
||||
// // Is there anything in this cell?
|
||||
// if !self.is_empty(p) {
|
||||
// let (tx, ty) = dir.from_point(p.x, p.y);
|
||||
// // Is the _target_ in bounds?
|
||||
// if self.in_bounds((tx, ty)) {
|
||||
// // Attempt to push the target out of the way
|
||||
// self.push(tx as usize, ty as usize, dir);
|
||||
// // Is the cell now empty?
|
||||
// if self.get(tx as usize, ty as usize).is_none() {
|
||||
// // Then finally, move the thing:
|
||||
// let thing = self.grid[x + y * self.width].take();
|
||||
// self.grid[tx as usize + ty as usize * self.width] = thing;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
/// Returns the [`ObjectId`]s of the **non-solid** objects at `(x, y)`.
|
||||
///
|
||||
/// These are the targets of an `enter` hook when a solid relocates onto the
|
||||
@@ -353,7 +256,7 @@ impl Board {
|
||||
let p = p.into();
|
||||
self.sensors
|
||||
.iter()
|
||||
.filter(|s| s.x == p.ux() && s.y == p.uy())
|
||||
.filter(|s| s.location == p)
|
||||
.map(|s| s.scripting.id)
|
||||
.collect()
|
||||
}
|
||||
@@ -361,7 +264,7 @@ impl Board {
|
||||
/// Find and return the portal at the given location
|
||||
pub fn portal_at<P: Into<Point>>(&self, p: P) -> Option<&Portal> {
|
||||
let p = p.into();
|
||||
self.portals.iter().find(|&portal| portal.location() == (p.ux(), p.uy()))
|
||||
self.portals.iter().find(|&portal| portal.location == p)
|
||||
}
|
||||
|
||||
/// Editor primitive: stamps `arch` (with visual `glyph`) into the cell at
|
||||
@@ -391,7 +294,7 @@ impl Board {
|
||||
/// `shift()` fn. Returns a [`ShiftOutcome`] carrying any error [`LogLine`]s for
|
||||
/// the caller to log plus the `(from, to)` relocations it performed (so the
|
||||
/// caller can fire `enter` on non-solids each moved solid landed on).
|
||||
pub fn apply_shift(&mut self, cells: &[(i64, i64)]) -> Result<Vec<((i64, i64), (i64, i64))>, String> {
|
||||
pub fn apply_shift(&mut self, cells: &[Point]) -> Result<Vec<(Point, Point)>, String> {
|
||||
// Validate all the cells are in bounds, error if not:
|
||||
if cells.iter().any(|&c| !self.in_bounds(c)) {
|
||||
return Err("Called shift() with a cell out of bounds".to_string())
|
||||
@@ -474,7 +377,7 @@ impl Board {
|
||||
|
||||
for (i, tile) in self.grid.iter().enumerate() {
|
||||
if let Some(Tile::Object(obj)) = tile && obj.scripting.id == id {
|
||||
return Some(Box::new(LocatedObject(obj, (i % self.width, i / self.width))))
|
||||
return Some(Box::new(LocatedObject(obj, (i % self.width, i / self.width).into())))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,7 +397,7 @@ impl Board {
|
||||
if let Some(Tile::Object(obj)) = tile &&
|
||||
let Some(n) = obj.scripting.name.as_ref() &&
|
||||
n.as_str() == name {
|
||||
return Some(Box::new(LocatedObject(obj, (i % self.width, i / self.width))))
|
||||
return Some(Box::new(LocatedObject(obj, (i % self.width, i / self.width).into())))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -530,7 +433,7 @@ impl Board {
|
||||
// Add the objects into it
|
||||
for (i, tile) in self.grid.iter().enumerate() {
|
||||
if let Some(Tile::Object(obj)) = tile {
|
||||
hookables.push(Box::new(LocatedObject(obj, (i % self.width, i / self.width))))
|
||||
hookables.push(Box::new(LocatedObject(obj, (i % self.width, i / self.width).into())))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -553,10 +456,9 @@ impl Board {
|
||||
|
||||
pub fn move_sensor(&mut self, id: ObjectId, dir: Direction) {
|
||||
if let Some((sensor_idx, _)) = self.sensors.iter().enumerate().find(|(idx, s)| s.scripting.id == id) {
|
||||
let new_loc = (self.sensors[sensor_idx].x as i64 + dir.dx(), self.sensors[sensor_idx].y as i64 + dir.dy());
|
||||
let new_loc = self.sensors[sensor_idx].location.in_dir(dir);
|
||||
if self.in_bounds(new_loc) {
|
||||
self.sensors[sensor_idx].x = new_loc.0 as usize;
|
||||
self.sensors[sensor_idx].y = new_loc.1 as usize;
|
||||
self.sensors[sensor_idx].location = new_loc
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -595,7 +497,7 @@ pub(crate) mod tests {
|
||||
use crate::builtin::Builtin;
|
||||
use crate::floor::Floor;
|
||||
use crate::glyph::Glyph;
|
||||
use crate::utils::{Direction, ObjectId};
|
||||
use crate::utils::{Direction, ObjectId, Point};
|
||||
use color::Rgba8;
|
||||
use std::collections::HashMap;
|
||||
use crate::object_def::ObjectDef;
|
||||
@@ -638,17 +540,17 @@ pub(crate) mod tests {
|
||||
|
||||
/// Stamps a crate cell onto the grid.
|
||||
pub(crate) fn crate_at(board: &mut Board, x: usize, y: usize) {
|
||||
*board.get_mut(x, y) = Some(TileSpec::krate().into_tile(&mut board.next_object_id).unwrap());
|
||||
*board.get_mut((x, y)) = Some(TileSpec::krate().into_tile(&mut board.next_object_id).unwrap());
|
||||
}
|
||||
|
||||
/// Stamps a wall cell onto the grid.
|
||||
pub(crate) fn wall_at(board: &mut Board, x: usize, y: usize) {
|
||||
*board.get_mut(x, y) = Some(TileSpec::wall().into_tile(&mut board.next_object_id).unwrap());
|
||||
*board.get_mut((x, y)) = Some(TileSpec::wall().into_tile(&mut board.next_object_id).unwrap());
|
||||
}
|
||||
|
||||
/// Stamps a gem cell onto the grid.
|
||||
pub(crate) fn gem_at(board: &mut Board, x: usize, y: usize) {
|
||||
*board.get_mut(x, y) = Some(TileSpec::gem().into_tile(&mut board.next_object_id).unwrap());
|
||||
*board.get_mut((x, y)) = Some(TileSpec::gem().into_tile(&mut board.next_object_id).unwrap());
|
||||
}
|
||||
|
||||
/// Stamps the builtin named `kind` (any alias accepted by [`Builtin::from_name`],
|
||||
@@ -666,7 +568,7 @@ pub(crate) mod tests {
|
||||
Tile::Object(obj) => obj.scripting.id,
|
||||
Tile::Player => unreachable!("a builtin never resolves to the player"),
|
||||
};
|
||||
*board.get_mut(x, y) = Some(tile);
|
||||
*board.get_mut((x, y)) = Some(tile);
|
||||
id
|
||||
}
|
||||
|
||||
@@ -697,7 +599,7 @@ pub(crate) mod tests {
|
||||
Tile::Object(obj) => obj.scripting.id,
|
||||
Tile::Player => unreachable!("an object spec never resolves to the player"),
|
||||
};
|
||||
*board.get_mut(x, y) = Some(tile);
|
||||
*board.get_mut((x, y)) = Some(tile);
|
||||
id
|
||||
}
|
||||
|
||||
@@ -726,7 +628,7 @@ pub(crate) mod tests {
|
||||
Tile::Object(obj) => obj.scripting.id,
|
||||
Tile::Player => unreachable!("an object spec never resolves to the player"),
|
||||
};
|
||||
*board.get_mut(x, y) = Some(tile);
|
||||
*board.get_mut((x, y)) = Some(tile);
|
||||
id
|
||||
}
|
||||
|
||||
@@ -755,7 +657,7 @@ pub(crate) mod tests {
|
||||
|
||||
/// Stamps a player cell onto the grid.
|
||||
pub(crate) fn player_at(board: &mut Board, x: usize, y: usize) {
|
||||
*board.get_mut(x, y) = Some(TileSpec::player().into_tile(&mut board.next_object_id).unwrap());
|
||||
*board.get_mut((x, y)) = Some(TileSpec::player().into_tile(&mut board.next_object_id).unwrap());
|
||||
}
|
||||
|
||||
pub(crate) fn lamp_at(board: &mut Board, x: usize, y: usize) {
|
||||
@@ -778,7 +680,7 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
pub(crate) fn is_builtin(board: &Board, x: usize, y: usize, tag: &str) -> bool {
|
||||
if let Some(Tile::Object(obj)) = board.get(x, y) {
|
||||
if let Some(Tile::Object(obj)) = board.get((x, y)) {
|
||||
obj.scripting.tags.contains(&format!("BUILTIN_{tag}"))
|
||||
} else { false }
|
||||
}
|
||||
@@ -794,20 +696,6 @@ pub(crate) mod tests {
|
||||
assert!(!board.in_bounds((0, 2)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_push_is_read_only_and_correct() {
|
||||
let mut board = open_board(3, 1, (0, 0));
|
||||
crate_at(&mut board, 1, 0);
|
||||
assert!(board.can_push(1, 0, Direction::East));
|
||||
assert_matches!(board.get(1, 0), Some(Tile::Object(_)));
|
||||
assert_matches!(board.get(2, 0), None);
|
||||
|
||||
let mut board = open_board(3, 1, (0, 0));
|
||||
crate_at(&mut board, 1, 0);
|
||||
wall_at(&mut board, 2, 0);
|
||||
assert!(!board.can_push(1, 0, Direction::East));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_shift_only_checks_the_cell_ahead() {
|
||||
// Source must be pushable.
|
||||
@@ -817,7 +705,7 @@ pub(crate) mod tests {
|
||||
// Crate with open space ahead: shiftable.
|
||||
crate_at(&mut board, 0, 0);
|
||||
assert!(board.can_shift(0, 0, Direction::East));
|
||||
assert_matches!(board.get(0, 0), Some(Tile::Object(_)));
|
||||
assert_matches!(board.get((0, 0)), Some(Tile::Object(_)));
|
||||
|
||||
// Crate with another pushable crate ahead: still shiftable (unlike can_push,
|
||||
// which would follow the chain to the wall and fail).
|
||||
@@ -861,9 +749,9 @@ 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!(board.get(1, 0).is_none());
|
||||
assert!(board.get((1, 0)).is_none());
|
||||
assert!(is_builtin(&board, 2, 0,"crate"));
|
||||
assert_eq!(board.player_pos(), (3, 0));
|
||||
assert_eq!(board.player_pos(), Point { x: 3, y: 0 });
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -875,7 +763,7 @@ pub(crate) mod tests {
|
||||
assert!(!board.can_push(1, 0, Direction::East));
|
||||
board.push(1, 0, Direction::East); // no-op
|
||||
assert!(is_builtin(&board, 1, 0, "crate"));
|
||||
assert_eq!(board.player_pos(), (2, 0));
|
||||
assert_eq!(board.player_pos(), Point { x: 2, y: 0 });
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -901,8 +789,8 @@ pub(crate) mod tests {
|
||||
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), Builtin::Wall.default_glyph_for("wall"));
|
||||
assert_eq!(board.glyph_at(1, 0), floor_glyph);
|
||||
assert_eq!(board.glyph_at((0, 0)), Builtin::Wall.default_glyph_for("wall"));
|
||||
assert_eq!(board.glyph_at((1, 0)), floor_glyph);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -939,7 +827,7 @@ pub(crate) mod tests {
|
||||
assert!(is_builtin(&board, 0, 0, "crate")); // wrapped from (4,0)
|
||||
assert!(is_builtin(&board, 1, 0, "crate")); // moved from (0,0)
|
||||
assert!(is_builtin(&board, 2, 0, "wall")); // blocked, immobile
|
||||
assert!(board.get(3, 0).is_none()); // cleared, crate moved
|
||||
assert!(board.get((3, 0)).is_none()); // cleared, crate moved
|
||||
assert!(is_builtin(&board, 4, 0, "crate")); // moved from (3,0)
|
||||
}
|
||||
|
||||
@@ -955,8 +843,8 @@ pub(crate) mod tests {
|
||||
fn wall_is_opaque_empty_is_not() {
|
||||
let mut board = open_board(3, 1, (0, 0));
|
||||
wall_at(&mut board, 1, 0);
|
||||
assert!(board.is_opaque_at(1, 0)); // wall blocks sight
|
||||
assert!(!board.is_opaque_at(2, 0)); // empty cell is transparent
|
||||
assert!(board.is_opaque_at((1, 0))); // wall blocks sight
|
||||
assert!(!board.is_opaque_at((2, 0))); // empty cell is transparent
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user