New select woka scene (#1997)

* 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 <p.hanusiak@workadventu.re>
Co-authored-by: David Négrier <d.negrier@thecodingmachine.com>
This commit is contained in:
Piotr Hanusiak 2022-04-22 17:56:28 +02:00 committed by GitHub
parent 4ffcc3139f
commit bb3f52df37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 362 additions and 356 deletions

View File

@ -2,18 +2,19 @@
import type { Game } from "../../Phaser/Game/Game"; import type { Game } from "../../Phaser/Game/Game";
import { SelectCharacterScene, SelectCharacterSceneName } from "../../Phaser/Login/SelectCharacterScene"; import { SelectCharacterScene, SelectCharacterSceneName } from "../../Phaser/Login/SelectCharacterScene";
import LL from "../../i18n/i18n-svelte"; import LL from "../../i18n/i18n-svelte";
import { customizeAvailableStore } from "../../Stores/SelectCharacterSceneStore"; import { customizeAvailableStore, selectedCollection } from "../../Stores/SelectCharacterSceneStore";
export let game: Game; export let game: Game;
const selectCharacterScene = game.scene.getScene(SelectCharacterSceneName) as SelectCharacterScene; const selectCharacterScene = game.scene.getScene(SelectCharacterSceneName) as SelectCharacterScene;
const showArrows = selectCharacterScene.getCollectionKeysSize() > 1;
function selectLeft() { function selectLeft() {
selectCharacterScene.moveToLeft(); selectCharacterScene.selectPreviousCollection();
} }
function selectRight() { function selectRight() {
selectCharacterScene.moveToRight(); selectCharacterScene.selectNextCollection();
} }
function cameraScene() { function cameraScene() {
@ -25,83 +26,72 @@
} }
</script> </script>
<form class="selectCharacterScene"> <section class="text-center">
<section class="text-center"> <h2>{$LL.woka.selectWoka.title()}</h2>
<h2>{$LL.woka.selectWoka.title()}</h2> </section>
<button class="selectCharacterButton selectCharacterButtonLeft nes-btn" on:click|preventDefault={selectLeft}> <section class="category">
&lt; {#if showArrows}
</button> <button class="selectCharacterButton nes-btn" on:click|preventDefault={selectLeft}> &lt; </button>
<button class="selectCharacterButton selectCharacterButtonRight nes-btn" on:click|preventDefault={selectRight}> <strong class="category-text">{$selectedCollection}</strong>
&gt; <button class="selectCharacterButton nes-btn" on:click|preventDefault={selectRight}> &gt; </button>
</button> {/if}
</section> </section>
<section class="action"> <section class="action">
<button
type="submit"
class="selectCharacterSceneFormSubmit nes-btn is-primary"
on:click|preventDefault={cameraScene}>{$LL.woka.selectWoka.continue()}</button
>
{#if $customizeAvailableStore}
<button <button
type="submit" type="submit"
class="selectCharacterSceneFormSubmit nes-btn is-primary" class="selectCharacterSceneFormCustomYourOwnSubmit nes-btn"
on:click|preventDefault={cameraScene}>{$LL.woka.selectWoka.continue()}</button on:click|preventDefault={customizeScene}>{$LL.woka.selectWoka.customize()}</button
> >
{#if $customizeAvailableStore} {/if}
<button </section>
type="submit"
class="selectCharacterSceneFormCustomYourOwnSubmit nes-btn"
on:click|preventDefault={customizeScene}>{$LL.woka.selectWoka.customize()}</button
>
{/if}
</section>
</form>
<style lang="scss"> <style lang="scss">
@import "../../../style/breakpoints.scss"; @import "../../../style/breakpoints.scss";
form.selectCharacterScene { section {
font-family: "Press Start 2P";
color: #ebeeee;
margin: 5px;
&.category {
text-align: center;
margin-top: 8vh;
.category-text {
font-family: "Press Start 2P";
display: inline-block;
width: 65%;
}
}
&.action {
position: absolute;
bottom: 2vh;
width: 100%;
text-align: center;
}
h2 {
font-family: "Press Start 2P";
margin: 1px;
}
&.text-center {
text-align: center;
}
button.selectCharacterButton {
margin: 0;
}
}
button {
font-family: "Press Start 2P"; font-family: "Press Start 2P";
pointer-events: auto; pointer-events: auto;
color: #ebeeee;
section {
margin: 10px;
&.action {
text-align: center;
margin-top: 55vh;
}
h2 {
font-family: "Press Start 2P";
margin: 1px;
}
&.text-center {
text-align: center;
}
button.selectCharacterButton {
position: absolute;
top: 33vh;
margin: 0;
}
}
button {
font-family: "Press Start 2P";
&.selectCharacterButtonLeft {
left: 33vw;
}
&.selectCharacterButtonRight {
right: 33vw;
}
}
}
@include media-breakpoint-up(md) {
form.selectCharacterScene button.selectCharacterButtonLeft {
left: 5vw;
}
form.selectCharacterScene button.selectCharacterButtonRight {
right: 5vw;
}
} }
</style> </style>

View File

@ -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);
}
}

View File

@ -36,14 +36,16 @@ export enum PlayerTexturesKey {
} }
export class PlayerTextures { export class PlayerTextures {
private PLAYER_RESOURCES: BodyResourceDescriptionListInterface = {}; private wokaResources: BodyResourceDescriptionListInterface = {};
private COLOR_RESOURCES: BodyResourceDescriptionListInterface = {}; private colorResources: BodyResourceDescriptionListInterface = {};
private EYES_RESOURCES: BodyResourceDescriptionListInterface = {}; private eyesResources: BodyResourceDescriptionListInterface = {};
private HAIR_RESOURCES: BodyResourceDescriptionListInterface = {}; private hairResources: BodyResourceDescriptionListInterface = {};
private CLOTHES_RESOURCES: BodyResourceDescriptionListInterface = {}; private clothesResources: BodyResourceDescriptionListInterface = {};
private HATS_RESOURCES: BodyResourceDescriptionListInterface = {}; private hatsResources: BodyResourceDescriptionListInterface = {};
private ACCESSORIES_RESOURCES: BodyResourceDescriptionListInterface = {}; private accessoriesResources: BodyResourceDescriptionListInterface = {};
private LAYERS: BodyResourceDescriptionListInterface[] = []; private layers: BodyResourceDescriptionListInterface[] = [];
private wokaCollections = new Map<string, BodyResourceDescriptionInterface[]>();
public loadPlayerTexturesMetadata(metadata: WokaList): void { public loadPlayerTexturesMetadata(metadata: WokaList): void {
this.mapTexturesMetadataIntoResources(metadata); this.mapTexturesMetadataIntoResources(metadata);
@ -52,43 +54,53 @@ export class PlayerTextures {
public getTexturesResources(key: PlayerTexturesKey): BodyResourceDescriptionListInterface { public getTexturesResources(key: PlayerTexturesKey): BodyResourceDescriptionListInterface {
switch (key) { switch (key) {
case PlayerTexturesKey.Accessory: case PlayerTexturesKey.Accessory:
return this.ACCESSORIES_RESOURCES; return this.accessoriesResources;
case PlayerTexturesKey.Body: case PlayerTexturesKey.Body:
return this.COLOR_RESOURCES; return this.colorResources;
case PlayerTexturesKey.Clothes: case PlayerTexturesKey.Clothes:
return this.CLOTHES_RESOURCES; return this.clothesResources;
case PlayerTexturesKey.Eyes: case PlayerTexturesKey.Eyes:
return this.EYES_RESOURCES; return this.eyesResources;
case PlayerTexturesKey.Hair: case PlayerTexturesKey.Hair:
return this.HAIR_RESOURCES; return this.hairResources;
case PlayerTexturesKey.Hat: case PlayerTexturesKey.Hat:
return this.HATS_RESOURCES; return this.hatsResources;
case PlayerTexturesKey.Woka: case PlayerTexturesKey.Woka:
return this.PLAYER_RESOURCES; return this.wokaResources;
} }
} }
public getLayers(): BodyResourceDescriptionListInterface[] { 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 { private mapTexturesMetadataIntoResources(metadata: WokaList): void {
this.PLAYER_RESOURCES = this.getMappedResources(metadata.woka); this.wokaResources = this.getMappedResources(metadata.woka);
this.COLOR_RESOURCES = this.getMappedResources(metadata.body); this.colorResources = this.getMappedResources(metadata.body);
this.EYES_RESOURCES = this.getMappedResources(metadata.eyes); this.eyesResources = this.getMappedResources(metadata.eyes);
this.HAIR_RESOURCES = this.getMappedResources(metadata.hair); this.hairResources = this.getMappedResources(metadata.hair);
this.CLOTHES_RESOURCES = this.getMappedResources(metadata.clothes); this.clothesResources = this.getMappedResources(metadata.clothes);
this.HATS_RESOURCES = this.getMappedResources(metadata.hat); this.hatsResources = this.getMappedResources(metadata.hat);
this.ACCESSORIES_RESOURCES = this.getMappedResources(metadata.accessory); this.accessoriesResources = this.getMappedResources(metadata.accessory);
this.LAYERS = [ this.layers = [
this.COLOR_RESOURCES, this.colorResources,
this.EYES_RESOURCES, this.eyesResources,
this.HAIR_RESOURCES, this.hairResources,
this.CLOTHES_RESOURCES, this.clothesResources,
this.HATS_RESOURCES, this.hatsResources,
this.ACCESSORIES_RESOURCES, this.accessoriesResources,
]; ];
this.mapWokaCollections(metadata.woka);
} }
private getMappedResources(category: WokaPartType): BodyResourceDescriptionListInterface { private getMappedResources(category: WokaPartType): BodyResourceDescriptionListInterface {
@ -103,6 +115,19 @@ export class PlayerTextures {
} }
return resources; 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[] = [ export const OBJECTS: BodyResourceDescriptionInterface[] = [

View File

@ -11,4 +11,8 @@ export abstract class AbstractCharacterScene extends ResizableScene {
this.playerTextures = new PlayerTextures(); this.playerTextures = new PlayerTextures();
this.superLoad = new SuperLoaderPlugin(this); this.superLoad = new SuperLoaderPlugin(this);
} }
preload() {
this.input.dragDistanceThreshold = 10;
}
} }

View File

@ -54,7 +54,7 @@ export class CustomizeScene extends AbstractCharacterScene {
} }
public preload(): void { public preload(): void {
this.input.dragDistanceThreshold = 10; super.preload();
this.load.image("iconClothes", "/resources/icons/icon_clothes.png"); this.load.image("iconClothes", "/resources/icons/icon_clothes.png");
this.load.image("iconAccessory", "/resources/icons/icon_accessory.png"); this.load.image("iconAccessory", "/resources/icons/icon_accessory.png");

View File

@ -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];
}
}

View File

@ -1,5 +1,4 @@
import { gameManager } from "../Game/GameManager"; import { gameManager } from "../Game/GameManager";
import Rectangle = Phaser.GameObjects.Rectangle;
import { EnableCameraSceneName } from "./EnableCameraScene"; import { EnableCameraSceneName } from "./EnableCameraScene";
import { CustomizeSceneName } from "./CustomizeScene"; import { CustomizeSceneName } from "./CustomizeScene";
import { localUserStore } from "../../Connexion/LocalUserStore"; import { localUserStore } from "../../Connexion/LocalUserStore";
@ -13,25 +12,25 @@ import { PinchManager } from "../UserInput/PinchManager";
import { selectCharacterSceneVisibleStore } from "../../Stores/SelectCharacterStore"; import { selectCharacterSceneVisibleStore } from "../../Stores/SelectCharacterStore";
import { waScaleManager } from "../Services/WaScaleManager"; import { waScaleManager } from "../Services/WaScaleManager";
import { analyticsClient } from "../../Administration/AnalyticsClient"; import { analyticsClient } from "../../Administration/AnalyticsClient";
import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils";
import { PUSHER_URL } from "../../Enum/EnvironmentVariable"; 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"; import { wokaList } from "../../Messages/JsonMessages/PlayerTextures";
//todo: put this constants in a dedicated file //todo: put this constants in a dedicated file
export const SelectCharacterSceneName = "SelectCharacterScene"; export const SelectCharacterSceneName = "SelectCharacterScene";
export class SelectCharacterScene extends AbstractCharacterScene { export class SelectCharacterScene extends AbstractCharacterScene {
protected readonly nbCharactersPerRow = 6; protected selectedWoka!: Phaser.GameObjects.Sprite | null; // null if we are selecting the "customize" option
protected selectedPlayer!: Phaser.Physics.Arcade.Sprite | null; // null if we are selecting the "customize" option
protected players: Array<Phaser.Physics.Arcade.Sprite> = new Array<Phaser.Physics.Arcade.Sprite>();
protected playerModels!: BodyResourceDescriptionInterface[]; protected playerModels!: BodyResourceDescriptionInterface[];
protected selectedRectangle!: Rectangle; private charactersDraggableGrid!: DraggableGrid;
private collectionKeys!: string[];
protected currentSelectUser = 0; private selectedCollectionIndex!: number;
protected pointerClicked: boolean = false; private selectedGridItemIndex?: number;
protected pointerTimer: number = 0; private gridRowsCount: number = 1;
protected lazyloadingAttempt = true; //permit to update texture loaded after renderer protected lazyloadingAttempt = true; //permit to update texture loaded after renderer
private loader: Loader; private loader: Loader;
@ -44,7 +43,8 @@ export class SelectCharacterScene extends AbstractCharacterScene {
this.playerTextures = new PlayerTextures(); this.playerTextures = new PlayerTextures();
} }
preload() { public preload() {
super.preload();
const wokaMetadataKey = "woka-list" + gameManager.currentStartedRoom.href; const wokaMetadataKey = "woka-list" + gameManager.currentStartedRoom.href;
this.cache.json.remove(wokaMetadataKey); this.cache.json.remove(wokaMetadataKey);
@ -67,228 +67,236 @@ export class SelectCharacterScene extends AbstractCharacterScene {
} }
) )
.catch((e) => console.error(e)); .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 function must stay at the end of preload function
this.loader.addLoader(); 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()); customizeAvailableStore.set(this.isCustomizationAvailable());
selectCharacterSceneVisibleStore.set(true); selectCharacterSceneVisibleStore.set(true);
this.events.addListener("wake", () => {
waScaleManager.saveZoom();
waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 2 : 1;
selectCharacterSceneVisibleStore.set(true);
});
if (touchScreenManager.supportTouchScreen) { if (touchScreenManager.supportTouchScreen) {
new PinchManager(this); new PinchManager(this);
} }
waScaleManager.saveZoom(); this.charactersDraggableGrid = new DraggableGrid(this, {
waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 2 : 1; position: { x: 0, y: 0 },
maskPosition: { x: 0, y: 0 },
const rectangleXStart = this.game.renderer.width / 2 - (this.nbCharactersPerRow / 2) * 32 + 16; dimension: { x: 485, y: 165 },
this.selectedRectangle = this.add.rectangle(rectangleXStart, 90, 32, 32).setStrokeStyle(2, 0xffffff); horizontal: true,
this.selectedRectangle.setDepth(2); repositionToCenter: true,
itemsInRow: 1,
/*create user*/ margin: {
this.createCurrentPlayer(); left: ((innerWidth - 200) / waScaleManager.getActualZoom()) * 0.5,
right: ((innerWidth - 200) / waScaleManager.getActualZoom()) * 0.5,
this.input.keyboard.on("keyup-ENTER", () => { },
return this.nextSceneToCameraScene(); spacing: 5,
debug: {
showDraggableSpace: false,
},
}); });
this.input.keyboard.on("keydown-RIGHT", () => { this.bindEventHandlers();
this.moveToRight();
}); this.onResize();
this.input.keyboard.on("keydown-LEFT", () => {
this.moveToLeft();
});
this.input.keyboard.on("keydown-UP", () => {
this.moveToUp();
});
this.input.keyboard.on("keydown-DOWN", () => {
this.moveToDown();
});
} }
public nextSceneToCameraScene(): void { public nextSceneToCameraScene(): void {
if (this.selectedPlayer !== null && !areCharacterLayersValid([this.selectedPlayer.texture.key])) { if (this.selectedWoka !== null && !areCharacterLayersValid([this.selectedWoka.texture.key])) {
return; return;
} }
if (!this.selectedPlayer) { if (!this.selectedWoka) {
return; return;
} }
analyticsClient.validationWoka("SelectWoka"); analyticsClient.validationWoka("SelectWoka");
gameManager.setCharacterLayers([this.selectedWoka.texture.key]);
this.selectedWoka = null;
this.scene.stop(SelectCharacterSceneName); this.scene.stop(SelectCharacterSceneName);
waScaleManager.restoreZoom();
gameManager.setCharacterLayers([this.selectedPlayer.texture.key]);
gameManager.tryResumingGame(EnableCameraSceneName); gameManager.tryResumingGame(EnableCameraSceneName);
this.players = [];
selectCharacterSceneVisibleStore.set(false); selectCharacterSceneVisibleStore.set(false);
this.events.removeListener("wake"); this.events.removeListener("wake");
} }
public nextSceneToCustomizeScene(): void { public nextSceneToCustomizeScene(): void {
if (this.selectedPlayer !== null && !areCharacterLayersValid([this.selectedPlayer.texture.key])) { if (this.selectedWoka !== null && !areCharacterLayersValid([this.selectedWoka.texture.key])) {
return; return;
} }
this.selectedWoka = null;
this.scene.sleep(SelectCharacterSceneName); this.scene.sleep(SelectCharacterSceneName);
waScaleManager.restoreZoom();
this.scene.run(CustomizeSceneName); this.scene.run(CustomizeSceneName);
selectCharacterSceneVisibleStore.set(false); selectCharacterSceneVisibleStore.set(false);
} }
createCurrentPlayer(): void { public update(): 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;
}
if (this.lazyloadingAttempt) { if (this.lazyloadingAttempt) {
//re-render players list
this.createCurrentPlayer();
this.moveUser();
this.lazyloadingAttempt = false; this.lazyloadingAttempt = false;
} }
} }
public onResize(): void { public onResize(): void {
//move position of user this.handleCharactersGridOnResize();
this.moveUser(); }
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 { private isCustomizationAvailable(): boolean {

View File

@ -1,3 +1,5 @@
import { writable } from "svelte/store"; import { writable } from "svelte/store";
export const customizeAvailableStore = writable(false); export const customizeAvailableStore = writable(false);
export const selectedCollection = writable<string>();

View File

@ -17,14 +17,12 @@ import { localUserStore } from "./Connexion/LocalUserStore";
import { ErrorScene } from "./Phaser/Reconnecting/ErrorScene"; import { ErrorScene } from "./Phaser/Reconnecting/ErrorScene";
import { iframeListener } from "./Api/IframeListener"; import { iframeListener } from "./Api/IframeListener";
import { desktopApi } from "./Api/desktop/index"; import { desktopApi } from "./Api/desktop/index";
import { SelectCharacterMobileScene } from "./Phaser/Login/SelectCharacterMobileScene";
import { HdpiManager } from "./Phaser/Services/HdpiManager"; import { HdpiManager } from "./Phaser/Services/HdpiManager";
import { waScaleManager } from "./Phaser/Services/WaScaleManager"; import { waScaleManager } from "./Phaser/Services/WaScaleManager";
import { Game } from "./Phaser/Game/Game"; import { Game } from "./Phaser/Game/Game";
import App from "./Components/App.svelte"; import App from "./Components/App.svelte";
import { HtmlUtils } from "./WebRtc/HtmlUtils"; import { HtmlUtils } from "./WebRtc/HtmlUtils";
import WebGLRenderer = Phaser.Renderer.WebGL.WebGLRenderer; import WebGLRenderer = Phaser.Renderer.WebGL.WebGLRenderer;
import { isMediaBreakpointUp } from "./Utils/BreakpointsUtils";
const { width, height } = coWebsiteManager.getGameSize(); const { width, height } = coWebsiteManager.getGameSize();
const valueGameQuality = localUserStore.getGameQualityValue(); const valueGameQuality = localUserStore.getGameQualityValue();
@ -91,7 +89,7 @@ const config: GameConfig = {
scene: [ scene: [
EntryScene, EntryScene,
LoginScene, LoginScene,
isMediaBreakpointUp("md") ? SelectCharacterMobileScene : SelectCharacterScene, SelectCharacterScene,
SelectCompanionScene, SelectCompanionScene,
EnableCameraScene, EnableCameraScene,
ReconnectingScene, ReconnectingScene,

View File

@ -4,5 +4,6 @@
}, },
"scripts": { "scripts": {
"prepare": "husky install" "prepare": "husky install"
} },
"dependencies": {}
} }

View File

@ -11,8 +11,9 @@ export async function login(
await page.fill('input[name="loginSceneName"]', userName); await page.fill('input[name="loginSceneName"]', userName);
await page.click('button.loginSceneFormSubmit'); await page.click('button.loginSceneFormSubmit');
await page.waitForTimeout(1000);
for (let i = 0; i < characterNumber; i++) { for (let i = 0; i < characterNumber; i++) {
await page.click('button.selectCharacterButtonRight'); await page.keyboard.press('ArrowRight');
} }
await page.click('button.selectCharacterSceneFormSubmit'); await page.click('button.selectCharacterSceneFormSubmit');