runtime circuit updates - Circuit Block works once more
This commit is contained in:
parent
dddde89b03
commit
4097aa6e84
@ -16,10 +16,17 @@ export function trace_floor_circuit(level, start_cell, start_edge, on_wire, on_d
|
|||||||
if (seen_edges & edgeinfo.bit)
|
if (seen_edges & edgeinfo.bit)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
let actor = cell.get_actor();
|
||||||
|
let wire_directions = terrain.wire_directions;
|
||||||
|
if (actor?.wire_directions ?? null !== null)
|
||||||
|
{
|
||||||
|
wire_directions = actor.wire_directions;
|
||||||
|
}
|
||||||
|
|
||||||
// The wire comes in from this edge towards the center; see how it connects within this
|
// The wire comes in from this edge towards the center; see how it connects within this
|
||||||
// cell, then check for any neighbors
|
// cell, then check for any neighbors
|
||||||
let connections = edgeinfo.bit;
|
let connections = edgeinfo.bit;
|
||||||
if (! is_first && ((terrain.wire_directions ?? 0) & edgeinfo.bit) === 0) {
|
if (! is_first && ((wire_directions ?? 0) & edgeinfo.bit) === 0) {
|
||||||
// There's not actually a wire here (but not if this is our starting cell, in which
|
// There's not actually a wire here (but not if this is our starting cell, in which
|
||||||
// case we trust the caller)
|
// case we trust the caller)
|
||||||
if (on_dead_end) {
|
if (on_dead_end) {
|
||||||
@ -31,16 +38,16 @@ export function trace_floor_circuit(level, start_cell, start_edge, on_wire, on_d
|
|||||||
// The wires in this tile never connect to each other
|
// The wires in this tile never connect to each other
|
||||||
}
|
}
|
||||||
else if (terrain.type.wire_propagation_mode === 'cross' ||
|
else if (terrain.type.wire_propagation_mode === 'cross' ||
|
||||||
(terrain.wire_directions === 0x0f && terrain.type.wire_propagation_mode !== 'all'))
|
(wire_directions === 0x0f && terrain.type.wire_propagation_mode !== 'all'))
|
||||||
{
|
{
|
||||||
// This is a cross pattern, so only opposite edges connect
|
// This is a cross pattern, so only opposite edges connect
|
||||||
if (terrain.wire_directions & edgeinfo.opposite_bit) {
|
if (wire_directions & edgeinfo.opposite_bit) {
|
||||||
connections |= edgeinfo.opposite_bit;
|
connections |= edgeinfo.opposite_bit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Everything connects
|
// Everything connects
|
||||||
connections |= terrain.wire_directions;
|
connections |= wire_directions;
|
||||||
}
|
}
|
||||||
|
|
||||||
seen_cells.set(cell, seen_edges | connections);
|
seen_cells.set(cell, seen_edges | connections);
|
||||||
|
|||||||
269
js/game.js
269
js/game.js
@ -529,123 +529,7 @@ export class Level extends LevelInterface {
|
|||||||
this.connect_button(connectable);
|
this.connect_button(connectable);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build circuits out of connected wires
|
this.recalculate_circuitry(true);
|
||||||
// TODO document this idea
|
|
||||||
this.circuits = [];
|
|
||||||
this.power_sources = [];
|
|
||||||
let wired_outputs = new Set;
|
|
||||||
this.wired_outputs = [];
|
|
||||||
let add_to_edge_map = (map, item, edges) => {
|
|
||||||
map.set(item, (map.get(item) ?? 0) | edges);
|
|
||||||
};
|
|
||||||
for (let cell of this.linear_cells) {
|
|
||||||
// We're interested in static circuitry, which means terrain
|
|
||||||
let terrain = cell.get_terrain();
|
|
||||||
if (! terrain) // ?!
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (terrain.type.is_power_source) {
|
|
||||||
this.power_sources.push(terrain);
|
|
||||||
}
|
|
||||||
|
|
||||||
let wire_directions = terrain.wire_directions;
|
|
||||||
if (! wire_directions && ! terrain.wire_tunnel_directions) {
|
|
||||||
// No wires, not interesting... unless it's a logic gate, which defines its own
|
|
||||||
// wires! We only care about outgoing ones here, on the off chance that they point
|
|
||||||
// directly into a non-wired tile, in which case a wire scan won't find them
|
|
||||||
if (terrain.type.name === 'logic_gate') {
|
|
||||||
let dir = terrain.direction;
|
|
||||||
let cxns = terrain.type._gate_types[terrain.gate_type];
|
|
||||||
if (! cxns) {
|
|
||||||
// Voodoo tile
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (let i = 0; i < 4; i++) {
|
|
||||||
let cxn = cxns[i];
|
|
||||||
if (cxn && cxn.match(/^out/)) {
|
|
||||||
wire_directions |= DIRECTIONS[dir].bit;
|
|
||||||
}
|
|
||||||
dir = DIRECTIONS[dir].right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let [direction, dirinfo] of Object.entries(DIRECTIONS)) {
|
|
||||||
if (! ((wire_directions | terrain.wire_tunnel_directions) & dirinfo.bit))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (terrain.circuits && terrain.circuits[dirinfo.index])
|
|
||||||
continue;
|
|
||||||
|
|
||||||
let circuit = {
|
|
||||||
is_powered: false,
|
|
||||||
tiles: new Map,
|
|
||||||
inputs: new Map,
|
|
||||||
};
|
|
||||||
this.circuits.push(circuit);
|
|
||||||
// At last, a wired cell edge we have not yet handled. Floodfill from here
|
|
||||||
algorithms.trace_floor_circuit(
|
|
||||||
this, terrain.cell, direction,
|
|
||||||
// Wire handling
|
|
||||||
(tile, edges) => {
|
|
||||||
if (! tile.circuits) {
|
|
||||||
tile.circuits = [null, null, null, null];
|
|
||||||
}
|
|
||||||
for (let [direction, dirinfo] of Object.entries(DIRECTIONS)) {
|
|
||||||
if (edges & dirinfo.bit) {
|
|
||||||
tile.circuits[dirinfo.index] = circuit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
add_to_edge_map(circuit.tiles, tile, edges);
|
|
||||||
if (tile.type.on_power) {
|
|
||||||
// Red teleporters contain wires and /also/ have an on_power
|
|
||||||
// FIXME this isn't quite right since there's seemingly a 1-frame delay
|
|
||||||
wired_outputs.add(tile);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tile.type.is_power_source) {
|
|
||||||
// TODO could just do this in a pass afterwards
|
|
||||||
add_to_edge_map(circuit.inputs, tile, edges);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Dead end handling (potentially logic gates, etc.)
|
|
||||||
(cell, edge) => {
|
|
||||||
for (let tile of cell) {
|
|
||||||
if (! tile) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if (tile.type.name === 'logic_gate') {
|
|
||||||
// Logic gates are the one non-wired tile that get attached to circuits,
|
|
||||||
// mostly so blue teleporters can follow them
|
|
||||||
if (! tile.circuits) {
|
|
||||||
tile.circuits = [null, null, null, null];
|
|
||||||
}
|
|
||||||
tile.circuits[DIRECTIONS[edge].index] = circuit;
|
|
||||||
|
|
||||||
let wire = tile.type._gate_types[tile.gate_type][
|
|
||||||
(DIRECTIONS[edge].index - DIRECTIONS[tile.direction].index + 4) % 4];
|
|
||||||
if (! wire)
|
|
||||||
return;
|
|
||||||
add_to_edge_map(circuit.tiles, tile, DIRECTIONS[edge].bit);
|
|
||||||
if (wire.match(/^out/)) {
|
|
||||||
add_to_edge_map(circuit.inputs, tile, DIRECTIONS[edge].bit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (tile.type.on_power) {
|
|
||||||
// FIXME this isn't quite right since there's seemingly a 1-frame delay
|
|
||||||
add_to_edge_map(circuit.tiles, tile, DIRECTIONS[edge].bit);
|
|
||||||
wired_outputs.add(tile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.wired_outputs = Array.from(wired_outputs);
|
|
||||||
this.wired_outputs.sort((a, b) => this.coords_to_scalar(a.cell.x, a.cell.y) - this.coords_to_scalar(b.cell.x, b.cell.y));
|
|
||||||
|
|
||||||
// Finally, let all tiles do custom init behavior... but backwards, to match actor order
|
// Finally, let all tiles do custom init behavior... but backwards, to match actor order
|
||||||
for (let i = this.linear_cells.length - 1; i >= 0; i--) {
|
for (let i = this.linear_cells.length - 1; i >= 0; i--) {
|
||||||
@ -748,6 +632,142 @@ export class Level extends LevelInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
recalculate_circuitry(first_time = false) {
|
||||||
|
// Build circuits out of connected wires
|
||||||
|
// TODO document this idea
|
||||||
|
|
||||||
|
if (!first_time) {
|
||||||
|
for (let circuit of this.circuits) {
|
||||||
|
for (let tile of circuit.tiles) {
|
||||||
|
tile[0].circuits = [null, null, null, null];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.circuits = [];
|
||||||
|
this.power_sources = [];
|
||||||
|
let wired_outputs = new Set;
|
||||||
|
this.wired_outputs = [];
|
||||||
|
let add_to_edge_map = (map, item, edges) => {
|
||||||
|
map.set(item, (map.get(item) ?? 0) | edges);
|
||||||
|
};
|
||||||
|
for (let cell of this.linear_cells) {
|
||||||
|
// We're interested in static circuitry, which means terrain
|
||||||
|
// OR circuit blocks on top
|
||||||
|
let terrain = cell.get_terrain();
|
||||||
|
if (! terrain) // ?!
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (terrain.type.is_power_source) {
|
||||||
|
this.power_sources.push(terrain);
|
||||||
|
}
|
||||||
|
|
||||||
|
let actor = cell.get_actor();
|
||||||
|
let wire_directions = terrain.wire_directions;
|
||||||
|
if (actor?.wire_directions ?? null !== null)
|
||||||
|
{
|
||||||
|
wire_directions = actor.wire_directions;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! wire_directions && ! terrain.wire_tunnel_directions) {
|
||||||
|
// No wires, not interesting... unless it's a logic gate, which defines its own
|
||||||
|
// wires! We only care about outgoing ones here, on the off chance that they point
|
||||||
|
// directly into a non-wired tile, in which case a wire scan won't find them
|
||||||
|
if (terrain.type.name === 'logic_gate') {
|
||||||
|
let dir = terrain.direction;
|
||||||
|
let cxns = terrain.type._gate_types[terrain.gate_type];
|
||||||
|
if (! cxns) {
|
||||||
|
// Voodoo tile
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
let cxn = cxns[i];
|
||||||
|
if (cxn && cxn.match(/^out/)) {
|
||||||
|
wire_directions |= DIRECTIONS[dir].bit;
|
||||||
|
}
|
||||||
|
dir = DIRECTIONS[dir].right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let [direction, dirinfo] of Object.entries(DIRECTIONS)) {
|
||||||
|
if (! ((wire_directions | terrain.wire_tunnel_directions) & dirinfo.bit))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (terrain.circuits && terrain.circuits[dirinfo.index])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
let circuit = {
|
||||||
|
is_powered: false,
|
||||||
|
tiles: new Map,
|
||||||
|
inputs: new Map,
|
||||||
|
};
|
||||||
|
this.circuits.push(circuit);
|
||||||
|
// At last, a wired cell edge we have not yet handled. Floodfill from here
|
||||||
|
algorithms.trace_floor_circuit(
|
||||||
|
this, terrain.cell, direction,
|
||||||
|
// Wire handling
|
||||||
|
(tile, edges) => {
|
||||||
|
if (! tile.circuits) {
|
||||||
|
tile.circuits = [null, null, null, null];
|
||||||
|
}
|
||||||
|
for (let [direction, dirinfo] of Object.entries(DIRECTIONS)) {
|
||||||
|
if (edges & dirinfo.bit) {
|
||||||
|
tile.circuits[dirinfo.index] = circuit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_to_edge_map(circuit.tiles, tile, edges);
|
||||||
|
if (tile.type.on_power) {
|
||||||
|
// Red teleporters contain wires and /also/ have an on_power
|
||||||
|
// FIXME this isn't quite right since there's seemingly a 1-frame delay
|
||||||
|
wired_outputs.add(tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tile.type.is_power_source) {
|
||||||
|
// TODO could just do this in a pass afterwards
|
||||||
|
add_to_edge_map(circuit.inputs, tile, edges);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Dead end handling (potentially logic gates, etc.)
|
||||||
|
(cell, edge) => {
|
||||||
|
for (let tile of cell) {
|
||||||
|
if (! tile) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (tile.type.name === 'logic_gate') {
|
||||||
|
// Logic gates are the one non-wired tile that get attached to circuits,
|
||||||
|
// mostly so blue teleporters can follow them
|
||||||
|
if (! tile.circuits) {
|
||||||
|
tile.circuits = [null, null, null, null];
|
||||||
|
}
|
||||||
|
tile.circuits[DIRECTIONS[edge].index] = circuit;
|
||||||
|
|
||||||
|
let wire = tile.type._gate_types[tile.gate_type][
|
||||||
|
(DIRECTIONS[edge].index - DIRECTIONS[tile.direction].index + 4) % 4];
|
||||||
|
if (! wire)
|
||||||
|
return;
|
||||||
|
add_to_edge_map(circuit.tiles, tile, DIRECTIONS[edge].bit);
|
||||||
|
if (wire.match(/^out/)) {
|
||||||
|
add_to_edge_map(circuit.inputs, tile, DIRECTIONS[edge].bit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (tile.type.on_power) {
|
||||||
|
// FIXME this isn't quite right since there's seemingly a 1-frame delay
|
||||||
|
add_to_edge_map(circuit.tiles, tile, DIRECTIONS[edge].bit);
|
||||||
|
wired_outputs.add(tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.wired_outputs = Array.from(wired_outputs);
|
||||||
|
this.wired_outputs.sort((a, b) => this.coords_to_scalar(a.cell.x, a.cell.y) - this.coords_to_scalar(b.cell.x, b.cell.y));
|
||||||
|
}
|
||||||
|
|
||||||
can_accept_input() {
|
can_accept_input() {
|
||||||
// We can accept input anytime the player can move, i.e. when they're not already moving and
|
// We can accept input anytime the player can move, i.e. when they're not already moving and
|
||||||
// not in an un-overrideable slide.
|
// not in an un-overrideable slide.
|
||||||
@ -1703,6 +1723,10 @@ export class Level extends LevelInterface {
|
|||||||
|
|
||||||
// Step on every tile in a cell we just arrived in
|
// Step on every tile in a cell we just arrived in
|
||||||
step_on_cell(actor, cell) {
|
step_on_cell(actor, cell) {
|
||||||
|
if (actor.type.on_finishing_move) {
|
||||||
|
actor.type.on_finishing_move(actor, this);
|
||||||
|
}
|
||||||
|
|
||||||
// Step on topmost things first -- notably, it's safe to step on water with flippers on top
|
// Step on topmost things first -- notably, it's safe to step on water with flippers on top
|
||||||
// TODO is there a custom order here similar to collision checking?
|
// TODO is there a custom order here similar to collision checking?
|
||||||
for (let layer = LAYERS.MAX - 1; layer >= 0; layer--) {
|
for (let layer = LAYERS.MAX - 1; layer >= 0; layer--) {
|
||||||
@ -2526,7 +2550,16 @@ export class Level extends LevelInterface {
|
|||||||
new_type.on_begin(tile, this);
|
new_type.on_begin(tile, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: update circuit networks?
|
//recalculate circuitry
|
||||||
|
//TODO: this does weird things with on/off switches, seemingly because of the 1 frame delay.
|
||||||
|
//anyway, we don't want to recalculate_circuitry just because the tile has wire directions now, unless they're different from what they used to be. but that seems unlikely unless on_begin added them...
|
||||||
|
//but we'd like transmuting into an on/off switch to work as expected, so I should look into it sometime
|
||||||
|
if (/*tile.wire_directions ||*/
|
||||||
|
(new_type.is_power_source && (new_type.is_power_source !== old_type.is_power_source)) ||
|
||||||
|
(new_type.wire_propagation_mode && (new_type.wire_propagation_mode !== old_type.wire_propagation_mode)))
|
||||||
|
{
|
||||||
|
this.recalculate_circuitry();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -837,6 +837,7 @@ const TILE_TYPES = {
|
|||||||
else if (other.type.name === 'circuit_block') {
|
else if (other.type.name === 'circuit_block') {
|
||||||
level.transmute_tile(me, 'floor');
|
level.transmute_tile(me, 'floor');
|
||||||
me.wire_directions = other.wire_directions;
|
me.wire_directions = other.wire_directions;
|
||||||
|
level.recalculate_circuitry();
|
||||||
level.transmute_tile(other, 'splash');
|
level.transmute_tile(other, 'splash');
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -2428,7 +2429,10 @@ const TILE_TYPES = {
|
|||||||
movement_speed: 4,
|
movement_speed: 4,
|
||||||
on_clone(me, original) {
|
on_clone(me, original) {
|
||||||
me.wire_directions = original.wire_directions;
|
me.wire_directions = original.wire_directions;
|
||||||
}
|
},
|
||||||
|
on_finishing_move(me, level) {
|
||||||
|
level.recalculate_circuitry();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// Time alteration
|
// Time alteration
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user