Skip to content

Commit

Permalink
feat: add support for multiple followers (with option for stack/chain) (
Browse files Browse the repository at this point in the history
#170)

adds support for multiple followers, with an option for whether to stack (keep all followers one step behind player) or chain (keep followers one step behind the follower ahead of them in the list of active followers)

also fixes an issue in the hack options injection regex in the test code

BREAKING CHANGE: this changes the `follower` export to `followers`, since it's now a list
  • Loading branch information
seleb authored Sep 5, 2020
1 parent b98d548 commit 675f713
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 26 deletions.
Binary file modified src/__image_snapshots__/follower-test-js-follower-1-snap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
80 changes: 56 additions & 24 deletions src/follower.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,25 @@ import {

export var hackOptions = {
allowFollowerCollision: false, // if true, the player can walk into the follower and talk to them (possible to get stuck this way)
follower: 'a', // id or name of sprite to be the follower; use '' to start without a follower
followers: ['a'], // ids or names of sprites to be followers; use [] to start without a follower
delay: 200, // delay between each follower step (0 is immediate, 400 is twice as slow as normal)
stack: false, // if true, followers stack on top of each other; otherwise, they will form a chain
};

export var follower;
export var followers = [];
var paths = {};

function setFollower(followerName) {
follower = followerName && getImage(followerName, bitsy.sprite);
var follower = followerName && getImage(followerName, bitsy.sprite);
if (!follower) {
throw new Error('Failed to find sprite with id/name "' + followerName + '"');
}
var idx = followers.indexOf(follower);
if (idx > 0) {
followers.splice(idx, 1);
} else {
followers.push(follower);
}
paths[follower.id] = paths[follower.id] || [];
takeStep();
}
Expand All @@ -78,22 +88,28 @@ function takeStep() {
}
walking = true;
setTimeout(() => {
var path = paths[follower.id];
var point = path.shift();
if (point) {
follower.x = point.x;
follower.y = point.y;
follower.room = point.room;
}
walking = false;
if (path.length) {
let takeAnother = false;
followers.forEach(function (follower) {
var path = paths[follower.id];
var point = path.shift();
if (point) {
follower.x = point.x;
follower.y = point.y;
follower.room = point.room;
}
walking = false;
if (path.length) {
takeAnother = true;
}
});
if (takeAnother) {
takeStep();
}
}, hackOptions.delay);
}

after('startExportedGame', function () {
setFollower(hackOptions.follower);
hackOptions.followers.forEach(setFollower);

// remove + add player to sprite list to force rendering them on top of follower
var p = bitsy.sprite[bitsy.playerId];
Expand All @@ -119,7 +135,7 @@ after('update', function () {
return;
}

if (!follower) {
if (!followers.length) {
return;
}

Expand All @@ -146,25 +162,41 @@ after('update', function () {
default:
break;
}
paths[follower.id].push(step);
followers.forEach(function (follower, idx) {
if (idx === 0 || hackOptions.stack) {
paths[follower.id].push(step);
} else {
var prevFollower = followers[idx - 1];
var prev = paths[prevFollower.id];
paths[follower.id].push(prev[prev.length - 2] || {
x: prevFollower.x,
y: prevFollower.y,
room: prevFollower.room,
});
}
});
takeStep();
});

// make follower walk "through" exits
before('movePlayerThroughExit', function (exit) {
if (follower) {
if (followers.length) {
movedFollower = true;
paths[follower.id].push({
x: exit.dest.x,
y: exit.dest.y,
room: exit.dest.room,
followers.forEach(function (follower) {
paths[follower.id].push({
x: exit.dest.x,
y: exit.dest.y,
room: exit.dest.room,
});
});
takeStep();
}
});

function filterFollowing(id) {
return follower === bitsy.sprite[id] ? null : id;
return followers.some(function (follower) {
return follower.id === id;
}) ? null : id;
}

var originalGetSpriteLeft;
Expand Down Expand Up @@ -221,13 +253,13 @@ addDualDialogTag('followerDelay', function (environment, parameters) {
hackOptions.delay = parseInt(parameters[0], 10);
});
addDualDialogTag('followerSync', function () {
if (follower) {
var player = bitsy.player();
var player = bitsy.player();
followers.forEach(function (follower) {
follower.room = player.room;
follower.x = player.x;
follower.y = player.y;
paths[follower.id].length = 0;
}
});
});

before('moveSprites', function () {
Expand Down
110 changes: 110 additions & 0 deletions src/follower.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,83 @@ import {
delay,
} from './test/bitsy';

snapshot, start
} from './test/bitsy';

const multiple = `
# BITSY VERSION 7.2
! ROOM_FORMAT 1
PAL 0
0,82,204
128,159,255
255,255,255
ROOM 0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,a,a,a,a,a,a,a,a,a,a,a,a,a,a,0
0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0
0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0
0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0
0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0
0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0
0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0
0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0
0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0
0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0
0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0
0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0
0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0
0,a,a,a,a,a,a,a,a,a,a,a,a,a,a,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
PAL 0
TIL a
11111111
10000001
10000001
10011001
10011001
10000001
10000001
11111111
SPR A
00011000
00011000
00011000
00111100
01111110
10111101
00100100
00100100
POS 0 4,12
SPR a
00000000
00000000
00001000
00011000
00001000
00001000
00011100
00000000
POS 0 6,12
SPR b
00000000
00000000
00011000
00100100
00001000
00010000
00111100
00000000
POS 0 8,12
`;

test('follower', async () => {
await start({
hacks: ['follower'],
Expand All @@ -19,3 +96,36 @@ test('follower', async () => {
await snapshot();
await end();
});

test('multiple followers (chain)', async () => {
await start({
hacks: [['follower', {
allowFollowerCollision: false,
followers: ['a', 'b'],
delay: 1,
}]],
gamedata: multiple,
});
await press('ArrowLeft');
await snapshot();
await press('ArrowLeft');
await snapshot();
await end();
});

test('multiple followers (stack)', async () => {
await start({
hacks: [['follower', {
allowFollowerCollision: false,
followers: ['a', 'b'],
delay: 1,
stack: true,
}]],
gamedata: multiple,
});
await press('ArrowLeft');
await snapshot();
await press('ArrowLeft');
await snapshot();
await end();
});
4 changes: 2 additions & 2 deletions src/test/bitsy.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export function delay(ms) {
return new Promise((r) => setTimeout(r, ms));
}

// start pupeteer
// start puppeteer
// and configure it for testing a bitsy game
export async function start({
gamedata = '',
Expand Down Expand Up @@ -80,7 +80,7 @@ export async function start({
return `<script>${hackDist[hack.replace(/\s/g, '-')].replace(/\$([0-9]+)/g, '$$$$$1')}</script>`;
}
const [hackStr, options] = hack;
return `<script>${hackDist[hackStr.replace(/\s/g, '-')].replace(/(var hackOptions.*=)[^]*?;/m, `$1 ${JSON.stringify(options, undefined, '\t')};`).replace(/\$([0-9]+)/g, '$$$$$1')}</script>`;
return `<script>${hackDist[hackStr.replace(/\s/g, '-')].replace(/(var hackOptions.*=)[^]*?;$/m, `$1 ${JSON.stringify(options, undefined, '\t')};`).replace(/\$([0-9]+)/g, '$$$$$1')}</script>`;
}).join('\n')
}$2`);
}
Expand Down

0 comments on commit 675f713

Please sign in to comment.