Emote silent zone (#1342)

* Add an emote when the user is in silent zone

* Update silent icon strategy

* Update strategy for silent zone

 - Add svelte store
 - Show silent zone indication and replace camera

This update permit to hide silent zone when user is in Jitsi discussion

* Fix css silent zone

Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com>
This commit is contained in:
grégoire parant 2021-09-05 18:36:22 +02:00 committed by GitHub
parent d2b8d7dc04
commit 4f0bb95a38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 241 additions and 165 deletions

View File

@ -1,6 +1,6 @@
<script lang="typescript"> <script lang="typescript">
import {requestedScreenSharingState, screenSharingAvailableStore} from "../Stores/ScreenSharingStore"; import {requestedScreenSharingState, screenSharingAvailableStore} from "../Stores/ScreenSharingStore";
import {requestedCameraState, requestedMicrophoneState} from "../Stores/MediaStore"; import {isSilentStore, requestedCameraState, requestedMicrophoneState} from "../Stores/MediaStore";
import monitorImg from "./images/monitor.svg"; import monitorImg from "./images/monitor.svg";
import monitorCloseImg from "./images/monitor-close.svg"; import monitorCloseImg from "./images/monitor-close.svg";
import cinemaImg from "./images/cinema.svg"; import cinemaImg from "./images/cinema.svg";
@ -12,6 +12,7 @@
import {layoutModeStore} from "../Stores/StreamableCollectionStore"; import {layoutModeStore} from "../Stores/StreamableCollectionStore";
import {LayoutMode} from "../WebRtc/LayoutManager"; import {LayoutMode} from "../WebRtc/LayoutManager";
import {peerStore} from "../Stores/PeerStore"; import {peerStore} from "../Stores/PeerStore";
import {onDestroy} from "svelte";
function screenSharingClick(): void { function screenSharingClick(): void {
if ($requestedScreenSharingState === true) { if ($requestedScreenSharingState === true) {
@ -44,6 +45,12 @@
$layoutModeStore = LayoutMode.Presentation; $layoutModeStore = LayoutMode.Presentation;
} }
} }
let isSilent: boolean;
const unsubscribeIsSilent = isSilentStore.subscribe(value => {
isSilent = value;
});
onDestroy(unsubscribeIsSilent);
</script> </script>
<div> <div>
@ -55,22 +62,22 @@
<img src={layoutChatImg} style="padding: 2px" alt="Switch to presentation mode"> <img src={layoutChatImg} style="padding: 2px" alt="Switch to presentation mode">
{/if} {/if}
</div> </div>
<div class="btn-monitor" on:click={screenSharingClick} class:hide={!$screenSharingAvailableStore} class:enabled={$requestedScreenSharingState}> <div class="btn-monitor" on:click={screenSharingClick} class:hide={!$screenSharingAvailableStore || isSilent} class:enabled={$requestedScreenSharingState}>
{#if $requestedScreenSharingState} {#if $requestedScreenSharingState && !isSilent}
<img src={monitorImg} alt="Start screen sharing"> <img src={monitorImg} alt="Start screen sharing">
{:else} {:else}
<img src={monitorCloseImg} alt="Stop screen sharing"> <img src={monitorCloseImg} alt="Stop screen sharing">
{/if} {/if}
</div> </div>
<div class="btn-video" on:click={cameraClick} class:disabled={!$requestedCameraState}> <div class="btn-video" on:click={cameraClick} class:disabled={!$requestedCameraState || isSilent}>
{#if $requestedCameraState} {#if $requestedCameraState && !isSilent}
<img src={cinemaImg} alt="Turn on webcam"> <img src={cinemaImg} alt="Turn on webcam">
{:else} {:else}
<img src={cinemaCloseImg} alt="Turn off webcam"> <img src={cinemaCloseImg} alt="Turn off webcam">
{/if} {/if}
</div> </div>
<div class="btn-micro" on:click={microphoneClick} class:disabled={!$requestedMicrophoneState}> <div class="btn-micro" on:click={microphoneClick} class:disabled={!$requestedMicrophoneState || isSilent}>
{#if $requestedMicrophoneState} {#if $requestedMicrophoneState && !isSilent}
<img src={microphoneImg} alt="Turn on microphone"> <img src={microphoneImg} alt="Turn on microphone">
{:else} {:else}
<img src={microphoneCloseImg} alt="Turn off microphone"> <img src={microphoneCloseImg} alt="Turn off microphone">

View File

@ -1,6 +1,6 @@
<script lang="typescript"> <script lang="typescript">
import {obtainedMediaConstraintStore} from "../Stores/MediaStore"; import {obtainedMediaConstraintStore} from "../Stores/MediaStore";
import {localStreamStore} from "../Stores/MediaStore"; import {localStreamStore, isSilentStore} from "../Stores/MediaStore";
import SoundMeterWidget from "./SoundMeterWidget.svelte"; import SoundMeterWidget from "./SoundMeterWidget.svelte";
import {onDestroy} from "svelte"; import {onDestroy} from "svelte";
import {srcObject} from "./Video/utils"; import {srcObject} from "./Video/utils";
@ -17,14 +17,25 @@
onDestroy(unsubscribe); onDestroy(unsubscribe);
let isSilent: boolean;
const unsubscribeIsSilent = isSilentStore.subscribe(value => {
isSilent = value;
});
onDestroy(unsubscribeIsSilent);
</script> </script>
<div> <div>
<div class="video-container div-myCamVideo" class:hide={!$obtainedMediaConstraintStore.video}> <div class="video-container div-myCamVideo" class:hide={!$obtainedMediaConstraintStore.video || isSilent}>
{#if $localStreamStore.type === "success" && $localStreamStore.stream} {#if $localStreamStore.type === "success" && $localStreamStore.stream}
<video class="myCamVideo" use:srcObject={stream} autoplay muted playsinline></video> <video class="myCamVideo" use:srcObject={stream} autoplay muted playsinline></video>
<SoundMeterWidget stream={stream}></SoundMeterWidget> <SoundMeterWidget stream={stream}></SoundMeterWidget>
{/if} {/if}
</div> </div>
<div class="is-silent" class:hide={isSilent}>
Silent zone
</div>
</div> </div>

View File

@ -9,6 +9,7 @@ import type {GameScene} from "../Game/GameScene";
import { DEPTH_INGAME_TEXT_INDEX } from "../Game/DepthIndexes"; import { DEPTH_INGAME_TEXT_INDEX } from "../Game/DepthIndexes";
import { waScaleManager } from "../Services/WaScaleManager"; import { waScaleManager } from "../Services/WaScaleManager";
import type OutlinePipelinePlugin from "phaser3-rex-plugins/plugins/outlinepipeline-plugin.js"; import type OutlinePipelinePlugin from "phaser3-rex-plugins/plugins/outlinepipeline-plugin.js";
import { isSilentStore } from "../../Stores/MediaStore";
const playerNameY = -25; const playerNameY = -25;
@ -17,7 +18,7 @@ interface AnimationData {
frameRate: number; frameRate: number;
repeat: number; repeat: number;
frameModel: string; //todo use an enum frameModel: string; //todo use an enum
frames : number[] frames: number[];
} }
const interactiveRadius = 35; const interactiveRadius = 35;
@ -35,7 +36,8 @@ export abstract class Character extends Container {
private emoteTween: Phaser.Tweens.Tween | null = null; private emoteTween: Phaser.Tweens.Tween | null = null;
scene: GameScene; scene: GameScene;
constructor(scene: GameScene, constructor(
scene: GameScene,
x: number, x: number,
y: number, y: number,
texturesPromise: Promise<string[]>, texturesPromise: Promise<string[]>,
@ -50,17 +52,22 @@ export abstract class Character extends Container {
super(scene, x, y /*, texture, frame*/); super(scene, x, y /*, texture, frame*/);
this.scene = scene; this.scene = scene;
this.PlayerValue = name; this.PlayerValue = name;
this.invisible = true this.invisible = true;
this.sprites = new Map<string, Sprite>(); this.sprites = new Map<string, Sprite>();
//textures are inside a Promise in case they need to be lazyloaded before use. //textures are inside a Promise in case they need to be lazyloaded before use.
texturesPromise.then((textures) => { texturesPromise.then((textures) => {
this.addTextures(textures, frame); this.addTextures(textures, frame);
this.invisible = false this.invisible = false;
}) });
this.playerName = new Text(scene, 0, playerNameY, name, {fontFamily: '"Press Start 2P"', fontSize: '8px', strokeThickness: 2, stroke: "gray"}); this.playerName = new Text(scene, 0, playerNameY, name, {
fontFamily: '"Press Start 2P"',
fontSize: "8px",
strokeThickness: 2,
stroke: "gray",
});
this.playerName.setOrigin(0.5).setDepth(DEPTH_INGAME_TEXT_INDEX); this.playerName.setOrigin(0.5).setDepth(DEPTH_INGAME_TEXT_INDEX);
this.add(this.playerName); this.add(this.playerName);
@ -71,18 +78,17 @@ export abstract class Character extends Container {
useHandCursor: true, useHandCursor: true,
}); });
this.on('pointerover',() => { this.on("pointerover", () => {
this.getOutlinePlugin()?.add(this.playerName, { this.getOutlinePlugin()?.add(this.playerName, {
thickness: 2, thickness: 2,
outlineColor: 0xffff00 outlineColor: 0xffff00,
}); });
this.scene.markDirty(); this.scene.markDirty();
}); });
this.on('pointerout',() => { this.on("pointerout", () => {
this.getOutlinePlugin()?.remove(this.playerName); this.getOutlinePlugin()?.remove(this.playerName);
this.scene.markDirty(); this.scene.markDirty();
}) });
} }
scene.add.existing(this); scene.add.existing(this);
@ -97,17 +103,17 @@ export abstract class Character extends Container {
this.playAnimation(direction, moving); this.playAnimation(direction, moving);
if (typeof companion === 'string') { if (typeof companion === "string") {
this.addCompanion(companion, companionTexturePromise); this.addCompanion(companion, companionTexturePromise);
} }
} }
private getOutlinePlugin(): OutlinePipelinePlugin | undefined { private getOutlinePlugin(): OutlinePipelinePlugin | undefined {
return this.scene.plugins.get('rexOutlinePipeline') as unknown as OutlinePipelinePlugin|undefined; return this.scene.plugins.get("rexOutlinePipeline") as unknown as OutlinePipelinePlugin | undefined;
} }
public addCompanion(name: string, texturePromise?: Promise<string>): void { public addCompanion(name: string, texturePromise?: Promise<string>): void {
if (typeof texturePromise !== 'undefined') { if (typeof texturePromise !== "undefined") {
this.companion = new Companion(this.scene, this.x, this.y, name, texturePromise); this.companion = new Companion(this.scene, this.x, this.y, name, texturePromise);
} }
} }
@ -115,18 +121,18 @@ export abstract class Character extends Container {
public addTextures(textures: string[], frame?: string | number): void { public addTextures(textures: string[], frame?: string | number): void {
for (const texture of textures) { for (const texture of textures) {
if (this.scene && !this.scene.textures.exists(texture)) { if (this.scene && !this.scene.textures.exists(texture)) {
throw new TextureError('texture not found'); throw new TextureError("texture not found");
} }
const sprite = new Sprite(this.scene, 0, 0, texture, frame); const sprite = new Sprite(this.scene, 0, 0, texture, frame);
this.add(sprite); this.add(sprite);
this.getPlayerAnimations(texture).forEach(d => { this.getPlayerAnimations(texture).forEach((d) => {
this.scene.anims.create({ this.scene.anims.create({
key: d.key, key: d.key,
frames: this.scene.anims.generateFrameNumbers(d.frameModel, { frames: d.frames }), frames: this.scene.anims.generateFrameNumbers(d.frameModel, { frames: d.frames }),
frameRate: d.frameRate, frameRate: d.frameRate,
repeat: d.repeat repeat: d.repeat,
});
}); });
})
// Needed, otherwise, animations are not handled correctly. // Needed, otherwise, animations are not handled correctly.
if (this.scene) { if (this.scene) {
this.scene.sys.updateList.add(sprite); this.scene.sys.updateList.add(sprite);
@ -136,68 +142,77 @@ export abstract class Character extends Container {
} }
private getPlayerAnimations(name: string): AnimationData[] { private getPlayerAnimations(name: string): AnimationData[] {
return [{ return [
{
key: `${name}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Walk}`, key: `${name}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Walk}`,
frameModel: name, frameModel: name,
frames: [0, 1, 2, 1], frames: [0, 1, 2, 1],
frameRate: 10, frameRate: 10,
repeat: -1 repeat: -1,
}, { },
{
key: `${name}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Walk}`, key: `${name}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Walk}`,
frameModel: name, frameModel: name,
frames: [3, 4, 5, 4], frames: [3, 4, 5, 4],
frameRate: 10, frameRate: 10,
repeat: -1 repeat: -1,
}, { },
{
key: `${name}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Walk}`, key: `${name}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Walk}`,
frameModel: name, frameModel: name,
frames: [6, 7, 8, 7], frames: [6, 7, 8, 7],
frameRate: 10, frameRate: 10,
repeat: -1 repeat: -1,
}, { },
{
key: `${name}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Walk}`, key: `${name}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Walk}`,
frameModel: name, frameModel: name,
frames: [9, 10, 11, 10], frames: [9, 10, 11, 10],
frameRate: 10, frameRate: 10,
repeat: -1 repeat: -1,
},{ },
{
key: `${name}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Idle}`, key: `${name}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Idle}`,
frameModel: name, frameModel: name,
frames: [1], frames: [1],
frameRate: 10, frameRate: 10,
repeat: 1 repeat: 1,
}, { },
{
key: `${name}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Idle}`, key: `${name}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Idle}`,
frameModel: name, frameModel: name,
frames: [4], frames: [4],
frameRate: 10, frameRate: 10,
repeat: 1 repeat: 1,
}, { },
{
key: `${name}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Idle}`, key: `${name}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Idle}`,
frameModel: name, frameModel: name,
frames: [7], frames: [7],
frameRate: 10, frameRate: 10,
repeat: 1 repeat: 1,
}, { },
{
key: `${name}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Idle}`, key: `${name}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Idle}`,
frameModel: name, frameModel: name,
frames: [10], frames: [10],
frameRate: 10, frameRate: 10,
repeat: 1 repeat: 1,
}]; },
];
} }
protected playAnimation(direction: PlayerAnimationDirections, moving: boolean): void { protected playAnimation(direction: PlayerAnimationDirections, moving: boolean): void {
if (this.invisible) return; if (this.invisible) return;
for (const [texture, sprite] of this.sprites.entries()) { for (const [texture, sprite] of this.sprites.entries()) {
if (!sprite.anims) { if (!sprite.anims) {
console.error('ANIMS IS NOT DEFINED!!!'); console.error("ANIMS IS NOT DEFINED!!!");
return; return;
} }
if (moving && (!sprite.anims.currentAnim || sprite.anims.currentAnim.key !== direction)) { if (moving && (!sprite.anims.currentAnim || sprite.anims.currentAnim.key !== direction)) {
sprite.play(texture+'-'+direction+'-'+PlayerAnimationTypes.Walk, true); sprite.play(texture + "-" + direction + "-" + PlayerAnimationTypes.Walk, true);
} else if (!moving) { } else if (!moving) {
sprite.anims.play(texture + '-' + direction + '-'+PlayerAnimationTypes.Idle, true); sprite.anims.play(texture + "-" + direction + "-" + PlayerAnimationTypes.Idle, true);
} }
} }
} }
@ -205,7 +220,7 @@ export abstract class Character extends Container {
protected getBody(): Phaser.Physics.Arcade.Body { protected getBody(): Phaser.Physics.Arcade.Body {
const body = this.body; const body = this.body;
if (!(body instanceof Phaser.Physics.Arcade.Body)) { if (!(body instanceof Phaser.Physics.Arcade.Body)) {
throw new Error('Container does not have arcade body'); throw new Error("Container does not have arcade body");
} }
return body; return body;
} }
@ -216,16 +231,20 @@ export abstract class Character extends Container {
body.setVelocity(x, y); body.setVelocity(x, y);
// up or down animations are prioritized over left and right // up or down animations are prioritized over left and right
if (body.velocity.y < 0) { //moving up if (body.velocity.y < 0) {
//moving up
this.lastDirection = PlayerAnimationDirections.Up; this.lastDirection = PlayerAnimationDirections.Up;
this.playAnimation(PlayerAnimationDirections.Up, true); this.playAnimation(PlayerAnimationDirections.Up, true);
} else if (body.velocity.y > 0) { //moving down } else if (body.velocity.y > 0) {
//moving down
this.lastDirection = PlayerAnimationDirections.Down; this.lastDirection = PlayerAnimationDirections.Down;
this.playAnimation(PlayerAnimationDirections.Down, true); this.playAnimation(PlayerAnimationDirections.Down, true);
} else if (body.velocity.x > 0) { //moving right } else if (body.velocity.x > 0) {
//moving right
this.lastDirection = PlayerAnimationDirections.Right; this.lastDirection = PlayerAnimationDirections.Right;
this.playAnimation(PlayerAnimationDirections.Right, true); this.playAnimation(PlayerAnimationDirections.Right, true);
} else if (body.velocity.x < 0) { //moving left } else if (body.velocity.x < 0) {
//moving left
this.lastDirection = PlayerAnimationDirections.Left; this.lastDirection = PlayerAnimationDirections.Left;
this.playAnimation(PlayerAnimationDirections.Left, true); this.playAnimation(PlayerAnimationDirections.Left, true);
} }
@ -244,13 +263,13 @@ export abstract class Character extends Container {
say(text: string) { say(text: string) {
if (this.bubble) return; if (this.bubble) return;
this.bubble = new SpeechBubble(this.scene, this, text) this.bubble = new SpeechBubble(this.scene, this, text);
setTimeout(() => { setTimeout(() => {
if (this.bubble !== null) { if (this.bubble !== null) {
this.bubble.destroy(); this.bubble.destroy();
this.bubble = null; this.bubble = null;
} }
}, 3000) }, 3000);
} }
destroy(): void { destroy(): void {
@ -259,10 +278,17 @@ export abstract class Character extends Container {
this.scene.sys.updateList.remove(sprite); this.scene.sys.updateList.remove(sprite);
} }
} }
this.list.forEach(objectContaining => objectContaining.destroy()) this.list.forEach((objectContaining) => objectContaining.destroy());
super.destroy(); super.destroy();
} }
isSilent() {
isSilentStore.set(true);
}
noSilent() {
isSilentStore.set(false);
}
playEmote(emoteKey: string) { playEmote(emoteKey: string) {
this.cancelPreviousEmote(); this.cancelPreviousEmote();
@ -287,11 +313,11 @@ export abstract class Character extends Container {
alpha: 1, alpha: 1,
y: emoteY, y: emoteY,
}, },
ease: 'Power2', ease: "Power2",
duration: 500, duration: 500,
onComplete: () => { onComplete: () => {
this.startPulseTransition(emoteY, scalingFactor); this.startPulseTransition(emoteY, scalingFactor);
} },
}); });
} }
@ -300,7 +326,7 @@ export abstract class Character extends Container {
targets: this.emote, targets: this.emote,
props: { props: {
y: emoteY * 1.3, y: emoteY * 1.3,
scale: scalingFactor * 1.1 scale: scalingFactor * 1.1,
}, },
duration: 250, duration: 250,
yoyo: true, yoyo: true,
@ -308,7 +334,7 @@ export abstract class Character extends Container {
completeDelay: 200, completeDelay: 200,
onComplete: () => { onComplete: () => {
this.startExitTransition(emoteY); this.startExitTransition(emoteY);
} },
}); });
} }
@ -319,11 +345,11 @@ export abstract class Character extends Container {
alpha: 0, alpha: 0,
y: 2 * emoteY, y: 2 * emoteY,
}, },
ease: 'Power2', ease: "Power2",
duration: 500, duration: 500,
onComplete: () => { onComplete: () => {
this.destroyEmote(); this.destroyEmote();
} },
}); });
} }
@ -331,7 +357,7 @@ export abstract class Character extends Container {
if (!this.emote) return; if (!this.emote) return;
this.emoteTween?.remove(); this.emoteTween?.remove();
this.destroyEmote() this.destroyEmote();
} }
private destroyEmote() { private destroyEmote() {

View File

@ -5,19 +5,18 @@ import type {RadialMenuItem} from "../Components/RadialMenu";
import LoaderPlugin = Phaser.Loader.LoaderPlugin; import LoaderPlugin = Phaser.Loader.LoaderPlugin;
import type { Subscription } from "rxjs"; import type { Subscription } from "rxjs";
interface RegisteredEmote extends BodyResourceDescriptionInterface { interface RegisteredEmote extends BodyResourceDescriptionInterface {
name: string; name: string;
img: string; img: string;
} }
export const emotes: { [key: string]: RegisteredEmote } = { export const emotes: { [key: string]: RegisteredEmote } = {
'emote-heart': {name: 'emote-heart', img: 'resources/emotes/heart-emote.png'}, "emote-heart": { name: "emote-heart", img: "resources/emotes/heart-emote.png" },
'emote-clap': {name: 'emote-clap', img: 'resources/emotes/clap-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-hand": { name: "emote-hand", img: "resources/emotes/hand-emote.png" },
'emote-thanks': {name: 'emote-thanks', img: 'resources/emotes/thanks-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-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'}, "emote-thumb-down": { name: "emote-thumb-down", img: "resources/emotes/thumb-down-emote.png" },
}; };
export class EmoteManager { export class EmoteManager {
@ -27,11 +26,11 @@ export class EmoteManager {
this.subscription = emoteEventStream.stream.subscribe((event) => { this.subscription = emoteEventStream.stream.subscribe((event) => {
const actor = this.scene.MapPlayersByKey.get(event.userId); const actor = this.scene.MapPlayersByKey.get(event.userId);
if (actor) { if (actor) {
this.lazyLoadEmoteTexture(event.emoteName).then(emoteKey => { this.lazyLoadEmoteTexture(event.emoteName).then((emoteKey) => {
actor.playEmote(emoteKey); actor.playEmote(emoteKey);
}) });
} }
}) });
} }
createLoadingPromise(loadPlugin: LoaderPlugin, playerResourceDescriptor: BodyResourceDescriptionInterface) { createLoadingPromise(loadPlugin: LoaderPlugin, playerResourceDescriptor: BodyResourceDescriptionInterface) {
return new Promise<string>((res) => { return new Promise<string>((res) => {
@ -39,18 +38,20 @@ export class EmoteManager {
return res(playerResourceDescriptor.name); return res(playerResourceDescriptor.name);
} }
loadPlugin.image(playerResourceDescriptor.name, playerResourceDescriptor.img); loadPlugin.image(playerResourceDescriptor.name, playerResourceDescriptor.img);
loadPlugin.once('filecomplete-image-' + playerResourceDescriptor.name, () => res(playerResourceDescriptor.name)); loadPlugin.once("filecomplete-image-" + playerResourceDescriptor.name, () =>
res(playerResourceDescriptor.name)
);
}); });
} }
lazyLoadEmoteTexture(textureKey: string): Promise<string> { lazyLoadEmoteTexture(textureKey: string): Promise<string> {
const emoteDescriptor = emotes[textureKey]; const emoteDescriptor = emotes[textureKey];
if (emoteDescriptor === undefined) { if (emoteDescriptor === undefined) {
throw 'Emote not found!'; throw "Emote not found!";
} }
const loadPromise = this.createLoadingPromise(this.scene.load, emoteDescriptor); const loadPromise = this.createLoadingPromise(this.scene.load, emoteDescriptor);
this.scene.load.start(); this.scene.load.start();
return loadPromise return loadPromise;
} }
getMenuImages(): Promise<RadialMenuItem[]> { getMenuImages(): Promise<RadialMenuItem[]> {
@ -60,7 +61,7 @@ export class EmoteManager {
return { return {
image: textureKey, image: textureKey,
name: textureKey, name: textureKey,
} };
}); });
promises.push(promise); promises.push(promise);
} }

