diff --git a/front/dist/resources/html/gameMenu.html b/front/dist/resources/html/gameMenu.html index d5e9ad7e..fa46090a 100644 --- a/front/dist/resources/html/gameMenu.html +++ b/front/dist/resources/html/gameMenu.html @@ -36,6 +36,9 @@
+
+ +
diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index ba5b6c07..5544ca47 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -151,6 +151,7 @@ video#myCamVideo{ .btn-cam-action { + pointer-events: all; position: absolute; bottom: 0px; right: 0px; @@ -186,18 +187,26 @@ video#myCamVideo{ transition: 280ms; } .btn-micro{ + pointer-events: auto; transition: all .3s; right: 44px; } .btn-video{ + pointer-events: auto; transition: all .25s; right: 134px; } .btn-monitor{ + pointer-events: auto; transition: all .2s; right: 224px; } - +.btn-copy{ + pointer-events: auto; + transition: all .3s; + right: 44px; + opacity: 1; +} .btn-cam-action div img{ height: 22px; width: 30px; @@ -502,6 +511,7 @@ input[type=range]:focus::-ms-fill-upper { position: absolute; width: 100%; height: 100%; + pointer-events: none; /* TODO: DO WE NEED FLEX HERE???? WE WANT A SIDEBAR OF EXACTLY 25% (note: flex useful for direction!!!) */ } @@ -537,6 +547,7 @@ input[type=range]:focus::-ms-fill-upper { .sidebar { flex: 0 0 25%; display: flex; + pointer-events: none; } .sidebar > div { diff --git a/front/package.json b/front/package.json index 1ba7f3c2..6189ff4c 100644 --- a/front/package.json +++ b/front/package.json @@ -31,6 +31,7 @@ "generic-type-guard": "^3.2.0", "google-protobuf": "^3.13.0", "phaser": "3.24.1", + "phaser3-rex-plugins": "^1.1.42", "queue-typescript": "^1.0.1", "quill": "^1.3.7", "rxjs": "^6.6.3", diff --git a/front/src/Connexion/LocalUserStore.ts b/front/src/Connexion/LocalUserStore.ts index 4741fc8c..11977f7a 100644 --- a/front/src/Connexion/LocalUserStore.ts +++ b/front/src/Connexion/LocalUserStore.ts @@ -9,7 +9,8 @@ const gameQualityKey = 'gameQuality'; const videoQualityKey = 'videoQuality'; const audioPlayerVolumeKey = 'audioVolume'; const audioPlayerMuteKey = 'audioMute'; -const helpCameraSettingsShown = 'helpCameraSettingsShown'; +const helpCameraSettingsShown = 'helpCameraSettingsShown'; +const joystickKey = 'showJoystick'; class LocalUserStore { saveUser(localUser: LocalUser) { @@ -100,6 +101,13 @@ class LocalUserStore { getHelpCameraSettingsShown(): boolean { return localStorage.getItem(helpCameraSettingsShown) === '1'; } + + setJoystick(value: boolean): void { + localStorage.setItem(joystickKey, value.toString()); + } + getJoystick(): boolean { + return localStorage.getItem(joystickKey) === 'true'; + } } export const localUserStore = new LocalUserStore(); diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 24511582..5d7d173f 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -51,6 +51,10 @@ import {Room} from "../../Connexion/Room"; import {jitsiFactory} from "../../WebRtc/JitsiFactory"; import {urlManager} from "../../Url/UrlManager"; import {audioManager} from "../../WebRtc/AudioManager"; +import {IVirtualJoystick} from "../../types"; +const { + default: VirtualJoystick, +} = require("phaser3-rex-plugins/plugins/virtualjoystick.js"); import {PresentationModeIcon} from "../Components/PresentationModeIcon"; import {ChatModeIcon} from "../Components/ChatModeIcon"; import {OpenChatIcon, openChatIconName} from "../Components/OpenChatIcon"; @@ -164,6 +168,7 @@ export class GameScene extends ResizableScene implements CenterListener { private messageSubscription: Subscription|null = null; private popUpElements : Map = new Map(); private originalMapUrl: string|undefined; + public virtualJoystick!: IVirtualJoystick; constructor(private room: Room, MapUrlFile: string, customKey?: string|undefined) { super({ @@ -183,6 +188,16 @@ export class GameScene extends ResizableScene implements CenterListener { this.connectionAnswerPromise = new Promise((resolve, reject): void => { this.connectionAnswerPromiseResolve = resolve; }) + const joystickVisible = localUserStore.getJoystick(); + if (joystickVisible) { + const canvas = document.querySelector('canvas') + canvas?.addEventListener('click', () => { + const body = document.querySelector('body') + body?.requestFullscreen() + }, { + once: true + }) + } } //hook preload scene @@ -399,9 +414,19 @@ export class GameScene extends ResizableScene implements CenterListener { //initialise list of other player this.MapPlayers = this.physics.add.group({immovable: true}); + this.virtualJoystick = new VirtualJoystick(this, { + x: this.game.renderer.width / 2, + y: this.game.renderer.height / 2, + radius: 20, + base: this.add.circle(0, 0, 20, 0x888888), + thumb: this.add.circle(0, 0, 10, 0xcccccc), + enable: true, + dir: "8dir", + }); + this.virtualJoystick.visible = localUserStore.getJoystick() //create input to move - this.userInputManager = new UserInputManager(this); mediaManager.setUserInputManager(this.userInputManager); + this.userInputManager = new UserInputManager(this, this.virtualJoystick); //notify game manager can to create currentUser in map this.createCurrentPlayer(); diff --git a/front/src/Phaser/Menu/MenuScene.ts b/front/src/Phaser/Menu/MenuScene.ts index f29fd39d..cdbe13f5 100644 --- a/front/src/Phaser/Menu/MenuScene.ts +++ b/front/src/Phaser/Menu/MenuScene.ts @@ -291,6 +291,9 @@ export class MenuScene extends Phaser.Scene { case 'editGameSettingsButton': this.openGameSettingsMenu(); break; + case 'showJoystick': + this.showJoystick(); + break; case 'adminConsoleButton': gameManager.getCurrentGameScene(this).ConsoleGlobalMessageManager.activeMessageConsole(); break; @@ -328,4 +331,21 @@ export class MenuScene extends Phaser.Scene { this.closeGameShare(); this.gameReportElement.close(); } + + private showJoystick() { + const gameScene = gameManager.getCurrentGameScene(this) + if (gameScene?.virtualJoystick) { + const joystickVisible = !gameScene.virtualJoystick.visible + gameScene.virtualJoystick.visible = joystickVisible + localUserStore.setJoystick(joystickVisible) + } + const body = document.querySelector('body') + if (body) { + if (document.fullscreenElement ?? document.fullscreen) { + document.exitFullscreen() + } else { + body.requestFullscreen(); + } + } + } } diff --git a/front/src/Phaser/UserInput/UserInputManager.ts b/front/src/Phaser/UserInput/UserInputManager.ts index 2e686d8e..9f6c76f3 100644 --- a/front/src/Phaser/UserInput/UserInputManager.ts +++ b/front/src/Phaser/UserInput/UserInputManager.ts @@ -1,4 +1,8 @@ +import { Direction, IVirtualJoystick } from "../../types"; import {GameScene} from "../Game/GameScene"; +const { + default: VirtualJoystick, +} = require("phaser3-rex-plugins/plugins/virtualjoystick.js"); interface UserInputManagerDatum { keyInstance: Phaser.Input.Keyboard.Key; @@ -25,6 +29,9 @@ export class ActiveEventList { set(event: UserInputEvent, value: boolean): void { this.KeysCode.set(event, value); } + forEach(callback: (value: boolean, key: UserInputEvent) => void): void { + this.KeysCode.forEach(callback); + } } //this class is responsible for catching user inputs and listing all active user actions at every game tick events. @@ -32,10 +39,32 @@ export class UserInputManager { private KeysCode!: UserInputManagerDatum[]; private Scene: GameScene; private isInputDisabled : boolean; - constructor(Scene : GameScene) { + private joystickEvents = new ActiveEventList(); + + constructor(Scene: GameScene, virtualJoystick: IVirtualJoystick) { this.Scene = Scene; - this.initKeyBoardEvent(); this.isInputDisabled = false; + this.initKeyBoardEvent(); + virtualJoystick.on("update", () => { + const cursorKeys = virtualJoystick.createCursorKeys(); + for (const name in cursorKeys) { + const key = cursorKeys[name as Direction]; + switch (name) { + case "up": + this.joystickEvents.set(UserInputEvent.MoveUp, key.isDown); + break; + case "left": + this.joystickEvents.set(UserInputEvent.MoveLeft, key.isDown); + break; + case "down": + this.joystickEvents.set(UserInputEvent.MoveDown, key.isDown); + break; + case "right": + this.joystickEvents.set(UserInputEvent.MoveRight, key.isDown); + break; + } + } + }); } initKeyBoardEvent(){ @@ -78,11 +107,15 @@ export class UserInputManager { if (this.isInputDisabled) { return eventsMap; } + this.joystickEvents.forEach((value, key) => { + if (value) { + eventsMap.set(key, value); + } + }); this.KeysCode.forEach(d => { if (d. keyInstance.isDown) { eventsMap.set(d.event, true); } - }); return eventsMap; } diff --git a/front/src/types.ts b/front/src/types.ts new file mode 100644 index 00000000..d65b7f5a --- /dev/null +++ b/front/src/types.ts @@ -0,0 +1,22 @@ +import Phaser from "phaser"; + +export type CursorKey = { + isDown: boolean +} + +export type Direction = 'left' | 'right' | 'up' | 'down' + +export interface CursorKeys extends Record { + left: CursorKey; + right: CursorKey; + up: CursorKey; + down: CursorKey; +} + +export interface IVirtualJoystick extends Phaser.GameObjects.GameObject { + y: number; + x: number; + visible: boolean; + createCursorKeys: () => CursorKeys; +} + diff --git a/front/tsconfig.json b/front/tsconfig.json index b5c8c74d..fea8fd54 100644 --- a/front/tsconfig.json +++ b/front/tsconfig.json @@ -9,17 +9,18 @@ "downlevelIteration": true, "jsx": "react", "allowJs": true, + "esModuleInterop": true, - "strict": true, /* Enable all strict type-checking options. */ - "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - "strictNullChecks": true, /* Enable strict null checks. */ - "strictFunctionTypes": true, /* Enable strict checking of function types. */ - "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + "strict": true, /* Enable all strict type-checking options. */ + "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + "strictNullChecks": true, /* Enable strict null checks. */ + "strictFunctionTypes": true, /* Enable strict checking of function types. */ + "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */ + "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */ } } diff --git a/front/yarn.lock b/front/yarn.lock index eac99e4e..26573239 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -1819,6 +1819,11 @@ eventemitter3@^2.0.3: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-2.0.3.tgz#b5e1079b59fb5e1ba2771c0a993be060a58c99ba" integrity sha1-teEHm1n7XhuidxwKmTvgYKWMmbo= +eventemitter3@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" + integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== + eventemitter3@^4.0.0, eventemitter3@^4.0.4: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" @@ -3064,6 +3069,11 @@ loglevel@^1.6.8: resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.0.tgz#728166855a740d59d38db01cf46f042caa041bb0" integrity sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ== +lokijs@^1.5.11: + version "1.5.11" + resolved "https://registry.yarnpkg.com/lokijs/-/lokijs-1.5.11.tgz#2b2ea82ec66050e4b112c6cfc588dac22d362b13" + integrity sha512-YYyuBPxMn/oS0tFznQDbIX5XL1ltMcwFqCboDr8voYE4VCDzR5vAsrvQDhlnua4lBeqMqHmLvUXRTmRUzUKH1Q== + lower-case@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.1.tgz#39eeb36e396115cc05e29422eaea9e692c9408c7" @@ -3625,6 +3635,11 @@ pako@~1.0.5: resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== +papaparse@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/papaparse/-/papaparse-5.3.0.tgz#ab1702feb96e79ab4309652f36db9536563ad05a" + integrity sha512-Lb7jN/4bTpiuGPrYy4tkKoUS8sTki8zacB5ke1p5zolhcSE4TlWgrlsxjrDTbG/dFVh07ck7X36hUf/b5V68pg== + parallel-transform@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc" @@ -3752,6 +3767,16 @@ pbkdf2@^3.0.3: safe-buffer "^5.0.1" sha.js "^2.4.8" +phaser3-rex-plugins@^1.1.42: + version "1.1.44" + resolved "https://registry.yarnpkg.com/phaser3-rex-plugins/-/phaser3-rex-plugins-1.1.44.tgz#83588ab2801c5b3a80a18be4f0ae37f1f32096b0" + integrity sha512-JPr3+UQv4+uv4etZr80aFhYxw2dk4UYG9/gQ4uMSSXQuc8gXQkXCr3J3zm8LJqH+1FXeK8nncpC7TKa4uKLgQw== + dependencies: + eventemitter3 "^3.1.2" + lokijs "^1.5.11" + papaparse "^5.3.0" + webfontloader "^1.6.28" + phaser@3.24.1: version "3.24.1" resolved "https://registry.yarnpkg.com/phaser/-/phaser-3.24.1.tgz#376e0c965d2a35af37c06ee78627dafbde5be017" @@ -5144,6 +5169,11 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" +webfontloader@^1.6.28: + version "1.6.28" + resolved "https://registry.yarnpkg.com/webfontloader/-/webfontloader-1.6.28.tgz#db786129253cb6e8eae54c2fb05f870af6675bae" + integrity sha1-23hhKSU8tujq5UwvsF+HCvZnW64= + webpack-cli@^3.3.11: version "3.3.12" resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.12.tgz#94e9ada081453cd0aa609c99e500012fd3ad2d4a"