diff --git a/src/css/ingame_hud/blueprint_placer.scss b/src/css/ingame_hud/blueprint_placer.scss new file mode 100644 index 00000000..acf66087 --- /dev/null +++ b/src/css/ingame_hud/blueprint_placer.scss @@ -0,0 +1,39 @@ +#ingame_HUD_BlueprintPlacer { + position: absolute; + @include S(top, 50px); + left: 50%; + transform: translateX(-50%); + color: #333; + z-index: 9999; + background: rgba(0, 10, 20, 0.5); + @include S(padding, 5px); + display: flex; + flex-direction: column; + color: #fff; + @include S(width, 120px); + align-items: center; + justify-content: center; + + .label { + @include PlainText; + text-transform: uppercase; + } + .costContainer { + display: flex; + align-items: center; + @include Heading; + + > canvas { + @include S(margin-left, 5px); + @include S(width, 30px); + @include S(height, 30px); + } + } + + &:not(.canAfford) { + background: rgba(98, 27, 41, 0.8); + // .costContainer { + color: rgb(255, 97, 128); + // } + } +} diff --git a/src/css/main.scss b/src/css/main.scss index 3f06853f..bc19b3a0 100644 --- a/src/css/main.scss +++ b/src/css/main.scss @@ -47,6 +47,7 @@ @import "ingame_hud/entity_debugger"; @import "ingame_hud/tutorial_hints"; @import "ingame_hud/watermark"; +@import "ingame_hud/blueprint_placer"; // prettier-ignore $elements: @@ -68,6 +69,7 @@ ingame_HUD_DebugInfo, ingame_HUD_EntityDebugger, ingame_HUD_TutorialHints, ingame_HUD_buildings_toolbar, +ingame_HUD_BlueprintPlacer, ingame_HUD_Watermark, // Overlays diff --git a/src/js/core/config.js b/src/js/core/config.js index 953af4e7..06af636c 100644 --- a/src/js/core/config.js +++ b/src/js/core/config.js @@ -83,7 +83,7 @@ export const globalConfig = { debug: { /* dev:start */ - // fastGameEnter: true, + fastGameEnter: true, // noArtificialDelays: true, // disableSavegameWrite: true, // showEntityBounds: true, diff --git a/src/js/game/hub_goals.js b/src/js/game/hub_goals.js index a6f24284..aa410e75 100644 --- a/src/js/game/hub_goals.js +++ b/src/js/game/hub_goals.js @@ -8,7 +8,7 @@ import { enumItemProcessorTypes } from "./components/item_processor"; import { GameRoot } from "./root"; import { enumSubShape, ShapeDefinition } from "./shape_definition"; import { enumHubGoalRewards, tutorialGoals } from "./tutorial_goals"; -import { UPGRADES } from "./upgrades"; +import { UPGRADES, blueprintShape } from "./upgrades"; export class HubGoals extends BasicSerializableObject { static getId() { @@ -53,6 +53,10 @@ export class HubGoals extends BasicSerializableObject { } this.upgradeImprovements[upgradeId] = totalImprovement; } + + if (G_IS_DEV) { + this.storedShapes[blueprintShape] = 1000; + } } /** @@ -77,6 +81,10 @@ export class HubGoals extends BasicSerializableObject { */ this.storedShapes = {}; + if (G_IS_DEV) { + this.storedShapes[blueprintShape] = 1000; + } + /** * Stores the levels for all upgrades * @type {Object} @@ -113,6 +121,19 @@ export class HubGoals extends BasicSerializableObject { getShapesStored(definition) { return this.storedShapes[definition.getHash()] || 0; } + + /** + * @param {string} key + * @param {number} amount + */ + takeShapeByKey(key, amount) { + assert(this.getShapesStoredByKey(key) >= amount, "Can not afford: " + key + " x " + amount); + assert(amount > 0, "Amount <= 0 for " + key); + assert(Number.isInteger(amount), "Invalid amount: " + amount); + this.storedShapes[key] -= amount; + return; + } + /** * Returns how much of the current shape is stored * @param {string} key diff --git a/src/js/game/hud/parts/blueprint.js b/src/js/game/hud/parts/blueprint.js index 59271469..16d0e7da 100644 --- a/src/js/game/hud/parts/blueprint.js +++ b/src/js/game/hud/parts/blueprint.js @@ -4,6 +4,9 @@ import { createLogger } from "../../../core/logging"; import { Vector } from "../../../core/vector"; import { Entity } from "../../entity"; import { GameRoot } from "../../root"; +import { findNiceIntegerValue } from "../../../core/utils"; +import { Math_pow } from "../../../core/builtins"; +import { blueprintShape } from "../../upgrades"; const logger = createLogger("blueprint"); @@ -47,6 +50,13 @@ export class Blueprint { return new Blueprint(newEntities); } + /** + * Returns the cost of this blueprint in shapes + */ + getCost() { + return findNiceIntegerValue(4 * Math_pow(this.entities.length, 1.1)); + } + /** * Draws the blueprint at the given origin * @param {DrawParameters} parameters @@ -147,6 +157,13 @@ export class Blueprint { return anyPlaceable; } + /** + * @param {GameRoot} root + */ + canAfford(root) { + return root.hubGoals.getShapesStoredByKey(blueprintShape) >= this.getCost(); + } + /** * Attempts to place the blueprint at the given tile * @param {GameRoot} root diff --git a/src/js/game/hud/parts/blueprint_placer.js b/src/js/game/hud/parts/blueprint_placer.js index 5034b2ba..7c0244e9 100644 --- a/src/js/game/hud/parts/blueprint_placer.js +++ b/src/js/game/hud/parts/blueprint_placer.js @@ -6,9 +6,23 @@ import { enumMouseButton } from "../../camera"; import { KEYMAPPINGS } from "../../key_action_mapper"; import { BaseHUDPart } from "../base_hud_part"; import { Blueprint } from "./blueprint"; +import { makeDiv } from "../../../core/utils"; +import { DynamicDomAttach } from "../dynamic_dom_attach"; +import { blueprintShape } from "../../upgrades"; +import { T } from "../../../translations"; export class HUDBlueprintPlacer extends BaseHUDPart { - createElements(parent) {} + createElements(parent) { + const blueprintCostShape = this.root.shapeDefinitionMgr.getShapeFromShortKey(blueprintShape); + const blueprintCostShapeCanvas = blueprintCostShape.generateAsCanvas(80); + + this.costDisplayParent = makeDiv(parent, "ingame_HUD_BlueprintPlacer", [], ``); + + makeDiv(this.costDisplayParent, null, ["label"], T.ingame.blueprintPlacer.cost); + const costContainer = makeDiv(this.costDisplayParent, null, ["costContainer"], ""); + this.costDisplayText = makeDiv(costContainer, null, ["costText"], ""); + costContainer.appendChild(blueprintCostShapeCanvas); + } initialize() { this.root.hud.signals.buildingsSelectedForCopy.add(this.onBuildingsSelected, this); @@ -27,6 +41,9 @@ export class HUDBlueprintPlacer extends BaseHUDPart { this.root.camera.movePreHandler.add(this.onMouseMove, this); this.root.hud.signals.selectedPlacementBuildingChanged.add(this.abortPlacement, this); + + this.domAttach = new DynamicDomAttach(this.root, this.costDisplayParent); + this.trackedCanAfford = new TrackedState(this.onCanAffordChanged, this); } abortPlacement() { @@ -37,7 +54,25 @@ export class HUDBlueprintPlacer extends BaseHUDPart { } } - onBlueprintChanged(blueprint) {} + onCanAffordChanged(canAfford) { + this.costDisplayParent.classList.toggle("canAfford", canAfford); + } + + update() { + this.domAttach.update(this.currentBlueprint.get()); + this.trackedCanAfford.set( + this.currentBlueprint.get() && this.currentBlueprint.get().canAfford(this.root) + ); + } + + /** + * @param {Blueprint} blueprint + */ + onBlueprintChanged(blueprint) { + if (blueprint) { + this.costDisplayText.innerText = "" + blueprint.getCost(); + } + } /** * mouse down pre handler @@ -55,9 +90,17 @@ export class HUDBlueprintPlacer extends BaseHUDPart { return; } + if (!blueprint.canAfford(this.root)) { + this.root.soundProxy.playUiError(); + return; + } + const worldPos = this.root.camera.screenToWorld(pos); const tile = worldPos.toTileSpace(); if (blueprint.tryPlace(this.root, tile)) { + const cost = blueprint.getCost(); + this.root.hubGoals.takeShapeByKey(blueprintShape, cost); + // This actually feels weird // if (!this.root.keyMapper.getBinding(KEYMAPPINGS.placementModifiers.placeMultiple).isCurrentlyPressed()) { // this.currentBlueprint.set(null); diff --git a/src/js/game/hud/parts/pinned_shapes.js b/src/js/game/hud/parts/pinned_shapes.js index 1c5fa341..48ab98f2 100644 --- a/src/js/game/hud/parts/pinned_shapes.js +++ b/src/js/game/hud/parts/pinned_shapes.js @@ -77,7 +77,7 @@ export class HUDPinnedShapes extends BaseHUDPart { this.internalPinShape(currentKey, currentGoal.required, false); if (this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_blueprints)) { - this.internalPinShape(blueprintShape, currentGoal.required, false); + this.internalPinShape(blueprintShape, null, false); } for (let i = 0; i < this.pinnedShapes.length; ++i) { @@ -114,7 +114,10 @@ export class HUDPinnedShapes extends BaseHUDPart { } const amountLabel = makeDiv(element, null, ["amountLabel"], ""); - const goalLabel = makeDiv(element, null, ["goalLabel"], "/" + formatBigNumber(goal)); + + if (goal) { + makeDiv(element, null, ["goalLabel"], "/" + formatBigNumber(goal)); + } this.handles.push({ key, diff --git a/translations/base-en.yaml b/translations/base-en.yaml index f00c68ef..27abd55a 100644 --- a/translations/base-en.yaml +++ b/translations/base-en.yaml @@ -298,6 +298,10 @@ ingame: showHint: Show hint hideHint: Close + # When placing a blueprint + blueprintPlacer: + cost: Cost + # All shop upgrades shopUpgrades: belt: