now use custom emotes with tweens instead of transistions
@ -68,6 +68,7 @@ export class SocketManager {
|
||||
private rooms: Map<string, GameRoom> = new Map<string, GameRoom>();
|
||||
|
||||
constructor() {
|
||||
|
||||
clientEventsEmitter.registerToClientJoin((clientUUid: string, roomId: string) => {
|
||||
gaugeManager.incNbClientPerRoomGauge(roomId);
|
||||
});
|
||||
|
BIN
front/dist/resources/emotes/clap-emote.png
vendored
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
front/dist/resources/emotes/hand-emote.png
vendored
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
front/dist/resources/emotes/heart-emote.png
vendored
Normal file
After Width: | Height: | Size: 7.9 KiB |
BIN
front/dist/resources/emotes/pipo-popupemotes001.png
vendored
Before Width: | Height: | Size: 747 B |
BIN
front/dist/resources/emotes/pipo-popupemotes002.png
vendored
Before Width: | Height: | Size: 920 B |
BIN
front/dist/resources/emotes/pipo-popupemotes021.png
vendored
Before Width: | Height: | Size: 810 B |
BIN
front/dist/resources/emotes/taba-clap-emote.png
vendored
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.9 KiB |
BIN
front/dist/resources/emotes/taba-thumbsup-emote.png
vendored
Before Width: | Height: | Size: 1.9 KiB |
BIN
front/dist/resources/emotes/thanks-emote.png
vendored
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
front/dist/resources/emotes/thumb-down-emote.png
vendored
Normal file
After Width: | Height: | Size: 8.6 KiB |
BIN
front/dist/resources/emotes/thumb-up-emote.png
vendored
Normal file
After Width: | Height: | Size: 8.6 KiB |
@ -11,7 +11,7 @@ class EmoteEventStream {
|
||||
public stream = this._stream.asObservable();
|
||||
|
||||
|
||||
onMessage(userId: number, emoteName:string) {
|
||||
fire(userId: number, emoteName:string) {
|
||||
this._stream.next({userId, emoteName});
|
||||
}
|
||||
}
|
||||
|
@ -149,7 +149,7 @@ export class RoomConnection implements RoomConnection {
|
||||
payload = subMessage.getItemeventmessage();
|
||||
} else if (subMessage.hasEmoteeventmessage()) {
|
||||
const emoteMessage = subMessage.getEmoteeventmessage() as EmoteEventMessage;
|
||||
emoteEventStream.onMessage(emoteMessage.getActoruserid(), emoteMessage.getEmote());
|
||||
emoteEventStream.fire(emoteMessage.getActoruserid(), emoteMessage.getEmote());
|
||||
} else {
|
||||
throw new Error('Unexpected batch message type');
|
||||
}
|
||||
|
@ -3,12 +3,10 @@ import {DEPTH_UI_INDEX} from "../Game/DepthIndexes";
|
||||
import {waScaleManager} from "../Services/WaScaleManager";
|
||||
|
||||
export interface RadialMenuItem {
|
||||
sprite: string,
|
||||
frame: number,
|
||||
image: string,
|
||||
name: string,
|
||||
}
|
||||
|
||||
const menuRadius = 60;
|
||||
export const RadialMenuClickEvent = 'radialClick';
|
||||
|
||||
export class RadialMenu extends Phaser.GameObjects.Container {
|
||||
@ -27,24 +25,26 @@ export class RadialMenu extends Phaser.GameObjects.Container {
|
||||
|
||||
private initItems() {
|
||||
const itemsNumber = this.items.length;
|
||||
this.items.forEach((item, index) => this.createRadialElement(item, index, itemsNumber))
|
||||
const menuRadius = 70 + (waScaleManager.uiScalingFactor - 1) * 20;
|
||||
this.items.forEach((item, index) => this.createRadialElement(item, index, itemsNumber, menuRadius))
|
||||
}
|
||||
|
||||
private createRadialElement(item: RadialMenuItem, index: number, itemsNumber: number) {
|
||||
const image = new Sprite(this.scene, 0, menuRadius, item.sprite, item.frame);
|
||||
private createRadialElement(item: RadialMenuItem, index: number, itemsNumber: number, menuRadius: number) {
|
||||
const image = new Sprite(this.scene, 0, menuRadius, item.image);
|
||||
this.add(image);
|
||||
this.scene.sys.updateList.add(image);
|
||||
image.setDepth(DEPTH_UI_INDEX)
|
||||
const scalingFactor = waScaleManager.uiScalingFactor * 0.075;
|
||||
image.setScale(scalingFactor)
|
||||
image.setInteractive({
|
||||
hitArea: new Phaser.Geom.Circle(0, 0, 25),
|
||||
hitAreaCallback: Phaser.Geom.Circle.Contains, //eslint-disable-line @typescript-eslint/unbound-method
|
||||
useHandCursor: true,
|
||||
});
|
||||
image.on('pointerdown', () => this.emit(RadialMenuClickEvent, item));
|
||||
image.on('pointerover', () => {
|
||||
this.scene.tweens.add({
|
||||
targets: image,
|
||||
scale: 2,
|
||||
props: {
|
||||
scale: 2 * scalingFactor,
|
||||
},
|
||||
duration: 500,
|
||||
ease: 'Power3',
|
||||
})
|
||||
@ -52,7 +52,9 @@ export class RadialMenu extends Phaser.GameObjects.Container {
|
||||
image.on('pointerout', () => {
|
||||
this.scene.tweens.add({
|
||||
targets: image,
|
||||
scale: 1,
|
||||
props: {
|
||||
scale: scalingFactor,
|
||||
},
|
||||
duration: 500,
|
||||
ease: 'Power3',
|
||||
})
|
||||
|
@ -5,7 +5,6 @@ import Container = Phaser.GameObjects.Container;
|
||||
import Sprite = Phaser.GameObjects.Sprite;
|
||||
import {TextureError} from "../../Exception/TextureError";
|
||||
import {Companion} from "../Companion/Companion";
|
||||
import {getEmoteAnimName} from "../Game/EmoteManager";
|
||||
import type {GameScene} from "../Game/GameScene";
|
||||
import {DEPTH_INGAME_TEXT_INDEX} from "../Game/DepthIndexes";
|
||||
import {waScaleManager} from "../Services/WaScaleManager";
|
||||
@ -32,6 +31,7 @@ export abstract class Character extends Container {
|
||||
private invisible: boolean;
|
||||
public companion?: Companion;
|
||||
private emote: Phaser.GameObjects.Sprite | null = null;
|
||||
private emoteTween: Phaser.Tweens.Tween|null = null;
|
||||
|
||||
constructor(scene: GameScene,
|
||||
x: number,
|
||||
@ -246,24 +246,76 @@ export abstract class Character extends Container {
|
||||
|
||||
playEmote(emoteKey: string) {
|
||||
this.cancelPreviousEmote();
|
||||
|
||||
const scalingFactor = waScaleManager.uiScalingFactor * 0.05;
|
||||
const emoteY = -30 - scalingFactor * 10;
|
||||
|
||||
this.playerName.setVisible(false);
|
||||
this.emote = new Sprite(this.scene, 0, -30 - waScaleManager.uiScalingFactor * 10, emoteKey, 1);
|
||||
this.emote.setDepth(DEPTH_INGAME_TEXT_INDEX);
|
||||
this.emote.setScale(waScaleManager.uiScalingFactor)
|
||||
this.emote = new Sprite(this.scene, 0, 0, emoteKey);
|
||||
this.emote.setAlpha(0);
|
||||
this.emote.setScale(0.1 * scalingFactor);
|
||||
this.add(this.emote);
|
||||
this.scene.sys.updateList.add(this.emote);
|
||||
this.emote.play(getEmoteAnimName(emoteKey));
|
||||
this.emote.on(Phaser.Animations.Events.ANIMATION_COMPLETE, () => {
|
||||
this.emote?.destroy();
|
||||
this.emote = null;
|
||||
this.playerName.setVisible(true);
|
||||
|
||||
this.createStartTransition(scalingFactor, emoteY);
|
||||
}
|
||||
|
||||
private createStartTransition(scalingFactor: number, emoteY: number) {
|
||||
this.emoteTween = this.scene.tweens.add({
|
||||
targets: this.emote,
|
||||
props: {
|
||||
scale: scalingFactor,
|
||||
alpha: 1,
|
||||
y: emoteY,
|
||||
},
|
||||
ease: 'Power2',
|
||||
duration: 500,
|
||||
onComplete: () => {
|
||||
this.startPulseTransition(emoteY, scalingFactor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private startPulseTransition(emoteY: number, scalingFactor: number) {
|
||||
this.emoteTween = this.scene.tweens.add({
|
||||
targets: this.emote,
|
||||
props: {
|
||||
y: emoteY * 1.3,
|
||||
scale: scalingFactor * 1.1
|
||||
},
|
||||
duration: 250,
|
||||
yoyo: true,
|
||||
repeat: 1,
|
||||
completeDelay: 200,
|
||||
onComplete: () => {
|
||||
this.startExitTransition(emoteY);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private startExitTransition(emoteY: number) {
|
||||
this.emoteTween = this.scene.tweens.add({
|
||||
targets: this.emote,
|
||||
props: {
|
||||
alpha: 0,
|
||||
y: 2 * emoteY,
|
||||
},
|
||||
ease: 'Power2',
|
||||
duration: 500,
|
||||
onComplete: () => {
|
||||
this.destroyEmote();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cancelPreviousEmote() {
|
||||
if (!this.emote) return;
|
||||
|
||||
this.emoteTween?.remove();
|
||||
this.destroyEmote()
|
||||
}
|
||||
|
||||
private destroyEmote() {
|
||||
this.emote?.destroy();
|
||||
this.emote = null;
|
||||
this.playerName.setVisible(true);
|
||||
|
@ -1,38 +1,30 @@
|
||||
import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
||||
import {createLoadingPromise} from "../Entity/PlayerTexturesLoadingManager";
|
||||
import {emoteEventStream} from "../../Connexion/EmoteEventStream";
|
||||
import type {GameScene} from "./GameScene";
|
||||
import type {RadialMenuItem} from "../Components/RadialMenu";
|
||||
import LoaderPlugin = Phaser.Loader.LoaderPlugin;
|
||||
import type {Subscription} from "rxjs";
|
||||
|
||||
enum RegisteredEmoteTypes {
|
||||
short = 1,
|
||||
long = 2,
|
||||
}
|
||||
|
||||
interface RegisteredEmote extends BodyResourceDescriptionInterface {
|
||||
name: string;
|
||||
img: string;
|
||||
type: RegisteredEmoteTypes
|
||||
}
|
||||
|
||||
//the last 3 emotes are courtesy of @tabascoeye
|
||||
export const emotes: {[key: string]: RegisteredEmote} = {
|
||||
'emote-exclamation': {name: 'emote-exclamation', img: 'resources/emotes/pipo-popupemotes001.png', type: RegisteredEmoteTypes.short, },
|
||||
'emote-interrogation': {name: 'emote-interrogation', img: 'resources/emotes/pipo-popupemotes002.png', type: RegisteredEmoteTypes.short},
|
||||
'emote-sleep': {name: 'emote-sleep', img: 'resources/emotes/pipo-popupemotes021.png', type: RegisteredEmoteTypes.short},
|
||||
'emote-clap': {name: 'emote-clap', img: 'resources/emotes/taba-clap-emote.png', type: RegisteredEmoteTypes.short},
|
||||
'emote-thumbsdown': {name: 'emote-thumbsdown', img: 'resources/emotes/taba-thumbsdown-emote.png', type: RegisteredEmoteTypes.long},
|
||||
'emote-thumbsup': {name: 'emote-thumbsup', img: 'resources/emotes/taba-thumbsup-emote.png', type: RegisteredEmoteTypes.long},
|
||||
'emote-heart': {name: 'emote-heart', img: 'resources/emotes/heart-emote.png'},
|
||||
'emote-clap': {name: 'emote-clap', img: 'resources/emotes/clap-emote.png'},
|
||||
'emote-hand': {name: 'emote-hand', img: 'resources/emotes/hand-emote.png'},
|
||||
'emote-thanks': {name: 'emote-thanks', img: 'resources/emotes/thanks-emote.png'},
|
||||
'emote-thumb-up': {name: 'emote-thumb-up', img: 'resources/emotes/thumb-up-emote.png'},
|
||||
'emote-thumb-down': {name: 'emote-thumb-down', img: 'resources/emotes/thumb-down-emote.png'},
|
||||
};
|
||||
|
||||
export const getEmoteAnimName = (emoteKey: string): string => {
|
||||
return 'anim-'+emoteKey;
|
||||
}
|
||||
|
||||
export class EmoteManager {
|
||||
private subscription: Subscription;
|
||||
|
||||
constructor(private scene: GameScene) {
|
||||
emoteEventStream.stream.subscribe((event) => {
|
||||
this.subscription = emoteEventStream.stream.subscribe((event) => {
|
||||
const actor = this.scene.MapPlayersByKey.get(event.userId);
|
||||
if (actor) {
|
||||
this.lazyLoadEmoteTexture(event.emoteName).then(emoteKey => {
|
||||
@ -41,45 +33,41 @@ export class EmoteManager {
|
||||
}
|
||||
})
|
||||
}
|
||||
createLoadingPromise(loadPlugin: LoaderPlugin, playerResourceDescriptor: BodyResourceDescriptionInterface) {
|
||||
return new Promise<string>((res) => {
|
||||
if (loadPlugin.textureManager.exists(playerResourceDescriptor.name)) {
|
||||
return res(playerResourceDescriptor.name);
|
||||
}
|
||||
loadPlugin.image(playerResourceDescriptor.name, playerResourceDescriptor.img);
|
||||
loadPlugin.once('filecomplete-image-' + playerResourceDescriptor.name, () => res(playerResourceDescriptor.name));
|
||||
});
|
||||
}
|
||||
|
||||
lazyLoadEmoteTexture(textureKey: string): Promise<string> {
|
||||
const emoteDescriptor = emotes[textureKey];
|
||||
if (emoteDescriptor === undefined) {
|
||||
throw 'Emote not found!';
|
||||
}
|
||||
const loadPromise = createLoadingPromise(this.scene.load, emoteDescriptor, {
|
||||
frameWidth: 32,
|
||||
frameHeight: 32,
|
||||
});
|
||||
const loadPromise = this.createLoadingPromise(this.scene.load, emoteDescriptor);
|
||||
this.scene.load.start();
|
||||
return loadPromise.then(() => {
|
||||
if (this.scene.anims.exists(getEmoteAnimName(textureKey))) {
|
||||
return Promise.resolve(textureKey);
|
||||
}
|
||||
const frameConfig = emoteDescriptor.type === RegisteredEmoteTypes.short ? {frames: [0,1,2,2]} : {frames : [0,1,2,3,4,]};
|
||||
this.scene.anims.create({
|
||||
key: getEmoteAnimName(textureKey),
|
||||
frames: this.scene.anims.generateFrameNumbers(textureKey, frameConfig),
|
||||
frameRate: 5,
|
||||
repeat: 2,
|
||||
});
|
||||
return textureKey;
|
||||
});
|
||||
return loadPromise
|
||||
}
|
||||
|
||||
getMenuImages(): Promise<RadialMenuItem[]> {
|
||||
const promises = [];
|
||||
for (const key in emotes) {
|
||||
const promise = this.lazyLoadEmoteTexture(key).then((textureKey) => {
|
||||
const emoteDescriptor = emotes[textureKey];
|
||||
return {
|
||||
sprite: textureKey,
|
||||
image: textureKey,
|
||||
name: textureKey,
|
||||
frame: emoteDescriptor.type === RegisteredEmoteTypes.short ? 1 : 4,
|
||||
}
|
||||
});
|
||||
promises.push(promise);
|
||||
}
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
}
|
@ -940,6 +940,7 @@ ${escapedMessage}
|
||||
this.messageSubscription?.unsubscribe();
|
||||
this.userInputManager.destroy();
|
||||
this.pinchManager?.destroy();
|
||||
this.emoteManager.destroy();
|
||||
|
||||
for(const iframeEvents of this.iframeSubscriptionList){
|
||||
iframeEvents.unsubscribe();
|
||||
|
@ -54,7 +54,7 @@ class WaScaleManager {
|
||||
* This is used to scale back the ui components to counter-act the zoom.
|
||||
*/
|
||||
public get uiScalingFactor(): number {
|
||||
return this.actualZoom > 1 ? 1 : 2;
|
||||
return this.actualZoom > 1 ? 1 : 1.2;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -183,7 +183,7 @@ export class IoSocketController {
|
||||
// If we get an HTTP 404, the token is invalid. Let's perform an anonymous login!
|
||||
console.warn('Cannot find user with uuid "'+userUuid+'". Performing an anonymous login instead.');
|
||||
} else if(err?.response?.status == 403) {
|
||||
// If we get an HTTP 404, the world is full. We need to broadcast a special error to the client.
|
||||
// If we get an HTTP 403, the world is full. We need to broadcast a special error to the client.
|
||||
// we finish immediately the upgrade then we will close the socket as soon as it starts opening.
|
||||
return res.upgrade({
|
||||
rejected: true,
|
||||
|