summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMatthias Schiffer <mschiffer@universe-factory.net>2018-11-21 21:44:11 +0100
committerMatthias Schiffer <mschiffer@universe-factory.net>2018-11-21 21:44:11 +0100
commit2c6cd362c32a2e9460e5ddebd85f050b9b5ab4e4 (patch)
tree7b02c4094968629dda47ccab5eae1c49314ffb87 /src
parent4443ca205812f4da4aa0ed1bf4897e731107616b (diff)
downloadrpgedit-2c6cd362c32a2e9460e5ddebd85f050b9b5ab4e4.tar
rpgedit-2c6cd362c32a2e9460e5ddebd85f050b9b5ab4e4.zip
Allow using animated entities as map tiles
A more optimized implementation for animated tiles is planned.
Diffstat (limited to 'src')
-rw-r--r--src/view/map.ts127
1 files changed, 103 insertions, 24 deletions
diff --git a/src/view/map.ts b/src/view/map.ts
index c796c6f..5eedaca 100644
--- a/src/view/map.ts
+++ b/src/view/map.ts
@@ -1,3 +1,4 @@
+import { EntityView } from './entity';
import { Renderer } from './renderer/renderer';
import { SpriteCoords, SpriteView, SpriteViewBuilder } from './sprite';
import { loadImage, mkTexture } from './util/image';
@@ -6,39 +7,91 @@ import { MapData } from '../model/data/map';
import { nextPowerOf2 } from '../util';
+import { vec2 } from 'gl-matrix';
+
+interface StaticMapTile {
+ type: 'static';
+ image: HTMLImageElement;
+}
+
+interface EntityTile {
+ type: 'entity';
+ entity: EntityView;
+}
+
+type MapTile = StaticMapTile | EntityTile;
+
+interface StaticTilesetTile {
+ type: 'static';
+ coords: SpriteCoords;
+}
+
+type TilesetTile = StaticTilesetTile | EntityTile;
+
interface Tileset {
texture: WebGLTexture;
- tiles: SpriteCoords[];
+ tiles: TilesetTile[];
}
-function loadTiles(tiles: string[]): Promise<HTMLImageElement[]> {
- return Promise.all(tiles.map((t) => loadImage(`resources/sprite/tile/${t}.png`)));
+async function loadTile(r: Renderer, tile: string): Promise<MapTile> {
+ const name = tile.substr(1);
+ switch (tile[0]) {
+ case '-':
+ return {
+ type: 'static',
+ image: await loadImage(`resources/sprite/tile/${name}.png`),
+ };
+
+ case '@':
+ return {
+ type: 'entity',
+ entity: await EntityView.load(r, name),
+ };
+
+ default:
+ throw new Error('invalid tile specifier');
+ }
+}
+
+function loadTiles(r: Renderer, tiles: string[]): Promise<MapTile[]> {
+ return Promise.all(tiles.map((tile) => loadTile(r, tile)));
}
function mkTileset(
r: Renderer,
- tiles: HTMLImageElement[],
+ mapTiles: MapTile[],
): Tileset {
const tileSize = 32;
- const canvasDim = nextPowerOf2(Math.sqrt(tiles.length));
+ const canvasDim = nextPowerOf2(Math.sqrt(mapTiles.length));
const canvasSize = canvasDim * tileSize;
const canvas = document.createElement('canvas');
canvas.width = canvas.height = canvasSize;
let x = 0, y = 0;
- const tileCoords: SpriteCoords[] = [];
+ const tiles: TilesetTile[] = [];
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
- for (const tile of tiles) {
- ctx.drawImage(tile, x * tileSize, y * tileSize);
- tileCoords.push([x / canvasDim, y / canvasDim, (x + 1) / canvasDim, (y + 1) / canvasDim]);
-
- x++;
- if (x === canvasDim) {
- x = 0;
- y++;
+ for (const tile of mapTiles) {
+ switch (tile.type) {
+ case 'static':
+ ctx.drawImage(tile.image, x * tileSize, y * tileSize);
+ tiles.push({
+ type: 'static',
+ coords: [x / canvasDim, y / canvasDim, (x + 1) / canvasDim, (y + 1) / canvasDim],
+ });
+
+ x++;
+ if (x === canvasDim) {
+ x = 0;
+ y++;
+ }
+ break;
+
+ case 'entity':
+ tiles.push(tile);
+ break;
}
}
@@ -46,40 +99,66 @@ function mkTileset(
return {
texture,
- tiles: tileCoords,
+ tiles,
};
}
-function addSprite(builder: SpriteViewBuilder, tileset: Tileset, x: number, y: number, tile: number) {
- if (tile === 0)
+function addSprite(
+ builder: SpriteViewBuilder,
+ entityTiles: Array<[[number, number], EntityView]>,
+ tileset: Tileset,
+ x: number,
+ y: number,
+ tile: number) {
+ if (!tile)
return;
- const tilePos = tileset.tiles[tile - 1];
- builder.addSprite([x, y, x + 1, y + 1], tilePos);
+ const tilesetTile = tileset.tiles[tile - 1];
+
+ switch (tilesetTile.type) {
+ case 'static':
+ builder.addSprite([x, y, x + 1, y + 1], tilesetTile.coords);
+ break;
+
+ case 'entity':
+ entityTiles.push([[x + 0.5, y + 0.5], tilesetTile.entity]);
+ break;
+ }
}
function buildMapLayer(r: Renderer, tileset: Tileset, layer: number[][]): MapLayerView {
const builder = new SpriteViewBuilder(r, tileset.texture);
+ const entityTiles: Array<[[number, number], EntityView]> = [];
for (let x = 0; x < layer[0].length; x++)
for (let y = 0; y < layer.length; y++)
- addSprite(builder, tileset, x, y, layer[y][x]);
+ addSprite(builder, entityTiles, tileset, x, y, layer[y][x]);
- return new MapLayerView(builder.build());
+ return new MapLayerView(r, builder.build(), entityTiles);
}
class MapLayerView {
- public constructor(private sprites: SpriteView) {
+ public constructor(
+ private r: Renderer,
+ private staticTiles: SpriteView,
+ private entityTiles: Array<[[number, number], EntityView]>,
+ ) {
}
public render(time: number): void {
- this.sprites.render();
+ this.r.setTranslation([0, 0]);
+ this.staticTiles.render();
+
+ for (const [coords, entity] of this.entityTiles) {
+ this.r.setTranslation(coords);
+ entity.renderByTime(time);
+ }
}
}
export class MapView {
public static async load(r: Renderer, map: MapData): Promise<MapView> {
- const tiles = await loadTiles(map.tiles);
+ const tiles = await loadTiles(r, map.tiles);
const tileset = mkTileset(r, tiles);
const layers = map.layers.map((layer) => buildMapLayer(r, tileset, layer.tiles));