munch
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.
 
 
 

966 line
28 KiB

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