diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index dd40b951..c58b3d9f 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -68,6 +68,7 @@ export class SocketManager { private rooms: Map = new Map(); constructor() { + clientEventsEmitter.registerToClientJoin((clientUUid: string, roomId: string) => { gaugeManager.incNbClientPerRoomGauge(roomId); }); diff --git a/front/dist/resources/emotes/clap-emote.png b/front/dist/resources/emotes/clap-emote.png new file mode 100644 index 00000000..a64f2e5f Binary files /dev/null and b/front/dist/resources/emotes/clap-emote.png differ diff --git a/front/dist/resources/emotes/hand-emote.png b/front/dist/resources/emotes/hand-emote.png new file mode 100644 index 00000000..3bc01acf Binary files /dev/null and b/front/dist/resources/emotes/hand-emote.png differ diff --git a/front/dist/resources/emotes/heart-emote.png b/front/dist/resources/emotes/heart-emote.png new file mode 100644 index 00000000..867d6be1 Binary files /dev/null and b/front/dist/resources/emotes/heart-emote.png differ diff --git a/front/dist/resources/emotes/pipo-popupemotes001.png b/front/dist/resources/emotes/pipo-popupemotes001.png deleted file mode 100644 index a3db6d6d..00000000 Binary files a/front/dist/resources/emotes/pipo-popupemotes001.png and /dev/null differ diff --git a/front/dist/resources/emotes/pipo-popupemotes002.png b/front/dist/resources/emotes/pipo-popupemotes002.png deleted file mode 100644 index 77033c0f..00000000 Binary files a/front/dist/resources/emotes/pipo-popupemotes002.png and /dev/null differ diff --git a/front/dist/resources/emotes/pipo-popupemotes021.png b/front/dist/resources/emotes/pipo-popupemotes021.png deleted file mode 100644 index c7a92465..00000000 Binary files a/front/dist/resources/emotes/pipo-popupemotes021.png and /dev/null differ diff --git a/front/dist/resources/emotes/taba-clap-emote.png b/front/dist/resources/emotes/taba-clap-emote.png deleted file mode 100644 index afdc6601..00000000 Binary files a/front/dist/resources/emotes/taba-clap-emote.png and /dev/null differ diff --git a/front/dist/resources/emotes/taba-thumbsdown-emote.png b/front/dist/resources/emotes/taba-thumbsdown-emote.png deleted file mode 100644 index 86e89c7b..00000000 Binary files a/front/dist/resources/emotes/taba-thumbsdown-emote.png and /dev/null differ diff --git a/front/dist/resources/emotes/taba-thumbsup-emote.png b/front/dist/resources/emotes/taba-thumbsup-emote.png deleted file mode 100644 index 46bfc7b4..00000000 Binary files a/front/dist/resources/emotes/taba-thumbsup-emote.png and /dev/null differ diff --git a/front/dist/resources/emotes/thanks-emote.png b/front/dist/resources/emotes/thanks-emote.png new file mode 100644 index 00000000..8e326ed5 Binary files /dev/null and b/front/dist/resources/emotes/thanks-emote.png differ diff --git a/front/dist/resources/emotes/thumb-down-emote.png b/front/dist/resources/emotes/thumb-down-emote.png new file mode 100644 index 00000000..8ec7c961 Binary files /dev/null and b/front/dist/resources/emotes/thumb-down-emote.png differ diff --git a/front/dist/resources/emotes/thumb-up-emote.png b/front/dist/resources/emotes/thumb-up-emote.png new file mode 100644 index 00000000..eecb0e57 Binary files /dev/null and b/front/dist/resources/emotes/thumb-up-emote.png differ diff --git a/front/src/Connexion/EmoteEventStream.ts b/front/src/Connexion/EmoteEventStream.ts index 97d0d213..9a639697 100644 --- a/front/src/Connexion/EmoteEventStream.ts +++ b/front/src/Connexion/EmoteEventStream.ts @@ -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}); } } diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index fa462f50..a64e5bfd 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -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'); } diff --git a/front/src/Phaser/Components/RadialMenu.ts b/front/src/Phaser/Components/RadialMenu.ts index d566258c..1094f73a 100644 --- a/front/src/Phaser/Components/RadialMenu.ts +++ b/front/src/Phaser/Components/RadialMenu.ts @@ -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', }) diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index 1975182c..b1a85943 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -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); diff --git a/front/src/Phaser/Game/EmoteManager.ts b/front/src/Phaser/Game/EmoteManager.ts index 5d8d7179..2e0bbd67 100644 --- a/front/src/Phaser/Game/EmoteManager.ts +++ b/front/src/Phaser/Game/EmoteManager.ts @@ -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((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 { 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 { 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(); + } } \ No newline at end of file diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index b6b3e57e..deebf9d3 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -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(); diff --git a/front/src/Phaser/Services/WaScaleManager.ts b/front/src/Phaser/Services/WaScaleManager.ts index ef375a39..ca8b668d 100644 --- a/front/src/Phaser/Services/WaScaleManager.ts +++ b/front/src/Phaser/Services/WaScaleManager.ts @@ -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; } } diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts index 15be68c7..6d120f50 100644 --- a/pusher/src/Controller/IoSocketController.ts +++ b/pusher/src/Controller/IoSocketController.ts @@ -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,