mirror of
https://github.com/Terncode/pixel.horse.git
synced 2026-09-25 14:15:51 +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)
|
SC (https://0somecunt0.tumblr.com/tagged/sfw)
|
||||||
SailorDolpin (https://vk.com/id324582699)
|
SailorDolpin (https://vk.com/id324582699)
|
||||||
Deeraw (https://www.deviantart.com/deerdraw, https://twitter.com/TheOnlyDeeraw)
|
Deeraw (https://www.deviantart.com/deerdraw, https://twitter.com/TheOnlyDeeraw)
|
||||||
Aviivix
|
Aviivix
|
||||||
SnowFl8keAnge1
|
SnowFl8keAnge1
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -276,6 +276,26 @@ Tools are accessible at `<base_url>/tools/` (only available in dev mode or when
|
|||||||
|
|
||||||
## Changelog
|
## 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
|
### Pony.Town v0.54.0
|
||||||
- Added a separate logo file for Visit Pony Town button
|
- Added a separate logo file for Visit Pony Town button
|
||||||
- Fixed users not being able to close actions menu in some cases
|
- 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",
|
"name": "pony-town",
|
||||||
"version": "0.54.0",
|
"version": "0.55.0",
|
||||||
"description": "A game of ponies building a town",
|
"description": "A game of ponies building a town",
|
||||||
"main": "pony-town.js",
|
"main": "pony-town.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { compact } from 'lodash';
|
|||||||
import {
|
import {
|
||||||
Expression, ExpressionButtonAction, CommandButtonAction, ActionButtonAction, ItemButtonAction,
|
Expression, ExpressionButtonAction, CommandButtonAction, ActionButtonAction, ItemButtonAction,
|
||||||
ColorShadow, Eye, Muzzle, Iris, ButtonActionSlot, ExpressionExtra, ButtonAction, ChatType, BodyAnimation,
|
ColorShadow, Eye, Muzzle, Iris, ButtonActionSlot, ExpressionExtra, ButtonAction, ChatType, BodyAnimation,
|
||||||
Action, isPartyChat, EntityButtonAction, defaultDrawOptions
|
Action, isPartyChat, EntityButtonAction, defaultDrawOptions, HeadAnimationProperties
|
||||||
} from '../common/interfaces';
|
} from '../common/interfaces';
|
||||||
import * as sprites from '../generated/sprites';
|
import * as sprites from '../generated/sprites';
|
||||||
import { createExpression } from './clientUtils';
|
import { createExpression } from './clientUtils';
|
||||||
@@ -11,7 +11,7 @@ import { PonyTownGame } from './game';
|
|||||||
import { boopAction, upAction, downAction, turnHeadAction } from './playerActions';
|
import { boopAction, upAction, downAction, turnHeadAction } from './playerActions';
|
||||||
import { ACTIONS_LIMIT, COMMAND_ACTION_TIME_DELAY } from '../common/constants';
|
import { ACTIONS_LIMIT, COMMAND_ACTION_TIME_DELAY } from '../common/constants';
|
||||||
import { cloneDeep, hasFlag } from '../common/utils';
|
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 { createDefaultPony, syncLockedPonyInfo, toPalette, mockPaletteManager } from '../common/ponyInfo';
|
||||||
import {
|
import {
|
||||||
ACTION_EXPRESSION_EYE_COLOR, ACTION_EXPRESSION_BG, ACTION_ACTION_COAT_COLOR, WHITE, HEARTS_COLOR,
|
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', 'Drop item', Action.Drop),
|
||||||
actionButtonAction('drop-toy', 'Drop toy', Action.DropToy),
|
actionButtonAction('drop-toy', 'Drop toy', Action.DropToy),
|
||||||
actionButtonAction('magic', 'Magic', Action.Magic),
|
actionButtonAction('magic', 'Magic', Action.Magic),
|
||||||
actionButtonAction('switch-tool', 'Switch tool', Action.SwitchTool),
|
actionButtonAction('kiss', 'Kiss', Action.Kiss),
|
||||||
actionButtonAction('switch-entity', 'Switch item to place'),
|
|
||||||
actionButtonAction('switch-entity-rev', 'Switch item to place (reverse)'),
|
|
||||||
actionButtonAction('switch-tile', 'Switch tile to place'),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const commandActions = [
|
const commandActions = [
|
||||||
@@ -394,7 +391,8 @@ export function drawAction(canvas: HTMLCanvasElement, action: ButtonAction | und
|
|||||||
const buffer = drawCanvas(bufferSize, bufferSize, sprites.paletteSpriteSheet, undefined, batch => {
|
const buffer = drawCanvas(bufferSize, bufferSize, sprites.paletteSpriteSheet, undefined, batch => {
|
||||||
const state = { ...createState(), expression: action.expression };
|
const state = { ...createState(), expression: action.expression };
|
||||||
const options = { ...defaultDrawPonyOptions(), noEars: true };
|
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) {
|
if (action.expression) {
|
||||||
const extra = action.expression.extra;
|
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);
|
batch.drawSprite(sprites.magic_icon, WHITE, defaultPalette, 4, 2);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'kiss': {
|
||||||
|
const state = { ...createState(), headAnimation: kiss, headAnimationFrame: 9 };
|
||||||
|
drawPony(batch, actionPony, state, 17, 40, defaultDrawPonyOptions());
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 'switch-tool': {
|
case 'switch-tool': {
|
||||||
const palette = mockPaletteManager.addArray(sprites.tools_icon.palettes![0]);
|
const palette = mockPaletteManager.addArray(sprites.tools_icon.palettes![0]);
|
||||||
batch.drawSprite(sprites.tools_icon.color, WHITE, palette, 0, 2);
|
batch.drawSprite(sprites.tools_icon.color, WHITE, palette, 0, 2);
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ export const CONTRIBUTORS: Contributors[] = [
|
|||||||
contributors: [
|
contributors: [
|
||||||
{ name: 'CyberPon3', links: ['https://twitter.com/CyberPon3'] },
|
{ name: 'CyberPon3', links: ['https://twitter.com/CyberPon3'] },
|
||||||
{ name: 'Industrialice' },
|
{ 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: 'Deeraw', links: ['https://www.deviantart.com/deerdraw', 'https://twitter.com/TheOnlyDeeraw'] },
|
||||||
{ name: 'Aviivix' },
|
{ name: 'Aviivix' },
|
||||||
{ name: 'SnowFl8keAnge1' },
|
{ name: 'SnowFl8keAnge1' },
|
||||||
|
{ name: '3aHo3a', links: ['http://twitter.com/imnuclearimwild'] },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ import { getPonyState, setPonyState, isPonyFlying, addChatBubble, isHidden } fro
|
|||||||
import {
|
import {
|
||||||
isPony, createPony, setPonyExpression, updatePonyInfo, updatePonyHold, doPonyAction, hasHeadAnimation,
|
isPony, createPony, setPonyExpression, updatePonyInfo, updatePonyHold, doPonyAction, hasHeadAnimation,
|
||||||
setHeadAnimation,
|
setHeadAnimation,
|
||||||
doBoopPonyAction
|
doBoopPonyAction,
|
||||||
|
isPonyBug
|
||||||
} from '../common/pony';
|
} from '../common/pony';
|
||||||
import { PonyTownGame } from './game';
|
import { PonyTownGame } from './game';
|
||||||
import { setupPlayer, savePlayerPosition } from './sec';
|
import { setupPlayer, savePlayerPosition } from './sec';
|
||||||
@@ -25,7 +26,7 @@ import { decodeUpdate, readOneUpdate } from '../common/encoders/updateDecoder';
|
|||||||
import { updateEntityVelocity } from '../common/entityUtils';
|
import { updateEntityVelocity } from '../common/entityUtils';
|
||||||
import { decodePonyInfo } from '../common/compressPony';
|
import { decodePonyInfo } from '../common/compressPony';
|
||||||
import { mockPaletteManager } from '../common/ponyInfo';
|
import { mockPaletteManager } from '../common/ponyInfo';
|
||||||
import { yawn, laugh, sneeze } from './ponyAnimations';
|
import { yawn, laugh, sneeze, kiss, kissFly, kissFlyBug } from './ponyAnimations';
|
||||||
import {
|
import {
|
||||||
findEntityById, getRegionGlobal, setTile, removeEntity, addEntity, removeEntityDirectly, setRegion,
|
findEntityById, getRegionGlobal, setTile, removeEntity, addEntity, removeEntityDirectly, setRegion,
|
||||||
addEntityToMapRegion, switchEntityRegion, getRegionUnsafe, addOrRemoveFromEntityList,
|
addEntityToMapRegion, switchEntityRegion, getRegionUnsafe, addOrRemoveFromEntityList,
|
||||||
@@ -415,6 +416,22 @@ export function handleAction(game: PonyTownGame, id: number, action: Action) {
|
|||||||
setHeadAnimation(pony, sneeze);
|
setHeadAnimation(pony, sneeze);
|
||||||
}
|
}
|
||||||
break;
|
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:
|
default:
|
||||||
log(`handleAction: Invalid action: ${action}`);
|
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';
|
import { repeat, flatten } from '../common/utils';
|
||||||
|
|
||||||
// body animations
|
// body animations
|
||||||
@@ -23,7 +23,7 @@ export function createBodyAnimation(
|
|||||||
name: string, fps: number, loop: boolean, frames: number[][], shadowOffsets?: number[][]
|
name: string, fps: number, loop: boolean, frames: number[][], shadowOffsets?: number[][]
|
||||||
): Readonly<BodyAnimation> {
|
): Readonly<BodyAnimation> {
|
||||||
if (shadowOffsets && shadowOffsets.length !== frames.length) {
|
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 }));
|
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]),
|
...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 flyUpAnims = [undefined, flyUp, flyUp, flyUp, flyUpBug];
|
||||||
export const flyDownAnims = [undefined, flyDown, flyDown, flyDown, flyDownBug];
|
export const flyDownAnims = [undefined, flyDown, flyDown, flyDown, flyDownBug];
|
||||||
|
|
||||||
export const animations = [
|
export const animations = [
|
||||||
stand, trot, boop, boopSit, boopLie, boopSwim, boopFly, boopFlyBug, sit, sitDown, standUp, lie, lieDown, sitUp,
|
stand, trot, boop, boopSit, boopLie, boopSwim, boopFly, boopFlyBug, sit, sitDown, standUp, lie, lieDown, sitUp,
|
||||||
fly, flyBug, flyUp, flyUpBug, flyDown, flyDownBug, sitToTrot, lieToTrot, flyToTrot, flyToTrotBug,
|
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]);
|
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 };
|
return { headX, headY, left, right, mouth };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createHeadAnimation(name: string, fps: number, loop: boolean, frames: number[][]): HeadAnimation {
|
export function createHeadAnimation(name: string, fps: number, loop: boolean, frames: number[][],
|
||||||
return { name, fps, loop, frames: frames.map(createHeadFrame) };
|
properties?: HeadAnimationProperties): HeadAnimation {
|
||||||
|
if (!properties) {
|
||||||
|
properties = HeadAnimationProperties.None;
|
||||||
|
}
|
||||||
|
return { name, fps, loop, properties, frames: frames.map(createHeadFrame) };
|
||||||
}
|
}
|
||||||
|
|
||||||
export const smile = createHeadAnimation('smile', 24, true, [
|
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(18, [1, -1, 12, 12, 16]),
|
||||||
...repeat(8, [0, 0, 12, 12, 12]),
|
...repeat(8, [0, 0, 12, 12, 12]),
|
||||||
[0, 0, 18, 18, 2],
|
[0, 0, 18, 18, 2],
|
||||||
]);
|
], HeadAnimationProperties.DontIncreaseEyeOpenness);
|
||||||
|
|
||||||
export const surprise = createHeadAnimation('surprise', 8, false, [
|
export const surprise = createHeadAnimation('surprise', 8, false, [
|
||||||
[0, 1, 6, 6, 1],
|
[0, 1, 6, 6, 1],
|
||||||
@@ -538,7 +691,7 @@ export const sneeze = createHeadAnimation('sneeze', 12, false, [
|
|||||||
...repeat(2, [1, -1, 18, 18, 16]),
|
...repeat(2, [1, -1, 18, 18, 16]),
|
||||||
...repeat(8, [-1, 1, 23, 23, 13]),
|
...repeat(8, [-1, 1, 23, 23, 13]),
|
||||||
...repeat(4, [0, 0, 18, 18, 7]),
|
...repeat(4, [0, 0, 18, 18, 7]),
|
||||||
]);
|
], HeadAnimationProperties.DontIncreaseEyeOpenness);
|
||||||
|
|
||||||
export const happy_tongue = createHeadAnimation('happy_tongue', 12, false, [
|
export const happy_tongue = createHeadAnimation('happy_tongue', 12, false, [
|
||||||
...repeat(3, [0, 0, 1, 1, 0]),
|
...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]),
|
...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 = [
|
export const headAnimations = [
|
||||||
smile, nom, laugh, yawn, surprise, surpriseSad, sneeze, excite,
|
smile, nom, laugh, yawn, surprise, surpriseSad, sneeze, excite, kiss, kissFly, kissFlyBug
|
||||||
];
|
];
|
||||||
|
|
||||||
// default animations
|
// default animations
|
||||||
|
|||||||
+44
-17
@@ -1,7 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
PonyEye, PonyState, PalettePonyInfo, PaletteSpriteSet, Palette, HeadAnimationFrame,
|
PonyEye, PonyState, PalettePonyInfo, PaletteSpriteSet, Palette, HeadAnimationFrame,
|
||||||
Eye, Iris, ColorExtraSets, ExpressionExtra, BodyAnimationFrame, DrawPonyOptions, Muzzle, BodyShadow, DrawOptions,
|
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';
|
} from '../common/interfaces';
|
||||||
import { WHITE, SHINES_COLOR, FAR_COLOR, TRANSPARENT, fillToOutlineColor } from '../common/colors';
|
import { WHITE, SHINES_COLOR, FAR_COLOR, TRANSPARENT, fillToOutlineColor } from '../common/colors';
|
||||||
import { toInt, hasFlag, repeat, flatten, point } from '../common/utils';
|
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) {
|
export function drawPony(batch: Batch, info: Info, state: State, ponyX: number, ponyY: number, options: Options) {
|
||||||
const frame = getPonyAnimationFrame(state.animation, state.animationFrame, defaultBodyFrame);
|
const frame = getPonyAnimationFrame(state.animation, state.animationFrame, defaultBodyFrame);
|
||||||
const headFrame = getPonyAnimationFrame(state.headAnimation || defaultHeadAnimation, state.headAnimationFrame, defaultHeadFrame);
|
const headFrame = getPonyAnimationFrame(state.headAnimation || defaultHeadAnimation, state.headAnimationFrame, defaultHeadFrame);
|
||||||
|
const headAnimationProperties = (state.headAnimation || defaultHeadAnimation).properties;
|
||||||
const baseX = ponyX - PONY_WIDTH / 2;
|
const baseX = ponyX - PONY_WIDTH / 2;
|
||||||
const baseY = ponyY - PONY_HEIGHT;
|
const baseY = ponyY - PONY_HEIGHT;
|
||||||
const x = baseX + frame.bodyX;
|
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);
|
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);
|
drawSet(batch, sprites.headAccessories, info.headAccessory, hatOffset.x, hatOffset.y + hatOffsetY, WHITE);
|
||||||
|
|
||||||
batch.restore();
|
batch.restore();
|
||||||
@@ -514,7 +516,7 @@ export function drawPony(batch: Batch, info: Info, state: State, ponyX: number,
|
|||||||
|
|
||||||
export function drawHead(
|
export function drawHead(
|
||||||
batch: Batch, info: Info, x: number, y: number, headSprites: ColorExtraSets, headFrame: HeadAnimationFrame,
|
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,
|
options: Options, flip: boolean, maneOffsetY: number,
|
||||||
) {
|
) {
|
||||||
const extraOffset = at(EXTRA_ACCESSORY_OFFSETS, info.mane && info.mane.type) || pointZero;
|
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
|
// make sure eyes are closed if sleeping
|
||||||
if (hasFlag(expression.extra, ExpressionExtra.Zzz)) {
|
if (hasFlag(expression.extra, ExpressionExtra.Zzz)) {
|
||||||
if (!isEyeSleeping(eyeLeftBase)) {
|
if (getEyeOpenness(eyeLeftBase) !== 0) {
|
||||||
eyeLeftBase = Eye.Closed;
|
eyeLeftBase = Eye.Closed;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isEyeSleeping(eyeRightBase)) {
|
if (getEyeOpenness(eyeRightBase) !== 0) {
|
||||||
eyeRightBase = Eye.Closed;
|
eyeRightBase = Eye.Closed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const eyeRight = getEyeFrame(info.eyeOpennessRight || 1, eyeRightBase, headFrame.right, blinkFrame);
|
const eyeRight = getEyeFrame(info.eyeOpennessRight || 1, eyeRightBase, headFrame.right, animationProperties, blinkFrame);
|
||||||
const eyeLeft = getEyeFrame(info.eyeOpennessLeft || 1, eyeLeftBase, headFrame.left, blinkFrame);
|
const eyeLeft = getEyeFrame(info.eyeOpennessLeft || 1, eyeLeftBase, headFrame.left, animationProperties, blinkFrame);
|
||||||
const eyeFrameLeft = flip ? eyeRight : eyeLeft;
|
const eyeFrameLeft = flip ? eyeRight : eyeLeft;
|
||||||
const eyeFrameRight = flip ? eyeLeft : eyeRight;
|
const eyeFrameRight = flip ? eyeLeft : eyeRight;
|
||||||
const eyeColorLeft = flip ? info.eyeColorRight : info.eyeColorLeft;
|
const eyeColorLeft = flip ? info.eyeColorRight : info.eyeColorLeft;
|
||||||
@@ -613,12 +615,7 @@ export function drawHead(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (draw(options, NoDraw.Body) && draw(options, NoDraw.Nose)) {
|
if (draw(options, NoDraw.Body) && draw(options, NoDraw.Nose)) {
|
||||||
const muzzle = holding ?
|
const muzzle = getMouthFrame(!!holding, expression, headFrame.mouth, info.muzzle, animationProperties);
|
||||||
Muzzle.Smile :
|
|
||||||
headFrame.mouth === -1 ?
|
|
||||||
(expression ? expression.muzzle : info.muzzle) :
|
|
||||||
headFrame.mouth;
|
|
||||||
|
|
||||||
const noses = at(sprites.noses, muzzle);
|
const noses = at(sprites.noses, muzzle);
|
||||||
const nose = att(noses, info.nose && info.nose.type)![0];
|
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) {
|
function getEyeFrame(base: Eye, expression: Eye, anim: Eye, animationProperties: HeadAnimationProperties, blinkFrame: number) {
|
||||||
if (anim !== -1)
|
|
||||||
return anim;
|
|
||||||
|
|
||||||
const frame = expression === -1 ? base : expression;
|
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];
|
const blink = blinkFrames[frame];
|
||||||
|
|
||||||
if (blinkFrame > 1 && blink) {
|
if (blinkFrame > 1 && blink) {
|
||||||
@@ -741,6 +745,29 @@ function getEyeFrame(base: Eye, expression: Eye, anim: Eye, blinkFrame: number)
|
|||||||
return frame;
|
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(
|
function drawEye(
|
||||||
batch: Batch, eye: PonyEye | undefined, iris: Iris, info: Info, palette: Palette | undefined, eyePalette: Palette,
|
batch: Batch, eye: PonyEye | undefined, iris: Iris, info: Info, palette: Palette | undefined, eyePalette: Palette,
|
||||||
x: number, y: number
|
x: number, y: number
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ import { animatorState as state, animatorTransition as transition, anyState, Ani
|
|||||||
import {
|
import {
|
||||||
stand, sit, sitDown, standUp, lie, lieDown, sitUp, flyBug, fly, flyUp, flyDown, flyUpBug, flyDownBug,
|
stand, sit, sitDown, standUp, lie, lieDown, sitUp, flyBug, fly, flyUp, flyDown, flyUpBug, flyDownBug,
|
||||||
trot, boop, boopSit, swim, sitToTrot, lieToTrot, boopLie, trotToFly, trotToFlyBug, boopFly, boopFlyBug,
|
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';
|
} from './ponyAnimations';
|
||||||
import { BodyAnimation } from '../common/interfaces';
|
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 boopingFlying = state(n('booping-flying'), boopFly, { bug: boopFlyBug });
|
||||||
export const boopingSwimming = state(n('booping-swimming'), boopSwim);
|
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 sitting = state(n('sitting'), sit);
|
||||||
export const sittingDown = state(n('sitting-down'), sitDown);
|
export const sittingDown = state(n('sitting-down'), sitDown);
|
||||||
export const standingUp = state(n('standing-up'), standUp);
|
export const standingUp = state(n('standing-up'), standUp);
|
||||||
@@ -51,7 +60,8 @@ export const ponyStates = [
|
|||||||
sitting, sittingDown, standingUp, sittingToTrotting,
|
sitting, sittingDown, standingUp, sittingToTrotting,
|
||||||
lying, lyingDown, sittingUp, lyingToTrotting,
|
lying, lyingDown, sittingUp, lyingToTrotting,
|
||||||
hovering, flying, flyingUp, flyingDown, trottingToFlying, flyingToTrotting,
|
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 });
|
transition(hovering, flyingDown, { exitAfter: 0 });
|
||||||
@@ -101,7 +111,6 @@ transition(flying, hovering, { exitAfter: 0, keepTime: true });
|
|||||||
|
|
||||||
transition(boopingSwimming, swimming);
|
transition(boopingSwimming, swimming);
|
||||||
transition(swimming, boopingSwimming, { exitAfter: 0 });
|
transition(swimming, boopingSwimming, { exitAfter: 0 });
|
||||||
|
|
||||||
transition(booping, standing);
|
transition(booping, standing);
|
||||||
transition(standing, booping, { exitAfter: 0 });
|
transition(standing, booping, { exitAfter: 0 });
|
||||||
transition(boopingSitting, sitting);
|
transition(boopingSitting, sitting);
|
||||||
@@ -111,7 +120,27 @@ transition(lying, boopingLying, { exitAfter: 0 });
|
|||||||
transition(boopingFlying, hovering, { enterTime: 1.1 / 10 });
|
transition(boopingFlying, hovering, { enterTime: 1.1 / 10 });
|
||||||
transition(hovering, boopingFlying, { exitAfter: 0 });
|
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, trotting, { exitAfter: 0, keepTime: true });
|
||||||
transition(anyState, flying, { 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) {
|
export function isSwimmingState(state: AnimatorState<BodyAnimation> | undefined) {
|
||||||
return state === swimming || state === trottingToSwimming || state === swimmingToTrotting ||
|
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) {
|
export function isFlyingUpOrDown(state: AnimatorState<BodyAnimation> | undefined) {
|
||||||
@@ -153,3 +183,14 @@ export function toBoopState(state: AnimatorState<BodyAnimation>) {
|
|||||||
default: return undefined;
|
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
|
// 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 pumpkinCollider = mixColliderRounded(-11, -6, 22, 12, 5, false);
|
||||||
const pumpkinPickable = mixPickable(26, 50);
|
const pumpkinPickable = mixPickable(26, 50);
|
||||||
const pumpkinParts = [pumpkinCollider, pumpkinPickable];
|
const pumpkinParts = [pumpkinCollider, pumpkinPickable];
|
||||||
const pumpkinDX = 11;
|
const pumpkinDX = 11;
|
||||||
const pumpkinDY = 15;
|
const pumpkinDY = 15;
|
||||||
|
const pumpkinAnimOff = [0];
|
||||||
|
const pumpkinAnimOn = [1];
|
||||||
|
|
||||||
export const pumpkin = doodad(n('pumpkin'), sprites.pumpkin_default, pumpkinDX, pumpkinDY, 0,
|
export const pumpkin = doodad(n('pumpkin'), sprites.pumpkin_default, pumpkinDX, pumpkinDY, 0,
|
||||||
...pumpkinParts);
|
...pumpkinParts);
|
||||||
@@ -1193,11 +1199,20 @@ export const jackoOn = doodad(n('jacko-on'), sprites.pumpkin_on, pumpkinDX, pump
|
|||||||
mixLight(jackoLightColor, 0, 0, 256, 192),
|
mixLight(jackoLightColor, 0, 0, 256, 192),
|
||||||
mixLightSprite(sprites.pumpkin_light, WHITE, pumpkinDX, pumpkinDY));
|
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,
|
...pumpkinParts,
|
||||||
mixLight(jackoLightColor, 0, 0, 256, 192),
|
mixLight(jackoLightColor, 0, 0, 256, 192),
|
||||||
mixLightSprite(sprites.pumpkin_light, WHITE, pumpkinDX, pumpkinDY),
|
mixLightSprite(sprites.pumpkin_light, WHITE, pumpkinDX, pumpkinDY),
|
||||||
mixFlags(EntityFlags.OnOff));
|
mixFlags(EntityFlags.OnOff));*/
|
||||||
|
|
||||||
// tombstones
|
// tombstones
|
||||||
|
|
||||||
@@ -2358,10 +2373,14 @@ export const fruits = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const tools = [
|
export const tools = [
|
||||||
{ type: saw.type, text: 'Saw: place & remove walls' },
|
{ type: saw.type, text: 'Saw: place & remove walls', textMobile: undefined },
|
||||||
{ type: broom.type, text: 'Broom: remove furniture' },
|
{ type: broom.type, text: 'Broom: remove furniture', textMobile: undefined },
|
||||||
{ type: hammer.type, text: 'Hammer: place furniture\nuse [mouse wheel] to switch item' },
|
{ type: hammer.type,
|
||||||
{ type: shovel.type, text: 'Shovel: change floor\nuse [mouse wheel] to switch floor 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);
|
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);
|
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) {
|
export function isMoving(entity: Entity) {
|
||||||
return entity.vx !== 0 || entity.vy !== 0;
|
return entity.vx !== 0 || entity.vy !== 0;
|
||||||
}
|
}
|
||||||
@@ -126,7 +138,7 @@ export function canBoop(pony: Pony) {
|
|||||||
return isIdle(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));
|
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.X, Eye.X, Muzzle.SmileOpen),
|
||||||
'😟': () => expression(Eye.Sad, Eye.Sad, Muzzle.Neutral),
|
'😟': () => expression(Eye.Sad, Eye.Sad, Muzzle.Neutral),
|
||||||
'😠': () => expression(Eye.Angry, Eye.Angry, Muzzle.Smile),
|
'😠': () => 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.Smile, Iris.Up, Iris.Forward),
|
||||||
'👿': () => expression(Eye.Angry, Eye.Angry, Muzzle.SmileTeeth),
|
'👿': () => expression(Eye.Angry, Eye.Angry, Muzzle.SmileTeeth),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -283,7 +283,7 @@ export const expressions: [string, Result][] = [
|
|||||||
['😆', [Eye.X, Eye.X, Muzzle.SmileOpen]],
|
['😆', [Eye.X, Eye.X, Muzzle.SmileOpen]],
|
||||||
['😟', [Eye.Sad, Eye.Sad, Muzzle.Neutral]],
|
['😟', [Eye.Sad, Eye.Sad, Muzzle.Neutral]],
|
||||||
['😠', [Eye.Angry, Eye.Angry, Muzzle.Smile]],
|
['😠', [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.Smile, Iris.Up, Iris.Forward]],
|
||||||
['👿', [Eye.Angry, Eye.Angry, Muzzle.SmileTeeth]],
|
['👿', [Eye.Angry, Eye.Angry, Muzzle.SmileTeeth]],
|
||||||
// unsafe faces
|
// unsafe faces
|
||||||
|
|||||||
@@ -566,6 +566,7 @@ export const enum DoAction {
|
|||||||
Boop,
|
Boop,
|
||||||
Swing,
|
Swing,
|
||||||
HoldPoof,
|
HoldPoof,
|
||||||
|
Kiss,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Pony extends Entity {
|
export interface Pony extends Entity {
|
||||||
@@ -1097,6 +1098,7 @@ export const enum Action {
|
|||||||
RequestEntityInfo,
|
RequestEntityInfo,
|
||||||
ACL,
|
ACL,
|
||||||
Magic,
|
Magic,
|
||||||
|
Kiss,
|
||||||
RemoveEntity,
|
RemoveEntity,
|
||||||
PlaceEntity,
|
PlaceEntity,
|
||||||
SwitchTool,
|
SwitchTool,
|
||||||
@@ -1219,6 +1221,7 @@ export interface SpriteSet<T> extends SpriteSetBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface PonyInfoBase<T, SET> {
|
export interface PonyInfoBase<T, SET> {
|
||||||
|
//body: SET | undefined;
|
||||||
head: SET | undefined;
|
head: SET | undefined;
|
||||||
nose: SET | undefined;
|
nose: SET | undefined;
|
||||||
ears: SET | undefined;
|
ears: SET | undefined;
|
||||||
@@ -1385,10 +1388,17 @@ export interface HeadAnimationFrame {
|
|||||||
mouth: Muzzle;
|
mouth: Muzzle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const enum HeadAnimationProperties {
|
||||||
|
None = 0,
|
||||||
|
DontIncreaseEyeOpenness = 1,
|
||||||
|
DontDecreaseMouthOpenness = 2
|
||||||
|
}
|
||||||
|
|
||||||
export interface HeadAnimation {
|
export interface HeadAnimation {
|
||||||
name: string;
|
name: string;
|
||||||
loop: boolean;
|
loop: boolean;
|
||||||
fps: number;
|
fps: number;
|
||||||
|
properties: HeadAnimationProperties;
|
||||||
frames: HeadAnimationFrame[];
|
frames: HeadAnimationFrame[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1546,10 +1556,44 @@ export const enum Muzzle {
|
|||||||
// max: 31
|
// max: 31
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CLOSED_MUZZLES = [
|
export function getMuzzleOpenness(muzzle: Muzzle) {
|
||||||
Muzzle.Smile, Muzzle.Frown, Muzzle.Neutral, Muzzle.Scrunch, Muzzle.Flat, Muzzle.Concerned,
|
switch (muzzle) {
|
||||||
Muzzle.Kiss, Muzzle.Kiss2,
|
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 {
|
export const enum Eye {
|
||||||
None = 0,
|
None = 0,
|
||||||
@@ -1580,10 +1624,43 @@ export const enum Eye {
|
|||||||
// max: 31
|
// max: 31
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isEyeSleeping(eye: Eye) {
|
export function getEyeOpenness(eye: Eye) {
|
||||||
return eye === Eye.Closed ||
|
switch (eye) {
|
||||||
(eye >= Eye.Lines && eye <= Eye.ClosedHappy) ||
|
case Eye.None:
|
||||||
(eye >= Eye.Peaceful && eye <= Eye.X2);
|
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 {
|
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 { stand, sneeze, defaultHeadAnimation, defaultBodyFrame, defaultHeadFrame } from '../client/ponyAnimations';
|
||||||
import {
|
import {
|
||||||
PaletteSpriteBatch, Pony, BodyAnimation, EntityState, SpriteBatch, ExpressionExtra, HeadAnimation, Palette,
|
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,
|
Iris, EntityPlayerState,
|
||||||
} from './interfaces';
|
} from './interfaces';
|
||||||
import { hasFlag, setFlag } from './utils';
|
import { hasFlag, setFlag } from './utils';
|
||||||
@@ -26,7 +26,7 @@ import {
|
|||||||
} from './animator';
|
} from './animator';
|
||||||
import {
|
import {
|
||||||
trotting, flying, hovering, toBoopState, isFlyingUpOrDown, isFlyingDown, isSittingDown, isSittingUp, swinging,
|
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';
|
} from '../client/ponyStates';
|
||||||
import { decodePonyInfo } from './compressPony';
|
import { decodePonyInfo } from './compressPony';
|
||||||
import { defaultPonyState, defaultDrawPonyOptions, isStateEqual } from '../client/ponyHelpers';
|
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) {
|
export function ensurePonyInfoDecoded(pony: Pony) {
|
||||||
if (pony.info !== undefined && pony.palettePonyInfo === undefined) {
|
if (pony.info !== undefined && pony.palettePonyInfo === undefined) {
|
||||||
pony.palettePonyInfo = decodePonyInfo(pony.info, pony.paletteManager);
|
pony.palettePonyInfo = decodePonyInfo(pony.info, pony.paletteManager);
|
||||||
@@ -445,6 +453,9 @@ export function updatePonyEntity(pony: Pony, delta: number, gameTime: number, sa
|
|||||||
case DoAction.HoldPoof:
|
case DoAction.HoldPoof:
|
||||||
playAnimation(pony.holdPoofEffect, holdPoofAnimation);
|
playAnimation(pony.holdPoofEffect, holdPoofAnimation);
|
||||||
break;
|
break;
|
||||||
|
case DoAction.Kiss:
|
||||||
|
setAnimatorState(pony.animator, toKissState(animationState) || animationState);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
if (DEVELOPMENT) {
|
if (DEVELOPMENT) {
|
||||||
console.error(`Invalid DoAction: ${pony.doAction}`);
|
console.error(`Invalid DoAction: ${pony.doAction}`);
|
||||||
@@ -587,8 +598,8 @@ function filterExpression(expression: Expression) {
|
|||||||
blush ||
|
blush ||
|
||||||
hasFlag(extra, ExpressionExtra.Hearts) ||
|
hasFlag(extra, ExpressionExtra.Hearts) ||
|
||||||
hasFlag(extra, ExpressionExtra.Cry) ||
|
hasFlag(extra, ExpressionExtra.Cry) ||
|
||||||
isEyeSleeping(expression.left) ||
|
(getEyeOpenness(expression.left) === 0) ||
|
||||||
isEyeSleeping(expression.right)
|
(getEyeOpenness(expression.right) === 0)
|
||||||
) {
|
) {
|
||||||
if (expression.muzzle === Muzzle.SmilePant || expression.muzzle === Muzzle.NeutralPant) {
|
if (expression.muzzle === Muzzle.SmilePant || expression.muzzle === Muzzle.NeutralPant) {
|
||||||
expression.muzzle = Muzzle.Neutral;
|
expression.muzzle = Muzzle.Neutral;
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ const exampleCM = [
|
|||||||
BLUE, BLUE, BLUE, BLUE, BLUE,
|
BLUE, BLUE, BLUE, BLUE, BLUE,
|
||||||
];
|
];
|
||||||
|
|
||||||
const frontLegsCount = 39;
|
const frontLegsCount = 40;
|
||||||
const backLegsCount = 27;
|
const backLegsCount = 27;
|
||||||
|
|
||||||
const frontLegsSheet = {
|
const frontLegsSheet = {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ h1(focusTitle) Account settings
|
|||||||
| Changes saved
|
| Changes saved
|
||||||
.form-group.text-right
|
.form-group.text-right
|
||||||
button.btn.btn-default(type="submit" [disabled]="!canSubmit" style="min-width: 100px;")
|
button.btn.btn-default(type="submit" [disabled]="!canSubmit" style="min-width: 100px;")
|
||||||
| Save
|
| {{saveButtonText}}
|
||||||
|
|
||||||
div(style="margin-bottom: 100px;")
|
div(style="margin-bottom: 100px;")
|
||||||
.form-group
|
.form-group
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { oauthProviders } from '../../../client/data';
|
|||||||
import { Model } from '../../services/model';
|
import { Model } from '../../services/model';
|
||||||
import { getProviderIcon } from '../../shared/sign-in-box/sign-in-box';
|
import { getProviderIcon } from '../../shared/sign-in-box/sign-in-box';
|
||||||
import { faStar, faExclamationCircle, faSync } from '../../../client/icons';
|
import { faStar, faExclamationCircle, faSync } from '../../../client/icons';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'account',
|
selector: 'account',
|
||||||
@@ -33,9 +34,10 @@ export class Account implements OnInit, OnDestroy {
|
|||||||
removedAccount?: boolean;
|
removedAccount?: boolean;
|
||||||
accountError?: string;
|
accountError?: string;
|
||||||
accountSaved = false;
|
accountSaved = false;
|
||||||
|
isNewAccount = false;
|
||||||
hides: HiddenPlayer[] | undefined = undefined;
|
hides: HiddenPlayer[] | undefined = undefined;
|
||||||
page = 0;
|
page = 0;
|
||||||
constructor(private model: Model) {
|
constructor(private model: Model, private router: Router) {
|
||||||
}
|
}
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
const account = this.account!;
|
const account = this.account!;
|
||||||
@@ -44,6 +46,7 @@ export class Account implements OnInit, OnDestroy {
|
|||||||
name: account.name,
|
name: account.name,
|
||||||
birthdate: account.birthdate,
|
birthdate: account.birthdate,
|
||||||
};
|
};
|
||||||
|
this.isNewAccount = account.birthdate === '';
|
||||||
|
|
||||||
this.pageChanged();
|
this.pageChanged();
|
||||||
}
|
}
|
||||||
@@ -88,6 +91,9 @@ export class Account implements OnInit, OnDestroy {
|
|||||||
get showAccountAlert() {
|
get showAccountAlert() {
|
||||||
return this.model.missingBirthdate;
|
return this.model.missingBirthdate;
|
||||||
}
|
}
|
||||||
|
get saveButtonText() {
|
||||||
|
return this.isNewAccount ? 'Save and continue' : 'Save';
|
||||||
|
}
|
||||||
icon(id: string) {
|
icon(id: string) {
|
||||||
return getProviderIcon(id);
|
return getProviderIcon(id);
|
||||||
}
|
}
|
||||||
@@ -98,6 +104,9 @@ export class Account implements OnInit, OnDestroy {
|
|||||||
this.model.updateAccount(this.data)
|
this.model.updateAccount(this.data)
|
||||||
.catch((e: Error) => this.accountError = e.message)
|
.catch((e: Error) => this.accountError = e.message)
|
||||||
.then(() => this.accountSaved = true);
|
.then(() => this.accountSaved = true);
|
||||||
|
if (this.isNewAccount) {
|
||||||
|
this.router.navigate(['home']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
removeSite(site: SocialSiteInfo) {
|
removeSite(site: SocialSiteInfo) {
|
||||||
|
|||||||
@@ -204,13 +204,15 @@ h1(focusTitle) Help
|
|||||||
p.text-fading.
|
p.text-fading.
|
||||||
This is usually caused by outdated graphics drivers or browser.
|
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
|
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.
|
p.text-fading.
|
||||||
If your browser is already up to date, try installing different browser.
|
If your browser is already up to date, try installing different browser.
|
||||||
We recommend using #[a(href="https://www.google.com/chrome/") Chrome]
|
We recommend using #[a(href="https://www.google.com/chrome/") Chrome]
|
||||||
or #[a(href="https://www.mozilla.org/en-US/firefox/new/") Firefox].
|
or #[a(href="https://www.mozilla.org/en-US/firefox/new/") Firefox].
|
||||||
p.text-fading.
|
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
|
section
|
||||||
h4 Graphical glitches, disappearing objects
|
h4 Graphical glitches, disappearing objects
|
||||||
|
|||||||
@@ -83,6 +83,13 @@ export class ActionBar {
|
|||||||
this.closeActions();
|
this.closeActions();
|
||||||
}
|
}
|
||||||
else {
|
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.isWaitingForActionsModal = true;
|
||||||
this.modalRef = this.modalService.show(this.actionsModal, { ignoreBackdropClick: true });
|
this.modalRef = this.modalService.show(this.actionsModal, { ignoreBackdropClick: true });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ export class ActionsModal implements OnInit, OnDestroy {
|
|||||||
this.game.editingActions = false;
|
this.game.editingActions = false;
|
||||||
clearInterval(this.interval);
|
clearInterval(this.interval);
|
||||||
this.subscription && this.subscription.unsubscribe();
|
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();
|
this.notify.emit();
|
||||||
}
|
}
|
||||||
ok() {
|
ok() {
|
||||||
|
|||||||
@@ -92,8 +92,8 @@
|
|||||||
.form-group
|
.form-group
|
||||||
custom-checkbox(
|
custom-checkbox(
|
||||||
[(checked)]="account.filterCyrillic"
|
[(checked)]="account.filterCyrillic"
|
||||||
help="Hide all messages containing russian alphabet")
|
help="Hide all messages containing Russian alphabet")
|
||||||
| Filter russian chat
|
| Filter Russian chat
|
||||||
.form-group
|
.form-group
|
||||||
custom-checkbox(
|
custom-checkbox(
|
||||||
[(checked)]="account.filterSwearWords"
|
[(checked)]="account.filterSwearWords"
|
||||||
|
|||||||
@@ -112,6 +112,12 @@ ng-template(#shareLinkPopover)
|
|||||||
.form-group
|
.form-group
|
||||||
button.btn.btn-sm.ml-1([(ngModel)]="animation.loop" btnCheckbox [btnHighlight]="animation.loop")
|
button.btn.btn-sm.ml-1([(ngModel)]="animation.loop" btnCheckbox [btnHighlight]="animation.loop")
|
||||||
| 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)
|
.btn-group.ml-1(dropdown)
|
||||||
button.btn.btn-sm.btn-default.dropdown-toggle(dropdownToggle title="Before animation")
|
button.btn.btn-sm.btn-default.dropdown-toggle(dropdownToggle title="Before animation")
|
||||||
| before: {{beforeAnimation?.name || 'none'}}
|
| before: {{beforeAnimation?.name || 'none'}}
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import {
|
|||||||
BodyAnimationFrame as IBodyAnimationFrame,
|
BodyAnimationFrame as IBodyAnimationFrame,
|
||||||
HeadAnimation as IHeadAnimation,
|
HeadAnimation as IHeadAnimation,
|
||||||
HeadAnimationFrame as IHeadAnimationFrame,
|
HeadAnimationFrame as IHeadAnimationFrame,
|
||||||
ColorExtraSet, PonyInfo, PonyObject, BodyShadow, PonyEye
|
ColorExtraSet, PonyInfo, PonyObject, BodyShadow, PonyEye, HeadAnimationProperties
|
||||||
} from '../../../common/interfaces';
|
} 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 { toPalette, createDefaultPony, syncLockedPonyInfo } from '../../../common/ponyInfo';
|
||||||
import { Key } from '../../../client/input/input';
|
import { Key } from '../../../client/input/input';
|
||||||
import { defaultPonyState, defaultDrawPonyOptions } from '../../../client/ponyHelpers';
|
import { defaultPonyState, defaultDrawPonyOptions } from '../../../client/ponyHelpers';
|
||||||
@@ -63,6 +63,8 @@ interface BodyAnimation extends BaseAnimation {
|
|||||||
interface HeadAnimation extends BaseAnimation {
|
interface HeadAnimation extends BaseAnimation {
|
||||||
lockEyes?: boolean;
|
lockEyes?: boolean;
|
||||||
frames: HeadAnimationFrame[];
|
frames: HeadAnimationFrame[];
|
||||||
|
dontOpenEyes?: boolean;
|
||||||
|
dontCloseMouth?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AnimationsData {
|
interface AnimationsData {
|
||||||
@@ -201,6 +203,13 @@ export class ToolsAnimation implements OnInit, OnDestroy {
|
|||||||
if (!this.playing) {
|
if (!this.playing) {
|
||||||
if (this.mode === 'body') {
|
if (this.mode === 'body') {
|
||||||
this.state.animationFrame = this.frame;
|
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 {
|
} else {
|
||||||
this.state.headAnimationFrame = this.frame;
|
this.state.headAnimationFrame = this.frame;
|
||||||
}
|
}
|
||||||
@@ -459,9 +468,10 @@ export class ToolsAnimation implements OnInit, OnDestroy {
|
|||||||
console.log(`shadow: [${shadow.map(x => `[${x.join(', ')}]`).join(', ')}]`);
|
console.log(`shadow: [${shadow.map(x => `[${x.join(', ')}]`).join(', ')}]`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const animation = toHeadAnimation(this.headAnimation, true);
|
const frames = this.headAnimation.frames
|
||||||
const compressed = animation.frames.map(compressHeadFrame);
|
.map(f => [f.duration, '[' + compressHeadFrame(f).join(', ') + ']'])
|
||||||
console.log(JSON.stringify(compressed));
|
.map(([repeat, frame]) => repeat > 1 ? `...repeat(${repeat}, ${frame})` : frame);
|
||||||
|
console.log(`frames: [\n${frames.map(x => `\t${x}`).join(',\n')}\n]`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
png(scale = 1) {
|
png(scale = 1) {
|
||||||
@@ -531,9 +541,11 @@ export class ToolsAnimation implements OnInit, OnDestroy {
|
|||||||
if (this.playing) {
|
if (this.playing) {
|
||||||
this.time += delta;
|
this.time += delta;
|
||||||
|
|
||||||
if (this.mode === 'body') {
|
const isBodyMode = this.mode === 'body';
|
||||||
|
|
||||||
|
if (isBodyMode) {
|
||||||
if (this.state.animation) {
|
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) {
|
if (frame > this.state.animation.frames.length && !this.state.animation.loop) {
|
||||||
this.bodyAnimationPlaying = (this.bodyAnimationPlaying + 1) % this.bodyAnimationsToPlay.length;
|
this.bodyAnimationPlaying = (this.bodyAnimationPlaying + 1) % this.bodyAnimationsToPlay.length;
|
||||||
@@ -541,14 +553,15 @@ export class ToolsAnimation implements OnInit, OnDestroy {
|
|||||||
this.state.animationFrame = 0;
|
this.state.animationFrame = 0;
|
||||||
this.time = 0;
|
this.time = 0;
|
||||||
} else {
|
} 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);
|
if (this.state.headAnimation) {
|
||||||
this.state.headAnimationFrame = frame % this.state.headAnimation.frames.length;
|
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', {});
|
return this.storage.getJSON<AnimationsData>('tools-animations', {});
|
||||||
}
|
}
|
||||||
private createAnimationSprites(scale: number) {
|
private createAnimationSprites(scale: number) {
|
||||||
|
const isBodyMode = this.mode === 'body';
|
||||||
const animation = toBodyAnimation(this.bodyAnimation, true, this.switch);
|
const animation = toBodyAnimation(this.bodyAnimation, true, this.switch);
|
||||||
const headAnimation = toHeadAnimation(this.headAnimation, true);
|
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 buffer = createCanvas(ponyWidth, ponyHeight);
|
||||||
const batch = new ContextSpriteBatch(buffer);
|
const batch = new ContextSpriteBatch(buffer);
|
||||||
const info = toPalette(this.pony.info);
|
const info = toPalette(this.pony.info);
|
||||||
@@ -582,14 +596,23 @@ export class ToolsAnimation implements OnInit, OnDestroy {
|
|||||||
const x = i % cols;
|
const x = i % cols;
|
||||||
const y = Math.floor(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);
|
batch.start(sprites.paletteSpriteSheet, 0);
|
||||||
|
|
||||||
drawPony(batch, info, {
|
drawPony(batch, info, {
|
||||||
...defaultPonyState(),
|
...defaultPonyState(),
|
||||||
animation,
|
animation,
|
||||||
animationFrame: this.mode === 'body' ? i : 0,
|
animationFrame: isBodyMode ? i : 0,
|
||||||
headAnimation: this.mode === 'head' ? headAnimation : undefined,
|
headAnimation: headAnimation,
|
||||||
headAnimationFrame: this.mode === 'head' ? i : 0,
|
headAnimationFrame: isBodyMode ? headFrame : i,
|
||||||
blinkFrame: 1,
|
blinkFrame: 1,
|
||||||
}, ponyWidth / 2, ponyHeight - 10, options);
|
}, ponyWidth / 2, ponyHeight - 10, options);
|
||||||
|
|
||||||
@@ -696,7 +719,7 @@ function compressBodyFrame(f: BodyAnimationFrame): number[] {
|
|||||||
], x => !x);
|
], 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[] = [];
|
const fs: HeadAnimationFrame[] = [];
|
||||||
|
|
||||||
frames.forEach(f => {
|
frames.forEach(f => {
|
||||||
@@ -713,18 +736,30 @@ function fromHeadAnimation({ name, fps, loop, frames }: IHeadAnimation, index: n
|
|||||||
builtin: true,
|
builtin: true,
|
||||||
fps,
|
fps,
|
||||||
loop,
|
loop,
|
||||||
|
dontOpenEyes: hasFlag(properties, HeadAnimationProperties.DontIncreaseEyeOpenness),
|
||||||
|
dontCloseMouth: hasFlag(properties, HeadAnimationProperties.DontDecreaseMouthOpenness),
|
||||||
name: `# builtin ${index.toString().padStart(2, '0')} # ${name}`,
|
name: `# builtin ${index.toString().padStart(2, '0')} # ${name}`,
|
||||||
frames: fs,
|
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;
|
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 {
|
return {
|
||||||
name,
|
name,
|
||||||
fps,
|
fps,
|
||||||
loop,
|
loop,
|
||||||
|
properties,
|
||||||
frames: flatMap(fs, f => repeat(full ? f.duration : 1, f)),
|
frames: flatMap(fs, f => repeat(full ? f.duration : 1, f)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -788,6 +823,7 @@ function fixHeadAnimation(a: HeadAnimation): HeadAnimation {
|
|||||||
fps: a.fps || 24,
|
fps: a.fps || 24,
|
||||||
loop: a.loop || false,
|
loop: a.loop || false,
|
||||||
lockEyes: a.lockEyes || false,
|
lockEyes: a.lockEyes || false,
|
||||||
|
dontOpenEyes: a.dontOpenEyes || false,
|
||||||
frames: (a.frames || []).map(fixHeadFrame),
|
frames: (a.frames || []).map(fixHeadFrame),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { createCamera } from '../../../common/camera';
|
|||||||
import { mockPaletteManager } from '../../../common/ponyInfo';
|
import { mockPaletteManager } from '../../../common/ponyInfo';
|
||||||
import { isCritter } from '../../../common/entityUtils';
|
import { isCritter } from '../../../common/entityUtils';
|
||||||
import {
|
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';
|
} from '../../../common/entities';
|
||||||
import { drawMap } from '../../../client/draw';
|
import { drawMap } from '../../../client/draw';
|
||||||
import { includes, observableToPromise, hasFlag } from '../../../common/utils';
|
import { includes, observableToPromise, hasFlag } from '../../../common/utils';
|
||||||
@@ -141,7 +141,7 @@ function drawTheMap(canvas: HTMLCanvasElement, map: WorldMap, info: ToolsMapOthe
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ignoreTypes = [
|
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);
|
].map(e => e.type);
|
||||||
|
|
||||||
const shouldDraw = (e: Entity) => {
|
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(['laugh', 'lol', 'haha', 'хаха', 'jaja'], Action.Laugh),
|
||||||
action(['sneeze', 'achoo'], Action.Sneeze),
|
action(['sneeze', 'achoo'], Action.Sneeze),
|
||||||
action(['magic'], Action.Magic),
|
action(['magic'], Action.Magic),
|
||||||
|
action(['kiss'], Action.Kiss),
|
||||||
|
|
||||||
// house
|
// house
|
||||||
command(['savehouse'], '/savehouse - saves current house setup', '', async ({ }, client) => {
|
command(['savehouse'], '/savehouse - saves current house setup', '', async ({ }, client) => {
|
||||||
@@ -342,18 +343,22 @@ export function createCommands(world: World): Command[] {
|
|||||||
client.reporter.systemLog(`House unlocked`);
|
client.reporter.systemLog(`House unlocked`);
|
||||||
}),
|
}),
|
||||||
command(['removetoolbox'], '/removetoolbox - removes toolbox from the house', '', ({ world }, client) => {
|
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;
|
return;
|
||||||
|
|
||||||
|
client.lastMapLoadOrSave = Date.now();
|
||||||
|
|
||||||
removeToolbox(world, client.map);
|
removeToolbox(world, client.map);
|
||||||
|
|
||||||
saySystem(client, 'Toolbox removed');
|
saySystem(client, 'Toolbox removed');
|
||||||
client.reporter.systemLog(`Toolbox removed`);
|
client.reporter.systemLog(`Toolbox removed`);
|
||||||
}),
|
}),
|
||||||
command(['restoretoolbox'], '/restoretoolbox - restores toolbox to the house', '', ({ }, client) => {
|
command(['restoretoolbox'], '/restoretoolbox - restores toolbox to the house', '', ({ }, client) => {
|
||||||
if (!isValidMapForEditing(client.map, client, false, true))
|
if (!isValidMapForEditing(client.map, client, true, true))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
client.lastMapLoadOrSave = Date.now();
|
||||||
|
|
||||||
restoreToolbox(world, client.map);
|
restoreToolbox(world, client.map);
|
||||||
|
|
||||||
saySystem(client, 'Toolbox restored');
|
saySystem(client, 'Toolbox restored');
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import {
|
|||||||
} from '../common/utils';
|
} from '../common/utils';
|
||||||
import { CharacterState, ServerConfig, AccountState, CharacterStateFlags, GameServerSettings } from '../common/adminInterfaces';
|
import { CharacterState, ServerConfig, AccountState, CharacterStateFlags, GameServerSettings } from '../common/adminInterfaces';
|
||||||
import {
|
import {
|
||||||
EntityState, Expression, PonyOptions, Action, ExpressionExtra, Eye, Muzzle, CLOSED_MUZZLES,
|
EntityState, Expression, PonyOptions, Action, ExpressionExtra, Eye, Muzzle, getMuzzleOpenness,
|
||||||
isExpressionAction, EntityPlayerState, UpdateFlags, InteractAction
|
isExpressionAction, EntityPlayerState, UpdateFlags, InteractAction, Rect
|
||||||
} from '../common/interfaces';
|
} from '../common/interfaces';
|
||||||
import { encodeExpression, EMPTY_EXPRESSION, decodeExpression } from '../common/encoders/expressionEncoder';
|
import { encodeExpression, EMPTY_EXPRESSION, decodeExpression } from '../common/encoders/expressionEncoder';
|
||||||
import { EXPRESSION_TIMEOUT, DAY, FLY_DELAY, SECOND, PONY_TYPE } from '../common/constants';
|
import { EXPRESSION_TIMEOUT, DAY, FLY_DELAY, SECOND, PONY_TYPE } from '../common/constants';
|
||||||
@@ -31,7 +31,7 @@ import {
|
|||||||
import { replaceEmojis } from '../client/emoji';
|
import { replaceEmojis } from '../client/emoji';
|
||||||
import { expression, parseExpression } from '../common/expressionUtils';
|
import { expression, parseExpression } from '../common/expressionUtils';
|
||||||
import {
|
import {
|
||||||
canBoop2, isPonySitting, isPonyStanding, getBoopRect, canStand, isPonyFlying, setPonyState, canSit, canLie
|
canBoopOrKiss, isPonySitting, isPonyStanding, getBoopRect, canStand, isPonyFlying, setPonyState, canSit, canLie, getkissRect, getSneezeRect
|
||||||
} from '../common/entityUtils';
|
} from '../common/entityUtils';
|
||||||
import { withBorder } from '../common/rect';
|
import { withBorder } from '../common/rect';
|
||||||
import { isOnlineFriend } from './services/friends';
|
import { isOnlineFriend } from './services/friends';
|
||||||
@@ -75,6 +75,17 @@ export function updateClientCharacter(client: IClient, character: ICharacter) {
|
|||||||
client.characterName = replaceEmojis(client.character.name);
|
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(
|
export function createClient(
|
||||||
client: IClient, account: IAccount, friends: string[], hides: string[], character: ICharacter, pony: ServerEntity,
|
client: IClient, account: IAccount, friends: string[], hides: string[], character: ICharacter, pony: ServerEntity,
|
||||||
defaultMap: ServerMap, reporter: Reporter, origin: IOriginInfo | undefined
|
defaultMap: ServerMap, reporter: Reporter, origin: IOriginInfo | undefined
|
||||||
@@ -85,6 +96,7 @@ export function createClient(
|
|||||||
client.country = origin && origin.country || '??';
|
client.country = origin && origin.country || '??';
|
||||||
client.userAgent = client.originalRequest && client.originalRequest.headers['user-agent'];
|
client.userAgent = client.originalRequest && client.originalRequest.headers['user-agent'];
|
||||||
|
|
||||||
|
client.isMobile = isMobileUserAgent(client.userAgent);
|
||||||
client.accountId = account._id.toString();
|
client.accountId = account._id.toString();
|
||||||
client.accountName = account.name;
|
client.accountName = account.name;
|
||||||
client.ignores = new Set(account.ignores);
|
client.ignores = new Set(account.ignores);
|
||||||
@@ -117,8 +129,7 @@ export function createClient(
|
|||||||
client.safeY = pony.y;
|
client.safeY = pony.y;
|
||||||
|
|
||||||
client.lastPacket = Date.now();
|
client.lastPacket = Date.now();
|
||||||
client.lastAction = 0;
|
client.lastBoopOrKissAction = 0;
|
||||||
client.lastBoopAction = 0;
|
|
||||||
client.lastExpressionAction = 0;
|
client.lastExpressionAction = 0;
|
||||||
client.lastSays = [];
|
client.lastSays = [];
|
||||||
client.lastX = pony.x;
|
client.lastX = pony.x;
|
||||||
@@ -307,7 +318,7 @@ export function parseOrCurrentExpression(pony: ServerEntity, message: string) {
|
|||||||
export function playerSleep(pony: ServerEntity, args = '') {
|
export function playerSleep(pony: ServerEntity, args = '') {
|
||||||
if (pony.vx === 0 && pony.vy === 0) {
|
if (pony.vx === 0 && pony.vy === 0) {
|
||||||
const base = parseOrCurrentExpression(pony, args) || expression(Eye.Closed, Eye.Closed, Muzzle.Neutral);
|
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 };
|
const expr = { ...base, muzzle, left: Eye.Closed, right: Eye.Closed, extra: ExpressionExtra.Zzz };
|
||||||
setEntityExpression(pony, expr, 0, true);
|
setEntityExpression(pony, expr, 0, true);
|
||||||
}
|
}
|
||||||
@@ -397,7 +408,8 @@ export function useHeldItem(client: IClient) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function canPerformAction(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) {
|
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 purpleGrapeTypes = entities.grapesPurple.map(x => x.type);
|
||||||
const greenGrapeTypes = entities.grapesGreen.map(x => x.type);
|
const greenGrapeTypes = entities.grapesGreen.map(x => x.type);
|
||||||
|
|
||||||
export function boop(client: IClient, now: number) {
|
function boopEntity(client: IClient, rect: Rect, isOnlyBooping: boolean) {
|
||||||
if (canPerformAction(client) && canBoop2(client.pony) && client.lastBoopAction < now) {
|
if (!client.shadowed && (isPonySitting(client.pony) || isPonyStanding(client.pony))) {
|
||||||
cancelEntityExpression(client.pony);
|
const boopBounds = withBorder(rect, 1);
|
||||||
sendAction(client.pony, Action.Boop);
|
const entities = findEntitiesInBounds(client.map, boopBounds);
|
||||||
|
const entity = entities.find(e => canBoopEntity(e, rect));
|
||||||
|
|
||||||
if (!client.shadowed && (isPonySitting(client.pony) || isPonyStanding(client.pony))) {
|
if (entity) {
|
||||||
const boopRect = getBoopRect(client.pony);
|
if (entity.boop) {
|
||||||
const boopBounds = withBorder(boopRect, 1);
|
entity.boop(client);
|
||||||
const entities = findEntitiesInBounds(client.map, boopBounds);
|
} else if (!isOnlyBooping && entity.type === PONY_TYPE) {
|
||||||
const entity = entities.find(e => canBoopEntity(e, boopRect));
|
const clientHold = client.pony.options!.hold || 0;
|
||||||
|
|
||||||
if (entity) {
|
if (isHoldingGrapes(entity) && clientHold !== grapeGreen.type && clientHold !== grapePurple.type) {
|
||||||
if (entity.boop) {
|
let index = purpleGrapeTypes.indexOf(entity.options!.hold || 0);
|
||||||
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) {
|
if (index !== -1) {
|
||||||
let index = purpleGrapeTypes.indexOf(entity.options!.hold || 0);
|
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) {
|
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);
|
unholdItem(entity);
|
||||||
} else {
|
} else {
|
||||||
holdItem(entity, purpleGrapeTypes[index + 1]);
|
holdItem(entity, greenGrapeTypes[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]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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) {
|
export function expressionAction(client: IClient, action: Action) {
|
||||||
if (canPerformAction(client) && isExpressionAction(action) && client.lastExpressionAction < Date.now()) {
|
if (canPerformAction(client) && isExpressionAction(action)) {
|
||||||
cancelEntityExpression(client.pony);
|
cancelEntityExpression(client.pony);
|
||||||
sendAction(client.pony, action);
|
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:
|
case Action.TurnHead:
|
||||||
turnHead(client);
|
turnHead(client);
|
||||||
break;
|
break;
|
||||||
|
case Action.Sneeze:
|
||||||
|
sneeze(client);
|
||||||
|
break;
|
||||||
case Action.Stand:
|
case Action.Stand:
|
||||||
stand(client);
|
stand(client);
|
||||||
break;
|
break;
|
||||||
@@ -766,6 +800,9 @@ export function execAction(client: IClient, action: Action, settings: GameServer
|
|||||||
updateEntityState(client.pony, setFlag(client.pony.state, EntityState.Magic, !has));
|
updateEntityState(client.pony, setFlag(client.pony.state, EntityState.Magic, !has));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case Action.Kiss:
|
||||||
|
kiss(client, Date.now());
|
||||||
|
break;
|
||||||
case Action.SwitchTool:
|
case Action.SwitchTool:
|
||||||
switchTool(client, false);
|
switchTool(client, false);
|
||||||
break;
|
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 newIndex = reverse ? (index === -1 ? tools.length - 1 : index - 1) : ((index + 1) % tools.length);
|
||||||
const tool = tools[newIndex];
|
const tool = tools[newIndex];
|
||||||
holdItem(client.pony, tool.type);
|
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 header = 'data:image/gif;base64,';
|
||||||
const buffer = Buffer.from(image.substr(header.length), 'base64');
|
const buffer = Buffer.from(image.substr(header.length), 'base64');
|
||||||
const magick = /^win/.test(process.platform) ? 'magick' : 'convert';
|
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')}"`;
|
+ `+repage${repeat(' +delete', remove)} "${filePath.replace(/png$/, 'gif')}"`;
|
||||||
|
console.log('executing ' + command);
|
||||||
|
|
||||||
fs.writeFileAsync(filePath, buffer)
|
fs.writeFileAsync(filePath, buffer)
|
||||||
.then(() => execAsync(command))
|
.then(() => execAsync(command))
|
||||||
|
|||||||
@@ -156,6 +156,7 @@ export interface IClient extends ClientActions, ClientExtensions {
|
|||||||
characterName: string;
|
characterName: string;
|
||||||
character: ICharacter;
|
character: ICharacter;
|
||||||
|
|
||||||
|
isMobile: boolean;
|
||||||
isMod: boolean;
|
isMod: boolean;
|
||||||
shadowed: boolean;
|
shadowed: boolean;
|
||||||
supporterLevel: number;
|
supporterLevel: number;
|
||||||
@@ -181,8 +182,7 @@ export interface IClient extends ClientActions, ClientExtensions {
|
|||||||
safeX: number;
|
safeX: number;
|
||||||
safeY: number;
|
safeY: number;
|
||||||
lastPacket: number;
|
lastPacket: number;
|
||||||
lastAction: number;
|
lastBoopOrKissAction: number;
|
||||||
lastBoopAction: number;
|
|
||||||
lastExpressionAction: number;
|
lastExpressionAction: number;
|
||||||
lastSays: LastSay[];
|
lastSays: LastSay[];
|
||||||
lastX: number;
|
lastX: number;
|
||||||
@@ -278,6 +278,7 @@ export interface Controller {
|
|||||||
update(delta: number, now: number): void;
|
update(delta: number, now: number): void;
|
||||||
sparseUpdate?(): void;
|
sparseUpdate?(): void;
|
||||||
toggleWall?(x: number, y: number, type: TileType): void;
|
toggleWall?(x: number, y: number, type: TileType): void;
|
||||||
|
removeWalls?(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AccountService {
|
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 {
|
getState(): WorldState {
|
||||||
return {
|
return {
|
||||||
time: this.time,
|
time: this.time,
|
||||||
@@ -291,22 +298,13 @@ export class World {
|
|||||||
const nowSeconds = now / 1000;
|
const nowSeconds = now / 1000;
|
||||||
const deltaSeconds = delta / 1000;
|
const deltaSeconds = delta / 1000;
|
||||||
|
|
||||||
timingStart('update tiles');
|
timingStart('update tiles and colliders');
|
||||||
for (const map of this.maps) {
|
for (const map of this.maps) {
|
||||||
if (!map.dontUpdateTilesAndColliders) {
|
if (!map.dontUpdateTilesAndColliders) {
|
||||||
for (const region of map.regions) {
|
for (const region of map.regions) {
|
||||||
if (region.tilesDirty) {
|
if (region.tilesDirty) {
|
||||||
updateTileIndices(region, map);
|
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) {
|
if (region.colliderDirty) {
|
||||||
generateRegionCollider(region, map);
|
generateRegionCollider(region, map);
|
||||||
}
|
}
|
||||||
@@ -325,9 +323,7 @@ export class World {
|
|||||||
|
|
||||||
if (delta > 0) {
|
if (delta > 0) {
|
||||||
if (entity.vx !== 0 || entity.vy !== 0) {
|
if (entity.vx !== 0 || entity.vy !== 0) {
|
||||||
timingStart('updatePosition()');
|
|
||||||
updatePosition(entity, delta, map);
|
updatePosition(entity, delta, map);
|
||||||
timingEnd();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
entity.timestamp = nowSeconds;
|
entity.timestamp = nowSeconds;
|
||||||
@@ -370,15 +366,15 @@ export class World {
|
|||||||
timingEnd();
|
timingEnd();
|
||||||
|
|
||||||
timingStart('timeoutEntityExpression + inTheAirDelay');
|
timingStart('timeoutEntityExpression + inTheAirDelay');
|
||||||
for (const { pony } of this.clients) {
|
for (const client of this.clients) {
|
||||||
// timeout expressions
|
// timeout expressions
|
||||||
if (pony.exprTimeout && pony.exprTimeout < now) {
|
if (client.pony.exprTimeout && client.pony.exprTimeout < now) {
|
||||||
setEntityExpression(pony, undefined); // NOTE: creates updates
|
setEntityExpression(client.pony, undefined); // NOTE: creates updates
|
||||||
}
|
}
|
||||||
|
|
||||||
// count down in-the-air delay
|
// count down in-the-air delay
|
||||||
if (pony.inTheAirDelay !== undefined && pony.inTheAirDelay > 0) {
|
if (client.pony.inTheAirDelay !== undefined && client.pony.inTheAirDelay > 0) {
|
||||||
pony.inTheAirDelay -= deltaSeconds;
|
client.pony.inTheAirDelay -= deltaSeconds;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
timingEnd();
|
timingEnd();
|
||||||
|
|||||||
@@ -94,7 +94,6 @@ export function mockClient(fields: any = {}): IClient {
|
|||||||
subscribes: [],
|
subscribes: [],
|
||||||
saysQueue: [],
|
saysQueue: [],
|
||||||
lastSays: [],
|
lastSays: [],
|
||||||
lastAction: 0,
|
|
||||||
lastBoopAction: 0,
|
lastBoopAction: 0,
|
||||||
lastExpressionAction: 0,
|
lastExpressionAction: 0,
|
||||||
viewWidth: 3,
|
viewWidth: 3,
|
||||||
|
|||||||
@@ -132,7 +132,6 @@ describe('playerUtils', () => {
|
|||||||
safeX: 10,
|
safeX: 10,
|
||||||
safeY: 20,
|
safeY: 20,
|
||||||
lastPacket: 123,
|
lastPacket: 123,
|
||||||
lastAction: 0,
|
|
||||||
lastBoopAction: 0,
|
lastBoopAction: 0,
|
||||||
lastExpressionAction: 0,
|
lastExpressionAction: 0,
|
||||||
lastX: 10,
|
lastX: 10,
|
||||||
@@ -516,11 +515,11 @@ describe('playerUtils', () => {
|
|||||||
|
|
||||||
describe('canPerformAction()', () => {
|
describe('canPerformAction()', () => {
|
||||||
it('returns true if last action date is below current time', () => {
|
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', () => {
|
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 = mockClient();
|
||||||
client.map = createServerMap('foo', 0, 1, 1);
|
client.map = createServerMap('foo', 0, 1, 1);
|
||||||
client.pony.region = client.map.regions[0];
|
client.pony.region = client.map.regions[0];
|
||||||
client.lastAction = 0;
|
client.lastExpressionAction = 0;
|
||||||
|
client.lastBoopOrKissAction = 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sends boop action', () => {
|
it('sends boop action', () => {
|
||||||
@@ -586,12 +586,12 @@ describe('playerUtils', () => {
|
|||||||
expect(client.pony.options!.expr).equal(EMPTY_EXPRESSION);
|
expect(client.pony.options!.expr).equal(EMPTY_EXPRESSION);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('updates last boop action', () => {
|
it('updates last boop/kiss action', () => {
|
||||||
client.lastBoopAction = 0;
|
client.lastBoopOrKissAction = 0;
|
||||||
|
|
||||||
boop(client, 100);
|
boop(client, 100);
|
||||||
|
|
||||||
expect(client.lastBoopAction).equal(100 + 500);
|
expect(client.lastBoopOrKissAction).equal(100 + 500);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('executes boop on found entity', () => {
|
it('executes boop on found entity', () => {
|
||||||
@@ -618,7 +618,7 @@ describe('playerUtils', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('does nothing if cannot perform action', () => {
|
it('does nothing if cannot perform action', () => {
|
||||||
client.lastAction = 1000;
|
client.lastBoopOrKissAction = 1000;
|
||||||
|
|
||||||
boop(client, 0);
|
boop(client, 0);
|
||||||
|
|
||||||
@@ -646,7 +646,7 @@ describe('playerUtils', () => {
|
|||||||
|
|
||||||
it('does not update flags if cannot perform action', () => {
|
it('does not update flags if cannot perform action', () => {
|
||||||
const client = mockClient();
|
const client = mockClient();
|
||||||
client.lastAction = Date.now() + 1000;
|
client.lastBoopOrKissAction = Date.now() + 1000;
|
||||||
client.pony.state = 0;
|
client.pony.state = 0;
|
||||||
|
|
||||||
turnHead(client);
|
turnHead(client);
|
||||||
@@ -703,7 +703,7 @@ describe('playerUtils', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('does nothing if cannot perform action', () => {
|
it('does nothing if cannot perform action', () => {
|
||||||
client.lastAction = Date.now() + 1000;
|
client.lastBoopOrKissAction = Date.now() + 1000;
|
||||||
client.pony.state = 0;
|
client.pony.state = 0;
|
||||||
|
|
||||||
stand(client);
|
stand(client);
|
||||||
@@ -752,7 +752,7 @@ describe('playerUtils', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('does nothing if cannot perform action', () => {
|
it('does nothing if cannot perform action', () => {
|
||||||
client.lastAction = Date.now() + 1000;
|
client.lastBoopOrKissAction = Date.now() + 1000;
|
||||||
client.pony.state = 0;
|
client.pony.state = 0;
|
||||||
|
|
||||||
sit(client, {});
|
sit(client, {});
|
||||||
@@ -796,7 +796,7 @@ describe('playerUtils', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('does nothing if cannot perform action', () => {
|
it('does nothing if cannot perform action', () => {
|
||||||
client.lastAction = Date.now() + 1000;
|
client.lastBoopOrKissAction = Date.now() + 1000;
|
||||||
client.pony.state = 0;
|
client.pony.state = 0;
|
||||||
|
|
||||||
lie(client);
|
lie(client);
|
||||||
@@ -844,7 +844,7 @@ describe('playerUtils', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('does nothing if already flying', () => {
|
it('does nothing if already flying', () => {
|
||||||
client.lastAction = Date.now() + 1000;
|
client.lastBoopOrKissAction = Date.now() + 1000;
|
||||||
client.pony.state = EntityState.PonyFlying;
|
client.pony.state = EntityState.PonyFlying;
|
||||||
|
|
||||||
fly(client);
|
fly(client);
|
||||||
@@ -854,7 +854,7 @@ describe('playerUtils', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('does nothing if cannot perform action', () => {
|
it('does nothing if cannot perform action', () => {
|
||||||
client.lastAction = Date.now() + 1000;
|
client.lastBoopOrKissAction = Date.now() + 1000;
|
||||||
client.pony.state = 0;
|
client.pony.state = 0;
|
||||||
|
|
||||||
fly(client);
|
fly(client);
|
||||||
@@ -896,7 +896,7 @@ describe('playerUtils', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('does nothing if cannot perform action', () => {
|
it('does nothing if cannot perform action', () => {
|
||||||
client.lastAction = Date.now() + 1000;
|
client.lastExpressionAction = Date.now() + 1000;
|
||||||
|
|
||||||
expressionAction(client, Action.Yawn);
|
expressionAction(client, Action.Yawn);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user