redoing bump
This commit is contained in:
+24
-17
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user