Allow pinning shapes

pull/33/head
tobspr 4 years ago
parent e43a22b56d
commit 38970141d8

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 854 B

@ -1,8 +0,0 @@
body.ingameDialogOpen {
#ingame_Canvas,
#ingame_HUD_GameMenu,
#ingame_HUD_KeybindingOverlay,
#ingame_HUD_buildings_toolbar {
filter: blur(5px);
}
}

@ -20,7 +20,7 @@
@include S(height, 10px);
width: 1px;
@include S(margin, 0, 3px);
background-color: #ccc;
background-color: #888;
transform: rotate(10deg);
// @include S(margin, 0, 3px);
}

@ -0,0 +1,54 @@
#ingame_HUD_PinnedShapes {
position: absolute;
@include S(left, 9px);
@include S(top, 120px);
@include PlainText;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
> .shape {
position: relative;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
@include S(margin-bottom, 5px);
&.unpinable {
> canvas {
cursor: pointer;
pointer-events: all;
}
}
> canvas {
@include S(width, 25px);
@include S(height, 25px);
}
> .amountLabel {
@include S(margin-left, 5px);
@include SuperSmallText;
font-weight: bold;
display: inline-flex;
align-items: center;
flex-direction: row;
}
&.marked .amountLabel {
&::after {
content: " ";
position: absolute;
display: inline-block;
@include S(width, 9px);
@include S(height, 9px);
opacity: 0.8;
@include S(top, -4px);
@include S(left, -4px);
background: uiResource("icons/current_goal_marker.png") center center / contain no-repeat;
}
}
}
}

@ -100,6 +100,50 @@
flex-direction: column;
align-items: center;
button.pin {
@include S(width, 12px);
@include S(height, 12px);
background: uiResource("icons/pin.png") center center / 95% no-repeat;
position: absolute;
@include S(top, -2px);
@include S(right, -2px);
opacity: 0.6;
cursor: pointer;
pointer-events: all;
@include IncreasedClickArea(5px);
transition: opacity 0.12s ease-in-out;
&:hover {
opacity: 0.7;
}
&.alreadyPinned {
opacity: 0.1 !important;
pointer-events: none;
cursor: default;
}
&.pinned {
opacity: 0.1;
pointer-events: none;
cursor: default;
@include InlineAnimation(0.3s ease-in-out) {
0% {
opacity: 1;
transform: scale(0.8);
}
30% {
opacity: 1;
transform: scale(1.2);
}
100% {
transform: scale(1);
}
}
}
}
canvas {
@include S(width, 40px);
@include S(height, 40px);

@ -31,16 +31,16 @@
@import "ingame_hud/unlock_notification";
@import "ingame_hud/shop";
@import "ingame_hud/game_menu";
@import "ingame_hud/blur_overlay";
@import "ingame_hud/dialogs";
@import "ingame_hud/mass_selector";
@import "ingame_hud/vignette_overlay";
@import "ingame_hud/statistics";
@import "ingame_hud/pinned_shapes";
// Z-Index
$elements: ingame_Canvas, ingame_VignetteOverlay, ingame_HUD_building_placer, ingame_HUD_buildings_toolbar,
ingame_HUD_GameMenu, ingame_HUD_KeybindingOverlay, ingame_HUD_Shop, ingame_HUD_Statistics,
ingame_HUD_BetaOverlay, ingame_HUD_MassSelector, ingame_HUD_UnlockNotification;
$elements: ingame_Canvas, ingame_VignetteOverlay, ingame_HUD_building_placer, ingame_HUD_PinnedShapes,
ingame_HUD_buildings_toolbar, ingame_HUD_GameMenu, ingame_HUD_KeybindingOverlay, ingame_HUD_Shop,
ingame_HUD_Statistics, ingame_HUD_BetaOverlay, ingame_HUD_MassSelector, ingame_HUD_UnlockNotification;
$zindex: 100;
@ -56,7 +56,17 @@ body.uiHidden {
#ingame_HUD_buildings_toolbar,
#ingame_HUD_building_placer,
#ingame_HUD_GameMenu,
#ingame_HUD_MassSelector {
#ingame_HUD_MassSelector,
#ingame_HUD_PinnedShapes {
display: none !important;
}
}
body.ingameDialogOpen {
#ingame_Canvas,
#ingame_HUD_GameMenu,
#ingame_HUD_KeybindingOverlay,
#ingame_HUD_buildings_toolbar,
#ingame_HUD_PinnedShapes {
filter: blur(5px);
}
}

