feat(players): NPC disposition types and launch-with-players session tokens
Add Hostile/Neutral/Friendly ring types with session-only inactive overrides on control, plus launch-with-players flow and live player tokens on the map. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+37
-3
@@ -31,6 +31,7 @@ import { SceneViewStore } from './sceneView/sceneViewStore';
|
||||
import { registerDndAssetProtocol } from './protocol/dndAssetProtocol';
|
||||
import { PlayersStore } from './players/playersStore';
|
||||
import { SceneNpcTokensSessionStore } from './players/sceneNpcTokensSessionStore';
|
||||
import { ScenePlayerTokensSessionStore } from './players/scenePlayerTokensSessionStore';
|
||||
import { SceneTokensSessionStore } from './tokens/sceneTokensSessionStore';
|
||||
import { TokensStore } from './tokens/tokensStore';
|
||||
import { installAutoUpdater } from './update/installAutoUpdater';
|
||||
@@ -174,6 +175,7 @@ const materialsOverlayStore = new MaterialsOverlayStore();
|
||||
const npcsOverlayStore = new NpcsOverlayStore();
|
||||
const sceneTokensSessionStore = new SceneTokensSessionStore();
|
||||
const sceneNpcTokensSessionStore = new SceneNpcTokensSessionStore();
|
||||
const scenePlayerTokensSessionStore = new ScenePlayerTokensSessionStore();
|
||||
let tokensStore: TokensStore | null = null;
|
||||
let playersStore: PlayersStore | null = null;
|
||||
|
||||
@@ -265,6 +267,13 @@ function emitSceneNpcTokensSessionState(): void {
|
||||
}
|
||||
}
|
||||
|
||||
function emitScenePlayerTokensSessionState(): void {
|
||||
const state = scenePlayerTokensSessionStore.getState();
|
||||
for (const win of BrowserWindow.getAllWindows()) {
|
||||
win.webContents.send(ipcChannels.scenePlayerTokensSession.stateChanged, { state });
|
||||
}
|
||||
}
|
||||
|
||||
function syncSceneDarknessForProject(project: Project): void {
|
||||
const cacheKey = project.currentGraphNodeId ?? project.currentSceneId ?? null;
|
||||
const scene = project.currentSceneId ? project.scenes[project.currentSceneId] : undefined;
|
||||
@@ -435,11 +444,16 @@ async function main() {
|
||||
registerHandler(ipcChannels.license.setToken, async ({ token }) => licenseService.setToken(token));
|
||||
registerHandler(ipcChannels.license.clearToken, () => licenseService.clearToken());
|
||||
registerHandler(ipcChannels.license.acceptEula, ({ version }) => licenseService.acceptEula(version));
|
||||
registerHandler(ipcChannels.windows.openMultiWindow, () => {
|
||||
registerHandler(ipcChannels.windows.openMultiWindow, (req) => {
|
||||
sceneDarknessStore.resetSession();
|
||||
sceneTrapsStore.resetSession();
|
||||
sceneTokensSessionStore.reset();
|
||||
sceneNpcTokensSessionStore.reset();
|
||||
scenePlayerTokensSessionStore.reset();
|
||||
const playerIds = Array.isArray(req?.playerIds) ? req.playerIds.map(String).filter(Boolean) : [];
|
||||
if (playerIds.length > 0) {
|
||||
scenePlayerTokensSessionStore.dispatch({ kind: 'setSelection', playerIds });
|
||||
}
|
||||
effectsStore.dispatch({ kind: 'tool.set', tool: effectsDefaultTool() });
|
||||
openMultiWindow();
|
||||
const project = projectStore.getOpenProject();
|
||||
@@ -451,6 +465,7 @@ async function main() {
|
||||
emitSceneTrapsState();
|
||||
emitSceneTokensSessionState();
|
||||
emitSceneNpcTokensSessionState();
|
||||
emitScenePlayerTokensSessionState();
|
||||
emitEffectsState();
|
||||
return { ok: true };
|
||||
});
|
||||
@@ -548,9 +563,11 @@ async function main() {
|
||||
sceneViewStore.reset();
|
||||
sceneTokensSessionStore.reset();
|
||||
sceneNpcTokensSessionStore.reset();
|
||||
scenePlayerTokensSessionStore.reset();
|
||||
emitSceneViewState();
|
||||
emitSceneTokensSessionState();
|
||||
emitSceneNpcTokensSessionState();
|
||||
emitScenePlayerTokensSessionState();
|
||||
emitSessionState();
|
||||
warmNpcsEditorWindow();
|
||||
return { project };
|
||||
@@ -566,6 +583,7 @@ async function main() {
|
||||
sceneViewStore.reset();
|
||||
sceneTokensSessionStore.reset();
|
||||
sceneNpcTokensSessionStore.reset();
|
||||
scenePlayerTokensSessionStore.reset();
|
||||
emitEffectsState();
|
||||
emitMaterialsOverlayState();
|
||||
emitNpcsOverlayState();
|
||||
@@ -574,6 +592,7 @@ async function main() {
|
||||
emitSceneViewState();
|
||||
emitSceneTokensSessionState();
|
||||
emitSceneNpcTokensSessionState();
|
||||
emitScenePlayerTokensSessionState();
|
||||
emitSessionState();
|
||||
return { ok: true };
|
||||
});
|
||||
@@ -590,6 +609,7 @@ async function main() {
|
||||
materialsOverlayStore.clear();
|
||||
npcsOverlayStore.clear();
|
||||
sceneViewStore.reset();
|
||||
scenePlayerTokensSessionStore.clearPlacements();
|
||||
// Token moves persist for the whole play session (reset only on project open/close).
|
||||
const project = projectStore.getOpenProject();
|
||||
if (project) {
|
||||
@@ -603,6 +623,7 @@ async function main() {
|
||||
emitSceneTrapsState();
|
||||
emitSceneViewState();
|
||||
emitSceneTokensSessionState();
|
||||
emitScenePlayerTokensSessionState();
|
||||
emitSessionState();
|
||||
return { currentSceneId: projectStore.getOpenProject()?.currentSceneId ?? null };
|
||||
});
|
||||
@@ -619,6 +640,7 @@ async function main() {
|
||||
materialsOverlayStore.clear();
|
||||
npcsOverlayStore.clear();
|
||||
sceneViewStore.reset();
|
||||
scenePlayerTokensSessionStore.clearPlacements();
|
||||
// Token moves persist for the whole play session (reset only on project open/close).
|
||||
const project = projectStore.getOpenProject();
|
||||
if (project) {
|
||||
@@ -632,6 +654,7 @@ async function main() {
|
||||
emitSceneTrapsState();
|
||||
emitSceneViewState();
|
||||
emitSceneTokensSessionState();
|
||||
emitScenePlayerTokensSessionState();
|
||||
emitSessionState();
|
||||
const p = projectStore.getOpenProject();
|
||||
return {
|
||||
@@ -784,7 +807,7 @@ async function main() {
|
||||
});
|
||||
registerHandler(
|
||||
ipcChannels.project.upsertNpc,
|
||||
async ({ npcId, name, description, filePath: pathFromDrop, groupId, ringColor, imageOffset }) => {
|
||||
async ({ npcId, name, description, filePath: pathFromDrop, groupId, ringColor, disposition, imageOffset }) => {
|
||||
let filePath = pathFromDrop;
|
||||
if (!filePath && !npcId) {
|
||||
const { canceled, filePaths } = await dialog.showOpenDialog({
|
||||
@@ -809,6 +832,7 @@ async function main() {
|
||||
...(filePath ? { filePath } : {}),
|
||||
...(groupId !== undefined ? { groupId } : {}),
|
||||
...(ringColor !== undefined ? { ringColor } : {}),
|
||||
...(disposition !== undefined ? { disposition } : {}),
|
||||
...(imageOffset !== undefined ? { imageOffset } : {}),
|
||||
},
|
||||
(p) => emitNpcUpsertProgress(p),
|
||||
@@ -821,13 +845,15 @@ async function main() {
|
||||
);
|
||||
registerHandler(
|
||||
ipcChannels.project.updateNpcFields,
|
||||
async ({ npcId, name, description, groupId, ringColor, imageOffset }) => {
|
||||
async ({ npcId, name, description, groupId, ringColor, disposition, imageOffset, imageScale }) => {
|
||||
const project = await projectStore.updateNpcFields(npcId, {
|
||||
...(typeof name === 'string' ? { name } : {}),
|
||||
...(typeof description === 'string' ? { description } : {}),
|
||||
...(groupId !== undefined ? { groupId } : {}),
|
||||
...(ringColor !== undefined ? { ringColor } : {}),
|
||||
...(disposition !== undefined ? { disposition } : {}),
|
||||
...(imageOffset !== undefined ? { imageOffset } : {}),
|
||||
...(imageScale !== undefined ? { imageScale } : {}),
|
||||
});
|
||||
emitSessionState();
|
||||
return { project };
|
||||
@@ -1428,6 +1454,14 @@ async function main() {
|
||||
emitSceneNpcTokensSessionState();
|
||||
return { ok: true };
|
||||
});
|
||||
registerHandler(ipcChannels.scenePlayerTokensSession.getState, () => {
|
||||
return { state: scenePlayerTokensSessionStore.getState() };
|
||||
});
|
||||
registerHandler(ipcChannels.scenePlayerTokensSession.dispatch, ({ event }) => {
|
||||
scenePlayerTokensSessionStore.dispatch(event);
|
||||
emitScenePlayerTokensSessionState();
|
||||
return { ok: true };
|
||||
});
|
||||
|
||||
registerHandler(ipcChannels.video.getState, () => {
|
||||
return { state: videoStore.getState() };
|
||||
|
||||
Reference in New Issue
Block a user