From bb3f52df371591ae9ba008c74f71c8ef6c061c73 Mon Sep 17 00:00:00 2001 From: Piotr Hanusiak Date: Fri, 22 Apr 2022 17:56:28 +0200 Subject: [PATCH] New select woka scene (#1997) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * animated grid tiles * change woka's category with arrow buttons * added some dynamic effects to woka's list * add some drag distance treshold * do not show arrows if only one collection * WSAD and arrow keys movement through WOKA models * Adapting end to end tests Co-authored-by: Piotr 'pwh' Hanusiak Co-authored-by: David NĂ©grier --- .../SelectCharacterScene.svelte | 134 +++--- .../Phaser/Components/SelectWoka/WokaSlot.ts | 35 ++ front/src/Phaser/Entity/PlayerTextures.ts | 85 ++-- .../Phaser/Login/AbstractCharacterScene.ts | 4 + front/src/Phaser/Login/CustomizeScene.ts | 2 +- .../Login/SelectCharacterMobileScene.ts | 58 --- .../src/Phaser/Login/SelectCharacterScene.ts | 388 +++++++++--------- front/src/Stores/SelectCharacterSceneStore.ts | 2 + front/src/index.ts | 4 +- package.json | 3 +- tests/tests/utils/roles.ts | 3 +- 11 files changed, 362 insertions(+), 356 deletions(-) create mode 100644 front/src/Phaser/Components/SelectWoka/WokaSlot.ts delete mode 100644 front/src/Phaser/Login/SelectCharacterMobileScene.ts diff --git a/front/src/Components/selectCharacter/SelectCharacterScene.svelte b/front/src/Components/selectCharacter/SelectCharacterScene.svelte index 807cf88f..ecf00828 100644 --- a/front/src/Components/selectCharacter/SelectCharacterScene.svelte +++ b/front/src/Components/selectCharacter/SelectCharacterScene.svelte @@ -2,18 +2,19 @@ import type { Game } from "../../Phaser/Game/Game"; import { SelectCharacterScene, SelectCharacterSceneName } from "../../Phaser/Login/SelectCharacterScene"; import LL from "../../i18n/i18n-svelte"; - import { customizeAvailableStore } from "../../Stores/SelectCharacterSceneStore"; + import { customizeAvailableStore, selectedCollection } from "../../Stores/SelectCharacterSceneStore"; export let game: Game; const selectCharacterScene = game.scene.getScene(SelectCharacterSceneName) as SelectCharacterScene; + const showArrows = selectCharacterScene.getCollectionKeysSize() > 1; function selectLeft() { - selectCharacterScene.moveToLeft(); + selectCharacterScene.selectPreviousCollection(); } function selectRight() { - selectCharacterScene.moveToRight(); + selectCharacterScene.selectNextCollection(); } function cameraScene() { @@ -25,83 +26,72 @@ } -
-
-

{$LL.woka.selectWoka.title()}

- - -
-
+
+

{$LL.woka.selectWoka.title()}

+
+
+ {#if showArrows} + + {$selectedCollection} + + {/if} +
+
+ + {#if $customizeAvailableStore} {$LL.woka.selectWoka.customize()} - {#if $customizeAvailableStore} - - {/if} -
- + {/if} +
diff --git a/front/src/Phaser/Components/SelectWoka/WokaSlot.ts b/front/src/Phaser/Components/SelectWoka/WokaSlot.ts new file mode 100644 index 00000000..696a9db7 --- /dev/null +++ b/front/src/Phaser/Components/SelectWoka/WokaSlot.ts @@ -0,0 +1,35 @@ +import { GridItem } from "@home-based-studio/phaser3-utils"; + +export class WokaSlot extends GridItem { + private sprite: Phaser.GameObjects.Sprite; + private selection: Phaser.GameObjects.Rectangle; + + private readonly SIZE: number = 50; + + constructor(scene: Phaser.Scene, spriteKey: string, id?: string) { + super(scene, id); + + this.sprite = this.scene.add.sprite(0, 0, spriteKey); + this.selection = this.scene.add + .rectangle(0, 0, this.SIZE, this.SIZE) + .setStrokeStyle(1, 0xffffff) + .setVisible(false); + + this.add([this.selection, this.sprite]); + this.setSize(this.SIZE, this.SIZE); + this.setInteractive({ cursor: "pointer" }); + this.scene.input.setDraggable(this); + + this.bindEventHandlers(); + + this.scene.add.existing(this); + } + + public getSprite(): Phaser.GameObjects.Sprite { + return this.sprite; + } + + public select(select: boolean = true): void { + this.selection.setVisible(select); + } +} diff --git a/front/src/Phaser/Entity/PlayerTextures.ts b/front/src/Phaser/Entity/PlayerTextures.ts index e1413f6d..66e02c7b 100644 --- a/front/src/Phaser/Entity/PlayerTextures.ts +++ b/front/src/Phaser/Entity/PlayerTextures.ts @@ -36,14 +36,16 @@ export enum PlayerTexturesKey { } export class PlayerTextures { - private PLAYER_RESOURCES: BodyResourceDescriptionListInterface = {}; - private COLOR_RESOURCES: BodyResourceDescriptionListInterface = {}; - private EYES_RESOURCES: BodyResourceDescriptionListInterface = {}; - private HAIR_RESOURCES: BodyResourceDescriptionListInterface = {}; - private CLOTHES_RESOURCES: BodyResourceDescriptionListInterface = {}; - private HATS_RESOURCES: BodyResourceDescriptionListInterface = {}; - private ACCESSORIES_RESOURCES: BodyResourceDescriptionListInterface = {}; - private LAYERS: BodyResourceDescriptionListInterface[] = []; + private wokaResources: BodyResourceDescriptionListInterface = {}; + private colorResources: BodyResourceDescriptionListInterface = {}; + private eyesResources: BodyResourceDescriptionListInterface = {}; + private hairResources: BodyResourceDescriptionListInterface = {}; + private clothesResources: BodyResourceDescriptionListInterface = {}; + private hatsResources: BodyResourceDescriptionListInterface = {}; + private accessoriesResources: BodyResourceDescriptionListInterface = {}; + private layers: BodyResourceDescriptionListInterface[] = []; + + private wokaCollections = new Map(); public loadPlayerTexturesMetadata(metadata: WokaList): void { this.mapTexturesMetadataIntoResources(metadata); @@ -52,43 +54,53 @@ export class PlayerTextures { public getTexturesResources(key: PlayerTexturesKey): BodyResourceDescriptionListInterface { switch (key) { case PlayerTexturesKey.Accessory: - return this.ACCESSORIES_RESOURCES; + return this.accessoriesResources; case PlayerTexturesKey.Body: - return this.COLOR_RESOURCES; + return this.colorResources; case PlayerTexturesKey.Clothes: - return this.CLOTHES_RESOURCES; + return this.clothesResources; case PlayerTexturesKey.Eyes: - return this.EYES_RESOURCES; + return this.eyesResources; case PlayerTexturesKey.Hair: - return this.HAIR_RESOURCES; + return this.hairResources; case PlayerTexturesKey.Hat: - return this.HATS_RESOURCES; + return this.hatsResources; case PlayerTexturesKey.Woka: - return this.PLAYER_RESOURCES; + return this.wokaResources; } } public getLayers(): BodyResourceDescriptionListInterface[] { - return this.LAYERS; + return this.layers; + } + + public getCollectionsKeys(): string[] { + return Array.from(this.wokaCollections.keys()); + } + + public getWokaCollectionTextures(key: string): BodyResourceDescriptionInterface[] { + return this.wokaCollections.get(key) ?? []; } private mapTexturesMetadataIntoResources(metadata: WokaList): void { - this.PLAYER_RESOURCES = this.getMappedResources(metadata.woka); - this.COLOR_RESOURCES = this.getMappedResources(metadata.body); - this.EYES_RESOURCES = this.getMappedResources(metadata.eyes); - this.HAIR_RESOURCES = this.getMappedResources(metadata.hair); - this.CLOTHES_RESOURCES = this.getMappedResources(metadata.clothes); - this.HATS_RESOURCES = this.getMappedResources(metadata.hat); - this.ACCESSORIES_RESOURCES = this.getMappedResources(metadata.accessory); + this.wokaResources = this.getMappedResources(metadata.woka); + this.colorResources = this.getMappedResources(metadata.body); + this.eyesResources = this.getMappedResources(metadata.eyes); + this.hairResources = this.getMappedResources(metadata.hair); + this.clothesResources = this.getMappedResources(metadata.clothes); + this.hatsResources = this.getMappedResources(metadata.hat); + this.accessoriesResources = this.getMappedResources(metadata.accessory); - this.LAYERS = [ - this.COLOR_RESOURCES, - this.EYES_RESOURCES, - this.HAIR_RESOURCES, - this.CLOTHES_RESOURCES, - this.HATS_RESOURCES, - this.ACCESSORIES_RESOURCES, + this.layers = [ + this.colorResources, + this.eyesResources, + this.hairResources, + this.clothesResources, + this.hatsResources, + this.accessoriesResources, ]; + + this.mapWokaCollections(metadata.woka); } private getMappedResources(category: WokaPartType): BodyResourceDescriptionListInterface { @@ -103,6 +115,19 @@ export class PlayerTextures { } return resources; } + + private mapWokaCollections(category: WokaPartType): void { + if (!category) { + return; + } + for (const collection of category.collections) { + const textures: BodyResourceDescriptionInterface[] = []; + for (const texture of collection.textures) { + textures.push({ id: texture.id, img: texture.url }); + } + this.wokaCollections.set(collection.name, textures); + } + } } export const OBJECTS: BodyResourceDescriptionInterface[] = [ diff --git a/front/src/Phaser/Login/AbstractCharacterScene.ts b/front/src/Phaser/Login/AbstractCharacterScene.ts index 27a4aeab..7545e26c 100644 --- a/front/src/Phaser/Login/AbstractCharacterScene.ts +++ b/front/src/Phaser/Login/AbstractCharacterScene.ts @@ -11,4 +11,8 @@ export abstract class AbstractCharacterScene extends ResizableScene { this.playerTextures = new PlayerTextures(); this.superLoad = new SuperLoaderPlugin(this); } + + preload() { + this.input.dragDistanceThreshold = 10; + } } diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index e03863c0..59bbfeed 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -54,7 +54,7 @@ export class CustomizeScene extends AbstractCharacterScene { } public preload(): void { - this.input.dragDistanceThreshold = 10; + super.preload(); this.load.image("iconClothes", "/resources/icons/icon_clothes.png"); this.load.image("iconAccessory", "/resources/icons/icon_accessory.png"); diff --git a/front/src/Phaser/Login/SelectCharacterMobileScene.ts b/front/src/Phaser/Login/SelectCharacterMobileScene.ts deleted file mode 100644 index c04d6fb3..00000000 --- a/front/src/Phaser/Login/SelectCharacterMobileScene.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { SelectCharacterScene } from "./SelectCharacterScene"; - -export class SelectCharacterMobileScene extends SelectCharacterScene { - create() { - super.create(); - this.onResize(); - this.selectedRectangle.destroy(); - } - - protected defineSetupPlayer(num: number) { - const deltaX = 30; - const deltaY = 2; - let [playerX, playerY] = this.getCharacterPosition(); - let playerVisible = true; - let playerScale = 1.5; - let playerOpacity = 1; - - if (this.currentSelectUser !== num) { - playerVisible = false; - } - if (num === this.currentSelectUser + 1) { - playerY -= deltaY; - playerX += deltaX; - playerScale = 0.8; - playerOpacity = 0.6; - playerVisible = true; - } - if (num === this.currentSelectUser + 2) { - playerY -= deltaY; - playerX += deltaX * 2; - playerScale = 0.8; - playerOpacity = 0.6; - playerVisible = true; - } - if (num === this.currentSelectUser - 1) { - playerY -= deltaY; - playerX -= deltaX; - playerScale = 0.8; - playerOpacity = 0.6; - playerVisible = true; - } - if (num === this.currentSelectUser - 2) { - playerY -= deltaY; - playerX -= deltaX * 2; - playerScale = 0.8; - playerOpacity = 0.6; - playerVisible = true; - } - return { playerX, playerY, playerScale, playerOpacity, playerVisible }; - } - - /** - * Returns pixel position by on column and row number - */ - protected getCharacterPosition(): [number, number] { - return [this.game.renderer.width / 2, this.game.renderer.height / 3]; - } -} diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index 13bc1e75..30323463 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -1,5 +1,4 @@ import { gameManager } from "../Game/GameManager"; -import Rectangle = Phaser.GameObjects.Rectangle; import { EnableCameraSceneName } from "./EnableCameraScene"; import { CustomizeSceneName } from "./CustomizeScene"; import { localUserStore } from "../../Connexion/LocalUserStore"; @@ -13,25 +12,25 @@ import { PinchManager } from "../UserInput/PinchManager"; import { selectCharacterSceneVisibleStore } from "../../Stores/SelectCharacterStore"; import { waScaleManager } from "../Services/WaScaleManager"; import { analyticsClient } from "../../Administration/AnalyticsClient"; -import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils"; import { PUSHER_URL } from "../../Enum/EnvironmentVariable"; -import { customizeAvailableStore } from "../../Stores/SelectCharacterSceneStore"; +import { customizeAvailableStore, selectedCollection } from "../../Stores/SelectCharacterSceneStore"; +import { DraggableGrid } from "@home-based-studio/phaser3-utils"; +import { WokaSlot } from "../Components/SelectWoka/WokaSlot"; +import { DraggableGridEvent } from "@home-based-studio/phaser3-utils/lib/utils/gui/containers/grids/DraggableGrid"; import { wokaList } from "../../Messages/JsonMessages/PlayerTextures"; //todo: put this constants in a dedicated file export const SelectCharacterSceneName = "SelectCharacterScene"; export class SelectCharacterScene extends AbstractCharacterScene { - protected readonly nbCharactersPerRow = 6; - protected selectedPlayer!: Phaser.Physics.Arcade.Sprite | null; // null if we are selecting the "customize" option - protected players: Array = new Array(); + protected selectedWoka!: Phaser.GameObjects.Sprite | null; // null if we are selecting the "customize" option protected playerModels!: BodyResourceDescriptionInterface[]; - protected selectedRectangle!: Rectangle; - - protected currentSelectUser = 0; - protected pointerClicked: boolean = false; - protected pointerTimer: number = 0; + private charactersDraggableGrid!: DraggableGrid; + private collectionKeys!: string[]; + private selectedCollectionIndex!: number; + private selectedGridItemIndex?: number; + private gridRowsCount: number = 1; protected lazyloadingAttempt = true; //permit to update texture loaded after renderer private loader: Loader; @@ -44,7 +43,8 @@ export class SelectCharacterScene extends AbstractCharacterScene { this.playerTextures = new PlayerTextures(); } - preload() { + public preload() { + super.preload(); const wokaMetadataKey = "woka-list" + gameManager.currentStartedRoom.href; this.cache.json.remove(wokaMetadataKey); @@ -67,228 +67,236 @@ export class SelectCharacterScene extends AbstractCharacterScene { } ) .catch((e) => console.error(e)); + this.playerModels = loadAllDefaultModels(this.load, this.playerTextures); + this.lazyloadingAttempt = false; //this function must stay at the end of preload function this.loader.addLoader(); } - create() { + public create() { + waScaleManager.zoomModifier = 1; + this.selectedWoka = null; + this.selectedCollectionIndex = 0; + this.collectionKeys = this.playerTextures.getCollectionsKeys(); + selectedCollection.set(this.getSelectedCollectionName()); + customizeAvailableStore.set(this.isCustomizationAvailable()); selectCharacterSceneVisibleStore.set(true); - this.events.addListener("wake", () => { - waScaleManager.saveZoom(); - waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 2 : 1; - selectCharacterSceneVisibleStore.set(true); - }); if (touchScreenManager.supportTouchScreen) { new PinchManager(this); } - waScaleManager.saveZoom(); - waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 2 : 1; - - 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.selectedRectangle.setDepth(2); - - /*create user*/ - this.createCurrentPlayer(); - - this.input.keyboard.on("keyup-ENTER", () => { - return this.nextSceneToCameraScene(); + this.charactersDraggableGrid = new DraggableGrid(this, { + position: { x: 0, y: 0 }, + maskPosition: { x: 0, y: 0 }, + dimension: { x: 485, y: 165 }, + horizontal: true, + repositionToCenter: true, + itemsInRow: 1, + margin: { + left: ((innerWidth - 200) / waScaleManager.getActualZoom()) * 0.5, + right: ((innerWidth - 200) / waScaleManager.getActualZoom()) * 0.5, + }, + spacing: 5, + debug: { + showDraggableSpace: false, + }, }); - this.input.keyboard.on("keydown-RIGHT", () => { - this.moveToRight(); - }); - this.input.keyboard.on("keydown-LEFT", () => { - this.moveToLeft(); - }); - this.input.keyboard.on("keydown-UP", () => { - this.moveToUp(); - }); - this.input.keyboard.on("keydown-DOWN", () => { - this.moveToDown(); - }); + this.bindEventHandlers(); + + this.onResize(); } public nextSceneToCameraScene(): void { - if (this.selectedPlayer !== null && !areCharacterLayersValid([this.selectedPlayer.texture.key])) { + if (this.selectedWoka !== null && !areCharacterLayersValid([this.selectedWoka.texture.key])) { return; } - if (!this.selectedPlayer) { + if (!this.selectedWoka) { return; } analyticsClient.validationWoka("SelectWoka"); + gameManager.setCharacterLayers([this.selectedWoka.texture.key]); + this.selectedWoka = null; this.scene.stop(SelectCharacterSceneName); - waScaleManager.restoreZoom(); - gameManager.setCharacterLayers([this.selectedPlayer.texture.key]); gameManager.tryResumingGame(EnableCameraSceneName); - this.players = []; selectCharacterSceneVisibleStore.set(false); this.events.removeListener("wake"); } public nextSceneToCustomizeScene(): void { - if (this.selectedPlayer !== null && !areCharacterLayersValid([this.selectedPlayer.texture.key])) { + if (this.selectedWoka !== null && !areCharacterLayersValid([this.selectedWoka.texture.key])) { return; } + this.selectedWoka = null; this.scene.sleep(SelectCharacterSceneName); - waScaleManager.restoreZoom(); this.scene.run(CustomizeSceneName); selectCharacterSceneVisibleStore.set(false); } - createCurrentPlayer(): void { - for (let i = 0; i < this.playerModels.length; i++) { - const playerResource = this.playerModels[i]; - //check already exist texture - if (this.players.find((c) => c.texture.key === playerResource.id)) { - continue; - } - - const [middleX, middleY] = this.getCharacterPosition(); - const player = this.physics.add.sprite(middleX, middleY, playerResource.id, 0); - this.setUpPlayer(player, i); - this.anims.create({ - key: playerResource.id, - frames: this.anims.generateFrameNumbers(playerResource.id, { start: 0, end: 11 }), - frameRate: 8, - repeat: -1, - }); - player.setInteractive().on("pointerdown", () => { - if (this.pointerClicked) { - return; - } - if (this.currentSelectUser === i) { - return; - } - //To not trigger two time the pointerdown events : - // We set a boolean to true so that pointerdown events does nothing when the boolean is true - // We set a timer that we decrease in update function to not trigger the pointerdown events twice - this.pointerClicked = true; - this.pointerTimer = 250; - this.currentSelectUser = i; - this.moveUser(); - }); - this.players.push(player); - } - if (this.currentSelectUser >= this.players.length) { - this.currentSelectUser = 0; - } - this.selectedPlayer = this.players[this.currentSelectUser]; - this.selectedPlayer.play(this.playerModels[this.currentSelectUser].id); - } - - protected moveUser() { - for (let i = 0; i < this.players.length; i++) { - const player = this.players[i]; - this.setUpPlayer(player, i); - } - this.updateSelectedPlayer(); - } - - public moveToLeft() { - if (this.currentSelectUser === 0) { - return; - } - this.currentSelectUser -= 1; - this.moveUser(); - } - - public moveToRight() { - if (this.currentSelectUser === this.players.length - 1) { - return; - } - this.currentSelectUser += 1; - this.moveUser(); - } - - protected moveToUp() { - if (this.currentSelectUser < this.nbCharactersPerRow) { - return; - } - this.currentSelectUser -= this.nbCharactersPerRow; - this.moveUser(); - } - - protected moveToDown() { - if (this.currentSelectUser + this.nbCharactersPerRow > this.players.length - 1) { - return; - } - this.currentSelectUser += this.nbCharactersPerRow; - this.moveUser(); - } - - protected defineSetupPlayer(num: number) { - const deltaX = 32; - const deltaY = 32; - let [playerX, playerY] = this.getCharacterPosition(); // player X and player y are middle of the - - playerX = playerX - deltaX * 2.5 + deltaX * (num % this.nbCharactersPerRow); // calcul position on line users - playerY = playerY - deltaY * 2 + deltaY * Math.floor(num / this.nbCharactersPerRow); // calcul position on column users - - const playerVisible = true; - const playerScale = 1; - const playerOpacity = 1; - - // if selected - if (num === this.currentSelectUser) { - this.selectedRectangle.setX(playerX); - this.selectedRectangle.setY(playerY); - } - - return { playerX, playerY, playerScale, playerOpacity, playerVisible }; - } - - protected setUpPlayer(player: Phaser.Physics.Arcade.Sprite, num: number) { - const { playerX, playerY, playerScale, playerOpacity, playerVisible } = this.defineSetupPlayer(num); - player.setBounce(0.2); - player.setCollideWorldBounds(false); - player.setVisible(playerVisible); - player.setScale(playerScale, playerScale); - player.setAlpha(playerOpacity); - player.setX(playerX); - player.setY(playerY); - } - - /** - * Returns pixel position by on column and row number - */ - protected getCharacterPosition(): [number, number] { - return [this.game.renderer.width / 2, this.game.renderer.height / 2.5]; - } - - protected updateSelectedPlayer(): void { - this.selectedPlayer?.anims?.pause(this.selectedPlayer?.anims.currentAnim.frames[0]); - const player = this.players[this.currentSelectUser]; - player?.play(this.playerModels[this.currentSelectUser].id); - this.selectedPlayer = player; - localUserStore.setPlayerCharacterIndex(this.currentSelectUser); - } - - update(time: number, delta: number): void { - // pointerTimer is set to 250 when pointerdown events is trigger - // After 250ms, pointerClicked is set to false and the pointerdown events can be trigger again - this.pointerTimer -= delta; - if (this.pointerTimer <= 0) { - this.pointerClicked = false; - } - + public update(): void { if (this.lazyloadingAttempt) { - //re-render players list - this.createCurrentPlayer(); - this.moveUser(); this.lazyloadingAttempt = false; } } public onResize(): void { - //move position of user - this.moveUser(); + this.handleCharactersGridOnResize(); + } + + public getSelectedCollectionName(): string { + return this.collectionKeys[this.selectedCollectionIndex] ?? ""; + } + + public getCollectionKeysSize(): number { + return this.playerTextures.getCollectionsKeys().length; + } + + public selectPreviousCollection(): void { + this.selectedCollectionIndex = (this.selectedCollectionIndex + 1) % this.collectionKeys.length; + selectedCollection.set(this.getSelectedCollectionName()); + this.populateGrid(); + } + + public selectNextCollection(): void { + if (this.collectionKeys.length === 1) { + return; + } + this.selectedCollectionIndex = + this.selectedCollectionIndex - 1 < 0 ? this.collectionKeys.length - 1 : this.selectedCollectionIndex - 1; + selectedCollection.set(this.getSelectedCollectionName()); + this.populateGrid(); + } + + private handleCharactersGridOnResize(): void { + const ratio = innerHeight / innerWidth; + this.gridRowsCount = ratio > 1 || innerHeight > 900 ? 2 : 1; + const gridHeight = this.gridRowsCount === 2 ? 210 : 105; + const gridWidth = innerWidth / waScaleManager.getActualZoom(); + const gridPos = { + x: this.cameras.main.worldView.x + this.cameras.main.width / 2, + y: this.cameras.main.worldView.y + this.cameras.main.height * (ratio > 1 ? 0.5 : 0.575), + }; + + try { + this.charactersDraggableGrid.changeDraggableSpacePosAndSize( + gridPos, + { x: gridWidth, y: gridHeight }, + gridPos + ); + } catch (error) { + console.warn(error); + } + this.charactersDraggableGrid.setItemsInRow(this.gridRowsCount); + this.populateGrid(); + } + + private populateGrid(): void { + const wokaDimension = 100; + + this.selectedWoka = null; + this.charactersDraggableGrid.clearAllItems(); + const textures = this.playerTextures.getWokaCollectionTextures(this.getSelectedCollectionName()); + for (let i = 0; i < textures.length; i += 1) { + const slot = new WokaSlot(this, textures[i].id).setDisplaySize(wokaDimension, wokaDimension); + this.charactersDraggableGrid.addItem(slot); + } + this.charactersDraggableGrid.moveContentToBeginning(); + void this.charactersDraggableGrid.moveContentTo(0.5, textures.length * 50); + } + + private bindEventHandlers(): void { + this.bindKeyboardEventHandlers(); + this.events.addListener("wake", () => { + selectCharacterSceneVisibleStore.set(true); + }); + + this.input.keyboard.on("keyup-ENTER", () => { + return this.nextSceneToCameraScene(); + }); + + this.charactersDraggableGrid.on(DraggableGridEvent.ItemClicked, (item: WokaSlot) => { + this.selectGridItem(item); + }); + } + + private selectGridItem(item: WokaSlot): void { + this.selectedGridItemIndex = this.charactersDraggableGrid.getAllItems().indexOf(item); + if (this.charactersDraggableGrid.getDraggableSpaceWidth() < this.charactersDraggableGrid.getGridSize().x) { + void this.charactersDraggableGrid.centerOnItem(this.selectedGridItemIndex, 500); + } + this.charactersDraggableGrid.getAllItems().forEach((slot) => (slot as WokaSlot).select(false)); + this.selectedWoka?.stop()?.setFrame(0); + this.selectedWoka = item.getSprite(); + const wokaKey = this.selectedWoka.texture.key; + this.createWokaAnimation(wokaKey); + this.selectedWoka.play(wokaKey); + item.select(true); + } + + private bindKeyboardEventHandlers(): void { + this.input.keyboard.on("keyup-SPACE", () => { + this.selectNextCollection(); + }); + this.input.keyboard.on("keydown-LEFT", () => { + this.selectNextGridItem(true, true); + }); + this.input.keyboard.on("keydown-RIGHT", () => { + this.selectNextGridItem(false, true); + }); + this.input.keyboard.on("keydown-UP", () => { + this.selectNextGridItem(true, false); + }); + this.input.keyboard.on("keydown-DOWN", () => { + this.selectNextGridItem(false, false); + }); + this.input.keyboard.on("keydown-W", () => { + this.selectNextGridItem(true, false); + }); + this.input.keyboard.on("keydown-S", () => { + this.selectNextGridItem(false, false); + }); + this.input.keyboard.on("keydown-A", () => { + this.selectNextGridItem(true, true); + }); + this.input.keyboard.on("keydown-D", () => { + this.selectNextGridItem(false, true); + }); + } + + private selectNextGridItem(previous: boolean = false, horizontally: boolean): void { + if (this.selectedGridItemIndex === undefined) { + this.selectedGridItemIndex = 0; + } + if ( + previous + ? this.selectedGridItemIndex > 0 + : this.selectedGridItemIndex < this.charactersDraggableGrid.getAllItems().length - 1 + ) { + // NOTE: getItemsInRowCount() not working properly. Fix on lib side needed + const jump = horizontally ? this.gridRowsCount : 1; + const item = this.charactersDraggableGrid.getAllItems()[ + this.selectedGridItemIndex + (previous ? -jump : jump) + ] as WokaSlot; + if (!item) { + return; + } + this.selectedGridItemIndex += previous ? -1 : 1; + this.selectGridItem(item); + } + } + + private createWokaAnimation(key: string): void { + this.anims.create({ + key, + frames: this.anims.generateFrameNumbers(key, { start: 0, end: 11 }), + frameRate: 8, + repeat: -1, + }); } private isCustomizationAvailable(): boolean { diff --git a/front/src/Stores/SelectCharacterSceneStore.ts b/front/src/Stores/SelectCharacterSceneStore.ts index 654da03c..34ca93d4 100644 --- a/front/src/Stores/SelectCharacterSceneStore.ts +++ b/front/src/Stores/SelectCharacterSceneStore.ts @@ -1,3 +1,5 @@ import { writable } from "svelte/store"; export const customizeAvailableStore = writable(false); + +export const selectedCollection = writable(); diff --git a/front/src/index.ts b/front/src/index.ts index d45829b5..11929f8d 100644 --- a/front/src/index.ts +++ b/front/src/index.ts @@ -17,14 +17,12 @@ import { localUserStore } from "./Connexion/LocalUserStore"; import { ErrorScene } from "./Phaser/Reconnecting/ErrorScene"; import { iframeListener } from "./Api/IframeListener"; import { desktopApi } from "./Api/desktop/index"; -import { SelectCharacterMobileScene } from "./Phaser/Login/SelectCharacterMobileScene"; import { HdpiManager } from "./Phaser/Services/HdpiManager"; 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; -import { isMediaBreakpointUp } from "./Utils/BreakpointsUtils"; const { width, height } = coWebsiteManager.getGameSize(); const valueGameQuality = localUserStore.getGameQualityValue(); @@ -91,7 +89,7 @@ const config: GameConfig = { scene: [ EntryScene, LoginScene, - isMediaBreakpointUp("md") ? SelectCharacterMobileScene : SelectCharacterScene, + SelectCharacterScene, SelectCompanionScene, EnableCameraScene, ReconnectingScene, diff --git a/package.json b/package.json index 038edf2c..ff6c2a33 100644 --- a/package.json +++ b/package.json @@ -4,5 +4,6 @@ }, "scripts": { "prepare": "husky install" - } + }, + "dependencies": {} } diff --git a/tests/tests/utils/roles.ts b/tests/tests/utils/roles.ts index b4d3b8af..7e8ebe20 100644 --- a/tests/tests/utils/roles.ts +++ b/tests/tests/utils/roles.ts @@ -11,8 +11,9 @@ export async function login( await page.fill('input[name="loginSceneName"]', userName); await page.click('button.loginSceneFormSubmit'); + await page.waitForTimeout(1000); for (let i = 0; i < characterNumber; i++) { - await page.click('button.selectCharacterButtonRight'); + await page.keyboard.press('ArrowRight'); } await page.click('button.selectCharacterSceneFormSubmit');