diff --git a/front/src/Connexion/LocalUserStore.ts b/front/src/Connexion/LocalUserStore.ts index 4989a9ea..a766f5dc 100644 --- a/front/src/Connexion/LocalUserStore.ts +++ b/front/src/Connexion/LocalUserStore.ts @@ -48,11 +48,14 @@ class LocalUserStore { return JSON.parse(localStorage.getItem(characterLayersKey) || "null"); } - setCompanion(companion: string): void { - localStorage.setItem(companionKey, companion); + setCompanion(companion: string|null): void { + return localStorage.setItem(companionKey, JSON.stringify(companion)); } getCompanion(): string|null { - return localStorage.getItem(companionKey); + return JSON.parse(localStorage.getItem(companionKey) || "null"); + } + wasCompanionSet(): boolean { + return localStorage.getItem(companionKey) ? true : false; } setGameQualityValue(value: number): void { diff --git a/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts b/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts index bc260c69..b7859a70 100644 --- a/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts +++ b/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts @@ -1,14 +1,16 @@ import LoaderPlugin = Phaser.Loader.LoaderPlugin; import { COMPANION_RESOURCES, CompanionResourceDescriptionInterface } from "./CompanionTextures"; -export const loadAll = (loader: LoaderPlugin): CompanionResourceDescriptionInterface[] => { - const resources = COMPANION_RESOURCES; +export const loadAll = (loader: LoaderPlugin): Promise => { + const promises: Promise[] = []; - resources.forEach((resource: CompanionResourceDescriptionInterface) => { - loader.spritesheet(resource.name, resource.img, { frameWidth: 32, frameHeight: 32, endFrame: 12 }); + COMPANION_RESOURCES.forEach((resource: CompanionResourceDescriptionInterface) => { + promises.push(lazyLoadResource(loader, resource.name)); }); - return resources; + return Promise.all(promises).then(() => { + return COMPANION_RESOURCES; + }); } export const lazyLoadResource = (loader: LoaderPlugin, name: string): Promise => { diff --git a/front/src/Phaser/Game/GameManager.ts b/front/src/Phaser/Game/GameManager.ts index fbe5102c..70625b24 100644 --- a/front/src/Phaser/Game/GameManager.ts +++ b/front/src/Phaser/Game/GameManager.ts @@ -5,6 +5,7 @@ import {MenuScene, MenuSceneName} from "../Menu/MenuScene"; import {HelpCameraSettingsScene, HelpCameraSettingsSceneName} from "../Menu/HelpCameraSettingsScene"; import {LoginSceneName} from "../Login/LoginScene"; import {SelectCharacterSceneName} from "../Login/SelectCharacterScene"; +import {SelectCompanionSceneName} from "../Login/SelectCompanionScene"; import {EnableCameraSceneName} from "../Login/EnableCameraScene"; import {localUserStore} from "../../Connexion/LocalUserStore"; @@ -39,6 +40,8 @@ export class GameManager { return LoginSceneName; } else if (!this.characterLayers) { return SelectCharacterSceneName; + } else if (!localUserStore.wasCompanionSet()) { + return SelectCompanionSceneName; } else { return EnableCameraSceneName; } @@ -65,6 +68,11 @@ export class GameManager { return this.characterLayers; } + + setCompanion(companion: string|null): void { + this.companion = companion; + } + getCompanion(): string|null { return this.companion; } diff --git a/front/src/Phaser/Login/SelectCompanionScene.ts b/front/src/Phaser/Login/SelectCompanionScene.ts new file mode 100644 index 00000000..9b1cb09a --- /dev/null +++ b/front/src/Phaser/Login/SelectCompanionScene.ts @@ -0,0 +1,248 @@ +import Image = Phaser.GameObjects.Image; +import Rectangle = Phaser.GameObjects.Rectangle; +import {gameManager} from "../Game/GameManager"; +import { addLoader } from "../Components/Loader"; +import {TextField} from "../Components/TextField"; +import { ResizableScene } from "./ResizableScene"; +import {EnableCameraSceneName} from "./EnableCameraScene"; +import {localUserStore} from "../../Connexion/LocalUserStore"; +import { loadAll } from "../Companion/CompanionTexturesLoadingManager"; +import { CompanionResourceDescriptionInterface } from "../Companion/CompanionTextures"; + +export const SelectCompanionSceneName = "SelectCompanionScene"; + +enum LoginTextures { + playButton = "play_button", + icon = "icon", + mainFont = "main_font" +} + +export class SelectCompanionScene extends ResizableScene { + private logo!: Image; + private textField!: TextField; + private pressReturnField!: TextField; + private readonly nbCharactersPerRow = 7; + + private selectedRectangle!: Rectangle; + private selectedRectangleXPos = 0; + private selectedRectangleYPos = 0; + + private selectedCompanion!: Phaser.Physics.Arcade.Sprite; + private companions: Array = new Array(); + private companionModels: Array = [null]; + + constructor() { + super({ + key: SelectCompanionSceneName + }); + } + + preload() { + loadAll(this.load).then(resourceDescriptions => { + resourceDescriptions.forEach(resourceDescription => { + this.companionModels.push(resourceDescription); + }); + }); + + this.load.image(LoginTextures.icon, "resources/logos/tcm_full.png"); + this.load.image(LoginTextures.playButton, "resources/objects/play_button.png"); + + // Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap + this.load.bitmapFont(LoginTextures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml'); + + addLoader(this); + } + + create() { + this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Select your companion'); + + this.pressReturnField = new TextField( + this, + this.game.renderer.width / 2, + 90 + 32 * Math.ceil(this.companionModels.length / this.nbCharactersPerRow), + 'Press enter to start' + ); + + const rectangleXStart = this.game.renderer.width / 2 - (this.nbCharactersPerRow / 2) * 32 + 16; + this.selectedRectangle = this.add.rectangle(rectangleXStart, 90, 32, 32).setStrokeStyle(2, 0xFFFFFF); + + this.logo = new Image(this, this.game.renderer.width - 30, this.game.renderer.height - 20, LoginTextures.icon); + this.add.existing(this.logo); + + // input events + this.input.keyboard.on('keyup-ENTER', () => { + return this.nextScene(); + }); + + this.input.keyboard.on('keydown-RIGHT', () => { + if(this.selectedRectangleYPos * this.nbCharactersPerRow + (this.selectedRectangleXPos + 2)) + if ( + this.selectedRectangleXPos < this.nbCharactersPerRow - 1 + && ((this.selectedRectangleYPos * this.nbCharactersPerRow) + (this.selectedRectangleXPos + 1) + 1) <= this.companionModels.length + ) { + this.selectedRectangleXPos++; + } + this.updateSelectedCompanion(); + }); + + this.input.keyboard.on('keydown-LEFT', () => { + if ( + this.selectedRectangleXPos > 0 + && ((this.selectedRectangleYPos * this.nbCharactersPerRow) + (this.selectedRectangleXPos - 1) + 1) <= this.companionModels.length + ) { + this.selectedRectangleXPos--; + } + this.updateSelectedCompanion(); + }); + + this.input.keyboard.on('keydown-DOWN', () => { + if ( + this.selectedRectangleYPos + 1 < Math.ceil(this.companionModels.length / this.nbCharactersPerRow) + && ( + (((this.selectedRectangleYPos + 1) * this.nbCharactersPerRow) + this.selectedRectangleXPos + 1) <= this.companionModels.length // check if companion isn't empty + || (this.selectedRectangleYPos + 1) === Math.ceil(this.companionModels.length / this.nbCharactersPerRow) // check if is custom rectangle + ) + ) { + this.selectedRectangleYPos++; + } + this.updateSelectedCompanion(); + }); + + this.input.keyboard.on('keydown-UP', () => { + if ( + this.selectedRectangleYPos > 0 + && (((this.selectedRectangleYPos - 1) * this.nbCharactersPerRow) + this.selectedRectangleXPos + 1) <= this.companionModels.length + ) { + this.selectedRectangleYPos--; + } + this.updateSelectedCompanion(); + }); + + this.createCurrentCompanion(); + + const companionNumber = this.getCompanionIndex(); + + this.selectedRectangleXPos = companionNumber % this.nbCharactersPerRow; + this.selectedRectangleYPos = Math.floor(companionNumber / this.nbCharactersPerRow); + + this.updateSelectedCompanion(); + } + + update(time: number, delta: number): void { + this.pressReturnField.setVisible(!!(Math.floor(time / 500) % 2)); + } + + private nextScene(): void { + // store companion + const companionNumber = this.selectedRectangleXPos + this.selectedRectangleYPos * this.nbCharactersPerRow; + const model = this.companionModels[companionNumber]; + const companion = model === null ? null : model.name; + + localUserStore.setCompanion(companion); + + // next scene + this.scene.stop(SelectCompanionSceneName); + + gameManager.setCompanion(companion); + gameManager.tryResumingGame(this, EnableCameraSceneName); + + this.scene.remove(SelectCompanionSceneName); + } + + private createCurrentCompanion(): void { + for (let i = 0; i < this.companionModels.length; i++) { + const companionResource = this.companionModels[i]; + + const col = i % this.nbCharactersPerRow; + const row = Math.floor(i / this.nbCharactersPerRow); + + const [x, y] = this.getCharacterPosition(col, row); + + let name = "null"; + if (companionResource !== null) { + name = companionResource.name; + } + + const companion = this.physics.add.sprite(x, y, name, 0); + companion.setBounce(0.2); + companion.setCollideWorldBounds(true); + + if (companionResource !== null) { + this.anims.create({ + key: name, + frames: this.anims.generateFrameNumbers(name, {start: 0, end: 2,}), + frameRate: 10, + repeat: -1 + }); + } + + companion.setInteractive().on("pointerdown", () => { + this.selectedRectangleXPos = col; + this.selectedRectangleYPos = row; + this.updateSelectedCompanion(); + }); + + this.companions.push(companion); + } + + this.selectedCompanion = this.companions[0]; + } + + private getCharacterPosition(x: number, y: number): [number, number] { + return [ + this.game.renderer.width / 2 + 16 + (x - this.nbCharactersPerRow / 2) * 32, + y * 32 + 90 + ]; + } + + private updateSelectedCompanion(): void { + this.selectedCompanion?.anims.pause(); + + const [x, y] = this.getCharacterPosition(this.selectedRectangleXPos, this.selectedRectangleYPos); + this.selectedRectangle.setVisible(true); + this.selectedRectangle.setX(x); + this.selectedRectangle.setY(y); + this.selectedRectangle.setSize(32, 32); + + const companionNumber = this.selectedRectangleXPos + this.selectedRectangleYPos * this.nbCharactersPerRow; + const model = this.companionModels[companionNumber]; + + const companion = this.companions[companionNumber]; + + if (model !== null) { + companion.play(model.name); + } + + this.selectedCompanion = companion; + } + + public onResize(ev: UIEvent): void { + this.textField.x = this.game.renderer.width / 2; + this.pressReturnField.x = this.game.renderer.width / 2; + this.logo.x = this.game.renderer.width - 30; + this.logo.y = this.game.renderer.height - 20; + + for (let i = 0; i model !== null && model.name === companion); + } +} diff --git a/front/src/index.ts b/front/src/index.ts index c0663acd..aab45a9b 100644 --- a/front/src/index.ts +++ b/front/src/index.ts @@ -6,6 +6,7 @@ import {DEBUG_MODE, JITSI_URL, RESOLUTION} from "./Enum/EnvironmentVariable"; import {LoginScene} from "./Phaser/Login/LoginScene"; import {ReconnectingScene} from "./Phaser/Reconnecting/ReconnectingScene"; import {SelectCharacterScene} from "./Phaser/Login/SelectCharacterScene"; +import {SelectCompanionScene} from "./Phaser/Login/SelectCompanionScene"; import {EnableCameraScene} from "./Phaser/Login/EnableCameraScene"; import {CustomizeScene} from "./Phaser/Login/CustomizeScene"; import {ResizableScene} from "./Phaser/Login/ResizableScene"; @@ -74,7 +75,7 @@ const config: GameConfig = { width: width / RESOLUTION, height: height / RESOLUTION, parent: "game", - scene: [EntryScene, LoginScene, SelectCharacterScene, EnableCameraScene, ReconnectingScene, ErrorScene, CustomizeScene, MenuScene, HelpCameraSettingsScene], + scene: [EntryScene, LoginScene, SelectCharacterScene, SelectCompanionScene, EnableCameraScene, ReconnectingScene, ErrorScene, CustomizeScene, MenuScene, HelpCameraSettingsScene], zoom: RESOLUTION, fps: fps, dom: {