46bec1a86a
When previewRotationDeg changes, remap non-player, NPC and trap markers (plus live session token overrides) so they stay on the art. Co-authored-by: Cursor <cursoragent@cursor.com>
76 lines
2.3 KiB
TypeScript
76 lines
2.3 KiB
TypeScript
import type { SceneTokensSessionEvent, SceneTokensSessionState } from '../../shared/types';
|
|
import { rotateMapNormPointByCwSteps } from '../../shared/types/scenePreviewRotation';
|
|
|
|
function emptyState(): SceneTokensSessionState {
|
|
return {
|
|
revision: 1,
|
|
byPlacementId: {},
|
|
};
|
|
}
|
|
|
|
export class SceneTokensSessionStore {
|
|
private state: SceneTokensSessionState = emptyState();
|
|
|
|
getState(): SceneTokensSessionState {
|
|
return this.state;
|
|
}
|
|
|
|
reset(): SceneTokensSessionState {
|
|
if (Object.keys(this.state.byPlacementId).length === 0) return this.state;
|
|
this.state = {
|
|
revision: this.state.revision + 1,
|
|
byPlacementId: {},
|
|
};
|
|
return this.state;
|
|
}
|
|
|
|
/** Rotate session position overrides with the scene preview (CSS rotate steps). */
|
|
rotateMapCwSteps(steps: number): SceneTokensSessionState {
|
|
const n = ((steps % 4) + 4) % 4;
|
|
if (n === 0) return this.state;
|
|
const ids = Object.keys(this.state.byPlacementId);
|
|
if (ids.length === 0) return this.state;
|
|
const byPlacementId: SceneTokensSessionState['byPlacementId'] = {};
|
|
for (const id of ids) {
|
|
const prev = this.state.byPlacementId[id];
|
|
if (!prev) continue;
|
|
const next = rotateMapNormPointByCwSteps(prev.nx, prev.ny, n);
|
|
byPlacementId[id] = { nx: next.nx, ny: next.ny };
|
|
}
|
|
this.state = {
|
|
revision: this.state.revision + 1,
|
|
byPlacementId,
|
|
};
|
|
return this.state;
|
|
}
|
|
|
|
dispatch(event: SceneTokensSessionEvent): SceneTokensSessionState {
|
|
switch (event.kind) {
|
|
case 'clear':
|
|
return this.reset();
|
|
case 'move': {
|
|
const placementId = String(event.placementId ?? '');
|
|
if (!placementId) return this.state;
|
|
const nx = Math.max(0, Math.min(1, event.nx));
|
|
const ny = Math.max(0, Math.min(1, event.ny));
|
|
if (!Number.isFinite(nx) || !Number.isFinite(ny)) return this.state;
|
|
const prev = this.state.byPlacementId[placementId];
|
|
if (prev && prev.nx === nx && prev.ny === ny) return this.state;
|
|
this.state = {
|
|
revision: this.state.revision + 1,
|
|
byPlacementId: {
|
|
...this.state.byPlacementId,
|
|
[placementId]: { nx, ny },
|
|
},
|
|
};
|
|
return this.state;
|
|
}
|
|
default: {
|
|
const _exhaustive: never = event;
|
|
void _exhaustive;
|
|
return this.state;
|
|
}
|
|
}
|
|
}
|
|
}
|