(() => { function checkSuspicion(add = 0) { const old = getStat("suspicion"); if (add >= 0) { add *= state.info.awareness.value; } changeStat("suspicion", add); if (getStat("suspicion") >= 100) { print(["Geta spots you!", "You're snatched up and tossed into the fox's bowl of cereal."]); goToRoom("in-bowl"); return false; } else if (getStat("suspicion") >= 75 && old < 75) { print(["The fox is very suspicious. You're going to get caught if you keep this up..."]); } return true; } function randomBodyPart() { const choices = Object.entries(state.player.limbs).filter(([name, status]) => { return status; }).map(([name, status]) => name); return choices[Math.floor(Math.random() * choices.length)]; } function limbsLost() { return Object.entries(state.player.limbs).filter(([name, status]) => { return !status; }).length; } function synonym(word) { const choices = { "slippery": ["slippery", "slimy", "slick", "glistening"], "chews": ["chews", "crunches"], "crushes": ["crushes", "pulverizes", "shatters", "smashes"] } return choices[word][Math.floor(Math.random() * choices[word].length)]; } function statLerp(stat, change, duration) { // pretty sure this'll be a random id... const id = new Date().getTime() + Math.random(); const iterations = duration / 1000 * 60; startTimer({ id: id, func: () => { changeStat(stat, change / iterations); return true; }, delay: 1000 / 60, loop: true, classes: [ ] }); startTimer({ id: id + "-stopper", func: () => { stopTimer(id); return false; }, delay: duration, loop: false, classes: [ ] }); } const limbs = { head: "head", leftArm: "left arm", rightArm: "right arm", leftLeg: "left leg", rightLeg: "right leg" }; stories.push({ "id": "geta-unaware", "info": { "name": "Geta's Breakfast", "desc": "Try to sneak past a fox after a catastrophic shrinking incident.", "tags": [ "prey", "fatal", "oral-vore", "hard-vore", "hard-digestion", "macro-micro" ] }, "intro": { "start": "pepper-grinder", "setup": () => { state.info.awareness = { id: "awareness", name: "Geta's Awareness", type: "counter", value: 1, get render() { if (this.value < 1) { return "Distracted"; } else if (this.value == 1) { return "Normal" } else { return "Alert" } } } state.geta = {}; state.player.stats.health = { name: "Health", type: "meter", value: 100, min: 0, max: 100, color: "rgb(255,55,55)" }; state.player.stats.stamina = { name: "Stamina", type: "meter", value: 100, min: 0, max: 100, color: "rgb(55,255,55)", hidden: true }; state.player.stats.suspicion = { name: "Suspicion", type: "meter", value: 0, min: 0, max: 100, color: "rgb(100,100,100)" }; state.player.stats.mawPos = { "name": "Struggle", "type": "meter", "value": 0.5, "min": 0, "max": 1, "color": "rgb(0,255,0)", hidden: true } state.info.time.value = 60 * 60 * 7 + 60 * 17; state.player.limbs = {}; state.player.limbs.head = true; state.player.limbs.leftArm = true; state.player.limbs.rightArm = true; state.player.limbs.leftLeg = true; state.player.limbs.rightLeg = true; startTimer({ id: "clock", func: () => { state.info.time.value += 1; state.info.time.value %= 86000; return true; }, delay: 1000, loop: true, classes: [ ] }); startTimer({ id: "suspicion-decay", func: () => { checkSuspicion(-0.1); return true; }, delay: 100, loop: true, classes: [ "free" ] }); startTimer({ id: "timeout", func: () => { if (state.info.time.value == 60 * 60 * 7 + 60 * 19) { print(["The fox is almost done with his breakfast..."]); } if (state.info.time.value >= 60 * 60 * 7 + 60 * 20) { print(["Time's up! In you go."]); goToRoom("maw"); return false; } return true; }, delay: 1000, loop: true, classes: [ "free" ] }); startTimer({ id: "geta-action", func: () => { const random = Math.random(); if (random < 0.7) { print(["Geta slurps up a spoonful of cereal."]); return Math.random() * 3000 + 3000 } else if (random < 0.9) { state.info.awareness.value = 0.1; print(["The fox yawns and stretches."]); startTimer({ id: "yawn-end", func: () => { print(["Geta finishes his stretch"]); state.info.awareness.value = 1; return true; }, delay: 5000, loop: false, classes: [ "free" ] }); return Math.random() * 3000 + 5000 } else { state.info.awareness.value = 2; print(["Geta narrows his eyes and looks around the table. Something seems off to him..."]); startTimer({ id: "squint-end", func: () => { print(["He goes back to his breakfast."]); state.info.awareness.value = 1; return true; }, delay: 5000, loop: false, classes: [ "free" ] }); return Math.random() * 1000 + 6000 } }, delay: 5000, loop: true, classes: [ "free" ] }); }, "intro": () => { print(["Game started", newline, "Exposition goes here later."]); } }, "sounds": [ "loop/stomach.ogg", "sfx/absorb.ogg", "sfx/digest.ogg", "sfx/swallow.ogg" ], "preload": [ ], "refresh": () => { setBackgroundColor(50 - state.player.stats.health.value / 2, 0, 0); }, "world": { "pepper-grinder": { "id": "pepper-grinder", "name": "Pepper Grinder", "desc": "You're hiding behind a pepper grinder", "move": (room) => { print(["You dart over to the pepper grinder, which looms over you like a greatwood."]); }, "enter": (room) => { }, "exit": (room) => { }, "actions": [ { name: "Tap", desc: "Bang on the pepper shaker", execute: (room) => { print(["You thump the pepper shaker, making a dull thud."]); const safe = checkSuspicion(25); if (safe && getStat("suspicion") > 50) { print(["Geta leans in to have a closer look. He's going to catch you if you don't move!"]); startTimer({ id: "pepper-investigate", func: () => { if (state.player.location == "pepper-grinder") { print(["He catches you.", newline, "You're tossed into the fox's jaws."]); goToRoom("maw"); } else { print(["You evaded the fox."]); } }, delay: 3000, loop: false, classes: [ "free" ] }); } }, show: [ ], conditions: [ ] }, { name: "Wait", desc: "Wait for the fox to finish his breakfast. Surely you'll be able to escape after that...right?", execute: (room) => { state.info.time.value = 60 * 60 * 7 + 60 * 20; }, show: [ ], conditions: [ ] }, ], "exits": { "up": { "target": "bowl", "desc": "Walk up to the cereal bowl", "show": [ ], "conditions": [ ], "hooks": [ (room, exit) => { return checkSuspicion(10); } ] }, "left": { "target": "table", "desc": "Run out into the open", "show": [ ], "conditions": [ ], "hooks": [ ] }, }, "hooks": [ ], "data": { "stats": { } } }, "bowl": { "id": "bowl", "name": "Behind the Bowl", "desc": "You're crouched behind Geta's bowl of cereal", "move": (room) => { print(["You scurry up to the looming bowl, staying low and out of Geta's sight."]); }, "enter": (room) => { }, "exit": (room) => { }, "actions": [ ], "exits": { "ascend": { "target": "in-bowl", "desc": "Climb into Geta's cereal", "show": [ ], "conditions": [ ], "hooks": [ ] }, "down": { "target": "pepper-grinder", "desc": "Run back behind the pepper grinder", "show": [ ], "conditions": [ ], "hooks": [ (room, exit) => { return checkSuspicion(15); } ] }, }, "hooks": [ ], "data": { "stats": { } } }, "table": { "id": "table", "name": "Table", "desc": "You're out in the open!", "move": (room) => { }, "enter": (room) => { startTimer({ id: "table-suspicion", func: () => { checkSuspicion(1.5); return true; }, delay: 100, loop: true, classes: [ "free" ] }); }, "exit": (room) => { stopTimer("table-suspicion"); }, "actions": [ ], "exits": { "right": { "target": "pepper-grinder", "desc": "Run back to cover", "show": [ ], "conditions": [ ], "hooks": [ ] }, }, "hooks": [ ], "data": { "stats": { } } }, "in-bowl": { "id": "in-bowl", "name": "Bowl", "desc": "You're in the cereal bowl...", "move": (room) => { print(["Why did you do that?"]); }, "enter": (room) => { state.player.stats.suspicion.hidden = true; stopClassTimers("free"); startTimer({ id: "geta-eat", func: () => { if (Math.random() < 0.6) { print(["Geta scoops up a spoonful of cereal; you narrowly avoid being caught."]); return true; } else { print(["Geta scoops you up and slurps you into his maw."]); goToRoom("maw"); return false; } }, delay: 3000, loop: true, classes: [ "free" ] }); }, "exit": (room) => { }, "actions": [ ], "exits": { "ascend": { "target": "bowl", "desc": "Try to climb back out!", "show": [ ], "conditions": [ ], "hooks": [ (room, exit) => { print([ "You grab at the rim of the bowl and try to pull yourself out. Alas, your struggles are for naught; Geta easily scoops you up in his spoon and, a heartbeat later, slurps you into his sloppy jaws. You didn't stand a chance." ]); goToRoom("maw"); return false; } ] }, }, "hooks": [ ], "data": { "stats": { } } }, "maw": { "id": "maw", "name": "Geta's Maw", "desc": "You've been slurped up into the fox's jaws", "move": (room) => { }, "enter": (room) => { state.player.stats.suspicion.hidden = true; stopClassTimers("free"); state.player.stats.stamina.hidden = false; state.player.stats.mawPos.hidden = false; state.geta.slurps = 0; state.geta.chews = 0; state.geta.swallowsLeft = 3 + Math.floor(Math.random() * 3); state.geta.mawMovement = 1; print(["You slip into Geta's maw. He'll swallow you like the snack you are if you slip too far back, but crawl too far forward, and you'll meet his fangs..."]) startTimer({ id: "maw-stamina", func: () => { changeStat("stamina", 0.1); return true; }, delay: 1000 / 60, loop: true, classes: [ "maw-struggle" ] }); startTimer({ id: "maw-random-movement", func: () => { const time = new Date().getTime(); const movementFactor = (state.geta.mawMovement + limbsLost()); const fastPart = Math.sin(time / 200) / 1600 / 3; const slowPart = Math.sin(time / 1000) / 1600; changeStat("mawPos", movementFactor * (fastPart + slowPart)); if (getStat("mawPos") <= 0.02) { print(["You slip too far back. Geta doesn't even have to try to swallow you like the food you are; you're lost from the world, buried in his hot, tight throat."]); goToRoom("throat"); return false; } else if (getStat("mawPos") >= 0.98) { print(["Geta's jaws close like a falling guillotine's blade. You're crushed like an insect. A sharp gulp drags you down to the fox's guts."]); playSfx("sfx/swallow.ogg"); changeStat("health", -90); goToRoom("stomach"); return false; } return true; }, delay: 1000 / 60, loop: true, classes: [ "maw-struggle" ] }); startTimer({ id: "maw-struggle", func: () => { if (state.geta.swallowsLeft <= 0) { print(["Geta picks up his bowl of cereal and drinks it down, swallowing you in the process."]); state.geta.swallowsLeft = -1; goToRoom("throat"); return false; } let choice; if (Math.random() < 0.2) { const choices = [ "slosh", "tilt-back", "shove", ]; choice = choices[Math.floor(Math.random() * choices.length)]; } else if (Math.random() > state.geta.slurps / 3) { choice = "slurp"; } else if (state.geta.chews - Math.floor(Math.random() * 3) < state.geta.slurps) { choice = "chew"; } else { choice = "swallow"; } if (choice == "swallow") { if (getStat("mawPos") < 0.15) { print(["You're too far back. The fox swallows a mouthful of cereal, taking you with it."]); goToRoom("throat"); return false; } else { printRandom([ ["A light swallow drags a lump of chewed-up cereal into Geta's depths."], ["Geta swallows, dragging you closer to your demise."], ["Your captor's throat ripples as he gulps down his breakfast."] ]); statLerp("mawPos", -0.25, 500); state.geta.slurps = 0; state.geta.chews = 0; state.geta.swallowsLeft -= 1; return Math.random() * 1500 + 1500; } } else if (choice == "slurp") { statLerp("mawPos", -0.1, 250); printRandom([ ["A spoonful of cereal slips into the fox's " + synonym("slippery") + " maw."], ["Geta slurps up some more of his breakfast."], ["You're shoved back a bit as Geta slurps up more cereal."] ]); state.geta.slurps += 1; return Math.random() * 1000 + 2500; } else if (choice == "chew") { if (getStat("mawPos") > 0.85) { const limb = randomBodyPart(); state.player.limbs[limb] = false; const limbName = limbs[limb]; if (limb == "head") { print(["Geta's jaws crush down on your head. You die."]); changeStat("health", -100); goToRoom("stomach"); } else { print(["You scream in pain as your " + limbName + " is shattered by the fox's jaws"]); changeStat("health", -40); return true; } } else { printRandom([ ["Cruel fangs crush down on the food around you."], ["Geta chews on his breakfast."], ["The fox's fangs close with a crackle-crunch of cereal."] ]); statLerp("mawPos", Math.random() / 10 - 0.05, 250); state.geta.chews += 1; return Math.random() * 500 + 1300; } } else if (choice == "slosh") { print(["Geta's tongue sloshes from side to side, throwing you around his maw like a ship in a storm."]); state.geta.mawMovement = 3; startTimer({ id: "maw-slosh-end", func: () => { state.geta.mawMovement = 1; print(["The sloshing ends."]); return true; }, delay: 4000, loop: false, classes: [ "maw-struggle" ] }); return Math.random() * 1500 + 4500; } else if (choice == "tilt-back") { print(["Geta tilts his head back, sending rivults of slobber flowing down into that yawning gullet."]); state.geta.mawMovement = 0.2; statLerp("mawPos", -1, 4000); startTimer({ id: "maw-tilt-text", func: () => { state.geta.mawMovement = 1; print(["The fox's muzzle tilts back down as he SWALLOWS hard."]); statLerp("mawPos", -0.3, 500); state.geta.swallowsLeft -= 1; state.geta.slurps = 0; state.geta.chews = 0; return true; }, delay: 4000, loop: false, classes: [ ] }); return 5000 + Math.random() * 1000; } else if (choice == "shove") { print(["Geta's tongue lurches forward, shoving you towards the front of his maw!"]); statLerp("mawPos", 0.2, 500); return 2000 + Math.random() * 1000; } }, delay: 0, loop: true, classes: [ "maw-struggle" ] }); startTimer({ id: "maw-taunts", func: () => { printRandom([ ["\"Did you really think I wouldn't notice you?\""], ["\"You're going to feel good dying in my guts.\""], ["\"I could just crush you...but where's the fun in that?\""] ]); return Math.random() * 5000 + 5000; }, delay: 5000, loop: true, classes: [ "maw-struggle" ] }); }, "exit": (room) => { state.player.stats.stamina.hidden = true; }, "actions": [ { name: "Struggle", desc: "Pull yourself away from the fox's throat! Just don't go too far forward...", execute: (room) => { if (getStat("stamina") < 25) { print(["You're too tired..."]); } else { print(["You drag yourself forward"]); changeStat("stamina", -25); statLerp("mawPos", 0.15 + Math.random() * 0.05, 250); } }, show: [ ], conditions: [ ] }, { name: "Slip Back", desc: "Slide back towards Geta's gullet", execute: (room) => { if (Math.random() * 25 > getStat("stamina")) { print(["You try to shimmy back an inch or two, but your sore muscles give way; you slide back perilously far!"]); statLerp("mawPos", -0.3 - Math.random() * 0.25, 250); } else if (Math.random() < 0.9) { print(["You let yourself slip back."]); statLerp("mawPos", -0.2 - Math.random() * 0.1, 250); } else { print(["You lose your grip, sliding back quite far!"]); statLerp("mawPos", -0.3 - Math.random() * 0.15, 250); } }, show: [ ], conditions: [ ] }, { name: "Dive In", desc: "Throw yourself towards the fox's throat", execute: (room) => { print(["Resigned to your fate, you toss yourself into Geta's throat. He seems surprised by your eagerness to submit, but swallows you all the same."]); goToRoom("throat"); }, show: [ ], conditions: [ ] }, ], "exits": { }, "hooks": [ ], "data": { "stats": { } } }, "throat": { "id": "throat", "name": "Geta's Gullet", "desc": "GULP!", "move": (room) => { }, "enter": (room) => { playSfx("sfx/swallow.ogg"); state.player.stats.mawPos.hidden = true; stopClassTimers("maw-struggle"); startTimer({ id: "throat-swallow", func: () => { print(["You slush down into Geta's stomach"]); goToRoom("stomach"); return true; }, delay: 7000, loop: false, classes: [ ] }); }, "exit": (room) => { }, "actions": [ { name: "Struggle", desc: "Try to climb back out!", execute: (room) => { print(["Nope"]); }, show: [ ], conditions: [ ] }, { name: "Give up", desc: "Dive down into Geta's stomach", execute: (room) => { print(["You submit to your predator."]); goToRoom("stomach"); stopTimer("throat-swallow"); }, show: [ ], conditions: [ ] }, ], "exits": { }, "hooks": [ ], "data": { "stats": { } } }, "stomach": { "id": "stomach", "name": "Geta's Stomach", "desc": "Glorp", "move": (room) => { }, "enter": (room) => { playLoop("loop/stomach.ogg"); stopClassTimers("maw-struggle"); state.geta.digestionStage = 0; state.geta.acidStrength = 1; startTimer({ id: "digest-stages", func: () => { if (100 - state.geta.digestionStage * 25 - 25 > getStat("health")) { state.geta.digestionStage = Math.floor((100 - getStat("health")) / 25); console.log(state.geta.digestionStage); switch (state.geta.digestionStage) { case 1: print(["Your skin begins to tingle."]); break; case 2: print(["The stinging acids work their way into your tender body."]); break; case 3: print(["You're starting to fall apart..."]); break; default: break; } } return true; }, delay: 1000, loop: true, classes: [ "digestion" ] }); startTimer({ id: "digest-random", func: () => { const choices = [ () => { const crushed = randomBodyPart(); const name = limbs[crushed]; if (name == "head") { print(["A powerful fold of muscle grips your head, crushing it like a grape and killing you instantly."]); changeStat("health", -100); return false; } else { print(["Geta's stomach grips your " + name + " and crushes it with a horrific CRACK"]); changeStat("health", -40); return true; } }, () => { printRandom([["Geta squeezes in on his gut with both hands, sloshing you around in the sickly stew of cereal, milk, and enzymatic slime."], ["Your organic prison snarls and churns, soaking you in fresh acids and hastening your wretched demise."]]); statLerp("health", -10, 2000); return true; }, () => { if (state.geta.swallowsLeft == 0) { print(["A deep series of *glurks* rattles your bones."]); startTimer({ id: "stomach-milk", func: () => { print(["A torrent of cold milk pours into the fox's stomach."]); return false; }, delay: 3000, loop: false, classes: [ "digestion" ] }); } else if (state.geta.swallowsLeft > 0) { print(["Muffled chewing comes from far above. A moment later, you hear a wet *gluk*"]); startTimer({ id: "stomach-cereal", func: () => { print(["A slimy heap of well-chewed corn flakes splatters down around you."]); return false; }, delay: 4000, loop: false, classes: [ ] }); } else if (state.geta.swallowsLeft < 0) { print(["You hear a few light swallows."]); startTimer({ id: "stomach-coffee", func: () => { print(["Gouts of hot, bitter coffee pour into the fox's guts."]); return false; }, delay: 3000, loop: false, classes: [ ] }); } return true; }, () => { print(["\"You were barely worth eating,\" murmurs the fox. \"So small. So weak.\""]); return true; } ]; if (choices[Math.floor(Math.random() * choices.length)]()) { return Math.random() * 3000 + 3500; } else { return false; } }, delay: 5000, loop: true, classes: [ "digestion" ] }); startTimer({ id: "digest", func: () => { changeStat("health", -0.3 * state.geta.acidStrength); if (getStat("health") <= 0) { print(["You're digested before too long."]); goToRoom("digested"); return false; } return true; }, delay: 100, loop: true, classes: [ "digestion" ] }); }, "exit": (room) => { }, "actions": [ { name: "Squirm", desc: "Rub at the walls of the fox's churning stomach", execute: (room) => { printRandom([ ["You punch and kick at the walls"], ["A powerful churn grabs hold of you, stifling any attempts at struggling"], ["Your little thumps and kicks do little to faze your captor"] ]); }, show: [ ], conditions: [ ] }, { name: "Beg", desc: "Plead for your life", execute: (room) => { printRandom([ [ "\"PLEASE!\" you scream, thumping on the walls of the vulpine's gut. \"Let me out!\"", ] ]) if (Math.random() < 0.7) { print(["Your pleas fall on deaf ears."]); } else { printRandom([ ["\"Shhhh,\" growls Geta, \"you're going to die in me. Stop whimpering.\""], ["A long moment passes. \"Poor thing,\" says your captor."] ]) } }, show: [ ], conditions: [ ] }, { name: "Scream", desc: "IT HURTS", execute: (room) => { printRandom([ [ "\"Oh god, oh god, oh god,\" you wail...quivering and quaking as you're digested alive. \"GETA!\"" ], [ "A blood-curdling scream bellows from your burning lungs." ], [ "You let out a hideous wail as the fox digests you alive." ] ]); if (Math.random() < 0.5) { print(["Geta doesn't notice."]); } else { print(["A booming chuckle rocks your body."]); printRandom([ ["\"I hope you're suffering in there.\""], ["\"Pathetic little snack.\""], ["\"Ready to die?\""] ]); } }, show: [ ], conditions: [ ] }, ], "exits": { }, "hooks": [ ], "data": { "stats": { } } }, "digested": { "id": "digested", "name": "Geta's Stomach", "desc": "You're just mush now", "move": (room) => { }, "enter": (room) => { stopClassTimers("digestion"); stopTimer("clock"); state.player.flags.digestTime = state.info.time.value; startTimer({ id: "absorb-clock", func: () => { state.info.time.value += 1; state.info.time.value %= 86000; if (state.info.time.value - state.player.flags.digestTime > 5 * 60) { print(["Your molten remains drain into the fox's depths..."]); goToRoom("absorbed"); return false; } return true; }, delay: 1000 / 15, loop: true, classes: [ ] }); playSfx("sfx/digest.ogg"); }, "exit": (room) => { }, "actions": [ { name: "Gurgle", desc: "Glorp", execute: (room) => { printRandom([ ["Grrrrgle"], ["Glorp"], ["Glrrrrrrnnnnnn..."], ["Gwoooooorgle"] ]); }, show: [ ], conditions: [ ] }, ], "exits": { }, "hooks": [ ], "data": { "stats": { } } }, "absorbed": { "id": "absorbed", "name": "Geta's Fat", "desc": "You're gone.", "move": (room) => { }, "enter": (room) => { playSfx("sfx/absorb.ogg"); }, "exit": (room) => { }, "actions": [ ], "exits": { }, "hooks": [ ], "data": { "stats": { } } }, } }); })();