From dfe1e64b279f9c5ccea4bb4857bfb34164d724dd Mon Sep 17 00:00:00 2001 From: tobspr Date: Wed, 27 May 2020 15:03:36 +0200 Subject: [PATCH] Further blueprint improvements --- src/css/states/changelog.scss | 3 +- src/js/changelog.js | 15 +-- src/js/core/config.js | 2 +- src/js/core/input_distributor.js | 31 ------ src/js/core/input_receiver.js | 3 - src/js/game/buildings/miner.js | 17 --- src/js/game/game_system_with_filter.js | 3 +- src/js/game/hud/parts/blueprint.js | 114 ++++++++++++-------- src/js/game/hud/parts/blueprint_placer.js | 19 +++- src/js/game/hud/parts/building_placer.js | 22 +++- src/js/game/hud/parts/keybinding_overlay.js | 20 ++-- src/js/game/key_action_mapper.js | 1 + src/js/game/logic.js | 32 +++--- src/js/game/meta_building.js | 13 --- src/js/game/root.js | 7 ++ src/js/game/systems/miner.js | 7 ++ translations/base-en.yaml | 2 + 17 files changed, 159 insertions(+), 152 deletions(-) diff --git a/src/css/states/changelog.scss b/src/css/states/changelog.scss index 69b7864c..8302da2c 100644 --- a/src/css/states/changelog.scss +++ b/src/css/states/changelog.scss @@ -20,8 +20,7 @@ @include SuperSmallText; @include S(padding-left, 20px); strong { - background: $colorBlueBright; - color: #fff; + color: #aaa; text-transform: uppercase; @include S(padding, 1px, 2px); @include S(margin-right, 3px); diff --git a/src/js/changelog.js b/src/js/changelog.js index 10c78d3a..fe398aae 100644 --- a/src/js/changelog.js +++ b/src/js/changelog.js @@ -3,24 +3,27 @@ export const CHANGELOG = [ version: "1.1.0", date: "unreleased", entries: [ - "UX Added background to toolbar to increase contrast", - "UX Added confirmation when deleting more than 500 buildings at a time", + "Allow changing all keybindings, including CTRL, ALT and SHIFT", + "Allow holding SHIFT to rotate counter clockwise", + "Added confirmation when deleting more than 500 buildings at a time", + "Added background to toolbar to increase contrast", + "Allow placing extractors anywhere again, but they don't work at all if not placed on a resource", ], }, { version: "1.0.4", date: "26.05.2020", entries: [ - "Balancing Reduce cost of first painting upgrade, and change 'Shape Processing' to 'Cutting, Rotating & Stacking'", - "Tutorial Add dialog after completing level 2 to check out the upgrades tab.", - "Misc Allow changing the keybindings in the demo version", + "Reduce cost of first painting upgrade, and change 'Shape Processing' to 'Cutting, Rotating & Stacking'", + "Add dialog after completing level 2 to check out the upgrades tab.", + "Allow changing the keybindings in the demo version", ], }, { version: "1.0.3", date: "24.05.2020", entries: [ - "Balancing Reduced the amount of shapes required for the first 5 levels to make it easier to get into the game.", + "Reduced the amount of shapes required for the first 5 levels to make it easier to get into the game.", ], }, { diff --git a/src/js/core/config.js b/src/js/core/config.js index f5bdbf76..953af4e7 100644 --- a/src/js/core/config.js +++ b/src/js/core/config.js @@ -40,7 +40,7 @@ export const globalConfig = { // Map mapChunkSize: 16, - mapChunkPrerenderMinZoom: 1.3, + mapChunkPrerenderMinZoom: 1.15, mapChunkOverviewMinZoom: 0.7, // Belt speeds diff --git a/src/js/core/input_distributor.js b/src/js/core/input_distributor.js index e0152774..4ed1429c 100644 --- a/src/js/core/input_distributor.js +++ b/src/js/core/input_distributor.js @@ -23,10 +23,6 @@ export class InputDistributor { /** @type {Array} */ this.filters = []; - this.shiftIsDown = false; - this.altIsDown = false; - this.ctrlIsDown = false; - this.bindToEvents(); } @@ -176,27 +172,13 @@ export class InputDistributor { * Handles when the page got blurred */ handleBlur() { - this.ctrlIsDown = false; - this.shiftIsDown = false; - this.altIsDown = false; this.forwardToReceiver("pageBlur", {}); - this.forwardToReceiver("shiftUp", {}); } /** * @param {KeyboardEvent} event */ handleKeydown(event) { - if (event.keyCode === 16) { - this.shiftIsDown = true; - } - if (event.keyCode === 17) { - this.ctrlIsDown = true; - } - if (event.keyCode === 18) { - this.altIsDown = true; - } - if ( // TAB event.keyCode === 9 || @@ -230,19 +212,6 @@ export class InputDistributor { * @param {KeyboardEvent} event */ handleKeyup(event) { - if (event.keyCode === 16) { - this.shiftIsDown = false; - this.forwardToReceiver("shiftUp", {}); - } - if (event.keyCode === 17) { - this.ctrlIsDown = false; - this.forwardToReceiver("ctrlUp", {}); - } - if (event.keyCode === 18) { - this.altIsDown = false; - this.forwardToReceiver("altUp", {}); - } - this.forwardToReceiver("keyup", { keyCode: event.keyCode, shift: event.shiftKey, diff --git a/src/js/core/input_receiver.js b/src/js/core/input_receiver.js index 1a373905..ae54f24d 100644 --- a/src/js/core/input_receiver.js +++ b/src/js/core/input_receiver.js @@ -9,9 +9,6 @@ export class InputReceiver { this.keydown = new Signal(); this.keyup = new Signal(); this.pageBlur = new Signal(); - this.shiftUp = new Signal(); - this.altUp = new Signal(); - this.ctrlUp = new Signal(); // Dispatched on destroy this.destroyed = new Signal(); diff --git a/src/js/game/buildings/miner.js b/src/js/game/buildings/miner.js index 25788917..ed87bc85 100644 --- a/src/js/game/buildings/miner.js +++ b/src/js/game/buildings/miner.js @@ -41,23 +41,6 @@ export class MetaMinerBuilding extends MetaBuilding { return super.getAvailableVariants(root); } - /** - * @param {GameRoot} root - * @param {object} param0 - * @param {Vector} param0.origin - * @param {number} param0.rotation - * @param {number} param0.rotationVariant - * @param {string} param0.variant - */ - performAdditionalPlacementChecks(root, { origin, rotation, rotationVariant, variant }) { - // Make sure its placed above a resource - const lowerLayer = root.map.getLowerLayerContentXY(origin.x, origin.y); - if (!lowerLayer) { - return false; - } - return true; - } - /** * Creates the entity at the given location * @param {Entity} entity diff --git a/src/js/game/game_system_with_filter.js b/src/js/game/game_system_with_filter.js index 52f8ddfe..24a719fb 100644 --- a/src/js/game/game_system_with_filter.js +++ b/src/js/game/game_system_with_filter.js @@ -34,6 +34,7 @@ export class GameSystemWithFilter extends GameSystem { this.root.signals.entityQueuedForDestroy.add(this.internalPopEntityIfMatching, this); this.root.signals.postLoadHook.add(this.internalPostLoadHook, this); + this.root.signals.bulkOperationFinished.add(this.refreshCaches, this); } /** @@ -175,7 +176,7 @@ export class GameSystemWithFilter extends GameSystem { internalRegisterEntity(entity) { this.allEntities.push(entity); - if (this.root.gameInitialized) { + if (this.root.gameInitialized && !this.root.bulkOperationRunning) { // Sort entities by uid so behaviour is predictable this.allEntities.sort((a, b) => a.uid - b.uid); } diff --git a/src/js/game/hud/parts/blueprint.js b/src/js/game/hud/parts/blueprint.js index f6dccc4c..59271469 100644 --- a/src/js/game/hud/parts/blueprint.js +++ b/src/js/game/hud/parts/blueprint.js @@ -1,10 +1,9 @@ -import { GameRoot } from "../../root"; +import { DrawParameters } from "../../../core/draw_parameters"; +import { Loader } from "../../../core/loader"; +import { createLogger } from "../../../core/logging"; 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"; +import { GameRoot } from "../../root"; const logger = createLogger("blueprint"); @@ -17,6 +16,7 @@ export class Blueprint { } /** + * Creates a new blueprint from the given entity uids * @param {GameRoot} root * @param {Array} uids */ @@ -48,7 +48,7 @@ export class Blueprint { } /** - * + * Draws the blueprint at the given origin * @param {DrawParameters} parameters */ draw(parameters, tile) { @@ -93,6 +93,31 @@ export class Blueprint { } /** + * 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 * @param {GameRoot} root * @param {Vector} tile */ @@ -123,54 +148,57 @@ export class Blueprint { } /** + * Attempts to place the blueprint at the given tile * @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) { + 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) { - 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; - } + if (contents && !contents.components.ReplaceableMapEntity) { + placeable = false; + break placementCheck; } } } - const clone = entity.duplicateWithoutContents(); - clone.components.StaticMapEntity.origin.addInplace(tile); + 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; + } + } + } + } - root.map.placeStaticEntity(clone); - root.entityMgr.registerEntity(clone); - anyPlaced = true; + const clone = entity.duplicateWithoutContents(); + clone.components.StaticMapEntity.origin.addInplace(tile); + + root.map.placeStaticEntity(clone); + root.entityMgr.registerEntity(clone); + anyPlaced = true; + } } - } - return anyPlaced; + return anyPlaced; + }); } } diff --git a/src/js/game/hud/parts/blueprint_placer.js b/src/js/game/hud/parts/blueprint_placer.js index 32993ffc..a3ff0f14 100644 --- a/src/js/game/hud/parts/blueprint_placer.js +++ b/src/js/game/hud/parts/blueprint_placer.js @@ -21,6 +21,7 @@ export class HUDBlueprintPlacer extends BaseHUDPart { keyActionMapper .getBinding(KEYMAPPINGS.placement.abortBuildingPlacement) .add(this.abortPlacement, this); + keyActionMapper.getBinding(KEYMAPPINGS.placement.rotateWhilePlacing).add(this.rotateBlueprint, this); this.root.camera.downPreHandler.add(this.onMouseDown, this); this.root.camera.movePreHandler.add(this.onMouseMove, this); @@ -54,13 +55,13 @@ export class HUDBlueprintPlacer extends BaseHUDPart { 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); - } + // This actually feels weird + // if (!this.root.keyMapper.getBinding(KEYMAPPINGS.placementModifiers.placeMultiple).currentlyDown) { + // this.currentBlueprint.set(null); + // } } } @@ -81,6 +82,16 @@ export class HUDBlueprintPlacer extends BaseHUDPart { this.currentBlueprint.set(Blueprint.fromUids(this.root, uids)); } + rotateBlueprint() { + if (this.currentBlueprint.get()) { + if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateInverseModifier).currentlyDown) { + this.currentBlueprint.get().rotateCcw(); + } else { + this.currentBlueprint.get().rotateCw(); + } + } + } + /** * * @param {DrawParameters} parameters diff --git a/src/js/game/hud/parts/building_placer.js b/src/js/game/hud/parts/building_placer.js index 1e09dbd4..ab3ebae4 100644 --- a/src/js/game/hud/parts/building_placer.js +++ b/src/js/game/hud/parts/building_placer.js @@ -161,14 +161,19 @@ export class HUDBuildingPlacer extends BaseHUDPart { if ( metaBuilding && metaBuilding.getRotateAutomaticallyWhilePlacing(this.currentVariant.get()) && - !this.root.keyMapper.getBinding(KEYMAPPINGS.placementModifiers.placementDisableAutoOrientation).currentlyDown + !this.root.keyMapper.getBinding( + KEYMAPPINGS.placementModifiers.placementDisableAutoOrientation + ).currentlyDown ) { 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).currentlyDown) { + if ( + this.root.keyMapper.getBinding(KEYMAPPINGS.placementModifiers.placeInverse) + .currentlyDown + ) { this.currentBaseRotation = (180 + this.currentBaseRotation) % 360; } } @@ -389,7 +394,12 @@ export class HUDBuildingPlacer extends BaseHUDPart { tryRotate() { const selectedBuilding = this.currentMetaBuilding.get(); if (selectedBuilding) { - this.currentBaseRotation = (this.currentBaseRotation + 90) % 360; + if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateInverseModifier).currentlyDown) { + this.currentBaseRotation = (this.currentBaseRotation + 270) % 360; + } else { + this.currentBaseRotation = (this.currentBaseRotation + 90) % 360; + } + const staticComp = this.fakeEntity.components.StaticMapEntity; staticComp.rotation = this.currentBaseRotation; } @@ -468,8 +478,10 @@ export class HUDBuildingPlacer extends BaseHUDPart { // Succesfully placed if ( - metaBuilding.getFlipOrientationAfterPlacement() && - !this.root.keyMapper.getBinding(KEYMAPPINGS.placementModifiers.placementDisableAutoOrientation).currentlyDown + metaBuilding.getFlipOrientationAfterPlacement() && + !this.root.keyMapper.getBinding( + KEYMAPPINGS.placementModifiers.placementDisableAutoOrientation + ).currentlyDown ) { this.currentBaseRotation = (180 + this.currentBaseRotation) % 360; } diff --git a/src/js/game/hud/parts/keybinding_overlay.js b/src/js/game/hud/parts/keybinding_overlay.js index b143186a..300c0885 100644 --- a/src/js/game/hud/parts/keybinding_overlay.js +++ b/src/js/game/hud/parts/keybinding_overlay.js @@ -1,24 +1,16 @@ -import { BaseHUDPart } from "../base_hud_part"; import { makeDiv } from "../../../core/utils"; -import { getStringForKeyCode, KEYMAPPINGS } from "../../key_action_mapper"; -import { TrackedState } from "../../../core/tracked_state"; -import { queryParamOptions } from "../../../core/query_parameters"; import { T } from "../../../translations"; +import { getStringForKeyCode, KEYMAPPINGS } from "../../key_action_mapper"; +import { BaseHUDPart } from "../base_hud_part"; export class HUDKeybindingOverlay extends BaseHUDPart { initialize() { - this.shiftDownTracker = new TrackedState(this.onShiftStateChanged, this); - this.root.hud.signals.selectedPlacementBuildingChanged.add( this.onSelectedBuildingForPlacementChanged, this ); } - onShiftStateChanged(shiftDown) { - this.element.classList.toggle("shiftDown", shiftDown); - } - createElements(parent) { const mapper = this.root.keyMapper; @@ -70,7 +62,9 @@ export class HUDKeybindingOverlay extends BaseHUDPart {
- ⇧ ${T.global.keys.shift} + ⇧ ${getKeycode( + KEYMAPPINGS.placementModifiers.placeMultiple + )}
` @@ -81,7 +75,5 @@ export class HUDKeybindingOverlay extends BaseHUDPart { this.element.classList.toggle("placementActive", !!selectedMetaBuilding); } - update() { - this.shiftDownTracker.set(this.root.app.inputMgr.shiftIsDown); - } + update() {} } diff --git a/src/js/game/key_action_mapper.js b/src/js/game/key_action_mapper.js index acc78024..2c575501 100644 --- a/src/js/game/key_action_mapper.js +++ b/src/js/game/key_action_mapper.js @@ -53,6 +53,7 @@ export const KEYMAPPINGS = { placement: { abortBuildingPlacement: { keyCode: key("Q") }, rotateWhilePlacing: { keyCode: key("R") }, + rotateInverseModifier: { keyCode: 16 }, // SHIFT cycleBuildingVariants: { keyCode: key("T") }, cycleBuildings: { keyCode: 9 }, // TAB }, diff --git a/src/js/game/logic.js b/src/js/game/logic.js index dc89f8c2..90597e52 100644 --- a/src/js/game/logic.js +++ b/src/js/game/logic.js @@ -3,10 +3,11 @@ import { Entity } from "./entity"; import { Vector, enumDirectionToVector, enumDirection } from "../core/vector"; import { MetaBuilding } from "./meta_building"; import { StaticMapEntityComponent } from "./components/static_map_entity"; -import { Math_abs } from "../core/builtins"; +import { Math_abs, performanceNow } from "../core/builtins"; import { createLogger } from "../core/logging"; import { MetaBeltBaseBuilding, arrayBeltVariantToRotation } from "./buildings/belt_base"; import { SOUNDS } from "../platform/sound"; +import { round2Digits } from "../core/utils"; const logger = createLogger("ingame/logic"); @@ -132,17 +133,6 @@ export class GameLogic { return false; } - if ( - !building.performAdditionalPlacementChecks(this.root, { - origin, - rotation, - rotationVariant, - variant, - }) - ) { - return false; - } - return this.isAreaFreeToBuild({ origin, rotation, @@ -202,6 +192,24 @@ export class GameLogic { return false; } + /** + * Performs a bulk operation, not updating caches in the meantime + * @param {function} operation + */ + performBulkOperation(operation) { + logger.log("Running bulk operation ..."); + assert(!this.root.bulkOperationRunning, "Can not run two bulk operations twice"); + this.root.bulkOperationRunning = true; + const now = performanceNow(); + const returnValue = operation(); + const duration = performanceNow() - now; + logger.log("Done in", round2Digits(duration), "ms"); + assert(this.root.bulkOperationRunning, "Bulk operation = false while bulk operation was running"); + this.root.bulkOperationRunning = false; + this.root.signals.bulkOperationFinished.dispatch(); + return returnValue; + } + /** * Returns whether the given building can get removed * @param {Entity} building diff --git a/src/js/game/meta_building.js b/src/js/game/meta_building.js index 8753aac5..262a33cb 100644 --- a/src/js/game/meta_building.js +++ b/src/js/game/meta_building.js @@ -129,19 +129,6 @@ export class MetaBuilding { return null; } - /** - * Should perform additional placement checks - * @param {GameRoot} root - * @param {object} param0 - * @param {Vector} param0.origin - * @param {number} param0.rotation - * @param {number} param0.rotationVariant - * @param {string} param0.variant - */ - performAdditionalPlacementChecks(root, { origin, rotation, rotationVariant, variant }) { - return true; - } - /** * Creates the entity at the given location * @param {object} param0 diff --git a/src/js/game/root.js b/src/js/game/root.js index 0c4e6792..91efd137 100644 --- a/src/js/game/root.js +++ b/src/js/game/root.js @@ -70,6 +70,11 @@ export class GameRoot { /** @type {boolean} */ this.gameInitialized = false; + /** + * Whether a bulk operation is running + */ + this.bulkOperationRunning = false; + //////// Other properties /////// /** @type {Camera} */ @@ -151,6 +156,8 @@ export class GameRoot { shapeDelivered: /** @type {TypedSignal<[ShapeDefinition]>} */ (new Signal()), itemProduced: /** @type {TypedSignal<[BaseItem]>} */ (new Signal()), + + bulkOperationFinished: /** @type {TypedSignal<[]>} */ (new Signal()), }; // RNG's diff --git a/src/js/game/systems/miner.js b/src/js/game/systems/miner.js index 4ecf1e2d..5420cf36 100644 --- a/src/js/game/systems/miner.js +++ b/src/js/game/systems/miner.js @@ -17,9 +17,16 @@ export class MinerSystem extends GameSystemWithFilter { for (let i = 0; i < this.allEntities.length; ++i) { const entity = this.allEntities[i]; + // Check if miner is above an actual tile + const minerComp = entity.components.Miner; const staticComp = entity.components.StaticMapEntity; + const tileBelow = this.root.map.getLowerLayerContentXY(staticComp.origin.x, staticComp.origin.y); + if (!tileBelow) { + continue; + } + // First, try to get rid of chained items if (minerComp.itemChainBuffer.length > 0) { if (this.tryPerformMinerEject(entity, minerComp.itemChainBuffer[0])) { diff --git a/translations/base-en.yaml b/translations/base-en.yaml index 889e6583..c12e81e9 100644 --- a/translations/base-en.yaml +++ b/translations/base-en.yaml @@ -557,6 +557,8 @@ keybindings: abortBuildingPlacement: Abort Placement rotateWhilePlacing: Rotate + rotateInverseModifier: >- + Modifier: Rotate CCW instead cycleBuildingVariants: Cycle Variants confirmMassDelete: Confirm Mass Delete cycleBuildings: Cycle Buildings