2021-06-25 18:14:40 +02:00
|
|
|
import type { PositionInterface } from "../../Connexion/ConnexionModels";
|
2021-07-05 17:25:23 +02:00
|
|
|
import type { ITiledMap, ITiledMapLayer, ITiledMapProperty, ITiledMapTileLayer } from "../Map/ITiledMap";
|
2021-06-25 18:14:40 +02:00
|
|
|
import type { GameMap } from "./GameMap";
|
2021-06-25 17:57:09 +02:00
|
|
|
|
2021-06-25 18:14:40 +02:00
|
|
|
const defaultStartLayerName = "start";
|
2021-06-25 17:57:09 +02:00
|
|
|
|
|
|
|
export class StartPositionCalculator {
|
2021-06-25 18:14:40 +02:00
|
|
|
public startPosition!: PositionInterface;
|
2021-06-25 17:57:09 +02:00
|
|
|
|
|
|
|
constructor(
|
|
|
|
private readonly gameMap: GameMap,
|
|
|
|
private readonly mapFile: ITiledMap,
|
|
|
|
private readonly initPosition: PositionInterface | null,
|
2021-06-25 18:14:40 +02:00
|
|
|
public readonly startLayerName: string | null
|
|
|
|
) {
|
2021-06-25 17:57:09 +02:00
|
|
|
this.initStartXAndStartY();
|
|
|
|
}
|
|
|
|
private initStartXAndStartY() {
|
|
|
|
// If there is an init position passed
|
|
|
|
if (this.initPosition !== null) {
|
2021-06-25 18:03:43 +02:00
|
|
|
this.startPosition = this.initPosition;
|
2021-06-25 17:57:09 +02:00
|
|
|
} else {
|
|
|
|
// Now, let's find the start layer
|
|
|
|
if (this.startLayerName) {
|
|
|
|
this.initPositionFromLayerName(this.startLayerName, this.startLayerName);
|
|
|
|
}
|
2021-06-25 18:03:43 +02:00
|
|
|
if (this.startPosition === undefined) {
|
2021-06-25 17:57:09 +02:00
|
|
|
// If we have no start layer specified or if the hash passed does not exist, let's go with the default start position.
|
|
|
|
this.initPositionFromLayerName(defaultStartLayerName, this.startLayerName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Still no start position? Something is wrong with the map, we need a "start" layer.
|
2021-06-25 18:03:43 +02:00
|
|
|
if (this.startPosition === undefined) {
|
2021-06-25 18:14:40 +02:00
|
|
|
console.warn(
|
|
|
|
'This map is missing a layer named "start" that contains the available default start positions.'
|
|
|
|
);
|
2021-06-25 17:57:09 +02:00
|
|
|
// Let's start in the middle of the map
|
2021-06-25 18:03:43 +02:00
|
|
|
this.startPosition = {
|
|
|
|
x: this.mapFile.width * 16,
|
2021-06-25 18:14:40 +02:00
|
|
|
y: this.mapFile.height * 16,
|
2021-06-25 18:03:43 +02:00
|
|
|
};
|
2021-06-25 17:57:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-06-25 18:14:40 +02:00
|
|
|
*
|
|
|
|
* @param selectedLayer this is always the layer that is selected with the hash in the url
|
|
|
|
* @param selectedOrDefaultLayer this can also be the {defaultStartLayerName} if the {selectedLayer} didnt yield any start points
|
|
|
|
*/
|
2021-06-25 17:57:09 +02:00
|
|
|
public initPositionFromLayerName(selectedOrDefaultLayer: string | null, selectedLayer: string | null) {
|
|
|
|
if (!selectedOrDefaultLayer) {
|
2021-06-25 18:14:40 +02:00
|
|
|
selectedOrDefaultLayer = defaultStartLayerName;
|
2021-06-25 17:57:09 +02:00
|
|
|
}
|
2021-06-25 18:20:16 +02:00
|
|
|
for (const layer of this.gameMap.flatLayers) {
|
2021-06-25 18:14:40 +02:00
|
|
|
if (
|
|
|
|
(selectedOrDefaultLayer === layer.name || layer.name.endsWith("/" + selectedOrDefaultLayer)) &&
|
|
|
|
layer.type === "tilelayer" &&
|
|
|
|
(selectedOrDefaultLayer === defaultStartLayerName || this.isStartLayer(layer))
|
|
|
|
) {
|
2021-06-25 17:57:09 +02:00
|
|
|
const startPosition = this.startUser(layer, selectedLayer);
|
2021-06-25 18:03:43 +02:00
|
|
|
this.startPosition = {
|
|
|
|
x: startPosition.x + this.mapFile.tilewidth / 2,
|
2021-06-25 18:14:40 +02:00
|
|
|
y: startPosition.y + this.mapFile.tileheight / 2,
|
|
|
|
};
|
2021-06-25 17:57:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private isStartLayer(layer: ITiledMapLayer): boolean {
|
|
|
|
return this.getProperty(layer, "startLayer") == true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-06-25 18:14:40 +02:00
|
|
|
*
|
2021-06-25 17:57:09 +02:00
|
|
|
* @param selectedLayer this is always the layer that is selected with the hash in the url
|
2021-06-25 18:14:40 +02:00
|
|
|
* @param selectedOrDefaultLayer this can also be the default layer if the {selectedLayer} didnt yield any start points
|
2021-06-25 17:57:09 +02:00
|
|
|
*/
|
|
|
|
private startUser(selectedOrDefaultLayer: ITiledMapTileLayer, selectedLayer: string | null): PositionInterface {
|
|
|
|
const tiles = selectedOrDefaultLayer.data;
|
2021-06-25 18:14:40 +02:00
|
|
|
if (typeof tiles === "string") {
|
|
|
|
throw new Error("The content of a JSON map must be filled as a JSON array, not as a string");
|
2021-06-25 17:57:09 +02:00
|
|
|
}
|
|
|
|
const possibleStartPositions: PositionInterface[] = [];
|
|
|
|
tiles.forEach((objectKey: number, key: number) => {
|
|
|
|
if (objectKey === 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const y = Math.floor(key / selectedOrDefaultLayer.width);
|
|
|
|
const x = key % selectedOrDefaultLayer.width;
|
|
|
|
|
|
|
|
if (selectedLayer && this.gameMap.hasStartTile) {
|
|
|
|
const properties = this.gameMap.getPropertiesForIndex(objectKey);
|
2021-06-25 18:14:40 +02:00
|
|
|
if (
|
|
|
|
!properties.length ||
|
|
|
|
!properties.some((property) => property.name == "start" && property.value == selectedLayer)
|
|
|
|
) {
|
|
|
|
return;
|
2021-06-25 17:57:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
possibleStartPositions.push({ x: x * this.mapFile.tilewidth, y: y * this.mapFile.tilewidth });
|
|
|
|
});
|
|
|
|
// Get a value at random amongst allowed values
|
|
|
|
if (possibleStartPositions.length === 0) {
|
|
|
|
console.warn('The start layer "' + selectedOrDefaultLayer.name + '" for this map is empty.');
|
|
|
|
return {
|
|
|
|
x: 0,
|
2021-06-25 18:14:40 +02:00
|
|
|
y: 0,
|
2021-06-25 17:57:09 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
// Choose one of the available start positions at random amongst the list of available start positions.
|
|
|
|
return possibleStartPositions[Math.floor(Math.random() * possibleStartPositions.length)];
|
|
|
|
}
|
|
|
|
|
|
|
|
private getProperty(layer: ITiledMapLayer | ITiledMap, name: string): string | boolean | number | undefined {
|
2021-07-05 17:25:23 +02:00
|
|
|
const properties: ITiledMapProperty[] | undefined = layer.properties;
|
2021-06-25 17:57:09 +02:00
|
|
|
if (!properties) {
|
|
|
|
return undefined;
|
|
|
|
}
|
2021-06-25 18:14:40 +02:00
|
|
|
const obj = properties.find(
|
2021-07-05 17:25:23 +02:00
|
|
|
(property: ITiledMapProperty) => property.name.toLowerCase() === name.toLowerCase()
|
2021-06-25 18:14:40 +02:00
|
|
|
);
|
2021-06-25 17:57:09 +02:00
|
|
|
if (obj === undefined) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
return obj.value;
|
|
|
|
}
|
2021-06-25 18:14:40 +02:00
|
|
|
}
|