Add explicit support in the level for sokoban buttons
Gets a lot of junk out of the sokoban buttons' implementations. Also, undo closures are gone now!
This commit is contained in:
parent
3c7b8948ae
commit
626d146375
76
js/game.js
76
js/game.js
@ -267,8 +267,8 @@ export class Cell extends Array {
|
|||||||
|
|
||||||
class UndoEntry {
|
class UndoEntry {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.misc_closures = [];
|
|
||||||
this.tile_changes = new Map;
|
this.tile_changes = new Map;
|
||||||
|
this.sokoban_changes = null;
|
||||||
this.level_props = {};
|
this.level_props = {};
|
||||||
this.actor_splices = [];
|
this.actor_splices = [];
|
||||||
this.toggle_green_tiles = false;
|
this.toggle_green_tiles = false;
|
||||||
@ -283,6 +283,15 @@ class UndoEntry {
|
|||||||
}
|
}
|
||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
preserve_sokoban(color, count) {
|
||||||
|
if (! this.sokoban_changes) {
|
||||||
|
this.sokoban_changes = {};
|
||||||
|
}
|
||||||
|
if (! (color in this.sokoban_changes)) {
|
||||||
|
this.sokoban_changes[color] = count;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -417,7 +426,8 @@ export class Level extends LevelInterface {
|
|||||||
let yellow_teleporter_count = 0;
|
let yellow_teleporter_count = 0;
|
||||||
this.allow_taking_yellow_teleporters = false;
|
this.allow_taking_yellow_teleporters = false;
|
||||||
// Sokoban buttons function as a group
|
// Sokoban buttons function as a group
|
||||||
this.sokoban_buttons_unpressed = {};
|
this.sokoban_unpressed = { red: 0, blue: 0, yellow: 0, green: 0 };
|
||||||
|
this.sokoban_satisfied = { red: true, blue: true, yellow: true, green: true };
|
||||||
for (let y = 0; y < this.height; y++) {
|
for (let y = 0; y < this.height; y++) {
|
||||||
let row = [];
|
let row = [];
|
||||||
for (let x = 0; x < this.width; x++) {
|
for (let x = 0; x < this.width; x++) {
|
||||||
@ -426,7 +436,7 @@ export class Level extends LevelInterface {
|
|||||||
this.linear_cells.push(cell);
|
this.linear_cells.push(cell);
|
||||||
|
|
||||||
let stored_cell = this.stored_level.linear_cells[n];
|
let stored_cell = this.stored_level.linear_cells[n];
|
||||||
n++;
|
n += 1;
|
||||||
for (let template_tile of stored_cell) {
|
for (let template_tile of stored_cell) {
|
||||||
if (! template_tile)
|
if (! template_tile)
|
||||||
continue;
|
continue;
|
||||||
@ -444,7 +454,7 @@ export class Level extends LevelInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tile.type.is_required_chip && this.stored_level.chips_required === null) {
|
if (tile.type.is_required_chip && this.stored_level.chips_required === null) {
|
||||||
this.chips_remaining++;
|
this.chips_remaining += 1;
|
||||||
}
|
}
|
||||||
if (tile.type.is_actor) {
|
if (tile.type.is_actor) {
|
||||||
this.actors.push(tile);
|
this.actors.push(tile);
|
||||||
@ -466,8 +476,8 @@ export class Level extends LevelInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (tile.type.name === 'sokoban_button') {
|
else if (tile.type.name === 'sokoban_button') {
|
||||||
this.sokoban_buttons_unpressed[tile.color] =
|
this.sokoban_unpressed[tile.color] += 1;
|
||||||
(this.sokoban_buttons_unpressed[tile.color] ?? 0) + 1;
|
this.sokoban_satisfied[tile.color] = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1302,6 +1312,8 @@ export class Level extends LevelInterface {
|
|||||||
this.pending_undo.toggle_green_tiles = true;
|
this.pending_undo.toggle_green_tiles = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.__check_sokoban_buttons();
|
||||||
}
|
}
|
||||||
|
|
||||||
__toggle_green_tiles() {
|
__toggle_green_tiles() {
|
||||||
@ -1322,6 +1334,27 @@ export class Level extends LevelInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for changes to sokoban buttons, and swap the appropriate floors/walls if necessary.
|
||||||
|
// NOT undo-safe; this is undone by calling it again after an undo.
|
||||||
|
__check_sokoban_buttons() {
|
||||||
|
for (let [color, was_satisfied] of Object.entries(this.sokoban_satisfied)) {
|
||||||
|
let is_satisfied = this.sokoban_unpressed[color] === 0;
|
||||||
|
if (was_satisfied !== is_satisfied) {
|
||||||
|
this.sokoban_satisfied[color] = is_satisfied;
|
||||||
|
let new_type = TILE_TYPES[is_satisfied ? 'sokoban_floor' : 'sokoban_wall'];
|
||||||
|
console.log(color, this.sokoban_unpressed[color], was_satisfied, is_satisfied, new_type);
|
||||||
|
for (let cell of this.linear_cells) {
|
||||||
|
let terrain = cell.get_terrain();
|
||||||
|
if ((terrain.type.name === 'sokoban_wall' || terrain.type.name === 'sokoban_floor') &&
|
||||||
|
terrain.color === color)
|
||||||
|
{
|
||||||
|
terrain.type = new_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_do_cleanup_phase() {
|
_do_cleanup_phase() {
|
||||||
// Lynx compat: Any blue tank that still has the reversal flag set here, but is in motion,
|
// Lynx compat: Any blue tank that still has the reversal flag set here, but is in motion,
|
||||||
// should ignore it. Unfortunately this has to be done as its own pass (as it is in Lynx!)
|
// should ignore it. Unfortunately this has to be done as its own pass (as it is in Lynx!)
|
||||||
@ -2542,14 +2575,13 @@ export class Level extends LevelInterface {
|
|||||||
console.log(entry);
|
console.log(entry);
|
||||||
|
|
||||||
// Undo in reverse order! There's no redo, so it's okay to use the destructive reverse().
|
// Undo in reverse order! There's no redo, so it's okay to use the destructive reverse().
|
||||||
// Green toggle goes first, since it's the last thing to happen in a tic
|
// These toggles go first, since they're the last things to happen in a tic
|
||||||
if (entry.pending_green_toggle) {
|
if (entry.pending_green_toggle) {
|
||||||
this.__toggle_green_tiles();
|
this.__toggle_green_tiles();
|
||||||
}
|
}
|
||||||
|
if (entry.sokoban_changes) {
|
||||||
entry.misc_closures.reverse();
|
Object.assign(this.sokoban_unpressed, entry.sokoban_changes);
|
||||||
for (let closure of entry.misc_closures) {
|
this.__check_sokoban_buttons();
|
||||||
closure();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.actor_splices.reverse();
|
entry.actor_splices.reverse();
|
||||||
@ -2588,12 +2620,6 @@ export class Level extends LevelInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_push_pending_undo(thunk) {
|
|
||||||
if (this.undo_enabled) {
|
|
||||||
this.pending_undo.misc_closures.push(thunk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Level alteration -------------------------------------------------------------------------------
|
// Level alteration -------------------------------------------------------------------------------
|
||||||
// EVERYTHING that changes the state of a level, including the state of a single tile, should do
|
// EVERYTHING that changes the state of a level, including the state of a single tile, should do
|
||||||
// it through one of these for undo/rewind purposes
|
// it through one of these for undo/rewind purposes
|
||||||
@ -2683,6 +2709,22 @@ export class Level extends LevelInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
press_sokoban(color) {
|
||||||
|
if (this.undo_enabled) {
|
||||||
|
this.pending_undo.preserve_sokoban(color, this.sokoban_unpressed[color]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sokoban_unpressed[color] -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
unpress_sokoban(color) {
|
||||||
|
if (this.undo_enabled) {
|
||||||
|
this.pending_undo.preserve_sokoban(color, this.sokoban_unpressed[color]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sokoban_unpressed[color] += 1;
|
||||||
|
}
|
||||||
|
|
||||||
kill_actor(actor, killer, animation_name = null, sfx = null, fail_reason = null) {
|
kill_actor(actor, killer, animation_name = null, sfx = null, fail_reason = null) {
|
||||||
if (actor.type.is_real_player) {
|
if (actor.type.is_real_player) {
|
||||||
// Resurrect using the ankh tile, if possible
|
// Resurrect using the ankh tile, if possible
|
||||||
|
|||||||
@ -1570,54 +1570,26 @@ const TILE_TYPES = {
|
|||||||
if (actor && ! (actor.type.name === 'sokoban_block' && actor.color !== me.color)) {
|
if (actor && ! (actor.type.name === 'sokoban_block' && actor.color !== me.color)) {
|
||||||
// Already held down, make sure the level knows
|
// Already held down, make sure the level knows
|
||||||
me.pressed = true;
|
me.pressed = true;
|
||||||
level.sokoban_buttons_unpressed[me.color] -= 1;
|
level.press_sokoban(me.color);
|
||||||
if (level.sokoban_buttons_unpressed[me.color] === 0) {
|
|
||||||
for (let cell of level.linear_cells) {
|
|
||||||
let terrain = cell.get_terrain();
|
|
||||||
if (terrain.type.name === 'sokoban_wall' && terrain.color === me.color) {
|
|
||||||
terrain.type = TILE_TYPES['sokoban_floor'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
on_arrive(me, level, other) {
|
on_arrive(me, level, other) {
|
||||||
|
if (me.pressed)
|
||||||
|
return;
|
||||||
if (other.type.name === 'sokoban_block' && me.color !== other.color)
|
if (other.type.name === 'sokoban_block' && me.color !== other.color)
|
||||||
return;
|
return;
|
||||||
level._set_tile_prop(me, 'pressed', true);
|
level._set_tile_prop(me, 'pressed', true);
|
||||||
level.sfx.play_once('button-press', me.cell);
|
level.sfx.play_once('button-press', me.cell);
|
||||||
|
|
||||||
level.sokoban_buttons_unpressed[me.color] -= 1;
|
level.press_sokoban(me.color);
|
||||||
level._push_pending_undo(() => {
|
|
||||||
level.sokoban_buttons_unpressed[me.color] += 1;
|
|
||||||
});
|
|
||||||
if (level.sokoban_buttons_unpressed[me.color] === 0) {
|
|
||||||
for (let cell of level.linear_cells) {
|
|
||||||
let terrain = cell.get_terrain();
|
|
||||||
if (terrain.type.name === 'sokoban_wall' && terrain.color === me.color) {
|
|
||||||
level.transmute_tile(terrain, 'sokoban_floor');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
on_depart(me, level, other) {
|
on_depart(me, level, other) {
|
||||||
level._set_tile_prop(me, 'pressed', false);
|
if (! me.pressed)
|
||||||
if (other.type.name === 'sokoban_block' && me.color !== other.color)
|
|
||||||
return;
|
return;
|
||||||
|
level._set_tile_prop(me, 'pressed', false);
|
||||||
level.sfx.play_once('button-release', me.cell);
|
level.sfx.play_once('button-release', me.cell);
|
||||||
|
|
||||||
level.sokoban_buttons_unpressed[me.color] += 1;
|
level.unpress_sokoban(me.color);
|
||||||
level._push_pending_undo(() => {
|
|
||||||
level.sokoban_buttons_unpressed[me.color] -= 1;
|
|
||||||
});
|
|
||||||
if (level.sokoban_buttons_unpressed[me.color] === 1) {
|
|
||||||
for (let cell of level.linear_cells) {
|
|
||||||
let terrain = cell.get_terrain();
|
|
||||||
if (terrain.type.name === 'sokoban_floor' && terrain.color === me.color) {
|
|
||||||
level.transmute_tile(terrain, 'sokoban_wall');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
visual_state(me) {
|
visual_state(me) {
|
||||||
return (me.color ?? 'red') + '_' + (me.pressed ? 'pressed' : 'released');
|
return (me.color ?? 'red') + '_' + (me.pressed ? 'pressed' : 'released');
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user