crunch
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 

567 lignes
16 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. function join(things) {
  19. if (things.length == 1) {
  20. return things[0].description("a");
  21. } else if (things.length == 2) {
  22. return things[0].description("a") + " and " + things[1].description("a");
  23. } else {
  24. let line = "";
  25. line = things.slice(0,-1).reduce((line, prey) => line + prey.description("a") + ", ", line);
  26. line += " and " + things[things.length-1].description("a");
  27. return line;
  28. }
  29. }
  30. function pickRandom(list) {
  31. return list[Math.floor(Math.random() * list.length)];
  32. }
  33. function pick(list, attacker, defender) {
  34. if (list.length == 0)
  35. return null;
  36. else {
  37. let sum = list.reduce((sum, choice) => choice.weight == undefined ? sum + 1 : sum + choice.weight(attacker, defender), 0);
  38. let target = Math.random() * sum;
  39. for (let i = 0; i < list.length; i++) {
  40. sum -= list[i].weight == undefined ? 1 : list[i].weight(attacker, defender);
  41. if (sum <= target) {
  42. return list[i];
  43. }
  44. }
  45. return list[list.length-1];
  46. }
  47. }
  48. function filterValid(options, attacker, defender) {
  49. let filtered = options.filter(option => option.conditions == undefined || option.conditions.reduce((result, test) => result && test(attacker, defender), true));
  50. return filtered.filter(option => option.requirements == undefined || option.requirements.reduce((result, test) => result && test(attacker, defender), true));
  51. }
  52. function filterPriority(options) {
  53. let max = options.reduce((max, option) => option.priority > max ? option.priority : max, -1000);
  54. return options.filter(option => option.priority == max);
  55. }
  56. function round(number, digits) {
  57. return Math.round(number * Math.pow(10,digits)) / Math.pow(10,digits);
  58. }
  59. function updateExploreCompass() {
  60. for (let i = 0; i < dirButtons.length; i++) {
  61. let button = dirButtons[i];
  62. button.classList.remove("active-button");
  63. button.classList.remove("inactive-button");
  64. button.classList.remove("disabled-button");
  65. if (currentRoom.exits[i] == null) {
  66. button.disabled = true;
  67. button.classList.add("inactive-button");
  68. button.innerHTML = "";
  69. } else {
  70. if (currentRoom.exits[i].conditions.reduce((result, test) => result && test(player.prefs), true)) {
  71. button.disabled = false;
  72. button.classList.add("active-button");
  73. button.innerHTML = currentRoom.exits[i].name;
  74. } else {
  75. button.disabled = true;
  76. button.classList.add("disabled-button");
  77. button.innerHTML = currentRoom.exits[i].name;
  78. }
  79. }
  80. }
  81. }
  82. function updateExploreActions() {
  83. for (let i = 0; i < actionButtons.length; i++) {
  84. if (i < actions.length) {
  85. actionButtons[i].disabled = false;
  86. actionButtons[i].innerHTML = actions[i].name;
  87. actionButtons[i].classList.remove("inactive-button");
  88. actionButtons[i].classList.add("active-button");
  89. }
  90. else {
  91. actionButtons[i].disabled = true;
  92. actionButtons[i].innerHTML = "";
  93. actionButtons[i].classList.remove("active-button");
  94. actionButtons[i].classList.add("inactive-button");
  95. }
  96. }
  97. }
  98. function updateExplore() {
  99. updateExploreCompass();
  100. updateExploreActions();
  101. }
  102. function updateEaten() {
  103. let list = document.getElementById("eaten");
  104. while(list.firstChild) {
  105. list.removeChild(list.firstChild);
  106. }
  107. struggles = filterValid(currentFoe.struggles, currentFoe, player);
  108. for (let i = 0; i < struggles.length; i++) {
  109. let li = document.createElement("li");
  110. let button = document.createElement("button");
  111. button.classList.add("eaten-button");
  112. button.innerHTML = struggles[i].name;
  113. button.addEventListener("click", function() { struggleClicked(i); } );
  114. button.addEventListener("mouseover", function() { struggleHovered(i); } );
  115. button.addEventListener("mouseout", function() { document.getElementById("eaten-desc").innerHTML = ""; } );
  116. li.appendChild(button);
  117. list.appendChild(li);
  118. }
  119. }
  120. function updateCombat() {
  121. let list = document.getElementById("combat");
  122. while(list.firstChild) {
  123. list.removeChild(list.firstChild);
  124. }
  125. playerAttacks = filterValid(player.attacks, player, currentFoe);
  126. if (playerAttacks.length == 0)
  127. playerAttacks = [player.backupAttack];
  128. for (let i = 0; i < playerAttacks.length; i++) {
  129. let li = document.createElement("li");
  130. let button = document.createElement("button");
  131. button.classList.add("combat-button");
  132. button.innerHTML = playerAttacks[i].name;
  133. button.addEventListener("click", function() { attackClicked(i); } );
  134. button.addEventListener("mouseover", function() { attackHovered(i); } );
  135. button.addEventListener("mouseout", function() { document.getElementById("combat-desc").innerHTML = ""; } );
  136. li.appendChild(button);
  137. list.appendChild(li);
  138. }
  139. document.getElementById("stat-foe-name").innerHTML = "Name: " + currentFoe.name;
  140. document.getElementById("stat-foe-health").innerHTML = "Health: " + currentFoe.health + "/" + currentFoe.maxHealth;
  141. document.getElementById("stat-foe-stamina").innerHTML = "Stamina: " + currentFoe.stamina + "/" + currentFoe.maxStamina;
  142. }
  143. function updateDialog() {
  144. let list = document.getElementById("dialog");
  145. while(list.firstChild) {
  146. list.removeChild(list.firstChild);
  147. }
  148. for (let i = 0; i < currentDialog.choices.length; i++) {
  149. let activated = currentDialog.choices[i].node.requirements == undefined || currentDialog.choices[i].node.requirements.reduce((result, test) => result && test(player, currentFoe), true);
  150. let li = document.createElement("li");
  151. let button = document.createElement("button");
  152. button.classList.add("dialog-button");
  153. button.innerHTML = currentDialog.choices[i].text;
  154. button.addEventListener("click", function() { dialogClicked(i); });
  155. if (!activated) {
  156. button.classList.add("disabled-button");
  157. button.disabled = true;
  158. }
  159. li.appendChild(button);
  160. list.appendChild(li);
  161. }
  162. }
  163. function updateDisplay() {
  164. document.querySelectorAll(".selector").forEach(function (x) {
  165. x.style.display = "none";
  166. });
  167. switch(mode) {
  168. case "explore":
  169. document.getElementById("selector-explore").style.display = "flex";
  170. updateExplore();
  171. break;
  172. case "combat":
  173. document.getElementById("selector-combat").style.display = "flex";
  174. updateCombat();
  175. break;
  176. case "dialog":
  177. document.getElementById("selector-dialog").style.display = "flex";
  178. updateDialog();
  179. break;
  180. case "eaten":
  181. document.getElementById("selector-eaten").style.display = "flex";
  182. updateEaten();
  183. break;
  184. }
  185. document.getElementById("time").innerHTML = "Time: " + renderTime(time);
  186. document.getElementById("date").innerHTML = "Day " + date;
  187. document.getElementById("stat-name").innerHTML = "Name: " + player.name;
  188. document.getElementById("stat-health").innerHTML = "Health: " + round(player.health,0) + "/" + round(player.maxHealth,0);
  189. document.getElementById("stat-cash").innerHTML = "Cash: $" + round(player.cash,0);
  190. document.getElementById("stat-stamina").innerHTML = "Stamina: " + round(player.stamina,0) + "/" + round(player.maxStamina,0);
  191. document.getElementById("stat-fullness").innerHTML = "Fullness: " + round(player.fullness(),0);
  192. if (player.prefs.scat) {
  193. document.getElementById("stat-bowels").innerHTML = "Bowels: " + round(player.bowels.fullness,0);
  194. } else {
  195. document.getElementById("stat-bowels").innerHTML = "";
  196. }
  197. }
  198. function advanceTime(amount) {
  199. time = (time + amount);
  200. date += Math.floor(time / 86400);
  201. time = time % 86400;
  202. player.restoreHealth(amount);
  203. player.restoreStamina(amount);
  204. update(player.stomach.digest(amount));
  205. update(player.butt.digest(amount));
  206. }
  207. function renderTime(time) {
  208. let suffix = (time < 43200) ? "AM" : "PM";
  209. let hour = Math.floor((time % 43200) / 3600);
  210. if (hour == 0)
  211. hour = 12;
  212. let minute = Math.floor(time / 60) % 60;
  213. if (minute < 9)
  214. minute = "0" + minute;
  215. return hour + ":" + minute + " " + suffix;
  216. }
  217. function move(direction) {
  218. let target = currentRoom.exits[direction];
  219. if (target == null) {
  220. alert("Tried to move to an empty room!");
  221. return;
  222. }
  223. moveTo(target,currentRoom.exitDescs[direction]);
  224. }
  225. function moveToByName(roomName, desc="You go places lol") {
  226. moveTo(world[roomName], desc);
  227. }
  228. function moveTo(room,desc="You go places lol") {
  229. actions = [];
  230. currentRoom = room;
  231. advanceTime(30);
  232. currentRoom.objects.forEach(function (object) {
  233. object.actions.forEach(function (action) {
  234. if (action.conditions == undefined || action.conditions.reduce((result, cond) => result && cond(player.prefs), true))
  235. actions.push(action);
  236. });
  237. });
  238. update([desc,newline]);
  239. currentRoom.visit();
  240. }
  241. window.addEventListener('load', function(event) {
  242. document.getElementById("start-button").addEventListener("click", start, false);
  243. });
  244. function start() {
  245. applySettings(generateSettings());
  246. document.getElementById("create").style.display = "none";
  247. document.getElementById("game").style.display = "block";
  248. loadActions();
  249. loadCompass();
  250. loadDialog();
  251. world = createWorld();
  252. currentRoom = world["Bedroom"];
  253. respawnRoom = currentRoom;
  254. moveTo(currentRoom);
  255. updateDisplay();
  256. }
  257. // copied from Stroll LUL
  258. function generateSettings() {
  259. let form = document.forms.namedItem("character-form");
  260. let settings = {};
  261. for (let i=0; i<form.length; i++) {
  262. let value = form[i].value == "" ? form[i].placeholder : form[i].value;
  263. if (form[i].type == "text")
  264. if (form[i].value == "")
  265. settings[form[i].name] = form[i].placeholder;
  266. else
  267. settings[form[i].name] = value;
  268. else if (form[i].type == "number")
  269. settings[form[i].name] = parseFloat(value);
  270. else if (form[i].type == "checkbox") {
  271. settings[form[i].name] = form[i].checked;
  272. } else if (form[i].type == "radio") {
  273. let name = form[i].name;
  274. if (form[i].checked)
  275. settings[name] = form[i].value;
  276. } else if (form[i].type == "select-one") {
  277. settings[form[i].name] = form[i][form[i].selectedIndex].value;
  278. }
  279. }
  280. return settings;
  281. }
  282. function applySettings(settings) {
  283. player.name = settings.name;
  284. player.species = settings.species;
  285. for (let key in settings) {
  286. if (settings.hasOwnProperty(key)) {
  287. if (key.match(/prefs/)) {
  288. let tokens = key.split("-");
  289. let pref = player.prefs;
  290. pref = tokens.slice(1,-1).reduce((pref, key) => pref[key], pref);
  291. pref[tokens.slice(-1)[0]] = settings[key];
  292. }
  293. }
  294. }
  295. }
  296. function saveSettings() {
  297. window.localStorage.setItem("settings", JSON.stringify(generateSettings()));
  298. }
  299. function retrieveSettings() {
  300. return JSON.parse(window.localStorage.getItem("settings"));
  301. }
  302. function update(lines=[]) {
  303. let log = document.getElementById("log");
  304. for (let i=0; i<lines.length; i++) {
  305. let div = document.createElement("div");
  306. div.innerHTML = lines[i];
  307. log.appendChild(div);
  308. }
  309. log.scrollTop = log.scrollHeight;
  310. updateDisplay();
  311. }
  312. function changeMode(newMode) {
  313. mode = newMode;
  314. let body = document.querySelector("body");
  315. document.getElementById("foe-stats").style.display = "none";
  316. body.className = "";
  317. switch(mode) {
  318. case "explore":
  319. case "dialog":
  320. body.classList.add("explore");
  321. break;
  322. case "combat":
  323. body.classList.add("combat");
  324. document.getElementById("foe-stats").style.display = "block";
  325. break;
  326. case "eaten":
  327. body.classList.add("eaten");
  328. break;
  329. }
  330. updateDisplay();
  331. }
  332. function respawn(respawnRoom) {
  333. if (killingBlow.gameover == undefined) {
  334. if (player.prefs.prey) {
  335. deaths.push("Digested by " + currentFoe.description("a") + " at " + renderTime(time) + " on day " + date);
  336. } else {
  337. deaths.push("Defeated by " + currentFoe.description("a") + " at " + renderTime(time) + " on day " + date);
  338. }
  339. } else {
  340. deaths.push(killingBlow.gameover() + " at " + renderTime(time) + " on day " + date);
  341. }
  342. moveTo(respawnRoom,"You drift through space and time...");
  343. player.clear();
  344. player.stomach.contents = [];
  345. player.butt.contents = [];
  346. player.bowels.contents = [];
  347. player.bowels.fullness = 0;
  348. advanceTime(Math.floor(86400 / 2 * (Math.random() * 0.5 - 0.25 + 1)));
  349. changeMode("explore");
  350. player.health = 100;
  351. update(["You wake back up in your bed."]);
  352. }
  353. function startCombat(opponent) {
  354. currentFoe = opponent;
  355. changeMode("combat");
  356. update(opponent.startCombat());
  357. }
  358. function attackClicked(index) {
  359. update(playerAttacks[index].attack(currentFoe));
  360. if (currentFoe.health <= 0) {
  361. currentFoe.defeated();
  362. } else if (mode == "combat") {
  363. let attack = pick(filterPriority(filterValid(currentFoe.attacks, currentFoe, player)), currentFoe, player);
  364. if (attack == null) {
  365. attack = currentFoe.backupAttack;
  366. }
  367. update(attack.attackPlayer(player));
  368. if (player.health <= -100) {
  369. killingBlow = attack;
  370. update(["You die..."]);
  371. respawn(respawnRoom);
  372. } else if (player.health <= 0) {
  373. update(["You fall to the ground..."]);
  374. if (player.prefs.prey) {
  375. changeMode("eaten");
  376. } else {
  377. killingBlow = attack;
  378. update(["You die..."]);
  379. respawn(respawnRoom);
  380. }
  381. }
  382. }
  383. }
  384. function attackHovered(index) {
  385. document.getElementById("combat-desc").innerHTML = playerAttacks[index].desc;
  386. }
  387. function struggleClicked(index) {
  388. let struggle = struggles[index];
  389. let result = struggle.struggle(player);
  390. update(result.lines.concat([newline]));
  391. if (result.escape == "stay") {
  392. changeMode("combat");
  393. } else if (result.escape == "escape") {
  394. changeMode("explore");
  395. } else {
  396. let digest = pick(filterValid(currentFoe.digests, currentFoe, player), currentFoe, player);
  397. if (digest == null) {
  398. digest = currentFoe.backupDigest;
  399. }
  400. update([digest.digest(player)]);
  401. if (player.health <= -100) {
  402. killingBlow = digest;
  403. update(currentFoe.finishDigest());
  404. respawn(respawnRoom);
  405. }
  406. }
  407. }
  408. function struggleHovered(index) {
  409. document.getElementById("eaten-desc").innerHTML = currentFoe.struggles[index].desc;
  410. }
  411. function startDialog(dialog) {
  412. currentDialog = dialog;
  413. changeMode("dialog");
  414. update(currentDialog.text);
  415. currentDialog.visit();
  416. updateDisplay();
  417. }
  418. function dialogClicked(index) {
  419. currentDialog = currentDialog.choices[index].node;
  420. update(currentDialog.text);
  421. currentDialog.visit();
  422. if (currentDialog.choices.length == 0 && mode == "dialog") {
  423. changeMode("explore");
  424. updateDisplay();
  425. }
  426. }
  427. function loadDialog() {
  428. dialogButtons = Array.from( document.querySelectorAll(".dialog-button"));
  429. for (let i = 0; i < dialogButtons.length; i++) {
  430. dialogButtons[i].addEventListener("click", function() { dialogClicked(i); });
  431. }
  432. }
  433. function actionClicked(index) {
  434. actions[index].action();
  435. }
  436. function loadActions() {
  437. actionButtons = Array.from( document.querySelectorAll(".action-button"));
  438. for (let i = 0; i < actionButtons.length; i++) {
  439. actionButtons[i].addEventListener("click", function() { actionClicked(i); });
  440. }
  441. }
  442. function loadCompass() {
  443. dirButtons[NORTH_WEST] = document.getElementById("compass-north-west");
  444. dirButtons[NORTH_WEST].addEventListener("click", function() {
  445. move(NORTH_WEST);
  446. });
  447. dirButtons[NORTH] = document.getElementById("compass-north");
  448. dirButtons[NORTH].addEventListener("click", function() {
  449. move(NORTH);
  450. });
  451. dirButtons[NORTH_EAST] = document.getElementById("compass-north-east");
  452. dirButtons[NORTH_EAST].addEventListener("click", function() {
  453. move(NORTH_EAST);
  454. });
  455. dirButtons[WEST] = document.getElementById("compass-west");
  456. dirButtons[WEST].addEventListener("click", function() {
  457. move(WEST);
  458. });
  459. dirButtons[EAST] = document.getElementById("compass-east");
  460. dirButtons[EAST].addEventListener("click", function() {
  461. move(EAST);
  462. });
  463. dirButtons[SOUTH_WEST] = document.getElementById("compass-south-west");
  464. dirButtons[SOUTH_WEST].addEventListener("click", function() {
  465. move(SOUTH_WEST);
  466. });
  467. dirButtons[SOUTH] = document.getElementById("compass-south");
  468. dirButtons[SOUTH].addEventListener("click", function() {
  469. move(SOUTH);
  470. });
  471. dirButtons[SOUTH_EAST] = document.getElementById("compass-south-east");
  472. dirButtons[SOUTH_EAST].addEventListener("click", function() {
  473. move(SOUTH_EAST);
  474. });
  475. document.getElementById("compass-look").addEventListener("click", look, false);
  476. }
  477. function look() {
  478. update([currentRoom.description]);
  479. }