Vue.component("sand", { props: ["layer", "data"], template: `
`, computed: { percentChipped: () => new Decimal(1).sub(player.sands.shrunkAmount.div(nextStoneCost())).times(10000) } }); /* Cannot use this class because the animations don't reset properly, and seem to be calcualting the duration or delays incorrectly. For now it seems easier to just keep it as a display-text. I don't think it's actually that non-performant. Vue.component("hourglass", { props: ["layer", "data"], template: `
{{ formatWhole(totalGrains.sub(grainsFallen)) }}
{{ formatWhole(grainsFallen) }}
`, computed: { userStyle: () => { const flipping = player.sands.flipping; const finished = player.sands.grainsFallen.eq(getTotalGrains()); const fallSpeed = new Decimal(4).div(player.devSpeed || 1).div(getFallSpeed()); const fallMult = getFallMult(); return { "--fill-duration": (flipping || finished ? 1 : getTotalGrains().div(fallMult).ceil().times(fallSpeed).toNumber() + 0.05) + "s", "--fill-delay": "-" + (flipping || finished ? .999 : player.sands.grainsFallen.div(fallMult).floor().times(fallSpeed).toNumber()) + "s", "--fill-state": flipping || finished ? "paused" : "running", "--flip-duration": new Decimal(5).div(player.devSpeed || 1).div(getFlipSpeed()).toNumber() + 0.05 + "s", "--flip-state": flipping ? "running" : "paused" }; }, totalGrains: getTotalGrains, grainsFallen: () => player.sands.grainsFallen } }); */ function nextStoneCost() { return new Decimal(10).times(new Decimal(1.05).pow(player.sands.stonesChipped)); } function getFallSpeed() { let fallSpeed = new Decimal(1); fallSpeed = fallSpeed.times(new Decimal(1.1).pow(getJobLevel("sands"))); fallSpeed = fallSpeed.times(buyableEffect("sands", 12)); if (player.sands.chipping && hasUpgrade("sands", 15)) { if (hasUpgrade("sands", 13)) { fallSpeed = fallSpeed.times(upgradeEffect("sands", 13).add(1)); } else { fallSpeed = fallSpeed.times(2); } } if (player.generators.sandsActive && (player.tab === "generators" || player.generators.timeLoopActive)) { fallSpeed = fallSpeed.div(10); } return fallSpeed; } function getFlipSpeed() { let flipSpeed = new Decimal(1); flipSpeed = flipSpeed.times(new Decimal(1.1).pow(getJobLevel("sands"))); flipSpeed = flipSpeed.times(buyableEffect("sands", 22)); if (player.generators.sandsActive && (player.tab === "generators" || player.generators.timeLoopActive)) { flipSpeed = flipSpeed.div(10); } return flipSpeed; } function getTotalGrains() { let grains = new Decimal(player.sands.stonesChipped); grains = grains.times(buyableEffect("sands", 14)); grains = grains.times(buyableEffect("sands", 24)); grains = grains.sub(player.sands.spentGrains); return grains.max(0); } function getFallMult() { let fallAmount = new Decimal(1); fallAmount = fallAmount.times(buyableEffect("sands", 13)); return fallAmount; } function getPotentiaMult() { let gain = new Decimal(1); gain = gain.times(buyableEffect("sands", 23)); if (hasUpgrade("sands", 14)) { gain = gain.times(upgradeEffect("sands", 14)); } if (player.sands.chipping && hasUpgrade("sands", 25)) { if (hasUpgrade("sands", 13)) { gain = gain.times(upgradeEffect("sands", 13).add(1)); } else { gain = gain.times(2); } } gain = gain.times(layers.generators.clickables.sands.effect()); if (player.sands.selectedLens === "potentia") { gain = gain.times(buyableEffect("sands", "glass")); } gain = gain.times(ritualEffect("gain")); if (player.generators.sandsActive && (player.tab === "generators" || player.generators.timeLoopActive)) { gain = gain.sqrt(); } return gain; } addLayer("sands", { name: "sands", resource: "potentia", image: "images/pexels-photo-1095601.jpeg", color: sandsColor, jobName: "Experiments with time", showJobDelay: 0.75, layerShown: () => hasMilestone("study", 5), startData() { return { unlocked: true, points: new Decimal(0), xp: new Decimal(0), lastLevel: new Decimal(0), timeLoopActive: false, grainsFallen: new Decimal(0), shrunkAmount: new Decimal(0), chipping: false, flipping: false, stonesChipped: new Decimal(0), fallTime: new Decimal(0), flipTime: new Decimal(0), lensLevel: new Decimal(0), selectedLens: "chip", spentGrains: new Decimal(0) }; }, tabFormat: { "Main": { content: () => { new Decimal(1).sub(player.sands.shrunkAmount.div(nextStoneCost())).times(10000); return [ "main-display", "blank", ["display-text", (() => { if (!hasMilestone("sands", 0)) { return "Discover new ways to experiment at level 2"; } if (!hasMilestone("sands", 1)) { return "Discover new ways to experiment at level 4"; } if (!hasMilestone("sands", 3)) { return "Discover new ways to experiment at level 6"; } if (!hasMilestone("sands", 4)) { return "Discover new ways to experiment at level 8"; } return ""; })()], "blank", ["sticky", ["80px", ["column", [ ["display-text", formatWhole(getTotalGrains().sub(player.sands.grainsFallen))], ["display-text", `
`], ["display-text", formatWhole(player.sands.grainsFallen)] ]]]], "blank", ["clickable", "flip"], "blank", "blank", ["display-text", `Zoom Level: 1 / ${format(nextStoneCost().div(10))}x`], "blank", "sand", "blank", ["clickable", "chip"], "blank", "blank", ["milestones-filtered", [2, 5, 6]] ]; } }, "Upgrades": { content: () => [ "main-display", "blank", ["display-text", (() => { if (!hasMilestone("sands", 0)) { return "Discover new ways to experiment at level 2"; } if (!hasMilestone("sands", 1)) { return "Discover new ways to experiment at level 4"; } if (!hasMilestone("sands", 3)) { return "Discover new ways to experiment at level 6"; } if (!hasMilestone("sands", 4)) { return "Discover new ways to experiment at level 8"; } return ""; })()], "blank", ["sticky", ["80px", ["column", [ ["display-text", formatWhole(getTotalGrains().sub(player.sands.grainsFallen))], ["display-text", `
`], ["display-text", formatWhole(player.sands.grainsFallen)] ]]]], "blank", ["clickable", "flip"], "blank", "blank", "buyables", "blank", "upgrades" ], unlocked: () => hasMilestone("sands", 0), shouldNotify: () => Object.values(tmp.sands.upgrades).some(upgrade => upgrade.unlocked && upgrade.canAfford) || Object.values(tmp.sands.buyables).some(buyable => buyable.id !== "glass" && buyable.unlocked && buyable.canAfford) }, "Glass": { content: () => [ "main-display", "blank", ["buyable", "glass"], "blank", ["row", [ ["clickable", "redLens"], "blank", ["clickable", "greenLens"], "blank", ["clickable", "blueLens"] ]] ], unlocked: () => hasMilestone("generators", 2), shouldNotify: () => tmp.sands.buyables.glass.unlocked && tmp.sands.buyables.glass.canAfford } }, update(diff) { if (player.tab === this.layer || player[this.layer].timeLoopActive) { let shrinkGain = new Decimal(0); if (hasUpgrade("sands", 11)) { shrinkGain = shrinkGain.add(.1); } if (hasUpgrade("sands", 12)) { shrinkGain = shrinkGain.add(.2); } if (hasUpgrade("sands", 21)) { shrinkGain = shrinkGain.add(.3); } if (hasUpgrade("sands", 22)) { shrinkGain = shrinkGain.add(.4); } if (player[this.layer].chipping) { if (hasUpgrade("sands", 13)) { shrinkGain = shrinkGain.add(upgradeEffect("sands", 13)); } else { shrinkGain = shrinkGain.add(1); } } if (shrinkGain.gt(0)) { shrinkGain = shrinkGain.times(diff); shrinkGain = shrinkGain.times(new Decimal(1.1).pow(getJobLevel(this.layer))); shrinkGain = shrinkGain.times(buyableEffect("sands", 11)); shrinkGain = shrinkGain.times(buyableEffect("sands", 21)); if (player.sands.selectedLens === "chip") { shrinkGain = shrinkGain.times(buyableEffect("sands", "glass")); } if (player.generators.sandsActive && (player.tab === "generators" || player.generators.timeLoopActive)) { shrinkGain = shrinkGain.div(10); } player[this.layer].shrunkAmount = player[this.layer].shrunkAmount.add(shrinkGain); } // https://gameanalytics.com/blog/idle-game-mathematics/ // b = 100 // r = 1.1 let grainsGain = player[this.layer].shrunkAmount.times(new Decimal(1.1).sub(1)).div(nextStoneCost()).add(1).log(1.1).floor(); if (grainsGain.gt(0)) { player[this.layer].shrunkAmount = player[this.layer].shrunkAmount.sub(nextStoneCost().times(new Decimal(1.1).pow(grainsGain).sub(1)).div(new Decimal(1.1).sub(1))); player[this.layer].stonesChipped = player[this.layer].stonesChipped.add(grainsGain); } if (player[this.layer].flipping) { player[this.layer].fallTime = new Decimal(0); player[this.layer].flipTime = player[this.layer].flipTime.add(getFlipSpeed().times(diff)); const flipDuration = new Decimal(5); if (player[this.layer].flipTime.gt(flipDuration)) { player[this.layer].flipping = false; player[this.layer].flipTime = new Decimal(0); player[this.layer].grainsFallen = new Decimal(0); } } else { player[this.layer].flipTime = new Decimal(0); if (player[this.layer].grainsFallen.lt(getTotalGrains())) { player[this.layer].fallTime = player[this.layer].fallTime.add(getFallSpeed().times(diff)); const fallDuration = new Decimal(4); const fallenGrains = player[this.layer].fallTime.div(fallDuration).floor().times(getFallMult()).clampMax(getTotalGrains().sub(player[this.layer].grainsFallen)); if (fallenGrains.gt(0)) { addPoints(this.layer, fallenGrains.times(getPotentiaMult())); player[this.layer].grainsFallen = player[this.layer].grainsFallen.add(fallenGrains); if (fallenGrains.eq(getTotalGrains())) { player[this.layer].fallTime = new Decimal(0); } else { player[this.layer].fallTime = player[this.layer].fallTime.sub(fallenGrains.div(getFallMult()).times(fallDuration)); } } } else { player[this.layer].fallTime = new Decimal(0); } } } }, onAddPoints(gain) { let xpGain = gain; if (hasUpgrade("generators", 13)) { xpGain = xpGain.times(layers.generators.clickables[this.layer].effect()); } if (player.sands.selectedLens === "xp") { xpGain = xpGain.times(Decimal.pow(buyableEffect("sands", "glass"), 2)); } xpGain = xpGain.times(ritualEffect("globalXp")); player[this.layer].xp = player[this.layer].xp.add(xpGain); checkJobXP(this.layer); }, milestones: { 0: { requirementDescription: "Level 2", done: () => player.sands.xp.gte(10) }, 1: { requirementDescription: "Level 4", done: () => player.sands.xp.gte(1e3) }, 2: { title: "I don't even know what I'm doing.", requirementDescription: "Level 5", "effectDescription": "Unlock a new time slot", done: () => player.sands.xp.gte(1e4), onComplete: () => { player.timeSlots = player.timeSlots.add(1); } }, 3: { requirementDescription: "Level 6", done: () => player.sands.xp.gte(1e5) }, 4: { requirementDescription: "Level 8", done: () => player.sands.xp.gte(1e7) }, 5: { title: "I mean, this stuff is way too advanced for me.", requirementDescription: "Level 10", "effectDescription": "Unlock rituals job", done: () => player.sands.xp.gte(1e9), unlocked: () => hasMilestone("sands", 2) }, 6: { title: "And what if I can't fix this? What are we gonna do?", requirementDescription: "Level 25", "effectDescription": "Unlock ???", done: () => player.sands.xp.gte(1e24) && player.chapter > 2, unlocked: () => hasMilestone("sands", 5) && player.chapter > 2 } }, clickables: { chip: { title: "Keep Moving Forward
", display: "Hover over this to chip away at the stone until it's the size of a grain of sand.", touchstart: () => { player.sands.chipping = true; }, touchend: () => { player.sands.chipping = false; } }, flip: { title: "But he doesn't give up!
", display: "Flip the hourglass", canClick: () => player.sands.grainsFallen.gt(0) && !player.sands.flipping, onClick: () => { player.sands.flipping = true; } }, redLens: { title: "Red Lens
", display() { return `Focus on chipping speed to improve it by x${formatWhole(layers.sands.buyables.glass.effect())}`; }, style: { borderColor: "red" }, canClick: () => player.sands.selectedLens !== "chip", onClick: () => player.sands.selectedLens = "chip" }, greenLens: { title: "Green Lens
", display() { return `Focus on potentia gain to improve it by x${formatWhole(layers.sands.buyables.glass.effect())}`; }, style: { borderColor: "green" }, canClick: () => player.sands.selectedLens !== "potentia", onClick: () => player.sands.selectedLens = "potentia" }, blueLens: { title: "Blue Lens
", display() { return `Focus on xp gain to improve it by x${formatWhole(Decimal.pow(layers.sands.buyables.glass.effect(), 2))}`; }, style: { borderColor: "blue" }, canClick: () => player.sands.selectedLens !== "xp", onClick: () => player.sands.selectedLens = "xp" } }, buyables: { rows: 2, cols: 4, glass: { title: "His insurance won't pay for contacts.
", layer: "sands", id: "glass", display() { return `Melt sand into glass. Doubles the effectiveness of lenses. Requires 110% of the cost to buy.

Currently: ${formatWhole(this.effect())}

Cost: ${format(this.cost())} grains of sand`; }, cost(x) { const amount = x || getBuyableAmount(this.layer, this.id); return new Decimal(1e6).times(Decimal.pow(10, amount)); }, effect() { return Decimal.pow(2, getBuyableAmount(this.layer, this.id).add(1)); }, canAfford() { return getTotalGrains().gte(this.cost().times(1.1)); }, buy() { setBuyableAmount(this.layer, this.id, getBuyableAmount(this.layer, this.id).add(1)); player.sands.spentGrains = new Decimal("1".repeat(getBuyableAmount("sands", "glass").toNumber()) + "000000"); }, unlocked: true }, 11: { title: "It's my dad's motto.
", display() { return `Additively increases chipping speed by 25%

Currently: x${format(this.effect())}

Cost: ${format(this.cost())} potentia`; }, cost(x) { const amount = x || getBuyableAmount(this.layer, this.id); return new Decimal(10).times(new Decimal(2).pow(amount)); }, effect() { return new Decimal(.25).times(getBuyableAmount(this.layer, this.id)).add(1); }, canAfford() { return player[this.layer].points.gte(this.cost()); }, buy() { player[this.layer].points = player[this.layer].points.sub(this.cost()); setBuyableAmount(this.layer, this.id, getBuyableAmount(this.layer, this.id).add(1)); }, unlocked: () => hasMilestone("sands", 0) }, 12: { title: "That's strange. She usually takes the Harley.
", display() { return `Additively increases how quickly grains of sand fall by 100%

Currently: x${format(this.effect())}

Cost: ${format(this.cost())} potentia`; }, cost(x) { const amount = x || getBuyableAmount(this.layer, this.id); return new Decimal(2).pow(Decimal.add(amount, 3)); }, effect() { return new Decimal(1).times(getBuyableAmount(this.layer, this.id)).add(1); }, canAfford() { return player[this.layer].points.gte(this.cost()); }, buy() { player[this.layer].points = player[this.layer].points.sub(this.cost()); setBuyableAmount(this.layer, this.id, getBuyableAmount(this.layer, this.id).add(1)); }, unlocked: () => hasMilestone("sands", 0) }, 13: { title: "You're smart, you fix it!
", display() { return `Additively increases how many grains of sand can fall through the hourglass at once by 1

Currently: x${format(this.effect())}

Cost: ${format(this.cost())} potentia`; }, cost(x) { const amount = x || getBuyableAmount(this.layer, this.id); return new Decimal(1.5).times(new Decimal(4).pow(Decimal.add(amount, 2))); }, effect() { return getBuyableAmount(this.layer, this.id).add(1); }, canAfford() { return player[this.layer].points.gte(this.cost()); }, buy() { player[this.layer].points = player[this.layer].points.sub(this.cost()); setBuyableAmount(this.layer, this.id, getBuyableAmount(this.layer, this.id).add(1)); }, unlocked: () => hasMilestone("sands", 0) }, 14: { title: "You want to know what I think about this?
", display() { return `Additively and retroactively increases how many grains of sand you gather from each completely chipped stone by 1

Currently: x${format(this.effect())}

Cost: ${format(this.cost())} potentia`; }, cost(x) { const amount = x || getBuyableAmount(this.layer, this.id); return new Decimal(25).times(new Decimal(6).pow(amount)); }, effect() { return getBuyableAmount(this.layer, this.id).add(1); }, canAfford() { return player[this.layer].points.gte(this.cost()); }, buy() { player[this.layer].points = player[this.layer].points.sub(this.cost()); setBuyableAmount(this.layer, this.id, getBuyableAmount(this.layer, this.id).add(1)); }, unlocked: () => hasMilestone("sands", 0) }, 21: { title: "I'm ignoring you for time reasons.
", display() { return `Multiplicatively increases chipping speed by 50%

Currently: x${format(this.effect())}

Cost: ${format(this.cost())} potentia`; }, cost(x) { const amount = x || getBuyableAmount(this.layer, this.id); return new Decimal(2).times(new Decimal(5).pow(Decimal.add(amount, 1))); }, effect() { return new Decimal(1.5).pow(getBuyableAmount(this.layer, this.id)); }, canAfford() { return player[this.layer].points.gte(this.cost()); }, buy() { player[this.layer].points = player[this.layer].points.sub(this.cost()); setBuyableAmount(this.layer, this.id, getBuyableAmount(this.layer, this.id).add(1)); }, unlocked: () => hasMilestone("sands", 3) }, 22: { title: "From failure, you learn; from success not so much.
", display() { return `Multiplicatively increases how quickly the hourglass flips by 20%

Currently: x${format(this.effect())}

Cost: ${format(this.cost())} potentia`; }, cost(x) { const amount = x || getBuyableAmount(this.layer, this.id); return new Decimal(3).times(new Decimal(2).pow(Decimal.add(amount, 1))); }, effect() { return new Decimal(1.2).pow(getBuyableAmount(this.layer, this.id)); }, canAfford() { return player[this.layer].points.gte(this.cost()); }, buy() { player[this.layer].points = player[this.layer].points.sub(this.cost()); setBuyableAmount(this.layer, this.id, getBuyableAmount(this.layer, this.id).add(1)); }, unlocked: () => hasMilestone("sands", 3) }, 23: { title: "To...the future!
", display() { return `Additively increases how much potentia you collect from each grain that falls by 1

Currently: x${format(this.effect())}

Cost: ${format(this.cost())} potentia`; }, cost(x) { const amount = x || getBuyableAmount(this.layer, this.id); return new Decimal(50).times(new Decimal(2).pow(amount)); }, effect() { return getBuyableAmount(this.layer, this.id).add(1); }, canAfford() { return player[this.layer].points.gte(this.cost()); }, buy() { player[this.layer].points = player[this.layer].points.sub(this.cost()); setBuyableAmount(this.layer, this.id, getBuyableAmount(this.layer, this.id).add(1)); }, unlocked: () => hasMilestone("sands", 3) }, 24: { title: "Or, what's left of it.
", display() { return `Multiplicatively and retroactively increases how many grains of sand you gather 2x

Currently: x${format(this.effect())}

Cost: ${format(this.cost())} potentia`; }, cost(x) { const amount = x || getBuyableAmount(this.layer, this.id); if (hasUpgrade("sands", 24)) { return new Decimal(75).times(new Decimal(8).pow(amount)); } return new Decimal(75).times(new Decimal(10).pow(amount)); }, effect() { return new Decimal(2).pow(getBuyableAmount(this.layer, this.id)); }, canAfford() { return player[this.layer].points.gte(this.cost()); }, buy() { player[this.layer].points = player[this.layer].points.sub(this.cost()); setBuyableAmount(this.layer, this.id, getBuyableAmount(this.layer, this.id).add(1)); }, unlocked: () => hasMilestone("sands", 3) } }, upgrades: { rows: 2, cols: 5, 11: { title: "I have a big head,
", description: "Automatically chip the stone at +10% efficiency", cost: new Decimal(200), unlocked: () => hasMilestone("sands", 1) }, 12: { title: "and little arms!
", description: "Automatically chip the stone at +20% efficiency", cost: new Decimal(400), unlocked: () => hasMilestone("sands", 1) }, 13: { title: "I'm not sure how well
", description: "Multiply \"Keep Moving Forward\"'s effect by this job's level
", cost: new Decimal(800), unlocked: () => hasMilestone("sands", 1), effect: () => getJobLevel("sands"), effectDisplay() { return `x${format(this.effect())}`; } }, 14: { title: "this plan was thought through.
", description: "Potentia gain is increased based on total number of grains
", cost: new Decimal(1600), unlocked: () => hasMilestone("sands", 1), effect: () => getTotalGrains().pow(.25).add(1), effectDisplay() { return `x${format(this.effect())}`; } }, 15: { title: "Master?
", description: "\"Keep Moving Forward\" also speeds up grains falling through the hourglass
", cost: new Decimal(6400), unlocked: () => hasMilestone("sands", 1) }, 21: { title: "I'll take two!
", description: "Automatically chip the stone at +30% efficiency", cost: new Decimal(1e6), unlocked: () => hasMilestone("sands", 4) }, 22: { title: "Bake them cookies, Lucille!
", description: "Automatically chip the stone at +40% efficiency", cost: new Decimal(2e6), unlocked: () => hasMilestone("sands", 4) }, 23: { title: "You are now under my control
", description: "Flip speed affects fall speed at 5% efficiency
", cost: new Decimal(5e6), unlocked: () => hasMilestone("sands", 4), effect: () => getFlipSpeed().sub(1).div(20).add(1), effectDisplay() { return `x${format(this.effect())}`; } }, 24: { title: "Stop laughing
", description: "Reduce the cost scaling of \"Or, what's left of it.\"
", cost: new Decimal(2.5e7), unlocked: () => hasMilestone("sands", 4) }, 25: { title: "Excellent
", description: "\"Keep Moving Forward\" also increases potentia gain
", cost: new Decimal(1e8), unlocked: () => hasMilestone("sands", 4) } }, bars: { job: getJobProgressBar("sands", sandsColor) } });