diff --git a/src/js/changelog.js b/src/js/changelog.js index 5b987c87..3a28acde 100644 --- a/src/js/changelog.js +++ b/src/js/changelog.js @@ -2,7 +2,11 @@ export const CHANGELOG = [ { version: "1.4.0", date: "UNRELEASED", - entries: ["Added puzzle mode", "Belts in blueprints should now always paste correctly"], + entries: [ + "Added puzzle mode", + "Belts in blueprints should now always paste correctly", + "You can now clear belts by selecting them, and then pressing 'B'", + ], }, { version: "1.3.1", diff --git a/src/js/core/signal.js b/src/js/core/signal.js index 7daae4ea..2dbc9f93 100644 --- a/src/js/core/signal.js +++ b/src/js/core/signal.js @@ -17,6 +17,17 @@ export class Signal { ++this.modifyCount; } + /** + * Adds a new signal listener + * @param {function} receiver + * @param {object} scope + */ + addToTop(receiver, scope = null) { + assert(receiver, "receiver is null"); + this.receivers.unshift({ receiver, scope }); + ++this.modifyCount; + } + /** * Dispatches the signal * @param {...any} payload diff --git a/src/js/game/components/belt.js b/src/js/game/components/belt.js index 138c4775..3144ad96 100644 --- a/src/js/game/components/belt.js +++ b/src/js/game/components/belt.js @@ -57,6 +57,12 @@ export class BeltComponent extends Component { this.assignedPath = null; } + clear() { + if (this.assignedPath) { + this.assignedPath.clearAllItems(); + } + } + /** * Returns the effective length of this belt in tile space * @returns {number} diff --git a/src/js/game/hub_goals.js b/src/js/game/hub_goals.js index 4ebd5cc7..8351775e 100644 --- a/src/js/game/hub_goals.js +++ b/src/js/game/hub_goals.js @@ -110,7 +110,7 @@ export class HubGoals extends BasicSerializableObject { // Allow quickly switching goals in dev mode if (G_IS_DEV) { window.addEventListener("keydown", ev => { - if (ev.key === "b") { + if (ev.key === "p") { // root is not guaranteed to exist within ~0.5s after loading in if (this.root && this.root.app && this.root.app.gameAnalytics) { if (!this.isEndOfDemoReached()) { diff --git a/src/js/game/hud/parts/keybinding_overlay.js b/src/js/game/hud/parts/keybinding_overlay.js index 65919d3c..2384ab84 100644 --- a/src/js/game/hud/parts/keybinding_overlay.js +++ b/src/js/game/hud/parts/keybinding_overlay.js @@ -254,6 +254,13 @@ export class HUDKeybindingOverlay extends BaseHUDPart { condition: () => this.anythingSelectedOnMap, }, + { + // [SELECTION] Clear + label: T.ingame.keybindingsOverlay.clearBelts, + keys: [k.massSelect.massSelectClear], + condition: () => this.anythingSelectedOnMap, + }, + { // Switch layers label: T.ingame.keybindingsOverlay.switchLayers, diff --git a/src/js/game/hud/parts/mass_selector.js b/src/js/game/hud/parts/mass_selector.js index d73e3be3..ab933da3 100644 --- a/src/js/game/hud/parts/mass_selector.js +++ b/src/js/game/hud/parts/mass_selector.js @@ -1,20 +1,19 @@ -import { BaseHUDPart } from "../base_hud_part"; -import { Vector } from "../../../core/vector"; -import { STOP_PROPAGATION } from "../../../core/signal"; -import { DrawParameters } from "../../../core/draw_parameters"; -import { Entity } from "../../entity"; -import { Loader } from "../../../core/loader"; import { globalConfig } from "../../../core/config"; -import { makeDiv, formatBigNumber, formatBigNumberFull } from "../../../core/utils"; -import { DynamicDomAttach } from "../dynamic_dom_attach"; +import { DrawParameters } from "../../../core/draw_parameters"; import { createLogger } from "../../../core/logging"; +import { STOP_PROPAGATION } from "../../../core/signal"; +import { formatBigNumberFull } from "../../../core/utils"; +import { Vector } from "../../../core/vector"; import { ACHIEVEMENTS } from "../../../platform/achievement_provider"; -import { enumMouseButton } from "../../camera"; import { T } from "../../../translations"; +import { Blueprint } from "../../blueprint"; +import { enumMouseButton } from "../../camera"; +import { Component } from "../../component"; +import { Entity } from "../../entity"; import { KEYMAPPINGS } from "../../key_action_mapper"; import { THEME } from "../../theme"; import { enumHubGoalRewards } from "../../tutorial_goals"; -import { Blueprint } from "../../blueprint"; +import { BaseHUDPart } from "../base_hud_part"; const logger = createLogger("hud/mass_selector"); @@ -33,12 +32,13 @@ export class HUDMassSelector extends BaseHUDPart { this.root.camera.movePreHandler.add(this.onMouseMove, this); this.root.camera.upPostHandler.add(this.onMouseUp, this); - this.root.keyMapper.getBinding(KEYMAPPINGS.general.back).add(this.onBack, this); + this.root.keyMapper.getBinding(KEYMAPPINGS.general.back).addToTop(this.onBack, this); this.root.keyMapper .getBinding(KEYMAPPINGS.massSelect.confirmMassDelete) .add(this.confirmDelete, this); this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectCut).add(this.confirmCut, this); this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectCopy).add(this.startCopy, this); + this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectClear).add(this.clearBelts, this); this.root.hud.signals.selectedPlacementBuildingChanged.add(this.clearSelection, this); this.root.signals.editModeChanged.add(this.clearSelection, this); @@ -142,6 +142,16 @@ export class HUDMassSelector extends BaseHUDPart { } } + clearBelts() { + for (const uid of this.selectedUids) { + const entity = this.root.entityMgr.findByUid(uid); + for (const component of Object.values(entity.components)) { + /** @type {Component} */ (component).clear(); + } + } + this.selectedUids = new Set(); + } + confirmCut() { if (!this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_blueprints)) { this.root.hud.parts.dialogs.showInfo( diff --git a/src/js/game/key_action_mapper.js b/src/js/game/key_action_mapper.js index ad9f6e8e..52ba4ad6 100644 --- a/src/js/game/key_action_mapper.js +++ b/src/js/game/key_action_mapper.js @@ -106,6 +106,7 @@ export const KEYMAPPINGS = { massSelectSelectMultiple: { keyCode: 16 }, // SHIFT massSelectCopy: { keyCode: key("C") }, massSelectCut: { keyCode: key("X") }, + massSelectClear: { keyCode: key("B") }, confirmMassDelete: { keyCode: 46 }, // DEL pasteLastBlueprint: { keyCode: key("V") }, }, @@ -328,6 +329,15 @@ export class Keybinding { this.signal.add(receiver, scope); } + /** + * Adds an event listener + * @param {function() : void} receiver + * @param {object=} scope + */ + addToTop(receiver, scope = null) { + this.signal.addToTop(receiver, scope); + } + /** * @param {Element} elem * @returns {HTMLElement} the created element, or null if the keybindings are not shown diff --git a/src/js/game/logic.js b/src/js/game/logic.js index c3adb0a3..20caca31 100644 --- a/src/js/game/logic.js +++ b/src/js/game/logic.js @@ -450,13 +450,6 @@ export class GameLogic { * Clears all belts and items */ clearAllBeltsAndItems() { - // Belts - const beltPaths = this.root.systemMgr.systems.belt.beltPaths; - for (const path of beltPaths) { - path.clearAllItems(); - } - - // Acceptors for (const entity of this.root.entityMgr.entities) { for (const component of Object.values(entity.components)) { /** @type {Component} */ (component).clear(); diff --git a/src/js/globals.d.ts b/src/js/globals.d.ts index d1fb5305..5bb3bbba 100644 --- a/src/js/globals.d.ts +++ b/src/js/globals.d.ts @@ -185,6 +185,7 @@ declare const STOP_PROPAGATION = "stop_propagation"; declare interface TypedSignal> { add(receiver: (...args: T) => /* STOP_PROPAGATION */ string | void, scope?: object); + addToTop(receiver: (...args: T) => /* STOP_PROPAGATION */ string | void, scope?: object); remove(receiver: (...args: T) => /* STOP_PROPAGATION */ string | void); dispatch(...args: T): /* STOP_PROPAGATION */ string | void; diff --git a/translations/base-en.yaml b/translations/base-en.yaml index f09b2e19..b0e510ae 100644 --- a/translations/base-en.yaml +++ b/translations/base-en.yaml @@ -393,6 +393,7 @@ ingame: plannerSwitchSide: Flip planner side cutSelection: Cut copySelection: Copy + clearBelts: Clear belts clearSelection: Clear selection pipette: Pipette switchLayers: Switch layers @@ -1324,6 +1325,7 @@ keybindings: massSelectSelectMultiple: Select multiple areas massSelectCopy: Copy area massSelectCut: Cut area + massSelectClear: Clear belts placementDisableAutoOrientation: Disable automatic orientation placeMultiple: Stay in placement mode