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

This commit is contained in:
_Bastler
2021-07-02 20:59:39 +02:00
58 changed files with 2227 additions and 1746 deletions
+5 -6
View File
@@ -1,17 +1,17 @@
import {ResizableScene} from "../Login/ResizableScene";
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";
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;
protected dirty: boolean = true;
private objectListChanged: boolean = true;
private physicsEnabled: boolean = false;
/**
@@ -59,7 +59,6 @@ export abstract class DirtyScene extends ResizableScene {
this.physicsEnabled = false;
}
});
}
private trackAnimation(): void {
@@ -71,7 +70,7 @@ export abstract class DirtyScene extends ResizableScene {
}
public markDirty(): void {
this.events.once(Phaser.Scenes.Events.POST_UPDATE, () => this.dirty = true);
this.events.once(Phaser.Scenes.Events.POST_UPDATE, () => (this.dirty = true));
}
public onResize(): void {
+31 -31
View File
@@ -1,27 +1,25 @@
import {GameScene} from "./GameScene";
import {connectionManager} from "../../Connexion/ConnectionManager";
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 { GameScene } from "./GameScene";
import { connectionManager } from "../../Connexion/ConnectionManager";
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";
import Axios from "axios";
import {get} from "svelte/store";
import {requestedCameraState, requestedMicrophoneState} from "../../Stores/MediaStore";
import {helpCameraSettingsVisibleStore} from "../../Stores/HelpCameraSettingsStore";
/**
* This class should be responsible for any scene starting/stopping
*/
export class GameManager {
private playerName: string|null;
private characterLayers: string[]|null;
private companion: string|null;
private startRoom!:Room;
currentGameSceneName: string|null = null;
private playerName: string | null;
private characterLayers: string[] | null;
private companion: string | null;
private startRoom!: Room;
currentGameSceneName: string | null = null;
constructor() {
this.playerName = localUserStore.getName();
@@ -57,23 +55,22 @@ export class GameManager {
localUserStore.setCharacterLayers(layers);
}
getPlayerName(): string|null {
getPlayerName(): string | null {
return this.playerName;
}
getCharacterLayers(): string[] {
if (!this.characterLayers) {
throw 'characterLayers are not set';
throw "characterLayers are not set";
}
return this.characterLayers;
}
setCompanion(companion: string|null): void {
setCompanion(companion: string | null): void {
this.companion = companion;
}
getCompanion(): string|null {
getCompanion(): string | null {
return this.companion;
}
@@ -82,18 +79,21 @@ export class GameManager {
const mapDetail = await room.getMapDetail();
const gameIndex = scenePlugin.getIndex(roomID);
if(gameIndex === -1){
const game : Phaser.Scene = new GameScene(room, mapDetail.mapUrl);
if (gameIndex === -1) {
const game: Phaser.Scene = new GameScene(room, mapDetail.mapUrl);
scenePlugin.add(roomID, game, false);
}
}
public goToStartingMap(scenePlugin: Phaser.Scenes.ScenePlugin): void {
console.log('starting '+ (this.currentGameSceneName || this.startRoom.id))
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))){
if (
!localUserStore.getHelpCameraSettingsShown() &&
(!get(requestedMicrophoneState) || !get(requestedCameraState))
) {
helpCameraSettingsVisibleStore.set(true);
localUserStore.setHelpCameraSettingsShown();
}
@@ -110,7 +110,7 @@ export class GameManager {
* This will close the socket connections and stop the gameScene, but won't remove it.
*/
leaveGame(scene: Phaser.Scene, targetSceneName: string, sceneClass: Phaser.Scene): void {
if (this.currentGameSceneName === null) throw 'No current scene id set!';
if (this.currentGameSceneName === null) throw "No current scene id set!";
const gameScene: GameScene = scene.scene.get(this.currentGameSceneName) as GameScene;
gameScene.cleanupClosingScene();
scene.scene.stop(this.currentGameSceneName);
@@ -129,13 +129,13 @@ export class GameManager {
scene.scene.start(this.currentGameSceneName);
scene.scene.wake(MenuSceneName);
} else {
scene.scene.run(fallbackSceneName)
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
if (this.currentGameSceneName === null) throw "No current scene id set!";
return scene.scene.get(this.currentGameSceneName) as GameScene;
}
}
+52 -33
View File
@@ -1,9 +1,13 @@
import type {ITiledMap, ITiledMapLayer, ITiledMapLayerProperty} from "../Map/ITiledMap";
import type { ITiledMap, ITiledMapLayer, ITiledMapLayerProperty } from "../Map/ITiledMap";
import { flattenGroupLayersMap } from "../Map/LayersFlattener";
import TilemapLayer = Phaser.Tilemaps.TilemapLayer;
import { DEPTH_OVERLAY_INDEX } from "./DepthIndexes";
export type PropertyChangeCallback = (newValue: string | number | boolean | undefined, oldValue: string | number | boolean | undefined, allProps: Map<string, string | boolean | number>) => void;
export type PropertyChangeCallback = (
newValue: string | number | boolean | undefined,
oldValue: string | number | boolean | undefined,
allProps: Map<string, string | boolean | number>
) => void;
/**
* A wrapper around a ITiledMap interface to provide additional capabilities.
@@ -19,37 +23,50 @@ export class GameMap {
public readonly flatLayers: ITiledMapLayer[];
public readonly phaserLayers: TilemapLayer[] = [];
public exitUrls: Array<string> = []
public exitUrls: Array<string> = [];
public constructor(private map: ITiledMap, phaserMap: Phaser.Tilemaps.Tilemap, terrains: Array<Phaser.Tilemaps.Tileset>) {
public hasStartTile = false;
public constructor(
private map: ITiledMap,
phaserMap: Phaser.Tilemaps.Tilemap,
terrains: Array<Phaser.Tilemaps.Tileset>
) {
this.flatLayers = flattenGroupLayersMap(map);
let depth = -2;
for (const layer of this.flatLayers) {
if(layer.type === 'tilelayer'){
if (layer.type === "tilelayer") {
this.phaserLayers.push(phaserMap.createLayer(layer.name, terrains, 0, 0).setDepth(depth));
}
if (layer.type === 'objectgroup' && layer.name === 'floorLayer') {
if (layer.type === "objectgroup" && layer.name === "floorLayer") {
depth = DEPTH_OVERLAY_INDEX;
}
}
for (const tileset of map.tilesets) {
tileset?.tiles?.forEach(tile => {
tileset?.tiles?.forEach((tile) => {
if (tile.properties) {
this.tileSetPropertyMap[tileset.firstgid + tile.id] = tile.properties
tile.properties.forEach(prop => {
if (prop.name == 'name' && typeof prop.value == "string") {
this.tileSetPropertyMap[tileset.firstgid + tile.id] = tile.properties;
tile.properties.forEach((prop) => {
if (prop.name == "name" && typeof prop.value == "string") {
this.tileNameMap.set(prop.value, tileset.firstgid + tile.id);
}
if (prop.name == "exitUrl" && typeof prop.value == "string") {
this.exitUrls.push(prop.value);
} else if (prop.name == "start") {
this.hasStartTile = true;
}
})
});
}
})
});
}
}
public getPropertiesForIndex(index: number): Array<ITiledMapLayerProperty> {
if (this.tileSetPropertyMap[index]) {
return this.tileSetPropertyMap[index];
}
return [];
}
/**
* Sets the position of the current player (in pixels)
@@ -93,7 +110,7 @@ export class GameMap {
const properties = new Map<string, string | boolean | number>();
for (const layer of this.flatLayers) {
if (layer.type !== 'tilelayer') {
if (layer.type !== "tilelayer") {
continue;
}
@@ -103,7 +120,7 @@ export class GameMap {
if (tiles[key] == 0) {
continue;
}
tileIndex = tiles[key]
tileIndex = tiles[key];
}
// There is a tile in this layer, let's embed the properties
@@ -117,20 +134,20 @@ export class GameMap {
}
if (tileIndex) {
this.tileSetPropertyMap[tileIndex]?.forEach(property => {
this.tileSetPropertyMap[tileIndex]?.forEach((property) => {
if (property.value) {
properties.set(property.name, property.value)
properties.set(property.name, property.value);
} else if (properties.has(property.name)) {
properties.delete(property.name)
properties.delete(property.name);
}
})
});
}
}
return properties;
}
public getMap(): ITiledMap{
public getMap(): ITiledMap {
return this.map;
}
@@ -138,7 +155,12 @@ export class GameMap {
return this.tileSetPropertyMap[index] || [];
}
private trigger(propName: string, oldValue: string | number | boolean | undefined, newValue: string | number | boolean | undefined, allProps: Map<string, string | boolean | number>) {
private trigger(
propName: string,
oldValue: string | number | boolean | undefined,
newValue: string | number | boolean | undefined,
allProps: Map<string, string | boolean | number>
) {
const callbacksArray = this.callbacks.get(propName);
if (callbacksArray !== undefined) {
for (const callback of callbacksArray) {
@@ -167,7 +189,7 @@ export class GameMap {
return this.phaserLayers.find((layer) => layer.layer.name === layerName);
}
public addTerrain(terrain : Phaser.Tilemaps.Tileset): void {
public addTerrain(terrain: Phaser.Tilemaps.Tileset): void {
for (const phaserLayer of this.phaserLayers) {
phaserLayer.tileset.push(terrain);
}
@@ -175,11 +197,11 @@ export class GameMap {
private putTileInFlatLayer(index: number, x: number, y: number, layer: string): void {
const fLayer = this.findLayer(layer);
if ( fLayer == undefined ) {
if (fLayer == undefined) {
console.error("The layer that you want to change doesn't exist.");
return;
}
if (fLayer.type !== 'tilelayer') {
if (fLayer.type !== "tilelayer") {
console.error("The layer that you want to change is not a tilelayer. Tile can only be put in tilelayer.");
return;
}
@@ -187,27 +209,25 @@ export class GameMap {
console.error("Data of the layer that you want to change is only readable.");
return;
}
fLayer.data[x+y*fLayer.height] = index;
fLayer.data[x + y * fLayer.height] = index;
}
public putTile(tile: string | number, x: number, y: number, layer: string): void {
const phaserLayer = this.findPhaserLayer(layer);
if ( phaserLayer ) {
if (phaserLayer) {
const tileIndex = this.getIndexForTileType(tile);
if ( tileIndex !== undefined ) {
if (tileIndex !== undefined) {
this.putTileInFlatLayer(tileIndex, x, y, layer);
const phaserTile = phaserLayer.putTileAt(tileIndex, x, y);
for (const property of this.getTileProperty(tileIndex)) {
if ( property.name === "collides" && property.value) {
if (property.name === "collides" && property.value) {
phaserTile.setCollision(true);
}
}
}
else {
} else {
console.error("The tile that you want to place doesn't exist.");
}
}
else {
} else {
console.error("The layer that you want to change is not a tilelayer. Tile can only be put in tilelayer.");
}
}
@@ -218,5 +238,4 @@ export class GameMap {
}
return this.tileNameMap.get(tile);
}
}
+61 -127
View File
@@ -1,4 +1,3 @@
import { Queue } from "queue-typescript";
import type { Subscription } from "rxjs";
import { GlobalMessageManager } from "../../Administration/GlobalMessageManager";
import { userMessageManager } from "../../Administration/UserMessageManager";
@@ -14,20 +13,9 @@ import type {
PositionInterface,
RoomJoinedMessageInterface,
} from "../../Connexion/ConnexionModels";
import { localUserStore } from "../../Connexion/LocalUserStore";
import { Room } from "../../Connexion/Room";
import type { RoomConnection } from "../../Connexion/RoomConnection";
import { worldFullMessageStream } from "../../Connexion/WorldFullMessageStream";
import { DEBUG_MODE, JITSI_PRIVATE_MODE, MAX_PER_GROUP, POSITION_DELAY } from "../../Enum/EnvironmentVariable";
import { TextureError } from "../../Exception/TextureError";
import type { UserMovedMessage } from "../../Messages/generated/messages_pb";
import { ProtobufClientUtils } from "../../Network/ProtobufClientUtils";
import { touchScreenManager } from "../../Touch/TouchScreenManager";
import { urlManager } from "../../Url/UrlManager";
import { audioManager } from "../../WebRtc/AudioManager";
import { coWebsiteManager } from "../../WebRtc/CoWebsiteManager";
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
import { jitsiFactory } from "../../WebRtc/JitsiFactory";
import { Queue } from "queue-typescript";
import {
AUDIO_LOOP_PROPERTY,
AUDIO_VOLUME_PROPERTY,
@@ -39,15 +27,21 @@ import {
TRIGGER_WEBSITE_PROPERTIES,
WEBSITE_MESSAGE_PROPERTIES,
} from "../../WebRtc/LayoutManager";
import { coWebsiteManager } from "../../WebRtc/CoWebsiteManager";
import type { UserMovedMessage } from "../../Messages/generated/messages_pb";
import { ProtobufClientUtils } from "../../Network/ProtobufClientUtils";
import type { RoomConnection } from "../../Connexion/RoomConnection";
import { Room } from "../../Connexion/Room";
import { jitsiFactory } from "../../WebRtc/JitsiFactory";
import { urlManager } from "../../Url/UrlManager";
import { audioManager } from "../../WebRtc/AudioManager";
import { TextureError } from "../../Exception/TextureError";
import { localUserStore } from "../../Connexion/LocalUserStore";
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
import { mediaManager } from "../../WebRtc/MediaManager";
import { SimplePeer, UserSimplePeerInterface } from "../../WebRtc/SimplePeer";
import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager";
import { ChatModeIcon } from "../Components/ChatModeIcon";
import { SimplePeer } from "../../WebRtc/SimplePeer";
import { addLoader } from "../Components/Loader";
import { joystickBaseImg, joystickBaseKey, joystickThumbImg, joystickThumbKey } from "../Components/MobileJoystick";
import { OpenChatIcon, openChatIconName } from "../Components/OpenChatIcon";
import { PresentationModeIcon } from "../Components/PresentationModeIcon";
import { TextUtils } from "../Components/TextUtils";
import { lazyLoadPlayerCharacterTextures, loadCustomTexture } from "../Entity/PlayerTexturesLoadingManager";
import { RemotePlayer } from "../Entity/RemotePlayer";
import type { ActionableItem } from "../Items/ActionableItem";
@@ -58,7 +52,6 @@ import type {
ITiledMapLayer,
ITiledMapLayerProperty,
ITiledMapObject,
ITiledMapTileLayer,
ITiledTileSet,
} from "../Map/ITiledMap";
import { MenuScene, MenuSceneName } from "../Menu/MenuScene";
@@ -66,13 +59,8 @@ import { PlayerAnimationDirections } from "../Player/Animation";
import { hasMovedEventName, Player, requestEmoteEventName } from "../Player/Player";
import { ErrorSceneName } from "../Reconnecting/ErrorScene";
import { ReconnectingSceneName } from "../Reconnecting/ReconnectingScene";
import { waScaleManager } from "../Services/WaScaleManager";
import { PinchManager } from "../UserInput/PinchManager";
import { UserInputManager } from "../UserInput/UserInputManager";
import type { AddPlayerInterface } from "./AddPlayerInterface";
import { DEPTH_OVERLAY_INDEX } from "./DepthIndexes";
import { DirtyScene } from "./DirtyScene";
import { EmoteManager } from "./EmoteManager";
import { gameManager } from "./GameManager";
import { GameMap } from "./GameMap";
import { PlayerMovement } from "./PlayerMovement";
@@ -83,12 +71,22 @@ 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 { worldFullMessageStream } from "../../Connexion/WorldFullMessageStream";
import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager";
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";
import { EmoteManager } from "./EmoteManager";
import EVENT_TYPE = Phaser.Scenes.Events;
import RenderTexture = Phaser.GameObjects.RenderTexture;
import Tilemap = Phaser.Tilemaps.Tilemap;
import type { HasPlayerMovedEvent } from "../../Api/Events/HasPlayerMovedEvent";
import AnimatedTiles from "phaser-animated-tiles";
import { StartPositionCalculator } from "./StartPositionCalculator";
import { soundManager } from "./SoundManager";
import { peerStore, screenSharingPeerStore } from "../../Stores/PeerStore";
import { videoFocusStore } from "../../Stores/VideoFocusStore";
@@ -129,8 +127,6 @@ interface DeleteGroupEventInterface {
groupId: number;
}
const defaultStartLayerName = "start";
export class GameScene extends DirtyScene {
Terrains: Array<Phaser.Tilemaps.Tileset>;
CurrentPlayer!: Player;
@@ -141,8 +137,6 @@ export class GameScene extends DirtyScene {
mapFile!: ITiledMap;
animatedTiles!: AnimatedTiles;
groups: Map<number, Sprite>;
startX!: number;
startY!: number;
circleTexture!: CanvasTexture;
circleRedTexture!: CanvasTexture;
pendingEvents: Queue<
@@ -194,7 +188,6 @@ export class GameScene extends DirtyScene {
private outlinedItem: ActionableItem | null = null;
public userInputManager!: UserInputManager;
private isReconnecting: boolean | undefined = undefined;
private startLayerName!: string | null;
private openChatIcon!: OpenChatIcon;
private playerName!: string;
private characterLayers!: string[];
@@ -206,6 +199,7 @@ export class GameScene extends DirtyScene {
private mapTransitioning: boolean = false; //used to prevent transitions happenning at the same time.
private emoteManager!: EmoteManager;
private preloading: boolean = true;
startPositionCalculator!: StartPositionCalculator;
constructor(private room: Room, MapUrlFile: string, customKey?: string | undefined) {
super({
@@ -426,7 +420,6 @@ export class GameScene extends DirtyScene {
gameManager.gameSceneIsCreated(this);
urlManager.pushRoomIdToUrl(this.room);
this.startLayerName = urlManager.getStartLayerNameFromUrl();
if (touchScreenManager.supportTouchScreen) {
this.pinchManager = new PinchManager(this);
@@ -489,7 +482,12 @@ export class GameScene extends DirtyScene {
this.loadNextGame(exitUrl);
});
this.initStartXAndStartY();
this.startPositionCalculator = new StartPositionCalculator(
this.gameMap,
this.mapFile,
this.initPosition,
urlManager.getStartLayerNameFromUrl()
);
//add entities
this.Objects = new Array<Phaser.Physics.Arcade.Sprite>();
@@ -586,8 +584,7 @@ export class GameScene extends DirtyScene {
this.playerName,
this.characterLayers,
{
x: this.startX,
y: this.startY,
...this.startPositionCalculator.startPosition,
},
{
left: camera.scrollX,
@@ -995,8 +992,8 @@ export class GameScene extends DirtyScene {
})
);
this.iframeSubscriptionList.push(
iframeListener.enablePlayerControlStream.subscribe(()=>{
this.iframeSubscriptionList.push(
iframeListener.enablePlayerControlStream.subscribe(() => {
this.userInputManager.restoreControls();
})
);
@@ -1053,16 +1050,21 @@ export class GameScene extends DirtyScene {
})
);
iframeListener.registerAnswerer('getState', () => {
return {
mapUrl: this.MapUrlFile,
startLayerName: this.startPositionCalculator.startLayerName,
uuid: localUserStore.getLocalUser()?.uuid,
nickname: localUserStore.getName(),
roomId: this.RoomId,
tags: this.connection ? this.connection.getAllTags() : [],
};
});
this.iframeSubscriptionList.push(
iframeListener.gameStateStream.subscribe(() => {
iframeListener.sendGameStateEvent({
mapUrl: this.MapUrlFile,
startLayerName: this.startLayerName,
uuid: localUserStore.getLocalUser()?.uuid,
nickname: localUserStore.getName(),
roomId: this.RoomId,
tags: this.connection ? this.connection.getAllTags() : [],
});
iframeListener.setTilesStream.subscribe((eventTiles) => {
for (const eventTile of eventTiles) {
this.gameMap.putTile(eventTile.tile, eventTile.x, eventTile.y, eventTile.layer);
}
})
);
@@ -1104,11 +1106,11 @@ export class GameScene extends DirtyScene {
console.warn('Could not find layer "' + layerName + '" when calling setProperty');
return;
}
const property = (layer.properties as ITiledMapLayerProperty[])?.find(
(property) => property.name === propertyName
);
if (property === undefined) {
if (layer.properties === undefined) {
layer.properties = [];
}
const property = layer.properties.find((property) => property.name === propertyName);
if (property === undefined) {
layer.properties.push({ name: propertyName, type: typeof propertyValue, value: propertyValue });
return;
}
@@ -1134,7 +1136,9 @@ export class GameScene extends DirtyScene {
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);
if (hash) {
urlManager.pushStartLayerNameToUrl(hash);
}
const menuScene: MenuScene = this.scene.get(MenuSceneName) as MenuScene;
menuScene.reset();
if (roomId !== this.scene.key) {
@@ -1148,9 +1152,9 @@ export class GameScene extends DirtyScene {
this.scene.start(roomId);
} else {
//if the exit points to the current map, we simply teleport the user back to the startLayer
this.initPositionFromLayerName(hash || defaultStartLayerName);
this.CurrentPlayer.x = this.startX;
this.CurrentPlayer.y = this.startY;
this.startPositionCalculator.initPositionFromLayerName(hash, hash);
this.CurrentPlayer.x = this.startPositionCalculator.startPosition.x;
this.CurrentPlayer.y = this.startPositionCalculator.startPosition.y;
setTimeout(() => (this.mapTransitioning = false), 500);
}
}
@@ -1176,6 +1180,7 @@ export class GameScene extends DirtyScene {
this.emoteManager.destroy();
this.peerStoreUnsubscribe();
this.biggestAvailableAreaStoreUnsubscribe();
iframeListener.unregisterAnswerer('getState');
mediaManager.hideGameOverlay();
@@ -1197,46 +1202,6 @@ export class GameScene extends DirtyScene {
this.MapPlayersByKey = new Map<number, RemotePlayer>();
}
private initStartXAndStartY() {
// If there is an init position passed
if (this.initPosition !== null) {
this.startX = this.initPosition.x;
this.startY = this.initPosition.y;
} else {
// Now, let's find the start layer
if (this.startLayerName) {
this.initPositionFromLayerName(this.startLayerName);
}
if (this.startX === undefined) {
// If we have no start layer specified or if the hash passed does not exist, let's go with the default start position.
this.initPositionFromLayerName(defaultStartLayerName);
}
}
// Still no start position? Something is wrong with the map, we need a "start" layer.
if (this.startX === undefined) {
console.warn(
'This map is missing a layer named "start" that contains the available default start positions.'
);
// Let's start in the middle of the map
this.startX = this.mapFile.width * 16;
this.startY = this.mapFile.height * 16;
}
}
private initPositionFromLayerName(layerName: string) {
for (const layer of this.gameMap.flatLayers) {
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 {
return this.getProperty(layer, "exitUrl") as string | undefined;
}
@@ -1248,10 +1213,6 @@ export class GameScene extends DirtyScene {
return this.getProperty(layer, "exitSceneUrl") as string | undefined;
}
private isStartLayer(layer: ITiledMapLayer): boolean {
return this.getProperty(layer, "startLayer") == true;
}
private getScriptUrls(map: ITiledMap): string[] {
return (this.getProperties(map, "script") as string[]).map((script) =>
new URL(script, this.MapUrlFile).toString()
@@ -1289,33 +1250,6 @@ export class GameScene extends DirtyScene {
return gameManager.loadMap(room, this.scene).catch(() => {});
}
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");
}
const possibleStartPositions: PositionInterface[] = [];
tiles.forEach((objectKey: number, key: number) => {
if (objectKey === 0) {
return;
}
const y = Math.floor(key / layer.width);
const x = key % layer.width;
possibleStartPositions.push({ x: x * this.mapFile.tilewidth, y: y * this.mapFile.tilewidth });
});
// Get a value at random amongst allowed values
if (possibleStartPositions.length === 0) {
console.warn('The start layer "' + layer.name + '" for this map is empty.');
return {
x: 0,
y: 0,
};
}
// Choose one of the available start positions at random amongst the list of available start positions.
return possibleStartPositions[Math.floor(Math.random() * possibleStartPositions.length)];
}
//todo: in a dedicated class/function?
initCamera() {
this.cameras.main.setBounds(0, 0, this.Map.widthInPixels, this.Map.heightInPixels);
@@ -1348,8 +1282,8 @@ export class GameScene extends DirtyScene {
try {
this.CurrentPlayer = new Player(
this,
this.startX,
this.startY,
this.startPositionCalculator.startPosition.x,
this.startPositionCalculator.startPosition.y,
this.playerName,
texturesPromise,
PlayerAnimationDirections.Down,
+15 -7
View File
@@ -1,10 +1,14 @@
import { MAX_EXTRAPOLATION_TIME } from "../../Enum/EnvironmentVariable";
import type { PositionInterface } from "../../Connexion/ConnexionModels";
import type { HasPlayerMovedEvent } from '../../Api/Events/HasPlayerMovedEvent';
import type { HasPlayerMovedEvent } from "../../Api/Events/HasPlayerMovedEvent";
export class PlayerMovement {
public constructor(private startPosition: PositionInterface, private startTick: number, private endPosition: HasPlayerMovedEvent, private endTick: number) {
}
public constructor(
private startPosition: PositionInterface,
private startTick: number,
private endPosition: HasPlayerMovedEvent,
private endTick: number
) {}
public isOutdated(tick: number): boolean {
//console.log(tick, this.endTick, MAX_EXTRAPOLATION_TIME)
@@ -24,14 +28,18 @@ export class PlayerMovement {
return this.endPosition;
}
const x = (this.endPosition.x - this.startPosition.x) * ((tick - this.startTick) / (this.endTick - this.startTick)) + this.startPosition.x;
const y = (this.endPosition.y - this.startPosition.y) * ((tick - this.startTick) / (this.endTick - this.startTick)) + this.startPosition.y;
const x =
(this.endPosition.x - this.startPosition.x) * ((tick - this.startTick) / (this.endTick - this.startTick)) +
this.startPosition.x;
const y =
(this.endPosition.y - this.startPosition.y) * ((tick - this.startTick) / (this.endTick - this.startTick)) +
this.startPosition.y;
//console.log('Computed position ', x, y)
return {
x,
y,
direction: this.endPosition.direction,
moving: true
}
moving: true,
};
}
}
@@ -2,7 +2,7 @@
* This class is in charge of computing the position of all players.
* Player movement is delayed by 200ms so position depends on ticks.
*/
import type { HasPlayerMovedEvent } from '../../Api/Events/HasPlayerMovedEvent';
import type { HasPlayerMovedEvent } from "../../Api/Events/HasPlayerMovedEvent";
import type { PlayerMovement } from "./PlayerMovement";
export class PlayersPositionInterpolator {
@@ -24,7 +24,7 @@ export class PlayersPositionInterpolator {
this.playerMovements.delete(userId);
}
//console.log("moving")
positions.set(userId, playerMovement.getPosition(tick))
positions.set(userId, playerMovement.getPosition(tick));
});
return positions;
}
@@ -0,0 +1,127 @@
import type { PositionInterface } from "../../Connexion/ConnexionModels";
import type { ITiledMap, ITiledMapLayer, ITiledMapLayerProperty, ITiledMapTileLayer } from "../Map/ITiledMap";
import type { GameMap } from "./GameMap";
const defaultStartLayerName = "start";
export class StartPositionCalculator {
public startPosition!: PositionInterface;
constructor(
private readonly gameMap: GameMap,
private readonly mapFile: ITiledMap,
private readonly initPosition: PositionInterface | null,
public readonly startLayerName: string | null
) {
this.initStartXAndStartY();
}
private initStartXAndStartY() {
// If there is an init position passed
if (this.initPosition !== null) {
this.startPosition = this.initPosition;
} else {
// Now, let's find the start layer
if (this.startLayerName) {
this.initPositionFromLayerName(this.startLayerName, this.startLayerName);
}
if (this.startPosition === undefined) {
// If we have no start layer specified or if the hash passed does not exist, let's go with the default start position.
this.initPositionFromLayerName(defaultStartLayerName, this.startLayerName);
}
}
// Still no start position? Something is wrong with the map, we need a "start" layer.
if (this.startPosition === undefined) {
console.warn(
'This map is missing a layer named "start" that contains the available default start positions.'
);
// Let's start in the middle of the map
this.startPosition = {
x: this.mapFile.width * 16,
y: this.mapFile.height * 16,
};
}
}
/**
*
* @param selectedLayer this is always the layer that is selected with the hash in the url
* @param selectedOrDefaultLayer this can also be the {defaultStartLayerName} if the {selectedLayer} didnt yield any start points
*/
public initPositionFromLayerName(selectedOrDefaultLayer: string | null, selectedLayer: string | null) {
if (!selectedOrDefaultLayer) {
selectedOrDefaultLayer = defaultStartLayerName;
}
for (const layer of this.gameMap.flatLayers) {
if (
(selectedOrDefaultLayer === layer.name || layer.name.endsWith("/" + selectedOrDefaultLayer)) &&
layer.type === "tilelayer" &&
(selectedOrDefaultLayer === defaultStartLayerName || this.isStartLayer(layer))
) {
const startPosition = this.startUser(layer, selectedLayer);
this.startPosition = {
x: startPosition.x + this.mapFile.tilewidth / 2,
y: startPosition.y + this.mapFile.tileheight / 2,
};
}
}
}
private isStartLayer(layer: ITiledMapLayer): boolean {
return this.getProperty(layer, "startLayer") == true;
}
/**
*
* @param selectedLayer this is always the layer that is selected with the hash in the url
* @param selectedOrDefaultLayer this can also be the default layer if the {selectedLayer} didnt yield any start points
*/
private startUser(selectedOrDefaultLayer: ITiledMapTileLayer, selectedLayer: string | null): PositionInterface {
const tiles = selectedOrDefaultLayer.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");
}
const possibleStartPositions: PositionInterface[] = [];
tiles.forEach((objectKey: number, key: number) => {
if (objectKey === 0) {
return;
}
const y = Math.floor(key / selectedOrDefaultLayer.width);
const x = key % selectedOrDefaultLayer.width;
if (selectedLayer && this.gameMap.hasStartTile) {
const properties = this.gameMap.getPropertiesForIndex(objectKey);
if (
!properties.length ||
!properties.some((property) => property.name == "start" && property.value == selectedLayer)
) {
return;
}
}
possibleStartPositions.push({ x: x * this.mapFile.tilewidth, y: y * this.mapFile.tilewidth });
});
// Get a value at random amongst allowed values
if (possibleStartPositions.length === 0) {
console.warn('The start layer "' + selectedOrDefaultLayer.name + '" for this map is empty.');
return {
x: 0,
y: 0,
};
}
// Choose one of the available start positions at random amongst the list of available start positions.
return possibleStartPositions[Math.floor(Math.random() * possibleStartPositions.length)];
}
private getProperty(layer: ITiledMapLayer | ITiledMap, name: string): string | boolean | number | undefined {
const properties: ITiledMapLayerProperty[] | undefined = layer.properties;
if (!properties) {
return undefined;
}
const obj = properties.find(
(property: ITiledMapLayerProperty) => property.name.toLowerCase() === name.toLowerCase()
);
if (obj === undefined) {
return undefined;
}
return obj.value;
}
}