munch
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 

1091 рядки
32 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; //time is calculated in seconds
  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 logPadLine = undefined;
  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. let actionButtons = document.querySelector("#actions");
  98. actionButtons.innerHTML = "";
  99. for (let i = 0; i < actions.length; i++) {
  100. let button = document.createElement("button");
  101. button.innerHTML = actions[i].name;
  102. button.classList.add("active-button");
  103. button.classList.add("action-button");
  104. button.addEventListener("click", function() { actions[i].action(); });
  105. actionButtons.appendChild(button);
  106. }
  107. }
  108. function updateExplore() {
  109. updateExploreCompass();
  110. updateExploreActions();
  111. }
  112. function updateEaten() {
  113. let list = document.getElementById("eaten");
  114. while(list.firstChild) {
  115. list.removeChild(list.firstChild);
  116. }
  117. if (player.health > 0)
  118. struggles = filterValid(currentFoe.struggles, currentFoe, player);
  119. else
  120. struggles = [submit(currentFoe)];
  121. for (let i = 0; i < struggles.length; i++) {
  122. let li = document.createElement("li");
  123. let button = document.createElement("button");
  124. button.classList.add("eaten-button");
  125. button.innerHTML = struggles[i].name;
  126. button.addEventListener("click", function() { struggleClicked(i); } );
  127. button.addEventListener("mouseover", function() { struggleHovered(i); } );
  128. button.addEventListener("mouseout", function() { document.getElementById("eaten-desc").innerHTML = ""; } );
  129. li.appendChild(button);
  130. list.appendChild(li);
  131. }
  132. }
  133. function updateCombat() {
  134. let list = document.getElementById("combat");
  135. while(list.firstChild) {
  136. list.removeChild(list.firstChild);
  137. }
  138. if (player.health > 0)
  139. if (currentFoe.playerAttacks == undefined)
  140. playerAttacks = filterValid(player.attacks, player, currentFoe);
  141. else
  142. playerAttacks = filterValid(currentFoe.playerAttacks.map(attack => attack(player)), player, currentFoe);
  143. else
  144. playerAttacks = [pass(player)];
  145. if (playerAttacks.length == 0)
  146. playerAttacks = [player.backupAttack];
  147. for (let i = 0; i < playerAttacks.length; i++) {
  148. let li = document.createElement("li");
  149. let button = document.createElement("button");
  150. button.classList.add("combat-button");
  151. button.innerHTML = playerAttacks[i].name;
  152. button.addEventListener("click", function() { attackClicked(i); } );
  153. button.addEventListener("mouseover", function() { attackHovered(i); } );
  154. button.addEventListener("mouseout", function() { document.getElementById("combat-desc").innerHTML = ""; } );
  155. li.appendChild(button);
  156. list.appendChild(li);
  157. }
  158. document.getElementById("stat-foe-name").innerHTML = "Name: " + currentFoe.name;
  159. document.getElementById("stat-foe-health").innerHTML = "Health: " + currentFoe.health + "/" + currentFoe.maxHealth;
  160. document.getElementById("stat-foe-stamina").innerHTML = "Stamina: " + currentFoe.stamina + "/" + currentFoe.maxStamina;
  161. document.getElementById("stat-foe-str").innerHTML = "Str: " + currentFoe.str;
  162. document.getElementById("stat-foe-dex").innerHTML = "Dex: " + currentFoe.dex;
  163. document.getElementById("stat-foe-con").innerHTML = "Con: " + currentFoe.con;
  164. }
  165. function updateDialog() {
  166. let list = document.getElementById("dialog");
  167. while(list.firstChild) {
  168. list.removeChild(list.firstChild);
  169. }
  170. for (let i = 0; i < currentDialog.choices.length; i++) {
  171. let activated = currentDialog.choices[i].node.requirements == undefined || currentDialog.choices[i].node.requirements.reduce((result, test) => result && test(player, currentFoe), true);
  172. let li = document.createElement("li");
  173. let button = document.createElement("button");
  174. button.classList.add("dialog-button");
  175. button.innerHTML = currentDialog.choices[i].text;
  176. button.addEventListener("click", function() { dialogClicked(i); });
  177. if (!activated) {
  178. button.classList.add("disabled-button");
  179. button.disabled = true;
  180. }
  181. li.appendChild(button);
  182. list.appendChild(li);
  183. }
  184. }
  185. function updateDisplay() {
  186. document.querySelectorAll(".selector").forEach(function (x) {
  187. x.style.display = "none";
  188. });
  189. switch(mode) {
  190. case "explore":
  191. document.getElementById("selector-explore").style.display = "flex";
  192. updateExplore();
  193. break;
  194. case "combat":
  195. document.getElementById("selector-combat").style.display = "flex";
  196. updateCombat();
  197. break;
  198. case "dialog":
  199. document.getElementById("selector-dialog").style.display = "flex";
  200. updateDialog();
  201. break;
  202. case "eaten":
  203. document.getElementById("selector-eaten").style.display = "flex";
  204. updateEaten();
  205. break;
  206. }
  207. document.getElementById("time").innerHTML = "Time: " + renderTime(time);
  208. document.getElementById("date").innerHTML = "Day " + date;
  209. document.getElementById("stat-name").innerHTML = "Name: " + player.name;
  210. document.getElementById("stat-health").innerHTML = "Health: " + round(player.health,0) + "/" + round(player.maxHealth,0);
  211. document.getElementById("stat-cash").innerHTML = "Cash: $" + round(player.cash,0);
  212. document.getElementById("stat-stamina").innerHTML = "Stamina: " + round(player.stamina,0) + "/" + round(player.maxStamina,0);
  213. document.getElementById("stat-str").innerHTML = "Str: " + player.str;
  214. document.getElementById("stat-dex").innerHTML = "Dex: " + player.dex;
  215. document.getElementById("stat-con").innerHTML = "Con: " + player.con;
  216. document.getElementById("stat-arousal").innerHTML = "Arousal: " + round(player.arousal,0) + "/" + round(player.arousalLimit(),0);
  217. document.getElementById("stat-stomach").innerHTML = "Stomach: " + round(player.stomach.fullness(),0) + "/" + round(player.stomach.capacity, 0);
  218. if (player.prefs.pred.anal || player.prefs.scat)
  219. document.getElementById("stat-bowels").innerHTML = "Bowels: " + round(player.bowels.fullness(),0) + "/" + round(player.bowels.capacity, 0);
  220. else
  221. document.getElementById("stat-bowels").innerHTML = "";
  222. if (player.prefs.pred.cock)
  223. document.getElementById("stat-balls").innerHTML = "Balls: " + round(player.balls.fullness(),0) + "/" + round(player.balls.capacity, 0);
  224. else
  225. document.getElementById("stat-balls").innerHTML = "";
  226. if (player.prefs.pred.unbirth)
  227. document.getElementById("stat-womb").innerHTML = "Womb: " + round(player.womb.fullness(),0) + "/" + round(player.womb.capacity, 0);
  228. else
  229. document.getElementById("stat-womb").innerHTML = "";
  230. if (player.prefs.pred.breast)
  231. document.getElementById("stat-breasts").innerHTML = "Breasts: " + round(player.breasts.fullness(),0) + "/" + round(player.breasts.capacity, 0);
  232. else
  233. document.getElementById("stat-breasts").innerHTML = "";
  234. }
  235. function advanceTimeTo(newTime, conscious=true) {
  236. advanceTime((86400 + newTime - time) % 86400, conscious);
  237. }
  238. function advanceTime(amount, conscious=true) {
  239. time = (time + amount);
  240. date += Math.floor(time / 86400);
  241. time = time % 86400;
  242. player.restoreHealth(amount);
  243. player.restoreStamina(amount);
  244. update(player.stomach.digest(amount));
  245. update(player.bowels.digest(amount));
  246. update(player.balls.digest(amount));
  247. update(player.womb.digest(amount));
  248. update(player.breasts.digest(amount));
  249. stretchOrgans(amount);
  250. if (conscious) {
  251. update(player.buildArousal(amount));
  252. }
  253. }
  254. function renderTime(time) {
  255. let suffix = (time < 43200) ? "AM" : "PM";
  256. let hour = Math.floor((time % 43200) / 3600);
  257. if (hour == 0)
  258. hour = 12;
  259. let minute = Math.floor(time / 60) % 60;
  260. if (minute < 9)
  261. minute = "0" + minute;
  262. return hour + ":" + minute + " " + suffix;
  263. }
  264. function move(direction) {
  265. if (noLog)
  266. clearScreen();
  267. else
  268. clearLogBreak();
  269. let target = currentRoom.exits[direction];
  270. if (target == null) {
  271. alert("Tried to move to an empty room!");
  272. return;
  273. }
  274. moveTo(target,currentRoom.exitDescs[direction]);
  275. }
  276. function updateActions() {
  277. actions = [];
  278. currentRoom.objects.forEach(function (object) {
  279. object.actions.forEach(function (action) {
  280. if (action.conditions == undefined || action.conditions.reduce((result, cond) => result && cond(player), true))
  281. actions.push(action);
  282. });
  283. });
  284. }
  285. function moveToByName(roomName, desc="You go places lol", loading=false) {
  286. moveTo(world[roomName], desc, loading);
  287. }
  288. function moveTo(room,desc="You go places lol", loading=false) {
  289. currentRoom = room;
  290. if (!loading)
  291. advanceTime(30);
  292. update([desc,newline]);
  293. currentRoom.visit();
  294. updateDisplay();
  295. }
  296. function next_step(stage) {
  297. document.querySelector("#character-step-" + (stage - 1)).style.display = "none";
  298. document.querySelector("#character-step-" + stage).style.display = "block";
  299. }
  300. window.addEventListener('load', function(event) {
  301. document.getElementById("character-step-1-next").addEventListener("click", function() { next_step(2); });
  302. document.getElementById("character-load").addEventListener("click", startLoaded, false);
  303. document.getElementById("start-button").addEventListener("click", start, false);
  304. });
  305. function start() {
  306. applySettings(generateSettings());
  307. transformVorePrefs(player.prefs);
  308. document.getElementById("create").style.display = "none";
  309. document.getElementById("game").style.display = "block";
  310. document.getElementById("stat-button-status").addEventListener("click", status, false);
  311. document.getElementById("log-button").addEventListener("click", toggleLog, false);
  312. document.getElementById("load-button").addEventListener("click", loadGameButton, false);
  313. document.getElementById("save-button").addEventListener("click", saveGameButton, false);
  314. loadCompass();
  315. loadDialog();
  316. setupStrechableOrgans();
  317. world = createWorld();
  318. currentRoom = world["Bedroom"];
  319. respawnRoom = currentRoom;
  320. update(new Array(50).fill(newline));
  321. update(["Welcome to Feast."]);
  322. moveTo(currentRoom,"");
  323. updateDisplay();
  324. }
  325. // copied from Stroll LUL
  326. function generateSettings() {
  327. let form = document.forms.namedItem("character-form");
  328. let settings = {};
  329. for (let i=0; i<form.length; i++) {
  330. let value = form[i].value == "" ? form[i].placeholder : form[i].value;
  331. if (form[i].type == "text")
  332. if (form[i].value == "")
  333. settings[form[i].name] = form[i].placeholder;
  334. else
  335. settings[form[i].name] = value;
  336. else if (form[i].type == "number")
  337. settings[form[i].name] = parseFloat(value);
  338. else if (form[i].type == "checkbox") {
  339. settings[form[i].name] = form[i].checked;
  340. } else if (form[i].type == "radio") {
  341. let name = form[i].name;
  342. if (form[i].checked) {
  343. if (form[i].value == "true")
  344. settings[name] = true;
  345. else if (form[i].value == "false")
  346. settings[name] = false;
  347. else
  348. settings[name] = form[i].value;
  349. }
  350. } else if (form[i].type == "select-one") {
  351. settings[form[i].name] = form[i][form[i].selectedIndex].value;
  352. }
  353. }
  354. return settings;
  355. }
  356. function applySettings(settings) {
  357. player.name = settings.name;
  358. player.species = settings.species;
  359. for (let key in settings) {
  360. if (settings.hasOwnProperty(key)) {
  361. if (key.match(/prefs/)) {
  362. let tokens = key.split("-");
  363. let pref = player.prefs;
  364. // construct missing child dictionaries if needed :)
  365. pref = tokens.slice(1,-1).reduce(function(pref, key) {
  366. if (pref[key] == undefined)
  367. pref[key] = {};
  368. return pref[key];
  369. }, pref);
  370. pref[tokens.slice(-1)[0]] = settings[key];
  371. } else if(key.match(/parts/)) {
  372. let tokens = key.split("-");
  373. player.parts[tokens[1]] = settings[key] >= 1;
  374. if (player.prefs.pred == undefined)
  375. player.prefs.pred = {};
  376. player.prefs.pred[tokens[1]] = settings[key] >= 2;
  377. }
  378. }
  379. }
  380. }
  381. // turn things like "1" into a number
  382. function transformVorePrefs(prefs) {
  383. let prey = false;
  384. for (let key in prefs.vore) {
  385. if (prefs.vore.hasOwnProperty(key)) {
  386. switch(prefs.vore[key]) {
  387. case "0": prefs.vore[key] = 0; break;
  388. case "1": prefs.vore[key] = 0.5; prey = true; break;
  389. case "2": prefs.vore[key] = 1; prey = true; break;
  390. case "3": prefs.vore[key] = 2; prey = true; break;
  391. }
  392. }
  393. }
  394. prefs.prey = prey;
  395. return prefs;
  396. }
  397. function saveSettings() {
  398. window.localStorage.setItem("settings", JSON.stringify(generateSettings()));
  399. }
  400. function retrieveSettings() {
  401. return JSON.parse(window.localStorage.getItem("settings"));
  402. }
  403. function clearScreen() {
  404. let log = document.getElementById("log");
  405. let child = log.firstChild;
  406. while (child != null) {
  407. log.removeChild(child);
  408. child = log.firstChild;
  409. }
  410. }
  411. function update(lines=[]) {
  412. let log = document.getElementById("log");
  413. for (let i=0; i<lines.length; i++) {
  414. let div = document.createElement("div");
  415. div.innerHTML = lines[i];
  416. if (logPadLine === undefined && !noLog) {
  417. logPadLine = div;
  418. logPadLine.classList.add("log-entry-padded");
  419. }
  420. log.appendChild(div);
  421. }
  422. log.scrollTop = log.scrollHeight;
  423. updateDisplay();
  424. }
  425. function clearLogBreak() {
  426. if (logPadLine !== undefined) {
  427. logPadLine.classList.remove("log-entry-padded");
  428. logPadLine = undefined;
  429. }
  430. }
  431. function changeMode(newMode) {
  432. mode = newMode;
  433. let body = document.querySelector("body");
  434. document.getElementById("foe-stats").style.display = "none";
  435. document.getElementById("options").style.display = "block";
  436. body.className = "";
  437. switch(mode) {
  438. case "explore":
  439. case "dialog":
  440. body.classList.add("explore");
  441. break;
  442. case "combat":
  443. body.classList.add("combat");
  444. document.getElementById("foe-stats").style.display = "block";
  445. document.getElementById("options").style.display = "none";
  446. break;
  447. case "eaten":
  448. body.classList.add("eaten");
  449. document.getElementById("options").style.display = "none";
  450. document.getElementById("foe-stats").style.display = "block";
  451. break;
  452. }
  453. updateDisplay();
  454. }
  455. // make it look like eaten mode, even when in combat
  456. function changeBackground(newMode) {
  457. let body = document.querySelector("body");
  458. document.getElementById("foe-stats").style.display = "none";
  459. body.className = "";
  460. switch(newMode) {
  461. case "explore":
  462. case "dialog":
  463. body.classList.add("explore");
  464. break;
  465. case "combat":
  466. body.classList.add("combat");
  467. document.getElementById("foe-stats").style.display = "block";
  468. break;
  469. case "eaten":
  470. body.classList.add("eaten");
  471. document.getElementById("foe-stats").style.display = "block";
  472. break;
  473. }
  474. }
  475. function respawn(respawnRoom) {
  476. if (killingBlow.gameover == undefined) {
  477. if (player.prefs.prey) {
  478. deaths.push("Digested by " + currentFoe.description("a") + " at " + renderTime(time) + " on day " + date);
  479. } else {
  480. deaths.push("Defeated by " + currentFoe.description("a") + " at " + renderTime(time) + " on day " + date);
  481. }
  482. } else {
  483. deaths.push(killingBlow.gameover() + " at " + renderTime(time) + " on day " + date);
  484. }
  485. moveTo(respawnRoom,"You drift through space and time...");
  486. player.clear();
  487. player.stomach.contents = [];
  488. player.bowels.contents = [];
  489. player.bowels.waste = 0;
  490. player.bowels.digested = [];
  491. player.womb.contents = [];
  492. player.womb.waste = 0;
  493. player.womb.digested = [];
  494. player.balls.contents = [];
  495. player.balls.waste = 0;
  496. player.balls.digested = [];
  497. player.breasts.contents = [];
  498. player.breasts.waste = 0;
  499. player.breasts.digested = [];
  500. advanceTime(Math.floor(86400 / 2 * (Math.random() * 0.5 - 0.25 + 1)), false);
  501. changeMode("explore");
  502. player.health = 100;
  503. update(["You wake back up in your bed."]);
  504. }
  505. function startCombat(opponent) {
  506. currentFoe = opponent;
  507. changeMode("combat");
  508. update(opponent.startCombat(player).concat([newline]));
  509. }
  510. function attackClicked(index) {
  511. if (noLog)
  512. clearScreen();
  513. else
  514. clearLogBreak();
  515. update(playerAttacks[index].attack(currentFoe).concat([newline]));
  516. if (currentFoe.health <= 0) {
  517. currentFoe.defeated();
  518. } else if (mode == "combat") {
  519. let attack = pick(filterPriority(filterValid(currentFoe.attacks, currentFoe, player)), currentFoe, player);
  520. if (attack == null) {
  521. attack = currentFoe.backupAttack;
  522. }
  523. update(attack.attackPlayer(player).concat([newline]));
  524. if (player.health <= -100) {
  525. killingBlow = attack;
  526. if (currentFoe.finishCombat != undefined)
  527. update(currentFoe.finishCombat().concat([newline]));
  528. update(["You die..."]);
  529. respawn(respawnRoom);
  530. } else if (player.health <= 0) {
  531. update(["You're too weak to do anything..."]);
  532. if (player.prefs.prey) {
  533. // nada
  534. } else {
  535. killingBlow = attack;
  536. update(["You die..."]);
  537. respawn(respawnRoom);
  538. }
  539. }
  540. if (currentFoe.status != undefined) {
  541. let status = currentFoe.status();
  542. if (status.length > 0)
  543. update(status.concat([newline]));
  544. }
  545. }
  546. }
  547. function attackHovered(index) {
  548. document.getElementById("combat-desc").innerHTML = playerAttacks[index].desc;
  549. }
  550. function struggleClicked(index) {
  551. if (noLog)
  552. clearScreen();
  553. else
  554. clearLogBreak();
  555. let struggle = struggles[index];
  556. let result = struggle.struggle(player);
  557. update(result.lines.concat([newline]));
  558. if (result.escape == "stay") {
  559. changeMode("combat");
  560. } else if (result.escape == "escape") {
  561. changeMode("explore");
  562. } else {
  563. let digest = pick(filterValid(currentFoe.digests, currentFoe, player), currentFoe, player);
  564. if (digest == null) {
  565. digest = currentFoe.backupDigest;
  566. }
  567. update(digest.digest(player).concat([newline]));
  568. if (player.health <= -100) {
  569. killingBlow = digest;
  570. update(currentFoe.finishDigest().concat([newline]));
  571. respawn(respawnRoom);
  572. }
  573. }
  574. }
  575. function struggleHovered(index) {
  576. document.getElementById("eaten-desc").innerHTML = currentFoe.struggles[index].desc;
  577. }
  578. function startDialog(dialog) {
  579. if (noLog)
  580. clearScreen();
  581. else
  582. clearLogBreak();
  583. currentDialog = dialog;
  584. changeMode("dialog");
  585. update(currentDialog.text.concat([newline]));
  586. currentDialog.visit();
  587. updateDisplay();
  588. }
  589. function dialogClicked(index) {
  590. currentDialog = currentDialog.choices[index].node;
  591. update(currentDialog.text.concat([newline]));
  592. currentDialog.visit();
  593. if (currentDialog.choices.length == 0 && mode == "dialog") {
  594. changeMode("explore");
  595. updateDisplay();
  596. }
  597. }
  598. function loadDialog() {
  599. dialogButtons = Array.from( document.querySelectorAll(".dialog-button"));
  600. for (let i = 0; i < dialogButtons.length; i++) {
  601. dialogButtons[i].addEventListener("click", function() { dialogClicked(i); });
  602. }
  603. }
  604. function loadCompass() {
  605. dirButtons[NORTH_WEST] = document.getElementById("compass-north-west");
  606. dirButtons[NORTH_WEST].addEventListener("click", function() {
  607. move(NORTH_WEST);
  608. });
  609. dirButtons[NORTH] = document.getElementById("compass-north");
  610. dirButtons[NORTH].addEventListener("click", function() {
  611. move(NORTH);
  612. });
  613. dirButtons[NORTH_EAST] = document.getElementById("compass-north-east");
  614. dirButtons[NORTH_EAST].addEventListener("click", function() {
  615. move(NORTH_EAST);
  616. });
  617. dirButtons[WEST] = document.getElementById("compass-west");
  618. dirButtons[WEST].addEventListener("click", function() {
  619. move(WEST);
  620. });
  621. dirButtons[EAST] = document.getElementById("compass-east");
  622. dirButtons[EAST].addEventListener("click", function() {
  623. move(EAST);
  624. });
  625. dirButtons[SOUTH_WEST] = document.getElementById("compass-south-west");
  626. dirButtons[SOUTH_WEST].addEventListener("click", function() {
  627. move(SOUTH_WEST);
  628. });
  629. dirButtons[SOUTH] = document.getElementById("compass-south");
  630. dirButtons[SOUTH].addEventListener("click", function() {
  631. move(SOUTH);
  632. });
  633. dirButtons[SOUTH_EAST] = document.getElementById("compass-south-east");
  634. dirButtons[SOUTH_EAST].addEventListener("click", function() {
  635. move(SOUTH_EAST);
  636. });
  637. document.getElementById("compass-look").addEventListener("click", look, false);
  638. }
  639. function look() {
  640. update([currentRoom.description]);
  641. }
  642. function status() {
  643. let lines = [];
  644. lines.push("You are a " + player.species);
  645. lines.push(newline);
  646. if (player.stomach.contents.length > 0) {
  647. lines.push("Your stomach bulges with prey.");
  648. player.stomach.contents.map(function(prey) {
  649. let state = "";
  650. let healthRatio = prey.health / prey.maxHealth;
  651. if (healthRatio > 0.75) {
  652. state = "is thrashing in your gut";
  653. } else if (healthRatio > 0.5) {
  654. state = "is squirming in your belly";
  655. } else if (healthRatio > 0.25) {
  656. state = "is pressing out at your stomach walls";
  657. } else if (healthRatio > 0) {
  658. state = "is weakly squirming";
  659. } else {
  660. state = "has stopped moving";
  661. }
  662. lines.push(prey.description("A") + " " + state);
  663. });
  664. lines.push(newline);
  665. }
  666. if (player.bowels.contents.length > 0) {
  667. lines.push("Your bowels churn with prey.");
  668. player.bowels.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 bowels";
  673. } else if (healthRatio > 0.5) {
  674. state = "is struggling against your intestines";
  675. } else if (healthRatio > 0.25) {
  676. state = "is bulging out of your lower belly";
  677. } else if (healthRatio > 0) {
  678. state = "is squirming weakly, slipping deeper and deeper";
  679. } else {
  680. state = "has succumbed to your bowels";
  681. }
  682. lines.push(prey.description("A") + " " + state);
  683. });
  684. lines.push(newline);
  685. }
  686. if (player.parts.cock) {
  687. if (player.balls.contents.length > 0) {
  688. lines.push("Your balls are bulging with prey.");
  689. player.balls.contents.map(function(prey) {
  690. let state = "";
  691. let healthRatio = prey.health / prey.maxHealth;
  692. if (healthRatio > 0.75) {
  693. state = "is writhing in your sac";
  694. } else if (healthRatio > 0.5) {
  695. state = "is struggling in a pool of cum";
  696. } else if (healthRatio > 0.25) {
  697. state = "is starting to turn soft";
  698. } else if (healthRatio > 0) {
  699. state = "is barely visible anymore";
  700. } else {
  701. state = "has succumbed to your balls";
  702. }
  703. lines.push(prey.description("A") + " " + state);
  704. });
  705. lines.push(newline);
  706. } else {
  707. if (player.balls.waste > 0) {
  708. lines.push("Your balls are heavy with cum.");
  709. lines.push(newline);
  710. }
  711. }
  712. }
  713. if (player.parts.unbirth) {
  714. if (player.womb.contents.length > 0) {
  715. lines.push("Your slit drips, hinting at prey trapped within.");
  716. player.womb.contents.map(function(prey) {
  717. let state = "";
  718. let healthRatio = prey.health / prey.maxHealth;
  719. if (healthRatio > 0.75) {
  720. state = "is thrashing in your womb";
  721. } else if (healthRatio > 0.5) {
  722. state = "is pressing out inside your lower belly";
  723. } else if (healthRatio > 0.25) {
  724. state = "is still trying to escape";
  725. } else if (healthRatio > 0) {
  726. state = "is barely moving";
  727. } else {
  728. state = "is dissolving into femcum";
  729. }
  730. lines.push(prey.description("A") + " " + state);
  731. });
  732. lines.push(newline);
  733. } else {
  734. if (player.womb.waste > 0) {
  735. lines.push("Your slit drips, holding back a tide of femcum.");
  736. lines.push(newline);
  737. }
  738. }
  739. }
  740. if (player.parts.breast) {
  741. if (player.breasts.contents.length > 0) {
  742. lines.push("Your breasts are bulging with prey.");
  743. player.breasts.contents.map(function(prey) {
  744. let state = "";
  745. let healthRatio = prey.health / prey.maxHealth;
  746. if (healthRatio > 0.75) {
  747. state = "is struggling to escape";
  748. } else if (healthRatio > 0.5) {
  749. state = "is putting up a fight";
  750. } else if (healthRatio > 0.25) {
  751. state = "is starting to weaken";
  752. } else if (healthRatio > 0) {
  753. state = "is struggling to keep their head free of your milk";
  754. } else {
  755. state = "has succumbed, swiftly melting into milk";
  756. }
  757. lines.push(prey.description("A") + " " + state);
  758. });
  759. lines.push(newline);
  760. } else {
  761. if (player.breasts.waste > 0) {
  762. lines.push("Your breasts slosh with milk.");
  763. lines.push(newline);
  764. }
  765. }
  766. }
  767. update(lines);
  768. }
  769. function checkOverfill(organ,returnValue=false, returnPercent=false){
  770. let percentFilled = (round(player[organ].fullness(),0) / player[organ].capacity);
  771. if (returnValue == false){
  772. if (percentFilled > 1){
  773. return (true);
  774. }else{
  775. return (false);
  776. }
  777. }else{
  778. if (returnPercent == true){
  779. return (round(player[organ].fullness(),0));
  780. }else{
  781. return (round(player[organ].fullness(),0) - player[organ].capacity);
  782. }
  783. }
  784. }
  785. var strechableOrgans = ["stomach","bowels","balls","womb","breasts"];
  786. function setupStrechableOrgans(){
  787. strechableOrgans = ["stomach"];
  788. if (player.prefs.pred.anal || player.prefs.scat){ //these if conditions are copied from the if statements above that define if the stat menu shows stats for a set organ
  789. strechableOrgans.push("bowels");
  790. }if (player.prefs.pred.cock){
  791. strechableOrgans.push("balls");
  792. }if (player.prefs.pred.unbirth){
  793. strechableOrgans.push("womb");
  794. }if (player.prefs.pred.breast){
  795. strechableOrgans.push("breasts");
  796. }
  797. }
  798. function stretchOrgans(time){
  799. for (i=0; i<strechableOrgans.length; i++){
  800. let organ = strechableOrgans[i];
  801. let overfillState = checkOverfill(organ, false, false);
  802. if (overfillState == true){
  803. excessMass = checkOverfill(organ,true, false);
  804. massDigested = time*player[organ].digestRate;
  805. massDigested = Math.min(excessMass, massDigested);
  806. player[organ].capacity += massDigested;
  807. }
  808. }
  809. }
  810. let toSave = ["str","dex","con","name","species","health","stamina"];
  811. function saveGame() {
  812. let save = {};
  813. save.player = {};
  814. save.player.str = player.str;
  815. save.player.dex = player.dex;
  816. save.player.con = player.con;
  817. save.player.name = player.name;
  818. save.player.species = player.species;
  819. save.player.health = player.health;
  820. save.player.health = player.stamina;
  821. save.prefs = JSON.stringify(player.prefs);
  822. save.position = currentRoom.name;
  823. save.date = date;
  824. save.time = time;
  825. save.deaths = deaths;
  826. let stringified = JSON.stringify(save);
  827. window.localStorage.setItem("save", stringified);
  828. update(["Game saved."]);
  829. updateDisplay();
  830. }
  831. function loadGame() {
  832. changeMode("explore");
  833. let save = JSON.parse(window.localStorage.getItem("save"));
  834. let playerSave = save.player;
  835. for (let key in playerSave) {
  836. if (playerSave.hasOwnProperty(key)) {
  837. player[key] = playerSave[key];
  838. }
  839. }
  840. player.prefs = JSON.parse(save.prefs);
  841. deaths = save.deaths;
  842. date = save.date;
  843. time = save.time;
  844. clearScreen();
  845. moveToByName(save.position, "");
  846. update(["Game loaded."]);
  847. updateDisplay();
  848. }
  849. function startLoaded() { //used to load the game via the main menu
  850. start();
  851. loadGame();
  852. }
  853. //these work in conjunction with buttonConfirm/buttonConfirmEnd and any functions that call them.
  854. var confirmTimer; //this is areference to the active setTimeout, only used to allow clearTimeout to know thich timeout to clear
  855. let confirmState = ""; //this records which function is asking for confirmation "" means nothing is asking for confirmation.
  856. let confirmStateText = ""; //this is where the original button text is stored when the button reads "Confirm?"
  857. function buttonConfirm(targetedButton, buttonText){ //starts a timer and requests the playter click the button again to confirm that they want to take the action
  858. if(confirmState != ""){
  859. buttonConfirmEnd();
  860. }
  861. document.getElementById([targetedButton]).innerHTML = "Confirm?"; //changes button text to "Confirm?"
  862. confirmState = targetedButton; //copies data to global variable to make sure only one button is requesting confirmation at any given time
  863. confirmStateText = buttonText; //copies data to global variable to make sure only one button is requesting confirmation at any given time
  864. confirmTimer = setTimeout(buttonConfirmEnd, 5000); //5000 is 5 seconds
  865. }
  866. function buttonConfirmEnd(){ //this resets the button once the request for confirmation has no longer active
  867. document.getElementById([confirmState]).innerHTML = [confirmStateText]; //resets text
  868. confirmState = ""; //resets confirmation state
  869. clearTimeout(confirmTimer); //keeps function from being called again if a timer is running
  870. }
  871. function saveGameButton(){//activates if the "Save Game" button is pressed
  872. let targetedButton = "save-button";
  873. if (confirmState === targetedButton){//if the confirm timer is active for this function, actually saves game
  874. buttonConfirmEnd();
  875. saveGame();
  876. }else{
  877. buttonConfirm(targetedButton, "Save Game"); //starts confirm timer for this function
  878. }
  879. }
  880. function loadGameButton(){//activates if the "Load Game" button is pressed
  881. let targetedButton = "load-button";
  882. if (confirmState === targetedButton){//if the confirm timer is active for this function, actually loads game
  883. buttonConfirmEnd();
  884. loadGame();
  885. }else{ //starts confirm timer for this function
  886. buttonConfirm(targetedButton, "Load Game");
  887. }
  888. }
  889. // wow polyfills
  890. if (![].includes) {
  891. Array.prototype.includes = function(searchElement /*, fromIndex*/ ) {
  892. 'use strict';
  893. var O = Object(this);
  894. var len = parseInt(O.length) || 0;
  895. if (len === 0) {
  896. return false;
  897. }
  898. var n = parseInt(arguments[1]) || 0;
  899. var k;
  900. if (n >= 0) {
  901. k = n;
  902. } else {
  903. k = len + n;
  904. if (k < 0) {k = 0;}
  905. }
  906. var currentElement;
  907. while (k < len) {
  908. currentElement = O[k];
  909. if (searchElement === currentElement ||
  910. (searchElement !== searchElement && currentElement !== currentElement)) {
  911. return true;
  912. }
  913. k++;
  914. }
  915. return false;
  916. };
  917. }