5412e29f40efa44e96e159a2dc4fa6900820dec7
1 /* The person who associated a work with this deed has dedicated the
2 work to the public domain by waiving all of his or her rights to
3 the work worldwide under copyright law, including all related and
4 neighboring rights, to the extent allowed by law.
6 You can copy, modify, distribute and perform the work, even for
7 commercial purposes, all without asking permission.
9 See https://creativecommons.org/publicdomain/zero/1.0/ for details.
15 { name
: "The Greenskins are mobilized.",
16 effect
: "Greenskins gain a bonus of +1 Strength." },
17 { name
: "Big eye is watching you.",
18 effect
: "Bubbleyes gain a bonus of +1 Strength.",
19 requires
: ["bubbleyes"] },
20 { name
: "General invocation.",
21 effect
: "Demons gain a bonus of +1 Strength.",
22 requires
: ["demons"] },
23 { name
: "Stronger than ever.",
24 effect
: "Undead gain a bonus of +1 Strength.",
25 requires
: ["undead"] },
26 { name
: "Meeting with Abunakkashi.",
27 effect
: "Abunakkashii and his Offspring gain a bonus of +2 Strength.",
28 requires
: ["abunakkashii"],
30 { name
: "Technological prowess.",
31 effect
: "Traps gain a bonus of +1 Strength.",
32 requires
: ["traps"] },
34 { name
: "Mental combat.",
35 effect
: "Psi monsters gain a bonus of +1 Strength.", },
36 { name
: "A cry in the night.",
37 action
: "An adventurer of your choice loses 1 Mana." },
38 { name
: "It's an ambush!",
39 action
: "Place a Tenacity token on each monster that does not have one." },
40 { name
: "The alarm is sounded.",
41 action
: "Place a Tenacity token on the weakest monster or monsters that do not already have one." },
42 { name
: "Flank attack.",
43 effect
: "Cards on either side corridor gain a bonus of +1 Strength.",
45 { name
: "Battle formation.",
46 effect
: "Cards in the central corridor(s) gain a bonus of +1 Strength.",
48 { name
: "Last bastion.",
49 effect
: "All cards gain a bonus of +1 Strength.",
52 { name
: "Destruction.",
53 action
: "All equipment and items are destroyed.",
55 { name
: "Epic combat.",
56 effect
: "The Final Monster gains a bonus of +3 Strength.",
58 { name
: "Red herring.",
59 action
: "Shuffle the remaining corridors together and redistribute the cards as if you were setting up the game." },
61 // Events from Sean Allen's random scenario generator.
62 // http://boardgamegeek.com/filepage/57107/random-scenario-generator
63 { name
: "Fire from above.",
64 effect
: "Dragons gain a bonus of +1 Strength.",
65 requires
: ["dragons", "noncanonical"],
67 { name
: "Bad dreams.",
68 action
: "Lose 1 extra Courage token. (The dungeon does not gain another Fear token.)",
69 requires
: ["noncanonical"],
71 { name
: "Backs against the wall.",
72 effect
: "Any monster with a Tenacity token is also <strong>Fierce</strong>.",
73 requires
: ["noncanonical"],
75 { name
: "Surrounded.",
76 effect
: "All monsters have <strong>Supremacy</strong>.",
77 requires
: ["noncanonical"],
80 // Events from Stephane Renard's scenario.
81 // http://docfox.free.fr/spip.php?article129
82 { name
: "What was that?",
83 action
: "The next dungeon card is placed face-down.",
84 requires
: ["noncanonical"] },
85 { name
: "Malediction.",
86 action
: "Discard all quests.",
87 requires
: ["noncanonical"],
89 { name
: "Reanimation.",
90 action
: "Return a random spent dungeon card to the smallest corridor and shuffle it. (If you haven't spent any cards, nothing happens.)",
91 requires
: ["noncanonical"] },
92 { name
: "Zone of silence.",
93 effect
: "Temporary and Ultimate Powers cannot be used until you reveal a new card.",
94 requires
: ["noncanonical"] },
96 // Events of my own devising.
97 { name
: "Unwanted attention.",
98 effect
: "Unique monsters gain a bonus of +1 Strength.",
99 requires
: ["noncanonical"],
101 { name
: "Spiked the punch.",
102 effect
: "Greenskins have 1d8 Strength.",
103 requires
: ["noncanonical"],
105 { name
: "Dropped the torch.",
106 effect
: "After defeating a card, roll a die. On an odd number its replacement is placed face-down.",
107 requires
: ["noncanonical"],
109 { name
: "Closer than you think.",
110 action
: "The dungeon gains another Fear token, and a new event occurs.",
111 requires
: ["noncanonical"],
112 another
: true, unique
: true },
113 { name
: "I thought you had it.",
114 action
: "Randomly discard four of your defeated dungeon cards.",
115 requires
: ["noncanonical"] },
116 { name
: "Adamantine armor.",
117 effect
: "All monsters gain <strong>Immunity 1</strong>.",
118 requires
: ["noncanonical"],
120 { name
: "Normative assumptions.",
121 effect
: "Effects concerning ♂ instead concern ♀, and vice versa.",
122 requires
: ["noncanonical"],
124 { name
: "Infighting.",
125 effect
: "Greenskins have <strong>Undead +1</strong>, <strong>Demons -1</strong>. Demons have <strong>Greenskins +1</strong>, <strong>Undead -1</strong>. Undead have <strong>Demons +1</strong>, <strong>Greenskins -1</strong>.",
126 requires
: ["noncanonical"],
130 var NOTHING
= { name
: "Nothing happens." };
131 var LOSE
= { name
: "Your adventuring party is defeated!" };
133 function randrange (a
, b
) {
134 return a
+ (Math
.random() * (b
- a
)) | 0;
137 function generate (flags
, events
, nop
) {
141 function canStillHappen (event
) {
142 return issubset(event
.requires
|| [], flags
)
143 && !(event
.unique
&& contains
.call(chosen
, event
));
146 for (i
= 0; i
< events
; ++i
)
147 chosen
.push(choice(EVENTS
.filter(canStillHappen
)));
149 for (i
= 0; i
< nop
; ++i
)
150 chosen
.splice(randrange(0, chosen
.length
), 0, NOTHING
);
156 function toHTML (event
) {
157 return ["<span class=fate-name>" + event
.name
+ "</span>",
159 ? "<span class=fate-action>" + event
.action
+ "</span>"
162 ? "<span class=fate-effect>" + event
.effect
+ "</span>"
167 var THEME
= ("h1 { color: hsl(XXX, 25%, 75%); }\n\
168 h2, .button, select { border-color: hsl(XXX, 30%, 85%); }\n\
170 a:link, a:visited, a:active {\n\
171 color: hsl(XXX, 25%, 50%);\n\
174 select, .button, tbody tr:nth-last-child(odd) {\n\
175 background-color: hsl(XXX, 30%, 85%);\n\
179 select:hover, select:focus,\n\
180 .button:hover, .button:focus {\n\
181 border-color: hsl(XXX, 25%, 50%);\n\
184 function randomizeName () {
185 var name
= generateName();
186 document
.getElementById("name").textContent
= name
;
187 document
.title
= document
.title
.replace(/[^-]*-/, name
+ " -");
188 document
.head
.lastChild
.textContent
= THEME
.replace(
189 /XXX/g, (Math
.random() * 256) | 0);
195 function generateScenario () {
196 var parts
= location
.hash
.slice(1).split(',');
197 events
= generate(parts
, parts
.shift() | 0, parts
.shift() | 0);
198 style
= document
.createElement("style");
199 document
.head
.appendChild(style
);
203 function getEvents (matcher
) {
204 return EVENTS
.filter(matcher
);
207 function wrapRow (row
) {
208 return "<tr><td>" + row
+ "</td></tr>";
211 function iscanonical (event
) {
212 return !isnoncanonical(event
);
214 function isnoncanonical (event
) {
215 return ~(event
.requires
|| []).indexOf('noncanonical');
218 function canonicalToHTML (sender
) {
219 sender
.innerHTML
= EVENTS
.filter(iscanonical
)
220 .map(toHTML
).sort().map(wrapRow
).join('');
223 function noncanonicalToHTML (sender
) {
224 sender
.innerHTML
= EVENTS
.filter(isnoncanonical
)
225 .map(toHTML
).sort().map(wrapRow
).join('');
228 function nextEvent (sender
) {
229 if (!events
.length
) {
234 var event
= events
.shift();
235 var body
= document
.querySelector("#fate tbody");
236 var fate
= body
.children
.length
+ 1;
237 var tr
= document
.createElement('tr');
238 tr
.innerHTML
= "<td><div>" + fate
+ "</div></td>"
239 + "<td><div>" + toHTML(event
) + "</div></td>";
240 body
.insertBefore(tr
, body
.firstChild
);
242 if (events
.length
=== 0) {
243 sender
.textContent
= "Try Again ▲";
247 setTimeout(function () { nextEvent(sender
); }, 333);