2020-05-27 12:30:59 +00:00
|
|
|
import { DrawParameters } from "../../../core/draw_parameters";
|
|
|
|
import { Loader } from "../../../core/loader";
|
2020-05-27 13:03:36 +00:00
|
|
|
import { createLogger } from "../../../core/logging";
|
|
|
|
import { Vector } from "../../../core/vector";
|
|
|
|
import { Entity } from "../../entity";
|
|
|
|
import { GameRoot } from "../../root";
|
2020-05-27 12:30:59 +00:00
|
|
|
|
|
|
|
const logger = createLogger("blueprint");
|
|
|
|
|
|
|
|
export class Blueprint {
|
|
|
|
/**
|
|
|
|
* @param {Array<Entity>} entities
|
|
|
|
*/
|
|
|
|
constructor(entities) {
|
|
|
|
this.entities = entities;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-05-27 13:03:36 +00:00
|
|
|
* Creates a new blueprint from the given entity uids
|
2020-05-27 12:30:59 +00:00
|
|
|
* @param {GameRoot} root
|
|
|
|
* @param {Array<number>} uids
|
|
|
|
*/
|
|
|
|
static fromUids(root, uids) {
|
|
|
|
const newEntities = [];
|
|
|
|
|
|
|
|
let averagePosition = new Vector();
|
|
|
|
|
|
|
|
// First, create a copy
|
|
|
|
for (let i = 0; i < uids.length; ++i) {
|
|
|
|
const entity = root.entityMgr.findByUid(uids[i]);
|
|
|
|
assert(entity, "Entity for blueprint not found:" + uids[i]);
|
|
|
|
|
|
|
|
const clone = entity.duplicateWithoutContents();
|
|
|
|
newEntities.push(clone);
|
|
|
|
|
|
|
|
const pos = entity.components.StaticMapEntity.getTileSpaceBounds().getCenter();
|
|
|
|
averagePosition.addInplace(pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
averagePosition.divideScalarInplace(uids.length);
|
|
|
|
const blueprintOrigin = averagePosition.floor();
|
|
|
|
for (let i = 0; i < uids.length; ++i) {
|
|
|
|
newEntities[i].components.StaticMapEntity.origin.subInplace(blueprintOrigin);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now, make sure the origin is 0,0
|
|
|
|
return new Blueprint(newEntities);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-05-27 13:03:36 +00:00
|
|
|
* Draws the blueprint at the given origin
|
2020-05-27 12:30:59 +00:00
|
|
|
* @param {DrawParameters} parameters
|
|
|
|
*/
|
|
|
|
draw(parameters, tile) {
|
|
|
|
parameters.context.globalAlpha = 0.8;
|
|
|
|
for (let i = 0; i < this.entities.length; ++i) {
|
|
|
|
const entity = this.entities[i];
|
|
|
|
const staticComp = entity.components.StaticMapEntity;
|
|
|
|
if (!staticComp.blueprintSpriteKey) {
|
|
|
|
logger.warn("Blueprint entity without sprite!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const newPos = staticComp.origin.add(tile);
|
|
|
|
|
|
|
|
const rect = staticComp.getTileSpaceBounds();
|
|
|
|
rect.moveBy(tile.x, tile.y);
|
|
|
|
|
|
|
|
let placeable = true;
|
|
|
|
placementCheck: for (let x = rect.x; x < rect.right(); ++x) {
|
|
|
|
for (let y = rect.y; y < rect.bottom(); ++y) {
|
|
|
|
if (parameters.root.map.isTileUsedXY(x, y)) {
|
|
|
|
placeable = false;
|
|
|
|
break placementCheck;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!placeable) {
|
|
|
|
parameters.context.globalAlpha = 0.3;
|
|
|
|
} else {
|
|
|
|
parameters.context.globalAlpha = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
staticComp.drawSpriteOnFullEntityBounds(
|
|
|
|
parameters,
|
|
|
|
Loader.getSprite(staticComp.blueprintSpriteKey),
|
|
|
|
0,
|
|
|
|
true,
|
|
|
|
newPos
|
|
|
|
);
|
|
|
|
}
|
|
|
|
parameters.context.globalAlpha = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-05-27 13:03:36 +00:00
|
|
|
* Rotates the blueprint clockwise
|
|
|
|
*/
|
|
|
|
rotateCw() {
|
|
|
|
for (let i = 0; i < this.entities.length; ++i) {
|
|
|
|
const entity = this.entities[i];
|
|
|
|
const staticComp = entity.components.StaticMapEntity;
|
|
|
|
|
|
|
|
staticComp.rotation = (staticComp.rotation + 90) % 360;
|
|
|
|
staticComp.originalRotation = (staticComp.originalRotation + 90) % 360;
|
|
|
|
staticComp.origin = staticComp.origin.rotateFastMultipleOf90(90);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Rotates the blueprint counter clock wise
|
|
|
|
*/
|
|
|
|
rotateCcw() {
|
|
|
|
// Well ...
|
|
|
|
for (let i = 0; i < 3; ++i) {
|
|
|
|
this.rotateCw();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks if the blueprint can be placed at the given tile
|
2020-05-27 12:30:59 +00:00
|
|
|
* @param {GameRoot} root
|
|
|
|
* @param {Vector} tile
|
|
|
|
*/
|
|
|
|
canPlace(root, tile) {
|
|
|
|
let anyPlaceable = false;
|
|
|
|
|
|
|
|
for (let i = 0; i < this.entities.length; ++i) {
|
|
|
|
let placeable = true;
|
|
|
|
const entity = this.entities[i];
|
|
|
|
const staticComp = entity.components.StaticMapEntity;
|
|
|
|
const rect = staticComp.getTileSpaceBounds();
|
|
|
|
rect.moveBy(tile.x, tile.y);
|
|
|
|
placementCheck: for (let x = rect.x; x < rect.right(); ++x) {
|
|
|
|
for (let y = rect.y; y < rect.bottom(); ++y) {
|
|
|
|
if (root.map.isTileUsedXY(x, y)) {
|
|
|
|
placeable = false;
|
|
|
|
break placementCheck;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (placeable) {
|
|
|
|
anyPlaceable = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return anyPlaceable;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-05-27 13:03:36 +00:00
|
|
|
* Attempts to place the blueprint at the given tile
|
2020-05-27 12:30:59 +00:00
|
|
|
* @param {GameRoot} root
|
|
|
|
* @param {Vector} tile
|
|
|
|
*/
|
|
|
|
tryPlace(root, tile) {
|
2020-05-27 13:03:36 +00:00
|
|
|
return root.logic.performBulkOperation(() => {
|
|
|
|
let anyPlaced = false;
|
|
|
|
for (let i = 0; i < this.entities.length; ++i) {
|
|
|
|
let placeable = true;
|
|
|
|
const entity = this.entities[i];
|
|
|
|
const staticComp = entity.components.StaticMapEntity;
|
|
|
|
const rect = staticComp.getTileSpaceBounds();
|
|
|
|
rect.moveBy(tile.x, tile.y);
|
|
|
|
placementCheck: for (let x = rect.x; x < rect.right(); ++x) {
|
|
|
|
for (let y = rect.y; y < rect.bottom(); ++y) {
|
|
|
|
const contents = root.map.getTileContentXY(x, y);
|
|
|
|
if (contents && !contents.components.ReplaceableMapEntity) {
|
|
|
|
placeable = false;
|
|
|
|
break placementCheck;
|
|
|
|
}
|
2020-05-27 12:30:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-27 13:03:36 +00:00
|
|
|
if (placeable) {
|
|
|
|
for (let x = rect.x; x < rect.right(); ++x) {
|
|
|
|
for (let y = rect.y; y < rect.bottom(); ++y) {
|
|
|
|
const contents = root.map.getTileContentXY(x, y);
|
|
|
|
if (contents) {
|
|
|
|
assert(
|
|
|
|
contents.components.ReplaceableMapEntity,
|
|
|
|
"Can not delete entity for blueprint"
|
2020-05-27 12:30:59 +00:00
|
|
|
);
|
2020-05-27 13:03:36 +00:00
|
|
|
if (!root.logic.tryDeleteBuilding(contents)) {
|
|
|
|
logger.error(
|
|
|
|
"Building has replaceable component but is also unremovable in blueprint"
|
|
|
|
);
|
|
|
|
return false;
|
|
|
|
}
|
2020-05-27 12:30:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-27 13:03:36 +00:00
|
|
|
const clone = entity.duplicateWithoutContents();
|
|
|
|
clone.components.StaticMapEntity.origin.addInplace(tile);
|
2020-05-27 12:30:59 +00:00
|
|
|
|
2020-05-27 13:03:36 +00:00
|
|
|
root.map.placeStaticEntity(clone);
|
|
|
|
root.entityMgr.registerEntity(clone);
|
|
|
|
anyPlaced = true;
|
|
|
|
}
|
2020-05-27 12:30:59 +00:00
|
|
|
}
|
2020-05-27 13:03:36 +00:00
|
|
|
return anyPlaced;
|
|
|
|
});
|
2020-05-27 12:30:59 +00:00
|
|
|
}
|
|
|
|
}
|