merged develop
This commit is contained in:
commit
99ffb7b450
177 changed files with 5904 additions and 2328 deletions
178
front/src/Phaser/Game/CameraManager.ts
Normal file
178
front/src/Phaser/Game/CameraManager.ts
Normal file
|
@ -0,0 +1,178 @@
|
|||
import { Easing } from "../../types";
|
||||
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
|
||||
import type { Box } from "../../WebRtc/LayoutManager";
|
||||
import type { Player } from "../Player/Player";
|
||||
import type { WaScaleManager } from "../Services/WaScaleManager";
|
||||
import type { GameScene } from "./GameScene";
|
||||
|
||||
export enum CameraMode {
|
||||
Free = "Free",
|
||||
Follow = "Follow",
|
||||
Focus = "Focus",
|
||||
}
|
||||
|
||||
export class CameraManager extends Phaser.Events.EventEmitter {
|
||||
private scene: GameScene;
|
||||
private camera: Phaser.Cameras.Scene2D.Camera;
|
||||
private cameraBounds: { x: number; y: number };
|
||||
private waScaleManager: WaScaleManager;
|
||||
|
||||
private cameraMode: CameraMode = CameraMode.Free;
|
||||
|
||||
private restoreZoomTween?: Phaser.Tweens.Tween;
|
||||
private startFollowTween?: Phaser.Tweens.Tween;
|
||||
|
||||
private cameraFollowTarget?: { x: number; y: number };
|
||||
|
||||
constructor(scene: GameScene, cameraBounds: { x: number; y: number }, waScaleManager: WaScaleManager) {
|
||||
super();
|
||||
this.scene = scene;
|
||||
|
||||
this.camera = scene.cameras.main;
|
||||
this.cameraBounds = cameraBounds;
|
||||
|
||||
this.waScaleManager = waScaleManager;
|
||||
|
||||
this.initCamera();
|
||||
|
||||
this.bindEventHandlers();
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
this.scene.game.events.off("wa-scale-manager:refresh-focus-on-target");
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
public getCamera(): Phaser.Cameras.Scene2D.Camera {
|
||||
return this.camera;
|
||||
}
|
||||
|
||||
public enterFocusMode(
|
||||
focusOn: { x: number; y: number; width: number; height: number },
|
||||
margin: number = 0,
|
||||
duration: number = 1000
|
||||
): void {
|
||||
this.setCameraMode(CameraMode.Focus);
|
||||
this.waScaleManager.saveZoom();
|
||||
this.waScaleManager.setFocusTarget(focusOn);
|
||||
|
||||
this.restoreZoomTween?.stop();
|
||||
this.startFollowTween?.stop();
|
||||
const marginMult = 1 + margin;
|
||||
const targetZoomModifier = this.waScaleManager.getTargetZoomModifierFor(
|
||||
focusOn.width * marginMult,
|
||||
focusOn.height * marginMult
|
||||
);
|
||||
const currentZoomModifier = this.waScaleManager.zoomModifier;
|
||||
const zoomModifierChange = targetZoomModifier - currentZoomModifier;
|
||||
this.camera.stopFollow();
|
||||
this.cameraFollowTarget = undefined;
|
||||
this.camera.pan(
|
||||
focusOn.x + focusOn.width * 0.5 * marginMult,
|
||||
focusOn.y + focusOn.height * 0.5 * marginMult,
|
||||
duration,
|
||||
Easing.SineEaseOut,
|
||||
true,
|
||||
(camera, progress, x, y) => {
|
||||
this.waScaleManager.zoomModifier = currentZoomModifier + progress * zoomModifierChange;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public leaveFocusMode(player: Player): void {
|
||||
this.waScaleManager.setFocusTarget();
|
||||
this.startFollow(player, 1000);
|
||||
this.restoreZoom(1000);
|
||||
}
|
||||
|
||||
public startFollow(target: object | Phaser.GameObjects.GameObject, duration: number = 0): void {
|
||||
this.cameraFollowTarget = target as { x: number; y: number };
|
||||
this.setCameraMode(CameraMode.Follow);
|
||||
if (duration === 0) {
|
||||
this.camera.startFollow(target, true);
|
||||
return;
|
||||
}
|
||||
const oldPos = { x: this.camera.scrollX, y: this.camera.scrollY };
|
||||
this.startFollowTween = this.scene.tweens.addCounter({
|
||||
from: 0,
|
||||
to: 1,
|
||||
duration,
|
||||
ease: Easing.SineEaseOut,
|
||||
onUpdate: (tween: Phaser.Tweens.Tween) => {
|
||||
if (!this.cameraFollowTarget) {
|
||||
return;
|
||||
}
|
||||
const shiftX =
|
||||
(this.cameraFollowTarget.x - this.camera.worldView.width * 0.5 - oldPos.x) * tween.getValue();
|
||||
const shiftY =
|
||||
(this.cameraFollowTarget.y - this.camera.worldView.height * 0.5 - oldPos.y) * tween.getValue();
|
||||
this.camera.setScroll(oldPos.x + shiftX, oldPos.y + shiftY);
|
||||
},
|
||||
onComplete: () => {
|
||||
this.camera.startFollow(target, true);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public updateCameraOffset(array: Box): void {
|
||||
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:
|
||||
|
||||
this.camera.setFollowOffset(
|
||||
((xCenter - game.offsetWidth / 2) * window.devicePixelRatio) / this.scene.scale.zoom,
|
||||
((yCenter - game.offsetHeight / 2) * window.devicePixelRatio) / this.scene.scale.zoom
|
||||
);
|
||||
}
|
||||
|
||||
public isCameraLocked(): boolean {
|
||||
return this.cameraMode === CameraMode.Focus;
|
||||
}
|
||||
|
||||
private setCameraMode(mode: CameraMode): void {
|
||||
if (this.cameraMode === mode) {
|
||||
return;
|
||||
}
|
||||
this.cameraMode = mode;
|
||||
}
|
||||
|
||||
private restoreZoom(duration: number = 0): void {
|
||||
if (duration === 0) {
|
||||
this.waScaleManager.zoomModifier = this.waScaleManager.getSaveZoom();
|
||||
return;
|
||||
}
|
||||
this.restoreZoomTween?.stop();
|
||||
this.restoreZoomTween = this.scene.tweens.addCounter({
|
||||
from: this.waScaleManager.zoomModifier,
|
||||
to: this.waScaleManager.getSaveZoom(),
|
||||
duration,
|
||||
ease: Easing.SineEaseOut,
|
||||
onUpdate: (tween: Phaser.Tweens.Tween) => {
|
||||
this.waScaleManager.zoomModifier = tween.getValue();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private initCamera() {
|
||||
this.camera = this.scene.cameras.main;
|
||||
this.camera.setBounds(0, 0, this.cameraBounds.x, this.cameraBounds.y);
|
||||
}
|
||||
|
||||
private bindEventHandlers(): void {
|
||||
this.scene.game.events.on(
|
||||
"wa-scale-manager:refresh-focus-on-target",
|
||||
(focusOn: { x: number; y: number; width: number; height: number }) => {
|
||||
if (!focusOn) {
|
||||
return;
|
||||
}
|
||||
this.camera.centerOn(focusOn.x + focusOn.width * 0.5, focusOn.y + focusOn.height * 0.5);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,8 +1,15 @@
|
|||
import type { ITiledMap, ITiledMapLayer, ITiledMapProperty } from "../Map/ITiledMap";
|
||||
import type {
|
||||
ITiledMap,
|
||||
ITiledMapLayer,
|
||||
ITiledMapObject,
|
||||
ITiledMapObjectLayer,
|
||||
ITiledMapProperty,
|
||||
} from "../Map/ITiledMap";
|
||||
import { flattenGroupLayersMap } from "../Map/LayersFlattener";
|
||||
import TilemapLayer = Phaser.Tilemaps.TilemapLayer;
|
||||
import { DEPTH_OVERLAY_INDEX } from "./DepthIndexes";
|
||||
import { GameMapProperties } from "./GameMapProperties";
|
||||
import { MathUtils } from "../../Utils/MathUtils";
|
||||
|
||||
export type PropertyChangeCallback = (
|
||||
newValue: string | number | boolean | undefined,
|
||||
|
@ -15,24 +22,48 @@ export type layerChangeCallback = (
|
|||
allLayersOnNewPosition: Array<ITiledMapLayer>
|
||||
) => void;
|
||||
|
||||
export type zoneChangeCallback = (
|
||||
zonesChangedByAction: Array<ITiledMapObject>,
|
||||
allZonesOnNewPosition: Array<ITiledMapObject>
|
||||
) => void;
|
||||
|
||||
/**
|
||||
* A wrapper around a ITiledMap interface to provide additional capabilities.
|
||||
* It is used to handle layer properties.
|
||||
*/
|
||||
export class GameMap {
|
||||
// oldKey is the index of the previous tile.
|
||||
/**
|
||||
* oldKey is the index of the previous tile.
|
||||
*/
|
||||
private oldKey: number | undefined;
|
||||
// key is the index of the current tile.
|
||||
/**
|
||||
* key is the index of the current tile.
|
||||
*/
|
||||
private key: number | undefined;
|
||||
/**
|
||||
* oldPosition is the previous position of the player.
|
||||
*/
|
||||
private oldPosition: { x: number; y: number } | undefined;
|
||||
/**
|
||||
* position is the current position of the player.
|
||||
*/
|
||||
private position: { x: number; y: number } | undefined;
|
||||
|
||||
private lastProperties = new Map<string, string | boolean | number>();
|
||||
private propertiesChangeCallbacks = new Map<string, Array<PropertyChangeCallback>>();
|
||||
|
||||
private enterLayerCallbacks = Array<layerChangeCallback>();
|
||||
private leaveLayerCallbacks = Array<layerChangeCallback>();
|
||||
private enterZoneCallbacks = Array<zoneChangeCallback>();
|
||||
private leaveZoneCallbacks = Array<zoneChangeCallback>();
|
||||
|
||||
private tileNameMap = new Map<string, number>();
|
||||
|
||||
private tileSetPropertyMap: { [tile_index: number]: Array<ITiledMapProperty> } = {};
|
||||
public readonly flatLayers: ITiledMapLayer[];
|
||||
public readonly tiledObjects: ITiledMapObject[];
|
||||
public readonly phaserLayers: TilemapLayer[] = [];
|
||||
public readonly zones: ITiledMapObject[] = [];
|
||||
|
||||
public exitUrls: Array<string> = [];
|
||||
|
||||
|
@ -44,6 +75,9 @@ export class GameMap {
|
|||
terrains: Array<Phaser.Tilemaps.Tileset>
|
||||
) {
|
||||
this.flatLayers = flattenGroupLayersMap(map);
|
||||
this.tiledObjects = this.getObjectsFromLayers(this.flatLayers);
|
||||
this.zones = this.tiledObjects.filter((object) => object.type === "zone");
|
||||
|
||||
let depth = -2;
|
||||
for (const layer of this.flatLayers) {
|
||||
if (layer.type === "tilelayer") {
|
||||
|
@ -88,6 +122,10 @@ export class GameMap {
|
|||
* This will trigger events if properties are changing.
|
||||
*/
|
||||
public setPosition(x: number, y: number) {
|
||||
this.oldPosition = this.position;
|
||||
this.position = { x, y };
|
||||
this.triggerZonesChange();
|
||||
|
||||
this.oldKey = this.key;
|
||||
|
||||
const xMap = Math.floor(x / this.map.tilewidth);
|
||||
|
@ -126,7 +164,7 @@ export class GameMap {
|
|||
}
|
||||
}
|
||||
|
||||
private triggerLayersChange() {
|
||||
private triggerLayersChange(): void {
|
||||
const layersByOldKey = this.oldKey ? this.getLayersByKey(this.oldKey) : [];
|
||||
const layersByNewKey = this.key ? this.getLayersByKey(this.key) : [];
|
||||
|
||||
|
@ -155,6 +193,53 @@ export class GameMap {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We use Tiled Objects with type "zone" as zones with defined x, y, width and height for easier event triggering.
|
||||
*/
|
||||
private triggerZonesChange(): void {
|
||||
const zonesByOldPosition = this.oldPosition
|
||||
? this.zones.filter((zone) => {
|
||||
if (!this.oldPosition) {
|
||||
return false;
|
||||
}
|
||||
return MathUtils.isOverlappingWithRectangle(this.oldPosition, zone);
|
||||
})
|
||||
: [];
|
||||
|
||||
const zonesByNewPosition = this.position
|
||||
? this.zones.filter((zone) => {
|
||||
if (!this.position) {
|
||||
return false;
|
||||
}
|
||||
return MathUtils.isOverlappingWithRectangle(this.position, zone);
|
||||
})
|
||||
: [];
|
||||
|
||||
const enterZones = new Set(zonesByNewPosition);
|
||||
const leaveZones = new Set(zonesByOldPosition);
|
||||
|
||||
enterZones.forEach((zone) => {
|
||||
if (leaveZones.has(zone)) {
|
||||
leaveZones.delete(zone);
|
||||
enterZones.delete(zone);
|
||||
}
|
||||
});
|
||||
|
||||
if (enterZones.size > 0) {
|
||||
const zonesArray = Array.from(enterZones);
|
||||
for (const callback of this.enterZoneCallbacks) {
|
||||
callback(zonesArray, zonesByNewPosition);
|
||||
}
|
||||
}
|
||||
|
||||
if (leaveZones.size > 0) {
|
||||
const zonesArray = Array.from(leaveZones);
|
||||
for (const callback of this.leaveZoneCallbacks) {
|
||||
callback(zonesArray, zonesByNewPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public getCurrentProperties(): Map<string, string | boolean | number> {
|
||||
return this.lastProperties;
|
||||
}
|
||||
|
@ -251,6 +336,20 @@ export class GameMap {
|
|||
this.leaveLayerCallbacks.push(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback called when the user moves inside another zone.
|
||||
*/
|
||||
public onEnterZone(callback: zoneChangeCallback) {
|
||||
this.enterZoneCallbacks.push(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback called when the user moves outside another zone.
|
||||
*/
|
||||
public onLeaveZone(callback: zoneChangeCallback) {
|
||||
this.leaveZoneCallbacks.push(callback);
|
||||
}
|
||||
|
||||
public findLayer(layerName: string): ITiledMapLayer | undefined {
|
||||
return this.flatLayers.find((layer) => layer.name === layerName);
|
||||
}
|
||||
|
@ -362,4 +461,17 @@ export class GameMap {
|
|||
this.trigger(oldPropName, oldPropValue, undefined, emptyProps);
|
||||
}
|
||||
}
|
||||
|
||||
private getObjectsFromLayers(layers: ITiledMapLayer[]): ITiledMapObject[] {
|
||||
const objects: ITiledMapObject[] = [];
|
||||
|
||||
const objectLayers = layers.filter((layer) => layer.type === "objectgroup");
|
||||
for (const objectLayer of objectLayers) {
|
||||
if (objectLayer.type === "objectgroup") {
|
||||
objects.push(...objectLayer.objects);
|
||||
}
|
||||
}
|
||||
|
||||
return objects;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import { scriptUtils } from "../../Api/ScriptUtils";
|
|||
import type { CoWebsite } from "../../WebRtc/CoWebsiteManager";
|
||||
import { coWebsiteManager } from "../../WebRtc/CoWebsiteManager";
|
||||
import { layoutManagerActionStore } from "../../Stores/LayoutManagerStore";
|
||||
import { localUserStore } from "../../Connexion/LocalUserStore";
|
||||
import { get } from "svelte/store";
|
||||
import { ON_ACTION_TRIGGER_BUTTON } from "../../WebRtc/LayoutManager";
|
||||
import type { ITiledMapLayer } from "../Map/ITiledMap";
|
||||
|
@ -33,7 +34,8 @@ export class GameMapPropertiesListener {
|
|||
}
|
||||
if (typeof newValue == "string" && newValue.length) {
|
||||
const openWebsiteTriggerValue = allProps.get(GameMapProperties.OPEN_WEBSITE_TRIGGER);
|
||||
if (openWebsiteTriggerValue && openWebsiteTriggerValue === ON_ACTION_TRIGGER_BUTTON) {
|
||||
const forceTrigger = localUserStore.getForceCowebsiteTrigger();
|
||||
if (forceTrigger || openWebsiteTriggerValue === ON_ACTION_TRIGGER_BUTTON) {
|
||||
let message = allProps.get(GameMapProperties.OPEN_WEBSITE_TRIGGER_MESSAGE);
|
||||
if (message === undefined) {
|
||||
message = "Press SPACE or touch here to open web site in new tab";
|
||||
|
@ -135,7 +137,8 @@ export class GameMapPropertiesListener {
|
|||
layoutManagerActionStore.removeAction(actionUuid);
|
||||
};
|
||||
|
||||
if (websiteTriggerProperty && websiteTriggerProperty === ON_ACTION_TRIGGER_BUTTON) {
|
||||
const forceTrigger = localUserStore.getForceCowebsiteTrigger();
|
||||
if (forceTrigger || websiteTriggerProperty === ON_ACTION_TRIGGER_BUTTON) {
|
||||
if (!websiteTriggerMessageProperty) {
|
||||
websiteTriggerMessageProperty = "Press SPACE or touch here to open web site";
|
||||
}
|
||||
|
|
|
@ -1,7 +1,54 @@
|
|||
import type { Subscription } from "rxjs";
|
||||
import AnimatedTiles from "phaser-animated-tiles";
|
||||
import { Queue } from "queue-typescript";
|
||||
import { get } from "svelte/store";
|
||||
|
||||
import { userMessageManager } from "../../Administration/UserMessageManager";
|
||||
import { iframeListener } from "../../Api/IframeListener";
|
||||
import { connectionManager } from "../../Connexion/ConnectionManager";
|
||||
import { CoWebsite, coWebsiteManager } from "../../WebRtc/CoWebsiteManager";
|
||||
import { urlManager } from "../../Url/UrlManager";
|
||||
import { mediaManager } from "../../WebRtc/MediaManager";
|
||||
import { UserInputManager } from "../UserInput/UserInputManager";
|
||||
import { gameManager } from "./GameManager";
|
||||
import { touchScreenManager } from "../../Touch/TouchScreenManager";
|
||||
import { PinchManager } from "../UserInput/PinchManager";
|
||||
import { waScaleManager } from "../Services/WaScaleManager";
|
||||
import { EmoteManager } from "./EmoteManager";
|
||||
import { soundManager } from "./SoundManager";
|
||||
import { SharedVariablesManager } from "./SharedVariablesManager";
|
||||
import { EmbeddedWebsiteManager } from "./EmbeddedWebsiteManager";
|
||||
|
||||
import { lazyLoadPlayerCharacterTextures, loadCustomTexture } from "../Entity/PlayerTexturesLoadingManager";
|
||||
import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager";
|
||||
import { ON_ACTION_TRIGGER_BUTTON } from "../../WebRtc/LayoutManager";
|
||||
import { iframeListener } from "../../Api/IframeListener";
|
||||
import { DEBUG_MODE, JITSI_PRIVATE_MODE, MAX_PER_GROUP, POSITION_DELAY } from "../../Enum/EnvironmentVariable";
|
||||
import { ProtobufClientUtils } from "../../Network/ProtobufClientUtils";
|
||||
import { Room } from "../../Connexion/Room";
|
||||
import { jitsiFactory } from "../../WebRtc/JitsiFactory";
|
||||
import { TextureError } from "../../Exception/TextureError";
|
||||
import { localUserStore } from "../../Connexion/LocalUserStore";
|
||||
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
|
||||
import { SimplePeer } from "../../WebRtc/SimplePeer";
|
||||
import { Loader } from "../Components/Loader";
|
||||
import { RemotePlayer } from "../Entity/RemotePlayer";
|
||||
import { SelectCharacterScene, SelectCharacterSceneName } from "../Login/SelectCharacterScene";
|
||||
import { PlayerAnimationDirections } from "../Player/Animation";
|
||||
import { hasMovedEventName, Player, requestEmoteEventName } from "../Player/Player";
|
||||
import { ErrorSceneName } from "../Reconnecting/ErrorScene";
|
||||
import { ReconnectingSceneName } from "../Reconnecting/ReconnectingScene";
|
||||
import { GameMap } from "./GameMap";
|
||||
import { PlayerMovement } from "./PlayerMovement";
|
||||
import { PlayersPositionInterpolator } from "./PlayersPositionInterpolator";
|
||||
import { worldFullMessageStream } from "../../Connexion/WorldFullMessageStream";
|
||||
import { DirtyScene } from "./DirtyScene";
|
||||
import { TextUtils } from "../Components/TextUtils";
|
||||
import { joystickBaseImg, joystickBaseKey, joystickThumbImg, joystickThumbKey } from "../Components/MobileJoystick";
|
||||
import { StartPositionCalculator } from "./StartPositionCalculator";
|
||||
import { PropertyUtils } from "../Map/PropertyUtils";
|
||||
import { GameMapPropertiesListener } from "./GameMapPropertiesListener";
|
||||
import { analyticsClient } from "../../Administration/AnalyticsClient";
|
||||
import { GameMapProperties } from "./GameMapProperties";
|
||||
import type {
|
||||
GroupCreatedUpdatedMessageInterface,
|
||||
MessageUserJoined,
|
||||
|
@ -12,86 +59,37 @@ import type {
|
|||
PositionInterface,
|
||||
RoomJoinedMessageInterface,
|
||||
} from "../../Connexion/ConnexionModels";
|
||||
import { DEBUG_MODE, JITSI_PRIVATE_MODE, MAX_PER_GROUP, POSITION_DELAY } from "../../Enum/EnvironmentVariable";
|
||||
|
||||
import { Queue } from "queue-typescript";
|
||||
import { Box, ON_ACTION_TRIGGER_BUTTON } from "../../WebRtc/LayoutManager";
|
||||
import { CoWebsite, 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 { TextureError } from "../../Exception/TextureError";
|
||||
import { localUserStore } from "../../Connexion/LocalUserStore";
|
||||
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
|
||||
import { mediaManager } from "../../WebRtc/MediaManager";
|
||||
import { SimplePeer } from "../../WebRtc/SimplePeer";
|
||||
import { Loader } from "../Components/Loader";
|
||||
import { lazyLoadPlayerCharacterTextures, loadCustomTexture } from "../Entity/PlayerTexturesLoadingManager";
|
||||
import { RemotePlayer } from "../Entity/RemotePlayer";
|
||||
import type { ActionableItem } from "../Items/ActionableItem";
|
||||
import type { ItemFactoryInterface } from "../Items/ItemFactoryInterface";
|
||||
import { SelectCharacterScene, SelectCharacterSceneName } from "../Login/SelectCharacterScene";
|
||||
import type { ITiledMap, ITiledMapLayer, ITiledMapProperty, ITiledMapObject, ITiledTileSet } from "../Map/ITiledMap";
|
||||
import { PlayerAnimationDirections } from "../Player/Animation";
|
||||
import { hasMovedEventName, Player, requestEmoteEventName } from "../Player/Player";
|
||||
import { ErrorSceneName } from "../Reconnecting/ErrorScene";
|
||||
import { ReconnectingSceneName } from "../Reconnecting/ReconnectingScene";
|
||||
import { UserInputManager } from "../UserInput/UserInputManager";
|
||||
import type { AddPlayerInterface } from "./AddPlayerInterface";
|
||||
import { gameManager } from "./GameManager";
|
||||
import { GameMap } from "./GameMap";
|
||||
import { PlayerMovement } from "./PlayerMovement";
|
||||
import { PlayersPositionInterpolator } from "./PlayersPositionInterpolator";
|
||||
import { CameraManager } from "./CameraManager";
|
||||
import type { HasPlayerMovedEvent } from "../../Api/Events/HasPlayerMovedEvent";
|
||||
import type { Character } from "../Entity/Character";
|
||||
|
||||
import { peerStore } from "../../Stores/PeerStore";
|
||||
import { biggestAvailableAreaStore } from "../../Stores/BiggestAvailableAreaStore";
|
||||
import { layoutManagerActionStore } from "../../Stores/LayoutManagerStore";
|
||||
import { playersStore } from "../../Stores/PlayersStore";
|
||||
import { emoteStore, emoteMenuStore } from "../../Stores/EmoteStore";
|
||||
import { userIsAdminStore } from "../../Stores/GameStore";
|
||||
import { contactPageStore } from "../../Stores/MenuStore";
|
||||
import type { WasCameraUpdatedEvent } from "../../Api/Events/WasCameraUpdatedEvent";
|
||||
import { audioManagerFileStore, audioManagerVisibilityStore } from "../../Stores/AudioManagerStore";
|
||||
|
||||
import EVENT_TYPE = Phaser.Scenes.Events;
|
||||
import Texture = Phaser.Textures.Texture;
|
||||
import Sprite = Phaser.GameObjects.Sprite;
|
||||
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 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";
|
||||
import { biggestAvailableAreaStore } from "../../Stores/BiggestAvailableAreaStore";
|
||||
import { SharedVariablesManager } from "./SharedVariablesManager";
|
||||
import { playersStore } from "../../Stores/PlayersStore";
|
||||
import { chatVisibilityStore } from "../../Stores/ChatStore";
|
||||
import { emoteStore, emoteMenuStore } from "../../Stores/EmoteStore";
|
||||
import {
|
||||
audioManagerFileStore,
|
||||
audioManagerVisibilityStore,
|
||||
audioManagerVolumeStore,
|
||||
} from "../../Stores/AudioManagerStore";
|
||||
import { PropertyUtils } from "../Map/PropertyUtils";
|
||||
import Tileset = Phaser.Tilemaps.Tileset;
|
||||
import { userIsAdminStore } from "../../Stores/GameStore";
|
||||
import { layoutManagerActionStore } from "../../Stores/LayoutManagerStore";
|
||||
import { EmbeddedWebsiteManager } from "./EmbeddedWebsiteManager";
|
||||
import { GameMapPropertiesListener } from "./GameMapPropertiesListener";
|
||||
import { analyticsClient } from "../../Administration/AnalyticsClient";
|
||||
import { get } from "svelte/store";
|
||||
import { contactPageStore } from "../../Stores/MenuStore";
|
||||
import { GameMapProperties } from "./GameMapProperties";
|
||||
import SpriteSheetFile = Phaser.Loader.FileTypes.SpriteSheetFile;
|
||||
import Camera = Phaser.Cameras.Scene2D.Camera;
|
||||
import type { WasCameraUpdatedEvent } from "../../Api/Events/WasCameraUpdatedEvent";
|
||||
|
||||
import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR;
|
||||
import { MapStore } from "../../Stores/Utils/MapStore";
|
||||
export interface GameSceneInitInterface {
|
||||
initPosition: PointInterface | null;
|
||||
reconnecting: boolean;
|
||||
|
@ -131,7 +129,7 @@ export class GameScene extends DirtyScene {
|
|||
Terrains: Array<Phaser.Tilemaps.Tileset>;
|
||||
CurrentPlayer!: Player;
|
||||
MapPlayers!: Phaser.Physics.Arcade.Group;
|
||||
MapPlayersByKey: Map<number, RemotePlayer> = new Map<number, RemotePlayer>();
|
||||
MapPlayersByKey: MapStore<number, RemotePlayer> = new MapStore<number, RemotePlayer>();
|
||||
Map!: Phaser.Tilemaps.Tilemap;
|
||||
Objects!: Array<Phaser.Physics.Arcade.Sprite>;
|
||||
mapFile!: ITiledMap;
|
||||
|
@ -200,6 +198,7 @@ export class GameScene extends DirtyScene {
|
|||
private pinchManager: PinchManager | undefined;
|
||||
private mapTransitioning: boolean = false; //used to prevent transitions happening at the same time.
|
||||
private emoteManager!: EmoteManager;
|
||||
private cameraManager!: CameraManager;
|
||||
private preloading: boolean = true;
|
||||
private startPositionCalculator!: StartPositionCalculator;
|
||||
private sharedVariablesManager!: SharedVariablesManager;
|
||||
|
@ -273,7 +272,9 @@ export class GameScene extends DirtyScene {
|
|||
// 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 base = new URL(window.location.href);
|
||||
base.pathname = "";
|
||||
const url = new URL(file.src, base.toString());
|
||||
const host = url.host.split(":")[0];
|
||||
if (
|
||||
window.location.protocol === "https:" &&
|
||||
|
@ -326,7 +327,6 @@ export class GameScene extends DirtyScene {
|
|||
this.onMapLoad(data);
|
||||
}
|
||||
|
||||
this.load.bitmapFont("main_font", "resources/fonts/arcade.png", "resources/fonts/arcade.xml");
|
||||
//eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(this.load as any).rexWebFont({
|
||||
custom: {
|
||||
|
@ -555,7 +555,13 @@ export class GameScene extends DirtyScene {
|
|||
this.createCurrentPlayer();
|
||||
this.removeAllRemotePlayers(); //cleanup the list of remote players in case the scene was rebooted
|
||||
|
||||
this.initCamera();
|
||||
this.cameraManager = new CameraManager(
|
||||
this,
|
||||
{ x: this.Map.widthInPixels, y: this.Map.heightInPixels },
|
||||
waScaleManager
|
||||
);
|
||||
biggestAvailableAreaStore.recompute();
|
||||
this.cameraManager.startFollow(this.CurrentPlayer);
|
||||
|
||||
this.animatedTiles.init(this.Map);
|
||||
this.events.on("tileanimationupdate", () => (this.dirty = true));
|
||||
|
@ -596,7 +602,7 @@ export class GameScene extends DirtyScene {
|
|||
|
||||
// From now, this game scene will be notified of reposition events
|
||||
this.biggestAvailableAreaStoreUnsubscribe = biggestAvailableAreaStore.subscribe((box) =>
|
||||
this.updateCameraOffset(box)
|
||||
this.cameraManager.updateCameraOffset(box)
|
||||
);
|
||||
|
||||
new GameMapPropertiesListener(this, this.gameMap).register();
|
||||
|
@ -649,7 +655,7 @@ export class GameScene extends DirtyScene {
|
|||
* Initializes the connection to Pusher.
|
||||
*/
|
||||
private connect(): void {
|
||||
const camera = this.cameras.main;
|
||||
const camera = this.cameraManager.getCamera();
|
||||
|
||||
connectionManager
|
||||
.connectToRoomSocket(
|
||||
|
@ -671,7 +677,6 @@ export class GameScene extends DirtyScene {
|
|||
this.connection = onConnect.connection;
|
||||
|
||||
playersStore.connectToRoomConnection(this.connection);
|
||||
|
||||
userIsAdminStore.set(this.connection.hasTag("admin"));
|
||||
|
||||
this.connection.onUserJoins((message: MessageUserJoined) => {
|
||||
|
@ -784,6 +789,42 @@ export class GameScene extends DirtyScene {
|
|||
iframeListener.sendLeaveLayerEvent(layer.name);
|
||||
});
|
||||
});
|
||||
|
||||
this.gameMap.onEnterZone((zones) => {
|
||||
for (const zone of zones) {
|
||||
const focusable = zone.properties?.find((property) => property.name === "focusable");
|
||||
if (focusable && focusable.value === true) {
|
||||
const zoomMargin = zone.properties?.find((property) => property.name === "zoom_margin");
|
||||
this.cameraManager.enterFocusMode(
|
||||
zone,
|
||||
zoomMargin ? Math.max(0, Number(zoomMargin.value)) : undefined
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
zones.forEach((zone) => {
|
||||
iframeListener.sendEnterZoneEvent(zone.name);
|
||||
});
|
||||
});
|
||||
|
||||
this.gameMap.onLeaveZone((zones) => {
|
||||
for (const zone of zones) {
|
||||
const focusable = zone.properties?.find((property) => property.name === "focusable");
|
||||
if (focusable && focusable.value === true) {
|
||||
this.cameraManager.leaveFocusMode(this.CurrentPlayer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
zones.forEach((zone) => {
|
||||
iframeListener.sendLeaveZoneEvent(zone.name);
|
||||
});
|
||||
});
|
||||
|
||||
// this.gameMap.onLeaveLayer((layers) => {
|
||||
// layers.forEach((layer) => {
|
||||
// iframeListener.sendLeaveLayerEvent(layer.name);
|
||||
// });
|
||||
// });
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -873,7 +914,8 @@ export class GameScene extends DirtyScene {
|
|||
};
|
||||
|
||||
const jitsiTriggerValue = allProps.get(GameMapProperties.JITSI_TRIGGER);
|
||||
if (jitsiTriggerValue && jitsiTriggerValue === ON_ACTION_TRIGGER_BUTTON) {
|
||||
const forceTrigger = localUserStore.getForceCowebsiteTrigger();
|
||||
if (forceTrigger || jitsiTriggerValue === ON_ACTION_TRIGGER_BUTTON) {
|
||||
let message = allProps.get(GameMapProperties.JITSI_TRIGGER_MESSAGE);
|
||||
if (message === undefined) {
|
||||
message = "Press SPACE or touch here to enter Jitsi Meet room";
|
||||
|
@ -1202,6 +1244,7 @@ ${escapedMessage}
|
|||
tags: this.connection ? this.connection.getAllTags() : [],
|
||||
variables: this.sharedVariablesManager.variables,
|
||||
playerVariables: localUserStore.getAllUserProperties(),
|
||||
userRoomToken: this.connection ? this.connection.userRoomToken : "",
|
||||
};
|
||||
});
|
||||
this.iframeSubscriptionList.push(
|
||||
|
@ -1427,6 +1470,7 @@ ${escapedMessage}
|
|||
this.userInputManager.destroy();
|
||||
this.pinchManager?.destroy();
|
||||
this.emoteManager.destroy();
|
||||
this.cameraManager.destroy();
|
||||
this.peerStoreUnsubscribe();
|
||||
this.emoteUnsubscribe();
|
||||
this.emoteMenuUnsubscribe();
|
||||
|
@ -1459,7 +1503,7 @@ ${escapedMessage}
|
|||
|
||||
this.MapPlayers.remove(player);
|
||||
});
|
||||
this.MapPlayersByKey = new Map<number, RemotePlayer>();
|
||||
this.MapPlayersByKey.clear();
|
||||
}
|
||||
|
||||
private getExitUrl(layer: ITiledMapLayer): string | undefined {
|
||||
|
@ -1517,13 +1561,6 @@ ${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);
|
||||
biggestAvailableAreaStore.recompute();
|
||||
}
|
||||
|
||||
createCollisionWithPlayer() {
|
||||
//add collision layer
|
||||
for (const phaserLayer of this.gameMap.phaserLayers) {
|
||||
|
@ -1917,23 +1954,6 @@ ${escapedMessage}
|
|||
biggestAvailableAreaStore.recompute();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(array: Box): void {
|
||||
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:
|
||||
|
||||
this.cameras.main.setFollowOffset(
|
||||
((xCenter - game.offsetWidth / 2) * window.devicePixelRatio) / this.scale.zoom,
|
||||
((yCenter - game.offsetHeight / 2) * window.devicePixelRatio) / this.scale.zoom
|
||||
);
|
||||
}
|
||||
|
||||
public startJitsi(roomName: string, jwt?: string): void {
|
||||
const allProps = this.gameMap.getCurrentProperties();
|
||||
const jitsiConfig = this.safeParseJSONstring(
|
||||
|
@ -2002,6 +2022,9 @@ ${escapedMessage}
|
|||
}
|
||||
|
||||
zoomByFactor(zoomFactor: number) {
|
||||
if (this.cameraManager.isCameraLocked()) {
|
||||
return;
|
||||
}
|
||||
waScaleManager.zoomModifier *= zoomFactor;
|
||||
biggestAvailableAreaStore.recompute();
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type { RoomConnection } from "../../Connexion/RoomConnection";
|
||||
import { iframeListener } from "../../Api/IframeListener";
|
||||
import type { GameMap } from "./GameMap";
|
||||
import type { ITiledMapLayer, ITiledMapObject, ITiledMapObjectLayer } from "../Map/ITiledMap";
|
||||
import type { ITiledMapLayer, ITiledMapObject } from "../Map/ITiledMap";
|
||||
import { GameMapProperties } from "./GameMapProperties";
|
||||
import type { SetVariableEvent } from "../../Api/Events/SetVariableEvent";
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue