Refactor to using cells with fixed slots
This better matches CC2 behavior and also makes some very common operations, like grabbing a cell's actor or terrain, way faster. It also allows me to efficiently implement CC2's layer order when checking for collisions; thin walls are checked before terrain, and actors only afterwards. The upshot is that bowling balls no longer destroy stuff on the other side of a thin wall! I also did some minor optimizing, mostly by turning loops over an entire cell's contents into checks for a single layer; Chromium now performs a bulk test about 30% faster. Downsides of this change: - All kinds of stuff may have broken! - It'll be a little difficult to ever emulate MSCC's curious behavior when stacking terrain on top of items or other terrain. But not impossible. - It'll be far more difficult to emulate buggy Lynx (or maybe it's just Tile World?) behavior where some combination of cloners and teleports allow a ton of monsters to accumulate in a few cells. I guess I wasn't planning on doing that anyway.
This commit is contained in:
parent
cff756597c
commit
323ed3ee18
@ -57,7 +57,7 @@ export const INPUT_BITS = {
|
|||||||
wait: 0x8000,
|
wait: 0x8000,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DRAW_LAYERS = {
|
export const LAYERS = {
|
||||||
terrain: 0,
|
terrain: 0,
|
||||||
item: 1,
|
item: 1,
|
||||||
item_mod: 2,
|
item_mod: 2,
|
||||||
@ -66,6 +66,7 @@ export const DRAW_LAYERS = {
|
|||||||
swivel: 5,
|
swivel: 5,
|
||||||
thin_wall: 6,
|
thin_wall: 6,
|
||||||
canopy: 7,
|
canopy: 7,
|
||||||
|
|
||||||
MAX: 8,
|
MAX: 8,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { DIRECTIONS, DIRECTION_ORDER } from './defs.js';
|
import { DIRECTIONS, DIRECTION_ORDER, LAYERS } from './defs.js';
|
||||||
import * as format_base from './format-base.js';
|
import * as format_base from './format-base.js';
|
||||||
import TILE_TYPES from './tiletypes.js';
|
import TILE_TYPES from './tiletypes.js';
|
||||||
import * as util from './util.js';
|
import * as util from './util.js';
|
||||||
@ -1438,7 +1438,7 @@ export function synthesize_level(stored_level) {
|
|||||||
// save it until we reach the terrain layer, and then sub it in instead.
|
// save it until we reach the terrain layer, and then sub it in instead.
|
||||||
// TODO if i follow in tyler's footsteps and give swivel its own layer then i'll need to
|
// TODO if i follow in tyler's footsteps and give swivel its own layer then i'll need to
|
||||||
// complicate this somewhat
|
// complicate this somewhat
|
||||||
if (tile.type.draw_layer === 0 && dummy_terrain_tile) {
|
if (tile.type.layer === LAYERS.terrain && dummy_terrain_tile) {
|
||||||
tile = dummy_terrain_tile;
|
tile = dummy_terrain_tile;
|
||||||
spec = REVERSE_TILE_ENCODING[tile.type.name];
|
spec = REVERSE_TILE_ENCODING[tile.type.name];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { DIRECTIONS } from './defs.js';
|
import { DIRECTIONS, LAYERS } from './defs.js';
|
||||||
import * as format_base from './format-base.js';
|
import * as format_base from './format-base.js';
|
||||||
import TILE_TYPES from './tiletypes.js';
|
import TILE_TYPES from './tiletypes.js';
|
||||||
import * as util from './util.js';
|
import * as util from './util.js';
|
||||||
@ -266,7 +266,7 @@ function parse_level(bytes, number) {
|
|||||||
|
|
||||||
// Fix the "floor/empty" nonsense here by adding floor to any cell with no terrain on bottom
|
// Fix the "floor/empty" nonsense here by adding floor to any cell with no terrain on bottom
|
||||||
for (let cell of level.linear_cells) {
|
for (let cell of level.linear_cells) {
|
||||||
if (cell.length === 0 || cell[0].type.draw_layer !== 0) {
|
if (cell.length === 0 || cell[0].type.layer !== LAYERS.terrain) {
|
||||||
// No terrain; insert a floor
|
// No terrain; insert a floor
|
||||||
cell.unshift({ type: TILE_TYPES['floor'] });
|
cell.unshift({ type: TILE_TYPES['floor'] });
|
||||||
}
|
}
|
||||||
|
|||||||
260
js/game.js
260
js/game.js
@ -1,5 +1,5 @@
|
|||||||
import * as algorithms from './algorithms.js';
|
import * as algorithms from './algorithms.js';
|
||||||
import { DIRECTIONS, DIRECTION_ORDER, INPUT_BITS, TICS_PER_SECOND } from './defs.js';
|
import { DIRECTIONS, DIRECTION_ORDER, LAYERS, INPUT_BITS, TICS_PER_SECOND } from './defs.js';
|
||||||
import { LevelInterface } from './format-base.js';
|
import { LevelInterface } from './format-base.js';
|
||||||
import TILE_TYPES from './tiletypes.js';
|
import TILE_TYPES from './tiletypes.js';
|
||||||
|
|
||||||
@ -54,10 +54,16 @@ export class Tile {
|
|||||||
// Extremely awkward special case: items don't block monsters if the cell also contains an
|
// Extremely awkward special case: items don't block monsters if the cell also contains an
|
||||||
// item modifier (i.e. "no" sign) or a real player
|
// item modifier (i.e. "no" sign) or a real player
|
||||||
// TODO would love to get this outta here
|
// TODO would love to get this outta here
|
||||||
if (this.type.is_item &&
|
if (this.type.is_item) {
|
||||||
this.cell.some(tile => tile.type.item_modifier || tile.type.is_real_player))
|
let item_mod = this.cell.get_item_mod();
|
||||||
|
if (item_mod && item_mod.type.item_modifier)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
let actor = this.cell.get_actor();
|
||||||
|
if (actor && actor.type.is_real_player)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.type.blocks_collision & other.type.collision_mask)
|
if (this.type.blocks_collision & other.type.collision_mask)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@ -122,6 +128,7 @@ export class Tile {
|
|||||||
direction = tile.cell.redirect_exit(tile, direction);
|
direction = tile.cell.redirect_exit(tile, direction);
|
||||||
// Need to explicitly check this here, otherwise you could /attempt/ to push a block,
|
// Need to explicitly check this here, otherwise you could /attempt/ to push a block,
|
||||||
// which would fail, but it would still change the block's direction
|
// which would fail, but it would still change the block's direction
|
||||||
|
// XXX this expects to take a level but it only matters with push_mode === 'push'
|
||||||
return tile.cell.try_leaving(tile, direction);
|
return tile.cell.try_leaving(tile, direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,37 +149,36 @@ Tile.prototype.wire_tunnel_directions = 0;
|
|||||||
|
|
||||||
export class Cell extends Array {
|
export class Cell extends Array {
|
||||||
constructor(x, y) {
|
constructor(x, y) {
|
||||||
super();
|
super(LAYERS.MAX);
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
_add(tile, index = null) {
|
_add(tile) {
|
||||||
if (index === null) {
|
let index = tile.type.layer;
|
||||||
this.push(tile);
|
if (this[index]) {
|
||||||
}
|
console.error("ATTEMPTING TO ADD", tile, "TO CELL", this, "WHICH ERASES EXISTING TILE", this[index]);
|
||||||
else {
|
this[index].cell = null;
|
||||||
this.splice(index, 0, tile);
|
|
||||||
}
|
}
|
||||||
|
this[index] = tile;
|
||||||
tile.cell = this;
|
tile.cell = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// DO NOT use me to remove a tile permanently, only to move it!
|
// DO NOT use me to remove a tile permanently, only to move it!
|
||||||
// Should only be called from Level, which handles some bookkeeping!
|
// Should only be called from Level, which handles some bookkeeping!
|
||||||
_remove(tile) {
|
_remove(tile) {
|
||||||
let index = this.indexOf(tile);
|
let index = tile.type.layer;
|
||||||
if (index < 0)
|
if (this[index] !== tile)
|
||||||
throw new Error("Asked to remove tile that doesn't seem to exist");
|
throw new Error("Asked to remove tile that doesn't seem to exist");
|
||||||
|
|
||||||
this.splice(index, 1);
|
this[index] = null;
|
||||||
tile.cell = null;
|
tile.cell = null;
|
||||||
return index;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get_wired_tile() {
|
get_wired_tile() {
|
||||||
let ret = null;
|
let ret = null;
|
||||||
for (let tile of this) {
|
for (let tile of this) {
|
||||||
if ((tile.wire_directions || tile.wire_tunnel_directions) && ! tile.movement_cooldown) {
|
if (tile && (tile.wire_directions || tile.wire_tunnel_directions) && ! tile.movement_cooldown) {
|
||||||
ret = tile;
|
ret = tile;
|
||||||
// Don't break; we want the topmost tile!
|
// Don't break; we want the topmost tile!
|
||||||
}
|
}
|
||||||
@ -181,52 +187,53 @@ export class Cell extends Array {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get_terrain() {
|
get_terrain() {
|
||||||
for (let tile of this) {
|
return this[LAYERS.terrain] ?? null;
|
||||||
if (tile.type.draw_layer === 0)
|
|
||||||
return tile;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get_actor() {
|
get_actor() {
|
||||||
for (let tile of this) {
|
return this[LAYERS.actor] ?? null;
|
||||||
if (tile.type.is_actor)
|
|
||||||
return tile;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get_item() {
|
get_item() {
|
||||||
for (let tile of this) {
|
return this[LAYERS.item] ?? null;
|
||||||
if (tile.type.is_item)
|
|
||||||
return tile;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get_item_mod() {
|
get_item_mod() {
|
||||||
for (let tile of this) {
|
return this[LAYERS.item_mod] ?? null;
|
||||||
if (tile.type.item_modifier)
|
|
||||||
return tile;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
has(name) {
|
has(name) {
|
||||||
return this.some(tile => tile.type.name === name);
|
let current = this[TILE_TYPES[name].layer];
|
||||||
|
return current && current.type.name === name;
|
||||||
}
|
}
|
||||||
|
|
||||||
try_leaving(actor, direction) {
|
// FIXME honestly no longer sure why these two are on Cell, or even separate really
|
||||||
for (let tile of this) {
|
try_leaving(actor, direction, level, push_mode) {
|
||||||
if (tile === actor)
|
// The only tiles that can trap us are thin walls and terrain, so for perf (this is very hot
|
||||||
continue;
|
// code), only bother checking those)
|
||||||
|
let terrain = this[LAYERS.terrain];
|
||||||
|
let thin_walls = this[LAYERS.thin_wall];
|
||||||
|
let blocker;
|
||||||
|
|
||||||
if (tile.type.traps && tile.type.traps(tile, actor))
|
if (thin_walls && thin_walls.type.blocks_leaving && thin_walls.type.blocks_leaving(thin_walls, actor, direction)) {
|
||||||
return false;
|
blocker = thin_walls;
|
||||||
|
}
|
||||||
|
else if (terrain.type.traps && terrain.type.traps(terrain, actor)) {
|
||||||
|
blocker = terrain;
|
||||||
|
}
|
||||||
|
else if (terrain.type.blocks_leaving && terrain.type.blocks_leaving(terrain, actor, direction)) {
|
||||||
|
blocker = terrain;
|
||||||
|
}
|
||||||
|
|
||||||
if (tile.type.blocks_leaving && tile.type.blocks_leaving(tile, actor, direction))
|
if (blocker) {
|
||||||
|
if (push_mode === 'push') {
|
||||||
|
if (actor.type.on_blocked) {
|
||||||
|
actor.type.on_blocked(actor, level, direction, blocker);
|
||||||
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,7 +245,6 @@ export class Cell extends Array {
|
|||||||
// - 'push': Fire bump triggers. Attempt to move pushable objects out of the way immediately.
|
// - 'push': Fire bump triggers. Attempt to move pushable objects out of the way immediately.
|
||||||
try_entering(actor, direction, level, push_mode = null) {
|
try_entering(actor, direction, level, push_mode = null) {
|
||||||
let pushable_tiles = [];
|
let pushable_tiles = [];
|
||||||
let blocked = false;
|
|
||||||
// Subtleties ahoy! This is **EXTREMELY** sensitive to ordering. Consider:
|
// Subtleties ahoy! This is **EXTREMELY** sensitive to ordering. Consider:
|
||||||
// - An actor with foil MUST NOT bump a wall on the other side of a thin wall.
|
// - An actor with foil MUST NOT bump a wall on the other side of a thin wall.
|
||||||
// - A ghost with foil MUST bump a wall (even on the other side of a thin wall) and be
|
// - A ghost with foil MUST bump a wall (even on the other side of a thin wall) and be
|
||||||
@ -252,20 +258,18 @@ export class Cell extends Array {
|
|||||||
// It seems the order is thus: canopy + thin wall; terrain; actor; item. Which is the usual
|
// It seems the order is thus: canopy + thin wall; terrain; actor; item. Which is the usual
|
||||||
// ordering from the top down, except that terrain is checked before actors. Really, the
|
// ordering from the top down, except that terrain is checked before actors. Really, the
|
||||||
// ordering is from "outermost" to "innermost", which makes physical sense.
|
// ordering is from "outermost" to "innermost", which makes physical sense.
|
||||||
// FIXME make that work, then. i think i may need to shift to fixed slots unfortunately
|
for (let layer of [
|
||||||
// (Note that here, and anywhere else that has any chance of altering the cell's contents,
|
LAYERS.canopy, LAYERS.thin_wall, LAYERS.terrain, LAYERS.swivel,
|
||||||
// we iterate over a copy of the cell to insulate ourselves from tiles appearing or
|
LAYERS.actor, LAYERS.item_mod, LAYERS.item])
|
||||||
// disappearing mid-iteration.)
|
{
|
||||||
for (let tile of Array.from(this).reverse()) {
|
let tile = this[layer];
|
||||||
|
if (! tile)
|
||||||
|
continue;
|
||||||
|
|
||||||
// TODO check ignores here?
|
// TODO check ignores here?
|
||||||
// Note that if they can't enter this cell because of a thin wall, then they can't bump
|
|
||||||
// any of our other tiles either. (This is my best guess at the actual behavior, seeing
|
|
||||||
// as walls also block everything but players can obviously bump /those/.)
|
|
||||||
if (! blocked) {
|
|
||||||
if (tile.type.on_bumped) {
|
if (tile.type.on_bumped) {
|
||||||
tile.type.on_bumped(tile, level, actor);
|
tile.type.on_bumped(tile, level, actor);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (! tile.blocks(actor, direction, level))
|
if (! tile.blocks(actor, direction, level))
|
||||||
continue;
|
continue;
|
||||||
@ -274,23 +278,19 @@ export class Cell extends Array {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (! actor.can_push(tile, direction)) {
|
if (! actor.can_push(tile, direction)) {
|
||||||
|
// It's in our way and we can't push it, so we're done here
|
||||||
if (push_mode === 'push') {
|
if (push_mode === 'push') {
|
||||||
// Track this instead of returning immediately, because 'push' mode also bumps
|
if (actor.type.on_blocked) {
|
||||||
// every tile in the cell
|
actor.type.on_blocked(actor, level, direction, tile);
|
||||||
blocked = true;
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Collect pushables for later, so we don't inadvertently push through a wall
|
// Collect pushables for later, so we don't inadvertently push through a wall
|
||||||
pushable_tiles.push(tile);
|
pushable_tiles.push(tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (blocked)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// If we got this far, all that's left is to deal with pushables
|
// If we got this far, all that's left is to deal with pushables
|
||||||
if (pushable_tiles.length > 0) {
|
if (pushable_tiles.length > 0) {
|
||||||
// This ends recursive push attempts, which can happen with a row of ice clogged by ice
|
// This ends recursive push attempts, which can happen with a row of ice clogged by ice
|
||||||
@ -340,7 +340,7 @@ export class Cell extends Array {
|
|||||||
// BLOX replay, and right at the end ice blocks spring mine each other. also, the wiki
|
// BLOX replay, and right at the end ice blocks spring mine each other. also, the wiki
|
||||||
// suggests something about another actor moving away at the same time?
|
// suggests something about another actor moving away at the same time?
|
||||||
if (! (level.compat.emulate_spring_mining && actor.type.is_real_player) &&
|
if (! (level.compat.emulate_spring_mining && actor.type.is_real_player) &&
|
||||||
push_mode === 'push' && this.some(tile => tile.blocks(actor, direction, level)))
|
push_mode === 'push' && this.some(tile => tile && tile.blocks(actor, direction, level)))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,10 +349,9 @@ export class Cell extends Array {
|
|||||||
|
|
||||||
// Special railroad ability: change the direction we attempt to leave
|
// Special railroad ability: change the direction we attempt to leave
|
||||||
redirect_exit(actor, direction) {
|
redirect_exit(actor, direction) {
|
||||||
for (let tile of this) {
|
let terrain = this.get_terrain();
|
||||||
if (tile.type.redirect_exit) {
|
if (terrain && terrain.type.redirect_exit) {
|
||||||
return tile.type.redirect_exit(tile, actor, direction);
|
return terrain.type.redirect_exit(terrain, actor, direction);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return direction;
|
return direction;
|
||||||
}
|
}
|
||||||
@ -455,6 +454,7 @@ export class Level extends LevelInterface {
|
|||||||
let stored_cell = this.stored_level.linear_cells[n];
|
let stored_cell = this.stored_level.linear_cells[n];
|
||||||
n++;
|
n++;
|
||||||
|
|
||||||
|
// FIXME give this same treatment to stored cells (otherwise the editor is fucked)
|
||||||
for (let template_tile of stored_cell) {
|
for (let template_tile of stored_cell) {
|
||||||
let tile = Tile.from_template(template_tile);
|
let tile = Tile.from_template(template_tile);
|
||||||
if (tile.type.is_hint) {
|
if (tile.type.is_hint) {
|
||||||
@ -508,7 +508,7 @@ export class Level extends LevelInterface {
|
|||||||
if (target_cell_n && target_cell_n < this.width * this.height) {
|
if (target_cell_n && target_cell_n < this.width * this.height) {
|
||||||
let [tx, ty] = this.stored_level.scalar_to_coords(target_cell_n);
|
let [tx, ty] = this.stored_level.scalar_to_coords(target_cell_n);
|
||||||
for (let tile of this.cell(tx, ty)) {
|
for (let tile of this.cell(tx, ty)) {
|
||||||
if (goals === tile.type.name) {
|
if (tile && goals === tile.type.name) {
|
||||||
connectable.connection = tile;
|
connectable.connection = tile;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -522,7 +522,7 @@ export class Level extends LevelInterface {
|
|||||||
for (let cell of this.iter_cells_in_diamond(connectable.cell)) {
|
for (let cell of this.iter_cells_in_diamond(connectable.cell)) {
|
||||||
let target = null;
|
let target = null;
|
||||||
for (let tile of cell) {
|
for (let tile of cell) {
|
||||||
if (goals.has(tile.type.name)) {
|
if (tile && goals.has(tile.type.name)) {
|
||||||
target = tile;
|
target = tile;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -625,7 +625,10 @@ export class Level extends LevelInterface {
|
|||||||
// Dead end handling (potentially logic gates, etc.)
|
// Dead end handling (potentially logic gates, etc.)
|
||||||
(cell, edge) => {
|
(cell, edge) => {
|
||||||
for (let tile of cell) {
|
for (let tile of cell) {
|
||||||
if (tile.type.name === 'logic_gate') {
|
if (! tile) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (tile.type.name === 'logic_gate') {
|
||||||
// Logic gates are the one non-wired tile that get attached to circuits,
|
// Logic gates are the one non-wired tile that get attached to circuits,
|
||||||
// mostly so blue teleporters can follow them
|
// mostly so blue teleporters can follow them
|
||||||
if (! tile.circuits) {
|
if (! tile.circuits) {
|
||||||
@ -659,6 +662,8 @@ export class Level extends LevelInterface {
|
|||||||
for (let i = this.linear_cells.length - 1; i >= 0; i--) {
|
for (let i = this.linear_cells.length - 1; i >= 0; i--) {
|
||||||
let cell = this.linear_cells[i];
|
let cell = this.linear_cells[i];
|
||||||
for (let tile of cell) {
|
for (let tile of cell) {
|
||||||
|
if (! tile)
|
||||||
|
continue;
|
||||||
if (tile.type.on_ready) {
|
if (tile.type.on_ready) {
|
||||||
tile.type.on_ready(tile, this);
|
tile.type.on_ready(tile, this);
|
||||||
}
|
}
|
||||||
@ -737,7 +742,7 @@ export class Level extends LevelInterface {
|
|||||||
for (let i = this.linear_cells.length - 1; i >= 0; i--) {
|
for (let i = this.linear_cells.length - 1; i >= 0; i--) {
|
||||||
let cell = this.linear_cells[i];
|
let cell = this.linear_cells[i];
|
||||||
for (let tile of cell) {
|
for (let tile of cell) {
|
||||||
if (tile.type.on_begin) {
|
if (tile && tile.type.on_begin) {
|
||||||
tile.type.on_begin(tile, this);
|
tile.type.on_begin(tile, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1116,6 +1121,7 @@ export class Level extends LevelInterface {
|
|||||||
this.commit();
|
this.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO this only has one caller
|
||||||
_extract_player_directions(input) {
|
_extract_player_directions(input) {
|
||||||
// Extract directions from an input mask
|
// Extract directions from an input mask
|
||||||
let dir1 = null, dir2 = null;
|
let dir1 = null, dir2 = null;
|
||||||
@ -1322,7 +1328,7 @@ export class Level extends LevelInterface {
|
|||||||
}
|
}
|
||||||
if (forced_only)
|
if (forced_only)
|
||||||
return;
|
return;
|
||||||
if (actor.cell.some(tile => tile.type.traps && tile.type.traps(tile, actor))) {
|
if (terrain.type.traps && terrain.type.traps(terrain, actor)) {
|
||||||
// An actor in a cloner or a closed trap can't turn
|
// An actor in a cloner or a closed trap can't turn
|
||||||
// TODO because of this, if a tank is trapped when a blue button is pressed, then
|
// TODO because of this, if a tank is trapped when a blue button is pressed, then
|
||||||
// when released, it will make one move out of the trap and /then/ turn around and
|
// when released, it will make one move out of the trap and /then/ turn around and
|
||||||
@ -1366,7 +1372,7 @@ export class Level extends LevelInterface {
|
|||||||
check_movement(actor, orig_cell, direction, push_mode) {
|
check_movement(actor, orig_cell, direction, push_mode) {
|
||||||
let dest_cell = this.get_neighboring_cell(orig_cell, direction);
|
let dest_cell = this.get_neighboring_cell(orig_cell, direction);
|
||||||
let success = (dest_cell &&
|
let success = (dest_cell &&
|
||||||
orig_cell.try_leaving(actor, direction) &&
|
orig_cell.try_leaving(actor, direction, this, push_mode) &&
|
||||||
dest_cell.try_entering(actor, direction, this, push_mode));
|
dest_cell.try_entering(actor, direction, this, push_mode));
|
||||||
|
|
||||||
// If we have the hook, pull anything behind us, now that we're out of the way.
|
// If we have the hook, pull anything behind us, now that we're out of the way.
|
||||||
@ -1439,12 +1445,8 @@ export class Level extends LevelInterface {
|
|||||||
let speed = actor.type.movement_speed;
|
let speed = actor.type.movement_speed;
|
||||||
|
|
||||||
let move = DIRECTIONS[direction].movement;
|
let move = DIRECTIONS[direction].movement;
|
||||||
if (! this.check_movement(actor, actor.cell, direction, 'push')) {
|
if (! this.check_movement(actor, actor.cell, direction, 'push'))
|
||||||
if (actor.type.on_blocked) {
|
|
||||||
actor.type.on_blocked(actor, this, direction);
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
// We're clear! Compute our speed and move us
|
// We're clear! Compute our speed and move us
|
||||||
// FIXME this feels clunky
|
// FIXME this feels clunky
|
||||||
@ -1497,6 +1499,8 @@ export class Level extends LevelInterface {
|
|||||||
// XXX that's still not perfect; if actor X is tic-misaligned, like if there's a chain
|
// XXX that's still not perfect; if actor X is tic-misaligned, like if there's a chain
|
||||||
// of 3 or more actors cloning directly onto red buttons for other cloners, then this
|
// of 3 or more actors cloning directly onto red buttons for other cloners, then this
|
||||||
// cannot possibly work
|
// cannot possibly work
|
||||||
|
// TODO now that i have steam-strict mode this is largely pointless, just do what seems
|
||||||
|
// correct
|
||||||
actor.cooldown_delay_hack = 1;
|
actor.cooldown_delay_hack = 1;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1513,14 +1517,21 @@ export class Level extends LevelInterface {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
let original_cell = actor.cell;
|
let original_cell = actor.cell;
|
||||||
|
// Physically remove the actor first, so that it won't get in the way of e.g. a splash
|
||||||
|
// spawned from stepping off of a lilypad
|
||||||
|
this.remove_tile(actor);
|
||||||
|
|
||||||
// Announce we're leaving, for the handful of tiles that care about it
|
// Announce we're leaving, for the handful of tiles that care about it
|
||||||
for (let tile of Array.from(original_cell)) {
|
for (let tile of original_cell) {
|
||||||
|
if (! tile)
|
||||||
|
continue;
|
||||||
if (tile === actor)
|
if (tile === actor)
|
||||||
continue;
|
continue;
|
||||||
if (actor.ignores(tile.type.name))
|
if (actor.ignores(tile.type.name))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// FIXME ah, stepping off a lilypad will add a splash but we're still here? but then
|
||||||
|
// why did the warning not catch it
|
||||||
if (tile.type.on_depart) {
|
if (tile.type.on_depart) {
|
||||||
tile.type.on_depart(tile, this, actor);
|
tile.type.on_depart(tile, this, actor);
|
||||||
}
|
}
|
||||||
@ -1532,7 +1543,7 @@ export class Level extends LevelInterface {
|
|||||||
}
|
}
|
||||||
for (let tile of goal_cell) {
|
for (let tile of goal_cell) {
|
||||||
// FIXME this could go in on_approach now
|
// FIXME this could go in on_approach now
|
||||||
if (actor === this.player && tile.type.is_hint) {
|
if (tile && actor === this.player && tile.type.is_hint) {
|
||||||
this.hint_shown = tile.hint_text ?? this.stored_level.hint;
|
this.hint_shown = tile.hint_text ?? this.stored_level.hint;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1540,7 +1551,9 @@ export class Level extends LevelInterface {
|
|||||||
// Announce we're approaching. Slide mode is set here, since it's about the tile we're
|
// Announce we're approaching. Slide mode is set here, since it's about the tile we're
|
||||||
// moving towards and needs to last through our next decision
|
// moving towards and needs to last through our next decision
|
||||||
this.make_slide(actor, null);
|
this.make_slide(actor, null);
|
||||||
for (let tile of Array.from(goal_cell)) {
|
for (let tile of goal_cell) {
|
||||||
|
if (! tile)
|
||||||
|
continue;
|
||||||
if (tile === actor)
|
if (tile === actor)
|
||||||
continue;
|
continue;
|
||||||
if (actor.ignores(tile.type.name))
|
if (actor.ignores(tile.type.name))
|
||||||
@ -1570,10 +1583,17 @@ export class Level extends LevelInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now physically move the actor; we wait until here in case some of those callbacks handled
|
// Now add the actor back; we have to wait this long because e.g. monsters erase splashes
|
||||||
// interactions between actors on the same layer (e.g. monsters erasing splashes)
|
if (goal_cell.get_actor()) {
|
||||||
this.remove_tile(actor);
|
// FIXME a monster or block killing the player will still move into her cell!!! i don't
|
||||||
|
// know what to do about this, i feel like i tried making monster/player block each
|
||||||
|
// other before and it did not go well. maybe it was an ordering issue though?
|
||||||
|
this.add_tile(actor, original_cell);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
this.add_tile(actor, goal_cell);
|
this.add_tile(actor, goal_cell);
|
||||||
|
}
|
||||||
|
|
||||||
// If we're a monster stepping on the player's tail, that also kills her immediately; the
|
// If we're a monster stepping on the player's tail, that also kills her immediately; the
|
||||||
// player and a monster must be strictly more than 4 tics apart
|
// player and a monster must be strictly more than 4 tics apart
|
||||||
@ -1598,7 +1618,11 @@ 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) {
|
||||||
// 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
|
||||||
for (let tile of Array.from(cell).reverse()) {
|
// TODO is there a custom order here similar to collision checking?
|
||||||
|
for (let layer = LAYERS.MAX - 1; layer >= 0; layer--) {
|
||||||
|
let tile = cell[layer];
|
||||||
|
if (! tile)
|
||||||
|
continue;
|
||||||
if (tile === actor)
|
if (tile === actor)
|
||||||
continue;
|
continue;
|
||||||
if (actor.ignores(tile.type.name))
|
if (actor.ignores(tile.type.name))
|
||||||
@ -1607,7 +1631,7 @@ export class Level extends LevelInterface {
|
|||||||
if (tile.type.is_item &&
|
if (tile.type.is_item &&
|
||||||
// FIXME implement item priority i'm begging you
|
// FIXME implement item priority i'm begging you
|
||||||
((actor.type.has_inventory && ! (tile.type.name === 'key_red' && ! actor.type.is_player)) ||
|
((actor.type.has_inventory && ! (tile.type.name === 'key_red' && ! actor.type.is_player)) ||
|
||||||
cell.some(t => t.type.item_modifier === 'pickup')) &&
|
(cell.get_item_mod() && cell.get_item_mod().type.item_modifier === 'pickup')) &&
|
||||||
this.attempt_take(actor, tile))
|
this.attempt_take(actor, tile))
|
||||||
{
|
{
|
||||||
if (tile.type.is_key) {
|
if (tile.type.is_key) {
|
||||||
@ -1684,7 +1708,7 @@ export class Level extends LevelInterface {
|
|||||||
for ([dest, direction] of teleporter.type.teleport_dest_order(teleporter, this, actor)) {
|
for ([dest, direction] of teleporter.type.teleport_dest_order(teleporter, this, actor)) {
|
||||||
// Teleporters already containing an actor are blocked and unusable
|
// Teleporters already containing an actor are blocked and unusable
|
||||||
// FIXME should check collision? otherwise this catches non-blocking vfx...
|
// FIXME should check collision? otherwise this catches non-blocking vfx...
|
||||||
if (dest.cell.some(tile => tile.type.is_actor && tile !== actor && ! tile.type.ttl))
|
if (dest.cell.some(tile => tile && tile.type.is_actor && tile !== actor && ! tile.type.ttl))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// XXX lynx treats this as a slide and does it in a pass in the main loop
|
// XXX lynx treats this as a slide and does it in a pass in the main loop
|
||||||
@ -1767,7 +1791,7 @@ export class Level extends LevelInterface {
|
|||||||
// Attempt to place an item in the world, as though dropped by an actor
|
// Attempt to place an item in the world, as though dropped by an actor
|
||||||
_place_dropped_item(name, cell, dropping_actor) {
|
_place_dropped_item(name, cell, dropping_actor) {
|
||||||
let type = TILE_TYPES[name];
|
let type = TILE_TYPES[name];
|
||||||
if (type.draw_layer === 0) {
|
if (type.layer === LAYERS.terrain) {
|
||||||
// Terrain items (i.e., yellow teleports) can only be dropped on regular floor
|
// Terrain items (i.e., yellow teleports) can only be dropped on regular floor
|
||||||
let terrain = cell.get_terrain();
|
let terrain = cell.get_terrain();
|
||||||
if (terrain.type.name !== 'floor')
|
if (terrain.type.name !== 'floor')
|
||||||
@ -1794,12 +1818,19 @@ export class Level extends LevelInterface {
|
|||||||
if (type.is_actor) {
|
if (type.is_actor) {
|
||||||
// This is tricky -- the item has become an actor, but whatever dropped it is
|
// This is tricky -- the item has become an actor, but whatever dropped it is
|
||||||
// already in this cell's actor layer. But we also know for sure that there's no
|
// already in this cell's actor layer. But we also know for sure that there's no
|
||||||
// item in this cell, so we'll cheat a little: add it in the item layer, set it
|
// item in this cell, so we'll cheat a little: remove the dropping actor, set the
|
||||||
// rolling (which should shift it into the next cell over), then switch it to the
|
// item moving, then put the dropping actor back before anyone notices.
|
||||||
// actor layer.
|
cell._remove(dropping_actor);
|
||||||
// TODO do that
|
this.add_tile(tile, cell);
|
||||||
|
if (! this.attempt_out_of_turn_step(tile, dropping_actor.direction)) {
|
||||||
|
// It was unable to move, so there's nothing we can do but destroy it
|
||||||
|
// TODO maybe blow it up with a nonblocking vfx? in cc2 it just vanishes
|
||||||
|
this.remove_tile(tile);
|
||||||
|
}
|
||||||
|
else {
|
||||||
this.add_actor(tile);
|
this.add_actor(tile);
|
||||||
this.attempt_out_of_turn_step(tile, dropping_actor.direction);
|
}
|
||||||
|
cell._add(dropping_actor);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.add_tile(tile, cell);
|
this.add_tile(tile, cell);
|
||||||
@ -1946,9 +1977,11 @@ export class Level extends LevelInterface {
|
|||||||
// Some non-actor tiles still want to act every tic. Note that this should happen AFTER wiring.
|
// Some non-actor tiles still want to act every tic. Note that this should happen AFTER wiring.
|
||||||
_do_static_phase() {
|
_do_static_phase() {
|
||||||
for (let tile of this.static_on_tic_tiles) {
|
for (let tile of this.static_on_tic_tiles) {
|
||||||
|
if (tile.type.on_tic) {
|
||||||
tile.type.on_tic(tile, this);
|
tile.type.on_tic(tile, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// Board inspection
|
// Board inspection
|
||||||
@ -1964,6 +1997,7 @@ export class Level extends LevelInterface {
|
|||||||
// The starting cell is iterated last.
|
// The starting cell is iterated last.
|
||||||
*iter_tiles_in_reading_order(start_cell, name, reverse = false) {
|
*iter_tiles_in_reading_order(start_cell, name, reverse = false) {
|
||||||
let i = this.coords_to_scalar(start_cell.x, start_cell.y);
|
let i = this.coords_to_scalar(start_cell.x, start_cell.y);
|
||||||
|
let index = TILE_TYPES[name].layer;
|
||||||
while (true) {
|
while (true) {
|
||||||
if (reverse) {
|
if (reverse) {
|
||||||
i -= 1;
|
i -= 1;
|
||||||
@ -1977,11 +2011,10 @@ export class Level extends LevelInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let cell = this.linear_cells[i];
|
let cell = this.linear_cells[i];
|
||||||
for (let tile of cell) {
|
let tile = cell[index];
|
||||||
if (tile.type.name === name) {
|
if (tile && tile.type.name === name) {
|
||||||
yield tile;
|
yield tile;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (cell === start_cell)
|
if (cell === start_cell)
|
||||||
return;
|
return;
|
||||||
@ -2228,12 +2261,12 @@ export class Level extends LevelInterface {
|
|||||||
|
|
||||||
remove_tile(tile) {
|
remove_tile(tile) {
|
||||||
let cell = tile.cell;
|
let cell = tile.cell;
|
||||||
let index = cell._remove(tile);
|
cell._remove(tile);
|
||||||
this._push_pending_undo(() => cell._add(tile, index));
|
this._push_pending_undo(() => cell._add(tile));
|
||||||
}
|
}
|
||||||
|
|
||||||
add_tile(tile, cell, index = null) {
|
add_tile(tile, cell) {
|
||||||
cell._add(tile, index);
|
cell._add(tile);
|
||||||
this._push_pending_undo(() => cell._remove(tile));
|
this._push_pending_undo(() => cell._remove(tile));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2260,13 +2293,28 @@ export class Level extends LevelInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
transmute_tile(tile, name) {
|
transmute_tile(tile, name) {
|
||||||
let current = tile.type.name;
|
let old_type = tile.type;
|
||||||
this._push_pending_undo(() => tile.type = TILE_TYPES[current]);
|
let new_type = TILE_TYPES[name];
|
||||||
tile.type = TILE_TYPES[name];
|
if (old_type.layer !== new_type.layer) {
|
||||||
|
// Move it to the right layer!
|
||||||
|
let cell = tile.cell;
|
||||||
|
cell._remove(tile);
|
||||||
|
tile.type = new_type;
|
||||||
|
cell._add(tile);
|
||||||
|
this._push_pending_undo(() => {
|
||||||
|
cell._remove(tile);
|
||||||
|
tile.type = old_type;
|
||||||
|
cell._add(tile);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tile.type = new_type;
|
||||||
|
this._push_pending_undo(() => tile.type = old_type);
|
||||||
|
}
|
||||||
|
|
||||||
// For transmuting into an animation, set up the timer immediately
|
// For transmuting into an animation, set up the timer immediately
|
||||||
if (tile.type.ttl) {
|
if (tile.type.ttl) {
|
||||||
if (! TILE_TYPES[current].is_actor) {
|
if (! old_type.is_actor) {
|
||||||
console.warn("Transmuting a non-actor into an animation!");
|
console.warn("Transmuting a non-actor into an animation!");
|
||||||
}
|
}
|
||||||
this._set_tile_prop(tile, 'previous_cell', null);
|
this._set_tile_prop(tile, 'previous_cell', null);
|
||||||
@ -2293,7 +2341,7 @@ export class Level extends LevelInterface {
|
|||||||
let dropped_item;
|
let dropped_item;
|
||||||
if (! tile.type.is_key && actor.toolbelt && actor.toolbelt.length >= 4) {
|
if (! tile.type.is_key && actor.toolbelt && actor.toolbelt.length >= 4) {
|
||||||
let oldest_item_type = TILE_TYPES[actor.toolbelt[0]];
|
let oldest_item_type = TILE_TYPES[actor.toolbelt[0]];
|
||||||
if (oldest_item_type.draw_layer === 0 && cell.get_terrain().type.name !== 'floor') {
|
if (oldest_item_type.layer === LAYERS.terrain && cell.get_terrain().type.name !== 'floor') {
|
||||||
// This is a yellow teleporter, and we are not standing on floor; abort!
|
// This is a yellow teleporter, and we are not standing on floor; abort!
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -2304,7 +2352,7 @@ export class Level extends LevelInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.give_actor(actor, tile.type.name)) {
|
if (this.give_actor(actor, tile.type.name)) {
|
||||||
if (tile.type.draw_layer === 0) {
|
if (tile.type.layer === LAYERS.terrain) {
|
||||||
// This should only happen for the yellow teleporter
|
// This should only happen for the yellow teleporter
|
||||||
this.transmute_tile(tile, 'floor');
|
this.transmute_tile(tile, 'floor');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import * as fflate from 'https://unpkg.com/fflate/esm/index.mjs';
|
import * as fflate from 'https://unpkg.com/fflate/esm/index.mjs';
|
||||||
|
|
||||||
import { DIRECTIONS, TICS_PER_SECOND } from './defs.js';
|
import { DIRECTIONS, LAYERS, TICS_PER_SECOND } from './defs.js';
|
||||||
import { TILES_WITH_PROPS } from './editor-tile-overlays.js';
|
import { TILES_WITH_PROPS } from './editor-tile-overlays.js';
|
||||||
import * as format_base from './format-base.js';
|
import * as format_base from './format-base.js';
|
||||||
import * as c2g from './format-c2g.js';
|
import * as c2g from './format-c2g.js';
|
||||||
@ -472,7 +472,7 @@ class PencilOperation extends DrawOperation {
|
|||||||
let cell = this.cell(x, y);
|
let cell = this.cell(x, y);
|
||||||
cell.length = 0;
|
cell.length = 0;
|
||||||
let type = this.editor.palette_selection.type;
|
let type = this.editor.palette_selection.type;
|
||||||
if (type.draw_layer !== 0) {
|
if (type.layer !== LAYERS.terrain) {
|
||||||
cell.push({type: TILE_TYPES.floor});
|
cell.push({type: TILE_TYPES.floor});
|
||||||
}
|
}
|
||||||
this.editor.place_in_cell(x, y, template);
|
this.editor.place_in_cell(x, y, template);
|
||||||
@ -3111,13 +3111,13 @@ export class Editor extends PrimaryView {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cell[i].type.draw_layer === tile.type.draw_layer) {
|
if (cell[i].type.layer === tile.type.layer) {
|
||||||
cell.splice(i, 1);
|
cell.splice(i, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cell.push(Object.assign({}, tile));
|
cell.push(Object.assign({}, tile));
|
||||||
cell.sort((a, b) => a.type.draw_layer - b.type.draw_layer);
|
cell.sort((a, b) => a.type.layer - b.type.layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
erase_tile(cell, tile = null) {
|
erase_tile(cell, tile = null) {
|
||||||
@ -3127,7 +3127,7 @@ export class Editor extends PrimaryView {
|
|||||||
tile = this.palette_selection;
|
tile = this.palette_selection;
|
||||||
}
|
}
|
||||||
|
|
||||||
let layer = tile.type.draw_layer;
|
let layer = tile.type.layer;
|
||||||
for (let i = cell.length - 1; i >= 0; i--) {
|
for (let i = cell.length - 1; i >= 0; i--) {
|
||||||
// If we find a tile of the same type as the one being drawn, see if it has custom
|
// If we find a tile of the same type as the one being drawn, see if it has custom
|
||||||
// combine behavior (only the case if it came from the palette)
|
// combine behavior (only the case if it came from the palette)
|
||||||
@ -3142,7 +3142,7 @@ export class Editor extends PrimaryView {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cell[i].type.draw_layer === layer) {
|
if (cell[i].type.layer === layer) {
|
||||||
cell.splice(i, 1);
|
cell.splice(i, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2622,6 +2622,7 @@ class PackTestDialog extends DialogOverlay {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
stored_level = pack.load_level(i);
|
stored_level = pack.load_level(i);
|
||||||
|
console.log(i + 1, stored_level.title);
|
||||||
if (! stored_level.has_replay) {
|
if (! stored_level.has_replay) {
|
||||||
record_result('no-replay', "No replay");
|
record_result('no-replay', "No replay");
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { DIRECTIONS, DRAW_LAYERS } from './defs.js';
|
import { DIRECTIONS, LAYERS } from './defs.js';
|
||||||
import { mk } from './util.js';
|
import { mk } from './util.js';
|
||||||
import TILE_TYPES from './tiletypes.js';
|
import TILE_TYPES from './tiletypes.js';
|
||||||
|
|
||||||
@ -157,12 +157,12 @@ export class CanvasRenderer {
|
|||||||
// FIXME this is a bit inefficient when there are a lot of rarely-used layers; consider
|
// FIXME this is a bit inefficient when there are a lot of rarely-used layers; consider
|
||||||
// instead drawing everything under actors, then actors, then everything above actors?
|
// instead drawing everything under actors, then actors, then everything above actors?
|
||||||
// (note: will need to first fix the game to ensure everything is stacked correctly!)
|
// (note: will need to first fix the game to ensure everything is stacked correctly!)
|
||||||
for (let layer = 0; layer < DRAW_LAYERS.MAX; layer++) {
|
for (let layer = 0; layer < LAYERS.MAX; layer++) {
|
||||||
for (let x = xf0; x <= x1; x++) {
|
for (let x = xf0; x <= x1; x++) {
|
||||||
for (let y = yf0; y <= y1; y++) {
|
for (let y = yf0; y <= y1; y++) {
|
||||||
let cell = this.level.cell(x, y);
|
let cell = this.level.cell(x, y);
|
||||||
for (let tile of cell) {
|
let tile = cell[layer];
|
||||||
if (tile.type.draw_layer !== layer)
|
if (! tile)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
let vx, vy;
|
let vx, vy;
|
||||||
@ -187,7 +187,7 @@ export class CanvasRenderer {
|
|||||||
let perception = this.perception;
|
let perception = this.perception;
|
||||||
if (perception !== 'normal' && tile.type.is_actor &&
|
if (perception !== 'normal' && tile.type.is_actor &&
|
||||||
! cell.some(t =>
|
! cell.some(t =>
|
||||||
t.type.draw_layer < layer &&
|
t && t.type.layer < layer &&
|
||||||
! (t.type.name === 'floor' && (t.wire_directions | t.wire_tunnel_directions) === 0)))
|
! (t.type.name === 'floor' && (t.wire_directions | t.wire_tunnel_directions) === 0)))
|
||||||
{
|
{
|
||||||
perception = 'normal';
|
perception = 'normal';
|
||||||
@ -199,7 +199,6 @@ export class CanvasRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (this.show_actor_bboxes && this.level.constructor.name === 'Level') { // FIXME dumb hack so this doesn't happen in editor
|
if (this.show_actor_bboxes && this.level.constructor.name === 'Level') { // FIXME dumb hack so this doesn't happen in editor
|
||||||
this.ctx.fillStyle = '#f004';
|
this.ctx.fillStyle = '#f004';
|
||||||
|
|||||||
427
js/tiletypes.js
427
js/tiletypes.js
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user