munch
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 

950 líneas
27 KiB

  1. let world = null;
  2. let currentRoom = null;
  3. let currentDialog = null;
  4. let currentFoe = null;
  5. let dirButtons = [];
  6. let mode = "explore";
  7. let actions = [];
  8. let time = 9*60*60;
  9. let date = 1;
  10. let newline = " ";
  11. let player = new Player();
  12. let playerAttacks = [];
  13. let struggles = [];
  14. let killingBlow = null;
  15. let deaths = [];
  16. let respawnRoom;
  17. let noLog = false;
  18. let MIDNIGHT = 0;
  19. let MORNING = 21600;
  20. let NOON = 43200;
  21. let EVENING = 64800;
  22. function toggleLog() {
  23. noLog = !noLog;
  24. if (noLog) {
  25. document.getElementById("log-button").innerHTML = "Log: Disabled";
  26. } else {
  27. document.getElementById("log-button").innerHTML = "Log: Enabled";
  28. }
  29. }
  30. function join(things) {
  31. if (things.length == 1) {
  32. return things[0].description("a");
  33. } else if (things.length == 2) {
  34. return things[0].description("a") + " and " + things[1].description("a");
  35. } else {
  36. let line = "";
  37. line = things.slice(0,-1).reduce((line, prey) => line + prey.description("a") + ", ", line);
  38. line += " and " + things[things.length-1].description("a");
  39. return line;
  40. }
  41. }
  42. function pickRandom(list) {
  43. return list[Math.floor(Math.random() * list.length)];
  44. }
  45. function pick(list, attacker, defender) {
  46. if (list.length == 0)
  47. return null;
  48. else {
  49. let sum = list.reduce((sum, choice) => choice.weight == undefined ? sum + 1 : sum + choice.weight(attacker, defender), 0);
  50. let target = Math.random() * sum;
  51. for (let i = 0; i < list.length; i++) {
  52. sum -= list[i].weight == undefined ? 1 : list[i].weight(attacker, defender);
  53. if (sum <= target) {
  54. return list[i];
  55. }
  56. }
  57. return list[list.length-1];
  58. }
  59. }
  60. function filterValid(options, attacker, defender) {
  61. let filtered = options.filter(option => option.conditions == undefined || option.conditions.reduce((result, test) => result && test(attacker, defender), true));
  62. return filtered.filter(option => option.requirements == undefined || option.requirements.reduce((result, test) => result && test(attacker, defender), true));
  63. }
  64. function filterPriority(options) {
  65. let max = options.reduce((max, option) => option.priority > max ? option.priority : max, -1000);
  66. return options.filter(option => option.priority == max);
  67. }
  68. function round(number, digits) {
  69. return Math.round(number * Math.pow(10,digits)) / Math.pow(10,digits);
  70. }
  71. function updateExploreCompass() {
  72. for (let i = 0; i < dirButtons.length; i++) {
  73. let button = dirButtons[i];
  74. button.classList.remove("active-button");
  75. button.classList.remove("inactive-button");
  76. button.classList.remove("disabled-button");
  77. if (currentRoom.exits[i] == null) {
  78. button.disabled = true;
  79. button.classList.add("inactive-button");
  80. button.innerHTML = "";
  81. } else {
  82. if (currentRoom.exits[i].conditions.reduce((result, test) => result && test(player.prefs), true)) {
  83. button.disabled = false;
  84. button.classList.add("active-button");
  85. button.innerHTML = currentRoom.exits[i].name;
  86. } else {
  87. button.disabled = true;
  88. button.classList.add("disabled-button");
  89. button.innerHTML = currentRoom.exits[i].name;
  90. }
  91. }
  92. }
  93. }
  94. function updateExploreActions() {
  95. updateActions();
  96. let actionButtons = document.querySelector("#actions");
  97. actionButtons.innerHTML = "";
  98. for (let i = 0; i < actions.length; i++) {
  99. let button = document.createElement("button");
  100. button.innerHTML = actions[i].name;
  101. button.classList.add("active-button");
  102. button.classList.add("action-button");
  103. button.addEventListener("click", function() { actions[i].action(); });
  104. actionButtons.appendChild(button);
  105. }
  106. }
  107. function updateExplore() {
  108. updateExploreCompass();
  109. updateExploreActions();
  110. }
  111. function updateEaten() {
  112. let list = document.getElementById("eaten");
  113. while(list.firstChild) {
  114. list.removeChild(list.firstChild);
  115. }
  116. if (player.health > 0)
  117. struggles = filterValid(currentFoe.struggles, currentFoe, player);
  118. else
  119. struggles = [submit(currentFoe)];
  120. for (let i = 0; i < struggles.length; i++) {
  121. let li = document.createElement("li");
  122. let button = document.createElement("button");
  123. button.classList.add("eaten-button");
  124. button.innerHTML = struggles[i].name;
  125. button.addEventListener("click", function() { struggleClicked(i); } );
  126. button.addEventListener("mouseover", function() { struggleHovered(i); } );
  127. button.addEventListener("mouseout", function() { document.getElementById("eaten-desc").innerHTML = ""; } );
  128. li.appendChild(button);
  129. list.appendChild(li);
  130. }
  131. }
  132. function updateCombat() {
  133. let list = document.getElementById("combat");
  134. while(list.firstChild) {
  135. list.removeChild(list.firstChild);
  136. }
  137. if (player.health > 0)
  138. if (currentFoe.playerAttacks == undefined)
  139. playerAttacks = filterValid(player.attacks, player, currentFoe);
  140. else
  141. playerAttacks = filterValid(currentFoe.playerAttacks.map(attack => attack(player)), player, currentFoe);
  142. else
  143. playerAttacks = [pass(player)];
  144. if (playerAttacks.length == 0)
  145. playerAttacks = [player.backupAttack];
  146. for (let i = 0; i < playerAttacks.length; i++) {
  147. let li = document.createElement("li");
  148. let button = document.createElement("button");
  149. button.classList.add("combat-button");
  150. button.innerHTML = playerAttacks[i].name;
  151. button.addEventListener("click", function() { attackClicked(i); } );
  152. button.addEventListener("mouseover", function() { attackHovered(i); } );
  153. button.addEventListener("mouseout", function() { document.getElementById("combat-desc").innerHTML = ""; } );
  154. li.appendChild(button);
  155. list.appendChild(li);
  156. }
  157. document.getElementById("stat-foe-name").innerHTML = "Name: " + currentFoe.name;
  158. document.getElementById("stat-foe-health").innerHTML = "Health: " + currentFoe.health + "/" + currentFoe.maxHealth;
  159. document.getElementById("stat-foe-stamina").innerHTML = "Stamina: " + currentFoe.stamina + "/" + currentFoe.maxStamina;
  160. document.getElementById("stat-foe-str").innerHTML = "Str: " + currentFoe.str;
  161. document.getElementById("stat-foe-dex").innerHTML = "Dex: " + currentFoe.dex;
  162. document.getElementById("stat-foe-con").innerHTML = "Con: " + currentFoe.con;
  163. }
  164. function updateDialog() {
  165. let list = document.getElementById("dialog");
  166. while(list.firstChild) {
  167. list.removeChild(list.firstChild);
  168. }
  169. for (let i = 0; i < currentDialog.choices.length; i++) {
  170. let activated = currentDialog.choices[i].node.requirements == undefined || currentDialog.choices[i].node.requirements.reduce((result, test) => result && test(player, currentFoe), true);
  171. let li = document.createElement("li");
  172. let button = document.createElement("button");
  173. button.classList.add("dialog-button");
  174. button.innerHTML = currentDialog.choices[i].text;
  175. button.addEventListener("click", function() { dialogClicked(i); });
  176. if (!activated) {
  177. button.classList.add("disabled-button");
  178. button.disabled = true;
  179. }
  180. li.appendChild(button);
  181. list.appendChild(li);
  182. }
  183. }
  184. function updateDisplay() {
  185. document.querySelectorAll(".selector").forEach(function (x) {
  186. x.style.display = "none";
  187. });
  188. switch(mode) {
  189. case "explore":
  190. document.getElementById("selector-explore").style.display = "flex";
  191. updateExplore();
  192. break;
  193. case "combat":
  194. document.getElementById("selector-combat").style.display = "flex";
  195. updateCombat();
  196. break;
  197. case "dialog":
  198. document.getElementById("selector-dialog").style.display = "flex";
  199. updateDialog();
  200. break;
  201. case "eaten":
  202. document.getElementById("selector-eaten").style.display = "flex";
  203. updateEaten();
  204. break;
  205. }
  206. document.getElementById("time").innerHTML = "Time: " + renderTime(time);
  207. document.getElementById("date").innerHTML = "Day " + date;
  208. document.getElementById("stat-name").innerHTML = "Name: " + player.name;
  209. document.getElementById("stat-health").innerHTML = "Health: " + round(player.health,0) + "/" + round(player.maxHealth,0);
  210. document.getElementById("stat-cash").innerHTML = "Cash: $" + round(player.cash,0);
  211. document.getElementById("stat-stamina").innerHTML = "Stamina: " + round(player.stamina,0) + "/" + round(player.maxStamina,0);
  212. document.getElementById("stat-str").innerHTML = "Str: " + player.str;
  213. document.getElementById("stat-dex").innerHTML = "Dex: " + player.dex;
  214. document.getElementById("stat-con").innerHTML = "Con: " + player.con;
  215. document.getElementById("stat-arousal").innerHTML = "Arousal: " + round(player.arousal,0) + "/" + round(player.arousalLimit(),0);
  216. document.getElementById("stat-stomach").innerHTML = "Stomach: " + round(player.stomach.fullness(),0) + "/" + player.stomach.capacity;
  217. if (player.prefs.pred.anal || player.prefs.scat)
  218. document.getElementById("stat-bowels").innerHTML = "Bowels: " + round(player.bowels.fullness(),0) + "/" + player.bowels.capacity;
  219. else
  220. document.getElementById("stat-bowels").innerHTML = "";
  221. if (player.prefs.pred.cock)
  222. document.getElementById("stat-balls").innerHTML = "Balls: " + round(player.balls.fullness(),0) + "/" + player.balls.capacity;
  223. else
  224. document.getElementById("stat-balls").innerHTML = "";
  225. if (player.prefs.pred.unbirth)
  226. document.getElementById("stat-womb").innerHTML = "Womb: " + round(player.womb.fullness(),0) + "/" + player.womb.capacity;
  227. else
  228. document.getElementById("stat-womb").innerHTML = "";
  229. if (player.prefs.pred.breast)
  230. document.getElementById("stat-breasts").innerHTML = "Breasts: " + round(player.breasts.fullness(),0) + "/" + player.breasts.capacity;
  231. else
  232. document.getElementById("stat-breasts").innerHTML = "";
  233. }
  234. function advanceTimeTo(newTime) {
  235. advanceTime((86400 + newTime - time) % 86400);
  236. }
  237. function advanceTime(amount) {
  238. time = (time + amount);
  239. date += Math.floor(time / 86400);
  240. time = time % 86400;
  241. player.restoreHealth(amount);
  242. player.restoreStamina(amount);
  243. update(player.stomach.digest(amount));
  244. update(player.bowels.digest(amount));
  245. update(player.balls.digest(amount));
  246. update(player.womb.digest(amount));
  247. update(player.breasts.digest(amount));
  248. update(player.buildArousal(amount));
  249. }
  250. function renderTime(time) {
  251. let suffix = (time < 43200) ? "AM" : "PM";
  252. let hour = Math.floor((time % 43200) / 3600);
  253. if (hour == 0)
  254. hour = 12;
  255. let minute = Math.floor(time / 60) % 60;
  256. if (minute < 9)
  257. minute = "0" + minute;
  258. return hour + ":" + minute + " " + suffix;
  259. }
  260. function move(direction) {
  261. if (noLog)
  262. clearScreen();
  263. let target = currentRoom.exits[direction];
  264. if (target == null) {
  265. alert("Tried to move to an empty room!");
  266. return;
  267. }
  268. moveTo(target,currentRoom.exitDescs[direction]);
  269. }
  270. function updateActions() {
  271. actions = [];
  272. currentRoom.objects.forEach(function (object) {
  273. object.actions.forEach(function (action) {
  274. if (action.conditions == undefined || action.conditions.reduce((result, cond) => result && cond(player), true))
  275. actions.push(action);
  276. });
  277. });
  278. }
  279. function moveToByName(roomName, desc="You go places lol", loading=false) {
  280. moveTo(world[roomName], desc, loading);
  281. }
  282. function moveTo(room,desc="You go places lol", loading=false) {
  283. currentRoom = room;
  284. if (!loading)
  285. advanceTime(30);
  286. update([desc,newline]);
  287. currentRoom.visit();
  288. updateDisplay();
  289. }
  290. window.addEventListener('load', function(event) {
  291. document.getElementById("start-button").addEventListener("click", start, false);
  292. });
  293. function start() {
  294. applySettings(generateSettings());
  295. transformVorePrefs(player.prefs);
  296. document.getElementById("create").style.display = "none";
  297. document.getElementById("game").style.display = "block";
  298. document.getElementById("stat-button-status").addEventListener("click", status, false);
  299. document.getElementById("log-button").addEventListener("click", toggleLog, false);
  300. loadCompass();
  301. loadDialog();
  302. world = createWorld();
  303. currentRoom = world["Bedroom"];
  304. respawnRoom = currentRoom;
  305. moveTo(currentRoom,"");
  306. updateDisplay();
  307. }
  308. // copied from Stroll LUL
  309. function generateSettings() {
  310. let form = document.forms.namedItem("character-form");
  311. let settings = {};
  312. for (let i=0; i<form.length; i++) {
  313. let value = form[i].value == "" ? form[i].placeholder : form[i].value;
  314. if (form[i].type == "text")
  315. if (form[i].value == "")
  316. settings[form[i].name] = form[i].placeholder;
  317. else
  318. settings[form[i].name] = value;
  319. else if (form[i].type == "number")
  320. settings[form[i].name] = parseFloat(value);
  321. else if (form[i].type == "checkbox") {
  322. settings[form[i].name] = form[i].checked;
  323. } else if (form[i].type == "radio") {
  324. let name = form[i].name;
  325. if (form[i].checked) {
  326. if (form[i].value == "true")
  327. settings[name] = true;
  328. else if (form[i].value == "false")
  329. settings[name] = false;
  330. else
  331. settings[name] = form[i].value;
  332. }
  333. } else if (form[i].type == "select-one") {
  334. settings[form[i].name] = form[i][form[i].selectedIndex].value;
  335. }
  336. }
  337. return settings;
  338. }
  339. function applySettings(settings) {
  340. player.name = settings.name;
  341. player.species = settings.species;
  342. for (let key in settings) {
  343. if (settings.hasOwnProperty(key)) {
  344. if (key.match(/prefs/)) {
  345. let tokens = key.split("-");
  346. let pref = player.prefs;
  347. // construct missing child dictionaries if needed :)
  348. pref = tokens.slice(1,-1).reduce(function(pref, key) {
  349. if (pref[key] == undefined)
  350. pref[key] = {};
  351. return pref[key];
  352. }, pref);
  353. pref[tokens.slice(-1)[0]] = settings[key];
  354. } else if(key.match(/parts/)) {
  355. let tokens = key.split("-");
  356. player.parts[tokens[1]] = settings[key] >= 1;
  357. if (player.prefs.pred == undefined)
  358. player.prefs.pred = {};
  359. player.prefs.pred[tokens[1]] = settings[key] >= 2;
  360. }
  361. }
  362. }
  363. }
  364. // turn things like "1" into a number
  365. function transformVorePrefs(prefs) {
  366. let prey = false;
  367. for (let key in prefs.vore) {
  368. if (prefs.vore.hasOwnProperty(key)) {
  369. switch(prefs.vore[key]) {
  370. case "0": prefs.vore[key] = 0; break;
  371. case "1": prefs.vore[key] = 0.5; prey = true; break;
  372. case "2": prefs.vore[key] = 1; prey = true; break;
  373. case "3": prefs.vore[key] = 2; prey = true; break;
  374. }
  375. }
  376. }
  377. prefs.prey = prey;
  378. return prefs;
  379. }
  380. function saveSettings() {
  381. window.localStorage.setItem("settings", JSON.stringify(generateSettings()));
  382. }
  383. function retrieveSettings() {
  384. return JSON.parse(window.localStorage.getItem("settings"));
  385. }
  386. function clearScreen() {
  387. let log = document.getElementById("log");
  388. let child = log.firstChild;
  389. while (child != null) {
  390. log.removeChild(child);
  391. child = log.firstChild;
  392. }
  393. }
  394. function update(lines=[]) {
  395. let log = document.getElementById("log");
  396. for (let i=0; i<lines.length; i++) {
  397. let div = document.createElement("div");
  398. div.innerHTML = lines[i];
  399. log.appendChild(div);
  400. }
  401. log.scrollTop = log.scrollHeight;
  402. updateDisplay();
  403. }
  404. function changeMode(newMode) {
  405. mode = newMode;
  406. let body = document.querySelector("body");
  407. document.getElementById("foe-stats").style.display = "none";
  408. body.className = "";
  409. switch(mode) {
  410. case "explore":
  411. case "dialog":
  412. body.classList.add("explore");
  413. break;
  414. case "combat":
  415. body.classList.add("combat");
  416. document.getElementById("foe-stats").style.display = "block";
  417. break;
  418. case "eaten":
  419. body.classList.add("eaten");
  420. document.getElementById("foe-stats").style.display = "block";
  421. break;
  422. }
  423. updateDisplay();
  424. }
  425. // make it look like eaten mode, even when in combat
  426. function changeBackground(newMode) {
  427. let body = document.querySelector("body");
  428. document.getElementById("foe-stats").style.display = "none";
  429. body.className = "";
  430. switch(newMode) {
  431. case "explore":
  432. case "dialog":
  433. body.classList.add("explore");
  434. break;
  435. case "combat":
  436. body.classList.add("combat");
  437. document.getElementById("foe-stats").style.display = "block";
  438. break;
  439. case "eaten":
  440. body.classList.add("eaten");
  441. document.getElementById("foe-stats").style.display = "block";
  442. break;
  443. }
  444. }
  445. function respawn(respawnRoom) {
  446. if (killingBlow.gameover == undefined) {
  447. if (player.prefs.prey) {
  448. deaths.push("Digested by " + currentFoe.description("a") + " at " + renderTime(time) + " on day " + date);
  449. } else {
  450. deaths.push("Defeated by " + currentFoe.description("a") + " at " + renderTime(time) + " on day " + date);
  451. }
  452. } else {
  453. deaths.push(killingBlow.gameover() + " at " + renderTime(time) + " on day " + date);
  454. }
  455. moveTo(respawnRoom,"You drift through space and time...");
  456. player.clear();
  457. player.stomach.contents = [];
  458. player.bowels.contents = [];
  459. player.bowels.waste = 0;
  460. player.bowels.digested = [];
  461. player.womb.contents = [];
  462. player.womb.waste = 0;
  463. player.womb.digested = [];
  464. player.balls.contents = [];
  465. player.balls.waste = 0;
  466. player.balls.digested = [];
  467. player.breasts.contents = [];
  468. player.breasts.waste = 0;
  469. player.breasts.digested = [];
  470. advanceTime(Math.floor(86400 / 2 * (Math.random() * 0.5 - 0.25 + 1)));
  471. changeMode("explore");
  472. player.health = 100;
  473. update(["You wake back up in your bed."]);
  474. }
  475. function startCombat(opponent) {
  476. currentFoe = opponent;
  477. changeMode("combat");
  478. update(opponent.startCombat(player).concat([newline]));
  479. }
  480. function attackClicked(index) {
  481. if (noLog)
  482. clearScreen();
  483. update(playerAttacks[index].attack(currentFoe).concat([newline]));
  484. if (currentFoe.health <= 0) {
  485. currentFoe.defeated();
  486. } else if (mode == "combat") {
  487. let attack = pick(filterPriority(filterValid(currentFoe.attacks, currentFoe, player)), currentFoe, player);
  488. if (attack == null) {
  489. attack = currentFoe.backupAttack;
  490. }
  491. update(attack.attackPlayer(player).concat([newline]));
  492. if (player.health <= -100) {
  493. killingBlow = attack;
  494. if (currentFoe.finishCombat != undefined)
  495. update(currentFoe.finishCombat().concat([newline]));
  496. update(["You die..."]);
  497. respawn(respawnRoom);
  498. } else if (player.health <= 0) {
  499. update(["You're too weak to do anything..."]);
  500. if (player.prefs.prey) {
  501. // nada
  502. } else {
  503. killingBlow = attack;
  504. update(["You die..."]);
  505. respawn(respawnRoom);
  506. }
  507. }
  508. if (currentFoe.status != undefined) {
  509. let status = currentFoe.status();
  510. if (status.length > 0)
  511. update(status.concat([newline]));
  512. }
  513. }
  514. }
  515. function attackHovered(index) {
  516. document.getElementById("combat-desc").innerHTML = playerAttacks[index].desc;
  517. }
  518. function struggleClicked(index) {
  519. if (noLog)
  520. clearScreen();
  521. let struggle = struggles[index];
  522. let result = struggle.struggle(player);
  523. update(result.lines.concat([newline]));
  524. if (result.escape == "stay") {
  525. changeMode("combat");
  526. } else if (result.escape == "escape") {
  527. changeMode("explore");
  528. } else {
  529. let digest = pick(filterValid(currentFoe.digests, currentFoe, player), currentFoe, player);
  530. if (digest == null) {
  531. digest = currentFoe.backupDigest;
  532. }
  533. update(digest.digest(player).concat([newline]));
  534. if (player.health <= -100) {
  535. killingBlow = digest;
  536. update(currentFoe.finishDigest().concat([newline]));
  537. respawn(respawnRoom);
  538. }
  539. }
  540. }
  541. function struggleHovered(index) {
  542. document.getElementById("eaten-desc").innerHTML = currentFoe.struggles[index].desc;
  543. }
  544. function startDialog(dialog) {
  545. if (noLog)
  546. clearScreen();
  547. currentDialog = dialog;
  548. changeMode("dialog");
  549. update(currentDialog.text.concat([newline]));
  550. currentDialog.visit();
  551. updateDisplay();
  552. }
  553. function dialogClicked(index) {
  554. currentDialog = currentDialog.choices[index].node;
  555. update(currentDialog.text.concat([newline]));
  556. currentDialog.visit();
  557. if (currentDialog.choices.length == 0 && mode == "dialog") {
  558. changeMode("explore");
  559. updateDisplay();
  560. }
  561. }
  562. function loadDialog() {
  563. dialogButtons = Array.from( document.querySelectorAll(".dialog-button"));
  564. for (let i = 0; i < dialogButtons.length; i++) {
  565. dialogButtons[i].addEventListener("click", function() { dialogClicked(i); });
  566. }
  567. }
  568. function loadCompass() {
  569. dirButtons[NORTH_WEST] = document.getElementById("compass-north-west");
  570. dirButtons[NORTH_WEST].addEventListener("click", function() {
  571. move(NORTH_WEST);
  572. });
  573. dirButtons[NORTH] = document.getElementById("compass-north");
  574. dirButtons[NORTH].addEventListener("click", function() {
  575. move(NORTH);
  576. });
  577. dirButtons[NORTH_EAST] = document.getElementById("compass-north-east");
  578. dirButtons[NORTH_EAST].addEventListener("click", function() {
  579. move(NORTH_EAST);
  580. });
  581. dirButtons[WEST] = document.getElementById("compass-west");
  582. dirButtons[WEST].addEventListener("click", function() {
  583. move(WEST);
  584. });
  585. dirButtons[EAST] = document.getElementById("compass-east");
  586. dirButtons[EAST].addEventListener("click", function() {
  587. move(EAST);
  588. });
  589. dirButtons[SOUTH_WEST] = document.getElementById("compass-south-west");
  590. dirButtons[SOUTH_WEST].addEventListener("click", function() {
  591. move(SOUTH_WEST);
  592. });
  593. dirButtons[SOUTH] = document.getElementById("compass-south");
  594. dirButtons[SOUTH].addEventListener("click", function() {
  595. move(SOUTH);
  596. });
  597. dirButtons[SOUTH_EAST] = document.getElementById("compass-south-east");
  598. dirButtons[SOUTH_EAST].addEventListener("click", function() {
  599. move(SOUTH_EAST);
  600. });
  601. document.getElementById("compass-look").addEventListener("click", look, false);
  602. }
  603. function look() {
  604. update([currentRoom.description]);
  605. }
  606. function status() {
  607. let lines = [];
  608. lines.push("You are a " + player.species);
  609. lines.push(newline);
  610. if (player.stomach.contents.length > 0) {
  611. lines.push("Your stomach bulges with prey.");
  612. player.stomach.contents.map(function(prey) {
  613. let state = "";
  614. let healthRatio = prey.health / prey.maxHealth;
  615. if (healthRatio > 0.75) {
  616. state = "is thrashing in your gut";
  617. } else if (healthRatio > 0.5) {
  618. state = "is squirming in your belly";
  619. } else if (healthRatio > 0.25) {
  620. state = "is pressing out at your stomach walls";
  621. } else if (healthRatio > 0) {
  622. state = "is weakly squirming";
  623. } else {
  624. state = "has stopped moving";
  625. }
  626. lines.push(prey.description("A") + " " + state);
  627. });
  628. lines.push(newline);
  629. }
  630. if (player.bowels.contents.length > 0) {
  631. lines.push("Your bowels churn with prey.");
  632. player.bowels.contents.map(function(prey) {
  633. let state = "";
  634. let healthRatio = prey.health / prey.maxHealth;
  635. if (healthRatio > 0.75) {
  636. state = "is writhing in your bowels";
  637. } else if (healthRatio > 0.5) {
  638. state = "is struggling against your intestines";
  639. } else if (healthRatio > 0.25) {
  640. state = "is bulging out of your lower belly";
  641. } else if (healthRatio > 0) {
  642. state = "is squirming weakly, slipping deeper and deeper";
  643. } else {
  644. state = "has succumbed to your bowels";
  645. }
  646. lines.push(prey.description("A") + " " + state);
  647. });
  648. lines.push(newline);
  649. }
  650. if (player.parts.cock) {
  651. if (player.balls.contents.length > 0) {
  652. lines.push("Your balls are bulging with prey.");
  653. player.balls.contents.map(function(prey) {
  654. let state = "";
  655. let healthRatio = prey.health / prey.maxHealth;
  656. if (healthRatio > 0.75) {
  657. state = "is writhing in your sac";
  658. } else if (healthRatio > 0.5) {
  659. state = "is struggling in a pool of cum";
  660. } else if (healthRatio > 0.25) {
  661. state = "is starting to turn soft";
  662. } else if (healthRatio > 0) {
  663. state = "is barely visible anymore";
  664. } else {
  665. state = "has succumbed to your balls";
  666. }
  667. lines.push(prey.description("A") + " " + state);
  668. });
  669. lines.push(newline);
  670. } else {
  671. if (player.balls.waste > 0) {
  672. lines.push("Your balls are heavy with cum.");
  673. lines.push(newline);
  674. }
  675. }
  676. }
  677. if (player.parts.unbirth) {
  678. if (player.womb.contents.length > 0) {
  679. lines.push("Your slit drips, hinting at prey trapped within.");
  680. player.womb.contents.map(function(prey) {
  681. let state = "";
  682. let healthRatio = prey.health / prey.maxHealth;
  683. if (healthRatio > 0.75) {
  684. state = "is thrashing in your womb";
  685. } else if (healthRatio > 0.5) {
  686. state = "is pressing out inside your lower belly";
  687. } else if (healthRatio > 0.25) {
  688. state = "is still trying to escape";
  689. } else if (healthRatio > 0) {
  690. state = "is barely moving";
  691. } else {
  692. state = "is dissolving into femcum";
  693. }
  694. lines.push(prey.description("A") + " " + state);
  695. });
  696. lines.push(newline);
  697. } else {
  698. if (player.womb.waste > 0) {
  699. lines.push("Your slit drips, holding back a tide of femcum.");
  700. lines.push(newline);
  701. }
  702. }
  703. }
  704. if (player.parts.breast) {
  705. if (player.breasts.contents.length > 0) {
  706. lines.push("Your breasts are bulging with prey.");
  707. player.breasts.contents.map(function(prey) {
  708. let state = "";
  709. let healthRatio = prey.health / prey.maxHealth;
  710. if (healthRatio > 0.75) {
  711. state = "is struggling to escape";
  712. } else if (healthRatio > 0.5) {
  713. state = "is putting up a fight";
  714. } else if (healthRatio > 0.25) {
  715. state = "is starting to weaken";
  716. } else if (healthRatio > 0) {
  717. state = "is struggling to keep their head free of your milk";
  718. } else {
  719. state = "has succumbed, swiftly melting into milk";
  720. }
  721. lines.push(prey.description("A") + " " + state);
  722. });
  723. lines.push(newline);
  724. } else {
  725. if (player.breasts.waste > 0) {
  726. lines.push("Your breasts slosh with milk.");
  727. lines.push(newline);
  728. }
  729. }
  730. }
  731. update(lines);
  732. }
  733. let toSave = ["str","dex","con","name","species","health","stamina"];
  734. function saveGame() {
  735. let save = {};
  736. save.player = {};
  737. save.player.str = player.str;
  738. save.player.dex = player.dex;
  739. save.player.con = player.con;
  740. save.player.name = player.name;
  741. save.player.species = player.species;
  742. save.player.health = player.health;
  743. save.player.health = player.stamina;
  744. save.prefs = JSON.stringify(player.prefs);
  745. save.position = currentRoom.name;
  746. save.date = date;
  747. save.time = time;
  748. save.deaths = deaths;
  749. let stringified = JSON.stringify(save);
  750. window.localStorage.setItem("save", stringified);
  751. }
  752. function loadGame() {
  753. changeMode("explore");
  754. let save = JSON.parse(window.localStorage.getItem("save"));
  755. let playerSave = save.player;
  756. for (let key in playerSave) {
  757. if (playerSave.hasOwnProperty(key)) {
  758. player[key] = playerSave[key];
  759. }
  760. }
  761. player.prefs = JSON.parse(save.prefs);
  762. deaths = save.deaths;
  763. date = save.date;
  764. time = save.time;
  765. clearScreen();
  766. moveToByName(save.position, "");
  767. }
  768. // wow polyfills
  769. if (![].includes) {
  770. Array.prototype.includes = function(searchElement /*, fromIndex*/ ) {
  771. 'use strict';
  772. var O = Object(this);
  773. var len = parseInt(O.length) || 0;
  774. if (len === 0) {
  775. return false;
  776. }
  777. var n = parseInt(arguments[1]) || 0;
  778. var k;
  779. if (n >= 0) {
  780. k = n;
  781. } else {
  782. k = len + n;
  783. if (k < 0) {k = 0;}
  784. }
  785. var currentElement;
  786. while (k < len) {
  787. currentElement = O[k];
  788. if (searchElement === currentElement ||
  789. (searchElement !== searchElement && currentElement !== currentElement)) {
  790. return true;
  791. }
  792. k++;
  793. }
  794. return false;
  795. };
  796. }