not really known
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.

4838 lines
120 KiB

  1. /*
  2. This is rot.js, the ROguelike Toolkit in JavaScript.
  3. Version 0.5~dev, generated on Wed Dec 18 21:12:34 CET 2013.
  4. */
  5. /**
  6. * @namespace Top-level ROT namespace
  7. */
  8. var ROT = {
  9. /**
  10. * @returns {bool} Is rot.js supported by this browser?
  11. */
  12. isSupported: function() {
  13. return !!(document.createElement("canvas").getContext && Function.prototype.bind);
  14. },
  15. /** Default with for display and map generators */
  16. DEFAULT_WIDTH: 80,
  17. /** Default height for display and map generators */
  18. DEFAULT_HEIGHT: 25,
  19. /** Directional constants. Ordering is important! */
  20. DIRS: {
  21. "4": [
  22. [ 0, -1],
  23. [ 1, 0],
  24. [ 0, 1],
  25. [-1, 0]
  26. ],
  27. "8": [
  28. [ 0, -1],
  29. [ 1, -1],
  30. [ 1, 0],
  31. [ 1, 1],
  32. [ 0, 1],
  33. [-1, 1],
  34. [-1, 0],
  35. [-1, -1]
  36. ],
  37. "6": [
  38. [-1, -1],
  39. [ 1, -1],
  40. [ 2, 0],
  41. [ 1, 1],
  42. [-1, 1],
  43. [-2, 0]
  44. ]
  45. },
  46. /** Cancel key. */
  47. VK_CANCEL: 3,
  48. /** Help key. */
  49. VK_HELP: 6,
  50. /** Backspace key. */
  51. VK_BACK_SPACE: 8,
  52. /** Tab key. */
  53. VK_TAB: 9,
  54. /** 5 key on Numpad when NumLock is unlocked. Or on Mac, clear key which is positioned at NumLock key. */
  55. VK_CLEAR: 12,
  56. /** Return/enter key on the main keyboard. */
  57. VK_RETURN: 13,
  58. /** Reserved, but not used. */
  59. VK_ENTER: 14,
  60. /** Shift key. */
  61. VK_SHIFT: 16,
  62. /** Control key. */
  63. VK_CONTROL: 17,
  64. /** Alt (Option on Mac) key. */
  65. VK_ALT: 18,
  66. /** Pause key. */
  67. VK_PAUSE: 19,
  68. /** Caps lock. */
  69. VK_CAPS_LOCK: 20,
  70. /** Escape key. */
  71. VK_ESCAPE: 27,
  72. /** Space bar. */
  73. VK_SPACE: 32,
  74. /** Page Up key. */
  75. VK_PAGE_UP: 33,
  76. /** Page Down key. */
  77. VK_PAGE_DOWN: 34,
  78. /** End key. */
  79. VK_END: 35,
  80. /** Home key. */
  81. VK_HOME: 36,
  82. /** Left arrow. */
  83. VK_LEFT: 37,
  84. /** Up arrow. */
  85. VK_UP: 38,
  86. /** Right arrow. */
  87. VK_RIGHT: 39,
  88. /** Down arrow. */
  89. VK_DOWN: 40,
  90. /** Print Screen key. */
  91. VK_PRINTSCREEN: 44,
  92. /** Ins(ert) key. */
  93. VK_INSERT: 45,
  94. /** Del(ete) key. */
  95. VK_DELETE: 46,
  96. /***/
  97. VK_0: 48,
  98. /***/
  99. VK_1: 49,
  100. /***/
  101. VK_2: 50,
  102. /***/
  103. VK_3: 51,
  104. /***/
  105. VK_4: 52,
  106. /***/
  107. VK_5: 53,
  108. /***/
  109. VK_6: 54,
  110. /***/
  111. VK_7: 55,
  112. /***/
  113. VK_8: 56,
  114. /***/
  115. VK_9: 57,
  116. /** Colon (:) key. Requires Gecko 15.0 */
  117. VK_COLON: 58,
  118. /** Semicolon (;) key. */
  119. VK_SEMICOLON: 59,
  120. /** Less-than (<) key. Requires Gecko 15.0 */
  121. VK_LESS_THAN: 60,
  122. /** Equals (=) key. */
  123. VK_EQUALS: 61,
  124. /** Greater-than (>) key. Requires Gecko 15.0 */
  125. VK_GREATER_THAN: 62,
  126. /** Question mark (?) key. Requires Gecko 15.0 */
  127. VK_QUESTION_MARK: 63,
  128. /** Atmark (@) key. Requires Gecko 15.0 */
  129. VK_AT: 64,
  130. /***/
  131. VK_A: 65,
  132. /***/
  133. VK_B: 66,
  134. /***/
  135. VK_C: 67,
  136. /***/
  137. VK_D: 68,
  138. /***/
  139. VK_E: 69,
  140. /***/
  141. VK_F: 70,
  142. /***/
  143. VK_G: 71,
  144. /***/
  145. VK_H: 72,
  146. /***/
  147. VK_I: 73,
  148. /***/
  149. VK_J: 74,
  150. /***/
  151. VK_K: 75,
  152. /***/
  153. VK_L: 76,
  154. /***/
  155. VK_M: 77,
  156. /***/
  157. VK_N: 78,
  158. /***/
  159. VK_O: 79,
  160. /***/
  161. VK_P: 80,
  162. /***/
  163. VK_Q: 81,
  164. /***/
  165. VK_R: 82,
  166. /***/
  167. VK_S: 83,
  168. /***/
  169. VK_T: 84,
  170. /***/
  171. VK_U: 85,
  172. /***/
  173. VK_V: 86,
  174. /***/
  175. VK_W: 87,
  176. /***/
  177. VK_X: 88,
  178. /***/
  179. VK_Y: 89,
  180. /***/
  181. VK_Z: 90,
  182. /***/
  183. VK_CONTEXT_MENU: 93,
  184. /** 0 on the numeric keypad. */
  185. VK_NUMPAD0: 96,
  186. /** 1 on the numeric keypad. */
  187. VK_NUMPAD1: 97,
  188. /** 2 on the numeric keypad. */
  189. VK_NUMPAD2: 98,
  190. /** 3 on the numeric keypad. */
  191. VK_NUMPAD3: 99,
  192. /** 4 on the numeric keypad. */
  193. VK_NUMPAD4: 100,
  194. /** 5 on the numeric keypad. */
  195. VK_NUMPAD5: 101,
  196. /** 6 on the numeric keypad. */
  197. VK_NUMPAD6: 102,
  198. /** 7 on the numeric keypad. */
  199. VK_NUMPAD7: 103,
  200. /** 8 on the numeric keypad. */
  201. VK_NUMPAD8: 104,
  202. /** 9 on the numeric keypad. */
  203. VK_NUMPAD9: 105,
  204. /** * on the numeric keypad. */
  205. VK_MULTIPLY: 106,
  206. /** + on the numeric keypad. */
  207. VK_ADD: 107,
  208. /***/
  209. VK_SEPARATOR: 108,
  210. /** - on the numeric keypad. */
  211. VK_SUBTRACT: 109,
  212. /** Decimal point on the numeric keypad. */
  213. VK_DECIMAL: 110,
  214. /** / on the numeric keypad. */
  215. VK_DIVIDE: 111,
  216. /** F1 key. */
  217. VK_F1: 112,
  218. /** F2 key. */
  219. VK_F2: 113,
  220. /** F3 key. */
  221. VK_F3: 114,
  222. /** F4 key. */
  223. VK_F4: 115,
  224. /** F5 key. */
  225. VK_F5: 116,
  226. /** F6 key. */
  227. VK_F6: 117,
  228. /** F7 key. */
  229. VK_F7: 118,
  230. /** F8 key. */
  231. VK_F8: 119,
  232. /** F9 key. */
  233. VK_F9: 120,
  234. /** F10 key. */
  235. VK_F10: 121,
  236. /** F11 key. */
  237. VK_F11: 122,
  238. /** F12 key. */
  239. VK_F12: 123,
  240. /** F13 key. */
  241. VK_F13: 124,
  242. /** F14 key. */
  243. VK_F14: 125,
  244. /** F15 key. */
  245. VK_F15: 126,
  246. /** F16 key. */
  247. VK_F16: 127,
  248. /** F17 key. */
  249. VK_F17: 128,
  250. /** F18 key. */
  251. VK_F18: 129,
  252. /** F19 key. */
  253. VK_F19: 130,
  254. /** F20 key. */
  255. VK_F20: 131,
  256. /** F21 key. */
  257. VK_F21: 132,
  258. /** F22 key. */
  259. VK_F22: 133,
  260. /** F23 key. */
  261. VK_F23: 134,
  262. /** F24 key. */
  263. VK_F24: 135,
  264. /** Num Lock key. */
  265. VK_NUM_LOCK: 144,
  266. /** Scroll Lock key. */
  267. VK_SCROLL_LOCK: 145,
  268. /** Circumflex (^) key. Requires Gecko 15.0 */
  269. VK_CIRCUMFLEX: 160,
  270. /** Exclamation (!) key. Requires Gecko 15.0 */
  271. VK_EXCLAMATION: 161,
  272. /** Double quote () key. Requires Gecko 15.0 */
  273. VK_DOUBLE_QUOTE: 162,
  274. /** Hash (#) key. Requires Gecko 15.0 */
  275. VK_HASH: 163,
  276. /** Dollar sign ($) key. Requires Gecko 15.0 */
  277. VK_DOLLAR: 164,
  278. /** Percent (%) key. Requires Gecko 15.0 */
  279. VK_PERCENT: 165,
  280. /** Ampersand (&) key. Requires Gecko 15.0 */
  281. VK_AMPERSAND: 166,
  282. /** Underscore (_) key. Requires Gecko 15.0 */
  283. VK_UNDERSCORE: 167,
  284. /** Open parenthesis (() key. Requires Gecko 15.0 */
  285. VK_OPEN_PAREN: 168,
  286. /** Close parenthesis ()) key. Requires Gecko 15.0 */
  287. VK_CLOSE_PAREN: 169,
  288. /* Asterisk (*) key. Requires Gecko 15.0 */
  289. VK_ASTERISK: 170,
  290. /** Plus (+) key. Requires Gecko 15.0 */
  291. VK_PLUS: 171,
  292. /** Pipe (|) key. Requires Gecko 15.0 */
  293. VK_PIPE: 172,
  294. /** Hyphen-US/docs/Minus (-) key. Requires Gecko 15.0 */
  295. VK_HYPHEN_MINUS: 173,
  296. /** Open curly bracket ({) key. Requires Gecko 15.0 */
  297. VK_OPEN_CURLY_BRACKET: 174,
  298. /** Close curly bracket (}) key. Requires Gecko 15.0 */
  299. VK_CLOSE_CURLY_BRACKET: 175,
  300. /** Tilde (~) key. Requires Gecko 15.0 */
  301. VK_TILDE: 176,
  302. /** Comma (,) key. */
  303. VK_COMMA: 188,
  304. /** Period (.) key. */
  305. VK_PERIOD: 190,
  306. /** Slash (/) key. */
  307. VK_SLASH: 191,
  308. /** Back tick (`) key. */
  309. VK_BACK_QUOTE: 192,
  310. /** Open square bracket ([) key. */
  311. VK_OPEN_BRACKET: 219,
  312. /** Back slash (\) key. */
  313. VK_BACK_SLASH: 220,
  314. /** Close square bracket (]) key. */
  315. VK_CLOSE_BRACKET: 221,
  316. /** Quote (''') key. */
  317. VK_QUOTE: 222,
  318. /** Meta key on Linux, Command key on Mac. */
  319. VK_META: 224,
  320. /** AltGr key on Linux. Requires Gecko 15.0 */
  321. VK_ALTGR: 225,
  322. /** Windows logo key on Windows. Or Super or Hyper key on Linux. Requires Gecko 15.0 */
  323. VK_WIN: 91,
  324. /** Linux support for this keycode was added in Gecko 4.0. */
  325. VK_KANA: 21,
  326. /** Linux support for this keycode was added in Gecko 4.0. */
  327. VK_HANGUL: 21,
  328. /** 英数 key on Japanese Mac keyboard. Requires Gecko 15.0 */
  329. VK_EISU: 22,
  330. /** Linux support for this keycode was added in Gecko 4.0. */
  331. VK_JUNJA: 23,
  332. /** Linux support for this keycode was added in Gecko 4.0. */
  333. VK_FINAL: 24,
  334. /** Linux support for this keycode was added in Gecko 4.0. */
  335. VK_HANJA: 25,
  336. /** Linux support for this keycode was added in Gecko 4.0. */
  337. VK_KANJI: 25,
  338. /** Linux support for this keycode was added in Gecko 4.0. */
  339. VK_CONVERT: 28,
  340. /** Linux support for this keycode was added in Gecko 4.0. */
  341. VK_NONCONVERT: 29,
  342. /** Linux support for this keycode was added in Gecko 4.0. */
  343. VK_ACCEPT: 30,
  344. /** Linux support for this keycode was added in Gecko 4.0. */
  345. VK_MODECHANGE: 31,
  346. /** Linux support for this keycode was added in Gecko 4.0. */
  347. VK_SELECT: 41,
  348. /** Linux support for this keycode was added in Gecko 4.0. */
  349. VK_PRINT: 42,
  350. /** Linux support for this keycode was added in Gecko 4.0. */
  351. VK_EXECUTE: 43,
  352. /** Linux support for this keycode was added in Gecko 4.0. */
  353. VK_SLEEP: 95
  354. };
  355. /**
  356. * @namespace
  357. * Contains text tokenization and breaking routines
  358. */
  359. ROT.Text = {
  360. RE_COLORS: /%([bc]){([^}]*)}/g,
  361. /* token types */
  362. TYPE_TEXT: 0,
  363. TYPE_NEWLINE: 1,
  364. TYPE_FG: 2,
  365. TYPE_BG: 3,
  366. /**
  367. * Measure size of a resulting text block
  368. */
  369. measure: function(str, maxWidth) {
  370. var result = {width:0, height:1};
  371. var tokens = this.tokenize(str, maxWidth);
  372. var lineWidth = 0;
  373. for (var i=0;i<tokens.length;i++) {
  374. var token = tokens[i];
  375. switch (token.type) {
  376. case this.TYPE_TEXT:
  377. lineWidth += token.value.length;
  378. break;
  379. case this.TYPE_NEWLINE:
  380. result.height++;
  381. result.width = Math.max(result.width, lineWidth);
  382. lineWidth = 0;
  383. break;
  384. }
  385. }
  386. result.width = Math.max(result.width, lineWidth);
  387. return result;
  388. },
  389. /**
  390. * Convert string to a series of a formatting commands
  391. */
  392. tokenize: function(str, maxWidth) {
  393. var result = [];
  394. /* first tokenization pass - split texts and color formatting commands */
  395. var offset = 0;
  396. str.replace(this.RE_COLORS, function(match, type, name, index) {
  397. /* string before */
  398. var part = str.substring(offset, index);
  399. if (part.length) {
  400. result.push({
  401. type: ROT.Text.TYPE_TEXT,
  402. value: part
  403. });
  404. }
  405. /* color command */
  406. result.push({
  407. type: (type == "c" ? ROT.Text.TYPE_FG : ROT.Text.TYPE_BG),
  408. value: name.trim()
  409. });
  410. offset = index + match.length;
  411. return "";
  412. });
  413. /* last remaining part */
  414. var part = str.substring(offset);
  415. if (part.length) {
  416. result.push({
  417. type: ROT.Text.TYPE_TEXT,
  418. value: part
  419. });
  420. }
  421. return this._breakLines(result, maxWidth);
  422. },
  423. /* insert line breaks into first-pass tokenized data */
  424. _breakLines: function(tokens, maxWidth) {
  425. if (!maxWidth) { maxWidth = Infinity; };
  426. var i = 0;
  427. var lineLength = 0;
  428. var lastTokenWithSpace = -1;
  429. while (i < tokens.length) { /* take all text tokens, remove space, apply linebreaks */
  430. var token = tokens[i];
  431. if (token.type == ROT.Text.TYPE_NEWLINE) { /* reset */
  432. lineLength = 0;
  433. lastTokenWithSpace = -1;
  434. }
  435. if (token.type != ROT.Text.TYPE_TEXT) { /* skip non-text tokens */
  436. i++;
  437. continue;
  438. }
  439. /* remove spaces at the beginning of line */
  440. while (lineLength == 0 && token.value.charAt(0) == " ") { token.value = token.value.substring(1); }
  441. /* forced newline? insert two new tokens after this one */
  442. var index = token.value.indexOf("\n");
  443. if (index != -1) {
  444. token.value = this._breakInsideToken(tokens, i, index, true);
  445. /* if there are spaces at the end, we must remove them (we do not want the line too long) */
  446. var arr = token.value.split("");
  447. while (arr[arr.length-1] == " ") { arr.pop(); }
  448. token.value = arr.join("");
  449. }
  450. /* token degenerated? */
  451. if (!token.value.length) {
  452. tokens.splice(i, 1);
  453. continue;
  454. }
  455. if (lineLength + token.value.length > maxWidth) { /* line too long, find a suitable breaking spot */
  456. /* is it possible to break within this token? */
  457. var index = -1;
  458. while (1) {
  459. var nextIndex = token.value.indexOf(" ", index+1);
  460. if (nextIndex == -1) { break; }
  461. if (lineLength + nextIndex > maxWidth) { break; }
  462. index = nextIndex;
  463. }
  464. if (index != -1) { /* break at space within this one */
  465. token.value = this._breakInsideToken(tokens, i, index, true);
  466. } else if (lastTokenWithSpace != -1) { /* is there a previous token where a break can occur? */
  467. var token = tokens[lastTokenWithSpace];
  468. var breakIndex = token.value.lastIndexOf(" ");
  469. token.value = this._breakInsideToken(tokens, lastTokenWithSpace, breakIndex, true);
  470. i = lastTokenWithSpace;
  471. } else { /* force break in this token */
  472. token.value = this._breakInsideToken(tokens, i, maxWidth-lineLength, false);
  473. }
  474. } else { /* line not long, continue */
  475. lineLength += token.value.length;
  476. if (token.value.indexOf(" ") != -1) { lastTokenWithSpace = i; }
  477. }
  478. i++; /* advance to next token */
  479. }
  480. tokens.push({type: ROT.Text.TYPE_NEWLINE}); /* insert fake newline to fix the last text line */
  481. /* remove trailing space from text tokens before newlines */
  482. var lastTextToken = null;
  483. for (var i=0;i<tokens.length;i++) {
  484. var token = tokens[i];
  485. switch (token.type) {
  486. case ROT.Text.TYPE_TEXT: lastTextToken = token; break;
  487. case ROT.Text.TYPE_NEWLINE:
  488. if (lastTextToken) { /* remove trailing space */
  489. var arr = lastTextToken.value.split("");
  490. while (arr[arr.length-1] == " ") { arr.pop(); }
  491. lastTextToken.value = arr.join("");
  492. }
  493. lastTextToken = null;
  494. break;
  495. }
  496. }
  497. tokens.pop(); /* remove fake token */
  498. return tokens;
  499. },
  500. /**
  501. * Create new tokens and insert them into the stream
  502. * @param {object[]} tokens
  503. * @param {int} tokenIndex Token being processed
  504. * @param {int} breakIndex Index within current token's value
  505. * @param {bool} removeBreakChar Do we want to remove the breaking character?
  506. * @returns {string} remaining unbroken token value
  507. */
  508. _breakInsideToken: function(tokens, tokenIndex, breakIndex, removeBreakChar) {
  509. var newBreakToken = {
  510. type: ROT.Text.TYPE_NEWLINE
  511. }
  512. var newTextToken = {
  513. type: ROT.Text.TYPE_TEXT,
  514. value: tokens[tokenIndex].value.substring(breakIndex + (removeBreakChar ? 1 : 0))
  515. }
  516. tokens.splice(tokenIndex+1, 0, newBreakToken, newTextToken);
  517. return tokens[tokenIndex].value.substring(0, breakIndex);
  518. }
  519. }
  520. /**
  521. * @returns {any} Randomly picked item, null when length=0
  522. */
  523. Array.prototype.random = function() {
  524. if (!this.length) { return null; }
  525. return this[Math.floor(ROT.RNG.getUniform() * this.length)];
  526. }
  527. /**
  528. * @returns {array} New array with randomized items
  529. * FIXME destroys this!
  530. */
  531. Array.prototype.randomize = function() {
  532. var result = [];
  533. while (this.length) {
  534. var index = this.indexOf(this.random());
  535. result.push(this.splice(index, 1)[0]);
  536. }
  537. return result;
  538. }
  539. /**
  540. * Always positive modulus
  541. * @param {int} n Modulus
  542. * @returns {int} this modulo n
  543. */
  544. Number.prototype.mod = function(n) {
  545. return ((this%n)+n)%n;
  546. }
  547. /**
  548. * @returns {string} First letter capitalized
  549. */
  550. String.prototype.capitalize = function() {
  551. return this.charAt(0).toUpperCase() + this.substring(1);
  552. }
  553. /**
  554. * Left pad
  555. * @param {string} [character="0"]
  556. * @param {int} [count=2]
  557. */
  558. String.prototype.lpad = function(character, count) {
  559. var ch = character || "0";
  560. var cnt = count || 2;
  561. var s = "";
  562. while (s.length < (cnt - this.length)) { s += ch; }
  563. s = s.substring(0, cnt-this.length);
  564. return s+this;
  565. }
  566. /**
  567. * Right pad
  568. * @param {string} [character="0"]
  569. * @param {int} [count=2]
  570. */
  571. String.prototype.rpad = function(character, count) {
  572. var ch = character || "0";
  573. var cnt = count || 2;
  574. var s = "";
  575. while (s.length < (cnt - this.length)) { s += ch; }
  576. s = s.substring(0, cnt-this.length);
  577. return this+s;
  578. }
  579. /**
  580. * Format a string in a flexible way. Scans for %s strings and replaces them with arguments. List of patterns is modifiable via String.format.map.
  581. * @param {string} template
  582. * @param {any} [argv]
  583. */
  584. String.format = function(template) {
  585. var map = String.format.map;
  586. var args = Array.prototype.slice.call(arguments, 1);
  587. var replacer = function(match, group1, group2, index) {
  588. if (template.charAt(index-1) == "%") { return match.substring(1); }
  589. if (!args.length) { return match; }
  590. var obj = args[0];
  591. var group = group1 || group2;
  592. var parts = group.split(",");
  593. var name = parts.shift();
  594. var method = map[name.toLowerCase()];
  595. if (!method) { return match; }
  596. var obj = args.shift();
  597. var replaced = obj[method].apply(obj, parts);
  598. var first = name.charAt(0);
  599. if (first != first.toLowerCase()) { replaced = replaced.capitalize(); }
  600. return replaced;
  601. }
  602. return template.replace(/%(?:([a-z]+)|(?:{([^}]+)}))/gi, replacer);
  603. }
  604. String.format.map = {
  605. "s": "toString"
  606. }
  607. /**
  608. * Convenience shortcut to String.format(this)
  609. */
  610. String.prototype.format = function() {
  611. var args = Array.prototype.slice.call(arguments);
  612. args.unshift(this);
  613. return String.format.apply(String, args);
  614. }
  615. if (!Object.create) {
  616. /**
  617. * ES5 Object.create
  618. */
  619. Object.create = function(o) {
  620. var tmp = function() {};
  621. tmp.prototype = o;
  622. return new tmp();
  623. };
  624. }
  625. /**
  626. * Sets prototype of this function to an instance of parent function
  627. * @param {function} parent
  628. */
  629. Function.prototype.extend = function(parent) {
  630. this.prototype = Object.create(parent.prototype);
  631. this.prototype.constructor = this;
  632. return this;
  633. }
  634. window.requestAnimationFrame =
  635. window.requestAnimationFrame
  636. || window.mozRequestAnimationFrame
  637. || window.webkitRequestAnimationFrame
  638. || window.oRequestAnimationFrame
  639. || window.msRequestAnimationFrame
  640. || function(cb) { return setTimeout(cb, 1000/60); };
  641. window.cancelAnimationFrame =
  642. window.cancelAnimationFrame
  643. || window.mozCancelAnimationFrame
  644. || window.webkitCancelAnimationFrame
  645. || window.oCancelAnimationFrame
  646. || window.msCancelAnimationFrame
  647. || function(id) { return clearTimeout(id); };
  648. /**
  649. * @class Visual map display
  650. * @param {object} [options]
  651. * @param {int} [options.width=ROT.DEFAULT_WIDTH]
  652. * @param {int} [options.height=ROT.DEFAULT_HEIGHT]
  653. * @param {int} [options.fontSize=15]
  654. * @param {string} [options.fontFamily="monospace"]
  655. * @param {string} [options.fontStyle=""] bold/italic/none/both
  656. * @param {string} [options.fg="#ccc"]
  657. * @param {string} [options.bg="#000"]
  658. * @param {float} [options.spacing=1]
  659. * @param {float} [options.border=0]
  660. * @param {string} [options.layout="rect"]
  661. * @param {int} [options.tileWidth=32]
  662. * @param {int} [options.tileHeight=32]
  663. * @param {object} [options.tileMap={}]
  664. * @param {image} [options.tileSet=null]
  665. */
  666. ROT.Display = function(options) {
  667. var canvas = document.createElement("canvas");
  668. this._context = canvas.getContext("2d");
  669. this._data = {};
  670. this._dirty = false; /* false = nothing, true = all, object = dirty cells */
  671. this._options = {};
  672. this._backend = null;
  673. var defaultOptions = {
  674. width: ROT.DEFAULT_WIDTH,
  675. height: ROT.DEFAULT_HEIGHT,
  676. layout: "rect",
  677. fontSize: 15,
  678. spacing: 1,
  679. border: 0,
  680. fontFamily: "monospace",
  681. fontStyle: "",
  682. fg: "#ccc",
  683. bg: "#000",
  684. tileWidth: 32,
  685. tileHeight: 32,
  686. tileMap: {},
  687. tileSet: null
  688. };
  689. for (var p in options) { defaultOptions[p] = options[p]; }
  690. this.setOptions(defaultOptions);
  691. this.DEBUG = this.DEBUG.bind(this);
  692. this._tick = this._tick.bind(this);
  693. requestAnimationFrame(this._tick);
  694. }
  695. /**
  696. * Debug helper, ideal as a map generator callback. Always bound to this.
  697. * @param {int} x
  698. * @param {int} y
  699. * @param {int} what
  700. */
  701. ROT.Display.prototype.DEBUG = function(x, y, what) {
  702. var colors = [this._options.bg, this._options.fg];
  703. this.draw(x, y, null, null, colors[what % colors.length]);
  704. }
  705. /**
  706. * Clear the whole display (cover it with background color)
  707. */
  708. ROT.Display.prototype.clear = function() {
  709. this._data = {};
  710. this._dirty = true;
  711. }
  712. /**
  713. * @see ROT.Display
  714. */
  715. ROT.Display.prototype.setOptions = function(options) {
  716. for (var p in options) { this._options[p] = options[p]; }
  717. if (options.width || options.height || options.fontSize || options.fontFamily || options.spacing || options.layout) {
  718. if (options.layout) {
  719. this._backend = new ROT.Display[options.layout.capitalize()](this._context);
  720. }
  721. var font = (this._options.fontStyle ? this._options.fontStyle + " " : "") + this._options.fontSize + "px " + this._options.fontFamily;
  722. this._context.font = font;
  723. this._backend.compute(this._options);
  724. this._context.font = font;
  725. this._context.textAlign = "center";
  726. this._context.textBaseline = "middle";
  727. this._dirty = true;
  728. }
  729. return this;
  730. }
  731. /**
  732. * Returns currently set options
  733. * @returns {object} Current options object
  734. */
  735. ROT.Display.prototype.getOptions = function() {
  736. return this._options;
  737. }
  738. /**
  739. * Returns the DOM node of this display
  740. * @returns {node} DOM node
  741. */
  742. ROT.Display.prototype.getContainer = function() {
  743. return this._context.canvas;
  744. }
  745. /**
  746. * Compute the maximum width/height to fit into a set of given constraints
  747. * @param {int} availWidth Maximum allowed pixel width
  748. * @param {int} availHeight Maximum allowed pixel height
  749. * @returns {int[2]} cellWidth,cellHeight
  750. */
  751. ROT.Display.prototype.computeSize = function(availWidth, availHeight) {
  752. return this._backend.computeSize(availWidth, availHeight, this._options);
  753. }
  754. /**
  755. * Compute the maximum font size to fit into a set of given constraints
  756. * @param {int} availWidth Maximum allowed pixel width
  757. * @param {int} availHeight Maximum allowed pixel height
  758. * @returns {int} fontSize
  759. */
  760. ROT.Display.prototype.computeFontSize = function(availWidth, availHeight) {
  761. return this._backend.computeFontSize(availWidth, availHeight, this._options);
  762. }
  763. /**
  764. * Convert a DOM event (mouse or touch) to map coordinates. Uses first touch for multi-touch.
  765. * @param {Event} e event
  766. * @returns {int[2]} -1 for values outside of the canvas
  767. */
  768. ROT.Display.prototype.eventToPosition = function(e) {
  769. if (e.touches) {
  770. var x = e.touches[0].clientX;
  771. var y = e.touches[0].clientY;
  772. } else {
  773. var x = e.clientX;
  774. var y = e.clientY;
  775. }
  776. var rect = this._context.canvas.getBoundingClientRect();
  777. x -= rect.left;
  778. y -= rect.top;
  779. if (x < 0 || y < 0 || x >= this._context.canvas.width || y >= this._context.canvas.height) { return [-1, -1]; }
  780. return this._backend.eventToPosition(x, y);
  781. }
  782. /**
  783. * @param {int} x
  784. * @param {int} y
  785. * @param {string || string[]} ch One or more chars (will be overlapping themselves)
  786. * @param {string} [fg] foreground color
  787. * @param {string} [bg] background color
  788. */
  789. ROT.Display.prototype.draw = function(x, y, ch, fg, bg) {
  790. if (!fg) { fg = this._options.fg; }
  791. if (!bg) { bg = this._options.bg; }
  792. this._data[x+","+y] = [x, y, ch, fg, bg];
  793. if (this._dirty === true) { return; } /* will already redraw everything */
  794. if (!this._dirty) { this._dirty = {}; } /* first! */
  795. this._dirty[x+","+y] = true;
  796. }
  797. /**
  798. * Draws a text at given position. Optionally wraps at a maximum length. Currently does not work with hex layout.
  799. * @param {int} x
  800. * @param {int} y
  801. * @param {string} text May contain color/background format specifiers, %c{name}/%b{name}, both optional. %c{}/%b{} resets to default.
  802. * @param {int} [maxWidth] wrap at what width?
  803. * @returns {int} lines drawn
  804. */
  805. ROT.Display.prototype.drawText = function(x, y, text, maxWidth) {
  806. var fg = null;
  807. var bg = null;
  808. var cx = x;
  809. var cy = y;
  810. var lines = 1;
  811. if (!maxWidth) { maxWidth = this._options.width-x; }
  812. var tokens = ROT.Text.tokenize(text, maxWidth);
  813. while (tokens.length) { /* interpret tokenized opcode stream */
  814. var token = tokens.shift();
  815. switch (token.type) {
  816. case ROT.Text.TYPE_TEXT:
  817. for (var i=0;i<token.value.length;i++) {
  818. this.draw(cx++, cy, token.value.charAt(i), fg, bg);
  819. }
  820. break;
  821. case ROT.Text.TYPE_FG:
  822. fg = token.value || null;
  823. break;
  824. case ROT.Text.TYPE_BG:
  825. bg = token.value || null;
  826. break;
  827. case ROT.Text.TYPE_NEWLINE:
  828. cx = x;
  829. cy++;
  830. lines++
  831. break;
  832. }
  833. }
  834. return lines;
  835. }
  836. /**
  837. * Timer tick: update dirty parts
  838. */
  839. ROT.Display.prototype._tick = function() {
  840. requestAnimationFrame(this._tick);
  841. if (!this._dirty) { return; }
  842. if (this._dirty === true) { /* draw all */
  843. this._context.fillStyle = this._options.bg;
  844. this._context.fillRect(0, 0, this._context.canvas.width, this._context.canvas.height);
  845. for (var id in this._data) { /* redraw cached data */
  846. this._draw(id, false);
  847. }
  848. } else { /* draw only dirty */
  849. for (var key in this._dirty) {
  850. this._draw(key, true);
  851. }
  852. }
  853. this._dirty = false;
  854. }
  855. /**
  856. * @param {string} key What to draw
  857. * @param {bool} clearBefore Is it necessary to clean before?
  858. */
  859. ROT.Display.prototype._draw = function(key, clearBefore) {
  860. var data = this._data[key];
  861. if (data[4] != this._options.bg) { clearBefore = true; }
  862. this._backend.draw(data, clearBefore);
  863. }
  864. /**
  865. * @class Abstract display backend module
  866. * @private
  867. */
  868. ROT.Display.Backend = function(context) {
  869. this._context = context;
  870. }
  871. ROT.Display.Backend.prototype.compute = function(options) {
  872. }
  873. ROT.Display.Backend.prototype.draw = function(data, clearBefore) {
  874. }
  875. ROT.Display.Backend.prototype.computeSize = function(availWidth, availHeight) {
  876. }
  877. ROT.Display.Backend.prototype.computeFontSize = function(availWidth, availHeight) {
  878. }
  879. ROT.Display.Backend.prototype.eventToPosition = function(x, y) {
  880. }
  881. /**
  882. * @class Rectangular backend
  883. * @private
  884. */
  885. ROT.Display.Rect = function(context) {
  886. ROT.Display.Backend.call(this, context);
  887. this._spacingX = 0;
  888. this._spacingY = 0;
  889. this._canvasCache = {};
  890. this._options = {};
  891. }
  892. ROT.Display.Rect.extend(ROT.Display.Backend);
  893. ROT.Display.Rect.cache = false;
  894. ROT.Display.Rect.prototype.compute = function(options) {
  895. this._canvasCache = {};
  896. this._options = options;
  897. var charWidth = Math.ceil(this._context.measureText("W").width);
  898. this._spacingX = Math.ceil(options.spacing * charWidth);
  899. this._spacingY = Math.ceil(options.spacing * options.fontSize);
  900. this._context.canvas.width = options.width * this._spacingX;
  901. this._context.canvas.height = options.height * this._spacingY;
  902. }
  903. ROT.Display.Rect.prototype.draw = function(data, clearBefore) {
  904. if (this.constructor.cache) {
  905. this._drawWithCache(data, clearBefore);
  906. } else {
  907. this._drawNoCache(data, clearBefore);
  908. }
  909. }
  910. ROT.Display.Rect.prototype._drawWithCache = function(data, clearBefore) {
  911. var x = data[0];
  912. var y = data[1];
  913. var ch = data[2];
  914. var fg = data[3];
  915. var bg = data[4];
  916. var hash = ""+ch+fg+bg;
  917. if (hash in this._canvasCache) {
  918. var canvas = this._canvasCache[hash];
  919. } else {
  920. var b = this._options.border;
  921. var canvas = document.createElement("canvas");
  922. var ctx = canvas.getContext("2d");
  923. canvas.width = this._spacingX;
  924. canvas.height = this._spacingY;
  925. ctx.fillStyle = bg;
  926. ctx.fillRect(b, b, canvas.width-b, canvas.height-b);
  927. if (ch) {
  928. ctx.fillStyle = fg;
  929. ctx.font = this._context.font;
  930. ctx.textAlign = "center";
  931. ctx.textBaseline = "middle";
  932. var chars = [].concat(ch);
  933. for (var i=0;i<chars.length;i++) {
  934. ctx.fillText(chars[i], this._spacingX/2, this._spacingY/2);
  935. }
  936. }
  937. this._canvasCache[hash] = canvas;
  938. }
  939. this._context.drawImage(canvas, x*this._spacingX, y*this._spacingY);
  940. }
  941. ROT.Display.Rect.prototype._drawNoCache = function(data, clearBefore) {
  942. var x = data[0];
  943. var y = data[1];
  944. var ch = data[2];
  945. var fg = data[3];
  946. var bg = data[4];
  947. if (clearBefore) {
  948. var b = this._options.border;
  949. this._context.fillStyle = bg;
  950. this._context.fillRect(x*this._spacingX + b, y*this._spacingY + b, this._spacingX - b, this._spacingY - b);
  951. }
  952. if (!ch) { return; }
  953. this._context.fillStyle = fg;
  954. var chars = [].concat(ch);
  955. for (var i=0;i<chars.length;i++) {
  956. this._context.fillText(chars[i], (x+0.5) * this._spacingX, (y+0.5) * this._spacingY);
  957. }
  958. }
  959. ROT.Display.Rect.prototype.computeSize = function(availWidth, availHeight) {
  960. var width = Math.floor(availWidth / this._spacingX);
  961. var height = Math.floor(availHeight / this._spacingY);
  962. return [width, height];
  963. }
  964. ROT.Display.Rect.prototype.computeFontSize = function(availWidth, availHeight) {
  965. var boxWidth = Math.floor(availWidth / this._options.width);
  966. var boxHeight = Math.floor(availHeight / this._options.height);
  967. /* compute char ratio */
  968. var oldFont = this._context.font;
  969. this._context.font = "100px " + this._options.fontFamily;
  970. var width = Math.ceil(this._context.measureText("W").width);
  971. this._context.font = oldFont;
  972. var ratio = width / 100;
  973. var widthFraction = ratio * boxHeight / boxWidth;
  974. if (widthFraction > 1) { /* too wide with current aspect ratio */
  975. boxHeight = Math.floor(boxHeight / widthFraction);
  976. }
  977. return Math.floor(boxHeight / this._options.spacing);
  978. }
  979. ROT.Display.Rect.prototype.eventToPosition = function(x, y) {
  980. return [Math.floor(x/this._spacingX), Math.floor(y/this._spacingY)];
  981. }
  982. /**
  983. * @class Hexagonal backend
  984. * @private
  985. */
  986. ROT.Display.Hex = function(context) {
  987. ROT.Display.Backend.call(this, context);
  988. this._spacingX = 0;
  989. this._spacingY = 0;
  990. this._hexSize = 0;
  991. this._options = {};
  992. }
  993. ROT.Display.Hex.extend(ROT.Display.Backend);
  994. ROT.Display.Hex.prototype.compute = function(options) {
  995. this._options = options;
  996. var charWidth = Math.ceil(this._context.measureText("W").width);
  997. this._hexSize = Math.floor(options.spacing * (options.fontSize + charWidth/Math.sqrt(3)) / 2);
  998. this._spacingX = this._hexSize * Math.sqrt(3) / 2;
  999. this._spacingY = this._hexSize * 1.5;
  1000. this._context.canvas.width = Math.ceil( (options.width + 1) * this._spacingX );
  1001. this._context.canvas.height = Math.ceil( (options.height - 1) * this._spacingY + 2*this._hexSize );
  1002. }
  1003. ROT.Display.Hex.prototype.draw = function(data, clearBefore) {
  1004. var x = data[0];
  1005. var y = data[1];
  1006. var ch = data[2];
  1007. var fg = data[3];
  1008. var bg = data[4];
  1009. var cx = (x+1) * this._spacingX;
  1010. var cy = y * this._spacingY + this._hexSize;
  1011. if (clearBefore) {
  1012. this._context.fillStyle = bg;
  1013. this._fill(cx, cy);
  1014. }
  1015. if (!ch) { return; }
  1016. this._context.fillStyle = fg;
  1017. var chars = [].concat(ch);
  1018. for (var i=0;i<chars.length;i++) {
  1019. this._context.fillText(chars[i], cx, cy);
  1020. }
  1021. }
  1022. ROT.Display.Hex.prototype.computeSize = function(availWidth, availHeight) {
  1023. var width = Math.floor(availWidth / this._spacingX) - 1;
  1024. var height = Math.floor((availHeight - 2*this._hexSize) / this._spacingY + 1);
  1025. return [width, height];
  1026. }
  1027. ROT.Display.Hex.prototype.computeFontSize = function(availWidth, availHeight) {
  1028. var hexSizeWidth = 2*availWidth / ((this._options.width+1) * Math.sqrt(3)) - 1;
  1029. var hexSizeHeight = availHeight / (2 + 1.5*(this._options.height-1));
  1030. var hexSize = Math.min(hexSizeWidth, hexSizeHeight);
  1031. /* compute char ratio */
  1032. var oldFont = this._context.font;
  1033. this._context.font = "100px " + this._options.fontFamily;
  1034. var width = Math.ceil(this._context.measureText("W").width);
  1035. this._context.font = oldFont;
  1036. var ratio = width / 100;
  1037. hexSize = Math.floor(hexSize)+1; /* closest larger hexSize */
  1038. var fontSize = 2*hexSize / (this._options.spacing * (1 + ratio / Math.sqrt(3)));
  1039. /* closest smaller fontSize */
  1040. return Math.ceil(fontSize)-1;
  1041. }
  1042. ROT.Display.Hex.prototype.eventToPosition = function(x, y) {
  1043. var height = this._context.canvas.height / this._options.height;
  1044. y = Math.floor(y/height);
  1045. if (y.mod(2)) { /* odd row */
  1046. x -= this._spacingX;
  1047. x = 1 + 2*Math.floor(x/(2*this._spacingX));
  1048. } else {
  1049. x = 2*Math.floor(x/(2*this._spacingX));
  1050. }
  1051. return [x, y];
  1052. }
  1053. ROT.Display.Hex.prototype._fill = function(cx, cy) {
  1054. var a = this._hexSize;
  1055. var b = this._options.border;
  1056. this._context.beginPath();
  1057. this._context.moveTo(cx, cy-a+b);
  1058. this._context.lineTo(cx + this._spacingX - b, cy-a/2+b);
  1059. this._context.lineTo(cx + this._spacingX - b, cy+a/2-b);
  1060. this._context.lineTo(cx, cy+a-b);
  1061. this._context.lineTo(cx - this._spacingX + b, cy+a/2-b);
  1062. this._context.lineTo(cx - this._spacingX + b, cy-a/2+b);
  1063. this._context.lineTo(cx, cy-a+b);
  1064. this._context.fill();
  1065. }
  1066. /**
  1067. * @class Tile backend
  1068. * @private
  1069. */
  1070. ROT.Display.Tile = function(context) {
  1071. ROT.Display.Rect.call(this, context);
  1072. this._options = {};
  1073. }
  1074. ROT.Display.Tile.extend(ROT.Display.Rect);
  1075. ROT.Display.Tile.prototype.compute = function(options) {
  1076. this._options = options;
  1077. this._context.canvas.width = options.width * options.tileWidth;
  1078. this._context.canvas.height = options.height * options.tileHeight;
  1079. }
  1080. ROT.Display.Tile.prototype.draw = function(data, clearBefore) {
  1081. var x = data[0];
  1082. var y = data[1];
  1083. var ch = data[2];
  1084. var fg = data[3];
  1085. var bg = data[4];
  1086. var tileWidth = this._options.tileWidth;
  1087. var tileHeight = this._options.tileHeight;
  1088. if (clearBefore) {
  1089. var b = this._options.border;
  1090. this._context.fillStyle = bg;
  1091. this._context.fillRect(x*tileWidth, y*tileHeight, tileWidth, tileHeight);
  1092. }
  1093. if (!ch) { return; }
  1094. var chars = [].concat(ch);
  1095. for (var i=0;i<chars.length;i++) {
  1096. var tile = this._options.tileMap[chars[i]];
  1097. if (!tile) { throw new Error("Char '" + chars[i] + "' not found in tileMap"); }
  1098. this._context.drawImage(
  1099. this._options.tileSet,
  1100. tile[0], tile[1], tileWidth, tileHeight,
  1101. x*tileWidth, y*tileHeight, tileWidth, tileHeight
  1102. );
  1103. }
  1104. }
  1105. ROT.Display.Tile.prototype.computeSize = function(availWidth, availHeight) {
  1106. var width = Math.floor(availWidth / this._options.tileWidth);
  1107. var height = Math.floor(availHeight / this._options.tileHeight);
  1108. return [width, height];
  1109. }
  1110. ROT.Display.Tile.prototype.computeFontSize = function(availWidth, availHeight) {
  1111. var width = Math.floor(availWidth / this._options.width);
  1112. var height = Math.floor(availHeight / this._options.height);
  1113. return [width, height];
  1114. }
  1115. /**
  1116. * @namespace
  1117. * This code is an implementation of Alea algorithm; (C) 2010 Johannes Baagøe.
  1118. * Alea is licensed according to the http://en.wikipedia.org/wiki/MIT_License.
  1119. */
  1120. ROT.RNG = {
  1121. /**
  1122. * @returns {number}
  1123. */
  1124. getSeed: function() {
  1125. return this._seed;
  1126. },
  1127. /**
  1128. * @param {number} seed Seed the number generator
  1129. */
  1130. setSeed: function(seed) {
  1131. seed = (seed < 1 ? 1/seed : seed);
  1132. this._seed = seed;
  1133. this._s0 = (seed >>> 0) * this._frac;
  1134. seed = (seed*69069 + 1) >>> 0;
  1135. this._s1 = seed * this._frac;
  1136. seed = (seed*69069 + 1) >>> 0;
  1137. this._s2 = seed * this._frac;
  1138. this._c = 1;
  1139. return this;
  1140. },
  1141. /**
  1142. * @returns {float} Pseudorandom value [0,1), uniformly distributed
  1143. */
  1144. getUniform: function() {
  1145. var t = 2091639 * this._s0 + this._c * this._frac;
  1146. this._s0 = this._s1;
  1147. this._s1 = this._s2;
  1148. this._c = t | 0;
  1149. this._s2 = t - this._c;
  1150. return this._s2;
  1151. },
  1152. /**
  1153. * @param {float} [mean=0] Mean value
  1154. * @param {float} [stddev=1] Standard deviation. ~95% of the absolute values will be lower than 2*stddev.
  1155. * @returns {float} A normally distributed pseudorandom value
  1156. */
  1157. getNormal: function(mean, stddev) {
  1158. do {
  1159. var u = 2*this.getUniform()-1;
  1160. var v = 2*this.getUniform()-1;
  1161. var r = u*u + v*v;
  1162. } while (r > 1 || r == 0);
  1163. var gauss = u * Math.sqrt(-2*Math.log(r)/r);
  1164. return (mean || 0) + gauss*(stddev || 1);
  1165. },
  1166. /**
  1167. * @returns {int} Pseudorandom value [1,100] inclusive, uniformly distributed
  1168. */
  1169. getPercentage: function() {
  1170. return 1 + Math.floor(this.getUniform()*100);
  1171. },
  1172. /**
  1173. * @param {object} data key=whatever, value=weight (relative probability)
  1174. * @returns {string} whatever
  1175. */
  1176. getWeightedValue: function(data) {
  1177. var avail = [];
  1178. var total = 0;
  1179. for (var id in data) {
  1180. total += data[id];
  1181. }
  1182. var random = Math.floor(this.getUniform()*total);
  1183. var part = 0;
  1184. for (var id in data) {
  1185. part += data[id];
  1186. if (random < part) { return id; }
  1187. }
  1188. return null;
  1189. },
  1190. /**
  1191. * Get RNG state. Useful for storing the state and re-setting it via setState.
  1192. * @returns {?} Internal state
  1193. */
  1194. getState: function() {
  1195. return [this._s0, this._s1, this._s2, this._c];
  1196. },
  1197. /**
  1198. * Set a previously retrieved state.
  1199. * @param {?} state
  1200. */
  1201. setState: function(state) {
  1202. this._s0 = state[0];
  1203. this._s1 = state[1];
  1204. this._s2 = state[2];
  1205. this._c = state[3];
  1206. return this;
  1207. },
  1208. _s0: 0,
  1209. _s1: 0,
  1210. _s2: 0,
  1211. _c: 0,
  1212. _frac: 2.3283064365386963e-10 /* 2^-32 */
  1213. }
  1214. ROT.RNG.setSeed(Date.now());
  1215. /**
  1216. * @class (Markov process)-based string generator.
  1217. * Copied from a <a href="http://www.roguebasin.roguelikedevelopment.org/index.php?title=Names_from_a_high_order_Markov_Process_and_a_simplified_Katz_back-off_scheme">RogueBasin article</a>.
  1218. * Offers configurable order and prior.
  1219. * @param {object} [options]
  1220. * @param {bool} [options.words=false] Use word mode?
  1221. * @param {int} [options.order=3]
  1222. * @param {float} [options.prior=0.001]
  1223. */
  1224. ROT.StringGenerator = function(options) {
  1225. this._options = {
  1226. words: false,
  1227. order: 3,
  1228. prior: 0.001
  1229. }
  1230. for (var p in options) { this._options[p] = options[p]; }
  1231. this._boundary = String.fromCharCode(0);
  1232. this._suffix = this._boundary;
  1233. this._prefix = [];
  1234. for (var i=0;i<this._options.order;i++) { this._prefix.push(this._boundary); }
  1235. this._priorValues = {};
  1236. this._priorValues[this._boundary] = this._options.prior;
  1237. this._data = {};
  1238. }
  1239. /**
  1240. * Remove all learning data
  1241. */
  1242. ROT.StringGenerator.prototype.clear = function() {
  1243. this._data = {};
  1244. this._priorValues = {};
  1245. }
  1246. /**
  1247. * @returns {string} Generated string
  1248. */
  1249. ROT.StringGenerator.prototype.generate = function() {
  1250. var result = [this._sample(this._prefix)];
  1251. while (result[result.length-1] != this._boundary) {
  1252. result.push(this._sample(result));
  1253. }
  1254. return this._join(result.slice(0, -1));
  1255. }
  1256. /**
  1257. * Observe (learn) a string from a training set
  1258. */
  1259. ROT.StringGenerator.prototype.observe = function(string) {
  1260. var tokens = this._split(string);
  1261. for (var i=0; i<tokens.length; i++) {
  1262. this._priorValues[tokens[i]] = this._options.prior;
  1263. }
  1264. tokens = this._prefix.concat(tokens).concat(this._suffix); /* add boundary symbols */
  1265. for (var i=this._options.order; i<tokens.length; i++) {
  1266. var context = tokens.slice(i-this._options.order, i);
  1267. var event = tokens[i];
  1268. for (var j=0; j<context.length; j++) {
  1269. var subcontext = context.slice(j);
  1270. this._observeEvent(subcontext, event);
  1271. }
  1272. }
  1273. }
  1274. ROT.StringGenerator.prototype.getStats = function() {
  1275. var parts = [];
  1276. var priorCount = 0;
  1277. for (var p in this._priorValues) { priorCount++; }
  1278. priorCount--; /* boundary */
  1279. parts.push("distinct samples: " + priorCount);
  1280. var dataCount = 0;
  1281. var eventCount = 0;
  1282. for (var p in this._data) {
  1283. dataCount++;
  1284. for (var key in this._data[p]) {
  1285. eventCount++;
  1286. }
  1287. }
  1288. parts.push("dictionary size (contexts): " + dataCount);
  1289. parts.push("dictionary size (events): " + eventCount);
  1290. return parts.join(", ");
  1291. }
  1292. /**
  1293. * @param {string}
  1294. * @returns {string[]}
  1295. */
  1296. ROT.StringGenerator.prototype._split = function(str) {
  1297. return str.split(this._options.words ? /\s+/ : "");
  1298. }
  1299. /**
  1300. * @param {string[]}
  1301. * @returns {string}
  1302. */
  1303. ROT.StringGenerator.prototype._join = function(arr) {
  1304. return arr.join(this._options.words ? " " : "");
  1305. }
  1306. /**
  1307. * @param {string[]} context
  1308. * @param {string} event
  1309. */
  1310. ROT.StringGenerator.prototype._observeEvent = function(context, event) {
  1311. var key = this._join(context);
  1312. if (!(key in this._data)) { this._data[key] = {}; }
  1313. var data = this._data[key];
  1314. if (!(event in data)) { data[event] = 0; }
  1315. data[event]++;
  1316. }
  1317. /**
  1318. * @param {string[]}
  1319. * @returns {string}
  1320. */
  1321. ROT.StringGenerator.prototype._sample = function(context) {
  1322. context = this._backoff(context);
  1323. var key = this._join(context);
  1324. var data = this._data[key];
  1325. var available = {};
  1326. if (this._options.prior) {
  1327. for (var event in this._priorValues) { available[event] = this._priorValues[event]; }
  1328. for (var event in data) { available[event] += data[event]; }
  1329. } else {
  1330. available = data;
  1331. }
  1332. return this._pickRandom(available);
  1333. }
  1334. /**
  1335. * @param {string[]}
  1336. * @returns {string[]}
  1337. */
  1338. ROT.StringGenerator.prototype._backoff = function(context) {
  1339. if (context.length > this._options.order) {
  1340. context = context.slice(-this._options.order);
  1341. } else if (context.length < this._options.order) {
  1342. context = this._prefix.slice(0, this._options.order - context.length).concat(context);
  1343. }
  1344. while (!(this._join(context) in this._data) && context.length > 0) { context = context.slice(1); }
  1345. return context;
  1346. }
  1347. ROT.StringGenerator.prototype._pickRandom = function(data) {
  1348. var total = 0;
  1349. for (var id in data) {
  1350. total += data[id];
  1351. }
  1352. var random = ROT.RNG.getUniform()*total;
  1353. var part = 0;
  1354. for (var id in data) {
  1355. part += data[id];
  1356. if (random < part) { return id; }
  1357. }
  1358. }
  1359. /**
  1360. * @class Generic event queue: stores events and retrieves them based on their time
  1361. */
  1362. ROT.EventQueue = function() {
  1363. this._time = 0;
  1364. this._events = [];
  1365. this._eventTimes = [];
  1366. }
  1367. /**
  1368. * @returns {number} Elapsed time
  1369. */
  1370. ROT.EventQueue.prototype.getTime = function() {
  1371. return this._time;
  1372. }
  1373. /**
  1374. * Clear all scheduled events
  1375. */
  1376. ROT.EventQueue.prototype.clear = function() {
  1377. this._events = [];
  1378. this._eventTimes = [];
  1379. return this;
  1380. }
  1381. /**
  1382. * @param {?} event
  1383. * @param {number} time
  1384. */
  1385. ROT.EventQueue.prototype.add = function(event, time) {
  1386. var index = this._events.length;
  1387. for (var i=0;i<this._eventTimes.length;i++) {
  1388. if (this._eventTimes[i] > time) {
  1389. index = i;
  1390. break;
  1391. }
  1392. }
  1393. this._events.splice(index, 0, event);
  1394. this._eventTimes.splice(index, 0, time);
  1395. }
  1396. /**
  1397. * Locates the nearest event, advances time if necessary. Returns that event and removes it from the queue.
  1398. * @returns {? || null} The event previously added by addEvent, null if no event available
  1399. */
  1400. ROT.EventQueue.prototype.get = function() {
  1401. if (!this._events.length) { return null; }
  1402. var time = this._eventTimes.splice(0, 1)[0];
  1403. if (time > 0) { /* advance */
  1404. this._time += time;
  1405. for (var i=0;i<this._eventTimes.length;i++) { this._eventTimes[i] -= time; }
  1406. }
  1407. return this._events.splice(0, 1)[0];
  1408. }
  1409. /**
  1410. * Remove an event from the queue
  1411. * @param {?} event
  1412. * @returns {bool} success?
  1413. */
  1414. ROT.EventQueue.prototype.remove = function(event) {
  1415. var index = this._events.indexOf(event);
  1416. if (index == -1) { return false }
  1417. this._remove(index);
  1418. return true;
  1419. }
  1420. /**
  1421. * Remove an event from the queue
  1422. * @param {int} index
  1423. */
  1424. ROT.EventQueue.prototype._remove = function(index) {
  1425. this._events.splice(index, 1);
  1426. this._eventTimes.splice(index, 1);
  1427. }
  1428. /**
  1429. * @class Abstract scheduler
  1430. */
  1431. ROT.Scheduler = function() {
  1432. this._queue = new ROT.EventQueue();
  1433. this._repeat = [];
  1434. this._current = null;
  1435. }
  1436. /**
  1437. * @see ROT.EventQueue#getTime
  1438. */
  1439. ROT.Scheduler.prototype.getTime = function() {
  1440. return this._queue.getTime();
  1441. }
  1442. /**
  1443. * @param {?} item
  1444. * @param {bool} repeat
  1445. */
  1446. ROT.Scheduler.prototype.add = function(item, repeat) {
  1447. if (repeat) { this._repeat.push(item); }
  1448. return this;
  1449. }
  1450. /**
  1451. * Clear all items
  1452. */
  1453. ROT.Scheduler.prototype.clear = function() {
  1454. this._queue.clear();
  1455. this._repeat = [];
  1456. this._current = null;
  1457. return this;
  1458. }
  1459. /**
  1460. * Remove a previously added item
  1461. * @param {?} item
  1462. * @returns {bool} successful?
  1463. */
  1464. ROT.Scheduler.prototype.remove = function(item) {
  1465. var result = this._queue.remove(item);
  1466. var index = this._repeat.indexOf(item);
  1467. if (index != -1) { this._repeat.splice(index, 1); }
  1468. if (this._current == item) { this._current = null; }
  1469. return result;
  1470. }
  1471. /**
  1472. * Schedule next item
  1473. * @returns {?}
  1474. */
  1475. ROT.Scheduler.prototype.next = function() {
  1476. this._current = this._queue.get();
  1477. return this._current;
  1478. }
  1479. /**
  1480. * @class Simple fair scheduler (round-robin style)
  1481. * @augments ROT.Scheduler
  1482. */
  1483. ROT.Scheduler.Simple = function() {
  1484. ROT.Scheduler.call(this);
  1485. }
  1486. ROT.Scheduler.Simple.extend(ROT.Scheduler);
  1487. /**
  1488. * @see ROT.Scheduler#add
  1489. */
  1490. ROT.Scheduler.Simple.prototype.add = function(item, repeat) {
  1491. this._queue.add(item, 0);
  1492. return ROT.Scheduler.prototype.add.call(this, item, repeat);
  1493. }
  1494. /**
  1495. * @see ROT.Scheduler#next
  1496. */
  1497. ROT.Scheduler.Simple.prototype.next = function() {
  1498. if (this._current && this._repeat.indexOf(this._current) != -1) {
  1499. this._queue.add(this._current, 0);
  1500. }
  1501. return ROT.Scheduler.prototype.next.call(this);
  1502. }
  1503. /**
  1504. * @class Speed-based scheduler
  1505. * @augments ROT.Scheduler
  1506. */
  1507. ROT.Scheduler.Speed = function() {
  1508. ROT.Scheduler.call(this);
  1509. }
  1510. ROT.Scheduler.Speed.extend(ROT.Scheduler);
  1511. /**
  1512. * @param {object} item anything with "getSpeed" method
  1513. * @param {bool} repeat
  1514. * @see ROT.Scheduler#add
  1515. */
  1516. ROT.Scheduler.Speed.prototype.add = function(item, repeat) {
  1517. this._queue.add(item, 1/item.getSpeed());
  1518. return ROT.Scheduler.prototype.add.call(this, item, repeat);
  1519. }
  1520. /**
  1521. * @see ROT.Scheduler#next
  1522. */
  1523. ROT.Scheduler.Speed.prototype.next = function() {
  1524. if (this._current && this._repeat.indexOf(this._current) != -1) {
  1525. this._queue.add(this._current, 1/this._current.getSpeed());
  1526. }
  1527. return ROT.Scheduler.prototype.next.call(this);
  1528. }
  1529. /**
  1530. * @class Action-based scheduler
  1531. * @augments ROT.Scheduler
  1532. */
  1533. ROT.Scheduler.Action = function() {
  1534. ROT.Scheduler.call(this);
  1535. this._defaultDuration = 1; /* for newly added */
  1536. this._duration = this._defaultDuration; /* for this._current */
  1537. }
  1538. ROT.Scheduler.Action.extend(ROT.Scheduler);
  1539. /**
  1540. * @param {object} item
  1541. * @param {bool} repeat
  1542. * @param {number} [time=1]
  1543. * @see ROT.Scheduler#add
  1544. */
  1545. ROT.Scheduler.Action.prototype.add = function(item, repeat, time) {
  1546. this._queue.add(item, time || this._defaultDuration);
  1547. return ROT.Scheduler.prototype.add.call(this, item, repeat);
  1548. }
  1549. ROT.Scheduler.Action.prototype.clear = function() {
  1550. this._duration = this._defaultDuration;
  1551. return ROT.Scheduler.prototype.clear.call(this);
  1552. }
  1553. ROT.Scheduler.Action.prototype.remove = function(item) {
  1554. if (item == this._current) { this._duration = this._defaultDuration; }
  1555. return ROT.Scheduler.prototype.remove.call(this, item);
  1556. }
  1557. /**
  1558. * @see ROT.Scheduler#next
  1559. */
  1560. ROT.Scheduler.Action.prototype.next = function() {
  1561. if (this._current && this._repeat.indexOf(this._current) != -1) {
  1562. this._queue.add(this._current, this._duration || this._defaultDuration);
  1563. this._duration = this._defaultDuration;
  1564. }
  1565. return ROT.Scheduler.prototype.next.call(this);
  1566. }
  1567. /**
  1568. * Set duration for the active item
  1569. */
  1570. ROT.Scheduler.Action.prototype.setDuration = function(time) {
  1571. if (this._current) { this._duration = time; }
  1572. return this;
  1573. }
  1574. /**
  1575. * @class Asynchronous main loop
  1576. * @param {ROT.Scheduler} scheduler
  1577. */
  1578. ROT.Engine = function(scheduler) {
  1579. this._scheduler = scheduler;
  1580. this._lock = 1;
  1581. }
  1582. /**
  1583. * Start the main loop. When this call returns, the loop is locked.
  1584. */
  1585. ROT.Engine.prototype.start = function() {
  1586. return this.unlock();
  1587. }
  1588. /**
  1589. * Interrupt the engine by an asynchronous action
  1590. */
  1591. ROT.Engine.prototype.lock = function() {
  1592. this._lock++;
  1593. }
  1594. /**
  1595. * Resume execution (paused by a previous lock)
  1596. */
  1597. ROT.Engine.prototype.unlock = function() {
  1598. if (!this._lock) { throw new Error("Cannot unlock unlocked engine"); }
  1599. this._lock--;
  1600. while (!this._lock) {
  1601. var actor = this._scheduler.next();
  1602. if (!actor) { return this.lock(); } /* no actors */
  1603. actor.act();
  1604. }
  1605. return this;
  1606. }
  1607. /**
  1608. * @class Base map generator
  1609. * @param {int} [width=ROT.DEFAULT_WIDTH]
  1610. * @param {int} [height=ROT.DEFAULT_HEIGHT]
  1611. */
  1612. ROT.Map = function(width, height) {
  1613. this._width = width || ROT.DEFAULT_WIDTH;
  1614. this._height = height || ROT.DEFAULT_HEIGHT;
  1615. };
  1616. ROT.Map.prototype.create = function(callback) {}
  1617. ROT.Map.prototype._fillMap = function(value) {
  1618. var map = [];
  1619. for (var i=0;i<this._width;i++) {
  1620. map.push([]);
  1621. for (var j=0;j<this._height;j++) { map[i].push(value); }
  1622. }
  1623. return map;
  1624. }
  1625. /**
  1626. * @class Simple empty rectangular room
  1627. * @augments ROT.Map
  1628. */
  1629. ROT.Map.Arena = function(width, height) {
  1630. ROT.Map.call(this, width, height);
  1631. }
  1632. ROT.Map.Arena.extend(ROT.Map);
  1633. ROT.Map.Arena.prototype.create = function(callback) {
  1634. var w = this._width-1;
  1635. var h = this._height-1;
  1636. for (var i=0;i<=w;i++) {
  1637. for (var j=0;j<=h;j++) {
  1638. var empty = (i && j && i<w && j<h);
  1639. callback(i, j, empty ? 0 : 1);
  1640. }
  1641. }
  1642. return this;
  1643. }
  1644. /**
  1645. * @class Recursively divided maze, http://en.wikipedia.org/wiki/Maze_generation_algorithm#Recursive_division_method
  1646. * @augments ROT.Map
  1647. */
  1648. ROT.Map.DividedMaze = function(width, height) {
  1649. ROT.Map.call(this, width, height);
  1650. this._stack = [];
  1651. }
  1652. ROT.Map.DividedMaze.extend(ROT.Map);
  1653. ROT.Map.DividedMaze.prototype.create = function(callback) {
  1654. var w = this._width;
  1655. var h = this._height;
  1656. this._map = [];
  1657. for (var i=0;i<w;i++) {
  1658. this._map.push([]);
  1659. for (var j=0;j<h;j++) {
  1660. var border = (i == 0 || j == 0 || i+1 == w || j+1 == h);
  1661. this._map[i].push(border ? 1 : 0);
  1662. }
  1663. }
  1664. this._stack = [
  1665. [1, 1, w-2, h-2]
  1666. ];
  1667. this._process();
  1668. for (var i=0;i<w;i++) {
  1669. for (var j=0;j<h;j++) {
  1670. callback(i, j, this._map[i][j]);
  1671. }
  1672. }
  1673. this._map = null;
  1674. return this;
  1675. }
  1676. ROT.Map.DividedMaze.prototype._process = function() {
  1677. while (this._stack.length) {
  1678. var room = this._stack.shift(); /* [left, top, right, bottom] */
  1679. this._partitionRoom(room);
  1680. }
  1681. }
  1682. ROT.Map.DividedMaze.prototype._partitionRoom = function(room) {
  1683. var availX = [];
  1684. var availY = [];
  1685. for (var i=room[0]+1;i<room[2];i++) {
  1686. var top = this._map[i][room[1]-1];
  1687. var bottom = this._map[i][room[3]+1];
  1688. if (top && bottom && !(i % 2)) { availX.push(i); }
  1689. }
  1690. for (var j=room[1]+1;j<room[3];j++) {
  1691. var left = this._map[room[0]-1][j];
  1692. var right = this._map[room[2]+1][j];
  1693. if (left && right && !(j % 2)) { availY.push(j); }
  1694. }
  1695. if (!availX.length || !availY.length) { return; }
  1696. var x = availX.random();
  1697. var y = availY.random();
  1698. this._map[x][y] = 1;
  1699. var walls = [];
  1700. var w = []; walls.push(w); /* left part */
  1701. for (var i=room[0]; i<x; i++) {
  1702. this._map[i][y] = 1;
  1703. w.push([i, y]);
  1704. }
  1705. var w = []; walls.push(w); /* right part */
  1706. for (var i=x+1; i<=room[2]; i++) {
  1707. this._map[i][y] = 1;
  1708. w.push([i, y]);
  1709. }
  1710. var w = []; walls.push(w); /* top part */
  1711. for (var j=room[1]; j<y; j++) {
  1712. this._map[x][j] = 1;
  1713. w.push([x, j]);
  1714. }
  1715. var w = []; walls.push(w); /* bottom part */
  1716. for (var j=y+1; j<=room[3]; j++) {
  1717. this._map[x][j] = 1;
  1718. w.push([x, j]);
  1719. }
  1720. var solid = walls.random();
  1721. for (var i=0;i<walls.length;i++) {
  1722. var w = walls[i];
  1723. if (w == solid) { continue; }
  1724. var hole = w.random();
  1725. this._map[hole[0]][hole[1]] = 0;
  1726. }
  1727. this._stack.push([room[0], room[1], x-1, y-1]); /* left top */
  1728. this._stack.push([x+1, room[1], room[2], y-1]); /* right top */
  1729. this._stack.push([room[0], y+1, x-1, room[3]]); /* left bottom */
  1730. this._stack.push([x+1, y+1, room[2], room[3]]); /* right bottom */
  1731. }
  1732. /**
  1733. * @class Icey's Maze generator
  1734. * See http://www.roguebasin.roguelikedevelopment.org/index.php?title=Simple_maze for explanation
  1735. * @augments ROT.Map
  1736. */
  1737. ROT.Map.IceyMaze = function(width, height, regularity) {
  1738. ROT.Map.call(this, width, height);
  1739. this._regularity = regularity || 0;
  1740. }
  1741. ROT.Map.IceyMaze.extend(ROT.Map);
  1742. ROT.Map.IceyMaze.prototype.create = function(callback) {
  1743. var width = this._width;
  1744. var height = this._height;
  1745. var map = this._fillMap(1);
  1746. width -= (width % 2 ? 1 : 2);
  1747. height -= (height % 2 ? 1 : 2);
  1748. var cx = 0;
  1749. var cy = 0;
  1750. var nx = 0;
  1751. var ny = 0;
  1752. var done = 0;
  1753. var blocked = false;
  1754. var dirs = [
  1755. [0, 0],
  1756. [0, 0],
  1757. [0, 0],
  1758. [0, 0]
  1759. ];
  1760. do {
  1761. cx = 1 + 2*Math.floor(ROT.RNG.getUniform()*(width-1) / 2);
  1762. cy = 1 + 2*Math.floor(ROT.RNG.getUniform()*(height-1) / 2);
  1763. if (!done) { map[cx][cy] = 0; }
  1764. if (!map[cx][cy]) {
  1765. this._randomize(dirs);
  1766. do {
  1767. if (Math.floor(ROT.RNG.getUniform()*(this._regularity+1)) == 0) { this._randomize(dirs); }
  1768. blocked = true;
  1769. for (var i=0;i<4;i++) {
  1770. nx = cx + dirs[i][0]*2;
  1771. ny = cy + dirs[i][1]*2;
  1772. if (this._isFree(map, nx, ny, width, height)) {
  1773. map[nx][ny] = 0;
  1774. map[cx + dirs[i][0]][cy + dirs[i][1]] = 0;
  1775. cx = nx;
  1776. cy = ny;
  1777. blocked = false;
  1778. done++;
  1779. break;
  1780. }
  1781. }
  1782. } while (!blocked);
  1783. }
  1784. } while (done+1 < width*height/4);
  1785. for (var i=0;i<this._width;i++) {
  1786. for (var j=0;j<this._height;j++) {
  1787. callback(i, j, map[i][j]);
  1788. }
  1789. }
  1790. this._map = null;
  1791. return this;
  1792. }
  1793. ROT.Map.IceyMaze.prototype._randomize = function(dirs) {
  1794. for (var i=0;i<4;i++) {
  1795. dirs[i][0] = 0;
  1796. dirs[i][1] = 0;
  1797. }
  1798. switch (Math.floor(ROT.RNG.getUniform()*4)) {
  1799. case 0:
  1800. dirs[0][0] = -1; dirs[1][0] = 1;
  1801. dirs[2][1] = -1; dirs[3][1] = 1;
  1802. break;
  1803. case 1:
  1804. dirs[3][0] = -1; dirs[2][0] = 1;
  1805. dirs[1][1] = -1; dirs[0][1] = 1;
  1806. break;
  1807. case 2:
  1808. dirs[2][0] = -1; dirs[3][0] = 1;
  1809. dirs[0][1] = -1; dirs[1][1] = 1;
  1810. break;
  1811. case 3:
  1812. dirs[1][0] = -1; dirs[0][0] = 1;
  1813. dirs[3][1] = -1; dirs[2][1] = 1;
  1814. break;
  1815. }
  1816. }
  1817. ROT.Map.IceyMaze.prototype._isFree = function(map, x, y, width, height) {
  1818. if (x < 1 || y < 1 || x >= width || y >= height) { return false; }
  1819. return map[x][y];
  1820. }
  1821. /**
  1822. * @class Maze generator - Eller's algorithm
  1823. * See http://homepages.cwi.nl/~tromp/maze.html for explanation
  1824. * @augments ROT.Map
  1825. */
  1826. ROT.Map.EllerMaze = function(width, height) {
  1827. ROT.Map.call(this, width, height);
  1828. }
  1829. ROT.Map.EllerMaze.extend(ROT.Map);
  1830. ROT.Map.EllerMaze.prototype.create = function(callback) {
  1831. var map = this._fillMap(1);
  1832. var w = Math.ceil((this._width-2)/2);
  1833. var rand = 9/24;
  1834. var L = [];
  1835. var R = [];
  1836. for (var i=0;i<w;i++) {
  1837. L.push(i);
  1838. R.push(i);
  1839. }
  1840. L.push(w-1); /* fake stop-block at the right side */
  1841. for (var j=1;j+3<this._height;j+=2) {
  1842. /* one row */
  1843. for (var i=0;i<w;i++) {
  1844. /* cell coords (will be always empty) */
  1845. var x = 2*i+1;
  1846. var y = j;
  1847. map[x][y] = 0;
  1848. /* right connection */
  1849. if (i != L[i+1] && ROT.RNG.getUniform() > rand) {
  1850. this._addToList(i, L, R);
  1851. map[x+1][y] = 0;
  1852. }
  1853. /* bottom connection */
  1854. if (i != L[i] && ROT.RNG.getUniform() > rand) {
  1855. /* remove connection */
  1856. this._removeFromList(i, L, R);
  1857. } else {
  1858. /* create connection */
  1859. map[x][y+1] = 0;
  1860. }
  1861. }
  1862. }
  1863. /* last row */
  1864. for (var i=0;i<w;i++) {
  1865. /* cell coords (will be always empty) */
  1866. var x = 2*i+1;
  1867. var y = j;
  1868. map[x][y] = 0;
  1869. /* right connection */
  1870. if (i != L[i+1] && (i == L[i] || ROT.RNG.getUniform() > rand)) {
  1871. /* dig right also if the cell is separated, so it gets connected to the rest of maze */
  1872. this._addToList(i, L, R);
  1873. map[x+1][y] = 0;
  1874. }
  1875. this._removeFromList(i, L, R);
  1876. }
  1877. for (var i=0;i<this._width;i++) {
  1878. for (var j=0;j<this._height;j++) {
  1879. callback(i, j, map[i][j]);
  1880. }
  1881. }
  1882. return this;
  1883. }
  1884. /**
  1885. * Remove "i" from its list
  1886. */
  1887. ROT.Map.EllerMaze.prototype._removeFromList = function(i, L, R) {
  1888. R[L[i]] = R[i];
  1889. L[R[i]] = L[i];
  1890. R[i] = i;
  1891. L[i] = i;
  1892. }
  1893. /**
  1894. * Join lists with "i" and "i+1"
  1895. */
  1896. ROT.Map.EllerMaze.prototype._addToList = function(i, L, R) {
  1897. R[L[i+1]] = R[i];
  1898. L[R[i]] = L[i+1];
  1899. R[i] = i+1;
  1900. L[i+1] = i;
  1901. }
  1902. /**
  1903. * @class Cellular automaton map generator
  1904. * @augments ROT.Map
  1905. * @param {int} [width=ROT.DEFAULT_WIDTH]
  1906. * @param {int} [height=ROT.DEFAULT_HEIGHT]
  1907. * @param {object} [options] Options
  1908. * @param {int[]} [options.born] List of neighbor counts for a new cell to be born in empty space
  1909. * @param {int[]} [options.survive] List of neighbor counts for an existing cell to survive
  1910. * @param {int} [options.topology] Topology 4 or 6 or 8
  1911. */
  1912. ROT.Map.Cellular = function(width, height, options) {
  1913. ROT.Map.call(this, width, height);
  1914. this._options = {
  1915. born: [5, 6, 7, 8],
  1916. survive: [4, 5, 6, 7, 8],
  1917. topology: 8
  1918. };
  1919. for (var p in options) { this._options[p] = options[p]; }
  1920. this._dirs = ROT.DIRS[this._options.topology];
  1921. this._map = this._fillMap(0);
  1922. }
  1923. ROT.Map.Cellular.extend(ROT.Map);
  1924. /**
  1925. * Fill the map with random values
  1926. * @param {float} probability Probability for a cell to become alive; 0 = all empty, 1 = all full
  1927. */
  1928. ROT.Map.Cellular.prototype.randomize = function(probability) {
  1929. for (var i=0;i<this._width;i++) {
  1930. for (var j=0;j<this._height;j++) {
  1931. this._map[i][j] = (ROT.RNG.getUniform() < probability ? 1 : 0);
  1932. }
  1933. }
  1934. return this;
  1935. }
  1936. ROT.Map.Cellular.prototype.set = function(x, y, value) {
  1937. this._map[x][y] = value;
  1938. }
  1939. ROT.Map.Cellular.prototype.create = function(callback) {
  1940. var newMap = this._fillMap(0);
  1941. var born = this._options.born;
  1942. var survive = this._options.survive;
  1943. for (var j=0;j<this._height;j++) {
  1944. var widthStep = 1;
  1945. var widthStart = 0;
  1946. if (this._options.topology == 6) {
  1947. widthStep = 2;
  1948. widthStart = j%2;
  1949. }
  1950. for (var i=widthStart; i<this._width; i+=widthStep) {
  1951. var cur = this._map[i][j];
  1952. var ncount = this._getNeighbors(i, j);
  1953. if (cur && survive.indexOf(ncount) != -1) { /* survive */
  1954. newMap[i][j] = 1;
  1955. } else if (!cur && born.indexOf(ncount) != -1) { /* born */
  1956. newMap[i][j] = 1;
  1957. }
  1958. if (callback) { callback(i, j, newMap[i][j]); }
  1959. }
  1960. }
  1961. this._map = newMap;
  1962. }
  1963. /**
  1964. * Get neighbor count at [i,j] in this._map
  1965. */
  1966. ROT.Map.Cellular.prototype._getNeighbors = function(cx, cy) {
  1967. var result = 0;
  1968. for (var i=0;i<this._dirs.length;i++) {
  1969. var dir = this._dirs[i];
  1970. var x = cx + dir[0];
  1971. var y = cy + dir[1];
  1972. if (x < 0 || x >= this._width || x < 0 || y >= this._width) { continue; }
  1973. result += (this._map[x][y] == 1 ? 1 : 0);
  1974. }
  1975. return result;
  1976. }
  1977. /**
  1978. * @class Dungeon map: has rooms and corridors
  1979. * @augments ROT.Map
  1980. */
  1981. ROT.Map.Dungeon = function(width, height) {
  1982. ROT.Map.call(this, width, height);
  1983. this._rooms = []; /* list of all rooms */
  1984. this._corridors = [];
  1985. }
  1986. ROT.Map.Dungeon.extend(ROT.Map);
  1987. /**
  1988. * Get all generated rooms
  1989. * @returns {ROT.Map.Feature.Room[]}
  1990. */
  1991. ROT.Map.Dungeon.prototype.getRooms = function() {
  1992. return this._rooms;
  1993. }
  1994. /**
  1995. * Get all generated corridors
  1996. * @returns {ROT.Map.Feature.Corridor[]}
  1997. */
  1998. ROT.Map.Dungeon.prototype.getCorridors = function() {
  1999. return this._corridors;
  2000. }
  2001. /**
  2002. * @class Random dungeon generator using human-like digging patterns.
  2003. * Heavily based on Mike Anderson's ideas from the "Tyrant" algo, mentioned at
  2004. * http://www.roguebasin.roguelikedevelopment.org/index.php?title=Dungeon-Building_Algorithm.
  2005. * @augments ROT.Map.Dungeon
  2006. */
  2007. ROT.Map.Digger = function(width, height, options) {
  2008. ROT.Map.Dungeon.call(this, width, height);
  2009. this._options = {
  2010. roomWidth: [3, 9], /* room minimum and maximum width */
  2011. roomHeight: [3, 5], /* room minimum and maximum height */
  2012. corridorLength: [3, 10], /* corridor minimum and maximum length */
  2013. dugPercentage: 0.2, /* we stop after this percentage of level area has been dug out */
  2014. timeLimit: 1000 /* we stop after this much time has passed (msec) */
  2015. }
  2016. for (var p in options) { this._options[p] = options[p]; }
  2017. this._features = {
  2018. "Room": 4,
  2019. "Corridor": 4
  2020. }
  2021. this._featureAttempts = 20; /* how many times do we try to create a feature on a suitable wall */
  2022. this._walls = {}; /* these are available for digging */
  2023. this._digCallback = this._digCallback.bind(this);
  2024. this._canBeDugCallback = this._canBeDugCallback.bind(this);
  2025. this._isWallCallback = this._isWallCallback.bind(this);
  2026. this._priorityWallCallback = this._priorityWallCallback.bind(this);
  2027. }
  2028. ROT.Map.Digger.extend(ROT.Map.Dungeon);
  2029. /**
  2030. * Create a map
  2031. * @see ROT.Map#create
  2032. */
  2033. ROT.Map.Digger.prototype.create = function(callback) {
  2034. this._rooms = [];
  2035. this._corridors = [];
  2036. this._map = this._fillMap(1);
  2037. this._walls = {};
  2038. this._dug = 0;
  2039. var area = (this._width-2) * (this._height-2);
  2040. this._firstRoom();
  2041. var t1 = Date.now();
  2042. do {
  2043. var t2 = Date.now();
  2044. if (t2 - t1 > this._options.timeLimit) { break; }
  2045. /* find a good wall */
  2046. var wall = this._findWall();
  2047. if (!wall) { break; } /* no more walls */
  2048. var parts = wall.split(",");
  2049. var x = parseInt(parts[0]);
  2050. var y = parseInt(parts[1]);
  2051. var dir = this._getDiggingDirection(x, y);
  2052. if (!dir) { continue; } /* this wall is not suitable */
  2053. // console.log("wall", x, y);
  2054. /* try adding a feature */
  2055. var featureAttempts = 0;
  2056. do {
  2057. featureAttempts++;
  2058. if (this._tryFeature(x, y, dir[0], dir[1])) { /* feature added */
  2059. //if (this._rooms.length + this._corridors.length == 2) { this._rooms[0].addDoor(x, y); } /* first room oficially has doors */
  2060. this._removeSurroundingWalls(x, y);
  2061. this._removeSurroundingWalls(x-dir[0], y-dir[1]);
  2062. break;
  2063. }
  2064. } while (featureAttempts < this._featureAttempts);
  2065. var priorityWalls = 0;
  2066. for (var id in this._walls) {
  2067. if (this._walls[id] > 1) { priorityWalls++; }
  2068. }
  2069. } while (this._dug/area < this._options.dugPercentage || priorityWalls); /* fixme number of priority walls */
  2070. this._addDoors();
  2071. if (callback) {
  2072. for (var i=0;i<this._width;i++) {
  2073. for (var j=0;j<this._height;j++) {
  2074. callback(i, j, this._map[i][j]);
  2075. }
  2076. }
  2077. }
  2078. this._walls = {};
  2079. this._map = null;
  2080. return this;
  2081. }
  2082. ROT.Map.Digger.prototype._digCallback = function(x, y, value) {
  2083. if (value == 0 || value == 2) { /* empty */
  2084. this._map[x][y] = 0;
  2085. this._dug++;
  2086. } else { /* wall */
  2087. this._walls[x+","+y] = 1;
  2088. }
  2089. }
  2090. ROT.Map.Digger.prototype._isWallCallback = function(x, y) {
  2091. if (x < 0 || y < 0 || x >= this._width || y >= this._height) { return false; }
  2092. return (this._map[x][y] == 1);
  2093. }
  2094. ROT.Map.Digger.prototype._canBeDugCallback = function(x, y) {
  2095. if (x < 1 || y < 1 || x+1 >= this._width || y+1 >= this._height) { return false; }
  2096. return (this._map[x][y] == 1);
  2097. }
  2098. ROT.Map.Digger.prototype._priorityWallCallback = function(x, y) {
  2099. this._walls[x+","+y] = 2;
  2100. }
  2101. ROT.Map.Digger.prototype._firstRoom = function() {
  2102. var cx = Math.floor(this._width/2);
  2103. var cy = Math.floor(this._height/2);
  2104. var room = ROT.Map.Feature.Room.createRandomCenter(cx, cy, this._options);
  2105. this._rooms.push(room);
  2106. room.create(this._digCallback);
  2107. }
  2108. /**
  2109. * Get a suitable wall
  2110. */
  2111. ROT.Map.Digger.prototype._findWall = function() {
  2112. var prio1 = [];
  2113. var prio2 = [];
  2114. for (var id in this._walls) {
  2115. var prio = this._walls[id];
  2116. if (prio == 2) {
  2117. prio2.push(id);
  2118. } else {
  2119. prio1.push(id);
  2120. }
  2121. }
  2122. var arr = (prio2.length ? prio2 : prio1);
  2123. if (!arr.length) { return null; } /* no walls :/ */
  2124. var id = arr.random();
  2125. delete this._walls[id];
  2126. return id;
  2127. }
  2128. /**
  2129. * Tries adding a feature
  2130. * @returns {bool} was this a successful try?
  2131. */
  2132. ROT.Map.Digger.prototype._tryFeature = function(x, y, dx, dy) {
  2133. var feature = ROT.RNG.getWeightedValue(this._features);
  2134. feature = ROT.Map.Feature[feature].createRandomAt(x, y, dx, dy, this._options);
  2135. if (!feature.isValid(this._isWallCallback, this._canBeDugCallback)) {
  2136. // console.log("not valid");
  2137. // feature.debug();
  2138. return false;
  2139. }
  2140. feature.create(this._digCallback);
  2141. // feature.debug();
  2142. if (feature instanceof ROT.Map.Feature.Room) { this._rooms.push(feature); }
  2143. if (feature instanceof ROT.Map.Feature.Corridor) {
  2144. feature.createPriorityWalls(this._priorityWallCallback);
  2145. this._corridors.push(feature);
  2146. }
  2147. return true;
  2148. }
  2149. ROT.Map.Digger.prototype._removeSurroundingWalls = function(cx, cy) {
  2150. var deltas = ROT.DIRS[4];
  2151. for (var i=0;i<deltas.length;i++) {
  2152. var delta = deltas[i];
  2153. var x = cx + delta[0];
  2154. var y = cy + delta[1];
  2155. delete this._walls[x+","+y];
  2156. var x = cx + 2*delta[0];
  2157. var y = cy + 2*delta[1];
  2158. delete this._walls[x+","+y];
  2159. }
  2160. }
  2161. /**
  2162. * Returns vector in "digging" direction, or false, if this does not exist (or is not unique)
  2163. */
  2164. ROT.Map.Digger.prototype._getDiggingDirection = function(cx, cy) {
  2165. var result = null;
  2166. var deltas = ROT.DIRS[4];
  2167. for (var i=0;i<deltas.length;i++) {
  2168. var delta = deltas[i];
  2169. var x = cx + delta[0];
  2170. var y = cy + delta[1];
  2171. if (x < 0 || y < 0 || x >= this._width || y >= this._width) { return null; }
  2172. if (!this._map[x][y]) { /* there already is another empty neighbor! */
  2173. if (result) { return null; }
  2174. result = delta;
  2175. }
  2176. }
  2177. /* no empty neighbor */
  2178. if (!result) { return null; }
  2179. return [-result[0], -result[1]];
  2180. }
  2181. /**
  2182. * Find empty spaces surrounding rooms, and apply doors.
  2183. */
  2184. ROT.Map.Digger.prototype._addDoors = function() {
  2185. var data = this._map;
  2186. var isWallCallback = function(x, y) {
  2187. return (data[x][y] == 1);
  2188. }
  2189. for (var i = 0; i < this._rooms.length; i++ ) {
  2190. var room = this._rooms[i];
  2191. room.clearDoors();
  2192. room.addDoors(isWallCallback);
  2193. }
  2194. }
  2195. /**
  2196. * @class Dungeon generator which tries to fill the space evenly. Generates independent rooms and tries to connect them.
  2197. * @augments ROT.Map.Dungeon
  2198. */
  2199. ROT.Map.Uniform = function(width, height, options) {
  2200. ROT.Map.Dungeon.call(this, width, height);
  2201. this._options = {
  2202. roomWidth: [3, 9], /* room minimum and maximum width */
  2203. roomHeight: [3, 5], /* room minimum and maximum height */
  2204. roomDugPercentage: 0.1, /* we stop after this percentage of level area has been dug out by rooms */
  2205. timeLimit: 1000 /* we stop after this much time has passed (msec) */
  2206. }
  2207. for (var p in options) { this._options[p] = options[p]; }
  2208. this._roomAttempts = 20; /* new room is created N-times until is considered as impossible to generate */
  2209. this._corridorAttempts = 20; /* corridors are tried N-times until the level is considered as impossible to connect */
  2210. this._connected = []; /* list of already connected rooms */
  2211. this._unconnected = []; /* list of remaining unconnected rooms */
  2212. this._digCallback = this._digCallback.bind(this);
  2213. this._canBeDugCallback = this._canBeDugCallback.bind(this);
  2214. this._isWallCallback = this._isWallCallback.bind(this);
  2215. }
  2216. ROT.Map.Uniform.extend(ROT.Map.Dungeon);
  2217. /**
  2218. * Create a map. If the time limit has been hit, returns null.
  2219. * @see ROT.Map#create
  2220. */
  2221. ROT.Map.Uniform.prototype.create = function(callback) {
  2222. var t1 = Date.now();
  2223. while (1) {
  2224. var t2 = Date.now();
  2225. if (t2 - t1 > this._options.timeLimit) { return null; } /* time limit! */
  2226. this._map = this._fillMap(1);
  2227. this._dug = 0;
  2228. this._rooms = [];
  2229. this._unconnected = [];
  2230. this._generateRooms();
  2231. if (this._generateCorridors()) { break; }
  2232. }
  2233. if (callback) {
  2234. for (var i=0;i<this._width;i++) {
  2235. for (var j=0;j<this._height;j++) {
  2236. callback(i, j, this._map[i][j]);
  2237. }
  2238. }
  2239. }
  2240. return this;
  2241. }
  2242. /**
  2243. * Generates a suitable amount of rooms
  2244. */
  2245. ROT.Map.Uniform.prototype._generateRooms = function() {
  2246. var w = this._width-2;
  2247. var h = this._height-2;
  2248. do {
  2249. var room = this._generateRoom();
  2250. if (this._dug/(w*h) > this._options.roomDugPercentage) { break; } /* achieved requested amount of free space */
  2251. } while (room);
  2252. /* either enough rooms, or not able to generate more of them :) */
  2253. }
  2254. /**
  2255. * Try to generate one room
  2256. */
  2257. ROT.Map.Uniform.prototype._generateRoom = function() {
  2258. var count = 0;
  2259. while (count < this._roomAttempts) {
  2260. count++;
  2261. var room = ROT.Map.Feature.Room.createRandom(this._width, this._height, this._options);
  2262. if (!room.isValid(this._isWallCallback, this._canBeDugCallback)) { continue; }
  2263. room.create(this._digCallback);
  2264. this._rooms.push(room);
  2265. return room;
  2266. }
  2267. /* no room was generated in a given number of attempts */
  2268. return null;
  2269. }
  2270. /**
  2271. * Generates connectors beween rooms
  2272. * @returns {bool} success Was this attempt successfull?
  2273. */
  2274. ROT.Map.Uniform.prototype._generateCorridors = function() {
  2275. var cnt = 0;
  2276. while (cnt < this._corridorAttempts) {
  2277. cnt++;
  2278. this._corridors = [];
  2279. /* dig rooms into a clear map */
  2280. this._map = this._fillMap(1);
  2281. for (var i=0;i<this._rooms.length;i++) {
  2282. var room = this._rooms[i];
  2283. room.clearDoors();
  2284. room.create(this._digCallback);
  2285. }
  2286. this._unconnected = this._rooms.slice().randomize();
  2287. this._connected = [];
  2288. if (this._unconnected.length) { this._connected.push(this._unconnected.pop()); } /* first one is always connected */
  2289. while (1) {
  2290. /* 1. pick random connected room */
  2291. var connected = this._connected.random();
  2292. /* 2. find closest unconnected */
  2293. var room1 = this._closestRoom(this._unconnected, connected);
  2294. /* 3. connect it to closest connected */
  2295. var room2 = this._closestRoom(this._connected, room1);
  2296. var ok = this._connectRooms(room1, room2);
  2297. if (!ok) { break; } /* stop connecting, re-shuffle */
  2298. if (!this._unconnected.length) { return true; } /* done; no rooms remain */
  2299. }
  2300. }
  2301. return false;
  2302. }
  2303. /**
  2304. * For a given room, find the closest one from the list
  2305. */
  2306. ROT.Map.Uniform.prototype._closestRoom = function(rooms, room) {
  2307. var dist = Infinity;
  2308. var center = room.getCenter();
  2309. var result = null;
  2310. for (var i=0;i<rooms.length;i++) {
  2311. var r = rooms[i];
  2312. var c = r.getCenter();
  2313. var dx = c[0]-center[0];
  2314. var dy = c[1]-center[1];
  2315. var d = dx*dx+dy*dy;
  2316. if (d < dist) {
  2317. dist = d;
  2318. result = r;
  2319. }
  2320. }
  2321. return result;
  2322. }
  2323. ROT.Map.Uniform.prototype._connectRooms = function(room1, room2) {
  2324. /*
  2325. room1.debug();
  2326. room2.debug();
  2327. */
  2328. var center1 = room1.getCenter();
  2329. var center2 = room2.getCenter();
  2330. var diffX = center2[0] - center1[0];
  2331. var diffY = center2[1] - center1[1];
  2332. if (Math.abs(diffX) < Math.abs(diffY)) { /* first try connecting north-south walls */
  2333. var dirIndex1 = (diffY > 0 ? 2 : 0);
  2334. var dirIndex2 = (dirIndex1 + 2) % 4;
  2335. var min = room2.getLeft();
  2336. var max = room2.getRight();
  2337. var index = 0;
  2338. } else { /* first try connecting east-west walls */
  2339. var dirIndex1 = (diffX > 0 ? 1 : 3);
  2340. var dirIndex2 = (dirIndex1 + 2) % 4;
  2341. var min = room2.getTop();
  2342. var max = room2.getBottom();
  2343. var index = 1;
  2344. }
  2345. var start = this._placeInWall(room1, dirIndex1); /* corridor will start here */
  2346. if (!start) { return false; }
  2347. if (start[index] >= min && start[index] <= max) { /* possible to connect with straight line (I-like) */
  2348. var end = start.slice();
  2349. var value = null;
  2350. switch (dirIndex2) {
  2351. case 0: value = room2.getTop()-1; break;
  2352. case 1: value = room2.getRight()+1; break;
  2353. case 2: value = room2.getBottom()+1; break;
  2354. case 3: value = room2.getLeft()-1; break;
  2355. }
  2356. end[(index+1)%2] = value;
  2357. this._digLine([start, end]);
  2358. } else if (start[index] < min-1 || start[index] > max+1) { /* need to switch target wall (L-like) */
  2359. var diff = start[index] - center2[index];
  2360. switch (dirIndex2) {
  2361. case 0:
  2362. case 1: var rotation = (diff < 0 ? 3 : 1); break;
  2363. case 2:
  2364. case 3: var rotation = (diff < 0 ? 1 : 3); break;
  2365. }
  2366. dirIndex2 = (dirIndex2 + rotation) % 4;
  2367. var end = this._placeInWall(room2, dirIndex2);
  2368. if (!end) { return false; }
  2369. var mid = [0, 0];
  2370. mid[index] = start[index];
  2371. var index2 = (index+1)%2;
  2372. mid[index2] = end[index2];
  2373. this._digLine([start, mid, end]);
  2374. } else { /* use current wall pair, but adjust the line in the middle (S-like) */
  2375. var index2 = (index+1)%2;
  2376. var end = this._placeInWall(room2, dirIndex2);
  2377. if (!end) { return; }
  2378. var mid = Math.round((end[index2] + start[index2])/2);
  2379. var mid1 = [0, 0];
  2380. var mid2 = [0, 0];
  2381. mid1[index] = start[index];
  2382. mid1[index2] = mid;
  2383. mid2[index] = end[index];
  2384. mid2[index2] = mid;
  2385. this._digLine([start, mid1, mid2, end]);
  2386. }
  2387. room1.addDoor(start[0], start[1]);
  2388. room2.addDoor(end[0], end[1]);
  2389. var index = this._unconnected.indexOf(room1);
  2390. if (index != -1) {
  2391. this._unconnected.splice(index, 1);
  2392. this._connected.push(room1);
  2393. }
  2394. var index = this._unconnected.indexOf(room2);
  2395. if (index != -1) {
  2396. this._unconnected.splice(index, 1);
  2397. this._connected.push(room2);
  2398. }
  2399. return true;
  2400. }
  2401. ROT.Map.Uniform.prototype._placeInWall = function(room, dirIndex) {
  2402. var start = [0, 0];
  2403. var dir = [0, 0];
  2404. var length = 0;
  2405. switch (dirIndex) {
  2406. case 0:
  2407. dir = [1, 0];
  2408. start = [room.getLeft(), room.getTop()-1];
  2409. length = room.getRight()-room.getLeft()+1;
  2410. break;
  2411. case 1:
  2412. dir = [0, 1];
  2413. start = [room.getRight()+1, room.getTop()];
  2414. length = room.getBottom()-room.getTop()+1;
  2415. break;
  2416. case 2:
  2417. dir = [1, 0];
  2418. start = [room.getLeft(), room.getBottom()+1];
  2419. length = room.getRight()-room.getLeft()+1;
  2420. break;
  2421. case 3:
  2422. dir = [0, 1];
  2423. start = [room.getLeft()-1, room.getTop()];
  2424. length = room.getBottom()-room.getTop()+1;
  2425. break;
  2426. }
  2427. var avail = [];
  2428. var lastBadIndex = -2;
  2429. for (var i=0;i<length;i++) {
  2430. var x = start[0] + i*dir[0];
  2431. var y = start[1] + i*dir[1];
  2432. avail.push(null);
  2433. var isWall = (this._map[x][y] == 1);
  2434. if (isWall) {
  2435. if (lastBadIndex != i-1) { avail[i] = [x, y]; }
  2436. } else {
  2437. lastBadIndex = i;
  2438. if (i) { avail[i-1] = null; }
  2439. }
  2440. }
  2441. for (var i=avail.length-1; i>=0; i--) {
  2442. if (!avail[i]) { avail.splice(i, 1); }
  2443. }
  2444. return (avail.length ? avail.random() : null);
  2445. }
  2446. /**
  2447. * Dig a polyline.
  2448. */
  2449. ROT.Map.Uniform.prototype._digLine = function(points) {
  2450. for (var i=1;i<points.length;i++) {
  2451. var start = points[i-1];
  2452. var end = points[i];
  2453. var corridor = new ROT.Map.Feature.Corridor(start[0], start[1], end[0], end[1]);
  2454. corridor.create(this._digCallback);
  2455. this._corridors.push(corridor);
  2456. }
  2457. }
  2458. ROT.Map.Uniform.prototype._digCallback = function(x, y, value) {
  2459. this._map[x][y] = value;
  2460. if (value == 0) { this._dug++; }
  2461. }
  2462. ROT.Map.Uniform.prototype._isWallCallback = function(x, y) {
  2463. if (x < 0 || y < 0 || x >= this._width || y >= this._height) { return false; }
  2464. return (this._map[x][y] == 1);
  2465. }
  2466. ROT.Map.Uniform.prototype._canBeDugCallback = function(x, y) {
  2467. if (x < 1 || y < 1 || x+1 >= this._width || y+1 >= this._height) { return false; }
  2468. return (this._map[x][y] == 1);
  2469. }
  2470. /**
  2471. * @author hyakugei
  2472. * @class Dungeon generator which uses the "orginal" Rogue dungeon generation algorithm. See http://kuoi.com/~kamikaze/GameDesign/art07_rogue_dungeon.php
  2473. * @augments ROT.Map
  2474. * @param {int} [width=ROT.DEFAULT_WIDTH]
  2475. * @param {int} [height=ROT.DEFAULT_HEIGHT]
  2476. * @param {object} [options] Options
  2477. * @param {int[]} [options.cellWidth=3] Number of cells to create on the horizontal (number of rooms horizontally)
  2478. * @param {int[]} [options.cellHeight=3] Number of cells to create on the vertical (number of rooms vertically)
  2479. * @param {int} [options.roomWidth] Room min and max width - normally set auto-magically via the constructor.
  2480. * @param {int} [options.roomHeight] Room min and max height - normally set auto-magically via the constructor.
  2481. */
  2482. ROT.Map.Rogue = function(width, height, options) {
  2483. ROT.Map.call(this, width, height);
  2484. this._options = {
  2485. cellWidth: 3, // NOTE to self, these could probably work the same as the roomWidth/room Height values
  2486. cellHeight: 3 // ie. as an array with min-max values for each direction....
  2487. }
  2488. for (var p in options) { this._options[p] = options[p]; }
  2489. /*
  2490. Set the room sizes according to the over-all width of the map,
  2491. and the cell sizes.
  2492. */
  2493. if (!this._options.hasOwnProperty("roomWidth")) {
  2494. this._options["roomWidth"] = this._calculateRoomSize(width, this._options["cellWidth"]);
  2495. }
  2496. if (!this._options.hasOwnProperty["roomHeight"]) {
  2497. this._options["roomHeight"] = this._calculateRoomSize(height, this._options["cellHeight"]);
  2498. }
  2499. }
  2500. ROT.Map.Rogue.extend(ROT.Map);
  2501. /**
  2502. * @see ROT.Map#create
  2503. */
  2504. ROT.Map.Rogue.prototype.create = function(callback) {
  2505. this.map = this._fillMap(1);
  2506. this.rooms = [];
  2507. this.connectedCells = [];
  2508. this._initRooms();
  2509. this._connectRooms();
  2510. this._connectUnconnectedRooms();
  2511. this._createRandomRoomConnections();
  2512. this._createRooms();
  2513. this._createCorridors();
  2514. if (callback) {
  2515. for (var i = 0; i < this._width; i++) {
  2516. for (var j = 0; j < this._height; j++) {
  2517. callback(i, j, this.map[i][j]);
  2518. }
  2519. }
  2520. }
  2521. return this;
  2522. }
  2523. ROT.Map.Rogue.prototype._getRandomInt = function(min, max) {
  2524. return Math.floor(ROT.RNG.getUniform() * (max - min + 1)) + min;
  2525. }
  2526. ROT.Map.Rogue.prototype._calculateRoomSize = function(size, cell) {
  2527. var max = Math.floor((size/cell) * 0.8);
  2528. var min = Math.floor((size/cell) * 0.25);
  2529. if (min < 2) min = 2;
  2530. if (max < 2) max = 2;
  2531. return [min, max];
  2532. }
  2533. ROT.Map.Rogue.prototype._initRooms = function () {
  2534. // create rooms array. This is the "grid" list from the algo.
  2535. for (var i = 0; i < this._options.cellWidth; i++) {
  2536. this.rooms.push([]);
  2537. for(var j = 0; j < this._options.cellHeight; j++) {
  2538. this.rooms[i].push({"x":0, "y":0, "width":0, "height":0, "connections":[], "cellx":i, "celly":j});
  2539. }
  2540. }
  2541. }
  2542. ROT.Map.Rogue.prototype._connectRooms = function() {
  2543. //pick random starting grid
  2544. var cgx = this._getRandomInt(0, this._options.cellWidth-1);
  2545. var cgy = this._getRandomInt(0, this._options.cellHeight-1);
  2546. var idx;
  2547. var ncgx;
  2548. var ncgy;
  2549. var found = false;
  2550. var room;
  2551. var otherRoom;
  2552. // find unconnected neighbour cells
  2553. do {
  2554. //var dirToCheck = [0,1,2,3,4,5,6,7];
  2555. var dirToCheck = [0,2,4,6];
  2556. dirToCheck = dirToCheck.randomize();
  2557. do {
  2558. found = false;
  2559. idx = dirToCheck.pop();
  2560. ncgx = cgx + ROT.DIRS[8][idx][0];
  2561. ncgy = cgy + ROT.DIRS[8][idx][1];
  2562. if(ncgx < 0 || ncgx >= this._options.cellWidth) continue;
  2563. if(ncgy < 0 || ncgy >= this._options.cellHeight) continue;
  2564. room = this.rooms[cgx][cgy];
  2565. if(room["connections"].length > 0)
  2566. {
  2567. // as long as this room doesn't already coonect to me, we are ok with it.
  2568. if(room["connections"][0][0] == ncgx &&
  2569. room["connections"][0][1] == ncgy)
  2570. {
  2571. break;
  2572. }
  2573. }
  2574. otherRoom = this.rooms[ncgx][ncgy];
  2575. if (otherRoom["connections"].length == 0) {
  2576. otherRoom["connections"].push([cgx,cgy]);
  2577. this.connectedCells.push([ncgx, ncgy]);
  2578. cgx = ncgx;
  2579. cgy = ncgy;
  2580. found = true;
  2581. }
  2582. } while (dirToCheck.length > 0 && found == false)
  2583. } while (dirToCheck.length > 0)
  2584. }
  2585. ROT.Map.Rogue.prototype._connectUnconnectedRooms = function() {
  2586. //While there are unconnected rooms, try to connect them to a random connected neighbor
  2587. //(if a room has no connected neighbors yet, just keep cycling, you'll fill out to it eventually).
  2588. var cw = this._options.cellWidth;
  2589. var ch = this._options.cellHeight;
  2590. var randomConnectedCell;
  2591. this.connectedCells = this.connectedCells.randomize();
  2592. var room;
  2593. var otherRoom;
  2594. var validRoom;
  2595. for (var i = 0; i < this._options.cellWidth; i++) {
  2596. for (var j = 0; j < this._options.cellHeight; j++) {
  2597. room = this.rooms[i][j];
  2598. if (room["connections"].length == 0) {
  2599. var directions = [0,2,4,6];
  2600. directions = directions.randomize();
  2601. var validRoom = false;
  2602. do {
  2603. var dirIdx = directions.pop();
  2604. var newI = i + ROT.DIRS[8][dirIdx][0];
  2605. var newJ = j + ROT.DIRS[8][dirIdx][1];
  2606. if (newI < 0 || newI >= cw ||
  2607. newJ < 0 || newJ >= ch) {
  2608. continue;
  2609. }
  2610. otherRoom = this.rooms[newI][newJ];
  2611. validRoom = true;
  2612. if (otherRoom["connections"].length == 0) {
  2613. break;
  2614. }
  2615. for (var k = 0; k < otherRoom["connections"].length; k++) {
  2616. if(otherRoom["connections"][k][0] == i &&
  2617. otherRoom["connections"][k][1] == j) {
  2618. validRoom = false;
  2619. break;
  2620. }
  2621. }
  2622. if (validRoom) break;
  2623. } while (directions.length)
  2624. if(validRoom) {
  2625. room["connections"].push( [otherRoom["cellx"], otherRoom["celly"]] );
  2626. } else {
  2627. console.log("-- Unable to connect room.");
  2628. }
  2629. }
  2630. }
  2631. }
  2632. }
  2633. ROT.Map.Rogue.prototype._createRandomRoomConnections = function(connections) {
  2634. // Empty for now.
  2635. }
  2636. ROT.Map.Rogue.prototype._createRooms = function() {
  2637. // Create Rooms
  2638. var w = this._width;
  2639. var h = this._height;
  2640. var cw = this._options.cellWidth;
  2641. var ch = this._options.cellHeight;
  2642. var cwp = Math.floor(this._width / cw);
  2643. var chp = Math.floor(this._height / ch);
  2644. var roomw;
  2645. var roomh;
  2646. var roomWidth = this._options["roomWidth"];
  2647. var roomHeight = this._options["roomHeight"];
  2648. var sx;
  2649. var sy;
  2650. var tx;
  2651. var ty;
  2652. var otherRoom;
  2653. for (var i = 0; i < cw; i++) {
  2654. for (var j = 0; j < ch; j++) {
  2655. sx = cwp * i;
  2656. sy = chp * j;
  2657. if (sx == 0) sx = 1;
  2658. if (sy == 0) sy = 1;
  2659. roomw = this._getRandomInt(roomWidth[0], roomWidth[1]);
  2660. roomh = this._getRandomInt(roomHeight[0], roomHeight[1]);
  2661. if (j > 0) {
  2662. otherRoom = this.rooms[i][j-1];
  2663. while (sy - (otherRoom["y"] + otherRoom["height"] ) < 3) {
  2664. sy++;
  2665. }
  2666. }
  2667. if (i > 0) {
  2668. otherRoom = this.rooms[i-1][j];
  2669. while(sx - (otherRoom["x"] + otherRoom["width"]) < 3) {
  2670. sx++;
  2671. }
  2672. }
  2673. var sxOffset = Math.round(this._getRandomInt(0, cwp-roomw)/2);
  2674. var syOffset = Math.round(this._getRandomInt(0, chp-roomh)/2);
  2675. while (sx + sxOffset + roomw >= w) {
  2676. if(sxOffset) {
  2677. sxOffset--;
  2678. } else {
  2679. roomw--;
  2680. }
  2681. }
  2682. while (sy + syOffset + roomh >= h) {
  2683. if(syOffset) {
  2684. syOffset--;
  2685. } else {
  2686. roomh--;
  2687. }
  2688. }
  2689. sx = sx + sxOffset;
  2690. sy = sy + syOffset;
  2691. this.rooms[i][j]["x"] = sx;
  2692. this.rooms[i][j]["y"] = sy;
  2693. this.rooms[i][j]["width"] = roomw;
  2694. this.rooms[i][j]["height"] = roomh;
  2695. for (var ii = sx; ii < sx + roomw; ii++) {
  2696. for (var jj = sy; jj < sy + roomh; jj++) {
  2697. this.map[ii][jj] = 0;
  2698. }
  2699. }
  2700. }
  2701. }
  2702. }
  2703. ROT.Map.Rogue.prototype._getWallPosition = function(aRoom, aDirection) {
  2704. var rx;
  2705. var ry;
  2706. var door;
  2707. if (aDirection == 1 || aDirection == 3) {
  2708. rx = this._getRandomInt(aRoom["x"] + 1, aRoom["x"] + aRoom["width"] - 2);
  2709. if (aDirection == 1) {
  2710. ry = aRoom["y"] - 2;
  2711. door = ry + 1;
  2712. } else {
  2713. ry = aRoom["y"] + aRoom["height"] + 1;
  2714. door = ry -1;
  2715. }
  2716. this.map[rx][door] = 0; // i'm not setting a specific 'door' tile value right now, just empty space.
  2717. } else if (aDirection == 2 || aDirection == 4) {
  2718. ry = this._getRandomInt(aRoom["y"] + 1, aRoom["y"] + aRoom["height"] - 2);
  2719. if(aDirection == 2) {
  2720. rx = aRoom["x"] + aRoom["width"] + 1;
  2721. door = rx - 1;
  2722. } else {
  2723. rx = aRoom["x"] - 2;
  2724. door = rx + 1;
  2725. }
  2726. this.map[door][ry] = 0; // i'm not setting a specific 'door' tile value right now, just empty space.
  2727. }
  2728. return [rx, ry];
  2729. }
  2730. /***
  2731. * @param startPosition a 2 element array
  2732. * @param endPosition a 2 element array
  2733. */
  2734. ROT.Map.Rogue.prototype._drawCorridore = function (startPosition, endPosition) {
  2735. var xOffset = endPosition[0] - startPosition[0];
  2736. var yOffset = endPosition[1] - startPosition[1];
  2737. var xpos = startPosition[0];
  2738. var ypos = startPosition[1];
  2739. var tempDist;
  2740. var xDir;
  2741. var yDir;
  2742. var move; // 2 element array, element 0 is the direction, element 1 is the total value to move.
  2743. var moves = []; // a list of 2 element arrays
  2744. var xAbs = Math.abs(xOffset);
  2745. var yAbs = Math.abs(yOffset);
  2746. var percent = ROT.RNG.getUniform(); // used to split the move at different places along the long axis
  2747. var firstHalf = percent;
  2748. var secondHalf = 1 - percent;
  2749. xDir = xOffset > 0 ? 2 : 6;
  2750. yDir = yOffset > 0 ? 4 : 0;
  2751. if (xAbs < yAbs) {
  2752. // move firstHalf of the y offset
  2753. tempDist = Math.ceil(yAbs * firstHalf);
  2754. moves.push([yDir, tempDist]);
  2755. // move all the x offset
  2756. moves.push([xDir, xAbs]);
  2757. // move sendHalf of the y offset
  2758. tempDist = Math.floor(yAbs * secondHalf);
  2759. moves.push([yDir, tempDist]);
  2760. } else {
  2761. // move firstHalf of the x offset
  2762. tempDist = Math.ceil(xAbs * firstHalf);
  2763. moves.push([xDir, tempDist]);
  2764. // move all the y offset
  2765. moves.push([yDir, yAbs]);
  2766. // move secondHalf of the x offset.
  2767. tempDist = Math.floor(xAbs * secondHalf);
  2768. moves.push([xDir, tempDist]);
  2769. }
  2770. this.map[xpos][ypos] = 0;
  2771. while (moves.length > 0) {
  2772. move = moves.pop();
  2773. while (move[1] > 0) {
  2774. xpos += ROT.DIRS[8][move[0]][0];
  2775. ypos += ROT.DIRS[8][move[0]][1];
  2776. this.map[xpos][ypos] = 0;
  2777. move[1] = move[1] - 1;
  2778. }
  2779. }
  2780. }
  2781. ROT.Map.Rogue.prototype._createCorridors = function () {
  2782. // Draw Corridors between connected rooms
  2783. var cw = this._options.cellWidth;
  2784. var ch = this._options.cellHeight;
  2785. var room;
  2786. var connection;
  2787. var otherRoom;
  2788. var wall;
  2789. var otherWall;
  2790. for (var i = 0; i < cw; i++) {
  2791. for (var j = 0; j < ch; j++) {
  2792. room = this.rooms[i][j];
  2793. for (var k = 0; k < room["connections"].length; k++) {
  2794. connection = room["connections"][k];
  2795. otherRoom = this.rooms[connection[0]][connection[1]];
  2796. // figure out what wall our corridor will start one.
  2797. // figure out what wall our corridor will end on.
  2798. if (otherRoom["cellx"] > room["cellx"] ) {
  2799. wall = 2;
  2800. otherWall = 4;
  2801. } else if (otherRoom["cellx"] < room["cellx"] ) {
  2802. wall = 4;
  2803. otherWall = 2;
  2804. } else if(otherRoom["celly"] > room["celly"]) {
  2805. wall = 3;
  2806. otherWall = 1;
  2807. } else if(otherRoom["celly"] < room["celly"]) {
  2808. wall = 1;
  2809. otherWall = 3;
  2810. }
  2811. this._drawCorridore(this._getWallPosition(room, wall), this._getWallPosition(otherRoom, otherWall));
  2812. }
  2813. }
  2814. }
  2815. }
  2816. /**
  2817. * @class Dungeon feature; has own .create() method
  2818. */
  2819. ROT.Map.Feature = function() {}
  2820. ROT.Map.Feature.prototype.isValid = function(canBeDugCallback) {}
  2821. ROT.Map.Feature.prototype.create = function(digCallback) {}
  2822. ROT.Map.Feature.prototype.debug = function() {}
  2823. ROT.Map.Feature.createRandomAt = function(x, y, dx, dy, options) {}
  2824. /**
  2825. * @class Room
  2826. * @augments ROT.Map.Feature
  2827. * @param {int} x1
  2828. * @param {int} y1
  2829. * @param {int} x2
  2830. * @param {int} y2
  2831. * @param {int} [doorX]
  2832. * @param {int} [doorY]
  2833. */
  2834. ROT.Map.Feature.Room = function(x1, y1, x2, y2, doorX, doorY) {
  2835. this._x1 = x1;
  2836. this._y1 = y1;
  2837. this._x2 = x2;
  2838. this._y2 = y2;
  2839. this._doors = {};
  2840. if (arguments.length > 4) { this.addDoor(doorX, doorY); }
  2841. }
  2842. ROT.Map.Feature.Room.extend(ROT.Map.Feature);
  2843. /**
  2844. * Room of random size, with a given doors and direction
  2845. */
  2846. ROT.Map.Feature.Room.createRandomAt = function(x, y, dx, dy, options) {
  2847. var min = options.roomWidth[0];
  2848. var max = options.roomWidth[1];
  2849. var width = min + Math.floor(ROT.RNG.getUniform()*(max-min+1));
  2850. var min = options.roomHeight[0];
  2851. var max = options.roomHeight[1];
  2852. var height = min + Math.floor(ROT.RNG.getUniform()*(max-min+1));
  2853. if (dx == 1) { /* to the right */
  2854. var y2 = y - Math.floor(ROT.RNG.getUniform() * height);
  2855. return new this(x+1, y2, x+width, y2+height-1, x, y);
  2856. }
  2857. if (dx == -1) { /* to the left */
  2858. var y2 = y - Math.floor(ROT.RNG.getUniform() * height);
  2859. return new this(x-width, y2, x-1, y2+height-1, x, y);
  2860. }
  2861. if (dy == 1) { /* to the bottom */
  2862. var x2 = x - Math.floor(ROT.RNG.getUniform() * width);
  2863. return new this(x2, y+1, x2+width-1, y+height, x, y);
  2864. }
  2865. if (dy == -1) { /* to the top */
  2866. var x2 = x - Math.floor(ROT.RNG.getUniform() * width);
  2867. return new this(x2, y-height, x2+width-1, y-1, x, y);
  2868. }
  2869. }
  2870. /**
  2871. * Room of random size, positioned around center coords
  2872. */
  2873. ROT.Map.Feature.Room.createRandomCenter = function(cx, cy, options) {
  2874. var min = options.roomWidth[0];
  2875. var max = options.roomWidth[1];
  2876. var width = min + Math.floor(ROT.RNG.getUniform()*(max-min+1));
  2877. var min = options.roomHeight[0];
  2878. var max = options.roomHeight[1];
  2879. var height = min + Math.floor(ROT.RNG.getUniform()*(max-min+1));
  2880. var x1 = cx - Math.floor(ROT.RNG.getUniform()*width);
  2881. var y1 = cy - Math.floor(ROT.RNG.getUniform()*height);
  2882. var x2 = x1 + width - 1;
  2883. var y2 = y1 + height - 1;
  2884. return new this(x1, y1, x2, y2);
  2885. }
  2886. /**
  2887. * Room of random size within a given dimensions
  2888. */
  2889. ROT.Map.Feature.Room.createRandom = function(availWidth, availHeight, options) {
  2890. var min = options.roomWidth[0];
  2891. var max = options.roomWidth[1];
  2892. var width = min + Math.floor(ROT.RNG.getUniform()*(max-min+1));
  2893. var min = options.roomHeight[0];
  2894. var max = options.roomHeight[1];
  2895. var height = min + Math.floor(ROT.RNG.getUniform()*(max-min+1));
  2896. var left = availWidth - width - 1;
  2897. var top = availHeight - height - 1;
  2898. var x1 = 1 + Math.floor(ROT.RNG.getUniform()*left);
  2899. var y1 = 1 + Math.floor(ROT.RNG.getUniform()*top);
  2900. var x2 = x1 + width - 1;
  2901. var y2 = y1 + height - 1;
  2902. return new this(x1, y1, x2, y2);
  2903. }
  2904. ROT.Map.Feature.Room.prototype.addDoor = function(x, y) {
  2905. this._doors[x+","+y] = 1;
  2906. return this;
  2907. }
  2908. /**
  2909. * @param {function}
  2910. */
  2911. ROT.Map.Feature.Room.prototype.getDoors = function(callback) {
  2912. for (var key in this._doors) {
  2913. var parts = key.split(",");
  2914. callback(parseInt(parts[0]), parseInt(parts[1]));
  2915. }
  2916. return this;
  2917. }
  2918. ROT.Map.Feature.Room.prototype.clearDoors = function() {
  2919. this._doors = {};
  2920. return this;
  2921. }
  2922. ROT.Map.Feature.Room.prototype.addDoors = function(isWallCallback) {
  2923. var left = this._x1-1;
  2924. var right = this._x2+1;
  2925. var top = this._y1-1;
  2926. var bottom = this._y2+1;
  2927. for (var x=left; x<=right; x++) {
  2928. for (var y=top; y<=bottom; y++) {
  2929. if (x != left && x != right && y != top && y != bottom) { continue; }
  2930. if (isWallCallback(x, y)) { continue; }
  2931. this.addDoor(x, y);
  2932. }
  2933. }
  2934. return this;
  2935. }
  2936. ROT.Map.Feature.Room.prototype.debug = function() {
  2937. console.log("room", this._x1, this._y1, this._x2, this._y2);
  2938. }
  2939. ROT.Map.Feature.Room.prototype.isValid = function(isWallCallback, canBeDugCallback) {
  2940. var left = this._x1-1;
  2941. var right = this._x2+1;
  2942. var top = this._y1-1;
  2943. var bottom = this._y2+1;
  2944. for (var x=left; x<=right; x++) {
  2945. for (var y=top; y<=bottom; y++) {
  2946. if (x == left || x == right || y == top || y == bottom) {
  2947. if (!isWallCallback(x, y)) { return false; }
  2948. } else {
  2949. if (!canBeDugCallback(x, y)) { return false; }
  2950. }
  2951. }
  2952. }
  2953. return true;
  2954. }
  2955. /**
  2956. * @param {function} digCallback Dig callback with a signature (x, y, value). Values: 0 = empty, 1 = wall, 2 = door. Multiple doors are allowed.
  2957. */
  2958. ROT.Map.Feature.Room.prototype.create = function(digCallback) {
  2959. var left = this._x1-1;
  2960. var right = this._x2+1;
  2961. var top = this._y1-1;
  2962. var bottom = this._y2+1;
  2963. var value = 0;
  2964. for (var x=left; x<=right; x++) {
  2965. for (var y=top; y<=bottom; y++) {
  2966. if (x+","+y in this._doors) {
  2967. value = 2;
  2968. } else if (x == left || x == right || y == top || y == bottom) {
  2969. value = 1;
  2970. } else {
  2971. value = 0;
  2972. }
  2973. digCallback(x, y, value);
  2974. }
  2975. }
  2976. }
  2977. ROT.Map.Feature.Room.prototype.getCenter = function() {
  2978. return [Math.round((this._x1 + this._x2)/2), Math.round((this._y1 + this._y2)/2)];
  2979. }
  2980. ROT.Map.Feature.Room.prototype.getLeft = function() {
  2981. return this._x1;
  2982. }
  2983. ROT.Map.Feature.Room.prototype.getRight = function() {
  2984. return this._x2;
  2985. }
  2986. ROT.Map.Feature.Room.prototype.getTop = function() {
  2987. return this._y1;
  2988. }
  2989. ROT.Map.Feature.Room.prototype.getBottom = function() {
  2990. return this._y2;
  2991. }
  2992. /**
  2993. * @class Corridor
  2994. * @augments ROT.Map.Feature
  2995. * @param {int} startX
  2996. * @param {int} startY
  2997. * @param {int} endX
  2998. * @param {int} endY
  2999. */
  3000. ROT.Map.Feature.Corridor = function(startX, startY, endX, endY) {
  3001. this._startX = startX;
  3002. this._startY = startY;
  3003. this._endX = endX;
  3004. this._endY = endY;
  3005. this._endsWithAWall = true;
  3006. }
  3007. ROT.Map.Feature.Corridor.extend(ROT.Map.Feature);
  3008. ROT.Map.Feature.Corridor.createRandomAt = function(x, y, dx, dy, options) {
  3009. var min = options.corridorLength[0];
  3010. var max = options.corridorLength[1];
  3011. var length = min + Math.floor(ROT.RNG.getUniform()*(max-min+1));
  3012. return new this(x, y, x + dx*length, y + dy*length);
  3013. }
  3014. ROT.Map.Feature.Corridor.prototype.debug = function() {
  3015. console.log("corridor", this._startX, this._startY, this._endX, this._endY);
  3016. }
  3017. ROT.Map.Feature.Corridor.prototype.isValid = function(isWallCallback, canBeDugCallback){
  3018. var sx = this._startX;
  3019. var sy = this._startY;
  3020. var dx = this._endX-sx;
  3021. var dy = this._endY-sy;
  3022. var length = 1 + Math.max(Math.abs(dx), Math.abs(dy));
  3023. if (dx) { dx = dx/Math.abs(dx); }
  3024. if (dy) { dy = dy/Math.abs(dy); }
  3025. var nx = dy;
  3026. var ny = -dx;
  3027. var ok = true;
  3028. for (var i=0; i<length; i++) {
  3029. var x = sx + i*dx;
  3030. var y = sy + i*dy;
  3031. if (!canBeDugCallback( x, y)) { ok = false; }
  3032. if (!isWallCallback (x + nx, y + ny)) { ok = false; }
  3033. if (!isWallCallback (x - nx, y - ny)) { ok = false; }
  3034. if (!ok) {
  3035. length = i;
  3036. this._endX = x-dx;
  3037. this._endY = y-dy;
  3038. break;
  3039. }
  3040. }
  3041. /**
  3042. * If the length degenerated, this corridor might be invalid
  3043. */
  3044. /* not supported */
  3045. if (length == 0) { return false; }
  3046. /* length 1 allowed only if the next space is empty */
  3047. if (length == 1 && isWallCallback(this._endX + dx, this._endY + dy)) { return false; }
  3048. /**
  3049. * We do not want the corridor to crash into a corner of a room;
  3050. * if any of the ending corners is empty, the N+1th cell of this corridor must be empty too.
  3051. *
  3052. * Situation:
  3053. * #######1
  3054. * .......?
  3055. * #######2
  3056. *
  3057. * The corridor was dug from left to right.
  3058. * 1, 2 - problematic corners, ? = N+1th cell (not dug)
  3059. */
  3060. var firstCornerBad = !isWallCallback(this._endX + dx + nx, this._endY + dy + ny);
  3061. var secondCornerBad = !isWallCallback(this._endX + dx - nx, this._endY + dy - ny);
  3062. this._endsWithAWall = isWallCallback(this._endX + dx, this._endY + dy);
  3063. if ((firstCornerBad || secondCornerBad) && this._endsWithAWall) { return false; }
  3064. return true;
  3065. }
  3066. /**
  3067. * @param {function} digCallback Dig callback with a signature (x, y, value). Values: 0 = empty.
  3068. */
  3069. ROT.Map.Feature.Corridor.prototype.create = function(digCallback) {
  3070. var sx = this._startX;
  3071. var sy = this._startY;
  3072. var dx = this._endX-sx;
  3073. var dy = this._endY-sy;
  3074. var length = 1+Math.max(Math.abs(dx), Math.abs(dy));
  3075. if (dx) { dx = dx/Math.abs(dx); }
  3076. if (dy) { dy = dy/Math.abs(dy); }
  3077. var nx = dy;
  3078. var ny = -dx;
  3079. for (var i=0; i<length; i++) {
  3080. var x = sx + i*dx;
  3081. var y = sy + i*dy;
  3082. digCallback(x, y, 0);
  3083. }
  3084. return true;
  3085. }
  3086. ROT.Map.Feature.Corridor.prototype.createPriorityWalls = function(priorityWallCallback) {
  3087. if (!this._endsWithAWall) { return; }
  3088. var sx = this._startX;
  3089. var sy = this._startY;
  3090. var dx = this._endX-sx;
  3091. var dy = this._endY-sy;
  3092. if (dx) { dx = dx/Math.abs(dx); }
  3093. if (dy) { dy = dy/Math.abs(dy); }
  3094. var nx = dy;
  3095. var ny = -dx;
  3096. priorityWallCallback(this._endX + dx, this._endY + dy);
  3097. priorityWallCallback(this._endX + nx, this._endY + ny);
  3098. priorityWallCallback(this._endX - nx, this._endY - ny);
  3099. }/**
  3100. * @class Base noise generator
  3101. */
  3102. ROT.Noise = function() {
  3103. };
  3104. ROT.Noise.prototype.get = function(x, y) {}
  3105. /**
  3106. * A simple 2d implementation of simplex noise by Ondrej Zara
  3107. *
  3108. * Based on a speed-improved simplex noise algorithm for 2D, 3D and 4D in Java.
  3109. * Which is based on example code by Stefan Gustavson (stegu@itn.liu.se).
  3110. * With Optimisations by Peter Eastman (peastman@drizzle.stanford.edu).
  3111. * Better rank ordering method by Stefan Gustavson in 2012.
  3112. */
  3113. /**
  3114. * @class 2D simplex noise generator
  3115. * @param {int} [gradients=256] Random gradients
  3116. */
  3117. ROT.Noise.Simplex = function(gradients) {
  3118. ROT.Noise.call(this);
  3119. this._F2 = 0.5 * (Math.sqrt(3) - 1);
  3120. this._G2 = (3 - Math.sqrt(3)) / 6;
  3121. this._gradients = [
  3122. [ 0, -1],
  3123. [ 1, -1],
  3124. [ 1, 0],
  3125. [ 1, 1],
  3126. [ 0, 1],
  3127. [-1, 1],
  3128. [-1, 0],
  3129. [-1, -1]
  3130. ];
  3131. var permutations = [];
  3132. var count = gradients || 256;
  3133. for (var i=0;i<count;i++) { permutations.push(i); }
  3134. permutations = permutations.randomize();
  3135. this._perms = [];
  3136. this._indexes = [];
  3137. for (var i=0;i<2*count;i++) {
  3138. this._perms.push(permutations[i % count]);
  3139. this._indexes.push(this._perms[i] % this._gradients.length);
  3140. }
  3141. };
  3142. ROT.Noise.Simplex.extend(ROT.Noise);
  3143. ROT.Noise.Simplex.prototype.get = function(xin, yin) {
  3144. var perms = this._perms;
  3145. var indexes = this._indexes;
  3146. var count = perms.length/2;
  3147. var G2 = this._G2;
  3148. var n0 =0, n1 = 0, n2 = 0, gi; // Noise contributions from the three corners
  3149. // Skew the input space to determine which simplex cell we're in
  3150. var s = (xin + yin) * this._F2; // Hairy factor for 2D
  3151. var i = Math.floor(xin + s);
  3152. var j = Math.floor(yin + s);
  3153. var t = (i + j) * G2;
  3154. var X0 = i - t; // Unskew the cell origin back to (x,y) space
  3155. var Y0 = j - t;
  3156. var x0 = xin - X0; // The x,y distances from the cell origin
  3157. var y0 = yin - Y0;
  3158. // For the 2D case, the simplex shape is an equilateral triangle.
  3159. // Determine which simplex we are in.
  3160. var i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords
  3161. if (x0 > y0) {
  3162. i1 = 1;
  3163. j1 = 0;
  3164. } else { // lower triangle, XY order: (0,0)->(1,0)->(1,1)
  3165. i1 = 0;
  3166. j1 = 1;
  3167. } // upper triangle, YX order: (0,0)->(0,1)->(1,1)
  3168. // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
  3169. // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
  3170. // c = (3-sqrt(3))/6
  3171. var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
  3172. var y1 = y0 - j1 + G2;
  3173. var x2 = x0 - 1 + 2*G2; // Offsets for last corner in (x,y) unskewed coords
  3174. var y2 = y0 - 1 + 2*G2;
  3175. // Work out the hashed gradient indices of the three simplex corners
  3176. var ii = i.mod(count);
  3177. var jj = j.mod(count);
  3178. // Calculate the contribution from the three corners
  3179. var t0 = 0.5 - x0*x0 - y0*y0;
  3180. if (t0 >= 0) {
  3181. t0 *= t0;
  3182. gi = indexes[ii+perms[jj]];
  3183. var grad = this._gradients[gi];
  3184. n0 = t0 * t0 * (grad[0] * x0 + grad[1] * y0);
  3185. }
  3186. var t1 = 0.5 - x1*x1 - y1*y1;
  3187. if (t1 >= 0) {
  3188. t1 *= t1;
  3189. gi = indexes[ii+i1+perms[jj+j1]];
  3190. var grad = this._gradients[gi];
  3191. n1 = t1 * t1 * (grad[0] * x1 + grad[1] * y1);
  3192. }
  3193. var t2 = 0.5 - x2*x2 - y2*y2;
  3194. if (t2 >= 0) {
  3195. t2 *= t2;
  3196. gi = indexes[ii+1+perms[jj+1]];
  3197. var grad = this._gradients[gi];
  3198. n2 = t2 * t2 * (grad[0] * x2 + grad[1] * y2);
  3199. }
  3200. // Add contributions from each corner to get the final noise value.
  3201. // The result is scaled to return values in the interval [-1,1].
  3202. return 70 * (n0 + n1 + n2);
  3203. }
  3204. /**
  3205. * @class Abstract FOV algorithm
  3206. * @param {function} lightPassesCallback Does the light pass through x,y?
  3207. * @param {object} [options]
  3208. * @param {int} [options.topology=8] 4/6/8
  3209. */
  3210. ROT.FOV = function(lightPassesCallback, options) {
  3211. this._lightPasses = lightPassesCallback;
  3212. this._options = {
  3213. topology: 8
  3214. }
  3215. for (var p in options) { this._options[p] = options[p]; }
  3216. };
  3217. /**
  3218. * Compute visibility
  3219. * @param {int} x
  3220. * @param {int} y
  3221. * @param {int} R Maximum visibility radius
  3222. * @param {function} callback
  3223. */
  3224. ROT.FOV.prototype.compute = function(x, y, R, callback) {}
  3225. /**
  3226. * Return all neighbors in a concentric ring
  3227. * @param {int} cx center-x
  3228. * @param {int} cy center-y
  3229. * @param {int} r range
  3230. */
  3231. ROT.FOV.prototype._getCircle = function(cx, cy, r) {
  3232. var result = [];
  3233. var dirs, countFactor, startOffset;
  3234. switch (this._options.topology) {
  3235. case 4:
  3236. countFactor = 1;
  3237. startOffset = [0, 1];
  3238. dirs = [
  3239. ROT.DIRS[8][7],
  3240. ROT.DIRS[8][1],
  3241. ROT.DIRS[8][3],
  3242. ROT.DIRS[8][5]
  3243. ]
  3244. break;
  3245. case 6:
  3246. dirs = ROT.DIRS[6];
  3247. countFactor = 1;
  3248. startOffset = [-1, 1];
  3249. break;
  3250. case 8:
  3251. dirs = ROT.DIRS[4];
  3252. countFactor = 2;
  3253. startOffset = [-1, 1];
  3254. break;
  3255. }
  3256. /* starting neighbor */
  3257. var x = cx + startOffset[0]*r;
  3258. var y = cy + startOffset[1]*r;
  3259. /* circle */
  3260. for (var i=0;i<dirs.length;i++) {
  3261. for (var j=0;j<r*countFactor;j++) {
  3262. result.push([x, y]);
  3263. x += dirs[i][0];
  3264. y += dirs[i][1];
  3265. }
  3266. }
  3267. return result;
  3268. }
  3269. /**
  3270. * @class Discrete shadowcasting algorithm
  3271. * @augments ROT.FOV
  3272. */
  3273. ROT.FOV.DiscreteShadowcasting = function(lightPassesCallback, options) {
  3274. ROT.FOV.call(this, lightPassesCallback, options);
  3275. }
  3276. ROT.FOV.DiscreteShadowcasting.extend(ROT.FOV);
  3277. /**
  3278. * @see ROT.FOV#compute
  3279. */
  3280. ROT.FOV.DiscreteShadowcasting.prototype.compute = function(x, y, R, callback) {
  3281. var center = this._coords;
  3282. var map = this._map;
  3283. /* this place is always visible */
  3284. callback(x, y, 0);
  3285. /* standing in a dark place. FIXME is this a good idea? */
  3286. if (!this._lightPasses(x, y)) { return; }
  3287. /* start and end angles */
  3288. var DATA = [];
  3289. var A, B, cx, cy, blocks;
  3290. /* analyze surrounding cells in concentric rings, starting from the center */
  3291. for (var r=1; r<=R; r++) {
  3292. var neighbors = this._getCircle(x, y, r);
  3293. var angle = 360 / neighbors.length;
  3294. for (var i=0;i<neighbors.length;i++) {
  3295. cx = neighbors[i][0];
  3296. cy = neighbors[i][1];
  3297. A = angle * (i - 0.5);
  3298. B = A + angle;
  3299. blocks = !this._lightPasses(cx, cy);
  3300. if (this._visibleCoords(Math.floor(A), Math.ceil(B), blocks, DATA)) { callback(cx, cy, r, 1); }
  3301. if (DATA.length == 2 && DATA[0] == 0 && DATA[1] == 360) { return; } /* cutoff? */
  3302. } /* for all cells in this ring */
  3303. } /* for all rings */
  3304. }
  3305. /**
  3306. * @param {int} A start angle
  3307. * @param {int} B end angle
  3308. * @param {bool} blocks Does current cell block visibility?
  3309. * @param {int[][]} DATA shadowed angle pairs
  3310. */
  3311. ROT.FOV.DiscreteShadowcasting.prototype._visibleCoords = function(A, B, blocks, DATA) {
  3312. if (A < 0) {
  3313. var v1 = arguments.callee(0, B, blocks, DATA);
  3314. var v2 = arguments.callee(360+A, 360, blocks, DATA);
  3315. return v1 || v2;
  3316. }
  3317. var index = 0;
  3318. while (index < DATA.length && DATA[index] < A) { index++; }
  3319. if (index == DATA.length) { /* completely new shadow */
  3320. if (blocks) { DATA.push(A, B); }
  3321. return true;
  3322. }
  3323. var count = 0;
  3324. if (index % 2) { /* this shadow starts in an existing shadow, or within its ending boundary */
  3325. while (index < DATA.length && DATA[index] < B) {
  3326. index++;
  3327. count++;
  3328. }
  3329. if (count == 0) { return false; }
  3330. if (blocks) {
  3331. if (count % 2) {
  3332. DATA.splice(index-count, count, B);
  3333. } else {
  3334. DATA.splice(index-count, count);
  3335. }
  3336. }
  3337. return true;
  3338. } else { /* this shadow starts outside an existing shadow, or within a starting boundary */
  3339. while (index < DATA.length && DATA[index] < B) {
  3340. index++;
  3341. count++;
  3342. }
  3343. /* visible when outside an existing shadow, or when overlapping */
  3344. if (A == DATA[index-count] && count == 1) { return false; }
  3345. if (blocks) {
  3346. if (count % 2) {
  3347. DATA.splice(index-count, count, A);
  3348. } else {
  3349. DATA.splice(index-count, count, A, B);
  3350. }
  3351. }
  3352. return true;
  3353. }
  3354. }
  3355. /**
  3356. * @class Precise shadowcasting algorithm
  3357. * @augments ROT.FOV
  3358. */
  3359. ROT.FOV.PreciseShadowcasting = function(lightPassesCallback, options) {
  3360. ROT.FOV.call(this, lightPassesCallback, options);
  3361. }
  3362. ROT.FOV.PreciseShadowcasting.extend(ROT.FOV);
  3363. /**
  3364. * @see ROT.FOV#compute
  3365. */
  3366. ROT.FOV.PreciseShadowcasting.prototype.compute = function(x, y, R, callback) {
  3367. /* this place is always visible */
  3368. callback(x, y, 0, 1);
  3369. /* standing in a dark place. FIXME is this a good idea? */
  3370. if (!this._lightPasses(x, y)) { return; }
  3371. /* list of all shadows */
  3372. var SHADOWS = [];
  3373. var cx, cy, blocks, A1, A2, visibility;
  3374. /* analyze surrounding cells in concentric rings, starting from the center */
  3375. for (var r=1; r<=R; r++) {
  3376. var neighbors = this._getCircle(x, y, r);
  3377. var neighborCount = neighbors.length;
  3378. for (var i=0;i<neighborCount;i++) {
  3379. cx = neighbors[i][0];
  3380. cy = neighbors[i][1];
  3381. /* shift half-an-angle backwards to maintain consistency of 0-th cells */
  3382. A1 = [i ? 2*i-1 : 2*neighborCount-1, 2*neighborCount];
  3383. A2 = [2*i+1, 2*neighborCount];
  3384. blocks = !this._lightPasses(cx, cy);
  3385. visibility = this._checkVisibility(A1, A2, blocks, SHADOWS);
  3386. if (visibility) { callback(cx, cy, r, visibility); }
  3387. if (SHADOWS.length == 2 && SHADOWS[0][0] == 0 && SHADOWS[1][0] == SHADOWS[1][1]) { return; } /* cutoff? */
  3388. } /* for all cells in this ring */
  3389. } /* for all rings */
  3390. }
  3391. /**
  3392. * @param {int[2]} A1 arc start
  3393. * @param {int[2]} A2 arc end
  3394. * @param {bool} blocks Does current arc block visibility?
  3395. * @param {int[][]} SHADOWS list of active shadows
  3396. */
  3397. ROT.FOV.PreciseShadowcasting.prototype._checkVisibility = function(A1, A2, blocks, SHADOWS) {
  3398. if (A1[0] > A2[0]) { /* split into two sub-arcs */
  3399. var v1 = this._checkVisibility(A1, [A1[1], A1[1]], blocks, SHADOWS);
  3400. var v2 = this._checkVisibility([0, 1], A2, blocks, SHADOWS);
  3401. return (v1+v2)/2;
  3402. }
  3403. /* index1: first shadow >= A1 */
  3404. var index1 = 0, edge1 = false;
  3405. while (index1 < SHADOWS.length) {
  3406. var old = SHADOWS[index1];
  3407. var diff = old[0]*A1[1] - A1[0]*old[1];
  3408. if (diff >= 0) { /* old >= A1 */
  3409. if (diff == 0 && !(index1 % 2)) { edge1 = true; }
  3410. break;
  3411. }
  3412. index1++;
  3413. }
  3414. /* index2: last shadow <= A2 */
  3415. var index2 = SHADOWS.length, edge2 = false;
  3416. while (index2--) {
  3417. var old = SHADOWS[index2];
  3418. var diff = A2[0]*old[1] - old[0]*A2[1];
  3419. if (diff >= 0) { /* old <= A2 */
  3420. if (diff == 0 && (index2 % 2)) { edge2 = true; }
  3421. break;
  3422. }
  3423. }
  3424. var visible = true;
  3425. if (index1 == index2 && (edge1 || edge2)) { /* subset of existing shadow, one of the edges match */
  3426. visible = false;
  3427. } else if (edge1 && edge2 && index1+1==index2 && (index2 % 2)) { /* completely equivalent with existing shadow */
  3428. visible = false;
  3429. } else if (index1 > index2 && (index1 % 2)) { /* subset of existing shadow, not touching */
  3430. visible = false;
  3431. }
  3432. if (!visible) { return 0; } /* fast case: not visible */
  3433. var visibleLength, P;
  3434. /* compute the length of visible arc, adjust list of shadows (if blocking) */
  3435. var remove = index2-index1+1;
  3436. if (remove % 2) {
  3437. if (index1 % 2) { /* first edge within existing shadow, second outside */
  3438. var P = SHADOWS[index1];
  3439. visibleLength = (A2[0]*P[1] - P[0]*A2[1]) / (P[1] * A2[1]);
  3440. if (blocks) { SHADOWS.splice(index1, remove, A2); }
  3441. } else { /* second edge within existing shadow, first outside */
  3442. var P = SHADOWS[index2];
  3443. visibleLength = (P[0]*A1[1] - A1[0]*P[1]) / (A1[1] * P[1]);
  3444. if (blocks) { SHADOWS.splice(index1, remove, A1); }
  3445. }
  3446. } else {
  3447. if (index1 % 2) { /* both edges within existing shadows */
  3448. var P1 = SHADOWS[index1];
  3449. var P2 = SHADOWS[index2];
  3450. visibleLength = (P2[0]*P1[1] - P1[0]*P2[1]) / (P1[1] * P2[1]);
  3451. if (blocks) { SHADOWS.splice(index1, remove); }
  3452. } else { /* both edges outside existing shadows */
  3453. if (blocks) { SHADOWS.splice(index1, remove, A1, A2); }
  3454. return 1; /* whole arc visible! */
  3455. }
  3456. }
  3457. var arcLength = (A2[0]*A1[1] - A1[0]*A2[1]) / (A1[1] * A2[1]);
  3458. return visibleLength/arcLength;
  3459. }
  3460. /**
  3461. * @namespace Color operations
  3462. */
  3463. ROT.Color = {
  3464. fromString: function(str) {
  3465. var cached, r;
  3466. if (str in this._cache) {
  3467. cached = this._cache[str];
  3468. } else {
  3469. if (str.charAt(0) == "#") { /* hex rgb */
  3470. var values = str.match(/[0-9a-f]/gi).map(function(x) { return parseInt(x, 16); });
  3471. if (values.length == 3) {
  3472. cached = values.map(function(x) { return x*17; });
  3473. } else {
  3474. for (var i=0;i<3;i++) {
  3475. values[i+1] += 16*values[i];
  3476. values.splice(i, 1);
  3477. }
  3478. cached = values;
  3479. }
  3480. } else if (r = str.match(/rgb\(([0-9, ]+)\)/i)) { /* decimal rgb */
  3481. cached = r[1].split(/\s*,\s*/).map(function(x) { return parseInt(x); });
  3482. } else { /* html name */
  3483. cached = [0, 0, 0];
  3484. }
  3485. this._cache[str] = cached;
  3486. }
  3487. return cached.slice();
  3488. },
  3489. /**
  3490. * Add two or more colors
  3491. * @param {number[]} color1
  3492. * @param {number[]} color2
  3493. * @returns {number[]}
  3494. */
  3495. add: function(color1, color2) {
  3496. var result = color1.slice();
  3497. for (var i=0;i<3;i++) {
  3498. for (var j=1;j<arguments.length;j++) {
  3499. result[i] += arguments[j][i];
  3500. }
  3501. }
  3502. return result;
  3503. },
  3504. /**
  3505. * Add two or more colors, MODIFIES FIRST ARGUMENT
  3506. * @param {number[]} color1
  3507. * @param {number[]} color2
  3508. * @returns {number[]}
  3509. */
  3510. add_: function(color1, color2) {
  3511. for (var i=0;i<3;i++) {
  3512. for (var j=1;j<arguments.length;j++) {
  3513. color1[i] += arguments[j][i];
  3514. }
  3515. }
  3516. return color1;
  3517. },
  3518. /**
  3519. * Multiply (mix) two or more colors
  3520. * @param {number[]} color1
  3521. * @param {number[]} color2
  3522. * @returns {number[]}
  3523. */
  3524. multiply: function(color1, color2) {
  3525. var result = color1.slice();
  3526. for (var i=0;i<3;i++) {
  3527. for (var j=1;j<arguments.length;j++) {
  3528. result[i] *= arguments[j][i] / 255;
  3529. }
  3530. result[i] = Math.round(result[i]);
  3531. }
  3532. return result;
  3533. },
  3534. /**
  3535. * Multiply (mix) two or more colors, MODIFIES FIRST ARGUMENT
  3536. * @param {number[]} color1
  3537. * @param {number[]} color2
  3538. * @returns {number[]}
  3539. */
  3540. multiply_: function(color1, color2) {
  3541. for (var i=0;i<3;i++) {
  3542. for (var j=1;j<arguments.length;j++) {
  3543. color1[i] *= arguments[j][i] / 255;
  3544. }
  3545. color1[i] = Math.round(color1[i]);
  3546. }
  3547. return color1;
  3548. },
  3549. /**
  3550. * Interpolate (blend) two colors with a given factor
  3551. * @param {number[]} color1
  3552. * @param {number[]} color2
  3553. * @param {float} [factor=0.5] 0..1
  3554. * @returns {number[]}
  3555. */
  3556. interpolate: function(color1, color2, factor) {
  3557. if (arguments.length < 3) { factor = 0.5; }
  3558. var result = color1.slice();
  3559. for (var i=0;i<3;i++) {
  3560. result[i] = Math.round(result[i] + factor*(color2[i]-color1[i]));
  3561. }
  3562. return result;
  3563. },
  3564. /**
  3565. * Interpolate (blend) two colors with a given factor in HSL mode
  3566. * @param {number[]} color1
  3567. * @param {number[]} color2
  3568. * @param {float} [factor=0.5] 0..1
  3569. * @returns {number[]}
  3570. */
  3571. interpolateHSL: function(color1, color2, factor) {
  3572. if (arguments.length < 3) { factor = 0.5; }
  3573. var hsl1 = this.rgb2hsl(color1);
  3574. var hsl2 = this.rgb2hsl(color2);
  3575. for (var i=0;i<3;i++) {
  3576. hsl1[i] += factor*(hsl2[i]-hsl1[i]);
  3577. }
  3578. return this.hsl2rgb(hsl1);
  3579. },
  3580. /**
  3581. * Create a new random color based on this one
  3582. * @param {number[]} color
  3583. * @param {number[]} diff Set of standard deviations
  3584. * @returns {number[]}
  3585. */
  3586. randomize: function(color, diff) {
  3587. if (!(diff instanceof Array)) { diff = ROT.RNG.getNormal(0, diff); }
  3588. var result = color.slice();
  3589. for (var i=0;i<3;i++) {
  3590. result[i] += (diff instanceof Array ? Math.round(ROT.RNG.getNormal(0, diff[i])) : diff);
  3591. }
  3592. return result;
  3593. },
  3594. /**
  3595. * Converts an RGB color value to HSL. Expects 0..255 inputs, produces 0..1 outputs.
  3596. * @param {number[]} color
  3597. * @returns {number[]}
  3598. */
  3599. rgb2hsl: function(color) {
  3600. var r = color[0]/255;
  3601. var g = color[1]/255;
  3602. var b = color[2]/255;
  3603. var max = Math.max(r, g, b), min = Math.min(r, g, b);
  3604. var h, s, l = (max + min) / 2;
  3605. if (max == min) {
  3606. h = s = 0; // achromatic
  3607. } else {
  3608. var d = max - min;
  3609. s = (l > 0.5 ? d / (2 - max - min) : d / (max + min));
  3610. switch(max) {
  3611. case r: h = (g - b) / d + (g < b ? 6 : 0); break;
  3612. case g: h = (b - r) / d + 2; break;
  3613. case b: h = (r - g) / d + 4; break;
  3614. }
  3615. h /= 6;
  3616. }
  3617. return [h, s, l];
  3618. },
  3619. /**
  3620. * Converts an HSL color value to RGB. Expects 0..1 inputs, produces 0..255 outputs.
  3621. * @param {number[]} color
  3622. * @returns {number[]}
  3623. */
  3624. hsl2rgb: function(color) {
  3625. var l = color[2];
  3626. if (color[1] == 0) {
  3627. l *= 255;
  3628. return [l, l, l];
  3629. } else {
  3630. function hue2rgb(p, q, t) {
  3631. if (t < 0) t += 1;
  3632. if (t > 1) t -= 1;
  3633. if (t < 1/6) return p + (q - p) * 6 * t;
  3634. if (t < 1/2) return q;
  3635. if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
  3636. return p;
  3637. }
  3638. var s = color[1];
  3639. var q = (l < 0.5 ? l * (1 + s) : l + s - l * s);
  3640. var p = 2 * l - q;
  3641. var r = hue2rgb(p, q, color[0] + 1/3);
  3642. var g = hue2rgb(p, q, color[0]);
  3643. var b = hue2rgb(p, q, color[0] - 1/3);
  3644. return [Math.round(r*255), Math.round(g*255), Math.round(b*255)];
  3645. }
  3646. },
  3647. toRGB: function(color) {
  3648. return "rgb(" + this._clamp(color[0]) + "," + this._clamp(color[1]) + "," + this._clamp(color[2]) + ")";
  3649. },
  3650. toHex: function(color) {
  3651. var parts = [];
  3652. for (var i=0;i<3;i++) {
  3653. parts.push(this._clamp(color[i]).toString(16).lpad("0", 2));
  3654. }
  3655. return "#" + parts.join("");
  3656. },
  3657. _clamp: function(num) {
  3658. if (num < 0) {
  3659. return 0;
  3660. } else if (num > 255) {
  3661. return 255;
  3662. } else {
  3663. return num;
  3664. }
  3665. },
  3666. _cache: {
  3667. "black": [0,0,0],
  3668. "navy": [0,0,128],
  3669. "darkblue": [0,0,139],
  3670. "mediumblue": [0,0,205],
  3671. "blue": [0,0,255],
  3672. "darkgreen": [0,100,0],
  3673. "green": [0,128,0],
  3674. "teal": [0,128,128],
  3675. "darkcyan": [0,139,139],
  3676. "deepskyblue": [0,191,255],
  3677. "darkturquoise": [0,206,209],
  3678. "mediumspringgreen": [0,250,154],
  3679. "lime": [0,255,0],
  3680. "springgreen": [0,255,127],
  3681. "aqua": [0,255,255],
  3682. "cyan": [0,255,255],
  3683. "midnightblue": [25,25,112],
  3684. "dodgerblue": [30,144,255],
  3685. "forestgreen": [34,139,34],
  3686. "seagreen": [46,139,87],
  3687. "darkslategray": [47,79,79],
  3688. "darkslategrey": [47,79,79],
  3689. "limegreen": [50,205,50],
  3690. "mediumseagreen": [60,179,113],
  3691. "turquoise": [64,224,208],
  3692. "royalblue": [65,105,225],
  3693. "steelblue": [70,130,180],
  3694. "darkslateblue": [72,61,139],
  3695. "mediumturquoise": [72,209,204],
  3696. "indigo": [75,0,130],
  3697. "darkolivegreen": [85,107,47],
  3698. "cadetblue": [95,158,160],
  3699. "cornflowerblue": [100,149,237],
  3700. "mediumaquamarine": [102,205,170],
  3701. "dimgray": [105,105,105],
  3702. "dimgrey": [105,105,105],
  3703. "slateblue": [106,90,205],
  3704. "olivedrab": [107,142,35],
  3705. "slategray": [112,128,144],
  3706. "slategrey": [112,128,144],
  3707. "lightslategray": [119,136,153],
  3708. "lightslategrey": [119,136,153],
  3709. "mediumslateblue": [123,104,238],
  3710. "lawngreen": [124,252,0],
  3711. "chartreuse": [127,255,0],
  3712. "aquamarine": [127,255,212],
  3713. "maroon": [128,0,0],
  3714. "purple": [128,0,128],
  3715. "olive": [128,128,0],
  3716. "gray": [128,128,128],
  3717. "grey": [128,128,128],
  3718. "skyblue": [135,206,235],
  3719. "lightskyblue": [135,206,250],
  3720. "blueviolet": [138,43,226],
  3721. "darkred": [139,0,0],
  3722. "darkmagenta": [139,0,139],
  3723. "saddlebrown": [139,69,19],
  3724. "darkseagreen": [143,188,143],
  3725. "lightgreen": [144,238,144],
  3726. "mediumpurple": [147,112,216],
  3727. "darkviolet": [148,0,211],
  3728. "palegreen": [152,251,152],
  3729. "darkorchid": [153,50,204],
  3730. "yellowgreen": [154,205,50],
  3731. "sienna": [160,82,45],
  3732. "brown": [165,42,42],
  3733. "darkgray": [169,169,169],
  3734. "darkgrey": [169,169,169],
  3735. "lightblue": [173,216,230],
  3736. "greenyellow": [173,255,47],
  3737. "paleturquoise": [175,238,238],
  3738. "lightsteelblue": [176,196,222],
  3739. "powderblue": [176,224,230],
  3740. "firebrick": [178,34,34],
  3741. "darkgoldenrod": [184,134,11],
  3742. "mediumorchid": [186,85,211],
  3743. "rosybrown": [188,143,143],
  3744. "darkkhaki": [189,183,107],
  3745. "silver": [192,192,192],
  3746. "mediumvioletred": [199,21,133],
  3747. "indianred": [205,92,92],
  3748. "peru": [205,133,63],
  3749. "chocolate": [210,105,30],
  3750. "tan": [210,180,140],
  3751. "lightgray": [211,211,211],
  3752. "lightgrey": [211,211,211],
  3753. "palevioletred": [216,112,147],
  3754. "thistle": [216,191,216],
  3755. "orchid": [218,112,214],
  3756. "goldenrod": [218,165,32],
  3757. "crimson": [220,20,60],
  3758. "gainsboro": [220,220,220],
  3759. "plum": [221,160,221],
  3760. "burlywood": [222,184,135],
  3761. "lightcyan": [224,255,255],
  3762. "lavender": [230,230,250],
  3763. "darksalmon": [233,150,122],
  3764. "violet": [238,130,238],
  3765. "palegoldenrod": [238,232,170],
  3766. "lightcoral": [240,128,128],
  3767. "khaki": [240,230,140],
  3768. "aliceblue": [240,248,255],
  3769. "honeydew": [240,255,240],
  3770. "azure": [240,255,255],
  3771. "sandybrown": [244,164,96],
  3772. "wheat": [245,222,179],
  3773. "beige": [245,245,220],
  3774. "whitesmoke": [245,245,245],
  3775. "mintcream": [245,255,250],
  3776. "ghostwhite": [248,248,255],
  3777. "salmon": [250,128,114],
  3778. "antiquewhite": [250,235,215],
  3779. "linen": [250,240,230],
  3780. "lightgoldenrodyellow": [250,250,210],
  3781. "oldlace": [253,245,230],
  3782. "red": [255,0,0],
  3783. "fuchsia": [255,0,255],
  3784. "magenta": [255,0,255],
  3785. "deeppink": [255,20,147],
  3786. "orangered": [255,69,0],
  3787. "tomato": [255,99,71],
  3788. "hotpink": [255,105,180],
  3789. "coral": [255,127,80],
  3790. "darkorange": [255,140,0],
  3791. "lightsalmon": [255,160,122],
  3792. "orange": [255,165,0],
  3793. "lightpink": [255,182,193],
  3794. "pink": [255,192,203],
  3795. "gold": [255,215,0],
  3796. "peachpuff": [255,218,185],
  3797. "navajowhite": [255,222,173],
  3798. "moccasin": [255,228,181],
  3799. "bisque": [255,228,196],
  3800. "mistyrose": [255,228,225],
  3801. "blanchedalmond": [255,235,205],
  3802. "papayawhip": [255,239,213],
  3803. "lavenderblush": [255,240,245],
  3804. "seashell": [255,245,238],
  3805. "cornsilk": [255,248,220],
  3806. "lemonchiffon": [255,250,205],
  3807. "floralwhite": [255,250,240],
  3808. "snow": [255,250,250],
  3809. "yellow": [255,255,0],
  3810. "lightyellow": [255,255,224],
  3811. "ivory": [255,255,240],
  3812. "white": [255,255,255]
  3813. }
  3814. }
  3815. /**
  3816. * @class Lighting computation, based on a traditional FOV for multiple light sources and multiple passes.
  3817. * @param {function} reflectivityCallback Callback to retrieve cell reflectivity (0..1)
  3818. * @param {object} [options]
  3819. * @param {int} [options.passes=1] Number of passes. 1 equals to simple FOV of all light sources, >1 means a *highly simplified* radiosity-like algorithm.
  3820. * @param {int} [options.emissionThreshold=100] Cells with emissivity > threshold will be treated as light source in the next pass.
  3821. * @param {int} [options.range=10] Max light range
  3822. */
  3823. ROT.Lighting = function(reflectivityCallback, options) {
  3824. this._reflectivityCallback = reflectivityCallback;
  3825. this._options = {
  3826. passes: 1,
  3827. emissionThreshold: 100,
  3828. range: 10
  3829. };
  3830. this._fov = null;
  3831. this._lights = {};
  3832. this._reflectivityCache = {};
  3833. this._fovCache = {};
  3834. this.setOptions(options);
  3835. }
  3836. /**
  3837. * Adjust options at runtime
  3838. * @see ROT.Lighting
  3839. * @param {object} [options]
  3840. */
  3841. ROT.Lighting.prototype.setOptions = function(options) {
  3842. for (var p in options) { this._options[p] = options[p]; }
  3843. if (options.range) { this.reset(); }
  3844. return this;
  3845. }
  3846. /**
  3847. * Set the used Field-Of-View algo
  3848. * @param {ROT.FOV} fov
  3849. */
  3850. ROT.Lighting.prototype.setFOV = function(fov) {
  3851. this._fov = fov;
  3852. this._fovCache = {};
  3853. return this;
  3854. }
  3855. /**
  3856. * Set (or remove) a light source
  3857. * @param {int} x
  3858. * @param {int} y
  3859. * @param {null || string || number[3]} color
  3860. */
  3861. ROT.Lighting.prototype.setLight = function(x, y, color) {
  3862. var key = x+","+y;
  3863. if (color) {
  3864. this._lights[key] = (typeof(color) == "string" ? ROT.Color.fromString(color) : color);
  3865. } else {
  3866. delete this._lights[key];
  3867. }
  3868. return this;
  3869. }
  3870. /**
  3871. * Reset the pre-computed topology values. Call whenever the underlying map changes its light-passability.
  3872. */
  3873. ROT.Lighting.prototype.reset = function() {
  3874. this._reflectivityCache = {};
  3875. this._fovCache = {};
  3876. return this;
  3877. }
  3878. /**
  3879. * Compute the lighting
  3880. * @param {function} lightingCallback Will be called with (x, y, color) for every lit cell
  3881. */
  3882. ROT.Lighting.prototype.compute = function(lightingCallback) {
  3883. var doneCells = {};
  3884. var emittingCells = {};
  3885. var litCells = {};
  3886. for (var key in this._lights) { /* prepare emitters for first pass */
  3887. var light = this._lights[key];
  3888. if (!(key in emittingCells)) { emittingCells[key] = [0, 0, 0]; }
  3889. ROT.Color.add_(emittingCells[key], light);
  3890. }
  3891. for (var i=0;i<this._options.passes;i++) { /* main loop */
  3892. this._emitLight(emittingCells, litCells, doneCells);
  3893. if (i+1 == this._options.passes) { continue; } /* not for the last pass */
  3894. emittingCells = this._computeEmitters(litCells, doneCells);
  3895. }
  3896. for (var litKey in litCells) { /* let the user know what and how is lit */
  3897. var parts = litKey.split(",");
  3898. var x = parseInt(parts[0]);
  3899. var y = parseInt(parts[1]);
  3900. lightingCallback(x, y, litCells[litKey]);
  3901. }
  3902. return this;
  3903. }
  3904. /**
  3905. * Compute one iteration from all emitting cells
  3906. * @param {object} emittingCells These emit light
  3907. * @param {object} litCells Add projected light to these
  3908. * @param {object} doneCells These already emitted, forbid them from further calculations
  3909. */
  3910. ROT.Lighting.prototype._emitLight = function(emittingCells, litCells, doneCells) {
  3911. for (var key in emittingCells) {
  3912. var parts = key.split(",");
  3913. var x = parseInt(parts[0]);
  3914. var y = parseInt(parts[1]);
  3915. this._emitLightFromCell(x, y, emittingCells[key], litCells);
  3916. doneCells[key] = 1;
  3917. }
  3918. return this;
  3919. }
  3920. /**
  3921. * Prepare a list of emitters for next pass
  3922. * @param {object} litCells
  3923. * @param {object} doneCells
  3924. * @returns {object}
  3925. */
  3926. ROT.Lighting.prototype._computeEmitters = function(litCells, doneCells) {
  3927. var result = {};
  3928. for (var key in litCells) {
  3929. if (key in doneCells) { continue; } /* already emitted */
  3930. var color = litCells[key];
  3931. if (key in this._reflectivityCache) {
  3932. var reflectivity = this._reflectivityCache[key];
  3933. } else {
  3934. var parts = key.split(",");
  3935. var x = parseInt(parts[0]);
  3936. var y = parseInt(parts[1]);
  3937. var reflectivity = this._reflectivityCallback(x, y);
  3938. this._reflectivityCache[key] = reflectivity;
  3939. }
  3940. if (reflectivity == 0) { continue; } /* will not reflect at all */
  3941. /* compute emission color */
  3942. var emission = [];
  3943. var intensity = 0;
  3944. for (var i=0;i<3;i++) {
  3945. var part = Math.round(color[i]*reflectivity);
  3946. emission[i] = part;
  3947. intensity += part;
  3948. }
  3949. if (intensity > this._options.emissionThreshold) { result[key] = emission; }
  3950. }
  3951. return result;
  3952. }
  3953. /**
  3954. * Compute one iteration from one cell
  3955. * @param {int} x
  3956. * @param {int} y
  3957. * @param {number[]} color
  3958. * @param {object} litCells Cell data to by updated
  3959. */
  3960. ROT.Lighting.prototype._emitLightFromCell = function(x, y, color, litCells) {
  3961. var key = x+","+y;
  3962. if (key in this._fovCache) {
  3963. var fov = this._fovCache[key];
  3964. } else {
  3965. var fov = this._updateFOV(x, y);
  3966. }
  3967. for (var fovKey in fov) {
  3968. var formFactor = fov[fovKey];
  3969. if (fovKey in litCells) { /* already lit */
  3970. var result = litCells[fovKey];
  3971. } else { /* newly lit */
  3972. var result = [0, 0, 0];
  3973. litCells[fovKey] = result;
  3974. }
  3975. for (var i=0;i<3;i++) { result[i] += Math.round(color[i]*formFactor); } /* add light color */
  3976. }
  3977. return this;
  3978. }
  3979. /**
  3980. * Compute FOV ("form factor") for a potential light source at [x,y]
  3981. * @param {int} x
  3982. * @param {int} y
  3983. * @returns {object}
  3984. */
  3985. ROT.Lighting.prototype._updateFOV = function(x, y) {
  3986. var key1 = x+","+y;
  3987. var cache = {};
  3988. this._fovCache[key1] = cache;
  3989. var range = this._options.range;
  3990. var cb = function(x, y, r, vis) {
  3991. var key2 = x+","+y;
  3992. var formFactor = vis * (1-r/range);
  3993. if (formFactor == 0) { return; }
  3994. cache[key2] = formFactor;
  3995. }
  3996. this._fov.compute(x, y, range, cb.bind(this));
  3997. return cache;
  3998. }
  3999. /**
  4000. * @class Abstract pathfinder
  4001. * @param {int} toX Target X coord
  4002. * @param {int} toY Target Y coord
  4003. * @param {function} passableCallback Callback to determine map passability
  4004. * @param {object} [options]
  4005. * @param {int} [options.topology=8]
  4006. */
  4007. ROT.Path = function(toX, toY, passableCallback, options) {
  4008. this._toX = toX;
  4009. this._toY = toY;
  4010. this._fromX = null;
  4011. this._fromY = null;
  4012. this._passableCallback = passableCallback;
  4013. this._options = {
  4014. topology: 8
  4015. }
  4016. for (var p in options) { this._options[p] = options[p]; }
  4017. this._dirs = ROT.DIRS[this._options.topology];
  4018. if (this._options.topology == 8) { /* reorder dirs for more aesthetic result (vertical/horizontal first) */
  4019. this._dirs = [
  4020. this._dirs[0],
  4021. this._dirs[2],
  4022. this._dirs[4],
  4023. this._dirs[6],
  4024. this._dirs[1],
  4025. this._dirs[3],
  4026. this._dirs[5],
  4027. this._dirs[7]
  4028. ]
  4029. }
  4030. }
  4031. /**
  4032. * Compute a path from a given point
  4033. * @param {int} fromX
  4034. * @param {int} fromY
  4035. * @param {function} callback Will be called for every path item with arguments "x" and "y"
  4036. */
  4037. ROT.Path.prototype.compute = function(fromX, fromY, callback) {
  4038. }
  4039. ROT.Path.prototype._getNeighbors = function(cx, cy) {
  4040. var result = [];
  4041. for (var i=0;i<this._dirs.length;i++) {
  4042. var dir = this._dirs[i];
  4043. var x = cx + dir[0];
  4044. var y = cy + dir[1];
  4045. if (!this._passableCallback(x, y)) { continue; }
  4046. result.push([x, y]);
  4047. }
  4048. return result;
  4049. }
  4050. /**
  4051. * @class Simplified Dijkstra's algorithm: all edges have a value of 1
  4052. * @augments ROT.Path
  4053. * @see ROT.Path
  4054. */
  4055. ROT.Path.Dijkstra = function(toX, toY, passableCallback, options) {
  4056. ROT.Path.call(this, toX, toY, passableCallback, options);
  4057. this._computed = {};
  4058. this._todo = [];
  4059. this._add(toX, toY, null);
  4060. }
  4061. ROT.Path.Dijkstra.extend(ROT.Path);
  4062. /**
  4063. * Compute a path from a given point
  4064. * @see ROT.Path#compute
  4065. */
  4066. ROT.Path.Dijkstra.prototype.compute = function(fromX, fromY, callback) {
  4067. var key = fromX+","+fromY;
  4068. if (!(key in this._computed)) { this._compute(fromX, fromY); }
  4069. if (!(key in this._computed)) { return; }
  4070. var item = this._computed[key];
  4071. while (item) {
  4072. callback(item.x, item.y);
  4073. item = item.prev;
  4074. }
  4075. }
  4076. /**
  4077. * Compute a non-cached value
  4078. */
  4079. ROT.Path.Dijkstra.prototype._compute = function(fromX, fromY) {
  4080. while (this._todo.length) {
  4081. var item = this._todo.shift();
  4082. if (item.x == fromX && item.y == fromY) { return; }
  4083. var neighbors = this._getNeighbors(item.x, item.y);
  4084. for (var i=0;i<neighbors.length;i++) {
  4085. var neighbor = neighbors[i];
  4086. var x = neighbor[0];
  4087. var y = neighbor[1];
  4088. var id = x+","+y;
  4089. if (id in this._computed) { continue; } /* already done */
  4090. this._add(x, y, item);
  4091. }
  4092. }
  4093. }
  4094. ROT.Path.Dijkstra.prototype._add = function(x, y, prev) {
  4095. var obj = {
  4096. x: x,
  4097. y: y,
  4098. prev: prev
  4099. }
  4100. this._computed[x+","+y] = obj;
  4101. this._todo.push(obj);
  4102. }
  4103. /**
  4104. * @class Simplified A* algorithm: all edges have a value of 1
  4105. * @augments ROT.Path
  4106. * @see ROT.Path
  4107. */
  4108. ROT.Path.AStar = function(toX, toY, passableCallback, options) {
  4109. ROT.Path.call(this, toX, toY, passableCallback, options);
  4110. this._todo = [];
  4111. this._done = {};
  4112. this._fromX = null;
  4113. this._fromY = null;
  4114. }
  4115. ROT.Path.AStar.extend(ROT.Path);
  4116. /**
  4117. * Compute a path from a given point
  4118. * @see ROT.Path#compute
  4119. */
  4120. ROT.Path.AStar.prototype.compute = function(fromX, fromY, callback) {
  4121. this._todo = [];
  4122. this._done = {};
  4123. this._fromX = fromX;
  4124. this._fromY = fromY;
  4125. this._add(this._toX, this._toY, null);
  4126. while (this._todo.length) {
  4127. var item = this._todo.shift();
  4128. if (item.x == fromX && item.y == fromY) { break; }
  4129. var neighbors = this._getNeighbors(item.x, item.y);
  4130. for (var i=0;i<neighbors.length;i++) {
  4131. var neighbor = neighbors[i];
  4132. var x = neighbor[0];
  4133. var y = neighbor[1];
  4134. var id = x+","+y;
  4135. if (id in this._done) { continue; }
  4136. this._add(x, y, item);
  4137. }
  4138. }
  4139. var item = this._done[fromX+","+fromY];
  4140. if (!item) { return; }
  4141. while (item) {
  4142. callback(item.x, item.y);
  4143. item = item.prev;
  4144. }
  4145. }
  4146. ROT.Path.AStar.prototype._add = function(x, y, prev) {
  4147. var obj = {
  4148. x: x,
  4149. y: y,
  4150. prev: prev,
  4151. g: (prev ? prev.g+1 : 0),
  4152. h: this._distance(x, y)
  4153. }
  4154. this._done[x+","+y] = obj;
  4155. /* insert into priority queue */
  4156. var f = obj.g + obj.h;
  4157. for (var i=0;i<this._todo.length;i++) {
  4158. var item = this._todo[i];
  4159. if (f < item.g + item.h) {
  4160. this._todo.splice(i, 0, obj);
  4161. return;
  4162. }
  4163. }
  4164. this._todo.push(obj);
  4165. }
  4166. ROT.Path.AStar.prototype._distance = function(x, y) {
  4167. switch (this._options.topology) {
  4168. case 4:
  4169. return (Math.abs(x-this._fromX) + Math.abs(y-this._fromY));
  4170. break;
  4171. case 6:
  4172. var dx = Math.abs(x - this._fromX);
  4173. var dy = Math.abs(y - this._fromY);
  4174. return dy + Math.max(0, (dx-dy)/2);
  4175. break;
  4176. case 8:
  4177. return Math.max(Math.abs(x-this._fromX), Math.abs(y-this._fromY));
  4178. break;
  4179. }
  4180. }