diff --git a/CHANGELOG.md b/CHANGELOG.md index 31f544eb..fa9d743e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,19 @@ -## Version 1.3.9 - in dev +## Version 1.4.x-dev + +### Updates + +- Added the ability to have animated tiles in maps #1216 #1217 +- Enabled outlines on actionable item again (they were disabled when migrating to Phaser 3.50) #1218 +- Enabled outlines on player names (when the mouse hovers on a player you can interact with) #1219 +- Migrated the admin console to Svelte, and redesigned the console #1211 + +## Version 1.4.1 + +### Bugfixes + +- Loading errors after the preload stage should not crash the game anymore + +## Version 1.4.0 ### BREAKING CHANGES diff --git a/front/src/Administration/ConsoleGlobalMessageManager.ts b/front/src/Administration/ConsoleGlobalMessageManager.ts deleted file mode 100644 index 0dbfe834..00000000 --- a/front/src/Administration/ConsoleGlobalMessageManager.ts +++ /dev/null @@ -1,397 +0,0 @@ -import {HtmlUtils} from "../WebRtc/HtmlUtils"; -import type {UserInputManager} from "../Phaser/UserInput/UserInputManager"; -import type {RoomConnection} from "../Connexion/RoomConnection"; -import type {PlayGlobalMessageInterface} from "../Connexion/ConnexionModels"; -import {AdminMessageEventTypes} from "../Connexion/AdminMessagesService"; - -export const CLASS_CONSOLE_MESSAGE = 'main-console'; -export const INPUT_CONSOLE_MESSAGE = 'input-send-text'; -export const UPLOAD_CONSOLE_MESSAGE = 'input-upload-music'; -export const INPUT_TYPE_CONSOLE = 'input-type'; -export const VIDEO_QUALITY_SELECT = 'select-video-quality'; - -export const AUDIO_TYPE = AdminMessageEventTypes.audio; -export const MESSAGE_TYPE = AdminMessageEventTypes.admin; - -interface EventTargetFiles extends EventTarget { - files: Array; -} - -/** - * @deprecated - */ -export class ConsoleGlobalMessageManager { - - private readonly divMainConsole: HTMLDivElement; - private readonly divMessageConsole: HTMLDivElement; - //private readonly divSettingConsole: HTMLDivElement; - private readonly buttonMainConsole: HTMLDivElement; - private readonly buttonSendMainConsole: HTMLImageElement; - //private readonly buttonAdminMainConsole: HTMLImageElement; - //private readonly buttonSettingsMainConsole: HTMLImageElement; - private activeConsole: boolean = false; - private activeMessage: boolean = false; - private activeSetting: boolean = false; - private userInputManager!: UserInputManager; - private static cssLoaded: boolean = false; - - constructor(private Connection: RoomConnection, userInputManager : UserInputManager, private isAdmin: Boolean) { - this.buttonMainConsole = document.createElement('div'); - this.buttonMainConsole.classList.add('console'); - this.buttonMainConsole.hidden = true; - this.divMainConsole = document.createElement('div'); - this.divMainConsole.className = CLASS_CONSOLE_MESSAGE; - this.divMessageConsole = document.createElement('div'); - this.divMessageConsole.className = 'message'; - //this.divSettingConsole = document.createElement('div'); - //this.divSettingConsole.className = 'setting'; - this.buttonSendMainConsole = document.createElement('img'); - this.buttonSendMainConsole.id = 'btn-send-message'; - //this.buttonSettingsMainConsole = document.createElement('img'); - //this.buttonAdminMainConsole = document.createElement('img'); - this.userInputManager = userInputManager; - this.initialise(); - - } - - initialise() { - for (const elem of document.getElementsByClassName(CLASS_CONSOLE_MESSAGE)) { - elem.remove(); - } - - const typeConsole = document.createElement('input'); - typeConsole.id = INPUT_TYPE_CONSOLE; - typeConsole.value = MESSAGE_TYPE; - typeConsole.type = 'hidden'; - - const menu = document.createElement('div'); - menu.classList.add('menu') - const textMessage = document.createElement('span'); - textMessage.innerText = "Message"; - textMessage.classList.add('active'); - textMessage.addEventListener('click', () => { - textMessage.classList.add('active'); - const messageSection = HtmlUtils.getElementByIdOrFail(this.getSectionId(INPUT_CONSOLE_MESSAGE)); - messageSection.classList.add('active'); - - textAudio.classList.remove('active'); - const audioSection = HtmlUtils.getElementByIdOrFail(this.getSectionId(UPLOAD_CONSOLE_MESSAGE)); - audioSection.classList.remove('active'); - - typeConsole.value = MESSAGE_TYPE; - }); - menu.appendChild(textMessage); - const textAudio = document.createElement('span'); - textAudio.innerText = "Audio"; - textAudio.addEventListener('click', () => { - textAudio.classList.add('active'); - const audioSection = HtmlUtils.getElementByIdOrFail(this.getSectionId(UPLOAD_CONSOLE_MESSAGE)); - audioSection.classList.add('active'); - - textMessage.classList.remove('active'); - const messageSection = HtmlUtils.getElementByIdOrFail(this.getSectionId(INPUT_CONSOLE_MESSAGE)); - messageSection.classList.remove('active'); - - typeConsole.value = AUDIO_TYPE; - }); - menu.appendChild(textMessage); - menu.appendChild(textAudio); - this.divMessageConsole.appendChild(menu); - - this.buttonSendMainConsole.src = 'resources/logos/send-yellow.svg'; - this.buttonSendMainConsole.addEventListener('click', () => { - if(this.activeMessage){ - this.disabledMessageConsole(); - }else{ - this.activeMessageConsole(); - } - }); - - /*this.buttonAdminMainConsole.src = 'resources/logos/setting-yellow.svg'; - this.buttonAdminMainConsole.addEventListener('click', () => { - window.open(ADMIN_URL, '_blank'); - });*/ - - /*this.buttonSettingsMainConsole.src = 'resources/logos/monitor-yellow.svg'; - this.buttonSettingsMainConsole.addEventListener('click', () => { - if(this.activeSetting){ - this.disabledSettingConsole(); - }else{ - this.activeSettingConsole(); - } - });*/ - - this.divMessageConsole.appendChild(typeConsole); - - /*if(this.isAdmin) { - this.buttonMainConsole.appendChild(this.buttonSendMainConsole); - //this.buttonMainConsole.appendChild(this.buttonAdminMainConsole); - }*/ - this.createTextMessagePart(); - this.createUploadAudioPart(); - //this.buttonMainConsole.appendChild(this.buttonSettingsMainConsole); - - this.divMainConsole.appendChild(this.buttonMainConsole); - this.divMainConsole.appendChild(this.divMessageConsole); - //this.divMainConsole.appendChild(this.divSettingConsole); - - const mainSectionDiv = HtmlUtils.getElementByIdOrFail('main-container'); - mainSectionDiv.appendChild(this.divMainConsole); - } - - createTextMessagePart(){ - const div = document.createElement('div'); - div.id = INPUT_CONSOLE_MESSAGE - const buttonSend = document.createElement('button'); - buttonSend.innerText = 'Send'; - buttonSend.classList.add('btn'); - buttonSend.addEventListener('click', (event: MouseEvent) => { - this.sendMessage(); - this.disabledMessageConsole(); - }); - const buttonDiv = document.createElement('div'); - buttonDiv.classList.add('btn-action'); - buttonDiv.appendChild(buttonSend) - - const section = document.createElement('section'); - section.id = this.getSectionId(INPUT_CONSOLE_MESSAGE); - section.classList.add('active'); - section.appendChild(div); - section.appendChild(buttonDiv); - this.divMessageConsole.appendChild(section); - - (async () => { - try{ - // Start loading CSS - const cssPromise = ConsoleGlobalMessageManager.loadCss(); - // Import quill - const {default: Quill}:any = await import("quill"); // eslint-disable-line @typescript-eslint/no-explicit-any - // Wait for CSS to be loaded - await cssPromise; - - const toolbarOptions = [ - ['bold', 'italic', 'underline', 'strike'], // toggled buttons - ['blockquote', 'code-block'], - - [{'header': 1}, {'header': 2}], // custom button values - [{'list': 'ordered'}, {'list': 'bullet'}], - [{'script': 'sub'}, {'script': 'super'}], // superscript/subscript - [{'indent': '-1'}, {'indent': '+1'}], // outdent/indent - [{'direction': 'rtl'}], // text direction - - [{'size': ['small', false, 'large', 'huge']}], // custom dropdown - [{'header': [1, 2, 3, 4, 5, 6, false]}], - - [{'color': []}, {'background': []}], // dropdown with defaults from theme - [{'font': []}], - [{'align': []}], - - ['clean'], - - ['link', 'image', 'video'] - // remove formatting button - ]; - - new Quill(`#${INPUT_CONSOLE_MESSAGE}`, { - theme: 'snow', - modules: { - toolbar: toolbarOptions - }, - }); - }catch(err){ - console.error(err); - } - })(); - } - - createUploadAudioPart(){ - const div = document.createElement('div'); - div.classList.add('upload'); - - const label = document.createElement('label'); - label.setAttribute('for', UPLOAD_CONSOLE_MESSAGE); - - const img = document.createElement('img'); - img.setAttribute('for', UPLOAD_CONSOLE_MESSAGE); - img.src = 'resources/logos/music-file.svg'; - - const input = document.createElement('input'); - input.type = 'file'; - input.id = UPLOAD_CONSOLE_MESSAGE - input.addEventListener('input', (e: Event) => { - if(!e.target){ - return; - } - const eventTarget : EventTargetFiles = (e.target as EventTargetFiles); - if(!eventTarget || !eventTarget.files || eventTarget.files.length === 0){ - return; - } - const file : File = eventTarget.files[0]; - - if(!file){ - return; - } - - try { - HtmlUtils.removeElementByIdOrFail('audi-message-filename'); - }catch (err) { - console.error(err) - } - - const p = document.createElement('p'); - p.id = 'audi-message-filename'; - p.innerText = `${file.name} : ${this.getFileSize(file.size)}`; - label.appendChild(p); - }); - - label.appendChild(img); - div.appendChild(label); - div.appendChild(input); - - const buttonSend = document.createElement('button'); - buttonSend.innerText = 'Send'; - buttonSend.classList.add('btn'); - buttonSend.addEventListener('click', (event: MouseEvent) => { - this.sendMessage(); - this.disabledMessageConsole(); - }); - const buttonDiv = document.createElement('div'); - buttonDiv.classList.add('btn-action'); - buttonDiv.appendChild(buttonSend) - - const section = document.createElement('section'); - section.id = this.getSectionId(UPLOAD_CONSOLE_MESSAGE); - section.appendChild(div); - section.appendChild(buttonDiv); - this.divMessageConsole.appendChild(section); - } - - private static loadCss(): Promise { - return new Promise((resolve, reject) => { - if (ConsoleGlobalMessageManager.cssLoaded) { - resolve(); - return; - } - const fileref = document.createElement("link") - fileref.setAttribute("rel", "stylesheet") - fileref.setAttribute("type", "text/css") - fileref.setAttribute("href", "https://cdn.quilljs.com/1.3.7/quill.snow.css"); - document.getElementsByTagName("head")[0].appendChild(fileref); - ConsoleGlobalMessageManager.cssLoaded = true; - fileref.onload = () => { - resolve(); - } - fileref.onerror = () => { - reject(); - } - }); - } - - sendMessage(){ - const inputType = HtmlUtils.getElementByIdOrFail(INPUT_TYPE_CONSOLE); - if(AUDIO_TYPE !== inputType.value && MESSAGE_TYPE !== inputType.value){ - throw "Error event type"; - } - if(AUDIO_TYPE === inputType.value){ - return this.sendAudioMessage(); - } - return this.sendTextMessage(); - } - - private sendTextMessage(){ - const elements = document.getElementsByClassName('ql-editor'); - const quillEditor = elements.item(0); - if(!quillEditor){ - throw "Error get quill node"; - } - const GlobalMessage : PlayGlobalMessageInterface = { - id: "1", // FIXME: use another ID? - message: quillEditor.innerHTML, - type: MESSAGE_TYPE - }; - quillEditor.innerHTML = ''; - this.Connection.emitGlobalMessage(GlobalMessage); - } - - private async sendAudioMessage(){ - const inputAudio = HtmlUtils.getElementByIdOrFail(UPLOAD_CONSOLE_MESSAGE); - const selectedFile = inputAudio.files ? inputAudio.files[0] : null; - if(!selectedFile){ - throw 'no file selected'; - } - - const fd = new FormData(); - fd.append('file', selectedFile); - const res = await this.Connection.uploadAudio(fd); - - const GlobalMessage : PlayGlobalMessageInterface = { - id: (res as {id: string}).id, - message: (res as {path: string}).path, - type: AUDIO_TYPE - }; - inputAudio.value = ''; - try { - HtmlUtils.removeElementByIdOrFail('audi-message-filename'); - }catch (err) { - console.error(err); - } - this.Connection.emitGlobalMessage(GlobalMessage); - } - - active(){ - this.userInputManager.disableControls(); - this.divMainConsole.style.top = '0'; - this.activeConsole = true; - } - - disabled(){ - this.userInputManager.initKeyBoardEvent(); - this.activeConsole = false; - this.divMainConsole.style.top = '-80%'; - } - - activeMessageConsole(){ - if(!this.isAdmin){ - throw "User is not admin"; - } - if(this.activeMessage){ - this.disabledMessageConsole(); - return; - } - this.activeMessage = true; - this.active(); - this.divMessageConsole.classList.add('active'); - this.buttonMainConsole.hidden = false; - this.buttonSendMainConsole.classList.add('active'); - //if button not - try{ - HtmlUtils.getElementByIdOrFail('btn-send-message'); - }catch (e) { - this.buttonMainConsole.appendChild(this.buttonSendMainConsole); - } - } - - disabledMessageConsole(){ - this.activeMessage = false; - this.disabled(); - this.buttonMainConsole.hidden = true; - this.divMessageConsole.classList.remove('active'); - this.buttonSendMainConsole.classList.remove('active'); - } - - private getSectionId(id: string) : string { - return `section-${id}`; - } - - private getFileSize(number: number) :string { - if (number < 1024) { - return number + 'bytes'; - } else if (number >= 1024 && number < 1048576) { - return (number / 1024).toFixed(1) + 'KB'; - } else if (number >= 1048576) { - return (number / 1048576).toFixed(1) + 'MB'; - }else{ - return ''; - } - } -} diff --git a/front/src/Administration/GlobalMessageManager.ts b/front/src/Administration/GlobalMessageManager.ts index 1500a6ec..aae695fa 100644 --- a/front/src/Administration/GlobalMessageManager.ts +++ b/front/src/Administration/GlobalMessageManager.ts @@ -1,10 +1,10 @@ import {HtmlUtils} from "./../WebRtc/HtmlUtils"; -import {AUDIO_TYPE, MESSAGE_TYPE} from "./ConsoleGlobalMessageManager"; import {PUSHER_URL, UPLOADER_URL} from "../Enum/EnvironmentVariable"; import type {RoomConnection} from "../Connexion/RoomConnection"; import type {PlayGlobalMessageInterface} from "../Connexion/ConnexionModels"; import {soundPlayingStore} from "../Stores/SoundPlayingStore"; import {soundManager} from "../Phaser/Game/SoundManager"; +import {AdminMessageEventTypes} from "../Connexion/AdminMessagesService"; export class GlobalMessageManager { @@ -36,11 +36,11 @@ export class GlobalMessageManager { previousMessage.remove(); } - if(AUDIO_TYPE === message.type){ + if(AdminMessageEventTypes.audio === message.type){ this.playAudioMessage(message.id, message.message); } - if(MESSAGE_TYPE === message.type){ + if(AdminMessageEventTypes.admin === message.type){ this.playTextMessage(message.id, message.message); } } diff --git a/front/src/Components/App.svelte b/front/src/Components/App.svelte index c973a6c2..2e159d2d 100644 --- a/front/src/Components/App.svelte +++ b/front/src/Components/App.svelte @@ -21,6 +21,8 @@ import AudioPlaying from "./UI/AudioPlaying.svelte"; import {soundPlayingStore} from "../Stores/SoundPlayingStore"; import ErrorDialog from "./UI/ErrorDialog.svelte"; + import {consoleGlobalMessageManagerVisibleStore} from "../Stores/ConsoleGlobalMessageManagerStore"; + import ConsoleGlobalMessageManager from "./ConsoleGlobalMessageManager/ConsoleGlobalMessageManager.svelte"; export let game: Game; @@ -70,6 +72,11 @@ {/if} + {#if $consoleGlobalMessageManagerVisibleStore} +
+ +
+ {/if} {#if $helpCameraSettingsVisibleStore}
diff --git a/front/src/Components/ConsoleGlobalMessageManager/ConsoleGlobalMessageManager.svelte b/front/src/Components/ConsoleGlobalMessageManager/ConsoleGlobalMessageManager.svelte new file mode 100644 index 00000000..83837f28 --- /dev/null +++ b/front/src/Components/ConsoleGlobalMessageManager/ConsoleGlobalMessageManager.svelte @@ -0,0 +1,44 @@ + + + +
+ +
+

Global Message

+
+ +
+ {#if inputSendTextActive} + + {/if} + {#if uploadMusicActive} + + {/if} +
+
+
+
\ No newline at end of file diff --git a/front/src/Components/ConsoleGlobalMessageManager/InputTextGlobalMessage.svelte b/front/src/Components/ConsoleGlobalMessageManager/InputTextGlobalMessage.svelte new file mode 100644 index 00000000..c11b4b0e --- /dev/null +++ b/front/src/Components/ConsoleGlobalMessageManager/InputTextGlobalMessage.svelte @@ -0,0 +1,99 @@ + + + +
+
+
+ +
+
+ + + diff --git a/front/src/Components/ConsoleGlobalMessageManager/UploadAudioGlobalMessage.svelte b/front/src/Components/ConsoleGlobalMessageManager/UploadAudioGlobalMessage.svelte new file mode 100644 index 00000000..50954005 --- /dev/null +++ b/front/src/Components/ConsoleGlobalMessageManager/UploadAudioGlobalMessage.svelte @@ -0,0 +1,130 @@ + + + +
+
+ Upload a file {fileinput.click();}}> + {#if filename != undefined} + + {/if} + {#if errorfile} +

No file selected. You need to upload a file before sending it.

+ {/if} + {inputAudioFile(e)}}> +
+
+ +
+
+ + \ No newline at end of file diff --git a/front/src/Components/images/music-file.svg b/front/src/Components/images/music-file.svg new file mode 100644 index 00000000..a97656ba --- /dev/null +++ b/front/src/Components/images/music-file.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index 2ff66178..7263a584 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -8,6 +8,7 @@ import {Companion} from "../Companion/Companion"; import type {GameScene} from "../Game/GameScene"; import {DEPTH_INGAME_TEXT_INDEX} from "../Game/DepthIndexes"; import {waScaleManager} from "../Services/WaScaleManager"; +import type OutlinePipelinePlugin from "phaser3-rex-plugins/plugins/outlinepipeline-plugin.js"; const playerNameY = - 25; @@ -32,6 +33,7 @@ export abstract class Character extends Container { public companion?: Companion; private emote: Phaser.GameObjects.Sprite | null = null; private emoteTween: Phaser.Tweens.Tween|null = null; + scene: GameScene; constructor(scene: GameScene, x: number, @@ -46,6 +48,7 @@ export abstract class Character extends Container { companionTexturePromise?: Promise ) { super(scene, x, y/*, texture, frame*/); + this.scene = scene; this.PlayerValue = name; this.invisible = true @@ -67,6 +70,19 @@ export abstract class Character extends Container { hitAreaCallback: Phaser.Geom.Circle.Contains, //eslint-disable-line @typescript-eslint/unbound-method useHandCursor: true, }); + + this.on('pointerover',() => { + this.getOutlinePlugin()?.add(this.playerName, { + thickness: 2, + outlineColor: 0xffff00 + }); + this.scene.markDirty(); + }); + this.on('pointerout',() => { + this.getOutlinePlugin()?.remove(this.playerName); + this.scene.markDirty(); + }) + } scene.add.existing(this); @@ -86,6 +102,10 @@ export abstract class Character extends Container { } } + private getOutlinePlugin(): OutlinePipelinePlugin|undefined { + return this.scene.plugins.get('rexOutlinePipeline') as unknown as OutlinePipelinePlugin|undefined; + } + public addCompanion(name: string, texturePromise?: Promise): void { if (typeof texturePromise !== 'undefined') { this.companion = new Companion(this.scene, this.x, this.y, name, texturePromise); diff --git a/front/src/Phaser/Game/DirtyScene.ts b/front/src/Phaser/Game/DirtyScene.ts index 8a559310..20602cca 100644 --- a/front/src/Phaser/Game/DirtyScene.ts +++ b/front/src/Phaser/Game/DirtyScene.ts @@ -70,6 +70,10 @@ export abstract class DirtyScene extends ResizableScene { return this.dirty || this.objectListChanged; } + public markDirty(): void { + this.events.once(Phaser.Scenes.Events.POST_UPDATE, () => this.dirty = true); + } + public onResize(): void { this.objectListChanged = true; } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index cc0ce1d3..8494503c 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -1,6 +1,5 @@ import { Queue } from 'queue-typescript'; import type { Subscription } from "rxjs"; -import { ConsoleGlobalMessageManager } from "../../Administration/ConsoleGlobalMessageManager"; import { GlobalMessageManager } from "../../Administration/GlobalMessageManager"; import { userMessageManager } from "../../Administration/UserMessageManager"; import { iframeListener } from "../../Api/IframeListener"; @@ -82,7 +81,6 @@ import { gameManager } from "./GameManager"; import { GameMap } from "./GameMap"; import { PlayerMovement } from "./PlayerMovement"; import { PlayersPositionInterpolator } from "./PlayersPositionInterpolator"; -import { soundManager } from "./SoundManager"; import Texture = Phaser.Textures.Texture; import Sprite = Phaser.GameObjects.Sprite; import CanvasTexture = Phaser.Textures.CanvasTexture; @@ -95,6 +93,7 @@ import Tilemap = Phaser.Tilemaps.Tilemap; import type { HasPlayerMovedEvent } from '../../Api/Events/HasPlayerMovedEvent'; import AnimatedTiles from "phaser-animated-tiles"; +import {soundManager} from "./SoundManager"; export interface GameSceneInitInterface { initPosition: PointInterface | null, @@ -153,7 +152,6 @@ export class GameScene extends DirtyScene implements CenterListener { public connection: RoomConnection | undefined; private simplePeer!: SimplePeer; private GlobalMessageManager!: GlobalMessageManager; - public ConsoleGlobalMessageManager!: ConsoleGlobalMessageManager; private connectionAnswerPromise: Promise; private connectionAnswerPromiseResolve!: (value: RoomJoinedMessageInterface | PromiseLike) => void; // A promise that will resolve when the "create" method is called (signaling loading is ended) @@ -672,7 +670,6 @@ export class GameScene extends DirtyScene implements CenterListener { //this.initUsersPosition(roomJoinedMessage.users); this.connectionAnswerPromiseResolve(onConnect.room); // Analyze tags to find if we are admin. If yes, show console. - this.ConsoleGlobalMessageManager = new ConsoleGlobalMessageManager(this.connection, this.userInputManager, this.connection.isAdmin()); this.scene.wake(); diff --git a/front/src/Phaser/Items/ActionableItem.ts b/front/src/Phaser/Items/ActionableItem.ts index f012b525..7c7090b0 100644 --- a/front/src/Phaser/Items/ActionableItem.ts +++ b/front/src/Phaser/Items/ActionableItem.ts @@ -3,8 +3,8 @@ * It has coordinates and an "activation radius" */ import Sprite = Phaser.GameObjects.Sprite; -import {OutlinePipeline} from "../Shaders/OutlinePipeline"; import type {GameScene} from "../Game/GameScene"; +import type OutlinePipelinePlugin from "phaser3-rex-plugins/plugins/outlinepipeline-plugin.js"; type EventCallback = (state: unknown, parameters: unknown) => void; @@ -42,11 +42,11 @@ export class ActionableItem { return; } this.isSelectable = true; - if (this.sprite.pipeline) { - // Commented out to try to fix MacOS issue - /*this.sprite.setPipeline(OutlinePipeline.KEY); - this.sprite.pipeline.set2f('uTextureSize', this.sprite.texture.getSourceImage().width, this.sprite.texture.getSourceImage().height);*/ - } + + this.getOutlinePlugin()?.add(this.sprite, { + thickness: 2, + outlineColor: 0xffff00 + }); } /** @@ -57,8 +57,11 @@ export class ActionableItem { return; } this.isSelectable = false; - // Commented out to try to fix MacOS issue - //this.sprite.resetPipeline(); + this.getOutlinePlugin()?.remove(this.sprite); + } + + private getOutlinePlugin(): OutlinePipelinePlugin|undefined { + return this.sprite.scene.plugins.get('rexOutlinePipeline') as unknown as OutlinePipelinePlugin|undefined; } /** diff --git a/front/src/Phaser/Menu/MenuScene.ts b/front/src/Phaser/Menu/MenuScene.ts index 15df4e5c..bf9c9a08 100644 --- a/front/src/Phaser/Menu/MenuScene.ts +++ b/front/src/Phaser/Menu/MenuScene.ts @@ -16,6 +16,8 @@ import { Subscription } from 'rxjs'; import { videoConstraintStore } from "../../Stores/MediaStore"; import {registerMenuCommandStream} from "../../Api/Events/ui/MenuItemRegisterEvent"; import {sendMenuClickedEvent} from "../../Api/iframe/Ui/MenuItem"; +import {consoleGlobalMessageManagerVisibleStore} from "../../Stores/ConsoleGlobalMessageManagerStore"; +import {get} from "svelte/store"; export const MenuSceneName = 'MenuScene'; const gameMenuKey = 'gameMenu'; @@ -190,7 +192,7 @@ export class MenuScene extends Phaser.Scene { this.sideMenuOpened = false; this.closeAll(); this.menuButton.getChildByID('openMenuButton').innerHTML = ``; - gameManager.getCurrentGameScene(this).ConsoleGlobalMessageManager.disabledMessageConsole(); + consoleGlobalMessageManagerVisibleStore.set(false); this.tweens.add({ targets: this.menuElement, x: closedSideMenuX, @@ -341,7 +343,11 @@ export class MenuScene extends Phaser.Scene { this.toggleFullscreen(); break; case 'adminConsoleButton': - gameManager.getCurrentGameScene(this).ConsoleGlobalMessageManager.activeMessageConsole(); + if (get(consoleGlobalMessageManagerVisibleStore)) { + consoleGlobalMessageManagerVisibleStore.set(false); + } else { + consoleGlobalMessageManagerVisibleStore.set(true); + } break; } } diff --git a/front/src/Phaser/Shaders/OutlinePipeline.ts b/front/src/Phaser/Shaders/OutlinePipeline.ts deleted file mode 100644 index 0d074bc3..00000000 --- a/front/src/Phaser/Shaders/OutlinePipeline.ts +++ /dev/null @@ -1,59 +0,0 @@ -export class OutlinePipeline extends Phaser.Renderer.WebGL.Pipelines.MultiPipeline { - - // the unique id of this pipeline - public static readonly KEY = 'Outline'; - - /** - * @param {Phaser.Game} game - the controller of the game instance - */ - constructor(game: Phaser.Game) - { - super({ - game: game, - fragShader: ` - precision mediump float; - - uniform sampler2D uMainSampler; - uniform vec2 uTextureSize; - - varying vec2 outTexCoord; - varying float outTintEffect; - varying vec4 outTint; - - void main(void) - { - vec4 texture = texture2D(uMainSampler, outTexCoord); - vec4 texel = vec4(outTint.rgb * outTint.a, outTint.a); - vec4 color = texture; - - if (outTintEffect == 0.0) - { - color = texture * texel; - } - else if (outTintEffect == 1.0) - { - color.rgb = mix(texture.rgb, outTint.rgb * outTint.a, texture.a); - color.a = texture.a * texel.a; - } - else if (outTintEffect == 2.0) - { - color = texel; - } - - vec2 onePixel = vec2(1.0, 1.0) / uTextureSize; - float upAlpha = texture2D(uMainSampler, outTexCoord + vec2(0.0, onePixel.y)).a; - float leftAlpha = texture2D(uMainSampler, outTexCoord + vec2(-onePixel.x, 0.0)).a; - float downAlpha = texture2D(uMainSampler, outTexCoord + vec2(0.0, -onePixel.y)).a; - float rightAlpha = texture2D(uMainSampler, outTexCoord + vec2(onePixel.x, 0.0)).a; - - if (texture.a == 0.0 && max(max(upAlpha, downAlpha), max(leftAlpha, rightAlpha)) == 1.0) - { - color = vec4(1.0, 1.0, 0.0, 1.0); - } - - gl_FragColor = color; - } - ` - }); - } -} diff --git a/front/src/Phaser/UserInput/UserInputManager.ts b/front/src/Phaser/UserInput/UserInputManager.ts index 70bb9b1b..068e84a2 100644 --- a/front/src/Phaser/UserInput/UserInputManager.ts +++ b/front/src/Phaser/UserInput/UserInputManager.ts @@ -2,6 +2,7 @@ import type { Direction } from "../../types"; import type {GameScene} from "../Game/GameScene"; import {touchScreenManager} from "../../Touch/TouchScreenManager"; import {MobileJoystick} from "../Components/MobileJoystick"; +import {enableUserInputsStore} from "../../Stores/UserInputStore"; interface UserInputManagerDatum { keyInstance: Phaser.Input.Keyboard.Key; @@ -58,6 +59,10 @@ export class UserInputManager { if (touchScreenManager.supportTouchScreen) { this.initVirtualJoystick(); } + + enableUserInputsStore.subscribe((enable) => { + enable ? this.restoreControls() : this.disableControls() + }) } initVirtualJoystick() { diff --git a/front/src/Stores/ConsoleGlobalMessageManagerStore.ts b/front/src/Stores/ConsoleGlobalMessageManagerStore.ts new file mode 100644 index 00000000..1fa04bfe --- /dev/null +++ b/front/src/Stores/ConsoleGlobalMessageManagerStore.ts @@ -0,0 +1,5 @@ +import { writable } from "svelte/store"; + +export const consoleGlobalMessageManagerVisibleStore = writable(false); + +export const consoleGlobalMessageManagerFocusStore = writable(false); \ No newline at end of file diff --git a/front/src/Stores/UserInputStore.ts b/front/src/Stores/UserInputStore.ts new file mode 100644 index 00000000..cbb7f0c3 --- /dev/null +++ b/front/src/Stores/UserInputStore.ts @@ -0,0 +1,10 @@ +import {derived} from "svelte/store"; +import {consoleGlobalMessageManagerFocusStore} from "./ConsoleGlobalMessageManagerStore"; + +//derived from the focus on Menu, ConsoleGlobal, Chat and ... +export const enableUserInputsStore = derived( + consoleGlobalMessageManagerFocusStore, + ($consoleGlobalMessageManagerFocusStore) => { + return !$consoleGlobalMessageManagerFocusStore; + } +); \ No newline at end of file diff --git a/front/src/index.ts b/front/src/index.ts index 90d4c612..59e748b4 100644 --- a/front/src/index.ts +++ b/front/src/index.ts @@ -10,6 +10,7 @@ import {SelectCompanionScene} from "./Phaser/Login/SelectCompanionScene"; import {EnableCameraScene} from "./Phaser/Login/EnableCameraScene"; import {CustomizeScene} from "./Phaser/Login/CustomizeScene"; import WebFontLoaderPlugin from 'phaser3-rex-plugins/plugins/webfontloader-plugin.js'; +import OutlinePipelinePlugin from 'phaser3-rex-plugins/plugins/outlinepipeline-plugin.js'; import {EntryScene} from "./Phaser/Login/EntryScene"; import {coWebsiteManager} from "./WebRtc/CoWebsiteManager"; import {MenuScene} from "./Phaser/Menu/MenuScene"; @@ -22,6 +23,8 @@ import {waScaleManager} from "./Phaser/Services/WaScaleManager"; import {Game} from "./Phaser/Game/Game"; import App from './Components/App.svelte'; import {HtmlUtils} from "./WebRtc/HtmlUtils"; +import WebGLRenderer = Phaser.Renderer.WebGL.WebGLRenderer; + const {width, height} = coWebsiteManager.getGameSize(); @@ -123,11 +126,11 @@ const config: GameConfig = { powerPreference: "low-power", callbacks: { postBoot: game => { - // Commented out to try to fix MacOS bug - /*const renderer = game.renderer; + // Install rexOutlinePipeline only if the renderer is WebGL. + const renderer = game.renderer; if (renderer instanceof WebGLRenderer) { - renderer.pipelines.add(OutlinePipeline.KEY, new OutlinePipeline(game)); - }*/ + game.plugins.install('rexOutlinePipeline', OutlinePipelinePlugin, true); + } } } }; diff --git a/front/src/rex-plugins.d.ts b/front/src/rex-plugins.d.ts index 2e160315..8c8a9fc1 100644 --- a/front/src/rex-plugins.d.ts +++ b/front/src/rex-plugins.d.ts @@ -1,4 +1,3 @@ - declare module 'phaser3-rex-plugins/plugins/virtualjoystick.js' { const content: any; // eslint-disable-line export default content; @@ -11,6 +10,17 @@ declare module 'phaser3-rex-plugins/plugins/webfontloader-plugin.js' { const content: any; // eslint-disable-line export default content; } +declare module 'phaser3-rex-plugins/plugins/outlinepipeline-plugin.js' { + import GameObject = Phaser.GameObjects.GameObject; + + class OutlinePipelinePlugin { + add(gameObject: GameObject, config: object); + + remove(gameObject: GameObject, name?: string); + } + + export default OutlinePipelinePlugin; +} declare module 'phaser3-rex-plugins/plugins/gestures.js' { export const Pinch: any; // eslint-disable-line } diff --git a/front/style/index.scss b/front/style/index.scss index 7ed141cd..a6afa557 100644 --- a/front/style/index.scss +++ b/front/style/index.scss @@ -3,3 +3,4 @@ @import "style"; @import "mobile-style.scss"; @import "fonts.scss"; +@import "svelte-style.scss"; diff --git a/front/style/style.scss b/front/style/style.scss index beed1db5..5c958309 100644 --- a/front/style/style.scss +++ b/front/style/style.scss @@ -627,17 +627,15 @@ input[type=range]:focus::-ms-fill-upper { grid-template-columns: repeat(4, 1fr); } -/*CONSOLE*/ - -.message-container, -.main-console{ +/*GLOBAL MESSAGE*/ +.message-container { position: absolute; width: 80%; height: 80%; min-height: 200px; max-height: 80%; top: -80%; - /*left: 10%;*/ + //left: 10%; left: 250px; background: #333333; z-index: 200; @@ -660,7 +658,6 @@ input[type=range]:focus::-ms-fill-upper { max-height: 400px; } -.main-console div.console, .message-container div.clear { position: absolute; color: white; @@ -675,22 +672,11 @@ input[type=range]:focus::-ms-fill-upper { text-align: center; } -.main-console div.message, -.main-console div.setting{ - display: none; -} - -.main-console div.message.active, -.main-console div.setting.active{ - display: block; -} - .message-container div.clear{ width: 100px; left: calc(50% - 50px); } -.main-console div.console img, .message-container div.clear img{ margin-top: 6px; width: 30px; @@ -701,112 +687,26 @@ input[type=range]:focus::-ms-fill-upper { transform: rotateY(0); opacity: 0.5; } -.main-console div.console img:hover, + .message-container div.clear img:hover{ opacity: 1; } -.main-console div.console img.active, .message-container div.clear img{ transform: rotateY(3.142rad); opacity: 1; } -.main-console div.console p, .message-container div.clear p{ margin-top: 12px; } -.main-console div.console:hover, .message-container div.clear:hover { cursor: url('./images/cursor_pointer.png'), pointer; top: calc(100% + 5px); transform: scale(1.2) translateY(3px); } -.main-console #input-send-text{ - min-height: 200px; -} - -.main-console #input-send-text .ql-editor{ - color: white; - min-height: 200px; - max-height: 300px; -} - -.main-console .ql-toolbar{ - background: white; -} - -.main-console .btn-action{ - margin: 10px; - text-align: center; -} - -.main-console .btn-action .btn{ - border: 1px solid black; - background-color: #00000000; - color: #ffda01; - border-radius: 15px; - padding: 10px 30px; - transition: all .2s ease; -} -.main-console .btn-action .btn:hover{ - cursor: url('./images/cursor_pointer.png'), pointer; - background-color: #ffda01; - color: black; - border: 1px solid black; - transform: scale(1.1); -} - -.main-console .menu { - padding: 20px; - color: #ffffffa6; - text-align: center; -} - -.main-console .menu span { - margin: 20px; - cursor: url('./images/cursor_pointer.png'), pointer; -} - -.main-console .menu span.active { - color: white; - border-bottom: solid 1px white; -} - -.main-console section{ - text-align: center; - display: none; -} - -.main-console section.active{ - display: block; -} - -.main-console section div.upload{ - text-align: center; - border: solid 1px #ffda01; - height: 150px; - margin: 10px 200px; - padding: 20px; - min-height: 200px; -} - -.main-console section div.upload label{ - color: #ffda01; -} -.main-console section div.upload input{ - display: none; -} -.main-console section div.upload label img{ - height: 150px; - cursor: url('./images/cursor_pointer.png'), pointer; -} -.main-console section div.upload label img{ - cursor: url('./images/cursor_pointer.png'), pointer; -} - /* VIDEO QUALITY */ .main-console div.setting h1{ diff --git a/front/style/svelte-style.scss b/front/style/svelte-style.scss new file mode 100644 index 00000000..7881dabb --- /dev/null +++ b/front/style/svelte-style.scss @@ -0,0 +1,60 @@ +//Contains all styles not unique to a svelte component. + +//ConsoleGlobalMessage +div.main-console.nes-container { + pointer-events: auto; + margin-left: auto; + margin-right: auto; + top: 20vh; + width: 50vw; + height: 50vh; + padding: 0; + background-color: #333333; + + .btn-action{ + margin: 10px; + text-align: center; + } + + .main-global-message { + width: 100%; + max-height: 100%; + } + + .main-global-message h2 { + text-align: center; + color: white; + } + + div.global-message { + display: flex; + max-height: 100%; + width: 100%; + } + + div.menu { + flex: auto; + } + + div.menu button { + margin: 7px; + } + + .main-input { + width: 95%; + } + +//InputTextGlobalMessage + .section-input-send-text { + margin: 10px; + } + + .section-input-send-text .input-send-text .ql-editor{ + color: white; + min-height: 200px; + } + + .section-input-send-text .ql-toolbar{ + background: white; + } +}