FEATURE: added the possibility toplay emotes

This commit is contained in:
kharhamel 2021-03-31 11:21:06 +02:00
parent b57a9957a3
commit a1d52b4265
23 changed files with 286 additions and 72 deletions

View file

@ -5,6 +5,9 @@ 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";
const playerNameY = - 25;
interface AnimationData {
key: string;
@ -23,6 +26,7 @@ export abstract class Character extends Container {
//private teleportation: Sprite;
private invisible: boolean;
public companion?: Companion;
private emote: Phaser.GameObjects.Sprite | null = null;
constructor(scene: Phaser.Scene,
x: number,
@ -54,7 +58,7 @@ export abstract class Character extends Container {
});
this.add(this.teleportation);*/
this.playerName = new BitmapText(scene, 0, - 25, 'main_font', name, 7);
this.playerName = new BitmapText(scene, 0, playerNameY, 'main_font', name, 7);
this.playerName.setOrigin(0.5).setCenterAlign().setDepth(99999);
this.add(this.playerName);
@ -225,7 +229,23 @@ export abstract class Character extends Container {
this.scene.sys.updateList.remove(sprite);
}
}
this.list.forEach(objectContaining => objectContaining.destroy())
super.destroy();
this.playerName.destroy();
}
playEmote(emoteKey: string) {
if (this.emote) return;
this.playerName.setVisible(false);
this.emote = new Sprite(this.scene, 0, -40, emoteKey, 1);
this.emote.setDepth(99999);
this.add(this.emote);
this.scene.sys.updateList.add(this.emote);
this.emote.play(getEmoteAnimName(emoteKey));
this.emote.on(Phaser.Animations.Events.SPRITE_ANIMATION_COMPLETE, () => {
this.emote?.destroy();
this.emote = null;
this.playerName.setVisible(true);
});
}
}

View file

@ -2,6 +2,10 @@ import LoaderPlugin = Phaser.Loader.LoaderPlugin;
import type {CharacterTexture} from "../../Connexion/LocalUser";
import {BodyResourceDescriptionInterface, LAYERS, PLAYER_RESOURCES} from "./PlayerTextures";
export interface FrameConfig {
frameWidth: number,
frameHeight: number,
}
export const loadAllLayers = (load: LoaderPlugin): BodyResourceDescriptionInterface[][] => {
const returnArray:BodyResourceDescriptionInterface[][] = [];
@ -26,7 +30,10 @@ export const loadAllDefaultModels = (load: LoaderPlugin): BodyResourceDescriptio
export const loadCustomTexture = (loaderPlugin: LoaderPlugin, texture: CharacterTexture) : Promise<BodyResourceDescriptionInterface> => {
const name = 'customCharacterTexture'+texture.id;
const playerResourceDescriptor: BodyResourceDescriptionInterface = {name, img: texture.url, level: texture.level}
return createLoadingPromise(loaderPlugin, playerResourceDescriptor);
return createLoadingPromise(loaderPlugin, playerResourceDescriptor, {
frameWidth: 32,
frameHeight: 32
});
}
export const lazyLoadPlayerCharacterTextures = (loadPlugin: LoaderPlugin, texturekeys:Array<string|BodyResourceDescriptionInterface>): Promise<string[]> => {
@ -36,7 +43,10 @@ export const lazyLoadPlayerCharacterTextures = (loadPlugin: LoaderPlugin, textur
//TODO refactor
const playerResourceDescriptor = getRessourceDescriptor(textureKey);
if (playerResourceDescriptor && !loadPlugin.textureManager.exists(playerResourceDescriptor.name)) {
promisesList.push(createLoadingPromise(loadPlugin, playerResourceDescriptor));
promisesList.push(createLoadingPromise(loadPlugin, playerResourceDescriptor, {
frameWidth: 32,
frameHeight: 32
}));
}
}catch (err){
console.error(err);
@ -69,15 +79,12 @@ export const getRessourceDescriptor = (textureKey: string|BodyResourceDescriptio
throw 'Could not find a data for texture '+textureName;
}
const createLoadingPromise = (loadPlugin: LoaderPlugin, playerResourceDescriptor: BodyResourceDescriptionInterface) => {
export const createLoadingPromise = (loadPlugin: LoaderPlugin, playerResourceDescriptor: BodyResourceDescriptionInterface, frameConfig: FrameConfig) => {
return new Promise<BodyResourceDescriptionInterface>((res) => {
if (loadPlugin.textureManager.exists(playerResourceDescriptor.name)) {
return res(playerResourceDescriptor);
}
loadPlugin.spritesheet(playerResourceDescriptor.name, playerResourceDescriptor.img, {
frameWidth: 32,
frameHeight: 32
});
loadPlugin.spritesheet(playerResourceDescriptor.name, playerResourceDescriptor.img, frameConfig);
loadPlugin.once('filecomplete-spritesheet-' + playerResourceDescriptor.name, () => res(playerResourceDescriptor));
});
}

View file

@ -0,0 +1,83 @@
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
import {createLoadingPromise} from "../Entity/PlayerTexturesLoadingManager";
import {emoteEventStream} from "../../Connexion/EmoteEventStream";
import {GameScene} from "./GameScene";
enum RegisteredEmoteTypes {
short = 1,
long = 2,
}
interface RegisteredEmote extends BodyResourceDescriptionInterface {
name: string;
img: string;
type: RegisteredEmoteTypes
}
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-popupemotes002.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},
};
export const getEmoteAnimName = (emoteKey: string): string => {
return 'anim-'+emoteKey;
}
export class EmoteManager {
constructor(private scene: GameScene) {
//todo: use a radial menu instead?
this.registerEmoteOnKey('keyup-Y', 'emote-clap');
this.registerEmoteOnKey('keyup-U', 'emote-thumbsup');
this.registerEmoteOnKey('keyup-I', 'emote-thumbsdown');
this.registerEmoteOnKey('keyup-O', 'emote-exclamation');
this.registerEmoteOnKey('keyup-P', 'emote-interrogation');
this.registerEmoteOnKey('keyup-T', 'emote-sleep');
emoteEventStream.stream.subscribe((event) => {
const actor = this.scene.MapPlayersByKey.get(event.userId);
if (actor) {
this.lazyLoadEmoteTexture(event.emoteName).then(emoteKey => {
actor.playEmote(emoteKey);
})
}
})
}
private registerEmoteOnKey(keyboardKey: string, emoteKey: string) {
this.scene.input.keyboard.on(keyboardKey, () => {
this.scene.connection?.emitEmoteEvent(emoteKey);
this.lazyLoadEmoteTexture(emoteKey).then(emoteKey => {
this.scene.CurrentPlayer.playEmote(emoteKey);
})
});
}
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,
});
this.scene.load.start();
return loadPromise.then(() => {
const frameConfig = emoteDescriptor.type === RegisteredEmoteTypes.short ? {frames: [0,1,2]} : {frames : [0,1,2,3,4,5,6,7]};
this.scene.anims.create({
key: getEmoteAnimName(textureKey),
frames: this.scene.anims.generateFrameNumbers(textureKey, frameConfig),
frameRate: 3,
repeat: 2,
});
return textureKey;
});
}
}

