Merge branch 'develop' of github.com:thecodingmachine/workadventure
This commit is contained in:
commit
3b3807d474
2
.github/workflows/end_to_end_tests.yml
vendored
2
.github/workflows/end_to_end_tests.yml
vendored
@ -32,7 +32,7 @@ jobs:
|
||||
mode: start
|
||||
github-token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
|
||||
ec2-image-id: ami-094dbcc53250a2480
|
||||
ec2-instance-type: t3.xlarge
|
||||
ec2-instance-type: m5.2xlarge
|
||||
subnet-id: subnet-0ac40025f559df1bc
|
||||
security-group-id: sg-0e36e96e3b8ed2d64
|
||||
#iam-role-name: my-role-name # optional, requires additional permissions
|
||||
|
1
front/.gitignore
vendored
1
front/.gitignore
vendored
@ -6,6 +6,7 @@
|
||||
/dist/main.*.css.map
|
||||
/dist/tests/
|
||||
/yarn-error.log
|
||||
/package-lock.json
|
||||
/dist/webpack.config.js
|
||||
/dist/webpack.config.js.map
|
||||
/dist/src
|
||||
|
@ -48,6 +48,7 @@
|
||||
"axios": "^0.21.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"deep-copy-ts": "^0.5.0",
|
||||
"easystarjs": "^0.4.4",
|
||||
"generic-type-guard": "^3.2.0",
|
||||
"google-protobuf": "^3.13.0",
|
||||
"nes.css": "^2.3.0",
|
||||
|
12
front/src/Interfaces/UserInputHandlerInterface.ts
Normal file
12
front/src/Interfaces/UserInputHandlerInterface.ts
Normal file
@ -0,0 +1,12 @@
|
||||
export interface UserInputHandlerInterface {
|
||||
handleMouseWheelEvent: (
|
||||
pointer: Phaser.Input.Pointer,
|
||||
gameObjects: Phaser.GameObjects.GameObject[],
|
||||
deltaX: number,
|
||||
deltaY: number,
|
||||
deltaZ: number
|
||||
) => void;
|
||||
handlePointerUpEvent: (pointer: Phaser.Input.Pointer, gameObjects: Phaser.GameObjects.GameObject[]) => void;
|
||||
handlePointerDownEvent: (pointer: Phaser.Input.Pointer, gameObjects: Phaser.GameObjects.GameObject[]) => void;
|
||||
handleSpaceKeyUpEvent: (event: Event) => Event;
|
||||
}
|
@ -40,30 +40,22 @@ export class MobileJoystick extends VirtualJoystick {
|
||||
this.visible = false;
|
||||
this.enable = false;
|
||||
|
||||
this.scene.input.on("pointerdown", (pointer: Phaser.Input.Pointer) => {
|
||||
if (!pointer.wasTouch) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Let's only display the joystick if there is one finger on the screen
|
||||
if ((pointer.event as TouchEvent).touches.length === 1) {
|
||||
this.x = pointer.x;
|
||||
this.y = pointer.y;
|
||||
this.visible = true;
|
||||
this.enable = true;
|
||||
} else {
|
||||
this.visible = false;
|
||||
this.enable = false;
|
||||
}
|
||||
});
|
||||
this.scene.input.on("pointerup", () => {
|
||||
this.visible = false;
|
||||
this.enable = false;
|
||||
});
|
||||
this.resizeCallback = this.resize.bind(this);
|
||||
this.scene.scale.on(Phaser.Scale.Events.RESIZE, this.resizeCallback);
|
||||
}
|
||||
|
||||
public showAt(x: number, y: number): void {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.visible = true;
|
||||
this.enable = true;
|
||||
}
|
||||
|
||||
public hide(): void {
|
||||
this.visible = false;
|
||||
this.enable = false;
|
||||
}
|
||||
|
||||
public resize() {
|
||||
this.base.setDisplaySize(this.getDisplaySizeByElement(baseSize), this.getDisplaySizeByElement(baseSize));
|
||||
this.thumb.setDisplaySize(this.getDisplaySizeByElement(thumbSize), this.getDisplaySizeByElement(thumbSize));
|
||||
|
@ -1,10 +1,4 @@
|
||||
import type {
|
||||
ITiledMap,
|
||||
ITiledMapLayer,
|
||||
ITiledMapObject,
|
||||
ITiledMapObjectLayer,
|
||||
ITiledMapProperty,
|
||||
} from "../Map/ITiledMap";
|
||||
import type { ITiledMap, ITiledMapLayer, ITiledMapObject, ITiledMapProperty } from "../Map/ITiledMap";
|
||||
import { flattenGroupLayersMap } from "../Map/LayersFlattener";
|
||||
import TilemapLayer = Phaser.Tilemaps.TilemapLayer;
|
||||
import { DEPTH_OVERLAY_INDEX } from "./DepthIndexes";
|
||||
@ -120,8 +114,24 @@ export class GameMap {
|
||||
return [];
|
||||
}
|
||||
|
||||
private getLayersByKey(key: number): Array<ITiledMapLayer> {
|
||||
return this.flatLayers.filter((flatLayer) => flatLayer.type === "tilelayer" && flatLayer.data[key] !== 0);
|
||||
public getCollisionsGrid(): number[][] {
|
||||
const grid: number[][] = [];
|
||||
for (let y = 0; y < this.map.height; y += 1) {
|
||||
const row: number[] = [];
|
||||
for (let x = 0; x < this.map.width; x += 1) {
|
||||
row.push(this.isCollidingAt(x, y) ? 1 : 0);
|
||||
}
|
||||
grid.push(row);
|
||||
}
|
||||
return grid;
|
||||
}
|
||||
|
||||
public getTileDimensions(): { width: number; height: number } {
|
||||
return { width: this.map.tilewidth, height: this.map.tileheight };
|
||||
}
|
||||
|
||||
public getTileIndexAt(x: number, y: number): { x: number; y: number } {
|
||||
return { x: Math.floor(x / this.map.tilewidth), y: Math.floor(y / this.map.tileheight) };
|
||||
}
|
||||
|
||||
/**
|
||||
@ -149,6 +159,167 @@ export class GameMap {
|
||||
this.triggerLayersChange();
|
||||
}
|
||||
|
||||
public getCurrentProperties(): Map<string, string | boolean | number> {
|
||||
return this.lastProperties;
|
||||
}
|
||||
|
||||
public getMap(): ITiledMap {
|
||||
return this.map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback called when the user moves to a tile where the property propName is different from the last tile the user was on.
|
||||
*/
|
||||
public onPropertyChange(propName: string, callback: PropertyChangeCallback) {
|
||||
let callbacksArray = this.propertiesChangeCallbacks.get(propName);
|
||||
if (callbacksArray === undefined) {
|
||||
callbacksArray = new Array<PropertyChangeCallback>();
|
||||
this.propertiesChangeCallbacks.set(propName, callbacksArray);
|
||||
}
|
||||
callbacksArray.push(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback called when the user moves inside another layer.
|
||||
*/
|
||||
public onEnterLayer(callback: layerChangeCallback) {
|
||||
this.enterLayerCallbacks.push(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback called when the user moves outside another layer.
|
||||
*/
|
||||
public onLeaveLayer(callback: layerChangeCallback) {
|
||||
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);
|
||||
}
|
||||
|
||||
public findPhaserLayer(layerName: string): TilemapLayer | undefined {
|
||||
return this.phaserLayers.find((layer) => layer.layer.name === layerName);
|
||||
}
|
||||
|
||||
public findPhaserLayers(groupName: string): TilemapLayer[] {
|
||||
return this.phaserLayers.filter((l) => l.layer.name.includes(groupName));
|
||||
}
|
||||
|
||||
public addTerrain(terrain: Phaser.Tilemaps.Tileset): void {
|
||||
for (const phaserLayer of this.phaserLayers) {
|
||||
phaserLayer.tileset.push(terrain);
|
||||
}
|
||||
}
|
||||
|
||||
public putTile(tile: string | number | null, x: number, y: number, layer: string): void {
|
||||
const phaserLayer = this.findPhaserLayer(layer);
|
||||
if (phaserLayer) {
|
||||
if (tile === null) {
|
||||
phaserLayer.putTileAt(-1, x, y);
|
||||
return;
|
||||
}
|
||||
const tileIndex = this.getIndexForTileType(tile);
|
||||
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 === GameMapProperties.COLLIDES && property.value) {
|
||||
phaserTile.setCollision(true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.error("The tile '" + tile + "' that you want to place doesn't exist.");
|
||||
}
|
||||
} else {
|
||||
console.error("The layer '" + layer + "' does not exist (or is not a tilelaye).");
|
||||
}
|
||||
}
|
||||
|
||||
public getLayerProperty(
|
||||
layerName: string,
|
||||
propertyName: string
|
||||
) : string | number | undefined | boolean {
|
||||
const layer = this.findLayer(layerName);
|
||||
if (layer === undefined) {
|
||||
console.warn('Could not find layer "' + layerName + '" when calling setProperty');
|
||||
return;
|
||||
}
|
||||
if (layer.properties === undefined) {
|
||||
layer.properties = [];
|
||||
}
|
||||
const property = layer.properties.find((property) => property.name === propertyName);
|
||||
return property?.value;
|
||||
}
|
||||
|
||||
public setLayerProperty(
|
||||
layerName: string,
|
||||
propertyName: string,
|
||||
propertyValue: string | number | undefined | boolean
|
||||
) {
|
||||
const layer = this.findLayer(layerName);
|
||||
if (layer === undefined) {
|
||||
console.warn('Could not find layer "' + layerName + '" when calling setProperty');
|
||||
return;
|
||||
}
|
||||
if (layer.properties === undefined) {
|
||||
layer.properties = [];
|
||||
}
|
||||
const property = layer.properties.find((property) => property.name === propertyName);
|
||||
if (property === undefined) {
|
||||
if (propertyValue === undefined) {
|
||||
return;
|
||||
}
|
||||
layer.properties.push({ name: propertyName, type: typeof propertyValue, value: propertyValue });
|
||||
return;
|
||||
}
|
||||
if (propertyValue === undefined) {
|
||||
const index = layer.properties.indexOf(property);
|
||||
layer.properties.splice(index, 1);
|
||||
}
|
||||
property.value = propertyValue;
|
||||
|
||||
this.triggerAllProperties();
|
||||
this.triggerLayersChange();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger all the callbacks (used when exiting a map)
|
||||
*/
|
||||
public triggerExitCallbacks(): void {
|
||||
const emptyProps = new Map<string, string | boolean | number>();
|
||||
for (const [oldPropName, oldPropValue] of this.lastProperties.entries()) {
|
||||
// We found a property that disappeared
|
||||
this.trigger(oldPropName, oldPropValue, undefined, emptyProps);
|
||||
}
|
||||
}
|
||||
|
||||
private getLayersByKey(key: number): Array<ITiledMapLayer> {
|
||||
return this.flatLayers.filter((flatLayer) => flatLayer.type === "tilelayer" && flatLayer.data[key] !== 0);
|
||||
}
|
||||
|
||||
private isCollidingAt(x: number, y: number): boolean {
|
||||
for (const layer of this.phaserLayers) {
|
||||
if (layer.getTileAt(x, y)?.properties[GameMapProperties.COLLIDES]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private triggerAllProperties(): void {
|
||||
const newProps = this.getProperties(this.key ?? 0);
|
||||
const oldProps = this.lastProperties;
|
||||
@ -247,10 +418,6 @@ export class GameMap {
|
||||
}
|
||||
}
|
||||
|
||||
public getCurrentProperties(): Map<string, string | boolean | number> {
|
||||
return this.lastProperties;
|
||||
}
|
||||
|
||||
private getProperties(key: number): Map<string, string | boolean | number> {
|
||||
const properties = new Map<string, string | boolean | number>();
|
||||
|
||||
@ -292,10 +459,6 @@ export class GameMap {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public getMap(): ITiledMap {
|
||||
return this.map;
|
||||
}
|
||||
|
||||
private getTileProperty(index: number): Array<ITiledMapProperty> {
|
||||
if (this.tileSetPropertyMap[index]) {
|
||||
return this.tileSetPropertyMap[index];
|
||||
@ -317,64 +480,6 @@ export class GameMap {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback called when the user moves to a tile where the property propName is different from the last tile the user was on.
|
||||
*/
|
||||
public onPropertyChange(propName: string, callback: PropertyChangeCallback) {
|
||||
let callbacksArray = this.propertiesChangeCallbacks.get(propName);
|
||||
if (callbacksArray === undefined) {
|
||||
callbacksArray = new Array<PropertyChangeCallback>();
|
||||
this.propertiesChangeCallbacks.set(propName, callbacksArray);
|
||||
}
|
||||
callbacksArray.push(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback called when the user moves inside another layer.
|
||||
*/
|
||||
public onEnterLayer(callback: layerChangeCallback) {
|
||||
this.enterLayerCallbacks.push(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback called when the user moves outside another layer.
|
||||
*/
|
||||
public onLeaveLayer(callback: layerChangeCallback) {
|
||||
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);
|
||||
}
|
||||
|
||||
public findPhaserLayer(layerName: string): TilemapLayer | undefined {
|
||||
return this.phaserLayers.find((layer) => layer.layer.name === layerName);
|
||||
}
|
||||
|
||||
public findPhaserLayers(groupName: string): TilemapLayer[] {
|
||||
return this.phaserLayers.filter((l) => l.layer.name.includes(groupName));
|
||||
}
|
||||
|
||||
public addTerrain(terrain: Phaser.Tilemaps.Tileset): void {
|
||||
for (const phaserLayer of this.phaserLayers) {
|
||||
phaserLayer.tileset.push(terrain);
|
||||
}
|
||||
}
|
||||
|
||||
private putTileInFlatLayer(index: number, x: number, y: number, layer: string): void {
|
||||
const fLayer = this.findLayer(layer);
|
||||
if (fLayer == undefined) {
|
||||
@ -396,30 +501,6 @@ export class GameMap {
|
||||
fLayer.data[x + y * fLayer.width] = index;
|
||||
}
|
||||
|
||||
public putTile(tile: string | number | null, x: number, y: number, layer: string): void {
|
||||
const phaserLayer = this.findPhaserLayer(layer);
|
||||
if (phaserLayer) {
|
||||
if (tile === null) {
|
||||
phaserLayer.putTileAt(-1, x, y);
|
||||
return;
|
||||
}
|
||||
const tileIndex = this.getIndexForTileType(tile);
|
||||
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 === GameMapProperties.COLLIDES && property.value) {
|
||||
phaserTile.setCollision(true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.error("The tile '" + tile + "' that you want to place doesn't exist.");
|
||||
}
|
||||
} else {
|
||||
console.error("The layer '" + layer + "' does not exist (or is not a tilelaye).");
|
||||
}
|
||||
}
|
||||
|
||||
private getIndexForTileType(tile: string | number): number | undefined {
|
||||
if (typeof tile == "number") {
|
||||
return tile;
|
||||
@ -427,64 +508,6 @@ export class GameMap {
|
||||
return this.tileNameMap.get(tile);
|
||||
}
|
||||
|
||||
public getLayerProperty(
|
||||
layerName: string,
|
||||
propertyName: string
|
||||
) : string | number | undefined | boolean {
|
||||
const layer = this.findLayer(layerName);
|
||||
if (layer === undefined) {
|
||||
console.warn('Could not find layer "' + layerName + '" when calling setProperty');
|
||||
return;
|
||||
}
|
||||
if (layer.properties === undefined) {
|
||||
layer.properties = [];
|
||||
}
|
||||
const property = layer.properties.find((property) => property.name === propertyName);
|
||||
return property?.value;
|
||||
}
|
||||
|
||||
public setLayerProperty(
|
||||
layerName: string,
|
||||
propertyName: string,
|
||||
propertyValue: string | number | undefined | boolean
|
||||
) {
|
||||
const layer = this.findLayer(layerName);
|
||||
if (layer === undefined) {
|
||||
console.warn('Could not find layer "' + layerName + '" when calling setProperty');
|
||||
return;
|
||||
}
|
||||
if (layer.properties === undefined) {
|
||||
layer.properties = [];
|
||||
}
|
||||
const property = layer.properties.find((property) => property.name === propertyName);
|
||||
if (property === undefined) {
|
||||
if (propertyValue === undefined) {
|
||||
return;
|
||||
}
|
||||
layer.properties.push({ name: propertyName, type: typeof propertyValue, value: propertyValue });
|
||||
return;
|
||||
}
|
||||
if (propertyValue === undefined) {
|
||||
const index = layer.properties.indexOf(property);
|
||||
layer.properties.splice(index, 1);
|
||||
}
|
||||
property.value = propertyValue;
|
||||
|
||||
this.triggerAllProperties();
|
||||
this.triggerLayersChange();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger all the callbacks (used when exiting a map)
|
||||
*/
|
||||
public triggerExitCallbacks(): void {
|
||||
const emptyProps = new Map<string, string | boolean | number>();
|
||||
for (const [oldPropName, oldPropValue] of this.lastProperties.entries()) {
|
||||
// We found a property that disappeared
|
||||
this.trigger(oldPropName, oldPropValue, undefined, emptyProps);
|
||||
}
|
||||
}
|
||||
|
||||
private getObjectsFromLayers(layers: ITiledMapLayer[]): ITiledMapObject[] {
|
||||
const objects: ITiledMapObject[] = [];
|
||||
|
||||
|
@ -48,9 +48,9 @@ import { PropertyUtils } from "../Map/PropertyUtils";
|
||||
import { GameMapPropertiesListener } from "./GameMapPropertiesListener";
|
||||
import { analyticsClient } from "../../Administration/AnalyticsClient";
|
||||
import { GameMapProperties } from "./GameMapProperties";
|
||||
import { PathfindingManager } from "../../Utils/PathfindingManager";
|
||||
import type {
|
||||
GroupCreatedUpdatedMessageInterface,
|
||||
MessageUserJoined,
|
||||
MessageUserMovedInterface,
|
||||
MessageUserPositionInterface,
|
||||
OnConnectInterface,
|
||||
@ -66,7 +66,6 @@ import type { ITiledMap, ITiledMapLayer, ITiledMapProperty, ITiledMapObject, ITi
|
||||
import type { AddPlayerInterface } from "./AddPlayerInterface";
|
||||
import { CameraManager, CameraManagerEvent, CameraManagerEventCameraUpdateData } 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";
|
||||
@ -89,9 +88,9 @@ import SpriteSheetFile = Phaser.Loader.FileTypes.SpriteSheetFile;
|
||||
import { deepCopy } from "deep-copy-ts";
|
||||
import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR;
|
||||
import { MapStore } from "../../Stores/Utils/MapStore";
|
||||
import { followUsersColorStore, followUsersStore } from "../../Stores/FollowStore";
|
||||
import { getColorRgbFromHue } from "../../WebRtc/ColorGenerator";
|
||||
import { followUsersColorStore } from "../../Stores/FollowStore";
|
||||
import Camera = Phaser.Cameras.Scene2D.Camera;
|
||||
import { GameSceneUserInputHandler } from "../UserInput/GameSceneUserInputHandler";
|
||||
|
||||
export interface GameSceneInitInterface {
|
||||
initPosition: PointInterface | null;
|
||||
@ -203,6 +202,7 @@ export class GameScene extends DirtyScene {
|
||||
private mapTransitioning: boolean = false; //used to prevent transitions happening at the same time.
|
||||
private emoteManager!: EmoteManager;
|
||||
private cameraManager!: CameraManager;
|
||||
private pathfindingManager!: PathfindingManager;
|
||||
private preloading: boolean = true;
|
||||
private startPositionCalculator!: StartPositionCalculator;
|
||||
private sharedVariablesManager!: SharedVariablesManager;
|
||||
@ -566,7 +566,7 @@ export class GameScene extends DirtyScene {
|
||||
this.MapPlayers = this.physics.add.group({ immovable: true });
|
||||
|
||||
//create input to move
|
||||
this.userInputManager = new UserInputManager(this);
|
||||
this.userInputManager = new UserInputManager(this, new GameSceneUserInputHandler(this));
|
||||
mediaManager.setUserInputManager(this.userInputManager);
|
||||
|
||||
if (localUserStore.getFullscreen()) {
|
||||
@ -585,6 +585,8 @@ export class GameScene extends DirtyScene {
|
||||
{ x: this.Map.widthInPixels, y: this.Map.heightInPixels },
|
||||
waScaleManager
|
||||
);
|
||||
|
||||
this.pathfindingManager = new PathfindingManager(this, this.gameMap.getCollisionsGrid());
|
||||
biggestAvailableAreaStore.recompute();
|
||||
this.cameraManager.startFollowPlayer(this.CurrentPlayer);
|
||||
|
||||
@ -622,10 +624,6 @@ export class GameScene extends DirtyScene {
|
||||
scriptPromises.push(iframeListener.registerScript(script, !disableModuleMode));
|
||||
}
|
||||
|
||||
this.userInputManager.spaceEvent(() => {
|
||||
this.outlinedItem?.activate();
|
||||
});
|
||||
|
||||
this.reposition();
|
||||
|
||||
// From now, this game scene will be notified of reposition events
|
||||
@ -694,6 +692,10 @@ export class GameScene extends DirtyScene {
|
||||
);
|
||||
}
|
||||
|
||||
public activateOutlinedItem(): void {
|
||||
this.outlinedItem?.activate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the connection to Pusher.
|
||||
*/
|
||||
@ -1721,7 +1723,6 @@ export class GameScene extends DirtyScene {
|
||||
texturesPromise,
|
||||
PlayerAnimationDirections.Down,
|
||||
false,
|
||||
this.userInputManager,
|
||||
this.companion,
|
||||
this.companion !== null ? lazyLoadCompanionResource(this.load, this.companion) : undefined
|
||||
);
|
||||
@ -1841,7 +1842,7 @@ export class GameScene extends DirtyScene {
|
||||
update(time: number, delta: number): void {
|
||||
this.dirty = false;
|
||||
this.currentTick = time;
|
||||
this.CurrentPlayer.moveUser(delta);
|
||||
this.CurrentPlayer.moveUser(delta, this.userInputManager.getEventListForGameTick());
|
||||
|
||||
// Let's handle all events
|
||||
while (this.pendingEvents.length !== 0) {
|
||||
@ -2245,4 +2246,16 @@ export class GameScene extends DirtyScene {
|
||||
this.scene.stop(this.scene.key);
|
||||
this.scene.remove(this.scene.key);
|
||||
}
|
||||
|
||||
public getGameMap(): GameMap {
|
||||
return this.gameMap;
|
||||
}
|
||||
|
||||
public getCameraManager(): CameraManager {
|
||||
return this.cameraManager;
|
||||
}
|
||||
|
||||
public getPathfindingManager(): PathfindingManager {
|
||||
return this.pathfindingManager;
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { PlayerAnimationDirections } from "./Animation";
|
||||
import type { GameScene } from "../Game/GameScene";
|
||||
import { ActiveEventList, UserInputEvent, UserInputManager } from "../UserInput/UserInputManager";
|
||||
import { ActiveEventList, UserInputEvent } from "../UserInput/UserInputManager";
|
||||
import { Character } from "../Entity/Character";
|
||||
import type { RemotePlayer } from "../Entity/RemotePlayer";
|
||||
|
||||
import { get } from "svelte/store";
|
||||
import { userMovingStore } from "../../Stores/GameStore";
|
||||
@ -12,6 +11,8 @@ export const hasMovedEventName = "hasMoved";
|
||||
export const requestEmoteEventName = "requestEmote";
|
||||
|
||||
export class Player extends Character {
|
||||
private pathToFollow?: { x: number; y: number }[];
|
||||
|
||||
constructor(
|
||||
Scene: GameScene,
|
||||
x: number,
|
||||
@ -20,7 +21,6 @@ export class Player extends Character {
|
||||
texturesPromise: Promise<string[]>,
|
||||
direction: PlayerAnimationDirections,
|
||||
moving: boolean,
|
||||
private userInputManager: UserInputManager,
|
||||
companion: string | null,
|
||||
companionTexturePromise?: Promise<string>
|
||||
) {
|
||||
@ -30,6 +30,55 @@ export class Player extends Character {
|
||||
this.getBody().setImmovable(false);
|
||||
}
|
||||
|
||||
public moveUser(delta: number, activeUserInputEvents: ActiveEventList): void {
|
||||
const state = get(followStateStore);
|
||||
const role = get(followRoleStore);
|
||||
|
||||
if (activeUserInputEvents.get(UserInputEvent.Follow)) {
|
||||
if (state === "off" && this.scene.groups.size > 0) {
|
||||
this.sendFollowRequest();
|
||||
} else if (state === "active") {
|
||||
followStateStore.set("ending");
|
||||
}
|
||||
}
|
||||
|
||||
if (this.pathToFollow && activeUserInputEvents.anyExcept(UserInputEvent.SpeedUp)) {
|
||||
this.pathToFollow = undefined;
|
||||
}
|
||||
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
if ((state === "active" || state === "ending") && role === "follower") {
|
||||
[x, y] = this.computeFollowMovement();
|
||||
}
|
||||
if (this.pathToFollow) {
|
||||
[x, y] = this.computeFollowPathMovement();
|
||||
}
|
||||
this.inputStep(activeUserInputEvents, x, y);
|
||||
}
|
||||
|
||||
public sendFollowRequest() {
|
||||
this.scene.connection?.emitFollowRequest();
|
||||
followRoleStore.set("leader");
|
||||
followStateStore.set("active");
|
||||
}
|
||||
|
||||
public startFollowing() {
|
||||
followStateStore.set("active");
|
||||
this.scene.connection?.emitFollowConfirmation();
|
||||
}
|
||||
|
||||
public setPathToFollow(path: { x: number; y: number }[]): void {
|
||||
// take collider offset into consideraton
|
||||
this.pathToFollow = this.adjustPathToFollowToColliderBounds(path);
|
||||
}
|
||||
|
||||
private adjustPathToFollowToColliderBounds(path: { x: number; y: number }[]): { x: number; y: number }[] {
|
||||
return path.map((step) => {
|
||||
return { x: step.x, y: step.y - this.getBody().offset.y };
|
||||
});
|
||||
}
|
||||
|
||||
private inputStep(activeEvents: ActiveEventList, x: number, y: number) {
|
||||
// Process input events
|
||||
if (activeEvents.get(UserInputEvent.MoveUp)) {
|
||||
@ -95,40 +144,29 @@ export class Player extends Character {
|
||||
if (distance < 2000) {
|
||||
return [0, 0];
|
||||
}
|
||||
const xMovement = xDistance / Math.sqrt(distance);
|
||||
const yMovement = yDistance / Math.sqrt(distance);
|
||||
return [xMovement, yMovement];
|
||||
return this.getMovementDirection(xDistance, yDistance, distance);
|
||||
}
|
||||
|
||||
public moveUser(delta: number): void {
|
||||
const activeEvents = this.userInputManager.getEventListForGameTick();
|
||||
const state = get(followStateStore);
|
||||
const role = get(followRoleStore);
|
||||
|
||||
if (activeEvents.get(UserInputEvent.Follow)) {
|
||||
if (state === "off" && this.scene.groups.size > 0) {
|
||||
this.sendFollowRequest();
|
||||
} else if (state === "active") {
|
||||
followStateStore.set("ending");
|
||||
private computeFollowPathMovement(): number[] {
|
||||
if (this.pathToFollow?.length === 0) {
|
||||
this.pathToFollow = undefined;
|
||||
}
|
||||
if (!this.pathToFollow) {
|
||||
return [0, 0];
|
||||
}
|
||||
const nextStep = this.pathToFollow[0];
|
||||
|
||||
// Compute movement direction
|
||||
const xDistance = nextStep.x - this.x;
|
||||
const yDistance = nextStep.y - this.y;
|
||||
const distance = Math.pow(xDistance, 2) + Math.pow(yDistance, 2);
|
||||
if (distance < 200) {
|
||||
this.pathToFollow.shift();
|
||||
}
|
||||
return this.getMovementDirection(xDistance, yDistance, distance);
|
||||
}
|
||||
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
if ((state === "active" || state === "ending") && role === "follower") {
|
||||
[x, y] = this.computeFollowMovement();
|
||||
}
|
||||
this.inputStep(activeEvents, x, y);
|
||||
}
|
||||
|
||||
public sendFollowRequest() {
|
||||
this.scene.connection?.emitFollowRequest();
|
||||
followRoleStore.set("leader");
|
||||
followStateStore.set("active");
|
||||
}
|
||||
|
||||
public startFollowing() {
|
||||
followStateStore.set("active");
|
||||
this.scene.connection?.emitFollowConfirmation();
|
||||
private getMovementDirection(xDistance: number, yDistance: number, distance: number): [number, number] {
|
||||
return [xDistance / Math.sqrt(distance), yDistance / Math.sqrt(distance)];
|
||||
}
|
||||
}
|
||||
|
58
front/src/Phaser/UserInput/GameSceneUserInputHandler.ts
Normal file
58
front/src/Phaser/UserInput/GameSceneUserInputHandler.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import type { UserInputHandlerInterface } from "../../Interfaces/UserInputHandlerInterface";
|
||||
import type { GameScene } from "../Game/GameScene";
|
||||
|
||||
export class GameSceneUserInputHandler implements UserInputHandlerInterface {
|
||||
private gameScene: GameScene;
|
||||
|
||||
constructor(gameScene: GameScene) {
|
||||
this.gameScene = gameScene;
|
||||
}
|
||||
|
||||
public handleMouseWheelEvent(
|
||||
pointer: Phaser.Input.Pointer,
|
||||
gameObjects: Phaser.GameObjects.GameObject[],
|
||||
deltaX: number,
|
||||
deltaY: number,
|
||||
deltaZ: number
|
||||
): void {
|
||||
this.gameScene.zoomByFactor(1 - (deltaY / 53) * 0.1);
|
||||
}
|
||||
|
||||
public handlePointerUpEvent(pointer: Phaser.Input.Pointer, gameObjects: Phaser.GameObjects.GameObject[]): void {
|
||||
if (pointer.rightButtonReleased() || pointer.getDuration() > 250) {
|
||||
return;
|
||||
}
|
||||
const camera = this.gameScene.getCameraManager().getCamera();
|
||||
const index = this.gameScene
|
||||
.getGameMap()
|
||||
.getTileIndexAt(pointer.x + camera.scrollX, pointer.y + camera.scrollY);
|
||||
const startTile = this.gameScene
|
||||
.getGameMap()
|
||||
.getTileIndexAt(this.gameScene.CurrentPlayer.x, this.gameScene.CurrentPlayer.y);
|
||||
this.gameScene
|
||||
.getPathfindingManager()
|
||||
.findPath(startTile, index, true)
|
||||
.then((path) => {
|
||||
const tileDimensions = this.gameScene.getGameMap().getTileDimensions();
|
||||
const pixelPath = path.map((step) => {
|
||||
return {
|
||||
x: step.x * tileDimensions.width + tileDimensions.width * 0.5,
|
||||
y: step.y * tileDimensions.height + tileDimensions.height * 0.5,
|
||||
};
|
||||
});
|
||||
// Remove first step as it is for the tile we are currently standing on
|
||||
pixelPath.shift();
|
||||
this.gameScene.CurrentPlayer.setPathToFollow(pixelPath);
|
||||
})
|
||||
.catch((reason) => {
|
||||
console.warn(reason);
|
||||
});
|
||||
}
|
||||
|
||||
public handlePointerDownEvent(pointer: Phaser.Input.Pointer, gameObjects: Phaser.GameObjects.GameObject[]): void {}
|
||||
|
||||
public handleSpaceKeyUpEvent(event: Event): Event {
|
||||
this.gameScene.activateOutlinedItem();
|
||||
return event;
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
import type { GameScene } from "../Game/GameScene";
|
||||
import { touchScreenManager } from "../../Touch/TouchScreenManager";
|
||||
import { MobileJoystick } from "../Components/MobileJoystick";
|
||||
import { enableUserInputsStore } from "../../Stores/UserInputStore";
|
||||
import type { Direction } from "phaser3-rex-plugins/plugins/virtualjoystick.js";
|
||||
import type { UserInputHandlerInterface } from "../../Interfaces/UserInputHandlerInterface";
|
||||
|
||||
interface UserInputManagerDatum {
|
||||
keyInstance: Phaser.Input.Keyboard.Key;
|
||||
@ -37,12 +37,21 @@ export class ActiveEventList {
|
||||
any(): boolean {
|
||||
return Array.from(this.eventMap.values()).reduce((accu, curr) => accu || curr, false);
|
||||
}
|
||||
anyExcept(...exceptions: UserInputEvent[]): boolean {
|
||||
const userInputEvents = Array.from(this.eventMap);
|
||||
for (const event of userInputEvents) {
|
||||
if (event[1] && !exceptions.includes(event[0])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//this class is responsible for catching user inputs and listing all active user actions at every game tick events.
|
||||
export class UserInputManager {
|
||||
private KeysCode!: UserInputManagerDatum[];
|
||||
private Scene: GameScene;
|
||||
private keysCode!: UserInputManagerDatum[];
|
||||
private scene: Phaser.Scene;
|
||||
private isInputDisabled: boolean;
|
||||
|
||||
private joystick!: MobileJoystick;
|
||||
@ -51,11 +60,15 @@ export class UserInputManager {
|
||||
private joystickForceAccuX = 0;
|
||||
private joystickForceAccuY = 0;
|
||||
|
||||
constructor(Scene: GameScene) {
|
||||
this.Scene = Scene;
|
||||
private userInputHandler: UserInputHandlerInterface;
|
||||
|
||||
constructor(scene: Phaser.Scene, userInputHandler: UserInputHandlerInterface) {
|
||||
this.scene = scene;
|
||||
this.userInputHandler = userInputHandler;
|
||||
|
||||
this.isInputDisabled = false;
|
||||
this.initKeyBoardEvent();
|
||||
this.initMouseWheel();
|
||||
this.bindInputEventHandlers();
|
||||
if (touchScreenManager.supportTouchScreen) {
|
||||
this.initVirtualJoystick();
|
||||
}
|
||||
@ -66,7 +79,7 @@ export class UserInputManager {
|
||||
}
|
||||
|
||||
initVirtualJoystick() {
|
||||
this.joystick = new MobileJoystick(this.Scene);
|
||||
this.joystick = new MobileJoystick(this.scene);
|
||||
this.joystick.on("update", () => {
|
||||
this.joystickForceAccuX = this.joystick.forceX ? this.joystickForceAccuX : 0;
|
||||
this.joystickForceAccuY = this.joystick.forceY ? this.joystickForceAccuY : 0;
|
||||
@ -92,80 +105,80 @@ export class UserInputManager {
|
||||
}
|
||||
|
||||
initKeyBoardEvent() {
|
||||
this.KeysCode = [
|
||||
this.keysCode = [
|
||||
{
|
||||
event: UserInputEvent.MoveUp,
|
||||
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Z, false),
|
||||
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Z, false),
|
||||
},
|
||||
{
|
||||
event: UserInputEvent.MoveUp,
|
||||
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.W, false),
|
||||
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.W, false),
|
||||
},
|
||||
{
|
||||
event: UserInputEvent.MoveLeft,
|
||||
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Q, false),
|
||||
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Q, false),
|
||||
},
|
||||
{
|
||||
event: UserInputEvent.MoveLeft,
|
||||
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A, false),
|
||||
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A, false),
|
||||
},
|
||||
{
|
||||
event: UserInputEvent.MoveDown,
|
||||
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S, false),
|
||||
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S, false),
|
||||
},
|
||||
{
|
||||
event: UserInputEvent.MoveRight,
|
||||
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D, false),
|
||||
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D, false),
|
||||
},
|
||||
|
||||
{
|
||||
event: UserInputEvent.MoveUp,
|
||||
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.UP, false),
|
||||
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.UP, false),
|
||||
},
|
||||
{
|
||||
event: UserInputEvent.MoveLeft,
|
||||
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.LEFT, false),
|
||||
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.LEFT, false),
|
||||
},
|
||||
{
|
||||
event: UserInputEvent.MoveDown,
|
||||
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.DOWN, false),
|
||||
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.DOWN, false),
|
||||
},
|
||||
{
|
||||
event: UserInputEvent.MoveRight,
|
||||
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.RIGHT, false),
|
||||
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.RIGHT, false),
|
||||
},
|
||||
|
||||
{
|
||||
event: UserInputEvent.SpeedUp,
|
||||
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SHIFT, false),
|
||||
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SHIFT, false),
|
||||
},
|
||||
|
||||
{
|
||||
event: UserInputEvent.Interact,
|
||||
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.E, false),
|
||||
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.E, false),
|
||||
},
|
||||
{
|
||||
event: UserInputEvent.Interact,
|
||||
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE, false),
|
||||
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE, false),
|
||||
},
|
||||
{
|
||||
event: UserInputEvent.Follow,
|
||||
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.F, false),
|
||||
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.F, false),
|
||||
},
|
||||
{
|
||||
event: UserInputEvent.Shout,
|
||||
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.F, false),
|
||||
keyInstance: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.F, false),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
clearAllListeners() {
|
||||
this.Scene.input.keyboard.removeAllListeners();
|
||||
this.scene.input.keyboard.removeAllListeners();
|
||||
}
|
||||
|
||||
//todo: should we also disable the joystick?
|
||||
disableControls() {
|
||||
this.Scene.input.keyboard.removeAllKeys();
|
||||
this.scene.input.keyboard.removeAllKeys();
|
||||
this.isInputDisabled = true;
|
||||
}
|
||||
|
||||
@ -201,7 +214,7 @@ export class UserInputManager {
|
||||
}
|
||||
});
|
||||
eventsMap.set(UserInputEvent.JoystickMove, this.joystickEvents.any());
|
||||
this.KeysCode.forEach((d) => {
|
||||
this.keysCode.forEach((d) => {
|
||||
if (d.keyInstance.isDown) {
|
||||
eventsMap.set(d.event, true);
|
||||
}
|
||||
@ -209,30 +222,60 @@ export class UserInputManager {
|
||||
return eventsMap;
|
||||
}
|
||||
|
||||
spaceEvent(callback: Function) {
|
||||
this.Scene.input.keyboard.on("keyup-SPACE", (event: Event) => {
|
||||
callback();
|
||||
return event;
|
||||
});
|
||||
}
|
||||
|
||||
addSpaceEventListner(callback: Function) {
|
||||
this.Scene.input.keyboard.addListener("keyup-SPACE", callback);
|
||||
this.scene.input.keyboard.addListener("keyup-SPACE", callback);
|
||||
}
|
||||
removeSpaceEventListner(callback: Function) {
|
||||
this.Scene.input.keyboard.removeListener("keyup-SPACE", callback);
|
||||
this.scene.input.keyboard.removeListener("keyup-SPACE", callback);
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
this.joystick?.destroy();
|
||||
}
|
||||
|
||||
private initMouseWheel() {
|
||||
this.Scene.input.on(
|
||||
"wheel",
|
||||
(pointer: unknown, gameObjects: unknown, deltaX: number, deltaY: number, deltaZ: number) => {
|
||||
this.Scene.zoomByFactor(1 - (deltaY / 53) * 0.1);
|
||||
private bindInputEventHandlers() {
|
||||
this.scene.input.on(
|
||||
Phaser.Input.Events.POINTER_WHEEL,
|
||||
(
|
||||
pointer: Phaser.Input.Pointer,
|
||||
gameObjects: Phaser.GameObjects.GameObject[],
|
||||
deltaX: number,
|
||||
deltaY: number,
|
||||
deltaZ: number
|
||||
) => {
|
||||
if (this.isInputDisabled) {
|
||||
return;
|
||||
}
|
||||
this.userInputHandler.handleMouseWheelEvent(pointer, gameObjects, deltaX, deltaY, deltaZ);
|
||||
}
|
||||
);
|
||||
|
||||
this.scene.input.on(
|
||||
Phaser.Input.Events.POINTER_UP,
|
||||
(pointer: Phaser.Input.Pointer, gameObjects: Phaser.GameObjects.GameObject[]) => {
|
||||
this.joystick.hide();
|
||||
this.userInputHandler.handlePointerUpEvent(pointer, gameObjects);
|
||||
}
|
||||
);
|
||||
|
||||
this.scene.input.on(
|
||||
Phaser.Input.Events.POINTER_DOWN,
|
||||
(pointer: Phaser.Input.Pointer, gameObjects: Phaser.GameObjects.GameObject[]) => {
|
||||
if (!pointer.wasTouch) {
|
||||
return;
|
||||
}
|
||||
this.userInputHandler.handlePointerDownEvent(pointer, gameObjects);
|
||||
// Let's only display the joystick if there is one finger on the screen
|
||||
if ((pointer.event as TouchEvent).touches.length === 1) {
|
||||
this.joystick.showAt(pointer.x, pointer.y);
|
||||
} else {
|
||||
this.joystick.hide();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
this.scene.input.keyboard.on("keyup-SPACE", (event: Event) => {
|
||||
this.userInputHandler.handleSpaceKeyUpEvent(event);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,13 @@ import { writable } from "svelte/store";
|
||||
import { playersStore } from "./PlayersStore";
|
||||
import type { PlayerInterface } from "../Phaser/Game/PlayerInterface";
|
||||
import { iframeListener } from "../Api/IframeListener";
|
||||
import { Subject } from "rxjs";
|
||||
|
||||
export const chatVisibilityStore = writable(false);
|
||||
export const chatInputFocusStore = writable(false);
|
||||
|
||||
export const newChatMessageStore = writable<string | null>(null);
|
||||
const _newChatMessageSubject = new Subject<string>();
|
||||
export const newChatMessageSubject = _newChatMessageSubject.asObservable();
|
||||
|
||||
export enum ChatMessageTypes {
|
||||
text = 1,
|
||||
@ -67,10 +69,9 @@ function createChatMessagesStore() {
|
||||
});
|
||||
},
|
||||
addPersonnalMessage(text: string) {
|
||||
//post message iframe listener
|
||||
iframeListener.sendUserInputChat(text);
|
||||
|
||||
newChatMessageStore.set(text);
|
||||
_newChatMessageSubject.next(text);
|
||||
update((list) => {
|
||||
const lastMessage = list[list.length - 1];
|
||||
if (lastMessage && lastMessage.type === ChatMessageTypes.me && lastMessage.text) {
|
||||
@ -83,7 +84,6 @@ function createChatMessagesStore() {
|
||||
});
|
||||
}
|
||||
|
||||
iframeListener.sendUserInputChat(text);
|
||||
return list;
|
||||
});
|
||||
},
|
||||
|
@ -22,4 +22,13 @@ export class MathUtils {
|
||||
public static isBetween(value: number, min: number, max: number): boolean {
|
||||
return value >= min && value <= max;
|
||||
}
|
||||
|
||||
public static distanceBetween(
|
||||
p1: { x: number; y: number },
|
||||
p2: { x: number; y: number },
|
||||
squared: boolean = true
|
||||
): number {
|
||||
const distance = Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2);
|
||||
return squared ? Math.sqrt(distance) : distance;
|
||||
}
|
||||
}
|
||||
|
109
front/src/Utils/PathfindingManager.ts
Normal file
109
front/src/Utils/PathfindingManager.ts
Normal file
@ -0,0 +1,109 @@
|
||||
import * as EasyStar from "easystarjs";
|
||||
import { MathUtils } from "./MathUtils";
|
||||
|
||||
export class PathfindingManager {
|
||||
private scene: Phaser.Scene;
|
||||
|
||||
private easyStar;
|
||||
private grid: number[][];
|
||||
|
||||
constructor(scene: Phaser.Scene, collisionsGrid: number[][]) {
|
||||
this.scene = scene;
|
||||
|
||||
this.easyStar = new EasyStar.js();
|
||||
this.easyStar.enableDiagonals();
|
||||
|
||||
this.grid = collisionsGrid;
|
||||
this.setEasyStarGrid(collisionsGrid);
|
||||
}
|
||||
|
||||
public async findPath(
|
||||
start: { x: number; y: number },
|
||||
end: { x: number; y: number },
|
||||
tryFindingNearestAvailable: boolean = false
|
||||
): Promise<{ x: number; y: number }[]> {
|
||||
let endPoints: { x: number; y: number }[] = [end];
|
||||
if (tryFindingNearestAvailable) {
|
||||
endPoints = [
|
||||
end,
|
||||
...this.getNeighbouringTiles(end).sort((a, b) => {
|
||||
const aDist = MathUtils.distanceBetween(a, start, false);
|
||||
const bDist = MathUtils.distanceBetween(b, start, false);
|
||||
if (aDist > bDist) {
|
||||
return 1;
|
||||
}
|
||||
if (aDist < bDist) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}),
|
||||
];
|
||||
}
|
||||
let path: { x: number; y: number }[] = [];
|
||||
while (endPoints.length > 0) {
|
||||
const endPoint = endPoints.shift();
|
||||
if (!endPoint) {
|
||||
return [];
|
||||
}
|
||||
// rejected Promise will return undefined for path
|
||||
path = await this.getPath(start, endPoint).catch();
|
||||
if (path && path.length > 0) {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
private getNeighbouringTiles(tile: { x: number; y: number }): { x: number; y: number }[] {
|
||||
const xOffsets = [-1, 0, 1, 1, 1, 0, -1, -1];
|
||||
const yOffsets = [-1, -1, -1, 0, 1, 1, 1, 0];
|
||||
|
||||
const neighbours: { x: number; y: number }[] = [];
|
||||
for (let i = 0; i < 8; i += 1) {
|
||||
const tileToCheck = { x: tile.x + xOffsets[i], y: tile.y + yOffsets[i] };
|
||||
if (this.isTileWithinMap(tileToCheck)) {
|
||||
neighbours.push(tileToCheck);
|
||||
}
|
||||
}
|
||||
return neighbours;
|
||||
}
|
||||
|
||||
private isTileWithinMap(tile: { x: number; y: number }): boolean {
|
||||
const mapHeight = this.grid.length ?? 0;
|
||||
const mapWidth = this.grid[0]?.length ?? 0;
|
||||
|
||||
return MathUtils.isBetween(tile.x, 0, mapWidth) && MathUtils.isBetween(tile.y, 0, mapHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns empty array if path was not found
|
||||
*/
|
||||
private async getPath(
|
||||
start: { x: number; y: number },
|
||||
end: { x: number; y: number }
|
||||
): Promise<{ x: number; y: number }[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.easyStar.findPath(start.x, start.y, end.x, end.y, (path) => {
|
||||
if (path === null) {
|
||||
resolve([]);
|
||||
} else {
|
||||
resolve(path);
|
||||
}
|
||||
});
|
||||
this.easyStar.calculate();
|
||||
});
|
||||
}
|
||||
|
||||
private setEasyStarGrid(grid: number[][]): void {
|
||||
this.easyStar.setGrid(grid);
|
||||
this.easyStar.setAcceptableTiles([0]); // zeroes are walkable
|
||||
}
|
||||
|
||||
private logGridToTheConsole(grid: number[][]): void {
|
||||
let rowNumber = 0;
|
||||
for (const row of grid) {
|
||||
console.log(`${rowNumber}:\t${row}`);
|
||||
rowNumber += 1;
|
||||
}
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ import type { UserSimplePeerInterface } from "./SimplePeer";
|
||||
import { readable, Readable, Unsubscriber } from "svelte/store";
|
||||
import { localStreamStore, obtainedMediaConstraintStore, ObtainedMediaStreamConstraints } from "../Stores/MediaStore";
|
||||
import { playersStore } from "../Stores/PlayersStore";
|
||||
import { chatMessagesStore, newChatMessageStore } from "../Stores/ChatStore";
|
||||
import { chatMessagesStore, newChatMessageSubject } from "../Stores/ChatStore";
|
||||
import { getIceServersConfig } from "../Components/Video/utils";
|
||||
import { isMobile } from "../Enum/EnvironmentVariable";
|
||||
|
||||
@ -35,7 +35,7 @@ export class VideoPeer extends Peer {
|
||||
public readonly streamStore: Readable<MediaStream | null>;
|
||||
public readonly statusStore: Readable<PeerStatus>;
|
||||
public readonly constraintsStore: Readable<ObtainedMediaStreamConstraints | null>;
|
||||
private newMessageunsubscriber: Unsubscriber | null = null;
|
||||
private newMessageSubscribtion: Subscription | undefined;
|
||||
private closing: Boolean = false; //this is used to prevent destroy() from being called twice
|
||||
private localStreamStoreSubscribe: Unsubscriber;
|
||||
private obtainedMediaConstraintStoreSubscribe: Unsubscriber;
|
||||
@ -129,7 +129,7 @@ export class VideoPeer extends Peer {
|
||||
this._connected = true;
|
||||
chatMessagesStore.addIncomingUser(this.userId);
|
||||
|
||||
this.newMessageunsubscriber = newChatMessageStore.subscribe((newMessage) => {
|
||||
this.newMessageSubscribtion = newChatMessageSubject.subscribe((newMessage) => {
|
||||
if (!newMessage) return;
|
||||
this.write(
|
||||
new Buffer(
|
||||
@ -138,8 +138,7 @@ export class VideoPeer extends Peer {
|
||||
message: newMessage,
|
||||
})
|
||||
)
|
||||
); //send more data
|
||||
newChatMessageStore.set(null); //This is to prevent a newly created SimplePeer to send an old message a 2nd time. Is there a better way?
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -262,7 +261,7 @@ export class VideoPeer extends Peer {
|
||||
this.closing = true;
|
||||
this.onBlockSubscribe.unsubscribe();
|
||||
this.onUnBlockSubscribe.unsubscribe();
|
||||
if (this.newMessageunsubscriber) this.newMessageunsubscriber();
|
||||
this.newMessageSubscribtion?.unsubscribe();
|
||||
chatMessagesStore.addOutcomingUser(this.userId);
|
||||
if (this.localStreamStoreSubscribe) this.localStreamStoreSubscribe();
|
||||
if (this.obtainedMediaConstraintStoreSubscribe) this.obtainedMediaConstraintStoreSubscribe();
|
||||
|
@ -102,6 +102,7 @@ const config: GameConfig = {
|
||||
dom: {
|
||||
createContainer: true,
|
||||
},
|
||||
disableContextMenu: true,
|
||||
render: {
|
||||
pixelArt: true,
|
||||
roundPixels: true,
|
||||
|
@ -2015,6 +2015,13 @@ dot-case@^3.0.4:
|
||||
no-case "^3.0.4"
|
||||
tslib "^2.0.3"
|
||||
|
||||
easystarjs@^0.4.4:
|
||||
version "0.4.4"
|
||||
resolved "https://registry.yarnpkg.com/easystarjs/-/easystarjs-0.4.4.tgz#8cec6d20d0d8660715da0301d1da440370a8f40a"
|
||||
integrity sha512-ZSt0TkB8xuIXRIrKsM3jkmk1/cZUtyvf0DqOXf6wuKq9slx9UA5kkLtiaWhtmOQFJFKdabbvXwk6RO0znghArQ==
|
||||
dependencies:
|
||||
heap "0.2.6"
|
||||
|
||||
ee-first@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
||||
@ -2950,6 +2957,11 @@ he@^1.2.0:
|
||||
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
|
||||
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
|
||||
|
||||
heap@0.2.6:
|
||||
version "0.2.6"
|
||||
resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.6.tgz#087e1f10b046932fc8594dd9e6d378afc9d1e5ac"
|
||||
integrity sha1-CH4fELBGky/IWU3Z5tN4r8nR5aw=
|
||||
|
||||
hmac-drbg@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM testcafe/testcafe:1.17.1
|
||||
FROM testcafe/testcafe:1.18.2
|
||||
|
||||
USER root
|
||||
RUN apk add docker-compose
|
||||
|
2947
tests/package-lock.json
generated
2947
tests/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -14,7 +14,7 @@ export async function assertLogMessage(t: TestController, message: string): Prom
|
||||
}
|
||||
await t.wait(1000);
|
||||
i++;
|
||||
} while (i < 10);
|
||||
} while (i < 30);
|
||||
|
||||
await t.expect(logs).contains(message);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user