525 lines
19 KiB
TypeScript
525 lines
19 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 { CustomizedCharacter } from "../Entity/CustomizedCharacter";
|
|
import { analyticsClient } from "../../Administration/AnalyticsClient";
|
|
import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils";
|
|
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,
|
|
WokaBodyPartSlotEvent,
|
|
} from "../Components/CustomizeWoka/WokaBodyPartSlot";
|
|
import { DraggableGridEvent } from "@home-based-studio/phaser3-utils/lib/utils/gui/containers/grids/DraggableGrid";
|
|
import { Button } from "../Components/Ui/Button";
|
|
|
|
export const CustomizeSceneName = "CustomizeScene";
|
|
|
|
export class CustomizeScene extends AbstractCharacterScene {
|
|
private customWokaPreviewer!: CustomWokaPreviewer;
|
|
private bodyPartsDraggableGridBackground!: Phaser.GameObjects.Graphics;
|
|
private bodyPartsDraggableGridForeground!: Phaser.GameObjects.Graphics;
|
|
private bodyPartsDraggableGrid!: DraggableGrid;
|
|
private bodyPartsSlots!: Record<CustomWokaBodyPart, WokaBodyPartSlot>;
|
|
|
|
private randomizeButton!: Button;
|
|
private finishButton!: Button;
|
|
|
|
private selectedLayers: number[] = [0, 0, 0, 0, 0, 0];
|
|
private containersRow: CustomizedCharacter[][] = [];
|
|
private layers: BodyResourceDescriptionInterface[][] = [];
|
|
private selectedBodyPartType?: CustomWokaBodyPart;
|
|
|
|
protected lazyloadingAttempt = true; //permit to update texture loaded after renderer
|
|
|
|
private loader: Loader;
|
|
|
|
constructor() {
|
|
super({
|
|
key: CustomizeSceneName,
|
|
});
|
|
this.loader = new Loader(this);
|
|
}
|
|
|
|
public preload(): void {
|
|
this.input.dragDistanceThreshold = 10;
|
|
const wokaMetadataKey = "woka-list";
|
|
this.cache.json.remove(wokaMetadataKey);
|
|
// FIXME: window.location.href is wrong. We need the URL of the main room (so we need to apply any redirect before!)
|
|
this.load.json(
|
|
wokaMetadataKey,
|
|
`${PUSHER_URL}/woka/list/` + encodeURIComponent(window.location.href),
|
|
undefined,
|
|
{
|
|
responseType: "text",
|
|
headers: {
|
|
Authorization: localUserStore.getAuthToken() ?? "",
|
|
},
|
|
withCredentials: true,
|
|
}
|
|
);
|
|
this.load.once(`filecomplete-json-${wokaMetadataKey}`, () => {
|
|
this.playerTextures.loadPlayerTexturesMetadata(this.cache.json.get(wokaMetadataKey));
|
|
this.loadCustomSceneSelectCharacters()
|
|
.then((bodyResourceDescriptions) => {
|
|
bodyResourceDescriptions.forEach((bodyResourceDescription) => {
|
|
if (
|
|
bodyResourceDescription.level == undefined ||
|
|
bodyResourceDescription.level < 0 ||
|
|
bodyResourceDescription.level > 5
|
|
) {
|
|
throw new Error("Texture level is null");
|
|
}
|
|
this.layers[bodyResourceDescription.level].unshift(bodyResourceDescription);
|
|
});
|
|
this.lazyloadingAttempt = true;
|
|
})
|
|
.catch((e) => console.error(e));
|
|
|
|
this.layers = loadAllLayers(this.load, this.playerTextures);
|
|
this.lazyloadingAttempt = false;
|
|
|
|
//this function must stay at the end of preload function
|
|
this.loader.addLoader();
|
|
});
|
|
}
|
|
|
|
public create(): void {
|
|
this.customWokaPreviewer = new CustomWokaPreviewer(
|
|
this,
|
|
0,
|
|
0,
|
|
this.getCustomWokaPreviewerConfig()
|
|
).setDisplaySize(200, 200);
|
|
|
|
this.bodyPartsDraggableGridBackground = this.add.graphics();
|
|
|
|
const gridBackgroundWidth = 500;
|
|
const gridBackgroundHeight = 170;
|
|
this.bodyPartsDraggableGridBackground.fillStyle(0xf9f9f9);
|
|
this.bodyPartsDraggableGridBackground.fillRect(
|
|
-gridBackgroundWidth / 2,
|
|
-gridBackgroundHeight / 2,
|
|
gridBackgroundWidth,
|
|
gridBackgroundHeight
|
|
);
|
|
|
|
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: 5,
|
|
right: 5,
|
|
},
|
|
spacing: 5,
|
|
debug: {
|
|
showDraggableSpace: false,
|
|
},
|
|
});
|
|
this.bodyPartsDraggableGridForeground = this.add.graphics();
|
|
|
|
this.bodyPartsSlots = {
|
|
[CustomWokaBodyPart.Hair]: new WokaBodyPartSlot(this, 0, 0, {
|
|
...this.getDefaultWokaBodyPartSlotConfig(),
|
|
categoryImageKey: "iconTalk",
|
|
}),
|
|
[CustomWokaBodyPart.Body]: new WokaBodyPartSlot(this, 0, 0, {
|
|
...this.getDefaultWokaBodyPartSlotConfig(),
|
|
categoryImageKey: "iconTalk",
|
|
}),
|
|
[CustomWokaBodyPart.Accessory]: new WokaBodyPartSlot(this, 0, 0, {
|
|
...this.getDefaultWokaBodyPartSlotConfig(),
|
|
categoryImageKey: "iconTalk",
|
|
}),
|
|
[CustomWokaBodyPart.Hat]: new WokaBodyPartSlot(this, 0, 0, {
|
|
...this.getDefaultWokaBodyPartSlotConfig(),
|
|
categoryImageKey: "iconTalk",
|
|
}),
|
|
[CustomWokaBodyPart.Clothes]: new WokaBodyPartSlot(this, 0, 0, {
|
|
...this.getDefaultWokaBodyPartSlotConfig(),
|
|
categoryImageKey: "iconTalk",
|
|
}),
|
|
[CustomWokaBodyPart.Eyes]: new WokaBodyPartSlot(this, 0, 0, {
|
|
...this.getDefaultWokaBodyPartSlotConfig(),
|
|
categoryImageKey: "iconTalk",
|
|
}),
|
|
};
|
|
|
|
this.initializeRandomizeButton();
|
|
this.initializeFinishButton();
|
|
|
|
this.refreshPlayerCurrentOutfit();
|
|
|
|
this.onResize();
|
|
|
|
this.bindEventHandlers();
|
|
}
|
|
|
|
public update(time: number, dt: number): void {
|
|
this.customWokaPreviewer.update();
|
|
}
|
|
|
|
public onResize(): void {
|
|
this.handleCustomWokaPreviewerOnResize();
|
|
this.handleBodyPartSlotsOnResize();
|
|
this.handleBodyPartsDraggableGridOnResize();
|
|
this.handleRandomizeButtonOnResize();
|
|
this.handleFinishButtonOnResize();
|
|
}
|
|
|
|
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);
|
|
waScaleManager.restoreZoom();
|
|
gameManager.tryResumingGame(EnableCameraSceneName);
|
|
}
|
|
|
|
public backToPreviousScene() {
|
|
this.scene.stop(CustomizeSceneName);
|
|
waScaleManager.restoreZoom();
|
|
this.scene.run(SelectCharacterSceneName);
|
|
}
|
|
|
|
private drawGridBackground(gridPosition: { x: number; y: number }): void {
|
|
const gridBackgroundWidth = innerWidth / waScaleManager.getActualZoom();
|
|
const gridBackgroundHeight = 115;
|
|
this.bodyPartsDraggableGridBackground.clear();
|
|
this.bodyPartsDraggableGridBackground.fillStyle(0xf9f9f9);
|
|
this.bodyPartsDraggableGridBackground.fillRect(
|
|
gridPosition.x - gridBackgroundWidth / 2,
|
|
gridPosition.y - gridBackgroundHeight / 2,
|
|
gridBackgroundWidth,
|
|
gridBackgroundHeight
|
|
);
|
|
}
|
|
|
|
private drawGridForeground(gridPosition: { x: number; y: number }): void {
|
|
const gridBackgroundWidth = (innerWidth + 10) / waScaleManager.getActualZoom();
|
|
const gridBackgroundHeight = 115;
|
|
this.bodyPartsDraggableGridForeground.clear();
|
|
this.bodyPartsDraggableGridForeground.lineStyle(4, 0xadafbc);
|
|
this.bodyPartsDraggableGridForeground.strokeRect(
|
|
gridPosition.x - gridBackgroundWidth / 2,
|
|
gridPosition.y - gridBackgroundHeight / 2,
|
|
gridBackgroundWidth,
|
|
gridBackgroundHeight
|
|
);
|
|
}
|
|
|
|
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);
|
|
this.bodyPartsSlots[bodyPart].setTextures(
|
|
this.layers[CustomWokaBodyPartOrder.Body][this.selectedLayers[CustomWokaBodyPartOrder.Body]].id,
|
|
this.layers[i][layerItem].id
|
|
);
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
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 handleBodyPartSlotsOnResize(): void {
|
|
const ratio = innerHeight / innerWidth;
|
|
const slotDimension = 100;
|
|
|
|
for (const part in this.bodyPartsSlots) {
|
|
this.bodyPartsSlots[part as CustomWokaBodyPart].setDisplaySize(slotDimension, slotDimension);
|
|
}
|
|
|
|
const slotSize = this.bodyPartsSlots.Accessory.displayHeight;
|
|
|
|
if (ratio > 1.6) {
|
|
const middle = Math.floor(this.customWokaPreviewer.x);
|
|
const left = Math.floor(middle - slotSize - 10);
|
|
const right = Math.floor(middle + slotSize + 10);
|
|
const top = Math.floor(
|
|
this.customWokaPreviewer.y + this.customWokaPreviewer.displayHeight * 0.5 + slotSize * 1.5 + 9
|
|
);
|
|
const bottom = Math.floor(top + slotSize + 10);
|
|
|
|
this.bodyPartsSlots.Hair.setPosition(left, top);
|
|
this.bodyPartsSlots.Hat.setPosition(middle, top);
|
|
this.bodyPartsSlots.Eyes.setPosition(right, top);
|
|
this.bodyPartsSlots.Body.setPosition(left, bottom);
|
|
this.bodyPartsSlots.Clothes.setPosition(middle, bottom);
|
|
this.bodyPartsSlots.Accessory.setPosition(right, bottom);
|
|
|
|
return;
|
|
}
|
|
|
|
const left = Math.floor(
|
|
this.customWokaPreviewer.x - this.customWokaPreviewer.displayWidth * 0.5 - slotSize * 0.5 - 10
|
|
);
|
|
const leftEdge = Math.floor(left - slotSize - 10);
|
|
const right = Math.floor(
|
|
this.customWokaPreviewer.x + this.customWokaPreviewer.displayWidth * 0.5 + slotSize * 0.5 + 10
|
|
);
|
|
const rightEdge = Math.floor(right + slotSize + 10);
|
|
const top = Math.floor(0 + slotSize * 0.5 + 9);
|
|
const middle = Math.floor(top + slotSize + 10);
|
|
const bottom = Math.floor(middle + slotSize + 10);
|
|
|
|
this.bodyPartsSlots.Hair.setPosition(left, top);
|
|
this.bodyPartsSlots.Body.setPosition(left, middle);
|
|
this.bodyPartsSlots.Accessory.setPosition(ratio < 0.6 ? leftEdge : left, ratio < 0.6 ? middle : bottom);
|
|
this.bodyPartsSlots.Hat.setPosition(right, top);
|
|
this.bodyPartsSlots.Clothes.setPosition(right, middle);
|
|
this.bodyPartsSlots.Eyes.setPosition(ratio < 0.6 ? rightEdge : right, ratio < 0.6 ? middle : bottom);
|
|
}
|
|
|
|
private handleBodyPartsDraggableGridOnResize(): void {
|
|
const gridHeight = 110;
|
|
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 - gridHeight * 0.5,
|
|
};
|
|
|
|
this.drawGridBackground(gridPos);
|
|
this.drawGridForeground(gridPos);
|
|
try {
|
|
this.bodyPartsDraggableGrid.changeDraggableSpacePosAndSize(
|
|
gridPos,
|
|
{ x: gridWidth, y: gridHeight },
|
|
gridPos
|
|
);
|
|
} catch (error) {
|
|
console.warn(error);
|
|
}
|
|
|
|
this.populateGrid();
|
|
this.bodyPartsDraggableGrid.moveContentToBeginning();
|
|
}
|
|
|
|
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 getDefaultWokaBodyPartSlotConfig(): WokaBodyPartSlotConfig {
|
|
return {
|
|
color: 0xffffff,
|
|
borderThickness: 1,
|
|
borderColor: 0xadafbc,
|
|
borderSelectedColor: 0x209cee,
|
|
offsetX: -4,
|
|
offsetY: -3,
|
|
};
|
|
}
|
|
|
|
private bindEventHandlers(): void {
|
|
this.events.addListener("wake", () => {
|
|
waScaleManager.saveZoom();
|
|
waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 3 : 1;
|
|
});
|
|
|
|
this.input.keyboard.on("keyup-ENTER", () => {
|
|
this.nextSceneToCamera();
|
|
});
|
|
this.input.keyboard.on("keyup-BACKSPACE", () => {
|
|
this.backToPreviousScene();
|
|
});
|
|
|
|
this.randomizeButton.on(Phaser.Input.Events.POINTER_UP, () => {
|
|
this.randomizeOutfit();
|
|
this.clearGrid();
|
|
this.deselectAllSlots();
|
|
this.refreshPlayerCurrentOutfit();
|
|
});
|
|
|
|
this.finishButton.on(Phaser.Input.Events.POINTER_UP, () => {
|
|
this.nextSceneToCamera();
|
|
});
|
|
|
|
for (const bodyPart in CustomWokaBodyPart) {
|
|
const slot = this.bodyPartsSlots[bodyPart as CustomWokaBodyPart];
|
|
slot.on(WokaBodyPartSlotEvent.Clicked, (selected: boolean) => {
|
|
if (!selected) {
|
|
this.selectedBodyPartType = bodyPart as CustomWokaBodyPart;
|
|
this.deselectAllSlots();
|
|
slot.select(true);
|
|
this.populateGrid();
|
|
} else {
|
|
this.selectedBodyPartType = undefined;
|
|
slot.select(false);
|
|
this.clearGrid();
|
|
}
|
|
});
|
|
}
|
|
|
|
this.bodyPartsDraggableGrid.on(DraggableGridEvent.ItemClicked, (item: WokaBodyPartSlot) => {
|
|
this.bodyPartsDraggableGrid.getAllItems().forEach((slot) => (slot as WokaBodyPartSlot).select(false));
|
|
this.changeOutfitPart(Number(item.getId()));
|
|
this.refreshPlayerCurrentOutfit();
|
|
item.select(true);
|
|
});
|
|
}
|
|
|
|
private randomizeOutfit(): void {
|
|
for (let i = 0; i < 6; 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 slotDimension = 100;
|
|
|
|
const bodyPartsLayer = this.layers[CustomWokaBodyPartOrder[this.selectedBodyPartType]];
|
|
|
|
this.bodyPartsDraggableGrid.clearAllItems();
|
|
for (let i = 0; i < bodyPartsLayer.length; i += 1) {
|
|
const slot = new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig(), i).setDisplaySize(
|
|
slotDimension,
|
|
slotDimension
|
|
);
|
|
if (this.selectedBodyPartType === CustomWokaBodyPart.Body) {
|
|
slot.setBodyTexture(bodyPartsLayer[i].id);
|
|
slot.setImageTexture();
|
|
} else {
|
|
slot.setBodyTexture(
|
|
this.layers[CustomWokaBodyPartOrder.Body][this.selectedLayers[CustomWokaBodyPartOrder.Body]].id
|
|
);
|
|
slot.setImageTexture(bodyPartsLayer[i].id);
|
|
}
|
|
this.bodyPartsDraggableGrid.addItem(slot);
|
|
}
|
|
this.bodyPartsDraggableGrid.moveContentToBeginning();
|
|
}
|
|
|
|
private clearGrid(): void {
|
|
this.bodyPartsDraggableGrid.clearAllItems();
|
|
}
|
|
|
|
private deselectAllSlots(): void {
|
|
for (const bodyPart in CustomWokaBodyPart) {
|
|
this.bodyPartsSlots[bodyPart as CustomWokaBodyPart].select(false);
|
|
}
|
|
}
|
|
}
|