Properly hide some hud elements when hovering

This commit is contained in:
tobspr 2020-09-13 09:05:05 +02:00
parent 87f79a6c25
commit a24e7b8d33
8 changed files with 560 additions and 481 deletions

View File

@ -18,12 +18,11 @@
background-color: #55585a; background-color: #55585a;
} }
pointer-events: all; transition: opacity 0.1s ease-out;
&.hovered {
&:hover { opacity: 0.1;
opacity: 10%;
.buildingImage { .buildingImage {
opacity: 0%; opacity: 0;
} }
} }
@ -79,6 +78,7 @@
@include S(height, 100px); @include S(height, 100px);
background: top left / 100% 100% no-repeat; background: top left / 100% 100% no-repeat;
@include S(border-radius, $globalBorderRadius); @include S(border-radius, $globalBorderRadius);
transition: opacity 0.1s ease-in-out;
} }
@include StyleBelowWidth(700px) { @include StyleBelowWidth(700px) {

View File

@ -31,7 +31,8 @@
pointer-events: all; pointer-events: all;
&:hover { transition: opacity 0.1s ease-out;
&.hovered {
opacity: 10%; opacity: 10%;
.helperGif { .helperGif {
opacity: 0%; opacity: 0%;
@ -57,5 +58,6 @@
@include S(margin-top, 5px); @include S(margin-top, 5px);
@include S(height, 150px); @include S(height, 150px);
background: center center / contain no-repeat; background: center center / contain no-repeat;
transition: opacity 0.1s ease-out;
} }
} }

View File

@ -1,69 +1,74 @@
#ingame_HUD_KeybindingOverlay { #ingame_HUD_KeybindingOverlay {
position: absolute; position: absolute;
@include S(top, 10px); @include S(top, 10px);
@include S(left, 10px); @include S(left, 10px);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
color: #333438; color: #333438;
backdrop-filter: blur(D(2px)); backdrop-filter: blur(D(2px));
padding: D(3px); padding: D(3px);
@include DarkThemeOverride { @include DarkThemeOverride {
color: #fff; color: #fff;
} }
> .binding { transition: opacity 0.1s ease-out;
&:not(.visible) { &.hovered {
display: none !important; opacity: 0.1;
} }
display: inline-grid; > .binding {
@include PlainText; &:not(.visible) {
align-items: center; display: none !important;
@include S(margin-bottom, 3px); }
grid-auto-flow: column;
@include S(grid-gap, 2px); display: inline-grid;
@include PlainText;
i { align-items: center;
display: inline-block; @include S(margin-bottom, 3px);
@include S(height, 10px); grid-auto-flow: column;
width: 1px; @include S(grid-gap, 2px);
@include S(margin, 0, 3px);
background-color: #fff; i {
transform: rotate(10deg); display: inline-block;
// @include S(margin, 0, 3px); @include S(height, 10px);
} width: 1px;
@include S(margin, 0, 3px);
code { background-color: #fff;
position: relative; transform: rotate(10deg);
top: unset; // @include S(margin, 0, 3px);
left: unset; }
margin: 0;
&.rightMouse { code {
background: #fff uiResource("icons/mouse_right.png") center center / 85% no-repeat; position: relative;
} top: unset;
left: unset;
&.leftMouse { margin: 0;
background: #fff uiResource("icons/mouse_left.png") center center / 85% no-repeat; &.rightMouse {
} background: #fff uiResource("icons/mouse_right.png") center center / 85% no-repeat;
} }
label { &.leftMouse {
color: #333438; background: #fff uiResource("icons/mouse_left.png") center center / 85% no-repeat;
@include SuperSmallText; }
text-transform: uppercase; }
// color: #fff;
@include DarkThemeOverride { label {
color: #fff; color: #333438;
} @include SuperSmallText;
text-transform: uppercase;
@include S(margin-left, 5px); // color: #fff;
} @include DarkThemeOverride {
} color: #fff;
} }
body.uiHidden #ingame_HUD_KeybindingOverlay .binding:not(.hudToggle) { @include S(margin-left, 5px);
display: none; }
} }
}
body.uiHidden #ingame_HUD_KeybindingOverlay .binding:not(.hudToggle) {
display: none;
}

View File

@ -19,6 +19,7 @@ export const CHANGELOG = [
"Updated and added new translations (Thanks to all contributors!)", "Updated and added new translations (Thanks to all contributors!)",
"Added setting to be able to delete buildings while placing (inspired by hexy)", "Added setting to be able to delete buildings while placing (inspired by hexy)",
"You can now adjust the sound and music volumes! (inspired by Yoshie2000)", "You can now adjust the sound and music volumes! (inspired by Yoshie2000)",
"Some hud elements now have reduced opacity when hovering, so you can see through (by mvb005)",
"Mark pinned shapes in statistics dialog and show them first (inspired by davidburhans)", "Mark pinned shapes in statistics dialog and show them first (inspired by davidburhans)",
"Added setting to show chunk borders", "Added setting to show chunk borders",
"Quad painters have been reworked! They now are integrated with the wires, and only paint the shape when the value is 1 (inspired by dengr1605)", "Quad painters have been reworked! They now are integrated with the wires, and only paint the shape when the value is 1 (inspired by dengr1605)",

View File

@ -1,3 +1,4 @@
import { TrackedState } from "../../core/tracked_state";
import { GameRoot } from "../root"; import { GameRoot } from "../root";
// Automatically attaches and detaches elements from the dom // Automatically attaches and detaches elements from the dom
@ -7,15 +8,28 @@ import { GameRoot } from "../root";
// Also attaches a class name if desired // Also attaches a class name if desired
export class DynamicDomAttach { export class DynamicDomAttach {
constructor(root, element, { timeToKeepSeconds = 0, attachClass = null } = {}) { /**
*
* @param {GameRoot} root
* @param {HTMLElement} element
* @param {object} param2
* @param {number=} param2.timeToKeepSeconds How long to keep the element visible (in ms) after it should be hidden.
* Useful for fade-out effects
* @param {string=} param2.attachClass If set, attaches a class while the element is visible
* @param {boolean=} param2.trackHover If set, attaches the 'hovered' class if the cursor is above the element. Useful
* for fading out the element if its below the cursor for example.
*/
constructor(root, element, { timeToKeepSeconds = 0, attachClass = null, trackHover = false } = {}) {
/** @type {GameRoot} */ /** @type {GameRoot} */
this.root = root; this.root = root;
/** @type {HTMLElement} */ /** @type {HTMLElement} */
this.element = element; this.element = element;
this.parent = this.element.parentElement; this.parent = this.element.parentElement;
assert(this.parent, "Dom attach created without parent");
this.attachClass = attachClass; this.attachClass = attachClass;
this.trackHover = trackHover;
this.timeToKeepSeconds = timeToKeepSeconds; this.timeToKeepSeconds = timeToKeepSeconds;
this.lastVisibleTime = 0; this.lastVisibleTime = 0;
@ -26,8 +40,19 @@ export class DynamicDomAttach {
this.internalIsClassAttached = false; this.internalIsClassAttached = false;
this.classAttachTimeout = null; this.classAttachTimeout = null;
// Store the last bounds we computed
/** @type {DOMRect} */
this.lastComputedBounds = null;
this.lastComputedBoundsTime = -1;
// Track the 'hovered' class
this.trackedIsHovered = new TrackedState(this.setIsHoveredClass, this);
} }
/**
* Internal method to attach the element
*/
internalAttach() { internalAttach() {
if (!this.attached) { if (!this.attached) {
this.parent.appendChild(this.element); this.parent.appendChild(this.element);
@ -36,6 +61,9 @@ export class DynamicDomAttach {
} }
} }
/**
* Internal method to detach the element
*/
internalDetach() { internalDetach() {
if (this.attached) { if (this.attached) {
assert(this.element.parentElement === this.parent, "Invalid parent #2"); assert(this.element.parentElement === this.parent, "Invalid parent #2");
@ -44,14 +72,50 @@ export class DynamicDomAttach {
} }
} }
/**
* Returns whether the element is currently attached
*/
isAttached() { isAttached() {
return this.attached; return this.attached;
} }
/**
* Actually sets the 'hovered' class
* @param {boolean} isHovered
*/
setIsHoveredClass(isHovered) {
this.element.classList.toggle("hovered", isHovered);
}
/**
* Call this every frame, and the dom attach class will take care of
* everything else
* @param {boolean} isVisible Whether the element should currently be visible or not
*/
update(isVisible) { update(isVisible) {
if (isVisible) { if (isVisible) {
this.lastVisibleTime = this.root ? this.root.time.realtimeNow() : 0; this.lastVisibleTime = this.root ? this.root.time.realtimeNow() : 0;
this.internalAttach(); this.internalAttach();
if (this.trackHover && this.root) {
let bounds = this.lastComputedBounds;
// Recompute bounds only once in a while
if (!bounds || this.root.time.realtimeNow() - this.lastComputedBoundsTime > 1.0) {
bounds = this.lastComputedBounds = this.element.getBoundingClientRect();
this.lastComputedBoundsTime = this.root.time.realtimeNow();
}
const mousePos = this.root.app.mousePosition;
if (mousePos) {
this.trackedIsHovered.set(
mousePos.x > bounds.left &&
mousePos.x < bounds.right &&
mousePos.y > bounds.top &&
mousePos.y < bounds.bottom
);
}
}
} else { } else {
if (!this.root || this.root.time.realtimeNow() - this.lastVisibleTime >= this.timeToKeepSeconds) { if (!this.root || this.root.time.realtimeNow() - this.lastVisibleTime >= this.timeToKeepSeconds) {
this.internalDetach(); this.internalDetach();

View File

@ -55,7 +55,7 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic {
this.signals.variantChanged.add(this.rerenderVariants, this); this.signals.variantChanged.add(this.rerenderVariants, this);
this.root.hud.signals.buildingSelectedForPlacement.add(this.startSelection, this); this.root.hud.signals.buildingSelectedForPlacement.add(this.startSelection, this);
this.domAttach = new DynamicDomAttach(this.root, this.element, {}); this.domAttach = new DynamicDomAttach(this.root, this.element, { trackHover: true });
this.variantsAttach = new DynamicDomAttach(this.root, this.variantsElement, {}); this.variantsAttach = new DynamicDomAttach(this.root, this.variantsElement, {});
this.currentInterpolatedCornerTile = new Vector(); this.currentInterpolatedCornerTile = new Vector();

View File

@ -1,81 +1,81 @@
import { BaseHUDPart } from "../base_hud_part"; import { BaseHUDPart } from "../base_hud_part";
import { makeDiv } from "../../../core/utils"; import { makeDiv } from "../../../core/utils";
import { GameRoot } from "../../root"; import { GameRoot } from "../../root";
import { MinerComponent } from "../../components/miner"; import { MinerComponent } from "../../components/miner";
import { DynamicDomAttach } from "../dynamic_dom_attach"; import { DynamicDomAttach } from "../dynamic_dom_attach";
import { TrackedState } from "../../../core/tracked_state"; import { TrackedState } from "../../../core/tracked_state";
import { cachebust } from "../../../core/cachebust"; import { cachebust } from "../../../core/cachebust";
import { T } from "../../../translations"; import { T } from "../../../translations";
const tutorialsByLevel = [ const tutorialsByLevel = [
// Level 1 // Level 1
[ [
// 1.1. place an extractor // 1.1. place an extractor
{ {
id: "1_1_extractor", id: "1_1_extractor",
condition: /** @param {GameRoot} root */ root => { condition: /** @param {GameRoot} root */ root => {
return root.entityMgr.getAllWithComponent(MinerComponent).length === 0; return root.entityMgr.getAllWithComponent(MinerComponent).length === 0;
}, },
}, },
// 1.2. connect to hub // 1.2. connect to hub
{ {
id: "1_2_conveyor", id: "1_2_conveyor",
condition: /** @param {GameRoot} root */ root => { condition: /** @param {GameRoot} root */ root => {
return root.hubGoals.getCurrentGoalDelivered() === 0; return root.hubGoals.getCurrentGoalDelivered() === 0;
}, },
}, },
// 1.3 wait for completion // 1.3 wait for completion
{ {
id: "1_3_expand", id: "1_3_expand",
condition: () => true, condition: () => true,
}, },
], ],
]; ];
export class HUDInteractiveTutorial extends BaseHUDPart { export class HUDInteractiveTutorial extends BaseHUDPart {
createElements(parent) { createElements(parent) {
this.element = makeDiv( this.element = makeDiv(
parent, parent,
"ingame_HUD_InteractiveTutorial", "ingame_HUD_InteractiveTutorial",
["animEven"], ["animEven"],
` `
<strong class="title">${T.ingame.interactiveTutorial.title}</strong> <strong class="title">${T.ingame.interactiveTutorial.title}</strong>
` `
); );
this.elementDescription = makeDiv(this.element, null, ["desc"]); this.elementDescription = makeDiv(this.element, null, ["desc"]);
this.elementGif = makeDiv(this.element, null, ["helperGif"]); this.elementGif = makeDiv(this.element, null, ["helperGif"]);
} }
initialize() { initialize() {
this.domAttach = new DynamicDomAttach(this.root, this.element); this.domAttach = new DynamicDomAttach(this.root, this.element, { trackHover: true });
this.currentHintId = new TrackedState(this.onHintChanged, this); this.currentHintId = new TrackedState(this.onHintChanged, this);
} }
onHintChanged(hintId) { onHintChanged(hintId) {
this.elementDescription.innerHTML = T.ingame.interactiveTutorial.hints[hintId]; this.elementDescription.innerHTML = T.ingame.interactiveTutorial.hints[hintId];
this.elementGif.style.backgroundImage = this.elementGif.style.backgroundImage =
"url('" + cachebust("res/ui/interactive_tutorial.noinline/" + hintId + ".gif") + "')"; "url('" + cachebust("res/ui/interactive_tutorial.noinline/" + hintId + ".gif") + "')";
this.element.classList.toggle("animEven"); this.element.classList.toggle("animEven");
this.element.classList.toggle("animOdd"); this.element.classList.toggle("animOdd");
} }
update() { update() {
// Compute current hint // Compute current hint
const thisLevelHints = tutorialsByLevel[this.root.hubGoals.level - 1]; const thisLevelHints = tutorialsByLevel[this.root.hubGoals.level - 1];
let targetHintId = null; let targetHintId = null;
if (thisLevelHints) { if (thisLevelHints) {
for (let i = 0; i < thisLevelHints.length; ++i) { for (let i = 0; i < thisLevelHints.length; ++i) {
const hint = thisLevelHints[i]; const hint = thisLevelHints[i];
if (hint.condition(this.root)) { if (hint.condition(this.root)) {
targetHintId = hint.id; targetHintId = hint.id;
break; break;
} }
} }
} }
this.currentHintId.set(targetHintId); this.currentHintId.set(targetHintId);
this.domAttach.update(!!targetHintId); this.domAttach.update(!!targetHintId);
} }
} }

View File

@ -1,323 +1,330 @@
import { makeDiv } from "../../../core/utils"; import { makeDiv } from "../../../core/utils";
import { T } from "../../../translations"; import { T } from "../../../translations";
import { import {
getStringForKeyCode, getStringForKeyCode,
KEYCODE_LMB, KEYCODE_LMB,
KEYCODE_MMB, KEYCODE_MMB,
KEYCODE_RMB, KEYCODE_RMB,
KEYMAPPINGS, KEYMAPPINGS,
} from "../../key_action_mapper"; } from "../../key_action_mapper";
import { BaseHUDPart } from "../base_hud_part"; import { BaseHUDPart } from "../base_hud_part";
import { DynamicDomAttach } from "../dynamic_dom_attach"; import { DynamicDomAttach } from "../dynamic_dom_attach";
const DIVIDER_TOKEN = "/"; const DIVIDER_TOKEN = "/";
const ADDER_TOKEN = "+"; const ADDER_TOKEN = "+";
/** /**
* @typedef {{ keyCode: number }} KeyCode * @typedef {{ keyCode: number }} KeyCode
*/ */
/** /**
* @typedef {{ * @typedef {{
* condition: () => boolean, * condition: () => boolean,
* keys: Array<KeyCode|number|string>, * keys: Array<KeyCode|number|string>,
* label: string, * label: string,
* cachedElement?: HTMLElement, * cachedElement?: HTMLElement,
* cachedVisibility?: boolean * cachedVisibility?: boolean
* }} KeyBinding * }} KeyBinding
*/ */
export class HUDKeybindingOverlay extends BaseHUDPart { export class HUDKeybindingOverlay extends BaseHUDPart {
initialize() {} /**
* HELPER / Returns if there is a building selected for placement
/** * @returns {boolean}
* HELPER / Returns if there is a building selected for placement */
* @returns {boolean} get buildingPlacementActive() {
*/ const placer = this.root.hud.parts.buildingPlacer;
get buildingPlacementActive() { return !this.mapOverviewActive && placer && !!placer.currentMetaBuilding.get();
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
* HELPER / Returns if there is a building selected for placement and * @returns {boolean}
* it supports the belt planner */
* @returns {boolean} get buildingPlacementSupportsBeltPlanner() {
*/ const placer = this.root.hud.parts.buildingPlacer;
get buildingPlacementSupportsBeltPlanner() { return (
const placer = this.root.hud.parts.buildingPlacer; !this.mapOverviewActive &&
return ( placer &&
!this.mapOverviewActive && placer.currentMetaBuilding.get() &&
placer && placer.currentMetaBuilding.get().getHasDirectionLockAvailable()
placer.currentMetaBuilding.get() && );
placer.currentMetaBuilding.get().getHasDirectionLockAvailable() }
);
} /**
* HELPER / Returns if there is a building selected for placement and
/** * it has multiplace enabled by default
* HELPER / Returns if there is a building selected for placement and * @returns {boolean}
* it has multiplace enabled by default */
* @returns {boolean} get buildingPlacementStaysInPlacement() {
*/ const placer = this.root.hud.parts.buildingPlacer;
get buildingPlacementStaysInPlacement() { return (
const placer = this.root.hud.parts.buildingPlacer; !this.mapOverviewActive &&
return ( placer &&
!this.mapOverviewActive && placer.currentMetaBuilding.get() &&
placer && placer.currentMetaBuilding.get().getStayInPlacementMode()
placer.currentMetaBuilding.get() && );
placer.currentMetaBuilding.get().getStayInPlacementMode() }
);
} /**
* HELPER / Returns if there is a blueprint selected for placement
/** * @returns {boolean}
* HELPER / Returns if there is a blueprint selected for placement */
* @returns {boolean} get blueprintPlacementActive() {
*/ const placer = this.root.hud.parts.blueprintPlacer;
get blueprintPlacementActive() { return placer && !!placer.currentBlueprint.get();
const placer = this.root.hud.parts.blueprintPlacer; }
return placer && !!placer.currentBlueprint.get();
} /**
* HELPER / Returns if the belt planner is currently active
/** * @returns {boolean}
* HELPER / Returns if the belt planner is currently active */
* @returns {boolean} get beltPlannerActive() {
*/ const placer = this.root.hud.parts.buildingPlacer;
get beltPlannerActive() { return !this.mapOverviewActive && placer && placer.isDirectionLockActive;
const placer = this.root.hud.parts.buildingPlacer; }
return !this.mapOverviewActive && placer && placer.isDirectionLockActive;
} /**
* HELPER / Returns if there is a last blueprint available
/** * @returns {boolean}
* HELPER / Returns if there is a last blueprint available */
* @returns {boolean} get lastBlueprintAvailable() {
*/ const placer = this.root.hud.parts.blueprintPlacer;
get lastBlueprintAvailable() { return placer && !!placer.lastBlueprintUsed;
const placer = this.root.hud.parts.blueprintPlacer; }
return placer && !!placer.lastBlueprintUsed;
} /**
* HELPER / Returns if there is anything selected on the map
/** * @returns {boolean}
* HELPER / Returns if there is anything selected on the map */
* @returns {boolean} get anythingSelectedOnMap() {
*/ const selector = this.root.hud.parts.massSelector;
get anythingSelectedOnMap() { return selector && selector.selectedUids.size > 0;
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}
* HELPER / Returns if there is a building or blueprint selected for placement */
* @returns {boolean} get anyPlacementActive() {
*/ return this.buildingPlacementActive || this.blueprintPlacementActive;
get anyPlacementActive() { }
return this.buildingPlacementActive || this.blueprintPlacementActive;
} /**
* HELPER / Returns if the map overview is active
/** * @returns {boolean}
* HELPER / Returns if the map overview is active */
* @returns {boolean} get mapOverviewActive() {
*/ return this.root.camera.getIsMapOverlayActive();
get mapOverviewActive() { }
return this.root.camera.getIsMapOverlayActive();
} /**
* Initializes the element
/** * @param {HTMLElement} parent
* Initializes the element */
* @param {HTMLElement} parent createElements(parent) {
*/ const mapper = this.root.keyMapper;
createElements(parent) { const k = KEYMAPPINGS;
const mapper = this.root.keyMapper;
const k = KEYMAPPINGS; /** @type {Array<KeyBinding>} */
this.keybindings = [
/** @type {Array<KeyBinding>} */ {
this.keybindings = [ // Move map - Including mouse
{ label: T.ingame.keybindingsOverlay.moveMap,
// Move map - Including mouse keys: [
label: T.ingame.keybindingsOverlay.moveMap, KEYCODE_LMB,
keys: [ DIVIDER_TOKEN,
KEYCODE_LMB, k.navigation.mapMoveUp,
DIVIDER_TOKEN, k.navigation.mapMoveLeft,
k.navigation.mapMoveUp, k.navigation.mapMoveDown,
k.navigation.mapMoveLeft, k.navigation.mapMoveRight,
k.navigation.mapMoveDown, ],
k.navigation.mapMoveRight, condition: () => !this.anyPlacementActive,
], },
condition: () => !this.anyPlacementActive,
}, {
// Move map - No mouse
{ label: T.ingame.keybindingsOverlay.moveMap,
// Move map - No mouse keys: [
label: T.ingame.keybindingsOverlay.moveMap, k.navigation.mapMoveUp,
keys: [ k.navigation.mapMoveLeft,
k.navigation.mapMoveUp, k.navigation.mapMoveDown,
k.navigation.mapMoveLeft, k.navigation.mapMoveRight,
k.navigation.mapMoveDown, ],
k.navigation.mapMoveRight, condition: () => this.anyPlacementActive,
], },
condition: () => this.anyPlacementActive,
}, {
// [OVERVIEW] Create marker with right click
{ label: T.ingame.keybindingsOverlay.createMarker,
// [OVERVIEW] Create marker with right click keys: [KEYCODE_RMB],
label: T.ingame.keybindingsOverlay.createMarker, condition: () => this.mapOverviewActive && !this.blueprintPlacementActive,
keys: [KEYCODE_RMB], },
condition: () => this.mapOverviewActive && !this.blueprintPlacementActive,
}, {
// Pipette
{ label: T.ingame.keybindingsOverlay.pipette,
// Pipette keys: [k.placement.pipette],
label: T.ingame.keybindingsOverlay.pipette, condition: () => !this.mapOverviewActive && !this.blueprintPlacementActive,
keys: [k.placement.pipette], },
condition: () => !this.mapOverviewActive && !this.blueprintPlacementActive,
}, {
// Cancel placement
{ label: T.ingame.keybindingsOverlay.stopPlacement,
// Cancel placement keys: [KEYCODE_RMB],
label: T.ingame.keybindingsOverlay.stopPlacement, condition: () => this.anyPlacementActive,
keys: [KEYCODE_RMB], },
condition: () => this.anyPlacementActive,
}, {
// Delete with right click
{ label: T.ingame.keybindingsOverlay.delete,
// Delete with right click keys: [KEYCODE_RMB],
label: T.ingame.keybindingsOverlay.delete, condition: () =>
keys: [KEYCODE_RMB], !this.anyPlacementActive && !this.mapOverviewActive && !this.anythingSelectedOnMap,
condition: () => },
!this.anyPlacementActive && !this.mapOverviewActive && !this.anythingSelectedOnMap,
}, {
// Area select
{ label: T.ingame.keybindingsOverlay.selectBuildings,
// Area select keys: [k.massSelect.massSelectStart, ADDER_TOKEN, KEYCODE_LMB],
label: T.ingame.keybindingsOverlay.selectBuildings, condition: () => !this.anyPlacementActive && !this.anythingSelectedOnMap,
keys: [k.massSelect.massSelectStart, ADDER_TOKEN, KEYCODE_LMB], },
condition: () => !this.anyPlacementActive && !this.anythingSelectedOnMap,
}, {
// Place building
{ label: T.ingame.keybindingsOverlay.placeBuilding,
// Place building keys: [KEYCODE_LMB],
label: T.ingame.keybindingsOverlay.placeBuilding, condition: () => this.anyPlacementActive,
keys: [KEYCODE_LMB], },
condition: () => this.anyPlacementActive,
}, {
// Rotate
{ label: T.ingame.keybindingsOverlay.rotateBuilding,
// Rotate keys: [k.placement.rotateWhilePlacing],
label: T.ingame.keybindingsOverlay.rotateBuilding, condition: () => this.anyPlacementActive && !this.beltPlannerActive,
keys: [k.placement.rotateWhilePlacing], },
condition: () => this.anyPlacementActive && !this.beltPlannerActive,
}, {
// [BELT PLANNER] Flip Side
{ label: T.ingame.keybindingsOverlay.plannerSwitchSide,
// [BELT PLANNER] Flip Side keys: [k.placement.switchDirectionLockSide],
label: T.ingame.keybindingsOverlay.plannerSwitchSide, condition: () => this.beltPlannerActive,
keys: [k.placement.switchDirectionLockSide], },
condition: () => this.beltPlannerActive,
}, {
// Place last blueprint
{ label: T.ingame.keybindingsOverlay.pasteLastBlueprint,
// Place last blueprint keys: [k.massSelect.pasteLastBlueprint],
label: T.ingame.keybindingsOverlay.pasteLastBlueprint, condition: () => !this.blueprintPlacementActive && this.lastBlueprintAvailable,
keys: [k.massSelect.pasteLastBlueprint], },
condition: () => !this.blueprintPlacementActive && this.lastBlueprintAvailable,
}, {
// Belt planner
{ label: T.ingame.keybindingsOverlay.lockBeltDirection,
// Belt planner keys: [k.placementModifiers.lockBeltDirection],
label: T.ingame.keybindingsOverlay.lockBeltDirection, condition: () => this.buildingPlacementSupportsBeltPlanner && !this.beltPlannerActive,
keys: [k.placementModifiers.lockBeltDirection], },
condition: () => this.buildingPlacementSupportsBeltPlanner && !this.beltPlannerActive,
}, {
// [SELECTION] Destroy
{ label: T.ingame.keybindingsOverlay.delete,
// [SELECTION] Destroy keys: [k.massSelect.confirmMassDelete],
label: T.ingame.keybindingsOverlay.delete, condition: () => this.anythingSelectedOnMap,
keys: [k.massSelect.confirmMassDelete], },
condition: () => this.anythingSelectedOnMap,
}, {
// [SELECTION] Cancel
{ label: T.ingame.keybindingsOverlay.clearSelection,
// [SELECTION] Cancel keys: [k.general.back],
label: T.ingame.keybindingsOverlay.clearSelection, condition: () => this.anythingSelectedOnMap,
keys: [k.general.back], },
condition: () => this.anythingSelectedOnMap, {
}, // [SELECTION] Cut
{ label: T.ingame.keybindingsOverlay.cutSelection,
// [SELECTION] Cut keys: [k.massSelect.massSelectCut],
label: T.ingame.keybindingsOverlay.cutSelection, condition: () => this.anythingSelectedOnMap,
keys: [k.massSelect.massSelectCut], },
condition: () => this.anythingSelectedOnMap,
}, {
// [SELECTION] Copy
{ label: T.ingame.keybindingsOverlay.copySelection,
// [SELECTION] Copy keys: [k.massSelect.massSelectCopy],
label: T.ingame.keybindingsOverlay.copySelection, condition: () => this.anythingSelectedOnMap,
keys: [k.massSelect.massSelectCopy], },
condition: () => this.anythingSelectedOnMap,
}, {
// Switch layers
{ label: T.ingame.keybindingsOverlay.switchLayers,
// Switch layers keys: [k.ingame.switchLayers],
label: T.ingame.keybindingsOverlay.switchLayers, condition: () => true,
keys: [k.ingame.switchLayers], },
condition: () => true, ];
},
]; if (!this.root.app.settings.getAllSettings().alwaysMultiplace) {
this.keybindings.push({
if (!this.root.app.settings.getAllSettings().alwaysMultiplace) { // Multiplace
this.keybindings.push({ label: T.ingame.keybindingsOverlay.placeMultiple,
// Multiplace keys: [k.placementModifiers.placeMultiple],
label: T.ingame.keybindingsOverlay.placeMultiple, condition: () => this.anyPlacementActive && !this.buildingPlacementStaysInPlacement,
keys: [k.placementModifiers.placeMultiple], });
condition: () => this.anyPlacementActive && !this.buildingPlacementStaysInPlacement, }
});
} this.element = makeDiv(parent, "ingame_HUD_KeybindingOverlay", []);
this.element = makeDiv(parent, "ingame_HUD_KeybindingOverlay", []); for (let i = 0; i < this.keybindings.length; ++i) {
let html = "";
for (let i = 0; i < this.keybindings.length; ++i) { const handle = this.keybindings[i];
let html = "";
const handle = this.keybindings[i]; for (let k = 0; k < handle.keys.length; ++k) {
const key = handle.keys[k];
for (let k = 0; k < handle.keys.length; ++k) {
const key = handle.keys[k]; switch (key) {
case KEYCODE_LMB:
switch (key) { html += `<code class="keybinding leftMouse"></code>`;
case KEYCODE_LMB: break;
html += `<code class="keybinding leftMouse"></code>`; case KEYCODE_RMB:
break; html += `<code class="keybinding rightMouse"></code>`;
case KEYCODE_RMB: break;
html += `<code class="keybinding rightMouse"></code>`; case KEYCODE_MMB:
break; html += `<code class="keybinding middleMouse"></code>`;
case KEYCODE_MMB: break;
html += `<code class="keybinding middleMouse"></code>`; case DIVIDER_TOKEN:
break; html += `<i></i>`;
case DIVIDER_TOKEN: break;
html += `<i></i>`; case ADDER_TOKEN:
break; html += `+`;
case ADDER_TOKEN: break;
html += `+`; default:
break; html += `<code class="keybinding">${getStringForKeyCode(
default: mapper.getBinding(/** @type {KeyCode} */ (key)).keyCode
html += `<code class="keybinding">${getStringForKeyCode( )}</code>`;
mapper.getBinding(/** @type {KeyCode} */ (key)).keyCode }
)}</code>`; }
} html += `<label>${handle.label}</label>`;
}
html += `<label>${handle.label}</label>`; handle.cachedElement = makeDiv(this.element, null, ["binding"], html);
handle.cachedVisibility = false;
handle.cachedElement = makeDiv(this.element, null, ["binding"], html); }
handle.cachedVisibility = false; }
}
} initialize() {
this.domAttach = new DynamicDomAttach(this.root, this.element, {
update() { trackHover: true,
for (let i = 0; i < this.keybindings.length; ++i) { });
const handle = this.keybindings[i]; }
const visibility = handle.condition();
if (visibility !== handle.cachedVisibility) { update() {
handle.cachedVisibility = visibility; for (let i = 0; i < this.keybindings.length; ++i) {
handle.cachedElement.classList.toggle("visible", visibility); const handle = this.keybindings[i];
} const visibility = handle.condition();
} if (visibility !== handle.cachedVisibility) {
} handle.cachedVisibility = visibility;
} handle.cachedElement.classList.toggle("visible", visibility);
}
}
// Required for hover
this.domAttach.update(true);
}
}