feat(control): таймер сессии поверх сцены
Отдельное окно countdown с «Показать игрокам»; на превью — move/resize/rotate, на презентации тот же layout. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -34,6 +34,7 @@ import { LicenseService } from './license/licenseService';
|
||||
import { readPresentationSettings, writePresentationSettings } from './preferences/appPreferences';
|
||||
import { MaterialsOverlayStore } from './materials/materialsOverlayStore';
|
||||
import { NpcsOverlayStore } from './npcs/npcsOverlayStore';
|
||||
import { TimerSessionStore } from './sessionTimer/timerSessionStore';
|
||||
import { ZipProjectStore } from './project/zipStore';
|
||||
import { SceneViewStore } from './sceneView/sceneViewStore';
|
||||
import { registerDndAssetProtocol } from './protocol/dndAssetProtocol';
|
||||
@@ -67,6 +68,7 @@ import {
|
||||
markAppQuitting,
|
||||
openMaterialsWindow,
|
||||
openPlaylistsWindow,
|
||||
openTimerWindow,
|
||||
openMultiWindow,
|
||||
openNpcsEditorWindow,
|
||||
openSceneEditorWindow,
|
||||
@@ -75,6 +77,7 @@ import {
|
||||
openSceneDescriptionWindow,
|
||||
closeMaterialsWindow,
|
||||
closePlaylistsWindow,
|
||||
closeTimerWindow,
|
||||
closeNpcsEditorWindow,
|
||||
closeSceneEditorWindow,
|
||||
closeTokenPathEditorWindow,
|
||||
@@ -178,6 +181,7 @@ const sceneViewStore = new SceneViewStore();
|
||||
const videoStore = new VideoPlaybackStore();
|
||||
const materialsOverlayStore = new MaterialsOverlayStore();
|
||||
const npcsOverlayStore = new NpcsOverlayStore();
|
||||
const timerSessionStore = new TimerSessionStore();
|
||||
const sceneTokensSessionStore = new SceneTokensSessionStore();
|
||||
const tokenPathSessionStore = new TokenPathSessionStore();
|
||||
const sceneNpcTokensSessionStore = new SceneNpcTokensSessionStore();
|
||||
@@ -272,6 +276,13 @@ function syncNpcsOverlayWithProject(project: Project | null): void {
|
||||
npcsOverlayStore.ensureNpcStillExists(ids);
|
||||
}
|
||||
|
||||
function emitSessionTimerState(): void {
|
||||
const state = timerSessionStore.getState();
|
||||
for (const win of BrowserWindow.getAllWindows()) {
|
||||
win.webContents.send(ipcChannels.sessionTimer.stateChanged, { state });
|
||||
}
|
||||
}
|
||||
|
||||
function emitSceneDarknessState(): void {
|
||||
const state = sceneDarknessStore.getState();
|
||||
for (const win of BrowserWindow.getAllWindows()) {
|
||||
@@ -561,6 +572,7 @@ async function main() {
|
||||
clearNpcMapSpawnDrag();
|
||||
scenePlayerTokensSessionStore.reset();
|
||||
tokenGridSnapSessionStore.reset();
|
||||
timerSessionStore.clear();
|
||||
if (USERS_BRANCH_FEATURES_ENABLED) {
|
||||
const playerIds = Array.isArray(req?.playerIds) ? req.playerIds.map(String).filter(Boolean) : [];
|
||||
if (playerIds.length > 0) {
|
||||
@@ -586,11 +598,14 @@ async function main() {
|
||||
emitScenePlayerTokensSessionState();
|
||||
emitTokenGridSnapState();
|
||||
emitEffectsState();
|
||||
emitSessionTimerState();
|
||||
return { ok: true };
|
||||
});
|
||||
registerHandler(ipcChannels.windows.closeMultiWindow, () => {
|
||||
closeMultiWindow();
|
||||
timerSessionStore.clear();
|
||||
tokenGridSnapSessionStore.reset();
|
||||
emitSessionTimerState();
|
||||
emitTokenGridSnapState();
|
||||
return { ok: true };
|
||||
});
|
||||
@@ -646,6 +661,14 @@ async function main() {
|
||||
closePlaylistsWindow();
|
||||
return { ok: true };
|
||||
});
|
||||
registerHandler(ipcChannels.windows.openTimer, () => {
|
||||
openTimerWindow();
|
||||
return { ok: true };
|
||||
});
|
||||
registerHandler(ipcChannels.windows.closeTimer, () => {
|
||||
closeTimerWindow();
|
||||
return { ok: true };
|
||||
});
|
||||
registerHandler(ipcChannels.windows.openNpcsEditor, () => {
|
||||
openNpcsEditorWindow();
|
||||
return { ok: true };
|
||||
@@ -701,6 +724,15 @@ async function main() {
|
||||
emitNpcsOverlayState();
|
||||
return { ok: true };
|
||||
});
|
||||
registerHandler(ipcChannels.sessionTimer.getState, () => {
|
||||
return { state: timerSessionStore.getState() };
|
||||
});
|
||||
registerHandler(ipcChannels.sessionTimer.dispatch, ({ event }) => {
|
||||
timerSessionStore.dispatch(event);
|
||||
timerSessionStore.settleIfExpired();
|
||||
emitSessionTimerState();
|
||||
return { state: timerSessionStore.getState() };
|
||||
});
|
||||
|
||||
registerHandler(ipcChannels.project.list, async () => {
|
||||
const projects = await projectStore.listProjects();
|
||||
@@ -742,6 +774,7 @@ async function main() {
|
||||
effectsStore.clear();
|
||||
materialsOverlayStore.clear();
|
||||
npcsOverlayStore.clear();
|
||||
timerSessionStore.clear();
|
||||
sceneDarknessStore.resetSession();
|
||||
sceneMarkerStore.resetSession();
|
||||
sceneTrapsStore.resetSession();
|
||||
@@ -754,6 +787,7 @@ async function main() {
|
||||
emitEffectsState();
|
||||
emitMaterialsOverlayState();
|
||||
emitNpcsOverlayState();
|
||||
emitSessionTimerState();
|
||||
emitSceneDarknessState();
|
||||
emitSceneMarkerState();
|
||||
emitSceneTrapsState();
|
||||
|
||||
Reference in New Issue
Block a user