import Phaser from "phaser";
const Player = require('../objects/player/player');
const Ogre = require('../objects/enemies/ogre');
const GameCoin = require('../objects/items/coin');
const ItemPickup = require('../objects/items/itempickup');
const Cylinder = require('../objects/destructibles/cylinder');
const Interact = require('../actions/interact');
const Parse = require('../utility/parse');
const dataManager = require('../objects/data');
/**
* Describes how each Tiled object is mapped to a javascript object or function.
*
* @memberof ClubCrawler.Parsers.BlueWorldParser
*/
const BLUEWORLD_DEFAULTS = {
tileSetKey: "blue-tileset",
mapKey: "blueworld",
objectConstructorMappings: {
layers: {
Spawns: {
type: {
"spawn-point": {
spawns: {
"cylinder": {
creates: Cylinder,
group: "destructibles"
}
}
},
"enemy-spawn": {
name: {
Ogre: {
creates: Ogre,
group: "enemies"
}
}
},
"playerspawn": {
creates: Parse.setPlayerStart,
function: true
}
}
},
Items: {
type: {
points: {
creates: GameCoin
},
"item-pickup": {
creates: ItemPickup
}
}
}
}
}
}
/**
* @classdesc
* Parses the BlueWorld map found in static/maps/blueworld.json
*
* Wraps and manages a Phaser.Tilemap, loads objects, applies physics to objects, and places player in the scene.
*
* Will eventually handle loading of other maps.
*
* @memberof ClubCrawler.Parsers
*
*/
class BlueWorldParser {
/**
* Creates the map floor tiles, wall tiles, and spawns.
* @constructor
* @param {Object} config - A configuration object
* @param {Phaser.Scene} config.scene - The {@link https://newdocs.phaser.io/docs/3.55.2/Phaser.Scene Phaser.Scene} creating this map manager
*
*/
constructor(config) {
Object.assign(this, BLUEWORLD_DEFAULTS);
Object.assign(this, config);
this.scene = config.scene;
/**
* The amount to scale the map - not yet implemented
* @todo Implement this
* @property {number}
*/
this.scale = config.scale ? config.scale : 1;
/**
* The {@link https://newdocs.phaser.io/docs/3.52.0/Phaser.Tilemaps.Tilemap Phaser.Tilemaps.Tilemap} for the current scene
*/
this.map = this.scene.make.tilemap({key: config.mapKey ? BLUEWORLD_DEFAULTS.mapKey : "blueworld"});
this.tileset = this.map.addTilesetImage('blue-patterned-world', config.tileSetKey ? BLUEWORLD_DEFAULTS.tileSetKey : "blue-tileset");
this.floors = this.map.createLayer('Floors', this.tileset, 0, 0);
this.walls = this.map.createLayer('Walls', this.tileset, 0, 0);
this.walls.setCollisionByExclusion(-1, true);
/**
* Player start X coordinates.
* @default 0
*/
this.startX = 0;
/**
* Player start Y coordinates.
* @default 0
*/
this.startY = 0;
// this.destructibles = this.scene.physics.add.group();
// this.enemies = this.scene.physics.add.group();
}
/**
* Places player at this.startX, this.startY
*
* @param {Player} player - The player
*/
startPlayer(player) {
if(dataManager.debug.on && dataManager.debug.map.placePlayer) {
dataManager.log(`Starting player at ${this.startX}, ${this.startY}`);
}
player.setX(this.startX);
player.setY(this.startY);
if(player.reticle) {
player.reticle.moveToPlayer();
}
let wallCollider = this.scene.physics.add.collider(this.walls, player);
if(dataManager.debug.on && dataManager.debug.map.placePlayer) {
dataManager.log(`Created a ${wallCollider.constructor.name} between ${this.walls.constructor.name} and ${player.constructor.name}`);
}
}
/**
* This will be improved so colliders can be defined in the mapping object as well.
*
* @deprecated
*
* @returns {any}
*/
addColliders() {
this.scene.physics.add.collider(this.walls, this.scene.player);
this.scene.physics.add.collider(this.destructibles, this.walls);
this.scene.physics.add.collider(this.enemies, this.walls);
this.scene.physics.add.collider(this.enemies, this.destructibles);
this.scene.physics.add.collider(this.enemies, this.enemies);
this.scene.physics.add.collider(this.enemies, this.scene.player, Interact.DamageCollision);
}
/**
* Places objects according to the mappings
*
* @returns {any}
*/
placeMappedObjects() {
var mapManager = this; // for pass through
if(dataManager.debug.on && dataManager.debug.map.layers) {
dataManager.log(`do we have layers? ${Object.keys(this.objectConstructorMappings.layers)}`);
}
var layers = this.objectConstructorMappings.layers; // see DEFAULTS const at top of this file for layer mapping example
for(let layerName of Object.keys(layers)) {
let mapLayer = this.map.getObjectLayer(layerName); // get the layer from Phaser
if(dataManager.debug.on && dataManager.debug.map.layers) {
dataManager.log(`Trying to grab a map layer resulted in: ${mapLayer.constructor.name}`)
}
mapLayer.objects.forEach( (tiledObject)=> { // iterate through each object in the layer
let extractedProperties = Parse.getFlatTiledObjectProperties(tiledObject.properties); // get the custom properties, flattened
if(dataManager.debug.on && dataManager.debug.map.objects) {
let dbname = tiledObject.name;
let preprop1 = tiledObject.properties;
let dbprop1 = Object.keys(extractedProperties)[0];
dataManager.log(`▶tiled name ${dbname}.▶ extracted: ${extractedProperties}▶ key1 : ${dbprop1} ▶preprop1: ${preprop1}`)
}
Object.assign(tiledObject, extractedProperties); // assign the custom properties to this object as top level properties rather than nested
let constructorConfig = Parse.getConstructorConfigFromLayerMap(tiledObject, layers[layerName]); // find the object with the "create" property that matches to this
if(dataManager.debug.on && dataManager.debug.map.layers) {
dataManager.log(`found ${layerName} constructor config properties: ${Object.keys(constructorConfig)}`)
}
if(constructorConfig.group) { // if the constructor configuration says the new object will be part of a group...
if(!mapManager[constructorConfig.group]) { // if this mapManager doesn't have a property corresponding to that group name yet...
mapManager[constructorConfig.group] = mapManager.scene.physics.add.group(); // then create that property as a new physics group
}
}
if(constructorConfig.creates) {
if(dataManager.debug.on && dataManager.debug.map.layers) {
dataManager.log(`Constructor config mapping found: ${constructorConfig.constructor.name}`)
}
let target = constructorConfig.creates;
extractedProperties.scene = mapManager.scene;
extractedProperties.x = tiledObject.x;
extractedProperties.y = tiledObject.y;
let constructorParameters = {
scene: mapManager.scene,
x: tiledObject.x,
y: tiledObject.y,
tiledData: tiledObject
}
if(!constructorConfig.function) { // if the target is a class, then create a class and add it to the physics group if there is one
let newInstance = new target(extractedProperties);
if(dataManager.debug.on && dataManager.debug.map.layers) {
dataManager.log(`Creating an instance of: ${newInstance.constructor.name}`)
}
if(constructorConfig.group) {
mapManager[constructorConfig.group].add(newInstance);
}
} else { //if it's not a class, target should be a function
let funcresult = target(constructorParameters);
if(dataManager.debug.on && dataManager.debug.map.functions) {
dataManager.log(`Function call, result: ${funcresult}`);
}
}
}
});
}
}
}
module.exports = BlueWorldParser;