Simplify wire phase undo

Turns out we don't really need to store these changes to tile power at
all; they can be rederived from circuit power.

One more undo closure gone.
This commit is contained in:
Eevee (Evelyn Woods) 2024-05-06 13:37:34 -06:00
parent b891d6f38c
commit d1f0ac4956
2 changed files with 36 additions and 46 deletions

View File

@ -272,6 +272,7 @@ class UndoEntry {
this.level_props = {}; this.level_props = {};
this.actor_splices = []; this.actor_splices = [];
this.toggle_green_tiles = false; this.toggle_green_tiles = false;
this.circuit_power_changes = null;
} }
tile_changes_for(tile) { tile_changes_for(tile) {
@ -2347,32 +2348,6 @@ export class Level extends LevelInterface {
if (this.circuits.length === 0) if (this.circuits.length === 0)
return; return;
// Prepare a big slab of undo. The only thing we directly change here (aside from
// emitting_edges, a normal tile property) is Tile.powered_edges, which tends to change for
// large numbers of tiles at a time, so store it all in one map and undo it in one shot.
let powered_edges_changes = new Map;
let _set_edges = (tile, new_edges) => {
if (this.undo_enabled) {
if (powered_edges_changes.has(tile)) {
if (powered_edges_changes.get(tile) === new_edges) {
powered_edges_changes.delete(tile);
}
}
else {
powered_edges_changes.set(tile, tile.powered_edges);
}
}
tile.powered_edges = new_edges;
};
let power_edges = (tile, edges) => {
let new_edges = tile.powered_edges | edges;
_set_edges(tile, new_edges);
};
let depower_edges = (tile, edges) => {
let new_edges = tile.powered_edges & ~edges;
_set_edges(tile, new_edges);
};
// Update the state of any tiles that can generate power. If none of them changed since // Update the state of any tiles that can generate power. If none of them changed since
// last wiring update, stop here. First, static power sources. // last wiring update, stop here. First, static power sources.
let any_changed = false; let any_changed = false;
@ -2397,7 +2372,7 @@ export class Level extends LevelInterface {
let emitting = 0; let emitting = 0;
if (actor.movement_cooldown === 0 && actor.has_item('lightning_bolt')) { if (actor.movement_cooldown === 0 && actor.has_item('lightning_bolt')) {
let wired_tile = actor.cell.get_wired_tile(); let wired_tile = actor.cell.get_wired_tile();
if (wired_tile && (wired_tile === actor || wired_tile.type.name === 'floor' || wired_tile.type.name === 'electrified_floor')) { if (wired_tile && (wired_tile === actor || wired_tile.type.can_be_powered_by_actor)) {
emitting = wired_tile.wire_directions; emitting = wired_tile.wire_directions;
for (let circuit of this.cells_to_circuits.get(this.cell_to_scalar(wired_tile.cell))) { for (let circuit of this.cells_to_circuits.get(this.cell_to_scalar(wired_tile.cell))) {
externally_powered_circuits.add(circuit); externally_powered_circuits.add(circuit);
@ -2420,8 +2395,7 @@ export class Level extends LevelInterface {
tile._prev_powered_edges = tile.powered_edges; tile._prev_powered_edges = tile.powered_edges;
} }
// Now go through every circuit, compute whether it's powered, and if that changed, inform // Go through every circuit and recompute whether it's powered
// its outputs
let circuit_changes = new Map; let circuit_changes = new Map;
for (let circuit of this.circuits) { for (let circuit of this.circuits) {
let is_powered = false; let is_powered = false;
@ -2446,17 +2420,11 @@ export class Level extends LevelInterface {
if (this.undo_enabled) { if (this.undo_enabled) {
circuit_changes.set(circuit, was_powered); circuit_changes.set(circuit, was_powered);
} }
for (let [tile, edges] of circuit.tiles.entries()) {
if (is_powered) {
power_edges(tile, edges);
}
else {
depower_edges(tile, edges);
}
}
} }
this.__apply_circuit_power_to_tiles();
// Finally, inform every tile of power changes, if any
for (let tile of this.wired_outputs) { for (let tile of this.wired_outputs) {
if (tile.powered_edges && ! tile._prev_powered_edges && tile.type.on_power) { if (tile.powered_edges && ! tile._prev_powered_edges && tile.type.on_power) {
tile.type.on_power(tile, this); tile.type.on_power(tile, this);
@ -2466,14 +2434,26 @@ export class Level extends LevelInterface {
} }
} }
this._push_pending_undo(() => { if (this.undo_enabled) {
for (let [tile, edges] of powered_edges_changes.entries()) { this.pending_undo.circuit_power_changes = circuit_changes;
tile.powered_edges = edges; }
}
// Recompute which tiles (across the whole level) are powered, based on the power status of the
// circuits. NOT undo-safe; instead, changes to circuit power are undone, and then this is
// called again to make everything consistent. This also does NOT call on_power or on_depower;
// any effects from those are done (and undone) by the caller.
__apply_circuit_power_to_tiles() {
for (let circuit of this.circuits) {
for (let [tile, edges] of circuit.tiles.entries()) {
if (circuit.is_powered) {
tile.powered_edges |= edges;
}
else {
tile.powered_edges &= ~edges;
}
} }
for (let [circuit, is_powered] of circuit_changes.entries()) {
circuit.is_powered = is_powered;
} }
});
} }
// Level inspection ------------------------------------------------------------------------------- // Level inspection -------------------------------------------------------------------------------
@ -2595,6 +2575,14 @@ export class Level extends LevelInterface {
for (let tile of needs_readding) { for (let tile of needs_readding) {
tile.cell._add(tile); tile.cell._add(tile);
} }
if (entry.circuit_power_changes) {
for (let [circuit, is_powered] of entry.circuit_power_changes.entries()) {
circuit.is_powered = is_powered;
}
this.__apply_circuit_power_to_tiles();
}
for (let [key, value] of Object.entries(entry.level_props)) { for (let [key, value] of Object.entries(entry.level_props)) {
this[key] = value; this[key] = value;
} }

View File

@ -278,6 +278,7 @@ const TILE_TYPES = {
layer: LAYERS.terrain, layer: LAYERS.terrain,
contains_wire: true, contains_wire: true,
wire_propagation_mode: 'autocross', wire_propagation_mode: 'autocross',
can_be_powered_by_actor: true,
on_approach(me, level, other) { on_approach(me, level, other) {
if (other.type.name === 'blob' || other.type.name === 'boulder') { if (other.type.name === 'blob' || other.type.name === 'boulder') {
// Blobs spread slime onto floor // Blobs spread slime onto floor
@ -2147,6 +2148,7 @@ const TILE_TYPES = {
electrified_floor: { electrified_floor: {
layer: LAYERS.terrain, layer: LAYERS.terrain,
wire_propagation_mode: 'all', wire_propagation_mode: 'all',
can_be_powered_by_actor: true,
on_begin(me, level) { on_begin(me, level) {
level._set_tile_prop(me, 'is_active', false); level._set_tile_prop(me, 'is_active', false);
level._set_tile_prop(me, 'wire_directions', 15); level._set_tile_prop(me, 'wire_directions', 15);