menu.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  1. /*
  2. * ===========================================================================
  3. *
  4. * Wolf3D Browser Version GPL Source Code
  5. * Copyright (C) 2012 id Software LLC, a ZeniMax Media company.
  6. *
  7. * This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").
  8. *
  9. * Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation, either version 2 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * Wolf3D Browser Source Code is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License version 2
  20. * along with Wolf3D Browser Source Code. If not, see <http://www.gnu.org/licenses/>.
  21. *
  22. * If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  23. *
  24. * ===========================================================================
  25. */
  26. /**
  27. * @namespace
  28. * @description Game menu management
  29. */
  30. Wolf.Menu = (function() {
  31. var setupDone = false,
  32. menuInputActive = false,
  33. activeIndex = 0,
  34. activeMouseItem = null,
  35. activeEpisode,
  36. messageBlink,
  37. activeMessage,
  38. activeSkill;
  39. var keySprites = {},
  40. i,
  41. keySpriteNames = [
  42. "BLANK",
  43. "QUESTION",
  44. "SHIFT",
  45. "SPACE",
  46. "CTRL",
  47. "LEFT",
  48. "RIGHT",
  49. "UP",
  50. "DOWN",
  51. "ENTER",
  52. "DEL",
  53. "PGUP",
  54. "PGDN",
  55. "INS",
  56. "SLASH",
  57. "HOME",
  58. "COMMA",
  59. "PERIOD",
  60. "PLUS",
  61. "MINUS",
  62. "0",
  63. "1",
  64. "2",
  65. "3",
  66. "4",
  67. "5",
  68. "6",
  69. "7",
  70. "8",
  71. "9",
  72. "A",
  73. "B",
  74. "C",
  75. "D",
  76. "E",
  77. "F",
  78. "G",
  79. "H",
  80. "I",
  81. "J",
  82. "K",
  83. "L",
  84. "M",
  85. "N",
  86. "O",
  87. "P",
  88. "Q",
  89. "R",
  90. "S",
  91. "T",
  92. "U",
  93. "V",
  94. "W",
  95. "X",
  96. "Y",
  97. "Z"
  98. ];
  99. for (i=0;i<keySpriteNames.length;i++) {
  100. if (keySpriteNames[i] !== "") {
  101. keySprites[keySpriteNames[i]] = i;
  102. }
  103. }
  104. function playSound(file) {
  105. Wolf.Sound.startSound(null, null, 1, Wolf.CHAN_AUTO, file, 1, Wolf.ATTN_NORM, 0);
  106. }
  107. function setActiveItem(item) {
  108. playSound("lsfx/005.wav");
  109. $("#menu div.menu.active li").removeClass("active");
  110. item.addClass("active");
  111. if ($("#menu div.menu.active").hasClass("skill")) {
  112. $("#menu div.menu.active div.face")
  113. .removeClass()
  114. .addClass("face " + item.data("skill"));
  115. }
  116. }
  117. /**
  118. * @description Bind events to menu items
  119. * @private
  120. */
  121. function setupEvents() {
  122. $(document).on("keydown", function(e) {
  123. if (!$("#menu").is(":visible")) {
  124. return;
  125. }
  126. if (!menuInputActive) {
  127. return;
  128. }
  129. var oldActive = activeIndex;
  130. switch (e.keyCode) {
  131. case 38:
  132. activeIndex--;
  133. activeMouseItem = null;
  134. break;
  135. case 40:
  136. activeIndex++;
  137. activeMouseItem = null;
  138. break;
  139. case 13:
  140. if (activeMouseItem) {
  141. activeMouseItem.trigger("click");
  142. } else {
  143. $("#menu div.menu.active li").eq(activeIndex).trigger("click");
  144. }
  145. break;
  146. case 27: // ESC
  147. var back = $("#menu div.menu.active").data("backmenu");
  148. if (back) {
  149. playSound("lsfx/039.wav");
  150. show(back);
  151. }
  152. return;
  153. }
  154. if (oldActive != activeIndex) {
  155. var items = $("#menu div.menu.active li:not(.hidden)");
  156. if (activeIndex < 0) {
  157. activeIndex += items.length;
  158. }
  159. activeIndex %= items.length;
  160. setActiveItem(items.eq(activeIndex));
  161. }
  162. });
  163. $("#menu li").mouseover(function() {
  164. if (!menuInputActive) {
  165. return;
  166. }
  167. activeMouseItem = $(this);
  168. setActiveItem($(this));
  169. });
  170. $("#menu li").on("click", function(e) {
  171. if (!menuInputActive) {
  172. return;
  173. }
  174. playSound("lsfx/032.wav");
  175. var $this = $(this),
  176. sub = $this.data("submenu");
  177. if (sub) {
  178. show(sub);
  179. e.stopPropagation();
  180. }
  181. if ($this.hasClass("sfxon")) {
  182. $("div.light", $this).addClass("on");
  183. $("#menu li.sfxoff div.light").removeClass("on");
  184. Wolf.Sound.toggleSound(true);
  185. }
  186. if ($this.hasClass("sfxoff")) {
  187. $("div.light", $this).addClass("on");
  188. $("#menu li.sfxon div.light").removeClass("on");
  189. Wolf.Sound.toggleSound(false);
  190. }
  191. if ($this.hasClass("musicon")) {
  192. $("div.light", $this).addClass("on");
  193. $("#menu li.musicoff div.light").removeClass("on");
  194. Wolf.Sound.toggleMusic(true);
  195. }
  196. if ($this.hasClass("musicoff")) {
  197. $("div.light", $this).addClass("on");
  198. $("#menu li.musicon div.light").removeClass("on");
  199. Wolf.Sound.toggleMusic(false);
  200. }
  201. if ($this.hasClass("mouseenabled")) {
  202. var mouseOn = Wolf.Game.isMouseEnabled();
  203. $("div.light", $this).toggleClass("on", !mouseOn);
  204. Wolf.Game.enableMouse(!mouseOn);
  205. }
  206. if ($this.hasClass("customizekeys")) {
  207. customizeKeys($this);
  208. e.stopPropagation();
  209. }
  210. });
  211. $("#menu div.menu.episodes li").on("click", function(e) {
  212. if (!menuInputActive) {
  213. return;
  214. }
  215. var episode = $(this).data("episode");
  216. if (Wolf.Game.isPlaying()) {
  217. showMessage("confirm-newgame", true, function(result) {
  218. if (result) {
  219. activeEpisode = episode;
  220. show("skill");
  221. } else {
  222. show("main");
  223. }
  224. });
  225. } else {
  226. activeEpisode = episode;
  227. show("skill");
  228. }
  229. });
  230. $("#menu div.menu.skill li").on("click", function(e) {
  231. if (!menuInputActive) {
  232. return;
  233. }
  234. activeSkill = $(this).data("skill");
  235. });
  236. $("#menu div.menu.main li.resumegame").on("click", function(e) {
  237. if (!menuInputActive) {
  238. return;
  239. }
  240. if (Wolf.Game.isPlaying()) {
  241. hide();
  242. Wolf.Game.resume();
  243. }
  244. });
  245. $("#menu div.menu.main li.readthis").on("click", function(e) {
  246. if (!menuInputActive) {
  247. return;
  248. }
  249. menuInputActive = false;
  250. $("#menu").fadeOut(null, function() {
  251. showText("help", 11, function() {
  252. $("#menu").fadeIn();
  253. });
  254. });
  255. e.stopPropagation();
  256. });
  257. $("#menu div.menu.levels li").on("click", function(e) {
  258. if (!menuInputActive) {
  259. return;
  260. }
  261. var level, gameState;
  262. hide();
  263. level = $(this).data("level");
  264. gameState = Wolf.Game.startGame(Wolf[activeSkill]);
  265. Wolf.Game.startLevel(gameState, activeEpisode, level);
  266. });
  267. }
  268. function customizeKeys($this) {
  269. menuInputActive = false;
  270. var current = 0,
  271. isBinding = false,
  272. blinkInterval;
  273. function selectKey(index) {
  274. if (index < 0) index += 4;
  275. index = index % 4;
  276. var currentSprite = $("span.active", $this);
  277. if (currentSprite[0]) {
  278. setCustomizeKey(
  279. currentSprite.data("action"),
  280. currentSprite.data("keyIndex"),
  281. false
  282. );
  283. }
  284. var sprite = $("span.k" + (index+1), $this);
  285. setCustomizeKey(
  286. sprite.data("action"),
  287. sprite.data("keyIndex"),
  288. true
  289. );
  290. current = index;
  291. }
  292. function activateKey(index) {
  293. isBinding = true;
  294. var sprite = $("span.k" + (index+1), $this),
  295. blink = false;
  296. setCustomizeKey(
  297. sprite.data("action"), "QUESTION", true
  298. );
  299. if (blinkInterval) {
  300. clearInterval(blinkInterval);
  301. }
  302. blinkInterval = setInterval(function() {
  303. setCustomizeKey(sprite.data("action"), (blink = !blink) ? "BLANK" : "QUESTION", true);
  304. }, 500)
  305. }
  306. function bindKey(index, key) {
  307. var sprite = $("span.k" + (index+1), $this);
  308. setCustomizeKey(
  309. sprite.data("action"),
  310. key,
  311. true
  312. );
  313. Wolf.Game.bindControl(sprite.data("action"), [key]);
  314. }
  315. function exitCustomize() {
  316. $(document).off("keydown", keyHandler);
  317. initCustomizeMenu();
  318. menuInputActive = true;
  319. }
  320. function keyHandler(e) {
  321. var i;
  322. if (isBinding) {
  323. // look for key in bindable key codes. TODO: LUT?
  324. for (i=2;i<keySpriteNames.length;i++) {
  325. if (Wolf.Keys[keySpriteNames[i]] == e.keyCode) {
  326. bindKey(current, keySpriteNames[i]);
  327. isBinding = false;
  328. clearInterval(blinkInterval);
  329. blinkInterval = 0;
  330. break;
  331. }
  332. }
  333. return;
  334. }
  335. switch (e.keyCode) {
  336. case 39: // right
  337. selectKey(current + 1);
  338. break;
  339. case 37: // left
  340. selectKey(current - 1);
  341. break;
  342. case 13: // enter
  343. activateKey(current);
  344. break;
  345. case 27: // ESC
  346. case 38: // up
  347. case 40: // down
  348. exitCustomize()
  349. break;
  350. }
  351. }
  352. $(document).on("keydown", keyHandler);
  353. selectKey(current);
  354. }
  355. function setCustomizeKey(action, keyIndex, active) {
  356. var menu = $("#menu div.menu.customize"),
  357. x = (active ? -256 : 0),
  358. y = -keySprites[keyIndex] * 32;
  359. $("span." + action, menu)
  360. .css(
  361. "backgroundPosition", x + "px " + y + "px"
  362. )
  363. .data("keyIndex", keyIndex)
  364. .toggleClass("active", !!active);
  365. }
  366. function initCustomizeMenu() {
  367. var controls = Wolf.Game.getControls(),
  368. keys = ["run", "use", "attack", "strafe", "left", "right", "up", "down"],
  369. i;
  370. for (i=0;i<keys.length;i++) {
  371. setCustomizeKey(keys[i], controls[keys[i]][0])
  372. }
  373. }
  374. function showMessage(name, blink, onclose) {
  375. var box,
  376. blinkOn = false;
  377. activeMessage = name;
  378. menuInputActive = false;
  379. if (messageBlink) {
  380. clearInterval(messageBlink);
  381. messageBlink = 0;
  382. }
  383. $("#menu .message." + name).show();
  384. box = $("#menu .message." + name + " div.box");
  385. box.removeClass("blink");
  386. if (blink) {
  387. setInterval(function() {
  388. blinkOn = !blinkOn;
  389. if (blinkOn) {
  390. box.addClass("blink");
  391. } else {
  392. box.removeClass("blink");
  393. }
  394. }, 200);
  395. }
  396. function close(value) {
  397. playSound("lsfx/039.wav");
  398. $(document).off("keydown", keyHandler);
  399. $("#menu .message." + name).hide();
  400. if (messageBlink) {
  401. clearInterval(messageBlink);
  402. messageBlink = 0;
  403. }
  404. menuInputActive = true;
  405. if (onclose) {
  406. onclose(value)
  407. }
  408. }
  409. function keyHandler(e) {
  410. switch (e.keyCode) {
  411. case 27: // ESC
  412. case 78: // N
  413. close(false);
  414. break;
  415. case 89: // Y
  416. close(true);
  417. break;
  418. }
  419. }
  420. $(document).on("keydown", keyHandler);
  421. }
  422. /**
  423. * @description Show the menu
  424. * @memberOf Wolf.Menu
  425. */
  426. function show(menuName) {
  427. var musicOn, soundOn, mouseOn;
  428. if (!setupDone) {
  429. setupEvents();
  430. setupDone = true;
  431. }
  432. Wolf.Sound.startMusic("music/WONDERIN.ogg");
  433. menuName = menuName || "main";
  434. if (menuName == "main") {
  435. if (Wolf.Game.isPlaying()) {
  436. $("#menu div.menu.main li.resumegame")
  437. .removeClass("hidden")
  438. .show();
  439. } else {
  440. $("#menu div.menu.main li.resumegame")
  441. .addClass("hidden")
  442. .hide();
  443. }
  444. }
  445. if (menuName == "customize") {
  446. initCustomizeMenu();
  447. }
  448. if (menuName == "episodes") {
  449. $("#menu div.menu.episodes li")
  450. .removeClass("hidden")
  451. .show();
  452. if (!Wolf.Episodes[0].enabled) {
  453. $("#menu div.menu.episodes li.episode-0")
  454. .addClass("hidden")
  455. .hide();
  456. }
  457. if (!Wolf.Episodes[1].enabled) {
  458. $("#menu div.menu.episodes li.episode-1")
  459. .addClass("hidden")
  460. .hide();
  461. }
  462. if (!Wolf.Episodes[2].enabled) {
  463. $("#menu div.menu.episodes li.episode-2")
  464. .addClass("hidden")
  465. .hide();
  466. }
  467. }
  468. if (menuName == "sound") {
  469. musicOn = Wolf.Sound.isMusicEnabled();
  470. soundOn = Wolf.Sound.isSoundEnabled();
  471. $("#menu li.sfxoff div.light").toggleClass("on", !soundOn);
  472. $("#menu li.sfxon div.light").toggleClass("on", soundOn);
  473. $("#menu li.musicoff div.light").toggleClass("on", !musicOn);
  474. $("#menu li.musicon div.light").toggleClass("on", musicOn);
  475. }
  476. if (menuName == "control") {
  477. mouseOn = Wolf.Game.isMouseEnabled();
  478. $("#menu li.mouseenabled div.light").toggleClass("on", mouseOn);
  479. }
  480. if ($("#menu").data("menu")) {
  481. $("#menu").removeClass($("#menu").data("menu"));
  482. }
  483. $("#menu div.menu").removeClass("active").hide();
  484. $("#menu li").removeClass("active");
  485. $("#menu").data("menu", menuName).addClass(menuName).show();
  486. $("#menu div.menu." + menuName).addClass("active").show();
  487. $("#menu div.menu." + menuName + " ul li").first().addClass("active");
  488. $("#menu").focus();
  489. activeIndex = 0;
  490. activeMouseItem = null;
  491. menuInputActive = true;
  492. }
  493. /**
  494. * @description Hide the menu
  495. * @memberOf Wolf.Menu
  496. */
  497. function hide() {
  498. $("#menu").hide();
  499. menuInputActive = false;
  500. }
  501. function showText(name, num, closeFunction) {
  502. var screen = $("#text-screen"),
  503. current = 0;
  504. menuInputActive = false;
  505. function show(moveIdx) {
  506. current += moveIdx;
  507. if (current < 0) {
  508. current += num;
  509. }
  510. current = current % num;
  511. screen.css({
  512. "backgroundImage" : "url(art/text-screens/" + name + "-" + (current+1) + ".png)"
  513. });
  514. // preload the next in the background
  515. var next = (current + 1) % num,
  516. nextImg = new Image();
  517. nextImg.src = "art/text-screens/" + name + "-" + (next+1) + ".png";
  518. }
  519. function close() {
  520. $(document).off("keydown", keyHandler);
  521. screen.fadeOut(null, closeFunction);
  522. menuInputActive = true;
  523. }
  524. function keyHandler(e) {
  525. switch (e.keyCode) {
  526. case 39: // right
  527. show(1);
  528. break;
  529. case 37: // left
  530. show(-1);
  531. break;
  532. case 27: // ESC
  533. close();
  534. break;
  535. }
  536. }
  537. show(0);
  538. screen.fadeIn(null, function() {
  539. $(document).on("keydown", keyHandler);
  540. });
  541. }
  542. return {
  543. show : show,
  544. hide : hide,
  545. showText : showText
  546. };
  547. })();