crunch
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 

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