/
docNemo
/
hex-map-editor
Обзор
Документация
Войти
/
docNemo
/
hex-map-editor
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
5
CI/CD
Аналитика
Безопасность
main
src/foundry/session.ts
126 строк
5 KB
docNemo
feat(layout)!: ориентация flat-top вместо pointy-top
03 авг 2026, 15:19
03 авг 2026, 15:19
c0336aa
Код
Авторство
О чём код?
import type { AssetCatalog } from '@core/asset-manifest'; import { logger } from '@core/log'; import type { HexMap } from '@core/map-model'; import type { HexMapMeta } from '@core/map-storage'; import { hexHeightFromGridSize } from '@core/layout'; import { HexMapLayer } from '@render/hex-layer'; import { TextureCache } from '@render/textures'; import { loadMapFromScene, saveMapToScene, type SceneLike } from './scene-storage'; /** * Состояние модуля для текущей сцены: загруженная карта и её слой отрисовки. * * Существует потому, что слой живёт ровно столько же, сколько активная сцена: * при переходе на другую его нужно снять и построить заново. Разбросанное по * инструментам, это состояние рассинхронизировалось бы с первой же сменой сцены. */ export class MapSession { private map: HexMap | null = null; private meta: HexMapMeta | null = null; private layer: HexMapLayer | null = null; private textures: TextureCache | null = null; /** Очередь активаций. См. `activate`. */ private queue: Promise<unknown> = Promise.resolve(); constructor(private readonly catalog: AssetCatalog) {} get hasMap(): boolean { return this.map !== null; } get currentMap(): HexMap | null { return this.map; } get currentMeta(): HexMapMeta | null { return this.meta; } get currentLayer(): HexMapLayer | null { return this.layer; } /** * Загружает карту активной сцены и строит слой. * Молча выходит, если карты на сцене нет: это обычное состояние любой другой сцены. * * Вызовы выстраиваются в очередь, а не выполняются параллельно. Причина * не теоретическая: при загрузке мира активация приходит и от хука * `canvasReady`, и явным вызовом при готовности модуля. Обе успевают дойти до * `await` на предзагрузке текстур, после чего вторая перезаписывает `this.layer`, * теряя ссылку на первый слой, — а тот остаётся на канвасе. Получаются два * набора спрайтов друг поверх друга, и карта выглядит сломанной. */ async activate(): Promise<boolean> { const run = this.queue.then( () => this.load(), () => this.load(), ); this.queue = run.catch(() => undefined); return run; } private async load(): Promise<boolean> { this.detach(); const scene = canvas.scene; if (scene === null) return false; const loaded = loadMapFromScene(scene as unknown as SceneLike); if (loaded === null) return false; this.map = loaded.map; this.meta = loaded.meta; this.textures = new TextureCache(this.catalog); const preload = await this.textures.preload(); if (preload.failed.length > 0) { logger.warn(`Не загрузилось текстур: ${preload.failed.length}`); } // Высота гекса берётся из сетки сцены: она задана при её настройке // и остаётся единственным источником правды о масштабе отрисовки. const hexPixels = hexHeightFromGridSize(canvas.grid?.size ?? 200); this.layer = new HexMapLayer(this.map, this.catalog, this.textures, { hexPixels, revealAll: game.user?.isGM === true, }); this.layer.draw(); this.layer.attach(canvas.primary); logger.info(`Карта сцены отрисована: ${this.layer.spriteCount()} спрайтов`); return true; } /** Применяет свежесгенерированную карту к текущей сцене. */ async apply(map: HexMap, meta: HexMapMeta): Promise<void> { const scene = canvas.scene; if (scene === null) throw new Error('Нет активной сцены.'); await saveMapToScene(scene as unknown as SceneLike, map, meta); await this.activate(); } /** Сохраняет текущую карту обратно на сцену после правок. */ async persist(): Promise<void> { const scene = canvas.scene; if (scene === null || this.map === null || this.meta === null) return; await saveMapToScene(scene as unknown as SceneLike, this.map, this.meta); } /** Перерисовывает изменённые гексы. */ redraw(indices: Iterable<number>): void { this.layer?.redrawArea(indices); } /** Снимает слой с канваса. Вызывается при смене сцены. */ detach(): void { this.layer?.destroy(); this.layer = null; this.textures?.clear(); this.textures = null; this.map = null; this.meta = null; } }