Files
partey_workadventure/front/src/Phaser/Login/CustomizeScene.ts
T
2022-04-04 17:53:28 +02:00

671 lines
25 KiB
TypeScript

import { EnableCameraSceneName } from "./EnableCameraScene";
import { loadAllLayers } from "../Entity/PlayerTexturesLoadingManager";
import { gameManager } from "../Game/GameManager";
import { localUserStore } from "../../Connexion/LocalUserStore";
import { Loader } from "../Components/Loader";
import type { BodyResourceDescriptionInterface } from "../Entity/PlayerTextures";
import { AbstractCharacterScene } from "./AbstractCharacterScene";
import { areCharacterLayersValid } from "../../Connexion/LocalUser";
import { SelectCharacterSceneName } from "./SelectCharacterScene";
import { waScaleManager } from "../Services/WaScaleManager";
import { analyticsClient } from "../../Administration/AnalyticsClient";
import { PUSHER_URL } from "../../Enum/EnvironmentVariable";
import {
CustomWokaBodyPart,
CustomWokaBodyPartOrder,
CustomWokaPreviewer,
CustomWokaPreviewerConfig,
} from "../Components/CustomizeWoka/CustomWokaPreviewer";
import { DraggableGrid } from "@home-based-studio/phaser3-utils";
import { WokaBodyPartSlot, WokaBodyPartSlotConfig } from "../Components/CustomizeWoka/WokaBodyPartSlot";
import { DraggableGridEvent } from "@home-based-studio/phaser3-utils/lib/utils/gui/containers/grids/DraggableGrid";
import { Button } from "../Components/Ui/Button";
import { wokaList } from "../../Messages/JsonMessages/PlayerTextures";
import { TexturesHelper } from "../Helpers/TexturesHelper";
import { IconButton, IconButtonConfig, IconButtonEvent } from "../Components/Ui/IconButton";
export const CustomizeSceneName = "CustomizeScene";
export class CustomizeScene extends AbstractCharacterScene {
private customWokaPreviewer!: CustomWokaPreviewer;
private bodyPartsDraggableGridLeftShadow!: Phaser.GameObjects.Image;
private bodyPartsDraggableGridRightShadow!: Phaser.GameObjects.Image;
private bodyPartsDraggableGrid!: DraggableGrid;
private bodyPartsButtons!: Record<CustomWokaBodyPart, IconButton>;
private randomizeButton!: Button;
private finishButton!: Button;
private selectedLayers: number[] = [0, 0, 0, 0, 0, 0];
private layers: BodyResourceDescriptionInterface[][] = [];
private selectedBodyPartType?: CustomWokaBodyPart;
protected lazyloadingAttempt = true; //permit to update texture loaded after renderer
private loader: Loader;
private readonly SLOT_DIMENSION = 100;
constructor() {
super({
key: CustomizeSceneName,
});
this.loader = new Loader(this);
}
public preload(): void {
this.input.dragDistanceThreshold = 10;
this.load.image("iconClothes", "/resources/icons/icon_clothes.png");
this.load.image("iconAccessory", "/resources/icons/icon_accessory.png");
this.load.image("iconHat", "/resources/icons/icon_hat.png");
this.load.image("iconHair", "/resources/icons/icon_hair.png");
this.load.image("iconEyes", "/resources/icons/icon_eyes.png");
this.load.image("iconBody", "/resources/icons/icon_body.png");
this.load.image("iconTurn", "/resources/icons/icon_turn.png");
this.load.spritesheet("floorTiles", "/resources/tilesets/floor_tiles.png", { frameWidth: 32, frameHeight: 32 });
TexturesHelper.createRectangleTexture(this, "gridEdgeShadow", this.cameras.main.width * 0.2, 115, 0x000000);
const wokaMetadataKey = "woka-list" + gameManager.currentStartedRoom.href;
this.cache.json.remove(wokaMetadataKey);
this.superLoad
.json(
wokaMetadataKey,
`${PUSHER_URL}/woka/list?roomUrl=` + encodeURIComponent(gameManager.currentStartedRoom.href),
undefined,
{
responseType: "text",
headers: {
Authorization: localUserStore.getAuthToken() ?? "",
},
withCredentials: true,
},
(key, type, data) => {
this.playerTextures.loadPlayerTexturesMetadata(wokaList.parse(data));
this.layers = loadAllLayers(this.load, this.playerTextures);
this.lazyloadingAttempt = false;
}
)
.catch((e) => console.error(e));
//this function must stay at the end of preload function
this.loader.addLoader();
}
public create(): void {
waScaleManager.zoomModifier = 1;
this.createSlotBackgroundTextures();
this.initializeCustomWokaPreviewer();
this.initializeBodyPartsDraggableGrid();
this.initializeEdgeShadows();
this.initializeBodyPartsButtons();
this.initializeRandomizeButton();
this.initializeFinishButton();
this.selectedBodyPartType = CustomWokaBodyPart.Body;
this.bodyPartsButtons.Body.select();
this.bindEventHandlers();
this.refreshPlayerCurrentOutfit();
this.onResize();
}
public update(time: number, dt: number): void {
this.customWokaPreviewer.update();
}
public onResize(): void {
this.handleCustomWokaPreviewerOnResize();
this.handleBodyPartButtonsOnResize();
this.handleRandomizeButtonOnResize();
this.handleFinishButtonOnResize();
this.handleBodyPartsDraggableGridOnResize();
}
public nextSceneToCamera() {
const layers: string[] = [];
let i = 0;
for (const layerItem of this.selectedLayers) {
if (layerItem !== undefined) {
layers.push(this.layers[i][layerItem].id);
}
i++;
}
if (!areCharacterLayersValid(layers)) {
return;
}
analyticsClient.validationWoka("CustomizeWoka");
gameManager.setCharacterLayers(layers);
this.scene.stop(CustomizeSceneName);
gameManager.tryResumingGame(EnableCameraSceneName);
}
public backToPreviousScene() {
this.scene.stop(CustomizeSceneName);
this.scene.run(SelectCharacterSceneName);
}
private createSlotBackgroundTextures(): void {
for (let i = 0; i < 4; i += 1) {
if (this.textures.getTextureKeys().includes(`floorTexture${i}`)) {
continue;
}
TexturesHelper.createFloorRectangleTexture(this, `floorTexture${i}`, 50, 50, "floorTiles", i);
}
}
private initializeCustomWokaPreviewer(): void {
this.customWokaPreviewer = new CustomWokaPreviewer(
this,
0,
0,
this.getCustomWokaPreviewerConfig()
).setDisplaySize(200, 200);
}
private initializeBodyPartsDraggableGrid(): void {
this.bodyPartsDraggableGrid = 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 / waScaleManager.getActualZoom() - this.SLOT_DIMENSION) * 0.5,
right: (innerWidth / waScaleManager.getActualZoom() - this.SLOT_DIMENSION) * 0.5,
},
spacing: 5,
debug: {
showDraggableSpace: false,
},
});
}
private initializeEdgeShadows(): void {
this.bodyPartsDraggableGridLeftShadow = this.add
.image(0, this.cameras.main.worldView.y + this.cameras.main.height, "gridEdgeShadow")
.setAlpha(1, 0, 1, 0)
.setOrigin(0, 0.5);
this.bodyPartsDraggableGridRightShadow = this.add
.image(
this.cameras.main.worldView.x + this.cameras.main.width,
this.cameras.main.worldView.y + this.cameras.main.height,
"gridEdgeShadow"
)
.setAlpha(1, 0, 1, 0)
.setFlipX(true)
.setOrigin(1, 0.5);
}
private initializeBodyPartsButtons(): void {
this.bodyPartsButtons = {
[CustomWokaBodyPart.Accessory]: new IconButton(
this,
0,
0,
this.getDefaultIconButtonConfig("iconAccessory")
),
[CustomWokaBodyPart.Body]: new IconButton(this, 0, 0, this.getDefaultIconButtonConfig("iconBody")),
[CustomWokaBodyPart.Clothes]: new IconButton(this, 0, 0, this.getDefaultIconButtonConfig("iconClothes")),
[CustomWokaBodyPart.Eyes]: new IconButton(this, 0, 0, this.getDefaultIconButtonConfig("iconEyes")),
[CustomWokaBodyPart.Hair]: new IconButton(this, 0, 0, this.getDefaultIconButtonConfig("iconHair")),
[CustomWokaBodyPart.Hat]: new IconButton(this, 0, 0, this.getDefaultIconButtonConfig("iconHat")),
};
}
private getDefaultIconButtonConfig(iconTextureKey: string): IconButtonConfig {
return {
iconTextureKey,
width: 25,
height: 25,
idle: {
color: 0xffffff,
borderThickness: 3,
borderColor: 0xe7e7e7,
},
hover: {
color: 0xe7e7e7,
borderThickness: 3,
borderColor: 0xadafbc,
},
pressed: {
color: 0xadafbc,
borderThickness: 3,
borderColor: 0xadafbc,
},
selected: {
color: 0xadafbc,
borderThickness: 3,
borderColor: 0x209cee,
},
};
}
private initializeRandomizeButton(): void {
this.randomizeButton = new Button(this, 50, 50, {
width: 95,
height: 50,
idle: {
color: 0xffffff,
textColor: "#000000",
borderThickness: 3,
borderColor: 0xe7e7e7,
},
hover: {
color: 0xe7e7e7,
textColor: "#000000",
borderThickness: 3,
borderColor: 0xadafbc,
},
pressed: {
color: 0xadafbc,
textColor: "#000000",
borderThickness: 3,
borderColor: 0xadafbc,
},
});
this.randomizeButton.setText("Randomize");
}
private initializeFinishButton(): void {
this.finishButton = new Button(this, 50, 50, {
width: 95,
height: 50,
idle: {
color: 0x209cee,
textColor: "#ffffff",
borderThickness: 3,
borderColor: 0x006bb3,
},
hover: {
color: 0x0987db,
textColor: "#ffffff",
borderThickness: 3,
borderColor: 0x006bb3,
},
pressed: {
color: 0x006bb3,
textColor: "#ffffff",
borderThickness: 3,
borderColor: 0x006bb3,
},
});
this.finishButton.setText("Finish");
}
private refreshPlayerCurrentOutfit(): void {
let i = 0;
for (const layerItem of this.selectedLayers) {
const bodyPart = CustomWokaBodyPart[CustomWokaBodyPartOrder[i] as CustomWokaBodyPart];
this.customWokaPreviewer.updateSprite(this.layers[i][layerItem].id, bodyPart);
i += 1;
}
}
private getCurrentlySelectedWokaTexturesRecord(): Record<CustomWokaBodyPart, string> {
return {
[CustomWokaBodyPart.Accessory]:
this.layers[CustomWokaBodyPartOrder.Accessory][this.selectedLayers[CustomWokaBodyPartOrder.Accessory]]
.id,
[CustomWokaBodyPart.Body]:
this.layers[CustomWokaBodyPartOrder.Body][this.selectedLayers[CustomWokaBodyPartOrder.Body]].id,
[CustomWokaBodyPart.Clothes]:
this.layers[CustomWokaBodyPartOrder.Clothes][this.selectedLayers[CustomWokaBodyPartOrder.Clothes]].id,
[CustomWokaBodyPart.Eyes]:
this.layers[CustomWokaBodyPartOrder.Eyes][this.selectedLayers[CustomWokaBodyPartOrder.Eyes]].id,
[CustomWokaBodyPart.Hair]:
this.layers[CustomWokaBodyPartOrder.Hair][this.selectedLayers[CustomWokaBodyPartOrder.Hair]].id,
[CustomWokaBodyPart.Hat]:
this.layers[CustomWokaBodyPartOrder.Hat][this.selectedLayers[CustomWokaBodyPartOrder.Hat]].id,
};
}
private handleCustomWokaPreviewerOnResize(): void {
this.customWokaPreviewer.x = this.cameras.main.worldView.x + this.cameras.main.width / 2;
this.customWokaPreviewer.y = this.customWokaPreviewer.displayHeight * 0.5 + 10;
}
private handleBodyPartButtonsOnResize(): void {
const ratio = innerHeight / innerWidth;
const slotDimension = 50;
for (const part in this.bodyPartsButtons) {
this.bodyPartsButtons[part as CustomWokaBodyPart].setDisplaySize(slotDimension, slotDimension);
}
const slotSize = this.bodyPartsButtons.Accessory.displayHeight;
if (ratio > 1.6) {
const middle = Math.floor(this.customWokaPreviewer.x);
const left = Math.floor(middle - slotSize - 23);
const right = Math.floor(middle + slotSize + 23);
const top = Math.floor(
this.customWokaPreviewer.y + this.customWokaPreviewer.displayHeight * 0.5 + slotSize * 1.5 + 30
);
const bottom = Math.floor(top + slotSize + 23);
this.bodyPartsButtons.Body.setPosition(left, top);
this.bodyPartsButtons.Eyes.setPosition(middle, top);
this.bodyPartsButtons.Hair.setPosition(right, top);
this.bodyPartsButtons.Clothes.setPosition(left, bottom);
this.bodyPartsButtons.Hat.setPosition(middle, bottom);
this.bodyPartsButtons.Accessory.setPosition(right, bottom);
return;
}
const left = Math.floor(
this.customWokaPreviewer.x - this.customWokaPreviewer.displayWidth * 0.5 - slotSize * 0.5 - 24
);
const right = Math.floor(
this.customWokaPreviewer.x + this.customWokaPreviewer.displayWidth * 0.5 + slotSize * 0.5 + 24
);
const top = Math.floor(0 + slotSize * 0.5 + 11);
const middle = Math.floor(top + slotSize + 24);
const bottom = Math.floor(middle + slotSize + 24);
this.bodyPartsButtons.Body.setPosition(left, top);
this.bodyPartsButtons.Eyes.setPosition(left, middle);
this.bodyPartsButtons.Hair.setPosition(left, bottom);
this.bodyPartsButtons.Clothes.setPosition(right, top);
this.bodyPartsButtons.Hat.setPosition(right, middle);
this.bodyPartsButtons.Accessory.setPosition(right, bottom);
}
private handleBodyPartsDraggableGridOnResize(): void {
const gridHeight = 110;
const gridWidth = innerWidth / waScaleManager.getActualZoom();
const gridTopMargin = Math.max(
this.finishButton.y + this.finishButton.displayHeight * 0.5,
this.bodyPartsButtons.Hair.y + this.bodyPartsButtons.Hair.displayHeight * 0.5
);
const gridBottomMargin = this.cameras.main.worldView.y + this.cameras.main.height;
const yPos = gridTopMargin + (gridBottomMargin - gridTopMargin) * 0.5;
const gridPos = {
x: this.cameras.main.worldView.x + this.cameras.main.width / 2,
y: yPos,
};
this.bodyPartsDraggableGridLeftShadow.setPosition(0, yPos);
this.bodyPartsDraggableGridRightShadow.setPosition(
this.cameras.main.worldView.x + this.cameras.main.width,
yPos
);
try {
this.bodyPartsDraggableGrid.changeDraggableSpacePosAndSize(
gridPos,
{ x: gridWidth, y: gridHeight },
gridPos
);
} catch (error) {
console.warn(error);
}
this.populateGrid();
const selectedGridItem = this.selectGridItem();
if (selectedGridItem) {
this.centerGridOnItem(selectedGridItem);
}
}
private handleRandomizeButtonOnResize(): void {
const x =
this.customWokaPreviewer.x +
(this.customWokaPreviewer.displayWidth - this.randomizeButton.displayWidth) * 0.5;
const y =
this.customWokaPreviewer.y +
(this.customWokaPreviewer.displayHeight + this.randomizeButton.displayHeight) * 0.5 +
10;
this.randomizeButton.setPosition(x, y);
}
private handleFinishButtonOnResize(): void {
const x =
this.customWokaPreviewer.x -
(this.customWokaPreviewer.displayWidth - this.randomizeButton.displayWidth) * 0.5;
const y =
this.customWokaPreviewer.y +
(this.customWokaPreviewer.displayHeight + this.randomizeButton.displayHeight) * 0.5 +
10;
this.finishButton.setPosition(x, y);
}
private getCustomWokaPreviewerConfig(): CustomWokaPreviewerConfig {
return {
color: 0xffffff,
borderThickness: 1,
borderColor: 0xadafbc,
bodyPartsOffsetX: -1,
};
}
private getWokaBodyPartSlotConfig(bodyPart?: CustomWokaBodyPart, newTextureKey?: string): WokaBodyPartSlotConfig {
const textures = this.getCurrentlySelectedWokaTexturesRecord();
if (bodyPart && newTextureKey) {
textures[bodyPart] = newTextureKey;
}
return {
color: 0xffffff,
borderThickness: 1,
borderColor: 0xadafbc,
borderSelectedColor: 0x209cee,
textureKeys: textures,
offsetX: -4,
offsetY: 2,
};
}
private bindEventHandlers(): void {
this.bindKeyboardEventHandlers();
this.randomizeButton.on(Phaser.Input.Events.POINTER_UP, () => {
this.randomizeOutfit();
this.clearGrid();
this.deselectAllButtons();
this.refreshPlayerCurrentOutfit();
});
this.finishButton.on(Phaser.Input.Events.POINTER_UP, () => {
this.nextSceneToCamera();
});
for (const bodyPart in CustomWokaBodyPart) {
const button = this.bodyPartsButtons[bodyPart as CustomWokaBodyPart];
button.on(IconButtonEvent.Clicked, (selected: boolean) => {
if (!selected) {
this.selectBodyPartType(bodyPart as CustomWokaBodyPart);
}
});
}
this.bodyPartsDraggableGrid.on(DraggableGridEvent.ItemClicked, (item: WokaBodyPartSlot) => {
void this.bodyPartsDraggableGrid.centerOnItem(this.bodyPartsDraggableGrid.getAllItems().indexOf(item), 500);
this.deselectAllSlots();
item.select(true);
this.setNewBodyPart(Number(item.getId()));
});
}
private bindKeyboardEventHandlers(): void {
this.input.keyboard.on("keyup-ENTER", () => {
this.nextSceneToCamera();
});
this.input.keyboard.on("keyup-BACKSPACE", () => {
this.backToPreviousScene();
});
this.input.keyboard.on("keydown-LEFT", () => {
this.selectNextGridItem(true);
});
this.input.keyboard.on("keydown-RIGHT", () => {
this.selectNextGridItem();
});
this.input.keyboard.on("keydown-UP", () => {
this.selectNextCategory(true);
});
this.input.keyboard.on("keydown-DOWN", () => {
this.selectNextCategory();
});
this.input.keyboard.on("keydown-W", () => {
this.selectNextCategory(true);
});
this.input.keyboard.on("keydown-S", () => {
this.selectNextCategory();
});
this.input.keyboard.on("keydown-A", () => {
this.selectNextGridItem(true);
});
this.input.keyboard.on("keydown-D", () => {
this.selectNextGridItem();
});
}
private selectBodyPartType(bodyPart: CustomWokaBodyPart): void {
this.selectedBodyPartType = bodyPart;
this.deselectAllButtons();
const button = this.bodyPartsButtons[bodyPart];
button.select(true);
this.populateGrid();
const selectedGridItem = this.selectGridItem();
if (!selectedGridItem) {
return;
}
this.bodyPartsDraggableGrid.moveContentToBeginning();
this.centerGridOnItem(selectedGridItem);
}
private setNewBodyPart(bodyPartIndex: number) {
this.changeOutfitPart(bodyPartIndex);
this.refreshPlayerCurrentOutfit();
}
private selectGridItem(): WokaBodyPartSlot | undefined {
const bodyPartType = this.selectedBodyPartType;
if (!bodyPartType) {
return;
}
const items = this.bodyPartsDraggableGrid.getAllItems() as WokaBodyPartSlot[];
const item = items.find(
(item) => item.getContentData()[bodyPartType] === this.getBodyPartSelectedItemId(bodyPartType)
);
item?.select();
return item;
}
private getBodyPartSelectedItemId(bodyPartType: CustomWokaBodyPart): string {
const categoryIndex = CustomWokaBodyPartOrder[bodyPartType];
return this.layers[categoryIndex][this.selectedLayers[categoryIndex]].id;
}
private selectNextGridItem(previous: boolean = false): void {
if (!this.selectedBodyPartType) {
return;
}
const currentIndex = this.getCurrentlySelectedItemIndex();
if (previous ? currentIndex > 0 : currentIndex < this.bodyPartsDraggableGrid.getAllItems().length - 1) {
this.deselectAllSlots();
const item = this.bodyPartsDraggableGrid.getAllItems()[
currentIndex + (previous ? -1 : 1)
] as WokaBodyPartSlot;
if (item) {
item.select();
this.setNewBodyPart(Number(item.getId()));
this.centerGridOnItem(item);
}
}
}
private selectNextCategory(previous: boolean = false): void {
if (!this.selectedBodyPartType) {
this.selectBodyPartType(CustomWokaBodyPart.Body);
return;
}
if (previous && this.selectedBodyPartType === CustomWokaBodyPart.Body) {
return;
}
if (!previous && this.selectedBodyPartType === CustomWokaBodyPart.Accessory) {
return;
}
const index = CustomWokaBodyPartOrder[this.selectedBodyPartType] + (previous ? -1 : 1);
this.selectBodyPartType(CustomWokaBodyPart[CustomWokaBodyPartOrder[index] as CustomWokaBodyPart]);
}
private getCurrentlySelectedItemIndex(): number {
const bodyPartType = this.selectedBodyPartType;
if (!bodyPartType) {
return -1;
}
const items = this.bodyPartsDraggableGrid.getAllItems() as WokaBodyPartSlot[];
return items.findIndex(
(item) => item.getContentData()[bodyPartType] === this.getBodyPartSelectedItemId(bodyPartType)
);
}
private centerGridOnItem(item: WokaBodyPartSlot, duration: number = 500): void {
void this.bodyPartsDraggableGrid.centerOnItem(
this.bodyPartsDraggableGrid.getAllItems().indexOf(item),
duration
);
}
private randomizeOutfit(): void {
for (let i = 0; i < this.selectedLayers.length; i += 1) {
this.selectedLayers[i] = Math.floor(Math.random() * this.layers[i].length);
}
}
private changeOutfitPart(index: number): void {
if (this.selectedBodyPartType === undefined) {
return;
}
this.selectedLayers[CustomWokaBodyPartOrder[this.selectedBodyPartType]] = index;
}
private populateGrid(): void {
if (this.selectedBodyPartType === undefined) {
return;
}
const bodyPartsLayer = this.layers[CustomWokaBodyPartOrder[this.selectedBodyPartType]];
this.clearGrid();
for (let i = 0; i < bodyPartsLayer.length; i += 1) {
const slot = new WokaBodyPartSlot(
this,
0,
0,
{
...this.getWokaBodyPartSlotConfig(this.selectedBodyPartType, bodyPartsLayer[i].id),
offsetX: 0,
offsetY: 0,
},
i
).setDisplaySize(this.SLOT_DIMENSION, this.SLOT_DIMENSION);
this.bodyPartsDraggableGrid.addItem(slot);
}
}
private clearGrid(): void {
this.bodyPartsDraggableGrid.clearAllItems();
}
private deselectAllButtons(): void {
for (const bodyPart in CustomWokaBodyPart) {
this.bodyPartsButtons[bodyPart as CustomWokaBodyPart].select(false);
}
}
private deselectAllSlots(): void {
this.bodyPartsDraggableGrid.getAllItems().forEach((slot) => (slot as WokaBodyPartSlot).select(false));
}
}