feat: add Ref city hover details to public maps
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
import { loadMapDefinitionByName as loadRuntimeMapDefinitionByName } from '@sammo-ts/game-engine/scenario/mapLoader.js';
|
||||
import {
|
||||
loadMapDefinitionByName as loadRuntimeMapDefinitionByName,
|
||||
loadRegionDisplayMapByName as loadRuntimeRegionDisplayMapByName,
|
||||
} from '@sammo-ts/game-engine/scenario/mapLoader.js';
|
||||
import type { MapDefinition } from '@sammo-ts/logic';
|
||||
|
||||
const mapCache = new Map<string, MapDefinition>();
|
||||
@@ -15,3 +18,6 @@ export const loadMapDefinitionByName = async (mapName: string): Promise<MapDefin
|
||||
mapCache.set(mapName, map);
|
||||
return map;
|
||||
};
|
||||
|
||||
export const loadRegionDisplayMapByName = (mapName: string): Promise<Record<number, string>> =>
|
||||
loadRuntimeRegionDisplayMapByName(mapName);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { loadScenarioDefinitionById } from '@sammo-ts/game-engine/scenario/scenarioLoader.js';
|
||||
import type { ScenarioDefinition } from '@sammo-ts/logic';
|
||||
|
||||
import { loadMapDefinitionByName } from './mapDefinition.js';
|
||||
import { loadMapDefinitionByName, loadRegionDisplayMapByName } from './mapDefinition.js';
|
||||
|
||||
export interface MapLayoutCity {
|
||||
id: number;
|
||||
@@ -23,10 +23,22 @@ export interface MapLayout {
|
||||
export interface MapLayoutLoaderOptions {
|
||||
loadScenario?: (scenarioId: number) => Promise<ScenarioDefinition>;
|
||||
loadMap?: typeof loadMapDefinitionByName;
|
||||
loadRegionMap?: typeof loadRegionDisplayMapByName;
|
||||
}
|
||||
|
||||
const layoutCache = new Map<string, MapLayout>();
|
||||
|
||||
const CITY_LEVEL_MAP: Record<number, string> = {
|
||||
1: '수',
|
||||
2: '진',
|
||||
3: '관',
|
||||
4: '이',
|
||||
5: '소',
|
||||
6: '중',
|
||||
7: '대',
|
||||
8: '특',
|
||||
};
|
||||
|
||||
const parseScenarioId = (scenario: string): number | null => {
|
||||
const normalized = scenario.replace(/^scenario_/i, '').replace(/\.json$/i, '');
|
||||
if (!/^\d+$/.test(normalized)) {
|
||||
@@ -56,13 +68,16 @@ const resolveMapName = async (
|
||||
|
||||
export const loadMapLayout = async (scenario: string, options: MapLayoutLoaderOptions = {}): Promise<MapLayout> => {
|
||||
const mapName = await resolveMapName(scenario, options.loadScenario ?? loadScenarioDefinitionById);
|
||||
const useCache = !options.loadScenario && !options.loadMap;
|
||||
const useCache = !options.loadScenario && !options.loadMap && !options.loadRegionMap;
|
||||
const cached = useCache ? layoutCache.get(mapName) : undefined;
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const map = await (options.loadMap ?? loadMapDefinitionByName)(mapName);
|
||||
const [map, regionMap] = await Promise.all([
|
||||
(options.loadMap ?? loadMapDefinitionByName)(mapName),
|
||||
(options.loadRegionMap ?? loadRegionDisplayMapByName)(mapName),
|
||||
]);
|
||||
const layout: MapLayout = {
|
||||
mapName,
|
||||
cityList: map.cities.map((city) => ({
|
||||
@@ -74,8 +89,8 @@ export const loadMapLayout = async (scenario: string, options: MapLayoutLoaderOp
|
||||
y: city.position.y,
|
||||
path: [...city.connections],
|
||||
})),
|
||||
regionMap: {},
|
||||
levelMap: {},
|
||||
regionMap,
|
||||
levelMap: CITY_LEVEL_MAP,
|
||||
};
|
||||
|
||||
if (useCache) {
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { loadRegionDisplayMapByName } from '../src/maps/mapDefinition.js';
|
||||
import { loadMapLayout } from '../src/maps/mapLayout.js';
|
||||
|
||||
describe('map layout resource adapter', () => {
|
||||
it('loads the Ref theme region labels from the shared resource', async () => {
|
||||
await expect(loadRegionDisplayMapByName('che')).resolves.toMatchObject({ 1: '하북', 8: '동이' });
|
||||
await expect(loadRegionDisplayMapByName('chess')).resolves.toMatchObject({ 1: '킹', 7: '빈칸' });
|
||||
});
|
||||
|
||||
it('resolves the scenario map through the shared runtime loaders', async () => {
|
||||
const loadScenario = vi.fn().mockResolvedValue({
|
||||
config: { environment: { mapName: 'custom-map' } },
|
||||
@@ -20,7 +26,9 @@ describe('map layout resource adapter', () => {
|
||||
],
|
||||
});
|
||||
|
||||
await expect(loadMapLayout('scenario_2601.json', { loadScenario, loadMap })).resolves.toEqual({
|
||||
const loadRegionMap = vi.fn().mockResolvedValue({ 2: '테스트권역' });
|
||||
|
||||
await expect(loadMapLayout('scenario_2601.json', { loadScenario, loadMap, loadRegionMap })).resolves.toEqual({
|
||||
mapName: 'custom-map',
|
||||
cityList: [
|
||||
{
|
||||
@@ -33,11 +41,12 @@ describe('map layout resource adapter', () => {
|
||||
path: [8],
|
||||
},
|
||||
],
|
||||
regionMap: {},
|
||||
levelMap: {},
|
||||
regionMap: { 2: '테스트권역' },
|
||||
levelMap: { 1: '수', 2: '진', 3: '관', 4: '이', 5: '소', 6: '중', 7: '대', 8: '특' },
|
||||
});
|
||||
expect(loadScenario).toHaveBeenCalledWith(2601);
|
||||
expect(loadMap).toHaveBeenCalledWith('custom-map');
|
||||
expect(loadRegionMap).toHaveBeenCalledWith('custom-map');
|
||||
});
|
||||
|
||||
it('retains the che fallback for unknown preserved scenarios', async () => {
|
||||
@@ -47,6 +56,7 @@ describe('map layout resource adapter', () => {
|
||||
loadMapLayout('custom-runtime', {
|
||||
loadScenario: vi.fn(),
|
||||
loadMap,
|
||||
loadRegionMap: vi.fn().mockResolvedValue({}),
|
||||
})
|
||||
).resolves.toMatchObject({ mapName: 'che' });
|
||||
expect(loadMap).toHaveBeenCalledWith('che');
|
||||
|
||||
Reference in New Issue
Block a user