From 36eb1474af2e56801fee340e98277a8244311138 Mon Sep 17 00:00:00 2001 From: Seth Posner Date: Tue, 6 Dec 2022 11:02:34 -0800 Subject: [PATCH] Add metal layer --- src/data/layers/coal.tsx | 100 +++++++ src/data/layers/metal.tsx | 395 +++++++++++++++++++++++++ src/data/projEntry.tsx | 42 +-- src/data/symbols/metal.png | Bin 0 -> 7036 bytes src/features/resources/MainDisplay.vue | 26 +- 5 files changed, 540 insertions(+), 23 deletions(-) create mode 100644 src/data/layers/metal.tsx create mode 100644 src/data/symbols/metal.png diff --git a/src/data/layers/coal.tsx b/src/data/layers/coal.tsx index 8a5c5d3..16e06ac 100644 --- a/src/data/layers/coal.tsx +++ b/src/data/layers/coal.tsx @@ -30,6 +30,7 @@ import { createUpgrade, Upgrade } from "features/upgrades/upgrade"; import elves from "./elves"; import paper from "./paper"; import boxes from "./boxes"; +import metal from "./metal"; interface BetterFertilizerUpgOptions { canAfford: () => boolean; @@ -294,6 +295,80 @@ const layer = createLayer(id, function (this: BaseLayer) { } })); + const activeDrills = persistent(0); + const drillCoal = computed(() => Decimal.times(activeDrills.value, 5e7)); + const buildDrill = createBuyable(() => ({ + resource: metal.metal, + cost() { + let v = this.amount.value; + if (Decimal.gte(v, 100)) v = Decimal.pow(v, 2).div(100); + if (Decimal.gte(v, 10000)) v = Decimal.pow(v, 2).div(10000); + // v = Decimal.pow(0.95, paper.books.drillBook.amount.value).times(v); + return Decimal.pow(v, 1.15).plus(10); + }, + display: jsx(() => ( + <> +

Mining Drill

+
+ Dig through the ground to find 50,000,000 coal +
+
+ Currently: +
+{format(drillCoal.value)} coal/sec +
+
+ Cost: {formatWhole(unref(buildDrill.cost!))} {buildDrill.resource.displayName} + + )), + onPurchase() { + activeDrills.value = Decimal.add(activeDrills.value, 1); + }, + style: { + color: colorText, + width: "160px" + } + })) as GenericBuyable & { resource: Resource }; + const minDrill = createClickable(() => ({ + display: "0", + style: { minHeight: "20px", width: "40px", color: colorText }, + canClick() { + return Decimal.gt(activeDrills.value, 0); + }, + onClick() { + activeDrills.value = 0; + } + })); + const removeDrill = createClickable(() => ({ + display: "-", + style: { minHeight: "20px", width: "40px", color: colorText }, + canClick() { + return Decimal.gt(activeDrills.value, 0); + }, + onClick() { + activeDrills.value = Decimal.sub(activeDrills.value, 1); + } + })); + const addDrill = createClickable(() => ({ + display: "+", + style: { minHeight: "20px", width: "40px", color: colorText }, + canClick() { + return Decimal.lt(activeDrills.value, buildDrill.amount.value); + }, + onClick() { + activeDrills.value = Decimal.add(activeDrills.value, 1); + } + })); + const maxDrill = createClickable(() => ({ + display: "Max", + style: { minHeight: "20px", width: "40px", color: colorText }, + canClick() { + return Decimal.lt(activeDrills.value, buildDrill.amount.value); + }, + onClick() { + activeDrills.value = buildDrill.amount.value; + } + })); + const warmerCutters = createUpgrade(() => ({ resource: noPersist(coal), cost: 5, @@ -535,6 +610,15 @@ const layer = createLayer(id, function (this: BaseLayer) { return Decimal.gt(activeKilns.value, 0); } })), + createAdditiveModifier(() => ({ + addend() { + return drillCoal.value; + }, + description: "Mining Drills", + enabled() { + return Decimal.gt(activeDrills.value, 0); + } + })), createMultiplicativeModifier(() => ({ multiplier: 2, description: "Carry coal in boxes", @@ -736,6 +820,7 @@ const layer = createLayer(id, function (this: BaseLayer) { color: colorCoal, coal, totalCoal, + computedCoalGain, ash, activeFires, buildFire, @@ -743,6 +828,8 @@ const layer = createLayer(id, function (this: BaseLayer) { buildBonfire, activeKilns, buildKiln, + activeDrills, + buildDrill, warmerCutters, warmerPlanters, basicFertilizer, @@ -820,6 +907,19 @@ const layer = createLayer(id, function (this: BaseLayer) { ) : undefined} + {metal.coalDrill.bought.value ? ( + <> + + + {render(buildDrill)} +
+ {formatWhole(activeDrills.value)}/ + {formatWhole(buildDrill.amount.value)} +
+ {renderRow(minDrill, removeDrill, addDrill, maxDrill)} +
+ + ) : undefined} {renderRow(...row1upgrades)} diff --git a/src/data/layers/metal.tsx b/src/data/layers/metal.tsx new file mode 100644 index 0000000..dcb7c02 --- /dev/null +++ b/src/data/layers/metal.tsx @@ -0,0 +1,395 @@ +import Spacer from "components/layout/Spacer.vue"; +import MainDisplay from "features/resources/MainDisplay.vue"; +import Toggle from "components/fields/Toggle.vue" +import Modal from "components/Modal.vue"; +import { createCollapsibleModifierSections, setUpDailyProgressTracker } from "data/common"; +import { jsx, showIf } from "features/feature"; +import { createResource, trackBest } from "features/resources/resource"; +import { BaseLayer, createLayer } from "game/layers"; +import Decimal, { DecimalSource } from "lib/break_eternity"; +import { render, renderRow } from "util/vue"; +import { persistent } from "game/persistence"; +import { globalBus } from "game/events"; +import { createAdditiveModifier, createMultiplicativeModifier, createSequentialModifier } from "game/modifiers"; +import { computed, ref, unref } from "vue"; +import { createBar } from "features/bars/bar"; +import { Direction } from "util/common"; +import { format, formatWhole } from "util/break_eternity"; +import { createClickable } from "features/clickables/clickable"; +import coal from "./coal" +import { createUpgrade, GenericUpgrade } from "features/upgrades/upgrade"; +import { noPersist } from "game/persistence" +import { createBuyable, GenericBuyable } from "features/buyable"; + +const id = "metal"; +const day = 7; +const layer = createLayer(id, function (this: BaseLayer) { + const name = "Metal"; + const color = "#888B8D"; + + const metal = createResource(0, "metal ingots", undefined, true); + const bestMetal = trackBest(metal); + + const ore = createResource(0, "ore"); + const bestOre = trackBest(ore); + + const orePurity = createSequentialModifier(() => [ + createMultiplicativeModifier(() => ({ + multiplier: 5, + description: "Crucible", + enabled: crucible.bought + })), + createMultiplicativeModifier(() => ({ + multiplier: 2, + description: "Industrial Furnace", + enabled: industrialFurnace.bought + })), + createMultiplicativeModifier(() => ({ + multiplier: () => Decimal.add(hotterForgeEffect.value, 1), + description: "Hotter Forges", + enabled: () => Decimal.gte(hotterForge.amount.value, 1) + })) + ]); + const computedOrePurity = computed(() => orePurity.apply(0.1)); + + const autoSmeltSpeed = createSequentialModifier(() => [ + createAdditiveModifier(() => ({ + addend: () => Decimal.times(industrialCrucible.amount.value, 10), + description: "Industrial Crucibles", + enabled: () => Decimal.gte(industrialCrucible.amount.value, 1) + })) + ]); + const computedAutoSmeltSpeed = computed(() => autoSmeltSpeed.apply(0)); + + const coalCost = 1e10; + const smeltableOre = computed(() => + Decimal.min( + ore.value, + Decimal.div(coal.coal.value, coalCost) + ).floor().max(0) + ); + + const smeltOreButton = createClickable(() => ({ + display: jsx(() => { + const cost = Decimal.gte(smeltableOre.value, 1) + ? smeltableOre.value + : Decimal.add(smeltableOre.value, 1); + return ( + <> + + Smelt {format(Decimal.times(smeltableOre.value, computedOrePurity.value))} {metal.displayName} +
+ + Cost: {formatWhole(cost)} {ore.displayName}; {formatWhole(Decimal.times(cost, coalCost))} {coal.coal.displayName} + + + ) + }), + canClick: () => Decimal.gte(smeltableOre.value, 1), + onClick() { + if (!unref(this.canClick)) return; + + smeltOre(smeltableOre.value); + }, + style: { + width: "600px", + minHeight: "unset" + } + })); + function smeltOre(amount: DecimalSource) { + let [metalGain, oreConsumption, coalConsumption] = [ + Decimal.times(amount, computedOrePurity.value), + amount, + Decimal.times(amount, coalCost) + ]; + metal.value = Decimal.add(metal.value, metalGain); + ore.value = Decimal.sub(ore.value, oreConsumption); + coal.coal.value = Decimal.sub(coal.coal.value, coalConsumption); + } + + const oreAmount = createSequentialModifier(() => [ + createAdditiveModifier(() => ({ + addend: () => oreDrill.amount.value, + description: "Mining Drills", + enabled: () => Decimal.gte(oreDrill.amount.value, 1) + })) + ]); + const computedOreAmount = computed(() => oreAmount.apply(1)); + const oreSpeed = createSequentialModifier(() => [ + createMultiplicativeModifier(() => ({ + multiplier: 2, + description: "A Simple Pickaxe", + enabled: simplePickaxe.bought + })), + createMultiplicativeModifier(() => ({ + multiplier: 2, + description: "Double Pickaxe", + enabled: doublePickaxe.bought + })), + createMultiplicativeModifier(() => ({ + multiplier: 2.5, + description: "Mining Drills", + enabled: () => Decimal.gte(oreDrill.amount.value, 1) + })) + ]); + const computedOreSpeed = computed(() => oreSpeed.apply(1)); + const oreProgress = persistent(0); + const maxOreProgress = 10; + const oreBar = createBar(() => ({ + width: 400, + height: 25, + direction: Direction.Right, + fillStyle: { backgroundColor: color }, + progress: () => Decimal.div(oreProgress.value, maxOreProgress) + })); + + const metalGain = createSequentialModifier(() => [ + createAdditiveModifier(() => ({ + addend: computedAutoSmeltSpeed, + enabled: autoSmeltEnabled + })), + createMultiplicativeModifier(() => ({ + multiplier: computedOrePurity + })) + ]); + const computedMetalGain = computed(() => metalGain.apply(0)); + const oreGain = createSequentialModifier(() => [ + createAdditiveModifier(() => ({ + addend: computedOreAmount + })), + createMultiplicativeModifier(() => ({ + multiplier: computedOreSpeed + })), + createMultiplicativeModifier(() => ({ + multiplier: Decimal.reciprocate(maxOreProgress) + })) + ]) + const computedOreGain = computed(() => oreGain.apply(0)); + const netOreGain = createSequentialModifier(() => [ + createAdditiveModifier(() => ({ + addend: computedOreGain + })), + createAdditiveModifier(() => ({ + addend: () => Decimal.negate(computedAutoSmeltSpeed.value), + enabled: autoSmeltEnabled + })) + ]) + const computedNetOreGain = computed(() => netOreGain.apply(0)); + + const simplePickaxe = createUpgrade(() => ({ + resource: noPersist(metal), + cost: 0.1, + display: { + title: "A Simple Pickaxe", + description: "Make a simple pickaxe to help mine faster.

Halve the time to mine more ore" + } + })); + const doublePickaxe = createUpgrade(() => ({ + resource: noPersist(metal), + cost: 0.1, + display: { + title: "Double Pickaxe", + description: "This is too slow. What if you swung two pickaxes at once?

Halve the time to mine ore, again" + }, + visibility: () => showIf(doublePickaxe.bought.value) + })) as GenericUpgrade; + const crucible = createUpgrade(() => ({ + resource: noPersist(metal), + cost: 1, + display: { + title: "Crucible", + description: "Smelting this all by hand is rather painful, and a lot of the metal is left in the slag. A small crucible should help a lot!

Increase the metal extracted per ore by 5x" + }, + visibility: () => showIf(crucible.bought.value || Decimal.div(bestOre.value, computedOrePurity.value).plus(bestMetal.value).gte(1)) + })) as GenericUpgrade; + const coalDrill = createUpgrade(() => ({ + resource: noPersist(metal), + cost: 0, + display: { + title: "Coal Drilling", + description: "These mining drills are pretty powerful, mining more ore than you can actually smelt. Could be worth making some to mine coal instead" + }, + visibility: () => showIf(Decimal.gte(oreDrill.amount.value, 1) && ( + coalDrill.bought.value || + Decimal.lt(coal.computedCoalGain.value, Decimal.times(computedOreAmount.value, computedOreSpeed.value).div(maxOreProgress).times(coalCost)))) + })) as GenericUpgrade; + const industrialFurnace = createUpgrade(() => ({ + canAfford() { + return Decimal.gte(metal.value, 50) && Decimal.gte(coal.coal.value, 1e11) + }, + onPurchase() { + metal.value = Decimal.sub(metal.value, 50); + coal.coal.value = Decimal.sub(coal.coal.value, 1e11); + }, + display: { + title: "Industrial Furnace", + description: `Moving smelting out of the open air and into a dedicated furnace should make efficiency even better. Double metal gained per ore +
+
+ Cost: 50 ${metal.displayName}
${format(1e11)} ${coal.coal.displayName}` + } + })); + + const oreDrill = createBuyable(() => ({ + resource: noPersist(metal), + cost() { return Decimal.pow(this.amount.value, 1.15).plus(10)}, + display: { + title: "Mining Drill", + description: "An automated machine to help you mine more ore, faster", + effectDisplay: jsx(() => <>Mine 2.5x faster. Increase ore mining amount by {formatWhole(oreDrill.amount.value)} ore per operation) + }, + visibility: () => showIf(Decimal.gte(oreDrill.amount.value, 1) || Decimal.div(bestOre.value, computedOrePurity.value).plus(bestMetal.value).gte(10)), + style: { width: '200px' } + })) as GenericBuyable + const industrialCrucible = createBuyable(() => ({ + resource: noPersist(metal), + cost() { return Decimal.pow(Decimal.times(this.amount.value, 10), 1.15).plus(10)}, + display: { + title: "Industrial Crucible", + description: "A giant automated crucible furnace, letting you smelt ore better and faster", + effectDisplay: jsx(() => <>Get 50% more metal per ore, and smelt {formatWhole(Decimal.times(industrialCrucible.amount.value, 10))} ore per second) + }, + visibility: () => showIf(Decimal.gte(industrialCrucible.amount.value, 1) || Decimal.gte(oreDrill.amount.value, 4) || Decimal.gte(bestOre.value, 50)), + style: { width: '200px' } + })) as GenericBuyable + const autoSmeltEnabled = persistent(true); + const hotterForge = createBuyable(() => ({ + resource: coal.coal, + cost() { return Decimal.pow(10, this.amount.value).times(1e12)}, + display: { + title: "Hotter Forges", + description: "More coal makes the fires burn hotter, getting just a little more metal out of each bit of ore", + effectDisplay: jsx(() => <>Gain {formatWhole(Decimal.times(hotterForgeEffect.value, 100))}% more metal per ore) + }, + visibility: () => showIf(Decimal.gte(hotterForge.amount.value, 1) || industrialFurnace.bought.value), + style: { width: '200px' } + })) as GenericBuyable; + const hotterForgeEffect = computed(() => Decimal.times(hotterForge.amount.value, 0.25)); + + globalBus.on("update", diff => { + oreProgress.value = Decimal.times(diff, computedOreSpeed.value).plus(oreProgress.value); + const oreGain = oreProgress.value.div(maxOreProgress).trunc(); + oreProgress.value = oreProgress.value.minus(oreGain.times(maxOreProgress)); + ore.value = Decimal.add(ore.value, oreGain); + + if (autoSmeltEnabled.value && Decimal.gte(smeltableOre.value, Decimal.times(industrialCrucible.amount.value, 10).times(diff))) { + smeltOre(Decimal.min( + smeltableOre.value, + Decimal.times(industrialCrucible.amount.value, 10).times(diff) + )); + } + }); + + const [generalTab, generalTabCollapsed] = createCollapsibleModifierSections(() => [ + { + title: "Automatic Smelting", + modifier: autoSmeltSpeed, + base: 0, + visible() { + return Decimal.gt(industrialCrucible.amount.value, 0); + } + }, + { + title: "Metal per Ore", + modifier: orePurity, + base: 0.1 + }, + { + title: "Ore per Mining Operation", + modifier: oreAmount, + base: 1 + }, + { + title: "Mining Speed", + modifier: oreSpeed, + base: 1 + } + ]); + const showModifiersModal = ref(false); + const modifiersModal = jsx(() => ( + (showModifiersModal.value = value)} + v-slots={{ + header: () =>

{name} Modifiers

, + body: generalTab + }} + /> + )); + + const { total: totalMetal, trackerDisplay } = setUpDailyProgressTracker({ + resource: metal, + goal: 5000, + name, + day, + color, + modal: { + show: showModifiersModal, + display: modifiersModal + } + }); + + return { + name, + day, + color, + ore, + bestOre, + oreProgress, + metal, + bestMetal, + totalMetal, + simplePickaxe, + doublePickaxe, + crucible, + coalDrill, + industrialFurnace, + oreDrill, + industrialCrucible, + autoSmeltEnabled, + hotterForge, + minWidth: 700, + display: jsx(() => ( + <> + {render(trackerDisplay)} + + {{ + [-1]: <>{format(computedMetalGain.value)}/s, + 0: undefined, + 1: <>+{format(computedMetalGain.value)}/s + }[Decimal.compare(computedMetalGain.value, 0)]} + } + /> + + {render(smeltOreButton)} + {Decimal.gte(industrialCrucible.amount.value, 1) + ?
+ (autoSmeltEnabled.value = value)}/> +
+ : undefined} + + {{ + [-1]: <>{format(computedNetOreGain.value)}/s, + 0: undefined, + 1: <>+{format(computedNetOreGain.value)}/s + }[Decimal.compare(computedNetOreGain.value, 0)]} + } + /> + +
+ Currently mining {format(computedOreAmount.value)} ore every {format(Decimal.div(maxOreProgress, computedOreSpeed.value))} seconds +
+ {render(oreBar)} + + {renderRow(simplePickaxe, doublePickaxe, crucible, coalDrill, industrialFurnace)} + {renderRow(oreDrill, industrialCrucible, hotterForge)} + + )) + } +}); + +export default layer; \ No newline at end of file diff --git a/src/data/projEntry.tsx b/src/data/projEntry.tsx index 9d1dd85..8c790af 100644 --- a/src/data/projEntry.tsx +++ b/src/data/projEntry.tsx @@ -26,10 +26,12 @@ import coalSymbol from "./symbols/coal.png"; import elfSymbol from "./symbols/elf.png"; import paperSymbol from "./symbols/paperStacks.png"; import boxesSymbol from "./symbols/cardboardBox.png"; +import metalSymbol from "./symbols/metal.png"; import coal from "./layers/coal"; import elves from "./layers/elves"; import paper from "./layers/paper"; import boxes from "./layers/boxes"; +import metal from "./layers/metal"; export interface Day extends VueFeature { day: number; @@ -198,15 +200,15 @@ export const main = createLayer("main", function (this: BaseLayer) { createDay(() => ({ day: 7, shouldNotify: false, - layer: null, - symbol: "", + layer: "metal", + symbol: metalSymbol, story: "", completedStory: "" })), createDay(() => ({ day: 8, shouldNotify: false, - layer: null, + layer: null, // "cloth" symbol: "", story: "", completedStory: "" @@ -214,7 +216,7 @@ export const main = createLayer("main", function (this: BaseLayer) { createDay(() => ({ day: 9, shouldNotify: false, - layer: null, + layer: null, // "oil" symbol: "", story: "", completedStory: "" @@ -222,7 +224,7 @@ export const main = createLayer("main", function (this: BaseLayer) { createDay(() => ({ day: 10, shouldNotify: false, - layer: null, + layer: null, // "plastic" symbol: "", story: "", completedStory: "" @@ -230,7 +232,7 @@ export const main = createLayer("main", function (this: BaseLayer) { createDay(() => ({ day: 11, shouldNotify: false, - layer: null, + layer: null, // "dyes" symbol: "", story: "", completedStory: "" @@ -238,7 +240,7 @@ export const main = createLayer("main", function (this: BaseLayer) { createDay(() => ({ day: 12, shouldNotify: false, - layer: null, + layer: null, // "management" symbol: "", story: "", completedStory: "" @@ -246,7 +248,7 @@ export const main = createLayer("main", function (this: BaseLayer) { createDay(() => ({ day: 13, shouldNotify: false, - layer: null, + layer: null, // "" symbol: "", story: "", completedStory: "" @@ -254,7 +256,7 @@ export const main = createLayer("main", function (this: BaseLayer) { createDay(() => ({ day: 14, shouldNotify: false, - layer: null, + layer: null, // "" symbol: "", story: "", completedStory: "" @@ -262,7 +264,7 @@ export const main = createLayer("main", function (this: BaseLayer) { createDay(() => ({ day: 15, shouldNotify: false, - layer: null, + layer: null, // "wrapping paper" symbol: "", story: "", completedStory: "" @@ -270,7 +272,7 @@ export const main = createLayer("main", function (this: BaseLayer) { createDay(() => ({ day: 16, shouldNotify: false, - layer: null, + layer: null, // "ribbons" symbol: "", story: "", completedStory: "" @@ -278,7 +280,7 @@ export const main = createLayer("main", function (this: BaseLayer) { createDay(() => ({ day: 17, shouldNotify: false, - layer: null, + layer: null, // "toys 1" symbol: "", story: "", completedStory: "" @@ -286,7 +288,7 @@ export const main = createLayer("main", function (this: BaseLayer) { createDay(() => ({ day: 18, shouldNotify: false, - layer: null, + layer: null, // "toys 2" symbol: "", story: "", completedStory: "" @@ -294,7 +296,7 @@ export const main = createLayer("main", function (this: BaseLayer) { createDay(() => ({ day: 19, shouldNotify: false, - layer: null, + layer: null, // "toys 3" symbol: "", story: "", completedStory: "" @@ -302,7 +304,7 @@ export const main = createLayer("main", function (this: BaseLayer) { createDay(() => ({ day: 20, shouldNotify: false, - layer: null, + layer: null, // "presents" symbol: "", story: "", completedStory: "" @@ -310,7 +312,7 @@ export const main = createLayer("main", function (this: BaseLayer) { createDay(() => ({ day: 21, shouldNotify: false, - layer: null, + layer: null, // "reindeer" symbol: "", story: "", completedStory: "" @@ -318,7 +320,7 @@ export const main = createLayer("main", function (this: BaseLayer) { createDay(() => ({ day: 22, shouldNotify: false, - layer: null, + layer: null, // "sleigh" symbol: "", story: "", completedStory: "" @@ -326,7 +328,7 @@ export const main = createLayer("main", function (this: BaseLayer) { createDay(() => ({ day: 23, shouldNotify: false, - layer: null, + layer: null, // "distribution route planning" symbol: "", story: "", completedStory: "" @@ -334,7 +336,7 @@ export const main = createLayer("main", function (this: BaseLayer) { createDay(() => ({ day: 24, shouldNotify: false, - layer: null, + layer: null, // "packing the presents" symbol: "", story: "", completedStory: "" @@ -398,7 +400,7 @@ export const main = createLayer("main", function (this: BaseLayer) { export const getInitialLayers = ( /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ player: Partial -): Array => [main, trees, workshop, coal, elves, paper, boxes]; +): Array => [main, trees, workshop, coal, elves, paper, boxes, metal]; /** * A computed ref whose value is true whenever the game is over. diff --git a/src/data/symbols/metal.png b/src/data/symbols/metal.png new file mode 100644 index 0000000000000000000000000000000000000000..c29c3bdda53351dfb47d411e8b4e3b3557976db3 GIT binary patch literal 7036 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 - +
+
+
+ You have + + {{ resource.displayName + }} + , + +
+
+
+