From 0a5e5c66c21fd569b2dff2d55b2ab127cd81bdc1 Mon Sep 17 00:00:00 2001 From: "Eevee (Evelyn Woods)" Date: Mon, 22 Apr 2024 09:44:50 -0600 Subject: [PATCH] Add a rough circuit preview to the wire tool --- js/editor/mouseops.js | 115 ++++++++++++++++++++++++++++++++++++++++++ style.css | 20 ++++++++ 2 files changed, 135 insertions(+) diff --git a/js/editor/mouseops.js b/js/editor/mouseops.js index c3c5dd6..ea70f32 100644 --- a/js/editor/mouseops.js +++ b/js/editor/mouseops.js @@ -1,6 +1,7 @@ // Types that handle mouse activity for a given tool, whether the mouse button is current held or // not. (When the mouse button is /not/ held, then only the operation bound to the left mouse // button gets events.) +import * as algorithms from '../algorithms.js'; import { DIRECTIONS, LAYERS } from '../defs.js'; import TILE_TYPES from '../tiletypes.js'; import { mk, mk_svg, walk_grid } from '../util.js'; @@ -718,6 +719,11 @@ export class SelectOperation extends MouseOperation { } } + +// ------------------------------------------------------------------------------------------------- +// FORCE FLOORS +// TODO ice would be kind of nice, could reasonably go with this as an alt mode + export class ForceFloorOperation extends MouseOperation { handle_press(x, y) { // Begin by placing an all-way force floor under the mouse @@ -785,6 +791,10 @@ export class ForceFloorOperation extends MouseOperation { } } + +// ------------------------------------------------------------------------------------------------- +// TRACKS + // TODO entered cell should get blank railroad? // TODO maybe place a straight track in the new cell so it looks like we're doing something, then // fix it if it wasn't there? @@ -878,6 +888,10 @@ export class TrackOperation extends MouseOperation { } } + +// ------------------------------------------------------------------------------------------------- +// CONNECT + export class ConnectOperation extends MouseOperation { constructor(...args) { super(...args); @@ -1017,7 +1031,91 @@ export class ConnectOperation extends MouseOperation { super.do_destroy(); } } + + +// ------------------------------------------------------------------------------------------------- +// WIRE + export class WireOperation extends MouseOperation { + constructor(...args) { + super(...args); + + this.circuitry_g = mk_svg('g.overlay-circuitry', {'data-source': 'WireOperation'}); + this.editor.svg_overlay.append(this.circuitry_g); + } + + handle_hover(client_x, client_y, frac_cell_x, frac_cell_y, cell_x, cell_y) { + // TODO doesn't this read the /previous/ edge + let edge = this.get_tile_edge(); + let edgebit = DIRECTIONS[edge].bit; + let cell = this.cell(cell_x, cell_y); + // Wire can only be in terrain or the circuit block, and this tool ignores circuit + // blocks for circuit-tracing purposes + let terrain = cell[LAYERS.terrain]; + // If this cell has no wire where we're aiming, do nothing, which includes leaving any + // existing circuit alone + if (! terrain.wire_directions || ! (terrain.wire_directions & edgebit)) + return; + + // If we're hovering a wire that's already part of the highlighted circuit, there's + // nothing new to do + if (this.circuit) { + let edges = this.circuit.tiles.get(terrain); + if (edges && (edges & DIRECTIONS[edge].bit)) + return; + } + + // Otherwise, clear the current circuit and trace a new one + // TODO what do we do when hovering a *cell* that touches multiple circuits? like a + // button? + // TODO trace this through logic gates, at least one level? + this.circuitry_g.textContent = ''; + + let circuit = algorithms.trace_floor_circuit(this.editor.stored_level, 'ignore', cell, edge); + // TODO uhoh, this is in terms of tiles and i don't have a way to get those at the moment + let tiles_to_cells = new Map; + for (let cell of this.editor.stored_level.linear_cells) { + tiles_to_cells.set(cell[LAYERS.terrain], cell); + } + let wire_data = []; + let tunnel_data = []; + // TODO it would be convenient to reduce the number of wires happening here + for (let [tile, edges] of circuit.tiles) { + let cell = tiles_to_cells.get(tile); + for (let [direction, dirinfo] of Object.entries(DIRECTIONS)) { + if (edges & dirinfo.bit) { + let d = `M ${cell.x + 0.5},${cell.y + 0.5} l ${dirinfo.movement[0] * 0.5},${dirinfo.movement[1] * 0.5}`; + if (tile.wire_tunnel_directions & dirinfo.bit) { + tunnel_data.push(d); + } + else { + wire_data.push(d); + } + } + } + if (! tile.wire_directions) { + // This is an output tile, like a trap or flame jet, so mark it + this.circuitry_g.append(mk_svg('circle.overlay-circuit-output', { + cx: cell.x + 0.5, + cy: cell.y + 0.5, + r: 0.1875, + })); + } + } + this.circuitry_g.append( + mk_svg('path.overlay-circuit-wires', {d: wire_data.join(' ')}), + mk_svg('path.overlay-circuit-tunnels', {d: tunnel_data.join(' ')}), + ); + for (let [tile, edges] of circuit.inputs) { + let cell = tiles_to_cells.get(tile); + this.circuitry_g.append(mk_svg('circle.overlay-circuit-input', { + cx: cell.x + 0.5, + cy: cell.y + 0.5, + r: 0.1875, + })); + } + } + handle_press() { if (this.alt_mode) { // Place or remove wire tunnels @@ -1191,8 +1289,17 @@ export class WireOperation extends MouseOperation { cleanup_press() { this.editor.commit_undo(); } + + do_destroy() { + this.circuitry_g.remove(); + super.do_destroy(); + } } + +// ------------------------------------------------------------------------------------------------- +// ROTATE + // TODO hmm there's no way to rotate the wires on a circuit block without rotating the block itself // TODO this highlights blocks even though they don't usually show their direction... // maybe put a pencil-like preview tile on here that highlights the tile being targeted, and also @@ -1306,6 +1413,10 @@ export class RotateOperation extends MouseOperation { // TODO should it? } + +// ------------------------------------------------------------------------------------------------- +// ADJUST + // Tiles the "adjust" tool will turn into each other const ADJUST_TILE_TYPES = {}; const ADJUST_GATE_TYPES = {}; @@ -1760,6 +1871,10 @@ export class AdjustOperation extends MouseOperation { } } + +// ------------------------------------------------------------------------------------------------- +// CAMERA + // FIXME currently allows creating outside the map bounds and moving beyond the right/bottom, sigh // FIXME undo // TODO view is not especially visible diff --git a/style.css b/style.css index c7d32fd..b6d0a83 100644 --- a/style.css +++ b/style.css @@ -2287,6 +2287,26 @@ svg.level-editor-overlay g.overlay-connection.--implicit line.-arrow { svg.level-editor-overlay g.overlay-connection line.-arrow { marker-end: url(#overlay-arrowhead); } +svg.level-editor-overlay g.overlay-circuitry { + filter: url(#overlay-filter-outline); + opacity: 0.75; +} +svg.level-editor-overlay .overlay-circuit-wires { + stroke: hsl(180, 100%, 50%); + stroke-linecap: square; /* so a corner meets nicely in the middle of a tile */ +} +svg.level-editor-overlay .overlay-circuit-tunnels { + stroke: hsl(180, 100%, 50%); + stroke-dasharray: 0.0625, 0.0625; +} +svg.level-editor-overlay circle.overlay-circuit-input { + stroke: hsl(180, 100%, 50%); + fill: hsl(180, 100%, 10%); +} +svg.level-editor-overlay circle.overlay-circuit-output { + stroke: hsl(180, 100%, 50%); + fill: hsl(180, 100%, 50%); +} svg.level-editor-overlay .overlay-adjust-cursor { /* shared between rotate+adjust tools, though they use different elements/shapes */ stroke: #444;