UNPKG

puzzlescript

Version:

Play PuzzleScript games in your terminal!

327 lines 12.7 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.EmptyGameEngineHandler = exports.shouldTick = exports.MESSAGE_TYPE = exports.pollingPromise = exports.spritesThatInteractWithPlayer = exports.DEBUG_FLAG = exports.getRandomSeed = exports.clearRandomValuesForTesting = exports.setRandomValuesForTesting = exports.resetRandomSeed = exports.nextRandom = exports.setDifference = exports.setIntersection = exports.setAddAll = exports.setEquals = exports.opposite = exports._debounce = exports._flatten = exports.RULE_DIRECTION_RELATIVE = exports.INPUT_BUTTON = exports.RULE_DIRECTION = void 0; var RULE_DIRECTION; (function (RULE_DIRECTION) { RULE_DIRECTION["UP"] = "UP"; RULE_DIRECTION["DOWN"] = "DOWN"; RULE_DIRECTION["LEFT"] = "LEFT"; RULE_DIRECTION["RIGHT"] = "RIGHT"; RULE_DIRECTION["ACTION"] = "ACTION"; RULE_DIRECTION["STATIONARY"] = "STATIONARY"; RULE_DIRECTION["RANDOMDIR"] = "RANDOMDIR"; })(RULE_DIRECTION = exports.RULE_DIRECTION || (exports.RULE_DIRECTION = {})); var INPUT_BUTTON; (function (INPUT_BUTTON) { INPUT_BUTTON["UP"] = "UP"; INPUT_BUTTON["DOWN"] = "DOWN"; INPUT_BUTTON["LEFT"] = "LEFT"; INPUT_BUTTON["RIGHT"] = "RIGHT"; INPUT_BUTTON["ACTION"] = "ACTION"; INPUT_BUTTON["UNDO"] = "UNDO"; INPUT_BUTTON["RESTART"] = "RESTART"; })(INPUT_BUTTON = exports.INPUT_BUTTON || (exports.INPUT_BUTTON = {})); var RULE_DIRECTION_RELATIVE; (function (RULE_DIRECTION_RELATIVE) { RULE_DIRECTION_RELATIVE["RELATIVE_LEFT"] = "<"; RULE_DIRECTION_RELATIVE["RELATIVE_RIGHT"] = ">"; RULE_DIRECTION_RELATIVE["RELATIVE_UP"] = "^"; RULE_DIRECTION_RELATIVE["RELATIVE_DOWN"] = "V"; })(RULE_DIRECTION_RELATIVE = exports.RULE_DIRECTION_RELATIVE || (exports.RULE_DIRECTION_RELATIVE = {})); // From https://stackoverflow.com/questions/10865025/merge-flatten-an-array-of-arrays-in-javascript/39000004#39000004 function _flatten(arrays) { // return [].concat.apply([], arrays) as T[] const ret = []; arrays.forEach((ary) => { ary.forEach((item) => { ret.push(item); }); }); return ret; } exports._flatten = _flatten; // export function filterNulls<T>(items: Array<Optional<T>>) { // const ret: T[] = [] // items.forEach((x) => { // if (x) { ret.push(x) } // }) // return ret // } // export function _zip<T1, T2>(array1: T1[], array2: T2[]) { // if (array1.length < array2.length) { // throw new Error(`BUG: Zip array length mismatch ${array1.length} != ${array2.length}`) // } // return array1.map((v1, index) => { // return [v1, array2[index]] // }) // } // export function _extend(dest: any, ...rest: any[]) { // for (const obj of rest) { // for (const key of Object.keys(obj)) { // dest[key] = obj[key] // } // } // return dest // } function _debounce(callback) { let timeout; // NodeJS.Timer return () => { if (timeout) { clearTimeout(timeout); } timeout = setTimeout(() => { callback(); }, 10); }; } exports._debounce = _debounce; function opposite(dir) { switch (dir) { case RULE_DIRECTION.UP: return RULE_DIRECTION.DOWN; case RULE_DIRECTION.DOWN: return RULE_DIRECTION.UP; case RULE_DIRECTION.LEFT: return RULE_DIRECTION.RIGHT; case RULE_DIRECTION.RIGHT: return RULE_DIRECTION.LEFT; default: throw new Error(`BUG: Invalid direction: "${dir}"`); } } exports.opposite = opposite; function setEquals(set1, set2) { if (set1.size !== set2.size) return false; for (const elem of set2) { if (!set1.has(elem)) return false; } return true; } exports.setEquals = setEquals; function setAddAll(setA, iterable) { const newSet = new Set(setA); for (const elem of iterable) { newSet.add(elem); } return newSet; } exports.setAddAll = setAddAll; function setIntersection(setA, setB) { const intersection = new Set(); for (const elem of setB) { if (setA.has(elem)) { intersection.add(elem); } } return intersection; } exports.setIntersection = setIntersection; function setDifference(setA, setB) { const difference = new Set(setA); for (const elem of setB) { difference.delete(elem); } return difference; } exports.setDifference = setDifference; // From https://stackoverflow.com/a/19303725 let seed = 1; let randomValuesForTesting = null; function nextRandom(maxNonInclusive) { if (randomValuesForTesting) { if (randomValuesForTesting.length <= seed - 1) { throw new Error(`BUG: the list of random values for testing was too short. See calls to setRandomValuesForTesting([...]). The list was [${randomValuesForTesting}]. Index being requested is ${seed - 1}`); } const ret = randomValuesForTesting[seed - 1]; seed++; // console.log(`Sending "random" value of "${ret}"`); return ret; } const x = Math.sin(seed++) * 10000; return Math.round((x - Math.floor(x)) * (maxNonInclusive - 1)); // return Math.round(Math.random() * (maxNonInclusive - 1)) } exports.nextRandom = nextRandom; function resetRandomSeed() { seed = 1; } exports.resetRandomSeed = resetRandomSeed; function setRandomValuesForTesting(values) { randomValuesForTesting = values; resetRandomSeed(); } exports.setRandomValuesForTesting = setRandomValuesForTesting; function clearRandomValuesForTesting() { randomValuesForTesting = null; resetRandomSeed(); } exports.clearRandomValuesForTesting = clearRandomValuesForTesting; function getRandomSeed() { return seed; } exports.getRandomSeed = getRandomSeed; /** * A `DEBUGGER` flag in the game source that causes the evaluation to pause. * It works like the * [debugger](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger) * keyword in JavaScript. * * **Note:** the game needs to run in debug mode (`node --inspect-brk path/to/puzzlescript.js` or `npm run play-debug`) * for this flag to have any effect. * * This string can be added to: * * - A Rule. Example: `DEBUGGER [ > player | cat ] -> [ > player | > cat ]` * - A bracket when the condition is updated: `[ > player | cat ] DEBUGGER -> [ > player | > cat ]` * - A bracket when it is evaluated: `[ > player | cat ] -> [ > player | > cat ] DEBUGGER` * - A neighbor when the condition is updated: `[ > player DEBUGGER | cat ] -> [ > player | > cat ]` * - A neighbor when it is evaluated: `[ > player | cat ] -> [ > player | > cat DEBUGGER ]` * - A tile when the condition is updated: `[ > player | DEBUGGER cat ] -> [ > player | > cat ]` * - A tile when it is matched: `[ > player | cat ] -> [ > player | DEBUGGER > cat ]` */ var DEBUG_FLAG; (function (DEBUG_FLAG) { DEBUG_FLAG["BREAKPOINT"] = "DEBUGGER"; /** * Pause when a Cell causes an entry to be removed from the set of matches for this rule/bracket/neighbor/tile */ DEBUG_FLAG["BREAKPOINT_REMOVE"] = "DEBUGGER_REMOVE"; })(DEBUG_FLAG = exports.DEBUG_FLAG || (exports.DEBUG_FLAG = {})); function spritesThatInteractWithPlayer(game) { const playerSprites = game.getPlayer().getSprites(); const interactsWithPlayer = new Set(playerSprites); // Add all the sprites in the same collision layer as the Player for (const playerSprite of interactsWithPlayer) { const collisionLayer = playerSprite.getCollisionLayer(); for (const sprite of game.objects) { if (sprite.getCollisionLayer() === collisionLayer) { interactsWithPlayer.add(sprite); } } } // Add all the winCondition sprites for (const win of game.winConditions) { for (const tile of win.a11yGetTiles()) { for (const sprite of tile.getSprites()) { interactsWithPlayer.add(sprite); } } } // Add all the other sprites that interact with the player for (const rule of game.rules) { for (const sprites of rule.a11yGetConditionSprites()) { if (setIntersection(sprites, interactsWithPlayer).size > 0) { for (const sprite of sprites) { interactsWithPlayer.add(sprite); } } } } // remove the background sprite (even though it transitively interacts) const background = game.getMagicBackgroundSprite(); if (background) { interactsWithPlayer.delete(background); } // remove transparent sprites once the dependecies are found return new Set([...interactsWithPlayer].filter((s) => !s.isTransparent())); } exports.spritesThatInteractWithPlayer = spritesThatInteractWithPlayer; // Webworker message interfaces // Polls until a condition is true function pollingPromise(ms, fn) { return new Promise((resolve) => { const timer = setInterval(() => { const value = fn(); if (value) { clearInterval(timer); resolve(value); } }, ms); }); } exports.pollingPromise = pollingPromise; var MESSAGE_TYPE; (function (MESSAGE_TYPE) { MESSAGE_TYPE["PAUSE"] = "PAUSE"; MESSAGE_TYPE["RESUME"] = "RESUME"; MESSAGE_TYPE["TICK"] = "TICK"; MESSAGE_TYPE["PRESS"] = "PRESS"; MESSAGE_TYPE["CLOSE"] = "CLOSE"; // Event handler events MESSAGE_TYPE["ON_GAME_CHANGE"] = "ON_GAME_CHANGE"; MESSAGE_TYPE["ON_PRESS"] = "ON_PRESS"; MESSAGE_TYPE["ON_MESSAGE"] = "ON_MESSAGE"; MESSAGE_TYPE["ON_MESSAGE_DONE"] = "ON_MESSAGE_DONE"; MESSAGE_TYPE["ON_LEVEL_LOAD"] = "ON_LEVEL_LOAD"; MESSAGE_TYPE["ON_LEVEL_CHANGE"] = "ON_LEVEL_CHANGE"; MESSAGE_TYPE["ON_WIN"] = "ON_WIN"; MESSAGE_TYPE["ON_SOUND"] = "ON_SOUND"; MESSAGE_TYPE["ON_TICK"] = "ON_TICK"; MESSAGE_TYPE["ON_PAUSE"] = "ON_PAUSE"; MESSAGE_TYPE["ON_RESUME"] = "ON_RESUME"; })(MESSAGE_TYPE = exports.MESSAGE_TYPE || (exports.MESSAGE_TYPE = {})); const shouldTick = (metadata, lastTick) => { const now = Date.now(); let minTime = Math.min(metadata.realtimeInterval || 1000, metadata.keyRepeatInterval || 1000, metadata.againInterval || 1000); if (minTime > 100 || Number.isNaN(minTime)) { minTime = .01; } return (now - lastTick) >= (minTime * 1000); }; exports.shouldTick = shouldTick; class EmptyGameEngineHandler { constructor(subHandlers) { this.subHandlers = subHandlers || []; } onGameChange(gameData) { for (const h of this.subHandlers) { h.onGameChange && h.onGameChange(gameData); } } onPress(dir) { for (const h of this.subHandlers) { h.onPress && h.onPress(dir); } } onMessage(msg) { return __awaiter(this, void 0, void 0, function* () { for (const h of this.subHandlers) { h.onMessage && (yield h.onMessage(msg)); } }); } onLevelLoad(level, newLevelSize) { for (const h of this.subHandlers) { h.onLevelLoad && h.onLevelLoad(level, newLevelSize); } } onLevelChange(level, cells, message) { for (const h of this.subHandlers) { h.onLevelChange && h.onLevelChange(level, cells, message); } } onWin() { for (const h of this.subHandlers) { h.onWin && h.onWin(); } } onSound(sound) { return __awaiter(this, void 0, void 0, function* () { for (const h of this.subHandlers) { h.onSound && h.onSound(sound); } }); } onTick(changedCells, checkpoint, hasAgain, a11yMessages) { for (const h of this.subHandlers) { h.onTick && h.onTick(changedCells, checkpoint, hasAgain, a11yMessages); } } onPause() { for (const h of this.subHandlers) { h.onPause && h.onPause(); } } onResume() { for (const h of this.subHandlers) { h.onResume && h.onResume(); } } } exports.EmptyGameEngineHandler = EmptyGameEngineHandler; //# sourceMappingURL=util.js.map