Remove the "stuck" flag and fix all the repercussions of that
This commit is contained in:
parent
e803af2fd2
commit
39d463932b
56
js/game.js
56
js/game.js
@ -82,7 +82,9 @@ export class Tile {
|
|||||||
return (
|
return (
|
||||||
this.type.pushes && this.type.pushes[tile.type.name] &&
|
this.type.pushes && this.type.pushes[tile.type.name] &&
|
||||||
(! tile.type.allows_push || tile.type.allows_push(tile, direction)) &&
|
(! 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
|
// Inventory stuff
|
||||||
@ -155,12 +157,14 @@ export class Cell extends Array {
|
|||||||
|
|
||||||
blocks_leaving(actor, direction) {
|
blocks_leaving(actor, direction) {
|
||||||
for (let tile of this) {
|
for (let tile of this) {
|
||||||
if (tile !== actor &&
|
if (tile === actor)
|
||||||
! tile.type.is_swivel && tile.type.thin_walls &&
|
continue;
|
||||||
tile.type.thin_walls.has(direction))
|
|
||||||
{
|
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 true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -266,7 +270,6 @@ export class Level {
|
|||||||
|
|
||||||
let stored_cell = this.stored_level.linear_cells[n];
|
let stored_cell = this.stored_level.linear_cells[n];
|
||||||
n++;
|
n++;
|
||||||
let has_cloner, has_trap, has_forbidden;
|
|
||||||
|
|
||||||
for (let template_tile of stored_cell) {
|
for (let template_tile of stored_cell) {
|
||||||
let tile = Tile.from_template(template_tile);
|
let tile = Tile.from_template(template_tile);
|
||||||
@ -279,31 +282,14 @@ export class Level {
|
|||||||
this.power_sources.push(tile);
|
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) {
|
if (tile.type.is_player) {
|
||||||
// TODO handle multiple players, also chip and melinda both
|
// TODO handle multiple players, also chip and melinda both
|
||||||
// TODO complain if no player
|
// TODO complain if no player
|
||||||
this.player = tile;
|
this.player = tile;
|
||||||
}
|
}
|
||||||
if (tile.type.is_actor) {
|
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);
|
cell._add(tile);
|
||||||
|
|
||||||
if (tile.type.connects_to) {
|
if (tile.type.connects_to) {
|
||||||
@ -581,9 +567,9 @@ export class Level {
|
|||||||
{
|
{
|
||||||
this._set_tile_prop(actor, 'pending_reverse', false);
|
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) {
|
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;
|
actor.decision = actor.pending_push;
|
||||||
this._set_tile_prop(actor, 'pending_push', null);
|
this._set_tile_prop(actor, 'pending_push', null);
|
||||||
continue;
|
continue;
|
||||||
@ -622,6 +608,13 @@ export class Level {
|
|||||||
}
|
}
|
||||||
continue;
|
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') {
|
else if (actor.type.movement_mode === 'forward') {
|
||||||
// blue tank behavior: keep moving forward, reverse if the flag is set
|
// blue tank behavior: keep moving forward, reverse if the flag is set
|
||||||
let direction = actor.direction;
|
let direction = actor.direction;
|
||||||
@ -715,9 +708,7 @@ export class Level {
|
|||||||
|
|
||||||
// Check which of those directions we *can*, probably, move in
|
// Check which of those directions we *can*, probably, move in
|
||||||
// TODO i think player on force floor will still have some issues here
|
// 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
|
if (direction_preference) {
|
||||||
// lynx behavior? also i hear something about blobs on cloners??
|
|
||||||
if (direction_preference && ! actor.stuck) {
|
|
||||||
let fallback_direction;
|
let fallback_direction;
|
||||||
for (let direction of direction_preference) {
|
for (let direction of direction_preference) {
|
||||||
if (direction === 'WALKER') {
|
if (direction === 'WALKER') {
|
||||||
@ -837,9 +828,6 @@ export class Level {
|
|||||||
|
|
||||||
this.set_actor_direction(actor, direction);
|
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
|
// Record our speed, and halve it below if we're stepping onto a sliding tile
|
||||||
let speed = actor.type.movement_speed;
|
let speed = actor.type.movement_speed;
|
||||||
|
|
||||||
@ -1628,8 +1616,4 @@ export class Level {
|
|||||||
set_actor_direction(actor, direction) {
|
set_actor_direction(actor, direction) {
|
||||||
this._set_tile_prop(actor, 'direction', direction);
|
this._set_tile_prop(actor, 'direction', direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
set_actor_stuck(actor, is_stuck) {
|
|
||||||
this._set_tile_prop(actor, 'stuck', is_stuck);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
102
js/tiletypes.js
102
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) {
|
function player_visual_state(me) {
|
||||||
if (! me) {
|
if (! me) {
|
||||||
return 'normal';
|
return 'normal';
|
||||||
@ -169,22 +173,27 @@ const TILE_TYPES = {
|
|||||||
thinwall_n: {
|
thinwall_n: {
|
||||||
draw_layer: LAYER_OVERLAY,
|
draw_layer: LAYER_OVERLAY,
|
||||||
thin_walls: new Set(['north']),
|
thin_walls: new Set(['north']),
|
||||||
|
blocks_leaving: blocks_leaving_thin_walls,
|
||||||
},
|
},
|
||||||
thinwall_s: {
|
thinwall_s: {
|
||||||
draw_layer: LAYER_OVERLAY,
|
draw_layer: LAYER_OVERLAY,
|
||||||
thin_walls: new Set(['south']),
|
thin_walls: new Set(['south']),
|
||||||
|
blocks_leaving: blocks_leaving_thin_walls,
|
||||||
},
|
},
|
||||||
thinwall_e: {
|
thinwall_e: {
|
||||||
draw_layer: LAYER_OVERLAY,
|
draw_layer: LAYER_OVERLAY,
|
||||||
thin_walls: new Set(['east']),
|
thin_walls: new Set(['east']),
|
||||||
|
blocks_leaving: blocks_leaving_thin_walls,
|
||||||
},
|
},
|
||||||
thinwall_w: {
|
thinwall_w: {
|
||||||
draw_layer: LAYER_OVERLAY,
|
draw_layer: LAYER_OVERLAY,
|
||||||
thin_walls: new Set(['west']),
|
thin_walls: new Set(['west']),
|
||||||
|
blocks_leaving: blocks_leaving_thin_walls,
|
||||||
},
|
},
|
||||||
thinwall_se: {
|
thinwall_se: {
|
||||||
draw_layer: LAYER_OVERLAY,
|
draw_layer: LAYER_OVERLAY,
|
||||||
thin_walls: new Set(['south', 'east']),
|
thin_walls: new Set(['south', 'east']),
|
||||||
|
blocks_leaving: blocks_leaving_thin_walls,
|
||||||
},
|
},
|
||||||
fake_wall: {
|
fake_wall: {
|
||||||
draw_layer: LAYER_TERRAIN,
|
draw_layer: LAYER_TERRAIN,
|
||||||
@ -263,7 +272,6 @@ const TILE_TYPES = {
|
|||||||
swivel_ne: {
|
swivel_ne: {
|
||||||
draw_layer: LAYER_OVERLAY,
|
draw_layer: LAYER_OVERLAY,
|
||||||
thin_walls: new Set(['north', 'east']),
|
thin_walls: new Set(['north', 'east']),
|
||||||
is_swivel: true,
|
|
||||||
on_depart(me, level, other) {
|
on_depart(me, level, other) {
|
||||||
if (other.direction === 'north') {
|
if (other.direction === 'north') {
|
||||||
level.transmute_tile(me, 'swivel_se');
|
level.transmute_tile(me, 'swivel_se');
|
||||||
@ -276,7 +284,6 @@ const TILE_TYPES = {
|
|||||||
swivel_se: {
|
swivel_se: {
|
||||||
draw_layer: LAYER_OVERLAY,
|
draw_layer: LAYER_OVERLAY,
|
||||||
thin_walls: new Set(['south', 'east']),
|
thin_walls: new Set(['south', 'east']),
|
||||||
is_swivel: true,
|
|
||||||
on_depart(me, level, other) {
|
on_depart(me, level, other) {
|
||||||
if (other.direction === 'south') {
|
if (other.direction === 'south') {
|
||||||
level.transmute_tile(me, 'swivel_ne');
|
level.transmute_tile(me, 'swivel_ne');
|
||||||
@ -289,7 +296,6 @@ const TILE_TYPES = {
|
|||||||
swivel_sw: {
|
swivel_sw: {
|
||||||
draw_layer: LAYER_OVERLAY,
|
draw_layer: LAYER_OVERLAY,
|
||||||
thin_walls: new Set(['south', 'west']),
|
thin_walls: new Set(['south', 'west']),
|
||||||
is_swivel: true,
|
|
||||||
on_depart(me, level, other) {
|
on_depart(me, level, other) {
|
||||||
if (other.direction === 'south') {
|
if (other.direction === 'south') {
|
||||||
level.transmute_tile(me, 'swivel_nw');
|
level.transmute_tile(me, 'swivel_nw');
|
||||||
@ -302,7 +308,6 @@ const TILE_TYPES = {
|
|||||||
swivel_nw: {
|
swivel_nw: {
|
||||||
draw_layer: LAYER_OVERLAY,
|
draw_layer: LAYER_OVERLAY,
|
||||||
thin_walls: new Set(['north', 'west']),
|
thin_walls: new Set(['north', 'west']),
|
||||||
is_swivel: true,
|
|
||||||
on_depart(me, level, other) {
|
on_depart(me, level, other) {
|
||||||
if (other.direction === 'north') {
|
if (other.direction === 'north') {
|
||||||
level.transmute_tile(me, 'swivel_sw');
|
level.transmute_tile(me, 'swivel_sw');
|
||||||
@ -450,6 +455,7 @@ const TILE_TYPES = {
|
|||||||
draw_layer: LAYER_TERRAIN,
|
draw_layer: LAYER_TERRAIN,
|
||||||
thin_walls: new Set(['south', 'west']),
|
thin_walls: new Set(['south', 'west']),
|
||||||
slide_mode: 'ice',
|
slide_mode: 'ice',
|
||||||
|
blocks_leaving: blocks_leaving_thin_walls,
|
||||||
on_arrive(me, level, other) {
|
on_arrive(me, level, other) {
|
||||||
if (other.direction === 'south') {
|
if (other.direction === 'south') {
|
||||||
level.set_actor_direction(other, 'east');
|
level.set_actor_direction(other, 'east');
|
||||||
@ -463,6 +469,7 @@ const TILE_TYPES = {
|
|||||||
draw_layer: LAYER_TERRAIN,
|
draw_layer: LAYER_TERRAIN,
|
||||||
thin_walls: new Set(['north', 'west']),
|
thin_walls: new Set(['north', 'west']),
|
||||||
slide_mode: 'ice',
|
slide_mode: 'ice',
|
||||||
|
blocks_leaving: blocks_leaving_thin_walls,
|
||||||
on_arrive(me, level, other) {
|
on_arrive(me, level, other) {
|
||||||
if (other.direction === 'north') {
|
if (other.direction === 'north') {
|
||||||
level.set_actor_direction(other, 'east');
|
level.set_actor_direction(other, 'east');
|
||||||
@ -476,6 +483,7 @@ const TILE_TYPES = {
|
|||||||
draw_layer: LAYER_TERRAIN,
|
draw_layer: LAYER_TERRAIN,
|
||||||
thin_walls: new Set(['north', 'east']),
|
thin_walls: new Set(['north', 'east']),
|
||||||
slide_mode: 'ice',
|
slide_mode: 'ice',
|
||||||
|
blocks_leaving: blocks_leaving_thin_walls,
|
||||||
on_arrive(me, level, other) {
|
on_arrive(me, level, other) {
|
||||||
if (other.direction === 'north') {
|
if (other.direction === 'north') {
|
||||||
level.set_actor_direction(other, 'west');
|
level.set_actor_direction(other, 'west');
|
||||||
@ -489,6 +497,7 @@ const TILE_TYPES = {
|
|||||||
draw_layer: LAYER_TERRAIN,
|
draw_layer: LAYER_TERRAIN,
|
||||||
thin_walls: new Set(['south', 'east']),
|
thin_walls: new Set(['south', 'east']),
|
||||||
slide_mode: 'ice',
|
slide_mode: 'ice',
|
||||||
|
blocks_leaving: blocks_leaving_thin_walls,
|
||||||
on_arrive(me, level, other) {
|
on_arrive(me, level, other) {
|
||||||
if (other.direction === 'south') {
|
if (other.direction === 'south') {
|
||||||
level.set_actor_direction(other, 'west');
|
level.set_actor_direction(other, 'west');
|
||||||
@ -718,30 +727,30 @@ const TILE_TYPES = {
|
|||||||
draw_layer: LAYER_TERRAIN,
|
draw_layer: LAYER_TERRAIN,
|
||||||
// TODO not the case for an empty one in cc2, apparently
|
// TODO not the case for an empty one in cc2, apparently
|
||||||
blocks_all: true,
|
blocks_all: true,
|
||||||
|
traps(me, actor) {
|
||||||
|
return ! actor._clone_release;
|
||||||
|
},
|
||||||
activate(me, level) {
|
activate(me, level) {
|
||||||
let cell = me.cell;
|
let actor = me.cell.get_actor();
|
||||||
// Copy, so we don't end up repeatedly cloning the same object
|
if (! actor)
|
||||||
for (let tile of Array.from(cell)) {
|
return;
|
||||||
if (tile !== me && tile.type.is_actor) {
|
|
||||||
// Copy this stuff in case the movement changes it
|
|
||||||
let type = tile.type;
|
|
||||||
let direction = tile.direction;
|
|
||||||
|
|
||||||
// Unstick and try to move the actor; if it's blocked,
|
// Copy this stuff in case the movement changes it
|
||||||
// abort the clone
|
// TODO should anything else be preserved?
|
||||||
level.set_actor_stuck(tile, false);
|
let type = actor.type;
|
||||||
if (level.attempt_step(tile, direction)) {
|
let direction = actor.direction;
|
||||||
level.add_actor(tile);
|
|
||||||
|
// 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
|
// FIXME add this underneath, just above the cloner
|
||||||
let new_tile = new tile.constructor(type, direction);
|
let new_template = new actor.constructor(type, direction);
|
||||||
level.add_tile(new_tile, cell);
|
level.add_tile(new_template, me.cell);
|
||||||
level.set_actor_stuck(new_tile, true);
|
level.add_actor(new_template);
|
||||||
}
|
|
||||||
else {
|
|
||||||
level.set_actor_stuck(tile, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
delete actor._clone_release;
|
||||||
},
|
},
|
||||||
// Also clones on rising pulse or gray button
|
// Also clones on rising pulse or gray button
|
||||||
on_power(me, level) {
|
on_power(me, level) {
|
||||||
@ -753,45 +762,17 @@ const TILE_TYPES = {
|
|||||||
},
|
},
|
||||||
trap: {
|
trap: {
|
||||||
draw_layer: LAYER_TERRAIN,
|
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) {
|
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;
|
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) {
|
add_press(me, level) {
|
||||||
level._set_tile_prop(me, 'presses', (me.presses ?? 0) + 1);
|
level._set_tile_prop(me, 'presses', (me.presses ?? 0) + 1);
|
||||||
if (me.presses === 1) {
|
if (me.presses === 1) {
|
||||||
// Free everything on us, if we went from 0 to 1 presses (i.e. closed to open)
|
// 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)) {
|
for (let tile of Array.from(me.cell)) {
|
||||||
if (tile.type.is_actor) {
|
if (tile.type.is_actor) {
|
||||||
level.set_actor_stuck(tile, false);
|
|
||||||
// Forcibly move anything released from a trap, to keep it in sync with
|
// Forcibly move anything released from a trap, to keep it in sync with
|
||||||
// whatever pushed the button
|
// whatever pushed the button
|
||||||
level.attempt_step(tile, tile.direction);
|
level.attempt_step(tile, tile.direction);
|
||||||
@ -801,23 +782,16 @@ const TILE_TYPES = {
|
|||||||
},
|
},
|
||||||
remove_press(me, level) {
|
remove_press(me, level) {
|
||||||
level._set_tile_prop(me, 'presses', me.presses - 1);
|
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)
|
traps(me, actor) {
|
||||||
for (let tile of me.cell) {
|
return ! me.presses;
|
||||||
if (tile.type.is_actor) {
|
|
||||||
level.set_actor_stuck(tile, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
on_power(me, level) {
|
on_power(me, level) {
|
||||||
// Treat being powered or not as an extra kind of brown button press
|
// Treat being powered or not as an extra kind of brown button press
|
||||||
me.type.add_press(me, level);
|
me.type.add_press(me, level);
|
||||||
console.log("powering UP", me.presses);
|
|
||||||
},
|
},
|
||||||
on_depower(me, level) {
|
on_depower(me, level) {
|
||||||
me.type.remove_press(me, level);
|
me.type.remove_press(me, level);
|
||||||
console.log("powering down...", me.presses);
|
|
||||||
},
|
},
|
||||||
visual_state(me) {
|
visual_state(me) {
|
||||||
if (me && me.presses) {
|
if (me && me.presses) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user