Merge branch 'develop' of github.com:thecodingmachine/workadventure into feature/animated-tiles
# Conflicts: # front/package.json # front/tsconfig.json # front/yarn.lock
This commit is contained in:
commit
e4dab5fd0d
555 changed files with 22975 additions and 17853 deletions
220
front/src/Phaser/Companion/Companion.ts
Normal file
220
front/src/Phaser/Companion/Companion.ts
Normal file
|
@ -0,0 +1,220 @@
|
|||
import Sprite = Phaser.GameObjects.Sprite;
|
||||
import Container = Phaser.GameObjects.Container;
|
||||
import { PlayerAnimationDirections, PlayerAnimationTypes } from "../Player/Animation";
|
||||
|
||||
export interface CompanionStatus {
|
||||
x: number;
|
||||
y: number;
|
||||
name: string;
|
||||
moving: boolean;
|
||||
direction: PlayerAnimationDirections;
|
||||
}
|
||||
|
||||
export class Companion extends Container {
|
||||
public sprites: Map<string, Sprite>;
|
||||
|
||||
private delta: number;
|
||||
private invisible: boolean;
|
||||
private updateListener: Function;
|
||||
private target: { x: number, y: number, direction: PlayerAnimationDirections };
|
||||
|
||||
private companionName: string;
|
||||
private direction: PlayerAnimationDirections;
|
||||
private animationType: PlayerAnimationTypes;
|
||||
|
||||
constructor(scene: Phaser.Scene, x: number, y: number, name: string, texturePromise: Promise<string>) {
|
||||
super(scene, x + 14, y + 4);
|
||||
|
||||
this.sprites = new Map<string, Sprite>();
|
||||
|
||||
this.delta = 0;
|
||||
this.invisible = true;
|
||||
this.target = { x, y, direction: PlayerAnimationDirections.Down };
|
||||
|
||||
this.direction = PlayerAnimationDirections.Down;
|
||||
this.animationType = PlayerAnimationTypes.Idle;
|
||||
|
||||
this.companionName = name;
|
||||
|
||||
texturePromise.then(resource => {
|
||||
this.addResource(resource);
|
||||
this.invisible = false;
|
||||
})
|
||||
|
||||
this.scene.physics.world.enableBody(this);
|
||||
|
||||
this.getBody().setImmovable(true);
|
||||
this.getBody().setCollideWorldBounds(false);
|
||||
this.setSize(16, 16);
|
||||
this.getBody().setSize(16, 16);
|
||||
this.getBody().setOffset(0, 8);
|
||||
|
||||
this.setDepth(-1);
|
||||
|
||||
this.updateListener = this.step.bind(this);
|
||||
this.scene.events.addListener('update', this.updateListener);
|
||||
|
||||
this.scene.add.existing(this);
|
||||
}
|
||||
|
||||
public setTarget(x: number, y: number, direction: PlayerAnimationDirections) {
|
||||
this.target = { x, y: y + 4, direction };
|
||||
}
|
||||
|
||||
public step(time: number, delta: number) {
|
||||
if (typeof this.target === 'undefined') return;
|
||||
|
||||
this.delta += delta;
|
||||
if (this.delta < 128) {
|
||||
return;
|
||||
}
|
||||
this.delta = 0;
|
||||
|
||||
const xDist = this.target.x - this.x;
|
||||
const yDist = this.target.y - this.y;
|
||||
|
||||
const distance = Math.pow(xDist, 2) + Math.pow(yDist, 2);
|
||||
|
||||
if (distance < 650) {
|
||||
this.animationType = PlayerAnimationTypes.Idle;
|
||||
this.direction = this.target.direction;
|
||||
|
||||
this.getBody().stop();
|
||||
} else {
|
||||
this.animationType = PlayerAnimationTypes.Walk;
|
||||
|
||||
const xDir = xDist / Math.max(Math.abs(xDist), 1);
|
||||
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);
|
||||
|
||||
if (Math.abs(xDist) > Math.abs(yDist)) {
|
||||
if (xDist < 0) {
|
||||
this.direction = PlayerAnimationDirections.Left;
|
||||
} else {
|
||||
this.direction = PlayerAnimationDirections.Right;
|
||||
}
|
||||
} else {
|
||||
if (yDist < 0) {
|
||||
this.direction = PlayerAnimationDirections.Up;
|
||||
} else {
|
||||
this.direction = PlayerAnimationDirections.Down;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.setDepth(this.y);
|
||||
this.playAnimation(this.direction, this.animationType);
|
||||
}
|
||||
|
||||
public getStatus(): CompanionStatus {
|
||||
const { x, y, direction, animationType, companionName } = this;
|
||||
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
direction,
|
||||
moving: animationType === PlayerAnimationTypes.Walk,
|
||||
name: companionName
|
||||
}
|
||||
}
|
||||
|
||||
private playAnimation(direction: PlayerAnimationDirections, type: PlayerAnimationTypes): void {
|
||||
if (this.invisible) return;
|
||||
|
||||
for (const [resource, sprite] of this.sprites.entries()) {
|
||||
sprite.play(`${resource}-${direction}-${type}`, true);
|
||||
}
|
||||
}
|
||||
|
||||
private addResource(resource: string, frame?: string | number): void {
|
||||
const sprite = new Sprite(this.scene, 0, 0, resource, frame);
|
||||
|
||||
this.add(sprite);
|
||||
|
||||
this.getAnimations(resource).forEach(animation => {
|
||||
this.scene.anims.create(animation);
|
||||
});
|
||||
|
||||
this.scene.sys.updateList.add(sprite);
|
||||
this.sprites.set(resource, sprite);
|
||||
}
|
||||
|
||||
private getAnimations(resource: string): Phaser.Types.Animations.Animation[] {
|
||||
return [
|
||||
{
|
||||
key: `${resource}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Idle}`,
|
||||
frames: this.scene.anims.generateFrameNumbers(resource, {frames: [1]}),
|
||||
frameRate: 10,
|
||||
repeat: 1
|
||||
},
|
||||
{
|
||||
key: `${resource}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Idle}`,
|
||||
frames: this.scene.anims.generateFrameNumbers(resource, {frames: [4]}),
|
||||
frameRate: 10,
|
||||
repeat: 1
|
||||
},
|
||||
{
|
||||
key: `${resource}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Idle}`,
|
||||
frames: this.scene.anims.generateFrameNumbers(resource, {frames: [7]}),
|
||||
frameRate: 10,
|
||||
repeat: 1
|
||||
},
|
||||
{
|
||||
key: `${resource}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Idle}`,
|
||||
frames: this.scene.anims.generateFrameNumbers(resource, {frames: [10]}),
|
||||
frameRate: 10,
|
||||
repeat: 1
|
||||
},
|
||||
{
|
||||
key: `${resource}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Walk}`,
|
||||
frames: this.scene.anims.generateFrameNumbers(resource, {frames: [0, 1, 2]}),
|
||||
frameRate: 15,
|
||||
repeat: -1
|
||||
},
|
||||
{
|
||||
key: `${resource}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Walk}`,
|
||||
frames: this.scene.anims.generateFrameNumbers(resource, {frames: [3, 4, 5]}),
|
||||
frameRate: 15,
|
||||
repeat: -1
|
||||
},
|
||||
{
|
||||
key: `${resource}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Walk}`,
|
||||
frames: this.scene.anims.generateFrameNumbers(resource, {frames: [6, 7, 8]}),
|
||||
frameRate: 15,
|
||||
repeat: -1
|
||||
},
|
||||
{
|
||||
key: `${resource}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Walk}`,
|
||||
frames: this.scene.anims.generateFrameNumbers(resource, {frames: [9, 10, 11]}),
|
||||
frameRate: 15,
|
||||
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');
|
||||
}
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
for (const sprite of this.sprites.values()) {
|
||||
if (this.scene) {
|
||||
this.scene.sys.updateList.remove(sprite);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.scene) {
|
||||
this.scene.events.removeListener('update', this.updateListener);
|
||||
}
|
||||
|
||||
super.destroy();
|
||||
}
|
||||
}
|
14
front/src/Phaser/Companion/CompanionTextures.ts
Normal file
14
front/src/Phaser/Companion/CompanionTextures.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
export interface CompanionResourceDescriptionInterface {
|
||||
name: string,
|
||||
img: string,
|
||||
behaviour: "dog" | "cat"
|
||||
}
|
||||
|
||||
export const COMPANION_RESOURCES: CompanionResourceDescriptionInterface[] = [
|
||||
{ name: "dog1", img: "resources/characters/pipoya/Dog 01-1.png", behaviour: "dog" },
|
||||
{ name: "dog2", img: "resources/characters/pipoya/Dog 01-2.png", behaviour: "dog" },
|
||||
{ name: "dog3", img: "resources/characters/pipoya/Dog 01-3.png", behaviour: "dog" },
|
||||
{ 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" },
|
||||
]
|
|
@ -0,0 +1,29 @@
|
|||
import LoaderPlugin = Phaser.Loader.LoaderPlugin;
|
||||
import { COMPANION_RESOURCES, CompanionResourceDescriptionInterface } from "./CompanionTextures";
|
||||
|
||||
export const getAllCompanionResources = (loader: LoaderPlugin): CompanionResourceDescriptionInterface[] => {
|
||||
COMPANION_RESOURCES.forEach((resource: CompanionResourceDescriptionInterface) => {
|
||||
lazyLoadCompanionResource(loader, resource.name);
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
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.
|
||||
});
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
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);
|
||||
|
@ -6,6 +8,6 @@ export class ChatModeIcon extends Phaser.GameObjects.Sprite {
|
|||
this.setOrigin(0, 1);
|
||||
this.setInteractive();
|
||||
this.setVisible(false);
|
||||
this.setDepth(99999);
|
||||
this.setDepth(DEPTH_INGAME_TEXT_INDEX);
|
||||
}
|
||||
}
|
54
front/src/Phaser/Components/Loader.ts
Normal file
54
front/src/Phaser/Components/Loader.ts
Normal file
|
@ -0,0 +1,54 @@
|
|||
import ImageFrameConfig = Phaser.Types.Loader.FileTypes.ImageFrameConfig;
|
||||
|
||||
const LogoNameIndex: string = 'logoLoading';
|
||||
const TextName: string = 'Loading...';
|
||||
const LogoResource: string = 'resources/logos/logo.png';
|
||||
const LogoFrame: ImageFrameConfig = {frameWidth: 307, frameHeight: 59};
|
||||
|
||||
export const addLoader = (scene: Phaser.Scene): void => {
|
||||
// If there is nothing to load, do not display the loader.
|
||||
if (scene.load.list.entries.length === 0) {
|
||||
return;
|
||||
}
|
||||
let loadingText: Phaser.GameObjects.Text|null = null;
|
||||
const loadingBarWidth: number = Math.floor(scene.game.renderer.width / 3);
|
||||
const loadingBarHeight: number = 16;
|
||||
const padding: number = 5;
|
||||
|
||||
const promiseLoadLogoTexture = new Promise<Phaser.GameObjects.Image>((res) => {
|
||||
if(scene.load.textureManager.exists(LogoNameIndex)){
|
||||
return res(scene.add.image(scene.game.renderer.width / 2, scene.game.renderer.height / 2 - 150, LogoNameIndex));
|
||||
}else{
|
||||
//add loading if logo image is not ready
|
||||
loadingText = scene.add.text(scene.game.renderer.width / 2, scene.game.renderer.height / 2 - 50, TextName);
|
||||
}
|
||||
scene.load.spritesheet(LogoNameIndex, LogoResource, LogoFrame);
|
||||
scene.load.once(`filecomplete-spritesheet-${LogoNameIndex}`, () => {
|
||||
if(loadingText){
|
||||
loadingText.destroy();
|
||||
}
|
||||
return res(scene.add.image(scene.game.renderer.width / 2, scene.game.renderer.height / 2 - 150, LogoNameIndex));
|
||||
});
|
||||
});
|
||||
|
||||
const progressContainer = scene.add.graphics();
|
||||
const progress = scene.add.graphics();
|
||||
progressContainer.fillStyle(0x444444, 0.8);
|
||||
progressContainer.fillRect((scene.game.renderer.width - loadingBarWidth) / 2 - padding, scene.game.renderer.height / 2 + 50 - padding, loadingBarWidth + padding * 2, loadingBarHeight + padding * 2);
|
||||
|
||||
scene.load.on('progress', (value: number) => {
|
||||
progress.clear();
|
||||
progress.fillStyle(0xBBBBBB, 1);
|
||||
progress.fillRect((scene.game.renderer.width - loadingBarWidth) / 2, scene.game.renderer.height / 2 + 50, loadingBarWidth * value, loadingBarHeight);
|
||||
});
|
||||
scene.load.on('complete', () => {
|
||||
if(loadingText){
|
||||
loadingText.destroy();
|
||||
}
|
||||
promiseLoadLogoTexture.then((resLoadingImage: Phaser.GameObjects.Image) => {
|
||||
resLoadingImage.destroy();
|
||||
});
|
||||
progress.destroy();
|
||||
progressContainer.destroy();
|
||||
});
|
||||
}
|
65
front/src/Phaser/Components/MobileJoystick.ts
Normal file
65
front/src/Phaser/Components/MobileJoystick.ts
Normal file
|
@ -0,0 +1,65 @@
|
|||
import VirtualJoystick from 'phaser3-rex-plugins/plugins/virtualjoystick.js';
|
||||
import {waScaleManager} from "../Services/WaScaleManager";
|
||||
import {DEPTH_INGAME_TEXT_INDEX} from "../Game/DepthIndexes";
|
||||
|
||||
//the assets were found here: https://hannemann.itch.io/virtual-joystick-pack-free
|
||||
export const joystickBaseKey = 'joystickBase';
|
||||
export const joystickBaseImg = 'resources/objects/joystickSplitted.png';
|
||||
export const joystickThumbKey = 'joystickThumb';
|
||||
export const joystickThumbImg = 'resources/objects/smallHandleFilledGrey.png';
|
||||
|
||||
const baseSize = 50;
|
||||
const thumbSize = 25;
|
||||
const radius = 17.5;
|
||||
|
||||
export class MobileJoystick extends VirtualJoystick {
|
||||
private resizeCallback: () => void;
|
||||
|
||||
constructor(scene: Phaser.Scene) {
|
||||
super(scene, {
|
||||
x: -1000,
|
||||
y: -1000,
|
||||
radius: radius * window.devicePixelRatio,
|
||||
base: scene.add.image(0, 0, joystickBaseKey).setDisplaySize(baseSize * window.devicePixelRatio, baseSize * window.devicePixelRatio).setDepth(DEPTH_INGAME_TEXT_INDEX),
|
||||
thumb: scene.add.image(0, 0, joystickThumbKey).setDisplaySize(thumbSize * window.devicePixelRatio, thumbSize * window.devicePixelRatio).setDepth(DEPTH_INGAME_TEXT_INDEX),
|
||||
enable: true,
|
||||
dir: "8dir",
|
||||
});
|
||||
this.visible = false;
|
||||
this.enable = false;
|
||||
|
||||
this.scene.input.on('pointerdown', (pointer: Phaser.Input.Pointer) => {
|
||||
if (!pointer.wasTouch) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Let's only display the joystick if there is one finger on the screen
|
||||
if ((pointer.event as TouchEvent).touches.length === 1) {
|
||||
this.x = pointer.x;
|
||||
this.y = pointer.y;
|
||||
this.visible = true;
|
||||
this.enable = true;
|
||||
} else {
|
||||
this.visible = false;
|
||||
this.enable = false;
|
||||
}
|
||||
});
|
||||
this.scene.input.on('pointerup', () => {
|
||||
this.visible = false;
|
||||
this.enable = false;
|
||||
});
|
||||
this.resizeCallback = this.resize.bind(this);
|
||||
this.scene.scale.on(Phaser.Scale.Events.RESIZE, this.resizeCallback);
|
||||
}
|
||||
|
||||
private resize() {
|
||||
this.base.setDisplaySize(baseSize / waScaleManager.zoomModifier * window.devicePixelRatio, baseSize / waScaleManager.zoomModifier * window.devicePixelRatio);
|
||||
this.thumb.setDisplaySize(thumbSize / waScaleManager.zoomModifier * window.devicePixelRatio, thumbSize / waScaleManager.zoomModifier * window.devicePixelRatio);
|
||||
this.setRadius(radius / waScaleManager.zoomModifier * window.devicePixelRatio);
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
super.destroy();
|
||||
this.scene.scale.removeListener(Phaser.Scale.Events.RESIZE, this.resizeCallback);
|
||||
}
|
||||
}
|
|
@ -1,17 +1,16 @@
|
|||
import {discussionManager} from "../../WebRtc/DiscussionManager";
|
||||
import {DEPTH_INGAME_TEXT_INDEX} from "../Game/DepthIndexes";
|
||||
|
||||
export const openChatIconName = 'openChatIcon';
|
||||
export class OpenChatIcon extends Phaser.GameObjects.Image {
|
||||
constructor(scene: Phaser.Scene, x: number, y: number) {
|
||||
super(scene, x, y, openChatIconName);
|
||||
super(scene, x, y, openChatIconName, 3);
|
||||
scene.add.existing(this);
|
||||
this.setScrollFactor(0, 0);
|
||||
this.setOrigin(0, 1);
|
||||
this.displayWidth = 30;
|
||||
this.displayHeight = 30;
|
||||
this.setInteractive();
|
||||
this.setVisible(false)
|
||||
this.setDepth(99999);
|
||||
this.setVisible(false);
|
||||
this.setDepth(DEPTH_INGAME_TEXT_INDEX);
|
||||
|
||||
this.on("pointerup", () => discussionManager.showDiscussionPart());
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
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);
|
||||
|
@ -6,6 +8,6 @@ export class PresentationModeIcon extends Phaser.GameObjects.Sprite {
|
|||
this.setOrigin(0, 1);
|
||||
this.setInteractive();
|
||||
this.setVisible(false);
|
||||
this.setDepth(99999);
|
||||
this.setDepth(DEPTH_INGAME_TEXT_INDEX);
|
||||
}
|
||||
}
|
74
front/src/Phaser/Components/RadialMenu.ts
Normal file
74
front/src/Phaser/Components/RadialMenu.ts
Normal file
|
@ -0,0 +1,74 @@
|
|||
import Sprite = Phaser.GameObjects.Sprite;
|
||||
import {DEPTH_UI_INDEX} from "../Game/DepthIndexes";
|
||||
import {waScaleManager} from "../Services/WaScaleManager";
|
||||
|
||||
export interface RadialMenuItem {
|
||||
image: string,
|
||||
name: string,
|
||||
}
|
||||
|
||||
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.scene.add.existing(this);
|
||||
this.initItems();
|
||||
|
||||
this.resize();
|
||||
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))
|
||||
}
|
||||
|
||||
private createRadialElement(item: RadialMenuItem, index: number, itemsNumber: number, menuRadius: number) {
|
||||
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.setInteractive({
|
||||
useHandCursor: true,
|
||||
});
|
||||
image.on('pointerdown', () => this.emit(RadialMenuClickEvent, item));
|
||||
image.on('pointerover', () => {
|
||||
this.scene.tweens.add({
|
||||
targets: image,
|
||||
props: {
|
||||
scale: 2 * scalingFactor,
|
||||
},
|
||||
duration: 500,
|
||||
ease: 'Power3',
|
||||
})
|
||||
});
|
||||
image.on('pointerout', () => {
|
||||
this.scene.tweens.add({
|
||||
targets: image,
|
||||
props: {
|
||||
scale: scalingFactor,
|
||||
},
|
||||
duration: 500,
|
||||
ease: 'Power3',
|
||||
})
|
||||
});
|
||||
const angle = 2 * Math.PI * index / itemsNumber;
|
||||
Phaser.Actions.RotateAroundDistance([image], {x: 0, y: 0}, angle, menuRadius);
|
||||
}
|
||||
|
||||
private resize() {
|
||||
this.setScale(waScaleManager.uiScalingFactor);
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
this.scene.scale.removeListener(Phaser.Scale.Events.RESIZE, this.resizeCallback);
|
||||
super.destroy();
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
import type {IAnalyserNode, IAudioContext, IMediaStreamAudioSourceNode} from 'standardized-audio-context';
|
||||
|
||||
/**
|
||||
* Class to measure the sound volume of a media stream
|
||||
*/
|
||||
|
@ -5,10 +7,10 @@ export class SoundMeter {
|
|||
private instant: number;
|
||||
private clip: number;
|
||||
//private script: ScriptProcessorNode;
|
||||
private analyser: AnalyserNode|undefined;
|
||||
private analyser: IAnalyserNode<IAudioContext>|undefined;
|
||||
private dataArray: Uint8Array|undefined;
|
||||
private context: AudioContext|undefined;
|
||||
private source: MediaStreamAudioSourceNode|undefined;
|
||||
private context: IAudioContext|undefined;
|
||||
private source: IMediaStreamAudioSourceNode<IAudioContext>|undefined;
|
||||
|
||||
constructor() {
|
||||
this.instant = 0.0;
|
||||
|
@ -16,19 +18,21 @@ export class SoundMeter {
|
|||
//this.script = context.createScriptProcessor(2048, 1, 1);
|
||||
}
|
||||
|
||||
private init(context: AudioContext) {
|
||||
if (this.context === undefined) {
|
||||
this.context = context;
|
||||
this.analyser = this.context.createAnalyser();
|
||||
private init(context: IAudioContext) {
|
||||
this.context = context;
|
||||
this.analyser = this.context.createAnalyser();
|
||||
|
||||
this.analyser.fftSize = 2048;
|
||||
const bufferLength = this.analyser.fftSize;
|
||||
this.dataArray = new Uint8Array(bufferLength);
|
||||
}
|
||||
this.analyser.fftSize = 2048;
|
||||
const bufferLength = this.analyser.fftSize;
|
||||
this.dataArray = new Uint8Array(bufferLength);
|
||||
}
|
||||
|
||||
public connectToSource(stream: MediaStream, context: AudioContext): void
|
||||
public connectToSource(stream: MediaStream, context: IAudioContext): void
|
||||
{
|
||||
if (this.source !== undefined) {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
this.init(context);
|
||||
|
||||
this.source = this.context?.createMediaStreamSource(stream);
|
||||
|
@ -83,56 +87,3 @@ export class SoundMeter {
|
|||
|
||||
}
|
||||
|
||||
|
||||
// Meter class that generates a number correlated to audio volume.
|
||||
// The meter class itself displays nothing, but it makes the
|
||||
// instantaneous and time-decaying volumes available for inspection.
|
||||
// It also reports on the fraction of samples that were at or near
|
||||
// the top of the measurement range.
|
||||
/*function SoundMeter(context) {
|
||||
this.context = context;
|
||||
this.instant = 0.0;
|
||||
this.slow = 0.0;
|
||||
this.clip = 0.0;
|
||||
this.script = context.createScriptProcessor(2048, 1, 1);
|
||||
const that = this;
|
||||
this.script.onaudioprocess = function(event) {
|
||||
const input = event.inputBuffer.getChannelData(0);
|
||||
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;
|
||||
}
|
||||
}
|
||||
that.instant = Math.sqrt(sum / input.length);
|
||||
that.slow = 0.95 * that.slow + 0.05 * that.instant;
|
||||
that.clip = clipcount / input.length;
|
||||
};
|
||||
}
|
||||
|
||||
SoundMeter.prototype.connectToSource = function(stream, callback) {
|
||||
console.log('SoundMeter connecting');
|
||||
try {
|
||||
this.mic = this.context.createMediaStreamSource(stream);
|
||||
this.mic.connect(this.script);
|
||||
// necessary to make sample run, but should not be.
|
||||
this.script.connect(this.context.destination);
|
||||
if (typeof callback !== 'undefined') {
|
||||
callback(null);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
if (typeof callback !== 'undefined') {
|
||||
callback(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SoundMeter.prototype.stop = function() {
|
||||
this.mic.disconnect();
|
||||
this.script.disconnect();
|
||||
};
|
||||
*/
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
import Container = Phaser.GameObjects.Container;
|
||||
import {Scene} from "phaser";
|
||||
import GameObject = Phaser.GameObjects.GameObject;
|
||||
import Rectangle = Phaser.GameObjects.Rectangle;
|
||||
|
||||
|
||||
export class SoundMeterSprite extends Container {
|
||||
private rectangles: Rectangle[] = new Array<Rectangle>();
|
||||
private static readonly NB_BARS = 20;
|
||||
|
||||
constructor(scene: Scene, x?: number, y?: number, children?: GameObject[]) {
|
||||
super(scene, x, y, children);
|
||||
|
||||
for (let i = 0; i < SoundMeterSprite.NB_BARS; i++) {
|
||||
const rectangle = new Rectangle(scene, i * 13, 0, 10, 20, (Math.round(255 - i * 255 / SoundMeterSprite.NB_BARS) << 8) + (Math.round(i * 255 / SoundMeterSprite.NB_BARS) << 16));
|
||||
this.add(rectangle);
|
||||
this.rectangles.push(rectangle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A number between 0 and 100
|
||||
*
|
||||
* @param volume
|
||||
*/
|
||||
public setVolume(volume: number): void {
|
||||
|
||||
const normalizedVolume = volume / 100 * SoundMeterSprite.NB_BARS;
|
||||
for (let i = 0; i < SoundMeterSprite.NB_BARS; i++) {
|
||||
if (normalizedVolume < i) {
|
||||
this.rectangles[i].alpha = 0.5;
|
||||
} else {
|
||||
this.rectangles[i].alpha = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public getWidth(): number {
|
||||
return SoundMeterSprite.NB_BARS * 13;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -1,46 +1,68 @@
|
|||
|
||||
const IGNORED_KEYS = new Set([
|
||||
'Esc',
|
||||
'Escape',
|
||||
'Alt',
|
||||
'Meta',
|
||||
'Control',
|
||||
'Ctrl',
|
||||
'Space',
|
||||
'Backspace'
|
||||
])
|
||||
|
||||
export class TextInput extends Phaser.GameObjects.BitmapText {
|
||||
private minUnderLineLength = 4;
|
||||
private underLine: Phaser.GameObjects.Text;
|
||||
private domInput = document.createElement('input');
|
||||
|
||||
constructor(scene: Phaser.Scene, x: number, y: number, maxLength: number, text: string, onChange: (text: string) => void) {
|
||||
constructor(scene: Phaser.Scene, x: number, y: number, maxLength: number, text: string,
|
||||
onChange: (text: string) => void) {
|
||||
super(scene, x, y, 'main_font', text, 32);
|
||||
this.setOrigin(0.5).setCenterAlign()
|
||||
this.setOrigin(0.5).setCenterAlign();
|
||||
this.scene.add.existing(this);
|
||||
|
||||
this.underLine = this.scene.add.text(x, y+1, this.getUnderLineBody(text.length), { fontFamily: 'Arial', fontSize: "32px", color: '#ffffff'})
|
||||
this.underLine.setOrigin(0.5)
|
||||
const style = {fontFamily: 'Arial', fontSize: "32px", color: '#ffffff'};
|
||||
this.underLine = this.scene.add.text(x, y+1, this.getUnderLineBody(text.length), style);
|
||||
this.underLine.setOrigin(0.5);
|
||||
|
||||
this.domInput.maxLength = maxLength;
|
||||
this.domInput.style.opacity = "0";
|
||||
if (text) {
|
||||
this.domInput.value = text;
|
||||
}
|
||||
|
||||
this.scene.input.keyboard.on('keydown', (event: KeyboardEvent) => {
|
||||
if (event.keyCode === 8 && this.text.length > 0) {
|
||||
this.deleteLetter();
|
||||
} else if ((event.keyCode === 32 || (event.keyCode >= 48 && event.keyCode <= 90)) && this.text.length < maxLength) {
|
||||
this.addLetter(event.key);
|
||||
this.domInput.addEventListener('keydown', event => {
|
||||
if (IGNORED_KEYS.has(event.key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!/[a-zA-Z0-9:.!&?()+-]/.exec(event.key)) {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
this.domInput.addEventListener('input', (event) => {
|
||||
if (event.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
this.text = this.domInput.value;
|
||||
this.underLine.text = this.getUnderLineBody(this.text.length);
|
||||
onChange(this.text);
|
||||
});
|
||||
|
||||
document.body.append(this.domInput);
|
||||
this.focus();
|
||||
}
|
||||
|
||||
|
||||
private getUnderLineBody(textLength:number): string {
|
||||
if (textLength < this.minUnderLineLength) textLength = this.minUnderLineLength;
|
||||
let text = '_______';
|
||||
for (let i = this.minUnderLineLength; i < textLength; i++) {
|
||||
text += '__'
|
||||
text += '__';
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
private deleteLetter() {
|
||||
this.text = this.text.substr(0, this.text.length - 1);
|
||||
}
|
||||
|
||||
|
||||
private addLetter(letter: string) {
|
||||
this.text += letter;
|
||||
}
|
||||
|
||||
getText(): string {
|
||||
return this.text;
|
||||
}
|
||||
|
@ -56,4 +78,13 @@ export class TextInput extends Phaser.GameObjects.BitmapText {
|
|||
this.underLine.y = y+1;
|
||||
return this;
|
||||
}
|
||||
|
||||
focus() {
|
||||
this.domInput.focus();
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
super.destroy();
|
||||
this.domInput.remove();
|
||||
}
|
||||
}
|
||||
|
|
51
front/src/Phaser/Components/TextUtils.ts
Normal file
51
front/src/Phaser/Components/TextUtils.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
import type {ITiledMapObject} from "../Map/ITiledMap";
|
||||
import type {GameScene} from "../Game/GameScene";
|
||||
|
||||
export class TextUtils {
|
||||
public static createTextFromITiledMapObject(scene: GameScene, object: ITiledMapObject): void {
|
||||
if (object.text === undefined) {
|
||||
throw new Error('This object has not textual representation.');
|
||||
}
|
||||
const options: {
|
||||
fontStyle?: string,
|
||||
fontSize?: string,
|
||||
fontFamily?: string,
|
||||
color?: string,
|
||||
align?: string,
|
||||
wordWrap?: {
|
||||
width: number,
|
||||
useAdvancedWrap?: boolean
|
||||
}
|
||||
} = {};
|
||||
if (object.text.italic) {
|
||||
options.fontStyle = 'italic';
|
||||
}
|
||||
// Note: there is no support for "strikeout" and "underline"
|
||||
let fontSize: number = 16;
|
||||
if (object.text.pixelsize) {
|
||||
fontSize = object.text.pixelsize;
|
||||
}
|
||||
options.fontSize = fontSize + 'px';
|
||||
if (object.text.fontfamily) {
|
||||
options.fontFamily = '"'+object.text.fontfamily+'"';
|
||||
}
|
||||
let color = '#000000';
|
||||
if (object.text.color !== undefined) {
|
||||
color = object.text.color;
|
||||
}
|
||||
options.color = color;
|
||||
if (object.text.wrap === true) {
|
||||
options.wordWrap = {
|
||||
width: object.width,
|
||||
//useAdvancedWrap: true
|
||||
}
|
||||
}
|
||||
if (object.text.halign !== undefined) {
|
||||
options.align = object.text.halign;
|
||||
}
|
||||
|
||||
console.warn(options);
|
||||
const textElem = scene.add.text(object.x, object.y, object.text.text, options);
|
||||
textElem.setAngle(object.rotation);
|
||||
}
|
||||
}
|
14
front/src/Phaser/Components/WarningContainer.ts
Normal file
14
front/src/Phaser/Components/WarningContainer.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
|
||||
export const warningContainerKey = 'warningContainer';
|
||||
export const warningContainerHtml = 'resources/html/warningContainer.html';
|
||||
|
||||
export class WarningContainer extends Phaser.GameObjects.DOMElement {
|
||||
|
||||
constructor(scene: Phaser.Scene) {
|
||||
super(scene, 100, 0);
|
||||
this.setOrigin(0, 0);
|
||||
this.createFromCache(warningContainerKey);
|
||||
this.scene.add.existing(this);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,83 +1,74 @@
|
|||
import {PlayerAnimationNames} from "../Player/Animation";
|
||||
import {PlayerAnimationDirections, PlayerAnimationTypes} from "../Player/Animation";
|
||||
import {SpeechBubble} from "./SpeechBubble";
|
||||
import BitmapText = Phaser.GameObjects.BitmapText;
|
||||
import Text = Phaser.GameObjects.Text;
|
||||
import Container = Phaser.GameObjects.Container;
|
||||
import Sprite = Phaser.GameObjects.Sprite;
|
||||
import {TextureError} from "../../Exception/TextureError";
|
||||
import {Companion} from "../Companion/Companion";
|
||||
import type {GameScene} from "../Game/GameScene";
|
||||
import {DEPTH_INGAME_TEXT_INDEX} from "../Game/DepthIndexes";
|
||||
import {waScaleManager} from "../Services/WaScaleManager";
|
||||
|
||||
export interface PlayerResourceDescriptionInterface {
|
||||
name: string,
|
||||
img: string
|
||||
}
|
||||
|
||||
export const PLAYER_RESOURCES: Array<PlayerResourceDescriptionInterface> = [
|
||||
{name: "male1", img: "resources/characters/pipoya/Male 01-1.png" /*, x: 32, y: 32*/},
|
||||
{name: "male2", img: "resources/characters/pipoya/Male 02-2.png"/*, x: 64, y: 32*/},
|
||||
{name: "male3", img: "resources/characters/pipoya/Male 03-4.png"/*, x: 96, y: 32*/},
|
||||
{name: "male4", img: "resources/characters/pipoya/Male 09-1.png"/*, x: 128, y: 32*/},
|
||||
|
||||
{name: "male5", img: "resources/characters/pipoya/Male 10-3.png"/*, x: 32, y: 64*/},
|
||||
{name: "male6", img: "resources/characters/pipoya/Male 17-2.png"/*, x: 64, y: 64*/},
|
||||
{name: "male7", img: "resources/characters/pipoya/Male 18-1.png"/*, x: 96, y: 64*/},
|
||||
{name: "male8", img: "resources/characters/pipoya/Male 16-4.png"/*, x: 128, y: 64*/},
|
||||
|
||||
{name: "Female1", img: "resources/characters/pipoya/Female 01-1.png"/*, x: 32, y: 96*/},
|
||||
{name: "Female2", img: "resources/characters/pipoya/Female 02-2.png"/*, x: 64, y: 96*/},
|
||||
{name: "Female3", img: "resources/characters/pipoya/Female 03-4.png"/*, x: 96, y: 96*/},
|
||||
{name: "Female4", img: "resources/characters/pipoya/Female 09-1.png"/*, x: 128, y: 96*/},
|
||||
|
||||
{name: "Female5", img: "resources/characters/pipoya/Female 10-3.png"/*, x: 32, y: 128*/},
|
||||
{name: "Female6", img: "resources/characters/pipoya/Female 17-2.png"/*, x: 64, y: 128*/},
|
||||
{name: "Female7", img: "resources/characters/pipoya/Female 18-1.png"/*, x: 96, y: 128*/},
|
||||
{name: "Female8", img: "resources/characters/pipoya/Female 16-4.png"/*, x: 128, y: 128*/}
|
||||
];
|
||||
const playerNameY = - 25;
|
||||
|
||||
interface AnimationData {
|
||||
key: string;
|
||||
frameRate: number;
|
||||
repeat: number;
|
||||
frameModel: string; //todo use an enum
|
||||
frameStart: number;
|
||||
frameEnd: number;
|
||||
frames : number[]
|
||||
}
|
||||
|
||||
const interactiveRadius = 35;
|
||||
|
||||
export abstract class Character extends Container {
|
||||
private bubble: SpeechBubble|null = null;
|
||||
private readonly playerName: BitmapText;
|
||||
private readonly playerName: Text;
|
||||
public PlayerValue: string;
|
||||
public sprites: Map<string, Sprite>;
|
||||
private lastDirection: string = PlayerAnimationNames.WalkDown;
|
||||
private lastDirection: PlayerAnimationDirections = PlayerAnimationDirections.Down;
|
||||
//private teleportation: Sprite;
|
||||
private invisible: boolean;
|
||||
public companion?: Companion;
|
||||
private emote: Phaser.GameObjects.Sprite | null = null;
|
||||
private emoteTween: Phaser.Tweens.Tween|null = null;
|
||||
|
||||
constructor(scene: Phaser.Scene,
|
||||
constructor(scene: GameScene,
|
||||
x: number,
|
||||
y: number,
|
||||
textures: string[],
|
||||
texturesPromise: Promise<string[]>,
|
||||
name: string,
|
||||
direction: string,
|
||||
direction: PlayerAnimationDirections,
|
||||
moving: boolean,
|
||||
frame?: string | number
|
||||
frame: string | number,
|
||||
isClickable: boolean,
|
||||
companion: string|null,
|
||||
companionTexturePromise?: Promise<string>
|
||||
) {
|
||||
super(scene, x, y/*, texture, frame*/);
|
||||
this.PlayerValue = name;
|
||||
this.invisible = true
|
||||
|
||||
this.sprites = new Map<string, Sprite>();
|
||||
|
||||
this.addTextures(textures, frame);
|
||||
//textures are inside a Promise in case they need to be lazyloaded before use.
|
||||
texturesPromise.then((textures) => {
|
||||
this.addTextures(textures, frame);
|
||||
this.invisible = false
|
||||
})
|
||||
|
||||
/*this.teleportation = new Sprite(scene, -20, -10, 'teleportation', 3);
|
||||
this.teleportation.setInteractive();
|
||||
this.teleportation.visible = false;
|
||||
this.teleportation.on('pointerup', () => {
|
||||
this.report.visible = false;
|
||||
this.teleportation.visible = false;
|
||||
});
|
||||
this.add(this.teleportation);*/
|
||||
|
||||
this.playerName = new BitmapText(scene, 0, - 25, 'main_font', name, 7);
|
||||
this.playerName.setOrigin(0.5).setCenterAlign().setDepth(99999);
|
||||
this.playerName = new Text(scene, 0, playerNameY, name, {fontFamily: '"Press Start 2P"', fontSize: '8px', strokeThickness: 2, stroke: "gray"});
|
||||
this.playerName.setOrigin(0.5).setDepth(DEPTH_INGAME_TEXT_INDEX);
|
||||
this.add(this.playerName);
|
||||
|
||||
if (isClickable) {
|
||||
this.setInteractive({
|
||||
hitArea: new Phaser.Geom.Circle(0, 0, interactiveRadius),
|
||||
hitAreaCallback: Phaser.Geom.Circle.Contains, //eslint-disable-line @typescript-eslint/unbound-method
|
||||
useHandCursor: true,
|
||||
});
|
||||
}
|
||||
|
||||
scene.add.existing(this);
|
||||
|
||||
this.scene.physics.world.enableBody(this);
|
||||
|
@ -89,20 +80,29 @@ export abstract class Character extends Container {
|
|||
this.setDepth(-1);
|
||||
|
||||
this.playAnimation(direction, moving);
|
||||
|
||||
if (typeof companion === 'string') {
|
||||
this.addCompanion(companion, companionTexturePromise);
|
||||
}
|
||||
}
|
||||
|
||||
public addCompanion(name: string, texturePromise?: Promise<string>): void {
|
||||
if (typeof texturePromise !== 'undefined') {
|
||||
this.companion = new Companion(this.scene, this.x, this.y, name, texturePromise);
|
||||
}
|
||||
}
|
||||
|
||||
public addTextures(textures: string[], frame?: string | number): void {
|
||||
for (const texture of textures) {
|
||||
if(!this.scene.textures.exists(texture)){
|
||||
if(this.scene && !this.scene.textures.exists(texture)){
|
||||
throw new TextureError('texture not found');
|
||||
}
|
||||
const sprite = new Sprite(this.scene, 0, 0, texture, frame);
|
||||
sprite.setInteractive({useHandCursor: true});
|
||||
this.add(sprite);
|
||||
this.getPlayerAnimations(texture).forEach(d => {
|
||||
this.scene.anims.create({
|
||||
key: d.key,
|
||||
frames: this.scene.anims.generateFrameNumbers(d.frameModel, {start: d.frameStart, end: d.frameEnd}),
|
||||
frames: this.scene.anims.generateFrameNumbers(d.frameModel, {frames: d.frames}),
|
||||
frameRate: d.frameRate,
|
||||
repeat: d.repeat
|
||||
});
|
||||
|
@ -117,47 +117,67 @@ export abstract class Character extends Container {
|
|||
|
||||
private getPlayerAnimations(name: string): AnimationData[] {
|
||||
return [{
|
||||
key: `${name}-${PlayerAnimationNames.WalkDown}`,
|
||||
key: `${name}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Walk}`,
|
||||
frameModel: name,
|
||||
frameStart: 0,
|
||||
frameEnd: 2,
|
||||
frames: [0, 1, 2, 1],
|
||||
frameRate: 10,
|
||||
repeat: -1
|
||||
}, {
|
||||
key: `${name}-${PlayerAnimationNames.WalkLeft}`,
|
||||
key: `${name}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Walk}`,
|
||||
frameModel: name,
|
||||
frameStart: 3,
|
||||
frameEnd: 5,
|
||||
frames: [3, 4, 5, 4],
|
||||
frameRate: 10,
|
||||
repeat: -1
|
||||
}, {
|
||||
key: `${name}-${PlayerAnimationNames.WalkRight}`,
|
||||
key: `${name}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Walk}`,
|
||||
frameModel: name,
|
||||
frameStart: 6,
|
||||
frameEnd: 8,
|
||||
frames: [6, 7, 8, 7],
|
||||
frameRate: 10,
|
||||
repeat: -1
|
||||
}, {
|
||||
key: `${name}-${PlayerAnimationNames.WalkUp}`,
|
||||
key: `${name}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Walk}`,
|
||||
frameModel: name,
|
||||
frameStart: 9,
|
||||
frameEnd: 11,
|
||||
frames: [9, 10, 11, 10],
|
||||
frameRate: 10,
|
||||
repeat: -1
|
||||
},{
|
||||
key: `${name}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Idle}`,
|
||||
frameModel: name,
|
||||
frames: [1],
|
||||
frameRate: 10,
|
||||
repeat: 1
|
||||
}, {
|
||||
key: `${name}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Idle}`,
|
||||
frameModel: name,
|
||||
frames: [4],
|
||||
frameRate: 10,
|
||||
repeat: 1
|
||||
}, {
|
||||
key: `${name}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Idle}`,
|
||||
frameModel: name,
|
||||
frames: [7],
|
||||
frameRate: 10,
|
||||
repeat: 1
|
||||
}, {
|
||||
key: `${name}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Idle}`,
|
||||
frameModel: name,
|
||||
frames: [10],
|
||||
frameRate: 10,
|
||||
repeat: 1
|
||||
}];
|
||||
}
|
||||
|
||||
protected playAnimation(direction : string, moving: boolean): void {
|
||||
protected playAnimation(direction : PlayerAnimationDirections, moving: boolean): void {
|
||||
if (this.invisible) return;
|
||||
for (const [texture, sprite] of this.sprites.entries()) {
|
||||
if (!sprite.anims) {
|
||||
console.error('ANIMS IS NOT DEFINED!!!');
|
||||
return;
|
||||
}
|
||||
if (moving && (!sprite.anims.currentAnim || sprite.anims.currentAnim.key !== direction)) {
|
||||
sprite.play(texture+'-'+direction, true);
|
||||
sprite.play(texture+'-'+direction+'-'+PlayerAnimationTypes.Walk, true);
|
||||
} else if (!moving) {
|
||||
sprite.anims.play(texture + '-' + direction, true);
|
||||
sprite.anims.stop();
|
||||
sprite.anims.play(texture + '-' + direction + '-'+PlayerAnimationTypes.Idle, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -177,21 +197,24 @@ export abstract class Character extends Container {
|
|||
|
||||
// up or down animations are prioritized over left and right
|
||||
if (body.velocity.y < 0) { //moving up
|
||||
this.lastDirection = PlayerAnimationNames.WalkUp;
|
||||
this.playAnimation(PlayerAnimationNames.WalkUp, true);
|
||||
this.lastDirection = PlayerAnimationDirections.Up;
|
||||
this.playAnimation(PlayerAnimationDirections.Up, true);
|
||||
} else if (body.velocity.y > 0) { //moving down
|
||||
this.lastDirection = PlayerAnimationNames.WalkDown;
|
||||
this.playAnimation(PlayerAnimationNames.WalkDown, true);
|
||||
this.lastDirection = PlayerAnimationDirections.Down;
|
||||
this.playAnimation(PlayerAnimationDirections.Down, true);
|
||||
} else if (body.velocity.x > 0) { //moving right
|
||||
this.lastDirection = PlayerAnimationNames.WalkRight;
|
||||
this.playAnimation(PlayerAnimationNames.WalkRight, true);
|
||||
this.lastDirection = PlayerAnimationDirections.Right;
|
||||
this.playAnimation(PlayerAnimationDirections.Right, true);
|
||||
} else if (body.velocity.x < 0) { //moving left
|
||||
this.lastDirection = PlayerAnimationNames.WalkLeft;
|
||||
this.playAnimation(PlayerAnimationNames.WalkLeft, true);
|
||||
this.lastDirection = PlayerAnimationDirections.Left;
|
||||
this.playAnimation(PlayerAnimationDirections.Left, true);
|
||||
}
|
||||
|
||||
//update depth user
|
||||
this.setDepth(this.y);
|
||||
|
||||
if (this.companion) {
|
||||
this.companion.setTarget(this.x, this.y, this.lastDirection);
|
||||
}
|
||||
}
|
||||
|
||||
stop(){
|
||||
|
@ -216,7 +239,84 @@ export abstract class Character extends Container {
|
|||
this.scene.sys.updateList.remove(sprite);
|
||||
}
|
||||
}
|
||||
this.list.forEach(objectContaining => objectContaining.destroy())
|
||||
super.destroy();
|
||||
this.playerName.destroy();
|
||||
}
|
||||
|
||||
playEmote(emoteKey: string) {
|
||||
this.cancelPreviousEmote();
|
||||
|
||||
const scalingFactor = waScaleManager.uiScalingFactor * 0.05;
|
||||
const emoteY = -30 - scalingFactor * 10;
|
||||
|
||||
this.playerName.setVisible(false);
|
||||
this.emote = new Sprite(this.scene, 0, 0, emoteKey);
|
||||
this.emote.setAlpha(0);
|
||||
this.emote.setScale(0.1 * scalingFactor);
|
||||
this.add(this.emote);
|
||||
this.scene.sys.updateList.add(this.emote);
|
||||
|
||||
this.createStartTransition(scalingFactor, emoteY);
|
||||
}
|
||||
|
||||
private createStartTransition(scalingFactor: number, emoteY: number) {
|
||||
this.emoteTween = this.scene?.tweens.add({
|
||||
targets: this.emote,
|
||||
props: {
|
||||
scale: scalingFactor,
|
||||
alpha: 1,
|
||||
y: emoteY,
|
||||
},
|
||||
ease: 'Power2',
|
||||
duration: 500,
|
||||
onComplete: () => {
|
||||
this.startPulseTransition(emoteY, scalingFactor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private startPulseTransition(emoteY: number, scalingFactor: number) {
|
||||
this.emoteTween = this.scene?.tweens.add({
|
||||
targets: this.emote,
|
||||
props: {
|
||||
y: emoteY * 1.3,
|
||||
scale: scalingFactor * 1.1
|
||||
},
|
||||
duration: 250,
|
||||
yoyo: true,
|
||||
repeat: 1,
|
||||
completeDelay: 200,
|
||||
onComplete: () => {
|
||||
this.startExitTransition(emoteY);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private startExitTransition(emoteY: number) {
|
||||
this.emoteTween = this.scene?.tweens.add({
|
||||
targets: this.emote,
|
||||
props: {
|
||||
alpha: 0,
|
||||
y: 2 * emoteY,
|
||||
},
|
||||
ease: 'Power2',
|
||||
duration: 500,
|
||||
onComplete: () => {
|
||||
this.destroyEmote();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cancelPreviousEmote() {
|
||||
if (!this.emote) return;
|
||||
|
||||
this.emoteTween?.remove();
|
||||
this.destroyEmote()
|
||||
}
|
||||
|
||||
private destroyEmote() {
|
||||
this.emote?.destroy();
|
||||
this.emote = null;
|
||||
this.playerName.setVisible(true);
|
||||
}
|
||||
}
|
||||
|
|
20
front/src/Phaser/Entity/CustomizedCharacter.ts
Normal file
20
front/src/Phaser/Entity/CustomizedCharacter.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import Container = Phaser.GameObjects.Container;
|
||||
import type {Scene} from "phaser";
|
||||
import Sprite = Phaser.GameObjects.Sprite;
|
||||
|
||||
/**
|
||||
* A sprite of a customized character (used in the Customize Scene only)
|
||||
*/
|
||||
export class CustomizedCharacter extends Container {
|
||||
public constructor(scene: Scene, x: number, y: number, layers: string[]) {
|
||||
super(scene, x, y);
|
||||
this.updateSprites(layers);
|
||||
}
|
||||
|
||||
public updateSprites(layers: string[]): void {
|
||||
this.removeAll(true);
|
||||
for (const layer of layers) {
|
||||
this.add(new Sprite(this.scene, 0, 0, layer));
|
||||
}
|
||||
}
|
||||
}
|
344
front/src/Phaser/Entity/PlayerTextures.ts
Normal file
344
front/src/Phaser/Entity/PlayerTextures.ts
Normal file
|
@ -0,0 +1,344 @@
|
|||
//The list of all the player textures, both the default models and the partial textures used for customization
|
||||
|
||||
export interface BodyResourceDescriptionListInterface {
|
||||
[key: string]: BodyResourceDescriptionInterface
|
||||
}
|
||||
|
||||
export interface BodyResourceDescriptionInterface {
|
||||
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"},
|
||||
|
||||
"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"}
|
||||
};
|
||||
|
||||
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"}
|
||||
|
||||
};
|
||||
|
||||
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"}
|
||||
};
|
||||
|
||||
|
||||
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"}
|
||||
};
|
||||
|
||||
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"}
|
||||
};
|
||||
|
||||
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"}
|
||||
};
|
||||
|
||||
export const LAYERS: BodyResourceDescriptionListInterface[] = [
|
||||
COLOR_RESOURCES,
|
||||
EYES_RESOURCES,
|
||||
HAIR_RESOURCES,
|
||||
CLOTHES_RESOURCES,
|
||||
HATS_RESOURCES,
|
||||
ACCESSORIES_RESOURCES
|
||||
];
|
||||
|
||||
export const OBJECTS: BodyResourceDescriptionInterface[] = [
|
||||
{name:'teleportation', img:'resources/objects/teleportation.png'},
|
||||
];
|
90
front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts
Normal file
90
front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts
Normal file
|
@ -0,0 +1,90 @@
|
|||
import LoaderPlugin = Phaser.Loader.LoaderPlugin;
|
||||
import type {CharacterTexture} from "../../Connexion/LocalUser";
|
||||
import {BodyResourceDescriptionInterface, LAYERS, PLAYER_RESOURCES} from "./PlayerTextures";
|
||||
|
||||
export interface FrameConfig {
|
||||
frameWidth: number,
|
||||
frameHeight: number,
|
||||
}
|
||||
|
||||
export const loadAllLayers = (load: LoaderPlugin): BodyResourceDescriptionInterface[][] => {
|
||||
const returnArray:BodyResourceDescriptionInterface[][] = [];
|
||||
LAYERS.forEach(layer => {
|
||||
const layerArray:BodyResourceDescriptionInterface[] = [];
|
||||
Object.values(layer).forEach((textureDescriptor) => {
|
||||
layerArray.push(textureDescriptor);
|
||||
load.spritesheet(textureDescriptor.name,textureDescriptor.img,{frameWidth: 32, frameHeight: 32});
|
||||
})
|
||||
returnArray.push(layerArray)
|
||||
});
|
||||
return returnArray;
|
||||
}
|
||||
export const loadAllDefaultModels = (load: LoaderPlugin): BodyResourceDescriptionInterface[] => {
|
||||
const returnArray = Object.values(PLAYER_RESOURCES);
|
||||
returnArray.forEach((playerResource: BodyResourceDescriptionInterface) => {
|
||||
load.spritesheet(playerResource.name, playerResource.img, {frameWidth: 32, frameHeight: 32});
|
||||
});
|
||||
return returnArray;
|
||||
}
|
||||
|
||||
export const loadCustomTexture = (loaderPlugin: LoaderPlugin, texture: CharacterTexture) : Promise<BodyResourceDescriptionInterface> => {
|
||||
const name = 'customCharacterTexture'+texture.id;
|
||||
const playerResourceDescriptor: BodyResourceDescriptionInterface = {name, img: texture.url, level: texture.level}
|
||||
return createLoadingPromise(loaderPlugin, playerResourceDescriptor, {
|
||||
frameWidth: 32,
|
||||
frameHeight: 32
|
||||
});
|
||||
}
|
||||
|
||||
export const lazyLoadPlayerCharacterTextures = (loadPlugin: LoaderPlugin, texturekeys:Array<string|BodyResourceDescriptionInterface>): Promise<string[]> => {
|
||||
const promisesList:Promise<unknown>[] = [];
|
||||
texturekeys.forEach((textureKey: string|BodyResourceDescriptionInterface) => {
|
||||
try {
|
||||
//TODO refactor
|
||||
const playerResourceDescriptor = getRessourceDescriptor(textureKey);
|
||||
if (playerResourceDescriptor && !loadPlugin.textureManager.exists(playerResourceDescriptor.name)) {
|
||||
promisesList.push(createLoadingPromise(loadPlugin, playerResourceDescriptor, {
|
||||
frameWidth: 32,
|
||||
frameHeight: 32
|
||||
}));
|
||||
}
|
||||
}catch (err){
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
let returnPromise:Promise<Array<string|BodyResourceDescriptionInterface>>;
|
||||
if (promisesList.length > 0) {
|
||||
loadPlugin.start();
|
||||
returnPromise = Promise.all(promisesList).then(() => texturekeys);
|
||||
} else {
|
||||
returnPromise = Promise.resolve(texturekeys);
|
||||
}
|
||||
return returnPromise.then((keys) => keys.map((key) => {
|
||||
return typeof key !== 'string' ? key.name : key;
|
||||
}))
|
||||
}
|
||||
|
||||
export const getRessourceDescriptor = (textureKey: string|BodyResourceDescriptionInterface): BodyResourceDescriptionInterface => {
|
||||
if (typeof textureKey !== 'string' && textureKey.img) {
|
||||
return textureKey;
|
||||
}
|
||||
const textureName:string = typeof textureKey === 'string' ? textureKey : textureKey.name;
|
||||
const playerResource = PLAYER_RESOURCES[textureName];
|
||||
if (playerResource !== undefined) return playerResource;
|
||||
|
||||
for (let i=0; i<LAYERS.length;i++) {
|
||||
const playerResource = LAYERS[i][textureName];
|
||||
if (playerResource !== undefined) return playerResource;
|
||||
}
|
||||
throw 'Could not find a data for texture '+textureName;
|
||||
}
|
||||
|
||||
export const createLoadingPromise = (loadPlugin: LoaderPlugin, playerResourceDescriptor: BodyResourceDescriptionInterface, frameConfig: FrameConfig) => {
|
||||
return new Promise<BodyResourceDescriptionInterface>((res) => {
|
||||
if (loadPlugin.textureManager.exists(playerResourceDescriptor.name)) {
|
||||
return res(playerResourceDescriptor);
|
||||
}
|
||||
loadPlugin.spritesheet(playerResourceDescriptor.name, playerResourceDescriptor.img, frameConfig);
|
||||
loadPlugin.once('filecomplete-spritesheet-' + playerResourceDescriptor.name, () => res(playerResourceDescriptor));
|
||||
});
|
||||
}
|
|
@ -1,13 +1,16 @@
|
|||
import {GameScene} from "../Game/GameScene";
|
||||
import {PointInterface} from "../../Connexion/ConnexionModels";
|
||||
import type {GameScene} from "../Game/GameScene";
|
||||
import type {PointInterface} from "../../Connexion/ConnexionModels";
|
||||
import {Character} from "../Entity/Character";
|
||||
import {Sprite} from "./Sprite";
|
||||
import type {PlayerAnimationDirections} from "../Player/Animation";
|
||||
import {requestVisitCardsStore} from "../../Stores/GameStore";
|
||||
|
||||
|
||||
/**
|
||||
* Class representing the sprite of a remote player (a player that plays on another computer)
|
||||
*/
|
||||
export class RemotePlayer extends Character {
|
||||
userId: number;
|
||||
private visitCardUrl: string|null;
|
||||
|
||||
constructor(
|
||||
userId: number,
|
||||
|
@ -15,21 +18,33 @@ export class RemotePlayer extends Character {
|
|||
x: number,
|
||||
y: number,
|
||||
name: string,
|
||||
PlayerTextures: string[],
|
||||
direction: string,
|
||||
moving: boolean
|
||||
texturesPromise: Promise<string[]>,
|
||||
direction: PlayerAnimationDirections,
|
||||
moving: boolean,
|
||||
visitCardUrl: string|null,
|
||||
companion: string|null,
|
||||
companionTexturePromise?: Promise<string>
|
||||
) {
|
||||
super(Scene, x, y, PlayerTextures, name, direction, moving, 1);
|
||||
|
||||
super(Scene, x, y, texturesPromise, name, direction, moving, 1, !!visitCardUrl, companion, companionTexturePromise);
|
||||
|
||||
//set data
|
||||
this.userId = userId;
|
||||
this.visitCardUrl = visitCardUrl;
|
||||
|
||||
this.on('pointerdown', () => {
|
||||
requestVisitCardsStore.set(this.visitCardUrl);
|
||||
})
|
||||
}
|
||||
|
||||
updatePosition(position: PointInterface): void {
|
||||
this.playAnimation(position.direction, position.moving);
|
||||
this.playAnimation(position.direction as PlayerAnimationDirections, position.moving);
|
||||
this.setX(position.x);
|
||||
this.setY(position.y);
|
||||
|
||||
|
||||
this.setDepth(position.y); //this is to make sure the perspective (player models closer the bottom of the screen will appear in front of models nearer the top of the screen).
|
||||
|
||||
if (this.companion) {
|
||||
this.companion.setTarget(position.x, position.y, position.direction as PlayerAnimationDirections);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import Scene = Phaser.Scene;
|
||||
import {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;
|
||||
|
@ -14,7 +14,7 @@ export class SpeechBubble {
|
|||
const bubbleWidth = bubblePadding * 2 + text.length * 10;
|
||||
const arrowHeight = bubbleHeight / 4;
|
||||
|
||||
this.bubble = scene.add.graphics({ x: player.x + 16, y: player.y - 80 });
|
||||
this.bubble = scene.add.graphics({ x: 16, y: -80 });
|
||||
player.add(this.bubble);
|
||||
|
||||
// Bubble shadow
|
||||
|
|
|
@ -1,355 +0,0 @@
|
|||
import LoaderPlugin = Phaser.Loader.LoaderPlugin;
|
||||
import {PLAYER_RESOURCES, PlayerResourceDescriptionInterface} from "./Character";
|
||||
import {CharacterTexture} from "../../Connexion/LocalUser";
|
||||
|
||||
export interface BodyResourceDescriptionInterface {
|
||||
name: string,
|
||||
img: string
|
||||
}
|
||||
|
||||
export const COLOR_RESOURCES: Array<BodyResourceDescriptionInterface> = [
|
||||
{name:"color_1", img: "resources/customisation/character_color/character_color0.png"},
|
||||
{name:"color_2", img: "resources/customisation/character_color/character_color1.png"},
|
||||
{name:"color_3", img: "resources/customisation/character_color/character_color2.png"},
|
||||
{name:"color_4", img: "resources/customisation/character_color/character_color3.png"},
|
||||
{name:"color_5", img: "resources/customisation/character_color/character_color4.png"},
|
||||
{name:"color_6", img: "resources/customisation/character_color/character_color5.png"},
|
||||
{name:"color_7", img: "resources/customisation/character_color/character_color6.png"},
|
||||
{name:"color_8", img: "resources/customisation/character_color/character_color7.png"},
|
||||
{name:"color_9", img: "resources/customisation/character_color/character_color8.png"},
|
||||
{name:"color_10",img: "resources/customisation/character_color/character_color9.png"},
|
||||
{name:"color_11",img: "resources/customisation/character_color/character_color10.png"},
|
||||
{name:"color_12",img: "resources/customisation/character_color/character_color11.png"},
|
||||
{name:"color_13",img: "resources/customisation/character_color/character_color12.png"},
|
||||
{name:"color_14",img: "resources/customisation/character_color/character_color13.png"},
|
||||
{name:"color_15",img: "resources/customisation/character_color/character_color14.png"},
|
||||
{name:"color_16",img: "resources/customisation/character_color/character_color15.png"},
|
||||
{name:"color_17",img: "resources/customisation/character_color/character_color16.png"},
|
||||
{name:"color_18",img: "resources/customisation/character_color/character_color17.png"},
|
||||
{name:"color_19",img: "resources/customisation/character_color/character_color18.png"},
|
||||
{name:"color_20",img: "resources/customisation/character_color/character_color19.png"},
|
||||
{name:"color_21",img: "resources/customisation/character_color/character_color20.png"},
|
||||
{name:"color_22",img: "resources/customisation/character_color/character_color21.png"},
|
||||
{name:"color_23",img: "resources/customisation/character_color/character_color22.png"},
|
||||
{name:"color_24",img: "resources/customisation/character_color/character_color23.png"},
|
||||
{name:"color_25",img: "resources/customisation/character_color/character_color24.png"},
|
||||
{name:"color_26",img: "resources/customisation/character_color/character_color25.png"},
|
||||
{name:"color_27",img: "resources/customisation/character_color/character_color26.png"},
|
||||
{name:"color_28",img: "resources/customisation/character_color/character_color27.png"},
|
||||
{name:"color_29",img: "resources/customisation/character_color/character_color28.png"},
|
||||
{name:"color_30",img: "resources/customisation/character_color/character_color29.png"},
|
||||
{name:"color_31",img: "resources/customisation/character_color/character_color30.png"},
|
||||
{name:"color_32",img: "resources/customisation/character_color/character_color31.png"},
|
||||
{name:"color_33",img: "resources/customisation/character_color/character_color32.png"}
|
||||
];
|
||||
|
||||
export const EYES_RESOURCES: Array<BodyResourceDescriptionInterface> = [
|
||||
{name: "eyes_1", img: "resources/customisation/character_eyes/character_eyes1.png"},
|
||||
{name: "eyes_2", img: "resources/customisation/character_eyes/character_eyes2.png"},
|
||||
{name: "eyes_3", img: "resources/customisation/character_eyes/character_eyes3.png"},
|
||||
{name: "eyes_4", img: "resources/customisation/character_eyes/character_eyes4.png"},
|
||||
{name: "eyes_5", img: "resources/customisation/character_eyes/character_eyes5.png"},
|
||||
{name: "eyes_6", img: "resources/customisation/character_eyes/character_eyes6.png"},
|
||||
{name: "eyes_7", img: "resources/customisation/character_eyes/character_eyes7.png"},
|
||||
{name: "eyes_8", img: "resources/customisation/character_eyes/character_eyes8.png"},
|
||||
{name: "eyes_9", img: "resources/customisation/character_eyes/character_eyes9.png"},
|
||||
{name: "eyes_10", img: "resources/customisation/character_eyes/character_eyes10.png"},
|
||||
{name: "eyes_11", img: "resources/customisation/character_eyes/character_eyes11.png"},
|
||||
{name: "eyes_12", img: "resources/customisation/character_eyes/character_eyes12.png"},
|
||||
{name: "eyes_13", img: "resources/customisation/character_eyes/character_eyes13.png"},
|
||||
{name: "eyes_14", img: "resources/customisation/character_eyes/character_eyes14.png"},
|
||||
{name: "eyes_15", img: "resources/customisation/character_eyes/character_eyes15.png"},
|
||||
{name: "eyes_16", img: "resources/customisation/character_eyes/character_eyes16.png"},
|
||||
{name: "eyes_17", img: "resources/customisation/character_eyes/character_eyes17.png"},
|
||||
{name: "eyes_18", img: "resources/customisation/character_eyes/character_eyes18.png"},
|
||||
{name: "eyes_19", img: "resources/customisation/character_eyes/character_eyes19.png"},
|
||||
{name: "eyes_20", img: "resources/customisation/character_eyes/character_eyes20.png"},
|
||||
{name: "eyes_21", img: "resources/customisation/character_eyes/character_eyes21.png"},
|
||||
{name: "eyes_22", img: "resources/customisation/character_eyes/character_eyes22.png"},
|
||||
{name: "eyes_23", img: "resources/customisation/character_eyes/character_eyes23.png"},
|
||||
{name: "eyes_24", img: "resources/customisation/character_eyes/character_eyes24.png"},
|
||||
{name: "eyes_25", img: "resources/customisation/character_eyes/character_eyes25.png"},
|
||||
{name: "eyes_26", img: "resources/customisation/character_eyes/character_eyes26.png"},
|
||||
{name: "eyes_27", img: "resources/customisation/character_eyes/character_eyes27.png"},
|
||||
{name: "eyes_28", img: "resources/customisation/character_eyes/character_eyes28.png"},
|
||||
{name: "eyes_29", img: "resources/customisation/character_eyes/character_eyes29.png"},
|
||||
{name: "eyes_30", img: "resources/customisation/character_eyes/character_eyes30.png"}
|
||||
|
||||
]
|
||||
|
||||
export const HAIR_RESOURCES: Array<BodyResourceDescriptionInterface> = [
|
||||
{name:"hair_1", img: "resources/customisation/character_hairs/character_hairs0.png"},
|
||||
{name:"hair_2", img: "resources/customisation/character_hairs/character_hairs1.png"},
|
||||
{name:"hair_3", img: "resources/customisation/character_hairs/character_hairs2.png"},
|
||||
{name:"hair_4", img: "resources/customisation/character_hairs/character_hairs3.png"},
|
||||
{name:"hair_5", img: "resources/customisation/character_hairs/character_hairs4.png"},
|
||||
{name:"hair_6", img: "resources/customisation/character_hairs/character_hairs5.png"},
|
||||
{name:"hair_7", img: "resources/customisation/character_hairs/character_hairs6.png"},
|
||||
{name:"hair_8", img: "resources/customisation/character_hairs/character_hairs7.png"},
|
||||
{name:"hair_9", img: "resources/customisation/character_hairs/character_hairs8.png"},
|
||||
{name:"hair_10",img: "resources/customisation/character_hairs/character_hairs9.png"},
|
||||
{name:"hair_11",img: "resources/customisation/character_hairs/character_hairs10.png"},
|
||||
{name:"hair_12",img: "resources/customisation/character_hairs/character_hairs11.png"},
|
||||
{name:"hair_13",img: "resources/customisation/character_hairs/character_hairs12.png"},
|
||||
{name:"hair_14",img: "resources/customisation/character_hairs/character_hairs13.png"},
|
||||
{name:"hair_15",img: "resources/customisation/character_hairs/character_hairs14.png"},
|
||||
{name:"hair_16",img: "resources/customisation/character_hairs/character_hairs15.png"},
|
||||
{name:"hair_17",img: "resources/customisation/character_hairs/character_hairs16.png"},
|
||||
{name:"hair_18",img: "resources/customisation/character_hairs/character_hairs17.png"},
|
||||
{name:"hair_19",img: "resources/customisation/character_hairs/character_hairs18.png"},
|
||||
{name:"hair_20",img: "resources/customisation/character_hairs/character_hairs19.png"},
|
||||
{name:"hair_21",img: "resources/customisation/character_hairs/character_hairs20.png"},
|
||||
{name:"hair_22",img: "resources/customisation/character_hairs/character_hairs21.png"},
|
||||
{name:"hair_23",img: "resources/customisation/character_hairs/character_hairs22.png"},
|
||||
{name:"hair_24",img: "resources/customisation/character_hairs/character_hairs23.png"},
|
||||
{name:"hair_25",img: "resources/customisation/character_hairs/character_hairs24.png"},
|
||||
{name:"hair_26",img: "resources/customisation/character_hairs/character_hairs25.png"},
|
||||
{name:"hair_27",img: "resources/customisation/character_hairs/character_hairs26.png"},
|
||||
{name:"hair_28",img: "resources/customisation/character_hairs/character_hairs27.png"},
|
||||
{name:"hair_29",img: "resources/customisation/character_hairs/character_hairs28.png"},
|
||||
{name:"hair_30",img: "resources/customisation/character_hairs/character_hairs29.png"},
|
||||
{name:"hair_31",img: "resources/customisation/character_hairs/character_hairs30.png"},
|
||||
{name:"hair_32",img: "resources/customisation/character_hairs/character_hairs31.png"},
|
||||
{name:"hair_33",img: "resources/customisation/character_hairs/character_hairs32.png"},
|
||||
{name:"hair_34",img: "resources/customisation/character_hairs/character_hairs33.png"},
|
||||
{name:"hair_35",img: "resources/customisation/character_hairs/character_hairs34.png"},
|
||||
{name:"hair_36",img: "resources/customisation/character_hairs/character_hairs35.png"},
|
||||
{name:"hair_37",img: "resources/customisation/character_hairs/character_hairs36.png"},
|
||||
{name:"hair_38",img: "resources/customisation/character_hairs/character_hairs37.png"},
|
||||
{name:"hair_39",img: "resources/customisation/character_hairs/character_hairs38.png"},
|
||||
{name:"hair_40",img: "resources/customisation/character_hairs/character_hairs39.png"},
|
||||
{name:"hair_41",img: "resources/customisation/character_hairs/character_hairs40.png"},
|
||||
{name:"hair_42",img: "resources/customisation/character_hairs/character_hairs41.png"},
|
||||
{name:"hair_43",img: "resources/customisation/character_hairs/character_hairs42.png"},
|
||||
{name:"hair_44",img: "resources/customisation/character_hairs/character_hairs43.png"},
|
||||
{name:"hair_45",img: "resources/customisation/character_hairs/character_hairs44.png"},
|
||||
{name:"hair_46",img: "resources/customisation/character_hairs/character_hairs45.png"},
|
||||
{name:"hair_47",img: "resources/customisation/character_hairs/character_hairs46.png"},
|
||||
{name:"hair_48",img: "resources/customisation/character_hairs/character_hairs47.png"},
|
||||
{name:"hair_49",img: "resources/customisation/character_hairs/character_hairs48.png"},
|
||||
{name:"hair_50",img: "resources/customisation/character_hairs/character_hairs49.png"},
|
||||
{name:"hair_51",img: "resources/customisation/character_hairs/character_hairs50.png"},
|
||||
{name:"hair_52",img: "resources/customisation/character_hairs/character_hairs51.png"},
|
||||
{name:"hair_53",img: "resources/customisation/character_hairs/character_hairs52.png"},
|
||||
{name:"hair_54",img: "resources/customisation/character_hairs/character_hairs53.png"},
|
||||
{name:"hair_55",img: "resources/customisation/character_hairs/character_hairs54.png"},
|
||||
{name:"hair_56",img: "resources/customisation/character_hairs/character_hairs55.png"},
|
||||
{name:"hair_57",img: "resources/customisation/character_hairs/character_hairs56.png"},
|
||||
{name:"hair_58",img: "resources/customisation/character_hairs/character_hairs57.png"},
|
||||
{name:"hair_59",img: "resources/customisation/character_hairs/character_hairs58.png"},
|
||||
{name:"hair_60",img: "resources/customisation/character_hairs/character_hairs59.png"},
|
||||
{name:"hair_61",img: "resources/customisation/character_hairs/character_hairs60.png"},
|
||||
{name:"hair_62",img: "resources/customisation/character_hairs/character_hairs61.png"},
|
||||
{name:"hair_63",img: "resources/customisation/character_hairs/character_hairs62.png"},
|
||||
{name:"hair_64",img: "resources/customisation/character_hairs/character_hairs63.png"},
|
||||
{name:"hair_65",img: "resources/customisation/character_hairs/character_hairs64.png"},
|
||||
{name:"hair_66",img: "resources/customisation/character_hairs/character_hairs65.png"},
|
||||
{name:"hair_67",img: "resources/customisation/character_hairs/character_hairs66.png"},
|
||||
{name:"hair_68",img: "resources/customisation/character_hairs/character_hairs67.png"},
|
||||
{name:"hair_69",img: "resources/customisation/character_hairs/character_hairs68.png"},
|
||||
{name:"hair_70",img: "resources/customisation/character_hairs/character_hairs69.png"},
|
||||
{name:"hair_71",img: "resources/customisation/character_hairs/character_hairs70.png"},
|
||||
{name:"hair_72",img: "resources/customisation/character_hairs/character_hairs71.png"},
|
||||
{name:"hair_73",img: "resources/customisation/character_hairs/character_hairs72.png"},
|
||||
{name:"hair_74",img: "resources/customisation/character_hairs/character_hairs73.png"}
|
||||
];
|
||||
|
||||
|
||||
export const CLOTHES_RESOURCES: Array<BodyResourceDescriptionInterface> = [
|
||||
{name:"clothes_1", img: "resources/customisation/character_clothes/character_clothes0.png"},
|
||||
{name:"clothes_2", img: "resources/customisation/character_clothes/character_clothes1.png"},
|
||||
{name:"clothes_3", img: "resources/customisation/character_clothes/character_clothes2.png"},
|
||||
{name:"clothes_4", img: "resources/customisation/character_clothes/character_clothes3.png"},
|
||||
{name:"clothes_5", img: "resources/customisation/character_clothes/character_clothes4.png"},
|
||||
{name:"clothes_6", img: "resources/customisation/character_clothes/character_clothes5.png"},
|
||||
{name:"clothes_7", img: "resources/customisation/character_clothes/character_clothes6.png"},
|
||||
{name:"clothes_8", img: "resources/customisation/character_clothes/character_clothes7.png"},
|
||||
{name:"clothes_9", img: "resources/customisation/character_clothes/character_clothes8.png"},
|
||||
{name:"clothes_10",img: "resources/customisation/character_clothes/character_clothes9.png"},
|
||||
{name:"clothes_11",img: "resources/customisation/character_clothes/character_clothes10.png"},
|
||||
{name:"clothes_12",img: "resources/customisation/character_clothes/character_clothes11.png"},
|
||||
{name:"clothes_13",img: "resources/customisation/character_clothes/character_clothes12.png"},
|
||||
{name:"clothes_14",img: "resources/customisation/character_clothes/character_clothes13.png"},
|
||||
{name:"clothes_15",img: "resources/customisation/character_clothes/character_clothes14.png"},
|
||||
{name:"clothes_16",img: "resources/customisation/character_clothes/character_clothes15.png"},
|
||||
{name:"clothes_17",img: "resources/customisation/character_clothes/character_clothes16.png"},
|
||||
{name:"clothes_18",img: "resources/customisation/character_clothes/character_clothes17.png"},
|
||||
{name:"clothes_19",img: "resources/customisation/character_clothes/character_clothes18.png"},
|
||||
{name:"clothes_20",img: "resources/customisation/character_clothes/character_clothes19.png"},
|
||||
{name:"clothes_21",img: "resources/customisation/character_clothes/character_clothes20.png"},
|
||||
{name:"clothes_22",img: "resources/customisation/character_clothes/character_clothes21.png"},
|
||||
{name:"clothes_23",img: "resources/customisation/character_clothes/character_clothes22.png"},
|
||||
{name:"clothes_24",img: "resources/customisation/character_clothes/character_clothes23.png"},
|
||||
{name:"clothes_25",img: "resources/customisation/character_clothes/character_clothes24.png"},
|
||||
{name:"clothes_26",img: "resources/customisation/character_clothes/character_clothes25.png"},
|
||||
{name:"clothes_27",img: "resources/customisation/character_clothes/character_clothes26.png"},
|
||||
{name:"clothes_28",img: "resources/customisation/character_clothes/character_clothes27.png"},
|
||||
{name:"clothes_29",img: "resources/customisation/character_clothes/character_clothes28.png"},
|
||||
{name:"clothes_30",img: "resources/customisation/character_clothes/character_clothes29.png"},
|
||||
{name:"clothes_31",img: "resources/customisation/character_clothes/character_clothes30.png"},
|
||||
{name:"clothes_32",img: "resources/customisation/character_clothes/character_clothes31.png"},
|
||||
{name:"clothes_33",img: "resources/customisation/character_clothes/character_clothes32.png"},
|
||||
{name:"clothes_34",img: "resources/customisation/character_clothes/character_clothes33.png"},
|
||||
{name:"clothes_35",img: "resources/customisation/character_clothes/character_clothes34.png"},
|
||||
{name:"clothes_36",img: "resources/customisation/character_clothes/character_clothes35.png"},
|
||||
{name:"clothes_37",img: "resources/customisation/character_clothes/character_clothes36.png"},
|
||||
{name:"clothes_38",img: "resources/customisation/character_clothes/character_clothes37.png"},
|
||||
{name:"clothes_39",img: "resources/customisation/character_clothes/character_clothes38.png"},
|
||||
{name:"clothes_40",img: "resources/customisation/character_clothes/character_clothes39.png"},
|
||||
{name:"clothes_41",img: "resources/customisation/character_clothes/character_clothes40.png"},
|
||||
{name:"clothes_42",img: "resources/customisation/character_clothes/character_clothes41.png"},
|
||||
{name:"clothes_43",img: "resources/customisation/character_clothes/character_clothes42.png"},
|
||||
{name:"clothes_44",img: "resources/customisation/character_clothes/character_clothes43.png"},
|
||||
{name:"clothes_45",img: "resources/customisation/character_clothes/character_clothes44.png"},
|
||||
{name:"clothes_46",img: "resources/customisation/character_clothes/character_clothes45.png"},
|
||||
{name:"clothes_47",img: "resources/customisation/character_clothes/character_clothes46.png"},
|
||||
{name:"clothes_48",img: "resources/customisation/character_clothes/character_clothes47.png"},
|
||||
{name:"clothes_49",img: "resources/customisation/character_clothes/character_clothes48.png"},
|
||||
{name:"clothes_50",img: "resources/customisation/character_clothes/character_clothes49.png"},
|
||||
{name:"clothes_51",img: "resources/customisation/character_clothes/character_clothes50.png"},
|
||||
{name:"clothes_52",img: "resources/customisation/character_clothes/character_clothes51.png"},
|
||||
{name:"clothes_53",img: "resources/customisation/character_clothes/character_clothes52.png"},
|
||||
{name:"clothes_54",img: "resources/customisation/character_clothes/character_clothes53.png"},
|
||||
{name:"clothes_55",img: "resources/customisation/character_clothes/character_clothes54.png"},
|
||||
{name:"clothes_56",img: "resources/customisation/character_clothes/character_clothes55.png"},
|
||||
{name:"clothes_57",img: "resources/customisation/character_clothes/character_clothes56.png"},
|
||||
{name:"clothes_58",img: "resources/customisation/character_clothes/character_clothes57.png"},
|
||||
{name:"clothes_59",img: "resources/customisation/character_clothes/character_clothes58.png"},
|
||||
{name:"clothes_60",img: "resources/customisation/character_clothes/character_clothes59.png"},
|
||||
{name:"clothes_61",img: "resources/customisation/character_clothes/character_clothes60.png"},
|
||||
{name:"clothes_62",img: "resources/customisation/character_clothes/character_clothes61.png"},
|
||||
{name:"clothes_63",img: "resources/customisation/character_clothes/character_clothes62.png"},
|
||||
{name:"clothes_64",img: "resources/customisation/character_clothes/character_clothes63.png"},
|
||||
{name:"clothes_65",img: "resources/customisation/character_clothes/character_clothes64.png"},
|
||||
{name:"clothes_66",img: "resources/customisation/character_clothes/character_clothes65.png"},
|
||||
{name:"clothes_67",img: "resources/customisation/character_clothes/character_clothes66.png"},
|
||||
{name:"clothes_68",img: "resources/customisation/character_clothes/character_clothes67.png"},
|
||||
{name:"clothes_69",img: "resources/customisation/character_clothes/character_clothes68.png"},
|
||||
{name:"clothes_70",img: "resources/customisation/character_clothes/character_clothes69.png"},
|
||||
{name:"clothes_pride_shirt",img: "resources/customisation/character_clothes/pride_shirt.png"},
|
||||
{name:"clothes_black_hoodie",img: "resources/customisation/character_clothes/black_hoodie.png"},
|
||||
{name:"clothes_white_hoodie",img: "resources/customisation/character_clothes/white_hoodie.png"},
|
||||
{name:"clothes_engelbert",img: "resources/customisation/character_clothes/engelbert.png"}
|
||||
];
|
||||
|
||||
export const HATS_RESOURCES: Array<BodyResourceDescriptionInterface> = [
|
||||
{name: "hats_1", img: "resources/customisation/character_hats/character_hats1.png"},
|
||||
{name: "hats_2", img: "resources/customisation/character_hats/character_hats2.png"},
|
||||
{name: "hats_3", img: "resources/customisation/character_hats/character_hats3.png"},
|
||||
{name: "hats_4", img: "resources/customisation/character_hats/character_hats4.png"},
|
||||
{name: "hats_5", img: "resources/customisation/character_hats/character_hats5.png"},
|
||||
{name: "hats_6", img: "resources/customisation/character_hats/character_hats6.png"},
|
||||
{name: "hats_7", img: "resources/customisation/character_hats/character_hats7.png"},
|
||||
{name: "hats_8", img: "resources/customisation/character_hats/character_hats8.png"},
|
||||
{name: "hats_9", img: "resources/customisation/character_hats/character_hats9.png"},
|
||||
{name: "hats_10", img: "resources/customisation/character_hats/character_hats10.png"},
|
||||
{name: "hats_11", img: "resources/customisation/character_hats/character_hats11.png"},
|
||||
{name: "hats_12", img: "resources/customisation/character_hats/character_hats12.png"},
|
||||
{name: "hats_13", img: "resources/customisation/character_hats/character_hats13.png"},
|
||||
{name: "hats_14", img: "resources/customisation/character_hats/character_hats14.png"},
|
||||
{name: "hats_15", img: "resources/customisation/character_hats/character_hats15.png"},
|
||||
{name: "hats_16", img: "resources/customisation/character_hats/character_hats16.png"},
|
||||
{name: "hats_17", img: "resources/customisation/character_hats/character_hats17.png"},
|
||||
{name: "hats_18", img: "resources/customisation/character_hats/character_hats18.png"},
|
||||
{name: "hats_19", img: "resources/customisation/character_hats/character_hats19.png"},
|
||||
{name: "hats_20", img: "resources/customisation/character_hats/character_hats20.png"},
|
||||
{name: "hats_21", img: "resources/customisation/character_hats/character_hats21.png"},
|
||||
{name: "hats_22", img: "resources/customisation/character_hats/character_hats22.png"},
|
||||
{name: "hats_23", img: "resources/customisation/character_hats/character_hats23.png"},
|
||||
{name: "hats_24", img: "resources/customisation/character_hats/character_hats24.png"},
|
||||
{name: "hats_25", img: "resources/customisation/character_hats/character_hats25.png"},
|
||||
{name: "hats_26", img: "resources/customisation/character_hats/character_hats26.png"},
|
||||
{name: "tinfoil_hat1", img: "resources/customisation/character_hats/tinfoil_hat1.png"}
|
||||
];
|
||||
|
||||
export const ACCESSORIES_RESOURCES: Array<BodyResourceDescriptionInterface> = [
|
||||
{name: "accessory_1", img: "resources/customisation/character_accessories/character_accessories1.png"},
|
||||
{name: "accessory_2", img: "resources/customisation/character_accessories/character_accessories2.png"},
|
||||
{name: "accessory_3", img: "resources/customisation/character_accessories/character_accessories3.png"},
|
||||
{name: "accessory_4", img: "resources/customisation/character_accessories/character_accessories4.png"},
|
||||
{name: "accessory_5", img: "resources/customisation/character_accessories/character_accessories5.png"},
|
||||
{name: "accessory_6", img: "resources/customisation/character_accessories/character_accessories6.png"},
|
||||
{name: "accessory_7", img: "resources/customisation/character_accessories/character_accessories7.png"},
|
||||
{name: "accessory_8", img: "resources/customisation/character_accessories/character_accessories8.png"},
|
||||
{name: "accessory_9", img: "resources/customisation/character_accessories/character_accessories9.png"},
|
||||
{name: "accessory_10", img: "resources/customisation/character_accessories/character_accessories10.png"},
|
||||
{name: "accessory_11", img: "resources/customisation/character_accessories/character_accessories11.png"},
|
||||
{name: "accessory_12", img: "resources/customisation/character_accessories/character_accessories12.png"},
|
||||
{name: "accessory_13", img: "resources/customisation/character_accessories/character_accessories13.png"},
|
||||
{name: "accessory_14", img: "resources/customisation/character_accessories/character_accessories14.png"},
|
||||
{name: "accessory_15", img: "resources/customisation/character_accessories/character_accessories15.png"},
|
||||
{name: "accessory_16", img: "resources/customisation/character_accessories/character_accessories16.png"},
|
||||
{name: "accessory_17", img: "resources/customisation/character_accessories/character_accessories17.png"},
|
||||
{name: "accessory_18", img: "resources/customisation/character_accessories/character_accessories18.png"},
|
||||
{name: "accessory_19", img: "resources/customisation/character_accessories/character_accessories19.png"},
|
||||
{name: "accessory_20", img: "resources/customisation/character_accessories/character_accessories20.png"},
|
||||
{name: "accessory_21", img: "resources/customisation/character_accessories/character_accessories21.png"},
|
||||
{name: "accessory_22", img: "resources/customisation/character_accessories/character_accessories22.png"},
|
||||
{name: "accessory_23", img: "resources/customisation/character_accessories/character_accessories23.png"},
|
||||
{name: "accessory_24", img: "resources/customisation/character_accessories/character_accessories24.png"},
|
||||
{name: "accessory_25", img: "resources/customisation/character_accessories/character_accessories25.png"},
|
||||
{name: "accessory_26", img: "resources/customisation/character_accessories/character_accessories26.png"},
|
||||
{name: "accessory_27", img: "resources/customisation/character_accessories/character_accessories27.png"},
|
||||
{name: "accessory_28", img: "resources/customisation/character_accessories/character_accessories28.png"},
|
||||
{name: "accessory_29", img: "resources/customisation/character_accessories/character_accessories29.png"},
|
||||
{name: "accessory_30", img: "resources/customisation/character_accessories/character_accessories30.png"},
|
||||
{name: "accessory_31", img: "resources/customisation/character_accessories/character_accessories31.png"},
|
||||
{name: "accessory_32", img: "resources/customisation/character_accessories/character_accessories32.png"},
|
||||
{name: "accessory_mate_bottle", img: "resources/customisation/character_accessories/mate_bottle1.png"},
|
||||
{name: "accessory_mask", img: "resources/customisation/character_accessories/mask.png"}
|
||||
];
|
||||
|
||||
export const LAYERS: Array<Array<BodyResourceDescriptionInterface>> = [
|
||||
COLOR_RESOURCES,
|
||||
EYES_RESOURCES,
|
||||
HAIR_RESOURCES,
|
||||
CLOTHES_RESOURCES,
|
||||
HATS_RESOURCES,
|
||||
ACCESSORIES_RESOURCES
|
||||
];
|
||||
|
||||
export const loadAllLayers = (load: LoaderPlugin) => {
|
||||
for (let j = 0; j < LAYERS.length; j++) {
|
||||
for (let i = 0; i < LAYERS[j].length; i++) {
|
||||
load.spritesheet(
|
||||
LAYERS[j][i].name,
|
||||
LAYERS[j][i].img,
|
||||
{frameWidth: 32, frameHeight: 32}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const loadCustomTexture = (load: LoaderPlugin, texture: CharacterTexture) => {
|
||||
const name = 'customCharacterTexture'+texture.id;
|
||||
load.spritesheet(
|
||||
name,
|
||||
texture.url,
|
||||
{frameWidth: 32, frameHeight: 32}
|
||||
);
|
||||
}
|
||||
|
||||
export const OBJECTS: Array<PlayerResourceDescriptionInterface> = [
|
||||
{name:'layout_modes', img:'resources/objects/layout_modes.png'},
|
||||
{name:'teleportation', img:'resources/objects/teleportation.png'},
|
||||
];
|
||||
|
||||
export const loadObject = (load: LoaderPlugin) => {
|
||||
for (let j = 0; j < OBJECTS.length; j++) {
|
||||
load.spritesheet(
|
||||
OBJECTS[j].name,
|
||||
OBJECTS[j].img,
|
||||
{frameWidth: 32, frameHeight: 32}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export const loadPlayerCharacters = (load: LoaderPlugin) => {
|
||||
PLAYER_RESOURCES.forEach((playerResource: PlayerResourceDescriptionInterface) => {
|
||||
load.spritesheet(
|
||||
playerResource.name,
|
||||
playerResource.img,
|
||||
{frameWidth: 32, frameHeight: 32}
|
||||
);
|
||||
});
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
import {PointInterface} from "../../Connexion/ConnexionModels";
|
||||
import {BodyResourceDescriptionInterface} from "../Entity/body_character";
|
||||
import type {PointInterface} from "../../Connexion/ConnexionModels";
|
||||
import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
||||
|
||||
export interface AddPlayerInterface {
|
||||
userId: number;
|
||||
name: string;
|
||||
characterLayers: BodyResourceDescriptionInterface[];
|
||||
position: PointInterface;
|
||||
visitCardUrl: string|null;
|
||||
companion: string|null;
|
||||
}
|
||||
|
|
8
front/src/Phaser/Game/DepthIndexes.ts
Normal file
8
front/src/Phaser/Game/DepthIndexes.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
//this file contains all the depth indexes which will be used in our game
|
||||
|
||||
export const DEPTH_TILE_INDEX = 0;
|
||||
//Note: Player characters use their y coordinate as their depth to simulate a perspective.
|
||||
//See the Character class.
|
||||
export const DEPTH_OVERLAY_INDEX = 10000;
|
||||
export const DEPTH_INGAME_TEXT_INDEX = 100000;
|
||||
export const DEPTH_UI_INDEX = 1000000;
|
75
front/src/Phaser/Game/DirtyScene.ts
Normal file
75
front/src/Phaser/Game/DirtyScene.ts
Normal file
|
@ -0,0 +1,75 @@
|
|||
import {ResizableScene} from "../Login/ResizableScene";
|
||||
import GameObject = Phaser.GameObjects.GameObject;
|
||||
import Events = Phaser.Scenes.Events;
|
||||
import AnimationEvents = Phaser.Animations.Events;
|
||||
import StructEvents = Phaser.Structs.Events;
|
||||
import {SKIP_RENDER_OPTIMIZATIONS} from "../../Enum/EnvironmentVariable";
|
||||
|
||||
/**
|
||||
* A scene that can track its dirty/pristine state.
|
||||
*/
|
||||
export abstract class DirtyScene extends ResizableScene {
|
||||
private isAlreadyTracking: boolean = false;
|
||||
protected dirty:boolean = true;
|
||||
private objectListChanged:boolean = true;
|
||||
private physicsEnabled: boolean = false;
|
||||
|
||||
/**
|
||||
* Track all objects added to the scene and adds a callback each time an animation is added.
|
||||
* Whenever an object is added, removed, or when an animation is played, the dirty state is set to true.
|
||||
*
|
||||
* Note: this does not work with animations from sprites inside containers.
|
||||
*/
|
||||
protected trackDirtyAnims(): void {
|
||||
if (this.isAlreadyTracking || SKIP_RENDER_OPTIMIZATIONS) {
|
||||
return;
|
||||
}
|
||||
this.isAlreadyTracking = true;
|
||||
const trackAnimationFunction = this.trackAnimation.bind(this);
|
||||
this.sys.updateList.on(StructEvents.PROCESS_QUEUE_ADD, (gameObject: GameObject) => {
|
||||
this.objectListChanged = true;
|
||||
gameObject.on(AnimationEvents.ANIMATION_UPDATE, trackAnimationFunction);
|
||||
});
|
||||
this.sys.updateList.on(StructEvents.PROCESS_QUEUE_REMOVE, (gameObject: GameObject) => {
|
||||
this.objectListChanged = true;
|
||||
gameObject.removeListener(AnimationEvents.ANIMATION_UPDATE, trackAnimationFunction);
|
||||
});
|
||||
|
||||
this.events.on(Events.RENDER, () => {
|
||||
this.objectListChanged = false;
|
||||
});
|
||||
|
||||
this.physics.disableUpdate();
|
||||
this.events.on(Events.POST_UPDATE, () => {
|
||||
let objectMoving = false;
|
||||
for (const body of this.physics.world.bodies.entries) {
|
||||
if (body.velocity.x !== 0 || body.velocity.y !== 0) {
|
||||
this.objectListChanged = true;
|
||||
objectMoving = true;
|
||||
if (!this.physicsEnabled) {
|
||||
this.physics.enableUpdate();
|
||||
this.physicsEnabled = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!objectMoving && this.physicsEnabled) {
|
||||
this.physics.disableUpdate();
|
||||
this.physicsEnabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private trackAnimation(): void {
|
||||
this.objectListChanged = true;
|
||||
}
|
||||
|
||||
public isDirty(): boolean {
|
||||
return this.dirty || this.objectListChanged;
|
||||
}
|
||||
|
||||
public onResize(): void {
|
||||
this.objectListChanged = true;
|
||||
}
|
||||
}
|
73
front/src/Phaser/Game/EmoteManager.ts
Normal file
73
front/src/Phaser/Game/EmoteManager.ts
Normal file
|
@ -0,0 +1,73 @@
|
|||
import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
||||
import {emoteEventStream} from "../../Connexion/EmoteEventStream";
|
||||
import type {GameScene} from "./GameScene";
|
||||
import type {RadialMenuItem} from "../Components/RadialMenu";
|
||||
import LoaderPlugin = Phaser.Loader.LoaderPlugin;
|
||||
import type {Subscription} from "rxjs";
|
||||
|
||||
|
||||
interface RegisteredEmote extends BodyResourceDescriptionInterface {
|
||||
name: string;
|
||||
img: string;
|
||||
}
|
||||
|
||||
export const emotes: {[key: string]: RegisteredEmote} = {
|
||||
'emote-heart': {name: 'emote-heart', img: 'resources/emotes/heart-emote.png'},
|
||||
'emote-clap': {name: 'emote-clap', img: 'resources/emotes/clap-emote.png'},
|
||||
'emote-hand': {name: 'emote-hand', img: 'resources/emotes/hand-emote.png'},
|
||||
'emote-thanks': {name: 'emote-thanks', img: 'resources/emotes/thanks-emote.png'},
|
||||
'emote-thumb-up': {name: 'emote-thumb-up', img: 'resources/emotes/thumb-up-emote.png'},
|
||||
'emote-thumb-down': {name: 'emote-thumb-down', img: 'resources/emotes/thumb-down-emote.png'},
|
||||
};
|
||||
|
||||
export class EmoteManager {
|
||||
private subscription: Subscription;
|
||||
|
||||
constructor(private scene: GameScene) {
|
||||
this.subscription = emoteEventStream.stream.subscribe((event) => {
|
||||
const actor = this.scene.MapPlayersByKey.get(event.userId);
|
||||
if (actor) {
|
||||
this.lazyLoadEmoteTexture(event.emoteName).then(emoteKey => {
|
||||
actor.playEmote(emoteKey);
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
createLoadingPromise(loadPlugin: LoaderPlugin, playerResourceDescriptor: BodyResourceDescriptionInterface) {
|
||||
return new Promise<string>((res) => {
|
||||
if (loadPlugin.textureManager.exists(playerResourceDescriptor.name)) {
|
||||
return res(playerResourceDescriptor.name);
|
||||
}
|
||||
loadPlugin.image(playerResourceDescriptor.name, playerResourceDescriptor.img);
|
||||
loadPlugin.once('filecomplete-image-' + playerResourceDescriptor.name, () => res(playerResourceDescriptor.name));
|
||||
});
|
||||
}
|
||||
|
||||
lazyLoadEmoteTexture(textureKey: string): Promise<string> {
|
||||
const emoteDescriptor = emotes[textureKey];
|
||||
if (emoteDescriptor === undefined) {
|
||||
throw 'Emote not found!';
|
||||
}
|
||||
const loadPromise = this.createLoadingPromise(this.scene.load, emoteDescriptor);
|
||||
this.scene.load.start();
|
||||
return loadPromise
|
||||
}
|
||||
|
||||
getMenuImages(): Promise<RadialMenuItem[]> {
|
||||
const promises = [];
|
||||
for (const key in emotes) {
|
||||
const promise = this.lazyLoadEmoteTexture(key).then((textureKey) => {
|
||||
return {
|
||||
image: textureKey,
|
||||
name: textureKey,
|
||||
}
|
||||
});
|
||||
promises.push(promise);
|
||||
}
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
}
|
132
front/src/Phaser/Game/Game.ts
Normal file
132
front/src/Phaser/Game/Game.ts
Normal file
|
@ -0,0 +1,132 @@
|
|||
import {SKIP_RENDER_OPTIMIZATIONS} from "../../Enum/EnvironmentVariable";
|
||||
import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager";
|
||||
import {waScaleManager} from "../Services/WaScaleManager";
|
||||
import {ResizableScene} from "../Login/ResizableScene";
|
||||
|
||||
const Events = Phaser.Core.Events;
|
||||
|
||||
/**
|
||||
* A specialization of the main Phaser Game scene.
|
||||
* It comes with an optimization to skip rendering.
|
||||
*
|
||||
* Beware, the "step" function might vary in future versions of Phaser.
|
||||
*
|
||||
* It also automatically calls "onResize" on any scenes extending ResizableScene.
|
||||
*/
|
||||
export class Game extends Phaser.Game {
|
||||
|
||||
private _isDirty = false;
|
||||
|
||||
|
||||
constructor(GameConfig: Phaser.Types.Core.GameConfig) {
|
||||
super(GameConfig);
|
||||
|
||||
this.scale.on(Phaser.Scale.Events.RESIZE, () => {
|
||||
for (const scene of this.scene.getScenes(true)) {
|
||||
if (scene instanceof ResizableScene) {
|
||||
scene.onResize();
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
/*window.addEventListener('resize', (event) => {
|
||||
// Let's trigger the onResize method of any active scene that is a ResizableScene
|
||||
for (const scene of this.scene.getScenes(true)) {
|
||||
if (scene instanceof ResizableScene) {
|
||||
scene.onResize(event);
|
||||
}
|
||||
}
|
||||
});*/
|
||||
}
|
||||
|
||||
public step(time: number, delta: number)
|
||||
{
|
||||
// @ts-ignore
|
||||
if (this.pendingDestroy)
|
||||
{
|
||||
// @ts-ignore
|
||||
return this.runDestroy();
|
||||
}
|
||||
|
||||
const eventEmitter = this.events;
|
||||
|
||||
// Global Managers like Input and Sound update in the prestep
|
||||
|
||||
eventEmitter.emit(Events.PRE_STEP, time, delta);
|
||||
|
||||
// This is mostly meant for user-land code and plugins
|
||||
|
||||
eventEmitter.emit(Events.STEP, time, delta);
|
||||
|
||||
// Update the Scene Manager and all active Scenes
|
||||
|
||||
this.scene.update(time, delta);
|
||||
|
||||
// Our final event before rendering starts
|
||||
|
||||
eventEmitter.emit(Events.POST_STEP, time, delta);
|
||||
|
||||
// This "if" is the changed introduced by the new "Game" class to avoid rendering unnecessarily.
|
||||
if (SKIP_RENDER_OPTIMIZATIONS || this.isDirty()) {
|
||||
const renderer = this.renderer;
|
||||
|
||||
// Run the Pre-render (clearing the canvas, setting background colors, etc)
|
||||
|
||||
renderer.preRender();
|
||||
|
||||
eventEmitter.emit(Events.PRE_RENDER, renderer, time, delta);
|
||||
|
||||
// The main render loop. Iterates all Scenes and all Cameras in those scenes, rendering to the renderer instance.
|
||||
|
||||
this.scene.render(renderer);
|
||||
|
||||
// The Post-Render call. Tidies up loose end, takes snapshots, etc.
|
||||
|
||||
renderer.postRender();
|
||||
|
||||
// The final event before the step repeats. Your last chance to do anything to the canvas before it all starts again.
|
||||
|
||||
eventEmitter.emit(Events.POST_RENDER, renderer, time, delta);
|
||||
} else {
|
||||
// @ts-ignore
|
||||
this.scene.isProcessing = false;
|
||||
}
|
||||
}
|
||||
|
||||
private isDirty(): boolean {
|
||||
if (this._isDirty) {
|
||||
this._isDirty = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Loop through the scenes in forward order
|
||||
for (let i = 0; i < this.scene.scenes.length; i++)
|
||||
{
|
||||
const scene = this.scene.scenes[i];
|
||||
const sys = scene.sys;
|
||||
|
||||
if (sys.settings.visible && sys.settings.status >= Phaser.Scenes.LOADING && sys.settings.status < Phaser.Scenes.SLEEPING)
|
||||
{
|
||||
// @ts-ignore
|
||||
if(typeof scene.isDirty === 'function') {
|
||||
// @ts-ignore
|
||||
const isDirty = scene.isDirty() || scene.tweens.getAllTweens().length > 0;
|
||||
if (isDirty) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the game as needing to be redrawn.
|
||||
*/
|
||||
public markDirty(): void {
|
||||
this._isDirty = true;
|
||||
}
|
||||
}
|
|
@ -1,11 +1,14 @@
|
|||
import {GameScene} from "./GameScene";
|
||||
import {connectionManager} from "../../Connexion/ConnectionManager";
|
||||
import {Room} from "../../Connexion/Room";
|
||||
import type {Room} from "../../Connexion/Room";
|
||||
import {MenuScene, MenuSceneName} from "../Menu/MenuScene";
|
||||
import {LoginSceneName} from "../Login/LoginScene";
|
||||
import {SelectCharacterSceneName} from "../Login/SelectCharacterScene";
|
||||
import {EnableCameraSceneName} from "../Login/EnableCameraScene";
|
||||
import {localUserStore} from "../../Connexion/LocalUserStore";
|
||||
import {get} from "svelte/store";
|
||||
import {requestedCameraState, requestedMicrophoneState} from "../../Stores/MediaStore";
|
||||
import {helpCameraSettingsVisibleStore} from "../../Stores/HelpCameraSettingsStore";
|
||||
|
||||
export interface HasMovedEvent {
|
||||
direction: string;
|
||||
|
@ -20,21 +23,23 @@ export interface HasMovedEvent {
|
|||
export class GameManager {
|
||||
private playerName: string|null;
|
||||
private characterLayers: string[]|null;
|
||||
private companion: string|null;
|
||||
private startRoom!:Room;
|
||||
currentGameSceneName: string|null = null;
|
||||
|
||||
|
||||
constructor() {
|
||||
this.playerName = localUserStore.getName();
|
||||
this.characterLayers = localUserStore.getCharacterLayers();
|
||||
this.companion = localUserStore.getCompanion();
|
||||
}
|
||||
|
||||
public async init(scenePlugin: Phaser.Scenes.ScenePlugin): Promise<string> {
|
||||
this.startRoom = await connectionManager.initGameConnexion();
|
||||
await this.loadMap(this.startRoom, scenePlugin);
|
||||
|
||||
|
||||
if (!this.playerName) {
|
||||
return LoginSceneName;
|
||||
} else if (!this.characterLayers) {
|
||||
} else if (!this.characterLayers || !this.characterLayers.length) {
|
||||
return SelectCharacterSceneName;
|
||||
} else {
|
||||
return EnableCameraSceneName;
|
||||
|
@ -55,18 +60,29 @@ export class GameManager {
|
|||
return this.playerName;
|
||||
}
|
||||
|
||||
getCharacterLayers(): string[]|null {
|
||||
getCharacterLayers(): string[] {
|
||||
if (!this.characterLayers) {
|
||||
throw 'characterLayers are not set';
|
||||
}
|
||||
return this.characterLayers;
|
||||
}
|
||||
|
||||
|
||||
setCompanion(companion: string|null): void {
|
||||
this.companion = companion;
|
||||
}
|
||||
|
||||
getCompanion(): string|null {
|
||||
return this.companion;
|
||||
}
|
||||
|
||||
public async loadMap(room: Room, scenePlugin: Phaser.Scenes.ScenePlugin): Promise<void> {
|
||||
const roomID = room.id;
|
||||
const mapUrl = await room.getMapUrl();
|
||||
const mapDetail = await room.getMapDetail();
|
||||
|
||||
const gameIndex = scenePlugin.getIndex(roomID);
|
||||
if(gameIndex === -1){
|
||||
const game : Phaser.Scene = new GameScene(room, mapUrl);
|
||||
const game : Phaser.Scene = new GameScene(room, mapDetail.mapUrl);
|
||||
scenePlugin.add(roomID, game, false);
|
||||
}
|
||||
}
|
||||
|
@ -75,8 +91,13 @@ export class GameManager {
|
|||
console.log('starting '+ (this.currentGameSceneName || this.startRoom.id))
|
||||
scenePlugin.start(this.currentGameSceneName || this.startRoom.id);
|
||||
scenePlugin.launch(MenuSceneName);
|
||||
|
||||
if(!localUserStore.getHelpCameraSettingsShown() && (!get(requestedMicrophoneState) || !get(requestedCameraState))){
|
||||
helpCameraSettingsVisibleStore.set(true);
|
||||
localUserStore.setHelpCameraSettingsShown();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public gameSceneIsCreated(scene: GameScene) {
|
||||
this.currentGameSceneName = scene.scene.key;
|
||||
const menuScene: MenuScene = scene.scene.get(MenuSceneName) as MenuScene;
|
||||
|
@ -110,7 +131,7 @@ export class GameManager {
|
|||
scene.scene.run(fallbackSceneName)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public getCurrentGameScene(scene: Phaser.Scene): GameScene {
|
||||
if (this.currentGameSceneName === null) throw 'No current scene id set!';
|
||||
return scene.scene.get(this.currentGameSceneName) as GameScene
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import {ITiledMap} from "../Map/ITiledMap";
|
||||
import type {ITiledMap, ITiledMapLayer} from "../Map/ITiledMap";
|
||||
import {LayersIterator} from "../Map/LayersIterator";
|
||||
|
||||
export type PropertyChangeCallback = (newValue: string | number | boolean | undefined, oldValue: string | number | boolean | undefined, allProps: Map<string, string | boolean | number>) => void;
|
||||
|
||||
|
@ -10,8 +11,10 @@ export class GameMap {
|
|||
private key: number|undefined;
|
||||
private lastProperties = new Map<string, string|boolean|number>();
|
||||
private callbacks = new Map<string, Array<PropertyChangeCallback>>();
|
||||
public readonly layersIterator: LayersIterator;
|
||||
|
||||
public constructor(private map: ITiledMap) {
|
||||
this.layersIterator = new LayersIterator(map);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -29,6 +32,7 @@ export class GameMap {
|
|||
|
||||
const newProps = this.getProperties(key);
|
||||
const oldProps = this.lastProperties;
|
||||
this.lastProperties = newProps;
|
||||
|
||||
// Let's compare the 2 maps:
|
||||
// First new properties vs oldProperties
|
||||
|
@ -45,14 +49,16 @@ export class GameMap {
|
|||
this.trigger(oldPropName, oldPropValue, undefined, newProps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.lastProperties = newProps;
|
||||
public getCurrentProperties(): Map<string, string|boolean|number> {
|
||||
return this.lastProperties;
|
||||
}
|
||||
|
||||
private getProperties(key: number): Map<string, string|boolean|number> {
|
||||
const properties = new Map<string, string|boolean|number>();
|
||||
|
||||
for (const layer of this.map.layers) {
|
||||
for (const layer of this.layersIterator) {
|
||||
if (layer.type !== 'tilelayer') {
|
||||
continue;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
|||
import {HasMovedEvent} from "./GameManager";
|
||||
import type {HasMovedEvent} from "./GameManager";
|
||||
import {MAX_EXTRAPOLATION_TIME} from "../../Enum/EnvironmentVariable";
|
||||
import {PositionInterface} from "../../Connexion/ConnexionModels";
|
||||
import type {PositionInterface} from "../../Connexion/ConnexionModels";
|
||||
|
||||
export class PlayerMovement {
|
||||
public constructor(private startPosition: PositionInterface, private startTick: number, private endPosition: HasMovedEvent, private endTick: number) {
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
* This class is in charge of computing the position of all players.
|
||||
* Player movement is delayed by 200ms so position depends on ticks.
|
||||
*/
|
||||
import {PlayerMovement} from "./PlayerMovement";
|
||||
import {HasMovedEvent} from "./GameManager";
|
||||
import type {PlayerMovement} from "./PlayerMovement";
|
||||
import type {HasMovedEvent} from "./GameManager";
|
||||
|
||||
export class PlayersPositionInterpolator {
|
||||
playerMovements: Map<number, PlayerMovement> = new Map<number, PlayerMovement>();
|
||||
|
|
39
front/src/Phaser/Game/SoundManager.ts
Normal file
39
front/src/Phaser/Game/SoundManager.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
import LoaderPlugin = Phaser.Loader.LoaderPlugin;
|
||||
import BaseSoundManager = Phaser.Sound.BaseSoundManager;
|
||||
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);
|
||||
if (soundPromise !== undefined) {
|
||||
return soundPromise;
|
||||
}
|
||||
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, () => {
|
||||
res(soundManager.add(soundUrl));
|
||||
});
|
||||
loadPlugin.start();
|
||||
});
|
||||
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);
|
||||
if (config === undefined) sound.play();
|
||||
else sound.play(config);
|
||||
}
|
||||
|
||||
public stopSound(soundManager : BaseSoundManager,soundUrl : string){
|
||||
soundManager.get(soundUrl).stop();
|
||||
}
|
||||
}
|
||||
export const soundManager = new SoundManager();
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
import Sprite = Phaser.GameObjects.Sprite;
|
||||
import {OutlinePipeline} from "../Shaders/OutlinePipeline";
|
||||
import {GameScene} from "../Game/GameScene";
|
||||
import type {GameScene} from "../Game/GameScene";
|
||||
|
||||
type EventCallback = (state: unknown, parameters: unknown) => void;
|
||||
|
||||
|
@ -42,8 +42,11 @@ export class ActionableItem {
|
|||
return;
|
||||
}
|
||||
this.isSelectable = true;
|
||||
this.sprite.setPipeline(OutlinePipeline.KEY);
|
||||
this.sprite.pipeline.set2f('uTextureSize', this.sprite.texture.getSourceImage().width, this.sprite.texture.getSourceImage().height);
|
||||
if (this.sprite.pipeline) {
|
||||
// Commented out to try to fix MacOS issue
|
||||
/*this.sprite.setPipeline(OutlinePipeline.KEY);
|
||||
this.sprite.pipeline.set2f('uTextureSize', this.sprite.texture.getSourceImage().width, this.sprite.texture.getSourceImage().height);*/
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -54,7 +57,8 @@ export class ActionableItem {
|
|||
return;
|
||||
}
|
||||
this.isSelectable = false;
|
||||
this.sprite.resetPipeline();
|
||||
// Commented out to try to fix MacOS issue
|
||||
//this.sprite.resetPipeline();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import * as Phaser from 'phaser';
|
||||
import {Scene} from "phaser";
|
||||
import Sprite = Phaser.GameObjects.Sprite;
|
||||
import {ITiledMapObject} from "../../Map/ITiledMap";
|
||||
import {ItemFactoryInterface} from "../ItemFactoryInterface";
|
||||
import {GameScene} from "../../Game/GameScene";
|
||||
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";
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type {GameScene} from "../Game/GameScene";
|
||||
import type {ITiledMapObject} from "../Map/ITiledMap";
|
||||
import type {ActionableItem} from "./ActionableItem";
|
||||
import LoaderPlugin = Phaser.Loader.LoaderPlugin;
|
||||
import {GameScene} from "../Game/GameScene";
|
||||
import {ITiledMapObject} from "../Map/ITiledMap";
|
||||
import {ActionableItem} from "./ActionableItem";
|
||||
|
||||
export interface ItemFactoryInterface {
|
||||
preload: (loader: LoaderPlugin) => void;
|
||||
|
|
41
front/src/Phaser/Login/AbstractCharacterScene.ts
Normal file
41
front/src/Phaser/Login/AbstractCharacterScene.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
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[]> {
|
||||
const textures = this.getTextures();
|
||||
const promises : Promise<BodyResourceDescriptionInterface>[] = [];
|
||||
if (textures) {
|
||||
for (const texture of textures) {
|
||||
if (texture.level === -1) {
|
||||
continue;
|
||||
}
|
||||
promises.push(loadCustomTexture(this.load, texture));
|
||||
}
|
||||
}
|
||||
return Promise.all(promises)
|
||||
}
|
||||
|
||||
loadSelectSceneCharacters() : Promise<BodyResourceDescriptionInterface[]> {
|
||||
const textures = this.getTextures();
|
||||
const promises: Promise<BodyResourceDescriptionInterface>[] = [];
|
||||
if (textures) {
|
||||
for (const texture of textures) {
|
||||
if (texture.level !== -1) {
|
||||
continue;
|
||||
}
|
||||
promises.push(loadCustomTexture(this.load, texture));
|
||||
}
|
||||
}
|
||||
return Promise.all(promises)
|
||||
}
|
||||
|
||||
private getTextures() : CharacterTexture[]|undefined{
|
||||
const localUser = localUserStore.getLocalUser();
|
||||
return localUser?.textures;
|
||||
}
|
||||
}
|
|
@ -1,46 +1,34 @@
|
|||
import {EnableCameraSceneName} from "./EnableCameraScene";
|
||||
import {TextField} from "../Components/TextField";
|
||||
import Image = Phaser.GameObjects.Image;
|
||||
import Rectangle = Phaser.GameObjects.Rectangle;
|
||||
import {BodyResourceDescriptionInterface, LAYERS, loadAllLayers, loadCustomTexture} from "../Entity/body_character";
|
||||
import {loadAllLayers} from "../Entity/PlayerTexturesLoadingManager";
|
||||
import Sprite = Phaser.GameObjects.Sprite;
|
||||
import Container = Phaser.GameObjects.Container;
|
||||
import {gameManager} from "../Game/GameManager";
|
||||
import {ResizableScene} from "./ResizableScene";
|
||||
import {localUserStore} from "../../Connexion/LocalUserStore";
|
||||
import {PlayerResourceDescriptionInterface} from "../Entity/Character";
|
||||
import {SelectCharacterSceneName} from "./SelectCharacterScene";
|
||||
import {LoginSceneName} from "./LoginScene";
|
||||
import {addLoader} from "../Components/Loader";
|
||||
import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
||||
import {AbstractCharacterScene} from "./AbstractCharacterScene";
|
||||
import {areCharacterLayersValid} from "../../Connexion/LocalUser";
|
||||
import { SelectCharacterSceneName } from "./SelectCharacterScene";
|
||||
import {customCharacterSceneVisibleStore} from "../../Stores/CustomCharacterStore";
|
||||
import {waScaleManager} from "../Services/WaScaleManager";
|
||||
import {isMobile} from "../../Enum/EnvironmentVariable";
|
||||
import {CustomizedCharacter} from "../Entity/CustomizedCharacter";
|
||||
|
||||
export const CustomizeSceneName = "CustomizeScene";
|
||||
|
||||
enum CustomizeTextures{
|
||||
icon = "icon",
|
||||
arrowRight = "arrow_right",
|
||||
mainFont = "main_font",
|
||||
arrowUp = "arrow_up",
|
||||
}
|
||||
|
||||
export class CustomizeScene extends ResizableScene {
|
||||
|
||||
private textField!: TextField;
|
||||
private enterField!: TextField;
|
||||
|
||||
private arrowRight!: Image;
|
||||
private arrowLeft!: Image;
|
||||
|
||||
private arrowDown!: Image;
|
||||
private arrowUp!: Image;
|
||||
|
||||
export class CustomizeScene extends AbstractCharacterScene {
|
||||
private Rectangle!: Rectangle;
|
||||
|
||||
private logo!: Image;
|
||||
|
||||
private selectedLayers: number[] = [0];
|
||||
private containersRow: Container[][] = [];
|
||||
private activeRow:number = 0;
|
||||
private containersRow: CustomizedCharacter[][] = [];
|
||||
public activeRow:number = 0;
|
||||
private layers: BodyResourceDescriptionInterface[][] = [];
|
||||
|
||||
protected lazyloadingAttempt = true; //permit to update texture loaded after renderer
|
||||
|
||||
private moveHorizontally: number = 0;
|
||||
private moveVertically: number = 0;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
key: CustomizeSceneName
|
||||
|
@ -48,60 +36,40 @@ export class CustomizeScene extends ResizableScene {
|
|||
}
|
||||
|
||||
preload() {
|
||||
this.load.image(CustomizeTextures.arrowRight, "resources/objects/arrow_right.png");
|
||||
this.load.image(CustomizeTextures.icon, "resources/logos/tcm_full.png");
|
||||
this.load.image(CustomizeTextures.arrowUp, "resources/objects/arrow_up.png");
|
||||
this.load.bitmapFont(CustomizeTextures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
|
||||
|
||||
//load all the png files
|
||||
loadAllLayers(this.load);
|
||||
this.loadCustomSceneSelectCharacters().then((bodyResourceDescriptions) => {
|
||||
bodyResourceDescriptions.forEach((bodyResourceDescription) => {
|
||||
if(bodyResourceDescription.level == undefined || bodyResourceDescription.level < 0 || bodyResourceDescription.level > 5 ){
|
||||
throw 'Texture level is null';
|
||||
}
|
||||
this.layers[bodyResourceDescription.level].unshift(bodyResourceDescription);
|
||||
});
|
||||
this.lazyloadingAttempt = true;
|
||||
});
|
||||
|
||||
// load custom layers
|
||||
this.layers = LAYERS;
|
||||
this.layers = loadAllLayers(this.load);
|
||||
this.lazyloadingAttempt = false;
|
||||
|
||||
const localUser = localUserStore.getLocalUser();
|
||||
|
||||
const textures = localUser?.textures;
|
||||
if (textures) {
|
||||
for (const texture of textures) {
|
||||
loadCustomTexture(this.load, texture);
|
||||
const name = 'customCharacterTexture'+texture.id;
|
||||
this.layers[texture.level].unshift({
|
||||
name,
|
||||
img: texture.url
|
||||
});
|
||||
}
|
||||
}
|
||||
//this function must stay at the end of preload function
|
||||
addLoader(this);
|
||||
}
|
||||
|
||||
create() {
|
||||
this.textField = new TextField(this, this.game.renderer.width / 2, 30, 'Customize your own Avatar!');
|
||||
customCharacterSceneVisibleStore.set(true);
|
||||
this.events.addListener('wake', () => {
|
||||
waScaleManager.saveZoom();
|
||||
waScaleManager.zoomModifier = isMobile() ? 3 : 1;
|
||||
customCharacterSceneVisibleStore.set(true);
|
||||
});
|
||||
|
||||
this.enterField = new TextField(this, this.game.renderer.width / 2, 40, 'you can start the game by pressing SPACE..');
|
||||
waScaleManager.saveZoom();
|
||||
waScaleManager.zoomModifier = isMobile() ? 3 : 1;
|
||||
|
||||
this.logo = new Image(this, this.game.renderer.width - 30, this.game.renderer.height - 20, CustomizeTextures.icon);
|
||||
this.add.existing(this.logo);
|
||||
|
||||
|
||||
this.arrowRight = new Image(this, this.game.renderer.width*0.9, this.game.renderer.height/2, CustomizeTextures.arrowRight);
|
||||
this.add.existing(this.arrowRight);
|
||||
|
||||
this.arrowLeft = new Image(this, this.game.renderer.width/9, this.game.renderer.height/2, CustomizeTextures.arrowRight);
|
||||
this.arrowLeft.flipX = true;
|
||||
this.add.existing(this.arrowLeft);
|
||||
|
||||
|
||||
this.Rectangle = this.add.rectangle(this.cameras.main.worldView.x + this.cameras.main.width / 2, this.cameras.main.worldView.y + this.cameras.main.height / 2, 32, 33)
|
||||
this.Rectangle = this.add.rectangle(this.cameras.main.worldView.x + this.cameras.main.width / 2, this.cameras.main.worldView.y + this.cameras.main.height / 3, 32, 33)
|
||||
this.Rectangle.setStrokeStyle(2, 0xFFFFFF);
|
||||
this.add.existing(this.Rectangle);
|
||||
|
||||
this.arrowDown = new Image(this, this.game.renderer.width - 30, 100, CustomizeTextures.arrowUp);
|
||||
this.arrowDown.flipY = true;
|
||||
this.add.existing(this.arrowDown);
|
||||
|
||||
this.arrowUp = new Image(this, this.game.renderer.width - 30, 60, CustomizeTextures.arrowUp);
|
||||
this.add.existing(this.arrowUp);
|
||||
|
||||
this.createCustomizeLayer(0, 0, 0);
|
||||
this.createCustomizeLayer(0, 0, 1);
|
||||
this.createCustomizeLayer(0, 0, 2);
|
||||
|
@ -111,25 +79,19 @@ export class CustomizeScene extends ResizableScene {
|
|||
|
||||
this.moveLayers();
|
||||
this.input.keyboard.on('keyup-ENTER', () => {
|
||||
const layers: string[] = [];
|
||||
let i = 0;
|
||||
for (const layerItem of this.selectedLayers) {
|
||||
if (layerItem !== undefined) {
|
||||
layers.push(this.layers[i][layerItem].name);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
gameManager.setCharacterLayers(layers);
|
||||
|
||||
this.scene.sleep(CustomizeSceneName);
|
||||
gameManager.tryResumingGame(this, EnableCameraSceneName);
|
||||
this.nextSceneToCamera();
|
||||
});
|
||||
this.input.keyboard.on('keyup-BACKSPACE', () => {
|
||||
this.backToPreviousScene();
|
||||
});
|
||||
|
||||
this.input.keyboard.on('keyup-RIGHT', () => this.moveCursorHorizontally(1));
|
||||
this.input.keyboard.on('keyup-LEFT', () => this.moveCursorHorizontally(-1));
|
||||
this.input.keyboard.on('keyup-DOWN', () => this.moveCursorVertically(1));
|
||||
this.input.keyboard.on('keyup-UP', () => this.moveCursorVertically(-1));
|
||||
// Note: the key bindings are not directly put on the moveCursorVertically or moveCursorHorizontally methods
|
||||
// because if 2 such events are fired close to one another, it makes the whole application crawl to a halt (for a reason I cannot
|
||||
// explain, the list of sprites managed by the update list become immense
|
||||
this.input.keyboard.on('keyup-RIGHT', () => this.moveHorizontally = 1);
|
||||
this.input.keyboard.on('keyup-LEFT', () => this.moveHorizontally = -1);
|
||||
this.input.keyboard.on('keyup-DOWN', () => this.moveVertically = 1);
|
||||
this.input.keyboard.on('keyup-UP', () => this.moveVertically = -1);
|
||||
|
||||
const customCursorPosition = localUserStore.getCustomCursorPosition();
|
||||
if (customCursorPosition) {
|
||||
|
@ -138,9 +100,19 @@ export class CustomizeScene extends ResizableScene {
|
|||
this.moveLayers();
|
||||
this.updateSelectedLayer();
|
||||
}
|
||||
|
||||
this.onResize();
|
||||
}
|
||||
|
||||
private moveCursorHorizontally(index: number): void {
|
||||
public moveCursorHorizontally(index: number): void {
|
||||
this.moveHorizontally = index;
|
||||
}
|
||||
|
||||
public moveCursorVertically(index: number): void {
|
||||
this.moveVertically = index;
|
||||
}
|
||||
|
||||
private doMoveCursorHorizontally(index: number): void {
|
||||
this.selectedLayers[this.activeRow] += index;
|
||||
if (this.selectedLayers[this.activeRow] < 0) {
|
||||
this.selectedLayers[this.activeRow] = 0
|
||||
|
@ -152,7 +124,8 @@ export class CustomizeScene extends ResizableScene {
|
|||
this.saveInLocalStorage();
|
||||
}
|
||||
|
||||
private moveCursorVertically(index:number): void {
|
||||
private doMoveCursorVertically(index:number): void {
|
||||
|
||||
this.activeRow += index;
|
||||
if (this.activeRow < 0) {
|
||||
this.activeRow = 0
|
||||
|
@ -167,11 +140,6 @@ export class CustomizeScene extends ResizableScene {
|
|||
localUserStore.setCustomCursorPosition(this.activeRow, this.selectedLayers);
|
||||
}
|
||||
|
||||
update(time: number, delta: number): void {
|
||||
super.update(time, delta);
|
||||
this.enterField.setVisible(!!(Math.floor(time / 500) % 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x, the layer's vertical position
|
||||
* @param y, the layer's horizontal position
|
||||
|
@ -205,20 +173,20 @@ export class CustomizeScene extends ResizableScene {
|
|||
* @param selectedItem, The number of the item select (0 for black body...)
|
||||
*/
|
||||
private generateCharacter(x: number, y: number, layerNumber: number, selectedItem: number) {
|
||||
return new Container(this, x, y,this.getContainerChildren(layerNumber,selectedItem));
|
||||
return new CustomizedCharacter(this, x, y, this.getContainerChildren(layerNumber,selectedItem));
|
||||
}
|
||||
|
||||
private getContainerChildren(layerNumber: number, selectedItem: number): Array<Sprite> {
|
||||
const children: Array<Sprite> = new Array<Sprite>();
|
||||
private getContainerChildren(layerNumber: number, selectedItem: number): Array<string> {
|
||||
const children: Array<string> = new Array<string>();
|
||||
for (let j = 0; j <= layerNumber; j++) {
|
||||
if (j === layerNumber) {
|
||||
children.push(this.generateLayers(0, 0, this.layers[j][selectedItem].name));
|
||||
children.push(this.layers[j][selectedItem].name);
|
||||
} else {
|
||||
const layer = this.selectedLayers[j];
|
||||
if (layer === undefined) {
|
||||
continue;
|
||||
}
|
||||
children.push(this.generateLayers(0, 0, this.layers[j][layer].name));
|
||||
children.push(this.layers[j][layer].name);
|
||||
}
|
||||
}
|
||||
return children;
|
||||
|
@ -229,7 +197,7 @@ export class CustomizeScene extends ResizableScene {
|
|||
*/
|
||||
private moveLayers(): void {
|
||||
const screenCenterX = this.cameras.main.worldView.x + this.cameras.main.width / 2;
|
||||
const screenCenterY = this.cameras.main.worldView.y + this.cameras.main.height / 2;
|
||||
const screenCenterY = this.cameras.main.worldView.y + this.cameras.main.height / 3;
|
||||
const screenWidth = this.game.renderer.width;
|
||||
const screenHeight = this.game.renderer.height;
|
||||
for (let i = 0; i < this.containersRow.length; i++) {
|
||||
|
@ -255,40 +223,71 @@ export class CustomizeScene extends ResizableScene {
|
|||
* @return a new sprite
|
||||
*/
|
||||
private generateLayers(x: number, y: number, name: string): Sprite {
|
||||
return new Sprite(this, x, y, name);
|
||||
//return new Sprite(this, x, y, name);
|
||||
return this.add.sprite(0, 0, name);
|
||||
}
|
||||
|
||||
private updateSelectedLayer() {
|
||||
for(let i = 0; i < this.containersRow.length; i++){
|
||||
for(let j = 0; j < this.containersRow[i].length; j++){
|
||||
const children = this.getContainerChildren(i, j);
|
||||
this.containersRow[i][j].removeAll(true);
|
||||
this.containersRow[i][j].add(children);
|
||||
const children = this.getContainerChildren(i, j);
|
||||
this.containersRow[i][j].updateSprites(children);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update(time: number, delta: number): void {
|
||||
|
||||
if(this.lazyloadingAttempt){
|
||||
this.moveLayers();
|
||||
this.lazyloadingAttempt = false;
|
||||
}
|
||||
|
||||
if (this.moveHorizontally !== 0) {
|
||||
this.doMoveCursorHorizontally(this.moveHorizontally);
|
||||
this.moveHorizontally = 0;
|
||||
}
|
||||
if (this.moveVertically !== 0) {
|
||||
this.doMoveCursorVertically(this.moveVertically);
|
||||
this.moveVertically = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public onResize(): void {
|
||||
this.moveLayers();
|
||||
|
||||
this.Rectangle.x = this.cameras.main.worldView.x + this.cameras.main.width / 2;
|
||||
this.Rectangle.y = this.cameras.main.worldView.y + this.cameras.main.height / 2;
|
||||
|
||||
this.textField.x = this.game.renderer.width/2;
|
||||
|
||||
this.logo.x = this.game.renderer.width - 30;
|
||||
this.logo.y = this.game.renderer.height - 20;
|
||||
|
||||
this.arrowUp.x = this.game.renderer.width - 30;
|
||||
this.arrowUp.y = 60;
|
||||
|
||||
this.arrowDown.x = this.game.renderer.width - 30;
|
||||
this.arrowDown.y = 100;
|
||||
|
||||
this.arrowLeft.x = this.game.renderer.width/9;
|
||||
this.arrowLeft.y = this.game.renderer.height/2;
|
||||
|
||||
this.arrowRight.x = this.game.renderer.width*0.9;
|
||||
this.arrowRight.y = this.game.renderer.height/2;
|
||||
this.Rectangle.y = this.cameras.main.worldView.y + this.cameras.main.height / 3;
|
||||
}
|
||||
|
||||
public nextSceneToCamera(){
|
||||
const layers: string[] = [];
|
||||
let i = 0;
|
||||
for (const layerItem of this.selectedLayers) {
|
||||
if (layerItem !== undefined) {
|
||||
layers.push(this.layers[i][layerItem].name);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (!areCharacterLayersValid(layers)) {
|
||||
return;
|
||||
}
|
||||
|
||||
gameManager.setCharacterLayers(layers);
|
||||
this.scene.sleep(CustomizeSceneName);
|
||||
waScaleManager.restoreZoom();
|
||||
this.events.removeListener('wake');
|
||||
gameManager.tryResumingGame(this, EnableCameraSceneName);
|
||||
customCharacterSceneVisibleStore.set(false);
|
||||
}
|
||||
|
||||
public backToPreviousScene(){
|
||||
this.scene.sleep(CustomizeSceneName);
|
||||
waScaleManager.restoreZoom();
|
||||
this.scene.run(SelectCharacterSceneName);
|
||||
customCharacterSceneVisibleStore.set(false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,281 +1,50 @@
|
|||
import {gameManager} from "../Game/GameManager";
|
||||
import {TextField} from "../Components/TextField";
|
||||
import Image = Phaser.GameObjects.Image;
|
||||
import {GameSceneInitInterface} from "../Game/GameScene";
|
||||
import {StartMapInterface} from "../../Connexion/ConnexionModels";
|
||||
import {mediaManager} from "../../WebRtc/MediaManager";
|
||||
import {RESOLUTION} from "../../Enum/EnvironmentVariable";
|
||||
import {SoundMeter} from "../Components/SoundMeter";
|
||||
import {SoundMeterSprite} from "../Components/SoundMeterSprite";
|
||||
import {HtmlUtils} from "../../WebRtc/HtmlUtils";
|
||||
import {touchScreenManager} from "../../Touch/TouchScreenManager";
|
||||
import {PinchManager} from "../UserInput/PinchManager";
|
||||
import Zone = Phaser.GameObjects.Zone;
|
||||
import { MenuScene } from "../Menu/MenuScene";
|
||||
import {ResizableScene} from "./ResizableScene";
|
||||
import {
|
||||
enableCameraSceneVisibilityStore,
|
||||
} from "../../Stores/MediaStore";
|
||||
|
||||
export const EnableCameraSceneName = "EnableCameraScene";
|
||||
enum LoginTextures {
|
||||
playButton = "play_button",
|
||||
icon = "icon",
|
||||
mainFont = "main_font",
|
||||
arrowRight = "arrow_right",
|
||||
arrowUp = "arrow_up"
|
||||
}
|
||||
|
||||
export class EnableCameraScene extends Phaser.Scene {
|
||||
private textField!: TextField;
|
||||
private pressReturnField!: TextField;
|
||||
private cameraNameField!: TextField;
|
||||
private logo!: Image;
|
||||
private arrowLeft!: Image;
|
||||
private arrowRight!: Image;
|
||||
private arrowDown!: Image;
|
||||
private arrowUp!: Image;
|
||||
private microphonesList: MediaDeviceInfo[] = new Array<MediaDeviceInfo>();
|
||||
private camerasList: MediaDeviceInfo[] = new Array<MediaDeviceInfo>();
|
||||
private cameraSelected: number = 0;
|
||||
private microphoneSelected: number = 0;
|
||||
private soundMeter: SoundMeter;
|
||||
private soundMeterSprite!: SoundMeterSprite;
|
||||
private microphoneNameField!: TextField;
|
||||
private repositionCallback!: (this: Window, ev: UIEvent) => void;
|
||||
export class EnableCameraScene extends ResizableScene {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
key: EnableCameraSceneName
|
||||
});
|
||||
this.soundMeter = new SoundMeter();
|
||||
}
|
||||
|
||||
preload() {
|
||||
this.load.image(LoginTextures.playButton, "resources/objects/play_button.png");
|
||||
this.load.image(LoginTextures.icon, "resources/logos/tcm_full.png");
|
||||
this.load.image(LoginTextures.arrowRight, "resources/objects/arrow_right.png");
|
||||
this.load.image(LoginTextures.arrowUp, "resources/objects/arrow_up.png");
|
||||
// Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap
|
||||
this.load.bitmapFont(LoginTextures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
|
||||
}
|
||||
|
||||
create() {
|
||||
this.textField = new TextField(this, this.game.renderer.width / 2, 20, 'Turn on your camera and microphone');
|
||||
|
||||
this.pressReturnField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height - 30, 'Press enter to start');
|
||||
|
||||
this.cameraNameField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height - 60, '');
|
||||
|
||||
this.microphoneNameField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height - 40, '');
|
||||
|
||||
this.arrowRight = new Image(this, 0, 0, LoginTextures.arrowRight);
|
||||
this.arrowRight.setOrigin(0.5, 0.5);
|
||||
this.arrowRight.setVisible(false);
|
||||
this.arrowRight.setInteractive().on('pointerdown', this.nextCam.bind(this));
|
||||
this.add.existing(this.arrowRight);
|
||||
|
||||
this.arrowLeft = new Image(this, 0, 0, LoginTextures.arrowRight);
|
||||
this.arrowLeft.setOrigin(0.5, 0.5);
|
||||
this.arrowLeft.setVisible(false);
|
||||
this.arrowLeft.flipX = true;
|
||||
this.arrowLeft.setInteractive().on('pointerdown', this.previousCam.bind(this));
|
||||
this.add.existing(this.arrowLeft);
|
||||
|
||||
this.arrowUp = new Image(this, 0, 0, LoginTextures.arrowUp);
|
||||
this.arrowUp.setOrigin(0.5, 0.5);
|
||||
this.arrowUp.setVisible(false);
|
||||
this.arrowUp.setInteractive().on('pointerdown', this.previousMic.bind(this));
|
||||
this.add.existing(this.arrowUp);
|
||||
|
||||
this.arrowDown = new Image(this, 0, 0, LoginTextures.arrowUp);
|
||||
this.arrowDown.setOrigin(0.5, 0.5);
|
||||
this.arrowDown.setVisible(false);
|
||||
this.arrowDown.flipY = true;
|
||||
this.arrowDown.setInteractive().on('pointerdown', this.nextMic.bind(this));
|
||||
this.add.existing(this.arrowDown);
|
||||
|
||||
this.logo = new Image(this, this.game.renderer.width - 30, this.game.renderer.height - 20, LoginTextures.icon);
|
||||
this.add.existing(this.logo);
|
||||
|
||||
this.input.keyboard.on('keyup-ENTER', () => {
|
||||
this.login();
|
||||
});
|
||||
|
||||
HtmlUtils.getElementByIdOrFail<HTMLDivElement>('webRtcSetup').classList.add('active');
|
||||
|
||||
const mediaPromise = mediaManager.getCamera();
|
||||
mediaPromise.then(this.getDevices.bind(this));
|
||||
mediaPromise.then(this.setupStream.bind(this));
|
||||
|
||||
this.input.keyboard.on('keydown-RIGHT', this.nextCam.bind(this));
|
||||
this.input.keyboard.on('keydown-LEFT', this.previousCam.bind(this));
|
||||
this.input.keyboard.on('keydown-DOWN', this.nextMic.bind(this));
|
||||
this.input.keyboard.on('keydown-UP', this.previousMic.bind(this));
|
||||
|
||||
this.soundMeterSprite = new SoundMeterSprite(this, 50, 50);
|
||||
this.soundMeterSprite.setVisible(false);
|
||||
this.add.existing(this.soundMeterSprite);
|
||||
|
||||
this.repositionCallback = this.reposition.bind(this);
|
||||
window.addEventListener('resize', this.repositionCallback);
|
||||
enableCameraSceneVisibilityStore.showEnableCameraScene();
|
||||
}
|
||||
|
||||
private previousCam(): void {
|
||||
if (this.cameraSelected === 0 || this.camerasList.length === 0) {
|
||||
return;
|
||||
}
|
||||
this.cameraSelected--;
|
||||
mediaManager.setCamera(this.camerasList[this.cameraSelected].deviceId).then(this.setupStream.bind(this));
|
||||
}
|
||||
|
||||
private nextCam(): void {
|
||||
if (this.cameraSelected === this.camerasList.length - 1 || this.camerasList.length === 0) {
|
||||
return;
|
||||
}
|
||||
this.cameraSelected++;
|
||||
// TODO: the change of camera should be OBSERVED (reactive)
|
||||
mediaManager.setCamera(this.camerasList[this.cameraSelected].deviceId).then(this.setupStream.bind(this));
|
||||
}
|
||||
|
||||
private previousMic(): void {
|
||||
if (this.microphoneSelected === 0 || this.microphonesList.length === 0) {
|
||||
return;
|
||||
}
|
||||
this.microphoneSelected--;
|
||||
mediaManager.setMicrophone(this.microphonesList[this.microphoneSelected].deviceId).then(this.setupStream.bind(this));
|
||||
}
|
||||
|
||||
private nextMic(): void {
|
||||
if (this.microphoneSelected === this.microphonesList.length - 1 || this.microphonesList.length === 0) {
|
||||
return;
|
||||
}
|
||||
this.microphoneSelected++;
|
||||
// TODO: the change of camera should be OBSERVED (reactive)
|
||||
mediaManager.setMicrophone(this.microphonesList[this.microphoneSelected].deviceId).then(this.setupStream.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Function called each time a camera is changed
|
||||
*/
|
||||
private setupStream(stream: MediaStream): void {
|
||||
const img = HtmlUtils.getElementByIdOrFail<HTMLDivElement>('webRtcSetupNoVideo');
|
||||
img.style.display = 'none';
|
||||
|
||||
const div = HtmlUtils.getElementByIdOrFail<HTMLVideoElement>('myCamVideoSetup');
|
||||
div.srcObject = stream;
|
||||
|
||||
this.soundMeter.connectToSource(stream, new window.AudioContext());
|
||||
this.soundMeterSprite.setVisible(true);
|
||||
|
||||
this.updateWebCamName();
|
||||
}
|
||||
|
||||
private updateWebCamName(): void {
|
||||
if (this.camerasList.length > 1) {
|
||||
const div = HtmlUtils.getElementByIdOrFail<HTMLVideoElement>('myCamVideoSetup');
|
||||
|
||||
let label = this.camerasList[this.cameraSelected].label;
|
||||
// remove text in parenthesis
|
||||
label = label.replace(/\([^()]*\)/g, '').trim();
|
||||
// remove accents
|
||||
label = label.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
||||
this.cameraNameField.text = label;
|
||||
|
||||
if (this.cameraSelected < this.camerasList.length - 1) {
|
||||
this.arrowRight.setVisible(true);
|
||||
} else {
|
||||
this.arrowRight.setVisible(false);
|
||||
}
|
||||
|
||||
if (this.cameraSelected > 0) {
|
||||
this.arrowLeft.setVisible(true);
|
||||
} else {
|
||||
this.arrowLeft.setVisible(false);
|
||||
}
|
||||
}
|
||||
if (this.microphonesList.length > 1) {
|
||||
let label = this.microphonesList[this.microphoneSelected].label;
|
||||
// remove text in parenthesis
|
||||
label = label.replace(/\([^()]*\)/g, '').trim();
|
||||
// remove accents
|
||||
label = label.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
||||
|
||||
this.microphoneNameField.text = label;
|
||||
|
||||
if (this.microphoneSelected < this.microphonesList.length - 1) {
|
||||
this.arrowDown.setVisible(true);
|
||||
} else {
|
||||
this.arrowDown.setVisible(false);
|
||||
}
|
||||
|
||||
if (this.microphoneSelected > 0) {
|
||||
this.arrowUp.setVisible(true);
|
||||
} else {
|
||||
this.arrowUp.setVisible(false);
|
||||
}
|
||||
|
||||
}
|
||||
this.reposition();
|
||||
}
|
||||
|
||||
private reposition(): void {
|
||||
let div = HtmlUtils.getElementByIdOrFail<HTMLVideoElement>('myCamVideoSetup');
|
||||
let bounds = div.getBoundingClientRect();
|
||||
if (!div.srcObject) {
|
||||
div = HtmlUtils.getElementByIdOrFail<HTMLVideoElement>('webRtcSetup');
|
||||
bounds = div.getBoundingClientRect();
|
||||
}
|
||||
|
||||
this.textField.x = this.game.renderer.width / 2;
|
||||
this.cameraNameField.x = this.game.renderer.width / 2;
|
||||
this.microphoneNameField.x = this.game.renderer.width / 2;
|
||||
this.pressReturnField.x = this.game.renderer.width / 2;
|
||||
this.pressReturnField.x = this.game.renderer.width / 2;
|
||||
|
||||
this.cameraNameField.y = bounds.top / RESOLUTION - 8;
|
||||
|
||||
this.soundMeterSprite.x = this.game.renderer.width / 2 - this.soundMeterSprite.getWidth() / 2;
|
||||
this.soundMeterSprite.y = bounds.bottom / RESOLUTION + 16;
|
||||
|
||||
this.microphoneNameField.y = this.soundMeterSprite.y + 22;
|
||||
|
||||
this.arrowRight.x = bounds.right / RESOLUTION + 16;
|
||||
this.arrowRight.y = (bounds.top + bounds.height / 2) / RESOLUTION;
|
||||
|
||||
this.arrowLeft.x = bounds.left / RESOLUTION - 16;
|
||||
this.arrowLeft.y = (bounds.top + bounds.height / 2) / RESOLUTION;
|
||||
|
||||
this.arrowDown.x = this.microphoneNameField.x + this.microphoneNameField.width / 2 + 16;
|
||||
this.arrowDown.y = this.microphoneNameField.y;
|
||||
|
||||
this.arrowUp.x = this.microphoneNameField.x - this.microphoneNameField.width / 2 - 16;
|
||||
this.arrowUp.y = this.microphoneNameField.y;
|
||||
|
||||
this.pressReturnField.y = Math.max(this.game.renderer.height - 30, this.microphoneNameField.y + 20);
|
||||
this.logo.x = this.game.renderer.width - 30;
|
||||
this.logo.y = Math.max(this.game.renderer.height - 20, this.microphoneNameField.y + 30);
|
||||
public onResize(): void {
|
||||
}
|
||||
|
||||
update(time: number, delta: number): void {
|
||||
this.pressReturnField.setVisible(!!(Math.floor(time / 500) % 2));
|
||||
|
||||
this.soundMeterSprite.setVolume(this.soundMeter.getVolume());
|
||||
}
|
||||
|
||||
private login(): void {
|
||||
HtmlUtils.getElementByIdOrFail<HTMLDivElement>('webRtcSetup').style.display = 'none';
|
||||
this.soundMeter.stop();
|
||||
window.removeEventListener('resize', this.repositionCallback);
|
||||
public login(): void {
|
||||
enableCameraSceneVisibilityStore.hideEnableCameraScene();
|
||||
|
||||
mediaManager.stopCamera();
|
||||
mediaManager.stopMicrophone();
|
||||
|
||||
this.scene.sleep(EnableCameraSceneName)
|
||||
this.scene.sleep(EnableCameraSceneName);
|
||||
gameManager.goToStartingMap(this.scene);
|
||||
}
|
||||
|
||||
private async getDevices() {
|
||||
const mediaDeviceInfos = await navigator.mediaDevices.enumerateDevices();
|
||||
for (const mediaDeviceInfo of mediaDeviceInfos) {
|
||||
if (mediaDeviceInfo.kind === 'audioinput') {
|
||||
this.microphonesList.push(mediaDeviceInfo);
|
||||
} else if (mediaDeviceInfo.kind === 'videoinput') {
|
||||
this.camerasList.push(mediaDeviceInfo);
|
||||
}
|
||||
}
|
||||
this.updateWebCamName();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import {gameManager} from "../Game/GameManager";
|
||||
import {Scene} from "phaser";
|
||||
import {LoginSceneName} from "./LoginScene";
|
||||
import {FourOFourSceneName} from "../Reconnecting/FourOFourScene";
|
||||
import {ErrorScene} from "../Reconnecting/ErrorScene";
|
||||
import {WAError} from "../Reconnecting/WAError";
|
||||
import {waScaleManager} from "../Services/WaScaleManager";
|
||||
|
||||
export const EntrySceneName = "EntryScene";
|
||||
|
||||
|
@ -17,13 +18,21 @@ export class EntryScene extends Scene {
|
|||
}
|
||||
|
||||
create() {
|
||||
|
||||
gameManager.init(this.scene).then((nextSceneName) => {
|
||||
// Let's rescale before starting the game
|
||||
// We can do it at this stage.
|
||||
waScaleManager.applyNewSize();
|
||||
this.scene.start(nextSceneName);
|
||||
}).catch((err) => {
|
||||
console.error(err)
|
||||
this.scene.start(FourOFourSceneName, {
|
||||
url: window.location.pathname.toString()
|
||||
});
|
||||
if (err.response && err.response.status == 404) {
|
||||
ErrorScene.showError(new WAError(
|
||||
'Access link incorrect',
|
||||
'Could not find map. Please check your access link.',
|
||||
'If you want more information, you may contact administrator or contact us at: workadventure@thecodingmachine.com'), this.scene);
|
||||
} else {
|
||||
ErrorScene.showError(err, this.scene);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +1,12 @@
|
|||
import {gameManager} from "../Game/GameManager";
|
||||
import {TextField} from "../Components/TextField";
|
||||
import {TextInput} from "../Components/TextInput";
|
||||
import Image = Phaser.GameObjects.Image;
|
||||
import {PLAYER_RESOURCES, PlayerResourceDescriptionInterface} from "../Entity/Character";
|
||||
import {cypressAsserter} from "../../Cypress/CypressAsserter";
|
||||
import {SelectCharacterSceneName} from "./SelectCharacterScene";
|
||||
import {ResizableScene} from "./ResizableScene";
|
||||
import {EnableCameraSceneName} from "./EnableCameraScene";
|
||||
import {loginSceneVisibleStore} from "../../Stores/LoginSceneStore";
|
||||
|
||||
//todo: put this constants in a dedicated file
|
||||
export const LoginSceneName = "LoginScene";
|
||||
enum LoginTextures {
|
||||
icon = "icon",
|
||||
mainFont = "main_font"
|
||||
}
|
||||
|
||||
export class LoginScene extends ResizableScene {
|
||||
private nameInput!: TextInput;
|
||||
private textField!: TextField;
|
||||
private infoTextField!: TextField;
|
||||
private pressReturnField!: TextField;
|
||||
private logo!: Image;
|
||||
|
||||
private name: string = '';
|
||||
|
||||
constructor() {
|
||||
|
@ -31,71 +17,25 @@ export class LoginScene extends ResizableScene {
|
|||
}
|
||||
|
||||
preload() {
|
||||
cypressAsserter.preloadStarted();
|
||||
//this.load.image(LoginTextures.playButton, "resources/objects/play_button.png");
|
||||
this.load.image(LoginTextures.icon, "resources/logos/tcm_full.png");
|
||||
// Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap
|
||||
this.load.bitmapFont(LoginTextures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
|
||||
cypressAsserter.preloadFinished();
|
||||
//add player png
|
||||
PLAYER_RESOURCES.forEach((playerResource: PlayerResourceDescriptionInterface) => {
|
||||
this.load.spritesheet(
|
||||
playerResource.name,
|
||||
playerResource.img,
|
||||
{frameWidth: 32, frameHeight: 32}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
create() {
|
||||
cypressAsserter.initStarted();
|
||||
|
||||
this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Enter your name:');
|
||||
this.nameInput = new TextInput(this, this.game.renderer.width / 2, 70, 8, this.name,(text: string) => {
|
||||
this.name = text;
|
||||
});
|
||||
|
||||
this.pressReturnField = new TextField(this, this.game.renderer.width / 2, 130, 'Press enter to start');
|
||||
|
||||
this.logo = new Image(this, this.game.renderer.width - 30, this.game.renderer.height - 20, LoginTextures.icon);
|
||||
this.add.existing(this.logo);
|
||||
|
||||
const infoText = "Commands: \n - Arrows or Z,Q,S,D to move\n - SHIFT to run";
|
||||
this.infoTextField = new TextField(this, 10, this.game.renderer.height - 35, infoText, false);
|
||||
|
||||
this.input.keyboard.on('keyup-ENTER', () => {
|
||||
if (this.name === '') {
|
||||
return
|
||||
}
|
||||
this.login(this.name);
|
||||
});
|
||||
|
||||
cypressAsserter.initFinished();
|
||||
loginSceneVisibleStore.set(true);
|
||||
}
|
||||
|
||||
update(time: number, delta: number): void {
|
||||
if (this.name == '') {
|
||||
this.pressReturnField?.setVisible(false);
|
||||
} else {
|
||||
this.pressReturnField?.setVisible(!!(Math.floor(time / 500) % 2));
|
||||
}
|
||||
}
|
||||
|
||||
private login(name: string): void {
|
||||
public login(name: string): void {
|
||||
name = name.trim();
|
||||
gameManager.setPlayerName(name);
|
||||
|
||||
this.scene.stop(LoginSceneName)
|
||||
gameManager.tryResumingGame(this, SelectCharacterSceneName);
|
||||
this.scene.remove(LoginSceneName)
|
||||
this.scene.remove(LoginSceneName);
|
||||
loginSceneVisibleStore.set(false);
|
||||
}
|
||||
|
||||
public onResize(ev: UIEvent): void {
|
||||
this.textField.x = this.game.renderer.width / 2;
|
||||
this.nameInput.setX(this.game.renderer.width / 2 - 64);
|
||||
this.pressReturnField.x = this.game.renderer.width / 2;
|
||||
this.logo.x = this.game.renderer.width - 30;
|
||||
this.logo.y = this.game.renderer.height - 20;
|
||||
this.infoTextField.y = this.game.renderer.height - 35;
|
||||
update(time: number, delta: number): void {
|
||||
}
|
||||
|
||||
public onResize(): void {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,23 @@
|
|||
import {Scene} from "phaser";
|
||||
import DOMElement = Phaser.GameObjects.DOMElement;
|
||||
|
||||
export abstract class ResizableScene extends Scene {
|
||||
public abstract onResize(ev: UIEvent): void;
|
||||
public abstract onResize(): void;
|
||||
|
||||
/**
|
||||
* Centers the DOM element on the X axis.
|
||||
*
|
||||
* @param object
|
||||
* @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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
63
front/src/Phaser/Login/SelectCharacterMobileScene.ts
Normal file
63
front/src/Phaser/Login/SelectCharacterMobileScene.ts
Normal file
|
@ -0,0 +1,63 @@
|
|||
import { SelectCharacterScene } from "./SelectCharacterScene";
|
||||
|
||||
export class SelectCharacterMobileScene extends SelectCharacterScene {
|
||||
|
||||
create(){
|
||||
super.create();
|
||||
this.onResize();
|
||||
this.selectedRectangle.destroy();
|
||||
}
|
||||
|
||||
protected defineSetupPlayer(num: number){
|
||||
const deltaX = 30;
|
||||
const deltaY = 2;
|
||||
let [playerX, playerY] = this.getCharacterPosition();
|
||||
let playerVisible = true;
|
||||
let playerScale = 1.5;
|
||||
let playerOpacity = 1;
|
||||
|
||||
if( this.currentSelectUser !== num ){
|
||||
playerVisible = false;
|
||||
}
|
||||
if( num === (this.currentSelectUser + 1) ){
|
||||
playerY -= deltaY;
|
||||
playerX += deltaX;
|
||||
playerScale = 0.8;
|
||||
playerOpacity = 0.6;
|
||||
playerVisible = true;
|
||||
}
|
||||
if( num === (this.currentSelectUser + 2) ){
|
||||
playerY -= deltaY;
|
||||
playerX += (deltaX * 2);
|
||||
playerScale = 0.8;
|
||||
playerOpacity = 0.6;
|
||||
playerVisible = true;
|
||||
}
|
||||
if( num === (this.currentSelectUser - 1) ){
|
||||
playerY -= deltaY;
|
||||
playerX -= deltaX;
|
||||
playerScale = 0.8;
|
||||
playerOpacity = 0.6;
|
||||
playerVisible = true;
|
||||
}
|
||||
if( num === (this.currentSelectUser - 2) ){
|
||||
playerY -= deltaY;
|
||||
playerX -= (deltaX * 2);
|
||||
playerScale = 0.8;
|
||||
playerOpacity = 0.6;
|
||||
playerVisible = true;
|
||||
}
|
||||
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
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -1,227 +1,271 @@
|
|||
import {gameManager} from "../Game/GameManager";
|
||||
import {TextField} from "../Components/TextField";
|
||||
import Image = Phaser.GameObjects.Image;
|
||||
import Rectangle = Phaser.GameObjects.Rectangle;
|
||||
import {PLAYER_RESOURCES, PlayerResourceDescriptionInterface} from "../Entity/Character";
|
||||
import {EnableCameraSceneName} from "./EnableCameraScene";
|
||||
import {CustomizeSceneName} from "./CustomizeScene";
|
||||
import {ResizableScene} from "./ResizableScene";
|
||||
import {localUserStore} from "../../Connexion/LocalUserStore";
|
||||
|
||||
import {loadAllDefaultModels} from "../Entity/PlayerTexturesLoadingManager";
|
||||
import {addLoader} from "../Components/Loader";
|
||||
import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
||||
import {AbstractCharacterScene} from "./AbstractCharacterScene";
|
||||
import {areCharacterLayersValid} from "../../Connexion/LocalUser";
|
||||
import {touchScreenManager} from "../../Touch/TouchScreenManager";
|
||||
import {PinchManager} from "../UserInput/PinchManager";
|
||||
import {selectCharacterSceneVisibleStore} from "../../Stores/SelectCharacterStore";
|
||||
import {waScaleManager} from "../Services/WaScaleManager";
|
||||
import {isMobile} from "../../Enum/EnvironmentVariable";
|
||||
|
||||
//todo: put this constants in a dedicated file
|
||||
export const SelectCharacterSceneName = "SelectCharacterScene";
|
||||
enum LoginTextures {
|
||||
playButton = "play_button",
|
||||
icon = "icon",
|
||||
mainFont = "main_font",
|
||||
customizeButton = "customize_button",
|
||||
customizeButtonSelected = "customize_button_selected"
|
||||
}
|
||||
|
||||
export class SelectCharacterScene extends ResizableScene {
|
||||
private readonly nbCharactersPerRow = 4;
|
||||
private textField!: TextField;
|
||||
private pressReturnField!: TextField;
|
||||
private logo!: Image;
|
||||
private customizeButton!: Image;
|
||||
private customizeButtonSelected!: Image;
|
||||
export class SelectCharacterScene extends AbstractCharacterScene {
|
||||
protected readonly nbCharactersPerRow = 6;
|
||||
protected selectedPlayer!: Phaser.Physics.Arcade.Sprite|null; // null if we are selecting the "customize" option
|
||||
protected players: Array<Phaser.Physics.Arcade.Sprite> = new Array<Phaser.Physics.Arcade.Sprite>();
|
||||
protected playerModels!: BodyResourceDescriptionInterface[];
|
||||
|
||||
private selectedRectangle!: Rectangle;
|
||||
private selectedRectangleXPos = 0; // Number of the character selected in the rows
|
||||
private selectedRectangleYPos = 0; // Number of the character selected in the columns
|
||||
private selectedPlayer!: Phaser.Physics.Arcade.Sprite|null; // null if we are selecting the "customize" option
|
||||
private players: Array<Phaser.Physics.Arcade.Sprite> = new Array<Phaser.Physics.Arcade.Sprite>();
|
||||
protected selectedRectangle!: Rectangle;
|
||||
|
||||
protected currentSelectUser = 0;
|
||||
protected pointerClicked: boolean = false;
|
||||
protected pointerTimer: number = 0;
|
||||
|
||||
protected lazyloadingAttempt = true; //permit to update texture loaded after renderer
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
key: SelectCharacterSceneName
|
||||
key: SelectCharacterSceneName,
|
||||
});
|
||||
}
|
||||
|
||||
preload() {
|
||||
this.load.image(LoginTextures.playButton, "resources/objects/play_button.png");
|
||||
this.load.image(LoginTextures.icon, "resources/logos/tcm_full.png");
|
||||
// Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap
|
||||
this.load.bitmapFont(LoginTextures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
|
||||
//add player png
|
||||
PLAYER_RESOURCES.forEach((playerResource: PlayerResourceDescriptionInterface) => {
|
||||
this.load.spritesheet(
|
||||
playerResource.name,
|
||||
playerResource.img,
|
||||
{frameWidth: 32, frameHeight: 32}
|
||||
);
|
||||
|
||||
this.loadSelectSceneCharacters().then((bodyResourceDescriptions) => {
|
||||
bodyResourceDescriptions.forEach((bodyResourceDescription) => {
|
||||
this.playerModels.push(bodyResourceDescription);
|
||||
});
|
||||
this.lazyloadingAttempt = true;
|
||||
});
|
||||
this.load.image(LoginTextures.customizeButton, 'resources/objects/customize.png');
|
||||
this.load.image(LoginTextures.customizeButtonSelected, 'resources/objects/customize_selected.png');
|
||||
this.playerModels = loadAllDefaultModels(this.load);
|
||||
this.lazyloadingAttempt = false;
|
||||
|
||||
//this function must stay at the end of preload function
|
||||
addLoader(this);
|
||||
}
|
||||
|
||||
create() {
|
||||
this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Select your character');
|
||||
selectCharacterSceneVisibleStore.set(true);
|
||||
this.events.addListener('wake', () => {
|
||||
waScaleManager.saveZoom();
|
||||
waScaleManager.zoomModifier = isMobile() ? 2 : 1;
|
||||
selectCharacterSceneVisibleStore.set(true);
|
||||
});
|
||||
|
||||
this.pressReturnField = new TextField(this, this.game.renderer.width / 2, 256, 'Press enter to start');
|
||||
if (touchScreenManager.supportTouchScreen) {
|
||||
new PinchManager(this);
|
||||
}
|
||||
|
||||
waScaleManager.saveZoom();
|
||||
waScaleManager.zoomModifier = isMobile() ? 2 : 1;
|
||||
|
||||
const rectangleXStart = this.game.renderer.width / 2 - (this.nbCharactersPerRow / 2) * 32 + 16;
|
||||
|
||||
this.selectedRectangle = this.add.rectangle(rectangleXStart, 90, 32, 32).setStrokeStyle(2, 0xFFFFFF);
|
||||
|
||||
this.logo = new Image(this, this.game.renderer.width - 30, this.game.renderer.height - 20, LoginTextures.icon);
|
||||
this.add.existing(this.logo);
|
||||
|
||||
this.input.keyboard.on('keyup-ENTER', () => {
|
||||
return this.nextScene();
|
||||
});
|
||||
|
||||
this.input.keyboard.on('keydown-RIGHT', () => {
|
||||
if (this.selectedRectangleXPos < this.nbCharactersPerRow - 1) {
|
||||
this.selectedRectangleXPos++;
|
||||
}
|
||||
this.updateSelectedPlayer();
|
||||
});
|
||||
this.input.keyboard.on('keydown-LEFT', () => {
|
||||
if (this.selectedRectangleXPos > 0) {
|
||||
this.selectedRectangleXPos--;
|
||||
}
|
||||
this.updateSelectedPlayer();
|
||||
});
|
||||
this.input.keyboard.on('keydown-DOWN', () => {
|
||||
if (this.selectedRectangleYPos < Math.ceil(PLAYER_RESOURCES.length / this.nbCharactersPerRow)) {
|
||||
this.selectedRectangleYPos++;
|
||||
}
|
||||
this.updateSelectedPlayer();
|
||||
});
|
||||
this.input.keyboard.on('keydown-UP', () => {
|
||||
if (this.selectedRectangleYPos > 0) {
|
||||
this.selectedRectangleYPos--;
|
||||
}
|
||||
this.updateSelectedPlayer();
|
||||
});
|
||||
this.selectedRectangle.setDepth(2);
|
||||
|
||||
/*create user*/
|
||||
this.createCurrentPlayer();
|
||||
|
||||
const playerNumber = localUserStore.getPlayerCharacterIndex();
|
||||
if (playerNumber && playerNumber !== -1) {
|
||||
this.selectedRectangleXPos = playerNumber % this.nbCharactersPerRow;
|
||||
this.selectedRectangleYPos = Math.floor(playerNumber / this.nbCharactersPerRow);
|
||||
this.updateSelectedPlayer();
|
||||
} else if (playerNumber === -1) {
|
||||
this.selectedRectangleYPos = Math.ceil(PLAYER_RESOURCES.length / this.nbCharactersPerRow);
|
||||
this.updateSelectedPlayer();
|
||||
|
||||
this.input.keyboard.on('keyup-ENTER', () => {
|
||||
return this.nextSceneToCameraScene();
|
||||
});
|
||||
|
||||
this.input.keyboard.on('keydown-RIGHT', () => {
|
||||
this.moveToRight();
|
||||
});
|
||||
this.input.keyboard.on('keydown-LEFT', () => {
|
||||
this.moveToLeft();
|
||||
});
|
||||
this.input.keyboard.on('keydown-UP', () => {
|
||||
this.moveToUp();
|
||||
});
|
||||
this.input.keyboard.on('keydown-DOWN', () => {
|
||||
this.moveToDown();
|
||||
});
|
||||
}
|
||||
|
||||
public nextSceneToCameraScene(): void {
|
||||
if (this.selectedPlayer !== null && !areCharacterLayersValid([this.selectedPlayer.texture.key])) {
|
||||
return;
|
||||
}
|
||||
if(!this.selectedPlayer){
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
update(time: number, delta: number): void {
|
||||
this.pressReturnField.setVisible(!!(Math.floor(time / 500) % 2));
|
||||
}
|
||||
|
||||
private nextScene(): void {
|
||||
this.scene.stop(SelectCharacterSceneName);
|
||||
if (this.selectedPlayer !== null) {
|
||||
gameManager.setCharacterLayers([this.selectedPlayer.texture.key]);
|
||||
gameManager.tryResumingGame(this, EnableCameraSceneName);
|
||||
} else {
|
||||
this.scene.run(CustomizeSceneName);
|
||||
waScaleManager.restoreZoom();
|
||||
gameManager.setCharacterLayers([this.selectedPlayer.texture.key]);
|
||||
gameManager.tryResumingGame(this, EnableCameraSceneName);
|
||||
this.players = [];
|
||||
selectCharacterSceneVisibleStore.set(false);
|
||||
this.events.removeListener('wake');
|
||||
}
|
||||
|
||||
public nextSceneToCustomizeScene(): void {
|
||||
if (this.selectedPlayer !== null && !areCharacterLayersValid([this.selectedPlayer.texture.key])) {
|
||||
return;
|
||||
}
|
||||
this.scene.remove(SelectCharacterSceneName);
|
||||
this.scene.sleep(SelectCharacterSceneName);
|
||||
waScaleManager.restoreZoom();
|
||||
this.scene.run(CustomizeSceneName);
|
||||
selectCharacterSceneVisibleStore.set(false);
|
||||
}
|
||||
|
||||
createCurrentPlayer(): void {
|
||||
for (let i = 0; i <PLAYER_RESOURCES.length; i++) {
|
||||
const playerResource = PLAYER_RESOURCES[i];
|
||||
for (let i = 0; i <this.playerModels.length; i++) {
|
||||
const playerResource = this.playerModels[i];
|
||||
|
||||
const col = i % this.nbCharactersPerRow;
|
||||
const row = Math.floor(i / this.nbCharactersPerRow);
|
||||
//check already exist texture
|
||||
if(this.players.find((c) => c.texture.key === playerResource.name)){
|
||||
continue;
|
||||
}
|
||||
|
||||
const [x, y] = this.getCharacterPosition(col, row);
|
||||
const player = this.physics.add.sprite(x, y, playerResource.name, 0);
|
||||
player.setBounce(0.2);
|
||||
player.setCollideWorldBounds(true);
|
||||
const [middleX, middleY] = this.getCharacterPosition();
|
||||
const player = this.physics.add.sprite(middleX, middleY, playerResource.name, 0);
|
||||
this.setUpPlayer(player, i);
|
||||
this.anims.create({
|
||||
key: playerResource.name,
|
||||
frames: this.anims.generateFrameNumbers(playerResource.name, {start: 0, end: 2,}),
|
||||
frameRate: 10,
|
||||
frames: this.anims.generateFrameNumbers(playerResource.name, {start: 0, end: 11}),
|
||||
frameRate: 8,
|
||||
repeat: -1
|
||||
});
|
||||
player.setInteractive().on("pointerdown", () => {
|
||||
this.selectedRectangleXPos = col;
|
||||
this.selectedRectangleYPos = row;
|
||||
this.updateSelectedPlayer();
|
||||
if (this.pointerClicked) {
|
||||
return;
|
||||
}
|
||||
if (this.currentSelectUser === i) {
|
||||
return;
|
||||
}
|
||||
//To not trigger two time the pointerdown events :
|
||||
// We set a boolean to true so that pointerdown events does nothing when the boolean is true
|
||||
// We set a timer that we decrease in update function to not trigger the pointerdown events twice
|
||||
this.pointerClicked = true;
|
||||
this.pointerTimer = 250;
|
||||
this.currentSelectUser = i;
|
||||
this.moveUser();
|
||||
});
|
||||
this.players.push(player);
|
||||
}
|
||||
this.selectedPlayer = this.players[this.currentSelectUser];
|
||||
this.selectedPlayer.play(this.playerModels[this.currentSelectUser].name);
|
||||
}
|
||||
|
||||
this.customizeButton = new Image(this, this.game.renderer.width / 2, 90 + 32 * 4 + 6, LoginTextures.customizeButton);
|
||||
this.customizeButton.setOrigin(0.5, 0.5);
|
||||
this.add.existing(this.customizeButton);
|
||||
this.customizeButtonSelected = new Image(this, this.game.renderer.width / 2, 90 + 32 * 4 + 6, LoginTextures.customizeButtonSelected);
|
||||
this.customizeButtonSelected.setOrigin(0.5, 0.5);
|
||||
this.customizeButtonSelected.setVisible(false);
|
||||
this.add.existing(this.customizeButtonSelected);
|
||||
protected moveUser(){
|
||||
for(let i = 0; i < this.players.length; i++){
|
||||
const player = this.players[i];
|
||||
this.setUpPlayer(player, i);
|
||||
}
|
||||
this.updateSelectedPlayer();
|
||||
}
|
||||
|
||||
this.customizeButton.setInteractive().on("pointerdown", () => {
|
||||
this.selectedRectangleYPos = Math.ceil(PLAYER_RESOURCES.length / this.nbCharactersPerRow);
|
||||
this.updateSelectedPlayer();
|
||||
});
|
||||
public moveToLeft(){
|
||||
if(this.currentSelectUser === 0){
|
||||
return;
|
||||
}
|
||||
this.currentSelectUser -= 1;
|
||||
this.moveUser();
|
||||
}
|
||||
|
||||
this.selectedPlayer = this.players[0];
|
||||
this.selectedPlayer.play(PLAYER_RESOURCES[0].name);
|
||||
public moveToRight(){
|
||||
if(this.currentSelectUser === (this.players.length - 1)){
|
||||
return;
|
||||
}
|
||||
this.currentSelectUser += 1;
|
||||
this.moveUser();
|
||||
}
|
||||
|
||||
protected moveToUp(){
|
||||
if(this.currentSelectUser < this.nbCharactersPerRow){
|
||||
return;
|
||||
}
|
||||
this.currentSelectUser -= this.nbCharactersPerRow;
|
||||
this.moveUser();
|
||||
}
|
||||
|
||||
protected moveToDown(){
|
||||
if((this.currentSelectUser + this.nbCharactersPerRow) > (this.players.length - 1)){
|
||||
return;
|
||||
}
|
||||
this.currentSelectUser += this.nbCharactersPerRow;
|
||||
this.moveUser();
|
||||
}
|
||||
|
||||
protected defineSetupPlayer(num: number){
|
||||
const deltaX = 32;
|
||||
const deltaY = 32;
|
||||
let [playerX, playerY] = this.getCharacterPosition(); // player X and player y are middle of the
|
||||
|
||||
playerX = ( (playerX - (deltaX * 2.5)) + ((deltaX) * (num % this.nbCharactersPerRow)) ); // calcul position on line users
|
||||
playerY = ( (playerY - (deltaY * 2)) + ((deltaY) * ( Math.floor(num / this.nbCharactersPerRow) )) ); // calcul position on column users
|
||||
|
||||
const playerVisible = true;
|
||||
const playerScale = 1;
|
||||
const playerOpacity = 1;
|
||||
|
||||
// if selected
|
||||
if( num === this.currentSelectUser ){
|
||||
this.selectedRectangle.setX(playerX);
|
||||
this.selectedRectangle.setY(playerY);
|
||||
}
|
||||
|
||||
return {playerX, playerY, playerScale, playerOpacity, playerVisible}
|
||||
}
|
||||
|
||||
protected setUpPlayer(player: Phaser.Physics.Arcade.Sprite, num: number){
|
||||
|
||||
const {playerX, playerY, playerScale, playerOpacity, playerVisible} = this.defineSetupPlayer(num);
|
||||
player.setBounce(0.2);
|
||||
player.setCollideWorldBounds(false);
|
||||
player.setVisible( playerVisible );
|
||||
player.setScale(playerScale, playerScale);
|
||||
player.setAlpha(playerOpacity);
|
||||
player.setX(playerX);
|
||||
player.setY(playerY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns pixel position by on column and row number
|
||||
*/
|
||||
private getCharacterPosition(x: number, y: number): [number, number] {
|
||||
protected getCharacterPosition(): [number, number] {
|
||||
return [
|
||||
this.game.renderer.width / 2 + 16 + (x - this.nbCharactersPerRow / 2) * 32,
|
||||
y * 32 + 90
|
||||
this.game.renderer.width / 2,
|
||||
this.game.renderer.height / 2.5
|
||||
];
|
||||
}
|
||||
|
||||
private updateSelectedPlayer(): void {
|
||||
this.selectedPlayer?.anims.pause();
|
||||
// If we selected the customize button
|
||||
if (this.selectedRectangleYPos === Math.ceil(PLAYER_RESOURCES.length / this.nbCharactersPerRow)) {
|
||||
this.selectedPlayer = null;
|
||||
this.selectedRectangle.setVisible(false);
|
||||
this.customizeButtonSelected.setVisible(true);
|
||||
this.customizeButton.setVisible(false);
|
||||
localUserStore.setPlayerCharacterIndex(-1);
|
||||
return;
|
||||
}
|
||||
this.customizeButtonSelected.setVisible(false);
|
||||
this.customizeButton.setVisible(true);
|
||||
const [x, y] = this.getCharacterPosition(this.selectedRectangleXPos, this.selectedRectangleYPos);
|
||||
this.selectedRectangle.setVisible(true);
|
||||
this.selectedRectangle.setX(x);
|
||||
this.selectedRectangle.setY(y);
|
||||
this.selectedRectangle.setSize(32, 32);
|
||||
const playerNumber = this.selectedRectangleXPos + this.selectedRectangleYPos * this.nbCharactersPerRow;
|
||||
const player = this.players[playerNumber];
|
||||
player.play(PLAYER_RESOURCES[playerNumber].name);
|
||||
protected updateSelectedPlayer(): void {
|
||||
this.selectedPlayer?.anims.pause(this.selectedPlayer?.anims.currentAnim.frames[0]);
|
||||
const player = this.players[this.currentSelectUser];
|
||||
player.play(this.playerModels[this.currentSelectUser].name);
|
||||
this.selectedPlayer = player;
|
||||
localUserStore.setPlayerCharacterIndex(playerNumber);
|
||||
localUserStore.setPlayerCharacterIndex(this.currentSelectUser);
|
||||
}
|
||||
|
||||
public onResize(ev: UIEvent): void {
|
||||
this.textField.x = this.game.renderer.width / 2;
|
||||
this.pressReturnField.x = this.game.renderer.width / 2;
|
||||
this.logo.x = this.game.renderer.width - 30;
|
||||
this.logo.y = this.game.renderer.height - 20;
|
||||
this.customizeButton.x = this.game.renderer.width / 2;
|
||||
this.customizeButtonSelected.x = this.game.renderer.width / 2;
|
||||
|
||||
for (let i = 0; i <PLAYER_RESOURCES.length; i++) {
|
||||
const player = this.players[i];
|
||||
|
||||
const col = i % this.nbCharactersPerRow;
|
||||
const row = Math.floor(i / this.nbCharactersPerRow);
|
||||
|
||||
const [x, y] = this.getCharacterPosition(col, row);
|
||||
player.x = x;
|
||||
player.y = y;
|
||||
update(time: number, delta: number): void {
|
||||
// pointerTimer is set to 250 when pointerdown events is trigger
|
||||
// After 250ms, pointerClicked is set to false and the pointerdown events can be trigger again
|
||||
this.pointerTimer -= delta;
|
||||
if (this.pointerTimer <= 0) {
|
||||
this.pointerClicked = false;
|
||||
}
|
||||
|
||||
if(this.lazyloadingAttempt){
|
||||
//re-render players list
|
||||
this.createCurrentPlayer();
|
||||
this.moveUser();
|
||||
this.lazyloadingAttempt = false;
|
||||
}
|
||||
this.updateSelectedPlayer();
|
||||
}
|
||||
|
||||
public onResize(): void {
|
||||
//move position of user
|
||||
this.moveUser();
|
||||
}
|
||||
}
|
||||
|
|
229
front/src/Phaser/Login/SelectCompanionScene.ts
Normal file
229
front/src/Phaser/Login/SelectCompanionScene.ts
Normal file
|
@ -0,0 +1,229 @@
|
|||
import Image = Phaser.GameObjects.Image;
|
||||
import Rectangle = Phaser.GameObjects.Rectangle;
|
||||
import { addLoader } from "../Components/Loader";
|
||||
import { gameManager} from "../Game/GameManager";
|
||||
import { ResizableScene } from "./ResizableScene";
|
||||
import { EnableCameraSceneName } from "./EnableCameraScene";
|
||||
import { localUserStore } from "../../Connexion/LocalUserStore";
|
||||
import type { CompanionResourceDescriptionInterface } from "../Companion/CompanionTextures";
|
||||
import { getAllCompanionResources } from "../Companion/CompanionTexturesLoadingManager";
|
||||
import {touchScreenManager} from "../../Touch/TouchScreenManager";
|
||||
import {PinchManager} from "../UserInput/PinchManager";
|
||||
import { MenuScene } from "../Menu/MenuScene";
|
||||
import {selectCompanionSceneVisibleStore} from "../../Stores/SelectCompanionStore";
|
||||
import {waScaleManager} from "../Services/WaScaleManager";
|
||||
import {isMobile} from "../../Enum/EnvironmentVariable";
|
||||
|
||||
export const SelectCompanionSceneName = "SelectCompanionScene";
|
||||
|
||||
export class SelectCompanionScene extends ResizableScene {
|
||||
private selectedCompanion!: Phaser.Physics.Arcade.Sprite;
|
||||
private companions: Array<Phaser.Physics.Arcade.Sprite> = new Array<Phaser.Physics.Arcade.Sprite>();
|
||||
private companionModels: Array<CompanionResourceDescriptionInterface> = [];
|
||||
private saveZoom: number = 0;
|
||||
|
||||
private currentCompanion = 0;
|
||||
private pointerClicked: boolean = false;
|
||||
private pointerTimer: number = 0;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
key: SelectCompanionSceneName
|
||||
});
|
||||
}
|
||||
|
||||
preload() {
|
||||
getAllCompanionResources(this.load).forEach(model => {
|
||||
this.companionModels.push(model);
|
||||
});
|
||||
|
||||
//this function must stay at the end of preload function
|
||||
addLoader(this);
|
||||
}
|
||||
|
||||
create() {
|
||||
|
||||
selectCompanionSceneVisibleStore.set(true);
|
||||
|
||||
waScaleManager.saveZoom();
|
||||
waScaleManager.zoomModifier = isMobile() ? 2 : 1;
|
||||
|
||||
if (touchScreenManager.supportTouchScreen) {
|
||||
new PinchManager(this);
|
||||
}
|
||||
|
||||
// input events
|
||||
this.input.keyboard.on('keyup-ENTER', this.selectCompanion.bind(this));
|
||||
|
||||
this.input.keyboard.on('keydown-RIGHT', this.moveToRight.bind(this));
|
||||
this.input.keyboard.on('keydown-LEFT', this.moveToLeft.bind(this));
|
||||
|
||||
if(localUserStore.getCompanion()){
|
||||
const companionIndex = this.companionModels.findIndex((companion) => companion.name === localUserStore.getCompanion());
|
||||
if(companionIndex > -1 || companionIndex < this.companions.length){
|
||||
this.currentCompanion = companionIndex;
|
||||
this.selectedCompanion = this.companions[companionIndex];
|
||||
}
|
||||
}
|
||||
|
||||
localUserStore.setCompanion(null);
|
||||
gameManager.setCompanion(null);
|
||||
|
||||
this.createCurrentCompanion();
|
||||
this.updateSelectedCompanion();
|
||||
}
|
||||
|
||||
update(time: number, delta: number): void {
|
||||
// pointerTimer is set to 250 when pointerdown events is trigger
|
||||
// After 250ms, pointerClicked is set to false and the pointerdown events can be trigger again
|
||||
this.pointerTimer -= delta;
|
||||
if (this.pointerTimer <= 0) {
|
||||
this.pointerClicked = false;
|
||||
}
|
||||
}
|
||||
|
||||
public selectCompanion(): void {
|
||||
localUserStore.setCompanion(this.companionModels[this.currentCompanion].name);
|
||||
gameManager.setCompanion(this.companionModels[this.currentCompanion].name);
|
||||
|
||||
this.closeScene();
|
||||
}
|
||||
|
||||
public closeScene(){
|
||||
// next scene
|
||||
this.scene.stop(SelectCompanionSceneName);
|
||||
waScaleManager.restoreZoom();
|
||||
gameManager.tryResumingGame(this, EnableCameraSceneName);
|
||||
this.scene.remove(SelectCompanionSceneName);
|
||||
selectCompanionSceneVisibleStore.set(false);
|
||||
}
|
||||
|
||||
private createCurrentCompanion(): void {
|
||||
for (let i = 0; i < this.companionModels.length; i++) {
|
||||
const companionResource = this.companionModels[i]
|
||||
const [middleX, middleY] = this.getCompanionPosition();
|
||||
const companion = this.physics.add.sprite(middleX, middleY, companionResource.name, 0);
|
||||
this.setUpCompanion(companion, i);
|
||||
this.anims.create({
|
||||
key: companionResource.name,
|
||||
frames: this.anims.generateFrameNumbers(companionResource.name, {start: 0, end: 2,}),
|
||||
frameRate: 10,
|
||||
repeat: -1
|
||||
});
|
||||
|
||||
companion.setInteractive().on("pointerdown", () => {
|
||||
if (this.pointerClicked) {
|
||||
return;
|
||||
}
|
||||
//To not trigger two time the pointerdown events :
|
||||
// We set a boolean to true so that pointerdown events does nothing when the boolean is true
|
||||
// We set a timer that we decrease in update function to not trigger the pointerdown events twice
|
||||
this.pointerClicked = true;
|
||||
this.pointerTimer = 250;
|
||||
this.currentCompanion = i;
|
||||
this.moveCompanion();
|
||||
});
|
||||
|
||||
this.companions.push(companion);
|
||||
}
|
||||
this.selectedCompanion = this.companions[this.currentCompanion];
|
||||
}
|
||||
|
||||
public onResize(): void {
|
||||
this.moveCompanion();
|
||||
}
|
||||
|
||||
private updateSelectedCompanion(): void {
|
||||
this.selectedCompanion?.anims.pause();
|
||||
const companion = this.companions[this.currentCompanion];
|
||||
companion.play(this.companionModels[this.currentCompanion].name);
|
||||
this.selectedCompanion = companion;
|
||||
}
|
||||
|
||||
private moveCompanion(){
|
||||
for(let i = 0; i < this.companions.length; i++){
|
||||
const companion = this.companions[i];
|
||||
this.setUpCompanion(companion, i);
|
||||
}
|
||||
this.updateSelectedCompanion();
|
||||
}
|
||||
|
||||
public moveToRight(){
|
||||
if(this.currentCompanion === (this.companions.length - 1)){
|
||||
return;
|
||||
}
|
||||
this.currentCompanion += 1;
|
||||
this.moveCompanion();
|
||||
}
|
||||
|
||||
public moveToLeft(){
|
||||
if(this.currentCompanion === 0){
|
||||
return;
|
||||
}
|
||||
this.currentCompanion -= 1;
|
||||
this.moveCompanion();
|
||||
}
|
||||
|
||||
private defineSetupCompanion(num: number){
|
||||
const deltaX = 30;
|
||||
const deltaY = 2;
|
||||
let [companionX, companionY] = this.getCompanionPosition();
|
||||
let companionVisible = true;
|
||||
let companionScale = 1.5;
|
||||
let companionOpactity = 1;
|
||||
if( this.currentCompanion !== num ){
|
||||
companionVisible = false;
|
||||
}
|
||||
if( num === (this.currentCompanion + 1) ){
|
||||
companionY -= deltaY;
|
||||
companionX += deltaX;
|
||||
companionScale = 0.8;
|
||||
companionOpactity = 0.6;
|
||||
companionVisible = true;
|
||||
}
|
||||
if( num === (this.currentCompanion + 2) ){
|
||||
companionY -= deltaY;
|
||||
companionX += (deltaX * 2);
|
||||
companionScale = 0.8;
|
||||
companionOpactity = 0.6;
|
||||
companionVisible = true;
|
||||
}
|
||||
if( num === (this.currentCompanion - 1) ){
|
||||
companionY -= deltaY;
|
||||
companionX -= deltaX;
|
||||
companionScale = 0.8;
|
||||
companionOpactity = 0.6;
|
||||
companionVisible = true;
|
||||
}
|
||||
if( num === (this.currentCompanion - 2) ){
|
||||
companionY -= deltaY;
|
||||
companionX -= (deltaX * 2);
|
||||
companionScale = 0.8;
|
||||
companionOpactity = 0.6;
|
||||
companionVisible = true;
|
||||
}
|
||||
return {companionX, companionY, companionScale, companionOpactity, companionVisible}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns pixel position by on column and row number
|
||||
*/
|
||||
private getCompanionPosition(): [number, number] {
|
||||
return [
|
||||
this.game.renderer.width / 2,
|
||||
this.game.renderer.height / 3
|
||||
];
|
||||
}
|
||||
|
||||
private setUpCompanion(companion: Phaser.Physics.Arcade.Sprite, numero: number){
|
||||
|
||||
const {companionX, companionY, companionScale, companionOpactity, companionVisible} = this.defineSetupCompanion(numero);
|
||||
companion.setBounce(0.2);
|
||||
companion.setCollideWorldBounds(true);
|
||||
companion.setVisible( companionVisible );
|
||||
companion.setScale(companionScale, companionScale);
|
||||
companion.setAlpha(companionOpactity);
|
||||
companion.setX(companionX);
|
||||
companion.setY(companionY);
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@ export interface ITiledMap {
|
|||
* Map orientation (orthogonal)
|
||||
*/
|
||||
orientation: string;
|
||||
properties: {[key: string]: string};
|
||||
properties?: ITiledMapLayerProperty[];
|
||||
|
||||
/**
|
||||
* Render order (right-down)
|
||||
|
@ -24,6 +24,11 @@ export interface ITiledMap {
|
|||
tilewidth: number;
|
||||
tilesets: ITiledTileSet[];
|
||||
version: number;
|
||||
compressionlevel?: number;
|
||||
infinite?: boolean;
|
||||
nextlayerid?: number;
|
||||
tiledversion?: string;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
export interface ITiledMapLayerProperty {
|
||||
|
@ -38,19 +43,35 @@ export interface ITiledMapLayerProperty {
|
|||
value: boolean
|
||||
}*/
|
||||
|
||||
export interface ITiledMapLayer {
|
||||
export type ITiledMapLayer = ITiledMapGroupLayer | ITiledMapObjectLayer | ITiledMapTileLayer;
|
||||
|
||||
export interface ITiledMapGroupLayer {
|
||||
id?: number,
|
||||
name: string;
|
||||
opacity: number;
|
||||
properties?: ITiledMapLayerProperty[];
|
||||
|
||||
type: "group";
|
||||
visible: boolean;
|
||||
x: number;
|
||||
y: number;
|
||||
/**
|
||||
* Layers for group layer
|
||||
*/
|
||||
layers: ITiledMapLayer[];
|
||||
}
|
||||
|
||||
export interface ITiledMapTileLayer {
|
||||
id?: number,
|
||||
data: number[]|string;
|
||||
height: number;
|
||||
name: string;
|
||||
opacity: number;
|
||||
properties: ITiledMapLayerProperty[];
|
||||
encoding: string;
|
||||
properties?: ITiledMapLayerProperty[];
|
||||
encoding?: string;
|
||||
compression?: string;
|
||||
|
||||
/**
|
||||
* Type of layer (tilelayer, objectgroup)
|
||||
*/
|
||||
type: string;
|
||||
type: "tilelayer";
|
||||
visible: boolean;
|
||||
width: number;
|
||||
x: number;
|
||||
|
@ -59,7 +80,28 @@ export interface ITiledMapLayer {
|
|||
/**
|
||||
* Draw order (topdown (default), index)
|
||||
*/
|
||||
draworder: string;
|
||||
draworder?: string;
|
||||
}
|
||||
|
||||
export interface ITiledMapObjectLayer {
|
||||
id?: number,
|
||||
height: number;
|
||||
name: string;
|
||||
opacity: number;
|
||||
properties?: ITiledMapLayerProperty[];
|
||||
encoding?: string;
|
||||
compression?: string;
|
||||
|
||||
type: "objectgroup";
|
||||
visible: boolean;
|
||||
width: number;
|
||||
x: number;
|
||||
y: number;
|
||||
|
||||
/**
|
||||
* Draw order (topdown (default), index)
|
||||
*/
|
||||
draworder?: string;
|
||||
objects: ITiledMapObject[];
|
||||
}
|
||||
|
||||
|
@ -94,6 +136,20 @@ export interface ITiledMapObject {
|
|||
* Polyline points
|
||||
*/
|
||||
polyline: {x: number, y: number}[];
|
||||
|
||||
text?: ITiledText
|
||||
}
|
||||
|
||||
export interface ITiledText {
|
||||
text: string,
|
||||
wrap?: boolean,
|
||||
fontfamily?: string,
|
||||
pixelsize?: number,
|
||||
color?: string,
|
||||
underline?: boolean,
|
||||
italic?: boolean,
|
||||
strikeout?: boolean,
|
||||
halign?: "center"|"right"|"justify"|"left"
|
||||
}
|
||||
|
||||
export interface ITiledTileSet {
|
||||
|
|
44
front/src/Phaser/Map/LayersIterator.ts
Normal file
44
front/src/Phaser/Map/LayersIterator.ts
Normal file
|
@ -0,0 +1,44 @@
|
|||
import type {ITiledMap, ITiledMapLayer} from "./ITiledMap";
|
||||
|
||||
/**
|
||||
* Iterates over the layers of a map, flattening the grouped layers
|
||||
*/
|
||||
export class LayersIterator implements IterableIterator<ITiledMapLayer> {
|
||||
|
||||
private layers: ITiledMapLayer[] = [];
|
||||
private pointer: number = 0;
|
||||
|
||||
constructor(private map: ITiledMap) {
|
||||
this.initLayersList(map.layers, '');
|
||||
}
|
||||
|
||||
private initLayersList(layers : ITiledMapLayer[], prefix : string) {
|
||||
for (const layer of layers) {
|
||||
if (layer.type === 'group') {
|
||||
this.initLayersList(layer.layers, prefix + layer.name + '/');
|
||||
} else {
|
||||
const layerWithNewName = { ...layer };
|
||||
layerWithNewName.name = prefix+layerWithNewName.name;
|
||||
this.layers.push(layerWithNewName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public next(): IteratorResult<ITiledMapLayer> {
|
||||
if (this.pointer < this.layers.length) {
|
||||
return {
|
||||
done: false,
|
||||
value: this.layers[this.pointer++]
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
done: true,
|
||||
value: null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Symbol.iterator](): IterableIterator<ITiledMapLayer> {
|
||||
return new LayersIterator(this.map);
|
||||
}
|
||||
}
|
|
@ -1,9 +1,16 @@
|
|||
import {LoginScene, LoginSceneName} from "../Login/LoginScene";
|
||||
import {SelectCharacterScene, SelectCharacterSceneName} from "../Login/SelectCharacterScene";
|
||||
import {SelectCompanionScene, SelectCompanionSceneName} from "../Login/SelectCompanionScene";
|
||||
import {gameManager} from "../Game/GameManager";
|
||||
import {localUserStore} from "../../Connexion/LocalUserStore";
|
||||
import {mediaManager} from "../../WebRtc/MediaManager";
|
||||
import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager";
|
||||
import {gameReportKey, gameReportRessource, ReportMenu} from "./ReportMenu";
|
||||
import {connectionManager} from "../../Connexion/ConnectionManager";
|
||||
import {GameConnexionTypes} from "../../Url/UrlManager";
|
||||
import {WarningContainer, warningContainerHtml, warningContainerKey} from "../Components/WarningContainer";
|
||||
import {worldFullWarningStream} from "../../Connexion/WorldFullWarningStream";
|
||||
import {menuIconVisible} from "../../Stores/MenuStore";
|
||||
import {videoConstraintStore} from "../../Stores/MediaStore";
|
||||
|
||||
export const MenuSceneName = 'MenuScene';
|
||||
const gameMenuKey = 'gameMenu';
|
||||
|
@ -11,7 +18,7 @@ const gameMenuIconKey = 'gameMenuIcon';
|
|||
const gameSettingsMenuKey = 'gameSettingsMenu';
|
||||
const gameShare = 'gameShare';
|
||||
|
||||
const closedSideMenuX = -200;
|
||||
const closedSideMenuX = -1000;
|
||||
const openedSideMenuX = 0;
|
||||
|
||||
/**
|
||||
|
@ -21,12 +28,15 @@ export class MenuScene extends Phaser.Scene {
|
|||
private menuElement!: Phaser.GameObjects.DOMElement;
|
||||
private gameQualityMenuElement!: Phaser.GameObjects.DOMElement;
|
||||
private gameShareElement!: Phaser.GameObjects.DOMElement;
|
||||
private gameReportElement!: ReportMenu;
|
||||
private sideMenuOpened = false;
|
||||
private settingsMenuOpened = false;
|
||||
private gameShareOpened = false;
|
||||
private gameQualityValue: number;
|
||||
private videoQualityValue: number;
|
||||
private menuButton!: Phaser.GameObjects.DOMElement;
|
||||
private warningContainer: WarningContainer | null = null;
|
||||
private warningContainerTimeout: NodeJS.Timeout | null = null;
|
||||
|
||||
constructor() {
|
||||
super({key: MenuSceneName});
|
||||
|
@ -40,20 +50,23 @@ export class MenuScene extends Phaser.Scene {
|
|||
this.load.html(gameMenuIconKey, 'resources/html/gameMenuIcon.html');
|
||||
this.load.html(gameSettingsMenuKey, 'resources/html/gameQualityMenu.html');
|
||||
this.load.html(gameShare, 'resources/html/gameShare.html');
|
||||
this.load.html(gameReportKey, gameReportRessource);
|
||||
this.load.html(warningContainerKey, warningContainerHtml);
|
||||
}
|
||||
|
||||
create() {
|
||||
menuIconVisible.set(true);
|
||||
this.menuElement = this.add.dom(closedSideMenuX, 30).createFromCache(gameMenuKey);
|
||||
this.menuElement.setOrigin(0);
|
||||
this.revealMenusAfterInit(this.menuElement, 'gameMenu');
|
||||
MenuScene.revealMenusAfterInit(this.menuElement, 'gameMenu');
|
||||
|
||||
const middleX = (window.innerWidth / 3) - 298;
|
||||
this.gameQualityMenuElement = this.add.dom(middleX, -400).createFromCache(gameSettingsMenuKey);
|
||||
this.revealMenusAfterInit(this.gameQualityMenuElement, 'gameQuality');
|
||||
MenuScene.revealMenusAfterInit(this.gameQualityMenuElement, 'gameQuality');
|
||||
|
||||
|
||||
this.gameShareElement = this.add.dom(middleX, -400).createFromCache(gameShare);
|
||||
this.revealMenusAfterInit(this.gameShareElement, gameShare);
|
||||
MenuScene.revealMenusAfterInit(this.gameShareElement, gameShare);
|
||||
this.gameShareElement.addListener('click');
|
||||
this.gameShareElement.on('click', (event:MouseEvent) => {
|
||||
event.preventDefault();
|
||||
|
@ -64,6 +77,12 @@ export class MenuScene extends Phaser.Scene {
|
|||
}
|
||||
});
|
||||
|
||||
this.gameReportElement = new ReportMenu(this, connectionManager.getConnexionType === GameConnexionTypes.anonymous);
|
||||
mediaManager.setShowReportModalCallBacks((userId, userName) => {
|
||||
this.closeAll();
|
||||
this.gameReportElement.open(parseInt(userId), userName);
|
||||
});
|
||||
|
||||
this.input.keyboard.on('keyup-TAB', () => {
|
||||
this.sideMenuOpened ? this.closeSideMenu() : this.openSideMenu();
|
||||
});
|
||||
|
@ -75,9 +94,12 @@ export class MenuScene extends Phaser.Scene {
|
|||
|
||||
this.menuElement.addListener('click');
|
||||
this.menuElement.on('click', this.onMenuClick.bind(this));
|
||||
|
||||
worldFullWarningStream.stream.subscribe(() => this.showWorldCapacityWarning());
|
||||
}
|
||||
|
||||
private revealMenusAfterInit(menuElement: Phaser.GameObjects.DOMElement, rootDomId: string) {
|
||||
//todo put this method in a parent menuElement class
|
||||
static revealMenusAfterInit(menuElement: Phaser.GameObjects.DOMElement, rootDomId: string) {
|
||||
//Dom elements will appear inside the viewer screen when creating before being moved out of it, which create a flicker effect.
|
||||
//To prevent this, we put a 'hidden' attribute on the root element, we remove it only after the init is done.
|
||||
setTimeout(() => {
|
||||
|
@ -86,7 +108,12 @@ export class MenuScene extends Phaser.Scene {
|
|||
}
|
||||
|
||||
public revealMenuIcon(): void {
|
||||
(this.menuButton.getChildByID('menuIcon') as HTMLElement).hidden = false
|
||||
//TODO fix me: add try catch because at the same time, 'this.menuButton' variable doesn't exist and there is error on 'getChildByID' function
|
||||
try {
|
||||
(this.menuButton.getChildByID('menuIcon') as HTMLElement).hidden = false;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
openSideMenu() {
|
||||
|
@ -94,10 +121,16 @@ export class MenuScene extends Phaser.Scene {
|
|||
this.closeAll();
|
||||
this.sideMenuOpened = true;
|
||||
this.menuButton.getChildByID('openMenuButton').innerHTML = 'X';
|
||||
if (gameManager.getCurrentGameScene(this).connection && gameManager.getCurrentGameScene(this).connection.isAdmin()) {
|
||||
const connection = gameManager.getCurrentGameScene(this).connection;
|
||||
if (connection && connection.isAdmin()) {
|
||||
const adminSection = this.menuElement.getChildByID('adminConsoleSection') as HTMLElement;
|
||||
adminSection.hidden = false;
|
||||
}
|
||||
//TODO bind with future metadata of card
|
||||
//if (connectionManager.getConnexionType === GameConnexionTypes.anonymous){
|
||||
const adminSection = this.menuElement.getChildByID('socialLinks') as HTMLElement;
|
||||
adminSection.hidden = false;
|
||||
//}
|
||||
this.tweens.add({
|
||||
targets: this.menuElement,
|
||||
x: openedSideMenuX,
|
||||
|
@ -106,10 +139,26 @@ export class MenuScene extends Phaser.Scene {
|
|||
});
|
||||
}
|
||||
|
||||
private showWorldCapacityWarning() {
|
||||
if (!this.warningContainer) {
|
||||
this.warningContainer = new WarningContainer(this);
|
||||
}
|
||||
if (this.warningContainerTimeout) {
|
||||
clearTimeout(this.warningContainerTimeout);
|
||||
}
|
||||
this.warningContainerTimeout = setTimeout(() => {
|
||||
this.warningContainer?.destroy();
|
||||
this.warningContainer = null
|
||||
this.warningContainerTimeout = null
|
||||
}, 120000);
|
||||
|
||||
}
|
||||
|
||||
private closeSideMenu(): void {
|
||||
if (!this.sideMenuOpened) return;
|
||||
this.sideMenuOpened = false;
|
||||
this.closeAll();
|
||||
this.menuButton.getChildByID('openMenuButton').innerHTML = `<img src="/static/images/menu.svg">`;
|
||||
gameManager.getCurrentGameScene(this).ConsoleGlobalMessageManager.disabledMessageConsole();
|
||||
this.tweens.add({
|
||||
targets: this.menuElement,
|
||||
|
@ -121,6 +170,7 @@ export class MenuScene extends Phaser.Scene {
|
|||
|
||||
private openGameSettingsMenu(): void {
|
||||
if (this.settingsMenuOpened) {
|
||||
this.closeGameQualityMenu();
|
||||
return;
|
||||
}
|
||||
//close all
|
||||
|
@ -145,11 +195,11 @@ export class MenuScene extends Phaser.Scene {
|
|||
}
|
||||
});
|
||||
|
||||
let middleY = (window.innerHeight / 3) - (257);
|
||||
let middleY = this.scale.height / 2 - 392/2;
|
||||
if(middleY < 0){
|
||||
middleY = 0;
|
||||
}
|
||||
let middleX = (window.innerWidth / 3) - 298;
|
||||
let middleX = this.scale.width / 2 - 457/2;
|
||||
if(middleX < 0){
|
||||
middleX = 0;
|
||||
}
|
||||
|
@ -189,11 +239,11 @@ export class MenuScene extends Phaser.Scene {
|
|||
|
||||
this.gameShareOpened = true;
|
||||
|
||||
let middleY = (window.innerHeight / 3) - (257);
|
||||
let middleY = this.scale.height / 2 - 85;
|
||||
if(middleY < 0){
|
||||
middleY = 0;
|
||||
}
|
||||
let middleX = (window.innerWidth / 3) - 298;
|
||||
let middleX = this.scale.width / 2 - 200;
|
||||
if(middleX < 0){
|
||||
middleX = 0;
|
||||
}
|
||||
|
@ -220,6 +270,9 @@ export class MenuScene extends Phaser.Scene {
|
|||
}
|
||||
|
||||
private onMenuClick(event:MouseEvent) {
|
||||
if((event?.target as HTMLInputElement).classList.contains('not-button')){
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
|
||||
switch ((event?.target as HTMLInputElement).id) {
|
||||
|
@ -234,6 +287,10 @@ export class MenuScene extends Phaser.Scene {
|
|||
this.closeSideMenu();
|
||||
gameManager.leaveGame(this, SelectCharacterSceneName, new SelectCharacterScene());
|
||||
break;
|
||||
case 'changeCompanionButton':
|
||||
this.closeSideMenu();
|
||||
gameManager.leaveGame(this, SelectCompanionSceneName, new SelectCompanionScene());
|
||||
break;
|
||||
case 'closeButton':
|
||||
this.closeSideMenu();
|
||||
break;
|
||||
|
@ -243,6 +300,9 @@ export class MenuScene extends Phaser.Scene {
|
|||
case 'editGameSettingsButton':
|
||||
this.openGameSettingsMenu();
|
||||
break;
|
||||
case 'toggleFullscreen':
|
||||
this.toggleFullscreen();
|
||||
break;
|
||||
case 'adminConsoleButton':
|
||||
gameManager.getCurrentGameScene(this).ConsoleGlobalMessageManager.activeMessageConsole();
|
||||
break;
|
||||
|
@ -265,19 +325,36 @@ export class MenuScene extends Phaser.Scene {
|
|||
if (valueVideo !== this.videoQualityValue) {
|
||||
this.videoQualityValue = valueVideo;
|
||||
localUserStore.setVideoQualityValue(valueVideo);
|
||||
mediaManager.updateCameraQuality(valueVideo);
|
||||
videoConstraintStore.setFrameRate(valueVideo);
|
||||
}
|
||||
this.closeGameQualityMenu();
|
||||
}
|
||||
|
||||
private gotToCreateMapPage() {
|
||||
const sparkHost = 'https://'+window.location.host.replace('play.', '')+'/choose-map.html';
|
||||
//const sparkHost = 'https://'+window.location.host.replace('play.', '')+'/choose-map.html';
|
||||
//TODO fix me: this button can to send us on WorkAdventure BO.
|
||||
const sparkHost = 'https://workadventu.re/getting-started';
|
||||
window.open(sparkHost, '_blank');
|
||||
}
|
||||
|
||||
private closeAll(){
|
||||
this.closeGameQualityMenu();
|
||||
this.closeGameShare();
|
||||
this.menuButton.getChildByID('openMenuButton').innerHTML = `<img src="/static/images/menu.svg">`;
|
||||
this.gameReportElement.close();
|
||||
}
|
||||
|
||||
private toggleFullscreen() {
|
||||
const body = document.querySelector('body')
|
||||
if (body) {
|
||||
if (document.fullscreenElement ?? document.fullscreen) {
|
||||
document.exitFullscreen()
|
||||
} else {
|
||||
body.requestFullscreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public isDirty(): boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
118
front/src/Phaser/Menu/ReportMenu.ts
Normal file
118
front/src/Phaser/Menu/ReportMenu.ts
Normal file
|
@ -0,0 +1,118 @@
|
|||
import {MenuScene} from "./MenuScene";
|
||||
import {gameManager} from "../Game/GameManager";
|
||||
import {blackListManager} from "../../WebRtc/BlackListManager";
|
||||
|
||||
export const gameReportKey = 'gameReport';
|
||||
export const gameReportRessource = 'resources/html/gameReport.html';
|
||||
|
||||
export class ReportMenu extends Phaser.GameObjects.DOMElement {
|
||||
private opened: boolean = false;
|
||||
|
||||
private userId!: number;
|
||||
private userName!: string|undefined;
|
||||
private anonymous: boolean;
|
||||
|
||||
constructor(scene: Phaser.Scene, anonymous: boolean) {
|
||||
super(scene, -2000, -2000);
|
||||
this.anonymous = anonymous;
|
||||
this.createFromCache(gameReportKey);
|
||||
|
||||
if (this.anonymous) {
|
||||
const divToHide = this.getChildByID('reportSection') as HTMLElement;
|
||||
divToHide.hidden = true;
|
||||
const textToHide = this.getChildByID('askActionP') as HTMLElement;
|
||||
textToHide.hidden = true;
|
||||
}
|
||||
|
||||
scene.add.existing(this);
|
||||
MenuScene.revealMenusAfterInit(this, gameReportKey);
|
||||
|
||||
this.addListener('click');
|
||||
this.on('click', (event:MouseEvent) => {
|
||||
event.preventDefault();
|
||||
if ((event?.target as HTMLInputElement).id === 'gameReportFormSubmit') {
|
||||
this.submitReport();
|
||||
} else if((event?.target as HTMLInputElement).id === 'gameReportFormCancel') {
|
||||
this.close();
|
||||
} else if((event?.target as HTMLInputElement).id === 'toggleBlockButton') {
|
||||
this.toggleBlock();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public open(userId: number, userName: string|undefined): void {
|
||||
if (this.opened) {
|
||||
this.close();
|
||||
return;
|
||||
}
|
||||
|
||||
this.userId = userId;
|
||||
this.userName = userName;
|
||||
|
||||
const mainEl = this.getChildByID('gameReport') as HTMLElement;
|
||||
this.x = this.getCenteredX(mainEl);
|
||||
this.y = this.getHiddenY(mainEl);
|
||||
|
||||
const gameTitleReport = this.getChildByID('nameReported') as HTMLElement;
|
||||
gameTitleReport.innerText = userName || '';
|
||||
|
||||
const blockButton = this.getChildByID('toggleBlockButton') as HTMLElement;
|
||||
blockButton.innerText = blackListManager.isBlackListed(this.userId) ? 'Unblock this user' : 'Block this user';
|
||||
|
||||
this.opened = true;
|
||||
|
||||
gameManager.getCurrentGameScene(this.scene).userInputManager.disableControls();
|
||||
|
||||
this.scene.tweens.add({
|
||||
targets: this,
|
||||
y: this.getCenteredY(mainEl),
|
||||
duration: 1000,
|
||||
ease: 'Power3'
|
||||
});
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
gameManager.getCurrentGameScene(this.scene).userInputManager.restoreControls();
|
||||
this.opened = false;
|
||||
const mainEl = this.getChildByID('gameReport') as HTMLElement;
|
||||
this.scene.tweens.add({
|
||||
targets: this,
|
||||
y: this.getHiddenY(mainEl),
|
||||
duration: 1000,
|
||||
ease: 'Power3'
|
||||
});
|
||||
}
|
||||
|
||||
//todo: into a parent class?
|
||||
private getCenteredX(mainEl: HTMLElement): number {
|
||||
return window.innerWidth / 4 - mainEl.clientWidth / 2;
|
||||
}
|
||||
private getHiddenY(mainEl: HTMLElement): number {
|
||||
return - mainEl.clientHeight - 50;
|
||||
}
|
||||
private getCenteredY(mainEl: HTMLElement): number {
|
||||
return window.innerHeight / 4 - mainEl.clientHeight / 2;
|
||||
}
|
||||
|
||||
private toggleBlock(): void {
|
||||
!blackListManager.isBlackListed(this.userId) ? blackListManager.blackList(this.userId) : blackListManager.cancelBlackList(this.userId);
|
||||
this.close();
|
||||
}
|
||||
|
||||
private submitReport(): void{
|
||||
const gamePError = this.getChildByID('gameReportErr') as HTMLParagraphElement;
|
||||
gamePError.innerText = '';
|
||||
gamePError.style.display = 'none';
|
||||
const gameTextArea = this.getChildByID('gameReportInput') as HTMLInputElement;
|
||||
if(!gameTextArea || !gameTextArea.value){
|
||||
gamePError.innerText = 'Report message cannot to be empty.';
|
||||
gamePError.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
gameManager.getCurrentGameScene(this.scene).connection?.emitReportPlayerMessage(
|
||||
this.userId,
|
||||
gameTextArea.value
|
||||
);
|
||||
this.close();
|
||||
}
|
||||
}
|
|
@ -1,9 +1,13 @@
|
|||
|
||||
export enum PlayerAnimationNames {
|
||||
WalkDown = 'down',
|
||||
WalkLeft = 'left',
|
||||
WalkUp = 'up',
|
||||
WalkRight = 'right',
|
||||
export enum PlayerAnimationDirections {
|
||||
Down = 'down',
|
||||
Left = 'left',
|
||||
Up = 'up',
|
||||
Right = 'right',
|
||||
}
|
||||
export enum PlayerAnimationTypes {
|
||||
Walk = 'walk',
|
||||
Idle = 'idle',
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,33 +1,43 @@
|
|||
import {PlayerAnimationNames} from "./Animation";
|
||||
import {GameScene} from "../Game/GameScene";
|
||||
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 {RadialMenu, RadialMenuClickEvent, RadialMenuItem} from "../Components/RadialMenu";
|
||||
|
||||
export const hasMovedEventName = "hasMoved";
|
||||
export interface CurrentGamerInterface extends Character{
|
||||
moveUser(delta: number) : void;
|
||||
say(text : string) : void;
|
||||
}
|
||||
export const requestEmoteEventName = "requestEmote";
|
||||
|
||||
export class Player extends Character implements CurrentGamerInterface {
|
||||
private previousDirection: string = PlayerAnimationNames.WalkDown;
|
||||
export class Player extends Character {
|
||||
private previousDirection: string = PlayerAnimationDirections.Down;
|
||||
private wasMoving: boolean = false;
|
||||
private emoteMenu: RadialMenu|null = null;
|
||||
private updateListener: () => void;
|
||||
|
||||
constructor(
|
||||
Scene: GameScene,
|
||||
x: number,
|
||||
y: number,
|
||||
name: string,
|
||||
PlayerTextures: string[],
|
||||
direction: string,
|
||||
texturesPromise: Promise<string[]>,
|
||||
direction: PlayerAnimationDirections,
|
||||
moving: boolean,
|
||||
private userInputManager: UserInputManager
|
||||
private userInputManager: UserInputManager,
|
||||
companion: string|null,
|
||||
companionTexturePromise?: Promise<string>
|
||||
) {
|
||||
super(Scene, x, y, PlayerTextures, name, direction, moving, 1);
|
||||
super(Scene, x, y, texturesPromise, name, direction, moving, 1, true, companion, companionTexturePromise);
|
||||
|
||||
//the current player model should be push away by other players to prevent conflict
|
||||
this.getBody().setImmovable(false);
|
||||
|
||||
this.updateListener = () => {
|
||||
if (this.emoteMenu) {
|
||||
this.emoteMenu.x = this.x;
|
||||
this.emoteMenu.y = this.y;
|
||||
}
|
||||
};
|
||||
this.scene.events.addListener('postupdate', this.updateListener);
|
||||
}
|
||||
|
||||
moveUser(delta: number): void {
|
||||
|
@ -42,37 +52,74 @@ export class Player extends Character implements CurrentGamerInterface {
|
|||
let x = 0;
|
||||
let y = 0;
|
||||
if (activeEvents.get(UserInputEvent.MoveUp)) {
|
||||
y = - moveAmount;
|
||||
direction = PlayerAnimationNames.WalkUp;
|
||||
y = -moveAmount;
|
||||
direction = PlayerAnimationDirections.Up;
|
||||
moving = true;
|
||||
} else if (activeEvents.get(UserInputEvent.MoveDown)) {
|
||||
y = moveAmount;
|
||||
direction = PlayerAnimationNames.WalkDown;
|
||||
direction = PlayerAnimationDirections.Down;
|
||||
moving = true;
|
||||
}
|
||||
if (activeEvents.get(UserInputEvent.MoveLeft)) {
|
||||
x = -moveAmount;
|
||||
direction = PlayerAnimationNames.WalkLeft;
|
||||
direction = PlayerAnimationDirections.Left;
|
||||
moving = true;
|
||||
} else if (activeEvents.get(UserInputEvent.MoveRight)) {
|
||||
x = moveAmount;
|
||||
direction = PlayerAnimationNames.WalkRight;
|
||||
direction = PlayerAnimationDirections.Right;
|
||||
moving = true;
|
||||
}
|
||||
moving = moving || activeEvents.get(UserInputEvent.JoystickMove);
|
||||
|
||||
if (x !== 0 || y !== 0) {
|
||||
this.move(x, y);
|
||||
this.emit(hasMovedEventName, {moving, direction, x: this.x, y: this.y});
|
||||
} else {
|
||||
if (this.wasMoving) {
|
||||
//direction = PlayerAnimationNames.None;
|
||||
this.stop();
|
||||
this.emit(hasMovedEventName, {moving, direction: this.previousDirection, x: this.x, y: this.y});
|
||||
}
|
||||
} else if (this.wasMoving && moving) {
|
||||
// slow joystick movement
|
||||
this.move(0, 0);
|
||||
this.emit(hasMovedEventName, {moving, direction: this.previousDirection, x: this.x, y: this.y});
|
||||
} else if (this.wasMoving && !moving) {
|
||||
this.stop();
|
||||
this.emit(hasMovedEventName, {moving, direction: this.previousDirection, x: this.x, y: this.y});
|
||||
}
|
||||
|
||||
if (direction !== null) {
|
||||
this.previousDirection = direction;
|
||||
}
|
||||
this.wasMoving = moving;
|
||||
userMovingStore.set(moving);
|
||||
}
|
||||
|
||||
public isMoving(): boolean {
|
||||
return this.wasMoving;
|
||||
}
|
||||
|
||||
openOrCloseEmoteMenu(emotes:RadialMenuItem[]) {
|
||||
if(this.emoteMenu) {
|
||||
this.closeEmoteMenu();
|
||||
} else {
|
||||
this.openEmoteMenu(emotes);
|
||||
}
|
||||
}
|
||||
|
||||
openEmoteMenu(emotes:RadialMenuItem[]): void {
|
||||
this.cancelPreviousEmote();
|
||||
this.emoteMenu = new RadialMenu(this.scene, this.x, this.y, emotes)
|
||||
this.emoteMenu.on(RadialMenuClickEvent, (item: RadialMenuItem) => {
|
||||
this.closeEmoteMenu();
|
||||
this.emit(requestEmoteEventName, item.name);
|
||||
this.playEmote(item.name);
|
||||
});
|
||||
}
|
||||
|
||||
closeEmoteMenu(): void {
|
||||
if (!this.emoteMenu) return;
|
||||
this.emoteMenu.destroy();
|
||||
this.emoteMenu = null;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.scene.events.removeListener('postupdate', this.updateListener);
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
|
|
120
front/src/Phaser/Reconnecting/ErrorScene.ts
Normal file
120
front/src/Phaser/Reconnecting/ErrorScene.ts
Normal file
|
@ -0,0 +1,120 @@
|
|||
import {TextField} from "../Components/TextField";
|
||||
import Image = Phaser.GameObjects.Image;
|
||||
import Sprite = Phaser.GameObjects.Sprite;
|
||||
import Text = Phaser.GameObjects.Text;
|
||||
import ScenePlugin = Phaser.Scenes.ScenePlugin;
|
||||
import {WAError} from "./WAError";
|
||||
|
||||
export const ErrorSceneName = "ErrorScene";
|
||||
enum Textures {
|
||||
icon = "icon",
|
||||
mainFont = "main_font"
|
||||
}
|
||||
|
||||
export class ErrorScene extends Phaser.Scene {
|
||||
private titleField!: TextField;
|
||||
private subTitleField!: TextField;
|
||||
private messageField!: Text;
|
||||
private logo!: Image;
|
||||
private cat!: Sprite;
|
||||
private title!: string;
|
||||
private subTitle!: string;
|
||||
private message!: string;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
key: ErrorSceneName
|
||||
});
|
||||
}
|
||||
|
||||
init({title, subTitle, message}: { title?: string, subTitle?: string, message?: string }) {
|
||||
this.title = title ? title : '';
|
||||
this.subTitle = subTitle ? subTitle : '';
|
||||
this.message = message ? message : '';
|
||||
}
|
||||
|
||||
preload() {
|
||||
this.load.image(Textures.icon, "resources/logos/tcm_full.png");
|
||||
// Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap
|
||||
this.load.bitmapFont(Textures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
|
||||
this.load.spritesheet(
|
||||
'cat',
|
||||
'resources/characters/pipoya/Cat 01-1.png',
|
||||
{frameWidth: 32, frameHeight: 32}
|
||||
);
|
||||
}
|
||||
|
||||
create() {
|
||||
this.logo = new Image(this, this.game.renderer.width - 30, this.game.renderer.height - 20, Textures.icon);
|
||||
this.add.existing(this.logo);
|
||||
|
||||
this.titleField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height / 2, this.title);
|
||||
|
||||
this.subTitleField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height / 2 + 24, this.subTitle);
|
||||
|
||||
this.messageField = this.add.text(this.game.renderer.width / 2, this.game.renderer.height / 2 + 48, this.message, {
|
||||
fontFamily: 'Georgia, "Goudy Bookletter 1911", Times, serif',
|
||||
fontSize: '10px'
|
||||
});
|
||||
this.messageField.setOrigin(0.5, 0.5);
|
||||
|
||||
this.cat = this.physics.add.sprite(this.game.renderer.width / 2, this.game.renderer.height / 2 - 32, 'cat', 6);
|
||||
this.cat.flipY = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the error page, with an error message matching the "error" parameters passed in.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public static showError(error: any, scene: ScenePlugin): void {
|
||||
console.error(error);
|
||||
|
||||
if (typeof error === 'string' || error instanceof String) {
|
||||
scene.start(ErrorSceneName, {
|
||||
title: 'An error occurred',
|
||||
subTitle: error
|
||||
});
|
||||
} else if (error instanceof WAError) {
|
||||
scene.start(ErrorSceneName, {
|
||||
title: error.title,
|
||||
subTitle: error.subTitle,
|
||||
message: error.details
|
||||
});
|
||||
} else if (error.response) {
|
||||
// Axios HTTP error
|
||||
// client received an error response (5xx, 4xx)
|
||||
scene.start(ErrorSceneName, {
|
||||
title: 'HTTP ' + error.response.status + ' - ' + error.response.statusText,
|
||||
subTitle: 'An error occurred while accessing URL:',
|
||||
message: error.response.config.url
|
||||
});
|
||||
} else if (error.request) {
|
||||
// Axios HTTP error
|
||||
// client never received a response, or request never left
|
||||
scene.start(ErrorSceneName, {
|
||||
title: 'Network error',
|
||||
subTitle: error.message
|
||||
});
|
||||
} else if (error instanceof Error) {
|
||||
// Error
|
||||
scene.start(ErrorSceneName, {
|
||||
title: 'An error occurred',
|
||||
subTitle: error.name,
|
||||
message: error.message
|
||||
});
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the error page, with an error message matching the "error" parameters passed in.
|
||||
*/
|
||||
public static startErrorPage(title: string, subTitle: string, message: string, scene: ScenePlugin): void {
|
||||
scene.start(ErrorSceneName, {
|
||||
title,
|
||||
subTitle,
|
||||
message
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
import {TextField} from "../Components/TextField";
|
||||
import Image = Phaser.GameObjects.Image;
|
||||
import Sprite = Phaser.GameObjects.Sprite;
|
||||
import Text = Phaser.GameObjects.Text;
|
||||
|
||||
export const FourOFourSceneName = "FourOFourScene";
|
||||
enum Textures {
|
||||
icon = "icon",
|
||||
mainFont = "main_font"
|
||||
}
|
||||
|
||||
export class FourOFourScene extends Phaser.Scene {
|
||||
private mapNotFoundField!: TextField;
|
||||
private couldNotFindField!: TextField;
|
||||
private fileNameField!: Text;
|
||||
private logo!: Image;
|
||||
private cat!: Sprite;
|
||||
private file: string|undefined;
|
||||
private url: string|undefined;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
key: FourOFourSceneName
|
||||
});
|
||||
}
|
||||
|
||||
init({ file, url }: { file?: string, url?: string }) {
|
||||
this.file = file;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
preload() {
|
||||
this.load.image(Textures.icon, "resources/logos/tcm_full.png");
|
||||
// Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap
|
||||
this.load.bitmapFont(Textures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
|
||||
this.load.spritesheet(
|
||||
'cat',
|
||||
'resources/characters/pipoya/Cat 01-1.png',
|
||||
{frameWidth: 32, frameHeight: 32}
|
||||
);
|
||||
}
|
||||
|
||||
create() {
|
||||
this.logo = new Image(this, this.game.renderer.width - 30, this.game.renderer.height - 20, Textures.icon);
|
||||
this.add.existing(this.logo);
|
||||
|
||||
this.mapNotFoundField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height / 2, "404 - File not found");
|
||||
|
||||
let text: string = '';
|
||||
if (this.file !== undefined) {
|
||||
text = "Could not load map"
|
||||
}
|
||||
if (this.url !== undefined) {
|
||||
text = "Invalid URL"
|
||||
}
|
||||
|
||||
this.couldNotFindField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height / 2 + 24, text);
|
||||
|
||||
const url = this.file ? this.file : this.url;
|
||||
if (url !== undefined) {
|
||||
this.fileNameField = this.add.text(this.game.renderer.width / 2, this.game.renderer.height / 2 + 38, url, { fontFamily: 'Georgia, "Goudy Bookletter 1911", Times, serif', fontSize: '10px' });
|
||||
this.fileNameField.setOrigin(0.5, 0.5);
|
||||
}
|
||||
|
||||
this.cat = this.physics.add.sprite(this.game.renderer.width / 2, this.game.renderer.height / 2 - 32, 'cat', 6);
|
||||
this.cat.flipY=true;
|
||||
}
|
||||
}
|
26
front/src/Phaser/Reconnecting/WAError.ts
Normal file
26
front/src/Phaser/Reconnecting/WAError.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
export class WAError extends Error {
|
||||
private _title: string;
|
||||
private _subTitle: string;
|
||||
private _details: string;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
get title(): string {
|
||||
return this._title;
|
||||
}
|
||||
|
||||
get subTitle(): string {
|
||||
return this._subTitle;
|
||||
}
|
||||
|
||||
get details(): string {
|
||||
return this._details;
|
||||
}
|
||||
}
|
111
front/src/Phaser/Services/HdpiManager.ts
Normal file
111
front/src/Phaser/Services/HdpiManager.ts
Normal file
|
@ -0,0 +1,111 @@
|
|||
|
||||
interface Size {
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
export class HdpiManager {
|
||||
private _zoomModifier: number = 1;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param minRecommendedGamePixelsNumber The minimum number of pixels we want to display "by default" to the user
|
||||
* @param absoluteMinPixelNumber The very minimum of game pixels to display. Below, we forbid zooming more
|
||||
*/
|
||||
public constructor(private minRecommendedGamePixelsNumber: number, private absoluteMinPixelNumber: number) {}
|
||||
|
||||
/**
|
||||
* Returns the optimal size in "game pixels" based on the screen size in "real pixels".
|
||||
*
|
||||
* Note: the function is returning the optimal size in "game pixels" in the "game" property,
|
||||
* but also recommends resizing the "real" pixel screen size of the canvas.
|
||||
* The proposed new real size is a few pixels bigger than the real size available (if the size is not a multiple of the pixel size) and should overflow.
|
||||
*
|
||||
* @param realPixelScreenSize
|
||||
*/
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
const optimalZoomLevel = this.getOptimalZoomLevel(realPixelNumber);
|
||||
|
||||
// Has the canvas more pixels than the screen? This is forbidden
|
||||
if (optimalZoomLevel * this._zoomModifier < 1) {
|
||||
// Let's reset the zoom modifier (WARNING this is a SIDE EFFECT in a getter)
|
||||
this._zoomModifier = 1 / optimalZoomLevel;
|
||||
|
||||
return {
|
||||
game: {
|
||||
width: realPixelScreenSize.width,
|
||||
height: realPixelScreenSize.height,
|
||||
},
|
||||
real: {
|
||||
width: realPixelScreenSize.width,
|
||||
height: realPixelScreenSize.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const gameWidth = Math.ceil(realPixelScreenSize.width / optimalZoomLevel / this._zoomModifier);
|
||||
const gameHeight = Math.ceil(realPixelScreenSize.height / optimalZoomLevel / this._zoomModifier);
|
||||
|
||||
// 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);
|
||||
|
||||
// Let's reset the zoom modifier (WARNING this is a SIDE EFFECT in a getter)
|
||||
this._zoomModifier = realPixelScreenSize.width / minGameWidth / optimalZoomLevel;
|
||||
|
||||
return {
|
||||
game: {
|
||||
width: minGameWidth,
|
||||
height: minGameHeight,
|
||||
},
|
||||
real: {
|
||||
width: realPixelScreenSize.width,
|
||||
height: realPixelScreenSize.height,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
game: {
|
||||
width: gameWidth,
|
||||
height: gameHeight,
|
||||
},
|
||||
real: {
|
||||
width: Math.ceil(realPixelScreenSize.width / optimalZoomLevel) * optimalZoomLevel,
|
||||
height: Math.ceil(realPixelScreenSize.height / optimalZoomLevel) * optimalZoomLevel,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We only accept integer but we make an exception for 1.5
|
||||
*/
|
||||
private getOptimalZoomLevel(realPixelNumber: number): number {
|
||||
const result = Math.sqrt(realPixelNumber / this.minRecommendedGamePixelsNumber);
|
||||
if (1.5 <= result && result < 2) {
|
||||
return 1.5
|
||||
} else {
|
||||
return Math.floor(result);
|
||||
}
|
||||
}
|
||||
|
||||
public get zoomModifier(): number {
|
||||
return this._zoomModifier;
|
||||
}
|
||||
|
||||
public set zoomModifier(zoomModifier: number) {
|
||||
this._zoomModifier = zoomModifier;
|
||||
}
|
||||
}
|
87
front/src/Phaser/Services/WaScaleManager.ts
Normal file
87
front/src/Phaser/Services/WaScaleManager.ts
Normal file
|
@ -0,0 +1,87 @@
|
|||
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";
|
||||
|
||||
|
||||
class WaScaleManager {
|
||||
private hdpiManager: HdpiManager;
|
||||
private scaleManager!: ScaleManager;
|
||||
private game!: Game;
|
||||
private actualZoom: number = 1;
|
||||
private _saveZoom: number = 1;
|
||||
|
||||
public constructor(private minGamePixelsNumber: number, private absoluteMinPixelNumber: number) {
|
||||
this.hdpiManager = new HdpiManager(minGamePixelsNumber, absoluteMinPixelNumber);
|
||||
}
|
||||
|
||||
public setGame(game: Game): void {
|
||||
this.scaleManager = game.scale;
|
||||
this.game = game;
|
||||
}
|
||||
|
||||
public applyNewSize() {
|
||||
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});
|
||||
|
||||
this.actualZoom = 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';
|
||||
|
||||
// Resize the game element at the same size at the canvas
|
||||
const gameStyle = HtmlUtils.getElementByIdOrFail<HTMLDivElement>('game').style;
|
||||
gameStyle.width = style.width;
|
||||
gameStyle.height = style.height;
|
||||
|
||||
// Note: onResize will be called twice (once here and once in Game.ts), but we have no better way.
|
||||
for (const scene of this.game.scene.getScenes(true)) {
|
||||
if (scene instanceof ResizableScene) {
|
||||
// We are delaying the call to the "render" event because otherwise, the "camera" coordinates are not correctly updated.
|
||||
scene.events.once(Phaser.Scenes.Events.RENDER, () => scene.onResize());
|
||||
}
|
||||
}
|
||||
|
||||
this.game.markDirty();
|
||||
}
|
||||
|
||||
public get zoomModifier(): number {
|
||||
return this.hdpiManager.zoomModifier;
|
||||
}
|
||||
|
||||
public set zoomModifier(zoomModifier: number) {
|
||||
this.hdpiManager.zoomModifier = zoomModifier;
|
||||
this.applyNewSize();
|
||||
}
|
||||
|
||||
public saveZoom(): void {
|
||||
this._saveZoom = this.hdpiManager.zoomModifier;
|
||||
}
|
||||
|
||||
public restoreZoom(): void{
|
||||
this.hdpiManager.zoomModifier = this._saveZoom;
|
||||
this.applyNewSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used to scale back the ui components to counter-act the zoom.
|
||||
*/
|
||||
public get uiScalingFactor(): number {
|
||||
return this.actualZoom > 1 ? 1 : 1.2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const waScaleManager = new WaScaleManager(640*480, 196*196);
|
41
front/src/Phaser/UserInput/PinchManager.ts
Normal file
41
front/src/Phaser/UserInput/PinchManager.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
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;
|
||||
private pinch!: any; // eslint-disable-line
|
||||
|
||||
constructor(scene: Phaser.Scene) {
|
||||
this.scene = scene;
|
||||
this.pinch = new Pinch(scene);
|
||||
this.pinch.setDragThreshold(10);
|
||||
|
||||
// The "pinch.scaleFactor" value is very sensitive and causes the screen to flicker.
|
||||
// We are smoothing its value with previous values to prevent the flicking.
|
||||
let smoothPinch = 1;
|
||||
|
||||
this.pinch.on('pinchstart', () => {
|
||||
smoothPinch = 1;
|
||||
});
|
||||
|
||||
|
||||
this.pinch.on('pinch', (pinch:any) => { // eslint-disable-line
|
||||
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;
|
||||
if (this.scene instanceof GameScene) {
|
||||
this.scene.zoomByFactor(smoothPinch);
|
||||
} else {
|
||||
waScaleManager.zoomModifier *= smoothPinch;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.pinch.removeAllListeners();
|
||||
}
|
||||
}
|
|
@ -1,4 +1,7 @@
|
|||
import {GameScene} from "../Game/GameScene";
|
||||
import type { Direction } from "../../types";
|
||||
import type {GameScene} from "../Game/GameScene";
|
||||
import {touchScreenManager} from "../../Touch/TouchScreenManager";
|
||||
import {MobileJoystick} from "../Components/MobileJoystick";
|
||||
|
||||
interface UserInputManagerDatum {
|
||||
keyInstance: Phaser.Input.Keyboard.Key;
|
||||
|
@ -13,17 +16,25 @@ export enum UserInputEvent {
|
|||
SpeedUp,
|
||||
Interact,
|
||||
Shout,
|
||||
JoystickMove,
|
||||
}
|
||||
|
||||
//we cannot the map structure so we have to create a replacment
|
||||
|
||||
//we cannot use a map structure so we have to create a replacment
|
||||
export class ActiveEventList {
|
||||
private KeysCode : Map<UserInputEvent, boolean> = new Map<UserInputEvent, boolean>();
|
||||
private eventMap : Map<UserInputEvent, boolean> = new Map<UserInputEvent, boolean>();
|
||||
|
||||
get(event: UserInputEvent): boolean {
|
||||
return this.KeysCode.get(event) || false;
|
||||
return this.eventMap.get(event) || false;
|
||||
}
|
||||
set(event: UserInputEvent, value: boolean): void {
|
||||
this.KeysCode.set(event, value);
|
||||
this.eventMap.set(event, value);
|
||||
}
|
||||
forEach(callback: (value: boolean, key: UserInputEvent) => void): void {
|
||||
this.eventMap.forEach(callback);
|
||||
}
|
||||
any(): boolean {
|
||||
return Array.from(this.eventMap.values()).reduce((accu, curr) => accu || curr, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,10 +42,48 @@ export class ActiveEventList {
|
|||
export class UserInputManager {
|
||||
private KeysCode!: UserInputManagerDatum[];
|
||||
private Scene: GameScene;
|
||||
private isInputDisabled : boolean;
|
||||
|
||||
constructor(Scene : GameScene) {
|
||||
private joystick!: MobileJoystick;
|
||||
private joystickEvents = new ActiveEventList();
|
||||
private joystickForceThreshold = 60;
|
||||
private joystickForceAccuX = 0;
|
||||
private joystickForceAccuY = 0;
|
||||
|
||||
constructor(Scene: GameScene) {
|
||||
this.Scene = Scene;
|
||||
this.isInputDisabled = false;
|
||||
this.initKeyBoardEvent();
|
||||
this.initMouseWheel();
|
||||
if (touchScreenManager.supportTouchScreen) {
|
||||
this.initVirtualJoystick();
|
||||
}
|
||||
}
|
||||
|
||||
initVirtualJoystick() {
|
||||
this.joystick = new MobileJoystick(this.Scene);
|
||||
this.joystick.on("update", () => {
|
||||
this.joystickForceAccuX = this.joystick.forceX ? this.joystickForceAccuX : 0;
|
||||
this.joystickForceAccuY = this.joystick.forceY ? this.joystickForceAccuY : 0;
|
||||
const cursorKeys = this.joystick.createCursorKeys();
|
||||
for (const name in cursorKeys) {
|
||||
const key = cursorKeys[name as Direction];
|
||||
switch (name) {
|
||||
case "up":
|
||||
this.joystickEvents.set(UserInputEvent.MoveUp, key.isDown);
|
||||
break;
|
||||
case "left":
|
||||
this.joystickEvents.set(UserInputEvent.MoveLeft, key.isDown);
|
||||
break;
|
||||
case "down":
|
||||
this.joystickEvents.set(UserInputEvent.MoveDown, key.isDown);
|
||||
break;
|
||||
case "right":
|
||||
this.joystickEvents.set(UserInputEvent.MoveRight, key.isDown);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initKeyBoardEvent(){
|
||||
|
@ -59,14 +108,50 @@ export class UserInputManager {
|
|||
];
|
||||
}
|
||||
|
||||
clearAllInputKeyboard(){
|
||||
this.Scene.input.keyboard.removeAllKeys();
|
||||
clearAllListeners(){
|
||||
this.Scene.input.keyboard.removeAllListeners();
|
||||
}
|
||||
|
||||
//todo: should we also disable the joystick?
|
||||
disableControls(){
|
||||
this.Scene.input.keyboard.removeAllKeys();
|
||||
this.isInputDisabled = true;
|
||||
}
|
||||
|
||||
restoreControls(){
|
||||
this.initKeyBoardEvent();
|
||||
this.isInputDisabled = false;
|
||||
}
|
||||
getEventListForGameTick(): ActiveEventList {
|
||||
const eventsMap = new ActiveEventList();
|
||||
if (this.isInputDisabled) {
|
||||
return eventsMap;
|
||||
}
|
||||
this.joystickEvents.forEach((value, key) => {
|
||||
if (value) {
|
||||
switch (key) {
|
||||
case UserInputEvent.MoveUp:
|
||||
case UserInputEvent.MoveDown:
|
||||
this.joystickForceAccuY += this.joystick.forceY;
|
||||
if (Math.abs(this.joystickForceAccuY) > this.joystickForceThreshold) {
|
||||
eventsMap.set(key, value);
|
||||
this.joystickForceAccuY = 0;
|
||||
}
|
||||
break;
|
||||
case UserInputEvent.MoveLeft:
|
||||
case UserInputEvent.MoveRight:
|
||||
this.joystickForceAccuX += this.joystick.forceX;
|
||||
if (Math.abs(this.joystickForceAccuX) > this.joystickForceThreshold) {
|
||||
eventsMap.set(key, value);
|
||||
this.joystickForceAccuX = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
eventsMap.set(UserInputEvent.JoystickMove, this.joystickEvents.any());
|
||||
this.KeysCode.forEach(d => {
|
||||
if (d. keyInstance.isDown) {
|
||||
if (d.keyInstance.isDown) {
|
||||
eventsMap.set(d.event, true);
|
||||
}
|
||||
});
|
||||
|
@ -86,4 +171,14 @@ export class UserInputManager {
|
|||
removeSpaceEventListner(callback : Function){
|
||||
this.Scene.input.keyboard.removeListener('keyup-SPACE', callback);
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
this.joystick?.destroy();
|
||||
}
|
||||
|
||||
private initMouseWheel() {
|
||||
this.Scene.input.on('wheel', (pointer: unknown, gameObjects: unknown, deltaX: number, deltaY: number, deltaZ: number) => {
|
||||
this.Scene.zoomByFactor(1 - deltaY / 53 * 0.1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue