mirror of
https://github.com/tobspr/shapez.io.git
synced 2024-10-27 20:34:29 +00:00
Refactor building placer
This commit is contained in:
parent
4b9df76f2f
commit
84969a9205
@ -978,3 +978,17 @@ export function formatItemsPerSecond(speed, double = false) {
|
||||
: T.ingame.buildingPlacement.infoTexts.itemsPerSecond.replace("<x>", "" + round2Digits(speed)) +
|
||||
(double ? " " + T.ingame.buildingPlacement.infoTexts.itemsPerSecondDouble : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the corner point between two vectors
|
||||
* @param {Vector} a
|
||||
* @param {Vector} b
|
||||
*/
|
||||
export function findCornerBetweenPoints(a, b) {
|
||||
const delta = b.sub(a);
|
||||
if (Math_abs(delta.x) > Math_abs(delta.y)) {
|
||||
return new Vector(a.x, b.y);
|
||||
} else {
|
||||
return new Vector(b.x, a.y);
|
||||
}
|
||||
}
|
||||
|
@ -38,10 +38,6 @@ export class MetaUndergroundBeltBuilding extends MetaBuilding {
|
||||
return true;
|
||||
}
|
||||
|
||||
getHasDirectionLockAvailable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
getStayInPlacementMode() {
|
||||
return true;
|
||||
}
|
||||
|
@ -885,25 +885,25 @@ export class Camera extends BasicSerializableObject {
|
||||
let forceY = 0;
|
||||
|
||||
const actionMapper = this.root.keyMapper;
|
||||
if (actionMapper.getBinding(KEYMAPPINGS.navigation.mapMoveUp).isCurrentlyPressed()) {
|
||||
if (actionMapper.getBinding(KEYMAPPINGS.navigation.mapMoveUp).pressed) {
|
||||
forceY -= 1;
|
||||
}
|
||||
|
||||
if (actionMapper.getBinding(KEYMAPPINGS.navigation.mapMoveDown).isCurrentlyPressed()) {
|
||||
if (actionMapper.getBinding(KEYMAPPINGS.navigation.mapMoveDown).pressed) {
|
||||
forceY += 1;
|
||||
}
|
||||
|
||||
if (actionMapper.getBinding(KEYMAPPINGS.navigation.mapMoveLeft).isCurrentlyPressed()) {
|
||||
if (actionMapper.getBinding(KEYMAPPINGS.navigation.mapMoveLeft).pressed) {
|
||||
forceX -= 1;
|
||||
}
|
||||
|
||||
if (actionMapper.getBinding(KEYMAPPINGS.navigation.mapMoveRight).isCurrentlyPressed()) {
|
||||
if (actionMapper.getBinding(KEYMAPPINGS.navigation.mapMoveRight).pressed) {
|
||||
forceX += 1;
|
||||
}
|
||||
|
||||
let movementSpeed =
|
||||
this.root.app.settings.getMovementSpeed() *
|
||||
(actionMapper.getBinding(KEYMAPPINGS.navigation.mapMoveFaster).isCurrentlyPressed() ? 4 : 1);
|
||||
(actionMapper.getBinding(KEYMAPPINGS.navigation.mapMoveFaster).pressed ? 4 : 1);
|
||||
|
||||
this.center.x += moveAmount * forceX * movementSpeed;
|
||||
this.center.y += moveAmount * forceY * movementSpeed;
|
||||
|
@ -108,7 +108,7 @@ export class HUDBlueprintPlacer extends BaseHUDPart {
|
||||
this.root.hubGoals.takeShapeByKey(blueprintShape, cost);
|
||||
|
||||
// This actually feels weird
|
||||
// if (!this.root.keyMapper.getBinding(KEYMAPPINGS.placementModifiers.placeMultiple).isCurrentlyPressed()) {
|
||||
// if (!this.root.keyMapper.getBinding(KEYMAPPINGS.placementModifiers.placeMultiple).pressed) {
|
||||
// this.currentBlueprint.set(null);
|
||||
// }
|
||||
}
|
||||
@ -133,11 +133,7 @@ export class HUDBlueprintPlacer extends BaseHUDPart {
|
||||
|
||||
rotateBlueprint() {
|
||||
if (this.currentBlueprint.get()) {
|
||||
if (
|
||||
this.root.keyMapper
|
||||
.getBinding(KEYMAPPINGS.placement.rotateInverseModifier)
|
||||
.isCurrentlyPressed()
|
||||
) {
|
||||
if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateInverseModifier).pressed) {
|
||||
this.currentBlueprint.get().rotateCcw();
|
||||
} else {
|
||||
this.currentBlueprint.get().rotateCw();
|
||||
|
@ -1,87 +1,21 @@
|
||||
import { Math_abs, Math_degrees, Math_radians, Math_sign } from "../../../core/builtins";
|
||||
import { Math_radians } from "../../../core/builtins";
|
||||
import { globalConfig } from "../../../core/config";
|
||||
import { DrawParameters } from "../../../core/draw_parameters";
|
||||
import { drawRotatedSprite } from "../../../core/draw_utils";
|
||||
import { Loader } from "../../../core/loader";
|
||||
import { STOP_PROPAGATION } from "../../../core/signal";
|
||||
import { TrackedState } from "../../../core/tracked_state";
|
||||
import { makeDiv, removeAllChildren } from "../../../core/utils";
|
||||
import {
|
||||
enumDirectionToAngle,
|
||||
enumDirectionToVector,
|
||||
enumInvertedDirections,
|
||||
Vector,
|
||||
enumAngleToDirection,
|
||||
} from "../../../core/vector";
|
||||
import { enumMouseButton } from "../../camera";
|
||||
import { StaticMapEntityComponent } from "../../components/static_map_entity";
|
||||
import { Entity } from "../../entity";
|
||||
import { defaultBuildingVariant, MetaBuilding } from "../../meta_building";
|
||||
import { BaseHUDPart } from "../base_hud_part";
|
||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||
import { findCornerBetweenPoints, makeDiv, removeAllChildren } from "../../../core/utils";
|
||||
import { enumDirectionToAngle, enumDirectionToVector, enumInvertedDirections } from "../../../core/vector";
|
||||
import { T } from "../../../translations";
|
||||
import { KEYMAPPINGS } from "../../key_action_mapper";
|
||||
import { defaultBuildingVariant } from "../../meta_building";
|
||||
import { THEME } from "../../theme";
|
||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||
import { HUDBuildingPlacerLogic } from "./building_placer_logic";
|
||||
|
||||
export class HUDBuildingPlacer extends BaseHUDPart {
|
||||
initialize() {
|
||||
/** @type {TypedTrackedState<MetaBuilding?>} */
|
||||
this.currentMetaBuilding = new TrackedState(this.onSelectedMetaBuildingChanged, this);
|
||||
this.currentBaseRotation = 0;
|
||||
|
||||
/** @type {Entity} */
|
||||
this.fakeEntity = null;
|
||||
|
||||
const keyActionMapper = this.root.keyMapper;
|
||||
keyActionMapper
|
||||
.getBinding(KEYMAPPINGS.placement.abortBuildingPlacement)
|
||||
.add(this.abortPlacement, this);
|
||||
keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.abortPlacement, this);
|
||||
|
||||
keyActionMapper.getBinding(KEYMAPPINGS.placement.rotateWhilePlacing).add(this.tryRotate, this);
|
||||
keyActionMapper.getBinding(KEYMAPPINGS.placement.cycleBuildingVariants).add(this.cycleVariants, this);
|
||||
|
||||
this.root.hud.signals.buildingsSelectedForCopy.add(this.abortPlacement, this);
|
||||
this.root.hud.signals.pasteBlueprintRequested.add(this.abortPlacement, this);
|
||||
|
||||
this.domAttach = new DynamicDomAttach(this.root, this.element, {});
|
||||
|
||||
this.root.camera.downPreHandler.add(this.onMouseDown, this);
|
||||
this.root.camera.movePreHandler.add(this.onMouseMove, this);
|
||||
this.root.camera.upPostHandler.add(this.abortDragging, this);
|
||||
|
||||
this.currentlyDragging = false;
|
||||
this.currentVariant = new TrackedState(this.rerenderVariants, this);
|
||||
|
||||
this.variantsAttach = new DynamicDomAttach(this.root, this.variantsElement, {});
|
||||
|
||||
export class HUDBuildingPlacer extends HUDBuildingPlacerLogic {
|
||||
/**
|
||||
* Whether we are currently drag-deleting
|
||||
* @param {HTMLElement} parent
|
||||
*/
|
||||
this.currentlyDeleting = false;
|
||||
|
||||
/**
|
||||
* Stores which variants for each building we prefer, this is based on what
|
||||
* the user last selected
|
||||
*/
|
||||
this.preferredVariants = {};
|
||||
|
||||
/**
|
||||
* The tile we last dragged onto
|
||||
* @type {Vector}
|
||||
* */
|
||||
this.lastDragTile = null;
|
||||
|
||||
/**
|
||||
* The tile we initially dragged from
|
||||
* @type {Vector}
|
||||
*/
|
||||
this.initialDragTile = null;
|
||||
|
||||
this.root.signals.storyGoalCompleted.add(this.rerenderVariants, this);
|
||||
this.root.signals.upgradePurchased.add(this.rerenderVariants, this);
|
||||
}
|
||||
|
||||
createElements(parent) {
|
||||
this.element = makeDiv(parent, "ingame_HUD_PlacementHints", [], ``);
|
||||
|
||||
@ -101,201 +35,15 @@ export class HUDBuildingPlacer extends BaseHUDPart {
|
||||
this.variantsElement = makeDiv(parent, "ingame_HUD_PlacerVariants");
|
||||
}
|
||||
|
||||
abortPlacement() {
|
||||
if (this.currentMetaBuilding.get()) {
|
||||
this.currentMetaBuilding.set(null);
|
||||
return STOP_PROPAGATION;
|
||||
}
|
||||
}
|
||||
initialize() {
|
||||
super.initialize();
|
||||
|
||||
/**
|
||||
* mouse down pre handler
|
||||
* @param {Vector} pos
|
||||
* @param {enumMouseButton} button
|
||||
*/
|
||||
onMouseDown(pos, button) {
|
||||
if (this.root.camera.getIsMapOverlayActive()) {
|
||||
return;
|
||||
}
|
||||
// Bind to signals
|
||||
this.signals.variantChanged.add(this.rerenderVariants, this);
|
||||
|
||||
// Placement
|
||||
if (button === enumMouseButton.left && this.currentMetaBuilding.get()) {
|
||||
this.currentlyDragging = true;
|
||||
this.currentlyDeleting = false;
|
||||
this.lastDragTile = this.root.camera.screenToWorld(pos).toTileSpace();
|
||||
this.domAttach = new DynamicDomAttach(this.root, this.element, {});
|
||||
|
||||
// Place initial building
|
||||
this.tryPlaceCurrentBuildingAt(this.lastDragTile);
|
||||
|
||||
return STOP_PROPAGATION;
|
||||
}
|
||||
|
||||
// Deletion
|
||||
if (button === enumMouseButton.right && !this.currentMetaBuilding.get()) {
|
||||
this.currentlyDragging = true;
|
||||
this.currentlyDeleting = true;
|
||||
this.lastDragTile = this.root.camera.screenToWorld(pos).toTileSpace();
|
||||
this.currentMetaBuilding.set(null);
|
||||
return STOP_PROPAGATION;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* mouse move pre handler
|
||||
* @param {Vector} pos
|
||||
*/
|
||||
onMouseMove(pos) {
|
||||
if (this.root.camera.getIsMapOverlayActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const metaBuilding = this.currentMetaBuilding.get();
|
||||
if ((metaBuilding || this.currentlyDeleting) && this.lastDragTile) {
|
||||
const oldPos = this.lastDragTile;
|
||||
let newPos = this.root.camera.screenToWorld(pos).toTileSpace();
|
||||
|
||||
// Check if camera is moving, since then we do nothing
|
||||
if (this.root.camera.desiredCenter) {
|
||||
this.lastDragTile = newPos;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for direction lock
|
||||
if (
|
||||
metaBuilding &&
|
||||
metaBuilding.getHasDirectionLockAvailable() &&
|
||||
this.root.keyMapper.getBinding(KEYMAPPINGS.placement.lockBeltDirection).isCurrentlyPressed()
|
||||
) {
|
||||
const vector = enumDirectionToVector[enumAngleToDirection[this.currentBaseRotation]];
|
||||
const delta = newPos.sub(oldPos);
|
||||
delta.x *= Math_abs(vector.x);
|
||||
delta.y *= Math_abs(vector.y);
|
||||
newPos = oldPos.add(delta);
|
||||
}
|
||||
|
||||
// Check if anything changed
|
||||
if (!oldPos.equals(newPos)) {
|
||||
// Automatic Direction
|
||||
if (
|
||||
metaBuilding &&
|
||||
metaBuilding.getRotateAutomaticallyWhilePlacing(this.currentVariant.get()) &&
|
||||
(!metaBuilding.getHasDirectionLockAvailable() ||
|
||||
!this.root.keyMapper
|
||||
.getBinding(KEYMAPPINGS.placement.lockBeltDirection)
|
||||
.isCurrentlyPressed()) &&
|
||||
!this.root.keyMapper
|
||||
.getBinding(KEYMAPPINGS.placementModifiers.placementDisableAutoOrientation)
|
||||
.isCurrentlyPressed()
|
||||
) {
|
||||
const delta = newPos.sub(oldPos);
|
||||
const angleDeg = Math_degrees(delta.angle());
|
||||
this.currentBaseRotation = (Math.round(angleDeg / 90) * 90 + 360) % 360;
|
||||
|
||||
// Holding alt inverts the placement
|
||||
if (
|
||||
this.root.keyMapper
|
||||
.getBinding(KEYMAPPINGS.placementModifiers.placeInverse)
|
||||
.isCurrentlyPressed()
|
||||
) {
|
||||
this.currentBaseRotation = (180 + this.currentBaseRotation) % 360;
|
||||
}
|
||||
}
|
||||
|
||||
// - Using bresenhams algorithmus
|
||||
|
||||
let x0 = oldPos.x;
|
||||
let y0 = oldPos.y;
|
||||
let x1 = newPos.x;
|
||||
let y1 = newPos.y;
|
||||
|
||||
var dx = Math_abs(x1 - x0);
|
||||
var dy = Math_abs(y1 - y0);
|
||||
var sx = x0 < x1 ? 1 : -1;
|
||||
var sy = y0 < y1 ? 1 : -1;
|
||||
var err = dx - dy;
|
||||
|
||||
while (this.currentlyDeleting || this.currentMetaBuilding.get()) {
|
||||
if (this.currentlyDeleting) {
|
||||
const contents = this.root.map.getTileContentXY(x0, y0);
|
||||
if (contents && !contents.queuedForDestroy && !contents.destroyed) {
|
||||
this.root.logic.tryDeleteBuilding(contents);
|
||||
}
|
||||
} else {
|
||||
this.tryPlaceCurrentBuildingAt(new Vector(x0, y0));
|
||||
}
|
||||
if (x0 === x1 && y0 === y1) break;
|
||||
var e2 = 2 * err;
|
||||
if (e2 > -dy) {
|
||||
err -= dy;
|
||||
x0 += sx;
|
||||
}
|
||||
if (e2 < dx) {
|
||||
err += dx;
|
||||
y0 += sy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.lastDragTile = newPos;
|
||||
|
||||
return STOP_PROPAGATION;
|
||||
}
|
||||
}
|
||||
|
||||
update() {
|
||||
// ALways update since the camera might have moved
|
||||
const mousePos = this.root.app.mousePosition;
|
||||
if (mousePos) {
|
||||
this.onMouseMove(mousePos);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* aborts any dragging op
|
||||
*/
|
||||
abortDragging() {
|
||||
this.currentlyDragging = true;
|
||||
this.currentlyDeleting = false;
|
||||
this.initialPlacementVector = null;
|
||||
this.lastDragTile = null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {MetaBuilding} metaBuilding
|
||||
*/
|
||||
startSelection(metaBuilding) {
|
||||
this.currentMetaBuilding.set(metaBuilding);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MetaBuilding} metaBuilding
|
||||
*/
|
||||
onSelectedMetaBuildingChanged(metaBuilding) {
|
||||
this.abortDragging();
|
||||
this.root.hud.signals.selectedPlacementBuildingChanged.dispatch(metaBuilding);
|
||||
if (metaBuilding) {
|
||||
const variant = this.preferredVariants[metaBuilding.getId()] || defaultBuildingVariant;
|
||||
this.currentVariant.set(variant);
|
||||
|
||||
this.fakeEntity = new Entity(null);
|
||||
metaBuilding.setupEntityComponents(this.fakeEntity, null);
|
||||
|
||||
this.fakeEntity.addComponent(
|
||||
new StaticMapEntityComponent({
|
||||
origin: new Vector(0, 0),
|
||||
rotation: 0,
|
||||
tileSize: metaBuilding.getDimensions(this.currentVariant.get()).copy(),
|
||||
blueprintSpriteKey: "",
|
||||
})
|
||||
);
|
||||
metaBuilding.updateVariants(this.fakeEntity, 0, this.currentVariant.get());
|
||||
} else {
|
||||
this.fakeEntity = null;
|
||||
}
|
||||
|
||||
// Since it depends on both, rerender twice
|
||||
this.rerenderVariants();
|
||||
this.variantsAttach = new DynamicDomAttach(this.root, this.variantsElement, {});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -390,151 +138,6 @@ export class HUDBuildingPlacer extends BaseHUDPart {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycles through the variants
|
||||
*/
|
||||
cycleVariants() {
|
||||
const metaBuilding = this.currentMetaBuilding.get();
|
||||
if (!metaBuilding) {
|
||||
this.currentVariant.set(defaultBuildingVariant);
|
||||
} else {
|
||||
const availableVariants = metaBuilding.getAvailableVariants(this.root);
|
||||
const index = availableVariants.indexOf(this.currentVariant.get());
|
||||
assert(
|
||||
index >= 0,
|
||||
"Current variant was invalid: " + this.currentVariant.get() + " out of " + availableVariants
|
||||
);
|
||||
const newIndex = (index + 1) % availableVariants.length;
|
||||
const newVariant = availableVariants[newIndex];
|
||||
this.currentVariant.set(newVariant);
|
||||
|
||||
this.preferredVariants[metaBuilding.getId()] = newVariant;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to rotate
|
||||
*/
|
||||
tryRotate() {
|
||||
const selectedBuilding = this.currentMetaBuilding.get();
|
||||
if (selectedBuilding) {
|
||||
if (
|
||||
this.root.keyMapper
|
||||
.getBinding(KEYMAPPINGS.placement.rotateInverseModifier)
|
||||
.isCurrentlyPressed()
|
||||
) {
|
||||
this.currentBaseRotation = (this.currentBaseRotation + 270) % 360;
|
||||
} else {
|
||||
this.currentBaseRotation = (this.currentBaseRotation + 90) % 360;
|
||||
}
|
||||
|
||||
const staticComp = this.fakeEntity.components.StaticMapEntity;
|
||||
staticComp.rotation = this.currentBaseRotation;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to delete the building under the mouse
|
||||
*/
|
||||
deleteBelowCursor() {
|
||||
const mousePosition = this.root.app.mousePosition;
|
||||
if (!mousePosition) {
|
||||
// Not on screen
|
||||
return;
|
||||
}
|
||||
|
||||
const worldPos = this.root.camera.screenToWorld(mousePosition);
|
||||
const tile = worldPos.toTileSpace();
|
||||
const contents = this.root.map.getTileContent(tile);
|
||||
if (contents) {
|
||||
this.root.logic.tryDeleteBuilding(contents);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Canvas click handler
|
||||
* @param {Vector} mousePos
|
||||
* @param {boolean} cancelAction
|
||||
*/
|
||||
onCanvasClick(mousePos, cancelAction = false) {
|
||||
if (cancelAction) {
|
||||
if (this.currentMetaBuilding.get()) {
|
||||
this.currentMetaBuilding.set(null);
|
||||
} else {
|
||||
this.deleteBelowCursor();
|
||||
}
|
||||
return STOP_PROPAGATION;
|
||||
}
|
||||
|
||||
if (!this.currentMetaBuilding.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
return STOP_PROPAGATION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to place the current building at the given tile
|
||||
* @param {Vector} tile
|
||||
*/
|
||||
tryPlaceCurrentBuildingAt(tile) {
|
||||
if (this.root.camera.zoomLevel < globalConfig.mapChunkOverviewMinZoom) {
|
||||
// Dont allow placing in overview mode
|
||||
return;
|
||||
}
|
||||
// Transform to world space
|
||||
|
||||
const metaBuilding = this.currentMetaBuilding.get();
|
||||
|
||||
const { rotation, rotationVariant } = metaBuilding.computeOptimalDirectionAndRotationVariantAtTile(
|
||||
this.root,
|
||||
tile,
|
||||
this.currentBaseRotation,
|
||||
this.currentVariant.get()
|
||||
);
|
||||
|
||||
if (
|
||||
this.root.logic.tryPlaceBuilding({
|
||||
origin: tile,
|
||||
rotation,
|
||||
rotationVariant,
|
||||
originalRotation: this.currentBaseRotation,
|
||||
building: this.currentMetaBuilding.get(),
|
||||
variant: this.currentVariant.get(),
|
||||
})
|
||||
) {
|
||||
// Succesfully placed
|
||||
|
||||
const entity = this.root.map.getTileContent(tile);
|
||||
assert(entity, "Entity was not actually placed");
|
||||
this.root.signals.entityManuallyPlaced.dispatch(entity);
|
||||
|
||||
if (
|
||||
metaBuilding.getFlipOrientationAfterPlacement() &&
|
||||
!this.root.keyMapper
|
||||
.getBinding(KEYMAPPINGS.placementModifiers.placementDisableAutoOrientation)
|
||||
.isCurrentlyPressed()
|
||||
) {
|
||||
this.currentBaseRotation = (180 + this.currentBaseRotation) % 360;
|
||||
}
|
||||
|
||||
if (
|
||||
!metaBuilding.getStayInPlacementMode() &&
|
||||
!this.root.keyMapper
|
||||
.getBinding(KEYMAPPINGS.placementModifiers.placeMultiple)
|
||||
.isCurrentlyPressed() &&
|
||||
!this.root.app.settings.getAllSettings().alwaysMultiplace
|
||||
) {
|
||||
// Stop placement
|
||||
this.currentMetaBuilding.set(null);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {DrawParameters} parameters
|
||||
@ -555,14 +158,28 @@ export class HUDBuildingPlacer extends BaseHUDPart {
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw direction lock
|
||||
if (this.isDirectionLockActive) {
|
||||
this.drawDirectionLock(parameters);
|
||||
} else {
|
||||
this.drawRegularPlacement(parameters);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DrawParameters} parameters
|
||||
*/
|
||||
drawRegularPlacement(parameters) {
|
||||
const mousePosition = this.root.app.mousePosition;
|
||||
if (!mousePosition) {
|
||||
// Not on screen
|
||||
return;
|
||||
}
|
||||
|
||||
const metaBuilding = this.currentMetaBuilding.get();
|
||||
|
||||
const worldPos = this.root.camera.screenToWorld(mousePosition);
|
||||
const tile = worldPos.toTileSpace();
|
||||
const mouseTile = worldPos.toTileSpace();
|
||||
|
||||
// Compute best rotation variant
|
||||
const {
|
||||
@ -571,7 +188,7 @@ export class HUDBuildingPlacer extends BaseHUDPart {
|
||||
connectedEntities,
|
||||
} = metaBuilding.computeOptimalDirectionAndRotationVariantAtTile(
|
||||
this.root,
|
||||
tile,
|
||||
mouseTile,
|
||||
this.currentBaseRotation,
|
||||
this.currentVariant.get()
|
||||
);
|
||||
@ -584,7 +201,7 @@ export class HUDBuildingPlacer extends BaseHUDPart {
|
||||
.getCenter()
|
||||
.toWorldSpace();
|
||||
|
||||
const startWsPoint = tile.toWorldSpaceCenterOfTile();
|
||||
const startWsPoint = mouseTile.toWorldSpaceCenterOfTile();
|
||||
|
||||
const startOffset = connectedWsPoint
|
||||
.sub(startWsPoint)
|
||||
@ -609,14 +226,14 @@ export class HUDBuildingPlacer extends BaseHUDPart {
|
||||
|
||||
// Synchronize rotation and origin
|
||||
const staticComp = this.fakeEntity.components.StaticMapEntity;
|
||||
staticComp.origin = tile;
|
||||
staticComp.origin = mouseTile;
|
||||
staticComp.rotation = rotation;
|
||||
staticComp.tileSize = metaBuilding.getDimensions(this.currentVariant.get());
|
||||
metaBuilding.updateVariants(this.fakeEntity, rotationVariant, this.currentVariant.get());
|
||||
|
||||
// Check if we could place the buildnig
|
||||
const canBuild = this.root.logic.checkCanPlaceBuilding({
|
||||
origin: tile,
|
||||
origin: mouseTile,
|
||||
rotation,
|
||||
rotationVariant,
|
||||
building: metaBuilding,
|
||||
@ -653,45 +270,53 @@ export class HUDBuildingPlacer extends BaseHUDPart {
|
||||
const previewSprite = metaBuilding.getBlueprintSprite(rotationVariant, this.currentVariant.get());
|
||||
staticComp.origin = worldPos.divideScalar(globalConfig.tileSize).subScalars(0.5, 0.5);
|
||||
staticComp.drawSpriteOnFullEntityBounds(parameters, previewSprite);
|
||||
staticComp.origin = tile;
|
||||
staticComp.origin = mouseTile;
|
||||
|
||||
// Draw ejectors
|
||||
if (canBuild) {
|
||||
this.drawMatchingAcceptorsAndEjectors(parameters);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw direction lock
|
||||
/**
|
||||
* @param {DrawParameters} parameters
|
||||
*/
|
||||
drawDirectionLock(parameters) {
|
||||
const mousePosition = this.root.app.mousePosition;
|
||||
if (!mousePosition) {
|
||||
// Not on screen
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
metaBuilding.getHasDirectionLockAvailable() &&
|
||||
this.root.keyMapper.getBinding(KEYMAPPINGS.placement.lockBeltDirection).isCurrentlyPressed()
|
||||
) {
|
||||
if (this.lastDragTile) {
|
||||
parameters.context.fillStyle = THEME.map.selectionBackground;
|
||||
parameters.context.strokeStyle = THEME.map.selectionOverlay;
|
||||
const mouseWorld = this.root.camera.screenToWorld(mousePosition);
|
||||
const mouseTile = mouseWorld.toTileSpace();
|
||||
parameters.context.fillStyle = THEME.map.directionLock;
|
||||
parameters.context.strokeStyle = THEME.map.directionLock;
|
||||
parameters.context.lineWidth = 3;
|
||||
|
||||
parameters.context.beginCircle(mouseWorld.x, mouseWorld.y, 4);
|
||||
parameters.context.fill();
|
||||
|
||||
if (this.lastDragTile) {
|
||||
const startLine = this.lastDragTile.toWorldSpaceCenterOfTile();
|
||||
const endLine = tile.toWorldSpaceCenterOfTile();
|
||||
const endLine = mouseTile.toWorldSpaceCenterOfTile();
|
||||
const midLine = this.currentDirectionLockCorner.toWorldSpaceCenterOfTile();
|
||||
|
||||
parameters.context.beginCircle(startLine.x, startLine.y, 7);
|
||||
parameters.context.fill();
|
||||
parameters.context.stroke();
|
||||
|
||||
parameters.context.beginPath();
|
||||
parameters.context.moveTo(startLine.x, startLine.y);
|
||||
parameters.context.lineTo(midLine.x, midLine.y);
|
||||
parameters.context.lineTo(endLine.x, endLine.y);
|
||||
parameters.context.stroke();
|
||||
|
||||
parameters.context.beginCircle(endLine.x, endLine.y, 4);
|
||||
parameters.context.fill();
|
||||
parameters.context.stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {DrawParameters} parameters
|
||||
*/
|
||||
drawMatchingAcceptorsAndEjectors(parameters) {
|
||||
|
523
src/js/game/hud/parts/building_placer_logic.js
Normal file
523
src/js/game/hud/parts/building_placer_logic.js
Normal file
@ -0,0 +1,523 @@
|
||||
import { Math_abs, Math_degrees, Math_round } from "../../../core/builtins";
|
||||
import { globalConfig } from "../../../core/config";
|
||||
import { Signal, STOP_PROPAGATION } from "../../../core/signal";
|
||||
import { TrackedState } from "../../../core/tracked_state";
|
||||
import { findCornerBetweenPoints } from "../../../core/utils";
|
||||
import { Vector } from "../../../core/vector";
|
||||
import { enumMouseButton } from "../../camera";
|
||||
import { StaticMapEntityComponent } from "../../components/static_map_entity";
|
||||
import { Entity } from "../../entity";
|
||||
import { KEYMAPPINGS } from "../../key_action_mapper";
|
||||
import { defaultBuildingVariant, MetaBuilding } from "../../meta_building";
|
||||
import { BaseHUDPart } from "../base_hud_part";
|
||||
|
||||
/**
|
||||
* Contains all logic for the building placer - this doesn't include the rendering
|
||||
* of info boxes or drawing.
|
||||
*/
|
||||
export class HUDBuildingPlacerLogic extends BaseHUDPart {
|
||||
/**
|
||||
* Initializes the logic
|
||||
* @see BaseHUDPart.initialize
|
||||
*/
|
||||
initialize() {
|
||||
/**
|
||||
* We use a fake entity to get information about how a building will look
|
||||
* once placed
|
||||
* @type {Entity}
|
||||
*/
|
||||
this.fakeEntity = null;
|
||||
|
||||
// Signals
|
||||
this.signals = {
|
||||
variantChanged: new Signal(),
|
||||
};
|
||||
|
||||
/**
|
||||
* The current building
|
||||
* @type {TypedTrackedState<MetaBuilding?>}
|
||||
*/
|
||||
this.currentMetaBuilding = new TrackedState(this.onSelectedMetaBuildingChanged, this);
|
||||
|
||||
/**
|
||||
* The current rotation
|
||||
* @type {number}
|
||||
*/
|
||||
this.currentBaseRotation = 0;
|
||||
|
||||
/**
|
||||
* Whether we are currently dragging
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.currentlyDragging = false;
|
||||
|
||||
/**
|
||||
* Current building variant
|
||||
* @type {TypedTrackedState<string>}
|
||||
*/
|
||||
this.currentVariant = new TrackedState(() => this.signals.variantChanged.dispatch());
|
||||
|
||||
/**
|
||||
* Whether we are currently drag-deleting
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.currentlyDeleting = false;
|
||||
|
||||
/**
|
||||
* Stores which variants for each building we prefer, this is based on what
|
||||
* the user last selected
|
||||
* @type {Object.<string, string>}
|
||||
*/
|
||||
this.preferredVariants = {};
|
||||
|
||||
/**
|
||||
* The tile we last dragged from
|
||||
* @type {Vector}
|
||||
*/
|
||||
this.lastDragTile = null;
|
||||
|
||||
this.initializeBindings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes all bindings
|
||||
*/
|
||||
initializeBindings() {
|
||||
// KEYBINDINGS
|
||||
const keyActionMapper = this.root.keyMapper;
|
||||
keyActionMapper.getBinding(KEYMAPPINGS.placement.rotateWhilePlacing).add(this.tryRotate, this);
|
||||
keyActionMapper.getBinding(KEYMAPPINGS.placement.cycleBuildingVariants).add(this.cycleVariants, this);
|
||||
keyActionMapper
|
||||
.getBinding(KEYMAPPINGS.placement.abortBuildingPlacement)
|
||||
.add(this.abortPlacement, this);
|
||||
keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.abortPlacement, this);
|
||||
|
||||
// BINDINGS TO GAME EVENTS
|
||||
this.root.hud.signals.buildingsSelectedForCopy.add(this.abortPlacement, this);
|
||||
this.root.hud.signals.pasteBlueprintRequested.add(this.abortPlacement, this);
|
||||
this.root.signals.storyGoalCompleted.add(() => this.signals.variantChanged.dispatch());
|
||||
this.root.signals.upgradePurchased.add(() => this.signals.variantChanged.dispatch());
|
||||
|
||||
// MOUSE BINDINGS
|
||||
this.root.camera.downPreHandler.add(this.onMouseDown, this);
|
||||
this.root.camera.movePreHandler.add(this.onMouseMove, this);
|
||||
this.root.camera.upPostHandler.add(this.onMouseUp, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the direction lock is currently active
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get isDirectionLockActive() {
|
||||
const metaBuilding = this.currentMetaBuilding.get();
|
||||
return (
|
||||
metaBuilding &&
|
||||
metaBuilding.getHasDirectionLockAvailable() &&
|
||||
this.root.keyMapper.getBinding(KEYMAPPINGS.placement.lockBeltDirection).pressed
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current direction lock corner, that is, the corner between
|
||||
* mouse and original start point
|
||||
* @returns {Vector|null}
|
||||
*/
|
||||
get currentDirectionLockCorner() {
|
||||
const mousePosition = this.root.app.mousePosition;
|
||||
if (!mousePosition) {
|
||||
// Not on screen
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!this.lastDragTile) {
|
||||
// Haven't dragged yet
|
||||
return null;
|
||||
}
|
||||
|
||||
// Figure which points the line visits
|
||||
const worldPos = this.root.camera.screenToWorld(mousePosition);
|
||||
const mouseTile = worldPos.toTileSpace();
|
||||
const cornerTile = findCornerBetweenPoints(this.lastDragTile, mouseTile);
|
||||
return cornerTile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aborts the placement
|
||||
*/
|
||||
abortPlacement() {
|
||||
if (this.currentMetaBuilding.get()) {
|
||||
this.currentMetaBuilding.set(null);
|
||||
return STOP_PROPAGATION;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Aborts any dragging
|
||||
*/
|
||||
abortDragging() {
|
||||
this.currentlyDragging = true;
|
||||
this.currentlyDeleting = false;
|
||||
this.initialPlacementVector = null;
|
||||
this.lastDragTile = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BaseHUDPart.update
|
||||
*/
|
||||
update() {
|
||||
// Always update since the camera might have moved
|
||||
const mousePos = this.root.app.mousePosition;
|
||||
if (mousePos) {
|
||||
this.onMouseMove(mousePos);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to rotate the current building
|
||||
*/
|
||||
tryRotate() {
|
||||
const selectedBuilding = this.currentMetaBuilding.get();
|
||||
if (selectedBuilding) {
|
||||
if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateInverseModifier).pressed) {
|
||||
this.currentBaseRotation = (this.currentBaseRotation + 270) % 360;
|
||||
} else {
|
||||
this.currentBaseRotation = (this.currentBaseRotation + 90) % 360;
|
||||
}
|
||||
const staticComp = this.fakeEntity.components.StaticMapEntity;
|
||||
staticComp.rotation = this.currentBaseRotation;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Tries to delete the building under the mouse
|
||||
*/
|
||||
deleteBelowCursor() {
|
||||
const mousePosition = this.root.app.mousePosition;
|
||||
if (!mousePosition) {
|
||||
// Not on screen
|
||||
return;
|
||||
}
|
||||
|
||||
const worldPos = this.root.camera.screenToWorld(mousePosition);
|
||||
const tile = worldPos.toTileSpace();
|
||||
const contents = this.root.map.getTileContent(tile);
|
||||
if (contents) {
|
||||
this.root.logic.tryDeleteBuilding(contents);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Canvas click handler
|
||||
* @param {Vector} mousePos
|
||||
* @param {boolean} cancelAction
|
||||
*/
|
||||
onCanvasClick(mousePos, cancelAction = false) {
|
||||
if (cancelAction) {
|
||||
if (this.currentMetaBuilding.get()) {
|
||||
this.currentMetaBuilding.set(null);
|
||||
} else {
|
||||
this.deleteBelowCursor();
|
||||
}
|
||||
return STOP_PROPAGATION;
|
||||
}
|
||||
|
||||
if (!this.currentMetaBuilding.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
return STOP_PROPAGATION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to place the current building at the given tile
|
||||
* @param {Vector} tile
|
||||
*/
|
||||
tryPlaceCurrentBuildingAt(tile) {
|
||||
if (this.root.camera.zoomLevel < globalConfig.mapChunkOverviewMinZoom) {
|
||||
// Dont allow placing in overview mode
|
||||
return;
|
||||
}
|
||||
|
||||
const metaBuilding = this.currentMetaBuilding.get();
|
||||
const { rotation, rotationVariant } = metaBuilding.computeOptimalDirectionAndRotationVariantAtTile(
|
||||
this.root,
|
||||
tile,
|
||||
this.currentBaseRotation,
|
||||
this.currentVariant.get()
|
||||
);
|
||||
|
||||
const entity = this.root.logic.tryPlaceBuilding({
|
||||
origin: tile,
|
||||
rotation,
|
||||
rotationVariant,
|
||||
originalRotation: this.currentBaseRotation,
|
||||
building: this.currentMetaBuilding.get(),
|
||||
variant: this.currentVariant.get(),
|
||||
});
|
||||
|
||||
if (entity) {
|
||||
// Succesfully placed, find which entity we actually placed
|
||||
this.root.signals.entityManuallyPlaced.dispatch(entity);
|
||||
|
||||
// Check if we should flip the orientation (used for tunnels)
|
||||
if (
|
||||
metaBuilding.getFlipOrientationAfterPlacement() &&
|
||||
!this.root.keyMapper.getBinding(
|
||||
KEYMAPPINGS.placementModifiers.placementDisableAutoOrientation
|
||||
).pressed
|
||||
) {
|
||||
this.currentBaseRotation = (180 + this.currentBaseRotation) % 360;
|
||||
}
|
||||
|
||||
// Check if we should stop placement
|
||||
if (
|
||||
!metaBuilding.getStayInPlacementMode() &&
|
||||
!this.root.keyMapper.getBinding(KEYMAPPINGS.placementModifiers.placeMultiple).pressed &&
|
||||
!this.root.app.settings.getAllSettings().alwaysMultiplace
|
||||
) {
|
||||
// Stop placement
|
||||
this.currentMetaBuilding.set(null);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycles through the variants
|
||||
*/
|
||||
cycleVariants() {
|
||||
const metaBuilding = this.currentMetaBuilding.get();
|
||||
if (!metaBuilding) {
|
||||
this.currentVariant.set(defaultBuildingVariant);
|
||||
} else {
|
||||
const availableVariants = metaBuilding.getAvailableVariants(this.root);
|
||||
const index = availableVariants.indexOf(this.currentVariant.get());
|
||||
assert(
|
||||
index >= 0,
|
||||
"Current variant was invalid: " + this.currentVariant.get() + " out of " + availableVariants
|
||||
);
|
||||
const newIndex = (index + 1) % availableVariants.length;
|
||||
const newVariant = availableVariants[newIndex];
|
||||
this.currentVariant.set(newVariant);
|
||||
|
||||
this.preferredVariants[metaBuilding.getId()] = newVariant;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the direction locked placement between two points after
|
||||
* releasing the mouse
|
||||
*/
|
||||
executeDirectionLockedPlacement() {
|
||||
const mousePosition = this.root.app.mousePosition;
|
||||
if (!mousePosition) {
|
||||
// Not on screen
|
||||
return;
|
||||
}
|
||||
|
||||
// Figure which points the line visits
|
||||
const worldPos = this.root.camera.screenToWorld(mousePosition);
|
||||
const mouseTile = worldPos.toTileSpace();
|
||||
const startTile = this.lastDragTile;
|
||||
|
||||
// Place from start to corner
|
||||
const pathToCorner = this.currentDirectionLockCorner.sub(startTile);
|
||||
const deltaToCorner = pathToCorner.normalize().round();
|
||||
const lengthToCorner = Math_round(pathToCorner.length());
|
||||
let currentPos = startTile.copy();
|
||||
|
||||
this.currentBaseRotation = (Math.round(Math_degrees(deltaToCorner.angle()) / 90) * 90 + 360) % 360;
|
||||
|
||||
for (let i = 0; i < lengthToCorner; ++i) {
|
||||
this.tryPlaceCurrentBuildingAt(currentPos);
|
||||
currentPos.addInplace(deltaToCorner);
|
||||
}
|
||||
|
||||
// Place from corner to end
|
||||
const pathFromCorner = mouseTile.sub(this.currentDirectionLockCorner);
|
||||
const deltaFromCorner = pathFromCorner.normalize().round();
|
||||
const lengthFromCorner = Math_round(pathFromCorner.length());
|
||||
this.currentBaseRotation = (Math.round(Math_degrees(deltaFromCorner.angle()) / 90) * 90 + 360) % 360;
|
||||
|
||||
for (let i = 0; i < lengthFromCorner + 1; ++i) {
|
||||
this.tryPlaceCurrentBuildingAt(currentPos);
|
||||
currentPos.addInplace(deltaFromCorner);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a given building
|
||||
* @param {MetaBuilding} metaBuilding
|
||||
*/
|
||||
startSelection(metaBuilding) {
|
||||
this.currentMetaBuilding.set(metaBuilding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the selected buildings changed
|
||||
* @param {MetaBuilding} metaBuilding
|
||||
*/
|
||||
onSelectedMetaBuildingChanged(metaBuilding) {
|
||||
this.abortDragging();
|
||||
this.root.hud.signals.selectedPlacementBuildingChanged.dispatch(metaBuilding);
|
||||
if (metaBuilding) {
|
||||
const variant = this.preferredVariants[metaBuilding.getId()] || defaultBuildingVariant;
|
||||
this.currentVariant.set(variant);
|
||||
|
||||
this.fakeEntity = new Entity(null);
|
||||
metaBuilding.setupEntityComponents(this.fakeEntity, null);
|
||||
|
||||
this.fakeEntity.addComponent(
|
||||
new StaticMapEntityComponent({
|
||||
origin: new Vector(0, 0),
|
||||
rotation: 0,
|
||||
tileSize: metaBuilding.getDimensions(this.currentVariant.get()).copy(),
|
||||
blueprintSpriteKey: "",
|
||||
})
|
||||
);
|
||||
metaBuilding.updateVariants(this.fakeEntity, 0, this.currentVariant.get());
|
||||
} else {
|
||||
this.fakeEntity = null;
|
||||
}
|
||||
|
||||
// Since it depends on both, rerender twice
|
||||
this.signals.variantChanged.dispatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* mouse down pre handler
|
||||
* @param {Vector} pos
|
||||
* @param {enumMouseButton} button
|
||||
*/
|
||||
onMouseDown(pos, button) {
|
||||
if (this.root.camera.getIsMapOverlayActive()) {
|
||||
// We do not allow dragging if the overlay is active
|
||||
return;
|
||||
}
|
||||
|
||||
const metaBuilding = this.currentMetaBuilding.get();
|
||||
|
||||
// Placement
|
||||
if (button === enumMouseButton.left && metaBuilding) {
|
||||
this.currentlyDragging = true;
|
||||
this.currentlyDeleting = false;
|
||||
this.lastDragTile = this.root.camera.screenToWorld(pos).toTileSpace();
|
||||
|
||||
// Place initial building, but only if direction lock is not active
|
||||
if (!this.isDirectionLockActive) {
|
||||
this.tryPlaceCurrentBuildingAt(this.lastDragTile);
|
||||
}
|
||||
return STOP_PROPAGATION;
|
||||
}
|
||||
|
||||
// Deletion
|
||||
if (button === enumMouseButton.right && !this.currentMetaBuilding.get()) {
|
||||
this.currentlyDragging = true;
|
||||
this.currentlyDeleting = true;
|
||||
this.lastDragTile = this.root.camera.screenToWorld(pos).toTileSpace();
|
||||
this.currentMetaBuilding.set(null);
|
||||
return STOP_PROPAGATION;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* mouse move pre handler
|
||||
* @param {Vector} pos
|
||||
*/
|
||||
onMouseMove(pos) {
|
||||
if (this.root.camera.getIsMapOverlayActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for direction lock
|
||||
if (this.isDirectionLockActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
const metaBuilding = this.currentMetaBuilding.get();
|
||||
if ((metaBuilding || this.currentlyDeleting) && this.lastDragTile) {
|
||||
const oldPos = this.lastDragTile;
|
||||
let newPos = this.root.camera.screenToWorld(pos).toTileSpace();
|
||||
|
||||
// Check if camera is moving, since then we do nothing
|
||||
if (this.root.camera.desiredCenter) {
|
||||
this.lastDragTile = newPos;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if anything changed
|
||||
if (!oldPos.equals(newPos)) {
|
||||
// Automatic Direction
|
||||
if (
|
||||
metaBuilding &&
|
||||
metaBuilding.getRotateAutomaticallyWhilePlacing(this.currentVariant.get()) &&
|
||||
!this.root.keyMapper.getBinding(
|
||||
KEYMAPPINGS.placementModifiers.placementDisableAutoOrientation
|
||||
).pressed
|
||||
) {
|
||||
const delta = newPos.sub(oldPos);
|
||||
const angleDeg = Math_degrees(delta.angle());
|
||||
this.currentBaseRotation = (Math.round(angleDeg / 90) * 90 + 360) % 360;
|
||||
|
||||
// Holding alt inverts the placement
|
||||
if (this.root.keyMapper.getBinding(KEYMAPPINGS.placementModifiers.placeInverse).pressed) {
|
||||
this.currentBaseRotation = (180 + this.currentBaseRotation) % 360;
|
||||
}
|
||||
}
|
||||
|
||||
// bresenham
|
||||
let x0 = oldPos.x;
|
||||
let y0 = oldPos.y;
|
||||
let x1 = newPos.x;
|
||||
let y1 = newPos.y;
|
||||
|
||||
var dx = Math_abs(x1 - x0);
|
||||
var dy = Math_abs(y1 - y0);
|
||||
var sx = x0 < x1 ? 1 : -1;
|
||||
var sy = y0 < y1 ? 1 : -1;
|
||||
var err = dx - dy;
|
||||
|
||||
while (this.currentlyDeleting || this.currentMetaBuilding.get()) {
|
||||
if (this.currentlyDeleting) {
|
||||
const contents = this.root.map.getTileContentXY(x0, y0);
|
||||
if (contents && !contents.queuedForDestroy && !contents.destroyed) {
|
||||
this.root.logic.tryDeleteBuilding(contents);
|
||||
}
|
||||
} else {
|
||||
this.tryPlaceCurrentBuildingAt(new Vector(x0, y0));
|
||||
}
|
||||
if (x0 === x1 && y0 === y1) break;
|
||||
var e2 = 2 * err;
|
||||
if (e2 > -dy) {
|
||||
err -= dy;
|
||||
x0 += sx;
|
||||
}
|
||||
if (e2 < dx) {
|
||||
err += dx;
|
||||
y0 += sy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.lastDragTile = newPos;
|
||||
return STOP_PROPAGATION;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mouse up handler
|
||||
*/
|
||||
onMouseUp() {
|
||||
if (this.root.camera.getIsMapOverlayActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for direction lock
|
||||
if (this.lastDragTile && this.currentlyDragging && this.isDirectionLockActive) {
|
||||
this.executeDirectionLockedPlacement();
|
||||
}
|
||||
|
||||
this.abortDragging();
|
||||
}
|
||||
}
|
@ -177,7 +177,7 @@ export class HUDMassSelector extends BaseHUDPart {
|
||||
* @param {enumMouseButton} mouseButton
|
||||
*/
|
||||
onMouseDown(pos, mouseButton) {
|
||||
if (!this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectStart).isCurrentlyPressed()) {
|
||||
if (!this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectStart).pressed) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -185,11 +185,7 @@ export class HUDMassSelector extends BaseHUDPart {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!this.root.keyMapper
|
||||
.getBinding(KEYMAPPINGS.massSelect.massSelectSelectMultiple)
|
||||
.isCurrentlyPressed()
|
||||
) {
|
||||
if (!this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectSelectMultiple).pressed) {
|
||||
// Start new selection
|
||||
this.selectedUids = new Set();
|
||||
}
|
||||
|
@ -261,14 +261,16 @@ export class Keybinding {
|
||||
|
||||
/**
|
||||
* Returns whether this binding is currently pressed
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isCurrentlyPressed() {
|
||||
get pressed() {
|
||||
// Check if the key is down
|
||||
if (this.app.inputMgr.keysDown.has(this.keyCode)) {
|
||||
// Check if it is the top reciever
|
||||
const reciever = this.keyMapper.inputReceiver;
|
||||
return this.app.inputMgr.getTopReciever() === reciever;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -151,6 +151,7 @@ export class GameLogic {
|
||||
* @param {number} param0.rotationVariant
|
||||
* @param {string} param0.variant
|
||||
* @param {MetaBuilding} param0.building
|
||||
* @returns {Entity}
|
||||
*/
|
||||
tryPlaceBuilding({ origin, rotation, rotationVariant, originalRotation, variant, building }) {
|
||||
if (this.checkCanPlaceBuilding({ origin, rotation, rotationVariant, variant, building })) {
|
||||
@ -170,13 +171,13 @@ export class GameLogic {
|
||||
if (contents) {
|
||||
if (!this.tryDeleteBuilding(contents)) {
|
||||
logger.error("Building has replaceable component but is also unremovable");
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
building.createAndPlaceEntity({
|
||||
const entity = building.createAndPlaceEntity({
|
||||
root: this.root,
|
||||
origin,
|
||||
rotation,
|
||||
@ -186,10 +187,9 @@ export class GameLogic {
|
||||
});
|
||||
|
||||
this.root.soundProxy.playUi(building.getPlacementSound());
|
||||
|
||||
return true;
|
||||
return entity;
|
||||
}
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -9,6 +9,8 @@
|
||||
"selectionOutline": "rgba(74, 163, 223, 0.5)",
|
||||
"selectionBackground": "rgba(74, 163, 223, 0.2)",
|
||||
|
||||
"directionLock": "rgb(74, 237, 134)",
|
||||
|
||||
"resources": {
|
||||
"shape": "#3d3f4a",
|
||||
"red": "#4a3d3f",
|
||||
|
@ -9,6 +9,8 @@
|
||||
"selectionOutline": "rgba(74, 163, 223, 0.5)",
|
||||
"selectionBackground": "rgba(74, 163, 223, 0.2)",
|
||||
|
||||
"directionLock": "rgb(74, 237, 134)",
|
||||
|
||||
"resources": {
|
||||
"shape": "#eaebec",
|
||||
"red": "#ffbfc1",
|
||||
|
Loading…
Reference in New Issue
Block a user