Class-based rendering
This commit is contained in:
76
ts/grid.ts
Normal file
76
ts/grid.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { Layer } from './layer.js';
|
||||
import { Tile } from './tile.js';
|
||||
|
||||
export class Grid {
|
||||
#prnt: HTMLElement;
|
||||
#tileset: string;
|
||||
#layers: Map<string, Layer> = new Map<string, Layer>();
|
||||
#size_x: number;
|
||||
#size_y: number;
|
||||
|
||||
constructor(prnt: HTMLElement) {
|
||||
this.#prnt = prnt;
|
||||
this.#prnt.style.display = 'grid';
|
||||
}
|
||||
|
||||
set_size(x: number, y: number) {
|
||||
this.#size_x = x;
|
||||
this.#size_y = y;
|
||||
|
||||
// TODO: Notify layers if shrink
|
||||
|
||||
this.#prnt.style.gridTemplateColumns = `repeat(${x}, 1fr)`;
|
||||
this.#prnt.style.gridTemplateRows = `repeat(${y}, 1fr)`;
|
||||
|
||||
// TODO: Notify layers if expand
|
||||
}
|
||||
|
||||
set_tileset(set: string) {
|
||||
this.#tileset = set;
|
||||
this.#prnt.style.backgroundImage = this.get_url('land');
|
||||
|
||||
// TODO: Notify layers
|
||||
}
|
||||
|
||||
set_layers(layers: string[]) {
|
||||
const newNames: Set<string> = new Set<string>(layers);
|
||||
|
||||
for (const name of newNames) {
|
||||
if (!this.#layers.has(name)) {
|
||||
const layer = new Layer(this.#size_x, this.#size_y);
|
||||
layer.set_tileset(this.#tileset);
|
||||
this.#layers.set(name, layer);
|
||||
}
|
||||
}
|
||||
|
||||
for (const name of this.#layers.keys()) {
|
||||
if (!newNames.has(name)) {
|
||||
// TODO: Notify layer to tear down
|
||||
this.#layers.delete(name);
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < layers.length; i++) {
|
||||
const name = layers[i];
|
||||
const level = i + 1;
|
||||
const layer = this.#layers.get(name)!;
|
||||
layer.set_level(level);
|
||||
}
|
||||
}
|
||||
|
||||
add_tile(layer: string, tile: Tile, x: number, y: number): boolean {
|
||||
const elem = this.#layers.get(layer)?.add_tile(tile, x, y);
|
||||
if (!elem) {
|
||||
return false;
|
||||
}
|
||||
// Grids are 1-indexed
|
||||
elem.style.gridColumnStart = `${x + 1}`;
|
||||
elem.style.gridRowStart = `${y + 1}`;
|
||||
this.#prnt.appendChild(elem);
|
||||
return true;
|
||||
}
|
||||
|
||||
private get_url(tile: string) {
|
||||
return `url("images/${this.#tileset}/${tile}.svg")`;
|
||||
}
|
||||
}
|
||||
85
ts/layer.ts
Normal file
85
ts/layer.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { Tile } from './tile.js';
|
||||
|
||||
export class Layer {
|
||||
#size_x: number;
|
||||
#size_y: number;
|
||||
#level: number;
|
||||
#tileset: string;
|
||||
|
||||
#occupied: boolean[][];
|
||||
|
||||
constructor(size_x: number, size_y: number) {
|
||||
this.#size_x = size_x;
|
||||
this.#size_y = size_y;
|
||||
|
||||
this.#occupied = [];
|
||||
|
||||
for (let xi = 0; xi < this.#size_x; xi++) {
|
||||
this.#occupied.push(Array(this.#size_y).fill(false));
|
||||
}
|
||||
}
|
||||
|
||||
set_level(level: number) {
|
||||
this.#level = level;
|
||||
}
|
||||
|
||||
set_tileset(tileset: string) {
|
||||
this.#tileset = tileset;
|
||||
}
|
||||
|
||||
add_tile(tile: Tile, x: number, y: number): HTMLElement | undefined {
|
||||
if (!this.can_add(tile, x, y)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
this.mark_occupied(tile, x, y);
|
||||
|
||||
const elem = tile.get_elem(this.#tileset);
|
||||
elem.style.zIndex = `${this.#level}`;
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
private can_add(tile: Tile, x: number, y: number) {
|
||||
const mask = tile.get_mask();
|
||||
|
||||
for (let xi = 0; xi < mask.length; xi++) {
|
||||
if (x + xi >= this.#occupied.length) {
|
||||
// Shape goes off grid (x)
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let yi = 0; yi < mask[xi].length; yi++) {
|
||||
if (!mask[xi][yi]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (y + yi >= this.#occupied[x + xi].length) {
|
||||
// Shape goes off grid (y)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.#occupied[x + xi][y + yi]) {
|
||||
// Conflicts
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private mark_occupied(tile: Tile, x: number, y: number) {
|
||||
const mask = tile.get_mask();
|
||||
|
||||
for (let xi = 0; xi < mask.length; xi++) {
|
||||
for (let yi = 0; yi < mask[xi].length; yi++) {
|
||||
if (!mask[xi][yi]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.#occupied[x + xi][y + yi] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
ts/tile.ts
Normal file
31
ts/tile.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
export class Tile {
|
||||
#name: string;
|
||||
#width: number;
|
||||
#height: number;
|
||||
#mask: boolean[][];
|
||||
|
||||
constructor(name: string, width: number, height: number, mask: boolean[][]) {
|
||||
this.#name = name;
|
||||
this.#width = width;
|
||||
this.#height = height;
|
||||
this.#mask = mask;
|
||||
}
|
||||
|
||||
static rectangle(name: string, width: number, height: number): Tile {
|
||||
const mask = Array(width).fill(Array(height).fill(true));
|
||||
return new Tile(name, width, height, mask);
|
||||
}
|
||||
|
||||
get_mask(): boolean[][] {
|
||||
return this.#mask;
|
||||
}
|
||||
|
||||
get_elem(tileset: string): HTMLElement {
|
||||
const elem = document.createElement('div');
|
||||
elem.style.backgroundImage = `url("images/${tileset}/${this.#name}.svg")`;
|
||||
elem.style.backgroundSize = 'cover';
|
||||
elem.style.gridColumnEnd = `span ${this.#width}`;
|
||||
elem.style.gridRowEnd = `span ${this.#height}`;
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
23
ts/tiles.ts
Normal file
23
ts/tiles.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Tile } from './tile.js';
|
||||
|
||||
// Straight
|
||||
export const ROAD_LR = Tile.rectangle('road-lr', 6, 4);
|
||||
export const ROAD_TB = Tile.rectangle('road-tb', 4, 6);
|
||||
|
||||
// Elbow
|
||||
export const ROAD_BL = Tile.rectangle('road-bl', 6, 6);
|
||||
export const ROAD_BR = Tile.rectangle('road-br', 6, 6);
|
||||
export const ROAD_TL = Tile.rectangle('road-tl', 6, 6);
|
||||
export const ROAD_TR = Tile.rectangle('road-tr', 6, 6);
|
||||
|
||||
// T
|
||||
export const ROAD_BLR = Tile.rectangle('road-blr', 8, 6);
|
||||
export const ROAD_TLR = Tile.rectangle('road-tlr', 8, 6);
|
||||
export const ROAD_LTB = Tile.rectangle('road-ltb', 6, 8);
|
||||
export const ROAD_RTB = Tile.rectangle('road-rtb', 6, 8);
|
||||
|
||||
// +
|
||||
export const ROAD_TBLR = Tile.rectangle('road-tblr', 8, 8);
|
||||
|
||||
// Tower base
|
||||
export const EMPTY = Tile.rectangle('empty', 4, 2);
|
||||
22
ts/tower.ts
22
ts/tower.ts
@@ -1,4 +1,5 @@
|
||||
import { TowerMap } from './tower_map.js';
|
||||
import { Grid } from './grid.js';
|
||||
import * as tiles from './tiles.js';
|
||||
|
||||
export function main() {
|
||||
document.body.style.margin = '0';
|
||||
@@ -9,9 +10,22 @@ export function main() {
|
||||
container.style.width = '100vmin';
|
||||
container.style.height = '100vmin';
|
||||
|
||||
const map = new TowerMap(container);
|
||||
map.set_size(20, 20);
|
||||
map.set_tileset('tropical');
|
||||
const grid = new Grid(container);
|
||||
grid.set_size(40, 40);
|
||||
grid.set_tileset('tropical');
|
||||
grid.set_layers(['water', 'road']);
|
||||
grid.add_tile('road', tiles.ROAD_LR, 0, 2);
|
||||
grid.add_tile('road', tiles.ROAD_BL, 6, 2);
|
||||
grid.add_tile('road', tiles.ROAD_TB, 8, 8);
|
||||
grid.add_tile('road', tiles.ROAD_TR, 8, 14);
|
||||
grid.add_tile('road', tiles.ROAD_TL, 14, 14);
|
||||
grid.add_tile('road', tiles.ROAD_BR, 16, 8);
|
||||
grid.add_tile('road', tiles.ROAD_LTB, 22, 6);
|
||||
grid.add_tile('road', tiles.ROAD_BLR, 22, 0);
|
||||
grid.add_tile('road', tiles.ROAD_TLR, 22, 14);
|
||||
grid.add_tile('road', tiles.ROAD_RTB, 34, 32);
|
||||
grid.add_tile('road', tiles.ROAD_TBLR, 30, 14);
|
||||
grid.add_tile('road', tiles.EMPTY, 14, 6);
|
||||
};
|
||||
|
||||
main();
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
export class TowerMap {
|
||||
#prnt: HTMLElement;
|
||||
#tileset: string;
|
||||
|
||||
constructor(prnt: HTMLElement) {
|
||||
this.#prnt = prnt;
|
||||
this.#prnt.style.display = 'grid';
|
||||
}
|
||||
|
||||
set_size(x: number, y: number) {
|
||||
this.#prnt.style.gridTemplateColumns = `repeat(${x}, 1fr)`;
|
||||
this.#prnt.style.gridTemplateRows = `repeat(${y}, 1fr)`;
|
||||
}
|
||||
|
||||
set_tileset(set: string) {
|
||||
this.#tileset = set;
|
||||
this.#prnt.style.backgroundImage = this.get_url('land');
|
||||
}
|
||||
|
||||
private get_url(tile: string) {
|
||||
return `url("images/${this.#tileset}/${tile}.svg")`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user