From 99dbc85a6379f824ba732a4963ff21290578c619 Mon Sep 17 00:00:00 2001 From: Mnem42 Date: Sun, 29 Jun 2025 14:22:39 +0100 Subject: [PATCH 01/38] Made the mod just about functional --- mods/fancy-loader.js | 2365 ++++++++++++++++++++++++++++ mods/loader_test/mod.toml | 25 + mods/loader_test/test_element.toml | 14 + 3 files changed, 2404 insertions(+) create mode 100644 mods/fancy-loader.js create mode 100644 mods/loader_test/mod.toml create mode 100644 mods/loader_test/test_element.toml diff --git a/mods/fancy-loader.js b/mods/fancy-loader.js new file mode 100644 index 00000000..9292aba9 --- /dev/null +++ b/mods/fancy-loader.js @@ -0,0 +1,2365 @@ +"use strict"; +(() => { + var __create = Object.create; + var __defProp = Object.defineProperty; + var __getOwnPropDesc = Object.getOwnPropertyDescriptor; + var __getOwnPropNames = Object.getOwnPropertyNames; + var __getProtoOf = Object.getPrototypeOf; + var __hasOwnProp = Object.prototype.hasOwnProperty; + var __commonJS = (cb, mod) => function __require() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; + }; + var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; + }; + var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod + )); + + // node_modules/utility-types/dist/aliases-and-guards.js + var require_aliases_and_guards = __commonJS({ + "node_modules/utility-types/dist/aliases-and-guards.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.isPrimitive = function(val) { + if (val === null || val === void 0) { + return true; + } + switch (typeof val) { + case "string": + case "number": + case "bigint": + case "boolean": + case "symbol": { + return true; + } + default: + return false; + } + }; + exports2.isFalsy = function(val) { + return !val; + }; + exports2.isNullish = function(val) { + return val == null; + }; + } + }); + + // node_modules/utility-types/dist/functional-helpers.js + var require_functional_helpers = __commonJS({ + "node_modules/utility-types/dist/functional-helpers.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + function getReturnOfExpression(expression) { + return void 0; + } + exports2.getReturnOfExpression = getReturnOfExpression; + } + }); + + // node_modules/utility-types/dist/index.js + var require_dist = __commonJS({ + "node_modules/utility-types/dist/index.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + var aliases_and_guards_1 = require_aliases_and_guards(); + exports2.isFalsy = aliases_and_guards_1.isFalsy; + exports2.isNullish = aliases_and_guards_1.isNullish; + exports2.isPrimitive = aliases_and_guards_1.isPrimitive; + var functional_helpers_1 = require_functional_helpers(); + exports2.getReturnOfExpression = functional_helpers_1.getReturnOfExpression; + } + }); + + // node_modules/@iarna/toml/lib/parser.js + var require_parser = __commonJS({ + "node_modules/@iarna/toml/lib/parser.js"(exports2, module2) { + "use strict"; + var ParserEND = 1114112; + var ParserError = class _ParserError extends Error { + /* istanbul ignore next */ + constructor(msg, filename, linenumber) { + super("[ParserError] " + msg, filename, linenumber); + this.name = "ParserError"; + this.code = "ParserError"; + if (Error.captureStackTrace) + Error.captureStackTrace(this, _ParserError); + } + }; + var State = class { + constructor(parser) { + this.parser = parser; + this.buf = ""; + this.returned = null; + this.result = null; + this.resultTable = null; + this.resultArr = null; + } + }; + var Parser = class { + constructor() { + this.pos = 0; + this.col = 0; + this.line = 0; + this.obj = {}; + this.ctx = this.obj; + this.stack = []; + this._buf = ""; + this.char = null; + this.ii = 0; + this.state = new State(this.parseStart); + } + parse(str) { + if (str.length === 0 || str.length == null) + return; + this._buf = String(str); + this.ii = -1; + this.char = -1; + let getNext; + while (getNext === false || this.nextChar()) { + getNext = this.runOne(); + } + this._buf = null; + } + nextChar() { + if (this.char === 10) { + ++this.line; + this.col = -1; + } + ++this.ii; + this.char = this._buf.codePointAt(this.ii); + ++this.pos; + ++this.col; + return this.haveBuffer(); + } + haveBuffer() { + return this.ii < this._buf.length; + } + runOne() { + return this.state.parser.call(this, this.state.returned); + } + finish() { + this.char = ParserEND; + let last; + do { + last = this.state.parser; + this.runOne(); + } while (this.state.parser !== last); + this.ctx = null; + this.state = null; + this._buf = null; + return this.obj; + } + next(fn) { + if (typeof fn !== "function") + throw new ParserError("Tried to set state to non-existent state: " + JSON.stringify(fn)); + this.state.parser = fn; + } + goto(fn) { + this.next(fn); + return this.runOne(); + } + call(fn, returnWith) { + if (returnWith) + this.next(returnWith); + this.stack.push(this.state); + this.state = new State(fn); + } + callNow(fn, returnWith) { + this.call(fn, returnWith); + return this.runOne(); + } + return(value) { + if (this.stack.length === 0) + throw this.error(new ParserError("Stack underflow")); + if (value === void 0) + value = this.state.buf; + this.state = this.stack.pop(); + this.state.returned = value; + } + returnNow(value) { + this.return(value); + return this.runOne(); + } + consume() { + if (this.char === ParserEND) + throw this.error(new ParserError("Unexpected end-of-buffer")); + this.state.buf += this._buf[this.ii]; + } + error(err) { + err.line = this.line; + err.col = this.col; + err.pos = this.pos; + return err; + } + /* istanbul ignore next */ + parseStart() { + throw new ParserError("Must declare a parseStart method"); + } + }; + Parser.END = ParserEND; + Parser.Error = ParserError; + module2.exports = Parser; + } + }); + + // node_modules/@iarna/toml/lib/create-datetime.js + var require_create_datetime = __commonJS({ + "node_modules/@iarna/toml/lib/create-datetime.js"(exports2, module2) { + "use strict"; + module2.exports = (value) => { + const date = new Date(value); + if (isNaN(date)) { + throw new TypeError("Invalid Datetime"); + } else { + return date; + } + }; + } + }); + + // node_modules/@iarna/toml/lib/format-num.js + var require_format_num = __commonJS({ + "node_modules/@iarna/toml/lib/format-num.js"(exports2, module2) { + "use strict"; + module2.exports = (d, num) => { + num = String(num); + while (num.length < d) + num = "0" + num; + return num; + }; + } + }); + + // node_modules/@iarna/toml/lib/create-datetime-float.js + var require_create_datetime_float = __commonJS({ + "node_modules/@iarna/toml/lib/create-datetime-float.js"(exports2, module2) { + "use strict"; + var f = require_format_num(); + var FloatingDateTime = class extends Date { + constructor(value) { + super(value + "Z"); + this.isFloating = true; + } + toISOString() { + const date = `${this.getUTCFullYear()}-${f(2, this.getUTCMonth() + 1)}-${f(2, this.getUTCDate())}`; + const time = `${f(2, this.getUTCHours())}:${f(2, this.getUTCMinutes())}:${f(2, this.getUTCSeconds())}.${f(3, this.getUTCMilliseconds())}`; + return `${date}T${time}`; + } + }; + module2.exports = (value) => { + const date = new FloatingDateTime(value); + if (isNaN(date)) { + throw new TypeError("Invalid Datetime"); + } else { + return date; + } + }; + } + }); + + // node_modules/@iarna/toml/lib/create-date.js + var require_create_date = __commonJS({ + "node_modules/@iarna/toml/lib/create-date.js"(exports2, module2) { + "use strict"; + var f = require_format_num(); + var DateTime = globalThis.Date; + var Date2 = class extends DateTime { + constructor(value) { + super(value); + this.isDate = true; + } + toISOString() { + return `${this.getUTCFullYear()}-${f(2, this.getUTCMonth() + 1)}-${f(2, this.getUTCDate())}`; + } + }; + module2.exports = (value) => { + const date = new Date2(value); + if (isNaN(date)) { + throw new TypeError("Invalid Datetime"); + } else { + return date; + } + }; + } + }); + + // node_modules/@iarna/toml/lib/create-time.js + var require_create_time = __commonJS({ + "node_modules/@iarna/toml/lib/create-time.js"(exports2, module2) { + "use strict"; + var f = require_format_num(); + var Time = class extends Date { + constructor(value) { + super(`0000-01-01T${value}Z`); + this.isTime = true; + } + toISOString() { + return `${f(2, this.getUTCHours())}:${f(2, this.getUTCMinutes())}:${f(2, this.getUTCSeconds())}.${f(3, this.getUTCMilliseconds())}`; + } + }; + module2.exports = (value) => { + const date = new Time(value); + if (isNaN(date)) { + throw new TypeError("Invalid Datetime"); + } else { + return date; + } + }; + } + }); + + // node_modules/@iarna/toml/lib/toml-parser.js + var require_toml_parser = __commonJS({ + "node_modules/@iarna/toml/lib/toml-parser.js"(exports, module) { + "use strict"; + module.exports = makeParserClass(require_parser()); + module.exports.makeParserClass = makeParserClass; + var TomlError = class _TomlError extends Error { + constructor(msg) { + super(msg); + this.name = "TomlError"; + if (Error.captureStackTrace) + Error.captureStackTrace(this, _TomlError); + this.fromTOML = true; + this.wrapped = null; + } + }; + TomlError.wrap = (err) => { + const terr = new TomlError(err.message); + terr.code = err.code; + terr.wrapped = err; + return terr; + }; + module.exports.TomlError = TomlError; + var createDateTime = require_create_datetime(); + var createDateTimeFloat = require_create_datetime_float(); + var createDate = require_create_date(); + var createTime = require_create_time(); + var CTRL_I = 9; + var CTRL_J = 10; + var CTRL_M = 13; + var CTRL_CHAR_BOUNDARY = 31; + var CHAR_SP = 32; + var CHAR_QUOT = 34; + var CHAR_NUM = 35; + var CHAR_APOS = 39; + var CHAR_PLUS = 43; + var CHAR_COMMA = 44; + var CHAR_HYPHEN = 45; + var CHAR_PERIOD = 46; + var CHAR_0 = 48; + var CHAR_1 = 49; + var CHAR_7 = 55; + var CHAR_9 = 57; + var CHAR_COLON = 58; + var CHAR_EQUALS = 61; + var CHAR_A = 65; + var CHAR_E = 69; + var CHAR_F = 70; + var CHAR_T = 84; + var CHAR_U = 85; + var CHAR_Z = 90; + var CHAR_LOWBAR = 95; + var CHAR_a = 97; + var CHAR_b = 98; + var CHAR_e = 101; + var CHAR_f = 102; + var CHAR_i = 105; + var CHAR_l = 108; + var CHAR_n = 110; + var CHAR_o = 111; + var CHAR_r = 114; + var CHAR_s = 115; + var CHAR_t = 116; + var CHAR_u = 117; + var CHAR_x = 120; + var CHAR_z = 122; + var CHAR_LCUB = 123; + var CHAR_RCUB = 125; + var CHAR_LSQB = 91; + var CHAR_BSOL = 92; + var CHAR_RSQB = 93; + var CHAR_DEL = 127; + var SURROGATE_FIRST = 55296; + var SURROGATE_LAST = 57343; + var escapes = { + [CHAR_b]: "\b", + [CHAR_t]: " ", + [CHAR_n]: "\n", + [CHAR_f]: "\f", + [CHAR_r]: "\r", + [CHAR_QUOT]: '"', + [CHAR_BSOL]: "\\" + }; + function isDigit(cp) { + return cp >= CHAR_0 && cp <= CHAR_9; + } + function isHexit(cp) { + return cp >= CHAR_A && cp <= CHAR_F || cp >= CHAR_a && cp <= CHAR_f || cp >= CHAR_0 && cp <= CHAR_9; + } + function isBit(cp) { + return cp === CHAR_1 || cp === CHAR_0; + } + function isOctit(cp) { + return cp >= CHAR_0 && cp <= CHAR_7; + } + function isAlphaNumQuoteHyphen(cp) { + return cp >= CHAR_A && cp <= CHAR_Z || cp >= CHAR_a && cp <= CHAR_z || cp >= CHAR_0 && cp <= CHAR_9 || cp === CHAR_APOS || cp === CHAR_QUOT || cp === CHAR_LOWBAR || cp === CHAR_HYPHEN; + } + function isAlphaNumHyphen(cp) { + return cp >= CHAR_A && cp <= CHAR_Z || cp >= CHAR_a && cp <= CHAR_z || cp >= CHAR_0 && cp <= CHAR_9 || cp === CHAR_LOWBAR || cp === CHAR_HYPHEN; + } + var _type = Symbol("type"); + var _declared = Symbol("declared"); + var hasOwnProperty = Object.prototype.hasOwnProperty; + var defineProperty = Object.defineProperty; + var descriptor = { configurable: true, enumerable: true, writable: true, value: void 0 }; + function hasKey(obj, key) { + if (hasOwnProperty.call(obj, key)) + return true; + if (key === "__proto__") + defineProperty(obj, "__proto__", descriptor); + return false; + } + var INLINE_TABLE = Symbol("inline-table"); + function InlineTable() { + return Object.defineProperties({}, { + [_type]: { value: INLINE_TABLE } + }); + } + function isInlineTable(obj) { + if (obj === null || typeof obj !== "object") + return false; + return obj[_type] === INLINE_TABLE; + } + var TABLE = Symbol("table"); + function Table() { + return Object.defineProperties({}, { + [_type]: { value: TABLE }, + [_declared]: { value: false, writable: true } + }); + } + function isTable(obj) { + if (obj === null || typeof obj !== "object") + return false; + return obj[_type] === TABLE; + } + var _contentType = Symbol("content-type"); + var INLINE_LIST = Symbol("inline-list"); + function InlineList(type) { + return Object.defineProperties([], { + [_type]: { value: INLINE_LIST }, + [_contentType]: { value: type } + }); + } + function isInlineList(obj) { + if (obj === null || typeof obj !== "object") + return false; + return obj[_type] === INLINE_LIST; + } + var LIST = Symbol("list"); + function List() { + return Object.defineProperties([], { + [_type]: { value: LIST } + }); + } + function isList(obj) { + if (obj === null || typeof obj !== "object") + return false; + return obj[_type] === LIST; + } + var _custom; + try { + const utilInspect = eval("require('util').inspect"); + _custom = utilInspect.custom; + } catch (_) { + } + var _inspect = _custom || "inspect"; + var BoxedBigInt = class { + constructor(value) { + try { + this.value = globalThis.BigInt.asIntN(64, value); + } catch (_) { + this.value = null; + } + Object.defineProperty(this, _type, { value: INTEGER }); + } + isNaN() { + return this.value === null; + } + /* istanbul ignore next */ + toString() { + return String(this.value); + } + /* istanbul ignore next */ + [_inspect]() { + return `[BigInt: ${this.toString()}]}`; + } + valueOf() { + return this.value; + } + }; + var INTEGER = Symbol("integer"); + function Integer(value) { + let num = Number(value); + if (Object.is(num, -0)) + num = 0; + if (globalThis.BigInt && !Number.isSafeInteger(num)) { + return new BoxedBigInt(value); + } else { + return Object.defineProperties(new Number(num), { + isNaN: { value: function() { + return isNaN(this); + } }, + [_type]: { value: INTEGER }, + [_inspect]: { value: () => `[Integer: ${value}]` } + }); + } + } + function isInteger(obj) { + if (obj === null || typeof obj !== "object") + return false; + return obj[_type] === INTEGER; + } + var FLOAT = Symbol("float"); + function Float(value) { + return Object.defineProperties(new Number(value), { + [_type]: { value: FLOAT }, + [_inspect]: { value: () => `[Float: ${value}]` } + }); + } + function isFloat(obj) { + if (obj === null || typeof obj !== "object") + return false; + return obj[_type] === FLOAT; + } + function tomlType(value) { + const type = typeof value; + if (type === "object") { + if (value === null) + return "null"; + if (value instanceof Date) + return "datetime"; + if (_type in value) { + switch (value[_type]) { + case INLINE_TABLE: + return "inline-table"; + case INLINE_LIST: + return "inline-list"; + case TABLE: + return "table"; + case LIST: + return "list"; + case FLOAT: + return "float"; + case INTEGER: + return "integer"; + } + } + } + return type; + } + function makeParserClass(Parser) { + class TOMLParser extends Parser { + constructor() { + super(); + this.ctx = this.obj = Table(); + } + /* MATCH HELPER */ + atEndOfWord() { + return this.char === CHAR_NUM || this.char === CTRL_I || this.char === CHAR_SP || this.atEndOfLine(); + } + atEndOfLine() { + return this.char === Parser.END || this.char === CTRL_J || this.char === CTRL_M; + } + parseStart() { + if (this.char === Parser.END) { + return null; + } else if (this.char === CHAR_LSQB) { + return this.call(this.parseTableOrList); + } else if (this.char === CHAR_NUM) { + return this.call(this.parseComment); + } else if (this.char === CTRL_J || this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M) { + return null; + } else if (isAlphaNumQuoteHyphen(this.char)) { + return this.callNow(this.parseAssignStatement); + } else { + throw this.error(new TomlError(`Unknown character "${this.char}"`)); + } + } + // HELPER, this strips any whitespace and comments to the end of the line + // then RETURNS. Last state in a production. + parseWhitespaceToEOL() { + if (this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M) { + return null; + } else if (this.char === CHAR_NUM) { + return this.goto(this.parseComment); + } else if (this.char === Parser.END || this.char === CTRL_J) { + return this.return(); + } else { + throw this.error(new TomlError("Unexpected character, expected only whitespace or comments till end of line")); + } + } + /* ASSIGNMENT: key = value */ + parseAssignStatement() { + return this.callNow(this.parseAssign, this.recordAssignStatement); + } + recordAssignStatement(kv) { + let target = this.ctx; + let finalKey = kv.key.pop(); + for (let kw of kv.key) { + if (hasKey(target, kw) && (!isTable(target[kw]) || target[kw][_declared])) { + throw this.error(new TomlError("Can't redefine existing key")); + } + target = target[kw] = target[kw] || Table(); + } + if (hasKey(target, finalKey)) { + throw this.error(new TomlError("Can't redefine existing key")); + } + if (isInteger(kv.value) || isFloat(kv.value)) { + target[finalKey] = kv.value.valueOf(); + } else { + target[finalKey] = kv.value; + } + return this.goto(this.parseWhitespaceToEOL); + } + /* ASSSIGNMENT expression, key = value possibly inside an inline table */ + parseAssign() { + return this.callNow(this.parseKeyword, this.recordAssignKeyword); + } + recordAssignKeyword(key) { + if (this.state.resultTable) { + this.state.resultTable.push(key); + } else { + this.state.resultTable = [key]; + } + return this.goto(this.parseAssignKeywordPreDot); + } + parseAssignKeywordPreDot() { + if (this.char === CHAR_PERIOD) { + return this.next(this.parseAssignKeywordPostDot); + } else if (this.char !== CHAR_SP && this.char !== CTRL_I) { + return this.goto(this.parseAssignEqual); + } + } + parseAssignKeywordPostDot() { + if (this.char !== CHAR_SP && this.char !== CTRL_I) { + return this.callNow(this.parseKeyword, this.recordAssignKeyword); + } + } + parseAssignEqual() { + if (this.char === CHAR_EQUALS) { + return this.next(this.parseAssignPreValue); + } else { + throw this.error(new TomlError('Invalid character, expected "="')); + } + } + parseAssignPreValue() { + if (this.char === CHAR_SP || this.char === CTRL_I) { + return null; + } else { + return this.callNow(this.parseValue, this.recordAssignValue); + } + } + recordAssignValue(value) { + return this.returnNow({ key: this.state.resultTable, value }); + } + /* COMMENTS: #...eol */ + parseComment() { + do { + if (this.char === Parser.END || this.char === CTRL_J) { + return this.return(); + } + } while (this.nextChar()); + } + /* TABLES AND LISTS, [foo] and [[foo]] */ + parseTableOrList() { + if (this.char === CHAR_LSQB) { + this.next(this.parseList); + } else { + return this.goto(this.parseTable); + } + } + /* TABLE [foo.bar.baz] */ + parseTable() { + this.ctx = this.obj; + return this.goto(this.parseTableNext); + } + parseTableNext() { + if (this.char === CHAR_SP || this.char === CTRL_I) { + return null; + } else { + return this.callNow(this.parseKeyword, this.parseTableMore); + } + } + parseTableMore(keyword) { + if (this.char === CHAR_SP || this.char === CTRL_I) { + return null; + } else if (this.char === CHAR_RSQB) { + if (hasKey(this.ctx, keyword) && (!isTable(this.ctx[keyword]) || this.ctx[keyword][_declared])) { + throw this.error(new TomlError("Can't redefine existing key")); + } else { + this.ctx = this.ctx[keyword] = this.ctx[keyword] || Table(); + this.ctx[_declared] = true; + } + return this.next(this.parseWhitespaceToEOL); + } else if (this.char === CHAR_PERIOD) { + if (!hasKey(this.ctx, keyword)) { + this.ctx = this.ctx[keyword] = Table(); + } else if (isTable(this.ctx[keyword])) { + this.ctx = this.ctx[keyword]; + } else if (isList(this.ctx[keyword])) { + this.ctx = this.ctx[keyword][this.ctx[keyword].length - 1]; + } else { + throw this.error(new TomlError("Can't redefine existing key")); + } + return this.next(this.parseTableNext); + } else { + throw this.error(new TomlError("Unexpected character, expected whitespace, . or ]")); + } + } + /* LIST [[a.b.c]] */ + parseList() { + this.ctx = this.obj; + return this.goto(this.parseListNext); + } + parseListNext() { + if (this.char === CHAR_SP || this.char === CTRL_I) { + return null; + } else { + return this.callNow(this.parseKeyword, this.parseListMore); + } + } + parseListMore(keyword) { + if (this.char === CHAR_SP || this.char === CTRL_I) { + return null; + } else if (this.char === CHAR_RSQB) { + if (!hasKey(this.ctx, keyword)) { + this.ctx[keyword] = List(); + } + if (isInlineList(this.ctx[keyword])) { + throw this.error(new TomlError("Can't extend an inline array")); + } else if (isList(this.ctx[keyword])) { + const next = Table(); + this.ctx[keyword].push(next); + this.ctx = next; + } else { + throw this.error(new TomlError("Can't redefine an existing key")); + } + return this.next(this.parseListEnd); + } else if (this.char === CHAR_PERIOD) { + if (!hasKey(this.ctx, keyword)) { + this.ctx = this.ctx[keyword] = Table(); + } else if (isInlineList(this.ctx[keyword])) { + throw this.error(new TomlError("Can't extend an inline array")); + } else if (isInlineTable(this.ctx[keyword])) { + throw this.error(new TomlError("Can't extend an inline table")); + } else if (isList(this.ctx[keyword])) { + this.ctx = this.ctx[keyword][this.ctx[keyword].length - 1]; + } else if (isTable(this.ctx[keyword])) { + this.ctx = this.ctx[keyword]; + } else { + throw this.error(new TomlError("Can't redefine an existing key")); + } + return this.next(this.parseListNext); + } else { + throw this.error(new TomlError("Unexpected character, expected whitespace, . or ]")); + } + } + parseListEnd(keyword) { + if (this.char === CHAR_RSQB) { + return this.next(this.parseWhitespaceToEOL); + } else { + throw this.error(new TomlError("Unexpected character, expected whitespace, . or ]")); + } + } + /* VALUE string, number, boolean, inline list, inline object */ + parseValue() { + if (this.char === Parser.END) { + throw this.error(new TomlError("Key without value")); + } else if (this.char === CHAR_QUOT) { + return this.next(this.parseDoubleString); + } + if (this.char === CHAR_APOS) { + return this.next(this.parseSingleString); + } else if (this.char === CHAR_HYPHEN || this.char === CHAR_PLUS) { + return this.goto(this.parseNumberSign); + } else if (this.char === CHAR_i) { + return this.next(this.parseInf); + } else if (this.char === CHAR_n) { + return this.next(this.parseNan); + } else if (isDigit(this.char)) { + return this.goto(this.parseNumberOrDateTime); + } else if (this.char === CHAR_t || this.char === CHAR_f) { + return this.goto(this.parseBoolean); + } else if (this.char === CHAR_LSQB) { + return this.call(this.parseInlineList, this.recordValue); + } else if (this.char === CHAR_LCUB) { + return this.call(this.parseInlineTable, this.recordValue); + } else { + throw this.error(new TomlError("Unexpected character, expecting string, number, datetime, boolean, inline array or inline table")); + } + } + recordValue(value) { + return this.returnNow(value); + } + parseInf() { + if (this.char === CHAR_n) { + return this.next(this.parseInf2); + } else { + throw this.error(new TomlError('Unexpected character, expected "inf", "+inf" or "-inf"')); + } + } + parseInf2() { + if (this.char === CHAR_f) { + if (this.state.buf === "-") { + return this.return(-Infinity); + } else { + return this.return(Infinity); + } + } else { + throw this.error(new TomlError('Unexpected character, expected "inf", "+inf" or "-inf"')); + } + } + parseNan() { + if (this.char === CHAR_a) { + return this.next(this.parseNan2); + } else { + throw this.error(new TomlError('Unexpected character, expected "nan"')); + } + } + parseNan2() { + if (this.char === CHAR_n) { + return this.return(NaN); + } else { + throw this.error(new TomlError('Unexpected character, expected "nan"')); + } + } + /* KEYS, barewords or basic, literal, or dotted */ + parseKeyword() { + if (this.char === CHAR_QUOT) { + return this.next(this.parseBasicString); + } else if (this.char === CHAR_APOS) { + return this.next(this.parseLiteralString); + } else { + return this.goto(this.parseBareKey); + } + } + /* KEYS: barewords */ + parseBareKey() { + do { + if (this.char === Parser.END) { + throw this.error(new TomlError("Key ended without value")); + } else if (isAlphaNumHyphen(this.char)) { + this.consume(); + } else if (this.state.buf.length === 0) { + throw this.error(new TomlError("Empty bare keys are not allowed")); + } else { + return this.returnNow(); + } + } while (this.nextChar()); + } + /* STRINGS, single quoted (literal) */ + parseSingleString() { + if (this.char === CHAR_APOS) { + return this.next(this.parseLiteralMultiStringMaybe); + } else { + return this.goto(this.parseLiteralString); + } + } + parseLiteralString() { + do { + if (this.char === CHAR_APOS) { + return this.return(); + } else if (this.atEndOfLine()) { + throw this.error(new TomlError("Unterminated string")); + } else if (this.char === CHAR_DEL || this.char <= CTRL_CHAR_BOUNDARY && this.char !== CTRL_I) { + throw this.errorControlCharInString(); + } else { + this.consume(); + } + } while (this.nextChar()); + } + parseLiteralMultiStringMaybe() { + if (this.char === CHAR_APOS) { + return this.next(this.parseLiteralMultiString); + } else { + return this.returnNow(); + } + } + parseLiteralMultiString() { + if (this.char === CTRL_M) { + return null; + } else if (this.char === CTRL_J) { + return this.next(this.parseLiteralMultiStringContent); + } else { + return this.goto(this.parseLiteralMultiStringContent); + } + } + parseLiteralMultiStringContent() { + do { + if (this.char === CHAR_APOS) { + return this.next(this.parseLiteralMultiEnd); + } else if (this.char === Parser.END) { + throw this.error(new TomlError("Unterminated multi-line string")); + } else if (this.char === CHAR_DEL || this.char <= CTRL_CHAR_BOUNDARY && this.char !== CTRL_I && this.char !== CTRL_J && this.char !== CTRL_M) { + throw this.errorControlCharInString(); + } else { + this.consume(); + } + } while (this.nextChar()); + } + parseLiteralMultiEnd() { + if (this.char === CHAR_APOS) { + return this.next(this.parseLiteralMultiEnd2); + } else { + this.state.buf += "'"; + return this.goto(this.parseLiteralMultiStringContent); + } + } + parseLiteralMultiEnd2() { + if (this.char === CHAR_APOS) { + return this.return(); + } else { + this.state.buf += "''"; + return this.goto(this.parseLiteralMultiStringContent); + } + } + /* STRINGS double quoted */ + parseDoubleString() { + if (this.char === CHAR_QUOT) { + return this.next(this.parseMultiStringMaybe); + } else { + return this.goto(this.parseBasicString); + } + } + parseBasicString() { + do { + if (this.char === CHAR_BSOL) { + return this.call(this.parseEscape, this.recordEscapeReplacement); + } else if (this.char === CHAR_QUOT) { + return this.return(); + } else if (this.atEndOfLine()) { + throw this.error(new TomlError("Unterminated string")); + } else if (this.char === CHAR_DEL || this.char <= CTRL_CHAR_BOUNDARY && this.char !== CTRL_I) { + throw this.errorControlCharInString(); + } else { + this.consume(); + } + } while (this.nextChar()); + } + recordEscapeReplacement(replacement) { + this.state.buf += replacement; + return this.goto(this.parseBasicString); + } + parseMultiStringMaybe() { + if (this.char === CHAR_QUOT) { + return this.next(this.parseMultiString); + } else { + return this.returnNow(); + } + } + parseMultiString() { + if (this.char === CTRL_M) { + return null; + } else if (this.char === CTRL_J) { + return this.next(this.parseMultiStringContent); + } else { + return this.goto(this.parseMultiStringContent); + } + } + parseMultiStringContent() { + do { + if (this.char === CHAR_BSOL) { + return this.call(this.parseMultiEscape, this.recordMultiEscapeReplacement); + } else if (this.char === CHAR_QUOT) { + return this.next(this.parseMultiEnd); + } else if (this.char === Parser.END) { + throw this.error(new TomlError("Unterminated multi-line string")); + } else if (this.char === CHAR_DEL || this.char <= CTRL_CHAR_BOUNDARY && this.char !== CTRL_I && this.char !== CTRL_J && this.char !== CTRL_M) { + throw this.errorControlCharInString(); + } else { + this.consume(); + } + } while (this.nextChar()); + } + errorControlCharInString() { + let displayCode = "\\u00"; + if (this.char < 16) { + displayCode += "0"; + } + displayCode += this.char.toString(16); + return this.error(new TomlError(`Control characters (codes < 0x1f and 0x7f) are not allowed in strings, use ${displayCode} instead`)); + } + recordMultiEscapeReplacement(replacement) { + this.state.buf += replacement; + return this.goto(this.parseMultiStringContent); + } + parseMultiEnd() { + if (this.char === CHAR_QUOT) { + return this.next(this.parseMultiEnd2); + } else { + this.state.buf += '"'; + return this.goto(this.parseMultiStringContent); + } + } + parseMultiEnd2() { + if (this.char === CHAR_QUOT) { + return this.return(); + } else { + this.state.buf += '""'; + return this.goto(this.parseMultiStringContent); + } + } + parseMultiEscape() { + if (this.char === CTRL_M || this.char === CTRL_J) { + return this.next(this.parseMultiTrim); + } else if (this.char === CHAR_SP || this.char === CTRL_I) { + return this.next(this.parsePreMultiTrim); + } else { + return this.goto(this.parseEscape); + } + } + parsePreMultiTrim() { + if (this.char === CHAR_SP || this.char === CTRL_I) { + return null; + } else if (this.char === CTRL_M || this.char === CTRL_J) { + return this.next(this.parseMultiTrim); + } else { + throw this.error(new TomlError("Can't escape whitespace")); + } + } + parseMultiTrim() { + if (this.char === CTRL_J || this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M) { + return null; + } else { + return this.returnNow(); + } + } + parseEscape() { + if (this.char in escapes) { + return this.return(escapes[this.char]); + } else if (this.char === CHAR_u) { + return this.call(this.parseSmallUnicode, this.parseUnicodeReturn); + } else if (this.char === CHAR_U) { + return this.call(this.parseLargeUnicode, this.parseUnicodeReturn); + } else { + throw this.error(new TomlError("Unknown escape character: " + this.char)); + } + } + parseUnicodeReturn(char) { + try { + const codePoint = parseInt(char, 16); + if (codePoint >= SURROGATE_FIRST && codePoint <= SURROGATE_LAST) { + throw this.error(new TomlError("Invalid unicode, character in range 0xD800 - 0xDFFF is reserved")); + } + return this.returnNow(String.fromCodePoint(codePoint)); + } catch (err) { + throw this.error(TomlError.wrap(err)); + } + } + parseSmallUnicode() { + if (!isHexit(this.char)) { + throw this.error(new TomlError("Invalid character in unicode sequence, expected hex")); + } else { + this.consume(); + if (this.state.buf.length >= 4) + return this.return(); + } + } + parseLargeUnicode() { + if (!isHexit(this.char)) { + throw this.error(new TomlError("Invalid character in unicode sequence, expected hex")); + } else { + this.consume(); + if (this.state.buf.length >= 8) + return this.return(); + } + } + /* NUMBERS */ + parseNumberSign() { + this.consume(); + return this.next(this.parseMaybeSignedInfOrNan); + } + parseMaybeSignedInfOrNan() { + if (this.char === CHAR_i) { + return this.next(this.parseInf); + } else if (this.char === CHAR_n) { + return this.next(this.parseNan); + } else { + return this.callNow(this.parseNoUnder, this.parseNumberIntegerStart); + } + } + parseNumberIntegerStart() { + if (this.char === CHAR_0) { + this.consume(); + return this.next(this.parseNumberIntegerExponentOrDecimal); + } else { + return this.goto(this.parseNumberInteger); + } + } + parseNumberIntegerExponentOrDecimal() { + if (this.char === CHAR_PERIOD) { + this.consume(); + return this.call(this.parseNoUnder, this.parseNumberFloat); + } else if (this.char === CHAR_E || this.char === CHAR_e) { + this.consume(); + return this.next(this.parseNumberExponentSign); + } else { + return this.returnNow(Integer(this.state.buf)); + } + } + parseNumberInteger() { + if (isDigit(this.char)) { + this.consume(); + } else if (this.char === CHAR_LOWBAR) { + return this.call(this.parseNoUnder); + } else if (this.char === CHAR_E || this.char === CHAR_e) { + this.consume(); + return this.next(this.parseNumberExponentSign); + } else if (this.char === CHAR_PERIOD) { + this.consume(); + return this.call(this.parseNoUnder, this.parseNumberFloat); + } else { + const result = Integer(this.state.buf); + if (result.isNaN()) { + throw this.error(new TomlError("Invalid number")); + } else { + return this.returnNow(result); + } + } + } + parseNoUnder() { + if (this.char === CHAR_LOWBAR || this.char === CHAR_PERIOD || this.char === CHAR_E || this.char === CHAR_e) { + throw this.error(new TomlError("Unexpected character, expected digit")); + } else if (this.atEndOfWord()) { + throw this.error(new TomlError("Incomplete number")); + } + return this.returnNow(); + } + parseNoUnderHexOctBinLiteral() { + if (this.char === CHAR_LOWBAR || this.char === CHAR_PERIOD) { + throw this.error(new TomlError("Unexpected character, expected digit")); + } else if (this.atEndOfWord()) { + throw this.error(new TomlError("Incomplete number")); + } + return this.returnNow(); + } + parseNumberFloat() { + if (this.char === CHAR_LOWBAR) { + return this.call(this.parseNoUnder, this.parseNumberFloat); + } else if (isDigit(this.char)) { + this.consume(); + } else if (this.char === CHAR_E || this.char === CHAR_e) { + this.consume(); + return this.next(this.parseNumberExponentSign); + } else { + return this.returnNow(Float(this.state.buf)); + } + } + parseNumberExponentSign() { + if (isDigit(this.char)) { + return this.goto(this.parseNumberExponent); + } else if (this.char === CHAR_HYPHEN || this.char === CHAR_PLUS) { + this.consume(); + this.call(this.parseNoUnder, this.parseNumberExponent); + } else { + throw this.error(new TomlError("Unexpected character, expected -, + or digit")); + } + } + parseNumberExponent() { + if (isDigit(this.char)) { + this.consume(); + } else if (this.char === CHAR_LOWBAR) { + return this.call(this.parseNoUnder); + } else { + return this.returnNow(Float(this.state.buf)); + } + } + /* NUMBERS or DATETIMES */ + parseNumberOrDateTime() { + if (this.char === CHAR_0) { + this.consume(); + return this.next(this.parseNumberBaseOrDateTime); + } else { + return this.goto(this.parseNumberOrDateTimeOnly); + } + } + parseNumberOrDateTimeOnly() { + if (this.char === CHAR_LOWBAR) { + return this.call(this.parseNoUnder, this.parseNumberInteger); + } else if (isDigit(this.char)) { + this.consume(); + if (this.state.buf.length > 4) + this.next(this.parseNumberInteger); + } else if (this.char === CHAR_E || this.char === CHAR_e) { + this.consume(); + return this.next(this.parseNumberExponentSign); + } else if (this.char === CHAR_PERIOD) { + this.consume(); + return this.call(this.parseNoUnder, this.parseNumberFloat); + } else if (this.char === CHAR_HYPHEN) { + return this.goto(this.parseDateTime); + } else if (this.char === CHAR_COLON) { + return this.goto(this.parseOnlyTimeHour); + } else { + return this.returnNow(Integer(this.state.buf)); + } + } + parseDateTimeOnly() { + if (this.state.buf.length < 4) { + if (isDigit(this.char)) { + return this.consume(); + } else if (this.char === CHAR_COLON) { + return this.goto(this.parseOnlyTimeHour); + } else { + throw this.error(new TomlError("Expected digit while parsing year part of a date")); + } + } else { + if (this.char === CHAR_HYPHEN) { + return this.goto(this.parseDateTime); + } else { + throw this.error(new TomlError("Expected hyphen (-) while parsing year part of date")); + } + } + } + parseNumberBaseOrDateTime() { + if (this.char === CHAR_b) { + this.consume(); + return this.call(this.parseNoUnderHexOctBinLiteral, this.parseIntegerBin); + } else if (this.char === CHAR_o) { + this.consume(); + return this.call(this.parseNoUnderHexOctBinLiteral, this.parseIntegerOct); + } else if (this.char === CHAR_x) { + this.consume(); + return this.call(this.parseNoUnderHexOctBinLiteral, this.parseIntegerHex); + } else if (this.char === CHAR_PERIOD) { + return this.goto(this.parseNumberInteger); + } else if (isDigit(this.char)) { + return this.goto(this.parseDateTimeOnly); + } else { + return this.returnNow(Integer(this.state.buf)); + } + } + parseIntegerHex() { + if (isHexit(this.char)) { + this.consume(); + } else if (this.char === CHAR_LOWBAR) { + return this.call(this.parseNoUnderHexOctBinLiteral); + } else { + const result = Integer(this.state.buf); + if (result.isNaN()) { + throw this.error(new TomlError("Invalid number")); + } else { + return this.returnNow(result); + } + } + } + parseIntegerOct() { + if (isOctit(this.char)) { + this.consume(); + } else if (this.char === CHAR_LOWBAR) { + return this.call(this.parseNoUnderHexOctBinLiteral); + } else { + const result = Integer(this.state.buf); + if (result.isNaN()) { + throw this.error(new TomlError("Invalid number")); + } else { + return this.returnNow(result); + } + } + } + parseIntegerBin() { + if (isBit(this.char)) { + this.consume(); + } else if (this.char === CHAR_LOWBAR) { + return this.call(this.parseNoUnderHexOctBinLiteral); + } else { + const result = Integer(this.state.buf); + if (result.isNaN()) { + throw this.error(new TomlError("Invalid number")); + } else { + return this.returnNow(result); + } + } + } + /* DATETIME */ + parseDateTime() { + if (this.state.buf.length < 4) { + throw this.error(new TomlError("Years less than 1000 must be zero padded to four characters")); + } + this.state.result = this.state.buf; + this.state.buf = ""; + return this.next(this.parseDateMonth); + } + parseDateMonth() { + if (this.char === CHAR_HYPHEN) { + if (this.state.buf.length < 2) { + throw this.error(new TomlError("Months less than 10 must be zero padded to two characters")); + } + this.state.result += "-" + this.state.buf; + this.state.buf = ""; + return this.next(this.parseDateDay); + } else if (isDigit(this.char)) { + this.consume(); + } else { + throw this.error(new TomlError("Incomplete datetime")); + } + } + parseDateDay() { + if (this.char === CHAR_T || this.char === CHAR_SP) { + if (this.state.buf.length < 2) { + throw this.error(new TomlError("Days less than 10 must be zero padded to two characters")); + } + this.state.result += "-" + this.state.buf; + this.state.buf = ""; + return this.next(this.parseStartTimeHour); + } else if (this.atEndOfWord()) { + return this.returnNow(createDate(this.state.result + "-" + this.state.buf)); + } else if (isDigit(this.char)) { + this.consume(); + } else { + throw this.error(new TomlError("Incomplete datetime")); + } + } + parseStartTimeHour() { + if (this.atEndOfWord()) { + return this.returnNow(createDate(this.state.result)); + } else { + return this.goto(this.parseTimeHour); + } + } + parseTimeHour() { + if (this.char === CHAR_COLON) { + if (this.state.buf.length < 2) { + throw this.error(new TomlError("Hours less than 10 must be zero padded to two characters")); + } + this.state.result += "T" + this.state.buf; + this.state.buf = ""; + return this.next(this.parseTimeMin); + } else if (isDigit(this.char)) { + this.consume(); + } else { + throw this.error(new TomlError("Incomplete datetime")); + } + } + parseTimeMin() { + if (this.state.buf.length < 2 && isDigit(this.char)) { + this.consume(); + } else if (this.state.buf.length === 2 && this.char === CHAR_COLON) { + this.state.result += ":" + this.state.buf; + this.state.buf = ""; + return this.next(this.parseTimeSec); + } else { + throw this.error(new TomlError("Incomplete datetime")); + } + } + parseTimeSec() { + if (isDigit(this.char)) { + this.consume(); + if (this.state.buf.length === 2) { + this.state.result += ":" + this.state.buf; + this.state.buf = ""; + return this.next(this.parseTimeZoneOrFraction); + } + } else { + throw this.error(new TomlError("Incomplete datetime")); + } + } + parseOnlyTimeHour() { + if (this.char === CHAR_COLON) { + if (this.state.buf.length < 2) { + throw this.error(new TomlError("Hours less than 10 must be zero padded to two characters")); + } + this.state.result = this.state.buf; + this.state.buf = ""; + return this.next(this.parseOnlyTimeMin); + } else { + throw this.error(new TomlError("Incomplete time")); + } + } + parseOnlyTimeMin() { + if (this.state.buf.length < 2 && isDigit(this.char)) { + this.consume(); + } else if (this.state.buf.length === 2 && this.char === CHAR_COLON) { + this.state.result += ":" + this.state.buf; + this.state.buf = ""; + return this.next(this.parseOnlyTimeSec); + } else { + throw this.error(new TomlError("Incomplete time")); + } + } + parseOnlyTimeSec() { + if (isDigit(this.char)) { + this.consume(); + if (this.state.buf.length === 2) { + return this.next(this.parseOnlyTimeFractionMaybe); + } + } else { + throw this.error(new TomlError("Incomplete time")); + } + } + parseOnlyTimeFractionMaybe() { + this.state.result += ":" + this.state.buf; + if (this.char === CHAR_PERIOD) { + this.state.buf = ""; + this.next(this.parseOnlyTimeFraction); + } else { + return this.return(createTime(this.state.result)); + } + } + parseOnlyTimeFraction() { + if (isDigit(this.char)) { + this.consume(); + } else if (this.atEndOfWord()) { + if (this.state.buf.length === 0) + throw this.error(new TomlError("Expected digit in milliseconds")); + return this.returnNow(createTime(this.state.result + "." + this.state.buf)); + } else { + throw this.error(new TomlError("Unexpected character in datetime, expected period (.), minus (-), plus (+) or Z")); + } + } + parseTimeZoneOrFraction() { + if (this.char === CHAR_PERIOD) { + this.consume(); + this.next(this.parseDateTimeFraction); + } else if (this.char === CHAR_HYPHEN || this.char === CHAR_PLUS) { + this.consume(); + this.next(this.parseTimeZoneHour); + } else if (this.char === CHAR_Z) { + this.consume(); + return this.return(createDateTime(this.state.result + this.state.buf)); + } else if (this.atEndOfWord()) { + return this.returnNow(createDateTimeFloat(this.state.result + this.state.buf)); + } else { + throw this.error(new TomlError("Unexpected character in datetime, expected period (.), minus (-), plus (+) or Z")); + } + } + parseDateTimeFraction() { + if (isDigit(this.char)) { + this.consume(); + } else if (this.state.buf.length === 1) { + throw this.error(new TomlError("Expected digit in milliseconds")); + } else if (this.char === CHAR_HYPHEN || this.char === CHAR_PLUS) { + this.consume(); + this.next(this.parseTimeZoneHour); + } else if (this.char === CHAR_Z) { + this.consume(); + return this.return(createDateTime(this.state.result + this.state.buf)); + } else if (this.atEndOfWord()) { + return this.returnNow(createDateTimeFloat(this.state.result + this.state.buf)); + } else { + throw this.error(new TomlError("Unexpected character in datetime, expected period (.), minus (-), plus (+) or Z")); + } + } + parseTimeZoneHour() { + if (isDigit(this.char)) { + this.consume(); + if (/\d\d$/.test(this.state.buf)) + return this.next(this.parseTimeZoneSep); + } else { + throw this.error(new TomlError("Unexpected character in datetime, expected digit")); + } + } + parseTimeZoneSep() { + if (this.char === CHAR_COLON) { + this.consume(); + this.next(this.parseTimeZoneMin); + } else { + throw this.error(new TomlError("Unexpected character in datetime, expected colon")); + } + } + parseTimeZoneMin() { + if (isDigit(this.char)) { + this.consume(); + if (/\d\d$/.test(this.state.buf)) + return this.return(createDateTime(this.state.result + this.state.buf)); + } else { + throw this.error(new TomlError("Unexpected character in datetime, expected digit")); + } + } + /* BOOLEAN */ + parseBoolean() { + if (this.char === CHAR_t) { + this.consume(); + return this.next(this.parseTrue_r); + } else if (this.char === CHAR_f) { + this.consume(); + return this.next(this.parseFalse_a); + } + } + parseTrue_r() { + if (this.char === CHAR_r) { + this.consume(); + return this.next(this.parseTrue_u); + } else { + throw this.error(new TomlError("Invalid boolean, expected true or false")); + } + } + parseTrue_u() { + if (this.char === CHAR_u) { + this.consume(); + return this.next(this.parseTrue_e); + } else { + throw this.error(new TomlError("Invalid boolean, expected true or false")); + } + } + parseTrue_e() { + if (this.char === CHAR_e) { + return this.return(true); + } else { + throw this.error(new TomlError("Invalid boolean, expected true or false")); + } + } + parseFalse_a() { + if (this.char === CHAR_a) { + this.consume(); + return this.next(this.parseFalse_l); + } else { + throw this.error(new TomlError("Invalid boolean, expected true or false")); + } + } + parseFalse_l() { + if (this.char === CHAR_l) { + this.consume(); + return this.next(this.parseFalse_s); + } else { + throw this.error(new TomlError("Invalid boolean, expected true or false")); + } + } + parseFalse_s() { + if (this.char === CHAR_s) { + this.consume(); + return this.next(this.parseFalse_e); + } else { + throw this.error(new TomlError("Invalid boolean, expected true or false")); + } + } + parseFalse_e() { + if (this.char === CHAR_e) { + return this.return(false); + } else { + throw this.error(new TomlError("Invalid boolean, expected true or false")); + } + } + /* INLINE LISTS */ + parseInlineList() { + if (this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M || this.char === CTRL_J) { + return null; + } else if (this.char === Parser.END) { + throw this.error(new TomlError("Unterminated inline array")); + } else if (this.char === CHAR_NUM) { + return this.call(this.parseComment); + } else if (this.char === CHAR_RSQB) { + return this.return(this.state.resultArr || InlineList()); + } else { + return this.callNow(this.parseValue, this.recordInlineListValue); + } + } + recordInlineListValue(value) { + if (this.state.resultArr) { + const listType = this.state.resultArr[_contentType]; + const valueType = tomlType(value); + if (listType !== valueType) { + throw this.error(new TomlError(`Inline lists must be a single type, not a mix of ${listType} and ${valueType}`)); + } + } else { + this.state.resultArr = InlineList(tomlType(value)); + } + if (isFloat(value) || isInteger(value)) { + this.state.resultArr.push(value.valueOf()); + } else { + this.state.resultArr.push(value); + } + return this.goto(this.parseInlineListNext); + } + parseInlineListNext() { + if (this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M || this.char === CTRL_J) { + return null; + } else if (this.char === CHAR_NUM) { + return this.call(this.parseComment); + } else if (this.char === CHAR_COMMA) { + return this.next(this.parseInlineList); + } else if (this.char === CHAR_RSQB) { + return this.goto(this.parseInlineList); + } else { + throw this.error(new TomlError("Invalid character, expected whitespace, comma (,) or close bracket (])")); + } + } + /* INLINE TABLE */ + parseInlineTable() { + if (this.char === CHAR_SP || this.char === CTRL_I) { + return null; + } else if (this.char === Parser.END || this.char === CHAR_NUM || this.char === CTRL_J || this.char === CTRL_M) { + throw this.error(new TomlError("Unterminated inline array")); + } else if (this.char === CHAR_RCUB) { + return this.return(this.state.resultTable || InlineTable()); + } else { + if (!this.state.resultTable) + this.state.resultTable = InlineTable(); + return this.callNow(this.parseAssign, this.recordInlineTableValue); + } + } + recordInlineTableValue(kv) { + let target = this.state.resultTable; + let finalKey = kv.key.pop(); + for (let kw of kv.key) { + if (hasKey(target, kw) && (!isTable(target[kw]) || target[kw][_declared])) { + throw this.error(new TomlError("Can't redefine existing key")); + } + target = target[kw] = target[kw] || Table(); + } + if (hasKey(target, finalKey)) { + throw this.error(new TomlError("Can't redefine existing key")); + } + if (isInteger(kv.value) || isFloat(kv.value)) { + target[finalKey] = kv.value.valueOf(); + } else { + target[finalKey] = kv.value; + } + return this.goto(this.parseInlineTableNext); + } + parseInlineTableNext() { + if (this.char === CHAR_SP || this.char === CTRL_I) { + return null; + } else if (this.char === Parser.END || this.char === CHAR_NUM || this.char === CTRL_J || this.char === CTRL_M) { + throw this.error(new TomlError("Unterminated inline array")); + } else if (this.char === CHAR_COMMA) { + return this.next(this.parseInlineTable); + } else if (this.char === CHAR_RCUB) { + return this.goto(this.parseInlineTable); + } else { + throw this.error(new TomlError("Invalid character, expected whitespace, comma (,) or close bracket (])")); + } + } + } + return TOMLParser; + } + } + }); + + // node_modules/@iarna/toml/parse-pretty-error.js + var require_parse_pretty_error = __commonJS({ + "node_modules/@iarna/toml/parse-pretty-error.js"(exports2, module2) { + "use strict"; + module2.exports = prettyError; + function prettyError(err, buf) { + if (err.pos == null || err.line == null) + return err; + let msg = err.message; + msg += ` at row ${err.line + 1}, col ${err.col + 1}, pos ${err.pos}: +`; + if (buf && buf.split) { + const lines = buf.split(/\n/); + const lineNumWidth = String(Math.min(lines.length, err.line + 3)).length; + let linePadding = " "; + while (linePadding.length < lineNumWidth) + linePadding += " "; + for (let ii = Math.max(0, err.line - 1); ii < Math.min(lines.length, err.line + 2); ++ii) { + let lineNum = String(ii + 1); + if (lineNum.length < lineNumWidth) + lineNum = " " + lineNum; + if (err.line === ii) { + msg += lineNum + "> " + lines[ii] + "\n"; + msg += linePadding + " "; + for (let hh = 0; hh < err.col; ++hh) { + msg += " "; + } + msg += "^\n"; + } else { + msg += lineNum + ": " + lines[ii] + "\n"; + } + } + } + err.message = msg + "\n"; + return err; + } + } + }); + + // node_modules/@iarna/toml/parse-string.js + var require_parse_string = __commonJS({ + "node_modules/@iarna/toml/parse-string.js"(exports2, module2) { + "use strict"; + module2.exports = parseString; + var TOMLParser = require_toml_parser(); + var prettyError = require_parse_pretty_error(); + function parseString(str) { + if (globalThis.Buffer && globalThis.Buffer.isBuffer(str)) { + str = str.toString("utf8"); + } + const parser = new TOMLParser(); + try { + parser.parse(str); + return parser.finish(); + } catch (err) { + throw prettyError(err, str); + } + } + } + }); + + // node_modules/@iarna/toml/parse-async.js + var require_parse_async = __commonJS({ + "node_modules/@iarna/toml/parse-async.js"(exports2, module2) { + "use strict"; + module2.exports = parseAsync; + var TOMLParser = require_toml_parser(); + var prettyError = require_parse_pretty_error(); + function parseAsync(str, opts) { + if (!opts) + opts = {}; + const index = 0; + const blocksize = opts.blocksize || 40960; + const parser = new TOMLParser(); + return new Promise((resolve, reject) => { + setImmediate(parseAsyncNext, index, blocksize, resolve, reject); + }); + function parseAsyncNext(index2, blocksize2, resolve, reject) { + if (index2 >= str.length) { + try { + return resolve(parser.finish()); + } catch (err) { + return reject(prettyError(err, str)); + } + } + try { + parser.parse(str.slice(index2, index2 + blocksize2)); + setImmediate(parseAsyncNext, index2 + blocksize2, blocksize2, resolve, reject); + } catch (err) { + reject(prettyError(err, str)); + } + } + } + } + }); + + // node_modules/component-emitter/index.js + var require_component_emitter = __commonJS({ + "node_modules/component-emitter/index.js"(exports2, module2) { + function Emitter(object) { + if (object) { + return mixin(object); + } + this._callbacks = /* @__PURE__ */ new Map(); + } + function mixin(object) { + Object.assign(object, Emitter.prototype); + object._callbacks = /* @__PURE__ */ new Map(); + return object; + } + Emitter.prototype.on = function(event, listener) { + const callbacks = this._callbacks.get(event) ?? []; + callbacks.push(listener); + this._callbacks.set(event, callbacks); + return this; + }; + Emitter.prototype.once = function(event, listener) { + const on = (...arguments_) => { + this.off(event, on); + listener.apply(this, arguments_); + }; + on.fn = listener; + this.on(event, on); + return this; + }; + Emitter.prototype.off = function(event, listener) { + if (event === void 0 && listener === void 0) { + this._callbacks.clear(); + return this; + } + if (listener === void 0) { + this._callbacks.delete(event); + return this; + } + const callbacks = this._callbacks.get(event); + if (callbacks) { + for (const [index, callback] of callbacks.entries()) { + if (callback === listener || callback.fn === listener) { + callbacks.splice(index, 1); + break; + } + } + if (callbacks.length === 0) { + this._callbacks.delete(event); + } else { + this._callbacks.set(event, callbacks); + } + } + return this; + }; + Emitter.prototype.emit = function(event, ...arguments_) { + const callbacks = this._callbacks.get(event); + if (callbacks) { + const callbacksCopy = [...callbacks]; + for (const callback of callbacksCopy) { + callback.apply(this, arguments_); + } + } + return this; + }; + Emitter.prototype.listeners = function(event) { + return this._callbacks.get(event) ?? []; + }; + Emitter.prototype.listenerCount = function(event) { + if (event) { + return this.listeners(event).length; + } + let totalCount = 0; + for (const callbacks of this._callbacks.values()) { + totalCount += callbacks.length; + } + return totalCount; + }; + Emitter.prototype.hasListeners = function(event) { + return this.listenerCount(event) > 0; + }; + Emitter.prototype.addEventListener = Emitter.prototype.on; + Emitter.prototype.removeListener = Emitter.prototype.off; + Emitter.prototype.removeEventListener = Emitter.prototype.off; + Emitter.prototype.removeAllListeners = Emitter.prototype.off; + if (typeof module2 !== "undefined") { + module2.exports = Emitter; + } + } + }); + + // node_modules/stream/index.js + var require_stream = __commonJS({ + "node_modules/stream/index.js"(exports2, module2) { + var Emitter = require_component_emitter(); + function Stream() { + Emitter.call(this); + } + Stream.prototype = new Emitter(); + module2.exports = Stream; + Stream.Stream = Stream; + Stream.prototype.pipe = function(dest, options) { + var source = this; + function ondata(chunk) { + if (dest.writable) { + if (false === dest.write(chunk) && source.pause) { + source.pause(); + } + } + } + source.on("data", ondata); + function ondrain() { + if (source.readable && source.resume) { + source.resume(); + } + } + dest.on("drain", ondrain); + if (!dest._isStdio && (!options || options.end !== false)) { + source.on("end", onend); + source.on("close", onclose); + } + var didOnEnd = false; + function onend() { + if (didOnEnd) + return; + didOnEnd = true; + dest.end(); + } + function onclose() { + if (didOnEnd) + return; + didOnEnd = true; + if (typeof dest.destroy === "function") + dest.destroy(); + } + function onerror(er) { + cleanup(); + if (!this.hasListeners("error")) { + throw er; + } + } + source.on("error", onerror); + dest.on("error", onerror); + function cleanup() { + source.off("data", ondata); + dest.off("drain", ondrain); + source.off("end", onend); + source.off("close", onclose); + source.off("error", onerror); + dest.off("error", onerror); + source.off("end", cleanup); + source.off("close", cleanup); + dest.off("end", cleanup); + dest.off("close", cleanup); + } + source.on("end", cleanup); + source.on("close", cleanup); + dest.on("end", cleanup); + dest.on("close", cleanup); + dest.emit("pipe", source); + return dest; + }; + } + }); + + // node_modules/@iarna/toml/parse-stream.js + var require_parse_stream = __commonJS({ + "node_modules/@iarna/toml/parse-stream.js"(exports2, module2) { + "use strict"; + module2.exports = parseStream; + var stream = require_stream(); + var TOMLParser = require_toml_parser(); + function parseStream(stm) { + if (stm) { + return parseReadable(stm); + } else { + return parseTransform(stm); + } + } + function parseReadable(stm) { + const parser = new TOMLParser(); + stm.setEncoding("utf8"); + return new Promise((resolve, reject) => { + let readable; + let ended = false; + let errored = false; + function finish() { + ended = true; + if (readable) + return; + try { + resolve(parser.finish()); + } catch (err) { + reject(err); + } + } + function error(err) { + errored = true; + reject(err); + } + stm.once("end", finish); + stm.once("error", error); + readNext(); + function readNext() { + readable = true; + let data; + while ((data = stm.read()) !== null) { + try { + parser.parse(data); + } catch (err) { + return error(err); + } + } + readable = false; + if (ended) + return finish(); + if (errored) + return; + stm.once("readable", readNext); + } + }); + } + function parseTransform() { + const parser = new TOMLParser(); + return new stream.Transform({ + objectMode: true, + transform(chunk, encoding, cb) { + try { + parser.parse(chunk.toString(encoding)); + } catch (err) { + this.emit("error", err); + } + cb(); + }, + flush(cb) { + try { + this.push(parser.finish()); + } catch (err) { + this.emit("error", err); + } + cb(); + } + }); + } + } + }); + + // node_modules/@iarna/toml/parse.js + var require_parse = __commonJS({ + "node_modules/@iarna/toml/parse.js"(exports2, module2) { + "use strict"; + module2.exports = require_parse_string(); + module2.exports.async = require_parse_async(); + module2.exports.stream = require_parse_stream(); + module2.exports.prettyError = require_parse_pretty_error(); + } + }); + + // node_modules/@iarna/toml/stringify.js + var require_stringify = __commonJS({ + "node_modules/@iarna/toml/stringify.js"(exports2, module2) { + "use strict"; + module2.exports = stringify; + module2.exports.value = stringifyInline; + function stringify(obj) { + if (obj === null) + throw typeError("null"); + if (obj === void 0) + throw typeError("undefined"); + if (typeof obj !== "object") + throw typeError(typeof obj); + if (typeof obj.toJSON === "function") + obj = obj.toJSON(); + if (obj == null) + return null; + const type = tomlType2(obj); + if (type !== "table") + throw typeError(type); + return stringifyObject("", "", obj); + } + function typeError(type) { + return new Error("Can only stringify objects, not " + type); + } + function arrayOneTypeError() { + return new Error("Array values can't have mixed types"); + } + function getInlineKeys(obj) { + return Object.keys(obj).filter((key) => isInline(obj[key])); + } + function getComplexKeys(obj) { + return Object.keys(obj).filter((key) => !isInline(obj[key])); + } + function toJSON(obj) { + let nobj = Array.isArray(obj) ? [] : Object.prototype.hasOwnProperty.call(obj, "__proto__") ? { ["__proto__"]: void 0 } : {}; + for (let prop of Object.keys(obj)) { + if (obj[prop] && typeof obj[prop].toJSON === "function" && !("toISOString" in obj[prop])) { + nobj[prop] = obj[prop].toJSON(); + } else { + nobj[prop] = obj[prop]; + } + } + return nobj; + } + function stringifyObject(prefix, indent, obj) { + obj = toJSON(obj); + var inlineKeys; + var complexKeys; + inlineKeys = getInlineKeys(obj); + complexKeys = getComplexKeys(obj); + var result = []; + var inlineIndent = indent || ""; + inlineKeys.forEach((key) => { + var type = tomlType2(obj[key]); + if (type !== "undefined" && type !== "null") { + result.push(inlineIndent + stringifyKey(key) + " = " + stringifyAnyInline(obj[key], true)); + } + }); + if (result.length > 0) + result.push(""); + var complexIndent = prefix && inlineKeys.length > 0 ? indent + " " : ""; + complexKeys.forEach((key) => { + result.push(stringifyComplex(prefix, complexIndent, key, obj[key])); + }); + return result.join("\n"); + } + function isInline(value) { + switch (tomlType2(value)) { + case "undefined": + case "null": + case "integer": + case "nan": + case "float": + case "boolean": + case "string": + case "datetime": + return true; + case "array": + return value.length === 0 || tomlType2(value[0]) !== "table"; + case "table": + return Object.keys(value).length === 0; + default: + return false; + } + } + function tomlType2(value) { + if (value === void 0) { + return "undefined"; + } else if (value === null) { + return "null"; + } else if (typeof value === "bigint" || Number.isInteger(value) && !Object.is(value, -0)) { + return "integer"; + } else if (typeof value === "number") { + return "float"; + } else if (typeof value === "boolean") { + return "boolean"; + } else if (typeof value === "string") { + return "string"; + } else if ("toISOString" in value) { + return isNaN(value) ? "undefined" : "datetime"; + } else if (Array.isArray(value)) { + return "array"; + } else { + return "table"; + } + } + function stringifyKey(key) { + var keyStr = String(key); + if (/^[-A-Za-z0-9_]+$/.test(keyStr)) { + return keyStr; + } else { + return stringifyBasicString(keyStr); + } + } + function stringifyBasicString(str) { + return '"' + escapeString(str).replace(/"/g, '\\"') + '"'; + } + function stringifyLiteralString(str) { + return "'" + str + "'"; + } + function numpad(num, str) { + while (str.length < num) + str = "0" + str; + return str; + } + function escapeString(str) { + return str.replace(/\\/g, "\\\\").replace(/[\b]/g, "\\b").replace(/\t/g, "\\t").replace(/\n/g, "\\n").replace(/\f/g, "\\f").replace(/\r/g, "\\r").replace(/([\u0000-\u001f\u007f])/, (c) => "\\u" + numpad(4, c.codePointAt(0).toString(16))); + } + function stringifyMultilineString(str) { + let escaped = str.split(/\n/).map((str2) => { + return escapeString(str2).replace(/"(?="")/g, '\\"'); + }).join("\n"); + if (escaped.slice(-1) === '"') + escaped += "\\\n"; + return '"""\n' + escaped + '"""'; + } + function stringifyAnyInline(value, multilineOk) { + let type = tomlType2(value); + if (type === "string") { + if (multilineOk && /\n/.test(value)) { + type = "string-multiline"; + } else if (!/[\b\t\n\f\r']/.test(value) && /"/.test(value)) { + type = "string-literal"; + } + } + return stringifyInline(value, type); + } + function stringifyInline(value, type) { + if (!type) + type = tomlType2(value); + switch (type) { + case "string-multiline": + return stringifyMultilineString(value); + case "string": + return stringifyBasicString(value); + case "string-literal": + return stringifyLiteralString(value); + case "integer": + return stringifyInteger(value); + case "float": + return stringifyFloat(value); + case "boolean": + return stringifyBoolean(value); + case "datetime": + return stringifyDatetime(value); + case "array": + return stringifyInlineArray(value.filter((_) => tomlType2(_) !== "null" && tomlType2(_) !== "undefined" && tomlType2(_) !== "nan")); + case "table": + return stringifyInlineTable(value); + default: + throw typeError(type); + } + } + function stringifyInteger(value) { + return String(value).replace(/\B(?=(\d{3})+(?!\d))/g, "_"); + } + function stringifyFloat(value) { + if (value === Infinity) { + return "inf"; + } else if (value === -Infinity) { + return "-inf"; + } else if (Object.is(value, NaN)) { + return "nan"; + } else if (Object.is(value, -0)) { + return "-0.0"; + } + var chunks = String(value).split("."); + var int = chunks[0]; + var dec = chunks[1] || 0; + return stringifyInteger(int) + "." + dec; + } + function stringifyBoolean(value) { + return String(value); + } + function stringifyDatetime(value) { + return value.toISOString(); + } + function isNumber(type) { + return type === "float" || type === "integer"; + } + function arrayType(values) { + var contentType = tomlType2(values[0]); + if (values.every((_) => tomlType2(_) === contentType)) + return contentType; + if (values.every((_) => isNumber(tomlType2(_)))) + return "float"; + return "mixed"; + } + function validateArray(values) { + const type = arrayType(values); + if (type === "mixed") { + throw arrayOneTypeError(); + } + return type; + } + function stringifyInlineArray(values) { + values = toJSON(values); + const type = validateArray(values); + var result = "["; + var stringified = values.map((_) => stringifyInline(_, type)); + if (stringified.join(", ").length > 60 || /\n/.test(stringified)) { + result += "\n " + stringified.join(",\n ") + "\n"; + } else { + result += " " + stringified.join(", ") + (stringified.length > 0 ? " " : ""); + } + return result + "]"; + } + function stringifyInlineTable(value) { + value = toJSON(value); + var result = []; + Object.keys(value).forEach((key) => { + result.push(stringifyKey(key) + " = " + stringifyAnyInline(value[key], false)); + }); + return "{ " + result.join(", ") + (result.length > 0 ? " " : "") + "}"; + } + function stringifyComplex(prefix, indent, key, value) { + var valueType = tomlType2(value); + if (valueType === "array") { + return stringifyArrayOfTables(prefix, indent, key, value); + } else if (valueType === "table") { + return stringifyComplexTable(prefix, indent, key, value); + } else { + throw typeError(valueType); + } + } + function stringifyArrayOfTables(prefix, indent, key, values) { + values = toJSON(values); + validateArray(values); + var firstValueType = tomlType2(values[0]); + if (firstValueType !== "table") + throw typeError(firstValueType); + var fullKey = prefix + stringifyKey(key); + var result = ""; + values.forEach((table) => { + if (result.length > 0) + result += "\n"; + result += indent + "[[" + fullKey + "]]\n"; + result += stringifyObject(fullKey + ".", indent, table); + }); + return result; + } + function stringifyComplexTable(prefix, indent, key, value) { + var fullKey = prefix + stringifyKey(key); + var result = ""; + if (getInlineKeys(value).length > 0) { + result += indent + "[" + fullKey + "]\n"; + } + return result + stringifyObject(fullKey + ".", indent, value); + } + } + }); + + // node_modules/@iarna/toml/toml.js + var require_toml = __commonJS({ + "node_modules/@iarna/toml/toml.js"(exports2) { + "use strict"; + exports2.parse = require_parse(); + exports2.stringify = require_stringify(); + } + }); + + // code/elemtoml.ts + var import_utility_types = __toESM(require_dist()); + function register_element(name, elem) { + window.elements[name] = elem; + } + + // code/cfg_loader.ts + var import_toml = __toESM(require_toml()); + var Package = class { + constructor(config) { + this.loaded_elems = []; + this.cfg = config; + console.log(this); + } + async load_elems() { + for (const i of this.cfg.mod.external_elements) { + console.log(i); + try { + let resp = await fetch(i.path); + const parsed = (0, import_toml.parse)(await resp.text()); + console.log(parsed); + register_element(i.name, parsed); + } catch (err) { + console.error(err); + } + } + } + get_loaded_elems() { + return this.loaded_elems; + } + run() { + fetch(this.cfg.mod.entry_point).then((resp) => { + resp.text().then((x) => Function(x)()); + }); + } + }; + function load(object) { + return object; + } + + // code/mod.ts + var import_toml2 = __toESM(require_toml()); + + // code/mod_finder.ts + function find_mod(name, onfind) { + console.log(name, `mods/${name}/mod.toml`); + fetch(`mods/${name}/mod.toml`).then(async (x) => { + console.log(x.url); + if (x.ok) { + onfind(await x.text()); + } else { + window.queuedMods.push(name); + } + }).catch((err) => { + console.error(err); + }); + } + + // code/mod.ts + find_mod("loader_test", (text) => { + const parsed = import_toml2.default.parse(text); + console.log(import_toml2.default.parse(text)); + let pkg = new Package(load(parsed)); + pkg.load_elems().then(() => { + console.log(pkg); + console.log(pkg.get_loaded_elems()); + }); + }); +})(); +/*! Bundled license information: + +utility-types/dist/index.js: + (** + * @author Piotr Witek (http://piotrwitek.github.io) + * @copyright Copyright (c) 2016 Piotr Witek + * @license MIT + *) +*/ diff --git a/mods/loader_test/mod.toml b/mods/loader_test/mod.toml new file mode 100644 index 00000000..8678dd7c --- /dev/null +++ b/mods/loader_test/mod.toml @@ -0,0 +1,25 @@ +[mod] +name = "test-finder" +version = "0.1.0" +entry_point = "" +external_elements = [ + {path = "mods/loader_test/test_element.toml", name = "test_element"}, +] + +[elements] + +# [elements.test_element] +# name = "test_element" +# color = ["#FF00FF", "#00FF00"] +# state = "solid" +# behaviour = [["XX","XX","XX"],["XX","XX","XX"],["XX","XX","XX"]] +# category = "solids" +# tempHigh = 100 +# stateHigh = "gold" +# density = 100 +# conduct = 1.0 + +# [elements.test_element.reactions] +# water = {elem = "ash", elem2 = "pop"} +# cement = {elem1 = "sand"} +# blood = {elem1 = "n_explosion"} \ No newline at end of file diff --git a/mods/loader_test/test_element.toml b/mods/loader_test/test_element.toml new file mode 100644 index 00000000..7e3e0634 --- /dev/null +++ b/mods/loader_test/test_element.toml @@ -0,0 +1,14 @@ +name = "test_element_b" +color = ["#FF00FF", "#00FF00"] +state = "solid" +behaviour = [["XX","XX","XX"],["XX","XX","XX"],["XX","XX","XX"]] +category = "solids" +tempHigh = 100 +stateHigh = "gold" +density = 100 +conduct = 1.0 + +[reactions] +water = {elem = "ash", elem2 = "pop"} +cement = {elem1 = "sand"} +blood = {elem1 = "n_explosion"} \ No newline at end of file From adbeb0df5d5281f63d72edcb6023ae65861286b0 Mon Sep 17 00:00:00 2001 From: Mnem42 Date: Wed, 2 Jul 2025 16:02:15 +0100 Subject: [PATCH 02/38] add stuff --- mods/fancy-loader.js | 58 +- mods/fancy_loader.js | 2442 ++++++++++++++++++++++++++++ mods/loader_test/mod.toml | 32 +- mods/loader_test/post_load.js | 1 + mods/loader_test/pre_load.js | 1 + mods/loader_test/random_file.js | 1 + mods/loader_test/test_element.toml | 4 +- 7 files changed, 2505 insertions(+), 34 deletions(-) create mode 100644 mods/fancy_loader.js create mode 100644 mods/loader_test/post_load.js create mode 100644 mods/loader_test/pre_load.js create mode 100644 mods/loader_test/random_file.js diff --git a/mods/fancy-loader.js b/mods/fancy-loader.js index 9292aba9..7222d664 100644 --- a/mods/fancy-loader.js +++ b/mods/fancy-loader.js @@ -2285,13 +2285,33 @@ } }); - // code/elemtoml.ts + // src/elemtoml.ts var import_utility_types = __toESM(require_dist()); function register_element(name, elem) { - window.elements[name] = elem; + console.debug( + "Element registered: ", + elem, + "Under name: ", + name, + "Named behaviour: ", + elem.namedBehavior, + window.behaviors[elem.namedBehavior] + ); + console.trace(); + let tmp_value = elem; + console.log(tmp_value.namedBehavior); + if (tmp_value.namedBehavior) { + const found_behaviour = window.behaviors[tmp_value.namedBehavior]; + if (typeof found_behaviour == "function") { + tmp_value.tick = found_behaviour; + } else { + tmp_value.behavior = found_behaviour; + } + } + window.elements[name] = tmp_value; } - // code/cfg_loader.ts + // src/cfg_loader.ts var import_toml = __toESM(require_toml()); var Package = class { constructor(config) { @@ -2325,10 +2345,10 @@ return object; } - // code/mod.ts + // src/mod.ts var import_toml2 = __toESM(require_toml()); - // code/mod_finder.ts + // src/mod_finder.ts function find_mod(name, onfind) { console.log(name, `mods/${name}/mod.toml`); fetch(`mods/${name}/mod.toml`).then(async (x) => { @@ -2343,16 +2363,24 @@ }); } - // code/mod.ts - find_mod("loader_test", (text) => { - const parsed = import_toml2.default.parse(text); - console.log(import_toml2.default.parse(text)); - let pkg = new Package(load(parsed)); - pkg.load_elems().then(() => { - console.log(pkg); - console.log(pkg.get_loaded_elems()); - }); - }); + // src/mod.ts + console.log(window.enabledMods); + for (const i in window.enabledMods) { + console.log(i); + if (i.endsWith(".toml")) { + console.log(i); + find_mod(i.slice(0, -5), (text) => { + console.log("Mod name:", i.slice(0, -5)); + const parsed = import_toml2.default.parse(text); + console.log("important shit:", import_toml2.default.parse(text)); + let pkg = new Package(load(parsed)); + pkg.load_elems().then(() => { + console.log(pkg); + console.log(pkg.get_loaded_elems()); + }); + }); + } + } })(); /*! Bundled license information: diff --git a/mods/fancy_loader.js b/mods/fancy_loader.js new file mode 100644 index 00000000..b701ba2e --- /dev/null +++ b/mods/fancy_loader.js @@ -0,0 +1,2442 @@ +"use strict"; +(() => { + var __create = Object.create; + var __defProp = Object.defineProperty; + var __getOwnPropDesc = Object.getOwnPropertyDescriptor; + var __getOwnPropNames = Object.getOwnPropertyNames; + var __getProtoOf = Object.getPrototypeOf; + var __hasOwnProp = Object.prototype.hasOwnProperty; + var __commonJS = (cb, mod) => function __require() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; + }; + var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; + }; + var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod + )); + + // node_modules/utility-types/dist/aliases-and-guards.js + var require_aliases_and_guards = __commonJS({ + "node_modules/utility-types/dist/aliases-and-guards.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.isPrimitive = function(val) { + if (val === null || val === void 0) { + return true; + } + switch (typeof val) { + case "string": + case "number": + case "bigint": + case "boolean": + case "symbol": { + return true; + } + default: + return false; + } + }; + exports2.isFalsy = function(val) { + return !val; + }; + exports2.isNullish = function(val) { + return val == null; + }; + } + }); + + // node_modules/utility-types/dist/functional-helpers.js + var require_functional_helpers = __commonJS({ + "node_modules/utility-types/dist/functional-helpers.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + function getReturnOfExpression(expression) { + return void 0; + } + exports2.getReturnOfExpression = getReturnOfExpression; + } + }); + + // node_modules/utility-types/dist/index.js + var require_dist = __commonJS({ + "node_modules/utility-types/dist/index.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + var aliases_and_guards_1 = require_aliases_and_guards(); + exports2.isFalsy = aliases_and_guards_1.isFalsy; + exports2.isNullish = aliases_and_guards_1.isNullish; + exports2.isPrimitive = aliases_and_guards_1.isPrimitive; + var functional_helpers_1 = require_functional_helpers(); + exports2.getReturnOfExpression = functional_helpers_1.getReturnOfExpression; + } + }); + + // node_modules/@iarna/toml/lib/parser.js + var require_parser = __commonJS({ + "node_modules/@iarna/toml/lib/parser.js"(exports2, module2) { + "use strict"; + var ParserEND = 1114112; + var ParserError = class _ParserError extends Error { + /* istanbul ignore next */ + constructor(msg, filename, linenumber) { + super("[ParserError] " + msg, filename, linenumber); + this.name = "ParserError"; + this.code = "ParserError"; + if (Error.captureStackTrace) Error.captureStackTrace(this, _ParserError); + } + }; + var State = class { + constructor(parser) { + this.parser = parser; + this.buf = ""; + this.returned = null; + this.result = null; + this.resultTable = null; + this.resultArr = null; + } + }; + var Parser = class { + constructor() { + this.pos = 0; + this.col = 0; + this.line = 0; + this.obj = {}; + this.ctx = this.obj; + this.stack = []; + this._buf = ""; + this.char = null; + this.ii = 0; + this.state = new State(this.parseStart); + } + parse(str) { + if (str.length === 0 || str.length == null) return; + this._buf = String(str); + this.ii = -1; + this.char = -1; + let getNext; + while (getNext === false || this.nextChar()) { + getNext = this.runOne(); + } + this._buf = null; + } + nextChar() { + if (this.char === 10) { + ++this.line; + this.col = -1; + } + ++this.ii; + this.char = this._buf.codePointAt(this.ii); + ++this.pos; + ++this.col; + return this.haveBuffer(); + } + haveBuffer() { + return this.ii < this._buf.length; + } + runOne() { + return this.state.parser.call(this, this.state.returned); + } + finish() { + this.char = ParserEND; + let last; + do { + last = this.state.parser; + this.runOne(); + } while (this.state.parser !== last); + this.ctx = null; + this.state = null; + this._buf = null; + return this.obj; + } + next(fn) { + if (typeof fn !== "function") throw new ParserError("Tried to set state to non-existent state: " + JSON.stringify(fn)); + this.state.parser = fn; + } + goto(fn) { + this.next(fn); + return this.runOne(); + } + call(fn, returnWith) { + if (returnWith) this.next(returnWith); + this.stack.push(this.state); + this.state = new State(fn); + } + callNow(fn, returnWith) { + this.call(fn, returnWith); + return this.runOne(); + } + return(value) { + if (this.stack.length === 0) throw this.error(new ParserError("Stack underflow")); + if (value === void 0) value = this.state.buf; + this.state = this.stack.pop(); + this.state.returned = value; + } + returnNow(value) { + this.return(value); + return this.runOne(); + } + consume() { + if (this.char === ParserEND) throw this.error(new ParserError("Unexpected end-of-buffer")); + this.state.buf += this._buf[this.ii]; + } + error(err) { + err.line = this.line; + err.col = this.col; + err.pos = this.pos; + return err; + } + /* istanbul ignore next */ + parseStart() { + throw new ParserError("Must declare a parseStart method"); + } + }; + Parser.END = ParserEND; + Parser.Error = ParserError; + module2.exports = Parser; + } + }); + + // node_modules/@iarna/toml/lib/create-datetime.js + var require_create_datetime = __commonJS({ + "node_modules/@iarna/toml/lib/create-datetime.js"(exports2, module2) { + "use strict"; + module2.exports = (value) => { + const date = new Date(value); + if (isNaN(date)) { + throw new TypeError("Invalid Datetime"); + } else { + return date; + } + }; + } + }); + + // node_modules/@iarna/toml/lib/format-num.js + var require_format_num = __commonJS({ + "node_modules/@iarna/toml/lib/format-num.js"(exports2, module2) { + "use strict"; + module2.exports = (d, num) => { + num = String(num); + while (num.length < d) num = "0" + num; + return num; + }; + } + }); + + // node_modules/@iarna/toml/lib/create-datetime-float.js + var require_create_datetime_float = __commonJS({ + "node_modules/@iarna/toml/lib/create-datetime-float.js"(exports2, module2) { + "use strict"; + var f = require_format_num(); + var FloatingDateTime = class extends Date { + constructor(value) { + super(value + "Z"); + this.isFloating = true; + } + toISOString() { + const date = `${this.getUTCFullYear()}-${f(2, this.getUTCMonth() + 1)}-${f(2, this.getUTCDate())}`; + const time = `${f(2, this.getUTCHours())}:${f(2, this.getUTCMinutes())}:${f(2, this.getUTCSeconds())}.${f(3, this.getUTCMilliseconds())}`; + return `${date}T${time}`; + } + }; + module2.exports = (value) => { + const date = new FloatingDateTime(value); + if (isNaN(date)) { + throw new TypeError("Invalid Datetime"); + } else { + return date; + } + }; + } + }); + + // node_modules/@iarna/toml/lib/create-date.js + var require_create_date = __commonJS({ + "node_modules/@iarna/toml/lib/create-date.js"(exports2, module2) { + "use strict"; + var f = require_format_num(); + var DateTime = globalThis.Date; + var Date2 = class extends DateTime { + constructor(value) { + super(value); + this.isDate = true; + } + toISOString() { + return `${this.getUTCFullYear()}-${f(2, this.getUTCMonth() + 1)}-${f(2, this.getUTCDate())}`; + } + }; + module2.exports = (value) => { + const date = new Date2(value); + if (isNaN(date)) { + throw new TypeError("Invalid Datetime"); + } else { + return date; + } + }; + } + }); + + // node_modules/@iarna/toml/lib/create-time.js + var require_create_time = __commonJS({ + "node_modules/@iarna/toml/lib/create-time.js"(exports2, module2) { + "use strict"; + var f = require_format_num(); + var Time = class extends Date { + constructor(value) { + super(`0000-01-01T${value}Z`); + this.isTime = true; + } + toISOString() { + return `${f(2, this.getUTCHours())}:${f(2, this.getUTCMinutes())}:${f(2, this.getUTCSeconds())}.${f(3, this.getUTCMilliseconds())}`; + } + }; + module2.exports = (value) => { + const date = new Time(value); + if (isNaN(date)) { + throw new TypeError("Invalid Datetime"); + } else { + return date; + } + }; + } + }); + + // node_modules/@iarna/toml/lib/toml-parser.js + var require_toml_parser = __commonJS({ + "node_modules/@iarna/toml/lib/toml-parser.js"(exports, module) { + "use strict"; + module.exports = makeParserClass(require_parser()); + module.exports.makeParserClass = makeParserClass; + var TomlError = class _TomlError extends Error { + constructor(msg) { + super(msg); + this.name = "TomlError"; + if (Error.captureStackTrace) Error.captureStackTrace(this, _TomlError); + this.fromTOML = true; + this.wrapped = null; + } + }; + TomlError.wrap = (err) => { + const terr = new TomlError(err.message); + terr.code = err.code; + terr.wrapped = err; + return terr; + }; + module.exports.TomlError = TomlError; + var createDateTime = require_create_datetime(); + var createDateTimeFloat = require_create_datetime_float(); + var createDate = require_create_date(); + var createTime = require_create_time(); + var CTRL_I = 9; + var CTRL_J = 10; + var CTRL_M = 13; + var CTRL_CHAR_BOUNDARY = 31; + var CHAR_SP = 32; + var CHAR_QUOT = 34; + var CHAR_NUM = 35; + var CHAR_APOS = 39; + var CHAR_PLUS = 43; + var CHAR_COMMA = 44; + var CHAR_HYPHEN = 45; + var CHAR_PERIOD = 46; + var CHAR_0 = 48; + var CHAR_1 = 49; + var CHAR_7 = 55; + var CHAR_9 = 57; + var CHAR_COLON = 58; + var CHAR_EQUALS = 61; + var CHAR_A = 65; + var CHAR_E = 69; + var CHAR_F = 70; + var CHAR_T = 84; + var CHAR_U = 85; + var CHAR_Z = 90; + var CHAR_LOWBAR = 95; + var CHAR_a = 97; + var CHAR_b = 98; + var CHAR_e = 101; + var CHAR_f = 102; + var CHAR_i = 105; + var CHAR_l = 108; + var CHAR_n = 110; + var CHAR_o = 111; + var CHAR_r = 114; + var CHAR_s = 115; + var CHAR_t = 116; + var CHAR_u = 117; + var CHAR_x = 120; + var CHAR_z = 122; + var CHAR_LCUB = 123; + var CHAR_RCUB = 125; + var CHAR_LSQB = 91; + var CHAR_BSOL = 92; + var CHAR_RSQB = 93; + var CHAR_DEL = 127; + var SURROGATE_FIRST = 55296; + var SURROGATE_LAST = 57343; + var escapes = { + [CHAR_b]: "\b", + [CHAR_t]: " ", + [CHAR_n]: "\n", + [CHAR_f]: "\f", + [CHAR_r]: "\r", + [CHAR_QUOT]: '"', + [CHAR_BSOL]: "\\" + }; + function isDigit(cp) { + return cp >= CHAR_0 && cp <= CHAR_9; + } + function isHexit(cp) { + return cp >= CHAR_A && cp <= CHAR_F || cp >= CHAR_a && cp <= CHAR_f || cp >= CHAR_0 && cp <= CHAR_9; + } + function isBit(cp) { + return cp === CHAR_1 || cp === CHAR_0; + } + function isOctit(cp) { + return cp >= CHAR_0 && cp <= CHAR_7; + } + function isAlphaNumQuoteHyphen(cp) { + return cp >= CHAR_A && cp <= CHAR_Z || cp >= CHAR_a && cp <= CHAR_z || cp >= CHAR_0 && cp <= CHAR_9 || cp === CHAR_APOS || cp === CHAR_QUOT || cp === CHAR_LOWBAR || cp === CHAR_HYPHEN; + } + function isAlphaNumHyphen(cp) { + return cp >= CHAR_A && cp <= CHAR_Z || cp >= CHAR_a && cp <= CHAR_z || cp >= CHAR_0 && cp <= CHAR_9 || cp === CHAR_LOWBAR || cp === CHAR_HYPHEN; + } + var _type = Symbol("type"); + var _declared = Symbol("declared"); + var hasOwnProperty = Object.prototype.hasOwnProperty; + var defineProperty = Object.defineProperty; + var descriptor = { configurable: true, enumerable: true, writable: true, value: void 0 }; + function hasKey(obj, key) { + if (hasOwnProperty.call(obj, key)) return true; + if (key === "__proto__") defineProperty(obj, "__proto__", descriptor); + return false; + } + var INLINE_TABLE = Symbol("inline-table"); + function InlineTable() { + return Object.defineProperties({}, { + [_type]: { value: INLINE_TABLE } + }); + } + function isInlineTable(obj) { + if (obj === null || typeof obj !== "object") return false; + return obj[_type] === INLINE_TABLE; + } + var TABLE = Symbol("table"); + function Table() { + return Object.defineProperties({}, { + [_type]: { value: TABLE }, + [_declared]: { value: false, writable: true } + }); + } + function isTable(obj) { + if (obj === null || typeof obj !== "object") return false; + return obj[_type] === TABLE; + } + var _contentType = Symbol("content-type"); + var INLINE_LIST = Symbol("inline-list"); + function InlineList(type) { + return Object.defineProperties([], { + [_type]: { value: INLINE_LIST }, + [_contentType]: { value: type } + }); + } + function isInlineList(obj) { + if (obj === null || typeof obj !== "object") return false; + return obj[_type] === INLINE_LIST; + } + var LIST = Symbol("list"); + function List() { + return Object.defineProperties([], { + [_type]: { value: LIST } + }); + } + function isList(obj) { + if (obj === null || typeof obj !== "object") return false; + return obj[_type] === LIST; + } + var _custom; + try { + const utilInspect = eval("require('util').inspect"); + _custom = utilInspect.custom; + } catch (_) { + } + var _inspect = _custom || "inspect"; + var BoxedBigInt = class { + constructor(value) { + try { + this.value = globalThis.BigInt.asIntN(64, value); + } catch (_) { + this.value = null; + } + Object.defineProperty(this, _type, { value: INTEGER }); + } + isNaN() { + return this.value === null; + } + /* istanbul ignore next */ + toString() { + return String(this.value); + } + /* istanbul ignore next */ + [_inspect]() { + return `[BigInt: ${this.toString()}]}`; + } + valueOf() { + return this.value; + } + }; + var INTEGER = Symbol("integer"); + function Integer(value) { + let num = Number(value); + if (Object.is(num, -0)) num = 0; + if (globalThis.BigInt && !Number.isSafeInteger(num)) { + return new BoxedBigInt(value); + } else { + return Object.defineProperties(new Number(num), { + isNaN: { value: function() { + return isNaN(this); + } }, + [_type]: { value: INTEGER }, + [_inspect]: { value: () => `[Integer: ${value}]` } + }); + } + } + function isInteger(obj) { + if (obj === null || typeof obj !== "object") return false; + return obj[_type] === INTEGER; + } + var FLOAT = Symbol("float"); + function Float(value) { + return Object.defineProperties(new Number(value), { + [_type]: { value: FLOAT }, + [_inspect]: { value: () => `[Float: ${value}]` } + }); + } + function isFloat(obj) { + if (obj === null || typeof obj !== "object") return false; + return obj[_type] === FLOAT; + } + function tomlType(value) { + const type = typeof value; + if (type === "object") { + if (value === null) return "null"; + if (value instanceof Date) return "datetime"; + if (_type in value) { + switch (value[_type]) { + case INLINE_TABLE: + return "inline-table"; + case INLINE_LIST: + return "inline-list"; + /* istanbul ignore next */ + case TABLE: + return "table"; + /* istanbul ignore next */ + case LIST: + return "list"; + case FLOAT: + return "float"; + case INTEGER: + return "integer"; + } + } + } + return type; + } + function makeParserClass(Parser) { + class TOMLParser extends Parser { + constructor() { + super(); + this.ctx = this.obj = Table(); + } + /* MATCH HELPER */ + atEndOfWord() { + return this.char === CHAR_NUM || this.char === CTRL_I || this.char === CHAR_SP || this.atEndOfLine(); + } + atEndOfLine() { + return this.char === Parser.END || this.char === CTRL_J || this.char === CTRL_M; + } + parseStart() { + if (this.char === Parser.END) { + return null; + } else if (this.char === CHAR_LSQB) { + return this.call(this.parseTableOrList); + } else if (this.char === CHAR_NUM) { + return this.call(this.parseComment); + } else if (this.char === CTRL_J || this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M) { + return null; + } else if (isAlphaNumQuoteHyphen(this.char)) { + return this.callNow(this.parseAssignStatement); + } else { + throw this.error(new TomlError(`Unknown character "${this.char}"`)); + } + } + // HELPER, this strips any whitespace and comments to the end of the line + // then RETURNS. Last state in a production. + parseWhitespaceToEOL() { + if (this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M) { + return null; + } else if (this.char === CHAR_NUM) { + return this.goto(this.parseComment); + } else if (this.char === Parser.END || this.char === CTRL_J) { + return this.return(); + } else { + throw this.error(new TomlError("Unexpected character, expected only whitespace or comments till end of line")); + } + } + /* ASSIGNMENT: key = value */ + parseAssignStatement() { + return this.callNow(this.parseAssign, this.recordAssignStatement); + } + recordAssignStatement(kv) { + let target = this.ctx; + let finalKey = kv.key.pop(); + for (let kw of kv.key) { + if (hasKey(target, kw) && (!isTable(target[kw]) || target[kw][_declared])) { + throw this.error(new TomlError("Can't redefine existing key")); + } + target = target[kw] = target[kw] || Table(); + } + if (hasKey(target, finalKey)) { + throw this.error(new TomlError("Can't redefine existing key")); + } + if (isInteger(kv.value) || isFloat(kv.value)) { + target[finalKey] = kv.value.valueOf(); + } else { + target[finalKey] = kv.value; + } + return this.goto(this.parseWhitespaceToEOL); + } + /* ASSSIGNMENT expression, key = value possibly inside an inline table */ + parseAssign() { + return this.callNow(this.parseKeyword, this.recordAssignKeyword); + } + recordAssignKeyword(key) { + if (this.state.resultTable) { + this.state.resultTable.push(key); + } else { + this.state.resultTable = [key]; + } + return this.goto(this.parseAssignKeywordPreDot); + } + parseAssignKeywordPreDot() { + if (this.char === CHAR_PERIOD) { + return this.next(this.parseAssignKeywordPostDot); + } else if (this.char !== CHAR_SP && this.char !== CTRL_I) { + return this.goto(this.parseAssignEqual); + } + } + parseAssignKeywordPostDot() { + if (this.char !== CHAR_SP && this.char !== CTRL_I) { + return this.callNow(this.parseKeyword, this.recordAssignKeyword); + } + } + parseAssignEqual() { + if (this.char === CHAR_EQUALS) { + return this.next(this.parseAssignPreValue); + } else { + throw this.error(new TomlError('Invalid character, expected "="')); + } + } + parseAssignPreValue() { + if (this.char === CHAR_SP || this.char === CTRL_I) { + return null; + } else { + return this.callNow(this.parseValue, this.recordAssignValue); + } + } + recordAssignValue(value) { + return this.returnNow({ key: this.state.resultTable, value }); + } + /* COMMENTS: #...eol */ + parseComment() { + do { + if (this.char === Parser.END || this.char === CTRL_J) { + return this.return(); + } + } while (this.nextChar()); + } + /* TABLES AND LISTS, [foo] and [[foo]] */ + parseTableOrList() { + if (this.char === CHAR_LSQB) { + this.next(this.parseList); + } else { + return this.goto(this.parseTable); + } + } + /* TABLE [foo.bar.baz] */ + parseTable() { + this.ctx = this.obj; + return this.goto(this.parseTableNext); + } + parseTableNext() { + if (this.char === CHAR_SP || this.char === CTRL_I) { + return null; + } else { + return this.callNow(this.parseKeyword, this.parseTableMore); + } + } + parseTableMore(keyword) { + if (this.char === CHAR_SP || this.char === CTRL_I) { + return null; + } else if (this.char === CHAR_RSQB) { + if (hasKey(this.ctx, keyword) && (!isTable(this.ctx[keyword]) || this.ctx[keyword][_declared])) { + throw this.error(new TomlError("Can't redefine existing key")); + } else { + this.ctx = this.ctx[keyword] = this.ctx[keyword] || Table(); + this.ctx[_declared] = true; + } + return this.next(this.parseWhitespaceToEOL); + } else if (this.char === CHAR_PERIOD) { + if (!hasKey(this.ctx, keyword)) { + this.ctx = this.ctx[keyword] = Table(); + } else if (isTable(this.ctx[keyword])) { + this.ctx = this.ctx[keyword]; + } else if (isList(this.ctx[keyword])) { + this.ctx = this.ctx[keyword][this.ctx[keyword].length - 1]; + } else { + throw this.error(new TomlError("Can't redefine existing key")); + } + return this.next(this.parseTableNext); + } else { + throw this.error(new TomlError("Unexpected character, expected whitespace, . or ]")); + } + } + /* LIST [[a.b.c]] */ + parseList() { + this.ctx = this.obj; + return this.goto(this.parseListNext); + } + parseListNext() { + if (this.char === CHAR_SP || this.char === CTRL_I) { + return null; + } else { + return this.callNow(this.parseKeyword, this.parseListMore); + } + } + parseListMore(keyword) { + if (this.char === CHAR_SP || this.char === CTRL_I) { + return null; + } else if (this.char === CHAR_RSQB) { + if (!hasKey(this.ctx, keyword)) { + this.ctx[keyword] = List(); + } + if (isInlineList(this.ctx[keyword])) { + throw this.error(new TomlError("Can't extend an inline array")); + } else if (isList(this.ctx[keyword])) { + const next = Table(); + this.ctx[keyword].push(next); + this.ctx = next; + } else { + throw this.error(new TomlError("Can't redefine an existing key")); + } + return this.next(this.parseListEnd); + } else if (this.char === CHAR_PERIOD) { + if (!hasKey(this.ctx, keyword)) { + this.ctx = this.ctx[keyword] = Table(); + } else if (isInlineList(this.ctx[keyword])) { + throw this.error(new TomlError("Can't extend an inline array")); + } else if (isInlineTable(this.ctx[keyword])) { + throw this.error(new TomlError("Can't extend an inline table")); + } else if (isList(this.ctx[keyword])) { + this.ctx = this.ctx[keyword][this.ctx[keyword].length - 1]; + } else if (isTable(this.ctx[keyword])) { + this.ctx = this.ctx[keyword]; + } else { + throw this.error(new TomlError("Can't redefine an existing key")); + } + return this.next(this.parseListNext); + } else { + throw this.error(new TomlError("Unexpected character, expected whitespace, . or ]")); + } + } + parseListEnd(keyword) { + if (this.char === CHAR_RSQB) { + return this.next(this.parseWhitespaceToEOL); + } else { + throw this.error(new TomlError("Unexpected character, expected whitespace, . or ]")); + } + } + /* VALUE string, number, boolean, inline list, inline object */ + parseValue() { + if (this.char === Parser.END) { + throw this.error(new TomlError("Key without value")); + } else if (this.char === CHAR_QUOT) { + return this.next(this.parseDoubleString); + } + if (this.char === CHAR_APOS) { + return this.next(this.parseSingleString); + } else if (this.char === CHAR_HYPHEN || this.char === CHAR_PLUS) { + return this.goto(this.parseNumberSign); + } else if (this.char === CHAR_i) { + return this.next(this.parseInf); + } else if (this.char === CHAR_n) { + return this.next(this.parseNan); + } else if (isDigit(this.char)) { + return this.goto(this.parseNumberOrDateTime); + } else if (this.char === CHAR_t || this.char === CHAR_f) { + return this.goto(this.parseBoolean); + } else if (this.char === CHAR_LSQB) { + return this.call(this.parseInlineList, this.recordValue); + } else if (this.char === CHAR_LCUB) { + return this.call(this.parseInlineTable, this.recordValue); + } else { + throw this.error(new TomlError("Unexpected character, expecting string, number, datetime, boolean, inline array or inline table")); + } + } + recordValue(value) { + return this.returnNow(value); + } + parseInf() { + if (this.char === CHAR_n) { + return this.next(this.parseInf2); + } else { + throw this.error(new TomlError('Unexpected character, expected "inf", "+inf" or "-inf"')); + } + } + parseInf2() { + if (this.char === CHAR_f) { + if (this.state.buf === "-") { + return this.return(-Infinity); + } else { + return this.return(Infinity); + } + } else { + throw this.error(new TomlError('Unexpected character, expected "inf", "+inf" or "-inf"')); + } + } + parseNan() { + if (this.char === CHAR_a) { + return this.next(this.parseNan2); + } else { + throw this.error(new TomlError('Unexpected character, expected "nan"')); + } + } + parseNan2() { + if (this.char === CHAR_n) { + return this.return(NaN); + } else { + throw this.error(new TomlError('Unexpected character, expected "nan"')); + } + } + /* KEYS, barewords or basic, literal, or dotted */ + parseKeyword() { + if (this.char === CHAR_QUOT) { + return this.next(this.parseBasicString); + } else if (this.char === CHAR_APOS) { + return this.next(this.parseLiteralString); + } else { + return this.goto(this.parseBareKey); + } + } + /* KEYS: barewords */ + parseBareKey() { + do { + if (this.char === Parser.END) { + throw this.error(new TomlError("Key ended without value")); + } else if (isAlphaNumHyphen(this.char)) { + this.consume(); + } else if (this.state.buf.length === 0) { + throw this.error(new TomlError("Empty bare keys are not allowed")); + } else { + return this.returnNow(); + } + } while (this.nextChar()); + } + /* STRINGS, single quoted (literal) */ + parseSingleString() { + if (this.char === CHAR_APOS) { + return this.next(this.parseLiteralMultiStringMaybe); + } else { + return this.goto(this.parseLiteralString); + } + } + parseLiteralString() { + do { + if (this.char === CHAR_APOS) { + return this.return(); + } else if (this.atEndOfLine()) { + throw this.error(new TomlError("Unterminated string")); + } else if (this.char === CHAR_DEL || this.char <= CTRL_CHAR_BOUNDARY && this.char !== CTRL_I) { + throw this.errorControlCharInString(); + } else { + this.consume(); + } + } while (this.nextChar()); + } + parseLiteralMultiStringMaybe() { + if (this.char === CHAR_APOS) { + return this.next(this.parseLiteralMultiString); + } else { + return this.returnNow(); + } + } + parseLiteralMultiString() { + if (this.char === CTRL_M) { + return null; + } else if (this.char === CTRL_J) { + return this.next(this.parseLiteralMultiStringContent); + } else { + return this.goto(this.parseLiteralMultiStringContent); + } + } + parseLiteralMultiStringContent() { + do { + if (this.char === CHAR_APOS) { + return this.next(this.parseLiteralMultiEnd); + } else if (this.char === Parser.END) { + throw this.error(new TomlError("Unterminated multi-line string")); + } else if (this.char === CHAR_DEL || this.char <= CTRL_CHAR_BOUNDARY && this.char !== CTRL_I && this.char !== CTRL_J && this.char !== CTRL_M) { + throw this.errorControlCharInString(); + } else { + this.consume(); + } + } while (this.nextChar()); + } + parseLiteralMultiEnd() { + if (this.char === CHAR_APOS) { + return this.next(this.parseLiteralMultiEnd2); + } else { + this.state.buf += "'"; + return this.goto(this.parseLiteralMultiStringContent); + } + } + parseLiteralMultiEnd2() { + if (this.char === CHAR_APOS) { + return this.return(); + } else { + this.state.buf += "''"; + return this.goto(this.parseLiteralMultiStringContent); + } + } + /* STRINGS double quoted */ + parseDoubleString() { + if (this.char === CHAR_QUOT) { + return this.next(this.parseMultiStringMaybe); + } else { + return this.goto(this.parseBasicString); + } + } + parseBasicString() { + do { + if (this.char === CHAR_BSOL) { + return this.call(this.parseEscape, this.recordEscapeReplacement); + } else if (this.char === CHAR_QUOT) { + return this.return(); + } else if (this.atEndOfLine()) { + throw this.error(new TomlError("Unterminated string")); + } else if (this.char === CHAR_DEL || this.char <= CTRL_CHAR_BOUNDARY && this.char !== CTRL_I) { + throw this.errorControlCharInString(); + } else { + this.consume(); + } + } while (this.nextChar()); + } + recordEscapeReplacement(replacement) { + this.state.buf += replacement; + return this.goto(this.parseBasicString); + } + parseMultiStringMaybe() { + if (this.char === CHAR_QUOT) { + return this.next(this.parseMultiString); + } else { + return this.returnNow(); + } + } + parseMultiString() { + if (this.char === CTRL_M) { + return null; + } else if (this.char === CTRL_J) { + return this.next(this.parseMultiStringContent); + } else { + return this.goto(this.parseMultiStringContent); + } + } + parseMultiStringContent() { + do { + if (this.char === CHAR_BSOL) { + return this.call(this.parseMultiEscape, this.recordMultiEscapeReplacement); + } else if (this.char === CHAR_QUOT) { + return this.next(this.parseMultiEnd); + } else if (this.char === Parser.END) { + throw this.error(new TomlError("Unterminated multi-line string")); + } else if (this.char === CHAR_DEL || this.char <= CTRL_CHAR_BOUNDARY && this.char !== CTRL_I && this.char !== CTRL_J && this.char !== CTRL_M) { + throw this.errorControlCharInString(); + } else { + this.consume(); + } + } while (this.nextChar()); + } + errorControlCharInString() { + let displayCode = "\\u00"; + if (this.char < 16) { + displayCode += "0"; + } + displayCode += this.char.toString(16); + return this.error(new TomlError(`Control characters (codes < 0x1f and 0x7f) are not allowed in strings, use ${displayCode} instead`)); + } + recordMultiEscapeReplacement(replacement) { + this.state.buf += replacement; + return this.goto(this.parseMultiStringContent); + } + parseMultiEnd() { + if (this.char === CHAR_QUOT) { + return this.next(this.parseMultiEnd2); + } else { + this.state.buf += '"'; + return this.goto(this.parseMultiStringContent); + } + } + parseMultiEnd2() { + if (this.char === CHAR_QUOT) { + return this.return(); + } else { + this.state.buf += '""'; + return this.goto(this.parseMultiStringContent); + } + } + parseMultiEscape() { + if (this.char === CTRL_M || this.char === CTRL_J) { + return this.next(this.parseMultiTrim); + } else if (this.char === CHAR_SP || this.char === CTRL_I) { + return this.next(this.parsePreMultiTrim); + } else { + return this.goto(this.parseEscape); + } + } + parsePreMultiTrim() { + if (this.char === CHAR_SP || this.char === CTRL_I) { + return null; + } else if (this.char === CTRL_M || this.char === CTRL_J) { + return this.next(this.parseMultiTrim); + } else { + throw this.error(new TomlError("Can't escape whitespace")); + } + } + parseMultiTrim() { + if (this.char === CTRL_J || this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M) { + return null; + } else { + return this.returnNow(); + } + } + parseEscape() { + if (this.char in escapes) { + return this.return(escapes[this.char]); + } else if (this.char === CHAR_u) { + return this.call(this.parseSmallUnicode, this.parseUnicodeReturn); + } else if (this.char === CHAR_U) { + return this.call(this.parseLargeUnicode, this.parseUnicodeReturn); + } else { + throw this.error(new TomlError("Unknown escape character: " + this.char)); + } + } + parseUnicodeReturn(char) { + try { + const codePoint = parseInt(char, 16); + if (codePoint >= SURROGATE_FIRST && codePoint <= SURROGATE_LAST) { + throw this.error(new TomlError("Invalid unicode, character in range 0xD800 - 0xDFFF is reserved")); + } + return this.returnNow(String.fromCodePoint(codePoint)); + } catch (err) { + throw this.error(TomlError.wrap(err)); + } + } + parseSmallUnicode() { + if (!isHexit(this.char)) { + throw this.error(new TomlError("Invalid character in unicode sequence, expected hex")); + } else { + this.consume(); + if (this.state.buf.length >= 4) return this.return(); + } + } + parseLargeUnicode() { + if (!isHexit(this.char)) { + throw this.error(new TomlError("Invalid character in unicode sequence, expected hex")); + } else { + this.consume(); + if (this.state.buf.length >= 8) return this.return(); + } + } + /* NUMBERS */ + parseNumberSign() { + this.consume(); + return this.next(this.parseMaybeSignedInfOrNan); + } + parseMaybeSignedInfOrNan() { + if (this.char === CHAR_i) { + return this.next(this.parseInf); + } else if (this.char === CHAR_n) { + return this.next(this.parseNan); + } else { + return this.callNow(this.parseNoUnder, this.parseNumberIntegerStart); + } + } + parseNumberIntegerStart() { + if (this.char === CHAR_0) { + this.consume(); + return this.next(this.parseNumberIntegerExponentOrDecimal); + } else { + return this.goto(this.parseNumberInteger); + } + } + parseNumberIntegerExponentOrDecimal() { + if (this.char === CHAR_PERIOD) { + this.consume(); + return this.call(this.parseNoUnder, this.parseNumberFloat); + } else if (this.char === CHAR_E || this.char === CHAR_e) { + this.consume(); + return this.next(this.parseNumberExponentSign); + } else { + return this.returnNow(Integer(this.state.buf)); + } + } + parseNumberInteger() { + if (isDigit(this.char)) { + this.consume(); + } else if (this.char === CHAR_LOWBAR) { + return this.call(this.parseNoUnder); + } else if (this.char === CHAR_E || this.char === CHAR_e) { + this.consume(); + return this.next(this.parseNumberExponentSign); + } else if (this.char === CHAR_PERIOD) { + this.consume(); + return this.call(this.parseNoUnder, this.parseNumberFloat); + } else { + const result = Integer(this.state.buf); + if (result.isNaN()) { + throw this.error(new TomlError("Invalid number")); + } else { + return this.returnNow(result); + } + } + } + parseNoUnder() { + if (this.char === CHAR_LOWBAR || this.char === CHAR_PERIOD || this.char === CHAR_E || this.char === CHAR_e) { + throw this.error(new TomlError("Unexpected character, expected digit")); + } else if (this.atEndOfWord()) { + throw this.error(new TomlError("Incomplete number")); + } + return this.returnNow(); + } + parseNoUnderHexOctBinLiteral() { + if (this.char === CHAR_LOWBAR || this.char === CHAR_PERIOD) { + throw this.error(new TomlError("Unexpected character, expected digit")); + } else if (this.atEndOfWord()) { + throw this.error(new TomlError("Incomplete number")); + } + return this.returnNow(); + } + parseNumberFloat() { + if (this.char === CHAR_LOWBAR) { + return this.call(this.parseNoUnder, this.parseNumberFloat); + } else if (isDigit(this.char)) { + this.consume(); + } else if (this.char === CHAR_E || this.char === CHAR_e) { + this.consume(); + return this.next(this.parseNumberExponentSign); + } else { + return this.returnNow(Float(this.state.buf)); + } + } + parseNumberExponentSign() { + if (isDigit(this.char)) { + return this.goto(this.parseNumberExponent); + } else if (this.char === CHAR_HYPHEN || this.char === CHAR_PLUS) { + this.consume(); + this.call(this.parseNoUnder, this.parseNumberExponent); + } else { + throw this.error(new TomlError("Unexpected character, expected -, + or digit")); + } + } + parseNumberExponent() { + if (isDigit(this.char)) { + this.consume(); + } else if (this.char === CHAR_LOWBAR) { + return this.call(this.parseNoUnder); + } else { + return this.returnNow(Float(this.state.buf)); + } + } + /* NUMBERS or DATETIMES */ + parseNumberOrDateTime() { + if (this.char === CHAR_0) { + this.consume(); + return this.next(this.parseNumberBaseOrDateTime); + } else { + return this.goto(this.parseNumberOrDateTimeOnly); + } + } + parseNumberOrDateTimeOnly() { + if (this.char === CHAR_LOWBAR) { + return this.call(this.parseNoUnder, this.parseNumberInteger); + } else if (isDigit(this.char)) { + this.consume(); + if (this.state.buf.length > 4) this.next(this.parseNumberInteger); + } else if (this.char === CHAR_E || this.char === CHAR_e) { + this.consume(); + return this.next(this.parseNumberExponentSign); + } else if (this.char === CHAR_PERIOD) { + this.consume(); + return this.call(this.parseNoUnder, this.parseNumberFloat); + } else if (this.char === CHAR_HYPHEN) { + return this.goto(this.parseDateTime); + } else if (this.char === CHAR_COLON) { + return this.goto(this.parseOnlyTimeHour); + } else { + return this.returnNow(Integer(this.state.buf)); + } + } + parseDateTimeOnly() { + if (this.state.buf.length < 4) { + if (isDigit(this.char)) { + return this.consume(); + } else if (this.char === CHAR_COLON) { + return this.goto(this.parseOnlyTimeHour); + } else { + throw this.error(new TomlError("Expected digit while parsing year part of a date")); + } + } else { + if (this.char === CHAR_HYPHEN) { + return this.goto(this.parseDateTime); + } else { + throw this.error(new TomlError("Expected hyphen (-) while parsing year part of date")); + } + } + } + parseNumberBaseOrDateTime() { + if (this.char === CHAR_b) { + this.consume(); + return this.call(this.parseNoUnderHexOctBinLiteral, this.parseIntegerBin); + } else if (this.char === CHAR_o) { + this.consume(); + return this.call(this.parseNoUnderHexOctBinLiteral, this.parseIntegerOct); + } else if (this.char === CHAR_x) { + this.consume(); + return this.call(this.parseNoUnderHexOctBinLiteral, this.parseIntegerHex); + } else if (this.char === CHAR_PERIOD) { + return this.goto(this.parseNumberInteger); + } else if (isDigit(this.char)) { + return this.goto(this.parseDateTimeOnly); + } else { + return this.returnNow(Integer(this.state.buf)); + } + } + parseIntegerHex() { + if (isHexit(this.char)) { + this.consume(); + } else if (this.char === CHAR_LOWBAR) { + return this.call(this.parseNoUnderHexOctBinLiteral); + } else { + const result = Integer(this.state.buf); + if (result.isNaN()) { + throw this.error(new TomlError("Invalid number")); + } else { + return this.returnNow(result); + } + } + } + parseIntegerOct() { + if (isOctit(this.char)) { + this.consume(); + } else if (this.char === CHAR_LOWBAR) { + return this.call(this.parseNoUnderHexOctBinLiteral); + } else { + const result = Integer(this.state.buf); + if (result.isNaN()) { + throw this.error(new TomlError("Invalid number")); + } else { + return this.returnNow(result); + } + } + } + parseIntegerBin() { + if (isBit(this.char)) { + this.consume(); + } else if (this.char === CHAR_LOWBAR) { + return this.call(this.parseNoUnderHexOctBinLiteral); + } else { + const result = Integer(this.state.buf); + if (result.isNaN()) { + throw this.error(new TomlError("Invalid number")); + } else { + return this.returnNow(result); + } + } + } + /* DATETIME */ + parseDateTime() { + if (this.state.buf.length < 4) { + throw this.error(new TomlError("Years less than 1000 must be zero padded to four characters")); + } + this.state.result = this.state.buf; + this.state.buf = ""; + return this.next(this.parseDateMonth); + } + parseDateMonth() { + if (this.char === CHAR_HYPHEN) { + if (this.state.buf.length < 2) { + throw this.error(new TomlError("Months less than 10 must be zero padded to two characters")); + } + this.state.result += "-" + this.state.buf; + this.state.buf = ""; + return this.next(this.parseDateDay); + } else if (isDigit(this.char)) { + this.consume(); + } else { + throw this.error(new TomlError("Incomplete datetime")); + } + } + parseDateDay() { + if (this.char === CHAR_T || this.char === CHAR_SP) { + if (this.state.buf.length < 2) { + throw this.error(new TomlError("Days less than 10 must be zero padded to two characters")); + } + this.state.result += "-" + this.state.buf; + this.state.buf = ""; + return this.next(this.parseStartTimeHour); + } else if (this.atEndOfWord()) { + return this.returnNow(createDate(this.state.result + "-" + this.state.buf)); + } else if (isDigit(this.char)) { + this.consume(); + } else { + throw this.error(new TomlError("Incomplete datetime")); + } + } + parseStartTimeHour() { + if (this.atEndOfWord()) { + return this.returnNow(createDate(this.state.result)); + } else { + return this.goto(this.parseTimeHour); + } + } + parseTimeHour() { + if (this.char === CHAR_COLON) { + if (this.state.buf.length < 2) { + throw this.error(new TomlError("Hours less than 10 must be zero padded to two characters")); + } + this.state.result += "T" + this.state.buf; + this.state.buf = ""; + return this.next(this.parseTimeMin); + } else if (isDigit(this.char)) { + this.consume(); + } else { + throw this.error(new TomlError("Incomplete datetime")); + } + } + parseTimeMin() { + if (this.state.buf.length < 2 && isDigit(this.char)) { + this.consume(); + } else if (this.state.buf.length === 2 && this.char === CHAR_COLON) { + this.state.result += ":" + this.state.buf; + this.state.buf = ""; + return this.next(this.parseTimeSec); + } else { + throw this.error(new TomlError("Incomplete datetime")); + } + } + parseTimeSec() { + if (isDigit(this.char)) { + this.consume(); + if (this.state.buf.length === 2) { + this.state.result += ":" + this.state.buf; + this.state.buf = ""; + return this.next(this.parseTimeZoneOrFraction); + } + } else { + throw this.error(new TomlError("Incomplete datetime")); + } + } + parseOnlyTimeHour() { + if (this.char === CHAR_COLON) { + if (this.state.buf.length < 2) { + throw this.error(new TomlError("Hours less than 10 must be zero padded to two characters")); + } + this.state.result = this.state.buf; + this.state.buf = ""; + return this.next(this.parseOnlyTimeMin); + } else { + throw this.error(new TomlError("Incomplete time")); + } + } + parseOnlyTimeMin() { + if (this.state.buf.length < 2 && isDigit(this.char)) { + this.consume(); + } else if (this.state.buf.length === 2 && this.char === CHAR_COLON) { + this.state.result += ":" + this.state.buf; + this.state.buf = ""; + return this.next(this.parseOnlyTimeSec); + } else { + throw this.error(new TomlError("Incomplete time")); + } + } + parseOnlyTimeSec() { + if (isDigit(this.char)) { + this.consume(); + if (this.state.buf.length === 2) { + return this.next(this.parseOnlyTimeFractionMaybe); + } + } else { + throw this.error(new TomlError("Incomplete time")); + } + } + parseOnlyTimeFractionMaybe() { + this.state.result += ":" + this.state.buf; + if (this.char === CHAR_PERIOD) { + this.state.buf = ""; + this.next(this.parseOnlyTimeFraction); + } else { + return this.return(createTime(this.state.result)); + } + } + parseOnlyTimeFraction() { + if (isDigit(this.char)) { + this.consume(); + } else if (this.atEndOfWord()) { + if (this.state.buf.length === 0) throw this.error(new TomlError("Expected digit in milliseconds")); + return this.returnNow(createTime(this.state.result + "." + this.state.buf)); + } else { + throw this.error(new TomlError("Unexpected character in datetime, expected period (.), minus (-), plus (+) or Z")); + } + } + parseTimeZoneOrFraction() { + if (this.char === CHAR_PERIOD) { + this.consume(); + this.next(this.parseDateTimeFraction); + } else if (this.char === CHAR_HYPHEN || this.char === CHAR_PLUS) { + this.consume(); + this.next(this.parseTimeZoneHour); + } else if (this.char === CHAR_Z) { + this.consume(); + return this.return(createDateTime(this.state.result + this.state.buf)); + } else if (this.atEndOfWord()) { + return this.returnNow(createDateTimeFloat(this.state.result + this.state.buf)); + } else { + throw this.error(new TomlError("Unexpected character in datetime, expected period (.), minus (-), plus (+) or Z")); + } + } + parseDateTimeFraction() { + if (isDigit(this.char)) { + this.consume(); + } else if (this.state.buf.length === 1) { + throw this.error(new TomlError("Expected digit in milliseconds")); + } else if (this.char === CHAR_HYPHEN || this.char === CHAR_PLUS) { + this.consume(); + this.next(this.parseTimeZoneHour); + } else if (this.char === CHAR_Z) { + this.consume(); + return this.return(createDateTime(this.state.result + this.state.buf)); + } else if (this.atEndOfWord()) { + return this.returnNow(createDateTimeFloat(this.state.result + this.state.buf)); + } else { + throw this.error(new TomlError("Unexpected character in datetime, expected period (.), minus (-), plus (+) or Z")); + } + } + parseTimeZoneHour() { + if (isDigit(this.char)) { + this.consume(); + if (/\d\d$/.test(this.state.buf)) return this.next(this.parseTimeZoneSep); + } else { + throw this.error(new TomlError("Unexpected character in datetime, expected digit")); + } + } + parseTimeZoneSep() { + if (this.char === CHAR_COLON) { + this.consume(); + this.next(this.parseTimeZoneMin); + } else { + throw this.error(new TomlError("Unexpected character in datetime, expected colon")); + } + } + parseTimeZoneMin() { + if (isDigit(this.char)) { + this.consume(); + if (/\d\d$/.test(this.state.buf)) return this.return(createDateTime(this.state.result + this.state.buf)); + } else { + throw this.error(new TomlError("Unexpected character in datetime, expected digit")); + } + } + /* BOOLEAN */ + parseBoolean() { + if (this.char === CHAR_t) { + this.consume(); + return this.next(this.parseTrue_r); + } else if (this.char === CHAR_f) { + this.consume(); + return this.next(this.parseFalse_a); + } + } + parseTrue_r() { + if (this.char === CHAR_r) { + this.consume(); + return this.next(this.parseTrue_u); + } else { + throw this.error(new TomlError("Invalid boolean, expected true or false")); + } + } + parseTrue_u() { + if (this.char === CHAR_u) { + this.consume(); + return this.next(this.parseTrue_e); + } else { + throw this.error(new TomlError("Invalid boolean, expected true or false")); + } + } + parseTrue_e() { + if (this.char === CHAR_e) { + return this.return(true); + } else { + throw this.error(new TomlError("Invalid boolean, expected true or false")); + } + } + parseFalse_a() { + if (this.char === CHAR_a) { + this.consume(); + return this.next(this.parseFalse_l); + } else { + throw this.error(new TomlError("Invalid boolean, expected true or false")); + } + } + parseFalse_l() { + if (this.char === CHAR_l) { + this.consume(); + return this.next(this.parseFalse_s); + } else { + throw this.error(new TomlError("Invalid boolean, expected true or false")); + } + } + parseFalse_s() { + if (this.char === CHAR_s) { + this.consume(); + return this.next(this.parseFalse_e); + } else { + throw this.error(new TomlError("Invalid boolean, expected true or false")); + } + } + parseFalse_e() { + if (this.char === CHAR_e) { + return this.return(false); + } else { + throw this.error(new TomlError("Invalid boolean, expected true or false")); + } + } + /* INLINE LISTS */ + parseInlineList() { + if (this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M || this.char === CTRL_J) { + return null; + } else if (this.char === Parser.END) { + throw this.error(new TomlError("Unterminated inline array")); + } else if (this.char === CHAR_NUM) { + return this.call(this.parseComment); + } else if (this.char === CHAR_RSQB) { + return this.return(this.state.resultArr || InlineList()); + } else { + return this.callNow(this.parseValue, this.recordInlineListValue); + } + } + recordInlineListValue(value) { + if (this.state.resultArr) { + const listType = this.state.resultArr[_contentType]; + const valueType = tomlType(value); + if (listType !== valueType) { + throw this.error(new TomlError(`Inline lists must be a single type, not a mix of ${listType} and ${valueType}`)); + } + } else { + this.state.resultArr = InlineList(tomlType(value)); + } + if (isFloat(value) || isInteger(value)) { + this.state.resultArr.push(value.valueOf()); + } else { + this.state.resultArr.push(value); + } + return this.goto(this.parseInlineListNext); + } + parseInlineListNext() { + if (this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M || this.char === CTRL_J) { + return null; + } else if (this.char === CHAR_NUM) { + return this.call(this.parseComment); + } else if (this.char === CHAR_COMMA) { + return this.next(this.parseInlineList); + } else if (this.char === CHAR_RSQB) { + return this.goto(this.parseInlineList); + } else { + throw this.error(new TomlError("Invalid character, expected whitespace, comma (,) or close bracket (])")); + } + } + /* INLINE TABLE */ + parseInlineTable() { + if (this.char === CHAR_SP || this.char === CTRL_I) { + return null; + } else if (this.char === Parser.END || this.char === CHAR_NUM || this.char === CTRL_J || this.char === CTRL_M) { + throw this.error(new TomlError("Unterminated inline array")); + } else if (this.char === CHAR_RCUB) { + return this.return(this.state.resultTable || InlineTable()); + } else { + if (!this.state.resultTable) this.state.resultTable = InlineTable(); + return this.callNow(this.parseAssign, this.recordInlineTableValue); + } + } + recordInlineTableValue(kv) { + let target = this.state.resultTable; + let finalKey = kv.key.pop(); + for (let kw of kv.key) { + if (hasKey(target, kw) && (!isTable(target[kw]) || target[kw][_declared])) { + throw this.error(new TomlError("Can't redefine existing key")); + } + target = target[kw] = target[kw] || Table(); + } + if (hasKey(target, finalKey)) { + throw this.error(new TomlError("Can't redefine existing key")); + } + if (isInteger(kv.value) || isFloat(kv.value)) { + target[finalKey] = kv.value.valueOf(); + } else { + target[finalKey] = kv.value; + } + return this.goto(this.parseInlineTableNext); + } + parseInlineTableNext() { + if (this.char === CHAR_SP || this.char === CTRL_I) { + return null; + } else if (this.char === Parser.END || this.char === CHAR_NUM || this.char === CTRL_J || this.char === CTRL_M) { + throw this.error(new TomlError("Unterminated inline array")); + } else if (this.char === CHAR_COMMA) { + return this.next(this.parseInlineTable); + } else if (this.char === CHAR_RCUB) { + return this.goto(this.parseInlineTable); + } else { + throw this.error(new TomlError("Invalid character, expected whitespace, comma (,) or close bracket (])")); + } + } + } + return TOMLParser; + } + } + }); + + // node_modules/@iarna/toml/parse-pretty-error.js + var require_parse_pretty_error = __commonJS({ + "node_modules/@iarna/toml/parse-pretty-error.js"(exports2, module2) { + "use strict"; + module2.exports = prettyError; + function prettyError(err, buf) { + if (err.pos == null || err.line == null) return err; + let msg = err.message; + msg += ` at row ${err.line + 1}, col ${err.col + 1}, pos ${err.pos}: +`; + if (buf && buf.split) { + const lines = buf.split(/\n/); + const lineNumWidth = String(Math.min(lines.length, err.line + 3)).length; + let linePadding = " "; + while (linePadding.length < lineNumWidth) linePadding += " "; + for (let ii = Math.max(0, err.line - 1); ii < Math.min(lines.length, err.line + 2); ++ii) { + let lineNum = String(ii + 1); + if (lineNum.length < lineNumWidth) lineNum = " " + lineNum; + if (err.line === ii) { + msg += lineNum + "> " + lines[ii] + "\n"; + msg += linePadding + " "; + for (let hh = 0; hh < err.col; ++hh) { + msg += " "; + } + msg += "^\n"; + } else { + msg += lineNum + ": " + lines[ii] + "\n"; + } + } + } + err.message = msg + "\n"; + return err; + } + } + }); + + // node_modules/@iarna/toml/parse-string.js + var require_parse_string = __commonJS({ + "node_modules/@iarna/toml/parse-string.js"(exports2, module2) { + "use strict"; + module2.exports = parseString; + var TOMLParser = require_toml_parser(); + var prettyError = require_parse_pretty_error(); + function parseString(str) { + if (globalThis.Buffer && globalThis.Buffer.isBuffer(str)) { + str = str.toString("utf8"); + } + const parser = new TOMLParser(); + try { + parser.parse(str); + return parser.finish(); + } catch (err) { + throw prettyError(err, str); + } + } + } + }); + + // node_modules/@iarna/toml/parse-async.js + var require_parse_async = __commonJS({ + "node_modules/@iarna/toml/parse-async.js"(exports2, module2) { + "use strict"; + module2.exports = parseAsync; + var TOMLParser = require_toml_parser(); + var prettyError = require_parse_pretty_error(); + function parseAsync(str, opts) { + if (!opts) opts = {}; + const index = 0; + const blocksize = opts.blocksize || 40960; + const parser = new TOMLParser(); + return new Promise((resolve, reject) => { + setImmediate(parseAsyncNext, index, blocksize, resolve, reject); + }); + function parseAsyncNext(index2, blocksize2, resolve, reject) { + if (index2 >= str.length) { + try { + return resolve(parser.finish()); + } catch (err) { + return reject(prettyError(err, str)); + } + } + try { + parser.parse(str.slice(index2, index2 + blocksize2)); + setImmediate(parseAsyncNext, index2 + blocksize2, blocksize2, resolve, reject); + } catch (err) { + reject(prettyError(err, str)); + } + } + } + } + }); + + // node_modules/component-emitter/index.js + var require_component_emitter = __commonJS({ + "node_modules/component-emitter/index.js"(exports2, module2) { + function Emitter(object) { + if (object) { + return mixin(object); + } + this._callbacks = /* @__PURE__ */ new Map(); + } + function mixin(object) { + Object.assign(object, Emitter.prototype); + object._callbacks = /* @__PURE__ */ new Map(); + return object; + } + Emitter.prototype.on = function(event, listener) { + const callbacks = this._callbacks.get(event) ?? []; + callbacks.push(listener); + this._callbacks.set(event, callbacks); + return this; + }; + Emitter.prototype.once = function(event, listener) { + const on = (...arguments_) => { + this.off(event, on); + listener.apply(this, arguments_); + }; + on.fn = listener; + this.on(event, on); + return this; + }; + Emitter.prototype.off = function(event, listener) { + if (event === void 0 && listener === void 0) { + this._callbacks.clear(); + return this; + } + if (listener === void 0) { + this._callbacks.delete(event); + return this; + } + const callbacks = this._callbacks.get(event); + if (callbacks) { + for (const [index, callback] of callbacks.entries()) { + if (callback === listener || callback.fn === listener) { + callbacks.splice(index, 1); + break; + } + } + if (callbacks.length === 0) { + this._callbacks.delete(event); + } else { + this._callbacks.set(event, callbacks); + } + } + return this; + }; + Emitter.prototype.emit = function(event, ...arguments_) { + const callbacks = this._callbacks.get(event); + if (callbacks) { + const callbacksCopy = [...callbacks]; + for (const callback of callbacksCopy) { + callback.apply(this, arguments_); + } + } + return this; + }; + Emitter.prototype.listeners = function(event) { + return this._callbacks.get(event) ?? []; + }; + Emitter.prototype.listenerCount = function(event) { + if (event) { + return this.listeners(event).length; + } + let totalCount = 0; + for (const callbacks of this._callbacks.values()) { + totalCount += callbacks.length; + } + return totalCount; + }; + Emitter.prototype.hasListeners = function(event) { + return this.listenerCount(event) > 0; + }; + Emitter.prototype.addEventListener = Emitter.prototype.on; + Emitter.prototype.removeListener = Emitter.prototype.off; + Emitter.prototype.removeEventListener = Emitter.prototype.off; + Emitter.prototype.removeAllListeners = Emitter.prototype.off; + if (typeof module2 !== "undefined") { + module2.exports = Emitter; + } + } + }); + + // node_modules/stream/index.js + var require_stream = __commonJS({ + "node_modules/stream/index.js"(exports2, module2) { + var Emitter = require_component_emitter(); + function Stream() { + Emitter.call(this); + } + Stream.prototype = new Emitter(); + module2.exports = Stream; + Stream.Stream = Stream; + Stream.prototype.pipe = function(dest, options) { + var source = this; + function ondata(chunk) { + if (dest.writable) { + if (false === dest.write(chunk) && source.pause) { + source.pause(); + } + } + } + source.on("data", ondata); + function ondrain() { + if (source.readable && source.resume) { + source.resume(); + } + } + dest.on("drain", ondrain); + if (!dest._isStdio && (!options || options.end !== false)) { + source.on("end", onend); + source.on("close", onclose); + } + var didOnEnd = false; + function onend() { + if (didOnEnd) return; + didOnEnd = true; + dest.end(); + } + function onclose() { + if (didOnEnd) return; + didOnEnd = true; + if (typeof dest.destroy === "function") dest.destroy(); + } + function onerror(er) { + cleanup(); + if (!this.hasListeners("error")) { + throw er; + } + } + source.on("error", onerror); + dest.on("error", onerror); + function cleanup() { + source.off("data", ondata); + dest.off("drain", ondrain); + source.off("end", onend); + source.off("close", onclose); + source.off("error", onerror); + dest.off("error", onerror); + source.off("end", cleanup); + source.off("close", cleanup); + dest.off("end", cleanup); + dest.off("close", cleanup); + } + source.on("end", cleanup); + source.on("close", cleanup); + dest.on("end", cleanup); + dest.on("close", cleanup); + dest.emit("pipe", source); + return dest; + }; + } + }); + + // node_modules/@iarna/toml/parse-stream.js + var require_parse_stream = __commonJS({ + "node_modules/@iarna/toml/parse-stream.js"(exports2, module2) { + "use strict"; + module2.exports = parseStream; + var stream = require_stream(); + var TOMLParser = require_toml_parser(); + function parseStream(stm) { + if (stm) { + return parseReadable(stm); + } else { + return parseTransform(stm); + } + } + function parseReadable(stm) { + const parser = new TOMLParser(); + stm.setEncoding("utf8"); + return new Promise((resolve, reject) => { + let readable; + let ended = false; + let errored = false; + function finish() { + ended = true; + if (readable) return; + try { + resolve(parser.finish()); + } catch (err) { + reject(err); + } + } + function error(err) { + errored = true; + reject(err); + } + stm.once("end", finish); + stm.once("error", error); + readNext(); + function readNext() { + readable = true; + let data; + while ((data = stm.read()) !== null) { + try { + parser.parse(data); + } catch (err) { + return error(err); + } + } + readable = false; + if (ended) return finish(); + if (errored) return; + stm.once("readable", readNext); + } + }); + } + function parseTransform() { + const parser = new TOMLParser(); + return new stream.Transform({ + objectMode: true, + transform(chunk, encoding, cb) { + try { + parser.parse(chunk.toString(encoding)); + } catch (err) { + this.emit("error", err); + } + cb(); + }, + flush(cb) { + try { + this.push(parser.finish()); + } catch (err) { + this.emit("error", err); + } + cb(); + } + }); + } + } + }); + + // node_modules/@iarna/toml/parse.js + var require_parse = __commonJS({ + "node_modules/@iarna/toml/parse.js"(exports2, module2) { + "use strict"; + module2.exports = require_parse_string(); + module2.exports.async = require_parse_async(); + module2.exports.stream = require_parse_stream(); + module2.exports.prettyError = require_parse_pretty_error(); + } + }); + + // node_modules/@iarna/toml/stringify.js + var require_stringify = __commonJS({ + "node_modules/@iarna/toml/stringify.js"(exports2, module2) { + "use strict"; + module2.exports = stringify; + module2.exports.value = stringifyInline; + function stringify(obj) { + if (obj === null) throw typeError("null"); + if (obj === void 0) throw typeError("undefined"); + if (typeof obj !== "object") throw typeError(typeof obj); + if (typeof obj.toJSON === "function") obj = obj.toJSON(); + if (obj == null) return null; + const type = tomlType2(obj); + if (type !== "table") throw typeError(type); + return stringifyObject("", "", obj); + } + function typeError(type) { + return new Error("Can only stringify objects, not " + type); + } + function arrayOneTypeError() { + return new Error("Array values can't have mixed types"); + } + function getInlineKeys(obj) { + return Object.keys(obj).filter((key) => isInline(obj[key])); + } + function getComplexKeys(obj) { + return Object.keys(obj).filter((key) => !isInline(obj[key])); + } + function toJSON(obj) { + let nobj = Array.isArray(obj) ? [] : Object.prototype.hasOwnProperty.call(obj, "__proto__") ? { ["__proto__"]: void 0 } : {}; + for (let prop of Object.keys(obj)) { + if (obj[prop] && typeof obj[prop].toJSON === "function" && !("toISOString" in obj[prop])) { + nobj[prop] = obj[prop].toJSON(); + } else { + nobj[prop] = obj[prop]; + } + } + return nobj; + } + function stringifyObject(prefix, indent, obj) { + obj = toJSON(obj); + var inlineKeys; + var complexKeys; + inlineKeys = getInlineKeys(obj); + complexKeys = getComplexKeys(obj); + var result = []; + var inlineIndent = indent || ""; + inlineKeys.forEach((key) => { + var type = tomlType2(obj[key]); + if (type !== "undefined" && type !== "null") { + result.push(inlineIndent + stringifyKey(key) + " = " + stringifyAnyInline(obj[key], true)); + } + }); + if (result.length > 0) result.push(""); + var complexIndent = prefix && inlineKeys.length > 0 ? indent + " " : ""; + complexKeys.forEach((key) => { + result.push(stringifyComplex(prefix, complexIndent, key, obj[key])); + }); + return result.join("\n"); + } + function isInline(value) { + switch (tomlType2(value)) { + case "undefined": + case "null": + case "integer": + case "nan": + case "float": + case "boolean": + case "string": + case "datetime": + return true; + case "array": + return value.length === 0 || tomlType2(value[0]) !== "table"; + case "table": + return Object.keys(value).length === 0; + /* istanbul ignore next */ + default: + return false; + } + } + function tomlType2(value) { + if (value === void 0) { + return "undefined"; + } else if (value === null) { + return "null"; + } else if (typeof value === "bigint" || Number.isInteger(value) && !Object.is(value, -0)) { + return "integer"; + } else if (typeof value === "number") { + return "float"; + } else if (typeof value === "boolean") { + return "boolean"; + } else if (typeof value === "string") { + return "string"; + } else if ("toISOString" in value) { + return isNaN(value) ? "undefined" : "datetime"; + } else if (Array.isArray(value)) { + return "array"; + } else { + return "table"; + } + } + function stringifyKey(key) { + var keyStr = String(key); + if (/^[-A-Za-z0-9_]+$/.test(keyStr)) { + return keyStr; + } else { + return stringifyBasicString(keyStr); + } + } + function stringifyBasicString(str) { + return '"' + escapeString(str).replace(/"/g, '\\"') + '"'; + } + function stringifyLiteralString(str) { + return "'" + str + "'"; + } + function numpad(num, str) { + while (str.length < num) str = "0" + str; + return str; + } + function escapeString(str) { + return str.replace(/\\/g, "\\\\").replace(/[\b]/g, "\\b").replace(/\t/g, "\\t").replace(/\n/g, "\\n").replace(/\f/g, "\\f").replace(/\r/g, "\\r").replace(/([\u0000-\u001f\u007f])/, (c) => "\\u" + numpad(4, c.codePointAt(0).toString(16))); + } + function stringifyMultilineString(str) { + let escaped = str.split(/\n/).map((str2) => { + return escapeString(str2).replace(/"(?="")/g, '\\"'); + }).join("\n"); + if (escaped.slice(-1) === '"') escaped += "\\\n"; + return '"""\n' + escaped + '"""'; + } + function stringifyAnyInline(value, multilineOk) { + let type = tomlType2(value); + if (type === "string") { + if (multilineOk && /\n/.test(value)) { + type = "string-multiline"; + } else if (!/[\b\t\n\f\r']/.test(value) && /"/.test(value)) { + type = "string-literal"; + } + } + return stringifyInline(value, type); + } + function stringifyInline(value, type) { + if (!type) type = tomlType2(value); + switch (type) { + case "string-multiline": + return stringifyMultilineString(value); + case "string": + return stringifyBasicString(value); + case "string-literal": + return stringifyLiteralString(value); + case "integer": + return stringifyInteger(value); + case "float": + return stringifyFloat(value); + case "boolean": + return stringifyBoolean(value); + case "datetime": + return stringifyDatetime(value); + case "array": + return stringifyInlineArray(value.filter((_) => tomlType2(_) !== "null" && tomlType2(_) !== "undefined" && tomlType2(_) !== "nan")); + case "table": + return stringifyInlineTable(value); + /* istanbul ignore next */ + default: + throw typeError(type); + } + } + function stringifyInteger(value) { + return String(value).replace(/\B(?=(\d{3})+(?!\d))/g, "_"); + } + function stringifyFloat(value) { + if (value === Infinity) { + return "inf"; + } else if (value === -Infinity) { + return "-inf"; + } else if (Object.is(value, NaN)) { + return "nan"; + } else if (Object.is(value, -0)) { + return "-0.0"; + } + var chunks = String(value).split("."); + var int = chunks[0]; + var dec = chunks[1] || 0; + return stringifyInteger(int) + "." + dec; + } + function stringifyBoolean(value) { + return String(value); + } + function stringifyDatetime(value) { + return value.toISOString(); + } + function isNumber(type) { + return type === "float" || type === "integer"; + } + function arrayType(values) { + var contentType = tomlType2(values[0]); + if (values.every((_) => tomlType2(_) === contentType)) return contentType; + if (values.every((_) => isNumber(tomlType2(_)))) return "float"; + return "mixed"; + } + function validateArray(values) { + const type = arrayType(values); + if (type === "mixed") { + throw arrayOneTypeError(); + } + return type; + } + function stringifyInlineArray(values) { + values = toJSON(values); + const type = validateArray(values); + var result = "["; + var stringified = values.map((_) => stringifyInline(_, type)); + if (stringified.join(", ").length > 60 || /\n/.test(stringified)) { + result += "\n " + stringified.join(",\n ") + "\n"; + } else { + result += " " + stringified.join(", ") + (stringified.length > 0 ? " " : ""); + } + return result + "]"; + } + function stringifyInlineTable(value) { + value = toJSON(value); + var result = []; + Object.keys(value).forEach((key) => { + result.push(stringifyKey(key) + " = " + stringifyAnyInline(value[key], false)); + }); + return "{ " + result.join(", ") + (result.length > 0 ? " " : "") + "}"; + } + function stringifyComplex(prefix, indent, key, value) { + var valueType = tomlType2(value); + if (valueType === "array") { + return stringifyArrayOfTables(prefix, indent, key, value); + } else if (valueType === "table") { + return stringifyComplexTable(prefix, indent, key, value); + } else { + throw typeError(valueType); + } + } + function stringifyArrayOfTables(prefix, indent, key, values) { + values = toJSON(values); + validateArray(values); + var firstValueType = tomlType2(values[0]); + if (firstValueType !== "table") throw typeError(firstValueType); + var fullKey = prefix + stringifyKey(key); + var result = ""; + values.forEach((table) => { + if (result.length > 0) result += "\n"; + result += indent + "[[" + fullKey + "]]\n"; + result += stringifyObject(fullKey + ".", indent, table); + }); + return result; + } + function stringifyComplexTable(prefix, indent, key, value) { + var fullKey = prefix + stringifyKey(key); + var result = ""; + if (getInlineKeys(value).length > 0) { + result += indent + "[" + fullKey + "]\n"; + } + return result + stringifyObject(fullKey + ".", indent, value); + } + } + }); + + // node_modules/@iarna/toml/toml.js + var require_toml = __commonJS({ + "node_modules/@iarna/toml/toml.js"(exports2) { + "use strict"; + exports2.parse = require_parse(); + exports2.stringify = require_stringify(); + } + }); + + // src/elemtoml.ts + var import_utility_types = __toESM(require_dist()); + function register_element(name, elem) { + console.debug("Element registered: ", elem); + let tmp_value = elem; + if (tmp_value.namedBehavior) { + const found_behaviour = window.behaviors[tmp_value.namedBehavior]; + if (typeof found_behaviour == "function") { + tmp_value.tick = found_behaviour; + } else { + tmp_value.behavior = found_behaviour; + } + } + window.elements[name] = tmp_value; + } + function register_elements(elems) { + Object.entries(elems).forEach(([key, value]) => { + register_element(key, value); + }); + } + + // src/cfg_loader.ts + var import_toml = __toESM(require_toml()); + + // src/utils.ts + var ScriptRunError = class extends Error { + constructor(message, asserter = void 0) { + super(message); + this.name = "MyError"; + Error.captureStackTrace?.(this, asserter || this.constructor); + } + }; + async function run_script(path) { + try { + let resp = await fetch(path); + const text = await resp.text(); + if (resp.ok) { + const result = Function(text)(); + if (result !== void 0 && result !== 0) { + throw new ScriptRunError(`Script exited with code ${result}`); + } + } else { + throw new ScriptRunError(`Script ${path} not found`); + } + } catch (e) { + throw e; + } + } + + // src/cfg_loader.ts + var Package = class { + /** + * Constructs a new Package instance with the given configuration (as loaded + * from a TOML configuration). + * + * @param config The parsed package configuration. + */ + constructor(config) { + /** + * The list of elements that have been loaded for this package. + */ + this.loaded_elems = []; + this.cfg = config; + console.log(this); + } + /** + * Loads external elements defined in the package configuration. + * Fetches, parses, and registers each element. + * + * @returns A promise that resolves to an `ElementDict` + * @private + */ + async load_elems() { + for (const i of this.cfg.mod.external_elements) { + console.log("loading element:", i); + try { + let resp = await fetch(i.path); + const parsed = (0, import_toml.parse)(await resp.text()); + console.log(parsed); + register_element(i.name, parsed); + } catch (err) { + console.error(err); + } + } + let tmp = {}; + this.loaded_elems.forEach((elem) => { + tmp[elem.name] = elem; + }); + return tmp; + } + /** + * Retrieves the list of elements that have been loaded for this package. + * @returns An array of loaded elements. + */ + get_loaded_elems() { + return this.loaded_elems; + } + /** + * Loads the mod, runs scripts, and registers elements. + */ + load_mod(prompt_quene2) { + const incompatibilities = window.enabledMods.filter((x) => this.cfg.mod.incompatible_mods.includes(x)); + if (incompatibilities.length != 0) { + prompt_quene2.push(() => { + window.promptText( + `A: ${this.cfg.mod.name} + B: ${incompatibilities.join(", ")}`, + () => { + }, + "Mod incompatibility" + ); + }); + return; + } + console.debug(this.cfg.scripts); + if (this.cfg.scripts.preload !== void 0) { + for (const i of this.cfg.scripts.preload) { + run_script(i); + } + } + this.load_elems().then((elems) => { + console.debug("elems:", elems); + register_elements(elems); + }); + if (this.cfg.scripts.postload !== void 0) { + for (const i of this.cfg.scripts.postload) { + run_script(i); + } + } + } + }; + function load(object) { + return object; + } + + // src/mod.ts + var import_toml2 = __toESM(require_toml()); + + // src/mod_finder.ts + function find_mod(name, onfind) { + console.log(name, `${name}/mod.toml`); + fetch(`${name}/mod.toml`).then(async (x) => { + console.log(x.url); + if (x.ok) { + onfind(await x.text()); + } else { + } + }).catch((err) => { + console.error(err); + }); + } + + // src/mod.ts + function shuffle_to_start(input, i) { + let tmp = input.filter((x) => x !== input[i]); + tmp.unshift(input[i]); + return tmp; + } + var prompt_quene = []; + var mods = JSON.parse(localStorage.getItem("enabledMods") || ""); + if (mods[0] !== "mods/fancy_loader.js") { + prompt_quene.push(() => { + window.promptConfirm( + `Refresh again to reload as the first mod (otherwise, there can be odd glitches with dependencies etc.)`, + (x) => { + if (x) { + window.location.reload(); + } + }, + "fancy_loader.js says..." + ); + }); + const shuffled_mods = shuffle_to_start(mods, mods.findIndex((x) => x == "mods/fancy_loader.js")); + localStorage.setItem("enabledMods", JSON.stringify(shuffled_mods)); + } + for (const i of window.enabledMods) { + if (i.endsWith(".toml")) { + console.trace("Loading mod:", i, i.slice(0, -5)); + find_mod(i.slice(0, -5), (text) => { + const parsed = import_toml2.default.parse(text); + console.debug("Parsed mod TOML:", import_toml2.default.parse(text)); + let pkg = new Package(load(parsed)); + pkg.load_mod(prompt_quene); + console.debug("Loaded mod:", pkg); + }); + } + } + window.addEventListener("load", () => { + for (const i of prompt_quene) { + i(); + } + }); +})(); +/*! Bundled license information: + +utility-types/dist/index.js: + (** + * @author Piotr Witek (http://piotrwitek.github.io) + * @copyright Copyright (c) 2016 Piotr Witek + * @license MIT + *) +*/ diff --git a/mods/loader_test/mod.toml b/mods/loader_test/mod.toml index 8678dd7c..586d0a28 100644 --- a/mods/loader_test/mod.toml +++ b/mods/loader_test/mod.toml @@ -1,25 +1,23 @@ [mod] name = "test-finder" version = "0.1.0" -entry_point = "" external_elements = [ {path = "mods/loader_test/test_element.toml", name = "test_element"}, ] -[elements] +# Completely arbitrary +incompatible_mods = [ + "mods/nousersthings.js", + "mods/lightmap.js", + "mods/betterSettings.js" +] -# [elements.test_element] -# name = "test_element" -# color = ["#FF00FF", "#00FF00"] -# state = "solid" -# behaviour = [["XX","XX","XX"],["XX","XX","XX"],["XX","XX","XX"]] -# category = "solids" -# tempHigh = 100 -# stateHigh = "gold" -# density = 100 -# conduct = 1.0 - -# [elements.test_element.reactions] -# water = {elem = "ash", elem2 = "pop"} -# cement = {elem1 = "sand"} -# blood = {elem1 = "n_explosion"} \ No newline at end of file +[scripts] +preload = [ + "mods/loader_test/pre_load.js", + "mods/loader_test/random_file.js" +] +postload = [ + "mods/loader_test/post_load.js", + "mods/loader_test/random_file.js" +] \ No newline at end of file diff --git a/mods/loader_test/post_load.js b/mods/loader_test/post_load.js new file mode 100644 index 00000000..78f2b805 --- /dev/null +++ b/mods/loader_test/post_load.js @@ -0,0 +1 @@ +console.log("POST LOAD RUNNING") \ No newline at end of file diff --git a/mods/loader_test/pre_load.js b/mods/loader_test/pre_load.js new file mode 100644 index 00000000..53652f98 --- /dev/null +++ b/mods/loader_test/pre_load.js @@ -0,0 +1 @@ +console.log("PRELOAD RUNNING") \ No newline at end of file diff --git a/mods/loader_test/random_file.js b/mods/loader_test/random_file.js new file mode 100644 index 00000000..9589b727 --- /dev/null +++ b/mods/loader_test/random_file.js @@ -0,0 +1 @@ +console.log("THIS OTHER THING RUNNING") \ No newline at end of file diff --git a/mods/loader_test/test_element.toml b/mods/loader_test/test_element.toml index 7e3e0634..71e49017 100644 --- a/mods/loader_test/test_element.toml +++ b/mods/loader_test/test_element.toml @@ -1,7 +1,7 @@ -name = "test_element_b" +name = "test_element" color = ["#FF00FF", "#00FF00"] state = "solid" -behaviour = [["XX","XX","XX"],["XX","XX","XX"],["XX","XX","XX"]] +namedBehavior = "POWDER_OLD" category = "solids" tempHigh = 100 stateHigh = "gold" From d4abb188baf6dfb8bfc86c7057ccb64024cb3fee Mon Sep 17 00:00:00 2001 From: slweeb <91897291+slweeb@users.noreply.github.com> Date: Mon, 1 Dec 2025 17:36:47 -0500 Subject: [PATCH 03/38] Update borders.js --- mods/borders.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mods/borders.js b/mods/borders.js index b7fc5903..0153f776 100644 --- a/mods/borders.js +++ b/mods/borders.js @@ -1,6 +1,8 @@ let isMachine = {"machines":true} elements.static.border = false; +elements.mixer.border = false; +elements.grinder.border = false; window.addEventListener("load", () => { let oldPreRenderer = viewInfo[1].pre; From 37cf6ec025d8d1c2dd2e5d8dd45542d95ce0a334 Mon Sep 17 00:00:00 2001 From: slweeb <91897291+slweeb@users.noreply.github.com> Date: Mon, 1 Dec 2025 18:04:07 -0500 Subject: [PATCH 04/38] Update borders.js --- mods/borders.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mods/borders.js b/mods/borders.js index 0153f776..9d96dcf3 100644 --- a/mods/borders.js +++ b/mods/borders.js @@ -12,6 +12,7 @@ window.addEventListener("load", () => { currentPixels.forEach(pixel => { if ((elements[pixel.element].movable !== true && isMachine[elements[pixel.element].category] === undefined) || elements[pixel.element].isGas === true) return; if (elements[pixel.element].border === false) return; + if (pixel.alpha === 0) return; let edge = false; for (var i = 0; i < adjacentCoords.length; i++) { var coords = adjacentCoords[i]; @@ -29,6 +30,7 @@ window.addEventListener("load", () => { viewInfo[1].pixel = function(pixel, ctx) { if (elements[pixel.element].movable || isMachine[elements[pixel.element].category] === true) return oldPixelRenderer(pixel, ctx); + if (pixel.alpha === 0) return; let edge = false; if (elements[pixel.element].border !== false) { for (var i = 0; i < adjacentCoords.length; i++) { From a280825e0214ba7801b66b2a482e78c4a157005b Mon Sep 17 00:00:00 2001 From: slweeb <91897291+slweeb@users.noreply.github.com> Date: Thu, 4 Dec 2025 15:53:47 -0500 Subject: [PATCH 05/38] no_blood.js --- mod-list.html | 2 ++ mods/no_blood.js | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 mods/no_blood.js diff --git a/mod-list.html b/mod-list.html index 5ffc9e13..506e778d 100644 --- a/mod-list.html +++ b/mod-list.html @@ -147,6 +147,7 @@ glow.js[CHROME ONLY] Adds a cool lighting effect to many emissive pixels, like FireR74n gravity_test.jsTest for altered gravity, makes all pixels move inwardR74n mustard.jsMustard and Mustard SeedsR74n +no_blood.jsEffectively removes Blood and related elementsR74n rainbow_cursor.jsMakes your cursor multicoloredR74n random_everything.jsAllows every element to be spawned with RandomR74n rich_grain.jsChanges pixel grain to create richer colorsR74n @@ -353,6 +354,7 @@ miscible_psoup_and_birthpool.jsPrimordial Soup and Birthpool can mix (fey_and_more.js)Alice mobs.jsCreepers, Zombies, and SkeletonsAlice moretrees.js25 more tree and wood typesguzzo86 +no_blood.jsEffectively removes Blood and related elementsR74n nocancer.jsRemoves cancer one tick after it is createdmollthecoder nocancer2.jsRemoves cancer from the game altogether; May be incompatible with other mods that spawn cancermollthecoder nograssgrow.jsPrevents Grass from growingmollthecoder diff --git a/mods/no_blood.js b/mods/no_blood.js new file mode 100644 index 00000000..b326937b --- /dev/null +++ b/mods/no_blood.js @@ -0,0 +1,23 @@ +elements.blood.hidden = true; +elements.blood.tick = function (pixel) { + tryDelete(pixel.x, pixel.y); +} +elements.blood.onPlace = function (pixel) { + tryDelete(pixel.x, pixel.y); +} + +elements.infection.hidden = true; +elements.infection.tick = function (pixel) { + tryDelete(pixel.x, pixel.y); +} +elements.infection.onPlace = function (pixel) { + tryDelete(pixel.x, pixel.y); +} + +elements.antibody.hidden = true; +elements.antibody.tick = function (pixel) { + tryDelete(pixel.x, pixel.y); +} +elements.antibody.onPlace = function (pixel) { + tryDelete(pixel.x, pixel.y); +} \ No newline at end of file From ea840ed51070339ecf9a2dee74269c80a67e7161 Mon Sep 17 00:00:00 2001 From: Phi Date: Sun, 7 Dec 2025 08:33:42 +0000 Subject: [PATCH 06/38] I think this works --- mods/mars.js | 79 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 60 insertions(+), 19 deletions(-) diff --git a/mods/mars.js b/mods/mars.js index 22d17f11..864d909b 100644 --- a/mods/mars.js +++ b/mods/mars.js @@ -113,7 +113,7 @@ elements.mars_ironheart_ore = { elements.mars_ironheart = { color: ["#e8e8e8", "#bd1102"], behavior: [ - "XX|CR:radiation%3|XX", + "XX|XX|XX", "XX|XX|XX", "XX|XX|XX", ], @@ -191,22 +191,6 @@ elements.depleted_ironheart = { "electric": { elem1: "smint", elem2: "null" }, }, } -elements.feynmanium = { - color: "#8C7656", - behavior: [ - "XX|XX|XX|", - "XX|XX|XX|", - "M2|M1|M2|", - ], - category: "mars", - density: 1200, - state: "solid", - stateHigh: "molten_mars", - tempHigh: 6000, - reactions: { - "electric": { elem1: "smint", elem2: "null" }, - }, -} elements.antigunk = { color: "#cecece", behavior: behaviors.DGAS, @@ -391,8 +375,59 @@ elements.ironheart_core = { conduct: 1, extraInfo: "wha" } - -// 2.10.20 +elements.mars_fossil = { + color: ["#3d3d3d", "#707070", "#395034"], + behavior: behaviors.WALL, + category: "mars", + state: "solid", + tempHigh: 2500, + hardness: 0.975, + stateHigh: "molten_mars", + tempLow: -170, + stateLow: "mars_rock", + extraInfo: "Fossils! From Mars! Simply just smash them to get some tiny creatures.", + hardness: 0.4, + breakInto: "mars_debris" +} +elements.ironheart_gas = { + color: ["#500000ff", "#a7a7a7ff", "#a80000ff"], + behavior: behaviors.GAS, + category: "mars", + state: "gas", + tempLow: -100, + stateLow: "liquid_ironheart_gas", + extraInfo: "gasses" +} +elements.liquid_ironheart_gas = { + color: ["#500000ff", "#a7a7a7ff", "#a80000ff"], + behavior: behaviors.LIQUID, + category: "mars", + state: "liquid", + temp: -150, + extraInfo: "liquids" +} +elements.phing = { + color: "#00c40aff", + behavior: [ + "XX|XX|XX|", + "XX|XX|XX", + "XX|XX|XX", + ], + category: "phings", + state: "solid", + tempHigh: "800", + stateHigh: "dead_phing", + breakInto: "dead_phing", + extraInfo: "wha" +} +elements.dead_phing = { + color: "#947e00ff", + behavior: behaviors.POWDER, + category: "phings", + state: "solid", + extraInfo: "DEAD! You KILLED THEM!" +} +// 2.11.20 // most elements done :D // iron heart ore // iron heart @@ -427,6 +462,12 @@ elements.ironheart_core = { // Red Gold is Solid // Feynmanium // make things have recaitons +// its here! +// remove feynmanium +// ironheart is no longer radioactive +// fossils! + // Creditssss // Ghanisma - idea for 2.9.16 + From d287ec2e9ffcd29477ab99e1d2fc6ebd8d45a3be Mon Sep 17 00:00:00 2001 From: Mnem42 Date: Sun, 7 Dec 2025 14:24:29 +0000 Subject: [PATCH 07/38] zoom.js: ui improvements --- mods/zoom.js | 300 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 300 insertions(+) create mode 100644 mods/zoom.js diff --git a/mods/zoom.js b/mods/zoom.js new file mode 100644 index 00000000..eebd9dbb --- /dev/null +++ b/mods/zoom.js @@ -0,0 +1,300 @@ +const zoom_levels = [ + 0.5, + 1, + 2, + 3, + 6, + 12 +] +window.zoom_data_div = null + +window.zoom_level = 1 +window.zoom_panning = [0,0] + +function handle_zoom(direction){ + switch (direction){ + case "in": + if (!(zoom_level+1 in zoom_levels)) { break; } + window.zoom_level += 1 + break; + case "out": + if (!(zoom_level-1 in zoom_levels)) { break; } + window.zoom_level -= 1 + break; + } + rescale() +} + +function handle_pan(direction, speed){ + switch (direction){ + case "right": + zoom_panning[0] -= speed + break; + case "left": + zoom_panning[0] += speed + break; + case "up": + zoom_panning[1] += speed + break; + case "down": + zoom_panning[1] -= speed + break; + } + rescale() +} + +function gen_button(row, col, html, click, nopos, id){ + const elem = document.createElement("button") + + + if (!nopos){ + elem.style.gridColumn = row + elem.style.gridRow = col + } + if (id) { elem.id = id } + + // Table for the data-pos to assign (row major). If null, don't add. + const data_pos_map = [ + ["tl", null, "tr"], + [null, null, null], + ["bl", null, "br"] + ] + + elem.innerHTML = html + elem.onclick = click + + if (data_pos_map[row-1][col-1] !== null) { + elem.dataset.pos = data_pos_map[row-1][col-1] + } + + return elem +} + +function add_css(){ + const CSS = ` + #zm_data_div { margin-bottom: 10px; } + #canvasDiv { overflow: hidden } + + @media(pointer=coarse){ + #zm_floater_container#zm_floater_container { + width: 40%; + height: auto; + } + #zm_floater_container:has(#zm_collapse[data-collapsed="true"]){ + width: calc(40% / 3); + } + } + + @media(pointer:coarse) and (orientation:landscape){ + #zm_floater_container#zm_floater_container { + width: auto; + top: 5px; + } + #zm_floater_container:has(#zm_collapse[data-collapsed="true"]){ + width: calc(40% / 3); + } + } + + #zm_floater_container { + position: absolute; + display: grid; + + right: 5px; + bottom: 5px; + height: 100px; + aspect-ratio: 1; + + max-width: 200px; + max-height: 200px; + + border: 2px solid white; + background-color: black; + font-size: 120%; + + button { text-align: center; border: 0px solid white } + + button:where([data-pos="tl"]) { border-width: 0px 2px 2px 0px }; + button:where([data-pos="tr"]) { border-width: 2px 2px 0px 0px }; + button:where([data-pos="bl"]) { border-width: 0px 0px 2px 2px }; + button:where([data-pos="br"]) { border-width: 2px 0px 0px 2px }; + } + #zm_floater_container:has(#zm_collapse[data-collapsed="true"]) { + button:not(#zm_collapse) { + display: none + } + } + + .zm_corner { border: 2px solid white; } + + #zm_collapse { + grid-row: 3; + grid-column: 3; + } + #zm_collapse[data-collapsed="true"] { + grid-row: 1; + grid-column: 1; + border-width: 0px; + } + ` + + const style_div = document.createElement("style") + style_div.innerHTML = CSS + + document.head.appendChild(style_div) +} + +function add_zoom_floaters(){ + const container = document.createElement("div") + container.id = "zm_floater_container" + + // Pan mode selector (C: Coarse F: Fine) + const pan_mode_sel = gen_button( + 1,3, "C", + (evt) => { + evt.target.dataset.mode = evt.target.dataset.mode == "F" ? "C" : "F" + evt.target.innerText = evt.target.dataset.mode + }, + false, + "zm_panmode_sel" + ) + + const speed = () => + (window.zoom_level > 3 ? 5 : 10) * // More granular at higher zoom levels + (pan_mode_sel.dataset.mode == "F" ? 0.25 : 1) // Increase granularity in fine mode + + container.append( + // Direction buttons + gen_button(2,1, "↑", () => handle_pan("up" ,speed())), + gen_button(1,2, "←", () => handle_pan("left" ,speed())), + gen_button(3,2, "→", () => handle_pan("right" ,speed())), + gen_button(2,3, "↓", () => handle_pan("down" ,speed())), + + // Zoom buttons + gen_button(1,1, "+", () => handle_zoom("in")), + gen_button(3,1, "-", () => handle_zoom("out")), + + // Collapse button + gen_button( + 3,3, "#", + (evt) => { + evt.target.dataset.collapsed = evt.target.dataset.collapsed == "true" + ? "false" + : "true" + }, + true, + "zm_collapse" + ), + pan_mode_sel + ) + + const canvas_div = document.getElementById("canvasDiv") + canvas_div.appendChild(container) +} + +function rescale(){ + log_info() + + const scale = zoom_levels[zoom_level] + const x = zoom_panning[0] * (pixelSize * scale) + const y = zoom_panning[1] * (pixelSize * scale) + + gameCanvas.style.transform = `translate(${x}px,${y}px) scale(${scale})` +} + +function log_info(){ + // Values are negated to make them more intuitive + const x_pan = (-zoom_panning[0]).toString().padEnd(4) + const y_pan = (-zoom_panning[1]).toString().padEnd(4) + + if (zoom_data_div === null){ return; } + + zoom_data_div.innerText = "" + zoom_data_div.innerText += `Scale: ${zoom_levels[zoom_level]}x\n` + zoom_data_div.innerText += `Pan : ${x_pan}, ${y_pan}` +} + +function patch_keybinds(){ + // Be more granular at higher zoom levels + const speed_a = () => zoom_level > 3 ? 5 : 10 + const speed_b = () => zoom_level > 3 ? 10 : 20 + + keybinds["9"] = () => handle_zoom("in") + keybinds["0"] = () => handle_zoom("out") + + keybinds["w"] = () => handle_pan("up", speed_a()) + keybinds["a"] = () => handle_pan("left", speed_a()) + keybinds["s"] = () => handle_pan("down", speed_a()) + keybinds["d"] = () => handle_pan("right", speed_a()) + + keybinds["W"] = () => handle_pan("up", speed_b()) + keybinds["A"] = () => handle_pan("left", speed_b()) + keybinds["S"] = () => handle_pan("down", speed_b()) + keybinds["D"] = () => handle_pan("right", speed_b()) +} + +function patch_ui(){ + add_css() + add_zoom_floaters() + + zoom_data_div = document.createElement("div") + zoom_data_div.id = "zm_data_div" + document.getElementById("logDiv").prepend(zoom_data_div) + + const controls_table = document.getElementById("controlsTable").lastElementChild + controls_table.insertAdjacentHTML("beforeBegin",` + + Zoom in/out + + 9/ + 0 + + + + Pan + + W + A + S + D + + + + Pan (fast) + + Shift + + W + A + S + D + + + `) +} + +// Redefine to give correct numbers when zoomed +window.getMousePos = (canvas, evt) => { + if (evt.touches) { + evt.preventDefault(); + evt = evt.touches[0]; + isMobile = true; + } + const rect = canvas.getBoundingClientRect(); + + let x = (evt.clientX - rect.left) / zoom_levels[zoom_level]; + let y = (evt.clientY - rect.top) / zoom_levels[zoom_level]; + + x = Math.floor((x / canvas.clientWidth) * (width+1)); + y = Math.floor((y / canvas.clientHeight) * (height+1)); + + return {x:x, y:y}; +} + +runAfterReset(() => { + window.zoom_level = 1 + rescale() +}) + +runAfterLoad(() => { + patch_keybinds() + patch_ui() +}) \ No newline at end of file From 6d6cf9cb2dd4e2f3417b63370d17f5773b175343 Mon Sep 17 00:00:00 2001 From: Mnem42 Date: Sun, 7 Dec 2025 14:31:01 +0000 Subject: [PATCH 08/38] . --- mods/fancy_loader.js | 2442 ---------------------------- mods/loader_test/mod.toml | 23 - mods/loader_test/post_load.js | 1 - mods/loader_test/pre_load.js | 1 - mods/loader_test/random_file.js | 1 - mods/loader_test/test_element.toml | 14 - 6 files changed, 2482 deletions(-) delete mode 100644 mods/fancy_loader.js delete mode 100644 mods/loader_test/mod.toml delete mode 100644 mods/loader_test/post_load.js delete mode 100644 mods/loader_test/pre_load.js delete mode 100644 mods/loader_test/random_file.js delete mode 100644 mods/loader_test/test_element.toml diff --git a/mods/fancy_loader.js b/mods/fancy_loader.js deleted file mode 100644 index b701ba2e..00000000 --- a/mods/fancy_loader.js +++ /dev/null @@ -1,2442 +0,0 @@ -"use strict"; -(() => { - var __create = Object.create; - var __defProp = Object.defineProperty; - var __getOwnPropDesc = Object.getOwnPropertyDescriptor; - var __getOwnPropNames = Object.getOwnPropertyNames; - var __getProtoOf = Object.getPrototypeOf; - var __hasOwnProp = Object.prototype.hasOwnProperty; - var __commonJS = (cb, mod) => function __require() { - return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; - }; - var __copyProps = (to, from, except, desc) => { - if (from && typeof from === "object" || typeof from === "function") { - for (let key of __getOwnPropNames(from)) - if (!__hasOwnProp.call(to, key) && key !== except) - __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); - } - return to; - }; - var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( - // If the importer is in node compatibility mode or this is not an ESM - // file that has been converted to a CommonJS file using a Babel- - // compatible transform (i.e. "__esModule" has not been set), then set - // "default" to the CommonJS "module.exports" for node compatibility. - isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, - mod - )); - - // node_modules/utility-types/dist/aliases-and-guards.js - var require_aliases_and_guards = __commonJS({ - "node_modules/utility-types/dist/aliases-and-guards.js"(exports2) { - "use strict"; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.isPrimitive = function(val) { - if (val === null || val === void 0) { - return true; - } - switch (typeof val) { - case "string": - case "number": - case "bigint": - case "boolean": - case "symbol": { - return true; - } - default: - return false; - } - }; - exports2.isFalsy = function(val) { - return !val; - }; - exports2.isNullish = function(val) { - return val == null; - }; - } - }); - - // node_modules/utility-types/dist/functional-helpers.js - var require_functional_helpers = __commonJS({ - "node_modules/utility-types/dist/functional-helpers.js"(exports2) { - "use strict"; - Object.defineProperty(exports2, "__esModule", { value: true }); - function getReturnOfExpression(expression) { - return void 0; - } - exports2.getReturnOfExpression = getReturnOfExpression; - } - }); - - // node_modules/utility-types/dist/index.js - var require_dist = __commonJS({ - "node_modules/utility-types/dist/index.js"(exports2) { - "use strict"; - Object.defineProperty(exports2, "__esModule", { value: true }); - var aliases_and_guards_1 = require_aliases_and_guards(); - exports2.isFalsy = aliases_and_guards_1.isFalsy; - exports2.isNullish = aliases_and_guards_1.isNullish; - exports2.isPrimitive = aliases_and_guards_1.isPrimitive; - var functional_helpers_1 = require_functional_helpers(); - exports2.getReturnOfExpression = functional_helpers_1.getReturnOfExpression; - } - }); - - // node_modules/@iarna/toml/lib/parser.js - var require_parser = __commonJS({ - "node_modules/@iarna/toml/lib/parser.js"(exports2, module2) { - "use strict"; - var ParserEND = 1114112; - var ParserError = class _ParserError extends Error { - /* istanbul ignore next */ - constructor(msg, filename, linenumber) { - super("[ParserError] " + msg, filename, linenumber); - this.name = "ParserError"; - this.code = "ParserError"; - if (Error.captureStackTrace) Error.captureStackTrace(this, _ParserError); - } - }; - var State = class { - constructor(parser) { - this.parser = parser; - this.buf = ""; - this.returned = null; - this.result = null; - this.resultTable = null; - this.resultArr = null; - } - }; - var Parser = class { - constructor() { - this.pos = 0; - this.col = 0; - this.line = 0; - this.obj = {}; - this.ctx = this.obj; - this.stack = []; - this._buf = ""; - this.char = null; - this.ii = 0; - this.state = new State(this.parseStart); - } - parse(str) { - if (str.length === 0 || str.length == null) return; - this._buf = String(str); - this.ii = -1; - this.char = -1; - let getNext; - while (getNext === false || this.nextChar()) { - getNext = this.runOne(); - } - this._buf = null; - } - nextChar() { - if (this.char === 10) { - ++this.line; - this.col = -1; - } - ++this.ii; - this.char = this._buf.codePointAt(this.ii); - ++this.pos; - ++this.col; - return this.haveBuffer(); - } - haveBuffer() { - return this.ii < this._buf.length; - } - runOne() { - return this.state.parser.call(this, this.state.returned); - } - finish() { - this.char = ParserEND; - let last; - do { - last = this.state.parser; - this.runOne(); - } while (this.state.parser !== last); - this.ctx = null; - this.state = null; - this._buf = null; - return this.obj; - } - next(fn) { - if (typeof fn !== "function") throw new ParserError("Tried to set state to non-existent state: " + JSON.stringify(fn)); - this.state.parser = fn; - } - goto(fn) { - this.next(fn); - return this.runOne(); - } - call(fn, returnWith) { - if (returnWith) this.next(returnWith); - this.stack.push(this.state); - this.state = new State(fn); - } - callNow(fn, returnWith) { - this.call(fn, returnWith); - return this.runOne(); - } - return(value) { - if (this.stack.length === 0) throw this.error(new ParserError("Stack underflow")); - if (value === void 0) value = this.state.buf; - this.state = this.stack.pop(); - this.state.returned = value; - } - returnNow(value) { - this.return(value); - return this.runOne(); - } - consume() { - if (this.char === ParserEND) throw this.error(new ParserError("Unexpected end-of-buffer")); - this.state.buf += this._buf[this.ii]; - } - error(err) { - err.line = this.line; - err.col = this.col; - err.pos = this.pos; - return err; - } - /* istanbul ignore next */ - parseStart() { - throw new ParserError("Must declare a parseStart method"); - } - }; - Parser.END = ParserEND; - Parser.Error = ParserError; - module2.exports = Parser; - } - }); - - // node_modules/@iarna/toml/lib/create-datetime.js - var require_create_datetime = __commonJS({ - "node_modules/@iarna/toml/lib/create-datetime.js"(exports2, module2) { - "use strict"; - module2.exports = (value) => { - const date = new Date(value); - if (isNaN(date)) { - throw new TypeError("Invalid Datetime"); - } else { - return date; - } - }; - } - }); - - // node_modules/@iarna/toml/lib/format-num.js - var require_format_num = __commonJS({ - "node_modules/@iarna/toml/lib/format-num.js"(exports2, module2) { - "use strict"; - module2.exports = (d, num) => { - num = String(num); - while (num.length < d) num = "0" + num; - return num; - }; - } - }); - - // node_modules/@iarna/toml/lib/create-datetime-float.js - var require_create_datetime_float = __commonJS({ - "node_modules/@iarna/toml/lib/create-datetime-float.js"(exports2, module2) { - "use strict"; - var f = require_format_num(); - var FloatingDateTime = class extends Date { - constructor(value) { - super(value + "Z"); - this.isFloating = true; - } - toISOString() { - const date = `${this.getUTCFullYear()}-${f(2, this.getUTCMonth() + 1)}-${f(2, this.getUTCDate())}`; - const time = `${f(2, this.getUTCHours())}:${f(2, this.getUTCMinutes())}:${f(2, this.getUTCSeconds())}.${f(3, this.getUTCMilliseconds())}`; - return `${date}T${time}`; - } - }; - module2.exports = (value) => { - const date = new FloatingDateTime(value); - if (isNaN(date)) { - throw new TypeError("Invalid Datetime"); - } else { - return date; - } - }; - } - }); - - // node_modules/@iarna/toml/lib/create-date.js - var require_create_date = __commonJS({ - "node_modules/@iarna/toml/lib/create-date.js"(exports2, module2) { - "use strict"; - var f = require_format_num(); - var DateTime = globalThis.Date; - var Date2 = class extends DateTime { - constructor(value) { - super(value); - this.isDate = true; - } - toISOString() { - return `${this.getUTCFullYear()}-${f(2, this.getUTCMonth() + 1)}-${f(2, this.getUTCDate())}`; - } - }; - module2.exports = (value) => { - const date = new Date2(value); - if (isNaN(date)) { - throw new TypeError("Invalid Datetime"); - } else { - return date; - } - }; - } - }); - - // node_modules/@iarna/toml/lib/create-time.js - var require_create_time = __commonJS({ - "node_modules/@iarna/toml/lib/create-time.js"(exports2, module2) { - "use strict"; - var f = require_format_num(); - var Time = class extends Date { - constructor(value) { - super(`0000-01-01T${value}Z`); - this.isTime = true; - } - toISOString() { - return `${f(2, this.getUTCHours())}:${f(2, this.getUTCMinutes())}:${f(2, this.getUTCSeconds())}.${f(3, this.getUTCMilliseconds())}`; - } - }; - module2.exports = (value) => { - const date = new Time(value); - if (isNaN(date)) { - throw new TypeError("Invalid Datetime"); - } else { - return date; - } - }; - } - }); - - // node_modules/@iarna/toml/lib/toml-parser.js - var require_toml_parser = __commonJS({ - "node_modules/@iarna/toml/lib/toml-parser.js"(exports, module) { - "use strict"; - module.exports = makeParserClass(require_parser()); - module.exports.makeParserClass = makeParserClass; - var TomlError = class _TomlError extends Error { - constructor(msg) { - super(msg); - this.name = "TomlError"; - if (Error.captureStackTrace) Error.captureStackTrace(this, _TomlError); - this.fromTOML = true; - this.wrapped = null; - } - }; - TomlError.wrap = (err) => { - const terr = new TomlError(err.message); - terr.code = err.code; - terr.wrapped = err; - return terr; - }; - module.exports.TomlError = TomlError; - var createDateTime = require_create_datetime(); - var createDateTimeFloat = require_create_datetime_float(); - var createDate = require_create_date(); - var createTime = require_create_time(); - var CTRL_I = 9; - var CTRL_J = 10; - var CTRL_M = 13; - var CTRL_CHAR_BOUNDARY = 31; - var CHAR_SP = 32; - var CHAR_QUOT = 34; - var CHAR_NUM = 35; - var CHAR_APOS = 39; - var CHAR_PLUS = 43; - var CHAR_COMMA = 44; - var CHAR_HYPHEN = 45; - var CHAR_PERIOD = 46; - var CHAR_0 = 48; - var CHAR_1 = 49; - var CHAR_7 = 55; - var CHAR_9 = 57; - var CHAR_COLON = 58; - var CHAR_EQUALS = 61; - var CHAR_A = 65; - var CHAR_E = 69; - var CHAR_F = 70; - var CHAR_T = 84; - var CHAR_U = 85; - var CHAR_Z = 90; - var CHAR_LOWBAR = 95; - var CHAR_a = 97; - var CHAR_b = 98; - var CHAR_e = 101; - var CHAR_f = 102; - var CHAR_i = 105; - var CHAR_l = 108; - var CHAR_n = 110; - var CHAR_o = 111; - var CHAR_r = 114; - var CHAR_s = 115; - var CHAR_t = 116; - var CHAR_u = 117; - var CHAR_x = 120; - var CHAR_z = 122; - var CHAR_LCUB = 123; - var CHAR_RCUB = 125; - var CHAR_LSQB = 91; - var CHAR_BSOL = 92; - var CHAR_RSQB = 93; - var CHAR_DEL = 127; - var SURROGATE_FIRST = 55296; - var SURROGATE_LAST = 57343; - var escapes = { - [CHAR_b]: "\b", - [CHAR_t]: " ", - [CHAR_n]: "\n", - [CHAR_f]: "\f", - [CHAR_r]: "\r", - [CHAR_QUOT]: '"', - [CHAR_BSOL]: "\\" - }; - function isDigit(cp) { - return cp >= CHAR_0 && cp <= CHAR_9; - } - function isHexit(cp) { - return cp >= CHAR_A && cp <= CHAR_F || cp >= CHAR_a && cp <= CHAR_f || cp >= CHAR_0 && cp <= CHAR_9; - } - function isBit(cp) { - return cp === CHAR_1 || cp === CHAR_0; - } - function isOctit(cp) { - return cp >= CHAR_0 && cp <= CHAR_7; - } - function isAlphaNumQuoteHyphen(cp) { - return cp >= CHAR_A && cp <= CHAR_Z || cp >= CHAR_a && cp <= CHAR_z || cp >= CHAR_0 && cp <= CHAR_9 || cp === CHAR_APOS || cp === CHAR_QUOT || cp === CHAR_LOWBAR || cp === CHAR_HYPHEN; - } - function isAlphaNumHyphen(cp) { - return cp >= CHAR_A && cp <= CHAR_Z || cp >= CHAR_a && cp <= CHAR_z || cp >= CHAR_0 && cp <= CHAR_9 || cp === CHAR_LOWBAR || cp === CHAR_HYPHEN; - } - var _type = Symbol("type"); - var _declared = Symbol("declared"); - var hasOwnProperty = Object.prototype.hasOwnProperty; - var defineProperty = Object.defineProperty; - var descriptor = { configurable: true, enumerable: true, writable: true, value: void 0 }; - function hasKey(obj, key) { - if (hasOwnProperty.call(obj, key)) return true; - if (key === "__proto__") defineProperty(obj, "__proto__", descriptor); - return false; - } - var INLINE_TABLE = Symbol("inline-table"); - function InlineTable() { - return Object.defineProperties({}, { - [_type]: { value: INLINE_TABLE } - }); - } - function isInlineTable(obj) { - if (obj === null || typeof obj !== "object") return false; - return obj[_type] === INLINE_TABLE; - } - var TABLE = Symbol("table"); - function Table() { - return Object.defineProperties({}, { - [_type]: { value: TABLE }, - [_declared]: { value: false, writable: true } - }); - } - function isTable(obj) { - if (obj === null || typeof obj !== "object") return false; - return obj[_type] === TABLE; - } - var _contentType = Symbol("content-type"); - var INLINE_LIST = Symbol("inline-list"); - function InlineList(type) { - return Object.defineProperties([], { - [_type]: { value: INLINE_LIST }, - [_contentType]: { value: type } - }); - } - function isInlineList(obj) { - if (obj === null || typeof obj !== "object") return false; - return obj[_type] === INLINE_LIST; - } - var LIST = Symbol("list"); - function List() { - return Object.defineProperties([], { - [_type]: { value: LIST } - }); - } - function isList(obj) { - if (obj === null || typeof obj !== "object") return false; - return obj[_type] === LIST; - } - var _custom; - try { - const utilInspect = eval("require('util').inspect"); - _custom = utilInspect.custom; - } catch (_) { - } - var _inspect = _custom || "inspect"; - var BoxedBigInt = class { - constructor(value) { - try { - this.value = globalThis.BigInt.asIntN(64, value); - } catch (_) { - this.value = null; - } - Object.defineProperty(this, _type, { value: INTEGER }); - } - isNaN() { - return this.value === null; - } - /* istanbul ignore next */ - toString() { - return String(this.value); - } - /* istanbul ignore next */ - [_inspect]() { - return `[BigInt: ${this.toString()}]}`; - } - valueOf() { - return this.value; - } - }; - var INTEGER = Symbol("integer"); - function Integer(value) { - let num = Number(value); - if (Object.is(num, -0)) num = 0; - if (globalThis.BigInt && !Number.isSafeInteger(num)) { - return new BoxedBigInt(value); - } else { - return Object.defineProperties(new Number(num), { - isNaN: { value: function() { - return isNaN(this); - } }, - [_type]: { value: INTEGER }, - [_inspect]: { value: () => `[Integer: ${value}]` } - }); - } - } - function isInteger(obj) { - if (obj === null || typeof obj !== "object") return false; - return obj[_type] === INTEGER; - } - var FLOAT = Symbol("float"); - function Float(value) { - return Object.defineProperties(new Number(value), { - [_type]: { value: FLOAT }, - [_inspect]: { value: () => `[Float: ${value}]` } - }); - } - function isFloat(obj) { - if (obj === null || typeof obj !== "object") return false; - return obj[_type] === FLOAT; - } - function tomlType(value) { - const type = typeof value; - if (type === "object") { - if (value === null) return "null"; - if (value instanceof Date) return "datetime"; - if (_type in value) { - switch (value[_type]) { - case INLINE_TABLE: - return "inline-table"; - case INLINE_LIST: - return "inline-list"; - /* istanbul ignore next */ - case TABLE: - return "table"; - /* istanbul ignore next */ - case LIST: - return "list"; - case FLOAT: - return "float"; - case INTEGER: - return "integer"; - } - } - } - return type; - } - function makeParserClass(Parser) { - class TOMLParser extends Parser { - constructor() { - super(); - this.ctx = this.obj = Table(); - } - /* MATCH HELPER */ - atEndOfWord() { - return this.char === CHAR_NUM || this.char === CTRL_I || this.char === CHAR_SP || this.atEndOfLine(); - } - atEndOfLine() { - return this.char === Parser.END || this.char === CTRL_J || this.char === CTRL_M; - } - parseStart() { - if (this.char === Parser.END) { - return null; - } else if (this.char === CHAR_LSQB) { - return this.call(this.parseTableOrList); - } else if (this.char === CHAR_NUM) { - return this.call(this.parseComment); - } else if (this.char === CTRL_J || this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M) { - return null; - } else if (isAlphaNumQuoteHyphen(this.char)) { - return this.callNow(this.parseAssignStatement); - } else { - throw this.error(new TomlError(`Unknown character "${this.char}"`)); - } - } - // HELPER, this strips any whitespace and comments to the end of the line - // then RETURNS. Last state in a production. - parseWhitespaceToEOL() { - if (this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M) { - return null; - } else if (this.char === CHAR_NUM) { - return this.goto(this.parseComment); - } else if (this.char === Parser.END || this.char === CTRL_J) { - return this.return(); - } else { - throw this.error(new TomlError("Unexpected character, expected only whitespace or comments till end of line")); - } - } - /* ASSIGNMENT: key = value */ - parseAssignStatement() { - return this.callNow(this.parseAssign, this.recordAssignStatement); - } - recordAssignStatement(kv) { - let target = this.ctx; - let finalKey = kv.key.pop(); - for (let kw of kv.key) { - if (hasKey(target, kw) && (!isTable(target[kw]) || target[kw][_declared])) { - throw this.error(new TomlError("Can't redefine existing key")); - } - target = target[kw] = target[kw] || Table(); - } - if (hasKey(target, finalKey)) { - throw this.error(new TomlError("Can't redefine existing key")); - } - if (isInteger(kv.value) || isFloat(kv.value)) { - target[finalKey] = kv.value.valueOf(); - } else { - target[finalKey] = kv.value; - } - return this.goto(this.parseWhitespaceToEOL); - } - /* ASSSIGNMENT expression, key = value possibly inside an inline table */ - parseAssign() { - return this.callNow(this.parseKeyword, this.recordAssignKeyword); - } - recordAssignKeyword(key) { - if (this.state.resultTable) { - this.state.resultTable.push(key); - } else { - this.state.resultTable = [key]; - } - return this.goto(this.parseAssignKeywordPreDot); - } - parseAssignKeywordPreDot() { - if (this.char === CHAR_PERIOD) { - return this.next(this.parseAssignKeywordPostDot); - } else if (this.char !== CHAR_SP && this.char !== CTRL_I) { - return this.goto(this.parseAssignEqual); - } - } - parseAssignKeywordPostDot() { - if (this.char !== CHAR_SP && this.char !== CTRL_I) { - return this.callNow(this.parseKeyword, this.recordAssignKeyword); - } - } - parseAssignEqual() { - if (this.char === CHAR_EQUALS) { - return this.next(this.parseAssignPreValue); - } else { - throw this.error(new TomlError('Invalid character, expected "="')); - } - } - parseAssignPreValue() { - if (this.char === CHAR_SP || this.char === CTRL_I) { - return null; - } else { - return this.callNow(this.parseValue, this.recordAssignValue); - } - } - recordAssignValue(value) { - return this.returnNow({ key: this.state.resultTable, value }); - } - /* COMMENTS: #...eol */ - parseComment() { - do { - if (this.char === Parser.END || this.char === CTRL_J) { - return this.return(); - } - } while (this.nextChar()); - } - /* TABLES AND LISTS, [foo] and [[foo]] */ - parseTableOrList() { - if (this.char === CHAR_LSQB) { - this.next(this.parseList); - } else { - return this.goto(this.parseTable); - } - } - /* TABLE [foo.bar.baz] */ - parseTable() { - this.ctx = this.obj; - return this.goto(this.parseTableNext); - } - parseTableNext() { - if (this.char === CHAR_SP || this.char === CTRL_I) { - return null; - } else { - return this.callNow(this.parseKeyword, this.parseTableMore); - } - } - parseTableMore(keyword) { - if (this.char === CHAR_SP || this.char === CTRL_I) { - return null; - } else if (this.char === CHAR_RSQB) { - if (hasKey(this.ctx, keyword) && (!isTable(this.ctx[keyword]) || this.ctx[keyword][_declared])) { - throw this.error(new TomlError("Can't redefine existing key")); - } else { - this.ctx = this.ctx[keyword] = this.ctx[keyword] || Table(); - this.ctx[_declared] = true; - } - return this.next(this.parseWhitespaceToEOL); - } else if (this.char === CHAR_PERIOD) { - if (!hasKey(this.ctx, keyword)) { - this.ctx = this.ctx[keyword] = Table(); - } else if (isTable(this.ctx[keyword])) { - this.ctx = this.ctx[keyword]; - } else if (isList(this.ctx[keyword])) { - this.ctx = this.ctx[keyword][this.ctx[keyword].length - 1]; - } else { - throw this.error(new TomlError("Can't redefine existing key")); - } - return this.next(this.parseTableNext); - } else { - throw this.error(new TomlError("Unexpected character, expected whitespace, . or ]")); - } - } - /* LIST [[a.b.c]] */ - parseList() { - this.ctx = this.obj; - return this.goto(this.parseListNext); - } - parseListNext() { - if (this.char === CHAR_SP || this.char === CTRL_I) { - return null; - } else { - return this.callNow(this.parseKeyword, this.parseListMore); - } - } - parseListMore(keyword) { - if (this.char === CHAR_SP || this.char === CTRL_I) { - return null; - } else if (this.char === CHAR_RSQB) { - if (!hasKey(this.ctx, keyword)) { - this.ctx[keyword] = List(); - } - if (isInlineList(this.ctx[keyword])) { - throw this.error(new TomlError("Can't extend an inline array")); - } else if (isList(this.ctx[keyword])) { - const next = Table(); - this.ctx[keyword].push(next); - this.ctx = next; - } else { - throw this.error(new TomlError("Can't redefine an existing key")); - } - return this.next(this.parseListEnd); - } else if (this.char === CHAR_PERIOD) { - if (!hasKey(this.ctx, keyword)) { - this.ctx = this.ctx[keyword] = Table(); - } else if (isInlineList(this.ctx[keyword])) { - throw this.error(new TomlError("Can't extend an inline array")); - } else if (isInlineTable(this.ctx[keyword])) { - throw this.error(new TomlError("Can't extend an inline table")); - } else if (isList(this.ctx[keyword])) { - this.ctx = this.ctx[keyword][this.ctx[keyword].length - 1]; - } else if (isTable(this.ctx[keyword])) { - this.ctx = this.ctx[keyword]; - } else { - throw this.error(new TomlError("Can't redefine an existing key")); - } - return this.next(this.parseListNext); - } else { - throw this.error(new TomlError("Unexpected character, expected whitespace, . or ]")); - } - } - parseListEnd(keyword) { - if (this.char === CHAR_RSQB) { - return this.next(this.parseWhitespaceToEOL); - } else { - throw this.error(new TomlError("Unexpected character, expected whitespace, . or ]")); - } - } - /* VALUE string, number, boolean, inline list, inline object */ - parseValue() { - if (this.char === Parser.END) { - throw this.error(new TomlError("Key without value")); - } else if (this.char === CHAR_QUOT) { - return this.next(this.parseDoubleString); - } - if (this.char === CHAR_APOS) { - return this.next(this.parseSingleString); - } else if (this.char === CHAR_HYPHEN || this.char === CHAR_PLUS) { - return this.goto(this.parseNumberSign); - } else if (this.char === CHAR_i) { - return this.next(this.parseInf); - } else if (this.char === CHAR_n) { - return this.next(this.parseNan); - } else if (isDigit(this.char)) { - return this.goto(this.parseNumberOrDateTime); - } else if (this.char === CHAR_t || this.char === CHAR_f) { - return this.goto(this.parseBoolean); - } else if (this.char === CHAR_LSQB) { - return this.call(this.parseInlineList, this.recordValue); - } else if (this.char === CHAR_LCUB) { - return this.call(this.parseInlineTable, this.recordValue); - } else { - throw this.error(new TomlError("Unexpected character, expecting string, number, datetime, boolean, inline array or inline table")); - } - } - recordValue(value) { - return this.returnNow(value); - } - parseInf() { - if (this.char === CHAR_n) { - return this.next(this.parseInf2); - } else { - throw this.error(new TomlError('Unexpected character, expected "inf", "+inf" or "-inf"')); - } - } - parseInf2() { - if (this.char === CHAR_f) { - if (this.state.buf === "-") { - return this.return(-Infinity); - } else { - return this.return(Infinity); - } - } else { - throw this.error(new TomlError('Unexpected character, expected "inf", "+inf" or "-inf"')); - } - } - parseNan() { - if (this.char === CHAR_a) { - return this.next(this.parseNan2); - } else { - throw this.error(new TomlError('Unexpected character, expected "nan"')); - } - } - parseNan2() { - if (this.char === CHAR_n) { - return this.return(NaN); - } else { - throw this.error(new TomlError('Unexpected character, expected "nan"')); - } - } - /* KEYS, barewords or basic, literal, or dotted */ - parseKeyword() { - if (this.char === CHAR_QUOT) { - return this.next(this.parseBasicString); - } else if (this.char === CHAR_APOS) { - return this.next(this.parseLiteralString); - } else { - return this.goto(this.parseBareKey); - } - } - /* KEYS: barewords */ - parseBareKey() { - do { - if (this.char === Parser.END) { - throw this.error(new TomlError("Key ended without value")); - } else if (isAlphaNumHyphen(this.char)) { - this.consume(); - } else if (this.state.buf.length === 0) { - throw this.error(new TomlError("Empty bare keys are not allowed")); - } else { - return this.returnNow(); - } - } while (this.nextChar()); - } - /* STRINGS, single quoted (literal) */ - parseSingleString() { - if (this.char === CHAR_APOS) { - return this.next(this.parseLiteralMultiStringMaybe); - } else { - return this.goto(this.parseLiteralString); - } - } - parseLiteralString() { - do { - if (this.char === CHAR_APOS) { - return this.return(); - } else if (this.atEndOfLine()) { - throw this.error(new TomlError("Unterminated string")); - } else if (this.char === CHAR_DEL || this.char <= CTRL_CHAR_BOUNDARY && this.char !== CTRL_I) { - throw this.errorControlCharInString(); - } else { - this.consume(); - } - } while (this.nextChar()); - } - parseLiteralMultiStringMaybe() { - if (this.char === CHAR_APOS) { - return this.next(this.parseLiteralMultiString); - } else { - return this.returnNow(); - } - } - parseLiteralMultiString() { - if (this.char === CTRL_M) { - return null; - } else if (this.char === CTRL_J) { - return this.next(this.parseLiteralMultiStringContent); - } else { - return this.goto(this.parseLiteralMultiStringContent); - } - } - parseLiteralMultiStringContent() { - do { - if (this.char === CHAR_APOS) { - return this.next(this.parseLiteralMultiEnd); - } else if (this.char === Parser.END) { - throw this.error(new TomlError("Unterminated multi-line string")); - } else if (this.char === CHAR_DEL || this.char <= CTRL_CHAR_BOUNDARY && this.char !== CTRL_I && this.char !== CTRL_J && this.char !== CTRL_M) { - throw this.errorControlCharInString(); - } else { - this.consume(); - } - } while (this.nextChar()); - } - parseLiteralMultiEnd() { - if (this.char === CHAR_APOS) { - return this.next(this.parseLiteralMultiEnd2); - } else { - this.state.buf += "'"; - return this.goto(this.parseLiteralMultiStringContent); - } - } - parseLiteralMultiEnd2() { - if (this.char === CHAR_APOS) { - return this.return(); - } else { - this.state.buf += "''"; - return this.goto(this.parseLiteralMultiStringContent); - } - } - /* STRINGS double quoted */ - parseDoubleString() { - if (this.char === CHAR_QUOT) { - return this.next(this.parseMultiStringMaybe); - } else { - return this.goto(this.parseBasicString); - } - } - parseBasicString() { - do { - if (this.char === CHAR_BSOL) { - return this.call(this.parseEscape, this.recordEscapeReplacement); - } else if (this.char === CHAR_QUOT) { - return this.return(); - } else if (this.atEndOfLine()) { - throw this.error(new TomlError("Unterminated string")); - } else if (this.char === CHAR_DEL || this.char <= CTRL_CHAR_BOUNDARY && this.char !== CTRL_I) { - throw this.errorControlCharInString(); - } else { - this.consume(); - } - } while (this.nextChar()); - } - recordEscapeReplacement(replacement) { - this.state.buf += replacement; - return this.goto(this.parseBasicString); - } - parseMultiStringMaybe() { - if (this.char === CHAR_QUOT) { - return this.next(this.parseMultiString); - } else { - return this.returnNow(); - } - } - parseMultiString() { - if (this.char === CTRL_M) { - return null; - } else if (this.char === CTRL_J) { - return this.next(this.parseMultiStringContent); - } else { - return this.goto(this.parseMultiStringContent); - } - } - parseMultiStringContent() { - do { - if (this.char === CHAR_BSOL) { - return this.call(this.parseMultiEscape, this.recordMultiEscapeReplacement); - } else if (this.char === CHAR_QUOT) { - return this.next(this.parseMultiEnd); - } else if (this.char === Parser.END) { - throw this.error(new TomlError("Unterminated multi-line string")); - } else if (this.char === CHAR_DEL || this.char <= CTRL_CHAR_BOUNDARY && this.char !== CTRL_I && this.char !== CTRL_J && this.char !== CTRL_M) { - throw this.errorControlCharInString(); - } else { - this.consume(); - } - } while (this.nextChar()); - } - errorControlCharInString() { - let displayCode = "\\u00"; - if (this.char < 16) { - displayCode += "0"; - } - displayCode += this.char.toString(16); - return this.error(new TomlError(`Control characters (codes < 0x1f and 0x7f) are not allowed in strings, use ${displayCode} instead`)); - } - recordMultiEscapeReplacement(replacement) { - this.state.buf += replacement; - return this.goto(this.parseMultiStringContent); - } - parseMultiEnd() { - if (this.char === CHAR_QUOT) { - return this.next(this.parseMultiEnd2); - } else { - this.state.buf += '"'; - return this.goto(this.parseMultiStringContent); - } - } - parseMultiEnd2() { - if (this.char === CHAR_QUOT) { - return this.return(); - } else { - this.state.buf += '""'; - return this.goto(this.parseMultiStringContent); - } - } - parseMultiEscape() { - if (this.char === CTRL_M || this.char === CTRL_J) { - return this.next(this.parseMultiTrim); - } else if (this.char === CHAR_SP || this.char === CTRL_I) { - return this.next(this.parsePreMultiTrim); - } else { - return this.goto(this.parseEscape); - } - } - parsePreMultiTrim() { - if (this.char === CHAR_SP || this.char === CTRL_I) { - return null; - } else if (this.char === CTRL_M || this.char === CTRL_J) { - return this.next(this.parseMultiTrim); - } else { - throw this.error(new TomlError("Can't escape whitespace")); - } - } - parseMultiTrim() { - if (this.char === CTRL_J || this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M) { - return null; - } else { - return this.returnNow(); - } - } - parseEscape() { - if (this.char in escapes) { - return this.return(escapes[this.char]); - } else if (this.char === CHAR_u) { - return this.call(this.parseSmallUnicode, this.parseUnicodeReturn); - } else if (this.char === CHAR_U) { - return this.call(this.parseLargeUnicode, this.parseUnicodeReturn); - } else { - throw this.error(new TomlError("Unknown escape character: " + this.char)); - } - } - parseUnicodeReturn(char) { - try { - const codePoint = parseInt(char, 16); - if (codePoint >= SURROGATE_FIRST && codePoint <= SURROGATE_LAST) { - throw this.error(new TomlError("Invalid unicode, character in range 0xD800 - 0xDFFF is reserved")); - } - return this.returnNow(String.fromCodePoint(codePoint)); - } catch (err) { - throw this.error(TomlError.wrap(err)); - } - } - parseSmallUnicode() { - if (!isHexit(this.char)) { - throw this.error(new TomlError("Invalid character in unicode sequence, expected hex")); - } else { - this.consume(); - if (this.state.buf.length >= 4) return this.return(); - } - } - parseLargeUnicode() { - if (!isHexit(this.char)) { - throw this.error(new TomlError("Invalid character in unicode sequence, expected hex")); - } else { - this.consume(); - if (this.state.buf.length >= 8) return this.return(); - } - } - /* NUMBERS */ - parseNumberSign() { - this.consume(); - return this.next(this.parseMaybeSignedInfOrNan); - } - parseMaybeSignedInfOrNan() { - if (this.char === CHAR_i) { - return this.next(this.parseInf); - } else if (this.char === CHAR_n) { - return this.next(this.parseNan); - } else { - return this.callNow(this.parseNoUnder, this.parseNumberIntegerStart); - } - } - parseNumberIntegerStart() { - if (this.char === CHAR_0) { - this.consume(); - return this.next(this.parseNumberIntegerExponentOrDecimal); - } else { - return this.goto(this.parseNumberInteger); - } - } - parseNumberIntegerExponentOrDecimal() { - if (this.char === CHAR_PERIOD) { - this.consume(); - return this.call(this.parseNoUnder, this.parseNumberFloat); - } else if (this.char === CHAR_E || this.char === CHAR_e) { - this.consume(); - return this.next(this.parseNumberExponentSign); - } else { - return this.returnNow(Integer(this.state.buf)); - } - } - parseNumberInteger() { - if (isDigit(this.char)) { - this.consume(); - } else if (this.char === CHAR_LOWBAR) { - return this.call(this.parseNoUnder); - } else if (this.char === CHAR_E || this.char === CHAR_e) { - this.consume(); - return this.next(this.parseNumberExponentSign); - } else if (this.char === CHAR_PERIOD) { - this.consume(); - return this.call(this.parseNoUnder, this.parseNumberFloat); - } else { - const result = Integer(this.state.buf); - if (result.isNaN()) { - throw this.error(new TomlError("Invalid number")); - } else { - return this.returnNow(result); - } - } - } - parseNoUnder() { - if (this.char === CHAR_LOWBAR || this.char === CHAR_PERIOD || this.char === CHAR_E || this.char === CHAR_e) { - throw this.error(new TomlError("Unexpected character, expected digit")); - } else if (this.atEndOfWord()) { - throw this.error(new TomlError("Incomplete number")); - } - return this.returnNow(); - } - parseNoUnderHexOctBinLiteral() { - if (this.char === CHAR_LOWBAR || this.char === CHAR_PERIOD) { - throw this.error(new TomlError("Unexpected character, expected digit")); - } else if (this.atEndOfWord()) { - throw this.error(new TomlError("Incomplete number")); - } - return this.returnNow(); - } - parseNumberFloat() { - if (this.char === CHAR_LOWBAR) { - return this.call(this.parseNoUnder, this.parseNumberFloat); - } else if (isDigit(this.char)) { - this.consume(); - } else if (this.char === CHAR_E || this.char === CHAR_e) { - this.consume(); - return this.next(this.parseNumberExponentSign); - } else { - return this.returnNow(Float(this.state.buf)); - } - } - parseNumberExponentSign() { - if (isDigit(this.char)) { - return this.goto(this.parseNumberExponent); - } else if (this.char === CHAR_HYPHEN || this.char === CHAR_PLUS) { - this.consume(); - this.call(this.parseNoUnder, this.parseNumberExponent); - } else { - throw this.error(new TomlError("Unexpected character, expected -, + or digit")); - } - } - parseNumberExponent() { - if (isDigit(this.char)) { - this.consume(); - } else if (this.char === CHAR_LOWBAR) { - return this.call(this.parseNoUnder); - } else { - return this.returnNow(Float(this.state.buf)); - } - } - /* NUMBERS or DATETIMES */ - parseNumberOrDateTime() { - if (this.char === CHAR_0) { - this.consume(); - return this.next(this.parseNumberBaseOrDateTime); - } else { - return this.goto(this.parseNumberOrDateTimeOnly); - } - } - parseNumberOrDateTimeOnly() { - if (this.char === CHAR_LOWBAR) { - return this.call(this.parseNoUnder, this.parseNumberInteger); - } else if (isDigit(this.char)) { - this.consume(); - if (this.state.buf.length > 4) this.next(this.parseNumberInteger); - } else if (this.char === CHAR_E || this.char === CHAR_e) { - this.consume(); - return this.next(this.parseNumberExponentSign); - } else if (this.char === CHAR_PERIOD) { - this.consume(); - return this.call(this.parseNoUnder, this.parseNumberFloat); - } else if (this.char === CHAR_HYPHEN) { - return this.goto(this.parseDateTime); - } else if (this.char === CHAR_COLON) { - return this.goto(this.parseOnlyTimeHour); - } else { - return this.returnNow(Integer(this.state.buf)); - } - } - parseDateTimeOnly() { - if (this.state.buf.length < 4) { - if (isDigit(this.char)) { - return this.consume(); - } else if (this.char === CHAR_COLON) { - return this.goto(this.parseOnlyTimeHour); - } else { - throw this.error(new TomlError("Expected digit while parsing year part of a date")); - } - } else { - if (this.char === CHAR_HYPHEN) { - return this.goto(this.parseDateTime); - } else { - throw this.error(new TomlError("Expected hyphen (-) while parsing year part of date")); - } - } - } - parseNumberBaseOrDateTime() { - if (this.char === CHAR_b) { - this.consume(); - return this.call(this.parseNoUnderHexOctBinLiteral, this.parseIntegerBin); - } else if (this.char === CHAR_o) { - this.consume(); - return this.call(this.parseNoUnderHexOctBinLiteral, this.parseIntegerOct); - } else if (this.char === CHAR_x) { - this.consume(); - return this.call(this.parseNoUnderHexOctBinLiteral, this.parseIntegerHex); - } else if (this.char === CHAR_PERIOD) { - return this.goto(this.parseNumberInteger); - } else if (isDigit(this.char)) { - return this.goto(this.parseDateTimeOnly); - } else { - return this.returnNow(Integer(this.state.buf)); - } - } - parseIntegerHex() { - if (isHexit(this.char)) { - this.consume(); - } else if (this.char === CHAR_LOWBAR) { - return this.call(this.parseNoUnderHexOctBinLiteral); - } else { - const result = Integer(this.state.buf); - if (result.isNaN()) { - throw this.error(new TomlError("Invalid number")); - } else { - return this.returnNow(result); - } - } - } - parseIntegerOct() { - if (isOctit(this.char)) { - this.consume(); - } else if (this.char === CHAR_LOWBAR) { - return this.call(this.parseNoUnderHexOctBinLiteral); - } else { - const result = Integer(this.state.buf); - if (result.isNaN()) { - throw this.error(new TomlError("Invalid number")); - } else { - return this.returnNow(result); - } - } - } - parseIntegerBin() { - if (isBit(this.char)) { - this.consume(); - } else if (this.char === CHAR_LOWBAR) { - return this.call(this.parseNoUnderHexOctBinLiteral); - } else { - const result = Integer(this.state.buf); - if (result.isNaN()) { - throw this.error(new TomlError("Invalid number")); - } else { - return this.returnNow(result); - } - } - } - /* DATETIME */ - parseDateTime() { - if (this.state.buf.length < 4) { - throw this.error(new TomlError("Years less than 1000 must be zero padded to four characters")); - } - this.state.result = this.state.buf; - this.state.buf = ""; - return this.next(this.parseDateMonth); - } - parseDateMonth() { - if (this.char === CHAR_HYPHEN) { - if (this.state.buf.length < 2) { - throw this.error(new TomlError("Months less than 10 must be zero padded to two characters")); - } - this.state.result += "-" + this.state.buf; - this.state.buf = ""; - return this.next(this.parseDateDay); - } else if (isDigit(this.char)) { - this.consume(); - } else { - throw this.error(new TomlError("Incomplete datetime")); - } - } - parseDateDay() { - if (this.char === CHAR_T || this.char === CHAR_SP) { - if (this.state.buf.length < 2) { - throw this.error(new TomlError("Days less than 10 must be zero padded to two characters")); - } - this.state.result += "-" + this.state.buf; - this.state.buf = ""; - return this.next(this.parseStartTimeHour); - } else if (this.atEndOfWord()) { - return this.returnNow(createDate(this.state.result + "-" + this.state.buf)); - } else if (isDigit(this.char)) { - this.consume(); - } else { - throw this.error(new TomlError("Incomplete datetime")); - } - } - parseStartTimeHour() { - if (this.atEndOfWord()) { - return this.returnNow(createDate(this.state.result)); - } else { - return this.goto(this.parseTimeHour); - } - } - parseTimeHour() { - if (this.char === CHAR_COLON) { - if (this.state.buf.length < 2) { - throw this.error(new TomlError("Hours less than 10 must be zero padded to two characters")); - } - this.state.result += "T" + this.state.buf; - this.state.buf = ""; - return this.next(this.parseTimeMin); - } else if (isDigit(this.char)) { - this.consume(); - } else { - throw this.error(new TomlError("Incomplete datetime")); - } - } - parseTimeMin() { - if (this.state.buf.length < 2 && isDigit(this.char)) { - this.consume(); - } else if (this.state.buf.length === 2 && this.char === CHAR_COLON) { - this.state.result += ":" + this.state.buf; - this.state.buf = ""; - return this.next(this.parseTimeSec); - } else { - throw this.error(new TomlError("Incomplete datetime")); - } - } - parseTimeSec() { - if (isDigit(this.char)) { - this.consume(); - if (this.state.buf.length === 2) { - this.state.result += ":" + this.state.buf; - this.state.buf = ""; - return this.next(this.parseTimeZoneOrFraction); - } - } else { - throw this.error(new TomlError("Incomplete datetime")); - } - } - parseOnlyTimeHour() { - if (this.char === CHAR_COLON) { - if (this.state.buf.length < 2) { - throw this.error(new TomlError("Hours less than 10 must be zero padded to two characters")); - } - this.state.result = this.state.buf; - this.state.buf = ""; - return this.next(this.parseOnlyTimeMin); - } else { - throw this.error(new TomlError("Incomplete time")); - } - } - parseOnlyTimeMin() { - if (this.state.buf.length < 2 && isDigit(this.char)) { - this.consume(); - } else if (this.state.buf.length === 2 && this.char === CHAR_COLON) { - this.state.result += ":" + this.state.buf; - this.state.buf = ""; - return this.next(this.parseOnlyTimeSec); - } else { - throw this.error(new TomlError("Incomplete time")); - } - } - parseOnlyTimeSec() { - if (isDigit(this.char)) { - this.consume(); - if (this.state.buf.length === 2) { - return this.next(this.parseOnlyTimeFractionMaybe); - } - } else { - throw this.error(new TomlError("Incomplete time")); - } - } - parseOnlyTimeFractionMaybe() { - this.state.result += ":" + this.state.buf; - if (this.char === CHAR_PERIOD) { - this.state.buf = ""; - this.next(this.parseOnlyTimeFraction); - } else { - return this.return(createTime(this.state.result)); - } - } - parseOnlyTimeFraction() { - if (isDigit(this.char)) { - this.consume(); - } else if (this.atEndOfWord()) { - if (this.state.buf.length === 0) throw this.error(new TomlError("Expected digit in milliseconds")); - return this.returnNow(createTime(this.state.result + "." + this.state.buf)); - } else { - throw this.error(new TomlError("Unexpected character in datetime, expected period (.), minus (-), plus (+) or Z")); - } - } - parseTimeZoneOrFraction() { - if (this.char === CHAR_PERIOD) { - this.consume(); - this.next(this.parseDateTimeFraction); - } else if (this.char === CHAR_HYPHEN || this.char === CHAR_PLUS) { - this.consume(); - this.next(this.parseTimeZoneHour); - } else if (this.char === CHAR_Z) { - this.consume(); - return this.return(createDateTime(this.state.result + this.state.buf)); - } else if (this.atEndOfWord()) { - return this.returnNow(createDateTimeFloat(this.state.result + this.state.buf)); - } else { - throw this.error(new TomlError("Unexpected character in datetime, expected period (.), minus (-), plus (+) or Z")); - } - } - parseDateTimeFraction() { - if (isDigit(this.char)) { - this.consume(); - } else if (this.state.buf.length === 1) { - throw this.error(new TomlError("Expected digit in milliseconds")); - } else if (this.char === CHAR_HYPHEN || this.char === CHAR_PLUS) { - this.consume(); - this.next(this.parseTimeZoneHour); - } else if (this.char === CHAR_Z) { - this.consume(); - return this.return(createDateTime(this.state.result + this.state.buf)); - } else if (this.atEndOfWord()) { - return this.returnNow(createDateTimeFloat(this.state.result + this.state.buf)); - } else { - throw this.error(new TomlError("Unexpected character in datetime, expected period (.), minus (-), plus (+) or Z")); - } - } - parseTimeZoneHour() { - if (isDigit(this.char)) { - this.consume(); - if (/\d\d$/.test(this.state.buf)) return this.next(this.parseTimeZoneSep); - } else { - throw this.error(new TomlError("Unexpected character in datetime, expected digit")); - } - } - parseTimeZoneSep() { - if (this.char === CHAR_COLON) { - this.consume(); - this.next(this.parseTimeZoneMin); - } else { - throw this.error(new TomlError("Unexpected character in datetime, expected colon")); - } - } - parseTimeZoneMin() { - if (isDigit(this.char)) { - this.consume(); - if (/\d\d$/.test(this.state.buf)) return this.return(createDateTime(this.state.result + this.state.buf)); - } else { - throw this.error(new TomlError("Unexpected character in datetime, expected digit")); - } - } - /* BOOLEAN */ - parseBoolean() { - if (this.char === CHAR_t) { - this.consume(); - return this.next(this.parseTrue_r); - } else if (this.char === CHAR_f) { - this.consume(); - return this.next(this.parseFalse_a); - } - } - parseTrue_r() { - if (this.char === CHAR_r) { - this.consume(); - return this.next(this.parseTrue_u); - } else { - throw this.error(new TomlError("Invalid boolean, expected true or false")); - } - } - parseTrue_u() { - if (this.char === CHAR_u) { - this.consume(); - return this.next(this.parseTrue_e); - } else { - throw this.error(new TomlError("Invalid boolean, expected true or false")); - } - } - parseTrue_e() { - if (this.char === CHAR_e) { - return this.return(true); - } else { - throw this.error(new TomlError("Invalid boolean, expected true or false")); - } - } - parseFalse_a() { - if (this.char === CHAR_a) { - this.consume(); - return this.next(this.parseFalse_l); - } else { - throw this.error(new TomlError("Invalid boolean, expected true or false")); - } - } - parseFalse_l() { - if (this.char === CHAR_l) { - this.consume(); - return this.next(this.parseFalse_s); - } else { - throw this.error(new TomlError("Invalid boolean, expected true or false")); - } - } - parseFalse_s() { - if (this.char === CHAR_s) { - this.consume(); - return this.next(this.parseFalse_e); - } else { - throw this.error(new TomlError("Invalid boolean, expected true or false")); - } - } - parseFalse_e() { - if (this.char === CHAR_e) { - return this.return(false); - } else { - throw this.error(new TomlError("Invalid boolean, expected true or false")); - } - } - /* INLINE LISTS */ - parseInlineList() { - if (this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M || this.char === CTRL_J) { - return null; - } else if (this.char === Parser.END) { - throw this.error(new TomlError("Unterminated inline array")); - } else if (this.char === CHAR_NUM) { - return this.call(this.parseComment); - } else if (this.char === CHAR_RSQB) { - return this.return(this.state.resultArr || InlineList()); - } else { - return this.callNow(this.parseValue, this.recordInlineListValue); - } - } - recordInlineListValue(value) { - if (this.state.resultArr) { - const listType = this.state.resultArr[_contentType]; - const valueType = tomlType(value); - if (listType !== valueType) { - throw this.error(new TomlError(`Inline lists must be a single type, not a mix of ${listType} and ${valueType}`)); - } - } else { - this.state.resultArr = InlineList(tomlType(value)); - } - if (isFloat(value) || isInteger(value)) { - this.state.resultArr.push(value.valueOf()); - } else { - this.state.resultArr.push(value); - } - return this.goto(this.parseInlineListNext); - } - parseInlineListNext() { - if (this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M || this.char === CTRL_J) { - return null; - } else if (this.char === CHAR_NUM) { - return this.call(this.parseComment); - } else if (this.char === CHAR_COMMA) { - return this.next(this.parseInlineList); - } else if (this.char === CHAR_RSQB) { - return this.goto(this.parseInlineList); - } else { - throw this.error(new TomlError("Invalid character, expected whitespace, comma (,) or close bracket (])")); - } - } - /* INLINE TABLE */ - parseInlineTable() { - if (this.char === CHAR_SP || this.char === CTRL_I) { - return null; - } else if (this.char === Parser.END || this.char === CHAR_NUM || this.char === CTRL_J || this.char === CTRL_M) { - throw this.error(new TomlError("Unterminated inline array")); - } else if (this.char === CHAR_RCUB) { - return this.return(this.state.resultTable || InlineTable()); - } else { - if (!this.state.resultTable) this.state.resultTable = InlineTable(); - return this.callNow(this.parseAssign, this.recordInlineTableValue); - } - } - recordInlineTableValue(kv) { - let target = this.state.resultTable; - let finalKey = kv.key.pop(); - for (let kw of kv.key) { - if (hasKey(target, kw) && (!isTable(target[kw]) || target[kw][_declared])) { - throw this.error(new TomlError("Can't redefine existing key")); - } - target = target[kw] = target[kw] || Table(); - } - if (hasKey(target, finalKey)) { - throw this.error(new TomlError("Can't redefine existing key")); - } - if (isInteger(kv.value) || isFloat(kv.value)) { - target[finalKey] = kv.value.valueOf(); - } else { - target[finalKey] = kv.value; - } - return this.goto(this.parseInlineTableNext); - } - parseInlineTableNext() { - if (this.char === CHAR_SP || this.char === CTRL_I) { - return null; - } else if (this.char === Parser.END || this.char === CHAR_NUM || this.char === CTRL_J || this.char === CTRL_M) { - throw this.error(new TomlError("Unterminated inline array")); - } else if (this.char === CHAR_COMMA) { - return this.next(this.parseInlineTable); - } else if (this.char === CHAR_RCUB) { - return this.goto(this.parseInlineTable); - } else { - throw this.error(new TomlError("Invalid character, expected whitespace, comma (,) or close bracket (])")); - } - } - } - return TOMLParser; - } - } - }); - - // node_modules/@iarna/toml/parse-pretty-error.js - var require_parse_pretty_error = __commonJS({ - "node_modules/@iarna/toml/parse-pretty-error.js"(exports2, module2) { - "use strict"; - module2.exports = prettyError; - function prettyError(err, buf) { - if (err.pos == null || err.line == null) return err; - let msg = err.message; - msg += ` at row ${err.line + 1}, col ${err.col + 1}, pos ${err.pos}: -`; - if (buf && buf.split) { - const lines = buf.split(/\n/); - const lineNumWidth = String(Math.min(lines.length, err.line + 3)).length; - let linePadding = " "; - while (linePadding.length < lineNumWidth) linePadding += " "; - for (let ii = Math.max(0, err.line - 1); ii < Math.min(lines.length, err.line + 2); ++ii) { - let lineNum = String(ii + 1); - if (lineNum.length < lineNumWidth) lineNum = " " + lineNum; - if (err.line === ii) { - msg += lineNum + "> " + lines[ii] + "\n"; - msg += linePadding + " "; - for (let hh = 0; hh < err.col; ++hh) { - msg += " "; - } - msg += "^\n"; - } else { - msg += lineNum + ": " + lines[ii] + "\n"; - } - } - } - err.message = msg + "\n"; - return err; - } - } - }); - - // node_modules/@iarna/toml/parse-string.js - var require_parse_string = __commonJS({ - "node_modules/@iarna/toml/parse-string.js"(exports2, module2) { - "use strict"; - module2.exports = parseString; - var TOMLParser = require_toml_parser(); - var prettyError = require_parse_pretty_error(); - function parseString(str) { - if (globalThis.Buffer && globalThis.Buffer.isBuffer(str)) { - str = str.toString("utf8"); - } - const parser = new TOMLParser(); - try { - parser.parse(str); - return parser.finish(); - } catch (err) { - throw prettyError(err, str); - } - } - } - }); - - // node_modules/@iarna/toml/parse-async.js - var require_parse_async = __commonJS({ - "node_modules/@iarna/toml/parse-async.js"(exports2, module2) { - "use strict"; - module2.exports = parseAsync; - var TOMLParser = require_toml_parser(); - var prettyError = require_parse_pretty_error(); - function parseAsync(str, opts) { - if (!opts) opts = {}; - const index = 0; - const blocksize = opts.blocksize || 40960; - const parser = new TOMLParser(); - return new Promise((resolve, reject) => { - setImmediate(parseAsyncNext, index, blocksize, resolve, reject); - }); - function parseAsyncNext(index2, blocksize2, resolve, reject) { - if (index2 >= str.length) { - try { - return resolve(parser.finish()); - } catch (err) { - return reject(prettyError(err, str)); - } - } - try { - parser.parse(str.slice(index2, index2 + blocksize2)); - setImmediate(parseAsyncNext, index2 + blocksize2, blocksize2, resolve, reject); - } catch (err) { - reject(prettyError(err, str)); - } - } - } - } - }); - - // node_modules/component-emitter/index.js - var require_component_emitter = __commonJS({ - "node_modules/component-emitter/index.js"(exports2, module2) { - function Emitter(object) { - if (object) { - return mixin(object); - } - this._callbacks = /* @__PURE__ */ new Map(); - } - function mixin(object) { - Object.assign(object, Emitter.prototype); - object._callbacks = /* @__PURE__ */ new Map(); - return object; - } - Emitter.prototype.on = function(event, listener) { - const callbacks = this._callbacks.get(event) ?? []; - callbacks.push(listener); - this._callbacks.set(event, callbacks); - return this; - }; - Emitter.prototype.once = function(event, listener) { - const on = (...arguments_) => { - this.off(event, on); - listener.apply(this, arguments_); - }; - on.fn = listener; - this.on(event, on); - return this; - }; - Emitter.prototype.off = function(event, listener) { - if (event === void 0 && listener === void 0) { - this._callbacks.clear(); - return this; - } - if (listener === void 0) { - this._callbacks.delete(event); - return this; - } - const callbacks = this._callbacks.get(event); - if (callbacks) { - for (const [index, callback] of callbacks.entries()) { - if (callback === listener || callback.fn === listener) { - callbacks.splice(index, 1); - break; - } - } - if (callbacks.length === 0) { - this._callbacks.delete(event); - } else { - this._callbacks.set(event, callbacks); - } - } - return this; - }; - Emitter.prototype.emit = function(event, ...arguments_) { - const callbacks = this._callbacks.get(event); - if (callbacks) { - const callbacksCopy = [...callbacks]; - for (const callback of callbacksCopy) { - callback.apply(this, arguments_); - } - } - return this; - }; - Emitter.prototype.listeners = function(event) { - return this._callbacks.get(event) ?? []; - }; - Emitter.prototype.listenerCount = function(event) { - if (event) { - return this.listeners(event).length; - } - let totalCount = 0; - for (const callbacks of this._callbacks.values()) { - totalCount += callbacks.length; - } - return totalCount; - }; - Emitter.prototype.hasListeners = function(event) { - return this.listenerCount(event) > 0; - }; - Emitter.prototype.addEventListener = Emitter.prototype.on; - Emitter.prototype.removeListener = Emitter.prototype.off; - Emitter.prototype.removeEventListener = Emitter.prototype.off; - Emitter.prototype.removeAllListeners = Emitter.prototype.off; - if (typeof module2 !== "undefined") { - module2.exports = Emitter; - } - } - }); - - // node_modules/stream/index.js - var require_stream = __commonJS({ - "node_modules/stream/index.js"(exports2, module2) { - var Emitter = require_component_emitter(); - function Stream() { - Emitter.call(this); - } - Stream.prototype = new Emitter(); - module2.exports = Stream; - Stream.Stream = Stream; - Stream.prototype.pipe = function(dest, options) { - var source = this; - function ondata(chunk) { - if (dest.writable) { - if (false === dest.write(chunk) && source.pause) { - source.pause(); - } - } - } - source.on("data", ondata); - function ondrain() { - if (source.readable && source.resume) { - source.resume(); - } - } - dest.on("drain", ondrain); - if (!dest._isStdio && (!options || options.end !== false)) { - source.on("end", onend); - source.on("close", onclose); - } - var didOnEnd = false; - function onend() { - if (didOnEnd) return; - didOnEnd = true; - dest.end(); - } - function onclose() { - if (didOnEnd) return; - didOnEnd = true; - if (typeof dest.destroy === "function") dest.destroy(); - } - function onerror(er) { - cleanup(); - if (!this.hasListeners("error")) { - throw er; - } - } - source.on("error", onerror); - dest.on("error", onerror); - function cleanup() { - source.off("data", ondata); - dest.off("drain", ondrain); - source.off("end", onend); - source.off("close", onclose); - source.off("error", onerror); - dest.off("error", onerror); - source.off("end", cleanup); - source.off("close", cleanup); - dest.off("end", cleanup); - dest.off("close", cleanup); - } - source.on("end", cleanup); - source.on("close", cleanup); - dest.on("end", cleanup); - dest.on("close", cleanup); - dest.emit("pipe", source); - return dest; - }; - } - }); - - // node_modules/@iarna/toml/parse-stream.js - var require_parse_stream = __commonJS({ - "node_modules/@iarna/toml/parse-stream.js"(exports2, module2) { - "use strict"; - module2.exports = parseStream; - var stream = require_stream(); - var TOMLParser = require_toml_parser(); - function parseStream(stm) { - if (stm) { - return parseReadable(stm); - } else { - return parseTransform(stm); - } - } - function parseReadable(stm) { - const parser = new TOMLParser(); - stm.setEncoding("utf8"); - return new Promise((resolve, reject) => { - let readable; - let ended = false; - let errored = false; - function finish() { - ended = true; - if (readable) return; - try { - resolve(parser.finish()); - } catch (err) { - reject(err); - } - } - function error(err) { - errored = true; - reject(err); - } - stm.once("end", finish); - stm.once("error", error); - readNext(); - function readNext() { - readable = true; - let data; - while ((data = stm.read()) !== null) { - try { - parser.parse(data); - } catch (err) { - return error(err); - } - } - readable = false; - if (ended) return finish(); - if (errored) return; - stm.once("readable", readNext); - } - }); - } - function parseTransform() { - const parser = new TOMLParser(); - return new stream.Transform({ - objectMode: true, - transform(chunk, encoding, cb) { - try { - parser.parse(chunk.toString(encoding)); - } catch (err) { - this.emit("error", err); - } - cb(); - }, - flush(cb) { - try { - this.push(parser.finish()); - } catch (err) { - this.emit("error", err); - } - cb(); - } - }); - } - } - }); - - // node_modules/@iarna/toml/parse.js - var require_parse = __commonJS({ - "node_modules/@iarna/toml/parse.js"(exports2, module2) { - "use strict"; - module2.exports = require_parse_string(); - module2.exports.async = require_parse_async(); - module2.exports.stream = require_parse_stream(); - module2.exports.prettyError = require_parse_pretty_error(); - } - }); - - // node_modules/@iarna/toml/stringify.js - var require_stringify = __commonJS({ - "node_modules/@iarna/toml/stringify.js"(exports2, module2) { - "use strict"; - module2.exports = stringify; - module2.exports.value = stringifyInline; - function stringify(obj) { - if (obj === null) throw typeError("null"); - if (obj === void 0) throw typeError("undefined"); - if (typeof obj !== "object") throw typeError(typeof obj); - if (typeof obj.toJSON === "function") obj = obj.toJSON(); - if (obj == null) return null; - const type = tomlType2(obj); - if (type !== "table") throw typeError(type); - return stringifyObject("", "", obj); - } - function typeError(type) { - return new Error("Can only stringify objects, not " + type); - } - function arrayOneTypeError() { - return new Error("Array values can't have mixed types"); - } - function getInlineKeys(obj) { - return Object.keys(obj).filter((key) => isInline(obj[key])); - } - function getComplexKeys(obj) { - return Object.keys(obj).filter((key) => !isInline(obj[key])); - } - function toJSON(obj) { - let nobj = Array.isArray(obj) ? [] : Object.prototype.hasOwnProperty.call(obj, "__proto__") ? { ["__proto__"]: void 0 } : {}; - for (let prop of Object.keys(obj)) { - if (obj[prop] && typeof obj[prop].toJSON === "function" && !("toISOString" in obj[prop])) { - nobj[prop] = obj[prop].toJSON(); - } else { - nobj[prop] = obj[prop]; - } - } - return nobj; - } - function stringifyObject(prefix, indent, obj) { - obj = toJSON(obj); - var inlineKeys; - var complexKeys; - inlineKeys = getInlineKeys(obj); - complexKeys = getComplexKeys(obj); - var result = []; - var inlineIndent = indent || ""; - inlineKeys.forEach((key) => { - var type = tomlType2(obj[key]); - if (type !== "undefined" && type !== "null") { - result.push(inlineIndent + stringifyKey(key) + " = " + stringifyAnyInline(obj[key], true)); - } - }); - if (result.length > 0) result.push(""); - var complexIndent = prefix && inlineKeys.length > 0 ? indent + " " : ""; - complexKeys.forEach((key) => { - result.push(stringifyComplex(prefix, complexIndent, key, obj[key])); - }); - return result.join("\n"); - } - function isInline(value) { - switch (tomlType2(value)) { - case "undefined": - case "null": - case "integer": - case "nan": - case "float": - case "boolean": - case "string": - case "datetime": - return true; - case "array": - return value.length === 0 || tomlType2(value[0]) !== "table"; - case "table": - return Object.keys(value).length === 0; - /* istanbul ignore next */ - default: - return false; - } - } - function tomlType2(value) { - if (value === void 0) { - return "undefined"; - } else if (value === null) { - return "null"; - } else if (typeof value === "bigint" || Number.isInteger(value) && !Object.is(value, -0)) { - return "integer"; - } else if (typeof value === "number") { - return "float"; - } else if (typeof value === "boolean") { - return "boolean"; - } else if (typeof value === "string") { - return "string"; - } else if ("toISOString" in value) { - return isNaN(value) ? "undefined" : "datetime"; - } else if (Array.isArray(value)) { - return "array"; - } else { - return "table"; - } - } - function stringifyKey(key) { - var keyStr = String(key); - if (/^[-A-Za-z0-9_]+$/.test(keyStr)) { - return keyStr; - } else { - return stringifyBasicString(keyStr); - } - } - function stringifyBasicString(str) { - return '"' + escapeString(str).replace(/"/g, '\\"') + '"'; - } - function stringifyLiteralString(str) { - return "'" + str + "'"; - } - function numpad(num, str) { - while (str.length < num) str = "0" + str; - return str; - } - function escapeString(str) { - return str.replace(/\\/g, "\\\\").replace(/[\b]/g, "\\b").replace(/\t/g, "\\t").replace(/\n/g, "\\n").replace(/\f/g, "\\f").replace(/\r/g, "\\r").replace(/([\u0000-\u001f\u007f])/, (c) => "\\u" + numpad(4, c.codePointAt(0).toString(16))); - } - function stringifyMultilineString(str) { - let escaped = str.split(/\n/).map((str2) => { - return escapeString(str2).replace(/"(?="")/g, '\\"'); - }).join("\n"); - if (escaped.slice(-1) === '"') escaped += "\\\n"; - return '"""\n' + escaped + '"""'; - } - function stringifyAnyInline(value, multilineOk) { - let type = tomlType2(value); - if (type === "string") { - if (multilineOk && /\n/.test(value)) { - type = "string-multiline"; - } else if (!/[\b\t\n\f\r']/.test(value) && /"/.test(value)) { - type = "string-literal"; - } - } - return stringifyInline(value, type); - } - function stringifyInline(value, type) { - if (!type) type = tomlType2(value); - switch (type) { - case "string-multiline": - return stringifyMultilineString(value); - case "string": - return stringifyBasicString(value); - case "string-literal": - return stringifyLiteralString(value); - case "integer": - return stringifyInteger(value); - case "float": - return stringifyFloat(value); - case "boolean": - return stringifyBoolean(value); - case "datetime": - return stringifyDatetime(value); - case "array": - return stringifyInlineArray(value.filter((_) => tomlType2(_) !== "null" && tomlType2(_) !== "undefined" && tomlType2(_) !== "nan")); - case "table": - return stringifyInlineTable(value); - /* istanbul ignore next */ - default: - throw typeError(type); - } - } - function stringifyInteger(value) { - return String(value).replace(/\B(?=(\d{3})+(?!\d))/g, "_"); - } - function stringifyFloat(value) { - if (value === Infinity) { - return "inf"; - } else if (value === -Infinity) { - return "-inf"; - } else if (Object.is(value, NaN)) { - return "nan"; - } else if (Object.is(value, -0)) { - return "-0.0"; - } - var chunks = String(value).split("."); - var int = chunks[0]; - var dec = chunks[1] || 0; - return stringifyInteger(int) + "." + dec; - } - function stringifyBoolean(value) { - return String(value); - } - function stringifyDatetime(value) { - return value.toISOString(); - } - function isNumber(type) { - return type === "float" || type === "integer"; - } - function arrayType(values) { - var contentType = tomlType2(values[0]); - if (values.every((_) => tomlType2(_) === contentType)) return contentType; - if (values.every((_) => isNumber(tomlType2(_)))) return "float"; - return "mixed"; - } - function validateArray(values) { - const type = arrayType(values); - if (type === "mixed") { - throw arrayOneTypeError(); - } - return type; - } - function stringifyInlineArray(values) { - values = toJSON(values); - const type = validateArray(values); - var result = "["; - var stringified = values.map((_) => stringifyInline(_, type)); - if (stringified.join(", ").length > 60 || /\n/.test(stringified)) { - result += "\n " + stringified.join(",\n ") + "\n"; - } else { - result += " " + stringified.join(", ") + (stringified.length > 0 ? " " : ""); - } - return result + "]"; - } - function stringifyInlineTable(value) { - value = toJSON(value); - var result = []; - Object.keys(value).forEach((key) => { - result.push(stringifyKey(key) + " = " + stringifyAnyInline(value[key], false)); - }); - return "{ " + result.join(", ") + (result.length > 0 ? " " : "") + "}"; - } - function stringifyComplex(prefix, indent, key, value) { - var valueType = tomlType2(value); - if (valueType === "array") { - return stringifyArrayOfTables(prefix, indent, key, value); - } else if (valueType === "table") { - return stringifyComplexTable(prefix, indent, key, value); - } else { - throw typeError(valueType); - } - } - function stringifyArrayOfTables(prefix, indent, key, values) { - values = toJSON(values); - validateArray(values); - var firstValueType = tomlType2(values[0]); - if (firstValueType !== "table") throw typeError(firstValueType); - var fullKey = prefix + stringifyKey(key); - var result = ""; - values.forEach((table) => { - if (result.length > 0) result += "\n"; - result += indent + "[[" + fullKey + "]]\n"; - result += stringifyObject(fullKey + ".", indent, table); - }); - return result; - } - function stringifyComplexTable(prefix, indent, key, value) { - var fullKey = prefix + stringifyKey(key); - var result = ""; - if (getInlineKeys(value).length > 0) { - result += indent + "[" + fullKey + "]\n"; - } - return result + stringifyObject(fullKey + ".", indent, value); - } - } - }); - - // node_modules/@iarna/toml/toml.js - var require_toml = __commonJS({ - "node_modules/@iarna/toml/toml.js"(exports2) { - "use strict"; - exports2.parse = require_parse(); - exports2.stringify = require_stringify(); - } - }); - - // src/elemtoml.ts - var import_utility_types = __toESM(require_dist()); - function register_element(name, elem) { - console.debug("Element registered: ", elem); - let tmp_value = elem; - if (tmp_value.namedBehavior) { - const found_behaviour = window.behaviors[tmp_value.namedBehavior]; - if (typeof found_behaviour == "function") { - tmp_value.tick = found_behaviour; - } else { - tmp_value.behavior = found_behaviour; - } - } - window.elements[name] = tmp_value; - } - function register_elements(elems) { - Object.entries(elems).forEach(([key, value]) => { - register_element(key, value); - }); - } - - // src/cfg_loader.ts - var import_toml = __toESM(require_toml()); - - // src/utils.ts - var ScriptRunError = class extends Error { - constructor(message, asserter = void 0) { - super(message); - this.name = "MyError"; - Error.captureStackTrace?.(this, asserter || this.constructor); - } - }; - async function run_script(path) { - try { - let resp = await fetch(path); - const text = await resp.text(); - if (resp.ok) { - const result = Function(text)(); - if (result !== void 0 && result !== 0) { - throw new ScriptRunError(`Script exited with code ${result}`); - } - } else { - throw new ScriptRunError(`Script ${path} not found`); - } - } catch (e) { - throw e; - } - } - - // src/cfg_loader.ts - var Package = class { - /** - * Constructs a new Package instance with the given configuration (as loaded - * from a TOML configuration). - * - * @param config The parsed package configuration. - */ - constructor(config) { - /** - * The list of elements that have been loaded for this package. - */ - this.loaded_elems = []; - this.cfg = config; - console.log(this); - } - /** - * Loads external elements defined in the package configuration. - * Fetches, parses, and registers each element. - * - * @returns A promise that resolves to an `ElementDict` - * @private - */ - async load_elems() { - for (const i of this.cfg.mod.external_elements) { - console.log("loading element:", i); - try { - let resp = await fetch(i.path); - const parsed = (0, import_toml.parse)(await resp.text()); - console.log(parsed); - register_element(i.name, parsed); - } catch (err) { - console.error(err); - } - } - let tmp = {}; - this.loaded_elems.forEach((elem) => { - tmp[elem.name] = elem; - }); - return tmp; - } - /** - * Retrieves the list of elements that have been loaded for this package. - * @returns An array of loaded elements. - */ - get_loaded_elems() { - return this.loaded_elems; - } - /** - * Loads the mod, runs scripts, and registers elements. - */ - load_mod(prompt_quene2) { - const incompatibilities = window.enabledMods.filter((x) => this.cfg.mod.incompatible_mods.includes(x)); - if (incompatibilities.length != 0) { - prompt_quene2.push(() => { - window.promptText( - `A: ${this.cfg.mod.name} - B: ${incompatibilities.join(", ")}`, - () => { - }, - "Mod incompatibility" - ); - }); - return; - } - console.debug(this.cfg.scripts); - if (this.cfg.scripts.preload !== void 0) { - for (const i of this.cfg.scripts.preload) { - run_script(i); - } - } - this.load_elems().then((elems) => { - console.debug("elems:", elems); - register_elements(elems); - }); - if (this.cfg.scripts.postload !== void 0) { - for (const i of this.cfg.scripts.postload) { - run_script(i); - } - } - } - }; - function load(object) { - return object; - } - - // src/mod.ts - var import_toml2 = __toESM(require_toml()); - - // src/mod_finder.ts - function find_mod(name, onfind) { - console.log(name, `${name}/mod.toml`); - fetch(`${name}/mod.toml`).then(async (x) => { - console.log(x.url); - if (x.ok) { - onfind(await x.text()); - } else { - } - }).catch((err) => { - console.error(err); - }); - } - - // src/mod.ts - function shuffle_to_start(input, i) { - let tmp = input.filter((x) => x !== input[i]); - tmp.unshift(input[i]); - return tmp; - } - var prompt_quene = []; - var mods = JSON.parse(localStorage.getItem("enabledMods") || ""); - if (mods[0] !== "mods/fancy_loader.js") { - prompt_quene.push(() => { - window.promptConfirm( - `Refresh again to reload as the first mod (otherwise, there can be odd glitches with dependencies etc.)`, - (x) => { - if (x) { - window.location.reload(); - } - }, - "fancy_loader.js says..." - ); - }); - const shuffled_mods = shuffle_to_start(mods, mods.findIndex((x) => x == "mods/fancy_loader.js")); - localStorage.setItem("enabledMods", JSON.stringify(shuffled_mods)); - } - for (const i of window.enabledMods) { - if (i.endsWith(".toml")) { - console.trace("Loading mod:", i, i.slice(0, -5)); - find_mod(i.slice(0, -5), (text) => { - const parsed = import_toml2.default.parse(text); - console.debug("Parsed mod TOML:", import_toml2.default.parse(text)); - let pkg = new Package(load(parsed)); - pkg.load_mod(prompt_quene); - console.debug("Loaded mod:", pkg); - }); - } - } - window.addEventListener("load", () => { - for (const i of prompt_quene) { - i(); - } - }); -})(); -/*! Bundled license information: - -utility-types/dist/index.js: - (** - * @author Piotr Witek (http://piotrwitek.github.io) - * @copyright Copyright (c) 2016 Piotr Witek - * @license MIT - *) -*/ diff --git a/mods/loader_test/mod.toml b/mods/loader_test/mod.toml deleted file mode 100644 index 586d0a28..00000000 --- a/mods/loader_test/mod.toml +++ /dev/null @@ -1,23 +0,0 @@ -[mod] -name = "test-finder" -version = "0.1.0" -external_elements = [ - {path = "mods/loader_test/test_element.toml", name = "test_element"}, -] - -# Completely arbitrary -incompatible_mods = [ - "mods/nousersthings.js", - "mods/lightmap.js", - "mods/betterSettings.js" -] - -[scripts] -preload = [ - "mods/loader_test/pre_load.js", - "mods/loader_test/random_file.js" -] -postload = [ - "mods/loader_test/post_load.js", - "mods/loader_test/random_file.js" -] \ No newline at end of file diff --git a/mods/loader_test/post_load.js b/mods/loader_test/post_load.js deleted file mode 100644 index 78f2b805..00000000 --- a/mods/loader_test/post_load.js +++ /dev/null @@ -1 +0,0 @@ -console.log("POST LOAD RUNNING") \ No newline at end of file diff --git a/mods/loader_test/pre_load.js b/mods/loader_test/pre_load.js deleted file mode 100644 index 53652f98..00000000 --- a/mods/loader_test/pre_load.js +++ /dev/null @@ -1 +0,0 @@ -console.log("PRELOAD RUNNING") \ No newline at end of file diff --git a/mods/loader_test/random_file.js b/mods/loader_test/random_file.js deleted file mode 100644 index 9589b727..00000000 --- a/mods/loader_test/random_file.js +++ /dev/null @@ -1 +0,0 @@ -console.log("THIS OTHER THING RUNNING") \ No newline at end of file diff --git a/mods/loader_test/test_element.toml b/mods/loader_test/test_element.toml deleted file mode 100644 index 71e49017..00000000 --- a/mods/loader_test/test_element.toml +++ /dev/null @@ -1,14 +0,0 @@ -name = "test_element" -color = ["#FF00FF", "#00FF00"] -state = "solid" -namedBehavior = "POWDER_OLD" -category = "solids" -tempHigh = 100 -stateHigh = "gold" -density = 100 -conduct = 1.0 - -[reactions] -water = {elem = "ash", elem2 = "pop"} -cement = {elem1 = "sand"} -blood = {elem1 = "n_explosion"} \ No newline at end of file From 300dc5e721fa1c4fb9dd689d61ccc31452584d00 Mon Sep 17 00:00:00 2001 From: Mnem42 Date: Sun, 7 Dec 2025 14:31:01 +0000 Subject: [PATCH 09/38] Temporarily remove that unrelated mod --- mods/fancy_loader.js | 2442 ---------------------------- mods/loader_test/mod.toml | 23 - mods/loader_test/post_load.js | 1 - mods/loader_test/pre_load.js | 1 - mods/loader_test/random_file.js | 1 - mods/loader_test/test_element.toml | 14 - 6 files changed, 2482 deletions(-) delete mode 100644 mods/fancy_loader.js delete mode 100644 mods/loader_test/mod.toml delete mode 100644 mods/loader_test/post_load.js delete mode 100644 mods/loader_test/pre_load.js delete mode 100644 mods/loader_test/random_file.js delete mode 100644 mods/loader_test/test_element.toml diff --git a/mods/fancy_loader.js b/mods/fancy_loader.js deleted file mode 100644 index b701ba2e..00000000 --- a/mods/fancy_loader.js +++ /dev/null @@ -1,2442 +0,0 @@ -"use strict"; -(() => { - var __create = Object.create; - var __defProp = Object.defineProperty; - var __getOwnPropDesc = Object.getOwnPropertyDescriptor; - var __getOwnPropNames = Object.getOwnPropertyNames; - var __getProtoOf = Object.getPrototypeOf; - var __hasOwnProp = Object.prototype.hasOwnProperty; - var __commonJS = (cb, mod) => function __require() { - return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; - }; - var __copyProps = (to, from, except, desc) => { - if (from && typeof from === "object" || typeof from === "function") { - for (let key of __getOwnPropNames(from)) - if (!__hasOwnProp.call(to, key) && key !== except) - __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); - } - return to; - }; - var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( - // If the importer is in node compatibility mode or this is not an ESM - // file that has been converted to a CommonJS file using a Babel- - // compatible transform (i.e. "__esModule" has not been set), then set - // "default" to the CommonJS "module.exports" for node compatibility. - isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, - mod - )); - - // node_modules/utility-types/dist/aliases-and-guards.js - var require_aliases_and_guards = __commonJS({ - "node_modules/utility-types/dist/aliases-and-guards.js"(exports2) { - "use strict"; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.isPrimitive = function(val) { - if (val === null || val === void 0) { - return true; - } - switch (typeof val) { - case "string": - case "number": - case "bigint": - case "boolean": - case "symbol": { - return true; - } - default: - return false; - } - }; - exports2.isFalsy = function(val) { - return !val; - }; - exports2.isNullish = function(val) { - return val == null; - }; - } - }); - - // node_modules/utility-types/dist/functional-helpers.js - var require_functional_helpers = __commonJS({ - "node_modules/utility-types/dist/functional-helpers.js"(exports2) { - "use strict"; - Object.defineProperty(exports2, "__esModule", { value: true }); - function getReturnOfExpression(expression) { - return void 0; - } - exports2.getReturnOfExpression = getReturnOfExpression; - } - }); - - // node_modules/utility-types/dist/index.js - var require_dist = __commonJS({ - "node_modules/utility-types/dist/index.js"(exports2) { - "use strict"; - Object.defineProperty(exports2, "__esModule", { value: true }); - var aliases_and_guards_1 = require_aliases_and_guards(); - exports2.isFalsy = aliases_and_guards_1.isFalsy; - exports2.isNullish = aliases_and_guards_1.isNullish; - exports2.isPrimitive = aliases_and_guards_1.isPrimitive; - var functional_helpers_1 = require_functional_helpers(); - exports2.getReturnOfExpression = functional_helpers_1.getReturnOfExpression; - } - }); - - // node_modules/@iarna/toml/lib/parser.js - var require_parser = __commonJS({ - "node_modules/@iarna/toml/lib/parser.js"(exports2, module2) { - "use strict"; - var ParserEND = 1114112; - var ParserError = class _ParserError extends Error { - /* istanbul ignore next */ - constructor(msg, filename, linenumber) { - super("[ParserError] " + msg, filename, linenumber); - this.name = "ParserError"; - this.code = "ParserError"; - if (Error.captureStackTrace) Error.captureStackTrace(this, _ParserError); - } - }; - var State = class { - constructor(parser) { - this.parser = parser; - this.buf = ""; - this.returned = null; - this.result = null; - this.resultTable = null; - this.resultArr = null; - } - }; - var Parser = class { - constructor() { - this.pos = 0; - this.col = 0; - this.line = 0; - this.obj = {}; - this.ctx = this.obj; - this.stack = []; - this._buf = ""; - this.char = null; - this.ii = 0; - this.state = new State(this.parseStart); - } - parse(str) { - if (str.length === 0 || str.length == null) return; - this._buf = String(str); - this.ii = -1; - this.char = -1; - let getNext; - while (getNext === false || this.nextChar()) { - getNext = this.runOne(); - } - this._buf = null; - } - nextChar() { - if (this.char === 10) { - ++this.line; - this.col = -1; - } - ++this.ii; - this.char = this._buf.codePointAt(this.ii); - ++this.pos; - ++this.col; - return this.haveBuffer(); - } - haveBuffer() { - return this.ii < this._buf.length; - } - runOne() { - return this.state.parser.call(this, this.state.returned); - } - finish() { - this.char = ParserEND; - let last; - do { - last = this.state.parser; - this.runOne(); - } while (this.state.parser !== last); - this.ctx = null; - this.state = null; - this._buf = null; - return this.obj; - } - next(fn) { - if (typeof fn !== "function") throw new ParserError("Tried to set state to non-existent state: " + JSON.stringify(fn)); - this.state.parser = fn; - } - goto(fn) { - this.next(fn); - return this.runOne(); - } - call(fn, returnWith) { - if (returnWith) this.next(returnWith); - this.stack.push(this.state); - this.state = new State(fn); - } - callNow(fn, returnWith) { - this.call(fn, returnWith); - return this.runOne(); - } - return(value) { - if (this.stack.length === 0) throw this.error(new ParserError("Stack underflow")); - if (value === void 0) value = this.state.buf; - this.state = this.stack.pop(); - this.state.returned = value; - } - returnNow(value) { - this.return(value); - return this.runOne(); - } - consume() { - if (this.char === ParserEND) throw this.error(new ParserError("Unexpected end-of-buffer")); - this.state.buf += this._buf[this.ii]; - } - error(err) { - err.line = this.line; - err.col = this.col; - err.pos = this.pos; - return err; - } - /* istanbul ignore next */ - parseStart() { - throw new ParserError("Must declare a parseStart method"); - } - }; - Parser.END = ParserEND; - Parser.Error = ParserError; - module2.exports = Parser; - } - }); - - // node_modules/@iarna/toml/lib/create-datetime.js - var require_create_datetime = __commonJS({ - "node_modules/@iarna/toml/lib/create-datetime.js"(exports2, module2) { - "use strict"; - module2.exports = (value) => { - const date = new Date(value); - if (isNaN(date)) { - throw new TypeError("Invalid Datetime"); - } else { - return date; - } - }; - } - }); - - // node_modules/@iarna/toml/lib/format-num.js - var require_format_num = __commonJS({ - "node_modules/@iarna/toml/lib/format-num.js"(exports2, module2) { - "use strict"; - module2.exports = (d, num) => { - num = String(num); - while (num.length < d) num = "0" + num; - return num; - }; - } - }); - - // node_modules/@iarna/toml/lib/create-datetime-float.js - var require_create_datetime_float = __commonJS({ - "node_modules/@iarna/toml/lib/create-datetime-float.js"(exports2, module2) { - "use strict"; - var f = require_format_num(); - var FloatingDateTime = class extends Date { - constructor(value) { - super(value + "Z"); - this.isFloating = true; - } - toISOString() { - const date = `${this.getUTCFullYear()}-${f(2, this.getUTCMonth() + 1)}-${f(2, this.getUTCDate())}`; - const time = `${f(2, this.getUTCHours())}:${f(2, this.getUTCMinutes())}:${f(2, this.getUTCSeconds())}.${f(3, this.getUTCMilliseconds())}`; - return `${date}T${time}`; - } - }; - module2.exports = (value) => { - const date = new FloatingDateTime(value); - if (isNaN(date)) { - throw new TypeError("Invalid Datetime"); - } else { - return date; - } - }; - } - }); - - // node_modules/@iarna/toml/lib/create-date.js - var require_create_date = __commonJS({ - "node_modules/@iarna/toml/lib/create-date.js"(exports2, module2) { - "use strict"; - var f = require_format_num(); - var DateTime = globalThis.Date; - var Date2 = class extends DateTime { - constructor(value) { - super(value); - this.isDate = true; - } - toISOString() { - return `${this.getUTCFullYear()}-${f(2, this.getUTCMonth() + 1)}-${f(2, this.getUTCDate())}`; - } - }; - module2.exports = (value) => { - const date = new Date2(value); - if (isNaN(date)) { - throw new TypeError("Invalid Datetime"); - } else { - return date; - } - }; - } - }); - - // node_modules/@iarna/toml/lib/create-time.js - var require_create_time = __commonJS({ - "node_modules/@iarna/toml/lib/create-time.js"(exports2, module2) { - "use strict"; - var f = require_format_num(); - var Time = class extends Date { - constructor(value) { - super(`0000-01-01T${value}Z`); - this.isTime = true; - } - toISOString() { - return `${f(2, this.getUTCHours())}:${f(2, this.getUTCMinutes())}:${f(2, this.getUTCSeconds())}.${f(3, this.getUTCMilliseconds())}`; - } - }; - module2.exports = (value) => { - const date = new Time(value); - if (isNaN(date)) { - throw new TypeError("Invalid Datetime"); - } else { - return date; - } - }; - } - }); - - // node_modules/@iarna/toml/lib/toml-parser.js - var require_toml_parser = __commonJS({ - "node_modules/@iarna/toml/lib/toml-parser.js"(exports, module) { - "use strict"; - module.exports = makeParserClass(require_parser()); - module.exports.makeParserClass = makeParserClass; - var TomlError = class _TomlError extends Error { - constructor(msg) { - super(msg); - this.name = "TomlError"; - if (Error.captureStackTrace) Error.captureStackTrace(this, _TomlError); - this.fromTOML = true; - this.wrapped = null; - } - }; - TomlError.wrap = (err) => { - const terr = new TomlError(err.message); - terr.code = err.code; - terr.wrapped = err; - return terr; - }; - module.exports.TomlError = TomlError; - var createDateTime = require_create_datetime(); - var createDateTimeFloat = require_create_datetime_float(); - var createDate = require_create_date(); - var createTime = require_create_time(); - var CTRL_I = 9; - var CTRL_J = 10; - var CTRL_M = 13; - var CTRL_CHAR_BOUNDARY = 31; - var CHAR_SP = 32; - var CHAR_QUOT = 34; - var CHAR_NUM = 35; - var CHAR_APOS = 39; - var CHAR_PLUS = 43; - var CHAR_COMMA = 44; - var CHAR_HYPHEN = 45; - var CHAR_PERIOD = 46; - var CHAR_0 = 48; - var CHAR_1 = 49; - var CHAR_7 = 55; - var CHAR_9 = 57; - var CHAR_COLON = 58; - var CHAR_EQUALS = 61; - var CHAR_A = 65; - var CHAR_E = 69; - var CHAR_F = 70; - var CHAR_T = 84; - var CHAR_U = 85; - var CHAR_Z = 90; - var CHAR_LOWBAR = 95; - var CHAR_a = 97; - var CHAR_b = 98; - var CHAR_e = 101; - var CHAR_f = 102; - var CHAR_i = 105; - var CHAR_l = 108; - var CHAR_n = 110; - var CHAR_o = 111; - var CHAR_r = 114; - var CHAR_s = 115; - var CHAR_t = 116; - var CHAR_u = 117; - var CHAR_x = 120; - var CHAR_z = 122; - var CHAR_LCUB = 123; - var CHAR_RCUB = 125; - var CHAR_LSQB = 91; - var CHAR_BSOL = 92; - var CHAR_RSQB = 93; - var CHAR_DEL = 127; - var SURROGATE_FIRST = 55296; - var SURROGATE_LAST = 57343; - var escapes = { - [CHAR_b]: "\b", - [CHAR_t]: " ", - [CHAR_n]: "\n", - [CHAR_f]: "\f", - [CHAR_r]: "\r", - [CHAR_QUOT]: '"', - [CHAR_BSOL]: "\\" - }; - function isDigit(cp) { - return cp >= CHAR_0 && cp <= CHAR_9; - } - function isHexit(cp) { - return cp >= CHAR_A && cp <= CHAR_F || cp >= CHAR_a && cp <= CHAR_f || cp >= CHAR_0 && cp <= CHAR_9; - } - function isBit(cp) { - return cp === CHAR_1 || cp === CHAR_0; - } - function isOctit(cp) { - return cp >= CHAR_0 && cp <= CHAR_7; - } - function isAlphaNumQuoteHyphen(cp) { - return cp >= CHAR_A && cp <= CHAR_Z || cp >= CHAR_a && cp <= CHAR_z || cp >= CHAR_0 && cp <= CHAR_9 || cp === CHAR_APOS || cp === CHAR_QUOT || cp === CHAR_LOWBAR || cp === CHAR_HYPHEN; - } - function isAlphaNumHyphen(cp) { - return cp >= CHAR_A && cp <= CHAR_Z || cp >= CHAR_a && cp <= CHAR_z || cp >= CHAR_0 && cp <= CHAR_9 || cp === CHAR_LOWBAR || cp === CHAR_HYPHEN; - } - var _type = Symbol("type"); - var _declared = Symbol("declared"); - var hasOwnProperty = Object.prototype.hasOwnProperty; - var defineProperty = Object.defineProperty; - var descriptor = { configurable: true, enumerable: true, writable: true, value: void 0 }; - function hasKey(obj, key) { - if (hasOwnProperty.call(obj, key)) return true; - if (key === "__proto__") defineProperty(obj, "__proto__", descriptor); - return false; - } - var INLINE_TABLE = Symbol("inline-table"); - function InlineTable() { - return Object.defineProperties({}, { - [_type]: { value: INLINE_TABLE } - }); - } - function isInlineTable(obj) { - if (obj === null || typeof obj !== "object") return false; - return obj[_type] === INLINE_TABLE; - } - var TABLE = Symbol("table"); - function Table() { - return Object.defineProperties({}, { - [_type]: { value: TABLE }, - [_declared]: { value: false, writable: true } - }); - } - function isTable(obj) { - if (obj === null || typeof obj !== "object") return false; - return obj[_type] === TABLE; - } - var _contentType = Symbol("content-type"); - var INLINE_LIST = Symbol("inline-list"); - function InlineList(type) { - return Object.defineProperties([], { - [_type]: { value: INLINE_LIST }, - [_contentType]: { value: type } - }); - } - function isInlineList(obj) { - if (obj === null || typeof obj !== "object") return false; - return obj[_type] === INLINE_LIST; - } - var LIST = Symbol("list"); - function List() { - return Object.defineProperties([], { - [_type]: { value: LIST } - }); - } - function isList(obj) { - if (obj === null || typeof obj !== "object") return false; - return obj[_type] === LIST; - } - var _custom; - try { - const utilInspect = eval("require('util').inspect"); - _custom = utilInspect.custom; - } catch (_) { - } - var _inspect = _custom || "inspect"; - var BoxedBigInt = class { - constructor(value) { - try { - this.value = globalThis.BigInt.asIntN(64, value); - } catch (_) { - this.value = null; - } - Object.defineProperty(this, _type, { value: INTEGER }); - } - isNaN() { - return this.value === null; - } - /* istanbul ignore next */ - toString() { - return String(this.value); - } - /* istanbul ignore next */ - [_inspect]() { - return `[BigInt: ${this.toString()}]}`; - } - valueOf() { - return this.value; - } - }; - var INTEGER = Symbol("integer"); - function Integer(value) { - let num = Number(value); - if (Object.is(num, -0)) num = 0; - if (globalThis.BigInt && !Number.isSafeInteger(num)) { - return new BoxedBigInt(value); - } else { - return Object.defineProperties(new Number(num), { - isNaN: { value: function() { - return isNaN(this); - } }, - [_type]: { value: INTEGER }, - [_inspect]: { value: () => `[Integer: ${value}]` } - }); - } - } - function isInteger(obj) { - if (obj === null || typeof obj !== "object") return false; - return obj[_type] === INTEGER; - } - var FLOAT = Symbol("float"); - function Float(value) { - return Object.defineProperties(new Number(value), { - [_type]: { value: FLOAT }, - [_inspect]: { value: () => `[Float: ${value}]` } - }); - } - function isFloat(obj) { - if (obj === null || typeof obj !== "object") return false; - return obj[_type] === FLOAT; - } - function tomlType(value) { - const type = typeof value; - if (type === "object") { - if (value === null) return "null"; - if (value instanceof Date) return "datetime"; - if (_type in value) { - switch (value[_type]) { - case INLINE_TABLE: - return "inline-table"; - case INLINE_LIST: - return "inline-list"; - /* istanbul ignore next */ - case TABLE: - return "table"; - /* istanbul ignore next */ - case LIST: - return "list"; - case FLOAT: - return "float"; - case INTEGER: - return "integer"; - } - } - } - return type; - } - function makeParserClass(Parser) { - class TOMLParser extends Parser { - constructor() { - super(); - this.ctx = this.obj = Table(); - } - /* MATCH HELPER */ - atEndOfWord() { - return this.char === CHAR_NUM || this.char === CTRL_I || this.char === CHAR_SP || this.atEndOfLine(); - } - atEndOfLine() { - return this.char === Parser.END || this.char === CTRL_J || this.char === CTRL_M; - } - parseStart() { - if (this.char === Parser.END) { - return null; - } else if (this.char === CHAR_LSQB) { - return this.call(this.parseTableOrList); - } else if (this.char === CHAR_NUM) { - return this.call(this.parseComment); - } else if (this.char === CTRL_J || this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M) { - return null; - } else if (isAlphaNumQuoteHyphen(this.char)) { - return this.callNow(this.parseAssignStatement); - } else { - throw this.error(new TomlError(`Unknown character "${this.char}"`)); - } - } - // HELPER, this strips any whitespace and comments to the end of the line - // then RETURNS. Last state in a production. - parseWhitespaceToEOL() { - if (this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M) { - return null; - } else if (this.char === CHAR_NUM) { - return this.goto(this.parseComment); - } else if (this.char === Parser.END || this.char === CTRL_J) { - return this.return(); - } else { - throw this.error(new TomlError("Unexpected character, expected only whitespace or comments till end of line")); - } - } - /* ASSIGNMENT: key = value */ - parseAssignStatement() { - return this.callNow(this.parseAssign, this.recordAssignStatement); - } - recordAssignStatement(kv) { - let target = this.ctx; - let finalKey = kv.key.pop(); - for (let kw of kv.key) { - if (hasKey(target, kw) && (!isTable(target[kw]) || target[kw][_declared])) { - throw this.error(new TomlError("Can't redefine existing key")); - } - target = target[kw] = target[kw] || Table(); - } - if (hasKey(target, finalKey)) { - throw this.error(new TomlError("Can't redefine existing key")); - } - if (isInteger(kv.value) || isFloat(kv.value)) { - target[finalKey] = kv.value.valueOf(); - } else { - target[finalKey] = kv.value; - } - return this.goto(this.parseWhitespaceToEOL); - } - /* ASSSIGNMENT expression, key = value possibly inside an inline table */ - parseAssign() { - return this.callNow(this.parseKeyword, this.recordAssignKeyword); - } - recordAssignKeyword(key) { - if (this.state.resultTable) { - this.state.resultTable.push(key); - } else { - this.state.resultTable = [key]; - } - return this.goto(this.parseAssignKeywordPreDot); - } - parseAssignKeywordPreDot() { - if (this.char === CHAR_PERIOD) { - return this.next(this.parseAssignKeywordPostDot); - } else if (this.char !== CHAR_SP && this.char !== CTRL_I) { - return this.goto(this.parseAssignEqual); - } - } - parseAssignKeywordPostDot() { - if (this.char !== CHAR_SP && this.char !== CTRL_I) { - return this.callNow(this.parseKeyword, this.recordAssignKeyword); - } - } - parseAssignEqual() { - if (this.char === CHAR_EQUALS) { - return this.next(this.parseAssignPreValue); - } else { - throw this.error(new TomlError('Invalid character, expected "="')); - } - } - parseAssignPreValue() { - if (this.char === CHAR_SP || this.char === CTRL_I) { - return null; - } else { - return this.callNow(this.parseValue, this.recordAssignValue); - } - } - recordAssignValue(value) { - return this.returnNow({ key: this.state.resultTable, value }); - } - /* COMMENTS: #...eol */ - parseComment() { - do { - if (this.char === Parser.END || this.char === CTRL_J) { - return this.return(); - } - } while (this.nextChar()); - } - /* TABLES AND LISTS, [foo] and [[foo]] */ - parseTableOrList() { - if (this.char === CHAR_LSQB) { - this.next(this.parseList); - } else { - return this.goto(this.parseTable); - } - } - /* TABLE [foo.bar.baz] */ - parseTable() { - this.ctx = this.obj; - return this.goto(this.parseTableNext); - } - parseTableNext() { - if (this.char === CHAR_SP || this.char === CTRL_I) { - return null; - } else { - return this.callNow(this.parseKeyword, this.parseTableMore); - } - } - parseTableMore(keyword) { - if (this.char === CHAR_SP || this.char === CTRL_I) { - return null; - } else if (this.char === CHAR_RSQB) { - if (hasKey(this.ctx, keyword) && (!isTable(this.ctx[keyword]) || this.ctx[keyword][_declared])) { - throw this.error(new TomlError("Can't redefine existing key")); - } else { - this.ctx = this.ctx[keyword] = this.ctx[keyword] || Table(); - this.ctx[_declared] = true; - } - return this.next(this.parseWhitespaceToEOL); - } else if (this.char === CHAR_PERIOD) { - if (!hasKey(this.ctx, keyword)) { - this.ctx = this.ctx[keyword] = Table(); - } else if (isTable(this.ctx[keyword])) { - this.ctx = this.ctx[keyword]; - } else if (isList(this.ctx[keyword])) { - this.ctx = this.ctx[keyword][this.ctx[keyword].length - 1]; - } else { - throw this.error(new TomlError("Can't redefine existing key")); - } - return this.next(this.parseTableNext); - } else { - throw this.error(new TomlError("Unexpected character, expected whitespace, . or ]")); - } - } - /* LIST [[a.b.c]] */ - parseList() { - this.ctx = this.obj; - return this.goto(this.parseListNext); - } - parseListNext() { - if (this.char === CHAR_SP || this.char === CTRL_I) { - return null; - } else { - return this.callNow(this.parseKeyword, this.parseListMore); - } - } - parseListMore(keyword) { - if (this.char === CHAR_SP || this.char === CTRL_I) { - return null; - } else if (this.char === CHAR_RSQB) { - if (!hasKey(this.ctx, keyword)) { - this.ctx[keyword] = List(); - } - if (isInlineList(this.ctx[keyword])) { - throw this.error(new TomlError("Can't extend an inline array")); - } else if (isList(this.ctx[keyword])) { - const next = Table(); - this.ctx[keyword].push(next); - this.ctx = next; - } else { - throw this.error(new TomlError("Can't redefine an existing key")); - } - return this.next(this.parseListEnd); - } else if (this.char === CHAR_PERIOD) { - if (!hasKey(this.ctx, keyword)) { - this.ctx = this.ctx[keyword] = Table(); - } else if (isInlineList(this.ctx[keyword])) { - throw this.error(new TomlError("Can't extend an inline array")); - } else if (isInlineTable(this.ctx[keyword])) { - throw this.error(new TomlError("Can't extend an inline table")); - } else if (isList(this.ctx[keyword])) { - this.ctx = this.ctx[keyword][this.ctx[keyword].length - 1]; - } else if (isTable(this.ctx[keyword])) { - this.ctx = this.ctx[keyword]; - } else { - throw this.error(new TomlError("Can't redefine an existing key")); - } - return this.next(this.parseListNext); - } else { - throw this.error(new TomlError("Unexpected character, expected whitespace, . or ]")); - } - } - parseListEnd(keyword) { - if (this.char === CHAR_RSQB) { - return this.next(this.parseWhitespaceToEOL); - } else { - throw this.error(new TomlError("Unexpected character, expected whitespace, . or ]")); - } - } - /* VALUE string, number, boolean, inline list, inline object */ - parseValue() { - if (this.char === Parser.END) { - throw this.error(new TomlError("Key without value")); - } else if (this.char === CHAR_QUOT) { - return this.next(this.parseDoubleString); - } - if (this.char === CHAR_APOS) { - return this.next(this.parseSingleString); - } else if (this.char === CHAR_HYPHEN || this.char === CHAR_PLUS) { - return this.goto(this.parseNumberSign); - } else if (this.char === CHAR_i) { - return this.next(this.parseInf); - } else if (this.char === CHAR_n) { - return this.next(this.parseNan); - } else if (isDigit(this.char)) { - return this.goto(this.parseNumberOrDateTime); - } else if (this.char === CHAR_t || this.char === CHAR_f) { - return this.goto(this.parseBoolean); - } else if (this.char === CHAR_LSQB) { - return this.call(this.parseInlineList, this.recordValue); - } else if (this.char === CHAR_LCUB) { - return this.call(this.parseInlineTable, this.recordValue); - } else { - throw this.error(new TomlError("Unexpected character, expecting string, number, datetime, boolean, inline array or inline table")); - } - } - recordValue(value) { - return this.returnNow(value); - } - parseInf() { - if (this.char === CHAR_n) { - return this.next(this.parseInf2); - } else { - throw this.error(new TomlError('Unexpected character, expected "inf", "+inf" or "-inf"')); - } - } - parseInf2() { - if (this.char === CHAR_f) { - if (this.state.buf === "-") { - return this.return(-Infinity); - } else { - return this.return(Infinity); - } - } else { - throw this.error(new TomlError('Unexpected character, expected "inf", "+inf" or "-inf"')); - } - } - parseNan() { - if (this.char === CHAR_a) { - return this.next(this.parseNan2); - } else { - throw this.error(new TomlError('Unexpected character, expected "nan"')); - } - } - parseNan2() { - if (this.char === CHAR_n) { - return this.return(NaN); - } else { - throw this.error(new TomlError('Unexpected character, expected "nan"')); - } - } - /* KEYS, barewords or basic, literal, or dotted */ - parseKeyword() { - if (this.char === CHAR_QUOT) { - return this.next(this.parseBasicString); - } else if (this.char === CHAR_APOS) { - return this.next(this.parseLiteralString); - } else { - return this.goto(this.parseBareKey); - } - } - /* KEYS: barewords */ - parseBareKey() { - do { - if (this.char === Parser.END) { - throw this.error(new TomlError("Key ended without value")); - } else if (isAlphaNumHyphen(this.char)) { - this.consume(); - } else if (this.state.buf.length === 0) { - throw this.error(new TomlError("Empty bare keys are not allowed")); - } else { - return this.returnNow(); - } - } while (this.nextChar()); - } - /* STRINGS, single quoted (literal) */ - parseSingleString() { - if (this.char === CHAR_APOS) { - return this.next(this.parseLiteralMultiStringMaybe); - } else { - return this.goto(this.parseLiteralString); - } - } - parseLiteralString() { - do { - if (this.char === CHAR_APOS) { - return this.return(); - } else if (this.atEndOfLine()) { - throw this.error(new TomlError("Unterminated string")); - } else if (this.char === CHAR_DEL || this.char <= CTRL_CHAR_BOUNDARY && this.char !== CTRL_I) { - throw this.errorControlCharInString(); - } else { - this.consume(); - } - } while (this.nextChar()); - } - parseLiteralMultiStringMaybe() { - if (this.char === CHAR_APOS) { - return this.next(this.parseLiteralMultiString); - } else { - return this.returnNow(); - } - } - parseLiteralMultiString() { - if (this.char === CTRL_M) { - return null; - } else if (this.char === CTRL_J) { - return this.next(this.parseLiteralMultiStringContent); - } else { - return this.goto(this.parseLiteralMultiStringContent); - } - } - parseLiteralMultiStringContent() { - do { - if (this.char === CHAR_APOS) { - return this.next(this.parseLiteralMultiEnd); - } else if (this.char === Parser.END) { - throw this.error(new TomlError("Unterminated multi-line string")); - } else if (this.char === CHAR_DEL || this.char <= CTRL_CHAR_BOUNDARY && this.char !== CTRL_I && this.char !== CTRL_J && this.char !== CTRL_M) { - throw this.errorControlCharInString(); - } else { - this.consume(); - } - } while (this.nextChar()); - } - parseLiteralMultiEnd() { - if (this.char === CHAR_APOS) { - return this.next(this.parseLiteralMultiEnd2); - } else { - this.state.buf += "'"; - return this.goto(this.parseLiteralMultiStringContent); - } - } - parseLiteralMultiEnd2() { - if (this.char === CHAR_APOS) { - return this.return(); - } else { - this.state.buf += "''"; - return this.goto(this.parseLiteralMultiStringContent); - } - } - /* STRINGS double quoted */ - parseDoubleString() { - if (this.char === CHAR_QUOT) { - return this.next(this.parseMultiStringMaybe); - } else { - return this.goto(this.parseBasicString); - } - } - parseBasicString() { - do { - if (this.char === CHAR_BSOL) { - return this.call(this.parseEscape, this.recordEscapeReplacement); - } else if (this.char === CHAR_QUOT) { - return this.return(); - } else if (this.atEndOfLine()) { - throw this.error(new TomlError("Unterminated string")); - } else if (this.char === CHAR_DEL || this.char <= CTRL_CHAR_BOUNDARY && this.char !== CTRL_I) { - throw this.errorControlCharInString(); - } else { - this.consume(); - } - } while (this.nextChar()); - } - recordEscapeReplacement(replacement) { - this.state.buf += replacement; - return this.goto(this.parseBasicString); - } - parseMultiStringMaybe() { - if (this.char === CHAR_QUOT) { - return this.next(this.parseMultiString); - } else { - return this.returnNow(); - } - } - parseMultiString() { - if (this.char === CTRL_M) { - return null; - } else if (this.char === CTRL_J) { - return this.next(this.parseMultiStringContent); - } else { - return this.goto(this.parseMultiStringContent); - } - } - parseMultiStringContent() { - do { - if (this.char === CHAR_BSOL) { - return this.call(this.parseMultiEscape, this.recordMultiEscapeReplacement); - } else if (this.char === CHAR_QUOT) { - return this.next(this.parseMultiEnd); - } else if (this.char === Parser.END) { - throw this.error(new TomlError("Unterminated multi-line string")); - } else if (this.char === CHAR_DEL || this.char <= CTRL_CHAR_BOUNDARY && this.char !== CTRL_I && this.char !== CTRL_J && this.char !== CTRL_M) { - throw this.errorControlCharInString(); - } else { - this.consume(); - } - } while (this.nextChar()); - } - errorControlCharInString() { - let displayCode = "\\u00"; - if (this.char < 16) { - displayCode += "0"; - } - displayCode += this.char.toString(16); - return this.error(new TomlError(`Control characters (codes < 0x1f and 0x7f) are not allowed in strings, use ${displayCode} instead`)); - } - recordMultiEscapeReplacement(replacement) { - this.state.buf += replacement; - return this.goto(this.parseMultiStringContent); - } - parseMultiEnd() { - if (this.char === CHAR_QUOT) { - return this.next(this.parseMultiEnd2); - } else { - this.state.buf += '"'; - return this.goto(this.parseMultiStringContent); - } - } - parseMultiEnd2() { - if (this.char === CHAR_QUOT) { - return this.return(); - } else { - this.state.buf += '""'; - return this.goto(this.parseMultiStringContent); - } - } - parseMultiEscape() { - if (this.char === CTRL_M || this.char === CTRL_J) { - return this.next(this.parseMultiTrim); - } else if (this.char === CHAR_SP || this.char === CTRL_I) { - return this.next(this.parsePreMultiTrim); - } else { - return this.goto(this.parseEscape); - } - } - parsePreMultiTrim() { - if (this.char === CHAR_SP || this.char === CTRL_I) { - return null; - } else if (this.char === CTRL_M || this.char === CTRL_J) { - return this.next(this.parseMultiTrim); - } else { - throw this.error(new TomlError("Can't escape whitespace")); - } - } - parseMultiTrim() { - if (this.char === CTRL_J || this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M) { - return null; - } else { - return this.returnNow(); - } - } - parseEscape() { - if (this.char in escapes) { - return this.return(escapes[this.char]); - } else if (this.char === CHAR_u) { - return this.call(this.parseSmallUnicode, this.parseUnicodeReturn); - } else if (this.char === CHAR_U) { - return this.call(this.parseLargeUnicode, this.parseUnicodeReturn); - } else { - throw this.error(new TomlError("Unknown escape character: " + this.char)); - } - } - parseUnicodeReturn(char) { - try { - const codePoint = parseInt(char, 16); - if (codePoint >= SURROGATE_FIRST && codePoint <= SURROGATE_LAST) { - throw this.error(new TomlError("Invalid unicode, character in range 0xD800 - 0xDFFF is reserved")); - } - return this.returnNow(String.fromCodePoint(codePoint)); - } catch (err) { - throw this.error(TomlError.wrap(err)); - } - } - parseSmallUnicode() { - if (!isHexit(this.char)) { - throw this.error(new TomlError("Invalid character in unicode sequence, expected hex")); - } else { - this.consume(); - if (this.state.buf.length >= 4) return this.return(); - } - } - parseLargeUnicode() { - if (!isHexit(this.char)) { - throw this.error(new TomlError("Invalid character in unicode sequence, expected hex")); - } else { - this.consume(); - if (this.state.buf.length >= 8) return this.return(); - } - } - /* NUMBERS */ - parseNumberSign() { - this.consume(); - return this.next(this.parseMaybeSignedInfOrNan); - } - parseMaybeSignedInfOrNan() { - if (this.char === CHAR_i) { - return this.next(this.parseInf); - } else if (this.char === CHAR_n) { - return this.next(this.parseNan); - } else { - return this.callNow(this.parseNoUnder, this.parseNumberIntegerStart); - } - } - parseNumberIntegerStart() { - if (this.char === CHAR_0) { - this.consume(); - return this.next(this.parseNumberIntegerExponentOrDecimal); - } else { - return this.goto(this.parseNumberInteger); - } - } - parseNumberIntegerExponentOrDecimal() { - if (this.char === CHAR_PERIOD) { - this.consume(); - return this.call(this.parseNoUnder, this.parseNumberFloat); - } else if (this.char === CHAR_E || this.char === CHAR_e) { - this.consume(); - return this.next(this.parseNumberExponentSign); - } else { - return this.returnNow(Integer(this.state.buf)); - } - } - parseNumberInteger() { - if (isDigit(this.char)) { - this.consume(); - } else if (this.char === CHAR_LOWBAR) { - return this.call(this.parseNoUnder); - } else if (this.char === CHAR_E || this.char === CHAR_e) { - this.consume(); - return this.next(this.parseNumberExponentSign); - } else if (this.char === CHAR_PERIOD) { - this.consume(); - return this.call(this.parseNoUnder, this.parseNumberFloat); - } else { - const result = Integer(this.state.buf); - if (result.isNaN()) { - throw this.error(new TomlError("Invalid number")); - } else { - return this.returnNow(result); - } - } - } - parseNoUnder() { - if (this.char === CHAR_LOWBAR || this.char === CHAR_PERIOD || this.char === CHAR_E || this.char === CHAR_e) { - throw this.error(new TomlError("Unexpected character, expected digit")); - } else if (this.atEndOfWord()) { - throw this.error(new TomlError("Incomplete number")); - } - return this.returnNow(); - } - parseNoUnderHexOctBinLiteral() { - if (this.char === CHAR_LOWBAR || this.char === CHAR_PERIOD) { - throw this.error(new TomlError("Unexpected character, expected digit")); - } else if (this.atEndOfWord()) { - throw this.error(new TomlError("Incomplete number")); - } - return this.returnNow(); - } - parseNumberFloat() { - if (this.char === CHAR_LOWBAR) { - return this.call(this.parseNoUnder, this.parseNumberFloat); - } else if (isDigit(this.char)) { - this.consume(); - } else if (this.char === CHAR_E || this.char === CHAR_e) { - this.consume(); - return this.next(this.parseNumberExponentSign); - } else { - return this.returnNow(Float(this.state.buf)); - } - } - parseNumberExponentSign() { - if (isDigit(this.char)) { - return this.goto(this.parseNumberExponent); - } else if (this.char === CHAR_HYPHEN || this.char === CHAR_PLUS) { - this.consume(); - this.call(this.parseNoUnder, this.parseNumberExponent); - } else { - throw this.error(new TomlError("Unexpected character, expected -, + or digit")); - } - } - parseNumberExponent() { - if (isDigit(this.char)) { - this.consume(); - } else if (this.char === CHAR_LOWBAR) { - return this.call(this.parseNoUnder); - } else { - return this.returnNow(Float(this.state.buf)); - } - } - /* NUMBERS or DATETIMES */ - parseNumberOrDateTime() { - if (this.char === CHAR_0) { - this.consume(); - return this.next(this.parseNumberBaseOrDateTime); - } else { - return this.goto(this.parseNumberOrDateTimeOnly); - } - } - parseNumberOrDateTimeOnly() { - if (this.char === CHAR_LOWBAR) { - return this.call(this.parseNoUnder, this.parseNumberInteger); - } else if (isDigit(this.char)) { - this.consume(); - if (this.state.buf.length > 4) this.next(this.parseNumberInteger); - } else if (this.char === CHAR_E || this.char === CHAR_e) { - this.consume(); - return this.next(this.parseNumberExponentSign); - } else if (this.char === CHAR_PERIOD) { - this.consume(); - return this.call(this.parseNoUnder, this.parseNumberFloat); - } else if (this.char === CHAR_HYPHEN) { - return this.goto(this.parseDateTime); - } else if (this.char === CHAR_COLON) { - return this.goto(this.parseOnlyTimeHour); - } else { - return this.returnNow(Integer(this.state.buf)); - } - } - parseDateTimeOnly() { - if (this.state.buf.length < 4) { - if (isDigit(this.char)) { - return this.consume(); - } else if (this.char === CHAR_COLON) { - return this.goto(this.parseOnlyTimeHour); - } else { - throw this.error(new TomlError("Expected digit while parsing year part of a date")); - } - } else { - if (this.char === CHAR_HYPHEN) { - return this.goto(this.parseDateTime); - } else { - throw this.error(new TomlError("Expected hyphen (-) while parsing year part of date")); - } - } - } - parseNumberBaseOrDateTime() { - if (this.char === CHAR_b) { - this.consume(); - return this.call(this.parseNoUnderHexOctBinLiteral, this.parseIntegerBin); - } else if (this.char === CHAR_o) { - this.consume(); - return this.call(this.parseNoUnderHexOctBinLiteral, this.parseIntegerOct); - } else if (this.char === CHAR_x) { - this.consume(); - return this.call(this.parseNoUnderHexOctBinLiteral, this.parseIntegerHex); - } else if (this.char === CHAR_PERIOD) { - return this.goto(this.parseNumberInteger); - } else if (isDigit(this.char)) { - return this.goto(this.parseDateTimeOnly); - } else { - return this.returnNow(Integer(this.state.buf)); - } - } - parseIntegerHex() { - if (isHexit(this.char)) { - this.consume(); - } else if (this.char === CHAR_LOWBAR) { - return this.call(this.parseNoUnderHexOctBinLiteral); - } else { - const result = Integer(this.state.buf); - if (result.isNaN()) { - throw this.error(new TomlError("Invalid number")); - } else { - return this.returnNow(result); - } - } - } - parseIntegerOct() { - if (isOctit(this.char)) { - this.consume(); - } else if (this.char === CHAR_LOWBAR) { - return this.call(this.parseNoUnderHexOctBinLiteral); - } else { - const result = Integer(this.state.buf); - if (result.isNaN()) { - throw this.error(new TomlError("Invalid number")); - } else { - return this.returnNow(result); - } - } - } - parseIntegerBin() { - if (isBit(this.char)) { - this.consume(); - } else if (this.char === CHAR_LOWBAR) { - return this.call(this.parseNoUnderHexOctBinLiteral); - } else { - const result = Integer(this.state.buf); - if (result.isNaN()) { - throw this.error(new TomlError("Invalid number")); - } else { - return this.returnNow(result); - } - } - } - /* DATETIME */ - parseDateTime() { - if (this.state.buf.length < 4) { - throw this.error(new TomlError("Years less than 1000 must be zero padded to four characters")); - } - this.state.result = this.state.buf; - this.state.buf = ""; - return this.next(this.parseDateMonth); - } - parseDateMonth() { - if (this.char === CHAR_HYPHEN) { - if (this.state.buf.length < 2) { - throw this.error(new TomlError("Months less than 10 must be zero padded to two characters")); - } - this.state.result += "-" + this.state.buf; - this.state.buf = ""; - return this.next(this.parseDateDay); - } else if (isDigit(this.char)) { - this.consume(); - } else { - throw this.error(new TomlError("Incomplete datetime")); - } - } - parseDateDay() { - if (this.char === CHAR_T || this.char === CHAR_SP) { - if (this.state.buf.length < 2) { - throw this.error(new TomlError("Days less than 10 must be zero padded to two characters")); - } - this.state.result += "-" + this.state.buf; - this.state.buf = ""; - return this.next(this.parseStartTimeHour); - } else if (this.atEndOfWord()) { - return this.returnNow(createDate(this.state.result + "-" + this.state.buf)); - } else if (isDigit(this.char)) { - this.consume(); - } else { - throw this.error(new TomlError("Incomplete datetime")); - } - } - parseStartTimeHour() { - if (this.atEndOfWord()) { - return this.returnNow(createDate(this.state.result)); - } else { - return this.goto(this.parseTimeHour); - } - } - parseTimeHour() { - if (this.char === CHAR_COLON) { - if (this.state.buf.length < 2) { - throw this.error(new TomlError("Hours less than 10 must be zero padded to two characters")); - } - this.state.result += "T" + this.state.buf; - this.state.buf = ""; - return this.next(this.parseTimeMin); - } else if (isDigit(this.char)) { - this.consume(); - } else { - throw this.error(new TomlError("Incomplete datetime")); - } - } - parseTimeMin() { - if (this.state.buf.length < 2 && isDigit(this.char)) { - this.consume(); - } else if (this.state.buf.length === 2 && this.char === CHAR_COLON) { - this.state.result += ":" + this.state.buf; - this.state.buf = ""; - return this.next(this.parseTimeSec); - } else { - throw this.error(new TomlError("Incomplete datetime")); - } - } - parseTimeSec() { - if (isDigit(this.char)) { - this.consume(); - if (this.state.buf.length === 2) { - this.state.result += ":" + this.state.buf; - this.state.buf = ""; - return this.next(this.parseTimeZoneOrFraction); - } - } else { - throw this.error(new TomlError("Incomplete datetime")); - } - } - parseOnlyTimeHour() { - if (this.char === CHAR_COLON) { - if (this.state.buf.length < 2) { - throw this.error(new TomlError("Hours less than 10 must be zero padded to two characters")); - } - this.state.result = this.state.buf; - this.state.buf = ""; - return this.next(this.parseOnlyTimeMin); - } else { - throw this.error(new TomlError("Incomplete time")); - } - } - parseOnlyTimeMin() { - if (this.state.buf.length < 2 && isDigit(this.char)) { - this.consume(); - } else if (this.state.buf.length === 2 && this.char === CHAR_COLON) { - this.state.result += ":" + this.state.buf; - this.state.buf = ""; - return this.next(this.parseOnlyTimeSec); - } else { - throw this.error(new TomlError("Incomplete time")); - } - } - parseOnlyTimeSec() { - if (isDigit(this.char)) { - this.consume(); - if (this.state.buf.length === 2) { - return this.next(this.parseOnlyTimeFractionMaybe); - } - } else { - throw this.error(new TomlError("Incomplete time")); - } - } - parseOnlyTimeFractionMaybe() { - this.state.result += ":" + this.state.buf; - if (this.char === CHAR_PERIOD) { - this.state.buf = ""; - this.next(this.parseOnlyTimeFraction); - } else { - return this.return(createTime(this.state.result)); - } - } - parseOnlyTimeFraction() { - if (isDigit(this.char)) { - this.consume(); - } else if (this.atEndOfWord()) { - if (this.state.buf.length === 0) throw this.error(new TomlError("Expected digit in milliseconds")); - return this.returnNow(createTime(this.state.result + "." + this.state.buf)); - } else { - throw this.error(new TomlError("Unexpected character in datetime, expected period (.), minus (-), plus (+) or Z")); - } - } - parseTimeZoneOrFraction() { - if (this.char === CHAR_PERIOD) { - this.consume(); - this.next(this.parseDateTimeFraction); - } else if (this.char === CHAR_HYPHEN || this.char === CHAR_PLUS) { - this.consume(); - this.next(this.parseTimeZoneHour); - } else if (this.char === CHAR_Z) { - this.consume(); - return this.return(createDateTime(this.state.result + this.state.buf)); - } else if (this.atEndOfWord()) { - return this.returnNow(createDateTimeFloat(this.state.result + this.state.buf)); - } else { - throw this.error(new TomlError("Unexpected character in datetime, expected period (.), minus (-), plus (+) or Z")); - } - } - parseDateTimeFraction() { - if (isDigit(this.char)) { - this.consume(); - } else if (this.state.buf.length === 1) { - throw this.error(new TomlError("Expected digit in milliseconds")); - } else if (this.char === CHAR_HYPHEN || this.char === CHAR_PLUS) { - this.consume(); - this.next(this.parseTimeZoneHour); - } else if (this.char === CHAR_Z) { - this.consume(); - return this.return(createDateTime(this.state.result + this.state.buf)); - } else if (this.atEndOfWord()) { - return this.returnNow(createDateTimeFloat(this.state.result + this.state.buf)); - } else { - throw this.error(new TomlError("Unexpected character in datetime, expected period (.), minus (-), plus (+) or Z")); - } - } - parseTimeZoneHour() { - if (isDigit(this.char)) { - this.consume(); - if (/\d\d$/.test(this.state.buf)) return this.next(this.parseTimeZoneSep); - } else { - throw this.error(new TomlError("Unexpected character in datetime, expected digit")); - } - } - parseTimeZoneSep() { - if (this.char === CHAR_COLON) { - this.consume(); - this.next(this.parseTimeZoneMin); - } else { - throw this.error(new TomlError("Unexpected character in datetime, expected colon")); - } - } - parseTimeZoneMin() { - if (isDigit(this.char)) { - this.consume(); - if (/\d\d$/.test(this.state.buf)) return this.return(createDateTime(this.state.result + this.state.buf)); - } else { - throw this.error(new TomlError("Unexpected character in datetime, expected digit")); - } - } - /* BOOLEAN */ - parseBoolean() { - if (this.char === CHAR_t) { - this.consume(); - return this.next(this.parseTrue_r); - } else if (this.char === CHAR_f) { - this.consume(); - return this.next(this.parseFalse_a); - } - } - parseTrue_r() { - if (this.char === CHAR_r) { - this.consume(); - return this.next(this.parseTrue_u); - } else { - throw this.error(new TomlError("Invalid boolean, expected true or false")); - } - } - parseTrue_u() { - if (this.char === CHAR_u) { - this.consume(); - return this.next(this.parseTrue_e); - } else { - throw this.error(new TomlError("Invalid boolean, expected true or false")); - } - } - parseTrue_e() { - if (this.char === CHAR_e) { - return this.return(true); - } else { - throw this.error(new TomlError("Invalid boolean, expected true or false")); - } - } - parseFalse_a() { - if (this.char === CHAR_a) { - this.consume(); - return this.next(this.parseFalse_l); - } else { - throw this.error(new TomlError("Invalid boolean, expected true or false")); - } - } - parseFalse_l() { - if (this.char === CHAR_l) { - this.consume(); - return this.next(this.parseFalse_s); - } else { - throw this.error(new TomlError("Invalid boolean, expected true or false")); - } - } - parseFalse_s() { - if (this.char === CHAR_s) { - this.consume(); - return this.next(this.parseFalse_e); - } else { - throw this.error(new TomlError("Invalid boolean, expected true or false")); - } - } - parseFalse_e() { - if (this.char === CHAR_e) { - return this.return(false); - } else { - throw this.error(new TomlError("Invalid boolean, expected true or false")); - } - } - /* INLINE LISTS */ - parseInlineList() { - if (this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M || this.char === CTRL_J) { - return null; - } else if (this.char === Parser.END) { - throw this.error(new TomlError("Unterminated inline array")); - } else if (this.char === CHAR_NUM) { - return this.call(this.parseComment); - } else if (this.char === CHAR_RSQB) { - return this.return(this.state.resultArr || InlineList()); - } else { - return this.callNow(this.parseValue, this.recordInlineListValue); - } - } - recordInlineListValue(value) { - if (this.state.resultArr) { - const listType = this.state.resultArr[_contentType]; - const valueType = tomlType(value); - if (listType !== valueType) { - throw this.error(new TomlError(`Inline lists must be a single type, not a mix of ${listType} and ${valueType}`)); - } - } else { - this.state.resultArr = InlineList(tomlType(value)); - } - if (isFloat(value) || isInteger(value)) { - this.state.resultArr.push(value.valueOf()); - } else { - this.state.resultArr.push(value); - } - return this.goto(this.parseInlineListNext); - } - parseInlineListNext() { - if (this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M || this.char === CTRL_J) { - return null; - } else if (this.char === CHAR_NUM) { - return this.call(this.parseComment); - } else if (this.char === CHAR_COMMA) { - return this.next(this.parseInlineList); - } else if (this.char === CHAR_RSQB) { - return this.goto(this.parseInlineList); - } else { - throw this.error(new TomlError("Invalid character, expected whitespace, comma (,) or close bracket (])")); - } - } - /* INLINE TABLE */ - parseInlineTable() { - if (this.char === CHAR_SP || this.char === CTRL_I) { - return null; - } else if (this.char === Parser.END || this.char === CHAR_NUM || this.char === CTRL_J || this.char === CTRL_M) { - throw this.error(new TomlError("Unterminated inline array")); - } else if (this.char === CHAR_RCUB) { - return this.return(this.state.resultTable || InlineTable()); - } else { - if (!this.state.resultTable) this.state.resultTable = InlineTable(); - return this.callNow(this.parseAssign, this.recordInlineTableValue); - } - } - recordInlineTableValue(kv) { - let target = this.state.resultTable; - let finalKey = kv.key.pop(); - for (let kw of kv.key) { - if (hasKey(target, kw) && (!isTable(target[kw]) || target[kw][_declared])) { - throw this.error(new TomlError("Can't redefine existing key")); - } - target = target[kw] = target[kw] || Table(); - } - if (hasKey(target, finalKey)) { - throw this.error(new TomlError("Can't redefine existing key")); - } - if (isInteger(kv.value) || isFloat(kv.value)) { - target[finalKey] = kv.value.valueOf(); - } else { - target[finalKey] = kv.value; - } - return this.goto(this.parseInlineTableNext); - } - parseInlineTableNext() { - if (this.char === CHAR_SP || this.char === CTRL_I) { - return null; - } else if (this.char === Parser.END || this.char === CHAR_NUM || this.char === CTRL_J || this.char === CTRL_M) { - throw this.error(new TomlError("Unterminated inline array")); - } else if (this.char === CHAR_COMMA) { - return this.next(this.parseInlineTable); - } else if (this.char === CHAR_RCUB) { - return this.goto(this.parseInlineTable); - } else { - throw this.error(new TomlError("Invalid character, expected whitespace, comma (,) or close bracket (])")); - } - } - } - return TOMLParser; - } - } - }); - - // node_modules/@iarna/toml/parse-pretty-error.js - var require_parse_pretty_error = __commonJS({ - "node_modules/@iarna/toml/parse-pretty-error.js"(exports2, module2) { - "use strict"; - module2.exports = prettyError; - function prettyError(err, buf) { - if (err.pos == null || err.line == null) return err; - let msg = err.message; - msg += ` at row ${err.line + 1}, col ${err.col + 1}, pos ${err.pos}: -`; - if (buf && buf.split) { - const lines = buf.split(/\n/); - const lineNumWidth = String(Math.min(lines.length, err.line + 3)).length; - let linePadding = " "; - while (linePadding.length < lineNumWidth) linePadding += " "; - for (let ii = Math.max(0, err.line - 1); ii < Math.min(lines.length, err.line + 2); ++ii) { - let lineNum = String(ii + 1); - if (lineNum.length < lineNumWidth) lineNum = " " + lineNum; - if (err.line === ii) { - msg += lineNum + "> " + lines[ii] + "\n"; - msg += linePadding + " "; - for (let hh = 0; hh < err.col; ++hh) { - msg += " "; - } - msg += "^\n"; - } else { - msg += lineNum + ": " + lines[ii] + "\n"; - } - } - } - err.message = msg + "\n"; - return err; - } - } - }); - - // node_modules/@iarna/toml/parse-string.js - var require_parse_string = __commonJS({ - "node_modules/@iarna/toml/parse-string.js"(exports2, module2) { - "use strict"; - module2.exports = parseString; - var TOMLParser = require_toml_parser(); - var prettyError = require_parse_pretty_error(); - function parseString(str) { - if (globalThis.Buffer && globalThis.Buffer.isBuffer(str)) { - str = str.toString("utf8"); - } - const parser = new TOMLParser(); - try { - parser.parse(str); - return parser.finish(); - } catch (err) { - throw prettyError(err, str); - } - } - } - }); - - // node_modules/@iarna/toml/parse-async.js - var require_parse_async = __commonJS({ - "node_modules/@iarna/toml/parse-async.js"(exports2, module2) { - "use strict"; - module2.exports = parseAsync; - var TOMLParser = require_toml_parser(); - var prettyError = require_parse_pretty_error(); - function parseAsync(str, opts) { - if (!opts) opts = {}; - const index = 0; - const blocksize = opts.blocksize || 40960; - const parser = new TOMLParser(); - return new Promise((resolve, reject) => { - setImmediate(parseAsyncNext, index, blocksize, resolve, reject); - }); - function parseAsyncNext(index2, blocksize2, resolve, reject) { - if (index2 >= str.length) { - try { - return resolve(parser.finish()); - } catch (err) { - return reject(prettyError(err, str)); - } - } - try { - parser.parse(str.slice(index2, index2 + blocksize2)); - setImmediate(parseAsyncNext, index2 + blocksize2, blocksize2, resolve, reject); - } catch (err) { - reject(prettyError(err, str)); - } - } - } - } - }); - - // node_modules/component-emitter/index.js - var require_component_emitter = __commonJS({ - "node_modules/component-emitter/index.js"(exports2, module2) { - function Emitter(object) { - if (object) { - return mixin(object); - } - this._callbacks = /* @__PURE__ */ new Map(); - } - function mixin(object) { - Object.assign(object, Emitter.prototype); - object._callbacks = /* @__PURE__ */ new Map(); - return object; - } - Emitter.prototype.on = function(event, listener) { - const callbacks = this._callbacks.get(event) ?? []; - callbacks.push(listener); - this._callbacks.set(event, callbacks); - return this; - }; - Emitter.prototype.once = function(event, listener) { - const on = (...arguments_) => { - this.off(event, on); - listener.apply(this, arguments_); - }; - on.fn = listener; - this.on(event, on); - return this; - }; - Emitter.prototype.off = function(event, listener) { - if (event === void 0 && listener === void 0) { - this._callbacks.clear(); - return this; - } - if (listener === void 0) { - this._callbacks.delete(event); - return this; - } - const callbacks = this._callbacks.get(event); - if (callbacks) { - for (const [index, callback] of callbacks.entries()) { - if (callback === listener || callback.fn === listener) { - callbacks.splice(index, 1); - break; - } - } - if (callbacks.length === 0) { - this._callbacks.delete(event); - } else { - this._callbacks.set(event, callbacks); - } - } - return this; - }; - Emitter.prototype.emit = function(event, ...arguments_) { - const callbacks = this._callbacks.get(event); - if (callbacks) { - const callbacksCopy = [...callbacks]; - for (const callback of callbacksCopy) { - callback.apply(this, arguments_); - } - } - return this; - }; - Emitter.prototype.listeners = function(event) { - return this._callbacks.get(event) ?? []; - }; - Emitter.prototype.listenerCount = function(event) { - if (event) { - return this.listeners(event).length; - } - let totalCount = 0; - for (const callbacks of this._callbacks.values()) { - totalCount += callbacks.length; - } - return totalCount; - }; - Emitter.prototype.hasListeners = function(event) { - return this.listenerCount(event) > 0; - }; - Emitter.prototype.addEventListener = Emitter.prototype.on; - Emitter.prototype.removeListener = Emitter.prototype.off; - Emitter.prototype.removeEventListener = Emitter.prototype.off; - Emitter.prototype.removeAllListeners = Emitter.prototype.off; - if (typeof module2 !== "undefined") { - module2.exports = Emitter; - } - } - }); - - // node_modules/stream/index.js - var require_stream = __commonJS({ - "node_modules/stream/index.js"(exports2, module2) { - var Emitter = require_component_emitter(); - function Stream() { - Emitter.call(this); - } - Stream.prototype = new Emitter(); - module2.exports = Stream; - Stream.Stream = Stream; - Stream.prototype.pipe = function(dest, options) { - var source = this; - function ondata(chunk) { - if (dest.writable) { - if (false === dest.write(chunk) && source.pause) { - source.pause(); - } - } - } - source.on("data", ondata); - function ondrain() { - if (source.readable && source.resume) { - source.resume(); - } - } - dest.on("drain", ondrain); - if (!dest._isStdio && (!options || options.end !== false)) { - source.on("end", onend); - source.on("close", onclose); - } - var didOnEnd = false; - function onend() { - if (didOnEnd) return; - didOnEnd = true; - dest.end(); - } - function onclose() { - if (didOnEnd) return; - didOnEnd = true; - if (typeof dest.destroy === "function") dest.destroy(); - } - function onerror(er) { - cleanup(); - if (!this.hasListeners("error")) { - throw er; - } - } - source.on("error", onerror); - dest.on("error", onerror); - function cleanup() { - source.off("data", ondata); - dest.off("drain", ondrain); - source.off("end", onend); - source.off("close", onclose); - source.off("error", onerror); - dest.off("error", onerror); - source.off("end", cleanup); - source.off("close", cleanup); - dest.off("end", cleanup); - dest.off("close", cleanup); - } - source.on("end", cleanup); - source.on("close", cleanup); - dest.on("end", cleanup); - dest.on("close", cleanup); - dest.emit("pipe", source); - return dest; - }; - } - }); - - // node_modules/@iarna/toml/parse-stream.js - var require_parse_stream = __commonJS({ - "node_modules/@iarna/toml/parse-stream.js"(exports2, module2) { - "use strict"; - module2.exports = parseStream; - var stream = require_stream(); - var TOMLParser = require_toml_parser(); - function parseStream(stm) { - if (stm) { - return parseReadable(stm); - } else { - return parseTransform(stm); - } - } - function parseReadable(stm) { - const parser = new TOMLParser(); - stm.setEncoding("utf8"); - return new Promise((resolve, reject) => { - let readable; - let ended = false; - let errored = false; - function finish() { - ended = true; - if (readable) return; - try { - resolve(parser.finish()); - } catch (err) { - reject(err); - } - } - function error(err) { - errored = true; - reject(err); - } - stm.once("end", finish); - stm.once("error", error); - readNext(); - function readNext() { - readable = true; - let data; - while ((data = stm.read()) !== null) { - try { - parser.parse(data); - } catch (err) { - return error(err); - } - } - readable = false; - if (ended) return finish(); - if (errored) return; - stm.once("readable", readNext); - } - }); - } - function parseTransform() { - const parser = new TOMLParser(); - return new stream.Transform({ - objectMode: true, - transform(chunk, encoding, cb) { - try { - parser.parse(chunk.toString(encoding)); - } catch (err) { - this.emit("error", err); - } - cb(); - }, - flush(cb) { - try { - this.push(parser.finish()); - } catch (err) { - this.emit("error", err); - } - cb(); - } - }); - } - } - }); - - // node_modules/@iarna/toml/parse.js - var require_parse = __commonJS({ - "node_modules/@iarna/toml/parse.js"(exports2, module2) { - "use strict"; - module2.exports = require_parse_string(); - module2.exports.async = require_parse_async(); - module2.exports.stream = require_parse_stream(); - module2.exports.prettyError = require_parse_pretty_error(); - } - }); - - // node_modules/@iarna/toml/stringify.js - var require_stringify = __commonJS({ - "node_modules/@iarna/toml/stringify.js"(exports2, module2) { - "use strict"; - module2.exports = stringify; - module2.exports.value = stringifyInline; - function stringify(obj) { - if (obj === null) throw typeError("null"); - if (obj === void 0) throw typeError("undefined"); - if (typeof obj !== "object") throw typeError(typeof obj); - if (typeof obj.toJSON === "function") obj = obj.toJSON(); - if (obj == null) return null; - const type = tomlType2(obj); - if (type !== "table") throw typeError(type); - return stringifyObject("", "", obj); - } - function typeError(type) { - return new Error("Can only stringify objects, not " + type); - } - function arrayOneTypeError() { - return new Error("Array values can't have mixed types"); - } - function getInlineKeys(obj) { - return Object.keys(obj).filter((key) => isInline(obj[key])); - } - function getComplexKeys(obj) { - return Object.keys(obj).filter((key) => !isInline(obj[key])); - } - function toJSON(obj) { - let nobj = Array.isArray(obj) ? [] : Object.prototype.hasOwnProperty.call(obj, "__proto__") ? { ["__proto__"]: void 0 } : {}; - for (let prop of Object.keys(obj)) { - if (obj[prop] && typeof obj[prop].toJSON === "function" && !("toISOString" in obj[prop])) { - nobj[prop] = obj[prop].toJSON(); - } else { - nobj[prop] = obj[prop]; - } - } - return nobj; - } - function stringifyObject(prefix, indent, obj) { - obj = toJSON(obj); - var inlineKeys; - var complexKeys; - inlineKeys = getInlineKeys(obj); - complexKeys = getComplexKeys(obj); - var result = []; - var inlineIndent = indent || ""; - inlineKeys.forEach((key) => { - var type = tomlType2(obj[key]); - if (type !== "undefined" && type !== "null") { - result.push(inlineIndent + stringifyKey(key) + " = " + stringifyAnyInline(obj[key], true)); - } - }); - if (result.length > 0) result.push(""); - var complexIndent = prefix && inlineKeys.length > 0 ? indent + " " : ""; - complexKeys.forEach((key) => { - result.push(stringifyComplex(prefix, complexIndent, key, obj[key])); - }); - return result.join("\n"); - } - function isInline(value) { - switch (tomlType2(value)) { - case "undefined": - case "null": - case "integer": - case "nan": - case "float": - case "boolean": - case "string": - case "datetime": - return true; - case "array": - return value.length === 0 || tomlType2(value[0]) !== "table"; - case "table": - return Object.keys(value).length === 0; - /* istanbul ignore next */ - default: - return false; - } - } - function tomlType2(value) { - if (value === void 0) { - return "undefined"; - } else if (value === null) { - return "null"; - } else if (typeof value === "bigint" || Number.isInteger(value) && !Object.is(value, -0)) { - return "integer"; - } else if (typeof value === "number") { - return "float"; - } else if (typeof value === "boolean") { - return "boolean"; - } else if (typeof value === "string") { - return "string"; - } else if ("toISOString" in value) { - return isNaN(value) ? "undefined" : "datetime"; - } else if (Array.isArray(value)) { - return "array"; - } else { - return "table"; - } - } - function stringifyKey(key) { - var keyStr = String(key); - if (/^[-A-Za-z0-9_]+$/.test(keyStr)) { - return keyStr; - } else { - return stringifyBasicString(keyStr); - } - } - function stringifyBasicString(str) { - return '"' + escapeString(str).replace(/"/g, '\\"') + '"'; - } - function stringifyLiteralString(str) { - return "'" + str + "'"; - } - function numpad(num, str) { - while (str.length < num) str = "0" + str; - return str; - } - function escapeString(str) { - return str.replace(/\\/g, "\\\\").replace(/[\b]/g, "\\b").replace(/\t/g, "\\t").replace(/\n/g, "\\n").replace(/\f/g, "\\f").replace(/\r/g, "\\r").replace(/([\u0000-\u001f\u007f])/, (c) => "\\u" + numpad(4, c.codePointAt(0).toString(16))); - } - function stringifyMultilineString(str) { - let escaped = str.split(/\n/).map((str2) => { - return escapeString(str2).replace(/"(?="")/g, '\\"'); - }).join("\n"); - if (escaped.slice(-1) === '"') escaped += "\\\n"; - return '"""\n' + escaped + '"""'; - } - function stringifyAnyInline(value, multilineOk) { - let type = tomlType2(value); - if (type === "string") { - if (multilineOk && /\n/.test(value)) { - type = "string-multiline"; - } else if (!/[\b\t\n\f\r']/.test(value) && /"/.test(value)) { - type = "string-literal"; - } - } - return stringifyInline(value, type); - } - function stringifyInline(value, type) { - if (!type) type = tomlType2(value); - switch (type) { - case "string-multiline": - return stringifyMultilineString(value); - case "string": - return stringifyBasicString(value); - case "string-literal": - return stringifyLiteralString(value); - case "integer": - return stringifyInteger(value); - case "float": - return stringifyFloat(value); - case "boolean": - return stringifyBoolean(value); - case "datetime": - return stringifyDatetime(value); - case "array": - return stringifyInlineArray(value.filter((_) => tomlType2(_) !== "null" && tomlType2(_) !== "undefined" && tomlType2(_) !== "nan")); - case "table": - return stringifyInlineTable(value); - /* istanbul ignore next */ - default: - throw typeError(type); - } - } - function stringifyInteger(value) { - return String(value).replace(/\B(?=(\d{3})+(?!\d))/g, "_"); - } - function stringifyFloat(value) { - if (value === Infinity) { - return "inf"; - } else if (value === -Infinity) { - return "-inf"; - } else if (Object.is(value, NaN)) { - return "nan"; - } else if (Object.is(value, -0)) { - return "-0.0"; - } - var chunks = String(value).split("."); - var int = chunks[0]; - var dec = chunks[1] || 0; - return stringifyInteger(int) + "." + dec; - } - function stringifyBoolean(value) { - return String(value); - } - function stringifyDatetime(value) { - return value.toISOString(); - } - function isNumber(type) { - return type === "float" || type === "integer"; - } - function arrayType(values) { - var contentType = tomlType2(values[0]); - if (values.every((_) => tomlType2(_) === contentType)) return contentType; - if (values.every((_) => isNumber(tomlType2(_)))) return "float"; - return "mixed"; - } - function validateArray(values) { - const type = arrayType(values); - if (type === "mixed") { - throw arrayOneTypeError(); - } - return type; - } - function stringifyInlineArray(values) { - values = toJSON(values); - const type = validateArray(values); - var result = "["; - var stringified = values.map((_) => stringifyInline(_, type)); - if (stringified.join(", ").length > 60 || /\n/.test(stringified)) { - result += "\n " + stringified.join(",\n ") + "\n"; - } else { - result += " " + stringified.join(", ") + (stringified.length > 0 ? " " : ""); - } - return result + "]"; - } - function stringifyInlineTable(value) { - value = toJSON(value); - var result = []; - Object.keys(value).forEach((key) => { - result.push(stringifyKey(key) + " = " + stringifyAnyInline(value[key], false)); - }); - return "{ " + result.join(", ") + (result.length > 0 ? " " : "") + "}"; - } - function stringifyComplex(prefix, indent, key, value) { - var valueType = tomlType2(value); - if (valueType === "array") { - return stringifyArrayOfTables(prefix, indent, key, value); - } else if (valueType === "table") { - return stringifyComplexTable(prefix, indent, key, value); - } else { - throw typeError(valueType); - } - } - function stringifyArrayOfTables(prefix, indent, key, values) { - values = toJSON(values); - validateArray(values); - var firstValueType = tomlType2(values[0]); - if (firstValueType !== "table") throw typeError(firstValueType); - var fullKey = prefix + stringifyKey(key); - var result = ""; - values.forEach((table) => { - if (result.length > 0) result += "\n"; - result += indent + "[[" + fullKey + "]]\n"; - result += stringifyObject(fullKey + ".", indent, table); - }); - return result; - } - function stringifyComplexTable(prefix, indent, key, value) { - var fullKey = prefix + stringifyKey(key); - var result = ""; - if (getInlineKeys(value).length > 0) { - result += indent + "[" + fullKey + "]\n"; - } - return result + stringifyObject(fullKey + ".", indent, value); - } - } - }); - - // node_modules/@iarna/toml/toml.js - var require_toml = __commonJS({ - "node_modules/@iarna/toml/toml.js"(exports2) { - "use strict"; - exports2.parse = require_parse(); - exports2.stringify = require_stringify(); - } - }); - - // src/elemtoml.ts - var import_utility_types = __toESM(require_dist()); - function register_element(name, elem) { - console.debug("Element registered: ", elem); - let tmp_value = elem; - if (tmp_value.namedBehavior) { - const found_behaviour = window.behaviors[tmp_value.namedBehavior]; - if (typeof found_behaviour == "function") { - tmp_value.tick = found_behaviour; - } else { - tmp_value.behavior = found_behaviour; - } - } - window.elements[name] = tmp_value; - } - function register_elements(elems) { - Object.entries(elems).forEach(([key, value]) => { - register_element(key, value); - }); - } - - // src/cfg_loader.ts - var import_toml = __toESM(require_toml()); - - // src/utils.ts - var ScriptRunError = class extends Error { - constructor(message, asserter = void 0) { - super(message); - this.name = "MyError"; - Error.captureStackTrace?.(this, asserter || this.constructor); - } - }; - async function run_script(path) { - try { - let resp = await fetch(path); - const text = await resp.text(); - if (resp.ok) { - const result = Function(text)(); - if (result !== void 0 && result !== 0) { - throw new ScriptRunError(`Script exited with code ${result}`); - } - } else { - throw new ScriptRunError(`Script ${path} not found`); - } - } catch (e) { - throw e; - } - } - - // src/cfg_loader.ts - var Package = class { - /** - * Constructs a new Package instance with the given configuration (as loaded - * from a TOML configuration). - * - * @param config The parsed package configuration. - */ - constructor(config) { - /** - * The list of elements that have been loaded for this package. - */ - this.loaded_elems = []; - this.cfg = config; - console.log(this); - } - /** - * Loads external elements defined in the package configuration. - * Fetches, parses, and registers each element. - * - * @returns A promise that resolves to an `ElementDict` - * @private - */ - async load_elems() { - for (const i of this.cfg.mod.external_elements) { - console.log("loading element:", i); - try { - let resp = await fetch(i.path); - const parsed = (0, import_toml.parse)(await resp.text()); - console.log(parsed); - register_element(i.name, parsed); - } catch (err) { - console.error(err); - } - } - let tmp = {}; - this.loaded_elems.forEach((elem) => { - tmp[elem.name] = elem; - }); - return tmp; - } - /** - * Retrieves the list of elements that have been loaded for this package. - * @returns An array of loaded elements. - */ - get_loaded_elems() { - return this.loaded_elems; - } - /** - * Loads the mod, runs scripts, and registers elements. - */ - load_mod(prompt_quene2) { - const incompatibilities = window.enabledMods.filter((x) => this.cfg.mod.incompatible_mods.includes(x)); - if (incompatibilities.length != 0) { - prompt_quene2.push(() => { - window.promptText( - `A: ${this.cfg.mod.name} - B: ${incompatibilities.join(", ")}`, - () => { - }, - "Mod incompatibility" - ); - }); - return; - } - console.debug(this.cfg.scripts); - if (this.cfg.scripts.preload !== void 0) { - for (const i of this.cfg.scripts.preload) { - run_script(i); - } - } - this.load_elems().then((elems) => { - console.debug("elems:", elems); - register_elements(elems); - }); - if (this.cfg.scripts.postload !== void 0) { - for (const i of this.cfg.scripts.postload) { - run_script(i); - } - } - } - }; - function load(object) { - return object; - } - - // src/mod.ts - var import_toml2 = __toESM(require_toml()); - - // src/mod_finder.ts - function find_mod(name, onfind) { - console.log(name, `${name}/mod.toml`); - fetch(`${name}/mod.toml`).then(async (x) => { - console.log(x.url); - if (x.ok) { - onfind(await x.text()); - } else { - } - }).catch((err) => { - console.error(err); - }); - } - - // src/mod.ts - function shuffle_to_start(input, i) { - let tmp = input.filter((x) => x !== input[i]); - tmp.unshift(input[i]); - return tmp; - } - var prompt_quene = []; - var mods = JSON.parse(localStorage.getItem("enabledMods") || ""); - if (mods[0] !== "mods/fancy_loader.js") { - prompt_quene.push(() => { - window.promptConfirm( - `Refresh again to reload as the first mod (otherwise, there can be odd glitches with dependencies etc.)`, - (x) => { - if (x) { - window.location.reload(); - } - }, - "fancy_loader.js says..." - ); - }); - const shuffled_mods = shuffle_to_start(mods, mods.findIndex((x) => x == "mods/fancy_loader.js")); - localStorage.setItem("enabledMods", JSON.stringify(shuffled_mods)); - } - for (const i of window.enabledMods) { - if (i.endsWith(".toml")) { - console.trace("Loading mod:", i, i.slice(0, -5)); - find_mod(i.slice(0, -5), (text) => { - const parsed = import_toml2.default.parse(text); - console.debug("Parsed mod TOML:", import_toml2.default.parse(text)); - let pkg = new Package(load(parsed)); - pkg.load_mod(prompt_quene); - console.debug("Loaded mod:", pkg); - }); - } - } - window.addEventListener("load", () => { - for (const i of prompt_quene) { - i(); - } - }); -})(); -/*! Bundled license information: - -utility-types/dist/index.js: - (** - * @author Piotr Witek (http://piotrwitek.github.io) - * @copyright Copyright (c) 2016 Piotr Witek - * @license MIT - *) -*/ diff --git a/mods/loader_test/mod.toml b/mods/loader_test/mod.toml deleted file mode 100644 index 586d0a28..00000000 --- a/mods/loader_test/mod.toml +++ /dev/null @@ -1,23 +0,0 @@ -[mod] -name = "test-finder" -version = "0.1.0" -external_elements = [ - {path = "mods/loader_test/test_element.toml", name = "test_element"}, -] - -# Completely arbitrary -incompatible_mods = [ - "mods/nousersthings.js", - "mods/lightmap.js", - "mods/betterSettings.js" -] - -[scripts] -preload = [ - "mods/loader_test/pre_load.js", - "mods/loader_test/random_file.js" -] -postload = [ - "mods/loader_test/post_load.js", - "mods/loader_test/random_file.js" -] \ No newline at end of file diff --git a/mods/loader_test/post_load.js b/mods/loader_test/post_load.js deleted file mode 100644 index 78f2b805..00000000 --- a/mods/loader_test/post_load.js +++ /dev/null @@ -1 +0,0 @@ -console.log("POST LOAD RUNNING") \ No newline at end of file diff --git a/mods/loader_test/pre_load.js b/mods/loader_test/pre_load.js deleted file mode 100644 index 53652f98..00000000 --- a/mods/loader_test/pre_load.js +++ /dev/null @@ -1 +0,0 @@ -console.log("PRELOAD RUNNING") \ No newline at end of file diff --git a/mods/loader_test/random_file.js b/mods/loader_test/random_file.js deleted file mode 100644 index 9589b727..00000000 --- a/mods/loader_test/random_file.js +++ /dev/null @@ -1 +0,0 @@ -console.log("THIS OTHER THING RUNNING") \ No newline at end of file diff --git a/mods/loader_test/test_element.toml b/mods/loader_test/test_element.toml deleted file mode 100644 index 71e49017..00000000 --- a/mods/loader_test/test_element.toml +++ /dev/null @@ -1,14 +0,0 @@ -name = "test_element" -color = ["#FF00FF", "#00FF00"] -state = "solid" -namedBehavior = "POWDER_OLD" -category = "solids" -tempHigh = 100 -stateHigh = "gold" -density = 100 -conduct = 1.0 - -[reactions] -water = {elem = "ash", elem2 = "pop"} -cement = {elem1 = "sand"} -blood = {elem1 = "n_explosion"} \ No newline at end of file From 50d82733f1c3e89afb66053c0edd78de96a690f8 Mon Sep 17 00:00:00 2001 From: Mnem42 Date: Sun, 7 Dec 2025 14:41:21 +0000 Subject: [PATCH 10/38] Forgot about the other one --- mods/fancy-loader.js | 2393 ------------------------------------------ 1 file changed, 2393 deletions(-) delete mode 100644 mods/fancy-loader.js diff --git a/mods/fancy-loader.js b/mods/fancy-loader.js deleted file mode 100644 index 7222d664..00000000 --- a/mods/fancy-loader.js +++ /dev/null @@ -1,2393 +0,0 @@ -"use strict"; -(() => { - var __create = Object.create; - var __defProp = Object.defineProperty; - var __getOwnPropDesc = Object.getOwnPropertyDescriptor; - var __getOwnPropNames = Object.getOwnPropertyNames; - var __getProtoOf = Object.getPrototypeOf; - var __hasOwnProp = Object.prototype.hasOwnProperty; - var __commonJS = (cb, mod) => function __require() { - return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; - }; - var __copyProps = (to, from, except, desc) => { - if (from && typeof from === "object" || typeof from === "function") { - for (let key of __getOwnPropNames(from)) - if (!__hasOwnProp.call(to, key) && key !== except) - __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); - } - return to; - }; - var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( - // If the importer is in node compatibility mode or this is not an ESM - // file that has been converted to a CommonJS file using a Babel- - // compatible transform (i.e. "__esModule" has not been set), then set - // "default" to the CommonJS "module.exports" for node compatibility. - isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, - mod - )); - - // node_modules/utility-types/dist/aliases-and-guards.js - var require_aliases_and_guards = __commonJS({ - "node_modules/utility-types/dist/aliases-and-guards.js"(exports2) { - "use strict"; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.isPrimitive = function(val) { - if (val === null || val === void 0) { - return true; - } - switch (typeof val) { - case "string": - case "number": - case "bigint": - case "boolean": - case "symbol": { - return true; - } - default: - return false; - } - }; - exports2.isFalsy = function(val) { - return !val; - }; - exports2.isNullish = function(val) { - return val == null; - }; - } - }); - - // node_modules/utility-types/dist/functional-helpers.js - var require_functional_helpers = __commonJS({ - "node_modules/utility-types/dist/functional-helpers.js"(exports2) { - "use strict"; - Object.defineProperty(exports2, "__esModule", { value: true }); - function getReturnOfExpression(expression) { - return void 0; - } - exports2.getReturnOfExpression = getReturnOfExpression; - } - }); - - // node_modules/utility-types/dist/index.js - var require_dist = __commonJS({ - "node_modules/utility-types/dist/index.js"(exports2) { - "use strict"; - Object.defineProperty(exports2, "__esModule", { value: true }); - var aliases_and_guards_1 = require_aliases_and_guards(); - exports2.isFalsy = aliases_and_guards_1.isFalsy; - exports2.isNullish = aliases_and_guards_1.isNullish; - exports2.isPrimitive = aliases_and_guards_1.isPrimitive; - var functional_helpers_1 = require_functional_helpers(); - exports2.getReturnOfExpression = functional_helpers_1.getReturnOfExpression; - } - }); - - // node_modules/@iarna/toml/lib/parser.js - var require_parser = __commonJS({ - "node_modules/@iarna/toml/lib/parser.js"(exports2, module2) { - "use strict"; - var ParserEND = 1114112; - var ParserError = class _ParserError extends Error { - /* istanbul ignore next */ - constructor(msg, filename, linenumber) { - super("[ParserError] " + msg, filename, linenumber); - this.name = "ParserError"; - this.code = "ParserError"; - if (Error.captureStackTrace) - Error.captureStackTrace(this, _ParserError); - } - }; - var State = class { - constructor(parser) { - this.parser = parser; - this.buf = ""; - this.returned = null; - this.result = null; - this.resultTable = null; - this.resultArr = null; - } - }; - var Parser = class { - constructor() { - this.pos = 0; - this.col = 0; - this.line = 0; - this.obj = {}; - this.ctx = this.obj; - this.stack = []; - this._buf = ""; - this.char = null; - this.ii = 0; - this.state = new State(this.parseStart); - } - parse(str) { - if (str.length === 0 || str.length == null) - return; - this._buf = String(str); - this.ii = -1; - this.char = -1; - let getNext; - while (getNext === false || this.nextChar()) { - getNext = this.runOne(); - } - this._buf = null; - } - nextChar() { - if (this.char === 10) { - ++this.line; - this.col = -1; - } - ++this.ii; - this.char = this._buf.codePointAt(this.ii); - ++this.pos; - ++this.col; - return this.haveBuffer(); - } - haveBuffer() { - return this.ii < this._buf.length; - } - runOne() { - return this.state.parser.call(this, this.state.returned); - } - finish() { - this.char = ParserEND; - let last; - do { - last = this.state.parser; - this.runOne(); - } while (this.state.parser !== last); - this.ctx = null; - this.state = null; - this._buf = null; - return this.obj; - } - next(fn) { - if (typeof fn !== "function") - throw new ParserError("Tried to set state to non-existent state: " + JSON.stringify(fn)); - this.state.parser = fn; - } - goto(fn) { - this.next(fn); - return this.runOne(); - } - call(fn, returnWith) { - if (returnWith) - this.next(returnWith); - this.stack.push(this.state); - this.state = new State(fn); - } - callNow(fn, returnWith) { - this.call(fn, returnWith); - return this.runOne(); - } - return(value) { - if (this.stack.length === 0) - throw this.error(new ParserError("Stack underflow")); - if (value === void 0) - value = this.state.buf; - this.state = this.stack.pop(); - this.state.returned = value; - } - returnNow(value) { - this.return(value); - return this.runOne(); - } - consume() { - if (this.char === ParserEND) - throw this.error(new ParserError("Unexpected end-of-buffer")); - this.state.buf += this._buf[this.ii]; - } - error(err) { - err.line = this.line; - err.col = this.col; - err.pos = this.pos; - return err; - } - /* istanbul ignore next */ - parseStart() { - throw new ParserError("Must declare a parseStart method"); - } - }; - Parser.END = ParserEND; - Parser.Error = ParserError; - module2.exports = Parser; - } - }); - - // node_modules/@iarna/toml/lib/create-datetime.js - var require_create_datetime = __commonJS({ - "node_modules/@iarna/toml/lib/create-datetime.js"(exports2, module2) { - "use strict"; - module2.exports = (value) => { - const date = new Date(value); - if (isNaN(date)) { - throw new TypeError("Invalid Datetime"); - } else { - return date; - } - }; - } - }); - - // node_modules/@iarna/toml/lib/format-num.js - var require_format_num = __commonJS({ - "node_modules/@iarna/toml/lib/format-num.js"(exports2, module2) { - "use strict"; - module2.exports = (d, num) => { - num = String(num); - while (num.length < d) - num = "0" + num; - return num; - }; - } - }); - - // node_modules/@iarna/toml/lib/create-datetime-float.js - var require_create_datetime_float = __commonJS({ - "node_modules/@iarna/toml/lib/create-datetime-float.js"(exports2, module2) { - "use strict"; - var f = require_format_num(); - var FloatingDateTime = class extends Date { - constructor(value) { - super(value + "Z"); - this.isFloating = true; - } - toISOString() { - const date = `${this.getUTCFullYear()}-${f(2, this.getUTCMonth() + 1)}-${f(2, this.getUTCDate())}`; - const time = `${f(2, this.getUTCHours())}:${f(2, this.getUTCMinutes())}:${f(2, this.getUTCSeconds())}.${f(3, this.getUTCMilliseconds())}`; - return `${date}T${time}`; - } - }; - module2.exports = (value) => { - const date = new FloatingDateTime(value); - if (isNaN(date)) { - throw new TypeError("Invalid Datetime"); - } else { - return date; - } - }; - } - }); - - // node_modules/@iarna/toml/lib/create-date.js - var require_create_date = __commonJS({ - "node_modules/@iarna/toml/lib/create-date.js"(exports2, module2) { - "use strict"; - var f = require_format_num(); - var DateTime = globalThis.Date; - var Date2 = class extends DateTime { - constructor(value) { - super(value); - this.isDate = true; - } - toISOString() { - return `${this.getUTCFullYear()}-${f(2, this.getUTCMonth() + 1)}-${f(2, this.getUTCDate())}`; - } - }; - module2.exports = (value) => { - const date = new Date2(value); - if (isNaN(date)) { - throw new TypeError("Invalid Datetime"); - } else { - return date; - } - }; - } - }); - - // node_modules/@iarna/toml/lib/create-time.js - var require_create_time = __commonJS({ - "node_modules/@iarna/toml/lib/create-time.js"(exports2, module2) { - "use strict"; - var f = require_format_num(); - var Time = class extends Date { - constructor(value) { - super(`0000-01-01T${value}Z`); - this.isTime = true; - } - toISOString() { - return `${f(2, this.getUTCHours())}:${f(2, this.getUTCMinutes())}:${f(2, this.getUTCSeconds())}.${f(3, this.getUTCMilliseconds())}`; - } - }; - module2.exports = (value) => { - const date = new Time(value); - if (isNaN(date)) { - throw new TypeError("Invalid Datetime"); - } else { - return date; - } - }; - } - }); - - // node_modules/@iarna/toml/lib/toml-parser.js - var require_toml_parser = __commonJS({ - "node_modules/@iarna/toml/lib/toml-parser.js"(exports, module) { - "use strict"; - module.exports = makeParserClass(require_parser()); - module.exports.makeParserClass = makeParserClass; - var TomlError = class _TomlError extends Error { - constructor(msg) { - super(msg); - this.name = "TomlError"; - if (Error.captureStackTrace) - Error.captureStackTrace(this, _TomlError); - this.fromTOML = true; - this.wrapped = null; - } - }; - TomlError.wrap = (err) => { - const terr = new TomlError(err.message); - terr.code = err.code; - terr.wrapped = err; - return terr; - }; - module.exports.TomlError = TomlError; - var createDateTime = require_create_datetime(); - var createDateTimeFloat = require_create_datetime_float(); - var createDate = require_create_date(); - var createTime = require_create_time(); - var CTRL_I = 9; - var CTRL_J = 10; - var CTRL_M = 13; - var CTRL_CHAR_BOUNDARY = 31; - var CHAR_SP = 32; - var CHAR_QUOT = 34; - var CHAR_NUM = 35; - var CHAR_APOS = 39; - var CHAR_PLUS = 43; - var CHAR_COMMA = 44; - var CHAR_HYPHEN = 45; - var CHAR_PERIOD = 46; - var CHAR_0 = 48; - var CHAR_1 = 49; - var CHAR_7 = 55; - var CHAR_9 = 57; - var CHAR_COLON = 58; - var CHAR_EQUALS = 61; - var CHAR_A = 65; - var CHAR_E = 69; - var CHAR_F = 70; - var CHAR_T = 84; - var CHAR_U = 85; - var CHAR_Z = 90; - var CHAR_LOWBAR = 95; - var CHAR_a = 97; - var CHAR_b = 98; - var CHAR_e = 101; - var CHAR_f = 102; - var CHAR_i = 105; - var CHAR_l = 108; - var CHAR_n = 110; - var CHAR_o = 111; - var CHAR_r = 114; - var CHAR_s = 115; - var CHAR_t = 116; - var CHAR_u = 117; - var CHAR_x = 120; - var CHAR_z = 122; - var CHAR_LCUB = 123; - var CHAR_RCUB = 125; - var CHAR_LSQB = 91; - var CHAR_BSOL = 92; - var CHAR_RSQB = 93; - var CHAR_DEL = 127; - var SURROGATE_FIRST = 55296; - var SURROGATE_LAST = 57343; - var escapes = { - [CHAR_b]: "\b", - [CHAR_t]: " ", - [CHAR_n]: "\n", - [CHAR_f]: "\f", - [CHAR_r]: "\r", - [CHAR_QUOT]: '"', - [CHAR_BSOL]: "\\" - }; - function isDigit(cp) { - return cp >= CHAR_0 && cp <= CHAR_9; - } - function isHexit(cp) { - return cp >= CHAR_A && cp <= CHAR_F || cp >= CHAR_a && cp <= CHAR_f || cp >= CHAR_0 && cp <= CHAR_9; - } - function isBit(cp) { - return cp === CHAR_1 || cp === CHAR_0; - } - function isOctit(cp) { - return cp >= CHAR_0 && cp <= CHAR_7; - } - function isAlphaNumQuoteHyphen(cp) { - return cp >= CHAR_A && cp <= CHAR_Z || cp >= CHAR_a && cp <= CHAR_z || cp >= CHAR_0 && cp <= CHAR_9 || cp === CHAR_APOS || cp === CHAR_QUOT || cp === CHAR_LOWBAR || cp === CHAR_HYPHEN; - } - function isAlphaNumHyphen(cp) { - return cp >= CHAR_A && cp <= CHAR_Z || cp >= CHAR_a && cp <= CHAR_z || cp >= CHAR_0 && cp <= CHAR_9 || cp === CHAR_LOWBAR || cp === CHAR_HYPHEN; - } - var _type = Symbol("type"); - var _declared = Symbol("declared"); - var hasOwnProperty = Object.prototype.hasOwnProperty; - var defineProperty = Object.defineProperty; - var descriptor = { configurable: true, enumerable: true, writable: true, value: void 0 }; - function hasKey(obj, key) { - if (hasOwnProperty.call(obj, key)) - return true; - if (key === "__proto__") - defineProperty(obj, "__proto__", descriptor); - return false; - } - var INLINE_TABLE = Symbol("inline-table"); - function InlineTable() { - return Object.defineProperties({}, { - [_type]: { value: INLINE_TABLE } - }); - } - function isInlineTable(obj) { - if (obj === null || typeof obj !== "object") - return false; - return obj[_type] === INLINE_TABLE; - } - var TABLE = Symbol("table"); - function Table() { - return Object.defineProperties({}, { - [_type]: { value: TABLE }, - [_declared]: { value: false, writable: true } - }); - } - function isTable(obj) { - if (obj === null || typeof obj !== "object") - return false; - return obj[_type] === TABLE; - } - var _contentType = Symbol("content-type"); - var INLINE_LIST = Symbol("inline-list"); - function InlineList(type) { - return Object.defineProperties([], { - [_type]: { value: INLINE_LIST }, - [_contentType]: { value: type } - }); - } - function isInlineList(obj) { - if (obj === null || typeof obj !== "object") - return false; - return obj[_type] === INLINE_LIST; - } - var LIST = Symbol("list"); - function List() { - return Object.defineProperties([], { - [_type]: { value: LIST } - }); - } - function isList(obj) { - if (obj === null || typeof obj !== "object") - return false; - return obj[_type] === LIST; - } - var _custom; - try { - const utilInspect = eval("require('util').inspect"); - _custom = utilInspect.custom; - } catch (_) { - } - var _inspect = _custom || "inspect"; - var BoxedBigInt = class { - constructor(value) { - try { - this.value = globalThis.BigInt.asIntN(64, value); - } catch (_) { - this.value = null; - } - Object.defineProperty(this, _type, { value: INTEGER }); - } - isNaN() { - return this.value === null; - } - /* istanbul ignore next */ - toString() { - return String(this.value); - } - /* istanbul ignore next */ - [_inspect]() { - return `[BigInt: ${this.toString()}]}`; - } - valueOf() { - return this.value; - } - }; - var INTEGER = Symbol("integer"); - function Integer(value) { - let num = Number(value); - if (Object.is(num, -0)) - num = 0; - if (globalThis.BigInt && !Number.isSafeInteger(num)) { - return new BoxedBigInt(value); - } else { - return Object.defineProperties(new Number(num), { - isNaN: { value: function() { - return isNaN(this); - } }, - [_type]: { value: INTEGER }, - [_inspect]: { value: () => `[Integer: ${value}]` } - }); - } - } - function isInteger(obj) { - if (obj === null || typeof obj !== "object") - return false; - return obj[_type] === INTEGER; - } - var FLOAT = Symbol("float"); - function Float(value) { - return Object.defineProperties(new Number(value), { - [_type]: { value: FLOAT }, - [_inspect]: { value: () => `[Float: ${value}]` } - }); - } - function isFloat(obj) { - if (obj === null || typeof obj !== "object") - return false; - return obj[_type] === FLOAT; - } - function tomlType(value) { - const type = typeof value; - if (type === "object") { - if (value === null) - return "null"; - if (value instanceof Date) - return "datetime"; - if (_type in value) { - switch (value[_type]) { - case INLINE_TABLE: - return "inline-table"; - case INLINE_LIST: - return "inline-list"; - case TABLE: - return "table"; - case LIST: - return "list"; - case FLOAT: - return "float"; - case INTEGER: - return "integer"; - } - } - } - return type; - } - function makeParserClass(Parser) { - class TOMLParser extends Parser { - constructor() { - super(); - this.ctx = this.obj = Table(); - } - /* MATCH HELPER */ - atEndOfWord() { - return this.char === CHAR_NUM || this.char === CTRL_I || this.char === CHAR_SP || this.atEndOfLine(); - } - atEndOfLine() { - return this.char === Parser.END || this.char === CTRL_J || this.char === CTRL_M; - } - parseStart() { - if (this.char === Parser.END) { - return null; - } else if (this.char === CHAR_LSQB) { - return this.call(this.parseTableOrList); - } else if (this.char === CHAR_NUM) { - return this.call(this.parseComment); - } else if (this.char === CTRL_J || this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M) { - return null; - } else if (isAlphaNumQuoteHyphen(this.char)) { - return this.callNow(this.parseAssignStatement); - } else { - throw this.error(new TomlError(`Unknown character "${this.char}"`)); - } - } - // HELPER, this strips any whitespace and comments to the end of the line - // then RETURNS. Last state in a production. - parseWhitespaceToEOL() { - if (this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M) { - return null; - } else if (this.char === CHAR_NUM) { - return this.goto(this.parseComment); - } else if (this.char === Parser.END || this.char === CTRL_J) { - return this.return(); - } else { - throw this.error(new TomlError("Unexpected character, expected only whitespace or comments till end of line")); - } - } - /* ASSIGNMENT: key = value */ - parseAssignStatement() { - return this.callNow(this.parseAssign, this.recordAssignStatement); - } - recordAssignStatement(kv) { - let target = this.ctx; - let finalKey = kv.key.pop(); - for (let kw of kv.key) { - if (hasKey(target, kw) && (!isTable(target[kw]) || target[kw][_declared])) { - throw this.error(new TomlError("Can't redefine existing key")); - } - target = target[kw] = target[kw] || Table(); - } - if (hasKey(target, finalKey)) { - throw this.error(new TomlError("Can't redefine existing key")); - } - if (isInteger(kv.value) || isFloat(kv.value)) { - target[finalKey] = kv.value.valueOf(); - } else { - target[finalKey] = kv.value; - } - return this.goto(this.parseWhitespaceToEOL); - } - /* ASSSIGNMENT expression, key = value possibly inside an inline table */ - parseAssign() { - return this.callNow(this.parseKeyword, this.recordAssignKeyword); - } - recordAssignKeyword(key) { - if (this.state.resultTable) { - this.state.resultTable.push(key); - } else { - this.state.resultTable = [key]; - } - return this.goto(this.parseAssignKeywordPreDot); - } - parseAssignKeywordPreDot() { - if (this.char === CHAR_PERIOD) { - return this.next(this.parseAssignKeywordPostDot); - } else if (this.char !== CHAR_SP && this.char !== CTRL_I) { - return this.goto(this.parseAssignEqual); - } - } - parseAssignKeywordPostDot() { - if (this.char !== CHAR_SP && this.char !== CTRL_I) { - return this.callNow(this.parseKeyword, this.recordAssignKeyword); - } - } - parseAssignEqual() { - if (this.char === CHAR_EQUALS) { - return this.next(this.parseAssignPreValue); - } else { - throw this.error(new TomlError('Invalid character, expected "="')); - } - } - parseAssignPreValue() { - if (this.char === CHAR_SP || this.char === CTRL_I) { - return null; - } else { - return this.callNow(this.parseValue, this.recordAssignValue); - } - } - recordAssignValue(value) { - return this.returnNow({ key: this.state.resultTable, value }); - } - /* COMMENTS: #...eol */ - parseComment() { - do { - if (this.char === Parser.END || this.char === CTRL_J) { - return this.return(); - } - } while (this.nextChar()); - } - /* TABLES AND LISTS, [foo] and [[foo]] */ - parseTableOrList() { - if (this.char === CHAR_LSQB) { - this.next(this.parseList); - } else { - return this.goto(this.parseTable); - } - } - /* TABLE [foo.bar.baz] */ - parseTable() { - this.ctx = this.obj; - return this.goto(this.parseTableNext); - } - parseTableNext() { - if (this.char === CHAR_SP || this.char === CTRL_I) { - return null; - } else { - return this.callNow(this.parseKeyword, this.parseTableMore); - } - } - parseTableMore(keyword) { - if (this.char === CHAR_SP || this.char === CTRL_I) { - return null; - } else if (this.char === CHAR_RSQB) { - if (hasKey(this.ctx, keyword) && (!isTable(this.ctx[keyword]) || this.ctx[keyword][_declared])) { - throw this.error(new TomlError("Can't redefine existing key")); - } else { - this.ctx = this.ctx[keyword] = this.ctx[keyword] || Table(); - this.ctx[_declared] = true; - } - return this.next(this.parseWhitespaceToEOL); - } else if (this.char === CHAR_PERIOD) { - if (!hasKey(this.ctx, keyword)) { - this.ctx = this.ctx[keyword] = Table(); - } else if (isTable(this.ctx[keyword])) { - this.ctx = this.ctx[keyword]; - } else if (isList(this.ctx[keyword])) { - this.ctx = this.ctx[keyword][this.ctx[keyword].length - 1]; - } else { - throw this.error(new TomlError("Can't redefine existing key")); - } - return this.next(this.parseTableNext); - } else { - throw this.error(new TomlError("Unexpected character, expected whitespace, . or ]")); - } - } - /* LIST [[a.b.c]] */ - parseList() { - this.ctx = this.obj; - return this.goto(this.parseListNext); - } - parseListNext() { - if (this.char === CHAR_SP || this.char === CTRL_I) { - return null; - } else { - return this.callNow(this.parseKeyword, this.parseListMore); - } - } - parseListMore(keyword) { - if (this.char === CHAR_SP || this.char === CTRL_I) { - return null; - } else if (this.char === CHAR_RSQB) { - if (!hasKey(this.ctx, keyword)) { - this.ctx[keyword] = List(); - } - if (isInlineList(this.ctx[keyword])) { - throw this.error(new TomlError("Can't extend an inline array")); - } else if (isList(this.ctx[keyword])) { - const next = Table(); - this.ctx[keyword].push(next); - this.ctx = next; - } else { - throw this.error(new TomlError("Can't redefine an existing key")); - } - return this.next(this.parseListEnd); - } else if (this.char === CHAR_PERIOD) { - if (!hasKey(this.ctx, keyword)) { - this.ctx = this.ctx[keyword] = Table(); - } else if (isInlineList(this.ctx[keyword])) { - throw this.error(new TomlError("Can't extend an inline array")); - } else if (isInlineTable(this.ctx[keyword])) { - throw this.error(new TomlError("Can't extend an inline table")); - } else if (isList(this.ctx[keyword])) { - this.ctx = this.ctx[keyword][this.ctx[keyword].length - 1]; - } else if (isTable(this.ctx[keyword])) { - this.ctx = this.ctx[keyword]; - } else { - throw this.error(new TomlError("Can't redefine an existing key")); - } - return this.next(this.parseListNext); - } else { - throw this.error(new TomlError("Unexpected character, expected whitespace, . or ]")); - } - } - parseListEnd(keyword) { - if (this.char === CHAR_RSQB) { - return this.next(this.parseWhitespaceToEOL); - } else { - throw this.error(new TomlError("Unexpected character, expected whitespace, . or ]")); - } - } - /* VALUE string, number, boolean, inline list, inline object */ - parseValue() { - if (this.char === Parser.END) { - throw this.error(new TomlError("Key without value")); - } else if (this.char === CHAR_QUOT) { - return this.next(this.parseDoubleString); - } - if (this.char === CHAR_APOS) { - return this.next(this.parseSingleString); - } else if (this.char === CHAR_HYPHEN || this.char === CHAR_PLUS) { - return this.goto(this.parseNumberSign); - } else if (this.char === CHAR_i) { - return this.next(this.parseInf); - } else if (this.char === CHAR_n) { - return this.next(this.parseNan); - } else if (isDigit(this.char)) { - return this.goto(this.parseNumberOrDateTime); - } else if (this.char === CHAR_t || this.char === CHAR_f) { - return this.goto(this.parseBoolean); - } else if (this.char === CHAR_LSQB) { - return this.call(this.parseInlineList, this.recordValue); - } else if (this.char === CHAR_LCUB) { - return this.call(this.parseInlineTable, this.recordValue); - } else { - throw this.error(new TomlError("Unexpected character, expecting string, number, datetime, boolean, inline array or inline table")); - } - } - recordValue(value) { - return this.returnNow(value); - } - parseInf() { - if (this.char === CHAR_n) { - return this.next(this.parseInf2); - } else { - throw this.error(new TomlError('Unexpected character, expected "inf", "+inf" or "-inf"')); - } - } - parseInf2() { - if (this.char === CHAR_f) { - if (this.state.buf === "-") { - return this.return(-Infinity); - } else { - return this.return(Infinity); - } - } else { - throw this.error(new TomlError('Unexpected character, expected "inf", "+inf" or "-inf"')); - } - } - parseNan() { - if (this.char === CHAR_a) { - return this.next(this.parseNan2); - } else { - throw this.error(new TomlError('Unexpected character, expected "nan"')); - } - } - parseNan2() { - if (this.char === CHAR_n) { - return this.return(NaN); - } else { - throw this.error(new TomlError('Unexpected character, expected "nan"')); - } - } - /* KEYS, barewords or basic, literal, or dotted */ - parseKeyword() { - if (this.char === CHAR_QUOT) { - return this.next(this.parseBasicString); - } else if (this.char === CHAR_APOS) { - return this.next(this.parseLiteralString); - } else { - return this.goto(this.parseBareKey); - } - } - /* KEYS: barewords */ - parseBareKey() { - do { - if (this.char === Parser.END) { - throw this.error(new TomlError("Key ended without value")); - } else if (isAlphaNumHyphen(this.char)) { - this.consume(); - } else if (this.state.buf.length === 0) { - throw this.error(new TomlError("Empty bare keys are not allowed")); - } else { - return this.returnNow(); - } - } while (this.nextChar()); - } - /* STRINGS, single quoted (literal) */ - parseSingleString() { - if (this.char === CHAR_APOS) { - return this.next(this.parseLiteralMultiStringMaybe); - } else { - return this.goto(this.parseLiteralString); - } - } - parseLiteralString() { - do { - if (this.char === CHAR_APOS) { - return this.return(); - } else if (this.atEndOfLine()) { - throw this.error(new TomlError("Unterminated string")); - } else if (this.char === CHAR_DEL || this.char <= CTRL_CHAR_BOUNDARY && this.char !== CTRL_I) { - throw this.errorControlCharInString(); - } else { - this.consume(); - } - } while (this.nextChar()); - } - parseLiteralMultiStringMaybe() { - if (this.char === CHAR_APOS) { - return this.next(this.parseLiteralMultiString); - } else { - return this.returnNow(); - } - } - parseLiteralMultiString() { - if (this.char === CTRL_M) { - return null; - } else if (this.char === CTRL_J) { - return this.next(this.parseLiteralMultiStringContent); - } else { - return this.goto(this.parseLiteralMultiStringContent); - } - } - parseLiteralMultiStringContent() { - do { - if (this.char === CHAR_APOS) { - return this.next(this.parseLiteralMultiEnd); - } else if (this.char === Parser.END) { - throw this.error(new TomlError("Unterminated multi-line string")); - } else if (this.char === CHAR_DEL || this.char <= CTRL_CHAR_BOUNDARY && this.char !== CTRL_I && this.char !== CTRL_J && this.char !== CTRL_M) { - throw this.errorControlCharInString(); - } else { - this.consume(); - } - } while (this.nextChar()); - } - parseLiteralMultiEnd() { - if (this.char === CHAR_APOS) { - return this.next(this.parseLiteralMultiEnd2); - } else { - this.state.buf += "'"; - return this.goto(this.parseLiteralMultiStringContent); - } - } - parseLiteralMultiEnd2() { - if (this.char === CHAR_APOS) { - return this.return(); - } else { - this.state.buf += "''"; - return this.goto(this.parseLiteralMultiStringContent); - } - } - /* STRINGS double quoted */ - parseDoubleString() { - if (this.char === CHAR_QUOT) { - return this.next(this.parseMultiStringMaybe); - } else { - return this.goto(this.parseBasicString); - } - } - parseBasicString() { - do { - if (this.char === CHAR_BSOL) { - return this.call(this.parseEscape, this.recordEscapeReplacement); - } else if (this.char === CHAR_QUOT) { - return this.return(); - } else if (this.atEndOfLine()) { - throw this.error(new TomlError("Unterminated string")); - } else if (this.char === CHAR_DEL || this.char <= CTRL_CHAR_BOUNDARY && this.char !== CTRL_I) { - throw this.errorControlCharInString(); - } else { - this.consume(); - } - } while (this.nextChar()); - } - recordEscapeReplacement(replacement) { - this.state.buf += replacement; - return this.goto(this.parseBasicString); - } - parseMultiStringMaybe() { - if (this.char === CHAR_QUOT) { - return this.next(this.parseMultiString); - } else { - return this.returnNow(); - } - } - parseMultiString() { - if (this.char === CTRL_M) { - return null; - } else if (this.char === CTRL_J) { - return this.next(this.parseMultiStringContent); - } else { - return this.goto(this.parseMultiStringContent); - } - } - parseMultiStringContent() { - do { - if (this.char === CHAR_BSOL) { - return this.call(this.parseMultiEscape, this.recordMultiEscapeReplacement); - } else if (this.char === CHAR_QUOT) { - return this.next(this.parseMultiEnd); - } else if (this.char === Parser.END) { - throw this.error(new TomlError("Unterminated multi-line string")); - } else if (this.char === CHAR_DEL || this.char <= CTRL_CHAR_BOUNDARY && this.char !== CTRL_I && this.char !== CTRL_J && this.char !== CTRL_M) { - throw this.errorControlCharInString(); - } else { - this.consume(); - } - } while (this.nextChar()); - } - errorControlCharInString() { - let displayCode = "\\u00"; - if (this.char < 16) { - displayCode += "0"; - } - displayCode += this.char.toString(16); - return this.error(new TomlError(`Control characters (codes < 0x1f and 0x7f) are not allowed in strings, use ${displayCode} instead`)); - } - recordMultiEscapeReplacement(replacement) { - this.state.buf += replacement; - return this.goto(this.parseMultiStringContent); - } - parseMultiEnd() { - if (this.char === CHAR_QUOT) { - return this.next(this.parseMultiEnd2); - } else { - this.state.buf += '"'; - return this.goto(this.parseMultiStringContent); - } - } - parseMultiEnd2() { - if (this.char === CHAR_QUOT) { - return this.return(); - } else { - this.state.buf += '""'; - return this.goto(this.parseMultiStringContent); - } - } - parseMultiEscape() { - if (this.char === CTRL_M || this.char === CTRL_J) { - return this.next(this.parseMultiTrim); - } else if (this.char === CHAR_SP || this.char === CTRL_I) { - return this.next(this.parsePreMultiTrim); - } else { - return this.goto(this.parseEscape); - } - } - parsePreMultiTrim() { - if (this.char === CHAR_SP || this.char === CTRL_I) { - return null; - } else if (this.char === CTRL_M || this.char === CTRL_J) { - return this.next(this.parseMultiTrim); - } else { - throw this.error(new TomlError("Can't escape whitespace")); - } - } - parseMultiTrim() { - if (this.char === CTRL_J || this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M) { - return null; - } else { - return this.returnNow(); - } - } - parseEscape() { - if (this.char in escapes) { - return this.return(escapes[this.char]); - } else if (this.char === CHAR_u) { - return this.call(this.parseSmallUnicode, this.parseUnicodeReturn); - } else if (this.char === CHAR_U) { - return this.call(this.parseLargeUnicode, this.parseUnicodeReturn); - } else { - throw this.error(new TomlError("Unknown escape character: " + this.char)); - } - } - parseUnicodeReturn(char) { - try { - const codePoint = parseInt(char, 16); - if (codePoint >= SURROGATE_FIRST && codePoint <= SURROGATE_LAST) { - throw this.error(new TomlError("Invalid unicode, character in range 0xD800 - 0xDFFF is reserved")); - } - return this.returnNow(String.fromCodePoint(codePoint)); - } catch (err) { - throw this.error(TomlError.wrap(err)); - } - } - parseSmallUnicode() { - if (!isHexit(this.char)) { - throw this.error(new TomlError("Invalid character in unicode sequence, expected hex")); - } else { - this.consume(); - if (this.state.buf.length >= 4) - return this.return(); - } - } - parseLargeUnicode() { - if (!isHexit(this.char)) { - throw this.error(new TomlError("Invalid character in unicode sequence, expected hex")); - } else { - this.consume(); - if (this.state.buf.length >= 8) - return this.return(); - } - } - /* NUMBERS */ - parseNumberSign() { - this.consume(); - return this.next(this.parseMaybeSignedInfOrNan); - } - parseMaybeSignedInfOrNan() { - if (this.char === CHAR_i) { - return this.next(this.parseInf); - } else if (this.char === CHAR_n) { - return this.next(this.parseNan); - } else { - return this.callNow(this.parseNoUnder, this.parseNumberIntegerStart); - } - } - parseNumberIntegerStart() { - if (this.char === CHAR_0) { - this.consume(); - return this.next(this.parseNumberIntegerExponentOrDecimal); - } else { - return this.goto(this.parseNumberInteger); - } - } - parseNumberIntegerExponentOrDecimal() { - if (this.char === CHAR_PERIOD) { - this.consume(); - return this.call(this.parseNoUnder, this.parseNumberFloat); - } else if (this.char === CHAR_E || this.char === CHAR_e) { - this.consume(); - return this.next(this.parseNumberExponentSign); - } else { - return this.returnNow(Integer(this.state.buf)); - } - } - parseNumberInteger() { - if (isDigit(this.char)) { - this.consume(); - } else if (this.char === CHAR_LOWBAR) { - return this.call(this.parseNoUnder); - } else if (this.char === CHAR_E || this.char === CHAR_e) { - this.consume(); - return this.next(this.parseNumberExponentSign); - } else if (this.char === CHAR_PERIOD) { - this.consume(); - return this.call(this.parseNoUnder, this.parseNumberFloat); - } else { - const result = Integer(this.state.buf); - if (result.isNaN()) { - throw this.error(new TomlError("Invalid number")); - } else { - return this.returnNow(result); - } - } - } - parseNoUnder() { - if (this.char === CHAR_LOWBAR || this.char === CHAR_PERIOD || this.char === CHAR_E || this.char === CHAR_e) { - throw this.error(new TomlError("Unexpected character, expected digit")); - } else if (this.atEndOfWord()) { - throw this.error(new TomlError("Incomplete number")); - } - return this.returnNow(); - } - parseNoUnderHexOctBinLiteral() { - if (this.char === CHAR_LOWBAR || this.char === CHAR_PERIOD) { - throw this.error(new TomlError("Unexpected character, expected digit")); - } else if (this.atEndOfWord()) { - throw this.error(new TomlError("Incomplete number")); - } - return this.returnNow(); - } - parseNumberFloat() { - if (this.char === CHAR_LOWBAR) { - return this.call(this.parseNoUnder, this.parseNumberFloat); - } else if (isDigit(this.char)) { - this.consume(); - } else if (this.char === CHAR_E || this.char === CHAR_e) { - this.consume(); - return this.next(this.parseNumberExponentSign); - } else { - return this.returnNow(Float(this.state.buf)); - } - } - parseNumberExponentSign() { - if (isDigit(this.char)) { - return this.goto(this.parseNumberExponent); - } else if (this.char === CHAR_HYPHEN || this.char === CHAR_PLUS) { - this.consume(); - this.call(this.parseNoUnder, this.parseNumberExponent); - } else { - throw this.error(new TomlError("Unexpected character, expected -, + or digit")); - } - } - parseNumberExponent() { - if (isDigit(this.char)) { - this.consume(); - } else if (this.char === CHAR_LOWBAR) { - return this.call(this.parseNoUnder); - } else { - return this.returnNow(Float(this.state.buf)); - } - } - /* NUMBERS or DATETIMES */ - parseNumberOrDateTime() { - if (this.char === CHAR_0) { - this.consume(); - return this.next(this.parseNumberBaseOrDateTime); - } else { - return this.goto(this.parseNumberOrDateTimeOnly); - } - } - parseNumberOrDateTimeOnly() { - if (this.char === CHAR_LOWBAR) { - return this.call(this.parseNoUnder, this.parseNumberInteger); - } else if (isDigit(this.char)) { - this.consume(); - if (this.state.buf.length > 4) - this.next(this.parseNumberInteger); - } else if (this.char === CHAR_E || this.char === CHAR_e) { - this.consume(); - return this.next(this.parseNumberExponentSign); - } else if (this.char === CHAR_PERIOD) { - this.consume(); - return this.call(this.parseNoUnder, this.parseNumberFloat); - } else if (this.char === CHAR_HYPHEN) { - return this.goto(this.parseDateTime); - } else if (this.char === CHAR_COLON) { - return this.goto(this.parseOnlyTimeHour); - } else { - return this.returnNow(Integer(this.state.buf)); - } - } - parseDateTimeOnly() { - if (this.state.buf.length < 4) { - if (isDigit(this.char)) { - return this.consume(); - } else if (this.char === CHAR_COLON) { - return this.goto(this.parseOnlyTimeHour); - } else { - throw this.error(new TomlError("Expected digit while parsing year part of a date")); - } - } else { - if (this.char === CHAR_HYPHEN) { - return this.goto(this.parseDateTime); - } else { - throw this.error(new TomlError("Expected hyphen (-) while parsing year part of date")); - } - } - } - parseNumberBaseOrDateTime() { - if (this.char === CHAR_b) { - this.consume(); - return this.call(this.parseNoUnderHexOctBinLiteral, this.parseIntegerBin); - } else if (this.char === CHAR_o) { - this.consume(); - return this.call(this.parseNoUnderHexOctBinLiteral, this.parseIntegerOct); - } else if (this.char === CHAR_x) { - this.consume(); - return this.call(this.parseNoUnderHexOctBinLiteral, this.parseIntegerHex); - } else if (this.char === CHAR_PERIOD) { - return this.goto(this.parseNumberInteger); - } else if (isDigit(this.char)) { - return this.goto(this.parseDateTimeOnly); - } else { - return this.returnNow(Integer(this.state.buf)); - } - } - parseIntegerHex() { - if (isHexit(this.char)) { - this.consume(); - } else if (this.char === CHAR_LOWBAR) { - return this.call(this.parseNoUnderHexOctBinLiteral); - } else { - const result = Integer(this.state.buf); - if (result.isNaN()) { - throw this.error(new TomlError("Invalid number")); - } else { - return this.returnNow(result); - } - } - } - parseIntegerOct() { - if (isOctit(this.char)) { - this.consume(); - } else if (this.char === CHAR_LOWBAR) { - return this.call(this.parseNoUnderHexOctBinLiteral); - } else { - const result = Integer(this.state.buf); - if (result.isNaN()) { - throw this.error(new TomlError("Invalid number")); - } else { - return this.returnNow(result); - } - } - } - parseIntegerBin() { - if (isBit(this.char)) { - this.consume(); - } else if (this.char === CHAR_LOWBAR) { - return this.call(this.parseNoUnderHexOctBinLiteral); - } else { - const result = Integer(this.state.buf); - if (result.isNaN()) { - throw this.error(new TomlError("Invalid number")); - } else { - return this.returnNow(result); - } - } - } - /* DATETIME */ - parseDateTime() { - if (this.state.buf.length < 4) { - throw this.error(new TomlError("Years less than 1000 must be zero padded to four characters")); - } - this.state.result = this.state.buf; - this.state.buf = ""; - return this.next(this.parseDateMonth); - } - parseDateMonth() { - if (this.char === CHAR_HYPHEN) { - if (this.state.buf.length < 2) { - throw this.error(new TomlError("Months less than 10 must be zero padded to two characters")); - } - this.state.result += "-" + this.state.buf; - this.state.buf = ""; - return this.next(this.parseDateDay); - } else if (isDigit(this.char)) { - this.consume(); - } else { - throw this.error(new TomlError("Incomplete datetime")); - } - } - parseDateDay() { - if (this.char === CHAR_T || this.char === CHAR_SP) { - if (this.state.buf.length < 2) { - throw this.error(new TomlError("Days less than 10 must be zero padded to two characters")); - } - this.state.result += "-" + this.state.buf; - this.state.buf = ""; - return this.next(this.parseStartTimeHour); - } else if (this.atEndOfWord()) { - return this.returnNow(createDate(this.state.result + "-" + this.state.buf)); - } else if (isDigit(this.char)) { - this.consume(); - } else { - throw this.error(new TomlError("Incomplete datetime")); - } - } - parseStartTimeHour() { - if (this.atEndOfWord()) { - return this.returnNow(createDate(this.state.result)); - } else { - return this.goto(this.parseTimeHour); - } - } - parseTimeHour() { - if (this.char === CHAR_COLON) { - if (this.state.buf.length < 2) { - throw this.error(new TomlError("Hours less than 10 must be zero padded to two characters")); - } - this.state.result += "T" + this.state.buf; - this.state.buf = ""; - return this.next(this.parseTimeMin); - } else if (isDigit(this.char)) { - this.consume(); - } else { - throw this.error(new TomlError("Incomplete datetime")); - } - } - parseTimeMin() { - if (this.state.buf.length < 2 && isDigit(this.char)) { - this.consume(); - } else if (this.state.buf.length === 2 && this.char === CHAR_COLON) { - this.state.result += ":" + this.state.buf; - this.state.buf = ""; - return this.next(this.parseTimeSec); - } else { - throw this.error(new TomlError("Incomplete datetime")); - } - } - parseTimeSec() { - if (isDigit(this.char)) { - this.consume(); - if (this.state.buf.length === 2) { - this.state.result += ":" + this.state.buf; - this.state.buf = ""; - return this.next(this.parseTimeZoneOrFraction); - } - } else { - throw this.error(new TomlError("Incomplete datetime")); - } - } - parseOnlyTimeHour() { - if (this.char === CHAR_COLON) { - if (this.state.buf.length < 2) { - throw this.error(new TomlError("Hours less than 10 must be zero padded to two characters")); - } - this.state.result = this.state.buf; - this.state.buf = ""; - return this.next(this.parseOnlyTimeMin); - } else { - throw this.error(new TomlError("Incomplete time")); - } - } - parseOnlyTimeMin() { - if (this.state.buf.length < 2 && isDigit(this.char)) { - this.consume(); - } else if (this.state.buf.length === 2 && this.char === CHAR_COLON) { - this.state.result += ":" + this.state.buf; - this.state.buf = ""; - return this.next(this.parseOnlyTimeSec); - } else { - throw this.error(new TomlError("Incomplete time")); - } - } - parseOnlyTimeSec() { - if (isDigit(this.char)) { - this.consume(); - if (this.state.buf.length === 2) { - return this.next(this.parseOnlyTimeFractionMaybe); - } - } else { - throw this.error(new TomlError("Incomplete time")); - } - } - parseOnlyTimeFractionMaybe() { - this.state.result += ":" + this.state.buf; - if (this.char === CHAR_PERIOD) { - this.state.buf = ""; - this.next(this.parseOnlyTimeFraction); - } else { - return this.return(createTime(this.state.result)); - } - } - parseOnlyTimeFraction() { - if (isDigit(this.char)) { - this.consume(); - } else if (this.atEndOfWord()) { - if (this.state.buf.length === 0) - throw this.error(new TomlError("Expected digit in milliseconds")); - return this.returnNow(createTime(this.state.result + "." + this.state.buf)); - } else { - throw this.error(new TomlError("Unexpected character in datetime, expected period (.), minus (-), plus (+) or Z")); - } - } - parseTimeZoneOrFraction() { - if (this.char === CHAR_PERIOD) { - this.consume(); - this.next(this.parseDateTimeFraction); - } else if (this.char === CHAR_HYPHEN || this.char === CHAR_PLUS) { - this.consume(); - this.next(this.parseTimeZoneHour); - } else if (this.char === CHAR_Z) { - this.consume(); - return this.return(createDateTime(this.state.result + this.state.buf)); - } else if (this.atEndOfWord()) { - return this.returnNow(createDateTimeFloat(this.state.result + this.state.buf)); - } else { - throw this.error(new TomlError("Unexpected character in datetime, expected period (.), minus (-), plus (+) or Z")); - } - } - parseDateTimeFraction() { - if (isDigit(this.char)) { - this.consume(); - } else if (this.state.buf.length === 1) { - throw this.error(new TomlError("Expected digit in milliseconds")); - } else if (this.char === CHAR_HYPHEN || this.char === CHAR_PLUS) { - this.consume(); - this.next(this.parseTimeZoneHour); - } else if (this.char === CHAR_Z) { - this.consume(); - return this.return(createDateTime(this.state.result + this.state.buf)); - } else if (this.atEndOfWord()) { - return this.returnNow(createDateTimeFloat(this.state.result + this.state.buf)); - } else { - throw this.error(new TomlError("Unexpected character in datetime, expected period (.), minus (-), plus (+) or Z")); - } - } - parseTimeZoneHour() { - if (isDigit(this.char)) { - this.consume(); - if (/\d\d$/.test(this.state.buf)) - return this.next(this.parseTimeZoneSep); - } else { - throw this.error(new TomlError("Unexpected character in datetime, expected digit")); - } - } - parseTimeZoneSep() { - if (this.char === CHAR_COLON) { - this.consume(); - this.next(this.parseTimeZoneMin); - } else { - throw this.error(new TomlError("Unexpected character in datetime, expected colon")); - } - } - parseTimeZoneMin() { - if (isDigit(this.char)) { - this.consume(); - if (/\d\d$/.test(this.state.buf)) - return this.return(createDateTime(this.state.result + this.state.buf)); - } else { - throw this.error(new TomlError("Unexpected character in datetime, expected digit")); - } - } - /* BOOLEAN */ - parseBoolean() { - if (this.char === CHAR_t) { - this.consume(); - return this.next(this.parseTrue_r); - } else if (this.char === CHAR_f) { - this.consume(); - return this.next(this.parseFalse_a); - } - } - parseTrue_r() { - if (this.char === CHAR_r) { - this.consume(); - return this.next(this.parseTrue_u); - } else { - throw this.error(new TomlError("Invalid boolean, expected true or false")); - } - } - parseTrue_u() { - if (this.char === CHAR_u) { - this.consume(); - return this.next(this.parseTrue_e); - } else { - throw this.error(new TomlError("Invalid boolean, expected true or false")); - } - } - parseTrue_e() { - if (this.char === CHAR_e) { - return this.return(true); - } else { - throw this.error(new TomlError("Invalid boolean, expected true or false")); - } - } - parseFalse_a() { - if (this.char === CHAR_a) { - this.consume(); - return this.next(this.parseFalse_l); - } else { - throw this.error(new TomlError("Invalid boolean, expected true or false")); - } - } - parseFalse_l() { - if (this.char === CHAR_l) { - this.consume(); - return this.next(this.parseFalse_s); - } else { - throw this.error(new TomlError("Invalid boolean, expected true or false")); - } - } - parseFalse_s() { - if (this.char === CHAR_s) { - this.consume(); - return this.next(this.parseFalse_e); - } else { - throw this.error(new TomlError("Invalid boolean, expected true or false")); - } - } - parseFalse_e() { - if (this.char === CHAR_e) { - return this.return(false); - } else { - throw this.error(new TomlError("Invalid boolean, expected true or false")); - } - } - /* INLINE LISTS */ - parseInlineList() { - if (this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M || this.char === CTRL_J) { - return null; - } else if (this.char === Parser.END) { - throw this.error(new TomlError("Unterminated inline array")); - } else if (this.char === CHAR_NUM) { - return this.call(this.parseComment); - } else if (this.char === CHAR_RSQB) { - return this.return(this.state.resultArr || InlineList()); - } else { - return this.callNow(this.parseValue, this.recordInlineListValue); - } - } - recordInlineListValue(value) { - if (this.state.resultArr) { - const listType = this.state.resultArr[_contentType]; - const valueType = tomlType(value); - if (listType !== valueType) { - throw this.error(new TomlError(`Inline lists must be a single type, not a mix of ${listType} and ${valueType}`)); - } - } else { - this.state.resultArr = InlineList(tomlType(value)); - } - if (isFloat(value) || isInteger(value)) { - this.state.resultArr.push(value.valueOf()); - } else { - this.state.resultArr.push(value); - } - return this.goto(this.parseInlineListNext); - } - parseInlineListNext() { - if (this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M || this.char === CTRL_J) { - return null; - } else if (this.char === CHAR_NUM) { - return this.call(this.parseComment); - } else if (this.char === CHAR_COMMA) { - return this.next(this.parseInlineList); - } else if (this.char === CHAR_RSQB) { - return this.goto(this.parseInlineList); - } else { - throw this.error(new TomlError("Invalid character, expected whitespace, comma (,) or close bracket (])")); - } - } - /* INLINE TABLE */ - parseInlineTable() { - if (this.char === CHAR_SP || this.char === CTRL_I) { - return null; - } else if (this.char === Parser.END || this.char === CHAR_NUM || this.char === CTRL_J || this.char === CTRL_M) { - throw this.error(new TomlError("Unterminated inline array")); - } else if (this.char === CHAR_RCUB) { - return this.return(this.state.resultTable || InlineTable()); - } else { - if (!this.state.resultTable) - this.state.resultTable = InlineTable(); - return this.callNow(this.parseAssign, this.recordInlineTableValue); - } - } - recordInlineTableValue(kv) { - let target = this.state.resultTable; - let finalKey = kv.key.pop(); - for (let kw of kv.key) { - if (hasKey(target, kw) && (!isTable(target[kw]) || target[kw][_declared])) { - throw this.error(new TomlError("Can't redefine existing key")); - } - target = target[kw] = target[kw] || Table(); - } - if (hasKey(target, finalKey)) { - throw this.error(new TomlError("Can't redefine existing key")); - } - if (isInteger(kv.value) || isFloat(kv.value)) { - target[finalKey] = kv.value.valueOf(); - } else { - target[finalKey] = kv.value; - } - return this.goto(this.parseInlineTableNext); - } - parseInlineTableNext() { - if (this.char === CHAR_SP || this.char === CTRL_I) { - return null; - } else if (this.char === Parser.END || this.char === CHAR_NUM || this.char === CTRL_J || this.char === CTRL_M) { - throw this.error(new TomlError("Unterminated inline array")); - } else if (this.char === CHAR_COMMA) { - return this.next(this.parseInlineTable); - } else if (this.char === CHAR_RCUB) { - return this.goto(this.parseInlineTable); - } else { - throw this.error(new TomlError("Invalid character, expected whitespace, comma (,) or close bracket (])")); - } - } - } - return TOMLParser; - } - } - }); - - // node_modules/@iarna/toml/parse-pretty-error.js - var require_parse_pretty_error = __commonJS({ - "node_modules/@iarna/toml/parse-pretty-error.js"(exports2, module2) { - "use strict"; - module2.exports = prettyError; - function prettyError(err, buf) { - if (err.pos == null || err.line == null) - return err; - let msg = err.message; - msg += ` at row ${err.line + 1}, col ${err.col + 1}, pos ${err.pos}: -`; - if (buf && buf.split) { - const lines = buf.split(/\n/); - const lineNumWidth = String(Math.min(lines.length, err.line + 3)).length; - let linePadding = " "; - while (linePadding.length < lineNumWidth) - linePadding += " "; - for (let ii = Math.max(0, err.line - 1); ii < Math.min(lines.length, err.line + 2); ++ii) { - let lineNum = String(ii + 1); - if (lineNum.length < lineNumWidth) - lineNum = " " + lineNum; - if (err.line === ii) { - msg += lineNum + "> " + lines[ii] + "\n"; - msg += linePadding + " "; - for (let hh = 0; hh < err.col; ++hh) { - msg += " "; - } - msg += "^\n"; - } else { - msg += lineNum + ": " + lines[ii] + "\n"; - } - } - } - err.message = msg + "\n"; - return err; - } - } - }); - - // node_modules/@iarna/toml/parse-string.js - var require_parse_string = __commonJS({ - "node_modules/@iarna/toml/parse-string.js"(exports2, module2) { - "use strict"; - module2.exports = parseString; - var TOMLParser = require_toml_parser(); - var prettyError = require_parse_pretty_error(); - function parseString(str) { - if (globalThis.Buffer && globalThis.Buffer.isBuffer(str)) { - str = str.toString("utf8"); - } - const parser = new TOMLParser(); - try { - parser.parse(str); - return parser.finish(); - } catch (err) { - throw prettyError(err, str); - } - } - } - }); - - // node_modules/@iarna/toml/parse-async.js - var require_parse_async = __commonJS({ - "node_modules/@iarna/toml/parse-async.js"(exports2, module2) { - "use strict"; - module2.exports = parseAsync; - var TOMLParser = require_toml_parser(); - var prettyError = require_parse_pretty_error(); - function parseAsync(str, opts) { - if (!opts) - opts = {}; - const index = 0; - const blocksize = opts.blocksize || 40960; - const parser = new TOMLParser(); - return new Promise((resolve, reject) => { - setImmediate(parseAsyncNext, index, blocksize, resolve, reject); - }); - function parseAsyncNext(index2, blocksize2, resolve, reject) { - if (index2 >= str.length) { - try { - return resolve(parser.finish()); - } catch (err) { - return reject(prettyError(err, str)); - } - } - try { - parser.parse(str.slice(index2, index2 + blocksize2)); - setImmediate(parseAsyncNext, index2 + blocksize2, blocksize2, resolve, reject); - } catch (err) { - reject(prettyError(err, str)); - } - } - } - } - }); - - // node_modules/component-emitter/index.js - var require_component_emitter = __commonJS({ - "node_modules/component-emitter/index.js"(exports2, module2) { - function Emitter(object) { - if (object) { - return mixin(object); - } - this._callbacks = /* @__PURE__ */ new Map(); - } - function mixin(object) { - Object.assign(object, Emitter.prototype); - object._callbacks = /* @__PURE__ */ new Map(); - return object; - } - Emitter.prototype.on = function(event, listener) { - const callbacks = this._callbacks.get(event) ?? []; - callbacks.push(listener); - this._callbacks.set(event, callbacks); - return this; - }; - Emitter.prototype.once = function(event, listener) { - const on = (...arguments_) => { - this.off(event, on); - listener.apply(this, arguments_); - }; - on.fn = listener; - this.on(event, on); - return this; - }; - Emitter.prototype.off = function(event, listener) { - if (event === void 0 && listener === void 0) { - this._callbacks.clear(); - return this; - } - if (listener === void 0) { - this._callbacks.delete(event); - return this; - } - const callbacks = this._callbacks.get(event); - if (callbacks) { - for (const [index, callback] of callbacks.entries()) { - if (callback === listener || callback.fn === listener) { - callbacks.splice(index, 1); - break; - } - } - if (callbacks.length === 0) { - this._callbacks.delete(event); - } else { - this._callbacks.set(event, callbacks); - } - } - return this; - }; - Emitter.prototype.emit = function(event, ...arguments_) { - const callbacks = this._callbacks.get(event); - if (callbacks) { - const callbacksCopy = [...callbacks]; - for (const callback of callbacksCopy) { - callback.apply(this, arguments_); - } - } - return this; - }; - Emitter.prototype.listeners = function(event) { - return this._callbacks.get(event) ?? []; - }; - Emitter.prototype.listenerCount = function(event) { - if (event) { - return this.listeners(event).length; - } - let totalCount = 0; - for (const callbacks of this._callbacks.values()) { - totalCount += callbacks.length; - } - return totalCount; - }; - Emitter.prototype.hasListeners = function(event) { - return this.listenerCount(event) > 0; - }; - Emitter.prototype.addEventListener = Emitter.prototype.on; - Emitter.prototype.removeListener = Emitter.prototype.off; - Emitter.prototype.removeEventListener = Emitter.prototype.off; - Emitter.prototype.removeAllListeners = Emitter.prototype.off; - if (typeof module2 !== "undefined") { - module2.exports = Emitter; - } - } - }); - - // node_modules/stream/index.js - var require_stream = __commonJS({ - "node_modules/stream/index.js"(exports2, module2) { - var Emitter = require_component_emitter(); - function Stream() { - Emitter.call(this); - } - Stream.prototype = new Emitter(); - module2.exports = Stream; - Stream.Stream = Stream; - Stream.prototype.pipe = function(dest, options) { - var source = this; - function ondata(chunk) { - if (dest.writable) { - if (false === dest.write(chunk) && source.pause) { - source.pause(); - } - } - } - source.on("data", ondata); - function ondrain() { - if (source.readable && source.resume) { - source.resume(); - } - } - dest.on("drain", ondrain); - if (!dest._isStdio && (!options || options.end !== false)) { - source.on("end", onend); - source.on("close", onclose); - } - var didOnEnd = false; - function onend() { - if (didOnEnd) - return; - didOnEnd = true; - dest.end(); - } - function onclose() { - if (didOnEnd) - return; - didOnEnd = true; - if (typeof dest.destroy === "function") - dest.destroy(); - } - function onerror(er) { - cleanup(); - if (!this.hasListeners("error")) { - throw er; - } - } - source.on("error", onerror); - dest.on("error", onerror); - function cleanup() { - source.off("data", ondata); - dest.off("drain", ondrain); - source.off("end", onend); - source.off("close", onclose); - source.off("error", onerror); - dest.off("error", onerror); - source.off("end", cleanup); - source.off("close", cleanup); - dest.off("end", cleanup); - dest.off("close", cleanup); - } - source.on("end", cleanup); - source.on("close", cleanup); - dest.on("end", cleanup); - dest.on("close", cleanup); - dest.emit("pipe", source); - return dest; - }; - } - }); - - // node_modules/@iarna/toml/parse-stream.js - var require_parse_stream = __commonJS({ - "node_modules/@iarna/toml/parse-stream.js"(exports2, module2) { - "use strict"; - module2.exports = parseStream; - var stream = require_stream(); - var TOMLParser = require_toml_parser(); - function parseStream(stm) { - if (stm) { - return parseReadable(stm); - } else { - return parseTransform(stm); - } - } - function parseReadable(stm) { - const parser = new TOMLParser(); - stm.setEncoding("utf8"); - return new Promise((resolve, reject) => { - let readable; - let ended = false; - let errored = false; - function finish() { - ended = true; - if (readable) - return; - try { - resolve(parser.finish()); - } catch (err) { - reject(err); - } - } - function error(err) { - errored = true; - reject(err); - } - stm.once("end", finish); - stm.once("error", error); - readNext(); - function readNext() { - readable = true; - let data; - while ((data = stm.read()) !== null) { - try { - parser.parse(data); - } catch (err) { - return error(err); - } - } - readable = false; - if (ended) - return finish(); - if (errored) - return; - stm.once("readable", readNext); - } - }); - } - function parseTransform() { - const parser = new TOMLParser(); - return new stream.Transform({ - objectMode: true, - transform(chunk, encoding, cb) { - try { - parser.parse(chunk.toString(encoding)); - } catch (err) { - this.emit("error", err); - } - cb(); - }, - flush(cb) { - try { - this.push(parser.finish()); - } catch (err) { - this.emit("error", err); - } - cb(); - } - }); - } - } - }); - - // node_modules/@iarna/toml/parse.js - var require_parse = __commonJS({ - "node_modules/@iarna/toml/parse.js"(exports2, module2) { - "use strict"; - module2.exports = require_parse_string(); - module2.exports.async = require_parse_async(); - module2.exports.stream = require_parse_stream(); - module2.exports.prettyError = require_parse_pretty_error(); - } - }); - - // node_modules/@iarna/toml/stringify.js - var require_stringify = __commonJS({ - "node_modules/@iarna/toml/stringify.js"(exports2, module2) { - "use strict"; - module2.exports = stringify; - module2.exports.value = stringifyInline; - function stringify(obj) { - if (obj === null) - throw typeError("null"); - if (obj === void 0) - throw typeError("undefined"); - if (typeof obj !== "object") - throw typeError(typeof obj); - if (typeof obj.toJSON === "function") - obj = obj.toJSON(); - if (obj == null) - return null; - const type = tomlType2(obj); - if (type !== "table") - throw typeError(type); - return stringifyObject("", "", obj); - } - function typeError(type) { - return new Error("Can only stringify objects, not " + type); - } - function arrayOneTypeError() { - return new Error("Array values can't have mixed types"); - } - function getInlineKeys(obj) { - return Object.keys(obj).filter((key) => isInline(obj[key])); - } - function getComplexKeys(obj) { - return Object.keys(obj).filter((key) => !isInline(obj[key])); - } - function toJSON(obj) { - let nobj = Array.isArray(obj) ? [] : Object.prototype.hasOwnProperty.call(obj, "__proto__") ? { ["__proto__"]: void 0 } : {}; - for (let prop of Object.keys(obj)) { - if (obj[prop] && typeof obj[prop].toJSON === "function" && !("toISOString" in obj[prop])) { - nobj[prop] = obj[prop].toJSON(); - } else { - nobj[prop] = obj[prop]; - } - } - return nobj; - } - function stringifyObject(prefix, indent, obj) { - obj = toJSON(obj); - var inlineKeys; - var complexKeys; - inlineKeys = getInlineKeys(obj); - complexKeys = getComplexKeys(obj); - var result = []; - var inlineIndent = indent || ""; - inlineKeys.forEach((key) => { - var type = tomlType2(obj[key]); - if (type !== "undefined" && type !== "null") { - result.push(inlineIndent + stringifyKey(key) + " = " + stringifyAnyInline(obj[key], true)); - } - }); - if (result.length > 0) - result.push(""); - var complexIndent = prefix && inlineKeys.length > 0 ? indent + " " : ""; - complexKeys.forEach((key) => { - result.push(stringifyComplex(prefix, complexIndent, key, obj[key])); - }); - return result.join("\n"); - } - function isInline(value) { - switch (tomlType2(value)) { - case "undefined": - case "null": - case "integer": - case "nan": - case "float": - case "boolean": - case "string": - case "datetime": - return true; - case "array": - return value.length === 0 || tomlType2(value[0]) !== "table"; - case "table": - return Object.keys(value).length === 0; - default: - return false; - } - } - function tomlType2(value) { - if (value === void 0) { - return "undefined"; - } else if (value === null) { - return "null"; - } else if (typeof value === "bigint" || Number.isInteger(value) && !Object.is(value, -0)) { - return "integer"; - } else if (typeof value === "number") { - return "float"; - } else if (typeof value === "boolean") { - return "boolean"; - } else if (typeof value === "string") { - return "string"; - } else if ("toISOString" in value) { - return isNaN(value) ? "undefined" : "datetime"; - } else if (Array.isArray(value)) { - return "array"; - } else { - return "table"; - } - } - function stringifyKey(key) { - var keyStr = String(key); - if (/^[-A-Za-z0-9_]+$/.test(keyStr)) { - return keyStr; - } else { - return stringifyBasicString(keyStr); - } - } - function stringifyBasicString(str) { - return '"' + escapeString(str).replace(/"/g, '\\"') + '"'; - } - function stringifyLiteralString(str) { - return "'" + str + "'"; - } - function numpad(num, str) { - while (str.length < num) - str = "0" + str; - return str; - } - function escapeString(str) { - return str.replace(/\\/g, "\\\\").replace(/[\b]/g, "\\b").replace(/\t/g, "\\t").replace(/\n/g, "\\n").replace(/\f/g, "\\f").replace(/\r/g, "\\r").replace(/([\u0000-\u001f\u007f])/, (c) => "\\u" + numpad(4, c.codePointAt(0).toString(16))); - } - function stringifyMultilineString(str) { - let escaped = str.split(/\n/).map((str2) => { - return escapeString(str2).replace(/"(?="")/g, '\\"'); - }).join("\n"); - if (escaped.slice(-1) === '"') - escaped += "\\\n"; - return '"""\n' + escaped + '"""'; - } - function stringifyAnyInline(value, multilineOk) { - let type = tomlType2(value); - if (type === "string") { - if (multilineOk && /\n/.test(value)) { - type = "string-multiline"; - } else if (!/[\b\t\n\f\r']/.test(value) && /"/.test(value)) { - type = "string-literal"; - } - } - return stringifyInline(value, type); - } - function stringifyInline(value, type) { - if (!type) - type = tomlType2(value); - switch (type) { - case "string-multiline": - return stringifyMultilineString(value); - case "string": - return stringifyBasicString(value); - case "string-literal": - return stringifyLiteralString(value); - case "integer": - return stringifyInteger(value); - case "float": - return stringifyFloat(value); - case "boolean": - return stringifyBoolean(value); - case "datetime": - return stringifyDatetime(value); - case "array": - return stringifyInlineArray(value.filter((_) => tomlType2(_) !== "null" && tomlType2(_) !== "undefined" && tomlType2(_) !== "nan")); - case "table": - return stringifyInlineTable(value); - default: - throw typeError(type); - } - } - function stringifyInteger(value) { - return String(value).replace(/\B(?=(\d{3})+(?!\d))/g, "_"); - } - function stringifyFloat(value) { - if (value === Infinity) { - return "inf"; - } else if (value === -Infinity) { - return "-inf"; - } else if (Object.is(value, NaN)) { - return "nan"; - } else if (Object.is(value, -0)) { - return "-0.0"; - } - var chunks = String(value).split("."); - var int = chunks[0]; - var dec = chunks[1] || 0; - return stringifyInteger(int) + "." + dec; - } - function stringifyBoolean(value) { - return String(value); - } - function stringifyDatetime(value) { - return value.toISOString(); - } - function isNumber(type) { - return type === "float" || type === "integer"; - } - function arrayType(values) { - var contentType = tomlType2(values[0]); - if (values.every((_) => tomlType2(_) === contentType)) - return contentType; - if (values.every((_) => isNumber(tomlType2(_)))) - return "float"; - return "mixed"; - } - function validateArray(values) { - const type = arrayType(values); - if (type === "mixed") { - throw arrayOneTypeError(); - } - return type; - } - function stringifyInlineArray(values) { - values = toJSON(values); - const type = validateArray(values); - var result = "["; - var stringified = values.map((_) => stringifyInline(_, type)); - if (stringified.join(", ").length > 60 || /\n/.test(stringified)) { - result += "\n " + stringified.join(",\n ") + "\n"; - } else { - result += " " + stringified.join(", ") + (stringified.length > 0 ? " " : ""); - } - return result + "]"; - } - function stringifyInlineTable(value) { - value = toJSON(value); - var result = []; - Object.keys(value).forEach((key) => { - result.push(stringifyKey(key) + " = " + stringifyAnyInline(value[key], false)); - }); - return "{ " + result.join(", ") + (result.length > 0 ? " " : "") + "}"; - } - function stringifyComplex(prefix, indent, key, value) { - var valueType = tomlType2(value); - if (valueType === "array") { - return stringifyArrayOfTables(prefix, indent, key, value); - } else if (valueType === "table") { - return stringifyComplexTable(prefix, indent, key, value); - } else { - throw typeError(valueType); - } - } - function stringifyArrayOfTables(prefix, indent, key, values) { - values = toJSON(values); - validateArray(values); - var firstValueType = tomlType2(values[0]); - if (firstValueType !== "table") - throw typeError(firstValueType); - var fullKey = prefix + stringifyKey(key); - var result = ""; - values.forEach((table) => { - if (result.length > 0) - result += "\n"; - result += indent + "[[" + fullKey + "]]\n"; - result += stringifyObject(fullKey + ".", indent, table); - }); - return result; - } - function stringifyComplexTable(prefix, indent, key, value) { - var fullKey = prefix + stringifyKey(key); - var result = ""; - if (getInlineKeys(value).length > 0) { - result += indent + "[" + fullKey + "]\n"; - } - return result + stringifyObject(fullKey + ".", indent, value); - } - } - }); - - // node_modules/@iarna/toml/toml.js - var require_toml = __commonJS({ - "node_modules/@iarna/toml/toml.js"(exports2) { - "use strict"; - exports2.parse = require_parse(); - exports2.stringify = require_stringify(); - } - }); - - // src/elemtoml.ts - var import_utility_types = __toESM(require_dist()); - function register_element(name, elem) { - console.debug( - "Element registered: ", - elem, - "Under name: ", - name, - "Named behaviour: ", - elem.namedBehavior, - window.behaviors[elem.namedBehavior] - ); - console.trace(); - let tmp_value = elem; - console.log(tmp_value.namedBehavior); - if (tmp_value.namedBehavior) { - const found_behaviour = window.behaviors[tmp_value.namedBehavior]; - if (typeof found_behaviour == "function") { - tmp_value.tick = found_behaviour; - } else { - tmp_value.behavior = found_behaviour; - } - } - window.elements[name] = tmp_value; - } - - // src/cfg_loader.ts - var import_toml = __toESM(require_toml()); - var Package = class { - constructor(config) { - this.loaded_elems = []; - this.cfg = config; - console.log(this); - } - async load_elems() { - for (const i of this.cfg.mod.external_elements) { - console.log(i); - try { - let resp = await fetch(i.path); - const parsed = (0, import_toml.parse)(await resp.text()); - console.log(parsed); - register_element(i.name, parsed); - } catch (err) { - console.error(err); - } - } - } - get_loaded_elems() { - return this.loaded_elems; - } - run() { - fetch(this.cfg.mod.entry_point).then((resp) => { - resp.text().then((x) => Function(x)()); - }); - } - }; - function load(object) { - return object; - } - - // src/mod.ts - var import_toml2 = __toESM(require_toml()); - - // src/mod_finder.ts - function find_mod(name, onfind) { - console.log(name, `mods/${name}/mod.toml`); - fetch(`mods/${name}/mod.toml`).then(async (x) => { - console.log(x.url); - if (x.ok) { - onfind(await x.text()); - } else { - window.queuedMods.push(name); - } - }).catch((err) => { - console.error(err); - }); - } - - // src/mod.ts - console.log(window.enabledMods); - for (const i in window.enabledMods) { - console.log(i); - if (i.endsWith(".toml")) { - console.log(i); - find_mod(i.slice(0, -5), (text) => { - console.log("Mod name:", i.slice(0, -5)); - const parsed = import_toml2.default.parse(text); - console.log("important shit:", import_toml2.default.parse(text)); - let pkg = new Package(load(parsed)); - pkg.load_elems().then(() => { - console.log(pkg); - console.log(pkg.get_loaded_elems()); - }); - }); - } - } -})(); -/*! Bundled license information: - -utility-types/dist/index.js: - (** - * @author Piotr Witek (http://piotrwitek.github.io) - * @copyright Copyright (c) 2016 Piotr Witek - * @license MIT - *) -*/ From 44c7fec32e6a51e8f543cb70e2f7cc6d9810205f Mon Sep 17 00:00:00 2001 From: slweeb <91897291+slweeb@users.noreply.github.com> Date: Sun, 7 Dec 2025 19:46:57 -0500 Subject: [PATCH 11/38] . --- mods/background_changer.js | 33 +++++++++---- mods/borders.js | 2 +- mods/content.js | 98 ++++++++++++++++++++++++++++++++++++++ mods/devsnacks.js | 2 + 4 files changed, 124 insertions(+), 11 deletions(-) create mode 100644 mods/content.js diff --git a/mods/background_changer.js b/mods/background_changer.js index 200daa97..1a61fc1f 100644 --- a/mods/background_changer.js +++ b/mods/background_changer.js @@ -1,4 +1,4 @@ -function setCanvasBG(url) { +function setCanvasBG(url, color) { delete settings.bg; let canvas = document.getElementById("game"); @@ -9,32 +9,45 @@ function setCanvasBG(url) { canvas.style.backgroundPosition = "bottom"; settings.bgimg = url; + if (color) settings.bgimgcolor = color; + else delete settings.bgimgcolor; + + if (color) document.head.insertAdjacentHTML("beforeend", ``); + else document.getElementById("canvasBGStyles")?.remove(); saveSettings() } +function clearCanvasBG() { + delete settings.bgimg; + let canvas = document.getElementById("game"); + canvas.style.backgroundImage = ""; + canvas.style.backgroundRepeat = ""; + canvas.style.backgroundSize = ""; + canvas.style.backgroundPosition = ""; + document.getElementById("canvasBGStyles")?.remove(); +} -keybinds.KeyB = () => { +function setCanvasBGPrompt() { promptInput("Enter an image URL. Leave blank to clear background.", (url) => { if (!url) { - delete settings.bgimg; - let canvas = document.getElementById("game"); - canvas.style.backgroundImage = ""; - canvas.style.backgroundRepeat = ""; - canvas.style.backgroundSize = ""; - canvas.style.backgroundPosition = ""; + clearCanvasBG(); } setCanvasBG(url); }, "Background") } +keybinds.KeyB = () => { + setCanvasBGPrompt() +} + document.addEventListener("load", () => { if (settings.bgimg) { - setCanvasBG(settings.bgimg) + setCanvasBG(settings.bgimg, settings.bgimgcolor) } }) runAfterReset(() => { if (settings.bgimg) { - setCanvasBG(settings.bgimg) + setCanvasBG(settings.bgimg, settings.bgimgcolor) } }) \ No newline at end of file diff --git a/mods/borders.js b/mods/borders.js index 9d96dcf3..443dc844 100644 --- a/mods/borders.js +++ b/mods/borders.js @@ -18,7 +18,7 @@ window.addEventListener("load", () => { var coords = adjacentCoords[i]; var x = pixel.x + coords[0]; var y = pixel.y + coords[1]; - if (isEmpty(x,y)) { + if (isEmpty(x,y, true)) { // if (elements[pixelMap[x][y].element].id !== elements[pixel.element].id || elements[pixelMap[x][y].element].state !== elements[pixel.element].id) continue edge = true; break; diff --git a/mods/content.js b/mods/content.js new file mode 100644 index 00000000..11dc6a63 --- /dev/null +++ b/mods/content.js @@ -0,0 +1,98 @@ +dependOn("borders.js", ()=>{}, true); +dependOn("glow.js", ()=>{}, true); +dependOn("background_changer.js", ()=>{}, true); +dependOn("cursor.js", ()=>{}, true); + +function minimizeElement(selector) { + document.querySelectorAll(selector).forEach(element => { + element.classList.add("minimized"); + }); +} +function maximizeAll() { + document.querySelectorAll(".minimized").forEach((e) => { + e.classList.remove("minimized"); + }) +} +document.head.insertAdjacentHTML("beforeend", ` + +`); + +settings.pixelsize = "6w"; + +contentModOptions = { + "Set Background": () => { + setCanvasBGPrompt(); + }, + "Set Cursor Color": () => { + promptInput("Enter an image URL. Leave blank to clear background.", (color) => { + if (!color) { + delete settings.mouseColor; + return; + } + mouseColor = color; + settings.mouseColor = color; + }, "Mouse Color") + }, + "Clear Background": () => { + clearCanvasBG(); + }, + "Grassland": () => setCanvasBG("https://i.imgur.com/Id9WZv4.png", "#2d6138ff"), + "Sky": () => setCanvasBG("https://i.imgur.com/Er068gC.png", "#647690ff"), + "Sky Warm": () => setCanvasBG("https://i.imgur.com/aJeSRLk.png", "#904f45ff"), + "Minimize UI": () => { + minimizeElement("#stats"); + minimizeElement("#extraInfo"); + minimizeElement("#bottomInfoBox"); + minimizeElement("#logDiv"); + minimizeElement("#toolControls"); + document.getElementById("category-tools").style.borderTop = "6px black solid"; + document.getElementById("category-tools").style.paddingTop = "0.5em"; + resetPrompt(); + }, + "Maximize UI": () => { + maximizeAll(); + }, + "Content Hub": () => { + window.open("https://r74n.com/content/", '_blank'); + }, +} + +keybinds.KeyV = () => { + promptChoose("", Object.keys(contentModOptions), (r) => { + contentModOptions[r](); + }) +} + +elements.invis = { + renderer: (pixel,ctx) => { + if (currentElement === "invis") { + drawSquare(ctx,"#00ff00",pixel.x,pixel.y); + } + }, + behavior: (pixel) => { + doDefaults(pixel); + }, + onPlace: (pixel) => { + pixel.alpha = 0; + }, + tool: (pixel) => { + pixel.alpha = 0; + }, + category: "special", + color: "#00ff00" +} + +window.addEventListener("load", () => { + contentModOptions["Minimize UI"](); +}) \ No newline at end of file diff --git a/mods/devsnacks.js b/mods/devsnacks.js index 20734dc8..2b52f790 100644 --- a/mods/devsnacks.js +++ b/mods/devsnacks.js @@ -5,6 +5,8 @@ smash into tea powder tea powder + hot water = colored tea */ +dependOn("mustard.js", ()=>{}, true); + elements.herb.tempHigh = 100; elements.herb.stateHigh = ["steamed_herb","steamed_herb","steam",null]; From 83334b0d6017cf815f740b85d278a6b646ef5084 Mon Sep 17 00:00:00 2001 From: slweeb <91897291+slweeb@users.noreply.github.com> Date: Thu, 11 Dec 2025 18:26:46 -0500 Subject: [PATCH 12/38] background images --- backgrounds/License.txt | 1 + backgrounds/sky.png | Bin 0 -> 22122 bytes backgrounds/sky_dark.png | Bin 0 -> 18222 bytes backgrounds/sky_grass.png | Bin 0 -> 23011 bytes backgrounds/sky_pink.png | Bin 0 -> 30484 bytes backgrounds/sky_purple.png | Bin 0 -> 24485 bytes backgrounds/sky_warm.png | Bin 0 -> 33927 bytes 7 files changed, 1 insertion(+) create mode 100644 backgrounds/License.txt create mode 100644 backgrounds/sky.png create mode 100644 backgrounds/sky_dark.png create mode 100644 backgrounds/sky_grass.png create mode 100644 backgrounds/sky_pink.png create mode 100644 backgrounds/sky_purple.png create mode 100644 backgrounds/sky_warm.png diff --git a/backgrounds/License.txt b/backgrounds/License.txt new file mode 100644 index 00000000..673e1aa6 --- /dev/null +++ b/backgrounds/License.txt @@ -0,0 +1 @@ +https://craftpix.net/file-licenses/ \ No newline at end of file diff --git a/backgrounds/sky.png b/backgrounds/sky.png new file mode 100644 index 0000000000000000000000000000000000000000..94f4a033f1b87674f342639055ecdc55ad02377c GIT binary patch literal 22122 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 literal 0 HcmV?d00001 diff --git a/backgrounds/sky_grass.png b/backgrounds/sky_grass.png new file mode 100644 index 0000000000000000000000000000000000000000..1b522a83f46a7598d61e7aa967ea2e8ffbfc2d71 GIT binary patch literal 23011 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ⅅ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 literal 0 HcmV?d00001 diff --git a/backgrounds/sky_purple.png b/backgrounds/sky_purple.png new file mode 100644 index 0000000000000000000000000000000000000000..d7b842740b03762bc4d10498f2cd9321e8f08830 GIT binary patch literal 24485 zcmce-1zeQf(>G46uyjcZvUE2r-Q8U($igm2cZ0Yf-6;){(j_PeNOzZ%fOHE;DEY45 z`P|R_d!P583;Wq?&zbqo%$Yee=bY=>a4ii*983yKBqSspWhFTsBqS6mBqU@xAPS;I z3Qn?%_&|2mQItWd9HHDnd^v&{DO;+mBe5c`fk^1cgh;4ANf3WX$P`HTeqAFWnIk{= z^V$ZP`F9!wCNdAwPh@Ju+Xb2T*Y$nGbwa2$65w~h7g8tP~c18F^T zbaQll=I8|C<>M3pF&kPtKpj0@S&)!;xp{d-c(_G)_z_(qyaFOT0!T=JIKZD07ITsR z(li(44{0b;xu}0!Bg_31`(KfG_(iz6k+k$2TM%6sPD;kENJv;jKi|km>6v7Ri!f_F zBR3;;H4zI(drrttH95WPoqi%AiFt`2F707%5RjL>or9~0mpJV&3K7Kh&t@)K&@U1< zTX9+=buEyrqYDfK=H%w&rj@`1fk0v|P)iXVIfdWZ5#PjVpSig?iEwdwdU|qt@^L!4 zSaIav&%;T)iFKAYL2}u5`bF{DC6}bG2}>c5<_JbO8Otg_t|KyNT1% z{uK1*>o+}NUe|pjV2RBy) zAJ4z?AqGiZ{ZH2aBF_ur^h=z*{l9X$y2*PWZ1{al{*}{J&)W&cr2}(yba%0U$$KC) zq5C)G-K;JDgUvsC{-ezZ-T(LU|4o+DA94Ketbca@CiPb+{N^FCpW`ng>uBfbqU!{) zfJyL({c_3QoBs0WZ`4}WUNAc&IctOuToJyP5D*0a&h*cof8{j(i<6I+|L>fC>-ife z)I!A4(ZwF(CINABva_~;{0ulQ4+rS)QS_&AVqAZb{*C`vFo-BwySh2Lc>m3G!~p!7 z4!SVs|JwRThn@AWuy%sDxWaz=T%7g~r~f$*{tWYjWS@OdG^FiQ(Q7%#t|&>!f3WB<=G zAQnG8Bw^tKgSf$D8lu)jv| zUn2BZ?ficlq)Tn)D`0K ze=|gX3*7&jJjA&EQ?e2JtwYnr+REAiVkhTl;r_FP{XPw$|HsqtKf?UwlHW!BO~h|Y z1gN70Ld>7pQiK=Ehe-HfC8g-vlZD z_muK~=B?js`5XJ+pnsM~k)P?-&i!XH(RZ+Rli(HO`nTqPav{bTF|xm^#LsH>i{CYC8`(bq)Da6!D8vo&FA)FTqQBGr4d<^gRF;+1 za&fe@wu4Fh+!lb8Ml&Th?xIll`Vle1fR`CLXtvKmXp@=LO$^Syysx-yeWM1+CGFQkK?nsU_+qL zP6#$RnPgk!i$((JxJ3YfYm4(;=elI-)#8vlC@zB=B(x;g*WLCEg^sA2iI6)Y;`400 z18g5$kzH~qa+4`iKT~S5=NiyFZKv_6QopQprf#Nk%f$EV#m(m%uACcc&()7oAbcPI z1#S&oyjclI-Mre4`Fe92Fsgc7<6YV5`m0k46d{xggxp7wHp`F0mV~#Vp}c$2j*(uH z(@+;(YuP_)vL)vkb1*x0IR1`lpI%-^2}hk ze?N0=6bu8I(N!n~)qwRO)bK@A1OS52ffNp^UdXy4^ZsQuIV zKQ&3(kB*=QIeh>E;GYi{d;5M-3ke{Gzc{;9N#d8K3-SnBk1Yv>hd;g7Rzl#V)|%k8 z8!vbA3tCNnC0JAPHp{V9SfDFr`wpSsv?!}3 z)g|3LOQiPOXQw22htFe6^HXDxlPgf^Jn!IBV-K}xwd=4U^uaG_NC0}r!o!hTj7TaY z5zp5czpNVoCe7}(Q2SID(%_0p0Hn=&bMpJRe<=$=bg|($`Qc)rs9JIG|7JW2lN7=P zK;nHCw4b7?(U84KkH3GVMT1uu6BEGbQBK`6;}1Iw5kr#-9BYx2P%ERN2x{{{5|{Cy zb(*ePbL$}dj0z`2IbFnxZN1&3USdvT5tl?w)a)oIkMb18L0PzW4y5>{FfBmJ1m6xf z(UU;8M@1kqBp!2Jn21-;&jgvCQiZ=T=klPU z1QV{__z?s>KkAIVJ?;G%UL_Tk4rn#qM(H;d8e#4fgp9n0rZ5Nxcs5|8l*zO27lX{6 zyljW);y*p)_kg3)2v(8?B$>?G0v#by&R7ImnMOj|)>@C1-hi;T-&aw5$K#B9yTwBGY%ASQfF@zFu@J95P#iCbwTK{u zSFQS$T)uJ9a`m&r2UMu=&j|hne@q8vc+&Jd8uh+@2gga1qKww*p zq+221C|({4{LjKA%Jh%Dk~W~v`S8h zghzufB!N?qcdH7#Fu>pKfU<6?TDZ$-Z zXmIisxk+b&l~1R2Zpr%RvGhw{w-JeY7T85mcdYz4s7*&r#uXQUoyVI#!qf2DxCiec zZZKaa6%e5Ys=7kKsv?MaAXfi+$gT0_2v0Nz zNi_$5$0a=7&er!@!LxJGY0*nJi=UtR-(^!qu2( zXsjOf12!g4+1c z+ou#*+OLL9r?eA{`!$s{DGh8W@|f=Yu#ZVJ5b3^60pwu@mUuTS5)n;NP3K~cQfG{5 zi9-{`nJb9Mw8~Xa+M`bC*_Z}|G!WGZzk;&nTTOm^8;pF4|1O204@o_ zK5bv|h_#@1>%YCwN&l?49pIR)6zNFsGmEuGTT4Cyo%k}>zyD0K)8nmQb)1Y*l+>r# zADkls9XN_@%6ea)pC48||2|fAfjio@R{dr3^;QYlth`PPQx59`xsqMt9)#PafPE?q zISv50iH*0OKX0;a-b<90fbV_2hl~E#BQ1houg}j!9b0SpqSaTqDQ=_GQ)L8%)Q|}g z{$@l`NTX$pv@**;8sW{A^xt7YP=uFNdyoz zy2u>flN;EE?KC6MUy@Mkmtzj9>Brwgo1-fEa!X^L9n(^Jh-Q(BSg(*uwHfEWp+|!s zNPh0I>t{?rcbUEIY^}jo!E0NuP_?%3<89$tH-9hq@*^MsCn+;*Z=$rq%W zs-va%=0tW4n%fEVzq73eSS(9s3my9 z`0vqBtZ8V~O9Jh2e;fwfY5tHr_N2sJpzJ)xnaV7zE5wc>&|Xfcae2(km-%(6i#_>P zm{`*H7{8WMLH)`lm3nD~ThajHO$)e;Bve7ZdoO(~;L}JWN+5-9r_?{0J8CoQc;kKipL=AJ~~vVIT(ob zIP5_r!-*p2kwqCu%C|Z3W(*639Rn-B; z&;+$l$`Qf=ct}e*Cx|4okgMCFRVK8Az55=g($@(rFSen}umxrX0O?fq%7?%e=p0wt z&-5t#CLMbp2jruzUgUpQ&bqvL2?JG=!P%K$$=O{Ln5v&Ry50wDrzL|QGy?BC_7{P- zZcUAJzB>V;m&2D&_TS-n7qm;=lH(12qctPVn=hREpLX@|C`|e&dRd;b|9I*@;O>Vzv;wP^7;ryOeNi}HM`H;c53p(_O$POys3_Nj(6|gb) zIG$`IA-;ElA5zZkl02q!PsaYfPPei>4Qmg65xi{yxPMDyR7(mg&jW?7rE>Dg!!st(;V)g}li`)cU-?hguT~H$rPF0xiQPv5H}p z1oiO50`-1Lp&^SWF0^NLz$kMk;Wb*aLiIoi6$7>j%Y1F=&YuSgl+?)jlp;jn@0LPL z`h_C#ZF&#eA((L^llv$*xO;K}P3S1SiXh*EQH>8>5EognXz0|P2ul8JJ7fp&);fxq zl5#WjlGZA6~||DZu?PfuD^Ypm`WWnQ%lPXj#H%3x+3${TZBD7r{R9M zL_zTwZ6jUE`e3jpIxsajIGN#mbw&d>af-neDLUDbyGX5r_{0uS*=|3*jJo$yK&gzV zZF?#F#jxpmCU&}Td6(&WO2+Ni$P1Bgv4`+Bd_ZmCceiqINZSi}kLzbtfxbc*fRQjM ztxx1u2Qx04kFq${dSB*cwLNOaFR;}%M7U#}Hso?o0SpJGr6D_* zAF`|&P%Lu6!q{Gy!b70ykOM>O%Qzz-ybVI26ggC&w|oidRIcniUGwsU$4$qG|!lXkadL&Au*-n#=IzHnyb~N2;6QAya*OUsM#jdz5&=nq1fFV<=BP+6Vy~>DiSZtg; zxoAUq2LrxvPp3;jmsmlTyg?bR4`xT~L$gG=(TdmQWI{KmZW29`5E9B0(|ZQc)eiG4}9q z3{#424{N&bt1M~8rK4gzU!7S?y2FrGXd*)dT%vZ{1d)0>%nYuQ)j)XY1{-8w$iHTPDlSt)cQrG<<8 zS7mqBPUnGsTN%f8XRpa|QG9_h$xL&Yg-NKwLzY|*g+IXToq^du1sZ>^2fl<*a18 z_#GwcR4^M9IuR^^ce- z45h=q;d5ViVRMmuPlOfgQ{(&*Fwb6HV+E6a_MrjVk!&(lvhKu(@|zjkm{|hnR9;nt zhY-!V#a(&m~(6V&?c}OK~raSXOU3hVK&QN=!)&y2pjG4Mo zU4Em+SlziBFIRCnPNgyeDpDfP1E)5h#rRq77%1@PZf0ahc24qPkVbjvCMi!Noj zf*%r!+z*|0Fs26@ljAZ-Ct$bicUPGy4cny&tKIO^#*-`ymz#4H*l2DC2lNsGU*ZD` z5J!!Lsqgt&P~(dtI-fvd?7QBP9PFksBxggEL+ROY=|*?`FTOl(dbh;yeTrI0V+J{@ zQtlttec+yqBJH?_SBJ<0dx&l93~w>BPU$xl+{7zBV+MT{Sq*L4I=oKA+Lu@(A=<`9 zDEUe7A+Wu|F-PKi1`GEd=~2kKE$(@qlNbxXx~^66OTRm>PFawXA7GU{RX|ZriN&yx zVyD9)Ii&&nc_8Q@Stm0HFnncs8h{Ef*0BKrYT|qIq}o;V|RrRr(NRnd0)tq z?PGJ;eUm8Vp?|hu=K!j`u7a&Mn_Py~c zYWT#U>4I?9aNr!$|kMo_NDndMH z5+Trxi+8&~Esmsdl^T3KwFPb+W2~c%(e@07*~TK4Ydx1^S2-g-Smy&Bgad3eh{GYG zp-q-&Q6+{1WNioQgx_oKMbHZCB&1C(XuQ|2CEXA20Sja7%d~)_zs!-$GWiAtG%llr zD#P$oqix=rA|di0k=iOJpy)ghTNg}ZlrkH6Rr#fwW-<-NbvG>Qq!!+0CN-C0$Bl1| z&47_mW;Du==8vEHl5Jh}=M9@8>){i{1Ee;=dMd#KdzAdcS;FbVP_S&QKp!DlFj)tL zg@&9mg<>#1uq>Az*8)~e%8l`*1K7n^&8J?O=!>20>XQl>X!nj(G%>FNtJ$HM=Rhr- zjPw_iCQ6$^ok83f$Uk<#SDx|ns}9Wyn%d59?}eG}5r9%u!fo33_EA4=_>8F#)a(Un z)$WhK@)`j(tdu@%%m1nZp!d~&;y$0)FDuG@9?4tKF~*YaBcre^0K^Y1Auo8(6JEb^ zxAe`kZc5(ACE1>}CZ6iiLFattEKS~-5Acoc^$Uhaus&U_$6*CT!aeAs+(y+ebcHvJ z3;BURgn&tmWd+m;jZYrSQa+r-s3YA#lipNbV6@nx=Saq$2+YMs99klwxX~1UaxnjCA9)zZMyS({XkGTx<&DUIo88%6It%tFRnEh4uy08`kdy2XiLzKCwc#}_3n zM-%ycDk&|{r^*&O0nEa)h*dui9r@_J!CYMKPWpEaH27j&3L=T95hmyg1V1xP?o$$6#6qZHCL2aF1olNW0a#BmSH zm@%gcbygQb+D$Q2BC`g{2qPQMPvSLz)b0aeuw)!Fk(+{?*SgVE;=rMvt*(lv~iU?<#(kgS!Yrz@q;LAz2qe}IL^uU8EQ;G0b3c4oMCw~ zNtLxB^%Y{&>w2HL3!M99YSPwUP?1STS@4;XoziJNnyk+j|MViFyZG(qwo@gaX}reT zJk3zG<^@E5KUwF#wG;oMUetwv<Uul`6(um*j$PxSGUgMIuJ4Q{Zry0MCA2t&g zCU81}n^JPf7@K>w|;PWCrht z@^CiNDrXNvB3izSN5%%6X2-v_dnDz~o2eM%pNd{sO&<8-I_}Y4+^*y&^xeMX_Hz(h zkbRnc#S*-RG@}q&wpa<{18yJ_0rwVP((2TNnX)kju7&_NXneQnN`h6?xr zC60@5Wzr1Z*Z5b}>PZFgCxVzFBFPoChJ6+UNTDShpJ$mh98XNT5EYX_XefN4WZ0u| zmbl8R^kO#RORf*9EP6^*|uK;n=<%%+t#b= zOp;o_54ax}78j#D(s_UnSnrnTf&>xi#}|aY73rTrGoGPjdG(}X7M^S#YD#0qb)UB< zY9jXaaX>a63u}b)z-{j(e@AjUb95;sy>+|QPKo}3MRobN30DWiNgcCzbVV?8yX;k;K)bM=VX5E~$FKWTLvhhSSW7N7CFu zytrTEPFfcGvGIW>CQLV%&Ik=llqBpk(9kj_7T@qHsq&^IUCCl!AYAw*4%f4f zzvm9uGuu5CR#m`s)|AxgDN#7I7!U|a_SzDqMR_Tve-A3u1v(V$wT9i&5P zV9xc>f;cq!VYPa&>OmUVqW{KV=KTz0Mw!`)PU;M~TN<-&jnI8=e>xy*#wYhnnYQoK z)xMd5sjQQS#TmeY3&S$43f?8P9!DuQ$EvYHdTG{#cPfsLp;`E$E!REgTrqT7Gs-~; zo|fQ`Gfl&_q)Xj+yQST(Tvu-;*azuvIP28VzQzk|=?n*dH}6ci}6`C z^Y7O=$DEUr;skc?zffnVXU{FN%0^iy-i67H73V~3tZ~5p97my=v>Rq@ zqsQy)0|10fq1T6$^)OJol|V}?C;6Xg8c?SDOURj(Ck$JgeZ;w>H4J=#$N#xDm!wPi z^-+0B!)wSKzZ?3n6o{z{U!PtY+Of?Qo=_gix zY4`$-BsQUPdlUBXZac4%OYq|*!u8ndpf=!lL&FFo>vzyZ zfP7cGL`}aeFr#x@14La-%>ez1nUWa``=FCFHY%H2p~Rd2Mqn|Kz7S&7L2yI!J+H`g z2y`1`Ja{$QHfud&uB+@HtvCB^;qiEA_D2HdbE1yn_mFB)l%D3;0rg5w7PdS6(WbCY z6}(I@x#nlWfTgKk%Mnm@!sBH7Z!E5e1J)ZRuG$iusOsff*&C@Mt9XsG`k`B-eU- z>IHw^)5Oi5{5}Q1hq!=#HhZ~1K_PDb@94hTAeKSr{@im3&GUrXQ_z_TqYH|pH0S|K z8K<8LB}tW|67;?=PY9u@b+b-`p!kTI7kM$Mp1x=Cv8g5Al1#z)3@-7fAEtA+ zLf@c7WWM;?zB;=~b>TL4l2sKQR(QljzY%jTWNirR7<_-8P*q>0VqJdx`R+XVQ$a6z zNxoP=#ZrW!Xr8iV3o%?lc^j(x0v*5lZ5-Cu+}+NLHudB@;nL-!`pXr)fi}f*;-ggMi@T78l+~$%bqN&ePy*DT8r$fZ4g%iw3|WBXu%ceVPgoo)Os8_7C-KF zguO4fZO5td5G#!;fLehP9NQf&TD;~akdgFudz)BB*=k$eCOR6# zZkqetu6g;{l_23D81B&9G~6?uczaECc`RFi*9RyV5&Q2a(Iw3-1;EHeP(`mum^=7Q(dr7=CUFi_PQ3*TwtYXw7)K%Qp#M1z9D{ zs0ypZlb;tn+1o-p&^@OBc5q(cvD~#B4r9NpT*v(M+{TPQ-g#an{RV@bD&%zutctFg zz!p0+<5|sw+6wpXZBZE!`H@!^MxwJ#KfmmhK4NFU*tew0FT}1sl>cS)Hi?G1RQ2)Z zK)6_ikMHf11ukK=6xBT%#I%=0nX|21u9)m(aikIH4Y@+K@}@gicrz&MKM++#gfYI5 z9@mZk{CJKA^zw31!f4M}cSxdKktzj>C4RXb0| zyo^|T*Lqy^NE=5QMw-zwJ7GcRKVM79+kc|xA+~?c<~yym`IdlMLXuwKB<7qUGx8$L~}LW5Txl`o`|>J_TSQo_qEEJYuk|yqjS0%4fRpTavB+ zZddB*Tdb%?jN+2V&vclcQLy>l4vsdTtcstrY-M3c*~}K5B4HaJmr)je#0RGs;`6f= z^ji(5Cxh?z((qmFwy6oIMH78#DGe1r+Vp-Wx;k+7WOE(!ykuc2$e#W2xFM^4*cCwG zkr7+uTybrfuX)8fcfF5|u6k%bWycHgSC1e)C`FT!ux(nCtxtyNtg7GxvaKr&$f-JveVL57sQFn6vloGr9w$jGV%{qI2R5okG0*PhT*O` z`;Yac%pL}&5(!x|=u0!%7fSgux_QXT+H+rBlu^CdFkIjwhOb>4lTI}GoJG_`6MFde zY>MRaa@G0BgDFcV%SW|U$WYN*OR3lUbtQEU;cq437u41P9#w^5x zI;Gb419;&5gD*KbZ-XLic6proO$+wDV>?mH>)b#sJ`q7e<;n@r)Q^||~($-*4Pb@%%HJo49*J8_1qQDGSpk;|yI z3SE)~b`_&1L`nGj@Nt)Q;{HG@_YQ~;@#}uNg3r3qy|J~&YUp%dd!GhBlQ5y=!BKoV zL2)b2@c@UGL&C0(yn^kAPQ3VymiV2)O_Vc?i^%lcria*aR5^@5!q2Vg z#I2WID%Ui-Y2A6ru5Mv)!}z>5^oteOxo-DkkL^-}M@BgLWtiH$DYj+M$#SMGmlloa z!i06B`zH4a4@lX=JHc3KrG48A?BxYS^vcq&D(u}A<6Vxf$5s0kvTdd3EmGOvC>!n< zMhR+?3kur~#_2Ue`WXDo+NhG#Z1hJ$b4vGcZ&iKBhDa0$Q+db~sB_EukFXKPi$;Df z@;=SO4X@+DQO+I)H@DUiX`NAxhp{>*xq&L@tDLoB5c{NN^2;E0CSgjtg*o+I1=?Uf zA?!~=wdU%h8*P+5Sa=S`v)6sJ2Q~DdK7+>(z3#N6le!h>v>Xa{+Q%M#wk*3OYd_!h zS#;y=%vCaD)*pG3-5!;N)QG9qOxC518r=u2X=FEAibH?-wMhuX&>>Eq!d*Vpu!1D?cB(9E@ysz1pDc)~h3wBczTU zo}#ZCVF;xv0BU8+qvV4J2)lVi>6r@)UG19lSKsliA4c$>+@}#ksq|hjnjk(-6Dy24 z1$$g>;n;@`KJpf!?{EoR$9$YJA&sK%PblbOI%?`NPTQoX(-6_ASH_chTjX{%{Z-eS zdD<2>6EJ_B#&}ioPBNz){Q=Gqr9GP6VEMN_77moPKxS2__4NefL9#=$ls5i~@N{lJ zNv2_a9-I3B4Q?Oggia=cJMIf(<{{@Bf5MxS$=amMJ?Tx<+qi*}T9pKi1Ii?SO$Rf^ zwMLMgUhe*0SjO4??pGYv${)|S(UZ1HCqpzja45lXEp}l`-i1f)7A6m(SGYThzf-iV z(?967V{1#^pH|bD!!Mxp5--DF_TPa`Oa+3hLz++l8-BYbi#h5zP*JPhft1vbcjvJO zts@5AG1ld!8xX!wzCu>M0H4f^<>!1j-D<|Ty5J+kX`4-b8K0-2A)reCPM^_{rf4!6 zTkxJ_0_vPC5v?Re?HeDnWk8p;Q`|gl9Y?DISM)qo=EoX!3;IOh*rvU6m8RIueJXy4 zpHTiF&KNS&f^fuhzC4mru0q0Z)ORbLH#B`g^P`216IELUG;1nZTJ2|_7T;bm4>Os1 zDb$eaYf@%2m(KILSA5iY2JxEdF*={`>eXC#Dm;4mJYg*EOM%)-!+TV^$ZWUpmL6Ub zPP1~cXWXgeD30BwV=GvM?-UI%!{; zpo51?ixEF;&bb@%>rrh7lRZpa)&%HH8t0WZEyA|cnVO070|YrBQm<&KzAlcNUi9iu z?|K>l3O=(ai6{%ombmVBDy`7fH&!cL>+@F1OHhK=5&P?enWnjdEBTLwdo z^Oi`TzlqR(IiKe;&575aDK_V7cui@Pb1+c_omjmgxf#6IU3arOw!)oxZa3)D@bpHD z-wTAhU~A9jvbwIEMwmfCI_)<9O06_|L`RlV;fmYTGMCfhia|Q1mSVnHcXSDBJPXuz z+jDP^gf`dNoYj`0sc$RV=?$RY>Dv6Jc~rvfhw|2YRjv9yz1u?Fs4Pw|BpqIZ%aB;y z0o0rU<$el&HU?h-QDspB=hUIsAHfH;Y`dDG-D5M*xxqu3ii8Dl^lBW4Fw^K3i&4Dar)7~8io^wm0KQ^yI?23*#Rwq|TKD+Scd`pe#t zYG|y^MolET(Wh&#wm5_qxlg~o%d|$EME@vIV-BT}k7z#VfDoo>qsB`lFG`suwDdgy zMy*ej8c17ZiU>!6k|*b*oJxGiWHa=PqQVl>H}w-nUJ5$Uc=&7`@<-xOGQNtWr=eq* zyJv}bn1vMhzC-n`92(zDFLn+#>8vfXPX^7lNmnUT(!`gzCPpe#eFINYZ{=?&Y!$oR zTFfa4;~VUW*%}K%QKee?JjO$&VpMOygvhcA$=o6xJx6thlVCVG>f;o*=_ccpWTrSd z&4E%bf#^pJ`PXl-ol9pUvwd-vU((FzLG(->q(UD#j)a#6Nz9Qh8b0D8)sgq2IfP9T3zuQ#H6IHJPS-5GW1avMEZ0eWfW4}8| z!69JAdlGa(Orx}wE5Zu(>zhd&NEnLY1N58rsZ>1PHpIkQnZJ?H+CkAgD`<}&k+I>j zkBXVt2c4k7=VZ@fA;$-10p`_|gAnmo{AU}ejl#j%xg+@84_p@FB5-+9#8z_dZF0*$ z+D+mJ?}TC`)}D|T??z)wf{f2s4+m!+9<)^7xU>eR;%YtDP(%X0Y@Q`WOW4|d6#|F<~qBE?p9guN$0vkEca4~r1 z^*$ef5uIy8c7v+FTsrsdO-I%Da^=p-O~lhR`JeA4-)DfazbGfHkbSBBq4JTPJ#PL- zO?5?&7vK8{i*Rl|ZM1`3fth54eVg806@PIo({uhq4e9vIIZ-p4WOB9)!>Ce<*|^yCQ7}CfDi6 zC5o8)?D?bw^<#4sg}Mx?q_8=F;d8mJz7|}{2Qz7LoSh34J3YJ_#v%BEosYpX~jqOwYa^kIEV+u zdp4_QGZ?usC2e?|>k9+gO>OMhw%&tDs$?|0?t^T;*B2|H%2b5jI~Tv$%akujkr6w8 z_&}k`>cu)FG3`2yf0WT5H7YhCw1(0k0!6$n4Z`Vl~QT4IrQ!#i72ThodmLNGP#qrM7 zDeQ{xjEkLo;bD9r$*wqi_$EDHBTH7I)i}FtqWNYmw2=PA6b=73p{Ve>$&ar0$uyKf zM&FsqDGjQGqGkv0!jsVpS+PoEDO{ARhDtSTb(2R9{PqoYwuD?B1r~-tB~lo$QxXU! zqe+Kq{qh-QvQ$RJYXJjCvhUPl5x?l5QQ-m5eM~W= zEU4u&H0561Y80Rau z`;RUSi0L0=@3W&W1>aS>rEWU=kc^F_G9|x;Z-fx^UGndmki08TEZC z{mSU+&{4WsIAAoa`i)Np50}+odM759xb%e5mi?WPHwqorn!@U=9h%QVa1(QYvZ2mH zp|`S)^UcBU*%!d2)xKwXBG{Q(*jt9*FRFVX&-g-Vz996Y`XBKgLbef@Zfdix5Ha&_}c4Dss)+1W_5QxL`jU>_K<55Xr1 z__)wlFh$>A4t2FbfKjM!GT=Uhc%V06=Z0Je1-4zQ>b4|FQ!&UVUu$u7o7&A8;HqfP zpPpOUd@s<;N|}Q7y~lS56U6SKQ#v1**PKsOhgE5RlX=c9Fu}wk&bQG_{Wy?Tlqx@o zbT(8uFF}p|4BIfi?Tzs{;60YmzTCcib*4ij^Law%>BaIb596a-Ih0c!4As$lcx%dm z>p)d=sGvP7+Wv{IVV zf>xyWm>NCg{5^%cXuL}Mk&V{C$K4>MevF+5MVl-8FzoaD>MUzo^Tv#;U)Gp0r z*sw9avbOrcXgJZiGaCudqft_$_X$mqyc-dhT8m`~SlubE6($Nr)HkF;STre{Vsc%# zH68}jJL4ShTU~pJ@J^8nNb~JR{bEZ0rKvvUCba4-U#d&@J}tEDCA%-n19zK)HnaH^ zQh$56sw7;aS9vZH%_~A)cK@u!vAKUz`W?)D%&($nhvO zlG*wZKSap_e>2v{g%0iAwR~+-bc}xR^f|?uV9PTbl2P1a66*TC^0!;8O}wiF=8eE8 z!iv@E(m;uI-35x1HHJKeVvSht5(V50!`2+UuugCbc^bC$iPd1r$jdBC*^se)xpCqv znnE!Op{Ql;@|G&4{b0D-M)jHQWWydUezwo*?Up=ZfE^Kp0Mqy339i|z?90E!LwwtRf+Fomg2}yoM>0r&et7$tdtq7sf8$ z)b4jz=v{pJ+Nl?kgyC$|na_2vL;4MtF#%2Tc0*cOAH?-3Jqd~{nSD1PO`ND2fP#k7 z1<{W!YZm3+RH$E;d8ks=r-+uSJMekN`VU)b z14Q5&o1I7dGEjmWY;j*aQG7lH`$m6SWFL0Y=T0xrSQdcz#mxR&`+Ai(q{gk`+gXC8 zjbd8Q$yLu2gYDyNr6f%py8!I1gDx}UKq&`vFp4E34JqkVV%Y099s*~MEXpfgmy6RQ z4i#XH-+g>4nJMgc5v+R`-#rInm?2I?C9MO9+KF<%QgV6~5cyn%*==>%U8`7|*OAw8 z(S_kF@gM7Y+MPYXj$X<#^7_L6W+^!{EmN za+8GYjtFYr8 zn8xefF=^4P%Q*_?yrFM{m*Zp!W9
|^YAA0l1zA>#>UdXfDT03Koj~b%D%S;@ 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 U?#;}jzx;STZKHejcO9SqABM2aq5uE@ literal 0 HcmV?d00001 diff --git a/backgrounds/sky_warm.png b/backgrounds/sky_warm.png new file mode 100644 index 0000000000000000000000000000000000000000..9d9f184110acc135d33f68092fea2ed81fed3382 GIT binary patch literal 33927 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 literal 0 HcmV?d00001 From 06fb3cf501c7e602024aad6a41703b4831ff0102 Mon Sep 17 00:00:00 2001 From: Cube14yt Date: Fri, 12 Dec 2025 16:30:40 +0800 Subject: [PATCH 13/38] Update mobile_keybinds.js --- mods/mobile_keybinds.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/mods/mobile_keybinds.js b/mods/mobile_keybinds.js index 9afbeb6d..f91c8079 100644 --- a/mods/mobile_keybinds.js +++ b/mods/mobile_keybinds.js @@ -1,16 +1,24 @@ (() => { function runKeybind() { promptInput("Input the keybind you want to run. (e.g. KeyA, Digit1, Backspace)", (keybind) => { + if (/^[A-Za-z]$/.test(keybind)) keybind = "Key" + keybind.toUpperCase() + if (/^[0-9]$/.test(keybind)) { + setView(Number(keybind)) + } if (keybinds[keybind]) { keybinds[keybind](); } }) } - if (isMobile) { + function loadButton() { const keybindButton = document.createElement("button") + if (!keybindButton) { + setTimeout(loadButton, 100) + return + } keybindButton.id = "keybindButton" - keybindButton.title = "Change static mode" + keybindButton.title = "Input a keybind" keybindButton.classList.add("controlButton") keybindButton.onclick = () => { runKeybind() @@ -18,4 +26,8 @@ keybindButton.textContent = "Keybind" document.getElementById("pauseButton").before(keybindButton) } -})() \ No newline at end of file + + if (isMobile) { + loadButton() + } +})() From ef9611f05977ef95407e690fe34cafa006a10846 Mon Sep 17 00:00:00 2001 From: slweeb <91897291+slweeb@users.noreply.github.com> Date: Fri, 12 Dec 2025 22:25:27 -0500 Subject: [PATCH 14/38] Create pressure_sensitive.js --- mods/pressure_sensitive.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 mods/pressure_sensitive.js diff --git a/mods/pressure_sensitive.js b/mods/pressure_sensitive.js new file mode 100644 index 00000000..81e91dbb --- /dev/null +++ b/mods/pressure_sensitive.js @@ -0,0 +1,20 @@ +runAfterLoad(() => { + let oldMouseSize; + let canvas = document.getElementById("game"); + canvas.addEventListener("pointerdown", (event) => { + oldMouseSize = mouseSize; + mouseSize = oldMouseSize * ((event.pressure || 0.5) / 0.5); + checkMouseSize(true); + }) + canvas.addEventListener("pointermove", (event) => { + if (!mouseIsDown) return; + mouseSize = oldMouseSize * ((event.pressure || 0.5) / 0.5); + checkMouseSize(true); + // console.log(oldMouseSize,event.pressure) + }) + canvas.addEventListener("pointerup", (event) => { + mouseSize = oldMouseSize; + oldMouseSize = undefined; + checkMouseSize(true); + }) +}) \ No newline at end of file From d681c0bf69a8d7581cc65afd11f2654c03cbcdb0 Mon Sep 17 00:00:00 2001 From: slweeb <91897291+slweeb@users.noreply.github.com> Date: Mon, 15 Dec 2025 17:48:46 -0500 Subject: [PATCH 15/38] Update ar.json --- lang/ar.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/ar.json b/lang/ar.json index 9e26dfee..a2bf3c15 100644 --- a/lang/ar.json +++ b/lang/ar.json @@ -1 +1 @@ -{} \ No newline at end of file +{"#lang.name":"العربية","land":"أرض","liquids":"سوائل","life":"الحياة","powders":"المساحيق","solids":"المواد صلبة","energy":"الطاقة","weapons":"الأسلحة","gases":"الغازات","food":"طعام","machines":"الآلات","special":"خاص","other":"آخر","states":"الولايات","heat":"حرارة","cool":"رائع","erase":"مسح","drag":"سحب","pick":"اختيار","mix":"مزج","lookup":"ابحث ","shock":"صدمة","paint":"طلاء","sand":"رمل","water":"ماء","salt_water":"ماء_مالح","sugar_water":"ماء_سكر","seltzer":"مياه غازية","dirty_water":"مياه_قذرة","pool_water":"مياه_المسبح","dirt":"الأوساخ","mud":"طين","wet_sand":"رمال_رطبة","rock":"صخر","rock_wall":"جدار_صخري","mudstone":"الحجر الطيني","packed_sand":"رمل_معبأ","plant":"نبات","dead_plant":"نبات_ميت","frozen_plant":"نبات_مجمد","grass":"عشب","algae":"الطحالب","concrete":"أسمنت","wall":"حائط","fire":"نار","bomb":"قنبلة","steam":"بخار","ice":"جليد","rime":"الصقيع","snow":"الثلج","slush":"طين","packed_snow":"الثلج_المعبأ","wood":"خشب","smoke":"ضباب","magma":"الصهارة","plasma":"بلازما","cold_fire":"نار_باردة","glass":"زجاج","molten_glass":"الزجاج المنصهر","molten_rad_glass":"زجاج_منصهر","rad_glass":"زجاج_راد","meat":"لحم","rotten_meat":"لحم فاسد","cured_meat":"لحم_معالج","cooked_meat":"لحم_مطبوخ","frozen_meat":"لحوم_مجمدة","salt":"ملح","molten_salt":"الملح_المنصهر","sugar":"سكر","flour":"دقيق","wire":"سلك","battery":"بطارية","cloner":"مستنسخ","sensor":"مستشعر","heater":"سخان","cooler":"مجمد","random":"عشوائي","image":"صورة","unpaint":"إزالة الطلاء","uncharge":"إلغاء الشحن","unburn":"غير محترق","smash":"سحق","filler":"حشو","lattice":"شعرية","gravel":"الحصى","slime":"الوحل","cement":"أسمنت","dust":"تراب","void":"فارغ","sun":"شمس","cell":"خلية","cancer":"سرطان","dna":"الحمض_النووي","plague":"وباء","worm":"دُودَة","frozen_worm":"دودة_مجمدة","flea":"قمل","termite":"نمل_ابيض","ant":"نمله","spider":"عنكبوت","web":"شبكة","fly":"ذبابة","firefly":"يراعة","bee":"نحلة","hive":"خلية","stink_bug":"خنفساء_نتنه","dead_bug":"حشرة_ميتة","human":"بشر","body":"هيكل","head":"رأس\n","bird":"طائر","rat":"فأر","frog":"ضفدع","frozen_frog":"ضفدع_مجمد","tadpole":"فرخ_ضفدع","fish":"سمكة","frozen_fish":"سمك_مجمد","slug":"بزاقه","snail":"حلزون","burner":"موقد","superheater":"مسخن","freezer":"مجمد","pipe":"ماسورة","pipe_wall":"جدار_ماسورة","mixer":"خلاط","grinder":"طاحونة","fuse":"فتيل","ewall":"جدار_فتراض","torch":"شعلة","spout":"صنبور","udder":"الضرع","bone_marrow":"نخاع_العظم","bone":"عظم","ball":"كرة","balloon":"بلون","antipowder":"مسحوق_مضاد","antimolten":"مضاد_الانصهار","antifire":"مضاد_للحريق","antifluid":"مضاد_للسوائل","antigas":"مضاد_للغاز","vertical":"رأسي","horizontal":"أفقي","ash":"رماد","molten_ash":"رماد_منصهر","light":"ضوء","liquid_light":"ضوء_سائل","laser":"ليزر","pointer":"مؤشر","charcoal":"فحم","tinder":"فتيل","sawdust":"نشارة_الخشب","strange_matter":"مادة_غريبة","permafrost":"التربة_الصقيعية","melted_butter":"الزبدة_المذابة","melted_cheese":"جبن_مذاب","mushroom_spore":"جراثيم_الفطر","mushroom_stalk":"ساق_الفطر","mushroom_gill":"خياشيم_الفطر","mushroom_cap":"غطاء_الفطر","hyphae":"خيوط_فطرية","mycelium":"الفطريات","mulch":"نشارة","ant_wall":"مستعمرت_النمل","lichen":"طحالب","antimatter":"المادة_المضادة","plastic":"بلاستيك","molten_plastic":"بلاستيك_المنصهر","cloth":"قماش","cellulose":"السلي لوز","wax":"شمع","melted_wax":"شمع_مذاب","dioxin":"دي وكسين","insulation":"عزل","sponge":"إسفنج","bamboo":"الخيزران","iron":"حديد","copper":"نحاس","gold":"ذهب","steel":"فُولاَذ","galvanized_steel":"فولاذ_المجلفن","zinc":"الزنك","silver":"فضة","tin":"قصدير","lead":"رصا ص","nickel":"نيكل","aluminum":"الألومنيوم","tungsten":"تنغستن","molten_tungsten":"تنغستن_منصهر","brass":"نحاس","bronze":"برونز","sterling":"فضة_نقيه","gallium":"كاليوم","molten_gallium":"كاليوم_منصهر","gallium_gas":"غاز_كاليوم","rose_gold":"ذهب_وردي","purple_gold":"ذهب_ارجواني","blue_gold":"ذهب_ازرق","electrum":"ذهب_ابيض","pyrite":"بيرت","solder":"لحام"} \ No newline at end of file From e601e24d20ed7f8fb7d2c64befb07d6f0c136838 Mon Sep 17 00:00:00 2001 From: Mnem42 <177770058+Mnem42@users.noreply.github.com> Date: Tue, 16 Dec 2025 21:19:15 +0000 Subject: [PATCH 16/38] (zoom.js) Show border to canvas Display borders on the canvas so the canvas bounds are more obvious. --- mods/zoom.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/mods/zoom.js b/mods/zoom.js index 7a9e488e..af44a3eb 100644 --- a/mods/zoom.js +++ b/mods/zoom.js @@ -72,8 +72,9 @@ function gen_button(row, col, html, click, nopos, id){ function add_css(){ const CSS = ` - #zm_data_div { margin-bottom: 10px; } - #canvasDiv { overflow: hidden } + #zm_data_div { margin-bottom: 10px } + #canvasDiv { overflow: hidden } + #game { border: solid white } @media(pointer=coarse){ #zm_floater_container#zm_floater_container { @@ -159,7 +160,7 @@ function add_zoom_floaters(){ ) const speed = () => - (window.zoom_level > 3 ? 5 : 10) * // More granular at higher zoom levels + (window.zoom_level > 3 ? 5 : 10) * // More granular at higher zoom levels (pan_mode_sel.dataset.mode == "F" ? 0.25 : 1) // Increase granularity in fine mode container.append( @@ -199,6 +200,16 @@ function rescale(){ const y = zoom_panning[1] * (pixelSize * scale) gameCanvas.style.transform = `translate(${x}px,${y}px) scale(${scale})` + + const width = 2 / scale + gameCanvas.style.borderTopWidth = + `${zoom_panning[1] > 0 ? width : 0}px` + gameCanvas.style.borderBottomWidth = + `${zoom_panning[1] < 0 ? width : 0}px` + gameCanvas.style.borderLeftWidth = + `${zoom_panning[0] > 0 ? width : 0}px` + gameCanvas.style.borderRightWidth = + `${zoom_panning[0] < 0 ? width : 0}px` } function log_info(){ From 3b8da6836ebe48cc54b975d730c463e5421fc6c8 Mon Sep 17 00:00:00 2001 From: binhnguyen-discord Date: Wed, 17 Dec 2025 10:42:27 +0700 Subject: [PATCH 17/38] Add files via upload --- mods/toms_stuff.js | 308 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 308 insertions(+) create mode 100644 mods/toms_stuff.js diff --git a/mods/toms_stuff.js b/mods/toms_stuff.js new file mode 100644 index 00000000..e7f8a860 --- /dev/null +++ b/mods/toms_stuff.js @@ -0,0 +1,308 @@ +// made by me haha +// didn't expect that, didn't you? +console.log('oh no') + +// My Testing Stuff +elements.solid_element = { + color: "#287cb8", + behaviors: behaviors.WALL, + category: "My Stuff", + state: "solid", + stateHigh: "ash", + tempHigh: "2345", + tempLow: "-234", + stateLow: "ice", + desc: "Test solid" +} +elements.liquid_element = { + color: "#00eeff", + behavior: behaviors.LIQUID, + category: "My Stuff", + state: "liquid", + stateHigh: "steam", + tempHigh: "2345", + tempLow: "-234", + stateLow: "ice", + desc: "Test liquid" +} + +// i based this off of cell machine +// movers +elements.mover = { + color: "#2f00ff", + behavior: [ + "XX|XX|XX", + "XX|XX|M1", + "XX|XX|XX", + ], + category: "Movers", + state: "solid", + stateHigh: "molten_metal", + tempHigh: "2500", + tempLow: "-2345", + stateLow: "ice", + desc: "Right mover" +} + +elements.reverse_mover = { + color: "#2f00ff", + behavior: [ + "XX|XX|XX", + "M1|XX|XX", + "XX|XX|XX", + ], + category: "Movers", + state: "solid", + stateHigh: "molten_metal", + tempHigh: "2500", + tempLow: "-2345", + stateLow: "ice", + desc: "Left mover" +} + +elements.up_mover = { + color: "#2f00ff", + behavior: [ + "XX|M1|XX", + "XX|XX|XX", + "XX|XX|XX", + ], + category: "Movers", + state: "solid", + stateHigh: "molten_metal", + tempHigh: "2500", + tempLow: "-2345", + stateLow: "ice", +} + +elements.down_mover = { + color: "#2f00ff", + behavior: [ + "XX|XX|XX", + "XX|XX|XX", + "XX|M1|XX", + ], + category: "Movers", + state: "solid", + stateHigh: "molten_metal", + tempHigh: "2500", + tempLow: "-2345", + stateLow: "ice", +} + +// converter +elements.sand_converter = { + color: "#000397", + behavior: [ + "XX|XX|CH:sand", + "XX|XX|M1", + "XX|XX|CH:sand", + ], + category: "Movers", + state: "solid", + stateHigh: "molten_metal", + tempHigh: "2500", + tempLow: "-2345", + stateLow: "ice", +} +// trashes +elements.trash = { + color: "#ff00ff", + behavior: [ + "XX|DL|XX", + "DL|XX|DL", + "XX|DL|XX", + ], + category: "Trashes", + state: "solid", + stateHigh: "molten_metal", + tempHigh: "2500", + tempLow: "-2345", + stateLow: "ice", +} + +// does enemies count as trashes? +// eh , why not +elements.enemy = { + color: "#ff0000", + behavior: [ + "XX|DB|XX", + "DB|XX|DB", + "XX|DB|XX", + ], + category: "Trashes", + state: "solid", + stateHigh: "molten_metal", + stateHigh: "molten_metal", + tempHigh: "2500", + tempLow: "-2345", + stateLow: "ice", +} + +// generators +elements.directional_generator = { + color: "#00ff00", + behavior: [ + "XX|CF|XX", + "CF|XX|CF", + "XX|CF|XX", + ], + category: "Generators", + state: "solid", + stateHigh: "molten_metal", + stateHigh: "molten_metal", + tempHigh: "2500", + tempLow: "-2345", + stateLow: "ice", +} + +// nuke cell +elements.nuke_cell = { + color: "#ffff00", + behavior: [ + "DL|CL|DL", + "CL|XX|CL", + "DL|CL|DL", + ], + category: "Special Cells", + state: "solid", + stateHigh: "molten_metal", + tempHigh: "2500", + tempLow: "-2345", + stateLow: "ice", +} +// some stuff +elements.rad_shrimp = { + color: ["#ff8484" , "#ff0000" , "#87ff62"], + behavior: [ + "XX|XX|XX", + "XX|LB:radiation|XX", + "M1|M2|M1", + ], + category: "food", + state: "powder", + stateHigh: "cooked_rad_shrimp", + tempHigh: "250", + tempLow: "-234", + stateLow: "frozen_rad_shrimp", +} +elements.cooked_rad_shrimp = { + color: ["#ff0000" , "#ffc278"], + behavior: behaviors.POWDER, + category: "food", + stateHigh: "ash", + tempHigh: "450", + stateLow: "rad_shrimp", + tempLow: "20", + temp: 250, +} +elements.frozen_rad_shrimp = { + color: ["#6193ff" , "#0044d6"], + category: "food", + stateHigh: "rad_shrimp", + temp: "-234", + temphigh: "20", + stateLow: "frozen_rad_shrimp", + temp: -234, +} + +// more sand +// lol + +// fresh sand +elements.fresh_sand = { + color: ["#f2e2a1" , "#d4b95b" , "#ffeb99"], + behavior: behaviors.POWDER, + category: "land", + stateHigh: "molten_glass", + tempHigh: "1450", + stateLow: "clay", + tempLow: "-234", + desc : "Sand fresh from the beach(jk its just retextured sand)." +} + +// blue sand +elements.blue_sand = { + color: ["#a1c4f2" , "#5b9ed4" , "#99caff"], + behavior: behaviors.POWDER, + category: "land", + stateHigh: "molten_glass", + tempHigh: "1450", + stateLow: "clay", + tempLow: "-234", + desc : "Blue sand from a cool island." +} +elements.wet_blue_sand = { + color: ["#6a89b7" , "#3c5f86" , "#7199d0"], + behavior: behaviors.POWDER, + category: "states", + tempHigh: "50", + stateHigh: "blue_sand", + desc: "so cool", +} + +// green sand +elements.green_sand = { + color: ["#a1f2b8" , "#5bd48e" , "#99ffd6"], + behavior: behaviors.POWDER, + category: "land", + stateHigh: "molten_glass", + tempHigh: "1450", + stateLow: "clay", + tempLow: "-234", + desc : "Green sand from money island.", + reactions: { + "water": { elem1:null, elem2:"wet_green_sand"}, + }, +} +elements.wet_green_sand = { + color: ["#6a9a78" , "#3c865b" , "#71bfa0"], + behavior: behaviors.POWDER, + category: "states", + tempHigh: "40", + stateHigh: "green_sand", + desc: "vro is not tuff", +} +// purple sand +elements.purple_sand = { + color: ["#dba1f2" , "#9e5bd4" , "#ff99ff"], + behavior: behaviors.POWDER, + category: "land", + stateHigh: "molten_glass", + tempHigh: "1450", + stateLow: "clay", + tempLow: "-234", + desc : "Purple sand from a magical island.", + reactions: { + "sawdust": { elem1:null, elem2:"confetti"}, + "water": { elem1:null, elem2:"wet_purple_sand"} + }, +} +elements.wet_purple_sand = { + color: ["#845796" , "#5b2d80" , "#b069b0"], + behavior: behaviors.POWDER, + category: "states", + tempHigh: "60", + stateHigh: "purple_sand", + desc: "ts so tuff", +} +// emeralds +elements.emerald = { + color: ["#50c878" , "#2e8b57" , "#a8e4a0"], + behavior: behaviors.POWDER, + category: "powders", + stateHigh: "molten_emerald", + tempHigh: "2100", + stateLow: "diamond", + tempLow: "-234", + desc : "A precious green gem." +} +elements.molten_emerald = { + color: "#7fffd4", + behavior: behaviors.LIQUID, + category: "liquids", + stateLow: "emerald", + tempLow: "-2100", + temp: 2100, + desc : "Molten emerald." +} \ No newline at end of file From efa7fa0cf3870bb76b400560d7461d6ce2a6fdeb Mon Sep 17 00:00:00 2001 From: slweeb <91897291+slweeb@users.noreply.github.com> Date: Wed, 17 Dec 2025 12:08:39 -0500 Subject: [PATCH 18/38] Version 1.13 - December 17, 2025 - Gizmos & Gadgets + Filter + Only allows the first element it touches to pass + Gate + Only allows pixels to pass if electrified + Only conducts electricity into itself and Wire + Canvas background images + Some presets and custom image URLs + Outline View (Press 5) + 2-wide and 4-wide brushes + Ruler tool in Special to measure lengths and angles + Manual element control + Shift-clicking certain element buttons prompt the user for more information + Erase tool allows only deleting certain elements + Cloners allow multiple comma-separated elements + Filters allow states (solid, liquid, or gas) or multiple elements + Sensors allow sensing specific elements + Portals allow the setting of channels + Void allows only deleting certain elements + Random tool allows pre-selected element choices + Explosion allows custom radius + Antibombs allow pre-selected explosion elements ~ Replaced LED Red, Green, and Blue with a single customizable LED ~ LEDs in old saves are automatically converted to the new element - Merged Superheaters and Freezers with Heaters and Coolers + Heaters can be heated to heat the same as a Superheater + Coolers can be cooled to cool the same as a Freezer - Removed Tesla Coil and Shocker ~ These elements in old saves are automatically converted to E-cloners ~ Drag tool is faster and smoother ~ Pixelated and colorful button borders + Translations for Swedish + Parital translations for Japanese, Thai, Arabic, Hebrew, and Viossa [Changes] + Pipes can transport pixels directly with Filter and Gate ~ Pipes no longer conduct heat ~ Brightened blue stage in Pipes ~ Incomplete pipes darken after being initialized + Oxygen can be used to dispose Molten Lead in reactors + Electrolysis of raw Salt + Smashing hot Cooked Meat releases Grease + Worms can compost Lettuce, Tomato, Corn, Pickle, and Bread + Rats can eat Spiders + Caramel and Herb make peppermint-colored sugar + Baking Soda can be made with Lye and Carbon Dioxide + Melting Ash with Dirt creates Tuff ~ Poison Gas no longer makes Slag from molten elements ~ Perfume can exist at room temperature ~ Acid doesn't dissolve Mercury or Diamond ~ Sulfur ignites when hot ~ All Cloud types can be smashed like Rain Cloud ~ Thermite burns into less Iron ~ Rock Wall deletes Light when placed over it for better eclipses - Removed Gallium-Sodium reaction ~ Copper Sulfate reacts with more carbs ~ Rainbow recipe uses Pointer instead of Flash ~ Bless deletes Strange Matter instead of turning it into Neutron + Static recipe + Malware can mix with some metals to create machine parts + Tea stains Paper + Unique fire color for Tin ~ Element buttons are consistently spaced on all sides ~ Recolored Pyrite ~ Acid has a smoother texture ~ Purple Gold is slightly more purple ~ Hyphae and Lichen are grainier ~ Recolored Random button ~ Recolored Balloon button ~ Recolored Confetti button ~ Recolored Hair button ~ Recolored Pistil button ~ Reordered colors in many buttons for consistent "lighting" look ~ Rice, Candy, Nut Oil, Dye, Rainbow, and Spray Paint buttons display with dark text + Firefly alias 'Lightning Bug' ~ Shortened hiding setting labels ~ Language should automatically be set from browser preferences (Untested) ~ Re-saving after saving automatically fills in name, author, and description ~ Save browser thumbnails are crisper ~ Steam Deck compliance [Technical] + 'onShiftSelect' element property, called when selecting element while holding Shift + 'onlyConduct' element property, list of the only elements to conduct electricity into + 'renderTool' element property, renderer function called once per frame while selected + 'updateOrder' element property, prioritizes (positive) or deprioritizes (negative) tick order (integer; default 0) + autoResizeRatio(height,width) function (e.g. (16,9)) ~ 'shiftDown' variable is set even when in a menu [Bug Fixes] ~ Fixed: Placing Humans with Replace Mode causes a gory mess ~ Fixed: Using Human on Cloners cause a gory mess ~ Fixed: Bottom pixels can be cut off on certain window sizes ~ Fixed: Line-drawing preview is opaque when using a theme ~ Fixed: Text in Debug popup can't be selected ~ Fixed: onClicked passes "unknown" when no element is selected, now passes null ~ Fixed: onMouseDown doesn't get called on mobile ~ Fixed: onMouseDown is called when clicking out of a menu ~ Fixed: Using Heat Ray on Stained Glass can make Rainbows ~ Fixed: Saves with pipes containing invalid elements fail to load ~ Fixed: Some Fireflies stay illuminated after hitting another ~ Fixed: Liquid/gaseous pixels with 'skip' property can be displaced by denser pixels ~ Fixed: Salt isn't considered a food and can't be eaten by Humans ~ Fixed: Corn can't be dragged or put into Pipes --- changelog.html | 104 ++ changelog.txt | 102 ++ controls.html | 2 + controls.txt | 2 + index.html | 1739 ++++++++++++++++++++++++---------- lang/cs.json | 2 +- lang/el.json | 2 +- lang/en.json | 1 + lang/es.json | 2 +- lang/fi.json | 2 +- lang/fil.json | 2 +- lang/fr.json | 2 +- lang/id.json | 2 +- lang/it.json | 2 +- lang/ja.json | 2 +- lang/ko.json | 2 +- lang/nl.json | 2 +- lang/pl.json | 2 +- lang/pt_br.json | 2 +- lang/qvp.json | 2 +- lang/ro.json | 2 +- lang/ru.json | 2 +- lang/sv.json | 2 +- lang/th.json | 1 + lang/tr.json | 2 +- lang/vi.json | 2 +- lang/xem.json | 2 +- lang/zh_cn.json | 2 +- lang/zh_hant.json | 2 +- mods/classic_textures.js | 10 +- mods/content.js | 55 +- mods/devsnacks.js | 33 +- mods/glow.js | 2 +- mods/survival.js | 1 + style.css | 121 ++- yandex_fc020778bf434546.html | 6 - 36 files changed, 1700 insertions(+), 523 deletions(-) create mode 100644 lang/en.json create mode 100644 lang/th.json delete mode 100644 yandex_fc020778bf434546.html diff --git a/changelog.html b/changelog.html index 28feec7e..5251b3a1 100644 --- a/changelog.html +++ b/changelog.html @@ -113,6 +113,110 @@ +

[Version 1.13 - December 17, 2025 - Gizmos & Gadgets]

+
    +
  • + Filter
  • +
  • + Only allows the first element it touches to pass
  • +
  • + Gate
  • +
  • + Only allows pixels to pass if electrified
  • +
  • + Only conducts electricity into itself and Wire
  • +
  • + Canvas background images
  • +
  • + Some presets and custom image URLs
  • +
  • + Outline View (Press 5)
  • +
  • + 2-wide and 4-wide brushes
  • +
  • + Ruler tool in Special to measure lengths and angles
  • +
  • + Manual element control
  • +
  • + Shift-clicking certain element buttons prompt the user for more information
  • +
  • + Erase tool allows only deleting certain elements
  • +
  • + Cloners allow multiple comma-separated elements
  • +
  • + Filters allow states (solid, liquid, or gas) or multiple elements
  • +
  • + Sensors allow sensing specific elements
  • +
  • + Portals allow the setting of channels
  • +
  • + Void allows only deleting certain elements
  • +
  • + Random tool allows pre-selected element choices
  • +
  • + Explosion allows custom radius
  • +
  • + Antibombs allow pre-selected explosion elements
  • +
  • ~ Replaced LED Red, Green, and Blue with a single customizable LED
  • +
  • ~ LEDs in old saves are automatically converted to the new element
  • +
  • - Merged Superheaters and Freezers with Heaters and Coolers
  • +
  • + Heaters can be heated to heat the same as a Superheater
  • +
  • + Coolers can be cooled to cool the same as a Freezer
  • +
  • - Removed Tesla Coil and Shocker
  • +
  • ~ These elements in old saves are automatically converted to E-cloners
  • +
  • ~ Drag tool is faster and smoother
  • +
  • ~ Pixelated and colorful button borders
  • +
  • + Translations for Swedish
  • +
  • + Parital translations for Japanese, Thai, Arabic, Hebrew, and Viossa
  • +
  • [Changes]
  • +
  • + Pipes can transport pixels directly with Filter and Gate
  • +
  • ~ Pipes no longer conduct heat
  • +
  • ~ Brightened blue stage in Pipes
  • +
  • ~ Incomplete pipes darken after being initialized
  • +
  • + Oxygen can be used to dispose Molten Lead in reactors
  • +
  • + Electrolysis of raw Salt
  • +
  • + Smashing hot Cooked Meat releases Grease
  • +
  • + Worms can compost Lettuce, Tomato, Corn, Pickle, and Bread
  • +
  • + Rats can eat Spiders
  • +
  • + Caramel and Herb make peppermint-colored sugar
  • +
  • + Baking Soda can be made with Lye and Carbon Dioxide
  • +
  • + Melting Ash with Dirt creates Tuff
  • +
  • ~ Poison Gas no longer makes Slag from molten elements
  • +
  • ~ Perfume can exist at room temperature
  • +
  • ~ Acid doesn't dissolve Mercury or Diamond
  • +
  • ~ Sulfur ignites when hot
  • +
  • ~ All Cloud types can be smashed like Rain Cloud
  • +
  • ~ Thermite burns into less Iron
  • +
  • ~ Rock Wall deletes Light when placed over it for better eclipses
  • +
  • - Removed Gallium-Sodium reaction
  • +
  • ~ Copper Sulfate reacts with more carbs
  • +
  • ~ Rainbow recipe uses Pointer instead of Flash
  • +
  • ~ Bless deletes Strange Matter instead of turning it into Neutron
  • +
  • + Static recipe
  • +
  • + Malware can mix with some metals to create machine parts
  • +
  • + Tea stains Paper
  • +
  • + Unique fire color for Tin
  • +
  • ~ Element buttons are consistently spaced on all sides
  • +
  • ~ Recolored Pyrite
  • +
  • ~ Acid has a smoother texture
  • +
  • ~ Purple Gold is slightly more purple
  • +
  • ~ Hyphae and Lichen are grainier
  • +
  • ~ Recolored Random button
  • +
  • ~ Recolored Balloon button
  • +
  • ~ Recolored Confetti button
  • +
  • ~ Recolored Hair button
  • +
  • ~ Recolored Pistil button
  • +
  • ~ Reordered colors in many buttons for consistent "lighting" look
  • +
  • ~ Rice, Candy, Nut Oil, Dye, Rainbow, and Spray Paint buttons display with dark text
  • +
  • + Firefly alias 'Lightning Bug'
  • +
  • ~ Shortened hiding setting labels
  • +
  • ~ Language should automatically be set from browser preferences (Untested)
  • +
  • ~ Re-saving after saving automatically fills in name, author, and description
  • +
  • ~ Save browser thumbnails are crisper
  • +
  • ~ Steam Deck compliance
  • +
  • [Technical]
  • +
  • + 'onShiftSelect' element property, called when selecting element while holding Shift
  • +
  • + 'onlyConduct' element property, list of the only elements to conduct electricity into
  • +
  • + 'renderTool' element property, renderer function called once per frame while selected
  • +
  • + 'updateOrder' element property, prioritizes (positive) or deprioritizes (negative) tick order (integer; default 0)
  • +
  • + autoResizeRatio(height,width) function (e.g. (16,9))
  • +
  • ~ 'shiftDown' variable is set even when in a menu
  • +
  • [Bug Fixes]
  • +
  • ~ Fixed: Placing Humans with Replace Mode causes a gory mess
  • +
  • ~ Fixed: Using Human on Cloners cause a gory mess
  • +
  • ~ Fixed: Bottom pixels can be cut off on certain window sizes
  • +
  • ~ Fixed: Line-drawing preview is opaque when using a theme
  • +
  • ~ Fixed: Text in Debug popup can't be selected
  • +
  • ~ Fixed: onClicked passes "unknown" when no element is selected, now passes null
  • +
  • ~ Fixed: onMouseDown doesn't get called on mobile
  • +
  • ~ Fixed: onMouseDown is called when clicking out of a menu
  • +
  • ~ Fixed: Using Heat Ray on Stained Glass can make Rainbows
  • +
  • ~ Fixed: Saves with pipes containing invalid elements fail to load
  • +
  • ~ Fixed: Some Fireflies stay illuminated after hitting another
  • +
  • ~ Fixed: Liquid/gaseous pixels with 'skip' property can be displaced by denser pixels
  • +
  • ~ Fixed: Salt isn't considered a food and can't be eaten by Humans
  • +
  • ~ Fixed: Corn can't be dragged or put into Pipes
  • +
+

[Version 1.12 - July 17, 2025 - Saves For All]

  • + Featured Saves tab
  • diff --git a/changelog.txt b/changelog.txt index 080661e4..cc1a4da8 100644 --- a/changelog.txt +++ b/changelog.txt @@ -4,6 +4,108 @@ See sneak peaks for upcoming updates on the Discord: https://discord.gg/ejUc6YPQ A fancier version of this changelog can be found here: https://sandboxels.R74n.com/changelog +[Version 1.13 - December 17, 2025 - Gizmos & Gadgets] + + Filter + + Only allows the first element it touches to pass + + Gate + + Only allows pixels to pass if electrified + + Only conducts electricity into itself and Wire + + Canvas background images + + Some presets and custom image URLs + + Outline View (Press 5) + + 2-wide and 4-wide brushes + + Ruler tool in Special to measure lengths and angles + + Manual element control + + Shift-clicking certain element buttons prompt the user for more information + + Erase tool allows only deleting certain elements + + Cloners allow multiple comma-separated elements + + Filters allow states (solid, liquid, or gas) or multiple elements + + Sensors allow sensing specific elements + + Portals allow the setting of channels + + Void allows only deleting certain elements + + Random tool allows pre-selected element choices + + Explosion allows custom radius + + Antibombs allow pre-selected explosion elements + ~ Replaced LED Red, Green, and Blue with a single customizable LED + ~ LEDs in old saves are automatically converted to the new element + - Merged Superheaters and Freezers with Heaters and Coolers + + Heaters can be heated to heat the same as a Superheater + + Coolers can be cooled to cool the same as a Freezer + - Removed Tesla Coil and Shocker + ~ These elements in old saves are automatically converted to E-cloners + ~ Drag tool is faster and smoother + ~ Pixelated and colorful button borders + + Translations for Swedish + + Parital translations for Japanese, Thai, Arabic, Hebrew, and Viossa + [Changes] + + Pipes can transport pixels directly with Filter and Gate + ~ Pipes no longer conduct heat + ~ Brightened blue stage in Pipes + ~ Incomplete pipes darken after being initialized + + Oxygen can be used to dispose Molten Lead in reactors + + Electrolysis of raw Salt + + Smashing hot Cooked Meat releases Grease + + Worms can compost Lettuce, Tomato, Corn, Pickle, and Bread + + Rats can eat Spiders + + Caramel and Herb make peppermint-colored sugar + + Baking Soda can be made with Lye and Carbon Dioxide + + Melting Ash with Dirt creates Tuff + ~ Poison Gas no longer makes Slag from molten elements + ~ Perfume can exist at room temperature + ~ Acid doesn't dissolve Mercury or Diamond + ~ Sulfur ignites when hot + ~ All Cloud types can be smashed like Rain Cloud + ~ Thermite burns into less Iron + ~ Rock Wall deletes Light when placed over it for better eclipses + - Removed Gallium-Sodium reaction + ~ Copper Sulfate reacts with more carbs + ~ Rainbow recipe uses Pointer instead of Flash + ~ Bless deletes Strange Matter instead of turning it into Neutron + + Static recipe + + Malware can mix with some metals to create machine parts + + Tea stains Paper + + Unique fire color for Tin + ~ Element buttons are consistently spaced on all sides + ~ Recolored Pyrite + ~ Acid has a smoother texture + ~ Purple Gold is slightly more purple + ~ Hyphae and Lichen are grainier + ~ Recolored Random button + ~ Recolored Balloon button + ~ Recolored Confetti button + ~ Recolored Hair button + ~ Recolored Pistil button + ~ Reordered colors in many buttons for consistent "lighting" look + ~ Rice, Candy, Nut Oil, Dye, Rainbow, and Spray Paint buttons display with dark text + + Firefly alias 'Lightning Bug' + ~ Shortened hiding setting labels + ~ Language should automatically be set from browser preferences (Untested) + ~ Re-saving after saving automatically fills in name, author, and description + ~ Save browser thumbnails are crisper + ~ Steam Deck compliance + [Technical] + + 'onShiftSelect' element property, called when selecting element while holding Shift + + 'onlyConduct' element property, list of the only elements to conduct electricity into + + 'renderTool' element property, renderer function called once per frame while selected + + 'updateOrder' element property, prioritizes (positive) or deprioritizes (negative) tick order (integer; default 0) + + autoResizeRatio(height,width) function (e.g. (16,9)) + ~ 'shiftDown' variable is set even when in a menu + [Bug Fixes] + ~ Fixed: Placing Humans with Replace Mode causes a gory mess + ~ Fixed: Using Human on Cloners cause a gory mess + ~ Fixed: Bottom pixels can be cut off on certain window sizes + ~ Fixed: Line-drawing preview is opaque when using a theme + ~ Fixed: Text in Debug popup can't be selected + ~ Fixed: onClicked passes "unknown" when no element is selected, now passes null + ~ Fixed: onMouseDown doesn't get called on mobile + ~ Fixed: onMouseDown is called when clicking out of a menu + ~ Fixed: Using Heat Ray on Stained Glass can make Rainbows + ~ Fixed: Saves with pipes containing invalid elements fail to load + ~ Fixed: Some Fireflies stay illuminated after hitting another + ~ Fixed: Liquid/gaseous pixels with 'skip' property can be displaced by denser pixels + ~ Fixed: Salt isn't considered a food and can't be eaten by Humans + ~ Fixed: Corn can't be dragged or put into Pipes + [Version 1.12 - July 17, 2025 - Saves For All] + Featured Saves tab + Discord Saves tab diff --git a/controls.html b/controls.html index 8742cb6c..7b47c61b 100644 --- a/controls.html +++ b/controls.html @@ -106,6 +106,7 @@ Thermal view 2 Basic view (No effects) 3 Streak view 4 + Outline view 5 Hide canvas H Toggle GUI F1 Capture screenshot C or F2 @@ -113,6 +114,7 @@ Close menu or Clear logs Esc Toggle Replace mode ; Pick element (Copy properties) Shift + Middle Click + Extra manual element controls Shift + Element Button Select secondary element Z Select previous element Backspace diff --git a/controls.txt b/controls.txt index 65bd0b80..6b41b068 100644 --- a/controls.txt +++ b/controls.txt @@ -29,6 +29,7 @@ F or F11 = Toggle Fullscreen 2 = Thermal View 3 = Basic View (No Effects) 4 = Streak View +5 = Outline View H = Hide Canvas F1 = Toggle GUI / HUD F2 or C = Capture Screenshot @@ -41,6 +42,7 @@ Ctrl + Shift + O = Reload last save Escape = Close Menu / Clear Logs ; = Replace Mode Shift + Mid Click = Pick Element (Copy Properties) +Shift + Button = Extra manual element controls Z = Select Secondary Element Baskspace = Select Previous Element diff --git a/index.html b/index.html index 1a10eee9..7eae4112 100644 --- a/index.html +++ b/index.html @@ -49,8 +49,8 @@ @@ -78,7 +78,7 @@ "availability":"https://schema.org/InStock" }, "genre": "Falling-sand game", - "softwareVersion":"1.11.2", + "softwareVersion":"1.13", "datePublished":"2021-12-15", "dateCreated":"2021-12-15", "gameTip":"https://sandboxels.r74n.com/controls", @@ -167,7 +167,7 @@ POWDER_OLD: [ "XX|XX|XX", "XX|XX|XX", - "M2|M1|M2", + "M2|M1|M2" ], POWDER: function(pixel) { if (pixel.start === pixelTicks) {return} @@ -189,12 +189,12 @@ POWDER: function(pixel) { AGPOWDER: [ "M2|M1|M2", "XX|XX|XX", - "XX|XX|XX", + "XX|XX|XX" ], LIQUID_OLD: [ "XX|XX|XX", "M2|XX|M2", - "M1|M1|M1", + "M1|M1|M1" ], LIQUID: function(pixel) { if (pixel.start === pixelTicks) {return} @@ -241,7 +241,7 @@ LIQUID: function(pixel) { SUPERFLUID_OLD: [ "XX|XX|XX", "XX|XX|M2 AND BO", - "XX|M1|M2", + "XX|M1|M2" ], SUPERFLUID: function(pixel) { if (pixel.start === pixelTicks) {return} @@ -269,27 +269,27 @@ SUPERFLUID: function(pixel) { LIGHTWEIGHT: [ "XX|XX|XX", "XX|FX%0.25|XX", - "M2%10|M1%10|M1%10", + "M2%10|M1%10|M1%10" ], SLIDE: [ "XX|XX|XX", "XX|XX|M2 AND BO", - "XX|M1|M1", + "XX|M1|M1" ], AGLIQUID: [ "M1|M1|M1", "M2|XX|M2", - "XX|XX|XX", + "XX|XX|XX" ], WALL: [ "XX|XX|XX", "XX|XX|XX", - "XX|XX|XX", + "XX|XX|XX" ], UL_UR: [ "M1|M1|M1", "M2|XX|M2", - "XX|M2|XX", + "XX|M2|XX" ], UL_UR_OPTIMIZED: function(pixel) { if (pixel.start === pixelTicks) {return} @@ -323,7 +323,7 @@ UL_UR_OPTIMIZED: function(pixel) { GAS_OLD: [ "M2|M1|M2", "M1|XX|M1", - "M2|M1|M2", + "M2|M1|M2" ], GAS: function(pixel) { if (pixel.start === pixelTicks) {return} @@ -353,7 +353,7 @@ GAS: function(pixel) { DGAS_OLD: [ "M2|M1|M2", "M1|DL%5|M1", - "M2|M1|M2", + "M2|M1|M2" ], DGAS: function(pixel) { if (Math.random() < 0.05) { @@ -365,28 +365,62 @@ DGAS: function(pixel) { SUPPORT: [ "XX|XX|XX", "SP|XX|SP", - "XX|M1|XX", + "XX|M1|XX" ], SUPPORTPOWDER: [ "XX|XX|XX", "SP|XX|SP", - "M2|M1|M2", + "M2|M1|M2" ], DELETE: [ "XX|DL|XX", "DL|XX|DL", - "XX|DL|XX", + "XX|DL|XX" ], FILL: [ "XX|CL|XX", "CL|XX|CL", - "XX|CL|XX", + "XX|CL|XX" ], -CLONER: [ +CLONER_OLD: [ "XX|CF|XX", "CF|XX|CF", - "XX|CF|XX", + "XX|CF|XX" ], +CLONER: function(pixel, chance) { + for (var i = 0; i < adjacentCoords.length; i++) { + var coords = adjacentCoords[i]; + var x = pixel.x + coords[0]; + var y = pixel.y + coords[1]; + if (!pixel.clone) { + if (!isEmpty(x,y,true)) { + let newPixel = pixelMap[x][y]; + if (newPixel.clone) { + pixel.clone = newPixel.clone; + pixel.temp = newPixel.temp; + pixelTempCheck(pixel); + } + if (elements[pixel.element].ignore && elements[pixel.element].ignore.indexOf(newPixel.element) !== -1) { + continue; + } + if (elements[pixel.element].id === elements[newPixel.element].id) { + continue; + } + pixel.clone = elements[newPixel.element].pickElement || newPixel.element; + pixel.temp = newPixel.temp; + pixelTempCheck(pixel); + } + } + else if (isEmpty(x,y)) { + if (chance && Math.random() > chance) continue; + createPixel(pixel.clone.match(/,/) ? choose(pixel.clone.split(",")) : pixel.clone, x, y); + if (pixelMap[x][y]) { + pixelMap[x][y].temp = pixel.temp; + pixelTempCheck(pixelMap[x][y]); + } + } + } +}, CLONE_ON_CLICK: function(pixel,element) { if (pixel.clone) return; if (elements[pixel.element].ignore.indexOf(element) !== -1) return; @@ -395,6 +429,14 @@ CLONE_ON_CLICK: function(pixel,element) { pixel.clone = element; if (elements[element].temp !== undefined) pixel.temp = elements[element].temp; }, +CLONER_SHIFT_SELECT: function(element) { + promptInput("Enter an element to clone. Enter multiple separated by commas.", function(r) { + r = validateElementList(r); + if (!r) return; + currentElementProp = { clone:r } + if (elements[r] && elements[r].temp !== undefined) currentElementProp.temp = elements[r].temp; + }, elemTitleCase(elements[element].name || element)); +}, STAIN_ON_MIX: function(pixel1,pixel2) { if (elements[pixel1.element].id === elements[pixel2.element].id) return; if (elements[pixel2.element].isGas) return; @@ -406,7 +448,7 @@ STAIN_ON_MIX: function(pixel1,pixel2) { STURDYPOWDER_OLD: [ "XX|XX|XX", "XX|XX|XX", - "XX|M1|XX", + "XX|M1|XX" ], STURDYPOWDER: function(pixel) { if (pixel.start === pixelTicks) {return} @@ -420,22 +462,22 @@ STURDYPOWDER: function(pixel) { SELFDELETE: [ "XX|XX|XX", "XX|DL|XX", - "XX|XX|XX", + "XX|XX|XX" ], FOAM: [ "XX|XX|XX", "XX|DL%5|XX", - "M2%25|M1%25|M2%25", + "M2%25|M1%25|M2%25" ], BUBBLE: [ "XX|XX|XX", "XX|DL%0.25 AND FX%1|M1%5", - "XX|M1%1|M1%2", + "XX|M1%1|M1%2" ], STICKY: [ "XX|ST|XX", "ST|XX|ST", - "XX|ST AND M1|XX", + "XX|ST AND M1|XX" ], MOLTEN: function(pixel) { if (Math.random() < 0.025 && isEmpty(pixel.x,pixel.y-1)) { @@ -450,22 +492,22 @@ MOLTEN: function(pixel) { MOLTEN_OLD: [ "XX|CR:fire%2.5|XX", "M2|XX|M2", - "M1|M1|M1", + "M1|M1|M1" ], RADPOWDER: [ "XX|XX|XX", "XX|RL:radiation%1|XX", - "M2|M1|M2", + "M2|M1|M2" ], RADMOLTEN: [ "XX|CR:fire,fire,fire,radiation%4.5|XX", "M2 AND CR:radiation%1|XX|M2 AND CR:radiation%1", - "M1|M1|M1", + "M1|M1|M1" ], RADLIQUID: [ "XX|XX|XX", "M2|RL:radiation%2|M2", - "M1|M1|M1", + "M1|M1|M1" ], BOUNCY: function(pixel) { if (pixel.bx===undefined) { @@ -833,7 +875,7 @@ SEEDRISE: function(pixel) { behavior: [ "HT:2|HT:2|HT:2", "HT:2|HT:2|HT:2", - "HT:2|HT:2|HT:2", + "HT:2|HT:2|HT:2" ], tool: function(pixel) { if (shiftDown) {pixel.temp += elements.heat.temp+(Math.random()*elements.heat.temp*1.5)*20;} @@ -851,7 +893,7 @@ SEEDRISE: function(pixel) { behavior: [ "CO:2|CO:2|CO:2", "CO:2|CO:2|CO:2", - "CO:2|CO:2|CO:2", + "CO:2|CO:2|CO:2" ], tool: function(pixel) { if (shiftDown) {pixel.temp += elements.cool.temp+(Math.random()*elements.cool.temp*1.5)*20;} @@ -866,12 +908,23 @@ SEEDRISE: function(pixel) { }, "erase": { color: "#fdb5ff", + onShiftSelect: function(element) { + promptInput("Enter an element to delete. Enter multiple separated by commas.", function(r) { + r = validateElementList(r); + if (!r) return; + currentElementProp = { filter:r } + }, elemTitleCase(elements[element].name || element)); + }, behavior: [ "DL|DL|DL", "DL|DL|DL", - "DL|DL|DL", + "DL|DL|DL" ], tool: function(pixel) { + if (currentElementProp && currentElementProp.filter && + currentElementProp.filter !== pixel.element && + !currentElementProp.filter.split(",").includes(pixel.element) + ) return; deletePixel(pixel.x,pixel.y); }, category: "tools", @@ -883,7 +936,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|XX|SW", - "XX|XX|XX", + "XX|XX|XX" ], tool: function(pixel) { if (dragStart === null) { @@ -908,27 +961,34 @@ SEEDRISE: function(pixel) { }, perTick: function() { if (!draggingPixels) { return; } + shuffleArray(draggingPixels); + draggingPixels.sort((pixel1, pixel2) => { + return pixelDistance(pixel1.x, pixel1.y, mousePos.x, mousePos.y) - pixelDistance(pixel2.x, pixel2.y, mousePos.x, mousePos.y); + }) for (var j = 0; j < (shiftDown ? 3 : 1); j++) { for (var i = 0; i < draggingPixels.length; i++) { var pixel = draggingPixels[i]; - if (pixel.del) { continue } - const x = pixel.x; - const y = pixel.y; - const [mX, mY] = [mousePos.x, mousePos.y]; - const empty = checkForEmptyPixels(x, y); - let bestVal = Math.sqrt(Math.pow(mX - x, 2) + Math.pow(mY - y, 2)); - let best = null; - for (const pixelPair of empty) { - const x_ = x + pixelPair[0]; - const y_ = y + pixelPair[1]; - const c = Math.sqrt(Math.pow(mX - x_, 2) + Math.pow(mY - y_, 2)); - if (c < bestVal) { - bestVal = c; - best = pixelPair; + let moves = Math.max(1, Math.round(pixelDistance(pixel.x, pixel.y, mousePos.x, mousePos.y) / 10)); + for (let m = 0; m < moves; m++) { + if (pixel.del) { break } + const x = pixel.x; + const y = pixel.y; + const [mX, mY] = [mousePos.x, mousePos.y]; + const empty = checkForEmptyPixels(x, y); + let bestVal = Math.sqrt(Math.pow(mX - x, 2) + Math.pow(mY - y, 2)); + let best = null; + for (const pixelPair of empty) { + const x_ = x + pixelPair[0]; + const y_ = y + pixelPair[1]; + const c = Math.sqrt(Math.pow(mX - x_, 2) + Math.pow(mY - y_, 2)); + if (c < bestVal) { + bestVal = c; + best = pixelPair; + } + } + if (best) { + tryMove(pixel, x + best[0], y + best[1], undefined, true); } - } - if (best) { - tryMove(pixel, x + best[0], y + best[1], undefined, true); } } } @@ -959,10 +1019,10 @@ SEEDRISE: function(pixel) { behavior: [ "CF|CF|CF", "CF|DL%5|CF", - "CF|CF|CF", + "CF|CF|CF" ], category: "tools", - maxSize: 0, + maxSize: 1, darkText: true, canPlace: false, desc: "Use on a pixel to select its element." @@ -972,7 +1032,7 @@ SEEDRISE: function(pixel) { behavior: [ "SW|SW|SW", "SW|DL%5|SW", - "SW|SW|SW", + "SW|SW|SW" ], tool: function(pixel){}, category: "tools", @@ -1006,7 +1066,7 @@ SEEDRISE: function(pixel) { behavior: [ "SH|SH|SH", "SH|DL%5|SH", - "SH|SH|SH", + "SH|SH|SH" ], tool: function(pixel) { // One loop that repeats 5 times if shiftDown else 1 time @@ -1274,7 +1334,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|CR:foam%3|XX", "M2|XX|M2", - "M2|M1|M2", + "M2|M1|M2" ], onMix: function(pixel) { releaseElement(pixel, "foam", shiftDown) @@ -1531,6 +1591,9 @@ SEEDRISE: function(pixel) { pixel.color = pixelColorPick(pixel,"#301B16") } } + else if (pixel.element === "light") { + deletePixel(pixel.x, pixel.y); + } }, canPlace: true, tempHigh: 950, @@ -1595,7 +1658,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|XX|XX", - "M2|M1|M2", + "M2|M1|M2" ], category:"life", tempHigh: 300, @@ -1673,7 +1736,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "SW:water,salt_water,dirty_water,sugar_water%1|XX|SW:water,salt_water,dirty_water,sugar_water%1", - "M2%10|M1|M2%10", + "M2%10|M1|M2%10" ], tick: function(pixel) { if (Math.random() < 0.01 && !isEmpty(pixel.x+1,pixel.y+1,true) && isEmpty(pixel.x+1,pixel.y)) { @@ -1810,7 +1873,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|EX:10|XX", "XX|XX|XX", - "M2|M1 AND EX:10|M2", + "M2|M1 AND EX:10|M2" ], category: "weapons", state: "solid", @@ -1877,7 +1940,7 @@ SEEDRISE: function(pixel) { behaviorOn: [ "XX|XX|XX", "XX|CH:rain_cloud|XX", - "XX|XX|XX", + "XX|XX|XX" ], temp: -5, tempHigh: 20, @@ -2005,7 +2068,7 @@ SEEDRISE: function(pixel) { behaviorOn: [ "M2|M1|M2", "CL%5 AND M1|XX|CL%5 AND M1", - "M2|M1|M2", + "M2|M1|M2" ], tool: function(pixel) { if (pixel.temp >= elements.plasma.temp || elements[pixel.element].insulate) {return;} @@ -2110,7 +2173,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|CR:radiation%0.075|XX", "CR:radiation%0.075|XX|CR:radiation%0.075", - "XX|CR:radiation%0.075|XX", + "XX|CR:radiation%0.075|XX" ], tempHigh: 1500, category: "solids", @@ -2126,7 +2189,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "SP|XX|SP", - "XX|M1|XX", + "XX|M1|XX" ], reactions: { "dirty_water": { elem1:"rotten_meat", chance:0.1 }, @@ -2180,7 +2243,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|CR:plague,stench,stench,stench,fly%0.25 AND CH:meat>rotten_meat%1|XX", "SP%99 AND CH:meat>rotten_meat%1|XX|SP%99 AND CH:meat>rotten_meat%1", - "XX|M1 AND CH:meat>rotten_meat%1|XX", + "XX|M1 AND CH:meat>rotten_meat%1|XX" ], reactions: { "water": { elem2:"broth", tempMin:70, color2:"#d7db69" }, @@ -2206,7 +2269,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "SP|XX|SP", - "XX|M1|XX", + "XX|M1|XX" ], reactions: { "water": { elem2:"broth", tempMin:70 }, @@ -2234,6 +2297,11 @@ SEEDRISE: function(pixel) { changePixel(pixel,"grease") } }, + onBreak: function(pixel) { + if (Math.random() <= (shiftDown ? 0.33 : 0.1) && pixel.temp > 125) { + releaseElement(pixel, "grease") + } + }, reactions: { "water": { elem2:"broth", tempMin:70 }, "salt_water": { elem2:"broth", tempMin:70 }, @@ -2272,14 +2340,26 @@ SEEDRISE: function(pixel) { "rime": { elem1:null, elem2:"salt_water", chance:0.075 }, "snow": { elem1:null, elem2:"salt_water", chance:0.25 }, "packed_snow": { elem1:null, elem2:"salt_water", chance:0.05 }, - "packed_ice": { elem1:null, elem2:"salt_water", chance:0.01 } + "packed_ice": { elem1:null, elem2:"salt_water", chance:0.01 }, + // electrolysis: + "aluminum": { elem1:["sodium","chlorine"], charged:true, chance:0.0025 }, + "zinc": { elem1:["sodium","chlorine"], charged:true, chance:0.015 }, + "steel": { elem1:["sodium","chlorine"], charged:true, chance:0.0125 }, + "iron": { elem1:["sodium","chlorine"], charged:true, chance:0.0125 }, + "tin": { elem1:["sodium","chlorine"], charged:true, chance:0.01 }, + "brass": { elem1:["sodium","chlorine"], charged:true, chance:0.001 }, + "bronze": { elem1:["sodium","chlorine"], charged:true, chance:0.001 }, + "copper": { elem1:["sodium","chlorine"], charged:true, chance:0.0075 }, + "silver": { elem1:["sodium","chlorine"], charged:true, chance:0.0075 }, + "gold": { elem1:["sodium","chlorine"], charged:true, chance:0.0075 } }, category: "food", tempHigh: 801, state: "solid", density: 2160, fireColor: "#F1E906", - alias: "sodium chloride" + alias: "sodium chloride", + isFood: true }, "molten_salt": { conduct: 0.1, @@ -2343,14 +2423,14 @@ SEEDRISE: function(pixel) { insulate: true, conduct: 1, noMix: true, - // ignoreConduct:["ecloner"], + // ignoreConduct:["ecloner"] }, "battery": { color: "#9c6c25", behavior: [ "XX|SH|XX", // shocks (adds charge) "SH|XX|SH", - "XX|SH|XX", + "XX|SH|XX" ], category: "machines", tempHigh: 1455.5, @@ -2362,14 +2442,22 @@ SEEDRISE: function(pixel) { color: "#dddd00", behavior: behaviors.CLONER, onClicked: behaviors.CLONE_ON_CLICK, + onShiftSelect: behaviors.CLONER_SHIFT_SELECT, ignore: ["ecloner","slow_cloner","clone_powder","floating_cloner","wall","ewall"], category:"machines", insulate:true, hardness: 1, - darkText: true, + darkText: true }, "sensor": { color: "#bebfa3", + onShiftSelect: function(element) { + promptInput("Enter an element to detect. Enter multiple separated by commas.", function(r) { + r = validateElementList(r); + if (!r) return; + currentElementProp = { sense:r } + }, elemTitleCase(elements[element].name || element)); + }, tick: function(pixel) { for (var i = 0; i < adjacentCoords.length; i++) { var coords = adjacentCoords[i]; @@ -2391,36 +2479,67 @@ SEEDRISE: function(pixel) { conduct: 1, movable: false, category:"machines", - darkText: true, + darkText: true }, "heater": { color: "#881111", - behavior: [ - "XX|HT:2|XX", - "HT:2|XX|HT:2", - "XX|HT:2|XX", - ], + tick: function(pixel) { + let temp = pixel.temp - airTemp; + temp = Math.max(temp, 2); + temp = Math.min(temp, 10); + for (var i = 0; i < adjacentCoords.length; i++) { + var coords = adjacentCoords[i]; + var x = pixel.x + coords[0]; + var y = pixel.y + coords[1]; + if (isEmpty(x,y,true)) continue; + + if (elements[pixelMap[x][y].element].insulate) continue; + pixelMap[x][y].temp += temp; + pixelTempCheck(pixelMap[x][y]); + } + }, + temp: 22, category:"machines", insulate:true }, "cooler": { color: "#111188", - behavior: [ - "XX|CO:2|XX", - "CO:2|XX|CO:2", - "XX|CO:2|XX", - ], + tick: function(pixel) { + let temp = pixel.temp - airTemp; + temp = Math.min(temp, -2); + temp = Math.max(temp, -10); + for (var i = 0; i < adjacentCoords.length; i++) { + var coords = adjacentCoords[i]; + var x = pixel.x + coords[0]; + var y = pixel.y + coords[1]; + if (isEmpty(x,y,true)) continue; + + if (elements[pixelMap[x][y].element].insulate) continue; + pixelMap[x][y].temp += temp; + pixelTempCheck(pixelMap[x][y]); + } + }, + temp: 18, category:"machines", insulate:true }, "random": { - color: ["#3e5f8a","#a334ec","#ea96f9","#a6ecf6","#70ebc8","#d9286b","#7eed91","#a18b30"], + color: ["#28BCD1","#9335E6","#E13294"], behavior: behaviors.WALL, + onShiftSelect: function(element) { + promptInput("Enter multiple elements separated by commas.", function(r) { + r = validateElementList(r); + if (!r) return; + currentElementProp = { randomChoices:r } + }, elemTitleCase(elements[element].name || element)); + }, tick: function(pixel) { - changePixel(pixel,randomChoices[Math.floor(Math.random() * randomChoices.length)]); + let choice = choose( (currentElementProp && currentElementProp.randomChoices) ? currentElementProp.randomChoices.split(",") : randomChoices ); + if (choice === "unknown") deletePixel(pixel.x, pixel.y); + else if (choice) changePixel(pixel,choice); }, onPlace: function(pixel) { - changePixel(pixel,randomChoices[Math.floor(Math.random() * randomChoices.length)]); + elements.random.tick(pixel); }, category: "special", excludeRandom: true @@ -2455,7 +2574,110 @@ SEEDRISE: function(pixel) { placingImage = null; }, tool: function() {}, + category: "special" +}, +"ruler": { + color: ["#C99255","#B6894C"], + onSelect: function() { + currentElementProp = {}; + // console.log(currentElementProp); + }, + onMouseDown: function() { + currentElementProp.startX = mousePos.x; + currentElementProp.startY = mousePos.y; + // console.log(currentElementProp); + }, + onMouseUp: function() { + currentElementProp = {}; + // console.log(currentElementProp); + }, + renderTool: function(ctx) { + if (currentElementProp.startX === undefined) return; + + let startX = currentElementProp.startX; + let startY = currentElementProp.startY; + let newX = mousePos.x; + let newY = mousePos.y; + let diffY = Math.abs(startY - newY) + 1; + let diffX = Math.abs(startX - newX) + 1; + if (shiftDown) { + if (diffX > diffY) newY = startY; + else newX = startX; + } + let triWidth = Math.abs(newX - startX) + 1; + let triHeight = Math.abs(newY - startY) + 1; + let hypot = Math.hypot(triWidth, triHeight); + let dirX = Math.sign(newX - startX || -1) * 3; + let dirY = Math.sign(newY - startY || 1) * 3; + let done = {}; + let coords = []; + + coords = coords.concat( + lineCoords(startX, startY, newX, startY, 1) + ); + coords = coords.concat( + lineCoords(startX, startY, newX, newY, 1) + ); + coords = coords.concat( + lineCoords(newX, startY, newX, newY, 1) + ); + + let nums = [ /* [value, x position, y position] */ ] + + if (triWidth >= 3 || triWidth >= triHeight) { /* midWidth */ + nums.push( [Math.abs(newX - startX) + 1, (newX + startX) / 2, startY - dirY] ); + } + if (triHeight >= 3 || triHeight > triWidth) { /* midHeight */ + nums.push( [Math.abs(newY - startY) + 1, newX + dirX, (newY + startY) / 2] ); + } + if (triWidth > 5 && triHeight >= 5) { /* hypot */ + nums.push( [hypot, (newX + startX) / 2 - dirX, (newY + startY) / 2 + dirY] ); + } + if (triWidth > 10 && triHeight >= 10) { + let area = (triWidth * triHeight) / 2; + let angle = Math.asin((2 * area) / (hypot * triWidth)); + angle = Math.round(angle * 180/Math.PI); + nums.push( [angle+"°", startX + (triWidth * Math.sign(dirX) * 0.25), startY + (triHeight * Math.sign(dirY) * 0.1)] ); + } + + for (let i = 0; i < coords.length; i++) { + const coord = coords[i]; + if (done[coord[0] + "," + coord[1]]) continue; + done[coord[0] + "," + coord[1]] = true; + + let color = i % 2 ? "#808080" : "#ffff00"; + drawSquare(ctx, color, coord[0], coord[1], undefined, 0.75); + } + ctx.font = (pixelSize*4)+"px 'VT323'"; + ctx.fillStyle = "#ffff00"; + ctx.strokeStyle = "#000000"; + ctx.lineWidth = pixelSize; + for (let i = 0; i < nums.length; i++) { + const num = nums[i]; + let value = typeof num[0] === "number" ? Math.round(num[0]) : num[0]; + let x = num[1]; + let y = num[2]; + + x -= value.toString().length / 2; + y += 1.5; + + x = Math.max(x, 5); + x = Math.min(x, width-5); + y = Math.max(y, 5); + y = Math.min(y, height-5); + + x = Math.round(x); + y = Math.round(y); + + ctx.strokeText(value, x * pixelSize, y * pixelSize); + ctx.fillText(value, x * pixelSize, y * pixelSize); + } + }, + tool: function() {}, category: "special", + maxSize: 1, + desc: "Use to measure lengths and angles on the canvas.", + excludeRandom: true }, "unpaint": { color: ["#ffffff","#000000"], @@ -2579,7 +2801,7 @@ SEEDRISE: function(pixel) { behavior: [ "CL|XX|CL", "XX|XX|XX", - "CL|XX|CL", + "CL|XX|CL" ], tick: function(pixel) { for (var i = 0; i < adjacentCoords.length; i++) { @@ -2675,18 +2897,27 @@ SEEDRISE: function(pixel) { }, "void": { color: "#262626", + onShiftSelect: function(element) { + promptInput("Enter an element to allow. Enter multiple separated by commas.", function(r) { + r = validateElementList(r); + if (!r) return; + currentElementProp = { filter:r } + }, elemTitleCase(elements[element].name || element)); + }, tick: function(pixel) { for (var i = 0; i < adjacentCoords.length; i++) { var x = pixel.x+adjacentCoords[i][0]; var y = pixel.y+adjacentCoords[i][1]; if (!isEmpty(x,y,true)) { var newPixel = pixelMap[x][y]; + if (pixel.filter && !(pixel.filter === newPixel.element || pixel.filter.split(",").includes(newPixel.element))) continue; if (elements[newPixel.element].hardness === 1) { continue; } deletePixel(x,y); } } doDefaults(pixel); }, + hoverStat: function(pixel) { return pixel.filter || "" }, category:"special", hardness: 1, excludeRandom: true, @@ -2753,7 +2984,7 @@ SEEDRISE: function(pixel) { movable: false }, "cell": { - color: ["#00ee00","#83ee00","#d6ee00"], + color: ["#d6ee00","#83ee00","#00ee00"], tick: function(pixel){ if (Math.random() < 0.005 && isEmpty(pixel.x,pixel.y-1)) clonePixel(pixel,pixel.x,pixel.y-1); if (Math.random() < 0.005 && isEmpty(pixel.x-1,pixel.y)) clonePixel(pixel,pixel.x-1,pixel.y); @@ -2796,7 +3027,7 @@ SEEDRISE: function(pixel) { breakInto: ["water","dna","dna","dna"] }, "cancer": { - color: ["#300b29","#5c114e","#870c71"], + color: ["#870c71","#5c114e","#300b29"], tick: function(pixel){ if (Math.random() < 0.01 && isEmpty(pixel.x,pixel.y-1)) clonePixel(pixel,pixel.x,pixel.y-1); if (Math.random() < 0.01 && isEmpty(pixel.x-1,pixel.y)) clonePixel(pixel,pixel.x-1,pixel.y); @@ -2838,28 +3069,12 @@ SEEDRISE: function(pixel) { nocheer: true, hidden: true }, -"dna": { - color: ["#ffe3e3","#e3e3ff","#ffffe3","#e3ffe3"], - behavior: behaviors.POWDER, - reactions: { - "fire": { elem2:null }, - "radiation": { "color1":["#ffe3e3","#e3e3ff","#ffffe3","#e3ffe3"] }, - "neutron": { "color1":["#ffe3e3","#e3e3ff","#ffffe3","#e3ffe3"] } - }, - tempHigh: 190, - stateHigh: "smoke", - state: "solid", - density: 1700, - category: "life", - hidden: true, - alias: "deoxyribonucleic acid" -}, "plague": { color: "#36005c", behavior: [ "M2|M1|M2", "M1|DL%1|M1", - "M2|M1|M2", + "M2|M1|M2" ], reactions: { "frog": { elem2:"plague", chance:0.05 }, @@ -2878,12 +3093,28 @@ SEEDRISE: function(pixel) { state: "gas", density: 600 }, +"dna": { + color: ["#ffe3e3","#e3e3ff","#ffffe3","#e3ffe3"], + behavior: behaviors.POWDER, + reactions: { + "fire": { elem2:null }, + "radiation": { "color1":["#ffe3e3","#e3e3ff","#ffffe3","#e3ffe3"] }, + "neutron": { "color1":["#ffe3e3","#e3e3ff","#ffffe3","#e3ffe3"] } + }, + tempHigh: 190, + stateHigh: "smoke", + state: "solid", + density: 1700, + category: "life", + hidden: true, + alias: "deoxyribonucleic acid" +}, "worm": { color: "#d34c37", behavior: [ "SW:dirt,sand,gravel,ash,mycelium,mud,wet_sand,clay_soil,water,salt_water,dirty_water,primordial_soup,blood,infection,color_sand%3|XX|SW:dirt,sand,gravel,ash,mycelium,mud,wet_sand,clay_soil,water,salt_water,dirty_water,primordial_soup,blood,infection,color_sand%3", "M2%10|XX|M2%10", - "SW:dirt,sand,gravel,ash,mycelium,mud,wet_sand,clay_soil,water,salt_water,dirty_water,primordial_soup,blood,infection,color_sand%3|M1|SW:dirt,sand,gravel,ash,mycelium,mud,wet_sand,clay_soil,water,salt_water,dirty_water,primordial_soup,blood,infection,color_sand%3", + "SW:dirt,sand,gravel,ash,mycelium,mud,wet_sand,clay_soil,water,salt_water,dirty_water,primordial_soup,blood,infection,color_sand%3|M1|SW:dirt,sand,gravel,ash,mycelium,mud,wet_sand,clay_soil,water,salt_water,dirty_water,primordial_soup,blood,infection,color_sand%3" ], reactions: { "ash": { elem2:[null,null,null,null,null,null,null,null,null,null,"dirt"], chance:0.1, func:behaviors.FEEDPIXEL }, @@ -2910,6 +3141,12 @@ SEEDRISE: function(pixel) { "yolk": { elem2:null, chance:0.01, func:behaviors.FEEDPIXEL }, "charcoal": { elem2:"dirt", chance:0.05, func:behaviors.FEEDPIXEL }, "straw": { elem2:"dirt", chance:0.05, func:behaviors.FEEDPIXEL }, + "lettuce": { elem2:"dirt", chance:0.01, func:behaviors.FEEDPIXEL }, + "tomato": { elem2:"dirt", chance:0.01, func:behaviors.FEEDPIXEL }, + "corn": { elem2:"dirt", chance:0.01, func:behaviors.FEEDPIXEL }, + "pickle": { elem2:"dirt", chance:0.01, func:behaviors.FEEDPIXEL }, + "bread": { elem2:"dirt", chance:0.01, func:behaviors.FEEDPIXEL }, + "toast": { elem2:"dirt", chance:0.01, func:behaviors.FEEDPIXEL }, "mudstone": { elem2:"dirt", chance:0.1 }, "permafrost": { elem2:"dirt", chance:0.1 }, "packed_sand": { elem2:"sand", chance:0.1 }, @@ -2949,7 +3186,7 @@ SEEDRISE: function(pixel) { behavior: [ "M2|XX|M2", "XX|XX|XX", - "M2|M1|M2", + "M2|M1|M2" ], reactions: { "blood": { elem2:null, chance:0.1875, func:behaviors.FEEDPIXEL }, @@ -2982,7 +3219,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|SW:wood,tree_branch,dirt,sand,gravel,clay_soil%5", "XX|FX%3|M2%15 AND BO", - "XX|M1|SW:wood,tree_branch,dirt,sand,gravel,clay_soil%5", + "XX|M1|SW:wood,tree_branch,dirt,sand,gravel,clay_soil%5" ], reactions: { "wood": { elem2:null, chance:0.04, func:behaviors.FEEDPIXEL }, @@ -3185,7 +3422,7 @@ SEEDRISE: function(pixel) { behaviorOn: [ "XX|CR:flash|XX", "CR:flash|CH:ash|CR:flash", - "XX|CR:flash|XX", + "XX|CR:flash|XX" ], reactions: { "dead_plant": { elem2:null, chance:0.15, func:behaviors.FEEDPIXEL }, @@ -3245,6 +3482,9 @@ SEEDRISE: function(pixel) { behaviors.FLY(pixel,function(firefly,newfly){ if (newfly) { newfly.fff = firefly.fff; + newfly.color = newfly.ffc||pixelColorPick(newfly,"#684841"); + newfly.glow = false; + newfly.emit = false; } }) }, @@ -3266,7 +3506,8 @@ SEEDRISE: function(pixel) { burnTime:25, state: "solid", density: 600, - conduct: 0.15 + conduct: 0.15, + alias: "lightning bug" }, "bee": { color: "#c4b100", @@ -3516,6 +3757,13 @@ SEEDRISE: function(pixel) { panic: 0 }, onPlace: function(pixel) { + if (!isEmpty(pixel.x, pixel.y-1, true) && pixelMap[pixel.x][pixel.y-1].element === "head") { + deletePixel(pixel.x, pixel.y-1); + } + else if (!isEmpty(pixel.x, pixel.y+1, true) && pixelMap[pixel.x][pixel.y+1].element === "body") { + deletePixel(pixel.x, pixel.y+1); + } + if (isEmpty(pixel.x, pixel.y+1)) { createPixel("body", pixel.x, pixel.y+1); var color = pixel.color; @@ -3552,7 +3800,7 @@ SEEDRISE: function(pixel) { }, related: ["body","head"], cooldown: defaultCooldown, - forceSaveColor: true, + forceSaveColor: true }, "body": { color: ["#069469","#047e99","#7f5fb0"], @@ -3944,7 +4192,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|M2%1.5|M2%5", "XX|FX%2 AND RL:plague%0.05|M2 AND BO", - "XX|M1|M2", + "XX|M1|M2" ], reactions: { "oxygen": { elem2:"carbon_dioxide", chance:0.5 }, @@ -4013,6 +4261,7 @@ SEEDRISE: function(pixel) { "jelly": { elem2:null, chance:0.1, func:behaviors.FEEDPIXEL }, "worm": { elem2:null, chance:0.1, func:behaviors.FEEDPIXEL }, "ant": { elem2:null, chance:0.1, func:behaviors.FEEDPIXEL }, + "spider": { elem2:null, chance:0.1, func:behaviors.FEEDPIXEL }, "frog": { elem2:null, chance:0.005, func:behaviors.FEEDPIXEL }, "snail": { elem2:"limestone", chance:0.1, func:behaviors.FEEDPIXEL }, "slug": { elem2:null, chance:0.1, func:behaviors.FEEDPIXEL } @@ -4036,7 +4285,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|M2%3 AND SW:water,salt_water,sugar_water,dirty_water,seltzer%7", "XX|FX%0.5|CR:slime%0.01 AND BO", - "XX|M1|XX", + "XX|M1|XX" ], reactions: { "fly": { elem2:null, chance:0.5, func:behaviors.FEEDPIXEL }, @@ -4098,7 +4347,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|M2%25 AND SW:water,salt_water,sugar_water,dirty_water,seltzer,pool_water%14", "XX|FX%0.5|SW:water,salt_water,sugar_water,dirty_water,seltzer,pool_water%14", - "XX|M1|SW:water,salt_water,sugar_water,dirty_water,seltzer,pool_water%14", + "XX|M1|SW:water,salt_water,sugar_water,dirty_water,seltzer,pool_water%14" ], tick: function(pixel) { if (pixelTicks-pixel.start > 500) { @@ -4125,7 +4374,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|M2%5|SW:water,salt_water,sugar_water,dirty_water,seltzer,pool_water,primordial_soup%14", "XX|FX%0.5|BO", - "M2|M1|M2 AND SW:water,salt_water,sugar_water,dirty_water,seltzer,pool_water,primordial_soup%5", + "M2|M1|M2 AND SW:water,salt_water,sugar_water,dirty_water,seltzer,pool_water,primordial_soup%5" ], reactions: { "algae": { elem2:null, chance:0.25, func:behaviors.FEEDPIXEL }, @@ -4192,7 +4441,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|FX%0.25|M2%0.5 AND BO", - "XX|M1|XX", + "XX|M1|XX" ], reactions: { "salt": { elem1: "slime", elem2: null }, @@ -4239,7 +4488,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|FX%0.25|M2%0.5 AND BO", - "XX|M1|XX", + "XX|M1|XX" ], reactions: { "salt": { elem1: "limestone", elem2: null }, @@ -4295,31 +4544,11 @@ SEEDRISE: function(pixel) { behavior: [ "CR:propane|CR:propane|CR:propane", "XX|XX|XX", - "XX|XX|XX", + "XX|XX|XX" ], category: "machines", conduct: 0.73 }, -"superheater": { - color: "#dd1111", - behavior: [ - "XX|HT:10|XX", - "HT:10|XX|HT:10", - "XX|HT:10|XX", - ], - category:"machines", - insulate:true -}, -"freezer": { - color: "#1111dd", - behavior: [ - "XX|CO:10|XX", - "CO:10|XX|CO:10", - "XX|CO:10|XX", - ], - category:"machines", - insulate:true -}, "pipe": { color: "#414c4f", onSelect: function() { @@ -4338,6 +4567,7 @@ SEEDRISE: function(pixel) { createPixel("pipe_wall",x,y); } } + pixel.color = pixelColorPick(pixel,"#293132"); pixel.stage = 1; } else if (pixel.stage === 1 && pixelTicks-pixel.start > 70) { //uninitialized @@ -4347,7 +4577,7 @@ SEEDRISE: function(pixel) { var y = pixel.y+coord[1]; if (isEmpty(x,y)) { pixel.stage = 2; //blue - pixel.color = pixelColorPick(pixel,"#000036"); + pixel.color = pixelColorPick(pixel,"#000056"); break; } } @@ -4364,7 +4594,7 @@ SEEDRISE: function(pixel) { switch (pixel.stage) { case 2: newPixel.stage = 3; newColor = "#003600"; break; //green case 3: newPixel.stage = 4; newColor = "#360000"; break; //red - case 4: newPixel.stage = 2; newColor = "#000036"; break; //blue + case 4: newPixel.stage = 2; newColor = "#000056"; break; //blue } newPixel.color = pixelColorPick(newPixel,newColor); } @@ -4419,9 +4649,12 @@ SEEDRISE: function(pixel) { pixel.con = null; break; } - if (!isEmpty(x,y,true) && pixelMap[x][y].element === "pipe") { + if (!isEmpty(x,y,true) && elements[pixelMap[x][y].element].canContain) { var newPixel = pixelMap[x][y]; - if (pixel.con && !newPixel.con && newPixel.stage === pixel.stage) { + if (newPixel.filter) { + if (newPixel.filter !== pixel.con.element && !newPixel.filter.split(",").includes(pixel.con.element)) continue + } + if (pixel.con && !newPixel.con && (pixelMap[x][y].element !== "pipe" || newPixel.stage === pixel.stage)) { newPixel.con = pixel.con; newPixel.con.x = newPixel.x; newPixel.con.y = newPixel.y; @@ -4438,36 +4671,241 @@ SEEDRISE: function(pixel) { movable: false, canContain: true, forceSaveColor: true, - hardness: 0.75 + hardness: 0.75, + insulate: true }, "pipe_wall": { color: "#586879", behavior: behaviors.WALL, category: "machines", noMix: true, - hidden: true + hidden: true, + insulate: true +}, +"filter": { + color: ["#885a3a","#64432b"], + colorKey: { + "L":"#885a3a", + "D":"#64432b" + }, + colorPattern: [ + "DL", + "LD" + ], + onShiftSelect: function(element) { + promptInput("Enter an element to allow. Enter multiple separated by commas.", function(r) { + r = validateElementList(r); + if (!r) return; + currentElementProp = { filter:r } + }, elemTitleCase(elements[element].name || element)); + }, + onClicked: function(pixel,element) { + if (pixel.filter) return; + if (elements[element].tool && elements[element].canPlace !== true) return; + if (elements[element].canPlace === false) return; + if (elements[element].movable) pixel.filter = element; + }, + tick: function(pixel) { + doDefaults(pixel); + if (!pixel.con) { //collect pixel + if (Math.random() > 0.2) return; + for (var i = 0; i < squareCoords.length; i++) { + var coord = squareCoords[i]; + var x = pixel.x+coord[0]; + var y = pixel.y+coord[1]; + let newPixel = getPixel(x, y); + if (!newPixel) continue; + if (newPixel.element === "filter") { + if (!newPixel.filter && pixel.filter) newPixel.filter = pixel.filter; + if (!pixel.filter && newPixel.filter) pixel.filter = newPixel.filter; + } + if (!elements[newPixel.element].movable) continue; + if (!pixel.filter) { + pixel.filter = newPixel.element; + } + if (elements[newPixel.element].movable && ( + pixel.filter === newPixel.element || + pixel.filter === elements[newPixel.element].state || + (pixel.filter.match(/,/) && pixel.filter.split(",").includes(newPixel.element)) + )) { + if (!elements[newPixel.element].isGas && coord[1] > 0) continue; + pixel.con = newPixel; + deletePixel(newPixel.x,newPixel.y); + pixel.con.x = pixel.x; + pixel.con.y = pixel.y; + pixel.con.del; + pixel.filterTick = pixelTicks; + break; + } + } + return; + } + + //move pixel + let targets; + let element = pixel.con.element; + if (elements[element].isGas) { //random direction + shuffleArray(squareCoordsShuffle); + targets = squareCoordsShuffle; + } + else { //downward, and then randomly diagonally + if (Math.random() < 0.5) return; + targets = [[0,1], ...(Math.random() < 0.5 ? [[1,1],[-1,1]] : [[-1,1],[1,1]])]; + } + + for (var i = 0; i < targets.length; i++) { + var coord = targets[i]; + var x = pixel.x+coord[0]; + var y = pixel.y+coord[1]; + if (isEmpty(x,y)) { //release pixel + delete pixel.con.del; + pixel.con.x = x; + pixel.con.y = y; + pixelMap[x][y] = pixel.con; + currentPixels.push(pixel.con); + pixel.con = null; + break; + } + let newPixel = getPixel(x, y); + if (!newPixel) continue; + if (elements[newPixel.element].canContain && !newPixel.con && newPixel.filterTick !== pixelTicks) { + if (newPixel.filter !== pixel.filter) { + if (newPixel.filter !== pixel.con.element && !newPixel.filter.split(",").includes(pixel.con.element)) continue + } + newPixel.con = pixel.con; + newPixel.con.x = newPixel.x; + newPixel.con.y = newPixel.y; + newPixel.con.del; //??? + newPixel.filterTick = pixelTicks; + delete newPixel.con.del; + pixel.con = null; + break; + } + } + + }, + hoverStat: function(pixel) { return pixel.filter ? pixel.filter.toUpperCase() : "NONE" }, + category: "machines", + movable: false, + canContain: true, + forceSaveColor: true, + hardness: 0.75 +}, +"gate": { + color: ["#883a88","#642b64"], + colorKey: { + "L":"#883a88", + "D":"#642b64" + }, + colorPattern: [ + "DD", + "LL" + ], + tick: function(pixel) { + doDefaults(pixel); + if (!pixel.charge) { + if (pixel.alpha < 1) pixel.alpha = 1; + return; + } + else { + if (pixel.alpha !== 0.25) pixel.alpha = 0.25; + } + if (!pixel.con) { //collect pixel + for (var i = 0; i < squareCoords.length; i++) { + var coord = squareCoords[i]; + var x = pixel.x+coord[0]; + var y = pixel.y+coord[1]; + let newPixel = getPixel(x, y); + if (!newPixel) continue; + if (!elements[newPixel.element].movable) continue; + if (elements[newPixel.element].movable) { + if (!elements[newPixel.element].isGas && coord[1] > 0) continue; + pixel.con = newPixel; + deletePixel(newPixel.x,newPixel.y); + pixel.con.x = pixel.x; + pixel.con.y = pixel.y; + pixel.con.del; + pixel.filterTick = pixelTicks; + break; + } + } + return; + } + + //move pixel + let targets; + let element = pixel.con.element; + if (elements[element].isGas) { //random direction + shuffleArray(squareCoordsShuffle); + targets = squareCoordsShuffle; + } + else { //downward, and then randomly diagonally + targets = [[0,1], ...(Math.random() < 0.5 ? [[1,1],[-1,1]] : [[-1,1],[1,1]])]; + } + + for (var i = 0; i < targets.length; i++) { + var coord = targets[i]; + var x = pixel.x+coord[0]; + var y = pixel.y+coord[1]; + if (isEmpty(x,y)) { //release pixel + delete pixel.con.del; + pixel.con.x = x; + pixel.con.y = y; + pixelMap[x][y] = pixel.con; + currentPixels.push(pixel.con); + pixel.con = null; + break; + } + let newPixel = getPixel(x, y); + if (!newPixel) continue; + if (elements[newPixel.element].canContain && !newPixel.con && newPixel.filterTick !== pixelTicks) { + if (newPixel.filter) { + if (newPixel.filter !== pixel.con.element && !newPixel.filter.split(",").includes(pixel.con.element)) continue + } + newPixel.con = pixel.con; + newPixel.con.x = newPixel.x; + newPixel.con.y = newPixel.y; + newPixel.con.del; //??? + newPixel.filterTick = pixelTicks; + delete newPixel.con.del; + pixel.con = null; + break; + } + } + + }, + category: "machines", + movable: false, + canContain: true, + forceSaveColor: true, + conduct: 1, + onlyConduct: ["gate","wire"], + hardness: 0.75, + insulate: true }, "mixer": { color: ["#9f936f","#b4a98e","#c8beac"], behavior: [ "MX|MX|MX", "MX|CC:#9f936f,#b4a98e,#c8beac|MX", - "MX|MX|MX", + "MX|MX|MX" ], category: "machines", noMix: true, - darkText: true + darkText: true, + outline: false }, "grinder": { color: ["#8a8986","#a3a29f","#bcbbb9"], behavior: [ "MX|SM%8 AND MX|MX", "SM%8 AND MX|CC:#8a8986,#a3a29f,#bcbbb9|SM%8 AND MX", - "MX|SM%8 AND MX|MX", + "MX|SM%8 AND MX|MX" ], category: "machines", noMix: true, - darkText: true + darkText: true, + outline: false }, "fuse": { color: "#825d38", @@ -4505,7 +4943,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|CR:fire|XX", "XX|XX|XX", - "XX|XX|XX", + "XX|XX|XX" ], reactions: { "water": { elem1:"wood" }, @@ -4534,7 +4972,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|CR:water|XX", "CR:water|XX|CR:water", - "XX|CR:water|XX", + "XX|CR:water|XX" ], category:"special", tempHigh: 1455.5, @@ -4585,14 +5023,14 @@ SEEDRISE: function(pixel) { burnTime:200, burnInto:"cooked_meat", category:"special", - movable: false, + movable: false }, "bone_marrow": { color: "#c97265", behavior: [ "XX|CR:blood,bone,bone%1|XX", "CR:blood,bone,bone%1|XX|CR:blood,bone,bone%1", - "XX|CR:blood,bone,bone%1|XX", + "XX|CR:blood,bone,bone%1|XX" ], category:"life", tempHigh: 750, @@ -4626,7 +5064,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|FY:0%5|XX", - "XX|M1 AND BO|XX", + "XX|M1 AND BO|XX" ], tempHigh: 250, stateHigh: "molten_plastic", @@ -4639,10 +5077,11 @@ SEEDRISE: function(pixel) { }, "balloon": { color: ["#fe4a75","#267cb0","#1a743c","#ff6ffa","#eaede5","#1dc9f3","#ff0101","#f4cd32","#bee347","#fab937","#91c7cc"], + buttonColor: ["#ff8080","#ff2626"], behavior: [ "M1%50|M1%50|M1%50", "M2%5|XX|M2%5", - "M2%5|M2%5|M2%5", + "M2%5|M2%5|M2%5" ], reactions: { "cloud": {elem1:"pop"}, @@ -4679,7 +5118,7 @@ SEEDRISE: function(pixel) { behavior: [ "M1|M1|M1", "M2|XX|M2", - "XX|CR:antifire%2.5|XX", + "XX|CR:antifire%2.5|XX" ], temp: 1850, tempLow: 1750, @@ -4695,7 +5134,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|M2|XX", "M2|XX|M2", - "M1|M1|M1", + "M1|M1|M1" ], reactions: { "antifluid": { elem1: "antigas" }, @@ -4715,7 +5154,7 @@ SEEDRISE: function(pixel) { hidden: true, state: "gas", density: 0.2, - ignoreAir: true, + ignoreAir: true }, "antifluid": { color: "#d1dbeb", @@ -4750,7 +5189,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|M1 AND BO|XX", "CR:wall|XX|CR:wall", - "XX|XX|XX", + "XX|XX|XX" ], reactions: { "electric": { elem1:"horizontal" } @@ -4764,7 +5203,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|CR:wall|XX", "XX|XX|M1 AND BO", - "XX|CR:wall|XX", + "XX|CR:wall|XX" ], reactions: { "proton": { elem1:"vertical" }, @@ -4787,7 +5226,8 @@ SEEDRISE: function(pixel) { "acid_cloud": { elem1: "pyrocumulus", chance:0.05, "y":[0,12], "setting":"clouds" }, "pyrocumulus": { elem1: "pyrocumulus", chance:0.08, "y":[0,12], "setting":"clouds" }, "tornado":{elem1:"pyrocumulus", oneway:true}, - "stench": { elem2:null, chance:0.1 } + "stench": { elem2:null, chance:0.1 }, + "molten_dirt": { elem1:null, elem2:"molten_tuff" } }, category:"powders", state: "solid", @@ -4868,7 +5308,7 @@ SEEDRISE: function(pixel) { "static": { "color1":["#ffffff","#bdbdbd","#808080","#424242","#1c1c1c"] }, "pipe": { }, "pipe_wall": { }, - "wall": { }, + "wall": { } }, temp: 35, tempLow: -273, @@ -4950,7 +5390,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|DL%0.25|XX", - "XX|XX|XX", + "XX|XX|XX" ], tick: behaviors.BOUNCY, temp: 35, @@ -4976,6 +5416,10 @@ SEEDRISE: function(pixel) { pixel.start = pixelTicks; } }, + reactions: { + "molten_stained_glass": { elem1:"rainbow" }, + "metal_scrap": { charged:true, elem1:"static" }, + }, canPlace: true, category:"special", customColor: true, @@ -4983,7 +5427,7 @@ SEEDRISE: function(pixel) { movable: false, state: "gas", glow: false, - density: 1, + density: 1 }, "charcoal": { color: "#2b2b2b", @@ -5113,7 +5557,8 @@ SEEDRISE: function(pixel) { "ozone": { elem1: "ozone", chance:0.01, "y":[0,12], "setting":"clouds" }, "light": { elem1: "ozone", elem2: null, chance:0.3, "y":[0,12], "setting":"clouds" }, "proton": { elem1:"flash", color1:"#e36d88", attr1:{delay:500}, elem2:"flash", color2:"#e36d88", attr2:{delay:500}, chance:0.25, "y":[0,10] }, - "paper": { elem1:"fragrance", chance:0.005 } + "paper": { elem1:"fragrance", chance:0.005 }, + "molten_lead": { elem2:"poison_gas" } }, category: "gases", // burn: 100, @@ -5190,7 +5635,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|FX%1|M1%5", - "XX|M1%1|M1%2", + "XX|M1%1|M1%2" ], tick: function(pixel) { if (Math.random() < 0.0025 || (pixel.clone && isEmpty(pixel.x,pixel.y-1))) { @@ -5259,7 +5704,7 @@ SEEDRISE: function(pixel) { }, "liquid_ammonia": { tempLow: -77.78, - density: 681.9, + density: 681.9 }, "oil": { color: "#470e00", @@ -5283,7 +5728,7 @@ SEEDRISE: function(pixel) { "pool_water": { burning1:true, elem2:"explosion" }, "seltzer": { burning1:true, elem2:"explosion" }, "coral": { elem2:null, chance:0.01 }, - "hydrogen": { elem1:"sulfur", elem2:"steam", burning1:false, burning2:false, tempMin:90, chance:0.1 }, + "hydrogen": { elem1:"sulfur", elem2:"steam", burning1:false, burning2:false, tempMin:90, chance:0.1 } }, category: "liquids", tempHigh: 500, @@ -5332,7 +5777,7 @@ SEEDRISE: function(pixel) { behaviorOn: [ "XX|XX|XX", "XX|CH:fire|XX", - "XX|XX|XX", + "XX|XX|XX" ], conduct: 0.01, category: "gases", @@ -5357,7 +5802,7 @@ SEEDRISE: function(pixel) { behaviorOn: [ "XX|XX|XX", "XX|CH:fire|XX", - "XX|XX|XX", + "XX|XX|XX" ], conduct: 0.01, category: "gases", @@ -5440,7 +5885,8 @@ SEEDRISE: function(pixel) { category: "special", movable: false, breakInto: "static", - fireColor: ["#ff0000","#ff8800","#ffff00","#00ff00","#00ffff","#0000ff","#ff00ff"] + fireColor: ["#ff0000","#ff8800","#ffff00","#00ff00","#00ffff","#0000ff","#ff00ff"], + darkText: true }, "static": { color: ["#ffffff","#888888","#000000"], @@ -5471,7 +5917,8 @@ SEEDRISE: function(pixel) { conduct: 1, movable: false, breakInto: "malware", - fireColor: ["#ffffff","#bfbfbf","#888888","#404040","#000000"] + fireColor: ["#ffffff","#bfbfbf","#888888","#404040","#000000"], + outline: false }, "border": { color: ["#00ffff","#000000","#00ffff","#000000","#00ffff"], @@ -5488,6 +5935,7 @@ SEEDRISE: function(pixel) { hardness: 1, insulate: true, movable: false, + outline: false }, "clay": { color: "#d4c59c", @@ -5511,7 +5959,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|XX|XX", - "M2%25|M1|M2%25", + "M2%25|M1|M2%25" ], reactions: { "water":{elem1:"clay",elem2:null}, @@ -5969,7 +6417,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|M2%0.05|XX", "XX|L2:grass|XX", - "XX|M1|XX", + "XX|M1|XX" ], tick: behaviors.SEEDRISE, tempHigh: 100, @@ -5991,7 +6439,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|M2%0.25|XX", "XX|L2:wheat AND C2:wheat%30|XX", - "XX|M1|XX", + "XX|M1|XX" ], tick: behaviors.SEEDRISE, tempHigh: 400, @@ -6029,7 +6477,7 @@ SEEDRISE: function(pixel) { density: 2403, hardness: 0.4, breakInto: "porcelain_shard", - noMix: true, + noMix: true }, "paper": { color: "#f0f0f0", @@ -6097,7 +6545,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|M2%1.5|XX", "XX|L2:plant AND C2:pistil%30|XX", - "XX|M1|XX", + "XX|M1|XX" ], tick: behaviors.SEEDRISE, tempHigh: 100, @@ -6114,7 +6562,8 @@ SEEDRISE: function(pixel) { seed: true }, "pistil": { - color: ["#734e39","#2f0603","#d2ac3a","#8a978f","#593117"], + color: ["#8a978f","#d2ac3a","#734e39","#593117","#2f0603"], + buttonColor: ["#d2ac3a","#2f0603"], tick: function(pixel) { if (!pixel.fColor) { // make it a hsl random hue, 100% saturation, 50% lightness @@ -6159,7 +6608,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|ST:pistil|XX", "ST:pistil|FX%0.25|ST:pistil", - "M2%10|ST:pistil AND M1%10|M1%10", + "M2%10|ST:pistil AND M1%10|M1%10" ], reactions: { "water": { elem2:"tea", tempMin:80, color2:"#9e4c00" }, @@ -6325,7 +6774,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|M2%2|XX", "XX|L2:bamboo AND C2:bamboo%10|XX", - "XX|M1|XX", + "XX|M1|XX" ], tick: behaviors.SEEDRISE, tempHigh: 100, @@ -6346,7 +6795,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|XX|XX", - "M2%25|M1%25|M2%25", + "M2%25|M1%25|M2%25" ], tick: function(pixel) { if (pixel.foam && isEmpty(pixel.x,pixel.y-1)) { @@ -6372,13 +6821,13 @@ SEEDRISE: function(pixel) { extinguish: true }, "acid": { - color: ["#b5cf91","#a1ff5e","#288f2a"], + color: ["#AEDF80","#A8EF6F","#a1ff5e","#79DA4D","#50B43B","#288f2a"], behavior: [ "XX|DB%5|XX", "DB%5 AND M2|XX|DB%5 AND M2", - "DB%5 AND M2|DB%10 AND M1|DB%5 AND M2", + "DB%5 AND M2|DB%10 AND M1|DB%5 AND M2" ], - ignore: ["fire","smoke","glass","rad_glass","glass_shard","rad_shard","stained_glass","baked_clay","acid_gas","neutral_acid","acid_cloud","water","salt_water","sugar_water","dirty_water","copper","gold","porcelain","plastic","bead","microplastic","molten_plastic","pool_water","chlorine","hydrogen","gold_coin","silver","nickel","calcium","bone","earthquake","tornado","tsunami","liquid_light","sensor","clay","pipe","pipe_wall","lye","rose_gold","purple_gold","blue_gold","electrum"], + ignore: ["fire","smoke","glass","rad_glass","glass_shard","rad_shard","stained_glass","baked_clay","acid_gas","neutral_acid","acid_cloud","water","salt_water","sugar_water","dirty_water","copper","gold","porcelain","plastic","bead","microplastic","molten_plastic","pool_water","chlorine","hydrogen","gold_coin","silver","nickel","calcium","bone","earthquake","tornado","tsunami","liquid_light","sensor","clay","pipe","pipe_wall","lye","rose_gold","purple_gold","blue_gold","electrum","mercury","bubble","diamond"], reactions: { "ash": { elem1:"neutral_acid", elem2:null }, "limestone": { elem1:"neutral_acid", elem2:["calcium","carbon_dioxide"] }, @@ -6408,7 +6857,8 @@ SEEDRISE: function(pixel) { "gravel": { elem1:null, elem2:"sand", chance:0.1 }, "wet_sand": { elem1:"neutral_acid", elem2:"clay" }, "snail": { elem1:"neutral_acid", elem2:"slug" }, - "silver": { stain2:"#2a2e2a" } + "silver": { stain2:"#2a2e2a" }, + "acid": { elem2:"bubble", color2:"#bfcfa9", attr2:{"clone":"acid"}, chance:0.001, tempMin:80 } }, category: "liquids", tempHigh: 110, @@ -6439,14 +6889,14 @@ SEEDRISE: function(pixel) { behavior: [ "M1|DB%5 AND M1|M1", "DB%5 AND M1|XX|DB%5 AND M1", - "DB%5 AND M1|DB%10 AND M1|DB%5 AND M1", + "DB%5 AND M1|DB%10 AND M1|DB%5 AND M1" ], tick: function(pixel) { if (pixel.temp > 1000 && Math.random() < 0.01) { changePixel(pixel,Math.random() < 0.66 ? "hydrogen" : "chlorine"); } }, - ignore: ["fire","smoke","glass","rad_glass","glass_shard","rad_shard","stained_glass","baked_clay","acid_gas","neutral_acid","acid_cloud","water","salt_water","sugar_water","dirty_water","copper","gold","porcelain","plastic","bead","microplastic","molten_plastic","pool_water","chlorine","hydrogen","gold_coin","silver","nickel","calcium","bone","earthquake","tornado","tsunami","liquid_light","sensor","pipe","pipe_wall","lye"], + ignore: ["fire","smoke","glass","rad_glass","glass_shard","rad_shard","stained_glass","baked_clay","acid_gas","neutral_acid","acid_cloud","water","salt_water","sugar_water","dirty_water","copper","gold","porcelain","plastic","bead","microplastic","molten_plastic","pool_water","chlorine","hydrogen","gold_coin","silver","nickel","calcium","bone","earthquake","tornado","tsunami","liquid_light","sensor","pipe","pipe_wall","lye","mercury","bubble","diamond"], reactions: { "acid_gas": { elem1: null, elem2: "acid_cloud", chance:0.3, "y":[0,12], "setting":"clouds" }, "rain_cloud": { elem1: null, elem2: "acid_cloud", chance:0.4, "y":[0,12], "setting":"clouds" }, @@ -6511,7 +6961,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "M2|XX|M2", - "M2|M1|M2", + "M2|M1|M2" ], tick: function(pixel) { if (Math.random() < 0.02 && isEmpty(pixel.x,pixel.y-1)) { @@ -6549,12 +6999,12 @@ SEEDRISE: function(pixel) { behavior: [ "XX|CH:gray_goo%25|XX", "M2%5 AND CH:gray_goo%25|DL%5|M2%5 AND CH:gray_goo%25", - "XX|CH:gray_goo%25 AND M1|XX", + "XX|CH:gray_goo%25 AND M1|XX" ], behaviorOn: [ "XX|XX|XX", "XX|DL%10|XX", - "M1|M2|M1", + "M1|M2|M1" ], reactions: { "antibody": { elem1:"malware", elem2:null } @@ -6574,7 +7024,7 @@ SEEDRISE: function(pixel) { behavior: [ "CL%1|CL%1 AND SH|CL%1", "CL%1 AND SH|SH%5 AND DL%10|CL%1 AND SH", - "M1%15 AND CL%1|M1%50 AND CL%2 AND SH|M1%15 AND CL%1", + "M1%15 AND CL%1|M1%50 AND CL%2 AND SH|M1%15 AND CL%1" ], reactions: { "gray_goo": { elem2:"malware" }, @@ -6595,16 +7045,16 @@ SEEDRISE: function(pixel) { "lattice": { elem1:"lattice", elem2:null }, "light_bulb": { elem2:"explosion", chance:0.01 }, "battery": { elem2:"explosion", chance:0.01 }, - "heater": { elem2:["cooler","superheater"], chance:0.01 }, - "cooler": { elem2:["heater","freezer"], chance:0.01 }, - "superheater": { elem2:["heater","freezer"], chance:0.01 }, - "freezer": { elem2:["cooler","superheater"], chance:0.01 }, - "led_r": { elem2:["led_g","led_b"], chance:0.01 }, - "led_g": { elem2:["led_r","led_b"], chance:0.01 }, - "led_b": { elem2:["led_r","led_g"], chance:0.01 }, + "heater": { elem2:"cooler", chance:0.01 }, + "cooler": { elem2:"heater", chance:0.01 }, + "led": { elem2:"explosion", chance:0.01 }, "ewall": { elem2:"wall", chance:0.01 }, "border": { elem2:null, chance:0.01 }, - "virus": { func: function(pixel1,pixel2){pixel2.heal=false;pixel2.origElem="malware"} } + "virus": { func: function(pixel1,pixel2){pixel2.heal=false;pixel2.origElem="malware"} }, + "lead": { elem2:"pipe", chance:0.05 }, + "tungsten": { elem2:"light_bulb", chance:0.05 }, + "zinc": { elem2:"battery", chance:0.05 }, + "copper": { elem2:"wire", chance:0.05 }, }, category: "special", state: "solid", @@ -6613,9 +7063,10 @@ SEEDRISE: function(pixel) { "ecloner": { name: "e-cloner", color: "#dddd00", - behavior: behaviors.WALL, - behaviorOn: behaviors.CLONER, + onShiftSelect: behaviors.CLONER_SHIFT_SELECT, tick: function(pixel) { + doDefaults(pixel); + if (pixel.charge) behaviors.CLONER(pixel); if (pixel.clone) { return } for (var i = 0; i < adjacentCoords.length; i++) { var coords = adjacentCoords[i]; @@ -6626,7 +7077,7 @@ SEEDRISE: function(pixel) { if (pixelMap[x][y].clone) { pixel.clone = pixelMap[x][y].clone; break } var element = pixelMap[x][y].element; if (element === pixel.element || elements[pixel.element].ignore.indexOf(element) !== -1 && element !== "fuse") { continue } - pixel.clone = element; + pixel.clone = elements[element].pickElement || element; break; } } @@ -6646,9 +7097,10 @@ SEEDRISE: function(pixel) { behavior: [ "XX|CF%10|XX", "CF%10|XX|CF%10", - "XX|CF%10|XX", + "XX|CF%10|XX" ], onClicked: behaviors.CLONE_ON_CLICK, + onShiftSelect: behaviors.CLONER_SHIFT_SELECT, ignore: ["cloner","ecloner","clone_powder","floating_cloner","wall","ewall"], category:"machines", insulate:true, @@ -6656,12 +7108,10 @@ SEEDRISE: function(pixel) { }, "clone_powder": { color: "#f0f000", - behavior: [ - "XX|CF|XX", - "CF|XX|CF", - "M2|CF AND M1|M2", - ], + behavior: behaviors.POWDER, + tick: behaviors.CLONER, onClicked: behaviors.CLONE_ON_CLICK, + onShiftSelect: behaviors.CLONER_SHIFT_SELECT, reactions: { "malware": { elem1:"floating_cloner" } }, @@ -6677,11 +7127,15 @@ SEEDRISE: function(pixel) { "floating_cloner": { color: "#c7c787", behavior: [ - "XX|CF%3 AND M1%10|XX", - "CF%3 AND M1%10|XX|CF%3 AND M1%10", - "XX|CF%3 AND M1%10|XX", + "XX|M1%10|XX", + "M1%10|XX|M1%10", + "XX|M1%10|XX" ], + tick: function(pixel) { + behaviors.CLONER(pixel, 0.03); + }, onClicked: behaviors.CLONE_ON_CLICK, + onShiftSelect: behaviors.CLONER_SHIFT_SELECT, reactions: { "malware": { elem1:"clone_powder" } }, @@ -6733,7 +7187,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|CH:ice%0.5|XX", - "M2|M1|M2", + "M2|M1|M2" ], reactions: { "water": { elem2: "ice_nine" }, @@ -6802,7 +7256,7 @@ SEEDRISE: function(pixel) { state: "liquid", density: 2000, excludeRandom: true, - ignore: ["fire","smoke","antimatter","strange_matter","wall","ewall","plasma","void","border","pointer"], + ignore: ["fire","smoke","antimatter","strange_matter","wall","ewall","plasma","void","border","pointer"] }, "permafrost": { color: ["#54443a","#4f4235","#453c30","#524639"], @@ -6837,7 +7291,7 @@ SEEDRISE: function(pixel) { state: "liquid", density: 911, isFood: true, - stain: 0.05, + stain: 0.05 }, "melted_cheese": { color: "#fcdb53", @@ -6862,7 +7316,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|M2%1.5|XX", "XX|L2:mushroom_stalk AND C2:mushroom_gill%20|XX", - "XX|M1|XX", + "XX|M1|XX" ], tick: behaviors.SEEDRISE, reactions: { @@ -6891,7 +7345,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|XX|XX", - "XX|CH:dirt>hyphae%1 AND M1|XX", + "XX|CH:dirt>hyphae%1 AND M1|XX" ], reactions: { "wood": { elem2:"dirt", chance:0.04 }, @@ -6992,11 +7446,12 @@ SEEDRISE: function(pixel) { breakInto: [null,null,"mycelium"] }, "hyphae": { - color: "#c79789", + color: ["#c79789","#bd937b"], + grain: 2, behavior: [ "CH:dirt>hyphae,hyphae,mycelium%0.5|CR:mushroom_spore%0.5|CH:dirt>hyphae,hyphae,mycelium%0.5", "CH:dirt>mycelium%0.5|XX|CH:dirt>mycelium%0.5", - "CH:dirt>hyphae,hyphae,mycelium%0.5|XX|CH:dirt>hyphae,hyphae,mycelium%0.5", + "CH:dirt>hyphae,hyphae,mycelium%0.5|XX|CH:dirt>hyphae,hyphae,mycelium%0.5" ], reactions: { "wood": { elem2:"dirt", chance:0.04 }, @@ -7026,6 +7481,7 @@ SEEDRISE: function(pixel) { }, "mycelium": { color: ["#734d5e","#734d5e","#734d5e","#61404f","#6b4b5a","#755061","#866372","#987886","#ab8e9a","#bea4ad","#d0b9c1","#e3cfd5"], + buttonColor: ["#e3cfd5","#734d5e"], behavior: behaviors.POWDER, reactions: { "dead_plant": { elem2:[null,null,null,"mycelium","hyphae"], chance:0.0025 }, @@ -7081,6 +7537,7 @@ SEEDRISE: function(pixel) { }, "lichen": { color: ["#b6d6c3","#769482"], + grain: 2, tick: function(pixel) { if (!tryMove(pixel,pixel.x,pixel.y+1)) { var coords = [ @@ -7123,7 +7580,7 @@ SEEDRISE: function(pixel) { behavior: [ "M2|DB%50 AND M2|M2", "M1|XX|M1", - "M1|DB%50 AND M1|M1", + "M1|DB%50 AND M1|M1" ], onCollide: function(pixel1,pixel2) { if (elements.antimatter.ignore.indexOf(pixel2.element) !== -1 || elements[pixel2.element].isGas) return; @@ -7158,7 +7615,7 @@ SEEDRISE: function(pixel) { behavior: behaviors.LIQUID, viscosity: 20, tempHigh: 600, - stateHigh: ["dioxin","smoke","dioxin","smoke","stench"], + stateHigh: ["dioxin","smoke","dioxin","smoke","stench"] }, "cloth": { color: ["#F7F7F7","#F1F1F1","#E8E8E8","#CDCDCD"], @@ -7335,7 +7792,7 @@ SEEDRISE: function(pixel) { stateHigh: "fire", state: "solid", density: 65, - movable: false, + movable: false }, "bamboo": { color: ["#7cc00c","#77a012"], @@ -7495,6 +7952,7 @@ SEEDRISE: function(pixel) { density: 7260, conduct: 0.45, hardness: 0.15, + fireColor: ["#DBD1E9","#D7E9F2","#9AB0D1"], movable: false, superconductAt: -269.45 }, @@ -7625,12 +8083,11 @@ SEEDRISE: function(pixel) { reactions: { "aluminum": { elem1:["molten_gallium","molten_gallium","alga"], elem2:null, chance:0.01 }, "molten_aluminum": { elem1:"alga", elem2:"molten_alga", chance:0.05 }, - "sodium": { elem1:"hydrogen", elem2:"salt", chance:0.005 }, "steel": { elem2:"iron", chance:0.005 }, "gold": { elem1:null, elem2:"blue_gold", chance:0.01 }, "gold_coin": { elem1:null, elem2:"blue_gold", chance:0.01 } }, - stain: 0.05, + stain: 0.05 }, "gallium_gas": { density: 6.31 @@ -7648,7 +8105,7 @@ SEEDRISE: function(pixel) { hidden: true }, "purple_gold": { - color: ["#f58fda","#d06cb5","#f58fda"], + color: ["#ee8ef5","#c96cd0","#ee8ef5"], behavior: behaviors.WALL, tempHigh: 1060, category: "solids", @@ -7685,7 +8142,7 @@ SEEDRISE: function(pixel) { alias: "green gold" }, "pyrite": { - color: ["#e8e0cb","#cdcaaf","#726a53","#8f835e","#bfb9a0",], + color: ["#f7f6e8","#d6d285","#d1bf75","#b3974d","#a48546"], behavior: behaviors.WALL, tempHigh: 1182.5, category: "solids", @@ -7900,7 +8357,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|CR:foam%1|XX", "M2|XX|M2", - "M2|M1|M2", + "M2|M1|M2" ], onMix: function(milk1,milk2) { if (Math.random() < 0.5) releaseElement(milk1, "foam"); @@ -8041,7 +8498,7 @@ SEEDRISE: function(pixel) { hidden: true, isFood: true, state: "liquid", - density: 959.97, + density: 959.97 }, "nut_milk": { color: "#D7D1C3", @@ -8146,7 +8603,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "M2%0.5|XX|M2%0.5", - "XX|M1|XX", + "XX|M1|XX" ], reactions: { "milk": { elem2:null, chance:0.025 }, @@ -8222,7 +8679,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|CR:plague,stench,stench,stench,fly%0.25 AND CH:cheese,cheese_powder>rotten_cheese%1|XX", "CH:cheese,cheese_powder>rotten_cheese%1|XX|CH:cheese,cheese_powder>rotten_cheese%1", - "XX|M1 AND CH:cheese,cheese_powder>rotten_cheese%1|XX", + "XX|M1 AND CH:cheese,cheese_powder>rotten_cheese%1|XX" ], tempHigh: 54, stateHigh: ["melted_cheese","melted_cheese","melted_cheese","melted_cheese","melted_cheese","melted_cheese","melted_cheese","melted_cheese","melted_cheese","stench"], @@ -8270,7 +8727,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|ST:vine|XX", "ST:vine|XX|ST:vine", - "M2|M1|M2", + "M2|M1|M2" ], reactions: { "radiation": { elem1:"explosion", chance:0.1, color1:"#291824" }, @@ -8362,7 +8819,7 @@ SEEDRISE: function(pixel) { category:"food", state: "solid", density: 1400, - isFood: true, + isFood: true }, "lettuce": { color: ["#a2c96b","#81C520","#639917"], @@ -8397,7 +8854,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|ST:vine|XX", "ST:vine|XX|ST:vine", - "XX|M1|XX", + "XX|M1|XX" ], reactions: { "rock": { elem1:"sauce", chance:0.1 }, @@ -8527,8 +8984,7 @@ SEEDRISE: function(pixel) { state: "solid", density: 721, seed: "corn_seed", - isFood: true, - movable: false, + isFood: true }, "popcorn": { color: ["#a6a076","#ebe4ab","#ebe4ab","#ebe4ab","#ebe4ab","#ebe4ab","#ebe4ab","#c99947"], @@ -8545,11 +9001,11 @@ SEEDRISE: function(pixel) { isFood: true }, "corn_seed": { - color: ["#f2b813","#f9e3ba"], + color: ["#f9e3ba","#f2b813"], behavior: [ "XX|M2%0.25|XX", "XX|L2:plant,corn AND C2:corn%30|XX", - "XX|M1|XX", + "XX|M1|XX" ], tick: behaviors.SEEDRISE, tempHigh: 400, @@ -8571,7 +9027,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|SH:wire%1|XX", "SH:wire%1|XX|SH:wire%1", - "M2|M1 AND SH:wire%1|M2", + "M2|M1 AND SH:wire%1|M2" ], reactions: { "zinc": { charge2:1, chance:0.01 }, @@ -8621,11 +9077,11 @@ SEEDRISE: function(pixel) { hidden: true }, "potato_seed": { - color: ["#cda57f","#aa7437","#bc9563"], + color: ["#cda57f","#bc9563","#aa7437"], behavior: [ "XX|CH:dirt>fiber|XX", "CH:dirt>potato%5|CH:potato%1|CH:dirt>potato%5", - "XX|SW:dirt%3 AND M1|XX", + "XX|SW:dirt%3 AND M1|XX" ], tempHigh: 400, stateHigh: "fire", @@ -8646,7 +9102,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|XX|XX", - "CH:dirt,mud,sand,wet_sand,clay_soil,clay,mycelium,grass,color_sand>root,fiber%0.5|CH:dirt,mud,sand,wet_sand,clay_soil,clay,mycelium,grass,color_sand>root,fiber,fiber%0.5|CH:dirt,mud,sand,wet_sand,clay_soil,clay,mycelium,grass,color_sand>root,fiber%0.5", + "CH:dirt,mud,sand,wet_sand,clay_soil,clay,mycelium,grass,color_sand>root,fiber%0.5|CH:dirt,mud,sand,wet_sand,clay_soil,clay,mycelium,grass,color_sand>root,fiber,fiber%0.5|CH:dirt,mud,sand,wet_sand,clay_soil,clay,mycelium,grass,color_sand>root,fiber%0.5" ], reactions: { "rock": { elem2:"sand", chance:0.0004 }, @@ -8690,7 +9146,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|CL:70%10|XX", "CL:70%10 AND SW:bread%30|XX|CL:70%10 AND SW:bread%30", - "XX|M1|XX", + "XX|M1|XX" ], reactions: { "bread": { elem1:"bread" }, @@ -8844,7 +9300,8 @@ SEEDRISE: function(pixel) { breakInto: "flour", state: "solid", density: 1182, - isFood: true + isFood: true, + darkText: true }, "candy": { color: "#e6cab1", @@ -8856,7 +9313,8 @@ SEEDRISE: function(pixel) { category: "food", state: "solid", density: 900, - isFood: true + isFood: true, + darkText: true }, "coffee_bean": { color: ["#994528","#772C12","#5c2513","#4a2416"], @@ -8925,7 +9383,7 @@ SEEDRISE: function(pixel) { category: "food", state: "solid", density: 325, - isFood: true, + isFood: true }, "nut_oil": { color: "#E7D784", @@ -8945,7 +9403,8 @@ SEEDRISE: function(pixel) { state: "liquid", density: 910, isFood: true, - alias: "cooking oil" + alias: "cooking oil", + darkText: true }, "nut_meat": { color: ["#deba8e","#d1a56f","#ba8b50"], @@ -8974,7 +9433,7 @@ SEEDRISE: function(pixel) { state: "solid", density: 905, isFood: true, - hidden: true, + hidden: true }, "nut_butter": { color: "#cd9141", @@ -8993,7 +9452,7 @@ SEEDRISE: function(pixel) { state: "liquid", density: 1090.5, isFood: true, - hidden: true, + hidden: true }, "jelly": { color: "#A35298", @@ -9036,7 +9495,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|XX|XX", - "M2%5|M1|M2%5", + "M2%5|M1|M2%5" ], tempHigh: 1000, stateHigh: ["smoke","smoke","smoke","calcium"], @@ -9053,7 +9512,7 @@ SEEDRISE: function(pixel) { behavior: behaviors.STURDYPOWDER, category: "food", isFood: true, - stateHighColorMultiplier: 0.955, + stateHighColorMultiplier: 0.955 }, "ice_cream": { color: ["#f7f7f7","#ededed","#dedede"], @@ -9180,10 +9639,11 @@ SEEDRISE: function(pixel) { state: "solid", density: 1019.5, conduct: 0.05, - movable: false, + movable: false }, "hair": { color: ["#C6C3BF","#EEDFC0","#DAB575","#BF9F6C","#C67945","#9B6E47","#967454","#945F36","#4C3D2E","#5A3E2D","#1c1c1c","#4A2227","#6D342B","#9D4C3B","#C25E3B"], + buttonColor: ["#C67945","#1c1c1c"], singleColor: true, behavior: behaviors.WALL, tick: function(pixel) { @@ -9297,7 +9757,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|CR:bubble%0.25|XX", "M2|XX|M2", - "M2|M1|M2", + "M2|M1|M2" ], reactions: { "rust": { elem2: "iron", chance:0.01 }, @@ -9461,7 +9921,8 @@ SEEDRISE: function(pixel) { state: "liquid", density: 998, stainSelf: true, - isFood: true + isFood: true, + darkText: true }, "ink": { color: "#171717", @@ -9561,7 +10022,7 @@ SEEDRISE: function(pixel) { "oxygen": { elem2:null, chance:0.05 }, "carbon_dioxide": { elem2:null, chance:0.05 }, "alcohol": { elem1:[null,"dna"], chance:0.02 }, - "blood": { elem2:"bubble", color2:"#f07878", attr2:{"clone":"blood"}, chance:0.001, tempMin:85 }, + "blood": { elem2:"bubble", color2:"#f07878", attr2:{"clone":"blood"}, chance:0.001, tempMin:85 } }, viscosity: 10, tempHigh: 124.55, @@ -9777,7 +10238,8 @@ SEEDRISE: function(pixel) { "pilk": { elem2:"foam", color1:"#9c7954", chance:0.005}, "cream": { elem2:null, color1:"#9c6c38", chance:0.005}, "ice_cream": { elem2:null, color1:"#9c6c38", chance:0.005}, - "tea": { elem2:"bubble", color2:"#87633d", attr2:{"clone":"tea"}, chance:0.001, tempMin:80 } + "tea": { elem2:"bubble", color2:"#87633d", attr2:{"clone":"tea"}, chance:0.001, tempMin:80 }, + "paper": { stain2:"#6c4317", chance:0.1 } }, tempHigh: 125, stateHigh: ["steam","fragrance",null], @@ -9846,7 +10308,7 @@ SEEDRISE: function(pixel) { isFood: true }, "sap": { - color: ["#b67f18","#c86305","#cf7a19","#e4ae3a"], + color: ["#e4ae3a","#cf7a19","#c86305","#b67f18"], behavior: behaviors.LIQUID, reactions: { "dead_bug": { elem1:"amber", elem2:null, chance:0.1 }, @@ -9879,7 +10341,8 @@ SEEDRISE: function(pixel) { behavior: behaviors.LIQUID, reactions: { "grape": { elem1:null, elem2:"jelly", chance:0.005, tempMin:100 }, - "salt": { elem2:null, color1:"#ffbd80", chance:0.005} + "salt": { elem2:null, color1:"#ffbd80", chance:0.005}, + "herb": { tempMax:180, elem1:"sugar", color1:["#c92626","#e3e3e3","#c92626","#e3e3e3","#c92626"], elem2:null, chance:0.1 } }, viscosity: 500, tempLow: -20, @@ -9971,7 +10434,7 @@ SEEDRISE: function(pixel) { stateHigh: "grease", reactions: { "caustic_potash": { elem1:"soap", elem2:null } - }, + } }, "melted_chocolate": { color: "#3b160b", @@ -10141,7 +10604,7 @@ SEEDRISE: function(pixel) { fireElement: null }, "limestone": { - color: ["#c5b79c","#d9ccb2","#f8f1db","#fcfaeb"], + color: ["#fcfaeb","#f8f1db","#d9ccb2","#c5b79c"], behavior: behaviors.STURDYPOWDER, tempHigh: 825, stateHigh: ["quicklime","quicklime","quicklime","quicklime","quicklime","quicklime","carbon_dioxide"], @@ -10326,6 +10789,7 @@ SEEDRISE: function(pixel) { "pool_water": { elem1:"bubble", chance:0.01, temp2:250 }, "primordial_soup": { elem1:"bubble", chance:0.01, temp2:250 }, "nut_milk": { elem1:"bubble", chance:0.01, temp2:250 }, + "carbon_dioxide": { elem1:"baking_soda", elem2:null, chance:0.02 } }, category: "powders", tempHigh: 323, @@ -10343,7 +10807,7 @@ SEEDRISE: function(pixel) { "molten_lead": { elem1:"hydrogen", elem2:null, chance:0.005 }, "molten_tin": { elem1:"hydrogen", elem2:null, chance:0.005 }, "molten_zinc": { elem1:"hydrogen", elem2:null, chance:0.005 }, - "molten_galvanized_steel": { elem1:"hydrogen", elem2:"molten_steel", chance:0.01 }, + "molten_galvanized_steel": { elem1:"hydrogen", elem2:"molten_steel", chance:0.01 } }, tempHigh: 1388, stateHigh: "smoke" @@ -10372,7 +10836,7 @@ SEEDRISE: function(pixel) { reactions: { "ice": { "elem1":"explosion" } }, - burnInto: [null,null,"molten_iron"] + burnInto: [null,null,null,null,"molten_iron"] }, "slag": { color: ["#4b3a2d","#6a5447","#6b5b53","#675851","#78756e"], @@ -10397,7 +10861,7 @@ SEEDRISE: function(pixel) { density: 13920, hardness: 0.1, hidden: true, - conduct: 0.37, + conduct: 0.37 }, "molten_aluminum": { color: ["#e6e0db","#d0d7cb","#d3d2d5"], @@ -10446,7 +10910,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|M2%5|XX", "M1%8|XX|M1%8", - "XX|M2%5|XX", + "XX|M2%5|XX" ], reactions: { "plant": { elem1:"cloud" }, @@ -10470,7 +10934,7 @@ SEEDRISE: function(pixel) { behavior: [ "M2|M1|M2", "M1|DL%0.5|M1", - "M2|M1|M2", + "M2|M1|M2" ], reactions: { "oxygen": { elem2:"stench", chance:0.01 }, @@ -10497,7 +10961,7 @@ SEEDRISE: function(pixel) { behavior: [ "M2|M1|M2", "M1|DL%0.5|M1", - "M2|M1|M2", + "M2|M1|M2" ], reactions: { "stench": { elem2:null }, @@ -10520,7 +10984,9 @@ SEEDRISE: function(pixel) { reactions: { "liquid_stench": { elem2:null } }, - category: "liquids" + category: "liquids", + tempHigh: 40, + stateHigh: "fragrance" }, "cyanide": { color: "#b6ccb6", @@ -10549,7 +11015,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "M1%7|XX|M1%7", - "XX|XX|XX", + "XX|XX|XX" ], reactions: { "carbon_dioxide": { elem1:null, elem2:null, chance:0.05 }, @@ -10613,7 +11079,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|CO:1%5|M1%2.5 AND BO", - "XX|XX|XX", + "XX|XX|XX" ], reactions: { "rain_cloud": { elem1:"rain_cloud", temp1:-20 }, @@ -10637,7 +11103,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|CH:water%0.05|M1%2.5 AND BO", - "CR:electric%0.05|CR:electric%0.05|CR:electric%0.05", + "CR:electric%0.05|CR:electric%0.05|CR:electric%0.05" ], reactions: { "anesthesia": { elem1:"acid_cloud", elem2:null, chance:0.05 }, @@ -10661,7 +11127,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|CH:snow%0.05|M1%2.5 AND BO", - "XX|XX|XX", + "XX|XX|XX" ], category:"gases", temp: -10, @@ -10670,6 +11136,7 @@ SEEDRISE: function(pixel) { tempLow: -200, stateLow: "hail_cloud", state: "gas", + breakInto: "snow", density: 0.55, ignoreAir: true, conduct: 0.01 @@ -10679,13 +11146,14 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|CH:hail%0.05|M1%2.5 AND BO", - "XX|XX|XX", + "XX|XX|XX" ], category:"gases", temp: -200, tempHigh: -160, stateHigh: "snow_cloud", state: "gas", + breakInto: "hail", density: 0.6, ignoreAir: true, conduct: 0.01 @@ -10695,7 +11163,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|CH:lightning%0.001 AND CH:water%0.05|M1%2.5 AND BO", - "XX|XX|XX", + "XX|XX|XX" ], tick: function(pixel) { pixel.temp = 70; @@ -10705,6 +11173,7 @@ SEEDRISE: function(pixel) { tempLow: 0, stateLow: "snow_cloud", state: "gas", + breakInto: "rain_cloud", density: 0.55, ignoreAir: true, conduct: 0.03 @@ -10714,7 +11183,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|XX|M1%2.5 AND BO", - "XX|XX|XX", + "XX|XX|XX" ], tick: function(pixel) { if (pixel.temp > elements.acid.tempLow && pixel.temp < elements.acid.tempHigh && Math.random() <= 0.0005) { @@ -10733,6 +11202,7 @@ SEEDRISE: function(pixel) { category:"gases", burn: 15, burnTime: 5, + breakInto: "acid", state: "gas", density: 0.7, ignoreAir: true @@ -10742,13 +11212,14 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|CH:sand%0.075|M1%2.5 AND BO", - "XX|XX|XX", + "XX|XX|XX" ], reactions: { "sand": { elem2: "sandstorm", chance:0.2, "y":[0,12], "setting":"clouds" } }, category:"gases", hidden: true, + breakInto: "sand", state: "gas", density: 0.8, ignoreAir: true @@ -10758,13 +11229,14 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|CH:ash%0.075|M1%2.5 AND BO", - "XX|XX|XX", + "XX|XX|XX" ], reactions: { "fireball": { elem1:null, elem2:"fire_cloud", chance:0.25 } }, category:"gases", hidden: true, + breakInto: "ash", state: "gas", density: 0.7, ignoreAir: true @@ -10774,7 +11246,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|CH:fireball%0.02|M1%2.5 AND BO", - "XX|XX|XX", + "XX|XX|XX" ], reactions: { "rain_cloud": { elem1: "pyrocumulus", elem2: "pyrocumulus" }, @@ -10788,6 +11260,7 @@ SEEDRISE: function(pixel) { stateLow: "pyrocumulus", category:"gases", state: "gas", + breakInto: ["ash","ash","fireball"], density: 0.8, ignoreAir: true, excludeRandom: true @@ -10797,7 +11270,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|CH:fallout,radiation%0.025|M1%2.5 AND BO", - "CR:radiation%0.05|CR:radiation%0.05|CR:radiation%0.05", + "CR:radiation%0.05|CR:radiation%0.05|CR:radiation%0.05" ], category:"gases", hidden: true, @@ -10810,7 +11283,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "M2%10|XX|M2%10", - "XX|M2%10|XX", + "XX|M2%10|XX" ], reactions: { "rad_steam": { elem1: null, elem2: "rad_cloud", chance:0.3, "y":[0,15], "setting":"clouds" }, @@ -10827,6 +11300,7 @@ SEEDRISE: function(pixel) { stateLow: "fallout", category: "gases", hidden: true, + breakInto: ["fallout","radiation","radiation"], state: "gas", density: 0.7 }, @@ -10847,10 +11321,22 @@ SEEDRISE: function(pixel) { burnTime: 1, density: 1.977, customColor: true, - stain: 0.25 + stain: 0.25, + darkText: true }, "portal_in": { color: "#ff9a00", + onShiftSelect: function(element) { + promptInput("Enter a number to use as the portal channel. (Default 0)", function(r) { + if (r === "") return; + r = parseInt(r); + if (isNaN(r)) { + logMessage("Invalid integer - portal channel not set.") + return; + }; + currentElementProp = { channel:r } + }, elemTitleCase(elements[element].name || element)); + }, tick: function(pixel) { // if (Math.random() > 0.1) return; if (!ticktemp.portal_out) ticktemp.portal_out = {}; @@ -10916,6 +11402,17 @@ SEEDRISE: function(pixel) { }, "portal_out": { color: "#00a2ff", + onShiftSelect: function(element) { + promptInput("Enter a number to use as the portal channel. (Default 0)", function(r) { + if (r === "") return; + r = parseInt(r); + if (isNaN(r)) { + logMessage("Invalid integer - portal channel not set.") + return; + }; + currentElementProp = { channel:r } + }, elemTitleCase(elements[element].name || element)); + }, tick: function(pixel){ doElectricity(pixel); }, @@ -10930,41 +11427,10 @@ SEEDRISE: function(pixel) { conduct:1, emit:true }, -"led_r": { +"led": { color: "#ff0000", - buttonColor: "#660000", - behavior: behaviors.WALL, - renderer: renderPresets.LED, - reactions: { - "light": {charge1:1,elem2:null}, - "liquid_light": {charge1:1,elem2:null} - }, - category: "machines", - tempHigh: 1500, - stateHigh: ["molten_glass","molten_glass","molten_glass","molten_gallium"], - conduct: 1, - breakInto: "glass_shard", - forceSaveColor: true -}, -"led_g": { - color: "#00ff00", - buttonColor: "#006600", - behavior: behaviors.WALL, - renderer: renderPresets.LED, - reactions: { - "light": {charge1:1,elem2:null}, - "liquid_light": {charge1:1,elem2:null} - }, - category: "machines", - tempHigh: 1500, - stateHigh: ["molten_glass","molten_glass","molten_glass","molten_gallium"], - conduct: 1, - breakInto: "glass_shard", - forceSaveColor: true -}, -"led_b": { - color: "#0000ff", - buttonColor: "#000066", + buttonColor: ["#660000","#660000","#006600","#006600","#000066","#000066"], + customColor: true, behavior: behaviors.WALL, renderer: renderPresets.LED, reactions: { @@ -10986,7 +11452,7 @@ SEEDRISE: function(pixel) { behaviorOn: [ "XX|CR:light|XX", "CR:light|XX|CR:light", - "XX|CR:light|XX", + "XX|CR:light|XX" ], category: "machines", tempHigh: 1500, @@ -11015,6 +11481,12 @@ SEEDRISE: function(pixel) { "molten_sulfur": { color: "#831502", behavior: behaviors.LIQUID, + tick: function(pixel) { + if (!pixel.burning && pixel.temp >= 232) { + pixel.burning = true; + pixel.burnStart = pixelTicks; + } + }, reactions: { "iron": { elem1: null, elem2: "pyrite" }, "magnesium": { elem1:null, elem2:"epsom_salt", chance:0.01 }, @@ -11040,7 +11512,7 @@ SEEDRISE: function(pixel) { color: "#b0a65d", burnTime: 10, density: 2.16, - burnInto: ["smoke","smoke","smoke","smoke","stench"], + burnInto: ["smoke","smoke","smoke","smoke","stench"] }, "copper_sulfate": { color: ["#4391fd","#004cfe"], @@ -11070,6 +11542,12 @@ SEEDRISE: function(pixel) { "candy": {elem1:"oxidized_copper", elem2:null, color1:["#CB3D3D","#A6292B","#6E1B1B"]}, "flour": {elem1:"oxidized_copper", elem2:null, color1:["#CB3D3D","#A6292B","#6E1B1B"]}, "chocolate": {elem1:"oxidized_copper", elem2:null, color1:["#CB3D3D","#A6292B","#6E1B1B"]}, + "soda": {elem1:"oxidized_copper", elem2:null, color1:["#CB3D3D","#A6292B","#6E1B1B"]}, + "chocolate_powder": {elem1:"oxidized_copper", elem2:null, color1:["#CB3D3D","#A6292B","#6E1B1B"]}, + "chocolate_milk": {elem1:"oxidized_copper", elem2:null, color1:["#CB3D3D","#A6292B","#6E1B1B"]}, + "juice": {elem1:"oxidized_copper", elem2:null, color1:["#CB3D3D","#A6292B","#6E1B1B"]}, + "fruit_milk": {elem1:"oxidized_copper", elem2:null, color1:["#CB3D3D","#A6292B","#6E1B1B"]}, + "ice_cream": {elem1:"oxidized_copper", elem2:null, color1:["#CB3D3D","#A6292B","#6E1B1B"]}, }, tempHigh: 110, fireColor: ["#91d106","#feff97","#248e01"], @@ -11083,7 +11561,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|LB:plant AND RT%5|M1 AND BO:1,2,3", - "XX|XX|XX", + "XX|XX|XX" ], reactions: { "rat": {elem2:null}, @@ -11099,7 +11577,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|M2|M1", "XX|RT%20|M2", - "CF|XX|XX", + "CF|XX|XX" ], onClicked: behaviors.CLONE_ON_CLICK, ignore: ["cloner","slow_cloner","clone_powder","floating_cloner","ecloner"], @@ -11113,7 +11591,7 @@ SEEDRISE: function(pixel) { "M1 AND SW%33|XX|M1 AND SW%33", "XX|M1 AND SW%33|XX" ], - category:"special", + category:"special" }, "midas_touch": { color: ["#ffdf5e","#ffe682"], @@ -11161,14 +11639,14 @@ SEEDRISE: function(pixel) { }, density: 193, state: "liquid", - category:"special", + category:"special" }, "radiation": { color: ["#00ff00","#6fff00"], behavior: [ "XX|M1%0.5 AND HT|XX", "M1%7 AND HT|DL%3|M1%7 AND HT", - "XX|M1%1 AND HT|XX", + "XX|M1%1 AND HT|XX" ], reactions: { "water": { elem2:"rad_steam", chance:0.4 }, @@ -11281,7 +11759,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|CR:radiation%2|XX", "CR:radiation%2|CH:radiation%0.5|CR:radiation%2", - "M2|M1 AND CR:radiation%2|M2", + "M2|M1 AND CR:radiation%2|M2" ], category:"energy", hidden: true, @@ -11293,7 +11771,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|CH:proton%0.25 AND DL%0.25|XX", - "XX|XX|XX", + "XX|XX|XX" ], tick: behaviors.BOUNCY, reactions: { @@ -11319,12 +11797,12 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|DL%0.5|XX", - "XX|XX|XX", + "XX|XX|XX" ], behaviorOn: [ "XX|XX|XX", "XX|CH:hydrogen|XX", - "XX|XX|XX", + "XX|XX|XX" ], tick: behaviors.BOUNCY, reactions: { @@ -11345,7 +11823,7 @@ SEEDRISE: function(pixel) { behavior: [ "CL%5|CL%5 AND SH|CL%5", "CL%5 AND SH|SH%5 AND DL%50|CL%5 AND SH", - "M1%15 AND CL%6|M1%50 AND CL%13 AND SH|M1%15 AND CL%6", + "M1%15 AND CL%6|M1%50 AND CL%13 AND SH|M1%15 AND CL%6" ], charge: 3, category: "energy", @@ -11353,8 +11831,6 @@ SEEDRISE: function(pixel) { density: 2.1, insulate: true, ignoreAir: true, - ignore: ["shocker"], - ignoreConduct: ["shocker"], alias: "electron" }, "uranium": { @@ -11362,7 +11838,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|RL:radiation%1 AND CH:lead%0.001|XX", - "M2|M1|M2", + "M2|M1|M2" ], reactions: { "neutron": { elem1:"n_explosion", tempMin:500, chance:0.1 } @@ -11452,7 +11928,6 @@ SEEDRISE: function(pixel) { density: 3905, conduct: 0.39, hardness: 0.1, - alias: "gallanylidynealumane", hidden: true }, "metal_scrap": { @@ -11532,7 +12007,8 @@ SEEDRISE: function(pixel) { state: "solid", density: 2000, hardness: 0.325, - hidden: true + hidden: true, + darkText: true }, "feather": { color: ["#ffffff","#e3e3e3","#d1d1d1"], @@ -11548,10 +12024,11 @@ SEEDRISE: function(pixel) { }, "confetti": { color: ["#dc2c37","#edce66","#0dbf62","#0679ea","#7144b2","#d92097"], + buttonColor: ["#ffe629","#ff29c2"], behavior: [ "XX|XX|XX", "XX|FX%0.25|XX", - "M2%25|M1%25|M1%25", + "M2%25|M1%25|M1%25" ], reactions: { "water": { elem1:[null,"cellulose"], chance:0.001 }, @@ -11571,7 +12048,7 @@ SEEDRISE: function(pixel) { density: 1201 }, "glitter": { - color: ["#dbbfe3", "#cc95db","#c477d9","#b85cd1", "#8d5cd1","#9e77d9","#cc95db", "#95a4db","#7789d9","#5c68d1", "#c1bfe3"], + color: ["#c1bfe3", "#dbbfe3", "#cc95db","#c477d9","#b85cd1", "#8d5cd1","#9e77d9","#cc95db", "#95a4db","#7789d9","#5c68d1"], behavior: behaviors.POWDER, category: "powders", tempHigh: 100, @@ -11646,7 +12123,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|XX|XX", - "M2|M1|M2", + "M2|M1|M2" ], tick: function(pixel) { if ((pixel.foam || Math.random() < 0.25) && isEmpty(pixel.x,pixel.y-1)) { @@ -11826,7 +12303,7 @@ SEEDRISE: function(pixel) { behavior: [ "M2|M1|M2", "M1|DL%25|M1", - "M2|M1|M2", + "M2|M1|M2" ], reactions: { "cancer": { elem2:null }, @@ -11907,7 +12384,7 @@ SEEDRISE: function(pixel) { "mercury_gas": { elem2: null }, "solid_mercury": { elem2: null }, "ice_nine": { elem2: "ice" }, - "strange_matter": { elem2: "neutron" }, + "strange_matter": { elem2: null }, "frozen_frog": { elem2: "frog" }, "frozen_worm": { elem2: "worm" }, "molten_thermite": { elem2: "rock" }, @@ -12082,7 +12559,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|EX:3|XX", - "XX|XX|XX", + "XX|XX|XX" ], category: "energy", state: "gas", @@ -12093,11 +12570,20 @@ SEEDRISE: function(pixel) { }, "explosion": { color: ["#ffb48f","#ffd991","#ffad91"], - behavior: [ - "XX|XX|XX", - "XX|EX:10|XX", - "XX|XX|XX", - ], + behavior: behaviors.WALL, + onShiftSelect: function(element) { + promptInput("Enter a radius from 1 to 100. (Default 10)", function(r) { + if (!r) return; + r = parseInt(r); + if (isNaN(r)) return; + r = Math.min(100, r); + r = Math.max(1, r); + currentElementProp = { radius:r } + }, elemTitleCase(elements[element].name || element)); + }, + tick: function(pixel) { + explodeAt(pixel.x, pixel.y, pixel.radius || 10, "fire") + }, temp: 300, category: "energy", state: "gas", @@ -12110,7 +12596,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|EX:40>plasma,plasma,plasma,plasma,radiation,rad_steam|XX", - "XX|XX|XX", + "XX|XX|XX" ], temp: 100000000, category: "energy", @@ -12126,7 +12612,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|EX:80>plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,molten_iron,molten_uranium,molten_lead,oxygen,molten_sodium,sulfur_gas,neon,chlorine,molten_calcium,molten_nickel,molten_copper,molten_zinc,gallium_gas,molten_potassium,mercury,molten_aluminum AND CH:void|XX", - "XX|XX|XX", + "XX|XX|XX" ], temp: 99999999700, category: "energy", @@ -12181,7 +12667,7 @@ SEEDRISE: function(pixel) { behavior: [ "M1%15 AND CL%6|M1%50 AND CL%13|M1%15 AND CL%6", "CL%5|DL%50|CL%5", - "CL%5|CL%5|CL%5", + "CL%5|CL%5|CL%5" ], reactions: { "electric": { elem1:"light", elem2:"explosion" }, @@ -12200,7 +12686,7 @@ SEEDRISE: function(pixel) { behaviorOn: [ "XX|XX|XX", "XX|EX:10|XX", - "XX|XX|XX", + "XX|XX|XX" ], conduct: 1, category: "weapons", @@ -12221,7 +12707,7 @@ SEEDRISE: function(pixel) { behaviorOn: [ "XX|XX|XX", "XX|EX:10|XX", - "XX|M1|XX", + "XX|M1|XX" ], conduct: 1, category: "weapons", @@ -12238,12 +12724,12 @@ SEEDRISE: function(pixel) { behavior: [ "XX|EX:6>metal_scrap,fire,fire,fire%1|XX", "XX|XX|XX", - "M2|M1 AND EX:6>metal_scrap,fire,fire,fire%1|M2", + "M2|M1 AND EX:6>metal_scrap,fire,fire,fire%1|M2" ], behaviorOn: [ "XX|XX|XX", "XX|EX:6>metal_scrap,fire,fire,fire%1|XX", - "M2|M1 AND EX:6>metal_scrap,fire,fire,fire%1|M2", + "M2|M1 AND EX:6>metal_scrap,fire,fire,fire%1|M2" ], category: "weapons", state: "solid", @@ -12261,7 +12747,7 @@ SEEDRISE: function(pixel) { behaviorOn: [ "XX|XX|XX", "XX|EX:15|XX", - "XX|XX|XX", + "XX|XX|XX" ], conduct: 1, category: "weapons", @@ -12278,7 +12764,7 @@ SEEDRISE: function(pixel) { behaviorOn: [ "XX|XX|XX", "XX|EX:10|XX", - "XX|XX|XX", + "XX|XX|XX" ], conduct: 1, category: "weapons", @@ -12297,7 +12783,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|M1|M1", "XX|XX|XX", - "XX|XX|XX", + "XX|XX|XX" ], onCollide: function(pixel1,pixel2) { if (elements[pixel2.element].fireColor) { @@ -12369,7 +12855,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|DL%25|M2", - "XX|M2|M1", + "XX|M2|M1" ], burning: true, burnInto: "ash", @@ -12382,14 +12868,15 @@ SEEDRISE: function(pixel) { hidden: true, state: "gas", density: 700, - alias: "firework ember" + alias: "firework ember", + darkText: true }, "nuke": { color: "#534636", behavior: [ "XX|XX|XX", "XX|XX|XX", - "M2|M1 AND EX:60>plasma,plasma,plasma,plasma,radiation,rad_steam|M2", + "M2|M1 AND EX:60>plasma,plasma,plasma,plasma,radiation,rad_steam|M2" ], category: "weapons", state: "solid", @@ -12402,7 +12889,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|XX|XX", - "M2|M1 AND EX:90>plasma,plasma,plasma,plasma,fire|M2", + "M2|M1 AND EX:90>plasma,plasma,plasma,plasma,fire|M2" ], category: "weapons", state: "solid", @@ -12416,7 +12903,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|XX|XX", - "M2|M1 AND EX:40>radiation,radiation,radiation,rad_steam|M2", + "M2|M1 AND EX:40>radiation,radiation,radiation,rad_steam|M2" ], category: "weapons", state: "solid", @@ -12458,7 +12945,7 @@ SEEDRISE: function(pixel) { behaviorOn: [ "XX|XX|XX", "XX|EX:10|XX", - "XX|XX|XX", + "XX|XX|XX" ], reactions: { "clay": { elem1:"dynamite", elem2:"dynamite" }, @@ -12517,7 +13004,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|CR:fire%25|XX", "XX|CC:782828,783b28,784b28%25|XX", - "M2|M1 AND EX:8|M2", + "M2|M1 AND EX:8|M2" ], reactions: { "water": { elem1:"rock", elem2:"steam" } @@ -12573,6 +13060,7 @@ SEEDRISE: function(pixel) { }, "antibomb": { color: "#adb3be", + onShiftSelect: behaviors.CLONER_SHIFT_SELECT, tick: function(pixel) { doDefaults(pixel) if (!tryMove(pixel,pixel.x,pixel.y+1)) { @@ -12584,7 +13072,7 @@ SEEDRISE: function(pixel) { var elem = "smoke"; } if (elem !== "antibomb") { - explodeAt(pixel.x,pixel.y,8,elem) + explodeAt(pixel.x,pixel.y,8,pixel.clone || elem); } } }, @@ -12599,7 +13087,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|EX:10>cold_fire|XX", "XX|XX|XX", - "M2|M1 AND EX:10>cold_fire|M2", + "M2|M1 AND EX:10>cold_fire|M2" ], category: "weapons", state: "solid", @@ -12614,7 +13102,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|HT:20000 AND EX:15>plasma|XX", "XX|XX|XX", - "M2|M1 AND HT:20000 AND EX:15>plasma|M2", + "M2|M1 AND HT:20000 AND EX:15>plasma|M2" ], category: "weapons", state: "solid", @@ -12627,7 +13115,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|EX:20>antimatter|XX", "XX|XX|XX", - "M2|M1 AND EX:20>antimatter|M2", + "M2|M1 AND EX:20>antimatter|M2" ], category: "weapons", state: "solid", @@ -12643,7 +13131,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|EX:15>confetti,flash,flash,smoke|XX", "XX|XX|XX", - "M2|M1 AND EX:15>confetti,flash,flash,smoke|M2", + "M2|M1 AND EX:15>confetti,flash,flash,smoke|M2" ], category: "weapons", state: "solid", @@ -12656,7 +13144,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|EX:20>flash%1|XX", "XX|XX|XX", - "M2|M1 AND EX:20>flash%1|M2", + "M2|M1 AND EX:20>flash%1|M2" ], category: "weapons", state: "solid", @@ -12687,7 +13175,6 @@ SEEDRISE: function(pixel) { }, reactions: { "blood": { elem1:"pointer" }, - "molten_stained_glass": { elem1:"rainbow" }, "electric": { elem1:"pointer" } }, category: "energy", @@ -12704,7 +13191,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|CR:smoke|XX", "XX|EX:4>smoke%1|XX", - "M2|M1|M2", + "M2|M1|M2" ], category: "weapons", state: "solid", @@ -12721,7 +13208,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|EX:20|XX", "XX|XX|XX", - "XX|M1|XX", + "XX|M1|XX" ], category: "weapons", state: "solid", @@ -12907,7 +13394,7 @@ SEEDRISE: function(pixel) { density: 100000000, maxSize: 1, cooldown: defaultCooldown, - excludeRandom: true, + excludeRandom: true }, "tsunami": { color: ["#91b9bd","#566f99","#192f61"], @@ -12965,7 +13452,7 @@ SEEDRISE: function(pixel) { maxSize: 1, density: 997, cooldown: defaultCooldown, - excludeRandom: true, + excludeRandom: true }, "blaster": { color: ["#ff0000","#ff4d00","#ff7700"], @@ -13010,7 +13497,7 @@ SEEDRISE: function(pixel) { behavior: [ "XX|XX|XX", "XX|EX:10>armageddon,fire,fire,fire,fire,fire,fire,fire,fire,fire,fire,fire,fire%25 AND DL%10|XX", - "XX|XX|XX", + "XX|XX|XX" ], category: "weapons", state: "solid", @@ -13020,34 +13507,6 @@ SEEDRISE: function(pixel) { maxSize: 1, cooldown: defaultCooldown }, -"tesla_coil": { - color: "#725c38", - behavior: behaviors.WALL, - behaviorOn: [ - "XX|CR:plasma|XX", - "CR:plasma|XX|CR:plasma", - "XX|CR:plasma|XX", - ], - category: "machines", - conduct: 1, - insulate: true, - ignoreConduct: ["plasma"], - temp: 7065 -}, -"shocker": { - color: "#78784c", - behavior: behaviors.WALL, - behaviorOn: [ - "XX|CR:electric AND SH|XX", - "CR:electric AND SH|XX|CR:electric AND SH", - "XX|CR:electric AND SH|XX", - ], - colorOn: "#ffff59", - category: "machines", - conduct: 1, - ignore: ["shocker"], - ignoreConduct: ["electric"] -}, "pressure_plate": { color: "#8a8a84", tick: function(pixel) { @@ -13087,12 +13546,12 @@ SEEDRISE: function(pixel) { behavior: [ "XX|CR:foam%2|XX", "M2|CH:algae,cell,mushroom_spore,lichen,yeast,antibody,cellulose,seltzer,oxygen%0.005|M2", - "M1|M1|M1", + "M1|M1|M1" ], behaviorOn: [ "XX|CR:foam%25|XX", "M2|CH:algae,cell,mushroom_spore,lichen,yeast,antibody,cellulose,seltzer,oxygen%5|M2", - "M1|M1|M1", + "M1|M1|M1" ], reactions: { "cancer": { elem1:"dirty_water" }, @@ -13233,7 +13692,7 @@ SEEDRISE: function(pixel) { pixel[currentProp] = currentPropValue; if (currentProp === "temp") pixelTempCheck(pixel); }, - category: "special", + category: "special" }, //ice color: "#c5e9f0" "salt_ice": { color: "#b6ced4" }, @@ -13297,7 +13756,7 @@ else if (currentMonth == 9) { // October elements.candy.colorPattern = [ "W","W","W","W","W", "O","O","O","O","O", - "Y","Y","Y","Y","Y", + "Y","Y","Y","Y","Y" ] elements.candy.colorKey = {"W":"#ffffff", "Y":"#ffcd55", "O":"#ff9933"} elements.candy.color = ["#ffffff","#ffffff","#ff9933","#ff9933","#ffcd55","#ffcd55"] @@ -13314,7 +13773,7 @@ else if (currentMonth == 11) { // December "RRWWRRRWWR", "RWWWRRWWWR", "RWWRRRWWRR", - "WWWRRWWWRR", + "WWWRRWWWRR" ] elements.candy.colorKey = {"R":"#c92626", "W":"#e3e3e3"} elements.candy.color = ["#c92626","#e3e3e3","#c92626","#e3e3e3","#c92626"] @@ -13382,7 +13841,7 @@ keybinds = {}; keybinds["Minus"] = ()=>{ // Decrease the mouse size by 2 if (shiftDown) {mouseSize = 1} else { - mouseSize -= 2; + mouseSize -= 1; if (mouseSize < 1) { mouseSize = 1; } } checkMouseSize(true); @@ -13390,7 +13849,7 @@ keybinds["Minus"] = ()=>{ // Decrease the mouse size by 2 keybinds["BracketLeft"] = keybinds["Minus"]; keybinds["Equal"] = ()=>{ // Increase the mouse size by 2 if (shiftDown) {mouseSize = (mouseSize+15)-((mouseSize+15) % 15)} - else {mouseSize += 2;} + else {mouseSize += 1;} checkMouseSize(true); } keybinds["BracketRight"] = keybinds["Equal"]; @@ -13614,6 +14073,16 @@ else if (settings.lang && settings.lang !== "en") { else if (window.initialData && window.initialData.langCode && window.initialData.langCode !== "en") { langCode = window.initialData.langCode; } +else if (!standalone) { + try { + // try auto-setting language + let code = navigator.languages ? navigator.languages[0] : navigator.language; + if (code && !code.match(/^en-?/)) { + langCode = code.toLowerCase().split("-")[0]; + } + } + catch (e) {} +} // langCode = "test"; if (langCode) { try { @@ -14095,6 +14564,11 @@ function langKey(key,fallback,template) { } return null; } + function pixelDistance(x1, y1, x2, y2) { + let a = x1 - x2; + let b = y1 - y2; + return Math.sqrt( a*a + b*b ); + } validDensitySwaps = { "solid": { @@ -14148,7 +14622,7 @@ function langKey(key,fallback,template) { // Density if (info.density !== undefined && elements[newPixel.element].density !== undefined) { // if the pixel's state + ">" + newPixel's state is in validDensitySwaps, and the pixel's density is larger than the newPixel's density, swap the pixels - if (validDensitySwaps[info.state][elements[newPixel.element].state] !== undefined && info.density >= elements[newPixel.element].density) { + if (validDensitySwaps[info.state][elements[newPixel.element].state] !== undefined && info.density >= elements[newPixel.element].density && newPixel.skip !== true) { // chance depending on the difference in density if (Math.random() < (info.density - elements[newPixel.element].density)/(info.density + elements[newPixel.element].density)) { swapPixels(pixel,newPixel); @@ -14445,7 +14919,7 @@ behaviorRules = { return; } if (newPixel.element !== btemp.pixel.element) { - btemp.pixel.clone = newPixel.element; + btemp.pixel.clone = elements[newPixel.element].pickElement || newPixel.element; btemp.pixel.temp = newPixel.temp; } else if (newPixel.clone) { @@ -14674,7 +15148,7 @@ behaviorRules = { } releaseElement(btemp.pixel, btemp.arg); }, - "XX": function() {}, + "XX": function() {} } function pixelTick(pixel,custom=null) { if (pixel.start === pixelTicks) {return} @@ -15007,7 +15481,10 @@ behaviorRules = { var con = elements[newPixel.element].conduct; if (con === undefined) {continue} if (elements[newPixel.element].ignoreConduct !== undefined && elements[newPixel.element].ignoreConduct.indexOf(pixel.element) !== -1) { - continue + continue; + } + if (elements[pixel.element].onlyConduct !== undefined && elements[pixel.element].onlyConduct.indexOf(newPixel.element) === -1) { + continue; } if (newPixel.temp <= elements[newPixel.element].superconductAt) { con = 1; @@ -15545,6 +16022,48 @@ behaviorRules = { "pixelMap":compressedMap } } + saveElementMigrations = { + "sticky_bomb": function(pixel) { + pixel.element = "bomb"; + }, + "cluster_bomb": function(pixel) { + pixel.element = "antibomb"; + pixel.clone = "grenade"; + }, + "electro_bomb": function(pixel) { + pixel.element = "antibomb"; + pixel.clone = "electric"; + }, + "water_bomb": function(pixel) { + pixel.element = "antibomb"; + pixel.clone = "water"; + }, + "tesla_coil": function(pixel) { + pixel.element = "ecloner"; + pixel.clone = "plasma"; + }, + "shocker": function(pixel) { + pixel.element = "ecloner"; + pixel.clone = "electric"; + }, + "led_r": function(pixel) { + pixel.element = "led"; + }, + "led_g": function(pixel) { + pixel.element = "led"; + }, + "led_b": function(pixel) { + pixel.element = "led"; + }, + "superheater": function(pixel) { + pixel.element = "heater"; + pixel.temp = 30; + }, + "freezer": function(pixel) { + pixel.element = "cooler"; + pixel.temp = 10; + }, + } function loadSave(saveJSON, confirmed=0, skip=0) { if (!saveJSON.pixelMap) { return } var compressedMap = saveJSON.pixelMap; @@ -15624,16 +16143,18 @@ behaviorRules = { for (var y = 0; y < pixelarray[x].length; y++) { var pixel = pixelarray[x][y]; if (pixel) { + let element; for (var prop in pixel) { if (saveJSON.codes[prop]) { pixel[saveJSON.codes[prop]] = pixel[prop]; delete pixel[prop]; } - if (pixel.element) { - pixel.element = saveJSON.codes[pixel.element]||pixel.element; - if (!elements[pixel.element]) { - pixel.element = "unknown"; - } + } + if (pixel.element) { + pixel.element = saveJSON.codes[pixel.element]||pixel.element; + if (element === undefined) element = pixel.element; + if (!elements[pixel.element]) { + pixel.element = "unknown"; } } createPixel(pixel.element,x,y); @@ -15642,6 +16163,12 @@ behaviorRules = { // if (!pixelMap[x][y]) continue; pixelMap[x][y][prop] = pixel[prop]; } + if (saveElementMigrations[element]) { + saveElementMigrations[element](pixelMap[x][y], version); + if (pixel.color === undefined && pixel.element === "unknown") { + pixelMap[x][y].color = pixelColorPick(pixelMap[x][y]); + } + } } else { pixelarray[x][y] = undefined; @@ -15880,7 +16407,7 @@ behaviorRules = { if (ctx === null) return clearLayers(); ctx.clearRect(0, 0, canvas.width, canvas.height); - if (settings.bg) { // Set background color + if (settings.bg && !settings.bgimg) { // Set background color if (canvas.style.backgroundColor !== settings.bg) { canvas.style.backgroundColor = settings.bg; // document.getElementById("stats").style.backgroundColor = settings.bg; @@ -15909,13 +16436,18 @@ behaviorRules = { } function drawCursor() { var layerCtx = canvasLayers.gui.getContext('2d'); - var mouseOffset = Math.trunc(mouseSize/2); - var topLeft = [mousePos.x-mouseOffset,mousePos.y-mouseOffset]; - var bottomRight = [mousePos.x+mouseOffset,mousePos.y+mouseOffset]; + let mouseOffset = mouseSize/2; + let mouseMin = Math.trunc(mouseOffset); + let mouseMax = mouseMin; + if (mouseOffset % 1 === 0) { + mouseMax --; + } + var topLeft = [mousePos.x-mouseMin,mousePos.y-mouseMin]; + var bottomRight = [mousePos.x+mouseMax,mousePos.y+mouseMax]; // Draw a square around the mouse layerCtx.globalAlpha = mouseAlpha; layerCtx.lineWidth = 2; - layerCtx.strokeStyle = settings.mouseColor||mouseColor||"white"; + layerCtx.strokeStyle = mouseColor||settings.mouseColor||"white"; layerCtx.strokeRect(topLeft[0]*pixelSize,topLeft[1]*pixelSize,(bottomRight[0]-topLeft[0]+1)*pixelSize,(bottomRight[1]-topLeft[1]+1)*pixelSize); // draw one transparent pixel in the center if (settings.precision) { @@ -15927,7 +16459,7 @@ behaviorRules = { let coords = lineCoords(shapeStart.x,shapeStart.y,mousePos.x,mousePos.y); coords.forEach((coord) => { if (outOfBounds(coord[0],coord[1])) return; - drawSquare(layerCtx,mouseColor,coord[0],coord[1],undefined,mouseAlpha) + drawSquare(layerCtx,mouseColor,coord[0],coord[1],undefined,0.5) }) } } @@ -15937,6 +16469,11 @@ behaviorRules = { var newCurrentPixels = currentPixels.slice(); shuffleArray(newCurrentPixels); + if (hasUpdateOrder) { + newCurrentPixels.sort((a, b) => { + return (elements[a.element].updateOrder || 0) - (elements[b.element].updateOrder || 0) + }) + } for (var i = 0; i < newCurrentPixels.length; i++) { var pixel = newCurrentPixels[i]; @@ -15979,6 +16516,7 @@ behaviorRules = { viewKey = {/*DEPRECATED*/} thermalMax = 100; thermalMin = 0; + isMachine = {"machines":true} viewInfo = { 1: { // Normal View (Default) name: "", @@ -16047,6 +16585,63 @@ behaviorRules = { pixel: function(pixel,ctx) { viewInfo[1].pixel(pixel,ctx); } + }, + 5: { // Outline View + name: "Outline", + effects: true, + colorEffects: true, + alias: 1, + onUnselect: function(ctx) { + if (viewInfo[1].onUnselect) viewInfo[1].onUnselect(ctx); + }, + pre: function(ctx) { + if (!settings.bg || settings.bg === '#000000') { + ticktemp.outline = '#ffffff'; + } + else ticktemp.outline = '#000000'; + if (viewInfo[1].pre) viewInfo[1].pre(ctx); + currentPixels.forEach(pixel => { + if ((elements[pixel.element].movable !== true && isMachine[elements[pixel.element].category] === undefined) || elements[pixel.element].isGas === true) return; + if (elements[pixel.element].outline === false) return; + if (pixel.alpha === 0) return; + let edge = false; + for (var i = 0; i < adjacentCoords.length; i++) { + var coords = adjacentCoords[i]; + var x = pixel.x + coords[0]; + var y = pixel.y + coords[1]; + if (isEmpty(x,y, true)) { + // if (elements[pixelMap[x][y].element].id !== elements[pixel.element].id || elements[pixelMap[x][y].element].state !== elements[pixel.element].id) continue + edge = true; + break; + } + } + if (edge) drawSquare(ctx,ticktemp.outline,pixel.x-1,pixel.y-1,3); + }) + }, + pixel: function(pixel, ctx) { + if (elements[pixel.element].movable || isMachine[elements[pixel.element].category] === true) return viewInfo[1].pixel(pixel, ctx); + if (pixel.alpha === 0) return; + let edge = false; + if (elements[pixel.element].outline !== false) { + for (var i = 0; i < adjacentCoords.length; i++) { + var coords = adjacentCoords[i]; + var x = pixel.x + coords[0]; + var y = pixel.y + coords[1]; + if (isEmpty(x,y) || (!outOfBounds(x,y) && + elements[pixelMap[x][y].element].movable + )) { + // if (elements[pixelMap[x][y].element].id !== elements[pixel.element].id || elements[pixelMap[x][y].element].state !== elements[pixel.element].id) continue + edge = true; + break; + } + } + }; + if (edge) drawSquare(ctx,ticktemp.outline,pixel.x,pixel.y); + else viewInfo[1].pixel(pixel, ctx); + }, + post: function(ctx) { + if (viewInfo[1].post) viewInfo[1].post(ctx); + } } } function canvasCoord(n) { @@ -16085,7 +16680,10 @@ behaviorRules = { i--; continue; } - if (pixel.con) { pixel = pixel.con } + if (pixel.con) { + if (!elements[pixel.con.element]) delete pixel.con; + else pixel = pixel.con; + } if (elements[pixel.element].isGas || elements[pixel.element].glow) { pixelsLast.push(pixel); } @@ -16130,6 +16728,9 @@ behaviorRules = { renderPostPixelList[i](ctx); } } + if (elements[currentElement].renderTool !== undefined) { + elements[currentElement].renderTool(ctx); + } if (viewInfo[view].post) { viewInfo[view].post(ctx); } @@ -16194,6 +16795,7 @@ behaviorRules = { promptState = null; mouseIsDown = false; isMobile = false; + hasUpdateOrder = false; genericMenuHandler = null; changedMods = false; forceReload = false; @@ -16365,14 +16967,16 @@ behaviorRules = { lastPos = mousePos; lastPlace = -100; if (typeof settings.mouseColor !== "string") { delete settings.mouseColor } - mouseColor = settings.mouseColor||"rgba(255,255,255)"; + mouseColor = settings.mouseColor||"rgb(255,255,255)"; mouseAlpha = 0.5; function mouseRange(mouseX,mouseY,size) { var coords = []; size = size || mouseSize; - var mouseOffset = Math.trunc(size/2); - var topLeft = [mouseX-mouseOffset,mouseY-mouseOffset]; - var bottomRight = [mouseX+mouseOffset,mouseY+mouseOffset]; + let mouseOffset = size/2; + let mouseMin = Math.trunc(mouseOffset); + let mouseMax = mouseMin - (mouseOffset % 1 ? 0 : 1); + var topLeft = [mouseX-mouseMin,mouseY-mouseMin]; + var bottomRight = [mouseX+mouseMax,mouseY+mouseMax]; // Starting at the top left, go through each pixel for (var x = topLeft[0]; x <= bottomRight[0]; x++) { for (var y = topLeft[1]; y <= bottomRight[1]; y++) { @@ -16441,7 +17045,7 @@ behaviorRules = { } } else if (existingPixel !== null && elements[existingPixel.element].onClicked && currentElement !== existingPixel.element) { - elements[existingPixel.element].onClicked(existingPixel,currentElement); + elements[existingPixel.element].onClicked(existingPixel,currentElement === "unknown" ? null : currentElement); } if (currentElement === "mix") { @@ -16469,12 +17073,14 @@ behaviorRules = { if (isEmpty(x, y)) { if (currentPixels.length < maxPixelCount) { createPixel(currentElement,x,y); - if (pixelMap[x][y] && currentElement === pixelMap[x][y].element && (elements[currentElement].customColor || elements[currentElement].singleColor)) { - pixelMap[x][y].color = pixelColorPick(pixelMap[x][y],currentColorMap[currentElement]); - } - if (currentElementProp) { - for (var key in currentElementProp) { - pixelMap[x][y][key] = currentElementProp[key] + if (pixelMap[x][y]) { + if (currentElement === pixelMap[x][y].element && (elements[currentElement].customColor || elements[currentElement].singleColor)) { + pixelMap[x][y].color = pixelColorPick(pixelMap[x][y],currentColorMap[currentElement]); + } + if (currentElementProp) { + for (var key in currentElementProp) { + pixelMap[x][y][key] = currentElementProp[key] + } } } } @@ -16591,6 +17197,9 @@ behaviorRules = { if (es !== null) { selectElement(es); selectCategory(elements[es].category); + if (shiftDown && elements[currentElement].onShiftSelect) { + elements[currentElement].onShiftSelect(currentElement); + } } else { logMessage("Element \"" + e + "\" not found"); @@ -16734,6 +17343,7 @@ behaviorRules = { function editDistance(s1, s2) {s1 = s1.toLowerCase();s2 = s2.toLowerCase();var costs = new Array();for (var i = 0; i <= s1.length; i++) {var lastValue = i;for (var j = 0; j <= s2.length; j++) {if (i == 0)costs[j] = j;else {if (j > 0) {var newValue = costs[j - 1];if (s1.charAt(i - 1) !== s2.charAt(j - 1))newValue = Math.min(Math.min(newValue, lastValue),costs[j]) + 1;costs[j - 1] = lastValue;lastValue = newValue;}}}if (i > 0)costs[s2.length] = lastValue;}return costs[s2.length];} function similarity(s1, s2) {var longer = s1;var shorter = s2;if (s1.length < s2.length) {longer = s2;shorter = s1;}var longerLength = longer.length;if (longerLength == 0) {return 1.0;}return (longerLength - editDistance(longer, shorter)) / parseFloat(longerLength);} function mostSimilarElement(s) { + if (elements[s]) return s; var max = 0; var maxElement = ""; for (var e in elements) { @@ -16808,12 +17418,42 @@ behaviorRules = { } else { selectElement(this.getAttribute("element")); + if (shiftDown && elements[currentElement].onShiftSelect) { + elements[currentElement].onShiftSelect(currentElement); + } } } function elemTitleCase(string) { string = string.replace(/_./g, function (match) {return match.toUpperCase();}).replace(/_/g, ''); return string.charAt(0).toUpperCase() + string.slice(1); } + function validateElementList(string) { + if (!string) return ""; + if (string.toString) string = string.toString(); + if (string.match(/,/)) { + let newList = []; + let list = string.split(","); + list.forEach(element => { + element = element.trim().replace(/ /g, "_"); + if (validDensitySwaps[element]) { + newList.push(element); + return; + } + if (element === "null" || element === "air") { + newList.push("unknown"); + return; + } + element = mostSimilarElement(element); + newList.push(elements[element] ? element : "unknown"); + }) + return newList.join(","); + } + string = string.trim().replace(/ /g, "_"); + if (validDensitySwaps[string]) return string; + string = mostSimilarElement(string); + if (string === "null" || string === "air") return "unknown"; + return elements[string] ? string : "unknown"; + } function createElementButton(element) { var button = document.createElement("button"); // if the element has the attribute "name", use that as the button's text, otherwise use the element with underscores replaced by spaces @@ -16843,19 +17483,26 @@ behaviorRules = { else colorObjects = [hexToRGB(buttonColor)]; } + let average = averageRGB(colorObjects).match(/\d+/g); + let hsl = RGBToHSL(average); + let bright = hsl[2] > 0.7; + if (elements[element].darkText || (bright && elements[element].darkText !== false)) { + button.classList.add("bright"); + hsl[1] = Math.min(hsl[1], 0.3) + } + else { + hsl[1] = Math.min(hsl[1], 0.5) + } + hsl[2] = 0.35; + button.setAttribute("style", "--button-border:hsl("+Math.round(hsl[0]*360)+"deg "+Math.round(hsl[1]*100)+"% "+Math.round(hsl[2]*100)+"%)") + if (colorObjects.length === 1) { button.style.background = buttonColor; var colorObject = elements[element].colorObject; - if (elements[element].darkText !== false && (elements[element].darkText || (colorObject.r+colorObject.g+colorObject.b)/3 > 200)) { - button.className += " bright" - } } else { button.style.backgroundImage = "linear-gradient(to bottom right, "+buttonColor.join(", ")+")"; var colorObject = colorObjects[Math.floor(colorObjects.length/2)]; - if (elements[element].darkText !== false && (elements[element].darkText || (colorObject.r+colorObject.g+colorObject.b)/3 > 200)) { - button.className += " bright" - } } if (elements[element].buttonGlow) { @@ -16901,7 +17548,7 @@ behaviorRules = { if (elements[element] && elements[element].hidden && !settings.unlocked[element]) { settings.unlocked[element] = true; if (settings.unhide === 2) { - createElementButton(element) + createElementButton(element); var categoryButton = document.querySelector(".categoryButton[current='true']"); var currentCategory = categoryButton.getAttribute("category"); if (currentCategory !== elements[element].category) { @@ -16930,6 +17577,7 @@ behaviorRules = { if (styleTag) styleTag.remove(); mouseColor = settings.mouseColor||"rgba(255,255,255)"; mouseAlpha = 0.5; + if (settings.bgimg) setCanvasBackground(settings.bgimg); return; } let rgb; @@ -18291,7 +18939,6 @@ for (var k = 0; k < b0.split(" AND ").length; k++) { } function confirmSave(confirmed=0) { if (!savingState) { return } - console.log(confirmed); if (confirmed < 1 && savingState.slot && settings.resetwarning!==0 && localStorage["SandboxelsSaves/"+savingState.slot]) { let tempState = savingState; promptConfirm("Are you sure you want to overwrite the current save in slot "+savingState.slot+"? This cannot be undone.", @@ -18353,6 +19000,13 @@ for (var k = 0; k < b0.split(" AND ").length; k++) { }, 0); closeMenu(); } + + if (currentSaveData) { + if (savingState && !isNaN(savingState.slot)) currentSaveData.slot = savingState.slot; + currentSaveData.name = saveName; + currentSaveData.author = saveAuthor; + currentSaveData.desc = saveDesc; + } } function saveToFile() { savingState = {slot:null}; @@ -18364,7 +19018,7 @@ for (var k = 0; k < b0.split(" AND ").length; k++) { function loadFromFile() { var input = document.createElement("input"); input.type = "file"; - // input.accept = ".sbxls,.json,.txt,text/*,application/json"; + if (!isMobile) input.accept = ".sbxls,.json,.txt,text/*,application/json,application/vnd.R74n.sandboxels+json"; input.addEventListener("change", function(e) { var file = e.target.files[0]; var reader = new FileReader(); @@ -18515,10 +19169,79 @@ for (var k = 0; k < b0.split(" AND ").length; k++) { if (setting) { setSetting(setting,0); } } } + canvasBackgrounds = { + "day": ["backgrounds/sky.png", "#9EC2F6", true], + "grass": ["backgrounds/sky_grass.png", "#4FA760", true], + "dawn": ["backgrounds/sky_pink.png", "#DB79B2", false], + "sunset": ["backgrounds/sky_warm.png", "#EC8575", true], + "dusk": ["backgrounds/sky_purple.png", "#763CB5", true], + "night": ["backgrounds/sky_dark.png", "#181628", false], + }; + function setCanvasBackground(key) { + if (key === "color") { + if (settings.bgimg) clearCanvasBackground(); + return; + }; + if (key === "custom") return; + let url; + let color; + let mousecolor; + if (key === "url") { + promptInput("Enter a URL for a background image.", (url) => { + if (!url) return; + if (!url.startsWith("http")) url = "https://"+url; + setCanvasBackground(url); + }); + return; + }; + if (key.startsWith("http")) url = key; + else if (canvasBackgrounds[key]) { + url = canvasBackgrounds[key][0]; + color = canvasBackgrounds[key][1]; + mousecolor = canvasBackgrounds[key][2] ? "rgb(0,0,0)" : "rgb(255,255,255)"; + } + + if (settings.bgimg !== key) { + if (color) { + settings.bg = color; + document.querySelector('.multisetting[setting="bg"] input').value = color; + } + settings.bgimg = key; + saveSettings(); + } + if (color && !settings.theme) setTheme(color); + if (mousecolor) { + mouseColor = mousecolor; + mouseAlpha = 0.5; + } + + gameCanvas.style.backgroundColor = ""; + setTimeout(function() { + gameCanvas.style.backgroundImage = `url(${url})`; + gameCanvas.style.backgroundRepeat = "no-repeat"; + gameCanvas.style.backgroundSize = "cover"; + gameCanvas.style.backgroundPosition = "bottom"; + },100); + } + function clearCanvasBackground() { + if (!settings.bgimg) return; + delete settings.bgimg; + gameCanvas.style.backgroundImage = ""; + gameCanvas.style.backgroundRepeat = ""; + gameCanvas.style.backgroundSize = ""; + gameCanvas.style.backgroundPosition = ""; + if (settings.theme) setThemeElement(settings.theme); + else setTheme(); + saveSettings(); + } function checkMouseSize(changed) { + let diff = Math.sign(mouseSize - lastMouseSize); if (changed) { lastMouseSize = mouseSize } else if (lastMouseSize) { mouseSize = lastMouseSize } if (mouseSize < 1) { mouseSize = 1; } + if (mouseSize % 2 === 0 && mouseSize !== 2 && lastMouseSize !== 4) { + mouseSize += diff || -1; + } if (settings.limitless && !elements[currentElement].tool) { return } if (mouseSize > elements[currentElement].maxSize) { mouseSize = elements[currentElement].maxSize; } if (mouseSize > (height > width ? (height + height % 2) : (width + width % 2))) { mouseSize = (height > width ? (height + height % 2) : (width + width % 2)); } @@ -18789,7 +19512,6 @@ function autoGen(newname,element,autoType) { elements[newname].reactions.smog = { elem1:null, elem2:"molten_slag" } elements[newname].reactions.pyrocumulus = { elem1:null, elem2:"molten_slag" } elements[newname].reactions.dioxin = { elem1:null, elem2:"molten_slag" } - elements[newname].reactions.poison_gas = { elem1:null, elem2:"molten_slag" } elements[newname].reactions.dirt = { elem1:null, elem2:"molten_slag" } elements[newname].reactions.molten_dirt = { elem1:null, elem2:"molten_slag" } }; @@ -19101,6 +19823,10 @@ window.onload = function() { finalizeColor(elements[key]) checkAutoGen(key,elements[key]) + + if (elements[key].updateOrder !== undefined) { + hasUpdateOrder = true; + } } // Loop through each element. If it has a tempHigh, but not a stateHigh, create a new molten element @@ -19172,9 +19898,9 @@ window.onload = function() { } } else { - var settingElements = settingSpans[i].getElementsByTagName("select") + var settingElements = settingSpans[i].getElementsByTagName("input") if (settingElements.length === 0) { - settingElements = settingSpans[i].getElementsByTagName("input"); + settingElements = settingSpans[i].getElementsByTagName("select"); } if (settingElements.length > 0) { settingElements[0].value = settingValue; @@ -19201,9 +19927,9 @@ window.onload = function() { if (newWidth > 1000) { newWidth = 1000; } // If we are on a desktop and the new height is greater than 500, set it to 500 if (window.innerWidth > 1000 && newHeight > 500) { newHeight = 500; } - canvas.style.backgroundColor = settings.bg||"#000000"; + if (!settings.bgimg) canvas.style.backgroundColor = settings.bg||"#000000"; - autoResizeCanvas = function(clear) { + autoResizeCanvas = function(clear, ratio) { pixelSize = parseInt(settings.pixelsize) || 6; if (window.innerWidth < 700) { pixelSize--; @@ -19221,8 +19947,8 @@ window.onload = function() { // If we are on a desktop and the new height is greater than 500, set it to 500 if (window.innerWidth > 1000 && newHeight > 500) { newHeight = 500; } - newHeight -= newHeight % pixelSize; - newWidth -= newWidth % pixelSize; + // newHeight -= newHeight % pixelSize; + // newWidth -= newWidth % pixelSize; document.getElementById("gameDiv").classList.remove("gameDiv-wide"); if (settings.pixelsize && settings.pixelsize.toString().slice(-1) === "w") { @@ -19231,11 +19957,26 @@ window.onload = function() { newWidth = window.innerWidth-10; document.getElementById("gameDiv").classList.add("gameDiv-wide"); } + + if (ratio) { + if (newHeight < newWidth) { + newWidth = ratio * newHeight; + } + else { + newHeight = ratio * newWidth; + } + } + resizeCanvas(newHeight,newWidth,pixelSize,clear); } + autoResizeRatio = function(height, width) { + autoResizeCanvas(undefined, width/height) + } resizeCanvas = function(newHeight,newWidth,newPixelSize,clear) { var gameCanvas = document.getElementById("game"); var ctx = gameCanvas.getContext("2d"); + newHeight -= newHeight % pixelSize; + newWidth -= newWidth % pixelSize; ctx.canvas.width = newWidth; ctx.canvas.height = newHeight; document.getElementById("gameDiv").style.width = newWidth + "px"; @@ -19262,6 +20003,25 @@ window.onload = function() { } autoResizeCanvas(false); + let bgSelect = document.querySelector('.multisetting[setting="bg"] select'); + for (let key in canvasBackgrounds) { + let option = document.createElement("option"); + option.value = key; + option.id = "setting-bg-"+key; + option.innerText = elemTitleCase(key); + bgSelect.appendChild(option); + } + if (settings.bgimg) { + if (bgSelect.querySelector(`option[value="${settings.bgimg}"]`)) { + bgSelect.value = settings.bgimg; + } + else { + bgSelect.value = "custom"; + } + setCanvasBackground(settings.bgimg); + } + bgSelect.appendChild(document.getElementById("setting-bg-url")); + document.getElementById("loadingP").style.display = "none"; document.getElementById("canvasDiv").style.display = "block"; @@ -19270,20 +20030,31 @@ window.onload = function() { randomChoices = Object.keys(elements).filter(function(e) { return elements[e].excludeRandom != true && elements[e].category !== "tools" && !elements[e].tool; }); - gameCanvas.addEventListener("mousedown", mouseClick); gameCanvas.addEventListener("mousedown", function(e){ - if (elements[currentElement] && elements[currentElement].onMouseDown) { + if (!showingMenu && elements[currentElement] && elements[currentElement].onMouseDown) { elements[currentElement].onMouseDown(e); } }); + gameCanvas.addEventListener("mousedown", mouseClick); gameCanvas.addEventListener("touchstart", function(e) { - if (e.touches) e = e.touches[0]; mousePos = getMousePos(canvas, e); + if (e.touches) e = e.touches[0]; + if (!showingMenu && elements[currentElement] && elements[currentElement].onMouseDown) { + elements[currentElement].onMouseDown(e); + } return false; }); gameCanvas.addEventListener("touchstart", mouseClick, { passive: false }); window.addEventListener("mouseup", mouseUp); window.addEventListener("touchend", mouseUp, { passive: false }); + gameCanvas.addEventListener("touchend", function(e) { + // mousePos = getMousePos(canvas, e); + if (e.touches) e = e.touches[0]; + if (!showingMenu && elements[currentElement] && elements[currentElement].onMouseUp) { + elements[currentElement].onMouseUp(e); + } + return false; + }); window.addEventListener("mousemove", mouseMove); gameCanvas.addEventListener("touchmove", mouseMove, { passive: false }); gameCanvas.addEventListener("wheel", wheelHandle); @@ -19379,6 +20150,20 @@ window.onload = function() { document.getElementById("game").oncontextmenu = function(e) { e.preventDefault(); return false; } document.addEventListener("keydown", function(e) { var keyName = e.code; + if (e.keyCode == 16) { // shift + if (event.location === KeyboardEvent.DOM_KEY_LOCATION_LEFT) { + shiftDown = 1; + } else if (event.location === KeyboardEvent.DOM_KEY_LOCATION_RIGHT) { + shiftDown = 3; + } + } + else if (e.keyCode == 18) { // alt + if (event.location === KeyboardEvent.DOM_KEY_LOCATION_LEFT) { + shiftDown = 2; + } else if (event.location === KeyboardEvent.DOM_KEY_LOCATION_RIGHT) { + shiftDown = 4; + } + } if (e.ctrlKey || e.metaKey) { if (e.keyCode == 83) { // Ctrl+S = Save e.preventDefault(); @@ -19450,26 +20235,12 @@ window.onload = function() { } } } + // if the user is in an input, return if (document.activeElement.tagName === "INPUT" || document.activeElement.tagName === "TEXTAREA") { return; } - if (e.keyCode == 16) { // shift - if (event.location === KeyboardEvent.DOM_KEY_LOCATION_LEFT) { - shiftDown = 1; - } else if (event.location === KeyboardEvent.DOM_KEY_LOCATION_RIGHT) { - shiftDown = 3; - } - } - else if (e.keyCode == 18) { // alt - if (event.location === KeyboardEvent.DOM_KEY_LOCATION_LEFT) { - shiftDown = 2; - } else if (event.location === KeyboardEvent.DOM_KEY_LOCATION_RIGHT) { - shiftDown = 4; - } - } - if (keybinds[e.key]) { e.preventDefault(); keybinds[e.key](e); @@ -19645,7 +20416,7 @@ window.onload = function() {
    - +
    + crossorigin="anonymous"> From 980f043ddbc91a7fae03a43bea234b372afa0108 Mon Sep 17 00:00:00 2001 From: slweeb <91897291+slweeb@users.noreply.github.com> Date: Sat, 27 Dec 2025 23:06:16 -0500 Subject: [PATCH 35/38] Fix LED names --- mods/oldleds.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mods/oldleds.js b/mods/oldleds.js index 9e3aa506..bb00cde5 100644 --- a/mods/oldleds.js +++ b/mods/oldleds.js @@ -1,18 +1,18 @@ -elements.rled = { +elements.led_r = { ...elements.led, customColor: false, color: "rgb(255,0,0)", colorObject: {r:255, g:0, b:0} }; -elements.gled = { +elements.led_g = { ...elements.led, customColor: false, color: "rgb(0,255,0)", colorObject: {r:0, g:255, b:0} }; -elements.bled = { +elements.led_b = { ...elements.led, customColor: false, color: "rgb(0,0,255)", From 60eb340dc07708e60b51ac031dbbf8242afaab63 Mon Sep 17 00:00:00 2001 From: redbirdly <155550833+redbirdly@users.noreply.github.com> Date: Sun, 28 Dec 2025 12:12:47 +0800 Subject: [PATCH 36/38] Fix inconsistent indentation --- mod-list.html | 962 +++++++++++++++++++++++++------------------------- 1 file changed, 481 insertions(+), 481 deletions(-) diff --git a/mod-list.html b/mod-list.html index 305efb22..234367e5 100644 --- a/mod-list.html +++ b/mod-list.html @@ -2,79 +2,79 @@ - - Sandboxels Mods + Tutorial - - - - - - - + + Sandboxels Mods + Tutorial + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - + + + + + + + + - - + + @@ -82,435 +82,435 @@
    -

    Sandboxels has a huge selection of mods that add new content to the simulator. They are created by community members and aren't endorsed by the developer of Sandboxels.

    +

    Sandboxels has a huge selection of mods that add new content to the simulator. They are created by community members and aren't endorsed by the developer of Sandboxels.

    -

    Jump to: Create your own mod, Issues with mods

    +

    Jump to: Create your own mod, Issues with mods

    -

    How to enable a mod

    -
      -
    1. Go to the official Sandboxels website.
    2. -
    3. Press the Mods button in the toolbar to open the Mod Manager.
    4. -
    5. Type the full file name of the mod, listed below. You can also enter a whole URL. It MUST be exactly as written, case-sensitive and including the ".js" ending.
    6. -
    7. Press enter, then refresh the page.
    8. -
    +

    How to enable a mod

    +
      +
    1. Go to the official Sandboxels website.
    2. +
    3. Press the Mods button in the toolbar to open the Mod Manager.
    4. +
    5. Type the full file name of the mod, listed below. You can also enter a whole URL. It MUST be exactly as written, case-sensitive and including the ".js" ending.
    6. +
    7. Press enter, then refresh the page.
    8. +
    -

    Sandboxels Mod List

    -

    Mods submitted to our GitHub repo are listed here.

    - - - - - +

    Sandboxels Mod List

    +

    Mods submitted to our GitHub repo are listed here.

    +
    Mod NameDescriptionCreator
    + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - -
    Mod NameDescriptionCreator
    Top-rated Mods
    chem.jsSeveral chemistry and physics-related elementslllllllllwith10ls
    aChefsDream.jsMore foods, animals, tools, and other cooking items [YouTube Playlist]SquareScreamYT
    delete_all_of_element.jsTool that deletes every pixel of the element(s) the user clicks onAlice
    survival.jsWith limited resources, you must craft, sell, and buy to progressR74n
    alchemy.jsStart with only 4 elements and unlock more by reacting them together (Most are not possible)R74n
    nousersthings.jsMany chemical elements, compounds, and morenousernamefound
    spring.jsMany nature elements, like sakura trees, butterflies, beehives, and moreR74n
    elementsManager.jsCreate and edit custom elementsggod
    weapons.jsVariety of different weaponsJayd
    fey_and_more.jsFairies, magic, and a lot of other thingsMelecie
    Top-rated Mods
    chem.jsSeveral chemistry and physics-related elementslllllllllwith10ls
    aChefsDream.jsMore foods, animals, tools, and other cooking items [YouTube Playlist]SquareScreamYT
    delete_all_of_element.jsTool that deletes every pixel of the element(s) the user clicks onAlice
    survival.jsWith limited resources, you must craft, sell, and buy to progressR74n
    alchemy.jsStart with only 4 elements and unlock more by reacting them together (Most are not possible)R74n
    nousersthings.jsMany chemical elements, compounds, and morenousernamefound
    spring.jsMany nature elements, like sakura trees, butterflies, beehives, and moreR74n
    elementsManager.jsCreate and edit custom elementsggod
    weapons.jsVariety of different weaponsJayd
    fey_and_more.jsFairies, magic, and a lot of other thingsMelecie
    Official
    alchemy.jsStart with only 4 elements and unlock more by reacting them together (Most are not possible)R74n
    building.jsBuilding generators and materialsR74n
    classic_explosives.jsRe-adds 4 explosives removed in v1.9.3R74n
    classic_textures.jsUse textures from early versions of the gameR74n
    color_everything.jsAllows every element to have a custom colorR74n
    death_count.jsMessages counting when Humans dieR74n
    devsnacks.jsExtra food ingredients and recipesR74n
    devtests.jsExperimental features from the Sandboxels developerR74n
    edible_everything.jsAllows every element to be mixed into Batter and DoughR74n
    fools.jsRe-adds FOOLS ModeR74n
    fools24.jsRe-adds the 2024 Multiversal Update (v5.9.1)R74n
    fools25.jsRe-adds the 2025 Element ModulatorR74n
    glow.js[CHROME ONLY] Adds a cool lighting effect to many emissive pixels, like FireR74n
    gravity_test.jsTest for altered gravity, makes all pixels move inwardR74n
    mustard.jsMustard and Mustard SeedsR74n
    no_blood.jsEffectively removes Blood and related elementsR74n
    rainbow_cursor.jsMakes your cursor multicoloredR74n
    random_everything.jsAllows every element to be spawned with RandomR74n
    rich_grain.jsChanges pixel grain to create richer colorsR74n
    smooth_water.jsChanges water mechanics so that it flows in one direction until it bounces off of somethingR74n
    souls.jsHuman Souls, Ectoplasm, and TombstonesR74n
    spring.jsMany nature elements, like sakura trees, butterflies, beehives, and moreR74n
    survival.jsWith limited resources, you must craft, sell, and buy to progressR74n
    velocity.jsBeta for explosion velocity, and later wind, which may come to the base game in the futureR74n
    Official
    alchemy.jsStart with only 4 elements and unlock more by reacting them together (Most are not possible)R74n
    building.jsBuilding generators and materialsR74n
    classic_explosives.jsRe-adds 4 explosives removed in v1.9.3R74n
    classic_textures.jsUse textures from early versions of the gameR74n
    color_everything.jsAllows every element to have a custom colorR74n
    death_count.jsMessages counting when Humans dieR74n
    devsnacks.jsExtra food ingredients and recipesR74n
    devtests.jsExperimental features from the Sandboxels developerR74n
    edible_everything.jsAllows every element to be mixed into Batter and DoughR74n
    fools.jsRe-adds FOOLS ModeR74n
    fools24.jsRe-adds the 2024 Multiversal Update (v5.9.1)R74n
    fools25.jsRe-adds the 2025 Element ModulatorR74n
    glow.js[CHROME ONLY] Adds a cool lighting effect to many emissive pixels, like FireR74n
    gravity_test.jsTest for altered gravity, makes all pixels move inwardR74n
    mustard.jsMustard and Mustard SeedsR74n
    no_blood.jsEffectively removes Blood and related elementsR74n
    rainbow_cursor.jsMakes your cursor multicoloredR74n
    random_everything.jsAllows every element to be spawned with RandomR74n
    rich_grain.jsChanges pixel grain to create richer colorsR74n
    smooth_water.jsChanges water mechanics so that it flows in one direction until it bounces off of somethingR74n
    souls.jsHuman Souls, Ectoplasm, and TombstonesR74n
    spring.jsMany nature elements, like sakura trees, butterflies, beehives, and moreR74n
    survival.jsWith limited resources, you must craft, sell, and buy to progressR74n
    velocity.jsBeta for explosion velocity, and later wind, which may come to the base game in the futureR74n
    Tools & Settings
    betterModManager.jsImprovements to the Mod Managerggod
    betterSettings.jsAdditional settings and functionalityggod
    betterStats.jsTrack actual running TPS of the simulationmollthecoder
    buildingreplicator.jsScans and replicates builds anywhere on the screen, along with some preset submitted buildsnousernamefound
    change.jsTool that only replaces existing pixelsAlice
    color_tools.jsTools that manipulate colorsAlice
    controllable_pixel.jsPixel controlled using WASD (keyboard required)Jayd
    cpt_alt.jsDestructive variant of the controllable pixelAlice
    customtemptools.jsCustomizable temperature toolsguzzo86
    debugRework.jsRevamps the Debug toolFioushemastor
    delete_all_of_element.jsTool that deletes every pixel of the element(s) the user clicks onAlice
    descriptions.jsDescriptions to the info page and tooltips of elementsmollthecoder
    editTools.jsSelections, Copying, Pasting, Cutting, Shapes, and moreggod
    elementEraser.jsTool that deletes only desired elementSquareScreamYT
    elementreplacer.jsTool that replaces elements with other elements while keeping their colorSuss
    elementsManager.jsCreate and edit custom elementsggod
    evenmoretemptools.jsTemperature-modifying tools (±350/tick, NaN, and Infinity)Alice
    extra_element_info.jsDescriptions to various vanilla elements. Used to provide the functionality that desc now does before it was added to vanillaMelecie
    extrasaveslots.jsExtra saves slots [KEEP IMPORTANT SAVES AS FILES!!]Jayd
    find.jsFind mode that highlights a chosen element as pulsating red and yellow [More Info]Alice
    gasdecay.jsGases will slowly decay over timenousernamefound
    hideandshowtools.jsTools to hide elements and show hidden elementsMicaelNotUsed
    human_friendly_design.jsDrag and Mix tools don't kill humansNekonico
    insane_random_events.jsMassively buffs random eventsAlice
    jaydsfunctions.jsExtra toolsJayd
    move_tools.jsTools that move pixelsAlice
    no_scroll.jsDisables canvas scrolling, useful for Apple Magic Mouse usersCrimera Games
    noconfirm.jsRemoves all confirmation popupsmollthecoder
    page_color.jsChange the page color with the "pageColor" query parameterAlice
    pixelResizeTool.jsButton to change pixel scalefeeshmaster
    prompt.jsPrimitive command consoleAlice
    prop.jsTool to edit the attributes of pixelsAlice
    random_everything.jsAllows every element to be spawned with RandomR74n
    replace_all.jsWay to replace every pixel of an element with another element [More Info]Alice
    replace.jsTool that replaces every pixel of a specified element with another specified element [" to open]Alice
    save_loading.jsThe ability to save and load scenes from files [See the info page of the element]Alice
    selective_paint.jsTool to paint only selected elementsSquareScreamYT
    stripe_paint.jsTool to paint with stripesAlice
    text.jsTools to write textRedBirdly
    texturepack.jsTools that let you create and share custom texture packsnousernamefound
    the_ground.jsSeveral rock types, worldgen settings, and gemstonesAlice
    worldEdit.jsSelection and editing toolsRedBirdly
    worldgenlibrary.jsWorld generation libraryOrchid
    Tools & Settings
    betterModManager.jsImprovements to the Mod Managerggod
    betterSettings.jsAdditional settings and functionalityggod
    betterStats.jsTrack actual running TPS of the simulationmollthecoder
    buildingreplicator.jsScans and replicates builds anywhere on the screen, along with some preset submitted buildsnousernamefound
    change.jsTool that only replaces existing pixelsAlice
    color_tools.jsTools that manipulate colorsAlice
    controllable_pixel.jsPixel controlled using WASD (keyboard required)Jayd
    cpt_alt.jsDestructive variant of the controllable pixelAlice
    customtemptools.jsCustomizable temperature toolsguzzo86
    debugRework.jsRevamps the Debug toolFioushemastor
    delete_all_of_element.jsTool that deletes every pixel of the element(s) the user clicks onAlice
    descriptions.jsDescriptions to the info page and tooltips of elementsmollthecoder
    editTools.jsSelections, Copying, Pasting, Cutting, Shapes, and moreggod
    elementEraser.jsTool that deletes only desired elementSquareScreamYT
    elementreplacer.jsTool that replaces elements with other elements while keeping their colorSuss
    elementsManager.jsCreate and edit custom elementsggod
    evenmoretemptools.jsTemperature-modifying tools (±350/tick, NaN, and Infinity)Alice
    extra_element_info.jsDescriptions to various vanilla elements. Used to provide the functionality that desc now does before it was added to vanillaMelecie
    extrasaveslots.jsExtra saves slots [KEEP IMPORTANT SAVES AS FILES!!]Jayd
    find.jsFind mode that highlights a chosen element as pulsating red and yellow [More Info]Alice
    gasdecay.jsGases will slowly decay over timenousernamefound
    hideandshowtools.jsTools to hide elements and show hidden elementsMicaelNotUsed
    human_friendly_design.jsDrag and Mix tools don't kill humansNekonico
    insane_random_events.jsMassively buffs random eventsAlice
    jaydsfunctions.jsExtra toolsJayd
    move_tools.jsTools that move pixelsAlice
    no_scroll.jsDisables canvas scrolling, useful for Apple Magic Mouse usersCrimera Games
    noconfirm.jsRemoves all confirmation popupsmollthecoder
    page_color.jsChange the page color with the "pageColor" query parameterAlice
    pixelResizeTool.jsButton to change pixel scalefeeshmaster
    prompt.jsPrimitive command consoleAlice
    prop.jsTool to edit the attributes of pixelsAlice
    random_everything.jsAllows every element to be spawned with RandomR74n
    replace_all.jsWay to replace every pixel of an element with another element [More Info]Alice
    replace.jsTool that replaces every pixel of a specified element with another specified element [" to open]Alice
    save_loading.jsThe ability to save and load scenes from files [See the info page of the element]Alice
    selective_paint.jsTool to paint only selected elementsSquareScreamYT
    stripe_paint.jsTool to paint with stripesAlice
    text.jsTools to write textRedBirdly
    texturepack.jsTools that let you create and share custom texture packsnousernamefound
    the_ground.jsSeveral rock types, worldgen settings, and gemstonesAlice
    worldEdit.jsSelection and editing toolsRedBirdly
    worldgenlibrary.jsWorld generation libraryOrchid
    Science & Chemistry
    alcohol.jsMethanol, (iso-)propanol, and butanolAlice
    alkahest.jsThe alkahest, a liquid which dissolves anythingAlice
    alkali_metal.jsThe missing alkali metals (Inspired by noblegas.js and halogen.js)Cube14yt
    aScientistsWish.jsSeveral things related to science and physicsCarbon Monoxide, salmonfishy
    bettermetalscrap.jsMetal scrap can be melted back into its original materialnousernamefound
    bigger_star_spawners.jsSpawners for larger starsAlice
    biology.jsVarious elements and features for building large organismsNekonico
    bioooze_and_pyrogens.jsBio-Ooze from Frackin' Universe and several heat-producing materials from various games' modsAlice
    boiling_things.jsVarious elements can be vaporizedAlice
    bouncing_balls.jsNew types of balls that bounce accurately and rollNekonico
    chalcopyrite.jsThe chalcopyrite oreSophie
    chalk.jsChalkCharsonBurensen
    charsonsmoduno.jsArsenic, Beryllium, Silicon, compounds, and some fake elementsCharsonBurensen
    chem.jsSeveral chemistry and physics-related elementslllllllllwith10ls
    clf3.jsChlorine TrifluorideAlice
    cmur.jsCharsonsModUno (???)CharsonBurensen
    debrisable.jsExpands the number of breakable elements, changes erosion, and adds way to craft certain elements from breaking other elementsNekonico
    eklegems.jsLarge amouunt of gemstones and other crystalsekle24
    fire_extinguisher.jsFire extinguisher blocks and realistic firefighting foam to put out nearly anythingDr_Lego
    fire_mod.jsVarious properties to change fire behavior and radioactive fireAlice
    fire_slime.jsPyrogenic version of slimeAlice
    glenn_gases.jsMost gases from the Glenn's Gases mod into SandboxelsAlice
    grav_mudstones.jsVarious forms of mudstone with different gravitiesAlice
    halogen.jsThe missing halogensnousernamefound
    hidden_ground.jsHides most rock variants from the_ground.js excluding the base rocks and wallsMelecie
    iocalfaeus_clones.jsIorefrius, Iolucius, and Ioradius gasAlice
    jaydstuff.jsVarious chemicals and compoundsJayd
    laetium.jsSeveral fictional elementsAlice
    liquid_energy.jsLiquid versions of the elements in the Energy categoryAlice
    liquid_mixing.jsLiquids can mix colors dynamicallyNekonico
    lye.jsLyeBatteRaquette58
    mars.jsMartian materialscharcoal_afterlife
    metals.jsSeveral metalsAlice
    mixture.jsMany chemicals can be mixedlllllllllwith10ls
    more_gold.jsGreen and Black Goldpixelegend4
    morechemistry.jsNew chemicals, compounds, and machinesOrchid
    moreliquids.jsVarious liquidste-agma-at
    neutronium_compressor.jsCompressor from Minecraft's Avaritia mod that compresses 10,000 pixels of an element into a singularityAlice
    noblegas.jsThe missing noble gasesnousernamefound
    nousersthings.jsMany chemical elements, compounds, and morenousernamefound
    petal_dye.jsBoil petals to make dyeSuss
    radioactive.jsRadioactive elements on the periodic table [WIP]kaeud
    random_rocks.jsRandomly generates rocks on game loadAlice
    roseyiede.jsSeveral variants of a substance called roseyiedeAlice
    solubility.jsAdds solubility and a simple-to-use format for other mods to use with itOrchid
    some_tf_liquids.jsVarious liquids from the Thermal Foundation Minecraft modAlice
    stickystuff.jsSlime, Honey, and others can stick to other elementsSuss
    the_ground.jsSeveral rocks, worldgen types, and gemstonesAlice
    Science & Chemistry
    alcohol.jsMethanol, (iso-)propanol, and butanolAlice
    alkahest.jsThe alkahest, a liquid which dissolves anythingAlice
    alkali_metal.jsThe missing alkali metals (Inspired by noblegas.js and halogen.js)Cube14yt
    aScientistsWish.jsSeveral things related to science and physicsCarbon Monoxide, salmonfishy
    bettermetalscrap.jsMetal scrap can be melted back into its original materialnousernamefound
    bigger_star_spawners.jsSpawners for larger starsAlice
    biology.jsVarious elements and features for building large organismsNekonico
    bioooze_and_pyrogens.jsBio-Ooze from Frackin' Universe and several heat-producing materials from various games' modsAlice
    boiling_things.jsVarious elements can be vaporizedAlice
    bouncing_balls.jsNew types of balls that bounce accurately and rollNekonico
    chalcopyrite.jsThe chalcopyrite oreSophie
    chalk.jsChalkCharsonBurensen
    charsonsmoduno.jsArsenic, Beryllium, Silicon, compounds, and some fake elementsCharsonBurensen
    chem.jsSeveral chemistry and physics-related elementslllllllllwith10ls
    clf3.jsChlorine TrifluorideAlice
    cmur.jsCharsonsModUno (???)CharsonBurensen
    debrisable.jsExpands the number of breakable elements, changes erosion, and adds way to craft certain elements from breaking other elementsNekonico
    eklegems.jsLarge amouunt of gemstones and other crystalsekle24
    fire_extinguisher.jsFire extinguisher blocks and realistic firefighting foam to put out nearly anythingDr_Lego
    fire_mod.jsVarious properties to change fire behavior and radioactive fireAlice
    fire_slime.jsPyrogenic version of slimeAlice
    glenn_gases.jsMost gases from the Glenn's Gases mod into SandboxelsAlice
    grav_mudstones.jsVarious forms of mudstone with different gravitiesAlice
    halogen.jsThe missing halogensnousernamefound
    hidden_ground.jsHides most rock variants from the_ground.js excluding the base rocks and wallsMelecie
    iocalfaeus_clones.jsIorefrius, Iolucius, and Ioradius gasAlice
    jaydstuff.jsVarious chemicals and compoundsJayd
    laetium.jsSeveral fictional elementsAlice
    liquid_energy.jsLiquid versions of the elements in the Energy categoryAlice
    liquid_mixing.jsLiquids can mix colors dynamicallyNekonico
    lye.jsLyeBatteRaquette58
    mars.jsMartian materialscharcoal_afterlife
    metals.jsSeveral metalsAlice
    mixture.jsMany chemicals can be mixedlllllllllwith10ls
    more_gold.jsGreen and Black Goldpixelegend4
    morechemistry.jsNew chemicals, compounds, and machinesOrchid
    moreliquids.jsVarious liquidste-agma-at
    neutronium_compressor.jsCompressor from Minecraft's Avaritia mod that compresses 10,000 pixels of an element into a singularityAlice
    noblegas.jsThe missing noble gasesnousernamefound
    nousersthings.jsMany chemical elements, compounds, and morenousernamefound
    petal_dye.jsBoil petals to make dyeSuss
    radioactive.jsRadioactive elements on the periodic table [WIP]kaeud
    random_rocks.jsRandomly generates rocks on game loadAlice
    roseyiede.jsSeveral variants of a substance called roseyiedeAlice
    solubility.jsAdds solubility and a simple-to-use format for other mods to use with itOrchid
    some_tf_liquids.jsVarious liquids from the Thermal Foundation Minecraft modAlice
    stickystuff.jsSlime, Honey, and others can stick to other elementsSuss
    the_ground.jsSeveral rocks, worldgen types, and gemstonesAlice
    Machines & Technology
    circuitcore.jsExtension for logicgates.js that adds advanced circuits [More Info]RedBirdly
    clone_liquid.jsLiquid form of clonerAlice
    colored_lightbulbs.jsLight bulb that can be paintedguzzo86, ggod
    combustion.jsComponents necessary for combustion enginesuptzik
    conveyance.jsConveyors, operated with and without electricityMelecie
    datawire.jsWire that transfers data and other operators and machines for itOrchid
    drill.jsDrills made out of several materialsSuss
    ExtraMachines.jsSensors, energy resources, materials, and moreMecoolnotcool
    fans.jsFansCube14yt
    fine_tuned_cloner.jsCloner that can spawn at different rates and prevent unwanted cloningBatteRaquette58
    flipflop.jsToggleable switches [More Info]Flix
    fueled_generators.jsFuel powered generatorsguzzo86
    gameOfLife.jsConway's Game of Life on a screenggod
    heatshield.jsHeatshields, makes Plasma coolerTaterbob
    human_friendly_design.jsPipes, Portals, Drag, and Mix don't kill humansNekonico
    industry.jsConveyors and emitters for most energy typespogdog
    logicgates.jsPredictable electricity and logic gatesnousernamefound
    note_block_advanced.jsEdit of Alice's note_block.js, adds different blocks with different frequenciesCharsonBurensen
    note_block.jsMusical Note BlocksAlice
    nousersthings.jsDestroyable machines, pipe variants, filters, and morenousernamefound
    portal.jsPortals that can teleport pixelsAlice
    pullers.jsPixels that pull pixels towards themvoidapex11
    pushers.jsPixels that push elements away from themAlice
    sandboxels.jsDigital screen to play a mini version of SandboxelsNekonico
    schematics.jsSchematics for logic gatesSquareScreamYT
    scp.jsCreatures and items from the SCP WikiNekonico
    spouts.jsSpouts for all liquidskaeud
    state_voids.jsSeveral elements that delete specific states of matterAlice
    switches.jsElectrical switches that can be toggledAlice
    thiquovite.jsThiquovite and wall buildersCharsonBurensen
    ticking_temp_stuff.jsHeater and Cooler variantsAlice
    video.jsVideo playerggod
    waterspout.jsRe-adds the old Water Spoutmollthecoder
    WhisperingTheory.jsMany more variants of heater and coolerkaeud
    wifi_draw.jsConnections between WiFi in wifi.js and logicgates.jsRedBirdly
    Machines & Technology
    circuitcore.jsExtension for logicgates.js that adds advanced circuits [More Info]RedBirdly
    clone_liquid.jsLiquid form of clonerAlice
    colored_lightbulbs.jsLight bulb that can be paintedguzzo86, ggod
    combustion.jsComponents necessary for combustion enginesuptzik
    conveyance.jsConveyors, operated with and without electricityMelecie
    datawire.jsWire that transfers data and other operators and machines for itOrchid
    drill.jsDrills made out of several materialsSuss
    ExtraMachines.jsSensors, energy resources, materials, and moreMecoolnotcool
    fans.jsFansCube14yt
    fine_tuned_cloner.jsCloner that can spawn at different rates and prevent unwanted cloningBatteRaquette58
    flipflop.jsToggleable switches [More Info]Flix
    fueled_generators.jsFuel powered generatorsguzzo86
    gameOfLife.jsConway's Game of Life on a screenggod
    heatshield.jsHeatshields, makes Plasma coolerTaterbob
    human_friendly_design.jsPipes, Portals, Drag, and Mix don't kill humansNekonico
    industry.jsConveyors and emitters for most energy typespogdog
    logicgates.jsPredictable electricity and logic gatesnousernamefound
    note_block_advanced.jsEdit of Alice's note_block.js, adds different blocks with different frequenciesCharsonBurensen
    note_block.jsMusical Note BlocksAlice
    nousersthings.jsDestroyable machines, pipe variants, filters, and morenousernamefound
    portal.jsPortals that can teleport pixelsAlice
    pullers.jsPixels that pull pixels towards themvoidapex11
    pushers.jsPixels that push elements away from themAlice
    sandboxels.jsDigital screen to play a mini version of SandboxelsNekonico
    schematics.jsSchematics for logic gatesSquareScreamYT
    scp.jsCreatures and items from the SCP WikiNekonico
    spouts.jsSpouts for all liquidskaeud
    state_voids.jsSeveral elements that delete specific states of matterAlice
    switches.jsElectrical switches that can be toggledAlice
    thiquovite.jsThiquovite and wall buildersCharsonBurensen
    ticking_temp_stuff.jsHeater and Cooler variantsAlice
    video.jsVideo playerggod
    waterspout.jsRe-adds the old Water Spoutmollthecoder
    WhisperingTheory.jsMany more variants of heater and coolerkaeud
    wifi_draw.jsConnections between WiFi in wifi.js and logicgates.jsRedBirdly
    Weapons
    aircrafts.jsAircrafts and aircraft partsJayd
    c_fighter_jet.jsControllable fighter jet [WASD to move, Q+WASD to shoot, GVBN for missiles]Jayd
    guided_rocket.jsHoming misilevoidapex11
    icb.jsVarious levels of nested cluster bombsAlice
    life_eater.jsWarhammer 40,000's Life-Eater Virus and Virus BombsAlice
    liquid_void.jsLiquid variant of VoidAlice
    meat_rockets.jsRockets that create meat when explodingMelecie
    more_breaking.jsMore elements can be brokenAlice
    rays.jsMore Ray typesAlice
    rays++.jsA couple more raysuptzik
    scp.jsCreatures and items from the SCP WikiNekonico
    subspace.jsThe Subspace Tripmine from Robloxnousernamefound
    war_crimes.jsTear gas and morevoidapex11
    weapons.jsVariety of different weaponsJayd
    Weapons
    aircrafts.jsAircrafts and aircraft partsJayd
    c_fighter_jet.jsControllable fighter jet [WASD to move, Q+WASD to shoot, GVBN for missiles]Jayd
    guided_rocket.jsHoming misilevoidapex11
    icb.jsVarious levels of nested cluster bombsAlice
    life_eater.jsWarhammer 40,000's Life-Eater Virus and Virus BombsAlice
    liquid_void.jsLiquid variant of VoidAlice
    meat_rockets.jsRockets that create meat when explodingMelecie
    more_breaking.jsMore elements can be brokenAlice
    rays.jsMore Ray typesAlice
    rays++.jsA couple more raysuptzik
    scp.jsCreatures and items from the SCP WikiNekonico
    subspace.jsThe Subspace Tripmine from Robloxnousernamefound
    war_crimes.jsTear gas and morevoidapex11
    weapons.jsVariety of different weaponsJayd
    Food & Cooking
    aChefsDream_beta.jsBeta testing for aChefsDream.jsSquareScreamYT
    aChefsDream.jsMore foods, animals, tools, and other cooking items [YouTube Playlist]SquareScreamYT
    aChefsDream2.jsThe sequel to aChefsDream.js, with brand new elements and toolsSquareScreamYT
    bananas.jsBananas and banana plantsAlice
    CherrySoda.jsMaterials to make cherry sodaguzzo86
    community_desserts.jsVarious desserts from community suggestionsTisquares
    devsnacks.jsExtra food ingredients and recipes; Only Tea stuff currentlyR74n
    GrapeSoda.jsMaterials to make grape sodaguzzo86
    greenitemsandmore.jsVarious green things, including apples and more foodzonneschijn7
    ketchup_mod.jsMany ketchup related elements, plus a few other condimentsNubo318, Devi, Alice
    lemonade.jsLemons and lemonadepersonman, baconthemyth
    morefoodsmod.jsMore foodsClide4
    mossstuff.jsLarge variety of elements, including foodselectricmoss
    mustard.jsMustard and Mustard SeedsR74n
    potato_chips.jsPotato chips and sunflower oilguzzo86
    sbstuff.jsMany foodsstefanblox
    soups.jsSeasoning and souppixelegend4
    weAllScreamFor.jsIce cream toppingsrottenEgghead
    Food & Cooking
    aChefsDream_beta.jsBeta testing for aChefsDream.jsSquareScreamYT
    aChefsDream.jsMore foods, animals, tools, and other cooking items [YouTube Playlist]SquareScreamYT
    aChefsDream2.jsThe sequel to aChefsDream.js, with brand new elements and toolsSquareScreamYT
    bananas.jsBananas and banana plantsAlice
    CherrySoda.jsMaterials to make cherry sodaguzzo86
    community_desserts.jsVarious desserts from community suggestionsTisquares
    devsnacks.jsExtra food ingredients and recipes; Only Tea stuff currentlyR74n
    GrapeSoda.jsMaterials to make grape sodaguzzo86
    greenitemsandmore.jsVarious green things, including apples and more foodzonneschijn7
    ketchup_mod.jsMany ketchup related elements, plus a few other condimentsNubo318, Devi, Alice
    lemonade.jsLemons and lemonadepersonman, baconthemyth
    morefoodsmod.jsMore foodsClide4
    mossstuff.jsLarge variety of elements, including foodselectricmoss
    mustard.jsMustard and Mustard SeedsR74n
    potato_chips.jsPotato chips and sunflower oilguzzo86
    sbstuff.jsMany foodsstefanblox
    soups.jsSeasoning and souppixelegend4
    weAllScreamFor.jsIce cream toppingsrottenEgghead
    Life & Nature
    apioforms_pre.jsIncomplete implementation of elements from the Apioform GameAlice
    baby.jsVarious babiesSuss
    bacteria_mod.jsContent from Minecraft's Bacterium ModAlice
    bananas.jsBananas and banana plantsAlice
    biology.jsVarious elements and features that let you build your own organismNekonico
    cat.jsCats and cat foodSquareScreamYT
    cells.jsSeveral experimental edits of the Cell elementAlice
    children.jsPesky little gremlinsTaterbob
    coldblooded.jsLizards, axolotls, snakes, and moreCube14yt
    colonies.jsRockets that contain settlers to terraform a planetNekonico
    crimson.jsElements relating to the Crimson from TerrariaAlice
    dogs.jsSimple dog and dog foodhedera-ivy
    eklegems.jsLarge amouunt of gemstones and other crystalsekle24
    fairy_chain.jsWay too many fairies to fey_and_more.jsAlice
    fantastic_creatures.jsVarious animalsMelecie
    fantasy_elements.jsFantasy creatures and substancespixelegend4
    fey_and_more.jsFairies, magic, and a lot of other thingsMelecie
    fishin.jsFishing rod and more fish with scientific names [More Info]Nekonico
    flowers_and_forests.jsTrees and flowerspixelegend4, SquareScreamYT
    fwibblen.jsFlying creature that turns nickel into itself, and another that does the same to the first oneAlice
    genetics.jsOrganism that evolves and changes as it creates more of itself by eatingNekonico
    human_edit.jsImprovements to humansAlice
    kopalstuff.jsCreatures, spirits, DNA, foods, and moreDaviStudios
    lizard_mod.jsLizardsRedBirdly
    lost_souls.jsSouls and related elementspixelegend4, SquareScreamYT, salmonfishy
    miscible_psoup_and_birthpool.jsPrimordial Soup and Birthpool can mix (fey_and_more.js)Alice
    mobs.jsCreepers, Zombies, and SkeletonsAlice
    moretrees.js25 more tree and wood typesguzzo86
    no_blood.jsEffectively removes Blood and related elementsR74n
    nocancer.jsRemoves cancer one tick after it is createdmollthecoder
    nocancer2.jsRemoves cancer from the game altogether; May be incompatible with other mods that spawn cancermollthecoder
    nograssgrow.jsPrevents Grass from growingmollthecoder
    ocean.jsMarine lifeSquareScreamYT
    ores.jsOre generation along with tools to mine themnousernamefound
    petal_dye.jsBoil petals to make dyeSuss
    plants.jsWide variety of new plants and fruitsOrchid
    primordial_birthpool.jsCross between Primordial Soup and Birthpool. Requires fey_and_more.jsAlice
    scp.jsCreatures and items from the SCP WikiNekonico
    spring.jsMany nature elements, like sakura trees, butterflies, beehives, and moreR74n
    the_ground_og.jsSimplified and more stable version of the_ground.jsAlice
    the_ground.jsSeveral rock types, worldgen settings, and gemstonesAlice
    toothpaste.jsTeeth and pasteAlice
    volcanic_expansion.jsObsidian, Pumice, and Andesite rocksJayd
    Life & Nature
    apioforms_pre.jsIncomplete implementation of elements from the Apioform GameAlice
    baby.jsVarious babiesSuss
    bacteria_mod.jsContent from Minecraft's Bacterium ModAlice
    bananas.jsBananas and banana plantsAlice
    biology.jsVarious elements and features that let you build your own organismNekonico
    cat.jsCats and cat foodSquareScreamYT
    cells.jsSeveral experimental edits of the Cell elementAlice
    children.jsPesky little gremlinsTaterbob
    coldblooded.jsLizards, axolotls, snakes, and moreCube14yt
    colonies.jsRockets that contain settlers to terraform a planetNekonico
    crimson.jsElements relating to the Crimson from TerrariaAlice
    dogs.jsSimple dog and dog foodhedera-ivy
    eklegems.jsLarge amouunt of gemstones and other crystalsekle24
    fairy_chain.jsWay too many fairies to fey_and_more.jsAlice
    fantastic_creatures.jsVarious animalsMelecie
    fantasy_elements.jsFantasy creatures and substancespixelegend4
    fey_and_more.jsFairies, magic, and a lot of other thingsMelecie
    fishin.jsFishing rod and more fish with scientific names [More Info]Nekonico
    flowers_and_forests.jsTrees and flowerspixelegend4, SquareScreamYT
    fwibblen.jsFlying creature that turns nickel into itself, and another that does the same to the first oneAlice
    genetics.jsOrganism that evolves and changes as it creates more of itself by eatingNekonico
    human_edit.jsImprovements to humansAlice
    kopalstuff.jsCreatures, spirits, DNA, foods, and moreDaviStudios
    lizard_mod.jsLizardsRedBirdly
    lost_souls.jsSouls and related elementspixelegend4, SquareScreamYT, salmonfishy
    miscible_psoup_and_birthpool.jsPrimordial Soup and Birthpool can mix (fey_and_more.js)Alice
    mobs.jsCreepers, Zombies, and SkeletonsAlice
    moretrees.js25 more tree and wood typesguzzo86
    no_blood.jsEffectively removes Blood and related elementsR74n
    nocancer.jsRemoves cancer one tick after it is createdmollthecoder
    nocancer2.jsRemoves cancer from the game altogether; May be incompatible with other mods that spawn cancermollthecoder
    nograssgrow.jsPrevents Grass from growingmollthecoder
    ocean.jsMarine lifeSquareScreamYT
    ores.jsOre generation along with tools to mine themnousernamefound
    petal_dye.jsBoil petals to make dyeSuss
    plants.jsWide variety of new plants and fruitsOrchid
    primordial_birthpool.jsCross between Primordial Soup and Birthpool. Requires fey_and_more.jsAlice
    scp.jsCreatures and items from the SCP WikiNekonico
    spring.jsMany nature elements, like sakura trees, butterflies, beehives, and moreR74n
    the_ground_og.jsSimplified and more stable version of the_ground.jsAlice
    the_ground.jsSeveral rock types, worldgen settings, and gemstonesAlice
    toothpaste.jsTeeth and pasteAlice
    volcanic_expansion.jsObsidian, Pumice, and Andesite rocksJayd
    Fun & Games
    3pms_mod.jsAdds random stuff and tools3pm
    10kelements.jsCustomizable amount of randomly generated elementsnousernamefound
    all_around_fillers.jsDirectional Filler variantsidk73248
    allliquids.jsMade all elements liquidsOrchid
    amogus.jsSmall Among Us structureAlice
    bfdi.jsSeveral references to Battle for Dream IslandTaterbob
    citybuilding.jsSeeds that create miniature buildings and other city-related itemsSquareScreamYT
    collab_mod.jsCreated by multiple people, adds random thingsmrapple, ilikepizza, stefanblox
    cubesstuff.jsSome random stuff like Disco Ball, Pyrite, and Nordic GoldCube14yt
    doom.jsAs seen on TikTok - Select the Doom element to start [WASD to move]ggod
    elem3.jsAll elements and combinations from Elemental 3 [Very Large]Sophie
    explosionsound.jsSound effects for explosionsnousernamefound
    fishin.jsFishing rod and more fish with scientific names [More Info]Nekonico
    fools+.jsImproves and makes fools.js extremely annoyingSquareScreamYT
    funny elements 2022-11-15.jsFew curated randomly-generated elementsAlice
    funny_solid.jsFecesAlice
    funnynames.jsVarious ways to mess with the names of elementsnousernamefound
    haseulite.jsLoona-related materials with various propertiesAlice
    lactose_intolerance_and_celiac.jsHumans explode on contact with milk, wheat, bread, or toastNubo318
    lattice_filler.jsCombination of lattice and filler and a destructive variantSuss
    liquid_mixing.jsLiquids can mix colors dynamicallyNekonico
    lone_urea.jsUreaAlice
    maze.jsSolvable maze generatorggod
    memelists.jsYou must select elements through a list at the bottom of the pagemollthecoder
    minecraft.jsSeveral things from Minecraftnousernamefound
    minesweeper.jsImplementation of MinesweeperAlice
    more_art_states.jsMore states of the Art elementACrazyPencil
    musicalfruit.jsHumans get gas from eating Beansmollthecoder
    nekonicos_stuff.jsRandom stuff like graphite, oobleck, and red ice, as well as many machinesNekonico
    prideflags.jsSome pride flags to the gameAdora
    random_elems.jsCurated randomly generated elementsAlice
    random_liquids.jsRandomly generates liquids on game loadAlice
    sbmixup.jsSilly elements from a Mix-Up! gamestefanblox
    scp.jsCreatures and items from the SCP WikiNekonico
    sensitive.jsMakes all elements sensitive to airpogdog
    sports_beta.jsSeveral sports itemsBluBun5193
    star_wars.jsVarious items from Star Wars by DisneySeaPickle754
    sus.jsAmong Us crewmateNv7
    thiquovite.jsThiquovite and wall buildersCharsonBurensen
    triggerable_random_powders.jsPowders with different abilities, such as heating and coolingAlice
    troll.jsVarious dumb elements that iterate randomly on the entire screenAlice
    WhisperingTheory.jsPowder and gas variant of heater and coolerkaeud
    Fun & Games
    3pms_mod.jsAdds random stuff and tools3pm
    10kelements.jsCustomizable amount of randomly generated elementsnousernamefound
    all_around_fillers.jsDirectional Filler variantsidk73248
    allliquids.jsMade all elements liquidsOrchid
    amogus.jsSmall Among Us structureAlice
    bfdi.jsSeveral references to Battle for Dream IslandTaterbob
    citybuilding.jsSeeds that create miniature buildings and other city-related itemsSquareScreamYT
    collab_mod.jsCreated by multiple people, adds random thingsmrapple, ilikepizza, stefanblox
    cubesstuff.jsSome random stuff like Disco Ball, Pyrite, and Nordic GoldCube14yt
    doom.jsAs seen on TikTok - Select the Doom element to start [WASD to move]ggod
    elem3.jsAll elements and combinations from Elemental 3 [Very Large]Sophie
    explosionsound.jsSound effects for explosionsnousernamefound
    fishin.jsFishing rod and more fish with scientific names [More Info]Nekonico
    fools+.jsImproves and makes fools.js extremely annoyingSquareScreamYT
    funny elements 2022-11-15.jsFew curated randomly-generated elementsAlice
    funny_solid.jsFecesAlice
    funnynames.jsVarious ways to mess with the names of elementsnousernamefound
    haseulite.jsLoona-related materials with various propertiesAlice
    lactose_intolerance_and_celiac.jsHumans explode on contact with milk, wheat, bread, or toastNubo318
    lattice_filler.jsCombination of lattice and filler and a destructive variantSuss
    liquid_mixing.jsLiquids can mix colors dynamicallyNekonico
    lone_urea.jsUreaAlice
    maze.jsSolvable maze generatorggod
    memelists.jsYou must select elements through a list at the bottom of the pagemollthecoder
    minecraft.jsSeveral things from Minecraftnousernamefound
    minesweeper.jsImplementation of MinesweeperAlice
    more_art_states.jsMore states of the Art elementACrazyPencil
    musicalfruit.jsHumans get gas from eating Beansmollthecoder
    nekonicos_stuff.jsRandom stuff like graphite, oobleck, and red ice, as well as many machinesNekonico
    prideflags.jsSome pride flags to the gameAdora
    random_elems.jsCurated randomly generated elementsAlice
    random_liquids.jsRandomly generates liquids on game loadAlice
    sbmixup.jsSilly elements from a Mix-Up! gamestefanblox
    scp.jsCreatures and items from the SCP WikiNekonico
    sensitive.jsMakes all elements sensitive to airpogdog
    sports_beta.jsSeveral sports itemsBluBun5193
    star_wars.jsVarious items from Star Wars by DisneySeaPickle754
    sus.jsAmong Us crewmateNv7
    thiquovite.jsThiquovite and wall buildersCharsonBurensen
    triggerable_random_powders.jsPowders with different abilities, such as heating and coolingAlice
    troll.jsVarious dumb elements that iterate randomly on the entire screenAlice
    WhisperingTheory.jsPowder and gas variant of heater and coolerkaeud
    Visual Effects
    acid_and_shapes.jsWeird visual effects enabled in settingsAlice
    asciiboxels.jsRenders pixels as ASCII charactersNekonico
    background_changer.jsPress 'B' to change canvas background to a URLR74n
    borders.jsBlack borders around pixels (Use bright background)R74n
    clouds.jsMoving clouds, sky.js recommendedRedBirdly
    customBackground.jsSet your background to an image linkJayd
    fractals.jsElement and tools to render fractals in gamenousernamefound
    heatglow.jsRed glowing effect for hot metalsnousernamefound
    hexagon_test.jsPixels look like hexagonsRedBirdly
    invisible_dye.jsElements like Dye and Spray Paint that take the color of the backgroundAlice
    invisible_wall.jsElement like Wall that takes the color of the backgroundAlice
    lightmap.jsLight sources glowRedBirdly
    liquid_mixing.jsLiquids can mix colors dynamicallyNekonico
    manyMoreThemes.jsAdditional themesJayd
    moreViews.jsMany new rendering modesggod
    occlusion.jsRealistic shadows, similar to Terraria's lightingRedBirdly
    onecolor.jsPlaced pixels are single-colorednousernamefound
    paint_event.jsRandom event that randomly paints a circleAlice
    rainbow_tests.jsVariants of the rainbow element with different mathsAlice
    real_light.jsEverything is dark unless hit with a photon (Light) pixel, hot, or glowingNekonico
    rich_grain.jsChanges pixel grain to create richer colorsR74n
    shader_by_jayd.jsGlow around light elementsJayd
    Shroomboxels.jsVariant of acid_and_shapes.js that uses a different trigonometric functionAlice
    singleColor.jsElements pick one color each time they're drawnstefanblox
    sky.jsDay-night cycleRedBirdly
    solidcolor.jsPlaced pixels have a solid colorSquareScreamYT
    texture_pack_by_jayd.jsCool backgroundJayd
    UwUify.js"UwU" backgroundJayd
    Visual Effects
    acid_and_shapes.jsWeird visual effects enabled in settingsAlice
    asciiboxels.jsRenders pixels as ASCII charactersNekonico
    background_changer.jsPress 'B' to change canvas background to a URLR74n
    borders.jsBlack borders around pixels (Use bright background)R74n
    clouds.jsMoving clouds, sky.js recommendedRedBirdly
    customBackground.jsSet your background to an image linkJayd
    fractals.jsElement and tools to render fractals in gamenousernamefound
    heatglow.jsRed glowing effect for hot metalsnousernamefound
    hexagon_test.jsPixels look like hexagonsRedBirdly
    invisible_dye.jsElements like Dye and Spray Paint that take the color of the backgroundAlice
    invisible_wall.jsElement like Wall that takes the color of the backgroundAlice
    lightmap.jsLight sources glowRedBirdly
    liquid_mixing.jsLiquids can mix colors dynamicallyNekonico
    manyMoreThemes.jsAdditional themesJayd
    moreViews.jsMany new rendering modesggod
    occlusion.jsRealistic shadows, similar to Terraria's lightingRedBirdly
    onecolor.jsPlaced pixels are single-colorednousernamefound
    paint_event.jsRandom event that randomly paints a circleAlice
    rainbow_tests.jsVariants of the rainbow element with different mathsAlice
    real_light.jsEverything is dark unless hit with a photon (Light) pixel, hot, or glowingNekonico
    rich_grain.jsChanges pixel grain to create richer colorsR74n
    shader_by_jayd.jsGlow around light elementsJayd
    Shroomboxels.jsVariant of acid_and_shapes.js that uses a different trigonometric functionAlice
    singleColor.jsElements pick one color each time they're drawnstefanblox
    sky.jsDay-night cycleRedBirdly
    solidcolor.jsPlaced pixels have a solid colorSquareScreamYT
    texture_pack_by_jayd.jsCool backgroundJayd
    UwUify.js"UwU" backgroundJayd
    Compilations
    food_mods.jsCombination of most food modsstefanblox, electricmoss, Tisquares, SquareScreamYT, Adora, pixelegend4, Alice, Nubo318, Clide4, rottenEgghead
    Compilations
    food_mods.jsCombination of most food modsstefanblox, electricmoss, Tisquares, SquareScreamYT, Adora, pixelegend4, Alice, Nubo318, Clide4, rottenEgghead
    Technical Libraries & Tests
    1.10example.jsExamples for modern rendering modding and moreR74n
    a_bundle_of_tests.jsSeveral test functionsAlice
    all_stain.jsEvery element stains solidsstefanblox
    betterMenuScreens.jsLibrary for mods to create their own menusggod
    changePixelDebug.jsThe changePixel() function aborts and logs to console when it tries to change to a non-existent elementAlice
    changeTempReactionParameter.jsThe changeTemp property to modded reactionsAlice
    code_library.jsFunctions and variables common to some other modsAlice
    controllable_pixel_test.jsPixel that can be controlled with the keyboard keys [More Info] [PC ONLY]Alice
    customexplosion.jsCustom explosion element and interface for itOrchid
    date_test.jsK-pop idol birthday testing stuffAlice
    drawPixels_change_test.jsTest of altering drawPixels(). Gives burning pixels a red overlay similar to the yellow overlay for charged pixelsAlice
    example_mod.jsExample mod for new moddersR74n
    explodeAtPlus.jsExtension of the explodeAt function with more optionsAlice
    fill_script.jsScript that fills an areaTealEgg#7646
    generative_mods.jsModpack with optional mass element generationAlice
    generator_prompt.jsPrompt to use generative_mods.js's element generators after the game finishes loadingAlice
    gradient_background_support.jsEnables linear gradients for backgrounds as arrays in settings.bgAlice
    libpacman-v1.jsLibrary for making modsmollthecoder
    libpixeltracking.jsLibrary for tracking pixelsmollthecoder
    maxColorOffset.jsProperty to specify how much a pixel's color can be randomly offset from the element colorAlice
    modlangs.jsCustomisable property in an element to allow for translations in mods. See the file for instructions on how to implementSquareScreamYT
    nested_for_reaction_example.jsExample of using a nested for loop to add reactions. It makes various things kill plantsAlice
    nv7.jsGiant Nv7 image [Large]Nv7
    orchidslibrary.jsLibrary used by morechemistry.js, plants.js, and datawire.jsOrchid
    place_all_elements.jsExperimental function that places every pixelAlice
    PRNGworldgenlib.jsWorld generation library with seeded randomnessOrchid
    randomness_but_tick.jsRandom experimental elements using the tick function featureAlice
    randomness_but_tool.jsRandom experimental elements using the tool function featureAlice
    randomness.jsRandom experimental elementsAlice
    scenexe.jsMove through and damage a simulated field of polygons [WIP]nousernamefound
    structure_test_2.jsAnother test for implementing structures into Sandboxels (requires the previous test)Alice
    structure_test.jsTest for implementing structures into SandboxelsAlice
    test.jsTest that adds mayo :)R74n
    tool_pixel_behavior.jsGives unique behaviors to tools if placed with cheatsAlice
    worldgen_test.jsElement that generates a save with a grass layer, dirt layer, rock layer, and a pondAlice
    worldgenlibrary.jsWorld generation libraryOrchid
    Technical Libraries & Tests
    1.10example.jsExamples for modern rendering modding and moreR74n
    a_bundle_of_tests.jsSeveral test functionsAlice
    all_stain.jsEvery element stains solidsstefanblox
    betterMenuScreens.jsLibrary for mods to create their own menusggod
    changePixelDebug.jsThe changePixel() function aborts and logs to console when it tries to change to a non-existent elementAlice
    changeTempReactionParameter.jsThe changeTemp property to modded reactionsAlice
    code_library.jsFunctions and variables common to some other modsAlice
    controllable_pixel_test.jsPixel that can be controlled with the keyboard keys [More Info] [PC ONLY]Alice
    customexplosion.jsCustom explosion element and interface for itOrchid
    date_test.jsK-pop idol birthday testing stuffAlice
    drawPixels_change_test.jsTest of altering drawPixels(). Gives burning pixels a red overlay similar to the yellow overlay for charged pixelsAlice
    example_mod.jsExample mod for new moddersR74n
    explodeAtPlus.jsExtension of the explodeAt function with more optionsAlice
    fill_script.jsScript that fills an areaTealEgg#7646
    generative_mods.jsModpack with optional mass element generationAlice
    generator_prompt.jsPrompt to use generative_mods.js's element generators after the game finishes loadingAlice
    gradient_background_support.jsEnables linear gradients for backgrounds as arrays in settings.bgAlice
    libpacman-v1.jsLibrary for making modsmollthecoder
    libpixeltracking.jsLibrary for tracking pixelsmollthecoder
    maxColorOffset.jsProperty to specify how much a pixel's color can be randomly offset from the element colorAlice
    modlangs.jsCustomisable property in an element to allow for translations in mods. See the file for instructions on how to implementSquareScreamYT
    nested_for_reaction_example.jsExample of using a nested for loop to add reactions. It makes various things kill plantsAlice
    nv7.jsGiant Nv7 image [Large]Nv7
    orchidslibrary.jsLibrary used by morechemistry.js, plants.js, and datawire.jsOrchid
    place_all_elements.jsExperimental function that places every pixelAlice
    PRNGworldgenlib.jsWorld generation library with seeded randomnessOrchid
    randomness_but_tick.jsRandom experimental elements using the tick function featureAlice
    randomness_but_tool.jsRandom experimental elements using the tool function featureAlice
    randomness.jsRandom experimental elementsAlice
    scenexe.jsMove through and damage a simulated field of polygons [WIP]nousernamefound
    structure_test_2.jsAnother test for implementing structures into Sandboxels (requires the previous test)Alice
    structure_test.jsTest for implementing structures into SandboxelsAlice
    test.jsTest that adds mayo :)R74n
    tool_pixel_behavior.jsGives unique behaviors to tools if placed with cheatsAlice
    worldgen_test.jsElement that generates a save with a grass layer, dirt layer, rock layer, and a pondAlice
    worldgenlibrary.jsWorld generation libraryOrchid
    Broken or Deprecated
    a_mod_by_alice.jsCombination of most of Alice's mods, and some other thingsAlice
    adjustablepixelsize.jsSet the pixelSize with a URL parameterAlice
    advanced_colonies.jsDavlers, creatures with complex coloniesDaviStudios
    background_changer.jsPress 'B' to change canvas background to a URLR74n
    borders.jsBlack borders around pixels (Use bright background)R74n
    fast_lightmap.jsLight sources glow, but fasterRedBirdly
    humans.jsHumans. Now part of the base gameR74n
    invertscroll.jsInverts the scroll wheel for adjusting brush size (now a setting)SquareScreamYT
    mobile_shift.jsButton for shift on mobile (Now in the base game)SquareScreamYT
    nicer_flame.jsFire is visually pleasingRedBirdly
    nopixellimit.jsRemoves the pixel limit. (now a setting)Jayd
    pizzasstuff.jsNew animals, foods, and plants_ilikepizza_
    unhide.jsUnhides all elements except molten ones. (This functionality now exists as a vanilla setting)R74n
    wheel_fix.jsAttempts to fix the brush scaling too much with the mouse wheel for some people. DeprecatedNubo318
    Broken or Deprecated
    a_mod_by_alice.jsCombination of most of Alice's mods, and some other thingsAlice
    adjustablepixelsize.jsSet the pixelSize with a URL parameterAlice
    advanced_colonies.jsDavlers, creatures with complex coloniesDaviStudios
    background_changer.jsPress 'B' to change canvas background to a URLR74n
    borders.jsBlack borders around pixels (Use bright background)R74n
    fast_lightmap.jsLight sources glow, but fasterRedBirdly
    humans.jsHumans. Now part of the base gameR74n
    invertscroll.jsInverts the scroll wheel for adjusting brush size (now a setting)SquareScreamYT
    mobile_shift.jsButton for shift on mobile (Now in the base game)SquareScreamYT
    nicer_flame.jsFire is visually pleasingRedBirdly
    nopixellimit.jsRemoves the pixel limit. (now a setting)Jayd
    pizzasstuff.jsNew animals, foods, and plants_ilikepizza_
    unhide.jsUnhides all elements except molten ones. (This functionality now exists as a vanilla setting)R74n
    wheel_fix.jsAttempts to fix the brush scaling too much with the mouse wheel for some people. DeprecatedNubo318
    + + -

    How to create your own mod

    +

    How to create your own mod

    -

    To learn to create your own Sandboxels mod, read the Modding Tutorial on the Sandboxels Wiki, and an Example Mod to base yours off of.

    -

    Mods are submitted to the GitHub repo.

    +

    To learn to create your own Sandboxels mod, read the Modding Tutorial on the Sandboxels Wiki, and an Example Mod to base yours off of.

    +

    Mods are submitted to the GitHub repo.

    -

    Issues with mods

    -

    We don't provide support for unofficial mods. You may ask for help in our Discord server, or press Clear Mods in Settings.

    -

    Some mods may not work every time you load the game, or even at all!

    +

    Issues with mods

    +

    We don't provide support for unofficial mods. You may ask for help in our Discord server, or press Clear Mods in Settings.

    +

    Some mods may not work every time you load the game, or even at all!

    @@ -520,15 +520,15 @@ + crossorigin="anonymous"> From 25d73641cfd7ac78ec34b405be0dde5c5e72e4fc Mon Sep 17 00:00:00 2001 From: suspasha111 Date: Thu, 1 Jan 2026 15:52:40 -0700 Subject: [PATCH 37/38] Add coresbyp.js This mod adds several nuclear cores that can be used as fuel in reactors, also this mod adds a liquid that is more resistant to temperatures to cool your reactor down. Also, a powerful BOOM. --- mods/coresbyp.js | 90 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 mods/coresbyp.js diff --git a/mods/coresbyp.js b/mods/coresbyp.js new file mode 100644 index 00000000..ae2e5d98 --- /dev/null +++ b/mods/coresbyp.js @@ -0,0 +1,90 @@ +elements.broken_core = { + color: "#2e2e2e", + behavior: behaviors.POWDER, + temp: 30, + desc: "Broken core. Result of overheat of safe/medium type cores.", + state: "solid", + category: "cores", +} + +elements.fa_explosion = { + color: ["#00ffa2", "#06c982"], + behavior: [ + "XX|XX|XX", + "XX|EX:390>plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,radiation,radiation,radiation,radiation,radiation,supernova,supernova,oxygen|XX", + "XX|XX|XX", + ], + category: "cores", + temp: 5473876573873892387, + desc: "SUPER POWERFUL EXPLOSION.", +} + +elements.safe_core = { + color: "#ffc400", + behavior: behaviors.WALL, + temp: 300, + desc: "Basic core. Doesn't heat up very much.", + category: "cores", + state: "solid", + stateHigh: "broken_core", + tempHigh: 4000, + tick: function (pixel) { + pixel.temp += 8; + }, +} + +elements.nuclear_core = { + color: "#eaff00", + behavior: behaviors.WALL, + temp: 600, + desc: "Nuclear core. Doesn't heat up... much.", + category: "cores", + state: "solid", + stateHigh: "broken_core", + tempHigh: 9000, + tick: function (pixel) { + pixel.temp += 21; + }, + hardness: 1, +} + +elements.thermonuclear_core = { + color: "#03fc5e", + behavior: behaviors.WALL, + temp: 9000, + desc: "Thermo-nuclear core. USE WITH EXTREME CAUTION.", + category: "cores", + state: "solid", + stateHigh: "n_explosion", + tempHigh: 96000, + tick: function (pixel) { + pixel.temp += 147; + }, + hardness: 1, +} + +elements.thermonuclear_fusion_core = { + color: "#630134", + behavior: behaviors.WALL, + temp: 754803, + desc: "Fusion core, basically the most destructive core. KEEP OUT AND DON'T USE.", + category: "cores", + state: "solid", + stateHigh: "fa_explosion", + tempHigh: 9600000, + tick: function (pixel) { + pixel.temp += 19047; + }, + hardness: 1, +} + +elements.reactor_fluid = { + color: "#00a5b8", + behavior: behaviors.LIQUID, + temp: -50, + tempHigh: 500, + stateHigh: "steam", + desc: "Nuclear reactor cooling fluid. You can use it instead of water.", + state: "liquid", + category: "cores" +} From b7ddc97385bcabe7ecece21898a2d8f46d2e0848 Mon Sep 17 00:00:00 2001 From: An Orbit <68935009+orbit-loona@users.noreply.github.com> Date: Mon, 5 Jan 2026 14:36:35 -0500 Subject: [PATCH 38/38] Update a_mod_by_alice.js --- mods/a_mod_by_alice.js | 65 ++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/mods/a_mod_by_alice.js b/mods/a_mod_by_alice.js index 703f08c0..9e9840e3 100644 --- a/mods/a_mod_by_alice.js +++ b/mods/a_mod_by_alice.js @@ -1466,6 +1466,7 @@ try { function createPixelReturn(elementIn,x,y) { //sugar var element = elementIn; while(element instanceof Array) { element = randomChoice(element) }; + if(pixelMap[x]?.[y] == undefined) { return {} } var newPixel = new Pixel(x, y, element); currentPixels.push(newPixel); checkUnlock(element); @@ -2214,8 +2215,8 @@ try { return thingsArray.join(", ") }; }; - function capitalizeFirstLetter(string,locale=null) { - return string[0][locale ? "toLocaleUpperCase" : "toUpperCase"](locale) + string.slice(1) + function capitalizeFirstLetter(string) { + return string[0].toUpperCase() + string.slice(1) }; //INTERFACE TO SET OTHER PIXEL PROPERTIES WHEN PLACING SPECIFIC ELEMENTS ## @@ -4568,24 +4569,28 @@ color1 and color2 spread through striped paint like dye does with itself. col } else if (Math.floor(Math.random()*100)col //console.log((pixel.x+randomMove1[0]) + " " + (pixel.y+randomMove1[1])) var newPixel = null; if(!outOfBounds(pixel.x+randomMove1[0],pixel.y+randomMove1[1])) { - newPixel = pixelMap[pixel.x+randomMove1[0]][pixel.y+randomMove1[1]]; //newPixel is AAA + if(pixelMap[pixel.x+randomMove1[0]] !== undefined) { + newPixel = pixelMap[pixel.x+randomMove1[0]][pixel.y+randomMove1[1]]; //newPixel is AAA + } }; if(outOfBounds(pixel.x+randomMove1[0],pixel.y+randomMove1[1]) || !reactionStealer(pixel,newPixel,"radiation")) { var randomMove2 = move2Spots[Math.floor(Math.random() * move2Spots.length)]; if(!tryMove(pixel, pixel.x+randomMove2[0], pixel.y+randomMove2[1])) { var newPixel = null; if(!outOfBounds(pixel.x+randomMove1[0],pixel.y+randomMove1[1])) { - newPixel = pixelMap[pixel.x+randomMove1[0]][pixel.y+randomMove1[1]]; //newPixel is AAA + if(pixelMap[pixel.x+randomMove1[0]] !== undefined) { + newPixel = pixelMap[pixel.x+randomMove1[0]][pixel.y+randomMove1[1]]; //newPixel is AAA + } }; if(newPixel !== null) { reactionStealer(pixel,newPixel,"radiation") }; }; @@ -9192,7 +9201,7 @@ color1 and color2 spread through striped paint like dye does with itself. col for(var i = 0; i < dyeColors.length; i++) { newLegacyFnmDye(dyeColors[i][0],dyeColors[i][1]) }; - eLists.LED = ["led_r","led_g","led_b","led"]; + eLists.LED = ["led_r","led_g","led_b"]; function newLED(abbrev,hexColor,baseColorOverrideHex=null) { if(!(hexColor.startsWith("#"))) { hexColor = "#" + hexColor }; if(baseColorOverrideHex && !(baseColorOverrideHex.startsWith("#"))) { baseColorOverrideHex = "#" + baseColorOverrideHex }; @@ -13708,7 +13717,7 @@ Pixel size (rendering only): (Use if the save looks cut o stats += "Burning"; } if (elements[currentPixel.element].hoverStat) { - stats += ""+elements[currentPixel.element].hoverStat(currentPixel)+""; + stats += ""+elements[currentPixel.element]?.hoverStat?.(currentPixel)+""; } else if (elements[currentElement].toolHoverStat) { stats += ""+elements[currentElement].toolHoverStat(currentPixel).toString().replaceAll("<","<")+""; @@ -14060,7 +14069,9 @@ Pixel size (rendering only): (Use if the save looks cut o //3. slice(1) removes empty (OOB) position at y=0 //4. indexOf(false) always shows the first matching item //5. an offset I don't understand (probably from that slice) shifts the first match to the empty spot above the first full pixel - var firstEmptyY = [...pixelMap[x].map(obj =>!obj || elements[obj.element].state == "gas"),false].slice(1).indexOf(false); + let row = pixelMap?.[x]; + if(!row) { return false }; + var firstEmptyY = [...(row.map(obj =>!obj || elements[obj.element].state == "gas")),false].slice(1).indexOf(false); if(firstEmptyY == -1) { return false; }; @@ -42225,7 +42236,7 @@ Make sure to save your command in a file if you want to add this preset again.` emptySlots = emptySlots.slice(0,2); for(var i = 0; i < emptySlots.length; i++) { var coords = emptySlots[i]; - createPixelReturn("steam",...coords).temp = pixel.temp + let n = tryCreatePixelReturn("steam",...coords); if(n) { n.temp = pixel.temp } }; changePixel(pixel,"calcium_sulfate",false); return @@ -42243,7 +42254,7 @@ Make sure to save your command in a file if you want to add this preset again.` emptySlots = emptySlots.slice(0,2); for(var i = 0; i < emptySlots.length; i++) { var coords = emptySlots[i]; - createPixelReturn("steam",...coords).temp = pixel.temp + let n = tryCreatePixelReturn("steam",...coords); if(n) { n.temp = pixel.temp } }; changePixel(pixel,"molten_calcium_sulfate",false); return @@ -44975,7 +44986,7 @@ maxPixels (default 1000): Maximum amount of pixels/changes (if xSpacing and ySpa function editDistance(s1, s2) {s1 = s1.toLowerCase();s2 = s2.toLowerCase();var costs = new Array();for (var i = 0; i <= s1.length; i++) {var lastValue = i;for (var j = 0; j <= s2.length; j++) {if (i == 0)costs[j] = j;else {if (j > 0) {var newValue = costs[j - 1];if (s1.charAt(i - 1) != s2.charAt(j - 1))newValue = Math.min(Math.min(newValue, lastValue),costs[j]) + 1;costs[j - 1] = lastValue;lastValue = newValue;}}}if (i > 0)costs[s2.length] = lastValue;}return costs[s2.length];} function similarity(s1, s2) {var longer = s1;var shorter = s2;if (s1.length < s2.length) {longer = s2;shorter = s1;}var longerLength = longer.length;if (longerLength == 0) {return 1.0;}return (longerLength - editDistance(longer, shorter)) / parseFloat(longerLength);} function mostSimilarElement(s) { - // delete elements; + delete elements; var max = 0; var maxElement = ""; for (var e in elements) { @@ -46005,6 +46016,12 @@ maxPixels (default 1000): Maximum amount of pixels/changes (if xSpacing and ySpa logMessage("a_mod_by_alice.js requires many other mods. Many of the elements and features added with it installed are actually added by the other mods it depends on.") logMessage('These mods are libhooktick.js, chem.js, minecraft.js, Neutronium Mod.js, fey_and_more.js, velocity.js, ketchup_mod.js, moretools.js, aChefsDream.js, nousersthings.js. They were enabled automatically') }) + isEmpty = function(x, y, ignoreBounds=false, oob=false) { + if (oob === true || outOfBounds(x,y)) { + return ignoreBounds; + } + return pixelMap[x]?.[y] === undefined; //fix failure to handle nonexistent columns + } } catch (error) { alert(`Load failed (try reloading).\nThis is likely a sporadic failure caused by inconsistencies in how mods are loaded, and will likely fix itself in a refresh or two. If it persists, then it's an issue.\nError: ${error.stack}`); console.error(error)

The original plain text version of this is still maintained.