mirror of
https://github.com/Terncode/pixel.horse.git
synced 2026-09-25 22:25:53 +02:00
1064 lines
28 KiB
TypeScript
1064 lines
28 KiB
TypeScript
import { expect } from 'chai';
|
|
import { useFakeTimers, SinonFakeTimers, SinonStub, stub, assert } from 'sinon';
|
|
import {
|
|
createClient, createIgnorePlayer, findClientByEntityId, setEntityExpression, interactWith,
|
|
cancelEntityExpression, canPerformAction, createClientAndPony, addIgnore, turnHead, boop, stand,
|
|
sit, lie, fly, expressionAction, isIgnored, resetClientUpdates, holdItem, unholdItem, holdToy, unholdToy
|
|
} from '../../server/playerUtils';
|
|
import { mockClient, clientPony, serverEntity, genObjectId, setupCollider } from '../mocks';
|
|
import { IClient } from '../../server/serverInterfaces';
|
|
import { createFunctionWithPromiseHandler } from '../lib';
|
|
import { parseExpression } from '../../common/expressionUtils';
|
|
import { encodeExpression, decodeExpression, EMPTY_EXPRESSION } from '../../common/encoders/expressionEncoder';
|
|
import { EXPRESSION_TIMEOUT } from '../../common/constants';
|
|
import { CharacterState } from '../../common/adminInterfaces';
|
|
import { Action, PonyOptions, EntityState, UpdateFlags } from '../../common/interfaces';
|
|
import { createServerRegion } from '../../server/serverRegion';
|
|
import { rect } from '../../common/rect';
|
|
import { CounterService } from '../../server/services/counter';
|
|
import { createCamera } from '../../common/camera';
|
|
import { getRegion } from '../../common/worldMap';
|
|
import { createServerMap } from '../../server/serverMap';
|
|
import { createBinaryWriter, getWriterBuffer } from 'ag-sockets';
|
|
import { sendAction } from '../../server/entityUtils';
|
|
import { updateColliders } from '../common/collision.spec';
|
|
|
|
describe('playerUtils', () => {
|
|
const def = { x: 0, y: 0, vx: 0, vy: 0, action: 0, playerState: 0, options: undefined };
|
|
|
|
let client: IClient;
|
|
|
|
beforeEach(() => {
|
|
client = mockClient();
|
|
});
|
|
|
|
describe('isIgnored()', () => {
|
|
it('returns true if target account id is on ignored list', () => {
|
|
expect(isIgnored({ accountId: 'foo' } as any, { ignores: new Set(['foo']) } as any)).true;
|
|
});
|
|
|
|
it('returns false if target account id is not on ignored list', () => {
|
|
expect(isIgnored({ accountId: 'foo' } as any, { ignores: new Set(['bar']) } as any)).false;
|
|
});
|
|
|
|
it('returns false if account ignore list is empty', () => {
|
|
expect(isIgnored({ accountId: 'foo' } as any, { ignores: new Set() } as any)).false;
|
|
});
|
|
|
|
it('returns false if account does not have ignore list', () => {
|
|
expect(isIgnored({ accountId: 'foo' } as any, { ignores: new Set() } as any)).false;
|
|
});
|
|
});
|
|
|
|
describe('createClientAndPony()', () => {
|
|
it('sets up client and pony', () => {
|
|
const account = { _id: genObjectId() };
|
|
const character = { _id: genObjectId(), name: 'Foo' };
|
|
const client = {
|
|
tokenData: { account, character },
|
|
} as any;
|
|
const map = { spawnArea: rect(0, 0, 0, 0) };
|
|
const world = {
|
|
getMainMap: () => map,
|
|
getMap: () => map,
|
|
isColliding: stub(),
|
|
};
|
|
|
|
createClientAndPony(client, [], [], { name: 'test' } as any, world as any, new CounterService<CharacterState>(1));
|
|
|
|
// ...
|
|
});
|
|
});
|
|
|
|
describe('createClient()', () => {
|
|
let clock: SinonFakeTimers;
|
|
|
|
beforeEach(() => {
|
|
clock = useFakeTimers();
|
|
clock.setSystemTime(123);
|
|
});
|
|
|
|
afterEach(() => {
|
|
clock.restore();
|
|
});
|
|
|
|
it('initializes client fields', () => {
|
|
const originalRequest = { headers: { 'user-agent': 'test' } };
|
|
const client = { originalRequest } as any;
|
|
const account = { _id: genObjectId(), _account: 1, name: 'Foo' } as any;
|
|
const character = { _id: genObjectId(), _character: 1, name: 'Im :apple:' } as any;
|
|
const pony = { _pony: 1, x: 10, y: 20 } as any;
|
|
const reporter = { _reporter: 1 } as any;
|
|
const origin = { ip: '', country: 'XY' };
|
|
const map = {} as any;
|
|
|
|
const result = createClient(client, account, [], [], character, pony, map, reporter, origin);
|
|
|
|
expect(result).equal(client);
|
|
expect(result).eql({
|
|
accountId: account._id.toString(),
|
|
accountName: 'Foo',
|
|
characterId: character._id.toString(),
|
|
characterName: 'Im 🍎',
|
|
ignores: new Set(),
|
|
hides: new Set(),
|
|
permaHides: new Set(),
|
|
friends: new Set(),
|
|
friendsCRC: undefined,
|
|
accountSettings: {},
|
|
originalRequest,
|
|
supporterLevel: 0,
|
|
isMod: false,
|
|
userAgent: 'test',
|
|
reporter,
|
|
account,
|
|
character,
|
|
ip: '',
|
|
isMobile: false,
|
|
map,
|
|
isSwitchingMap: false,
|
|
pony,
|
|
notifications: [],
|
|
updateQueue: createBinaryWriter(128),
|
|
regionUpdates: [],
|
|
saysQueue: [],
|
|
unsubscribes: [],
|
|
subscribes: [],
|
|
regions: [],
|
|
camera: Object.assign(createCamera(), { w: 800, h: 600 }),
|
|
lastSays: [],
|
|
lastSwap: 0,
|
|
shadowed: false,
|
|
country: 'XY',
|
|
safeX: 10,
|
|
safeY: 20,
|
|
lastPacket: 123,
|
|
lastBoopOrKissAction: 0,
|
|
lastExpressionAction: 0,
|
|
lastX: 10,
|
|
lastY: 20,
|
|
lastTime: 0,
|
|
lastVX: 0,
|
|
lastVY: 0,
|
|
lastCameraX: 0,
|
|
lastCameraY: 0,
|
|
lastCameraW: 0,
|
|
lastCameraH: 0,
|
|
lastMapSwitch: 0,
|
|
sitCount: 0,
|
|
lastSitX: 0,
|
|
lastSitY: 0,
|
|
lastSitTime: 0,
|
|
lastMapLoadOrSave: 0,
|
|
positions: [],
|
|
});
|
|
});
|
|
|
|
it('sets shadowed field if is shadowed', () => {
|
|
const originalRequest = { headers: { 'user-agent': 'test' } };
|
|
const client = { originalRequest } as any;
|
|
const account = { _id: genObjectId(), shadow: -1 } as any;
|
|
const character = { _id: genObjectId() } as any;
|
|
const pony = {} as any;
|
|
const reporter = {} as any;
|
|
const map = {} as any;
|
|
|
|
const result = createClient(client, account, [], [], character, pony, map, reporter, undefined);
|
|
|
|
expect(result.shadowed).true;
|
|
expect(result.country).equal('??');
|
|
});
|
|
});
|
|
|
|
describe('resetClientUpdates()', () => {
|
|
it('resets all queues', () => {
|
|
const client = mockClient();
|
|
client.updateQueue.offset = 100;
|
|
client.regionUpdates.push({} as any);
|
|
client.saysQueue.push({} as any);
|
|
client.unsubscribes.push({} as any);
|
|
client.subscribes.push({} as any);
|
|
|
|
resetClientUpdates(client);
|
|
|
|
expect(client.updateQueue.offset).equal(0);
|
|
expect(client.regionUpdates).eql([]);
|
|
expect(client.saysQueue).eql([]);
|
|
expect(client.unsubscribes).eql([]);
|
|
expect(client.subscribes).eql([]);
|
|
});
|
|
});
|
|
|
|
describe('ignorePlayer()', () => {
|
|
let ignorePlayer: (client: IClient, target: IClient, ignored: boolean) => void;
|
|
let updateAccount: SinonStub;
|
|
|
|
beforeEach(() => {
|
|
updateAccount = stub().resolves();
|
|
ignorePlayer = createFunctionWithPromiseHandler(createIgnorePlayer, updateAccount);
|
|
});
|
|
|
|
it('adds client to targets ignores list', async () => {
|
|
const client = mockClient();
|
|
const target = mockClient();
|
|
|
|
await ignorePlayer(client, target, true);
|
|
|
|
assert.calledWithMatch(updateAccount, target.accountId, { $push: { ignores: client.accountId } });
|
|
});
|
|
|
|
it('removes client from targets ignores list', async () => {
|
|
const client = mockClient();
|
|
const target = mockClient();
|
|
addIgnore(target, client.accountId);
|
|
|
|
await ignorePlayer(client, target, false);
|
|
|
|
assert.calledWithMatch(updateAccount, target.accountId, { $pull: { ignores: client.accountId } });
|
|
});
|
|
|
|
it('does nothing if already ignored', async () => {
|
|
const client = mockClient();
|
|
const target = mockClient();
|
|
addIgnore(target, client.accountId);
|
|
|
|
await ignorePlayer(client, target, true);
|
|
|
|
assert.notCalled(updateAccount);
|
|
});
|
|
|
|
it('does nothing if already unignored', async () => {
|
|
const client = mockClient();
|
|
const target = mockClient();
|
|
|
|
await ignorePlayer(client, target, false);
|
|
|
|
assert.notCalled(updateAccount);
|
|
});
|
|
|
|
it('does nothing if called for self', async () => {
|
|
const client = mockClient();
|
|
|
|
await ignorePlayer(client, client, true);
|
|
|
|
assert.notCalled(updateAccount);
|
|
});
|
|
|
|
it('adds client to targets account instance ignores list', async () => {
|
|
const client = mockClient();
|
|
const target = mockClient();
|
|
target.account.ignores = undefined;
|
|
|
|
await ignorePlayer(client, target, true);
|
|
|
|
expect(target.account.ignores).eql([client.accountId]);
|
|
});
|
|
|
|
it('removes client from targets account instance ignores list', async () => {
|
|
const client = mockClient();
|
|
const target = mockClient();
|
|
addIgnore(target, client.accountId);
|
|
|
|
await ignorePlayer(client, target, false);
|
|
|
|
expect(target.account.ignores).eql([]);
|
|
});
|
|
|
|
it('sends target player state update to client', async () => {
|
|
const client = mockClient();
|
|
const target = mockClient();
|
|
target.pony.id = 123;
|
|
|
|
await ignorePlayer(client, target, true);
|
|
|
|
expect(Array.from(getWriterBuffer(client.updateQueue)))
|
|
.eql([2, 4, 0, 0, 0, 0, 123, 1]);
|
|
});
|
|
});
|
|
|
|
describe('findClientByEntityId()', () => {
|
|
it('returns client from selected pony', () => {
|
|
const self = mockClient();
|
|
const client = mockClient();
|
|
self.selected = client.pony;
|
|
|
|
expect(findClientByEntityId(self, client.pony.id)).equal(client);
|
|
});
|
|
|
|
it('returns client from party clients', () => {
|
|
const self = mockClient();
|
|
const client = mockClient();
|
|
self.party = {
|
|
id: '',
|
|
clients: [client],
|
|
leader: client,
|
|
pending: [],
|
|
};
|
|
|
|
expect(findClientByEntityId(self, client.pony.id)).equal(client);
|
|
});
|
|
|
|
it('returns client from party pending', () => {
|
|
const self = mockClient();
|
|
const client = mockClient();
|
|
self.party = {
|
|
id: '',
|
|
clients: [],
|
|
leader: client,
|
|
pending: [{ client, notificationId: 0 }],
|
|
};
|
|
|
|
expect(findClientByEntityId(self, client.pony.id)).equal(client);
|
|
});
|
|
|
|
it('returns undefined if not found in party', () => {
|
|
const self = mockClient();
|
|
const client = mockClient();
|
|
self.party = {
|
|
id: '',
|
|
clients: [],
|
|
leader: mockClient(),
|
|
pending: [],
|
|
};
|
|
|
|
expect(findClientByEntityId(self, client.pony.id)).undefined;
|
|
});
|
|
|
|
it('returns client from notifications', () => {
|
|
const self = mockClient();
|
|
const client = mockClient();
|
|
self.notifications = [
|
|
{ id: 0, name: 'name', message: '', entityId: client.pony.id, sender: client },
|
|
];
|
|
|
|
expect(findClientByEntityId(self, client.pony.id)).equal(client);
|
|
});
|
|
|
|
it('returns undefined if not found', () => {
|
|
const self = mockClient();
|
|
|
|
expect(findClientByEntityId(self, 1)).undefined;
|
|
});
|
|
});
|
|
|
|
describe('cancelEntityExpression()', () => {
|
|
it('cancels expression', () => {
|
|
const entity = serverEntity(0);
|
|
entity.options = { expr: 1234 } as PonyOptions;
|
|
entity.exprCancellable = true;
|
|
entity.exprPermanent = decodeExpression(1111);
|
|
|
|
cancelEntityExpression(entity);
|
|
|
|
expect(entity.options).eql({ expr: 1111 });
|
|
expect(entity.exprCancellable).false;
|
|
});
|
|
|
|
it('does nothing if entity does not have cancellable expression', () => {
|
|
const entity = serverEntity(0);
|
|
entity.options = { expr: 1234 } as PonyOptions;
|
|
|
|
cancelEntityExpression(entity);
|
|
|
|
expect(entity.options).eql({ expr: 1234 });
|
|
});
|
|
});
|
|
|
|
describe('setEntityExpression()', () => {
|
|
let clock: SinonFakeTimers;
|
|
|
|
beforeEach(() => {
|
|
clock = useFakeTimers();
|
|
});
|
|
|
|
afterEach(() => {
|
|
clock.restore();
|
|
});
|
|
|
|
it('sets expression on pony options', () => {
|
|
const pony = clientPony();
|
|
const expression = parseExpression(':)');
|
|
|
|
setEntityExpression(pony, expression);
|
|
|
|
expect(pony.options!.expr).equal(encodeExpression(expression));
|
|
});
|
|
|
|
it('sets empty expression by default', () => {
|
|
const pony = clientPony();
|
|
|
|
setEntityExpression(pony, undefined);
|
|
|
|
expect(pony.options!.expr).equal(encodeExpression(undefined));
|
|
});
|
|
|
|
it('uses permanent expression if no expression provided', () => {
|
|
const pony = clientPony();
|
|
const expression = parseExpression(':)');
|
|
pony.exprPermanent = expression;
|
|
|
|
setEntityExpression(pony, undefined);
|
|
|
|
expect(pony.options!.expr).equal(encodeExpression(expression));
|
|
});
|
|
|
|
it('sets default expression timeout', () => {
|
|
const pony = clientPony();
|
|
clock.setSystemTime(1234);
|
|
|
|
setEntityExpression(pony, parseExpression(':)'));
|
|
|
|
expect(pony.exprTimeout).equal(1234 + EXPRESSION_TIMEOUT);
|
|
});
|
|
|
|
it('sets custom expression timeout if provided', () => {
|
|
const pony = clientPony();
|
|
clock.setSystemTime(1234);
|
|
|
|
setEntityExpression(pony, parseExpression(':)'), 123);
|
|
|
|
expect(pony.exprTimeout).equal(1234 + 123);
|
|
});
|
|
|
|
it('unsets expression timeout if given 0 for timeout', () => {
|
|
const pony = clientPony();
|
|
pony.exprTimeout = 1234;
|
|
|
|
setEntityExpression(pony, parseExpression(':)'), 0);
|
|
|
|
expect(pony.exprTimeout).undefined;
|
|
});
|
|
|
|
it('sets expression cancellable flag', () => {
|
|
const pony = clientPony();
|
|
|
|
setEntityExpression(pony, parseExpression(':)'), 123, true);
|
|
|
|
expect(pony.exprCancellable).true;
|
|
});
|
|
|
|
it('sets expression cancellable flag', () => {
|
|
const pony = clientPony();
|
|
|
|
setEntityExpression(pony, parseExpression(':)'), 123, false);
|
|
|
|
expect(pony.exprCancellable).false;
|
|
});
|
|
|
|
it('adds expression to region updates', () => {
|
|
const entity = clientPony();
|
|
const region = createServerRegion(0, 0);
|
|
entity.region = region;
|
|
|
|
setEntityExpression(entity, parseExpression(':)'), 123, false);
|
|
|
|
expect(region.entityUpdates).eql([
|
|
{ entity, flags: UpdateFlags.Expression, x: 0, y: 0, vx: 0, vy: 0, action: 0, playerState: 0, options: undefined },
|
|
]);
|
|
});
|
|
|
|
it('sends expression update to user instead of region updates for shadowed client', () => {
|
|
const pony = clientPony();
|
|
pony.region = createServerRegion(0, 0);
|
|
pony.client!.shadowed = true;
|
|
pony.id = 123;
|
|
|
|
setEntityExpression(pony, parseExpression(':)'), 123, false);
|
|
|
|
expect(Array.from(getWriterBuffer(pony.client!.updateQueue)))
|
|
.eql([2, 0, 8, 0, 0, 0, 123, 0, 0, 4, 32]);
|
|
});
|
|
});
|
|
|
|
describe('interactWith()', () => {
|
|
it('calls interact method on target', () => {
|
|
const client = mockClient();
|
|
const interact = stub();
|
|
const target = serverEntity(1, 0, 0, 1, { interact });
|
|
|
|
interactWith(client, target);
|
|
|
|
assert.calledWith(interact, target, client);
|
|
});
|
|
|
|
it('calls interact method on target if within range', () => {
|
|
const client = mockClient();
|
|
const interact = stub();
|
|
const target = serverEntity(1, 10, 10, 1, { interact, interactRange: 5 });
|
|
client.pony.x = 9;
|
|
client.pony.y = 11;
|
|
|
|
interactWith(client, target);
|
|
|
|
assert.calledWith(interact, target, client);
|
|
});
|
|
|
|
it('does not call interact if out of range', () => {
|
|
const client = mockClient();
|
|
const interact = stub();
|
|
const target = serverEntity(1, 10, 10, 1, { interact, interactRange: 5 });
|
|
client.pony.x = 2;
|
|
client.pony.y = 1;
|
|
|
|
interactWith(client, target);
|
|
|
|
assert.notCalled(interact);
|
|
});
|
|
|
|
it('does nothing for undefined entity', () => {
|
|
interactWith(mockClient(), undefined);
|
|
});
|
|
|
|
it('does nothing for entity without interact', () => {
|
|
interactWith(mockClient(), serverEntity(1));
|
|
});
|
|
});
|
|
|
|
describe('canPerformAction()', () => {
|
|
it('returns true if last action date is below current time', () => {
|
|
expect(canPerformAction(mockClient({ lastBoopOrKissAction: 1234 }))).true;
|
|
});
|
|
|
|
it('returns false if last action date is ahead or current time', () => {
|
|
expect(canPerformAction(mockClient({ lastExpressionAction: Date.now() + 1000 }))).false;
|
|
});
|
|
});
|
|
|
|
describe('sendAction()', () => {
|
|
it('adds action to region', () => {
|
|
const entity = serverEntity(1);
|
|
const region = createServerRegion(0, 0);
|
|
entity.region = region;
|
|
|
|
sendAction(entity, Action.Boop);
|
|
|
|
expect(region.entityUpdates).eql([
|
|
{
|
|
entity, flags: UpdateFlags.Action, x: 0, y: 0, vx: 0, vy: 0, action: Action.Boop,
|
|
playerState: 0, options: undefined,
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('sends only to entity client if shadowed', () => {
|
|
const client = mockClient();
|
|
client.shadowed = true;
|
|
const entity = client.pony;
|
|
const region = createServerRegion(0, 0);
|
|
entity.region = region;
|
|
entity.region.clients.push(mockClient(), client);
|
|
client.pony.id = 123;
|
|
|
|
sendAction(entity, Action.Boop);
|
|
|
|
expect(Array.from(getWriterBuffer(client.updateQueue))).eql([2, 0, 128, 0, 0, 0, 123, 1]);
|
|
expect(region.entityUpdates).eql([]);
|
|
});
|
|
});
|
|
|
|
describe('boop()', () => {
|
|
let client: IClient;
|
|
|
|
beforeEach(() => {
|
|
client = mockClient();
|
|
client.map = createServerMap('foo', 0, 1, 1);
|
|
client.pony.region = client.map.regions[0];
|
|
client.lastExpressionAction = 0;
|
|
client.lastBoopOrKissAction = 0;
|
|
});
|
|
|
|
it('sends boop action', () => {
|
|
boop(client, 1000);
|
|
|
|
expect(client.pony.region!.entityUpdates).eql([
|
|
{
|
|
entity: client.pony, flags: UpdateFlags.Action, x: 0, y: 0, vx: 0, vy: 0, action: Action.Boop,
|
|
playerState: 0, options: undefined,
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('cancels expression', () => {
|
|
client.pony.exprCancellable = true;
|
|
client.pony.options!.expr = 123;
|
|
|
|
boop(client, 1000);
|
|
|
|
expect(client.pony.options!.expr).equal(EMPTY_EXPRESSION);
|
|
});
|
|
|
|
it('updates last boop/kiss action', () => {
|
|
client.lastBoopOrKissAction = 0;
|
|
|
|
boop(client, 100);
|
|
|
|
expect(client.lastBoopOrKissAction).equal(100 + 850);
|
|
});
|
|
|
|
it('executes boop on found entity', () => {
|
|
const boop = stub();
|
|
client.pony.x = 5;
|
|
client.pony.y = 5;
|
|
getRegion(client.map, 0, 0).entities.push(serverEntity(0, 4.2, 5, 0, { boop }));
|
|
|
|
boop(client);
|
|
|
|
assert.calledWith(boop, client);
|
|
});
|
|
|
|
it('does not execute boop on found entity if shadowed', () => {
|
|
const stubBoop = stub();
|
|
client.pony.x = 5;
|
|
client.pony.y = 5;
|
|
client.shadowed = true;
|
|
getRegion(client.map, 0, 0).entities.push(serverEntity(0, 4.2, 5, 0, { boop: stubBoop }));
|
|
|
|
boop(client, 0);
|
|
|
|
assert.notCalled(stubBoop);
|
|
expect(client.pony.region!.entityUpdates.length).eql(0);
|
|
});
|
|
|
|
it('does nothing if cannot perform action', () => {
|
|
client.lastBoopOrKissAction = 1000;
|
|
|
|
boop(client, 0);
|
|
|
|
expect(client.pony.region!.entityUpdates.length).eql(0);
|
|
});
|
|
|
|
it('does nothing if moving', () => {
|
|
client.pony.vx = 1;
|
|
|
|
boop(client, 0);
|
|
|
|
expect(client.pony.region!.entityUpdates.length).eql(0);
|
|
});
|
|
});
|
|
|
|
describe('turnHead()', () => {
|
|
it('updates HeadTurned flag', () => {
|
|
const client = mockClient();
|
|
client.pony.state = 0;
|
|
|
|
turnHead(client);
|
|
|
|
expect(client.pony.state).equal(EntityState.HeadTurned);
|
|
});
|
|
|
|
it('does not update flags if cannot perform action', () => {
|
|
const client = mockClient();
|
|
client.lastBoopOrKissAction = Date.now() + 1000;
|
|
client.pony.state = 0;
|
|
|
|
turnHead(client);
|
|
|
|
expect(client.pony.state).equal(0);
|
|
});
|
|
});
|
|
|
|
describe('stand()', () => {
|
|
beforeEach(() => {
|
|
client.pony.exprCancellable = true;
|
|
client.pony.options!.expr = 123;
|
|
});
|
|
|
|
it('updates entity flag to standing', () => {
|
|
client.pony.state = EntityState.PonySitting;
|
|
client.lastBoopOrKissAction = 0;
|
|
client.lastExpressionAction = 0;
|
|
|
|
stand(client);
|
|
|
|
expect(client.pony.state).equal(EntityState.PonyStanding);
|
|
});
|
|
|
|
it('does not change other entity flags', () => {
|
|
client.pony.state = EntityState.PonySitting | EntityState.FacingRight;
|
|
|
|
stand(client);
|
|
|
|
expect(client.pony.state).equal(EntityState.PonyStanding | EntityState.FacingRight);
|
|
});
|
|
|
|
it('cancels expression', () => {
|
|
client.pony.state = EntityState.PonySitting;
|
|
|
|
stand(client);
|
|
|
|
expect(client.pony.options!.expr).equal(EMPTY_EXPRESSION);
|
|
});
|
|
|
|
it('does not cancel expression if transitioning from flying', () => {
|
|
client.pony.state = EntityState.PonyFlying;
|
|
|
|
stand(client);
|
|
|
|
expect(client.pony.options!.expr).equal(123);
|
|
});
|
|
|
|
it('does nothing if already standing', () => {
|
|
client.pony.state = EntityState.PonyStanding;
|
|
|
|
stand(client);
|
|
|
|
expect(client.pony.state).equal(EntityState.PonyStanding);
|
|
expect(client.pony.options!.expr).equal(123);
|
|
});
|
|
|
|
it('does nothing if cannot perform action', () => {
|
|
client.lastBoopOrKissAction = Date.now() + 1000;
|
|
client.pony.state = 0;
|
|
|
|
stand(client);
|
|
|
|
expect(client.pony.state).equal(0);
|
|
expect(client.pony.options!.expr).equal(123);
|
|
});
|
|
|
|
it('does nothing if cannot land', () => {
|
|
client.pony.state = EntityState.PonyFlying;
|
|
client.pony.x = 0.5;
|
|
client.pony.y = 0.5;
|
|
setupCollider(client.map, 0.5, 0.5);
|
|
updateColliders(client.map);
|
|
|
|
stand(client);
|
|
|
|
expect(client.pony.state).equal(EntityState.PonyFlying);
|
|
expect(client.pony.options!.expr).equal(123);
|
|
});
|
|
});
|
|
|
|
describe('sit()', () => {
|
|
it('updates entity flag to sitting', () => {
|
|
client.pony.state = EntityState.PonyStanding;
|
|
|
|
sit(client, {});
|
|
|
|
expect(client.pony.state).equal(EntityState.PonySitting);
|
|
});
|
|
|
|
it('does not change other entity flags', () => {
|
|
client.pony.state = EntityState.PonyStanding | EntityState.FacingRight;
|
|
|
|
sit(client, {});
|
|
|
|
expect(client.pony.state).equal(EntityState.PonySitting | EntityState.FacingRight);
|
|
});
|
|
|
|
it('does nothing if already sitting', () => {
|
|
client.pony.state = EntityState.PonySitting;
|
|
|
|
sit(client, {});
|
|
|
|
expect(client.pony.state).equal(EntityState.PonySitting);
|
|
});
|
|
|
|
it('does nothing if cannot perform action', () => {
|
|
client.lastBoopOrKissAction = Date.now() + 1000;
|
|
client.pony.state = 0;
|
|
|
|
sit(client, {});
|
|
|
|
expect(client.pony.state).equal(0);
|
|
});
|
|
|
|
it('does nothing if moving', () => {
|
|
client.pony.vx = 1;
|
|
client.pony.state = 0;
|
|
|
|
sit(client, {});
|
|
|
|
expect(client.pony.state).equal(0);
|
|
});
|
|
});
|
|
|
|
describe('lie()', () => {
|
|
it('updates entity flag to lying', () => {
|
|
client.pony.state = EntityState.PonyStanding;
|
|
|
|
lie(client);
|
|
|
|
expect(client.pony.state).equal(EntityState.PonyLying);
|
|
});
|
|
|
|
it('does not change other entity flags', () => {
|
|
client.pony.state = EntityState.PonyStanding | EntityState.FacingRight;
|
|
|
|
lie(client);
|
|
|
|
expect(client.pony.state).equal(EntityState.PonyLying | EntityState.FacingRight);
|
|
});
|
|
|
|
it('does nothing if already lying', () => {
|
|
client.pony.state = EntityState.PonyLying;
|
|
|
|
lie(client);
|
|
|
|
expect(client.pony.state).equal(EntityState.PonyLying);
|
|
});
|
|
|
|
it('does nothing if cannot perform action', () => {
|
|
client.lastBoopOrKissAction = Date.now() + 1000;
|
|
client.pony.state = 0;
|
|
|
|
lie(client);
|
|
|
|
expect(client.pony.state).equal(0);
|
|
});
|
|
|
|
it('does nothing if moving', () => {
|
|
client.pony.vx = 1;
|
|
client.pony.state = 0;
|
|
|
|
lie(client);
|
|
|
|
expect(client.pony.state).equal(0);
|
|
});
|
|
});
|
|
|
|
describe('fly()', () => {
|
|
beforeEach(() => {
|
|
client.pony.canFly = true;
|
|
client.pony.exprCancellable = true;
|
|
client.pony.options!.expr = 123;
|
|
});
|
|
|
|
it('updates entity flag to flying', () => {
|
|
client.pony.state = EntityState.PonyStanding;
|
|
|
|
fly(client);
|
|
|
|
expect(client.pony.state).equal(EntityState.PonyFlying | EntityState.Flying);
|
|
});
|
|
|
|
it('does not change other entity flags', () => {
|
|
client.pony.state = EntityState.PonyStanding | EntityState.FacingRight;
|
|
|
|
fly(client);
|
|
|
|
expect(client.pony.state).equal(EntityState.PonyFlying | EntityState.FacingRight | EntityState.Flying);
|
|
});
|
|
|
|
it('cancels expression', () => {
|
|
fly(client);
|
|
|
|
expect(client.pony.options!.expr).equal(EMPTY_EXPRESSION);
|
|
});
|
|
|
|
it('does nothing if already flying', () => {
|
|
client.lastBoopOrKissAction = Date.now() + 1000;
|
|
client.pony.state = EntityState.PonyFlying;
|
|
|
|
fly(client);
|
|
|
|
expect(client.pony.state).equal(EntityState.PonyFlying);
|
|
expect(client.pony.options!.expr).equal(123);
|
|
});
|
|
|
|
it('does nothing if cannot perform action', () => {
|
|
client.lastBoopOrKissAction = Date.now() + 1000;
|
|
client.pony.state = 0;
|
|
|
|
fly(client);
|
|
|
|
expect(client.pony.state).equal(0);
|
|
expect(client.pony.options!.expr).equal(123);
|
|
});
|
|
|
|
it('does nothing if cannot fly', () => {
|
|
client.pony.canFly = false;
|
|
client.pony.state = 0;
|
|
|
|
fly(client);
|
|
|
|
expect(client.pony.state).equal(0);
|
|
expect(client.pony.options!.expr).equal(123);
|
|
});
|
|
});
|
|
|
|
describe('expressionAction()', () => {
|
|
beforeEach(() => {
|
|
client.pony.region = createServerRegion(1, 1);
|
|
client.pony.exprCancellable = true;
|
|
client.pony.options!.expr = 123;
|
|
});
|
|
|
|
it('sends given action', () => {
|
|
client.lastExpressionAction = 0;
|
|
client.lastBoopOrKissAction = 0;
|
|
expressionAction(client, Action.Yawn);
|
|
|
|
expect(client.pony.region!.entityUpdates).eql([
|
|
{ ...def, entity: client.pony, flags: UpdateFlags.Expression | UpdateFlags.Action, action: Action.Yawn },
|
|
]);
|
|
});
|
|
|
|
it('cancels expression', () => {
|
|
expressionAction(client, Action.Yawn);
|
|
|
|
expect(client.pony.options!.expr).equal(EMPTY_EXPRESSION);
|
|
});
|
|
|
|
it('does nothing if cannot perform action', () => {
|
|
client.lastExpressionAction = Date.now() + 2500;
|
|
|
|
expressionAction(client, Action.Yawn);
|
|
|
|
expect(client.pony.region!.entityUpdates).eql([]);
|
|
});
|
|
|
|
it('does nothing if not expression action', () => {
|
|
client.pony.canFly = false;
|
|
|
|
expressionAction(client, Action.Boop);
|
|
|
|
expect(client.pony.region!.entityUpdates).eql([]);
|
|
});
|
|
|
|
it('updates last expression action', () => {
|
|
client.lastExpressionAction = 0;
|
|
client.lastBoopOrKissAction = 0;
|
|
|
|
expressionAction(client, Action.Yawn);
|
|
|
|
expect(client.lastExpressionAction).greaterThan(Date.now());
|
|
});
|
|
});
|
|
|
|
describe('holdItem()', () => {
|
|
it('updates entity options', () => {
|
|
const entity = serverEntity(123);
|
|
|
|
holdItem(entity, 456);
|
|
|
|
expect(entity.options).eql({ hold: 456 });
|
|
});
|
|
|
|
it('sends entity update', () => {
|
|
const entity = serverEntity(123);
|
|
const region = createServerRegion(1, 1);
|
|
entity.region = region;
|
|
|
|
holdItem(entity, 456);
|
|
|
|
expect(region.entityUpdates).eql([
|
|
{ ...def, entity, flags: UpdateFlags.Options, options: { hold: 456 } },
|
|
]);
|
|
});
|
|
|
|
it('does not send entity update if hold is already set', () => {
|
|
const entity = serverEntity(123);
|
|
const region = createServerRegion(1, 1);
|
|
entity.region = region;
|
|
entity.options = { hold: 456 };
|
|
|
|
holdItem(entity, 456);
|
|
|
|
expect(region.entityUpdates).eql([]);
|
|
});
|
|
});
|
|
|
|
describe('unholdItem()', () => {
|
|
it('updates entity options', () => {
|
|
const entity = serverEntity(123);
|
|
entity.options = { hold: 456 };
|
|
|
|
unholdItem(entity);
|
|
|
|
expect(entity.options).eql({});
|
|
});
|
|
|
|
it('sends entity update', () => {
|
|
const entity = serverEntity(123);
|
|
const region = createServerRegion(1, 1);
|
|
entity.region = region;
|
|
entity.options = { hold: 456 };
|
|
|
|
unholdItem(entity);
|
|
|
|
expect(region.entityUpdates).eql([
|
|
{ ...def, entity, flags: UpdateFlags.Options, options: { hold: 0 } },
|
|
]);
|
|
});
|
|
|
|
it('does not send entity update if hold is not set', () => {
|
|
const entity = serverEntity(123);
|
|
const region = createServerRegion(1, 1);
|
|
entity.region = region;
|
|
|
|
unholdItem(entity);
|
|
|
|
expect(region.entityUpdates).eql([]);
|
|
});
|
|
});
|
|
|
|
describe('holdToy()', () => {
|
|
it('updates entity options', () => {
|
|
const entity = serverEntity(123);
|
|
|
|
holdToy(entity, 456);
|
|
|
|
expect(entity.options).eql({ toy: 456 });
|
|
});
|
|
|
|
it('sends entity update', () => {
|
|
const entity = serverEntity(123);
|
|
const region = createServerRegion(1, 1);
|
|
entity.region = region;
|
|
|
|
holdToy(entity, 456);
|
|
|
|
expect(region.entityUpdates).eql([
|
|
{ ...def, entity, flags: UpdateFlags.Options, options: { toy: 456 } },
|
|
]);
|
|
});
|
|
|
|
it('does not send entity update if hold is already set', () => {
|
|
const entity = serverEntity(123);
|
|
const region = createServerRegion(1, 1);
|
|
entity.region = region;
|
|
entity.options = { toy: 456 };
|
|
|
|
holdToy(entity, 456);
|
|
|
|
expect(region.entityUpdates).eql([]);
|
|
});
|
|
});
|
|
|
|
describe('unholdToy()', () => {
|
|
it('updates entity options', () => {
|
|
const entity = serverEntity(123);
|
|
entity.options = { toy: 456 };
|
|
|
|
unholdToy(entity);
|
|
|
|
expect(entity.options).eql({});
|
|
});
|
|
|
|
it('sends entity update', () => {
|
|
const entity = serverEntity(123);
|
|
const region = createServerRegion(1, 1);
|
|
entity.region = region;
|
|
entity.options = { toy: 456 };
|
|
|
|
unholdToy(entity);
|
|
|
|
expect(region.entityUpdates).eql([
|
|
{ ...def, entity, flags: UpdateFlags.Options, options: { toy: 0 } },
|
|
]);
|
|
});
|
|
|
|
it('does not send entity update if toy is not set', () => {
|
|
const entity = serverEntity(123);
|
|
const region = createServerRegion(1, 1);
|
|
entity.region = region;
|
|
|
|
unholdToy(entity);
|
|
|
|
expect(region.entityUpdates).eql([]);
|
|
});
|
|
});
|
|
});
|