02d73ddf81
Add trap overlays with session state, material legend editor/panel, and a dedicated scene editor window wired through IPC and project persistence. Co-authored-by: Cursor <cursoragent@cursor.com>
135 lines
4.1 KiB
TypeScript
135 lines
4.1 KiB
TypeScript
import {
|
|
defaultTrapRuntime,
|
|
type SceneTrapsEvent,
|
|
type SceneTrapsState,
|
|
type SceneTrapRuntime,
|
|
} from '../../shared/types';
|
|
|
|
function emptyState(): SceneTrapsState {
|
|
return {
|
|
revision: 1,
|
|
cacheKey: null,
|
|
byId: {},
|
|
lastActivation: null,
|
|
};
|
|
}
|
|
|
|
function cloneById(byId: Record<string, SceneTrapRuntime>): Record<string, SceneTrapRuntime> {
|
|
const out: Record<string, SceneTrapRuntime> = {};
|
|
for (const [k, v] of Object.entries(byId)) {
|
|
out[k] = { ...v };
|
|
}
|
|
return out;
|
|
}
|
|
|
|
export class SceneTrapsStore {
|
|
private state: SceneTrapsState = emptyState();
|
|
/** Кэш runtime по ключу сцены/ноды на время сессии показа. */
|
|
private cache = new Map<string, Record<string, SceneTrapRuntime>>();
|
|
private currentKey: string | null = null;
|
|
private activationToken = 0;
|
|
|
|
getState(): SceneTrapsState {
|
|
return {
|
|
...this.state,
|
|
byId: cloneById(this.state.byId),
|
|
lastActivation: this.state.lastActivation ? { ...this.state.lastActivation } : null,
|
|
};
|
|
}
|
|
|
|
resetSession(): void {
|
|
this.cache.clear();
|
|
this.currentKey = null;
|
|
this.state = emptyState();
|
|
}
|
|
|
|
/**
|
|
* Переключение сцены: сохраняем runtime в кэш и подгружаем/инициализируем для новых trapIds.
|
|
*/
|
|
switchScene(cacheKey: string | null, trapIds: readonly string[]): SceneTrapsState {
|
|
if (this.currentKey !== null) {
|
|
this.cache.set(this.currentKey, cloneById(this.state.byId));
|
|
}
|
|
this.currentKey = cacheKey;
|
|
const cached = cacheKey ? this.cache.get(cacheKey) : undefined;
|
|
const byId: Record<string, SceneTrapRuntime> = {};
|
|
for (const id of trapIds) {
|
|
byId[id] = cached?.[id] ? { ...cached[id]! } : defaultTrapRuntime();
|
|
}
|
|
if (cacheKey) {
|
|
this.cache.set(cacheKey, cloneById(byId));
|
|
}
|
|
this.state = {
|
|
revision: this.state.revision + 1,
|
|
cacheKey,
|
|
byId,
|
|
lastActivation: null,
|
|
};
|
|
return this.getState();
|
|
}
|
|
|
|
dispatch(event: SceneTrapsEvent): SceneTrapsState {
|
|
switch (event.kind) {
|
|
case 'syncTrapIds': {
|
|
const byId: Record<string, SceneTrapRuntime> = {};
|
|
for (const id of event.trapIds) {
|
|
byId[id] = this.state.byId[id] ? { ...this.state.byId[id]! } : defaultTrapRuntime();
|
|
}
|
|
if (this.currentKey) this.cache.set(this.currentKey, cloneById(byId));
|
|
this.state = {
|
|
...this.state,
|
|
revision: this.state.revision + 1,
|
|
byId,
|
|
};
|
|
return this.getState();
|
|
}
|
|
case 'reveal': {
|
|
const cur = this.state.byId[event.trapId];
|
|
if (!cur) return this.getState();
|
|
const byId = cloneById(this.state.byId);
|
|
byId[event.trapId] = { ...cur, revealed: true };
|
|
if (this.currentKey) this.cache.set(this.currentKey, cloneById(byId));
|
|
this.state = {
|
|
...this.state,
|
|
revision: this.state.revision + 1,
|
|
byId,
|
|
};
|
|
return this.getState();
|
|
}
|
|
case 'activate': {
|
|
const cur = this.state.byId[event.trapId];
|
|
if (!cur) return this.getState();
|
|
const byId = cloneById(this.state.byId);
|
|
byId[event.trapId] = { status: 'active', revealed: true };
|
|
this.activationToken += 1;
|
|
if (this.currentKey) this.cache.set(this.currentKey, cloneById(byId));
|
|
this.state = {
|
|
...this.state,
|
|
revision: this.state.revision + 1,
|
|
byId,
|
|
lastActivation: { trapId: event.trapId, token: this.activationToken },
|
|
};
|
|
return this.getState();
|
|
}
|
|
case 'disarm': {
|
|
const cur = this.state.byId[event.trapId];
|
|
if (!cur) return this.getState();
|
|
const byId = cloneById(this.state.byId);
|
|
byId[event.trapId] = { status: 'disarmed', revealed: true };
|
|
if (this.currentKey) this.cache.set(this.currentKey, cloneById(byId));
|
|
this.state = {
|
|
...this.state,
|
|
revision: this.state.revision + 1,
|
|
byId,
|
|
};
|
|
return this.getState();
|
|
}
|
|
default: {
|
|
const _x: never = event;
|
|
void _x;
|
|
return this.getState();
|
|
}
|
|
}
|
|
}
|
|
}
|