Plot Secrets with GM Mode
This mod allows scenario creators to obscure plot secrets directly on Story Cards. [[PS This text would be obscured the player, but the AI receives the text unaltered. PS]] Players will see [[Plot Secrets]] if they snoop into the a Story Card that has a secret. GM Mode bypasses those secrets and can be toggled on and off if the password is sent. /gmMode raven //This example of the GM Mode functionuses the default password. Library tab: // ===== PLOT SECRETS with GM Mode ===== const PS_PASSWORD = "raven"; // set your own const PS_TAG = /\[\[PS([\s\S]*?)PS\]\]/g; // author secrets like [[PS text PS]] const PS_LABEL = "[[Plot Secrets]]"; // what players see instead function psInit() { if (!state.psRaw) state.psRaw = {}; // cardId -> original entry text if (state.gmMode === undefined) state.gmMode = false; } function psReveal(raw) { return raw.replace(PS_TAG, (m, inner) => inner.trim()); } function psLock(raw) { return raw.replace(PS_TAG, PS_LABEL); } // Keeps every secret-bearing card's visible entry in sync with GM Mode function psSyncCards() { psInit(); for (let i = 0; i < storyCards.length; i++) { const card = storyCards[i]; if (!card.entry) continue; if (card.entry.includes('[[PS') && state.psRaw[card.id] === undefined) { state.psRaw[card.id] = card.entry; // remember the real text once } const raw = state.psRaw[card.id]; if (raw === undefined) continue; const shouldShow = state.gmMode ? psReveal(raw) : psLock(raw); if (card.entry !== shouldShow) updateStoryCard(i, card.keys, shouldShow, card.type); } } // Plot Secrets are never hidden from the AI function psRevealForAI(text) { psInit(); const beforeSync = storyCards.map(c => c.entry); // what's actually baked into `text` psSyncCards(); for (let i = 0; i < storyCards.length; i++) { const raw = state.psRaw[storyCards[i].id]; if (raw === undefined) continue; const priorEntry = beforeSync[i]; if (priorEntry && text.includes(priorEntry)) { text = text.split(priorEntry).join(psReveal(raw)); } } text = text.replace(PS_TAG, (m, inner) => inner.trim()); // covers a card's very first turn return text; } Input tab: const modifier = (text) => { const match = text.match(/\/gmMode\s+([^\s"'.,!?]+)/i); if (match) { psInit(); if (match[1] === PS_PASSWORD) { state.gmMode = !state.gmMode; state.__sceSkipTimeThisTurn = true; return { text: "((GM Mode toggled. This text can be safely deleted.))" }; } return { text: "((Incorrect GM Mode password. This text can be safely deleted.))" }; } return { text }; }; modifier(text); Context tab: const modifier = (text) => { text = psRevealForAI(text); return { text }; }; modifier(text); Output tab: const modifier = (text) => { text = text.replace(PS_TAG, '').split(PS_LABEL).join(''); // safety net only return { text }; }; modifier(text);
