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> </div>
</template> </template>
</Modal> </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> </div>
</template> </template>
@ -52,6 +81,7 @@ import Scene from "data/Scene.vue";
import type { GenericLayer } from "game/layers"; import type { GenericLayer } from "game/layers";
import { layers } from "game/layers"; import { layers } from "game/layers";
import player from "game/player"; import player from "game/player";
import { convertComputable } from "util/computed";
import { computeOptionalComponent } from "util/vue"; import { computeOptionalComponent } from "util/vue";
import { computed, toRef, unref } from "vue"; import { computed, toRef, unref } from "vue";
import Layer from "./Layer.vue"; import Layer from "./Layer.vue";

View file

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

View file

@ -98,7 +98,7 @@ const isPaused = computed({
}); });
const canAutoSave = 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(() => ( const autosaveTitle = jsx(() => (

View file

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

View file

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

View file

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

View file

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

View file

@ -44,6 +44,7 @@ import plastic from "./plastic";
import reindeer from "./reindeer"; import reindeer from "./reindeer";
import trees from "./trees"; import trees from "./trees";
import wrappingPaper from "./wrapping-paper"; import wrappingPaper from "./wrapping-paper";
import packing from "./packing";
interface BetterFertilizerUpgOptions { interface BetterFertilizerUpgOptions {
canAfford: () => boolean; 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, 100)) v = Decimal.pow(v, 2).div(100);
if (Decimal.gte(v, 10000)) v = Decimal.pow(v, 2).div(10000); if (Decimal.gte(v, 10000)) v = Decimal.pow(v, 2).div(10000);
v = Decimal.pow(0.95, paper.books.kilnBook.totalAmount.value).times(v); 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) { 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)); 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, 10000)) v = Decimal.mul(v, 10000).root(2);
if (Decimal.gte(v, 100)) v = Decimal.mul(v, 100).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, 100)) v = Decimal.pow(v, 2).div(100);
if (Decimal.gte(v, 10000)) v = Decimal.pow(v, 2).div(10000); if (Decimal.gte(v, 10000)) v = Decimal.pow(v, 2).div(10000);
v = Decimal.pow(0.95, paper.books.coalDrillBook.totalAmount.value).times(v); 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) { if (management.elfTraining.fertilizerElfTraining.milestones[2].earned.value) {
cost = cost.div(Decimal.add(trees.totalLogs.value, Math.E).ln()); 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) { if (management.elfTraining.fertilizerElfTraining.milestones[2].earned.value) {
x = Decimal.mul(x, Decimal.add(trees.totalLogs.value, Math.E).ln()); 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)); 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, 10000)) v = Decimal.mul(v, 10000).root(2);
if (Decimal.gte(v, 100)) v = Decimal.mul(v, 100).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 factory from "./factory";
import reindeer from "./reindeer"; import reindeer from "./reindeer";
import routing from "./routing"; import routing from "./routing";
import packing from "./packing";
interface Dye { interface Dye {
name: string; name: string;
@ -171,20 +172,21 @@ const layer = createLayer(id, function (this: BaseLayer) {
multiplier: 2, multiplier: 2,
description: "Wetter Dyes", description: "Wetter Dyes",
enabled: upgrades.yellowDyeUpg.bought enabled: upgrades.yellowDyeUpg.bought
})) })),
);
modifiers.push(
createMultiplicativeModifier(() => ({ createMultiplicativeModifier(() => ({
multiplier: () => Decimal.add(cloth.cloth.value, Math.E).ln(), multiplier: () => Decimal.add(cloth.cloth.value, Math.E).ln(),
description: "Gingersnap Level 1", description: "Gingersnap Level 1",
enabled: management.elfTraining.clothElfTraining.milestones[0].earned enabled: management.elfTraining.clothElfTraining.milestones[0].earned
})) })),
);
modifiers.push(
createMultiplicativeModifier(() => ({ createMultiplicativeModifier(() => ({
multiplier: 2, multiplier: 2,
description: "Carol Level 1", description: "Carol Level 1",
enabled: management.elfTraining.dyeElfTraining.milestones[0].earned 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) Decimal.add(dyes.orange.amount.value, 1)
.log2() .log2()
.plus(1) .plus(1)
.mul(packing.packingMilestones.secondaryDyeBoost.earned.value ? 2 : 1)
.pow(oil.row3Upgrades[1].bought.value ? 2.5 : 1) .pow(oil.row3Upgrades[1].bought.value ? 2.5 : 1)
), ),
green1: computed(() => green1: computed(() =>
@ -739,6 +742,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
Decimal.add(dyes.green.amount.value, 1) Decimal.add(dyes.green.amount.value, 1)
.log2() .log2()
.plus(1) .plus(1)
.mul(packing.packingMilestones.secondaryDyeBoost.earned.value ? 2 : 1)
.pow(upgrades.coalUpg.bought.value ? 2 : 1) .pow(upgrades.coalUpg.bought.value ? 2 : 1)
), ),
purple1: computed(() => purple1: computed(() =>
@ -746,7 +750,12 @@ const layer = createLayer(id, function (this: BaseLayer) {
.pow(upgrades.coalUpg.bought.value ? 1.2 : 1) .pow(upgrades.coalUpg.bought.value ? 1.2 : 1)
.pow(management.elfTraining.clothElfTraining.milestones[3].earned.value ? 1.1 : 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(() => black1: computed(() =>
Decimal.pow(2, Decimal.add(dyes.black.amount.value, 1).log2().sqrt()) Decimal.pow(2, Decimal.add(dyes.black.amount.value, 1).log2().sqrt())
.pow(upgrades.coalUpg.bought.value ? 1.2 : 1) .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 dyes, { enumColor } from "./dyes";
import ribbon from "./ribbon"; import ribbon from "./ribbon";
import letters from "./letters"; import letters from "./letters";
import packing from "./packing";
export interface ElfBuyable extends GenericBuyable { export interface ElfBuyable extends GenericBuyable {
/** The inverse function of the cost formula, used to calculate the maximum amount that can be bought by elves. */ /** 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 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(() => [ const [generalTab, generalTabCollapsed] = createCollapsibleModifierSections(() => [
{ {
@ -590,6 +608,13 @@ const layer = createLayer(id, function (this: BaseLayer) {
base: 10, base: 10,
unit: "/s", unit: "/s",
visible: plastic.masteryEffectActive visible: plastic.masteryEffectActive
},
{
title: "Jingle Auto-Buy Frequency",
modifier: packingCooldown,
base: 10,
unit: "/s",
visible: packing.upgrades.packingElf.bought
} }
]); ]);
const showModifiersModal = ref(false); const showModifiersModal = ref(false);
@ -732,7 +757,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
onPurchase() { onPurchase() {
options.onPurchase?.(); options.onPurchase?.();
if ( if (
!["Peppermint", "Twinkle", "Cocoa", "Frosty", "Carol"].includes( !["Peppermint", "Twinkle", "Cocoa", "Frosty", "Carol", "Jingle"].includes(
options.name options.name
) )
) { ) {
@ -1013,6 +1038,20 @@ const layer = createLayer(id, function (this: BaseLayer) {
buyMax: () => management.elfTraining.plasticElfTraining.milestones[4].earned.value buyMax: () => management.elfTraining.plasticElfTraining.milestones[4].earned.value
}); });
const wrappingPaperElves = [dyeElf, plasticElf]; 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 = { const elves = {
cuttersElf, cuttersElf,
plantersElf, plantersElf,
@ -1031,7 +1070,8 @@ const layer = createLayer(id, function (this: BaseLayer) {
oilElf, oilElf,
metalElf, metalElf,
dyeElf, dyeElf,
plasticElf plasticElf,
packingElf
}; };
const totalElves = computed(() => Object.values(elves).filter(elf => elf.bought.value).length); 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), buyProgress: persistent<DecimalSource>(0),
amountOfTimesDone: persistent<number>(0), amountOfTimesDone: persistent<number>(0),
bought: persistent<boolean>(false) bought: persistent<boolean>(false)
},
packingElf: {
buyProgress: persistent<DecimalSource>(0),
amountOftimesDone: persistent<number>(0),
bought: persistent<boolean>(false)
} }
}, },
milestones: [ milestones: [
@ -1333,7 +1378,8 @@ const layer = createLayer(id, function (this: BaseLayer) {
fireElves, fireElves,
plasticElves, plasticElves,
managementElves, managementElves,
managementElves2.concat(wrappingPaperElves) managementElves2.concat(wrappingPaperElves),
[packingElf]
)} )}
</div> </div>
{milestonesDisplay()} {milestonesDisplay()}

View file

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

View file

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

View file

@ -45,6 +45,7 @@ import toys from "./toys";
import factory from "./factory"; import factory from "./factory";
import reindeer from "./reindeer"; import reindeer from "./reindeer";
import routing from "./routing"; import routing from "./routing";
import packing from "./packing"
const id = "oil"; const id = "oil";
const day = 9; const day = 9;
@ -924,6 +925,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
description: "Red Dye", description: "Red Dye",
enabled: dyes.masteryEffectActive enabled: dyes.masteryEffectActive
})), })),
createMultiplicativeModifier(() => ({
multiplier: 3,
description: "2,800 Presents Packaged",
enabled: packing.packingMilestones.oilBoost.earned
})),
createExponentialModifier(() => ({ createExponentialModifier(() => ({
exponent: 1.2, exponent: 1.2,
description: "Diamond-tipped drills", 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 dyes from "./dyes";
import elves, { ElfBuyable } from "./elves"; import elves, { ElfBuyable } from "./elves";
import management from "./management"; import management from "./management";
import packing from "./packing";
import plastic from "./plastic"; import plastic from "./plastic";
import reindeer from "./reindeer"; import reindeer from "./reindeer";
import ribbon from "./ribbon"; import ribbon from "./ribbon";
@ -131,6 +132,9 @@ const layer = createLayer(id, function (this: BaseLayer) {
if (["Peppermint", "Twinkle", "Cocoa", "Frosty"].includes(options.elfName)) { if (["Peppermint", "Twinkle", "Cocoa", "Frosty"].includes(options.elfName)) {
cost = cost.mul(1e31); cost = cost.mul(1e31);
} }
if (["Jingle"].includes(options.elfName)) {
cost = cost.mul(1e123);
}
if (management.elfTraining.paperElfTraining.milestones[0].earned.value) { if (management.elfTraining.paperElfTraining.milestones[0].earned.value) {
cost = Decimal.div(cost, sumBooks.value.max(1)); 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)) { if (["Peppermint", "Twinkle", "Cocoa", "Frosty"].includes(options.elfName)) {
v = v.div(1e31); v = v.div(1e31);
} }
if (["Jingle"].includes(options.elfName)) {
v = v.div(1e123);
}
v = v.log(scaling); v = v.log(scaling);
v = v.div(Decimal.pow(0.95, paperBook.totalAmount.value)); v = v.div(Decimal.pow(0.95, paperBook.totalAmount.value));
@ -305,6 +312,12 @@ const layer = createLayer(id, function (this: BaseLayer) {
buyableName: "Plastic Buyables", buyableName: "Plastic Buyables",
visibility: () => showIf(plastic.masteryEffectActive.value) 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 = { const books = {
cuttersBook, cuttersBook,
plantersBook, plantersBook,
@ -324,7 +337,8 @@ const layer = createLayer(id, function (this: BaseLayer) {
metalBook, metalBook,
primaryDyeBook, primaryDyeBook,
secondaryDyeBook, secondaryDyeBook,
plasticBook plasticBook,
packingBook
}; };
const sumBooks = computed(() => const sumBooks = computed(() =>
Object.values(books).reduce((acc, curr) => acc.add(curr.amount.value), new Decimal(0)) 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", description: "Sunshine Wrapping Paper",
enabled: () => Decimal.gte(wrappingPaper.boosts.sunshine1.value, 2) 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 reindeer.reindeer.prancer.modifier
]) as WithRequired<Modifier, "description" | "revert">; ]) as WithRequired<Modifier, "description" | "revert">;
const ashCost = createSequentialModifier(() => [ const ashCost = createSequentialModifier(() => [
@ -513,7 +532,8 @@ const layer = createLayer(id, function (this: BaseLayer) {
metalBook: { amount: persistent<DecimalSource>(0) }, metalBook: { amount: persistent<DecimalSource>(0) },
primaryDyeBook: { amount: persistent<DecimalSource>(0) }, primaryDyeBook: { amount: persistent<DecimalSource>(0) },
secondaryDyeBook: { 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: { upgrades: {
clothUpgrade: { bought: persistent<boolean>(false) }, clothUpgrade: { bought: persistent<boolean>(false) },

View file

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

View file

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

View file

@ -1002,6 +1002,9 @@ const layer = createLayer(id, function (this: BaseLayer) {
citiesCompleted.value, citiesCompleted.value,
Decimal.times(computedMetaSolvingSpeed.value, diff) Decimal.times(computedMetaSolvingSpeed.value, diff)
).min(5000000); ).min(5000000);
if (Decimal.isNaN(citiesCompleted.value)) {
citiesCompleted.value = 50;
}
if (metaMilestones[0].earned.value) { if (metaMilestones[0].earned.value) {
management.classrooms.amount.value = Decimal.add( management.classrooms.amount.value = Decimal.add(
@ -1149,7 +1152,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
<> <>
You've solved all cities on Earth! You've solved all cities on Earth!
<br /> <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)) (and proved the travelling salesman problem to be O(1))
</span> </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 { @keyframes reindeer-bar {
from { from {
background: 0 0 / 28px 28px repeat background: 0 0 / 28px 28px repeat

View file

@ -43,6 +43,7 @@ import factory from "./factory";
import reindeer from "./reindeer"; import reindeer from "./reindeer";
import sleigh from "./sleigh"; import sleigh from "./sleigh";
import routing from "./routing"; import routing from "./routing";
import packing from "./packing";
const id = "trees"; const id = "trees";
const day = 1; const day = 1;
@ -590,6 +591,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
exponent: 1.05, exponent: 1.05,
description: "Un-Processing", description: "Un-Processing",
enabled: paper.upgrades2.treeUpgrade.bought 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 { default as dyes, type enumColor } from "./dyes";
import elves from "./elves"; import elves from "./elves";
import toys from "./toys"; import toys from "./toys";
import packing from "./packing"
const id = "wrappingPaper"; const id = "wrappingPaper";
const day = 15; const day = 15;
@ -259,21 +260,22 @@ const layer = createLayer(id, () => {
] ]
}) })
}; };
const packingBoost = computed(() => packing.packingMilestones.wrappingPaperBoost.earned.value ? 2 : 1)
const boosts = { const boosts = {
christmas1: computed(() => 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 ), // Probably not the best way to do this, but it works
rainbow1: computed(() => 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(() => 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(() => 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(() => 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(() => beach1: computed(() =>
main.isMastery.value main.isMastery.value
@ -281,6 +283,7 @@ const layer = createLayer(id, () => {
: Decimal.add(wrappingPaper.beach.buyable.amount.value, 1) : Decimal.add(wrappingPaper.beach.buyable.amount.value, 1)
.log10() .log10()
.add(1) .add(1)
.mul(packingBoost.value)
.pow(toys.milestones.milestone3.earned.value ? 1.6 : 1) .pow(toys.milestones.milestone3.earned.value ? 1.6 : 1)
) )
}; };

View file

@ -4,8 +4,10 @@ import {
Component, Component,
GatherProps, GatherProps,
GenericComponent, GenericComponent,
jsx jsx,
Visibility
} from "features/feature"; } from "features/feature";
import { createParticles } from "features/particles/particles";
import { BaseLayer, createLayer, GenericLayer, layers } from "game/layers"; import { BaseLayer, createLayer, GenericLayer, layers } from "game/layers";
import { isPersistent, Persistent, persistent } from "game/persistence"; import { isPersistent, Persistent, persistent } from "game/persistence";
import type { PlayerData } from "game/player"; import type { PlayerData } from "game/player";
@ -14,10 +16,10 @@ import { format, formatTime } from "util/bignum";
import { Computable, convertComputable, ProcessedComputable } from "util/computed"; import { Computable, convertComputable, ProcessedComputable } from "util/computed";
import { createLazyProxy } from "util/proxies"; import { createLazyProxy } from "util/proxies";
import { save } from "util/save"; import { save } from "util/save";
import { renderRow, VueFeature } from "util/vue"; import { render, renderRow, VueFeature } from "util/vue";
import type { Ref } from "vue"; import { computed, Ref, ref, unref, watchEffect } from "vue";
import { computed, ref, unref } from "vue";
import "./advent.css"; import "./advent.css";
import { credits } from "./credits";
import Day from "./Day.vue"; import Day from "./Day.vue";
import boxes from "./layers/boxes"; import boxes from "./layers/boxes";
import cloth from "./layers/cloth"; import cloth from "./layers/cloth";
@ -25,19 +27,21 @@ import coal from "./layers/coal";
import dyes from "./layers/dyes"; import dyes from "./layers/dyes";
import elves from "./layers/elves"; import elves from "./layers/elves";
import factory from "./layers/factory"; import factory from "./layers/factory";
import presentSymbol from "./layers/factory-components/present.svg";
import letters from "./layers/letters"; import letters from "./layers/letters";
import management from "./layers/management"; import management from "./layers/management";
import metal from "./layers/metal"; import metal from "./layers/metal";
import oil from "./layers/oil"; import oil from "./layers/oil";
import packing from "./layers/packing";
import paper from "./layers/paper"; import paper from "./layers/paper";
import plastic from "./layers/plastic"; import plastic from "./layers/plastic";
import reindeer from "./layers/reindeer"; import reindeer from "./layers/reindeer";
import ribbon from "./layers/ribbon"; import ribbon from "./layers/ribbon";
import routing from "./layers/routing"; import routing from "./layers/routing";
import sleigh from "./layers/sleigh";
import toys from "./layers/toys"; import toys from "./layers/toys";
import trees from "./layers/trees"; import trees from "./layers/trees";
import workshop from "./layers/workshop"; import workshop from "./layers/workshop";
import sleigh from "./layers/sleigh";
import wrappingPaper from "./layers/wrapping-paper"; import wrappingPaper from "./layers/wrapping-paper";
import boxesSymbol from "./symbols/cardboardBox.png"; import boxesSymbol from "./symbols/cardboardBox.png";
import clothSymbol from "./symbols/cloth.png"; import clothSymbol from "./symbols/cloth.png";
@ -46,6 +50,7 @@ import dyesSymbol from "./symbols/dyes.png";
import elfSymbol from "./symbols/elf.png"; import elfSymbol from "./symbols/elf.png";
import managementSymbol from "./symbols/elfManagement.png"; import managementSymbol from "./symbols/elfManagement.png";
import factorySymbol from "./symbols/gears.png"; import factorySymbol from "./symbols/gears.png";
import routingSymbol from "./symbols/gps.png";
import lettersSymbol from "./symbols/letterbox.png"; import lettersSymbol from "./symbols/letterbox.png";
import metalSymbol from "./symbols/metal.png"; import metalSymbol from "./symbols/metal.png";
import oilSymbol from "./symbols/oil.png"; import oilSymbol from "./symbols/oil.png";
@ -54,14 +59,15 @@ import plasticSymbol from "./symbols/plastic.png";
import presentsSymbol from "./symbols/presents.png"; import presentsSymbol from "./symbols/presents.png";
import reindeerSymbol from "./symbols/reindeer.png"; import reindeerSymbol from "./symbols/reindeer.png";
import ribbonsSymbol from "./symbols/ribbons.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 workshopSymbol from "./symbols/sws.png";
import advFactorySymbol from "./symbols/teddyBear.png"; import advFactorySymbol from "./symbols/teddyBear.png";
import treeSymbol from "./symbols/tree.png"; import treeSymbol from "./symbols/tree.png";
import toysSymbol from "./symbols/truck.png"; import toysSymbol from "./symbols/truck.png";
import advManagementSymbol from "./symbols/workshopMansion.png"; import advManagementSymbol from "./symbols/workshopMansion.png";
import wrappingPaperSymbol from "./symbols/wrappingPaper.png"; import wrappingPaperSymbol from "./symbols/wrappingPaper.png";
import sleighSymbol from "./symbols/sleigh.png";
import routingSymbol from "./symbols/gps.png";
export interface Day extends VueFeature { export interface Day extends VueFeature {
day: number; day: number;
@ -73,10 +79,13 @@ export interface Day extends VueFeature {
opened: Persistent<boolean>; opened: Persistent<boolean>;
recentlyUpdated: Ref<boolean>; // Has the tab recieved an update since the player last opened it? recentlyUpdated: Ref<boolean>; // Has the tab recieved an update since the player last opened it?
shouldNotify: ProcessedComputable<boolean>; shouldNotify: ProcessedComputable<boolean>;
visibility?: Visibility;
} }
export const main = createLayer("main", function (this: BaseLayer) { export const main = createLayer("main", function (this: BaseLayer) {
const day = persistent<number>(1); const day = persistent<number>(1);
const hasWon = persistent<boolean>(false);
const timeUntilNewDay = computed( const timeUntilNewDay = computed(
() => (+new Date(new Date().getFullYear(), 11, day.value) - player.time) / 1000 () => (+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 loreTitle = ref<string>("");
const loreBody = ref<CoercableComponent | undefined>(); 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(() => const currentlyMastering = computed(() =>
isMastery.value isMastery.value
? Object.values(layers).find( ? Object.values(layers).find(
@ -168,6 +261,7 @@ export const main = createLayer("main", function (this: BaseLayer) {
story: string; story: string;
completedStory: string; completedStory: string;
masteredStory: string; masteredStory: string;
visibility?: Visibility;
} }
): Day { ): Day {
const opened = persistent<boolean>(false); const opened = persistent<boolean>(false);
@ -197,7 +291,8 @@ export const main = createLayer("main", function (this: BaseLayer) {
story, story,
completedStory, completedStory,
masteredStory, masteredStory,
recentlyUpdated recentlyUpdated,
visibility
} = this; } = this;
const mastered: Ref<boolean> = const mastered: Ref<boolean> =
@ -210,6 +305,7 @@ export const main = createLayer("main", function (this: BaseLayer) {
recentlyUpdated, recentlyUpdated,
shouldNotify, shouldNotify,
mastered, mastered,
visibility,
onOpenLore() { onOpenLore() {
const completed = main.day.value > day; const completed = main.day.value > day;
loreScene.value = completed ? day - 1 : -1; loreScene.value = completed ? day - 1 : -1;
@ -230,6 +326,7 @@ export const main = createLayer("main", function (this: BaseLayer) {
showLoreModal.value = true; showLoreModal.value = true;
}, },
onOpenLayer() { onOpenLayer() {
if (day == 25) return;
recentlyUpdated.value = false; recentlyUpdated.value = false;
// 1468 is because two tabs with minWidth of 700px plus the minimized calendar of 60px plus 2 dividers of 4px each // 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) { 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; layers[layer ?? "trees"]!.minimized.value = false;
}, },
onUnlockLayer() { onUnlockLayer() {
if (layer != null) { if (layer != null || day == 25) {
opened.value = true; opened.value = true;
setTimeout(() => { setTimeout(() => {
loreScene.value = -1; loreScene.value = -1;
loreTitle.value = unref(layers[layer ?? "trees"]?.name ?? ""); loreTitle.value =
day == 25
? "The End!"
: unref(layers[layer ?? "trees"]?.name ?? "");
loreBody.value = story; loreBody.value = story;
if (player.autoPause) player.devSpeed = null; if (player.autoPause) player.devSpeed = null;
showLoreModal.value = true; showLoreModal.value = true;
@ -511,11 +611,22 @@ export const main = createLayer("main", function (this: BaseLayer) {
createDay(() => ({ createDay(() => ({
day: 24, day: 24,
shouldNotify: false, shouldNotify: false,
layer: null, // "packing the presents" layer: "packing",
symbol: "", symbol: packingSymbol,
story: "", 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: "", completedStory:
"At last, you've crammed in all the presents Santa needs. Santa can take it from here. Good Job!",
masteredStory: "" 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 { return {
name: "Calendar", name: "Calendar",
days, days,
@ -557,6 +678,7 @@ export const main = createLayer("main", function (this: BaseLayer) {
loreScene, loreScene,
loreTitle, loreTitle,
loreBody, loreBody,
particles,
showLoreModal, showLoreModal,
completeDay, completeDay,
completeMastery, completeMastery,
@ -566,6 +688,9 @@ export const main = createLayer("main", function (this: BaseLayer) {
swappingMastery, swappingMastery,
currentlyMastering, currentlyMastering,
masteredDays, masteredDays,
creditsOpen,
credits,
hasWon,
display: jsx(() => ( display: jsx(() => (
<> <>
{player.devSpeed === 0 ? <div>Game Paused</div> : null} {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))} .map((days: Day[]) => renderRow(...days))}
</div> </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, factory,
reindeer, reindeer,
sleigh, 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 { processComputable } from "util/computed";
import { createLazyProxy } from "util/proxies"; import { createLazyProxy } from "util/proxies";
import { coerceComponent, isCoercableComponent } from "util/vue"; import { coerceComponent, isCoercableComponent } from "util/vue";
import type { Ref } from "vue"; import { isReadonly, Ref } from "vue";
import { computed, unref } from "vue"; import { computed, unref } from "vue";
export const BuyableType = Symbol("Buyable"); export const BuyableType = Symbol("Buyable");
@ -155,14 +155,18 @@ export function createBuyable<T extends BuyableOptions>(
return; return;
} }
const cost = unref(genericBuyable.cost); 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 = Decimal.sub(
genericBuyable.resource.value, genericBuyable.resource.value,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
cost! cost!
); );
genericBuyable.amount.value = Decimal.add(genericBuyable.amount.value, 1);
} }
genericBuyable.amount.value = Decimal.add(genericBuyable.amount.value, 1);
genericBuyable.onPurchase?.(cost); genericBuyable.onPurchase?.(cost);
}; };
processComputable(buyable as T, "display"); processComputable(buyable as T, "display");

View file

@ -134,7 +134,8 @@ export async function loadSave(playerObj: Partial<PlayerData>): Promise<void> {
setInterval(() => { setInterval(() => {
if ( if (
player.autosave && 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(); save();
} }
@ -142,7 +143,8 @@ setInterval(() => {
window.onbeforeunload = () => { window.onbeforeunload = () => {
if ( if (
player.autosave && 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) import.meta.env.DEV)
) { ) {
save(); save();