munch
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 

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