detecting zoe enter and leave events
This commit is contained in:
parent
591467f1e3
commit
eecf831ca5
@ -5,7 +5,6 @@ import {
|
|||||||
ITiledMap,
|
ITiledMap,
|
||||||
ITiledMapLayer,
|
ITiledMapLayer,
|
||||||
ITiledMapObject,
|
ITiledMapObject,
|
||||||
ITiledMapObjectLayer,
|
|
||||||
} from "@workadventure/tiled-map-type-guard/dist";
|
} from "@workadventure/tiled-map-type-guard/dist";
|
||||||
import { User } from "_Model/User";
|
import { User } from "_Model/User";
|
||||||
import { variablesRepository } from "./Repository/VariablesRepository";
|
import { variablesRepository } from "./Repository/VariablesRepository";
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import type { ITiledMap, ITiledMapLayer, ITiledMapProperty } from "../Map/ITiledMap";
|
import type { 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";
|
||||||
import { GameMapProperties } from "./GameMapProperties";
|
import { GameMapProperties } from "./GameMapProperties";
|
||||||
|
import { MathUtils } from '../../Utils/MathUtils';
|
||||||
|
|
||||||
export type PropertyChangeCallback = (
|
export type PropertyChangeCallback = (
|
||||||
newValue: string | number | boolean | undefined,
|
newValue: string | number | boolean | undefined,
|
||||||
@ -15,23 +16,46 @@ export type layerChangeCallback = (
|
|||||||
allLayersOnNewPosition: Array<ITiledMapLayer>
|
allLayersOnNewPosition: Array<ITiledMapLayer>
|
||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
|
export type zoneChangeCallback = (
|
||||||
|
zonesChangedByAction: Array<ITiledMapObject>,
|
||||||
|
allZonesOnNewPosition: Array<ITiledMapObject>
|
||||||
|
) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A wrapper around a ITiledMap interface to provide additional capabilities.
|
* A wrapper around a ITiledMap interface to provide additional capabilities.
|
||||||
* It is used to handle layer properties.
|
* It is used to handle layer properties.
|
||||||
*/
|
*/
|
||||||
export class GameMap {
|
export class GameMap {
|
||||||
// oldKey is the index of the previous tile.
|
/**
|
||||||
|
* oldKey is the index of the previous tile.
|
||||||
|
*/
|
||||||
private oldKey: number | undefined;
|
private oldKey: number | undefined;
|
||||||
// key is the index of the current tile.
|
/**
|
||||||
|
* key is the index of the current tile.
|
||||||
|
*/
|
||||||
private key: number | undefined;
|
private key: number | undefined;
|
||||||
|
/**
|
||||||
|
* oldPosition is the previous position of the player.
|
||||||
|
*/
|
||||||
|
private oldPosition: { x: number, y: number } | undefined;
|
||||||
|
/**
|
||||||
|
* position is the current position of the player.
|
||||||
|
*/
|
||||||
|
private position: { x: number, y: number } | undefined;
|
||||||
|
|
||||||
private lastProperties = new Map<string, string | boolean | number>();
|
private lastProperties = new Map<string, string | boolean | number>();
|
||||||
private propertiesChangeCallbacks = new Map<string, Array<PropertyChangeCallback>>();
|
private propertiesChangeCallbacks = new Map<string, Array<PropertyChangeCallback>>();
|
||||||
|
|
||||||
private enterLayerCallbacks = Array<layerChangeCallback>();
|
private enterLayerCallbacks = Array<layerChangeCallback>();
|
||||||
private leaveLayerCallbacks = Array<layerChangeCallback>();
|
private leaveLayerCallbacks = Array<layerChangeCallback>();
|
||||||
|
private enterZoneCallbacks = Array<zoneChangeCallback>();
|
||||||
|
private leaveZoneCallbacks = Array<zoneChangeCallback>();
|
||||||
|
|
||||||
private tileNameMap = new Map<string, number>();
|
private tileNameMap = new Map<string, number>();
|
||||||
|
|
||||||
private tileSetPropertyMap: { [tile_index: number]: Array<ITiledMapProperty> } = {};
|
private tileSetPropertyMap: { [tile_index: number]: Array<ITiledMapProperty> } = {};
|
||||||
public readonly flatLayers: ITiledMapLayer[];
|
public readonly flatLayers: ITiledMapLayer[];
|
||||||
|
public readonly tiledObjects: ITiledMapObject[];
|
||||||
public readonly phaserLayers: TilemapLayer[] = [];
|
public readonly phaserLayers: TilemapLayer[] = [];
|
||||||
|
|
||||||
public exitUrls: Array<string> = [];
|
public exitUrls: Array<string> = [];
|
||||||
@ -44,6 +68,8 @@ export class GameMap {
|
|||||||
terrains: Array<Phaser.Tilemaps.Tileset>
|
terrains: Array<Phaser.Tilemaps.Tileset>
|
||||||
) {
|
) {
|
||||||
this.flatLayers = flattenGroupLayersMap(map);
|
this.flatLayers = flattenGroupLayersMap(map);
|
||||||
|
this.tiledObjects = this.getObjectsFromLayers(this.flatLayers);
|
||||||
|
|
||||||
let depth = -2;
|
let depth = -2;
|
||||||
for (const layer of this.flatLayers) {
|
for (const layer of this.flatLayers) {
|
||||||
if (layer.type === "tilelayer") {
|
if (layer.type === "tilelayer") {
|
||||||
@ -88,6 +114,9 @@ export class GameMap {
|
|||||||
* This will trigger events if properties are changing.
|
* This will trigger events if properties are changing.
|
||||||
*/
|
*/
|
||||||
public setPosition(x: number, y: number) {
|
public setPosition(x: number, y: number) {
|
||||||
|
this.oldPosition = this.position;
|
||||||
|
this.position = { x, y };
|
||||||
|
|
||||||
this.oldKey = this.key;
|
this.oldKey = this.key;
|
||||||
|
|
||||||
const xMap = Math.floor(x / this.map.tilewidth);
|
const xMap = Math.floor(x / this.map.tilewidth);
|
||||||
@ -102,6 +131,7 @@ export class GameMap {
|
|||||||
|
|
||||||
this.triggerAllProperties();
|
this.triggerAllProperties();
|
||||||
this.triggerLayersChange();
|
this.triggerLayersChange();
|
||||||
|
this.triggerZonesChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
private triggerAllProperties(): void {
|
private triggerAllProperties(): void {
|
||||||
@ -126,7 +156,7 @@ export class GameMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private triggerLayersChange() {
|
private triggerLayersChange(): void {
|
||||||
const layersByOldKey = this.oldKey ? this.getLayersByKey(this.oldKey) : [];
|
const layersByOldKey = this.oldKey ? this.getLayersByKey(this.oldKey) : [];
|
||||||
const layersByNewKey = this.key ? this.getLayersByKey(this.key) : [];
|
const layersByNewKey = this.key ? this.getLayersByKey(this.key) : [];
|
||||||
|
|
||||||
@ -155,6 +185,54 @@ export class GameMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We user Tiled Objects with type "zone" as zones with defined x, y, width and height for easier event triggering.
|
||||||
|
*/
|
||||||
|
private triggerZonesChange(): void {
|
||||||
|
const zones = this.tiledObjects.filter(object => object.type === "zone");
|
||||||
|
|
||||||
|
// P.H. NOTE: We could also get all of the zones and add properties of occupied tiles to them, so we could later on check collision by using tileKeys
|
||||||
|
const zonesByOldPosition = this.oldPosition ?
|
||||||
|
zones.filter((zone) => {
|
||||||
|
if (!this.oldPosition) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return MathUtils.isOverlappingWithRectangle(this.oldPosition, zone);
|
||||||
|
}) : [];
|
||||||
|
|
||||||
|
const zonesByNewPosition = this.position ?
|
||||||
|
zones.filter((zone) => {
|
||||||
|
if (!this.position) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return MathUtils.isOverlappingWithRectangle(this.position, zone);
|
||||||
|
}) : [];
|
||||||
|
|
||||||
|
const enterZones = new Set(zonesByNewPosition);
|
||||||
|
const leaveZones = new Set(zonesByOldPosition);
|
||||||
|
|
||||||
|
enterZones.forEach((zone) => {
|
||||||
|
if (leaveZones.has(zone)) {
|
||||||
|
leaveZones.delete(zone);
|
||||||
|
enterZones.delete(zone);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (enterZones.size > 0) {
|
||||||
|
const zonesArray = Array.from(enterZones);
|
||||||
|
for (const callback of this.enterZoneCallbacks) {
|
||||||
|
callback(zonesArray, zonesByNewPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leaveZones.size > 0) {
|
||||||
|
const zonesArray = Array.from(leaveZones);
|
||||||
|
for (const callback of this.leaveZoneCallbacks) {
|
||||||
|
callback(zonesArray, zonesByNewPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public getCurrentProperties(): Map<string, string | boolean | number> {
|
public getCurrentProperties(): Map<string, string | boolean | number> {
|
||||||
return this.lastProperties;
|
return this.lastProperties;
|
||||||
}
|
}
|
||||||
@ -251,6 +329,20 @@ export class GameMap {
|
|||||||
this.leaveLayerCallbacks.push(callback);
|
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 {
|
public findLayer(layerName: string): ITiledMapLayer | undefined {
|
||||||
return this.flatLayers.find((layer) => layer.name === layerName);
|
return this.flatLayers.find((layer) => layer.name === layerName);
|
||||||
}
|
}
|
||||||
@ -362,4 +454,22 @@ export class GameMap {
|
|||||||
this.trigger(oldPropName, oldPropValue, undefined, emptyProps);
|
this.trigger(oldPropName, oldPropValue, undefined, emptyProps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getObjectsFromLayers(layers: ITiledMapLayer[]): ITiledMapObject[] {
|
||||||
|
const objects: ITiledMapObject[] = [];
|
||||||
|
|
||||||
|
const objectLayers = layers.filter(layer => layer.type === "objectgroup");
|
||||||
|
for (const objectLayer of objectLayers) {
|
||||||
|
if (this.isOfTypeITiledMapObjectLayer(objectLayer)) {
|
||||||
|
objects.push(...objectLayer.objects);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return objects;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Simple typeguard for Objects Layer.
|
||||||
|
private isOfTypeITiledMapObjectLayer(obj: ITiledMapLayer): obj is ITiledMapObjectLayer {
|
||||||
|
return (obj as ITiledMapObjectLayer).objects !== undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -778,6 +778,28 @@ export class GameScene extends DirtyScene {
|
|||||||
iframeListener.sendLeaveLayerEvent(layer.name);
|
iframeListener.sendLeaveLayerEvent(layer.name);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.gameMap.onEnterZone((zones) => {
|
||||||
|
console.log('enter zones');
|
||||||
|
console.log(zones);
|
||||||
|
// zones.forEach((zone) => {
|
||||||
|
// iframeListener.sendEnterLayerEvent(zone.name);
|
||||||
|
// });
|
||||||
|
});
|
||||||
|
|
||||||
|
this.gameMap.onLeaveZone((zones) => {
|
||||||
|
console.log('leave zones');
|
||||||
|
console.log(zones);
|
||||||
|
// zones.forEach((zone) => {
|
||||||
|
// iframeListener.sendEnterLayerEvent(zone.name);
|
||||||
|
// });
|
||||||
|
});
|
||||||
|
|
||||||
|
// this.gameMap.onLeaveLayer((layers) => {
|
||||||
|
// layers.forEach((layer) => {
|
||||||
|
// iframeListener.sendLeaveLayerEvent(layer.name);
|
||||||
|
// });
|
||||||
|
// });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import type { RoomConnection } from "../../Connexion/RoomConnection";
|
import type { RoomConnection } from "../../Connexion/RoomConnection";
|
||||||
import { iframeListener } from "../../Api/IframeListener";
|
import { iframeListener } from "../../Api/IframeListener";
|
||||||
import type { GameMap } from "./GameMap";
|
import type { GameMap } from "./GameMap";
|
||||||
import type { ITiledMapLayer, ITiledMapObject, ITiledMapObjectLayer } from "../Map/ITiledMap";
|
import type { ITiledMapLayer, ITiledMapObject } from "../Map/ITiledMap";
|
||||||
import { GameMapProperties } from "./GameMapProperties";
|
import { GameMapProperties } from "./GameMapProperties";
|
||||||
|
|
||||||
interface Variable {
|
interface Variable {
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import * as Phaser from "phaser";
|
import * as Phaser from "phaser";
|
||||||
import { Scene } from "phaser";
|
|
||||||
import Sprite = Phaser.GameObjects.Sprite;
|
import Sprite = Phaser.GameObjects.Sprite;
|
||||||
import type { ITiledMapObject } from "../../Map/ITiledMap";
|
import type { ITiledMapObject } from "../../Map/ITiledMap";
|
||||||
import type { ItemFactoryInterface } from "../ItemFactoryInterface";
|
import type { ItemFactoryInterface } from "../ItemFactoryInterface";
|
||||||
|
27
front/src/Utils/MathUtils.ts
Normal file
27
front/src/Utils/MathUtils.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
export class MathUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param p Position to check.
|
||||||
|
* @param r Rectangle to check the overlap against.
|
||||||
|
* @returns true is overlapping
|
||||||
|
*/
|
||||||
|
public static isOverlappingWithRectangle(
|
||||||
|
p: { x: number, y: number},
|
||||||
|
r: { x: number, y: number, width: number, height: number},
|
||||||
|
): boolean {
|
||||||
|
return (this.isBetween(p.x, r.x, r.x + r.width) && this.isBetween(p.y, r.y, r.y + r.height));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param value Value to check
|
||||||
|
* @param min inclusive min value
|
||||||
|
* @param max inclusive max value
|
||||||
|
* @returns true if value is in <min, max>
|
||||||
|
*/
|
||||||
|
public static isBetween(value: number, min: number, max: number): boolean {
|
||||||
|
return (value >= min) && (value <= max);
|
||||||
|
}
|
||||||
|
}
|
@ -148,6 +148,28 @@
|
|||||||
"width":128,
|
"width":128,
|
||||||
"x":512,
|
"x":512,
|
||||||
"y":0
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"height":128,
|
||||||
|
"id":9,
|
||||||
|
"name":"chillZone",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"display_name",
|
||||||
|
"type":"string",
|
||||||
|
"value":"Chilling Room"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"focusable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"zone",
|
||||||
|
"visible":true,
|
||||||
|
"width":192,
|
||||||
|
"x":32,
|
||||||
|
"y":96
|
||||||
}],
|
}],
|
||||||
"opacity":1,
|
"opacity":1,
|
||||||
"type":"objectgroup",
|
"type":"objectgroup",
|
||||||
@ -192,7 +214,7 @@
|
|||||||
"y":0
|
"y":0
|
||||||
}],
|
}],
|
||||||
"nextlayerid":39,
|
"nextlayerid":39,
|
||||||
"nextobjectid":9,
|
"nextobjectid":11,
|
||||||
"orientation":"orthogonal",
|
"orientation":"orthogonal",
|
||||||
"properties":[
|
"properties":[
|
||||||
{
|
{
|
||||||
@ -226,7 +248,7 @@
|
|||||||
"value":"..\/dist\/script.js"
|
"value":"..\/dist\/script.js"
|
||||||
}],
|
}],
|
||||||
"renderorder":"right-down",
|
"renderorder":"right-down",
|
||||||
"tiledversion":"1.7.0",
|
"tiledversion":"1.7.2",
|
||||||
"tileheight":32,
|
"tileheight":32,
|
||||||
"tilesets":[
|
"tilesets":[
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user