crunch
25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 
 

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