Merge branch 'develop' of https://github.com/thecodingmachine/workadventure into SoundInMapScript
# Conflicts: # front/src/Api/IframeListener.ts # front/src/Phaser/Game/GameScene.ts # front/src/iframe_api.ts # maps/Tuto/scriptTuto.js # maps/Village/Village.json
This commit is contained in:
commit
0c3b9ccfbf
189 changed files with 11326 additions and 2993 deletions
|
@ -1,5 +1,5 @@
|
|||
import {gameManager, HasMovedEvent} from "./GameManager";
|
||||
import {
|
||||
import type {
|
||||
GroupCreatedUpdatedMessageInterface,
|
||||
MessageUserJoined,
|
||||
MessageUserMovedInterface,
|
||||
|
@ -10,9 +10,22 @@ import {
|
|||
RoomJoinedMessageInterface
|
||||
} from "../../Connexion/ConnexionModels";
|
||||
import {CurrentGamerInterface, hasMovedEventName, Player} from "../Player/Player";
|
||||
import {DEBUG_MODE, JITSI_PRIVATE_MODE, POSITION_DELAY, RESOLUTION, ZOOM_LEVEL} from "../../Enum/EnvironmentVariable";
|
||||
import {ITiledMap, ITiledMapLayer, ITiledMapLayerProperty, ITiledMapObject, ITiledTileSet} from "../Map/ITiledMap";
|
||||
import {AddPlayerInterface} from "./AddPlayerInterface";
|
||||
import {
|
||||
DEBUG_MODE,
|
||||
JITSI_PRIVATE_MODE,
|
||||
MAX_PER_GROUP,
|
||||
POSITION_DELAY,
|
||||
} from "../../Enum/EnvironmentVariable";
|
||||
import type {
|
||||
ITiledMap,
|
||||
ITiledMapLayer,
|
||||
ITiledMapLayerProperty,
|
||||
ITiledMapObject,
|
||||
ITiledText,
|
||||
ITiledMapTileLayer,
|
||||
ITiledTileSet
|
||||
} from "../Map/ITiledMap";
|
||||
import type {AddPlayerInterface} from "./AddPlayerInterface";
|
||||
import {PlayerAnimationDirections} from "../Player/Animation";
|
||||
import {PlayerMovement} from "./PlayerMovement";
|
||||
import {PlayersPositionInterpolator} from "./PlayersPositionInterpolator";
|
||||
|
@ -36,14 +49,14 @@ import {
|
|||
import {GameMap} from "./GameMap";
|
||||
import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager";
|
||||
import {mediaManager} from "../../WebRtc/MediaManager";
|
||||
import {ItemFactoryInterface} from "../Items/ItemFactoryInterface";
|
||||
import {ActionableItem} from "../Items/ActionableItem";
|
||||
import type {ItemFactoryInterface} from "../Items/ItemFactoryInterface";
|
||||
import type {ActionableItem} from "../Items/ActionableItem";
|
||||
import {UserInputManager} from "../UserInput/UserInputManager";
|
||||
import {soundManager} from "./SoundManager";
|
||||
import {UserMovedMessage} from "../../Messages/generated/messages_pb";
|
||||
import type {UserMovedMessage} from "../../Messages/generated/messages_pb";
|
||||
import {ProtobufClientUtils} from "../../Network/ProtobufClientUtils";
|
||||
import {connectionManager} from "../../Connexion/ConnectionManager";
|
||||
import {RoomConnection} from "../../Connexion/RoomConnection";
|
||||
import type {RoomConnection} from "../../Connexion/RoomConnection";
|
||||
import {GlobalMessageManager} from "../../Administration/GlobalMessageManager";
|
||||
import {userMessageManager} from "../../Administration/UserMessageManager";
|
||||
import {ConsoleGlobalMessageManager} from "../../Administration/ConsoleGlobalMessageManager";
|
||||
|
@ -68,8 +81,17 @@ import CanvasTexture = Phaser.Textures.CanvasTexture;
|
|||
import GameObject = Phaser.GameObjects.GameObject;
|
||||
import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR;
|
||||
import DOMElement = Phaser.GameObjects.DOMElement;
|
||||
import {Subscription} from "rxjs";
|
||||
import type {Subscription} from "rxjs";
|
||||
import {worldFullMessageStream} from "../../Connexion/WorldFullMessageStream";
|
||||
import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager";
|
||||
import RenderTexture = Phaser.GameObjects.RenderTexture;
|
||||
import Tilemap = Phaser.Tilemaps.Tilemap;
|
||||
import {DirtyScene} from "./DirtyScene";
|
||||
import {TextUtils} from "../Components/TextUtils";
|
||||
import {touchScreenManager} from "../../Touch/TouchScreenManager";
|
||||
import {PinchManager} from "../UserInput/PinchManager";
|
||||
import {joystickBaseImg, joystickBaseKey, joystickThumbImg, joystickThumbKey} from "../Components/MobileJoystick";
|
||||
import {waScaleManager} from "../Services/WaScaleManager";
|
||||
|
||||
export interface GameSceneInitInterface {
|
||||
initPosition: PointInterface|null,
|
||||
|
@ -108,13 +130,13 @@ interface DeleteGroupEventInterface {
|
|||
|
||||
const defaultStartLayerName = 'start';
|
||||
|
||||
export class GameScene extends ResizableScene implements CenterListener {
|
||||
export class GameScene extends DirtyScene implements CenterListener {
|
||||
Terrains : Array<Phaser.Tilemaps.Tileset>;
|
||||
CurrentPlayer!: CurrentGamerInterface;
|
||||
MapPlayers!: Phaser.Physics.Arcade.Group;
|
||||
MapPlayersByKey : Map<number, RemotePlayer> = new Map<number, RemotePlayer>();
|
||||
Map!: Phaser.Tilemaps.Tilemap;
|
||||
Layers!: Array<Phaser.Tilemaps.StaticTilemapLayer>;
|
||||
Layers!: Array<Phaser.Tilemaps.TilemapLayer>;
|
||||
Objects!: Array<Phaser.Physics.Arcade.Sprite>;
|
||||
mapFile!: ITiledMap;
|
||||
groups: Map<number, Sprite>;
|
||||
|
@ -125,12 +147,12 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||
pendingEvents: Queue<InitUserPositionEventInterface|AddPlayerEventInterface|RemovePlayerEventInterface|UserMovedEventInterface|GroupCreatedUpdatedEventInterface|DeleteGroupEventInterface> = new Queue<InitUserPositionEventInterface|AddPlayerEventInterface|RemovePlayerEventInterface|UserMovedEventInterface|GroupCreatedUpdatedEventInterface|DeleteGroupEventInterface>();
|
||||
private initPosition: PositionInterface|null = null;
|
||||
private playersPositionInterpolator = new PlayersPositionInterpolator();
|
||||
public connection!: RoomConnection;
|
||||
public connection: RoomConnection|undefined;
|
||||
private simplePeer!: SimplePeer;
|
||||
private GlobalMessageManager!: GlobalMessageManager;
|
||||
public ConsoleGlobalMessageManager!: ConsoleGlobalMessageManager;
|
||||
private connectionAnswerPromise: Promise<RoomJoinedMessageInterface>;
|
||||
private connectionAnswerPromiseResolve!: (value?: RoomJoinedMessageInterface | PromiseLike<RoomJoinedMessageInterface>) => void;
|
||||
private connectionAnswerPromiseResolve!: (value: RoomJoinedMessageInterface | PromiseLike<RoomJoinedMessageInterface>) => void;
|
||||
// A promise that will resolve when the "create" method is called (signaling loading is ended)
|
||||
private createPromise: Promise<void>;
|
||||
private createPromiseResolve!: (value?: void | PromiseLike<void>) => void;
|
||||
|
@ -160,8 +182,14 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||
private openChatIcon!: OpenChatIcon;
|
||||
private playerName!: string;
|
||||
private characterLayers!: string[];
|
||||
private companion!: string|null;
|
||||
private messageSubscription: Subscription|null = null;
|
||||
private popUpElements : Map<number, DOMElement> = new Map<number, Phaser.GameObjects.DOMElement>();
|
||||
private originalMapUrl: string|undefined;
|
||||
private pinchManager: PinchManager|undefined;
|
||||
private physicsEnabled: boolean = true;
|
||||
private mapTransitioning: boolean = false; //used to prevent transitions happenning at the same time.
|
||||
private onVisibilityChangeCallback: () => void;
|
||||
|
||||
constructor(private room: Room, MapUrlFile: string, customKey?: string|undefined) {
|
||||
super({
|
||||
|
@ -177,10 +205,11 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||
|
||||
this.createPromise = new Promise<void>((resolve, reject): void => {
|
||||
this.createPromiseResolve = resolve;
|
||||
})
|
||||
});
|
||||
this.connectionAnswerPromise = new Promise<RoomJoinedMessageInterface>((resolve, reject): void => {
|
||||
this.connectionAnswerPromiseResolve = resolve;
|
||||
})
|
||||
});
|
||||
this.onVisibilityChangeCallback = this.onVisibilityChange.bind(this);
|
||||
}
|
||||
|
||||
//hook preload scene
|
||||
|
@ -194,9 +223,14 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||
}
|
||||
|
||||
this.load.image(openChatIconName, 'resources/objects/talk.png');
|
||||
if (touchScreenManager.supportTouchScreen) {
|
||||
this.load.image(joystickBaseKey, joystickBaseImg);
|
||||
this.load.image(joystickThumbKey, joystickThumbImg);
|
||||
}
|
||||
this.load.on(FILE_LOAD_ERROR, (file: {src: string}) => {
|
||||
// If we happen to be in HTTP and we are trying to load a URL in HTTPS only... (this happens only in dev environments)
|
||||
if (window.location.protocol === 'http:' && file.src === this.MapUrlFile && file.src.startsWith('http:')) {
|
||||
if (window.location.protocol === 'http:' && file.src === this.MapUrlFile && file.src.startsWith('http:') && this.originalMapUrl === undefined) {
|
||||
this.originalMapUrl = this.MapUrlFile;
|
||||
this.MapUrlFile = this.MapUrlFile.replace('http://', 'https://');
|
||||
this.load.tilemapTiledJSON(this.MapUrlFile, this.MapUrlFile);
|
||||
this.load.on('filecomplete-tilemapJSON-'+this.MapUrlFile, (key: string, type: string, data: unknown) => {
|
||||
|
@ -204,10 +238,25 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||
});
|
||||
return;
|
||||
}
|
||||
// 127.0.0.1, localhost and *.localhost are considered secure, even on HTTP.
|
||||
// So if we are in https, we can still try to load a HTTP local resource (can be useful for testing purposes)
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure
|
||||
const url = new URL(file.src);
|
||||
const host = url.host.split(':')[0];
|
||||
if (window.location.protocol === 'https:' && file.src === this.MapUrlFile && (host === '127.0.0.1' || host === 'localhost' || host.endsWith('.localhost')) && this.originalMapUrl === undefined) {
|
||||
this.originalMapUrl = this.MapUrlFile;
|
||||
this.MapUrlFile = this.MapUrlFile.replace('https://', 'http://');
|
||||
this.load.tilemapTiledJSON(this.MapUrlFile, this.MapUrlFile);
|
||||
this.load.on('filecomplete-tilemapJSON-'+this.MapUrlFile, (key: string, type: string, data: unknown) => {
|
||||
this.onMapLoad(data);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.scene.start(ErrorSceneName, {
|
||||
title: 'Network error',
|
||||
subTitle: 'An error occurred while loading resource:',
|
||||
message: file.src
|
||||
message: this.originalMapUrl ?? file.src
|
||||
});
|
||||
});
|
||||
this.load.on('filecomplete-tilemapJSON-'+this.MapUrlFile, (key: string, type: string, data: unknown) => {
|
||||
|
@ -225,6 +274,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||
this.load.spritesheet('layout_modes', 'resources/objects/layout_modes.png', {frameWidth: 32, frameHeight: 32});
|
||||
this.load.bitmapFont('main_font', 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
|
||||
|
||||
//this function must stay at the end of preload function
|
||||
addLoader(this);
|
||||
}
|
||||
|
||||
|
@ -324,11 +374,17 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||
|
||||
//hook create scene
|
||||
create(): void {
|
||||
this.trackDirtyAnims();
|
||||
|
||||
gameManager.gameSceneIsCreated(this);
|
||||
urlManager.pushRoomIdToUrl(this.room);
|
||||
this.startLayerName = urlManager.getStartLayerNameFromUrl();
|
||||
|
||||
this.messageSubscription = worldFullMessageStream.stream.subscribe((message) => this.showWorldFullError())
|
||||
if (touchScreenManager.supportTouchScreen) {
|
||||
this.pinchManager = new PinchManager(this);
|
||||
}
|
||||
|
||||
this.messageSubscription = worldFullMessageStream.stream.subscribe((message) => this.showWorldFullError(message))
|
||||
|
||||
const playerName = gameManager.getPlayerName();
|
||||
if (!playerName) {
|
||||
|
@ -336,7 +392,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||
}
|
||||
this.playerName = playerName;
|
||||
this.characterLayers = gameManager.getCharacterLayers();
|
||||
|
||||
this.companion = gameManager.getCompanion();
|
||||
|
||||
//initalise map
|
||||
this.Map = this.add.tilemap(this.MapUrlFile);
|
||||
|
@ -350,11 +406,11 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||
this.physics.world.setBounds(0, 0, this.Map.widthInPixels, this.Map.heightInPixels);
|
||||
|
||||
//add layer on map
|
||||
this.Layers = new Array<Phaser.Tilemaps.StaticTilemapLayer>();
|
||||
this.Layers = new Array<Phaser.Tilemaps.TilemapLayer>();
|
||||
let depth = -2;
|
||||
for (const layer of this.mapFile.layers) {
|
||||
for (const layer of this.gameMap.layersIterator) {
|
||||
if (layer.type === 'tilelayer') {
|
||||
this.addLayer(this.Map.createStaticLayer(layer.name, this.Terrains, 0, 0).setDepth(depth));
|
||||
this.addLayer(this.Map.createLayer(layer.name, this.Terrains, 0, 0).setDepth(depth));
|
||||
|
||||
const exitSceneUrl = this.getExitSceneUrl(layer);
|
||||
if (exitSceneUrl !== undefined) {
|
||||
|
@ -368,9 +424,16 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||
if (layer.type === 'objectgroup' && layer.name === 'floorLayer') {
|
||||
depth = 10000;
|
||||
}
|
||||
if (layer.type === 'objectgroup') {
|
||||
for (const object of layer.objects) {
|
||||
if (object.text) {
|
||||
TextUtils.createTextFromITiledMapObject(this, object);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (depth === -2) {
|
||||
throw new Error('Your map MUST contain a layer of type "objectgroup" whose name is "floorLayer" that represents the layer characters are drawn at.');
|
||||
throw new Error('Your map MUST contain a layer of type "objectgroup" whose name is "floorLayer" that represents the layer characters are drawn at. This layer cannot be contained in a group.');
|
||||
}
|
||||
|
||||
this.initStartXAndStartY();
|
||||
|
@ -381,10 +444,15 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||
//initialise list of other player
|
||||
this.MapPlayers = this.physics.add.group({immovable: true});
|
||||
|
||||
|
||||
//create input to move
|
||||
this.userInputManager = new UserInputManager(this);
|
||||
mediaManager.setUserInputManager(this.userInputManager);
|
||||
|
||||
if (localUserStore.getFullscreen()) {
|
||||
document.querySelector('body')?.requestFullscreen();
|
||||
}
|
||||
|
||||
//notify game manager can to create currentUser in map
|
||||
this.createCurrentPlayer();
|
||||
this.removeAllRemotePlayers(); //cleanup the list of remote players in case the scene was rebooted
|
||||
|
@ -424,9 +492,10 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||
this.openChatIcon = new OpenChatIcon(this, 2, this.game.renderer.height - 2)
|
||||
|
||||
// FIXME: change this to use the UserInputManager class for input
|
||||
this.input.keyboard.on('keyup-M', () => {
|
||||
// FIXME: Comment this feature because when user write M key in report input, the layout change.
|
||||
/*this.input.keyboard.on('keyup-M', () => {
|
||||
this.switchLayoutMode();
|
||||
});
|
||||
});*/
|
||||
|
||||
this.reposition();
|
||||
|
||||
|
@ -439,6 +508,8 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||
if (!this.room.isDisconnected()) {
|
||||
this.connect();
|
||||
}
|
||||
|
||||
document.addEventListener('visibilitychange', this.onVisibilityChangeCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -460,7 +531,9 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||
top: camera.scrollY,
|
||||
right: camera.scrollX + camera.width,
|
||||
bottom: camera.scrollY + camera.height,
|
||||
}).then((onConnect: OnConnectInterface) => {
|
||||
},
|
||||
this.companion
|
||||
).then((onConnect: OnConnectInterface) => {
|
||||
this.connection = onConnect.connection;
|
||||
|
||||
this.connection.onUserJoins((message: MessageUserJoined) => {
|
||||
|
@ -468,7 +541,8 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||
userId: message.userId,
|
||||
characterLayers: message.characterLayers,
|
||||
name: message.name,
|
||||
position: message.position
|
||||
position: message.position,
|
||||
companion: message.companion
|
||||
}
|
||||
this.addPlayer(userMessage);
|
||||
});
|
||||
|
@ -557,6 +631,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||
self.chatModeSprite.setVisible(false);
|
||||
self.openChatIcon.setVisible(false);
|
||||
audioManager.restoreVolume();
|
||||
self.onVisibilityChange();
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -647,7 +722,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||
if(openWebsiteTriggerValue && openWebsiteTriggerValue === ON_ACTION_TRIGGER_BUTTON) {
|
||||
let message = allProps.get(WEBSITE_MESSAGE_PROPERTIES);
|
||||
if(message === undefined){
|
||||
message = 'Press on SPACE to open the web site';
|
||||
message = 'Press SPACE or touch here to open web site';
|
||||
}
|
||||
layoutManager.addActionButton('openWebsite', message.toString(), () => {
|
||||
openWebsiteFunction();
|
||||
|
@ -668,7 +743,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||
if (JITSI_PRIVATE_MODE && !jitsiUrl) {
|
||||
const adminTag = allProps.get("jitsiRoomAdminTag") as string|undefined;
|
||||
|
||||
this.connection.emitQueryJitsiJwtMessage(roomName, adminTag);
|
||||
this.connection?.emitQueryJitsiJwtMessage(roomName, adminTag);
|
||||
} else {
|
||||
this.startJitsi(roomName, undefined);
|
||||
}
|
||||
|
@ -679,7 +754,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||
if(jitsiTriggerValue && jitsiTriggerValue === ON_ACTION_TRIGGER_BUTTON) {
|
||||
let message = allProps.get(JITSI_MESSAGE_PROPERTIES);
|
||||
if (message === undefined) {
|
||||
message = 'Press on SPACE to enter in jitsi meet room';
|
||||
message = 'Press SPACE or touch here to enter Jitsi Meet room';
|
||||
}
|
||||
layoutManager.addActionButton('jitsiRoom', message.toString(), () => {
|
||||
openJitsiRoomFunction();
|
||||
|
@ -691,9 +766,9 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||
});
|
||||
this.gameMap.onPropertyChange('silent', (newValue, oldValue) => {
|
||||
if (newValue === undefined || newValue === false || newValue === '') {
|
||||
this.connection.setSilent(false);
|
||||
this.connection?.setSilent(false);
|
||||
} else {
|
||||
this.connection.setSilent(true);
|
||||
this.connection?.setSilent(true);
|
||||
}
|
||||
});
|
||||
this.gameMap.onPropertyChange('playAudio', (newValue, oldValue, allProps) => {
|
||||
|
@ -834,6 +909,8 @@ ${escapedMessage}
|
|||
}
|
||||
|
||||
private onMapExit(exitKey: string) {
|
||||
if (this.mapTransitioning) return;
|
||||
this.mapTransitioning = true;
|
||||
const {roomId, hash} = Room.getIdFromIdentifier(exitKey, this.MapUrlFile, this.instance);
|
||||
if (!roomId) throw new Error('Could not find the room from its exit key: '+exitKey);
|
||||
urlManager.pushStartLayerNameToUrl(hash);
|
||||
|
@ -851,6 +928,7 @@ ${escapedMessage}
|
|||
this.initPositionFromLayerName(hash || defaultStartLayerName);
|
||||
this.CurrentPlayer.x = this.startX;
|
||||
this.CurrentPlayer.y = this.startY;
|
||||
setTimeout(() => this.mapTransitioning = false, 500);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -867,18 +945,27 @@ ${escapedMessage}
|
|||
audioManager.unloadAudio();
|
||||
// We are completely destroying the current scene to avoid using a half-backed instance when coming back to the same map.
|
||||
this.connection?.closeConnection();
|
||||
this.simplePeer.closeAllConnections();
|
||||
this.simplePeer?.closeAllConnections();
|
||||
this.simplePeer?.unregister();
|
||||
this.messageSubscription?.unsubscribe();
|
||||
this.userInputManager.destroy();
|
||||
this.pinchManager?.destroy();
|
||||
|
||||
for(const iframeEvents of this.iframeSubscriptionList){
|
||||
iframeEvents.unsubscribe();
|
||||
}
|
||||
|
||||
document.removeEventListener('visibilitychange', this.onVisibilityChangeCallback);
|
||||
}
|
||||
|
||||
private removeAllRemotePlayers(): void {
|
||||
this.MapPlayersByKey.forEach((player: RemotePlayer) => {
|
||||
player.destroy();
|
||||
|
||||
if (player.companion) {
|
||||
player.companion.destroy();
|
||||
}
|
||||
|
||||
this.MapPlayers.remove(player);
|
||||
});
|
||||
this.MapPlayersByKey = new Map<number, RemotePlayer>();
|
||||
|
@ -926,13 +1013,14 @@ ${escapedMessage}
|
|||
}
|
||||
|
||||
private initPositionFromLayerName(layerName: string) {
|
||||
for (const layer of this.mapFile.layers) {
|
||||
if (layerName === layer.name && layer.type === 'tilelayer' && (layerName === defaultStartLayerName || this.isStartLayer(layer))) {
|
||||
for (const layer of this.gameMap.layersIterator) {
|
||||
if ((layerName === layer.name || layer.name.endsWith('/'+layerName)) && layer.type === 'tilelayer' && (layerName === defaultStartLayerName || this.isStartLayer(layer))) {
|
||||
const startPosition = this.startUser(layer);
|
||||
this.startX = startPosition.x + this.mapFile.tilewidth/2;
|
||||
this.startY = startPosition.y + this.mapFile.tileheight/2;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private getExitUrl(layer: ITiledMapLayer): string|undefined {
|
||||
|
@ -955,7 +1043,7 @@ ${escapedMessage}
|
|||
}
|
||||
|
||||
private getProperty(layer: ITiledMapLayer|ITiledMap, name: string): string|boolean|number|undefined {
|
||||
const properties: ITiledMapLayerProperty[] = layer.properties;
|
||||
const properties: ITiledMapLayerProperty[]|undefined = layer.properties;
|
||||
if (!properties) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -967,7 +1055,7 @@ ${escapedMessage}
|
|||
}
|
||||
|
||||
private getProperties(layer: ITiledMapLayer|ITiledMap, name: string): (string|number|boolean|undefined)[] {
|
||||
const properties: ITiledMapLayerProperty[] = layer.properties;
|
||||
const properties: ITiledMapLayerProperty[]|undefined = layer.properties;
|
||||
if (!properties) {
|
||||
return [];
|
||||
}
|
||||
|
@ -975,13 +1063,13 @@ ${escapedMessage}
|
|||
}
|
||||
|
||||
//todo: push that into the gameManager
|
||||
private async loadNextGame(exitSceneIdentifier: string){
|
||||
private loadNextGame(exitSceneIdentifier: string): void {
|
||||
const {roomId, hash} = Room.getIdFromIdentifier(exitSceneIdentifier, this.MapUrlFile, this.instance);
|
||||
const room = new Room(roomId);
|
||||
await gameManager.loadMap(room, this.scene);
|
||||
gameManager.loadMap(room, this.scene).catch(() => {});
|
||||
}
|
||||
|
||||
private startUser(layer: ITiledMapLayer): PositionInterface {
|
||||
private startUser(layer: ITiledMapTileLayer): PositionInterface {
|
||||
const tiles = layer.data;
|
||||
if (typeof(tiles) === 'string') {
|
||||
throw new Error('The content of a JSON map must be filled as a JSON array, not as a string');
|
||||
|
@ -1011,17 +1099,19 @@ ${escapedMessage}
|
|||
//todo: in a dedicated class/function?
|
||||
initCamera() {
|
||||
this.cameras.main.setBounds(0,0, this.Map.widthInPixels, this.Map.heightInPixels);
|
||||
this.cameras.main.startFollow(this.CurrentPlayer, true);
|
||||
this.updateCameraOffset();
|
||||
this.cameras.main.setZoom(ZOOM_LEVEL);
|
||||
}
|
||||
|
||||
addLayer(Layer : Phaser.Tilemaps.StaticTilemapLayer){
|
||||
addLayer(Layer : Phaser.Tilemaps.TilemapLayer){
|
||||
this.Layers.push(Layer);
|
||||
}
|
||||
|
||||
createCollisionWithPlayer() {
|
||||
this.physics.disableUpdate();
|
||||
this.physicsEnabled = false;
|
||||
//add collision layer
|
||||
this.Layers.forEach((Layer: Phaser.Tilemaps.StaticTilemapLayer) => {
|
||||
this.Layers.forEach((Layer: Phaser.Tilemaps.TilemapLayer) => {
|
||||
this.physics.add.collider(this.CurrentPlayer, Layer, (object1: GameObject, object2: GameObject) => {
|
||||
//this.CurrentPlayer.say("Collision with layer : "+ (object2 as Tile).layer.name)
|
||||
});
|
||||
|
@ -1049,7 +1139,9 @@ ${escapedMessage}
|
|||
texturesPromise,
|
||||
PlayerAnimationDirections.Down,
|
||||
false,
|
||||
this.userInputManager
|
||||
this.userInputManager,
|
||||
this.companion,
|
||||
this.companion !== null ? lazyLoadCompanionResource(this.load, this.companion) : undefined
|
||||
);
|
||||
}catch (err){
|
||||
if(err instanceof TextureError) {
|
||||
|
@ -1135,7 +1227,7 @@ ${escapedMessage}
|
|||
this.lastMoveEventSent = event;
|
||||
this.lastSentTick = this.currentTick;
|
||||
const camera = this.cameras.main;
|
||||
this.connection.sharePosition(event.x, event.y, event.direction, event.moving, {
|
||||
this.connection?.sharePosition(event.x, event.y, event.direction, event.moving, {
|
||||
left: camera.scrollX,
|
||||
top: camera.scrollY,
|
||||
right: camera.scrollX + camera.width,
|
||||
|
@ -1148,12 +1240,27 @@ ${escapedMessage}
|
|||
* @param delta The delta time in ms since the last frame. This is a smoothed and capped value based on the FPS rate.
|
||||
*/
|
||||
update(time: number, delta: number) : void {
|
||||
mediaManager.setLastUpdateScene();
|
||||
this.dirty = false;
|
||||
mediaManager.updateScene();
|
||||
this.currentTick = time;
|
||||
if (this.CurrentPlayer.isMoving()) {
|
||||
this.dirty = true;
|
||||
}
|
||||
this.CurrentPlayer.moveUser(delta);
|
||||
if (this.CurrentPlayer.isMoving()) {
|
||||
this.dirty = true;
|
||||
if (!this.physicsEnabled) {
|
||||
this.physics.enableUpdate();
|
||||
this.physicsEnabled = true;
|
||||
}
|
||||
} else if (this.physicsEnabled) {
|
||||
this.physics.disableUpdate();
|
||||
this.physicsEnabled = false;
|
||||
}
|
||||
|
||||
// Let's handle all events
|
||||
while (this.pendingEvents.length !== 0) {
|
||||
this.dirty = true;
|
||||
const event = this.pendingEvents.dequeue();
|
||||
switch (event.type) {
|
||||
case "InitUserPositionEvent":
|
||||
|
@ -1179,6 +1286,7 @@ ${escapedMessage}
|
|||
// Let's move all users
|
||||
const updatedPlayersPositions = this.playersPositionInterpolator.getUpdatedPositions(time);
|
||||
updatedPlayersPositions.forEach((moveEvent: HasMovedEvent, userId: number) => {
|
||||
this.dirty = true;
|
||||
const player: RemotePlayer | undefined = this.MapPlayersByKey.get(userId);
|
||||
if (player === undefined) {
|
||||
throw new Error('Cannot find player with ID "' + userId + '"');
|
||||
|
@ -1201,7 +1309,7 @@ ${escapedMessage}
|
|||
* Put all the players on the map on map load.
|
||||
*/
|
||||
private doInitUsersPosition(usersPosition: MessageUserPositionInterface[]): void {
|
||||
const currentPlayerId = this.connection.getUserId();
|
||||
const currentPlayerId = this.connection?.getUserId();
|
||||
this.removeAllRemotePlayers();
|
||||
// load map
|
||||
usersPosition.forEach((userPosition : MessageUserPositionInterface) => {
|
||||
|
@ -1241,7 +1349,9 @@ ${escapedMessage}
|
|||
addPlayerData.name,
|
||||
texturesPromise,
|
||||
addPlayerData.position.direction as PlayerAnimationDirections,
|
||||
addPlayerData.position.moving
|
||||
addPlayerData.position.moving,
|
||||
addPlayerData.companion,
|
||||
addPlayerData.companion !== null ? lazyLoadCompanionResource(this.load, addPlayerData.companion) : undefined
|
||||
);
|
||||
this.MapPlayers.add(player);
|
||||
this.MapPlayersByKey.set(player.userId, player);
|
||||
|
@ -1264,6 +1374,11 @@ ${escapedMessage}
|
|||
console.error('Cannot find user with id ', userId);
|
||||
} else {
|
||||
player.destroy();
|
||||
|
||||
if (player.companion) {
|
||||
player.companion.destroy();
|
||||
}
|
||||
|
||||
this.MapPlayers.remove(player);
|
||||
}
|
||||
this.MapPlayersByKey.delete(userId);
|
||||
|
@ -1308,7 +1423,7 @@ ${escapedMessage}
|
|||
this,
|
||||
Math.round(groupPositionMessage.position.x),
|
||||
Math.round(groupPositionMessage.position.y),
|
||||
groupPositionMessage.groupSize === 4 ? 'circleSprite-red' : 'circleSprite-white'
|
||||
groupPositionMessage.groupSize === MAX_PER_GROUP ? 'circleSprite-red' : 'circleSprite-white'
|
||||
);
|
||||
sprite.setDisplayOrigin(48, 48);
|
||||
this.add.existing(sprite);
|
||||
|
@ -1338,15 +1453,16 @@ ${escapedMessage}
|
|||
* Sends to the server an event emitted by one of the ActionableItems.
|
||||
*/
|
||||
emitActionableEvent(itemId: number, eventName: string, state: unknown, parameters: unknown) {
|
||||
this.connection.emitActionableEvent(itemId, eventName, state, parameters);
|
||||
this.connection?.emitActionableEvent(itemId, eventName, state, parameters);
|
||||
}
|
||||
|
||||
public onResize(): void {
|
||||
public onResize(ev: UIEvent): void {
|
||||
super.onResize(ev);
|
||||
this.reposition();
|
||||
|
||||
// Send new viewport to server
|
||||
const camera = this.cameras.main;
|
||||
this.connection.setViewport({
|
||||
this.connection?.setViewport({
|
||||
left: camera.scrollX,
|
||||
top: camera.scrollY,
|
||||
right: camera.scrollX + camera.width,
|
||||
|
@ -1376,19 +1492,18 @@ ${escapedMessage}
|
|||
}
|
||||
|
||||
/**
|
||||
* Updates the offset of the character compared to the center of the screen according to the layout mananger
|
||||
* (tries to put the character in the center of the reamining space if there is a discussion going on.
|
||||
* Updates the offset of the character compared to the center of the screen according to the layout manager
|
||||
* (tries to put the character in the center of the remaining space if there is a discussion going on.
|
||||
*/
|
||||
private updateCameraOffset(): void {
|
||||
const array = layoutManager.findBiggestAvailableArray();
|
||||
let xCenter = (array.xEnd - array.xStart) / 2 + array.xStart;
|
||||
let yCenter = (array.yEnd - array.yStart) / 2 + array.yStart;
|
||||
const xCenter = (array.xEnd - array.xStart) / 2 + array.xStart;
|
||||
const yCenter = (array.yEnd - array.yStart) / 2 + array.yStart;
|
||||
|
||||
const game = HtmlUtils.querySelectorOrFail<HTMLCanvasElement>('#game canvas');
|
||||
// Let's put this in Game coordinates by applying the zoom level:
|
||||
xCenter /= ZOOM_LEVEL * RESOLUTION;
|
||||
yCenter /= ZOOM_LEVEL * RESOLUTION;
|
||||
|
||||
this.cameras.main.startFollow(this.CurrentPlayer, true, 1, 1, xCenter - this.game.renderer.width / 2, yCenter - this.game.renderer.height / 2);
|
||||
this.cameras.main.setFollowOffset((xCenter - game.offsetWidth/2) * window.devicePixelRatio / this.scale.zoom , (yCenter - game.offsetHeight/2) * window.devicePixelRatio / this.scale.zoom);
|
||||
}
|
||||
|
||||
public onCenterChange(): void {
|
||||
|
@ -1402,13 +1517,15 @@ ${escapedMessage}
|
|||
const jitsiUrl = allProps.get("jitsiUrl") as string|undefined;
|
||||
|
||||
jitsiFactory.start(roomName, this.playerName, jwt, jitsiConfig, jitsiInterfaceConfig, jitsiUrl);
|
||||
this.connection.setSilent(true);
|
||||
this.connection?.setSilent(true);
|
||||
mediaManager.hideGameOverlay();
|
||||
|
||||
//permit to stop jitsi when user close iframe
|
||||
mediaManager.addTriggerCloseJitsiFrameButton('close-jisi',() => {
|
||||
this.stopJitsi();
|
||||
});
|
||||
|
||||
this.onVisibilityChange();
|
||||
}
|
||||
|
||||
public stopJitsi(): void {
|
||||
|
@ -1417,6 +1534,7 @@ ${escapedMessage}
|
|||
mediaManager.showGameOverlay();
|
||||
|
||||
mediaManager.removeTriggerCloseJitsiFrameButton('close-jisi');
|
||||
this.onVisibilityChange();
|
||||
}
|
||||
|
||||
//todo: put this into an 'orchestrator' scene (EntryScene?)
|
||||
|
@ -1431,14 +1549,45 @@ ${escapedMessage}
|
|||
}
|
||||
|
||||
//todo: put this into an 'orchestrator' scene (EntryScene?)
|
||||
private showWorldFullError(): void {
|
||||
private showWorldFullError(message: string|null): void {
|
||||
this.cleanupClosingScene();
|
||||
this.scene.stop(ReconnectingSceneName);
|
||||
this.scene.remove(ReconnectingSceneName);
|
||||
this.userInputManager.disableControls();
|
||||
this.scene.start(ErrorSceneName, {
|
||||
title: 'Connection rejected',
|
||||
subTitle: 'The world you are trying to join is full. Try again later.',
|
||||
message: 'If you want more information, you may contact us at: workadventure@thecodingmachine.com'
|
||||
});
|
||||
//FIX ME to use status code
|
||||
if(message == undefined){
|
||||
this.scene.start(ErrorSceneName, {
|
||||
title: 'Connection rejected',
|
||||
subTitle: 'The world you are trying to join is full. Try again later.',
|
||||
message: 'If you want more information, you may contact us at: workadventure@thecodingmachine.com'
|
||||
});
|
||||
}else{
|
||||
this.scene.start(ErrorSceneName, {
|
||||
title: 'Connection rejected',
|
||||
subTitle: 'You cannot join the World. Try again later. \n\r \n\r Error: '+message+'.',
|
||||
message: 'If you want more information, you may contact administrator or contact us at: workadventure@thecodingmachine.com'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
zoomByFactor(zoomFactor: number) {
|
||||
waScaleManager.zoomModifier *= zoomFactor;
|
||||
this.updateCameraOffset();
|
||||
}
|
||||
|
||||
private onVisibilityChange(): void {
|
||||
// If the overlay is not displayed, we are in Jitsi. We don't need the webcam.
|
||||
if (!mediaManager.isGameOverlayVisible()) {
|
||||
mediaManager.blurCamera();
|
||||
return;
|
||||
}
|
||||
|
||||
if (document.visibilityState === 'visible') {
|
||||
mediaManager.focusCamera();
|
||||
} else {
|
||||
if (this.simplePeer.getNbConnections() === 0) {
|
||||
mediaManager.blurCamera();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue