UNPKG

rot-js

Version:

A roguelike toolkit in JavaScript

1,451 lines (1,436 loc) 184 kB
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _createForOfIteratorHelperLoose(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (it) return (it = it.call(o)).next.bind(it); if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; return function () { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ROT = {})); })(this, function (exports) { 'use strict'; /** * 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. */ var FRAC = 2.3283064365386963e-10; /* 2^-32 */ var RNG = /*#__PURE__*/function () { function RNG() { this._seed = 0; this._s0 = 0; this._s1 = 0; this._s2 = 0; this._c = 0; } var _proto = RNG.prototype; _proto.getSeed = function getSeed() { return this._seed; } /** * Seed the number generator */; _proto.setSeed = function setSeed(seed) { seed = seed < 1 ? 1 / seed : seed; this._seed = seed; this._s0 = (seed >>> 0) * FRAC; seed = seed * 69069 + 1 >>> 0; this._s1 = seed * FRAC; seed = seed * 69069 + 1 >>> 0; this._s2 = seed * FRAC; this._c = 1; return this; } /** * @returns Pseudorandom value [0,1), uniformly distributed */; _proto.getUniform = function getUniform() { var t = 2091639 * this._s0 + this._c * FRAC; this._s0 = this._s1; this._s1 = this._s2; this._c = t | 0; this._s2 = t - this._c; return this._s2; } /** * @param lowerBound The lower end of the range to return a value from, inclusive * @param upperBound The upper end of the range to return a value from, inclusive * @returns Pseudorandom value [lowerBound, upperBound], using ROT.RNG.getUniform() to distribute the value */; _proto.getUniformInt = function getUniformInt(lowerBound, upperBound) { var max = Math.max(lowerBound, upperBound); var min = Math.min(lowerBound, upperBound); return Math.floor(this.getUniform() * (max - min + 1)) + min; } /** * @param mean Mean value * @param stddev Standard deviation. ~95% of the absolute values will be lower than 2*stddev. * @returns A normally distributed pseudorandom value */; _proto.getNormal = function getNormal(mean, stddev) { if (mean === void 0) { mean = 0; } if (stddev === void 0) { stddev = 1; } var u, v, r; do { u = 2 * this.getUniform() - 1; v = 2 * this.getUniform() - 1; r = u * u + v * v; } while (r > 1 || r == 0); var gauss = u * Math.sqrt(-2 * Math.log(r) / r); return mean + gauss * stddev; } /** * @returns Pseudorandom value [1,100] inclusive, uniformly distributed */; _proto.getPercentage = function getPercentage() { return 1 + Math.floor(this.getUniform() * 100); } /** * @returns Randomly picked item, null when length=0 */; _proto.getItem = function getItem(array) { if (!array.length) { return null; } return array[Math.floor(this.getUniform() * array.length)]; } /** * @returns New array with randomized items */; _proto.shuffle = function shuffle(array) { var result = []; var clone = array.slice(); while (clone.length) { var _index = clone.indexOf(this.getItem(clone)); result.push(clone.splice(_index, 1)[0]); } return result; } /** * @param data key=whatever, value=weight (relative probability) * @returns whatever */; _proto.getWeightedValue = function getWeightedValue(data) { var total = 0; for (var _id in data) { total += data[_id]; } var random = this.getUniform() * total; var id, part = 0; for (id in data) { part += data[id]; if (random < part) { return id; } } // If by some floating-point annoyance we have // random >= total, just return the last id. return id; } /** * Get RNG state. Useful for storing the state and re-setting it via setState. * @returns Internal state */; _proto.getState = function getState() { return [this._s0, this._s1, this._s2, this._c]; } /** * Set a previously retrieved state. */; _proto.setState = function setState(state) { this._s0 = state[0]; this._s1 = state[1]; this._s2 = state[2]; this._c = state[3]; return this; } /** * Returns a cloned RNG */; _proto.clone = function clone() { var clone = new RNG(); return clone.setState(this.getState()); }; return RNG; }(); var RNG$1 = new RNG().setSeed(Date.now()); /** * @class Abstract display backend module * @private */ var Backend = /*#__PURE__*/function () { function Backend() {} var _proto2 = Backend.prototype; _proto2.getContainer = function getContainer() { return null; }; _proto2.setOptions = function setOptions(options) { this._options = options; }; return Backend; }(); var Canvas = /*#__PURE__*/function (_Backend) { _inheritsLoose(Canvas, _Backend); function Canvas() { var _this; _this = _Backend.call(this) || this; _this._ctx = document.createElement("canvas").getContext("2d"); return _this; } var _proto3 = Canvas.prototype; _proto3.schedule = function schedule(cb) { requestAnimationFrame(cb); }; _proto3.getContainer = function getContainer() { return this._ctx.canvas; }; _proto3.setOptions = function setOptions(opts) { _Backend.prototype.setOptions.call(this, opts); var style = opts.fontStyle ? opts.fontStyle + " " : ""; var font = style + " " + opts.fontSize + "px " + opts.fontFamily; this._ctx.font = font; this._updateSize(); this._ctx.font = font; this._ctx.textAlign = "center"; this._ctx.textBaseline = "middle"; }; _proto3.clear = function clear() { var oldComposite = this._ctx.globalCompositeOperation; this._ctx.globalCompositeOperation = "copy"; this._ctx.fillStyle = this._options.bg; this._ctx.fillRect(0, 0, this._ctx.canvas.width, this._ctx.canvas.height); this._ctx.globalCompositeOperation = oldComposite; }; _proto3.eventToPosition = function eventToPosition(x, y) { var canvas = this._ctx.canvas; var rect = canvas.getBoundingClientRect(); x -= rect.left; y -= rect.top; x *= canvas.width / rect.width; y *= canvas.height / rect.height; if (x < 0 || y < 0 || x >= canvas.width || y >= canvas.height) { return [-1, -1]; } return this._normalizedEventToPosition(x, y); }; return Canvas; }(Backend); /** * Always positive modulus * @param x Operand * @param n Modulus * @returns x modulo n */ function mod(x, n) { return (x % n + n) % n; } function clamp(val, min, max) { if (min === void 0) { min = 0; } if (max === void 0) { max = 1; } if (val < min) return min; if (val > max) return max; return val; } function capitalize(string) { return string.charAt(0).toUpperCase() + string.substring(1); } /** * Format a string in a flexible way. Scans for %s strings and replaces them with arguments. List of patterns is modifiable via String.format.map. * @param {string} template * @param {any} [argv] */ function format(template) { for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } var map = format.map; var replacer = function replacer(match, group1, group2, index) { if (template.charAt(index - 1) == "%") { return match.substring(1); } if (!args.length) { return match; } var obj = args[0]; var group = group1 || group2; var parts = group.split(","); var name = parts.shift() || ""; var method = map[name.toLowerCase()]; if (!method) { return match; } obj = args.shift(); var replaced = obj[method].apply(obj, parts); var first = name.charAt(0); if (first != first.toLowerCase()) { replaced = capitalize(replaced); } return replaced; }; return template.replace(/%(?:([a-z]+)|(?:{([^}]+)}))/gi, replacer); } format.map = { "s": "toString" }; var util = /*#__PURE__*/Object.freeze({ __proto__: null, mod: mod, clamp: clamp, capitalize: capitalize, format: format }); /** * @class Hexagonal backend * @private */ var Hex = /*#__PURE__*/function (_Canvas) { _inheritsLoose(Hex, _Canvas); function Hex() { var _this2; _this2 = _Canvas.call(this) || this; _this2._spacingX = 0; _this2._spacingY = 0; _this2._hexSize = 0; return _this2; } var _proto4 = Hex.prototype; _proto4.draw = function draw(data, clearBefore) { var x = data[0], y = data[1], ch = data[2], fg = data[3], bg = data[4]; var px = [(x + 1) * this._spacingX, y * this._spacingY + this._hexSize]; if (this._options.transpose) { px.reverse(); } if (clearBefore) { this._ctx.fillStyle = bg; this._fill(px[0], px[1]); } if (!ch) { return; } this._ctx.fillStyle = fg; var chars = [].concat(ch); for (var i = 0; i < chars.length; i++) { this._ctx.fillText(chars[i], px[0], Math.ceil(px[1])); } }; _proto4.computeSize = function computeSize(availWidth, availHeight) { if (this._options.transpose) { availWidth += availHeight; availHeight = availWidth - availHeight; availWidth -= availHeight; } var width = Math.floor(availWidth / this._spacingX) - 1; var height = Math.floor((availHeight - 2 * this._hexSize) / this._spacingY + 1); return [width, height]; }; _proto4.computeFontSize = function computeFontSize(availWidth, availHeight) { if (this._options.transpose) { availWidth += availHeight; availHeight = availWidth - availHeight; 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._ctx.font; this._ctx.font = "100px " + this._options.fontFamily; var width = Math.ceil(this._ctx.measureText("W").width); this._ctx.font = oldFont; var ratio = width / 100; hexSize = Math.floor(hexSize) + 1; // closest larger hexSize // FIXME char size computation does not respect transposed hexes var fontSize = 2 * hexSize / (this._options.spacing * (1 + ratio / Math.sqrt(3))); // closest smaller fontSize return Math.ceil(fontSize) - 1; }; _proto4._normalizedEventToPosition = function _normalizedEventToPosition(x, y) { var nodeSize; if (this._options.transpose) { x += y; y = x - y; x -= y; nodeSize = this._ctx.canvas.width; } else { nodeSize = this._ctx.canvas.height; } var size = nodeSize / this._options.height; y = Math.floor(y / size); if (mod(y, 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]; } /** * Arguments are pixel values. If "transposed" mode is enabled, then these two are already swapped. */; _proto4._fill = function _fill(cx, cy) { var a = this._hexSize; var b = this._options.border; var ctx = this._ctx; ctx.beginPath(); if (this._options.transpose) { ctx.moveTo(cx - a + b, cy); ctx.lineTo(cx - a / 2 + b, cy + this._spacingX - b); ctx.lineTo(cx + a / 2 - b, cy + this._spacingX - b); ctx.lineTo(cx + a - b, cy); ctx.lineTo(cx + a / 2 - b, cy - this._spacingX + b); ctx.lineTo(cx - a / 2 + b, cy - this._spacingX + b); ctx.lineTo(cx - a + b, cy); } else { ctx.moveTo(cx, cy - a + b); ctx.lineTo(cx + this._spacingX - b, cy - a / 2 + b); ctx.lineTo(cx + this._spacingX - b, cy + a / 2 - b); ctx.lineTo(cx, cy + a - b); ctx.lineTo(cx - this._spacingX + b, cy + a / 2 - b); ctx.lineTo(cx - this._spacingX + b, cy - a / 2 + b); ctx.lineTo(cx, cy - a + b); } ctx.fill(); }; _proto4._updateSize = function _updateSize() { var opts = this._options; var charWidth = Math.ceil(this._ctx.measureText("W").width); this._hexSize = Math.floor(opts.spacing * (opts.fontSize + charWidth / Math.sqrt(3)) / 2); this._spacingX = this._hexSize * Math.sqrt(3) / 2; this._spacingY = this._hexSize * 1.5; var xprop; var yprop; if (opts.transpose) { xprop = "height"; yprop = "width"; } else { xprop = "width"; yprop = "height"; } this._ctx.canvas[xprop] = Math.ceil((opts.width + 1) * this._spacingX); this._ctx.canvas[yprop] = Math.ceil((opts.height - 1) * this._spacingY + 2 * this._hexSize); }; return Hex; }(Canvas); /** * @class Rectangular backend * @private */ var Rect = /*#__PURE__*/function (_Canvas2) { _inheritsLoose(Rect, _Canvas2); function Rect() { var _this3; _this3 = _Canvas2.call(this) || this; _this3._spacingX = 0; _this3._spacingY = 0; _this3._canvasCache = {}; return _this3; } var _proto5 = Rect.prototype; _proto5.setOptions = function setOptions(options) { _Canvas2.prototype.setOptions.call(this, options); this._canvasCache = {}; }; _proto5.draw = function draw(data, clearBefore) { if (Rect.cache) { this._drawWithCache(data); } else { this._drawNoCache(data, clearBefore); } }; _proto5._drawWithCache = function _drawWithCache(data) { var x = data[0], y = data[1], ch = data[2], fg = data[3], bg = data[4]; var hash = "" + ch + fg + bg; var canvas; if (hash in this._canvasCache) { canvas = this._canvasCache[hash]; } else { var b = this._options.border; canvas = document.createElement("canvas"); var ctx = canvas.getContext("2d"); canvas.width = this._spacingX; canvas.height = this._spacingY; ctx.fillStyle = bg; ctx.fillRect(b, b, canvas.width - b, canvas.height - b); if (ch) { ctx.fillStyle = fg; ctx.font = this._ctx.font; ctx.textAlign = "center"; ctx.textBaseline = "middle"; var chars = [].concat(ch); for (var i = 0; i < chars.length; i++) { ctx.fillText(chars[i], this._spacingX / 2, Math.ceil(this._spacingY / 2)); } } this._canvasCache[hash] = canvas; } this._ctx.drawImage(canvas, x * this._spacingX, y * this._spacingY); }; _proto5._drawNoCache = function _drawNoCache(data, clearBefore) { var x = data[0], y = data[1], ch = data[2], fg = data[3], bg = data[4]; if (clearBefore) { var b = this._options.border; this._ctx.fillStyle = bg; this._ctx.fillRect(x * this._spacingX + b, y * this._spacingY + b, this._spacingX - b, this._spacingY - b); } if (!ch) { return; } this._ctx.fillStyle = fg; var chars = [].concat(ch); for (var i = 0; i < chars.length; i++) { this._ctx.fillText(chars[i], (x + 0.5) * this._spacingX, Math.ceil((y + 0.5) * this._spacingY)); } }; _proto5.computeSize = function computeSize(availWidth, availHeight) { var width = Math.floor(availWidth / this._spacingX); var height = Math.floor(availHeight / this._spacingY); return [width, height]; }; _proto5.computeFontSize = function computeFontSize(availWidth, availHeight) { var boxWidth = Math.floor(availWidth / this._options.width); var boxHeight = Math.floor(availHeight / this._options.height); /* compute char ratio */ var oldFont = this._ctx.font; this._ctx.font = "100px " + this._options.fontFamily; var width = Math.ceil(this._ctx.measureText("W").width); this._ctx.font = oldFont; var ratio = width / 100; var widthFraction = ratio * boxHeight / boxWidth; if (widthFraction > 1) { /* too wide with current aspect ratio */ boxHeight = Math.floor(boxHeight / widthFraction); } return Math.floor(boxHeight / this._options.spacing); }; _proto5._normalizedEventToPosition = function _normalizedEventToPosition(x, y) { return [Math.floor(x / this._spacingX), Math.floor(y / this._spacingY)]; }; _proto5._updateSize = function _updateSize() { var opts = this._options; var charWidth = Math.ceil(this._ctx.measureText("W").width); this._spacingX = Math.ceil(opts.spacing * charWidth); this._spacingY = Math.ceil(opts.spacing * opts.fontSize); if (opts.forceSquareRatio) { this._spacingX = this._spacingY = Math.max(this._spacingX, this._spacingY); } this._ctx.canvas.width = opts.width * this._spacingX; this._ctx.canvas.height = opts.height * this._spacingY; }; return Rect; }(Canvas); Rect.cache = false; /** * @class Tile backend * @private */ var Tile = /*#__PURE__*/function (_Canvas3) { _inheritsLoose(Tile, _Canvas3); function Tile() { var _this4; _this4 = _Canvas3.call(this) || this; _this4._colorCanvas = document.createElement("canvas"); return _this4; } var _proto6 = Tile.prototype; _proto6.draw = function draw(data, clearBefore) { var x = data[0], y = data[1], ch = data[2], fg = data[3], bg = data[4]; var tileWidth = this._options.tileWidth; var tileHeight = this._options.tileHeight; if (clearBefore) { if (this._options.tileColorize) { this._ctx.clearRect(x * tileWidth, y * tileHeight, tileWidth, tileHeight); } else { this._ctx.fillStyle = bg; this._ctx.fillRect(x * tileWidth, y * tileHeight, tileWidth, tileHeight); } } if (!ch) { return; } var chars = [].concat(ch); var fgs = [].concat(fg); var bgs = [].concat(bg); for (var i = 0; i < chars.length; i++) { var tile = this._options.tileMap[chars[i]]; if (!tile) { throw new Error("Char \"" + chars[i] + "\" not found in tileMap"); } if (this._options.tileColorize) { // apply colorization var canvas = this._colorCanvas; var context = canvas.getContext("2d"); context.globalCompositeOperation = "source-over"; context.clearRect(0, 0, tileWidth, tileHeight); var _fg = fgs[i]; var _bg = bgs[i]; context.drawImage(this._options.tileSet, tile[0], tile[1], tileWidth, tileHeight, 0, 0, tileWidth, tileHeight); if (_fg != "transparent") { context.fillStyle = _fg; context.globalCompositeOperation = "source-atop"; context.fillRect(0, 0, tileWidth, tileHeight); } if (_bg != "transparent") { context.fillStyle = _bg; context.globalCompositeOperation = "destination-over"; context.fillRect(0, 0, tileWidth, tileHeight); } this._ctx.drawImage(canvas, x * tileWidth, y * tileHeight, tileWidth, tileHeight); } else { // no colorizing, easy this._ctx.drawImage(this._options.tileSet, tile[0], tile[1], tileWidth, tileHeight, x * tileWidth, y * tileHeight, tileWidth, tileHeight); } } }; _proto6.computeSize = function computeSize(availWidth, availHeight) { var width = Math.floor(availWidth / this._options.tileWidth); var height = Math.floor(availHeight / this._options.tileHeight); return [width, height]; }; _proto6.computeFontSize = function computeFontSize() { throw new Error("Tile backend does not understand font size"); }; _proto6._normalizedEventToPosition = function _normalizedEventToPosition(x, y) { return [Math.floor(x / this._options.tileWidth), Math.floor(y / this._options.tileHeight)]; }; _proto6._updateSize = function _updateSize() { var opts = this._options; this._ctx.canvas.width = opts.width * opts.tileWidth; this._ctx.canvas.height = opts.height * opts.tileHeight; this._colorCanvas.width = opts.tileWidth; this._colorCanvas.height = opts.tileHeight; }; return Tile; }(Canvas); function fromString(str) { var cached, r; if (str in CACHE) { cached = CACHE[str]; } else { if (str.charAt(0) == "#") { // hex rgb var matched = str.match(/[0-9a-f]/gi) || []; var values = matched.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]; } CACHE[str] = cached; } return cached.slice(); } /** * Add two or more colors */ function add(color1) { var result = color1.slice(); for (var _len2 = arguments.length, colors = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { colors[_key2 - 1] = arguments[_key2]; } for (var i = 0; i < 3; i++) { for (var j = 0; j < colors.length; j++) { result[i] += colors[j][i]; } } return result; } /** * Add two or more colors, MODIFIES FIRST ARGUMENT */ function add_(color1) { for (var _len3 = arguments.length, colors = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) { colors[_key3 - 1] = arguments[_key3]; } for (var i = 0; i < 3; i++) { for (var j = 0; j < colors.length; j++) { color1[i] += colors[j][i]; } } return color1; } /** * Multiply (mix) two or more colors */ function multiply(color1) { var result = color1.slice(); for (var _len4 = arguments.length, colors = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) { colors[_key4 - 1] = arguments[_key4]; } for (var i = 0; i < 3; i++) { for (var j = 0; j < colors.length; j++) { result[i] *= colors[j][i] / 255; } result[i] = Math.round(result[i]); } return result; } /** * Multiply (mix) two or more colors, MODIFIES FIRST ARGUMENT */ function multiply_(color1) { for (var _len5 = arguments.length, colors = new Array(_len5 > 1 ? _len5 - 1 : 0), _key5 = 1; _key5 < _len5; _key5++) { colors[_key5 - 1] = arguments[_key5]; } for (var i = 0; i < 3; i++) { for (var j = 0; j < colors.length; j++) { color1[i] *= colors[j][i] / 255; } color1[i] = Math.round(color1[i]); } return color1; } /** * Interpolate (blend) two colors with a given factor */ function interpolate(color1, color2, factor) { if (factor === void 0) { factor = 0.5; } var result = color1.slice(); for (var i = 0; i < 3; i++) { result[i] = Math.round(result[i] + factor * (color2[i] - color1[i])); } return result; } var lerp = interpolate; /** * Interpolate (blend) two colors with a given factor in HSL mode */ function interpolateHSL(color1, color2, factor) { if (factor === void 0) { factor = 0.5; } var hsl1 = rgb2hsl(color1); var hsl2 = rgb2hsl(color2); for (var i = 0; i < 3; i++) { hsl1[i] += factor * (hsl2[i] - hsl1[i]); } return hsl2rgb(hsl1); } var lerpHSL = interpolateHSL; /** * Create a new random color based on this one * @param color * @param diff Set of standard deviations */ function randomize(color, diff) { if (!(diff instanceof Array)) { diff = Math.round(RNG$1.getNormal(0, diff)); } var result = color.slice(); for (var i = 0; i < 3; i++) { result[i] += diff instanceof Array ? Math.round(RNG$1.getNormal(0, diff[i])) : diff; } return result; } /** * Converts an RGB color value to HSL. Expects 0..255 inputs, produces 0..1 outputs. */ function rgb2hsl(color) { var r = color[0] / 255; var g = color[1] / 255; var b = color[2] / 255; var max = Math.max(r, g, b), min = Math.min(r, g, b); var h = 0, s, l = (max + min) / 2; if (max == min) { s = 0; // achromatic } else { var d = max - min; s = l > 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]; } 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; } /** * Converts an HSL color value to RGB. Expects 0..1 inputs, produces 0..255 outputs. */ function hsl2rgb(color) { var l = color[2]; if (color[1] == 0) { l = Math.round(l * 255); return [l, l, l]; } else { 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)]; } } function toRGB(color) { var clamped = color.map(function (x) { return clamp(x, 0, 255); }); return "rgb(" + clamped.join(",") + ")"; } function toHex(color) { var clamped = color.map(function (x) { return clamp(x, 0, 255).toString(16).padStart(2, "0"); }); return "#" + clamped.join(""); } var 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] }; var color = /*#__PURE__*/Object.freeze({ __proto__: null, fromString: fromString, add: add, add_: add_, multiply: multiply, multiply_: multiply_, interpolate: interpolate, lerp: lerp, interpolateHSL: interpolateHSL, lerpHSL: lerpHSL, randomize: randomize, rgb2hsl: rgb2hsl, hsl2rgb: hsl2rgb, toRGB: toRGB, toHex: toHex }); /** * @class Tile backend * @private */ var TileGL = /*#__PURE__*/function (_Backend2) { _inheritsLoose(TileGL, _Backend2); function TileGL() { var _this5; _this5 = _Backend2.call(this) || this; _this5._uniforms = {}; try { _this5._gl = _this5._initWebGL(); } catch (e) { if (typeof e === "string") { alert(e); } else if (e instanceof Error) { alert(e.message); } } return _this5; } TileGL.isSupported = function isSupported() { return !!document.createElement("canvas").getContext("webgl2", { preserveDrawingBuffer: true }); }; var _proto7 = TileGL.prototype; _proto7.schedule = function schedule(cb) { requestAnimationFrame(cb); }; _proto7.getContainer = function getContainer() { return this._gl.canvas; }; _proto7.setOptions = function setOptions(opts) { var _this6 = this; _Backend2.prototype.setOptions.call(this, opts); this._updateSize(); var tileSet = this._options.tileSet; if (tileSet && "complete" in tileSet && !tileSet.complete) { tileSet.addEventListener("load", function () { return _this6._updateTexture(tileSet); }); } else { this._updateTexture(tileSet); } }; _proto7.draw = function draw(data, clearBefore) { var gl = this._gl; var opts = this._options; var x = data[0], y = data[1], ch = data[2], fg = data[3], bg = data[4]; var scissorY = gl.canvas.height - (y + 1) * opts.tileHeight; gl.scissor(x * opts.tileWidth, scissorY, opts.tileWidth, opts.tileHeight); if (clearBefore) { if (opts.tileColorize) { gl.clearColor(0, 0, 0, 0); } else { gl.clearColor.apply(gl, parseColor(bg)); } gl.clear(gl.COLOR_BUFFER_BIT); } if (!ch) { return; } var chars = [].concat(ch); var bgs = [].concat(bg); var fgs = [].concat(fg); gl.uniform2fv(this._uniforms["targetPosRel"], [x, y]); for (var i = 0; i < chars.length; i++) { var tile = this._options.tileMap[chars[i]]; if (!tile) { throw new Error("Char \"" + chars[i] + "\" not found in tileMap"); } gl.uniform1f(this._uniforms["colorize"], opts.tileColorize ? 1 : 0); gl.uniform2fv(this._uniforms["tilesetPosAbs"], tile); if (opts.tileColorize) { gl.uniform4fv(this._uniforms["tint"], parseColor(fgs[i])); gl.uniform4fv(this._uniforms["bg"], parseColor(bgs[i])); } gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); } /* for (let i=0;i<chars.length;i++) { if (this._options.tileColorize) { // apply colorization let canvas = this._colorCanvas; let context = canvas.getContext("2d") as CanvasRenderingContext2D; context.globalCompositeOperation = "source-over"; context.clearRect(0, 0, tileWidth, tileHeight); let fg = fgs[i]; let bg = bgs[i]; context.drawImage( this._options.tileSet!, tile[0], tile[1], tileWidth, tileHeight, 0, 0, tileWidth, tileHeight ); if (fg != "transparent") { context.fillStyle = fg; context.globalCompositeOperation = "source-atop"; context.fillRect(0, 0, tileWidth, tileHeight); } if (bg != "transparent") { context.fillStyle = bg; context.globalCompositeOperation = "destination-over"; context.fillRect(0, 0, tileWidth, tileHeight); } this._ctx.drawImage(canvas, x*tileWidth, y*tileHeight, tileWidth, tileHeight); } else { // no colorizing, easy this._ctx.drawImage( this._options.tileSet!, tile[0], tile[1], tileWidth, tileHeight, x*tileWidth, y*tileHeight, tileWidth, tileHeight ); } } */ }; _proto7.clear = function clear() { var gl = this._gl; gl.clearColor.apply(gl, parseColor(this._options.bg)); gl.scissor(0, 0, gl.canvas.width, gl.canvas.height); gl.clear(gl.COLOR_BUFFER_BIT); }; _proto7.computeSize = function computeSize(availWidth, availHeight) { var width = Math.floor(availWidth / this._options.tileWidth); var height = Math.floor(availHeight / this._options.tileHeight); return [width, height]; }; _proto7.computeFontSize = function computeFontSize() { throw new Error("Tile backend does not understand font size"); }; _proto7.eventToPosition = function eventToPosition(x, y) { var canvas = this._gl.canvas; var rect = canvas.getBoundingClientRect(); x -= rect.left; y -= rect.top; x *= canvas.width / rect.width; y *= canvas.height / rect.height; if (x < 0 || y < 0 || x >= canvas.width || y >= canvas.height) { return [-1, -1]; } return this._normalizedEventToPosition(x, y); }; _proto7._initWebGL = function _initWebGL() { var _this7 = this; var gl = document.createElement("canvas").getContext("webgl2", { preserveDrawingBuffer: true }); window.gl = gl; var program = createProgram(gl, VS, FS); gl.useProgram(program); createQuad(gl); UNIFORMS.forEach(function (name) { return _this7._uniforms[name] = gl.getUniformLocation(program, name); }); this._program = program; gl.enable(gl.BLEND); gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); gl.enable(gl.SCISSOR_TEST); return gl; }; _proto7._normalizedEventToPosition = function _normalizedEventToPosition(x, y) { return [Math.floor(x / this._options.tileWidth), Math.floor(y / this._options.tileHeight)]; }; _proto7._updateSize = function _updateSize() { var gl = this._gl; var opts = this._options; var canvasSize = [opts.width * opts.tileWidth, opts.height * opts.tileHeight]; gl.canvas.width = canvasSize[0]; gl.canvas.height = canvasSize[1]; gl.viewport(0, 0, canvasSize[0], canvasSize[1]); gl.uniform2fv(this._uniforms["tileSize"], [opts.tileWidth, opts.tileHeight]); gl.uniform2fv(this._uniforms["targetSize"], canvasSize); }; _proto7._updateTexture = function _updateTexture(tileSet) { createTexture(this._gl, tileSet); }; return TileGL; }(Backend); var UNIFORMS = ["targetPosRel", "tilesetPosAbs", "tileSize", "targetSize", "colorize", "bg", "tint"]; var VS = "\n#version 300 es\n\nin vec2 tilePosRel;\nout vec2 tilesetPosPx;\n\nuniform vec2 tilesetPosAbs;\nuniform vec2 tileSize;\nuniform vec2 targetSize;\nuniform vec2 targetPosRel;\n\nvoid main() {\n\tvec2 targetPosPx = (targetPosRel + tilePosRel) * tileSize;\n\tvec2 targetPosNdc = ((targetPosPx / targetSize)-0.5)*2.0;\n\ttargetPosNdc.y *= -1.0;\n\n\tgl_Position = vec4(targetPosNdc, 0.0, 1.0);\n\ttilesetPosPx = tilesetPosAbs + tilePosRel * tileSize;\n}".trim(); var FS = "\n#version 300 es\nprecision highp float;\n\nin vec2 tilesetPosPx;\nout vec4 fragColor;\nuniform sampler2D image;\nuniform bool colorize;\nuniform vec4 bg;\nuniform vec4 tint;\n\nvoid main() {\n\tfragColor = vec4(0, 0, 0, 1);\n\n\tvec4 texel = texelFetch(image, ivec2(tilesetPosPx), 0);\n\n\tif (colorize) {\n\t\ttexel.rgb = tint.a * tint.rgb + (1.0-tint.a) * texel.rgb;\n\t\tfragColor.rgb = texel.a*texel.rgb + (1.0-texel.a)*bg.rgb;\n\t\tfragColor.a = texel.a + (1.0-texel.a)*bg.a;\n\t} else {\n\t\tfragColor = texel;\n\t}\n}".trim(); function createProgram(gl, vss, fss) { var vs = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vs, vss); gl.compileShader(vs); if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS)) { throw new Error(gl.getShaderInfoLog(vs) || ""); } var fs = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fs, fss); gl.compileShader(fs); if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS)) { throw new Error(gl.getShaderInfoLog(fs) || ""); } var p = gl.createProgram(); gl.attachShader(p, vs); gl.attachShader(p, fs); gl.linkProgram(p); if (!gl.getProgramParameter(p, gl.LINK_STATUS)) { throw new Error(gl.getProgramInfoLog(p) || ""); } return p; } function createQuad(gl) { var pos = new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]); var buf = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buf); gl.bufferData(gl.ARRAY_BUFFER, pos, gl.STATIC_DRAW); gl.enableVertexAttribArray(0); gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); } function createTexture(gl, data) { var t = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, t); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 0); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, data); return t; } var colorCache = {}; function parseColor(color$1) { if (!(color$1 in colorCache)) { var parsed; if (color$1 == "transparent") { parsed = [0, 0, 0, 0]; } else if (color$1.indexOf("rgba") > -1) { parsed = (color$1.match(/[\d.]+/g) || []).map(Number); for (var i = 0; i < 3; i++) { parsed[i] = parsed[i] / 255; } } else { parsed = fromString(color$1).map(function ($) { return $ / 255; }); parsed.push(1); } colorCache[color$1] = parsed; } return colorCache[color$1]; } function clearToAnsi(bg) { return "\x1B[0;48;5;" + termcolor(bg) + "m\x1B[2J"; } function colorToAnsi(fg, bg) { return "\x1B[0;38;5;" + termcolor(fg) + ";48;5;" + termcolor(bg) + "m"; } function positionToAnsi(x, y) { return "\x1B[" + (y + 1) + ";" + (x + 1) + "H"; } function termcolor(color$1) { var SRC_COLORS = 256.0; var DST_COLORS = 6.0; var COLOR_RATIO = DST_COLORS / SRC_COLORS; var rgb = fromString(color$1); var r = Math.floor(rgb[0] * COLOR_RATIO); var g = Math.floor(rgb[1] * COLOR_RATIO); var b = Math.floor(rgb[2] * COLOR_RATIO); return r * 36 + g * 6 + b * 1 + 16; } var Term = /*#__PURE__*/function (_Backend3) { _inheritsLoose(Term, _Backend3); function Term() { var _this8; _this8 = _Backend3.call(this) || this; _this8._offset = [0, 0]; _this8._cursor = [-1, -1]; _this8._lastColor = ""; return _this8; } var _proto8 = Term.prototype; _proto8.schedule = function schedule(cb) { setTimeout(cb, 1000 / 60); }; _proto8.setOptions = function setOptions(options) { _Backend3.prototype.setOptions.call(this, options); var size = [options.width, options.height]; var avail = this.computeSize(); this._offset = avail.map(function (val, index) { return Math.floor((val - size[index]) / 2); }); }; _proto8.clear = function clear() { process.stdout.write(clearToAnsi(this._options.bg)); }; _proto8.draw = function draw(data, clearBefore) { // determine where to draw what with what colors var x = data[0], y = data[1], ch = data[2], fg = data[3], bg = data[4]; // determine if we need to move the terminal cursor var dx = this._offset[0] + x; var dy = this._offset[1] + y; var size = this.computeSize(); if (dx < 0 || dx >= size[0]) { return; } if (dy < 0 || dy >= size[1]) { return; } if (dx !== this._cursor[0] || dy !== this._cursor[1]) { process.stdout.write(positionToAnsi(dx, dy)); this._cursor[0] = dx; this._cursor[1] = dy; } // terminals automatically clear, but if we're clearing when we're // not otherwise provided with a character, just use a space instead if (clearBefore) { if (!ch) { ch = " "; } } // if we're not clearing and not provided with a character, do nothing if (!ch) { return; } // determine if we need to change colors var newColor = colorToAnsi(fg, bg); if (newColor !== this._lastColor) { process.stdout.write(newColor); this._lastColor = newColor; } if (ch != '\t') { // write the provided symbol to the display var chars = [].concat(ch); process.stdout.write(chars[0]); } // update our position, given that we wrote a character this._cursor[0]++; if (this._cursor[0] >= size[0]) { this._cursor[0] = 0; this._cursor[1]++; } }; _proto8.computeFontSize = function computeFontSize() { throw new Error("Terminal backend has no notion of font size"); }; _proto8.eventToPosition = function eventToPosition(x, y) { return [x, y]; }; _proto8.computeSize = function computeSize() { return [process.stdout.columns, process.stdout.rows]; }; return Term; }(Backend); /** * @namespace * Contains text tokenization and breaking routines */ var RE_COLORS = /%([bc]){([^}]*)}/g; // token types var TYPE_TEXT = 0; var TYPE_NEWLINE = 1; var TYPE_FG = 2; var TYPE_BG = 3; /** * Measure size of a resulting text block */ function measure(str, maxWidth) { var result = { width: 0, height: 1 }; var tokens = tokenize(str, maxWidth); var lineWidth = 0; for (var i = 0; i < tokens.length; i++) { var token = tokens[i]; switch (token.type) { case TYPE_TEXT: lineWidth += token.value.length; break; case TYPE_NEWLINE: result.height++; result.width = Math.max(result.width, lineWidth); lineWidth = 0; break; } } result.width = Math.max(result.width, lineWidth); return result; } /** * Convert string to a series of a formatting commands */ function tokenize(str, maxWidth) { var result = []; /* first tokenization pass - split texts and color formatting commands */ var offset = 0; str.replace(RE_COLORS, function (match, type, name, index) { /* string before */ var part = str.substring(offset, index); if (part.length) { result.push({ type: TYPE_TEXT, value: part }); } /* color command */ result.push({ type: type == "c" ? TYPE_FG : TYPE_BG, value: name.trim() }); offset = index + match.length; return ""; }); /* last remaining part */ var part = str.substring(offset); if (part.length) { result.push({ type: TYPE_TEXT, value: part }); } return breakLines(result, maxWidth); } /* insert line breaks into first-pass tokenized data */ function breakLines(tokens, maxWidth) { if (!maxWidth) { maxWidth = Infinity; } var i = 0; var lineLength = 0; var lastTokenWithSpace = -1; while (i < tokens.length) { /* take all text tokens, remove space, apply linebreaks */ var token = tokens[i]; if (token.type == TYPE_NEWLINE) { /* reset */