diff --git a/front/src/Api/Events/ui/TriggerMessageEventHandler.ts b/front/src/Api/Events/ui/TriggerMessageEventHandler.ts
index fb64c742..f7da0ad2 100644
--- a/front/src/Api/Events/ui/TriggerMessageEventHandler.ts
+++ b/front/src/Api/Events/ui/TriggerMessageEventHandler.ts
@@ -3,9 +3,9 @@ import {
isTriggerActionMessageEvent,
removeActionMessage,
triggerActionMessage,
-} from './TriggerActionMessageEvent';
+} from "./TriggerActionMessageEvent";
-import * as tg from 'generic-type-guard';
+import * as tg from "generic-type-guard";
const isTriggerMessageEventObject = new tg.IsInterface()
.withProperties({
diff --git a/front/src/Components/App.svelte b/front/src/Components/App.svelte
index ec644c93..d65f699e 100644
--- a/front/src/Components/App.svelte
+++ b/front/src/Components/App.svelte
@@ -35,6 +35,8 @@
import WarningContainer from "./WarningContainer/WarningContainer.svelte";
import {layoutManagerVisibilityStore} from "../Stores/LayoutManagerStore";
import LayoutManager from "./LayoutManager/LayoutManager.svelte";
+ import {audioManagerVisibilityStore} from "../Stores/AudioManagerStore";
+ import AudioManager from "./AudioManager/AudioManager.svelte"
export let game: Game;
@@ -81,6 +83,11 @@
{/if}
+ {#if $audioManagerVisibilityStore}
+
diff --git a/front/src/Components/AudioManager/AudioManager.svelte b/front/src/Components/AudioManager/AudioManager.svelte
new file mode 100644
index 00000000..a78b4bde
--- /dev/null
+++ b/front/src/Components/AudioManager/AudioManager.svelte
@@ -0,0 +1,119 @@
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
diff --git a/front/src/Components/images/audio-mute.svg b/front/src/Components/images/audio-mute.svg
new file mode 100644
index 00000000..c2ad1eca
--- /dev/null
+++ b/front/src/Components/images/audio-mute.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/front/src/Components/images/audio.svg b/front/src/Components/images/audio.svg
new file mode 100644
index 00000000..190f7612
--- /dev/null
+++ b/front/src/Components/images/audio.svg
@@ -0,0 +1,8 @@
+
\ No newline at end of file
diff --git a/front/src/Phaser/Components/TextInput.ts b/front/src/Phaser/Components/TextInput.ts
deleted file mode 100644
index a8ea772f..00000000
--- a/front/src/Phaser/Components/TextInput.ts
+++ /dev/null
@@ -1,90 +0,0 @@
-
-const IGNORED_KEYS = new Set([
- 'Esc',
- 'Escape',
- 'Alt',
- 'Meta',
- 'Control',
- 'Ctrl',
- 'Space',
- 'Backspace'
-])
-
-export class TextInput extends Phaser.GameObjects.BitmapText {
- private minUnderLineLength = 4;
- private underLine: Phaser.GameObjects.Text;
- private domInput = document.createElement('input');
-
- constructor(scene: Phaser.Scene, x: number, y: number, maxLength: number, text: string,
- onChange: (text: string) => void) {
- super(scene, x, y, 'main_font', text, 32);
- this.setOrigin(0.5).setCenterAlign();
- this.scene.add.existing(this);
-
- const style = {fontFamily: 'Arial', fontSize: "32px", color: '#ffffff'};
- this.underLine = this.scene.add.text(x, y+1, this.getUnderLineBody(text.length), style);
- this.underLine.setOrigin(0.5);
-
- this.domInput.maxLength = maxLength;
- this.domInput.style.opacity = "0";
- if (text) {
- this.domInput.value = text;
- }
-
- this.domInput.addEventListener('keydown', event => {
- if (IGNORED_KEYS.has(event.key)) {
- return;
- }
-
- if (!/[a-zA-Z0-9:.!&?()+-]/.exec(event.key)) {
- event.preventDefault();
- }
- });
-
- this.domInput.addEventListener('input', (event) => {
- if (event.defaultPrevented) {
- return;
- }
- this.text = this.domInput.value;
- this.underLine.text = this.getUnderLineBody(this.text.length);
- onChange(this.text);
- });
-
- document.body.append(this.domInput);
- this.focus();
- }
-
- private getUnderLineBody(textLength:number): string {
- if (textLength < this.minUnderLineLength) textLength = this.minUnderLineLength;
- let text = '_______';
- for (let i = this.minUnderLineLength; i < textLength; i++) {
- text += '__';
- }
- return text;
- }
-
- getText(): string {
- return this.text;
- }
-
- setX(x: number): this {
- super.setX(x);
- this.underLine.x = x;
- return this;
- }
-
- setY(y: number): this {
- super.setY(y);
- this.underLine.y = y+1;
- return this;
- }
-
- focus() {
- this.domInput.focus();
- }
-
- destroy(): void {
- super.destroy();
- this.domInput.remove();
- }
-}
diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts
index 37d18acf..ce947224 100644
--- a/front/src/Phaser/Game/GameScene.ts
+++ b/front/src/Phaser/Game/GameScene.ts
@@ -32,7 +32,6 @@ import type { RoomConnection } from "../../Connexion/RoomConnection";
import { Room } from "../../Connexion/Room";
import { jitsiFactory } from "../../WebRtc/JitsiFactory";
import { urlManager } from "../../Url/UrlManager";
-import { audioManager } from "../../WebRtc/AudioManager";
import { TextureError } from "../../Exception/TextureError";
import { localUserStore } from "../../Connexion/LocalUserStore";
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
@@ -84,6 +83,11 @@ import { biggestAvailableAreaStore } from "../../Stores/BiggestAvailableAreaStor
import { SharedVariablesManager } from "./SharedVariablesManager";
import { playersStore } from "../../Stores/PlayersStore";
import { chatVisibilityStore } from "../../Stores/ChatStore";
+import {
+ audioManagerFileStore,
+ audioManagerVisibilityStore,
+ audioManagerVolumeStore,
+} from "../../Stores/AudioManagerStore";
import { PropertyUtils } from "../Map/PropertyUtils";
import Tileset = Phaser.Tilemaps.Tileset;
import { userIsAdminStore } from "../../Stores/GameStore";
@@ -727,12 +731,12 @@ export class GameScene extends DirtyScene {
this.simplePeer.registerPeerConnectionListener({
onConnect(peer) {
//self.openChatIcon.setVisible(true);
- audioManager.decreaseVolume();
+ audioManagerVolumeStore.setTalking(true);
},
onDisconnect(userId: number) {
if (self.simplePeer.getNbConnections() === 0) {
//self.openChatIcon.setVisible(false);
- audioManager.restoreVolume();
+ audioManagerVolumeStore.setTalking(false);
}
},
});
@@ -898,14 +902,16 @@ export class GameScene extends DirtyScene {
const volume = allProps.get(AUDIO_VOLUME_PROPERTY) as number | undefined;
const loop = allProps.get(AUDIO_LOOP_PROPERTY) as boolean | undefined;
newValue === undefined
- ? audioManager.unloadAudio()
- : audioManager.playAudio(newValue, this.getMapDirUrl(), volume, loop);
+ ? audioManagerFileStore.unloadAudio()
+ : audioManagerFileStore.playAudio(newValue, this.getMapDirUrl(), volume, loop);
+ audioManagerVisibilityStore.set(!(newValue === undefined));
});
// TODO: This legacy property should be removed at some point
this.gameMap.onPropertyChange("playAudioLoop", (newValue, oldValue) => {
newValue === undefined
- ? audioManager.unloadAudio()
- : audioManager.playAudio(newValue, this.getMapDirUrl(), undefined, true);
+ ? audioManagerFileStore.unloadAudio()
+ : audioManagerFileStore.playAudio(newValue, this.getMapDirUrl(), undefined, true);
+ audioManagerVisibilityStore.set(!(newValue === undefined));
});
this.gameMap.onPropertyChange("zone", (newValue, oldValue) => {
@@ -1323,7 +1329,7 @@ ${escapedMessage}
}
this.stopJitsi();
- audioManager.unloadAudio();
+ audioManagerFileStore.unloadAudio();
// We are completely destroying the current scene to avoid using a half-backed instance when coming back to the same map.
this.connection?.closeConnection();
this.simplePeer?.closeAllConnections();
diff --git a/front/src/Stores/AudioManagerStore.ts b/front/src/Stores/AudioManagerStore.ts
new file mode 100644
index 00000000..1bf30ea3
--- /dev/null
+++ b/front/src/Stores/AudioManagerStore.ts
@@ -0,0 +1,105 @@
+import { get, writable } from "svelte/store";
+
+export interface audioManagerVolume {
+ muted: boolean;
+ volume: number;
+ decreaseWhileTalking: boolean;
+ volumeReduced: boolean;
+ loop: boolean;
+ talking: boolean;
+}
+
+function createAudioManagerVolumeStore() {
+ const { subscribe, update } = writable
({
+ muted: false,
+ volume: 1,
+ decreaseWhileTalking: true,
+ volumeReduced: false,
+ loop: false,
+ talking: false,
+ });
+
+ return {
+ subscribe,
+ setMuted: (newMute: boolean): void => {
+ update((audioPlayerVolume: audioManagerVolume) => {
+ audioPlayerVolume.muted = newMute;
+ return audioPlayerVolume;
+ });
+ },
+ setVolume: (newVolume: number): void => {
+ update((audioPlayerVolume: audioManagerVolume) => {
+ audioPlayerVolume.volume = newVolume;
+ return audioPlayerVolume;
+ });
+ },
+ setDecreaseWhileTalking: (newDecrease: boolean): void => {
+ update((audioManagerVolume: audioManagerVolume) => {
+ audioManagerVolume.decreaseWhileTalking = newDecrease;
+ return audioManagerVolume;
+ });
+ },
+ setVolumeReduced: (newVolumeReduced: boolean): void => {
+ update((audioManagerVolume: audioManagerVolume) => {
+ audioManagerVolume.volumeReduced = newVolumeReduced;
+ return audioManagerVolume;
+ });
+ },
+ setLoop: (newLoop: boolean): void => {
+ update((audioManagerVolume: audioManagerVolume) => {
+ audioManagerVolume.loop = newLoop;
+ return audioManagerVolume;
+ });
+ },
+ setTalking: (newTalk: boolean): void => {
+ update((audioManagerVolume: audioManagerVolume) => {
+ audioManagerVolume.talking = newTalk;
+ return audioManagerVolume;
+ });
+ },
+ };
+}
+
+function createAudioManagerFileStore() {
+ const { subscribe, update } = writable("");
+
+ return {
+ subscribe,
+ playAudio: (
+ url: string | number | boolean,
+ mapDirUrl: string,
+ volume: number | undefined,
+ loop = false
+ ): void => {
+ update((file: string) => {
+ const audioPath = url as string;
+
+ if (audioPath.indexOf("://") > 0) {
+ // remote file or stream
+ file = audioPath;
+ } else {
+ // local file, include it relative to map directory
+ file = mapDirUrl + "/" + url;
+ }
+ audioManagerVolumeStore.setVolume(
+ volume ? Math.min(volume, get(audioManagerVolumeStore).volume) : get(audioManagerVolumeStore).volume
+ );
+ audioManagerVolumeStore.setLoop(loop);
+
+ return file;
+ });
+ },
+ unloadAudio: () => {
+ update((file: string) => {
+ audioManagerVolumeStore.setLoop(false);
+ return "";
+ });
+ },
+ };
+}
+
+export const audioManagerVisibilityStore = writable(false);
+
+export const audioManagerVolumeStore = createAudioManagerVolumeStore();
+
+export const audioManagerFileStore = createAudioManagerFileStore();
diff --git a/front/src/WebRtc/AudioManager.ts b/front/src/WebRtc/AudioManager.ts
deleted file mode 100644
index 60255a77..00000000
--- a/front/src/WebRtc/AudioManager.ts
+++ /dev/null
@@ -1,188 +0,0 @@
-import {HtmlUtils} from "./HtmlUtils";
-import {isUndefined} from "generic-type-guard";
-import {localUserStore} from "../Connexion/LocalUserStore";
-
-enum audioStates {
- closed = 0,
- loading = 1,
- playing = 2
-}
-
-const audioPlayerDivId = "audioplayer";
-const audioPlayerCtrlId = "audioplayerctrl";
-const audioPlayerVolId = "audioplayer_volume";
-const audioPlayerMuteId = "audioplayer_volume_icon_playing";
-const animationTime = 500;
-
-class AudioManager {
- private opened = audioStates.closed;
-
- private audioPlayerDiv: HTMLDivElement;
- private audioPlayerCtrl: HTMLDivElement;
- private audioPlayerElem: HTMLAudioElement | undefined;
- private audioPlayerVol: HTMLInputElement;
- private audioPlayerMute: HTMLInputElement;
-
- private volume = 1;
- private muted = false;
- private decreaseWhileTalking = true;
- private volumeReduced = false;
-
- constructor() {
- this.audioPlayerDiv = HtmlUtils.getElementByIdOrFail(audioPlayerDivId);
- this.audioPlayerCtrl = HtmlUtils.getElementByIdOrFail(audioPlayerCtrlId);
- this.audioPlayerVol = HtmlUtils.getElementByIdOrFail(audioPlayerVolId);
- this.audioPlayerMute = HtmlUtils.getElementByIdOrFail(audioPlayerMuteId);
-
- this.volume = localUserStore.getAudioPlayerVolume();
- this.audioPlayerVol.value = '' + this.volume;
-
- this.muted = localUserStore.getAudioPlayerMuted();
- if (this.muted) {
- this.audioPlayerMute.classList.add('muted');
- }
- }
-
- public playAudio(url: string|number|boolean, mapDirUrl: string, volume: number|undefined, loop=false): void {
- const audioPath = url as string;
- let realAudioPath = '';
-
- if (audioPath.indexOf('://') > 0) {
- // remote file or stream
- realAudioPath = audioPath;
- } else {
- // local file, include it relative to map directory
- realAudioPath = mapDirUrl + '/' + url;
- }
-
- this.loadAudio(realAudioPath, volume);
-
- if (loop) {
- this.loop();
- }
- }
-
- private close(): void {
- this.audioPlayerCtrl.classList.remove('loading');
- this.audioPlayerCtrl.classList.add('hidden');
- this.opened = audioStates.closed;
- }
-
- private load(): void {
- this.audioPlayerCtrl.classList.remove('hidden');
- this.audioPlayerCtrl.classList.add('loading');
- this.opened = audioStates.loading;
- }
-
- private open(): void {
- this.audioPlayerCtrl.classList.remove('hidden', 'loading');
- this.opened = audioStates.playing;
- }
-
- private changeVolume(talking = false): void {
- if (isUndefined(this.audioPlayerElem)) {
- return;
- }
-
- const reduceVolume = talking && this.decreaseWhileTalking;
- if (reduceVolume && !this.volumeReduced) {
- this.volume *= 0.5;
- } else if (!reduceVolume && this.volumeReduced) {
- this.volume *= 2.0;
- }
- this.volumeReduced = reduceVolume;
-
- this.audioPlayerElem.volume = this.volume;
- this.audioPlayerVol.value = '' + this.volume;
- this.audioPlayerElem.muted = this.muted;
- }
-
- private setVolume(volume: number): void {
- this.volume = volume;
- localUserStore.setAudioPlayerVolume(volume);
- }
-
- private loadAudio(url: string, volume: number|undefined): void {
- this.load();
-
- /* Solution 1, remove whole audio player */
- this.audioPlayerDiv.innerHTML = ''; // necessary, if switching from one audio context to another! (else both streams would play simultaneously)
-
- this.audioPlayerElem = document.createElement('audio');
- this.audioPlayerElem.id = 'audioplayerelem';
- this.audioPlayerElem.controls = false;
- this.audioPlayerElem.preload = 'none';
-
- const srcElem = document.createElement('source');
- srcElem.type = "audio/mp3";
- srcElem.src = url;
-
- this.audioPlayerElem.append(srcElem);
-
- this.audioPlayerDiv.append(this.audioPlayerElem);
- this.volume = volume ? Math.min(volume, this.volume) : this.volume;
- this.changeVolume();
- this.audioPlayerElem.play();
-
- const muteElem = HtmlUtils.getElementByIdOrFail('audioplayer_mute');
- muteElem.onclick = (ev: Event) => {
- this.muted = !this.muted;
- this.changeVolume();
- localUserStore.setAudioPlayerMuted(this.muted);
-
- if (this.muted) {
- this.audioPlayerMute.classList.add('muted');
- } else {
- this.audioPlayerMute.classList.remove('muted');
- }
- }
-
- this.audioPlayerVol.oninput = (ev: Event)=> {
- this.setVolume(parseFloat((ev.currentTarget).value));
- this.changeVolume();
-
- (ev.currentTarget).blur();
- }
-
- const decreaseElem = HtmlUtils.getElementByIdOrFail('audioplayer_decrease_while_talking');
- decreaseElem.oninput = (ev: Event)=> {
- this.decreaseWhileTalking = (ev.currentTarget).checked;
- this.changeVolume();
- }
-
- this.open();
- }
-
- private loop(): void {
- if (this.audioPlayerElem !== undefined) {
- this.audioPlayerElem.loop = true;
- }
- }
-
- public unloadAudio(): void {
- try {
- const audioElem = HtmlUtils.getElementByIdOrFail('audioplayerelem');
- this.volume = audioElem.volume;
- this.muted = audioElem.muted;
- audioElem.pause();
- audioElem.loop = false;
- audioElem.src = "";
- audioElem.innerHTML = "";
- audioElem.load();
- } catch (e) {
- console.log('No audio element loaded to unload');
- }
-
- this.close();
- }
-
- public decreaseVolume(): void {
- this.changeVolume(true);
- }
-
- public restoreVolume(): void {
- this.changeVolume(false);
- }
-}
-
-export const audioManager = new AudioManager();