// Mythic Oracle — Fate Check and Random Event Check import { getActiveCampaign } from './app.js'; const NO_CAMPAIGN_MESSAGE = 'Select or create a campaign to use the oracle.'; // Mythic GME v2 probability matrix — base% -> thresholds indexed by chaos factor 1-9 const probTable = { 5: [1, 1, 2, 3, 4, 5, 6, 7, 8], 15: [1, 2, 4, 6, 8, 10, 12, 15, 18], 25: [2, 4, 7, 11, 15, 19, 23, 27, 32], 35: [3, 6, 10, 15, 20, 25, 30, 35, 41], 50: [5, 9, 14, 20, 26, 32, 38, 44, 50], 65: [9, 14, 20, 28, 36, 44, 52, 60, 68], 75: [11, 17, 24, 33, 42, 51, 60, 68, 76], 85: [14, 21, 30, 40, 50, 60, 70, 78, 85], 95: [18, 27, 38, 50, 62, 73, 82, 89, 95], }; let selectedProb = { label: '50/50', base: 50 }; function initProbGrid() { const buttons = document.querySelectorAll('#probGrid .prob-btn'); buttons.forEach((btn) => { btn.addEventListener('click', () => { buttons.forEach((b) => b.classList.remove('selected')); btn.classList.add('selected'); selectedProb = { label: btn.dataset.label, base: Number(btn.dataset.base) }; }); }); } function rollFate() { const box = document.getElementById('fateResult'); const campaign = getActiveCampaign(); if (!campaign) { box.className = 'result-box'; box.innerHTML = `${NO_CAMPAIGN_MESSAGE}`; return; } const chaos = campaign.chaos_factor; const roll = Math.floor(Math.random() * 100) + 1; const thresholds = probTable[selectedProb.base]; const threshold = thresholds[chaos - 1]; // Exceptional results occur on doubles (11,22,33...) or within 10% of threshold const isDouble = roll % 11 === 0 || (roll < 100 && Math.floor(roll / 10) === roll % 10); const yesResult = roll <= threshold; let resultClass, mainText, subText; if (yesResult && isDouble) { resultClass = 'exceptional-yes'; mainText = 'Exceptional Yes'; subText = 'Something beyond what was asked occurs in your favor.'; } else if (!yesResult && isDouble) { resultClass = 'exceptional-no'; mainText = 'Exceptional No'; subText = 'Something beyond a simple no — complications arise.'; } else if (yesResult) { resultClass = 'yes'; mainText = 'Yes'; subText = 'The answer is affirmative.'; } else { resultClass = 'no'; mainText = 'No'; subText = 'The answer is negative.'; } // Random event check bundled into fate check (Mythic v2 behavior) if (roll <= chaos) { subText += ' · Random event triggered.'; resultClass = yesResult ? resultClass : 'random-event'; } const mainColorClass = yesResult ? 'yes-color' : resultClass === 'random-event' ? 'blue-color' : 'no-color'; box.className = `result-box animate ${resultClass}`; box.innerHTML = `
${mainText}
${subText}
Roll: ${roll}  |  Threshold: ${threshold}  |  Prob: ${selectedProb.label}  |  CF: ${chaos}
`; } function rollEventCheck() { const box = document.getElementById('eventResult'); const campaign = getActiveCampaign(); if (!campaign) { box.className = 'event-result'; box.innerHTML = `${NO_CAMPAIGN_MESSAGE}`; return; } const chaos = campaign.chaos_factor; const roll = Math.floor(Math.random() * 10) + 1; if (roll <= chaos) { box.className = 'event-result animate'; box.style.borderColor = 'var(--info)'; box.innerHTML = `
Random Event!
Roll: ${roll}  ≤  CF: ${chaos}
`; } else { box.className = 'event-result animate'; box.style.borderColor = 'var(--border)'; box.innerHTML = `
No Event
Roll: ${roll}  >  CF: ${chaos} — scene proceeds as expected.
`; } } function init() { initProbGrid(); document.getElementById('fateRollBtn').addEventListener('click', rollFate); document.getElementById('eventRollBtn').addEventListener('click', rollEventCheck); } document.addEventListener('DOMContentLoaded', init);