a munch adventure
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

955 строки
33 KiB

  1. (() => {
  2. function checkSuspicion(state, add = 0) {
  3. const old = getStat("suspicion", state);
  4. if (add >= 0) {
  5. add *= state.info.awareness.value;
  6. }
  7. changeStat("suspicion", add, state);
  8. if (getStat("suspicion", state) >= 100) {
  9. print(["Geta spots you!", "You're snatched up and tossed into the fox's bowl of cereal."]);
  10. goToRoom("in-bowl", state);
  11. return false;
  12. } else if (getStat("suspicion", state) >= 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(state) {
  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(state) {
  24. return Object.entries(state.player.limbs).filter(([name, status]) => {
  25. return !status;
  26. }).length;
  27. }
  28. function statLerp(id, stat, change, duration) {
  29. const iterations = duration / 1000 * 60;
  30. startTimer({
  31. id: id,
  32. func: state => {
  33. changeStat(stat, change / iterations, state);
  34. return true;
  35. },
  36. delay: 1000 / 60,
  37. loop: true,
  38. classes: [
  39. ]
  40. }, state);
  41. startTimer({
  42. id: id + "-stopper",
  43. func: state => {
  44. stopTimer(id, state);
  45. return false;
  46. },
  47. delay: duration,
  48. loop: false,
  49. classes: [
  50. ]
  51. }, state);
  52. }
  53. const limbs = {
  54. head: "head",
  55. leftArm: "left arm",
  56. rightArm: "right arm",
  57. leftLeg: "left leg",
  58. rightLeg: "right leg"
  59. };
  60. stories.push({
  61. "id": "geta-unaware",
  62. "name": "Geta's Breakfast",
  63. "tags": [
  64. "Player Prey",
  65. "Digestion",
  66. "Macro/Micro",
  67. "Hard Vore"
  68. ],
  69. "intro": {
  70. "start": "pepper-grinder",
  71. "setup": state => {
  72. state.info.awareness = {
  73. id: "awareness",
  74. name: "Geta's Awareness",
  75. type: "counter",
  76. value: 1,
  77. get render() {
  78. if (this.value < 1) {
  79. return "Distracted";
  80. } else if (this.value == 1) {
  81. return "Normal"
  82. } else {
  83. return "Alert"
  84. }
  85. }
  86. }
  87. state.geta = {};
  88. state.player.stats.health = { name: "Health", type: "meter", value: 100, min: 0, max: 100, color: "rgb(255,55,55)" };
  89. state.player.stats.suspicion = { name: "Suspicion", type: "meter", value: 0, min: 0, max: 100, color: "rgb(100,100,100)" };
  90. state.player.stats.mawPos = { "name": "Struggle", "type": "meter", "value": 0.5, "min": 0, "max": 1, "color": "rgb(0,255,0)", hidden: true }
  91. state.info.time.value = 60 * 60 * 7 + 60 * 17;
  92. state.player.limbs = {};
  93. state.player.limbs.head = true;
  94. state.player.limbs.leftArm = true;
  95. state.player.limbs.rightArm = true;
  96. state.player.limbs.leftLeg = true;
  97. state.player.limbs.rightLef = true;
  98. startTimer({
  99. id: "clock",
  100. func: state => {
  101. state.info.time.value += 1;
  102. state.info.time.value %= 86000;
  103. return true;
  104. },
  105. delay: 1000,
  106. loop: true,
  107. classes: [
  108. ]
  109. }, state);
  110. startTimer({
  111. id: "suspicion-decay",
  112. func: state => {
  113. checkSuspicion(state, -0.1);
  114. return true;
  115. },
  116. delay: 100,
  117. loop: true,
  118. classes: [
  119. "free"
  120. ]
  121. }, state);
  122. startTimer({
  123. id: "timeout",
  124. func: state => {
  125. if (state.info.time.value == 60 * 60 * 7 + 60 * 19) {
  126. print(["The fox is almost done with his breakfast..."]);
  127. }
  128. if (state.info.time.value >= 60 * 60 * 7 + 60 * 20) {
  129. print(["Time's up! In you go."]);
  130. goToRoom("maw", state);
  131. return false;
  132. }
  133. return true;
  134. },
  135. delay: 1000,
  136. loop: true,
  137. classes: [
  138. "free"
  139. ]
  140. }, state);
  141. startTimer({
  142. id: "geta-action",
  143. func: state => {
  144. const random = Math.random();
  145. if (random < 0.7) {
  146. print(["Geta slurps up a spoonful of cereal."]);
  147. return Math.random() * 3000 + 3000
  148. } else if (random < 0.9) {
  149. state.info.awareness.value = 0.1;
  150. print(["The fox yawns and stretches."]);
  151. startTimer({
  152. id: "yawn-end",
  153. func: state => {
  154. print(["Geta finishes his stretch"]);
  155. state.info.awareness.value = 1;
  156. return true;
  157. },
  158. delay: 5000,
  159. loop: false,
  160. classes: [
  161. "free"
  162. ]
  163. }, state);
  164. return Math.random() * 3000 + 5000
  165. } else {
  166. state.info.awareness.value = 2;
  167. print(["Geta narrows his eyes and looks around the table. Something seems off to him..."]);
  168. startTimer({
  169. id: "squint-end",
  170. func: state => {
  171. print(["He goes back to his breakfast."]);
  172. state.info.awareness.value = 1;
  173. return true;
  174. },
  175. delay: 5000,
  176. loop: false,
  177. classes: [
  178. "free"
  179. ]
  180. }, state);
  181. return Math.random() * 1000 + 6000
  182. }
  183. },
  184. delay: 5000,
  185. loop: true,
  186. classes: [
  187. "free"
  188. ]
  189. }, state);
  190. },
  191. "intro": state => {
  192. print(["Game started", newline, "Exposition goes here later."]);
  193. }
  194. },
  195. "sounds": [
  196. "loop/stomach.ogg"
  197. ],
  198. "preload": [
  199. ],
  200. "world": {
  201. "pepper-grinder": {
  202. "id": "pepper-grinder",
  203. "name": "Pepper Grinder",
  204. "desc": "You're hiding behind a pepper grinder",
  205. "move": (room, state) => {
  206. print(["You dart over to the pepper grinder, which looms over you like a greatwood."]);
  207. },
  208. "enter": (room, state) => {
  209. },
  210. "exit": (room, state) => {
  211. },
  212. "actions": [
  213. {
  214. name: "Tap",
  215. desc: "Bang on the pepper shaker",
  216. execute: (room, state) => {
  217. print(["You thump the pepper shaker, making a dull thud."]);
  218. const safe = checkSuspicion(state, 25);
  219. if (safe && getStat("suspicion", state) > 50) {
  220. print(["Geta leans in to have a closer look. He's going to catch you if you don't move!"]);
  221. startTimer({
  222. id: "pepper-investigate",
  223. func: state => {
  224. if (state.player.location == "pepper-grinder") {
  225. print(["He catches you.", newline, "You're tossed into the fox's jaws."]);
  226. goToRoom("maw", state);
  227. } else {
  228. print(["You evaded the fox."]);
  229. }
  230. },
  231. delay: 3000,
  232. loop: false,
  233. classes: [
  234. "free"
  235. ]
  236. }, state);
  237. }
  238. },
  239. show: [
  240. ],
  241. conditions: [
  242. ]
  243. },
  244. {
  245. name: "Wait",
  246. desc: "Wait for the fox to finish his breakfast. Surely you'll be able to escape after that...right?",
  247. execute: (room, state) => {
  248. state.info.time.value = 60 * 60 * 7 + 60 * 20;
  249. },
  250. show: [
  251. ],
  252. conditions: [
  253. ]
  254. },
  255. ],
  256. "exits": {
  257. "up": {
  258. "target": "bowl",
  259. "desc": "Walk up to the cereal bowl",
  260. "show": [
  261. ],
  262. "conditions": [
  263. ],
  264. "hooks": [
  265. (room, exit, state) => {
  266. return checkSuspicion(state, 10);
  267. }
  268. ]
  269. },
  270. "left": {
  271. "target": "table",
  272. "desc": "Run out into the open",
  273. "show": [
  274. ],
  275. "conditions": [
  276. ],
  277. "hooks": [
  278. ]
  279. },
  280. },
  281. "hooks": [
  282. ],
  283. "data": {
  284. "stats": {
  285. }
  286. }
  287. },
  288. "bowl": {
  289. "id": "bowl",
  290. "name": "Behind the Bowl",
  291. "desc": "You're crouched behind Geta's bowl of cereal",
  292. "move": (room, state) => {
  293. print(["You scurry up to the looming bowl, staying low and out of Geta's sight."]);
  294. },
  295. "enter": (room, state) => {
  296. },
  297. "exit": (room, state) => {
  298. },
  299. "actions": [
  300. ],
  301. "exits": {
  302. "ascend": {
  303. "target": "in-bowl",
  304. "desc": "Climb into Geta's cereal",
  305. "show": [
  306. ],
  307. "conditions": [
  308. ],
  309. "hooks": [
  310. ]
  311. },
  312. "down": {
  313. "target": "pepper-grinder",
  314. "desc": "Run back behind the pepper grinder",
  315. "show": [
  316. ],
  317. "conditions": [
  318. ],
  319. "hooks": [
  320. (room, exit, state) => {
  321. return checkSuspicion(state, 15);
  322. }
  323. ]
  324. },
  325. },
  326. "hooks": [
  327. ],
  328. "data": {
  329. "stats": {
  330. }
  331. }
  332. },
  333. "table": {
  334. "id": "table",
  335. "name": "Table",
  336. "desc": "You're out in the open!",
  337. "move": (room, state) => {
  338. },
  339. "enter": (room, state) => {
  340. startTimer({
  341. id: "table-suspicion",
  342. func: state => {
  343. checkSuspicion(state, 1.5);
  344. return true;
  345. },
  346. delay: 100,
  347. loop: true,
  348. classes: [
  349. "free"
  350. ]
  351. }, state);
  352. },
  353. "exit": (room, state) => {
  354. stopTimer("table-suspicion", state);
  355. },
  356. "actions": [
  357. ],
  358. "exits": {
  359. "right": {
  360. "target": "pepper-grinder",
  361. "desc": "Run back to cover",
  362. "show": [
  363. ],
  364. "conditions": [
  365. ],
  366. "hooks": [
  367. ]
  368. },
  369. },
  370. "hooks": [
  371. ],
  372. "data": {
  373. "stats": {
  374. }
  375. }
  376. },
  377. "in-bowl": {
  378. "id": "in-bowl",
  379. "name": "Bowl",
  380. "desc": "You're in the cereal bowl...",
  381. "move": (room, state) => {
  382. print(["Why did you do that?"]);
  383. },
  384. "enter": (room, state) => {
  385. stopClassTimers("free", state);
  386. startTimer({
  387. id: "geta-eat",
  388. func: state => {
  389. if (Math.random() < 0.6) {
  390. print(["Geta scoops up a spoonful of cereal; you narrowly avoid being caught."]);
  391. return true;
  392. } else {
  393. print(["Geta scoops you up and slurps you into his maw."]);
  394. goToRoom("maw", state);
  395. return false;
  396. }
  397. },
  398. delay: 3000,
  399. loop: true,
  400. classes: [
  401. "free"
  402. ]
  403. }, state);
  404. },
  405. "exit": (room, state) => {
  406. },
  407. "actions": [
  408. ],
  409. "exits": {
  410. "ascend": {
  411. "target": "bowl",
  412. "desc": "Try to climb back out!",
  413. "show": [
  414. ],
  415. "conditions": [
  416. ],
  417. "hooks": [
  418. (room, exit, state) => {
  419. print([
  420. "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."
  421. ]);
  422. goToRoom("maw", state);
  423. return false;
  424. }
  425. ]
  426. },
  427. },
  428. "hooks": [
  429. ],
  430. "data": {
  431. "stats": {
  432. }
  433. }
  434. },
  435. "maw": {
  436. "id": "maw",
  437. "name": "Geta's Maw",
  438. "desc": "You've been slurped up into the fox's jaws",
  439. "move": (room, state) => {
  440. },
  441. "enter": (room, state) => {
  442. stopClassTimers("free", state);
  443. state.player.stats.mawPos.hidden = false;
  444. state.geta.slurps = 0;
  445. state.geta.chews = 0;
  446. state.geta.swallowsLeft = 3 + Math.floor(Math.random() * 3);
  447. state.geta.mawMovement = 1;
  448. startTimer({
  449. id: "maw-random-movement",
  450. func: state => {
  451. const time = new Date().getTime();
  452. const movementFactor = (state.geta.mawMovement + limbsLost(state));
  453. const fastPart = Math.sin(time / 200) / 1600 / 3;
  454. const slowPart = Math.sin(time / 1000) / 1600;
  455. changeStat("mawPos", movementFactor * (fastPart + slowPart), state);
  456. if (getStat("mawPos", state) <= 0.02) {
  457. print(["You slip too far back. Geta doesn't even have to try to swallow you like the food you are."]);
  458. goToRoom("throat", state);
  459. return false;
  460. } else if (getStat("mawPos", state) >= 0.98) {
  461. 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."]);
  462. changeStat("health", -90, state);
  463. goToRoom("stomach", state);
  464. return false;
  465. }
  466. return true;
  467. },
  468. delay: 1000 / 60,
  469. loop: true,
  470. classes: [
  471. "maw-struggle"
  472. ]
  473. }, state);
  474. startTimer({
  475. id: "maw-struggle",
  476. func: state => {
  477. if (state.geta.swallowsLeft <= 0) {
  478. print(["Geta picks up his bowl of cereal and drinks it down, swallowing you in the process."]);
  479. goToRoom("throat", state);
  480. return false;
  481. }
  482. let choice;
  483. if (Math.random() < 0.1) {
  484. choice = "slosh";
  485. } else if (Math.random() > state.geta.slurps / 3) {
  486. choice = "slurp";
  487. } else if (state.geta.chews - Math.floor(Math.random() * 3) < state.geta.slurps) {
  488. choice = "chew";
  489. } else {
  490. choice = "swallow";
  491. }
  492. if (choice == "swallow") {
  493. if (getStat("mawPos", state) < 0.15) {
  494. print(["Swallowed!"]);
  495. goToRoom("throat", state);
  496. return false;
  497. } else {
  498. print(["Swallows"]);
  499. statLerp("maw-swallow", "mawPos", -0.25, 500);
  500. state.geta.slurps = 0;
  501. state.geta.chews = 0;
  502. state.geta.swallowsLeft -= 1;
  503. return Math.random() * 1500 + 2500;
  504. }
  505. } else if (choice == "slurp") {
  506. statLerp("maw-swallow", "mawPos", -0.1, 250);
  507. print(["Slurps"]);
  508. state.geta.slurps += 1;
  509. return Math.random() * 1000 + 1500;
  510. } else if (choice == "chew") {
  511. if (getStat("mawPos", state) > 0.85) {
  512. const limb = randomBodyPart(state);
  513. state.player.limbs[limb] = false;
  514. const limbName = limbs[limb];
  515. if (limb == "head") {
  516. print(["He chewed your head :((("]);
  517. changeStat("health", -100, state);
  518. goToRoom("stomach", state);
  519. } else {
  520. print(["He chewed your " + limbName + " :("]);
  521. changeStat("health", -40, state);
  522. return true;
  523. }
  524. } else {
  525. print(["Chews"]);
  526. state.geta.chews += 1;
  527. return Math.random() * 500 + 1000;
  528. }
  529. } else if (choice == "slosh") {
  530. print(["Geta's tongue sloshes from side to side, throwing you around his maw like a ship in a storm."]);
  531. state.geta.mawMovement = 3;
  532. startTimer({
  533. id: "maw-slosh-end",
  534. func: state => {
  535. state.geta.mawMovement = 1;
  536. print(["The sloshing ends."]);
  537. return true;
  538. },
  539. delay: 4000,
  540. loop: false,
  541. classes: [
  542. "maw-struggle"
  543. ]
  544. }, state);
  545. return Math.random() * 1500 + 4500;
  546. }
  547. },
  548. delay: 0,
  549. loop: true,
  550. classes: [
  551. "maw-struggle"
  552. ]
  553. }, state);
  554. startTimer({
  555. id: "maw-taunts",
  556. func: state => {
  557. printRandom([
  558. ["\"Did you really think I wouldn't notice you?\""],
  559. ["\"You're going to feel good dying in my guts.\""],
  560. ["\"I could just crush you...but where's the fun in that?\""]
  561. ]);
  562. return Math.random() * 5000 + 5000;
  563. },
  564. delay: 5000,
  565. loop: true,
  566. classes: [
  567. "maw-struggle"
  568. ]
  569. }, state);
  570. },
  571. "exit": (room, state) => {
  572. },
  573. "actions": [
  574. {
  575. name: "Struggle",
  576. desc: "Pull yourself away from the fox's throat! Just don't go too far forward...",
  577. execute: (room, state) => {
  578. print(["You drag yourself forward"]);
  579. statLerp("maw-swallow", "mawPos", 0.15 + Math.random() * 0.05, 250);
  580. },
  581. show: [
  582. ],
  583. conditions: [
  584. ]
  585. },
  586. {
  587. name: "Slip Back",
  588. desc: "Slide back towards Geta's gullet",
  589. execute: (room, state) => {
  590. if (Math.random() < 0.9){
  591. print(["You let yourself slip back."]);
  592. statLerp("maw-swallow", "mawPos", -0.2 - Math.random() * 0.1, 250);
  593. } else {
  594. print(["You lose your grip, sliding back quite far!"]);
  595. statLerp("maw-swallow", "mawPos", -0.3 - Math.random() * 0.15, 250);
  596. }
  597. },
  598. show: [
  599. ],
  600. conditions: [
  601. ]
  602. },
  603. ],
  604. "exits": {
  605. },
  606. "hooks": [
  607. ],
  608. "data": {
  609. "stats": {
  610. }
  611. }
  612. },
  613. "throat": {
  614. "id": "throat",
  615. "name": "Geta's Gullet",
  616. "desc": "GULP!",
  617. "move": (room, state) => {
  618. },
  619. "enter": (room, state) => {
  620. state.player.stats.mawPos.hidden = true;
  621. stopClassTimers("maw-struggle", state);
  622. startTimer({
  623. id: "throat-swallow",
  624. func: state => {
  625. print(["You slush down into Geta's stomach"]);
  626. goToRoom("stomach", state);
  627. return true;
  628. },
  629. delay: 7000,
  630. loop: false,
  631. classes: [
  632. ]
  633. }, state);
  634. },
  635. "exit": (room, state) => {
  636. },
  637. "actions": [
  638. {
  639. name: "Struggle",
  640. desc: "Try to climb back out!",
  641. execute: (room, state) => {
  642. print(["Nope"]);
  643. },
  644. show: [
  645. ],
  646. conditions: [
  647. ]
  648. },
  649. {
  650. name: "Give up",
  651. desc: "Dive down into Geta's stomach",
  652. execute: (room, state) => {
  653. print(["You submit to your predator."]);
  654. goToRoom("stomach", state);
  655. stopTimer("throat-swallow", state);
  656. },
  657. show: [
  658. ],
  659. conditions: [
  660. ]
  661. },
  662. ],
  663. "exits": {
  664. },
  665. "hooks": [
  666. ],
  667. "data": {
  668. "stats": {
  669. }
  670. }
  671. },
  672. "stomach": {
  673. "id": "stomach",
  674. "name": "Geta's Stomach",
  675. "desc": "Glorp",
  676. "move": (room, state) => {
  677. },
  678. "enter": (room, state) => {
  679. playLoop("loop/stomach.ogg");
  680. stopClassTimers("maw-struggle", state);
  681. state.geta.acidStrength = 1;
  682. startTimer({
  683. id: "digest-random",
  684. func: state => {
  685. const choices = [
  686. () => {
  687. const crushed = randomBodyPart(state);
  688. const name = limbs[crushed];
  689. if (name == "head") {
  690. print(["A powerful fold of muscle grips your head, crushing it like a grape and killing you instantly."]);
  691. changeStat("health", -100, state);
  692. return false;
  693. } else {
  694. print(["Geta's stomach grips your " + name + " and crushes it with a horrific CRACK"]);
  695. changeStat("health", -40, state);
  696. return true;
  697. }
  698. },
  699. () => {
  700. print(["Geta squeezes in on his gut with both hands, sloshing you around in the sickly stew of cereal, milk, and enzymatic slime."]);
  701. changeStat("health", -10, state);
  702. return true;
  703. },
  704. () => {
  705. print(["Your organic prison snarls and churns, soaking you in fresh acids and hastening your wretched demise."]);
  706. state.geta.acidStretngth += 1;
  707. return true;
  708. },
  709. () => {
  710. print(["\"You were barely worth eating,\" murmurs the fox. \"So small. So weak.\""]);
  711. return true;
  712. }
  713. ];
  714. return choices[Math.floor(Math.random() * choices.length)]();
  715. },
  716. delay: 5000,
  717. loop: true,
  718. classes: [
  719. "digestion"
  720. ]
  721. }, state);
  722. startTimer({
  723. id: "digest",
  724. func: state => {
  725. changeStat("health", -0.3 * state.geta.acidStrength, state);
  726. if (getStat("health", state) <= 0) {
  727. print(["You're digested before too long."]);
  728. goToRoom("digested", state);
  729. return false;
  730. }
  731. return true;
  732. },
  733. delay: 100,
  734. loop: true,
  735. classes: [
  736. "digestion"
  737. ]
  738. }, state);
  739. },
  740. "exit": (room, state) => {
  741. },
  742. "actions": [
  743. {
  744. name: "Squirm",
  745. desc: "Rub at the walls of the fox's churning stomach",
  746. execute: (room, state) => {
  747. printRandom([
  748. ["You punch and kick at the walls"],
  749. ["A powerful churn grabs hold of you, stifling any attempts at struggling"],
  750. ["Your little thumps and kicks do little to faze your captor"]
  751. ]);
  752. },
  753. show: [
  754. ],
  755. conditions: [
  756. ]
  757. },
  758. ],
  759. "exits": {
  760. },
  761. "hooks": [
  762. ],
  763. "data": {
  764. "stats": {
  765. }
  766. }
  767. },
  768. "digested": {
  769. "id": "digested",
  770. "name": "Fat",
  771. "desc": "You're just fat now",
  772. "move": (room, state) => {
  773. },
  774. "enter": (room, state) => {
  775. stopClassTimers("digestion", state);
  776. },
  777. "exit": (room, state) => {
  778. },
  779. "actions": [
  780. {
  781. name: "Gurgle",
  782. desc: "Glorp",
  783. execute: (room, state) => {
  784. printRandom([
  785. ["Grrrrgle"],
  786. ["Glorp"],
  787. ["Glrrrrrrnnnnnn..."],
  788. ["Gwoooooorgle"]
  789. ]);
  790. },
  791. show: [
  792. ],
  793. conditions: [
  794. ]
  795. },
  796. ],
  797. "exits": {
  798. },
  799. "hooks": [
  800. ],
  801. "data": {
  802. "stats": {
  803. }
  804. }
  805. },
  806. }
  807. });
  808. })();