Perform bonking for normal force floors as well, which fixes several subtle block pushing issues

This commit is contained in:
Eevee (Evelyn Woods) 2021-02-28 18:31:08 -07:00
parent f1ba1815f7
commit 0ca0467192

View File

@ -355,17 +355,31 @@ export class Cell extends Array {
if (actor === level.player) { if (actor === level.player) {
level._set_tile_prop(actor, 'is_pushing', true); level._set_tile_prop(actor, 'is_pushing', true);
} }
// We can't directly push a sliding block, even one on a force floor that's
// stuck on a wall. Instead, it becomes a pending move for the block, which
// will use this as a decision next time it's allowed to move
// FIXME this is clumsy and creates behavior dependent on actor order. my
// original implementation only did this if the push /failed/; is that worth
// a compat option? also, how does any of this work under lynx rules?
if (tile.slide_mode === 'force' ||
(tile.slide_mode !== null && tile.movement_cooldown > 0))
{
level._set_tile_prop(tile, 'pending_push', direction);
// FIXME if the block has already made a decision then this is necessary
// to override it. but i don't like it; (a) it might cause blocks to
// get stuck against walls on force floors, because the code to fix that
// is at decision time; (b) it's done for pulling too and just feels
// hacky?
tile.decision = direction;
return false;
}
if (level.attempt_out_of_turn_step(tile, direction)) { if (level.attempt_out_of_turn_step(tile, direction)) {
if (actor === level.player) { if (actor === level.player) {
level.sfx.play_once('push'); level.sfx.play_once('push');
} }
} }
else { else {
// If the push failed and the obstacle is in the middle of a slide,
// remember this as the next move it'll make.
if (tile.slide_mode !== null && tile.movement_cooldown > 0) {
level._set_tile_prop(tile, 'pending_push', direction);
}
return false; return false;
} }
} }
@ -1108,7 +1122,7 @@ export class Level extends LevelInterface {
let success = this.attempt_step(actor, direction); let success = this.attempt_step(actor, direction);
// CC2 handles bonking for all kinds of sliding here -- bonking on ice causes an immediate // CC2 handles bonking for all kinds of sliding here -- bonking on ice causes an immediate
// turnaround, and bonking on an RFF rolls a new direction and tries again // turnaround, and bonking on a force floor tries again (including rolling a new RFF)
// TODO this assumes the slide comes from the terrain, which is always the case atm // TODO this assumes the slide comes from the terrain, which is always the case atm
if (! success) { if (! success) {
let terrain = actor.cell.get_terrain(); let terrain = actor.cell.get_terrain();
@ -1120,19 +1134,24 @@ export class Level extends LevelInterface {
(terrain.type.slide_mode === 'ice' && ( (terrain.type.slide_mode === 'ice' && (
! actor.ignores(terrain.type.name) || actor.type.name === 'ghost')) || ! actor.ignores(terrain.type.name) || actor.type.name === 'ghost')) ||
// But they only bonk on a force floor if it affects them // But they only bonk on a force floor if it affects them
(terrain.type.name === 'force_floor_all' && (terrain.type.slide_mode === 'force' &&
actor.slide_mode && ! actor.ignores(terrain.type.name)))) actor.slide_mode && ! actor.ignores(terrain.type.name))))
{ {
// Turn the actor around (so ice corners bonk correctly), pretend they stepped on // Turn the actor around so ice corners bonk correctly
// the tile again (so RFFs roll again), and try moving again if (terrain.type.slide_mode === 'ice') {
this.set_actor_direction(actor, DIRECTIONS[direction].opposite); this.set_actor_direction(actor, DIRECTIONS[direction].opposite);
}
// Pretend they stepped on the tile again
// Note that ghosts bonk even on ice corners, which they can otherwise pass through, // Note that ghosts bonk even on ice corners, which they can otherwise pass through,
// argh! // argh!
if (terrain.type.on_arrive && actor.type.name !== 'ghost') { if (terrain.type.on_arrive && actor.type.name !== 'ghost') {
terrain.type.on_arrive(terrain, this, actor); terrain.type.on_arrive(terrain, this, actor);
} }
// If we got a new direction, try moving again
if (direction !== actor.direction) {
success = this.attempt_step(actor, actor.direction); success = this.attempt_step(actor, actor.direction);
} }
}
else if (actor.slide_mode === 'teleport') { else if (actor.slide_mode === 'teleport') {
// Failed teleport slides only last for a single attempt. (Successful teleports // Failed teleport slides only last for a single attempt. (Successful teleports
// continue the slide until landing on a new tile, as normal; otherwise you couldn't // continue the slide until landing on a new tile, as normal; otherwise you couldn't
@ -1420,11 +1439,10 @@ export class Level extends LevelInterface {
} }
} }
// If we're overriding a force floor but the direction we're moving in is blocked, the // If we're overriding a force floor but the direction we're moving in is blocked, this
// force floor takes priority (and we've already bumped the wall(s)) // counts as a forced move
if (actor.slide_mode === 'force' && ! open) { if (actor.slide_mode === 'force' && ! open) {
this._set_tile_prop(actor, 'last_move_was_force', true); this._set_tile_prop(actor, 'last_move_was_force', true);
actor.decision = actor.direction;
} }
else { else {
// Otherwise this is 100% a conscious move so we lose our override power next tic // Otherwise this is 100% a conscious move so we lose our override power next tic
@ -1451,8 +1469,10 @@ export class Level extends LevelInterface {
if (actor.pending_push) { if (actor.pending_push) {
// Blocks that were pushed while sliding will move in the push direction as soon as // Blocks that were pushed while sliding will move in the push direction as soon as
// they stop sliding, regardless of what they landed on. Also used for hooking. // they can make a decision, even if they're still sliding or are off-tic. Also used
// This isn't cleared until the block makes another move; see _do_actor_movement. // for hooking. (Note that if the block is on a force floor and is blocked in the push
// direction, under CC2 rules it'll then try the force floor; see attempt_step.)
// This isn't cleared until the block actually attempts a move; see _do_actor_movement.
actor.decision = actor.pending_push; actor.decision = actor.pending_push;
return; return;
} }