Remove Level.cells in favor of linear_cells

This commit is contained in:
Eevee (Evelyn Woods) 2020-12-17 15:51:57 -07:00
parent 2fa35336cb
commit 48f085d0df
6 changed files with 83 additions and 110 deletions

View File

@ -48,8 +48,37 @@ export class Replay {
} }
} }
export class StoredLevel { // Small shared helper methods for navigating a StoredLevel or Level
export class LevelInterface {
// Expected attributes:
// .size_x
// .size_y
// .linear_cells
scalar_to_coords(n) {
return [n % this.size_x, Math.floor(n / this.size_x)];
}
coords_to_scalar(x, y) {
return x + y * this.size_x;
}
is_point_within_bounds(x, y) {
return (x >= 0 && x < this.size_x && y >= 0 && y < this.size_y);
}
cell(x, y) {
if (this.is_point_within_bounds(x, y)) {
return this.linear_cells[this.coords_to_scalar(x, y)];
}
else {
return null;
}
}
}
export class StoredLevel extends LevelInterface {
constructor(number) { constructor(number) {
super();
// TODO still not sure this belongs here // TODO still not sure this belongs here
this.number = number; // one-based this.number = number; // one-based
this.title = ''; this.title = '';
@ -88,14 +117,6 @@ export class StoredLevel {
this.camera_regions = []; this.camera_regions = [];
} }
scalar_to_coords(n) {
return [n % this.size_x, Math.floor(n / this.size_x)];
}
coords_to_scalar(x, y) {
return x + y * this.size_x;
}
check() { check() {
} }

View File

@ -1,4 +1,5 @@
import { DIRECTIONS, DIRECTION_ORDER, INPUT_BITS, TICS_PER_SECOND } from './defs.js'; import { DIRECTIONS, DIRECTION_ORDER, INPUT_BITS, TICS_PER_SECOND } from './defs.js';
import { LevelInterface } from './format-base.js';
import TILE_TYPES from './tiletypes.js'; import TILE_TYPES from './tiletypes.js';
export class Tile { export class Tile {
@ -301,8 +302,9 @@ Cell.prototype.powered_edges = 0;
// sitting completely idle, undo consumes about 2 MB every five seconds, so this shouldn't go beyond // sitting completely idle, undo consumes about 2 MB every five seconds, so this shouldn't go beyond
// 12 MB for any remotely reasonable level. // 12 MB for any remotely reasonable level.
const UNDO_BUFFER_SIZE = TICS_PER_SECOND * 30; const UNDO_BUFFER_SIZE = TICS_PER_SECOND * 30;
export class Level { export class Level extends LevelInterface {
constructor(stored_level, compat = {}) { constructor(stored_level, compat = {}) {
super();
this.stored_level = stored_level; this.stored_level = stored_level;
this.restart(compat); this.restart(compat);
} }
@ -322,7 +324,7 @@ export class Level {
this.size_x = this.stored_level.size_x; this.size_x = this.stored_level.size_x;
this.size_y = this.stored_level.size_y; this.size_y = this.stored_level.size_y;
this.cells = []; this.linear_cells = [];
this.player = null; this.player = null;
this.p1_input = 0; this.p1_input = 0;
this.p1_released = 0xff; this.p1_released = 0xff;
@ -380,10 +382,10 @@ export class Level {
// - 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++) {
let row = []; let row = [];
this.cells.push(row);
for (let x = 0; x < this.width; x++) { for (let x = 0; x < this.width; x++) {
let cell = new Cell(x, y); let cell = new Cell(x, y);
row.push(cell); row.push(cell);
this.linear_cells.push(cell);
let stored_cell = this.stored_level.linear_cells[n]; let stored_cell = this.stored_level.linear_cells[n];
n++; n++;
@ -444,7 +446,7 @@ export class Level {
} }
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.cells[ty][tx]) { for (let tile of this.cell(tx, ty)) {
if (goals === tile.type.name) { if (goals === tile.type.name) {
connectable.connection = tile; connectable.connection = tile;
break; break;
@ -483,8 +485,7 @@ export class Level {
} }
// Finally, let all tiles do any custom init behavior // Finally, let all tiles do any custom init behavior
for (let row of this.cells) { for (let cell of this.linear_cells) {
for (let cell of row) {
for (let tile of cell) { for (let tile of cell) {
if (tile.type.on_ready) { if (tile.type.on_ready) {
tile.type.on_ready(tile, this); tile.type.on_ready(tile, this);
@ -494,7 +495,6 @@ export class Level {
} }
} }
} }
}
// Erase undo, in case any on_ready added to it (we don't want to undo initialization!) // Erase undo, in case any on_ready added to it (we don't want to undo initialization!)
this.pending_undo = this.create_undo_entry(); this.pending_undo = this.create_undo_entry();
} }
@ -1453,14 +1453,10 @@ export class Level {
return; 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 for (let cell of this.linear_cells) {
// like hell to keep updated though)
for (let row of this.cells) {
for (let cell of row) {
cell.prev_powered_edges = cell.powered_edges; cell.prev_powered_edges = cell.powered_edges;
cell.powered_edges = 0; cell.powered_edges = 0;
} }
}
// Iterate over emitters and flood-fill outwards one edge at a time // 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
@ -1519,10 +1515,9 @@ export class Level {
while (true) { while (true) {
x += dirinfo.movement[0]; x += dirinfo.movement[0];
y += dirinfo.movement[1]; y += dirinfo.movement[1];
if (! this.is_point_within_bounds(x, y)) let candidate = this.cell(x, y);
if (! candidate)
break; break;
let candidate = this.cells[y][x];
neighbor_wire = candidate.get_wired_tile(); neighbor_wire = candidate.get_wired_tile();
if (neighbor_wire) { if (neighbor_wire) {
if ((neighbor_wire.wire_tunnel_directions ?? 0) & opposite_bit) { if ((neighbor_wire.wire_tunnel_directions ?? 0) & opposite_bit) {
@ -1559,8 +1554,7 @@ 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 cell of this.linear_cells) {
for (let cell of row) {
if ((cell.prev_powered_edges === 0) !== (cell.powered_edges === 0)) { if ((cell.prev_powered_edges === 0) !== (cell.powered_edges === 0)) {
let method = cell.powered_edges ? 'on_power' : 'on_depower'; let method = cell.powered_edges ? 'on_power' : 'on_depower';
for (let tile of cell) { for (let tile of cell) {
@ -1571,7 +1565,6 @@ export class Level {
} }
} }
} }
}
// Performs a depth-first search for connected wires and wire objects, extending out from the // Performs a depth-first search for connected wires and wire objects, extending out from the
// given starting cell // given starting cell
@ -1581,53 +1574,30 @@ export class Level {
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Board inspection // Board inspection
is_point_within_bounds(x, y) {
return (x >= 0 && x < this.width && y >= 0 && y < this.height);
}
cell(x, y) {
if (this.is_point_within_bounds(x, y)) {
return this.cells[y][x];
}
else {
return null;
}
}
get_neighboring_cell(cell, direction) { get_neighboring_cell(cell, direction) {
let move = DIRECTIONS[direction].movement; let move = DIRECTIONS[direction].movement;
let goal_x = cell.x + move[0]; let goal_x = cell.x + move[0];
let goal_y = cell.y + move[1]; let goal_y = cell.y + move[1];
if (this.is_point_within_bounds(goal_x, goal_y)) { return this.cell(cell.x + move[0], cell.y + move[1]);
return this.cells[goal_y][goal_x];
}
else {
return null;
}
} }
// Iterates over the grid in (reverse?) reading order and yields all tiles with the given name. // Iterates over the grid in (reverse?) reading order and yields all tiles with the given name.
// 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 x = start_cell.x; let i = this.coords_to_scalar(start_cell.x, start_cell.y);
let y = start_cell.y;
while (true) { while (true) {
if (reverse) { if (reverse) {
x -= 1; i -= 1;
if (x < 0) { if (i < 0) {
x = this.width - 1; i += this.size_x * this.size_y;
y = (y - 1 + this.height) % this.height;
} }
} }
else { else {
x += 1; i += 1;
if (x >= this.width) { i %= this.size_x * this.size_y;
x = 0;
y = (y + 1) % this.height;
}
} }
let cell = this.cells[y][x]; let cell = this.linear_cells[i];
for (let tile of cell) { for (let tile of cell) {
if (tile.type.name === name) { if (tile.type.name === name) {
yield tile; yield tile;
@ -1649,8 +1619,9 @@ export class Level {
let sy = start_cell.y; let sy = start_cell.y;
for (let direction of [[-1, -1], [-1, 1], [1, 1], [1, -1]]) { for (let direction of [[-1, -1], [-1, 1], [1, 1], [1, -1]]) {
for (let i = 0; i < dist; i++) { for (let i = 0; i < dist; i++) {
if (this.is_point_within_bounds(sx, sy)) { let cell = this.cell(sx, sy);
yield this.cells[sy][sx]; if (cell) {
yield cell;
} }
sx += direction[0]; sx += direction[0];
sy += direction[1]; sy += direction[1];

View File

@ -243,7 +243,6 @@ class EditorLevelBrowserOverlay extends DialogOverlay {
let index = this.awaiting_renders.shift(); let index = this.awaiting_renders.shift();
let element = this.list.childNodes[index]; let element = this.list.childNodes[index];
let stored_level = this.conductor.stored_game.load_level(index); let stored_level = this.conductor.stored_game.load_level(index);
this.conductor.editor._xxx_update_stored_level_cells(stored_level);
this.renderer.set_level(stored_level); this.renderer.set_level(stored_level);
this.renderer.set_viewport_size(stored_level.size_x, stored_level.size_y); this.renderer.set_viewport_size(stored_level.size_x, stored_level.size_y);
this.renderer.draw(); this.renderer.draw();
@ -1851,26 +1850,11 @@ export class Editor extends PrimaryView {
load_game(stored_game) { load_game(stored_game) {
} }
_xxx_update_stored_level_cells(stored_level) {
// XXX need this for renderer compat, not used otherwise, PLEASE delete
stored_level.cells = [];
let row;
for (let [i, cell] of stored_level.linear_cells.entries()) {
if (i % stored_level.size_x === 0) {
row = [];
stored_level.cells.push(row);
}
row.push(cell);
}
}
load_level(stored_level) { load_level(stored_level) {
// TODO support a game too i guess // TODO support a game too i guess
this.stored_level = stored_level; this.stored_level = stored_level;
this.update_viewport_size(); this.update_viewport_size();
this._xxx_update_stored_level_cells(this.stored_level);
// Load connections // Load connections
this.connections_g.textContent = ''; this.connections_g.textContent = '';
for (let [src, dest] of Object.entries(this.stored_level.custom_trap_wiring)) { for (let [src, dest] of Object.entries(this.stored_level.custom_trap_wiring)) {
@ -2100,7 +2084,6 @@ export class Editor extends PrimaryView {
this.stored_level.linear_cells = new_cells; this.stored_level.linear_cells = new_cells;
this.stored_level.size_x = size_x; this.stored_level.size_x = size_x;
this.stored_level.size_y = size_y; this.stored_level.size_y = size_y;
this._xxx_update_stored_level_cells(this.stored_level);
this.update_viewport_size(); this.update_viewport_size();
this.renderer.draw(); this.renderer.draw();
} }

View File

@ -411,7 +411,7 @@ class Player extends PrimaryView {
return; return;
let [x, y] = this.renderer.cell_coords_from_event(ev); let [x, y] = this.renderer.cell_coords_from_event(ev);
this.level.move_to(this.level.player, this.level.cells[y][x], 1); this.level.move_to(this.level.player, this.level.cell(x, y), 1);
// TODO this behaves a bit weirdly when paused (doesn't redraw even with a force), i // TODO this behaves a bit weirdly when paused (doesn't redraw even with a force), i
// think because we're still claiming a speed of 1 so time has to pass before the move // think because we're still claiming a speed of 1 so time has to pass before the move
// actually "happens" // actually "happens"

View File

@ -147,7 +147,7 @@ export class CanvasRenderer {
for (let layer = 0; layer < DRAW_LAYERS.MAX; layer++) { for (let layer = 0; layer < DRAW_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++) {
for (let tile of this.level.cells[y][x]) { for (let tile of this.level.cell(x, y)) {
if (tile.type.draw_layer !== layer) if (tile.type.draw_layer !== layer)
continue; continue;
@ -185,11 +185,11 @@ export class CanvasRenderer {
} }
} }
if (this.show_actor_bboxes && ! this.level.linear_cells) { // 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';
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 actor = this.level.cells[y][x].get_actor(); let actor = this.level.cell(x, y).get_actor();
if (! actor) if (! actor)
continue; continue;
let [vx, vy] = actor.visual_position(tic_offset); let [vx, vy] = actor.visual_position(tic_offset);

View File

@ -1412,8 +1412,7 @@ const TILE_TYPES = {
do_button(level) { do_button(level) {
// Swap green floors and walls // Swap green floors and walls
// TODO could probably make this more compact for undo purposes // TODO could probably make this more compact for undo purposes
for (let row of level.cells) { for (let cell of level.linear_cells) {
for (let cell of row) {
for (let tile of cell) { for (let tile of cell) {
if (tile.type.name === 'green_floor') { if (tile.type.name === 'green_floor') {
level.transmute_tile(tile, 'green_wall'); level.transmute_tile(tile, 'green_wall');
@ -1429,7 +1428,6 @@ const TILE_TYPES = {
} }
} }
} }
}
}, },
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);
@ -1558,7 +1556,7 @@ const TILE_TYPES = {
for (let x = Math.max(0, me.cell.x - 2); x <= Math.min(level.width - 1, me.cell.x + 2); x++) { for (let x = Math.max(0, me.cell.x - 2); x <= Math.min(level.width - 1, me.cell.x + 2); x++) {
for (let y = Math.max(0, me.cell.y - 2); y <= Math.min(level.height - 1, me.cell.y + 2); y++) { for (let y = Math.max(0, me.cell.y - 2); y <= Math.min(level.height - 1, me.cell.y + 2); y++) {
let cell = level.cells[y][x]; let cell = level.cell(x, y);
// TODO wait is this right // TODO wait is this right
if (cell === me.cell) if (cell === me.cell)
continue; continue;