For example, you can now make connections with the connection tool. Remarkable. Unfortunately, implicit connections aren't updated as you edit the level yet. Also came with some refactors for searching a level and whatnot.
184 lines
6.4 KiB
JavaScript
184 lines
6.4 KiB
JavaScript
import { DIRECTIONS, LAYERS } from './defs.js';
|
|
|
|
// Iterates over every terrain tile in the grid that has one of the given types (a Set of type
|
|
// names), in linear order, optionally in reverse. The starting cell is checked last.
|
|
// Yields [tile, cell].
|
|
export function* find_terrain_linear(levelish, start_cell, type_names, reverse = false) {
|
|
let i = levelish.coords_to_scalar(start_cell.x, start_cell.y);
|
|
while (true) {
|
|
if (reverse) {
|
|
i -= 1;
|
|
if (i < 0) {
|
|
i += levelish.size_x * levelish.size_y;
|
|
}
|
|
}
|
|
else {
|
|
i += 1;
|
|
i %= levelish.size_x * levelish.size_y;
|
|
}
|
|
|
|
let cell = levelish.linear_cells[i];
|
|
let tile = cell[LAYERS.terrain];
|
|
if (tile && type_names.has(tile.type.name)) {
|
|
yield [tile, cell];
|
|
}
|
|
|
|
if (cell === start_cell)
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Iterates over every terrain tile in the grid that has one of the given types (a Set of type
|
|
// names), spreading outward in a diamond pattern. The starting cell is not included.
|
|
// Only used by orange buttons.
|
|
// Yields [tile, cell].
|
|
export function* find_terrain_diamond(levelish, start_cell, type_names) {
|
|
let max_search_radius = (
|
|
Math.max(start_cell.x, levelish.size_x - start_cell.x) +
|
|
Math.max(start_cell.y, levelish.size_y - start_cell.y));
|
|
for (let dist = 1; dist <= max_search_radius; dist++) {
|
|
// Start east and move counterclockwise
|
|
let sx = start_cell.x + dist;
|
|
let sy = start_cell.y;
|
|
for (let direction of [[-1, -1], [-1, 1], [1, 1], [1, -1]]) {
|
|
for (let i = 0; i < dist; i++) {
|
|
let cell = levelish.cell(sx, sy);
|
|
sx += direction[0];
|
|
sy += direction[1];
|
|
|
|
if (! cell)
|
|
continue;
|
|
let terrain = cell[LAYERS.terrain];
|
|
if (type_names.has(terrain.type.name)) {
|
|
yield [terrain, cell];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO make this guy work generically for orange, red, brown buttons? others...?
|
|
export function find_implicit_connection() {
|
|
}
|
|
|
|
export function trace_floor_circuit(level, start_cell, start_edge, on_wire, on_dead_end) {
|
|
let is_first = true;
|
|
let pending = [[start_cell, start_edge]];
|
|
let seen_cells = new Map;
|
|
while (pending.length > 0) {
|
|
let next = [];
|
|
for (let [cell, edge] of pending) {
|
|
let terrain = cell.get_terrain();
|
|
if (! terrain)
|
|
continue;
|
|
|
|
let edgeinfo = DIRECTIONS[edge];
|
|
let seen_edges = seen_cells.get(cell) ?? 0;
|
|
if (seen_edges & edgeinfo.bit)
|
|
continue;
|
|
|
|
let tile = terrain;
|
|
let actor = cell.get_actor();
|
|
if (actor && actor.type.contains_wire && (
|
|
actor.movement_cooldown === 0 || level.compat.tiles_react_instantly))
|
|
{
|
|
tile = actor;
|
|
}
|
|
|
|
// The wire comes in from this edge towards the center; see how it connects within this
|
|
// cell, then check for any neighbors
|
|
let connections = edgeinfo.bit;
|
|
let mode = tile.wire_propagation_mode ?? tile.type.wire_propagation_mode;
|
|
if (! is_first && ((tile.wire_directions ?? 0) & edgeinfo.bit) === 0) {
|
|
// There's not actually a wire here (but not if this is our starting cell, in which
|
|
// case we trust the caller)
|
|
if (on_dead_end) {
|
|
on_dead_end(cell, edge);
|
|
}
|
|
continue;
|
|
}
|
|
else if (mode === 'none') {
|
|
// The wires in this tile never connect to each other
|
|
}
|
|
else if (mode === 'cross' || (mode === 'autocross' && tile.wire_directions === 0x0f)) {
|
|
// This is a cross pattern, so only opposite edges connect
|
|
if (tile.wire_directions & edgeinfo.opposite_bit) {
|
|
connections |= edgeinfo.opposite_bit;
|
|
}
|
|
}
|
|
else {
|
|
// Everything connects
|
|
connections |= tile.wire_directions;
|
|
}
|
|
|
|
seen_cells.set(cell, seen_edges | connections);
|
|
|
|
if (on_wire) {
|
|
on_wire(tile, connections);
|
|
}
|
|
|
|
for (let [direction, dirinfo] of Object.entries(DIRECTIONS)) {
|
|
// Obviously don't go backwards, but that doesn't apply if this is our first pass
|
|
if (direction === edge && ! is_first)
|
|
continue;
|
|
|
|
if ((connections & dirinfo.bit) === 0)
|
|
continue;
|
|
|
|
let neighbor;
|
|
if ((terrain.wire_tunnel_directions ?? 0) & dirinfo.bit) {
|
|
// Search in this direction for a matching tunnel
|
|
// Note that while actors (the fuckin circuit block) can be wired, tunnels ONLY
|
|
// appear on terrain, and are NOT affected by actors on top
|
|
neighbor = find_matching_wire_tunnel(level, cell.x, cell.y, direction);
|
|
}
|
|
else {
|
|
neighbor = level.get_neighboring_cell(cell, direction);
|
|
}
|
|
|
|
/*
|
|
if (! neighbor || (((neighbor.get_terrain().wire_directions ?? 0) & dirinfo.opposite_bit) === 0)) {
|
|
console.log("bailing here", neighbor, direction);
|
|
continue;
|
|
}
|
|
*/
|
|
if (! neighbor)
|
|
continue;
|
|
|
|
next.push([neighbor, dirinfo.opposite]);
|
|
}
|
|
}
|
|
pending = next;
|
|
is_first = false;
|
|
}
|
|
}
|
|
|
|
export function find_matching_wire_tunnel(level, x, y, direction) {
|
|
let dirinfo = DIRECTIONS[direction];
|
|
let [dx, dy] = dirinfo.movement;
|
|
let nesting = 0;
|
|
while (true) {
|
|
x += dx;
|
|
y += dy;
|
|
let candidate = level.cell(x, y);
|
|
if (! candidate)
|
|
return null;
|
|
|
|
let neighbor = candidate.get_terrain();
|
|
if (! neighbor)
|
|
continue;
|
|
|
|
if ((neighbor.wire_tunnel_directions ?? 0) & dirinfo.opposite_bit) {
|
|
if (nesting === 0) {
|
|
return candidate;
|
|
}
|
|
else {
|
|
nesting -= 1;
|
|
}
|
|
}
|
|
if ((neighbor.wire_tunnel_directions ?? 0) & dirinfo.bit) {
|
|
nesting += 1;
|
|
}
|
|
}
|
|
}
|