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',
|
||||
},
|
||||
0x5c: {
|
||||
// TODO (modifier chooses logic gate) name: 'doppelganger2',
|
||||
// TODO modifier: ...
|
||||
error: "Logic gates are not yet implemented, sorry!",
|
||||
name: 'logic_gate',
|
||||
modifier: {
|
||||
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: {
|
||||
name: 'button_pink',
|
||||
@ -503,10 +527,10 @@ const TILE_ENCODING = {
|
||||
},
|
||||
},
|
||||
0x72: {
|
||||
name: 'purple_wall',
|
||||
name: 'purple_floor',
|
||||
},
|
||||
0x73: {
|
||||
name: 'purple_floor',
|
||||
name: 'purple_wall',
|
||||
},
|
||||
0x76: {
|
||||
name: '#mod8',
|
||||
|
||||
187
js/game.js
187
js/game.js
@ -95,6 +95,7 @@ export class Tile {
|
||||
}
|
||||
}
|
||||
}
|
||||
Tile.prototype.emitting_edges = 0;
|
||||
|
||||
export class Cell extends Array {
|
||||
constructor(x, y) {
|
||||
@ -159,8 +160,8 @@ export class Cell extends Array {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Cell.prototype.was_powered = false;
|
||||
Cell.prototype.is_powered = false;
|
||||
Cell.prototype.prev_powered_edges = 0;
|
||||
Cell.prototype.powered_edges = 0;
|
||||
|
||||
class GameEnded extends Error {}
|
||||
|
||||
@ -228,9 +229,7 @@ export class Level {
|
||||
|
||||
let n = 0;
|
||||
let connectables = [];
|
||||
// Handle CC2 wiring; a contiguous region of wire is all updated as a single unit, so detect
|
||||
// those units ahead of time for simplicity and call them "clusters"
|
||||
this.wire_clusters = [];
|
||||
this.power_sources = [];
|
||||
// FIXME handle traps correctly:
|
||||
// - 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++) {
|
||||
@ -251,6 +250,10 @@ export class Level {
|
||||
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
|
||||
// with a specific pass at the beginning of the level?
|
||||
// TODO also assumes a specific order...
|
||||
@ -272,9 +275,9 @@ export class Level {
|
||||
// list?
|
||||
tile.stuck = true;
|
||||
}
|
||||
}
|
||||
if (! tile.stuck) {
|
||||
this.actors.push(tile);
|
||||
if (! tile.stuck) {
|
||||
this.actors.push(tile);
|
||||
}
|
||||
}
|
||||
cell._add(tile);
|
||||
|
||||
@ -991,7 +994,8 @@ export class Level {
|
||||
continue;
|
||||
|
||||
// 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))
|
||||
{
|
||||
if (tile.type.is_key) {
|
||||
@ -1075,87 +1079,132 @@ export class Level {
|
||||
// this needs to happen at all
|
||||
// FIXME none of this is currently undoable
|
||||
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
|
||||
// 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)
|
||||
for (let row of this.cells) {
|
||||
for (let cell of row) {
|
||||
cell.was_powered = cell.is_powered;
|
||||
cell.is_powered = false;
|
||||
cell.prev_powered_edges = cell.powered_edges;
|
||||
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
|
||||
for (let row of this.cells) {
|
||||
for (let cell of row) {
|
||||
// TODO probably this should set a prop on the tile
|
||||
if (! cell.some(tile => tile.type.is_emitting && tile.type.is_emitting(tile, this)))
|
||||
while (neighbors.length > 0) {
|
||||
let [cell, source_direction] = neighbors.shift();
|
||||
let wire = cell.get_wired_tile();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Common case: power entering a wired edge and propagating outwards. The only
|
||||
// special case is that four-way wiring is two separate wires, N/S and E/W
|
||||
if (wire.wire_directions === 0x0f) {
|
||||
cell.powered_edges |= bit;
|
||||
cell.powered_edges |= DIRECTIONS[DIRECTIONS[source_direction].opposite].bit;
|
||||
}
|
||||
else {
|
||||
cell.powered_edges = wire.wire_directions;
|
||||
}
|
||||
}
|
||||
|
||||
// Propagate current to neighbors
|
||||
for (let [direction, dirinfo] of Object.entries(DIRECTIONS)) {
|
||||
if (direction === source_direction)
|
||||
continue;
|
||||
if ((cell.powered_edges & dirinfo.bit) === 0)
|
||||
continue;
|
||||
|
||||
// We have an emitter! Flood-fill outwards
|
||||
let neighbors = [cell];
|
||||
for (let neighbor of neighbors) {
|
||||
// Power it even if it's not wired itself, so that e.g. purple tiles work
|
||||
neighbor.is_powered = true;
|
||||
let neighbor, neighbor_wire;
|
||||
let opposite_bit = DIRECTIONS[dirinfo.opposite].bit;
|
||||
if (wire && (wire.wire_tunnel_directions & dirinfo.bit)) {
|
||||
// Search in the given direction until we find a matching tunnel
|
||||
// FIXME these act like nested parens!
|
||||
let x = cell.x;
|
||||
let y = cell.y;
|
||||
let nesting = 0;
|
||||
while (true) {
|
||||
x += dirinfo.movement[0];
|
||||
y += dirinfo.movement[1];
|
||||
if (! this.is_point_within_bounds(x, y))
|
||||
break;
|
||||
|
||||
let wire = neighbor.get_wired_tile();
|
||||
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)) {
|
||||
if (! (wire.wire_directions & dirinfo.bit))
|
||||
continue;
|
||||
|
||||
let neighbor2, wire2;
|
||||
let opposite_bit = DIRECTIONS[dirinfo.opposite].bit;
|
||||
if (wire.wire_tunnel_directions & dirinfo.bit) {
|
||||
// Search in the given direction until we find a matching tunnel
|
||||
// FIXME these act like nested parens!
|
||||
let x = neighbor.x;
|
||||
let y = neighbor.y;
|
||||
let nesting = 0;
|
||||
while (true) {
|
||||
x += dirinfo.movement[0];
|
||||
y += dirinfo.movement[1];
|
||||
if (! this.is_point_within_bounds(x, y))
|
||||
break;
|
||||
|
||||
let candidate = this.cells[y][x];
|
||||
wire2 = candidate.get_wired_tile();
|
||||
if (wire2 && (wire2.wire_tunnel_directions ?? 0) & opposite_bit) {
|
||||
neighbor2 = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Otherwise this is easy
|
||||
neighbor2 = this.get_neighboring_cell(neighbor, direction);
|
||||
wire2 = neighbor2.get_wired_tile();
|
||||
}
|
||||
|
||||
if (neighbor2 && ! neighbor2.is_powered &&
|
||||
// Unwired tiles are OK; they might be something activated by power.
|
||||
// Wired tiles that do NOT connect to us are ignored.
|
||||
(! wire2 || wire2.wire_directions & opposite_bit))
|
||||
{
|
||||
neighbors.push(neighbor2);
|
||||
let candidate = this.cells[y][x];
|
||||
neighbor_wire = candidate.get_wired_tile();
|
||||
if (neighbor_wire && ((neighbor_wire.wire_tunnel_directions ?? 0) & opposite_bit)) {
|
||||
neighbor = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// No tunnel; this is easy
|
||||
neighbor = this.get_neighboring_cell(cell, direction);
|
||||
neighbor_wire = neighbor.get_wired_tile();
|
||||
}
|
||||
|
||||
if (neighbor && (neighbor.powered_edges & opposite_bit) === 0 &&
|
||||
// Unwired tiles are OK; they might be something activated by power.
|
||||
// Wired tiles that do NOT connect to us are ignored.
|
||||
(! neighbor_wire || neighbor_wire.wire_directions & opposite_bit))
|
||||
{
|
||||
neighbors.push([neighbor, dirinfo.opposite]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Inform any affected cells of power changes
|
||||
for (let row of this.cells) {
|
||||
for (let cell of row) {
|
||||
if (cell.was_powered !== cell.is_powered) {
|
||||
let method = cell.is_powered ? 'on_power' : 'on_depower';
|
||||
if ((cell.prev_powered_edges === 0) !== (cell.powered_edges === 0)) {
|
||||
let method = cell.powered_edges ? 'on_power' : 'on_depower';
|
||||
for (let tile of cell) {
|
||||
if (tile.type[method]) {
|
||||
tile.type[method](tile, this);
|
||||
|
||||
@ -485,6 +485,11 @@ const EDITOR_PALETTE = [{
|
||||
'wall_invisible', 'wall_appearing',
|
||||
'gravel',
|
||||
'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',
|
||||
'water', 'turtle', 'fire',
|
||||
'ice', 'ice_nw', 'ice_ne', 'ice_sw', 'ice_se',
|
||||
@ -495,6 +500,7 @@ const EDITOR_PALETTE = [{
|
||||
tiles: [
|
||||
'key_blue', 'key_red', 'key_yellow', 'key_green',
|
||||
'flippers', 'fire_boots', 'cleats', 'suction_boots',
|
||||
'no_sign', // 'bestowal_bow',
|
||||
],
|
||||
}, {
|
||||
title: "Creatures",
|
||||
|
||||
122
js/tileset.js
122
js/tileset.js
@ -35,6 +35,7 @@ export const CC2_TILESET_LAYOUT = {
|
||||
// Wiring!
|
||||
base: [0, 2],
|
||||
wired: [8, 26],
|
||||
wired_cross: [10, 26],
|
||||
is_wired_optional: true,
|
||||
},
|
||||
wall_invisible: [0, 2],
|
||||
@ -206,6 +207,7 @@ export const CC2_TILESET_LAYOUT = {
|
||||
// Wiring!
|
||||
base: [15, 10],
|
||||
wired: [9, 26],
|
||||
wired_cross: [11, 26],
|
||||
is_wired_optional: true,
|
||||
},
|
||||
|
||||
@ -369,6 +371,55 @@ export const CC2_TILESET_LAYOUT = {
|
||||
[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],
|
||||
'#powered': [15, 26],
|
||||
|
||||
@ -600,7 +651,6 @@ export const LL_TILESET_LAYOUT = Object.assign({}, CC2_TILESET_LAYOUT, {
|
||||
teeth: Object.assign({}, CC2_TILESET_LAYOUT.teeth, {
|
||||
north: [[0, 32], [1, 32], [2, 32], [1, 32]],
|
||||
}),
|
||||
popwall2: [9, 32],
|
||||
|
||||
// Extra player sprites
|
||||
player: Object.assign({}, CC2_TILESET_LAYOUT.player, {
|
||||
@ -627,6 +677,10 @@ export const LL_TILESET_LAYOUT = Object.assign({}, CC2_TILESET_LAYOUT, {
|
||||
overlay: [6, 33],
|
||||
base: 'floor',
|
||||
},
|
||||
|
||||
// Custom tiles
|
||||
popwall2: [9, 32],
|
||||
bestowal_bow: [10, 32],
|
||||
});
|
||||
|
||||
export class Tileset {
|
||||
@ -673,31 +727,51 @@ export class Tileset {
|
||||
coords = drawspec.tile;
|
||||
}
|
||||
else if (drawspec.wired) {
|
||||
if (tile && tile.wire_directions) {
|
||||
// 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);
|
||||
// This /should/ match CC2's draw order exactly, based on experimentation
|
||||
let wire_radius = this.layout['#wire-width'] / 2;
|
||||
if (tile && tile.wire_directions === 0x0f) {
|
||||
// This is a wired tile with crossing wires, which acts a little differently
|
||||
// Draw the base tile
|
||||
blit(drawspec.base[0], drawspec.base[1]);
|
||||
|
||||
// Draw a masked part of the base tile
|
||||
let wiredir = tile.wire_directions;
|
||||
let wire_radius = this.layout['#wire-width'] / 2;
|
||||
let wire0 = 0.5 - wire_radius;
|
||||
let wire1 = 0.5 + wire_radius;
|
||||
let [bx, by] = drawspec.base;
|
||||
if ((wiredir & DIRECTIONS['north'].bit) === 0) {
|
||||
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);
|
||||
}
|
||||
// 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);
|
||||
|
||||
// Then draw the wired tile as normal
|
||||
// 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;
|
||||
}
|
||||
else {
|
||||
|
||||
@ -4,7 +4,7 @@ import { random_choice } from './util.js';
|
||||
// Draw layers
|
||||
const LAYER_TERRAIN = 0;
|
||||
const LAYER_ITEM = 1;
|
||||
const LAYER_NO_SIGN = 2;
|
||||
const LAYER_ITEM_MOD = 2;
|
||||
const LAYER_ACTOR = 3;
|
||||
const LAYER_OVERLAY = 4;
|
||||
// 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: {
|
||||
draw_layer: LAYER_NO_SIGN,
|
||||
draw_layer: LAYER_ITEM_MOD,
|
||||
disables_pickup: true,
|
||||
blocks(me, level, other) {
|
||||
let item;
|
||||
@ -562,6 +562,10 @@ const TILE_TYPES = {
|
||||
return item && other.has_item(item);
|
||||
},
|
||||
},
|
||||
bestowal_bow: {
|
||||
draw_layer: LAYER_ITEM_MOD,
|
||||
allows_all_pickup: true,
|
||||
},
|
||||
|
||||
// Mechanisms
|
||||
dirt_block: {
|
||||
@ -1046,9 +1050,15 @@ const TILE_TYPES = {
|
||||
},
|
||||
button_pink: {
|
||||
draw_layer: LAYER_TERRAIN,
|
||||
is_emitting(me, level) {
|
||||
// We emit current as long as there's an actor on us
|
||||
return me.cell.some(tile => tile.type.is_actor);
|
||||
is_power_source: true,
|
||||
get_emitting_edges(me, level) {
|
||||
// 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) {
|
||||
level.sfx.play_once('button-press', me.cell);
|
||||
@ -1060,6 +1070,16 @@ const TILE_TYPES = {
|
||||
button_black: {
|
||||
// TODO not implemented
|
||||
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: {
|
||||
// TODO only partially implemented
|
||||
@ -1086,6 +1106,51 @@ const TILE_TYPES = {
|
||||
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
|
||||
stopwatch_bonus: {
|
||||
@ -1390,6 +1455,14 @@ const TILE_TYPES = {
|
||||
blocks_monsters: 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
|
||||
player: {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user