diff --git a/src/css/ingame_hud/keybindings_overlay.scss b/src/css/ingame_hud/keybindings_overlay.scss index 6d0f99c8..38b560d8 100644 --- a/src/css/ingame_hud/keybindings_overlay.scss +++ b/src/css/ingame_hud/keybindings_overlay.scss @@ -14,6 +14,10 @@ } > .binding { + &:not(.visible) { + display: none !important; + } + display: inline-grid; @include PlainText; align-items: center; @@ -57,50 +61,6 @@ @include S(margin-left, 5px); } } - - &:not(.placementActive) .binding.placementOnly, - &.mapOverviewActive .binding.placementOnly { - display: none; - } - - &.placementActive:not(.mapOverviewActive) .noPlacementOnly { - display: none; - } - - &:not(.mapOverviewActive) .binding.overviewOnly { - display: none; - } - - &.mapOverviewActive .noOverviewOnly { - display: none; - } - - &:not(.hasDirectionLock) .binding.directionLock { - display: none; - } - - &.hasDirectionLock .noDirectionLock { - display: none; - } - - .binding.placementOnly, - &:not(.placementActive) .binding.noPlacementOnly { - transform-origin: 0% 50%; - @include InlineAnimation(0.3s ease-in-out) { - 0% { - color: $colorRedBright; - transform: scale(1.2); - } - } - } - - .keybinding.builtinKey { - transition: all 0.1s ease-in-out; - transition-property: background-color, color, border-color; - background: $colorRedBright; - border-color: $colorRedBright; - color: #fff; - } } body.uiHidden #ingame_HUD_KeybindingOverlay .binding:not(.hudToggle) { diff --git a/src/css/ingame_hud/mass_selector.scss b/src/css/ingame_hud/mass_selector.scss deleted file mode 100644 index ddd2d40a..00000000 --- a/src/css/ingame_hud/mass_selector.scss +++ /dev/null @@ -1,22 +0,0 @@ -#ingame_HUD_MassSelector { - position: absolute; - @include S(top, 50px); - left: 50%; - transform: translateX(-50%); - background: $ingameHudBg; - @include S(padding, 6px, 10px); - @include SuperSmallText; - color: #fff; - // color: #f77; - - .keybinding { - vertical-align: middle; - @include S(margin, 0, 1px); - position: relative; - top: unset; - left: unset; - right: unset; - bottom: unset; - @include S(margin-top, -2px); - } -} diff --git a/src/css/main.scss b/src/css/main.scss index 10bf384e..598e9e92 100644 --- a/src/css/main.scss +++ b/src/css/main.scss @@ -37,7 +37,6 @@ @import "ingame_hud/shop"; @import "ingame_hud/game_menu"; @import "ingame_hud/dialogs"; -@import "ingame_hud/mass_selector"; @import "ingame_hud/vignette_overlay"; @import "ingame_hud/statistics"; @import "ingame_hud/pinned_shapes"; @@ -67,7 +66,6 @@ ingame_HUD_PinnedShapes, ingame_HUD_GameMenu, ingame_HUD_KeybindingOverlay, ingame_HUD_Notifications, -ingame_HUD_MassSelector, ingame_HUD_DebugInfo, ingame_HUD_EntityDebugger, ingame_HUD_InteractiveTutorial, @@ -101,7 +99,6 @@ body.uiHidden { #ingame_HUD_buildings_toolbar, #ingame_HUD_PlacementHints, #ingame_HUD_GameMenu, - #ingame_HUD_MassSelector, #ingame_HUD_PinnedShapes, #ingame_HUD_Notifications, #ingame_HUD_TutorialHints, diff --git a/src/js/changelog.js b/src/js/changelog.js index 81024f86..d4316215 100644 --- a/src/js/changelog.js +++ b/src/js/changelog.js @@ -5,6 +5,10 @@ export const CHANGELOG = [ entries: [ "The game soundtrack has been extended! There are now 4 songs with over 13 minutes of playtime from Peppsen!", "Fix belt planner not placing the last belt", + "Refactor keybindings overlay to show more appropriate keybindings", + "Show keybindings for area-select in the upper left instead", + "Automatically deselect area when selecting a new building", + "Optimize performance by caching miner items (by Phlosioneer)", ], }, { diff --git a/src/js/game/hud/parts/keybinding_overlay.js b/src/js/game/hud/parts/keybinding_overlay.js index e469bcca..7aca3722 100644 --- a/src/js/game/hud/parts/keybinding_overlay.js +++ b/src/js/game/hud/parts/keybinding_overlay.js @@ -1,123 +1,309 @@ import { makeDiv } from "../../../core/utils"; import { T } from "../../../translations"; -import { getStringForKeyCode, KEYMAPPINGS } from "../../key_action_mapper"; +import { + getStringForKeyCode, + KEYCODE_LMB, + KEYCODE_MMB, + KEYCODE_RMB, + KEYMAPPINGS, +} from "../../key_action_mapper"; import { BaseHUDPart } from "../base_hud_part"; -import { TrackedState } from "../../../core/tracked_state"; -import { MetaBuilding } from "../../meta_building"; +import { DynamicDomAttach } from "../dynamic_dom_attach"; + +const DIVIDER_TOKEN = "/"; +const ADDER_TOKEN = "+"; + +/** + * @typedef {{ keyCode: number }} KeyCode + */ + +/** + * @typedef {{ + * condition: () => boolean, + * keys: Array, + * label: string, + * cachedElement?: HTMLElement, + * cachedVisibility?: boolean + * }} KeyBinding + */ export class HUDKeybindingOverlay extends BaseHUDPart { - initialize() { - this.root.hud.signals.selectedPlacementBuildingChanged.add( - this.onSelectedBuildingForPlacementChanged, - this - ); + initialize() {} - this.trackedMapOverviewActive = new TrackedState(this.applyCssClasses, this); + /** + * HELPER / Returns if there is a building selected for placement + * @returns {boolean} + */ + get buildingPlacementActive() { + const placer = this.root.hud.parts.buildingPlacer; + return !this.mapOverviewActive && placer && !!placer.currentMetaBuilding.get(); } - createElements(parent) { - const mapper = this.root.keyMapper; - - const getKeycode = id => { - return getStringForKeyCode(mapper.getBinding(id).keyCode); - }; - - this.element = makeDiv( - parent, - "ingame_HUD_KeybindingOverlay", - [], - ` - -
- - ${getKeycode(KEYMAPPINGS.navigation.mapMoveUp)} - ${getKeycode(KEYMAPPINGS.navigation.mapMoveLeft)} - ${getKeycode(KEYMAPPINGS.navigation.mapMoveDown)} - ${getKeycode(KEYMAPPINGS.navigation.mapMoveRight)} - -
- - - -
- - -
- -
- - -
- - -
- ${getKeycode( - KEYMAPPINGS.massSelect.massSelectStart - )}+ - - -
- -
- - -
- -
- - ${getKeycode(KEYMAPPINGS.placement.abortBuildingPlacement)} - -
- -
- ${getKeycode(KEYMAPPINGS.placement.rotateWhilePlacing)} - -
- - ` + - (this.root.app.settings.getAllSettings().alwaysMultiplace - ? "" - : ` -
- ${getKeycode( - KEYMAPPINGS.placementModifiers.placeMultiple - )} - -
`) + - ` - -
- ${getKeycode( - KEYMAPPINGS.placementModifiers.lockBeltDirection - )} - -
- -
- ${getKeycode(KEYMAPPINGS.placement.switchDirectionLockSide)} - -
- ` + /** + * HELPER / Returns if there is a building selected for placement and + * it supports the belt planner + * @returns {boolean} + */ + get buildingPlacementBeltPlanner() { + const placer = this.root.hud.parts.buildingPlacer; + return ( + !this.mapOverviewActive && + placer && + placer.currentMetaBuilding.get() && + placer.currentMetaBuilding.get().getHasDirectionLockAvailable() ); } /** - * - * @param {MetaBuilding} selectedMetaBuilding + * HELPER / Returns if there is a building selected for placement and + * it has multiplace enabled by default + * @returns {boolean} */ - onSelectedBuildingForPlacementChanged(selectedMetaBuilding) { - this.element.classList.toggle("placementActive", !!selectedMetaBuilding); - this.element.classList.toggle( - "hasDirectionLock", - selectedMetaBuilding && selectedMetaBuilding.getHasDirectionLockAvailable() + get buildingPlacementStaysInPlacement() { + const placer = this.root.hud.parts.buildingPlacer; + return ( + !this.mapOverviewActive && + placer && + placer.currentMetaBuilding.get() && + placer.currentMetaBuilding.get().getStayInPlacementMode() ); } - applyCssClasses() { - this.element.classList.toggle("mapOverviewActive", this.root.camera.getIsMapOverlayActive()); + /** + * HELPER / Returns if there is a blueprint selected for placement + * @returns {boolean} + */ + get blueprintPlacementActive() { + const placer = this.root.hud.parts.blueprintPlacer; + return placer && !!placer.currentBlueprint.get(); + } + + /** + * HELPER / Returns if the belt planner is currently active + * @returns {boolean} + */ + get beltPlannerActive() { + const placer = this.root.hud.parts.buildingPlacer; + return !this.mapOverviewActive && placer && placer.isDirectionLockActive; + } + + /** + * HELPER / Returns if there is a last blueprint available + * @returns {boolean} + */ + get lastBlueprintAvailable() { + const placer = this.root.hud.parts.blueprintPlacer; + return placer && !!placer.lastBlueprintUsed; + } + + /** + * HELPER / Returns if there is anything selected on the map + * @returns {boolean} + */ + get anythingSelectedOnMap() { + const selector = this.root.hud.parts.massSelector; + return selector && selector.selectedUids.size > 0; + } + + /** + * HELPER / Returns if there is a building or blueprint selected for placement + * @returns {boolean} + */ + get anyPlacementActive() { + return this.buildingPlacementActive || this.blueprintPlacementActive; + } + + /** + * HELPER / Returns if the map overview is active + * @returns {boolean} + */ + get mapOverviewActive() { + return this.root.camera.getIsMapOverlayActive(); + } + + /** + * Initializes the element + * @param {HTMLElement} parent + */ + createElements(parent) { + const mapper = this.root.keyMapper; + const k = KEYMAPPINGS; + + /** @type {Array} */ + this.keybindings = [ + { + // Move map - Including mouse + label: T.ingame.keybindingsOverlay.moveMap, + keys: [ + KEYCODE_LMB, + DIVIDER_TOKEN, + k.navigation.mapMoveUp, + k.navigation.mapMoveLeft, + k.navigation.mapMoveDown, + k.navigation.mapMoveRight, + ], + condition: () => !this.anyPlacementActive, + }, + + { + // Move map - No mouse + label: T.ingame.keybindingsOverlay.moveMap, + keys: [ + k.navigation.mapMoveUp, + k.navigation.mapMoveLeft, + k.navigation.mapMoveDown, + k.navigation.mapMoveRight, + ], + condition: () => this.anyPlacementActive, + }, + + { + // [OVERVIEW] Create marker with right click + label: T.ingame.keybindingsOverlay.createMarker, + keys: [KEYCODE_RMB], + condition: () => this.mapOverviewActive && !this.blueprintPlacementActive, + }, + + { + // Cancel placement + label: T.ingame.keybindingsOverlay.stopPlacement, + keys: [KEYCODE_RMB, DIVIDER_TOKEN, k.placement.abortBuildingPlacement], + condition: () => this.anyPlacementActive, + }, + + { + // Delete with right click + label: T.ingame.keybindingsOverlay.delete, + keys: [KEYCODE_RMB], + condition: () => + !this.anyPlacementActive && !this.mapOverviewActive && !this.anythingSelectedOnMap, + }, + + { + // Area select + label: T.ingame.keybindingsOverlay.selectBuildings, + keys: [k.massSelect.massSelectStart, ADDER_TOKEN, KEYCODE_LMB], + condition: () => !this.anyPlacementActive && !this.anythingSelectedOnMap, + }, + + { + // Place building + label: T.ingame.keybindingsOverlay.placeBuilding, + keys: [KEYCODE_LMB], + condition: () => this.anyPlacementActive, + }, + + { + // Rotate + label: T.ingame.keybindingsOverlay.rotateBuilding, + keys: [k.placement.rotateWhilePlacing], + condition: () => this.anyPlacementActive && !this.beltPlannerActive, + }, + + { + // [BELT PLANNER] Flip Side + label: T.ingame.keybindingsOverlay.plannerSwitchSide, + keys: [k.placement.switchDirectionLockSide], + condition: () => this.beltPlannerActive, + }, + + { + // Place last blueprint + label: T.ingame.keybindingsOverlay.pasteLastBlueprint, + keys: [k.massSelect.pasteLastBlueprint], + condition: () => !this.blueprintPlacementActive && this.lastBlueprintAvailable, + }, + + { + // Belt planner + label: T.ingame.keybindingsOverlay.lockBeltDirection, + keys: [k.placementModifiers.lockBeltDirection], + condition: () => this.buildingPlacementActive && !this.beltPlannerActive, + }, + + { + // [SELECTION] Destroy + label: T.ingame.keybindingsOverlay.delete, + keys: [k.massSelect.confirmMassDelete], + condition: () => this.anythingSelectedOnMap, + }, + + { + // [SELECTION] Cancel + label: T.ingame.keybindingsOverlay.clearSelection, + keys: [k.general.back], + condition: () => this.anythingSelectedOnMap, + }, + { + // [SELECTION] Cut + label: T.ingame.keybindingsOverlay.cutSelection, + keys: [k.massSelect.massSelectCut], + condition: () => this.anythingSelectedOnMap, + }, + + { + // [SELECTION] Copy + label: T.ingame.keybindingsOverlay.copySelection, + keys: [k.massSelect.massSelectCopy], + condition: () => this.anythingSelectedOnMap, + }, + ]; + + if (!this.root.app.settings.getAllSettings().alwaysMultiplace) { + this.keybindings.push({ + // Multiplace + label: T.ingame.keybindingsOverlay.placeMultiple, + keys: [k.placementModifiers.placeMultiple], + condition: () => this.anyPlacementActive && !this.buildingPlacementStaysInPlacement, + }); + } + + this.element = makeDiv(parent, "ingame_HUD_KeybindingOverlay", []); + + for (let i = 0; i < this.keybindings.length; ++i) { + let html = ""; + const handle = this.keybindings[i]; + + for (let k = 0; k < handle.keys.length; ++k) { + const key = handle.keys[k]; + + switch (key) { + case KEYCODE_LMB: + html += ``; + break; + case KEYCODE_RMB: + html += ``; + break; + case KEYCODE_MMB: + html += ``; + break; + case DIVIDER_TOKEN: + html += ``; + break; + case ADDER_TOKEN: + html += `+`; + break; + default: + html += `${getStringForKeyCode( + mapper.getBinding(/** @type {KeyCode} */ (key)).keyCode + )}`; + } + } + html += ``; + + handle.cachedElement = makeDiv(this.element, null, ["binding"], html); + handle.cachedVisibility = false; + } } update() { - this.trackedMapOverviewActive.set(this.root.camera.getIsMapOverlayActive()); + for (let i = 0; i < this.keybindings.length; ++i) { + const handle = this.keybindings[i]; + const visibility = handle.condition(); + if (visibility !== handle.cachedVisibility) { + handle.cachedVisibility = visibility; + handle.cachedElement.classList.toggle("visible", visibility); + } + } } } diff --git a/src/js/game/hud/parts/mass_selector.js b/src/js/game/hud/parts/mass_selector.js index 0dc872c5..7e6710c0 100644 --- a/src/js/game/hud/parts/mass_selector.js +++ b/src/js/game/hud/parts/mass_selector.js @@ -17,29 +17,7 @@ import { enumHubGoalRewards } from "../../tutorial_goals"; const logger = createLogger("hud/mass_selector"); export class HUDMassSelector extends BaseHUDPart { - createElements(parent) { - const removalKeybinding = this.root.keyMapper - .getBinding(KEYMAPPINGS.massSelect.confirmMassDelete) - .getKeyCodeString(); - const abortKeybinding = this.root.keyMapper.getBinding(KEYMAPPINGS.general.back).getKeyCodeString(); - const cutKeybinding = this.root.keyMapper - .getBinding(KEYMAPPINGS.massSelect.massSelectCut) - .getKeyCodeString(); - const copyKeybinding = this.root.keyMapper - .getBinding(KEYMAPPINGS.massSelect.massSelectCopy) - .getKeyCodeString(); - - this.element = makeDiv( - parent, - "ingame_HUD_MassSelector", - [], - T.ingame.massSelect.infoText - .replace("", `${removalKeybinding}`) - .replace("", `${cutKeybinding}`) - .replace("", `${copyKeybinding}`) - .replace("", `${abortKeybinding}`) - ); - } + createElements(parent) {} initialize() { this.deletionMarker = Loader.getSprite("sprites/misc/deletion_marker.png"); @@ -49,6 +27,7 @@ export class HUDMassSelector extends BaseHUDPart { this.selectedUids = new Set(); this.root.signals.entityQueuedForDestroy.add(this.onEntityDestroyed, this); + this.root.hud.signals.pasteBlueprintRequested.add(this.clearSelection, this); this.root.camera.downPreHandler.add(this.onMouseDown, this); this.root.camera.movePreHandler.add(this.onMouseMove, this); @@ -61,7 +40,7 @@ export class HUDMassSelector extends BaseHUDPart { this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectCut).add(this.confirmCut, this); this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectCopy).add(this.startCopy, this); - this.domAttach = new DynamicDomAttach(this.root, this.element); + this.root.hud.signals.selectedPlacementBuildingChanged.add(this.clearSelection, this); } /** @@ -83,6 +62,13 @@ export class HUDMassSelector extends BaseHUDPart { } } + /** + * Clears the entire selection + */ + clearSelection() { + this.selectedUids = new Set(); + } + confirmDelete() { if (this.selectedUids.size > 100) { const { ok } = this.root.hud.parts.dialogs.showWarning( @@ -230,10 +216,6 @@ export class HUDMassSelector extends BaseHUDPart { } } - update() { - this.domAttach.update(this.selectedUids.size > 0); - } - /** * * @param {DrawParameters} parameters diff --git a/src/js/game/key_action_mapper.js b/src/js/game/key_action_mapper.js index 6a1e265c..659f01e6 100644 --- a/src/js/game/key_action_mapper.js +++ b/src/js/game/key_action_mapper.js @@ -88,6 +88,10 @@ for (const categoryId in KEYMAPPINGS) { } } +export const KEYCODE_LMB = 1; +export const KEYCODE_MMB = 2; +export const KEYCODE_RMB = 3; + /** * Returns a keycode -> string * @param {number} code @@ -95,11 +99,11 @@ for (const categoryId in KEYMAPPINGS) { */ export function getStringForKeyCode(code) { switch (code) { - case 1: + case KEYCODE_LMB: return "LMB"; - case 2: + case KEYCODE_MMB: return "MMB"; - case 3: + case KEYCODE_RMB: return "RMB"; case 4: return "MB4"; diff --git a/translations/base-en.yaml b/translations/base-en.yaml index 8321e20b..afe4907d 100644 --- a/translations/base-en.yaml +++ b/translations/base-en.yaml @@ -285,6 +285,9 @@ ingame: pasteLastBlueprint: Paste last blueprint lockBeltDirection: Enable belt planner plannerSwitchSide: Flip planner side + cutSelection: Cut + copySelection: Copy + clearSelection: Clear Selection # Everything related to placing buildings (I.e. as soon as you selected a building # from the toolbar) @@ -320,11 +323,6 @@ ingame: newUpgrade: A new upgrade is available! gameSaved: Your game has been saved. - # Mass select information, this is when you hold CTRL and then drag with your mouse - # to select multiple buildings - massSelect: - infoText: Press to cut, to copy, to remove and to cancel. - # The "Upgrades" window shop: title: Upgrades