Merge branch 'day-17-toys' into day-20-factory

This commit is contained in:
ducdat0507 2022-12-20 18:05:46 +07:00
commit 6b9a53685b
35 changed files with 3518 additions and 691 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

58
src/components/Hotkey.vue Normal file
View file

@ -0,0 +1,58 @@
<!-- Make eslint not whine about putting spaces before the +'s -->
<!-- eslint-disable prettier/prettier -->
<template>
<template v-if="isCtrl"
><div class="key">Ctrl</div
>+</template
><template v-if="isShift"
><div class="key">Shift</div
>+</template
>
<div class="key">{{ key }}</div>
</template>
<script setup lang="ts">
import { GenericHotkey } from "features/hotkey";
import { reactive } from "vue";
const props = defineProps<{
hotkey: GenericHotkey;
}>();
let key = reactive(props.hotkey).key;
let isCtrl = key.startsWith("ctrl+");
if (isCtrl) key = key.slice(5);
let isShift = key.startsWith("shift+");
if (isShift) key = key.slice(6);
let isAlpha = key.length == 1 && key.toLowerCase() != key.toUpperCase();
if (isAlpha) key = key.toUpperCase();
</script>
<style scoped>
.key {
display: inline-block;
height: 1.4em;
min-width: 1em;
margin-block: 0.1em;
padding-inline: 0.2em;
vertical-align: 0.1em;
background: var(--foreground);
color: var(--feature-foreground);
border: 1px solid #0007;
border-radius: 0.3em;
box-shadow: 0 0.1em #0007, 0 0.1em var(--foreground);
font-size: smaller;
text-align: center;
user-select: none;
transition: transform 0s, box-shadow 0s;
}
.key:active {
transform: translateY(0.1em);
box-shadow: none;
}
</style>

View file

@ -1,13 +1,26 @@
<template> <template>
<div class="day feature dontMerge opened" v-if="opened.value"> <div
<Tooltip :display="(layers[layer ?? '']?.name ?? '')" :direction="Direction.Up" yoffset="5px"> class="day feature dontMerge opened"
<Transition appear name="door"> :class="{
mastered: unref(mastered),
masteryLock,
wallpaper: day < 8
}"
v-if="opened.value"
>
<div class="ribbon" v-if="day >= 8" />
<Tooltip :display="layers[layer ?? '']?.name ?? ''" :direction="Direction.Up" yoffset="5px">
<Transition appear :name="masteryLock ? 'door-close' : 'door'">
<div class="doors" @click="emit('openLayer')"> <div class="doors" @click="emit('openLayer')">
<div class="date">Dec<br />{{ day }}</div> <div class="date">Dec<br />{{ day }}</div>
<div class="date">Dec<br />{{ day }}</div> <div class="date">Dec<br />{{ day }}</div>
</div> </div>
</Transition> </Transition>
<div class="icon" @click="emit('openLayer')" :style="{ backgroundImage: `url(${symbol})` }"></div> <div
class="icon"
@click="emit('openLayer')"
:style="{ backgroundImage: `url(${symbol})` }"
></div>
<div class="lore" @click="emit('openLore')">?</div> <div class="lore" @click="emit('openLore')">?</div>
<Notif v-if="unref(shouldNotify)" /> <Notif v-if="unref(shouldNotify)" />
</Tooltip> </Tooltip>
@ -15,14 +28,18 @@
<div <div
v-else v-else
class="day feature dontMerge" class="day feature dontMerge"
:class="{ can: canOpen, locked: !canOpen, canOpen }" :class="{ can: canOpen, locked: !canOpen, canOpen, mastered: unref(mastered) }"
@click="tryUnlock" @click="tryUnlock"
> >
<div class="doors"></div> <div class="doors"></div>
<div class="date">Dec<br />{{ day }}</div> <div class="date">Dec<br />{{ day }}</div>
<div v-if="!canOpen" class="material-icons lock">lock</div> <div v-if="!canOpen" class="material-icons lock">lock</div>
<div v-if="main.day.value === day && !canOpen" class="timer"> <div v-if="main.day.value === day && !canOpen" class="timer">
{{ main.timeUntilNewDay.value < 0 ? "NYI, sorry" : formatTime(main.timeUntilNewDay.value, 0) }} {{
main.timeUntilNewDay.value < 0
? "Not Ready"
: formatTime(main.timeUntilNewDay.value, 0)
}}
</div> </div>
<Notif v-if="canOpen" /> <Notif v-if="canOpen" />
</div> </div>
@ -36,9 +53,11 @@ import { layers } from "game/layers";
import { Direction } from "util/common"; import { Direction } from "util/common";
import { formatTime } from "util/break_eternity"; import { formatTime } from "util/break_eternity";
import { ProcessedComputable } from "util/computed"; import { ProcessedComputable } from "util/computed";
import type { Ref } from "vue"; import { Ref, Transition } from "vue";
import { computed, unref } from "vue"; import { computed, unref } from "vue";
import { main } from "./projEntry"; import { main } from "./projEntry";
import coal from "./layers/coal";
import dyes from "./layers/dyes";
const props = defineProps<{ const props = defineProps<{
day: number; day: number;
@ -47,6 +66,7 @@ const props = defineProps<{
opened: Ref<boolean>; opened: Ref<boolean>;
recentlyUpdated: Ref<boolean>; recentlyUpdated: Ref<boolean>;
shouldNotify: ProcessedComputable<boolean>; shouldNotify: ProcessedComputable<boolean>;
mastered: Ref<boolean>;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
@ -63,6 +83,17 @@ const canOpen = computed(
new Date().getDate() >= props.day new Date().getDate() >= props.day
); );
const isMastering = main.isMastery;
const includeMastery = computed(
() =>
props.mastered.value ||
main.currentlyMastering.value == layers[props.layer ?? ""] ||
["wrappingPaper", "ribbon"].includes(props.layer || "") ||
(coal.mastered.value && props.layer == "elves") ||
(dyes.mastered.value && props.layer == "elves")
);
const masteryLock = computed(() => isMastering.value && !includeMastery.value);
function tryUnlock() { function tryUnlock() {
if (canOpen.value) { if (canOpen.value) {
emit("unlockLayer"); emit("unlockLayer");
@ -80,59 +111,88 @@ function tryUnlock() {
margin: 5%; margin: 5%;
} }
.mastered.day.wallpaper {
box-shadow: rgb(0 0 0 / 25%) 0px 0px 0px 3px inset;
background: linear-gradient(
225deg,
rgb(255, 76, 76) 11.1%,
rgb(255, 255, 255) 11.1% 22.2%,
rgb(65, 255, 95) 22.2% 33.3%,
rgb(255, 255, 255) 33.3% 44.4%,
rgb(255, 76, 76) 44.4% 55.5%,
rgb(255, 255, 255) 55.5% 66.6%,
rgb(65, 255, 95) 66.6% 77.7%,
rgb(255, 255, 255) 77.7% 88.8%,
rgb(255, 76, 76) 88.8%
);
}
.door-enter-from::before, .door-enter-from::before,
.door-enter-from::after, .door-enter-from::after,
.door-leave-to::before, .door-close-enter-to::before,
.door-leave-to::after { .door-close-enter-to::after {
transform: perspective(150px) rotateY(0) !important; transform: perspective(150px) rotateY(0) !important;
} }
.door-enter-from .date, .door-enter-from .date,
.door-leave-to .date { .door-close-enter-to .date {
transform: translate(-50%, -50%) perspective(150px) rotateY(0) !important; transform: translate(-50%, -50%) perspective(150px) rotateY(0) !important;
} }
.door-enter-active::before, .door-enter-active::before,
.door-enter-active::after, .door-enter-active::after,
.door-leave-active::before, .door-close-enter-active::before,
.door-leave-active::after { .door-close-enter-active::after {
z-index: 2; z-index: 2;
} }
.door-enter-active .date, .door-enter-active .date,
.door-leave-active .date { .door-close-enter-active .date {
z-index: 3; z-index: 3;
} }
.day.opened .doors::before, .day .doors::before,
.day.opened .doors::after, .day .doors::after,
.day.opened .doors .date { .day .doors .date {
transition: 1s; transition: 1s;
} }
.day.opened .doors::before { .day.opened .doors::before {
transform-origin: left; transform-origin: left;
transform: perspective(150px) rotateY(-135deg);
} }
.day.opened .doors::after { .day.opened .doors::after {
transform-origin: right; transform-origin: right;
}
.day.opened:not(.masteryLock) .doors::before {
transform: perspective(150px) rotateY(-135deg);
}
.day.opened:not(.masteryLock) .doors::after {
transform: perspective(150px) rotateY(135deg); transform: perspective(150px) rotateY(135deg);
} }
.day.opened .doors .date:first-child { .day.opened .doors .date:first-child {
transform-origin: left; transform-origin: left;
transform: translate(-50%, -50%) perspective(150px) rotateY(-135deg);
clip-path: polygon(0 0, 50% 0, 50% 100%, 0 100%); clip-path: polygon(0 0, 50% 0, 50% 100%, 0 100%);
} }
.day.opened .doors .date:last-child { .day.opened .doors .date:last-child {
transform-origin: right; transform-origin: right;
transform: translate(-50%, -50%) perspective(150px) rotateY(135deg);
clip-path: polygon(100% 0, 50% 0, 50% 100%, 100% 100%); clip-path: polygon(100% 0, 50% 0, 50% 100%, 100% 100%);
} }
.tooltip-container, .doors { .day.opened:not(.masteryLock) .doors .date:first-child {
transform: translate(-50%, -50%) perspective(150px) rotateY(-135deg);
}
.day.opened:not(.masteryLock) .doors .date:last-child {
transform: translate(-50%, -50%) perspective(150px) rotateY(135deg);
}
.tooltip-container,
.doors {
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -152,6 +212,7 @@ function tryUnlock() {
width: 50%; width: 50%;
height: 100%; height: 100%;
pointer-events: none; pointer-events: none;
z-index: 1;
} }
.doors::before { .doors::before {
@ -164,6 +225,73 @@ function tryUnlock() {
right: 0; right: 0;
} }
.masteryLock {
cursor: not-allowed;
}
.masteryLock > * {
pointer-events: none;
}
.masteryLock > * > :not(.doors) {
opacity: 0;
}
.masteryLock .icon {
transition-duration: 0.2s;
transition-delay: 0.8s;
}
.mastered.wallpaper .doors::before,
.mastered.wallpaper .doors::after {
background: linear-gradient(
225deg,
rgb(255, 76, 76) 11.1%,
rgb(255, 255, 255) 11.1% 22.2%,
rgb(65, 255, 95) 22.2% 33.3%,
rgb(255, 255, 255) 33.3% 44.4%,
rgb(255, 76, 76) 44.4% 55.5%,
rgb(255, 255, 255) 55.5% 66.6%,
rgb(65, 255, 95) 66.6% 77.7%,
rgb(255, 255, 255) 77.7% 88.8%,
rgb(255, 76, 76) 88.8%
);
}
.mastered .ribbon {
position: absolute;
top: -2px;
left: 0px;
width: calc(100% + 0px);
height: calc(100% + 4px);
overflow: hidden;
pointer-events: none;
user-select: none;
z-index: 11;
}
.mastered .ribbon::after {
content: "🎀";
color: red;
position: absolute;
top: -5px;
left: -5px;
font-size: xx-large;
transform: rotateZ(-45deg);
z-index: 1;
}
.mastered .ribbon::before {
content: "";
width: calc(100% - 24px);
height: 100%;
border: solid darkred 8px;
transform: rotateZ(45deg);
position: absolute;
top: 0;
left: 0;
border-top: none;
border-bottom: none;
z-index: 1;
}
.date { .date {
position: absolute; position: absolute;
top: 50%; top: 50%;
@ -174,7 +302,7 @@ function tryUnlock() {
pointer-events: none; pointer-events: none;
user-select: none; user-select: none;
backface-visibility: hidden; backface-visibility: hidden;
width: 100%; width: calc(100% - 14px);
} }
.timer { .timer {
@ -224,5 +352,6 @@ function tryUnlock() {
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
opacity: 0.2; opacity: 0.2;
font-size: 400%; font-size: 400%;
z-index: 2;
} }
</style> </style>

View file

@ -56,6 +56,8 @@
<img v-if="day >= 5" :src="boxes" class="scene-item" /> <img v-if="day >= 5" :src="boxes" class="scene-item" />
<img v-if="day >= 9" :src="plastic" class="scene-item" /> <img v-if="day >= 9" :src="plastic" class="scene-item" />
<img v-if="day >= 10" :src="dyes" class="scene-item" /> <img v-if="day >= 10" :src="dyes" class="scene-item" />
<img v-if="day >= 14" :src="wrappingPaper" class="scene-item" />
<img v-if="day >= 15" :src="ribbons" class="scene-item" />
</div> </div>
</div> </div>
</template> </template>
@ -75,6 +77,8 @@ import dyes from "./symbols/dyes.png";
import management from "./symbols/elfManagement.png"; import management from "./symbols/elfManagement.png";
import advManagement from "./symbols/workshopMansion.png"; import advManagement from "./symbols/workshopMansion.png";
import letters from "./symbols/letterbox.png"; import letters from "./symbols/letterbox.png";
import wrappingPaper from "./symbols/wrappingPaper.png";
import ribbons from "./symbols/ribbons.png";
defineProps<{ defineProps<{
day: number; day: number;

View file

@ -12,6 +12,13 @@
aspect-ratio: 3151 / 4190; aspect-ratio: 3151 / 4190;
} }
.advent.decorating {
filter: hue-rotate(-150deg);
}
.advent.decorating > * {
filter: hue-rotate(150deg);
}
.advent > .table { .advent > .table {
width: 100%; width: 100%;
} }

View file

@ -16,12 +16,13 @@ import { GenericMilestone } from "features/milestones/milestone";
import { displayResource, Resource, trackTotal } from "features/resources/resource"; import { displayResource, Resource, trackTotal } from "features/resources/resource";
import type { GenericTree, GenericTreeNode, TreeNode, TreeNodeOptions } from "features/trees/tree"; import type { GenericTree, GenericTreeNode, TreeNode, TreeNodeOptions } from "features/trees/tree";
import { createTreeNode } from "features/trees/tree"; import { createTreeNode } from "features/trees/tree";
import { BaseLayer, Layer } from "game/layers";
import type { Modifier } from "game/modifiers"; import type { Modifier } from "game/modifiers";
import type { Persistent } from "game/persistence"; import type { Persistent } from "game/persistence";
import { DefaultValue, persistent } from "game/persistence"; import { DefaultValue, persistent } from "game/persistence";
import player from "game/player"; import player from "game/player";
import settings from "game/settings"; import settings from "game/settings";
import type { DecimalSource } from "util/bignum"; import { DecimalSource, formatSmall } from "util/bignum";
import Decimal, { format } from "util/bignum"; import Decimal, { format } from "util/bignum";
import { formatWhole } from "util/break_eternity"; import { formatWhole } from "util/break_eternity";
import { Direction, WithRequired } from "util/common"; import { Direction, WithRequired } from "util/common";
@ -36,6 +37,7 @@ import { getFirstFeature, render, renderColJSX, renderJSX, VueFeature } from "ut
import { Ref, watchEffect } from "vue"; import { Ref, watchEffect } from "vue";
import { computed, unref } from "vue"; import { computed, unref } from "vue";
import "./common.css"; import "./common.css";
import "data/layers/styles/day-gradients.css";
import { main } from "./projEntry"; import { main } from "./projEntry";
/** An object that configures a {@link ResetButton} */ /** An object that configures a {@link ResetButton} */
@ -334,7 +336,11 @@ export function createCollapsibleModifierSections(
return ( return (
<> <>
{hasPreviousSection ? <br /> : null} {hasPreviousSection ? <br /> : null}
<div style={{"--unit": settings.alignUnits && s.unit ? "'" + s.unit + "'" : ""}}> <div
style={{
"--unit": settings.alignUnits && s.unit ? "'" + s.unit + "'" : ""
}}
>
{header} {header}
<br /> <br />
{modifiers} {modifiers}
@ -342,7 +348,7 @@ export function createCollapsibleModifierSections(
<div class="modifier-container"> <div class="modifier-container">
<span class="modifier-description">Total</span> <span class="modifier-description">Total</span>
<span class="modifier-amount"> <span class="modifier-amount">
{format(s.modifier.apply(unref(processed.base[i]) ?? 1))} {formatSmall(s.modifier.apply(unref(processed.base[i]) ?? 1))}
{s.unit} {s.unit}
</span> </span>
</div> </div>
@ -409,9 +415,15 @@ export function createCollapsibleMilestones(milestones: Record<string, GenericMi
export function setUpDailyProgressTracker(options: { export function setUpDailyProgressTracker(options: {
resource: Resource; resource: Resource;
goal: DecimalSource; goal: DecimalSource;
masteryGoal?: DecimalSource;
name: string; name: string;
day: number; day: number;
color: string; background:
| string
| {
gradient: string;
duration: string;
};
textColor?: string; textColor?: string;
modal?: { modal?: {
show: Ref<boolean>; show: Ref<boolean>;
@ -422,9 +434,10 @@ export function setUpDailyProgressTracker(options: {
}) { }) {
const total = options.ignoreTotal ? options.resource : trackTotal(options.resource); const total = options.ignoreTotal ? options.resource : trackTotal(options.resource);
const progressFunc = () => { const progressFunc = () => {
if (main.day.value !== options.day) return 1; const isMastering = main.currentlyMastering.value?.name === options.name;
if (main.day.value !== options.day && !isMastering) return 1;
let progress = Decimal.add(total.value, 1); let progress = Decimal.add(total.value, 1);
let requirement = options.goal; let requirement = isMastering ? options.masteryGoal ?? options.goal : options.goal;
if (options.usingLog?.value ?? settings.usingLog) { if (options.usingLog?.value ?? settings.usingLog) {
progress = progress.log10(); progress = progress.log10();
requirement = Decimal.log10(requirement); requirement = Decimal.log10(requirement);
@ -435,13 +448,25 @@ export function setUpDailyProgressTracker(options: {
direction: Direction.Right, direction: Direction.Right,
width: 600, width: 600,
height: 25, height: 25,
fillStyle: { backgroundColor: options.color }, /* eslint-disable prettier/prettier */
fillStyle: typeof options.background == "string" ? {
backgroundColor: options.background,
} : {
animation: options.background.duration + " " + options.background.gradient + " linear infinite",
},
/* eslint-enable prettier/prettier */
textStyle: options.textColor ? { color: options.textColor } : undefined, textStyle: options.textColor ? { color: options.textColor } : undefined,
progress: progressFunc, progress: progressFunc,
display: jsx(() => display: jsx(() =>
main.day.value === options.day ? ( main.day.value === options.day ||
main.currentlyMastering.value?.name === options.name ? (
<> <>
{formatWhole(total.value)}/{formatWhole(options.goal)} {formatWhole(total.value)}/
{formatWhole(
main.currentlyMastering.value?.name === options.name
? options.masteryGoal ?? options.goal
: options.goal
)}
</> </>
) : ( ) : (
"" ""
@ -457,6 +482,12 @@ export function setUpDailyProgressTracker(options: {
Reach {formatWhole(options.goal)} {options.ignoreTotal ? "" : "total "} Reach {formatWhole(options.goal)} {options.ignoreTotal ? "" : "total "}
{options.resource.displayName} to complete the day {options.resource.displayName} to complete the day
</> </>
) : main.currentlyMastering.value?.name === options.name ? (
<>
Reach {formatWhole(options.masteryGoal ?? options.goal)}{" "}
{options.ignoreTotal ? "" : "total "}
{options.resource.displayName} to decorate the day
</>
) : ( ) : (
<>{options.name} Complete!</> <>{options.name} Complete!</>
)} )}
@ -482,6 +513,11 @@ export function setUpDailyProgressTracker(options: {
watchEffect(() => { watchEffect(() => {
if (main.day.value === options.day && Decimal.gte(total.value, options.goal)) { if (main.day.value === options.day && Decimal.gte(total.value, options.goal)) {
main.completeDay(); main.completeDay();
} else if (
main.currentlyMastering.value?.name === options.name &&
Decimal.gte(total.value, options.masteryGoal ?? options.goal)
) {
main.completeMastery();
} }
}); });
@ -544,3 +580,11 @@ export function changeActiveBuyables(options: {
max max
}; };
} }
/* ugh
export function masteryHelper(layer: BaseLayer & {mastery: Partial<typeof layer>}, main: Layer<any> & { isMastery: Ref<boolean> }): ProxyHandler<typeof layer.mastery>{
return new Proxy({}, {
get (__, key: keyof typeof layer.mastery) {
return main.isMastery.value ? layer.mastery[key] : layer[key]
}
})
} */

View file

@ -6,7 +6,7 @@ import Spacer from "components/layout/Spacer.vue";
import Modal from "components/Modal.vue"; import Modal from "components/Modal.vue";
import { createCollapsibleModifierSections, setUpDailyProgressTracker } from "data/common"; import { createCollapsibleModifierSections, setUpDailyProgressTracker } from "data/common";
import { main } from "data/projEntry"; import { main } from "data/projEntry";
import { createBuyable, GenericBuyable } from "features/buyable"; import { createBuyable } from "features/buyable";
import { createClickable } from "features/clickables/clickable"; import { createClickable } from "features/clickables/clickable";
import { createCumulativeConversion, createPolynomialScaling } from "features/conversion"; import { createCumulativeConversion, createPolynomialScaling } from "features/conversion";
import { jsx, showIf } from "features/feature"; import { jsx, showIf } from "features/feature";
@ -21,13 +21,13 @@ import {
createSequentialModifier, createSequentialModifier,
Modifier Modifier
} from "game/modifiers"; } from "game/modifiers";
import { noPersist } from "game/persistence"; import { noPersist, persistent } from "game/persistence";
import Decimal, { DecimalSource, format, formatWhole } from "util/bignum"; import Decimal, { DecimalSource, format, formatWhole } from "util/bignum";
import { WithRequired } from "util/common"; import { WithRequired } from "util/common";
import { render, renderGrid, renderRow } from "util/vue"; import { render, renderGrid } from "util/vue";
import { computed, ComputedRef, ref, unref } from "vue"; import { computed, ComputedRef, ref, unref } from "vue";
import dyes from "./dyes"; import dyes from "./dyes";
import { ElfBuyable } from "./elves"; import elves, { ElfBuyable } from "./elves";
import management from "./management"; import management from "./management";
import paper from "./paper"; import paper from "./paper";
import plastic from "./plastic"; import plastic from "./plastic";
@ -98,7 +98,8 @@ const layer = createLayer(id, function (this: BaseLayer) {
} }
boxesConversion.convert(); boxesConversion.convert();
}, },
style: "width: 600px; min-height: unset" style: "width: 600px; min-height: unset",
visibility: () => showIf(!main.isMastery.value || masteryEffectActive.value)
})); }));
const logsUpgrade = createUpgrade(() => ({ const logsUpgrade = createUpgrade(() => ({
@ -107,6 +108,9 @@ const layer = createLayer(id, function (this: BaseLayer) {
description: "Double log gain and unlock a new elf for training" description: "Double log gain and unlock a new elf for training"
}, },
onPurchase() { onPurchase() {
if (masteryEffectActive.value) {
elves.elves.smallFireElf.bought.value = true;
}
main.days[3].recentlyUpdated.value = true; main.days[3].recentlyUpdated.value = true;
}, },
resource: noPersist(boxes), resource: noPersist(boxes),
@ -118,6 +122,9 @@ const layer = createLayer(id, function (this: BaseLayer) {
description: "Double ash gain and unlock a new elf for training" description: "Double ash gain and unlock a new elf for training"
}, },
onPurchase() { onPurchase() {
if (masteryEffectActive.value) {
elves.elves.bonfireElf.bought.value = true;
}
main.days[3].recentlyUpdated.value = true; main.days[3].recentlyUpdated.value = true;
}, },
resource: noPersist(boxes), resource: noPersist(boxes),
@ -129,6 +136,9 @@ const layer = createLayer(id, function (this: BaseLayer) {
description: "Double coal gain and unlock a new elf for training" description: "Double coal gain and unlock a new elf for training"
}, },
onPurchase() { onPurchase() {
if (masteryEffectActive.value) {
elves.elves.kilnElf.bought.value = true;
}
main.days[3].recentlyUpdated.value = true; main.days[3].recentlyUpdated.value = true;
}, },
resource: noPersist(boxes), resource: noPersist(boxes),
@ -179,13 +189,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
visibility: () => showIf(management.elfTraining.boxElfTraining.milestones[4].earned.value), visibility: () => showIf(management.elfTraining.boxElfTraining.milestones[4].earned.value),
display: { display: {
title: "Carry dye in boxes", title: "Carry dye in boxes",
description: "Double all dye gain but reset all dyes" description: "Double all dye gain"
},
onPurchase() {
(["red", "yellow", "blue", "orange", "green", "purple"] as const).forEach(dyeColor => {
dyes.dyes[dyeColor].amount.value = 0;
dyes.dyes[dyeColor].buyable.amount.value = 0;
});
} }
})) as GenericUpgrade; })) as GenericUpgrade;
const xpUpgrade = createUpgrade(() => ({ const xpUpgrade = createUpgrade(() => ({
@ -247,14 +251,21 @@ const layer = createLayer(id, function (this: BaseLayer) {
return Decimal.isNaN(v) ? Decimal.dZero : v.floor().max(0); return Decimal.isNaN(v) ? Decimal.dZero : v.floor().max(0);
}, },
visibility: () => showIf(logsUpgrade.bought.value), visibility: () => showIf(logsUpgrade.bought.value),
freeLevels: computed(() => freeLevels: computed(() => {
management.elfTraining.boxElfTraining.milestones[0].earned.value let levels: DecimalSource = 0;
? Decimal.max(ashBoxesBuyable.amount.value, 1) if (management.elfTraining.boxElfTraining.milestones[0].earned.value) {
.sqrt() levels = Decimal.max(ashBoxesBuyable.amount.value, 1)
.floor() .sqrt()
.add(Decimal.max(coalBoxesBuyable.amount.value, 1).sqrt().floor()) .floor()
: 0 .add(Decimal.max(coalBoxesBuyable.amount.value, 1).sqrt().floor());
), }
if (masteryEffectActive.value) {
levels = Decimal.pow(logBoxesBuyable.amount.value, 2)
.sub(logBoxesBuyable.amount.value)
.add(levels);
}
return levels;
}),
totalAmount: computed(() => totalAmount: computed(() =>
Decimal.add(logBoxesBuyable.amount.value, logBoxesBuyable.freeLevels.value) Decimal.add(logBoxesBuyable.amount.value, logBoxesBuyable.freeLevels.value)
) )
@ -302,14 +313,21 @@ const layer = createLayer(id, function (this: BaseLayer) {
return Decimal.isNaN(v) ? Decimal.dZero : v.floor().max(0); return Decimal.isNaN(v) ? Decimal.dZero : v.floor().max(0);
}, },
visibility: () => showIf(ashUpgrade.bought.value), visibility: () => showIf(ashUpgrade.bought.value),
freeLevels: computed(() => freeLevels: computed(() => {
management.elfTraining.boxElfTraining.milestones[0].earned.value let levels: DecimalSource = 0;
? Decimal.max(logBoxesBuyable.amount.value, 1) if (management.elfTraining.boxElfTraining.milestones[0].earned.value) {
.sqrt() levels = Decimal.max(logBoxesBuyable.amount.value, 1)
.floor() .sqrt()
.add(Decimal.max(coalBoxesBuyable.amount.value, 1).sqrt().floor()) .floor()
: 0 .add(Decimal.max(coalBoxesBuyable.amount.value, 1).sqrt().floor());
), }
if (masteryEffectActive.value) {
levels = Decimal.pow(ashBoxesBuyable.amount.value, 2)
.sub(ashBoxesBuyable.amount.value)
.add(levels);
}
return levels;
}),
totalAmount: computed(() => totalAmount: computed(() =>
Decimal.add(ashBoxesBuyable.amount.value, ashBoxesBuyable.freeLevels.value) Decimal.add(ashBoxesBuyable.amount.value, ashBoxesBuyable.freeLevels.value)
) )
@ -357,14 +375,21 @@ const layer = createLayer(id, function (this: BaseLayer) {
return Decimal.isNaN(v) ? Decimal.dZero : v.floor().max(0); return Decimal.isNaN(v) ? Decimal.dZero : v.floor().max(0);
}, },
visibility: () => showIf(coalUpgrade.bought.value), visibility: () => showIf(coalUpgrade.bought.value),
freeLevels: computed(() => freeLevels: computed(() => {
management.elfTraining.boxElfTraining.milestones[0].earned.value let levels: DecimalSource = 0;
? Decimal.max(logBoxesBuyable.amount.value, 1) if (management.elfTraining.boxElfTraining.milestones[0].earned.value) {
.sqrt() levels = Decimal.max(logBoxesBuyable.amount.value, 1)
.floor() .sqrt()
.add(Decimal.max(ashBoxesBuyable.amount.value, 1).sqrt().floor()) .floor()
: 0 .add(Decimal.max(ashBoxesBuyable.amount.value, 1).sqrt().floor());
), }
if (masteryEffectActive.value) {
levels = Decimal.pow(coalBoxesBuyable.amount.value, 2)
.sub(coalBoxesBuyable.amount.value)
.add(levels);
}
return levels;
}),
totalAmount: computed(() => totalAmount: computed(() =>
Decimal.add(coalBoxesBuyable.amount.value, coalBoxesBuyable.freeLevels.value) Decimal.add(coalBoxesBuyable.amount.value, coalBoxesBuyable.freeLevels.value)
) )
@ -394,7 +419,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
resource: noPersist(boxes), resource: noPersist(boxes),
cost() { cost() {
let v = this.amount.value; let v = this.amount.value;
v = Decimal.pow(0.95, paper.books.boxBook.amount.value).times(v); v = Decimal.pow(0.95, paper.books.boxBook.totalAmount.value).times(v);
let scaling = 10; let scaling = 10;
if (management.elfTraining.boxElfTraining.milestones[2].earned.value) { if (management.elfTraining.boxElfTraining.milestones[2].earned.value) {
scaling--; scaling--;
@ -419,14 +444,21 @@ const layer = createLayer(id, function (this: BaseLayer) {
return Decimal.isNaN(v) ? Decimal.dZero : v.floor().max(0); return Decimal.isNaN(v) ? Decimal.dZero : v.floor().max(0);
}, },
visibility: () => showIf(management.elfTraining.boxElfTraining.milestones[3].earned.value), visibility: () => showIf(management.elfTraining.boxElfTraining.milestones[3].earned.value),
freeLevels: computed(() => freeLevels: computed(() => {
management.elfTraining.boxElfTraining.milestones[0].earned.value let levels: DecimalSource = 0;
? Decimal.max(metalBoxesBuyable.amount.value, 1) if (management.elfTraining.boxElfTraining.milestones[0].earned.value) {
.sqrt() levels = Decimal.max(metalBoxesBuyable.amount.value, 1)
.floor() .sqrt()
.add(Decimal.max(plasticBoxesBuyable.amount.value, 1).sqrt().floor()) .floor()
: 0 .add(Decimal.max(plasticBoxesBuyable.amount.value, 1).sqrt().floor());
), }
if (masteryEffectActive.value) {
levels = Decimal.pow(oreBoxesBuyable.amount.value, 2)
.sub(oreBoxesBuyable.amount.value)
.add(levels);
}
return levels;
}),
totalAmount: computed(() => totalAmount: computed(() =>
Decimal.add(oreBoxesBuyable.amount.value, oreBoxesBuyable.freeLevels.value) Decimal.add(oreBoxesBuyable.amount.value, oreBoxesBuyable.freeLevels.value)
) )
@ -455,7 +487,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
resource: noPersist(boxes), resource: noPersist(boxes),
cost() { cost() {
let v = this.amount.value; let v = this.amount.value;
v = Decimal.pow(0.95, paper.books.boxBook.amount.value).times(v); v = Decimal.pow(0.95, paper.books.boxBook.totalAmount.value).times(v);
let scaling = 15; let scaling = 15;
if (management.elfTraining.boxElfTraining.milestones[2].earned.value) { if (management.elfTraining.boxElfTraining.milestones[2].earned.value) {
scaling--; scaling--;
@ -474,14 +506,21 @@ const layer = createLayer(id, function (this: BaseLayer) {
return Decimal.isNaN(v) ? Decimal.dZero : v.floor().max(0); return Decimal.isNaN(v) ? Decimal.dZero : v.floor().max(0);
}, },
visibility: () => showIf(management.elfTraining.boxElfTraining.milestones[3].earned.value), visibility: () => showIf(management.elfTraining.boxElfTraining.milestones[3].earned.value),
freeLevels: computed(() => freeLevels: computed(() => {
management.elfTraining.boxElfTraining.milestones[0].earned.value let levels: DecimalSource = 0;
? Decimal.max(oreBoxesBuyable.amount.value, 1) if (management.elfTraining.boxElfTraining.milestones[0].earned.value) {
.sqrt() levels = Decimal.max(oreBoxesBuyable.amount.value, 1)
.floor() .sqrt()
.add(Decimal.max(plasticBoxesBuyable.amount.value, 1).sqrt().floor()) .floor()
: 0 .add(Decimal.max(plasticBoxesBuyable.amount.value, 1).sqrt().floor());
), }
if (masteryEffectActive.value) {
levels = Decimal.pow(metalBoxesBuyable.amount.value, 2)
.sub(metalBoxesBuyable.amount.value)
.add(levels);
}
return levels;
}),
totalAmount: computed(() => totalAmount: computed(() =>
Decimal.add(metalBoxesBuyable.amount.value, metalBoxesBuyable.freeLevels.value) Decimal.add(metalBoxesBuyable.amount.value, metalBoxesBuyable.freeLevels.value)
) )
@ -510,7 +549,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
resource: noPersist(boxes), resource: noPersist(boxes),
cost() { cost() {
let v = this.amount.value; let v = this.amount.value;
v = Decimal.pow(0.95, paper.books.boxBook.amount.value).times(v); v = Decimal.pow(0.95, paper.books.boxBook.totalAmount.value).times(v);
let scaling = 20; let scaling = 20;
if (management.elfTraining.boxElfTraining.milestones[2].earned.value) { if (management.elfTraining.boxElfTraining.milestones[2].earned.value) {
scaling--; scaling--;
@ -529,14 +568,21 @@ const layer = createLayer(id, function (this: BaseLayer) {
return Decimal.isNaN(v) ? Decimal.dZero : v.floor().max(0); return Decimal.isNaN(v) ? Decimal.dZero : v.floor().max(0);
}, },
visibility: () => showIf(management.elfTraining.boxElfTraining.milestones[3].earned.value), visibility: () => showIf(management.elfTraining.boxElfTraining.milestones[3].earned.value),
freeLevels: computed(() => freeLevels: computed(() => {
management.elfTraining.boxElfTraining.milestones[0].earned.value let levels: DecimalSource = 0;
? Decimal.max(oreBoxesBuyable.amount.value, 1) if (management.elfTraining.boxElfTraining.milestones[0].earned.value) {
.sqrt() levels = Decimal.max(oreBoxesBuyable.amount.value, 1)
.floor() .sqrt()
.add(Decimal.max(metalBoxesBuyable.amount.value, 1).sqrt().floor()) .floor()
: 0 .add(Decimal.max(metalBoxesBuyable.amount.value, 1).sqrt().floor());
), }
if (masteryEffectActive.value) {
levels = Decimal.pow(plasticBoxesBuyable.amount.value, 2)
.sub(plasticBoxesBuyable.amount.value)
.add(levels);
}
return levels;
}),
totalAmount: computed(() => totalAmount: computed(() =>
Decimal.add(plasticBoxesBuyable.amount.value, plasticBoxesBuyable.freeLevels.value) Decimal.add(plasticBoxesBuyable.amount.value, plasticBoxesBuyable.freeLevels.value)
) )
@ -575,15 +621,50 @@ const layer = createLayer(id, function (this: BaseLayer) {
const { total: totalBoxes, trackerDisplay } = setUpDailyProgressTracker({ const { total: totalBoxes, trackerDisplay } = setUpDailyProgressTracker({
resource: boxes, resource: boxes,
goal: 5e4, goal: 5e4,
masteryGoal: 5e5,
name, name,
day, day,
color, background: color,
modal: { modal: {
display: modifiersModal, display: modifiersModal,
show: showModifiersModal show: showModifiersModal
} }
}); });
const mastery = {
boxes: persistent<DecimalSource>(0),
totalBoxes: persistent<DecimalSource>(0),
upgrades: {
logsUpgrade: { bought: persistent<boolean>(false) },
ashUpgrade: { bought: persistent<boolean>(false) },
coalUpgrade: { bought: persistent<boolean>(false) }
},
row2Upgrades: {
oreUpgrade: { bought: persistent<boolean>(false) },
metalUpgrade: { bought: persistent<boolean>(false) },
plasticUpgrade: { bought: persistent<boolean>(false) }
},
row3Upgrades: {
clothUpgrade: { bought: persistent<boolean>(false) },
dyeUpgrade: { bought: persistent<boolean>(false) },
xpUpgrade: { bought: persistent<boolean>(false) }
},
buyables: {
logBoxesBuyable: { amount: persistent<DecimalSource>(0) },
ashBoxesBuyable: { amount: persistent<DecimalSource>(0) },
coalBoxesBuyable: { amount: persistent<DecimalSource>(0) }
},
buyables2: {
oreBoxesBuyable: { amount: persistent<DecimalSource>(0) },
metalBoxesBuyable: { amount: persistent<DecimalSource>(0) },
plasticBoxesBuyable: { amount: persistent<DecimalSource>(0) }
}
};
const mastered = persistent<boolean>(false);
const masteryEffectActive = computed(
() => mastered.value || main.currentlyMastering.value?.name === name
);
return { return {
name, name,
day, day,
@ -602,6 +683,16 @@ const layer = createLayer(id, function (this: BaseLayer) {
<> <>
{render(trackerDisplay)} {render(trackerDisplay)}
<Spacer /> <Spacer />
{masteryEffectActive.value ? (
<>
<div class="decoration-effect">
Decoration effect:
<br />
Effective boxes buyables' levels are squared
</div>
<Spacer />
</>
) : null}
<MainDisplay resource={boxes} color={color} style="margin-bottom: 0" /> <MainDisplay resource={boxes} color={color} style="margin-bottom: 0" />
<Spacer /> <Spacer />
{render(makeBoxes)} {render(makeBoxes)}
@ -618,9 +709,13 @@ const layer = createLayer(id, function (this: BaseLayer) {
minimizedDisplay: jsx(() => ( minimizedDisplay: jsx(() => (
<div> <div>
{name}{" "} {name}{" "}
<span class="desc">{format(boxes.value)} {boxes.displayName}</span> <span class="desc">
</div> {format(boxes.value)} {boxes.displayName}
</span>
</div>
)), )),
mastery,
mastered
}; };
}); });

View file

@ -2,13 +2,14 @@
* @module * @module
* @hidden * @hidden
*/ */
import HotkeyVue from "components/Hotkey.vue";
import Row from "components/layout/Row.vue"; import Row from "components/layout/Row.vue";
import Spacer from "components/layout/Spacer.vue"; import Spacer from "components/layout/Spacer.vue";
import Modal from "components/Modal.vue"; import Modal from "components/Modal.vue";
import { createCollapsibleModifierSections, setUpDailyProgressTracker } from "data/common"; import { createCollapsibleModifierSections, setUpDailyProgressTracker } from "data/common";
import { main } from "data/projEntry"; import { main } from "data/projEntry";
import { createBar } from "features/bars/bar"; import { createBar } from "features/bars/bar";
import { createBuyable, GenericBuyable } from "features/buyable"; import { createBuyable } from "features/buyable";
import { createClickable } from "features/clickables/clickable"; import { createClickable } from "features/clickables/clickable";
import { jsx, showIf } from "features/feature"; import { jsx, showIf } from "features/feature";
import { createHotkey } from "features/hotkey"; import { createHotkey } from "features/hotkey";
@ -27,7 +28,7 @@ import Decimal, { DecimalSource, format } from "util/bignum";
import { formatWhole } from "util/break_eternity"; import { formatWhole } from "util/break_eternity";
import { Direction } from "util/common"; import { Direction } from "util/common";
import { render, renderCol, renderRow } from "util/vue"; import { render, renderCol, renderRow } from "util/vue";
import { computed, ref } from "vue"; import { computed, ref, unref } from "vue";
import boxes from "./boxes"; import boxes from "./boxes";
import dyes from "./dyes"; import dyes from "./dyes";
import { ElfBuyable } from "./elves"; import { ElfBuyable } from "./elves";
@ -61,7 +62,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
})); }));
const breeding = createClickable(() => ({ const breeding = createClickable(() => ({
display: { display: {
title: "Breed sheep", title: jsx(() => (
<h3>
Breed sheep <HotkeyVue hotkey={breedSheepHK} />
</h3>
)),
description: jsx(() => ( description: jsx(() => (
<> <>
Breed {formatWhole(Decimal.floor(computedSheepGain.value))} sheep Breed {formatWhole(Decimal.floor(computedSheepGain.value))} sheep
@ -73,14 +78,25 @@ const layer = createLayer(id, function (this: BaseLayer) {
style: { style: {
minHeight: "80px" minHeight: "80px"
}, },
canClick: () => Decimal.gte(breedingProgress.value, computedBreedingCooldown.value), canClick: () =>
Decimal.gte(breedingProgress.value, computedBreedingCooldown.value) &&
(!main.isMastery.value || masteryEffectActive.value),
onClick() { onClick() {
if (Decimal.lt(breedingProgress.value, computedBreedingCooldown.value)) { if (!unref(breeding.canClick)) {
return; return;
} }
// Breed
const amount = Decimal.floor(computedSheepGain.value); const amount = Decimal.floor(computedSheepGain.value);
sheep.value = Decimal.add(sheep.value, amount); sheep.value = Decimal.add(sheep.value, amount);
breedingProgress.value = 0; breedingProgress.value = 0;
if (masteryEffectActive.value) {
// Then shear
let amount = Decimal.min(sheep.value, computedShearingAmount.value).floor();
wool.value = Decimal.add(wool.value, amount);
// Then spin
amount = Decimal.min(wool.value, computedSpinningAmount.value).floor();
cloth.value = Decimal.add(cloth.value, amount);
}
} }
})); }));
@ -97,7 +113,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
})); }));
const shearing = createClickable(() => ({ const shearing = createClickable(() => ({
display: { display: {
title: "Shear sheep", title: jsx(() => (
<h3>
Shear sheep <HotkeyVue hotkey={shearSheepHK} />
</h3>
)),
description: jsx(() => ( description: jsx(() => (
<> <>
Shear up to {formatWhole(Decimal.floor(computedShearingAmount.value))} sheep Shear up to {formatWhole(Decimal.floor(computedShearingAmount.value))} sheep
@ -109,14 +129,27 @@ const layer = createLayer(id, function (this: BaseLayer) {
style: { style: {
minHeight: "80px" minHeight: "80px"
}, },
canClick: () => Decimal.gte(shearingProgress.value, computedShearingCooldown.value), canClick: () =>
Decimal.gte(shearingProgress.value, computedShearingCooldown.value) &&
(!main.isMastery.value || masteryEffectActive.value),
onClick() { onClick() {
if (Decimal.lt(shearingProgress.value, computedShearingCooldown.value)) { if (!unref(shearing.canClick)) {
return; return;
} }
if (masteryEffectActive.value) {
// Breed
const amount = Decimal.floor(computedSheepGain.value);
sheep.value = Decimal.add(sheep.value, amount);
}
// Then shear
const amount = Decimal.min(sheep.value, computedShearingAmount.value).floor(); const amount = Decimal.min(sheep.value, computedShearingAmount.value).floor();
wool.value = Decimal.add(wool.value, amount); wool.value = Decimal.add(wool.value, amount);
shearingProgress.value = 0; shearingProgress.value = 0;
if (masteryEffectActive.value) {
// Then spin
const amount = Decimal.min(wool.value, computedSpinningAmount.value).floor();
cloth.value = Decimal.add(cloth.value, amount);
}
} }
})); }));
@ -133,7 +166,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
})); }));
const spinning = createClickable(() => ({ const spinning = createClickable(() => ({
display: { display: {
title: "Spinning wool", title: jsx(() => (
<h3>
Spin wool <HotkeyVue hotkey={spinWoolHK} />
</h3>
)),
description: jsx(() => ( description: jsx(() => (
<> <>
Spin {formatWhole(Decimal.floor(computedSpinningAmount.value))} wool Spin {formatWhole(Decimal.floor(computedSpinningAmount.value))} wool
@ -145,40 +182,56 @@ const layer = createLayer(id, function (this: BaseLayer) {
style: { style: {
minHeight: "80px" minHeight: "80px"
}, },
canClick: () => Decimal.gte(spinningProgress.value, computedSpinningCooldown.value), canClick: () =>
Decimal.gte(spinningProgress.value, computedSpinningCooldown.value) &&
(!main.isMastery.value || masteryEffectActive.value),
onClick() { onClick() {
if (Decimal.lt(spinningProgress.value, computedSpinningCooldown.value)) { if (!unref(spinning.canClick)) {
return; return;
} }
if (masteryEffectActive.value) {
// Breed
let amount = Decimal.floor(computedSheepGain.value);
sheep.value = Decimal.add(sheep.value, amount);
// Then shear
amount = Decimal.min(sheep.value, computedShearingAmount.value).floor();
wool.value = Decimal.add(wool.value, amount);
}
// Then spin
const amount = Decimal.min(wool.value, computedSpinningAmount.value).floor(); const amount = Decimal.min(wool.value, computedSpinningAmount.value).floor();
cloth.value = Decimal.add(cloth.value, amount); cloth.value = Decimal.add(cloth.value, amount);
wool.value = Decimal.sub(wool.value, amount); if (!masteryEffectActive.value) {
wool.value = Decimal.sub(wool.value, amount);
}
spinningProgress.value = 0; spinningProgress.value = 0;
} }
})); }));
const breedSheepHK = createHotkey(() => ({ const breedSheepHK = createHotkey(() => ({
key: "b", key: "b",
description: 'Press the "Breed Sheep" button', description: "Breed sheep",
onPress: () => { onPress: () => {
if (breeding.canClick.value) breeding.onClick(); if (breeding.canClick.value) breeding.onClick();
} },
enabled: main.days[day - 1].opened
})); }));
const shearSheepHK = createHotkey(() => ({ const shearSheepHK = createHotkey(() => ({
key: "h", // For some reason, "shift+s" doesn't work properly key: "h", // For some reason, "shift+s" doesn't work properly
description: 'Press the "Shear Sheep" button', description: "Shear sheep",
onPress: () => { onPress: () => {
if (shearing.canClick.value) shearing.onClick(); if (shearing.canClick.value) shearing.onClick();
} },
enabled: main.days[day - 1].opened
})); }));
const spinWoolHK = createHotkey(() => ({ const spinWoolHK = createHotkey(() => ({
key: "s", key: "s",
description: 'Press the "Spin Wool" button', description: "Spin wool",
onPress: () => { onPress: () => {
if (spinning.canClick.value) spinning.onClick(); if (spinning.canClick.value) spinning.onClick();
} },
enabled: main.days[day - 1].opened
})); }));
const buildPens = createBuyable(() => ({ const buildPens = createBuyable(() => ({
@ -391,6 +444,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
multiplier: 2, multiplier: 2,
description: "Carry cloth in boxes", description: "Carry cloth in boxes",
enabled: boxes.row3Upgrades.clothUpgrade.bought enabled: boxes.row3Upgrades.clothUpgrade.bought
})),
createMultiplicativeModifier(() => ({
multiplier: dyes.boosts.yellow2,
description: "Yellow Dye",
enabled: dyes.masteryEffectActive
})) }))
]); ]);
const computedSheepGain = computed(() => sheepGain.apply(1)); const computedSheepGain = computed(() => sheepGain.apply(1));
@ -431,6 +489,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
multiplier: 2, multiplier: 2,
description: "Carry cloth in boxes", description: "Carry cloth in boxes",
enabled: boxes.row3Upgrades.clothUpgrade.bought enabled: boxes.row3Upgrades.clothUpgrade.bought
})),
createMultiplicativeModifier(() => ({
multiplier: dyes.boosts.yellow2,
description: "Yellow Dye",
enabled: dyes.masteryEffectActive
})) }))
]); ]);
const computedShearingAmount = computed(() => shearingAmount.apply(1)); const computedShearingAmount = computed(() => shearingAmount.apply(1));
@ -471,6 +534,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
multiplier: 2, multiplier: 2,
description: "Carry cloth in boxes", description: "Carry cloth in boxes",
enabled: boxes.row3Upgrades.clothUpgrade.bought enabled: boxes.row3Upgrades.clothUpgrade.bought
})),
createMultiplicativeModifier(() => ({
multiplier: dyes.boosts.yellow2,
description: "Yellow Dye",
enabled: dyes.masteryEffectActive
})) }))
]); ]);
const computedSpinningAmount = computed(() => spinningAmount.apply(1)); const computedSpinningAmount = computed(() => spinningAmount.apply(1));
@ -565,7 +633,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
goal: 1e3, goal: 1e3,
name, name,
day, day,
color, background: color,
textColor: "var(--feature-foreground)", textColor: "var(--feature-foreground)",
modal: { modal: {
show: showModifiersModal, show: showModifiersModal,
@ -573,8 +641,41 @@ const layer = createLayer(id, function (this: BaseLayer) {
} }
}); });
const mastery = {
cloth: persistent<DecimalSource>(0),
totalCloth: persistent<DecimalSource>(0),
wool: persistent<DecimalSource>(0),
sheep: persistent<DecimalSource>(0),
buildPens: { amount: persistent<DecimalSource>(0) },
betterShears: { amount: persistent<DecimalSource>(0) },
fasterSpinning: { amount: persistent<DecimalSource>(0) },
treesUpgrades: {
treesUpgrade1: { bought: persistent<boolean>(false) },
treesUpgrade2: { bought: persistent<boolean>(false) },
treesUpgrade3: { bought: persistent<boolean>(false) },
treesUpgrade4: { bought: persistent<boolean>(false) }
},
metalUpgrades: {
metalUpgrade1: { bought: persistent<boolean>(false) },
metalUpgrade2: { bought: persistent<boolean>(false) },
metalUpgrade3: { bought: persistent<boolean>(false) },
metalUpgrade4: { bought: persistent<boolean>(false) }
},
paperUpgrades: {
paperUpgrade1: { bought: persistent<boolean>(false) },
paperUpgrade2: { bought: persistent<boolean>(false) },
paperUpgrade3: { bought: persistent<boolean>(false) },
paperUpgrade4: { bought: persistent<boolean>(false) }
}
};
const mastered = persistent<boolean>(false);
const masteryEffectActive = computed(
() => mastered.value || main.currentlyMastering.value?.name === name
);
return { return {
name, name,
day,
color, color,
cloth, cloth,
totalCloth, totalCloth,
@ -598,6 +699,17 @@ const layer = createLayer(id, function (this: BaseLayer) {
<> <>
{render(trackerDisplay)} {render(trackerDisplay)}
<Spacer /> <Spacer />
{masteryEffectActive.value ? (
<>
<div class="decoration-effect ribbon">
Decoration effect:
<br />
Performing any action performs all actions and spinning doesn't spend
wool
</div>
<Spacer />
</>
) : null}
<MainDisplay resource={cloth} style="margin-bottom: 0" /> <MainDisplay resource={cloth} style="margin-bottom: 0" />
<MainDisplay resource={wool} style="margin-bottom: 0" /> <MainDisplay resource={wool} style="margin-bottom: 0" />
<MainDisplay resource={sheep} style="margin-bottom: 0" /> <MainDisplay resource={sheep} style="margin-bottom: 0" />
@ -614,9 +726,13 @@ const layer = createLayer(id, function (this: BaseLayer) {
minimizedDisplay: jsx(() => ( minimizedDisplay: jsx(() => (
<div> <div>
{name}{" "} {name}{" "}
<span class="desc">{format(cloth.value)} {cloth.displayName}</span> <span class="desc">
</div> {format(cloth.value)} {cloth.displayName}
</span>
</div>
)), )),
mastery,
mastered
}; };
}); });

View file

@ -12,7 +12,7 @@ import {
setUpDailyProgressTracker setUpDailyProgressTracker
} from "data/common"; } from "data/common";
import { main } from "data/projEntry"; import { main } from "data/projEntry";
import { createBuyable, GenericBuyable } from "features/buyable"; import { createBuyable } from "features/buyable";
import { jsx, JSXFunction, showIf, StyleValue, Visibility } from "features/feature"; import { jsx, JSXFunction, showIf, StyleValue, Visibility } from "features/feature";
import MainDisplay from "features/resources/MainDisplay.vue"; import MainDisplay from "features/resources/MainDisplay.vue";
import { createResource, Resource } from "features/resources/resource"; import { createResource, Resource } from "features/resources/resource";
@ -33,15 +33,15 @@ import { render, renderGrid, renderRow } from "util/vue";
import { computed, ref, unref } from "vue"; import { computed, ref, unref } from "vue";
import boxes from "./boxes"; import boxes from "./boxes";
import cloth from "./cloth"; import cloth from "./cloth";
import dyes from "./dyes";
import elves, { ElfBuyable } from "./elves"; import elves, { ElfBuyable } from "./elves";
import management from "./management";
import metal from "./metal"; import metal from "./metal";
import oil from "./oil"; import oil from "./oil";
import paper from "./paper"; import paper from "./paper";
import trees from "./trees";
import dyes from "./dyes";
import management from "./management";
import wrappingPaper from "./wrapping-paper";
import plastic from "./plastic"; import plastic from "./plastic";
import trees from "./trees";
import wrappingPaper from "./wrapping-paper";
interface BetterFertilizerUpgOptions { interface BetterFertilizerUpgOptions {
canAfford: () => boolean; canAfford: () => boolean;
@ -101,10 +101,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.smallFireBook.totalAmount.value).times(v); v = Decimal.pow(0.95, paper.books.smallFireBook.totalAmount.value).times(v);
return v.pow(1.5).times(1e4); return v.pow(masteryEffectActive.value ? 1.1 : 1.5).times(1e4);
}, },
inverseCost(x: DecimalSource) { inverseCost(x: DecimalSource) {
let v = Decimal.div(x, 1e4).root(1.5); let v = Decimal.div(x, 1e4).root(masteryEffectActive.value ? 1.1 : 1.5);
v = v.div(Decimal.pow(0.95, paper.books.smallFireBook.totalAmount.value)); v = v.div(Decimal.pow(0.95, paper.books.smallFireBook.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);
@ -134,7 +134,8 @@ const layer = createLayer(id, function (this: BaseLayer) {
color: colorText, color: colorText,
width: "160px", width: "160px",
flexGrow: 1 flexGrow: 1
} },
visibility: () => showIf(!main.isMastery.value || masteryEffectActive.value)
})) as ElfBuyable & { resource: Resource }; })) as ElfBuyable & { resource: Resource };
const { const {
@ -617,7 +618,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
createMultiplicativeModifier(() => ({ createMultiplicativeModifier(() => ({
multiplier: 2, multiplier: 2,
description: "Dedicated Cutter Heaters", description: "Dedicated Cutter Heaters",
enabled: dedicatedCutters.bought enabled: () => dedicatedCutters.bought.value
})) }))
]); ]);
const computedHeatedCutterEffect = computed(() => heatedCutterEffect.apply(1)); const computedHeatedCutterEffect = computed(() => heatedCutterEffect.apply(1));
@ -635,7 +636,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
createMultiplicativeModifier(() => ({ createMultiplicativeModifier(() => ({
multiplier: 2, multiplier: 2,
description: "Dedicated Planter Heaters", description: "Dedicated Planter Heaters",
enabled: dedicatedPlanters.bought enabled: () => dedicatedPlanters.bought.value
})) }))
]); ]);
const computedHeatedPlanterEffect = computed(() => heatedPlanterEffect.apply(1)); const computedHeatedPlanterEffect = computed(() => heatedPlanterEffect.apply(1));
@ -653,7 +654,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
createMultiplicativeModifier(() => ({ createMultiplicativeModifier(() => ({
multiplier: 2, multiplier: 2,
description: "Mulched Soil", description: "Mulched Soil",
enabled: betterFertilizer.bought enabled: () => betterFertilizer.bought.value
})) }))
]); ]);
const computedFertilizerEffect = computed(() => fertilizerEffect.apply(1)); const computedFertilizerEffect = computed(() => fertilizerEffect.apply(1));
@ -788,6 +789,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
exponent: 1.05, exponent: 1.05,
description: "Jack Level 2", description: "Jack Level 2",
enabled: management.elfTraining.heatedCutterElfTraining.milestones[1].earned enabled: management.elfTraining.heatedCutterElfTraining.milestones[1].earned
})),
createAdditiveModifier(() => ({
addend: oil.burnerCoal,
description: "Oil Decoration",
enabled: oil.masteryEffectActive
})) }))
]) as WithRequired<Modifier, "description" | "revert">; ]) as WithRequired<Modifier, "description" | "revert">;
const computedCoalGain = computed(() => coalGain.apply(0)); const computedCoalGain = computed(() => coalGain.apply(0));
@ -985,15 +991,49 @@ const layer = createLayer(id, function (this: BaseLayer) {
goal: 1e7, goal: 1e7,
name, name,
day, day,
color: colorCoal, background: colorCoal,
modal: { modal: {
show: showModifiersModal, show: showModifiersModal,
display: modifiersModal display: modifiersModal
} }
}); });
const mastery = {
coal: persistent<DecimalSource>(0),
totalCoal: persistent<DecimalSource>(0),
ash: persistent<DecimalSource>(0),
activeFires: persistent<DecimalSource>(0),
buildFire: { amount: persistent<DecimalSource>(0) },
activeBonfires: persistent<DecimalSource>(0),
buildBonfire: { amount: persistent<DecimalSource>(0) },
activeKilns: persistent<DecimalSource>(0),
buildKiln: { amount: persistent<DecimalSource>(0) },
activeDrills: persistent<DecimalSource>(0),
buildDrill: { amount: persistent<DecimalSource>(0) },
warmerCutters: { bought: persistent<boolean>(false) },
warmerPlanters: { bought: persistent<boolean>(false) },
basicFertilizer: { bought: persistent<boolean>(false) },
unlockBonfire: { bought: persistent<boolean>(false) },
dedicatedCutters: { bought: persistent<boolean>(false) },
dedicatedPlanters: { bought: persistent<boolean>(false) },
betterFertilizer: { bought: persistent<boolean>(false) },
unlockKiln: { bought: persistent<boolean>(false) },
efficientSmelther: { bought: persistent<boolean>(false) },
arsonistAssistance: { bought: persistent<boolean>(false) },
refinedCoal: { bought: persistent<boolean>(false) },
coloredFire: { bought: persistent<boolean>(false) },
heatedCutters: { amount: persistent<DecimalSource>(0) },
heatedPlanters: { amount: persistent<DecimalSource>(0) },
moreFertilizer: { amount: persistent<DecimalSource>(0) }
};
const mastered = persistent<boolean>(false);
const masteryEffectActive = computed(
() => mastered.value || main.currentlyMastering.value?.name === name
);
return { return {
name, name,
day,
color: colorCoal, color: colorCoal,
coal, coal,
totalCoal, totalCoal,
@ -1031,6 +1071,16 @@ const layer = createLayer(id, function (this: BaseLayer) {
<> <>
{render(trackerDisplay)} {render(trackerDisplay)}
<Spacer /> <Spacer />
{masteryEffectActive.value ? (
<>
<div class="decoration-effect">
Decoration effect:
<br />
Small fires' price increases drastically slower
</div>
<Spacer />
</>
) : null}
<MainDisplay <MainDisplay
resource={coal} resource={coal}
color={colorCoal} color={colorCoal}
@ -1111,9 +1161,13 @@ const layer = createLayer(id, function (this: BaseLayer) {
minimizedDisplay: jsx(() => ( minimizedDisplay: jsx(() => (
<div> <div>
{name}{" "} {name}{" "}
<span class="desc">{format(coal.value)} {coal.displayName}</span> <span class="desc">
</div> {format(coal.value)} {coal.displayName}
</span>
</div>
)), )),
mastery,
mastered
}; };
}); });

View file

@ -2,12 +2,14 @@
* @module * @module
* @hidden * @hidden
*/ */
import HotkeyVue from "components/Hotkey.vue";
import Spacer from "components/layout/Spacer.vue"; import Spacer from "components/layout/Spacer.vue";
import Sqrt from "components/math/Sqrt.vue"; import Sqrt from "components/math/Sqrt.vue";
import Modal from "components/Modal.vue"; import Modal from "components/Modal.vue";
import { createCollapsibleModifierSections, setUpDailyProgressTracker } from "data/common"; import { createCollapsibleModifierSections, setUpDailyProgressTracker } from "data/common";
import { BuyableOptions, createBuyable, GenericBuyable } from "features/buyable"; import { BuyableOptions, createBuyable } from "features/buyable";
import { jsx, JSXFunction, showIf, Visibility } from "features/feature"; import { jsx, JSXFunction, showIf, Visibility } from "features/feature";
import { createHotkey, GenericHotkey } from "features/hotkey";
import MainDisplay from "features/resources/MainDisplay.vue"; import MainDisplay from "features/resources/MainDisplay.vue";
import { createResource, Resource } from "features/resources/resource"; import { createResource, Resource } from "features/resources/resource";
import { createUpgrade, GenericUpgrade } from "features/upgrades/upgrade"; import { createUpgrade, GenericUpgrade } from "features/upgrades/upgrade";
@ -18,30 +20,30 @@ import {
createSequentialModifier, createSequentialModifier,
Modifier Modifier
} from "game/modifiers"; } from "game/modifiers";
import { NonPersistent, noPersist, Persistent } from "game/persistence"; import { persistent } from "game/persistence";
import Decimal, { DecimalSource, format, formatWhole } from "util/bignum"; import Decimal, { DecimalSource, format, formatWhole } from "util/bignum";
import { WithRequired } from "util/common"; import { WithRequired } from "util/common";
import { Computable, convertComputable } from "util/computed"; import { Computable, convertComputable } from "util/computed";
import { render, renderCol, renderRow } from "util/vue"; import { render, renderCol, renderRow } from "util/vue";
import { computed, ComputedRef, ref, Ref, unref } from "vue"; import { computed, ComputedRef, ref, Ref, unref } from "vue";
import { main } from "../projEntry";
import boxes from "./boxes";
import cloth from "./cloth"; import cloth from "./cloth";
import coal from "./coal"; import coal from "./coal";
import { ElfBuyable } from "./elves";
import management from "./management"; import management from "./management";
import oil from "./oil"; import oil from "./oil";
import trees from "./trees";
import wrappingPaper from "./wrapping-paper";
import paper from "./paper"; import paper from "./paper";
import boxes from "./boxes"; import trees from "./trees";
import { ElfBuyable } from "./elves";
interface Dye { interface Dye {
name: string; name: string;
amount: Resource<DecimalSource> & amount: Resource<DecimalSource>;
Persistent<DecimalSource> & { [NonPersistent]: Resource<DecimalSource> };
buyable: ElfBuyable; buyable: ElfBuyable;
toGenerate: WithRequired<Modifier, "description" | "revert">; toGenerate: WithRequired<Modifier, "description" | "revert">;
computedToGenerate: ComputedRef<DecimalSource>; computedToGenerate: ComputedRef<DecimalSource>;
display: JSXFunction; display: JSXFunction;
hotkey: GenericHotkey;
} }
type DyeUpg = type DyeUpg =
@ -53,7 +55,7 @@ type DyeUpg =
| "blueDyeUpg2" | "blueDyeUpg2"
| "coalUpg"; | "coalUpg";
export type enumColor = "red" | "green" | "blue" | "yellow" | "purple" | "orange"; export type enumColor = "red" | "green" | "blue" | "yellow" | "purple" | "orange" | "black";
const id = "dyes"; const id = "dyes";
const day = 11; const day = 11;
@ -61,10 +63,16 @@ const layer = createLayer(id, function (this: BaseLayer) {
const name = "Dyes"; const name = "Dyes";
const color = "#D4D4F4"; const color = "#D4D4F4";
const masteryEffectActive = computed(
() => mastered.value || main.currentlyMastering.value?.name === name
);
function createDye( function createDye(
options: { options: {
name: string; name: string;
color: string; color: string;
shadowColor?: string;
key: string;
costs: Computable< costs: Computable<
{ {
base: Ref<DecimalSource> | DecimalSource; base: Ref<DecimalSource> | DecimalSource;
@ -82,7 +90,15 @@ const layer = createLayer(id, function (this: BaseLayer) {
}[]; }[];
} & Partial<BuyableOptions> } & Partial<BuyableOptions>
): Dye { ): Dye {
const amount = createResource<DecimalSource>(0, options.name); const amount = createResource(
computed(() =>
Decimal.add(buyable.amount.value, 1)
.mul(buyable.amount.value)
.div(2)
.mul(computedToGenerate.value)
),
options.name
);
const toGenerate = createSequentialModifier(() => { const toGenerate = createSequentialModifier(() => {
const modifiers = [ const modifiers = [
@ -91,27 +107,29 @@ const layer = createLayer(id, function (this: BaseLayer) {
description: `${options.name} Chambers` description: `${options.name} Chambers`
})) }))
]; ];
if (options.color === "yellow" && oil.row3Upgrades[0].bought.value) { if (options.color === "yellow") {
modifiers.push( modifiers.push(
createMultiplicativeModifier(() => ({ createMultiplicativeModifier(() => ({
multiplier() { multiplier() {
return Decimal.add(dyes.red.amount.value, 1).log10().add(1).pow(0.75); return Decimal.add(dyes.red.amount.value, 1).log10().add(1).pow(0.75);
}, },
description: "Dye Synergy I" description: "Dye Synergy I",
enabled: oil.row3Upgrades[0].bought
})) }))
); );
} }
if (options.color === "red" && oil.row3Upgrades[3].bought.value) { if (options.color === "red") {
modifiers.push( modifiers.push(
createMultiplicativeModifier(() => ({ createMultiplicativeModifier(() => ({
multiplier() { multiplier() {
return Decimal.add(dyes.blue.amount.value, 1).log10(); return Decimal.add(dyes.blue.amount.value, 1).log10().add(1);
}, },
description: "Dye Synergy II" description: "Dye Synergy II",
enabled: oil.row3Upgrades[3].bought
})) }))
); );
} }
if (options.color === "red" || options.color === "yellow") { if (["red", "yellow"].includes(options.color)) {
modifiers.push( modifiers.push(
createMultiplicativeModifier(() => ({ createMultiplicativeModifier(() => ({
multiplier: boosts.orange1, multiplier: boosts.orange1,
@ -119,7 +137,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
})) }))
); );
} }
if (options.color == "yellow" || options.color == "blue") { if (["yellow", "blue"].includes(options.color)) {
modifiers.push( modifiers.push(
createMultiplicativeModifier(() => ({ createMultiplicativeModifier(() => ({
multiplier: boosts.green1, multiplier: boosts.green1,
@ -127,7 +145,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
})) }))
); );
} }
if (options.color == "red" || options.color == "blue") { if (["red", "blue"].includes(options.color)) {
modifiers.push( modifiers.push(
createMultiplicativeModifier(() => ({ createMultiplicativeModifier(() => ({
multiplier: boosts.purple1, multiplier: boosts.purple1,
@ -135,7 +153,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
})) }))
); );
} }
if (options.color == "red" || options.color == "yellow" || options.color == "blue") { if (["red", "yellow", "blue"].includes(options.color)) {
modifiers.push( modifiers.push(
createMultiplicativeModifier(() => ({ createMultiplicativeModifier(() => ({
multiplier: 2, multiplier: 2,
@ -153,21 +171,17 @@ const layer = createLayer(id, function (this: BaseLayer) {
modifiers.push( modifiers.push(
createMultiplicativeModifier(() => ({ createMultiplicativeModifier(() => ({
multiplier: 2, multiplier: 2,
description: "Wrapping Paper Milestone 1", description: "Carol Level 1",
enabled: wrappingPaper.milestones.primaryBoost.earned enabled: management.elfTraining.dyeElfTraining.milestones[0].earned
})) }))
); );
} }
if ( if (["orange", "green", "purple"].includes(options.color)) {
options.color == "orange" ||
options.color == "green" ||
options.color == "purple"
) {
modifiers.push( modifiers.push(
createMultiplicativeModifier(() => ({ createMultiplicativeModifier(() => ({
multiplier: 2, multiplier: 2,
description: "Wrapping Paper Milestone 2", description: "Carol Level 2",
enabled: wrappingPaper.milestones.secondaryBoost.earned enabled: management.elfTraining.dyeElfTraining.milestones[1].earned
})) }))
); );
} }
@ -189,6 +203,25 @@ const layer = createLayer(id, function (this: BaseLayer) {
}) as WithRequired<Modifier, "description" | "revert">; }) as WithRequired<Modifier, "description" | "revert">;
const computedToGenerate = computed(() => toGenerate.apply(0)); const computedToGenerate = computed(() => toGenerate.apply(0));
let dyeBook: ElfBuyable & {
resource: Resource;
freeLevels: ComputedRef<DecimalSource>;
totalAmount: ComputedRef<DecimalSource>;
};
switch (options.color) {
case "red":
case "yellow":
case "blue":
case "black":
dyeBook = paper.books.primaryDyeBook;
break;
case "orange":
case "green":
case "purple":
dyeBook = paper.books.secondaryDyeBook;
break;
}
const buyable: ElfBuyable = createBuyable(() => { const buyable: ElfBuyable = createBuyable(() => {
const costs = convertComputable(options.costs); const costs = convertComputable(options.costs);
return { return {
@ -200,7 +233,9 @@ const layer = createLayer(id, function (this: BaseLayer) {
display: jsx(() => { display: jsx(() => {
return ( return (
<span> <span>
<h3>{options.name} Chambers</h3> <h3>
{options.name} Chambers <HotkeyVue hotkey={hotkey} />
</h3>
<br /> <br />
Create {format(computedToGenerate.value)} {options.name} Create {format(computedToGenerate.value)} {options.name}
{options.dyesToReset.length > 0 {options.dyesToReset.length > 0
@ -222,7 +257,21 @@ const layer = createLayer(id, function (this: BaseLayer) {
{unref(costs).map(c => {unref(costs).map(c =>
render( render(
jsx(() => ( jsx(() => (
<div> <div
class={
Decimal.lt(
c.res.value,
unref(
Decimal.pow(
unref(buyable.cost) ?? Decimal.dInf,
unref(c.root ?? 1)
).times(unref(c.base))
)
)
? "unaffordable"
: ""
}
>
{format( {format(
unref( unref(
Decimal.pow( Decimal.pow(
@ -245,12 +294,14 @@ const layer = createLayer(id, function (this: BaseLayer) {
let v = buyable.amount.value; let v = buyable.amount.value;
if (Decimal.gte(v, 25)) v = Decimal.pow(v, 2).div(20); // intentional price jump #2 if (Decimal.gte(v, 25)) v = Decimal.pow(v, 2).div(20); // intentional price jump #2
if (Decimal.gte(v, 10)) v = Decimal.pow(v, 2).div(5); // intentional price jump if (Decimal.gte(v, 10)) v = Decimal.pow(v, 2).div(5); // intentional price jump
v = Decimal.mul(v, Decimal.pow(0.95, paper.books.dyeBook.totalAmount.value)); if (Decimal.gte(v, 3125)) v = Decimal.pow(v, 2).div(3125);
v = Decimal.mul(v, Decimal.pow(0.95, dyeBook.totalAmount.value));
return Decimal.div(v, 10).plus(1); return Decimal.div(v, 10).plus(1);
}, },
inverseCostPre(x: DecimalSource) { inverseCostPre(x: DecimalSource) {
let v = Decimal.sub(x, 1).mul(10); let v = Decimal.sub(x, 1).mul(10);
v = v.div(Decimal.pow(0.95, paper.books.dyeBook.totalAmount.value)); v = v.div(Decimal.pow(0.95, dyeBook.totalAmount.value));
if (Decimal.gte(v, 3125)) v = Decimal.mul(v, 3125).root(2);
if (Decimal.gte(v, 10)) v = Decimal.mul(v, 5).root(2); if (Decimal.gte(v, 10)) v = Decimal.mul(v, 5).root(2);
if (Decimal.gte(v, 25)) v = Decimal.mul(v, 20).root(2); if (Decimal.gte(v, 25)) v = Decimal.mul(v, 20).root(2);
return Decimal.isNaN(v) ? Decimal.dZero : v.floor().max(0); return Decimal.isNaN(v) ? Decimal.dZero : v.floor().max(0);
@ -269,7 +320,12 @@ const layer = createLayer(id, function (this: BaseLayer) {
); );
}, },
canPurchase: computed((cost?: DecimalSource) => { canPurchase: computed((cost?: DecimalSource) => {
if (unref(buyable.visibility) != Visibility.Visible) return false; if (unref(buyable.visibility) != Visibility.Visible) {
return false;
}
if (main.isMastery.value && !masteryEffectActive.value) {
return false;
}
const trueCost = cost ?? unref(buyable.cost) ?? Decimal.dInf; const trueCost = cost ?? unref(buyable.cost) ?? Decimal.dInf;
return unref(costs).every(c => return unref(costs).every(c =>
Decimal.div(c.res.value, unref(c.base)) Decimal.div(c.res.value, unref(c.base))
@ -278,34 +334,57 @@ const layer = createLayer(id, function (this: BaseLayer) {
); );
}), }),
onPurchase(cost?: DecimalSource) { onPurchase(cost?: DecimalSource) {
const trueCost = cost ?? unref(buyable.cost) ?? Decimal.dInf; let buyMax = false;
switch (options.color) {
case "red":
case "yellow":
case "blue":
buyMax =
management.elfTraining.dyeElfTraining.milestones[2].earned.value;
break;
case "orange":
case "green":
case "purple":
buyMax =
management.elfTraining.dyeElfTraining.milestones[4].earned.value;
break;
}
amount.value = Decimal.add(amount.value, computedToGenerate.value); if (buyMax) {
buyable.amount.value = Decimal.add(buyable.amount.value, 1); const buyAmount = this.inverseCost().sub(this.amount.value).plus(1);
if (buyAmount.lte(0)) return;
if (!wrappingPaper.milestones.secondaryNoReset.earned.value) { buyable.amount.value = Decimal.add(buyable.amount.value, buyAmount);
unref(costs).forEach(c => { } else {
c.res.value = Decimal.sub( buyable.amount.value = Decimal.add(buyable.amount.value, 1);
c.res.value, }
Decimal.pow(trueCost, unref(c.root ?? 1)).times(unref(c.base)) if (!management.elfTraining.dyeElfTraining.milestones[3].earned.value) {
);
});
options.dyesToReset.forEach(dye => dye.reset()); options.dyesToReset.forEach(dye => dye.reset());
} }
} }
}; };
}); });
const hotkey = createHotkey(() => ({
key: options.key,
description: `${options.name} Chambers`,
onPress: () => {
if (unref(buyable.canClick)) buyable.onClick();
},
enabled: main.days[day - 1].opened
}));
return { return {
name: options.name, name: options.name,
amount, amount,
buyable, buyable,
hotkey,
toGenerate, toGenerate,
computedToGenerate, computedToGenerate,
display: jsx(() => ( display: jsx(() => (
<MainDisplay <MainDisplay
resource={amount} resource={amount}
color={options.color} color={options.color}
shadowColor={options.shadowColor ?? options.color}
style="margin: 0; width: 200px; width: 180px; padding: 10px;" style="margin: 0; width: 200px; width: 180px; padding: 10px;"
sticky={false} sticky={false}
/> />
@ -317,6 +396,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
red: createDye({ red: createDye({
name: "Red Dye", name: "Red Dye",
color: "red", color: "red",
key: "r",
costs: () => [ costs: () => [
{ {
base: "2e18", base: "2e18",
@ -338,6 +418,10 @@ const layer = createLayer(id, function (this: BaseLayer) {
boosts.red1.value boosts.red1.value
)} effective Oil Pumps (does not impact coal consumption)` )} effective Oil Pumps (does not impact coal consumption)`
) )
},
{
visible: masteryEffectActive,
desc: computed(() => `x${format(boosts.red2.value)} drill power`)
} }
], ],
dyesToReset: [] dyesToReset: []
@ -345,6 +429,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
yellow: createDye({ yellow: createDye({
name: "Yellow Dye", name: "Yellow Dye",
color: "yellow", color: "yellow",
key: "y",
costs: () => [ costs: () => [
{ {
base: "1e18", base: "1e18",
@ -361,6 +446,10 @@ const layer = createLayer(id, function (this: BaseLayer) {
{ {
visible: true, visible: true,
desc: computed(() => `x${format(boosts.yellow1.value)} Paper \& Plastic gain`) desc: computed(() => `x${format(boosts.yellow1.value)} Paper \& Plastic gain`)
},
{
visible: masteryEffectActive,
desc: computed(() => `x${format(boosts.yellow2.value)} cloth actions`)
} }
], ],
dyesToReset: [] dyesToReset: []
@ -368,6 +457,8 @@ const layer = createLayer(id, function (this: BaseLayer) {
blue: createDye({ blue: createDye({
name: "Blue Dye", name: "Blue Dye",
color: "blue", color: "blue",
shadowColor: "lightblue",
key: "u",
costs: () => [ costs: () => [
{ {
base: "5e17", base: "5e17",
@ -387,8 +478,36 @@ const layer = createLayer(id, function (this: BaseLayer) {
() => () =>
`+${formatWhole( `+${formatWhole(
boosts.blue1.value boosts.blue1.value
)} forest size (after all other modifiers).` )} forest size (after all other modifiers)`
) )
},
{
visible: masteryEffectActive,
desc: computed(() => `/${format(boosts.blue2.value)} plastic buyables cost`)
}
],
dyesToReset: []
}),
black: createDye({
name: "Black Dye",
color: "black",
key: "a",
costs: () => [
{
base: "1e42",
root: 5,
res: trees.logs
},
{
base: computed(() => (upgrades.yellowDyeUpg2.bought.value ? "1e15" : "2e15")),
root: 2,
res: oil.oil
}
],
listedBoosts: [
{
visible: true,
desc: computed(() => `*${format(boosts.black1.value)} letters processed.`)
} }
], ],
dyesToReset: [] dyesToReset: []
@ -396,6 +515,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
orange: createDye({ orange: createDye({
name: "Orange Dye", name: "Orange Dye",
color: "orange", color: "orange",
key: "o",
costs: () => [ costs: () => [
{ {
base: 15, base: 15,
@ -422,14 +542,12 @@ const layer = createLayer(id, function (this: BaseLayer) {
{ {
name: "Red Dye", name: "Red Dye",
reset() { reset() {
dyes.red.amount.value = 0;
dyes.red.buyable.amount.value = 0; dyes.red.buyable.amount.value = 0;
} }
}, },
{ {
name: "Yellow Dye", name: "Yellow Dye",
reset() { reset() {
dyes.yellow.amount.value = 0;
dyes.yellow.buyable.amount.value = 0; dyes.yellow.buyable.amount.value = 0;
} }
} }
@ -438,6 +556,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
green: createDye({ green: createDye({
name: "Green Dye", name: "Green Dye",
color: "green", color: "green",
key: "g",
costs: () => [ costs: () => [
{ {
base: 15, base: 15,
@ -469,14 +588,12 @@ const layer = createLayer(id, function (this: BaseLayer) {
{ {
name: "Yellow Dye", name: "Yellow Dye",
reset() { reset() {
dyes.yellow.amount.value = 0;
dyes.yellow.buyable.amount.value = 0; dyes.yellow.buyable.amount.value = 0;
} }
}, },
{ {
name: "Blue Dye", name: "Blue Dye",
reset() { reset() {
dyes.blue.amount.value = 0;
dyes.blue.buyable.amount.value = 0; dyes.blue.buyable.amount.value = 0;
} }
} }
@ -485,6 +602,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
purple: createDye({ purple: createDye({
name: "Purple Dye", name: "Purple Dye",
color: "purple", color: "purple",
key: "e",
costs: () => [ costs: () => [
{ {
base: 15, base: 15,
@ -513,14 +631,12 @@ const layer = createLayer(id, function (this: BaseLayer) {
{ {
name: "Blue Dye", name: "Blue Dye",
reset() { reset() {
dyes.blue.amount.value = 0;
dyes.blue.buyable.amount.value = 0; dyes.blue.buyable.amount.value = 0;
} }
}, },
{ {
name: "Red Dye", name: "Red Dye",
reset() { reset() {
dyes.red.amount.value = 0;
dyes.red.buyable.amount.value = 0; dyes.red.buyable.amount.value = 0;
} }
} }
@ -535,8 +651,16 @@ const layer = createLayer(id, function (this: BaseLayer) {
upgrades.blueDyeUpg2.bought.value ? 1.5 : 1 upgrades.blueDyeUpg2.bought.value ? 1.5 : 1
) )
), ),
red2: computed(() =>
Decimal.pow(
Decimal.add(dyes.red.amount.value, 1).log2().plus(1),
upgrades.blueDyeUpg2.bought.value ? 1.5 : 1
)
),
yellow1: computed(() => Decimal.add(dyes.yellow.amount.value, 1).log2().plus(1)), yellow1: computed(() => Decimal.add(dyes.yellow.amount.value, 1).log2().plus(1)),
yellow2: computed(() => Decimal.add(dyes.yellow.amount.value, 1).log2().plus(1).times(3)),
blue1: computed(() => Decimal.add(dyes.blue.amount.value, 1).log2().sqrt().times(5e6)), blue1: computed(() => Decimal.add(dyes.blue.amount.value, 1).log2().sqrt().times(5e6)),
blue2: computed(() => Decimal.add(dyes.blue.amount.value, 1).log2().plus(1).pow(2)),
orange1: computed(() => orange1: computed(() =>
Decimal.pow(2, Decimal.add(dyes.orange.amount.value, 1).log2().sqrt()) Decimal.pow(2, Decimal.add(dyes.orange.amount.value, 1).log2().sqrt())
@ -565,7 +689,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)),
black1: computed(() =>
Decimal.pow(2, Decimal.add(dyes.black.amount.value, 1).log2().sqrt())
.pow(upgrades.coalUpg.bought.value ? 1.2 : 1)
.pow(management.elfTraining.clothElfTraining.milestones[3].earned.value ? 1.1 : 1)
)
}; };
const [generalTab, generalTabCollapsed] = createCollapsibleModifierSections(() => [ const [generalTab, generalTabCollapsed] = createCollapsibleModifierSections(() => [
@ -584,6 +713,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
modifier: dyes.blue.toGenerate, modifier: dyes.blue.toGenerate,
base: 0 base: 0
}, },
{
title: "Black Dye Creation",
modifier: dyes.black.toGenerate,
base: 0
},
{ {
title: "Orange Dye Creation", title: "Orange Dye Creation",
modifier: dyes.orange.toGenerate, modifier: dyes.orange.toGenerate,
@ -618,9 +752,8 @@ const layer = createLayer(id, function (this: BaseLayer) {
)) ))
}, },
cost: 1000, cost: 1000,
resource: noPersist(dyes.blue.amount), resource: dyes.blue.amount,
onPurchase() { onPurchase() {
dyes.blue.amount.value = 0;
dyes.blue.buyable.amount.value = 0; dyes.blue.buyable.amount.value = 0;
} }
})), })),
@ -640,9 +773,8 @@ const layer = createLayer(id, function (this: BaseLayer) {
)) ))
}, },
cost: 1500, cost: 1500,
resource: noPersist(dyes.red.amount), resource: dyes.red.amount,
onPurchase() { onPurchase() {
dyes.red.amount.value = 0;
dyes.red.buyable.amount.value = 0; dyes.red.buyable.amount.value = 0;
} }
})), })),
@ -655,20 +787,10 @@ const layer = createLayer(id, function (this: BaseLayer) {
), ),
display: { display: {
title: "Wetter Dyes", title: "Wetter Dyes",
description: "Double Red, Yellow, and Blue Dye gain, but reset their amounts." description: "Double Red, Yellow, and Blue Dye gain."
}, },
cost: 2000, cost: 2000,
resource: noPersist(dyes.yellow.amount), resource: dyes.yellow.amount
onPurchase() {
dyes.red.amount.value = 0;
dyes.red.buyable.amount.value = 0;
dyes.yellow.amount.value = 0;
dyes.yellow.buyable.amount.value = 0;
dyes.blue.amount.value = 0;
dyes.blue.buyable.amount.value = 0;
}
})), })),
yellowDyeUpg2: createUpgrade(() => ({ yellowDyeUpg2: createUpgrade(() => ({
visibility: () => showIf(upgrades.yellowDyeUpg.bought.value), visibility: () => showIf(upgrades.yellowDyeUpg.bought.value),
@ -677,9 +799,8 @@ const layer = createLayer(id, function (this: BaseLayer) {
description: "Halve the Oil cost of Red, Yellow, and Blue Dyes." description: "Halve the Oil cost of Red, Yellow, and Blue Dyes."
}, },
cost: 5000, cost: 5000,
resource: noPersist(dyes.yellow.amount), resource: dyes.yellow.amount,
onPurchase() { onPurchase() {
dyes.yellow.amount.value = 0;
dyes.yellow.buyable.amount.value = 0; dyes.yellow.buyable.amount.value = 0;
} }
})), })),
@ -694,9 +815,8 @@ const layer = createLayer(id, function (this: BaseLayer) {
)) ))
}, },
cost: 6000, cost: 6000,
resource: noPersist(dyes.red.amount), resource: dyes.red.amount,
onPurchase() { onPurchase() {
dyes.red.amount.value = 0;
dyes.red.buyable.amount.value = 0; dyes.red.buyable.amount.value = 0;
} }
})), })),
@ -707,9 +827,8 @@ const layer = createLayer(id, function (this: BaseLayer) {
description: "Raise Red Dye's effect ^1.5." description: "Raise Red Dye's effect ^1.5."
}, },
cost: 7500, cost: 7500,
resource: noPersist(dyes.blue.amount), resource: dyes.blue.amount,
onPurchase() { onPurchase() {
dyes.blue.amount.value = 0;
dyes.blue.buyable.amount.value = 0; dyes.blue.buyable.amount.value = 0;
} }
})), })),
@ -723,20 +842,10 @@ const layer = createLayer(id, function (this: BaseLayer) {
display: { display: {
title: "Denser Spectrum", title: "Denser Spectrum",
description: description:
"Orange, Green, and Purple Dyes' first effect is raised ^1.2, and Green Dye's second effect is squared. Buying this resets Red, Yellow, and Blue Dyes." "Orange, Green, and Purple Dyes' first effect is raised ^1.2, and Green Dye's second effect is squared."
}, },
cost: "5e30", cost: "5e30",
resource: coal.coal, resource: coal.coal
onPurchase() {
dyes.red.amount.value = 0;
dyes.red.buyable.amount.value = 0;
dyes.yellow.amount.value = 0;
dyes.yellow.buyable.amount.value = 0;
dyes.blue.amount.value = 0;
dyes.blue.buyable.amount.value = 0;
}
})) }))
}; };
@ -770,7 +879,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
goal: 6e4, goal: 6e4,
name, name,
day, day,
color, background: color,
textColor: "var(--feature-foreground)", textColor: "var(--feature-foreground)",
modal: { modal: {
show: showModifiersModal, show: showModifiersModal,
@ -779,8 +888,42 @@ const layer = createLayer(id, function (this: BaseLayer) {
ignoreTotal: true ignoreTotal: true
}); });
const mastery = {
dyes: {
red: {
buyable: { amount: persistent<DecimalSource>(0) }
},
green: {
buyable: { amount: persistent<DecimalSource>(0) }
},
blue: {
buyable: { amount: persistent<DecimalSource>(0) }
},
yellow: {
buyable: { amount: persistent<DecimalSource>(0) }
},
purple: {
buyable: { amount: persistent<DecimalSource>(0) }
},
orange: {
buyable: { amount: persistent<DecimalSource>(0) }
}
},
upgrades: {
blueDyeUpg: { bought: persistent<boolean>(false) },
redDyeUpg: { bought: persistent<boolean>(false) },
yellowDyeUpg: { bought: persistent<boolean>(false) },
yellowDyeUpg2: { bought: persistent<boolean>(false) },
redDyeUpg2: { bought: persistent<boolean>(false) },
blueDyeUpg2: { bought: persistent<boolean>(false) },
coalUpg: { bought: persistent<boolean>(false) }
}
};
const mastered = persistent<boolean>(false);
return { return {
name, name,
day,
color, color,
dyes, dyes,
dyeSum, dyeSum,
@ -794,7 +937,20 @@ const layer = createLayer(id, function (this: BaseLayer) {
<> <>
{render(trackerDisplay)} {render(trackerDisplay)}
<Spacer /> <Spacer />
{masteryEffectActive.value ? (
<>
<div class="decoration-effect ribbon">
Decoration effect:
<br />
Each primary dye gains a second effect
</div>
<Spacer />
</>
) : null}
<div style="width: 620px"> <div style="width: 620px">
{renderRow(dyes.black.display)}
{renderRow(dyes.black.buyable)}
<Spacer />
{renderRow(dyes.red.display, dyes.yellow.display, dyes.blue.display)} {renderRow(dyes.red.display, dyes.yellow.display, dyes.blue.display)}
{renderRow(dyes.red.buyable, dyes.yellow.buyable, dyes.blue.buyable)} {renderRow(dyes.red.buyable, dyes.yellow.buyable, dyes.blue.buyable)}
<Spacer /> <Spacer />
@ -809,7 +965,10 @@ const layer = createLayer(id, function (this: BaseLayer) {
</div> </div>
{render(upgrades.coalUpg)} {render(upgrades.coalUpg)}
</> </>
)) )),
mastery,
mastered,
masteryEffectActive
}; };
}); });

View file

@ -36,7 +36,9 @@ import plastic from "./plastic";
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 dyes from "./dyes"; import dyes, { enumColor } from "./dyes";
import ribbon from "./ribbon";
import letters from "./letters";
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. */
@ -418,12 +420,39 @@ const layer = createLayer(id, function (this: BaseLayer) {
enabled: elvesMilestone2.earned enabled: elvesMilestone2.earned
})) }))
]); ]);
const dyeCooldown = createSequentialModifier(() => [ const dyeCooldown = createSequentialModifier(() => [
createMultiplicativeModifier(() => ({ createMultiplicativeModifier(() => ({
multiplier: Infinity, multiplier: 2,
description: "Dye", description: "6 Elves Trained",
enabled: () => true enabled: elvesMilestone.earned
})),
createMultiplicativeModifier(() => ({
multiplier: () =>
Decimal.times(paper.books.primaryDyeBook.totalAmount.value, 0.1).add(1),
description: "Arts and Crafts",
enabled: () => Decimal.gt(paper.books.primaryDyeBook.totalAmount.value, 0)
})),
createMultiplicativeModifier(() => ({
multiplier: 2,
description: "10 Elves Trained",
enabled: elvesMilestone2.earned
}))
]);
const plasticCooldown = createSequentialModifier(() => [
createMultiplicativeModifier(() => ({
multiplier: 2,
description: "6 Elves Trained",
enabled: elvesMilestone.earned
})),
createMultiplicativeModifier(() => ({
multiplier: () => Decimal.times(paper.books.plasticBook.totalAmount.value, 0.1).add(1),
description: "One Plastic Bag",
enabled: () => Decimal.gt(paper.books.plasticBook.totalAmount.value, 0)
})),
createMultiplicativeModifier(() => ({
multiplier: 2,
description: "10 Elves Trained",
enabled: elvesMilestone2.earned
})) }))
]); ]);
@ -517,28 +546,50 @@ const layer = createLayer(id, function (this: BaseLayer) {
modifier: coalDrillCooldown, modifier: coalDrillCooldown,
base: 10, base: 10,
unit: "/s", unit: "/s",
visible: management.elfTraining.expandersElfTraining.milestones[3].earned visible: () =>
management.elfTraining.expandersElfTraining.milestones[3].earned.value ||
letters.masteryEffectActive.value
}, },
{ {
title: "Frosty Auto-Buy Frequency", title: "Frosty Auto-Buy Frequency",
modifier: heavyDrillCooldown, modifier: heavyDrillCooldown,
base: 10, base: 10,
unit: "/s", unit: "/s",
visible: management.elfTraining.cutterElfTraining.milestones[4].earned.value visible: () =>
management.elfTraining.cutterElfTraining.milestones[4].earned.value ||
letters.masteryEffectActive.value
}, },
{ {
title: "Cocoa Auto-Buy Frequency", title: "Cocoa Auto-Buy Frequency",
modifier: oilCooldown, modifier: oilCooldown,
base: 10, base: 10,
unit: "/s", unit: "/s",
visible: management.elfTraining.heatedCutterElfTraining.milestones[4].earned.value visible: () =>
management.elfTraining.heatedCutterElfTraining.milestones[4].earned.value ||
letters.masteryEffectActive.value
}, },
{ {
title: "Twinkle Auto-Buy Frequency", title: "Twinkle Auto-Buy Frequency",
modifier: metalCooldown, modifier: metalCooldown,
base: 10, base: 10,
unit: "/s", unit: "/s",
visible: management.elfTraining.fertilizerElfTraining.milestones[4].earned visible: () =>
management.elfTraining.fertilizerElfTraining.milestones[4].earned.value ||
letters.masteryEffectActive.value
},
{
title: "Carol Auto-Buy Frequency",
modifier: dyeCooldown,
base: 10,
unit: "/s",
visible: wrappingPaper.unlockDyeElfMilestone.earned.value && !main.isMastery.value
},
{
title: "Tinsel Auto-Buy Frequency",
modifier: plasticCooldown,
base: 10,
unit: "/s",
visible: plastic.masteryEffectActive
} }
]); ]);
const showModifiersModal = ref(false); const showModifiersModal = ref(false);
@ -554,10 +605,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
)); ));
const trainingCost = computed(() => { const trainingCost = computed(() => {
let cost = Decimal.pow( let cost = Decimal.pow(4, totalElves.value).times(1e6);
Decimal.sub(4, wrappingPaper.boosts.jazzy1.value),
totalElves.value
).times(1e6);
if (Decimal.gte(totalElves.value, 9)) { if (Decimal.gte(totalElves.value, 9)) {
cost = Decimal.times(cost, 1e15); cost = Decimal.times(cost, 1e15);
} }
@ -588,7 +636,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
} & Partial<ClickableOptions> } & Partial<ClickableOptions>
) { ) {
const buyProgress = persistent<DecimalSource>(0); const buyProgress = persistent<DecimalSource>(0);
const amountOfTimesDone = persistent(0); const amountOfTimesDone = persistent<number>(0);
const toggle = options.hasToggle ? persistent<boolean>(false) : ref(true); const toggle = options.hasToggle ? persistent<boolean>(false) : ref(true);
const computedAutoBuyCooldown = computed(() => options.cooldownModifier.apply(10)); const computedAutoBuyCooldown = computed(() => options.cooldownModifier.apply(10));
@ -647,6 +695,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
computedAutoBuyCooldown, computedAutoBuyCooldown,
amountOfTimesDone, amountOfTimesDone,
name: options.name, name: options.name,
canAfford() {
return (
Decimal.gte(coal.coal.value, unref(trainingCost)) && !main.isMastery.value
);
},
display: () => ({ display: () => ({
title: options.name, title: options.name,
description: jsx(() => ( description: jsx(() => (
@ -678,13 +731,17 @@ const layer = createLayer(id, function (this: BaseLayer) {
style: "width: 190px", style: "width: 190px",
onPurchase() { onPurchase() {
options.onPurchase?.(); options.onPurchase?.();
if (!["Peppermint", "Twinkle", "Cocoa", "Frosty"].includes(options.name)) { if (
!["Peppermint", "Twinkle", "Cocoa", "Frosty", "Carol"].includes(
options.name
)
) {
elfReset.reset(); elfReset.reset();
} }
} }
}; };
}) as GenericUpgrade & { }) as GenericUpgrade & {
buyProgress: Ref<number>; buyProgress: Ref<DecimalSource>;
update: (diff: number) => void; update: (diff: number) => void;
toggle: Ref<boolean>; toggle: Ref<boolean>;
name: string; name: string;
@ -740,7 +797,8 @@ const layer = createLayer(id, function (this: BaseLayer) {
description: description:
"Noel will automatically purchase fertilized soil you can afford, without actually spending any ash.", "Noel will automatically purchase fertilized soil you can afford, without actually spending any ash.",
buyable: coal.moreFertilizer, buyable: coal.moreFertilizer,
cooldownModifier: fertilizerCooldown cooldownModifier: fertilizerCooldown,
buyMax: () => management.elfTraining.heatedPlanterElfTraining.milestones[2].earned.value
}); });
const coalElves = [heatedCuttersElf, heatedPlantersElf, fertilizerElf]; const coalElves = [heatedCuttersElf, heatedPlantersElf, fertilizerElf];
const smallFireElf = createElf({ const smallFireElf = createElf({
@ -838,7 +896,10 @@ const layer = createLayer(id, function (this: BaseLayer) {
buyable: coal.buildDrill, buyable: coal.buildDrill,
cooldownModifier: coalDrillCooldown, cooldownModifier: coalDrillCooldown,
visibility: () => visibility: () =>
showIf(management.elfTraining.expandersElfTraining.milestones[3].earned.value), showIf(
management.elfTraining.expandersElfTraining.milestones[3].earned.value ||
letters.masteryEffectActive.value
),
hasToggle: true, hasToggle: true,
toggleDesc: "Activate auto-purchased coal drills", toggleDesc: "Activate auto-purchased coal drills",
onAutoPurchase(_, amount) { onAutoPurchase(_, amount) {
@ -854,7 +915,10 @@ const layer = createLayer(id, function (this: BaseLayer) {
buyable: [oil.buildHeavy, oil.buildHeavy2, oil.buildExtractor], buyable: [oil.buildHeavy, oil.buildHeavy2, oil.buildExtractor],
cooldownModifier: heavyDrillCooldown, cooldownModifier: heavyDrillCooldown,
visibility: () => visibility: () =>
showIf(management.elfTraining.cutterElfTraining.milestones[4].earned.value), showIf(
management.elfTraining.cutterElfTraining.milestones[4].earned.value ||
letters.masteryEffectActive.value
),
hasToggle: true, hasToggle: true,
toggleDesc: "Activate auto-purchased oil drills", toggleDesc: "Activate auto-purchased oil drills",
onAutoPurchase(buyable, amount) { onAutoPurchase(buyable, amount) {
@ -876,7 +940,10 @@ const layer = createLayer(id, function (this: BaseLayer) {
buyable: [oil.buildPump, oil.buildBurner, oil.buildSmelter], buyable: [oil.buildPump, oil.buildBurner, oil.buildSmelter],
cooldownModifier: oilCooldown, cooldownModifier: oilCooldown,
visibility: () => visibility: () =>
showIf(management.elfTraining.heatedCutterElfTraining.milestones[4].earned.value), showIf(
management.elfTraining.heatedCutterElfTraining.milestones[4].earned.value ||
letters.masteryEffectActive.value
),
hasToggle: true, hasToggle: true,
toggleDesc: "Activate auto-purchased oil-using machines", toggleDesc: "Activate auto-purchased oil-using machines",
onAutoPurchase(buyable, amount) { onAutoPurchase(buyable, amount) {
@ -899,18 +966,48 @@ const layer = createLayer(id, function (this: BaseLayer) {
buyable: [metal.oreDrill, metal.industrialCrucible, metal.hotterForge], buyable: [metal.oreDrill, metal.industrialCrucible, metal.hotterForge],
cooldownModifier: metalCooldown, cooldownModifier: metalCooldown,
visibility: () => visibility: () =>
showIf(management.elfTraining.fertilizerElfTraining.milestones[4].earned.value) showIf(
management.elfTraining.fertilizerElfTraining.milestones[4].earned.value ||
letters.masteryEffectActive.value
)
}); });
const managementElves2 = [metalElf]; const managementElves2 = [metalElf];
const dyeColors = Object.fromEntries(
(["blue", "red", "yellow", "orange", "green", "purple"] as enumColor[]).map(color => [
dyes.dyes[color].buyable.id,
color
])
) as Record<string, enumColor>;
const dyeElf = createElf({ const dyeElf = createElf({
name: "Carol", name: "Carol",
description: description:
"Carol will automatically purchase all dyes you can afford, without actually spending any resources.", "Carol will automatically purchase all primary dyes you can afford, without actually spending any resources.",
buyable: Object.values(dyes.dyes).map(dye => dye.buyable), buyable: Object.values(dyes.dyes).map(dye => dye.buyable),
cooldownModifier: dyeCooldown, // Note: Buy max will be unlocked at this point cooldownModifier: dyeCooldown, // Note: Buy max will be unlocked at this point
visibility: () => showIf(wrappingPaper.milestones.unlockDyeElf.earned.value) visibility: () =>
showIf(wrappingPaper.unlockDyeElfMilestone.earned.value && !main.isMastery.value),
buyMax: () => management.elfTraining.dyeElfTraining.milestones[2].earned.value,
onAutoPurchase(buyable, amount) {
buyable.amount.value = Decimal.sub(buyable.amount.value, amount);
if (["orange", "green", "purple"].includes(dyeColors[buyable.id])) {
if (!ribbon.milestones.secondaryDyeElf.earned.value) {
return;
}
}
buyable.amount.value = Decimal.add(buyable.amount.value, amount);
}
}); });
const wrappingPaperElves = [dyeElf]; const plasticElf = createElf({
name: "Tinsel",
description:
"Tinsel will automatically purchase all plastic buyables you can afford, without actually spending any resources.",
buyable: Object.values(plastic.buyables),
cooldownModifier: plasticCooldown,
visibility: () => showIf(plastic.masteryEffectActive.value),
buyMax: () => management.elfTraining.plasticElfTraining.milestones[4].earned.value
});
const wrappingPaperElves = [dyeElf, plasticElf];
const elves = { const elves = {
cuttersElf, cuttersElf,
plantersElf, plantersElf,
@ -928,7 +1025,8 @@ const layer = createLayer(id, function (this: BaseLayer) {
heavyDrillElf, heavyDrillElf,
oilElf, oilElf,
metalElf, metalElf,
dyeElf dyeElf,
plasticElf
}; };
const totalElves = computed(() => Object.values(elves).filter(elf => elf.bought.value).length); const totalElves = computed(() => Object.values(elves).filter(elf => elf.bought.value).length);
@ -1010,7 +1108,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
effectDisplay: "Elves work twice as fast (again)" effectDisplay: "Elves work twice as fast (again)"
}, },
shouldEarn: () => Decimal.gte(totalElves.value, 10), shouldEarn: () => Decimal.gte(totalElves.value, 10),
visibility: () => showIf(main.day.value >= 10) visibility: () => showIf(main.day.value >= 10 && treeUpgradesMilestone.earned.value)
})); }));
const coalUpgradesMilestone = createMilestone(() => ({ const coalUpgradesMilestone = createMilestone(() => ({
display: { display: {
@ -1078,8 +1176,124 @@ const layer = createLayer(id, function (this: BaseLayer) {
} }
}); });
const mastery = {
elves: {
cuttersElf: {
buyProgress: persistent<DecimalSource>(0),
amountOfTimesDone: persistent<number>(0),
bought: persistent<boolean>(false)
},
plantersElf: {
buyProgress: persistent<DecimalSource>(0),
amountOfTimesDone: persistent<number>(0),
bought: persistent<boolean>(false)
},
expandersElf: {
buyProgress: persistent<DecimalSource>(0),
amountOfTimesDone: persistent<number>(0),
bought: persistent<boolean>(false)
},
heatedCuttersElf: {
buyProgress: persistent<DecimalSource>(0),
amountOfTimesDone: persistent<number>(0),
bought: persistent<boolean>(false)
},
heatedPlantersElf: {
buyProgress: persistent<DecimalSource>(0),
amountOfTimesDone: persistent<number>(0),
bought: persistent<boolean>(false)
},
fertilizerElf: {
buyProgress: persistent<DecimalSource>(0),
amountOfTimesDone: persistent<number>(0),
bought: persistent<boolean>(false)
},
smallFireElf: {
buyProgress: persistent<DecimalSource>(0),
amountOfTimesDone: persistent<number>(0),
toggle: persistent<boolean>(false),
bought: persistent<boolean>(false)
},
bonfireElf: {
buyProgress: persistent<DecimalSource>(0),
amountOfTimesDone: persistent<number>(0),
toggle: persistent<boolean>(false),
bought: persistent<boolean>(false)
},
kilnElf: {
buyProgress: persistent<DecimalSource>(0),
amountOfTimesDone: persistent<number>(0),
toggle: persistent<boolean>(false),
bought: persistent<boolean>(false)
},
paperElf: {
buyProgress: persistent<DecimalSource>(0),
amountOfTimesDone: persistent<number>(0),
bought: persistent<boolean>(false)
},
boxElf: {
buyProgress: persistent<DecimalSource>(0),
amountOfTimesDone: persistent<number>(0),
bought: persistent<boolean>(false)
},
clothElf: {
buyProgress: persistent<DecimalSource>(0),
amountOfTimesDone: persistent<number>(0),
bought: persistent<boolean>(false)
},
coalDrillElf: {
buyProgress: persistent<DecimalSource>(0),
amountOfTimesDone: persistent<number>(0),
toggle: persistent<boolean>(false),
bought: persistent<boolean>(false)
},
heavyDrillElf: {
buyProgress: persistent<DecimalSource>(0),
amountOfTimesDone: persistent<number>(0),
toggle: persistent<boolean>(false),
bought: persistent<boolean>(false)
},
oilElf: {
buyProgress: persistent<DecimalSource>(0),
amountOfTimesDone: persistent<number>(0),
toggle: persistent<boolean>(false),
bought: persistent<boolean>(false)
},
metalElf: {
buyProgress: persistent<DecimalSource>(0),
amountOfTimesDone: persistent<number>(0),
bought: persistent<boolean>(false)
},
dyeElf: {
buyProgress: persistent<DecimalSource>(0),
amountOfTimesDone: persistent<number>(0),
bought: persistent<boolean>(false)
},
plasticElf: {
buyProgress: persistent<DecimalSource>(0),
amountOfTimesDone: persistent<number>(0),
bought: persistent<boolean>(false)
}
},
milestones: [
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) }
]
};
return { return {
name, name,
day,
color: colorBright, color: colorBright,
elves, elves,
totalElves, totalElves,
@ -1119,7 +1333,8 @@ const layer = createLayer(id, function (this: BaseLayer) {
</div> </div>
{milestonesDisplay()} {milestonesDisplay()}
</> </>
)) )),
mastery
}; };
}); });

View file

@ -24,9 +24,12 @@ import { createBuyable, GenericBuyable } from "features/buyable";
import metal from "./metal"; import metal from "./metal";
import plastic from "./plastic"; import plastic from "./plastic";
import paper from "./paper"; import paper from "./paper";
import dyes from "./dyes";
import SqrtVue from "components/math/Sqrt.vue"; import SqrtVue from "components/math/Sqrt.vue";
import { globalBus } from "game/events"; import { globalBus } from "game/events";
import { main } from "data/projEntry"; import { main } from "data/projEntry";
import { createHotkey } from "features/hotkey";
import HotkeyVue from "components/Hotkey.vue";
const id = "letters"; const id = "letters";
const day = 14; const day = 14;
@ -43,13 +46,17 @@ const layer = createLayer(id, function (this: BaseLayer) {
height: 10, height: 10,
style: "margin-top: 8px", style: "margin-top: 8px",
borderStyle: "border-color: black", borderStyle: "border-color: black",
baseStyle: "margin-top: 0", baseStyle: "margin-top: -1px",
fillStyle: "margin-top: 0; transition-duration: 0s; background: black", fillStyle: "margin-top: -1px; transition-duration: 0s; background: black",
progress: () => Decimal.div(processingProgress.value, computedProcessingCooldown.value) progress: () => Decimal.div(processingProgress.value, computedProcessingCooldown.value)
})); }));
const process = createClickable(() => ({ const process = createClickable(() => ({
display: { display: {
title: "Process Letters", title: jsx(() => (
<h3>
Process letters <HotkeyVue hotkey={processHK} />
</h3>
)),
description: jsx(() => ( description: jsx(() => (
<> <>
Process {format(computedLettersGain.value, 1)} letters Process {format(computedLettersGain.value, 1)} letters
@ -61,7 +68,9 @@ const layer = createLayer(id, function (this: BaseLayer) {
style: { style: {
minHeight: "80px" minHeight: "80px"
}, },
canClick: () => Decimal.gte(processingProgress.value, computedProcessingCooldown.value), canClick: () =>
Decimal.gte(processingProgress.value, computedProcessingCooldown.value) &&
(!main.isMastery.value || masteryEffectActive.value),
onClick() { onClick() {
if (Decimal.lt(processingProgress.value, computedProcessingCooldown.value)) { if (Decimal.lt(processingProgress.value, computedProcessingCooldown.value)) {
return; return;
@ -75,6 +84,15 @@ const layer = createLayer(id, function (this: BaseLayer) {
} }
})); }));
const processHK = createHotkey(() => ({
key: "l",
description: "Process letters",
onPress: () => {
if (process.canClick.value) process.onClick();
},
enabled: main.days[day - 1].opened
}));
const metalBuyable = createBuyable(() => ({ const metalBuyable = createBuyable(() => ({
display: { display: {
title: "Sorting Machine", title: "Sorting Machine",
@ -87,7 +105,8 @@ const layer = createLayer(id, function (this: BaseLayer) {
resource: metal.metal, resource: metal.metal,
cost() { cost() {
return Decimal.pow(10, metalBuyable.amount.value).times(1e21); return Decimal.pow(10, metalBuyable.amount.value).times(1e21);
} },
visibility: () => showIf(!main.isMastery.value || masteryEffectActive.value)
})) as GenericBuyable; })) as GenericBuyable;
const plasticBuyable = createBuyable(() => ({ const plasticBuyable = createBuyable(() => ({
display: { display: {
@ -101,7 +120,8 @@ const layer = createLayer(id, function (this: BaseLayer) {
resource: plastic.plastic, resource: plastic.plastic,
cost() { cost() {
return Decimal.pow(1.5, plasticBuyable.amount.value).times(1e9); return Decimal.pow(1.5, plasticBuyable.amount.value).times(1e9);
} },
visibility: () => showIf(!main.isMastery.value || masteryEffectActive.value)
})) as GenericBuyable; })) as GenericBuyable;
const paperBuyable = createBuyable(() => ({ const paperBuyable = createBuyable(() => ({
display: { display: {
@ -114,7 +134,8 @@ const layer = createLayer(id, function (this: BaseLayer) {
resource: paper.paper, resource: paper.paper,
cost() { cost() {
return Decimal.pow(3, paperBuyable.amount.value).times(1e38); return Decimal.pow(3, paperBuyable.amount.value).times(1e38);
} },
visibility: () => showIf(!main.isMastery.value || masteryEffectActive.value)
})) as GenericBuyable; })) as GenericBuyable;
const buyables = { metalBuyable, plasticBuyable, paperBuyable }; const buyables = { metalBuyable, plasticBuyable, paperBuyable };
@ -169,13 +190,17 @@ const layer = createLayer(id, function (this: BaseLayer) {
createCollapsibleMilestones(milestones); createCollapsibleMilestones(milestones);
const synergy = computed(() => { const synergy = computed(() => {
const amount = Decimal.add(totalLetters.value, 1); let amount = Decimal.add(totalLetters.value, 1);
if (synergyMilestone.earned.value) { if (synergyMilestone.earned.value) {
const preSoftcap = Decimal.log2(10001).add(1); const preSoftcap = Decimal.log2(10001).add(1);
return preSoftcap.add(amount.sub(9999).sqrt()); amount = preSoftcap.add(amount.sub(9999).sqrt());
} else { } else {
return Decimal.log2(amount).add(1); amount = Decimal.log2(amount).add(1);
} }
if (masteryEffectActive.value) {
amount = Decimal.pow(amount, 2);
}
return amount;
}); });
const lettersGain = createSequentialModifier(() => [ const lettersGain = createSequentialModifier(() => [
@ -190,13 +215,23 @@ const layer = createLayer(id, function (this: BaseLayer) {
createMultiplicativeModifier(() => ({ createMultiplicativeModifier(() => ({
multiplier: () => Decimal.div(paperBuyable.amount.value, 2).add(1), multiplier: () => Decimal.div(paperBuyable.amount.value, 2).add(1),
description: "Printed Labels" description: "Printed Labels"
})),
createMultiplicativeModifier(() => ({
multiplier: () => dyes.boosts.black1.value,
description: "Black Dye Boost"
})) }))
]); ]);
const computedLettersGain = computed(() => lettersGain.apply(1)); const computedLettersGain = computed(() => lettersGain.apply(1));
const processingCooldown = createSequentialModifier(() => [ const processingCooldown = createSequentialModifier(() => [
createMultiplicativeModifier(() => ({ createMultiplicativeModifier(() => ({
multiplier: () => Decimal.div(metalBuyable.amount.value, 2).add(1).recip(), multiplier: () => Decimal.div(metalBuyable.amount.value, 2).add(1).recip(),
description: "Sorting Machine" description: "Sorting Machine"
})),
createMultiplicativeModifier(() => ({
multiplier: () => Decimal.sqrt(synergy.value).recip(),
description: "Letters Decoration",
enabled: masteryEffectActive
})) }))
]); ]);
const computedProcessingCooldown = computed(() => processingCooldown.apply(5)); const computedProcessingCooldown = computed(() => processingCooldown.apply(5));
@ -245,7 +280,10 @@ const layer = createLayer(id, function (this: BaseLayer) {
goal: 1e6, goal: 1e6,
name, name,
day, day,
color, background: {
gradient: "letters-bar",
duration: "15s"
},
textColor: "var(--feature-foreground)", textColor: "var(--feature-foreground)",
modal: { modal: {
show: showModifiersModal, show: showModifiersModal,
@ -253,6 +291,26 @@ const layer = createLayer(id, function (this: BaseLayer) {
} }
}); });
const mastery = {
letters: persistent<DecimalSource>(0),
totalLetters: persistent<DecimalSource>(0),
buyables: {
metalBuyable: { amount: persistent<DecimalSource>(0) },
plasticBuyable: { amount: persistent<DecimalSource>(0) },
paperBuyable: { amount: persistent<DecimalSource>(0) }
},
milestones: {
autoSmeltingMilestone: { earned: persistent<boolean>(false) },
miningMilestone: { earned: persistent<boolean>(false) },
synergyMilestone: { earned: persistent<boolean>(false) },
industrialCrucibleMilestone: { earned: persistent<boolean>(false) }
}
};
const mastered = persistent<boolean>(false);
const masteryEffectActive = computed(
() => mastered.value || main.currentlyMastering.value?.name === name
);
return { return {
name, name,
day, day,
@ -260,6 +318,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
letters, letters,
totalLetters, totalLetters,
processingProgress, processingProgress,
processHK,
buyables, buyables,
milestones, milestones,
minWidth: 700, minWidth: 700,
@ -269,6 +328,17 @@ const layer = createLayer(id, function (this: BaseLayer) {
<> <>
{render(trackerDisplay)} {render(trackerDisplay)}
<Spacer /> <Spacer />
{masteryEffectActive.value ? (
<>
<div class="decoration-effect ribbon">
Decoration effect:
<br />
Letter processing experience is stronger and affects processing cooldown
at reduced rate
</div>
<Spacer />
</>
) : null}
<MainDisplay resource={letters} color={color} /> <MainDisplay resource={letters} color={color} />
{render(process)} {render(process)}
<div> <div>
@ -284,9 +354,14 @@ const layer = createLayer(id, function (this: BaseLayer) {
minimizedDisplay: jsx(() => ( minimizedDisplay: jsx(() => (
<div> <div>
{name}{" "} {name}{" "}
<span class="desc">{format(letters.value)} {letters.displayName}</span> <span class="desc">
</div> {format(letters.value)} {letters.displayName}
</span>
</div>
)), )),
mastery,
mastered,
masteryEffectActive
}; };
}); });

View file

@ -34,6 +34,8 @@ import plastic from "./plastic";
import trees from "./trees"; import trees from "./trees";
import "./styles/management.css"; import "./styles/management.css";
import { Resource } from "features/resources/resource";
import { isArray } from "@vue/shared";
const id = "management"; const id = "management";
const day = 12; const day = 12;
@ -99,7 +101,8 @@ const layer = createLayer(id, () => {
"The Elves probably need to be taught if they're to do better. Maybe you'll build a school so you can teach them?" "The Elves probably need to be taught if they're to do better. Maybe you'll build a school so you can teach them?"
}, },
resource: trees.logs, resource: trees.logs,
cost: 1e21 cost: 1e21,
visibility: () => showIf(!main.isMastery.value)
})); }));
const classroomUpgrade = createUpgrade(() => ({ const classroomUpgrade = createUpgrade(() => ({
@ -120,7 +123,11 @@ const layer = createLayer(id, () => {
"Time for some advanced training! Now that all the elves know the basics, you have a foundation you can truly build off of. Everyone seems to be learning twice as quickly!" "Time for some advanced training! Now that all the elves know the basics, you have a foundation you can truly build off of. Everyone seems to be learning twice as quickly!"
}, },
visibility: () => visibility: () =>
showIf(main.day.value >= advancedDay && main.days[advancedDay - 1].opened.value), showIf(
!main.isMastery.value &&
main.day.value >= advancedDay &&
main.days[advancedDay - 1].opened.value
),
resource: boxes.boxes, resource: boxes.boxes,
style: "width: 150px", style: "width: 150px",
cost: 1e25 cost: 1e25
@ -180,7 +187,8 @@ const layer = createLayer(id, () => {
"Frosty", "Frosty",
"Cocoa", "Cocoa",
"Twinkle", "Twinkle",
"Carol" "Carol",
"Tinsel"
].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;
@ -344,7 +352,10 @@ const layer = createLayer(id, () => {
effectDisplay: "Unlock an elf that autobuys oil drills and extractors." effectDisplay: "Unlock an elf that autobuys oil drills and extractors."
}, },
visibility: () => showIf(cutterElfMilestones[3].earned.value && main.day.value >= 13), visibility: () => showIf(cutterElfMilestones[3].earned.value && main.day.value >= 13),
shouldEarn: () => cutterElfTraining.level.value >= 5 shouldEarn: () => cutterElfTraining.level.value >= 5,
onComplete() {
main.days[3].recentlyUpdated.value = true;
}
})) }))
] as Array<GenericMilestone>; ] as Array<GenericMilestone>;
const planterElfMilestones = [ const planterElfMilestones = [
@ -509,7 +520,7 @@ const layer = createLayer(id, () => {
createMilestone(() => ({ createMilestone(() => ({
display: { display: {
requirement: "Mary Level 3", requirement: "Mary Level 3",
effectDisplay: "Mary and Faith now buy max." effectDisplay: "Mary, Noel, and Faith now buy max."
}, },
visibility: () => showIf(heatedPlanterElfMilestones[1].earned.value), visibility: () => showIf(heatedPlanterElfMilestones[1].earned.value),
shouldEarn: () => heatedPlanterElfTraining.level.value >= 3 shouldEarn: () => heatedPlanterElfTraining.level.value >= 3
@ -831,33 +842,18 @@ const layer = createLayer(id, () => {
createMilestone(() => ({ createMilestone(() => ({
display: { display: {
requirement: "Gingersnap Level 3", requirement: "Gingersnap Level 3",
effectDisplay: "Double all dye colors and cloth actions, but reset all dyes." effectDisplay: "Double all dye colors and cloth actions"
}, },
visibility: () => showIf(clothElfMilestones[1].earned.value), visibility: () => showIf(clothElfMilestones[1].earned.value),
shouldEarn: () => clothElfTraining.level.value >= 3, shouldEarn: () => clothElfTraining.level.value >= 3
onComplete() {
(["red", "yellow", "blue", "orange", "green", "purple"] as const).forEach(
dyeColor => {
dyes.dyes[dyeColor].amount.value = 0;
dyes.dyes[dyeColor].buyable.amount.value = 0;
}
);
}
})), })),
createMilestone(() => ({ createMilestone(() => ({
display: { display: {
requirement: "Gingersnap Level 4", requirement: "Gingersnap Level 4",
effectDisplay: effectDisplay: "Raise secondary dyes' first effects to the 1.1"
"Raise secondary dyes' first effects to the 1.1 but reset primary dyes"
}, },
visibility: () => showIf(clothElfMilestones[2].earned.value && main.day.value >= 13), visibility: () => showIf(clothElfMilestones[2].earned.value && main.day.value >= 13),
shouldEarn: () => clothElfTraining.level.value >= 4, shouldEarn: () => clothElfTraining.level.value >= 4
onComplete() {
(["red", "yellow", "blue"] as const).forEach(dyeColor => {
dyes.dyes[dyeColor].amount.value = 0;
dyes.dyes[dyeColor].buyable.amount.value = 0;
});
}
})), })),
createMilestone(() => ({ createMilestone(() => ({
display: { display: {
@ -1056,6 +1052,93 @@ const layer = createLayer(id, () => {
} }
})) }))
] as Array<GenericMilestone>; ] as Array<GenericMilestone>;
const dyeElfMilestones = [
createMilestone(() => ({
display: {
requirement: "Carol Level 1",
effectDisplay: "Double primary dye gain"
},
shouldEarn: () => dyeElfTraining.level.value >= 1
})),
createMilestone(() => ({
display: {
requirement: "Carol Level 2",
effectDisplay: "Double secondary dye gain"
},
shouldEarn: () => dyeElfTraining.level.value >= 2,
visibility: () => showIf(dyeElfMilestones[0].earned.value)
})),
createMilestone(() => ({
display: {
requirement: "Carol Level 3",
effectDisplay: "Buy maximum primary dyes"
},
shouldEarn: () => dyeElfTraining.level.value >= 3,
visibility: () => showIf(dyeElfMilestones[1].earned.value)
})),
createMilestone(() => ({
display: {
requirement: "Carol Level 4",
effectDisplay: "Secondary dyes don't spend primary dyes"
},
shouldEarn: () => dyeElfTraining.level.value >= 4,
visibility: () => showIf(dyeElfMilestones[2].earned.value && main.day.value >= 16)
})),
createMilestone(() => ({
display: {
requirement: "Carol Level 5",
effectDisplay: "Buy maximum secondary dyes"
},
shouldEarn: () => dyeElfTraining.level.value >= 5,
visibility: () => showIf(dyeElfMilestones[3].earned.value && main.day.value >= 16)
}))
] as Array<GenericMilestone>;
const plasticElfMilestones = [
createMilestone(() => ({
display: {
requirement: "Tinsel Level 1",
effectDisplay: "Double plastic gain"
},
shouldEarn: () => plasticElfTraining.level.value >= 1
})),
createMilestone(() => ({
display: {
requirement: "Tinsel Level 2",
effectDisplay: jsx(() => (
<>
Every plastic buyable adds <Sqrt>level</Sqrt> levels to the other plastic
buyables.
</>
))
},
shouldEarn: () => plasticElfTraining.level.value >= 2,
visibility: () => showIf(plasticElfMilestones[0].earned.value)
})),
createMilestone(() => ({
display: {
requirement: "Tinsel Level 3",
effectDisplay: "Refineries don't spend oil"
},
shouldEarn: () => plasticElfTraining.level.value >= 3,
visibility: () => showIf(plasticElfMilestones[1].earned.value)
})),
createMilestone(() => ({
display: {
requirement: "Tinsel Level 4",
effectDisplay: "Increase plastic gain by +1% for each refinery"
},
shouldEarn: () => plasticElfTraining.level.value >= 4,
visibility: () => showIf(plasticElfMilestones[2].earned.value && main.day.value >= 16)
})),
createMilestone(() => ({
display: {
requirement: "Tinsel Level 5",
effectDisplay: "Buy maximum plastic buyables"
},
shouldEarn: () => plasticElfTraining.level.value >= 5,
visibility: () => showIf(plasticElfMilestones[3].earned.value && main.day.value >= 16)
}))
] as Array<GenericMilestone>;
// ------------------------------------------------------------------------------- Milestone display // ------------------------------------------------------------------------------- Milestone display
const currentShown = persistent<string>("Holly"); const currentShown = persistent<string>("Holly");
@ -1123,7 +1206,7 @@ const layer = createLayer(id, () => {
})) }))
); );
const clothElfTraining = createElfTraining(elves.elves.clothElf, clothElfMilestones); const clothElfTraining = createElfTraining(elves.elves.clothElf, clothElfMilestones);
const plasticElfTraining = [paperElfTraining, boxElfTraining, clothElfTraining]; const plasticElvesTraining = [paperElfTraining, boxElfTraining, clothElfTraining];
const coalDrillElfTraining = createElfTraining( const coalDrillElfTraining = createElfTraining(
elves.elves.coalDrillElf, elves.elves.coalDrillElf,
coalDrillElfMilestones coalDrillElfMilestones
@ -1134,8 +1217,10 @@ const layer = createLayer(id, () => {
elves.elves.heavyDrillElf, elves.elves.heavyDrillElf,
heavyDrillElfMilestones heavyDrillElfMilestones
); );
const dyeElfTraining = createElfTraining(elves.elves.dyeElf, dyeElfMilestones);
const plasticElfTraining = createElfTraining(elves.elves.plasticElf, plasticElfMilestones);
const row5Elves = [coalDrillElfTraining, heavyDrillElfTraining, oilElfTraining]; const row5Elves = [coalDrillElfTraining, heavyDrillElfTraining, oilElfTraining];
const row6Elves = [metalElfTraining]; const row6Elves = [metalElfTraining, dyeElfTraining, plasticElfTraining];
const elfTraining = { const elfTraining = {
cutterElfTraining, cutterElfTraining,
planterElfTraining, planterElfTraining,
@ -1152,7 +1237,9 @@ const layer = createLayer(id, () => {
coalDrillElfTraining, coalDrillElfTraining,
metalElfTraining, metalElfTraining,
oilElfTraining, oilElfTraining,
heavyDrillElfTraining heavyDrillElfTraining,
dyeElfTraining,
plasticElfTraining
}; };
const day12Elves = [ const day12Elves = [
cutterElfTraining, cutterElfTraining,
@ -1409,6 +1496,19 @@ const layer = createLayer(id, () => {
}; };
}); });
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>
);
}
const schools = createBuyable(() => ({ const schools = createBuyable(() => ({
display: jsx(() => ( display: jsx(() => (
<> <>
@ -1424,13 +1524,19 @@ const layer = createLayer(id, () => {
</div> </div>
{Decimal.lt(schools.amount.value, unref(schools.purchaseLimit)) ? ( {Decimal.lt(schools.amount.value, unref(schools.purchaseLimit)) ? (
<div> <div>
Costs {format(schoolCost.value.wood)} logs, {format(schoolCost.value.coal)}{" "} Costs {displayCost(trees.logs, schoolCost.value.wood, "logs")},{" "}
coal, {format(schoolCost.value.paper)} paper,{" "} {displayCost(coal.coal, schoolCost.value.coal, "coal")},{" "}
{format(schoolCost.value.boxes)} boxes,{" "} {displayCost(paper.paper, schoolCost.value.paper, "paper")},{" "}
{format(schoolCost.value.metalIngots)} metal ingots,{" "} {displayCost(boxes.boxes, schoolCost.value.boxes, "boxes")},{" "}
{format(schoolCost.value.cloth)} cloth, {format(schoolCost.value.plastic)}{" "} {displayCost(metal.metal, schoolCost.value.metalIngots, "metal ingots")},{" "}
plastic, and requires {format(schoolCost.value.dye)} of red, yellow, and {displayCost(cloth.cloth, schoolCost.value.cloth, "cloth")},{" "}
blue dye {displayCost(plastic.plastic, schoolCost.value.plastic, "plastic")}, and
requires{" "}
{displayCost(
[dyes.dyes.red.amount, dyes.dyes.yellow.amount, dyes.dyes.blue.amount],
schoolCost.value.dye,
"red, yellow, and blue dye"
)}
</div> </div>
) : null} ) : null}
</> </>
@ -1498,9 +1604,10 @@ const layer = createLayer(id, () => {
multiplying elves' XP gain by {format(classroomEffect.value)} multiplying elves' XP gain by {format(classroomEffect.value)}
</div> </div>
<div> <div>
Costs {format(classroomCost.value.wood)} logs, Costs {displayCost(trees.logs, classroomCost.value.wood, "logs")},
{format(classroomCost.value.paper)} paper, {format(classroomCost.value.boxes)}{" "} {displayCost(paper.paper, classroomCost.value.paper, "paper")},{" "}
boxes, {format(classroomCost.value.metalIngots)} metal ingots {displayCost(boxes.boxes, classroomCost.value.boxes, "boxes")},{" "}
{displayCost(metal.metal, classroomCost.value.metalIngots, "metal ingots")}
</div> </div>
</> </>
)), )),
@ -1618,6 +1725,42 @@ const layer = createLayer(id, () => {
modifier: clothElfTraining.elfXPGain, modifier: clothElfTraining.elfXPGain,
base: 0.1, base: 0.1,
unit: " XP" unit: " XP"
},
{
title: "Peppermint XP Gain per Action",
modifier: coalDrillElfTraining.elfXPGain,
base: 0.1,
unit: " XP"
},
{
title: "Frosty XP Gain per Action",
modifier: heavyDrillElfTraining.elfXPGain,
base: 0.1,
unit: " XP"
},
{
title: "Cocoa XP Gain per Action",
modifier: oilElfTraining.elfXPGain,
base: 0.1,
unit: " XP"
},
{
title: "Twinkle XP Gain per Action",
modifier: metalElfTraining.elfXPGain,
base: 0.1,
unit: " XP"
},
{
title: "Carol XP Gain per Action",
modifier: dyeElfTraining.elfXPGain,
base: 0.1,
unit: " XP"
},
{
title: "Tinsel XP Gain per Action",
modifier: plasticElfTraining.elfXPGain,
base: 0.1,
unit: " XP"
} }
]); ]);
const showModifiersModal = ref(false); const showModifiersModal = ref(false);
@ -1637,12 +1780,217 @@ const layer = createLayer(id, () => {
main.completeDay(); main.completeDay();
} else if ( } else if (
main.day.value === advancedDay && main.day.value === advancedDay &&
day12Elves.every(elf => elf.level.value >= 5) &&
day13Elves.every(elf => elf.level.value >= 5) day13Elves.every(elf => elf.level.value >= 5)
) { ) {
main.completeDay(); main.completeDay();
} }
}); });
const mastery = {
elfTraining: {
bonfireElfTraining: {
exp: persistent<DecimalSource>(0),
milestones: [
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) }
]
},
boxElfTraining: {
exp: persistent<DecimalSource>(0),
milestones: [
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) }
]
},
clothElfTraining: {
exp: persistent<DecimalSource>(0),
milestones: [
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) }
]
},
coalDrillElfTraining: {
exp: persistent<DecimalSource>(0),
milestones: [
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) }
]
},
cutterElfTraining: {
exp: persistent<DecimalSource>(0),
milestones: [
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) }
]
},
expandersElfTraining: {
exp: persistent<DecimalSource>(0),
milestones: [
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) }
]
},
fertilizerElfTraining: {
exp: persistent<DecimalSource>(0),
milestones: [
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) }
]
},
heatedCutterElfTraining: {
exp: persistent<DecimalSource>(0),
milestones: [
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) }
]
},
heatedPlanterElfTraining: {
exp: persistent<DecimalSource>(0),
milestones: [
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) }
]
},
heavyDrillElfTraining: {
exp: persistent<DecimalSource>(0),
milestones: [
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) }
]
},
kilnElfTraining: {
exp: persistent<DecimalSource>(0),
milestones: [
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) }
]
},
metalElfTraining: {
exp: persistent<DecimalSource>(0),
milestones: [
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) }
]
},
oilElfTraining: {
exp: persistent<DecimalSource>(0),
milestones: [
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) }
]
},
paperElfTraining: {
exp: persistent<DecimalSource>(0),
milestones: [
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) }
]
},
planterElfTraining: {
exp: persistent<DecimalSource>(0),
milestones: [
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) }
]
},
smallfireElfTraining: {
exp: persistent<DecimalSource>(0),
milestones: [
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) }
]
},
dyeElfTraining: {
exp: persistent<DecimalSource>(0),
milestones: [
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) }
]
},
plasticElfTraining: {
exp: persistent<DecimalSource>(0),
milestones: [
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) }
]
}
},
teaching: { bought: persistent<boolean>(false) },
schools: { amount: persistent<DecimalSource>(0) },
classrooms: { amount: persistent<DecimalSource>(0) },
classroomUpgrade: { bought: persistent<boolean>(false) },
advancedUpgrade: { bought: persistent<boolean>(false) },
upgrades: [
{ bought: persistent<boolean>(false) },
{ bought: persistent<boolean>(false) },
{ bought: persistent<boolean>(false) }
],
upgrades2: [
{ bought: persistent<boolean>(false) },
{ bought: persistent<boolean>(false) },
{ bought: persistent<boolean>(false) }
],
focusMulti: persistent<DecimalSource>(1),
focusTargets: persistent<Record<string, boolean>>({}),
focusCooldown: persistent<number>(0),
focusTime: persistent<number>(0)
};
// ------------------------------------------------------------------------------- Return // ------------------------------------------------------------------------------- Return
return { return {
@ -1664,7 +2012,7 @@ const layer = createLayer(id, () => {
classroomUpgrade, classroomUpgrade,
advancedUpgrade, advancedUpgrade,
focusMultiplier: focusMulti, focusMulti,
upgrades, upgrades,
upgrades2, upgrades2,
focusTargets, focusTargets,
@ -1694,19 +2042,19 @@ const layer = createLayer(id, () => {
<Spacer /> <Spacer />
{Decimal.gt(schools.amount.value, 0) ? ( {Decimal.gt(schools.amount.value, 0) ? (
<> <>
<br /> <Spacer />
Click on an elf to see their milestones. Click on an elf to see their milestones.
<br /> <Spacer />
<br /> <Spacer />
{render(focusButton)} {render(focusButton)}
{renderGrid(upgrades, upgrades2)} {renderGrid(upgrades, upgrades2)}
<br /> <Spacer />
{renderGrid( {renderGrid(
[focusMeter], [focusMeter],
treeElfTraining, treeElfTraining,
coalElfTraining, coalElfTraining,
fireElfTraining, fireElfTraining,
plasticElfTraining, plasticElvesTraining,
row5Elves, row5Elves,
row6Elves row6Elves
)} )}
@ -1717,7 +2065,9 @@ const layer = createLayer(id, () => {
"" ""
)} )}
</> </>
)) )),
mastery
}; };
}); });

View file

@ -35,6 +35,8 @@ import oil from "./oil";
import paper from "./paper"; import paper from "./paper";
import plastic from "./plastic"; import plastic from "./plastic";
import workshop from "./workshop"; import workshop from "./workshop";
import wrappingPaper from "./wrapping-paper";
import toys from "./toys";
const id = "metal"; const id = "metal";
const day = 7; const day = 7;
@ -48,6 +50,9 @@ const layer = createLayer(id, function (this: BaseLayer) {
const ore = createResource<DecimalSource>(0, "ore"); const ore = createResource<DecimalSource>(0, "ore");
const bestOre = trackBest(ore); const bestOre = trackBest(ore);
const lastOreGained = ref<DecimalSource>(0);
const lastOreSmelted = ref<DecimalSource>(0);
const orePurity = createSequentialModifier(() => [ const orePurity = createSequentialModifier(() => [
createMultiplicativeModifier(() => ({ createMultiplicativeModifier(() => ({
multiplier: 5, multiplier: 5,
@ -169,6 +174,16 @@ const layer = createLayer(id, function (this: BaseLayer) {
.log10(), .log10(),
description: "The Ultimate Metal Dye", description: "The Ultimate Metal Dye",
enabled: oil.row3Upgrades[4].bought enabled: oil.row3Upgrades[4].bought
})),
createMultiplicativeModifier(() => ({
multiplier: wrappingPaper.boosts.jazzy1,
description: "Jazzy Wrapping Paper",
enabled: computed(() => Decimal.gt(wrappingPaper.boosts.jazzy1.value, 1))
})),
createAdditiveModifier(() => ({
addend: () => Decimal.sub(lastOreGained.value, lastOreSmelted.value).max(0),
description: "Metal Decoration",
enabled: masteryEffectActive
})) }))
]); ]);
const computedAutoSmeltSpeed = computed(() => autoSmeltSpeed.apply(0)); const computedAutoSmeltSpeed = computed(() => autoSmeltSpeed.apply(0));
@ -193,6 +208,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
multiplier: () => Decimal.add(industrialCrucible.amount.value, 1).sqrt(), multiplier: () => Decimal.add(industrialCrucible.amount.value, 1).sqrt(),
description: "100,000 Letters Processed", description: "100,000 Letters Processed",
enabled: letters.milestones.industrialCrucibleMilestone.earned enabled: letters.milestones.industrialCrucibleMilestone.earned
})),
createMultiplicativeModifier(() => ({
multiplier: () => Decimal.add(toys.clothes.value, 1),
description: "Give elves clothes to wear",
enabled: toys.row1Upgrades[1].bought
})) }))
]); ]);
const computedAutoSmeltMulti = computed(() => autoSmeltMulti.apply(1)); const computedAutoSmeltMulti = computed(() => autoSmeltMulti.apply(1));
@ -268,6 +288,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
multiplier: () => Decimal.add(dyes.dyes.blue.amount.value, 1).sqrt(), multiplier: () => Decimal.add(dyes.dyes.blue.amount.value, 1).sqrt(),
description: "1000 Letters Processed", description: "1000 Letters Processed",
enabled: letters.milestones.miningMilestone.earned enabled: letters.milestones.miningMilestone.earned
})),
createMultiplicativeModifier(() => ({
multiplier: () => Decimal.add(toys.clothes.value, 1),
description: "Give elves clothes to wear",
enabled: toys.row1Upgrades[1].bought
})) }))
]); ]);
const computedOreAmount = computed(() => oreAmount.apply(1)); const computedOreAmount = computed(() => oreAmount.apply(1));
@ -305,6 +330,14 @@ const layer = createLayer(id, function (this: BaseLayer) {
), ),
description: "100 Letters Processed", description: "100 Letters Processed",
enabled: letters.milestones.autoSmeltingMilestone.earned enabled: letters.milestones.autoSmeltingMilestone.earned
})),
createAdditiveModifier(() => ({
addend: () =>
Decimal.sub(lastOreSmelted.value, lastOreGained.value)
.max(0)
.div(computedOreAmount.value),
description: "Metal Decoration",
enabled: masteryEffectActive
})) }))
]); ]);
const computedOreSpeed = computed(() => oreSpeed.apply(Decimal.recip(maxOreProgress))); const computedOreSpeed = computed(() => oreSpeed.apply(Decimal.recip(maxOreProgress)));
@ -314,7 +347,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
width: 400, width: 400,
height: 25, height: 25,
direction: Direction.Right, direction: Direction.Right,
fillStyle: { backgroundColor: color }, fillStyle: { backgroundColor: color, transitionDuration: "0s" },
progress: () => oreProgress.value progress: () => oreProgress.value
})); }));
@ -552,6 +585,26 @@ const layer = createLayer(id, function (this: BaseLayer) {
const hotterForgeEffect = computed(() => Decimal.times(hotterForge.amount.value, 0.25)); const hotterForgeEffect = computed(() => Decimal.times(hotterForge.amount.value, 0.25));
globalBus.on("update", diff => { globalBus.on("update", diff => {
if (
Decimal.lt(main.day.value, day) ||
(main.isMastery.value &&
!mastered.value &&
main.currentlyMastering.value?.name !== name)
) {
return;
}
const oreGained = Decimal.sub(
Decimal.times(computedOreSpeed.value, computedOreAmount.value),
Decimal.sub(lastOreSmelted.value, lastOreGained.value).max(0)
);
const oreSmelted = Decimal.sub(
computedAutoSmeltSpeed.value,
Decimal.sub(lastOreGained.value, lastOreSmelted.value).max(0)
);
lastOreGained.value = Decimal.isNaN(oreGained) ? 0 : oreGained;
lastOreSmelted.value = Decimal.isNaN(oreSmelted) ? 0 : oreSmelted;
oreProgress.value = Decimal.times(diff, computedOreSpeed.value).plus(oreProgress.value); oreProgress.value = Decimal.times(diff, computedOreSpeed.value).plus(oreProgress.value);
const oreGain = oreProgress.value.trunc(); const oreGain = oreProgress.value.trunc();
oreProgress.value = oreProgress.value.minus(oreGain); oreProgress.value = oreProgress.value.minus(oreGain);
@ -572,7 +625,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
base: 0, base: 0,
unit: "/s", unit: "/s",
visible() { visible() {
return Decimal.gt(industrialCrucible.amount.value, 0); return Decimal.gt(industrialCrucible.amount.value, 0) || masteryEffectActive.value;
} }
}, },
{ {
@ -617,13 +670,35 @@ const layer = createLayer(id, function (this: BaseLayer) {
goal: 25000, goal: 25000,
name, name,
day, day,
color, background: color,
modal: { modal: {
show: showModifiersModal, show: showModifiersModal,
display: modifiersModal display: modifiersModal
} }
}); });
const mastery = {
ore: persistent<DecimalSource>(0),
bestOre: persistent<DecimalSource>(0),
oreProgress: persistent<DecimalSource>(0),
metal: persistent<DecimalSource>(0),
bestMetal: persistent<DecimalSource>(0),
totalMetal: persistent<DecimalSource>(0),
simplePickaxe: { bought: persistent<boolean>(false) },
doublePickaxe: { bought: persistent<boolean>(false) },
crucible: { bought: persistent<boolean>(false) },
coalDrill: { bought: persistent<boolean>(false) },
industrialFurnace: { bought: persistent<boolean>(false) },
efficientDrill: { bought: persistent<boolean>(false) },
oreDrill: { amount: persistent<DecimalSource>(0) },
industrialCrucible: { amount: persistent<DecimalSource>(0) },
hotterForge: { amount: persistent<DecimalSource>(0) }
};
const mastered = persistent<boolean>(false);
const masteryEffectActive = computed(
() => mastered.value || main.currentlyMastering.value?.name === name
);
return { return {
name, name,
day, day,
@ -650,6 +725,17 @@ const layer = createLayer(id, function (this: BaseLayer) {
<> <>
{render(trackerDisplay)} {render(trackerDisplay)}
<Spacer /> <Spacer />
{masteryEffectActive.value ? (
<>
<div class="decoration-effect">
Decoration effect:
<br />
The lesser of ore mining amount x speed and auto smelting speed is
increased to match the greater
</div>
<Spacer />
</>
) : null}
<MainDisplay <MainDisplay
resource={metal} resource={metal}
color={color} color={color}
@ -658,7 +744,8 @@ const layer = createLayer(id, function (this: BaseLayer) {
productionDisplay={jsx(() => ( productionDisplay={jsx(() => (
<> <>
{autoSmeltEnabled.value && {autoSmeltEnabled.value &&
Decimal.gte(industrialCrucible.amount.value, 1) (Decimal.gte(industrialCrucible.amount.value, 1) ||
masteryEffectActive.value)
? `+${formatLimit( ? `+${formatLimit(
[ [
[computedAutoSmeltSpeed.value, "smelting speed"], [computedAutoSmeltSpeed.value, "smelting speed"],
@ -680,7 +767,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
/> />
<Spacer /> <Spacer />
{render(smeltOreButton)} {render(smeltOreButton)}
{Decimal.gte(industrialCrucible.amount.value, 1) ? ( {Decimal.gte(industrialCrucible.amount.value, 1) || masteryEffectActive.value ? (
<div style={{ width: "150px" }}> <div style={{ width: "150px" }}>
<Toggle <Toggle
title="Auto Smelt" title="Auto Smelt"
@ -722,9 +809,13 @@ const layer = createLayer(id, function (this: BaseLayer) {
minimizedDisplay: jsx(() => ( minimizedDisplay: jsx(() => (
<div> <div>
{name}{" "} {name}{" "}
<span class="desc">{format(metal.value)} {metal.displayName}</span> <span class="desc">
</div> {format(metal.value)} {metal.displayName}
</span>
</div>
)), )),
mastery,
mastered
}; };
}); });

View file

@ -87,12 +87,16 @@ const layer = createLayer(id, function (this: BaseLayer) {
const activeHeavy = persistent<DecimalSource>(0); const activeHeavy = persistent<DecimalSource>(0);
const heavyCoal = computed(() => const heavyCoal = computed(() =>
Decimal.times( masteryEffectActive.value
Decimal.pow(activeHeavy.value, heavy2Power.value).pow( ? 0
management.elfTraining.coalDrillElfTraining.milestones[0].earned.value ? 2.5 : 2 : Decimal.times(
), Decimal.pow(activeHeavy.value, heavy2Power.value).pow(
1e14 management.elfTraining.coalDrillElfTraining.milestones[0].earned.value
) ? 2.5
: 2
),
1e14
)
); );
const heavyPower = computed(() => const heavyPower = computed(() =>
Decimal.times(Decimal.pow(activeHeavy.value, heavy2Power.value), 1) Decimal.times(Decimal.pow(activeHeavy.value, heavy2Power.value), 1)
@ -141,7 +145,8 @@ const layer = createLayer(id, function (this: BaseLayer) {
color: colorText, color: colorText,
width: "160px", width: "160px",
flexGrow: 1 flexGrow: 1
} },
visibility: () => showIf(!main.isMastery.value || masteryEffectActive.value)
})) as ElfBuyable & { resource: Resource }; })) as ElfBuyable & { resource: Resource };
const { const {
min: minHeavy, min: minHeavy,
@ -222,7 +227,9 @@ const layer = createLayer(id, function (this: BaseLayer) {
}); });
const activeExtractor = persistent<DecimalSource>(0); const activeExtractor = persistent<DecimalSource>(0);
const extractorPower = computed(() => Decimal.pow(1 / 3, activeExtractor.value)); const extractorPower = computed(() =>
masteryEffectActive.value ? 1 : Decimal.pow(1 / 3, activeExtractor.value)
);
const extractorCoal = computed(() => Decimal.pow(2, activeExtractor.value)); const extractorCoal = computed(() => Decimal.pow(2, activeExtractor.value));
const extractorOre = computed(() => Decimal.pow(1.2, activeExtractor.value)); const extractorOre = computed(() => Decimal.pow(1.2, activeExtractor.value));
const buildExtractor = createBuyable(() => ({ const buildExtractor = createBuyable(() => ({
@ -279,7 +286,9 @@ const layer = createLayer(id, function (this: BaseLayer) {
const activePump = persistent<DecimalSource>(0); const activePump = persistent<DecimalSource>(0);
const pumpCoal = computed(() => const pumpCoal = computed(() =>
Decimal.pow(row2Upgrades[3].bought.value ? 4 : 5, activePump.value) masteryEffectActive.value
? 1
: Decimal.pow(row2Upgrades[3].bought.value ? 4 : 5, activePump.value)
); );
const pumpOil = computed(() => const pumpOil = computed(() =>
Decimal.add(activePump.value, computedExtraOilPumps.value) Decimal.add(activePump.value, computedExtraOilPumps.value)
@ -367,7 +376,9 @@ const layer = createLayer(id, function (this: BaseLayer) {
} }
return burners; return burners;
}); });
const burnerOil = computed(() => Decimal.pow(effectiveBurners.value, 2)); const burnerOil = computed(() =>
masteryEffectActive.value ? 0 : Decimal.pow(effectiveBurners.value, 2)
);
const burnerCoal = computed(() => Decimal.pow(effectiveBurners.value, 3).mul(1e19)); const burnerCoal = computed(() => Decimal.pow(effectiveBurners.value, 3).mul(1e19));
const burnerMetal = computed(() => Decimal.add(effectiveBurners.value, 1)); const burnerMetal = computed(() => Decimal.add(effectiveBurners.value, 1));
const buildBurner = createBuyable(() => ({ const buildBurner = createBuyable(() => ({
@ -429,7 +440,9 @@ const layer = createLayer(id, function (this: BaseLayer) {
}); });
const activeSmelter = persistent<DecimalSource>(0); const activeSmelter = persistent<DecimalSource>(0);
const smelterOil = computed(() => Decimal.pow(activeSmelter.value, 2).mul(100)); const smelterOil = computed(() =>
masteryEffectActive.value ? 0 : Decimal.pow(activeSmelter.value, 2).mul(100)
);
const smelterMetal = computed(() => Decimal.add(activeSmelter.value, 1)); const smelterMetal = computed(() => Decimal.add(activeSmelter.value, 1));
const buildSmelter = createBuyable(() => ({ const buildSmelter = createBuyable(() => ({
resource: metal.metal, resource: metal.metal,
@ -861,10 +874,20 @@ const layer = createLayer(id, function (this: BaseLayer) {
description: "Cocoa Level 3", description: "Cocoa Level 3",
enabled: management.elfTraining.oilElfTraining.milestones[2].earned enabled: management.elfTraining.oilElfTraining.milestones[2].earned
})), })),
createMultiplicativeModifier(() => ({
multiplier: 4,
description: "Workshop 1200%",
enabled: workshop.milestones.extraExpansionMilestone6.earned
})),
createMultiplicativeModifier(() => ({ createMultiplicativeModifier(() => ({
multiplier: () => coalEffectiveness.value, multiplier: () => coalEffectiveness.value,
description: "Effectiveness", description: "Effectiveness",
enabled: () => Decimal.lt(coalEffectiveness.value, 1) enabled: () => Decimal.lt(coalEffectiveness.value, 1)
})),
createMultiplicativeModifier(() => ({
multiplier: dyes.boosts.red2,
description: "Red Dye",
enabled: dyes.masteryEffectActive
})) }))
]); ]);
const computedDrillPower = computed(() => drillPower.apply(0)); const computedDrillPower = computed(() => drillPower.apply(0));
@ -1079,13 +1102,72 @@ const layer = createLayer(id, function (this: BaseLayer) {
goal: 250000, goal: 250000,
name, name,
day, day,
color, background: color,
modal: { modal: {
show: showModifiersModal, show: showModifiersModal,
display: modifiersModal display: modifiersModal
} }
}); });
const mastery = {
oil: persistent<DecimalSource>(0),
totalOil: persistent<DecimalSource>(0),
depth: persistent<DecimalSource>(0),
drillProgress: persistent<DecimalSource>(0),
activeHeavy: persistent<DecimalSource>(0),
buildHeavy: { amount: persistent<DecimalSource>(0) },
activeHeavy2: persistent<DecimalSource>(0),
buildHeavy2: { amount: persistent<DecimalSource>(0) },
activeExtractor: persistent<DecimalSource>(0),
buildExtractor: { amount: persistent<DecimalSource>(0) },
activePump: persistent<DecimalSource>(0),
buildPump: { amount: persistent<DecimalSource>(0) },
activeBurner: persistent<DecimalSource>(0),
buildBurner: { amount: persistent<DecimalSource>(0) },
activeSmelter: persistent<DecimalSource>(0),
buildSmelter: { amount: persistent<DecimalSource>(0) },
depthMilestones: [
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) }
],
oilMilestones: [
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) },
{ earned: persistent<boolean>(false) }
],
row1Upgrades: [
{ bought: persistent<boolean>(false) },
{ bought: persistent<boolean>(false) },
{ bought: persistent<boolean>(false) },
{ bought: persistent<boolean>(false) },
{ bought: persistent<boolean>(false) }
],
row2Upgrades: [
{ bought: persistent<boolean>(false) },
{ bought: persistent<boolean>(false) },
{ bought: persistent<boolean>(false) },
{ bought: persistent<boolean>(false) },
{ bought: persistent<boolean>(false) }
],
row3Upgrades: [
{ bought: persistent<boolean>(false) },
{ bought: persistent<boolean>(false) },
{ bought: persistent<boolean>(false) },
{ bought: persistent<boolean>(false) },
{ bought: persistent<boolean>(false) }
]
};
const mastered = persistent<boolean>(false);
const masteryEffectActive = computed(
() => mastered.value || main.currentlyMastering.value?.name === name
);
return { return {
name, name,
day, day,
@ -1104,6 +1186,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
buildExtractor, buildExtractor,
activePump, activePump,
buildPump, buildPump,
burnerCoal,
activeBurner, activeBurner,
effectiveBurners, effectiveBurners,
buildBurner, buildBurner,
@ -1145,6 +1228,17 @@ const layer = createLayer(id, function (this: BaseLayer) {
<> <>
{render(trackerDisplay)} {render(trackerDisplay)}
<Spacer /> <Spacer />
{masteryEffectActive.value ? (
<>
<div class="decoration-effect ribbon">
Decoration effect:
<br />
Remove all negative effects of mining drills and oil machines, and
oil burner produces coal
</div>
<Spacer />
</>
) : null}
{Decimal.lt(coalEffectiveness.value, 1) ? ( {Decimal.lt(coalEffectiveness.value, 1) ? (
<div> <div>
Coal efficiency: {format(Decimal.mul(coalEffectiveness.value, 100))}% Coal efficiency: {format(Decimal.mul(coalEffectiveness.value, 100))}%
@ -1284,10 +1378,14 @@ const layer = createLayer(id, function (this: BaseLayer) {
minimizedDisplay: jsx(() => ( minimizedDisplay: jsx(() => (
<div> <div>
{name}{" "} {name}{" "}
<span class="desc">{format(oil.value)} {oil.displayName}</span> <span class="desc">
</div> {format(oil.value)} {oil.displayName}
</span>
</div>
)), )),
mastery,
mastered,
masteryEffectActive
}; };
}); });

View file

@ -16,7 +16,7 @@ import { createUpgrade, GenericUpgrade } from "features/upgrades/upgrade";
import { globalBus } from "game/events"; import { globalBus } from "game/events";
import { BaseLayer, createLayer } from "game/layers"; import { BaseLayer, createLayer } from "game/layers";
import { createMultiplicativeModifier, createSequentialModifier, Modifier } from "game/modifiers"; import { createMultiplicativeModifier, createSequentialModifier, Modifier } from "game/modifiers";
import { noPersist } from "game/persistence"; import { noPersist, persistent } from "game/persistence";
import Decimal, { DecimalSource, format, formatSmall, formatWhole } from "util/bignum"; import Decimal, { DecimalSource, format, formatSmall, formatWhole } from "util/bignum";
import { WithRequired } from "util/common"; import { WithRequired } from "util/common";
import { render, renderCol, renderGrid } from "util/vue"; import { render, renderCol, renderGrid } from "util/vue";
@ -27,6 +27,7 @@ import dyes from "./dyes";
import elves, { ElfBuyable } from "./elves"; import elves, { ElfBuyable } from "./elves";
import management from "./management"; import management from "./management";
import plastic from "./plastic"; import plastic from "./plastic";
import ribbon from "./ribbon";
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";
@ -91,7 +92,8 @@ const layer = createLayer(id, function (this: BaseLayer) {
} }
paperConversion.convert(); paperConversion.convert();
}, },
style: "width: 600px; min-height: unset" style: "width: 600px; min-height: unset",
visibility: () => showIf(!main.isMastery.value || masteryEffectActive.value)
})); }));
function createBook( function createBook(
@ -283,12 +285,25 @@ const layer = createLayer(id, function (this: BaseLayer) {
buyableName: "Metal Machines", buyableName: "Metal Machines",
visibility: () => showIf(elves.elves.metalElf.bought.value) visibility: () => showIf(elves.elves.metalElf.bought.value)
}); });
const dyeBook = createBook({ const primaryDyeBook = createBook({
name: "Arts and Crafts", name: "Arts and Crafts",
elfName: "Carol", elfName: "Carol",
buyableName: "Dyes", buyableName: "Primary Dyes",
visibility: () => showIf(elves.elves.dyeElf.bought.value) visibility: () => showIf(elves.elves.dyeElf.bought.value)
}); });
const secondaryDyeBook = createBook({
name: "Natural Dyeing",
elfName: "Carol",
buyableName: "Secondary Dyes",
visibility: () =>
showIf(elves.elves.dyeElf.bought.value && ribbon.milestones.dyeBook.earned.value)
});
const plasticBook = createBook({
name: "One Plastic Bag",
elfName: "Tinsel",
buyableName: "Plastic Buyables",
visibility: () => showIf(plastic.masteryEffectActive.value)
});
const books = { const books = {
cuttersBook, cuttersBook,
plantersBook, plantersBook,
@ -306,7 +321,9 @@ const layer = createLayer(id, function (this: BaseLayer) {
heavyDrillBook, heavyDrillBook,
oilBook, oilBook,
metalBook, metalBook,
dyeBook primaryDyeBook,
secondaryDyeBook,
plasticBook
}; };
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))
@ -413,6 +430,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
multiplier: 0.1, multiplier: 0.1,
description: "Star Level 2", description: "Star Level 2",
enabled: management.elfTraining.paperElfTraining.milestones[1].earned enabled: management.elfTraining.paperElfTraining.milestones[1].earned
})),
createMultiplicativeModifier(() => ({
multiplier: 0,
description: "Coal Decoration",
enabled: masteryEffectActive
})) }))
]) as WithRequired<Modifier, "description" | "revert">; ]) as WithRequired<Modifier, "description" | "revert">;
const computedAshCost = computed(() => ashCost.apply(1e6)); const computedAshCost = computed(() => ashCost.apply(1e6));
@ -456,9 +478,10 @@ const layer = createLayer(id, function (this: BaseLayer) {
const { total: totalPaper, trackerDisplay } = setUpDailyProgressTracker({ const { total: totalPaper, trackerDisplay } = setUpDailyProgressTracker({
resource: paper, resource: paper,
goal: 5e3, goal: 5e3,
masteryGoal: 5e7,
name, name,
day, day,
color, background: color,
textColor: "var(--feature-foreground)", textColor: "var(--feature-foreground)",
modal: { modal: {
show: showModifiersModal, show: showModifiersModal,
@ -466,6 +489,46 @@ const layer = createLayer(id, function (this: BaseLayer) {
} }
}); });
const mastery = {
paper: persistent<DecimalSource>(0),
totalPaper: persistent<DecimalSource>(0),
books: {
cuttersBook: { amount: persistent<DecimalSource>(0) },
plantersBook: { amount: persistent<DecimalSource>(0) },
expandersBook: { amount: persistent<DecimalSource>(0) },
heatedCuttersBook: { amount: persistent<DecimalSource>(0) },
heatedPlantersBook: { amount: persistent<DecimalSource>(0) },
fertilizerBook: { amount: persistent<DecimalSource>(0) },
smallFireBook: { amount: persistent<DecimalSource>(0) },
bonfireBook: { amount: persistent<DecimalSource>(0) },
kilnBook: { amount: persistent<DecimalSource>(0) },
paperBook: { amount: persistent<DecimalSource>(0) },
boxBook: { amount: persistent<DecimalSource>(0) },
clothBook: { amount: persistent<DecimalSource>(0) },
coalDrillBook: { amount: persistent<DecimalSource>(0) },
heavyDrillBook: { amount: persistent<DecimalSource>(0) },
oilBook: { amount: persistent<DecimalSource>(0) },
metalBook: { amount: persistent<DecimalSource>(0) },
primaryDyeBook: { amount: persistent<DecimalSource>(0) },
secondaryDyeBook: { amount: persistent<DecimalSource>(0) },
plasticBook: { amount: persistent<DecimalSource>(0) }
},
upgrades: {
clothUpgrade: { bought: persistent<boolean>(false) },
drillingUpgrade: { bought: persistent<boolean>(false) },
oilUpgrade: { bought: persistent<boolean>(false) }
},
upgrades2: {
ashUpgrade: { bought: persistent<boolean>(false) },
bookUpgrade: { bought: persistent<boolean>(false) },
treeUpgrade: { bought: persistent<boolean>(false) }
}
};
const mastered = persistent<boolean>(false);
const masteryEffectActive = computed(
() => mastered.value || main.currentlyMastering.value?.name === name
);
return { return {
name, name,
day, day,
@ -482,21 +545,39 @@ const layer = createLayer(id, function (this: BaseLayer) {
<> <>
{render(trackerDisplay)} {render(trackerDisplay)}
<Spacer /> <Spacer />
{masteryEffectActive.value ? (
<>
<div class="decoration-effect">
Decoration effect:
<br />
Pulp no longer requires ash
</div>
<Spacer />
</>
) : null}
<MainDisplay resource={paper} color={color} style="margin-bottom: 0" /> <MainDisplay resource={paper} color={color} style="margin-bottom: 0" />
<Spacer /> <Spacer />
{render(makePaper)} {!main.isMastery.value || masteryEffectActive.value ? (
<Spacer /> <>
{renderGrid(Object.values(upgrades), Object.values(upgrades2))} {render(makePaper)}
<Spacer /> <Spacer />
{renderCol(...Object.values(books))} {renderGrid(Object.values(upgrades), Object.values(upgrades2))}
<Spacer />
{renderCol(...Object.values(books))}
</>
) : null}
</> </>
)), )),
minimizedDisplay: jsx(() => ( minimizedDisplay: jsx(() => (
<div> <div>
{name}{" "} {name}{" "}
<span class="desc">{format(paper.value)} {paper.displayName}</span> <span class="desc">
</div> {format(paper.value)} {paper.displayName}
</span>
</div>
)), )),
mastery,
mastered
}; };
}); });

View file

@ -28,11 +28,13 @@ import { noPersist, persistent } from "game/persistence";
import Decimal, { DecimalSource, format, formatWhole } from "util/bignum"; import Decimal, { DecimalSource, format, formatWhole } from "util/bignum";
import { render, renderCol, renderRow } from "util/vue"; import { render, renderCol, renderRow } from "util/vue";
import { computed, ComputedRef, ref, unref } from "vue"; import { computed, ComputedRef, ref, unref } from "vue";
import boxes from "./boxes"; import boxes, { BoxesBuyable } from "./boxes";
import dyes from "./dyes";
import elves from "./elves";
import management from "./management";
import metal from "./metal"; import metal from "./metal";
import oil from "./oil"; import oil from "./oil";
import dyes from "./dyes"; import paper from "./paper";
import management from "./management";
import workshop from "./workshop"; import workshop from "./workshop";
const id = "plastic"; const id = "plastic";
@ -64,9 +66,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
const activeRefinery = persistent<DecimalSource>(0); const activeRefinery = persistent<DecimalSource>(0);
const oilCost = computed(() => const oilCost = computed(() =>
Decimal.times(activeRefinery.value, 100).times( management.elfTraining.plasticElfTraining.milestones[2].earned.value
management.elfTraining.oilElfTraining.milestones[3].earned.value ? 5 : 1 ? 0
) : Decimal.times(activeRefinery.value, 100).times(
management.elfTraining.oilElfTraining.milestones[3].earned.value ? 5 : 1
)
) as ComputedRef<DecimalSource>; ) as ComputedRef<DecimalSource>;
const buildRefinery = createBuyable(() => ({ const buildRefinery = createBuyable(() => ({
resource: metal.metal, resource: metal.metal,
@ -101,7 +105,8 @@ const layer = createLayer(id, function (this: BaseLayer) {
}, },
style: { style: {
width: "300px" width: "300px"
} },
visibility: () => showIf(!main.isMastery.value || masteryEffectActive.value)
})) as GenericBuyable & { resource: Resource }; })) as GenericBuyable & { resource: Resource };
const { const {
min: minRefinery, min: minRefinery,
@ -116,7 +121,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
const upgradeCost = computed(() => const upgradeCost = computed(() =>
Decimal.pow( Decimal.pow(
5, masteryEffectActive.value ? 4 : 5,
Decimal.add( Decimal.add(
[...Object.values(upgrades), ...Object.values(elfUpgrades)].filter( [...Object.values(upgrades), ...Object.values(elfUpgrades)].filter(
upg => upg.bought.value upg => upg.bought.value
@ -162,7 +167,12 @@ const layer = createLayer(id, function (this: BaseLayer) {
title: "Paper Elf Recruitment", title: "Paper Elf Recruitment",
description: "Double plastic gain and unlock a new elf for training", description: "Double plastic gain and unlock a new elf for training",
showCost: !paperElf.bought.value showCost: !paperElf.bought.value
}) }),
onPurchase() {
if (masteryEffectActive.value) {
elves.elves.paperElf.bought.value = true;
}
}
})) as GenericUpgrade; })) as GenericUpgrade;
const boxElf = createUpgrade(() => ({ const boxElf = createUpgrade(() => ({
resource: noPersist(plastic), resource: noPersist(plastic),
@ -172,7 +182,12 @@ const layer = createLayer(id, function (this: BaseLayer) {
title: "Box Elf Recruitment", title: "Box Elf Recruitment",
description: "Double plastic gain and unlock a new elf for training", description: "Double plastic gain and unlock a new elf for training",
showCost: !boxElf.bought.value showCost: !boxElf.bought.value
}) }),
onPurchase() {
if (masteryEffectActive.value) {
elves.elves.boxElf.bought.value = true;
}
}
})) as GenericUpgrade; })) as GenericUpgrade;
const clothElf = createUpgrade(() => ({ const clothElf = createUpgrade(() => ({
resource: noPersist(plastic), resource: noPersist(plastic),
@ -182,54 +197,116 @@ const layer = createLayer(id, function (this: BaseLayer) {
title: "Cloth Elf Recruitment", title: "Cloth Elf Recruitment",
description: "Double plastic gain and unlock a new elf for training", description: "Double plastic gain and unlock a new elf for training",
showCost: !clothElf.bought.value showCost: !clothElf.bought.value
}) }),
onPurchase() {
if (masteryEffectActive.value) {
elves.elves.clothElf.bought.value = true;
}
}
})) as GenericUpgrade; })) as GenericUpgrade;
const elfUpgrades = { paperElf, boxElf, clothElf }; const elfUpgrades = { paperElf, boxElf, clothElf };
const passivePaper = createBuyable(() => ({ const passivePaper = createBuyable(() => ({
resource: noPersist(plastic), resource: noPersist(plastic),
cost() { cost() {
const amount = this.amount.value; let v = passivePaper.amount.value;
return Decimal.pow(1.3, amount).times(100); v = Decimal.pow(0.95, paper.books.plasticBook.totalAmount.value).times(v);
return Decimal.pow(1.3, v).times(100).div(dyes.boosts.blue2.value);
},
inverseCost(x: DecimalSource) {
let v = Decimal.times(x, dyes.boosts.blue2.value).div(100).log(1.3);
v = v.div(Decimal.pow(0.95, paper.books.plasticBook.totalAmount.value));
return Decimal.isNaN(v) ? Decimal.dZero : v.floor().max(0);
}, },
visibility: () => showIf(paperElf.bought.value), visibility: () => showIf(paperElf.bought.value),
display: { display: {
title: "Plastic Printing Press", title: "Plastic Printing Press",
description: "Gain +1% of your paper gain per second", description: "Gain +1% of your paper gain per second",
effectDisplay: jsx(() => <>{formatWhole(passivePaper.amount.value)}%</>), effectDisplay: jsx(() => <>{formatWhole(passivePaper.totalAmount.value)}%</>),
showAmount: false showAmount: false
} },
})) as GenericBuyable; freeLevels: computed(() => {
let levels: DecimalSource = 0;
if (management.elfTraining.plasticElfTraining.milestones[1].earned.value) {
levels = Decimal.max(passiveBoxes.amount.value, 1)
.sqrt()
.floor()
.add(Decimal.max(clothGains.amount.value, 1).sqrt().floor());
}
return levels;
}),
totalAmount: computed(() =>
Decimal.add(passivePaper.amount.value, passivePaper.freeLevels.value)
)
})) as BoxesBuyable;
const passiveBoxes = createBuyable(() => ({ const passiveBoxes = createBuyable(() => ({
resource: noPersist(plastic), resource: noPersist(plastic),
cost() { cost() {
const amount = this.amount.value; let v = passiveBoxes.amount.value;
return Decimal.pow(1.3, amount).times(100); v = Decimal.pow(0.95, paper.books.plasticBook.totalAmount.value).times(v);
return Decimal.pow(1.3, v).times(100).div(dyes.boosts.blue2.value);
},
inverseCost(x: DecimalSource) {
let v = Decimal.times(x, dyes.boosts.blue2.value).div(100).log(1.3);
v = v.div(Decimal.pow(0.95, paper.books.plasticBook.totalAmount.value));
return Decimal.isNaN(v) ? Decimal.dZero : v.floor().max(0);
}, },
visibility: () => showIf(boxElf.bought.value), visibility: () => showIf(boxElf.bought.value),
display: { display: {
title: "Plastic Box Folder", title: "Plastic Box Folder",
description: "Gain +1% of your box gain per second", description: "Gain +1% of your box gain per second",
effectDisplay: jsx(() => <>{formatWhole(passiveBoxes.amount.value)}%</>), effectDisplay: jsx(() => <>{formatWhole(passiveBoxes.totalAmount.value)}%</>),
showAmount: false showAmount: false
} },
})) as GenericBuyable; freeLevels: computed(() => {
let levels: DecimalSource = 0;
if (management.elfTraining.plasticElfTraining.milestones[1].earned.value) {
levels = Decimal.max(passivePaper.amount.value, 1)
.sqrt()
.floor()
.add(Decimal.max(clothGains.amount.value, 1).sqrt().floor());
}
return levels;
}),
totalAmount: computed(() =>
Decimal.add(passiveBoxes.amount.value, passiveBoxes.freeLevels.value)
)
})) as BoxesBuyable;
const clothGains = createBuyable(() => ({ const clothGains = createBuyable(() => ({
resource: noPersist(plastic), resource: noPersist(plastic),
cost() { cost() {
const amount = this.amount.value; let v = clothGains.amount.value;
return Decimal.pow(1.3, amount).times(100); v = Decimal.pow(0.95, paper.books.plasticBook.totalAmount.value).times(v);
return Decimal.pow(1.3, v).times(100).div(dyes.boosts.blue2.value);
},
inverseCost(x: DecimalSource) {
let v = Decimal.times(x, dyes.boosts.blue2.value).div(100).log(1.3);
v = v.div(Decimal.pow(0.95, paper.books.plasticBook.totalAmount.value));
return Decimal.isNaN(v) ? Decimal.dZero : v.floor().max(0);
}, },
visibility: () => showIf(clothElf.bought.value), visibility: () => showIf(clothElf.bought.value),
display: { display: {
title: "Plastic Shepherd", title: "Plastic Shepherd",
description: "All cloth actions are +10% more efficient", description: "All cloth actions are +10% more efficient",
effectDisplay: jsx(() => ( effectDisplay: jsx(() => (
<>{formatWhole(Decimal.times(clothGains.amount.value, 10))}%</> <>{formatWhole(Decimal.times(clothGains.totalAmount.value, 10))}%</>
)), )),
showAmount: false showAmount: false
} },
})) as GenericBuyable; freeLevels: computed(() => {
let levels: DecimalSource = 0;
if (management.elfTraining.plasticElfTraining.milestones[1].earned.value) {
levels = Decimal.max(passivePaper.amount.value, 1)
.sqrt()
.floor()
.add(Decimal.max(passiveBoxes.amount.value, 1).sqrt().floor());
}
return levels;
}),
totalAmount: computed(() =>
Decimal.add(clothGains.amount.value, clothGains.freeLevels.value)
)
})) as BoxesBuyable;
const buyables = { passivePaper, passiveBoxes, clothGains }; const buyables = { passivePaper, passiveBoxes, clothGains };
const plasticGain = createSequentialModifier(() => [ const plasticGain = createSequentialModifier(() => [
@ -286,6 +363,16 @@ const layer = createLayer(id, function (this: BaseLayer) {
multiplier: () => Decimal.add(dyes.secondaryDyeSum.value, 1).cbrt(), multiplier: () => Decimal.add(dyes.secondaryDyeSum.value, 1).cbrt(),
description: "Colorful Plastic", description: "Colorful Plastic",
enabled: oil.row3Upgrades[2].bought enabled: oil.row3Upgrades[2].bought
})),
createMultiplicativeModifier(() => ({
multiplier: 2,
description: "Tinsel Level 1",
enabled: management.elfTraining.plasticElfTraining.milestones[0].earned
})),
createMultiplicativeModifier(() => ({
multiplier: () => Decimal.div(buildRefinery.amount.value, 100).add(1),
description: "Tinsel Level 4",
enabled: management.elfTraining.plasticElfTraining.milestones[3].earned
})) }))
]); ]);
const computedPlasticGain = computed(() => plasticGain.apply(0)); const computedPlasticGain = computed(() => plasticGain.apply(0));
@ -303,7 +390,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
goal: 2.5e5, goal: 2.5e5,
name, name,
day, day,
color, background: color,
textColor: "var(--feature-foreground)", textColor: "var(--feature-foreground)",
modal: { modal: {
show: showModifiersModal, show: showModifiersModal,
@ -311,8 +398,35 @@ const layer = createLayer(id, function (this: BaseLayer) {
} }
}); });
const mastery = {
plastic: persistent<DecimalSource>(0),
totalPlastic: persistent<DecimalSource>(0),
activeRefinery: persistent<DecimalSource>(0),
buildRefinery: { amount: persistent<DecimalSource>(0) },
upgrades: {
paperTools: { bought: persistent<boolean>(false) },
boxTools: { bought: persistent<boolean>(false) },
clothTools: { bought: persistent<boolean>(false) }
},
elfUpgrades: {
paperElf: { bought: persistent<boolean>(false) },
boxElf: { bought: persistent<boolean>(false) },
clothElf: { bought: persistent<boolean>(false) }
},
buyables: {
passivePaper: { amount: persistent<DecimalSource>(0) },
passiveBoxes: { amount: persistent<DecimalSource>(0) },
clothGains: { amount: persistent<DecimalSource>(0) }
}
};
const mastered = persistent<boolean>(false);
const masteryEffectActive = computed(
() => mastered.value || main.currentlyMastering.value?.name === name
);
return { return {
name, name,
day,
color, color,
plastic, plastic,
totalPlastic, totalPlastic,
@ -328,6 +442,16 @@ const layer = createLayer(id, function (this: BaseLayer) {
<> <>
{render(trackerDisplay)} {render(trackerDisplay)}
<Spacer /> <Spacer />
{masteryEffectActive.value ? (
<>
<div class="decoration-effect ribbon">
Decoration effect:
<br />
Unlock a new elf for training, and upgrades go up in cost slower
</div>
<Spacer />
</>
) : null}
<MainDisplay <MainDisplay
resource={plastic} resource={plastic}
color={color} color={color}
@ -357,9 +481,14 @@ const layer = createLayer(id, function (this: BaseLayer) {
minimizedDisplay: jsx(() => ( minimizedDisplay: jsx(() => (
<div> <div>
{name}{" "} {name}{" "}
<span class="desc">{format(plastic.value)} {plastic.displayName}</span> <span class="desc">
</div> {format(plastic.value)} {plastic.displayName}
</span>
</div>
)), )),
mastery,
mastered,
masteryEffectActive
}; };
}); });

276
src/data/layers/ribbon.tsx Normal file
View file

@ -0,0 +1,276 @@
import Spacer from "components/layout/Spacer.vue";
import Modal from "components/Modal.vue";
import { createCollapsibleMilestones, createCollapsibleModifierSections } from "data/common";
import { createBar, GenericBar } from "features/bars/bar";
import { createClickable } from "features/clickables/clickable";
import { softcap } from "features/conversion";
import { jsx, showIf } from "features/feature";
import { createMilestone } from "features/milestones/milestone";
import MainDisplay from "features/resources/MainDisplay.vue";
import { createResource } from "features/resources/resource";
import { globalBus } from "game/events";
import { createLayer, layers } from "game/layers";
import { createSequentialModifier } from "game/modifiers";
import { persistent } from "game/persistence";
import player from "game/player";
import { DecimalSource } from "lib/break_eternity";
import Decimal, { format, formatWhole } from "util/bignum";
import { Direction } from "util/common";
import { render } from "util/vue";
import { computed, ref, unref, watchEffect } from "vue";
import { main } from "../projEntry";
import cloth from "./cloth";
import dyes from "./dyes";
import elves from "./elves";
const id = "ribbon";
const day = 16;
const layer = createLayer(id, () => {
const name = "Ribbon";
const color = "darkred";
const ribbon = createResource<DecimalSource>(0, "Ribbon");
const currentDyeCost = computed(() =>
Decimal.times(
softcap(ribbon.value, 10, 2),
[dyes.dyes.orange, dyes.dyes.green, dyes.dyes.purple].includes(currentDyeType.value)
? 2e6
: 1e13
)
);
const currentDyeType = computed(
() => Object.values(dyes.dyes)[new Decimal(ribbon.value).toNumber() % 6]
);
const ribbonProgress = persistent<DecimalSource>(0);
const ribbonProgressBar = createBar(() => ({
direction: Direction.Right,
width: 100,
height: 10,
style: "margin-top: 8px",
borderStyle: "border-color: black",
baseStyle: "margin-top: -1px",
fillStyle: "margin-top: -1px; transition-duration: 0s; background: black",
progress: () => Decimal.div(ribbonProgress.value, computedRibbonCooldown.value)
}));
const makeRibbon = createClickable(() => ({
display: {
title: "Make Ribbon",
description: jsx(() => (
<>
Create another ribbon with {format(currentDyeCost.value)}{" "}
{currentDyeType.value.name} and {format(1e9)} {cloth.cloth.displayName}
<br />
{render(ribbonProgressBar)}
</>
))
},
style: {
minHeight: "80px"
},
canClick: () =>
Decimal.gte(ribbonProgress.value, computedRibbonCooldown.value) &&
Decimal.gte(currentDyeType.value.amount.value, currentDyeCost.value) &&
Decimal.gte(cloth.cloth.value, 1e9),
onClick() {
if (!unref(makeRibbon.canClick)) {
return;
}
currentDyeType.value.amount.value = 0;
currentDyeType.value.buyable.amount.value = 0;
cloth.cloth.value = Decimal.sub(cloth.cloth.value, 1e9);
ribbon.value = Decimal.add(ribbon.value, 1);
ribbonProgress.value = 0;
}
}));
const ribbonCooldown = createSequentialModifier(() => []);
const computedRibbonCooldown = computed(() => ribbonCooldown.apply(10));
const [generalTab, generalTabCollapsed] = createCollapsibleModifierSections(() => [
{
title: "Ribbon Cooldown",
modifier: ribbonCooldown,
base: 10
}
]);
const showModifiersModal = ref(false);
const modifiersModal = jsx(() => (
<Modal
modelValue={showModifiersModal.value}
onUpdate:modelValue={(value: boolean) => (showModifiersModal.value = value)}
v-slots={{
header: () => <h2>{name} Modifiers</h2>,
body: generalTab
}}
/>
));
const secondaryDyeElf = createMilestone(() => ({
display: {
requirement: "5 Ribbons",
effectDisplay: "Carol will now mix secondary dyes for you"
},
shouldEarn: () => Decimal.gte(ribbon.value, 5)
}));
const dyeBook = createMilestone(() => ({
display: {
requirement: "10 Ribbons",
effectDisplay: "Unlock a new book"
},
shouldEarn: () => Decimal.gte(ribbon.value, 10),
visibility: () => showIf(secondaryDyeElf.earned.value)
}));
const milestones = { secondaryDyeElf, dyeBook };
const { collapseMilestones, display: milestonesDisplay } =
createCollapsibleMilestones(milestones);
const masteryReq = computed(() =>
Decimal.sub(main.masteredDays.value, 5).times(
Decimal.sub(main.masteredDays.value, 4).div(2)
)
);
const enterMasteryButton = createClickable(() => ({
display: () => ({
title: `${main.isMastery.value ? "Stop Decorating" : "Begin Decorating"} ${
Object.values(layers).find(
layer =>
unref((layer as any).mastered) === false &&
!["Elves", "Management"].includes(unref(layer?.name ?? ""))
)?.name
}`,
description: jsx(() => {
return (
<>
<br />
Decorating brings you to a separate version of each day that only allows
layers that are decorated or being decorated to work. These days will have a
new decoration effect that applies outside of decorating as well.
<br />
You can safely start and stop decorating without losing progress
{main.isMastery.value ? null : (
<>
<br />
<br />
Requires {formatWhole(masteryReq.value)} total ribbons
</>
)}
</>
);
})
}),
visibility: () => showIf(main.day.value === day),
canClick() {
return main.isMastery.value || Decimal.gte(ribbon.value, masteryReq.value);
},
onClick() {
if (!unref(enterMasteryButton.canClick)) {
return;
}
main.toggleMastery();
const layer = main.currentlyMastering.value?.id ?? "trees";
if (!player.tabs.includes(layer)) {
main.openDay(layer);
}
if (layer === "cloth") {
elves.elves.plasticElf.bought.value = true;
} else if (layer === "letters") {
elves.elves.coalDrillElf.bought.value = true;
elves.elves.heavyDrillElf.bought.value = true;
elves.elves.oilElf.bought.value = true;
elves.elves.metalElf.bought.value = true;
}
},
style: {
width: "300px",
minHeight: "160px"
}
}));
const dayProgress = createBar(() => ({
direction: Direction.Right,
width: 600,
height: 25,
fillStyle: "animation: 15s ribbon-bar linear infinite",
progress: () => (main.day.value === day ? Decimal.div(main.masteredDays.value - 6, 5) : 1),
display: jsx(() =>
main.day.value === day ? (
<>
{main.masteredDays.value - 6}
/5 days decorated
</>
) : (
""
)
)
})) as GenericBar;
watchEffect(() => {
if (
main.day.value === day &&
Decimal.gte(main.masteredDays.value, 11) &&
main.showLoreModal.value === false
) {
main.completeDay();
}
});
globalBus.on("update", diff => {
if (Decimal.lt(main.day.value, day)) {
return;
}
if (Decimal.gte(ribbonProgress.value, computedRibbonCooldown.value)) {
ribbonProgress.value = computedRibbonCooldown.value;
} else {
ribbonProgress.value = Decimal.add(ribbonProgress.value, diff);
if (makeRibbon.isHolding.value) {
makeRibbon.onClick();
}
}
});
return {
name,
day,
color,
ribbon,
ribbonProgress,
milestones,
collapseMilestones,
generalTabCollapsed,
display: jsx(() => {
return (
<div style="width: 620px">
<div>
{main.day.value === day
? `Decorate 5 previous days to complete the day`
: `${name} Complete!`}{" "}
-{" "}
<button
class="button"
style="display: inline-block;"
onClick={() => (showModifiersModal.value = true)}
>
Check Modifiers
</button>
</div>
{render(dayProgress)}
{render(modifiersModal)}
<Spacer />
<MainDisplay resource={ribbon} color={color} />
{render(makeRibbon)}
<Spacer />
{render(enterMasteryButton)}
<Spacer />
{render(milestonesDisplay)}
</div>
);
}),
minWidth: 700
};
});
export default layer;

View file

@ -0,0 +1,65 @@
@keyframes letters-bar {
from {
background: 0 0 / auto 70% no-repeat linear-gradient(white, white), 0 0 / 113px 113px repeat
repeating-linear-gradient(-45deg,
red 0 20px, white 20px 40px,
blue 40px 60px, white 60px 80px
);
}
to {
background: 0 0 / auto 70% no-repeat linear-gradient(white, white), 113px 0px / 113px 113px repeat
repeating-linear-gradient(-45deg,
red 0 20px, white 20px 40px,
blue 40px 60px, white 60px 80px
);
}
}
@keyframes wrapping-paper-bar {
from {
background: 0 0 / 113px 113px repeat repeating-linear-gradient(-45deg,
rgb(255, 76, 76) 0 10px,
rgb(255, 255, 255) 10px 20px,
rgb(65, 255, 95) 20px 30px,
rgb(255, 255, 255) 30px 40px
);
}
to {
background: 113px 0 / 113px 113px repeat repeating-linear-gradient(-45deg,
rgb(255, 76, 76) 0 10px,
rgb(255, 255, 255) 10px 20px,
rgb(65, 255, 95) 20px 30px,
rgb(255, 255, 255) 30px 40px
);
}
}
@keyframes ribbon-bar {
from {
background: 0 0 / 114px 114px repeat repeating-linear-gradient(-45deg,
darkred 0 10px,
#af0000 10px 20px
);
}
to {
background: 114px 0px / 114px 114px repeat repeating-linear-gradient(-45deg,
darkred 0 10px,
#af0000 10px 20px
);
}
}
@keyframes toys-bar {
from {
background: 0 0 / 114px 114px repeat repeating-linear-gradient(-45deg,
#4bdc13 0 10px,
green 10px 20px
);
}
to {
background: 114px 0px / 114px 114px repeat repeating-linear-gradient(-45deg,
#4bdc13 0 10px,
green 10px 20px
);
}
}

View file

@ -1,21 +1,21 @@
@keyframes focused-focus-bar { @keyframes focused-focus-bar {
from { from {
background: 0 0 / 28px 28px repeat background: 0 0 / 28px 28px repeat
repeating-linear-gradient(-45deg, red, red 10px, green 10px, green 20px); repeating-linear-gradient(-45deg, red 0 10px, green 10px 20px);
} }
to { to {
background: 28px 0px / 28px 28px repeat background: 28px 0px / 28px 28px repeat
repeating-linear-gradient(-45deg, red, red 10px, green 10px, green 20px); repeating-linear-gradient(-45deg, red 0 10px, green 10px 20px);
} }
} }
@keyframes focused-xp-bar { @keyframes focused-xp-bar {
from { from {
background: 0 0 / 28px 28px repeat background: 0 0 / 28px 28px repeat
repeating-linear-gradient(-45deg, yellow, yellow 10px, lime 10px, lime 20px); repeating-linear-gradient(-45deg, yellow 0 10px, lime 10px 20px);
} }
to { to {
background: 28px 0px / 28px 28px repeat background: 28px 0px / 28px 28px repeat
repeating-linear-gradient(-45deg, yellow, yellow 10px, lime 10px, lime 20px); repeating-linear-gradient(-45deg, yellow 0 10px, lime 10px 20px);
} }
} }

325
src/data/layers/toys.tsx Normal file
View file

@ -0,0 +1,325 @@
/**
* @module
* @hidden
*/
import Spacer from "components/layout/Spacer.vue";
import Modal from "components/Modal.vue";
import { main } from "data/projEntry";
import { createBar } from "features/bars/bar";
import {
createCollapsibleMilestones,
createCollapsibleModifierSections,
setUpDailyProgressTracker
} from "data/common";
import { createBuyable, GenericBuyable } from "features/buyable";
import { createClickable } from "features/clickables/clickable";
import { jsx, showIf } from "features/feature";
import { createHotkey } from "features/hotkey";
import MainDisplay from "features/resources/MainDisplay.vue";
import { createMilestone } from "features/milestones/milestone";
import { createResource, Resource } from "features/resources/resource";
import { createUpgrade } from "features/upgrades/upgrade";
import { globalBus } from "game/events";
import { BaseLayer, createLayer } from "game/layers";
import {
createAdditiveModifier,
createExponentialModifier,
createMultiplicativeModifier,
createSequentialModifier,
Modifier
} from "game/modifiers";
import { noPersist, persistent } from "game/persistence";
import Decimal, { DecimalSource, format, formatGain, formatLimit, formatWhole } from "util/bignum";
import { Direction, WithRequired } from "util/common";
import { render, renderGrid, renderRow } from "util/vue";
import { computed, ref } from "vue";
import metal from "./metal";
import plastic from "./plastic";
import cloth from "./cloth";
import trees from "./trees";
import dyes from "./dyes";
import paper from "./paper";
import workshop from "./workshop";
const id = "toys";
const day = 17;
const layer = createLayer(id, function (this: BaseLayer) {
const name = "Toys";
const colorBright = "#4BDC13";
const colorDark = "green";
const clothes = createResource<DecimalSource>(0, "clothes");
const woodenBlocks = createResource<DecimalSource>(0, " wooden blocks");
const trucks = createResource<DecimalSource>(0, "trucks");
const toyGain = createSequentialModifier(() => []);
const toySum = createResource(
computed(() => Decimal.add(clothes.value, woodenBlocks.value).add(trucks.value)),
"toys"
);
const clothesCost = computed(() => {
let clothFactor = Decimal.add(1, clothesBuyable.amount.value);
if (milestones.milestone1.earned) {
clothFactor = clothFactor.div(
Decimal.div(workshop.foundationProgress.value, 100).floor()
);
}
return {
cloth: clothFactor.mul(1e8),
dye: clothFactor.mul(1e6)
};
});
const clothesBuyable = createBuyable(() => ({
display: jsx(() => (
<>
<h3>Make Clothes</h3>
<div>Click this buyable to make some clothes!</div>
<div>You have {formatWhole(clothes.value)} clothes.</div>
<div>
Costs {format(clothesCost.value.cloth)} cloth and requires{" "}
{format(clothesCost.value.dye)} of red, yellow, and blue dye
</div>
</>
)),
canPurchase(): boolean {
return (
clothesCost.value.cloth.lte(cloth.cloth.value) &&
clothesCost.value.dye.lte(dyes.dyes.blue.amount.value) &&
clothesCost.value.dye.lte(dyes.dyes.red.amount.value) &&
clothesCost.value.dye.lte(dyes.dyes.yellow.amount.value)
);
},
onPurchase() {
cloth.cloth.value = Decimal.sub(cloth.cloth.value, clothesCost.value.cloth);
this.amount.value = Decimal.add(this.amount.value, 1);
clothes.value = this.amount.value;
}
})) as GenericBuyable;
const woodenBlocksCost = computed(() => {
let woodFactor = Decimal.add(1, woodenBlocksBuyable.amount.value).pow(5);
if (milestones.milestone1.earned) {
woodFactor = woodFactor.div(
Decimal.div(workshop.foundationProgress.value, 100).floor()
);
}
return {
wood: woodFactor.mul(1e40)
};
});
const woodenBlocksBuyable = createBuyable(() => ({
display: jsx(() => (
<>
<h3>Make Wooden Blocks</h3>
<div>Click this buyable to make some wooden blocks!</div>
<div>You have {formatWhole(woodenBlocks.value)} wooden blocks.</div>
<div>Costs {format(woodenBlocksCost.value.wood)} logs</div>
</>
)),
canPurchase(): boolean {
return woodenBlocksCost.value.wood.lte(trees.logs.value);
},
onPurchase() {
trees.logs.value = Decimal.sub(trees.logs.value, woodenBlocksCost.value.wood);
this.amount.value = Decimal.add(this.amount.value, 1);
woodenBlocks.value = this.amount.value;
}
})) as GenericBuyable;
const trucksCost = computed(() => {
let factor = Decimal.add(1, trucksBuyable.amount.value).pow(3);
let plasticFactor = Decimal.add(1, trucksBuyable.amount.value);
if (milestones.milestone1.earned) {
factor = factor.div(Decimal.div(workshop.foundationProgress.value, 100).floor());
plasticFactor = plasticFactor.div(
Decimal.div(workshop.foundationProgress.value, 100).floor()
);
}
return {
metal: factor.mul(1e25),
plastic: plasticFactor.mul(1e10)
};
});
const trucksBuyable = createBuyable(() => ({
display: jsx(() => (
<>
<h3>Make Trucks</h3>
<div>Click this buyable to make some trucks!</div>
<div>You have {formatWhole(trucks.value)} trucks.</div>
<div>
Costs {format(trucksCost.value.metal)} metal and{" "}
{format(trucksCost.value.plastic)} plastic
</div>
</>
)),
canPurchase(): boolean {
return (
trucksCost.value.metal.lte(metal.metal.value) &&
trucksCost.value.plastic.lte(plastic.plastic.value)
);
},
onPurchase() {
metal.metal.value = Decimal.sub(metal.metal.value, trucksCost.value.metal);
plastic.plastic.value = Decimal.sub(plastic.plastic.value, trucksCost.value.plastic);
this.amount.value = Decimal.add(this.amount.value, 1);
trucks.value = this.amount.value;
}
})) as GenericBuyable;
const buyables = [clothesBuyable, woodenBlocksBuyable, trucksBuyable];
const trucksUpgrade1 = createUpgrade(() => ({
resource: noPersist(trucks),
cost: 10,
display: {
title: "Load logs onto trucks",
description: "Log gain is doubled."
}
}));
const clothesUpgrade1 = createUpgrade(() => ({
resource: noPersist(clothes),
cost: 30,
display: {
title: "Give elves clothes to wear",
description:
"Multiply ore per mining operation and auto-smelt purity by the number of clothes you have."
}
}));
const woodenBlocksUpgrade1 = createUpgrade(() => ({
resource: noPersist(woodenBlocks),
cost: 15,
display: {
title: "Build wooden towers",
description: "You can now build 2 extra tall workshops!"
}
}));
const row1Upgrades = [trucksUpgrade1, clothesUpgrade1, woodenBlocksUpgrade1];
const milestone1 = createMilestone(() => ({
display: {
requirement: "10 toys",
effectDisplay:
"The cost of making toys is divided by the number of complete workshops you have."
},
shouldEarn: () => Decimal.gte(toySum.value, 10)
}));
const milestone2 = createMilestone(() => ({
display: {
requirement: "100 toys",
effectDisplay: "Unlock black dyes."
},
shouldEarn: () => Decimal.gte(toySum.value, 100)
}));
const milestones = { milestone1, milestone2 };
const { collapseMilestones, display: milestonesDisplay } =
createCollapsibleMilestones(milestones);
const [generalTab, generalTabCollapsed] = createCollapsibleModifierSections(() => [
{
title: `Toy Gain`,
modifier: toyGain,
base: 1,
visible: true
}
]);
const showModifiersModal = ref(false);
const modifiersModal = jsx(() => (
<Modal
modelValue={showModifiersModal.value}
onUpdate:modelValue={(value: boolean) => (showModifiersModal.value = value)}
v-slots={{
header: () => <h2>{name} Modifiers</h2>,
body: generalTab
}}
/>
));
globalBus.on("update", diff => {
if (Decimal.lt(main.day.value, day)) {
return;
}
if (Decimal.lt(clothes.value, clothesBuyable.amount.value)) {
clothesBuyable.amount.value = clothes.value;
}
if (Decimal.lt(woodenBlocks.value, woodenBlocksBuyable.amount.value)) {
woodenBlocksBuyable.amount.value = woodenBlocks.value;
}
if (Decimal.lt(trucks.value, trucksBuyable.amount.value)) {
trucksBuyable.amount.value = trucks.value;
}
});
const { total: totalToys, trackerDisplay } = setUpDailyProgressTracker({
resource: toySum,
goal: 200,
name,
day,
background: {
gradient: "toys-bar",
duration: "15s"
},
modal: {
show: showModifiersModal,
display: modifiersModal
}
});
return {
name,
day,
color: colorBright,
clothes,
woodenBlocks,
trucks,
toySum,
totalToys,
buyables,
row1Upgrades,
milestones,
generalTabCollapsed,
collapseMilestones,
minWidth: 700,
display: jsx(() => (
<>
{render(trackerDisplay)}
<Spacer />
<MainDisplay
resource={clothes}
color={colorBright}
style="margin-bottom: 0"
productionDisplay={undefined}
/>
<MainDisplay
resource={woodenBlocks}
color={colorDark}
style="margin-bottom: 0"
productionDisplay={undefined}
/>
<MainDisplay
resource={trucks}
color={colorDark}
style="margin-bottom: 0"
productionDisplay={undefined}
/>
<Spacer />
{renderRow(...buyables)}
<Spacer />
{renderGrid(row1Upgrades)}
<Spacer />
{milestonesDisplay()}
</>
)),
minimizedDisplay: jsx(() => (
<div>
{name} - {format(toySum.value)} {"total toys"}
</div>
))
};
});
export default layer;

View file

