2021-06-19 15:17:28 +02:00
|
|
|
import type { ITiledMap, ITiledMapLayerProperty } from "../Map/ITiledMap";
|
|
|
|
import { LayersIterator } from "../Map/LayersIterator";
|
2020-08-30 15:44:22 +02:00
|
|
|
|
2020-10-16 19:13:26 +02:00
|
|
|
export type PropertyChangeCallback = (newValue: string | number | boolean | undefined, oldValue: string | number | boolean | undefined, allProps: Map<string, string | boolean | number>) => void;
|
2020-08-30 15:44:22 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* A wrapper around a ITiledMap interface to provide additional capabilities.
|
|
|
|
* It is used to handle layer properties.
|
|
|
|
*/
|
|
|
|
export class GameMap {
|
2021-06-19 15:24:27 +02:00
|
|
|
private key: number | undefined;
|
|
|
|
private lastProperties = new Map<string, string | boolean | number>();
|
2020-08-30 15:44:22 +02:00
|
|
|
private callbacks = new Map<string, Array<PropertyChangeCallback>>();
|
2021-06-19 15:17:28 +02:00
|
|
|
|
2021-06-19 15:41:58 +02:00
|
|
|
private tileSetPropertyMap: { [tilset_firstgid: number]: { [tile_id: number]: Array<ITiledMapLayerProperty> } } = {}
|
2021-04-15 22:39:35 +02:00
|
|
|
public readonly layersIterator: LayersIterator;
|
2020-08-30 15:44:22 +02:00
|
|
|
|
2021-06-19 15:17:28 +02:00
|
|
|
public exitUrls: Array<string> = []
|
|
|
|
|
2020-08-30 15:44:22 +02:00
|
|
|
public constructor(private map: ITiledMap) {
|
2021-04-15 22:39:35 +02:00
|
|
|
this.layersIterator = new LayersIterator(map);
|
2021-06-19 15:17:28 +02:00
|
|
|
|
|
|
|
for (const tileset of map.tilesets) {
|
2021-06-19 15:41:58 +02:00
|
|
|
if (!this.tileSetPropertyMap[tileset.firstgid]) {
|
|
|
|
this.tileSetPropertyMap[tileset.firstgid] = {}
|
2021-06-19 15:17:28 +02:00
|
|
|
}
|
|
|
|
tileset?.tiles?.forEach(tile => {
|
|
|
|
if (tile.properties) {
|
2021-06-19 15:41:58 +02:00
|
|
|
this.tileSetPropertyMap[tileset.firstgid][tile.id] = tile.properties
|
2021-06-19 15:17:28 +02:00
|
|
|
tile.properties.forEach(prop => {
|
|
|
|
if (prop.name == "exitUrl" && typeof prop.value == "string") {
|
|
|
|
this.exitUrls.push(prop.value);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2020-08-30 15:44:22 +02:00
|
|
|
}
|
|
|
|
|
2021-06-19 15:17:28 +02:00
|
|
|
|
|
|
|
|
2020-08-30 15:44:22 +02:00
|
|
|
/**
|
|
|
|
* Sets the position of the current player (in pixels)
|
|
|
|
* This will trigger events if properties are changing.
|
|
|
|
*/
|
|
|
|
public setPosition(x: number, y: number) {
|
|
|
|
const xMap = Math.floor(x / this.map.tilewidth);
|
|
|
|
const yMap = Math.floor(y / this.map.tileheight);
|
|
|
|
const key = xMap + yMap * this.map.width;
|
|
|
|
if (key === this.key) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.key = key;
|
|
|
|
|
|
|
|
const newProps = this.getProperties(key);
|
|
|
|
const oldProps = this.lastProperties;
|
2021-02-18 11:32:37 +01:00
|
|
|
this.lastProperties = newProps;
|
2020-08-30 15:44:22 +02:00
|
|
|
|
|
|
|
// Let's compare the 2 maps:
|
|
|
|
// First new properties vs oldProperties
|
|
|
|
for (const [newPropName, newPropValue] of newProps.entries()) {
|
|
|
|
const oldPropValue = oldProps.get(newPropName);
|
|
|
|
if (oldPropValue !== newPropValue) {
|
2020-10-16 19:13:26 +02:00
|
|
|
this.trigger(newPropName, oldPropValue, newPropValue, newProps);
|
2020-08-30 15:44:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const [oldPropName, oldPropValue] of oldProps.entries()) {
|
|
|
|
if (!newProps.has(oldPropName)) {
|
|
|
|
// We found a property that disappeared
|
2020-10-16 19:13:26 +02:00
|
|
|
this.trigger(oldPropName, oldPropValue, undefined, newProps);
|
2020-08-30 15:44:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-19 15:24:27 +02:00
|
|
|
public getCurrentProperties(): Map<string, string | boolean | number> {
|
2021-02-10 11:08:57 +01:00
|
|
|
return this.lastProperties;
|
|
|
|
}
|
|
|
|
|
2021-06-19 15:24:27 +02:00
|
|
|
private getProperties(key: number): Map<string, string | boolean | number> {
|
|
|
|
const properties = new Map<string, string | boolean | number>();
|
2020-08-30 15:44:22 +02:00
|
|
|
|
2021-04-15 22:39:35 +02:00
|
|
|
for (const layer of this.layersIterator) {
|
2020-08-30 15:44:22 +02:00
|
|
|
if (layer.type !== 'tilelayer') {
|
|
|
|
continue;
|
|
|
|
}
|
2021-06-19 15:46:32 +02:00
|
|
|
|
|
|
|
let tileIndex: number | undefined = undefined;
|
|
|
|
if (layer.data) {
|
|
|
|
const tiles = layer.data as number[];
|
|
|
|
if (tiles[key] == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
tileIndex = tiles[key]
|
2020-08-30 15:44:22 +02:00
|
|
|
}
|
2021-06-19 15:46:32 +02:00
|
|
|
|
2020-08-30 15:44:22 +02:00
|
|
|
// There is a tile in this layer, let's embed the properties
|
|
|
|
if (layer.properties !== undefined) {
|
|
|
|
for (const layerProperty of layer.properties) {
|
|
|
|
if (layerProperty.value === undefined) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
properties.set(layerProperty.name, layerProperty.value);
|
|
|
|
}
|
|
|
|
}
|
2021-06-19 15:17:28 +02:00
|
|
|
|
|
|
|
if (tileIndex) {
|
|
|
|
const tileset = this.map.tilesets.find(tileset => tileset.firstgid + tileset.tilecount > (tileIndex as number))
|
|
|
|
if (tileset) {
|
2021-06-19 15:41:58 +02:00
|
|
|
const tileProperties = this.tileSetPropertyMap[tileset?.firstgid][tileIndex - tileset.firstgid]
|
2021-06-19 15:46:32 +02:00
|
|
|
tileProperties?.forEach(property => {
|
|
|
|
if (property.value) {
|
|
|
|
properties.set(property.name, property.value)
|
|
|
|
} else if (properties.has(property.name)) {
|
|
|
|
properties.delete(property.name)
|
2021-06-19 15:17:28 +02:00
|
|
|
}
|
2021-06-19 15:46:32 +02:00
|
|
|
})
|
2021-06-19 15:17:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2020-08-30 15:44:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return properties;
|
|
|
|
}
|
|
|
|
|
2020-10-16 19:13:26 +02:00
|
|
|
private trigger(propName: string, oldValue: string | number | boolean | undefined, newValue: string | number | boolean | undefined, allProps: Map<string, string | boolean | number>) {
|
2020-08-30 17:40:04 +02:00
|
|
|
const callbacksArray = this.callbacks.get(propName);
|
2020-08-30 15:44:22 +02:00
|
|
|
if (callbacksArray !== undefined) {
|
|
|
|
for (const callback of callbacksArray) {
|
2020-10-16 19:13:26 +02:00
|
|
|
callback(newValue, oldValue, allProps);
|
2020-08-30 15:44:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.callbacks.get(propName);
|
|
|
|
if (callbacksArray === undefined) {
|
|
|
|
callbacksArray = new Array<PropertyChangeCallback>();
|
|
|
|
this.callbacks.set(propName, callbacksArray);
|
|
|
|
}
|
|
|
|
callbacksArray.push(callback);
|
|
|
|
}
|
|
|
|
}
|