diff --git a/src/data/planes.tsx b/src/data/planes.tsx index 619a418..a8ded1d 100644 --- a/src/data/planes.tsx +++ b/src/data/planes.tsx @@ -20,7 +20,7 @@ import { Direction, WithRequired, camelToTitle } from "util/common"; import { VueFeature, render, renderRow, trackHover } from "util/vue"; import { ComputedRef, Ref, computed, ref } from "vue"; import { createCollapsibleModifierSections, createFormulaPreview, estimateTime } from "./common"; -import { main, Resources, resourceNames } from "./projEntry"; +import { main, Resources, resourceNames, mineLootTable, ResourceState } from "./projEntry"; import { getColor, getName, sfc32 } from "./utils"; import ModalVue from "components/Modal.vue"; import { addTooltip } from "features/tooltips/tooltip"; @@ -185,7 +185,10 @@ export function createPlane(id: string, tier: Resources, seed: number) { break; } const treasureWeights = { - dirtGeneration: 16 + cache: 100, + generation: 10, + resourceMulti: 5, + energyMulti: 5 }; const sumTreasureWeights = Object.values(treasureWeights).reduce((a, b) => a + b); const treasureWeightsKeys = Object.keys( @@ -209,11 +212,43 @@ export function createPlane(id: string, tier: Resources, seed: number) { let update: (diff: number) => void; let onComplete: VoidFunction; let link: ComputedRef; + let randomResource: Resources; + let effectedResource: Resources | "energy"; + let resourceMulti: DecimalSource; switch (treasureType) { - case "dirtGeneration": - description = `Gain ${format(difficulty)} dirt/s while plane is active`; - update = diff => main.grantResource("dirt", Decimal.times(diff, difficulty)); - link = computed(() => main.resourceNodes.value["dirt"]); + case "cache": + randomResource = getRandomResource(random); + description = `Gain ${format(difficulty)}x your current ${randomResource}.`; + onComplete = () => + main.grantResource( + randomResource, + Decimal.times( + ( + main.resourceNodes.value[randomResource] + ?.state as unknown as ResourceState | null + )?.amount ?? 0, + difficulty + ) + ); + break; + case "generation": + randomResource = getRandomResource(random); + const gain = Decimal.div(difficulty, 120).times(mineLootTable[randomResource]); + description = `Gain ${format(gain)} ${randomResource}/s while plane is active.`; + update = diff => main.grantResource(randomResource, Decimal.times(diff, gain)); + link = computed(() => main.resourceNodes.value[randomResource]); + break; + case "resourceMulti": + effectedResource = randomResource = getRandomResource(random); + resourceMulti = Decimal.div(difficulty, 17).pow_base(2); + description = `Gain ${format( + resourceMulti + )}x ${randomResource} while plane is active.`; + break; + case "energyMulti": + effectedResource = "energy"; + resourceMulti = Decimal.div(difficulty, 17); + description = `Gain ${format(resourceMulti)}x energy while plane is active.`; break; } const cost = Decimal.times(difficulty, random() + 0.5) @@ -237,7 +272,9 @@ export function createPlane(id: string, tier: Resources, seed: number) { }, update, onComplete, - link + link, + effectedResource, + resourceMulti })) as GenericAchievement; features.push([milestone]); visibility = milestone.earned; @@ -317,6 +354,32 @@ export function createPlane(id: string, tier: Resources, seed: number) { return links; }); + const resourceMultis = computed(() => { + const multis: Partial> = {}; + for (let i = 1; i < features.length; i += 2) { + const treasure = features[i][0] as GenericAchievement & { + effectedResource?: Resources | "energy"; + resourceMulti: DecimalSource; + }; + if ( + treasure.earned.value && + treasure.effectedResource != null && + treasure.resourceMulti != null + ) { + if (multis[treasure.effectedResource] != null) { + multis[treasure.effectedResource] = Decimal.times( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + multis[treasure.effectedResource]!, + treasure.resourceMulti + ); + } else { + multis[treasure.effectedResource] = treasure.resourceMulti; + } + } + } + return multis; + }); + return { tier: persistent(tier), seed: persistent(seed), @@ -331,6 +394,7 @@ export function createPlane(id: string, tier: Resources, seed: number) { features, resourceTabCollapsed, links, + resourceMultis, display: jsx(() => ( <> @@ -375,4 +439,23 @@ export function createPlane(id: string, tier: Resources, seed: number) { }); } +// Using separate method from what's used in mining, because planes are influenced by influences and not things like dowsing +function getRandomResource(random: () => number) { + const sumResourceWeights = (Object.values(mineLootTable) as number[]).reduce((a, b) => a + b); + const resourceWeightsKeys = Object.keys(mineLootTable) as Resources[]; + const r = Math.floor(random() * sumResourceWeights); + let weight = 0; + let resource: Resources; + for (let i = 0; i < resourceWeightsKeys.length; i++) { + const type = resourceWeightsKeys[i]; + weight += mineLootTable[type]; + if (r < weight) { + resource = type; + break; + } + } + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return resource!; +} + export type GenericPlane = ReturnType; diff --git a/src/data/projEntry.tsx b/src/data/projEntry.tsx index eea6641..a83628d 100644 --- a/src/data/projEntry.tsx +++ b/src/data/projEntry.tsx @@ -74,7 +74,7 @@ export interface PortalState { powered: boolean; } -const mineLootTable = { +export const mineLootTable = { dirt: 120, sand: 60, gravel: 40, @@ -202,7 +202,7 @@ const passives = { }, gravel: { description: (empowered: boolean) => - empowered ? "Quadruples material drops" : "Doubles material drops" + empowered ? "Quadruples mine ore drops" : "Doubles mine ore drops" }, stone: { description: (empowered: boolean) => @@ -715,10 +715,14 @@ export const main = createLayer("main", function (this: BaseLayer) { progressColor: "var(--accent3)", classes: node => ({ "affected-node": - dowsing.value != null && - isPowered(dowsing.value) && - (dowsing.value.state as unknown as DowsingState).resources.includes( - (node.state as unknown as ResourceState).type + (dowsing.value != null && + isPowered(dowsing.value) && + (dowsing.value.state as unknown as DowsingState).resources.includes( + (node.state as unknown as ResourceState).type + )) || + Decimal.neq( + planarMultis.value[(node.state as unknown as ResourceState).type] ?? 1, + 1 ) }), draggable: true @@ -1107,20 +1111,34 @@ export const main = createLayer("main", function (this: BaseLayer) { }); // TODO link to influences } - links.push( - ...activePortals.value - .map(node => - ( - layers[(node.state as unknown as PortalState).id] as GenericPlane - ).links.value.map(n => ({ + (board as GenericBoard).types.portal.nodes.value.forEach(node => { + const plane = layers[(node.state as unknown as PortalState).id] as GenericPlane; + plane.links.value.forEach(n => { + if (n.value != null) { + links.push({ startNode: node, endNode: n.value, - stroke: "var(--accent3)", + stroke: isPowered(node) ? "var(--accent3)" : "var(--foreground)", strokeWidth: 4 - })) - ) - .reduce((a, b) => [...a, ...b], []) - ); + }); + } + }); + (Object.keys(plane.resourceMultis.value) as (Resources | "energy")[]).forEach( + type => { + if (type !== "energy" && type in resourceNodes.value) { + links.push({ + startNode: node, + endNode: resourceNodes.value[type], + stroke: isPowered(node) + ? "var(--accent1)" + : "var(--foreground)", + strokeWidth: 4 + }); + } + } + ); + return links; + }); } return links; } @@ -1143,6 +1161,7 @@ export const main = createLayer("main", function (this: BaseLayer) { function grantResource(type: Resources, amount: DecimalSource) { let node = resourceNodes.value[type]; + amount = Decimal.times(amount, resourceGain[type].computedModifier.value); if (node == null) { node = { id: getUniqueNodeID(board), @@ -1169,6 +1188,26 @@ export const main = createLayer("main", function (this: BaseLayer) { ) ); + const planarMultis = computed(() => { + const multis: Partial> = {}; + board.types.portal.nodes.value.forEach(n => { + if (!isPowered(n)) { + return; + } + const plane = layers[(n.state as unknown as PortalState).id] as GenericPlane; + const planeMultis = plane.resourceMultis.value; + (Object.keys(planeMultis) as (Resources | "energy")[]).forEach(type => { + if (multis[type] != null) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + multis[type] = Decimal.times(multis[type]!, planeMultis[type]!); + } else { + multis[type] = planeMultis[type]; + } + }); + }); + return multis; + }); + const energyModifier = createSequentialModifier(() => [ ...resourceNames.map(resource => createMultiplicativeModifier(() => ({ @@ -1205,6 +1244,11 @@ export const main = createLayer("main", function (this: BaseLayer) { : "") + tools.stone.name, enabled: () => toolNodes.value["stone"] != null })), + createMultiplicativeModifier(() => ({ + multiplier: () => planarMultis.value.energy ?? 1, + description: "Planar Treasures", + enabled: () => Decimal.neq(planarMultis.value.energy ?? 1, 1) + })), createAdditiveModifier(() => ({ addend: () => Decimal.pow(100, poweredMachines.value).div(10).neg(), description: "Powered Machines (100^n/10 energy/s)", @@ -1293,6 +1337,23 @@ export const main = createLayer("main", function (this: BaseLayer) { return acc; }, {} as Record; computedModifier: ComputedRef; section: Section }>); + const resourceGain = (Object.keys(mineLootTable) as Resources[]).reduce((acc, resource) => { + const modifier = createSequentialModifier(() => [ + createMultiplicativeModifier(() => ({ + multiplier: () => planarMultis.value[resource] ?? 1, + description: "Planar Treasures", + enabled: () => Decimal.neq(planarMultis.value[resource] ?? 1, 1) + })) + ]); + const computedModifier = computed(() => modifier.apply(1)); + const section = { + title: `${camelToTitle(resource)} Gain`, + modifier + }; + acc[resource] = { modifier, computedModifier, section }; + return acc; + }, {} as Record; computedModifier: ComputedRef; section: Section }>); + const [energyTab, energyTabCollapsed] = createCollapsibleModifierSections(() => [ { title: "Energy Gain", @@ -1325,6 +1386,9 @@ export const main = createLayer("main", function (this: BaseLayer) { const [resourcesTab, resourcesCollapsed] = createCollapsibleModifierSections(() => Object.values(dropRates).map(d => d.section) ); + const [resourceGainTab, resourceGainCollapsed] = createCollapsibleModifierSections(() => + Object.values(resourceGain).map(d => d.section) + ); const modifierTabs = createTabFamily({ general: () => ({ display: "Energy", @@ -1335,7 +1399,7 @@ export const main = createLayer("main", function (this: BaseLayer) { energyTabCollapsed }), mining: () => ({ - display: "Mining", + display: "Mine", glowColor(): string { return modifierTabs.activeTab.value === this.tab ? "white" : ""; }, @@ -1344,13 +1408,23 @@ export const main = createLayer("main", function (this: BaseLayer) { miningTabCollapsed }), resources: () => ({ - display: "Resources", + display: "Mine Rates", glowColor(): string { return modifierTabs.activeTab.value === this.tab ? "white" : ""; }, visibility: () => dowsing.value != null, tab: resourcesTab, resourcesCollapsed + }), + resourcesGain: () => ({ + display: "Resource Gain", + glowColor(): string { + return modifierTabs.activeTab.value === this.tab ? "white" : ""; + }, + visibility: () => + Object.values(resourceGain).some(r => Decimal.neq(r.computedModifier.value, 1)), + tab: resourceGainTab, + resourceGainCollapsed }) }); const showModifiersModal = ref(false);