/* The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. See https://creativecommons.org/publicdomain/zero/1.0/ for details. */ "use strict"; var EVENTS = [ { name: "The Greenskins are mobilized.", effect: "Greenskins gain a bonus of +1 Strength." }, { name: "Big eye is watching you.", effect: "Bubbleyes gain a bonus of +1 Strength.", requires: ["bubbleyes"] }, { name: "General invocation.", effect: "Demons gain a bonus of +1 Strength.", requires: ["demons"] }, { name: "Stronger than ever.", effect: "Undead gain a bonus of +1 Strength.", requires: ["undead"] }, { name: "Encounter with Abunakkashi.", effect: "Abunakkashii and his offspring gain a bonus of +2 Strength.", requires: ["abunakkashii"], unique: true }, { name: "The start of a legend.", effect: "Abunakkashii and his offspring gain a bonus of +1 Strength.", requires: ["abunakkashii"], unique: true }, { name: "Technological prowess.", effect: "Traps gain a bonus of +1 Strength.", requires: ["traps"] }, { name: "Mental combat.", effect: "Psi monsters gain a bonus of +1 Strength.", }, { name: "A cry in the night.", action: "An adventurer of your choice loses 1 Mana." }, { name: "Psi wave.", action: "Lose all your Mana tokens." }, { name: "It's an ambush!", action: "Place a Tenacity token on each monster that does not have one." }, { name: "The alarm is sounded.", action: "Place a Tenacity token on the weakest monster or monsters that do not already have one." }, { name: "Flank attack.", effect: "Cards on either side corridor gain a bonus of +1 Strength.", unique: true }, { name: "Battle formation.", effect: "Cards in the central corridor(s) gain a bonus of +1 Strength.", unique: true }, { name: "Last bastion.", effect: "All cards gain a bonus of +1 Strength.", unique: true }, { name: "Destruction.", action: "All of your weapons and armor are destroyed.", unique: true }, { name: "Epic combat.", effect: "The Final Monster gains a bonus of +3 Strength.", unique: true }, { name: "Red herring.", action: "Shuffle the remaining corridors together and redistribute the cards as if you were setting up the game." }, { name: "The torch has gone out.", action: "The next dungeon card is placed face-down.", }, { name: "Anti-psi zone.", effect: "You do not gain Mana tokens for the next three creatures you defeat, even if they have a Mana icon.", unique: true }, { name: "Necromancy.", action: "Return a random defeated dungeon card to the lowest corridor and shuffle it." }, { name: "Reanimation.", action: "Return a random defeated dungeon card to the highest corridor and shuffle it." }, { name: "Malediction.", action: "Discard a quest or a magic object." }, { name: "False brethren.", effect: "All Demons gain Immunity 5 and Immunity 7.", requires: ["demons"], unique: true }, { name: "Psi assault.", effect: "You cannot use ultimate powers.", lock: ["ultimates"], unique: true }, { name: "Entering an anti-magic zone.", effect: "You cannot use ultimate powers.", lock: ["ultimates"], duration: 3, later: { name: "Leaving the anti-magic zone.", effect: "You may use ultimate powers again.", clear: ["ultimates"] } }, // Events from Sean Allen's random scenario generator. // http://boardgamegeek.com/filepage/57107/random-scenario-generator { name: "Fire from above.", effect: "Dragons gain a bonus of +1 Strength.", requires: ["dragons", "noncanonical"], unique: true }, { name: "Backs against the wall.", effect: "Any monster with a Tenacity token is also Fierce.", requires: ["noncanonical"], unique: true }, { name: "The tide has turned.", effect: "All monsters have Supremacy.", requires: ["noncanonical"], unique: true }, { name: "Into the darkness.", effect: "Choose one corridor. Its cards are now always placed face-down.", requires: ["noncanonical"] }, // Events of my own devising. { name: "Unwanted attention.", effect: "Unique monsters gain a bonus of +1 Strength.", requires: ["noncanonical"], unique: true }, { name: "Spiked the punch.", effect: "Greenskins have 1d8 Strength.", requires: ["noncanonical"], unique: true }, { name: "Twisty passages.", effect: "After defeating a card, roll a die. On an odd number its replacement is placed face-down.", requires: ["noncanonical"], unique: true }, { name: "Closer than you think.", action: "The dungeon gains another Fear token, and a new event occurs.", requires: ["noncanonical"], another: true, unique: true }, { name: "I thought you had it.", action: "Randomly discard four of your defeated dungeon cards.", requires: ["noncanonical"] }, { name: "Adamantine armor.", effect: "All monsters gain Immunity 1.", requires: ["noncanonical"], unique: true }, { name: "Normative assumptions.", effect: "Effects concerning ♂ instead concern ♀, and vice versa.", requires: ["noncanonical"], unique: true }, { name: "Infighting.", effect: "Greenskins have Undead +1, Demons -1. Demons have Greenskins +1, Undead -1. Undead have Demons +1, Greenskins -1.", requires: ["noncanonical", "demons", "undead"], unique: true }, { name: "New moon.", effect: "Demons have Shrouded.", requires: ["noncanonical", "demons"], unique: true }, { name: "Camouflage.", effect: "Greenskins have Shrouded.", requires: ["noncanonical"], unique: true }, { name: "Cryptic shades.", effect: "Undead have Shrouded.", requires: ["noncanonical", "undead"], unique: true }, { name: "Lingering smoke.", effect: "Dragons have Shrouded.", requires: ["noncanonical", "dragons"], unique: true }, { name: "Hypnotizing gaze.", effect: "Bubbleyes have Shrouded.", requires: ["noncanonical", "bubbleyes"], unique: true }, { name: "Leeched power.", action: "A random adventurer loses a Mana token.", requires: ["noncanonical"] }, ]; var NOP = [ { name: "Nothing happens." }, { name: "A draft blows down the hallway.", unique: true, requires: ["noncanonical"] }, { name: "You sneeze.", unique: true, requires: ["noncanonical"] }, { name: "There's a skittering in the distance.", unique: true, requires: ["noncanonical"] }, { name: "The torch flickers.", unique: true, requires: ["noncanonical"] }, { name: "Shadows dance across the walls.", unique: true, requires: ["noncanonical"] }, ]; var HELPFUL = [ { name: "It cuts both ways.", action: "Next time you roll a 1, put a Tenacity token on all face-up monsters and roll again.", requires: ["helpful"] }, { name: "Breached their defense.", action: "Gain 2 Courage tokens and the dungeon gains 1 Fear token and the Fate Chart advances; or gain 1 Mana token.", requires: ["helpful"], unique: true }, { name: "Mana ritual.", action: "Discard up to 2 Mana tokens. For each one discarded, gain 1 Courage token.", requires: ["helpful"] } ]; var LOSE = { name: "Your adventuring party is defeated!" }; function randrange (a, b) { return a + (Math.random() * (b - a)) | 0; } function generate (flags, events, nop) { var chosen = []; var i; var pending = []; var locks = []; var event; function pend (event, i) { while (pending[i]) ++i pending[i] = event; } function canStillHappen (event) { return issubset(event.requires || [], flags) && !(event.unique && contains.call(chosen, event)) && !(event.lock && intersects(event.lock || [], locks)); } for (i = 0; i < events; ++i) { event = pending.shift() || choice(EVENTS.filter(canStillHappen)); chosen.push(event); if (event.later) pend(event.later, (Math.random() * chosen[i].duration) | 0); locks = locks.concat(event.lock || []) .filter(not(contains), event.clear || []); } for (i = 0; i < pending.length; ++i) if (pending[i]) chosen.push(pending[i]); for (i = 0; i < nop / 2; ++i) { var helpful = HELPFUL.filter(canStillHappen); var neutral = NOP.filter(canStillHappen); chosen.splice( randrange(0, chosen.length), 0, choice(helpful.length ? helpful : neutral)); } for (;i < nop; ++i) { var neutral = NOP.filter(canStillHappen); chosen.splice(randrange(0, chosen.length), 0, choice(neutral)); } chosen.push(LOSE); return chosen; } function toHTML (event) { return ["" + event.name + "", event.action ? "" + event.action + "" : "", event.effect ? "" + event.effect + "" : "", ].join(" "); } var THEME = ("h1 { color: hsl(XXX, 25%, 75%); }\n\ h2, .button, select { border-color: hsl(XXX, 30%, 85%); }\n\ \n\ a:link, a:visited, a:active {\n\ color: hsl(XXX, 25%, 50%);\n\ }\n\ \n\ select, .button, tbody tr:nth-last-child(odd) {\n\ background-color: hsl(XXX, 30%, 85%);\n\ }\n\ \n\ h1,\n\ select:hover, select:focus,\n\ .button:hover, .button:focus {\n\ border-color: hsl(XXX, 25%, 50%);\n\ }"); function randomizeName () { var name = generateName(); document.getElementById("name").textContent = name; document.title = document.title.replace(/[^-]*-/, name + " -"); document.head.lastChild.textContent = THEME.replace( /XXX/g, (Math.random() * 256) | 0); } var events = []; var style; function generateScenario () { var parts = location.hash.slice(1).split(','); events = generate(parts, parts.shift() | 0, parts.shift() | 0); style = document.createElement("style"); document.head.appendChild(style); randomizeName(); } function getEvents (matcher) { return EVENTS.filter(matcher); } function wrapRow (row) { return "