diff --git a/package-lock.json b/package-lock.json index ff1750e..8aea58f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.19.0", "license": "MIT", "dependencies": { - "@wowserhq/format": "^0.13.1" + "@wowserhq/format": "^0.15.0" }, "devDependencies": { "@commitlint/config-conventional": "^18.4.3", @@ -2347,9 +2347,9 @@ } }, "node_modules/@wowserhq/format": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/@wowserhq/format/-/format-0.13.1.tgz", - "integrity": "sha512-FGEDHZK5tZENcpud1P4JvunxpvLfhLY69k5S0ijmRaTmcwaFR06xCS5TA0ORscCFsLmtezpFWOpwUxrNUDmiCg==", + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@wowserhq/format/-/format-0.15.0.tgz", + "integrity": "sha512-n6Dyz3VQAiJPl0yyz//U8FT6MoVlV8HFwwGCy8nrDNtDDFpMcQf0vJOIGxY3LXMoNmCv4yvTpQvzUl9CUmwt+w==", "dependencies": { "@wowserhq/io": "^2.0.2", "gl-matrix": "^3.4.3" diff --git a/package.json b/package.json index 6bc303e..686425b 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "three.js" ], "dependencies": { - "@wowserhq/format": "^0.13.1" + "@wowserhq/format": "^0.15.0" }, "peerDependencies": { "three": "^0.160.0" diff --git a/src/lib/map/MapManager.ts b/src/lib/map/MapManager.ts index dc8024e..86deb20 100644 --- a/src/lib/map/MapManager.ts +++ b/src/lib/map/MapManager.ts @@ -1,5 +1,13 @@ import * as THREE from 'three'; -import { Map, MAP_CHUNK_HEIGHT, MAP_AREA_COUNT_Y } from '@wowserhq/format'; +import { + AreaTableRecord, + ClientDb, + Map, + MAP_AREA_COUNT_Y, + MAP_CHUNK_COUNT_Y, + MAP_CHUNK_COUNT_X, + MAP_CHUNK_HEIGHT, +} from '@wowserhq/format'; import TerrainManager from './terrain/TerrainManager.js'; import TextureManager from '../texture/TextureManager.js'; import DoodadManager from './DoodadManager.js'; @@ -19,7 +27,7 @@ type MapManagerOptions = { viewDistance?: number; }; -class MapManager { +class MapManager extends EventTarget { #mapName: string; #mapDir: string; #map: MapSpec; @@ -39,11 +47,15 @@ class MapManager { #mapLight: MapLight; + #areaTableDb: ClientDb; + #target = new THREE.Vector2(); #targetAreaX: number; #targetAreaY: number; #targetChunkX: number; #targetChunkY: number; + #targetArea: MapAreaSpec; + #targetAreaTableId: number; #viewDistance = DEFAULT_VIEW_DISTANCE; #detailDistance = this.#viewDistance; @@ -54,6 +66,8 @@ class MapManager { #desiredAreas = new Set(); constructor(options: MapManagerOptions) { + super(); + if (options.viewDistance) { this.#viewDistance = options.viewDistance; this.#detailDistance = this.#viewDistance; @@ -105,6 +119,7 @@ class MapManager { this.#root.name = `map:${mapName}`; + this.#loadDbs().catch((error) => console.error(error)); this.#loadMap().catch((error) => console.error(error)); this.#syncAreas().catch((error) => console.error(error)); @@ -118,6 +133,7 @@ class MapManager { const previousAreaY = this.#targetAreaY; const previousChunkX = this.#targetChunkX; const previousChunkY = this.#targetChunkY; + const previousAreaTableId = this.#targetAreaTableId; const { areaX, areaY, chunkX, chunkY } = Map.getIndicesFromPosition(x, y); this.#targetAreaX = areaX; @@ -125,9 +141,27 @@ class MapManager { this.#targetChunkX = chunkX; this.#targetChunkY = chunkY; + const targetArea = this.#loadedAreas.get(this.#getAreaId(areaX, areaY)); + if (targetArea) { + this.#targetArea = targetArea; + + const localChunkX = chunkX % MAP_CHUNK_COUNT_X; + const localChunkY = chunkY % MAP_CHUNK_COUNT_Y; + const localChunkIndex = localChunkX * MAP_CHUNK_COUNT_Y + localChunkY; + const targetAreaTableId = targetArea.areaTableIds[localChunkIndex]; + + if (targetAreaTableId) { + this.#targetAreaTableId = targetAreaTableId; + } + } + if (previousChunkX !== chunkX || previousChunkY !== chunkY) { this.#calculateDesiredAreas(); } + + if (previousAreaTableId !== this.#targetAreaTableId) { + this.#handleAreaTableIdChange(); + } } update(deltaTime: number, camera: THREE.Camera) { @@ -164,6 +198,28 @@ class MapManager { } } + #handleAreaTableIdChange() { + if (!this.#areaTableDb || !this.#targetAreaTableId) { + return; + } + + const areaTableRecord = this.#areaTableDb.getRecord(this.#targetAreaTableId); + if (!areaTableRecord) { + return; + } + + const parentAreaTableRecord = this.#areaTableDb.getRecord(areaTableRecord.parentAreaId); + + const detail = { + areaName: areaTableRecord.areaName[0], + areaId: areaTableRecord.id, + parentAreaName: parentAreaTableRecord?.areaName[0], + parentAreaId: parentAreaTableRecord?.id, + }; + + this.dispatchEvent(new CustomEvent('area:change', { detail })); + } + async #syncAreas() { if (!this.#map) { requestAnimationFrame(() => this.#syncAreas().catch((error) => console.error(error))); @@ -284,6 +340,10 @@ class MapManager { this.#desiredAreas = desiredAreas; } + async #loadDbs() { + this.#areaTableDb = await this.#dbManager.get('AreaTable.dbc', AreaTableRecord); + } + async #loadMap() { const mapPath = `${this.#mapDir}/${this.#mapName}.wdt`; this.#map = await this.#loader.loadMapSpec(mapPath); diff --git a/src/lib/map/loader/MapLoaderWorker.ts b/src/lib/map/loader/MapLoaderWorker.ts index 942f121..bfbb08f 100644 --- a/src/lib/map/loader/MapLoaderWorker.ts +++ b/src/lib/map/loader/MapLoaderWorker.ts @@ -46,8 +46,15 @@ class MapLoaderWorker extends SceneWorker { const buffers = new Set(); + const areaTableIds = new Uint32Array(area.chunks.length); + buffers.add(areaTableIds.buffer); + const terrainSpecs: TerrainSpec[] = []; - for (const chunk of area.chunks) { + for (let i = 0; i < area.chunks.length; i++) { + const chunk = area.chunks[i]; + + areaTableIds[i] = chunk.areaId; + if (chunk.layers.length === 0) { continue; } @@ -70,6 +77,7 @@ class MapLoaderWorker extends SceneWorker { const spec: MapAreaSpec = { terrain: terrainSpecs, + areaTableIds, doodadDefs: area.doodadDefs.map((def) => ({ id: def.id, name: def.name, diff --git a/src/lib/map/loader/types.ts b/src/lib/map/loader/types.ts index 492039e..d4922a3 100644 --- a/src/lib/map/loader/types.ts +++ b/src/lib/map/loader/types.ts @@ -40,6 +40,7 @@ type TerrainSpec = { type MapAreaSpec = { terrain: TerrainSpec[]; + areaTableIds: Uint32Array; doodadDefs: MapDoodadDefSpec[]; };