mirror of
https://github.com/Terncode/pixel.horse.git
synced 2026-09-24 13:55:04 +02:00
Archive update v0.55.0
This commit is contained in:
+1
-1
@@ -30,7 +30,7 @@ creator(s):
|
||||
SC (https://0somecunt0.tumblr.com/tagged/sfw)
|
||||
SailorDolpin (https://vk.com/id324582699)
|
||||
Deeraw (https://www.deviantart.com/deerdraw, https://twitter.com/TheOnlyDeeraw)
|
||||
Aviivix
|
||||
Aviivix
|
||||
SnowFl8keAnge1
|
||||
|
||||
|
||||
|
||||
@@ -276,6 +276,26 @@ Tools are accessible at `<base_url>/tools/` (only available in dev mode or when
|
||||
|
||||
## 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.
BIN
Binary file not shown.
BIN
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 } from './ponyAnimations';
|
||||
import { boop, defaultHeadFrame, stand, sneeze, yawn, lie, sit, fly, laugh, kiss } 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,
|
||||
@@ -117,10 +117,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 = [
|
||||
@@ -394,7 +391,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;
|
||||
@@ -519,6 +517,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);
|
||||
|
||||
@@ -38,6 +38,7 @@ export const CONTRIBUTORS: Contributors[] = [
|
||||
contributors: [
|
||||
{ name: 'CyberPon3', links: ['https://twitter.com/CyberPon3'] },
|
||||
{ name: 'Industrialice' },
|
||||
{ name: 'Stubenhocker', links: ['https://twitter.com/Stubenhocker13'] },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -76,6 +77,7 @@ export const CONTRIBUTORS: Contributors[] = [
|
||||
{ name: 'Deeraw', links: ['https://www.deviantart.com/deerdraw', 'https://twitter.com/TheOnlyDeeraw'] },
|
||||
{ name: 'Aviivix' },
|
||||
{ name: 'SnowFl8keAnge1' },
|
||||
{ name: '3aHo3a', links: ['http://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 } from './ponyAnimations';
|
||||
import { yawn, laugh, sneeze, kiss, kissFly, kissFlyBug } from './ponyAnimations';
|
||||
import {
|
||||
findEntityById, getRegionGlobal, setTile, removeEntity, addEntity, removeEntityDirectly, setRegion,
|
||||
addEntityToMapRegion, switchEntityRegion, getRegionUnsafe, addOrRemoveFromEntityList,
|
||||
@@ -415,6 +416,22 @@ export function handleAction(game: PonyTownGame, id: number, action: Action) {
|
||||
setHeadAnimation(pony, sneeze);
|
||||
}
|
||||
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 {
|
||||
@@ -1097,6 +1098,7 @@ export const enum Action {
|
||||
RequestEntityInfo,
|
||||
ACL,
|
||||
Magic,
|
||||
Kiss,
|
||||
RemoveEntity,
|
||||
PlaceEntity,
|
||||
SwitchTool,
|
||||
@@ -1219,6 +1221,7 @@ export interface SpriteSet<T> extends SpriteSetBase {
|
||||
}
|
||||
|
||||
export interface PonyInfoBase<T, SET> {
|
||||
//body: SET | undefined;
|
||||
head: SET | undefined;
|
||||
nose: SET | undefined;
|
||||
ears: SET | undefined;
|
||||
@@ -1385,10 +1388,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[];
|
||||
}
|
||||
|
||||
@@ -1546,10 +1556,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,
|
||||
@@ -1580,10 +1624,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) {
|
||||
|
||||
@@ -204,13 +204,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() {
|
||||
|
||||
@@ -92,8 +92,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';
|
||||
@@ -141,7 +141,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
@@ -275,6 +275,7 @@ export function createCommands(world: World): Command[] {
|
||||
action(['laugh', 'lol', 'haha', 'хаха', 'jaja'], Action.Laugh),
|
||||
action(['sneeze', 'achoo'], Action.Sneeze),
|
||||
action(['magic'], Action.Magic),
|
||||
action(['kiss'], Action.Kiss),
|
||||
|
||||
// house
|
||||
command(['savehouse'], '/savehouse - saves current house setup', '', async ({ }, client) => {
|
||||
@@ -342,18 +343,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