parent
f5f08a08e2
commit
0cd324c82b
@ -0,0 +1,176 @@
|
||||
import { GameRoot } from "../../root";
|
||||
import { Vector } from "../../../core/vector";
|
||||
import { Entity } from "../../entity";
|
||||
import { DrawParameters } from "../../../core/draw_parameters";
|
||||
import { StaticMapEntityComponent } from "../../components/static_map_entity";
|
||||
import { createLogger } from "../../../core/logging";
|
||||
import { Loader } from "../../../core/loader";
|
||||
|
||||
const logger = createLogger("blueprint");
|
||||
|
||||
export class Blueprint {
|
||||
/**
|
||||
* @param {Array<Entity>} entities
|
||||
*/
|
||||
constructor(entities) {
|
||||
this.entities = entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GameRoot} root
|
||||
* @param {Vector} tile
|
||||
*/
|
||||
tryPlace(root, tile) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
);
|
||||
if (!root.logic.tryDeleteBuilding(contents)) {
|
||||
logger.error(
|
||||
"Building has replaceable component but is also unremovable in blueprint"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const clone = entity.duplicateWithoutContents();
|
||||
clone.components.StaticMapEntity.origin.addInplace(tile);
|
||||
|
||||
root.map.placeStaticEntity(clone);
|
||||
root.entityMgr.registerEntity(clone);
|
||||
anyPlaced = true;
|
||||
}
|
||||
}
|
||||
return anyPlaced;
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
import { DrawParameters } from "../../../core/draw_parameters";
|
||||
import { STOP_PROPAGATION } from "../../../core/signal";
|
||||
import { TrackedState } from "../../../core/tracked_state";
|
||||
import { Vector } from "../../../core/vector";
|
||||
import { enumMouseButton } from "../../camera";
|
||||
import { KEYMAPPINGS } from "../../key_action_mapper";
|
||||
import { BaseHUDPart } from "../base_hud_part";
|
||||
import { Blueprint } from "./blueprint";
|
||||
|
||||
export class HUDBlueprintPlacer extends BaseHUDPart {
|
||||
createElements(parent) {}
|
||||
|
||||
initialize() {
|
||||
this.root.hud.signals.buildingsSelectedForCopy.add(this.onBuildingsSelected, this);
|
||||
|
||||
/** @type {TypedTrackedState<Blueprint?>} */
|
||||
this.currentBlueprint = new TrackedState(this.onBlueprintChanged, this);
|
||||
|
||||
const keyActionMapper = this.root.keyMapper;
|
||||
keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.abortPlacement, this);
|
||||
keyActionMapper
|
||||
.getBinding(KEYMAPPINGS.placement.abortBuildingPlacement)
|
||||
.add(this.abortPlacement, this);
|
||||
|
||||
this.root.camera.downPreHandler.add(this.onMouseDown, this);
|
||||
this.root.camera.movePreHandler.add(this.onMouseMove, this);
|
||||
|
||||
this.root.hud.signals.selectedPlacementBuildingChanged.add(this.abortPlacement, this);
|
||||
}
|
||||
|
||||
abortPlacement() {
|
||||
if (this.currentBlueprint.get()) {
|
||||
this.currentBlueprint.set(null);
|
||||
|
||||
return STOP_PROPAGATION;
|
||||
}
|
||||
}
|
||||
|
||||
onBlueprintChanged(blueprint) {}
|
||||
|
||||
/**
|
||||
* mouse down pre handler
|
||||
* @param {Vector} pos
|
||||
* @param {enumMouseButton} button
|
||||
*/
|
||||
onMouseDown(pos, button) {
|
||||
if (button === enumMouseButton.right) {
|
||||
this.abortPlacement();
|
||||
return STOP_PROPAGATION;
|
||||
}
|
||||
|
||||
const blueprint = this.currentBlueprint.get();
|
||||
if (!blueprint) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("down");
|
||||
const worldPos = this.root.camera.screenToWorld(pos);
|
||||
const tile = worldPos.toTileSpace();
|
||||
if (blueprint.tryPlace(this.root, tile)) {
|
||||
if (!this.root.app.inputMgr.shiftIsDown) {
|
||||
this.currentBlueprint.set(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMouseMove() {
|
||||
// Prevent movement while blueprint is selected
|
||||
if (this.currentBlueprint.get()) {
|
||||
return STOP_PROPAGATION;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array<number>} uids
|
||||
*/
|
||||
onBuildingsSelected(uids) {
|
||||
if (uids.length === 0) {
|
||||
return;
|
||||
}
|
||||
this.currentBlueprint.set(Blueprint.fromUids(this.root, uids));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {DrawParameters} parameters
|
||||
*/
|
||||
draw(parameters) {
|
||||
const blueprint = this.currentBlueprint.get();
|
||||
if (!blueprint) {
|
||||
return;
|
||||
}
|
||||
const mousePosition = this.root.app.mousePosition;
|
||||
if (!mousePosition) {
|
||||
// Not on screen
|
||||
return;
|
||||
}
|
||||
|
||||
const worldPos = this.root.camera.screenToWorld(mousePosition);
|
||||
const tile = worldPos.toTileSpace();
|
||||
blueprint.draw(parameters, tile);
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
import { SavegameInterface_V1000 } from "./1000.js";
|
||||
import { createLogger } from "../../core/logging.js";
|
||||
|
||||
const schema = require("./1001.json");
|
||||
|
||||
const logger = createLogger("savegame_interface/1001");
|
||||
|
||||
export class SavegameInterface_V1001 extends SavegameInterface_V1000 {
|
||||
getVersion() {
|
||||
return 1001;
|
||||
}
|
||||
|
||||
getSchemaUncached() {
|
||||
return schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("../savegame_typedefs.js").SavegameData} data
|
||||
*/
|
||||
static migrate1000to1001(data) {
|
||||
logger.log("Migrating 1000 to 1001");
|
||||
const dump = data.dump;
|
||||
if (!dump) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const entities = dump.entities;
|
||||
for (let i = 0; i < entities.length; ++i) {
|
||||
const entity = entities[i];
|
||||
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
const beltComp = entity.components.Belt;
|
||||
if (staticComp) {
|
||||
if (staticComp.spriteKey) {
|
||||
staticComp.blueprintSpriteKey = staticComp.spriteKey.replace(
|
||||
"sprites/buildings",
|
||||
"sprites/blueprints"
|
||||
);
|
||||
} else {
|
||||
if (entity.components.Hub) {
|
||||
staticComp.blueprintSpriteKey = "";
|
||||
} else if (beltComp) {
|
||||
const direction = beltComp.direction;
|
||||
staticComp.blueprintSpriteKey = "sprites/blueprints/belt_" + direction + ".png";
|
||||
} else {
|
||||
assertAlways(false, "Could not deduct entity type for migrating 1000 -> 1001");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
{
|
||||
"type": "object",
|
||||
"required": [],
|
||||
"additionalProperties": true
|
||||
}
|
Loading…
Reference in new issue