X-Git-Url: https://git.yukkurigames.com/?p=heroik.git;a=blobdiff_plain;f=scenarios.js;h=2b24e6f435bd8eaacf55c283df3a2e4cc5a8b7a9;hp=ce36acc94525936a0557ccbdd07f368e585ed94c;hb=dff204dd481ca30605bf45af479e502eb550b165;hpb=a06e3713b2b32abdf1469267190d56eeb88f058d diff --git a/scenarios.js b/scenarios.js index ce36acc..2b24e6f 100644 --- a/scenarios.js +++ b/scenarios.js @@ -27,6 +27,10 @@ var EVENTS = [ 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"] }, @@ -35,6 +39,8 @@ var EVENTS = [ 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: "Vague psi.", + 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.", @@ -57,6 +63,38 @@ var EVENTS = [ 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 item." }, + + { 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 @@ -64,7 +102,7 @@ var EVENTS = [ effect: "Dragons gain a bonus of +1 Strength.", requires: ["dragons", "noncanonical"], unique: true }, - { name: "Bad dreams.", + { name: "Overwhelming fear.", action: "Lose 1 extra Courage token. (The dungeon does not gain another Fear token.)", requires: ["noncanonical"], unique: true }, @@ -72,26 +110,17 @@ var EVENTS = [ effect: "Any monster with a Tenacity token is also Fierce.", requires: ["noncanonical"], unique: true }, - { name: "Surrounded.", + { name: "The tide has turned.", effect: "All monsters have Supremacy.", requires: ["noncanonical"], unique: true }, - { name: "The torch has gone out.", - action: "The next dungeon card is placed face-down.", }, - - // Events from Stephane Renard's scenario. - // http://docfox.free.fr/spip.php?article129 - { name: "Malediction.", - action: "Discard all quests.", - requires: ["noncanonical"], - unique: true }, - { name: "Reanimation.", - action: "Return a random spent dungeon card to the smallest corridor and shuffle it. (If you haven't spent any cards, nothing happens.)", + { name: "Lost.", + action: "Shuffle the remaining corridors together and redistribute the cards as if you were setting up the game.", requires: ["noncanonical"] }, - { name: "Zone of silence.", - effect: "Temporary and Ultimate Powers cannot be used until you reveal a new card.", + { 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.", @@ -101,7 +130,7 @@ var EVENTS = [ effect: "Greenskins have 1d8 Strength.", requires: ["noncanonical"], unique: true }, - { name: "Lost in the fog.", + { 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 }, @@ -144,10 +173,34 @@ var EVENTS = [ effect: "Bubbleyes have Veil of Shadow.", requires: ["noncanonical", "bubbleyes"], unique: true }, + { name: "Leeched power.", + action: "A random adventurer loses a Mana token.", + requires: ["noncanonical"] }, ]; -var NOTHING = { name: "Nothing happens." }; +var NOP = [ + { name: "Nothing happens." }, + { name: "A draft blows down the hallway.", + requires: ["noncanonical"] }, + { name: "You sneeze.", + requires: ["noncanonical"] }, + { name: "There's a skittering in the distance.", + 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) { @@ -158,16 +211,49 @@ 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.unique && contains.call(chosen, event)) + && !intersects(event.lock || [], locks); } - for (i = 0; i < events; ++i) - chosen.push(choice(EVENTS.filter(canStillHappen))); + 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; ++i) - chosen.splice(randrange(0, chosen.length), 0, NOTHING); + + 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;