View File

@ -93,6 +93,7 @@ import { userIsAdminStore } from "../../Stores/GameStore";
import { layoutManagerActionStore } from "../../Stores/LayoutManagerStore"; import { layoutManagerActionStore } from "../../Stores/LayoutManagerStore";
import { EmbeddedWebsiteManager } from "./EmbeddedWebsiteManager"; import { EmbeddedWebsiteManager } from "./EmbeddedWebsiteManager";
import { GameMapPropertiesListener } from "./GameMapPropertiesListener"; import { GameMapPropertiesListener } from "./GameMapPropertiesListener";
import type { RadialMenuItem } from "../Components/RadialMenu";
export interface GameSceneInitInterface { export interface GameSceneInitInterface {
initPosition: PointInterface | null; initPosition: PointInterface | null;
@ -853,8 +854,10 @@ export class GameScene extends DirtyScene {
this.gameMap.onPropertyChange("silent", (newValue, oldValue) => { this.gameMap.onPropertyChange("silent", (newValue, oldValue) => {
if (newValue === undefined || newValue === false || newValue === "") { if (newValue === undefined || newValue === false || newValue === "") {
this.connection?.setSilent(false); this.connection?.setSilent(false);
this.CurrentPlayer.noSilent();
} else { } else {
this.connection?.setSilent(true); this.connection?.setSilent(true);
this.CurrentPlayer.isSilent();
} }
}); });
this.gameMap.onPropertyChange("playAudio", (newValue, oldValue, allProps) => { this.gameMap.onPropertyChange("playAudio", (newValue, oldValue, allProps) => {

View File

@ -37,7 +37,7 @@ export class Player extends Character {
this.emoteMenu.y = this.y; this.emoteMenu.y = this.y;
} }
}; };
this.scene.events.addListener('postupdate', this.updateListener); this.scene.events.addListener("postupdate", this.updateListener);
} }
moveUser(delta: number): void { moveUser(delta: number): void {
@ -104,7 +104,7 @@ export class Player extends Character {
openEmoteMenu(emotes: RadialMenuItem[]): void { openEmoteMenu(emotes: RadialMenuItem[]): void {
this.cancelPreviousEmote(); this.cancelPreviousEmote();
this.emoteMenu = new RadialMenu(this.scene, this.x, this.y, emotes) this.emoteMenu = new RadialMenu(this.scene, this.x, this.y, emotes);
this.emoteMenu.on(RadialMenuClickEvent, (item: RadialMenuItem) => { this.emoteMenu.on(RadialMenuClickEvent, (item: RadialMenuItem) => {
this.closeEmoteMenu(); this.closeEmoteMenu();
this.emit(requestEmoteEventName, item.name); this.emit(requestEmoteEventName, item.name);
@ -112,6 +112,13 @@ export class Player extends Character {
}); });
} }
isSilent() {
super.isSilent();
}
noSilent() {
super.noSilent();
}
closeEmoteMenu(): void { closeEmoteMenu(): void {
if (!this.emoteMenu) return; if (!this.emoteMenu) return;
this.emoteMenu.destroy(); this.emoteMenu.destroy();
@ -119,7 +126,7 @@ export class Player extends Character {
} }
destroy() { destroy() {
this.scene.events.removeListener('postupdate', this.updateListener); this.scene.events.removeListener("postupdate", this.updateListener);
super.destroy(); super.destroy();
} }
} }

View File

@ -569,3 +569,8 @@ localStreamStore.subscribe((streamResult) => {
* A store containing the real active media is mobile * A store containing the real active media is mobile
*/ */
export const obtainedMediaConstraintIsMobileStore = writable(false); export const obtainedMediaConstraintIsMobileStore = writable(false);
/**
* A store containing if user is silent, so if he is in silent zone. This permit to show et hide camera of user
*/
export const isSilentStore = writable(false);

View File

@ -1083,3 +1083,19 @@ div.action.danger p.action-body{
} }
} }
} }
div.is-silent {
position: absolute;
bottom: 40px;
border-radius: 15px 15px 15px 15px;
max-height: 20%;
transition: right 350ms;
right: -20vw;
background-color: black;
font-size: 20px;
color: white;
padding: 30px 20px;
}
div.is-silent.hide {
right: 15px;
}