Merge branch 'develop' of github.com:thecodingmachine/workadventure into develop
@ -210,11 +210,7 @@ export class GameRoom {
|
||||
}
|
||||
|
||||
updatePlayerDetails(user: User, playerDetailsMessage: SetPlayerDetailsMessage) {
|
||||
if (playerDetailsMessage.getRemoveoutlinecolor()) {
|
||||
user.outlineColor = undefined;
|
||||
} else {
|
||||
user.outlineColor = playerDetailsMessage.getOutlinecolor();
|
||||
}
|
||||
user.updateDetails(playerDetailsMessage);
|
||||
}
|
||||
|
||||
private updateUserGroup(user: User): void {
|
||||
|
@ -45,8 +45,8 @@ export class PositionNotifier {
|
||||
private zones: Zone[][] = [];
|
||||
|
||||
constructor(
|
||||
private zoneWidth: number,
|
||||
private zoneHeight: number,
|
||||
private readonly zoneWidth: number,
|
||||
private readonly zoneHeight: number,
|
||||
private onUserEnters: EntersCallback,
|
||||
private onUserMoves: MovesCallback,
|
||||
private onUserLeaves: LeavesCallback,
|
||||
|
@ -15,6 +15,7 @@ import {
|
||||
SubMessage,
|
||||
} from "../Messages/generated/messages_pb";
|
||||
import { CharacterLayer } from "_Model/Websocket/CharacterLayer";
|
||||
import { BoolValue, UInt32Value } from "google-protobuf/google/protobuf/wrappers_pb";
|
||||
|
||||
export type UserSocket = ServerDuplexStream<PusherToBackMessage, ServerToClientMessage>;
|
||||
|
||||
@ -37,7 +38,8 @@ export class User implements Movable {
|
||||
public readonly name: string,
|
||||
public readonly characterLayers: CharacterLayer[],
|
||||
public readonly companion?: CompanionMessage,
|
||||
private _outlineColor?: number | undefined
|
||||
private outlineColor?: number,
|
||||
private voiceIndicatorShown?: boolean
|
||||
) {
|
||||
this.listenedZones = new Set<Zone>();
|
||||
|
||||
@ -83,6 +85,10 @@ export class User implements Movable {
|
||||
return this.followedBy.size !== 0;
|
||||
}
|
||||
|
||||
public getOutlineColor(): number | undefined {
|
||||
return this.outlineColor;
|
||||
}
|
||||
|
||||
get following(): User | undefined {
|
||||
return this._following;
|
||||
}
|
||||
@ -115,14 +121,21 @@ export class User implements Movable {
|
||||
}
|
||||
}
|
||||
|
||||
public set outlineColor(value: number | undefined) {
|
||||
this._outlineColor = value;
|
||||
public updateDetails(details: SetPlayerDetailsMessage) {
|
||||
if (details.getRemoveoutlinecolor()) {
|
||||
this.outlineColor = undefined;
|
||||
} else if (details.getOutlinecolor()?.getValue() !== undefined) {
|
||||
this.outlineColor = details.getOutlinecolor()?.getValue();
|
||||
}
|
||||
this.voiceIndicatorShown = details.getShowvoiceindicator()?.getValue();
|
||||
|
||||
const playerDetails = new SetPlayerDetailsMessage();
|
||||
if (value === undefined) {
|
||||
playerDetails.setRemoveoutlinecolor(true);
|
||||
} else {
|
||||
playerDetails.setOutlinecolor(value);
|
||||
|
||||
if (this.outlineColor !== undefined) {
|
||||
playerDetails.setOutlinecolor(new UInt32Value().setValue(this.outlineColor));
|
||||
}
|
||||
if (this.voiceIndicatorShown !== undefined) {
|
||||
playerDetails.setShowvoiceindicator(new BoolValue().setValue(this.voiceIndicatorShown));
|
||||
}
|
||||
|
||||
this.positionNotifier.updatePlayerDetails(this, playerDetails);
|
||||
|
@ -338,11 +338,12 @@ export class SocketManager {
|
||||
userJoinedZoneMessage.setVisitcardurl(thing.visitCardUrl);
|
||||
}
|
||||
userJoinedZoneMessage.setCompanion(thing.companion);
|
||||
if (thing.outlineColor === undefined) {
|
||||
const outlineColor = thing.getOutlineColor();
|
||||
if (outlineColor === undefined) {
|
||||
userJoinedZoneMessage.setHasoutline(false);
|
||||
} else {
|
||||
userJoinedZoneMessage.setHasoutline(true);
|
||||
userJoinedZoneMessage.setOutlinecolor(thing.outlineColor);
|
||||
userJoinedZoneMessage.setOutlinecolor(outlineColor);
|
||||
}
|
||||
|
||||
const subMessage = new SubToPusherMessage();
|
||||
|
@ -2206,9 +2206,9 @@ minimatch@^3.0.4:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimist@^1.2.0, minimist@^1.2.5:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
||||
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
|
||||
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
|
||||
|
||||
mkdirp@^0.5.1, mkdirp@^0.5.4:
|
||||
version "0.5.5"
|
||||
|
@ -4,7 +4,8 @@
|
||||
"main": "index.js",
|
||||
"license": "SEE LICENSE IN LICENSE.txt",
|
||||
"devDependencies": {
|
||||
"@geprog/vite-plugin-env-config": "^4.0.0",
|
||||
"@geprog/vite-plugin-env-config": "^4.0.3",
|
||||
"@home-based-studio/phaser3-utils": "^0.4.2",
|
||||
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.36",
|
||||
"@tsconfig/svelte": "^1.0.10",
|
||||
"@types/google-protobuf": "^3.7.3",
|
||||
@ -46,7 +47,7 @@
|
||||
"easystarjs": "^0.4.4",
|
||||
"generic-type-guard": "^3.4.2",
|
||||
"google-protobuf": "^3.13.0",
|
||||
"phaser": "^3.54.0",
|
||||
"phaser": "3.55.1",
|
||||
"phaser-animated-tiles": "workadventure/phaser-animated-tiles#da68bbededd605925621dd4f03bd27e69284b254",
|
||||
"phaser3-rex-plugins": "^1.1.42",
|
||||
"posthog-js": "^1.14.1",
|
||||
|
BIN
front/public/resources/icons/icon_accessory.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
front/public/resources/icons/icon_body.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
front/public/resources/icons/icon_clothes.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
front/public/resources/icons/icon_eyes.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
front/public/resources/icons/icon_hair.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
front/public/resources/icons/icon_hat.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
front/public/resources/icons/icon_turn.png
Normal file
After Width: | Height: | Size: 7.0 KiB |
1
front/public/resources/logos/report.svg
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
front/public/resources/tilesets/floor_tiles.png
Normal file
After Width: | Height: | Size: 19 KiB |
@ -1,14 +1,12 @@
|
||||
<script lang="ts">
|
||||
import type { Game } from "../Phaser/Game/Game";
|
||||
import { chatVisibilityStore } from "../Stores/ChatStore";
|
||||
import { customCharacterSceneVisibleStore } from "../Stores/CustomCharacterStore";
|
||||
import { errorStore } from "../Stores/ErrorStore";
|
||||
import { loginSceneVisibleStore } from "../Stores/LoginSceneStore";
|
||||
import { enableCameraSceneVisibilityStore } from "../Stores/MediaStore";
|
||||
import { selectCharacterSceneVisibleStore } from "../Stores/SelectCharacterStore";
|
||||
import { selectCompanionSceneVisibleStore } from "../Stores/SelectCompanionStore";
|
||||
import Chat from "./Chat/Chat.svelte";
|
||||
import CustomCharacterScene from "./CustomCharacterScene/CustomCharacterScene.svelte";
|
||||
import EnableCameraScene from "./EnableCamera/EnableCameraScene.svelte";
|
||||
import LoginScene from "./Login/LoginScene.svelte";
|
||||
import MainLayout from "./MainLayout.svelte";
|
||||
@ -31,10 +29,6 @@
|
||||
<div>
|
||||
<SelectCharacterScene {game} />
|
||||
</div>
|
||||
{:else if $customCharacterSceneVisibleStore}
|
||||
<div>
|
||||
<CustomCharacterScene {game} />
|
||||
</div>
|
||||
{:else if $selectCompanionSceneVisibleStore}
|
||||
<div>
|
||||
<SelectCompanionScene {game} />
|
||||
|
@ -1,142 +0,0 @@
|
||||
<script lang="ts">
|
||||
import type { Game } from "../../Phaser/Game/Game";
|
||||
import { CustomizeScene, CustomizeSceneName } from "../../Phaser/Login/CustomizeScene";
|
||||
import { activeRowStore } from "../../Stores/CustomCharacterStore";
|
||||
import LL from "../../i18n/i18n-svelte";
|
||||
|
||||
export let game: Game;
|
||||
|
||||
const customCharacterScene = game.scene.getScene(CustomizeSceneName) as CustomizeScene;
|
||||
|
||||
function selectLeft() {
|
||||
customCharacterScene.moveCursorHorizontally(-1);
|
||||
}
|
||||
|
||||
function selectRight() {
|
||||
customCharacterScene.moveCursorHorizontally(1);
|
||||
}
|
||||
|
||||
function selectUp() {
|
||||
customCharacterScene.moveCursorVertically(-1);
|
||||
}
|
||||
|
||||
function selectDown() {
|
||||
customCharacterScene.moveCursorVertically(1);
|
||||
}
|
||||
|
||||
function previousScene() {
|
||||
customCharacterScene.backToPreviousScene();
|
||||
}
|
||||
|
||||
function finish() {
|
||||
customCharacterScene.nextSceneToCamera();
|
||||
}
|
||||
</script>
|
||||
|
||||
<form class="customCharacterScene">
|
||||
<section class="text-center">
|
||||
<h2>{$LL.woka.customWoka.title()}</h2>
|
||||
</section>
|
||||
<section class="action action-move">
|
||||
<button
|
||||
class="customCharacterSceneButton customCharacterSceneButtonLeft nes-btn"
|
||||
on:click|preventDefault={selectLeft}
|
||||
>
|
||||
<
|
||||
</button>
|
||||
<button
|
||||
class="customCharacterSceneButton customCharacterSceneButtonRight nes-btn"
|
||||
on:click|preventDefault={selectRight}
|
||||
>
|
||||
>
|
||||
</button>
|
||||
</section>
|
||||
<section class="action">
|
||||
{#if $activeRowStore === 0}
|
||||
<button type="submit" class="customCharacterSceneFormBack nes-btn" on:click|preventDefault={previousScene}
|
||||
>{$LL.woka.customWoka.navigation.return()}</button
|
||||
>
|
||||
{/if}
|
||||
{#if $activeRowStore !== 0}
|
||||
<button type="submit" class="customCharacterSceneFormBack nes-btn" on:click|preventDefault={selectUp}
|
||||
>{$LL.woka.customWoka.navigation.back()}
|
||||
<img src="resources/objects/arrow_up_black.png" alt="" /></button
|
||||
>
|
||||
{/if}
|
||||
{#if $activeRowStore === 5}
|
||||
<button
|
||||
type="submit"
|
||||
class="customCharacterSceneFormSubmit nes-btn is-primary"
|
||||
on:click|preventDefault={finish}>{$LL.woka.customWoka.navigation.finish()}</button
|
||||
>
|
||||
{/if}
|
||||
{#if $activeRowStore !== 5}
|
||||
<button
|
||||
type="submit"
|
||||
class="customCharacterSceneFormSubmit nes-btn is-primary"
|
||||
on:click|preventDefault={selectDown}
|
||||
>{$LL.woka.customWoka.navigation.next()}
|
||||
<img src="resources/objects/arrow_down.png" alt="" /></button
|
||||
>
|
||||
{/if}
|
||||
</section>
|
||||
</form>
|
||||
|
||||
<style lang="scss">
|
||||
@import "../../../style/breakpoints.scss";
|
||||
|
||||
form.customCharacterScene {
|
||||
font-family: "Press Start 2P";
|
||||
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.customCharacterSceneButton {
|
||||
position: absolute;
|
||||
top: 33vh;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
button.customCharacterSceneFormBack {
|
||||
color: #292929;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
font-family: "Press Start 2P";
|
||||
|
||||
&.customCharacterSceneButtonLeft {
|
||||
left: 33vw;
|
||||
}
|
||||
|
||||
&.customCharacterSceneButtonRight {
|
||||
right: 33vw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
form.customCharacterScene button.customCharacterSceneButtonLeft {
|
||||
left: 5vw;
|
||||
}
|
||||
form.customCharacterScene button.customCharacterSceneButtonRight {
|
||||
right: 5vw;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -77,12 +77,6 @@ export interface ItemEventMessageInterface {
|
||||
parameters: unknown;
|
||||
}
|
||||
|
||||
export interface PlayerDetailsUpdatedMessageInterface {
|
||||
userId: number;
|
||||
outlineColor: number;
|
||||
removeOutlineColor: boolean;
|
||||
}
|
||||
|
||||
export interface RoomJoinedMessageInterface {
|
||||
//users: MessageUserPositionInterface[],
|
||||
//groups: GroupCreatedUpdatedMessageInterface[],
|
||||
|
@ -504,6 +504,20 @@ export class RoomConnection implements RoomConnection {
|
||||
this.socket.send(clientToServerMessage.serializeBinary().buffer);
|
||||
}*/
|
||||
|
||||
public emitPlayerShowVoiceIndicator(show: boolean): void {
|
||||
const message = SetPlayerDetailsMessageTsProto.fromPartial({
|
||||
showVoiceIndicator: show,
|
||||
});
|
||||
const bytes = ClientToServerMessageTsProto.encode({
|
||||
message: {
|
||||
$case: "setPlayerDetailsMessage",
|
||||
setPlayerDetailsMessage: message,
|
||||
},
|
||||
}).finish();
|
||||
|
||||
this.socket.send(bytes);
|
||||
}
|
||||
|
||||
public emitPlayerOutlineColor(color: number | null) {
|
||||
let message: SetPlayerDetailsMessageTsProto;
|
||||
if (color === null) {
|
||||
|
184
front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts
Normal file
@ -0,0 +1,184 @@
|
||||
import { Easing } from "../../../types";
|
||||
import { getPlayerAnimations, PlayerAnimationDirections, PlayerAnimationTypes } from "../../Player/Animation";
|
||||
|
||||
export enum CustomWokaBodyPart {
|
||||
Body = "Body",
|
||||
Eyes = "Eyes",
|
||||
Hair = "Hair",
|
||||
Clothes = "Clothes",
|
||||
Hat = "Hat",
|
||||
Accessory = "Accessory",
|
||||
}
|
||||
|
||||
export enum CustomWokaBodyPartOrder {
|
||||
Body,
|
||||
Eyes,
|
||||
Hair,
|
||||
Clothes,
|
||||
Hat,
|
||||
Accessory,
|
||||
}
|
||||
|
||||
export interface CustomWokaPreviewerConfig {
|
||||
color: number;
|
||||
borderThickness: number;
|
||||
borderColor: number;
|
||||
bodyPartsOffsetX: number;
|
||||
}
|
||||
|
||||
export class CustomWokaPreviewer extends Phaser.GameObjects.Container {
|
||||
private background: Phaser.GameObjects.Image;
|
||||
private frame: Phaser.GameObjects.Graphics;
|
||||
private sprites: Record<CustomWokaBodyPart, Phaser.GameObjects.Sprite>;
|
||||
private turnIcon: Phaser.GameObjects.Image;
|
||||
|
||||
private animationDirection: PlayerAnimationDirections = PlayerAnimationDirections.Down;
|
||||
private moving: boolean = true;
|
||||
|
||||
private turnIconTween?: Phaser.Tweens.Tween;
|
||||
|
||||
private config: CustomWokaPreviewerConfig;
|
||||
|
||||
public readonly SIZE: number = 50;
|
||||
|
||||
constructor(scene: Phaser.Scene, x: number, y: number, config: CustomWokaPreviewerConfig) {
|
||||
super(scene, x, y);
|
||||
|
||||
this.config = config;
|
||||
|
||||
this.sprites = {
|
||||
[CustomWokaBodyPart.Accessory]: this.scene.add
|
||||
.sprite(this.config.bodyPartsOffsetX, 0, "")
|
||||
.setVisible(false),
|
||||
[CustomWokaBodyPart.Body]: this.scene.add.sprite(this.config.bodyPartsOffsetX, 0, "").setVisible(false),
|
||||
[CustomWokaBodyPart.Clothes]: this.scene.add.sprite(this.config.bodyPartsOffsetX, 0, "").setVisible(false),
|
||||
[CustomWokaBodyPart.Eyes]: this.scene.add.sprite(this.config.bodyPartsOffsetX, 0, "").setVisible(false),
|
||||
[CustomWokaBodyPart.Hair]: this.scene.add.sprite(this.config.bodyPartsOffsetX, 0, "").setVisible(false),
|
||||
[CustomWokaBodyPart.Hat]: this.scene.add.sprite(this.config.bodyPartsOffsetX, 0, "").setVisible(false),
|
||||
};
|
||||
|
||||
this.background = this.scene.add.image(0, 0, "floorTexture0");
|
||||
this.frame = this.scene.add.graphics();
|
||||
this.turnIcon = this.scene.add
|
||||
.image(this.background.displayWidth * 0.35, this.background.displayHeight * 0.35, "iconTurn")
|
||||
.setScale(0.25)
|
||||
.setTintFill(0xffffff)
|
||||
.setAlpha(0.5);
|
||||
|
||||
this.drawFrame();
|
||||
this.setSize(this.SIZE, this.SIZE);
|
||||
this.setInteractive({ cursor: "pointer" });
|
||||
|
||||
this.add([
|
||||
this.background,
|
||||
this.frame,
|
||||
this.sprites.Body,
|
||||
this.sprites.Eyes,
|
||||
this.sprites.Hair,
|
||||
this.sprites.Clothes,
|
||||
this.sprites.Hat,
|
||||
this.sprites.Accessory,
|
||||
this.turnIcon,
|
||||
]);
|
||||
|
||||
this.bindEventHandlers();
|
||||
|
||||
this.scene.add.existing(this);
|
||||
}
|
||||
|
||||
public update(): void {
|
||||
this.animate();
|
||||
}
|
||||
|
||||
public changeAnimation(direction: PlayerAnimationDirections, moving: boolean): void {
|
||||
this.animationDirection = direction;
|
||||
this.moving = moving;
|
||||
}
|
||||
|
||||
public updateSprite(textureKey: string, bodyPart: CustomWokaBodyPart): void {
|
||||
this.sprites[bodyPart].anims.stop();
|
||||
this.sprites[bodyPart].setTexture(textureKey).setVisible(textureKey !== "");
|
||||
if (textureKey === "") {
|
||||
return;
|
||||
}
|
||||
getPlayerAnimations(textureKey).forEach((d) => {
|
||||
this.scene.anims.create({
|
||||
key: d.key,
|
||||
frames: this.scene.anims.generateFrameNumbers(d.frameModel, { frames: d.frames }),
|
||||
frameRate: d.frameRate,
|
||||
repeat: d.repeat,
|
||||
});
|
||||
});
|
||||
// Needed, otherwise, animations are not handled correctly.
|
||||
if (this.scene) {
|
||||
this.scene.sys.updateList.add(this.sprites[bodyPart]);
|
||||
}
|
||||
}
|
||||
|
||||
public isMoving(): boolean {
|
||||
return this.moving;
|
||||
}
|
||||
|
||||
public getAnimationDirection(): PlayerAnimationDirections {
|
||||
return this.animationDirection;
|
||||
}
|
||||
|
||||
private bindEventHandlers(): void {
|
||||
this.on(Phaser.Input.Events.POINTER_UP, () => {
|
||||
const direction = this.getNextAnimationDirection();
|
||||
const moving = direction === PlayerAnimationDirections.Down ? !this.moving : this.moving;
|
||||
this.changeAnimation(direction, moving);
|
||||
|
||||
this.turnIconTween?.stop();
|
||||
this.turnIcon.setScale(0.25);
|
||||
this.turnIconTween = this.scene.tweens.add({
|
||||
targets: [this.turnIcon],
|
||||
duration: 100,
|
||||
scale: 0.2,
|
||||
yoyo: true,
|
||||
ease: Easing.SineEaseIn,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private drawFrame(): void {
|
||||
this.frame.clear();
|
||||
this.frame.lineStyle(this.config.borderThickness, 0xadafbc);
|
||||
this.frame.strokeRect(-this.SIZE / 2, -this.SIZE / 2, this.SIZE, this.SIZE);
|
||||
}
|
||||
|
||||
private animate(): void {
|
||||
for (const bodyPartKey in this.sprites) {
|
||||
const sprite = this.sprites[bodyPartKey as CustomWokaBodyPart];
|
||||
if (!sprite.anims) {
|
||||
console.error("ANIMS IS NOT DEFINED!!!");
|
||||
return;
|
||||
}
|
||||
const textureKey = sprite.texture.key;
|
||||
if (textureKey === "__MISSING") {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
this.moving &&
|
||||
(!sprite.anims.currentAnim || sprite.anims.currentAnim.key !== this.animationDirection)
|
||||
) {
|
||||
sprite.play(textureKey + "-" + this.animationDirection + "-" + PlayerAnimationTypes.Walk, true);
|
||||
} else if (!this.moving) {
|
||||
sprite.anims.play(textureKey + "-" + this.animationDirection + "-" + PlayerAnimationTypes.Idle, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private getNextAnimationDirection(): PlayerAnimationDirections {
|
||||
switch (this.animationDirection) {
|
||||
case PlayerAnimationDirections.Down:
|
||||
return PlayerAnimationDirections.Left;
|
||||
case PlayerAnimationDirections.Left:
|
||||
return PlayerAnimationDirections.Up;
|
||||
case PlayerAnimationDirections.Up:
|
||||
return PlayerAnimationDirections.Right;
|
||||
case PlayerAnimationDirections.Right:
|
||||
return PlayerAnimationDirections.Down;
|
||||
}
|
||||
}
|
||||
}
|
143
front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts
Normal file
@ -0,0 +1,143 @@
|
||||
import { GridItem } from "@home-based-studio/phaser3-utils";
|
||||
import { GridItemEvent } from "@home-based-studio/phaser3-utils/lib/utils/gui/containers/grids/GridItem";
|
||||
import { CustomWokaBodyPart } from "./CustomWokaPreviewer";
|
||||
|
||||
export interface WokaBodyPartSlotConfig {
|
||||
color: number;
|
||||
borderThickness: number;
|
||||
borderColor: number;
|
||||
borderSelectedColor: number;
|
||||
offsetX: number;
|
||||
offsetY: number;
|
||||
textureKeys: Record<CustomWokaBodyPart, string>;
|
||||
categoryImageKey?: string;
|
||||
selected?: boolean;
|
||||
}
|
||||
|
||||
export enum WokaBodyPartSlotEvent {
|
||||
Clicked = "WokaBodyPartSlotEvent:Clicked",
|
||||
}
|
||||
|
||||
export class WokaBodyPartSlot extends GridItem {
|
||||
private background: Phaser.GameObjects.Image;
|
||||
private frame: Phaser.GameObjects.Graphics;
|
||||
private categoryImage?: Phaser.GameObjects.Image;
|
||||
private sprites: Record<CustomWokaBodyPart, Phaser.GameObjects.Sprite>;
|
||||
|
||||
private config: WokaBodyPartSlotConfig;
|
||||
|
||||
private selected: boolean;
|
||||
|
||||
public static readonly SIZE: number = 50;
|
||||
|
||||
constructor(scene: Phaser.Scene, x: number, y: number, config: WokaBodyPartSlotConfig, id?: number) {
|
||||
super(scene, `${id}`, { x, y });
|
||||
|
||||
this.config = config;
|
||||
|
||||
const textures = this.config.textureKeys;
|
||||
this.sprites = {
|
||||
[CustomWokaBodyPart.Accessory]: this.scene.add
|
||||
.sprite(this.config.offsetX, this.config.offsetY, textures.Accessory)
|
||||
.setVisible(textures.Accessory !== ""),
|
||||
[CustomWokaBodyPart.Body]: this.scene.add
|
||||
.sprite(this.config.offsetX, this.config.offsetY, textures.Body)
|
||||
.setVisible(textures.Body !== ""),
|
||||
[CustomWokaBodyPart.Clothes]: this.scene.add
|
||||
.sprite(this.config.offsetX, this.config.offsetY, textures.Clothes)
|
||||
.setVisible(textures.Clothes !== ""),
|
||||
[CustomWokaBodyPart.Eyes]: this.scene.add
|
||||
.sprite(this.config.offsetX, this.config.offsetY, textures.Eyes)
|
||||
.setVisible(textures.Eyes !== ""),
|
||||
[CustomWokaBodyPart.Hair]: this.scene.add
|
||||
.sprite(this.config.offsetX, this.config.offsetY, textures.Hair)
|
||||
.setVisible(textures.Hair !== ""),
|
||||
[CustomWokaBodyPart.Hat]: this.scene.add
|
||||
.sprite(this.config.offsetX, this.config.offsetY, textures.Hat)
|
||||
.setVisible(textures.Hat !== ""),
|
||||
};
|
||||
|
||||
this.selected = this.config.selected ?? false;
|
||||
|
||||
this.background = this.background = this.scene.add.image(0, 0, `floorTexture0`);
|
||||
this.frame = this.scene.add.graphics();
|
||||
this.drawFrame();
|
||||
this.add([
|
||||
this.background,
|
||||
this.frame,
|
||||
this.sprites.Body,
|
||||
this.sprites.Eyes,
|
||||
this.sprites.Hair,
|
||||
this.sprites.Clothes,
|
||||
this.sprites.Hat,
|
||||
this.sprites.Accessory,
|
||||
]);
|
||||
|
||||
if (this.config.categoryImageKey) {
|
||||
this.categoryImage = this.scene.add
|
||||
.image(WokaBodyPartSlot.SIZE / 2 - 1, -WokaBodyPartSlot.SIZE / 2 + 1, this.config.categoryImageKey)
|
||||
.setDisplaySize(16, 16)
|
||||
.setAlpha(0.75)
|
||||
.setOrigin(1, 0);
|
||||
this.add(this.categoryImage);
|
||||
}
|
||||
|
||||
this.setSize(WokaBodyPartSlot.SIZE, WokaBodyPartSlot.SIZE);
|
||||
this.setInteractive({ cursor: "pointer" });
|
||||
this.scene.input.setDraggable(this);
|
||||
|
||||
this.bindEventHandlers();
|
||||
|
||||
this.scene.add.existing(this);
|
||||
}
|
||||
|
||||
public getContentData(): Record<CustomWokaBodyPart, string> {
|
||||
return this.config.textureKeys;
|
||||
}
|
||||
|
||||
public setTextures(textureKeys: Record<CustomWokaBodyPart, string>): void {
|
||||
this.config.textureKeys = textureKeys;
|
||||
this.sprites.Accessory.setTexture(textureKeys.Accessory).setVisible(textureKeys.Accessory !== "");
|
||||
this.sprites.Body.setTexture(textureKeys.Body).setVisible(textureKeys.Body !== "");
|
||||
this.sprites.Clothes.setTexture(textureKeys.Clothes).setVisible(textureKeys.Clothes !== "");
|
||||
this.sprites.Eyes.setTexture(textureKeys.Eyes).setVisible(textureKeys.Eyes !== "");
|
||||
this.sprites.Hair.setTexture(textureKeys.Hair).setVisible(textureKeys.Hair !== "");
|
||||
this.sprites.Hat.setTexture(textureKeys.Hat).setVisible(textureKeys.Hat !== "");
|
||||
}
|
||||
|
||||
public select(select: boolean = true): void {
|
||||
if (this.selected === select) {
|
||||
return;
|
||||
}
|
||||
this.selected = select;
|
||||
this.updateSelected();
|
||||
}
|
||||
|
||||
public isSelected(): boolean {
|
||||
return this.selected;
|
||||
}
|
||||
|
||||
protected bindEventHandlers(): void {
|
||||
super.bindEventHandlers();
|
||||
|
||||
this.on(GridItemEvent.Clicked, () => {
|
||||
this.emit(WokaBodyPartSlotEvent.Clicked, this.selected);
|
||||
});
|
||||
}
|
||||
|
||||
private drawFrame(): void {
|
||||
this.frame.clear();
|
||||
this.frame.lineStyle(
|
||||
this.config.borderThickness,
|
||||
this.selected ? this.config.borderSelectedColor : this.config.borderColor
|
||||
);
|
||||
|
||||
const size = WokaBodyPartSlot.SIZE;
|
||||
|
||||
this.frame.strokeRect(-size / 2, -size / 2, size, size);
|
||||
}
|
||||
|
||||
private updateSelected(): void {
|
||||
this.drawFrame();
|
||||
}
|
||||
}
|
99
front/src/Phaser/Components/Ui/Button.ts
Normal file
@ -0,0 +1,99 @@
|
||||
export interface ButtonConfig {
|
||||
width: number;
|
||||
height: number;
|
||||
idle: ButtonAppearanceConfig;
|
||||
hover: ButtonAppearanceConfig;
|
||||
pressed: ButtonAppearanceConfig;
|
||||
}
|
||||
|
||||
export interface ButtonAppearanceConfig {
|
||||
textColor: string;
|
||||
color: number;
|
||||
borderThickness: number;
|
||||
borderColor: number;
|
||||
}
|
||||
|
||||
export class Button extends Phaser.GameObjects.Container {
|
||||
private background: Phaser.GameObjects.Graphics;
|
||||
private text: Phaser.GameObjects.Text;
|
||||
|
||||
private config: ButtonConfig;
|
||||
|
||||
private hovered: boolean = false;
|
||||
private pressed: boolean = false;
|
||||
|
||||
constructor(scene: Phaser.Scene, x: number, y: number, config: ButtonConfig) {
|
||||
super(scene, x, y);
|
||||
|
||||
this.config = config;
|
||||
|
||||
this.background = this.scene.add.graphics();
|
||||
this.text = this.scene.add
|
||||
.text(0, 0, "", {
|
||||
color: "0x000000",
|
||||
fontFamily: '"Press Start 2P"',
|
||||
fontSize: "9px",
|
||||
})
|
||||
.setOrigin(0.5, 0.45);
|
||||
this.drawBackground(this.config.idle);
|
||||
|
||||
this.add([this.background, this.text]);
|
||||
|
||||
this.setSize(this.config.width, this.config.height);
|
||||
this.setInteractive({ cursor: "pointer" });
|
||||
|
||||
this.bindEventHandlers();
|
||||
|
||||
this.scene.add.existing(this);
|
||||
}
|
||||
|
||||
public setText(text: string): void {
|
||||
this.text.setText(text);
|
||||
}
|
||||
|
||||
private updateButtonAppearance(): void {
|
||||
if (this.pressed) {
|
||||
this.drawBackground(this.config.pressed);
|
||||
return;
|
||||
}
|
||||
if (this.hovered) {
|
||||
this.drawBackground(this.config.hover);
|
||||
return;
|
||||
}
|
||||
this.drawBackground(this.config.idle);
|
||||
}
|
||||
|
||||
private drawBackground(appearance: ButtonAppearanceConfig): void {
|
||||
this.background.clear();
|
||||
this.background.fillStyle(appearance.color);
|
||||
this.background.lineStyle(appearance.borderThickness, appearance.borderColor);
|
||||
|
||||
const w = this.config.width;
|
||||
const h = this.config.height;
|
||||
|
||||
this.background.fillRect(-w / 2, -h / 2, w, h);
|
||||
this.background.strokeRect(-w / 2, -h / 2, w, h);
|
||||
|
||||
this.text.setColor(appearance.textColor);
|
||||
}
|
||||
|
||||
private bindEventHandlers(): void {
|
||||
this.on(Phaser.Input.Events.POINTER_OVER, () => {
|
||||
this.hovered = true;
|
||||
this.updateButtonAppearance();
|
||||
});
|
||||
this.on(Phaser.Input.Events.POINTER_OUT, () => {
|
||||
this.hovered = false;
|
||||
this.pressed = false;
|
||||
this.updateButtonAppearance();
|
||||
});
|
||||
this.on(Phaser.Input.Events.POINTER_DOWN, () => {
|
||||
this.pressed = true;
|
||||
this.updateButtonAppearance();
|
||||
});
|
||||
this.on(Phaser.Input.Events.POINTER_UP, () => {
|
||||
this.pressed = false;
|
||||
this.updateButtonAppearance();
|
||||
});
|
||||
}
|
||||
}
|
115
front/src/Phaser/Components/Ui/IconButton.ts
Normal file
@ -0,0 +1,115 @@
|
||||
export interface IconButtonConfig {
|
||||
width: number;
|
||||
height: number;
|
||||
iconTextureKey: string;
|
||||
idle: IconButtonAppearanceConfig;
|
||||
hover: IconButtonAppearanceConfig;
|
||||
pressed: IconButtonAppearanceConfig;
|
||||
selected: IconButtonAppearanceConfig;
|
||||
}
|
||||
|
||||
export interface IconButtonAppearanceConfig {
|
||||
color: number;
|
||||
borderThickness: number;
|
||||
borderColor: number;
|
||||
}
|
||||
|
||||
export enum IconButtonEvent {
|
||||
Clicked = "IconButton:Clicked",
|
||||
}
|
||||
|
||||
export class IconButton extends Phaser.GameObjects.Container {
|
||||
private background: Phaser.GameObjects.Graphics;
|
||||
private icon: Phaser.GameObjects.Image;
|
||||
|
||||
private config: IconButtonConfig;
|
||||
|
||||
private hovered: boolean = false;
|
||||
private pressed: boolean = false;
|
||||
private selected: boolean = false;
|
||||
|
||||
constructor(scene: Phaser.Scene, x: number, y: number, config: IconButtonConfig) {
|
||||
super(scene, x, y);
|
||||
|
||||
this.config = config;
|
||||
|
||||
this.background = this.scene.add.graphics();
|
||||
this.icon = this.scene.add.image(0, 0, this.config.iconTextureKey);
|
||||
this.drawBackground(this.config.idle);
|
||||
|
||||
this.add([this.background, this.icon]);
|
||||
|
||||
this.setSize(this.config.width, this.config.height);
|
||||
this.setInteractive({ cursor: "pointer" });
|
||||
|
||||
this.bindEventHandlers();
|
||||
|
||||
this.scene.add.existing(this);
|
||||
}
|
||||
|
||||
public select(select: boolean = true): void {
|
||||
if (this.selected === select) {
|
||||
return;
|
||||
}
|
||||
this.selected = select;
|
||||
this.updateButtonAppearance();
|
||||
}
|
||||
|
||||
private updateButtonAppearance(): void {
|
||||
if (this.selected) {
|
||||
this.drawBackground(this.config.selected);
|
||||
return;
|
||||
}
|
||||
if (this.pressed) {
|
||||
this.drawBackground(this.config.pressed);
|
||||
return;
|
||||
}
|
||||
if (this.hovered) {
|
||||
this.drawBackground(this.config.hover);
|
||||
return;
|
||||
}
|
||||
this.drawBackground(this.config.idle);
|
||||
}
|
||||
|
||||
private drawBackground(appearance: IconButtonAppearanceConfig): void {
|
||||
this.background.clear();
|
||||
this.background.fillStyle(appearance.color);
|
||||
this.background.lineStyle(appearance.borderThickness, appearance.borderColor);
|
||||
|
||||
const w = this.config.width;
|
||||
const h = this.config.height;
|
||||
|
||||
this.background.fillRect(-w / 2, -h / 2, w, h);
|
||||
this.background.strokeRect(-w / 2, -h / 2, w, h);
|
||||
}
|
||||
|
||||
private bindEventHandlers(): void {
|
||||
this.on(Phaser.Input.Events.POINTER_OVER, () => {
|
||||
if (this.selected) {
|
||||
return;
|
||||
}
|
||||
this.hovered = true;
|
||||
this.updateButtonAppearance();
|
||||
});
|
||||
this.on(Phaser.Input.Events.POINTER_OUT, () => {
|
||||
this.hovered = false;
|
||||
this.pressed = false;
|
||||
this.updateButtonAppearance();
|
||||
});
|
||||
this.on(Phaser.Input.Events.POINTER_DOWN, () => {
|
||||
if (this.selected) {
|
||||
return;
|
||||
}
|
||||
this.pressed = true;
|
||||
this.updateButtonAppearance();
|
||||
});
|
||||
this.on(Phaser.Input.Events.POINTER_UP, () => {
|
||||
if (this.selected) {
|
||||
return;
|
||||
}
|
||||
this.pressed = false;
|
||||
this.updateButtonAppearance();
|
||||
this.emit(IconButtonEvent.Clicked, this.selected);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,4 +1,9 @@
|
||||
import { PlayerAnimationDirections, PlayerAnimationTypes } from "../Player/Animation";
|
||||
import {
|
||||
AnimationData,
|
||||
getPlayerAnimations,
|
||||
PlayerAnimationDirections,
|
||||
PlayerAnimationTypes,
|
||||
} from "../Player/Animation";
|
||||
import { SpeechBubble } from "./SpeechBubble";
|
||||
import Text = Phaser.GameObjects.Text;
|
||||
import Container = Phaser.GameObjects.Container;
|
||||
@ -21,15 +26,6 @@ import { TalkIcon } from "../Components/TalkIcon";
|
||||
import { Deferred } from "ts-deferred";
|
||||
|
||||
const playerNameY = -25;
|
||||
|
||||
interface AnimationData {
|
||||
key: string;
|
||||
frameRate: number;
|
||||
repeat: number;
|
||||
frameModel: string; //todo use an enum
|
||||
frames: number[];
|
||||
}
|
||||
|
||||
const interactiveRadius = 35;
|
||||
|
||||
export abstract class Character extends Container implements OutlineableInterface {
|
||||
@ -248,7 +244,7 @@ export abstract class Character extends Container implements OutlineableInterfac
|
||||
}
|
||||
}
|
||||
|
||||
public addTextures(textures: string[], frame?: string | number): void {
|
||||
private addTextures(textures: string[], frame?: string | number): void {
|
||||
if (textures.length < 1) {
|
||||
throw new TextureError("no texture given");
|
||||
}
|
||||
@ -259,7 +255,7 @@ export abstract class Character extends Container implements OutlineableInterfac
|
||||
}
|
||||
const sprite = new Sprite(this.scene, 0, 0, texture, frame);
|
||||
this.add(sprite);
|
||||
this.getPlayerAnimations(texture).forEach((d) => {
|
||||
getPlayerAnimations(texture).forEach((d) => {
|
||||
this.scene.anims.create({
|
||||
key: d.key,
|
||||
frames: this.scene.anims.generateFrameNumbers(d.frameModel, { frames: d.frames }),
|
||||
@ -279,67 +275,6 @@ export abstract class Character extends Container implements OutlineableInterfac
|
||||
return this.scene.plugins.get("rexOutlinePipeline") as unknown as OutlinePipelinePlugin | undefined;
|
||||
}
|
||||
|
||||
private getPlayerAnimations(name: string): AnimationData[] {
|
||||
return [
|
||||
{
|
||||
key: `${name}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Walk}`,
|
||||
frameModel: name,
|
||||
frames: [0, 1, 2, 1],
|
||||
frameRate: 10,
|
||||
repeat: -1,
|
||||
},
|
||||
{
|
||||
key: `${name}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Walk}`,
|
||||
frameModel: name,
|
||||
frames: [3, 4, 5, 4],
|
||||
frameRate: 10,
|
||||
repeat: -1,
|
||||
},
|
||||
{
|
||||
key: `${name}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Walk}`,
|
||||
frameModel: name,
|
||||
frames: [6, 7, 8, 7],
|
||||
frameRate: 10,
|
||||
repeat: -1,
|
||||
},
|
||||
{
|
||||
key: `${name}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Walk}`,
|
||||
frameModel: name,
|
||||
frames: [9, 10, 11, 10],
|
||||
frameRate: 10,
|
||||
repeat: -1,
|
||||
},
|
||||
{
|
||||
key: `${name}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Idle}`,
|
||||
frameModel: name,
|
||||
frames: [1],
|
||||
frameRate: 10,
|
||||
repeat: 1,
|
||||
},
|
||||
{
|
||||
key: `${name}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Idle}`,
|
||||
frameModel: name,
|
||||
frames: [4],
|
||||
frameRate: 10,
|
||||
repeat: 1,
|
||||
},
|
||||
{
|
||||
key: `${name}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Idle}`,
|
||||
frameModel: name,
|
||||
frames: [7],
|
||||
frameRate: 10,
|
||||
repeat: 1,
|
||||
},
|
||||
{
|
||||
key: `${name}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Idle}`,
|
||||
frameModel: name,
|
||||
frames: [10],
|
||||
frameRate: 10,
|
||||
repeat: 1,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
protected playAnimation(direction: PlayerAnimationDirections, moving: boolean): void {
|
||||
if (this.invisible) return;
|
||||
for (const [texture, sprite] of this.sprites.entries()) {
|
||||
|
@ -1,20 +0,0 @@
|
||||
import Container = Phaser.GameObjects.Container;
|
||||
import type { Scene } from "phaser";
|
||||
import Sprite = Phaser.GameObjects.Sprite;
|
||||
|
||||
/**
|
||||
* A sprite of a customized character (used in the Customize Scene only)
|
||||
*/
|
||||
export class CustomizedCharacter extends Container {
|
||||
public constructor(scene: Scene, x: number, y: number, layers: string[]) {
|
||||
super(scene, x, y);
|
||||
this.updateSprites(layers);
|
||||
}
|
||||
|
||||
public updateSprites(layers: string[]): void {
|
||||
this.removeAll(true);
|
||||
for (const layer of layers) {
|
||||
this.add(new Sprite(this.scene, 0, 0, layer));
|
||||
}
|
||||
}
|
||||
}
|
@ -54,7 +54,6 @@ import type {
|
||||
MessageUserMovedInterface,
|
||||
MessageUserPositionInterface,
|
||||
OnConnectInterface,
|
||||
PlayerDetailsUpdatedMessageInterface,
|
||||
PointInterface,
|
||||
PositionInterface,
|
||||
RoomJoinedMessageInterface,
|
||||
@ -103,6 +102,7 @@ import type { VideoPeer } from "../../WebRtc/VideoPeer";
|
||||
import CancelablePromise from "cancelable-promise";
|
||||
import { Deferred } from "ts-deferred";
|
||||
import { SuperLoaderPlugin } from "../Services/SuperLoaderPlugin";
|
||||
import { PlayerDetailsUpdatedMessage } from "../../Messages/ts-proto-generated/protos/messages";
|
||||
export interface GameSceneInitInterface {
|
||||
initPosition: PointInterface | null;
|
||||
reconnecting: boolean;
|
||||
@ -140,7 +140,7 @@ interface DeleteGroupEventInterface {
|
||||
|
||||
interface PlayerDetailsUpdatedInterface {
|
||||
type: "PlayerDetailsUpdated";
|
||||
details: PlayerDetailsUpdatedMessageInterface;
|
||||
details: PlayerDetailsUpdatedMessage;
|
||||
}
|
||||
|
||||
export class GameScene extends DirtyScene {
|
||||
@ -176,7 +176,6 @@ export class GameScene extends DirtyScene {
|
||||
private emoteUnsubscribe!: Unsubscriber;
|
||||
private emoteMenuUnsubscribe!: Unsubscriber;
|
||||
|
||||
private volumeStoreUnsubscribers: Map<number, Unsubscriber> = new Map<number, Unsubscriber>();
|
||||
private localVolumeStoreUnsubscriber: Unsubscriber | undefined;
|
||||
private followUsersColorStoreUnsubscribe!: Unsubscriber;
|
||||
private currentPlayerGroupIdStoreUnsubscribe!: Unsubscriber;
|
||||
@ -221,6 +220,7 @@ export class GameScene extends DirtyScene {
|
||||
private loader: Loader;
|
||||
private lastCameraEvent: WasCameraUpdatedEvent | undefined;
|
||||
private firstCameraUpdateSent: boolean = false;
|
||||
private showVoiceIndicatorChangeMessageSent: boolean = false;
|
||||
private currentPlayerGroupId?: number;
|
||||
public readonly superLoad: SuperLoaderPlugin;
|
||||
|
||||
@ -650,55 +650,43 @@ export class GameScene extends DirtyScene {
|
||||
}
|
||||
|
||||
const talkIconVolumeTreshold = 10;
|
||||
const oldPeers = new Map<number, VideoPeer>();
|
||||
let oldPeersNumber = 0;
|
||||
this.peerStoreUnsubscribe = peerStore.subscribe((peers) => {
|
||||
this.volumeStoreUnsubscribers.forEach((unsubscribe) => unsubscribe());
|
||||
this.volumeStoreUnsubscribers.clear();
|
||||
|
||||
for (const [key, videoStream] of peers) {
|
||||
this.volumeStoreUnsubscribers.set(
|
||||
key,
|
||||
videoStream.volumeStore.subscribe((volume) => {
|
||||
if (volume) {
|
||||
this.MapPlayersByKey.get(key)?.showTalkIcon(volume > talkIconVolumeTreshold);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const newPeerNumber = peers.size;
|
||||
if (newPeerNumber > oldPeers.size) {
|
||||
if (newPeerNumber > oldPeersNumber) {
|
||||
this.playSound("audio-webrtc-in");
|
||||
} else if (newPeerNumber < oldPeers.size) {
|
||||
} else if (newPeerNumber < oldPeersNumber) {
|
||||
this.playSound("audio-webrtc-out");
|
||||
const oldPeersKeys = oldPeers.keys();
|
||||
const newPeersKeys = Array.from(peers.keys());
|
||||
for (const oldKey of oldPeersKeys) {
|
||||
if (!newPeersKeys.includes(oldKey)) {
|
||||
this.MapPlayersByKey.get(oldKey)?.showTalkIcon(false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (newPeerNumber > 0) {
|
||||
if (!this.localVolumeStoreUnsubscriber) {
|
||||
this.localVolumeStoreUnsubscriber = localVolumeStore.subscribe((volume) => {
|
||||
if (volume) {
|
||||
this.CurrentPlayer.showTalkIcon(volume > talkIconVolumeTreshold);
|
||||
if (volume === undefined) {
|
||||
return;
|
||||
}
|
||||
const aboveTreshold = volume > talkIconVolumeTreshold;
|
||||
this.CurrentPlayer.showTalkIcon(aboveTreshold);
|
||||
|
||||
if (this.showVoiceIndicatorChangeMessageSent && !aboveTreshold) {
|
||||
this.connection?.emitPlayerShowVoiceIndicator(false);
|
||||
this.showVoiceIndicatorChangeMessageSent = false;
|
||||
} else if (!this.showVoiceIndicatorChangeMessageSent && aboveTreshold) {
|
||||
this.connection?.emitPlayerShowVoiceIndicator(true);
|
||||
this.showVoiceIndicatorChangeMessageSent = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.CurrentPlayer.showTalkIcon(false, true);
|
||||
this.connection?.emitPlayerShowVoiceIndicator(false);
|
||||
this.showVoiceIndicatorChangeMessageSent = false;
|
||||
this.MapPlayersByKey.forEach((remotePlayer) => remotePlayer.showTalkIcon(false, true));
|
||||
if (this.localVolumeStoreUnsubscriber) {
|
||||
this.localVolumeStoreUnsubscriber();
|
||||
this.localVolumeStoreUnsubscriber = undefined;
|
||||
}
|
||||
}
|
||||
oldPeers.clear();
|
||||
for (const [key, val] of peers) {
|
||||
oldPeers.set(key, val);
|
||||
}
|
||||
oldPeersNumber = peers.size;
|
||||
});
|
||||
|
||||
this.emoteUnsubscribe = emoteStore.subscribe((emote) => {
|
||||
@ -850,11 +838,7 @@ export class GameScene extends DirtyScene {
|
||||
}
|
||||
this.pendingEvents.enqueue({
|
||||
type: "PlayerDetailsUpdated",
|
||||
details: {
|
||||
userId: message.userId,
|
||||
outlineColor: message.details.outlineColor,
|
||||
removeOutlineColor: message.details.removeOutlineColor,
|
||||
},
|
||||
details: message,
|
||||
});
|
||||
});
|
||||
|
||||
@ -2105,7 +2089,7 @@ export class GameScene extends DirtyScene {
|
||||
this.groups.delete(groupId);
|
||||
}
|
||||
|
||||
doUpdatePlayerDetails(message: PlayerDetailsUpdatedMessageInterface): void {
|
||||
doUpdatePlayerDetails(message: PlayerDetailsUpdatedMessage): void {
|
||||
const character = this.MapPlayersByKey.get(message.userId);
|
||||
if (character === undefined) {
|
||||
console.log(
|
||||
@ -2115,10 +2099,13 @@ export class GameScene extends DirtyScene {
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (message.removeOutlineColor) {
|
||||
if (message.details?.removeOutlineColor) {
|
||||
character.removeApiOutlineColor();
|
||||
} else {
|
||||
character.setApiOutlineColor(message.outlineColor);
|
||||
} else if (message.details?.outlineColor !== undefined) {
|
||||
character.setApiOutlineColor(message.details?.outlineColor);
|
||||
}
|
||||
if (message.details?.showVoiceIndicator !== undefined) {
|
||||
character.showTalkIcon(message.details?.showVoiceIndicator);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,4 +31,40 @@ export class TexturesHelper {
|
||||
throw new Error("Could not get the snapshot");
|
||||
}
|
||||
}
|
||||
|
||||
public static createFloorRectangleTexture(
|
||||
scene: Phaser.Scene,
|
||||
newTextureKey: string,
|
||||
width: number,
|
||||
height: number,
|
||||
sourceTextureKey: string,
|
||||
sourceTextureFrame?: number | string,
|
||||
sourceTextureWidth: number = 32,
|
||||
sourceTextureHeight: number = 32
|
||||
): void {
|
||||
const rt = scene.make.renderTexture({ x: 0, y: 0, width, height }, false);
|
||||
const widthTiles = Math.ceil(width / sourceTextureWidth);
|
||||
const heightTiles = Math.ceil(height / sourceTextureHeight);
|
||||
|
||||
for (let x = 0; x < widthTiles; x += 1) {
|
||||
for (let y = 0; y < heightTiles; y += 1) {
|
||||
rt.drawFrame(sourceTextureKey, sourceTextureFrame, x * 32, y * 32);
|
||||
}
|
||||
}
|
||||
|
||||
rt.saveTexture(newTextureKey);
|
||||
rt.destroy();
|
||||
}
|
||||
|
||||
public static createRectangleTexture(
|
||||
scene: Phaser.Scene,
|
||||
textureKey: string,
|
||||
width: number,
|
||||
height: number,
|
||||
color: number
|
||||
): void {
|
||||
const rectangleTexture = scene.add.graphics().fillStyle(color, 1).fillRect(0, 0, width, height);
|
||||
rectangleTexture.generateTexture(textureKey, width, height);
|
||||
rectangleTexture.destroy();
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,5 @@
|
||||
import { ResizableScene } from "./ResizableScene";
|
||||
import { BodyResourceDescriptionInterface, PlayerTexturesKey } from "../Entity/PlayerTextures";
|
||||
import { loadWokaTexture } from "../Entity/PlayerTexturesLoadingManager";
|
||||
import type CancelablePromise from "cancelable-promise";
|
||||
import { PlayerTextures } from "../Entity/PlayerTextures";
|
||||
import Texture = Phaser.Textures.Texture;
|
||||
import { SuperLoaderPlugin } from "../Services/SuperLoaderPlugin";
|
||||
|
||||
export abstract class AbstractCharacterScene extends ResizableScene {
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { EnableCameraSceneName } from "./EnableCameraScene";
|
||||
import Rectangle = Phaser.GameObjects.Rectangle;
|
||||
import { loadAllLayers } from "../Entity/PlayerTexturesLoadingManager";
|
||||
import Sprite = Phaser.GameObjects.Sprite;
|
||||
import { gameManager } from "../Game/GameManager";
|
||||
import { localUserStore } from "../../Connexion/LocalUserStore";
|
||||
import { Loader } from "../Components/Loader";
|
||||
@ -9,31 +7,46 @@ import type { BodyResourceDescriptionInterface } from "../Entity/PlayerTextures"
|
||||
import { AbstractCharacterScene } from "./AbstractCharacterScene";
|
||||
import { areCharacterLayersValid } from "../../Connexion/LocalUser";
|
||||
import { SelectCharacterSceneName } from "./SelectCharacterScene";
|
||||
import { activeRowStore, customCharacterSceneVisibleStore } from "../../Stores/CustomCharacterStore";
|
||||
import { waScaleManager } from "../Services/WaScaleManager";
|
||||
import { CustomizedCharacter } from "../Entity/CustomizedCharacter";
|
||||
import { get } from "svelte/store";
|
||||
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 } 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 Rectangle!: Rectangle;
|
||||
private customWokaPreviewer!: CustomWokaPreviewer;
|
||||
private bodyPartsDraggableGridLeftShadow!: Phaser.GameObjects.Image;
|
||||
private bodyPartsDraggableGridRightShadow!: Phaser.GameObjects.Image;
|
||||
private bodyPartsDraggableGrid!: DraggableGrid;
|
||||
private bodyPartsButtons!: Record<CustomWokaBodyPart, IconButton>;
|
||||
|
||||
private selectedLayers: number[] = [0];
|
||||
private containersRow: CustomizedCharacter[][] = [];
|
||||
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 moveHorizontally: number = 0;
|
||||
private moveVertically: number = 0;
|
||||
|
||||
private loader: Loader;
|
||||
|
||||
private readonly SLOT_DIMENSION = 100;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
key: CustomizeSceneName,
|
||||
@ -41,7 +54,20 @@ export class CustomizeScene extends AbstractCharacterScene {
|
||||
this.loader = new Loader(this);
|
||||
}
|
||||
|
||||
preload() {
|
||||
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
|
||||
@ -68,212 +94,34 @@ export class CustomizeScene extends AbstractCharacterScene {
|
||||
this.loader.addLoader();
|
||||
}
|
||||
|
||||
create() {
|
||||
customCharacterSceneVisibleStore.set(true);
|
||||
this.events.addListener("wake", () => {
|
||||
waScaleManager.saveZoom();
|
||||
waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 3 : 1;
|
||||
customCharacterSceneVisibleStore.set(true);
|
||||
});
|
||||
public create(): void {
|
||||
this.createSlotBackgroundTextures();
|
||||
this.initializeCustomWokaPreviewer();
|
||||
this.initializeBodyPartsDraggableGrid();
|
||||
this.initializeEdgeShadows();
|
||||
this.initializeBodyPartsButtons();
|
||||
this.initializeRandomizeButton();
|
||||
this.initializeFinishButton();
|
||||
|
||||
waScaleManager.saveZoom();
|
||||
waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 3 : 1;
|
||||
this.selectedBodyPartType = CustomWokaBodyPart.Body;
|
||||
this.bodyPartsButtons.Body.select();
|
||||
|
||||
this.Rectangle = this.add.rectangle(
|
||||
this.cameras.main.worldView.x + this.cameras.main.width / 2,
|
||||
this.cameras.main.worldView.y + this.cameras.main.height / 3,
|
||||
32,
|
||||
33
|
||||
);
|
||||
this.Rectangle.setStrokeStyle(2, 0xffffff);
|
||||
this.add.existing(this.Rectangle);
|
||||
|
||||
this.createCustomizeLayer(0, 0, 0);
|
||||
this.createCustomizeLayer(0, 0, 1);
|
||||
this.createCustomizeLayer(0, 0, 2);
|
||||
this.createCustomizeLayer(0, 0, 3);
|
||||
this.createCustomizeLayer(0, 0, 4);
|
||||
this.createCustomizeLayer(0, 0, 5);
|
||||
|
||||
this.moveLayers();
|
||||
this.input.keyboard.on("keyup-ENTER", () => {
|
||||
this.nextSceneToCamera();
|
||||
});
|
||||
this.input.keyboard.on("keyup-BACKSPACE", () => {
|
||||
this.backToPreviousScene();
|
||||
});
|
||||
|
||||
// Note: the key bindings are not directly put on the moveCursorVertically or moveCursorHorizontally methods
|
||||
// because if 2 such events are fired close to one another, it makes the whole application crawl to a halt (for a reason I cannot
|
||||
// explain, the list of sprites managed by the update list become immense
|
||||
this.input.keyboard.on("keyup-RIGHT", () => (this.moveHorizontally = 1));
|
||||
this.input.keyboard.on("keyup-LEFT", () => (this.moveHorizontally = -1));
|
||||
this.input.keyboard.on("keyup-DOWN", () => (this.moveVertically = 1));
|
||||
this.input.keyboard.on("keyup-UP", () => (this.moveVertically = -1));
|
||||
|
||||
const customCursorPosition = localUserStore.getCustomCursorPosition();
|
||||
if (customCursorPosition) {
|
||||
activeRowStore.set(customCursorPosition.activeRow);
|
||||
this.selectedLayers = customCursorPosition.selectedLayers;
|
||||
this.moveLayers();
|
||||
this.updateSelectedLayer();
|
||||
}
|
||||
this.bindEventHandlers();
|
||||
|
||||
this.refreshPlayerCurrentOutfit();
|
||||
this.onResize();
|
||||
}
|
||||
|
||||
public moveCursorHorizontally(index: number): void {
|
||||
this.moveHorizontally = index;
|
||||
}
|
||||
|
||||
public moveCursorVertically(index: number): void {
|
||||
this.moveVertically = index;
|
||||
}
|
||||
|
||||
private doMoveCursorHorizontally(index: number): void {
|
||||
this.selectedLayers[get(activeRowStore)] += index;
|
||||
if (this.selectedLayers[get(activeRowStore)] < 0) {
|
||||
this.selectedLayers[get(activeRowStore)] = 0;
|
||||
} else if (this.selectedLayers[get(activeRowStore)] > this.layers[get(activeRowStore)].length - 1) {
|
||||
this.selectedLayers[get(activeRowStore)] = this.layers[get(activeRowStore)].length - 1;
|
||||
}
|
||||
this.moveLayers();
|
||||
this.updateSelectedLayer();
|
||||
this.saveInLocalStorage();
|
||||
}
|
||||
|
||||
private doMoveCursorVertically(index: number): void {
|
||||
activeRowStore.set(get(activeRowStore) + index);
|
||||
if (get(activeRowStore) < 0) {
|
||||
activeRowStore.set(0);
|
||||
} else if (get(activeRowStore) > this.layers.length - 1) {
|
||||
activeRowStore.set(this.layers.length - 1);
|
||||
}
|
||||
this.moveLayers();
|
||||
this.saveInLocalStorage();
|
||||
}
|
||||
|
||||
private saveInLocalStorage() {
|
||||
localUserStore.setCustomCursorPosition(get(activeRowStore), this.selectedLayers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x, the layer's vertical position
|
||||
* @param y, the layer's horizontal position
|
||||
* @param layerNumber, index of the this.layers array
|
||||
* create the layer and display it on the scene
|
||||
*/
|
||||
private createCustomizeLayer(x: number, y: number, layerNumber: number): void {
|
||||
this.containersRow[layerNumber] = [];
|
||||
this.selectedLayers[layerNumber] = 0;
|
||||
let alpha = 0;
|
||||
let layerPosX = 0;
|
||||
for (let i = 0; i < this.layers[layerNumber].length; i++) {
|
||||
const container = this.generateCharacter(300 + x + layerPosX, y, layerNumber, i);
|
||||
|
||||
this.containersRow[layerNumber][i] = container;
|
||||
this.add.existing(container);
|
||||
layerPosX += 30;
|
||||
alpha += 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a character from the current selected items BUT replaces
|
||||
* one layer item with an item we pass in parameter.
|
||||
*
|
||||
* Current selected items are fetched from this.selectedLayers
|
||||
*
|
||||
* @param x,
|
||||
* @param y,
|
||||
* @param layerNumber, The selected layer number (0 for body...)
|
||||
* @param selectedItem, The number of the item select (0 for black body...)
|
||||
*/
|
||||
private generateCharacter(x: number, y: number, layerNumber: number, selectedItem: number) {
|
||||
return new CustomizedCharacter(this, x, y, this.getContainerChildren(layerNumber, selectedItem));
|
||||
}
|
||||
|
||||
private getContainerChildren(layerNumber: number, selectedItem: number): Array<string> {
|
||||
const children: Array<string> = new Array<string>();
|
||||
for (let j = 0; j <= layerNumber; j++) {
|
||||
if (j === layerNumber) {
|
||||
children.push(this.layers[j][selectedItem].id);
|
||||
} else {
|
||||
const layer = this.selectedLayers[j];
|
||||
if (layer === undefined) {
|
||||
continue;
|
||||
}
|
||||
children.push(this.layers[j][layer].id);
|
||||
}
|
||||
}
|
||||
return children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the layer left, right, up and down and update the selected layer
|
||||
*/
|
||||
private moveLayers(): void {
|
||||
const screenCenterX = this.cameras.main.worldView.x + this.cameras.main.width / 2;
|
||||
const screenCenterY = this.cameras.main.worldView.y + this.cameras.main.height / 3;
|
||||
const screenWidth = this.game.renderer.width;
|
||||
const screenHeight = this.game.renderer.height;
|
||||
for (let i = 0; i < this.containersRow.length; i++) {
|
||||
for (let j = 0; j < this.containersRow[i].length; j++) {
|
||||
let selectedX = this.selectedLayers[i];
|
||||
if (selectedX === undefined) {
|
||||
selectedX = 0;
|
||||
}
|
||||
this.containersRow[i][j].x = screenCenterX + (j - selectedX) * 40;
|
||||
this.containersRow[i][j].y = screenCenterY + (i - get(activeRowStore)) * 40;
|
||||
const alpha1 = (Math.abs(selectedX - j) * 47 * 2) / screenWidth;
|
||||
const alpha2 = (Math.abs(get(activeRowStore) - i) * 49 * 2) / screenHeight;
|
||||
this.containersRow[i][j].setAlpha((1 - alpha1) * (1 - alpha2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x, the sprite's vertical position
|
||||
* @param y, the sprites's horizontal position
|
||||
* @param name, the sprite's name
|
||||
* @return a new sprite
|
||||
*/
|
||||
private generateLayers(x: number, y: number, name: string): Sprite {
|
||||
//return new Sprite(this, x, y, name);
|
||||
return this.add.sprite(0, 0, name);
|
||||
}
|
||||
|
||||
private updateSelectedLayer() {
|
||||
for (let i = 0; i < this.containersRow.length; i++) {
|
||||
for (let j = 0; j < this.containersRow[i].length; j++) {
|
||||
const children = this.getContainerChildren(i, j);
|
||||
this.containersRow[i][j].updateSprites(children);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update(time: number, delta: number): void {
|
||||
if (this.lazyloadingAttempt) {
|
||||
this.moveLayers();
|
||||
this.doMoveCursorHorizontally(this.moveHorizontally);
|
||||
this.lazyloadingAttempt = false;
|
||||
}
|
||||
|
||||
if (this.moveHorizontally !== 0) {
|
||||
this.doMoveCursorHorizontally(this.moveHorizontally);
|
||||
this.moveHorizontally = 0;
|
||||
}
|
||||
if (this.moveVertically !== 0) {
|
||||
this.doMoveCursorVertically(this.moveVertically);
|
||||
this.moveVertically = 0;
|
||||
}
|
||||
public update(time: number, dt: number): void {
|
||||
this.customWokaPreviewer.update();
|
||||
}
|
||||
|
||||
public onResize(): void {
|
||||
this.moveLayers();
|
||||
|
||||
this.Rectangle.x = this.cameras.main.worldView.x + this.cameras.main.width / 2;
|
||||
this.Rectangle.y = this.cameras.main.worldView.y + this.cameras.main.height / 3;
|
||||
this.handleCustomWokaPreviewerOnResize();
|
||||
this.handleBodyPartButtonsOnResize();
|
||||
this.handleRandomizeButtonOnResize();
|
||||
this.handleFinishButtonOnResize();
|
||||
this.handleBodyPartsDraggableGridOnResize();
|
||||
}
|
||||
|
||||
public nextSceneToCamera() {
|
||||
@ -295,13 +143,532 @@ export class CustomizeScene extends AbstractCharacterScene {
|
||||
this.scene.stop(CustomizeSceneName);
|
||||
waScaleManager.restoreZoom();
|
||||
gameManager.tryResumingGame(EnableCameraSceneName);
|
||||
customCharacterSceneVisibleStore.set(false);
|
||||
}
|
||||
|
||||
public backToPreviousScene() {
|
||||
this.scene.stop(CustomizeSceneName);
|
||||
waScaleManager.restoreZoom();
|
||||
this.scene.run(SelectCharacterSceneName);
|
||||
customCharacterSceneVisibleStore.set(false);
|
||||
}
|
||||
|
||||
private createSlotBackgroundTextures(): void {
|
||||
for (let i = 0; i < 4; i += 1) {
|
||||
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.events.addListener("wake", () => {
|
||||
waScaleManager.saveZoom();
|
||||
waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 3 : 1;
|
||||
});
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { gameManager } from "../Game/GameManager";
|
||||
import { Scene } from "phaser";
|
||||
import { ErrorScene, ErrorSceneName } from "../Reconnecting/ErrorScene";
|
||||
import { ErrorScene } from "../Reconnecting/ErrorScene";
|
||||
import { WAError } from "../Reconnecting/WAError";
|
||||
import { waScaleManager } from "../Services/WaScaleManager";
|
||||
import { ReconnectingTextures } from "../Reconnecting/ReconnectingScene";
|
||||
|
@ -145,7 +145,6 @@ export class SelectCharacterScene extends AbstractCharacterScene {
|
||||
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;
|
||||
|
@ -8,3 +8,72 @@ export enum PlayerAnimationTypes {
|
||||
Walk = "walk",
|
||||
Idle = "idle",
|
||||
}
|
||||
|
||||
export interface AnimationData {
|
||||
key: string;
|
||||
frameRate: number;
|
||||
repeat: number;
|
||||
frameModel: string; //todo use an enum
|
||||
frames: number[];
|
||||
}
|
||||
|
||||
export function getPlayerAnimations(name: string): AnimationData[] {
|
||||
return [
|
||||
{
|
||||
key: `${name}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Walk}`,
|
||||
frameModel: name,
|
||||
frames: [0, 1, 2, 1],
|
||||
frameRate: 10,
|
||||
repeat: -1,
|
||||
},
|
||||
{
|
||||
key: `${name}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Walk}`,
|
||||
frameModel: name,
|
||||
frames: [3, 4, 5, 4],
|
||||
frameRate: 10,
|
||||
repeat: -1,
|
||||
},
|
||||
{
|
||||
key: `${name}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Walk}`,
|
||||
frameModel: name,
|
||||
frames: [6, 7, 8, 7],
|
||||
frameRate: 10,
|
||||
repeat: -1,
|
||||
},
|
||||
{
|
||||
key: `${name}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Walk}`,
|
||||
frameModel: name,
|
||||
frames: [9, 10, 11, 10],
|
||||
frameRate: 10,
|
||||
repeat: -1,
|
||||
},
|
||||
{
|
||||
key: `${name}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Idle}`,
|
||||
frameModel: name,
|
||||
frames: [1],
|
||||
frameRate: 10,
|
||||
repeat: 1,
|
||||
},
|
||||
{
|
||||
key: `${name}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Idle}`,
|
||||
frameModel: name,
|
||||
frames: [4],
|
||||
frameRate: 10,
|
||||
repeat: 1,
|
||||
},
|
||||
{
|
||||
key: `${name}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Idle}`,
|
||||
frameModel: name,
|
||||
frames: [7],
|
||||
frameRate: 10,
|
||||
repeat: 1,
|
||||
},
|
||||
{
|
||||
key: `${name}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Idle}`,
|
||||
frameModel: name,
|
||||
frames: [10],
|
||||
frameRate: 10,
|
||||
repeat: 1,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
@ -128,6 +128,10 @@ export class WaScaleManager {
|
||||
this.applyNewSize();
|
||||
}
|
||||
|
||||
public getActualZoom(): number {
|
||||
return this.actualZoom;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used to scale back the ui components to counter-act the zoom.
|
||||
*/
|
||||
|
@ -1,5 +1,3 @@
|
||||
import { derived, writable, Writable } from "svelte/store";
|
||||
|
||||
export const customCharacterSceneVisibleStore = writable(false);
|
||||
|
||||
export const activeRowStore = writable(0);
|
||||
|
@ -10,8 +10,6 @@ import { chatMessagesStore, newChatMessageSubject } from "../Stores/ChatStore";
|
||||
import { getIceServersConfig } from "../Components/Video/utils";
|
||||
import { isMediaBreakpointUp } from "../Utils/BreakpointsUtils";
|
||||
import { SoundMeter } from "../Phaser/Components/SoundMeter";
|
||||
import { AudioContext } from "standardized-audio-context";
|
||||
import { Console } from "console";
|
||||
import Peer from "simple-peer/simplepeer.min.js";
|
||||
import { Buffer } from "buffer";
|
||||
|
||||
|
@ -104,7 +104,6 @@ const config: GameConfig = {
|
||||
dom: {
|
||||
createContainer: true,
|
||||
},
|
||||
disableContextMenu: true,
|
||||
render: {
|
||||
pixelArt: true,
|
||||
roundPixels: true,
|
||||
@ -142,6 +141,14 @@ const game = new Game(config);
|
||||
|
||||
waScaleManager.setGame(game);
|
||||
|
||||
/*
|
||||
TODO: replace with disableContextMenu when Phaser does not disable context menu on document.body
|
||||
see https://github.com/photonstorm/phaser/issues/6064
|
||||
*/
|
||||
HtmlUtils.querySelectorOrFail("#game canvas").addEventListener("contextmenu", function (e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
window.addEventListener("resize", function (event) {
|
||||
coWebsiteManager.resetStyleMain();
|
||||
|
||||
|
@ -72,10 +72,17 @@
|
||||
dependencies:
|
||||
"@fortawesome/fontawesome-common-types" "^0.2.35"
|
||||
|
||||
"@geprog/vite-plugin-env-config@^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@geprog/vite-plugin-env-config/-/vite-plugin-env-config-4.0.0.tgz#989d95f23fbab5eae7c4c96d04a18abdc289b81e"
|
||||
integrity sha512-25ZMNdpssqkyv1sxfa6gBhmL8yCxCqjRRc1c05GJfhPkqD6Cn9dnG6xnHHHfJaEqrDFCViD0Bcnr+tgs76OZ2Q==
|
||||
"@geprog/vite-plugin-env-config@^4.0.3":
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@geprog/vite-plugin-env-config/-/vite-plugin-env-config-4.0.3.tgz#ca04bd9ad9f55fe568917db79266afe8e766e25e"
|
||||
integrity sha512-2HDCV+6XXJjSuBAhDWLRr111buMQ3bIZrKo3dymIhEJ4oJCC/3yDqg7HDQIn8Y8KKbsM0AtuHMZW4yz2tPBsYg==
|
||||
|
||||
"@home-based-studio/phaser3-utils@^0.4.2":
|
||||
version "0.4.2"
|
||||
resolved "https://registry.yarnpkg.com/@home-based-studio/phaser3-utils/-/phaser3-utils-0.4.2.tgz#b2c1815a6b51321ea8dab027b5badcf714d99fd6"
|
||||
integrity sha512-S0VkAq3z0Kf0vEUUyCDes911icvc+nkUq7lGp23zD/5lk7LTGM51NswSAfel7Rm/DLY8IBxvDTBJADTf/De82w==
|
||||
dependencies:
|
||||
phaser "3.55.1"
|
||||
|
||||
"@humanwhocodes/config-array@^0.9.2":
|
||||
version "0.9.2"
|
||||
@ -2161,10 +2168,10 @@ phaser3-rex-plugins@^1.1.42:
|
||||
papaparse "^5.3.0"
|
||||
webfontloader "^1.6.28"
|
||||
|
||||
phaser@^3.54.0:
|
||||
version "3.54.0"
|
||||
resolved "https://registry.yarnpkg.com/phaser/-/phaser-3.54.0.tgz#46b191e46059aab2a9a57f78525c60b595767eee"
|
||||
integrity sha512-/1XVI6J2siS0OGwJez7vLbRjars1zb//EvJdYMVyd3wNTUf5DHrvYUj1f6TsEISr4vjnbrNtS66QIuPbGU8x6A==
|
||||
phaser@3.55.1:
|
||||
version "3.55.1"
|
||||
resolved "https://registry.yarnpkg.com/phaser/-/phaser-3.55.1.tgz#25923fe845f6598aec57cfb37a5641834e9943a7"
|
||||
integrity sha512-A5J9/diRz05qc498UNJAaXp85JVkBAEMqxP8pmcRMu1RCLBs4Kx7axd7YxNbXnQuK58JBhTRucngLt8LSpsUlQ==
|
||||
dependencies:
|
||||
eventemitter3 "^4.0.7"
|
||||
path "^0.12.7"
|
||||
|
@ -50,12 +50,9 @@ message PingMessage {
|
||||
}
|
||||
|
||||
message SetPlayerDetailsMessage {
|
||||
//string name = 1;
|
||||
//repeated string characterLayers = 2;
|
||||
|
||||
// TODO: switch to google.protobuf.Int32Value when we migrate to ts-proto
|
||||
uint32 outlineColor = 3;
|
||||
bool removeOutlineColor = 4;
|
||||
google.protobuf.UInt32Value outlineColor = 3;
|
||||
google.protobuf.BoolValue removeOutlineColor = 4;
|
||||
google.protobuf.BoolValue showVoiceIndicator = 5;
|
||||
}
|
||||
|
||||
message UserMovesMessage {
|
||||
|
@ -2938,9 +2938,9 @@ minimatch@^3.0.4:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimist@^1.2.0, minimist@^1.2.5:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
||||
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
|
||||
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
|
||||
|
||||
minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0:
|
||||
version "2.9.0"
|
||||
|
48
pusher/src/Enum/PlayerTextures.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import * as tg from "generic-type-guard";
|
||||
import { z } from "zod";
|
||||
|
||||
//The list of all the player textures, both the default models and the partial textures used for customization
|
||||
|
||||
const wokaTexture = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
url: z.string(),
|
||||
tags: z.array(z.string()).optional(),
|
||||
tintable: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export type WokaTexture = z.infer<typeof wokaTexture>;
|
||||
|
||||
const wokaTextureCollection = z.object({
|
||||
name: z.string(),
|
||||
textures: z.array(wokaTexture),
|
||||
});
|
||||
|
||||
export type WokaTextureCollection = z.infer<typeof wokaTextureCollection>;
|
||||
|
||||
const wokaPartType = z.object({
|
||||
collections: z.array(wokaTextureCollection),
|
||||
required: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export type WokaPartType = z.infer<typeof wokaPartType>;
|
||||
|
||||
export const wokaList = z.record(wokaPartType);
|
||||
|
||||
export type WokaList = z.infer<typeof wokaList>;
|
||||
|
||||
export const wokaPartNames = ["woka", "body", "eyes", "hair", "clothes", "hat", "accessory"];
|
||||
|
||||
export const isWokaDetail = new tg.IsInterface()
|
||||
.withProperties({
|
||||
id: tg.isString,
|
||||
})
|
||||
.withOptionalProperties({
|
||||
url: tg.isString,
|
||||
layer: tg.isString,
|
||||
})
|
||||
.get();
|
||||
|
||||
export type WokaDetail = tg.GuardedType<typeof isWokaDetail>;
|
||||
|
||||
export type WokaDetailsResult = WokaDetail[];
|
@ -87,7 +87,7 @@ export class UserDescriptor {
|
||||
if (playerDetails.getRemoveoutlinecolor()) {
|
||||
this.outlineColor = undefined;
|
||||
} else {
|
||||
this.outlineColor = playerDetails.getOutlinecolor();
|
||||
this.outlineColor = playerDetails.getOutlinecolor()?.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1785,9 +1785,9 @@ minimatch@^3.0.4:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimist@^1.2.5:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
||||
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
|
||||
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
|
||||
|
||||
minipass@^3.0.0:
|
||||
version "3.1.6"
|
||||
|
12
tests/package-lock.json
generated
@ -2123,9 +2123,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/mkdirp-classic": {
|
||||
@ -4303,9 +4303,9 @@
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
|
||||
"dev": true
|
||||
},
|
||||
"mkdirp-classic": {
|
||||
|
@ -1048,9 +1048,9 @@ minimatch@^3.0.4:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimist@^1.1.3, minimist@^1.2.5:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
||||
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
|
||||
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
|
||||
|
||||
mkdirp@^0.5.1:
|
||||
version "0.5.5"
|
||||
|