// NOTE: It's super not clear how much of this should be in rust vs // javascript. Dealing with the level code is really nice in javascript // but there's a bunch of weird stuff that really feels lower level. import { load_texture } from "./assets"; import { Texture, print, spr, use_texture } from "./graphics"; import { load_string } from "./io"; // TODO: Use io-ts? YIKES. export type Tile = { px: [number, number]; src: [number, number]; f: number; t: number; a: number; }; export type TileLayer = { type: "tile"; texture: Texture; grid_size: number; offset: [number, number]; tiles: Tile[]; }; export type Entity = { type: string; id: string; px: [number, number]; bounds: [number, number]; // TODO: More props here. }; export type EntityLayer = { type: "entity"; entities: Entity[]; }; export type Level = { world_x: number; world_y: number; width: number; height: number; tile_layers: TileLayer[]; entity_layers: EntityLayer[]; }; export type TileSet = { id: number; texture: Texture }; export type World = { levels: Level[]; tilesets: Map }; async function load_tileset(def: { uid: number; relPath: string; }): Promise { let relPath = def.relPath as string; if (relPath.endsWith(".aseprite")) { // Whoops let's load the export instead? relPath = relPath.substring(0, relPath.length - 8) + "png"; } let texture = await load_texture(relPath); print("Loaded tileset", def.uid, "from", relPath, "as ID", texture.id()); return { id: def.uid, texture }; } type TileLayerInstance = { __type: "Tiles"; __gridSize: number; __pxTotalOffsetX: number; __pxTotalOffsetY: number; __tilesetDefUid: number; gridTiles: { px: [number, number]; src: [number, number]; f: number; t: number; a: number; }[]; }; function load_tile_layer_instance( tile_sets: Map, li: TileLayerInstance ): TileLayer { const tileset = tile_sets.get(li.__tilesetDefUid); if (!tileset) { throw new Error("Unable to find texture!!! " + li.__tilesetDefUid); } return { type: "tile", texture: tileset.texture, grid_size: li.__gridSize, offset: [li.__pxTotalOffsetX, li.__pxTotalOffsetY], tiles: li.gridTiles, }; } type EntityLayerInstance = { __type: "Entities"; entityInstances: { __identifier: string; iid: string; width: number; height: number; px: [number, number]; }[]; }; function load_entity_layer_instance(li: EntityLayerInstance): EntityLayer { return { type: "entity", entities: li.entityInstances.map((ei) => { return { type: ei.__identifier, id: ei.iid, px: ei.px, bounds: [ei.width, ei.height], }; }), }; } type LayerInstance = TileLayerInstance | EntityLayerInstance; function is_tile_layer_instance(x: LayerInstance): x is TileLayerInstance { return x.__type == "Tiles"; } function is_entity_layer_instance(x: LayerInstance): x is EntityLayerInstance { return x.__type == "Entities"; } function load_level( tile_sets: Map, def: { worldX: number; worldY: number; pxWid: number; pxHei: number; layerInstances: LayerInstance[]; } ): Level { const result: Level = { world_x: def.worldX, world_y: def.worldY, width: def.pxWid, height: def.pxHei, tile_layers: [], entity_layers: [], }; for (const li of def.layerInstances) { if (is_tile_layer_instance(li)) { result.tile_layers.push(load_tile_layer_instance(tile_sets, li)); } else if (is_entity_layer_instance(li)) { result.entity_layers.push(load_entity_layer_instance(li)); } } return result; } export async function load_world(path: string): Promise { print("Loading map:", path); const blob = await load_string(path); const map = JSON.parse(blob); const tilesets = new Map(); let loaded_tilesets = await Promise.all( map.defs.tilesets .filter((def: any) => def.relPath != null) .map((def: any) => load_tileset(def)) ); for (const ts of loaded_tilesets) { tilesets.set(ts.id, ts); } const levels = map.levels.map((l: any) => load_level(tilesets, l)); return { levels, tilesets }; } export function draw_level( level: Level, offset_x: number = 0, offset_y: number = 0 ) { for (const layer of level.tile_layers) { use_texture(layer.texture); let [ofx, ofy] = layer.offset; ofx += offset_x; ofy += offset_y; for (const tile of layer.tiles) { // TODO: Flip and whatnot. spr( tile.px[0] + ofx, tile.px[1] + ofy, layer.grid_size, layer.grid_size, tile.src[0], tile.src[1] ); } } }