Merge pull request #1735 from thecodingmachine/walking-shortest-path
Walking shortest path on click
This commit is contained in:
commit
1e3c81617f
1
front/.gitignore
vendored
1
front/.gitignore
vendored
@ -6,6 +6,7 @@
|
|||||||
/dist/main.*.css.map
|
/dist/main.*.css.map
|
||||||
/dist/tests/
|
/dist/tests/
|
||||||
/yarn-error.log
|
/yarn-error.log
|
||||||
|
/package-lock.json
|
||||||
/dist/webpack.config.js
|
/dist/webpack.config.js
|
||||||
/dist/webpack.config.js.map
|
/dist/webpack.config.js.map
|
||||||
/dist/src
|
/dist/src
|
||||||
|
@ -48,6 +48,7 @@
|
|||||||
"axios": "^0.21.2",
|
"axios": "^0.21.2",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"deep-copy-ts": "^0.5.0",
|
"deep-copy-ts": "^0.5.0",
|
||||||
|
"easystarjs": "^0.4.4",
|
||||||
"generic-type-guard": "^3.2.0",
|
"generic-type-guard": "^3.2.0",
|
||||||
"google-protobuf": "^3.13.0",
|
"google-protobuf": "^3.13.0",
|
||||||
"phaser": "^3.54.0",
|
"phaser": "^3.54.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.visible = false;
|
||||||
this.enable = 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.resizeCallback = this.resize.bind(this);
|
||||||
this.scene.scale.on(Phaser.Scale.Events.RESIZE, this.resizeCallback);
|
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() {
|
public resize() {
|
||||||
this.base.setDisplaySize(this.getDisplaySizeByElement(baseSize), this.getDisplaySizeByElement(baseSize));
|
this.base.setDisplaySize(this.getDisplaySizeByElement(baseSize), this.getDisplaySizeByElement(baseSize));
|
||||||
this.thumb.setDisplaySize(this.getDisplaySizeByElement(thumbSize), this.getDisplaySizeByElement(thumbSize));
|
this.thumb.setDisplaySize(this.getDisplaySizeByElement(thumbSize), this.getDisplaySizeByElement(thumbSize));
|
||||||
|
@ -1,10 +1,4 @@
|
|||||||
import type {
|
import type { ITiledMap, ITiledMapLayer, ITiledMapObject, ITiledMapProperty } from "../Map/ITiledMap";
|
||||||
ITiledMap,
|
|
||||||
ITiledMapLayer,
|
|
||||||
ITiledMapObject,
|
|
||||||
ITiledMapObjectLayer,
|
|
||||||
ITiledMapProperty,
|
|
||||||
} from "../Map/ITiledMap";
|
|
||||||
import { flattenGroupLayersMap } from "../Map/LayersFlattener";
|
import { flattenGroupLayersMap } from "../Map/LayersFlattener";
|
||||||
import TilemapLayer = Phaser.Tilemaps.TilemapLayer;
|
import TilemapLayer = Phaser.Tilemaps.TilemapLayer;
|
||||||
import { DEPTH_OVERLAY_INDEX } from "./DepthIndexes";
|
import { DEPTH_OVERLAY_INDEX } from "./DepthIndexes";
|
||||||
@ -120,8 +114,24 @@ export class GameMap {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
private getLayersByKey(key: number): Array<ITiledMapLayer> {
|
public getCollisionsGrid(): number[][] {
|
||||||
return this.flatLayers.filter((flatLayer) => flatLayer.type === "tilelayer" && flatLayer.data[key] !== 0);
|
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,151 @@ export class GameMap {
|
|||||||
this.triggerLayersChange();
|
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 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 {
|
private triggerAllProperties(): void {
|
||||||
const newProps = this.getProperties(this.key ?? 0);
|
const newProps = this.getProperties(this.key ?? 0);
|
||||||
const oldProps = this.lastProperties;
|
const oldProps = this.lastProperties;
|
||||||
@ -247,10 +402,6 @@ export class GameMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCurrentProperties(): Map<string, string | boolean | number> {
|
|
||||||
return this.lastProperties;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getProperties(key: number): Map<string, string | boolean | number> {
|
private getProperties(key: number): Map<string, string | boolean | number> {
|
||||||
const properties = new Map<string, string | boolean | number>();
|
const properties = new Map<string, string | boolean | number>();
|
||||||
|
|
||||||
@ -292,10 +443,6 @@ export class GameMap {
|
|||||||
return properties;
|
return properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getMap(): ITiledMap {
|
|
||||||
return this.map;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getTileProperty(index: number): Array<ITiledMapProperty> {
|
private getTileProperty(index: number): Array<ITiledMapProperty> {
|
||||||
if (this.tileSetPropertyMap[index]) {
|
if (this.tileSetPropertyMap[index]) {
|
||||||
return this.tileSetPropertyMap[index];
|
return this.tileSetPropertyMap[index];
|
||||||
@ -317,64 +464,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 {
|
private putTileInFlatLayer(index: number, x: number, y: number, layer: string): void {
|
||||||
const fLayer = this.findLayer(layer);
|
const fLayer = this.findLayer(layer);
|
||||||
if (fLayer == undefined) {
|
if (fLayer == undefined) {
|
||||||
@ -396,30 +485,6 @@ export class GameMap {
|
|||||||
fLayer.data[x + y * fLayer.width] = index;
|
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 {
|
private getIndexForTileType(tile: string | number): number | undefined {
|
||||||
if (typeof tile == "number") {
|
if (typeof tile == "number") {
|
||||||
return tile;
|
return tile;
|
||||||
@ -427,48 +492,6 @@ export class GameMap {
|
|||||||
return this.tileNameMap.get(tile);
|
return this.tileNameMap.get(tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
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[] {
|
private getObjectsFromLayers(layers: ITiledMapLayer[]): ITiledMapObject[] {
|
||||||
const objects: ITiledMapObject[] = [];
|
const objects: ITiledMapObject[] = [];
|
||||||
|
|
||||||
|
@ -48,9 +48,9 @@ import { PropertyUtils } from "../Map/PropertyUtils";
|
|||||||
import { GameMapPropertiesListener } from "./GameMapPropertiesListener";
|
import { GameMapPropertiesListener } from "./GameMapPropertiesListener";
|
||||||
import { analyticsClient } from "../../Administration/AnalyticsClient";
|
import { analyticsClient } from "../../Administration/AnalyticsClient";
|
||||||
import { GameMapProperties } from "./GameMapProperties";
|
import { GameMapProperties } from "./GameMapProperties";
|
||||||
|
import { PathfindingManager } from "../../Utils/PathfindingManager";
|
||||||
import type {
|
import type {
|
||||||
GroupCreatedUpdatedMessageInterface,
|
GroupCreatedUpdatedMessageInterface,
|
||||||
MessageUserJoined,
|
|
||||||
MessageUserMovedInterface,
|
MessageUserMovedInterface,
|
||||||
MessageUserPositionInterface,
|
MessageUserPositionInterface,
|
||||||
OnConnectInterface,
|
OnConnectInterface,
|
||||||
@ -66,7 +66,6 @@ import type { ITiledMap, ITiledMapLayer, ITiledMapProperty, ITiledMapObject, ITi
|
|||||||
import type { AddPlayerInterface } from "./AddPlayerInterface";
|
import type { AddPlayerInterface } from "./AddPlayerInterface";
|
||||||
import { CameraManager, CameraManagerEvent, CameraManagerEventCameraUpdateData } from "./CameraManager";
|
import { CameraManager, CameraManagerEvent, CameraManagerEventCameraUpdateData } from "./CameraManager";
|
||||||
import type { HasPlayerMovedEvent } from "../../Api/Events/HasPlayerMovedEvent";
|
import type { HasPlayerMovedEvent } from "../../Api/Events/HasPlayerMovedEvent";
|
||||||
import type { Character } from "../Entity/Character";
|
|
||||||
|
|
||||||
import { peerStore } from "../../Stores/PeerStore";
|
import { peerStore } from "../../Stores/PeerStore";
|
||||||
import { biggestAvailableAreaStore } from "../../Stores/BiggestAvailableAreaStore";
|
import { biggestAvailableAreaStore } from "../../Stores/BiggestAvailableAreaStore";
|
||||||
@ -89,9 +88,9 @@ import SpriteSheetFile = Phaser.Loader.FileTypes.SpriteSheetFile;
|
|||||||
import { deepCopy } from "deep-copy-ts";
|
import { deepCopy } from "deep-copy-ts";
|
||||||
import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR;
|
import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR;
|
||||||
import { MapStore } from "../../Stores/Utils/MapStore";
|
import { MapStore } from "../../Stores/Utils/MapStore";
|
||||||
import { followUsersColorStore, followUsersStore } from "../../Stores/FollowStore";
|
import { followUsersColorStore } from "../../Stores/FollowStore";
|
||||||
import { getColorRgbFromHue } from "../../WebRtc/ColorGenerator";
|
|
||||||
import Camera = Phaser.Cameras.Scene2D.Camera;
|
import Camera = Phaser.Cameras.Scene2D.Camera;
|
||||||
|
import { GameSceneUserInputHandler } from "../UserInput/GameSceneUserInputHandler";
|
||||||
|
|
||||||
export interface GameSceneInitInterface {
|
export interface GameSceneInitInterface {
|
||||||
initPosition: PointInterface | null;
|
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 mapTransitioning: boolean = false; //used to prevent transitions happening at the same time.
|
||||||
private emoteManager!: EmoteManager;
|
private emoteManager!: EmoteManager;
|
||||||
private cameraManager!: CameraManager;
|
private cameraManager!: CameraManager;
|
||||||
|
private pathfindingManager!: PathfindingManager;
|
||||||
private preloading: boolean = true;
|
private preloading: boolean = true;
|
||||||
private startPositionCalculator!: StartPositionCalculator;
|
private startPositionCalculator!: StartPositionCalculator;
|
||||||
private sharedVariablesManager!: SharedVariablesManager;
|
private sharedVariablesManager!: SharedVariablesManager;
|
||||||
@ -549,7 +549,7 @@ export class GameScene extends DirtyScene {
|
|||||||
this.MapPlayers = this.physics.add.group({ immovable: true });
|
this.MapPlayers = this.physics.add.group({ immovable: true });
|
||||||
|
|
||||||
//create input to move
|
//create input to move
|
||||||
this.userInputManager = new UserInputManager(this);
|
this.userInputManager = new UserInputManager(this, new GameSceneUserInputHandler(this));
|
||||||
mediaManager.setUserInputManager(this.userInputManager);
|
mediaManager.setUserInputManager(this.userInputManager);
|
||||||
|
|
||||||
if (localUserStore.getFullscreen()) {
|
if (localUserStore.getFullscreen()) {
|
||||||
@ -568,6 +568,8 @@ export class GameScene extends DirtyScene {
|
|||||||
{ x: this.Map.widthInPixels, y: this.Map.heightInPixels },
|
{ x: this.Map.widthInPixels, y: this.Map.heightInPixels },
|
||||||
waScaleManager
|
waScaleManager
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.pathfindingManager = new PathfindingManager(this, this.gameMap.getCollisionsGrid());
|
||||||
biggestAvailableAreaStore.recompute();
|
biggestAvailableAreaStore.recompute();
|
||||||
this.cameraManager.startFollowPlayer(this.CurrentPlayer);
|
this.cameraManager.startFollowPlayer(this.CurrentPlayer);
|
||||||
|
|
||||||
@ -605,10 +607,6 @@ export class GameScene extends DirtyScene {
|
|||||||
scriptPromises.push(iframeListener.registerScript(script, !disableModuleMode));
|
scriptPromises.push(iframeListener.registerScript(script, !disableModuleMode));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.userInputManager.spaceEvent(() => {
|
|
||||||
this.outlinedItem?.activate();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.reposition();
|
this.reposition();
|
||||||
|
|
||||||
// From now, this game scene will be notified of reposition events
|
// From now, this game scene will be notified of reposition events
|
||||||
@ -677,6 +675,10 @@ export class GameScene extends DirtyScene {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public activateOutlinedItem(): void {
|
||||||
|
this.outlinedItem?.activate();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the connection to Pusher.
|
* Initializes the connection to Pusher.
|
||||||
*/
|
*/
|
||||||
@ -1688,7 +1690,6 @@ ${escapedMessage}
|
|||||||
texturesPromise,
|
texturesPromise,
|
||||||
PlayerAnimationDirections.Down,
|
PlayerAnimationDirections.Down,
|
||||||
false,
|
false,
|
||||||
this.userInputManager,
|
|
||||||
this.companion,
|
this.companion,
|
||||||
this.companion !== null ? lazyLoadCompanionResource(this.load, this.companion) : undefined
|
this.companion !== null ? lazyLoadCompanionResource(this.load, this.companion) : undefined
|
||||||
);
|
);
|
||||||
@ -1808,7 +1809,7 @@ ${escapedMessage}
|
|||||||
update(time: number, delta: number): void {
|
update(time: number, delta: number): void {
|
||||||
this.dirty = false;
|
this.dirty = false;
|
||||||
this.currentTick = time;
|
this.currentTick = time;
|
||||||
this.CurrentPlayer.moveUser(delta);
|
this.CurrentPlayer.moveUser(delta, this.userInputManager.getEventListForGameTick());
|
||||||
|
|
||||||
// Let's handle all events
|
// Let's handle all events
|
||||||
while (this.pendingEvents.length !== 0) {
|
while (this.pendingEvents.length !== 0) {
|
||||||
@ -2172,4 +2173,16 @@ ${escapedMessage}
|
|||||||
this.scene.stop(this.scene.key);
|
this.scene.stop(this.scene.key);
|
||||||
this.scene.remove(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 { PlayerAnimationDirections } from "./Animation";
|
||||||
import type { GameScene } from "../Game/GameScene";
|
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 { Character } from "../Entity/Character";
|
||||||
import type { RemotePlayer } from "../Entity/RemotePlayer";
|
|
||||||
|
|
||||||
import { get } from "svelte/store";
|
import { get } from "svelte/store";
|
||||||
import { userMovingStore } from "../../Stores/GameStore";
|
import { userMovingStore } from "../../Stores/GameStore";
|
||||||
@ -12,6 +11,8 @@ export const hasMovedEventName = "hasMoved";
|
|||||||
export const requestEmoteEventName = "requestEmote";
|
export const requestEmoteEventName = "requestEmote";
|
||||||
|
|
||||||
export class Player extends Character {
|
export class Player extends Character {
|
||||||
|
private pathToFollow?: { x: number; y: number }[];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
Scene: GameScene,
|
Scene: GameScene,
|
||||||
x: number,
|
x: number,
|
||||||
@ -20,7 +21,6 @@ export class Player extends Character {
|
|||||||
texturesPromise: Promise<string[]>,
|
texturesPromise: Promise<string[]>,
|
||||||
direction: PlayerAnimationDirections,
|
direction: PlayerAnimationDirections,
|
||||||
moving: boolean,
|
moving: boolean,
|
||||||
private userInputManager: UserInputManager,
|
|
||||||
companion: string | null,
|
companion: string | null,
|
||||||
companionTexturePromise?: Promise<string>
|
companionTexturePromise?: Promise<string>
|
||||||
) {
|
) {
|
||||||
@ -30,6 +30,55 @@ export class Player extends Character {
|
|||||||
this.getBody().setImmovable(false);
|
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) {
|
private inputStep(activeEvents: ActiveEventList, x: number, y: number) {
|
||||||
// Process input events
|
// Process input events
|
||||||
if (activeEvents.get(UserInputEvent.MoveUp)) {
|
if (activeEvents.get(UserInputEvent.MoveUp)) {
|
||||||
@ -95,40 +144,29 @@ export class Player extends Character {
|
|||||||
if (distance < 2000) {
|
if (distance < 2000) {
|
||||||
return [0, 0];
|
return [0, 0];
|
||||||
}
|
}
|
||||||
const xMovement = xDistance / Math.sqrt(distance);
|
return this.getMovementDirection(xDistance, yDistance, distance);
|
||||||
const yMovement = yDistance / Math.sqrt(distance);
|
|
||||||
return [xMovement, yMovement];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public moveUser(delta: number): void {
|
private computeFollowPathMovement(): number[] {
|
||||||
const activeEvents = this.userInputManager.getEventListForGameTick();
|
if (this.pathToFollow?.length === 0) {
|
||||||
const state = get(followStateStore);
|
this.pathToFollow = undefined;
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (!this.pathToFollow) {
|
||||||
let x = 0;
|
return [0, 0];
|
||||||
let y = 0;
|
|
||||||
if ((state === "active" || state === "ending") && role === "follower") {
|
|
||||||
[x, y] = this.computeFollowMovement();
|
|
||||||
}
|
}
|
||||||
this.inputStep(activeEvents, x, y);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
public sendFollowRequest() {
|
private getMovementDirection(xDistance: number, yDistance: number, distance: number): [number, number] {
|
||||||
this.scene.connection?.emitFollowRequest();
|
return [xDistance / Math.sqrt(distance), yDistance / Math.sqrt(distance)];
|
||||||
followRoleStore.set("leader");
|
|
||||||
followStateStore.set("active");
|
|
||||||
}
|
|
||||||
|
|
||||||
public startFollowing() {
|
|
||||||
followStateStore.set("active");
|
|
||||||
this.scene.connection?.emitFollowConfirmation();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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 { touchScreenManager } from "../../Touch/TouchScreenManager";
|
||||||
import { MobileJoystick } from "../Components/MobileJoystick";
|
import { MobileJoystick } from "../Components/MobileJoystick";
|
||||||
import { enableUserInputsStore } from "../../Stores/UserInputStore";
|
import { enableUserInputsStore } from "../../Stores/UserInputStore";
|
||||||
import type { Direction } from "phaser3-rex-plugins/plugins/virtualjoystick.js";
|
import type { Direction } from "phaser3-rex-plugins/plugins/virtualjoystick.js";
|
||||||
|
import type { UserInputHandlerInterface } from "../../Interfaces/UserInputHandlerInterface";
|
||||||
|
|
||||||
interface UserInputManagerDatum {
|
interface UserInputManagerDatum {
|
||||||
keyInstance: Phaser.Input.Keyboard.Key;
|
keyInstance: Phaser.Input.Keyboard.Key;
|
||||||
@ -37,12 +37,21 @@ export class ActiveEventList {
|
|||||||
any(): boolean {
|
any(): boolean {
|
||||||
return Array.from(this.eventMap.values()).reduce((accu, curr) => accu || curr, false);
|
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.
|
//this class is responsible for catching user inputs and listing all active user actions at every game tick events.
|
||||||
export class UserInputManager {
|
export class UserInputManager {
|
||||||
private KeysCode!: UserInputManagerDatum[];
|
private keysCode!: UserInputManagerDatum[];
|
||||||
private Scene: GameScene;
|
private scene: Phaser.Scene;
|
||||||
private isInputDisabled: boolean;
|
private isInputDisabled: boolean;
|
||||||
|
|
||||||
private joystick!: MobileJoystick;
|
private joystick!: MobileJoystick;
|
||||||
@ -51,11 +60,15 @@ export class UserInputManager {
|
|||||||
private joystickForceAccuX = 0;
|
private joystickForceAccuX = 0;
|
||||||
private joystickForceAccuY = 0;
|
private joystickForceAccuY = 0;
|
||||||
|
|
||||||
constructor(Scene: GameScene) {
|
private userInputHandler: UserInputHandlerInterface;
|
||||||
this.Scene = Scene;
|
|
||||||
|
constructor(scene: Phaser.Scene, userInputHandler: UserInputHandlerInterface) {
|
||||||
|
this.scene = scene;
|
||||||
|
this.userInputHandler = userInputHandler;
|
||||||
|
|
||||||
this.isInputDisabled = false;
|
this.isInputDisabled = false;
|
||||||
this.initKeyBoardEvent();
|
this.initKeyBoardEvent();
|
||||||
this.initMouseWheel();
|
this.bindInputEventHandlers();
|
||||||
if (touchScreenManager.supportTouchScreen) {
|
if (touchScreenManager.supportTouchScreen) {
|
||||||
this.initVirtualJoystick();
|
this.initVirtualJoystick();
|
||||||
}
|
}
|
||||||
@ -66,7 +79,7 @@ export class UserInputManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initVirtualJoystick() {
|
initVirtualJoystick() {
|
||||||
this.joystick = new MobileJoystick(this.Scene);
|
this.joystick = new MobileJoystick(this.scene);
|
||||||
this.joystick.on("update", () => {
|
this.joystick.on("update", () => {
|
||||||
this.joystickForceAccuX = this.joystick.forceX ? this.joystickForceAccuX : 0;
|
this.joystickForceAccuX = this.joystick.forceX ? this.joystickForceAccuX : 0;
|
||||||
this.joystickForceAccuY = this.joystick.forceY ? this.joystickForceAccuY : 0;
|
this.joystickForceAccuY = this.joystick.forceY ? this.joystickForceAccuY : 0;
|
||||||
@ -92,80 +105,80 @@ export class UserInputManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initKeyBoardEvent() {
|
initKeyBoardEvent() {
|
||||||
this.KeysCode = [
|
this.keysCode = [
|
||||||
{
|
{
|
||||||
event: UserInputEvent.MoveUp,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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() {
|
clearAllListeners() {
|
||||||
this.Scene.input.keyboard.removeAllListeners();
|
this.scene.input.keyboard.removeAllListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
//todo: should we also disable the joystick?
|
//todo: should we also disable the joystick?
|
||||||
disableControls() {
|
disableControls() {
|
||||||
this.Scene.input.keyboard.removeAllKeys();
|
this.scene.input.keyboard.removeAllKeys();
|
||||||
this.isInputDisabled = true;
|
this.isInputDisabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,7 +214,7 @@ export class UserInputManager {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
eventsMap.set(UserInputEvent.JoystickMove, this.joystickEvents.any());
|
eventsMap.set(UserInputEvent.JoystickMove, this.joystickEvents.any());
|
||||||
this.KeysCode.forEach((d) => {
|
this.keysCode.forEach((d) => {
|
||||||
if (d.keyInstance.isDown) {
|
if (d.keyInstance.isDown) {
|
||||||
eventsMap.set(d.event, true);
|
eventsMap.set(d.event, true);
|
||||||
}
|
}
|
||||||
@ -209,30 +222,60 @@ export class UserInputManager {
|
|||||||
return eventsMap;
|
return eventsMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
spaceEvent(callback: Function) {
|
|
||||||
this.Scene.input.keyboard.on("keyup-SPACE", (event: Event) => {
|
|
||||||
callback();
|
|
||||||
return event;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
addSpaceEventListner(callback: Function) {
|
addSpaceEventListner(callback: Function) {
|
||||||
this.Scene.input.keyboard.addListener("keyup-SPACE", callback);
|
this.scene.input.keyboard.addListener("keyup-SPACE", callback);
|
||||||
}
|
}
|
||||||
removeSpaceEventListner(callback: Function) {
|
removeSpaceEventListner(callback: Function) {
|
||||||
this.Scene.input.keyboard.removeListener("keyup-SPACE", callback);
|
this.scene.input.keyboard.removeListener("keyup-SPACE", callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
this.joystick?.destroy();
|
this.joystick?.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
private initMouseWheel() {
|
private bindInputEventHandlers() {
|
||||||
this.Scene.input.on(
|
this.scene.input.on(
|
||||||
"wheel",
|
Phaser.Input.Events.POINTER_WHEEL,
|
||||||
(pointer: unknown, gameObjects: unknown, deltaX: number, deltaY: number, deltaZ: number) => {
|
(
|
||||||
this.Scene.zoomByFactor(1 - (deltaY / 53) * 0.1);
|
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);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,4 +22,13 @@ export class MathUtils {
|
|||||||
public static isBetween(value: number, min: number, max: number): boolean {
|
public static isBetween(value: number, min: number, max: number): boolean {
|
||||||
return value >= min && value <= max;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -102,6 +102,7 @@ const config: GameConfig = {
|
|||||||
dom: {
|
dom: {
|
||||||
createContainer: true,
|
createContainer: true,
|
||||||
},
|
},
|
||||||
|
disableContextMenu: true,
|
||||||
render: {
|
render: {
|
||||||
pixelArt: true,
|
pixelArt: true,
|
||||||
roundPixels: true,
|
roundPixels: true,
|
||||||
|
@ -1990,6 +1990,13 @@ dot-case@^3.0.4:
|
|||||||
no-case "^3.0.4"
|
no-case "^3.0.4"
|
||||||
tslib "^2.0.3"
|
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:
|
ee-first@1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
||||||
@ -2925,6 +2932,11 @@ he@^1.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
|
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
|
||||||
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
|
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:
|
hmac-drbg@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
|
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
|
||||||
|
Loading…
Reference in New Issue
Block a user