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

This commit is contained in:
_Bastler 2021-09-06 18:49:47 +02:00
commit 64613b076e
66 changed files with 953 additions and 822 deletions

View File

@ -61,6 +61,10 @@ jobs:
run: yarn run lint
working-directory: "front"
- name: "Pretty"
run: yarn run pretty
working-directory: "front"
- name: "Jasmine"
run: yarn test
working-directory: "front"

View File

@ -14,7 +14,7 @@ WA.state.onVariableChange(key : string).subscribe((data: unknown) => {}) : Subsc
WA.state.[any property]: unknown
```
These methods and properties can be used to save, load and track changes in variables related to the current room.
These methods and properties can be used to save, load and track changes in [variables related to the current room](variables.md).
Variables stored in `WA.state` can be any value that is serializable in JSON.
@ -63,44 +63,11 @@ that you get the expected type).
For security reasons, the list of variables you are allowed to access and modify is **restricted** (otherwise, anyone on your map could set any data).
Variables storage is subject to an authorization process. Read below to learn more.
### Declaring allowed keys
## Defining a variable
In order to declare allowed keys related to a room, you need to add **objects** in an "object layer" of the map.
Each object will represent a variable.
<div class="row">
<div class="col">
<img src="images/object_variable.png" class="figure-img img-fluid rounded" alt="" />
</div>
</div>
The name of the variable is the name of the object.
The object **type** MUST be **variable**.
You can set a default value for the object in the `default` property.
### Persisting variables state
Use the `persist` property to save the state of the variable in database. If `persist` is false, the variable will stay
in the memory of the WorkAdventure servers but will be wiped out of the memory as soon as the room is empty (or if the
server restarts).
{.alert.alert-info}
Do not use `persist` for highly dynamic values that have a short life spawn.
### Managing access rights to variables
With `readableBy` and `writableBy`, you control who can read of write in this variable. The property accepts a string
representing a "tag". Anyone having this "tag" can read/write in the variable.
{.alert.alert-warning}
`readableBy` and `writableBy` are specific to the "online" version of WorkAdventure because the notion of tags
is not available unless you have an "admin" server (that is not part of the self-hosted version of WorkAdventure).
Finally, the `jsonSchema` property can contain [a complete JSON schema](https://json-schema.org/) to validate the content of the variable.
Trying to set a variable to a value that is not compatible with the schema will fail.
Out of the box, you cannot edit *any* variable. Variables MUST be declared in the map.
Check the [dedicated variables page](variables.md) to learn how to declare a variable in a map.
## Tracking variables changes

59
docs/maps/variables.md Normal file
View File

@ -0,0 +1,59 @@
{.section-title.accent.text-primary}
# Variables
Maps can contain **variables**. Variables are piece of information that store some data. In computer science, we like
to say variables are storing the "state" of the room.
- Variables are shared amongst all players in a given room. When the value of a variable changes for one player, it changes
for everyone.
- Variables are **invisible**. There are plenty of ways they can act on the room, but by default, you don't see them.
## Declaring a variable
In order to declare allowed variables in a room, you need to add **objects** in an "object layer" of the map.
Each object will represent a variable.
<div class="row">
<div class="col">
<img src="images/object_variable.png" class="figure-img img-fluid rounded" alt="" />
</div>
</div>
The name of the variable is the name of the object.
The object **type** MUST be **variable**.
You can set a default value for the object in the `default` property.
## Persisting variables state
Use the `persist` property to save the state of the variable in database. If `persist` is false, the variable will stay
in the memory of the WorkAdventure servers but will be wiped out of the memory as soon as the room is empty (or if the
server restarts).
{.alert.alert-info}
Do not use `persist` for highly dynamic values that have a short life spawn.
## Managing access rights to variables
With `readableBy` and `writableBy`, you control who can read of write in this variable. The property accepts a string
representing a "tag". Anyone having this "tag" can read/write in the variable.
{.alert.alert-warning}
`readableBy` and `writableBy` are specific to the "online" version of WorkAdventure because the notion of tags
is not available unless you have an "admin" server (that is not part of the self-hosted version of WorkAdventure).
In a future release, the `jsonSchema` property will contain [a complete JSON schema](https://json-schema.org/) to validate the content of the variable.
Trying to set a variable to a value that is not compatible with the schema will fail.
## Using variables
There are plenty of ways to use variables in WorkAdventure:
- Using the [scripting API](api-state.md), you can read, edit or track the content of variables.
- Using the [Action zones](https://workadventu.re/map-building-extra/generic-action-zones.md), you can set the value of a variable when someone is entering or leaving a zone
- By [binding variable values to properties in the map](https://workadventu.re/map-building-extra/variable-to-property-binding.md)
- By [using automatically generated configuration screens](https://workadventu.re/map-building-extra/automatic-configuration.md) to create forms to edit the value of variables
In general, variables can be used by third party libraries that you can embed in your map to add extra features.
A good example of such a library is the ["Scripting API Extra" library](https://workadventu.re/map-building-extra/about.md)

View File

View File

@ -1,11 +1,11 @@
import * as tg from "generic-type-guard";
export const isButtonClickedEvent =
new tg.IsInterface().withProperties({
export const isButtonClickedEvent = new tg.IsInterface()
.withProperties({
popupId: tg.isNumber,
buttonId: tg.isNumber,
input : tg.isBoolean,
inputValue : tg.isString,
input: tg.isBoolean,
inputValue: tg.isString,
}).get();
/**
* A message sent from the game to the iFrame when a user enters or leaves a zone marked with the "zone" property.

View File

@ -1,10 +1,11 @@
import * as tg from "generic-type-guard";
export const isChatEvent =
new tg.IsInterface().withProperties({
export const isChatEvent = new tg.IsInterface()
.withProperties({
message: tg.isString,
author: tg.isString,
}).get();
})
.get();
/**
* A message sent from the iFrame to the game to add a message in the chat.
*/

View File

@ -1,9 +1,9 @@
import * as tg from "generic-type-guard";
export const isClosePopupEvent =
new tg.IsInterface().withProperties({
export const isClosePopupEvent = new tg.IsInterface()
.withProperties({
popupId: tg.isNumber,
inputValue : tg.isString,
inputValue: tg.isString,
}).get();
/**

View File

@ -1,9 +1,10 @@
import * as tg from "generic-type-guard";
export const isEnterLeaveEvent =
new tg.IsInterface().withProperties({
export const isEnterLeaveEvent = new tg.IsInterface()
.withProperties({
name: tg.isString,
}).get();
})
.get();
/**
* A message sent from the game to the iFrame when a user enters or leaves a zone marked with the "zone" property.
*/

View File

@ -1,11 +1,10 @@
import * as tg from "generic-type-guard";
export const isGoToPageEvent =
new tg.IsInterface().withProperties({
export const isGoToPageEvent = new tg.IsInterface()
.withProperties({
url: tg.isString,
}).get();
})
.get();
/**
* A message sent from the iFrame to the game to add a message in the chat.

View File

@ -1,9 +1,10 @@
import * as tg from "generic-type-guard";
export const isLoadSoundEvent =
new tg.IsInterface().withProperties({
export const isLoadSoundEvent = new tg.IsInterface()
.withProperties({
url: tg.isString,
}).get();
})
.get();
/**
* A message sent from the iFrame to the game to add a message in the chat.

View File

@ -1,13 +1,14 @@
import * as tg from "generic-type-guard";
const isButtonDescriptor =
new tg.IsInterface().withProperties({
const isButtonDescriptor = new tg.IsInterface()
.withProperties({
label: tg.isString,
className: tg.isOptional(tg.isString)
}).get();
className: tg.isOptional(tg.isString),
})
.get();
export const isOpenPopupEvent =
new tg.IsInterface().withProperties({
export const isOpenPopupEvent = new tg.IsInterface()
.withProperties({
popupId: tg.isNumber,
targetObject: tg.isString,
message: tg.isString,

View File

@ -1,11 +1,10 @@
import * as tg from "generic-type-guard";
export const isOpenTabEvent =
new tg.IsInterface().withProperties({
export const isOpenTabEvent = new tg.IsInterface()
.withProperties({
url: tg.isString,
}).get();
})
.get();
/**
* A message sent from the iFrame to the game to add a message in the chat.

View File

@ -1,22 +1,23 @@
import * as tg from "generic-type-guard";
const isSoundConfig =
new tg.IsInterface().withProperties({
const isSoundConfig = new tg.IsInterface()
.withProperties({
volume: tg.isOptional(tg.isNumber),
loop: tg.isOptional(tg.isBoolean),
mute: tg.isOptional(tg.isBoolean),
rate: tg.isOptional(tg.isNumber),
detune: tg.isOptional(tg.isNumber),
seek: tg.isOptional(tg.isNumber),
delay: tg.isOptional(tg.isNumber)
}).get();
delay: tg.isOptional(tg.isNumber),
})
.get();
export const isPlaySoundEvent =
new tg.IsInterface().withProperties({
export const isPlaySoundEvent = new tg.IsInterface()
.withProperties({
url: tg.isString,
config : tg.isOptional(isSoundConfig),
}).get();
config: tg.isOptional(isSoundConfig),
})
.get();
/**
* A message sent from the iFrame to the game to add a message in the chat.

View File

@ -1,9 +1,10 @@
import * as tg from "generic-type-guard";
export const isStopSoundEvent =
new tg.IsInterface().withProperties({
export const isStopSoundEvent = new tg.IsInterface()
.withProperties({
url: tg.isString,
}).get();
})
.get();
/**
* A message sent from the iFrame to the game to add a message in the chat.

View File

@ -1,9 +1,10 @@
import * as tg from "generic-type-guard";
export const isUserInputChatEvent =
new tg.IsInterface().withProperties({
export const isUserInputChatEvent = new tg.IsInterface()
.withProperties({
message: tg.isString,
}).get();
})
.get();
/**
* A message sent from the game to the iFrame when a user types a message in the chat.
*/

View File

@ -1,4 +1,4 @@
import type {Popup} from "./Popup";
import type { Popup } from "./Popup";
export type ButtonClickedCallback = (popup: Popup) => void;
@ -6,13 +6,13 @@ export interface ButtonDescriptor {
/**
* The label of the button
*/
label: string,
label: string;
/**
* The type of the button. Can be one of "normal", "primary", "success", "warning", "error", "disabled"
*/
className?: "normal" | "primary" | "success" | "warning" | "error" | "disabled",
className?: "normal" | "primary" | "success" | "warning" | "error" | "disabled";
/**
* Callback called if the button is pressed
*/
callback: ButtonClickedCallback,
callback: ButtonClickedCallback;
}

View File

@ -1,5 +1,5 @@
import {sendToWorkadventure} from "../IframeApiContribution";
import type {ClosePopupEvent} from "../../Events/ClosePopupEvent";
import { sendToWorkadventure } from "../IframeApiContribution";
import type { ClosePopupEvent } from "../../Events/ClosePopupEvent";
export class Popup {

View File

@ -1,16 +1,18 @@
import type {IframeResponseEventMap} from "../../Api/Events/IframeEvent";
import type {IframeCallback} from "../../Api/iframe/IframeApiContribution";
import type {IframeCallbackContribution} from "../../Api/iframe/IframeApiContribution";
import type { IframeResponseEventMap } from "../../Api/Events/IframeEvent";
import type { IframeCallback } from "../../Api/iframe/IframeApiContribution";
import type { IframeCallbackContribution } from "../../Api/iframe/IframeApiContribution";
export const registeredCallbacks: { [K in keyof IframeResponseEventMap]?: IframeCallback<K> } = {}
export const registeredCallbacks: { [K in keyof IframeResponseEventMap]?: IframeCallback<K> } = {};
export function apiCallback<T extends keyof IframeResponseEventMap>(callbackData: IframeCallbackContribution<T>): IframeCallbackContribution<keyof IframeResponseEventMap> {
export function apiCallback<T extends keyof IframeResponseEventMap>(
callbackData: IframeCallbackContribution<T>
): IframeCallbackContribution<keyof IframeResponseEventMap> {
const iframeCallback = {
typeChecker: callbackData.typeChecker,
callback: callbackData.callback
callback: callbackData.callback,
} as IframeCallback<T>;
const newCallback = { [callbackData.type]: iframeCallback };
Object.assign(registeredCallbacks, newCallback)
Object.assign(registeredCallbacks, newCallback);
return callbackData as unknown as IframeCallbackContribution<keyof IframeResponseEventMap>;
}

View File

@ -1,10 +1,5 @@
<script lang="ts">
function goToGettingStarted() {
const sparkHost = "https://workadventu.re/getting-started";
window.open(sparkHost, "_blank");
}
function goToBuildingMap() {
const sparkHost = "https://wiki.bstly.de/services/partey/map";
window.open(sparkHost, "_blank");

View File

@ -1,56 +1,49 @@
<script lang="typescript">
import {gameManager} from "../../Phaser/Game/GameManager";
import {SelectCompanionScene, SelectCompanionSceneName} from "../../Phaser/Login/SelectCompanionScene";
import {menuIconVisiblilityStore, menuVisiblilityStore, userIsConnected} from "../../Stores/MenuStore";
import {selectCompanionSceneVisibleStore} from "../../Stores/SelectCompanionStore";
import {LoginScene, LoginSceneName} from "../../Phaser/Login/LoginScene";
import {loginSceneVisibleStore} from "../../Stores/LoginSceneStore";
import {selectCharacterSceneVisibleStore} from "../../Stores/SelectCharacterStore";
import {SelectCharacterScene, SelectCharacterSceneName} from "../../Phaser/Login/SelectCharacterScene";
import {connectionManager} from "../../Connexion/ConnectionManager";
import {PROFILE_URL} from "../../Enum/EnvironmentVariable";
import {localUserStore} from "../../Connexion/LocalUserStore";
import {EnableCameraScene, EnableCameraSceneName} from "../../Phaser/Login/EnableCameraScene";
import {enableCameraSceneVisibilityStore} from "../../Stores/MediaStore";
import { gameManager } from "../../Phaser/Game/GameManager";
import { SelectCompanionScene, SelectCompanionSceneName } from "../../Phaser/Login/SelectCompanionScene";
import { menuIconVisiblilityStore, menuVisiblilityStore, userIsConnected } from "../../Stores/MenuStore";
import { selectCompanionSceneVisibleStore } from "../../Stores/SelectCompanionStore";
import { loginSceneVisibleStore } from "../../Stores/LoginSceneStore";
import { selectCharacterSceneVisibleStore } from "../../Stores/SelectCharacterStore";
import { SelectCharacterScene, SelectCharacterSceneName } from "../../Phaser/Login/SelectCharacterScene";
import { connectionManager } from "../../Connexion/ConnectionManager";
import { PROFILE_URL } from "../../Enum/EnvironmentVariable";
import { localUserStore } from "../../Connexion/LocalUserStore";
import { EnableCameraScene, EnableCameraSceneName } from "../../Phaser/Login/EnableCameraScene";
import { enableCameraSceneVisibilityStore } from "../../Stores/MediaStore";
function disableMenuStores(){
function disableMenuStores() {
menuVisiblilityStore.set(false);
menuIconVisiblilityStore.set(false);
}
function openEditCompanionScene(){
function openEditCompanionScene() {
disableMenuStores();
selectCompanionSceneVisibleStore.set(true);
gameManager.leaveGame(SelectCompanionSceneName,new SelectCompanionScene());
gameManager.leaveGame(SelectCompanionSceneName, new SelectCompanionScene());
}
function openEditNameScene(){
disableMenuStores();
loginSceneVisibleStore.set(true);
gameManager.leaveGame(LoginSceneName,new LoginScene());
}
function openEditSkinScene(){
function openEditSkinScene() {
disableMenuStores();
selectCharacterSceneVisibleStore.set(true);
gameManager.leaveGame(SelectCharacterSceneName,new SelectCharacterScene());
gameManager.leaveGame(SelectCharacterSceneName, new SelectCharacterScene());
}
function logOut(){
function logOut() {
disableMenuStores();
loginSceneVisibleStore.set(true);
connectionManager.logout();
}
function getProfileUrl(){
function getProfileUrl() {
return PROFILE_URL + `?token=${localUserStore.getAuthToken()}`;
}
function openEnableCameraScene(){
function openEnableCameraScene() {
disableMenuStores();
enableCameraSceneVisibilityStore.showEnableCameraScene();
gameManager.leaveGame(EnableCameraSceneName,new EnableCameraScene());
gameManager.leaveGame(EnableCameraSceneName, new EnableCameraScene());
}
//TODO: Uncomment when login will be completely developed
@ -61,56 +54,55 @@
<div class="customize-main">
{#if $userIsConnected}
<section>
{#if PROFILE_URL != undefined}
<iframe title="profile" src="{getProfileUrl()}"></iframe>
{/if}
</section>
<section>
<button type="button" class="nes-btn" on:click|preventDefault={logOut}>Log out</button>
</section>
<section>
{#if PROFILE_URL != undefined}
<iframe title="profile" src="{getProfileUrl()}"></iframe>
{/if}
</section>
<section>
<button type="button" class="nes-btn" on:click|preventDefault={logOut}>Log out</button>
</section>
{:else}
<section>
<a type="button" class="nes-btn" href="/login">Sing in</a>
</section>
<section>
<a type="button" class="nes-btn" href="/login">Sing in</a>
</section>
{/if}
<section>
<!-- <button type="button" class="nes-btn" on:click|preventDefault={openEditNameScene}>Edit Name</button> -->
<button type="button" class="nes-btn is-rounded" on:click|preventDefault={openEditSkinScene}>Edit Skin</button>
<button type="button" class="nes-btn" on:click|preventDefault={openEditCompanionScene}>Edit Companion</button>
</section>
<section>
<button type="button" class="nes-btn" on:click|preventDefault={openEnableCameraScene}>Setup camera</button>
</section>
<!-- <section>
<!-- <section>
<button type="button" class="nes-btn is-primary" on:click|preventDefault={clickLogin}>Login</button>
</section>-->
</div>
<style lang="scss">
div.customize-main{
section {
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 20px;
div.customize-main {
section {
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 20px;
iframe{
width: 100%;
height: 50vh;
border: none;
}
iframe {
width: 100%;
height: 50vh;
border: none;
}
button {
height: 50px;
width: 250px;
button {
height: 50px;
width: 250px;
}
}
}
}
@media only screen and (max-width: 800px) {
div.customize-main section button {
width: 130px;
}
div.customize-main section button {
width: 130px;
}
}
</style>

View File

@ -1,11 +1,11 @@
<script lang="typescript">
import {obtainedMediaConstraintStore} from "../Stores/MediaStore";
import {localStreamStore, isSilentStore} from "../Stores/MediaStore";
import { obtainedMediaConstraintStore } from "../Stores/MediaStore";
import { localStreamStore, isSilentStore } from "../Stores/MediaStore";
import SoundMeterWidget from "./SoundMeterWidget.svelte";
import {onDestroy} from "svelte";
import {srcObject} from "./Video/utils";
import { onDestroy } from "svelte";
import { srcObject } from "./Video/utils";
let stream : MediaStream|null;
let stream: MediaStream | null;
const unsubscribe = localStreamStore.subscribe(value => {
if (value.type === 'success') {
@ -29,7 +29,8 @@
<div>
<div class="video-container nes-container is-rounded is-dark div-myCamVideo" class:hide={!$obtainedMediaConstraintStore.video || isSilent}>
<div class="video-container nes-container is-rounded is-dark div-myCamVideo"
class:hide={!$obtainedMediaConstraintStore.video || isSilent}>
{#if $localStreamStore.type === "success" && $localStreamStore.stream}
<video class="myCamVideo" use:srcObject={stream} autoplay muted playsinline></video>
<SoundMeterWidget stream={stream}></SoundMeterWidget>
@ -38,4 +39,4 @@
<div class="is-silent" class:hide={isSilent}>
Silent zone
</div>
</div>
</div>

View File

@ -1,15 +1,15 @@
import {Subject} from "rxjs";
import type {BanUserMessage, SendUserMessage} from "../Messages/generated/messages_pb";
import { Subject } from "rxjs";
import type { BanUserMessage, SendUserMessage } from "../Messages/generated/messages_pb";
export enum AdminMessageEventTypes {
admin = 'message',
audio = 'audio',
ban = 'ban',
banned = 'banned',
admin = "message",
audio = "audio",
ban = "ban",
banned = "banned",
}
interface AdminMessageEvent {
type: AdminMessageEventTypes,
type: AdminMessageEventTypes;
text: string;
//todo add optional properties for other event types
}
@ -21,14 +21,14 @@ class AdminMessagesService {
public messageStream = this._messageStream.asObservable();
constructor() {
this.messageStream.subscribe((event) => console.log('message', event))
this.messageStream.subscribe((event) => console.log("message", event));
}
onSendusermessage(message: SendUserMessage|BanUserMessage) {
onSendusermessage(message: SendUserMessage | BanUserMessage) {
this._messageStream.next({
type: message.getType() as unknown as AdminMessageEventTypes,
text: message.getMessage(),
})
});
}
}

View File

@ -1,19 +1,18 @@
import {Subject} from "rxjs";
import { Subject } from "rxjs";
interface EmoteEvent {
userId: number,
emote: string,
userId: number;
emote: string;
}
class EmoteEventStream {
private _stream:Subject<EmoteEvent> = new Subject();
private _stream: Subject<EmoteEvent> = new Subject();
public stream = this._stream.asObservable();
fire(userId: number, emote:string) {
this._stream.next({userId, emote});
fire(userId: number, emote: string) {
this._stream.next({ userId, emote });
}
}
export const emoteEventStream = new EmoteEventStream();
export const emoteEventStream = new EmoteEventStream();

View File

@ -1,14 +1,12 @@
import {Subject} from "rxjs";
import { Subject } from "rxjs";
class WorldFullMessageStream {
private _stream:Subject<string|null> = new Subject<string|null>();
private _stream: Subject<string | null> = new Subject<string | null>();
public stream = this._stream.asObservable();
onMessage(message? :string) {
onMessage(message?: string) {
this._stream.next(message);
}
}
export const worldFullMessageStream = new WorldFullMessageStream();
export const worldFullMessageStream = new WorldFullMessageStream();

View File

@ -1 +1 @@
export class TextureError extends Error{}
export class TextureError extends Error {}

View File

@ -1,19 +1,21 @@
export class MessageUI {
static warningMessage(text: string){
static warningMessage(text: string) {
this.removeMessage();
const body = document.getElementById("body");
body?.insertAdjacentHTML('afterbegin', `
body?.insertAdjacentHTML(
"afterbegin",
`
<div id="message-reconnect" class="message-info warning">
${text}
</div>
`);
`
);
}
static removeMessage(id : string|null = null) {
if(!id){
static removeMessage(id: string | null = null) {
if (!id) {
const messages = document.getElementsByClassName("message-info");
for (let i = 0; i < messages.length; i++){
for (let i = 0; i < messages.length; i++) {
messages.item(i)?.remove();
}
return;

View File

@ -1,23 +1,22 @@
import {PositionMessage} from "../Messages/generated/messages_pb";
import { PositionMessage } from "../Messages/generated/messages_pb";
import Direction = PositionMessage.Direction;
import type {PointInterface} from "../Connexion/ConnexionModels";
import type { PointInterface } from "../Connexion/ConnexionModels";
export class ProtobufClientUtils {
public static toPointInterface(position: PositionMessage): PointInterface {
let direction: string;
switch (position.getDirection()) {
case Direction.UP:
direction = 'up';
direction = "up";
break;
case Direction.DOWN:
direction = 'down';
direction = "down";
break;
case Direction.LEFT:
direction = 'left';
direction = "left";
break;
case Direction.RIGHT:
direction = 'right';
direction = "right";
break;
default:
throw new Error("Unexpected direction");

View File

@ -16,7 +16,7 @@ export class Companion extends Container {
private delta: number;
private invisible: boolean;
private updateListener: Function;
private target: { x: number, y: number, direction: PlayerAnimationDirections };
private target: { x: number; y: number; direction: PlayerAnimationDirections };
private companionName: string;
private direction: PlayerAnimationDirections;
@ -36,10 +36,10 @@ export class Companion extends Container {
this.companionName = name;
texturePromise.then(resource => {
texturePromise.then((resource) => {
this.addResource(resource);
this.invisible = false;
})
});
this.scene.physics.world.enableBody(this);
@ -52,7 +52,7 @@ export class Companion extends Container {
this.setDepth(-1);
this.updateListener = this.step.bind(this);
this.scene.events.addListener('update', this.updateListener);
this.scene.events.addListener("update", this.updateListener);
this.scene.add.existing(this);
}
@ -62,7 +62,7 @@ export class Companion extends Container {
}
public step(time: number, delta: number) {
if (typeof this.target === 'undefined') return;
if (typeof this.target === "undefined") return;
this.delta += delta;
if (this.delta < 128) {
@ -87,7 +87,10 @@ export class Companion extends Container {
const yDir = yDist / Math.max(Math.abs(yDist), 1);
const speed = 256;
this.getBody().setVelocity(Math.min(Math.abs(xDist * 2.5), speed) * xDir, Math.min(Math.abs(yDist * 2.5), speed) * yDir);
this.getBody().setVelocity(
Math.min(Math.abs(xDist * 2.5), speed) * xDir,
Math.min(Math.abs(yDist * 2.5), speed) * yDir
);
if (Math.abs(xDist) > Math.abs(yDist)) {
if (xDist < 0) {
@ -116,8 +119,8 @@ export class Companion extends Container {
y,
direction,
moving: animationType === PlayerAnimationTypes.Walk,
name: companionName
}
name: companionName,
};
}
private playAnimation(direction: PlayerAnimationDirections, type: PlayerAnimationTypes): void {
@ -133,7 +136,7 @@ export class Companion extends Container {
this.add(sprite);
this.getAnimations(resource).forEach(animation => {
this.getAnimations(resource).forEach((animation) => {
this.scene.anims.create(animation);
});
@ -145,60 +148,60 @@ export class Companion extends Container {
return [
{
key: `${resource}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Idle}`,
frames: this.scene.anims.generateFrameNumbers(resource, {frames: [1]}),
frames: this.scene.anims.generateFrameNumbers(resource, { frames: [1] }),
frameRate: 10,
repeat: 1
repeat: 1,
},
{
key: `${resource}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Idle}`,
frames: this.scene.anims.generateFrameNumbers(resource, {frames: [4]}),
frames: this.scene.anims.generateFrameNumbers(resource, { frames: [4] }),
frameRate: 10,
repeat: 1
repeat: 1,
},
{
key: `${resource}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Idle}`,
frames: this.scene.anims.generateFrameNumbers(resource, {frames: [7]}),
frames: this.scene.anims.generateFrameNumbers(resource, { frames: [7] }),
frameRate: 10,
repeat: 1
repeat: 1,
},
{
key: `${resource}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Idle}`,
frames: this.scene.anims.generateFrameNumbers(resource, {frames: [10]}),
frames: this.scene.anims.generateFrameNumbers(resource, { frames: [10] }),
frameRate: 10,
repeat: 1
repeat: 1,
},
{
key: `${resource}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Walk}`,
frames: this.scene.anims.generateFrameNumbers(resource, {frames: [0, 1, 2]}),
frames: this.scene.anims.generateFrameNumbers(resource, { frames: [0, 1, 2] }),
frameRate: 15,
repeat: -1
repeat: -1,
},
{
key: `${resource}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Walk}`,
frames: this.scene.anims.generateFrameNumbers(resource, {frames: [3, 4, 5]}),
frames: this.scene.anims.generateFrameNumbers(resource, { frames: [3, 4, 5] }),
frameRate: 15,
repeat: -1
repeat: -1,
},
{
key: `${resource}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Walk}`,
frames: this.scene.anims.generateFrameNumbers(resource, {frames: [6, 7, 8]}),
frames: this.scene.anims.generateFrameNumbers(resource, { frames: [6, 7, 8] }),
frameRate: 15,
repeat: -1
repeat: -1,
},
{
key: `${resource}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Walk}`,
frames: this.scene.anims.generateFrameNumbers(resource, {frames: [9, 10, 11]}),
frames: this.scene.anims.generateFrameNumbers(resource, { frames: [9, 10, 11] }),
frameRate: 15,
repeat: -1
}
]
repeat: -1,
},
];
}
private getBody(): Phaser.Physics.Arcade.Body {
const body = this.body;
if (!(body instanceof Phaser.Physics.Arcade.Body)) {
throw new Error('Container does not have arcade body');
throw new Error("Container does not have arcade body");
}
return body;
@ -212,7 +215,7 @@ export class Companion extends Container {
}
if (this.scene) {
this.scene.events.removeListener('update', this.updateListener);
this.scene.events.removeListener("update", this.updateListener);
}
super.destroy();

View File

@ -1,7 +1,7 @@
export interface CompanionResourceDescriptionInterface {
name: string,
img: string,
behaviour: "dog" | "cat"
name: string;
img: string;
behaviour: "dog" | "cat";
}
export const COMPANION_RESOURCES: CompanionResourceDescriptionInterface[] = [
@ -11,4 +11,4 @@ export const COMPANION_RESOURCES: CompanionResourceDescriptionInterface[] = [
{ name: "cat1", img: "resources/characters/pipoya/Cat 01-1.png", behaviour: "cat" },
{ name: "cat2", img: "resources/characters/pipoya/Cat 01-2.png", behaviour: "cat" },
{ name: "cat3", img: "resources/characters/pipoya/Cat 01-3.png", behaviour: "cat" },
]
];

View File

@ -7,23 +7,23 @@ export const getAllCompanionResources = (loader: LoaderPlugin): CompanionResourc
});
return COMPANION_RESOURCES;
}
};
export const lazyLoadCompanionResource = (loader: LoaderPlugin, name: string): Promise<string> => {
return new Promise((resolve, reject) => {
const resource = COMPANION_RESOURCES.find(item => item.name === name);
const resource = COMPANION_RESOURCES.find((item) => item.name === name);
if (typeof resource === 'undefined') {
if (typeof resource === "undefined") {
return reject(`Texture '${name}' not found!`);
}
if (loader.textureManager.exists(resource.name)) {
return resolve(resource.name);
}
loader.spritesheet(resource.name, resource.img, { frameWidth: 32, frameHeight: 32, endFrame: 12 });
loader.once(`filecomplete-spritesheet-${resource.name}`, () => resolve(resource.name));
loader.start(); // It's only automatically started during the Scene preload.
});
}
};

View File

@ -2,7 +2,7 @@ import { DEPTH_INGAME_TEXT_INDEX } from "../Game/DepthIndexes";
export class ChatModeIcon extends Phaser.GameObjects.Sprite {
constructor(scene: Phaser.Scene, x: number, y: number) {
super(scene, x, y, 'layout_modes', 3);
super(scene, x, y, "layout_modes", 3);
scene.add.existing(this);
this.setScrollFactor(0, 0);
this.setOrigin(0, 1);
@ -10,4 +10,4 @@ export class ChatModeIcon extends Phaser.GameObjects.Sprite {
this.setVisible(false);
this.setDepth(DEPTH_INGAME_TEXT_INDEX);
}
}
}

View File

@ -1,11 +1,8 @@
export class ClickButton extends Phaser.GameObjects.Image{
export class ClickButton extends Phaser.GameObjects.Image {
constructor(scene: Phaser.Scene, x: number, y: number, textureName: string, callback: Function) {
super(scene, x, y, textureName);
this.scene.add.existing(this);
this.setInteractive();
this.on("pointerup", callback);
}
}
}

View File

@ -1,8 +1,8 @@
import {DEPTH_INGAME_TEXT_INDEX} from "../Game/DepthIndexes";
import { DEPTH_INGAME_TEXT_INDEX } from "../Game/DepthIndexes";
export class PresentationModeIcon extends Phaser.GameObjects.Sprite {
constructor(scene: Phaser.Scene, x: number, y: number) {
super(scene, x, y, 'layout_modes', 0);
super(scene, x, y, "layout_modes", 0);
scene.add.existing(this);
this.setScrollFactor(0, 0);
this.setOrigin(0, 1);
@ -10,4 +10,4 @@ export class PresentationModeIcon extends Phaser.GameObjects.Sprite {
this.setVisible(false);
this.setDepth(DEPTH_INGAME_TEXT_INDEX);
}
}
}

View File

@ -1,20 +1,20 @@
import Sprite = Phaser.GameObjects.Sprite;
import {DEPTH_UI_INDEX} from "../Game/DepthIndexes";
import {waScaleManager} from "../Services/WaScaleManager";
import { DEPTH_UI_INDEX } from "../Game/DepthIndexes";
import { waScaleManager } from "../Services/WaScaleManager";
export interface RadialMenuItem {
image: string,
name: string,
image: string;
name: string;
}
export const RadialMenuClickEvent = 'radialClick';
export const RadialMenuClickEvent = "radialClick";
export class RadialMenu extends Phaser.GameObjects.Container {
private resizeCallback: OmitThisParameter<() => void>;
constructor(scene: Phaser.Scene, x: number, y: number, private items: RadialMenuItem[]) {
super(scene, x, y);
this.setDepth(DEPTH_UI_INDEX)
this.setDepth(DEPTH_UI_INDEX);
this.scene.add.existing(this);
this.initItems();
@ -22,45 +22,45 @@ export class RadialMenu extends Phaser.GameObjects.Container {
this.resizeCallback = this.resize.bind(this);
this.scene.scale.on(Phaser.Scale.Events.RESIZE, this.resizeCallback);
}
private initItems() {
const itemsNumber = this.items.length;
const menuRadius = 70 + (waScaleManager.uiScalingFactor - 1) * 20;
this.items.forEach((item, index) => this.createRadialElement(item, index, itemsNumber, menuRadius))
this.items.forEach((item, index) => this.createRadialElement(item, index, itemsNumber, menuRadius));
}
private createRadialElement(item: RadialMenuItem, index: number, itemsNumber: number, menuRadius: number) {
const image = new Sprite(this.scene, 0, menuRadius, item.image);
const image = new Sprite(this.scene, 0, menuRadius, item.image);
this.add(image);
this.scene.sys.updateList.add(image);
const scalingFactor = waScaleManager.uiScalingFactor * 0.075;
image.setScale(scalingFactor)
image.setScale(scalingFactor);
image.setInteractive({
useHandCursor: true,
});
image.on('pointerdown', () => this.emit(RadialMenuClickEvent, item));
image.on('pointerover', () => {
image.on("pointerdown", () => this.emit(RadialMenuClickEvent, item));
image.on("pointerover", () => {
this.scene.tweens.add({
targets: image,
props: {
scale: 2 * scalingFactor,
},
duration: 500,
ease: 'Power3',
})
ease: "Power3",
});
});
image.on('pointerout', () => {
image.on("pointerout", () => {
this.scene.tweens.add({
targets: image,
props: {
scale: scalingFactor,
},
duration: 500,
ease: 'Power3',
})
ease: "Power3",
});
});
const angle = 2 * Math.PI * index / itemsNumber;
Phaser.Actions.RotateAroundDistance([image], {x: 0, y: 0}, angle, menuRadius);
const angle = (2 * Math.PI * index) / itemsNumber;
Phaser.Actions.RotateAroundDistance([image], { x: 0, y: 0 }, angle, menuRadius);
}
private resize() {
@ -71,4 +71,4 @@ export class RadialMenu extends Phaser.GameObjects.Container {
this.scene.scale.removeListener(Phaser.Scale.Events.RESIZE, this.resizeCallback);
super.destroy();
}
}
}

View File

@ -1,4 +1,4 @@
import type {IAnalyserNode, IAudioContext, IMediaStreamAudioSourceNode} from 'standardized-audio-context';
import type { IAnalyserNode, IAudioContext, IMediaStreamAudioSourceNode } from "standardized-audio-context";
/**
* Class to measure the sound volume of a media stream
@ -7,10 +7,10 @@ export class SoundMeter {
private instant: number;
private clip: number;
//private script: ScriptProcessorNode;
private analyser: IAnalyserNode<IAudioContext>|undefined;
private dataArray: Uint8Array|undefined;
private context: IAudioContext|undefined;
private source: IMediaStreamAudioSourceNode<IAudioContext>|undefined;
private analyser: IAnalyserNode<IAudioContext> | undefined;
private dataArray: Uint8Array | undefined;
private context: IAudioContext | undefined;
private source: IMediaStreamAudioSourceNode<IAudioContext> | undefined;
constructor() {
this.instant = 0.0;
@ -27,8 +27,7 @@ export class SoundMeter {
this.dataArray = new Uint8Array(bufferLength);
}
public connectToSource(stream: MediaStream, context: IAudioContext): void
{
public connectToSource(stream: MediaStream, context: IAudioContext): void {
if (this.source !== undefined) {
this.stop();
}
@ -42,8 +41,6 @@ export class SoundMeter {
//analyser.connect(distortion);
//distortion.connect(this.context.destination);
//this.analyser.connect(this.context.destination);
}
public getVolume(): number {
@ -52,16 +49,15 @@ export class SoundMeter {
}
this.analyser.getByteFrequencyData(this.dataArray);
const input = this.dataArray;
let i;
let sum = 0.0;
//let clipcount = 0;
for (i = 0; i < input.length; ++i) {
sum += input[i] * input[i];
// if (Math.abs(input[i]) > 0.99) {
// clipcount += 1;
// }
// if (Math.abs(input[i]) > 0.99) {
// clipcount += 1;
// }
}
this.instant = Math.sqrt(sum / input.length);
//this.slow = 0.95 * that.slow + 0.05 * that.instant;
@ -84,6 +80,4 @@ export class SoundMeter {
this.dataArray = undefined;
this.source = undefined;
}
}

View File

@ -1,10 +1,9 @@
export class TextField extends Phaser.GameObjects.BitmapText {
constructor(scene: Phaser.Scene, x: number, y: number, text: string | string[], center: boolean = true) {
super(scene, x, y, 'main_font', text, 8);
super(scene, x, y, "main_font", text, 8);
this.scene.add.existing(this);
if (center) {
this.setOrigin(0.5).setCenterAlign()
this.setOrigin(0.5).setCenterAlign();
}
}
}

View File

@ -33,7 +33,7 @@ export abstract class Character extends Container {
private invisible: boolean;
public companion?: Companion;
private emote: Phaser.GameObjects.Text | null = null;
private emoteTween: Phaser.Tweens.Tween|null = null;
private emoteTween: Phaser.Tweens.Tween | null = null;
scene: GameScene;
constructor(
@ -146,56 +146,56 @@ export abstract class Character extends Container {
{
key: `${name}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Walk}`,
frameModel: name,
frames: [0, 1, 2, 1],
frames: [ 0, 1, 2, 1 ],
frameRate: 10,
repeat: -1,
},
{
key: `${name}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Walk}`,
frameModel: name,
frames: [3, 4, 5, 4],
frames: [ 3, 4, 5, 4 ],
frameRate: 10,
repeat: -1,
},
{
key: `${name}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Walk}`,
frameModel: name,
frames: [6, 7, 8, 7],
frames: [ 6, 7, 8, 7 ],
frameRate: 10,
repeat: -1,
},
{
key: `${name}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Walk}`,
frameModel: name,
frames: [9, 10, 11, 10],
frames: [ 9, 10, 11, 10 ],
frameRate: 10,
repeat: -1,
},
{
key: `${name}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Idle}`,
frameModel: name,
frames: [1],
frames: [ 1 ],
frameRate: 10,
repeat: 1,
},
{
key: `${name}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Idle}`,
frameModel: name,
frames: [4],
frames: [ 4 ],
frameRate: 10,
repeat: 1,
},
{
key: `${name}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Idle}`,
frameModel: name,
frames: [7],
frames: [ 7 ],
frameRate: 10,
repeat: 1,
},
{
key: `${name}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Idle}`,
frameModel: name,
frames: [10],
frames: [ 10 ],
frameRate: 10,
repeat: 1,
},
@ -204,7 +204,7 @@ export abstract class Character extends Container {
protected playAnimation(direction: PlayerAnimationDirections, moving: boolean): void {
if (this.invisible) return;
for (const [texture, sprite] of this.sprites.entries()) {
for (const [ texture, sprite ] of this.sprites.entries()) {
if (!sprite.anims) {
console.error("ANIMS IS NOT DEFINED!!!");
return;
@ -296,7 +296,7 @@ export abstract class Character extends Container {
const emoteY = -60;
this.playerName.setVisible(false);
this.emote = new Text(this.scene, -12, 0, emote, {fontFamily: '"twemoji"', fontSize:'24px'});
this.emote = new Text(this.scene, -12, 0, emote, { fontFamily: '"twemoji"', fontSize: '24px' });
this.emote.setAlpha(0);
this.add(this.emote);
this.createStartTransition(scalingFactor, emoteY);

View File

@ -1,5 +1,5 @@
import Container = Phaser.GameObjects.Container;
import type {Scene} from "phaser";
import type { Scene } from "phaser";
import Sprite = Phaser.GameObjects.Sprite;
/**

View File

@ -1,334 +1,440 @@
//The list of all the player textures, both the default models and the partial textures used for customization
export interface BodyResourceDescriptionListInterface {
[key: string]: BodyResourceDescriptionInterface
[ key: string ]: BodyResourceDescriptionInterface;
}
export interface BodyResourceDescriptionInterface {
name: string,
img: string,
level?: number
name: string;
img: string;
level?: number;
}
export const PLAYER_RESOURCES: BodyResourceDescriptionListInterface = {
"male1": {name: "male1", img: "resources/characters/pipoya/Male 01-1.png"},
"male2": {name: "male2", img: "resources/characters/pipoya/Male 02-2.png"},
"male3": {name: "male3", img: "resources/characters/pipoya/Male 03-4.png"},
"male4": {name: "male4", img: "resources/characters/pipoya/Male 09-1.png"},
"male5": {name: "male5", img: "resources/characters/pipoya/Male 10-3.png"},
"male6": {name: "male6", img: "resources/characters/pipoya/Male 17-2.png"},
"male7": {name: "male7", img: "resources/characters/pipoya/Male 18-1.png"},
"male8": {name: "male8", img: "resources/characters/pipoya/Male 16-4.png"},
"male9": {name: "male9", img: "resources/characters/pipoya/Male 07-2.png"},
"male10": {name: "male10", img: "resources/characters/pipoya/Male 05-3.png"},
"male11": {name: "male11", img: "resources/characters/pipoya/Teacher male 02.png"},
"male12": {name: "male12", img: "resources/characters/pipoya/su4 Student male 12.png"},
male1: { name: "male1", img: "resources/characters/pipoya/Male 01-1.png" },
male2: { name: "male2", img: "resources/characters/pipoya/Male 02-2.png" },
male3: { name: "male3", img: "resources/characters/pipoya/Male 03-4.png" },
male4: { name: "male4", img: "resources/characters/pipoya/Male 09-1.png" },
male5: { name: "male5", img: "resources/characters/pipoya/Male 10-3.png" },
male6: { name: "male6", img: "resources/characters/pipoya/Male 17-2.png" },
male7: { name: "male7", img: "resources/characters/pipoya/Male 18-1.png" },
male8: { name: "male8", img: "resources/characters/pipoya/Male 16-4.png" },
male9: { name: "male9", img: "resources/characters/pipoya/Male 07-2.png" },
male10: { name: "male10", img: "resources/characters/pipoya/Male 05-3.png" },
male11: { name: "male11", img: "resources/characters/pipoya/Teacher male 02.png" },
male12: { name: "male12", img: "resources/characters/pipoya/su4 Student male 12.png" },
"Female1": {name: "Female1", img: "resources/characters/pipoya/Female 01-1.png"},
"Female2": {name: "Female2", img: "resources/characters/pipoya/Female 02-2.png"},
"Female3": {name: "Female3", img: "resources/characters/pipoya/Female 03-4.png"},
"Female4": {name: "Female4", img: "resources/characters/pipoya/Female 09-1.png"},
"Female5": {name: "Female5", img: "resources/characters/pipoya/Female 10-3.png"},
"Female6": {name: "Female6", img: "resources/characters/pipoya/Female 17-2.png"},
"Female7": {name: "Female7", img: "resources/characters/pipoya/Female 18-1.png"},
"Female8": {name: "Female8", img: "resources/characters/pipoya/Female 16-4.png"},
"Female9": {name: "Female9", img: "resources/characters/pipoya/Female 07-2.png"},
"Female10": {name: "Female10", img: "resources/characters/pipoya/Female 05-3.png"},
"Female11": {name: "Female11", img: "resources/characters/pipoya/Teacher fmale 02.png"},
"Female12": {name: "Female12", img: "resources/characters/pipoya/su4 Student fmale 12.png"},
Female1: { name: "Female1", img: "resources/characters/pipoya/Female 01-1.png" },
Female2: { name: "Female2", img: "resources/characters/pipoya/Female 02-2.png" },
Female3: { name: "Female3", img: "resources/characters/pipoya/Female 03-4.png" },
Female4: { name: "Female4", img: "resources/characters/pipoya/Female 09-1.png" },
Female5: { name: "Female5", img: "resources/characters/pipoya/Female 10-3.png" },
Female6: { name: "Female6", img: "resources/characters/pipoya/Female 17-2.png" },
Female7: { name: "Female7", img: "resources/characters/pipoya/Female 18-1.png" },
Female8: { name: "Female8", img: "resources/characters/pipoya/Female 16-4.png" },
Female9: { name: "Female9", img: "resources/characters/pipoya/Female 07-2.png" },
Female10: { name: "Female10", img: "resources/characters/pipoya/Female 05-3.png" },
Female11: { name: "Female11", img: "resources/characters/pipoya/Teacher fmale 02.png" },
Female12: { name: "Female12", img: "resources/characters/pipoya/su4 Student fmale 12.png" },
};
export const COLOR_RESOURCES: BodyResourceDescriptionListInterface = {
"color_1": {name: "color_1", img: "resources/customisation/character_color/character_color0.png"},
"color_2": {name: "color_2", img: "resources/customisation/character_color/character_color1.png"},
"color_3": {name: "color_3", img: "resources/customisation/character_color/character_color2.png"},
"color_4": {name: "color_4", img: "resources/customisation/character_color/character_color3.png"},
"color_5": {name: "color_5", img: "resources/customisation/character_color/character_color4.png"},
"color_6": {name: "color_6", img: "resources/customisation/character_color/character_color5.png"},
"color_7": {name: "color_7", img: "resources/customisation/character_color/character_color6.png"},
"color_8": {name: "color_8", img: "resources/customisation/character_color/character_color7.png"},
"color_9": {name: "color_9", img: "resources/customisation/character_color/character_color8.png"},
"color_10": {name: "color_10", img: "resources/customisation/character_color/character_color9.png"},
"color_11": {name: "color_11", img: "resources/customisation/character_color/character_color10.png"},
"color_12": {name: "color_12", img: "resources/customisation/character_color/character_color11.png"},
"color_13": {name: "color_13", img: "resources/customisation/character_color/character_color12.png"},
"color_14": {name: "color_14", img: "resources/customisation/character_color/character_color13.png"},
"color_15": {name: "color_15", img: "resources/customisation/character_color/character_color14.png"},
"color_16": {name: "color_16", img: "resources/customisation/character_color/character_color15.png"},
"color_17": {name: "color_17", img: "resources/customisation/character_color/character_color16.png"},
"color_18": {name: "color_18", img: "resources/customisation/character_color/character_color17.png"},
"color_19": {name: "color_19", img: "resources/customisation/character_color/character_color18.png"},
"color_20": {name: "color_20", img: "resources/customisation/character_color/character_color19.png"},
"color_21": {name: "color_21", img: "resources/customisation/character_color/character_color20.png"},
"color_22": {name: "color_22", img: "resources/customisation/character_color/character_color21.png"},
"color_23": {name: "color_23", img: "resources/customisation/character_color/character_color22.png"},
"color_24": {name: "color_24", img: "resources/customisation/character_color/character_color23.png"},
"color_25": {name: "color_25", img: "resources/customisation/character_color/character_color24.png"},
"color_26": {name: "color_26", img: "resources/customisation/character_color/character_color25.png"},
"color_27": {name: "color_27", img: "resources/customisation/character_color/character_color26.png"},
"color_28": {name: "color_28", img: "resources/customisation/character_color/character_color27.png"},
"color_29": {name: "color_29", img: "resources/customisation/character_color/character_color28.png"},
"color_30": {name: "color_30", img: "resources/customisation/character_color/character_color29.png"},
"color_31": {name: "color_31", img: "resources/customisation/character_color/character_color30.png"},
"color_32": {name: "color_32", img: "resources/customisation/character_color/character_color31.png"},
"color_33": {name: "color_33", img: "resources/customisation/character_color/character_color32.png"}
color_1: { name: "color_1", img: "resources/customisation/character_color/character_color0.png" },
color_2: { name: "color_2", img: "resources/customisation/character_color/character_color1.png" },
color_3: { name: "color_3", img: "resources/customisation/character_color/character_color2.png" },
color_4: { name: "color_4", img: "resources/customisation/character_color/character_color3.png" },
color_5: { name: "color_5", img: "resources/customisation/character_color/character_color4.png" },
color_6: { name: "color_6", img: "resources/customisation/character_color/character_color5.png" },
color_7: { name: "color_7", img: "resources/customisation/character_color/character_color6.png" },
color_8: { name: "color_8", img: "resources/customisation/character_color/character_color7.png" },
color_9: { name: "color_9", img: "resources/customisation/character_color/character_color8.png" },
color_10: { name: "color_10", img: "resources/customisation/character_color/character_color9.png" },
color_11: { name: "color_11", img: "resources/customisation/character_color/character_color10.png" },
color_12: { name: "color_12", img: "resources/customisation/character_color/character_color11.png" },
color_13: { name: "color_13", img: "resources/customisation/character_color/character_color12.png" },
color_14: { name: "color_14", img: "resources/customisation/character_color/character_color13.png" },
color_15: { name: "color_15", img: "resources/customisation/character_color/character_color14.png" },
color_16: { name: "color_16", img: "resources/customisation/character_color/character_color15.png" },
color_17: { name: "color_17", img: "resources/customisation/character_color/character_color16.png" },
color_18: { name: "color_18", img: "resources/customisation/character_color/character_color17.png" },
color_19: { name: "color_19", img: "resources/customisation/character_color/character_color18.png" },
color_20: { name: "color_20", img: "resources/customisation/character_color/character_color19.png" },
color_21: { name: "color_21", img: "resources/customisation/character_color/character_color20.png" },
color_22: { name: "color_22", img: "resources/customisation/character_color/character_color21.png" },
color_23: { name: "color_23", img: "resources/customisation/character_color/character_color22.png" },
color_24: { name: "color_24", img: "resources/customisation/character_color/character_color23.png" },
color_25: { name: "color_25", img: "resources/customisation/character_color/character_color24.png" },
color_26: { name: "color_26", img: "resources/customisation/character_color/character_color25.png" },
color_27: { name: "color_27", img: "resources/customisation/character_color/character_color26.png" },
color_28: { name: "color_28", img: "resources/customisation/character_color/character_color27.png" },
color_29: { name: "color_29", img: "resources/customisation/character_color/character_color28.png" },
color_30: { name: "color_30", img: "resources/customisation/character_color/character_color29.png" },
color_31: { name: "color_31", img: "resources/customisation/character_color/character_color30.png" },
color_32: { name: "color_32", img: "resources/customisation/character_color/character_color31.png" },
color_33: { name: "color_33", img: "resources/customisation/character_color/character_color32.png" },
};
export const EYES_RESOURCES: BodyResourceDescriptionListInterface = {
"eyes_1": {name: "eyes_1", img: "resources/customisation/character_eyes/character_eyes1.png"},
"eyes_2": {name: "eyes_2", img: "resources/customisation/character_eyes/character_eyes2.png"},
"eyes_3": {name: "eyes_3", img: "resources/customisation/character_eyes/character_eyes3.png"},
"eyes_4": {name: "eyes_4", img: "resources/customisation/character_eyes/character_eyes4.png"},
"eyes_5": {name: "eyes_5", img: "resources/customisation/character_eyes/character_eyes5.png"},
"eyes_6": {name: "eyes_6", img: "resources/customisation/character_eyes/character_eyes6.png"},
"eyes_7": {name: "eyes_7", img: "resources/customisation/character_eyes/character_eyes7.png"},
"eyes_8": {name: "eyes_8", img: "resources/customisation/character_eyes/character_eyes8.png"},
"eyes_9": {name: "eyes_9", img: "resources/customisation/character_eyes/character_eyes9.png"},
"eyes_10": {name: "eyes_10", img: "resources/customisation/character_eyes/character_eyes10.png"},
"eyes_11": {name: "eyes_11", img: "resources/customisation/character_eyes/character_eyes11.png"},
"eyes_12": {name: "eyes_12", img: "resources/customisation/character_eyes/character_eyes12.png"},
"eyes_13": {name: "eyes_13", img: "resources/customisation/character_eyes/character_eyes13.png"},
"eyes_14": {name: "eyes_14", img: "resources/customisation/character_eyes/character_eyes14.png"},
"eyes_15": {name: "eyes_15", img: "resources/customisation/character_eyes/character_eyes15.png"},
"eyes_16": {name: "eyes_16", img: "resources/customisation/character_eyes/character_eyes16.png"},
"eyes_17": {name: "eyes_17", img: "resources/customisation/character_eyes/character_eyes17.png"},
"eyes_18": {name: "eyes_18", img: "resources/customisation/character_eyes/character_eyes18.png"},
"eyes_19": {name: "eyes_19", img: "resources/customisation/character_eyes/character_eyes19.png"},
"eyes_20": {name: "eyes_20", img: "resources/customisation/character_eyes/character_eyes20.png"},
"eyes_21": {name: "eyes_21", img: "resources/customisation/character_eyes/character_eyes21.png"},
"eyes_22": {name: "eyes_22", img: "resources/customisation/character_eyes/character_eyes22.png"},
"eyes_23": {name: "eyes_23", img: "resources/customisation/character_eyes/character_eyes23.png"},
"eyes_24": {name: "eyes_24", img: "resources/customisation/character_eyes/character_eyes24.png"},
"eyes_25": {name: "eyes_25", img: "resources/customisation/character_eyes/character_eyes25.png"},
"eyes_26": {name: "eyes_26", img: "resources/customisation/character_eyes/character_eyes26.png"},
"eyes_27": {name: "eyes_27", img: "resources/customisation/character_eyes/character_eyes27.png"},
"eyes_28": {name: "eyes_28", img: "resources/customisation/character_eyes/character_eyes28.png"},
"eyes_29": {name: "eyes_29", img: "resources/customisation/character_eyes/character_eyes29.png"},
"eyes_30": {name: "eyes_30", img: "resources/customisation/character_eyes/character_eyes30.png"}
eyes_1: { name: "eyes_1", img: "resources/customisation/character_eyes/character_eyes1.png" },
eyes_2: { name: "eyes_2", img: "resources/customisation/character_eyes/character_eyes2.png" },
eyes_3: { name: "eyes_3", img: "resources/customisation/character_eyes/character_eyes3.png" },
eyes_4: { name: "eyes_4", img: "resources/customisation/character_eyes/character_eyes4.png" },
eyes_5: { name: "eyes_5", img: "resources/customisation/character_eyes/character_eyes5.png" },
eyes_6: { name: "eyes_6", img: "resources/customisation/character_eyes/character_eyes6.png" },
eyes_7: { name: "eyes_7", img: "resources/customisation/character_eyes/character_eyes7.png" },
eyes_8: { name: "eyes_8", img: "resources/customisation/character_eyes/character_eyes8.png" },
eyes_9: { name: "eyes_9", img: "resources/customisation/character_eyes/character_eyes9.png" },
eyes_10: { name: "eyes_10", img: "resources/customisation/character_eyes/character_eyes10.png" },
eyes_11: { name: "eyes_11", img: "resources/customisation/character_eyes/character_eyes11.png" },
eyes_12: { name: "eyes_12", img: "resources/customisation/character_eyes/character_eyes12.png" },
eyes_13: { name: "eyes_13", img: "resources/customisation/character_eyes/character_eyes13.png" },
eyes_14: { name: "eyes_14", img: "resources/customisation/character_eyes/character_eyes14.png" },
eyes_15: { name: "eyes_15", img: "resources/customisation/character_eyes/character_eyes15.png" },
eyes_16: { name: "eyes_16", img: "resources/customisation/character_eyes/character_eyes16.png" },
eyes_17: { name: "eyes_17", img: "resources/customisation/character_eyes/character_eyes17.png" },
eyes_18: { name: "eyes_18", img: "resources/customisation/character_eyes/character_eyes18.png" },
eyes_19: { name: "eyes_19", img: "resources/customisation/character_eyes/character_eyes19.png" },
eyes_20: { name: "eyes_20", img: "resources/customisation/character_eyes/character_eyes20.png" },
eyes_21: { name: "eyes_21", img: "resources/customisation/character_eyes/character_eyes21.png" },
eyes_22: { name: "eyes_22", img: "resources/customisation/character_eyes/character_eyes22.png" },
eyes_23: { name: "eyes_23", img: "resources/customisation/character_eyes/character_eyes23.png" },
eyes_24: { name: "eyes_24", img: "resources/customisation/character_eyes/character_eyes24.png" },
eyes_25: { name: "eyes_25", img: "resources/customisation/character_eyes/character_eyes25.png" },
eyes_26: { name: "eyes_26", img: "resources/customisation/character_eyes/character_eyes26.png" },
eyes_27: { name: "eyes_27", img: "resources/customisation/character_eyes/character_eyes27.png" },
eyes_28: { name: "eyes_28", img: "resources/customisation/character_eyes/character_eyes28.png" },
eyes_29: { name: "eyes_29", img: "resources/customisation/character_eyes/character_eyes29.png" },
eyes_30: { name: "eyes_30", img: "resources/customisation/character_eyes/character_eyes30.png" },
};
export const HAIR_RESOURCES: BodyResourceDescriptionListInterface = {
"hair_1": {name:"hair_1", img: "resources/customisation/character_hairs/character_hairs0.png"},
"hair_2": {name:"hair_2", img: "resources/customisation/character_hairs/character_hairs1.png"},
"hair_3": {name:"hair_3", img: "resources/customisation/character_hairs/character_hairs2.png"},
"hair_4": {name:"hair_4", img: "resources/customisation/character_hairs/character_hairs3.png"},
"hair_5": {name:"hair_5", img: "resources/customisation/character_hairs/character_hairs4.png"},
"hair_6": {name:"hair_6", img: "resources/customisation/character_hairs/character_hairs5.png"},
"hair_7": {name:"hair_7", img: "resources/customisation/character_hairs/character_hairs6.png"},
"hair_8": {name:"hair_8", img: "resources/customisation/character_hairs/character_hairs7.png"},
"hair_9": {name:"hair_9", img: "resources/customisation/character_hairs/character_hairs8.png"},
"hair_10": {name:"hair_10",img: "resources/customisation/character_hairs/character_hairs9.png"},
"hair_11": {name:"hair_11",img: "resources/customisation/character_hairs/character_hairs10.png"},
"hair_12": {name:"hair_12",img: "resources/customisation/character_hairs/character_hairs11.png"},
"hair_13": {name:"hair_13",img: "resources/customisation/character_hairs/character_hairs12.png"},
"hair_14": {name:"hair_14",img: "resources/customisation/character_hairs/character_hairs13.png"},
"hair_15": {name:"hair_15",img: "resources/customisation/character_hairs/character_hairs14.png"},
"hair_16": {name:"hair_16",img: "resources/customisation/character_hairs/character_hairs15.png"},
"hair_17": {name:"hair_17",img: "resources/customisation/character_hairs/character_hairs16.png"},
"hair_18": {name:"hair_18",img: "resources/customisation/character_hairs/character_hairs17.png"},
"hair_19": {name:"hair_19",img: "resources/customisation/character_hairs/character_hairs18.png"},
"hair_20": {name:"hair_20",img: "resources/customisation/character_hairs/character_hairs19.png"},
"hair_21": {name:"hair_21",img: "resources/customisation/character_hairs/character_hairs20.png"},
"hair_22": {name:"hair_22",img: "resources/customisation/character_hairs/character_hairs21.png"},
"hair_23": {name:"hair_23",img: "resources/customisation/character_hairs/character_hairs22.png"},
"hair_24": {name:"hair_24",img: "resources/customisation/character_hairs/character_hairs23.png"},
"hair_25": {name:"hair_25",img: "resources/customisation/character_hairs/character_hairs24.png"},
"hair_26": {name:"hair_26",img: "resources/customisation/character_hairs/character_hairs25.png"},
"hair_27": {name:"hair_27",img: "resources/customisation/character_hairs/character_hairs26.png"},
"hair_28": {name:"hair_28",img: "resources/customisation/character_hairs/character_hairs27.png"},
"hair_29": {name:"hair_29",img: "resources/customisation/character_hairs/character_hairs28.png"},
"hair_30": {name:"hair_30",img: "resources/customisation/character_hairs/character_hairs29.png"},
"hair_31": {name:"hair_31",img: "resources/customisation/character_hairs/character_hairs30.png"},
"hair_32": {name:"hair_32",img: "resources/customisation/character_hairs/character_hairs31.png"},
"hair_33": {name:"hair_33",img: "resources/customisation/character_hairs/character_hairs32.png"},
"hair_34": {name:"hair_34",img: "resources/customisation/character_hairs/character_hairs33.png"},
"hair_35": {name:"hair_35",img: "resources/customisation/character_hairs/character_hairs34.png"},
"hair_36": {name:"hair_36",img: "resources/customisation/character_hairs/character_hairs35.png"},
"hair_37": {name:"hair_37",img: "resources/customisation/character_hairs/character_hairs36.png"},
"hair_38": {name:"hair_38",img: "resources/customisation/character_hairs/character_hairs37.png"},
"hair_39": {name:"hair_39",img: "resources/customisation/character_hairs/character_hairs38.png"},
"hair_40": {name:"hair_40",img: "resources/customisation/character_hairs/character_hairs39.png"},
"hair_41": {name:"hair_41",img: "resources/customisation/character_hairs/character_hairs40.png"},
"hair_42": {name:"hair_42",img: "resources/customisation/character_hairs/character_hairs41.png"},
"hair_43": {name:"hair_43",img: "resources/customisation/character_hairs/character_hairs42.png"},
"hair_44": {name:"hair_44",img: "resources/customisation/character_hairs/character_hairs43.png"},
"hair_45": {name:"hair_45",img: "resources/customisation/character_hairs/character_hairs44.png"},
"hair_46": {name:"hair_46",img: "resources/customisation/character_hairs/character_hairs45.png"},
"hair_47": {name:"hair_47",img: "resources/customisation/character_hairs/character_hairs46.png"},
"hair_48": {name:"hair_48",img: "resources/customisation/character_hairs/character_hairs47.png"},
"hair_49": {name:"hair_49",img: "resources/customisation/character_hairs/character_hairs48.png"},
"hair_50": {name:"hair_50",img: "resources/customisation/character_hairs/character_hairs49.png"},
"hair_51": {name:"hair_51",img: "resources/customisation/character_hairs/character_hairs50.png"},
"hair_52": {name:"hair_52",img: "resources/customisation/character_hairs/character_hairs51.png"},
"hair_53": {name:"hair_53",img: "resources/customisation/character_hairs/character_hairs52.png"},
"hair_54": {name:"hair_54",img: "resources/customisation/character_hairs/character_hairs53.png"},
"hair_55": {name:"hair_55",img: "resources/customisation/character_hairs/character_hairs54.png"},
"hair_56": {name:"hair_56",img: "resources/customisation/character_hairs/character_hairs55.png"},
"hair_57": {name:"hair_57",img: "resources/customisation/character_hairs/character_hairs56.png"},
"hair_58": {name:"hair_58",img: "resources/customisation/character_hairs/character_hairs57.png"},
"hair_59": {name:"hair_59",img: "resources/customisation/character_hairs/character_hairs58.png"},
"hair_60": {name:"hair_60",img: "resources/customisation/character_hairs/character_hairs59.png"},
"hair_61": {name:"hair_61",img: "resources/customisation/character_hairs/character_hairs60.png"},
"hair_62": {name:"hair_62",img: "resources/customisation/character_hairs/character_hairs61.png"},
"hair_63": {name:"hair_63",img: "resources/customisation/character_hairs/character_hairs62.png"},
"hair_64": {name:"hair_64",img: "resources/customisation/character_hairs/character_hairs63.png"},
"hair_65": {name:"hair_65",img: "resources/customisation/character_hairs/character_hairs64.png"},
"hair_66": {name:"hair_66",img: "resources/customisation/character_hairs/character_hairs65.png"},
"hair_67": {name:"hair_67",img: "resources/customisation/character_hairs/character_hairs66.png"},
"hair_68": {name:"hair_68",img: "resources/customisation/character_hairs/character_hairs67.png"},
"hair_69": {name:"hair_69",img: "resources/customisation/character_hairs/character_hairs68.png"},
"hair_70": {name:"hair_70",img: "resources/customisation/character_hairs/character_hairs69.png"},
"hair_71": {name:"hair_71",img: "resources/customisation/character_hairs/character_hairs70.png"},
"hair_72": {name:"hair_72",img: "resources/customisation/character_hairs/character_hairs71.png"},
"hair_73": {name:"hair_73",img: "resources/customisation/character_hairs/character_hairs72.png"},
"hair_74": {name:"hair_74",img: "resources/customisation/character_hairs/character_hairs73.png"},
"hair_75": {name:"hair_75",img: "resources/customisation/character_hairs/character_hairs74.png"}
hair_1: { name: "hair_1", img: "resources/customisation/character_hairs/character_hairs0.png" },
hair_2: { name: "hair_2", img: "resources/customisation/character_hairs/character_hairs1.png" },
hair_3: { name: "hair_3", img: "resources/customisation/character_hairs/character_hairs2.png" },
hair_4: { name: "hair_4", img: "resources/customisation/character_hairs/character_hairs3.png" },
hair_5: { name: "hair_5", img: "resources/customisation/character_hairs/character_hairs4.png" },
hair_6: { name: "hair_6", img: "resources/customisation/character_hairs/character_hairs5.png" },
hair_7: { name: "hair_7", img: "resources/customisation/character_hairs/character_hairs6.png" },
hair_8: { name: "hair_8", img: "resources/customisation/character_hairs/character_hairs7.png" },
hair_9: { name: "hair_9", img: "resources/customisation/character_hairs/character_hairs8.png" },
hair_10: { name: "hair_10", img: "resources/customisation/character_hairs/character_hairs9.png" },
hair_11: { name: "hair_11", img: "resources/customisation/character_hairs/character_hairs10.png" },
hair_12: { name: "hair_12", img: "resources/customisation/character_hairs/character_hairs11.png" },
hair_13: { name: "hair_13", img: "resources/customisation/character_hairs/character_hairs12.png" },
hair_14: { name: "hair_14", img: "resources/customisation/character_hairs/character_hairs13.png" },
hair_15: { name: "hair_15", img: "resources/customisation/character_hairs/character_hairs14.png" },
hair_16: { name: "hair_16", img: "resources/customisation/character_hairs/character_hairs15.png" },
hair_17: { name: "hair_17", img: "resources/customisation/character_hairs/character_hairs16.png" },
hair_18: { name: "hair_18", img: "resources/customisation/character_hairs/character_hairs17.png" },
hair_19: { name: "hair_19", img: "resources/customisation/character_hairs/character_hairs18.png" },
hair_20: { name: "hair_20", img: "resources/customisation/character_hairs/character_hairs19.png" },
hair_21: { name: "hair_21", img: "resources/customisation/character_hairs/character_hairs20.png" },
hair_22: { name: "hair_22", img: "resources/customisation/character_hairs/character_hairs21.png" },
hair_23: { name: "hair_23", img: "resources/customisation/character_hairs/character_hairs22.png" },
hair_24: { name: "hair_24", img: "resources/customisation/character_hairs/character_hairs23.png" },
hair_25: { name: "hair_25", img: "resources/customisation/character_hairs/character_hairs24.png" },
hair_26: { name: "hair_26", img: "resources/customisation/character_hairs/character_hairs25.png" },
hair_27: { name: "hair_27", img: "resources/customisation/character_hairs/character_hairs26.png" },
hair_28: { name: "hair_28", img: "resources/customisation/character_hairs/character_hairs27.png" },
hair_29: { name: "hair_29", img: "resources/customisation/character_hairs/character_hairs28.png" },
hair_30: { name: "hair_30", img: "resources/customisation/character_hairs/character_hairs29.png" },
hair_31: { name: "hair_31", img: "resources/customisation/character_hairs/character_hairs30.png" },
hair_32: { name: "hair_32", img: "resources/customisation/character_hairs/character_hairs31.png" },
hair_33: { name: "hair_33", img: "resources/customisation/character_hairs/character_hairs32.png" },
hair_34: { name: "hair_34", img: "resources/customisation/character_hairs/character_hairs33.png" },
hair_35: { name: "hair_35", img: "resources/customisation/character_hairs/character_hairs34.png" },
hair_36: { name: "hair_36", img: "resources/customisation/character_hairs/character_hairs35.png" },
hair_37: { name: "hair_37", img: "resources/customisation/character_hairs/character_hairs36.png" },
hair_38: { name: "hair_38", img: "resources/customisation/character_hairs/character_hairs37.png" },
hair_39: { name: "hair_39", img: "resources/customisation/character_hairs/character_hairs38.png" },
hair_40: { name: "hair_40", img: "resources/customisation/character_hairs/character_hairs39.png" },
hair_41: { name: "hair_41", img: "resources/customisation/character_hairs/character_hairs40.png" },
hair_42: { name: "hair_42", img: "resources/customisation/character_hairs/character_hairs41.png" },
hair_43: { name: "hair_43", img: "resources/customisation/character_hairs/character_hairs42.png" },
hair_44: { name: "hair_44", img: "resources/customisation/character_hairs/character_hairs43.png" },
hair_45: { name: "hair_45", img: "resources/customisation/character_hairs/character_hairs44.png" },
hair_46: { name: "hair_46", img: "resources/customisation/character_hairs/character_hairs45.png" },
hair_47: { name: "hair_47", img: "resources/customisation/character_hairs/character_hairs46.png" },
hair_48: { name: "hair_48", img: "resources/customisation/character_hairs/character_hairs47.png" },
hair_49: { name: "hair_49", img: "resources/customisation/character_hairs/character_hairs48.png" },
hair_50: { name: "hair_50", img: "resources/customisation/character_hairs/character_hairs49.png" },
hair_51: { name: "hair_51", img: "resources/customisation/character_hairs/character_hairs50.png" },
hair_52: { name: "hair_52", img: "resources/customisation/character_hairs/character_hairs51.png" },
hair_53: { name: "hair_53", img: "resources/customisation/character_hairs/character_hairs52.png" },
hair_54: { name: "hair_54", img: "resources/customisation/character_hairs/character_hairs53.png" },
hair_55: { name: "hair_55", img: "resources/customisation/character_hairs/character_hairs54.png" },
hair_56: { name: "hair_56", img: "resources/customisation/character_hairs/character_hairs55.png" },
hair_57: { name: "hair_57", img: "resources/customisation/character_hairs/character_hairs56.png" },
hair_58: { name: "hair_58", img: "resources/customisation/character_hairs/character_hairs57.png" },
hair_59: { name: "hair_59", img: "resources/customisation/character_hairs/character_hairs58.png" },
hair_60: { name: "hair_60", img: "resources/customisation/character_hairs/character_hairs59.png" },
hair_61: { name: "hair_61", img: "resources/customisation/character_hairs/character_hairs60.png" },
hair_62: { name: "hair_62", img: "resources/customisation/character_hairs/character_hairs61.png" },
hair_63: { name: "hair_63", img: "resources/customisation/character_hairs/character_hairs62.png" },
hair_64: { name: "hair_64", img: "resources/customisation/character_hairs/character_hairs63.png" },
hair_65: { name: "hair_65", img: "resources/customisation/character_hairs/character_hairs64.png" },
hair_66: { name: "hair_66", img: "resources/customisation/character_hairs/character_hairs65.png" },
hair_67: { name: "hair_67", img: "resources/customisation/character_hairs/character_hairs66.png" },
hair_68: { name: "hair_68", img: "resources/customisation/character_hairs/character_hairs67.png" },
hair_69: { name: "hair_69", img: "resources/customisation/character_hairs/character_hairs68.png" },
hair_70: { name: "hair_70", img: "resources/customisation/character_hairs/character_hairs69.png" },
hair_71: { name: "hair_71", img: "resources/customisation/character_hairs/character_hairs70.png" },
hair_72: { name: "hair_72", img: "resources/customisation/character_hairs/character_hairs71.png" },
hair_73: { name: "hair_73", img: "resources/customisation/character_hairs/character_hairs72.png" },
hair_74: { name: "hair_74", img: "resources/customisation/character_hairs/character_hairs73.png" },
hair_75: { name: "hair_75", img: "resources/customisation/character_hairs/character_hairs74.png" }
};
export const CLOTHES_RESOURCES: BodyResourceDescriptionListInterface = {
"clothes_1": {name:"clothes_1", img: "resources/customisation/character_clothes/character_clothes0.png"},
"clothes_2": {name:"clothes_2", img: "resources/customisation/character_clothes/character_clothes1.png"},
"clothes_3": {name:"clothes_3", img: "resources/customisation/character_clothes/character_clothes2.png"},
"clothes_4": {name:"clothes_4", img: "resources/customisation/character_clothes/character_clothes3.png"},
"clothes_5": {name:"clothes_5", img: "resources/customisation/character_clothes/character_clothes4.png"},
"clothes_6": {name:"clothes_6", img: "resources/customisation/character_clothes/character_clothes5.png"},
"clothes_7": {name:"clothes_7", img: "resources/customisation/character_clothes/character_clothes6.png"},
"clothes_8": {name:"clothes_8", img: "resources/customisation/character_clothes/character_clothes7.png"},
"clothes_9": {name:"clothes_9", img: "resources/customisation/character_clothes/character_clothes8.png"},
"clothes_10": {name:"clothes_10",img: "resources/customisation/character_clothes/character_clothes9.png"},
"clothes_11": {name:"clothes_11",img: "resources/customisation/character_clothes/character_clothes10.png"},
"clothes_12": {name:"clothes_12",img: "resources/customisation/character_clothes/character_clothes11.png"},
"clothes_13": {name:"clothes_13",img: "resources/customisation/character_clothes/character_clothes12.png"},
"clothes_14": {name:"clothes_14",img: "resources/customisation/character_clothes/character_clothes13.png"},
"clothes_15": {name:"clothes_15",img: "resources/customisation/character_clothes/character_clothes14.png"},
"clothes_16": {name:"clothes_16",img: "resources/customisation/character_clothes/character_clothes15.png"},
"clothes_17": {name:"clothes_17",img: "resources/customisation/character_clothes/character_clothes16.png"},
"clothes_18": {name:"clothes_18",img: "resources/customisation/character_clothes/character_clothes17.png"},
"clothes_19": {name:"clothes_19",img: "resources/customisation/character_clothes/character_clothes18.png"},
"clothes_20": {name:"clothes_20",img: "resources/customisation/character_clothes/character_clothes19.png"},
"clothes_21": {name:"clothes_21",img: "resources/customisation/character_clothes/character_clothes20.png"},
"clothes_22": {name:"clothes_22",img: "resources/customisation/character_clothes/character_clothes21.png"},
"clothes_23": {name:"clothes_23",img: "resources/customisation/character_clothes/character_clothes22.png"},
"clothes_24": {name:"clothes_24",img: "resources/customisation/character_clothes/character_clothes23.png"},
"clothes_25": {name:"clothes_25",img: "resources/customisation/character_clothes/character_clothes24.png"},
"clothes_26": {name:"clothes_26",img: "resources/customisation/character_clothes/character_clothes25.png"},
"clothes_27": {name:"clothes_27",img: "resources/customisation/character_clothes/character_clothes26.png"},
"clothes_28": {name:"clothes_28",img: "resources/customisation/character_clothes/character_clothes27.png"},
"clothes_29": {name:"clothes_29",img: "resources/customisation/character_clothes/character_clothes28.png"},
"clothes_30": {name:"clothes_30",img: "resources/customisation/character_clothes/character_clothes29.png"},
"clothes_31": {name:"clothes_31",img: "resources/customisation/character_clothes/character_clothes30.png"},
"clothes_32": {name:"clothes_32",img: "resources/customisation/character_clothes/character_clothes31.png"},
"clothes_33": {name:"clothes_33",img: "resources/customisation/character_clothes/character_clothes32.png"},
"clothes_34": {name:"clothes_34",img: "resources/customisation/character_clothes/character_clothes33.png"},
"clothes_35": {name:"clothes_35",img: "resources/customisation/character_clothes/character_clothes34.png"},
"clothes_36": {name:"clothes_36",img: "resources/customisation/character_clothes/character_clothes35.png"},
"clothes_37": {name:"clothes_37",img: "resources/customisation/character_clothes/character_clothes36.png"},
"clothes_38": {name:"clothes_38",img: "resources/customisation/character_clothes/character_clothes37.png"},
"clothes_39": {name:"clothes_39",img: "resources/customisation/character_clothes/character_clothes38.png"},
"clothes_40": {name:"clothes_40",img: "resources/customisation/character_clothes/character_clothes39.png"},
"clothes_41": {name:"clothes_41",img: "resources/customisation/character_clothes/character_clothes40.png"},
"clothes_42": {name:"clothes_42",img: "resources/customisation/character_clothes/character_clothes41.png"},
"clothes_43": {name:"clothes_43",img: "resources/customisation/character_clothes/character_clothes42.png"},
"clothes_44": {name:"clothes_44",img: "resources/customisation/character_clothes/character_clothes43.png"},
"clothes_45": {name:"clothes_45",img: "resources/customisation/character_clothes/character_clothes44.png"},
"clothes_46": {name:"clothes_46",img: "resources/customisation/character_clothes/character_clothes45.png"},
"clothes_47": {name:"clothes_47",img: "resources/customisation/character_clothes/character_clothes46.png"},
"clothes_48": {name:"clothes_48",img: "resources/customisation/character_clothes/character_clothes47.png"},
"clothes_49": {name:"clothes_49",img: "resources/customisation/character_clothes/character_clothes48.png"},
"clothes_50": {name:"clothes_50",img: "resources/customisation/character_clothes/character_clothes49.png"},
"clothes_51": {name:"clothes_51",img: "resources/customisation/character_clothes/character_clothes50.png"},
"clothes_52": {name:"clothes_52",img: "resources/customisation/character_clothes/character_clothes51.png"},
"clothes_53": {name:"clothes_53",img: "resources/customisation/character_clothes/character_clothes52.png"},
"clothes_54": {name:"clothes_54",img: "resources/customisation/character_clothes/character_clothes53.png"},
"clothes_55": {name:"clothes_55",img: "resources/customisation/character_clothes/character_clothes54.png"},
"clothes_56": {name:"clothes_56",img: "resources/customisation/character_clothes/character_clothes55.png"},
"clothes_57": {name:"clothes_57",img: "resources/customisation/character_clothes/character_clothes56.png"},
"clothes_58": {name:"clothes_58",img: "resources/customisation/character_clothes/character_clothes57.png"},
"clothes_59": {name:"clothes_59",img: "resources/customisation/character_clothes/character_clothes58.png"},
"clothes_60": {name:"clothes_60",img: "resources/customisation/character_clothes/character_clothes59.png"},
"clothes_61": {name:"clothes_61",img: "resources/customisation/character_clothes/character_clothes60.png"},
"clothes_62": {name:"clothes_62",img: "resources/customisation/character_clothes/character_clothes61.png"},
"clothes_63": {name:"clothes_63",img: "resources/customisation/character_clothes/character_clothes62.png"},
"clothes_64": {name:"clothes_64",img: "resources/customisation/character_clothes/character_clothes63.png"},
"clothes_65": {name:"clothes_65",img: "resources/customisation/character_clothes/character_clothes64.png"},
"clothes_66": {name:"clothes_66",img: "resources/customisation/character_clothes/character_clothes65.png"},
"clothes_67": {name:"clothes_67",img: "resources/customisation/character_clothes/character_clothes66.png"},
"clothes_68": {name:"clothes_68",img: "resources/customisation/character_clothes/character_clothes67.png"},
"clothes_69": {name:"clothes_69",img: "resources/customisation/character_clothes/character_clothes68.png"},
"clothes_70": {name:"clothes_70",img: "resources/customisation/character_clothes/character_clothes69.png"},
"clothes_pride_shirt": {name:"clothes_pride_shirt",img: "resources/customisation/character_clothes/pride_shirt.png"},
"clothes_black_hoodie": {name:"clothes_black_hoodie",img: "resources/customisation/character_clothes/black_hoodie.png"},
"clothes_white_hoodie": {name:"clothes_white_hoodie",img: "resources/customisation/character_clothes/white_hoodie.png"},
"clothes_engelbert": {name:"clothes_engelbert",img: "resources/customisation/character_clothes/engelbert.png"}
clothes_1: { name: "clothes_1", img: "resources/customisation/character_clothes/character_clothes0.png" },
clothes_2: { name: "clothes_2", img: "resources/customisation/character_clothes/character_clothes1.png" },
clothes_3: { name: "clothes_3", img: "resources/customisation/character_clothes/character_clothes2.png" },
clothes_4: { name: "clothes_4", img: "resources/customisation/character_clothes/character_clothes3.png" },
clothes_5: { name: "clothes_5", img: "resources/customisation/character_clothes/character_clothes4.png" },
clothes_6: { name: "clothes_6", img: "resources/customisation/character_clothes/character_clothes5.png" },
clothes_7: { name: "clothes_7", img: "resources/customisation/character_clothes/character_clothes6.png" },
clothes_8: { name: "clothes_8", img: "resources/customisation/character_clothes/character_clothes7.png" },
clothes_9: { name: "clothes_9", img: "resources/customisation/character_clothes/character_clothes8.png" },
clothes_10: { name: "clothes_10", img: "resources/customisation/character_clothes/character_clothes9.png" },
clothes_11: { name: "clothes_11", img: "resources/customisation/character_clothes/character_clothes10.png" },
clothes_12: { name: "clothes_12", img: "resources/customisation/character_clothes/character_clothes11.png" },
clothes_13: { name: "clothes_13", img: "resources/customisation/character_clothes/character_clothes12.png" },
clothes_14: { name: "clothes_14", img: "resources/customisation/character_clothes/character_clothes13.png" },
clothes_15: { name: "clothes_15", img: "resources/customisation/character_clothes/character_clothes14.png" },
clothes_16: { name: "clothes_16", img: "resources/customisation/character_clothes/character_clothes15.png" },
clothes_17: { name: "clothes_17", img: "resources/customisation/character_clothes/character_clothes16.png" },
clothes_18: { name: "clothes_18", img: "resources/customisation/character_clothes/character_clothes17.png" },
clothes_19: { name: "clothes_19", img: "resources/customisation/character_clothes/character_clothes18.png" },
clothes_20: { name: "clothes_20", img: "resources/customisation/character_clothes/character_clothes19.png" },
clothes_21: { name: "clothes_21", img: "resources/customisation/character_clothes/character_clothes20.png" },
clothes_22: { name: "clothes_22", img: "resources/customisation/character_clothes/character_clothes21.png" },
clothes_23: { name: "clothes_23", img: "resources/customisation/character_clothes/character_clothes22.png" },
clothes_24: { name: "clothes_24", img: "resources/customisation/character_clothes/character_clothes23.png" },
clothes_25: { name: "clothes_25", img: "resources/customisation/character_clothes/character_clothes24.png" },
clothes_26: { name: "clothes_26", img: "resources/customisation/character_clothes/character_clothes25.png" },
clothes_27: { name: "clothes_27", img: "resources/customisation/character_clothes/character_clothes26.png" },
clothes_28: { name: "clothes_28", img: "resources/customisation/character_clothes/character_clothes27.png" },
clothes_29: { name: "clothes_29", img: "resources/customisation/character_clothes/character_clothes28.png" },
clothes_30: { name: "clothes_30", img: "resources/customisation/character_clothes/character_clothes29.png" },
clothes_31: { name: "clothes_31", img: "resources/customisation/character_clothes/character_clothes30.png" },
clothes_32: { name: "clothes_32", img: "resources/customisation/character_clothes/character_clothes31.png" },
clothes_33: { name: "clothes_33", img: "resources/customisation/character_clothes/character_clothes32.png" },
clothes_34: { name: "clothes_34", img: "resources/customisation/character_clothes/character_clothes33.png" },
clothes_35: { name: "clothes_35", img: "resources/customisation/character_clothes/character_clothes34.png" },
clothes_36: { name: "clothes_36", img: "resources/customisation/character_clothes/character_clothes35.png" },
clothes_37: { name: "clothes_37", img: "resources/customisation/character_clothes/character_clothes36.png" },
clothes_38: { name: "clothes_38", img: "resources/customisation/character_clothes/character_clothes37.png" },
clothes_39: { name: "clothes_39", img: "resources/customisation/character_clothes/character_clothes38.png" },
clothes_40: { name: "clothes_40", img: "resources/customisation/character_clothes/character_clothes39.png" },
clothes_41: { name: "clothes_41", img: "resources/customisation/character_clothes/character_clothes40.png" },
clothes_42: { name: "clothes_42", img: "resources/customisation/character_clothes/character_clothes41.png" },
clothes_43: { name: "clothes_43", img: "resources/customisation/character_clothes/character_clothes42.png" },
clothes_44: { name: "clothes_44", img: "resources/customisation/character_clothes/character_clothes43.png" },
clothes_45: { name: "clothes_45", img: "resources/customisation/character_clothes/character_clothes44.png" },
clothes_46: { name: "clothes_46", img: "resources/customisation/character_clothes/character_clothes45.png" },
clothes_47: { name: "clothes_47", img: "resources/customisation/character_clothes/character_clothes46.png" },
clothes_48: { name: "clothes_48", img: "resources/customisation/character_clothes/character_clothes47.png" },
clothes_49: { name: "clothes_49", img: "resources/customisation/character_clothes/character_clothes48.png" },
clothes_50: { name: "clothes_50", img: "resources/customisation/character_clothes/character_clothes49.png" },
clothes_51: { name: "clothes_51", img: "resources/customisation/character_clothes/character_clothes50.png" },
clothes_52: { name: "clothes_52", img: "resources/customisation/character_clothes/character_clothes51.png" },
clothes_53: { name: "clothes_53", img: "resources/customisation/character_clothes/character_clothes52.png" },
clothes_54: { name: "clothes_54", img: "resources/customisation/character_clothes/character_clothes53.png" },
clothes_55: { name: "clothes_55", img: "resources/customisation/character_clothes/character_clothes54.png" },
clothes_56: { name: "clothes_56", img: "resources/customisation/character_clothes/character_clothes55.png" },
clothes_57: { name: "clothes_57", img: "resources/customisation/character_clothes/character_clothes56.png" },
clothes_58: { name: "clothes_58", img: "resources/customisation/character_clothes/character_clothes57.png" },
clothes_59: { name: "clothes_59", img: "resources/customisation/character_clothes/character_clothes58.png" },
clothes_60: { name: "clothes_60", img: "resources/customisation/character_clothes/character_clothes59.png" },
clothes_61: { name: "clothes_61", img: "resources/customisation/character_clothes/character_clothes60.png" },
clothes_62: { name: "clothes_62", img: "resources/customisation/character_clothes/character_clothes61.png" },
clothes_63: { name: "clothes_63", img: "resources/customisation/character_clothes/character_clothes62.png" },
clothes_64: { name: "clothes_64", img: "resources/customisation/character_clothes/character_clothes63.png" },
clothes_65: { name: "clothes_65", img: "resources/customisation/character_clothes/character_clothes64.png" },
clothes_66: { name: "clothes_66", img: "resources/customisation/character_clothes/character_clothes65.png" },
clothes_67: { name: "clothes_67", img: "resources/customisation/character_clothes/character_clothes66.png" },
clothes_68: { name: "clothes_68", img: "resources/customisation/character_clothes/character_clothes67.png" },
clothes_69: { name: "clothes_69", img: "resources/customisation/character_clothes/character_clothes68.png" },
clothes_70: { name: "clothes_70", img: "resources/customisation/character_clothes/character_clothes69.png" },
clothes_pride_shirt: {
name: "clothes_pride_shirt",
img: "resources/customisation/character_clothes/pride_shirt.png",
},
clothes_black_hoodie: {
name: "clothes_black_hoodie",
img: "resources/customisation/character_clothes/black_hoodie.png",
},
clothes_white_hoodie: {
name: "clothes_white_hoodie",
img: "resources/customisation/character_clothes/white_hoodie.png",
},
clothes_engelbert: { name: "clothes_engelbert", img: "resources/customisation/character_clothes/engelbert.png" },
};
export const HATS_RESOURCES: BodyResourceDescriptionListInterface = {
"hats_1": {name: "hats_1", img: "resources/customisation/character_hats/character_hats1.png"},
"hats_2": {name: "hats_2", img: "resources/customisation/character_hats/character_hats2.png"},
"hats_3": {name: "hats_3", img: "resources/customisation/character_hats/character_hats3.png"},
"hats_4": {name: "hats_4", img: "resources/customisation/character_hats/character_hats4.png"},
"hats_5": {name: "hats_5", img: "resources/customisation/character_hats/character_hats5.png"},
"hats_6": {name: "hats_6", img: "resources/customisation/character_hats/character_hats6.png"},
"hats_7": {name: "hats_7", img: "resources/customisation/character_hats/character_hats7.png"},
"hats_8": {name: "hats_8", img: "resources/customisation/character_hats/character_hats8.png"},
"hats_9": {name: "hats_9", img: "resources/customisation/character_hats/character_hats9.png"},
"hats_10": {name: "hats_10", img: "resources/customisation/character_hats/character_hats10.png"},
"hats_11": {name: "hats_11", img: "resources/customisation/character_hats/character_hats11.png"},
"hats_12": {name: "hats_12", img: "resources/customisation/character_hats/character_hats12.png"},
"hats_13": {name: "hats_13", img: "resources/customisation/character_hats/character_hats13.png"},
"hats_14": {name: "hats_14", img: "resources/customisation/character_hats/character_hats14.png"},
"hats_15": {name: "hats_15", img: "resources/customisation/character_hats/character_hats15.png"},
"hats_16": {name: "hats_16", img: "resources/customisation/character_hats/character_hats16.png"},
"hats_17": {name: "hats_17", img: "resources/customisation/character_hats/character_hats17.png"},
"hats_18": {name: "hats_18", img: "resources/customisation/character_hats/character_hats18.png"},
"hats_19": {name: "hats_19", img: "resources/customisation/character_hats/character_hats19.png"},
"hats_20": {name: "hats_20", img: "resources/customisation/character_hats/character_hats20.png"},
"hats_21": {name: "hats_21", img: "resources/customisation/character_hats/character_hats21.png"},
"hats_22": {name: "hats_22", img: "resources/customisation/character_hats/character_hats22.png"},
"hats_23": {name: "hats_23", img: "resources/customisation/character_hats/character_hats23.png"},
"hats_24": {name: "hats_24", img: "resources/customisation/character_hats/character_hats24.png"},
"hats_25": {name: "hats_25", img: "resources/customisation/character_hats/character_hats25.png"},
"hats_26": {name: "hats_26", img: "resources/customisation/character_hats/character_hats26.png"},
"tinfoil_hat1": {name: "tinfoil_hat1", img: "resources/customisation/character_hats/tinfoil_hat1.png"}
hats_1: { name: "hats_1", img: "resources/customisation/character_hats/character_hats1.png" },
hats_2: { name: "hats_2", img: "resources/customisation/character_hats/character_hats2.png" },
hats_3: { name: "hats_3", img: "resources/customisation/character_hats/character_hats3.png" },
hats_4: { name: "hats_4", img: "resources/customisation/character_hats/character_hats4.png" },
hats_5: { name: "hats_5", img: "resources/customisation/character_hats/character_hats5.png" },
hats_6: { name: "hats_6", img: "resources/customisation/character_hats/character_hats6.png" },
hats_7: { name: "hats_7", img: "resources/customisation/character_hats/character_hats7.png" },
hats_8: { name: "hats_8", img: "resources/customisation/character_hats/character_hats8.png" },
hats_9: { name: "hats_9", img: "resources/customisation/character_hats/character_hats9.png" },
hats_10: { name: "hats_10", img: "resources/customisation/character_hats/character_hats10.png" },
hats_11: { name: "hats_11", img: "resources/customisation/character_hats/character_hats11.png" },
hats_12: { name: "hats_12", img: "resources/customisation/character_hats/character_hats12.png" },
hats_13: { name: "hats_13", img: "resources/customisation/character_hats/character_hats13.png" },
hats_14: { name: "hats_14", img: "resources/customisation/character_hats/character_hats14.png" },
hats_15: { name: "hats_15", img: "resources/customisation/character_hats/character_hats15.png" },
hats_16: { name: "hats_16", img: "resources/customisation/character_hats/character_hats16.png" },
hats_17: { name: "hats_17", img: "resources/customisation/character_hats/character_hats17.png" },
hats_18: { name: "hats_18", img: "resources/customisation/character_hats/character_hats18.png" },
hats_19: { name: "hats_19", img: "resources/customisation/character_hats/character_hats19.png" },
hats_20: { name: "hats_20", img: "resources/customisation/character_hats/character_hats20.png" },
hats_21: { name: "hats_21", img: "resources/customisation/character_hats/character_hats21.png" },
hats_22: { name: "hats_22", img: "resources/customisation/character_hats/character_hats22.png" },
hats_23: { name: "hats_23", img: "resources/customisation/character_hats/character_hats23.png" },
hats_24: { name: "hats_24", img: "resources/customisation/character_hats/character_hats24.png" },
hats_25: { name: "hats_25", img: "resources/customisation/character_hats/character_hats25.png" },
hats_26: { name: "hats_26", img: "resources/customisation/character_hats/character_hats26.png" },
tinfoil_hat1: { name: "tinfoil_hat1", img: "resources/customisation/character_hats/tinfoil_hat1.png" },
};
export const ACCESSORIES_RESOURCES: BodyResourceDescriptionListInterface = {
"accessory_1": {name: "accessory_1", img: "resources/customisation/character_accessories/character_accessories1.png"},
"accessory_2": {name: "accessory_2", img: "resources/customisation/character_accessories/character_accessories2.png"},
"accessory_3": {name: "accessory_3", img: "resources/customisation/character_accessories/character_accessories3.png"},
"accessory_4": {name: "accessory_4", img: "resources/customisation/character_accessories/character_accessories4.png"},
"accessory_5": {name: "accessory_5", img: "resources/customisation/character_accessories/character_accessories5.png"},
"accessory_6": {name: "accessory_6", img: "resources/customisation/character_accessories/character_accessories6.png"},
"accessory_7": {name: "accessory_7", img: "resources/customisation/character_accessories/character_accessories7.png"},
"accessory_8": {name: "accessory_8", img: "resources/customisation/character_accessories/character_accessories8.png"},
"accessory_9": {name: "accessory_9", img: "resources/customisation/character_accessories/character_accessories9.png"},
"accessory_10": {name: "accessory_10", img: "resources/customisation/character_accessories/character_accessories10.png"},
"accessory_11": {name: "accessory_11", img: "resources/customisation/character_accessories/character_accessories11.png"},
"accessory_12": {name: "accessory_12", img: "resources/customisation/character_accessories/character_accessories12.png"},
"accessory_13": {name: "accessory_13", img: "resources/customisation/character_accessories/character_accessories13.png"},
"accessory_14": {name: "accessory_14", img: "resources/customisation/character_accessories/character_accessories14.png"},
"accessory_15": {name: "accessory_15", img: "resources/customisation/character_accessories/character_accessories15.png"},
"accessory_16": {name: "accessory_16", img: "resources/customisation/character_accessories/character_accessories16.png"},
"accessory_17": {name: "accessory_17", img: "resources/customisation/character_accessories/character_accessories17.png"},
"accessory_18": {name: "accessory_18", img: "resources/customisation/character_accessories/character_accessories18.png"},
"accessory_19": {name: "accessory_19", img: "resources/customisation/character_accessories/character_accessories19.png"},
"accessory_20": {name: "accessory_20", img: "resources/customisation/character_accessories/character_accessories20.png"},
"accessory_21": {name: "accessory_21", img: "resources/customisation/character_accessories/character_accessories21.png"},
"accessory_22": {name: "accessory_22", img: "resources/customisation/character_accessories/character_accessories22.png"},
"accessory_23": {name: "accessory_23", img: "resources/customisation/character_accessories/character_accessories23.png"},
"accessory_24": {name: "accessory_24", img: "resources/customisation/character_accessories/character_accessories24.png"},
"accessory_25": {name: "accessory_25", img: "resources/customisation/character_accessories/character_accessories25.png"},
"accessory_26": {name: "accessory_26", img: "resources/customisation/character_accessories/character_accessories26.png"},
"accessory_27": {name: "accessory_27", img: "resources/customisation/character_accessories/character_accessories27.png"},
"accessory_28": {name: "accessory_28", img: "resources/customisation/character_accessories/character_accessories28.png"},
"accessory_29": {name: "accessory_29", img: "resources/customisation/character_accessories/character_accessories29.png"},
"accessory_30": {name: "accessory_30", img: "resources/customisation/character_accessories/character_accessories30.png"},
"accessory_31": {name: "accessory_31", img: "resources/customisation/character_accessories/character_accessories31.png"},
"accessory_32": {name: "accessory_32", img: "resources/customisation/character_accessories/character_accessories32.png"},
"accessory_mate_bottle": {name: "accessory_mate_bottle", img: "resources/customisation/character_accessories/mate_bottle1.png"},
"accessory_mask": {name: "accessory_mask", img: "resources/customisation/character_accessories/mask.png"}
accessory_1: {
name: "accessory_1",
img: "resources/customisation/character_accessories/character_accessories1.png",
},
accessory_2: {
name: "accessory_2",
img: "resources/customisation/character_accessories/character_accessories2.png",
},
accessory_3: {
name: "accessory_3",
img: "resources/customisation/character_accessories/character_accessories3.png",
},
accessory_4: {
name: "accessory_4",
img: "resources/customisation/character_accessories/character_accessories4.png",
},
accessory_5: {
name: "accessory_5",
img: "resources/customisation/character_accessories/character_accessories5.png",
},
accessory_6: {
name: "accessory_6",
img: "resources/customisation/character_accessories/character_accessories6.png",
},
accessory_7: {
name: "accessory_7",
img: "resources/customisation/character_accessories/character_accessories7.png",
},
accessory_8: {
name: "accessory_8",
img: "resources/customisation/character_accessories/character_accessories8.png",
},
accessory_9: {
name: "accessory_9",
img: "resources/customisation/character_accessories/character_accessories9.png",
},
accessory_10: {
name: "accessory_10",
img: "resources/customisation/character_accessories/character_accessories10.png",
},
accessory_11: {
name: "accessory_11",
img: "resources/customisation/character_accessories/character_accessories11.png",
},
accessory_12: {
name: "accessory_12",
img: "resources/customisation/character_accessories/character_accessories12.png",
},
accessory_13: {
name: "accessory_13",
img: "resources/customisation/character_accessories/character_accessories13.png",
},
accessory_14: {
name: "accessory_14",
img: "resources/customisation/character_accessories/character_accessories14.png",
},
accessory_15: {
name: "accessory_15",
img: "resources/customisation/character_accessories/character_accessories15.png",
},
accessory_16: {
name: "accessory_16",
img: "resources/customisation/character_accessories/character_accessories16.png",
},
accessory_17: {
name: "accessory_17",
img: "resources/customisation/character_accessories/character_accessories17.png",
},
accessory_18: {
name: "accessory_18",
img: "resources/customisation/character_accessories/character_accessories18.png",
},
accessory_19: {
name: "accessory_19",
img: "resources/customisation/character_accessories/character_accessories19.png",
},
accessory_20: {
name: "accessory_20",
img: "resources/customisation/character_accessories/character_accessories20.png",
},
accessory_21: {
name: "accessory_21",
img: "resources/customisation/character_accessories/character_accessories21.png",
},
accessory_22: {
name: "accessory_22",
img: "resources/customisation/character_accessories/character_accessories22.png",
},
accessory_23: {
name: "accessory_23",
img: "resources/customisation/character_accessories/character_accessories23.png",
},
accessory_24: {
name: "accessory_24",
img: "resources/customisation/character_accessories/character_accessories24.png",
},
accessory_25: {
name: "accessory_25",
img: "resources/customisation/character_accessories/character_accessories25.png",
},
accessory_26: {
name: "accessory_26",
img: "resources/customisation/character_accessories/character_accessories26.png",
},
accessory_27: {
name: "accessory_27",
img: "resources/customisation/character_accessories/character_accessories27.png",
},
accessory_28: {
name: "accessory_28",
img: "resources/customisation/character_accessories/character_accessories28.png",
},
accessory_29: {
name: "accessory_29",
img: "resources/customisation/character_accessories/character_accessories29.png",
},
accessory_30: {
name: "accessory_30",
img: "resources/customisation/character_accessories/character_accessories30.png",
},
accessory_31: {
name: "accessory_31",
img: "resources/customisation/character_accessories/character_accessories31.png",
},
accessory_32: {
name: "accessory_32",
img: "resources/customisation/character_accessories/character_accessories32.png",
},
accessory_mate_bottle: {
name: "accessory_mate_bottle",
img: "resources/customisation/character_accessories/mate_bottle1.png",
},
accessory_mask: { name: "accessory_mask", img: "resources/customisation/character_accessories/mask.png" },
};
export const LAYERS: BodyResourceDescriptionListInterface[] = [
@ -337,9 +443,9 @@ export const LAYERS: BodyResourceDescriptionListInterface[] = [
HAIR_RESOURCES,
CLOTHES_RESOURCES,
HATS_RESOURCES,
ACCESSORIES_RESOURCES
ACCESSORIES_RESOURCES,
];
export const OBJECTS: BodyResourceDescriptionInterface[] = [
{name:'teleportation', img:'resources/objects/teleportation.png'},
];
{ name: "teleportation", img: "resources/objects/teleportation.png" },
];

View File

@ -1,14 +1,12 @@
import Scene = Phaser.Scene;
import type {Character} from "./Character";
import type { Character } from "./Character";
//todo: improve this WIP
export class SpeechBubble {
private bubble: Phaser.GameObjects.Graphics;
private content: Phaser.GameObjects.Text;
constructor(scene: Scene, player: Character, text: string = "") {
const bubbleHeight = 50;
const bubblePadding = 10;
const bubbleWidth = bubblePadding * 2 + text.length * 10;
@ -49,15 +47,24 @@ export class SpeechBubble {
this.bubble.lineBetween(point2X, point2Y, point3X, point3Y);
this.bubble.lineBetween(point1X, point1Y, point3X, point3Y);
this.content = scene.add.text(0, 0, text, { fontFamily: 'Arial', fontSize: '20', color: '#000000', align: 'center', wordWrap: { width: bubbleWidth - (bubblePadding * 2) } });
this.content = scene.add.text(0, 0, text, {
fontFamily: "Arial",
fontSize: "20",
color: "#000000",
align: "center",
wordWrap: { width: bubbleWidth - bubblePadding * 2 },
});
player.add(this.content);
const bounds = this.content.getBounds();
this.content.setPosition(this.bubble.x + (bubbleWidth / 2) - (bounds.width / 2), this.bubble.y + (bubbleHeight / 2) - (bounds.height / 2));
this.content.setPosition(
this.bubble.x + bubbleWidth / 2 - bounds.width / 2,
this.bubble.y + bubbleHeight / 2 - bounds.height / 2
);
}
destroy(): void {
this.bubble.setVisible(false) //todo find a better way
this.bubble.setVisible(false); //todo find a better way
this.bubble.destroy();
this.content.destroy();
}

View File

@ -1,8 +1,7 @@
export class Sprite extends Phaser.GameObjects.Sprite {
constructor(scene: Phaser.Scene, x: number, y: number, texture: string, frame?: number | string) {
super(scene, x, y, texture, frame);
scene.sys.updateList.add(this);
scene.sys.displayList.add(this);
}
}
}

View File

@ -1,6 +1,6 @@
import {emoteEventStream} from "../../Connexion/EmoteEventStream";
import type {GameScene} from "./GameScene";
import type {Subscription} from "rxjs";
import { emoteEventStream } from "../../Connexion/EmoteEventStream";
import type { GameScene } from "./GameScene";
import type { Subscription } from "rxjs";
export class EmoteManager {
private subscription: Subscription;
@ -8,7 +8,7 @@ export class EmoteManager {
constructor(private scene: GameScene) {
this.subscription = emoteEventStream.stream.subscribe((event) => {
const actor = this.scene.MapPlayersByKey.get(event.userId);
if(actor) {
if (actor) {
actor.playEmote(event.emote);
}
});

View File

@ -36,11 +36,11 @@ export class GameManager {
this.startRoom = await connectionManager.initGameConnexion();
this.loadMap(this.startRoom);
if(!this.playerName) {
if (!this.playerName) {
const res = await Axios.get("/");
this.playerName = res.headers['bstlyusername'];
this.playerName = res.headers[ 'bstlyusername' ];
}
//If player name was not set show login scene with player name
//If Room si not public and Auth was not set, show login scene to authenticate user (OpenID - SSO - Anonymous)
if (!this.playerName || (this.startRoom.authenticationMandatory && !localUserStore.getAuthToken())) {

View File

@ -4,35 +4,39 @@ import BaseSound = Phaser.Sound.BaseSound;
import SoundConfig = Phaser.Types.Sound.SoundConfig;
class SoundManager {
private soundPromises : Map<string,Promise<BaseSound>> = new Map<string, Promise<Phaser.Sound.BaseSound>>();
public loadSound (loadPlugin: LoaderPlugin, soundManager : BaseSoundManager, soundUrl: string) : Promise<BaseSound> {
let soundPromise = this.soundPromises.get(soundUrl);
private soundPromises: Map<string, Promise<BaseSound>> = new Map<string, Promise<Phaser.Sound.BaseSound>>();
public loadSound(loadPlugin: LoaderPlugin, soundManager: BaseSoundManager, soundUrl: string): Promise<BaseSound> {
let soundPromise = this.soundPromises.get(soundUrl);
if (soundPromise !== undefined) {
return soundPromise;
}
soundPromise = new Promise<BaseSound>((res) => {
soundPromise = new Promise<BaseSound>((res) => {
const sound = soundManager.get(soundUrl);
if (sound !== null) {
return res(sound);
}
loadPlugin.audio(soundUrl, soundUrl);
loadPlugin.once('filecomplete-audio-' + soundUrl, () => {
loadPlugin.once("filecomplete-audio-" + soundUrl, () => {
res(soundManager.add(soundUrl));
});
loadPlugin.start();
});
this.soundPromises.set(soundUrl,soundPromise);
this.soundPromises.set(soundUrl, soundPromise);
return soundPromise;
}
public async playSound(loadPlugin: LoaderPlugin, soundManager : BaseSoundManager, soundUrl: string, config: SoundConfig|undefined) : Promise<void> {
const sound = await this.loadSound(loadPlugin,soundManager,soundUrl);
public async playSound(
loadPlugin: LoaderPlugin,
soundManager: BaseSoundManager,
soundUrl: string,
config: SoundConfig | undefined
): Promise<void> {
const sound = await this.loadSound(loadPlugin, soundManager, soundUrl);
if (config === undefined) sound.play();
else sound.play(config);
}
public stopSound(soundManager : BaseSoundManager,soundUrl : string){
public stopSound(soundManager: BaseSoundManager, soundUrl: string) {
soundManager.get(soundUrl).stop();
}
}

View File

@ -3,17 +3,23 @@
* It has coordinates and an "activation radius"
*/
import Sprite = Phaser.GameObjects.Sprite;
import type {GameScene} from "../Game/GameScene";
import type { GameScene } from "../Game/GameScene";
import type OutlinePipelinePlugin from "phaser3-rex-plugins/plugins/outlinepipeline-plugin.js";
type EventCallback = (state: unknown, parameters: unknown) => void;
export class ActionableItem {
private readonly activationRadiusSquared : number;
private readonly activationRadiusSquared: number;
private isSelectable: boolean = false;
private callbacks: Map<string, Array<EventCallback>> = new Map<string, Array<EventCallback>>();
public constructor(private id: number, private sprite: Sprite, private eventHandler: GameScene, private activationRadius: number, private onActivateCallback: (item: ActionableItem) => void) {
public constructor(
private id: number,
private sprite: Sprite,
private eventHandler: GameScene,
private activationRadius: number,
private onActivateCallback: (item: ActionableItem) => void
) {
this.activationRadiusSquared = activationRadius * activationRadius;
}
@ -25,8 +31,8 @@ export class ActionableItem {
* Returns the square of the distance to the object center IF we are in item action range
* OR null if we are out of range.
*/
public actionableDistance(x: number, y: number): number|null {
const distanceSquared = (x - this.sprite.x)*(x - this.sprite.x) + (y - this.sprite.y)*(y - this.sprite.y);
public actionableDistance(x: number, y: number): number | null {
const distanceSquared = (x - this.sprite.x) * (x - this.sprite.x) + (y - this.sprite.y) * (y - this.sprite.y);
if (distanceSquared < this.activationRadiusSquared) {
return distanceSquared;
} else {
@ -45,7 +51,7 @@ export class ActionableItem {
this.getOutlinePlugin()?.add(this.sprite, {
thickness: 2,
outlineColor: 0xffff00
outlineColor: 0xffff00,
});
}
@ -60,8 +66,8 @@ export class ActionableItem {
this.getOutlinePlugin()?.remove(this.sprite);
}
private getOutlinePlugin(): OutlinePipelinePlugin|undefined {
return this.sprite.scene.plugins.get('rexOutlinePipeline') as unknown as OutlinePipelinePlugin|undefined;
private getOutlinePlugin(): OutlinePipelinePlugin | undefined {
return this.sprite.scene.plugins.get("rexOutlinePipeline") as unknown as OutlinePipelinePlugin | undefined;
}
/**
@ -78,7 +84,7 @@ export class ActionableItem {
}
public on(eventName: string, callback: EventCallback): void {
let callbacksArray: Array<EventCallback>|undefined = this.callbacks.get(eventName);
let callbacksArray: Array<EventCallback> | undefined = this.callbacks.get(eventName);
if (callbacksArray === undefined) {
callbacksArray = new Array<EventCallback>();
this.callbacks.set(eventName, callbacksArray);

View File

@ -1,86 +1,91 @@
import * as Phaser from 'phaser';
import {Scene} from "phaser";
import * as Phaser from "phaser";
import { Scene } from "phaser";
import Sprite = Phaser.GameObjects.Sprite;
import type {ITiledMapObject} from "../../Map/ITiledMap";
import type {ItemFactoryInterface} from "../ItemFactoryInterface";
import type {GameScene} from "../../Game/GameScene";
import {ActionableItem} from "../ActionableItem";
import type { ITiledMapObject } from "../../Map/ITiledMap";
import type { ItemFactoryInterface } from "../ItemFactoryInterface";
import type { GameScene } from "../../Game/GameScene";
import { ActionableItem } from "../ActionableItem";
import * as tg from "generic-type-guard";
const isComputerState =
new tg.IsInterface().withProperties({
const isComputerState = new tg.IsInterface()
.withProperties({
status: tg.isString,
}).get();
})
.get();
type ComputerState = tg.GuardedType<typeof isComputerState>;
let state: ComputerState = {
'status': 'off'
status: "off",
};
export default {
preload: (loader: Phaser.Loader.LoaderPlugin): void => {
loader.atlas('computer', '/resources/items/computer/computer.png', '/resources/items/computer/computer_atlas.json');
loader.atlas(
"computer",
"/resources/items/computer/computer.png",
"/resources/items/computer/computer_atlas.json"
);
},
create: (scene: GameScene): void => {
scene.anims.create({
key: 'computer_off',
key: "computer_off",
frames: [
{
key: 'computer',
frame: 'computer_off'
}
key: "computer",
frame: "computer_off",
},
],
frameRate: 10,
repeat: -1
repeat: -1,
});
scene.anims.create({
key: 'computer_run',
key: "computer_run",
frames: [
{
key: 'computer',
frame: 'computer_on1'
},
{
key: 'computer',
frame: 'computer_on2'
}
],
{
key: "computer",
frame: "computer_on1",
},
{
key: "computer",
frame: "computer_on2",
},
],
frameRate: 5,
repeat: -1
repeat: -1,
});
},
factory: (scene: GameScene, object: ITiledMapObject, initState: unknown): ActionableItem => {
if (initState !== undefined) {
if (!isComputerState(initState)) {
throw new Error('Invalid state received for computer object');
throw new Error("Invalid state received for computer object");
}
state = initState;
}
// Idée: ESSAYER WebPack? https://paultavares.wordpress.com/2018/07/02/webpack-how-to-generate-an-es-module-bundle/
const computer = new Sprite(scene, object.x, object.y, 'computer');
const computer = new Sprite(scene, object.x, object.y, "computer");
scene.add.existing(computer);
if (state.status === 'on') {
computer.anims.play('computer_run');
if (state.status === "on") {
computer.anims.play("computer_run");
}
const item = new ActionableItem(object.id, computer, scene, 32, (item: ActionableItem) => {
if (state.status === 'off') {
state.status = 'on';
item.emit('TURN_ON', state);
if (state.status === "off") {
state.status = "on";
item.emit("TURN_ON", state);
} else {
state.status = 'off';
item.emit('TURN_OFF', state);
state.status = "off";
item.emit("TURN_OFF", state);
}
});
item.on('TURN_ON', () => {
computer.anims.play('computer_run');
item.on("TURN_ON", () => {
computer.anims.play("computer_run");
});
item.on('TURN_OFF', () => {
computer.anims.play('computer_off');
item.on("TURN_OFF", () => {
computer.anims.play("computer_off");
});
return item;
//scene.add.sprite(object.x, object.y, 'computer');
}
},
} as ItemFactoryInterface;

View File

@ -1,6 +1,6 @@
import type {GameScene} from "../Game/GameScene";
import type {ITiledMapObject} from "../Map/ITiledMap";
import type {ActionableItem} from "./ActionableItem";
import type { GameScene } from "../Game/GameScene";
import type { ITiledMapObject } from "../Map/ITiledMap";
import type { ActionableItem } from "./ActionableItem";
import LoaderPlugin = Phaser.Loader.LoaderPlugin;
export interface ItemFactoryInterface {

View File

@ -1,14 +1,13 @@
import {ResizableScene} from "./ResizableScene";
import {localUserStore} from "../../Connexion/LocalUserStore";
import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
import {loadCustomTexture} from "../Entity/PlayerTexturesLoadingManager";
import type {CharacterTexture} from "../../Connexion/LocalUser";
import { ResizableScene } from "./ResizableScene";
import { localUserStore } from "../../Connexion/LocalUserStore";
import type { BodyResourceDescriptionInterface } from "../Entity/PlayerTextures";
import { loadCustomTexture } from "../Entity/PlayerTexturesLoadingManager";
import type { CharacterTexture } from "../../Connexion/LocalUser";
export abstract class AbstractCharacterScene extends ResizableScene {
loadCustomSceneSelectCharacters() : Promise<BodyResourceDescriptionInterface[]> {
loadCustomSceneSelectCharacters(): Promise<BodyResourceDescriptionInterface[]> {
const textures = this.getTextures();
const promises : Promise<BodyResourceDescriptionInterface>[] = [];
const promises: Promise<BodyResourceDescriptionInterface>[] = [];
if (textures) {
for (const texture of textures) {
if (texture.level === -1) {
@ -17,10 +16,10 @@ export abstract class AbstractCharacterScene extends ResizableScene {
promises.push(loadCustomTexture(this.load, texture));
}
}
return Promise.all(promises)
return Promise.all(promises);
}
loadSelectSceneCharacters() : Promise<BodyResourceDescriptionInterface[]> {
loadSelectSceneCharacters(): Promise<BodyResourceDescriptionInterface[]> {
const textures = this.getTextures();
const promises: Promise<BodyResourceDescriptionInterface>[] = [];
if (textures) {
@ -31,10 +30,10 @@ export abstract class AbstractCharacterScene extends ResizableScene {
promises.push(loadCustomTexture(this.load, texture));
}
}
return Promise.all(promises)
return Promise.all(promises);
}
private getTextures() : CharacterTexture[]|undefined{
private getTextures(): CharacterTexture[] | undefined {
const localUser = localUserStore.getLocalUser();
return localUser?.textures;
}

View File

@ -1,4 +1,4 @@
import {Scene} from "phaser";
import { Scene } from "phaser";
import DOMElement = Phaser.GameObjects.DOMElement;
export abstract class ResizableScene extends Scene {
@ -11,13 +11,10 @@ export abstract class ResizableScene extends Scene {
* @param defaultWidth The width of the DOM element. We try to compute it but it may not be available if called from "create".
*/
public centerXDomElement(object: DOMElement, defaultWidth: number): void {
object.x = (this.scale.width / 2) -
(
object
&& object.node
&& object.node.getBoundingClientRect().width > 0
? (object.node.getBoundingClientRect().width / 2 / this.scale.zoom)
: (defaultWidth / this.scale.zoom)
);
object.x =
this.scale.width / 2 -
(object && object.node && object.node.getBoundingClientRect().width > 0
? object.node.getBoundingClientRect().width / 2 / this.scale.zoom
: defaultWidth / this.scale.zoom);
}
}

View File

@ -1,14 +1,13 @@
import { SelectCharacterScene } from "./SelectCharacterScene";
export class SelectCharacterMobileScene extends SelectCharacterScene {
create(){
create() {
super.create();
this.onResize();
this.selectedRectangle.destroy();
}
protected defineSetupPlayer(num: number){
protected defineSetupPlayer(num: number) {
const deltaX = 30;
const deltaY = 2;
let [playerX, playerY] = this.getCharacterPosition();
@ -16,48 +15,44 @@ export class SelectCharacterMobileScene extends SelectCharacterScene {
let playerScale = 1.5;
let playerOpacity = 1;
if( this.currentSelectUser !== num ){
if (this.currentSelectUser !== num) {
playerVisible = false;
}
if( num === (this.currentSelectUser + 1) ){
if (num === this.currentSelectUser + 1) {
playerY -= deltaY;
playerX += deltaX;
playerScale = 0.8;
playerOpacity = 0.6;
playerVisible = true;
}
if( num === (this.currentSelectUser + 2) ){
if (num === this.currentSelectUser + 2) {
playerY -= deltaY;
playerX += (deltaX * 2);
playerX += deltaX * 2;
playerScale = 0.8;
playerOpacity = 0.6;
playerVisible = true;
}
if( num === (this.currentSelectUser - 1) ){
if (num === this.currentSelectUser - 1) {
playerY -= deltaY;
playerX -= deltaX;
playerScale = 0.8;
playerOpacity = 0.6;
playerVisible = true;
}
if( num === (this.currentSelectUser - 2) ){
if (num === this.currentSelectUser - 2) {
playerY -= deltaY;
playerX -= (deltaX * 2);
playerX -= deltaX * 2;
playerScale = 0.8;
playerOpacity = 0.6;
playerVisible = true;
}
return {playerX, playerY, playerScale, playerOpacity, playerVisible}
return { playerX, playerY, playerScale, playerOpacity, playerVisible };
}
/**
/**
* Returns pixel position by on column and row number
*/
protected getCharacterPosition(): [number, number] {
return [
this.game.renderer.width / 2,
this.game.renderer.height / 3
];
}
protected getCharacterPosition(): [number, number] {
return [this.game.renderer.width / 2, this.game.renderer.height / 3];
}
}

View File

@ -1,13 +1,10 @@
export enum PlayerAnimationDirections {
Down = 'down',
Left = 'left',
Up = 'up',
Right = 'right',
Down = "down",
Left = "left",
Up = "up",
Right = "right",
}
export enum PlayerAnimationTypes {
Walk = 'walk',
Idle = 'idle',
Walk = "walk",
Idle = "idle",
}

View File

@ -1,9 +1,9 @@
import {PlayerAnimationDirections} from "./Animation";
import type {GameScene} from "../Game/GameScene";
import {UserInputEvent, UserInputManager} from "../UserInput/UserInputManager";
import {Character} from "../Entity/Character";
import {userMovingStore} from "../../Stores/GameStore";
import {EmoteMenu, EmoteMenuClickEvent} from "../Components/EmoteMenu";
import { PlayerAnimationDirections } from "./Animation";
import type { GameScene } from "../Game/GameScene";
import { UserInputEvent, UserInputManager } from "../UserInput/UserInputManager";
import { Character } from "../Entity/Character";
import { userMovingStore } from "../../Stores/GameStore";
import { EmoteMenu, EmoteMenuClickEvent } from "../Components/EmoteMenu";
export const hasMovedEventName = "hasMoved";
export const requestEmoteEventName = "requestEmote";
@ -11,7 +11,7 @@ export const requestEmoteEventName = "requestEmote";
export class Player extends Character {
private previousDirection: string = PlayerAnimationDirections.Down;
private wasMoving: boolean = false;
private emoteMenu: EmoteMenu|null = null;
private emoteMenu: EmoteMenu | null = null;
private updateListener: () => void;
constructor(
@ -99,7 +99,7 @@ export class Player extends Character {
this.emoteMenu = new EmoteMenu(this.scene, this.x, this.y, this.userInputManager)
}
if(this.emoteMenu.isOpen()) {
if (this.emoteMenu.isOpen()) {
this.closeEmoteMenu();
} else {
this.openEmoteMenu();

View File

@ -3,13 +3,13 @@ export class WAError extends Error {
private _subTitle: string;
private _details: string;
constructor (title: string, subTitle: string, details: string) {
super(title+' - '+subTitle+' - '+details);
constructor(title: string, subTitle: string, details: string) {
super(title + " - " + subTitle + " - " + details);
this._title = title;
this._subTitle = subTitle;
this._details = details;
// Set the prototype explicitly.
Object.setPrototypeOf (this, WAError.prototype);
Object.setPrototypeOf(this, WAError.prototype);
}
get title(): string {

View File

@ -1,4 +1,3 @@
interface Size {
width: number;
height: number;
@ -23,14 +22,14 @@ export class HdpiManager {
*
* @param realPixelScreenSize
*/
public getOptimalGameSize(realPixelScreenSize: Size): { game: Size, real: Size } {
public getOptimalGameSize(realPixelScreenSize: Size): { game: Size; real: Size } {
const realPixelNumber = realPixelScreenSize.width * realPixelScreenSize.height;
// If the screen has not a definition small enough to match the minimum number of pixels we want to display,
// let's make the canvas the size of the screen (in real pixels)
if (realPixelNumber <= this.minRecommendedGamePixelsNumber) {
return {
game: realPixelScreenSize,
real: realPixelScreenSize
real: realPixelScreenSize,
};
}
@ -49,8 +48,8 @@ export class HdpiManager {
real: {
width: realPixelScreenSize.width,
height: realPixelScreenSize.height,
}
}
},
};
}
const gameWidth = Math.ceil(realPixelScreenSize.width / optimalZoomLevel / this._zoomModifier);
@ -58,8 +57,12 @@ export class HdpiManager {
// Let's ensure we display a minimum of pixels, even if crazily zoomed in.
if (gameWidth * gameHeight < this.absoluteMinPixelNumber) {
const minGameHeight = Math.sqrt(this.absoluteMinPixelNumber * realPixelScreenSize.height / realPixelScreenSize.width);
const minGameWidth = Math.sqrt(this.absoluteMinPixelNumber * realPixelScreenSize.width / realPixelScreenSize.height);
const minGameHeight = Math.sqrt(
(this.absoluteMinPixelNumber * realPixelScreenSize.height) / realPixelScreenSize.width
);
const minGameWidth = Math.sqrt(
(this.absoluteMinPixelNumber * realPixelScreenSize.width) / realPixelScreenSize.height
);
// Let's reset the zoom modifier (WARNING this is a SIDE EFFECT in a getter)
this._zoomModifier = realPixelScreenSize.width / minGameWidth / optimalZoomLevel;
@ -72,9 +75,8 @@ export class HdpiManager {
real: {
width: realPixelScreenSize.width,
height: realPixelScreenSize.height,
}
}
},
};
}
return {
@ -85,8 +87,8 @@ export class HdpiManager {
real: {
width: Math.ceil(realPixelScreenSize.width / optimalZoomLevel) * optimalZoomLevel,
height: Math.ceil(realPixelScreenSize.height / optimalZoomLevel) * optimalZoomLevel,
}
}
},
};
}
/**
@ -95,7 +97,7 @@ export class HdpiManager {
private getOptimalZoomLevel(realPixelNumber: number): number {
const result = Math.sqrt(realPixelNumber / this.minRecommendedGamePixelsNumber);
if (1.5 <= result && result < 2) {
return 1.5
return 1.5;
} else {
return Math.floor(result);
}

View File

@ -1,10 +1,9 @@
import {HdpiManager} from "./HdpiManager";
import { HdpiManager } from "./HdpiManager";
import ScaleManager = Phaser.Scale.ScaleManager;
import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager";
import type {Game} from "../Game/Game";
import {ResizableScene} from "../Login/ResizableScene";
import {HtmlUtils} from "../../WebRtc/HtmlUtils";
import { coWebsiteManager } from "../../WebRtc/CoWebsiteManager";
import type { Game } from "../Game/Game";
import { ResizableScene } from "../Login/ResizableScene";
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
class WaScaleManager {
private hdpiManager: HdpiManager;
@ -23,26 +22,29 @@ class WaScaleManager {
}
public applyNewSize() {
const {width, height} = coWebsiteManager.getGameSize();
const { width, height } = coWebsiteManager.getGameSize();
let devicePixelRatio = 1;
if (window.devicePixelRatio) {
devicePixelRatio = window.devicePixelRatio;
}
const { game: gameSize, real: realSize } = this.hdpiManager.getOptimalGameSize({width: width * devicePixelRatio, height: height * devicePixelRatio});
const { game: gameSize, real: realSize } = this.hdpiManager.getOptimalGameSize({
width: width * devicePixelRatio,
height: height * devicePixelRatio,
});
this.actualZoom = realSize.width / gameSize.width / devicePixelRatio;
this.scaleManager.setZoom(realSize.width / gameSize.width / devicePixelRatio)
this.scaleManager.setZoom(realSize.width / gameSize.width / devicePixelRatio);
this.scaleManager.resize(gameSize.width, gameSize.height);
// Override bug in canvas resizing in Phaser. Let's resize the canvas ourselves
const style = this.scaleManager.canvas.style;
style.width = Math.ceil(realSize.width / devicePixelRatio) + 'px';
style.height = Math.ceil(realSize.height / devicePixelRatio) + 'px';
style.width = Math.ceil(realSize.width / devicePixelRatio) + "px";
style.height = Math.ceil(realSize.height / devicePixelRatio) + "px";
// Resize the game element at the same size at the canvas
const gameStyle = HtmlUtils.getElementByIdOrFail<HTMLDivElement>('game').style;
const gameStyle = HtmlUtils.getElementByIdOrFail<HTMLDivElement>("game").style;
gameStyle.width = style.width;
gameStyle.height = style.height;
@ -70,7 +72,7 @@ class WaScaleManager {
this._saveZoom = this.hdpiManager.zoomModifier;
}
public restoreZoom(): void{
public restoreZoom(): void {
this.hdpiManager.zoomModifier = this._saveZoom;
this.applyNewSize();
}
@ -81,7 +83,6 @@ class WaScaleManager {
public get uiScalingFactor(): number {
return this.actualZoom > 1 ? 1 : 1.2;
}
}
export const waScaleManager = new WaScaleManager(640*480, 196*196);
export const waScaleManager = new WaScaleManager(640 * 480, 196 * 196);

View File

@ -1,6 +1,6 @@
import {Pinch} from "phaser3-rex-plugins/plugins/gestures.js";
import {waScaleManager} from "../Services/WaScaleManager";
import {GameScene} from "../Game/GameScene";
import { Pinch } from "phaser3-rex-plugins/plugins/gestures.js";
import { waScaleManager } from "../Services/WaScaleManager";
import { GameScene } from "../Game/GameScene";
export class PinchManager {
private scene: Phaser.Scene;
@ -15,18 +15,18 @@ export class PinchManager {
// We are smoothing its value with previous values to prevent the flicking.
let smoothPinch = 1;
this.pinch.on('pinchstart', () => {
this.pinch.on("pinchstart", () => {
smoothPinch = 1;
});
this.pinch.on('pinch', (pinch:any) => { // eslint-disable-line
// eslint-disable-next-line
this.pinch.on("pinch", (pinch: any) => {
if (pinch.scaleFactor > 1.2 || pinch.scaleFactor < 0.8) {
// Pinch too fast! Probably a bad measure.
return;
}
smoothPinch = 3/5*smoothPinch + 2/5*pinch.scaleFactor;
smoothPinch = (3 / 5) * smoothPinch + (2 / 5) * pinch.scaleFactor;
if (this.scene instanceof GameScene) {
this.scene.zoomByFactor(smoothPinch);
} else {

View File

@ -2,4 +2,4 @@ import { writable } from "svelte/store";
export const consoleGlobalMessageManagerVisibleStore = writable(false);
export const consoleGlobalMessageManagerFocusStore = writable(false);
export const consoleGlobalMessageManagerFocusStore = writable(false);

View File

@ -1,4 +1,4 @@
import {writable} from "svelte/store";
import { writable } from "svelte/store";
/**
* A store that contains a list of error messages to be displayed.
@ -8,7 +8,7 @@ function createErrorStore() {
return {
subscribe,
addErrorMessage: (e: string|Error): void => {
addErrorMessage: (e: string | Error): void => {
update((messages: string[]) => {
let message: string;
if (e instanceof Error) {
@ -26,7 +26,7 @@ function createErrorStore() {
},
clearMessages: (): void => {
set([]);
}
},
};
}

View File

@ -1,8 +1,10 @@
export class BrowserTooOldError extends Error {
static NAME = 'BrowserTooOldError';
static NAME = "BrowserTooOldError";
constructor() {
super('Unable to access your camera or microphone. Your browser is too old. Please consider upgrading your browser or try using a recent version of Chrome.');
super(
"Unable to access your camera or microphone. Your browser is too old. Please consider upgrading your browser or try using a recent version of Chrome."
);
this.name = BrowserTooOldError.NAME;
}
}

View File

@ -1,8 +1,10 @@
export class WebviewOnOldIOS extends Error {
static NAME = 'WebviewOnOldIOS';
static NAME = "WebviewOnOldIOS";
constructor() {
super('Your iOS version cannot use video/audio in the browser unless you are using Safari. Please switch to Safari or upgrade iOS to 14.3 or above.');
super(
"Your iOS version cannot use video/audio in the browser unless you are using Safari. Please switch to Safari or upgrade iOS to 14.3 or above."
);
this.name = WebviewOnOldIOS.NAME;
}
}

View File

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

View File

@ -4,7 +4,7 @@ import { writable } from "svelte/store";
* A store that contains the URL of the sound currently playing
*/
function createSoundPlayingStore() {
const { subscribe, set, update } = writable<string|null>(null);
const { subscribe, set, update } = writable<string | null>(null);
return {
subscribe,
@ -13,9 +13,7 @@ function createSoundPlayingStore() {
},
soundEnded: () => {
set(null);
}
},
};
}

View File

@ -1,16 +1,14 @@
class TouchScreenManager {
readonly supportTouchScreen:boolean;
readonly supportTouchScreen: boolean;
constructor() {
this.supportTouchScreen = this.detectTouchscreen();
}
//found here: https://stackoverflow.com/questions/4817029/whats-the-best-way-to-detect-a-touch-screen-device-using-javascript#4819886
detectTouchscreen(): boolean {
return (('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0));
return "ontouchstart" in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0;
}
}
export const touchScreenManager = new TouchScreenManager();
export const touchScreenManager = new TouchScreenManager();

View File

@ -1,12 +1,9 @@
export function isIOS(): boolean {
return [
'iPad Simulator',
'iPhone Simulator',
'iPod Simulator',
'iPad',
'iPhone',
'iPod'
].includes(navigator.platform)
return (
["iPad Simulator", "iPhone Simulator", "iPod Simulator", "iPad", "iPhone", "iPod"].includes(
navigator.platform
) ||
// iPad on iOS 13 detection
|| (navigator.userAgent.includes("Mac") && "ontouchend" in document)
(navigator.userAgent.includes("Mac") && "ontouchend" in document)
);
}

View File

@ -1,16 +1,16 @@
declare module 'phaser3-rex-plugins/plugins/virtualjoystick.js' {
declare module "phaser3-rex-plugins/plugins/virtualjoystick.js" {
const content: any; // eslint-disable-line
export default content;
}
declare module 'phaser3-rex-plugins/plugins/gestures-plugin.js' {
declare module "phaser3-rex-plugins/plugins/gestures-plugin.js" {
const content: any; // eslint-disable-line
export default content;
}
declare module 'phaser3-rex-plugins/plugins/webfontloader-plugin.js' {
declare module "phaser3-rex-plugins/plugins/webfontloader-plugin.js" {
const content: any; // eslint-disable-line
export default content;
}
declare module 'phaser3-rex-plugins/plugins/outlinepipeline-plugin.js' {
declare module "phaser3-rex-plugins/plugins/outlinepipeline-plugin.js" {
import GameObject = Phaser.GameObjects.GameObject;
class OutlinePipelinePlugin {
@ -21,6 +21,6 @@ declare module 'phaser3-rex-plugins/plugins/outlinepipeline-plugin.js' {
export default OutlinePipelinePlugin;
}
declare module 'phaser3-rex-plugins/plugins/gestures.js' {
declare module "phaser3-rex-plugins/plugins/gestures.js" {
export const Pinch: any; // eslint-disable-line
}

View File

@ -9,7 +9,7 @@ const JITSI_URL: string | undefined = process.env.JITSI_URL === "" ? undefined :
const JITSI_ISS = process.env.JITSI_ISS || "";
const SECRET_JITSI_KEY = process.env.SECRET_JITSI_KEY || "";
const PUSHER_HTTP_PORT = parseInt(process.env.PUSHER_HTTP_PORT || "8080") || 8080;
export const SOCKET_IDLE_TIMER = parseInt(process.env.SOCKET_IDLE_TIMER as string) || 30; // maximum time (in second) without activity before a socket is closed
export const SOCKET_IDLE_TIMER = parseInt(process.env.SOCKET_IDLE_TIMER as string) || 32; // maximum time (in second) without activity before a socket is closed
export const FRONT_URL = process.env.FRONT_URL || "http://localhost";
export const OPID_CLIENT_ID = process.env.OPID_CLIENT_ID || "";