@ -2,17 +2,18 @@
* @module * @module
* @hidden * @hidden
*/ */
import HotkeyVue from "components/Hotkey.vue";
import Spacer from "components/layout/Spacer.vue"; import Spacer from "components/layout/Spacer.vue";
import Modal from "components/Modal.vue"; import Modal from "components/Modal.vue";
import { createCollapsibleModifierSections, setUpDailyProgressTracker } from "data/common"; import { createCollapsibleModifierSections, setUpDailyProgressTracker } from "data/common";
import { main } from "data/projEntry"; import { main } from "data/projEntry";
import { createBar } from "features/bars/bar"; import { createBar } from "features/bars/bar";
import { createBuyable, GenericBuyable } from "features/buyable"; import { createBuyable } from "features/buyable";
import { createClickable } from "features/clickables/clickable"; import { createClickable } from "features/clickables/clickable";
import { jsx, showIf } from "features/feature"; import { jsx, showIf } from "features/feature";
import { createHotkey } from "features/hotkey"; import { createHotkey } from "features/hotkey";
import MainDisplay from "features/resources/MainDisplay.vue"; import MainDisplay from "features/resources/MainDisplay.vue";
import { createResource, Resource } from "features/resources/resource"; import { createResource, Resource, trackTotal } from "features/resources/resource";
import { createUpgrade } from "features/upgrades/upgrade"; import { createUpgrade } from "features/upgrades/upgrade";
import { globalBus } from "game/events"; import { globalBus } from "game/events";
import { BaseLayer, createLayer } from "game/layers"; import { BaseLayer, createLayer } from "game/layers";
@ -37,6 +38,7 @@ import management from "./management";
import paper from "./paper"; import paper from "./paper";
import workshop from "./workshop"; import workshop from "./workshop";
import wrappingPaper from "./wrapping-paper"; import wrappingPaper from "./wrapping-paper";
import toys from "./toys";
const id = "trees"; const id = "trees";
const day = 1; const day = 1;
@ -51,8 +53,9 @@ const layer = createLayer(id, function (this: BaseLayer) {
const logs = createResource<DecimalSource>(0, "logs"); const logs = createResource<DecimalSource>(0, "logs");
// Think of saplings as spent trees // Think of saplings as spent trees
const saplings = createResource<DecimalSource>(0, "saplings"); const saplings = createResource<DecimalSource>(0, "saplings");
const createdSaplings = persistent<DecimalSource>(0);
const ema = ref<DecimalSource>(0); const averageLogGain = ref<DecimalSource>(0);
const lastAutoCuttingAmount = ref<DecimalSource>(0); const lastAutoCuttingAmount = ref<DecimalSource>(0);
const lastAutoPlantedAmount = ref<DecimalSource>(0); const lastAutoPlantedAmount = ref<DecimalSource>(0);
@ -92,6 +95,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
addend: () => Decimal.pow(computedManualCuttingAmount.value, 0.99), addend: () => Decimal.pow(computedManualCuttingAmount.value, 0.99),
description: "Hope Level 1", description: "Hope Level 1",
enabled: management.elfTraining.expandersElfTraining.milestones[0].earned enabled: management.elfTraining.expandersElfTraining.milestones[0].earned
})),
createAdditiveModifier(() => ({
addend: createdSaplings,
description: "Trees Decoration",
enabled: masteryEffectActive
})) }))
]) as WithRequired<Modifier, "description" | "revert">; ]) as WithRequired<Modifier, "description" | "revert">;
const trees = createResource( const trees = createResource(
@ -396,7 +404,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
createAdditiveModifier(() => ({ createAdditiveModifier(() => ({
addend: 1, addend: 1,
description: "Automated Spade", description: "Automated Spade",
enabled: autoPlantUpgrade1.bought enabled: autoPlantUpgrade1.bought.value
})), })),
createAdditiveModifier(() => ({ createAdditiveModifier(() => ({
addend: () => Decimal.div(autoPlantingBuyable1.amount.value, 2), addend: () => Decimal.div(autoPlantingBuyable1.amount.value, 2),
@ -528,6 +536,16 @@ const layer = createLayer(id, function (this: BaseLayer) {
description: "Christmas Wrapping Paper", description: "Christmas Wrapping Paper",
enabled: computed(() => Decimal.gt(wrappingPaper.boosts.christmas1.value, 1)) enabled: computed(() => Decimal.gt(wrappingPaper.boosts.christmas1.value, 1))
})), })),
createMultiplicativeModifier(() => ({
multiplier: () => Decimal.add(computedTotalTrees.value, 1).log10(),
description: "Trees Decoration",
enabled: masteryEffectActive
})),
createMultiplicativeModifier(() => ({
multiplier: 2,
description: "Load logs onto trucks",
enabled: toys.row1Upgrades[0].bought
})),
createExponentialModifier(() => ({ createExponentialModifier(() => ({
exponent: 1.2, exponent: 1.2,
description: "100% Foundation Completed", description: "100% Foundation Completed",
@ -553,7 +571,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
const cutTree = createClickable(() => ({ const cutTree = createClickable(() => ({
display: { display: {
title: "Cut trees", title: jsx(() => (
<h3>
Cut trees <HotkeyVue hotkey={cutTreeHK} />
</h3>
)),
description: jsx(() => ( description: jsx(() => (
<> <>
Cut down up to {formatWhole(Decimal.floor(computedManualCuttingAmount.value))}{" "} Cut down up to {formatWhole(Decimal.floor(computedManualCuttingAmount.value))}{" "}
@ -585,9 +607,14 @@ const layer = createLayer(id, function (this: BaseLayer) {
).floor() ).floor()
) )
) )
); ).max(0);
if (masteryEffectActive.value) {
createdSaplings.value = Decimal.add(createdSaplings.value, amount).max(0);
}
logs.value = Decimal.add(logs.value, Decimal.times(logGain.apply(1), amount)); logs.value = Decimal.add(logs.value, Decimal.times(logGain.apply(1), amount));
saplings.value = Decimal.add(saplings.value, amount); saplings.value = Decimal.mul(amount, masteryEffectActive.value ? 2 : 1).add(
saplings.value
);
manualCutProgress.value = 0; manualCutProgress.value = 0;
} }
})); }));
@ -604,7 +631,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
})); }));
const plantTree = createClickable(() => ({ const plantTree = createClickable(() => ({
display: { display: {
title: "Plant trees", title: jsx(() => (
<h3>
Plant trees <HotkeyVue hotkey={plantTreeHK} />
</h3>
)),
description: jsx(() => ( description: jsx(() => (
<> <>
Plant up to {formatWhole(Decimal.floor(computedManualPlantingAmount.value))}{" "} Plant up to {formatWhole(Decimal.floor(computedManualPlantingAmount.value))}{" "}
@ -636,7 +667,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
).floor() ).floor()
) )
) )
); ).max(0);
saplings.value = Decimal.sub(saplings.value, amount); saplings.value = Decimal.sub(saplings.value, amount);
manualPlantProgress.value = 0; manualPlantProgress.value = 0;
} }
@ -688,14 +719,14 @@ const layer = createLayer(id, function (this: BaseLayer) {
title: `Auto Planting Amount`, title: `Auto Planting Amount`,
modifier: autoPlantingAmount, modifier: autoPlantingAmount,
base: 0, base: 0,
visible: autoPlantUpgrade1.bought, visible: autoCutUpgrade1.bought,
unit: "/s" unit: "/s"
}, },
{ {
title: `Forest Size`, title: `Forest Size`,
modifier: totalTrees, modifier: totalTrees,
base: 10, base: 10,
visible: researchUpgrade2.bought visible: () => researchUpgrade2.bought.value || masteryEffectActive.value
} }
]); ]);
const showModifiersModal = ref(false); const showModifiersModal = ref(false);
@ -746,27 +777,32 @@ const layer = createLayer(id, function (this: BaseLayer) {
const amountCut = Decimal.min( const amountCut = Decimal.min(
trees.value, trees.value,
Decimal.times(computedAutoCuttingAmount.value, diff) Decimal.times(computedAutoCuttingAmount.value, diff)
); ).max(0);
const logsGained = Decimal.mul(logGain.apply(1), amountCut); const logsGained = Decimal.mul(logGain.apply(1), amountCut);
const effectiveLogsGained = Decimal.div(logsGained, diff); const effectiveLogsGained = Decimal.div(logsGained, diff);
ema.value = Decimal.mul(effectiveLogsGained, SMOOTHING_FACTOR).add( averageLogGain.value = Decimal.mul(effectiveLogsGained, SMOOTHING_FACTOR).add(
Decimal.mul(ema.value, Decimal.dOne.sub(SMOOTHING_FACTOR)) Decimal.mul(averageLogGain.value, Decimal.dOne.sub(SMOOTHING_FACTOR))
); );
logs.value = Decimal.add(logs.value, logsGained); logs.value = Decimal.add(logs.value, logsGained);
saplings.value = Decimal.add(saplings.value, amountCut); saplings.value = Decimal.mul(amountCut, masteryEffectActive.value ? 2 : 1).add(
saplings.value
);
if (masteryEffectActive.value) {
createdSaplings.value = Decimal.add(createdSaplings.value, amountCut);
}
const amountPlanted = Decimal.min( const amountPlanted = Decimal.min(
saplings.value, saplings.value,
Decimal.times(computedAutoPlantingAmount.value, diff) Decimal.times(computedAutoPlantingAmount.value, diff)
); ).max(0);
saplings.value = Decimal.sub(saplings.value, amountPlanted); saplings.value = Decimal.sub(saplings.value, amountPlanted);
if(Decimal.gte(saplings.value, computedTotalTrees.value)) saplings.value = computedTotalTrees.value;
}); });
const netSaplingGain = computed(() => const netSaplingGain = computed(() =>
Decimal.sub(computedAutoCuttingAmount.value, computedAutoPlantingAmount.value) Decimal.sub(
Decimal.mul(computedAutoCuttingAmount.value, mastered.value ? 2 : 1),
computedAutoPlantingAmount.value
)
); );
const netTreeGain = computed(() => const netTreeGain = computed(() =>
Decimal.sub(computedAutoPlantingAmount.value, computedAutoCuttingAmount.value) Decimal.sub(computedAutoPlantingAmount.value, computedAutoCuttingAmount.value)
@ -774,17 +810,19 @@ const layer = createLayer(id, function (this: BaseLayer) {
const cutTreeHK = createHotkey(() => ({ const cutTreeHK = createHotkey(() => ({
key: "c", key: "c",
description: 'Press the "Cut trees" button.', description: "Cut trees",
onPress: () => { onPress: () => {
if (cutTree.canClick.value) cutTree.onClick(); if (cutTree.canClick.value) cutTree.onClick();
} },
enabled: main.days[day - 1].opened
})); }));
const plantTreeHK = createHotkey(() => ({ const plantTreeHK = createHotkey(() => ({
key: "p", key: "p",
description: 'Press the "Plant trees" button.', description: "Plant trees",
onPress: () => { onPress: () => {
if (plantTree.canClick.value) plantTree.onClick(); if (plantTree.canClick.value) plantTree.onClick();
} },
enabled: main.days[day - 1].opened
})); }));
const { total: totalLogs, trackerDisplay } = setUpDailyProgressTracker({ const { total: totalLogs, trackerDisplay } = setUpDailyProgressTracker({
@ -792,20 +830,52 @@ const layer = createLayer(id, function (this: BaseLayer) {
goal: 1e4, goal: 1e4,
name, name,
day, day,
color: colorDark, background: colorDark,
modal: { modal: {
show: showModifiersModal, show: showModifiersModal,
display: modifiersModal display: modifiersModal
} }
}); });
const mastery = {
logs: persistent<DecimalSource>(0),
totalLogs: persistent<DecimalSource>(0),
saplings: persistent<DecimalSource>(0),
createdSaplings: persistent<DecimalSource>(0),
row1Upgrades: [
{ bought: persistent<boolean>(false) },
{ bought: persistent<boolean>(false) },
{ bought: persistent<boolean>(false) },
{ bought: persistent<boolean>(false) },
{ bought: persistent<boolean>(false) }
],
row2Upgrades: [
{ bought: persistent<boolean>(false) },
{ bought: persistent<boolean>(false) },
{ bought: persistent<boolean>(false) },
{ bought: persistent<boolean>(false) },
{ bought: persistent<boolean>(false) }
],
row1Buyables: [
{ amount: persistent<DecimalSource>(0) },
{ amount: persistent<DecimalSource>(0) },
{ amount: persistent<DecimalSource>(0) }
]
};
const mastered = persistent<boolean>(false);
const masteryEffectActive = computed(
() => mastered.value || main.currentlyMastering.value?.name === name
);
return { return {
name, name,
day,
color: colorBright, color: colorBright,
logs, logs,
totalLogs, totalLogs,
trees, trees,
saplings, saplings,
createdSaplings,
cutTree, cutTree,
plantTree, plantTree,
cutTreeHK, cutTreeHK,
@ -822,13 +892,25 @@ const layer = createLayer(id, function (this: BaseLayer) {
<> <>
{render(trackerDisplay)} {render(trackerDisplay)}
<Spacer /> <Spacer />
{masteryEffectActive.value ? (
<>
<div class="decoration-effect">
Decoration effect:
<br />
Trees drop 2 saplings, and forest size increases log gain
</div>
<Spacer />
</>
) : null}
<MainDisplay <MainDisplay
resource={logs} resource={logs}
color={colorBright} color={colorBright}
style="margin-bottom: 0" style="margin-bottom: 0"
productionDisplay={ productionDisplay={
Decimal.gt(computedAutoCuttingAmount.value, 0) Decimal.gt(computedAutoCuttingAmount.value, 0)
? `+${format(ema.value)}/s average<br/>equilibrium: +${formatLimit( ? `+${format(
averageLogGain.value
)}/s average<br/>equilibrium: +${formatLimit(
[ [
[computedAutoCuttingAmount.value, "cutting speed"], [computedAutoCuttingAmount.value, "cutting speed"],
[computedAutoPlantingAmount.value, "planting speed"], [computedAutoPlantingAmount.value, "planting speed"],
@ -864,9 +946,13 @@ const layer = createLayer(id, function (this: BaseLayer) {
minimizedDisplay: jsx(() => ( minimizedDisplay: jsx(() => (
<div> <div>
{name}{" "} {name}{" "}
<span class="desc">{format(logs.value)} {logs.displayName}</span> <span class="desc">
</div> {format(logs.value)} {logs.displayName}
</span>
</div>
)), )),
mastery,
mastered
}; };
}); });

View file

@ -2,6 +2,7 @@
* @module * @module
* @hidden * @hidden
*/ */
import HotkeyVue from "components/Hotkey.vue";
import Spacer from "components/layout/Spacer.vue"; import Spacer from "components/layout/Spacer.vue";
import { createCollapsibleMilestones } from "data/common"; import { createCollapsibleMilestones } from "data/common";
import { main } from "data/projEntry"; import { main } from "data/projEntry";
@ -23,8 +24,8 @@ import {
createMultiplicativeModifier, createMultiplicativeModifier,
createSequentialModifier createSequentialModifier
} from "game/modifiers"; } from "game/modifiers";
import { noPersist } from "game/persistence"; import { noPersist, persistent } from "game/persistence";
import Decimal, { DecimalSource, format, formatWhole } from "util/bignum"; import Decimal, { DecimalSource, formatWhole } from "util/bignum";
import { Direction } from "util/common"; import { Direction } from "util/common";
import { render } from "util/vue"; import { render } from "util/vue";
import { computed, unref, watchEffect } from "vue"; import { computed, unref, watchEffect } from "vue";
@ -32,6 +33,7 @@ import elves from "./elves";
import management from "./management"; import management from "./management";
import trees from "./trees"; import trees from "./trees";
import wrappingPaper from "./wrapping-paper"; import wrappingPaper from "./wrapping-paper";
import toys from "./toys";
const id = "workshop"; const id = "workshop";
const day = 2; const day = 2;
@ -47,7 +49,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
scaling: addHardcap( scaling: addHardcap(
addSoftcap(addSoftcap(createPolynomialScaling(250, 1.5), 5423, 1 / 1e10), 1e20, 3e8), addSoftcap(addSoftcap(createPolynomialScaling(250, 1.5), 5423, 1 / 1e10), 1e20, 3e8),
computed(() => computed(() =>
management.elfTraining.expandersElfTraining.milestones[2].earned.value ? 1000 : 100 toys.row1Upgrades[2].bought
? 1200
: management.elfTraining.expandersElfTraining.milestones[2].earned.value
? 1000
: 100
) )
), ),
baseResource: trees.logs, baseResource: trees.logs,
@ -55,18 +61,19 @@ const layer = createLayer(id, function (this: BaseLayer) {
roundUpCost: true, roundUpCost: true,
// buyMax: management.elfTraining.expandersElfTraining.milestones[2].earned, // buyMax: management.elfTraining.expandersElfTraining.milestones[2].earned,
spend(gain, spent) { spend(gain, spent) {
if (masteryEffectActive.value) return;
trees.logs.value = Decimal.sub(trees.logs.value, spent); trees.logs.value = Decimal.sub(trees.logs.value, spent);
}, },
costModifier: createSequentialModifier(() => [ costModifier: createSequentialModifier(() => [
createMultiplicativeModifier(() => ({ createMultiplicativeModifier(() => ({
multiplier: computed(() => wrappingPaper.boosts.beach1.value), multiplier: wrappingPaper.boosts.beach1,
description: "Beach Wrapping Paper", description: "Beach Wrapping Paper",
enabled: computed(() => Decimal.gt(wrappingPaper.boosts.beach1.value, 1)) enabled: computed(() => Decimal.gt(wrappingPaper.boosts.beach1.value, 1))
})), })),
createExponentialModifier(() => ({ createExponentialModifier(() => ({
exponent: 1 / 0.99, exponent: 1 / 0.99,
description: "Hope Level 5", description: "Holly Level 5",
enabled: management.elfTraining.expandersElfTraining.milestones[4].earned enabled: management.elfTraining.cutterElfTraining.milestones[4].earned
})) }))
]) ])
})); }));
@ -75,12 +82,13 @@ const layer = createLayer(id, function (this: BaseLayer) {
display: jsx(() => ( display: jsx(() => (
<> <>
<b style="font-size: x-large"> <b style="font-size: x-large">
Build {formatWhole(foundationConversion.actualGain.value)}% of the foundation Build {formatWhole(foundationConversion.actualGain.value)}% of the foundation{" "}
<HotkeyVue hotkey={buildFoundationHK} />
</b> </b>
<br /> <br />
<br /> <br />
<span style="font-size: large"> <span style="font-size: large">
Cost:{" "} {masteryEffectActive.value ? "Requirement" : "Cost"}:{" "}
{displayResource( {displayResource(
trees.logs, trees.logs,
Decimal.gte(foundationConversion.actualGain.value, 1) Decimal.gte(foundationConversion.actualGain.value, 1)
@ -95,17 +103,32 @@ const layer = createLayer(id, function (this: BaseLayer) {
showIf( showIf(
Decimal.lt( Decimal.lt(
foundationProgress.value, foundationProgress.value,
management.elfTraining.expandersElfTraining.milestones[2].earned.value toys.row1Upgrades[2].bought.value
? 1200
: management.elfTraining.expandersElfTraining.milestones[2].earned.value
? 1000 ? 1000
: 100 : 100
) )
), ),
canClick: () => canClick: () => {
Decimal.gte(trees.logs.value, foundationConversion.nextAt.value) && if (Decimal.lt(trees.logs.value, foundationConversion.nextAt.value)) {
Decimal.lt( return false;
foundationProgress.value, }
management.elfTraining.expandersElfTraining.milestones[2].earned.value ? 1000 : 100 if (main.isMastery.value && main.currentlyMastering.value?.name === "Trees") {
), return false;
}
let cap = 100;
if (management.elfTraining.expandersElfTraining.milestones[2].earned.value) {
cap = 1000;
}
if (toys.row1Upgrades[2].bought.value) {
cap = 1200;
}
if (Decimal.gte(foundationProgress.value, cap)) {
return false;
}
return true;
},
onClick() { onClick() {
if (!unref(this.canClick)) { if (!unref(this.canClick)) {
return; return;
@ -117,10 +140,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
const buildFoundationHK = createHotkey(() => ({ const buildFoundationHK = createHotkey(() => ({
key: "w", key: "w",
description: "Build part of the foundation.", description: "Build foundation",
onPress: () => { onPress: () => {
if (buildFoundation.canClick.value) buildFoundation.onClick(); if (buildFoundation.canClick.value) buildFoundation.onClick();
} },
enabled: main.days[day - 1].opened
})); }));
const shouldShowPopups = computed(() => !elves.milestones[6].earned.value); const shouldShowPopups = computed(() => !elves.milestones[6].earned.value);
@ -261,6 +285,16 @@ const layer = createLayer(id, function (this: BaseLayer) {
), ),
showPopups: shouldShowPopups showPopups: shouldShowPopups
})); }));
const extraExpansionMilestone6 = createMilestone(() => ({
display: {
requirement: "1200% Foundation Completed",
effectDisplay: "Quadruple oil gain"
},
shouldEarn: () => Decimal.gte(foundationProgress.value, 1200),
visibility: () =>
showIf(extraExpansionMilestone5.earned.value && toys.row1Upgrades[2].bought.value),
showPopups: shouldShowPopups
}));
const milestones = { const milestones = {
logGainMilestone1, logGainMilestone1,
autoCutMilestone1, autoCutMilestone1,
@ -274,7 +308,8 @@ const layer = createLayer(id, function (this: BaseLayer) {
extraExpansionMilestone2, extraExpansionMilestone2,
extraExpansionMilestone3, extraExpansionMilestone3,
extraExpansionMilestone4, extraExpansionMilestone4,
extraExpansionMilestone5 extraExpansionMilestone5,
extraExpansionMilestone6
}; };
const { collapseMilestones, display: milestonesDisplay } = const { collapseMilestones, display: milestonesDisplay } =
createCollapsibleMilestones(milestones); createCollapsibleMilestones(milestones);
@ -284,18 +319,53 @@ const layer = createLayer(id, function (this: BaseLayer) {
width: 600, width: 600,
height: 25, height: 25,
fillStyle: `backgroundColor: ${colorDark}`, fillStyle: `backgroundColor: ${colorDark}`,
progress: () => (main.day.value === day ? Decimal.div(foundationProgress.value, 100) : 1), progress: () =>
main.day.value === day || main.currentlyMastering.value?.name === name
? Decimal.div(foundationProgress.value, 100)
: 1,
display: jsx(() => display: jsx(() =>
main.day.value === day ? <>{formatWhole(foundationProgress.value)}%</> : "" main.day.value === day || main.currentlyMastering.value?.name === name ? (
<>{formatWhole(foundationProgress.value)}%</>
) : (
""
)
) )
})); }));
watchEffect(() => { watchEffect(() => {
if (main.day.value === day && Decimal.gte(foundationProgress.value, 100)) { if (main.day.value === day && Decimal.gte(foundationProgress.value, 100)) {
main.completeDay(); main.completeDay();
} else if (
main.currentlyMastering.value?.name === name &&
Decimal.gte(foundationProgress.value, 100)
) {
main.completeMastery();
} }
}); });
const mastery = {
foundationProgress: persistent<DecimalSource>(0),
milestones: {
logGainMilestone1: { earned: persistent<boolean>(false) },
autoCutMilestone1: { earned: persistent<boolean>(false) },
autoPlantMilestone1: { earned: persistent<boolean>(false) },
autoCutMilestone2: { earned: persistent<boolean>(false) },
autoPlantMilestone2: { earned: persistent<boolean>(false) },
logGainMilestone2: { earned: persistent<boolean>(false) },
morePlantsMilestone1: { earned: persistent<boolean>(false) },
logGainMilestone3: { earned: persistent<boolean>(false) },
extraExpansionMilestone1: { earned: persistent<boolean>(false) },
extraExpansionMilestone2: { earned: persistent<boolean>(false) },
extraExpansionMilestone3: { earned: persistent<boolean>(false) },
extraExpansionMilestone4: { earned: persistent<boolean>(false) },
extraExpansionMilestone5: { earned: persistent<boolean>(false) }
}
};
const mastered = persistent<boolean>(false);
const masteryEffectActive = computed(
() => mastered.value || main.currentlyMastering.value?.name === name
);
return { return {
name, name,
day, day,
@ -311,10 +381,22 @@ const layer = createLayer(id, function (this: BaseLayer) {
<div> <div>
{main.day.value === day {main.day.value === day
? `Complete the foundation to complete the day` ? `Complete the foundation to complete the day`
: main.currentlyMastering.value?.name === name
? `Complete the foundation to decorate the day`
: `${name} Complete!`} : `${name} Complete!`}
</div> </div>
{render(dayProgress)} {render(dayProgress)}
<Spacer /> <Spacer />
{masteryEffectActive.value ? (
<>
<div class="decoration-effect">
Decoration effect:
<br />
Logs are just a requirement instead of a cost
</div>
<Spacer />
</>
) : null}
<div> <div>
<span>The foundation is </span> <span>The foundation is </span>
<h2 style={`color: ${color}; text-shadow: 0 0 10px ${color}`}> <h2 style={`color: ${color}; text-shadow: 0 0 10px ${color}`}>
@ -334,9 +416,13 @@ const layer = createLayer(id, function (this: BaseLayer) {
minimizedDisplay: jsx(() => ( minimizedDisplay: jsx(() => (
<div> <div>
{name}{" "} {name}{" "}
<span class="desc">{formatWhole(foundationProgress.value)}% {foundationProgress.displayName}</span> <span class="desc">
{formatWhole(foundationProgress.value)}% {foundationProgress.displayName}
</span>
</div> </div>
)), )),
mastery,
mastered
}; };
}); });

View file

@ -1,19 +1,21 @@
import { BuyableOptions, createBuyable, GenericBuyable } from "features/buyable";
import { jsx, JSXFunction, showIf } from "features/feature";
import { createResource, Resource } from "features/resources/resource";
import { createLayer } from "game/layers";
import { noPersist, persistent } from "game/persistence";
import Decimal, { DecimalSource } from "util/bignum";
import MainDisplay from "features/resources/MainDisplay.vue";
import { render, renderRow } from "util/vue";
import { default as dyes, type enumColor } from "./dyes"
import { BaseTransition, computed, Ref, ref, unref } from "vue"
import Spacer from "components/layout/Spacer.vue"; import Spacer from "components/layout/Spacer.vue";
import { Computable } from "util/computed"; import { createBar, GenericBar } from "features/bars/bar";
import { format } from "util/bignum"; import { BuyableOptions, createBuyable, GenericBuyable } from "features/buyable";
import { createCollapsibleModifierSections, setUpDailyProgressTracker, createCollapsibleMilestones } from "data/common"; import { createClickable } from "features/clickables/clickable";
import Modal from "components/Modal.vue"; import { jsx, JSXFunction, showIf } from "features/feature";
import { createMilestone } from "features/milestones/milestone"; import { createMilestone } from "features/milestones/milestone";
import MainDisplay from "features/resources/MainDisplay.vue";
import { createResource, Resource } from "features/resources/resource";
import { createLayer, layers } from "game/layers";
import player from "game/player";
import Decimal, { DecimalSource, format, formatWhole } from "util/bignum";
import { Direction } from "util/common";
import { Computable } from "util/computed";
import { render, renderRow } from "util/vue";
import { computed, Ref, unref, watchEffect } from "vue";
import { main } from "../projEntry";
import { default as dyes, type enumColor } from "./dyes";
import elves from "./elves";
const id = "wrappingPaper"; const id = "wrappingPaper";
const day = 15; const day = 15;
@ -37,39 +39,42 @@ interface Scaling {
interface WrappingPaperOptions { interface WrappingPaperOptions {
ratio: { ratio: {
[key in enumColor]?: Scaling; [key in enumColor]?: Scaling;
}, };
name: string; name: string;
id: string; id: string;
background: string; background: string;
listedBoosts: { listedBoosts: {
desc: Ref<string>; desc: Ref<string>;
}[] }[];
} }
const layer = createLayer (id, () => { const layer = createLayer(id, () => {
const name = "Wrapping Paper"; const name = "Wrapping Paper";
const color = "white"; // todo: change const color = "gold";
const createWrappingPaper = (options: WrappingPaperOptions & Partial<BuyableOptions>) => { const createWrappingPaper = (options: WrappingPaperOptions & Partial<BuyableOptions>) => {
const getCost: Computable<{ const getCost: Computable<
resource: Resource; {
cost: DecimalSource; resource: Resource;
}[]> = computed(() => { cost: DecimalSource;
}[]
> = computed(() => {
const dyeCosts = []; const dyeCosts = [];
for (const [color, ratio] of Object.entries(options.ratio)) { for (const [color, ratio] of Object.entries(options.ratio)) {
dyeCosts.push({ dyeCosts.push({
resource: dyes.dyes[color as enumColor].amount, resource: dyes.dyes[color as enumColor].amount,
cost: Decimal.mul(ratio.base, Decimal.pow(ratio.exponent, buyable.amount.value)), cost: Decimal.mul(ratio.base, Decimal.pow(ratio.exponent, buyable.amount.value))
}); });
} }
return dyeCosts; return dyeCosts;
}) });
const buyable: GenericBuyable = createBuyable(() => { const buyable: GenericBuyable = createBuyable(() => {
return { return {
style: () => ({ style: () => ({
background: unref(buyable.canPurchase) ? options.background : "#545454", background: unref(buyable.canPurchase) ? options.background : "#545454",
minWidth: "200px", minWidth: "200px",
boxShadow: "0 3px 0 #00000022 inset, 3px 0 0 #00000022 inset, 0 0 3px #00000022 inset, 0 0 0 3px #00000022 inset", boxShadow:
"0 3px 0 #00000022 inset, 3px 0 0 #00000022 inset, 0 0 3px #00000022 inset, 0 0 0 3px #00000022 inset",
border: "none" border: "none"
}), }),
display: jsx(() => { display: jsx(() => {
@ -79,72 +84,75 @@ const layer = createLayer (id, () => {
<br /> <br />
Create {options.name}. Create {options.name}.
<br /> <br />
Requirement:{" "}{ Requirement:{" "}
getCost.value.map(({ resource, cost }) => { {getCost.value.map(({ resource, cost }) => {
return render(jsx(() => ( return render(
<div> jsx(() => (
<div
class={
Decimal.lt(resource.value, cost)
? "unaffordable"
: ""
}
>
{format(cost)} {resource.displayName} <br /> {format(cost)} {resource.displayName} <br />
</div> </div>
))) ))
}) );
} })}
<br /> <br />
Currently:{" "} Currently:{" "}
{ {options.listedBoosts.map(({ desc }) => {
options.listedBoosts.map(({desc}) => { return render(jsx(() => <div>{unref(desc)}</div>));
return render(jsx(() => ( })}
<div>
{unref(desc)}
</div>
)))
})
}
</span> </span>
) );
}), }),
canPurchase () { canPurchase() {
for (let {resource, cost} of getCost.value) { for (const { resource, cost } of getCost.value) {
if (Decimal.lt(resource.value, cost)) return false; if (Decimal.lt(resource.value, cost)) return false;
} }
return true; return true;
}, },
onPurchase () { onPurchase() {
buyable.amount.value = Decimal.add(buyable.amount.value, 1); buyable.amount.value = Decimal.add(buyable.amount.value, 1);
// todo: stuff // todo: stuff
} }
} };
}) });
const resource = createResource(buyable.amount, options.name) const resource = createResource(buyable.amount, options.name);
return { return {
resource, resource,
buyable, buyable,
name: options.name, name: options.name,
display: jsx(() => { display: jsx(() => {
return ( return (
<MainDisplay <MainDisplay
resource={resource} resource={resource}
style="margin: 0; width: 200px; width: 180px; padding: 10px;" style="margin: 0; width: 200px; width: 180px; padding: 10px;"
sticky={false} sticky={false}
/> />
);
)
}) })
} };
} };
const wrappingPaper: Record<string, WrappingPaper> = { const wrappingPaper: Record<string, WrappingPaper> = {
christmas: createWrappingPaper({ christmas: createWrappingPaper({
name: "Christmas Wrapping Paper", name: "Christmas Wrapping Paper",
id: "christmas", id: "christmas",
ratio: { ratio: {
red: {base: basePrimaryCost * 3, exponent: basePrimaryRatio}, red: { base: basePrimaryCost * 3, exponent: basePrimaryRatio },
green: {base: baseSecondaryCost * 3, exponent: baseSecondaryRatio}, green: { base: baseSecondaryCost * 3, exponent: baseSecondaryRatio }
}, },
background: "linear-gradient(225deg, rgba(255,76,76,1) 10.8%, rgba(255,255,255,1) 11.1%, rgba(255,255,255,1) 21.9%, rgba(65,255,95,1) 22.2%, rgba(65,255,95,1) 33.0%, rgba(255,255,255,1) 33.3%, rgba(255,255,255,1) 44.1%, rgba(255,76,76,1) 44.4%, rgba(255,76,76,1) 55.2%, rgba(255,255,255,1) 55.5%, rgba(255,255,255,1) 66.3%, rgba(65,255,95,1) 66.6%, rgba(65,255,95,1) 77.4%, rgba(255,255,255,1) 77.7%, rgba(255,255,255,1) 88.5%, rgba(255,76,76,1) 88.8%)", background:
"linear-gradient(225deg, rgba(255,76,76,1) 10.8%, rgba(255,255,255,1) 11.1%, rgba(255,255,255,1) 21.9%, rgba(65,255,95,1) 22.2%, rgba(65,255,95,1) 33.0%, rgba(255,255,255,1) 33.3%, rgba(255,255,255,1) 44.1%, rgba(255,76,76,1) 44.4%, rgba(255,76,76,1) 55.2%, rgba(255,255,255,1) 55.5%, rgba(255,255,255,1) 66.3%, rgba(65,255,95,1) 66.6%, rgba(65,255,95,1) 77.4%, rgba(255,255,255,1) 77.7%, rgba(255,255,255,1) 88.5%, rgba(255,76,76,1) 88.8%)",
listedBoosts: [ listedBoosts: [
{ {
desc: computed(() => ` desc: computed(
() => `
x${format(unref(boosts.christmas1))} to wood production x${format(unref(boosts.christmas1))} to wood production
`) `
)
} }
] ]
}), }),
@ -152,19 +160,22 @@ const layer = createLayer (id, () => {
name: "Rainbow Wrapping Paper", name: "Rainbow Wrapping Paper",
id: "rainbow", id: "rainbow",
ratio: { ratio: {
red: {base: basePrimaryCost, exponent: basePrimaryRatio + 0.2}, red: { base: basePrimaryCost, exponent: basePrimaryRatio + 0.2 },
green: {base: baseSecondaryCost, exponent: baseSecondaryRatio + 0.1}, green: { base: baseSecondaryCost, exponent: baseSecondaryRatio + 0.1 },
blue: {base: basePrimaryCost, exponent: basePrimaryRatio + 0.2}, blue: { base: basePrimaryCost, exponent: basePrimaryRatio + 0.2 },
yellow: {base: basePrimaryCost, exponent: basePrimaryRatio + 0.2}, yellow: { base: basePrimaryCost, exponent: basePrimaryRatio + 0.2 },
purple: {base: baseSecondaryCost, exponent: baseSecondaryRatio + 0.1}, purple: { base: baseSecondaryCost, exponent: baseSecondaryRatio + 0.1 },
orange: {base: baseSecondaryCost, exponent: baseSecondaryRatio + 0.1}, orange: { base: baseSecondaryCost, exponent: baseSecondaryRatio + 0.1 }
}, },
background: "linear-gradient(135deg, rgba(255,0,0,1) 0%, rgba(255,0,0,1) 2%, rgba(255,155,0,1) 14%, rgba(255,155,0,1) 18%, rgba(255,254,0,1) 31%, rgba(255,254,0,1) 35%, rgba(100,244,61,1) 48%, rgba(100,244,61,1) 52%, rgba(70,218,234,1) 64%, rgba(70,218,234,1) 68%, rgba(205,0,210,1) 81%, rgba(205,0,210,1) 85%, rgba(255,0,0,1) 98%, rgba(255,0,0,1) 100%)", background:
"linear-gradient(135deg, rgba(255,0,0,1) 0%, rgba(255,0,0,1) 2%, rgba(255,155,0,1) 14%, rgba(255,155,0,1) 18%, rgba(255,254,0,1) 31%, rgba(255,254,0,1) 35%, rgba(100,244,61,1) 48%, rgba(100,244,61,1) 52%, rgba(70,218,234,1) 64%, rgba(70,218,234,1) 68%, rgba(205,0,210,1) 81%, rgba(205,0,210,1) 85%, rgba(255,0,0,1) 98%, rgba(255,0,0,1) 100%)",
listedBoosts: [ listedBoosts: [
{ {
desc: computed(() => ` desc: computed(
() => `
/${format(unref(boosts.rainbow1))} to coal buyable cost /${format(unref(boosts.rainbow1))} to coal buyable cost
`) `
)
} }
] ]
}), }),
@ -172,15 +183,18 @@ const layer = createLayer (id, () => {
name: "Jazzy Wrapping Paper", name: "Jazzy Wrapping Paper",
id: "jazzy", id: "jazzy",
ratio: { ratio: {
purple: {base: baseSecondaryCost * 3, exponent: baseSecondaryRatio}, purple: { base: baseSecondaryCost * 3, exponent: baseSecondaryRatio },
orange: {base: baseSecondaryCost * 3, exponent: baseSecondaryRatio}, orange: { base: baseSecondaryCost * 3, exponent: baseSecondaryRatio }
}, },
background: "linear-gradient(90deg, rgba(255,177,0,1) 10.8%, rgba(189,69,255,1) 11.1%, rgba(189,69,255,1) 21.9%, rgba(255,177,0,1) 22.2%, rgba(255,177,0,1) 33.0%, rgba(189,69,255,1) 33.3%, rgba(189,69,255,1) 44.1%, rgba(255,177,0,1) 44.4%, rgba(255,177,0,1) 55.2%, rgba(189,69,255,1) 55.5%, rgba(189,69,255,1) 66.3%, rgba(255,177,0,1) 66.6%, rgba(255,177,0,1) 77.4%, rgba(189,69,255,1) 77.7%, rgba(189,69,255,1) 88.5%, rgba(255,177,0,1) 88.8%)", background:
"linear-gradient(90deg, rgba(255,177,0,1) 10.8%, rgba(189,69,255,1) 11.1%, rgba(189,69,255,1) 21.9%, rgba(255,177,0,1) 22.2%, rgba(255,177,0,1) 33.0%, rgba(189,69,255,1) 33.3%, rgba(189,69,255,1) 44.1%, rgba(255,177,0,1) 44.4%, rgba(255,177,0,1) 55.2%, rgba(189,69,255,1) 55.5%, rgba(189,69,255,1) 66.3%, rgba(255,177,0,1) 66.6%, rgba(255,177,0,1) 77.4%, rgba(189,69,255,1) 77.7%, rgba(189,69,255,1) 88.5%, rgba(255,177,0,1) 88.8%)",
listedBoosts: [ listedBoosts: [
{ {
desc: computed(() => ` desc: computed(
-${format(unref(boosts.jazzy1))} to elf cost scaling () => `
`) x${format(unref(boosts.jazzy1))} to auto-smelting speed
`
)
} }
] ]
}), }),
@ -188,16 +202,19 @@ const layer = createLayer (id, () => {
name: "Sunshine Wrapping Paper", name: "Sunshine Wrapping Paper",
id: "sunshine", id: "sunshine",
ratio: { ratio: {
red: {base: basePrimaryCost * 2, exponent: basePrimaryRatio + .1}, red: { base: basePrimaryCost * 2, exponent: basePrimaryRatio + 0.1 },
yellow: {base: basePrimaryCost * 2, exponent: basePrimaryRatio + .1}, yellow: { base: basePrimaryCost * 2, exponent: basePrimaryRatio + 0.1 },
orange: {base: baseSecondaryCost * 2, exponent: baseSecondaryRatio + .05}, orange: { base: baseSecondaryCost * 2, exponent: baseSecondaryRatio + 0.05 }
}, },
background: "radial-gradient(circle, rgba(238,250,0,1) 16%, rgba(250,157,0,1) 50%, rgba(255,76,76,1) 83%)", background:
"radial-gradient(circle, rgba(238,250,0,1) 16%, rgba(250,157,0,1) 50%, rgba(255,76,76,1) 83%)",
listedBoosts: [ listedBoosts: [
{ {
desc: computed(() => ` desc: computed(
() => `
x${format(unref(boosts.sunshine1))} to paper production x${format(unref(boosts.sunshine1))} to paper production
`) `
)
} }
] ]
}), }),
@ -205,16 +222,19 @@ const layer = createLayer (id, () => {
name: "Ocean Wrapping Paper", name: "Ocean Wrapping Paper",
id: "ocean", id: "ocean",
ratio: { ratio: {
blue: {base: basePrimaryCost * 2, exponent: basePrimaryRatio + .1}, blue: { base: basePrimaryCost * 2, exponent: basePrimaryRatio + 0.1 },
green: {base: baseSecondaryCost * 2, exponent: baseSecondaryRatio + .05}, green: { base: baseSecondaryCost * 2, exponent: baseSecondaryRatio + 0.05 },
purple: {base: baseSecondaryCost * 2, exponent: baseSecondaryRatio + .05}, purple: { base: baseSecondaryCost * 2, exponent: baseSecondaryRatio + 0.05 }
}, },
background: "linear-gradient(20deg, rgba(0,183,250,0.6) 8%, rgba(0,223,62,0.6) 12%, rgba(0,183,250,0.6) 17%, rgba(0,183,250,0.6) 27%, rgba(124,109,230,0.6) 38%, rgba(0,183,250,0.6) 46%, rgba(0,183,250,0.6) 50%, rgba(0,223,62,0.6) 53%, rgba(0,183,250,0.6) 60%, rgba(124,109,230,0.6) 67%, rgba(0,183,250,0.6) 73%, rgba(0,183,250,0.6) 84%, rgba(0,223,62,0.6) 88%, rgba(0,183,250,0.6) 91%), linear-gradient(340deg, rgba(0,183,250,0.6) 8%, rgba(0,223,62,0.6) 12%, rgba(0,183,250,0.6) 17%, rgba(0,183,250,0.6) 27%, rgba(124,109,230,0.6) 38%, rgba(0,183,250,0.6) 46%, rgba(0,183,250,0.6) 50%, rgba(0,223,62,0.6) 53%, rgba(0,183,250,0.6) 60%, rgba(124,109,230,0.6) 67%, rgba(0,183,250,0.6) 73%, rgba(0,183,250,0.6) 84%, rgba(0,223,62,0.6) 88%, rgba(0,183,250,0.6) 91%)", background:
"linear-gradient(20deg, rgba(0,183,250,0.6) 8%, rgba(0,223,62,0.6) 12%, rgba(0,183,250,0.6) 17%, rgba(0,183,250,0.6) 27%, rgba(124,109,230,0.6) 38%, rgba(0,183,250,0.6) 46%, rgba(0,183,250,0.6) 50%, rgba(0,223,62,0.6) 53%, rgba(0,183,250,0.6) 60%, rgba(124,109,230,0.6) 67%, rgba(0,183,250,0.6) 73%, rgba(0,183,250,0.6) 84%, rgba(0,223,62,0.6) 88%, rgba(0,183,250,0.6) 91%), linear-gradient(340deg, rgba(0,183,250,0.6) 8%, rgba(0,223,62,0.6) 12%, rgba(0,183,250,0.6) 17%, rgba(0,183,250,0.6) 27%, rgba(124,109,230,0.6) 38%, rgba(0,183,250,0.6) 46%, rgba(0,183,250,0.6) 50%, rgba(0,223,62,0.6) 53%, rgba(0,183,250,0.6) 60%, rgba(124,109,230,0.6) 67%, rgba(0,183,250,0.6) 73%, rgba(0,183,250,0.6) 84%, rgba(0,223,62,0.6) 88%, rgba(0,183,250,0.6) 91%)",
listedBoosts: [ listedBoosts: [
{ {
desc: computed(() => ` desc: computed(
() => `
/${format(unref(boosts.ocean1))} to box buyable cost /${format(unref(boosts.ocean1))} to box buyable cost
`) `
)
} }
] ]
}), }),
@ -222,158 +242,203 @@ const layer = createLayer (id, () => {
name: "Beach Wrapping Paper", name: "Beach Wrapping Paper",
id: "beach", id: "beach",
ratio: { ratio: {
yellow: {base: basePrimaryCost * 3, exponent: basePrimaryRatio}, yellow: { base: basePrimaryCost * 3, exponent: basePrimaryRatio },
blue: {base: basePrimaryCost * 3, exponent: basePrimaryRatio}, blue: { base: basePrimaryCost * 3, exponent: basePrimaryRatio }
}, },
background: "radial-gradient(circle at 80% 10%, rgba(255,255,76,1) 8%, rgba(0,0,0,0) 21%), linear-gradient(180deg, rgba(0,255,246,1) 60%, rgba(0,255,246,0) 61%), linear-gradient(215deg, rgba(0,93,255,0) 0%, rgba(0,93,255,0) 66%, rgba(255,255,76,1) 68%), linear-gradient(180deg, rgba(0,0,0,0) 68%, rgba(0,93,255,1) 70%), linear-gradient(205deg, rgba(0,255,246,1) 0%, rgba(0,255,246,1) 100%)", background:
"radial-gradient(circle at 80% 10%, rgba(255,255,76,1) 8%, rgba(0,0,0,0) 21%), linear-gradient(180deg, rgba(0,255,246,1) 60%, rgba(0,255,246,0) 61%), linear-gradient(215deg, rgba(0,93,255,0) 0%, rgba(0,93,255,0) 66%, rgba(255,255,76,1) 68%), linear-gradient(180deg, rgba(0,0,0,0) 68%, rgba(0,93,255,1) 70%), linear-gradient(205deg, rgba(0,255,246,1) 0%, rgba(0,255,246,1) 100%)",
listedBoosts: [ listedBoosts: [
{ {
desc: computed(() => ` desc: computed(
() => `
/${format(unref(boosts.beach1))} to workshop cost /${format(unref(boosts.beach1))} to workshop cost
`) `
)
} }
] ]
}) })
} };
const boosts = { const boosts = {
christmas1: computed(() => Decimal.add(wrappingPaper.christmas.buyable.amount.value, 1)), // Probably not the best way to do this, but it works christmas1: computed(() =>
rainbow1: computed(() => main.isMastery.value ? 1 : Decimal.add(wrappingPaper.christmas.buyable.amount.value, 1)
Decimal.pow(2, wrappingPaper.rainbow.buyable.amount.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)
), ),
jazzy1: computed(() => jazzy1: computed(() =>
Decimal.ln( main.isMastery.value ? 1 : Decimal.add(wrappingPaper.jazzy.buyable.amount.value, 1)
Decimal.add( ),
Decimal.ln(Decimal.add(wrappingPaper.jazzy.buyable.amount.value, 1)), sunshine1: computed(() =>
1 main.isMastery.value ? 1 : Decimal.add(wrappingPaper.sunshine.buyable.amount.value, 1)
)
)
), ),
sunshine1: computed(() => Decimal.add(wrappingPaper.sunshine.buyable.amount.value, 1)),
ocean1: computed(() => ocean1: computed(() =>
Decimal.pow(1.5, wrappingPaper.ocean.buyable.amount.value) main.isMastery.value ? 1 : Decimal.pow(1.5, wrappingPaper.ocean.buyable.amount.value)
), ),
beach1: computed(() => Decimal.add(wrappingPaper.beach.buyable.amount.value, 1)), beach1: computed(() =>
} main.isMastery.value
const wrappingPaperSum = createResource(computed(() => Object.values(wrappingPaper).map(paper => paper.buyable.amount.value).reduce(Decimal.add, 0)), "Total Wrapping Paper") ? 1
const showModifiersModal = ref(false); : Decimal.add(wrappingPaper.beach.buyable.amount.value, 1).log10().add(1)
const modifiersModal = jsx(() => ( )
<Modal };
modelValue={showModifiersModal.value} const wrappingPaperSum = createResource(
onUpdate:modelValue={(value: boolean) => (showModifiersModal.value = value)} computed(() =>
v-slots={{ Object.values(wrappingPaper)
header: () => <h2>{name} Modifiers</h2>, .map(paper => paper.buyable.amount.value)
body: generalTab .reduce(Decimal.add, 0)
}} ),
/> "Total Wrapping Paper"
)); );
const [generalTab, generalTabCollapsed] = createCollapsibleModifierSections(() => [
]);
const { total: totalWrappingPaper, trackerDisplay } = setUpDailyProgressTracker({
resource: wrappingPaperSum,
goal: 1e20,
name,
day,
color,
textColor: "var(--feature-foreground)",
modal: {
show: showModifiersModal,
display: modifiersModal
},
ignoreTotal: true
});
const milestoneCosts = [6, 12, 18, 24, 30, 36] // change
const primaryBoostMilestone = createMilestone(() => ({
display: {
requirement: milestoneCosts[0] + " Total Wrapping Paper",
effectDisplay: "Double primary colour dye gain"
},
shouldEarn: () => Decimal.gte(totalWrappingPaper.value, milestoneCosts[0]),
visibility: () => showIf(true)
}));
const secondaryBoostMilestone = createMilestone(() => ({
display: {
requirement: milestoneCosts[1] + " Total Wrapping Paper",
effectDisplay: "Double secondary colour dye gain"
},
shouldEarn: () => Decimal.gte(totalWrappingPaper.value, milestoneCosts[1]),
visibility: () => showIf(primaryBoostMilestone.earned.value)
}));
const buyMaxPrimaryMilestone = createMilestone(() => ({
display: {
requirement: milestoneCosts[2] + " Total Wrapping Paper",
effectDisplay: "Buy maximum primary colour dyes"
},
shouldEarn: () => Decimal.gte(totalWrappingPaper.value, milestoneCosts[2]),
visibility: () => showIf(secondaryBoostMilestone.earned.value)
}));
const secondaryNoResetMilestone = createMilestone(() => ({
display: {
requirement: milestoneCosts[3] + " Total Wrapping Paper",
effectDisplay: "Secondary colour dyes don't spend primary colour dyes"
},
shouldEarn: () => Decimal.gte(totalWrappingPaper.value, milestoneCosts[3]),
visibility: () => showIf(buyMaxPrimaryMilestone.earned.value)
}));
const buyMaxSecondaryMilestone = createMilestone(() => ({
display: {
requirement: milestoneCosts[4] + " Total Wrapping Paper",
effectDisplay: "Buy maximum secondary colour dyes"
},
shouldEarn: () => Decimal.gte(totalWrappingPaper.value, milestoneCosts[4]),
visibility: () => showIf(secondaryNoResetMilestone.earned.value)
}));
const unlockDyeElfMilestone = createMilestone(() => ({ const unlockDyeElfMilestone = createMilestone(() => ({
display: { display: {
requirement: milestoneCosts[5] + " Total Wrapping Paper", requirement: "80 Total Wrapping Paper",
effectDisplay: "Unlock a new elf to help with dyes" effectDisplay: "Unlock a new elf to help with dyes"
}, },
shouldEarn: () => Decimal.gte(totalWrappingPaper.value, milestoneCosts[5]), shouldEarn: () => Decimal.gte(wrappingPaperSum.value, 80),
visibility: () => showIf(buyMaxSecondaryMilestone.earned.value) onComplete() {
main.days[3].recentlyUpdated.value = true;
}
})); }));
const milestones = { const masteryReq = computed(() =>
primaryBoost: primaryBoostMilestone, Decimal.add(main.masteredDays.value, 1).times(20).add(140).ceil()
secondaryBoost: secondaryBoostMilestone, );
buyMaxPrimary: buyMaxPrimaryMilestone,
secondaryNoReset: secondaryNoResetMilestone,
buyMaxSecondary: buyMaxSecondaryMilestone,
unlockDyeElf: unlockDyeElfMilestone
}
const { collapseMilestones, display: milestonesDisplay } = const enterMasteryButton = createClickable(() => ({
createCollapsibleMilestones(milestones); display: () => ({
title: `${main.isMastery.value ? "Stop Decorating" : "Begin Decorating"} ${
Object.values(layers).find(
layer =>
unref((layer as any).mastered) === false &&
!["Elves", "Management"].includes(unref(layer?.name ?? ""))
)?.name
}`,
description: jsx(() => {
return (
<>
<br />
Decorating brings you to a separate version of each day that only allows
layers that are decorated or being decorated to work. These days will have a
new decoration effect that applies outside of decorating as well.
<br />
You can safely start and stop decorating without losing progress
{main.isMastery.value ? null : (
<>
<br />
<br />
Requires {formatWhole(masteryReq.value)} total wrapping paper
</>
)}
</>
);
})
}),
visibility: () => showIf(main.day.value === day),
canClick() {
return main.isMastery.value || Decimal.gte(wrappingPaperSum.value, masteryReq.value);
},
onClick() {
if (!unref(enterMasteryButton.canClick)) {
return;
}
main.toggleMastery();
const layer = main.currentlyMastering.value?.id ?? "trees";
if (!player.tabs.includes(layer)) {
main.openDay(layer);
}
if (layer === "paper") {
// Purchase first 6 elves
elves.elves.cuttersElf.bought.value = true;
elves.elves.plantersElf.bought.value = true;
elves.elves.expandersElf.bought.value = true;
elves.elves.heatedCuttersElf.bought.value = true;
elves.elves.heatedPlantersElf.bought.value = true;
elves.elves.fertilizerElf.bought.value = true;
}
},
style: {
width: "300px",
minHeight: "160px"
}
}));
const dayProgress = createBar(() => ({
direction: Direction.Right,
width: 600,
height: 25,
fillStyle: `animation: 15s wrapping-paper-bar linear infinite`,
textStyle: `color: var(--feature-foreground)`,
progress: () => (main.day.value === day ? Decimal.div(main.masteredDays.value, 6) : 1),
display: jsx(() =>
main.day.value === day ? (
<>
{main.masteredDays.value}
/6 days decorated
</>
) : (
""
)
)
})) as GenericBar;
watchEffect(() => {
if (
main.day.value === day &&
Decimal.gte(main.masteredDays.value, 6) &&
main.showLoreModal.value === false
) {
main.completeDay();
}
});
return { return {
name, name,
day,
color,
display: jsx(() => { display: jsx(() => {
return ( return (
<div style="width: 620px"> <div style="width: 620px">
{render(trackerDisplay)} <div>
{main.day.value === day
? `Decorate 6 previous days to complete the day`
: `${name} Complete!`}
</div>
{render(dayProgress)}
<Spacer />
<MainDisplay resource={wrappingPaperSum} /> <MainDisplay resource={wrappingPaperSum} />
{renderRow(wrappingPaper.christmas.display, wrappingPaper.rainbow.display, wrappingPaper.jazzy.display)} {renderRow(
{renderRow(wrappingPaper.christmas.buyable, wrappingPaper.rainbow.buyable, wrappingPaper.jazzy.buyable)} wrappingPaper.christmas.display,
wrappingPaper.rainbow.display,
wrappingPaper.jazzy.display
)}
{renderRow(
wrappingPaper.christmas.buyable,
wrappingPaper.rainbow.buyable,
wrappingPaper.jazzy.buyable
)}
<Spacer /> <Spacer />
{renderRow(wrappingPaper.sunshine.display, wrappingPaper.ocean.display, wrappingPaper.beach.display)} {renderRow(
{renderRow(wrappingPaper.sunshine.buyable, wrappingPaper.ocean.buyable, wrappingPaper.beach.buyable)} wrappingPaper.sunshine.display,
wrappingPaper.ocean.display,
wrappingPaper.beach.display
)}
{renderRow(
wrappingPaper.sunshine.buyable,
wrappingPaper.ocean.buyable,
wrappingPaper.beach.buyable
)}
<Spacer /> <Spacer />
button goes here {render(enterMasteryButton)}
<Spacer /> <Spacer />
{milestonesDisplay()} {render(unlockDyeElfMilestone)}
</div> </div>
) );
}), }),
wrappingPaper, wrappingPaper,
totalWrappingPaper,
generalTabCollapsed,
boosts, boosts,
milestones, unlockDyeElfMilestone,
collapseMilestones minWidth: 700
} };
}) });
export default layer; export default layer;

View file

@ -7,7 +7,7 @@ import {
jsx jsx
} from "features/feature"; } from "features/feature";
import { BaseLayer, createLayer, GenericLayer, layers } from "game/layers"; import { BaseLayer, createLayer, GenericLayer, layers } from "game/layers";
import { persistent } from "game/persistence"; import { isPersistent, persistent } from "game/persistence";
import type { LayerData, PlayerData } from "game/player"; import type { LayerData, PlayerData } from "game/player";
import player from "game/player"; import player from "game/player";
import Decimal, { format, formatTime } from "util/bignum"; import Decimal, { format, formatTime } from "util/bignum";
@ -30,6 +30,8 @@ import metal from "./layers/metal";
import oil from "./layers/oil"; import oil from "./layers/oil";
import paper from "./layers/paper"; import paper from "./layers/paper";
import plastic from "./layers/plastic"; import plastic from "./layers/plastic";
import ribbon from "./layers/ribbon";
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 factory from "./layers/factory"; import factory from "./layers/factory";
@ -45,6 +47,7 @@ import metalSymbol from "./symbols/metal.png";
import oilSymbol from "./symbols/oil.png"; import oilSymbol from "./symbols/oil.png";
import paperSymbol from "./symbols/paperStacks.png"; import paperSymbol from "./symbols/paperStacks.png";
import plasticSymbol from "./symbols/plastic.png"; import plasticSymbol from "./symbols/plastic.png";
import ribbonsSymbol from "./symbols/ribbons.png";
import workshopSymbol from "./symbols/sws.png"; import workshopSymbol from "./symbols/sws.png";
import treeSymbol from "./symbols/tree.png"; import treeSymbol from "./symbols/tree.png";
import advManagementSymbol from "./symbols/workshopMansion.png"; import advManagementSymbol from "./symbols/workshopMansion.png";
@ -56,6 +59,7 @@ export interface Day extends VueFeature {
symbol: string; symbol: string;
story: string; story: string;
completedStory: string; completedStory: string;
masteredStory: string;
opened: Ref<boolean>; opened: Ref<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>;
@ -72,6 +76,79 @@ 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 currentlyMastering = computed(() =>
isMastery.value
? Object.values(layers).find(
layer =>
unref((layer as any).mastered) === false &&
!["Elves", "Management"].includes(unref(layer?.name ?? ""))
)
: undefined
);
const swappingMastery = ref(false);
const isMastery = persistent<boolean>(false);
const toggleMastery = () => {
swappingMastery.value = true;
isMastery.value = !isMastery.value;
for (const layer of [
trees,
workshop,
coal,
elves,
paper,
boxes,
metal,
cloth,
oil,
plastic,
dyes,
management,
letters
]) {
swapMastery(layer.mastery, layer);
}
swappingMastery.value = false;
};
function swapMastery(mastery: Record<string, any>, layer: Record<string, any>) {
for (const key of Object.keys(mastery)) {
if (isPersistent(mastery[key])) {
[mastery[key].value, layer[key].value] = [layer[key].value, mastery[key].value];
} else {
swapMastery(mastery[key], layer[key]);
}
}
}
const masteredDays = computed(() => {
let index = Object.values(layers)
.filter(l => l && "mastered" in l)
.findIndex(l => (l as any).mastered.value === false);
if (index === -1) {
index = Object.values(layers).filter(l => l && "mastered" in l).length;
}
return index;
});
function openDay(layer: string) {
// 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) {
// Desktop, allow multiple tabs to be open
if (player.tabs.includes(layer)) {
const index = player.tabs.lastIndexOf(layer);
player.tabs.splice(index, 1);
} else {
player.tabs.push(layer);
main.minimized.value = true;
}
} else {
// Mobile, use single tab mode
player.tabs.splice(1, Infinity, layer);
}
layers[layer]!.minimized.value = false;
}
function createDay( function createDay(
optionsFunc: () => { optionsFunc: () => {
day: number; day: number;
@ -80,6 +157,7 @@ export const main = createLayer("main", function (this: BaseLayer) {
symbol: string; symbol: string;
story: string; story: string;
completedStory: string; completedStory: string;
masteredStory: string;
} }
): Day { ): Day {
const opened = persistent<boolean>(false); const opened = persistent<boolean>(false);
@ -108,9 +186,12 @@ export const main = createLayer("main", function (this: BaseLayer) {
shouldNotify, shouldNotify,
story, story,
completedStory, completedStory,
masteredStory,
recentlyUpdated recentlyUpdated
} = this; } = this;
const mastered: Ref<boolean> =
(layers[layer ?? ""] as any)?.mastered ?? ref(false);
return { return {
day, day,
symbol, symbol,
@ -118,13 +199,22 @@ export const main = createLayer("main", function (this: BaseLayer) {
opened, opened,
recentlyUpdated, recentlyUpdated,
shouldNotify, shouldNotify,
mastered,
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;
const title = unref(layers[layer ?? "trees"]?.name ?? ""); const title = unref(layers[layer ?? "trees"]?.name ?? "");
loreTitle.value = completed ? `${title} - Completed!` : title; loreTitle.value = mastered.value
? `${title} - Decorated!`
: completed
? `${title} - Completed!`
: title;
loreBody.value = completed loreBody.value = completed
? `${story}<hr style=" ? unref(mastered)
? `${story}<hr style="
margin: 10px 0;"/>${completedStory}<hr style="
margin: 10px 0;"/>${masteredStory}`
: `${story}<hr style="
margin: 10px 0;"/>${completedStory}` margin: 10px 0;"/>${completedStory}`
: story; : story;
showLoreModal.value = true; showLoreModal.value = true;
@ -150,7 +240,7 @@ export const main = createLayer("main", function (this: BaseLayer) {
} }
layers[layer ?? "trees"]!.minimized.value = false; layers[layer ?? "trees"]!.minimized.value = false;
} else { } else {
player.tabs.splice(0, Infinity, layer) player.tabs.splice(0, Infinity, layer);
} }
}, },
onUnlockLayer() { onUnlockLayer() {
@ -179,7 +269,9 @@ export const main = createLayer("main", function (this: BaseLayer) {
symbol: treeSymbol, symbol: treeSymbol,
story: "Oh no! Santa forgot about Christmas and it's only 25 days away! He's asked for your help due to your history getting large quantities of things in short amounts of time. Unfortunately you're really starting from scratch here - let's start with getting wood, which you'll need for everything from building workshops to wrapping paper to many of the toys themselves!", story: "Oh no! Santa forgot about Christmas and it's only 25 days away! He's asked for your help due to your history getting large quantities of things in short amounts of time. Unfortunately you're really starting from scratch here - let's start with getting wood, which you'll need for everything from building workshops to wrapping paper to many of the toys themselves!",
completedStory: completedStory:
"Santa looks at all the wood you've gathered and tells you you've done well! He says you should take the rest of the day off so you're refreshed for tomorrow's work. Good Job!" "Santa looks at all the wood you've gathered and tells you you've done well! He says you should take the rest of the day off so you're refreshed for tomorrow's work. Good Job!",
masteredStory:
"As you repeat the basic actions again, you feel like you've learned something that you didn't know the first time around. Santa is impressed at your new knowledge and inspires you to attempt this with more jobs. Great Job!"
})), })),
createDay(() => ({ createDay(() => ({
day: 2, day: 2,
@ -188,7 +280,9 @@ export const main = createLayer("main", function (this: BaseLayer) {
symbol: workshopSymbol, symbol: workshopSymbol,
story: "Santa looked over your tree farm and was impressed with how much you could accomplish in just one day. Today's goal is to get a workshop built up for the elves to work in - and apparently, they need quite a lot of space to work!", story: "Santa looked over your tree farm and was impressed with how much you could accomplish in just one day. Today's goal is to get a workshop built up for the elves to work in - and apparently, they need quite a lot of space to work!",
completedStory: completedStory:
"The workshop complete, Santa once again dismisses you for the day. With a strong foundation, this workshop should suffice for supporting future work toward this impossible mission. Good Job!" "The workshop complete, Santa once again dismisses you for the day. With a strong foundation, this workshop should suffice for supporting future work toward this impossible mission. Good Job!",
masteredStory:
"As you attempt to build the workshop again with your newfound experiences and resources, you realize you could have built the workshop a little bit better. As you keep building and building, you realize that you could've built it without wasting any resources. Great Job!"
})), })),
createDay(() => ({ createDay(() => ({
day: 3, day: 3,
@ -197,7 +291,9 @@ export const main = createLayer("main", function (this: BaseLayer) {
symbol: coalSymbol, symbol: coalSymbol,
story: "Santa tells you that unfortunately there are quite a few naughty children out there this year, and he's going to need you to gather as much coal as you can for him to give out.", story: "Santa tells you that unfortunately there are quite a few naughty children out there this year, and he's going to need you to gather as much coal as you can for him to give out.",
completedStory: completedStory:
"Santa looks at all the coal you've gathered and tells you you've done well! He says you should take the rest of the day off so you're refreshed for tomorrow's work. Good Job!" "Santa looks at all the coal you've gathered and tells you you've done well! He says you should take the rest of the day off so you're refreshed for tomorrow's work. Good Job!",
masteredStory:
"It's another typical day, attempting to redo your work again, but this time for coal. While doing this tedious task, an elf comes up to you. It gives you a improved blueprint on how to make small fires. You try it, and you realize that it's a lot more efficent than your old buildings designs. You thank the elf, and resume your work. Great Job!"
})), })),
createDay(() => ({ createDay(() => ({
day: 4, day: 4,
@ -206,7 +302,9 @@ export const main = createLayer("main", function (this: BaseLayer) {
symbol: elfSymbol, symbol: elfSymbol,
story: "Alright, it seems you finally have enough things set up to start bringing in the elves! Unfortunately, it seems they'll need to be retrained on how to help, since they've stopped practicing for 11 months!", story: "Alright, it seems you finally have enough things set up to start bringing in the elves! Unfortunately, it seems they'll need to be retrained on how to help, since they've stopped practicing for 11 months!",
completedStory: completedStory:
"The workshop now hums with the bustling elves working on everything. They can take it from here - you deserve a break after such a long day! Good Job!" "The workshop now hums with the bustling elves working on everything. They can take it from here - you deserve a break after such a long day! Good Job!",
masteredStory:
"This place feels a lot more better, with less naughty elves who are more excited than ever before to do something! As you collapse into a chair thinking of all of your hard work, Santa comes by yet again to congratulate you on your hard work. You feel a pang of jealousy as Santa is taking all the credit for your work, but you decide that saving Christmas is worth it. Great Job!"
})), })),
createDay(() => ({ createDay(() => ({
day: 5, day: 5,
@ -215,7 +313,9 @@ export const main = createLayer("main", function (this: BaseLayer) {
symbol: paperSymbol, symbol: paperSymbol,
story: "With the elves trained, we're almost ready to start working on these presents! Just a couple more pre-reqs first, starting with turning all this wood into wood pulp and finally into paper, which will be required for wrapping paper later on but in the meantime can be used to help write guides which will help these elves continue their education!", story: "With the elves trained, we're almost ready to start working on these presents! Just a couple more pre-reqs first, starting with turning all this wood into wood pulp and finally into paper, which will be required for wrapping paper later on but in the meantime can be used to help write guides which will help these elves continue their education!",
completedStory: completedStory:
"You look upon your rivers of book pulp as you hand out stacks of papers to elves to read through. You've continued getting closer and closer to preparing for Christmas, and can go to bed satisfied with your progress. Good Job!" "You look upon your rivers of book pulp as you hand out stacks of papers to elves to read through. You've continued getting closer and closer to preparing for Christmas, and can go to bed satisfied with your progress. Good Job!",
masteredStory:
"Paper. Who knew it could be so versatile? As you slowly but surely improve your skills on making paper, you find more efficent ways to make it, and as a bonus, it's also environmentally friendly (which kinda makes up for you chopping a bit too many trees)! As you pass this information along to Santa's elves, they become more excited. Great Job!"
})), })),
createDay(() => ({ createDay(() => ({
day: 6, day: 6,
@ -224,7 +324,9 @@ export const main = createLayer("main", function (this: BaseLayer) {
symbol: boxesSymbol, symbol: boxesSymbol,
story: "You watch all these elves carrying incredibly large loads just in their open elf-sized hands, and realize there's probably a better way. You need to put the toys in boxes anyways, so why don't we get started working on those so the workers can take advantage as well?", story: "You watch all these elves carrying incredibly large loads just in their open elf-sized hands, and realize there's probably a better way. You need to put the toys in boxes anyways, so why don't we get started working on those so the workers can take advantage as well?",
completedStory: completedStory:
"Wow, those boxes are really convenient! The workshop feels more and more proper with every day. You tick another requirement on your list and start looking towards tomorrow. Good Job!" "Wow, those boxes are really convenient! The workshop feels more and more proper with every day. You tick another requirement on your list and start looking towards tomorrow. Good Job!",
masteredStory:
"You look at your massive amounts of boxes, but something doesn't feel right. Oh wait, the elves are only filling the boxes to half the amount that it can actually store! As realisation hits you on how you can make boxes more efficent by using simple methods, you realize that you ought to teach the art of dumping-more-stuff-in-boxes-also-known-as-hoarding to the elves. Whew, that was a lot of work. Great Job!"
})), })),
createDay(() => ({ createDay(() => ({
day: 7, day: 7,
@ -233,7 +335,9 @@ export const main = createLayer("main", function (this: BaseLayer) {
symbol: metalSymbol, symbol: metalSymbol,
story: "You woke up ready to make some toys, before realizing most toys these days are made out of more than just wood! You're sure you're close to really getting to work, but there's a few more materials you're going to need - like metal! Lots of things need metal!", story: "You woke up ready to make some toys, before realizing most toys these days are made out of more than just wood! You're sure you're close to really getting to work, but there's a few more materials you're going to need - like metal! Lots of things need metal!",
completedStory: completedStory:
"The sounds of drills and metal clanging join the already loud din as yet another piece of the puzzle fits into place. You're making solid progress, Good Job!" "The sounds of drills and metal clanging join the already loud din as yet another piece of the puzzle fits into place. You're making solid progress, Good Job!",
masteredStory:
"Cling clang clang clang. The sounds of even more drills hit your ears. As you fondly look back at the terrific work you've done, you become more motivated to work harder. Just then, Santa appears in front of you and you scream. He says, \"I see you're working hard. I suggest that you take a break.\" You thank Santa for the break, sit in a chair made by the elves as a gift, and relax. Great Job!"
})), })),
createDay(() => ({ createDay(() => ({
day: 8, day: 8,
@ -242,7 +346,9 @@ export const main = createLayer("main", function (this: BaseLayer) {
symbol: clothSymbol, symbol: clothSymbol,
story: "Another resource you're going to need for gifts is cloth! Fortunately you think this should be pretty easy to prepare using a sheep farm - and as you've already proven with the tree farm, that's something you can handle!", story: "Another resource you're going to need for gifts is cloth! Fortunately you think this should be pretty easy to prepare using a sheep farm - and as you've already proven with the tree farm, that's something you can handle!",
completedStory: completedStory:
"You fall into a pile of wool, sighing contentedly as you look at all the progress you've made today. Good Job!" "You fall into a pile of wool, sighing contentedly as you look at all the progress you've made today. Good Job!",
masteredStory:
"You're able to bundle yourself in layer after layer of clothing. You watch as everything happens together, harmoniously. Great Job!"
})), })),
createDay(() => ({ createDay(() => ({
day: 9, day: 9,
@ -251,7 +357,9 @@ export const main = createLayer("main", function (this: BaseLayer) {
symbol: oilSymbol, symbol: oilSymbol,
story: "Looks like you just need one more thing before the toy factory can start running: plastic! Every toy nowadays is made with plastic! But wait, how are you going to get plastic? What can make plastic? Wait that's right, oil! You figured out you might as well repurpose your coal and ore drills into something that can get you oil, but unfortunately you'll need to mine much deeper that you're currently doing, so let's get to work!", story: "Looks like you just need one more thing before the toy factory can start running: plastic! Every toy nowadays is made with plastic! But wait, how are you going to get plastic? What can make plastic? Wait that's right, oil! You figured out you might as well repurpose your coal and ore drills into something that can get you oil, but unfortunately you'll need to mine much deeper that you're currently doing, so let's get to work!",
completedStory: completedStory:
"It took a while, but you finally got enough oil for the next step! You deserve a good rest after all this digging work - tomorrow will be a busy day! Good Job!" "It took a while, but you finally got enough oil for the next step! You deserve a good rest after all this digging work - tomorrow will be a busy day! Good Job!",
masteredStory:
"Oil shoots into the air like never before. Physics itself seems to be broken, as there's no other explanation for how you can make everything perfectly efficient without any kind of loss whatsoever. But to be fair, there's probably already a bit of physics shenanigans going on in a typical Christmas anyways. Great Job!"
})), })),
createDay(() => ({ createDay(() => ({
day: 10, day: 10,
@ -260,7 +368,9 @@ export const main = createLayer("main", function (this: BaseLayer) {
symbol: plasticSymbol, symbol: plasticSymbol,
story: "Now that plenty of oil has been prepared, it's time to start refining it into plastic! This should be incredibly useful not only for toys, but making tools and other items!", story: "Now that plenty of oil has been prepared, it's time to start refining it into plastic! This should be incredibly useful not only for toys, but making tools and other items!",
completedStory: completedStory:
"You've started refining massive amounts of oil into slightly less massive amounts of plastic. You have a slight pang of regret thinking of the environmental impact, but ultimately decide Christmas is worth it. Good Job!" "You've started refining massive amounts of oil into slightly less massive amounts of plastic. You have a slight pang of regret thinking of the environmental impact, but ultimately decide Christmas is worth it. Good Job!",
masteredStory:
"You're now making more plastic than you know what to do with. You'll be able to make so many toys with all of this! Great Job!"
})), })),
createDay(() => ({ createDay(() => ({
day: 11, day: 11,
@ -269,7 +379,9 @@ export const main = createLayer("main", function (this: BaseLayer) {
symbol: dyesSymbol, symbol: dyesSymbol,
story: "To make toys, we're going to need some color to make them look nice and enticing! We can't just give kids clear toys after all! To add some color to our toys, we'll need some dyes!", story: "To make toys, we're going to need some color to make them look nice and enticing! We can't just give kids clear toys after all! To add some color to our toys, we'll need some dyes!",
completedStory: completedStory:
"After all that effort, you finally have a rainbow of dyes to choose from! Now the children won't be able to resist the toys you have to offer, once you get them made of course... Good Job!" "After all that effort, you finally have a rainbow of dyes to choose from! Now the children won't be able to resist the toys you have to offer, once you get them made of course... Good Job!",
masteredStory:
"You remember back to when making various dyes was such a painful process, and contrast it to now where everything is trivialized and you even have more uses for all the dyes! Great Job!"
})), })),
createDay(() => ({ createDay(() => ({
day: 12, day: 12,
@ -278,7 +390,8 @@ export const main = createLayer("main", function (this: BaseLayer) {
symbol: managementSymbol, symbol: managementSymbol,
story: "You watch as the elves work, and you realize that they could probably be trained to help out better. Just then, Santa comes over to check on your progress. You reply that you're doing fine, except that the elves may need a bit of behavior management. Santa offers to help, saying that he doesn't want to leave you to do everything. Unfortunately for you, the behavior problems won't fix themselves, so let's get to work!", story: "You watch as the elves work, and you realize that they could probably be trained to help out better. Just then, Santa comes over to check on your progress. You reply that you're doing fine, except that the elves may need a bit of behavior management. Santa offers to help, saying that he doesn't want to leave you to do everything. Unfortunately for you, the behavior problems won't fix themselves, so let's get to work!",
completedStory: completedStory:
"Woo! You are exhausted - this layer felt really long to you. It's great seeing the elves so productive, although you worry a bit about your own job security now! Good Job!" "Woo! You are exhausted - this layer felt really long to you. It's great seeing the elves so productive, although you worry a bit about your own job security now! Good Job!",
masteredStory: ""
})), })),
createDay(() => ({ createDay(() => ({
day: 13, day: 13,
@ -287,7 +400,8 @@ export const main = createLayer("main", function (this: BaseLayer) {
symbol: advManagementSymbol, symbol: advManagementSymbol,
story: "So after a good night's rest you decide that maybe making these elves able to do all the work for you isn't something to be scared of, but rather encouraged. Let's spend another day continuing to train them up and really get this place spinning. They are Santa's elves after all, they're supposed to be able to run everything without you!", story: "So after a good night's rest you decide that maybe making these elves able to do all the work for you isn't something to be scared of, but rather encouraged. Let's spend another day continuing to train them up and really get this place spinning. They are Santa's elves after all, they're supposed to be able to run everything without you!",
completedStory: completedStory:
"The elves are doing an incredible job, and Santa does not seem keen on firing you - Score! Now you can get to work on guiding this properly trained highly functional group of hard workers to make Christmas as great as possible. Good Job!" "The elves are doing an incredible job, and Santa does not seem keen on firing you - Score! Now you can get to work on guiding this properly trained highly functional group of hard workers to make Christmas as great as possible. Good Job!",
masteredStory: ""
})), })),
createDay(() => ({ createDay(() => ({
day: 14, day: 14,
@ -296,48 +410,56 @@ export const main = createLayer("main", function (this: BaseLayer) {
symbol: lettersSymbol, symbol: lettersSymbol,
story: "Fully prepared to start working on presents, you realize you don't actually know what to make! You ask Santa and he points at a massive pile of letters hiding just off-camera. Those are all the letters to Santa that need to be processed, sorted, and categorized appropriately so every kid gets what they need!", story: "Fully prepared to start working on presents, you realize you don't actually know what to make! You ask Santa and he points at a massive pile of letters hiding just off-camera. Those are all the letters to Santa that need to be processed, sorted, and categorized appropriately so every kid gets what they need!",
completedStory: completedStory:
"The letters are sorted! You have a slight feeling you may have rushed a little, and suddenly understand why sometimes you don't get everything you asked Santa for every year, or even the occasional bad gift. You sympathetically pat Santa on the back as you head to bed for the day. Good Job!" "The letters are sorted! You have a slight feeling you may have rushed a little, and suddenly understand why sometimes you don't get everything you asked Santa for every year, or even the occasional bad gift. You sympathetically pat Santa on the back as you head to bed for the day. Good Job!",
masteredStory:
"Finally, you've become the letter processing machine you always knew you could be. There's nothing anyone can do to stop you from processing every gosh darn letter to Santa there is. Great Job!"
})), })),
createDay(() => ({ createDay(() => ({
day: 15, day: 15,
shouldNotify: false, shouldNotify: false,
layer: null, // "wrapping paper" layer: "wrappingPaper",
symbol: wrappingPaperSymbol, symbol: wrappingPaperSymbol,
story: "You'll need to produce wrapping paper so the presents can be wrapped. The elves are getting a bit bored of their boring old workstations, so you decide to let them decorate with some wrapping paper.", story: "You'll need to produce wrapping paper so the presents can be wrapped. The elves are getting a bit bored of their boring old workstations, so you decide to let them decorate with some wrapping paper.",
completedStory: completedStory:
"You've produced enough wrapping paper, and the elves are happy with their new workstations. However, some will need more than just wrapping paper to decorate." "You've produced enough wrapping paper, and the elves are happy with their new workstations. However, some will need more than just wrapping paper to decorate. For now, Good Job!",
masteredStory: ""
})), })),
createDay(() => ({ createDay(() => ({
day: 16, day: 16,
shouldNotify: false, shouldNotify: false,
layer: null, // "ribbons" layer: "ribbon",
symbol: "", symbol: ribbonsSymbol,
story: "", story: "In addition to wrapping paper, you think some ribbons are in order! These should work pretty similarly, allowing you to decorate even more workstations!",
completedStory: "" completedStory:
"Ribbon surrounds the north pole now - everything looks fantastic, and you're pretty sure now you have every single material you could possibly need to start making toys and preparing them for Christmas! With just under 10 days left until Christmas, you go to sleep giddy with anticipation. Good Job!",
masteredStory: ""
})), })),
createDay(() => ({ createDay(() => ({
day: 17, day: 17,
shouldNotify: false, shouldNotify: false,
layer: null, // "toys 1" layer: "toys", // "toys1"
symbol: "", symbol: "",
story: "", story: "",
completedStory: "" completedStory: "",
masteredStory: ""
})), })),
createDay(() => ({ createDay(() => ({
day: 18, day: 18,
shouldNotify: false, shouldNotify: false,
layer: null, // "toys 2" layer: null, // "toys2"
symbol: "", symbol: "",
story: "", story: "",
completedStory: "" completedStory: "",
masteredStory: ""
})), })),
createDay(() => ({ createDay(() => ({
day: 19, day: 19,
shouldNotify: false, shouldNotify: false,
layer: null, // "toys 3" layer: null, // "toys3"
symbol: "", symbol: "",
story: "", story: "",
completedStory: "" completedStory: "",
masteredStory: ""
})), })),
createDay(() => ({ createDay(() => ({
day: 20, day: 20,
@ -345,7 +467,8 @@ export const main = createLayer("main", function (this: BaseLayer) {
layer: "factory", // "presents" layer: "factory", // "presents"
symbol: wrappingPaperSymbol, symbol: wrappingPaperSymbol,
story: "", story: "",
completedStory: "" completedStory: "",
masteredStory: ""
})), })),
createDay(() => ({ createDay(() => ({
day: 21, day: 21,
@ -353,7 +476,8 @@ export const main = createLayer("main", function (this: BaseLayer) {
layer: null, // "reindeer" layer: null, // "reindeer"
symbol: "", symbol: "",
story: "", story: "",
completedStory: "" completedStory: "",
masteredStory: ""
})), })),
createDay(() => ({ createDay(() => ({
day: 22, day: 22,
@ -361,7 +485,8 @@ export const main = createLayer("main", function (this: BaseLayer) {
layer: null, // "sleigh" layer: null, // "sleigh"
symbol: "", symbol: "",
story: "", story: "",
completedStory: "" completedStory: "",
masteredStory: ""
})), })),
createDay(() => ({ createDay(() => ({
day: 23, day: 23,
@ -369,7 +494,8 @@ export const main = createLayer("main", function (this: BaseLayer) {
layer: null, // "distribution route planning" layer: null, // "distribution route planning"
symbol: "", symbol: "",
story: "", story: "",
completedStory: "" completedStory: "",
masteredStory: ""
})), })),
createDay(() => ({ createDay(() => ({
day: 24, day: 24,
@ -377,7 +503,8 @@ export const main = createLayer("main", function (this: BaseLayer) {
layer: null, // "packing the presents" layer: null, // "packing the presents"
symbol: "", symbol: "",
story: "", story: "",
completedStory: "" completedStory: "",
masteredStory: ""
})) }))
]; ];
@ -392,17 +519,42 @@ export const main = createLayer("main", function (this: BaseLayer) {
save(); save();
} }
function completeMastery() {
const completedLayer = currentlyMastering.value;
if (completedLayer == null) {
return;
}
loreScene.value = (completedLayer as any).day - 1;
loreTitle.value = "Day Decorated!";
loreBody.value = days[loreScene.value].masteredStory;
showLoreModal.value = true;
if ((completedLayer as any).mastered != null) {
(completedLayer as any).mastered.value = true;
}
toggleMastery();
if (completedLayer.id === "cloth") {
elves.elves.plasticElf.bought.value = true;
}
}
return { return {
name: "Calendar", name: "Calendar",
days, days,
day, day,
openDay,
timeUntilNewDay, timeUntilNewDay,
loreScene, loreScene,
loreTitle, loreTitle,
loreBody, loreBody,
showLoreModal, showLoreModal,
completeDay, completeDay,
completeMastery,
minWidth: 700, minWidth: 700,
isMastery,
toggleMastery,
swappingMastery,
currentlyMastering,
masteredDays,
display: jsx(() => ( display: jsx(() => (
<> <>
{player.devSpeed === 0 ? <div>Game Paused</div> : null} {player.devSpeed === 0 ? <div>Game Paused</div> : null}
@ -413,7 +565,13 @@ export const main = createLayer("main", function (this: BaseLayer) {
<div>Offline Time: {formatTime(player.offlineTime)}</div> <div>Offline Time: {formatTime(player.offlineTime)}</div>
) : null} ) : null}
<Spacer /> <Spacer />
<div class="advent"> {isMastery.value ? (
<>
<div>Now decorating {currentlyMastering.value?.name}</div>
<Spacer />
</>
) : null}
<div class={{ advent: true, decorating: isMastery.value }}>
{days {days
.reduce( .reduce(
(acc, curr) => { (acc, curr) => {
@ -452,10 +610,12 @@ export const getInitialLayers = (
oil, oil,
plastic, plastic,
dyes, dyes,
wrappingPaper,
management, management,
factory, letters,
letters wrappingPaper,
ribbon,
toys,
factory
]; ];
/** /**

View file

@ -28,7 +28,7 @@
<div <div
class="overlayTextContainer border" class="overlayTextContainer border"
:style="[ :style="[
{ width: unref(width) + 'px', height: (unref(height) - 1) + 'px' }, { width: unref(width) - 1 + 'px', height: unref(height) - 1 + 'px' },
unref(borderStyle) ?? {} unref(borderStyle) ?? {}
]" ]"
> >

View file

@ -13,6 +13,7 @@ import type {
import { processComputable } from "util/computed"; import { processComputable } from "util/computed";
import { createLazyProxy } from "util/proxies"; import { createLazyProxy } from "util/proxies";
import { shallowReactive, unref } from "vue"; import { shallowReactive, unref } from "vue";
import HotkeyVue from "components/Hotkey.vue";
export const hotkeys: Record<string, GenericHotkey | undefined> = shallowReactive({}); export const hotkeys: Record<string, GenericHotkey | undefined> = shallowReactive({});
export const HotkeyType = Symbol("Hotkey"); export const HotkeyType = Symbol("Hotkey");
@ -101,11 +102,13 @@ registerInfoComponent(
<div> <div>
<br /> <br />
<h4>Hotkeys</h4> <h4>Hotkeys</h4>
{keys.map(hotkey => ( <div style="column-count: 2">
<div> {keys.map(hotkey => (
{hotkey?.key}: {hotkey?.description} <div>
</div> <HotkeyVue hotkey={hotkey as GenericHotkey} /> {hotkey?.description}
))} </div>
))}
</div>
</div> </div>
); );
}) })

View file

@ -1,5 +1,5 @@
import Select from "components/fields/Select.vue"; import Select from "components/fields/Select.vue";
import type { CoercableComponent, OptionsFunc, Replace, StyleValue } from "features/feature"; import type { CoercableComponent, GenericComponent, OptionsFunc, Replace, StyleValue } from "features/feature";
import { Component, GatherProps, getUniqueID, jsx, setDefault, Visibility } from "features/feature"; import { Component, GatherProps, getUniqueID, jsx, setDefault, Visibility } from "features/feature";
import MilestoneComponent from "features/milestones/Milestone.vue"; import MilestoneComponent from "features/milestones/Milestone.vue";
import { globalBus } from "game/events"; import { globalBus } from "game/events";
@ -55,7 +55,7 @@ export interface BaseMilestone {
earned: Persistent<boolean>; earned: Persistent<boolean>;
complete: VoidFunction; complete: VoidFunction;
type: typeof MilestoneType; type: typeof MilestoneType;
[Component]: typeof MilestoneComponent; [Component]: GenericComponent;
[GatherProps]: () => Record<string, unknown>; [GatherProps]: () => Record<string, unknown>;
} }
@ -85,7 +85,7 @@ export function createMilestone<T extends MilestoneOptions>(
const milestone = optionsFunc?.() ?? ({} as ReturnType<NonNullable<typeof optionsFunc>>); const milestone = optionsFunc?.() ?? ({} as ReturnType<NonNullable<typeof optionsFunc>>);
milestone.id = getUniqueID("milestone-"); milestone.id = getUniqueID("milestone-");
milestone.type = MilestoneType; milestone.type = MilestoneType;
milestone[Component] = MilestoneComponent; milestone[Component] = MilestoneComponent as GenericComponent;
milestone.earned = earned; milestone.earned = earned;
milestone.complete = function () { milestone.complete = function () {

View file

@ -7,7 +7,12 @@
> >
<div class="main-display"> <div class="main-display">
<span v-if="showPrefix">You have </span> <span v-if="showPrefix">You have </span>
<ResourceVue :resource="resource" :color="color || 'white'" :style="resourceStyle" /> <ResourceVue
:resource="resource"
:color="color || 'white'"
:shadow-color="shadowColor"
:style="resourceStyle"
/>
{{ resource.displayName {{ resource.displayName
}}<!-- remove whitespace --> }}<!-- remove whitespace -->
<span v-if="effectComponent" <span v-if="effectComponent"
@ -20,14 +25,20 @@
</div> </div>
</div> </div>
</Sticky> </Sticky>
<div v-else <div
v-else
class="main-display-container" class="main-display-container"
:class="classes ?? {}" :class="classes ?? {}"
:style="[{ 'min-height': '50px' }, style ?? {}]" :style="[{ 'min-height': '50px' }, style ?? {}]"
> >
<div class="main-display"> <div class="main-display">
<span v-if="showPrefix">You have </span> <span v-if="showPrefix">You have </span>
<ResourceVue :resource="resource" :color="color || 'white'" :style="resourceStyle" /> <ResourceVue
:resource="resource"
:color="color || 'white'"
:shadow-color="shadowColor"
:style="resourceStyle"
/>
{{ resource.displayName {{ resource.displayName
}}<!-- remove whitespace --> }}<!-- remove whitespace -->
<span v-if="effectComponent" <span v-if="effectComponent"
@ -51,16 +62,20 @@ import { computeOptionalComponent } from "util/vue";
import { ComponentPublicInstance, ref, Ref, StyleValue } from "vue"; import { ComponentPublicInstance, ref, Ref, StyleValue } from "vue";
import { computed, toRefs } from "vue"; import { computed, toRefs } from "vue";
const _props = withDefaults(defineProps<{ const _props = withDefaults(
resource: Resource; defineProps<{
color?: string; resource: Resource;
classes?: Record<string, boolean>; color?: string;
style?: StyleValue; shadowColor?: string;
resourceStyle?: StyleValue; classes?: Record<string, boolean>;
effectDisplay?: CoercableComponent; style?: StyleValue;
productionDisplay?: CoercableComponent; resourceStyle?: StyleValue;
sticky?: boolean effectDisplay?: CoercableComponent;
}>(), {sticky: true}); productionDisplay?: CoercableComponent;
sticky?: boolean;
}>(),
{ sticky: true }
);
const props = toRefs(_props); const props = toRefs(_props);
const effectRef = ref<ComponentPublicInstance | null>(null); const effectRef = ref<ComponentPublicInstance | null>(null);

View file

@ -1,5 +1,5 @@
<template> <template>
<h2 :style="[{ color, 'text-shadow': '0px 0px 10px ' + color }, style ?? {}]"> <h2 :style="[{ color, 'text-shadow': '0px 0px 10px ' + (shadowColor ?? color) }, style ?? {}]">
{{ amount }} {{ amount }}
</h2> </h2>
</template> </template>
@ -12,6 +12,7 @@ import { computed, StyleValue } from "vue";
const props = defineProps<{ const props = defineProps<{
resource: Resource; resource: Resource;
color: string; color: string;
shadowColor?: string;
style?: StyleValue; style?: StyleValue;
}>(); }>();

View file

@ -1,3 +1,4 @@
import { main } from "data/projEntry";
import { globalBus } from "game/events"; import { globalBus } from "game/events";
import { NonPersistent, Persistent, State } from "game/persistence"; import { NonPersistent, Persistent, State } from "game/persistence";
import { persistent } from "game/persistence"; import { persistent } from "game/persistence";
@ -67,7 +68,7 @@ export function trackBest(resource: Resource): Ref<DecimalSource> {
export function trackTotal(resource: Resource): Ref<DecimalSource> { export function trackTotal(resource: Resource): Ref<DecimalSource> {
const total = persistent(resource.value); const total = persistent(resource.value);
watch(resource, (amount, prevAmount) => { watch(resource, (amount, prevAmount) => {
if (loadingSave.value) { if (loadingSave.value || main.swappingMastery.value) {
return; return;
} }
if (Decimal.gt(amount, prevAmount)) { if (Decimal.gt(amount, prevAmount)) {

View file

@ -1,4 +1,10 @@
import type { CoercableComponent, GenericComponent, OptionsFunc, Replace, StyleValue } from "features/feature"; import type {
CoercableComponent,
GenericComponent,
OptionsFunc,
Replace,
StyleValue
} from "features/feature";
import { import {
Component, Component,
findFeatures, findFeatures,
@ -23,7 +29,7 @@ import type {
} from "util/computed"; } from "util/computed";
import { processComputable } from "util/computed"; import { processComputable } from "util/computed";
import { createLazyProxy } from "util/proxies"; import { createLazyProxy } from "util/proxies";
import type { Ref } from "vue"; import { isReadonly, Ref } from "vue";
import { computed, unref } from "vue"; import { computed, unref } from "vue";
export const UpgradeType = Symbol("Upgrade"); export const UpgradeType = Symbol("Upgrade");
@ -120,7 +126,11 @@ export function createUpgrade<T extends UpgradeOptions>(
if (!unref(genericUpgrade.canPurchase)) { if (!unref(genericUpgrade.canPurchase)) {
return; return;
} }
if (genericUpgrade.resource != null && genericUpgrade.cost != null) { if (
genericUpgrade.resource != null &&
!isReadonly(genericUpgrade.resource) &&
genericUpgrade.cost != null
) {
genericUpgrade.resource.value = Decimal.sub( genericUpgrade.resource.value = Decimal.sub(
genericUpgrade.resource.value, genericUpgrade.resource.value,
unref(genericUpgrade.cost) unref(genericUpgrade.cost)

View file

@ -137,6 +137,33 @@ ul {
background: #070710; background: #070710;
} }
.unaffordable {
color: var(--danger);
}
.decoration-effect {
border: solid 8px darkred;
padding: 4px;
width: 576px;
position: relative;
border-radius: 10px;
}
.decoration-effect:not(.ribbon) {
border-image: repeating-linear-gradient(-45deg, rgb(255, 76, 76) 0 10px, rgb(255, 255, 255) 10px 20px, rgb(65, 255, 95) 20px 30px, rgb(255, 255, 255) 30px 40px) 12/10px;
}
.decoration-effect.ribbon::before {
content: "🎀";
color: red;
position: absolute;
top: -20px;
left: -20px;
font-size: xx-large;
transform: rotateZ(-45deg);
z-index: 1;
}
.layer-container { .layer-container {
min-width: 100%; min-width: 100%;
min-height: 100%; min-height: 100%;

View file

@ -229,7 +229,7 @@ export function computeOptionalComponent(
const comp = shallowRef<Component | "" | null>(null); const comp = shallowRef<Component | "" | null>(null);
watchEffect(() => { watchEffect(() => {
const currComponent = unwrapRef(component); const currComponent = unwrapRef(component);
comp.value = currComponent == null ? null : coerceComponent(currComponent, defaultWrapper); comp.value = !currComponent ? null : coerceComponent(currComponent, defaultWrapper);
}); });
return comp; return comp;
} }