@ -75,6 +75,14 @@ export class HubGoals extends BasicSerializableObject {
getShapesStored(definition) {
return this.storedShapes[definition.getHash()] || 0;
}
/**
* Returns how much of the current shape is stored
* @param {string} key
* @returns {number}
*/
getShapesStoredByKey(key) {
return this.storedShapes[key] || 0;
}
/**
* Returns how much of the current goal was already delivered
@ -158,11 +166,12 @@ export class HubGoals extends BasicSerializableObject {
onGoalCompleted() {
const reward = this.currentGoal.reward;
this.gainedRewards[reward] = (this.gainedRewards[reward] || 0) + 1;
this.root.signals.storyGoalCompleted.dispatch(this.level, reward);
this.root.app.gameAnalytics.handleLevelCompleted(this.level);
++this.level;
this.createNextGoal();
this.root.signals.storyGoalCompleted.dispatch(this.level - 1, reward);
}
/**

@ -17,6 +17,8 @@ import { HUDMassSelector } from "./parts/mass_selector";
import { HUDVignetteOverlay } from "./parts/vignette_overlay";
import { HUDStatistics } from "./parts/statistics";
import { MetaBuilding } from "../meta_building";
import { HUDPinnedShapes } from "./parts/pinned_shapes";
import { ShapeDefinition } from "../shape_definition";
export class GameHUD {
/**
@ -47,11 +49,14 @@ export class GameHUD {
vignetteOverlay: new HUDVignetteOverlay(this.root),
pinnedShapes: new HUDPinnedShapes(this.root),
// betaOverlay: new HUDBetaOverlay(this.root),
};
this.signals = {
selectedPlacementBuildingChanged: /** @type {TypedSignal<[MetaBuilding|null]>} */ (new Signal()),
shapePinRequested: /** @type {TypedSignal<[ShapeDefinition]>} */ (new Signal()),
};
if (!IS_MOBILE) {

@ -0,0 +1,128 @@
import { BaseHUDPart } from "../base_hud_part";
import { makeDiv, removeAllChildren, formatBigNumber } from "../../../core/utils";
import { ClickDetector } from "../../../core/click_detector";
import { ShapeDefinition } from "../../shape_definition";
export class HUDPinnedShapes extends BaseHUDPart {
createElements(parent) {
this.element = makeDiv(parent, "ingame_HUD_PinnedShapes", []);
}
initialize() {
this.pinnedShapes = [];
/** @type {Array<{key: string, amountLabel: HTMLElement, lastRenderedValue: number, element: HTMLElement, detector?: ClickDetector}>} */
this.handles = [];
this.rerenderFull();
this.root.signals.storyGoalCompleted.add(this.rerenderFull, this);
this.root.hud.signals.shapePinRequested.add(this.pinNewShape, this);
}
/**
* Returns whether a given shape is pinned
* @param {string} key
*/
isShapePinned(key) {
if (!this.pinnedShapes) {
return false;
}
return (
this.pinnedShapes.indexOf(key) >= 0 || key === this.root.hubGoals.currentGoal.definition.getHash()
);
}
rerenderFull() {
const currentGoal = this.root.hubGoals.currentGoal.definition.getHash();
// First, remove old ones
for (let i = 0; i < this.handles.length; ++i) {
this.handles[i].element.remove();
const detector = this.handles[i].detector;
if (detector) {
detector.cleanup();
}
}
this.handles = [];
this.internalPinShape(currentGoal, false);
for (let i = 0; i < this.pinnedShapes.length; ++i) {
const key = this.pinnedShapes[i];
if (key !== currentGoal) {
this.internalPinShape(key);
}
}
}
/**
* Pins a shape
* @param {string} key
* @param {boolean} canUnpin
*/
internalPinShape(key, canUnpin = true) {
const definition = this.root.shapeDefinitionMgr.getShapeFromShortKey(key);
const element = makeDiv(this.element, null, ["shape"]);
const canvas = definition.generateAsCanvas(120);
element.appendChild(canvas);
let detector = null;
if (canUnpin) {
element.classList.add("unpinable");
detector = new ClickDetector(element, {
consumeEvents: true,
preventDefault: true,
});
detector.click.add(() => this.unpinShape(key));
} else {
element.classList.add("marked");
}
const amountLabel = makeDiv(element, null, ["amountLabel"], "123");
this.handles.push({
key,
element,
amountLabel,
lastRenderedValue: -1,
});
}
update() {
for (let i = 0; i < this.handles.length; ++i) {
const handle = this.handles[i];
const currentValue = this.root.hubGoals.getShapesStoredByKey(handle.key);
if (currentValue !== handle.lastRenderedValue) {
handle.lastRenderedValue = currentValue;
handle.amountLabel.innerText = formatBigNumber(currentValue);
}
}
}
unpinShape(key) {
const index = this.pinnedShapes.indexOf(key);
if (index >= 0) {
const key = this.pinnedShapes[index];
this.pinnedShapes.splice(index, 1);
this.rerenderFull();
}
}
/**
* @param {ShapeDefinition} definition
*/
pinNewShape(definition) {
const key = definition.getHash();
if (key === this.root.hubGoals.currentGoal.definition.getHash()) {
// Can not pin current goal
return;
}
if (this.pinnedShapes.indexOf(key) < 0) {
// Pin
this.pinnedShapes.push(key);
this.rerenderFull();
}
}
}

@ -6,6 +6,7 @@ import { DynamicDomAttach } from "../dynamic_dom_attach";
import { InputReceiver } from "../../../core/input_receiver";
import { KeyActionMapper } from "../../key_action_mapper";
import { Math_min } from "../../../core/builtins";
import { ClickDetector } from "../../../core/click_detector";
export class HUDShop extends BaseHUDPart {
createElements(parent) {
@ -61,7 +62,6 @@ export class HUDShop extends BaseHUDPart {
for (const upgradeId in this.upgradeToElements) {
const handle = this.upgradeToElements[upgradeId];
const { description, tiers } = UPGRADES[upgradeId];
// removeAllChildren(handle.elem);
const currentTier = this.root.hubGoals.getUpgradeLevel(upgradeId);
const tierHandle = tiers[currentTier];
@ -70,9 +70,15 @@ export class HUDShop extends BaseHUDPart {
handle.elemTierLabel.innerText = "Tier " + TIER_LABELS[currentTier];
handle.elemTierLabel.setAttribute("data-tier", currentTier);
// Cleanup detectors
for (let i = 0; i < handle.requireIndexToElement.length; ++i) {
const requiredHandle = handle.requireIndexToElement[i];
requiredHandle.container.remove();
requiredHandle.pinDetector.cleanup();
}
// Cleanup
handle.requireIndexToElement = [];
removeAllChildren(handle.elemRequirements);
handle.elem.classList.toggle("maxLevel", !tierHandle);
@ -86,14 +92,14 @@ export class HUDShop extends BaseHUDPart {
handle.elemDescription.innerText = description(tierHandle.improvement);
tierHandle.required.forEach(({ shape, amount }) => {
const requireDiv = makeDiv(handle.elemRequirements, null, ["requirement"]);
const container = makeDiv(handle.elemRequirements, null, ["requirement"]);
const shapeDef = this.root.shapeDefinitionMgr.getShapeFromShortKey(shape);
const shapeCanvas = shapeDef.generateAsCanvas(120);
shapeCanvas.classList.add();
requireDiv.appendChild(shapeCanvas);
container.appendChild(shapeCanvas);
const progressContainer = makeDiv(requireDiv, null, ["amount"]);
const progressContainer = makeDiv(container, null, ["amount"]);
const progressBar = document.createElement("label");
progressBar.classList.add("progressBar");
progressContainer.appendChild(progressBar);
@ -101,11 +107,31 @@ export class HUDShop extends BaseHUDPart {
const progressLabel = document.createElement("label");
progressContainer.appendChild(progressLabel);
const pinButton = document.createElement("button");
pinButton.classList.add("pin");
container.appendChild(pinButton);
if (this.root.hud.parts.pinnedShapes.isShapePinned(shape)) {
console.log("ALREADY PINNED:", shape);
pinButton.classList.add("alreadyPinned");
}
const pinDetector = new ClickDetector(pinButton, {
consumeEvents: true,
preventDefault: true,
});
pinDetector.click.add(() => {
this.root.hud.signals.shapePinRequested.dispatch(shapeDef);
pinButton.classList.add("pinned");
});
handle.requireIndexToElement.push({
container,
progressLabel,
progressBar,
definition: shapeDef,
required: amount,
pinDetector,
});
});
}
@ -148,6 +174,17 @@ export class HUDShop extends BaseHUDPart {
cleanup() {
document.body.classList.remove("ingameDialogOpen");
// Cleanup detectors
for (const upgradeId in this.upgradeToElements) {
const handle = this.upgradeToElements[upgradeId];
for (let i = 0; i < handle.requireIndexToElement.length; ++i) {
const requiredHandle = handle.requireIndexToElement[i];
requiredHandle.container.remove();
requiredHandle.pinDetector.cleanup();
}
handle.requireIndexToElement = [];
}
}
show() {
@ -155,7 +192,7 @@ export class HUDShop extends BaseHUDPart {
document.body.classList.add("ingameDialogOpen");
// this.background.classList.add("visible");
this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
this.update();
this.rerenderFull();
}
close() {

@ -153,6 +153,9 @@ export class HUDStatistics extends BaseHUDPart {
* Performs a full rerender, regenerating everything
*/
rerenderFull() {
for (const key in this.activeHandles) {
this.activeHandles[key].detach();
}
removeAllChildren(this.contentDiv);
// Now, attach new ones
@ -170,8 +173,6 @@ export class HUDStatistics extends BaseHUDPart {
}
}
// const entries = Object.entries(this.root.hubGoals.storedShapes);
entries.sort((a, b) => b[1] - a[1]);
let rendered = new Set();
@ -179,7 +180,6 @@ export class HUDStatistics extends BaseHUDPart {
for (let i = 0; i < Math_min(entries.length, 200); ++i) {
const entry = entries[i];
const shapeKey = entry[0];
const amount = entry[1];
let handle = this.activeHandles[shapeKey];
if (!handle) {

Loading…
Cancel
Save