Clean up wiring drawing and logic (zero gates is now a no-op!); begin implementing logic gates
This commit is contained in:
parent
37b44bcca4
commit
49ff0d9723
@ -421,9 +421,33 @@ const TILE_ENCODING = {
|
|||||||
name: 'no_player1_sign',
|
name: 'no_player1_sign',
|
||||||
},
|
},
|
||||||
0x5c: {
|
0x5c: {
|
||||||
// TODO (modifier chooses logic gate) name: 'doppelganger2',
|
name: 'logic_gate',
|
||||||
// TODO modifier: ...
|
modifier: {
|
||||||
error: "Logic gates are not yet implemented, sorry!",
|
decode(tile, modifier) {
|
||||||
|
if (modifier >= 0x1e && modifier <= 0x27) {
|
||||||
|
// Counter, which can't be rotated
|
||||||
|
tile.direction = 'north';
|
||||||
|
tile.gate_type = 'counter';
|
||||||
|
tile.counter_value = modifier - 0x1e;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tile.direction = ['north', 'east', 'south', 'west'][modifier & 0x03];
|
||||||
|
let type = modifier >> 2;
|
||||||
|
if (type < 6) {
|
||||||
|
tile.gate_type = ['not', 'and', 'or', 'xor', 'latch-cw', 'nand'][type];
|
||||||
|
}
|
||||||
|
else if (type === 16) {
|
||||||
|
tile.gate_type = 'latch-ccw';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tile.gate_type = 'bogus';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
encode(tile) {
|
||||||
|
// FIXME implement
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
0x5e: {
|
0x5e: {
|
||||||
name: 'button_pink',
|
name: 'button_pink',
|
||||||
@ -503,10 +527,10 @@ const TILE_ENCODING = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
0x72: {
|
0x72: {
|
||||||
name: 'purple_wall',
|
name: 'purple_floor',
|
||||||
},
|
},
|
||||||
0x73: {
|
0x73: {
|
||||||
name: 'purple_floor',
|
name: 'purple_wall',
|
||||||
},
|
},
|
||||||
0x76: {
|
0x76: {
|
||||||
name: '#mod8',
|
name: '#mod8',
|
||||||
|
|||||||
139
js/game.js
139
js/game.js
@ -95,6 +95,7 @@ export class Tile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Tile.prototype.emitting_edges = 0;
|
||||||
|
|
||||||
export class Cell extends Array {
|
export class Cell extends Array {
|
||||||
constructor(x, y) {
|
constructor(x, y) {
|
||||||
@ -159,8 +160,8 @@ export class Cell extends Array {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Cell.prototype.was_powered = false;
|
Cell.prototype.prev_powered_edges = 0;
|
||||||
Cell.prototype.is_powered = false;
|
Cell.prototype.powered_edges = 0;
|
||||||
|
|
||||||
class GameEnded extends Error {}
|
class GameEnded extends Error {}
|
||||||
|
|
||||||
@ -228,9 +229,7 @@ export class Level {
|
|||||||
|
|
||||||
let n = 0;
|
let n = 0;
|
||||||
let connectables = [];
|
let connectables = [];
|
||||||
// Handle CC2 wiring; a contiguous region of wire is all updated as a single unit, so detect
|
this.power_sources = [];
|
||||||
// those units ahead of time for simplicity and call them "clusters"
|
|
||||||
this.wire_clusters = [];
|
|
||||||
// FIXME handle traps correctly:
|
// FIXME handle traps correctly:
|
||||||
// - if an actor is in the cell, set the trap to open and unstick everything in it
|
// - if an actor is in the cell, set the trap to open and unstick everything in it
|
||||||
for (let y = 0; y < this.height; y++) {
|
for (let y = 0; y < this.height; y++) {
|
||||||
@ -251,6 +250,10 @@ export class Level {
|
|||||||
tile.specific_hint = template_tile.specific_hint ?? null;
|
tile.specific_hint = template_tile.specific_hint ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tile.type.is_power_source) {
|
||||||
|
this.power_sources.push(tile);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO well this is pretty special-casey. maybe come up
|
// TODO well this is pretty special-casey. maybe come up
|
||||||
// with a specific pass at the beginning of the level?
|
// with a specific pass at the beginning of the level?
|
||||||
// TODO also assumes a specific order...
|
// TODO also assumes a specific order...
|
||||||
@ -272,10 +275,10 @@ export class Level {
|
|||||||
// list?
|
// list?
|
||||||
tile.stuck = true;
|
tile.stuck = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (! tile.stuck) {
|
if (! tile.stuck) {
|
||||||
this.actors.push(tile);
|
this.actors.push(tile);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
cell._add(tile);
|
cell._add(tile);
|
||||||
|
|
||||||
if (tile.type.connects_to) {
|
if (tile.type.connects_to) {
|
||||||
@ -991,7 +994,8 @@ export class Level {
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// TODO some actors can pick up some items...
|
// TODO some actors can pick up some items...
|
||||||
if (actor.type.is_player && tile.type.is_item &&
|
if (tile.type.is_item &&
|
||||||
|
(actor.type.is_player || cell.some(t => t.allows_all_pickup)) &&
|
||||||
this.attempt_take(actor, tile))
|
this.attempt_take(actor, tile))
|
||||||
{
|
{
|
||||||
if (tile.type.is_key) {
|
if (tile.type.is_key) {
|
||||||
@ -1075,49 +1079,96 @@ export class Level {
|
|||||||
// this needs to happen at all
|
// this needs to happen at all
|
||||||
// FIXME none of this is currently undoable
|
// FIXME none of this is currently undoable
|
||||||
update_wiring() {
|
update_wiring() {
|
||||||
|
// Gather every tile that's emitting power. Along the way, check whether any of them have
|
||||||
|
// changed since last tic, so we can skip this work entirely if none did
|
||||||
|
let neighbors = [];
|
||||||
|
let any_changed = false;
|
||||||
|
for (let tile of this.power_sources) {
|
||||||
|
if (! tile.cell)
|
||||||
|
continue;
|
||||||
|
let emitting = tile.type.get_emitting_edges(tile, this);
|
||||||
|
if (emitting) {
|
||||||
|
neighbors.push([tile.cell, emitting]);
|
||||||
|
}
|
||||||
|
if (emitting !== tile.emitting_edges) {
|
||||||
|
any_changed = true;
|
||||||
|
tile.emitting_edges = emitting;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Also check actors, since any of them might be holding a lightning bolt (argh)
|
||||||
|
for (let actor of this.actors) {
|
||||||
|
if (! actor.cell)
|
||||||
|
continue;
|
||||||
|
// Only count when they're on a tile, not in transit!
|
||||||
|
let emitting = actor.movement_cooldown === 0 && actor.has_item('lightning_bolt');
|
||||||
|
if (emitting) {
|
||||||
|
neighbors.push([actor.cell, emitting]);
|
||||||
|
}
|
||||||
|
if (emitting !== actor.emitting_edges) {
|
||||||
|
any_changed = true;
|
||||||
|
actor.emitting_edges = emitting;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If none changed, we're done
|
||||||
|
if (! any_changed)
|
||||||
|
return;
|
||||||
|
|
||||||
// Turn off power to every cell
|
// Turn off power to every cell
|
||||||
// TODO wonder if i need a linear cell list, or even a flat list of all tiles (that sounds
|
// TODO wonder if i need a linear cell list, or even a flat list of all tiles (that sounds
|
||||||
// like hell to keep updated though)
|
// like hell to keep updated though)
|
||||||
for (let row of this.cells) {
|
for (let row of this.cells) {
|
||||||
for (let cell of row) {
|
for (let cell of row) {
|
||||||
cell.was_powered = cell.is_powered;
|
cell.prev_powered_edges = cell.powered_edges;
|
||||||
cell.is_powered = false;
|
cell.powered_edges = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate through the grid looking for emitters — tiles that are generating current — and
|
// Iterate over emitters and flood-fill outwards one edge at a time
|
||||||
// propagated it via flood-fill through neighboring wires
|
// propagated it via flood-fill through neighboring wires
|
||||||
for (let row of this.cells) {
|
while (neighbors.length > 0) {
|
||||||
for (let cell of row) {
|
let [cell, source_direction] = neighbors.shift();
|
||||||
// TODO probably this should set a prop on the tile
|
let wire = cell.get_wired_tile();
|
||||||
if (! cell.some(tile => tile.type.is_emitting && tile.type.is_emitting(tile, this)))
|
|
||||||
|
// Power this cell
|
||||||
|
if (typeof(source_direction) === 'number') {
|
||||||
|
// This cell is emitting power itself, and the source direction is actually a
|
||||||
|
// bitmask of directions
|
||||||
|
cell.powered_edges = source_direction;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let bit = DIRECTIONS[source_direction].bit;
|
||||||
|
if (wire === null || (wire.wire_directions & bit) === 0) {
|
||||||
|
// No wire on this side, so the power doesn't actually propagate, but it DOES
|
||||||
|
// stay on this edge (so if this is e.g. a purple tile, it'll be powered)
|
||||||
|
cell.powered_edges |= bit;
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// We have an emitter! Flood-fill outwards
|
// Common case: power entering a wired edge and propagating outwards. The only
|
||||||
let neighbors = [cell];
|
// special case is that four-way wiring is two separate wires, N/S and E/W
|
||||||
for (let neighbor of neighbors) {
|
if (wire.wire_directions === 0x0f) {
|
||||||
// Power it even if it's not wired itself, so that e.g. purple tiles work
|
cell.powered_edges |= bit;
|
||||||
neighbor.is_powered = true;
|
cell.powered_edges |= DIRECTIONS[DIRECTIONS[source_direction].opposite].bit;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cell.powered_edges = wire.wire_directions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let wire = neighbor.get_wired_tile();
|
// Propagate current to neighbors
|
||||||
if (! wire)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Emit along every wire direction, and add any unpowered neighbors to the
|
|
||||||
// pending list to continue the floodfill
|
|
||||||
// TODO but only if wires connect
|
|
||||||
// TODO handle wire tunnels
|
|
||||||
for (let [direction, dirinfo] of Object.entries(DIRECTIONS)) {
|
for (let [direction, dirinfo] of Object.entries(DIRECTIONS)) {
|
||||||
if (! (wire.wire_directions & dirinfo.bit))
|
if (direction === source_direction)
|
||||||
|
continue;
|
||||||
|
if ((cell.powered_edges & dirinfo.bit) === 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
let neighbor2, wire2;
|
let neighbor, neighbor_wire;
|
||||||
let opposite_bit = DIRECTIONS[dirinfo.opposite].bit;
|
let opposite_bit = DIRECTIONS[dirinfo.opposite].bit;
|
||||||
if (wire.wire_tunnel_directions & dirinfo.bit) {
|
if (wire && (wire.wire_tunnel_directions & dirinfo.bit)) {
|
||||||
// Search in the given direction until we find a matching tunnel
|
// Search in the given direction until we find a matching tunnel
|
||||||
// FIXME these act like nested parens!
|
// FIXME these act like nested parens!
|
||||||
let x = neighbor.x;
|
let x = cell.x;
|
||||||
let y = neighbor.y;
|
let y = cell.y;
|
||||||
let nesting = 0;
|
let nesting = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
x += dirinfo.movement[0];
|
x += dirinfo.movement[0];
|
||||||
@ -1126,27 +1177,25 @@ export class Level {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
let candidate = this.cells[y][x];
|
let candidate = this.cells[y][x];
|
||||||
wire2 = candidate.get_wired_tile();
|
neighbor_wire = candidate.get_wired_tile();
|
||||||
if (wire2 && (wire2.wire_tunnel_directions ?? 0) & opposite_bit) {
|
if (neighbor_wire && ((neighbor_wire.wire_tunnel_directions ?? 0) & opposite_bit)) {
|
||||||
neighbor2 = candidate;
|
neighbor = candidate;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Otherwise this is easy
|
// No tunnel; this is easy
|
||||||
neighbor2 = this.get_neighboring_cell(neighbor, direction);
|
neighbor = this.get_neighboring_cell(cell, direction);
|
||||||
wire2 = neighbor2.get_wired_tile();
|
neighbor_wire = neighbor.get_wired_tile();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (neighbor2 && ! neighbor2.is_powered &&
|
if (neighbor && (neighbor.powered_edges & opposite_bit) === 0 &&
|
||||||
// Unwired tiles are OK; they might be something activated by power.
|
// Unwired tiles are OK; they might be something activated by power.
|
||||||
// Wired tiles that do NOT connect to us are ignored.
|
// Wired tiles that do NOT connect to us are ignored.
|
||||||
(! wire2 || wire2.wire_directions & opposite_bit))
|
(! neighbor_wire || neighbor_wire.wire_directions & opposite_bit))
|
||||||
{
|
{
|
||||||
neighbors.push(neighbor2);
|
neighbors.push([neighbor, dirinfo.opposite]);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1154,8 +1203,8 @@ export class Level {
|
|||||||
// Inform any affected cells of power changes
|
// Inform any affected cells of power changes
|
||||||
for (let row of this.cells) {
|
for (let row of this.cells) {
|
||||||
for (let cell of row) {
|
for (let cell of row) {
|
||||||
if (cell.was_powered !== cell.is_powered) {
|
if ((cell.prev_powered_edges === 0) !== (cell.powered_edges === 0)) {
|
||||||
let method = cell.is_powered ? 'on_power' : 'on_depower';
|
let method = cell.powered_edges ? 'on_power' : 'on_depower';
|
||||||
for (let tile of cell) {
|
for (let tile of cell) {
|
||||||
if (tile.type[method]) {
|
if (tile.type[method]) {
|
||||||
tile.type[method](tile, this);
|
tile.type[method](tile, this);
|
||||||
|
|||||||
@ -485,6 +485,11 @@ const EDITOR_PALETTE = [{
|
|||||||
'wall_invisible', 'wall_appearing',
|
'wall_invisible', 'wall_appearing',
|
||||||
'gravel',
|
'gravel',
|
||||||
'dirt',
|
'dirt',
|
||||||
|
'dirt',
|
||||||
|
|
||||||
|
'floor_custom_green', 'floor_custom_pink', 'floor_custom_yellow', 'floor_custom_blue',
|
||||||
|
'wall_custom_green', 'wall_custom_pink', 'wall_custom_yellow', 'wall_custom_blue',
|
||||||
|
|
||||||
'door_blue', 'door_red', 'door_yellow', 'door_green',
|
'door_blue', 'door_red', 'door_yellow', 'door_green',
|
||||||
'water', 'turtle', 'fire',
|
'water', 'turtle', 'fire',
|
||||||
'ice', 'ice_nw', 'ice_ne', 'ice_sw', 'ice_se',
|
'ice', 'ice_nw', 'ice_ne', 'ice_sw', 'ice_se',
|
||||||
@ -495,6 +500,7 @@ const EDITOR_PALETTE = [{
|
|||||||
tiles: [
|
tiles: [
|
||||||
'key_blue', 'key_red', 'key_yellow', 'key_green',
|
'key_blue', 'key_red', 'key_yellow', 'key_green',
|
||||||
'flippers', 'fire_boots', 'cleats', 'suction_boots',
|
'flippers', 'fire_boots', 'cleats', 'suction_boots',
|
||||||
|
'no_sign', // 'bestowal_bow',
|
||||||
],
|
],
|
||||||
}, {
|
}, {
|
||||||
title: "Creatures",
|
title: "Creatures",
|
||||||
|
|||||||
122
js/tileset.js
122
js/tileset.js
@ -35,6 +35,7 @@ export const CC2_TILESET_LAYOUT = {
|
|||||||
// Wiring!
|
// Wiring!
|
||||||
base: [0, 2],
|
base: [0, 2],
|
||||||
wired: [8, 26],
|
wired: [8, 26],
|
||||||
|
wired_cross: [10, 26],
|
||||||
is_wired_optional: true,
|
is_wired_optional: true,
|
||||||
},
|
},
|
||||||
wall_invisible: [0, 2],
|
wall_invisible: [0, 2],
|
||||||
@ -206,6 +207,7 @@ export const CC2_TILESET_LAYOUT = {
|
|||||||
// Wiring!
|
// Wiring!
|
||||||
base: [15, 10],
|
base: [15, 10],
|
||||||
wired: [9, 26],
|
wired: [9, 26],
|
||||||
|
wired_cross: [11, 26],
|
||||||
is_wired_optional: true,
|
is_wired_optional: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -369,6 +371,55 @@ export const CC2_TILESET_LAYOUT = {
|
|||||||
[15, 24],
|
[15, 24],
|
||||||
],
|
],
|
||||||
|
|
||||||
|
logic_gate: {
|
||||||
|
// TODO currently, 'wired' can't coexist with visual state etc...
|
||||||
|
// TODO *long sigh* of course, logic gates have parts with independent current too
|
||||||
|
'latch-ccw': {
|
||||||
|
north: [8, 21],
|
||||||
|
east: [9, 21],
|
||||||
|
south: [10, 21],
|
||||||
|
west: [11, 21],
|
||||||
|
},
|
||||||
|
not: {
|
||||||
|
north: [0, 25],
|
||||||
|
east: [1, 25],
|
||||||
|
south: [2, 25],
|
||||||
|
west: [3, 25],
|
||||||
|
},
|
||||||
|
and: {
|
||||||
|
north: [4, 25],
|
||||||
|
east: [5, 25],
|
||||||
|
south: [6, 25],
|
||||||
|
west: [7, 25],
|
||||||
|
},
|
||||||
|
or: {
|
||||||
|
north: [8, 25],
|
||||||
|
east: [9, 25],
|
||||||
|
south: [10, 25],
|
||||||
|
west: [11, 25],
|
||||||
|
},
|
||||||
|
xor: {
|
||||||
|
north: [12, 25],
|
||||||
|
east: [13, 25],
|
||||||
|
south: [14, 25],
|
||||||
|
west: [15, 25],
|
||||||
|
},
|
||||||
|
'latch-cw': {
|
||||||
|
north: [0, 26],
|
||||||
|
east: [1, 26],
|
||||||
|
south: [2, 26],
|
||||||
|
west: [3, 26],
|
||||||
|
},
|
||||||
|
nand: {
|
||||||
|
north: [4, 26],
|
||||||
|
east: [5, 26],
|
||||||
|
south: [6, 26],
|
||||||
|
west: [7, 26],
|
||||||
|
},
|
||||||
|
// FIXME need to draw the number as well
|
||||||
|
counter: [14, 26],
|
||||||
|
},
|
||||||
|
|
||||||
'#unpowered': [13, 26],
|
'#unpowered': [13, 26],
|
||||||
'#powered': [15, 26],
|
'#powered': [15, 26],
|
||||||
|
|
||||||
@ -600,7 +651,6 @@ export const LL_TILESET_LAYOUT = Object.assign({}, CC2_TILESET_LAYOUT, {
|
|||||||
teeth: Object.assign({}, CC2_TILESET_LAYOUT.teeth, {
|
teeth: Object.assign({}, CC2_TILESET_LAYOUT.teeth, {
|
||||||
north: [[0, 32], [1, 32], [2, 32], [1, 32]],
|
north: [[0, 32], [1, 32], [2, 32], [1, 32]],
|
||||||
}),
|
}),
|
||||||
popwall2: [9, 32],
|
|
||||||
|
|
||||||
// Extra player sprites
|
// Extra player sprites
|
||||||
player: Object.assign({}, CC2_TILESET_LAYOUT.player, {
|
player: Object.assign({}, CC2_TILESET_LAYOUT.player, {
|
||||||
@ -627,6 +677,10 @@ export const LL_TILESET_LAYOUT = Object.assign({}, CC2_TILESET_LAYOUT, {
|
|||||||
overlay: [6, 33],
|
overlay: [6, 33],
|
||||||
base: 'floor',
|
base: 'floor',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Custom tiles
|
||||||
|
popwall2: [9, 32],
|
||||||
|
bestowal_bow: [10, 32],
|
||||||
});
|
});
|
||||||
|
|
||||||
export class Tileset {
|
export class Tileset {
|
||||||
@ -673,31 +727,51 @@ export class Tileset {
|
|||||||
coords = drawspec.tile;
|
coords = drawspec.tile;
|
||||||
}
|
}
|
||||||
else if (drawspec.wired) {
|
else if (drawspec.wired) {
|
||||||
if (tile && tile.wire_directions) {
|
// This /should/ match CC2's draw order exactly, based on experimentation
|
||||||
// TODO all four is a different thing entirely with two separate parts, ugh
|
|
||||||
// Draw the appropriate wire underlay
|
|
||||||
this.draw_type(tile.cell.is_powered ? '#powered' : '#unpowered', tile, tic, blit);
|
|
||||||
|
|
||||||
// Draw a masked part of the base tile
|
|
||||||
let wiredir = tile.wire_directions;
|
|
||||||
let wire_radius = this.layout['#wire-width'] / 2;
|
let wire_radius = this.layout['#wire-width'] / 2;
|
||||||
let wire0 = 0.5 - wire_radius;
|
if (tile && tile.wire_directions === 0x0f) {
|
||||||
let wire1 = 0.5 + wire_radius;
|
// This is a wired tile with crossing wires, which acts a little differently
|
||||||
let [bx, by] = drawspec.base;
|
// Draw the base tile
|
||||||
if ((wiredir & DIRECTIONS['north'].bit) === 0) {
|
blit(drawspec.base[0], drawspec.base[1]);
|
||||||
blit(bx, by, 0, 0, 1, wire0);
|
|
||||||
}
|
|
||||||
if ((wiredir & DIRECTIONS['south'].bit) === 0) {
|
|
||||||
blit(bx, by + wire1, 0, wire1, 1, wire0);
|
|
||||||
}
|
|
||||||
if ((wiredir & DIRECTIONS['west'].bit) === 0) {
|
|
||||||
blit(bx, by, 0, 0, wire0, 1);
|
|
||||||
}
|
|
||||||
if ((wiredir & DIRECTIONS['east'].bit) === 0) {
|
|
||||||
blit(bx + wire1, by, wire1, 0, wire0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then draw the wired tile as normal
|
// Draw the two wires as separate rectangles, NS then EW
|
||||||
|
let wire_inset = 0.5 - wire_radius;
|
||||||
|
let wire_coords_ns = this.layout[
|
||||||
|
tile.cell && tile.cell.powered_edges & DIRECTIONS['north'].bit ? '#powered' : '#unpowered'];
|
||||||
|
let wire_coords_ew = this.layout[
|
||||||
|
tile.cell && tile.cell.powered_edges & DIRECTIONS['east'].bit ? '#powered' : '#unpowered'];
|
||||||
|
blit(wire_coords_ns[0] + wire_inset, wire_coords_ns[1], wire_inset, 0, wire_radius * 2, 1);
|
||||||
|
blit(wire_coords_ew[0], wire_coords_ew[1] + wire_inset, 0, wire_inset, 1, wire_radius * 2);
|
||||||
|
|
||||||
|
// Draw the cross tile on top
|
||||||
|
coords = drawspec.wired_cross ?? drawspec.wired;
|
||||||
|
}
|
||||||
|
else if (tile && tile.wire_directions) {
|
||||||
|
// Draw the base tile
|
||||||
|
blit(drawspec.base[0], drawspec.base[1]);
|
||||||
|
|
||||||
|
// Draw the wire part as a single rectangle, initially just a small dot in the
|
||||||
|
// center, but extending out to any edge that has a wire present
|
||||||
|
let x0 = 0.5 - wire_radius;
|
||||||
|
let x1 = 0.5 + wire_radius;
|
||||||
|
let y0 = 0.5 - wire_radius;
|
||||||
|
let y1 = 0.5 + wire_radius;
|
||||||
|
if (tile.wire_directions & DIRECTIONS['north'].bit) {
|
||||||
|
y0 = 0;
|
||||||
|
}
|
||||||
|
if (tile.wire_directions & DIRECTIONS['east'].bit) {
|
||||||
|
x1 = 1;
|
||||||
|
}
|
||||||
|
if (tile.wire_directions & DIRECTIONS['south'].bit) {
|
||||||
|
y1 = 1;
|
||||||
|
}
|
||||||
|
if (tile.wire_directions & DIRECTIONS['west'].bit) {
|
||||||
|
x0 = 0;
|
||||||
|
}
|
||||||
|
let wire_coords = this.layout[tile.cell && tile.cell.powered_edges ? '#powered' : '#unpowered'];
|
||||||
|
blit(wire_coords[0] + x0, wire_coords[1] + y0, x0, y0, x1 - x0, y1 - y0);
|
||||||
|
|
||||||
|
// Then draw the wired tile on top of it all
|
||||||
coords = drawspec.wired;
|
coords = drawspec.wired;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { random_choice } from './util.js';
|
|||||||
// Draw layers
|
// Draw layers
|
||||||
const LAYER_TERRAIN = 0;
|
const LAYER_TERRAIN = 0;
|
||||||
const LAYER_ITEM = 1;
|
const LAYER_ITEM = 1;
|
||||||
const LAYER_NO_SIGN = 2;
|
const LAYER_ITEM_MOD = 2;
|
||||||
const LAYER_ACTOR = 3;
|
const LAYER_ACTOR = 3;
|
||||||
const LAYER_OVERLAY = 4;
|
const LAYER_OVERLAY = 4;
|
||||||
// TODO cc2 order is: swivel, thinwalls, canopy (and yes you can have them all in the same tile)
|
// TODO cc2 order is: swivel, thinwalls, canopy (and yes you can have them all in the same tile)
|
||||||
@ -549,7 +549,7 @@ const TILE_TYPES = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
no_sign: {
|
no_sign: {
|
||||||
draw_layer: LAYER_NO_SIGN,
|
draw_layer: LAYER_ITEM_MOD,
|
||||||
disables_pickup: true,
|
disables_pickup: true,
|
||||||
blocks(me, level, other) {
|
blocks(me, level, other) {
|
||||||
let item;
|
let item;
|
||||||
@ -562,6 +562,10 @@ const TILE_TYPES = {
|
|||||||
return item && other.has_item(item);
|
return item && other.has_item(item);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
bestowal_bow: {
|
||||||
|
draw_layer: LAYER_ITEM_MOD,
|
||||||
|
allows_all_pickup: true,
|
||||||
|
},
|
||||||
|
|
||||||
// Mechanisms
|
// Mechanisms
|
||||||
dirt_block: {
|
dirt_block: {
|
||||||
@ -1046,9 +1050,15 @@ const TILE_TYPES = {
|
|||||||
},
|
},
|
||||||
button_pink: {
|
button_pink: {
|
||||||
draw_layer: LAYER_TERRAIN,
|
draw_layer: LAYER_TERRAIN,
|
||||||
is_emitting(me, level) {
|
is_power_source: true,
|
||||||
// We emit current as long as there's an actor on us
|
get_emitting_edges(me, level) {
|
||||||
return me.cell.some(tile => tile.type.is_actor);
|
// We emit current as long as there's an actor fully on us
|
||||||
|
if (me.cell.some(tile => tile.type.is_actor && tile.movement_cooldown === 0)) {
|
||||||
|
return me.wire_directions;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
on_arrive(me, level, other) {
|
on_arrive(me, level, other) {
|
||||||
level.sfx.play_once('button-press', me.cell);
|
level.sfx.play_once('button-press', me.cell);
|
||||||
@ -1060,6 +1070,16 @@ const TILE_TYPES = {
|
|||||||
button_black: {
|
button_black: {
|
||||||
// TODO not implemented
|
// TODO not implemented
|
||||||
draw_layer: LAYER_TERRAIN,
|
draw_layer: LAYER_TERRAIN,
|
||||||
|
is_power_source: true,
|
||||||
|
get_emitting_edges(me, level) {
|
||||||
|
// We emit current as long as there's NOT an actor fully on us
|
||||||
|
if (! me.cell.some(tile => tile.type.is_actor && tile.movement_cooldown === 0)) {
|
||||||
|
return me.wire_directions;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
button_gray: {
|
button_gray: {
|
||||||
// TODO only partially implemented
|
// TODO only partially implemented
|
||||||
@ -1086,6 +1106,51 @@ const TILE_TYPES = {
|
|||||||
level.sfx.play_once('button-release', me.cell);
|
level.sfx.play_once('button-release', me.cell);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// Logic gates, all consolidated into a single tile type
|
||||||
|
logic_gate: {
|
||||||
|
// gate_type: not, and, or, xor, nand, latch-cw, latch-ccw, counter, bogus
|
||||||
|
_gate_types: {
|
||||||
|
not: ['out', null, 'in1', null],
|
||||||
|
and: ['out', 'in2', null, 'in1'],
|
||||||
|
or: [],
|
||||||
|
xor: [],
|
||||||
|
nand: [],
|
||||||
|
'latch-cw': [],
|
||||||
|
'latch-ccw': [],
|
||||||
|
},
|
||||||
|
draw_layer: LAYER_TERRAIN,
|
||||||
|
is_power_source: true,
|
||||||
|
get_emitting_edges(me, level) {
|
||||||
|
if (me.gate_type === 'and') {
|
||||||
|
let vars = {};
|
||||||
|
let out_bit = 0;
|
||||||
|
let dir = me.direction;
|
||||||
|
for (let name of me.type._gate_types[me.gate_type]) {
|
||||||
|
let dirinfo = DIRECTIONS[dir];
|
||||||
|
if (name === 'out') {
|
||||||
|
out_bit |= dirinfo.bit;
|
||||||
|
}
|
||||||
|
else if (name) {
|
||||||
|
vars[name] = (me.cell.powered_edges & dirinfo.bit) !== 0;
|
||||||
|
}
|
||||||
|
dir = dirinfo.right;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vars.in1 && vars.in2) {
|
||||||
|
return out_bit;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
visual_state(me) {
|
||||||
|
return me.gate_type;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// Time alteration
|
// Time alteration
|
||||||
stopwatch_bonus: {
|
stopwatch_bonus: {
|
||||||
@ -1390,6 +1455,14 @@ const TILE_TYPES = {
|
|||||||
blocks_monsters: true,
|
blocks_monsters: true,
|
||||||
blocks_blocks: true,
|
blocks_blocks: true,
|
||||||
},
|
},
|
||||||
|
lightning_bolt: {
|
||||||
|
// TODO not implemented
|
||||||
|
draw_layer: LAYER_ITEM,
|
||||||
|
is_item: true,
|
||||||
|
is_tool: true,
|
||||||
|
blocks_monsters: true,
|
||||||
|
blocks_blocks: true,
|
||||||
|
},
|
||||||
|
|
||||||
// Progression
|
// Progression
|
||||||
player: {
|
player: {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user