Implement energy gain and resource displays

This commit is contained in:
thepaperpilot 2023-04-21 23:47:39 -05:00
parent 6ecbe3ecd0
commit e44a43aad5
4 changed files with 99 additions and 12 deletions

4
src/data/main.css Normal file
View file

@ -0,0 +1,4 @@
.tooltip-inline-container,
.tooltip-inline-container > .tooltip-container {
display: inline;
}

View file

@ -1,3 +1,4 @@
import StickyVue from "components/layout/Sticky.vue";
import {
BoardNode,
ProgressDisplay,
@ -7,17 +8,25 @@ import {
} from "features/boards/board";
import { jsx } from "features/feature";
import MainDisplay from "features/resources/MainDisplay.vue";
import { createResource } from "features/resources/resource";
import { createResource, displayResource } from "features/resources/resource";
import TooltipVue from "features/tooltips/Tooltip.vue";
import Formula, { calculateCost } from "game/formulas/formulas";
import type { BaseLayer, GenericLayer } from "game/layers";
import { createLayer } from "game/layers";
import {
createModifierSection,
createMultiplicativeModifier,
createSequentialModifier
} from "game/modifiers";
import { State } from "game/persistence";
import type { Player } from "game/player";
import player from "game/player";
import Decimal, { DecimalSource } from "lib/break_eternity";
import { format, formatTime } from "util/bignum";
import { format, formatTime, formatWhole } from "util/bignum";
import { Direction } from "util/common";
import { render } from "util/vue";
import { ComputedRef, computed, reactive } from "vue";
import "./main.css";
export interface ResourceState {
type: Resources;
@ -52,13 +61,21 @@ export const main = createLayer("main", function (this: BaseLayer) {
const energy = createResource<DecimalSource>(0, "energy");
const resourceLevelFormula = Formula.variable(0).add(1);
function getResourceLevel(amount: DecimalSource) {
const currentLevel = Decimal.floor(
resourceLevelFormula.invertIntegral(
Decimal.add(amount, resourceLevelFormula.evaluateIntegral())
)
);
// TODO sum last x purchases?
function getResourceLevelProgress(amount: DecimalSource) {
// Sub 10 and then manually sum until we go over amount
let currentLevel = Decimal.floor(resourceLevelFormula.invertIntegral(amount))
.sub(10)
.clampMin(0);
let summedCost = calculateCost(resourceLevelFormula, currentLevel, true, 0);
while (true) {
const nextCost = resourceLevelFormula.evaluate(currentLevel);
if (Decimal.add(summedCost, nextCost).lte(amount)) {
currentLevel = currentLevel.add(1);
summedCost = Decimal.add(summedCost, nextCost);
} else {
break;
}
}
const requiredForCurrentLevel = calculateCost(resourceLevelFormula, currentLevel, true);
const requiredForNextLevel = calculateCost(
resourceLevelFormula,
@ -66,6 +83,7 @@ export const main = createLayer("main", function (this: BaseLayer) {
true
);
return Decimal.sub(amount, requiredForCurrentLevel)
.max(0)
.div(Decimal.sub(requiredForNextLevel, requiredForCurrentLevel))
.toNumber();
}
@ -91,7 +109,9 @@ export const main = createLayer("main", function (this: BaseLayer) {
shape: Shape.Circle,
size: 50,
title: node => (node.state as unknown as ResourceState).type,
progress: node => getResourceLevel((node.state as unknown as ResourceState).amount),
subtitle: node => formatWhole((node.state as unknown as ResourceState).amount),
progress: node =>
getResourceLevelProgress((node.state as unknown as ResourceState).amount),
progressDisplay: ProgressDisplay.Outline,
progressColor: "var(--accent3)",
draggable: true
@ -152,6 +172,38 @@ export const main = createLayer("main", function (this: BaseLayer) {
const sumMineWeights = (Object.values(mineLootTable) as number[]).reduce((a, b) => a + b);
const resourceNames = Object.keys(mineLootTable) as Resources[];
const energyModifier = createSequentialModifier(() =>
resourceNames.map(resource =>
createMultiplicativeModifier(() => ({
description: resource,
multiplier: () =>
Decimal.pow(
1.01,
resourceLevelFormula.invertIntegral(
(resourceNodes.value[resource]?.state as ResourceState | undefined)
?.amount ?? 0
)
),
enabled: () =>
resource in resourceNodes.value &&
Decimal.gt(
(resourceNodes.value[resource].state as ResourceState | undefined)
?.amount ?? 0,
0
)
}))
)
);
const computedEnergyModifier = computed(() => energyModifier.apply(1));
const energyGainTooltip = jsx(() =>
createModifierSection({
title: "Energy Gain",
modifier: energyModifier,
base: 1,
unit: "/s"
})
);
this.on("preUpdate", diff => {
Object.keys(resourceMinedCooldown).forEach(resource => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
@ -196,7 +248,7 @@ export const main = createLayer("main", function (this: BaseLayer) {
}
}
// TODO increment energy based on its modifier
energy.value = Decimal.add(energy.value, Decimal.times(computedEnergyModifier.value, diff));
});
return {
@ -213,6 +265,21 @@ export const main = createLayer("main", function (this: BaseLayer) {
<div>Offline Time: {formatTime(player.offlineTime)}</div>
) : null}
<MainDisplay resource={energy} />
<StickyVue style="margin-top: -20px">
You are gaining{" "}
<span class="tooltip-inline-container">
<TooltipVue
display={energyGainTooltip}
direction={Direction.Down}
style="width: 200px; text-align: left"
>
<h3 style="color: white; text-shadow: 0px 0px 10px white;">
{displayResource(energy, computedEnergyModifier.value)}
</h3>
</TooltipVue>
</span>{" "}
energy/sec{" "}
</StickyVue>
{render(board)}
</>
))

View file

@ -108,7 +108,10 @@
/>
</g>
<text :fill="titleColor" class="node-title">{{ title }}</text>
<text :fill="titleColor" class="node-title" :y="subtitle ? 10 : 0">{{ title }}</text>
<text v-if="subtitle" :fill="titleColor" class="node-subtitle" y="-15">{{
subtitle
}}</text>
</g>
<transition name="fade" appear>
@ -204,6 +207,7 @@ const position = computed(() => {
const shape = computed(() => getNodeProperty(props.nodeType.value.shape, unref(props.node)));
const title = computed(() => getNodeProperty(props.nodeType.value.title, unref(props.node)));
const subtitle = computed(() => getNodeProperty(props.nodeType.value.subtitle, unref(props.node)));
const label = computed(() => getNodeProperty(props.nodeType.value.label, unref(props.node)));
const size = computed(() => getNodeProperty(props.nodeType.value.size, unref(props.node)));
const progress = computed(
@ -280,6 +284,14 @@ function mouseUp(e: MouseEvent | TouchEvent) {
pointer-events: none;
}
.node-subtitle {
text-anchor: middle;
dominant-baseline: middle;
font-family: monospace;
font-size: 150%;
pointer-events: none;
}
.progress {
transition-duration: 0.05s;
}

View file

@ -88,6 +88,8 @@ export type BoardData = {
export interface NodeTypeOptions {
/** The title to display for the node. */
title: NodeComputable<string>;
/** The subtitle to display for the node. */
subtitle?: NodeComputable<string>;
/** An optional label for the node. */
label?: NodeComputable<NodeLabel | null>;
/** The size of the node - diameter for circles, width and height for squares. */
@ -135,6 +137,7 @@ export type NodeType<T extends NodeTypeOptions> = Replace<
T & BaseNodeType,
{
title: GetComputableType<T["title"]>;
subtitle: GetComputableType<T["subtitle"]>;
label: GetComputableType<T["label"]>;
size: GetComputableTypeWithDefault<T["size"], 50>;
draggable: GetComputableTypeWithDefault<T["draggable"], false>;
@ -375,6 +378,7 @@ export function createBoard<T extends BoardOptions>(
const nodeType: NodeTypeOptions & Partial<BaseNodeType> = board.types[type];
processComputable(nodeType as NodeTypeOptions, "title");
processComputable(nodeType as NodeTypeOptions, "subtitle");
processComputable(nodeType as NodeTypeOptions, "label");
processComputable(nodeType as NodeTypeOptions, "size");
setDefault(nodeType, "size", 50);