diff --git a/src/data/planes.tsx b/src/data/planes.tsx index 5dc40b6..ca62ab1 100644 --- a/src/data/planes.tsx +++ b/src/data/planes.tsx @@ -29,6 +29,7 @@ import { VueFeature, render, renderRow, trackHover } from "util/vue"; import { ComputedRef, Ref, computed, ref, unref } from "vue"; import { createCollapsibleModifierSections, createFormulaPreview, estimateTime } from "./common"; import { + BoosterState, InfluenceState, Influences, influences as influenceTypes, @@ -45,7 +46,7 @@ import TooltipVue from "features/tooltips/Tooltip.vue"; const toast = useToast(); export type Treasure = GenericAchievement & { - update?: (diff: number) => void; + update?: (diff: DecimalSource) => void; link?: ComputedRef; effectedResource?: Resources | "energy"; resourceMulti: DecimalSource; @@ -390,7 +391,7 @@ export function createPlane( treasureType = "relic"; } let description = ""; - let update: (diff: number) => void; + let update: (diff: DecimalSource) => void; let onComplete: VoidFunction; let link: ComputedRef; let randomResource: Resources; @@ -526,12 +527,40 @@ export function createPlane( RepeatableType ) as GenericRepeatable[]; + const planarSpeedModifier = createSequentialModifier(() => [ + createMultiplicativeModifier(() => ({ + multiplier: () => + Decimal.add( + ( + main.board.types.booster.nodes.value[0]?.state as unknown as + | BoosterState + | undefined + )?.level ?? 0, + 1 + ), + description: "Booster", + enabled: () => + ( + main.board.types.booster.nodes.value[0]?.state as unknown as + | BoosterState + | undefined + )?.portals.includes(id) ?? false + })) + ]); + const computedPlanarSpeedModifier = computed(() => planarSpeedModifier.apply(1)); + const [resourceTab, resourceTabCollapsed] = createCollapsibleModifierSections(() => [ { title: `${camelToTitle(resource.displayName)} Gain`, modifier: resourceGainModifier, base: 0, unit: "/s" + }, + { + title: `${camelToTitle(resource.displayName)} Time Speed`, + modifier: planarSpeedModifier, + base: 1, + visible: () => Decimal.gt(computedPlanarSpeedModifier.value, 1) } ]); const showModifiersModal = ref(false); @@ -552,12 +581,15 @@ export function createPlane( ) { return; } + const totalDiff = Decimal.times(computedPlanarSpeedModifier.value, diff); - timeActive.value = Decimal.add(timeActive.value, diff); - resource.value = Decimal.times(computedResourceGain.value, diff).add(resource.value); + timeActive.value = Decimal.add(timeActive.value, totalDiff); + resource.value = Decimal.times(computedResourceGain.value, totalDiff).add( + resource.value + ); earnedTreasures.value.forEach(treasure => { - treasure.update?.(diff); + treasure.update?.(totalDiff); }); }); diff --git a/src/data/projEntry.tsx b/src/data/projEntry.tsx index 6099c5c..ef635f8 100644 --- a/src/data/projEntry.tsx +++ b/src/data/projEntry.tsx @@ -79,6 +79,13 @@ export interface InfluenceState { data: State; } +export interface BoosterState { + portals: string[]; + maxConnections: number; + powered: boolean; + level: DecimalSource; +} + export const mineLootTable = { dirt: 120, sand: 60, @@ -160,7 +167,7 @@ const tools = { cost: 1e15, name: "Booster", type: "booster", - state: { planes: [], maxConnections: 1, powered: false } + state: { planes: [], maxConnections: 1, powered: false, level: 1 } }, emerald: { cost: 1e19, @@ -195,7 +202,8 @@ const tools = { ultimatum: { cost: 1e54, name: "Investments", - type: "investments" + type: "investments", + state: { planes: [], maxConnections: 1, powered: false } } } as const satisfies Record< Resources, @@ -443,6 +451,8 @@ export const influences = { >; export type Influences = keyof typeof influences; +const increaseBoostFormula = Formula.variable(0).add(8).times(2).pow10(); + /** * @hidden */ @@ -466,7 +476,8 @@ export const main = createLayer("main", function (this: BaseLayer) { sand: board.types.dowsing.nodes.value[0], wood: board.types.quarry.nodes.value[0], coal: board.types.empowerer.nodes.value[0], - iron: board.types.portalGenerator.nodes.value[0] + iron: board.types.portalGenerator.nodes.value[0], + gold: board.types.booster.nodes.value[0] })); const influenceNodes: ComputedRef> = computed(() => ({ @@ -727,6 +738,33 @@ export const main = createLayer("main", function (this: BaseLayer) { return null; } + function labelForAcceptingPortal( + node: BoardNode, + description: (portal: string) => string + ): NodeLabel | null { + if ((board as GenericBoard).draggingNode.value?.type === "portal") { + const portal = ( + (board as GenericBoard).draggingNode.value?.state as unknown as PortalState + ).id; + const { maxConnections, portals } = node.state as unknown as BoosterState; + if (portals.includes(portal)) { + return { text: "Disconnect", color: "var(--accent2)" }; + } + if ( + Decimal.add(maxConnections, computedBonusConnectionsModifier.value).lte( + portals.length + ) + ) { + return { text: "Max connections", color: "var(--danger)" }; + } + return { + text: description(portal), + color: "var(--accent2)" + }; + } + return null; + } + function canAcceptResource(node: BoardNode, otherNode: BoardNode) { if (otherNode.type !== "resource") { return false; @@ -801,6 +839,43 @@ export const main = createLayer("main", function (this: BaseLayer) { board.selectedNode.value = node; } + function canAcceptPortal(node: BoardNode, otherNode: BoardNode) { + if (otherNode.type !== "portal") { + return false; + } + const portal = (otherNode.state as unknown as PortalState).id; + const { maxConnections, portals } = node.state as unknown as BoosterState; + if (portals.includes(portal)) { + return true; + } + if ( + Decimal.add(maxConnections, computedBonusConnectionsModifier.value).lte(portals.length) + ) { + return false; + } + return true; + } + + function onDropPortal(node: BoardNode, otherNode: BoardNode) { + if (otherNode.type !== "portal") { + return; + } + const portal = (otherNode.state as unknown as PortalState).id; + const { portals } = node.state as unknown as BoosterState; + if (portals.includes(portal)) { + node.state = { + ...(node.state as object), + tools: portals.filter(r => r !== portal) + }; + } else { + node.state = { + ...(node.state as object), + tools: [...portals, portal] + }; + } + board.selectedNode.value = node; + } + const board = createBoard(board => ({ startNodes: () => [ { position: { x: 0, y: 0 }, type: "mine", state: { progress: 0, powered: false } }, @@ -1368,6 +1443,95 @@ export const main = createLayer("main", function (this: BaseLayer) { }, outlineColor: "var(--danger)", draggable: true + }, + booster: { + shape: Shape.Diamond, + size: 50, + title: "⌛", + label: node => { + if (node === board.selectedNode.value) { + return { + text: + (node.state as unknown as BoosterState).portals.length === 0 + ? "Booster - Drag a portal to me!" + : `Boosting by ${formatWhole( + Decimal.add( + 1, + (node.state as unknown as BoosterState).level + ) + )}x (${ + (node.state as { tools: Passives[] }).tools.length + }/${Decimal.add( + (node.state as { maxConnections: number }).maxConnections, + computedBonusConnectionsModifier.value + )})` + }; + } + return labelForAcceptingPortal(node, portal => { + return `Boost ${(layers[portal] as GenericPlane).name}'s speed`; + }); + }, + actionDistance: Math.PI / 4, + actions: [ + { + id: "deselect", + icon: "close", + tooltip: { + text: "Disconnect portals" + }, + onClick(node: BoardNode) { + node.state = { ...(node.state as object), portals: [] }; + board.selectedAction.value = null; + board.selectedNode.value = null; + }, + visibility: (node: BoardNode) => + (node.state as unknown as BoosterState)?.portals.length ?? 0 > 0 + }, + getIncreaseConnectionsAction(x => x.add(6).pow_base(1000)), + { + id: "increaseBoost", + icon: "arrow_upward", + tooltip(node: BoardNode) { + return { + text: `Increase boost - ${formatWhole( + increaseBoostFormula.evaluate( + (node.state as unknown as BoosterState).level + ) + )} energy` + }; + }, + confirmationLabel(node: BoardNode) { + return Decimal.gte( + energy.value, + increaseBoostFormula.evaluate( + (node.state as unknown as BoosterState).level + ) + ) + ? { text: "Tap again to confirm" } + : { text: "Cannot afford", color: "var(--danger)" }; + }, + onClick(node: BoardNode) { + const cost = increaseBoostFormula.evaluate( + (node.state as unknown as BoosterState).level + ); + if (Decimal.gte(energy.value, cost)) { + energy.value = Decimal.sub(energy.value, cost); + } + node.state = { + ...(node.state as object), + level: Decimal.add((node.state as unknown as BoosterState).level, 1) + }; + board.selectedAction.value = null; + } + }, + togglePoweredAction + ], + canAccept: canAcceptPortal, + onDrop: onDropPortal, + classes: node => ({ + running: isPowered(node) + }), + draggable: true } }, style: { @@ -1491,6 +1655,21 @@ export const main = createLayer("main", function (this: BaseLayer) { return links; }); } + if (booster.value != null) { + (booster.value.state as unknown as BoosterState).portals.forEach(portal => { + links.push({ + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + startNode: booster.value!, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + endNode: (board as GenericBoard).types.portal.nodes.value.find( + node => (node.state as unknown as PortalState).id === portal + )!, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + stroke: isPowered(booster.value!) ? "var(--accent1)" : "var(--foreground)", + strokeWidth: 4 + }); + }); + } Object.values(influenceNodes.value).forEach(node => { const state = node.state as unknown as InfluenceState; if (state.type === "increaseResources" || state.type === "decreaseResources") { @@ -1522,7 +1701,8 @@ export const main = createLayer("main", function (this: BaseLayer) { const portalGenerator: ComputedRef = computed( () => toolNodes.value.iron ); - const poweredMachines = [mine, dowsing, quarry, empowerer]; + const booster: ComputedRef = computed(() => toolNodes.value.gold); + const poweredMachines = [mine, dowsing, quarry, empowerer, booster]; function grantResource(type: Resources, amount: DecimalSource) { let node = resourceNodes.value[type]; @@ -1981,6 +2161,24 @@ export const main = createLayer("main", function (this: BaseLayer) { }; } } + if (booster.value) { + const maxConnections = (booster.value.state as unknown as BoosterState) + .maxConnections; + if ( + Decimal.lt( + (booster.value.state as unknown as BoosterState).portals.length, + Decimal.add(maxConnections, curr) + ) + ) { + booster.value.state = { + ...(booster.value.state as object), + resources: (booster.value.state as unknown as BoosterState).portals.slice( + 0, + Decimal.add(maxConnections, curr).toNumber() + ) + }; + } + } } });