1025 lines
51 KiB
JavaScript
1025 lines
51 KiB
JavaScript
/* The Quest for the Deleted Website (quest.html only).
|
|
|
|
The quest survives reloads (and even restoring the website): the save
|
|
file is questHero + questPronouns (JSON) + questWeapon + questHand +
|
|
questEncounters (JSON) + questEnc1 (JSON) + questEnc2 + questKid +
|
|
questEnc3 + questEnc4 + questEnc5 + questPrincess + questWeaponLost +
|
|
questVillain + questVillainPronouns (JSON) + questAttacked in
|
|
localStorage.
|
|
|
|
All story text lives in TEXT, written in full with {token} substitutions.
|
|
Tokens: {name} plus the pronoun forms {subj} {obj} {posd} {refl}.
|
|
Capitalize the token to capitalize the substitution: {Subj} -> "She" */
|
|
(function () {
|
|
var naming = document.getElementById('naming');
|
|
if (!naming) return;
|
|
|
|
var TEXT = {
|
|
intro: '{name} was but a simple farmer living in the magical kingdom of' +
|
|
' Grazaikya, until one day when gade.gg was deleted.',
|
|
setout: '{name} had wanted to log onto gade.gg, {posd} favorite website, but saw' +
|
|
' that it had been deleted, presumably by an evil, heartless monster.' +
|
|
' So {subj} picked up {posd} preferred weapon, and embarked on a quest' +
|
|
' to find the deleted website.',
|
|
traverse: 'Clutching {posd} {weapon} in {posd} {hand} hand, {name}' +
|
|
' traversed across the lands of Grazaikya, determined to recover the' +
|
|
' deleted site.',
|
|
enc1: '{name} attempted to cross a bridge over a river, but was' +
|
|
' accosted by a hideous creature. A bridge troll stopped {obj} and' +
|
|
' said "Before you may cross the river, you must first answer my' +
|
|
' riddle!".',
|
|
enc1riddle: '"Very well, then", said {name}, "tell me your riddle,' +
|
|
' hideous beast". "Wow, rude" said the troll, aghast. It then stood' +
|
|
' for a moment, waiting for an apology that never came, and then' +
|
|
' continued: "I run, but never walk; I babble, but never talk. What' +
|
|
' am I?".',
|
|
enc1attack: '{name} {attacked} the troll{withweapon}, but the' +
|
|
' troll attacked back, ripping off {posd} leg. {name} {attacked} the' +
|
|
' troll one more time, which ended its life. {name} hobbled away.',
|
|
enc1wrong: '"{answer}?" asked {name}, unsure of {refl}. "Nope", said' +
|
|
' the troll, picking up {name} above its head. With a horrible' +
|
|
" sound, the troll ripped off {name}'s leg, and threw {obj} across" +
|
|
' to the other side of the bridge. {name} hobbled away from the' +
|
|
' troll, who was laughing triumphantly.',
|
|
enc1correct: '"A river!" said {name}. "Yeah, that\'s right!", answered' +
|
|
' the troll, "you\'re, like, super smart!". "Now let me pass, hideous' +
|
|
' beast", demanded {name}. The troll obliged, despite being pretty' +
|
|
' hurt by the name-calling. {name} continued onwards on {posd} quest.',
|
|
enc2: 'While walking on a path through grassy fields, a young boy, no' +
|
|
' older than 16, ran up to {name}. "Are you an adventurer? Are you' +
|
|
' on a quest? Can I join?", the kid asked excitedly.',
|
|
enc2yes: '"Why, certainly. The more the merrier!", said {name},' +
|
|
' cheerfully. The kid jumped up and twirled around in the air.' +
|
|
' "You won\'t regret this!", he said, foreshadowingly, "My name\'s' +
|
|
' Breigan!". Breigan and {name} continued on their quest.',
|
|
enc2no: '"Shouldn\'t you be with your parents, kid?", asked {name}.' +
|
|
' The boy lowered his head and looked at the ground.' +
|
|
' "I don\'t have parents", he said, "not since the fire". He then' +
|
|
' slowly walked away, sulking, leaving {name} to continue {posd}' +
|
|
' quest alone.',
|
|
enc3: 'At nightfall, {name} came across an abandoned cottage. Feeling' +
|
|
' tired, {subj} decided to use it for shelter for the night. Inside' +
|
|
' the cottage, {name} saw a seemingly untouched chest amongst the' +
|
|
' rubble. {Subj} tried to open it, but it was locked.',
|
|
enc3pryBtn: 'Pry it open with your {weapon}',
|
|
enc3pry: '{name} attempted to force the chest open with {posd}' +
|
|
' {weapon}. After a few tries, the chest broke open, revealing a' +
|
|
' horrifying row of teeth inside. The mimic lunged forward towards' +
|
|
' {name}, who, in {posd} panic, let go of {posd} {weapon}. {name}' +
|
|
' left the cottage, continuing the quest without {posd} {weapon}.',
|
|
enc3leave: '"Better to leave things like that alone", {name} thought.' +
|
|
" {Subj} had a good night's rest in the cottage, and continued" +
|
|
' onwards on {posd} quest in the morning.',
|
|
enc4: 'While travelling along a road, {name} met an old man selling' +
|
|
' potions. "Potionseller!", {subj} said, "I am going into battle,' +
|
|
' and I need your strongest potions". "Well", the old man answered,' +
|
|
' "my strongest potions are quite expensive. Do you have the coin?".' +
|
|
' {name} did, in fact, not have any money.',
|
|
enc4steal: '{name} {attacked} the potionseller{withweapon}, and took' +
|
|
' one of his potions. Once {subj} was far enough away, {subj} drank' +
|
|
' it. Suddenly, {subj} could feel {posd} body changing: {posd} limbs' +
|
|
' shrinking and {posd} nose embiggening. Suddenly, {name} found' +
|
|
' {refl} in the body of a pig. {Subj} sulked for a bit, and then' +
|
|
' continued onwards on {posd} quest, as a pig.',
|
|
enc4explain: '"I have no money", {name} said, "but if you-", {subj}' +
|
|
' continued, but before {subj} could finish, the potionseller spat' +
|
|
' in {posd} face and said "piss off, poor-y!". {name} continued' +
|
|
' onwards on {posd} quest, potionless.',
|
|
enc5: '{name} was walking along a road when {subj} passed a tower' +
|
|
' standing by the path. A beautiful young woman stuck her head out' +
|
|
' of the tallest window and shouted at {name}, "Oi! Are you here to' +
|
|
' rescue me?". Flustered, {name} answered "I wasn\'t planning on' +
|
|
' it. Are you a princess or something?". "Yeah, probably", said the' +
|
|
' likely princess.',
|
|
enc5decline: '"Actually, I\'m quite busy", said {name}, walking away.' +
|
|
' "Aw, come on?", the probable princess asked. "No no, I simply' +
|
|
' must go", replied {name}, already too far away to hear whatever' +
|
|
' the potential princess said next.',
|
|
enc5agree: '"All right, then", said {name}, "I\'m sure saving you' +
|
|
' won\'t take too much time". {Subj} looked at {posd} options.',
|
|
enc5vines: '{name} started climbing up the side of the tower,' +
|
|
' grabbing on to the vines. The probable princess looked puzzled:' +
|
|
' "You know the door is unlocked, right?". {name}, already out of' +
|
|
' breath, replied "No, I\'ve committed now!". {Subj} had climbed up' +
|
|
' roughly 30 cm and was already straining all of {posd} muscles to' +
|
|
' hang on to the vines. "Seriously", said the princess, worried,' +
|
|
' "you should really just take the door". {name} lost {posd} grip' +
|
|
' on the vines and tumbled to the ground. Embarrassed, {subj}' +
|
|
' quickly ran away, continuing {posd} quest.',
|
|
enc5door: '{name} walked up to the front door, expecting it to be' +
|
|
' locked. It was not. {Subj} then walked up the spiral staircase,' +
|
|
' expecting to be jumped by a monster or something. {Subj} was not.' +
|
|
" Finally, {subj} reached the potential princess' door, which was" +
|
|
' already open. "I\'m confused", said {name}, "What did you need' +
|
|
' saving from?". "Boredom, mainly", replied the' +
|
|
' now-not-quite-so-likely princess. "I\'m Anastasia, by the way",' +
|
|
' she said, while walking down the stairs. Before {name} could' +
|
|
' protest, Anastasia had already joined {posd} quest.',
|
|
princessrename: "{name} didn't like Anastasia's name, so {subj}" +
|
|
' started calling her {princess} instead.',
|
|
kidrename: "{name} didn't like Breigan's name, so {subj} started" +
|
|
' calling him {kid} instead.',
|
|
arrival: '{name} arrived at the tower of the evil wizard {subj} had' +
|
|
' heard was behind the deletion of gade.gg, {posd} favorite website.',
|
|
confront: '{name} went inside the tower, seeing the massive dark' +
|
|
' gothic interior. There, sitting on a throne, was {vname}.' +
|
|
' "I suspect you have come to kill me", {vsubj} said, "for deleting' +
|
|
' gade.gg, your favorite website."'
|
|
};
|
|
|
|
/* The showdown endings, one handwritten text per situation. Key:
|
|
chosen encounters' 1-based pool positions, a colon, then one S/F
|
|
letter each, all in pool order — '123:SFS' = troll+kid+mimic with
|
|
only the kid failed. Success is: troll = riddle solved, Breigan = joined,
|
|
mimic = chest left alone. The wizard is only beatable with >= 2 S.
|
|
Villain tokens: {vname} {vsubj} {vobj} {vposd} {vrefl}.
|
|
Empty entries render nothing */
|
|
var ENDINGS = {
|
|
'123:SSS': '{name} ran towards {vname}, {attacking} {vobj} with {posd}' +
|
|
' {weapon}. {kid} came up behind {vname}, pushing {vobj}, and' +
|
|
' making {vobj} lose {vposd} balance. {vname} tried to cast a spell' +
|
|
' at either of them, but it was too late. {name} and {kid} killed' +
|
|
' {vname}, restoring the website for good.',
|
|
'123:SSF': '{name} and {kid} ran towards {vname}, ready to punch {vobj}.' +
|
|
' With a flick of {vposd} wrist, {vname} cast a spell on {kid},' +
|
|
' disintegrating him immediately. {name} yelled out towards him, but' +
|
|
' there was nothing {subj} could do. Nothing, except avenge him.' +
|
|
' {name} ran up to {vname} and delivered a powerful punch. A punch' +
|
|
' so powerful that {vposd} head exploded. {vname} was dead, and the' +
|
|
' website was restored for good.',
|
|
'123:SFS': '{name} ran towards {vname}. An epic duel commenced, with {vname}' +
|
|
' throwing spells and {name} {attacking} with {posd} {weapon}.' +
|
|
' Finally, when the dust settled, {name} stood victorious. {vname}' +
|
|
' was dead, and the website was restored for good.',
|
|
'123:SFF': '{name} ran towards {vname}, ready to punch {vobj}. With a flick' +
|
|
' of {vposd} wrist, {vname} cast a spell on {name}, disintegrating' +
|
|
' {obj} immediately.',
|
|
'123:FSS': '{name} hobbled towards {vname}, {attacking} {vobj} with {posd}' +
|
|
' {weapon}. {kid} came up behind {vname}, pushing {vobj}, and' +
|
|
' making {vobj} lose {vposd} balance. {vname} tried to cast a spell' +
|
|
' at either of them, but it was too late. {name} and {kid} killed' +
|
|
' {vname}, restoring the website for good.',
|
|
'123:FSF': '{name} hobbled towards {vname}, ready to punch {vobj}. With a' +
|
|
' flick of {vposd} wrist, {vname} cast a spell on {kid},' +
|
|
' disintegrating him immediately. {name} yelled out towards him, but' +
|
|
' there was nothing {subj} could do. With another flick of {vposd}' +
|
|
' wrist, {vname} cast the same spell on {name}, disintegrating {obj}' +
|
|
' as well.',
|
|
'123:FFS': '{name} hobbled towards {vname}, {attacking} {vobj} with {posd}' +
|
|
' {weapon}. With a flick of {vposd} wrist, {vname} cast a spell on' +
|
|
' {name}, disintegrating {obj} immediately.',
|
|
'123:FFF': '{name} hobbled towards {vname}, ready to punch {vobj}. With a' +
|
|
' flick of {vposd} wrist, {vname} cast a spell on {name},' +
|
|
' disintegrating {obj} immediately.',
|
|
/* Family 124 (troll, Breigan, potion): the one-legged pigs. The
|
|
other six situations are aliased below */
|
|
'124:FSF': '{name} hobbled towards {vname} on {posd} three remaining' +
|
|
' trotters, {posd} {weapon} clenched in {posd} pig mouth, with' +
|
|
' {kid} running ahead. With a flick of {vposd} wrist, {vname} cast' +
|
|
' a spell on {kid}, disintegrating him immediately. {name} let out' +
|
|
' a devastated squeal. With another flick of {vposd} wrist, {vname}' +
|
|
' cast the same spell on {name}, disintegrating {obj} as well.',
|
|
'124:FFF': '{name} hobbled towards {vname} on {posd} three remaining' +
|
|
' trotters, {attacking} {vobj} with {posd} {weapon}. {vname}' +
|
|
' watched the limping pig approach for a while, mildly fascinated.' +
|
|
' Then, with a flick of {vposd} wrist, {vsubj} cast a spell on' +
|
|
' {name}, disintegrating {obj} immediately.',
|
|
/* Family 134 (troll, mimic, potion): no Breigan, so the win
|
|
threshold lands differently — same body, different fate. The
|
|
three overlapping-outcome situations are aliased below */
|
|
'134:SSF': '{name} charged towards {vname}, clutching {posd} {weapon}' +
|
|
' in {posd} pig mouth. {vname} laughed at the sight, flicking' +
|
|
' {vposd} wrist to cast a spell. But the spell missed: {vname} had' +
|
|
' never fought a pig before, and pigs are much lower to the ground' +
|
|
' than the heroes {vsubj} was used to. {name} {attacked} {vobj}' +
|
|
' before {vsubj} could try again. {vname} was dead, and the website' +
|
|
' was restored for good.',
|
|
'134:SFS': '{name} ran towards {vname}, ready to punch {vobj}.' +
|
|
' {vname} flicked {vposd} wrist and cast a spell, but {name} dodged' +
|
|
' it and kept running. {Subj} reached the throne and delivered a' +
|
|
' powerful punch. A punch so powerful that {vposd} head exploded.' +
|
|
' {vname} was dead, and the website was restored for good.',
|
|
'134:FSS': '{name} hobbled towards {vname}, {attacking} {vobj} with' +
|
|
' {posd} {weapon}. An epic duel commenced, with {vname} throwing' +
|
|
' spells and {name} deflecting them on one leg. Finally, when the' +
|
|
' dust settled, {name} stood victorious. {vname} was dead, and the' +
|
|
' website was restored for good.',
|
|
'134:FFS': '{name} hobbled towards {vname}, ready to punch {vobj}.' +
|
|
' {vname} watched {posd} slow approach with something between pity' +
|
|
' and boredom. With a flick of {vposd} wrist, {vname} cast a spell' +
|
|
' on {name}, disintegrating {obj} immediately.',
|
|
'134:FFF': '{name} hobbled towards {vname} on {posd} three remaining' +
|
|
' trotters, ready to bite {vobj}. {vname} waited politely for the' +
|
|
' pig to arrive. Then, with a flick of {vposd} wrist, {vsubj} cast' +
|
|
' a spell on {name}, disintegrating {obj} immediately.',
|
|
/* Family 234 (Breigan, mimic, potion): pig situations. The human
|
|
ones are aliased from family 123 below */
|
|
'234:SSF': '{name} charged towards {vname}, clutching {posd} {weapon}' +
|
|
' in {posd} pig mouth. {vname} hesitated. {Vsubj} had prepared for' +
|
|
' many things, but not for a pig with {aweapon}. {kid} used the' +
|
|
' moment to come up behind {vname}, pushing {vobj} and making {vobj}' +
|
|
' lose {vposd} balance. {name} {attacked} {vobj} before {vsubj}' +
|
|
' could get back up. {vname} was dead, and the website was restored' +
|
|
' for good.',
|
|
'234:SFF': '{kid} ran towards {vname}, ready to punch {vobj}, with' +
|
|
' {name} trotting closely behind. With a flick of {vposd} wrist,' +
|
|
' {vname} cast a spell on {kid}, disintegrating him immediately.' +
|
|
' {name} let out a devastated squeal. With another flick of {vposd}' +
|
|
' wrist, {vname} cast the same spell on {name}, disintegrating {obj}' +
|
|
' as well.',
|
|
'234:FSF': '{name} trotted towards {vname}, {attacking} {vobj} with' +
|
|
' {posd} {weapon}. {vname} was briefly impressed by the sight of a' +
|
|
' pig wielding {aweapon}, but not enough to be stopped. With a' +
|
|
' flick of {vposd} wrist, {vname} cast a spell on {name},' +
|
|
' disintegrating {obj} immediately.',
|
|
'234:FFF': '{name} trotted towards {vname}, ready to bite {vobj}.' +
|
|
' {vname} looked at the pig for a long moment, almost feeling sorry' +
|
|
' for it. Then, with a flick of {vposd} wrist, {vsubj} cast a spell' +
|
|
' on {name}, disintegrating {obj} immediately.',
|
|
/* Family 345 (mimic, potion, princess): Anastasia situations. The
|
|
princess-less four are aliased below */
|
|
'345:SSS': '{name} ran towards {vname}, {attacking} {vobj} with' +
|
|
' {posd} {weapon}. {vname} blocked the attack with a shield spell' +
|
|
' and began preparing a counterattack. While the wizard was busy,' +
|
|
' {princess} calmly walked up behind {vobj} and kicked {vobj} off' +
|
|
' the throne. {name} {attacked} {vobj} one final time before' +
|
|
' {vsubj} could get up. {vname} was dead, and the website was' +
|
|
' restored for good.',
|
|
'345:SFS': '{name} charged towards {vname}, clutching {posd} {weapon}' +
|
|
' in {posd} pig mouth. "Is that a pig?", asked {vname}, bewildered.' +
|
|
' "I\'m honestly as confused as you are", answered {princess},' +
|
|
' already walking up behind the throne. Before {vname} could figure' +
|
|
' out what was going on, {princess} pushed {vobj} to the ground,' +
|
|
' and {name} {attacked} {vobj}. {vname} was dead, and the website' +
|
|
' was restored for good.',
|
|
'345:FSS': '{name} ran towards {vname}, ready to punch {vobj}.' +
|
|
' {vname} flicked {vposd} wrist and cast a spell at {name}, but' +
|
|
' {princess} pulled {obj} out of the way at the last moment. Before' +
|
|
' {vname} could try again, {name} reached the throne and delivered' +
|
|
' a powerful punch. A punch so powerful that {vposd} head exploded.' +
|
|
' {vname} was dead, and the website was restored for good.',
|
|
'345:FFS': '{name} trotted towards {vname}, ready to bite {vobj},' +
|
|
' with {princess} walking behind. With a flick of {vposd} wrist,' +
|
|
' {vname} cast a spell at {princess}, who calmly sidestepped it.' +
|
|
' {Vsubj} then cast the same spell at {name}, who did not sidestep' +
|
|
' it, and was disintegrated immediately. {princess} shrugged, and' +
|
|
' showed herself out.',
|
|
/* Family 145 (troll, potion, princess): the one-legged Anastasia
|
|
situations. The other six are aliased below */
|
|
'145:FSS': '{name} hobbled towards {vname}, {attacking} {vobj} with' +
|
|
' {posd} {weapon}. {vname} aimed a spell at {posd} remaining leg,' +
|
|
' but {princess} threw a rock at {vposd} head, ruining {vposd}' +
|
|
' concentration. Before {vname} could recover, {name} reached the' +
|
|
' throne and {attacked} {vobj} one final time. {vname} was dead,' +
|
|
' and the website was restored for good.',
|
|
'145:FFS': '{name} hobbled towards {vname} on {posd} three remaining' +
|
|
' trotters, {posd} {weapon} clenched in {posd} pig mouth, with' +
|
|
' {princess} walking behind. With a flick of {vposd} wrist, {vname}' +
|
|
' cast a spell at {princess}, who calmly sidestepped it. {Vsubj}' +
|
|
' then cast the same spell at {name}, who, being a three-legged' +
|
|
' pig, could not. {princess} sighed, muttered something under her' +
|
|
' breath, and left the tower.',
|
|
/* Family 135 (troll, mimic, princess): one new situation — the rest
|
|
alias below */
|
|
'135:FFS': '{name} hobbled towards {vname}, ready to punch {vobj},' +
|
|
' leaning on {princess} for support. With a flick of {vposd} wrist,' +
|
|
' {vname} cast a spell at {princess}, who calmly sidestepped it.' +
|
|
' {Vsubj} then cast the same spell at {name}, who could not' +
|
|
' sidestep anything, and was disintegrated immediately. {princess}' +
|
|
' stood in silence for a moment, then left the tower without a word.',
|
|
/* Family 125 (troll, Breigan, princess): armed human throughout, so
|
|
three losses have no armed-and-able precedent to alias */
|
|
'125:SSS': '{name}, {kid} and {princess} charged towards {vname}' +
|
|
' together. {vname} began casting a spell, but {princess} reached' +
|
|
' the throne first and pushed {vobj} to the floor. What followed' +
|
|
' was less of a battle and more of a group activity: the three of' +
|
|
' them simply kicked {vname} until {vsubj} stopped moving. {name}' +
|
|
' had briefly considered using {posd} {weapon}, but kicking had' +
|
|
' felt more appropriate. {vname} was dead, and the website was' +
|
|
' restored for good.',
|
|
'125:FSS': '{name} hobbled towards {vname}, but {kid} and {princess}' +
|
|
' had already run ahead. {princess} knocked {vname} to the ground,' +
|
|
' and {kid} sat down on {vposd} back to keep {vobj} there. By the' +
|
|
' time {name} finally reached the throne, {vname} was already dead.' +
|
|
' {Subj} tried not to feel left out. The website was restored for' +
|
|
' good.',
|
|
'125:SFF': '{name} ran towards {vname}, {attacking} {vobj} with' +
|
|
' {posd} {weapon}. An epic duel commenced, with {vname} throwing' +
|
|
' spells and {name} {attacking} back. Slowly, however, {name} began' +
|
|
' to tire. With a flick of {vposd} wrist, {vname} cast a spell on' +
|
|
' {name}, disintegrating {obj} immediately.',
|
|
'125:FSF': '{name} hobbled towards {vname}, {attacking} {vobj} with' +
|
|
' {posd} {weapon}, with {kid} running ahead. With a flick of' +
|
|
' {vposd} wrist, {vname} cast a spell on {kid}, disintegrating him' +
|
|
' immediately. {name} let out a scream and {attacked} {vobj} with' +
|
|
' everything {subj} had left. It was not enough. With another flick' +
|
|
' of {vposd} wrist, {vname} cast the same spell on {name},' +
|
|
' disintegrating {obj} as well.',
|
|
'125:FFS': '{name} hobbled towards {vname}, {attacking} {vobj} with' +
|
|
' {posd} {weapon}, leaning on {princess} for support. With a flick' +
|
|
' of {vposd} wrist, {vname} cast a spell at {princess}, who calmly' +
|
|
' sidestepped it. {name}, who had been leaning on her, lost {posd}' +
|
|
' balance and fell. With another flick of {vposd} wrist, {vname}' +
|
|
' cast a spell on {name}, disintegrating {obj} immediately.' +
|
|
' {princess} gave {vname} a long, disappointed look, and left the' +
|
|
' tower.',
|
|
/* Family 235 (Breigan, mimic, princess): intact human throughout.
|
|
The weaponless group kicking needs its own entry (the 125 version
|
|
mentions the weapon); plus the unarmed one-companion losses */
|
|
'235:SFS': '{name}, {kid} and {princess} charged towards {vname}' +
|
|
' together. {vname} began casting a spell, but {princess} reached' +
|
|
' the throne first and pushed {vobj} to the floor. What followed' +
|
|
' was less of a battle and more of a group activity: the three of' +
|
|
' them simply kicked {vname} until {vsubj} stopped moving. {vname}' +
|
|
' was dead, and the website was restored for good.',
|
|
'235:SFF': '{name} and {kid} ran towards {vname}, ready to punch' +
|
|
' {vobj}. With a flick of {vposd} wrist, {vname} cast a spell on' +
|
|
' {kid}, disintegrating him immediately. {name} yelled out towards' +
|
|
' him, but there was nothing {subj} could do. With another flick of' +
|
|
' {vposd} wrist, {vname} cast the same spell on {name},' +
|
|
' disintegrating {obj} as well.',
|
|
'235:FFS': '{name} ran towards {vname}, ready to punch {vobj}, with' +
|
|
' {princess} running beside {obj}. With a flick of {vposd} wrist,' +
|
|
' {vname} cast a spell at {princess}, who calmly sidestepped it.' +
|
|
' {Vsubj} then cast the same spell at {name}, who tried to sidestep' +
|
|
' it the same way {princess} had. {Subj} did not succeed, and was' +
|
|
' disintegrated immediately. {princess} pinched the bridge of her' +
|
|
' nose, turned around, and left the tower.',
|
|
/* Family 245 (Breigan, potion, princess): armed and able-bodied
|
|
throughout — the news are the pig situations */
|
|
'245:SFS': '{name}, {kid} and {princess} charged towards {vname}' +
|
|
' together. {vname} took a moment too long deciding whether to aim' +
|
|
' at the boy, the princess, or the pig with {aweapon} in its mouth.' +
|
|
' {princess} used that moment to push {vobj} off the throne, {kid}' +
|
|
' pinned {vposd} arms, and {name} {attacked} {vobj}. {vname} was' +
|
|
' dead, and the website was restored for good.',
|
|
'245:SFF': '{kid} ran towards {vname}, with {name} trotting behind,' +
|
|
' {posd} {weapon} clenched in {posd} pig mouth. With a flick of' +
|
|
' {vposd} wrist, {vname} cast a spell on {kid}, disintegrating him' +
|
|
' immediately. {name} let out a devastated squeal and charged. With' +
|
|
' another flick of {vposd} wrist, {vname} cast the same spell on' +
|
|
' {name}, disintegrating {obj} as well.',
|
|
'245:FFS': '{name} trotted towards {vname}, {posd} {weapon} clenched' +
|
|
' in {posd} pig mouth, with {princess} walking behind. With a flick' +
|
|
' of {vposd} wrist, {vname} cast a spell at {princess}, who calmly' +
|
|
' sidestepped it. {Vsubj} then cast the same spell at {name}. The' +
|
|
' {weapon} clattered to the floor, its owner now a pile of dust.' +
|
|
' {princess} picked it up, considered it for a moment, then put it' +
|
|
' down again and left the tower.'
|
|
};
|
|
/* Family 234 with a human hero (potion succeeded) is the same fight as
|
|
the corresponding two-legged 123 ending, reused verbatim */
|
|
ENDINGS['234:SSS'] = ENDINGS['123:SSS'];
|
|
ENDINGS['234:SFS'] = ENDINGS['123:SSF'];
|
|
ENDINGS['234:FSS'] = ENDINGS['123:SFS'];
|
|
ENDINGS['234:FFS'] = ENDINGS['123:SFF'];
|
|
/* Family 124 reuses: humans match 123 (same legs/kid/weapon), pigs
|
|
with both legs match 234 */
|
|
ENDINGS['124:SSS'] = ENDINGS['123:SSS'];
|
|
ENDINGS['124:SSF'] = ENDINGS['234:SSF'];
|
|
ENDINGS['124:SFS'] = ENDINGS['123:SFS'];
|
|
ENDINGS['124:SFF'] = ENDINGS['234:FSF'];
|
|
ENDINGS['124:FSS'] = ENDINGS['123:FSS'];
|
|
ENDINGS['124:FFS'] = ENDINGS['123:FFS'];
|
|
/* Family 134 reuses, matched on state AND outcome */
|
|
ENDINGS['134:SSS'] = ENDINGS['123:SFS'];
|
|
ENDINGS['134:SFF'] = ENDINGS['234:FFF'];
|
|
ENDINGS['134:FSF'] = ENDINGS['124:FFF'];
|
|
/* Family 345 reuses: no princess collapses to lone-hero situations */
|
|
ENDINGS['345:SSF'] = ENDINGS['123:SFS'];
|
|
ENDINGS['345:SFF'] = ENDINGS['234:FSF'];
|
|
ENDINGS['345:FSF'] = ENDINGS['123:SFF'];
|
|
ENDINGS['345:FFF'] = ENDINGS['234:FFF'];
|
|
/* Family 125 reuses (letters: troll, Breigan, princess) */
|
|
ENDINGS['125:SSF'] = ENDINGS['123:SSS'];
|
|
ENDINGS['125:SFS'] = ENDINGS['345:SSS'];
|
|
ENDINGS['125:FFF'] = ENDINGS['123:FFS'];
|
|
/* Family 245 reuses (letters: Breigan, potion, princess) */
|
|
ENDINGS['245:SSS'] = ENDINGS['125:SSS'];
|
|
ENDINGS['245:SSF'] = ENDINGS['123:SSS'];
|
|
ENDINGS['245:FSS'] = ENDINGS['345:SSS'];
|
|
ENDINGS['245:FSF'] = ENDINGS['125:SFF'];
|
|
ENDINGS['245:FFF'] = ENDINGS['234:FSF'];
|
|
/* Family 235 reuses (letters: Breigan, mimic, princess) */
|
|
ENDINGS['235:SSS'] = ENDINGS['125:SSS'];
|
|
ENDINGS['235:SSF'] = ENDINGS['123:SSS'];
|
|
ENDINGS['235:FSS'] = ENDINGS['345:SSS'];
|
|
ENDINGS['235:FSF'] = ENDINGS['125:SFF'];
|
|
ENDINGS['235:FFF'] = ENDINGS['123:SFF'];
|
|
/* Family 135 reuses (letters: troll, mimic, princess) */
|
|
ENDINGS['135:SSS'] = ENDINGS['345:SSS'];
|
|
ENDINGS['135:SSF'] = ENDINGS['123:SFS'];
|
|
ENDINGS['135:SFS'] = ENDINGS['345:FSS'];
|
|
ENDINGS['135:SFF'] = ENDINGS['123:SFF'];
|
|
ENDINGS['135:FSS'] = ENDINGS['145:FSS'];
|
|
ENDINGS['135:FSF'] = ENDINGS['123:FFS'];
|
|
ENDINGS['135:FFF'] = ENDINGS['134:FFS'];
|
|
/* Family 145 reuses (letters: troll, potion, princess) */
|
|
ENDINGS['145:SSS'] = ENDINGS['345:SSS'];
|
|
ENDINGS['145:SSF'] = ENDINGS['123:SFS'];
|
|
ENDINGS['145:SFS'] = ENDINGS['345:SFS'];
|
|
ENDINGS['145:SFF'] = ENDINGS['234:FSF'];
|
|
ENDINGS['145:FSF'] = ENDINGS['123:FFS'];
|
|
ENDINGS['145:FFF'] = ENDINGS['124:FFF'];
|
|
|
|
/* The encounter pool. ROLLED of these are drawn per hero when the hand
|
|
is chosen, stored in order in questEncounters; the ENDINGS set
|
|
signature uses 1-based pool positions. Each entry knows how its save
|
|
key resolves and what counts as success */
|
|
var POOL = ['troll', 'kid', 'mimic', 'potion', 'princess'];
|
|
var ROLLED = 3;
|
|
function rollEncounters() {
|
|
var pool = POOL.slice();
|
|
var picked = [];
|
|
while (picked.length < ROLLED && pool.length) {
|
|
picked.push(pool.splice(Math.floor(Math.random() * pool.length), 1)[0]);
|
|
}
|
|
return picked;
|
|
}
|
|
|
|
/* The hero. The weapon is an icon glyph dropped into the {w} cell of the
|
|
art (a fixed 2ch box, so the monospace grid survives). Keys are viewer
|
|
sides, not hands: the hero faces the viewer */
|
|
var ART = {
|
|
base: ' o\n/|\\\n/ \\',
|
|
right: ' o\n/|\\_{w}\n/ \\',
|
|
left: ' o\n{w}_/|\\\n / \\',
|
|
/* Breigan, age 16, shorter than the hero */
|
|
kid: ' o\n/|\\',
|
|
/* Anastasia. The circumflex is a crown */
|
|
princess: ' \u00f4\n/|\\\n/ \\',
|
|
/* The hero as a pig (potion theft). Still clutches the weapon */
|
|
pig: {
|
|
base: '^..^\n(oo)\n /\\',
|
|
right: '^..^\n(oo)_{w}\n /\\',
|
|
left: ' ^..^\n{w}_(oo)\n /\\'
|
|
}
|
|
};
|
|
|
|
/* fa: Font Awesome class; em: Noto Emoji glyph (subsetted into
|
|
fonts/noto-emoji.woff2 — resubset if the roster changes);
|
|
attacked/attacking: the combat verbs */
|
|
var WEAPONS = {
|
|
sword: { em: '\uD83D\uDDE1', rot: true, attacked: 'slashed', attacking: 'slashing' },
|
|
axe: { em: '\uD83E\uDE93', reversed: true, attacked: 'chopped', attacking: 'chopping' },
|
|
wand: { fa: 'fa-wand-sparkles', attacked: 'zapped', attacking: 'zapping' },
|
|
gun: { fa: 'fa-gun', attacked: 'shot', attacking: 'shooting' }
|
|
};
|
|
|
|
var PRESETS = {
|
|
she: { subj: 'she', obj: 'her', posd: 'her', refl: 'herself' },
|
|
he: { subj: 'he', obj: 'him', posd: 'his', refl: 'himself' },
|
|
they: { subj: 'they', obj: 'them', posd: 'their', refl: 'themself' }
|
|
};
|
|
|
|
var story = document.getElementById('story');
|
|
var pronouns = document.getElementById('pronouns');
|
|
var custom = document.getElementById('custom');
|
|
var weapon = document.getElementById('weapon');
|
|
var hand = document.getElementById('hand');
|
|
var vnaming = document.getElementById('vnaming');
|
|
var vpronouns = document.getElementById('vpronouns');
|
|
|
|
function loadKey(key) {
|
|
try { return localStorage.getItem(key); } catch (e) { return null; }
|
|
}
|
|
function saveKey(key, value) {
|
|
try { localStorage.setItem(key, value); } catch (e) {}
|
|
}
|
|
function state() {
|
|
var pron = null, enc1 = null, vpron = null, order = null;
|
|
try { pron = JSON.parse(loadKey('questPronouns')); } catch (e) {}
|
|
try { enc1 = JSON.parse(loadKey('questEnc1')); } catch (e) {}
|
|
try { vpron = JSON.parse(loadKey('questVillainPronouns')); } catch (e) {}
|
|
try { order = JSON.parse(loadKey('questEncounters')); } catch (e) {}
|
|
var weaponKey = loadKey('questWeapon');
|
|
return {
|
|
name: loadKey('questHero'),
|
|
pron: pron,
|
|
weapon: WEAPONS[weaponKey] ? weaponKey : null,
|
|
hand: loadKey('questHand'),
|
|
/* The rolled encounter ids, in play order */
|
|
order: order && order.length ? order : null,
|
|
/* Set by the mimic eating the weapon: bare fists from then on,
|
|
{attacked} becomes 'punched' */
|
|
weaponLost: loadKey('questWeaponLost') === '1',
|
|
/* { asked: bool, result: 'attack'|'solved'|'wrong'|null,
|
|
answer: string (wrong only) } */
|
|
enc1: enc1,
|
|
/* 'yes' (Breigan joins) | 'no' | null */
|
|
enc2: loadKey('questEnc2'),
|
|
/* The kid's name once the rename question is answered ('Breigan'
|
|
if declined); null while unanswered. He/him either way */
|
|
kid: loadKey('questKid'),
|
|
/* 'pry' (mimic eats the weapon) | 'leave' | null */
|
|
enc3: loadKey('questEnc3'),
|
|
/* 'steal' (hero becomes a pig) | 'explain' | null */
|
|
enc4: loadKey('questEnc4'),
|
|
/* 'decline' | 'agree' (mid-choice) | 'vines' | 'door' | null */
|
|
enc5: loadKey('questEnc5'),
|
|
/* The princess's name once the rename question is answered
|
|
('Anastasia' if declined); null while unanswered */
|
|
princess: loadKey('questPrincess'),
|
|
vname: loadKey('questVillain'),
|
|
vpron: vpron,
|
|
attacked: loadKey('questAttacked') === '1'
|
|
};
|
|
}
|
|
|
|
function fill(text, vars) {
|
|
return text.replace(/\{([A-Za-z]+)\}/g, function (token, key) {
|
|
var value = vars[key.toLowerCase()];
|
|
if (value === undefined) return token;
|
|
if (key[0] !== key[0].toLowerCase()) {
|
|
value = value.charAt(0).toUpperCase() + value.slice(1);
|
|
}
|
|
return value;
|
|
});
|
|
}
|
|
|
|
/* Everything on the page is a function of the save file */
|
|
function render() {
|
|
var s = state();
|
|
var vars = { name: s.name, kid: s.kid || 'Breigan',
|
|
princess: s.princess || 'Anastasia' };
|
|
if (s.pron) for (var k in s.pron) vars[k] = s.pron[k];
|
|
naming.style.display = s.name ? 'none' : '';
|
|
story.style.display = s.name ? '' : 'none';
|
|
pronouns.style.display = s.name && !s.pron ? '' : 'none';
|
|
weapon.style.display = s.pron && !s.weapon ? '' : 'none';
|
|
hand.style.display = s.weapon && !s.hand ? '' : 'none';
|
|
if (s.name) {
|
|
document.getElementById('intro').textContent = fill(TEXT.intro, vars);
|
|
}
|
|
var setout = document.getElementById('setout');
|
|
setout.style.display = s.pron ? '' : 'none';
|
|
if (s.pron) setout.textContent = fill(TEXT.setout, vars);
|
|
var traverse = document.getElementById('traverse');
|
|
var armed = s.weapon && s.hand;
|
|
traverse.style.display = armed ? '' : 'none';
|
|
if (armed) {
|
|
vars.weapon = s.weapon;
|
|
/* {aweapon}: article included — 'a sword' but 'an axe' */
|
|
vars.aweapon = (/^[aeiou]/.test(s.weapon) ? 'an ' : 'a ') + s.weapon;
|
|
vars.hand = s.hand;
|
|
traverse.textContent = fill(TEXT.traverse, vars);
|
|
}
|
|
|
|
/* The roll: which encounters this hero gets, in which order. Rolled
|
|
lazily so pre-roll saves pick one up on their next render */
|
|
var order = s.order;
|
|
if (armed && !order) {
|
|
order = rollEncounters();
|
|
saveKey('questEncounters', JSON.stringify(order));
|
|
}
|
|
var resolvedEnc = {
|
|
troll: !!(s.enc1 && s.enc1.result),
|
|
kid: s.enc2 === 'no' || (s.enc2 === 'yes' && !!s.kid),
|
|
mimic: !!s.enc3,
|
|
potion: !!s.enc4,
|
|
princess: s.enc5 === 'decline' || s.enc5 === 'vines' ||
|
|
(s.enc5 === 'door' && !!s.princess)
|
|
};
|
|
/* Reorder the story's encounter groups to match the roll, and find
|
|
the single encounter currently awaiting the player */
|
|
var active = null;
|
|
var arrival = document.getElementById('arrival');
|
|
if (armed && order) {
|
|
for (var oi = 0; oi < order.length; oi++) {
|
|
var g = document.getElementById(
|
|
'grp' + order[oi].charAt(0).toUpperCase() + order[oi].slice(1));
|
|
if (g) story.insertBefore(g, arrival);
|
|
if (active === null && !resolvedEnc[order[oi]]) active = order[oi];
|
|
}
|
|
}
|
|
function appeared(id) {
|
|
if (!armed || !order || order.indexOf(id) < 0) return false;
|
|
return resolvedEnc[id] || id === active;
|
|
}
|
|
/* Weapon state as of a given encounter: lost only if the mimic ate it
|
|
earlier in the order (texts must not change retroactively) */
|
|
function armedAt(id) {
|
|
var i = order ? order.indexOf(id) : -1;
|
|
for (var j = 0; j < i; j++) {
|
|
if (order[j] === 'mimic' && s.enc3 === 'pry') return false;
|
|
}
|
|
return true;
|
|
}
|
|
function combatVars(isArmed) {
|
|
vars.attacked = isArmed ? WEAPONS[s.weapon].attacked : 'punched';
|
|
vars.withweapon = isArmed ? ' with ' + vars.posd + ' ' + s.weapon : '';
|
|
}
|
|
|
|
/* The bridge troll's riddle */
|
|
var showTroll = appeared('troll');
|
|
if (showTroll) combatVars(armedAt('troll'));
|
|
var enc1 = document.getElementById('enc1');
|
|
enc1.style.display = showTroll ? '' : 'none';
|
|
if (showTroll) enc1.textContent = fill(TEXT.enc1, vars);
|
|
document.getElementById('enc1choice').style.display =
|
|
showTroll && !s.enc1 ? '' : 'none';
|
|
var asked = showTroll && s.enc1 && s.enc1.asked;
|
|
var enc1riddle = document.getElementById('enc1riddle');
|
|
enc1riddle.style.display = asked ? '' : 'none';
|
|
if (asked) enc1riddle.textContent = fill(TEXT.enc1riddle, vars);
|
|
document.getElementById('riddle').style.display =
|
|
asked && !s.enc1.result ? '' : 'none';
|
|
var attacked = showTroll && s.enc1 && s.enc1.result === 'attack';
|
|
var enc1attack = document.getElementById('enc1attack');
|
|
enc1attack.style.display = attacked ? '' : 'none';
|
|
if (attacked) enc1attack.textContent = fill(TEXT.enc1attack, vars);
|
|
var wrong = showTroll && s.enc1 && s.enc1.result === 'wrong';
|
|
var enc1wrong = document.getElementById('enc1wrong');
|
|
enc1wrong.style.display = wrong ? '' : 'none';
|
|
if (wrong) {
|
|
vars.answer = s.enc1.answer;
|
|
enc1wrong.textContent = fill(TEXT.enc1wrong, vars);
|
|
}
|
|
var solved = showTroll && s.enc1 && s.enc1.result === 'solved';
|
|
var enc1correct = document.getElementById('enc1correct');
|
|
enc1correct.style.display = solved ? '' : 'none';
|
|
if (solved) enc1correct.textContent = fill(TEXT.enc1correct, vars);
|
|
|
|
/* Breigan */
|
|
var showKid = appeared('kid');
|
|
var enc2 = document.getElementById('enc2');
|
|
enc2.style.display = showKid ? '' : 'none';
|
|
if (showKid) enc2.textContent = fill(TEXT.enc2, vars);
|
|
document.getElementById('enc2choice').style.display =
|
|
showKid && !s.enc2 ? '' : 'none';
|
|
var enc2yes = document.getElementById('enc2yes');
|
|
var joined = showKid && s.enc2 === 'yes';
|
|
enc2yes.style.display = joined ? '' : 'none';
|
|
if (joined) enc2yes.textContent = fill(TEXT.enc2yes, vars);
|
|
var enc2no = document.getElementById('enc2no');
|
|
var declined = showKid && s.enc2 === 'no';
|
|
enc2no.style.display = declined ? '' : 'none';
|
|
if (declined) enc2no.textContent = fill(TEXT.enc2no, vars);
|
|
/* The rename question holds up the journey until answered */
|
|
document.getElementById('rename').style.display =
|
|
joined && !s.kid ? '' : 'none';
|
|
|
|
/* The mimic chest */
|
|
var showMimic = appeared('mimic');
|
|
var enc3 = document.getElementById('enc3');
|
|
enc3.style.display = showMimic ? '' : 'none';
|
|
if (showMimic) enc3.textContent = fill(TEXT.enc3, vars);
|
|
var choosing3 = showMimic && !s.enc3;
|
|
document.getElementById('enc3choice').style.display = choosing3 ? '' : 'none';
|
|
if (choosing3) {
|
|
document.getElementById('pryBtn').textContent = fill(TEXT.enc3pryBtn, vars);
|
|
}
|
|
var pried = showMimic && s.enc3 === 'pry';
|
|
var enc3pry = document.getElementById('enc3pry');
|
|
enc3pry.style.display = pried ? '' : 'none';
|
|
if (pried) enc3pry.textContent = fill(TEXT.enc3pry, vars);
|
|
var left = showMimic && s.enc3 === 'leave';
|
|
var enc3leave = document.getElementById('enc3leave');
|
|
enc3leave.style.display = left ? '' : 'none';
|
|
if (left) enc3leave.textContent = fill(TEXT.enc3leave, vars);
|
|
|
|
/* The potion seller */
|
|
var showPotion = appeared('potion');
|
|
var enc4 = document.getElementById('enc4');
|
|
enc4.style.display = showPotion ? '' : 'none';
|
|
if (showPotion) enc4.textContent = fill(TEXT.enc4, vars);
|
|
document.getElementById('enc4choice').style.display =
|
|
showPotion && !s.enc4 ? '' : 'none';
|
|
var stole = showPotion && s.enc4 === 'steal';
|
|
var enc4steal = document.getElementById('enc4steal');
|
|
enc4steal.style.display = stole ? '' : 'none';
|
|
if (stole) {
|
|
combatVars(armedAt('potion'));
|
|
enc4steal.textContent = fill(TEXT.enc4steal, vars);
|
|
}
|
|
var explained = showPotion && s.enc4 === 'explain';
|
|
var enc4explain = document.getElementById('enc4explain');
|
|
enc4explain.style.display = explained ? '' : 'none';
|
|
if (explained) enc4explain.textContent = fill(TEXT.enc4explain, vars);
|
|
|
|
/* Breigan's rename line (only when he was actually renamed) */
|
|
var kidrename = document.getElementById('kidrename');
|
|
var kidRenamed = joined && s.kid && s.kid !== 'Breigan';
|
|
kidrename.style.display = kidRenamed ? '' : 'none';
|
|
if (kidRenamed) kidrename.textContent = fill(TEXT.kidrename, vars);
|
|
|
|
/* The probable princess */
|
|
var showPrincess = appeared('princess');
|
|
var enc5 = document.getElementById('enc5');
|
|
enc5.style.display = showPrincess ? '' : 'none';
|
|
if (showPrincess) enc5.textContent = fill(TEXT.enc5, vars);
|
|
document.getElementById('enc5choice').style.display =
|
|
showPrincess && !s.enc5 ? '' : 'none';
|
|
var declined5 = showPrincess && s.enc5 === 'decline';
|
|
var enc5decline = document.getElementById('enc5decline');
|
|
enc5decline.style.display = declined5 ? '' : 'none';
|
|
if (declined5) enc5decline.textContent = fill(TEXT.enc5decline, vars);
|
|
var agreed = showPrincess && (s.enc5 === 'agree' ||
|
|
s.enc5 === 'vines' || s.enc5 === 'door');
|
|
var enc5agree = document.getElementById('enc5agree');
|
|
enc5agree.style.display = agreed ? '' : 'none';
|
|
if (agreed) enc5agree.textContent = fill(TEXT.enc5agree, vars);
|
|
document.getElementById('enc5method').style.display =
|
|
showPrincess && s.enc5 === 'agree' ? '' : 'none';
|
|
var fell = showPrincess && s.enc5 === 'vines';
|
|
var enc5vines = document.getElementById('enc5vines');
|
|
enc5vines.style.display = fell ? '' : 'none';
|
|
if (fell) enc5vines.textContent = fill(TEXT.enc5vines, vars);
|
|
var rescued = showPrincess && s.enc5 === 'door';
|
|
var enc5door = document.getElementById('enc5door');
|
|
enc5door.style.display = rescued ? '' : 'none';
|
|
if (rescued) enc5door.textContent = fill(TEXT.enc5door, vars);
|
|
document.getElementById('prename').style.display =
|
|
rescued && !s.princess ? '' : 'none';
|
|
var princessrename = document.getElementById('princessrename');
|
|
var pRenamed = rescued && s.princess && s.princess !== 'Anastasia';
|
|
princessrename.style.display = pRenamed ? '' : 'none';
|
|
if (pRenamed) princessrename.textContent = fill(TEXT.princessrename, vars);
|
|
|
|
/* The wizard's tower: arrival, villain creation, then the ending */
|
|
var encountersDone = armed && order && active === null;
|
|
arrival.style.display = encountersDone ? '' : 'none';
|
|
if (encountersDone) arrival.textContent = fill(TEXT.arrival, vars);
|
|
vnaming.style.display = encountersDone && !s.vname ? '' : 'none';
|
|
vpronouns.style.display =
|
|
encountersDone && s.vname && !s.vpron ? '' : 'none';
|
|
var showdown = encountersDone && s.vname && s.vpron;
|
|
var confront = document.getElementById('confront');
|
|
confront.style.display = showdown ? '' : 'none';
|
|
if (showdown) {
|
|
vars.vname = s.vname;
|
|
for (var vk in s.vpron) vars['v' + vk] = s.vpron[vk];
|
|
/* Showdown-time forms: unlike the troll fight, the ending happens
|
|
after any weapon loss, so here punched/bare-handed applies */
|
|
vars.attacked = s.weaponLost ? 'punched' : WEAPONS[s.weapon].attacked;
|
|
vars.attacking = s.weaponLost ? 'punching' : WEAPONS[s.weapon].attacking;
|
|
vars.withweapon = s.weaponLost ? '' : ' with ' + vars.posd + ' ' + s.weapon;
|
|
confront.textContent = fill(TEXT.confront, vars);
|
|
}
|
|
document.getElementById('showdownAttack').style.display =
|
|
showdown && !s.attacked ? '' : 'none';
|
|
var ending = document.getElementById('ending');
|
|
var endingText = '';
|
|
var won = false;
|
|
if (showdown && s.attacked) {
|
|
/* The situation key: chosen encounters in pool order (1-based
|
|
positions), then their outcomes in the same order */
|
|
var successOf = {
|
|
troll: s.enc1 && s.enc1.result === 'solved',
|
|
kid: s.enc2 === 'yes',
|
|
mimic: s.enc3 === 'leave',
|
|
potion: s.enc4 === 'explain',
|
|
princess: s.enc5 === 'door'
|
|
};
|
|
var chosen = order.slice().sort(function (a, b) {
|
|
return POOL.indexOf(a) - POOL.indexOf(b);
|
|
});
|
|
var sig = '', letters = '', successes = 0;
|
|
chosen.forEach(function (id) {
|
|
sig += POOL.indexOf(id) + 1;
|
|
letters += successOf[id] ? 'S' : 'F';
|
|
successes += successOf[id] ? 1 : 0;
|
|
});
|
|
won = successes >= 2;
|
|
endingText = ENDINGS[sig + ':' + letters] || '';
|
|
}
|
|
ending.style.display = endingText ? '' : 'none';
|
|
if (endingText) ending.textContent = fill(endingText, vars);
|
|
document.getElementById('endWin').style.display =
|
|
showdown && s.attacked && won ? '' : 'none';
|
|
/* Restart is offered after every ending — losses need it, wins may
|
|
want another go */
|
|
document.getElementById('endRestart').style.display =
|
|
showdown && s.attacked ? '' : 'none';
|
|
|
|
var figure = document.getElementById('figure');
|
|
/* The hero faces the viewer: their left hand is on the viewer's
|
|
right, so the art side is the opposite of the handedness */
|
|
var side = s.hand === 'left' ? 'right' : 'left';
|
|
/* The potion theft turned the hero into a pig (weapon still in trotter) */
|
|
var isPig = s.enc4 === 'steal';
|
|
var artSet = isPig ? ART.pig : ART;
|
|
var hero;
|
|
if (armed && !s.weaponLost) {
|
|
var w = WEAPONS[s.weapon];
|
|
/* Mirror so the weapon faces away from the body; 'reversed' marks
|
|
glyphs whose natural facing is opposite the FA convention */
|
|
var mirrored = side === 'left';
|
|
if (w.reversed) mirrored = !mirrored;
|
|
var flip = mirrored ? ' wpn-flip' : '';
|
|
var icon = w.fa
|
|
? '<i class="fa-solid ' + w.fa + '"></i>'
|
|
: '<span class="wpn-em' + (w.rot ? ' wpn-rot' : '') + '">' +
|
|
w.em + '</span>';
|
|
icon = '<span class="wpn' + flip + '">' + icon + '</span>';
|
|
hero = artSet[side].replace('{w}', icon);
|
|
} else {
|
|
hero = artSet.base;
|
|
}
|
|
/* The troll took a leg (in combat or as a quiz penalty) */
|
|
if (armed && (attacked || wrong)) {
|
|
hero = hero.replace(isPig ? '/\\' : '/ \\', '/');
|
|
}
|
|
/* Companions line up on the hero's free side, away from the weapon —
|
|
and stay there even if the weapon is later lost */
|
|
var figs = ['<span class="fig">' + hero + '</span>'];
|
|
function addCompanion(art) {
|
|
var span = '<span class="fig">' + art + '</span>';
|
|
if (side === 'right') figs.unshift(span);
|
|
else figs.push(span);
|
|
}
|
|
if (joined) addCompanion(ART.kid);
|
|
if (rescued) addCompanion(ART.princess);
|
|
figure.innerHTML = figs.join('');
|
|
}
|
|
|
|
naming.addEventListener('submit', function (e) {
|
|
e.preventDefault();
|
|
var name = document.getElementById('heroName').value.trim();
|
|
if (!name) return;
|
|
saveKey('questHero', name);
|
|
render();
|
|
});
|
|
|
|
/* Hero and villain share the pronoun-picker wiring; ids differ by prefix */
|
|
function wirePronouns(form, customTable, customBtn, prefix, storageKey) {
|
|
form.addEventListener('click', function (e) {
|
|
var preset = e.target.getAttribute && e.target.getAttribute('data-preset');
|
|
if (!preset) return;
|
|
saveKey(storageKey, JSON.stringify(PRESETS[preset]));
|
|
render();
|
|
});
|
|
customBtn.addEventListener('click', function () {
|
|
customTable.style.display = customTable.style.display ? '' : 'none';
|
|
});
|
|
form.addEventListener('submit', function (e) {
|
|
e.preventDefault();
|
|
var pron = {
|
|
subj: document.getElementById(prefix + 'Subj').value.trim(),
|
|
obj: document.getElementById(prefix + 'Obj').value.trim(),
|
|
posd: document.getElementById(prefix + 'Posd').value.trim(),
|
|
refl: document.getElementById(prefix + 'Refl').value.trim()
|
|
};
|
|
for (var k in pron) if (!pron[k]) return;
|
|
saveKey(storageKey, JSON.stringify(pron));
|
|
render();
|
|
});
|
|
}
|
|
wirePronouns(pronouns, custom,
|
|
document.getElementById('customBtn'), 'pn', 'questPronouns');
|
|
wirePronouns(vpronouns, document.getElementById('vcustom'),
|
|
document.getElementById('vcustomBtn'), 'vpn', 'questVillainPronouns');
|
|
|
|
vnaming.addEventListener('submit', function (e) {
|
|
e.preventDefault();
|
|
var name = document.getElementById('villainName').value.trim();
|
|
if (!name) return;
|
|
saveKey('questVillain', name);
|
|
render();
|
|
});
|
|
|
|
document.getElementById('showdownAttack').addEventListener('click', function (e) {
|
|
if (e.target.tagName !== 'BUTTON') return;
|
|
saveKey('questAttacked', '1');
|
|
render();
|
|
});
|
|
|
|
/* A restart or a completed quest wipes the whole save file, hero
|
|
included — a fresh run starts at "Name your hero" */
|
|
function clearRun() {
|
|
try {
|
|
['questHero', 'questPronouns', 'questWeapon', 'questHand', 'questEncounters',
|
|
'questEnc1', 'questEnc2', 'questKid', 'questEnc3', 'questEnc4', 'questEnc5', 'questPrincess',
|
|
'questWeaponLost',
|
|
'questVillain', 'questVillainPronouns', 'questAttacked'
|
|
].forEach(function (k) { localStorage.removeItem(k); });
|
|
} catch (err) {}
|
|
}
|
|
|
|
/* Winning genuinely restores gade.gg: same keys the 404's restore
|
|
hatch clears, then home — where the guard no longer redirects */
|
|
document.getElementById('endWin').addEventListener('click', function (e) {
|
|
if (e.target.tagName !== 'BUTTON') return;
|
|
/* Permanent — survives the save wipe and the restore. The
|
|
achievements system on the main page reads it, so the Adventurer
|
|
toast greets the return home */
|
|
saveKey('questWon', '1');
|
|
clearRun();
|
|
try {
|
|
localStorage.removeItem('siteDeleted');
|
|
localStorage.removeItem('siteDeletedViews');
|
|
localStorage.removeItem('questStarted');
|
|
} catch (err) {}
|
|
location.href = '/';
|
|
});
|
|
|
|
document.getElementById('endRestart').addEventListener('click', function (e) {
|
|
if (e.target.tagName !== 'BUTTON') return;
|
|
clearRun();
|
|
render();
|
|
});
|
|
|
|
weapon.addEventListener('click', function (e) {
|
|
var chosen = e.target.getAttribute && e.target.getAttribute('data-weapon');
|
|
if (!chosen) return;
|
|
saveKey('questWeapon', chosen);
|
|
render();
|
|
});
|
|
|
|
hand.addEventListener('click', function (e) {
|
|
var chosen = e.target.getAttribute && e.target.getAttribute('data-hand');
|
|
if (!chosen) return;
|
|
saveKey('questHand', chosen);
|
|
render();
|
|
});
|
|
|
|
document.getElementById('enc1choice').addEventListener('click', function (e) {
|
|
var act = e.target.getAttribute && e.target.getAttribute('data-act');
|
|
if (!act) return;
|
|
saveKey('questEnc1', JSON.stringify(act === 'attack'
|
|
? { asked: false, result: 'attack' }
|
|
: { asked: true, result: null }));
|
|
render();
|
|
});
|
|
|
|
document.getElementById('riddle').addEventListener('submit', function (e) {
|
|
e.preventDefault();
|
|
var raw = document.getElementById('riddleAnswer').value.trim();
|
|
if (!raw) return;
|
|
var answer = raw.toLowerCase();
|
|
var enc1 = answer === 'a river' || answer === 'river' || answer === 'water'
|
|
? { asked: true, result: 'solved' }
|
|
: { asked: true, result: 'wrong', answer: raw };
|
|
saveKey('questEnc1', JSON.stringify(enc1));
|
|
render();
|
|
});
|
|
|
|
document.getElementById('riddleAttack').addEventListener('click', function () {
|
|
saveKey('questEnc1', JSON.stringify({ asked: true, result: 'attack' }));
|
|
render();
|
|
});
|
|
|
|
document.getElementById('enc2choice').addEventListener('click', function (e) {
|
|
var ans = e.target.getAttribute && e.target.getAttribute('data-ans');
|
|
if (!ans) return;
|
|
saveKey('questEnc2', ans);
|
|
render();
|
|
});
|
|
|
|
/* Breigan and Anastasia share the rename wiring */
|
|
function wireRename(divId, formId, inputId, storageKey, defaultName) {
|
|
var form = document.getElementById(formId);
|
|
document.getElementById(divId).addEventListener('click', function (e) {
|
|
var ans = e.target.getAttribute && e.target.getAttribute('data-rename');
|
|
if (!ans) return;
|
|
if (ans === 'no') {
|
|
saveKey(storageKey, defaultName);
|
|
render();
|
|
} else {
|
|
form.style.display = '';
|
|
}
|
|
});
|
|
form.addEventListener('submit', function (e) {
|
|
e.preventDefault();
|
|
var name = document.getElementById(inputId).value.trim();
|
|
if (!name) return;
|
|
saveKey(storageKey, name);
|
|
render();
|
|
});
|
|
}
|
|
wireRename('rename', 'renameForm', 'kidName', 'questKid', 'Breigan');
|
|
wireRename('prename', 'prenameForm', 'princessName',
|
|
'questPrincess', 'Anastasia');
|
|
|
|
document.getElementById('enc3choice').addEventListener('click', function (e) {
|
|
var act = e.target.getAttribute && e.target.getAttribute('data-act');
|
|
if (!act) return;
|
|
saveKey('questEnc3', act);
|
|
if (act === 'pry') saveKey('questWeaponLost', '1');
|
|
render();
|
|
});
|
|
|
|
document.getElementById('enc4choice').addEventListener('click', function (e) {
|
|
var act = e.target.getAttribute && e.target.getAttribute('data-act');
|
|
if (!act) return;
|
|
saveKey('questEnc4', act);
|
|
render();
|
|
});
|
|
|
|
document.getElementById('enc5choice').addEventListener('click', function (e) {
|
|
var act = e.target.getAttribute && e.target.getAttribute('data-act');
|
|
if (!act) return;
|
|
saveKey('questEnc5', act);
|
|
render();
|
|
});
|
|
|
|
document.getElementById('enc5method').addEventListener('click', function (e) {
|
|
var act = e.target.getAttribute && e.target.getAttribute('data-act');
|
|
if (!act) return;
|
|
saveKey('questEnc5', act);
|
|
render();
|
|
});
|
|
|
|
render();
|
|
})();
|