Remove the "stuck" flag and fix all the repercussions of that
This commit is contained in:
parent
e803af2fd2
commit
39d463932b
58
js/game.js
58
js/game.js
@ -82,7 +82,9 @@ export class Tile {
|
||||
return (
|
||||
this.type.pushes && this.type.pushes[tile.type.name] &&
|
||||
(! tile.type.allows_push || tile.type.allows_push(tile, direction)) &&
|
||||
! tile.stuck);
|
||||
// Need to explicitly check this here, otherwise you could /attempt/ to push a block,
|
||||
// which would fail, but it would still change the block's direction
|
||||
! tile.cell.blocks_leaving(tile, direction));
|
||||
}
|
||||
|
||||
// Inventory stuff
|
||||
@ -155,12 +157,14 @@ export class Cell extends Array {
|
||||
|
||||
blocks_leaving(actor, direction) {
|
||||
for (let tile of this) {
|
||||
if (tile !== actor &&
|
||||
! tile.type.is_swivel && tile.type.thin_walls &&
|
||||
tile.type.thin_walls.has(direction))
|
||||
{
|
||||
if (tile === actor)
|
||||
continue;
|
||||
|
||||
if (tile.type.traps && tile.type.traps(tile, actor))
|
||||
return true;
|
||||
|
||||
if (tile.type.blocks_leaving && tile.type.blocks_leaving(tile, actor, direction))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -266,7 +270,6 @@ export class Level {
|
||||
|
||||
let stored_cell = this.stored_level.linear_cells[n];
|
||||
n++;
|
||||
let has_cloner, has_trap, has_forbidden;
|
||||
|
||||
for (let template_tile of stored_cell) {
|
||||
let tile = Tile.from_template(template_tile);
|
||||
@ -279,30 +282,13 @@ export class Level {
|
||||
this.power_sources.push(tile);
|
||||
}
|
||||
|
||||
// TODO well this is pretty special-casey. maybe come up
|
||||
// with a specific pass at the beginning of the level?
|
||||
// TODO also assumes a specific order...
|
||||
if (tile.type.name === 'cloner') {
|
||||
has_cloner = true;
|
||||
}
|
||||
if (tile.type.name === 'trap') {
|
||||
has_trap = true;
|
||||
}
|
||||
|
||||
if (tile.type.is_player) {
|
||||
// TODO handle multiple players, also chip and melinda both
|
||||
// TODO complain if no player
|
||||
this.player = tile;
|
||||
}
|
||||
if (tile.type.is_actor) {
|
||||
if (has_cloner) {
|
||||
// TODO is there any reason not to add clone templates to the actor
|
||||
// list?
|
||||
tile.stuck = true;
|
||||
}
|
||||
if (! tile.stuck) {
|
||||
this.actors.push(tile);
|
||||
}
|
||||
this.actors.push(tile);
|
||||
}
|
||||
cell._add(tile);
|
||||
|
||||
@ -581,9 +567,9 @@ export class Level {
|
||||
{
|
||||
this._set_tile_prop(actor, 'pending_reverse', false);
|
||||
}
|
||||
// Blocks that were pushed while sliding will move in the push direction as soon as they
|
||||
// stop sliding, regardless of what they landed on
|
||||
if (actor.pending_push) {
|
||||
// Blocks that were pushed while sliding will move in the push direction as soon as
|
||||
// they stop sliding, regardless of what they landed on
|
||||
actor.decision = actor.pending_push;
|
||||
this._set_tile_prop(actor, 'pending_push', null);
|
||||
continue;
|
||||
@ -622,6 +608,13 @@ export class Level {
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (actor.cell.some(tile => tile.type.traps && tile.type.traps(tile, actor))) {
|
||||
// An actor in a cloner or a closed trap can't turn
|
||||
// TODO because of this, if a tank is trapped when a blue button is pressed, then
|
||||
// when released, it will make one move out of the trap and /then/ turn around and
|
||||
// go back into the trap. this is consistent with CC2 but not ms/lynx
|
||||
continue;
|
||||
}
|
||||
else if (actor.type.movement_mode === 'forward') {
|
||||
// blue tank behavior: keep moving forward, reverse if the flag is set
|
||||
let direction = actor.direction;
|
||||
@ -715,9 +708,7 @@ export class Level {
|
||||
|
||||
// Check which of those directions we *can*, probably, move in
|
||||
// TODO i think player on force floor will still have some issues here
|
||||
// FIXME probably bail earlier for stuck actors so the prng isn't advanced? what is the
|
||||
// lynx behavior? also i hear something about blobs on cloners??
|
||||
if (direction_preference && ! actor.stuck) {
|
||||
if (direction_preference) {
|
||||
let fallback_direction;
|
||||
for (let direction of direction_preference) {
|
||||
if (direction === 'WALKER') {
|
||||
@ -837,9 +828,6 @@ export class Level {
|
||||
|
||||
this.set_actor_direction(actor, direction);
|
||||
|
||||
if (actor.stuck)
|
||||
return false;
|
||||
|
||||
// Record our speed, and halve it below if we're stepping onto a sliding tile
|
||||
let speed = actor.type.movement_speed;
|
||||
|
||||
@ -1628,8 +1616,4 @@ export class Level {
|
||||
set_actor_direction(actor, direction) {
|
||||
this._set_tile_prop(actor, 'direction', direction);
|
||||
}
|
||||
|
||||
set_actor_stuck(actor, is_stuck) {
|
||||
this._set_tile_prop(actor, 'stuck', is_stuck);
|
||||
}
|
||||
}
|
||||
|
||||
104
js/tiletypes.js
104
js/tiletypes.js
@ -43,6 +43,10 @@ function on_ready_force_floor(me, level) {
|
||||
}
|
||||
}
|
||||
|
||||
function blocks_leaving_thin_walls(me, actor, direction) {
|
||||
return me.type.thin_walls.has(direction);
|
||||
}
|
||||
|
||||
function player_visual_state(me) {
|
||||
if (! me) {
|
||||
return 'normal';
|
||||
@ -169,22 +173,27 @@ const TILE_TYPES = {
|
||||
thinwall_n: {
|
||||
draw_layer: LAYER_OVERLAY,
|
||||
thin_walls: new Set(['north']),
|
||||
blocks_leaving: blocks_leaving_thin_walls,
|
||||
},
|
||||
thinwall_s: {
|
||||
draw_layer: LAYER_OVERLAY,
|
||||
thin_walls: new Set(['south']),
|
||||
blocks_leaving: blocks_leaving_thin_walls,
|
||||
},
|
||||
thinwall_e: {
|
||||
draw_layer: LAYER_OVERLAY,
|
||||
thin_walls: new Set(['east']),
|
||||
blocks_leaving: blocks_leaving_thin_walls,
|
||||
},
|
||||
thinwall_w: {
|
||||
draw_layer: LAYER_OVERLAY,
|
||||
thin_walls: new Set(['west']),
|
||||
blocks_leaving: blocks_leaving_thin_walls,
|
||||
},
|
||||
thinwall_se: {
|
||||
draw_layer: LAYER_OVERLAY,
|
||||
thin_walls: new Set(['south', 'east']),
|
||||
blocks_leaving: blocks_leaving_thin_walls,
|
||||
},
|
||||
fake_wall: {
|
||||
draw_layer: LAYER_TERRAIN,
|
||||
@ -263,7 +272,6 @@ const TILE_TYPES = {
|
||||
swivel_ne: {
|
||||
draw_layer: LAYER_OVERLAY,
|
||||
thin_walls: new Set(['north', 'east']),
|
||||
is_swivel: true,
|
||||
on_depart(me, level, other) {
|
||||
if (other.direction === 'north') {
|
||||
level.transmute_tile(me, 'swivel_se');
|
||||
@ -276,7 +284,6 @@ const TILE_TYPES = {
|
||||
swivel_se: {
|
||||
draw_layer: LAYER_OVERLAY,
|
||||
thin_walls: new Set(['south', 'east']),
|
||||
is_swivel: true,
|
||||
on_depart(me, level, other) {
|
||||
if (other.direction === 'south') {
|
||||
level.transmute_tile(me, 'swivel_ne');
|
||||
@ -289,7 +296,6 @@ const TILE_TYPES = {
|
||||
swivel_sw: {
|
||||
draw_layer: LAYER_OVERLAY,
|
||||
thin_walls: new Set(['south', 'west']),
|
||||
is_swivel: true,
|
||||
on_depart(me, level, other) {
|
||||
if (other.direction === 'south') {
|
||||
level.transmute_tile(me, 'swivel_nw');
|
||||
@ -302,7 +308,6 @@ const TILE_TYPES = {
|
||||
swivel_nw: {
|
||||
draw_layer: LAYER_OVERLAY,
|
||||
thin_walls: new Set(['north', 'west']),
|
||||
is_swivel: true,
|
||||
on_depart(me, level, other) {
|
||||
if (other.direction === 'north') {
|
||||
level.transmute_tile(me, 'swivel_sw');
|
||||
@ -450,6 +455,7 @@ const TILE_TYPES = {
|
||||
draw_layer: LAYER_TERRAIN,
|
||||
thin_walls: new Set(['south', 'west']),
|
||||
slide_mode: 'ice',
|
||||
blocks_leaving: blocks_leaving_thin_walls,
|
||||
on_arrive(me, level, other) {
|
||||
if (other.direction === 'south') {
|
||||
level.set_actor_direction(other, 'east');
|
||||
@ -463,6 +469,7 @@ const TILE_TYPES = {
|
||||
draw_layer: LAYER_TERRAIN,
|
||||
thin_walls: new Set(['north', 'west']),
|
||||
slide_mode: 'ice',
|
||||
blocks_leaving: blocks_leaving_thin_walls,
|
||||
on_arrive(me, level, other) {
|
||||
if (other.direction === 'north') {
|
||||
level.set_actor_direction(other, 'east');
|
||||
@ -476,6 +483,7 @@ const TILE_TYPES = {
|
||||
draw_layer: LAYER_TERRAIN,
|
||||
thin_walls: new Set(['north', 'east']),
|
||||
slide_mode: 'ice',
|
||||
blocks_leaving: blocks_leaving_thin_walls,
|
||||
on_arrive(me, level, other) {
|
||||
if (other.direction === 'north') {
|
||||
level.set_actor_direction(other, 'west');
|
||||
@ -489,6 +497,7 @@ const TILE_TYPES = {
|
||||
draw_layer: LAYER_TERRAIN,
|
||||
thin_walls: new Set(['south', 'east']),
|
||||
slide_mode: 'ice',
|
||||
blocks_leaving: blocks_leaving_thin_walls,
|
||||
on_arrive(me, level, other) {
|
||||
if (other.direction === 'south') {
|
||||
level.set_actor_direction(other, 'west');
|
||||
@ -718,30 +727,30 @@ const TILE_TYPES = {
|
||||
draw_layer: LAYER_TERRAIN,
|
||||
// TODO not the case for an empty one in cc2, apparently
|
||||
blocks_all: true,
|
||||
traps(me, actor) {
|
||||
return ! actor._clone_release;
|
||||
},
|
||||
activate(me, level) {
|
||||
let cell = me.cell;
|
||||
// Copy, so we don't end up repeatedly cloning the same object
|
||||
for (let tile of Array.from(cell)) {
|
||||
if (tile !== me && tile.type.is_actor) {
|
||||
// Copy this stuff in case the movement changes it
|
||||
let type = tile.type;
|
||||
let direction = tile.direction;
|
||||
let actor = me.cell.get_actor();
|
||||
if (! actor)
|
||||
return;
|
||||
|
||||
// Unstick and try to move the actor; if it's blocked,
|
||||
// abort the clone
|
||||
level.set_actor_stuck(tile, false);
|
||||
if (level.attempt_step(tile, direction)) {
|
||||
level.add_actor(tile);
|
||||
// FIXME add this underneath, just above the cloner
|
||||
let new_tile = new tile.constructor(type, direction);
|
||||
level.add_tile(new_tile, cell);
|
||||
level.set_actor_stuck(new_tile, true);
|
||||
}
|
||||
else {
|
||||
level.set_actor_stuck(tile, true);
|
||||
}
|
||||
}
|
||||
// Copy this stuff in case the movement changes it
|
||||
// TODO should anything else be preserved?
|
||||
let type = actor.type;
|
||||
let direction = actor.direction;
|
||||
|
||||
// Unstick and try to move the actor; if it's blocked, abort the clone.
|
||||
// This temporary flag tells us to let it leave; it doesn't need to be undoable, since
|
||||
// it doesn't persist for more than a tic
|
||||
actor._clone_release = true;
|
||||
if (level.attempt_step(actor, direction)) {
|
||||
// FIXME add this underneath, just above the cloner
|
||||
let new_template = new actor.constructor(type, direction);
|
||||
level.add_tile(new_template, me.cell);
|
||||
level.add_actor(new_template);
|
||||
}
|
||||
delete actor._clone_release;
|
||||
},
|
||||
// Also clones on rising pulse or gray button
|
||||
on_power(me, level) {
|
||||
@ -753,45 +762,17 @@ const TILE_TYPES = {
|
||||
},
|
||||
trap: {
|
||||
draw_layer: LAYER_TERRAIN,
|
||||
on_ready(me, level) {
|
||||
// Trap any actor standing on us, unless we already know we're pressed
|
||||
if (me.presses)
|
||||
return;
|
||||
|
||||
for (let tile of me.cell) {
|
||||
if (tile.type.is_actor) {
|
||||
tile.stuck = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
add_press_ready(me, level, other) {
|
||||
// Same as below, but without using the undo stack or ejection
|
||||
// Same as below, but without ejection
|
||||
me.presses = (me.presses ?? 0) + 1;
|
||||
if (me.presses === 1) {
|
||||
for (let tile of me.cell) {
|
||||
if (tile.type.is_actor) {
|
||||
tile.stuck = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
on_arrive(me, level, other) {
|
||||
if (me.presses) {
|
||||
// Lynx: Traps immediately eject their contents, if possible
|
||||
// TODO compat this, cc2 doens't do it!
|
||||
//level.attempt_step(other, other.direction);
|
||||
}
|
||||
else {
|
||||
level.set_actor_stuck(other, true);
|
||||
}
|
||||
},
|
||||
// Lynx (not cc2): open traps immediately eject their contents on arrival, if possible
|
||||
add_press(me, level) {
|
||||
level._set_tile_prop(me, 'presses', (me.presses ?? 0) + 1);
|
||||
if (me.presses === 1) {
|
||||
// Free everything on us, if we went from 0 to 1 presses (i.e. closed to open)
|
||||
for (let tile of Array.from(me.cell)) {
|
||||
if (tile.type.is_actor) {
|
||||
level.set_actor_stuck(tile, false);
|
||||
// Forcibly move anything released from a trap, to keep it in sync with
|
||||
// whatever pushed the button
|
||||
level.attempt_step(tile, tile.direction);
|
||||
@ -801,23 +782,16 @@ const TILE_TYPES = {
|
||||
},
|
||||
remove_press(me, level) {
|
||||
level._set_tile_prop(me, 'presses', me.presses - 1);
|
||||
if (me.presses === 0) {
|
||||
// Trap everything on us, if we went from 1 to 0 presses (i.e. open to closed)
|
||||
for (let tile of me.cell) {
|
||||
if (tile.type.is_actor) {
|
||||
level.set_actor_stuck(tile, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
traps(me, actor) {
|
||||
return ! me.presses;
|
||||
},
|
||||
on_power(me, level) {
|
||||
// Treat being powered or not as an extra kind of brown button press
|
||||
me.type.add_press(me, level);
|
||||
console.log("powering UP", me.presses);
|
||||
},
|
||||
on_depower(me, level) {
|
||||
me.type.remove_press(me, level);
|
||||
console.log("powering down...", me.presses);
|
||||
},
|
||||
visual_state(me) {
|
||||
if (me && me.presses) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user