mirror of
https://github.com/tobspr/shapez.io.git
synced 2024-10-27 20:34:29 +00:00
Allow pinning shapes
This commit is contained in:
parent
e43a22b56d
commit
38970141d8
BIN
res/ui/icons/current_goal_marker.png
Normal file
BIN
res/ui/icons/current_goal_marker.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 878 B |
BIN
res/ui/icons/pin.png
Normal file
BIN
res/ui/icons/pin.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
BIN
res/ui/icons/unpin.png
Normal file
BIN
res/ui/icons/unpin.png
Normal file
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);
|
||||
}
|
||||
|
54
src/css/ingame_hud/pinned_shapes.scss
Normal file
54
src/css/ingame_hud/pinned_shapes.scss
Normal file
@ -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) {
|
||||
|
128
src/js/game/hud/parts/pinned_shapes.js
Normal file
128
src/js/game/hud/parts/pinned_shapes.js
Normal file
@ -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…
Reference in New Issue
Block a user