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.
 
 
 

491 lines
14 KiB

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