X-Git-Url: https://git.yukkurigames.com/?p=heroik.git;a=blobdiff_plain;f=scenarios.js;h=ce7d3820d13b350191c72bdc6d1b95f8fc23e6e2;hp=ce36acc94525936a0557ccbdd07f368e585ed94c;hb=b3a8e4e8782e0247c73601c44982564771aa00fa;hpb=a06e3713b2b32abdf1469267190d56eeb88f058d
diff --git a/scenarios.js b/scenarios.js
index ce36acc..ce7d382 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))
+ && !(event.lock && 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;