mirror of
https://github.com/Terncode/pixel.horse.git
synced 2026-09-24 21:55:52 +02:00
Merge remote-tracking branch 'upstream/archive' into 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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -342,6 +342,26 @@ Read more about it [here](https://stackoverflow.com/questions/1139762/ignore-fil
|
|||||||
|
|
||||||
## 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.
+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, excite } from './ponyAnimations';
|
import { boop, defaultHeadFrame, stand, sneeze, yawn, lie, sit, fly, laugh, kiss, excite } from './ponyAnimations';
|
||||||
import { createDefaultPony, syncLockedPonyInfo, toPalette, mockPaletteManager } from '../common/ponyInfo';
|
import { 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,
|
||||||
@@ -118,10 +118,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 = [
|
||||||
@@ -395,7 +392,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;
|
||||||
@@ -525,6 +523,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);
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ export const CONTRIBUTORS: Contributors[] = [
|
|||||||
group: 'Programmers',
|
group: 'Programmers',
|
||||||
contributors: [
|
contributors: [
|
||||||
{ name: 'Industrialice' },
|
{ name: 'Industrialice' },
|
||||||
|
{ name: 'Stubenhocker', links: ['https://twitter.com/Stubenhocker13'] },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -129,6 +130,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, excite } from './ponyAnimations';
|
import { yawn, laugh, sneeze, kiss, kissFly, kissFlyBug, excite } 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,
|
||||||
@@ -420,6 +421,22 @@ export function handleAction(game: PonyTownGame, id: number, action: Action) {
|
|||||||
setHeadAnimation(pony, excite);
|
setHeadAnimation(pony, excite);
|
||||||
}
|
}
|
||||||
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 {
|
||||||
@@ -1098,6 +1099,7 @@ export const enum Action {
|
|||||||
RequestEntityInfo,
|
RequestEntityInfo,
|
||||||
ACL,
|
ACL,
|
||||||
Magic,
|
Magic,
|
||||||
|
Kiss,
|
||||||
RemoveEntity,
|
RemoveEntity,
|
||||||
PlaceEntity,
|
PlaceEntity,
|
||||||
SwitchTool,
|
SwitchTool,
|
||||||
@@ -1221,6 +1223,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;
|
||||||
@@ -1387,10 +1390,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[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1548,10 +1558,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,
|
||||||
@@ -1582,10 +1626,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) {
|
||||||
|
|||||||
@@ -205,13 +205,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() {
|
||||||
|
|||||||
@@ -102,8 +102,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';
|
||||||
@@ -142,7 +142,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
@@ -258,6 +258,7 @@ export function createCommands(world: World): Command[] {
|
|||||||
action(['sneeze', 'achoo'], Action.Sneeze),
|
action(['sneeze', 'achoo'], Action.Sneeze),
|
||||||
action(['excite', 'tada'], Action.Excite),
|
action(['excite', 'tada'], Action.Excite),
|
||||||
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) => {
|
||||||
@@ -325,18 +326,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