import { clampNpcsLayout, DEFAULT_NPCS_OVERLAY_LAYOUT, type NpcId, type NpcsOverlayEvent, type NpcsOverlayLayout, type NpcsOverlayState, type NpcsZoomTool, zoomNpcsLayoutAt, } from '../../shared/types'; function emptyState(): NpcsOverlayState { return { revision: 1, activeNpcId: null, layout: { ...DEFAULT_NPCS_OVERLAY_LAYOUT }, zoomTool: null, }; } export class NpcsOverlayStore { private state: NpcsOverlayState = emptyState(); getState(): NpcsOverlayState { return this.state; } clear(): NpcsOverlayState { if (this.state.activeNpcId === null && this.state.zoomTool === null) { return this.state; } this.state = { revision: this.state.revision + 1, activeNpcId: null, layout: { ...DEFAULT_NPCS_OVERLAY_LAYOUT }, zoomTool: null, }; return this.state; } dispatch(event: NpcsOverlayEvent): NpcsOverlayState { switch (event.kind) { case 'hide': return this.clear(); case 'show': this.state = { revision: this.state.revision + 1, activeNpcId: event.npcId, layout: { ...DEFAULT_NPCS_OVERLAY_LAYOUT }, zoomTool: this.state.zoomTool, }; return this.state; case 'toggle': { if (this.state.activeNpcId === event.npcId) { return this.clear(); } this.state = { revision: this.state.revision + 1, activeNpcId: event.npcId, layout: { ...DEFAULT_NPCS_OVERLAY_LAYOUT }, zoomTool: this.state.zoomTool, }; return this.state; } case 'layout.set': { if (this.state.activeNpcId === null) return this.state; this.state = { ...this.state, revision: this.state.revision + 1, layout: clampNpcsLayout(event.layout), }; return this.state; } case 'zoomTool.set': { const tool: NpcsZoomTool = event.tool; this.state = { ...this.state, revision: this.state.revision + 1, zoomTool: tool, }; return this.state; } case 'zoomAt': { if (this.state.activeNpcId === null || !this.state.zoomTool) return this.state; const factor = this.state.zoomTool === 'zoomIn' ? 1.25 : 1 / 1.25; const layout: NpcsOverlayLayout = zoomNpcsLayoutAt( this.state.layout, event.nx, event.ny, factor, ); this.state = { ...this.state, revision: this.state.revision + 1, layout, }; return this.state; } default: return this.state; } } ensureNpcStillExists(npcIds: ReadonlySet): NpcsOverlayState { const active = this.state.activeNpcId; if (active === null || npcIds.has(active)) return this.state; return this.clear(); } }