|
|
|
import { makeDiv } from "../../../core/utils";
|
|
|
|
import { T } from "../../../translations";
|
|
|
|
import {
|
|
|
|
getStringForKeyCode,
|
|
|
|
KEYCODE_LMB,
|
|
|
|
KEYCODE_MMB,
|
|
|
|
KEYCODE_RMB,
|
|
|
|
KEYMAPPINGS,
|
|
|
|
} from "../../key_action_mapper";
|
|
|
|
import { BaseHUDPart } from "../base_hud_part";
|
|
|
|
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
|
|
|
|
|
|
|
const DIVIDER_TOKEN = "/";
|
|
|
|
const ADDER_TOKEN = "+";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @typedef {{ keyCode: number }} KeyCode
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @typedef {{
|
|
|
|
* condition: () => boolean,
|
|
|
|
* keys: Array<KeyCode|number|string>,
|
|
|
|
* label: string,
|
|
|
|
* cachedElement?: HTMLElement,
|
|
|
|
* cachedVisibility?: boolean
|
|
|
|
* }} KeyBinding
|
|
|
|
*/
|
|
|
|
|
|
|
|
export class HUDKeybindingOverlay extends BaseHUDPart {
|
|
|
|
initialize() {}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* HELPER / Returns if there is a building selected for placement and
|
|
|
|
* it has multiplace enabled by default
|
|
|
|
* @returns {boolean}
|
|
|
|
*/
|
|
|
|
get buildingPlacementStaysInPlacement() {
|
|
|
|
const placer = this.root.hud.parts.buildingPlacer;
|
|
|
|
return (
|
|
|
|
!this.mapOverviewActive &&
|
|
|
|
placer &&
|
|
|
|
placer.currentMetaBuilding.get() &&
|
|
|
|
placer.currentMetaBuilding.get().getStayInPlacementMode()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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<KeyBinding>} */
|
|
|
|
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 += `<code class="keybinding leftMouse"></code>`;
|
|
|
|
break;
|
|
|
|
case KEYCODE_RMB:
|
|
|
|
html += `<code class="keybinding rightMouse"></code>`;
|
|
|
|
break;
|
|
|
|
case KEYCODE_MMB:
|
|
|
|
html += `<code class="keybinding middleMouse"></code>`;
|
|
|
|
break;
|
|
|
|
case DIVIDER_TOKEN:
|
|
|
|
html += `<i></i>`;
|
|
|
|
break;
|
|
|
|
case ADDER_TOKEN:
|
|
|
|
html += `+`;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
html += `<code class="keybinding">${getStringForKeyCode(
|
|
|
|
mapper.getBinding(/** @type {KeyCode} */ (key)).keyCode
|
|
|
|
)}</code>`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
html += `<label>${handle.label}</label>`;
|
|
|
|
|
|
|
|
handle.cachedElement = makeDiv(this.element, null, ["binding"], html);
|
|
|
|
handle.cachedVisibility = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
update() {
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|