Merge branch 'develop' of github.com:thecodingmachine/workadventure into develop

This commit is contained in:
_Bastler 2022-04-04 16:25:33 +02:00
commit 6c3c3f8d44
44 changed files with 1403 additions and 562 deletions

View File

@ -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 {

View File

@ -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,

View File

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

View File

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

View File

@ -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"

View File

@ -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",

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -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} />

View File

@ -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}
>
&lt;
</button>
<button
class="customCharacterSceneButton customCharacterSceneButtonRight nes-btn"
on:click|preventDefault={selectRight}
>
&gt;
</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>

View File

@ -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[],

View File

@ -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) {

View 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;
}
}
}

View 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();
}
}

View 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();
});
}
}

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

View File

@ -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()) {

View File

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

View File

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

View File

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

View File

@ -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 {

View File

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

View File

@ -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";

View File

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

View File

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

View File

@ -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.
*/

View File

@ -1,5 +1,3 @@
import { derived, writable, Writable } from "svelte/store";
export const customCharacterSceneVisibleStore = writable(false);
export const activeRowStore = writable(0);

View File

@ -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";

View File

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

View File

@ -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"

View File

@ -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 {

View File

@ -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"

View 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[];

View File

@ -87,7 +87,7 @@ export class UserDescriptor {
if (playerDetails.getRemoveoutlinecolor()) {
this.outlineColor = undefined;
} else {
this.outlineColor = playerDetails.getOutlinecolor();
this.outlineColor = playerDetails.getOutlinecolor()?.getValue();
}
}

View File

@ -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"

View File

@ -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": {

View File

@ -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"