From e29588d6e154dd6c53a15c1fd4312a463e76b922 Mon Sep 17 00:00:00 2001 From: Yann Ducrocq Date: Sun, 17 Feb 2013 12:03:06 +0100 Subject: [PATCH 01/20] _digCallback function fix check if this._map[x] exists in the _digCallback function --- rot.js | 1 + 1 file changed, 1 insertion(+) diff --git a/rot.js b/rot.js index 0d847d64..bf4dce60 100644 --- a/rot.js +++ b/rot.js @@ -2048,6 +2048,7 @@ ROT.Map.Digger.prototype.create = function(callback) { ROT.Map.Digger.prototype._digCallback = function(x, y, value) { if (value == 0 || value == 2) { /* empty */ + if (!this._map[x]) this._map[x]=[]; this._map[x][y] = 0; this._dug++; } else { /* wall */ From 9813bdd0f665e16b713b5116c3e5be1199471e3c Mon Sep 17 00:00:00 2001 From: mdtrooper Date: Wed, 7 Aug 2013 01:09:16 +0200 Subject: [PATCH 02/20] Added the first version of ASCII art image converter (from http://blog.nihilogic.dk/2008/03/jsascii.html). --- src/image/image.js | 5 ++ src/image/jsascii.js | 194 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 src/image/image.js create mode 100644 src/image/jsascii.js diff --git a/src/image/image.js b/src/image/image.js new file mode 100644 index 00000000..81e22a68 --- /dev/null +++ b/src/image/image.js @@ -0,0 +1,5 @@ +/** + * @class Base image (as ASCII art) converter + */ +ROT.image = function() { +}; \ No newline at end of file diff --git a/src/image/jsascii.js b/src/image/jsascii.js new file mode 100644 index 00000000..7af9886b --- /dev/null +++ b/src/image/jsascii.js @@ -0,0 +1,194 @@ +/* + * jsAscii 0.1 + * Copyright (c) 2008 Jacob Seidelin, jseidelin@nihilogic.dk, http://blog.nihilogic.dk/ + * MIT License [http://www.nihilogic.dk/licenses/mit-license.txt] + */ + +var jsAscii = (function() { + + var aDefaultCharList = (" .,:;i1tfLCG08@").split(""); + var aDefaultColorCharList = (" CGO08@").split(""); + var strFont = "courier new"; + + + // convert img element to ascii + function asciifyImage(oImg, oCanvasImg) + { + var oCanvas = document.createElement("canvas"); + if (!oCanvas.getContext) { + return; + } + var oCtx = oCanvas.getContext("2d"); + if (!oCtx.getImageData) { + return; + } + + var iScale = parseInt(oImg.getAttribute("asciiscale")) || 1; + var bColor = (oImg.getAttribute("asciicolor") == "true"); + var bAlpha = (oImg.getAttribute("asciialpha") == "true"); + var bBlock = (oImg.getAttribute("asciiblock") == "true"); + var bInvert = (oImg.getAttribute("asciiinvert") == "true"); + var strResolution = oImg.getAttribute("asciiresolution") || "medium"; + var aCharList = oImg.getAttribute("asciichars") || + (bColor ? aDefaultColorCharList : aDefaultCharList); + + var fResolution = 0.5; + switch (strResolution) { + case "low" : fResolution = 0.25; break; + case "medium" : fResolution = 0.5; break; + case "high" : fResolution = 1; break; + } + + var iWidth = Math.round(parseInt(oImg.offsetWidth) * fResolution); + var iHeight = Math.round(parseInt(oImg.offsetHeight) * fResolution); + + oCanvas.width = iWidth; + oCanvas.height = iHeight; + oCanvas.style.display = "none"; + oCanvas.style.width = iWidth; + oCanvas.style.height = iHeight; + + oCtx.drawImage(oCanvasImg, 0, 0, iWidth, iHeight); + var oImgData = oCtx.getImageData(0, 0, iWidth, iHeight).data; + + var strChars = ""; + + for (var y=0;y" + strThisChar + ""; + } else { + strChars += strThisChar; + } + } + strChars += "
"; + } + + + var fFontSize = (2/fResolution)*iScale; + var fLineHeight = (2/fResolution)*iScale; + + // adjust letter-spacing for all combinations of scale and resolution to get it to fit the image width. + var fLetterSpacing = 0; + if (strResolution == "low") { + switch (iScale) { + case 1 : fLetterSpacing = -1; break; + case 2 : + case 3 : fLetterSpacing = -2.1; break; + case 4 : fLetterSpacing = -3.1; break; + case 5 : fLetterSpacing = -4.15; break; + } + } + if (strResolution == "medium") { + switch (iScale) { + case 1 : fLetterSpacing = 0; break; + case 2 : fLetterSpacing = -1; break; + case 3 : fLetterSpacing = -1.04; break; + case 4 : + case 5 : fLetterSpacing = -2.1; break; + } + } + if (strResolution == "high") { + switch (iScale) { + case 1 : + case 2 : fLetterSpacing = 0; break; + case 3 : + case 4 : + case 5 : fLetterSpacing = -1; break; + } + } + + + // can't get a span or div to flow like an img element, but a table works? + var oAscii = document.createElement("table"); + oAscii.innerHTML = "" + strChars + ""; + + if (oImg.style.backgroundColor) { + oAscii.rows[0].cells[0].style.backgroundColor = oImg.style.backgroundColor; + oAscii.rows[0].cells[0].style.color = oImg.style.color; + } + + oAscii.cellSpacing = 0; + oAscii.cellPadding = 0; + + var oStyle = oAscii.style; + oStyle.display = "inline"; + oStyle.width = Math.round(iWidth/fResolution*iScale) + "px"; + oStyle.height = Math.round(iHeight/fResolution*iScale) + "px"; + oStyle.whiteSpace = "pre"; + oStyle.margin = "0px"; + oStyle.padding = "0px"; + oStyle.letterSpacing = fLetterSpacing + "px"; + oStyle.fontFamily = strFont; + oStyle.fontSize = fFontSize + "px"; + oStyle.lineHeight = fLineHeight + "px"; + oStyle.textAlign = "left"; + oStyle.textDecoration = "none"; + + oImg.parentNode.replaceChild(oAscii, oImg); + + } + + // load the image file + function asciifyImageLoad(oImg) + { + var oCanvasImg = new Image(); + oCanvasImg.src = oImg.src; + if (oCanvasImg.complete) { + asciifyImage(oImg, oCanvasImg); + } else { + oCanvasImg.onload = function() { + asciifyImage(oImg, oCanvasImg) + } + } + } + + return function() { + var aImg = document.getElementsByTagName("img"); + var aImages = []; + for (var i=0;i Date: Fri, 9 Aug 2013 01:25:26 +0200 Subject: [PATCH 03/20] Start to create the Image class from jsascii code. And added into the "makefile" for rot.js . --- src/gen-rot.sh | 1 + src/image/image.js | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/gen-rot.sh b/src/gen-rot.sh index a98ca5dd..e80242d6 100755 --- a/src/gen-rot.sh +++ b/src/gen-rot.sh @@ -40,6 +40,7 @@ lighting.js path/path.js path/dijkstra.js path/astar.js +image/image.js " star() { diff --git a/src/image/image.js b/src/image/image.js index 81e22a68..d0ab5926 100644 --- a/src/image/image.js +++ b/src/image/image.js @@ -1,5 +1,12 @@ /** * @class Base image (as ASCII art) converter */ -ROT.image = function() { -}; \ No newline at end of file +ROT.Image = function() { + this.aDefaultCharList = (" .,:;i1tfLCG08@").split(""); + this.aDefaultColorCharList = (" CGO08@").split(""); + this.strFont = "courier new"; +}; + +ROT.Image.prototype.get = function(xin, yin) { + console.log(888); +} From eeafcdf536cc6f293eb47e2b11e31709c536babf Mon Sep 17 00:00:00 2001 From: mdtrooper Date: Fri, 9 Aug 2013 01:27:13 +0200 Subject: [PATCH 04/20] Added a directory with some examples (well it is more a working directory for check the rot.js ;) ). --- examples/rot.js | 4743 ++++++++++++++++++++++++++++++++++++++++++++ examples/test.html | 31 + 2 files changed, 4774 insertions(+) create mode 100644 examples/rot.js create mode 100644 examples/test.html diff --git a/examples/rot.js b/examples/rot.js new file mode 100644 index 00000000..fcc2dc6d --- /dev/null +++ b/examples/rot.js @@ -0,0 +1,4743 @@ +/* + This is rot.js, the ROguelike Toolkit in JavaScript. + Version 0.5~dev, generated on vie ago 9 01:23:35 CEST 2013. +*/ + +/** + * @namespace Top-level ROT namespace + */ +var ROT = { + /** + * @returns {bool} Is rot.js supported by this browser? + */ + isSupported: function() { + return !!(document.createElement("canvas").getContext && Function.prototype.bind); + }, + + /** Default with for display and map generators */ + DEFAULT_WIDTH: 80, + /** Default height for display and map generators */ + DEFAULT_HEIGHT: 25, + + /** Directional constants. Ordering is important! */ + DIRS: { + "4": [ + [ 0, -1], + [ 1, 0], + [ 0, 1], + [-1, 0] + ], + "8": [ + [ 0, -1], + [ 1, -1], + [ 1, 0], + [ 1, 1], + [ 0, 1], + [-1, 1], + [-1, 0], + [-1, -1] + ], + "6": [ + [-1, -1], + [ 1, -1], + [ 2, 0], + [ 1, 1], + [-1, 1], + [-2, 0] + ] + }, + + /** Cancel key. */ + VK_CANCEL: 3, + /** Help key. */ + VK_HELP: 6, + /** Backspace key. */ + VK_BACK_SPACE: 8, + /** Tab key. */ + VK_TAB: 9, + /** 5 key on Numpad when NumLock is unlocked. Or on Mac, clear key which is positioned at NumLock key. */ + VK_CLEAR: 12, + /** Return/enter key on the main keyboard. */ + VK_RETURN: 13, + /** Reserved, but not used. */ + VK_ENTER: 14, + /** Shift key. */ + VK_SHIFT: 16, + /** Control key. */ + VK_CONTROL: 17, + /** Alt (Option on Mac) key. */ + VK_ALT: 18, + /** Pause key. */ + VK_PAUSE: 19, + /** Caps lock. */ + VK_CAPS_LOCK: 20, + /** Escape key. */ + VK_ESCAPE: 27, + /** Space bar. */ + VK_SPACE: 32, + /** Page Up key. */ + VK_PAGE_UP: 33, + /** Page Down key. */ + VK_PAGE_DOWN: 34, + /** End key. */ + VK_END: 35, + /** Home key. */ + VK_HOME: 36, + /** Left arrow. */ + VK_LEFT: 37, + /** Up arrow. */ + VK_UP: 38, + /** Right arrow. */ + VK_RIGHT: 39, + /** Down arrow. */ + VK_DOWN: 40, + /** Print Screen key. */ + VK_PRINTSCREEN: 44, + /** Ins(ert) key. */ + VK_INSERT: 45, + /** Del(ete) key. */ + VK_DELETE: 46, + /***/ + VK_0: 48, + /***/ + VK_1: 49, + /***/ + VK_2: 50, + /***/ + VK_3: 51, + /***/ + VK_4: 52, + /***/ + VK_5: 53, + /***/ + VK_6: 54, + /***/ + VK_7: 55, + /***/ + VK_8: 56, + /***/ + VK_9: 57, + /** Colon (:) key. Requires Gecko 15.0 */ + VK_COLON: 58, + /** Semicolon (;) key. */ + VK_SEMICOLON: 59, + /** Less-than (<) key. Requires Gecko 15.0 */ + VK_LESS_THAN: 60, + /** Equals (=) key. */ + VK_EQUALS: 61, + /** Greater-than (>) key. Requires Gecko 15.0 */ + VK_GREATER_THAN: 62, + /** Question mark (?) key. Requires Gecko 15.0 */ + VK_QUESTION_MARK: 63, + /** Atmark (@) key. Requires Gecko 15.0 */ + VK_AT: 64, + /***/ + VK_A: 65, + /***/ + VK_B: 66, + /***/ + VK_C: 67, + /***/ + VK_D: 68, + /***/ + VK_E: 69, + /***/ + VK_F: 70, + /***/ + VK_G: 71, + /***/ + VK_H: 72, + /***/ + VK_I: 73, + /***/ + VK_J: 74, + /***/ + VK_K: 75, + /***/ + VK_L: 76, + /***/ + VK_M: 77, + /***/ + VK_N: 78, + /***/ + VK_O: 79, + /***/ + VK_P: 80, + /***/ + VK_Q: 81, + /***/ + VK_R: 82, + /***/ + VK_S: 83, + /***/ + VK_T: 84, + /***/ + VK_U: 85, + /***/ + VK_V: 86, + /***/ + VK_W: 87, + /***/ + VK_X: 88, + /***/ + VK_Y: 89, + /***/ + VK_Z: 90, + /***/ + VK_CONTEXT_MENU: 93, + /** 0 on the numeric keypad. */ + VK_NUMPAD0: 96, + /** 1 on the numeric keypad. */ + VK_NUMPAD1: 97, + /** 2 on the numeric keypad. */ + VK_NUMPAD2: 98, + /** 3 on the numeric keypad. */ + VK_NUMPAD3: 99, + /** 4 on the numeric keypad. */ + VK_NUMPAD4: 100, + /** 5 on the numeric keypad. */ + VK_NUMPAD5: 101, + /** 6 on the numeric keypad. */ + VK_NUMPAD6: 102, + /** 7 on the numeric keypad. */ + VK_NUMPAD7: 103, + /** 8 on the numeric keypad. */ + VK_NUMPAD8: 104, + /** 9 on the numeric keypad. */ + VK_NUMPAD9: 105, + /** * on the numeric keypad. */ + VK_MULTIPLY: 106, + /** + on the numeric keypad. */ + VK_ADD: 107, + /***/ + VK_SEPARATOR: 108, + /** - on the numeric keypad. */ + VK_SUBTRACT: 109, + /** Decimal point on the numeric keypad. */ + VK_DECIMAL: 110, + /** / on the numeric keypad. */ + VK_DIVIDE: 111, + /** F1 key. */ + VK_F1: 112, + /** F2 key. */ + VK_F2: 113, + /** F3 key. */ + VK_F3: 114, + /** F4 key. */ + VK_F4: 115, + /** F5 key. */ + VK_F5: 116, + /** F6 key. */ + VK_F6: 117, + /** F7 key. */ + VK_F7: 118, + /** F8 key. */ + VK_F8: 119, + /** F9 key. */ + VK_F9: 120, + /** F10 key. */ + VK_F10: 121, + /** F11 key. */ + VK_F11: 122, + /** F12 key. */ + VK_F12: 123, + /** F13 key. */ + VK_F13: 124, + /** F14 key. */ + VK_F14: 125, + /** F15 key. */ + VK_F15: 126, + /** F16 key. */ + VK_F16: 127, + /** F17 key. */ + VK_F17: 128, + /** F18 key. */ + VK_F18: 129, + /** F19 key. */ + VK_F19: 130, + /** F20 key. */ + VK_F20: 131, + /** F21 key. */ + VK_F21: 132, + /** F22 key. */ + VK_F22: 133, + /** F23 key. */ + VK_F23: 134, + /** F24 key. */ + VK_F24: 135, + /** Num Lock key. */ + VK_NUM_LOCK: 144, + /** Scroll Lock key. */ + VK_SCROLL_LOCK: 145, + /** Circumflex (^) key. Requires Gecko 15.0 */ + VK_CIRCUMFLEX: 160, + /** Exclamation (!) key. Requires Gecko 15.0 */ + VK_EXCLAMATION: 161, + /** Double quote () key. Requires Gecko 15.0 */ + VK_DOUBLE_QUOTE: 162, + /** Hash (#) key. Requires Gecko 15.0 */ + VK_HASH: 163, + /** Dollar sign ($) key. Requires Gecko 15.0 */ + VK_DOLLAR: 164, + /** Percent (%) key. Requires Gecko 15.0 */ + VK_PERCENT: 165, + /** Ampersand (&) key. Requires Gecko 15.0 */ + VK_AMPERSAND: 166, + /** Underscore (_) key. Requires Gecko 15.0 */ + VK_UNDERSCORE: 167, + /** Open parenthesis (() key. Requires Gecko 15.0 */ + VK_OPEN_PAREN: 168, + /** Close parenthesis ()) key. Requires Gecko 15.0 */ + VK_CLOSE_PAREN: 169, + /* Asterisk (*) key. Requires Gecko 15.0 */ + VK_ASTERISK: 170, + /** Plus (+) key. Requires Gecko 15.0 */ + VK_PLUS: 171, + /** Pipe (|) key. Requires Gecko 15.0 */ + VK_PIPE: 172, + /** Hyphen-US/docs/Minus (-) key. Requires Gecko 15.0 */ + VK_HYPHEN_MINUS: 173, + /** Open curly bracket ({) key. Requires Gecko 15.0 */ + VK_OPEN_CURLY_BRACKET: 174, + /** Close curly bracket (}) key. Requires Gecko 15.0 */ + VK_CLOSE_CURLY_BRACKET: 175, + /** Tilde (~) key. Requires Gecko 15.0 */ + VK_TILDE: 176, + /** Comma (,) key. */ + VK_COMMA: 188, + /** Period (.) key. */ + VK_PERIOD: 190, + /** Slash (/) key. */ + VK_SLASH: 191, + /** Back tick (`) key. */ + VK_BACK_QUOTE: 192, + /** Open square bracket ([) key. */ + VK_OPEN_BRACKET: 219, + /** Back slash (\) key. */ + VK_BACK_SLASH: 220, + /** Close square bracket (]) key. */ + VK_CLOSE_BRACKET: 221, + /** Quote (''') key. */ + VK_QUOTE: 222, + /** Meta key on Linux, Command key on Mac. */ + VK_META: 224, + /** AltGr key on Linux. Requires Gecko 15.0 */ + VK_ALTGR: 225, + /** Windows logo key on Windows. Or Super or Hyper key on Linux. Requires Gecko 15.0 */ + VK_WIN: 91, + /** Linux support for this keycode was added in Gecko 4.0. */ + VK_KANA: 21, + /** Linux support for this keycode was added in Gecko 4.0. */ + VK_HANGUL: 21, + /** 英数 key on Japanese Mac keyboard. Requires Gecko 15.0 */ + VK_EISU: 22, + /** Linux support for this keycode was added in Gecko 4.0. */ + VK_JUNJA: 23, + /** Linux support for this keycode was added in Gecko 4.0. */ + VK_FINAL: 24, + /** Linux support for this keycode was added in Gecko 4.0. */ + VK_HANJA: 25, + /** Linux support for this keycode was added in Gecko 4.0. */ + VK_KANJI: 25, + /** Linux support for this keycode was added in Gecko 4.0. */ + VK_CONVERT: 28, + /** Linux support for this keycode was added in Gecko 4.0. */ + VK_NONCONVERT: 29, + /** Linux support for this keycode was added in Gecko 4.0. */ + VK_ACCEPT: 30, + /** Linux support for this keycode was added in Gecko 4.0. */ + VK_MODECHANGE: 31, + /** Linux support for this keycode was added in Gecko 4.0. */ + VK_SELECT: 41, + /** Linux support for this keycode was added in Gecko 4.0. */ + VK_PRINT: 42, + /** Linux support for this keycode was added in Gecko 4.0. */ + VK_EXECUTE: 43, + /** Linux support for this keycode was added in Gecko 4.0. */ + VK_SLEEP: 95 +}; +/** + * @namespace + * Contains text tokenization and breaking routines + */ +ROT.Text = { + RE_COLORS: /%([bc]){([^}]*)}/g, + + /* token types */ + TYPE_TEXT: 0, + TYPE_NEWLINE: 1, + TYPE_FG: 2, + TYPE_BG: 3, + + /** + * Measure size of a resulting text block + */ + measure: function(str, maxWidth) { + var result = {width:0, height:1}; + var tokens = this.tokenize(str, maxWidth); + var lineWidth = 0; + + for (var i=0;i maxWidth) { /* line too long, find a suitable breaking spot */ + + /* is it possible to break within this token? */ + var index = -1; + while (1) { + var nextIndex = token.value.indexOf(" ", index+1); + if (nextIndex == -1) { break; } + if (lineLength + nextIndex > maxWidth) { break; } + index = nextIndex; + } + + if (index != -1) { /* break at space within this one */ + token.value = this._breakInsideToken(tokens, i, index, true); + } else if (lastTokenWithSpace != -1) { /* is there a previous token where a break can occur? */ + var token = tokens[lastTokenWithSpace]; + var breakIndex = token.value.lastIndexOf(" "); + token.value = this._breakInsideToken(tokens, lastTokenWithSpace, breakIndex, true); + i = lastTokenWithSpace; + } else { /* force break in this token */ + token.value = this._breakInsideToken(tokens, i, maxWidth-lineLength, false); + } + + } else { /* line not long, continue */ + lineLength += token.value.length; + if (token.value.indexOf(" ") != -1) { lastTokenWithSpace = i; } + } + + i++; /* advance to next token */ + } + + + tokens.push({type: ROT.Text.TYPE_NEWLINE}); /* insert fake newline to fix the last text line */ + + /* remove trailing space from text tokens before newlines */ + var lastTextToken = null; + for (var i=0;i= this._context.canvas.width || y >= this._context.canvas.height) { return [-1, -1]; } + + return this._backend.eventToPosition(x, y); +} + +/** + * @param {int} x + * @param {int} y + * @param {string} ch + * @param {string} [fg] foreground color + * @param {string} [bg] background color + */ +ROT.Display.prototype.draw = function(x, y, ch, fg, bg) { + if (!fg) { fg = this._options.fg; } + if (!bg) { bg = this._options.bg; } + this._data[x+","+y] = [x, y, ch, fg, bg]; + + if (this._dirty === true) { return; } /* will already redraw everything */ + if (!this._dirty) { this._dirty = {}; } /* first! */ + this._dirty[x+","+y] = true; +} + +/** + * Draws a text at given position. Optionally wraps at a maximum length. Currently does not work with hex layout. + * @param {int} x + * @param {int} y + * @param {string} text May contain color/background format specifiers, %c{name}/%b{name}, both optional. %c{}/%b{} resets to default. + * @param {int} [maxWidth] wrap at what width? + * @returns {int} lines drawn + */ +ROT.Display.prototype.drawText = function(x, y, text, maxWidth) { + var fg = null; + var bg = null; + var cx = x; + var cy = y; + var lines = 1; + if (!maxWidth) { maxWidth = this._options.width-x; } + + var tokens = ROT.Text.tokenize(text, maxWidth); + + while (tokens.length) { /* interpret tokenized opcode stream */ + var token = tokens.shift(); + switch (token.type) { + case ROT.Text.TYPE_TEXT: + for (var i=0;i 1) { /* too wide with current aspect ratio */ + boxHeight = Math.floor(boxHeight / widthFraction); + } + return Math.floor(boxHeight / this._options.spacing); +} + +ROT.Display.Rect.prototype.eventToPosition = function(x, y) { + return [Math.floor(x/this._spacingX), Math.floor(y/this._spacingY)]; +} +/** + * @class Hexagonal backend + * @private + */ +ROT.Display.Hex = function(context) { + ROT.Display.Backend.call(this, context); + + this._spacingX = 0; + this._spacingY = 0; + this._hexSize = 0; + this._options = {}; +} +ROT.Display.Hex.extend(ROT.Display.Backend); + +ROT.Display.Hex.prototype.compute = function(options) { + this._options = options; + + var charWidth = Math.ceil(this._context.measureText("W").width); + this._hexSize = Math.floor(options.spacing * (options.fontSize + charWidth/Math.sqrt(3)) / 2); + this._spacingX = this._hexSize * Math.sqrt(3) / 2; + this._spacingY = this._hexSize * 1.5; + this._context.canvas.width = Math.ceil( (options.width + 1) * this._spacingX ); + this._context.canvas.height = Math.ceil( (options.height - 1) * this._spacingY + 2*this._hexSize ); +} + +ROT.Display.Hex.prototype.draw = function(data, clearBefore) { + var x = data[0]; + var y = data[1]; + var ch = data[2]; + var fg = data[3]; + var bg = data[4]; + + var cx = (x+1) * this._spacingX; + var cy = y * this._spacingY + this._hexSize; + + if (clearBefore) { + this._context.fillStyle = bg; + this._fill(cx, cy); + } + + if (!ch) { return; } + + this._context.fillStyle = fg; + this._context.fillText(ch, cx, cy); +} + + +ROT.Display.Hex.prototype.computeSize = function(availWidth, availHeight) { + var width = Math.floor(availWidth / this._spacingX) - 1; + var height = Math.floor((availHeight - 2*this._hexSize) / this._spacingY + 1); + return [width, height]; +} + +ROT.Display.Hex.prototype.computeFontSize = function(availWidth, availHeight) { + var hexSizeWidth = 2*availWidth / ((this._options.width+1) * Math.sqrt(3)) - 1; + var hexSizeHeight = availHeight / (2 + 1.5*(this._options.height-1)); + var hexSize = Math.min(hexSizeWidth, hexSizeHeight); + + /* compute char ratio */ + var oldFont = this._context.font; + this._context.font = "100px " + this._options.fontFamily; + var width = Math.ceil(this._context.measureText("W").width); + this._context.font = oldFont; + var ratio = width / 100; + + hexSize = Math.floor(hexSize)+1; /* closest larger hexSize */ + + var fontSize = 2*hexSize / (this._options.spacing * (1 + ratio / Math.sqrt(3))); + + /* closest smaller fontSize */ + return Math.ceil(fontSize)-1; +} + +ROT.Display.Hex.prototype.eventToPosition = function(x, y) { + var height = this._context.canvas.height / this._options.height; + y = Math.floor(y/height); + + if (y.mod(2)) { /* odd row */ + x -= this._spacingX; + x = 1 + 2*Math.floor(x/(2*this._spacingX)); + } else { + x = 2*Math.floor(x/(2*this._spacingX)); + } + + return [x, y]; +} + +ROT.Display.Hex.prototype._fill = function(cx, cy) { + var a = this._hexSize; + var b = this._options.border; + + this._context.beginPath(); + this._context.moveTo(cx, cy-a+b); + this._context.lineTo(cx + this._spacingX - b, cy-a/2+b); + this._context.lineTo(cx + this._spacingX - b, cy+a/2-b); + this._context.lineTo(cx, cy+a-b); + this._context.lineTo(cx - this._spacingX + b, cy+a/2-b); + this._context.lineTo(cx - this._spacingX + b, cy-a/2+b); + this._context.lineTo(cx, cy-a+b); + this._context.fill(); +} +/** + * @namespace + * This code is an implementation of Alea algorithm; (C) 2010 Johannes Baagøe. + * Alea is licensed according to the http://en.wikipedia.org/wiki/MIT_License. + */ +ROT.RNG = { + /** + * @returns {number} + */ + getSeed: function() { + return this._seed; + }, + + /** + * @param {number} seed Seed the number generator + */ + setSeed: function(seed) { + seed = (seed < 1 ? 1/seed : seed); + + this._seed = seed; + this._s0 = (seed >>> 0) * this._frac; + + seed = (seed*69069 + 1) >>> 0; + this._s1 = seed * this._frac; + + seed = (seed*69069 + 1) >>> 0; + this._s2 = seed * this._frac; + + this._c = 1; + return this; + }, + + /** + * @returns {float} Pseudorandom value [0,1), uniformly distributed + */ + getUniform: function() { + var t = 2091639 * this._s0 + this._c * this._frac; + this._s0 = this._s1; + this._s1 = this._s2; + this._c = t | 0; + this._s2 = t - this._c; + return this._s2; + }, + + /** + * @param {float} [mean=0] Mean value + * @param {float} [stddev=1] Standard deviation. ~95% of the absolute values will be lower than 2*stddev. + * @returns {float} A normally distributed pseudorandom value + */ + getNormal: function(mean, stddev) { + do { + var u = 2*this.getUniform()-1; + var v = 2*this.getUniform()-1; + var r = u*u + v*v; + } while (r > 1 || r == 0); + + var gauss = u * Math.sqrt(-2*Math.log(r)/r); + return (mean || 0) + gauss*(stddev || 1); + }, + + /** + * @returns {int} Pseudorandom value [1,100] inclusive, uniformly distributed + */ + getPercentage: function() { + return 1 + Math.floor(this.getUniform()*100); + }, + + /** + * @param {object} data key=whatever, value=weight (relative probability) + * @returns {string} whatever + */ + getWeightedValue: function(data) { + var avail = []; + var total = 0; + + for (var id in data) { + total += data[id]; + } + var random = Math.floor(this.getUniform()*total); + + var part = 0; + for (var id in data) { + part += data[id]; + if (random < part) { return id; } + } + + return null; + }, + + /** + * Get RNG state. Useful for storing the state and re-setting it via setState. + * @returns {?} Internal state + */ + getState: function() { + return [this._s0, this._s1, this._s2, this._c]; + }, + + /** + * Set a previously retrieved state. + * @param {?} state + */ + setState: function(state) { + this._s0 = state[0]; + this._s1 = state[1]; + this._s2 = state[2]; + this._c = state[3]; + return this; + }, + + _s0: 0, + _s1: 0, + _s2: 0, + _c: 0, + _frac: 2.3283064365386963e-10 /* 2^-32 */ +} + +ROT.RNG.setSeed(Date.now()); +/** + * @class (Markov process)-based string generator. + * Copied from a RogueBasin article. + * Offers configurable order and prior. + * @param {object} [options] + * @param {bool} [options.words=false] Use word mode? + * @param {int} [options.order=3] + * @param {float} [options.prior=0.001] + */ +ROT.StringGenerator = function(options) { + this._options = { + words: false, + order: 3, + prior: 0.001 + } + for (var p in options) { this._options[p] = options[p]; } + + this._boundary = String.fromCharCode(0); + this._suffix = this._boundary; + this._prefix = []; + for (var i=0;i this._options.order) { + context = context.slice(-this._options.order); + } else if (context.length < this._options.order) { + context = this._prefix.slice(0, this._options.order - context.length).concat(context); + } + + while (!(this._join(context) in this._data) && context.length > 0) { context = context.slice(1); } + + return context; +} + + +ROT.StringGenerator.prototype._pickRandom = function(data) { + var total = 0; + + for (var id in data) { + total += data[id]; + } + var random = ROT.RNG.getUniform()*total; + + var part = 0; + for (var id in data) { + part += data[id]; + if (random < part) { return id; } + } +} +/** + * @class Generic event queue: stores events and retrieves them based on their time + */ +ROT.EventQueue = function() { + this._time = 0; + this._events = []; + this._eventTimes = []; +} + +/** + * @returns {number} Elapsed time + */ +ROT.EventQueue.prototype.getTime = function() { + return this._time; +} + +/** + * Clear all scheduled events + */ +ROT.EventQueue.prototype.clear = function() { + this._events = []; + this._eventTimes = []; + return this; +} + +/** + * @param {?} event + * @param {number} time + */ +ROT.EventQueue.prototype.add = function(event, time) { + var index = this._events.length; + for (var i=0;i time) { + index = i; + break; + } + } + + this._events.splice(index, 0, event); + this._eventTimes.splice(index, 0, time); +} + +/** + * Locates the nearest event, advances time if necessary. Returns that event and removes it from the queue. + * @returns {? || null} The event previously added by addEvent, null if no event available + */ +ROT.EventQueue.prototype.get = function() { + if (!this._events.length) { return null; } + + var time = this._eventTimes.splice(0, 1)[0]; + if (time > 0) { /* advance */ + this._time += time; + for (var i=0;i= width || y >= height) { return false; } + return map[x][y]; +} +/** + * @class Maze generator - Eller's algorithm + * See http://homepages.cwi.nl/~tromp/maze.html for explanation + * @augments ROT.Map + */ +ROT.Map.EllerMaze = function(width, height) { + ROT.Map.call(this, width, height); +} +ROT.Map.EllerMaze.extend(ROT.Map); + +ROT.Map.EllerMaze.prototype.create = function(callback) { + var map = this._fillMap(1); + var w = Math.ceil((this._width-2)/2); + + var rand = 9/24; + + var L = []; + var R = []; + + for (var i=0;i rand) { + this._addToList(i, L, R); + map[x+1][y] = 0; + } + + /* bottom connection */ + if (i != L[i] && ROT.RNG.getUniform() > rand) { + /* remove connection */ + this._removeFromList(i, L, R); + } else { + /* create connection */ + map[x][y+1] = 0; + } + } + } + + /* last row */ + for (var i=0;i rand)) { + /* dig right also if the cell is separated, so it gets connected to the rest of maze */ + this._addToList(i, L, R); + map[x+1][y] = 0; + } + + this._removeFromList(i, L, R); + } + + for (var i=0;i= this._width || x < 0 || y >= this._width) { continue; } + result += (this._map[x][y] == 1 ? 1 : 0); + } + + return result; +} +/** + * @class Dungeon map: has rooms and corridors + * @augments ROT.Map + */ +ROT.Map.Dungeon = function(width, height) { + ROT.Map.call(this, width, height); + this._rooms = []; /* list of all rooms */ + this._corridors = []; +} +ROT.Map.Dungeon.extend(ROT.Map); + +/** + * Get all generated rooms + * @returns {ROT.Map.Feature.Room[]} + */ +ROT.Map.Dungeon.prototype.getRooms = function() { + return this._rooms; +} + +/** + * Get all generated corridors + * @returns {ROT.Map.Feature.Corridor[]} + */ +ROT.Map.Dungeon.prototype.getCorridors = function() { + return this._corridors; +} +/** + * @class Random dungeon generator using human-like digging patterns. + * Heavily based on Mike Anderson's ideas from the "Tyrant" algo, mentioned at + * http://www.roguebasin.roguelikedevelopment.org/index.php?title=Dungeon-Building_Algorithm. + * @augments ROT.Map.Dungeon + */ +ROT.Map.Digger = function(width, height, options) { + ROT.Map.Dungeon.call(this, width, height); + + this._options = { + roomWidth: [3, 9], /* room minimum and maximum width */ + roomHeight: [3, 5], /* room minimum and maximum height */ + corridorLength: [3, 10], /* corridor minimum and maximum length */ + dugPercentage: 0.2, /* we stop after this percentage of level area has been dug out */ + timeLimit: 1000 /* we stop after this much time has passed (msec) */ + } + for (var p in options) { this._options[p] = options[p]; } + + this._features = { + "Room": 4, + "Corridor": 4 + } + this._featureAttempts = 20; /* how many times do we try to create a feature on a suitable wall */ + this._walls = {}; /* these are available for digging */ + + this._digCallback = this._digCallback.bind(this); + this._canBeDugCallback = this._canBeDugCallback.bind(this); + this._isWallCallback = this._isWallCallback.bind(this); + this._priorityWallCallback = this._priorityWallCallback.bind(this); +} +ROT.Map.Digger.extend(ROT.Map.Dungeon); + +/** + * Create a map + * @see ROT.Map#create + */ +ROT.Map.Digger.prototype.create = function(callback) { + this._rooms = []; + this._corridors = []; + this._map = this._fillMap(1); + this._walls = {}; + this._dug = 0; + var area = (this._width-2) * (this._height-2); + + this._firstRoom(); + + var t1 = Date.now(); + + do { + var t2 = Date.now(); + if (t2 - t1 > this._options.timeLimit) { break; } + + /* find a good wall */ + var wall = this._findWall(); + if (!wall) { break; } /* no more walls */ + + var parts = wall.split(","); + var x = parseInt(parts[0]); + var y = parseInt(parts[1]); + var dir = this._getDiggingDirection(x, y); + if (!dir) { continue; } /* this wall is not suitable */ + +// console.log("wall", x, y); + + /* try adding a feature */ + var featureAttempts = 0; + do { + featureAttempts++; + if (this._tryFeature(x, y, dir[0], dir[1])) { /* feature added */ + //if (this._rooms.length + this._corridors.length == 2) { this._rooms[0].addDoor(x, y); } /* first room oficially has doors */ + this._removeSurroundingWalls(x, y); + this._removeSurroundingWalls(x-dir[0], y-dir[1]); + break; + } + } while (featureAttempts < this._featureAttempts); + + var priorityWalls = 0; + for (var id in this._walls) { + if (this._walls[id] > 1) { priorityWalls++; } + } + + } while (this._dug/area < this._options.dugPercentage || priorityWalls); /* fixme number of priority walls */ + + this._addDoors(); + + if (callback) { + for (var i=0;i= this._width || y >= this._height) { return false; } + return (this._map[x][y] == 1); +} + +ROT.Map.Digger.prototype._canBeDugCallback = function(x, y) { + if (x < 1 || y < 1 || x+1 >= this._width || y+1 >= this._height) { return false; } + return (this._map[x][y] == 1); +} + +ROT.Map.Digger.prototype._priorityWallCallback = function(x, y) { + this._walls[x+","+y] = 2; +} + +ROT.Map.Digger.prototype._firstRoom = function() { + var cx = Math.floor(this._width/2); + var cy = Math.floor(this._height/2); + var room = ROT.Map.Feature.Room.createRandomCenter(cx, cy, this._options); + this._rooms.push(room); + room.create(this._digCallback); +} + +/** + * Get a suitable wall + */ +ROT.Map.Digger.prototype._findWall = function() { + var prio1 = []; + var prio2 = []; + for (var id in this._walls) { + var prio = this._walls[id]; + if (prio == 2) { + prio2.push(id); + } else { + prio1.push(id); + } + } + + var arr = (prio2.length ? prio2 : prio1); + if (!arr.length) { return null; } /* no walls :/ */ + + var id = arr.random(); + delete this._walls[id]; + + return id; +} + +/** + * Tries adding a feature + * @returns {bool} was this a successful try? + */ +ROT.Map.Digger.prototype._tryFeature = function(x, y, dx, dy) { + var feature = ROT.RNG.getWeightedValue(this._features); + feature = ROT.Map.Feature[feature].createRandomAt(x, y, dx, dy, this._options); + + if (!feature.isValid(this._isWallCallback, this._canBeDugCallback)) { +// console.log("not valid"); +// feature.debug(); + return false; + } + + feature.create(this._digCallback); +// feature.debug(); + + if (feature instanceof ROT.Map.Feature.Room) { this._rooms.push(feature); } + if (feature instanceof ROT.Map.Feature.Corridor) { + feature.createPriorityWalls(this._priorityWallCallback); + this._corridors.push(feature); + } + + return true; +} + +ROT.Map.Digger.prototype._removeSurroundingWalls = function(cx, cy) { + var deltas = ROT.DIRS[4]; + + for (var i=0;i= this._width || y >= this._width) { return null; } + + if (!this._map[x][y]) { /* there already is another empty neighbor! */ + if (result) { return null; } + result = delta; + } + } + + /* no empty neighbor */ + if (!result) { return null; } + + return [-result[0], -result[1]]; +} + +/** + * Find empty spaces surrounding rooms, and apply doors. + */ +ROT.Map.Digger.prototype._addDoors = function() { + var data = this._map; + var isWallCallback = function(x, y) { + return (data[x][y] == 1); + } + for (var i = 0; i < this._rooms.length; i++ ) { + var room = this._rooms[i]; + room.clearDoors(); + room.addDoors(isWallCallback); + } +} +/** + * @class Dungeon generator which tries to fill the space evenly. Generates independent rooms and tries to connect them. + * @augments ROT.Map.Dungeon + */ +ROT.Map.Uniform = function(width, height, options) { + ROT.Map.Dungeon.call(this, width, height); + + this._options = { + roomWidth: [3, 9], /* room minimum and maximum width */ + roomHeight: [3, 5], /* room minimum and maximum height */ + roomDugPercentage: 0.1, /* we stop after this percentage of level area has been dug out by rooms */ + timeLimit: 1000 /* we stop after this much time has passed (msec) */ + } + for (var p in options) { this._options[p] = options[p]; } + + this._roomAttempts = 20; /* new room is created N-times until is considered as impossible to generate */ + this._corridorAttempts = 20; /* corridors are tried N-times until the level is considered as impossible to connect */ + + this._connected = []; /* list of already connected rooms */ + this._unconnected = []; /* list of remaining unconnected rooms */ + + this._digCallback = this._digCallback.bind(this); + this._canBeDugCallback = this._canBeDugCallback.bind(this); + this._isWallCallback = this._isWallCallback.bind(this); +} +ROT.Map.Uniform.extend(ROT.Map.Dungeon); + +/** + * Create a map. If the time limit has been hit, returns null. + * @see ROT.Map#create + */ +ROT.Map.Uniform.prototype.create = function(callback) { + var t1 = Date.now(); + while (1) { + var t2 = Date.now(); + if (t2 - t1 > this._options.timeLimit) { return null; } /* time limit! */ + + this._map = this._fillMap(1); + this._dug = 0; + this._rooms = []; + this._unconnected = []; + this._generateRooms(); + if (this._generateCorridors()) { break; } + } + + if (callback) { + for (var i=0;i this._options.roomDugPercentage) { break; } /* achieved requested amount of free space */ + } while (room); + + /* either enough rooms, or not able to generate more of them :) */ +} + +/** + * Try to generate one room + */ +ROT.Map.Uniform.prototype._generateRoom = function() { + var count = 0; + while (count < this._roomAttempts) { + count++; + + var room = ROT.Map.Feature.Room.createRandom(this._width, this._height, this._options); + if (!room.isValid(this._isWallCallback, this._canBeDugCallback)) { continue; } + + room.create(this._digCallback); + this._rooms.push(room); + return room; + } + + /* no room was generated in a given number of attempts */ + return null; +} + +/** + * Generates connectors beween rooms + * @returns {bool} success Was this attempt successfull? + */ +ROT.Map.Uniform.prototype._generateCorridors = function() { + var cnt = 0; + while (cnt < this._corridorAttempts) { + cnt++; + this._corridors = []; + + /* dig rooms into a clear map */ + this._map = this._fillMap(1); + for (var i=0;i 0 ? 2 : 0); + var dirIndex2 = (dirIndex1 + 2) % 4; + var min = room2.getLeft(); + var max = room2.getRight(); + var index = 0; + } else { /* first try connecting east-west walls */ + var dirIndex1 = (diffX > 0 ? 1 : 3); + var dirIndex2 = (dirIndex1 + 2) % 4; + var min = room2.getTop(); + var max = room2.getBottom(); + var index = 1; + } + + var start = this._placeInWall(room1, dirIndex1); /* corridor will start here */ + if (!start) { return false; } + + if (start[index] >= min && start[index] <= max) { /* possible to connect with straight line (I-like) */ + var end = start.slice(); + var value = null; + switch (dirIndex2) { + case 0: value = room2.getTop()-1; break; + case 1: value = room2.getRight()+1; break; + case 2: value = room2.getBottom()+1; break; + case 3: value = room2.getLeft()-1; break; + } + end[(index+1)%2] = value; + this._digLine([start, end]); + + } else if (start[index] < min-1 || start[index] > max+1) { /* need to switch target wall (L-like) */ + + var diff = start[index] - center2[index]; + switch (dirIndex2) { + case 0: + case 1: var rotation = (diff < 0 ? 3 : 1); break; + case 2: + case 3: var rotation = (diff < 0 ? 1 : 3); break; + } + dirIndex2 = (dirIndex2 + rotation) % 4; + + var end = this._placeInWall(room2, dirIndex2); + if (!end) { return false; } + + var mid = [0, 0]; + mid[index] = start[index]; + var index2 = (index+1)%2; + mid[index2] = end[index2]; + this._digLine([start, mid, end]); + + } else { /* use current wall pair, but adjust the line in the middle (S-like) */ + + var index2 = (index+1)%2; + var end = this._placeInWall(room2, dirIndex2); + if (!end) { return; } + var mid = Math.round((end[index2] + start[index2])/2); + + var mid1 = [0, 0]; + var mid2 = [0, 0]; + mid1[index] = start[index]; + mid1[index2] = mid; + mid2[index] = end[index]; + mid2[index2] = mid; + this._digLine([start, mid1, mid2, end]); + } + + room1.addDoor(start[0], start[1]); + room2.addDoor(end[0], end[1]); + + var index = this._unconnected.indexOf(room1); + if (index != -1) { + this._unconnected.splice(index, 1); + this._connected.push(room1); + } + + var index = this._unconnected.indexOf(room2); + if (index != -1) { + this._unconnected.splice(index, 1); + this._connected.push(room2); + } + + return true; +} + +ROT.Map.Uniform.prototype._placeInWall = function(room, dirIndex) { + var start = [0, 0]; + var dir = [0, 0]; + var length = 0; + + switch (dirIndex) { + case 0: + dir = [1, 0]; + start = [room.getLeft(), room.getTop()-1]; + length = room.getRight()-room.getLeft()+1; + break; + case 1: + dir = [0, 1]; + start = [room.getRight()+1, room.getTop()]; + length = room.getBottom()-room.getTop()+1; + break; + case 2: + dir = [1, 0]; + start = [room.getLeft(), room.getBottom()+1]; + length = room.getRight()-room.getLeft()+1; + break; + case 3: + dir = [0, 1]; + start = [room.getLeft()-1, room.getTop()]; + length = room.getBottom()-room.getTop()+1; + break; + } + + var avail = []; + var lastBadIndex = -2; + + for (var i=0;i=0; i--) { + if (!avail[i]) { avail.splice(i, 1); } + } + return (avail.length ? avail.random() : null); +} + +/** + * Dig a polyline. + */ +ROT.Map.Uniform.prototype._digLine = function(points) { + for (var i=1;i= this._width || y >= this._height) { return false; } + return (this._map[x][y] == 1); +} + +ROT.Map.Uniform.prototype._canBeDugCallback = function(x, y) { + if (x < 1 || y < 1 || x+1 >= this._width || y+1 >= this._height) { return false; } + return (this._map[x][y] == 1); +} + +/** + * @author hyakugei + * @class Dungeon generator which uses the "orginal" Rogue dungeon generation algorithm. See http://kuoi.com/~kamikaze/GameDesign/art07_rogue_dungeon.php + * @augments ROT.Map + * @param {int} [width=ROT.DEFAULT_WIDTH] + * @param {int} [height=ROT.DEFAULT_HEIGHT] + * @param {object} [options] Options + * @param {int[]} [options.cellWidth=3] Number of cells to create on the horizontal (number of rooms horizontally) + * @param {int[]} [options.cellHeight=3] Number of cells to create on the vertical (number of rooms vertically) + * @param {int} [options.roomWidth] Room min and max width - normally set auto-magically via the constructor. + * @param {int} [options.roomHeight] Room min and max height - normally set auto-magically via the constructor. + */ +ROT.Map.Rogue = function(width, height, options) { + ROT.Map.call(this, width, height); + + this._options = { + cellWidth: 3, // NOTE to self, these could probably work the same as the roomWidth/room Height values + cellHeight: 3 // ie. as an array with min-max values for each direction.... + } + + for (var p in options) { this._options[p] = options[p]; } + + /* + Set the room sizes according to the over-all width of the map, + and the cell sizes. + */ + + if (!this._options.hasOwnProperty("roomWidth")) { + this._options["roomWidth"] = this._calculateRoomSize(width, this._options["cellWidth"]); + } + if (!this._options.hasOwnProperty["roomHeight"]) { + this._options["roomHeight"] = this._calculateRoomSize(height, this._options["cellHeight"]); + } + +} + +ROT.Map.Rogue.extend(ROT.Map); + +/** + * @see ROT.Map#create + */ +ROT.Map.Rogue.prototype.create = function(callback) { + this.map = this._fillMap(1); + this.rooms = []; + this.connectedCells = []; + + this._initRooms(); + this._connectRooms(); + this._connectUnconnectedRooms(); + this._createRandomRoomConnections(); + this._createRooms(); + this._createCorridors(); + + if (callback) { + for (var i = 0; i < this._width; i++) { + for (var j = 0; j < this._height; j++) { + callback(i, j, this.map[i][j]); + } + } + } + + return this; +} + +ROT.Map.Rogue.prototype._getRandomInt = function(min, max) { + return Math.floor(ROT.RNG.getUniform() * (max - min + 1)) + min; +} + +ROT.Map.Rogue.prototype._calculateRoomSize = function(size, cell) { + var max = Math.floor((size/cell) * 0.8); + var min = Math.floor((size/cell) * 0.25); + if (min < 2) min = 2; + if (max < 2) max = 2; + return [min, max]; +} + +ROT.Map.Rogue.prototype._initRooms = function () { + // create rooms array. This is the "grid" list from the algo. + for (var i = 0; i < this._options.cellWidth; i++) { + this.rooms.push([]); + for(var j = 0; j < this._options.cellHeight; j++) { + this.rooms[i].push({"x":0, "y":0, "width":0, "height":0, "connections":[], "cellx":i, "celly":j}); + } + } +} + +ROT.Map.Rogue.prototype._connectRooms = function() { + //pick random starting grid + var cgx = this._getRandomInt(0, this._options.cellWidth-1); + var cgy = this._getRandomInt(0, this._options.cellHeight-1); + + var idx; + var ncgx; + var ncgy; + + var found = false; + var room; + var otherRoom; + + // find unconnected neighbour cells + do { + + //var dirToCheck = [0,1,2,3,4,5,6,7]; + var dirToCheck = [0,2,4,6]; + dirToCheck = dirToCheck.randomize(); + + do { + found = false; + idx = dirToCheck.pop(); + + + ncgx = cgx + ROT.DIRS[8][idx][0]; + ncgy = cgy + ROT.DIRS[8][idx][1]; + + if(ncgx < 0 || ncgx >= this._options.cellWidth) continue; + if(ncgy < 0 || ncgy >= this._options.cellHeight) continue; + + room = this.rooms[cgx][cgy]; + + if(room["connections"].length > 0) + { + // as long as this room doesn't already coonect to me, we are ok with it. + if(room["connections"][0][0] == ncgx && + room["connections"][0][1] == ncgy) + { + break; + } + } + + otherRoom = this.rooms[ncgx][ncgy]; + + if (otherRoom["connections"].length == 0) { + otherRoom["connections"].push([cgx,cgy]); + + this.connectedCells.push([ncgx, ncgy]); + cgx = ncgx; + cgy = ncgy; + found = true; + } + + } while (dirToCheck.length > 0 && found == false) + + } while (dirToCheck.length > 0) + +} + +ROT.Map.Rogue.prototype._connectUnconnectedRooms = function() { + //While there are unconnected rooms, try to connect them to a random connected neighbor + //(if a room has no connected neighbors yet, just keep cycling, you'll fill out to it eventually). + var cw = this._options.cellWidth; + var ch = this._options.cellHeight; + + var randomConnectedCell; + this.connectedCells = this.connectedCells.randomize(); + var room; + var otherRoom; + var validRoom; + + for (var i = 0; i < this._options.cellWidth; i++) { + for (var j = 0; j < this._options.cellHeight; j++) { + + room = this.rooms[i][j]; + + if (room["connections"].length == 0) { + var directions = [0,2,4,6]; + directions = directions.randomize(); + + var validRoom = false; + + do { + + var dirIdx = directions.pop(); + var newI = i + ROT.DIRS[8][dirIdx][0]; + var newJ = j + ROT.DIRS[8][dirIdx][1]; + + if (newI < 0 || newI >= cw || + newJ < 0 || newJ >= ch) { + continue; + } + + otherRoom = this.rooms[newI][newJ]; + + validRoom = true; + + if (otherRoom["connections"].length == 0) { + break; + } + + for (var k = 0; k < otherRoom["connections"].length; k++) { + if(otherRoom["connections"][k][0] == i && + otherRoom["connections"][k][1] == j) { + validRoom = false; + break; + } + } + + if (validRoom) break; + + } while (directions.length) + + if(validRoom) { + room["connections"].push( [otherRoom["cellx"], otherRoom["celly"]] ); + } else { + console.log("-- Unable to connect room."); + } + } + } + } +} + +ROT.Map.Rogue.prototype._createRandomRoomConnections = function(connections) { + // Empty for now. +} + + +ROT.Map.Rogue.prototype._createRooms = function() { + // Create Rooms + + var w = this._width; + var h = this._height; + + var cw = this._options.cellWidth; + var ch = this._options.cellHeight; + + var cwp = Math.floor(this._width / cw); + var chp = Math.floor(this._height / ch); + + var roomw; + var roomh; + var roomWidth = this._options["roomWidth"]; + var roomHeight = this._options["roomHeight"]; + var sx; + var sy; + var tx; + var ty; + var otherRoom; + + for (var i = 0; i < cw; i++) { + for (var j = 0; j < ch; j++) { + sx = cwp * i; + sy = chp * j; + + if (sx == 0) sx = 1; + if (sy == 0) sy = 1; + + roomw = this._getRandomInt(roomWidth[0], roomWidth[1]); + roomh = this._getRandomInt(roomHeight[0], roomHeight[1]); + + if (j > 0) { + otherRoom = this.rooms[i][j-1]; + while (sy - (otherRoom["y"] + otherRoom["height"] ) < 3) { + sy++; + } + } + + if (i > 0) { + otherRoom = this.rooms[i-1][j]; + while(sx - (otherRoom["x"] + otherRoom["width"]) < 3) { + sx++; + } + } + + var sxOffset = Math.round(this._getRandomInt(0, cwp-roomw)/2); + var syOffset = Math.round(this._getRandomInt(0, chp-roomh)/2); + + while (sx + sxOffset + roomw >= w) { + if(sxOffset) { + sxOffset--; + } else { + roomw--; + } + } + + while (sy + syOffset + roomh >= h) { + if(syOffset) { + syOffset--; + } else { + roomh--; + } + } + + sx = sx + sxOffset; + sy = sy + syOffset; + + this.rooms[i][j]["x"] = sx; + this.rooms[i][j]["y"] = sy; + this.rooms[i][j]["width"] = roomw; + this.rooms[i][j]["height"] = roomh; + + for (var ii = sx; ii < sx + roomw; ii++) { + for (var jj = sy; jj < sy + roomh; jj++) { + this.map[ii][jj] = 0; + } + } + } + } +} + +ROT.Map.Rogue.prototype._getWallPosition = function(aRoom, aDirection) { + var rx; + var ry; + var door; + + if (aDirection == 1 || aDirection == 3) { + rx = this._getRandomInt(aRoom["x"] + 1, aRoom["x"] + aRoom["width"] - 2); + if (aDirection == 1) { + ry = aRoom["y"] - 2; + door = ry + 1; + } else { + ry = aRoom["y"] + aRoom["height"] + 1; + door = ry -1; + } + + this.map[rx][door] = 0; // i'm not setting a specific 'door' tile value right now, just empty space. + + } else if (aDirection == 2 || aDirection == 4) { + ry = this._getRandomInt(aRoom["y"] + 1, aRoom["y"] + aRoom["height"] - 2); + if(aDirection == 2) { + rx = aRoom["x"] + aRoom["width"] + 1; + door = rx - 1; + } else { + rx = aRoom["x"] - 2; + door = rx + 1; + } + + this.map[door][ry] = 0; // i'm not setting a specific 'door' tile value right now, just empty space. + + } + return [rx, ry]; +} + +/*** +* @param startPosition a 2 element array +* @param endPosition a 2 element array +*/ +ROT.Map.Rogue.prototype._drawCorridore = function (startPosition, endPosition) { + var xOffset = endPosition[0] - startPosition[0]; + var yOffset = endPosition[1] - startPosition[1]; + + var xpos = startPosition[0]; + var ypos = startPosition[1]; + + var tempDist; + var xDir; + var yDir; + + var move; // 2 element array, element 0 is the direction, element 1 is the total value to move. + var moves = []; // a list of 2 element arrays + + var xAbs = Math.abs(xOffset); + var yAbs = Math.abs(yOffset); + + var percent = ROT.RNG.getUniform(); // used to split the move at different places along the long axis + var firstHalf = percent; + var secondHalf = 1 - percent; + + xDir = xOffset > 0 ? 2 : 6; + yDir = yOffset > 0 ? 4 : 0; + + if (xAbs < yAbs) { + // move firstHalf of the y offset + tempDist = Math.ceil(yAbs * firstHalf); + moves.push([yDir, tempDist]); + // move all the x offset + moves.push([xDir, xAbs]); + // move sendHalf of the y offset + tempDist = Math.floor(yAbs * secondHalf); + moves.push([yDir, tempDist]); + } else { + // move firstHalf of the x offset + tempDist = Math.ceil(xAbs * firstHalf); + moves.push([xDir, tempDist]); + // move all the y offset + moves.push([yDir, yAbs]); + // move secondHalf of the x offset. + tempDist = Math.floor(xAbs * secondHalf); + moves.push([xDir, tempDist]); + } + + this.map[xpos][ypos] = 0; + + while (moves.length > 0) { + move = moves.pop(); + while (move[1] > 0) { + xpos += ROT.DIRS[8][move[0]][0]; + ypos += ROT.DIRS[8][move[0]][1]; + this.map[xpos][ypos] = 0; + move[1] = move[1] - 1; + } + } +} + +ROT.Map.Rogue.prototype._createCorridors = function () { + // Draw Corridors between connected rooms + + var cw = this._options.cellWidth; + var ch = this._options.cellHeight; + var room; + var connection; + var otherRoom; + var wall; + var otherWall; + + for (var i = 0; i < cw; i++) { + for (var j = 0; j < ch; j++) { + room = this.rooms[i][j]; + + for (var k = 0; k < room["connections"].length; k++) { + + connection = room["connections"][k]; + + otherRoom = this.rooms[connection[0]][connection[1]]; + + // figure out what wall our corridor will start one. + // figure out what wall our corridor will end on. + if (otherRoom["cellx"] > room["cellx"] ) { + wall = 2; + otherWall = 4; + } else if (otherRoom["cellx"] < room["cellx"] ) { + wall = 4; + otherWall = 2; + } else if(otherRoom["celly"] > room["celly"]) { + wall = 3; + otherWall = 1; + } else if(otherRoom["celly"] < room["celly"]) { + wall = 1; + otherWall = 3; + } + + this._drawCorridore(this._getWallPosition(room, wall), this._getWallPosition(otherRoom, otherWall)); + } + } + } +} +/** + * @class Dungeon feature; has own .create() method + */ +ROT.Map.Feature = function() {} +ROT.Map.Feature.prototype.isValid = function(canBeDugCallback) {} +ROT.Map.Feature.prototype.create = function(digCallback) {} +ROT.Map.Feature.prototype.debug = function() {} +ROT.Map.Feature.createRandomAt = function(x, y, dx, dy, options) {} + +/** + * @class Room + * @augments ROT.Map.Feature + * @param {int} x1 + * @param {int} y1 + * @param {int} x2 + * @param {int} y2 + * @param {int} [doorX] + * @param {int} [doorY] + */ +ROT.Map.Feature.Room = function(x1, y1, x2, y2, doorX, doorY) { + this._x1 = x1; + this._y1 = y1; + this._x2 = x2; + this._y2 = y2; + this._doors = {}; + if (arguments.length > 4) { this.addDoor(doorX, doorY); } +} +ROT.Map.Feature.Room.extend(ROT.Map.Feature); + +/** + * Room of random size, with a given doors and direction + */ +ROT.Map.Feature.Room.createRandomAt = function(x, y, dx, dy, options) { + var min = options.roomWidth[0]; + var max = options.roomWidth[1]; + var width = min + Math.floor(ROT.RNG.getUniform()*(max-min+1)); + + var min = options.roomHeight[0]; + var max = options.roomHeight[1]; + var height = min + Math.floor(ROT.RNG.getUniform()*(max-min+1)); + + if (dx == 1) { /* to the right */ + var y2 = y - Math.floor(ROT.RNG.getUniform() * height); + return new this(x+1, y2, x+width, y2+height-1, x, y); + } + + if (dx == -1) { /* to the left */ + var y2 = y - Math.floor(ROT.RNG.getUniform() * height); + return new this(x-width, y2, x-1, y2+height-1, x, y); + } + + if (dy == 1) { /* to the bottom */ + var x2 = x - Math.floor(ROT.RNG.getUniform() * width); + return new this(x2, y+1, x2+width-1, y+height, x, y); + } + + if (dy == -1) { /* to the top */ + var x2 = x - Math.floor(ROT.RNG.getUniform() * width); + return new this(x2, y-height, x2+width-1, y-1, x, y); + } +} + +/** + * Room of random size, positioned around center coords + */ +ROT.Map.Feature.Room.createRandomCenter = function(cx, cy, options) { + var min = options.roomWidth[0]; + var max = options.roomWidth[1]; + var width = min + Math.floor(ROT.RNG.getUniform()*(max-min+1)); + + var min = options.roomHeight[0]; + var max = options.roomHeight[1]; + var height = min + Math.floor(ROT.RNG.getUniform()*(max-min+1)); + + var x1 = cx - Math.floor(ROT.RNG.getUniform()*width); + var y1 = cy - Math.floor(ROT.RNG.getUniform()*height); + var x2 = x1 + width - 1; + var y2 = y1 + height - 1; + + return new this(x1, y1, x2, y2); +} + +/** + * Room of random size within a given dimensions + */ +ROT.Map.Feature.Room.createRandom = function(availWidth, availHeight, options) { + var min = options.roomWidth[0]; + var max = options.roomWidth[1]; + var width = min + Math.floor(ROT.RNG.getUniform()*(max-min+1)); + + var min = options.roomHeight[0]; + var max = options.roomHeight[1]; + var height = min + Math.floor(ROT.RNG.getUniform()*(max-min+1)); + + var left = availWidth - width - 1; + var top = availHeight - height - 1; + + var x1 = 1 + Math.floor(ROT.RNG.getUniform()*left); + var y1 = 1 + Math.floor(ROT.RNG.getUniform()*top); + var x2 = x1 + width - 1; + var y2 = y1 + height - 1; + + return new this(x1, y1, x2, y2); +} + +ROT.Map.Feature.Room.prototype.addDoor = function(x, y) { + this._doors[x+","+y] = 1; + return this; +} + +/** + * @param {function} + */ +ROT.Map.Feature.Room.prototype.getDoors = function(callback) { + for (var key in this._doors) { + var parts = key.split(","); + callback(parseInt(parts[0]), parseInt(parts[1])); + } + return this; +} + +ROT.Map.Feature.Room.prototype.clearDoors = function() { + this._doors = {}; + return this; +} + +ROT.Map.Feature.Room.prototype.addDoors = function(isWallCallback) { + var left = this._x1-1; + var right = this._x2+1; + var top = this._y1-1; + var bottom = this._y2+1; + + for (var x=left; x<=right; x++) { + for (var y=top; y<=bottom; y++) { + if (x != left && x != right && y != top && y != bottom) { continue; } + if (isWallCallback(x, y)) { continue; } + + this.addDoor(x, y); + } + } + + return this; +} + +ROT.Map.Feature.Room.prototype.debug = function() { + console.log("room", this._x1, this._y1, this._x2, this._y2); +} + +ROT.Map.Feature.Room.prototype.isValid = function(isWallCallback, canBeDugCallback) { + var left = this._x1-1; + var right = this._x2+1; + var top = this._y1-1; + var bottom = this._y2+1; + + for (var x=left; x<=right; x++) { + for (var y=top; y<=bottom; y++) { + if (x == left || x == right || y == top || y == bottom) { + if (!isWallCallback(x, y)) { return false; } + } else { + if (!canBeDugCallback(x, y)) { return false; } + } + } + } + + return true; +} + +/** + * @param {function} digCallback Dig callback with a signature (x, y, value). Values: 0 = empty, 1 = wall, 2 = door. Multiple doors are allowed. + */ +ROT.Map.Feature.Room.prototype.create = function(digCallback) { + var left = this._x1-1; + var right = this._x2+1; + var top = this._y1-1; + var bottom = this._y2+1; + + var value = 0; + for (var x=left; x<=right; x++) { + for (var y=top; y<=bottom; y++) { + if (x+","+y in this._doors) { + value = 2; + } else if (x == left || x == right || y == top || y == bottom) { + value = 1; + } else { + value = 0; + } + digCallback(x, y, value); + } + } +} + +ROT.Map.Feature.Room.prototype.getCenter = function() { + return [Math.round((this._x1 + this._x2)/2), Math.round((this._y1 + this._y2)/2)]; +} + +ROT.Map.Feature.Room.prototype.getLeft = function() { + return this._x1; +} + +ROT.Map.Feature.Room.prototype.getRight = function() { + return this._x2; +} + +ROT.Map.Feature.Room.prototype.getTop = function() { + return this._y1; +} + +ROT.Map.Feature.Room.prototype.getBottom = function() { + return this._y2; +} + +/** + * @class Corridor + * @augments ROT.Map.Feature + * @param {int} startX + * @param {int} startY + * @param {int} endX + * @param {int} endY + */ +ROT.Map.Feature.Corridor = function(startX, startY, endX, endY) { + this._startX = startX; + this._startY = startY; + this._endX = endX; + this._endY = endY; + this._endsWithAWall = true; +} +ROT.Map.Feature.Corridor.extend(ROT.Map.Feature); + +ROT.Map.Feature.Corridor.createRandomAt = function(x, y, dx, dy, options) { + var min = options.corridorLength[0]; + var max = options.corridorLength[1]; + var length = min + Math.floor(ROT.RNG.getUniform()*(max-min+1)); + + return new this(x, y, x + dx*length, y + dy*length); +} + +ROT.Map.Feature.Corridor.prototype.debug = function() { + console.log("corridor", this._startX, this._startY, this._endX, this._endY); +} + +ROT.Map.Feature.Corridor.prototype.isValid = function(isWallCallback, canBeDugCallback){ + var sx = this._startX; + var sy = this._startY; + var dx = this._endX-sx; + var dy = this._endY-sy; + var length = 1 + Math.max(Math.abs(dx), Math.abs(dy)); + + if (dx) { dx = dx/Math.abs(dx); } + if (dy) { dy = dy/Math.abs(dy); } + var nx = dy; + var ny = -dx; + + var ok = true; + for (var i=0; i y0) { + i1 = 1; + j1 = 0; + } else { // lower triangle, XY order: (0,0)->(1,0)->(1,1) + i1 = 0; + j1 = 1; + } // upper triangle, YX order: (0,0)->(0,1)->(1,1) + + // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and + // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where + // c = (3-sqrt(3))/6 + var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords + var y1 = y0 - j1 + G2; + var x2 = x0 - 1 + 2*G2; // Offsets for last corner in (x,y) unskewed coords + var y2 = y0 - 1 + 2*G2; + + // Work out the hashed gradient indices of the three simplex corners + var ii = i.mod(count); + var jj = j.mod(count); + + // Calculate the contribution from the three corners + var t0 = 0.5 - x0*x0 - y0*y0; + if (t0 >= 0) { + t0 *= t0; + gi = indexes[ii+perms[jj]]; + var grad = this._gradients[gi]; + n0 = t0 * t0 * (grad[0] * x0 + grad[1] * y0); + } + + var t1 = 0.5 - x1*x1 - y1*y1; + if (t1 >= 0) { + t1 *= t1; + gi = indexes[ii+i1+perms[jj+j1]]; + var grad = this._gradients[gi]; + n1 = t1 * t1 * (grad[0] * x1 + grad[1] * y1); + } + + var t2 = 0.5 - x2*x2 - y2*y2; + if (t2 >= 0) { + t2 *= t2; + gi = indexes[ii+1+perms[jj+1]]; + var grad = this._gradients[gi]; + n2 = t2 * t2 * (grad[0] * x2 + grad[1] * y2); + } + + // Add contributions from each corner to get the final noise value. + // The result is scaled to return values in the interval [-1,1]. + return 70 * (n0 + n1 + n2); +} +/** + * @class Abstract FOV algorithm + * @param {function} lightPassesCallback Does the light pass through x,y? + * @param {object} [options] + * @param {int} [options.topology=8] 4/6/8 + */ +ROT.FOV = function(lightPassesCallback, options) { + this._lightPasses = lightPassesCallback; + this._options = { + topology: 8 + } + for (var p in options) { this._options[p] = options[p]; } +}; + +/** + * Compute visibility + * @param {int} x + * @param {int} y + * @param {int} R Maximum visibility radius + * @param {function} callback + */ +ROT.FOV.prototype.compute = function(x, y, R, callback) {} + +/** + * Return all neighbors in a concentric ring + * @param {int} cx center-x + * @param {int} cy center-y + * @param {int} r range + */ +ROT.FOV.prototype._getCircle = function(cx, cy, r) { + var result = []; + var dirs, countFactor, startOffset; + + switch (this._options.topology) { + case 4: + countFactor = 1; + startOffset = [0, 1]; + dirs = [ + ROT.DIRS[8][7], + ROT.DIRS[8][1], + ROT.DIRS[8][3], + ROT.DIRS[8][5] + ] + break; + + case 6: + dirs = ROT.DIRS[6]; + countFactor = 1; + startOffset = [-1, 1]; + break; + + case 8: + dirs = ROT.DIRS[4]; + countFactor = 2; + startOffset = [-1, 1]; + break; + } + + /* starting neighbor */ + var x = cx + startOffset[0]*r; + var y = cy + startOffset[1]*r; + + /* circle */ + for (var i=0;i A2[0]) { /* split into two sub-arcs */ + var v1 = this._checkVisibility(A1, [A1[1], A1[1]], blocks, SHADOWS); + var v2 = this._checkVisibility([0, 1], A2, blocks, SHADOWS); + return (v1+v2)/2; + } + + /* index1: first shadow >= A1 */ + var index1 = 0, edge1 = false; + while (index1 < SHADOWS.length) { + var old = SHADOWS[index1]; + var diff = old[0]*A1[1] - A1[0]*old[1]; + if (diff >= 0) { /* old >= A1 */ + if (diff == 0 && !(index1 % 2)) { edge1 = true; } + break; + } + index1++; + } + + /* index2: last shadow <= A2 */ + var index2 = SHADOWS.length, edge2 = false; + while (index2--) { + var old = SHADOWS[index2]; + var diff = A2[0]*old[1] - old[0]*A2[1]; + if (diff >= 0) { /* old <= A2 */ + if (diff == 0 && (index2 % 2)) { edge2 = true; } + break; + } + } + + var visible = true; + if (index1 == index2 && (edge1 || edge2)) { /* subset of existing shadow, one of the edges match */ + visible = false; + } else if (edge1 && edge2 && index1+1==index2 && (index2 % 2)) { /* completely equivalent with existing shadow */ + visible = false; + } else if (index1 > index2 && (index1 % 2)) { /* subset of existing shadow, not touching */ + visible = false; + } + + if (!visible) { return 0; } /* fast case: not visible */ + + var visibleLength, P; + + /* compute the length of visible arc, adjust list of shadows (if blocking) */ + var remove = index2-index1+1; + if (remove % 2) { + if (index1 % 2) { /* first edge within existing shadow, second outside */ + var P = SHADOWS[index1]; + visibleLength = (A2[0]*P[1] - P[0]*A2[1]) / (P[1] * A2[1]); + if (blocks) { SHADOWS.splice(index1, remove, A2); } + } else { /* second edge within existing shadow, first outside */ + var P = SHADOWS[index2]; + visibleLength = (P[0]*A1[1] - A1[0]*P[1]) / (A1[1] * P[1]); + if (blocks) { SHADOWS.splice(index1, remove, A1); } + } + } else { + if (index1 % 2) { /* both edges within existing shadows */ + var P1 = SHADOWS[index1]; + var P2 = SHADOWS[index2]; + visibleLength = (P2[0]*P1[1] - P1[0]*P2[1]) / (P1[1] * P2[1]); + if (blocks) { SHADOWS.splice(index1, remove); } + } else { /* both edges outside existing shadows */ + if (blocks) { SHADOWS.splice(index1, remove, A1, A2); } + return 1; /* whole arc visible! */ + } + } + + var arcLength = (A2[0]*A1[1] - A1[0]*A2[1]) / (A1[1] * A2[1]); + + return visibleLength/arcLength; +} +/** + * @namespace Color operations + */ +ROT.Color = { + fromString: function(str) { + var cached, r; + if (str in this._cache) { + cached = this._cache[str]; + } else { + if (str.charAt(0) == "#") { /* hex rgb */ + + var values = str.match(/[0-9a-f]/gi).map(function(x) { return parseInt(x, 16); }); + if (values.length == 3) { + cached = values.map(function(x) { return x*17; }); + } else { + for (var i=0;i<3;i++) { + values[i+1] += 16*values[i]; + values.splice(i, 1); + } + cached = values; + } + + } else if (r = str.match(/rgb\(([0-9, ]+)\)/i)) { /* decimal rgb */ + cached = r[1].split(/\s*,\s*/).map(function(x) { return parseInt(x); }); + } else { /* html name */ + cached = [0, 0, 0]; + } + + this._cache[str] = cached; + } + + return cached.slice(); + }, + + /** + * Add two or more colors + * @param {number[]} color1 + * @param {number[]} color2 + * @returns {number[]} + */ + add: function(color1, color2) { + var result = color1.slice(); + for (var i=0;i<3;i++) { + for (var j=1;j 0.5 ? d / (2 - max - min) : d / (max + min)); + switch(max) { + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + } + h /= 6; + } + + return [h, s, l]; + }, + + /** + * Converts an HSL color value to RGB. Expects 0..1 inputs, produces 0..255 outputs. + * @param {number[]} color + * @returns {number[]} + */ + hsl2rgb: function(color) { + var l = color[2]; + + if (color[1] == 0) { + l *= 255; + return [l, l, l]; + } else { + function hue2rgb(p, q, t) { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1/6) return p + (q - p) * 6 * t; + if (t < 1/2) return q; + if (t < 2/3) return p + (q - p) * (2/3 - t) * 6; + return p; + } + + var s = color[1]; + var q = (l < 0.5 ? l * (1 + s) : l + s - l * s); + var p = 2 * l - q; + var r = hue2rgb(p, q, color[0] + 1/3); + var g = hue2rgb(p, q, color[0]); + var b = hue2rgb(p, q, color[0] - 1/3); + return [Math.round(r*255), Math.round(g*255), Math.round(b*255)]; + } + }, + + toRGB: function(color) { + return "rgb(" + this._clamp(color[0]) + "," + this._clamp(color[1]) + "," + this._clamp(color[2]) + ")"; + }, + + toHex: function(color) { + var parts = []; + for (var i=0;i<3;i++) { + parts.push(this._clamp(color[i]).toString(16).lpad("0", 2)); + } + return "#" + parts.join(""); + }, + + _clamp: function(num) { + if (num < 0) { + return 0; + } else if (num > 255) { + return 255; + } else { + return num; + } + }, + + _cache: { + "black": [0,0,0], + "navy": [0,0,128], + "darkblue": [0,0,139], + "mediumblue": [0,0,205], + "blue": [0,0,255], + "darkgreen": [0,100,0], + "green": [0,128,0], + "teal": [0,128,128], + "darkcyan": [0,139,139], + "deepskyblue": [0,191,255], + "darkturquoise": [0,206,209], + "mediumspringgreen": [0,250,154], + "lime": [0,255,0], + "springgreen": [0,255,127], + "aqua": [0,255,255], + "cyan": [0,255,255], + "midnightblue": [25,25,112], + "dodgerblue": [30,144,255], + "forestgreen": [34,139,34], + "seagreen": [46,139,87], + "darkslategray": [47,79,79], + "darkslategrey": [47,79,79], + "limegreen": [50,205,50], + "mediumseagreen": [60,179,113], + "turquoise": [64,224,208], + "royalblue": [65,105,225], + "steelblue": [70,130,180], + "darkslateblue": [72,61,139], + "mediumturquoise": [72,209,204], + "indigo": [75,0,130], + "darkolivegreen": [85,107,47], + "cadetblue": [95,158,160], + "cornflowerblue": [100,149,237], + "mediumaquamarine": [102,205,170], + "dimgray": [105,105,105], + "dimgrey": [105,105,105], + "slateblue": [106,90,205], + "olivedrab": [107,142,35], + "slategray": [112,128,144], + "slategrey": [112,128,144], + "lightslategray": [119,136,153], + "lightslategrey": [119,136,153], + "mediumslateblue": [123,104,238], + "lawngreen": [124,252,0], + "chartreuse": [127,255,0], + "aquamarine": [127,255,212], + "maroon": [128,0,0], + "purple": [128,0,128], + "olive": [128,128,0], + "gray": [128,128,128], + "grey": [128,128,128], + "skyblue": [135,206,235], + "lightskyblue": [135,206,250], + "blueviolet": [138,43,226], + "darkred": [139,0,0], + "darkmagenta": [139,0,139], + "saddlebrown": [139,69,19], + "darkseagreen": [143,188,143], + "lightgreen": [144,238,144], + "mediumpurple": [147,112,216], + "darkviolet": [148,0,211], + "palegreen": [152,251,152], + "darkorchid": [153,50,204], + "yellowgreen": [154,205,50], + "sienna": [160,82,45], + "brown": [165,42,42], + "darkgray": [169,169,169], + "darkgrey": [169,169,169], + "lightblue": [173,216,230], + "greenyellow": [173,255,47], + "paleturquoise": [175,238,238], + "lightsteelblue": [176,196,222], + "powderblue": [176,224,230], + "firebrick": [178,34,34], + "darkgoldenrod": [184,134,11], + "mediumorchid": [186,85,211], + "rosybrown": [188,143,143], + "darkkhaki": [189,183,107], + "silver": [192,192,192], + "mediumvioletred": [199,21,133], + "indianred": [205,92,92], + "peru": [205,133,63], + "chocolate": [210,105,30], + "tan": [210,180,140], + "lightgray": [211,211,211], + "lightgrey": [211,211,211], + "palevioletred": [216,112,147], + "thistle": [216,191,216], + "orchid": [218,112,214], + "goldenrod": [218,165,32], + "crimson": [220,20,60], + "gainsboro": [220,220,220], + "plum": [221,160,221], + "burlywood": [222,184,135], + "lightcyan": [224,255,255], + "lavender": [230,230,250], + "darksalmon": [233,150,122], + "violet": [238,130,238], + "palegoldenrod": [238,232,170], + "lightcoral": [240,128,128], + "khaki": [240,230,140], + "aliceblue": [240,248,255], + "honeydew": [240,255,240], + "azure": [240,255,255], + "sandybrown": [244,164,96], + "wheat": [245,222,179], + "beige": [245,245,220], + "whitesmoke": [245,245,245], + "mintcream": [245,255,250], + "ghostwhite": [248,248,255], + "salmon": [250,128,114], + "antiquewhite": [250,235,215], + "linen": [250,240,230], + "lightgoldenrodyellow": [250,250,210], + "oldlace": [253,245,230], + "red": [255,0,0], + "fuchsia": [255,0,255], + "magenta": [255,0,255], + "deeppink": [255,20,147], + "orangered": [255,69,0], + "tomato": [255,99,71], + "hotpink": [255,105,180], + "coral": [255,127,80], + "darkorange": [255,140,0], + "lightsalmon": [255,160,122], + "orange": [255,165,0], + "lightpink": [255,182,193], + "pink": [255,192,203], + "gold": [255,215,0], + "peachpuff": [255,218,185], + "navajowhite": [255,222,173], + "moccasin": [255,228,181], + "bisque": [255,228,196], + "mistyrose": [255,228,225], + "blanchedalmond": [255,235,205], + "papayawhip": [255,239,213], + "lavenderblush": [255,240,245], + "seashell": [255,245,238], + "cornsilk": [255,248,220], + "lemonchiffon": [255,250,205], + "floralwhite": [255,250,240], + "snow": [255,250,250], + "yellow": [255,255,0], + "lightyellow": [255,255,224], + "ivory": [255,255,240], + "white": [255,255,255] + } +} +/** + * @class Lighting computation, based on a traditional FOV for multiple light sources and multiple passes. + * @param {function} reflectivityCallback Callback to retrieve cell reflectivity (0..1) + * @param {object} [options] + * @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. + * @param {int} [options.emissionThreshold=100] Cells with emissivity > threshold will be treated as light source in the next pass. + * @param {int} [options.range=10] Max light range + */ +ROT.Lighting = function(reflectivityCallback, options) { + this._reflectivityCallback = reflectivityCallback; + this._options = { + passes: 1, + emissionThreshold: 100, + range: 10 + }; + this._fov = null; + + this._lights = {}; + this._reflectivityCache = {}; + this._fovCache = {}; + + this.setOptions(options); +} + +/** + * Adjust options at runtime + * @see ROT.Lighting + * @param {object} [options] + */ +ROT.Lighting.prototype.setOptions = function(options) { + for (var p in options) { this._options[p] = options[p]; } + if (options.range) { this.reset(); } + return this; +} + +/** + * Set the used Field-Of-View algo + * @param {ROT.FOV} fov + */ +ROT.Lighting.prototype.setFOV = function(fov) { + this._fov = fov; + this._fovCache = {}; + return this; +} + +/** + * Set (or remove) a light source + * @param {int} x + * @param {int} y + * @param {null || string || number[3]} color + */ +ROT.Lighting.prototype.setLight = function(x, y, color) { + var key = x+","+y; + + if (color) { + this._lights[key] = (typeof(color) == "string" ? ROT.Color.fromString(color) : color); + } else { + delete this._lights[key]; + } + return this; +} + +/** + * Reset the pre-computed topology values. Call whenever the underlying map changes its light-passability. + */ +ROT.Lighting.prototype.reset = function() { + this._reflectivityCache = {}; + this._fovCache = {}; + + return this; +} + +/** + * Compute the lighting + * @param {function} lightingCallback Will be called with (x, y, color) for every lit cell + */ +ROT.Lighting.prototype.compute = function(lightingCallback) { + var doneCells = {}; + var emittingCells = {}; + var litCells = {}; + + for (var key in this._lights) { /* prepare emitters for first pass */ + var light = this._lights[key]; + if (!(key in emittingCells)) { emittingCells[key] = [0, 0, 0]; } + + ROT.Color.add_(emittingCells[key], light); + } + + for (var i=0;i this._options.emissionThreshold) { result[key] = emission; } + } + + return result; +} + +/** + * Compute one iteration from one cell + * @param {int} x + * @param {int} y + * @param {number[]} color + * @param {object} litCells Cell data to by updated + */ +ROT.Lighting.prototype._emitLightFromCell = function(x, y, color, litCells) { + var key = x+","+y; + if (key in this._fovCache) { + var fov = this._fovCache[key]; + } else { + var fov = this._updateFOV(x, y); + } + + for (var fovKey in fov) { + var formFactor = fov[fovKey]; + + if (fovKey in litCells) { /* already lit */ + var result = litCells[fovKey]; + } else { /* newly lit */ + var result = [0, 0, 0]; + litCells[fovKey] = result; + } + + for (var i=0;i<3;i++) { result[i] += Math.round(color[i]*formFactor); } /* add light color */ + } + + return this; +} + +/** + * Compute FOV ("form factor") for a potential light source at [x,y] + * @param {int} x + * @param {int} y + * @returns {object} + */ +ROT.Lighting.prototype._updateFOV = function(x, y) { + var key1 = x+","+y; + var cache = {}; + this._fovCache[key1] = cache; + var range = this._options.range; + var cb = function(x, y, r, vis) { + var key2 = x+","+y; + var formFactor = vis * (1-r/range); + if (formFactor == 0) { return; } + cache[key2] = formFactor; + } + this._fov.compute(x, y, range, cb.bind(this)); + + return cache; +} +/** + * @class Abstract pathfinder + * @param {int} toX Target X coord + * @param {int} toY Target Y coord + * @param {function} passableCallback Callback to determine map passability + * @param {object} [options] + * @param {int} [options.topology=8] + */ +ROT.Path = function(toX, toY, passableCallback, options) { + this._toX = toX; + this._toY = toY; + this._fromX = null; + this._fromY = null; + this._passableCallback = passableCallback; + this._options = { + topology: 8 + } + for (var p in options) { this._options[p] = options[p]; } + + this._dirs = ROT.DIRS[this._options.topology]; +} + +/** + * Compute a path from a given point + * @param {int} fromX + * @param {int} fromY + * @param {function} callback Will be called for every path item with arguments "x" and "y" + */ +ROT.Path.prototype.compute = function(fromX, fromY, callback) { +} + +ROT.Path.prototype._getNeighbors = function(cx, cy) { + var result = []; + for (var i=0;i + +test + + + + + + + \ No newline at end of file From e83835c38055d90c99765f8717e8d6354cdd42b1 Mon Sep 17 00:00:00 2001 From: mdtrooper Date: Tue, 20 Aug 2013 02:47:44 +0200 Subject: [PATCH 05/20] More job into the image ascii art. --- rot.js | 14 ++++++- rot.min.js | 2 +- src/image/image.js | 96 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 109 insertions(+), 3 deletions(-) diff --git a/rot.js b/rot.js index 52128abc..fcc2dc6d 100644 --- a/rot.js +++ b/rot.js @@ -1,6 +1,6 @@ /* This is rot.js, the ROguelike Toolkit in JavaScript. - Version 0.5~dev, generated on Thu Jul 25 08:44:01 CEST 2013. + Version 0.5~dev, generated on vie ago 9 01:23:35 CEST 2013. */ /** @@ -4729,3 +4729,15 @@ ROT.Path.AStar.prototype._distance = function(x, y) { break; } } +/** + * @class Base image (as ASCII art) converter + */ +ROT.Image = function() { + this.aDefaultCharList = (" .,:;i1tfLCG08@").split(""); + this.aDefaultColorCharList = (" CGO08@").split(""); + this.strFont = "courier new"; +}; + +ROT.Image.prototype.get = function(xin, yin) { + console.log(888); +} diff --git a/rot.min.js b/rot.min.js index 06a991c1..870c7304 100644 --- a/rot.min.js +++ b/rot.min.js @@ -132,4 +132,4 @@ ROT.Path.Dijkstra=function(a,b,c,d){ROT.Path.call(this,a,b,c,d);this._computed={ ROT.Path.Dijkstra.prototype._compute=function(a,b){for(;this._todo.length;){var c=this._todo.shift();if(c.x==a&&c.y==b)break;for(var d=this._getNeighbors(c.x,c.y),e=0;e" + strThisChar + ""; + //~ } else { + //~ strChars += strThisChar; + //~ } + } + strChars += "
"; + } + + return strChars; +}; + +ROT.Image.prototype.load = function(image_url) { + this.img = new Image(); + this.img.src = image_url; + if (this.img.complete) { + this.ascii_art = this.loadASCII(); + } + else { + this.img.onload = function() { + this.ascii_art = this.loadASCII() + } + } }; ROT.Image.prototype.get = function(xin, yin) { console.log(888); -} +}; \ No newline at end of file From 82c4f889dfa0ccc5e0ec518e387f8c9f307c3fb4 Mon Sep 17 00:00:00 2001 From: mdtrooper Date: Tue, 20 Aug 2013 02:49:32 +0200 Subject: [PATCH 06/20] Delete a ugly files. --- examples/rot.js | 4743 -------------------------------------------- examples/test.html | 31 - 2 files changed, 4774 deletions(-) delete mode 100644 examples/rot.js delete mode 100644 examples/test.html diff --git a/examples/rot.js b/examples/rot.js deleted file mode 100644 index fcc2dc6d..00000000 --- a/examples/rot.js +++ /dev/null @@ -1,4743 +0,0 @@ -/* - This is rot.js, the ROguelike Toolkit in JavaScript. - Version 0.5~dev, generated on vie ago 9 01:23:35 CEST 2013. -*/ - -/** - * @namespace Top-level ROT namespace - */ -var ROT = { - /** - * @returns {bool} Is rot.js supported by this browser? - */ - isSupported: function() { - return !!(document.createElement("canvas").getContext && Function.prototype.bind); - }, - - /** Default with for display and map generators */ - DEFAULT_WIDTH: 80, - /** Default height for display and map generators */ - DEFAULT_HEIGHT: 25, - - /** Directional constants. Ordering is important! */ - DIRS: { - "4": [ - [ 0, -1], - [ 1, 0], - [ 0, 1], - [-1, 0] - ], - "8": [ - [ 0, -1], - [ 1, -1], - [ 1, 0], - [ 1, 1], - [ 0, 1], - [-1, 1], - [-1, 0], - [-1, -1] - ], - "6": [ - [-1, -1], - [ 1, -1], - [ 2, 0], - [ 1, 1], - [-1, 1], - [-2, 0] - ] - }, - - /** Cancel key. */ - VK_CANCEL: 3, - /** Help key. */ - VK_HELP: 6, - /** Backspace key. */ - VK_BACK_SPACE: 8, - /** Tab key. */ - VK_TAB: 9, - /** 5 key on Numpad when NumLock is unlocked. Or on Mac, clear key which is positioned at NumLock key. */ - VK_CLEAR: 12, - /** Return/enter key on the main keyboard. */ - VK_RETURN: 13, - /** Reserved, but not used. */ - VK_ENTER: 14, - /** Shift key. */ - VK_SHIFT: 16, - /** Control key. */ - VK_CONTROL: 17, - /** Alt (Option on Mac) key. */ - VK_ALT: 18, - /** Pause key. */ - VK_PAUSE: 19, - /** Caps lock. */ - VK_CAPS_LOCK: 20, - /** Escape key. */ - VK_ESCAPE: 27, - /** Space bar. */ - VK_SPACE: 32, - /** Page Up key. */ - VK_PAGE_UP: 33, - /** Page Down key. */ - VK_PAGE_DOWN: 34, - /** End key. */ - VK_END: 35, - /** Home key. */ - VK_HOME: 36, - /** Left arrow. */ - VK_LEFT: 37, - /** Up arrow. */ - VK_UP: 38, - /** Right arrow. */ - VK_RIGHT: 39, - /** Down arrow. */ - VK_DOWN: 40, - /** Print Screen key. */ - VK_PRINTSCREEN: 44, - /** Ins(ert) key. */ - VK_INSERT: 45, - /** Del(ete) key. */ - VK_DELETE: 46, - /***/ - VK_0: 48, - /***/ - VK_1: 49, - /***/ - VK_2: 50, - /***/ - VK_3: 51, - /***/ - VK_4: 52, - /***/ - VK_5: 53, - /***/ - VK_6: 54, - /***/ - VK_7: 55, - /***/ - VK_8: 56, - /***/ - VK_9: 57, - /** Colon (:) key. Requires Gecko 15.0 */ - VK_COLON: 58, - /** Semicolon (;) key. */ - VK_SEMICOLON: 59, - /** Less-than (<) key. Requires Gecko 15.0 */ - VK_LESS_THAN: 60, - /** Equals (=) key. */ - VK_EQUALS: 61, - /** Greater-than (>) key. Requires Gecko 15.0 */ - VK_GREATER_THAN: 62, - /** Question mark (?) key. Requires Gecko 15.0 */ - VK_QUESTION_MARK: 63, - /** Atmark (@) key. Requires Gecko 15.0 */ - VK_AT: 64, - /***/ - VK_A: 65, - /***/ - VK_B: 66, - /***/ - VK_C: 67, - /***/ - VK_D: 68, - /***/ - VK_E: 69, - /***/ - VK_F: 70, - /***/ - VK_G: 71, - /***/ - VK_H: 72, - /***/ - VK_I: 73, - /***/ - VK_J: 74, - /***/ - VK_K: 75, - /***/ - VK_L: 76, - /***/ - VK_M: 77, - /***/ - VK_N: 78, - /***/ - VK_O: 79, - /***/ - VK_P: 80, - /***/ - VK_Q: 81, - /***/ - VK_R: 82, - /***/ - VK_S: 83, - /***/ - VK_T: 84, - /***/ - VK_U: 85, - /***/ - VK_V: 86, - /***/ - VK_W: 87, - /***/ - VK_X: 88, - /***/ - VK_Y: 89, - /***/ - VK_Z: 90, - /***/ - VK_CONTEXT_MENU: 93, - /** 0 on the numeric keypad. */ - VK_NUMPAD0: 96, - /** 1 on the numeric keypad. */ - VK_NUMPAD1: 97, - /** 2 on the numeric keypad. */ - VK_NUMPAD2: 98, - /** 3 on the numeric keypad. */ - VK_NUMPAD3: 99, - /** 4 on the numeric keypad. */ - VK_NUMPAD4: 100, - /** 5 on the numeric keypad. */ - VK_NUMPAD5: 101, - /** 6 on the numeric keypad. */ - VK_NUMPAD6: 102, - /** 7 on the numeric keypad. */ - VK_NUMPAD7: 103, - /** 8 on the numeric keypad. */ - VK_NUMPAD8: 104, - /** 9 on the numeric keypad. */ - VK_NUMPAD9: 105, - /** * on the numeric keypad. */ - VK_MULTIPLY: 106, - /** + on the numeric keypad. */ - VK_ADD: 107, - /***/ - VK_SEPARATOR: 108, - /** - on the numeric keypad. */ - VK_SUBTRACT: 109, - /** Decimal point on the numeric keypad. */ - VK_DECIMAL: 110, - /** / on the numeric keypad. */ - VK_DIVIDE: 111, - /** F1 key. */ - VK_F1: 112, - /** F2 key. */ - VK_F2: 113, - /** F3 key. */ - VK_F3: 114, - /** F4 key. */ - VK_F4: 115, - /** F5 key. */ - VK_F5: 116, - /** F6 key. */ - VK_F6: 117, - /** F7 key. */ - VK_F7: 118, - /** F8 key. */ - VK_F8: 119, - /** F9 key. */ - VK_F9: 120, - /** F10 key. */ - VK_F10: 121, - /** F11 key. */ - VK_F11: 122, - /** F12 key. */ - VK_F12: 123, - /** F13 key. */ - VK_F13: 124, - /** F14 key. */ - VK_F14: 125, - /** F15 key. */ - VK_F15: 126, - /** F16 key. */ - VK_F16: 127, - /** F17 key. */ - VK_F17: 128, - /** F18 key. */ - VK_F18: 129, - /** F19 key. */ - VK_F19: 130, - /** F20 key. */ - VK_F20: 131, - /** F21 key. */ - VK_F21: 132, - /** F22 key. */ - VK_F22: 133, - /** F23 key. */ - VK_F23: 134, - /** F24 key. */ - VK_F24: 135, - /** Num Lock key. */ - VK_NUM_LOCK: 144, - /** Scroll Lock key. */ - VK_SCROLL_LOCK: 145, - /** Circumflex (^) key. Requires Gecko 15.0 */ - VK_CIRCUMFLEX: 160, - /** Exclamation (!) key. Requires Gecko 15.0 */ - VK_EXCLAMATION: 161, - /** Double quote () key. Requires Gecko 15.0 */ - VK_DOUBLE_QUOTE: 162, - /** Hash (#) key. Requires Gecko 15.0 */ - VK_HASH: 163, - /** Dollar sign ($) key. Requires Gecko 15.0 */ - VK_DOLLAR: 164, - /** Percent (%) key. Requires Gecko 15.0 */ - VK_PERCENT: 165, - /** Ampersand (&) key. Requires Gecko 15.0 */ - VK_AMPERSAND: 166, - /** Underscore (_) key. Requires Gecko 15.0 */ - VK_UNDERSCORE: 167, - /** Open parenthesis (() key. Requires Gecko 15.0 */ - VK_OPEN_PAREN: 168, - /** Close parenthesis ()) key. Requires Gecko 15.0 */ - VK_CLOSE_PAREN: 169, - /* Asterisk (*) key. Requires Gecko 15.0 */ - VK_ASTERISK: 170, - /** Plus (+) key. Requires Gecko 15.0 */ - VK_PLUS: 171, - /** Pipe (|) key. Requires Gecko 15.0 */ - VK_PIPE: 172, - /** Hyphen-US/docs/Minus (-) key. Requires Gecko 15.0 */ - VK_HYPHEN_MINUS: 173, - /** Open curly bracket ({) key. Requires Gecko 15.0 */ - VK_OPEN_CURLY_BRACKET: 174, - /** Close curly bracket (}) key. Requires Gecko 15.0 */ - VK_CLOSE_CURLY_BRACKET: 175, - /** Tilde (~) key. Requires Gecko 15.0 */ - VK_TILDE: 176, - /** Comma (,) key. */ - VK_COMMA: 188, - /** Period (.) key. */ - VK_PERIOD: 190, - /** Slash (/) key. */ - VK_SLASH: 191, - /** Back tick (`) key. */ - VK_BACK_QUOTE: 192, - /** Open square bracket ([) key. */ - VK_OPEN_BRACKET: 219, - /** Back slash (\) key. */ - VK_BACK_SLASH: 220, - /** Close square bracket (]) key. */ - VK_CLOSE_BRACKET: 221, - /** Quote (''') key. */ - VK_QUOTE: 222, - /** Meta key on Linux, Command key on Mac. */ - VK_META: 224, - /** AltGr key on Linux. Requires Gecko 15.0 */ - VK_ALTGR: 225, - /** Windows logo key on Windows. Or Super or Hyper key on Linux. Requires Gecko 15.0 */ - VK_WIN: 91, - /** Linux support for this keycode was added in Gecko 4.0. */ - VK_KANA: 21, - /** Linux support for this keycode was added in Gecko 4.0. */ - VK_HANGUL: 21, - /** 英数 key on Japanese Mac keyboard. Requires Gecko 15.0 */ - VK_EISU: 22, - /** Linux support for this keycode was added in Gecko 4.0. */ - VK_JUNJA: 23, - /** Linux support for this keycode was added in Gecko 4.0. */ - VK_FINAL: 24, - /** Linux support for this keycode was added in Gecko 4.0. */ - VK_HANJA: 25, - /** Linux support for this keycode was added in Gecko 4.0. */ - VK_KANJI: 25, - /** Linux support for this keycode was added in Gecko 4.0. */ - VK_CONVERT: 28, - /** Linux support for this keycode was added in Gecko 4.0. */ - VK_NONCONVERT: 29, - /** Linux support for this keycode was added in Gecko 4.0. */ - VK_ACCEPT: 30, - /** Linux support for this keycode was added in Gecko 4.0. */ - VK_MODECHANGE: 31, - /** Linux support for this keycode was added in Gecko 4.0. */ - VK_SELECT: 41, - /** Linux support for this keycode was added in Gecko 4.0. */ - VK_PRINT: 42, - /** Linux support for this keycode was added in Gecko 4.0. */ - VK_EXECUTE: 43, - /** Linux support for this keycode was added in Gecko 4.0. */ - VK_SLEEP: 95 -}; -/** - * @namespace - * Contains text tokenization and breaking routines - */ -ROT.Text = { - RE_COLORS: /%([bc]){([^}]*)}/g, - - /* token types */ - TYPE_TEXT: 0, - TYPE_NEWLINE: 1, - TYPE_FG: 2, - TYPE_BG: 3, - - /** - * Measure size of a resulting text block - */ - measure: function(str, maxWidth) { - var result = {width:0, height:1}; - var tokens = this.tokenize(str, maxWidth); - var lineWidth = 0; - - for (var i=0;i maxWidth) { /* line too long, find a suitable breaking spot */ - - /* is it possible to break within this token? */ - var index = -1; - while (1) { - var nextIndex = token.value.indexOf(" ", index+1); - if (nextIndex == -1) { break; } - if (lineLength + nextIndex > maxWidth) { break; } - index = nextIndex; - } - - if (index != -1) { /* break at space within this one */ - token.value = this._breakInsideToken(tokens, i, index, true); - } else if (lastTokenWithSpace != -1) { /* is there a previous token where a break can occur? */ - var token = tokens[lastTokenWithSpace]; - var breakIndex = token.value.lastIndexOf(" "); - token.value = this._breakInsideToken(tokens, lastTokenWithSpace, breakIndex, true); - i = lastTokenWithSpace; - } else { /* force break in this token */ - token.value = this._breakInsideToken(tokens, i, maxWidth-lineLength, false); - } - - } else { /* line not long, continue */ - lineLength += token.value.length; - if (token.value.indexOf(" ") != -1) { lastTokenWithSpace = i; } - } - - i++; /* advance to next token */ - } - - - tokens.push({type: ROT.Text.TYPE_NEWLINE}); /* insert fake newline to fix the last text line */ - - /* remove trailing space from text tokens before newlines */ - var lastTextToken = null; - for (var i=0;i= this._context.canvas.width || y >= this._context.canvas.height) { return [-1, -1]; } - - return this._backend.eventToPosition(x, y); -} - -/** - * @param {int} x - * @param {int} y - * @param {string} ch - * @param {string} [fg] foreground color - * @param {string} [bg] background color - */ -ROT.Display.prototype.draw = function(x, y, ch, fg, bg) { - if (!fg) { fg = this._options.fg; } - if (!bg) { bg = this._options.bg; } - this._data[x+","+y] = [x, y, ch, fg, bg]; - - if (this._dirty === true) { return; } /* will already redraw everything */ - if (!this._dirty) { this._dirty = {}; } /* first! */ - this._dirty[x+","+y] = true; -} - -/** - * Draws a text at given position. Optionally wraps at a maximum length. Currently does not work with hex layout. - * @param {int} x - * @param {int} y - * @param {string} text May contain color/background format specifiers, %c{name}/%b{name}, both optional. %c{}/%b{} resets to default. - * @param {int} [maxWidth] wrap at what width? - * @returns {int} lines drawn - */ -ROT.Display.prototype.drawText = function(x, y, text, maxWidth) { - var fg = null; - var bg = null; - var cx = x; - var cy = y; - var lines = 1; - if (!maxWidth) { maxWidth = this._options.width-x; } - - var tokens = ROT.Text.tokenize(text, maxWidth); - - while (tokens.length) { /* interpret tokenized opcode stream */ - var token = tokens.shift(); - switch (token.type) { - case ROT.Text.TYPE_TEXT: - for (var i=0;i 1) { /* too wide with current aspect ratio */ - boxHeight = Math.floor(boxHeight / widthFraction); - } - return Math.floor(boxHeight / this._options.spacing); -} - -ROT.Display.Rect.prototype.eventToPosition = function(x, y) { - return [Math.floor(x/this._spacingX), Math.floor(y/this._spacingY)]; -} -/** - * @class Hexagonal backend - * @private - */ -ROT.Display.Hex = function(context) { - ROT.Display.Backend.call(this, context); - - this._spacingX = 0; - this._spacingY = 0; - this._hexSize = 0; - this._options = {}; -} -ROT.Display.Hex.extend(ROT.Display.Backend); - -ROT.Display.Hex.prototype.compute = function(options) { - this._options = options; - - var charWidth = Math.ceil(this._context.measureText("W").width); - this._hexSize = Math.floor(options.spacing * (options.fontSize + charWidth/Math.sqrt(3)) / 2); - this._spacingX = this._hexSize * Math.sqrt(3) / 2; - this._spacingY = this._hexSize * 1.5; - this._context.canvas.width = Math.ceil( (options.width + 1) * this._spacingX ); - this._context.canvas.height = Math.ceil( (options.height - 1) * this._spacingY + 2*this._hexSize ); -} - -ROT.Display.Hex.prototype.draw = function(data, clearBefore) { - var x = data[0]; - var y = data[1]; - var ch = data[2]; - var fg = data[3]; - var bg = data[4]; - - var cx = (x+1) * this._spacingX; - var cy = y * this._spacingY + this._hexSize; - - if (clearBefore) { - this._context.fillStyle = bg; - this._fill(cx, cy); - } - - if (!ch) { return; } - - this._context.fillStyle = fg; - this._context.fillText(ch, cx, cy); -} - - -ROT.Display.Hex.prototype.computeSize = function(availWidth, availHeight) { - var width = Math.floor(availWidth / this._spacingX) - 1; - var height = Math.floor((availHeight - 2*this._hexSize) / this._spacingY + 1); - return [width, height]; -} - -ROT.Display.Hex.prototype.computeFontSize = function(availWidth, availHeight) { - var hexSizeWidth = 2*availWidth / ((this._options.width+1) * Math.sqrt(3)) - 1; - var hexSizeHeight = availHeight / (2 + 1.5*(this._options.height-1)); - var hexSize = Math.min(hexSizeWidth, hexSizeHeight); - - /* compute char ratio */ - var oldFont = this._context.font; - this._context.font = "100px " + this._options.fontFamily; - var width = Math.ceil(this._context.measureText("W").width); - this._context.font = oldFont; - var ratio = width / 100; - - hexSize = Math.floor(hexSize)+1; /* closest larger hexSize */ - - var fontSize = 2*hexSize / (this._options.spacing * (1 + ratio / Math.sqrt(3))); - - /* closest smaller fontSize */ - return Math.ceil(fontSize)-1; -} - -ROT.Display.Hex.prototype.eventToPosition = function(x, y) { - var height = this._context.canvas.height / this._options.height; - y = Math.floor(y/height); - - if (y.mod(2)) { /* odd row */ - x -= this._spacingX; - x = 1 + 2*Math.floor(x/(2*this._spacingX)); - } else { - x = 2*Math.floor(x/(2*this._spacingX)); - } - - return [x, y]; -} - -ROT.Display.Hex.prototype._fill = function(cx, cy) { - var a = this._hexSize; - var b = this._options.border; - - this._context.beginPath(); - this._context.moveTo(cx, cy-a+b); - this._context.lineTo(cx + this._spacingX - b, cy-a/2+b); - this._context.lineTo(cx + this._spacingX - b, cy+a/2-b); - this._context.lineTo(cx, cy+a-b); - this._context.lineTo(cx - this._spacingX + b, cy+a/2-b); - this._context.lineTo(cx - this._spacingX + b, cy-a/2+b); - this._context.lineTo(cx, cy-a+b); - this._context.fill(); -} -/** - * @namespace - * This code is an implementation of Alea algorithm; (C) 2010 Johannes Baagøe. - * Alea is licensed according to the http://en.wikipedia.org/wiki/MIT_License. - */ -ROT.RNG = { - /** - * @returns {number} - */ - getSeed: function() { - return this._seed; - }, - - /** - * @param {number} seed Seed the number generator - */ - setSeed: function(seed) { - seed = (seed < 1 ? 1/seed : seed); - - this._seed = seed; - this._s0 = (seed >>> 0) * this._frac; - - seed = (seed*69069 + 1) >>> 0; - this._s1 = seed * this._frac; - - seed = (seed*69069 + 1) >>> 0; - this._s2 = seed * this._frac; - - this._c = 1; - return this; - }, - - /** - * @returns {float} Pseudorandom value [0,1), uniformly distributed - */ - getUniform: function() { - var t = 2091639 * this._s0 + this._c * this._frac; - this._s0 = this._s1; - this._s1 = this._s2; - this._c = t | 0; - this._s2 = t - this._c; - return this._s2; - }, - - /** - * @param {float} [mean=0] Mean value - * @param {float} [stddev=1] Standard deviation. ~95% of the absolute values will be lower than 2*stddev. - * @returns {float} A normally distributed pseudorandom value - */ - getNormal: function(mean, stddev) { - do { - var u = 2*this.getUniform()-1; - var v = 2*this.getUniform()-1; - var r = u*u + v*v; - } while (r > 1 || r == 0); - - var gauss = u * Math.sqrt(-2*Math.log(r)/r); - return (mean || 0) + gauss*(stddev || 1); - }, - - /** - * @returns {int} Pseudorandom value [1,100] inclusive, uniformly distributed - */ - getPercentage: function() { - return 1 + Math.floor(this.getUniform()*100); - }, - - /** - * @param {object} data key=whatever, value=weight (relative probability) - * @returns {string} whatever - */ - getWeightedValue: function(data) { - var avail = []; - var total = 0; - - for (var id in data) { - total += data[id]; - } - var random = Math.floor(this.getUniform()*total); - - var part = 0; - for (var id in data) { - part += data[id]; - if (random < part) { return id; } - } - - return null; - }, - - /** - * Get RNG state. Useful for storing the state and re-setting it via setState. - * @returns {?} Internal state - */ - getState: function() { - return [this._s0, this._s1, this._s2, this._c]; - }, - - /** - * Set a previously retrieved state. - * @param {?} state - */ - setState: function(state) { - this._s0 = state[0]; - this._s1 = state[1]; - this._s2 = state[2]; - this._c = state[3]; - return this; - }, - - _s0: 0, - _s1: 0, - _s2: 0, - _c: 0, - _frac: 2.3283064365386963e-10 /* 2^-32 */ -} - -ROT.RNG.setSeed(Date.now()); -/** - * @class (Markov process)-based string generator. - * Copied from a RogueBasin article. - * Offers configurable order and prior. - * @param {object} [options] - * @param {bool} [options.words=false] Use word mode? - * @param {int} [options.order=3] - * @param {float} [options.prior=0.001] - */ -ROT.StringGenerator = function(options) { - this._options = { - words: false, - order: 3, - prior: 0.001 - } - for (var p in options) { this._options[p] = options[p]; } - - this._boundary = String.fromCharCode(0); - this._suffix = this._boundary; - this._prefix = []; - for (var i=0;i this._options.order) { - context = context.slice(-this._options.order); - } else if (context.length < this._options.order) { - context = this._prefix.slice(0, this._options.order - context.length).concat(context); - } - - while (!(this._join(context) in this._data) && context.length > 0) { context = context.slice(1); } - - return context; -} - - -ROT.StringGenerator.prototype._pickRandom = function(data) { - var total = 0; - - for (var id in data) { - total += data[id]; - } - var random = ROT.RNG.getUniform()*total; - - var part = 0; - for (var id in data) { - part += data[id]; - if (random < part) { return id; } - } -} -/** - * @class Generic event queue: stores events and retrieves them based on their time - */ -ROT.EventQueue = function() { - this._time = 0; - this._events = []; - this._eventTimes = []; -} - -/** - * @returns {number} Elapsed time - */ -ROT.EventQueue.prototype.getTime = function() { - return this._time; -} - -/** - * Clear all scheduled events - */ -ROT.EventQueue.prototype.clear = function() { - this._events = []; - this._eventTimes = []; - return this; -} - -/** - * @param {?} event - * @param {number} time - */ -ROT.EventQueue.prototype.add = function(event, time) { - var index = this._events.length; - for (var i=0;i time) { - index = i; - break; - } - } - - this._events.splice(index, 0, event); - this._eventTimes.splice(index, 0, time); -} - -/** - * Locates the nearest event, advances time if necessary. Returns that event and removes it from the queue. - * @returns {? || null} The event previously added by addEvent, null if no event available - */ -ROT.EventQueue.prototype.get = function() { - if (!this._events.length) { return null; } - - var time = this._eventTimes.splice(0, 1)[0]; - if (time > 0) { /* advance */ - this._time += time; - for (var i=0;i= width || y >= height) { return false; } - return map[x][y]; -} -/** - * @class Maze generator - Eller's algorithm - * See http://homepages.cwi.nl/~tromp/maze.html for explanation - * @augments ROT.Map - */ -ROT.Map.EllerMaze = function(width, height) { - ROT.Map.call(this, width, height); -} -ROT.Map.EllerMaze.extend(ROT.Map); - -ROT.Map.EllerMaze.prototype.create = function(callback) { - var map = this._fillMap(1); - var w = Math.ceil((this._width-2)/2); - - var rand = 9/24; - - var L = []; - var R = []; - - for (var i=0;i rand) { - this._addToList(i, L, R); - map[x+1][y] = 0; - } - - /* bottom connection */ - if (i != L[i] && ROT.RNG.getUniform() > rand) { - /* remove connection */ - this._removeFromList(i, L, R); - } else { - /* create connection */ - map[x][y+1] = 0; - } - } - } - - /* last row */ - for (var i=0;i rand)) { - /* dig right also if the cell is separated, so it gets connected to the rest of maze */ - this._addToList(i, L, R); - map[x+1][y] = 0; - } - - this._removeFromList(i, L, R); - } - - for (var i=0;i= this._width || x < 0 || y >= this._width) { continue; } - result += (this._map[x][y] == 1 ? 1 : 0); - } - - return result; -} -/** - * @class Dungeon map: has rooms and corridors - * @augments ROT.Map - */ -ROT.Map.Dungeon = function(width, height) { - ROT.Map.call(this, width, height); - this._rooms = []; /* list of all rooms */ - this._corridors = []; -} -ROT.Map.Dungeon.extend(ROT.Map); - -/** - * Get all generated rooms - * @returns {ROT.Map.Feature.Room[]} - */ -ROT.Map.Dungeon.prototype.getRooms = function() { - return this._rooms; -} - -/** - * Get all generated corridors - * @returns {ROT.Map.Feature.Corridor[]} - */ -ROT.Map.Dungeon.prototype.getCorridors = function() { - return this._corridors; -} -/** - * @class Random dungeon generator using human-like digging patterns. - * Heavily based on Mike Anderson's ideas from the "Tyrant" algo, mentioned at - * http://www.roguebasin.roguelikedevelopment.org/index.php?title=Dungeon-Building_Algorithm. - * @augments ROT.Map.Dungeon - */ -ROT.Map.Digger = function(width, height, options) { - ROT.Map.Dungeon.call(this, width, height); - - this._options = { - roomWidth: [3, 9], /* room minimum and maximum width */ - roomHeight: [3, 5], /* room minimum and maximum height */ - corridorLength: [3, 10], /* corridor minimum and maximum length */ - dugPercentage: 0.2, /* we stop after this percentage of level area has been dug out */ - timeLimit: 1000 /* we stop after this much time has passed (msec) */ - } - for (var p in options) { this._options[p] = options[p]; } - - this._features = { - "Room": 4, - "Corridor": 4 - } - this._featureAttempts = 20; /* how many times do we try to create a feature on a suitable wall */ - this._walls = {}; /* these are available for digging */ - - this._digCallback = this._digCallback.bind(this); - this._canBeDugCallback = this._canBeDugCallback.bind(this); - this._isWallCallback = this._isWallCallback.bind(this); - this._priorityWallCallback = this._priorityWallCallback.bind(this); -} -ROT.Map.Digger.extend(ROT.Map.Dungeon); - -/** - * Create a map - * @see ROT.Map#create - */ -ROT.Map.Digger.prototype.create = function(callback) { - this._rooms = []; - this._corridors = []; - this._map = this._fillMap(1); - this._walls = {}; - this._dug = 0; - var area = (this._width-2) * (this._height-2); - - this._firstRoom(); - - var t1 = Date.now(); - - do { - var t2 = Date.now(); - if (t2 - t1 > this._options.timeLimit) { break; } - - /* find a good wall */ - var wall = this._findWall(); - if (!wall) { break; } /* no more walls */ - - var parts = wall.split(","); - var x = parseInt(parts[0]); - var y = parseInt(parts[1]); - var dir = this._getDiggingDirection(x, y); - if (!dir) { continue; } /* this wall is not suitable */ - -// console.log("wall", x, y); - - /* try adding a feature */ - var featureAttempts = 0; - do { - featureAttempts++; - if (this._tryFeature(x, y, dir[0], dir[1])) { /* feature added */ - //if (this._rooms.length + this._corridors.length == 2) { this._rooms[0].addDoor(x, y); } /* first room oficially has doors */ - this._removeSurroundingWalls(x, y); - this._removeSurroundingWalls(x-dir[0], y-dir[1]); - break; - } - } while (featureAttempts < this._featureAttempts); - - var priorityWalls = 0; - for (var id in this._walls) { - if (this._walls[id] > 1) { priorityWalls++; } - } - - } while (this._dug/area < this._options.dugPercentage || priorityWalls); /* fixme number of priority walls */ - - this._addDoors(); - - if (callback) { - for (var i=0;i= this._width || y >= this._height) { return false; } - return (this._map[x][y] == 1); -} - -ROT.Map.Digger.prototype._canBeDugCallback = function(x, y) { - if (x < 1 || y < 1 || x+1 >= this._width || y+1 >= this._height) { return false; } - return (this._map[x][y] == 1); -} - -ROT.Map.Digger.prototype._priorityWallCallback = function(x, y) { - this._walls[x+","+y] = 2; -} - -ROT.Map.Digger.prototype._firstRoom = function() { - var cx = Math.floor(this._width/2); - var cy = Math.floor(this._height/2); - var room = ROT.Map.Feature.Room.createRandomCenter(cx, cy, this._options); - this._rooms.push(room); - room.create(this._digCallback); -} - -/** - * Get a suitable wall - */ -ROT.Map.Digger.prototype._findWall = function() { - var prio1 = []; - var prio2 = []; - for (var id in this._walls) { - var prio = this._walls[id]; - if (prio == 2) { - prio2.push(id); - } else { - prio1.push(id); - } - } - - var arr = (prio2.length ? prio2 : prio1); - if (!arr.length) { return null; } /* no walls :/ */ - - var id = arr.random(); - delete this._walls[id]; - - return id; -} - -/** - * Tries adding a feature - * @returns {bool} was this a successful try? - */ -ROT.Map.Digger.prototype._tryFeature = function(x, y, dx, dy) { - var feature = ROT.RNG.getWeightedValue(this._features); - feature = ROT.Map.Feature[feature].createRandomAt(x, y, dx, dy, this._options); - - if (!feature.isValid(this._isWallCallback, this._canBeDugCallback)) { -// console.log("not valid"); -// feature.debug(); - return false; - } - - feature.create(this._digCallback); -// feature.debug(); - - if (feature instanceof ROT.Map.Feature.Room) { this._rooms.push(feature); } - if (feature instanceof ROT.Map.Feature.Corridor) { - feature.createPriorityWalls(this._priorityWallCallback); - this._corridors.push(feature); - } - - return true; -} - -ROT.Map.Digger.prototype._removeSurroundingWalls = function(cx, cy) { - var deltas = ROT.DIRS[4]; - - for (var i=0;i= this._width || y >= this._width) { return null; } - - if (!this._map[x][y]) { /* there already is another empty neighbor! */ - if (result) { return null; } - result = delta; - } - } - - /* no empty neighbor */ - if (!result) { return null; } - - return [-result[0], -result[1]]; -} - -/** - * Find empty spaces surrounding rooms, and apply doors. - */ -ROT.Map.Digger.prototype._addDoors = function() { - var data = this._map; - var isWallCallback = function(x, y) { - return (data[x][y] == 1); - } - for (var i = 0; i < this._rooms.length; i++ ) { - var room = this._rooms[i]; - room.clearDoors(); - room.addDoors(isWallCallback); - } -} -/** - * @class Dungeon generator which tries to fill the space evenly. Generates independent rooms and tries to connect them. - * @augments ROT.Map.Dungeon - */ -ROT.Map.Uniform = function(width, height, options) { - ROT.Map.Dungeon.call(this, width, height); - - this._options = { - roomWidth: [3, 9], /* room minimum and maximum width */ - roomHeight: [3, 5], /* room minimum and maximum height */ - roomDugPercentage: 0.1, /* we stop after this percentage of level area has been dug out by rooms */ - timeLimit: 1000 /* we stop after this much time has passed (msec) */ - } - for (var p in options) { this._options[p] = options[p]; } - - this._roomAttempts = 20; /* new room is created N-times until is considered as impossible to generate */ - this._corridorAttempts = 20; /* corridors are tried N-times until the level is considered as impossible to connect */ - - this._connected = []; /* list of already connected rooms */ - this._unconnected = []; /* list of remaining unconnected rooms */ - - this._digCallback = this._digCallback.bind(this); - this._canBeDugCallback = this._canBeDugCallback.bind(this); - this._isWallCallback = this._isWallCallback.bind(this); -} -ROT.Map.Uniform.extend(ROT.Map.Dungeon); - -/** - * Create a map. If the time limit has been hit, returns null. - * @see ROT.Map#create - */ -ROT.Map.Uniform.prototype.create = function(callback) { - var t1 = Date.now(); - while (1) { - var t2 = Date.now(); - if (t2 - t1 > this._options.timeLimit) { return null; } /* time limit! */ - - this._map = this._fillMap(1); - this._dug = 0; - this._rooms = []; - this._unconnected = []; - this._generateRooms(); - if (this._generateCorridors()) { break; } - } - - if (callback) { - for (var i=0;i this._options.roomDugPercentage) { break; } /* achieved requested amount of free space */ - } while (room); - - /* either enough rooms, or not able to generate more of them :) */ -} - -/** - * Try to generate one room - */ -ROT.Map.Uniform.prototype._generateRoom = function() { - var count = 0; - while (count < this._roomAttempts) { - count++; - - var room = ROT.Map.Feature.Room.createRandom(this._width, this._height, this._options); - if (!room.isValid(this._isWallCallback, this._canBeDugCallback)) { continue; } - - room.create(this._digCallback); - this._rooms.push(room); - return room; - } - - /* no room was generated in a given number of attempts */ - return null; -} - -/** - * Generates connectors beween rooms - * @returns {bool} success Was this attempt successfull? - */ -ROT.Map.Uniform.prototype._generateCorridors = function() { - var cnt = 0; - while (cnt < this._corridorAttempts) { - cnt++; - this._corridors = []; - - /* dig rooms into a clear map */ - this._map = this._fillMap(1); - for (var i=0;i 0 ? 2 : 0); - var dirIndex2 = (dirIndex1 + 2) % 4; - var min = room2.getLeft(); - var max = room2.getRight(); - var index = 0; - } else { /* first try connecting east-west walls */ - var dirIndex1 = (diffX > 0 ? 1 : 3); - var dirIndex2 = (dirIndex1 + 2) % 4; - var min = room2.getTop(); - var max = room2.getBottom(); - var index = 1; - } - - var start = this._placeInWall(room1, dirIndex1); /* corridor will start here */ - if (!start) { return false; } - - if (start[index] >= min && start[index] <= max) { /* possible to connect with straight line (I-like) */ - var end = start.slice(); - var value = null; - switch (dirIndex2) { - case 0: value = room2.getTop()-1; break; - case 1: value = room2.getRight()+1; break; - case 2: value = room2.getBottom()+1; break; - case 3: value = room2.getLeft()-1; break; - } - end[(index+1)%2] = value; - this._digLine([start, end]); - - } else if (start[index] < min-1 || start[index] > max+1) { /* need to switch target wall (L-like) */ - - var diff = start[index] - center2[index]; - switch (dirIndex2) { - case 0: - case 1: var rotation = (diff < 0 ? 3 : 1); break; - case 2: - case 3: var rotation = (diff < 0 ? 1 : 3); break; - } - dirIndex2 = (dirIndex2 + rotation) % 4; - - var end = this._placeInWall(room2, dirIndex2); - if (!end) { return false; } - - var mid = [0, 0]; - mid[index] = start[index]; - var index2 = (index+1)%2; - mid[index2] = end[index2]; - this._digLine([start, mid, end]); - - } else { /* use current wall pair, but adjust the line in the middle (S-like) */ - - var index2 = (index+1)%2; - var end = this._placeInWall(room2, dirIndex2); - if (!end) { return; } - var mid = Math.round((end[index2] + start[index2])/2); - - var mid1 = [0, 0]; - var mid2 = [0, 0]; - mid1[index] = start[index]; - mid1[index2] = mid; - mid2[index] = end[index]; - mid2[index2] = mid; - this._digLine([start, mid1, mid2, end]); - } - - room1.addDoor(start[0], start[1]); - room2.addDoor(end[0], end[1]); - - var index = this._unconnected.indexOf(room1); - if (index != -1) { - this._unconnected.splice(index, 1); - this._connected.push(room1); - } - - var index = this._unconnected.indexOf(room2); - if (index != -1) { - this._unconnected.splice(index, 1); - this._connected.push(room2); - } - - return true; -} - -ROT.Map.Uniform.prototype._placeInWall = function(room, dirIndex) { - var start = [0, 0]; - var dir = [0, 0]; - var length = 0; - - switch (dirIndex) { - case 0: - dir = [1, 0]; - start = [room.getLeft(), room.getTop()-1]; - length = room.getRight()-room.getLeft()+1; - break; - case 1: - dir = [0, 1]; - start = [room.getRight()+1, room.getTop()]; - length = room.getBottom()-room.getTop()+1; - break; - case 2: - dir = [1, 0]; - start = [room.getLeft(), room.getBottom()+1]; - length = room.getRight()-room.getLeft()+1; - break; - case 3: - dir = [0, 1]; - start = [room.getLeft()-1, room.getTop()]; - length = room.getBottom()-room.getTop()+1; - break; - } - - var avail = []; - var lastBadIndex = -2; - - for (var i=0;i=0; i--) { - if (!avail[i]) { avail.splice(i, 1); } - } - return (avail.length ? avail.random() : null); -} - -/** - * Dig a polyline. - */ -ROT.Map.Uniform.prototype._digLine = function(points) { - for (var i=1;i= this._width || y >= this._height) { return false; } - return (this._map[x][y] == 1); -} - -ROT.Map.Uniform.prototype._canBeDugCallback = function(x, y) { - if (x < 1 || y < 1 || x+1 >= this._width || y+1 >= this._height) { return false; } - return (this._map[x][y] == 1); -} - -/** - * @author hyakugei - * @class Dungeon generator which uses the "orginal" Rogue dungeon generation algorithm. See http://kuoi.com/~kamikaze/GameDesign/art07_rogue_dungeon.php - * @augments ROT.Map - * @param {int} [width=ROT.DEFAULT_WIDTH] - * @param {int} [height=ROT.DEFAULT_HEIGHT] - * @param {object} [options] Options - * @param {int[]} [options.cellWidth=3] Number of cells to create on the horizontal (number of rooms horizontally) - * @param {int[]} [options.cellHeight=3] Number of cells to create on the vertical (number of rooms vertically) - * @param {int} [options.roomWidth] Room min and max width - normally set auto-magically via the constructor. - * @param {int} [options.roomHeight] Room min and max height - normally set auto-magically via the constructor. - */ -ROT.Map.Rogue = function(width, height, options) { - ROT.Map.call(this, width, height); - - this._options = { - cellWidth: 3, // NOTE to self, these could probably work the same as the roomWidth/room Height values - cellHeight: 3 // ie. as an array with min-max values for each direction.... - } - - for (var p in options) { this._options[p] = options[p]; } - - /* - Set the room sizes according to the over-all width of the map, - and the cell sizes. - */ - - if (!this._options.hasOwnProperty("roomWidth")) { - this._options["roomWidth"] = this._calculateRoomSize(width, this._options["cellWidth"]); - } - if (!this._options.hasOwnProperty["roomHeight"]) { - this._options["roomHeight"] = this._calculateRoomSize(height, this._options["cellHeight"]); - } - -} - -ROT.Map.Rogue.extend(ROT.Map); - -/** - * @see ROT.Map#create - */ -ROT.Map.Rogue.prototype.create = function(callback) { - this.map = this._fillMap(1); - this.rooms = []; - this.connectedCells = []; - - this._initRooms(); - this._connectRooms(); - this._connectUnconnectedRooms(); - this._createRandomRoomConnections(); - this._createRooms(); - this._createCorridors(); - - if (callback) { - for (var i = 0; i < this._width; i++) { - for (var j = 0; j < this._height; j++) { - callback(i, j, this.map[i][j]); - } - } - } - - return this; -} - -ROT.Map.Rogue.prototype._getRandomInt = function(min, max) { - return Math.floor(ROT.RNG.getUniform() * (max - min + 1)) + min; -} - -ROT.Map.Rogue.prototype._calculateRoomSize = function(size, cell) { - var max = Math.floor((size/cell) * 0.8); - var min = Math.floor((size/cell) * 0.25); - if (min < 2) min = 2; - if (max < 2) max = 2; - return [min, max]; -} - -ROT.Map.Rogue.prototype._initRooms = function () { - // create rooms array. This is the "grid" list from the algo. - for (var i = 0; i < this._options.cellWidth; i++) { - this.rooms.push([]); - for(var j = 0; j < this._options.cellHeight; j++) { - this.rooms[i].push({"x":0, "y":0, "width":0, "height":0, "connections":[], "cellx":i, "celly":j}); - } - } -} - -ROT.Map.Rogue.prototype._connectRooms = function() { - //pick random starting grid - var cgx = this._getRandomInt(0, this._options.cellWidth-1); - var cgy = this._getRandomInt(0, this._options.cellHeight-1); - - var idx; - var ncgx; - var ncgy; - - var found = false; - var room; - var otherRoom; - - // find unconnected neighbour cells - do { - - //var dirToCheck = [0,1,2,3,4,5,6,7]; - var dirToCheck = [0,2,4,6]; - dirToCheck = dirToCheck.randomize(); - - do { - found = false; - idx = dirToCheck.pop(); - - - ncgx = cgx + ROT.DIRS[8][idx][0]; - ncgy = cgy + ROT.DIRS[8][idx][1]; - - if(ncgx < 0 || ncgx >= this._options.cellWidth) continue; - if(ncgy < 0 || ncgy >= this._options.cellHeight) continue; - - room = this.rooms[cgx][cgy]; - - if(room["connections"].length > 0) - { - // as long as this room doesn't already coonect to me, we are ok with it. - if(room["connections"][0][0] == ncgx && - room["connections"][0][1] == ncgy) - { - break; - } - } - - otherRoom = this.rooms[ncgx][ncgy]; - - if (otherRoom["connections"].length == 0) { - otherRoom["connections"].push([cgx,cgy]); - - this.connectedCells.push([ncgx, ncgy]); - cgx = ncgx; - cgy = ncgy; - found = true; - } - - } while (dirToCheck.length > 0 && found == false) - - } while (dirToCheck.length > 0) - -} - -ROT.Map.Rogue.prototype._connectUnconnectedRooms = function() { - //While there are unconnected rooms, try to connect them to a random connected neighbor - //(if a room has no connected neighbors yet, just keep cycling, you'll fill out to it eventually). - var cw = this._options.cellWidth; - var ch = this._options.cellHeight; - - var randomConnectedCell; - this.connectedCells = this.connectedCells.randomize(); - var room; - var otherRoom; - var validRoom; - - for (var i = 0; i < this._options.cellWidth; i++) { - for (var j = 0; j < this._options.cellHeight; j++) { - - room = this.rooms[i][j]; - - if (room["connections"].length == 0) { - var directions = [0,2,4,6]; - directions = directions.randomize(); - - var validRoom = false; - - do { - - var dirIdx = directions.pop(); - var newI = i + ROT.DIRS[8][dirIdx][0]; - var newJ = j + ROT.DIRS[8][dirIdx][1]; - - if (newI < 0 || newI >= cw || - newJ < 0 || newJ >= ch) { - continue; - } - - otherRoom = this.rooms[newI][newJ]; - - validRoom = true; - - if (otherRoom["connections"].length == 0) { - break; - } - - for (var k = 0; k < otherRoom["connections"].length; k++) { - if(otherRoom["connections"][k][0] == i && - otherRoom["connections"][k][1] == j) { - validRoom = false; - break; - } - } - - if (validRoom) break; - - } while (directions.length) - - if(validRoom) { - room["connections"].push( [otherRoom["cellx"], otherRoom["celly"]] ); - } else { - console.log("-- Unable to connect room."); - } - } - } - } -} - -ROT.Map.Rogue.prototype._createRandomRoomConnections = function(connections) { - // Empty for now. -} - - -ROT.Map.Rogue.prototype._createRooms = function() { - // Create Rooms - - var w = this._width; - var h = this._height; - - var cw = this._options.cellWidth; - var ch = this._options.cellHeight; - - var cwp = Math.floor(this._width / cw); - var chp = Math.floor(this._height / ch); - - var roomw; - var roomh; - var roomWidth = this._options["roomWidth"]; - var roomHeight = this._options["roomHeight"]; - var sx; - var sy; - var tx; - var ty; - var otherRoom; - - for (var i = 0; i < cw; i++) { - for (var j = 0; j < ch; j++) { - sx = cwp * i; - sy = chp * j; - - if (sx == 0) sx = 1; - if (sy == 0) sy = 1; - - roomw = this._getRandomInt(roomWidth[0], roomWidth[1]); - roomh = this._getRandomInt(roomHeight[0], roomHeight[1]); - - if (j > 0) { - otherRoom = this.rooms[i][j-1]; - while (sy - (otherRoom["y"] + otherRoom["height"] ) < 3) { - sy++; - } - } - - if (i > 0) { - otherRoom = this.rooms[i-1][j]; - while(sx - (otherRoom["x"] + otherRoom["width"]) < 3) { - sx++; - } - } - - var sxOffset = Math.round(this._getRandomInt(0, cwp-roomw)/2); - var syOffset = Math.round(this._getRandomInt(0, chp-roomh)/2); - - while (sx + sxOffset + roomw >= w) { - if(sxOffset) { - sxOffset--; - } else { - roomw--; - } - } - - while (sy + syOffset + roomh >= h) { - if(syOffset) { - syOffset--; - } else { - roomh--; - } - } - - sx = sx + sxOffset; - sy = sy + syOffset; - - this.rooms[i][j]["x"] = sx; - this.rooms[i][j]["y"] = sy; - this.rooms[i][j]["width"] = roomw; - this.rooms[i][j]["height"] = roomh; - - for (var ii = sx; ii < sx + roomw; ii++) { - for (var jj = sy; jj < sy + roomh; jj++) { - this.map[ii][jj] = 0; - } - } - } - } -} - -ROT.Map.Rogue.prototype._getWallPosition = function(aRoom, aDirection) { - var rx; - var ry; - var door; - - if (aDirection == 1 || aDirection == 3) { - rx = this._getRandomInt(aRoom["x"] + 1, aRoom["x"] + aRoom["width"] - 2); - if (aDirection == 1) { - ry = aRoom["y"] - 2; - door = ry + 1; - } else { - ry = aRoom["y"] + aRoom["height"] + 1; - door = ry -1; - } - - this.map[rx][door] = 0; // i'm not setting a specific 'door' tile value right now, just empty space. - - } else if (aDirection == 2 || aDirection == 4) { - ry = this._getRandomInt(aRoom["y"] + 1, aRoom["y"] + aRoom["height"] - 2); - if(aDirection == 2) { - rx = aRoom["x"] + aRoom["width"] + 1; - door = rx - 1; - } else { - rx = aRoom["x"] - 2; - door = rx + 1; - } - - this.map[door][ry] = 0; // i'm not setting a specific 'door' tile value right now, just empty space. - - } - return [rx, ry]; -} - -/*** -* @param startPosition a 2 element array -* @param endPosition a 2 element array -*/ -ROT.Map.Rogue.prototype._drawCorridore = function (startPosition, endPosition) { - var xOffset = endPosition[0] - startPosition[0]; - var yOffset = endPosition[1] - startPosition[1]; - - var xpos = startPosition[0]; - var ypos = startPosition[1]; - - var tempDist; - var xDir; - var yDir; - - var move; // 2 element array, element 0 is the direction, element 1 is the total value to move. - var moves = []; // a list of 2 element arrays - - var xAbs = Math.abs(xOffset); - var yAbs = Math.abs(yOffset); - - var percent = ROT.RNG.getUniform(); // used to split the move at different places along the long axis - var firstHalf = percent; - var secondHalf = 1 - percent; - - xDir = xOffset > 0 ? 2 : 6; - yDir = yOffset > 0 ? 4 : 0; - - if (xAbs < yAbs) { - // move firstHalf of the y offset - tempDist = Math.ceil(yAbs * firstHalf); - moves.push([yDir, tempDist]); - // move all the x offset - moves.push([xDir, xAbs]); - // move sendHalf of the y offset - tempDist = Math.floor(yAbs * secondHalf); - moves.push([yDir, tempDist]); - } else { - // move firstHalf of the x offset - tempDist = Math.ceil(xAbs * firstHalf); - moves.push([xDir, tempDist]); - // move all the y offset - moves.push([yDir, yAbs]); - // move secondHalf of the x offset. - tempDist = Math.floor(xAbs * secondHalf); - moves.push([xDir, tempDist]); - } - - this.map[xpos][ypos] = 0; - - while (moves.length > 0) { - move = moves.pop(); - while (move[1] > 0) { - xpos += ROT.DIRS[8][move[0]][0]; - ypos += ROT.DIRS[8][move[0]][1]; - this.map[xpos][ypos] = 0; - move[1] = move[1] - 1; - } - } -} - -ROT.Map.Rogue.prototype._createCorridors = function () { - // Draw Corridors between connected rooms - - var cw = this._options.cellWidth; - var ch = this._options.cellHeight; - var room; - var connection; - var otherRoom; - var wall; - var otherWall; - - for (var i = 0; i < cw; i++) { - for (var j = 0; j < ch; j++) { - room = this.rooms[i][j]; - - for (var k = 0; k < room["connections"].length; k++) { - - connection = room["connections"][k]; - - otherRoom = this.rooms[connection[0]][connection[1]]; - - // figure out what wall our corridor will start one. - // figure out what wall our corridor will end on. - if (otherRoom["cellx"] > room["cellx"] ) { - wall = 2; - otherWall = 4; - } else if (otherRoom["cellx"] < room["cellx"] ) { - wall = 4; - otherWall = 2; - } else if(otherRoom["celly"] > room["celly"]) { - wall = 3; - otherWall = 1; - } else if(otherRoom["celly"] < room["celly"]) { - wall = 1; - otherWall = 3; - } - - this._drawCorridore(this._getWallPosition(room, wall), this._getWallPosition(otherRoom, otherWall)); - } - } - } -} -/** - * @class Dungeon feature; has own .create() method - */ -ROT.Map.Feature = function() {} -ROT.Map.Feature.prototype.isValid = function(canBeDugCallback) {} -ROT.Map.Feature.prototype.create = function(digCallback) {} -ROT.Map.Feature.prototype.debug = function() {} -ROT.Map.Feature.createRandomAt = function(x, y, dx, dy, options) {} - -/** - * @class Room - * @augments ROT.Map.Feature - * @param {int} x1 - * @param {int} y1 - * @param {int} x2 - * @param {int} y2 - * @param {int} [doorX] - * @param {int} [doorY] - */ -ROT.Map.Feature.Room = function(x1, y1, x2, y2, doorX, doorY) { - this._x1 = x1; - this._y1 = y1; - this._x2 = x2; - this._y2 = y2; - this._doors = {}; - if (arguments.length > 4) { this.addDoor(doorX, doorY); } -} -ROT.Map.Feature.Room.extend(ROT.Map.Feature); - -/** - * Room of random size, with a given doors and direction - */ -ROT.Map.Feature.Room.createRandomAt = function(x, y, dx, dy, options) { - var min = options.roomWidth[0]; - var max = options.roomWidth[1]; - var width = min + Math.floor(ROT.RNG.getUniform()*(max-min+1)); - - var min = options.roomHeight[0]; - var max = options.roomHeight[1]; - var height = min + Math.floor(ROT.RNG.getUniform()*(max-min+1)); - - if (dx == 1) { /* to the right */ - var y2 = y - Math.floor(ROT.RNG.getUniform() * height); - return new this(x+1, y2, x+width, y2+height-1, x, y); - } - - if (dx == -1) { /* to the left */ - var y2 = y - Math.floor(ROT.RNG.getUniform() * height); - return new this(x-width, y2, x-1, y2+height-1, x, y); - } - - if (dy == 1) { /* to the bottom */ - var x2 = x - Math.floor(ROT.RNG.getUniform() * width); - return new this(x2, y+1, x2+width-1, y+height, x, y); - } - - if (dy == -1) { /* to the top */ - var x2 = x - Math.floor(ROT.RNG.getUniform() * width); - return new this(x2, y-height, x2+width-1, y-1, x, y); - } -} - -/** - * Room of random size, positioned around center coords - */ -ROT.Map.Feature.Room.createRandomCenter = function(cx, cy, options) { - var min = options.roomWidth[0]; - var max = options.roomWidth[1]; - var width = min + Math.floor(ROT.RNG.getUniform()*(max-min+1)); - - var min = options.roomHeight[0]; - var max = options.roomHeight[1]; - var height = min + Math.floor(ROT.RNG.getUniform()*(max-min+1)); - - var x1 = cx - Math.floor(ROT.RNG.getUniform()*width); - var y1 = cy - Math.floor(ROT.RNG.getUniform()*height); - var x2 = x1 + width - 1; - var y2 = y1 + height - 1; - - return new this(x1, y1, x2, y2); -} - -/** - * Room of random size within a given dimensions - */ -ROT.Map.Feature.Room.createRandom = function(availWidth, availHeight, options) { - var min = options.roomWidth[0]; - var max = options.roomWidth[1]; - var width = min + Math.floor(ROT.RNG.getUniform()*(max-min+1)); - - var min = options.roomHeight[0]; - var max = options.roomHeight[1]; - var height = min + Math.floor(ROT.RNG.getUniform()*(max-min+1)); - - var left = availWidth - width - 1; - var top = availHeight - height - 1; - - var x1 = 1 + Math.floor(ROT.RNG.getUniform()*left); - var y1 = 1 + Math.floor(ROT.RNG.getUniform()*top); - var x2 = x1 + width - 1; - var y2 = y1 + height - 1; - - return new this(x1, y1, x2, y2); -} - -ROT.Map.Feature.Room.prototype.addDoor = function(x, y) { - this._doors[x+","+y] = 1; - return this; -} - -/** - * @param {function} - */ -ROT.Map.Feature.Room.prototype.getDoors = function(callback) { - for (var key in this._doors) { - var parts = key.split(","); - callback(parseInt(parts[0]), parseInt(parts[1])); - } - return this; -} - -ROT.Map.Feature.Room.prototype.clearDoors = function() { - this._doors = {}; - return this; -} - -ROT.Map.Feature.Room.prototype.addDoors = function(isWallCallback) { - var left = this._x1-1; - var right = this._x2+1; - var top = this._y1-1; - var bottom = this._y2+1; - - for (var x=left; x<=right; x++) { - for (var y=top; y<=bottom; y++) { - if (x != left && x != right && y != top && y != bottom) { continue; } - if (isWallCallback(x, y)) { continue; } - - this.addDoor(x, y); - } - } - - return this; -} - -ROT.Map.Feature.Room.prototype.debug = function() { - console.log("room", this._x1, this._y1, this._x2, this._y2); -} - -ROT.Map.Feature.Room.prototype.isValid = function(isWallCallback, canBeDugCallback) { - var left = this._x1-1; - var right = this._x2+1; - var top = this._y1-1; - var bottom = this._y2+1; - - for (var x=left; x<=right; x++) { - for (var y=top; y<=bottom; y++) { - if (x == left || x == right || y == top || y == bottom) { - if (!isWallCallback(x, y)) { return false; } - } else { - if (!canBeDugCallback(x, y)) { return false; } - } - } - } - - return true; -} - -/** - * @param {function} digCallback Dig callback with a signature (x, y, value). Values: 0 = empty, 1 = wall, 2 = door. Multiple doors are allowed. - */ -ROT.Map.Feature.Room.prototype.create = function(digCallback) { - var left = this._x1-1; - var right = this._x2+1; - var top = this._y1-1; - var bottom = this._y2+1; - - var value = 0; - for (var x=left; x<=right; x++) { - for (var y=top; y<=bottom; y++) { - if (x+","+y in this._doors) { - value = 2; - } else if (x == left || x == right || y == top || y == bottom) { - value = 1; - } else { - value = 0; - } - digCallback(x, y, value); - } - } -} - -ROT.Map.Feature.Room.prototype.getCenter = function() { - return [Math.round((this._x1 + this._x2)/2), Math.round((this._y1 + this._y2)/2)]; -} - -ROT.Map.Feature.Room.prototype.getLeft = function() { - return this._x1; -} - -ROT.Map.Feature.Room.prototype.getRight = function() { - return this._x2; -} - -ROT.Map.Feature.Room.prototype.getTop = function() { - return this._y1; -} - -ROT.Map.Feature.Room.prototype.getBottom = function() { - return this._y2; -} - -/** - * @class Corridor - * @augments ROT.Map.Feature - * @param {int} startX - * @param {int} startY - * @param {int} endX - * @param {int} endY - */ -ROT.Map.Feature.Corridor = function(startX, startY, endX, endY) { - this._startX = startX; - this._startY = startY; - this._endX = endX; - this._endY = endY; - this._endsWithAWall = true; -} -ROT.Map.Feature.Corridor.extend(ROT.Map.Feature); - -ROT.Map.Feature.Corridor.createRandomAt = function(x, y, dx, dy, options) { - var min = options.corridorLength[0]; - var max = options.corridorLength[1]; - var length = min + Math.floor(ROT.RNG.getUniform()*(max-min+1)); - - return new this(x, y, x + dx*length, y + dy*length); -} - -ROT.Map.Feature.Corridor.prototype.debug = function() { - console.log("corridor", this._startX, this._startY, this._endX, this._endY); -} - -ROT.Map.Feature.Corridor.prototype.isValid = function(isWallCallback, canBeDugCallback){ - var sx = this._startX; - var sy = this._startY; - var dx = this._endX-sx; - var dy = this._endY-sy; - var length = 1 + Math.max(Math.abs(dx), Math.abs(dy)); - - if (dx) { dx = dx/Math.abs(dx); } - if (dy) { dy = dy/Math.abs(dy); } - var nx = dy; - var ny = -dx; - - var ok = true; - for (var i=0; i y0) { - i1 = 1; - j1 = 0; - } else { // lower triangle, XY order: (0,0)->(1,0)->(1,1) - i1 = 0; - j1 = 1; - } // upper triangle, YX order: (0,0)->(0,1)->(1,1) - - // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and - // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where - // c = (3-sqrt(3))/6 - var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords - var y1 = y0 - j1 + G2; - var x2 = x0 - 1 + 2*G2; // Offsets for last corner in (x,y) unskewed coords - var y2 = y0 - 1 + 2*G2; - - // Work out the hashed gradient indices of the three simplex corners - var ii = i.mod(count); - var jj = j.mod(count); - - // Calculate the contribution from the three corners - var t0 = 0.5 - x0*x0 - y0*y0; - if (t0 >= 0) { - t0 *= t0; - gi = indexes[ii+perms[jj]]; - var grad = this._gradients[gi]; - n0 = t0 * t0 * (grad[0] * x0 + grad[1] * y0); - } - - var t1 = 0.5 - x1*x1 - y1*y1; - if (t1 >= 0) { - t1 *= t1; - gi = indexes[ii+i1+perms[jj+j1]]; - var grad = this._gradients[gi]; - n1 = t1 * t1 * (grad[0] * x1 + grad[1] * y1); - } - - var t2 = 0.5 - x2*x2 - y2*y2; - if (t2 >= 0) { - t2 *= t2; - gi = indexes[ii+1+perms[jj+1]]; - var grad = this._gradients[gi]; - n2 = t2 * t2 * (grad[0] * x2 + grad[1] * y2); - } - - // Add contributions from each corner to get the final noise value. - // The result is scaled to return values in the interval [-1,1]. - return 70 * (n0 + n1 + n2); -} -/** - * @class Abstract FOV algorithm - * @param {function} lightPassesCallback Does the light pass through x,y? - * @param {object} [options] - * @param {int} [options.topology=8] 4/6/8 - */ -ROT.FOV = function(lightPassesCallback, options) { - this._lightPasses = lightPassesCallback; - this._options = { - topology: 8 - } - for (var p in options) { this._options[p] = options[p]; } -}; - -/** - * Compute visibility - * @param {int} x - * @param {int} y - * @param {int} R Maximum visibility radius - * @param {function} callback - */ -ROT.FOV.prototype.compute = function(x, y, R, callback) {} - -/** - * Return all neighbors in a concentric ring - * @param {int} cx center-x - * @param {int} cy center-y - * @param {int} r range - */ -ROT.FOV.prototype._getCircle = function(cx, cy, r) { - var result = []; - var dirs, countFactor, startOffset; - - switch (this._options.topology) { - case 4: - countFactor = 1; - startOffset = [0, 1]; - dirs = [ - ROT.DIRS[8][7], - ROT.DIRS[8][1], - ROT.DIRS[8][3], - ROT.DIRS[8][5] - ] - break; - - case 6: - dirs = ROT.DIRS[6]; - countFactor = 1; - startOffset = [-1, 1]; - break; - - case 8: - dirs = ROT.DIRS[4]; - countFactor = 2; - startOffset = [-1, 1]; - break; - } - - /* starting neighbor */ - var x = cx + startOffset[0]*r; - var y = cy + startOffset[1]*r; - - /* circle */ - for (var i=0;i A2[0]) { /* split into two sub-arcs */ - var v1 = this._checkVisibility(A1, [A1[1], A1[1]], blocks, SHADOWS); - var v2 = this._checkVisibility([0, 1], A2, blocks, SHADOWS); - return (v1+v2)/2; - } - - /* index1: first shadow >= A1 */ - var index1 = 0, edge1 = false; - while (index1 < SHADOWS.length) { - var old = SHADOWS[index1]; - var diff = old[0]*A1[1] - A1[0]*old[1]; - if (diff >= 0) { /* old >= A1 */ - if (diff == 0 && !(index1 % 2)) { edge1 = true; } - break; - } - index1++; - } - - /* index2: last shadow <= A2 */ - var index2 = SHADOWS.length, edge2 = false; - while (index2--) { - var old = SHADOWS[index2]; - var diff = A2[0]*old[1] - old[0]*A2[1]; - if (diff >= 0) { /* old <= A2 */ - if (diff == 0 && (index2 % 2)) { edge2 = true; } - break; - } - } - - var visible = true; - if (index1 == index2 && (edge1 || edge2)) { /* subset of existing shadow, one of the edges match */ - visible = false; - } else if (edge1 && edge2 && index1+1==index2 && (index2 % 2)) { /* completely equivalent with existing shadow */ - visible = false; - } else if (index1 > index2 && (index1 % 2)) { /* subset of existing shadow, not touching */ - visible = false; - } - - if (!visible) { return 0; } /* fast case: not visible */ - - var visibleLength, P; - - /* compute the length of visible arc, adjust list of shadows (if blocking) */ - var remove = index2-index1+1; - if (remove % 2) { - if (index1 % 2) { /* first edge within existing shadow, second outside */ - var P = SHADOWS[index1]; - visibleLength = (A2[0]*P[1] - P[0]*A2[1]) / (P[1] * A2[1]); - if (blocks) { SHADOWS.splice(index1, remove, A2); } - } else { /* second edge within existing shadow, first outside */ - var P = SHADOWS[index2]; - visibleLength = (P[0]*A1[1] - A1[0]*P[1]) / (A1[1] * P[1]); - if (blocks) { SHADOWS.splice(index1, remove, A1); } - } - } else { - if (index1 % 2) { /* both edges within existing shadows */ - var P1 = SHADOWS[index1]; - var P2 = SHADOWS[index2]; - visibleLength = (P2[0]*P1[1] - P1[0]*P2[1]) / (P1[1] * P2[1]); - if (blocks) { SHADOWS.splice(index1, remove); } - } else { /* both edges outside existing shadows */ - if (blocks) { SHADOWS.splice(index1, remove, A1, A2); } - return 1; /* whole arc visible! */ - } - } - - var arcLength = (A2[0]*A1[1] - A1[0]*A2[1]) / (A1[1] * A2[1]); - - return visibleLength/arcLength; -} -/** - * @namespace Color operations - */ -ROT.Color = { - fromString: function(str) { - var cached, r; - if (str in this._cache) { - cached = this._cache[str]; - } else { - if (str.charAt(0) == "#") { /* hex rgb */ - - var values = str.match(/[0-9a-f]/gi).map(function(x) { return parseInt(x, 16); }); - if (values.length == 3) { - cached = values.map(function(x) { return x*17; }); - } else { - for (var i=0;i<3;i++) { - values[i+1] += 16*values[i]; - values.splice(i, 1); - } - cached = values; - } - - } else if (r = str.match(/rgb\(([0-9, ]+)\)/i)) { /* decimal rgb */ - cached = r[1].split(/\s*,\s*/).map(function(x) { return parseInt(x); }); - } else { /* html name */ - cached = [0, 0, 0]; - } - - this._cache[str] = cached; - } - - return cached.slice(); - }, - - /** - * Add two or more colors - * @param {number[]} color1 - * @param {number[]} color2 - * @returns {number[]} - */ - add: function(color1, color2) { - var result = color1.slice(); - for (var i=0;i<3;i++) { - for (var j=1;j 0.5 ? d / (2 - max - min) : d / (max + min)); - switch(max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - - return [h, s, l]; - }, - - /** - * Converts an HSL color value to RGB. Expects 0..1 inputs, produces 0..255 outputs. - * @param {number[]} color - * @returns {number[]} - */ - hsl2rgb: function(color) { - var l = color[2]; - - if (color[1] == 0) { - l *= 255; - return [l, l, l]; - } else { - function hue2rgb(p, q, t) { - if (t < 0) t += 1; - if (t > 1) t -= 1; - if (t < 1/6) return p + (q - p) * 6 * t; - if (t < 1/2) return q; - if (t < 2/3) return p + (q - p) * (2/3 - t) * 6; - return p; - } - - var s = color[1]; - var q = (l < 0.5 ? l * (1 + s) : l + s - l * s); - var p = 2 * l - q; - var r = hue2rgb(p, q, color[0] + 1/3); - var g = hue2rgb(p, q, color[0]); - var b = hue2rgb(p, q, color[0] - 1/3); - return [Math.round(r*255), Math.round(g*255), Math.round(b*255)]; - } - }, - - toRGB: function(color) { - return "rgb(" + this._clamp(color[0]) + "," + this._clamp(color[1]) + "," + this._clamp(color[2]) + ")"; - }, - - toHex: function(color) { - var parts = []; - for (var i=0;i<3;i++) { - parts.push(this._clamp(color[i]).toString(16).lpad("0", 2)); - } - return "#" + parts.join(""); - }, - - _clamp: function(num) { - if (num < 0) { - return 0; - } else if (num > 255) { - return 255; - } else { - return num; - } - }, - - _cache: { - "black": [0,0,0], - "navy": [0,0,128], - "darkblue": [0,0,139], - "mediumblue": [0,0,205], - "blue": [0,0,255], - "darkgreen": [0,100,0], - "green": [0,128,0], - "teal": [0,128,128], - "darkcyan": [0,139,139], - "deepskyblue": [0,191,255], - "darkturquoise": [0,206,209], - "mediumspringgreen": [0,250,154], - "lime": [0,255,0], - "springgreen": [0,255,127], - "aqua": [0,255,255], - "cyan": [0,255,255], - "midnightblue": [25,25,112], - "dodgerblue": [30,144,255], - "forestgreen": [34,139,34], - "seagreen": [46,139,87], - "darkslategray": [47,79,79], - "darkslategrey": [47,79,79], - "limegreen": [50,205,50], - "mediumseagreen": [60,179,113], - "turquoise": [64,224,208], - "royalblue": [65,105,225], - "steelblue": [70,130,180], - "darkslateblue": [72,61,139], - "mediumturquoise": [72,209,204], - "indigo": [75,0,130], - "darkolivegreen": [85,107,47], - "cadetblue": [95,158,160], - "cornflowerblue": [100,149,237], - "mediumaquamarine": [102,205,170], - "dimgray": [105,105,105], - "dimgrey": [105,105,105], - "slateblue": [106,90,205], - "olivedrab": [107,142,35], - "slategray": [112,128,144], - "slategrey": [112,128,144], - "lightslategray": [119,136,153], - "lightslategrey": [119,136,153], - "mediumslateblue": [123,104,238], - "lawngreen": [124,252,0], - "chartreuse": [127,255,0], - "aquamarine": [127,255,212], - "maroon": [128,0,0], - "purple": [128,0,128], - "olive": [128,128,0], - "gray": [128,128,128], - "grey": [128,128,128], - "skyblue": [135,206,235], - "lightskyblue": [135,206,250], - "blueviolet": [138,43,226], - "darkred": [139,0,0], - "darkmagenta": [139,0,139], - "saddlebrown": [139,69,19], - "darkseagreen": [143,188,143], - "lightgreen": [144,238,144], - "mediumpurple": [147,112,216], - "darkviolet": [148,0,211], - "palegreen": [152,251,152], - "darkorchid": [153,50,204], - "yellowgreen": [154,205,50], - "sienna": [160,82,45], - "brown": [165,42,42], - "darkgray": [169,169,169], - "darkgrey": [169,169,169], - "lightblue": [173,216,230], - "greenyellow": [173,255,47], - "paleturquoise": [175,238,238], - "lightsteelblue": [176,196,222], - "powderblue": [176,224,230], - "firebrick": [178,34,34], - "darkgoldenrod": [184,134,11], - "mediumorchid": [186,85,211], - "rosybrown": [188,143,143], - "darkkhaki": [189,183,107], - "silver": [192,192,192], - "mediumvioletred": [199,21,133], - "indianred": [205,92,92], - "peru": [205,133,63], - "chocolate": [210,105,30], - "tan": [210,180,140], - "lightgray": [211,211,211], - "lightgrey": [211,211,211], - "palevioletred": [216,112,147], - "thistle": [216,191,216], - "orchid": [218,112,214], - "goldenrod": [218,165,32], - "crimson": [220,20,60], - "gainsboro": [220,220,220], - "plum": [221,160,221], - "burlywood": [222,184,135], - "lightcyan": [224,255,255], - "lavender": [230,230,250], - "darksalmon": [233,150,122], - "violet": [238,130,238], - "palegoldenrod": [238,232,170], - "lightcoral": [240,128,128], - "khaki": [240,230,140], - "aliceblue": [240,248,255], - "honeydew": [240,255,240], - "azure": [240,255,255], - "sandybrown": [244,164,96], - "wheat": [245,222,179], - "beige": [245,245,220], - "whitesmoke": [245,245,245], - "mintcream": [245,255,250], - "ghostwhite": [248,248,255], - "salmon": [250,128,114], - "antiquewhite": [250,235,215], - "linen": [250,240,230], - "lightgoldenrodyellow": [250,250,210], - "oldlace": [253,245,230], - "red": [255,0,0], - "fuchsia": [255,0,255], - "magenta": [255,0,255], - "deeppink": [255,20,147], - "orangered": [255,69,0], - "tomato": [255,99,71], - "hotpink": [255,105,180], - "coral": [255,127,80], - "darkorange": [255,140,0], - "lightsalmon": [255,160,122], - "orange": [255,165,0], - "lightpink": [255,182,193], - "pink": [255,192,203], - "gold": [255,215,0], - "peachpuff": [255,218,185], - "navajowhite": [255,222,173], - "moccasin": [255,228,181], - "bisque": [255,228,196], - "mistyrose": [255,228,225], - "blanchedalmond": [255,235,205], - "papayawhip": [255,239,213], - "lavenderblush": [255,240,245], - "seashell": [255,245,238], - "cornsilk": [255,248,220], - "lemonchiffon": [255,250,205], - "floralwhite": [255,250,240], - "snow": [255,250,250], - "yellow": [255,255,0], - "lightyellow": [255,255,224], - "ivory": [255,255,240], - "white": [255,255,255] - } -} -/** - * @class Lighting computation, based on a traditional FOV for multiple light sources and multiple passes. - * @param {function} reflectivityCallback Callback to retrieve cell reflectivity (0..1) - * @param {object} [options] - * @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. - * @param {int} [options.emissionThreshold=100] Cells with emissivity > threshold will be treated as light source in the next pass. - * @param {int} [options.range=10] Max light range - */ -ROT.Lighting = function(reflectivityCallback, options) { - this._reflectivityCallback = reflectivityCallback; - this._options = { - passes: 1, - emissionThreshold: 100, - range: 10 - }; - this._fov = null; - - this._lights = {}; - this._reflectivityCache = {}; - this._fovCache = {}; - - this.setOptions(options); -} - -/** - * Adjust options at runtime - * @see ROT.Lighting - * @param {object} [options] - */ -ROT.Lighting.prototype.setOptions = function(options) { - for (var p in options) { this._options[p] = options[p]; } - if (options.range) { this.reset(); } - return this; -} - -/** - * Set the used Field-Of-View algo - * @param {ROT.FOV} fov - */ -ROT.Lighting.prototype.setFOV = function(fov) { - this._fov = fov; - this._fovCache = {}; - return this; -} - -/** - * Set (or remove) a light source - * @param {int} x - * @param {int} y - * @param {null || string || number[3]} color - */ -ROT.Lighting.prototype.setLight = function(x, y, color) { - var key = x+","+y; - - if (color) { - this._lights[key] = (typeof(color) == "string" ? ROT.Color.fromString(color) : color); - } else { - delete this._lights[key]; - } - return this; -} - -/** - * Reset the pre-computed topology values. Call whenever the underlying map changes its light-passability. - */ -ROT.Lighting.prototype.reset = function() { - this._reflectivityCache = {}; - this._fovCache = {}; - - return this; -} - -/** - * Compute the lighting - * @param {function} lightingCallback Will be called with (x, y, color) for every lit cell - */ -ROT.Lighting.prototype.compute = function(lightingCallback) { - var doneCells = {}; - var emittingCells = {}; - var litCells = {}; - - for (var key in this._lights) { /* prepare emitters for first pass */ - var light = this._lights[key]; - if (!(key in emittingCells)) { emittingCells[key] = [0, 0, 0]; } - - ROT.Color.add_(emittingCells[key], light); - } - - for (var i=0;i this._options.emissionThreshold) { result[key] = emission; } - } - - return result; -} - -/** - * Compute one iteration from one cell - * @param {int} x - * @param {int} y - * @param {number[]} color - * @param {object} litCells Cell data to by updated - */ -ROT.Lighting.prototype._emitLightFromCell = function(x, y, color, litCells) { - var key = x+","+y; - if (key in this._fovCache) { - var fov = this._fovCache[key]; - } else { - var fov = this._updateFOV(x, y); - } - - for (var fovKey in fov) { - var formFactor = fov[fovKey]; - - if (fovKey in litCells) { /* already lit */ - var result = litCells[fovKey]; - } else { /* newly lit */ - var result = [0, 0, 0]; - litCells[fovKey] = result; - } - - for (var i=0;i<3;i++) { result[i] += Math.round(color[i]*formFactor); } /* add light color */ - } - - return this; -} - -/** - * Compute FOV ("form factor") for a potential light source at [x,y] - * @param {int} x - * @param {int} y - * @returns {object} - */ -ROT.Lighting.prototype._updateFOV = function(x, y) { - var key1 = x+","+y; - var cache = {}; - this._fovCache[key1] = cache; - var range = this._options.range; - var cb = function(x, y, r, vis) { - var key2 = x+","+y; - var formFactor = vis * (1-r/range); - if (formFactor == 0) { return; } - cache[key2] = formFactor; - } - this._fov.compute(x, y, range, cb.bind(this)); - - return cache; -} -/** - * @class Abstract pathfinder - * @param {int} toX Target X coord - * @param {int} toY Target Y coord - * @param {function} passableCallback Callback to determine map passability - * @param {object} [options] - * @param {int} [options.topology=8] - */ -ROT.Path = function(toX, toY, passableCallback, options) { - this._toX = toX; - this._toY = toY; - this._fromX = null; - this._fromY = null; - this._passableCallback = passableCallback; - this._options = { - topology: 8 - } - for (var p in options) { this._options[p] = options[p]; } - - this._dirs = ROT.DIRS[this._options.topology]; -} - -/** - * Compute a path from a given point - * @param {int} fromX - * @param {int} fromY - * @param {function} callback Will be called for every path item with arguments "x" and "y" - */ -ROT.Path.prototype.compute = function(fromX, fromY, callback) { -} - -ROT.Path.prototype._getNeighbors = function(cx, cy) { - var result = []; - for (var i=0;i - -test - - - - - - - \ No newline at end of file From d14790c084eea68d980cc37f55aff1ec33260945 Mon Sep 17 00:00:00 2001 From: mdtrooper Date: Tue, 20 Aug 2013 02:51:15 +0200 Subject: [PATCH 07/20] Add a small example to testing and check the progress. --- examples/test.html | 78 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 examples/test.html diff --git a/examples/test.html b/examples/test.html new file mode 100644 index 00000000..c09da2aa --- /dev/null +++ b/examples/test.html @@ -0,0 +1,78 @@ + + +test + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 7a7e92621169950b7a894fa171ad6ad802094a14 Mon Sep 17 00:00:00 2001 From: mdtrooper Date: Tue, 3 Sep 2013 01:32:46 +0200 Subject: [PATCH 08/20] More changes. --- src/image/image.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/image/image.js b/src/image/image.js index f627e259..e3b17ae5 100644 --- a/src/image/image.js +++ b/src/image/image.js @@ -5,14 +5,19 @@ ROT.Image = function() { this.aDefaultCharList = (" .,:;i1tfLCG08@").split(""); this.aDefaultColorCharList = (" CGO08@").split(""); this.strFont = "courier new"; + this.bBlock = true; + this.bAlpha = false; + this.bColor = true; + this.strResolution = "low"; this.ascii_art = ""; }; ROT.Image.prototype.loadASCII = function() { var aCharList = this.aDefaultCharList; - var bBlock = true; - var bAlpha = false; + var bBlock = this.bBlock; + var bAlpha = this.bAlpha; + var bColor = this.bColor; var oCanvas = document.createElement("canvas"); if (!oCanvas.getContext) { @@ -23,7 +28,7 @@ ROT.Image.prototype.loadASCII = function() { return; } - var strResolution = "low"; + var strResolution = this.strResolution; var fResolution = 0.5; @@ -72,15 +77,15 @@ ROT.Image.prototype.loadASCII = function() { if (strThisChar == " ") strThisChar = " "; - //~ if (bColor) { + if (bColor) { strChars += "" + strThisChar + ""; - //~ } else { - //~ strChars += strThisChar; - //~ } + } else { + strChars += strThisChar; + } } strChars += "
"; } From ea857af6d76e549329695e06edbc4eabfa4a81b2 Mon Sep 17 00:00:00 2001 From: mdtrooper Date: Wed, 4 Sep 2013 01:27:27 +0200 Subject: [PATCH 09/20] First beta version. There is a method to print a ascii art in rot!! --- src/image/image.js | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/image/image.js b/src/image/image.js index e3b17ae5..77a16041 100644 --- a/src/image/image.js +++ b/src/image/image.js @@ -11,6 +11,9 @@ ROT.Image = function() { this.strResolution = "low"; this.ascii_art = ""; + this.ascii_art_array = null; + this.height = 0; + this.width = 0; }; ROT.Image.prototype.loadASCII = function() { @@ -40,6 +43,8 @@ ROT.Image.prototype.loadASCII = function() { var iWidth = Math.round(parseInt(this.img.width) * fResolution); var iHeight = Math.round(parseInt(this.img.height) * fResolution); + this.width = iWidth; + this.height = iHeight; oCanvas.width = iWidth; oCanvas.height = iHeight; @@ -52,8 +57,10 @@ ROT.Image.prototype.loadASCII = function() { var oImgData = oCtx.getImageData(0, 0, iWidth, iHeight).data; var strChars = ""; + var array_chars = []; for (var y=0;y" + strThisChar + ""; + + array_chars[y][x] = {'r': iRed, 'g' : iGreen, 'b' : iBlue, 'char': unscape_char}; } else { strChars += strThisChar; + array_chars[y][x] = {'char': unscape_char}; } } strChars += "
"; } + this.ascii_art_array = array_chars; + return strChars; }; @@ -107,5 +120,31 @@ ROT.Image.prototype.load = function(image_url) { }; ROT.Image.prototype.get = function(xin, yin) { - console.log(888); + return this.ascii_art_array[yin][xin]; +}; + +ROT.Image.prototype.paint = function(display, offset_x, offset_y) { + var i = 0; + for (var y = 0; y < this.height; y += 2) { + var j = 0; + for (var x = 0; x < this.width; x++) { + if (this.bColor) { + var color = "#" + + this.ascii_art_array[y][x]['r'].toString(16) + + this.ascii_art_array[y][x]['g'].toString(16) + + this.ascii_art_array[y][x]['b'].toString(16); + if (this.bBlock) { + display.draw(offset_x + j, offset_y + i, this.ascii_art_array[y][x]['char'], color, color); + } + else { + display.draw(offset_x + j, offset_y + i, this.ascii_art_array[y][x]['char'], color); + } + } + else { + display.draw(offset_x + j, offset_y + i, this.ascii_art_array[y][x]['char']); + } + j++; + } + i++; + } }; \ No newline at end of file From dc2aac4d6d64327bc1520f1b385bab239eb2ce24 Mon Sep 17 00:00:00 2001 From: mdtrooper Date: Fri, 27 Sep 2013 02:22:07 +0200 Subject: [PATCH 10/20] It is a early version of image ascii art, there are blit functions. --- src/image/image.js | 43 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/src/image/image.js b/src/image/image.js index 77a16041..d70e9083 100644 --- a/src/image/image.js +++ b/src/image/image.js @@ -59,8 +59,9 @@ ROT.Image.prototype.loadASCII = function() { var strChars = ""; var array_chars = []; + var iterator_y = 0; for (var y=0;y" + strThisChar + ""; - array_chars[y][x] = {'r': iRed, 'g' : iGreen, 'b' : iBlue, 'char': unscape_char}; - } else { + array_chars[iterator_y][x] = {'r': iRed, 'g' : iGreen, 'b' : iBlue, 'char': unscape_char}; + } + else { strChars += strThisChar; - array_chars[y][x] = {'char': unscape_char}; + array_chars[iterator_y][x] = {'char': unscape_char}; } } + iterator_y++; strChars += "
"; } + this.height = iterator_y; + this.ascii_art_array = array_chars; return strChars; @@ -123,9 +128,37 @@ ROT.Image.prototype.get = function(xin, yin) { return this.ascii_art_array[yin][xin]; }; +ROT.Image.prototype.blit = function(display, display_x, display_y, image_x, image_y, rect_w, rect_h) { + var i = 0; + + for (var y = image_y; y < rect_h; y++) { + var j = 0; + for (var x = image_x; x < rect_w; x++) { + if (this.bColor) { + var color = "#" + + this.ascii_art_array[y][x]['r'].toString(16) + + this.ascii_art_array[y][x]['g'].toString(16) + + this.ascii_art_array[y][x]['b'].toString(16); + if (this.bBlock) { + display.draw(display_x + j, display_y + i, this.ascii_art_array[y][x]['char'], color, color); + } + else { + display.draw(display_x + j, display_y + i, this.ascii_art_array[y][x]['char'], color); + } + } + else { + display.draw(display_x + j, display_y + i, this.ascii_art_array[y][x]['char']); + } + j++; + } + i++; + } +}; + ROT.Image.prototype.paint = function(display, offset_x, offset_y) { var i = 0; - for (var y = 0; y < this.height; y += 2) { + + for (var y = 0; y < this.height; y++) { var j = 0; for (var x = 0; x < this.width; x++) { if (this.bColor) { From 64f31fed23adb2e74308ec58fd8f84b619a8b963 Mon Sep 17 00:00:00 2001 From: mdtrooper Date: Fri, 27 Sep 2013 02:26:52 +0200 Subject: [PATCH 11/20] Added in the comment where is the original library (jsAscii, author Jacob Seidelin) and remove the library. --- src/image/image.js | 7 ++ src/image/jsascii.js | 194 ------------------------------------------- 2 files changed, 7 insertions(+), 194 deletions(-) delete mode 100644 src/image/jsascii.js diff --git a/src/image/image.js b/src/image/image.js index d70e9083..4ddee283 100644 --- a/src/image/image.js +++ b/src/image/image.js @@ -1,6 +1,13 @@ /** * @class Base image (as ASCII art) converter */ + + //BASED IN THE WORK OF: + /* + * jsAscii 0.1 + * Copyright (c) 2008 Jacob Seidelin, jseidelin@nihilogic.dk, http://blog.nihilogic.dk/ + * MIT License [http://www.nihilogic.dk/licenses/mit-license.txt] + */ ROT.Image = function() { this.aDefaultCharList = (" .,:;i1tfLCG08@").split(""); this.aDefaultColorCharList = (" CGO08@").split(""); diff --git a/src/image/jsascii.js b/src/image/jsascii.js deleted file mode 100644 index 7af9886b..00000000 --- a/src/image/jsascii.js +++ /dev/null @@ -1,194 +0,0 @@ -/* - * jsAscii 0.1 - * Copyright (c) 2008 Jacob Seidelin, jseidelin@nihilogic.dk, http://blog.nihilogic.dk/ - * MIT License [http://www.nihilogic.dk/licenses/mit-license.txt] - */ - -var jsAscii = (function() { - - var aDefaultCharList = (" .,:;i1tfLCG08@").split(""); - var aDefaultColorCharList = (" CGO08@").split(""); - var strFont = "courier new"; - - - // convert img element to ascii - function asciifyImage(oImg, oCanvasImg) - { - var oCanvas = document.createElement("canvas"); - if (!oCanvas.getContext) { - return; - } - var oCtx = oCanvas.getContext("2d"); - if (!oCtx.getImageData) { - return; - } - - var iScale = parseInt(oImg.getAttribute("asciiscale")) || 1; - var bColor = (oImg.getAttribute("asciicolor") == "true"); - var bAlpha = (oImg.getAttribute("asciialpha") == "true"); - var bBlock = (oImg.getAttribute("asciiblock") == "true"); - var bInvert = (oImg.getAttribute("asciiinvert") == "true"); - var strResolution = oImg.getAttribute("asciiresolution") || "medium"; - var aCharList = oImg.getAttribute("asciichars") || - (bColor ? aDefaultColorCharList : aDefaultCharList); - - var fResolution = 0.5; - switch (strResolution) { - case "low" : fResolution = 0.25; break; - case "medium" : fResolution = 0.5; break; - case "high" : fResolution = 1; break; - } - - var iWidth = Math.round(parseInt(oImg.offsetWidth) * fResolution); - var iHeight = Math.round(parseInt(oImg.offsetHeight) * fResolution); - - oCanvas.width = iWidth; - oCanvas.height = iHeight; - oCanvas.style.display = "none"; - oCanvas.style.width = iWidth; - oCanvas.style.height = iHeight; - - oCtx.drawImage(oCanvasImg, 0, 0, iWidth, iHeight); - var oImgData = oCtx.getImageData(0, 0, iWidth, iHeight).data; - - var strChars = ""; - - for (var y=0;y" + strThisChar + ""; - } else { - strChars += strThisChar; - } - } - strChars += "
"; - } - - - var fFontSize = (2/fResolution)*iScale; - var fLineHeight = (2/fResolution)*iScale; - - // adjust letter-spacing for all combinations of scale and resolution to get it to fit the image width. - var fLetterSpacing = 0; - if (strResolution == "low") { - switch (iScale) { - case 1 : fLetterSpacing = -1; break; - case 2 : - case 3 : fLetterSpacing = -2.1; break; - case 4 : fLetterSpacing = -3.1; break; - case 5 : fLetterSpacing = -4.15; break; - } - } - if (strResolution == "medium") { - switch (iScale) { - case 1 : fLetterSpacing = 0; break; - case 2 : fLetterSpacing = -1; break; - case 3 : fLetterSpacing = -1.04; break; - case 4 : - case 5 : fLetterSpacing = -2.1; break; - } - } - if (strResolution == "high") { - switch (iScale) { - case 1 : - case 2 : fLetterSpacing = 0; break; - case 3 : - case 4 : - case 5 : fLetterSpacing = -1; break; - } - } - - - // can't get a span or div to flow like an img element, but a table works? - var oAscii = document.createElement("table"); - oAscii.innerHTML = "" + strChars + ""; - - if (oImg.style.backgroundColor) { - oAscii.rows[0].cells[0].style.backgroundColor = oImg.style.backgroundColor; - oAscii.rows[0].cells[0].style.color = oImg.style.color; - } - - oAscii.cellSpacing = 0; - oAscii.cellPadding = 0; - - var oStyle = oAscii.style; - oStyle.display = "inline"; - oStyle.width = Math.round(iWidth/fResolution*iScale) + "px"; - oStyle.height = Math.round(iHeight/fResolution*iScale) + "px"; - oStyle.whiteSpace = "pre"; - oStyle.margin = "0px"; - oStyle.padding = "0px"; - oStyle.letterSpacing = fLetterSpacing + "px"; - oStyle.fontFamily = strFont; - oStyle.fontSize = fFontSize + "px"; - oStyle.lineHeight = fLineHeight + "px"; - oStyle.textAlign = "left"; - oStyle.textDecoration = "none"; - - oImg.parentNode.replaceChild(oAscii, oImg); - - } - - // load the image file - function asciifyImageLoad(oImg) - { - var oCanvasImg = new Image(); - oCanvasImg.src = oImg.src; - if (oCanvasImg.complete) { - asciifyImage(oImg, oCanvasImg); - } else { - oCanvasImg.onload = function() { - asciifyImage(oImg, oCanvasImg) - } - } - } - - return function() { - var aImg = document.getElementsByTagName("img"); - var aImages = []; - for (var i=0;i Date: Fri, 27 Sep 2013 16:21:01 +0200 Subject: [PATCH 12/20] Added some "awesome" examples of image (ASCII art) class. --- examples/{test.html => combat.html} | 25 ++++------ examples/combat.png | Bin 0 -> 19756 bytes examples/src | 1 + examples/title.html | 69 ++++++++++++++++++++++++++++ examples/title.png | Bin 0 -> 21777 bytes src/image/image.js | 2 +- 6 files changed, 79 insertions(+), 18 deletions(-) rename examples/{test.html => combat.html} (87%) create mode 100644 examples/combat.png create mode 120000 examples/src create mode 100644 examples/title.html create mode 100644 examples/title.png diff --git a/examples/test.html b/examples/combat.html similarity index 87% rename from examples/test.html rename to examples/combat.html index c09da2aa..ea6eec96 100644 --- a/examples/test.html +++ b/examples/combat.html @@ -48,31 +48,22 @@ - \ No newline at end of file diff --git a/examples/combat.png b/examples/combat.png new file mode 100644 index 0000000000000000000000000000000000000000..286e34dea4430937272e9f8c36c94f9a9032d169 GIT binary patch literal 19756 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 BHH82G literal 0 HcmV?d00001 diff --git a/examples/src b/examples/src new file mode 120000 index 00000000..e057607e --- /dev/null +++ b/examples/src @@ -0,0 +1 @@ +../src/ \ No newline at end of file diff --git a/examples/title.html b/examples/title.html new file mode 100644 index 00000000..b82a2c66 --- /dev/null +++ b/examples/title.html @@ -0,0 +1,69 @@ + + +test + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/title.png b/examples/title.png new file mode 100644 index 0000000000000000000000000000000000000000..2b555d988b53d2fd068f97ab106bd3596ebd4555 GIT binary patch literal 21777 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

