mirror of
https://github.com/Terncode/pixel.horse.git
synced 2026-09-24 13:55:04 +02:00
Merge pull request #144 from BytewaveMLP/archive-update-v0.55.0
Archive update v0.55.0
This commit is contained in:
+2
-2
@@ -34,8 +34,8 @@ creator(s):
|
||||
SnowFl8keAnge1
|
||||
|
||||
|
||||
Pony Town's art assets are released under the CC BY-NC 4.0 non-commercial license.
|
||||
The assets cannot be used for commercial purposes in any way including crowdfunding such as Patreon or PayPal donations without permission.
|
||||
Pony Town's art assets are released under the CC BY-NC 4.0 non-commercial license.
|
||||
The assets cannot be used for commercial purposes in any way including crowdfunding such as Patreon or PayPal donations without permission.
|
||||
To discuss licensing, contact [email protected].
|
||||
|
||||
|
||||
|
||||
@@ -342,6 +342,26 @@ Read more about it [here](https://stackoverflow.com/questions/1139762/ignore-fil
|
||||
|
||||
## Changelog
|
||||
|
||||
### Pony.Town v0.55.0
|
||||
- Added Kiss action
|
||||
- Added being able to extinguish torches by sneezing
|
||||
- Fixed house tool instructions saying "use mouse wheel" on mobile
|
||||
- Fixed being able to open two actions menus in some cases
|
||||
- Fixed actions menu button getting disabled by closing actions menu with the Escape key
|
||||
- Fixed Yawn and Sneeze animations glitching with some eye styles
|
||||
- Fixed :thinking: expression being inconsistent with /thinking command
|
||||
- Fixed flying entities slowing down when flying over water
|
||||
- Fixed jack-o'-lantern not turning off during the day
|
||||
- Fixed some UI issues
|
||||
|
||||
- Added combining of head and body animations in the Animation tool
|
||||
- Added flag to not allow closing mouths in Animation tool
|
||||
- Fixed issue with gif preview in Animation tool
|
||||
- Added a front leg frame
|
||||
- Added Save button in account settings automatically redirecting to Home for new accounts
|
||||
- Updated Help page to provide more workarounds for users who experience game showing black screen
|
||||
- Updated contributor list
|
||||
|
||||
### Pony.Town v0.54.0
|
||||
- Added a separate logo file for Visit Pony Town button
|
||||
- Fixed users not being able to close actions menu in some cases
|
||||
|
||||
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pony-town",
|
||||
"version": "0.54.0",
|
||||
"version": "0.55.0",
|
||||
"description": "A game of ponies building a town",
|
||||
"main": "pony-town.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { compact } from 'lodash';
|
||||
import {
|
||||
Expression, ExpressionButtonAction, CommandButtonAction, ActionButtonAction, ItemButtonAction,
|
||||
ColorShadow, Eye, Muzzle, Iris, ButtonActionSlot, ExpressionExtra, ButtonAction, ChatType, BodyAnimation,
|
||||
Action, isPartyChat, EntityButtonAction, defaultDrawOptions
|
||||
Action, isPartyChat, EntityButtonAction, defaultDrawOptions, HeadAnimationProperties
|
||||
} from '../common/interfaces';
|
||||
import * as sprites from '../generated/sprites';
|
||||
import { createExpression } from './clientUtils';
|
||||
@@ -11,7 +11,7 @@ import { PonyTownGame } from './game';
|
||||
import { boopAction, upAction, downAction, turnHeadAction } from './playerActions';
|
||||
import { ACTIONS_LIMIT, COMMAND_ACTION_TIME_DELAY } from '../common/constants';
|
||||
import { cloneDeep, hasFlag } from '../common/utils';
|
||||
import { boop, defaultHeadFrame, stand, sneeze, yawn, lie, sit, fly, laugh, excite } from './ponyAnimations';
|
||||
import { boop, defaultHeadFrame, stand, sneeze, yawn, lie, sit, fly, laugh, kiss, excite } from './ponyAnimations';
|
||||
import { createDefaultPony, syncLockedPonyInfo, toPalette, mockPaletteManager } from '../common/ponyInfo';
|
||||
import {
|
||||
ACTION_EXPRESSION_EYE_COLOR, ACTION_EXPRESSION_BG, ACTION_ACTION_COAT_COLOR, WHITE, HEARTS_COLOR,
|
||||
@@ -118,10 +118,7 @@ const actionActions = [
|
||||
actionButtonAction('drop', 'Drop item', Action.Drop),
|
||||
actionButtonAction('drop-toy', 'Drop toy', Action.DropToy),
|
||||
actionButtonAction('magic', 'Magic', Action.Magic),
|
||||
actionButtonAction('switch-tool', 'Switch tool', Action.SwitchTool),
|
||||
actionButtonAction('switch-entity', 'Switch item to place'),
|
||||
actionButtonAction('switch-entity-rev', 'Switch item to place (reverse)'),
|
||||
actionButtonAction('switch-tile', 'Switch tile to place'),
|
||||
actionButtonAction('kiss', 'Kiss', Action.Kiss),
|
||||
];
|
||||
|
||||
const commandActions = [
|
||||
@@ -395,7 +392,8 @@ export function drawAction(canvas: HTMLCanvasElement, action: ButtonAction | und
|
||||
const buffer = drawCanvas(bufferSize, bufferSize, sprites.paletteSpriteSheet, undefined, batch => {
|
||||
const state = { ...createState(), expression: action.expression };
|
||||
const options = { ...defaultDrawPonyOptions(), noEars: true };
|
||||
drawHead(batch, expressionPony, headX, headY, undefined, defaultHeadFrame, state, options, false, 0);
|
||||
drawHead(batch, expressionPony, headX, headY, undefined,
|
||||
defaultHeadFrame, HeadAnimationProperties.None, state, options, false, 0);
|
||||
|
||||
if (action.expression) {
|
||||
const extra = action.expression.extra;
|
||||
@@ -525,6 +523,11 @@ export function drawAction(canvas: HTMLCanvasElement, action: ButtonAction | und
|
||||
batch.drawSprite(sprites.magic_icon, WHITE, defaultPalette, 4, 2);
|
||||
break;
|
||||
}
|
||||
case 'kiss': {
|
||||
const state = { ...createState(), headAnimation: kiss, headAnimationFrame: 9 };
|
||||
drawPony(batch, actionPony, state, 17, 40, defaultDrawPonyOptions());
|
||||
break;
|
||||
}
|
||||
case 'switch-tool': {
|
||||
const palette = mockPaletteManager.addArray(sprites.tools_icon.palettes![0]);
|
||||
batch.drawSprite(sprites.tools_icon.color, WHITE, palette, 0, 2);
|
||||
|
||||
@@ -64,7 +64,7 @@ export const CREDITS: Credit[] = [
|
||||
name: 'Stubenhocker',
|
||||
title: 'Programmer',
|
||||
avatarIndex: 6,
|
||||
links: ['https://github.com/Stubenhocker1399']
|
||||
links: ['https://github.com/Stubenhocker1399', 'https://twitter.com/Stubenhocker13']
|
||||
},
|
||||
{
|
||||
name: 'Terncode',
|
||||
@@ -129,6 +129,7 @@ export const CONTRIBUTORS: Contributors[] = [
|
||||
{ name: 'Deeraw', links: ['https://www.deviantart.com/deerdraw', 'https://twitter.com/TheOnlyDeeraw'] },
|
||||
{ name: 'Aviivix' },
|
||||
{ name: 'SnowFl8keAnge1' },
|
||||
{ name: '3aHo3a', links: ['https://twitter.com/imnuclearimwild'] },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -14,7 +14,8 @@ import { getPonyState, setPonyState, isPonyFlying, addChatBubble, isHidden } fro
|
||||
import {
|
||||
isPony, createPony, setPonyExpression, updatePonyInfo, updatePonyHold, doPonyAction, hasHeadAnimation,
|
||||
setHeadAnimation,
|
||||
doBoopPonyAction
|
||||
doBoopPonyAction,
|
||||
isPonyBug
|
||||
} from '../common/pony';
|
||||
import { PonyTownGame } from './game';
|
||||
import { setupPlayer, savePlayerPosition } from './sec';
|
||||
@@ -25,7 +26,7 @@ import { decodeUpdate, readOneUpdate } from '../common/encoders/updateDecoder';
|
||||
import { updateEntityVelocity } from '../common/entityUtils';
|
||||
import { decodePonyInfo } from '../common/compressPony';
|
||||
import { mockPaletteManager } from '../common/ponyInfo';
|
||||
import { yawn, laugh, sneeze, excite } from './ponyAnimations';
|
||||
import { yawn, laugh, sneeze, kiss, kissFly, kissFlyBug, excite } from './ponyAnimations';
|
||||
import {
|
||||
findEntityById, getRegionGlobal, setTile, removeEntity, addEntity, removeEntityDirectly, setRegion,
|
||||
addEntityToMapRegion, switchEntityRegion, getRegionUnsafe, addOrRemoveFromEntityList,
|
||||
@@ -420,6 +421,22 @@ export function handleAction(game: PonyTownGame, id: number, action: Action) {
|
||||
setHeadAnimation(pony, excite);
|
||||
}
|
||||
break;
|
||||
case Action.Kiss:
|
||||
if (!pony.ponyState.headTurned) {
|
||||
doPonyAction(pony, DoAction.Kiss);
|
||||
}
|
||||
if (isPonyFlying(pony)) {
|
||||
if (isPonyBug(pony)) {
|
||||
setHeadAnimation(pony, kissFlyBug);
|
||||
}
|
||||
else {
|
||||
setHeadAnimation(pony, kissFly);
|
||||
}
|
||||
}
|
||||
else {
|
||||
setHeadAnimation(pony, kiss);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
log(`handleAction: Invalid action: ${action}`);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BodyAnimation, BodyAnimationFrame, HeadAnimation, HeadAnimationFrame, BodyShadow } from '../common/interfaces';
|
||||
import { BodyAnimation, BodyAnimationFrame, HeadAnimation, HeadAnimationFrame, BodyShadow, HeadAnimationProperties } from '../common/interfaces';
|
||||
import { repeat, flatten } from '../common/utils';
|
||||
|
||||
// body animations
|
||||
@@ -23,7 +23,7 @@ export function createBodyAnimation(
|
||||
name: string, fps: number, loop: boolean, frames: number[][], shadowOffsets?: number[][]
|
||||
): Readonly<BodyAnimation> {
|
||||
if (shadowOffsets && shadowOffsets.length !== frames.length) {
|
||||
throw new Error(`Incorrect frame count for shadowOffsets for ${name}`);
|
||||
throw new Error(`Incorrect frame count for shadowOffsets for ${name}, animation frames ${frames.length}, shadow frames ${shadowOffsets.length}`);
|
||||
}
|
||||
|
||||
const shadow = shadowOffsets && shadowOffsets.map<BodyShadow>(([frame, offset]) => ({ frame, offset }));
|
||||
@@ -459,14 +459,163 @@ export const swing = createBodyAnimation('swing', 12, false, [
|
||||
...repeat(3, [2, 1, 0, 0, 12, 17, 11, 11, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1]),
|
||||
]);
|
||||
|
||||
export const flyAnims = [undefined, fly, fly, fly, flyBug];
|
||||
export const kissBody = createBodyAnimation('kiss-body', 24, false, [
|
||||
...repeat(3, [2, 1, 0, 0, 1, 1, 1, 1, -1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1]),
|
||||
[2, 1, 1, 0, 28, 28, 18, 18, -2, 0, -1, 1, 1, 0, 1, 0, 1, 0, 1],
|
||||
[2, 1, 1, 0, 26, 26, 19, 19, -3, 0, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1],
|
||||
...repeat(2, [5, 1, 1, 0, 26, 26, 19, 19, -10, -6, 1, 0, 1, 1, 1, 1, 1, -1, 1, -1]),
|
||||
...repeat(60, [5, 1, 1, 0, 27, 27, 20, 20, -11, -6, 1, 0, 1, 1, 1, 1, 1, -1, 1, -1]),
|
||||
...repeat(2, [5, 1, 1, 0, 26, 26, 19, 19, -10, -6, 1, 0, 1, 1, 1, 1, 1, -1, 1, -1]),
|
||||
[5, 1, 1, 0, 28, 28, 18, 18, -9, -5, 1, 0, 1, 1, 1, 1, 1, -1, 1, -1],
|
||||
...repeat(2, [2, 1, 1, 0, 1, 1, 1, 1, -1, 0, -1, 1, 1, 0, 1, 0, 1, 0, 1])
|
||||
]);
|
||||
|
||||
export const kissLiftHoofBody = createBodyAnimation('kiss-lift-hoof-body', 24, false, [
|
||||
...repeat(3, [2, 1, 0, 0, 1, 1, 1, 1, -1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1]),
|
||||
[2, 1, 1, 0, 28, 28, 18, 18, -2, 0, -1, 1, 1, 0, 1, 0, 1, 0, 1],
|
||||
[2, 1, 1, 0, 27, 26, 19, 19, -3, 0, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1],
|
||||
...repeat(2, [5, 1, 1, 0, 8, 26, 19, 19, -10, -6, 1, 0, 1, 2, 1, 1, 1, -1, 1, -1]),
|
||||
...repeat(60, [5, 1, 1, 0, 8, 27, 20, 20, -11, -6, 1, 0, 1, 1, 1, 1, 1, -1, 1, -1]),
|
||||
...repeat(2, [5, 1, 1, 0, 8, 26, 19, 19, -10, -6, 1, 0, 1, 2, 1, 1, 1, -1, 1, -1]),
|
||||
[5, 1, 1, 0, 28, 28, 18, 18, -9, -5, 1, 0, 1, 1, 1, 1, 1, -1, 1, -1],
|
||||
...repeat(2, [2, 1, 1, 0, 1, 1, 1, 1, -1, 0, -1, 1, 1, 0, 1, 0, 1, 0, 1])
|
||||
]);
|
||||
|
||||
export const kissFlyBody = createBodyAnimation('kiss-fly-body', 16, false, [
|
||||
[1, 1, 3, 0, 8, 10, 6, 5, 0, -16],
|
||||
[1, 1, 4, 0, 8, 10, 6, 5, 0, -15],
|
||||
[1, 1, 5, 0, 8, 10, 6, 5, 0, -14],
|
||||
[1, 1, 6, 0, 8, 10, 6, 5, 0, -14],
|
||||
[1, 1, 7, 0, 8, 10, 6, 5, -1, -15],
|
||||
[1, 1, 8, 0, 9, 10, 5, 5, -1, -17, -1],
|
||||
[1, 1, 9, 0, 9, 10, 4, 4, -1, -18, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 10, 0, 9, 10, 4, 4, -1, -18, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 11, 0, 9, 10, 4, 4, -1, -18, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 12, 0, 9, 10, 4, 4, -1, -17, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 3, 0, 9, 10, 4, 4, -1, -16, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 4, 0, 9, 10, 4, 4, -1, -15, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 5, 0, 9, 10, 4, 4, -1, -14, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 6, 0, 9, 10, 4, 4, -1, -14, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 7, 0, 9, 10, 4, 4, -1, -15, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 8, 0, 9, 10, 4, 4, -1, -17, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 9, 0, 9, 10, 4, 4, -1, -18, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 10, 0, 9, 10, 4, 4, -1, -18, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 11, 0, 9, 10, 4, 4, -1, -18, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 12, 0, 9, 10, 4, 4, -1, -17, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 3, 0, 9, 10, 4, 4, -1, -16, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 4, 0, 9, 10, 4, 4, -1, -15, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 5, 0, 9, 10, 4, 4, -1, -14, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 6, 0, 9, 10, 4, 4, -1, -14, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 7, 0, 9, 10, 4, 4, -1, -15, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 8, 0, 9, 10, 4, 4, -1, -17, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 9, 0, 9, 10, 4, 4, -1, -18, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 10, 0, 9, 10, 4, 4, -1, -18, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 11, 0, 9, 10, 4, 4, -1, -18, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 12, 0, 9, 10, 4, 4, 0, -17, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 3, 0, 8, 10, 5, 4, 0, -16, 0, 0, 0, 0, 0, -1],
|
||||
[1, 1, 4, 0, 8, 10, 6, 5, 0, -15]
|
||||
]);
|
||||
|
||||
export const kissFlyBugBody = createBodyAnimation('kiss-fly-bug-body', 24, false, [
|
||||
[1, 1, 3, 0, 8, 10, 6, 5, 0, -16],
|
||||
[1, 1, 4, 0, 8, 10, 6, 5, 0, -16],
|
||||
[1, 1, 5, 0, 8, 10, 6, 5, 0, -15],
|
||||
[1, 1, 4, 0, 8, 10, 6, 5, 0, -15],
|
||||
[1, 1, 3, 0, 8, 10, 6, 5, -1, -14],
|
||||
[1, 1, 4, 0, 9, 10, 5, 5, -1, -14],
|
||||
[1, 1, 5, 0, 9, 10, 4, 4, -1, -14, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 4, 0, 9, 10, 4, 4, -1, -14, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 3, 0, 9, 10, 4, 4, -1, -15, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 4, 0, 9, 10, 4, 4, -1, -15, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 5, 0, 9, 10, 4, 4, -1, -16, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 4, 0, 9, 10, 4, 4, -1, -17, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 3, 0, 9, 10, 4, 4, -1, -17, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 4, 0, 9, 10, 4, 4, -1, -18, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 5, 0, 9, 10, 4, 4, -1, -18, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 4, 0, 9, 10, 4, 4, -1, -18, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 3, 0, 9, 10, 4, 4, -1, -18, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 4, 0, 9, 10, 4, 4, -1, -17, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 5, 0, 9, 10, 4, 4, -1, -17, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 4, 0, 9, 10, 4, 4, -1, -17, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 3, 0, 9, 10, 4, 4, -1, -16, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 4, 0, 9, 10, 4, 4, -1, -16, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 5, 0, 9, 10, 4, 4, -1, -15, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 4, 0, 9, 10, 4, 4, -1, -15, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 3, 0, 9, 10, 4, 4, -1, -14, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 4, 0, 9, 10, 4, 4, -1, -14, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 5, 0, 9, 10, 4, 4, -1, -14, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 4, 0, 9, 10, 4, 4, -1, -14, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 3, 0, 9, 10, 4, 4, -1, -15, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 4, 0, 9, 10, 4, 4, -1, -15, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 5, 0, 9, 10, 4, 4, -1, -16, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 4, 0, 9, 10, 4, 4, -1, -17, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 3, 0, 9, 10, 4, 4, -1, -17, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 4, 0, 9, 10, 4, 4, -1, -18, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 5, 0, 9, 10, 4, 4, -1, -18, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 4, 0, 9, 10, 4, 4, -1, -18, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 3, 0, 9, 10, 4, 4, -1, -18, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 4, 0, 9, 10, 4, 4, 0, -17, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 5, 0, 8, 10, 5, 4, 0, -17, 0, 0, 0, 0, 0, -1],
|
||||
[1, 1, 4, 0, 8, 10, 6, 5, 0, -17]
|
||||
]);
|
||||
|
||||
export const kissLieBody = createBodyAnimation('kiss-lie-body', 24, false, [
|
||||
...repeat(3, [14, 1, 0, 2, 38, 38, 26, 26]),
|
||||
...repeat(2, [15, 1, 0, 2, 38, 38, 26, 26]),
|
||||
[13, 1, 0, 2, 38, 38, 26, 26, 0, 0, -1],
|
||||
...repeat(2, [13, 1, 0, 2, 38, 38, 26, 26, -1, 0, -1, 0, 1, 0, 1, 0, 1, 0, 1]),
|
||||
...repeat(57, [13, 1, 0, 2, 38, 38, 26, 26, -1, 0, -2, 0, 1, 0, 1, 0, 1, 0, 1]),
|
||||
...repeat(2, [13, 1, 0, 2, 38, 38, 26, 26, 0, 0, -2]),
|
||||
[13, 1, 0, 2, 38, 38, 26, 26, 0, 0, -1],
|
||||
...repeat(2, [14, 1, 0, 2, 38, 38, 26, 26, 0, 0, -1]),
|
||||
...repeat(2, [14, 1, 0, 2, 38, 38, 26, 26])
|
||||
], [...repeat(72, [3, 3])]);
|
||||
|
||||
export const kissSitBody = createBodyAnimation('kiss-sit-body', 24, false, [
|
||||
...repeat(3, [8, 1, 2, 2, 34, 34, 25, 25]),
|
||||
...repeat(2, [9, 1, 2, 2, 34, 34, 26, 26]),
|
||||
[9, 1, 2, 2, 34, 34, 26, 26, 0, -1, -1, 0, 0, 1, 0, 1, 0, 1, 0, 1],
|
||||
...repeat(2, [9, 1, 2, 2, 39, 39, 26, 26, -1, -1, -1, 0, 0, 1, 0, 1, 1, 1, 1, 1]),
|
||||
...repeat(60, [9, 1, 2, 2, 39, 39, 26, 26, -1, -1, -2, 0, 0, 1, 0, 1, 1, 1, 1, 1]),
|
||||
...repeat(2, [9, 1, 2, 2, 34, 34, 26, 26, 0, -1, -2, 0, 0, 1, 0, 1, 0, 1, 0, 1]),
|
||||
...repeat(2, [9, 1, 2, 2, 34, 34, 26, 26, 0, 0, -1])
|
||||
], [...repeat(72, [0, 6])]);
|
||||
|
||||
export const kissSwimBody = createBodyAnimation('kiss-swim-body', 8, false, [
|
||||
...repeat(2, [1, 1, 0, 0, 8, 10, 6, 5, 0, 14]),
|
||||
[1, 1, 0, 0, 9, 10, 5, 5, -1, 13],
|
||||
[1, 1, 0, 0, 9, 10, 4, 4, -1, 13, -1, 0, 0, 0, 0, -1],
|
||||
...repeat(2, [1, 1, 0, 0, 9, 10, 4, 4, -1, 12, -1, 0, 0, 0, 0, -1]),
|
||||
...repeat(2, [1, 1, 0, 0, 9, 10, 4, 4, -1, 13, -1, 0, 0, 0, 0, -1]),
|
||||
...repeat(2, [1, 1, 0, 0, 9, 10, 4, 4, -1, 14, -1, 0, 0, 0, 0, -1]),
|
||||
...repeat(2, [1, 1, 0, 0, 9, 10, 4, 4, -1, 13, -1, 0, 0, 0, 0, -1]),
|
||||
...repeat(2, [1, 1, 0, 0, 9, 10, 4, 4, -1, 12, -1, 0, 0, 0, 0, -1]),
|
||||
...repeat(2, [1, 1, 0, 0, 9, 10, 4, 4, -1, 13, -1, 0, 0, 0, 0, -1]),
|
||||
...repeat(2, [1, 1, 0, 0, 9, 10, 4, 4, -1, 14, -1, 0, 0, 0, 0, -1]),
|
||||
...repeat(2, [1, 1, 0, 0, 9, 10, 4, 4, -1, 13, -1, 0, 0, 0, 0, -1]),
|
||||
...repeat(2, [1, 1, 0, 0, 9, 10, 4, 4, -1, 12, -1, 0, 0, 0, 0, -1]),
|
||||
[1, 1, 0, 0, 8, 10, 4, 4, 0, 13, -1, 0, 0, 0, 0, -1],
|
||||
[1, 1, 0, 0, 8, 10, 5, 5, 0, 13]
|
||||
]);
|
||||
|
||||
export const kissToTrot = createBodyAnimation('kiss-to-trot-body', 24, false, [
|
||||
[2, 1, 0, 0, 8, 4, 19, 4, -1, 0, -1, 1, 0, 0, 0, 0, 1, -1],
|
||||
[1, 1, 0, 0, 12, 5, 13, 5, 0, -2],
|
||||
[1, 1, 0, 0, 14, 6, 14, 6, 0, -2],
|
||||
[1, 1, 0, 0, 15, 7, 15, 7, 0, -2],
|
||||
[1, 1, 0, 0, 16, 8, 16, 8, 0, -1],
|
||||
[1, 1, 0, 0, 17, 9, 17, 9]
|
||||
]);
|
||||
|
||||
export const flyAnims = [undefined, fly, fly, fly, flyBug, kissFlyBody, kissFlyBugBody];
|
||||
export const flyUpAnims = [undefined, flyUp, flyUp, flyUp, flyUpBug];
|
||||
export const flyDownAnims = [undefined, flyDown, flyDown, flyDown, flyDownBug];
|
||||
|
||||
export const animations = [
|
||||
stand, trot, boop, boopSit, boopLie, boopSwim, boopFly, boopFlyBug, sit, sitDown, standUp, lie, lieDown, sitUp,
|
||||
fly, flyBug, flyUp, flyUpBug, flyDown, flyDownBug, sitToTrot, lieToTrot, flyToTrot, flyToTrotBug,
|
||||
swim, trotToSwim, swimToTrot, flyToSwim, swimToFly,
|
||||
swim, trotToSwim, swimToTrot, flyToSwim, swimToFly, kissBody, kissLiftHoofBody, kissFlyBody, kissFlyBugBody, kissLieBody,
|
||||
kissSitBody, kissSwimBody, kissToTrot
|
||||
];
|
||||
|
||||
export const sitDownUp = mergeAnimations('sit', 24, false, [...repeat(12, stand), sitDown, ...repeat(12, sit), standUp]);
|
||||
@@ -488,8 +637,12 @@ export function createHeadFrame([headX = 0, headY = 0, left = 0, right = 0, mout
|
||||
return { headX, headY, left, right, mouth };
|
||||
}
|
||||
|
||||
export function createHeadAnimation(name: string, fps: number, loop: boolean, frames: number[][]): HeadAnimation {
|
||||
return { name, fps, loop, frames: frames.map(createHeadFrame) };
|
||||
export function createHeadAnimation(name: string, fps: number, loop: boolean, frames: number[][],
|
||||
properties?: HeadAnimationProperties): HeadAnimation {
|
||||
if (!properties) {
|
||||
properties = HeadAnimationProperties.None;
|
||||
}
|
||||
return { name, fps, loop, properties, frames: frames.map(createHeadFrame) };
|
||||
}
|
||||
|
||||
export const smile = createHeadAnimation('smile', 24, true, [
|
||||
@@ -510,7 +663,7 @@ export const yawn = createHeadAnimation('yawn', 12, false, [
|
||||
...repeat(18, [1, -1, 12, 12, 16]),
|
||||
...repeat(8, [0, 0, 12, 12, 12]),
|
||||
[0, 0, 18, 18, 2],
|
||||
]);
|
||||
], HeadAnimationProperties.DontIncreaseEyeOpenness);
|
||||
|
||||
export const surprise = createHeadAnimation('surprise', 8, false, [
|
||||
[0, 1, 6, 6, 1],
|
||||
@@ -538,7 +691,7 @@ export const sneeze = createHeadAnimation('sneeze', 12, false, [
|
||||
...repeat(2, [1, -1, 18, 18, 16]),
|
||||
...repeat(8, [-1, 1, 23, 23, 13]),
|
||||
...repeat(4, [0, 0, 18, 18, 7]),
|
||||
]);
|
||||
], HeadAnimationProperties.DontIncreaseEyeOpenness);
|
||||
|
||||
export const happy_tongue = createHeadAnimation('happy_tongue', 12, false, [
|
||||
...repeat(3, [0, 0, 1, 1, 0]),
|
||||
@@ -558,8 +711,32 @@ export const happy_tongue_meno_2 = createHeadAnimation('happy_tongue_meno_2', 12
|
||||
...repeat(8, [0, 0, 11, 11, 4]),
|
||||
]);
|
||||
|
||||
export const kiss = createHeadAnimation('kiss', 24, false, [
|
||||
...repeat(2, [0, 0, 1, 1, 0]),
|
||||
...repeat(2, [0, 0, 3, 3, 0]),
|
||||
...repeat(63, [0, 0, 6, 6, 13]),
|
||||
[0, 0, 3, 3, 17],
|
||||
[0, 0, 1, 1, 0]
|
||||
], HeadAnimationProperties.DontIncreaseEyeOpenness);
|
||||
|
||||
export const kissFly = createHeadAnimation('kiss-fly', 24, false, [
|
||||
...repeat(2, [0, 0, 1, 1, 0]),
|
||||
...repeat(2, [0, 0, 3, 3, 0]),
|
||||
...repeat(38, [0, 0, 6, 6, 13]),
|
||||
[0, 0, 3, 3, 17],
|
||||
[0, 0, 1, 1, 0]
|
||||
], HeadAnimationProperties.DontIncreaseEyeOpenness);
|
||||
|
||||
export const kissFlyBug = createHeadAnimation('kiss-fly-bug', 24, false, [
|
||||
...repeat(2, [0, 0, 1, 1, 0]),
|
||||
...repeat(2, [0, 0, 3, 3, 0]),
|
||||
...repeat(35, [0, 0, 6, 6, 13]),
|
||||
[0, 0, 3, 3, 17],
|
||||
[0, 0, 1, 1, 0]
|
||||
], HeadAnimationProperties.DontIncreaseEyeOpenness);
|
||||
|
||||
export const headAnimations = [
|
||||
smile, nom, laugh, yawn, surprise, surpriseSad, sneeze, excite,
|
||||
smile, nom, laugh, yawn, surprise, surpriseSad, sneeze, excite, kiss, kissFly, kissFlyBug
|
||||
];
|
||||
|
||||
// default animations
|
||||
|
||||
+44
-17
@@ -1,7 +1,8 @@
|
||||
import {
|
||||
PonyEye, PonyState, PalettePonyInfo, PaletteSpriteSet, Palette, HeadAnimationFrame,
|
||||
Eye, Iris, ColorExtraSets, ExpressionExtra, BodyAnimationFrame, DrawPonyOptions, Muzzle, BodyShadow, DrawOptions,
|
||||
NoDraw, PaletteSpriteBatch, defaultDrawOptions, PonyStateFlags, PaletteManager, isEyeSleeping, Matrix2D,
|
||||
NoDraw, PaletteSpriteBatch, defaultDrawOptions, PonyStateFlags, PaletteManager, Matrix2D, HeadAnimationProperties,
|
||||
getEyeOpenness, Expression, getMuzzleOpenness,
|
||||
} from '../common/interfaces';
|
||||
import { WHITE, SHINES_COLOR, FAR_COLOR, TRANSPARENT, fillToOutlineColor } from '../common/colors';
|
||||
import { toInt, hasFlag, repeat, flatten, point } from '../common/utils';
|
||||
@@ -232,6 +233,7 @@ function getWakeIndex(info: Info) {
|
||||
export function drawPony(batch: Batch, info: Info, state: State, ponyX: number, ponyY: number, options: Options) {
|
||||
const frame = getPonyAnimationFrame(state.animation, state.animationFrame, defaultBodyFrame);
|
||||
const headFrame = getPonyAnimationFrame(state.headAnimation || defaultHeadAnimation, state.headAnimationFrame, defaultHeadFrame);
|
||||
const headAnimationProperties = (state.headAnimation || defaultHeadAnimation).properties;
|
||||
const baseX = ponyX - PONY_WIDTH / 2;
|
||||
const baseY = ponyY - PONY_HEIGHT;
|
||||
const x = baseX + frame.bodyX;
|
||||
@@ -502,7 +504,7 @@ export function drawPony(batch: Batch, info: Info, state: State, ponyX: number,
|
||||
drawSet(batch, sprites.behindManes, info.mane, 0, maneBehindOffsetY, WHITE);
|
||||
}
|
||||
|
||||
drawHead(batch, info, 0, 0, headSprite, headFrame, state, options, headFlip, maneOffsetY);
|
||||
drawHead(batch, info, 0, 0, headSprite, headFrame, headAnimationProperties, state, options, headFlip, maneOffsetY);
|
||||
drawSet(batch, sprites.headAccessories, info.headAccessory, hatOffset.x, hatOffset.y + hatOffsetY, WHITE);
|
||||
|
||||
batch.restore();
|
||||
@@ -514,7 +516,7 @@ export function drawPony(batch: Batch, info: Info, state: State, ponyX: number,
|
||||
|
||||
export function drawHead(
|
||||
batch: Batch, info: Info, x: number, y: number, headSprites: ColorExtraSets, headFrame: HeadAnimationFrame,
|
||||
{ blinkFrame, expression, holding, blushColor, drawFaceExtra }: State,
|
||||
animationProperties: HeadAnimationProperties, { blinkFrame, expression, holding, blushColor, drawFaceExtra }: State,
|
||||
options: Options, flip: boolean, maneOffsetY: number,
|
||||
) {
|
||||
const extraOffset = at(EXTRA_ACCESSORY_OFFSETS, info.mane && info.mane.type) || pointZero;
|
||||
@@ -550,18 +552,18 @@ export function drawHead(
|
||||
|
||||
// make sure eyes are closed if sleeping
|
||||
if (hasFlag(expression.extra, ExpressionExtra.Zzz)) {
|
||||
if (!isEyeSleeping(eyeLeftBase)) {
|
||||
if (getEyeOpenness(eyeLeftBase) !== 0) {
|
||||
eyeLeftBase = Eye.Closed;
|
||||
}
|
||||
|
||||
if (!isEyeSleeping(eyeRightBase)) {
|
||||
if (getEyeOpenness(eyeRightBase) !== 0) {
|
||||
eyeRightBase = Eye.Closed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const eyeRight = getEyeFrame(info.eyeOpennessRight || 1, eyeRightBase, headFrame.right, blinkFrame);
|
||||
const eyeLeft = getEyeFrame(info.eyeOpennessLeft || 1, eyeLeftBase, headFrame.left, blinkFrame);
|
||||
const eyeRight = getEyeFrame(info.eyeOpennessRight || 1, eyeRightBase, headFrame.right, animationProperties, blinkFrame);
|
||||
const eyeLeft = getEyeFrame(info.eyeOpennessLeft || 1, eyeLeftBase, headFrame.left, animationProperties, blinkFrame);
|
||||
const eyeFrameLeft = flip ? eyeRight : eyeLeft;
|
||||
const eyeFrameRight = flip ? eyeLeft : eyeRight;
|
||||
const eyeColorLeft = flip ? info.eyeColorRight : info.eyeColorLeft;
|
||||
@@ -613,12 +615,7 @@ export function drawHead(
|
||||
}
|
||||
|
||||
if (draw(options, NoDraw.Body) && draw(options, NoDraw.Nose)) {
|
||||
const muzzle = holding ?
|
||||
Muzzle.Smile :
|
||||
headFrame.mouth === -1 ?
|
||||
(expression ? expression.muzzle : info.muzzle) :
|
||||
headFrame.mouth;
|
||||
|
||||
const muzzle = getMouthFrame(!!holding, expression, headFrame.mouth, info.muzzle, animationProperties);
|
||||
const noses = at(sprites.noses, muzzle);
|
||||
const nose = att(noses, info.nose && info.nose.type)![0];
|
||||
|
||||
@@ -723,11 +720,18 @@ function drawLeg(
|
||||
}
|
||||
}
|
||||
|
||||
function getEyeFrame(base: Eye, expression: Eye, anim: Eye, blinkFrame: number) {
|
||||
if (anim !== -1)
|
||||
return anim;
|
||||
|
||||
function getEyeFrame(base: Eye, expression: Eye, anim: Eye, animationProperties: HeadAnimationProperties, blinkFrame: number) {
|
||||
const frame = expression === -1 ? base : expression;
|
||||
|
||||
if (anim !== -1) {
|
||||
if (hasFlag(animationProperties, HeadAnimationProperties.DontIncreaseEyeOpenness)) {
|
||||
if (getEyeOpenness(anim) > getEyeOpenness(frame)) {
|
||||
return frame;
|
||||
}
|
||||
}
|
||||
return anim;
|
||||
}
|
||||
|
||||
const blink = blinkFrames[frame];
|
||||
|
||||
if (blinkFrame > 1 && blink) {
|
||||
@@ -741,6 +745,29 @@ function getEyeFrame(base: Eye, expression: Eye, anim: Eye, blinkFrame: number)
|
||||
return frame;
|
||||
}
|
||||
|
||||
function getMouthFrame(holding: boolean, expression: Expression | undefined, headFrameMuzzle: Muzzle,
|
||||
currentMuzzle: Muzzle | undefined, properties: HeadAnimationProperties) {
|
||||
if (holding) {
|
||||
return Muzzle.Smile;
|
||||
}
|
||||
|
||||
let applyMuzzle = currentMuzzle;
|
||||
if (expression) {
|
||||
applyMuzzle = expression.muzzle;
|
||||
}
|
||||
|
||||
if (headFrameMuzzle !== -1) {
|
||||
if (applyMuzzle && hasFlag(properties, HeadAnimationProperties.DontDecreaseMouthOpenness)) {
|
||||
if (getMuzzleOpenness(headFrameMuzzle) < getMuzzleOpenness(applyMuzzle)) {
|
||||
return applyMuzzle;
|
||||
}
|
||||
}
|
||||
return headFrameMuzzle;
|
||||
}
|
||||
|
||||
return applyMuzzle;
|
||||
}
|
||||
|
||||
function drawEye(
|
||||
batch: Batch, eye: PonyEye | undefined, iris: Iris, info: Info, palette: Palette | undefined, eyePalette: Palette,
|
||||
x: number, y: number
|
||||
|
||||
@@ -2,7 +2,8 @@ import { animatorState as state, animatorTransition as transition, anyState, Ani
|
||||
import {
|
||||
stand, sit, sitDown, standUp, lie, lieDown, sitUp, flyBug, fly, flyUp, flyDown, flyUpBug, flyDownBug,
|
||||
trot, boop, boopSit, swim, sitToTrot, lieToTrot, boopLie, trotToFly, trotToFlyBug, boopFly, boopFlyBug,
|
||||
flyToTrot, flyToTrotBug, swing, swimToTrot, trotToSwim, swimToFly, flyToSwim, boopSwim, swimToFlyBug, flyToSwimBug
|
||||
flyToTrot, flyToTrotBug, swing, swimToTrot, trotToSwim, swimToFly, flyToSwim, boopSwim, swimToFlyBug, flyToSwimBug,
|
||||
kissBody, kissLiftHoofBody, kissFlyBody, kissFlyBugBody, kissLieBody, kissSitBody, kissSwimBody, kissToTrot
|
||||
} from './ponyAnimations';
|
||||
import { BodyAnimation } from '../common/interfaces';
|
||||
|
||||
@@ -25,6 +26,14 @@ export const boopingLying = state(n('booping-lying'), boopLie);
|
||||
export const boopingFlying = state(n('booping-flying'), boopFly, { bug: boopFlyBug });
|
||||
export const boopingSwimming = state(n('booping-swimming'), boopSwim);
|
||||
|
||||
export const kissing = state(n('kissing'), kissBody);
|
||||
export const kissingHoof = state(n('kissing'), kissLiftHoofBody);
|
||||
export const kissingFlying = state(n('kissing-flying'), kissFlyBody, { bug: kissFlyBugBody });
|
||||
export const kissingLying = state(n('kissing-lying'), kissLieBody);
|
||||
export const kissingSitting = state(n('kissing-sitting'), kissSitBody);
|
||||
export const kissingSwimming = state(n('kissing-swimming'), kissSwimBody);
|
||||
export const kissingToTrotting = state(n('sitting-to-trotting'), kissToTrot);
|
||||
|
||||
export const sitting = state(n('sitting'), sit);
|
||||
export const sittingDown = state(n('sitting-down'), sitDown);
|
||||
export const standingUp = state(n('standing-up'), standUp);
|
||||
@@ -51,7 +60,8 @@ export const ponyStates = [
|
||||
sitting, sittingDown, standingUp, sittingToTrotting,
|
||||
lying, lyingDown, sittingUp, lyingToTrotting,
|
||||
hovering, flying, flyingUp, flyingDown, trottingToFlying, flyingToTrotting,
|
||||
swinging, swimmingToFlying, flyingToSwimming, boopingSwimming,
|
||||
swinging, swimmingToFlying, flyingToSwimming, boopingSwimming, kissing, kissingHoof,
|
||||
kissingFlying, kissingLying, kissingSitting, kissingSwimming, kissingToTrotting
|
||||
];
|
||||
|
||||
transition(hovering, flyingDown, { exitAfter: 0 });
|
||||
@@ -101,7 +111,6 @@ transition(flying, hovering, { exitAfter: 0, keepTime: true });
|
||||
|
||||
transition(boopingSwimming, swimming);
|
||||
transition(swimming, boopingSwimming, { exitAfter: 0 });
|
||||
|
||||
transition(booping, standing);
|
||||
transition(standing, booping, { exitAfter: 0 });
|
||||
transition(boopingSitting, sitting);
|
||||
@@ -111,7 +120,27 @@ transition(lying, boopingLying, { exitAfter: 0 });
|
||||
transition(boopingFlying, hovering, { enterTime: 1.1 / 10 });
|
||||
transition(hovering, boopingFlying, { exitAfter: 0 });
|
||||
|
||||
// transition(anyState, trottingToSwimming, { exitAfter: 0 });
|
||||
transition(kissingSwimming, swimming);
|
||||
transition(swimming, kissingSwimming, { exitAfter: 0 });
|
||||
transition(kissing, standing);
|
||||
transition(standing, kissing, { exitAfter: 0 });
|
||||
transition(kissingHoof, standing);
|
||||
transition(standing, kissingHoof, { exitAfter: 0 });
|
||||
transition(kissingFlying, hovering, { enterTime: 1.1 / 10 });
|
||||
transition(hovering, kissingFlying, { exitAfter: 0 });
|
||||
transition(kissingLying, lying);
|
||||
transition(lying, kissingLying, { exitAfter: 0 });
|
||||
transition(kissingSitting, sitting);
|
||||
transition(sitting, kissingSitting, { exitAfter: 0 });
|
||||
|
||||
transition(kissing, kissingToTrotting, { exitAfter: 0, onlyDirectTo: trotting });
|
||||
transition(kissingToTrotting, trotting);
|
||||
|
||||
transition(kissingSitting, sittingToTrotting, { exitAfter: 0, onlyDirectTo: trotting });
|
||||
transition(sittingToTrotting, standing);
|
||||
|
||||
transition(kissingLying, lyingToTrotting, { exitAfter: 0, onlyDirectTo: trotting });
|
||||
transition(lyingToTrotting, standing);
|
||||
|
||||
transition(anyState, trotting, { exitAfter: 0, keepTime: true });
|
||||
transition(anyState, flying, { exitAfter: 0, keepTime: true });
|
||||
@@ -129,7 +158,8 @@ export function isFlyingDown(state: AnimatorState<BodyAnimation> | undefined) {
|
||||
|
||||
export function isSwimmingState(state: AnimatorState<BodyAnimation> | undefined) {
|
||||
return state === swimming || state === trottingToSwimming || state === swimmingToTrotting ||
|
||||
state === flyingToSwimming || state === swimmingToFlying || state === boopingSwimming;
|
||||
state === flyingToSwimming || state === swimmingToFlying || state === boopingSwimming ||
|
||||
state === kissingSwimming;
|
||||
}
|
||||
|
||||
export function isFlyingUpOrDown(state: AnimatorState<BodyAnimation> | undefined) {
|
||||
@@ -153,3 +183,14 @@ export function toBoopState(state: AnimatorState<BodyAnimation>) {
|
||||
default: return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function toKissState(state: AnimatorState<BodyAnimation>) {
|
||||
switch (state) {
|
||||
case standing: return (Math.random() < 0.5 ? kissing : kissingHoof);
|
||||
case sitting: return kissingSitting;
|
||||
case lying: return kissingLying;
|
||||
case hovering: return kissingFlying;
|
||||
case swimming: return kissingSwimming;
|
||||
default: return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1176,11 +1176,17 @@ export const sandPileBig = doodad(n('sandpile-big'), sprites.snowpile_big, 43, 2
|
||||
|
||||
// pumpkins
|
||||
|
||||
const pumpkinOffOnSprites: AnimatedRenderable = {frames: [sprites.pumpkin_off.color, sprites.pumpkin_on.color],
|
||||
palette: sprites.pumpkin_on.palettes![0],
|
||||
shadow: sprites.pumpkin_on.shadow};
|
||||
const pumpkinLightSprite: AnimatedRenderable1 = {frames: [undefined, sprites.pumpkin_light]};
|
||||
const pumpkinCollider = mixColliderRounded(-11, -6, 22, 12, 5, false);
|
||||
const pumpkinPickable = mixPickable(26, 50);
|
||||
const pumpkinParts = [pumpkinCollider, pumpkinPickable];
|
||||
const pumpkinDX = 11;
|
||||
const pumpkinDY = 15;
|
||||
const pumpkinAnimOff = [0];
|
||||
const pumpkinAnimOn = [1];
|
||||
|
||||
export const pumpkin = doodad(n('pumpkin'), sprites.pumpkin_default, pumpkinDX, pumpkinDY, 0,
|
||||
...pumpkinParts);
|
||||
@@ -1193,11 +1199,20 @@ export const jackoOn = doodad(n('jacko-on'), sprites.pumpkin_on, pumpkinDX, pump
|
||||
mixLight(jackoLightColor, 0, 0, 256, 192),
|
||||
mixLightSprite(sprites.pumpkin_light, WHITE, pumpkinDX, pumpkinDY));
|
||||
|
||||
export const jacko = doodad(n('jacko'), sprites.pumpkin_on, pumpkinDX, pumpkinDY, 0,
|
||||
export const jacko = registerMix(n('jacko'),
|
||||
mixAnimation(pumpkinOffOnSprites, 8, pumpkinDX, pumpkinDY, {
|
||||
lightSprite: pumpkinLightSprite,
|
||||
animations: [pumpkinAnimOff, pumpkinAnimOn],
|
||||
}),
|
||||
...pumpkinParts,
|
||||
mixLight(jackoLightColor, 0, 0, 256, 192),
|
||||
mixFlags(EntityFlags.OnOff));
|
||||
|
||||
/*export const jacko = doodad(n('jacko'), sprites.pumpkin_on, pumpkinDX, pumpkinDY, 0,
|
||||
...pumpkinParts,
|
||||
mixLight(jackoLightColor, 0, 0, 256, 192),
|
||||
mixLightSprite(sprites.pumpkin_light, WHITE, pumpkinDX, pumpkinDY),
|
||||
mixFlags(EntityFlags.OnOff));
|
||||
mixFlags(EntityFlags.OnOff));*/
|
||||
|
||||
// tombstones
|
||||
|
||||
@@ -2358,10 +2373,14 @@ export const fruits = [
|
||||
];
|
||||
|
||||
export const tools = [
|
||||
{ type: saw.type, text: 'Saw: place & remove walls' },
|
||||
{ type: broom.type, text: 'Broom: remove furniture' },
|
||||
{ type: hammer.type, text: 'Hammer: place furniture\nuse [mouse wheel] to switch item' },
|
||||
{ type: shovel.type, text: 'Shovel: change floor\nuse [mouse wheel] to switch floor type' },
|
||||
{ type: saw.type, text: 'Saw: place & remove walls', textMobile: undefined },
|
||||
{ type: broom.type, text: 'Broom: remove furniture', textMobile: undefined },
|
||||
{ type: hammer.type,
|
||||
text: 'Hammer: place furniture\nuse [mouse wheel] to switch item',
|
||||
textMobile: 'Hammer: place furniture\nuse [Switch item to place] action to switch item' },
|
||||
{ type: shovel.type,
|
||||
text: 'Shovel: change floor\nuse [mouse wheel] to switch floor type',
|
||||
textMobile: 'Shovel: change floor\nuse [Switch tile to place] action to switch floor type' },
|
||||
];
|
||||
|
||||
export const candies1Types = [candyCane1, candyCane2, cookie, cookiePony].map(e => e.type);
|
||||
|
||||
@@ -60,6 +60,18 @@ export function getBoopRect(entity: Entity) {
|
||||
return rect(entity.x + (right ? 0.6 : -0.9) * (sitting ? 0.6 : 1), entity.y - 0.2, 0.3, 0.4);
|
||||
}
|
||||
|
||||
export function getkissRect(entity: Entity) {
|
||||
const right = hasFlag(entity.state, EntityState.FacingRight);
|
||||
const sitting = isPonySitting(entity);
|
||||
return rect(entity.x + (right ? 0.4 : -0.8) * (sitting ? 0.6 : 1), entity.y - 0.225, 0.4, 0.45);
|
||||
}
|
||||
|
||||
export function getSneezeRect(entity: Entity) {
|
||||
const right = hasFlag(entity.state, EntityState.FacingRight);
|
||||
const sitting = isPonySitting(entity);
|
||||
return rect(entity.x + (right ? 0.45 : -0.95) * (sitting ? 0.6 : 1), entity.y - 0.25, 0.6, 0.5);
|
||||
}
|
||||
|
||||
export function isMoving(entity: Entity) {
|
||||
return entity.vx !== 0 || entity.vy !== 0;
|
||||
}
|
||||
@@ -126,7 +138,7 @@ export function canBoop(pony: Pony) {
|
||||
return isIdle(pony);
|
||||
}
|
||||
|
||||
export function canBoop2(entity: Entity) {
|
||||
export function canBoopOrKiss(entity: Entity) {
|
||||
return !isMoving(entity) && (isPonyStanding(entity) || isPonySitting(entity) || isPonyLying(entity) || isPonyFlying(entity));
|
||||
}
|
||||
|
||||
|
||||
@@ -280,7 +280,7 @@ const constants = createPlainMap<() => Expression | undefined>({
|
||||
'😆': () => expression(Eye.X, Eye.X, Muzzle.SmileOpen),
|
||||
'😟': () => expression(Eye.Sad, Eye.Sad, Muzzle.Neutral),
|
||||
'😠': () => expression(Eye.Angry, Eye.Angry, Muzzle.Smile),
|
||||
'🤔': () => expression(Eye.Neutral, Eye.Frown2, Muzzle.Kiss),
|
||||
'🤔': () => expression(Eye.Neutral, Eye.Frown2, Muzzle.Concerned),
|
||||
'😈': () => expression(Eye.Angry, Eye.Angry, Muzzle.Smile, Iris.Up, Iris.Forward),
|
||||
'👿': () => expression(Eye.Angry, Eye.Angry, Muzzle.SmileTeeth),
|
||||
});
|
||||
|
||||
@@ -283,7 +283,7 @@ export const expressions: [string, Result][] = [
|
||||
['😆', [Eye.X, Eye.X, Muzzle.SmileOpen]],
|
||||
['😟', [Eye.Sad, Eye.Sad, Muzzle.Neutral]],
|
||||
['😠', [Eye.Angry, Eye.Angry, Muzzle.Smile]],
|
||||
['🤔', [Eye.Neutral, Eye.Frown2, Muzzle.Kiss]],
|
||||
['🤔', [Eye.Neutral, Eye.Frown2, Muzzle.Concerned]],
|
||||
['😈', [Eye.Angry, Eye.Angry, Muzzle.Smile, Iris.Up, Iris.Forward]],
|
||||
['👿', [Eye.Angry, Eye.Angry, Muzzle.SmileTeeth]],
|
||||
// unsafe faces
|
||||
|
||||
@@ -566,6 +566,7 @@ export const enum DoAction {
|
||||
Boop,
|
||||
Swing,
|
||||
HoldPoof,
|
||||
Kiss,
|
||||
}
|
||||
|
||||
export interface Pony extends Entity {
|
||||
@@ -1098,6 +1099,7 @@ export const enum Action {
|
||||
RequestEntityInfo,
|
||||
ACL,
|
||||
Magic,
|
||||
Kiss,
|
||||
RemoveEntity,
|
||||
PlaceEntity,
|
||||
SwitchTool,
|
||||
@@ -1221,6 +1223,7 @@ export interface SpriteSet<T> extends SpriteSetBase {
|
||||
}
|
||||
|
||||
export interface PonyInfoBase<T, SET> {
|
||||
//body: SET | undefined;
|
||||
head: SET | undefined;
|
||||
nose: SET | undefined;
|
||||
ears: SET | undefined;
|
||||
@@ -1387,10 +1390,17 @@ export interface HeadAnimationFrame {
|
||||
mouth: Muzzle;
|
||||
}
|
||||
|
||||
export const enum HeadAnimationProperties {
|
||||
None = 0,
|
||||
DontIncreaseEyeOpenness = 1,
|
||||
DontDecreaseMouthOpenness = 2
|
||||
}
|
||||
|
||||
export interface HeadAnimation {
|
||||
name: string;
|
||||
loop: boolean;
|
||||
fps: number;
|
||||
properties: HeadAnimationProperties;
|
||||
frames: HeadAnimationFrame[];
|
||||
}
|
||||
|
||||
@@ -1548,10 +1558,44 @@ export const enum Muzzle {
|
||||
// max: 31
|
||||
}
|
||||
|
||||
export const CLOSED_MUZZLES = [
|
||||
Muzzle.Smile, Muzzle.Frown, Muzzle.Neutral, Muzzle.Scrunch, Muzzle.Flat, Muzzle.Concerned,
|
||||
Muzzle.Kiss, Muzzle.Kiss2,
|
||||
];
|
||||
export function getMuzzleOpenness(muzzle: Muzzle) {
|
||||
switch (muzzle) {
|
||||
case Muzzle.Smile:
|
||||
case Muzzle.Frown:
|
||||
case Muzzle.Neutral:
|
||||
case Muzzle.Scrunch:
|
||||
case Muzzle.Blep:
|
||||
case Muzzle.Flat:
|
||||
case Muzzle.Concerned:
|
||||
case Muzzle.Kiss:
|
||||
case Muzzle.Kiss2:
|
||||
case Muzzle.FlatBlep:
|
||||
return 0;
|
||||
case Muzzle.SmileOpen:
|
||||
case Muzzle.ConcernedOpen:
|
||||
case Muzzle.FrownOpen:
|
||||
case Muzzle.NeutralOpen2:
|
||||
case Muzzle.SmileTeeth:
|
||||
case Muzzle.FrownTeeth:
|
||||
case Muzzle.NeutralTeeth:
|
||||
case Muzzle.ConcernedTeeth:
|
||||
case Muzzle.Oh:
|
||||
return 1;
|
||||
case Muzzle.SmileOpen2:
|
||||
return 2;
|
||||
case Muzzle.ConcernedOpen2:
|
||||
case Muzzle.SmileOpen3:
|
||||
case Muzzle.NeutralOpen3:
|
||||
case Muzzle.SmilePant:
|
||||
case Muzzle.NeutralPant:
|
||||
return 3;
|
||||
case Muzzle.ConcernedOpen3:
|
||||
return 4;
|
||||
default:
|
||||
console.error('unregistered muzzle in getMuzzleOpenness');
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
export const enum Eye {
|
||||
None = 0,
|
||||
@@ -1582,10 +1626,43 @@ export const enum Eye {
|
||||
// max: 31
|
||||
}
|
||||
|
||||
export function isEyeSleeping(eye: Eye) {
|
||||
return eye === Eye.Closed ||
|
||||
(eye >= Eye.Lines && eye <= Eye.ClosedHappy) ||
|
||||
(eye >= Eye.Peaceful && eye <= Eye.X2);
|
||||
export function getEyeOpenness(eye: Eye) {
|
||||
switch (eye) {
|
||||
case Eye.None:
|
||||
case Eye.Closed:
|
||||
case Eye.ClosedHappy:
|
||||
case Eye.ClosedHappy2:
|
||||
case Eye.ClosedHappy3:
|
||||
case Eye.Lines:
|
||||
case Eye.Peaceful:
|
||||
case Eye.Peaceful2:
|
||||
case Eye.X:
|
||||
case Eye.X2:
|
||||
return 0;
|
||||
case Eye.Neutral5:
|
||||
case Eye.Frown4:
|
||||
return 1;
|
||||
case Eye.Neutral4:
|
||||
case Eye.Frown3:
|
||||
case Eye.Sad4:
|
||||
return 2;
|
||||
case Eye.Neutral3:
|
||||
case Eye.Frown2:
|
||||
case Eye.Sad3:
|
||||
case Eye.Angry2:
|
||||
return 3;
|
||||
case Eye.Neutral2:
|
||||
case Eye.Sad2:
|
||||
case Eye.Angry:
|
||||
case Eye.Frown:
|
||||
return 4;
|
||||
case Eye.Neutral:
|
||||
case Eye.Sad:
|
||||
return 5;
|
||||
default:
|
||||
console.error('unregistered eye in getEyeOpenness');
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
|
||||
export const enum Iris {
|
||||
|
||||
+15
-4
@@ -2,7 +2,7 @@ import { PONY_WIDTH, PONY_HEIGHT, BLINK_FRAMES, canFly } from '../client/ponyUti
|
||||
import { stand, sneeze, defaultHeadAnimation, defaultBodyFrame, defaultHeadFrame } from '../client/ponyAnimations';
|
||||
import {
|
||||
PaletteSpriteBatch, Pony, BodyAnimation, EntityState, SpriteBatch, ExpressionExtra, HeadAnimation, Palette,
|
||||
PaletteManager, DrawOptions, Rect, EntityFlags, IMap, Entity, DoAction, Muzzle, Expression, isEyeSleeping,
|
||||
PaletteManager, DrawOptions, Rect, EntityFlags, IMap, Entity, DoAction, Muzzle, Expression, getEyeOpenness,
|
||||
Iris, EntityPlayerState,
|
||||
} from './interfaces';
|
||||
import { hasFlag, setFlag } from './utils';
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
} from './animator';
|
||||
import {
|
||||
trotting, flying, hovering, toBoopState, isFlyingUpOrDown, isFlyingDown, isSittingDown, isSittingUp, swinging,
|
||||
standing, sitting, lying, swimming, isSwimmingState, swimmingToFlying,
|
||||
standing, sitting, lying, swimming, isSwimmingState, swimmingToFlying, toKissState,
|
||||
} from '../client/ponyStates';
|
||||
import { decodePonyInfo } from './compressPony';
|
||||
import { defaultPonyState, defaultDrawPonyOptions, isStateEqual } from '../client/ponyHelpers';
|
||||
@@ -199,6 +199,14 @@ export function updatePonyInfo(pony: Pony, info: string | Uint8Array, apply: ()
|
||||
}
|
||||
}
|
||||
|
||||
export function isPonyBug(pony: Pony) {
|
||||
if (pony.info === undefined || pony.palettePonyInfo === undefined) {
|
||||
return false;
|
||||
}
|
||||
const wingType = pony.palettePonyInfo.wings && pony.palettePonyInfo.wings.type || 0;
|
||||
return wingType === 4;
|
||||
}
|
||||
|
||||
export function ensurePonyInfoDecoded(pony: Pony) {
|
||||
if (pony.info !== undefined && pony.palettePonyInfo === undefined) {
|
||||
pony.palettePonyInfo = decodePonyInfo(pony.info, pony.paletteManager);
|
||||
@@ -445,6 +453,9 @@ export function updatePonyEntity(pony: Pony, delta: number, gameTime: number, sa
|
||||
case DoAction.HoldPoof:
|
||||
playAnimation(pony.holdPoofEffect, holdPoofAnimation);
|
||||
break;
|
||||
case DoAction.Kiss:
|
||||
setAnimatorState(pony.animator, toKissState(animationState) || animationState);
|
||||
break;
|
||||
default:
|
||||
if (DEVELOPMENT) {
|
||||
console.error(`Invalid DoAction: ${pony.doAction}`);
|
||||
@@ -587,8 +598,8 @@ function filterExpression(expression: Expression) {
|
||||
blush ||
|
||||
hasFlag(extra, ExpressionExtra.Hearts) ||
|
||||
hasFlag(extra, ExpressionExtra.Cry) ||
|
||||
isEyeSleeping(expression.left) ||
|
||||
isEyeSleeping(expression.right)
|
||||
(getEyeOpenness(expression.left) === 0) ||
|
||||
(getEyeOpenness(expression.right) === 0)
|
||||
) {
|
||||
if (expression.muzzle === Muzzle.SmilePant || expression.muzzle === Muzzle.NeutralPant) {
|
||||
expression.muzzle = Muzzle.Neutral;
|
||||
|
||||
@@ -142,7 +142,7 @@ const exampleCM = [
|
||||
BLUE, BLUE, BLUE, BLUE, BLUE,
|
||||
];
|
||||
|
||||
const frontLegsCount = 39;
|
||||
const frontLegsCount = 40;
|
||||
const backLegsCount = 27;
|
||||
|
||||
const frontLegsSheet = {
|
||||
|
||||
@@ -26,7 +26,7 @@ h1(focusTitle) Account settings
|
||||
| Changes saved
|
||||
.form-group.text-right
|
||||
button.btn.btn-default(type="submit" [disabled]="!canSubmit" style="min-width: 100px;")
|
||||
| Save
|
||||
| {{saveButtonText}}
|
||||
|
||||
div(style="margin-bottom: 100px;")
|
||||
.form-group
|
||||
|
||||
@@ -8,6 +8,7 @@ import { oauthProviders } from '../../../client/data';
|
||||
import { Model } from '../../services/model';
|
||||
import { getProviderIcon } from '../../shared/sign-in-box/sign-in-box';
|
||||
import { faStar, faExclamationCircle, faSync } from '../../../client/icons';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'account',
|
||||
@@ -33,9 +34,10 @@ export class Account implements OnInit, OnDestroy {
|
||||
removedAccount?: boolean;
|
||||
accountError?: string;
|
||||
accountSaved = false;
|
||||
isNewAccount = false;
|
||||
hides: HiddenPlayer[] | undefined = undefined;
|
||||
page = 0;
|
||||
constructor(private model: Model) {
|
||||
constructor(private model: Model, private router: Router) {
|
||||
}
|
||||
ngOnInit() {
|
||||
const account = this.account!;
|
||||
@@ -44,6 +46,7 @@ export class Account implements OnInit, OnDestroy {
|
||||
name: account.name,
|
||||
birthdate: account.birthdate,
|
||||
};
|
||||
this.isNewAccount = account.birthdate === '';
|
||||
|
||||
this.pageChanged();
|
||||
}
|
||||
@@ -88,6 +91,9 @@ export class Account implements OnInit, OnDestroy {
|
||||
get showAccountAlert() {
|
||||
return this.model.missingBirthdate;
|
||||
}
|
||||
get saveButtonText() {
|
||||
return this.isNewAccount ? 'Save and continue' : 'Save';
|
||||
}
|
||||
icon(id: string) {
|
||||
return getProviderIcon(id);
|
||||
}
|
||||
@@ -98,6 +104,9 @@ export class Account implements OnInit, OnDestroy {
|
||||
this.model.updateAccount(this.data)
|
||||
.catch((e: Error) => this.accountError = e.message)
|
||||
.then(() => this.accountSaved = true);
|
||||
if (this.isNewAccount) {
|
||||
this.router.navigate(['home']);
|
||||
}
|
||||
}
|
||||
}
|
||||
removeSite(site: SocialSiteInfo) {
|
||||
|
||||
@@ -205,13 +205,15 @@ h1(focusTitle) Help
|
||||
p.text-fading.
|
||||
This is usually caused by outdated graphics drivers or browser.
|
||||
Check if your browser is #[a(href="https://updatemybrowser.org/") up to date] and check
|
||||
if there are new updates for your graphics card or device.
|
||||
if there are new updates for your graphics driver or your device.
|
||||
p.text-fading.
|
||||
If your browser is already up to date, try installing different browser.
|
||||
We recommend using #[a(href="https://www.google.com/chrome/") Chrome]
|
||||
or #[a(href="https://www.mozilla.org/en-US/firefox/new/") Firefox].
|
||||
p.text-fading.
|
||||
Some older devices don't support necessary functions and will never work with the game.
|
||||
If other workarounds didn't help, switching Graphics Quality to Low usually works.
|
||||
You'll find that option in the game settings. After switching to Low, it
|
||||
might take a minute for the screen to stop glitching.
|
||||
|
||||
section
|
||||
h4 Graphical glitches, disappearing objects
|
||||
|
||||
@@ -83,6 +83,13 @@ export class ActionBar {
|
||||
this.closeActions();
|
||||
}
|
||||
else {
|
||||
// if the action menu was opened from the settings dropdown, just do nothing
|
||||
// it means you can't close that menu by clicking on this button, but it's
|
||||
// a minor issue
|
||||
if (document.body.classList.contains('actions-modal-opened')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isWaitingForActionsModal = true;
|
||||
this.modalRef = this.modalService.show(this.actionsModal, { ignoreBackdropClick: true });
|
||||
}
|
||||
|
||||
@@ -94,6 +94,7 @@ export class ActionsModal implements OnInit, OnDestroy {
|
||||
this.game.editingActions = false;
|
||||
clearInterval(this.interval);
|
||||
this.subscription && this.subscription.unsubscribe();
|
||||
this.close.emit(); // need to emit in case the menu wasn't closed with the Close button
|
||||
this.notify.emit();
|
||||
}
|
||||
ok() {
|
||||
|
||||
@@ -102,8 +102,8 @@
|
||||
.form-group
|
||||
custom-checkbox(
|
||||
[(checked)]="account.filterCyrillic"
|
||||
help="Hide all messages containing russian alphabet")
|
||||
| Filter russian chat
|
||||
help="Hide all messages containing Russian alphabet")
|
||||
| Filter Russian chat
|
||||
.form-group
|
||||
custom-checkbox(
|
||||
[(checked)]="account.filterSwearWords"
|
||||
|
||||
@@ -112,6 +112,12 @@ ng-template(#shareLinkPopover)
|
||||
.form-group
|
||||
button.btn.btn-sm.ml-1([(ngModel)]="animation.loop" btnCheckbox [btnHighlight]="animation.loop")
|
||||
| loop
|
||||
.form-group(*ngIf="mode !== 'body'")
|
||||
button.btn.btn-sm.ml-1([(ngModel)]="animation.dontOpenEyes" btnCheckbox [btnHighlight]="animation.dontOpenEyes")
|
||||
| dontOpenEyes
|
||||
.form-group(*ngIf="mode !== 'body'")
|
||||
button.btn.btn-sm.ml-1([(ngModel)]="animation.dontCloseMouth" btnCheckbox [btnHighlight]="animation.dontCloseMouth")
|
||||
| dontCloseMouth
|
||||
.btn-group.ml-1(dropdown)
|
||||
button.btn.btn-sm.btn-default.dropdown-toggle(dropdownToggle title="Before animation")
|
||||
| before: {{beforeAnimation?.name || 'none'}}
|
||||
|
||||
@@ -7,9 +7,9 @@ import {
|
||||
BodyAnimationFrame as IBodyAnimationFrame,
|
||||
HeadAnimation as IHeadAnimation,
|
||||
HeadAnimationFrame as IHeadAnimationFrame,
|
||||
ColorExtraSet, PonyInfo, PonyObject, BodyShadow, PonyEye
|
||||
ColorExtraSet, PonyInfo, PonyObject, BodyShadow, PonyEye, HeadAnimationProperties
|
||||
} from '../../../common/interfaces';
|
||||
import { removeItem, repeat, isKeyEventInvalid, cloneDeep, array } from '../../../common/utils';
|
||||
import { removeItem, repeat, isKeyEventInvalid, cloneDeep, array, hasFlag } from '../../../common/utils';
|
||||
import { toPalette, createDefaultPony, syncLockedPonyInfo } from '../../../common/ponyInfo';
|
||||
import { Key } from '../../../client/input/input';
|
||||
import { defaultPonyState, defaultDrawPonyOptions } from '../../../client/ponyHelpers';
|
||||
@@ -63,6 +63,8 @@ interface BodyAnimation extends BaseAnimation {
|
||||
interface HeadAnimation extends BaseAnimation {
|
||||
lockEyes?: boolean;
|
||||
frames: HeadAnimationFrame[];
|
||||
dontOpenEyes?: boolean;
|
||||
dontCloseMouth?: boolean;
|
||||
}
|
||||
|
||||
interface AnimationsData {
|
||||
@@ -201,6 +203,13 @@ export class ToolsAnimation implements OnInit, OnDestroy {
|
||||
if (!this.playing) {
|
||||
if (this.mode === 'body') {
|
||||
this.state.animationFrame = this.frame;
|
||||
|
||||
if (this.state.headAnimation) {
|
||||
const headFrames = this.state.headAnimation.frames.length;
|
||||
let headFrame = Math.floor(this.frame / this.state.animation.frames.length * headFrames);
|
||||
headFrame = Math.min(headFrame, headFrames - 1);
|
||||
this.state.headAnimationFrame = headFrame;
|
||||
}
|
||||
} else {
|
||||
this.state.headAnimationFrame = this.frame;
|
||||
}
|
||||
@@ -459,9 +468,10 @@ export class ToolsAnimation implements OnInit, OnDestroy {
|
||||
console.log(`shadow: [${shadow.map(x => `[${x.join(', ')}]`).join(', ')}]`);
|
||||
}
|
||||
} else {
|
||||
const animation = toHeadAnimation(this.headAnimation, true);
|
||||
const compressed = animation.frames.map(compressHeadFrame);
|
||||
console.log(JSON.stringify(compressed));
|
||||
const frames = this.headAnimation.frames
|
||||
.map(f => [f.duration, '[' + compressHeadFrame(f).join(', ') + ']'])
|
||||
.map(([repeat, frame]) => repeat > 1 ? `...repeat(${repeat}, ${frame})` : frame);
|
||||
console.log(`frames: [\n${frames.map(x => `\t${x}`).join(',\n')}\n]`);
|
||||
}
|
||||
}
|
||||
png(scale = 1) {
|
||||
@@ -531,9 +541,11 @@ export class ToolsAnimation implements OnInit, OnDestroy {
|
||||
if (this.playing) {
|
||||
this.time += delta;
|
||||
|
||||
if (this.mode === 'body') {
|
||||
const isBodyMode = this.mode === 'body';
|
||||
|
||||
if (isBodyMode) {
|
||||
if (this.state.animation) {
|
||||
const frame = this.time * this.state.animation.fps;
|
||||
const frame = Math.floor(this.time * this.state.animation.fps);
|
||||
|
||||
if (frame > this.state.animation.frames.length && !this.state.animation.loop) {
|
||||
this.bodyAnimationPlaying = (this.bodyAnimationPlaying + 1) % this.bodyAnimationsToPlay.length;
|
||||
@@ -541,14 +553,15 @@ export class ToolsAnimation implements OnInit, OnDestroy {
|
||||
this.state.animationFrame = 0;
|
||||
this.time = 0;
|
||||
} else {
|
||||
this.state.animationFrame = Math.floor(frame) % this.state.animation.frames.length;
|
||||
this.state.animationFrame = frame % this.state.animation.frames.length;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (this.state.headAnimation) {
|
||||
const frame = Math.floor(this.time * this.state.headAnimation.fps);
|
||||
this.state.headAnimationFrame = frame % this.state.headAnimation.frames.length;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.state.headAnimation) {
|
||||
const fps = this.state.headAnimation.fps;
|
||||
const frame = Math.floor(this.time * fps) + (isBodyMode && !this.state.headAnimation.loop ? fps : 0);
|
||||
this.state.headAnimationFrame = frame % this.state.headAnimation.frames.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -564,9 +577,10 @@ export class ToolsAnimation implements OnInit, OnDestroy {
|
||||
return this.storage.getJSON<AnimationsData>('tools-animations', {});
|
||||
}
|
||||
private createAnimationSprites(scale: number) {
|
||||
const isBodyMode = this.mode === 'body';
|
||||
const animation = toBodyAnimation(this.bodyAnimation, true, this.switch);
|
||||
const headAnimation = toHeadAnimation(this.headAnimation, true);
|
||||
const frames = this.mode === 'body' ? animation.frames.length : headAnimation.frames.length;
|
||||
const frames = isBodyMode ? animation.frames.length : headAnimation.frames.length;
|
||||
const buffer = createCanvas(ponyWidth, ponyHeight);
|
||||
const batch = new ContextSpriteBatch(buffer);
|
||||
const info = toPalette(this.pony.info);
|
||||
@@ -582,14 +596,23 @@ export class ToolsAnimation implements OnInit, OnDestroy {
|
||||
const x = i % cols;
|
||||
const y = Math.floor(i / cols);
|
||||
|
||||
let headFrame = 0;
|
||||
if (isBodyMode) {
|
||||
let animTime = i / animation.fps;
|
||||
if (!headAnimation.loop) {
|
||||
animTime += 1;
|
||||
}
|
||||
headFrame = Math.floor(animTime * headAnimation.fps);
|
||||
}
|
||||
|
||||
batch.start(sprites.paletteSpriteSheet, 0);
|
||||
|
||||
drawPony(batch, info, {
|
||||
...defaultPonyState(),
|
||||
animation,
|
||||
animationFrame: this.mode === 'body' ? i : 0,
|
||||
headAnimation: this.mode === 'head' ? headAnimation : undefined,
|
||||
headAnimationFrame: this.mode === 'head' ? i : 0,
|
||||
animationFrame: isBodyMode ? i : 0,
|
||||
headAnimation: headAnimation,
|
||||
headAnimationFrame: isBodyMode ? headFrame : i,
|
||||
blinkFrame: 1,
|
||||
}, ponyWidth / 2, ponyHeight - 10, options);
|
||||
|
||||
@@ -696,7 +719,7 @@ function compressBodyFrame(f: BodyAnimationFrame): number[] {
|
||||
], x => !x);
|
||||
}
|
||||
|
||||
function fromHeadAnimation({ name, fps, loop, frames }: IHeadAnimation, index: number): HeadAnimation {
|
||||
function fromHeadAnimation({ name, fps, loop, properties, frames }: IHeadAnimation, index: number): HeadAnimation {
|
||||
const fs: HeadAnimationFrame[] = [];
|
||||
|
||||
frames.forEach(f => {
|
||||
@@ -713,18 +736,30 @@ function fromHeadAnimation({ name, fps, loop, frames }: IHeadAnimation, index: n
|
||||
builtin: true,
|
||||
fps,
|
||||
loop,
|
||||
dontOpenEyes: hasFlag(properties, HeadAnimationProperties.DontIncreaseEyeOpenness),
|
||||
dontCloseMouth: hasFlag(properties, HeadAnimationProperties.DontDecreaseMouthOpenness),
|
||||
name: `# builtin ${index.toString().padStart(2, '0')} # ${name}`,
|
||||
frames: fs,
|
||||
};
|
||||
}
|
||||
|
||||
function toHeadAnimation({ name, frames, fps, loop }: HeadAnimation, full: boolean): IHeadAnimation {
|
||||
function toHeadAnimation({ name, frames, fps, loop, dontOpenEyes, dontCloseMouth }: HeadAnimation, full: boolean):
|
||||
IHeadAnimation {
|
||||
const fs = (full && !loop) ? repeat(fps, createDefaultHeadFrame()).concat(frames) : frames;
|
||||
|
||||
let properties = HeadAnimationProperties.None;
|
||||
if (dontOpenEyes) {
|
||||
properties |= HeadAnimationProperties.DontIncreaseEyeOpenness;
|
||||
}
|
||||
if (dontCloseMouth) {
|
||||
properties |= HeadAnimationProperties.DontDecreaseMouthOpenness;
|
||||
}
|
||||
|
||||
return {
|
||||
name,
|
||||
fps,
|
||||
loop,
|
||||
properties,
|
||||
frames: flatMap(fs, f => repeat(full ? f.duration : 1, f)),
|
||||
};
|
||||
}
|
||||
@@ -788,6 +823,7 @@ function fixHeadAnimation(a: HeadAnimation): HeadAnimation {
|
||||
fps: a.fps || 24,
|
||||
loop: a.loop || false,
|
||||
lockEyes: a.lockEyes || false,
|
||||
dontOpenEyes: a.dontOpenEyes || false,
|
||||
frames: (a.frames || []).map(fixHeadFrame),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import { createCamera } from '../../../common/camera';
|
||||
import { mockPaletteManager } from '../../../common/ponyInfo';
|
||||
import { isCritter } from '../../../common/entityUtils';
|
||||
import {
|
||||
createAnEntity, cloud, pony, apple, apple2, orange, orange2, candy, gift1, gift2, appleGreen, appleGreen2
|
||||
createAnEntity, cloud, pony, apple, apple2, orange, orange2, candy, gift1, gift2, appleGreen, appleGreen2, bunny
|
||||
} from '../../../common/entities';
|
||||
import { drawMap } from '../../../client/draw';
|
||||
import { includes, observableToPromise, hasFlag } from '../../../common/utils';
|
||||
@@ -142,7 +142,7 @@ function drawTheMap(canvas: HTMLCanvasElement, map: WorldMap, info: ToolsMapOthe
|
||||
};
|
||||
|
||||
const ignoreTypes = [
|
||||
cloud, pony, apple, apple2, appleGreen, appleGreen2, orange, orange2, candy, gift1, gift2
|
||||
cloud, pony, apple, apple2, appleGreen, appleGreen2, orange, orange2, candy, gift1, gift2, bunny
|
||||
].map(e => e.type);
|
||||
|
||||
const shouldDraw = (e: Entity) => {
|
||||
|
||||
+2307
-2303
File diff suppressed because one or more lines are too long
@@ -258,6 +258,7 @@ export function createCommands(world: World): Command[] {
|
||||
action(['sneeze', 'achoo'], Action.Sneeze),
|
||||
action(['excite', 'tada'], Action.Excite),
|
||||
action(['magic'], Action.Magic),
|
||||
action(['kiss'], Action.Kiss),
|
||||
|
||||
// house
|
||||
command(['savehouse'], '/savehouse - saves current house setup', '', async ({ }, client) => {
|
||||
@@ -325,18 +326,22 @@ export function createCommands(world: World): Command[] {
|
||||
client.reporter.systemLog(`House unlocked`);
|
||||
}),
|
||||
command(['removetoolbox'], '/removetoolbox - removes toolbox from the house', '', ({ world }, client) => {
|
||||
if (!isValidMapForEditing(client.map, client, false, true))
|
||||
if (!isValidMapForEditing(client.map, client, true, true))
|
||||
return;
|
||||
|
||||
client.lastMapLoadOrSave = Date.now();
|
||||
|
||||
removeToolbox(world, client.map);
|
||||
|
||||
saySystem(client, 'Toolbox removed');
|
||||
client.reporter.systemLog(`Toolbox removed`);
|
||||
}),
|
||||
command(['restoretoolbox'], '/restoretoolbox - restores toolbox to the house', '', ({ }, client) => {
|
||||
if (!isValidMapForEditing(client.map, client, false, true))
|
||||
if (!isValidMapForEditing(client.map, client, true, true))
|
||||
return;
|
||||
|
||||
client.lastMapLoadOrSave = Date.now();
|
||||
|
||||
restoreToolbox(world, client.map);
|
||||
|
||||
saySystem(client, 'Toolbox restored');
|
||||
|
||||
@@ -10,8 +10,8 @@ import {
|
||||
} from '../common/utils';
|
||||
import { CharacterState, ServerConfig, AccountState, CharacterStateFlags, GameServerSettings } from '../common/adminInterfaces';
|
||||
import {
|
||||
EntityState, Expression, PonyOptions, Action, ExpressionExtra, Eye, Muzzle, CLOSED_MUZZLES,
|
||||
isExpressionAction, EntityPlayerState, UpdateFlags, InteractAction
|
||||
EntityState, Expression, PonyOptions, Action, ExpressionExtra, Eye, Muzzle, getMuzzleOpenness,
|
||||
isExpressionAction, EntityPlayerState, UpdateFlags, InteractAction, Rect
|
||||
} from '../common/interfaces';
|
||||
import { encodeExpression, EMPTY_EXPRESSION, decodeExpression } from '../common/encoders/expressionEncoder';
|
||||
import { EXPRESSION_TIMEOUT, DAY, FLY_DELAY, SECOND, PONY_TYPE } from '../common/constants';
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
import { replaceEmojis } from '../client/emoji';
|
||||
import { expression, parseExpression } from '../common/expressionUtils';
|
||||
import {
|
||||
canBoop2, isPonySitting, isPonyStanding, getBoopRect, canStand, isPonyFlying, setPonyState, canSit, canLie
|
||||
canBoopOrKiss, isPonySitting, isPonyStanding, getBoopRect, canStand, isPonyFlying, setPonyState, canSit, canLie, getkissRect, getSneezeRect
|
||||
} from '../common/entityUtils';
|
||||
import { withBorder } from '../common/rect';
|
||||
import { isOnlineFriend } from './services/friends';
|
||||
@@ -75,6 +75,17 @@ export function updateClientCharacter(client: IClient, character: ICharacter) {
|
||||
client.characterName = replaceEmojis(client.character.name);
|
||||
}
|
||||
|
||||
function isMobileUserAgent(userAgent?: string) {
|
||||
if (!userAgent) {
|
||||
return false;
|
||||
}
|
||||
return userAgent.includes('Android') ||
|
||||
userAgent.includes('iPhone') ||
|
||||
userAgent.includes('iPad') ||
|
||||
userAgent.includes('iPod') ||
|
||||
userAgent.includes('Windows Phone');
|
||||
}
|
||||
|
||||
export function createClient(
|
||||
client: IClient, account: IAccount, friends: string[], hides: string[], character: ICharacter, pony: ServerEntity,
|
||||
defaultMap: ServerMap, reporter: Reporter, origin: IOriginInfo | undefined
|
||||
@@ -85,6 +96,7 @@ export function createClient(
|
||||
client.country = origin && origin.country || '??';
|
||||
client.userAgent = client.originalRequest && client.originalRequest.headers['user-agent'];
|
||||
|
||||
client.isMobile = isMobileUserAgent(client.userAgent);
|
||||
client.accountId = account._id.toString();
|
||||
client.accountName = account.name;
|
||||
client.ignores = new Set(account.ignores);
|
||||
@@ -117,8 +129,7 @@ export function createClient(
|
||||
client.safeY = pony.y;
|
||||
|
||||
client.lastPacket = Date.now();
|
||||
client.lastAction = 0;
|
||||
client.lastBoopAction = 0;
|
||||
client.lastBoopOrKissAction = 0;
|
||||
client.lastExpressionAction = 0;
|
||||
client.lastSays = [];
|
||||
client.lastX = pony.x;
|
||||
@@ -307,7 +318,7 @@ export function parseOrCurrentExpression(pony: ServerEntity, message: string) {
|
||||
export function playerSleep(pony: ServerEntity, args = '') {
|
||||
if (pony.vx === 0 && pony.vy === 0) {
|
||||
const base = parseOrCurrentExpression(pony, args) || expression(Eye.Closed, Eye.Closed, Muzzle.Neutral);
|
||||
const muzzle = CLOSED_MUZZLES.indexOf(base.muzzle) !== -1 ? base.muzzle : Muzzle.Neutral;
|
||||
const muzzle = getMuzzleOpenness(base.muzzle) === 0 ? base.muzzle : Muzzle.Neutral;
|
||||
const expr = { ...base, muzzle, left: Eye.Closed, right: Eye.Closed, extra: ExpressionExtra.Zzz };
|
||||
setEntityExpression(pony, expr, 0, true);
|
||||
}
|
||||
@@ -397,7 +408,8 @@ export function useHeldItem(client: IClient) {
|
||||
}
|
||||
|
||||
export function canPerformAction(client: IClient) {
|
||||
return client.lastAction < Date.now();
|
||||
const now = Date.now();
|
||||
return client.lastExpressionAction < now && client.lastBoopOrKissAction < now;
|
||||
}
|
||||
|
||||
export function updateEntityPlayerState(client: IClient, entity: ServerEntity) {
|
||||
@@ -416,53 +428,72 @@ export function turnHead(client: IClient) {
|
||||
const purpleGrapeTypes = entities.grapesPurple.map(x => x.type);
|
||||
const greenGrapeTypes = entities.grapesGreen.map(x => x.type);
|
||||
|
||||
export function boop(client: IClient, now: number) {
|
||||
if (canPerformAction(client) && canBoop2(client.pony) && client.lastBoopAction < now) {
|
||||
cancelEntityExpression(client.pony);
|
||||
sendAction(client.pony, Action.Boop);
|
||||
function boopEntity(client: IClient, rect: Rect, isOnlyBooping: boolean) {
|
||||
if (!client.shadowed && (isPonySitting(client.pony) || isPonyStanding(client.pony))) {
|
||||
const boopBounds = withBorder(rect, 1);
|
||||
const entities = findEntitiesInBounds(client.map, boopBounds);
|
||||
const entity = entities.find(e => canBoopEntity(e, rect));
|
||||
|
||||
if (!client.shadowed && (isPonySitting(client.pony) || isPonyStanding(client.pony))) {
|
||||
const boopRect = getBoopRect(client.pony);
|
||||
const boopBounds = withBorder(boopRect, 1);
|
||||
const entities = findEntitiesInBounds(client.map, boopBounds);
|
||||
const entity = entities.find(e => canBoopEntity(e, boopRect));
|
||||
if (entity) {
|
||||
if (entity.boop) {
|
||||
entity.boop(client);
|
||||
} else if (!isOnlyBooping && entity.type === PONY_TYPE) {
|
||||
const clientHold = client.pony.options!.hold || 0;
|
||||
|
||||
if (entity) {
|
||||
if (entity.boop) {
|
||||
entity.boop(client);
|
||||
} else if (entity.type === PONY_TYPE) {
|
||||
const clientHold = client.pony.options!.hold || 0;
|
||||
if (isHoldingGrapes(entity) && clientHold !== grapeGreen.type && clientHold !== grapePurple.type) {
|
||||
let index = purpleGrapeTypes.indexOf(entity.options!.hold || 0);
|
||||
|
||||
if (isHoldingGrapes(entity) && clientHold !== grapeGreen.type && clientHold !== grapePurple.type) {
|
||||
let index = purpleGrapeTypes.indexOf(entity.options!.hold || 0);
|
||||
if (index !== -1) {
|
||||
holdItem(client.pony, grapePurple.type);
|
||||
|
||||
if (index === (purpleGrapeTypes.length - 1)) {
|
||||
unholdItem(entity);
|
||||
} else {
|
||||
holdItem(entity, purpleGrapeTypes[index + 1]);
|
||||
}
|
||||
} else {
|
||||
let index = greenGrapeTypes.indexOf(entity.options!.hold || 0);
|
||||
|
||||
if (index !== -1) {
|
||||
holdItem(client.pony, grapePurple.type);
|
||||
holdItem(client.pony, grapeGreen.type);
|
||||
|
||||
if (index === (purpleGrapeTypes.length - 1)) {
|
||||
if (index === (greenGrapeTypes.length - 1)) {
|
||||
unholdItem(entity);
|
||||
} else {
|
||||
holdItem(entity, purpleGrapeTypes[index + 1]);
|
||||
}
|
||||
} else {
|
||||
let index = greenGrapeTypes.indexOf(entity.options!.hold || 0);
|
||||
|
||||
if (index !== -1) {
|
||||
holdItem(client.pony, grapeGreen.type);
|
||||
|
||||
if (index === (greenGrapeTypes.length - 1)) {
|
||||
unholdItem(entity);
|
||||
} else {
|
||||
holdItem(entity, greenGrapeTypes[index + 1]);
|
||||
}
|
||||
holdItem(entity, greenGrapeTypes[index + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.lastBoopAction = now + 500;
|
||||
export function boop(client: IClient, now: number) {
|
||||
if (canPerformAction(client) && canBoopOrKiss(client.pony)) {
|
||||
cancelEntityExpression(client.pony);
|
||||
sendAction(client.pony, Action.Boop);
|
||||
boopEntity(client, getBoopRect(client.pony), false);
|
||||
client.lastBoopOrKissAction = now + 850;
|
||||
}
|
||||
}
|
||||
|
||||
export function kiss(client: IClient, now: number) {
|
||||
if (canPerformAction(client) && canBoopOrKiss(client.pony)) {
|
||||
cancelEntityExpression(client.pony);
|
||||
sendAction(client.pony, Action.Kiss);
|
||||
boopEntity(client, getkissRect(client.pony), false);
|
||||
client.lastBoopOrKissAction = now + 3350;
|
||||
}
|
||||
}
|
||||
|
||||
export function sneeze(client: IClient) {
|
||||
if (canPerformAction(client)) {
|
||||
cancelEntityExpression(client.pony);
|
||||
sendAction(client.pony, Action.Sneeze);
|
||||
boopEntity(client, getSneezeRect(client.pony), true);
|
||||
client.lastExpressionAction = Date.now() + 750;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,10 +557,10 @@ export function fly(client: IClient) {
|
||||
}
|
||||
|
||||
export function expressionAction(client: IClient, action: Action) {
|
||||
if (canPerformAction(client) && isExpressionAction(action) && client.lastExpressionAction < Date.now()) {
|
||||
if (canPerformAction(client) && isExpressionAction(action)) {
|
||||
cancelEntityExpression(client.pony);
|
||||
sendAction(client.pony, action);
|
||||
client.lastExpressionAction = Date.now() + 500;
|
||||
client.lastExpressionAction = Date.now() + 750;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -729,6 +760,9 @@ export function execAction(client: IClient, action: Action, settings: GameServer
|
||||
case Action.TurnHead:
|
||||
turnHead(client);
|
||||
break;
|
||||
case Action.Sneeze:
|
||||
sneeze(client);
|
||||
break;
|
||||
case Action.Stand:
|
||||
stand(client);
|
||||
break;
|
||||
@@ -766,6 +800,9 @@ export function execAction(client: IClient, action: Action, settings: GameServer
|
||||
updateEntityState(client.pony, setFlag(client.pony.state, EntityState.Magic, !has));
|
||||
}
|
||||
break;
|
||||
case Action.Kiss:
|
||||
kiss(client, Date.now());
|
||||
break;
|
||||
case Action.SwitchTool:
|
||||
switchTool(client, false);
|
||||
break;
|
||||
@@ -799,7 +836,9 @@ export function switchTool(client: IClient, reverse: boolean) {
|
||||
const newIndex = reverse ? (index === -1 ? tools.length - 1 : index - 1) : ((index + 1) % tools.length);
|
||||
const tool = tools[newIndex];
|
||||
holdItem(client.pony, tool.type);
|
||||
saySystem(client, tool.text);
|
||||
// console.log('isMobile ' + client.isMobile);
|
||||
const text = (client.isMobile && tool.textMobile) ? tool.textMobile : tool.text;
|
||||
saySystem(client, text);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,8 +50,9 @@ export default function (server: ServerConfig, settings: Settings, world: World
|
||||
const header = 'data:image/gif;base64,';
|
||||
const buffer = Buffer.from(image.substr(header.length), 'base64');
|
||||
const magick = /^win/.test(process.platform) ? 'magick' : 'convert';
|
||||
const command = `${magick} -dispose 3 -delay ${100 / fps} -loop 0 "${filePath}" -crop ${width}x${height} `
|
||||
const command = `${magick} -dispose Background -delay ${100 / fps} -loop 0 "${filePath}" -crop ${width}x${height} `
|
||||
+ `+repage${repeat(' +delete', remove)} "${filePath.replace(/png$/, 'gif')}"`;
|
||||
console.log('executing ' + command);
|
||||
|
||||
fs.writeFileAsync(filePath, buffer)
|
||||
.then(() => execAsync(command))
|
||||
|
||||
@@ -156,6 +156,7 @@ export interface IClient extends ClientActions, ClientExtensions {
|
||||
characterName: string;
|
||||
character: ICharacter;
|
||||
|
||||
isMobile: boolean;
|
||||
isMod: boolean;
|
||||
shadowed: boolean;
|
||||
supporterLevel: number;
|
||||
@@ -181,8 +182,7 @@ export interface IClient extends ClientActions, ClientExtensions {
|
||||
safeX: number;
|
||||
safeY: number;
|
||||
lastPacket: number;
|
||||
lastAction: number;
|
||||
lastBoopAction: number;
|
||||
lastBoopOrKissAction: number;
|
||||
lastExpressionAction: number;
|
||||
lastSays: LastSay[];
|
||||
lastX: number;
|
||||
@@ -278,6 +278,7 @@ export interface Controller {
|
||||
update(delta: number, now: number): void;
|
||||
sparseUpdate?(): void;
|
||||
toggleWall?(x: number, y: number, type: TileType): void;
|
||||
removeWalls?(): void;
|
||||
}
|
||||
|
||||
export interface AccountService {
|
||||
|
||||
+13
-17
@@ -138,6 +138,13 @@ export class World {
|
||||
}
|
||||
}
|
||||
}
|
||||
removeWalls(map: ServerMap) {
|
||||
for (const controller of map.controllers) {
|
||||
if (controller.removeWalls) {
|
||||
controller.removeWalls();
|
||||
}
|
||||
}
|
||||
}
|
||||
getState(): WorldState {
|
||||
return {
|
||||
time: this.time,
|
||||
@@ -291,22 +298,13 @@ export class World {
|
||||
const nowSeconds = now / 1000;
|
||||
const deltaSeconds = delta / 1000;
|
||||
|
||||
timingStart('update tiles');
|
||||
timingStart('update tiles and colliders');
|
||||
for (const map of this.maps) {
|
||||
if (!map.dontUpdateTilesAndColliders) {
|
||||
for (const region of map.regions) {
|
||||
if (region.tilesDirty) {
|
||||
updateTileIndices(region, map);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
timingEnd();
|
||||
|
||||
timingStart('update colliders');
|
||||
for (const map of this.maps) {
|
||||
if (!map.dontUpdateTilesAndColliders) {
|
||||
for (const region of map.regions) {
|
||||
if (region.colliderDirty) {
|
||||
generateRegionCollider(region, map);
|
||||
}
|
||||
@@ -325,9 +323,7 @@ export class World {
|
||||
|
||||
if (delta > 0) {
|
||||
if (entity.vx !== 0 || entity.vy !== 0) {
|
||||
timingStart('updatePosition()');
|
||||
updatePosition(entity, delta, map);
|
||||
timingEnd();
|
||||
}
|
||||
|
||||
entity.timestamp = nowSeconds;
|
||||
@@ -370,15 +366,15 @@ export class World {
|
||||
timingEnd();
|
||||
|
||||
timingStart('timeoutEntityExpression + inTheAirDelay');
|
||||
for (const { pony } of this.clients) {
|
||||
for (const client of this.clients) {
|
||||
// timeout expressions
|
||||
if (pony.exprTimeout && pony.exprTimeout < now) {
|
||||
setEntityExpression(pony, undefined); // NOTE: creates updates
|
||||
if (client.pony.exprTimeout && client.pony.exprTimeout < now) {
|
||||
setEntityExpression(client.pony, undefined); // NOTE: creates updates
|
||||
}
|
||||
|
||||
// count down in-the-air delay
|
||||
if (pony.inTheAirDelay !== undefined && pony.inTheAirDelay > 0) {
|
||||
pony.inTheAirDelay -= deltaSeconds;
|
||||
if (client.pony.inTheAirDelay !== undefined && client.pony.inTheAirDelay > 0) {
|
||||
client.pony.inTheAirDelay -= deltaSeconds;
|
||||
}
|
||||
}
|
||||
timingEnd();
|
||||
|
||||
@@ -94,7 +94,6 @@ export function mockClient(fields: any = {}): IClient {
|
||||
subscribes: [],
|
||||
saysQueue: [],
|
||||
lastSays: [],
|
||||
lastAction: 0,
|
||||
lastBoopAction: 0,
|
||||
lastExpressionAction: 0,
|
||||
viewWidth: 3,
|
||||
|
||||
@@ -132,7 +132,6 @@ describe('playerUtils', () => {
|
||||
safeX: 10,
|
||||
safeY: 20,
|
||||
lastPacket: 123,
|
||||
lastAction: 0,
|
||||
lastBoopAction: 0,
|
||||
lastExpressionAction: 0,
|
||||
lastX: 10,
|
||||
@@ -516,11 +515,11 @@ describe('playerUtils', () => {
|
||||
|
||||
describe('canPerformAction()', () => {
|
||||
it('returns true if last action date is below current time', () => {
|
||||
expect(canPerformAction(mockClient({ lastAction: 1234 }))).true;
|
||||
expect(canPerformAction(mockClient({ lastBoopOrKissAction: 1234 }))).true;
|
||||
});
|
||||
|
||||
it('returns false if last action date is ahead or current time', () => {
|
||||
expect(canPerformAction(mockClient({ lastAction: Date.now() + 1000 }))).false;
|
||||
expect(canPerformAction(mockClient({ lastExpressionAction: Date.now() + 1000 }))).false;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -563,7 +562,8 @@ describe('playerUtils', () => {
|
||||
client = mockClient();
|
||||
client.map = createServerMap('foo', 0, 1, 1);
|
||||
client.pony.region = client.map.regions[0];
|
||||
client.lastAction = 0;
|
||||
client.lastExpressionAction = 0;
|
||||
client.lastBoopOrKissAction = 0;
|
||||
});
|
||||
|
||||
it('sends boop action', () => {
|
||||
@@ -586,12 +586,12 @@ describe('playerUtils', () => {
|
||||
expect(client.pony.options!.expr).equal(EMPTY_EXPRESSION);
|
||||
});
|
||||
|
||||
it('updates last boop action', () => {
|
||||
client.lastBoopAction = 0;
|
||||
it('updates last boop/kiss action', () => {
|
||||
client.lastBoopOrKissAction = 0;
|
||||
|
||||
boop(client, 100);
|
||||
|
||||
expect(client.lastBoopAction).equal(100 + 500);
|
||||
expect(client.lastBoopOrKissAction).equal(100 + 500);
|
||||
});
|
||||
|
||||
it('executes boop on found entity', () => {
|
||||
@@ -618,7 +618,7 @@ describe('playerUtils', () => {
|
||||
});
|
||||
|
||||
it('does nothing if cannot perform action', () => {
|
||||
client.lastAction = 1000;
|
||||
client.lastBoopOrKissAction = 1000;
|
||||
|
||||
boop(client, 0);
|
||||
|
||||
@@ -646,7 +646,7 @@ describe('playerUtils', () => {
|
||||
|
||||
it('does not update flags if cannot perform action', () => {
|
||||
const client = mockClient();
|
||||
client.lastAction = Date.now() + 1000;
|
||||
client.lastBoopOrKissAction = Date.now() + 1000;
|
||||
client.pony.state = 0;
|
||||
|
||||
turnHead(client);
|
||||
@@ -703,7 +703,7 @@ describe('playerUtils', () => {
|
||||
});
|
||||
|
||||
it('does nothing if cannot perform action', () => {
|
||||
client.lastAction = Date.now() + 1000;
|
||||
client.lastBoopOrKissAction = Date.now() + 1000;
|
||||
client.pony.state = 0;
|
||||
|
||||
stand(client);
|
||||
@@ -752,7 +752,7 @@ describe('playerUtils', () => {
|
||||
});
|
||||
|
||||
it('does nothing if cannot perform action', () => {
|
||||
client.lastAction = Date.now() + 1000;
|
||||
client.lastBoopOrKissAction = Date.now() + 1000;
|
||||
client.pony.state = 0;
|
||||
|
||||
sit(client, {});
|
||||
@@ -796,7 +796,7 @@ describe('playerUtils', () => {
|
||||
});
|
||||
|
||||
it('does nothing if cannot perform action', () => {
|
||||
client.lastAction = Date.now() + 1000;
|
||||
client.lastBoopOrKissAction = Date.now() + 1000;
|
||||
client.pony.state = 0;
|
||||
|
||||
lie(client);
|
||||
@@ -844,7 +844,7 @@ describe('playerUtils', () => {
|
||||
});
|
||||
|
||||
it('does nothing if already flying', () => {
|
||||
client.lastAction = Date.now() + 1000;
|
||||
client.lastBoopOrKissAction = Date.now() + 1000;
|
||||
client.pony.state = EntityState.PonyFlying;
|
||||
|
||||
fly(client);
|
||||
@@ -854,7 +854,7 @@ describe('playerUtils', () => {
|
||||
});
|
||||
|
||||
it('does nothing if cannot perform action', () => {
|
||||
client.lastAction = Date.now() + 1000;
|
||||
client.lastBoopOrKissAction = Date.now() + 1000;
|
||||
client.pony.state = 0;
|
||||
|
||||
fly(client);
|
||||
@@ -896,7 +896,7 @@ describe('playerUtils', () => {
|
||||
});
|
||||
|
||||
it('does nothing if cannot perform action', () => {
|
||||
client.lastAction = Date.now() + 1000;
|
||||
client.lastExpressionAction = Date.now() + 1000;
|
||||
|
||||
expressionAction(client, Action.Yawn);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user