feat(control): 3D-кубики в отдельном окне

Бросок синхронизирован на пульте и презентации; d100 как десятки; оверлей скрывается только кнопкой «Убрать».

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Fontosh
2026-09-04 13:01:34 +08:00
parent 86d6d157f0
commit 9ac01d217f
137 changed files with 958 additions and 1 deletions
+38
View File
@@ -37,6 +37,7 @@ import { MaterialsOverlayStore } from './materials/materialsOverlayStore';
import { NpcsOverlayStore } from './npcs/npcsOverlayStore';
import { TimerSessionStore } from './sessionTimer/timerSessionStore';
import { InitiativeSessionStore } from './initiative/initiativeSessionStore';
import { DiceRollSessionStore } from './diceRoll/diceRollSessionStore';
import { ZipProjectStore } from './project/zipStore';
import { SceneViewStore } from './sceneView/sceneViewStore';
import { registerDndAssetProtocol } from './protocol/dndAssetProtocol';
@@ -72,6 +73,7 @@ import {
openPlaylistsWindow,
openTimerWindow,
openInitiativeWindow,
openDiceWindow,
openMultiWindow,
openNpcsEditorWindow,
openSceneEditorWindow,
@@ -82,6 +84,7 @@ import {
closePlaylistsWindow,
closeTimerWindow,
closeInitiativeWindow,
closeDiceWindow,
closeNpcsEditorWindow,
closeSceneEditorWindow,
closeTokenPathEditorWindow,
@@ -187,6 +190,7 @@ const materialsOverlayStore = new MaterialsOverlayStore();
const npcsOverlayStore = new NpcsOverlayStore();
const timerSessionStore = new TimerSessionStore();
const initiativeSessionStore = new InitiativeSessionStore();
const diceRollSessionStore = new DiceRollSessionStore();
const sceneTokensSessionStore = new SceneTokensSessionStore();
const tokenPathSessionStore = new TokenPathSessionStore();
const sceneNpcTokensSessionStore = new SceneNpcTokensSessionStore();
@@ -300,6 +304,18 @@ function emitInitiativeState(): void {
}
}
function emitDiceRollState(): void {
const state = diceRollSessionStore.getState();
for (const win of BrowserWindow.getAllWindows()) {
if (win.isDestroyed() || win.webContents.isDestroyed()) continue;
try {
win.webContents.send(ipcChannels.diceRoll.stateChanged, { state });
} catch {
/* teardown */
}
}
}
function syncInitiativePresenceFromSession(): void {
const project = projectStore.getOpenProject();
const sceneId = project?.currentSceneId ?? null;
@@ -637,6 +653,7 @@ async function main() {
tokenGridSnapSessionStore.reset();
timerSessionStore.clear();
initiativeSessionStore.clear();
diceRollSessionStore.clear();
if (USERS_BRANCH_FEATURES_ENABLED) {
const playerIds = Array.isArray(req?.playerIds) ? req.playerIds.map(String).filter(Boolean) : [];
if (playerIds.length > 0) {
@@ -665,15 +682,18 @@ async function main() {
emitSessionTimerState();
syncInitiativePresenceFromSession();
emitInitiativeState();
emitDiceRollState();
return { ok: true };
});
registerHandler(ipcChannels.windows.closeMultiWindow, () => {
closeMultiWindow();
timerSessionStore.clear();
initiativeSessionStore.clear();
diceRollSessionStore.clear();
tokenGridSnapSessionStore.reset();
emitSessionTimerState();
emitInitiativeState();
emitDiceRollState();
emitTokenGridSnapState();
return { ok: true };
});
@@ -755,6 +775,14 @@ async function main() {
emitInitiativeState();
return { ok: true };
});
registerHandler(ipcChannels.windows.openDice, () => {
openDiceWindow();
return { ok: true };
});
registerHandler(ipcChannels.windows.closeDice, () => {
closeDiceWindow();
return { ok: true };
});
registerHandler(ipcChannels.windows.openNpcsEditor, () => {
openNpcsEditorWindow();
return { ok: true };
@@ -827,6 +855,14 @@ async function main() {
emitInitiativeState();
return { state: initiativeSessionStore.getState() };
});
registerHandler(ipcChannels.diceRoll.getState, () => {
return { state: diceRollSessionStore.getState() };
});
registerHandler(ipcChannels.diceRoll.dispatch, ({ event }) => {
diceRollSessionStore.dispatch(event);
emitDiceRollState();
return { state: diceRollSessionStore.getState() };
});
registerHandler(ipcChannels.project.list, async () => {
const projects = await projectStore.listProjects();
@@ -870,6 +906,7 @@ async function main() {
npcsOverlayStore.clear();
timerSessionStore.clear();
initiativeSessionStore.clear();
diceRollSessionStore.clear();
sceneDarknessStore.resetSession();
sceneMarkerStore.resetSession();
sceneTrapsStore.resetSession();
@@ -884,6 +921,7 @@ async function main() {
emitNpcsOverlayState();
emitSessionTimerState();
emitInitiativeState();
emitDiceRollState();
emitSceneDarknessState();
emitSceneMarkerState();
emitSceneTrapsState();