puzzlescript
Version:
Play PuzzleScript games in your terminal!
905 lines (715 loc) • 19 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
/* eslint-env jasmine */
const engine_1 = require("./engine");
const parser_1 = __importDefault(require("./parser/parser"));
const util_1 = require("./util");
function parseEngine(code) {
const { data } = parser_1.default.parse(code);
const engine = new engine_1.LevelEngine(data);
engine.setLevel(0);
return { engine, data };
}
describe('Directions', () => {
beforeEach(() => {
(0, util_1.clearRandomValuesForTesting)();
});
it('"randomly" generates integers', () => {
expect((0, util_1.nextRandom)(4)).toBe(2);
expect((0, util_1.nextRandom)(4)).toBe(3);
expect((0, util_1.nextRandom)(4)).toBe(1);
expect((0, util_1.nextRandom)(4)).toBe(3);
expect((0, util_1.nextRandom)(4)).toBe(2);
expect((0, util_1.nextRandom)(4)).toBe(3);
expect((0, util_1.nextRandom)(4)).toBe(3);
expect((0, util_1.nextRandom)(4)).toBe(2);
expect((0, util_1.nextRandom)(4)).toBe(1);
expect((0, util_1.nextRandom)(4)).toBe(2);
expect((0, util_1.nextRandom)(4)).toBe(0);
expect((0, util_1.nextRandom)(4)).toBe(1);
expect((0, util_1.nextRandom)(4)).toBe(2);
expect((0, util_1.nextRandom)(4)).toBe(0);
expect((0, util_1.nextRandom)(4)).toBe(3);
expect((0, util_1.nextRandom)(4)).toBe(3);
expect((0, util_1.nextRandom)(4)).toBe(0);
expect((0, util_1.nextRandom)(4)).toBe(0);
expect((0, util_1.nextRandom)(4)).toBe(2);
expect((0, util_1.nextRandom)(4)).toBe(1);
expect((0, util_1.nextRandom)(4)).toBe(2);
expect((0, util_1.nextRandom)(4)).toBe(1);
expect((0, util_1.nextRandom)(4)).toBe(2);
expect((0, util_1.nextRandom)(4)).toBe(1);
expect((0, util_1.nextRandom)(4)).toBe(1);
expect((0, util_1.nextRandom)(4)).toBe(2);
expect((0, util_1.nextRandom)(4)).toBe(2);
expect((0, util_1.nextRandom)(4)).toBe(0);
expect((0, util_1.nextRandom)(4)).toBe(2);
expect((0, util_1.nextRandom)(4)).toBe(2);
expect((0, util_1.nextRandom)(4)).toBe(2);
expect((0, util_1.nextRandom)(4)).toBe(1);
expect((0, util_1.nextRandom)(4)).toBe(0);
expect((0, util_1.nextRandom)(4)).toBe(2);
expect((0, util_1.nextRandom)(4)).toBe(1);
expect((0, util_1.nextRandom)(4)).toBe(1);
});
it('Marks a sprite when it wants to move', () => {
const { engine, data } = parseEngine(`
title foo
realtime_interval .01
========
OBJECTS
========
Background
green
Player
blue
=======
LEGEND
=======
. = Background
P = Player
================
COLLISIONLAYERS
================
Background
Player
===
RULES
===
RIGHT [ Player ] -> [ > Player ]
=======
LEVELS
=======
P.
`);
const player = data.getSpriteByName('player');
const { changedCells } = engine.tickUpdateCells();
expect(engine.toSnapshot()).toMatchSnapshot();
// Once these sprites actually move, we neet to separate engine.tick() into multiple steps:
// 1. Update all the cells with new sprites and the wantsToMove directions
// 2. Move all the sprites that want to move
// 3. Late: Update all the cells with new sprites ...
// 4. Late: Move all the sprites that want to move
// next tick for all the AGAIN rules
expect(engine.getCurrentLevel().getCells()[0][0].getWantsToMove(player)).toBe('RIGHT');
// Ensure only 1 cell was marked for update
expect(changedCells.size).toBe(1);
});
it('Moves the sprite', () => {
const { engine, data } = parseEngine(`
title foo
========
OBJECTS
========
Background
green
Player
blue
=======
LEGEND
=======
. = Background
P = Player
================
COLLISIONLAYERS
================
Background
Player
===
RULES
===
RIGHT [ Player ] -> [ > Player ]
=======
LEVELS
=======
P.
`);
const { changedCells } = engine.tick();
// expect(engine.toSnapshot()).toMatchSnapshot()
const player = data.getSpriteByName('player');
expect(engine.getCurrentLevel().getCells()[0][1].getSpritesAsSet().has(player)).toBe(true);
// Ensure both cells were marked for re-rendering
expect(changedCells.size).toBe(2);
expect(changedCells).toContain(engine.getCurrentLevel().getCells()[0][0]);
expect(changedCells).toContain(engine.getCurrentLevel().getCells()[0][1]);
});
it('Does not move the sprite if it collides with a sprite in another cell (same collisionlayer)', () => {
const { engine, data } = parseEngine(`
title foo
========
OBJECTS
========
Background
green
Player
blue
Wall
brown
=======
LEGEND
=======
. = Background
P = Player
W = Wall
================
COLLISIONLAYERS
================
Background
Player, Wall
===
RULES
===
RIGHT [ Player ] -> [ > Player ]
=======
LEVELS
=======
PW
`);
engine.tick();
// expect(engine.toSnapshot()).toMatchSnapshot()
const player = data.getSpriteByName('player');
expect(engine.getCurrentLevel().getCells()[0][1].getSpritesAsSet().has(player)).toBe(false);
expect(engine.getCurrentLevel().getCells()[0][0].getSpritesAsSet().has(player)).toBe(true);
// Make sure the wantsToMove flag is cleared
expect(engine.getCurrentLevel().getCells()[0][0].getWantsToMove(player)).toBe('STATIONARY');
// nothing actually changed visually
// expect(changedCells.size).toBe(0)
});
it('Does not move the sprite when ACTION is added to it', () => {
const { engine, data } = parseEngine(`
title foo
========
OBJECTS
========
Background
green
Player
blue
=======
LEGEND
=======
. = Background
P = Player
================
COLLISIONLAYERS
================
Background
Player
===
RULES
===
[ Player ] -> [ ACTION Player ]
=======
LEVELS
=======
P.
`);
engine.tick();
// expect(engine.toSnapshot()).toMatchSnapshot()
const player = data.getSpriteByName('player');
expect(engine.getCurrentLevel().getCells()[0][0].getSpritesAsSet().has(player)).toBe(true);
// Make sure the wantsToMove flag is cleared
expect(engine.getCurrentLevel().getCells()[0][0].getWantsToMove(player)).toBe('STATIONARY');
// nothing actually changed visually
// expect(changedCells.size).toBe(0)
});
it('Randomly decides whether to add the sprite using "RANDOM" in a bracket', () => {
const { engine, data } = parseEngine(`
title foo
========
OBJECTS
========
Background
green
Player
blue
one
white black
00000
00000
00100
00000
00000
two
white black
00000
01000
00000
00010
00000
three
white black
00000
01000
00100
00010
00000
=======
LEGEND
=======
. = Background
P = Player
DieSide = one OR two OR three
================
COLLISIONLAYERS
================
Background
Player
DieSide
===
RULES
===
(set direction so it is only evaluated once)
LEFT [ Background NO DieSide ] -> [ Background RANDOM DieSide ]
=======
LEVELS
=======
P.
`);
(0, util_1.setRandomValuesForTesting)([0, 1]);
engine.tick();
expect((0, util_1.getRandomSeed)()).toBe(2 + 1); // Add one because the background sprite was added to the level? (weird)
// expect(engine.toSnapshot()).toMatchSnapshot()
const two = data.getSpriteByName('two');
const three = data.getSpriteByName('three');
// Check that one popped up (because we set the "random" values above)
// Because the OR is stuck into a set, rather than a list, the 1st item is "three", not "one"
const threeCells = [...three.getCellsThatMatch()];
expect(threeCells.length).toBe(1);
expect(engine.getCurrentLevel().getCells()[0][1].getSpritesAsSet().has(two /*three*/)).toBe(true);
});
it('Moves the sprite in a "random" direction using "RANDOMDIR" in a bracket', () => {
const { engine, data } = parseEngine(`
title foo
========
OBJECTS
========
Background
green
Player
blue
=======
LEGEND
=======
. = Background
P = Player
================
COLLISIONLAYERS
================
Background
Player
===
RULES
===
RIGHT [ Player ] -> [ RANDOMDIR Player ]
=======
LEVELS
=======
.....
.....
..P..
.....
.....
`);
(0, util_1.setRandomValuesForTesting)([2, 1]);
const { changedCells } = engine.tick();
// expect(engine.toSnapshot()).toMatchSnapshot()
const player = data.getSpriteByName('player');
expect(engine.getCurrentLevel().getCells()[2][2].getSpritesAsSet().has(player)).toBe(false);
// Check that the player is around thir previous location
let playerCells = [...player.getCellsThatMatch()];
let playerCell = playerCells[0];
expect(playerCells.length).toBe(1);
expect(engine.getCurrentLevel().getCells()[playerCell.rowIndex][playerCell.colIndex].getSpritesAsSet().has(player)).toBe(true);
// Ensure 2 cells were marked for re-rendering
expect(changedCells.size).toBe(2);
expect(changedCells).toContain(engine.getCurrentLevel().getCells()[2][2]);
expect(changedCells).toContain(engine.getCurrentLevel().getCells()[playerCell.rowIndex][playerCell.colIndex]);
engine.tick();
// Check that the player is no longer in the spot they were
expect(engine.getCurrentLevel().getCells()[playerCell.rowIndex][playerCell.colIndex].getSpritesAsSet().has(player)).toBe(false);
// Check that the player is around thir previous location
playerCells = [...player.getCellsThatMatch()];
playerCell = playerCells[0];
expect(playerCells.length).toBe(1);
expect(engine.getCurrentLevel().getCells()[playerCell.rowIndex][playerCell.colIndex].getSpritesAsSet().has(player)).toBe(true);
});
it('supports STATIONARY modifier (simple)', () => {
const { engine, data } = parseEngine(`
title foo
========
OBJECTS
========
background
green
player
blue
incorrect
red
=======
LEGEND
=======
. = Background
P = player
================
COLLISIONLAYERS
================
Background
player
incorrect
===
RULES
===
RIGHT [ player ] -> [ > player ]
[ STATIONARY player ] -> [ incorrect ]
=======
LEVELS
=======
P.
`);
const player = data.getSpriteByName('player');
const incorrect = data.getSpriteByName('incorrect');
engine.tick();
const playerCells = [...player.getCellsThatMatch()];
const playerCell = playerCells[0];
const incorrectCells = [...incorrect.getCellsThatMatch()];
expect(incorrectCells.length).toBe(0);
expect(playerCells.length).toBe(1);
expect(playerCell.rowIndex).toBe(0);
expect(playerCell.colIndex).toBe(1);
});
it('supports STATIONARY modifier with other sprites that are added', () => {
const { engine, data } = parseEngine(`
title foo
========
OBJECTS
========
Background
green
player
blue
cooldown
transparent
=======
LEGEND
=======
. = Background
P = player
================
COLLISIONLAYERS
================
Background
player
cooldown
===
RULES
===
RIGHT [ STATIONARY player NO cooldown ] -> [ > player > cooldown ]
=======
LEVELS
=======
...
.P.
...
`);
const player = data.getSpriteByName('player');
const cooldown = data.getSpriteByName('cooldown');
engine.tick();
const playerCells = [...player.getCellsThatMatch()];
const playerCell = playerCells[0];
expect(player.getCellsThatMatch().size).toBe(1);
expect(playerCell.rowIndex).toBe(1);
expect(playerCell.colIndex).toBe(2);
// Check that there REALLY is only 1 Player sprite
expect(engine.getCurrentLevel().getCells()[0][1].getSpritesAsSet().has(player)).toBe(false);
expect(engine.getCurrentLevel().getCells()[1][0].getSpritesAsSet().has(player)).toBe(false);
expect(engine.getCurrentLevel().getCells()[2][1].getSpritesAsSet().has(player)).toBe(false);
// Check that the cooldown sprite was also added
expect(engine.getCurrentLevel().getCells()[1][2].getSpritesAsSet().has(cooldown)).toBe(true);
});
it('supports setting STATIONARY so sprites do not move', () => {
const { engine, data } = parseEngine(`
title foo
========
OBJECTS
========
Background
green
player
blue
=======
LEGEND
=======
. = Background
P = player
================
COLLISIONLAYERS
================
Background
player
===
RULES
===
RIGHT [ STATIONARY player ] -> [ > player ]
[ > player ] -> [ STATIONARY player ]
=======
LEVELS
=======
P.
`);
const player = data.getSpriteByName('player');
engine.tick();
const playerCells = [...player.getCellsThatMatch()];
const playerCell = playerCells[0];
expect(player.getCellsThatMatch().size).toBe(1);
expect(playerCell.rowIndex).toBe(0);
expect(playerCell.colIndex).toBe(0);
// Check that there REALLY is only 1 Player sprite
expect(engine.getCurrentLevel().getCells()[0][0].getSpritesAsSet().has(player)).toBe(true);
expect(engine.getCurrentLevel().getCells()[0][1].getSpritesAsSet().has(player)).toBe(false);
});
it('puts a top hat on the player (beam islands)', () => {
const { engine, data } = parseEngine(`
title foo
========
OBJECTS
========
Background
green
player
yellow
playertop
yellow
=======
LEGEND
=======
. = Background
P = player
================
COLLISIONLAYERS
================
Background
player
playertop
===
RULES
===
UP [ player | ] -> [ player | playertop ]
=======
LEVELS
=======
.
P
`);
const playerTop = data.getSpriteByName('playertop');
engine.tick();
const playerTopCells = [...playerTop.getCellsThatMatch()];
const playerTopCell = playerTopCells[0];
expect(playerTop.getCellsThatMatch().size).toBe(1);
expect(playerTopCell.rowIndex).toBe(0);
expect(playerTopCell.colIndex).toBe(0);
// Check that there REALLY is only 1 Player sprite
expect(engine.getCurrentLevel().getCells()[0][0].getSpritesAsSet().has(playerTop)).toBe(true);
});
it('Supports trivial tile negation', () => {
const { engine, data } = parseEngine(`
title foo
========
OBJECTS
========
Background
green
player
yellow
culprit
transparent
wrong
transparent
correct
transparent
=======
LEGEND
=======
. = Background
Z = culprit
================
COLLISIONLAYERS
================
Background
player
culprit
wrong
correct
===
RULES
===
[ NO culprit ] -> [ wrong ]
[ NO player ] -> [ correct ]
=======
LEVELS
=======
Z
`);
const wrong = data.getSpriteByName('wrong');
const correct = data.getSpriteByName('correct');
engine.tick();
expect(wrong.getCellsThatMatch().size).toBe(0);
expect(correct.getCellsThatMatch().size).toBe(1);
});
it('Supports tile negation (slightly more complicated)', () => {
const { engine, data } = parseEngine(`
title foo
=========
OBJECTS
=========
Background
blue
Player
#f7e26b #000000
01010
.000.
.0.0.
.....
.....
temp E
Transparent yellow
....1
....1
....1
....1
....1
wrong
Transparent red
1...1
.1.1.
..1..
.1.1.
1...1
========
LEGEND
========
@ = Player
. = background
========
SOUNDS
========
=================
COLLISIONLAYERS
=================
Background
temp
wrong
Player
=======
RULES
=======
( Preparation )
[ Player ] -> [ temp ]
(This rule is the culprit)
RIGHT [ NO temp ] -> [ wrong ]
===============
WINCONDITIONS
===============
========
LEVELS
========
@
`);
const wrong = data.getSpriteByName('wrong');
engine.tick();
expect(wrong.getCellsThatMatch().size).toBe(0);
});
it('Uses the absolute direction when moving a sprite, not the relative one in the rule (e.g. "[ < player ]" )', () => {
const { engine, data } = parseEngine(`
title foo
========
OBJECTS
========
Background
green
Player
blue
=======
LEGEND
=======
. = Background
P = Player
================
COLLISIONLAYERS
================
Background
Player
===
RULES
===
LEFT [ Player ] -> [ ^ Player ]
=======
LEVELS
=======
P
.
`);
const { changedCells } = engine.tickUpdateCells();
// expect(engine.toSnapshot()).toMatchSnapshot()
const player = data.getSpriteByName('player');
expect(engine.getCurrentLevel().getCells()[1][0].getSpritesAsSet().has(player)).toBe(false);
// Check that the player is around thir previous location
let playerCells = [...player.getCellsThatMatch()];
expect(playerCells.length).toBe(1);
expect(engine.getCurrentLevel().getCells()[0][0].getSpritesAsSet().has(player)).toBe(true);
expect(engine.getCurrentLevel().getCells()[0][0].getWantsToMove(player)).toBe('DOWN');
engine.tickMoveSprites(changedCells);
// Check that the player is no longer in the spot they were
playerCells = [...player.getCellsThatMatch()];
expect(playerCells.length).toBe(1);
expect(engine.getCurrentLevel().getCells()[1][0].getSpritesAsSet().has(player)).toBe(true);
});
it('removes the match when an OR tile loses its direction', () => {
const { engine, data } = parseEngine(`
title foo
========
OBJECTS
========
Background
gray
Player
yellow
Crate
brown
Wrong
red
=======
LEGEND
=======
. = Background
X = Player AND Crate
Movable = Player OR Crate
================
COLLISIONLAYERS
================
Background
Player
Crate
Wrong
===
RULES
===
RIGHT [ Player ] -> [ > Player ]
RIGHT [ > Player ] -> [ Player ] (remove the wantsToMove on the Player. The next rule should no longer match )
RIGHT [ > Movable ] -> [ Wrong ]
=======
LEVELS
=======
X.
`);
engine.tick();
// expect(engine.toSnapshot()).toMatchSnapshot()
const wrong = data.getSpriteByName('Wrong');
expect(engine.getCurrentLevel().getCells()[0][0].getSpritesAsSet().has(wrong)).toBe(false);
expect(wrong.getCellsThatMatch().size).toBe(0);
});
});
//# sourceMappingURL=directions.spec.js.map