<_p;X)wT8^UJ+(1EQCDc%#@46;U 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 Date: Tue, 22 Apr 2014 00:39:22 +0200 Subject: [PATCH 13/20] Added a short explain about the ASCIIart support --- README.md | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4b9fe16d..5dc38c35 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,36 @@ -rot.js -====== +rot.js _(with ASCIIart support)_ +================================= -ROguelike Toolkit +**RO**guelike **T**oolkit More info: http://ondras.github.com/rot.js RogueBasin page (with links to some rot.js-based games): http://www.roguebasin.roguelikedevelopment.org/index.php?title=Rot.js + +ASCIIart support +---------------- +This fork has a support for [ASCIIart (link to wikipedia)](http://en.wikipedia.org/wiki/ASCII_art) for you use the image files in native format (jpg, png…) and the game shows a pretty ASCIIart. + +It is very easy to use. + +```javascript +var image = new ROT.Image(); + +image.strResolution = "medium"; +image.bBlock = false; +image.load("combat.png"); + +var display = new ROT.Display({width:w, height:h, fontSize:10}); + +image.paint(display, 0, 0); +``` + +The image methods are: +* load(url): get a image from url and convert to ASCIIart. +* blit(display, display_x, display_y, image_x, image_y, rect_w, rect_h): paint a part of ASCIIart into the display. +* paint(display, offset_x, offset_y): paint the entire ASCIIart into a position in the display. + +And the config attributes are: +* bBlock: for to paint blocks instead characters. +* bColor: ASCIIart in color or not. +* strResolution: (low, medium, high) the ASCII art resolution. From 2b7cafcfc91292123414cdb90c5476415c15f8b5 Mon Sep 17 00:00:00 2001 From: mdtrooper Date: Tue, 1 Nov 2016 20:21:16 +0100 Subject: [PATCH 14/20] Making a new js files. --- rot.js | 28 +++++++++-- rot.min.js | 144 ----------------------------------------------------- 2 files changed, 24 insertions(+), 148 deletions(-) delete mode 100644 rot.min.js diff --git a/rot.js b/rot.js index 25259921..aaea5e33 100644 --- a/rot.js +++ b/rot.js @@ -1,6 +1,6 @@ /* This is rot.js, the ROguelike Toolkit in JavaScript. - Version 0.7~dev, generated on Tue Aug 30 12:08:59 CEST 2016. + Version 0.7~dev, generated on mar nov 1 20:20:43 CET 2016. */ /** * @namespace Top-level ROT namespace @@ -1712,6 +1712,17 @@ ROT.EventQueue.prototype.get = function() { return this._events.splice(0, 1)[0]; } +/** + * Get the time associated with the given event + * @param {?} event + * @returns {number} time + */ +ROT.EventQueue.prototype.getEventTime = function(event) { + var index = this._events.indexOf(event); + if (index == -1) { return undefined } + return this._eventTimes[index]; +} + /** * Remove an event from the queue * @param {?} event @@ -1757,6 +1768,15 @@ ROT.Scheduler.prototype.add = function(item, repeat) { return this; } +/** + * Get the time the given item is scheduled for + * @param {?} item + * @returns {number} time + */ +ROT.Scheduler.prototype.getTimeOf = function(item) { + return this._queue.getEventTime(item); +} + /** * Clear all items */ @@ -1829,10 +1849,11 @@ ROT.Scheduler.Speed.extend(ROT.Scheduler); /** * @param {object} item anything with "getSpeed" method * @param {bool} repeat + * @param {number} [time=1/item.getSpeed()] * @see ROT.Scheduler#add */ -ROT.Scheduler.Speed.prototype.add = function(item, repeat) { - this._queue.add(item, 1/item.getSpeed()); +ROT.Scheduler.Speed.prototype.add = function(item, repeat, time) { + this._queue.add(item, time !== undefined ? time : 1/item.getSpeed()); return ROT.Scheduler.prototype.add.call(this, item, repeat); } @@ -2694,7 +2715,6 @@ ROT.Map.Digger.prototype.create = function(callback) { ROT.Map.Digger.prototype._digCallback = function(x, y, value) { if (value == 0 || value == 2) { /* empty */ - if (!this._map[x]) this._map[x]=[]; this._map[x][y] = 0; this._dug++; } else { /* wall */ diff --git a/rot.min.js b/rot.min.js deleted file mode 100644 index 5b853613..00000000 --- a/rot.min.js +++ /dev/null @@ -1,144 +0,0 @@ -var ROT={isSupported:function(){return!(!document.createElement("canvas").getContext||!Function.prototype.bind)},DEFAULT_WIDTH:80,DEFAULT_HEIGHT:25,DIRS:{4:[[0,-1],[1,0],[0,1],[-1,0]],8:[[0,-1],[1,-1],[1,0],[1,1],[0,1],[-1,1],[-1,0],[-1,-1]],6:[[-1,-1],[1,-1],[2,0],[1,1],[-1,1],[-2,0]]},VK_CANCEL:3,VK_HELP:6,VK_BACK_SPACE:8,VK_TAB:9,VK_CLEAR:12,VK_RETURN:13,VK_ENTER:14,VK_SHIFT:16,VK_CONTROL:17,VK_ALT:18,VK_PAUSE:19,VK_CAPS_LOCK:20,VK_ESCAPE:27,VK_SPACE:32,VK_PAGE_UP:33,VK_PAGE_DOWN:34,VK_END:35, -VK_HOME:36,VK_LEFT:37,VK_UP:38,VK_RIGHT:39,VK_DOWN:40,VK_PRINTSCREEN:44,VK_INSERT:45,VK_DELETE:46,VK_0:48,VK_1:49,VK_2:50,VK_3:51,VK_4:52,VK_5:53,VK_6:54,VK_7:55,VK_8:56,VK_9:57,VK_COLON:58,VK_SEMICOLON:59,VK_LESS_THAN:60,VK_EQUALS:61,VK_GREATER_THAN:62,VK_QUESTION_MARK:63,VK_AT:64,VK_A:65,VK_B:66,VK_C:67,VK_D:68,VK_E:69,VK_F:70,VK_G:71,VK_H:72,VK_I:73,VK_J:74,VK_K:75,VK_L:76,VK_M:77,VK_N:78,VK_O:79,VK_P:80,VK_Q:81,VK_R:82,VK_S:83,VK_T:84,VK_U:85,VK_V:86,VK_W:87,VK_X:88,VK_Y:89,VK_Z:90,VK_CONTEXT_MENU:93, -VK_NUMPAD0:96,VK_NUMPAD1:97,VK_NUMPAD2:98,VK_NUMPAD3:99,VK_NUMPAD4:100,VK_NUMPAD5:101,VK_NUMPAD6:102,VK_NUMPAD7:103,VK_NUMPAD8:104,VK_NUMPAD9:105,VK_MULTIPLY:106,VK_ADD:107,VK_SEPARATOR:108,VK_SUBTRACT:109,VK_DECIMAL:110,VK_DIVIDE:111,VK_F1:112,VK_F2:113,VK_F3:114,VK_F4:115,VK_F5:116,VK_F6:117,VK_F7:118,VK_F8:119,VK_F9:120,VK_F10:121,VK_F11:122,VK_F12:123,VK_F13:124,VK_F14:125,VK_F15:126,VK_F16:127,VK_F17:128,VK_F18:129,VK_F19:130,VK_F20:131,VK_F21:132,VK_F22:133,VK_F23:134,VK_F24:135,VK_NUM_LOCK:144, -VK_SCROLL_LOCK:145,VK_CIRCUMFLEX:160,VK_EXCLAMATION:161,VK_DOUBLE_QUOTE:162,VK_HASH:163,VK_DOLLAR:164,VK_PERCENT:165,VK_AMPERSAND:166,VK_UNDERSCORE:167,VK_OPEN_PAREN:168,VK_CLOSE_PAREN:169,VK_ASTERISK:170,VK_PLUS:171,VK_PIPE:172,VK_HYPHEN_MINUS:173,VK_OPEN_CURLY_BRACKET:174,VK_CLOSE_CURLY_BRACKET:175,VK_TILDE:176,VK_COMMA:188,VK_PERIOD:190,VK_SLASH:191,VK_BACK_QUOTE:192,VK_OPEN_BRACKET:219,VK_BACK_SLASH:220,VK_CLOSE_BRACKET:221,VK_QUOTE:222,VK_META:224,VK_ALTGR:225,VK_WIN:91,VK_KANA:21,VK_HANGUL:21, -VK_EISU:22,VK_JUNJA:23,VK_FINAL:24,VK_HANJA:25,VK_KANJI:25,VK_CONVERT:28,VK_NONCONVERT:29,VK_ACCEPT:30,VK_MODECHANGE:31,VK_SELECT:41,VK_PRINT:42,VK_EXECUTE:43,VK_SLEEP:95,Text:{RE_COLORS:/%([bc]){([^}]*)}/g,TYPE_TEXT:0,TYPE_NEWLINE:1,TYPE_FG:2,TYPE_BG:3,measure:function(a,b){for(var c={width:0,height:1},d=this.tokenize(a,b),e=0,f=0;fb){for(g=-1;;){var h=f.value.indexOf(" ",g+1);if(-1==h)break;if(d+h>b)break;g=h}-1!=g?f.value=this._breakInsideToken(a,c,g,!0):-1!=e?(f=a[e],c=f.value.lastIndexOf(" "),f.value=this._breakInsideToken(a,e,c,!0),c= -e):f.value=this._breakInsideToken(a,c,b-d,!1)}else d+=f.value.length,-1!=f.value.indexOf(" ")&&(e=c);c++}else a.splice(c,1)}}a.push({type:ROT.Text.TYPE_NEWLINE});d=null;for(c=0;cb||0>a||b>=this._context.canvas.width||a>=this._context.canvas.height?[-1,-1]:this._backend.eventToPosition(b,a)}; -ROT.Display.prototype.draw=function(a,b,c,d,e){d||(d=this._options.fg);e||(e=this._options.bg);this._data[a+","+b]=[a,b,c,d,e];!0!==this._dirty&&(this._dirty||(this._dirty={}),this._dirty[a+","+b]=!0)}; -ROT.Display.prototype.drawText=function(a,b,c,d){var e=null,f=null,g=a,h=1;d||(d=this._options.width-a);for(c=ROT.Text.tokenize(c,d);c.length;)switch(d=c.shift(),d.type){case ROT.Text.TYPE_TEXT:for(var k=0;ka?1/a:a;this._s0=(a>>>0)*this._frac;a=69069*a+1>>>0;this._s1=a*this._frac;this._s2=(69069*a+1>>>0)*this._frac;this._c=1;return this},getUniform:function(){var a=2091639*this._s0+this._c*this._frac;this._s0=this._s1;this._s1=this._s2;this._c=a|0;return this._s2=a-this._c},getUniformInt:function(a,b){var c=Math.max(a,b),d=Math.min(a,b);return Math.floor(this.getUniform()*(c-d+1))+d},getNormal:function(a,b){do var c=2* -this.getUniform()-1,d=2*this.getUniform()-1,d=c*c+d*d;while(1this._options.order?a=a.slice(-this._options.order):a.lengthb){c=d;break}this._events.splice(c,0,a);this._eventTimes.splice(c,0,b)}; -ROT.EventQueue.prototype.get=function(){if(!this._events.length)return null;var a=this._eventTimes.splice(0,1)[0];if(0p;p++)if(g=e+2*m[p][0],h=f+2*m[p][1],this._isFree(d,g,h,b,c)){d[g][h]=0;d[e+m[p][0]][f+ -m[p][1]]=0;e=g;f=h;l=!1;k++;break}}while(!l)}while(k+1b;b++)a[b][0]=0,a[b][1]=0;switch(Math.floor(4*ROT.RNG.getUniform())){case 0:a[0][0]=-1;a[1][0]=1;a[2][1]=-1;a[3][1]=1;break;case 1:a[3][0]=-1;a[2][0]=1;a[1][1]=-1;a[0][1]=1;break;case 2:a[2][0]=-1;a[3][0]=1;a[0][1]=-1;a[1][1]=1;break;case 3:a[1][0]=-1,a[0][0]=1,a[3][1]=-1,a[2][1]=1}};ROT.Map.IceyMaze.prototype._isFree=function(a,b,c,d,e){return 1>b||1>c||b>=d||c>=e?!1:a[b][c]};ROT.Map.EllerMaze=function(a,b){ROT.Map.call(this,a,b)};ROT.Map.EllerMaze.extend(ROT.Map); -ROT.Map.EllerMaze.prototype.create=function(a){for(var b=this._fillMap(1),c=Math.ceil((this._width-2)/2),d=[],e=[],f=0;ff||f>=this._width||0>f||e>=this._width||(c+=1==this._map[f][e]?1:0)}return c};ROT.Map.Dungeon=function(a,b){ROT.Map.call(this,a,b);this._rooms=[];this._corridors=[]};ROT.Map.Dungeon.extend(ROT.Map);ROT.Map.Dungeon.prototype.getRooms=function(){return this._rooms};ROT.Map.Dungeon.prototype.getCorridors=function(){return this._corridors}; -ROT.Map.Digger=function(a,b,c){ROT.Map.Dungeon.call(this,a,b);this._options={roomWidth:[3,9],roomHeight:[3,5],corridorLength:[3,10],dugPercentage:0.2,timeLimit:1E3};for(var d in c)this._options[d]=c[d];this._features={Room:4,Corridor:4};this._featureAttempts=20;this._walls={};this._digCallback=this._digCallback.bind(this);this._canBeDugCallback=this._canBeDugCallback.bind(this);this._isWallCallback=this._isWallCallback.bind(this);this._priorityWallCallback=this._priorityWallCallback.bind(this)};ROT.Map.Digger.extend(ROT.Map.Dungeon); -ROT.Map.Digger.prototype.create=function(a){this._rooms=[];this._corridors=[];this._map=this._fillMap(1);this._walls={};this._dug=0;var b=(this._width-2)*(this._height-2);this._firstRoom();var c=Date.now();do{if(Date.now()-c>this._options.timeLimit)break;var d=this._findWall();if(!d)break;var e=d.split(","),d=parseInt(e[0]),e=parseInt(e[1]),f=this._getDiggingDirection(d,e);if(f){var g=0;do if(g++,this._tryFeature(d,e,f[0],f[1])){this._removeSurroundingWalls(d,e);this._removeSurroundingWalls(d-f[0], -e-f[1]);break}while(ga||0>b||a>=this._width||b>=this._height?!1:1==this._map[a][b]};ROT.Map.Digger.prototype._canBeDugCallback=function(a,b){return 1>a||1>b||a+1>=this._width||b+1>=this._height?!1:1==this._map[a][b]};ROT.Map.Digger.prototype._priorityWallCallback=function(a,b){this._walls[a+","+b]=2}; -ROT.Map.Digger.prototype._firstRoom=function(){var a=ROT.Map.Feature.Room.createRandomCenter(Math.floor(this._width/2),Math.floor(this._height/2),this._options);this._rooms.push(a);a.create(this._digCallback)};ROT.Map.Digger.prototype._findWall=function(){var a=[],b=[],c;for(c in this._walls)2==this._walls[c]?b.push(c):a.push(c);a=b.length?b:a;if(!a.length)return null;c=a.random();delete this._walls[c];return c}; -ROT.Map.Digger.prototype._tryFeature=function(a,b,c,d){var e=ROT.RNG.getWeightedValue(this._features),e=ROT.Map.Feature[e].createRandomAt(a,b,c,d,this._options);if(!e.isValid(this._isWallCallback,this._canBeDugCallback))return!1;e.create(this._digCallback);e instanceof ROT.Map.Feature.Room&&this._rooms.push(e);e instanceof ROT.Map.Feature.Corridor&&(e.createPriorityWalls(this._priorityWallCallback),this._corridors.push(e));return!0}; -ROT.Map.Digger.prototype._removeSurroundingWalls=function(a,b){for(var c=ROT.DIRS[4],d=0;dg||0>h||g>=this._width||h>=this._width)return null;if(!this._map[g][h]){if(c)return null;c=f}}return c?[-c[0],-c[1]]:null}; -ROT.Map.Digger.prototype._addDoors=function(){for(var a=this._map,b=function(b,c){return 1==a[b][c]},c=0;cthis._options.timeLimit)return null;this._map=this._fillMap(1);this._dug=0;this._rooms=[];this._unconnected=[];this._generateRooms();if(!(2>this._rooms.length)&&this._generateCorridors())break}if(a)for(b=0;bthis._options.roomDugPercentage)break}while(c)};ROT.Map.Uniform.prototype._generateRoom=function(){for(var a=0;a=g&&c[e]<=h){var k=c.slice(),d=null;switch(f){case 0:d=b.getTop()-1;break;case 1:d=b.getRight()+1;break;case 2:d=b.getBottom()+1;break;case 3:d=b.getLeft()-1}k[(e+1)%2]=d;this._digLine([c,k])}else if(c[e]< -g-1||c[e]>h+1){d=c[e]-d[e];switch(f){case 0:case 1:k=0>d?3:1;break;case 2:case 3:k=0>d?1:3}k=this._placeInWall(b,(f+k)%4);if(!k)return!1;f=[0,0];f[e]=c[e];d=(e+1)%2;f[d]=k[d];this._digLine([c,f,k])}else{d=(e+1)%2;k=this._placeInWall(b,f);if(!k)return;f=Math.round((k[d]+c[d])/2);g=[0,0];h=[0,0];g[e]=c[e];g[d]=f;h[e]=k[e];h[d]=f;this._digLine([c,g,h,k])}a.addDoor(c[0],c[1]);b.addDoor(k[0],k[1]);e=this._unconnected.indexOf(a);-1!=e&&(this._unconnected.splice(e,1),this._connected.push(a));e=this._unconnected.indexOf(b); --1!=e&&(this._unconnected.splice(e,1),this._connected.push(b));return!0}; -ROT.Map.Uniform.prototype._placeInWall=function(a,b){var c=[0,0],d=[0,0],e=0;switch(b){case 0:d=[1,0];c=[a.getLeft(),a.getTop()-1];e=a.getRight()-a.getLeft()+1;break;case 1:d=[0,1];c=[a.getRight()+1,a.getTop()];e=a.getBottom()-a.getTop()+1;break;case 2:d=[1,0];c=[a.getLeft(),a.getBottom()+1];e=a.getRight()-a.getLeft()+1;break;case 3:d=[0,1],c=[a.getLeft()-1,a.getTop()],e=a.getBottom()-a.getTop()+1}for(var f=[],g=-2,h=0;ha||0>b||a>=this._width||b>=this._height?!1:1==this._map[a][b]};ROT.Map.Uniform.prototype._canBeDugCallback=function(a,b){return 1>a||1>b||a+1>=this._width||b+1>=this._height?!1:1==this._map[a][b]}; -ROT.Map.Rogue=function(a,b,c){ROT.Map.call(this,a,b);this._options={cellWidth:3,cellHeight:3};for(var d in c)this._options[d]=c[d];this._options.hasOwnProperty("roomWidth")||(this._options.roomWidth=this._calculateRoomSize(a,this._options.cellWidth));this._options.hasOwnProperty.roomHeight||(this._options.roomHeight=this._calculateRoomSize(b,this._options.cellHeight))};ROT.Map.Rogue.extend(ROT.Map); -ROT.Map.Rogue.prototype.create=function(a){this.map=this._fillMap(1);this.rooms=[];this.connectedCells=[];this._initRooms();this._connectRooms();this._connectUnconnectedRooms();this._createRandomRoomConnections();this._createRooms();this._createCorridors();if(a)for(var b=0;bd&&(d=2);2>c&&(c=2);return[d,c]};ROT.Map.Rogue.prototype._initRooms=function(){for(var a=0;ad||d>=this._options.cellWidth||0>c||c>=this._options.cellHeight)){f=this.rooms[a][b];if(0l||l>=a||0>k||k>=b)){d=this.rooms[l][k];e=!0;if(0==d.connections.length)break;for(l=0;lp-(n.y+n.height);)p++;if(0m-(n.x+n.width);)m++; -n=Math.round(this._getRandomInt(0,e-g)/2);for(var s=Math.round(this._getRandomInt(0,f-h)/2);m+n+g>=a;)n?n--:g--;for(;p+s+h>=b;)s?s--:h--;m+=n;p+=s;this.rooms[q][r].x=m;this.rooms[q][r].y=p;this.rooms[q][r].width=g;this.rooms[q][r].height=h;for(n=m;nc.cellx?(e=2,f=4):d.cellxc.celly?(e=3,f=1):d.cellyq?(r=1,s=0):(r=0,s=1);var u=n-r+f,v=q-s+f,m=n-1+2*f,f=q-1+2*f,p=p.mod(e),e=l.mod(e),t=0.5-n*n-q*q;0<=t&&(t*=t,l=d[p+c[e]],l=this._gradients[l],g=t*t*(l[0]*n+l[1]*q));n=0.5-u*u-v*v;0<=n&&(n*=n,l=d[p+r+c[e+s]],l=this._gradients[l],h=n*n*(l[0]*u+l[1]*v));n=0.5-m*m-f*f;0<=n&&(n*=n,l=d[p+1+c[e+ -1]],l=this._gradients[l],k=n*n*(l[0]*m+l[1]*f));return 70*(g+h+k)};ROT.FOV=function(a,b){this._lightPasses=a;this._options={topology:8};for(var c in b)this._options[c]=b[c]};ROT.FOV.prototype.compute=function(a,b,c,d){}; -ROT.FOV.prototype._getCircle=function(a,b,c){var d=[],e,f,g;switch(this._options.topology){case 4:f=1;g=[0,1];e=[ROT.DIRS[8][7],ROT.DIRS[8][1],ROT.DIRS[8][3],ROT.DIRS[8][5]];break;case 6:e=ROT.DIRS[6];f=1;g=[-1,1];break;case 8:e=ROT.DIRS[4],f=2,g=[-1,1]}a+=g[0]*c;b+=g[1]*c;for(g=0;ga){var e=arguments.callee(0,b,c,d),f=arguments.callee(360+a,360,c,d);return e||f}for(e=0;eb[0])return a=this._checkVisibility(a,[a[1],a[1]],c,d),b=this._checkVisibility([0,1],b,c,d),(a+b)/2;for(var e=0,f=!1;eh&&e%2&&(g=!1);if(!g)return 0;f=h-e+1;if(f%2)e%2?(h=d[e],h=(b[0]*h[1]-h[0]*b[1])/ -(h[1]*b[1]),c&&d.splice(e,f,b)):(h=d[h],h=(h[0]*a[1]-a[0]*h[1])/(a[1]*h[1]),c&&d.splice(e,f,a));else if(e%2)k=d[e],h=d[h],h=(h[0]*k[1]-k[0]*h[1])/(k[1]*h[1]),c&&d.splice(e,f);else return c&&d.splice(e,f,a,b),1;return h/((b[0]*a[1]-a[0]*b[1])/(a[1]*b[1]))};ROT.FOV.RecursiveShadowcasting=function(a,b){ROT.FOV.call(this,a,b)};ROT.FOV.RecursiveShadowcasting.extend(ROT.FOV);ROT.FOV.RecursiveShadowcasting.OCTANTS=[[-1,0,0,1],[0,-1,1,0],[0,-1,-1,0],[-1,0,0,-1],[1,0,0,-1],[0,1,-1,0],[0,1,1,0],[1,0,0,1]]; -ROT.FOV.RecursiveShadowcasting.prototype.compute=function(a,b,c,d){d(a,b,0,!0);for(var e=0;e=p;){var p=p+1,s=a+p*g+n*h,u=b+p*k+n*l,v=(p-0.5)/(n+0.5),t=(p+0.5)/(n-0.5);if(!(t>d)){if(vc;c++)b[c+1]+=16*b[c],b.splice(c,1);else b=(c=a.match(/rgb\(([0-9, ]+)\)/i))?c[1].split(/\s*,\s*/).map(function(a){return parseInt(a)}):[0,0,0];this._cache[a]=b}return b.slice()},add:function(a,b){for(var c=a.slice(),d=0;3>d;d++)for(var e=1;ec;c++)for(var d=1;dd;d++){for(var e=1;ec;c++){for(var d=1;darguments.length&&(c=0.5);for(var d=a.slice(),e=0;3> -e;e++)d[e]=Math.round(d[e]+c*(b[e]-a[e]));return d},interpolateHSL:function(a,b,c){3>arguments.length&&(c=0.5);for(var d=this.rgb2hsl(a),e=this.rgb2hsl(b),f=0;3>f;f++)d[f]+=c*(e[f]-d[f]);return this.hsl2rgb(d)},randomize:function(a,b){b instanceof Array||(b=ROT.RNG.getNormal(0,b));for(var c=a.slice(),d=0;3>d;d++)c[d]+=b instanceof Array?Math.round(ROT.RNG.getNormal(0,b[d])):b;return c},rgb2hsl:function(a){var b=a[0]/255,c=a[1]/255;a=a[2]/255;var d=Math.max(b,c,a),e=Math.min(b,c,a),f,g=(d+e)/2;if(d== -e)f=e=0;else{var h=d-e,e=0.5c&&(c+=1);1c?b:c<2/3?a+(b-a)*(2/3-c)*6:a},d=a[1],d=0.5>b?b*(1+d):b+d-b*d,e=2*b-d,b=c(e,d,a[0]+1/3),f=c(e,d,a[0]);a=c(e,d,a[0]-1/3);return[Math.round(255*b),Math.round(255*f),Math.round(255*a)]},toRGB:function(a){return"rgb("+ -this._clamp(a[0])+","+this._clamp(a[1])+","+this._clamp(a[2])+")"},toHex:function(a){for(var b=[],c=0;3>c;c++)b.push(this._clamp(a[c]).toString(16).lpad("0",2));return"#"+b.join("")},_clamp:function(a){return 0>a?0:255k;k++){var l=Math.round(e[k]*f);g[k]=l;h+=l}h>this._options.emissionThreshold&&(c[d]=g)}}return c}; -ROT.Lighting.prototype._emitLightFromCell=function(a,b,c,d){var e=a+","+b;a=e in this._fovCache?this._fovCache[e]:this._updateFOV(a,b);for(var f in a){b=a[f];f in d?e=d[f]:(e=[0,0,0],d[f]=e);for(var g=0;3>g;g++)e[g]+=Math.round(c[g]*b)}return this};ROT.Lighting.prototype._updateFOV=function(a,b){var c={};this._fovCache[a+","+b]=c;var d=this._options.range;this._fov.compute(a,b,d,function(a,b,g,h){g=h*(1-g/d);0!=g&&(c[a+","+b]=g)}.bind(this));return c}; -ROT.Path=function(a,b,c,d){this._toX=a;this._toY=b;this._fromY=this._fromX=null;this._passableCallback=c;this._options={topology:8};for(var e in d)this._options[e]=d[e];this._dirs=ROT.DIRS[this._options.topology];8==this._options.topology&&(this._dirs=[this._dirs[0],this._dirs[2],this._dirs[4],this._dirs[6],this._dirs[1],this._dirs[3],this._dirs[5],this._dirs[7]])};ROT.Path.prototype.compute=function(a,b,c){}; -ROT.Path.prototype._getNeighbors=function(a,b){for(var c=[],d=0;d Date: Tue, 1 Nov 2016 20:23:21 +0100 Subject: [PATCH 15/20] Upload last generate js files. --- rot.js | 2 +- rot.min.js | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 rot.min.js diff --git a/rot.js b/rot.js index aaea5e33..f657836f 100644 --- a/rot.js +++ b/rot.js @@ -1,6 +1,6 @@ /* This is rot.js, the ROguelike Toolkit in JavaScript. - Version 0.7~dev, generated on mar nov 1 20:20:43 CET 2016. + Version 0.7~dev, generated on mar nov 1 20:22:45 CET 2016. */ /** * @namespace Top-level ROT namespace diff --git a/rot.min.js b/rot.min.js new file mode 100644 index 00000000..f7e7886d --- /dev/null +++ b/rot.min.js @@ -0,0 +1,154 @@ +var ROT={isSupported:function(){return!(!document.createElement("canvas").getContext||!Function.prototype.bind)},DEFAULT_WIDTH:80,DEFAULT_HEIGHT:25,DIRS:{4:[[0,-1],[1,0],[0,1],[-1,0]],8:[[0,-1],[1,-1],[1,0],[1,1],[0,1],[-1,1],[-1,0],[-1,-1]],6:[[-1,-1],[1,-1],[2,0],[1,1],[-1,1],[-2,0]]},VK_CANCEL:3,VK_HELP:6,VK_BACK_SPACE:8,VK_TAB:9,VK_CLEAR:12,VK_RETURN:13,VK_ENTER:14,VK_SHIFT:16,VK_CONTROL:17,VK_ALT:18,VK_PAUSE:19,VK_CAPS_LOCK:20,VK_ESCAPE:27,VK_SPACE:32,VK_PAGE_UP:33,VK_PAGE_DOWN:34,VK_END:35, +VK_HOME:36,VK_LEFT:37,VK_UP:38,VK_RIGHT:39,VK_DOWN:40,VK_PRINTSCREEN:44,VK_INSERT:45,VK_DELETE:46,VK_0:48,VK_1:49,VK_2:50,VK_3:51,VK_4:52,VK_5:53,VK_6:54,VK_7:55,VK_8:56,VK_9:57,VK_COLON:58,VK_SEMICOLON:59,VK_LESS_THAN:60,VK_EQUALS:61,VK_GREATER_THAN:62,VK_QUESTION_MARK:63,VK_AT:64,VK_A:65,VK_B:66,VK_C:67,VK_D:68,VK_E:69,VK_F:70,VK_G:71,VK_H:72,VK_I:73,VK_J:74,VK_K:75,VK_L:76,VK_M:77,VK_N:78,VK_O:79,VK_P:80,VK_Q:81,VK_R:82,VK_S:83,VK_T:84,VK_U:85,VK_V:86,VK_W:87,VK_X:88,VK_Y:89,VK_Z:90,VK_CONTEXT_MENU:93, +VK_NUMPAD0:96,VK_NUMPAD1:97,VK_NUMPAD2:98,VK_NUMPAD3:99,VK_NUMPAD4:100,VK_NUMPAD5:101,VK_NUMPAD6:102,VK_NUMPAD7:103,VK_NUMPAD8:104,VK_NUMPAD9:105,VK_MULTIPLY:106,VK_ADD:107,VK_SEPARATOR:108,VK_SUBTRACT:109,VK_DECIMAL:110,VK_DIVIDE:111,VK_F1:112,VK_F2:113,VK_F3:114,VK_F4:115,VK_F5:116,VK_F6:117,VK_F7:118,VK_F8:119,VK_F9:120,VK_F10:121,VK_F11:122,VK_F12:123,VK_F13:124,VK_F14:125,VK_F15:126,VK_F16:127,VK_F17:128,VK_F18:129,VK_F19:130,VK_F20:131,VK_F21:132,VK_F22:133,VK_F23:134,VK_F24:135,VK_NUM_LOCK:144, +VK_SCROLL_LOCK:145,VK_CIRCUMFLEX:160,VK_EXCLAMATION:161,VK_DOUBLE_QUOTE:162,VK_HASH:163,VK_DOLLAR:164,VK_PERCENT:165,VK_AMPERSAND:166,VK_UNDERSCORE:167,VK_OPEN_PAREN:168,VK_CLOSE_PAREN:169,VK_ASTERISK:170,VK_PLUS:171,VK_PIPE:172,VK_HYPHEN_MINUS:173,VK_OPEN_CURLY_BRACKET:174,VK_CLOSE_CURLY_BRACKET:175,VK_TILDE:176,VK_COMMA:188,VK_PERIOD:190,VK_SLASH:191,VK_BACK_QUOTE:192,VK_OPEN_BRACKET:219,VK_BACK_SLASH:220,VK_CLOSE_BRACKET:221,VK_QUOTE:222,VK_META:224,VK_ALTGR:225,VK_WIN:91,VK_KANA:21,VK_HANGUL:21, +VK_EISU:22,VK_JUNJA:23,VK_FINAL:24,VK_HANJA:25,VK_KANJI:25,VK_CONVERT:28,VK_NONCONVERT:29,VK_ACCEPT:30,VK_MODECHANGE:31,VK_SELECT:41,VK_PRINT:42,VK_EXECUTE:43,VK_SLEEP:95,Text:{RE_COLORS:/%([bc]){([^}]*)}/g,TYPE_TEXT:0,TYPE_NEWLINE:1,TYPE_FG:2,TYPE_BG:3,measure:function(a,b){for(var c={width:0,height:1},d=this.tokenize(a,b),e=0,f=0;fb){for(g=-1;;){var h=f.value.indexOf(" ",g+1);if(-1==h)break;if(d+h>b)break;g=h}-1!=g?f.value=this._breakInsideToken(a,c,g,!0):-1!=e?(f=a[e],c=f.value.lastIndexOf(" "),f.value=this._breakInsideToken(a,e,c,!0),c= +e):f.value=this._breakInsideToken(a,c,b-d,!1)}else d+=f.value.length,-1!=f.value.indexOf(" ")&&(e=c);c++}else a.splice(c,1)}}a.push({type:ROT.Text.TYPE_NEWLINE});d=null;for(c=0;cb||0>a||b>=this._context.canvas.width||a>=this._context.canvas.height?[-1,-1]:this._backend.eventToPosition(b,a)}; +ROT.Display.prototype.draw=function(a,b,c,d,e){d||(d=this._options.fg);e||(e=this._options.bg);this._data[a+","+b]=[a,b,c,d,e];!0!==this._dirty&&(this._dirty||(this._dirty={}),this._dirty[a+","+b]=!0)}; +ROT.Display.prototype.drawText=function(a,b,c,d){var e=null,f=null,g=a,h=1;d||(d=this._options.width-a);for(c=ROT.Text.tokenize(c,d);c.length;)switch(d=c.shift(),d.type){case ROT.Text.TYPE_TEXT:for(var l,m=!1,n,q=!1,p=0;pl||65500l&&65518a?1/a:a;this._s0=(a>>>0)*this._frac;a=69069*a+1>>>0;this._s1=a*this._frac;this._s2=(69069*a+1>>>0)*this._frac;this._c=1;return this},getUniform:function(){var a=2091639*this._s0+this._c*this._frac;this._s0=this._s1;this._s1=this._s2;this._c=a|0;return this._s2=a-this._c},getUniformInt:function(a,b){var c=Math.max(a,b),d=Math.min(a,b);return Math.floor(this.getUniform()*(c-d+1))+d},getNormal:function(a,b){do var c=2* +this.getUniform()-1,d=2*this.getUniform()-1,d=c*c+d*d;while(1this._options.order?a=a.slice(-this._options.order):a.lengthb){c=d;break}this._events.splice(c,0,a);this._eventTimes.splice(c,0,b)};ROT.EventQueue.prototype.get=function(){if(!this._events.length)return null;var a=this._eventTimes.splice(0,1)[0];if(0q;q++)if(g=e+2*n[q][0],h=f+2*n[q][1],this._isFree(d,g,h,b,c)){d[g][h]=0;d[e+n[q][0]][f+n[q][1]]= +0;e=g;f=h;m=!1;l++;break}}while(!m)}while(l+1b;b++)a[b][0]=0,a[b][1]=0;switch(Math.floor(4*ROT.RNG.getUniform())){case 0:a[0][0]=-1;a[1][0]=1;a[2][1]=-1;a[3][1]=1;break;case 1:a[3][0]=-1;a[2][0]=1;a[1][1]=-1;a[0][1]=1;break;case 2:a[2][0]=-1;a[3][0]=1;a[0][1]=-1;a[1][1]=1;break;case 3:a[1][0]=-1,a[0][0]=1,a[3][1]=-1,a[2][1]=1}}; +ROT.Map.IceyMaze.prototype._isFree=function(a,b,c,d,e){return 1>b||1>c||b>=d||c>=e?!1:a[b][c]};ROT.Map.EllerMaze=function(a,b){ROT.Map.call(this,a,b)};ROT.Map.EllerMaze.extend(ROT.Map); +ROT.Map.EllerMaze.prototype.create=function(a){for(var b=this._fillMap(1),c=Math.ceil((this._width-2)/2),d=[],e=[],f=0;ff||f>=this._width||0>e||e>=this._width||(c+=1==this._map[f][e]?1:0)}return c}; +ROT.Map.Cellular.prototype.connect=function(a,b,c){b||(b=0);for(var d=[],e={},f=0;fh&&!(f.lengthe);h++);return[c,d]}; +ROT.Map.Cellular.prototype._getClosest=function(a,b){var c=null,d=null;for(k in b){var e=b[k],f=(e[0]-a[0])*(e[0]-a[0])+(e[1]-a[1])*(e[1]-a[1]);if(null==d||fthis._options.timeLimit)break;var d=this._findWall();if(!d)break;var e=d.split(","),d=parseInt(e[0]),e=parseInt(e[1]),f=this._getDiggingDirection(d,e);if(f){var g=0;do if(g++,this._tryFeature(d,e,f[0],f[1])){this._removeSurroundingWalls(d,e);this._removeSurroundingWalls(d-f[0], +e-f[1]);break}while(ga||0>b||a>=this._width||b>=this._height?!1:1==this._map[a][b]};ROT.Map.Digger.prototype._canBeDugCallback=function(a,b){return 1>a||1>b||a+1>=this._width||b+1>=this._height?!1:1==this._map[a][b]};ROT.Map.Digger.prototype._priorityWallCallback=function(a,b){this._walls[a+","+b]=2}; +ROT.Map.Digger.prototype._firstRoom=function(){var a=ROT.Map.Feature.Room.createRandomCenter(Math.floor(this._width/2),Math.floor(this._height/2),this._options);this._rooms.push(a);a.create(this._digCallback)};ROT.Map.Digger.prototype._findWall=function(){var a=[],b=[],c;for(c in this._walls)2==this._walls[c]?b.push(c):a.push(c);a=b.length?b:a;if(!a.length)return null;c=a.random();delete this._walls[c];return c}; +ROT.Map.Digger.prototype._tryFeature=function(a,b,c,d){var e=ROT.RNG.getWeightedValue(this._features),e=ROT.Map.Feature[e].createRandomAt(a,b,c,d,this._options);if(!e.isValid(this._isWallCallback,this._canBeDugCallback))return!1;e.create(this._digCallback);e instanceof ROT.Map.Feature.Room&&this._rooms.push(e);e instanceof ROT.Map.Feature.Corridor&&(e.createPriorityWalls(this._priorityWallCallback),this._corridors.push(e));return!0}; +ROT.Map.Digger.prototype._removeSurroundingWalls=function(a,b){for(var c=ROT.DIRS[4],d=0;d=a||0>=b||a>=this._width-1||b>=this._height-1)return null;for(var c=null,d=ROT.DIRS[4],e=0;ethis._options.timeLimit)return null;this._map=this._fillMap(1);this._dug=0;this._rooms=[];this._unconnected=[];this._generateRooms();if(!(2>this._rooms.length)&&this._generateCorridors())break}if(a)for(b=0;bthis._options.roomDugPercentage)break}while(c)};ROT.Map.Uniform.prototype._generateRoom=function(){for(var a=0;a=g&&c[e]<=h){var l=c.slice(),d=null;switch(f){case 0:d=b.getTop()-1;break;case 1:d=b.getRight()+1;break;case 2:d=b.getBottom()+1;break;case 3:d=b.getLeft()-1}l[(e+1)%2]=d;this._digLine([c,l])}else if(c[e]< +g-1||c[e]>h+1){d=c[e]-d[e];switch(f){case 0:case 1:l=0>d?3:1;break;case 2:case 3:l=0>d?1:3}l=this._placeInWall(b,(f+l)%4);if(!l)return!1;f=[0,0];f[e]=c[e];d=(e+1)%2;f[d]=l[d];this._digLine([c,f,l])}else{d=(e+1)%2;l=this._placeInWall(b,f);if(!l)return!1;f=Math.round((l[d]+c[d])/2);g=[0,0];h=[0,0];g[e]=c[e];g[d]=f;h[e]=l[e];h[d]=f;this._digLine([c,g,h,l])}a.addDoor(c[0],c[1]);b.addDoor(l[0],l[1]);e=this._unconnected.indexOf(a);-1!=e&&(this._unconnected.splice(e,1),this._connected.push(a));e=this._unconnected.indexOf(b); +-1!=e&&(this._unconnected.splice(e,1),this._connected.push(b));return!0}; +ROT.Map.Uniform.prototype._placeInWall=function(a,b){var c=[0,0],d=[0,0],e=0;switch(b){case 0:d=[1,0];c=[a.getLeft(),a.getTop()-1];e=a.getRight()-a.getLeft()+1;break;case 1:d=[0,1];c=[a.getRight()+1,a.getTop()];e=a.getBottom()-a.getTop()+1;break;case 2:d=[1,0];c=[a.getLeft(),a.getBottom()+1];e=a.getRight()-a.getLeft()+1;break;case 3:d=[0,1],c=[a.getLeft()-1,a.getTop()],e=a.getBottom()-a.getTop()+1}for(var f=[],g=-2,h=0;ha||0>b||a>=this._width||b>=this._height?!1:1==this._map[a][b]};ROT.Map.Uniform.prototype._canBeDugCallback=function(a,b){return 1>a||1>b||a+1>=this._width||b+1>=this._height?!1:1==this._map[a][b]}; +ROT.Map.Rogue=function(a,b,c){ROT.Map.call(this,a,b);this._options={cellWidth:3,cellHeight:3};for(var d in c)this._options[d]=c[d];this._options.hasOwnProperty("roomWidth")||(this._options.roomWidth=this._calculateRoomSize(this._width,this._options.cellWidth));this._options.hasOwnProperty("roomHeight")||(this._options.roomHeight=this._calculateRoomSize(this._height,this._options.cellHeight))};ROT.Map.Rogue.extend(ROT.Map); +ROT.Map.Rogue.prototype.create=function(a){this.map=this._fillMap(1);this.rooms=[];this.connectedCells=[];this._initRooms();this._connectRooms();this._connectUnconnectedRooms();this._createRandomRoomConnections();this._createRooms();this._createCorridors();if(a)for(var b=0;bd&&(d=2);2>c&&(c=2);return[d,c]}; +ROT.Map.Rogue.prototype._initRooms=function(){for(var a=0;ad||d>=this._options.cellWidth||0>c||c>=this._options.cellHeight)){f=this.rooms[a][b];if(0m||m>=a||0>l||l>=b)){d=this.rooms[m][l];e=!0;if(0==d.connections.length)break;for(m=0;mq-(p.y+p.height);)q++;if(0n-(p.x+p.width);)n++; +p=Math.round(ROT.RNG.getUniformInt(0,e-g)/2);for(var u=Math.round(ROT.RNG.getUniformInt(0,f-h)/2);n+p+g>=a;)p?p--:g--;for(;q+u+h>=b;)u?u--:h--;n+=p;q+=u;this.rooms[r][t].x=n;this.rooms[r][t].y=q;this.rooms[r][t].width=g;this.rooms[r][t].height=h;for(p=n;pc.cellx?(e=2,f=4):d.cellxc.celly?(e=3,f=1):d.cellyr?(t=1,u=0):(t=0,u=1);var w=p-t+f,x=r-u+f,n=p-1+2*f,f=r-1+2*f,q=q.mod(e),e=m.mod(e),v=.5-p*p-r*r;0<=v&&(v*=v,m=d[q+c[e]],m=this._gradients[m],g=v*v*(m[0]*p+m[1]*r));p=.5-w*w-x*x;0<=p&&(p*=p,m=d[q+t+c[e+u]],m=this._gradients[m],h=p*p*(m[0]*w+m[1]*x));p=.5-n*n-f*f;0<=p&&(p*=p,m=d[q+1+c[e+1]], +m=this._gradients[m],l=p*p*(m[0]*n+m[1]*f));return 70*(g+h+l)};ROT.FOV=function(a,b){this._lightPasses=a;this._options={topology:8};for(var c in b)this._options[c]=b[c]};ROT.FOV.prototype.compute=function(a,b,c,d){}; +ROT.FOV.prototype._getCircle=function(a,b,c){var d=[],e,f,g;switch(this._options.topology){case 4:f=1;g=[0,1];e=[ROT.DIRS[8][7],ROT.DIRS[8][1],ROT.DIRS[8][3],ROT.DIRS[8][5]];break;case 6:e=ROT.DIRS[6];f=1;g=[-1,1];break;case 8:e=ROT.DIRS[4],f=2,g=[-1,1]}a+=g[0]*c;b+=g[1]*c;for(g=0;ga){var e=arguments.callee(0,b,c,d),f=arguments.callee(360+a,360,c,d);return e||f}for(e=0;eb[0])return a=this._checkVisibility(a,[a[1],a[1]],c,d),b=this._checkVisibility([0,1],b,c,d),(a+b)/2;for(var e=0,f=!1;eh&&e%2&&(g=!1);if(!g)return 0;f=h-e+1;if(f%2)e%2?(h=d[e],h=(b[0]*h[1]-h[0]*b[1])/ +(h[1]*b[1]),c&&d.splice(e,f,b)):(h=d[h],h=(h[0]*a[1]-a[0]*h[1])/(a[1]*h[1]),c&&d.splice(e,f,a));else if(e%2)l=d[e],h=d[h],h=(h[0]*l[1]-l[0]*h[1])/(l[1]*h[1]),c&&d.splice(e,f);else return c&&d.splice(e,f,a,b),1;return h/((b[0]*a[1]-a[0]*b[1])/(a[1]*b[1]))};ROT.FOV.RecursiveShadowcasting=function(a,b){ROT.FOV.call(this,a,b)};ROT.FOV.RecursiveShadowcasting.extend(ROT.FOV);ROT.FOV.RecursiveShadowcasting.OCTANTS=[[-1,0,0,1],[0,-1,1,0],[0,-1,-1,0],[-1,0,0,-1],[1,0,0,-1],[0,1,-1,0],[0,1,1,0],[1,0,0,1]]; +ROT.FOV.RecursiveShadowcasting.prototype.compute=function(a,b,c,d){d(a,b,0,1);for(var e=0;e=q;){var q=q+1,u=a+q*g+p*h,w=b+q*l+p*m,x=(q-.5)/(p+.5),v=(q+.5)/(p-.5);if(!(v>d)){if(xc;c++)b[c+1]+=16*b[c],b.splice(c,1);else b=(c=a.match(/rgb\(([0-9, ]+)\)/i))?c[1].split(/\s*,\s*/).map(function(a){return parseInt(a)}):[0,0,0];this._cache[a]=b}return b.slice()},add:function(a,b){for(var c=a.slice(),d=0;3>d;d++)for(var e=1;ec;c++)for(var d=1;dd;d++){for(var e=1;ec;c++){for(var d=1;darguments.length&&(c=.5);for(var d=a.slice(),e=0;3>e;e++)d[e]= +Math.round(d[e]+c*(b[e]-a[e]));return d},interpolateHSL:function(a,b,c){3>arguments.length&&(c=.5);for(var d=this.rgb2hsl(a),e=this.rgb2hsl(b),f=0;3>f;f++)d[f]+=c*(e[f]-d[f]);return this.hsl2rgb(d)},randomize:function(a,b){b instanceof Array||(b=Math.round(ROT.RNG.getNormal(0,b)));for(var c=a.slice(),d=0;3>d;d++)c[d]+=b instanceof Array?Math.round(ROT.RNG.getNormal(0,b[d])):b;return c},rgb2hsl:function(a){var b=a[0]/255,c=a[1]/255;a=a[2]/255;var d=Math.max(b,c,a),e=Math.min(b,c,a),f,g=(d+e)/2;if(d== +e)f=e=0;else{var h=d-e,e=.5c&&(c+=1);1c?b:c<2/3?a+(b-a)*(2/3-c)*6:a},d=a[1],d=.5>b?b*(1+d):b+d-b*d,e=2*b-d,b=c(e,d,a[0]+1/3),f=c(e,d,a[0]);a=c(e,d,a[0]-1/3);return[Math.round(255*b),Math.round(255*f),Math.round(255*a)]},toRGB:function(a){return"rgb("+ +this._clamp(a[0])+","+this._clamp(a[1])+","+this._clamp(a[2])+")"},toHex:function(a){for(var b=[],c=0;3>c;c++)b.push(this._clamp(a[c]).toString(16).lpad("0",2));return"#"+b.join("")},_clamp:function(a){return 0>a?0:255l;l++){var m=Math.round(e[l]*f);g[l]=m;h+=m}h>this._options.emissionThreshold&&(c[d]=g)}}return c}; +ROT.Lighting.prototype._emitLightFromCell=function(a,b,c,d){var e=a+","+b;a=e in this._fovCache?this._fovCache[e]:this._updateFOV(a,b);for(var f in a){b=a[f];f in d?e=d[f]:(e=[0,0,0],d[f]=e);for(var g=0;3>g;g++)e[g]+=Math.round(c[g]*b)}return this};ROT.Lighting.prototype._updateFOV=function(a,b){var c={};this._fovCache[a+","+b]=c;var d=this._options.range;this._fov.compute(a,b,d,function(a,b,g,h){g=h*(1-g/d);0!=g&&(c[a+","+b]=g)}.bind(this));return c}; +ROT.Path=function(a,b,c,d){this._toX=a;this._toY=b;this._fromY=this._fromX=null;this._passableCallback=c;this._options={topology:8};for(var e in d)this._options[e]=d[e];this._dirs=ROT.DIRS[this._options.topology];8==this._options.topology&&(this._dirs=[this._dirs[0],this._dirs[2],this._dirs[4],this._dirs[6],this._dirs[1],this._dirs[3],this._dirs[5],this._dirs[7]])};ROT.Path.prototype.compute=function(a,b,c){}; +ROT.Path.prototype._getNeighbors=function(a,b){for(var c=[],d=0;d Date: Tue, 1 Nov 2016 20:25:28 +0100 Subject: [PATCH 16/20] Added the own image files into the makefile. --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 6219e160..590a87b7 100644 --- a/Makefile +++ b/Makefile @@ -42,6 +42,7 @@ SOURCES = src/rot.js \ src/path/path.js \ src/path/dijkstra.js \ src/path/astar.js + src/image/image.js NODE_VERSION = "$(shell head -1 < NODE_VERSION)" NODE_PRE_SOURCES = node/node-shim.js From ec4eb449313c1266778e854144c196d93377a5fe Mon Sep 17 00:00:00 2001 From: mdtrooper Date: Tue, 1 Nov 2016 20:26:23 +0100 Subject: [PATCH 17/20] Fixed typo bug in the Makefile. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 590a87b7..35964f4a 100644 --- a/Makefile +++ b/Makefile @@ -41,7 +41,7 @@ SOURCES = src/rot.js \ src/lighting.js \ src/path/path.js \ src/path/dijkstra.js \ - src/path/astar.js + src/path/astar.js \ src/image/image.js NODE_VERSION = "$(shell head -1 < NODE_VERSION)" From d8bca521c2381dbfd18fc4e56d076f996ee379c9 Mon Sep 17 00:00:00 2001 From: mdtrooper Date: Tue, 1 Nov 2016 21:04:27 +0100 Subject: [PATCH 18/20] Some fixes in image module and re-generated js libraries. --- rot.js | 197 ++++++++++++++++++++++++++++++++++++++++++++- rot.min.js | 10 ++- src/image/image.js | 7 +- 3 files changed, 210 insertions(+), 4 deletions(-) diff --git a/rot.js b/rot.js index f657836f..d4a4e856 100644 --- a/rot.js +++ b/rot.js @@ -1,6 +1,6 @@ /* This is rot.js, the ROguelike Toolkit in JavaScript. - Version 0.7~dev, generated on mar nov 1 20:22:45 CET 2016. + Version 0.7~dev, generated on mar nov 1 20:53:53 CET 2016. */ /** * @namespace Top-level ROT namespace @@ -5326,3 +5326,198 @@ ROT.Path.AStar.prototype._distance = function(x, y) { throw new Error("Illegal topology"); } +/** + * @class Base image (as ASCII art) converter + */ + + //BASED IN THE WORK OF: + /* + * jsAscii 0.1 + * Copyright (c) 2008 Jacob Seidelin, jseidelin@nihilogic.dk, http://blog.nihilogic.dk/ + * MIT License [http://www.nihilogic.dk/licenses/mit-license.txt] + */ +ROT.Image = function() { + this.aDefaultCharList = (" .,:;i1tfLCG08@").split(""); + this.aDefaultColorCharList = (" CGO08@").split(""); + this.strFont = "courier new"; + this.bBlock = true; + this.bAlpha = false; + this.bColor = true; + this.strResolution = "medium"; + + this.ascii_art = ""; + this.ascii_art_array = null; + this.height = 0; + this.width = 0; +}; + +ROT.Image.prototype.loadASCII = function() { + var aCharList = this.aDefaultCharList; + var bBlock = this.bBlock; + var bAlpha = this.bAlpha; + var bColor = this.bColor; + + var oCanvas = document.createElement("canvas"); + if (!oCanvas.getContext) { + return; + } + var oCtx = oCanvas.getContext("2d"); + if (!oCtx.getImageData) { + return; + } + + var strResolution = this.strResolution; + + var fResolution = 0.5; + + switch (strResolution) { + case "low" : fResolution = 0.25; break; + case "medium" : fResolution = 0.5; break; + case "high" : fResolution = 1; break; + } + + var iWidth = Math.round(parseInt(this.img.width) * fResolution); + var iHeight = Math.round(parseInt(this.img.height) * fResolution); + this.width = iWidth; + this.height = iHeight; + + oCanvas.width = iWidth; + oCanvas.height = iHeight; + //oCanvas.style.display = "none"; + oCanvas.style.width = iWidth; + oCanvas.style.height = iHeight; + + oCtx.drawImage(this.img, 0, 0, iWidth, iHeight); + + var oImgData = oCtx.getImageData(0, 0, iWidth, iHeight).data; + + var strChars = ""; + var array_chars = []; + + var iterator_y = 0; + for (var y=0;y" + strThisChar + ""; + + array_chars[iterator_y][x] = {'r': iRed, 'g' : iGreen, 'b' : iBlue, 'char': unscape_char}; + } + else { + strChars += strThisChar; + array_chars[iterator_y][x] = {'char': unscape_char}; + } + } + iterator_y++; + strChars += "
"; + } + + this.height = iterator_y; + + this.ascii_art_array = array_chars; + + return strChars; +}; + +ROT.Image.prototype.load = function(image_url) { + this.img = new Image(); + this.img.setAttribute('crossOrigin', ''); + this.img.src = image_url; + + var self = this; + + if (this.img.complete) { + console.log(this); + this.ascii_art = this.loadASCII(); + } + else { + this.img.onload = function() { + self.ascii_art = self.loadASCII(); + } + } +}; + +ROT.Image.prototype.get = function(xin, yin) { + return this.ascii_art_array[yin][xin]; +}; + +ROT.Image.prototype.blit = function(display, display_x, display_y, image_x, image_y, rect_w, rect_h) { + var i = 0; + + for (var y = image_y; y < rect_h; y++) { + var j = 0; + for (var x = image_x; x < rect_w; x++) { + if (this.bColor) { + var color = "#" + + this.ascii_art_array[y][x]['r'].toString(16) + + this.ascii_art_array[y][x]['g'].toString(16) + + this.ascii_art_array[y][x]['b'].toString(16); + if (this.bBlock) { + display.draw(display_x + j, display_y + i, this.ascii_art_array[y][x]['char'], color, color); + } + else { + display.draw(display_x + j, display_y + i, this.ascii_art_array[y][x]['char'], color); + } + } + else { + display.draw(display_x + j, display_y + i, this.ascii_art_array[y][x]['char']); + } + j++; + } + i++; + } +}; + +ROT.Image.prototype.paint = function(display, offset_x, offset_y) { + var i = 0; + + for (var y = 0; y < this.height; y++) { + var j = 0; + for (var x = 0; x < this.width; x++) { + if (this.bColor) { + var color = "#" + + this.ascii_art_array[y][x]['r'].toString(16) + + this.ascii_art_array[y][x]['g'].toString(16) + + this.ascii_art_array[y][x]['b'].toString(16); + if (this.bBlock) { + display.draw(offset_x + j, offset_y + i, this.ascii_art_array[y][x]['char'], color, color); + } + else { + display.draw(offset_x + j, offset_y + i, this.ascii_art_array[y][x]['char'], color); + } + } + else { + display.draw(offset_x + j, offset_y + i, this.ascii_art_array[y][x]['char']); + } + j++; + } + i++; + } +}; \ No newline at end of file diff --git a/rot.min.js b/rot.min.js index f7e7886d..0e0a26d5 100644 --- a/rot.min.js +++ b/rot.min.js @@ -116,7 +116,7 @@ ROT.Map.Feature.Corridor.prototype.isValid=function(a,b){var c=this._startX,d=th e,this._endY+f);return(c||h)&&this._endsWithAWall?!1:!0};ROT.Map.Feature.Corridor.prototype.create=function(a){var b=this._startX,c=this._startY,d=this._endX-b,e=this._endY-c,f=1+Math.max(Math.abs(d),Math.abs(e));d&&(d/=Math.abs(d));e&&(e/=Math.abs(e));for(var g=0;gr?(t=1,u=0):(t=0,u=1);var w=p-t+f,x=r-u+f,n=p-1+2*f,f=r-1+2*f,q=q.mod(e),e=m.mod(e),v=.5-p*p-r*r;0<=v&&(v*=v,m=d[q+c[e]],m=this._gradients[m],g=v*v*(m[0]*p+m[1]*r));p=.5-w*w-x*x;0<=p&&(p*=p,m=d[q+t+c[e+u]],m=this._gradients[m],h=p*p*(m[0]*w+m[1]*x));p=.5-n*n-f*f;0<=p&&(p*=p,m=d[q+1+c[e+1]], +ROT.Noise.Simplex.prototype.get=function(a,b){var c=this._perms,d=this._indexes,e=c.length/2,f=this._G2,g=0,h=0,l=0,m,n=(a+b)*this._F2,q=Math.floor(a+n);m=Math.floor(b+n);var n=(q+m)*f,p=a-(q-n),r=b-(m-n),t,u;p>r?(t=1,u=0):(t=0,u=1);var x=p-t+f,v=r-u+f,n=p-1+2*f,f=r-1+2*f,q=q.mod(e),e=m.mod(e),w=.5-p*p-r*r;0<=w&&(w*=w,m=d[q+c[e]],m=this._gradients[m],g=w*w*(m[0]*p+m[1]*r));p=.5-x*x-v*v;0<=p&&(p*=p,m=d[q+t+c[e+u]],m=this._gradients[m],h=p*p*(m[0]*x+m[1]*v));p=.5-n*n-f*f;0<=p&&(p*=p,m=d[q+1+c[e+1]], m=this._gradients[m],l=p*p*(m[0]*n+m[1]*f));return 70*(g+h+l)};ROT.FOV=function(a,b){this._lightPasses=a;this._options={topology:8};for(var c in b)this._options[c]=b[c]};ROT.FOV.prototype.compute=function(a,b,c,d){}; ROT.FOV.prototype._getCircle=function(a,b,c){var d=[],e,f,g;switch(this._options.topology){case 4:f=1;g=[0,1];e=[ROT.DIRS[8][7],ROT.DIRS[8][1],ROT.DIRS[8][3],ROT.DIRS[8][5]];break;case 6:e=ROT.DIRS[6];f=1;g=[-1,1];break;case 8:e=ROT.DIRS[4],f=2,g=[-1,1]}a+=g[0]*c;b+=g[1]*c;for(g=0;g=q;){var q=q+1,u=a+q*g+p*h,w=b+q*l+p*m,x=(q-.5)/(p+.5),v=(q+.5)/(p-.5);if(!(v>d)){if(x=q;){var q=q+1,u=a+q*g+p*h,x=b+q*l+p*m,v=(q-.5)/(p+.5),w=(q+.5)/(p-.5);if(!(w>d)){if(vc;c++)b[c+1]+=16*b[c],b.splice(c,1);else b=(c=a.match(/rgb\(([0-9, ]+)\)/i))?c[1].split(/\s*,\s*/).map(function(a){return parseInt(a)}):[0,0,0];this._cache[a]=b}return b.slice()},add:function(a,b){for(var c=a.slice(),d=0;3>d;d++)for(var e=1;ec;c++)for(var d=1;dd;d++){for(var e=1;ec;c++){for(var d=1;darguments.length&&(c=.5);for(var d=a.slice(),e=0;3>e;e++)d[e]= Math.round(d[e]+c*(b[e]-a[e]));return d},interpolateHSL:function(a,b,c){3>arguments.length&&(c=.5);for(var d=this.rgb2hsl(a),e=this.rgb2hsl(b),f=0;3>f;f++)d[f]+=c*(e[f]-d[f]);return this.hsl2rgb(d)},randomize:function(a,b){b instanceof Array||(b=Math.round(ROT.RNG.getNormal(0,b)));for(var c=a.slice(),d=0;3>d;d++)c[d]+=b instanceof Array?Math.round(ROT.RNG.getNormal(0,b[d])):b;return c},rgb2hsl:function(a){var b=a[0]/255,c=a[1]/255;a=a[2]/255;var d=Math.max(b,c,a),e=Math.min(b,c,a),f,g=(d+e)/2;if(d== @@ -152,3 +152,9 @@ ROT.Path.Dijkstra.prototype._add=function(a,b,c){c={x:a,y:b,prev:c};this._comput ROT.Path.AStar.prototype.compute=function(a,b,c){this._todo=[];this._done={};this._fromX=a;this._fromY=b;for(this._add(this._toX,this._toY,null);this._todo.length;){var d=this._todo.shift();if(d.x==a&&d.y==b)break;for(var e=this._getNeighbors(d.x,d.y),f=0;f"+v+"",l[m][q]={r:r,g:t,b:u,"char":w}):(f+=v,l[m][q]={"char":w})}m++;f+="
"}this.height=m;this.ascii_art_array= +l;return f}}};ROT.Image.prototype.load=function(a){this.img=new Image;this.img.setAttribute("crossOrigin","");this.img.src=a;var b=this;this.img.complete?(console.log(this),this.ascii_art=this.loadASCII()):this.img.onload=function(){b.ascii_art=b.loadASCII()}};ROT.Image.prototype.get=function(a,b){return this.ascii_art_array[b][a]}; +ROT.Image.prototype.blit=function(a,b,c,d,e,f,g){for(var h=0;e Date: Tue, 1 Nov 2016 21:04:54 +0100 Subject: [PATCH 19/20] Cleaned source code of examples for ASCIIart. --- examples/combat.html | 46 ++++---------------------------------------- examples/title.html | 46 ++++---------------------------------------- 2 files changed, 8 insertions(+), 84 deletions(-) diff --git a/examples/combat.html b/examples/combat.html index ea6eec96..d0c6a2f5 100644 --- a/examples/combat.html +++ b/examples/combat.html @@ -3,54 +3,16 @@ test - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - + + test + + + + + + + + + \ No newline at end of file diff --git a/examples/title.html b/examples/title.html index b4ff8ee3..557297ae 100644 --- a/examples/title.html +++ b/examples/title.html @@ -1,31 +1,34 @@ - -test - - - - - - - - - + + test + + + + + + + + + \ No newline at end of file diff --git a/rot.js b/rot.js index d4a4e856..3280624d 100644 --- a/rot.js +++ b/rot.js @@ -1,6 +1,6 @@ /* This is rot.js, the ROguelike Toolkit in JavaScript. - Version 0.7~dev, generated on mar nov 1 20:53:53 CET 2016. + Version 0.7~dev, generated on mié nov 2 00:54:29 CET 2016. */ /** * @namespace Top-level ROT namespace @@ -5446,7 +5446,7 @@ ROT.Image.prototype.loadASCII = function() { return strChars; }; -ROT.Image.prototype.load = function(image_url) { +ROT.Image.prototype.load = function(image_url, callback_load_complete) { this.img = new Image(); this.img.setAttribute('crossOrigin', ''); this.img.src = image_url; @@ -5454,13 +5454,19 @@ ROT.Image.prototype.load = function(image_url) { var self = this; if (this.img.complete) { - console.log(this); this.ascii_art = this.loadASCII(); + + return true; } else { this.img.onload = function() { self.ascii_art = self.loadASCII(); + + if (typeof(callback_load_complete) != "undefined") + callback_load_complete(); } + + return false; } }; diff --git a/rot.min.js b/rot.min.js index 0e0a26d5..9940312f 100644 --- a/rot.min.js +++ b/rot.min.js @@ -155,6 +155,6 @@ ROT.Path.AStar.prototype._distance=function(a,b){switch(this._options.topology){ ROT.Image=function(){this.aDefaultCharList=" .,:;i1tfLCG08@".split("");this.aDefaultColorCharList=" CGO08@".split("");this.strFont="courier new";this.bBlock=!0;this.bAlpha=!1;this.bColor=!0;this.strResolution="medium";this.ascii_art="";this.ascii_art_array=null;this.width=this.height=0}; ROT.Image.prototype.loadASCII=function(){var a=this.aDefaultCharList,b=this.bBlock,c=this.bAlpha,d=this.bColor,e=document.createElement("canvas");if(e.getContext){var f=e.getContext("2d");if(f.getImageData){var g=.5;switch(this.strResolution){case "low":g=.25;break;case "medium":g=.5;break;case "high":g=1}var h=Math.round(parseInt(this.img.width)*g),g=Math.round(parseInt(this.img.height)*g);this.width=h;this.height=g;e.width=h;e.height=g;e.style.width=h;e.style.height=g;f.drawImage(this.img,0,0,h, g);for(var e=f.getImageData(0,0,h,g).data,f="",l=[],m=0,n=0;n"+v+"",l[m][q]={r:r,g:t,b:u,"char":w}):(f+=v,l[m][q]={"char":w})}m++;f+="
"}this.height=m;this.ascii_art_array= -l;return f}}};ROT.Image.prototype.load=function(a){this.img=new Image;this.img.setAttribute("crossOrigin","");this.img.src=a;var b=this;this.img.complete?(console.log(this),this.ascii_art=this.loadASCII()):this.img.onload=function(){b.ascii_art=b.loadASCII()}};ROT.Image.prototype.get=function(a,b){return this.ascii_art_array[b][a]}; +l;return f}}};ROT.Image.prototype.load=function(a,b){this.img=new Image;this.img.setAttribute("crossOrigin","");this.img.src=a;var c=this;if(this.img.complete)return this.ascii_art=this.loadASCII(),!0;this.img.onload=function(){c.ascii_art=c.loadASCII();"undefined"!=typeof b&&b()};return!1};ROT.Image.prototype.get=function(a,b){return this.ascii_art_array[b][a]}; ROT.Image.prototype.blit=function(a,b,c,d,e,f,g){for(var h=0;e