redoing bump

This commit is contained in:
2026-07-08 13:21:27 -05:00
parent f407b5d9a6
commit e545395ac6
12 changed files with 243 additions and 90 deletions
+24 -17
View File
@@ -208,7 +208,7 @@ impl GameState {
// Logs are collected here rather than pushed inline, since the board borrow
// below also borrows `self`.
let mut logs: Vec<LogLine> = Vec::new();
let mut bumps: Vec<(ObjectId, i64)> = Vec::new();
let mut bumps: Vec<(ObjectId, Direction)> = Vec::new();
// Net change to player stats from AddGems / AlterHealth actions; applied
// to `self` after the board borrow drops.
let mut gem_delta: i64 = 0;
@@ -225,7 +225,9 @@ impl GameState {
match ba.action {
Action::Move(dir) => {
if let Some(bumped) = step_object(&mut board, ba.source, dir) {
bumps.push((bumped, ba.source as i64));
// The bump "comes from" the side the mover advanced from,
// i.e. the opposite of its travel direction.
bumps.push((bumped, dir.opposite()));
}
}
Action::SetTile(tile) => {
@@ -382,8 +384,8 @@ impl GameState {
self.log.push(LogLine::error(format!("set_key: unknown color {color:?}")));
}
}
for (bumped, bumper) in bumps {
self.scripts.run_bump(bumped, bumper);
for (bumped, dir) in bumps {
self.scripts.run_bump(bumped, dir);
}
for (target, fn_name, arg) in sends {
self.scripts.run_send(target, &fn_name, arg);
@@ -462,8 +464,9 @@ impl GameState {
///
/// The move is ignored if the target cell is out of bounds, or it is neither
/// passable nor a pushable solid that can be shoved aside. No-ops silently (the
/// caller does not need to check). If the target cell holds a solid object, that
/// object's `bump(-1)` hook fires (whether or not the player ends up moving).
/// caller does not need to check). If a solid object lies in the path — directly
/// or at the end of a chain of crates the player is shoving — its `bump` hook
/// fires with the direction the bump came from (whether or not the player moves).
pub fn try_move(&mut self, dir: Direction) {
let bumped;
let grabbed;
@@ -479,12 +482,13 @@ impl GameState {
// Walking onto a grab thing (e.g. a gem) is never blocked: the player
// moves onto it and its grab() hook fires (the thing despawns itself).
grabbed = board.grab_object_at(nx, ny);
// A solid object in the way is bumped by the player (id -1) — but a
// grab thing fires grab() instead of bump(), so don't also bump it.
bumped = board
.solid_at(nx, ny)
.filter(|_| grabbed.is_none())
.and_then(|s| s.object_id());
// A solid object in the way is bumped by the player — possibly through a
// chain of crates the player is shoving (see `bump_target`) — but a grab
// thing fires grab() instead of bump(), so don't also bump it.
bumped = grabbed
.is_none()
.then(|| board.bump_target(nx, ny, dir))
.flatten();
if grabbed.is_some() || board.is_passable(nx, ny) || board.can_push(nx, ny, dir) {
// Don't push a grab thing aside — walk onto it. Otherwise shove any
// pushable chain out of the way (no-op when there's nothing to push).
@@ -515,7 +519,8 @@ impl GameState {
self.resolve();
}
if let Some(idx) = bumped {
self.scripts.run_bump(idx, -1);
// The player advanced in `dir`, so the bump arrives from the opposite side.
self.scripts.run_bump(idx, dir.opposite());
self.drain_errors();
}
}
@@ -526,9 +531,10 @@ impl GameState {
///
/// The move itself proceeds only if the target is in bounds and either passable or a
/// pushable solid the object can shove out of the way (see [`Board::can_push`]). The
/// bump is recorded whenever a solid object occupies the target — whether it gets
/// pushed aside or blocks the move — since something tried to move into it. Walls and
/// crates carry no script, so only solid objects yield a bump.
/// bump is recorded for the solid object the move presses into — directly, or at the
/// end of a chain of crates being shoved (see [`Board::bump_target`]) — whether it
/// gets pushed aside or blocks the move. Walls and crates carry no script, so only
/// solid objects yield a bump.
fn step_object(board: &mut Board, id: ObjectId, dir: Direction) -> Option<ObjectId> {
let (dx, dy): (i64, i64) = dir.into();
let (ox, oy) = board.objects.get(&id).map(|o| (o.x, o.y))?;
@@ -538,7 +544,8 @@ fn step_object(board: &mut Board, id: ObjectId, dir: Direction) -> Option<Object
}
let (nx, ny) = (target.0 as usize, target.1 as usize);
// Capture the bumped object before any push relocates it (its id is stable).
let bumped = board.solid_at(nx, ny).and_then(|s| s.object_id());
// Walks through a pushed crate chain to the object it presses against.
let bumped = board.bump_target(nx, ny, dir);
if board.is_passable(nx, ny) || board.can_push(nx, ny, dir) {
board.push(nx, ny, dir); // shoves a crate/object out of the way; no-op otherwise
let obj = board.objects.get_mut(&id).expect("id checked above");