View file

@ -91,6 +91,7 @@ import {touchScreenManager} from "../../Touch/TouchScreenManager";
import {PinchManager} from "../UserInput/PinchManager";
import {joystickBaseImg, joystickBaseKey, joystickThumbImg, joystickThumbKey} from "../Components/MobileJoystick";
import {waScaleManager} from "../Services/WaScaleManager";
import {EmoteManager} from "./EmoteManager";
export interface GameSceneInitInterface {
initPosition: PointInterface|null,
@ -189,6 +190,7 @@ export class GameScene extends DirtyScene implements CenterListener {
private physicsEnabled: boolean = true;
private mapTransitioning: boolean = false; //used to prevent transitions happenning at the same time.
private onVisibilityChangeCallback: () => void;
private emoteManager!: EmoteManager;
constructor(private room: Room, MapUrlFile: string, customKey?: string|undefined) {
super({
@ -226,6 +228,11 @@ export class GameScene extends DirtyScene implements CenterListener {
this.load.image(joystickBaseKey, joystickBaseImg);
this.load.image(joystickThumbKey, joystickThumbImg);
}
//todo: in an emote manager.
this.load.spritesheet('emote-music', 'resources/emotes/pipo-popupemotes005.png', {
frameHeight: 32,
frameWidth: 32,
});
this.load.on(FILE_LOAD_ERROR, (file: {src: string}) => {
// If we happen to be in HTTP and we are trying to load a URL in HTTPS only... (this happens only in dev environments)
if (window.location.protocol === 'http:' && file.src === this.MapUrlFile && file.src.startsWith('http:') && this.originalMapUrl === undefined) {
@ -509,6 +516,8 @@ export class GameScene extends DirtyScene implements CenterListener {
}
document.addEventListener('visibilitychange', this.onVisibilityChangeCallback);
this.emoteManager = new EmoteManager(this);
}
/**