import { createServer } from 'vite'; const expectedOpeningPageIds = ['yellow-turban-chaos', 'liu-bei-resolve']; const expectedDeparturePageIds = [ 'first-sortie', 'yellow-turban-nearby', 'first-battle-plan-talk', 'battle-briefing' ]; const expectedRequiredObjectiveIds = [ 'meet-zhang-fei', 'meet-guan-yu', 'check-supplies' ]; const expectedCampaignMarkerIds = [ 'prologue-village-entered', 'prologue-village-meet-zhang-fei', 'prologue-village-meet-guan-yu', 'prologue-village-check-supplies', 'prologue-village-complete' ]; const server = await createServer({ logLevel: 'error', server: { middlewareMode: true }, appType: 'custom' }); try { const village = await server.ssrLoadModule('/src/game/data/prologueVillage.ts'); const campaign = await server.ssrLoadModule('/src/game/state/campaignState.ts'); const unitAssets = await server.ssrLoadModule('/src/game/data/unitAssets.ts'); const failures = []; const openingPages = village.prologueOpeningPages(); const departurePages = village.prologueDeparturePages(); assertArrayEquals( openingPages.map((page) => page.id), expectedOpeningPageIds, 'opening story page order', failures ); assertArrayEquals( departurePages.map((page) => page.id), expectedDeparturePageIds, 'departure story page order', failures ); assertArrayEquals( [...village.prologueVillageRequiredObjectiveIds], expectedRequiredObjectiveIds, 'required objective order', failures ); const objectives = village.prologueVillageObjectiveDefinitions; assert(objectives.length === 4, `expected 4 objective rows, received ${objectives.length}`, failures); assert( new Set(objectives.map((objective) => objective.id)).size === objectives.length, 'objective ids must be unique', failures ); assert( objectives.at(-1)?.id === 'make-oath', 'the final objective must be make-oath', failures ); const npcs = village.prologueVillageNpcDefinitions; const objectiveNpcIds = npcs .filter((npc) => npc.objectiveId) .map((npc) => npc.objectiveId); assertArrayEquals( [...objectiveNpcIds].sort(), [...expectedRequiredObjectiveIds].sort(), 'required NPC objective coverage', failures ); assert( new Set(npcs.map((npc) => npc.id)).size === npcs.length, 'NPC ids must be unique', failures ); assert(npcs.some((npc) => !npc.objectiveId), 'at least one optional villager should be available', failures); for (const npc of npcs) { assert( Number.isFinite(npc.x) && npc.x >= 70 && npc.x <= 1435 && Number.isFinite(npc.y) && npc.y >= 130 && npc.y <= 900, `${npc.id}: position must remain inside the FHD village play area`, failures ); assert(unitAssets.hasUnitSheetAsset(npc.textureKey), `${npc.id}: missing ${npc.textureKey}`, failures); assert(Array.isArray(npc.dialogue) && npc.dialogue.length > 0, `${npc.id}: dialogue is required`, failures); for (const [lineIndex, line] of npc.dialogue.entries()) { assert( typeof line.speaker === 'string' && line.speaker.trim().length > 0, `${npc.id}[${lineIndex}]: speaker is required`, failures ); assert( typeof line.text === 'string' && line.text.trim().length >= 8 && line.text.length <= 180, `${npc.id}[${lineIndex}]: dialogue must be 8-180 characters`, failures ); if (line.textureKey) { assert( unitAssets.hasUnitSheetAsset(line.textureKey), `${npc.id}[${lineIndex}]: missing dialogue sprite ${line.textureKey}`, failures ); } } } assert( unitAssets.hasUnitSheetAsset('unit-liu-bei'), 'the controllable Liu Bei base sheet is required', failures ); assertArrayEquals( Object.values(campaign.prologueVillageCampaignTutorialIds), expectedCampaignMarkerIds, 'campaign progress marker ids', failures ); assert( expectedCampaignMarkerIds.every((id) => campaign.campaignTutorialIds.includes(id)), 'every village marker must survive campaign save normalization', failures ); if (failures.length > 0) { throw new Error( `Prologue village data verification failed with ${failures.length} issue(s):\n` + failures.map((failure) => `- ${failure}`).join('\n') ); } console.log( `Verified ${npcs.length} village NPCs, ${expectedRequiredObjectiveIds.length} required interactions, ` + `${openingPages.length} opening pages, ${departurePages.length} departure pages, and save markers.` ); } finally { await server.close(); } function assert(condition, message, failures) { if (!condition) { failures.push(message); } } function assertArrayEquals(actual, expected, label, failures) { assert( JSON.stringify(actual) === JSON.stringify(expected), `${label}: expected [${expected.join(', ')}], received [${actual.join(', ')}]`, failures ); }