Remove Level.cells in favor of linear_cells
This commit is contained in:
parent
2fa35336cb
commit
48f085d0df
@ -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() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
73
js/game.js
73
js/game.js
@ -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];
|
||||||
|
|||||||
@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user