Merge branch 'day-24-packing'

This commit is contained in:
thepaperpilot 2022-12-24 20:03:21 -06:00
commit 7b115697a0
33 changed files with 1405 additions and 65 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -42,6 +42,35 @@
</div>
</template>
</Modal>
<Modal
:modelValue="main.creditsOpen.value"
@update:model-value="value => (main.creditsOpen.value = value)"
>
<template v-slot:header>
<h2>Credits</h2>
</template>
<template v-slot:body>
<div>
<component :is=convertComputable(main.credits) />
</div>
<!--<div v-if="main.loreScene.value !== -1">
<Scene :day="main.loreScene.value" />
<br />
You can help continue the <i>advent</i>ure at:
<a
href="https://discord.gg/WzejVAx"
class="info-modal-discord-link"
target="_blank"
>
<span class="material-icons info-modal-discord">discord</span>
The Paper Pilot Community
</a>
</div>-->
</template>
</Modal>
<!--<component :is="main.particles" />-->
</div>
</template>
@ -52,6 +81,7 @@ import Scene from "data/Scene.vue";
import type { GenericLayer } from "game/layers";
import { layers } from "game/layers";
import player from "game/player";
import { convertComputable } from "util/computed";
import { computeOptionalComponent } from "util/vue";
import { computed, toRef, unref } from "vue";
import Layer from "./Layer.vue";

View file

@ -26,11 +26,10 @@
import projInfo from "data/projInfo.json";
import type { CoercableComponent } from "features/feature";
import type { FeatureNode } from "game/layers";
import type { Persistent } from "game/persistence";
import player from "game/player";
import { computeComponent, computeOptionalComponent, processedPropType, wrapRef } from "util/vue";
import { computeComponent, computeOptionalComponent, processedPropType, unwrapRef } from "util/vue";
import type { PropType, Ref } from "vue";
import { computed, defineComponent, nextTick, toRefs, unref, watch } from "vue";
import { computed, defineComponent, toRefs, unref } from "vue";
import Context from "./Context.vue";
export default defineComponent({
@ -67,7 +66,7 @@ export default defineComponent({
const component = computeComponent(display);
const minimizedComponent = computeOptionalComponent(minimizedDisplay);
const showGoBack = computed(
() => projInfo.allowGoBack && index.value > 0 && !minimized.value
() => projInfo.allowGoBack && index.value > 0 && !unwrapRef(minimized)
);
function goBack() {

View file

@ -98,7 +98,7 @@ const isPaused = computed({
});
const canAutoSave = computed(
() => (layers as any).main?.days[(layers as any).main?.day.value - 1].opened.value
() => (layers as any).main?.day.value >= 25 || (layers as any).main?.days[(layers as any).main?.day.value - 1].opened.value
);
const autosaveTitle = jsx(() => (

View file

@ -6,7 +6,7 @@
masteryLock,
wallpaper: day < 8
}"
v-if="opened.value"
v-if="opened.value && visibility !== Visibility.None"
>
<div class="ribbon" v-if="day >= 8" />
<Tooltip :display="layers[layer ?? '']?.name ?? ''" :direction="Direction.Up" yoffset="5px">
@ -26,7 +26,7 @@
</Tooltip>
</div>
<div
v-else
v-else-if="visibility !== Visibility.None"
class="day feature dontMerge"
:class="{ can: canOpen, locked: !canOpen, canOpen, mastered: unref(mastered) }"
@click="tryUnlock"
@ -47,6 +47,7 @@
<script setup lang="ts">
import Notif from "components/Notif.vue";
import { Visibility } from "features/feature";
import Tooltip from "features/tooltips/Tooltip.vue";
import { layers } from "game/layers";
import Decimal from "util/bignum";
@ -66,6 +67,7 @@ const props = defineProps<{
recentlyUpdated: Ref<boolean>;
shouldNotify: ProcessedComputable<boolean>;
mastered: Ref<boolean>;
visibility?: Visibility;
}>();
const emit = defineEmits<{

View file

@ -21,7 +21,7 @@
/>
<img
v-if="day >= 21"
:src="sleigh"
:src="day >= 23 ? packing : sleigh"
class="scene-item"
style="left: 10%; bottom: 56%; transform: rotate(24deg); width: 100px; height: 100px"
/>
@ -116,6 +116,7 @@ import presents from "./symbols/presents.png";
import reindeer from "./symbols/reindeer.png";
import sleigh from "./symbols/sleigh.png";
import routing from "./symbols/gps.png";
import packing from "./symbols/sleighWSack.png";
defineProps<{
day: number;

View file

@ -29,3 +29,13 @@
justify-content: space-around;
margin-bottom: -5%;
}
.present-clickable {
cursor: pointer;
width: 30%;
text-align: center;
transition: all .2s ease-in-out;
}
.present-clickable:hover {
transform: scale(1.2);
}

207
src/data/credits.tsx Normal file
View file

@ -0,0 +1,207 @@
import Spacer from "components/layout/Spacer.vue";
import { jsx } from "features/feature";
import { layers } from "game/layers";
import { render } from "util/vue";
import { unref } from "vue";
type Credits = {
name: string;
creator: string;
help?: string;
other?: string[];
symbol?: string;
fs?: string;
};
import { main } from "./projEntry";
const dayCredits: Credits[] = [
{
name: "Trees",
creator: "thepaperpilot",
help: "Jacorb, Escapee"
},
{
name: "The Workshop",
creator: "thepaperpilot",
help: "Jacorb, emanresu"
},
{
name: "Coal",
creator: "Escapee",
help: "Jacorb, thepaperpilot"
},
{
name: "Elf Training",
creator: "thepaperpilot",
help: "incremental_gamer, emanresu"
},
{
name: "Paper",
creator: "thepaperpilot",
help: "Adsaf"
},
{
name: "Boxes",
creator: "thepaperpilot",
help: "ducdat0507"
},
{
name: "Metal",
creator: "Escapee",
help: "ducdat0507, thepaperpilot, yhvr"
},
{
name: "Cloth",
creator: "thepaperpilot",
help: "emanresu, Jacorb"
},
{
name: "Oil",
creator: "ducdat0507",
help: "thepaperpilot, Jacorb, incremental_gamer"
},
{
name: "Plastic",
creator: "thepaperpilot",
help: "Jacorb"
},
{
name: "Dyes",
creator: "Jacorb",
help: "thepaperpilot, ducdat0507"
},
{
name: "Management",
creator: "incremental_gamer, downvoid, thepaperpilot, Escapee"
},
{
name: "Management II",
creator: "incremental_gamer, downvoid, thepaperpilot, Escapee"
},
{
name: "Letters",
creator: "thepaperpilot"
},
{
name: "Wrapping Paper",
creator: "emanresu, thepaperpilot, Escapee",
fs: "28px"
},
{
name: "Ribbons",
creator: "thepaperpilot, Escapee"
},
{
name: "Toys",
creator: "downvoid",
help: "thepaperpilot"
},
{
name: "Factory",
creator: "incremental_gamer",
help: "thepaperpilot, ducdat, downvoid, emanresu, yhvr",
other: ["Art by emanresu"]
},
{
name: "Factory II",
creator: "downvoid",
help: "thepaperpilot",
other: ["Art by emanresu"]
},
{
name: "Presents",
creator: "incremental_gamer",
help: "ducdat0507",
other: ["Art by emanresu"]
},
{
name: "Reindeer",
creator: "thepaperpilot"
},
{
name: "Sleigh Repair",
creator: "downvoid",
help: "ducdat0507"
},
{
name: "Routing",
creator: "thepaperpilot, ducdat0507"
},
{
name: "Present Packing",
creator: "Escapee, emanresu",
help: "thepaperpilot",
fs: "26px"
}
];
const display = jsx(() => (
<div style="text-align: center; line-spacing: 5px; width: 700px">
<h1>Advent Incremental</h1>
<br />
<h2>Created by thepaperpilot and friends</h2>
<Spacer />
{dayCredits.map(({ name, help, other, creator, fs }, day) =>
render(
jsx(() => (
<div style="position: relative">
<span style="width: calc(100% - 260px); display: inline-block;">
<h1
style={{
color: unref(
layers[main.days[day].layer ?? ""]?.color ?? "white"
),
fontSize: fs ?? "30px"
}}
>
Day {day + 1} - {name}
</h1>
<br />
<br />
Created by {creator} <br />
{help != null ? (
<>
With help from {help}
<br />
</>
) : undefined}
{other
? other?.map(other => (
<>
{other}
<br />
</>
))
: undefined}
<br />
<br />
</span>
<img
style={`position: absolute; top: 5px; ${
day % 2 ? "left" : "right"
}: 20px; width: 100px;`}
src={main.days[day].symbol}
/>
</div>
))
)
)}
<h1>Special Thanks</h1>
<p>Nekosity</p>
<p>Yhvr</p>
<p>Ducdat0507</p>
<p>Haley</p>
<p>emanresu</p>
<br />
<p style="width: 600px">
And last but not least, a massive thanks to everyone who played and provided feedback on
the game.
</p>
<Spacer />
<h1 style="font-family: 'Great Vibes', cursive">Thanks for playing!</h1>
<Spacer />
</div>
));
export { display as credits };

View file

@ -36,6 +36,7 @@ import reindeer from "./reindeer";
import trees from "./trees";
import workshop from "./workshop";
import wrappingPaper from "./wrapping-paper";
import packing from "./packing";
export type BoxesBuyable = ElfBuyable & {
resource: Resource;
@ -58,11 +59,17 @@ const layer = createLayer(id, function (this: BaseLayer) {
enabled: workshop.milestones.extraExpansionMilestone5.earned
})),
reindeer.reindeer.vixen.modifier,
createMultiplicativeModifier(() => ({
multiplier: 1.1,
description: "120 Presents Packed",
enabled: packing.packingMilestones.boxBoost.earned
})),
createExponentialModifier(() => ({
exponent: 1.1,
description: "Bell Level 2",
enabled: management.elfTraining.boxElfTraining.milestones[1].earned
}))
})),
]) as WithRequired<Modifier, "description" | "revert">;
const boxesConversion = createCumulativeConversion(() => ({

View file

@ -42,6 +42,7 @@ import reindeer from "./reindeer";
import routing from "./routing";
import trees from "./trees";
import workshop from "./workshop";
import packing from "./packing"
const id = "cloth";
const day = 8;
@ -500,6 +501,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
description: "Yellow Dye",
enabled: dyes.masteryEffectActive
})),
createMultiplicativeModifier(() => ({
multiplier: 10,
description: "600 Presents Packed",
enabled: packing.packingMilestones.clothBoost.earned
})),
reindeer.reindeer.cupid.modifier
]) as WithRequired<Modifier, "description" | "revert">;
const computedShearingAmount = computed(() => shearingAmount.apply(1));

