diff --git a/front/src/Connexion/EmoteEventStream.ts b/front/src/Connexion/EmoteEventStream.ts index 9a639697..a0a3b543 100644 --- a/front/src/Connexion/EmoteEventStream.ts +++ b/front/src/Connexion/EmoteEventStream.ts @@ -2,7 +2,7 @@ import {Subject} from "rxjs"; interface EmoteEvent { userId: number, - emoteName: string, + emote: string, } class EmoteEventStream { @@ -11,8 +11,8 @@ class EmoteEventStream { public stream = this._stream.asObservable(); - fire(userId: number, emoteName:string) { - this._stream.next({userId, emoteName}); + fire(userId: number, emote:string) { + this._stream.next({userId, emote}); } } diff --git a/front/src/Phaser/Components/EmoteMenu.ts b/front/src/Phaser/Components/EmoteMenu.ts new file mode 100644 index 00000000..3415dfbf --- /dev/null +++ b/front/src/Phaser/Components/EmoteMenu.ts @@ -0,0 +1,71 @@ +import Sprite = Phaser.GameObjects.Sprite; +import Text = Phaser.GameObjects.Text; +import {DEPTH_UI_INDEX} from "../Game/DepthIndexes"; +import {waScaleManager} from "../Services/WaScaleManager"; + +export const EmoteMenuClickEvent = 'emoteClick'; + +export class EmoteMenu extends Phaser.GameObjects.Container { + private resizeCallback: OmitThisParameter<() => void>; + + constructor(scene: Phaser.Scene, x: number, y: number, private items: string[]) { + super(scene, x, y); + this.setDepth(DEPTH_UI_INDEX) + this.scene.add.existing(this); + this.initItems(); + + this.resize(); + this.resizeCallback = this.resize.bind(this); + this.scene.scale.on(Phaser.Scale.Events.RESIZE, this.resizeCallback); + } + + private initItems() { + const itemsNumber = this.items.length; + const menuRadius = 70 + (waScaleManager.uiScalingFactor - 1) * 20; + this.items.forEach((item, index) => this.createEmoteElement(item, index, itemsNumber, menuRadius)) + } + + private createEmoteElement(item: string, index: number, itemsNumber: number, menuRadius: number) { + // const image = new Sprite(this.scene, 0, menuRadius, item.image); + const image = new Text(this.scene, -12, menuRadius, item, {fontFamily: '"twemoji"', fontSize:'75px'}); + this.add(image); + // this.scene.sys.updateList.add(image); + const scalingFactor = waScaleManager.uiScalingFactor * 0.3; + image.setScale(scalingFactor) + image.setInteractive({ + useHandCursor: true, + }); + image.on('pointerdown', () => this.emit(EmoteMenuClickEvent, item)); + image.on('pointerover', () => { + this.scene.tweens.add({ + targets: image, + props: { + scale: 1.5 * scalingFactor, + }, + duration: 500, + ease: 'Power3', + }) + }); + image.on('pointerout', () => { + this.scene.tweens.add({ + targets: image, + props: { + scale: scalingFactor, + }, + duration: 500, + ease: 'Power3', + }) + }); + const angle = 2 * Math.PI * index / itemsNumber; + Phaser.Actions.RotateAroundDistance([image], {x: -12, y: -12}, angle, menuRadius); + } + + private resize() { + this.setScale(waScaleManager.uiScalingFactor); + } + + public destroy() { + this.scene.scale.removeListener(Phaser.Scale.Events.RESIZE, this.resizeCallback); + super.destroy(); + } +} \ No newline at end of file diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index a22242b1..8bfe21a0 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -31,7 +31,7 @@ export abstract class Character extends Container { //private teleportation: Sprite; private invisible: boolean; public companion?: Companion; - private emote: Phaser.GameObjects.Sprite | null = null; + private emote: Phaser.GameObjects.Text | null = null; private emoteTween: Phaser.Tweens.Tween|null = null; scene: GameScene; @@ -263,18 +263,18 @@ export abstract class Character extends Container { super.destroy(); } - playEmote(emoteKey: string) { + playEmote(emote: string) { this.cancelPreviousEmote(); - const scalingFactor = waScaleManager.uiScalingFactor * 0.05; - const emoteY = -30 - scalingFactor * 10; + const scalingFactor = waScaleManager.uiScalingFactor * 0.5; + const emoteY = -60 - scalingFactor * 10; this.playerName.setVisible(false); - this.emote = new Sprite(this.scene, 0, 0, emoteKey); + this.emote = new Text(this.scene, -12, 0, emote, {fontFamily: '"twemoji"', fontSize:'55px'}); this.emote.setAlpha(0); this.emote.setScale(0.1 * scalingFactor); this.add(this.emote); - this.scene.sys.updateList.add(this.emote); + // this.scene.sys.updateList.add(this.emote); this.createStartTransition(scalingFactor, emoteY); } diff --git a/front/src/Phaser/Game/EmoteManager.ts b/front/src/Phaser/Game/EmoteManager.ts index 2e0bbd67..8316240c 100644 --- a/front/src/Phaser/Game/EmoteManager.ts +++ b/front/src/Phaser/Game/EmoteManager.ts @@ -1,72 +1,26 @@ -import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures"; 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"; - -interface RegisteredEmote extends BodyResourceDescriptionInterface { - name: string; - img: string; -} - -export const emotes: {[key: string]: RegisteredEmote} = { - '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 emotes: string[] = ['❤️', '👏', '✋', '🙏', '👍', '👎']; export class EmoteManager { private subscription: Subscription; - + constructor(private scene: GameScene) { this.subscription = emoteEventStream.stream.subscribe((event) => { const actor = this.scene.MapPlayersByKey.get(event.userId); - if (actor) { - this.lazyLoadEmoteTexture(event.emoteName).then(emoteKey => { - actor.playEmote(emoteKey); - }) + if(actor) { + actor.playEmote(event.emote); } }) } - 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)); - }); + + getEmotes(): string[] { + // TODO: localstorage + management + return emotes; } - - lazyLoadEmoteTexture(textureKey: string): Promise { - const emoteDescriptor = emotes[textureKey]; - if (emoteDescriptor === undefined) { - throw 'Emote not found!'; - } - const loadPromise = this.createLoadingPromise(this.scene.load, emoteDescriptor); - this.scene.load.start(); - return loadPromise - } - - getMenuImages(): Promise { - const promises = []; - for (const key in emotes) { - const promise = this.lazyLoadEmoteTexture(key).then((textureKey) => { - return { - image: textureKey, - name: textureKey, - } - }); - promises.push(promise); - } - return Promise.all(promises); - } - + destroy() { this.subscription.unsubscribe(); } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 5521d8f2..f34098b8 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -1227,7 +1227,8 @@ export class GameScene extends DirtyScene { if (pointer.wasTouch && (pointer.event as TouchEvent).touches.length > 1) { return; //we don't want the menu to open when pinching on a touch screen. } - this.emoteManager.getMenuImages().then((emoteMenuElements) => this.CurrentPlayer.openOrCloseEmoteMenu(emoteMenuElements)) + + this.CurrentPlayer.openOrCloseEmoteMenu( this.emoteManager.getEmotes()); }) this.CurrentPlayer.on(requestEmoteEventName, (emoteKey: string) => { this.connection?.emitEmoteEvent(emoteKey); diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index 7ed366f7..cd6f3fed 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -3,7 +3,7 @@ import type {GameScene} from "../Game/GameScene"; import {UserInputEvent, UserInputManager} from "../UserInput/UserInputManager"; import {Character} from "../Entity/Character"; import {userMovingStore} from "../../Stores/GameStore"; -import {RadialMenu, RadialMenuClickEvent, RadialMenuItem} from "../Components/RadialMenu"; +import {EmoteMenu, EmoteMenuClickEvent} from "../Components/EmoteMenu"; export const hasMovedEventName = "hasMoved"; export const requestEmoteEventName = "requestEmote"; @@ -11,7 +11,7 @@ export const requestEmoteEventName = "requestEmote"; export class Player extends Character { private previousDirection: string = PlayerAnimationDirections.Down; private wasMoving: boolean = false; - private emoteMenu: RadialMenu|null = null; + private emoteMenu: EmoteMenu|null = null; private updateListener: () => void; constructor( @@ -94,7 +94,7 @@ export class Player extends Character { return this.wasMoving; } - openOrCloseEmoteMenu(emotes:RadialMenuItem[]) { + openOrCloseEmoteMenu(emotes:string[]) { if(this.emoteMenu) { this.closeEmoteMenu(); } else { @@ -102,13 +102,13 @@ export class Player extends Character { } } - openEmoteMenu(emotes:RadialMenuItem[]): void { + openEmoteMenu(emotes:string[]): void { this.cancelPreviousEmote(); - this.emoteMenu = new RadialMenu(this.scene, this.x, this.y, emotes) - this.emoteMenu.on(RadialMenuClickEvent, (item: RadialMenuItem) => { + this.emoteMenu = new EmoteMenu(this.scene, this.x, this.y, emotes) + this.emoteMenu.on(EmoteMenuClickEvent, (emote: string) => { this.closeEmoteMenu(); - this.emit(requestEmoteEventName, item.name); - this.playEmote(item.name); + this.emit(requestEmoteEventName, emote); + this.playEmote(emote); }); } diff --git a/front/style/fonts.scss b/front/style/fonts.scss index a4452599..d7386902 100644 --- a/front/style/fonts.scss +++ b/front/style/fonts.scss @@ -1,5 +1,10 @@ @import "~@fontsource/press-start-2p/index.css"; -*{ - font-family: "Press Start 2P",monospace; +@font-face { + font-family: twemoji; + src: url("./fonts/TwemojiMozilla.ttf") format('truetype'); } + +*{ + font-family: "twemoji","Press Start 2P",monospace; +} \ No newline at end of file diff --git a/front/style/fonts/TwemojiMozilla.ttf b/front/style/fonts/TwemojiMozilla.ttf new file mode 100644 index 00000000..6091c679 Binary files /dev/null and b/front/style/fonts/TwemojiMozilla.ttf differ diff --git a/front/style/style.scss b/front/style/style.scss index f3ce066b..0a48358b 100644 --- a/front/style/style.scss +++ b/front/style/style.scss @@ -39,7 +39,6 @@ body .message-info.warning { .video-container { position: relative; transition: all 0.2s ease; - background-color: #00000099; height: 100%; video { @@ -146,7 +145,6 @@ body .message-info.warning { } video.myCamVideo{ - background-color: #00000099; max-height: 20vh; width: 15vw; -webkit-transform: scaleX(-1); @@ -373,6 +371,7 @@ body { } .sidebar>div { max-height: 21%; + height: 21%; } .sidebar>div:hover { max-height: 25%;