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.
 
 
 

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