View file

@ -44,6 +44,7 @@ import plastic from "./plastic";
import reindeer from "./reindeer";
import trees from "./trees";
import wrappingPaper from "./wrapping-paper";
import packing from "./packing";
interface BetterFertilizerUpgOptions {
canAfford: () => boolean;
@ -237,10 +238,10 @@ const layer = createLayer(id, function (this: BaseLayer) {
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.kilnBook.totalAmount.value).times(v);
return Decimal.pow(1.1, v).times(1e7);
return Decimal.pow(packing.packingMilestones.coalBoost.earned.value ? 1.05 : 1.1, v).times(1e7);
},
inverseCost(x: DecimalSource) {
let v = Decimal.div(x, 1e7).log(1.1);
let v = Decimal.div(x, 1e7).log(packing.packingMilestones.coalBoost.earned.value ? 1.05 : 1.1);
v = v.div(Decimal.pow(0.95, paper.books.kilnBook.totalAmount.value));
if (Decimal.gte(v, 10000)) v = Decimal.mul(v, 10000).root(2);
if (Decimal.gte(v, 100)) v = Decimal.mul(v, 100).root(2);
@ -299,7 +300,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
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.coalDrillBook.totalAmount.value).times(v);
let cost = Decimal.pow(1.15, v).times(10);
let cost = Decimal.pow(packing.packingMilestones.coalBoost.earned.value ? 1.075 : 1.15, v).times(10);
if (management.elfTraining.fertilizerElfTraining.milestones[2].earned.value) {
cost = cost.div(Decimal.add(trees.totalLogs.value, Math.E).ln());
}
@ -315,7 +316,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
if (management.elfTraining.fertilizerElfTraining.milestones[2].earned.value) {
x = Decimal.mul(x, Decimal.add(trees.totalLogs.value, Math.E).ln());
}
let v = Decimal.div(x, 10).log(1.15);
let v = Decimal.div(x, 10).log(packing.packingMilestones.coalBoost.earned.value ? 1.075 : 1.15);
v = v.div(Decimal.pow(0.95, paper.books.coalDrillBook.totalAmount.value));
if (Decimal.gte(v, 10000)) v = Decimal.mul(v, 10000).root(2);
if (Decimal.gte(v, 100)) v = Decimal.mul(v, 100).root(2);

View file

@ -0,0 +1,17 @@
import { jsx } from "features/feature";
import { createLayer } from "game/layers"
const id = "credits"
const day = 25;
const name = "Credits"
const credits = createLayer(id, () => {
return {
display: jsx(() =>
<div>
TODO: layer
</div>),
name,
day
}
})

View file

@ -39,6 +39,7 @@ import toys from "./toys";
import factory from "./factory";
import reindeer from "./reindeer";
import routing from "./routing";
import packing from "./packing";
interface Dye {
name: string;
@ -171,20 +172,21 @@ const layer = createLayer(id, function (this: BaseLayer) {
multiplier: 2,
description: "Wetter Dyes",
enabled: upgrades.yellowDyeUpg.bought
}))
);
modifiers.push(
})),
createMultiplicativeModifier(() => ({
multiplier: () => Decimal.add(cloth.cloth.value, Math.E).ln(),
description: "Gingersnap Level 1",
enabled: management.elfTraining.clothElfTraining.milestones[0].earned
}))
);
modifiers.push(
})),
createMultiplicativeModifier(() => ({
multiplier: 2,
description: "Carol Level 1",
enabled: management.elfTraining.dyeElfTraining.milestones[0].earned
})),
createMultiplicativeModifier(() => ({
multiplier: 5,
description: "977,000,000 Presents Packed",
enabled: packing.packingMilestones.primaryDyeBoost.earned
}))
);
}
@ -728,6 +730,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
Decimal.add(dyes.orange.amount.value, 1)
.log2()
.plus(1)
.mul(packing.packingMilestones.secondaryDyeBoost.earned.value ? 2 : 1)
.pow(oil.row3Upgrades[1].bought.value ? 2.5 : 1)
),
green1: computed(() =>
@ -739,6 +742,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
Decimal.add(dyes.green.amount.value, 1)
.log2()
.plus(1)
.mul(packing.packingMilestones.secondaryDyeBoost.earned.value ? 2 : 1)
.pow(upgrades.coalUpg.bought.value ? 2 : 1)
),
purple1: computed(() =>
@ -746,7 +750,12 @@ const layer = createLayer(id, function (this: BaseLayer) {
.pow(upgrades.coalUpg.bought.value ? 1.2 : 1)
.pow(management.elfTraining.clothElfTraining.milestones[3].earned.value ? 1.1 : 1)
),
purple2: computed(() => Decimal.add(dyes.purple.amount.value, 1).log2().plus(1)),
purple2: computed(() =>
Decimal.add(dyes.purple.amount.value, 1)
.log2()
.plus(1)
.mul(packing.packingMilestones.secondaryDyeBoost.earned.value ? 2 : 1)
),
black1: computed(() =>
Decimal.pow(2, Decimal.add(dyes.black.amount.value, 1).log2().sqrt())
.pow(upgrades.coalUpg.bought.value ? 1.2 : 1)

View file

@ -39,6 +39,7 @@ import wrappingPaper from "./wrapping-paper";
import dyes, { enumColor } from "./dyes";
import ribbon from "./ribbon";
import letters from "./letters";
import packing from "./packing";
export interface ElfBuyable extends GenericBuyable {
/** The inverse function of the cost formula, used to calculate the maximum amount that can be bought by elves. */
@ -455,6 +456,23 @@ const layer = createLayer(id, function (this: BaseLayer) {
enabled: elvesMilestone2.earned
}))
]);
const packingCooldown = createSequentialModifier(() => [
createMultiplicativeModifier(() => ({
multiplier: 2,
description: "6 Elves Trained",
enabled: elvesMilestone.earned
})),
createMultiplicativeModifier(() => ({
multiplier: () => Decimal.times(paper.books.packingBook.totalAmount.value, 0.1).add(1),
description: "The Tetris Effect",
enabled: () => Decimal.gt(paper.books.packingBook.totalAmount.value, 0)
})),
createMultiplicativeModifier(() => ({
multiplier: 2,
description: "10 Elves Trained",
enabled: elvesMilestone2.earned
}))
]);
const [generalTab, generalTabCollapsed] = createCollapsibleModifierSections(() => [
{
@ -590,6 +608,13 @@ const layer = createLayer(id, function (this: BaseLayer) {
base: 10,
unit: "/s",
visible: plastic.masteryEffectActive
},
{
title: "Jingle Auto-Buy Frequency",
modifier: packingCooldown,
base: 10,
unit: "/s",
visible: packing.upgrades.packingElf.bought
}
]);
const showModifiersModal = ref(false);
@ -732,7 +757,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
onPurchase() {
options.onPurchase?.();
if (
!["Peppermint", "Twinkle", "Cocoa", "Frosty", "Carol"].includes(
!["Peppermint", "Twinkle", "Cocoa", "Frosty", "Carol", "Jingle"].includes(
options.name
)
) {
@ -1013,6 +1038,20 @@ const layer = createLayer(id, function (this: BaseLayer) {
buyMax: () => management.elfTraining.plasticElfTraining.milestones[4].earned.value
});
const wrappingPaperElves = [dyeElf, plasticElf];
const packingElf = createElf({
name: "Jingle",
description: "Jingle will automatically hire more elves to help out with packing the sleigh.",
buyable: [packing.helpers.elf, packing.helpers.loader],
cooldownModifier: packingCooldown,
visibility: () => showIf(packing.upgrades.packingElf.bought.value),
buyMax: true,
onAutoPurchase(buyable, amount) {
if (buyable === packing.helpers.loader && !management.elfTraining.packingElfTraining.milestones[3].earned.value) {
buyable.amount.value = Decimal.sub(buyable.amount.value, amount);
}
}
});
const elves = {
cuttersElf,
plantersElf,
@ -1031,7 +1070,8 @@ const layer = createLayer(id, function (this: BaseLayer) {
oilElf,
metalElf,
dyeElf,
plasticElf
plasticElf,
packingElf
};
const totalElves = computed(() => Object.values(elves).filter(elf => elf.bought.value).length);
@ -1278,6 +1318,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
buyProgress: persistent<DecimalSource>(0),
amountOfTimesDone: persistent<number>(0),
bought: persistent<boolean>(false)
},
packingElf: {
buyProgress: persistent<DecimalSource>(0),
amountOftimesDone: persistent<number>(0),
bought: persistent<boolean>(false)
}
},
milestones: [
@ -1333,7 +1378,8 @@ const layer = createLayer(id, function (this: BaseLayer) {
fireElves,
plasticElves,
managementElves,
managementElves2.concat(wrappingPaperElves)
managementElves2.concat(wrappingPaperElves),
[packingElf]
)}
</div>
{milestonesDisplay()}

View file

@ -26,7 +26,6 @@ import boxes from "./boxes";
import cloth from "./cloth";
import coal from "./coal";
import dyes from "./dyes";
import elves from "./elves";
import metal from "./metal";
import oil from "./oil";
import paper from "./paper";
@ -38,6 +37,8 @@ import { Resource } from "features/resources/resource";
import { isArray } from "@vue/shared";
import { createTab } from "features/tabs/tab";
import routing from "./routing";
import packing from "./packing";
import elves from "./elves";
const id = "management";
const day = 12;
@ -190,11 +191,15 @@ const layer = createLayer(id, () => {
"Cocoa",
"Twinkle",
"Carol",
"Tinsel"
"Tinsel",
"Jingle"
].indexOf(elf.name) + 1;
if (elf.name == "Star" || elf.name == "Bell") {
costMulti /= 3;
}
if (elf.name == "Jingle") {
costMulti *= 100000;
}
const costBase = 4000 * costMulti;
const expRequiredForNextLevel = computed(() => Decimal.pow(5, level.value).mul(costBase));
const level = computed(() =>
@ -1145,6 +1150,70 @@ const layer = createLayer(id, () => {
visibility: () => showIf(plasticElfMilestones[3].earned.value && main.day.value >= 16)
}))
] as Array<GenericMilestone>;
const packingElfMilestones = [
createMilestone(() => ({
display: {
requirement: "Jingle Level 1",
effectDisplay: "Double elf packing speed"
},
shouldEarn: () => packingElfTraining.level.value >= 1
})),
createMilestone(() => ({
display: {
requirement: "Jingle Level 2",
effectDisplay: jsx(() => (
<>
Each elf assistant increases packing speed by 10%
<br />
Currently: +
{formatWhole(
Decimal.times(packing.helpers.elf.amount.value, 0.1).times(100)
)}
%
</>
))
},
shouldEarn: () => packingElfTraining.level.value >= 2,
visibility: () => showIf(packingElfMilestones[0].earned.value)
})),
createMilestone(() => ({
display: {
requirement: "Jingle Level 3",
effectDisplay: jsx(() => (
<>
Multiply packing speed by the number of completed packing milestones
<br />
Currently:{" "}
{formatWhole(
Object.values(packing.packingMilestones).filter(
milestone => milestone.earned.value
).length + 1
)}
x
</>
))
},
shouldEarn: () => packingElfTraining.level.value >= 3,
visibility: () => showIf(packingElfMilestones[1].earned.value)
})),
createMilestone(() => ({
display: {
requirement: "Jingle Level 4",
effectDisplay: "Jingle will now also buy loaders"
},
shouldEarn: () => packingElfTraining.level.value >= 4,
visibility: () => showIf(packingElfMilestones[2].earned.value && main.day.value >= 16)
})),
createMilestone(() => ({
display: {
requirement: "Jingle Level 5",
effectDisplay:
"Multipliers to elf packing speed also apply to loaders at reduced rate"
},
shouldEarn: () => packingElfTraining.level.value >= 5,
visibility: () => showIf(packingElfMilestones[3].earned.value && main.day.value >= 16)
}))
] as Array<GenericMilestone>;
// ------------------------------------------------------------------------------- Milestone display
const currentShown = persistent<string>("Holly");
@ -1225,8 +1294,10 @@ const layer = createLayer(id, () => {
);
const dyeElfTraining = createElfTraining(elves.elves.dyeElf, dyeElfMilestones);
const plasticElfTraining = createElfTraining(elves.elves.plasticElf, plasticElfMilestones);
const packingElfTraining = createElfTraining(elves.elves.packingElf, packingElfMilestones);
const row5Elves = [coalDrillElfTraining, heavyDrillElfTraining, oilElfTraining];
const row6Elves = [metalElfTraining, dyeElfTraining, plasticElfTraining];
const row7Elves = [packingElfTraining];
const elfTraining = {
cutterElfTraining,
planterElfTraining,
@ -1245,7 +1316,8 @@ const layer = createLayer(id, () => {
oilElfTraining,
heavyDrillElfTraining,
dyeElfTraining,
plasticElfTraining
plasticElfTraining,
packingElfTraining
};
const day12Elves = [
cutterElfTraining,
@ -1324,6 +1396,11 @@ const layer = createLayer(id, () => {
multiplier: 2,
description: "Focus Upgrade 1",
enabled: focusUpgrade1.bought
})),
createMultiplicativeModifier(() => ({
multiplier: () => Decimal.pow(2, packing.packingResets.value),
description: `${format(6.4e9)} ${packing.packedPresents.displayName}`,
enabled: packing.packingMilestones.moreFocus.earned
}))
]) as WithRequired<Modifier, "revert" | "description">;
const maximumElvesModifier = createSequentialModifier(() => [
@ -1355,8 +1432,8 @@ const layer = createLayer(id, () => {
direction: Direction.Right,
width: 566,
height: 50,
style: `border-radius: 4px 4px 0 0`,
borderStyle: `border-radius: 4px 4px 0 0`,
style: `border-radius: 0`,
borderStyle: `border-radius: 0`,
fillStyle: () => ({
background: focusTime.value > 0 ? color : "#7f7f00",
animation: focusTime.value > 0 ? "1s focused-focus-bar linear infinite" : "",
@ -1401,7 +1478,9 @@ const layer = createLayer(id, () => {
))
},
style: {
width: "300px"
width: "570px",
minHeight: "80px",
zIndex: 4
},
canClick: () => Decimal.eq(focusCooldown.value, 0),
onClick() {
@ -1420,10 +1499,20 @@ const layer = createLayer(id, () => {
let x = 0;
focusTargets.value = {};
const newCount = Decimal.min(count, range);
if (packing.packingMilestones.focusSelected.earned.value) {
const elf = Object.values(elfTraining).find(
training => training.name === currentShown.value
);
const roll = elf?.name ?? "";
if (!focusTargets.value[roll] && unref(elf?.visibility) === Visibility.Visible) {
focusTargets.value[roll] = true;
x++;
}
}
while (newCount.gt(x)) {
const elf = Object.values(elfTraining)[Math.floor(Math.random() * range)];
const roll = elf?.name ?? "";
if (!focusTargets.value[roll] && unref(elf.visibility) === Visibility.Visible) {
if (!focusTargets.value[roll] && unref(elf?.visibility) === Visibility.Visible) {
focusTargets.value[roll] = true;
x++;
}
@ -1770,6 +1859,12 @@ const layer = createLayer(id, () => {
modifier: plasticElfTraining.elfXPGain,
base: 0.1,
unit: " XP"
},
{
title: "Jingle XP Gain per Action",
modifier: packingElfTraining.elfXPGain,
base: 0.1,
unit: " XP"
}
]);
const showModifiersModal = ref(false);
@ -2055,17 +2150,18 @@ const layer = createLayer(id, () => {
Click on an elf to see their milestones.
<Spacer />
<Spacer />
{render(focusButton)}
{renderGrid(upgrades, upgrades2)}
<Spacer />
{renderGrid(
[focusButton],
[focusMeter],
treeElfTraining,
coalElfTraining,
fireElfTraining,
plasticElvesTraining,
row5Elves,
row6Elves
row6Elves,
row7Elves
)}
<Spacer />
{currentElfDisplay()}

View file

@ -41,6 +41,7 @@ import reindeer from "./reindeer";
import sleigh from "./sleigh";
import factory from "./factory";
import routing from "./routing";
import packing from "./packing";
const id = "metal";
const day = 7;
@ -128,6 +129,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
exponent: 1.2,
description: "100% Sleigh Fixed",
enabled: sleigh.milestones.milestone8.earned
})),
createExponentialModifier(() => ({
exponent: 1.5,
description: "69,200 Presents Packed",
enabled: packing.packingMilestones.metalBoost.earned
}))
]);
const computedOrePurity = computed(() => orePurity.apply(0.1));
@ -386,6 +392,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
})),
createMultiplicativeModifier(() => ({
multiplier: computedOreSpeed
})),
createMultiplicativeModifier(() => ({
multiplier: computedOreSpeed,
description: "1,670,000 Presents Packed",
enabled: packing.packingMilestones.oreBoost.earned
}))
]);
const computedOreGain = computed(() => oreGain.apply(0));

View file

@ -45,6 +45,7 @@ import toys from "./toys";
import factory from "./factory";
import reindeer from "./reindeer";
import routing from "./routing";
import packing from "./packing"
const id = "oil";
const day = 9;
@ -924,6 +925,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
description: "Red Dye",
enabled: dyes.masteryEffectActive
})),
createMultiplicativeModifier(() => ({
multiplier: 3,
description: "2,800 Presents Packaged",
enabled: packing.packingMilestones.oilBoost.earned
})),
createExponentialModifier(() => ({
exponent: 1.2,
description: "Diamond-tipped drills",

658
src/data/layers/packing.tsx Normal file
View file

@ -0,0 +1,658 @@
import { isArray } from "@vue/shared";
import SpacerVue from "components/layout/Spacer.vue";
import { createCollapsibleModifierSections, setUpDailyProgressTracker } from "data/common";
import { main } from "data/projEntry";
import { createBar } from "features/bars/bar";
import { createBuyable, GenericBuyable } from "features/buyable";
import { createClickable } from "features/clickables/clickable";
import { jsx, showIf } from "features/feature";
import { createMilestone, GenericMilestone } from "features/milestones/milestone";
import MainDisplayVue from "features/resources/MainDisplay.vue";
import { createResource, trackBest, trackTotal, Resource } from "features/resources/resource";
import { createLayer, BaseLayer } from "game/layers";
import {
createAdditiveModifier,
createMultiplicativeModifier,
createSequentialModifier
} from "game/modifiers";
import { persistent } from "game/persistence";
import Decimal, { DecimalSource, format, formatWhole } from "util/bignum";
import { Direction } from "util/common";
import { render, renderGrid, renderRow } from "util/vue";
import { computed, ComputedRef, ref, unref } from "vue";
import metal from "./metal";
import oil from "./oil";
import { createCollapsibleMilestones } from "data/common";
import { globalBus } from "game/events";
import { createUpgrade, GenericUpgrade } from "features/upgrades/upgrade";
import elves, { ElfBuyable } from "./elves";
import management from "./management";
import paper from "./paper";
import ModalVue from "components/Modal.vue";
import ribbon from "./ribbon";
import { createReset } from "features/reset";
import ResourceVue from "features/resources/Resource.vue";
const id = "packing";
const day = 24;
const layer = createLayer(id, function (this: BaseLayer) {
const name = "Packing the Sleigh";
const color = "lightblue";
const packedPresents = createResource<DecimalSource>(0, "packed presents");
const bestPresents = trackBest(packedPresents);
const totalPresents = trackTotal(packedPresents);
const totalPresentsResource = createResource<DecimalSource>(
computed(() => totalPresents.value),
"total packed presents"
);
const sledSpace = 64e6;
const packingResets = persistent<number>(0);
const packingReset = createReset(() => ({
thingsToReset: [elf as any, loader as any, packedPresents],
onReset() {
packingResets.value++;
}
}));
const resetPacking = createClickable(() => ({
display: {
description:
"Oh no! You've run out of space! You'll need to take all the presents out and repack them more tightly..."
},
visibility: () =>
showIf(Decimal.lt(packedPresents.value, 8e9) && Decimal.lte(remainingSize.value, 0)),
onClick: packingReset.reset
}));
const packingProgress = persistent<DecimalSource>(0);
const packingProgressBar = createBar(() => ({
direction: Direction.Right,
width: 100,
height: 10,
fillStyle: {
animation: "15s packing-bar linear infinite"
},
progress: () => packingProgress.value
}));
const manualAmount = computed(() =>
Decimal.add(
Decimal.times(computedElfPackingSpeed.value, elf.amount.value),
Decimal.times(computedLoaderPackingSpeed.value, loader.amount.value)
).times(2)
);
const packPresent = createClickable(() => ({
display: {
description: jsx(() => (
<>
{upgrades2.manual.bought.value ? (
<h3>Pack {format(manualAmount.value)} presents</h3>
) : (
<h3>Pack a present</h3>
)}
<br />
{render(packingProgressBar)}
</>
))
},
style: "min-height: 60px; width: 200px",
visibility: () => showIf(Decimal.gt(remainingSize.value, 0)),
canClick: () => Decimal.gte(packingProgress.value, 1),
onClick() {
if (Decimal.lt(packingProgress.value, 1)) {
return;
}
const amount = upgrades2.manual.bought.value ? manualAmount.value : 1;
packedPresents.value = Decimal.add(packedPresents.value, amount).min(
currentMaxPresents.value
);
packingProgress.value = 0;
}
}));
const packingDensity = computed(() => {
switch (packingResets.value) {
default:
return 0.6;
case 1:
return 0.7;
case 2:
return 0.85;
case 3:
return 1;
}
});
const packedPresentsSize = computed(() =>
Decimal.times(packedPresents.value, 0.008).dividedBy(packingDensity.value)
);
const currentMaxPresents = computed(() =>
Decimal.times(sledSpace, packingDensity.value).div(0.008)
);
const remainingSize = computed(() => Decimal.sub(sledSpace, packedPresentsSize.value));
const elfPackingSpeed = createSequentialModifier(() => [
createMultiplicativeModifier(() => ({
multiplier: () => Decimal.pow(0.5, packingResets.value),
description: "Better Organization",
enabled: () => packingResets.value >= 1
})),
createMultiplicativeModifier(() => ({
multiplier: 2,
description: "Jingle Level 1",
enabled: management.elfTraining.packingElfTraining.milestones[0].earned
})),
createMultiplicativeModifier(() => ({
multiplier: () => Decimal.times(helpers.elf.amount.value, 0.005).plus(1),
description: "Jingle Level 2",
enabled: management.elfTraining.packingElfTraining.milestones[1].earned
})),
createMultiplicativeModifier(() => ({
multiplier: () =>
1 + Object.values(packingMilestones).filter(milestone => milestone.earned).length,
description: "Jingle Level 3",
enabled: management.elfTraining.packingElfTraining.milestones[2].earned
})),
createMultiplicativeModifier(() => ({
multiplier: () => Decimal.log10(packedPresents.value).plus(1),
description: "10,000 Presents Packed",
enabled: () => Decimal.gte(packedPresents.value, 1e4)
})),
createMultiplicativeModifier(() => ({
multiplier: () => Decimal.times(management.totalElfLevels.value, 0.05).add(1),
description: "Communal Assistance",
enabled: upgrades.elfLevel.bought
})),
createMultiplicativeModifier(() => ({
multiplier: () => Decimal.pow(1.02, ribbon.ribbon.value),
description: "Spare Bows",
enabled: upgrades2.ribbons.bought
}))
]);
const computedElfPackingSpeed = computed(() => elfPackingSpeed.apply(1));
const loaderPackingSpeed = createSequentialModifier(() => [
createAdditiveModifier(() => ({
addend: () => Decimal.times(elf.amount.value, 5),
description: "Loading Assistants",
enabled: upgrades2.assistantSynergy.bought
})),
createMultiplicativeModifier(() => ({
multiplier: () => Decimal.pow(0.5, packingResets.value),
description: "Better Organization",
enabled: () => packingResets.value >= 1
})),
createMultiplicativeModifier(() => ({
multiplier: () => Decimal.sqrt(elfPackingSpeed.apply(1)),
description: "Jingle Level 5",
enabled: management.elfTraining.packingElfTraining.milestones[4].earned
}))
]);
const computedLoaderPackingSpeed = computed(() => loaderPackingSpeed.apply(1000));
const elf = createBuyable(() => ({
visibility: () => showIf(Decimal.gte(totalPresents.value, 10)),
cost() {
let v = this.amount.value;
v = Decimal.pow(0.98, paper.books.packingBook.totalAmount.value).times(v);
return Decimal.pow(1.2, v).times(10).floor();
},
inverseCost(cost: DecimalSource) {
let amount = Decimal.div(cost, 10).log(1.2);
amount = amount.div(Decimal.pow(0.98, paper.books.packingBook.totalAmount.value));
return Decimal.isNaN(amount) ? Decimal.dZero : amount.floor().max(0);
},
resource: totalPresentsResource,
display: jsx(() => (
<>
<div>
<h3>Hire an elf assistant</h3>
</div>
Packs {format(computedElfPackingSpeed.value)} presents per second
<div>
<br />
Amount: {formatWhole(helpers.elf.amount.value)}
</div>
<div>
<br />
Currently packing{" "}
{format(
Decimal.times(helpers.elf.amount.value, computedElfPackingSpeed.value)
)}{" "}
presents per second
</div>
<div>
Requires: {formatWhole(unref(helpers.elf.cost!))}{" "}
{helpers.elf.resource!.displayName}
</div>
</>
)),
style: {
width: "200px"
}
})) as ElfBuyable;
const loader = createBuyable(() => ({
visibility: () => showIf(upgrades.loaderUnlock.bought.value),
metalCost: computed(() => Decimal.pow(1.2, helpers.loader.amount.value).times(1e70)),
oilCost: computed(() => Decimal.pow(1.2, helpers.loader.amount.value).times(1e25)),
canPurchase(
this: GenericBuyable & {
metalCost: ComputedRef<DecimalSource>;
oilCost: ComputedRef<DecimalSource>;
}
) {
return (
Decimal.gte(metal.metal.value, this.metalCost.value) &&
Decimal.gte(oil.oil.value, this.oilCost.value)
);
},
onPurchase() {
metal.metal.value = Decimal.sub(metal.metal.value, this.metalCost.value);
oil.oil.value = Decimal.sub(oil.oil.value, this.oilCost.value);
this.amount.value = Decimal.add(this.amount.value, 1);
},
inverseCost() {
const metalAmount = Decimal.div(metal.metal.value, 1e40).log(1.5);
const oilAmount = Decimal.div(oil.oil.value, 1e20).log(1.5);
if (Decimal.isNaN(metalAmount) || Decimal.isNaN(oilAmount)) return Decimal.dZero;
return Decimal.min(metalAmount, oilAmount).floor().max(0);
},
display: jsx(() => (
<>
<div>
<h3>Build a loader</h3>
</div>
Loads {format(computedLoaderPackingSpeed.value)} presents per second
<div>
<br />
Amount: {formatWhole(helpers.loader.amount.value)}
</div>
<div>
<br />
Currently packing{" "}
{format(
Decimal.times(helpers.loader.amount.value, computedLoaderPackingSpeed.value)
)}{" "}
persents per second
</div>
<div>
Cost:{" "}
{displayCost(
metal.metal,
helpers.loader.metalCost.value,
metal.metal.displayName
)}
,{displayCost(oil.oil, helpers.loader.oilCost.value, oil.oil.displayName)}
</div>
</>
)),
style: {
width: "200px"
}
})) as ElfBuyable & {
metalCost: ComputedRef<DecimalSource>;
oilCost: ComputedRef<DecimalSource>;
};
const helpers = { elf, loader };
const upgrades = {
packingElf: createUpgrade(() => ({
display: {
title: "An Elf's Elf",
description: "Train an Elf to help you hire more Elves."
},
cost: 1000,
resource: totalPresentsResource,
style: {
width: "200px"
},
visibility() {
return showIf(Decimal.gte(packedPresents.value, 10) || this.bought.value);
},
onPurchase() {
main.days[12].recentlyUpdated.value = true;
elves.elves.packingElf.bought.value = true;
}
})),
loaderUnlock: createUpgrade(() => ({
display: {
title: "Heavy Machinery",
description:
"Those construction vehicles you have from building the workshop should be useful for loading presents too."
},
cost: 1000000,
resource: totalPresentsResource,
style: {
width: "200px"
},
visibility() {
return showIf(Decimal.gte(totalPresents.value, 10000) || this.bought.value);
}
})),
elfLevel: createUpgrade(() => ({
display: {
title: "Communal Assistance",
description: "Each elf level increases elf packing speed by 5%"
},
cost: 100000000,
resource: totalPresentsResource,
style: {
width: "200px"
},
visibility() {
return showIf(Decimal.gte(totalPresents.value, 10000000) || this.bought.value);
}
}))
};
const upgrades2 = {
ribbons: createUpgrade(() => ({
display: {
title: "Spare Bows",
description: "Each ribbon multiplies elf packing speed by 1.02x"
},
cost: 2e9,
resource: totalPresentsResource,
style: {
width: "200px"
},
visibility() {
return showIf(Decimal.gte(totalPresents.value, 1e9) || this.bought.value);
}
})),
assistantSynergy: createUpgrade(() => ({
display: {
title: "Loading Assistants",
description:
"Each elf assistant increases how much the loader can load per second by 5"
},
cost: 5e9,
resource: totalPresentsResource,
style: {
width: "200px"
},
visibility() {
return showIf(Decimal.gte(totalPresents.value, 4.8e9) || this.bought.value);
}
})),
manual: createUpgrade(() => ({
display: {
title: "DIY",
description:
"Each present manually packed gives 2 seconds of automatic present packing production"
},
cost: 1e10,
resource: totalPresentsResource,
style: {
width: "200px"
},
visibility() {
return showIf(Decimal.gte(totalPresents.value, 5e9) || this.bought.value);
}
}))
};
const packingMilestones: Record<string, GenericMilestone> = {
logBoost: createMilestone(() => ({
display: {
requirement: `25 ${packedPresents.displayName}`,
effectDisplay: "Trees size is raised to the 1.25th power"
},
shouldEarn: () => Decimal.gte(packedPresents.value, 25)
})),
boxBoost: createMilestone(() => ({
display: {
requirement: `120 ${packedPresents.displayName}`,
effectDisplay: "Create 10% more boxes"
},
shouldEarn: () => Decimal.gte(packedPresents.value, 120),
visibility: () => showIf(packingMilestones.logBoost.earned.value)
})),
clothBoost: createMilestone(() => ({
display: {
requirement: `600 ${packedPresents.displayName}`,
effectDisplay: "Sheep grow 10x as much wool"
},
shouldEarn: () => Decimal.gte(packedPresents.value, 600),
visibility: () => showIf(packingMilestones.boxBoost.earned.value)
})),
oilBoost: createMilestone(() => ({
display: {
requirement: `2,800 ${packedPresents.displayName}`,
effectDisplay: "Triple drill power"
},
shouldEarn: () => Decimal.gte(packedPresents.value, 2800),
visibility: () => showIf(packingMilestones.clothBoost.earned.value)
})),
packingBoost: createMilestone(() => ({
display: {
requirement: `10,000 ${packedPresents.displayName}`,
effectDisplay: "Multiply packing speed by log(presents)"
},
shouldEarn: () => Decimal.gte(packedPresents.value, 2800),
visibility: () => showIf(packingMilestones.clothBoost.earned.value)
})),
coalBoost: createMilestone(() => ({
display: {
requirement: `14,000 ${packedPresents.displayName}`,
effectDisplay: "Coal producer costs grow half as fast"
},
shouldEarn: () => Decimal.gte(packedPresents.value, 14000),
visibility: () => showIf(packingMilestones.oilBoost.earned.value)
})),
metalBoost: createMilestone(() => ({
display: {
requirement: `69,200 ${packedPresents.displayName}`,
effectDisplay: "Raise ore purity to the 1.5th power"
},
shouldEarn: () => Decimal.gte(packedPresents.value, 69200),
visibility: () => showIf(packingMilestones.coalBoost.earned.value)
})),
wrappingPaperBoost: createMilestone(() => ({
display: {
requirement: `340,000 ${packedPresents.displayName}`,
effectDisplay: "Double the strength of wrapping paper bonuses"
},
shouldEarn: () => Decimal.gte(packedPresents.value, 340000),
visibility: () => showIf(packingMilestones.metalBoost.earned.value)
})),
oreBoost: createMilestone(() => ({
display: {
requirement: `1,670,000 ${packedPresents.displayName}`,
effectDisplay: "Ore mining speed multiplies ore gain"
},
shouldEarn: () => Decimal.gte(packedPresents.value, 1670000),
visibility: () => showIf(packingMilestones.wrappingPaperBoost.earned.value)
})),
ribbonBoost: createMilestone(() => ({
display: {
requirement: `8,230,000 ${packedPresents.displayName}`,
effectDisplay: "Halve ribbon's dye cost"
},
shouldEarn: () => Decimal.gte(packedPresents.value, 8230000),
visibility: () => showIf(packingMilestones.oreBoost.earned.value)
})),
secondaryDyeBoost: createMilestone(() => ({
display: {
requirement: `40,400,000 ${packedPresents.displayName}`,
effectDisplay: "Double the second effect of each secondary dye"
},
shouldEarn: () => Decimal.gte(packedPresents.value, 40400000),
visibility: () => showIf(packingMilestones.ribbonBoost.earned.value)
})),
paperBoost: createMilestone(() => ({
display: {
requirement: `199,000,000 ${packedPresents.displayName}`,
effectDisplay: "Produce 10x as much paper"
},
shouldEarn: () => Decimal.gte(packedPresents.value, 199000000),
visibility: () => showIf(packingMilestones.secondaryDyeBoost.earned.value)
})),
primaryDyeBoost: createMilestone(() => ({
display: {
requirement: `977,000,000 ${packedPresents.displayName}`,
effectDisplay: "Quintuple primary dye gain"
},
shouldEarn: () => Decimal.gte(packedPresents.value, 977000000),
visibility: () => showIf(packingMilestones.paperBoost.earned.value)
})),
focusSelected: createMilestone(() => ({
display: {
requirement: `${format(4.2e9)} ${packedPresents.displayName}`,
effectDisplay: "Focusing elves always chooses the selected elf"
},
shouldEarn: () => Decimal.gte(packedPresents.value, 4.2e9),
visibility: () => showIf(packingMilestones.primaryDyeBoost.earned.value)
})),
moreFocus: createMilestone(() => ({
display: {
requirement: `${format(6.4e9)} ${packedPresents.displayName}`,
effectDisplay: "Each packing reset doubles the max elf focus multiplier"
},
shouldEarn: () => Decimal.gte(packedPresents.value, 6.4e9),
visibility: () => showIf(packingMilestones.focusSelected.earned.value)
}))
};
const { collapseMilestones, display: milestonesDisplay } =
createCollapsibleMilestones(packingMilestones);
const [generalTab, generalTabCollapsed] = createCollapsibleModifierSections(() => [
{
title: "Elf Packing Speed",
modifier: elfPackingSpeed,
base: 1,
unit: "/s"
},
{
title: "Loader Packing Speed",
modifier: loaderPackingSpeed,
base: 1000,
unit: "/s"
}
]);
const showModifiersModal = ref(false);
const modifiersModal = jsx(() => (
<ModalVue
modelValue={showModifiersModal.value}
onUpdate:modelValue={(value: boolean) => (showModifiersModal.value = value)}
v-slots={{
header: () => <h2>{name} Modifiers</h2>,
body: generalTab
}}
/>
));
const { trackerDisplay } = setUpDailyProgressTracker({
resource: packedPresents,
ignoreTotal: true,
goal: 8e9,
name,
day,
background: {
gradient: "packing-bar",
duration: "15s"
},
textColor: "var(--bought)",
modal: {
show: showModifiersModal,
display: modifiersModal
}
});
globalBus.on("update", diff => {
if (Decimal.lt(main.day.value, day)) {
return;
}
if (Decimal.gte(packingProgress.value, 1)) {
packingProgress.value = 1;
} else {
packingProgress.value = Decimal.add(packingProgress.value, diff);
if (packPresent.isHolding.value) {
packPresent.onClick();
}
}
if (Decimal.lt(remainingSize.value, 0)) {
return;
}
packedPresents.value = Decimal.add(
Decimal.times(helpers.elf.amount.value, computedElfPackingSpeed.value),
Decimal.times(helpers.loader.amount.value, computedLoaderPackingSpeed.value)
)
.times(diff)
.plus(packedPresents.value)
.min(currentMaxPresents.value);
});
return {
name,
day,
color,
packedPresents,
bestPresents,
totalPresents,
packingResets,
packingProgress,
helpers,
upgrades,
upgrades2,
packingMilestones,
collapseMilestones,
generalTabCollapsed,
minWidth: 700,
display: jsx(() => (
<>
{render(trackerDisplay)}
<SpacerVue />
<MainDisplayVue resource={packedPresents} color={color} style="margin-bottom: 0" />
{packingResets.value === 0 ? null : (
<div>
<SpacerVue />
You've restarted packing {formatWhole(packingResets.value)} times,
<br />
packing a total of{" "}
<ResourceVue resource={totalPresentsResource} color={color} /> presents
</div>
)}
<SpacerVue />
<p>
The bag has {format(remainingSize.value)} m<sup>3</sup> empty room
</p>
<SpacerVue />
{render(resetPacking)}
{render(packPresent)}
{main.day.value === day - 1 ? <SpacerVue /> : null}
{renderRow(...Object.values(helpers))}
<SpacerVue />
{renderGrid(Object.values(upgrades), Object.values(upgrades2))}
<SpacerVue />
{milestonesDisplay()}
</>
)),
minimizedDisplay: jsx(() => (
<div>
{name}{" "}
<span class="desc">
{formatWhole(packedPresents.value)} {packedPresents.displayName}
</span>
</div>
))
};
});
export default layer;
function displayCost(
res: Resource<DecimalSource> | Resource<DecimalSource>[],
cost: DecimalSource,
label: string
) {
const affordable = (isArray(res) ? res : [res]).every(res => Decimal.gte(res.value, cost));
return (
<span class={affordable ? "" : "unaffordable"}>
{format(cost)} {label}
</span>
);
}

View file

@ -26,6 +26,7 @@ import coal from "./coal";
import dyes from "./dyes";
import elves, { ElfBuyable } from "./elves";
import management from "./management";
import packing from "./packing";
import plastic from "./plastic";
import reindeer from "./reindeer";
import ribbon from "./ribbon";
@ -131,6 +132,9 @@ const layer = createLayer(id, function (this: BaseLayer) {
if (["Peppermint", "Twinkle", "Cocoa", "Frosty"].includes(options.elfName)) {
cost = cost.mul(1e31);
}
if (["Jingle"].includes(options.elfName)) {
cost = cost.mul(1e123);
}
if (management.elfTraining.paperElfTraining.milestones[0].earned.value) {
cost = Decimal.div(cost, sumBooks.value.max(1));
}
@ -156,6 +160,9 @@ const layer = createLayer(id, function (this: BaseLayer) {
if (["Peppermint", "Twinkle", "Cocoa", "Frosty"].includes(options.elfName)) {
v = v.div(1e31);
}
if (["Jingle"].includes(options.elfName)) {
v = v.div(1e123);
}
v = v.log(scaling);
v = v.div(Decimal.pow(0.95, paperBook.totalAmount.value));
@ -305,6 +312,12 @@ const layer = createLayer(id, function (this: BaseLayer) {
buyableName: "Plastic Buyables",
visibility: () => showIf(plastic.masteryEffectActive.value)
});
const packingBook = createBook({
name: "The Tetris Effect",
elfName: "Jingle",
buyableName: "Elf Assistants",
visibility: () => showIf(packing.upgrades.packingElf.bought.value)
})
const books = {
cuttersBook,
plantersBook,
@ -324,7 +337,8 @@ const layer = createLayer(id, function (this: BaseLayer) {
metalBook,
primaryDyeBook,
secondaryDyeBook,
plasticBook
plasticBook,
packingBook
};
const sumBooks = computed(() =>
Object.values(books).reduce((acc, curr) => acc.add(curr.amount.value), new Decimal(0))
@ -425,6 +439,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
description: "Sunshine Wrapping Paper",
enabled: () => Decimal.gte(wrappingPaper.boosts.sunshine1.value, 2)
})),
createMultiplicativeModifier(() => ({
multiplier: 10,
description: "199,000,000 Presents Packed",
enabled: packing.packingMilestones.paperBoost.earned
})),
reindeer.reindeer.prancer.modifier
]) as WithRequired<Modifier, "description" | "revert">;
const ashCost = createSequentialModifier(() => [
@ -513,7 +532,8 @@ const layer = createLayer(id, function (this: BaseLayer) {
metalBook: { amount: persistent<DecimalSource>(0) },
primaryDyeBook: { amount: persistent<DecimalSource>(0) },
secondaryDyeBook: { amount: persistent<DecimalSource>(0) },
plasticBook: { amount: persistent<DecimalSource>(0) }
plasticBook: { amount: persistent<DecimalSource>(0) },
packingBook: { amount: persistent<DecimalSource>(0) }
},
upgrades: {
clothUpgrade: { bought: persistent<boolean>(false) },

View file

@ -141,9 +141,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
zIndex: 4
},
canClick: () => Decimal.eq(currCooldown.value, 0),
onClick() {
focus();
}
onClick: focus
}));
const cooldown = createSequentialModifier(() => [

View file

@ -22,6 +22,7 @@ import { main } from "../projEntry";
import cloth from "./cloth";
import dyes from "./dyes";
import elves from "./elves";
import packing from "./packing"
const id = "ribbon";
const day = 16;
@ -38,7 +39,7 @@ const layer = createLayer(id, () => {
[dyes.dyes.orange, dyes.dyes.green, dyes.dyes.purple].includes(currentDyeType.value)
? 2e6
: 1e13
)
).mul(packing.packingMilestones.ribbonBoost.earned.value ? 0.5 : 1)
);
const currentDyeType = computed(
() =>

View file

@ -1002,6 +1002,9 @@ const layer = createLayer(id, function (this: BaseLayer) {
citiesCompleted.value,
Decimal.times(computedMetaSolvingSpeed.value, diff)
).min(5000000);
if (Decimal.isNaN(citiesCompleted.value)) {
citiesCompleted.value = 50;
}
if (metaMilestones[0].earned.value) {
management.classrooms.amount.value = Decimal.add(
@ -1149,7 +1152,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
<>
You've solved all cities on Earth!
<br />
<span style="text-decoration: line-through">
<span style="text-decoration: line-through; font-size: smaller">
(and proved the travelling salesman problem to be O(1))
</span>
</>

View file

@ -79,6 +79,23 @@
}
}
@keyframes packing-bar {
from {
background: 0 0 / 170px 170px repeat repeating-linear-gradient(-45deg,
rgb(255, 76, 76) 0 10px, white 10px 20px,
rgb(65, 255, 95) 20px 30px, white 30px 40px,
rgb(76, 76, 255) 40px 50px, white 50px 60px
);
}
to {
background: 170px 0 / 170px 170px repeat repeating-linear-gradient(-45deg,
rgb(255, 76, 76) 0 10px, white 10px 20px,
rgb(65, 255, 95) 20px 30px, white 30px 40px,
rgb(76, 76, 255) 40px 50px, white 50px 60px
);
}
}
@keyframes reindeer-bar {
from {
background: 0 0 / 28px 28px repeat

View file

@ -43,6 +43,7 @@ import factory from "./factory";
import reindeer from "./reindeer";
import sleigh from "./sleigh";
import routing from "./routing";
import packing from "./packing";
const id = "trees";
const day = 1;
@ -590,6 +591,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
exponent: 1.05,
description: "Un-Processing",
enabled: paper.upgrades2.treeUpgrade.bought
})),
createExponentialModifier(() => ({
exponent: 1.25,
description: "25 Presents Packed",
enabled: packing.packingMilestones.logBoost.earned
}))
]);

View file

@ -17,6 +17,7 @@ import { main } from "../projEntry";
import { default as dyes, type enumColor } from "./dyes";
import elves from "./elves";
import toys from "./toys";
import packing from "./packing"
const id = "wrappingPaper";
const day = 15;
@ -259,21 +260,22 @@ const layer = createLayer(id, () => {
]
})
};
const packingBoost = computed(() => packing.packingMilestones.wrappingPaperBoost.earned.value ? 2 : 1)
const boosts = {
christmas1: computed(() =>
main.isMastery.value ? 1 : Decimal.add(wrappingPaper.christmas.buyable.amount.value, 1)
main.isMastery.value ? 1 : Decimal.add(wrappingPaper.christmas.buyable.amount.value, 1).mul(packingBoost.value)
), // Probably not the best way to do this, but it works
rainbow1: computed(() =>
main.isMastery.value ? 1 : Decimal.pow(2, wrappingPaper.rainbow.buyable.amount.value)
main.isMastery.value ? 1 : Decimal.pow(2, wrappingPaper.rainbow.buyable.amount.value).mul(packingBoost.value)
),
jazzy1: computed(() =>
main.isMastery.value ? 1 : Decimal.add(wrappingPaper.jazzy.buyable.amount.value, 1)
main.isMastery.value ? 1 : Decimal.add(wrappingPaper.jazzy.buyable.amount.value, 1).mul(packingBoost.value)
),
sunshine1: computed(() =>
main.isMastery.value ? 1 : Decimal.add(wrappingPaper.sunshine.buyable.amount.value, 1)
main.isMastery.value ? 1 : Decimal.add(wrappingPaper.sunshine.buyable.amount.value, 1).mul(packingBoost.value)
),
ocean1: computed(() =>
main.isMastery.value ? 1 : Decimal.pow(1.5, wrappingPaper.ocean.buyable.amount.value)
main.isMastery.value ? 1 : Decimal.pow(1.5, wrappingPaper.ocean.buyable.amount.value).mul(packingBoost.value)
),
beach1: computed(() =>
main.isMastery.value
@ -281,6 +283,7 @@ const layer = createLayer(id, () => {
: Decimal.add(wrappingPaper.beach.buyable.amount.value, 1)
.log10()
.add(1)
.mul(packingBoost.value)
.pow(toys.milestones.milestone3.earned.value ? 1.6 : 1)
)
};

View file

@ -4,8 +4,10 @@ import {
Component,
GatherProps,
GenericComponent,
jsx
jsx,
Visibility
} from "features/feature";
import { createParticles } from "features/particles/particles";
import { BaseLayer, createLayer, GenericLayer, layers } from "game/layers";
import { isPersistent, Persistent, persistent } from "game/persistence";
import type { PlayerData } from "game/player";
@ -14,10 +16,10 @@ import { format, formatTime } from "util/bignum";
import { Computable, convertComputable, ProcessedComputable } from "util/computed";
import { createLazyProxy } from "util/proxies";
import { save } from "util/save";
import { renderRow, VueFeature } from "util/vue";
import type { Ref } from "vue";
import { computed, ref, unref } from "vue";
import { render, renderRow, VueFeature } from "util/vue";
import { computed, Ref, ref, unref, watchEffect } from "vue";
import "./advent.css";
import { credits } from "./credits";
import Day from "./Day.vue";
import boxes from "./layers/boxes";
import cloth from "./layers/cloth";
@ -25,19 +27,21 @@ import coal from "./layers/coal";
import dyes from "./layers/dyes";
import elves from "./layers/elves";
import factory from "./layers/factory";
import presentSymbol from "./layers/factory-components/present.svg";
import letters from "./layers/letters";
import management from "./layers/management";
import metal from "./layers/metal";
import oil from "./layers/oil";
import packing from "./layers/packing";
import paper from "./layers/paper";
import plastic from "./layers/plastic";
import reindeer from "./layers/reindeer";
import ribbon from "./layers/ribbon";
import routing from "./layers/routing";
import sleigh from "./layers/sleigh";
import toys from "./layers/toys";
import trees from "./layers/trees";
import workshop from "./layers/workshop";
import sleigh from "./layers/sleigh";
import wrappingPaper from "./layers/wrapping-paper";
import boxesSymbol from "./symbols/cardboardBox.png";
import clothSymbol from "./symbols/cloth.png";
@ -46,6 +50,7 @@ import dyesSymbol from "./symbols/dyes.png";
import elfSymbol from "./symbols/elf.png";
import managementSymbol from "./symbols/elfManagement.png";
import factorySymbol from "./symbols/gears.png";
import routingSymbol from "./symbols/gps.png";
import lettersSymbol from "./symbols/letterbox.png";
import metalSymbol from "./symbols/metal.png";
import oilSymbol from "./symbols/oil.png";
@ -54,14 +59,15 @@ import plasticSymbol from "./symbols/plastic.png";
import presentsSymbol from "./symbols/presents.png";
import reindeerSymbol from "./symbols/reindeer.png";
import ribbonsSymbol from "./symbols/ribbons.png";
import packingSymbol from "./symbols/santasSack.png";
import sleighSymbol from "./symbols/sleigh.png";
import snowflakeSymbol from "./symbols/snowflake.svg";
import workshopSymbol from "./symbols/sws.png";
import advFactorySymbol from "./symbols/teddyBear.png";
import treeSymbol from "./symbols/tree.png";
import toysSymbol from "./symbols/truck.png";
import advManagementSymbol from "./symbols/workshopMansion.png";
import wrappingPaperSymbol from "./symbols/wrappingPaper.png";
import sleighSymbol from "./symbols/sleigh.png";
import routingSymbol from "./symbols/gps.png";
export interface Day extends VueFeature {
day: number;
@ -73,10 +79,13 @@ export interface Day extends VueFeature {
opened: Persistent<boolean>;
recentlyUpdated: Ref<boolean>; // Has the tab recieved an update since the player last opened it?
shouldNotify: ProcessedComputable<boolean>;
visibility?: Visibility;
}
export const main = createLayer("main", function (this: BaseLayer) {
const day = persistent<number>(1);
const hasWon = persistent<boolean>(false);
const timeUntilNewDay = computed(
() => (+new Date(new Date().getFullYear(), 11, day.value) - player.time) / 1000
);
@ -86,6 +95,90 @@ export const main = createLayer("main", function (this: BaseLayer) {
const loreTitle = ref<string>("");
const loreBody = ref<CoercableComponent | undefined>();
const creditsOpen = ref<boolean>(false);
// I don't understand how this works
const particles = createParticles(() => ({
boundingRect: ref<null | DOMRect>(null),
onContainerResized(boundingRect) {
this.boundingRect.value = boundingRect;
},
style: "z-index: -1"
}));
const emitter = particles.addEmitter({
emit: false,
autoUpdate: true,
lifetime: { min: 10, max: 10 },
emitterLifetime: -1,
pos: { x: 0, y: 0 },
frequency: 0.05,
maxParticles: 1000,
behaviors: [
{
type: "alphaStatic",
config: {
alpha: 1
}
},
{
type: "scaleStatic",
config: {
min: 1,
max: 1
}
},
{
type: "moveSpeed",
config: {
speed: {
list: [
{
value: 200,
time: 0
},
{
value: 100,
time: 1
}
],
isStepped: false
}
}
},
{
type: "rotationStatic",
config: {
min: 70,
max: 110
}
},
{
type: "spawnShape",
config: {
type: "rect",
data: {
x: 0,
y: 0,
w: 1600,
h: 1
}
}
},
{
type: "textureSingle",
config: {
texture: snowflakeSymbol
}
}
]
});
watchEffect(() => {
const shouldEmit = day.value === 25;
emitter.then(e => (e.emit = shouldEmit));
});
const currentlyMastering = computed(() =>
isMastery.value
? Object.values(layers).find(
@ -168,6 +261,7 @@ export const main = createLayer("main", function (this: BaseLayer) {
story: string;
completedStory: string;
masteredStory: string;
visibility?: Visibility;
}
): Day {
const opened = persistent<boolean>(false);
@ -197,7 +291,8 @@ export const main = createLayer("main", function (this: BaseLayer) {
story,
completedStory,
masteredStory,
recentlyUpdated
recentlyUpdated,
visibility
} = this;
const mastered: Ref<boolean> =
@ -210,6 +305,7 @@ export const main = createLayer("main", function (this: BaseLayer) {
recentlyUpdated,
shouldNotify,
mastered,
visibility,
onOpenLore() {
const completed = main.day.value > day;
loreScene.value = completed ? day - 1 : -1;
@ -230,6 +326,7 @@ export const main = createLayer("main", function (this: BaseLayer) {
showLoreModal.value = true;
},
onOpenLayer() {
if (day == 25) return;
recentlyUpdated.value = false;
// 1468 is because two tabs with minWidth of 700px plus the minimized calendar of 60px plus 2 dividers of 4px each
if (window.matchMedia("(min-width: 1468px)").matches) {
@ -248,11 +345,14 @@ export const main = createLayer("main", function (this: BaseLayer) {
layers[layer ?? "trees"]!.minimized.value = false;
},
onUnlockLayer() {
if (layer != null) {
if (layer != null || day == 25) {
opened.value = true;
setTimeout(() => {
loreScene.value = -1;
loreTitle.value = unref(layers[layer ?? "trees"]?.name ?? "");
loreTitle.value =
day == 25
? "The End!"
: unref(layers[layer ?? "trees"]?.name ?? "");
loreBody.value = story;
if (player.autoPause) player.devSpeed = null;
showLoreModal.value = true;
@ -511,11 +611,22 @@ export const main = createLayer("main", function (this: BaseLayer) {
createDay(() => ({
day: 24,
shouldNotify: false,
layer: null, // "packing the presents"
symbol: "",
story: "",
completedStory: "",
layer: "packing",
symbol: packingSymbol,
story: "You're almost done! The last step is to load up the sleigh with all the presents and get ready to go! You're going to need to pack a lot of presents, so you'll need to make sure you pack them tightly enough. Good Luck!",
completedStory:
"At last, you've crammed in all the presents Santa needs. Santa can take it from here. Good Job!",
masteredStory: ""
})),
createDay(() => ({
day: 25,
shouldNotify: false,
layer: null, // credits
symbol: snowflakeSymbol,
story: `It's Christmas. Thanks to your efforts, Santa has delivered all the presents to people all over the world. That is, all but one... <br><br> <div style='text-align: center'><img class='present-clickable' onclick ='layers.main.showLoreModal.value = false; layers.main.creditsOpen.value = true' src='${presentSymbol}' /><br>Open your present</div><br/>`,
completedStory: "",
masteredStory: "",
visibility: Visibility.None
}))
];
@ -548,6 +659,16 @@ export const main = createLayer("main", function (this: BaseLayer) {
}
}
watchEffect(() => {
if (day.value === 25 && showLoreModal.value === false && !hasWon.value) {
loreScene.value = -1;
loreTitle.value = "Merry Christmas!";
loreBody.value = days[day.value - 1].story;
showLoreModal.value = true;
hasWon.value = true;
}
});
return {
name: "Calendar",
days,
@ -557,6 +678,7 @@ export const main = createLayer("main", function (this: BaseLayer) {
loreScene,
loreTitle,
loreBody,
particles,
showLoreModal,
completeDay,
completeMastery,
@ -566,6 +688,9 @@ export const main = createLayer("main", function (this: BaseLayer) {
swappingMastery,
currentlyMastering,
masteredDays,
creditsOpen,
credits,
hasWon,
display: jsx(() => (
<>
{player.devSpeed === 0 ? <div>Game Paused</div> : null}
@ -596,6 +721,19 @@ export const main = createLayer("main", function (this: BaseLayer) {
)
.map((days: Day[]) => renderRow(...days))}
</div>
{hasWon.value ? (
<>
<Spacer />
<button
class="button"
style="font-size: xx-large"
onClick={() => (creditsOpen.value = true)}
>
Open Credits
</button>
</>
) : null}
{render(particles)}
</>
))
};
@ -629,7 +767,8 @@ export const getInitialLayers = (
factory,
reindeer,
sleigh,
routing
routing,
packing
];
/**

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

View file

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="50"
height="50"
viewBox="0 0 13.229166 13.229167"
version="1.1"
id="svg5"
inkscape:export-filename=".png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="false" />
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
id="rect234"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.356532;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:6;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
d="M 6.2022054,1.0314616 V 2.4411947 L 5.0069295,1.7507975 4.5883504,2.4758179 6.2022054,3.4070272 V 5.7913778 L 4.1351481,4.5976522 4.1356649,2.7342 H 3.2985067 L 3.2990234,4.1149943 2.0779093,3.4101278 1.712557,4.0431641 2.9217855,4.7413127 1.7156576,5.4384277 2.1337199,6.1629313 3.7589437,5.2244873 5.7464193,6.3722209 3.7558431,7.5215047 2.1301025,6.5830607 1.7120402,7.3080811 2.9186849,8.0046794 1.7089396,8.702828 2.0748088,9.336381 3.2954061,8.6315145 V 10.011792 H 4.1325643 L 4.1320475,8.1483398 6.2280436,6.9380778 v 2.4463623 l -1.6252238,0.9384439 0.4185791,0.724504 1.2066447,-0.696599 v 1.396814 H 6.9592651 V 10.33787 L 8.154541,11.028267 8.5731201,10.303247 6.9592651,9.3720378 V 6.9876872 L 9.0263224,8.1814128 9.0258057,10.044865 H 9.8629639 L 9.8624471,8.6640706 11.083561,9.3689372 11.448914,8.7359009 10.239685,8.0377523 11.445813,7.3406372 11.027751,6.6161336 9.4025269,7.5545776 7.4150513,6.4068441 9.4056274,5.2575602 11.031368,6.1960042 11.44943,5.4709839 10.242786,4.7743856 11.452531,4.076237 11.086662,3.4426839 9.8660645,4.1475505 V 2.7672729 H 9.0289062 L 9.029423,4.6307251 6.9334269,5.8409871 V 3.3946248 L 8.5586507,2.4561808 8.1400716,1.7316772 6.9334269,2.4282756 v -1.396814 z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View file

@ -21,7 +21,7 @@ import type {
import { processComputable } from "util/computed";
import { createLazyProxy } from "util/proxies";
import { coerceComponent, isCoercableComponent } from "util/vue";
import type { Ref } from "vue";
import { isReadonly, Ref } from "vue";
import { computed, unref } from "vue";
export const BuyableType = Symbol("Buyable");
@ -155,14 +155,18 @@ export function createBuyable<T extends BuyableOptions>(
return;
}
const cost = unref(genericBuyable.cost);
if (genericBuyable.cost != null && genericBuyable.resource != null) {
if (
genericBuyable.cost != null &&
genericBuyable.resource != null &&
!isReadonly(genericBuyable.resource)
) {
genericBuyable.resource.value = Decimal.sub(
genericBuyable.resource.value,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
cost!
);
genericBuyable.amount.value = Decimal.add(genericBuyable.amount.value, 1);
}
genericBuyable.amount.value = Decimal.add(genericBuyable.amount.value, 1);
genericBuyable.onPurchase?.(cost);
};
processComputable(buyable as T, "display");

View file

@ -134,7 +134,8 @@ export async function loadSave(playerObj: Partial<PlayerData>): Promise<void> {
setInterval(() => {
if (
player.autosave &&
(layers as any).main.days[(layers as any).main.day.value - 1].opened.value
((layers as any).main.day.value >= 25 ||
(layers as any).main.days[(layers as any).main.day.value - 1].opened.value)
) {
save();
}
@ -142,7 +143,8 @@ setInterval(() => {
window.onbeforeunload = () => {
if (
player.autosave &&
((layers as any).main.days[(layers as any).main.day.value - 1].opened.value ||
((layers as any).main.day.value >= 25 ||
(layers as any).main.days[(layers as any).main.day.value - 1].opened.value ||
import.meta.env.DEV)
) {
save();