a munch adventure
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

1430 lines
55 KiB

  1. (() => {
  2. function checkSuspicion(add = 0) {
  3. const old = getStat("suspicion");
  4. if (add >= 0) {
  5. add *= state.info.awareness.value;
  6. }
  7. changeStat("suspicion", add);
  8. if (getStat("suspicion") >= 100) {
  9. print(["Geta spots you!", "You're snatched up and tossed into the fox's bowl of cereal."]);
  10. goToRoom("in-bowl");
  11. return false;
  12. } else if (getStat("suspicion") >= 75 && old < 75) {
  13. print(["The fox is very suspicious. You're going to get caught if you keep this up..."]);
  14. }
  15. return true;
  16. }
  17. function randomBodyPart() {
  18. const choices = Object.entries(state.player.limbs).filter(([name, status]) => {
  19. return status;
  20. }).map(([name, status]) => name);
  21. return choices[Math.floor(Math.random() * choices.length)];
  22. }
  23. function limbsLost() {
  24. return Object.entries(state.player.limbs).filter(([name, status]) => {
  25. return !status;
  26. }).length;
  27. }
  28. function pickRandom(list) {
  29. return list[Math.floor(Math.random() * list.length)];
  30. }
  31. const word = {
  32. get swallow() { return pickRandom(["swallow", "gulp"]); },
  33. get slimy() { return pickRandom(["slimy", "sloppy", "slick", "glistening"])},
  34. get disgusting() { return pickRandom(["disgusting", "abhorrent", "rank", "horrific", "nauseating", "sickening", "wretched"])},
  35. get foul() { return pickRandom(["foul", "rank", "gross"])},
  36. get fatal() { return pickRandom(["fatal", "deadly"])},
  37. get painful() { return pickRandom(["painful", "agonizing", "unbearable"])}
  38. }
  39. function statLerp(stat, change, duration) {
  40. // pretty sure this'll be a random id...
  41. const id = new Date().getTime() + Math.random();
  42. const iterations = duration / 1000 * 60;
  43. startTimer({
  44. id: id,
  45. func: () => {
  46. changeStat(stat, change / iterations);
  47. return true;
  48. },
  49. delay: 1000 / 60,
  50. loop: true,
  51. classes: [
  52. ]
  53. });
  54. startTimer({
  55. id: id + "-stopper",
  56. func: () => {
  57. stopTimer(id);
  58. return false;
  59. },
  60. delay: duration,
  61. loop: false,
  62. classes: [
  63. ]
  64. });
  65. }
  66. const limbs = {
  67. head: "head",
  68. leftArm: "left arm",
  69. rightArm: "right arm",
  70. leftLeg: "left leg",
  71. rightLeg: "right leg"
  72. };
  73. stories.push({
  74. "id": "geta-unaware",
  75. "info": {
  76. "name": "Geta's Breakfast",
  77. "desc": "Try to sneak past a fox after a catastrophic shrinking incident.",
  78. "tags": [
  79. "prey",
  80. "fatal",
  81. "oral-vore",
  82. "hard-vore",
  83. "hard-digestion",
  84. "macro-micro"
  85. ]
  86. },
  87. "intro": {
  88. "start": "pepper-grinder",
  89. "setup": () => {
  90. state.info.awareness = {
  91. id: "awareness",
  92. name: "Geta's Awareness",
  93. type: "counter",
  94. value: 1,
  95. get render() {
  96. if (this.value < 1) {
  97. return "Distracted";
  98. } else if (this.value == 1) {
  99. return "Normal"
  100. } else {
  101. return "Alert"
  102. }
  103. }
  104. }
  105. state.geta = {};
  106. state.player.stats.health = { name: "Health", type: "meter", value: 100, min: 0, max: 100, get color() { return "rgb(" + (155+this.value) + "," + (this.value/2) + "," + (this.value/2) + ")" }};
  107. state.player.stats.stamina = { name: "Stamina", type: "meter", value: 100, min: 0, max: 100, get color() { return "rgb(55," + (155+this.value) + ",55)"}, hidden: true };
  108. state.player.stats.suspicion = { name: "Suspicion", type: "meter", value: 0, min: 0, max: 100, get color() { return "rgb(" + (155+this.value) + ",100,100)" }};
  109. state.player.stats.mawPos = { "name": "Struggle", "type": "meter", "value": 0.5, "min": 0, "max": 1, get color() {
  110. return (this.value <= 0.15 || this.value >= 0.85) ? "rgb(255,100,0)" : "rgb(0,255,0)"}, hidden: true }
  111. state.player.stats.throatPos = { "name": "Descent", "type": "meter", "value": 0.25, "min": 0, "max": 1, get color() { return "rgb(" + (100+155*this.value) + ",0,0)"}, hidden: true }
  112. state.player.stats.absorption = { "name": "Absorption", "type": "meter", "value": 0, "min": 0, "max": 1, get color() { return "rgb(" + (100+155*this.value) + ",0,0)"}, hidden: true }
  113. state.info.time.value = 60 * 60 * 7 + 60 * 17;
  114. state.player.limbs = {};
  115. state.player.limbs.head = true;
  116. state.player.limbs.leftArm = true;
  117. state.player.limbs.rightArm = true;
  118. state.player.limbs.leftLeg = true;
  119. state.player.limbs.rightLeg = true;
  120. startTimer({
  121. id: "clock",
  122. func: () => {
  123. state.info.time.value += 1;
  124. state.info.time.value %= 86000;
  125. return true;
  126. },
  127. delay: 1000,
  128. loop: true,
  129. classes: [
  130. ]
  131. });
  132. startTimer({
  133. id: "suspicion-decay",
  134. func: () => {
  135. checkSuspicion(-0.1);
  136. return true;
  137. },
  138. delay: 100,
  139. loop: true,
  140. classes: [
  141. "free"
  142. ]
  143. });
  144. startTimer({
  145. id: "timeout",
  146. func: () => {
  147. if (state.info.time.value == 60 * 60 * 7 + 60 * 19) {
  148. print(["The fox is almost done with his breakfast..."]);
  149. }
  150. if (state.info.time.value >= 60 * 60 * 7 + 60 * 20) {
  151. print(["Time's up! In you go."]);
  152. goToRoom("maw");
  153. return false;
  154. }
  155. return true;
  156. },
  157. delay: 1000,
  158. loop: true,
  159. classes: [
  160. "free"
  161. ]
  162. });
  163. startTimer({
  164. id: "geta-action",
  165. func: () => {
  166. const random = Math.random();
  167. if (random < 0.7) {
  168. print(["Geta slurps up a spoonful of cereal."]);
  169. return Math.random() * 3000 + 3000
  170. } else if (random < 0.9) {
  171. state.info.awareness.value = 0.1;
  172. print(["The fox yawns and stretches."]);
  173. startTimer({
  174. id: "yawn-end",
  175. func: () => {
  176. print(["Geta finishes his stretch"]);
  177. state.info.awareness.value = 1;
  178. return true;
  179. },
  180. delay: 5000,
  181. loop: false,
  182. classes: [
  183. "free"
  184. ]
  185. });
  186. return Math.random() * 3000 + 5000
  187. } else {
  188. state.info.awareness.value = 2;
  189. print(["Geta narrows his eyes and looks around the table. Something seems off to him..."]);
  190. startTimer({
  191. id: "squint-end",
  192. func: () => {
  193. print(["He goes back to his breakfast."]);
  194. state.info.awareness.value = 1;
  195. return true;
  196. },
  197. delay: 5000,
  198. loop: false,
  199. classes: [
  200. "free"
  201. ]
  202. });
  203. return Math.random() * 1000 + 6000
  204. }
  205. },
  206. delay: 5000,
  207. loop: true,
  208. classes: [
  209. "free"
  210. ]
  211. });
  212. },
  213. "intro": () => {
  214. print(["Game started", newline, "Exposition goes here later."]);
  215. }
  216. },
  217. "sounds": [
  218. "loop/heartbeat.ogg",
  219. "loop/stomach.ogg",
  220. "sfx/absorb.ogg",
  221. "sfx/big-gulp.ogg",
  222. "sfx/digest.ogg",
  223. "sfx/gulp-1.ogg",
  224. "sfx/gulp-2.ogg",
  225. "sfx/gulp-3.ogg",
  226. "sfx/swallow.ogg"
  227. ],
  228. "preload": [
  229. ],
  230. "refresh": () => {
  231. setBackgroundColor(50 - state.player.stats.health.value / 2, 0, 0);
  232. },
  233. "world": {
  234. "pepper-grinder": {
  235. "id": "pepper-grinder",
  236. "name": "Pepper Grinder",
  237. "desc": "You're hiding behind a pepper grinder",
  238. "move": (room) => {
  239. print(["You dart over to the pepper grinder, which looms over you like a greatwood."]);
  240. },
  241. "enter": (room) => {
  242. },
  243. "exit": (room) => {
  244. },
  245. "actions": [
  246. {
  247. name: "Tap",
  248. desc: "Bang on the pepper shaker",
  249. execute: (room) => {
  250. print(["You thump the pepper shaker, making a dull thud."]);
  251. const safe = checkSuspicion(25);
  252. if (safe && getStat("suspicion") > 50) {
  253. print(["Geta leans in to have a closer look. He's going to catch you if you don't move!"]);
  254. startTimer({
  255. id: "pepper-investigate",
  256. func: () => {
  257. if (state.player.location == "pepper-grinder") {
  258. print(["He catches you.", newline, "You're tossed into the fox's jaws."]);
  259. goToRoom("maw");
  260. } else {
  261. print(["You evaded the fox."]);
  262. }
  263. },
  264. delay: 3000,
  265. loop: false,
  266. classes: [
  267. "free"
  268. ]
  269. });
  270. }
  271. },
  272. show: [
  273. ],
  274. conditions: [
  275. ]
  276. },
  277. {
  278. name: "Wait",
  279. desc: "Wait for the fox to finish his breakfast. Surely you'll be able to escape after that...right?",
  280. execute: (room) => {
  281. state.info.time.value = 60 * 60 * 7 + 60 * 20;
  282. },
  283. show: [
  284. ],
  285. conditions: [
  286. ]
  287. },
  288. ],
  289. "exits": {
  290. "up": {
  291. "target": "bowl",
  292. "desc": "Walk up to the cereal bowl",
  293. "show": [
  294. ],
  295. "conditions": [
  296. ],
  297. "hooks": [
  298. (room, exit) => {
  299. return checkSuspicion(10);
  300. }
  301. ]
  302. },
  303. "left": {
  304. "target": "table",
  305. "desc": "Run out into the open",
  306. "show": [
  307. ],
  308. "conditions": [
  309. ],
  310. "hooks": [
  311. ]
  312. },
  313. },
  314. "hooks": [
  315. ],
  316. "data": {
  317. "stats": {
  318. }
  319. }
  320. },
  321. "bowl": {
  322. "id": "bowl",
  323. "name": "Behind the Bowl",
  324. "desc": "You're crouched behind Geta's bowl of cereal",
  325. "move": (room) => {
  326. print(["You scurry up to the looming bowl, staying low and out of Geta's sight."]);
  327. },
  328. "enter": (room) => {
  329. },
  330. "exit": (room) => {
  331. },
  332. "actions": [
  333. ],
  334. "exits": {
  335. "ascend": {
  336. "target": "in-bowl",
  337. "desc": "Climb into Geta's cereal",
  338. "show": [
  339. ],
  340. "conditions": [
  341. ],
  342. "hooks": [
  343. ]
  344. },
  345. "down": {
  346. "target": "pepper-grinder",
  347. "desc": "Run back behind the pepper grinder",
  348. "show": [
  349. ],
  350. "conditions": [
  351. ],
  352. "hooks": [
  353. (room, exit) => {
  354. return checkSuspicion(15);
  355. }
  356. ]
  357. },
  358. },
  359. "hooks": [
  360. ],
  361. "data": {
  362. "stats": {
  363. }
  364. }
  365. },
  366. "table": {
  367. "id": "table",
  368. "name": "Table",
  369. "desc": "You're out in the open!",
  370. "move": (room) => {
  371. },
  372. "enter": (room) => {
  373. startTimer({
  374. id: "table-suspicion",
  375. func: () => {
  376. checkSuspicion(1.5);
  377. return true;
  378. },
  379. delay: 100,
  380. loop: true,
  381. classes: [
  382. "free"
  383. ]
  384. });
  385. },
  386. "exit": (room) => {
  387. stopTimer("table-suspicion");
  388. },
  389. "actions": [
  390. ],
  391. "exits": {
  392. "right": {
  393. "target": "pepper-grinder",
  394. "desc": "Run back to cover",
  395. "show": [
  396. ],
  397. "conditions": [
  398. ],
  399. "hooks": [
  400. ]
  401. },
  402. },
  403. "hooks": [
  404. ],
  405. "data": {
  406. "stats": {
  407. }
  408. }
  409. },
  410. "in-bowl": {
  411. "id": "in-bowl",
  412. "name": "Bowl",
  413. "desc": "You're in the cereal bowl...",
  414. "move": (room) => {
  415. print(["Why did you do that?"]);
  416. },
  417. "enter": (room) => {
  418. state.player.stats.suspicion.hidden = true;
  419. stopClassTimers("free");
  420. startTimer({
  421. id: "geta-eat",
  422. func: () => {
  423. if (Math.random() < 0.6) {
  424. print(["Geta scoops up a spoonful of cereal; you narrowly avoid being caught."]);
  425. return true;
  426. } else {
  427. print(["Geta scoops you up and slurps you into his maw."]);
  428. goToRoom("maw");
  429. return false;
  430. }
  431. },
  432. delay: 3000,
  433. loop: true,
  434. classes: [
  435. "free"
  436. ]
  437. });
  438. },
  439. "exit": (room) => {
  440. },
  441. "actions": [
  442. ],
  443. "exits": {
  444. "ascend": {
  445. "target": "bowl",
  446. "desc": "Try to climb back out!",
  447. "show": [
  448. ],
  449. "conditions": [
  450. ],
  451. "hooks": [
  452. (room, exit) => {
  453. print([
  454. "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."
  455. ]);
  456. goToRoom("maw");
  457. return false;
  458. }
  459. ]
  460. },
  461. },
  462. "hooks": [
  463. ],
  464. "data": {
  465. "stats": {
  466. }
  467. }
  468. },
  469. "maw": {
  470. "id": "maw",
  471. "name": "Geta's Maw",
  472. "desc": "You've been slurped up into the fox's " + word.foul + " jaws",
  473. "move": (room) => {
  474. },
  475. "enter": (room) => {
  476. state.player.stats.suspicion.hidden = true;
  477. stopClassTimers("free");
  478. state.player.stats.stamina.hidden = false;
  479. state.player.stats.mawPos.hidden = false;
  480. state.geta.slurps = 0;
  481. state.geta.chews = 0;
  482. state.geta.swallowsLeft = 3 + Math.floor(Math.random() * 3);
  483. state.geta.mawMovement = 1;
  484. print(["You slip into Geta's maw; the atmosphere is " + word.disgusting + ". He'll swallow you alive if you slip too far back, but crawl too far forward, and you'll meet his fangs..."])
  485. startTimer({
  486. id: "maw-stamina",
  487. func: () => {
  488. changeStat("stamina", 0.1);
  489. return true;
  490. },
  491. delay: 1000 / 60,
  492. loop: true,
  493. classes: [
  494. "maw-struggle"
  495. ]
  496. });
  497. startTimer({
  498. id: "maw-random-movement",
  499. func: () => {
  500. const time = new Date().getTime();
  501. const movementFactor = (state.geta.mawMovement + limbsLost());
  502. const fastPart = Math.sin(time / 200) / 1600 / 3;
  503. const slowPart = Math.sin(time / 1000) / 1600;
  504. changeStat("mawPos", movementFactor * (fastPart + slowPart));
  505. if (getStat("mawPos") <= 0.02) {
  506. 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."]);
  507. goToRoom("throat");
  508. return false;
  509. } else if (getStat("mawPos") >= 0.98) {
  510. 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."]);
  511. playSfx("sfx/swallow.ogg");
  512. changeStat("health", -90);
  513. goToRoom("throat");
  514. state.player.flags.throatSurrender = true;
  515. return false;
  516. }
  517. return true;
  518. },
  519. delay: 1000 / 60,
  520. loop: true,
  521. classes: [
  522. "maw-struggle"
  523. ]
  524. });
  525. startTimer({
  526. id: "maw-struggle",
  527. func: () => {
  528. if (state.geta.swallowsLeft <= 0) {
  529. print(["Geta picks up his bowl of cereal and drinks it down, swallowing you in the process."]);
  530. state.geta.swallowsLeft = -1;
  531. goToRoom("throat");
  532. return false;
  533. }
  534. let choice;
  535. if (Math.random() < 0.2) {
  536. const choices = [
  537. "slosh",
  538. "tilt-back",
  539. "shove",
  540. ];
  541. choice = choices[Math.floor(Math.random() * choices.length)];
  542. } else if (Math.random() > state.geta.slurps / 3) {
  543. choice = "slurp";
  544. } else if (state.geta.chews - Math.floor(Math.random() * 3) < state.geta.slurps) {
  545. choice = "chew";
  546. } else {
  547. choice = "swallow";
  548. }
  549. if (choice == "swallow") {
  550. if (getStat("mawPos") <= 0.15) {
  551. print(["You're too far back. The fox swallows a mouthful of cereal, taking you with it."]);
  552. goToRoom("throat");
  553. return false;
  554. } else {
  555. printRandom([
  556. ["A light swallow drags a lump of chewed-up cereal into Geta's " + word.fatal + " depths."],
  557. ["Geta swallows, dragging you closer to your demise."],
  558. ["Your captor's throat ripples as he gulps down his breakfast."]
  559. ]);
  560. statLerp("mawPos", -0.25, 500);
  561. state.geta.slurps = 0;
  562. state.geta.chews = 0;
  563. state.geta.swallowsLeft -= 1;
  564. return Math.random() * 1500 + 1500;
  565. }
  566. } else if (choice == "slurp") {
  567. statLerp("mawPos", -0.1, 250);
  568. printRandom([
  569. ["A spoonful of cereal slips into the fox's sloppy maw."],
  570. ["Geta slurps up some more of his breakfast."],
  571. ["You're shoved back a bit as Geta slurps up more cereal."]
  572. ]);
  573. state.geta.slurps += 1;
  574. return Math.random() * 1000 + 2500;
  575. } else if (choice == "chew") {
  576. if (getStat("mawPos") >= 0.85) {
  577. const limb = randomBodyPart();
  578. state.player.limbs[limb] = false;
  579. const limbName = limbs[limb];
  580. if (limb == "head") {
  581. print(["Geta's jaws crush down on your head. You die."]);
  582. changeStat("health", -100);
  583. goToRoom("stomach");
  584. } else {
  585. print(["You scream in pain as your " + limbName + " is shattered by the fox's jaws"]);
  586. changeStat("health", -40);
  587. return true;
  588. }
  589. } else {
  590. printRandom([
  591. ["Cruel fangs crush down on the food around you."],
  592. ["Geta chews on his breakfast."],
  593. ["The fox's fangs close with a crackle-crunch of cereal."]
  594. ]);
  595. statLerp("mawPos", Math.random() / 10 - 0.05, 250);
  596. state.geta.chews += 1;
  597. return Math.random() * 500 + 1300;
  598. }
  599. } else if (choice == "slosh") {
  600. print(["Geta's tongue sloshes from side to side, throwing you around his maw like a ship in a storm."]);
  601. state.geta.mawMovement = 3;
  602. startTimer({
  603. id: "maw-slosh-end",
  604. func: () => {
  605. state.geta.mawMovement = 1;
  606. print(["The sloshing ends."]);
  607. return true;
  608. },
  609. delay: 4000,
  610. loop: false,
  611. classes: [
  612. "maw-struggle"
  613. ]
  614. });
  615. return Math.random() * 1500 + 4500;
  616. } else if (choice == "tilt-back") {
  617. print(["Geta tilts his head back, sending rivults of slobber flowing down into that yawning gullet."]);
  618. state.geta.mawMovement = 0.2;
  619. statLerp("mawPos", -1, 4000);
  620. startTimer({
  621. id: "maw-tilt-text",
  622. func: () => {
  623. state.geta.mawMovement = 1;
  624. print(["The fox's muzzle tilts back down as he SWALLOWS hard."]);
  625. statLerp("mawPos", -0.3, 500);
  626. state.geta.swallowsLeft -= 1;
  627. state.geta.slurps = 0;
  628. state.geta.chews = 0;
  629. return true;
  630. },
  631. delay: 4000,
  632. loop: false,
  633. classes: [
  634. "maw-struggle"
  635. ]
  636. });
  637. return 5000 + Math.random() * 1000;
  638. } else if (choice == "shove") {
  639. print(["Geta's tongue lurches forward, shoving you towards the front of his maw!"]);
  640. statLerp("mawPos", 0.2, 500);
  641. return 2000 + Math.random() * 1000;
  642. }
  643. },
  644. delay: 0,
  645. loop: true,
  646. classes: [
  647. "maw-struggle"
  648. ]
  649. });
  650. startTimer({
  651. id: "maw-taunts",
  652. func: () => {
  653. printRandom([
  654. ["\"Did you really think I wouldn't notice you?\""],
  655. ["\"You're going to feel good dying in my guts.\""],
  656. ["\"I could just crush you...but where's the fun in that?\""]
  657. ]);
  658. return Math.random() * 5000 + 5000;
  659. },
  660. delay: 5000,
  661. loop: true,
  662. classes: [
  663. "maw-struggle"
  664. ]
  665. });
  666. },
  667. "exit": (room) => {
  668. state.player.stats.stamina.hidden = true;
  669. state.player.stats.mawPos.hidden = true;
  670. stopClassTimers("maw-struggle");
  671. },
  672. "actions": [
  673. {
  674. name: "Struggle",
  675. desc: "Pull yourself away from the fox's throat! Just don't go too far forward...",
  676. execute: (room) => {
  677. if (getStat("stamina") < 25) {
  678. print(["You're too tired..."]);
  679. } else {
  680. print(["You drag yourself forward"]);
  681. changeStat("stamina", -25);
  682. statLerp("mawPos", 0.15 + Math.random() * 0.05, 250);
  683. }
  684. },
  685. show: [
  686. ],
  687. conditions: [
  688. ]
  689. },
  690. {
  691. name: "Slip Back",
  692. desc: "Slide back towards Geta's gullet",
  693. execute: (room) => {
  694. if (Math.random() * 25 > getStat("stamina")) {
  695. print(["You try to shimmy back an inch or two, but your sore muscles give way; you slide back perilously far!"]);
  696. statLerp("mawPos", -0.3 - Math.random() * 0.25, 250);
  697. }
  698. else if (Math.random() < 0.9) {
  699. print(["You let yourself slip back."]);
  700. statLerp("mawPos", -0.2 - Math.random() * 0.1, 250);
  701. } else {
  702. print(["You lose your grip, sliding back quite far!"]);
  703. statLerp("mawPos", -0.3 - Math.random() * 0.15, 250);
  704. }
  705. },
  706. show: [
  707. ],
  708. conditions: [
  709. ]
  710. },
  711. {
  712. name: "Dive In",
  713. desc: "Throw yourself towards the fox's throat",
  714. execute: (room) => {
  715. 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."]);
  716. goToRoom("throat");
  717. },
  718. show: [
  719. ],
  720. conditions: [
  721. ]
  722. },
  723. ],
  724. "exits": {
  725. },
  726. "hooks": [
  727. ],
  728. "data": {
  729. "stats": {
  730. }
  731. }
  732. },
  733. "throat": {
  734. "id": "throat",
  735. "name": "Geta's Gullet",
  736. "desc": "GULP!",
  737. "move": (room) => {
  738. },
  739. "enter": (room) => {
  740. playSfx("sfx/swallow.ogg");
  741. playLoop("loop/stomach.ogg", 0.3);
  742. state.player.stats.stamina.hidden = false;
  743. state.player.stats.throatPos.hidden = false;
  744. startTimer({
  745. id: "throat-stamina",
  746. func: () => {
  747. changeStat("stamina", 0.03);
  748. return true;
  749. },
  750. delay: 1000/60,
  751. loop: true,
  752. classes: [
  753. "throat-struggle"
  754. ]
  755. });
  756. state.geta.throatStage = 1;
  757. startTimer({
  758. id: "throat-stages",
  759. func: () => {
  760. if (state.geta.throatStage / 5 < getStat("throatPos")) {
  761. state.geta.throatStage = Math.ceil(getStat("throatPos") * 5);
  762. switch (state.geta.throatStage) {
  763. case 3:
  764. print(["You're sinking into the fox's chest..."]);
  765. break;
  766. case 4:
  767. print(["Deeper, deeper still..."]);
  768. break;
  769. case 5:
  770. print(["Your grave is just a swallow or two away."]);
  771. break;
  772. default:
  773. break;
  774. }
  775. }
  776. return true;
  777. },
  778. delay: 1000,
  779. loop: true,
  780. classes: [
  781. "throat-struggle"
  782. ]
  783. });
  784. startTimer({
  785. id: "throat-descent",
  786. func: () => {
  787. if (getStat("throatPos") <= 0.01) {
  788. print(["Geta swallows HARD, cramming you back down like the food you are."]);
  789. changeStat("throatPos", 0.1);
  790. statLerp("throatPos", 0.5, 1000);
  791. }
  792. if (getStat("throatPos") >= 0.99) {
  793. goToRoom("stomach");
  794. return false;
  795. }
  796. changeStat("throatPos", state.player.flags.throatSurrender ? 0.0005 : 0.0001);
  797. playLoop("loop/heartbeat.ogg", 0.3 + getStat("throatPos") * 0.7);
  798. return true;
  799. },
  800. delay: 1000 / 60,
  801. loop: true,
  802. classes: [
  803. "throat-struggle"
  804. ]
  805. });
  806. startTimer({
  807. id: "throat-swallows",
  808. func: () => {
  809. const choice = Math.random();
  810. if (choice < 0.7 ) {
  811. print(["Geta's throat pumps you deeper"]);
  812. playSfx(pickRandom([
  813. "sfx/gulp-1.ogg",
  814. "sfx/gulp-2.ogg",
  815. "sfx/gulp-3.ogg"
  816. ]));
  817. statLerp("throatPos", 0.1, 1250);
  818. return Math.random() * 2000 + 2000;
  819. } else if (choice < 0.85) {
  820. if (getStat("throatPos") < 0.4) {
  821. print(["A finger presses in on you from the outside. Your captor is enjoying himself...but at least it slows your descent a little."]);
  822. statLerp("throatPos", 0.05, 2000);
  823. return Math.random() * 4000 + 2000;
  824. } else {
  825. return Math.random() * 200 + 200;
  826. }
  827. } else {
  828. print(["A crushing swallow grips your body and crams you down deep."]);
  829. playSfx("sfx/big-gulp.ogg");
  830. statLerp("throatPos", 0.3, 1500);
  831. return Math.random() * 2000 + 2000;
  832. }
  833. },
  834. delay: 2000,
  835. loop: true,
  836. classes: [
  837. "throat-struggle"
  838. ]
  839. });
  840. },
  841. "exit": (room) => {
  842. state.player.stats.stamina.hidden = true;
  843. state.player.stats.throatPos.hidden = true;
  844. print(["You slush down into Geta's stomach"]);
  845. stopClassTimers("throat-struggle");
  846. },
  847. "actions": [
  848. {
  849. name: "Struggle",
  850. desc: "Try to climb back out!",
  851. execute: (room) => {
  852. if (Math.random() * 50 > getStat("stamina")) {
  853. print(["You try your best, but your sore muscles are no match."]);
  854. } else {
  855. print(["Your valiant struggles drag you a little closer to freedom."]);
  856. statLerp("throatPos", -0.15, 1000);
  857. changeStat("stamina", -20);
  858. }
  859. },
  860. show: [
  861. (room) => {
  862. return !state.player.flags.throatSurrender;
  863. }
  864. ],
  865. conditions: [
  866. ]
  867. },
  868. {
  869. name: "Give up",
  870. desc: "Dive down into Geta's stomach",
  871. execute: (room) => {
  872. print(["You submit to your predator."]);
  873. state.player.flags.throatSurrender = true;
  874. },
  875. show: [
  876. (room) => {
  877. return !state.player.flags.throatSurrender;
  878. }
  879. ],
  880. conditions: [
  881. ]
  882. },
  883. ],
  884. "exits": {
  885. },
  886. "hooks": [
  887. ],
  888. "data": {
  889. "stats": {
  890. }
  891. }
  892. },
  893. "stomach": {
  894. "id": "stomach",
  895. "name": "Geta's Stomach",
  896. "desc": "Glorp",
  897. "move": (room) => {
  898. },
  899. "enter": (room) => {
  900. playLoop("loop/stomach.ogg");
  901. stopLoop("loop/heartbeat.ogg");
  902. state.geta.digestionStage = 0;
  903. state.geta.acidStrength = 1;
  904. startTimer({
  905. id: "digest-stages",
  906. func: () => {
  907. if (100 - state.geta.digestionStage * 25 - 25 > getStat("health")) {
  908. state.geta.digestionStage = Math.floor((100 - getStat("health")) / 25);
  909. console.log(state.geta.digestionStage);
  910. switch (state.geta.digestionStage) {
  911. case 1:
  912. print(["Your skin begins to tingle."]);
  913. break;
  914. case 2:
  915. print(["The stinging acids work their way into your tender body."]);
  916. break;
  917. case 3:
  918. print(["You're starting to fall apart..."]);
  919. break;
  920. default:
  921. break;
  922. }
  923. }
  924. return true;
  925. },
  926. delay: 1000,
  927. loop: true,
  928. classes: [
  929. "digestion"
  930. ]
  931. });
  932. startTimer({
  933. id: "digest-random",
  934. func: () => {
  935. const choices = [
  936. () => {
  937. const crushed = randomBodyPart();
  938. const name = limbs[crushed];
  939. if (name == "head") {
  940. print(["A powerful fold of muscle grips your head, crushing it like a grape and killing you instantly."]);
  941. changeStat("health", -100);
  942. return false;
  943. } else {
  944. print(["Geta's stomach grips your " + name + " and crushes it with a horrific CRACK"]);
  945. changeStat("health", -40);
  946. return true;
  947. }
  948. },
  949. () => {
  950. printRandom([["Geta squeezes in on his gut with both hands, sloshing you around in the sickly stew of cereal, milk, and enzymatic slime."],
  951. ["Your organic prison snarls and churns, soaking you in fresh acids and hastening your wretched demise."]]);
  952. statLerp("health", -10, 2000);
  953. return true;
  954. },
  955. () => {
  956. if (state.geta.swallowsLeft == 0) {
  957. print(["A deep series of *glurks* rattles your bones."]);
  958. startTimer({
  959. id: "stomach-milk",
  960. func: () => {
  961. print(["A torrent of cold milk pours into the fox's stomach."]);
  962. return false;
  963. },
  964. delay: 3000,
  965. loop: false,
  966. classes: [
  967. "digestion"
  968. ]
  969. });
  970. } else if (state.geta.swallowsLeft > 0) {
  971. print(["Muffled chewing comes from far above. A moment later, you hear a wet *gluk*"]);
  972. startTimer({
  973. id: "stomach-cereal",
  974. func: () => {
  975. print(["A slimy heap of well-chewed corn flakes splatters down around you."]);
  976. return false;
  977. },
  978. delay: 4000,
  979. loop: false,
  980. classes: [
  981. ]
  982. });
  983. } else if (state.geta.swallowsLeft < 0) {
  984. print(["You hear a few light swallows."]);
  985. startTimer({
  986. id: "stomach-coffee",
  987. func: () => {
  988. print(["Gouts of hot, bitter coffee pour into the fox's guts."]);
  989. return false;
  990. },
  991. delay: 3000,
  992. loop: false,
  993. classes: [
  994. ]
  995. });
  996. }
  997. return true;
  998. },
  999. () => {
  1000. print(["\"You were barely worth eating,\" murmurs the fox. \"So small. So weak.\""]);
  1001. return true;
  1002. }
  1003. ];
  1004. if (choices[Math.floor(Math.random() * choices.length)]()) {
  1005. return Math.random() * 3000 + 3500;
  1006. } else {
  1007. return false;
  1008. }
  1009. },
  1010. delay: 5000,
  1011. loop: true,
  1012. classes: [
  1013. "digestion"
  1014. ]
  1015. });
  1016. startTimer({
  1017. id: "digest",
  1018. func: () => {
  1019. changeStat("health", -0.3 * state.geta.acidStrength);
  1020. if (getStat("health") <= 0) {
  1021. print(["You're gradually digested, merciful oblivion ending your torment in the " + word.disgusting + " depths of your captor..."]);
  1022. goToRoom("digested");
  1023. return false;
  1024. }
  1025. return true;
  1026. },
  1027. delay: 100,
  1028. loop: true,
  1029. classes: [
  1030. "digestion"
  1031. ]
  1032. });
  1033. },
  1034. "exit": (room) => {
  1035. },
  1036. "actions": [
  1037. {
  1038. name: "Squirm",
  1039. desc: "Rub at the walls of the fox's churning stomach",
  1040. execute: (room) => {
  1041. printRandom([
  1042. ["You punch and kick at the walls"],
  1043. ["A powerful churn grabs hold of you, stifling any attempts at struggling"],
  1044. ["Your little thumps and kicks do little to faze your captor"]
  1045. ]);
  1046. },
  1047. show: [
  1048. ],
  1049. conditions: [
  1050. ]
  1051. },
  1052. {
  1053. name: "Beg",
  1054. desc: "Plead for your life",
  1055. execute: (room) => {
  1056. printRandom([
  1057. [
  1058. "\"PLEASE!\" you scream, thumping on the walls of the vulpine's gut. \"Let me out!\"",
  1059. ]
  1060. ])
  1061. if (Math.random() < 0.7) {
  1062. print(["Your pleas fall on deaf ears."]);
  1063. } else {
  1064. printRandom([
  1065. ["\"Shhhh,\" growls Geta, \"you're going to die in me. Stop whimpering.\""],
  1066. ["A long moment passes. \"Poor thing,\" says your captor."]
  1067. ])
  1068. }
  1069. },
  1070. show: [
  1071. ],
  1072. conditions: [
  1073. ]
  1074. },
  1075. {
  1076. name: "Scream",
  1077. desc: "IT HURTS",
  1078. execute: (room) => {
  1079. printRandom([
  1080. [
  1081. "\"Oh god, oh god, oh god,\" you wail...quivering and quaking as you're digested alive. \"GETA!\""
  1082. ],
  1083. [
  1084. "A blood-curdling scream bellows from your burning lungs."
  1085. ],
  1086. [
  1087. "You let out a hideous wail as the fox digests you alive."
  1088. ]
  1089. ]);
  1090. if (Math.random() < 0.5) {
  1091. print(["Geta doesn't notice."]);
  1092. } else {
  1093. print(["A booming chuckle rocks your body."]);
  1094. printRandom([
  1095. ["\"I hope you're suffering in there.\""],
  1096. ["\"Pathetic little snack.\""],
  1097. ["\"Ready to die?\""]
  1098. ]);
  1099. }
  1100. },
  1101. show: [
  1102. ],
  1103. conditions: [
  1104. ]
  1105. },
  1106. ],
  1107. "exits": {
  1108. },
  1109. "hooks": [
  1110. ],
  1111. "data": {
  1112. "stats": {
  1113. }
  1114. }
  1115. },
  1116. "digested": {
  1117. "id": "digested",
  1118. "name": "Geta's Stomach",
  1119. "desc": "You're just mush now",
  1120. "move": (room) => {
  1121. },
  1122. "enter": (room) => {
  1123. stopClassTimers("digestion");
  1124. stopTimer("clock");
  1125. state.player.stats.health.hidden = true;
  1126. state.player.stats.absorption.hidden = false;
  1127. startTimer({
  1128. id: "clock",
  1129. func: () => {
  1130. state.info.time.value += 2;
  1131. state.info.time.value %= 86000;
  1132. return true;
  1133. },
  1134. delay: 1000/60,
  1135. loop: true,
  1136. classes: [
  1137. ]
  1138. });
  1139. startTimer({
  1140. id: "passive-absorb",
  1141. func: () => {
  1142. changeStat("absorption", 0.0004);
  1143. if (getStat("absorption") >= 1) {
  1144. goToRoom("absorbed");
  1145. return false;
  1146. }
  1147. return true;
  1148. },
  1149. delay: 1000/60,
  1150. loop: true,
  1151. classes: [
  1152. "absorption"
  1153. ]
  1154. });
  1155. startTimer({
  1156. id: "absorbption-random",
  1157. func: () => {
  1158. const result = pickRandom([
  1159. () => {
  1160. print(["The fox kneads on his flattening belly"]);
  1161. statLerp("absorption", 0.1, 1000);
  1162. },
  1163. () => {
  1164. print(["A crass belch pours from Geta's maw"]);
  1165. statLerp("absorption", 0.05, 1000);
  1166. },
  1167. () => {
  1168. print(["Geta's guts writhe and squeeze, soaking up your digested body and crushing your acid-pitted bones"]);
  1169. statLerp("absorption", 0.2, 1000);
  1170. }
  1171. ])();
  1172. return result ? result : Math.random() * 3000 + 4000;
  1173. },
  1174. delay: 2000,
  1175. loop: true,
  1176. classes: [
  1177. "absorption"
  1178. ]
  1179. });
  1180. playSfx("sfx/digest.ogg");
  1181. },
  1182. "exit": (room) => {
  1183. },
  1184. "actions": [
  1185. {
  1186. name: "Gurgle",
  1187. desc: "Glorp",
  1188. execute: (room) => {
  1189. printRandom([
  1190. ["Grrrrgle"],
  1191. ["Glorp"],
  1192. ["Glrrrrrrnnnnnn..."],
  1193. ["Gwoooooorgle"]
  1194. ]);
  1195. },
  1196. show: [
  1197. ],
  1198. conditions: [
  1199. ]
  1200. },
  1201. ],
  1202. "exits": {
  1203. },
  1204. "hooks": [
  1205. ],
  1206. "data": {
  1207. "stats": {
  1208. }
  1209. }
  1210. },
  1211. "absorbed": {
  1212. "id": "absorbed",
  1213. "name": "Geta's Fat",
  1214. "desc": "You're gone.",
  1215. "move": (room) => {
  1216. },
  1217. "enter": (room) => {
  1218. playSfx("sfx/absorb.ogg");
  1219. stopTimer("clock");
  1220. stopClassTimers("absorption");
  1221. state.player.stats.absorption.hidden = true;
  1222. print(["Your erasure is completed with a dull, slimy gurgle. Geta has destroyed you."]);
  1223. },
  1224. "exit": (room) => {
  1225. },
  1226. "actions": [
  1227. ],
  1228. "exits": {
  1229. },
  1230. "hooks": [
  1231. ],
  1232. "data": {
  1233. "stats": {
  1234. }
  1235. }
  1236. },
  1237. }
  1238. });
  1239. })();