UNPKG

puzzlescript

Version:

Play PuzzleScript games in your terminal!

678 lines 31.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AstBuilder = void 0; const colors_1 = require("../colors"); const collisionLayer_1 = require("../models/collisionLayer"); const colors_2 = require("../models/colors"); const game_1 = require("../models/game"); const metadata_1 = require("../models/metadata"); const rule_1 = require("../models/rule"); const tile_1 = require("../models/tile"); const winCondition_1 = require("../models/winCondition"); const util_1 = require("../util"); const ast = __importStar(require("./astTypes")); const RULE_DIRECTION_LIST = [ util_1.RULE_DIRECTION.UP, util_1.RULE_DIRECTION.DOWN, util_1.RULE_DIRECTION.LEFT, util_1.RULE_DIRECTION.RIGHT ]; const RULE_DIRECTION_SET = new Set(RULE_DIRECTION_LIST); function removeNulls(ary) { // return ary.filter(a => !!a) // TypeScript-friendly version const ret = []; for (const item of ary) { if (item) { ret.push(item); } } return ret; } function cacheSetAndGet(cache, obj) { const key = obj.toKey(); if (!cache.has(key)) { cache.set(key, obj); } return cache.get(key); } function relativeDirectionToAbsolute(currentDirection, relativeModifier) { let currentDir; switch (currentDirection) { case util_1.RULE_DIRECTION.RIGHT: currentDir = 0; break; case util_1.RULE_DIRECTION.UP: currentDir = 1; break; case util_1.RULE_DIRECTION.LEFT: currentDir = 2; break; case util_1.RULE_DIRECTION.DOWN: currentDir = 3; break; default: throw new Error(`BUG! Invalid rule direction "${currentDirection}`); } switch (relativeModifier) { case util_1.RULE_DIRECTION_RELATIVE.RELATIVE_RIGHT: currentDir += 0; break; case util_1.RULE_DIRECTION_RELATIVE.RELATIVE_UP: currentDir += 1; break; case util_1.RULE_DIRECTION_RELATIVE.RELATIVE_LEFT: currentDir += 2; break; case util_1.RULE_DIRECTION_RELATIVE.RELATIVE_DOWN: currentDir += 3; break; default: throw new Error(`BUG! invalid relative direction "${relativeModifier}"`); } switch (currentDir % 4) { case 0: return util_1.RULE_DIRECTION.RIGHT; case 1: return util_1.RULE_DIRECTION.UP; case 2: return util_1.RULE_DIRECTION.LEFT; case 3: return util_1.RULE_DIRECTION.DOWN; default: throw new Error(`BUG! Incorrectly computed rule direction "${currentDirection}" "${relativeModifier}"`); } } class AstBuilder { constructor(code) { this.code = code; this.tileCache = new Map(); this.soundCache = new Map(); } build(root) { const source = this.toSource({ _sourceOffset: 0 }); const metadata = new metadata_1.GameMetadata(); root.metadata.forEach((pair) => { let value; if (typeof pair.value === 'object' && pair.value.type) { switch (pair.value.type) { case ast.COLOR_TYPE.HEX8: case ast.COLOR_TYPE.HEX6: case ast.COLOR_TYPE.NAME: { const v = pair.value; value = this.buildColor(v, metadata.colorPalette); } break; case 'WIDTH_AND_HEIGHT': { const v = pair.value; const v2 = v; value = new metadata_1.Dimension(v2.width, v2.height); } break; default: throw new Error(`BUG: Invalid type at this point in time: ${pair.value}`); } } else { value = pair.value; } metadata._setValue(pair.type, value); }); const sprites = root.sprites.map((n) => this.buildSprite(n, metadata.colorPalette)); // assign an index to each GameSprite sprites.forEach((sprite, index) => { sprite.allSpritesBitSetIndex = index; }); // Load the legend items up (used in Rules and Levels later on) const legendItems = root.legendItems.map((n) => this.buildLegendItem(n)); const sounds = root.sounds.map((n) => this.buildSound(n)); const collisionLayers = root.collisionLayers.map((n) => this.buildCollisionLayer(n)); const simpleRules = this.buildSimpleRules(root.rules); const winConditions = root.winConditions.map((n) => this.buildWinConditon(n)); const levels = root.levels.map((n) => this.buildLevel(n)); return new game_1.GameData(source, root.title, metadata, sprites, legendItems, sounds, collisionLayers, simpleRules, winConditions, levels); } buildSprite(node, colorPalette) { let ret; if (node.type === ast.SPRITE_TYPE.WITH_PIXELS) { const source = this.toSource(node); const colors = node.colors.map((n) => this.buildColor(n, colorPalette)); const pixels = node.pixels.map((row) => { return row.map((col) => { if (col === '.') { return new colors_2.TransparentColor(source); } else { return colors[col] || new colors_2.TransparentColor(source); } }); }); // Pixel colors are 0-indexed. ret = new tile_1.GameSpritePixels(source, node.name, node.mapChar, pixels); } else { ret = new tile_1.GameSpriteSingleColor(this.toSource(node), node.name, node.mapChar, node.colors.map((n) => this.buildColor(n, colorPalette))); } this.cacheAdd(node.name, ret); if (node.mapChar) { this.cacheAdd(node.mapChar, ret); } return ret; } buildColor(node, colorPalette) { const source = this.toSource(node); const currentColorPalette = colorPalette || 'arnecolors'; switch (node.type) { case ast.COLOR_TYPE.HEX8: case ast.COLOR_TYPE.HEX6: return new colors_2.HexColor(source, node.value); case ast.COLOR_TYPE.NAME: if (node.value.toUpperCase() === 'TRANSPARENT') { return new colors_2.TransparentColor(source); } else { // Look up the color const hex = (0, colors_1.lookupColorPalette)(currentColorPalette, node.value); if (hex) { return new colors_2.HexColor(source, hex); } else { return new colors_2.TransparentColor(source); } } default: throw new Error(`Unsupported type ${node}`); } } buildLegendItem(node) { const source = this.toSource(node); switch (node.type) { case ast.TILE_TYPE.SIMPLE: if (!node.tile) { throw new Error(`BUG!!!!!!`); } { const ret = new tile_1.GameLegendTileSimple(source, node.name, this.cacheGet(node.tile)); this.cacheAdd(node.name, ret); return ret; } case ast.TILE_TYPE.AND: if (!node.tiles) { throw new Error(`BUG!!!!!!`); } { const ret = new tile_1.GameLegendTileAnd(source, node.name, node.tiles.map((n) => this.cacheGet(n))); this.cacheAdd(node.name, ret); return ret; } case ast.TILE_TYPE.OR: if (!node.tiles) { throw new Error(`BUG!!!!!!`); } { const ret = new tile_1.GameLegendTileOr(source, node.name, node.tiles.map((n) => this.cacheGet(n))); this.cacheAdd(node.name, ret); return ret; } default: throw new Error(`Unsupported type ${node}`); } } buildCollisionLayer(node) { const source = this.toSource(node); return new collisionLayer_1.CollisionLayer(source, node.tiles.map((n) => this.cacheGet(n))); } buildSound(node) { switch (node.type) { case 'SOUND_SFX': this.soundCacheAdd(node.soundEffect, node); return node; case 'SOUND_WHEN': return node; case 'SOUND_SPRITE_MOVE': case 'SOUND_SPRITE_DIRECTION': case 'SOUND_SPRITE_EVENT': return Object.assign(Object.assign({}, node), { sprite: this.cacheGet(node.sprite) }); default: throw new Error(`Unsupported type ${node}`); } } buildCommand(node) { switch (node.type) { case ast.COMMAND_TYPE.SFX: if (!this.soundCacheHas(node.sound)) { return null; } const sound = this.soundCacheGet(node.sound); return Object.assign(Object.assign({}, node), { sound }); case ast.COMMAND_TYPE.AGAIN: case ast.COMMAND_TYPE.CANCEL: case ast.COMMAND_TYPE.MESSAGE: case ast.COMMAND_TYPE.CHECKPOINT: case ast.COMMAND_TYPE.RESTART: case ast.COMMAND_TYPE.WIN: return node; default: throw new Error(`Unsupported type ${node}`); } } buildWinConditon(node) { const source = this.toSource(node); switch (node.type) { case ast.WIN_CONDITION_TYPE.ON: return new winCondition_1.WinConditionOn(source, node.qualifier, this.cacheGet(node.tile), this.cacheGet(node.onTile)); case ast.WIN_CONDITION_TYPE.SIMPLE: return new winCondition_1.WinConditionSimple(source, node.qualifier, this.cacheGet(node.tile)); default: throw new Error(`Unsupported type ${node}`); } } buildLevel(node) { switch (node.type) { case ast.LEVEL_TYPE.MESSAGE: return node; case ast.LEVEL_TYPE.MAP: return Object.assign(Object.assign({}, node), { cells: node.cells.map((row) => row.map((cell) => this.cacheGet(cell))) }); default: throw new Error(`Unsupported type ${node}`); } } buildSimpleRules(rules) { // Simplify the rules by de-duplicating them const ruleCache = new Map(); const bracketCache = new Map(); const neighborCache = new Map(); const tileCache = new Map(); const simpleRules = rules.map((n) => this.simplifyRule(n, ruleCache, bracketCache, neighborCache, tileCache)); return simpleRules; } simplifyRule(node, ruleCache, bracketCache, neighborCache, tileCache) { const source = this.toSource(node); switch (node.type) { case ast.RULE_TYPE.LOOP: { const subRules = node.rules.map((n) => this.simplifyRule(n, ruleCache, bracketCache, neighborCache, tileCache)); return new rule_1.SimpleRuleLoop(source, false /*isRandom*/, subRules); } case ast.RULE_TYPE.GROUP: // Extra checks to make TypeScript happy if (node.rules[0]) { const firstRule = node.rules[0]; const isRandom = !!firstRule.isRandom; const subRules = node.rules.map((n) => this.simplifyRule(n, ruleCache, bracketCache, neighborCache, tileCache)); // if (rules.length === 1) { // return rules[0] // } return new rule_1.SimpleRuleGroup(source, isRandom, subRules); } throw new Error(`BUG!!!!!!`); case ast.RULE_TYPE.SIMPLE: { /** * Expands this Rule into multiple `SimpleRule` objects. * * For example, `HORIZONTAL [ > player ] -> [ < crate ]` gets expanded to the following `SimpleRule`s: * * - `LEFT [ LEFT player ] -> [ RIGHT crate ]` * - `RIGHT [ RIGHT player ] -> [ LEFT crate ]` * * The `SimpleRule`s only have absolute directions * rather than the relative ones specified in the original game code. */ const isRandom = !!node.isRandom; const conditions = node.conditions; const actions = node.actions; // Check if valid if (conditions.length !== actions.length && actions.length !== 0) { throw new Error(`Left side has "${conditions.length}" conditions and right side has "${actions.length}" actions!`); } if (conditions.length === actions.length) { // do nothing } else if (actions.length !== 0) { throw new Error(`Invalid Rule. The number of brackets on the right must match the structure of the left hand side or be 0`); } const simpleRules = this.rule_convertToMultiple(node).map((r) => this.rule_toSimple(r, ruleCache, bracketCache, neighborCache, tileCache)); // If the brackets are all the same object then that means we can just output 1 rule // (the brackets don't have any directions. Otherwise they would not have been // deduplicated as part of the .toKey() and cacheGetAndSet) const isDuplicate = simpleRules.length === 1 || (!node.isRandom && simpleRules[1] && simpleRules[0].canCollapseBecauseBracketsMatch(simpleRules[1])); if (isDuplicate) { simpleRules[0].subscribeToCellChanges(); // we still need it to be in a RuleGroup // so the Rule can be evaluated multiple times (not just once) return new rule_1.SimpleRuleGroup(source, isRandom, [simpleRules[0]]); } else { for (const rule of simpleRules) { rule.subscribeToCellChanges(); } return new rule_1.SimpleRuleGroup(source, isRandom, simpleRules); } } default: throw new Error(`Unsupported type ${node}`); } } rule_toSimple(node, ruleCache, bracketCache, neighborCache, tileCache) { const source = this.toSource(node); const directions = this.rule_getDirectionModifiers(node); const commands = removeNulls(node.commands.map((n) => this.buildCommand(n))); if (directions.length !== 1) { throw new Error(`BUG: should have exactly 1 direction by now but found the following: "${directions}"`); } // Check if the condition matches the action. If so, we can simplify evaluation. const conditionBrackets = node.conditions.map((x) => this.bracket_toSimple(x, directions[0], ruleCache, bracketCache, neighborCache, tileCache)); const actionBrackets = node.actions.map((x) => this.bracket_toSimple(x, directions[0], ruleCache, bracketCache, neighborCache, tileCache)); for (let index = 0; index < conditionBrackets.length; index++) { const action = actionBrackets[index]; // Skip rules with no action bracket `[ > Player ] -> CHECKPOINT` if (!action) { continue; } } return cacheSetAndGet(ruleCache, new rule_1.SimpleRule(source, conditionBrackets, actionBrackets, commands, node.isLate, node.isRigid, node.debugFlag)); } rule_convertToMultiple(node) { let rulesToConvert = []; let convertedRules = []; for (const direction of this.rule_getDirectionModifiers(node)) { const expandedDirection = this.rule_clone(node, direction, null, null); rulesToConvert.push(expandedDirection); } const expandModifiers = new Map(); expandModifiers.set(ast.RULE_MODIFIER.HORIZONTAL, [util_1.RULE_DIRECTION.LEFT, util_1.RULE_DIRECTION.RIGHT]); expandModifiers.set(ast.RULE_MODIFIER.VERTICAL, [util_1.RULE_DIRECTION.UP, util_1.RULE_DIRECTION.DOWN]); expandModifiers.set(ast.RULE_MODIFIER.MOVING, [util_1.RULE_DIRECTION.UP, util_1.RULE_DIRECTION.DOWN, util_1.RULE_DIRECTION.LEFT, util_1.RULE_DIRECTION.RIGHT, util_1.RULE_DIRECTION.ACTION]); let didExpandRulesToConvert; do { didExpandRulesToConvert = false; for (const rule of rulesToConvert) { let didExpand = false; const direction = this.rule_getDirectionModifiers(rule)[0]; if (this.rule_getDirectionModifiers(rule).length !== 1) { throw new Error(`BUG: should have already expanded the rule to only contian one direction`); } for (const [nameToExpand, variations] of expandModifiers) { if (this.rule_hasModifier(rule, nameToExpand)) { for (const variation of variations) { convertedRules.push(this.rule_clone(rule, direction, nameToExpand, variation)); didExpand = true; didExpandRulesToConvert = true; } } } if (!didExpand) { // Try expanding PARALLEL and ORTHOGONAL (since they depend on the rule direction) let perpendiculars; let parallels; switch (direction) { case util_1.RULE_DIRECTION.UP: case util_1.RULE_DIRECTION.DOWN: perpendiculars = [util_1.RULE_DIRECTION.LEFT, util_1.RULE_DIRECTION.RIGHT]; parallels = [util_1.RULE_DIRECTION.UP, util_1.RULE_DIRECTION.DOWN]; break; case util_1.RULE_DIRECTION.LEFT: case util_1.RULE_DIRECTION.RIGHT: perpendiculars = [util_1.RULE_DIRECTION.UP, util_1.RULE_DIRECTION.DOWN]; parallels = [util_1.RULE_DIRECTION.LEFT, util_1.RULE_DIRECTION.RIGHT]; break; default: throw new Error(`BUG: There must be some direction`); } if (perpendiculars && parallels) { const orthoParallels = [ { nameToExpand: ast.RULE_MODIFIER.ORTHOGONAL, variations: perpendiculars }, { nameToExpand: ast.RULE_MODIFIER.PERPENDICULAR, variations: perpendiculars }, { nameToExpand: ast.RULE_MODIFIER.PARALLEL, variations: parallels } ]; for (const { nameToExpand, variations } of orthoParallels) { if (this.rule_hasModifier(rule, nameToExpand)) { for (const variation of variations) { convertedRules.push(this.rule_clone(rule, direction, nameToExpand, variation)); didExpand = true; didExpandRulesToConvert = true; } } } } } // If nothing was expanded and this is the current rule // then just keep it if (!didExpand) { convertedRules.push(rule); } } rulesToConvert = convertedRules; convertedRules = []; } while (didExpandRulesToConvert); return rulesToConvert; } rule_clone(node, direction, nameToExpand, newName) { const conditions = node.conditions.map((bracket) => this.bracket_clone(bracket, direction, nameToExpand, newName)); const actions = node.actions.map((bracket) => this.bracket_clone(bracket, direction, nameToExpand, newName)); // retain LATE and RIGID but discard the rest of the modifiers let directionModifier; switch (direction) { case util_1.RULE_DIRECTION.UP: directionModifier = ast.RULE_MODIFIER.UP; break; case util_1.RULE_DIRECTION.DOWN: directionModifier = ast.RULE_MODIFIER.DOWN; break; case util_1.RULE_DIRECTION.LEFT: directionModifier = ast.RULE_MODIFIER.LEFT; break; case util_1.RULE_DIRECTION.RIGHT: directionModifier = ast.RULE_MODIFIER.RIGHT; break; default: throw new Error(`BUG: Invalid direction "${direction}"`); } return Object.assign(Object.assign({}, node), { directions: [directionModifier], conditions, actions }); } rule_getDirectionModifiers(node) { // Convert HORIZONTAL and VERTICAL to 2: if (node.directions.indexOf(ast.RULE_MODIFIER.HORIZONTAL) >= 0) { return [util_1.RULE_DIRECTION.LEFT, util_1.RULE_DIRECTION.RIGHT]; } if (node.directions.indexOf(ast.RULE_MODIFIER.VERTICAL) >= 0) { return [util_1.RULE_DIRECTION.UP, util_1.RULE_DIRECTION.DOWN]; } const directions = node.directions.filter((m) => RULE_DIRECTION_SET.has(m)).map((d) => { switch (d) { case ast.RULE_MODIFIER.UP: return util_1.RULE_DIRECTION.UP; case ast.RULE_MODIFIER.DOWN: return util_1.RULE_DIRECTION.DOWN; case ast.RULE_MODIFIER.LEFT: return util_1.RULE_DIRECTION.LEFT; case ast.RULE_MODIFIER.RIGHT: return util_1.RULE_DIRECTION.RIGHT; default: throw new Error(`BUG: Invalid rule direction "${d}"`); } }); if (directions.length === 0) { return RULE_DIRECTION_LIST; } else { return directions; } } rule_hasModifier(node, modifier) { for (const bracket of node.conditions) { for (const neighbor of this.rule_getAllBracketNeighbors(bracket)) { for (const t of neighbor.tileWithModifiers) { if (t.direction === modifier) { // HACK: cast to string return true; } } } } return false; } rule_getAllBracketNeighbors(node) { switch (node.type) { case ast.BRACKET_TYPE.SIMPLE: return node.neighbors; case ast.BRACKET_TYPE.ELLIPSIS: return [...node.beforeNeighbors, ...node.afterNeighbors]; default: throw new Error(`Unsupported type ${node}`); } } bracket_clone(node, direction, nameToExpand, newName) { switch (node.type) { case ast.BRACKET_TYPE.SIMPLE: const neighbors = node.neighbors.map((n) => this.neighbor_clone(n, direction, nameToExpand, newName)); return Object.assign(Object.assign({}, node), { neighbors }); case ast.BRACKET_TYPE.ELLIPSIS: const beforeNeighbors = node.beforeNeighbors.map((n) => this.neighbor_clone(n, direction, nameToExpand, newName)); const afterNeighbors = node.afterNeighbors.map((n) => this.neighbor_clone(n, direction, nameToExpand, newName)); return Object.assign(Object.assign({}, node), { beforeNeighbors, afterNeighbors }); default: throw new Error(`Unsupported type ${node}`); } } neighbor_clone(node, direction, nameToExpand, newName) { return Object.assign(Object.assign({}, node), { tileWithModifiers: node.tileWithModifiers.map((t) => this.tile_clone(t, direction, nameToExpand, newName)) }); } tile_clone(node, direction, nameToExpand, newName) { switch (node.direction) { case util_1.RULE_DIRECTION_RELATIVE.RELATIVE_UP: case util_1.RULE_DIRECTION_RELATIVE.RELATIVE_DOWN: case util_1.RULE_DIRECTION_RELATIVE.RELATIVE_LEFT: case util_1.RULE_DIRECTION_RELATIVE.RELATIVE_RIGHT: const modifier = relativeDirectionToAbsolute(direction, node.direction); return Object.assign(Object.assign({}, node), { direction: modifier }); case nameToExpand: return Object.assign(Object.assign({}, node), { direction: newName }); case util_1.RULE_DIRECTION.UP: case util_1.RULE_DIRECTION.DOWN: case util_1.RULE_DIRECTION.LEFT: case util_1.RULE_DIRECTION.RIGHT: case util_1.RULE_DIRECTION.ACTION: case util_1.RULE_DIRECTION.STATIONARY: case util_1.RULE_DIRECTION.RANDOMDIR: case null: case undefined: return node; default: return node; // throw new Error(`BUG: Unsupported tile direction ${JSON.stringify(node)}`) } } bracket_toSimple(node, direction, ruleCache, bracketCache, neighborCache, tileCache) { const source = this.toSource(node); switch (node.type) { case ast.BRACKET_TYPE.SIMPLE: const neighbors = node.neighbors.map((x) => this.neighbor_toSimple(x, neighborCache, tileCache)); return cacheSetAndGet(bracketCache, new rule_1.SimpleBracket(source, direction, neighbors, node.debugFlag)); case ast.BRACKET_TYPE.ELLIPSIS: const beforeEllipsis = node.beforeNeighbors.map((x) => this.neighbor_toSimple(x, neighborCache, tileCache)); const afterEllipsis = node.afterNeighbors.map((x) => this.neighbor_toSimple(x, neighborCache, tileCache)); return cacheSetAndGet(bracketCache, new rule_1.SimpleEllipsisBracket(source, direction, beforeEllipsis, afterEllipsis, node.debugFlag)); default: throw new Error(`Unsupported type ${node}`); } } neighbor_toSimple(node, neighborCache, tileCache) { const source = this.toSource(node); // Collapse duplicate tiles into one. // e.g. Cyber-Lasso has the following rule: // ... -> [ ElectricFloor Powered no ElectricFloor Claimed ] // // ElectricFloor occurs twice (one is negated) // We keep the first and remove the rest const tilesMap = new Map(); for (const t of node.tileWithModifiers) { if (!tilesMap.has(t.tile)) { tilesMap.set(t.tile, t); } } const tileWithModifiers = [...tilesMap.values()]; const simpleTilesWithModifier = new Set(removeNulls(tileWithModifiers.map((x) => this.tile_toSimple(x, tileCache)))); return cacheSetAndGet(neighborCache, new rule_1.SimpleNeighbor(source, simpleTilesWithModifier, node.debugFlag)); } tile_toSimple(node, tileCache) { const source = this.toSource(node); // Some games mistakenly use SFX# in a bracket when the SFX should be in the commands list after the brackets if (!this.cacheHas(node.tile)) { return null; } const tile = this.cacheGet(node.tile); let direction; switch (node.direction) { case util_1.RULE_DIRECTION_RELATIVE.RELATIVE_UP: case util_1.RULE_DIRECTION_RELATIVE.RELATIVE_DOWN: case util_1.RULE_DIRECTION_RELATIVE.RELATIVE_LEFT: case util_1.RULE_DIRECTION_RELATIVE.RELATIVE_RIGHT: throw new Error(`BUG: Relative directions should have been resolved by now`); default: direction = node.direction || null; // could be undefined (causes problems when evaluating) } return cacheSetAndGet(tileCache, new rule_1.SimpleTileWithModifier(source, node.isNegated, node.isRandom, direction, tile, node.debugFlag)); } toSource(node) { return { code: this.code, sourceOffset: node._sourceOffset }; } cacheHas(name) { return this.tileCache.has(name.toLowerCase()); } cacheAdd(name, value) { if (this.tileCache.has(name.toLowerCase())) { throw new Error(`BUG??? duplicate definition of ${name}`); } this.tileCache.set(name.toLowerCase(), value); } cacheGet(name) { const value = this.tileCache.get(name.toLowerCase()); if (value) { return value; } else { throw new Error(`BUG: Could not find tile named ${name}`); } } soundCacheAdd(name, value) { if (this.soundCache.has(name.toLowerCase())) { throw new Error(`BUG??? duplicate definition of ${name}`); } this.soundCache.set(name.toLowerCase(), value); } soundCacheGet(name) { const value = this.soundCache.get(name.toLowerCase()); if (value) { return value; } else { throw new Error(`BUG: Could not find sound named ${name}`); } } soundCacheHas(name) { return this.soundCache.has(name.toLowerCase()); } } exports.AstBuilder = AstBuilder; //# sourceMappingURL=astBuilder.js.map