mirror of
https://github.com/thepaperpilot/Advent-Incremental.git
synced 2024-11-24 09:21:48 +00:00
Add layer stuff
This commit is contained in:
parent
332fadc7ee
commit
f5d29ed117
2 changed files with 596 additions and 4 deletions
589
src/data/layers/routing.tsx
Normal file
589
src/data/layers/routing.tsx
Normal file
|
@ -0,0 +1,589 @@
|
|||
/**
|
||||
* @module
|
||||
* @hidden
|
||||
*/
|
||||
import HotkeyVue from "components/Hotkey.vue";
|
||||
import Spacer from "components/layout/Spacer.vue";
|
||||
import Modal from "components/Modal.vue";
|
||||
import { createCollapsibleModifierSections } from "data/common";
|
||||
import { main } from "data/projEntry";
|
||||
import { createBar, GenericBar } from "features/bars/bar";
|
||||
import { createClickable } from "features/clickables/clickable";
|
||||
import { jsx } from "features/feature";
|
||||
import { createHotkey, GenericHotkey } from "features/hotkey";
|
||||
import { createUpgrade } from "features/upgrades/upgrade";
|
||||
import { globalBus } from "game/events";
|
||||
import { BaseLayer, createLayer } from "game/layers";
|
||||
import {
|
||||
createAdditiveModifier,
|
||||
createMultiplicativeModifier,
|
||||
createSequentialModifier
|
||||
} from "game/modifiers";
|
||||
import { persistent } from "game/persistence";
|
||||
import Decimal, { DecimalSource, format, formatTime, formatWhole } from "util/bignum";
|
||||
import { Direction } from "util/common";
|
||||
import { render, renderGrid } from "util/vue";
|
||||
import { computed, ref, unref, watchEffect } from "vue";
|
||||
import boxes from "./boxes";
|
||||
import cloth from "./cloth";
|
||||
import coal from "./coal";
|
||||
import dyes from "./dyes";
|
||||
import metal from "./metal";
|
||||
import oil from "./oil";
|
||||
import paper from "./paper";
|
||||
import plastic from "./plastic";
|
||||
import "./styles/reindeer.css";
|
||||
import trees from "./trees";
|
||||
|
||||
const id = "routing";
|
||||
const day = 23;
|
||||
const layer = createLayer(id, function (this: BaseLayer) {
|
||||
const name = "Routing";
|
||||
const color = "navajowhite";
|
||||
|
||||
const feedGoal = 2.5e3;
|
||||
|
||||
const timeSinceFocus = persistent<number>(0);
|
||||
|
||||
const currMultiplier = persistent<DecimalSource>(1);
|
||||
const currTargets = persistent<Record<string, boolean>>({});
|
||||
const currCooldown = persistent<number>(0);
|
||||
const crit = persistent<number>(0);
|
||||
|
||||
const maxMultiplier = createSequentialModifier(() => [
|
||||
createMultiplicativeModifier(() => ({
|
||||
multiplier: 2,
|
||||
description: "Carry food in boxes",
|
||||
enabled: upgrade4.bought
|
||||
}))
|
||||
]);
|
||||
const computedMaxMultiplier = computed(() => maxMultiplier.apply(2));
|
||||
const targetsCount = createSequentialModifier(() => [
|
||||
createAdditiveModifier(() => ({
|
||||
addend: 1,
|
||||
description: "Guide to Reindeer Handling",
|
||||
enabled: upgrade3.bought
|
||||
})),
|
||||
createAdditiveModifier(() => ({
|
||||
addend: crit,
|
||||
description: "Metal clapper",
|
||||
enabled: upgrade5.bought
|
||||
}))
|
||||
]);
|
||||
const computedTargetsCount = computed(() => targetsCount.apply(1));
|
||||
const computedMaxCooldown = computed(() => 10);
|
||||
|
||||
function focus() {
|
||||
let targetsSelected = 0;
|
||||
currTargets.value = {};
|
||||
timeSinceFocus.value = 0;
|
||||
while (Decimal.gt(computedTargetsCount.value, targetsSelected)) {
|
||||
const selectedReindeer =
|
||||
Object.values(reindeer)[Math.floor(Math.random() * Object.values(reindeer).length)];
|
||||
const roll = selectedReindeer?.name ?? "";
|
||||
if (!currTargets.value[roll]) {
|
||||
currTargets.value[roll] = true;
|
||||
targetsSelected++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const focusMeter = createBar(() => ({
|
||||
direction: Direction.Right,
|
||||
width: 476,
|
||||
height: 50,
|
||||
style: `border-radius: 0`,
|
||||
borderStyle: `border-radius: 0`,
|
||||
fillStyle: () => ({
|
||||
background: currCooldown.value > 0 ? color : "#7f7f00",
|
||||
animation: currCooldown.value > 0 ? "1s focused-eating-bar linear infinite" : "",
|
||||
opacity: currCooldown.value > 0 ? currCooldown.value / 10 : 1,
|
||||
transition: "none"
|
||||
}),
|
||||
progress: () =>
|
||||
Decimal.sub(currMultiplier.value, 1)
|
||||
.div(Decimal.sub(computedMaxMultiplier.value, 1))
|
||||
.toNumber(),
|
||||
display: jsx(() => (
|
||||
<>
|
||||
{format(currMultiplier.value)}x
|
||||
{currCooldown.value > 0 ? (
|
||||
<>
|
||||
{" "}
|
||||
to {Object.keys(currTargets.value).join(", ")} for{" "}
|
||||
{formatTime(currCooldown.value)}
|
||||
</>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</>
|
||||
))
|
||||
})) as GenericBar;
|
||||
|
||||
const focusButton = createClickable(() => ({
|
||||
display: {
|
||||
title: "Focus",
|
||||
description: jsx(() => (
|
||||
<>
|
||||
Motivate reindeer to eat, multiplying {formatWhole(computedTargetsCount.value)}{" "}
|
||||
random reindeer's eating rate by up to {format(computedMaxMultiplier.value)}x
|
||||
for {formatTime(computedMaxCooldown.value)}, equal to the focus bar's effect.
|
||||
</>
|
||||
))
|
||||
},
|
||||
style: {
|
||||
width: "480px",
|
||||
minHeight: "80px",
|
||||
zIndex: 4
|
||||
},
|
||||
canClick: () => Decimal.eq(currCooldown.value, 0),
|
||||
onClick() {
|
||||
currCooldown.value = Decimal.fromValue(computedMaxCooldown.value).toNumber();
|
||||
focus();
|
||||
}
|
||||
}));
|
||||
|
||||
const cooldown = createSequentialModifier(() => [
|
||||
createMultiplicativeModifier(() => ({
|
||||
multiplier: 0.5,
|
||||
description: "Pile of coal",
|
||||
enabled: upgrade2.bought
|
||||
}))
|
||||
]);
|
||||
const computedCooldown = computed(() => cooldown.apply(10));
|
||||
|
||||
function createReindeer(options: {
|
||||
name: string;
|
||||
key: string;
|
||||
boostDescription: string;
|
||||
boostAmount: DecimalSource;
|
||||
}) {
|
||||
const timesFed = persistent<DecimalSource>(0);
|
||||
const progress = persistent<DecimalSource>(0);
|
||||
|
||||
const hotkey = createHotkey(() => ({
|
||||
key: "Numpad " + options.key,
|
||||
description: "Feed " + options.name,
|
||||
enabled: main.days[day - 1].opened,
|
||||
onPress: clickable.onClick
|
||||
})) as GenericHotkey;
|
||||
|
||||
const clickable = createClickable(() => {
|
||||
const progressBar = createBar(() => ({
|
||||
direction: Direction.Right,
|
||||
width: 140,
|
||||
height: 10,
|
||||
style: "margin-top: 8px",
|
||||
borderStyle: "border-color: black",
|
||||
baseStyle: "margin-top: -1px",
|
||||
fillStyle: () => ({
|
||||
marginTop: "-1px",
|
||||
transitionDuration: "0s",
|
||||
background: "black",
|
||||
animation:
|
||||
currTargets.value[options.name] && currCooldown.value > 0
|
||||
? ".5s focused-eating-bar linear infinite"
|
||||
: ""
|
||||
}),
|
||||
progress: () => Decimal.div(progress.value, computedCooldown.value)
|
||||
}));
|
||||
|
||||
const modifier = createMultiplicativeModifier(() => ({
|
||||
multiplier: effect,
|
||||
description: options.name,
|
||||
enabled: () => Decimal.gt(timesFed.value, 0)
|
||||
}));
|
||||
|
||||
const effect = computed(() =>
|
||||
Decimal.times(options.boostAmount, timesFed.value)
|
||||
.add(1)
|
||||
.pow(upgrade9.bought.value ? 1.1 : 1)
|
||||
);
|
||||
|
||||
return {
|
||||
...options,
|
||||
hotkey,
|
||||
timesFed,
|
||||
progress,
|
||||
effect,
|
||||
modifier,
|
||||
display: {
|
||||
title: jsx(() => (
|
||||
<h3>
|
||||
Feed {options.name} <HotkeyVue hotkey={hotkey} />
|
||||
</h3>
|
||||
)),
|
||||
description: jsx(() => (
|
||||
<>
|
||||
<br />
|
||||
Each time you feed {options.name} will increase your{" "}
|
||||
{options.boostDescription} by +{format(options.boostAmount)}x
|
||||
<Spacer />
|
||||
Currently {format(effect.value)}x
|
||||
<br />
|
||||
{render(progressBar)}
|
||||
</>
|
||||
))
|
||||
},
|
||||
style: {
|
||||
width: "160px",
|
||||
height: "160px"
|
||||
},
|
||||
canClick() {
|
||||
return Decimal.gte(progress.value, computedCooldown.value);
|
||||
},
|
||||
onClick() {
|
||||
if (!unref(clickable.canClick)) {
|
||||
return;
|
||||
}
|
||||
let amount = Decimal.div(progress.value, computedCooldown.value);
|
||||
if (upgrade1.bought.value) {
|
||||
amount = Decimal.times(amount, 2);
|
||||
}
|
||||
timesFed.value = Decimal.add(timesFed.value, amount);
|
||||
progress.value = 0;
|
||||
},
|
||||
update(diff: number) {
|
||||
if (Decimal.gte(progress.value, computedCooldown.value)) {
|
||||
progress.value = computedCooldown.value;
|
||||
} else {
|
||||
let amount: DecimalSource = diff;
|
||||
const isFocused = currTargets.value[options.name] && currCooldown.value > 0;
|
||||
if (isFocused) {
|
||||
amount = Decimal.times(amount, currMultiplier.value);
|
||||
}
|
||||
progress.value = Decimal.add(progress.value, amount);
|
||||
if (clickable.isHolding.value || (upgrade8.bought.value && isFocused)) {
|
||||
clickable.onClick();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
return clickable;
|
||||
}
|
||||
const dasher = createReindeer({
|
||||
name: "Dasher",
|
||||
key: "7",
|
||||
boostDescription: "log gain",
|
||||
boostAmount: 1
|
||||
});
|
||||
const dancer = createReindeer({
|
||||
name: "Dancer",
|
||||
key: "8",
|
||||
boostDescription: "coal gain",
|
||||
boostAmount: 0.1
|
||||
});
|
||||
const prancer = createReindeer({
|
||||
name: "Prancer",
|
||||
key: "9",
|
||||
boostDescription: "paper gain",
|
||||
boostAmount: 0.1
|
||||
});
|
||||
const vixen = createReindeer({
|
||||
name: "Vixen",
|
||||
key: "4",
|
||||
boostDescription: "boxes gain",
|
||||
boostAmount: 0.1
|
||||
});
|
||||
const comet = createReindeer({
|
||||
name: "Comet",
|
||||
key: "5",
|
||||
boostDescription: "metal gain",
|
||||
boostAmount: 0.1
|
||||
});
|
||||
const cupid = createReindeer({
|
||||
name: "Cupid",
|
||||
key: "6",
|
||||
boostDescription: "cloth actions",
|
||||
boostAmount: 0.1
|
||||
});
|
||||
const donner = createReindeer({
|
||||
name: "Donner",
|
||||
key: "1",
|
||||
boostDescription: "oil gain",
|
||||
boostAmount: 0.01
|
||||
});
|
||||
const blitzen = createReindeer({
|
||||
name: "Blitzen",
|
||||
key: "2",
|
||||
boostDescription: "plastic gain",
|
||||
boostAmount: 0.1
|
||||
});
|
||||
const rudolph = createReindeer({
|
||||
name: "Rudolph",
|
||||
key: "3",
|
||||
boostDescription: "dye gain",
|
||||
boostAmount: 0.01
|
||||
});
|
||||
// order is designed so hotkeys appear 1-9, even though they're displayed in numpad order in the layer itself
|
||||
const reindeer = { donner, blitzen, rudolph, vixen, comet, cupid, dasher, dancer, prancer };
|
||||
|
||||
const sumTimesFed = computed(() =>
|
||||
Object.values(reindeer)
|
||||
.map(r => r.timesFed.value)
|
||||
.reduce(Decimal.add, Decimal.dZero)
|
||||
);
|
||||
|
||||
const upgrade1 = createUpgrade(() => ({
|
||||
resource: trees.logs,
|
||||
cost: 0,
|
||||
style: {
|
||||
width: "160px"
|
||||
},
|
||||
display: {
|
||||
title: "Sawdust?",
|
||||
description:
|
||||
"Adding some sawdust to the feed allows you to make more of it. Each feed action counts twice"
|
||||
}
|
||||
}));
|
||||
const upgrade2 = createUpgrade(() => ({
|
||||
resource: coal.coal,
|
||||
cost: 0,
|
||||
style: {
|
||||
width: "160px"
|
||||
},
|
||||
display: {
|
||||
title: "Pile of coal",
|
||||
description:
|
||||
"Building a threatening pile of coal encourages the reindeer to behave. Each reindeer eats twice as fast"
|
||||
}
|
||||
}));
|
||||
const upgrade3 = createUpgrade(() => ({
|
||||
resource: paper.paper,
|
||||
cost: 0,
|
||||
style: {
|
||||
width: "160px"
|
||||
},
|
||||
display: {
|
||||
title: "Guide to Reindeer Handling",
|
||||
description:
|
||||
"Written reindeer handling instructions allow you to help more focus at once. Increase focus targets by one"
|
||||
}
|
||||
}));
|
||||
const upgrade4 = createUpgrade(() => ({
|
||||
resource: boxes.boxes,
|
||||
cost: 0,
|
||||
style: {
|
||||
width: "160px"
|
||||
},
|
||||
display: {
|
||||
title: "Carry food in boxes",
|
||||
description:
|
||||
"Carrying reindeer food in boxes allows you to distribute it faster. Double the maximum focus multiplier"
|
||||
}
|
||||
}));
|
||||
const upgrade5 = createUpgrade(() => ({
|
||||
resource: metal.metal,
|
||||
cost: 0,
|
||||
style: {
|
||||
width: "160px"
|
||||
},
|
||||
display: {
|
||||
title: "Metal clapper",
|
||||
description:
|
||||
'Striking two rods of metal can help get more reindeer\'s attention when done right. "Critical" focuses now affect up to two additional reindeer'
|
||||
}
|
||||
}));
|
||||
const upgrade6 = createUpgrade(() => ({
|
||||
resource: cloth.cloth,
|
||||
cost: 0,
|
||||
style: {
|
||||
width: "160px"
|
||||
},
|
||||
display: {
|
||||
title: "Focus bar padding",
|
||||
description:
|
||||
"Adding padding to the focus bar lets you slow it down when it's closer to the max value"
|
||||
}
|
||||
}));
|
||||
const upgrade7 = createUpgrade(() => ({
|
||||
resource: oil.oil,
|
||||
cost: 0,
|
||||
style: {
|
||||
width: "160px"
|
||||
},
|
||||
display: {
|
||||
title: "Oil can do that?",
|
||||
description:
|
||||
"Using a lot of oil somehow let's reindeers focus themselves with a random value when left un-focused for 10s"
|
||||
}
|
||||
}));
|
||||
const upgrade8 = createUpgrade(() => ({
|
||||
resource: plastic.plastic,
|
||||
cost: 0,
|
||||
style: {
|
||||
width: "160px"
|
||||
},
|
||||
display: {
|
||||
title: "Autoamted feeder",
|
||||
description: "An automated feeder let's focused reindeer eat automatically"
|
||||
}
|
||||
}));
|
||||
const upgrade9 = createUpgrade(() => ({
|
||||
resource: dyes.dyes.white.amount,
|
||||
cost: 0,
|
||||
style: {
|
||||
width: "160px"
|
||||
},
|
||||
display: {
|
||||
title: "Colorful food",
|
||||
description:
|
||||
"Adding some non-toxic dyes to the food makes them more powerful. Raise each reindeer's effect to the ^1.1"
|
||||
}
|
||||
}));
|
||||
const upgrades = {
|
||||
upgrade1,
|
||||
upgrade2,
|
||||
upgrade3,
|
||||
upgrade4,
|
||||
upgrade5,
|
||||
upgrade6,
|
||||
upgrade7,
|
||||
upgrade8,
|
||||
upgrade9
|
||||
};
|
||||
|
||||
const [generalTab, generalTabCollapsed] = createCollapsibleModifierSections(() => [
|
||||
{
|
||||
title: "Max Focus Multiplier",
|
||||
modifier: maxMultiplier,
|
||||
base: 2
|
||||
},
|
||||
{
|
||||
title: "Focus Targets",
|
||||
modifier: targetsCount,
|
||||
base: 1
|
||||
},
|
||||
{
|
||||
title: "Eating duration",
|
||||
modifier: cooldown,
|
||||
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
|
||||
}}
|
||||
/>
|
||||
));
|
||||
|
||||
globalBus.on("update", diff => {
|
||||
if (Decimal.lt(main.day.value, day)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object.values(reindeer).forEach(reindeer => reindeer.update(diff));
|
||||
|
||||
currCooldown.value = Math.max(currCooldown.value - diff, 0);
|
||||
|
||||
let auto = false;
|
||||
if (upgrade7.bought.value) {
|
||||
timeSinceFocus.value += diff;
|
||||
if (timeSinceFocus.value > 10) {
|
||||
auto = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (Decimal.eq(currCooldown.value, 0)) {
|
||||
let speed = 1000;
|
||||
if (auto) {
|
||||
speed = Math.random() * 1000;
|
||||
}
|
||||
let stoppedAt = 1 - Math.abs(Math.sin((Date.now() / speed) * 2));
|
||||
if (upgrade6.bought.value) {
|
||||
stoppedAt = 1 - (1 - stoppedAt) ** 2;
|
||||
}
|
||||
crit.value = stoppedAt > 0.975 ? 2 : stoppedAt > 0.9 ? 1 : 0;
|
||||
currMultiplier.value = Decimal.pow(computedMaxMultiplier.value, stoppedAt);
|
||||
if (auto) {
|
||||
focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const dayProgress = createBar(() => ({
|
||||
direction: Direction.Right,
|
||||
width: 600,
|
||||
height: 25,
|
||||
fillStyle: `backgroundColor: ${color}`,
|
||||
progress: () => (main.day.value === day ? Decimal.div(sumTimesFed.value, feedGoal) : 1),
|
||||
display: jsx(() =>
|
||||
main.day.value === day ? (
|
||||
<>
|
||||
{formatWhole(sumTimesFed.value)}/{formatWhole(feedGoal)}
|
||||
</>
|
||||
) : (
|
||||
""
|
||||
)
|
||||
)
|
||||
})) as GenericBar;
|
||||
|
||||
watchEffect(() => {
|
||||
if (main.day.value === day && Decimal.gte(sumTimesFed.value, feedGoal)) {
|
||||
main.completeDay();
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
name,
|
||||
day,
|
||||
color,
|
||||
reindeer,
|
||||
generalTabCollapsed,
|
||||
timeSinceFocus,
|
||||
currMultiplier,
|
||||
currTargets,
|
||||
currCooldown,
|
||||
upgrades,
|
||||
crit,
|
||||
minWidth: 700,
|
||||
display: jsx(() => (
|
||||
<>
|
||||
<div>
|
||||
{main.day.value === day
|
||||
? `Feed reindeer ${formatWhole(feedGoal)} times 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 />
|
||||
<div>You have fed reindeer {formatWhole(sumTimesFed.value)} times</div>
|
||||
<Spacer />
|
||||
{renderGrid(
|
||||
[focusButton],
|
||||
[focusMeter],
|
||||
[dasher, dancer, prancer],
|
||||
[vixen, comet, cupid],
|
||||
[donner, blitzen, rudolph]
|
||||
)}
|
||||
<Spacer />
|
||||
{renderGrid(
|
||||
[upgrade1, upgrade2, upgrade3],
|
||||
[upgrade4, upgrade5, upgrade6],
|
||||
[upgrade7, upgrade8, upgrade9]
|
||||
)}
|
||||
</>
|
||||
)),
|
||||
minimizedDisplay: jsx(() => (
|
||||
<div>
|
||||
{name} <span class="desc">{format(sumTimesFed.value)} times fed</span>
|
||||
</div>
|
||||
))
|
||||
};
|
||||
});
|
||||
|
||||
export default layer;
|
|
@ -33,6 +33,7 @@ import paper from "./layers/paper";
|
|||
import plastic from "./layers/plastic";
|
||||
import reindeer from "./layers/reindeer";
|
||||
import ribbon from "./layers/ribbon";
|
||||
import routing from "./layers/routing";
|
||||
import toys from "./layers/toys";
|
||||
import trees from "./layers/trees";
|
||||
import workshop from "./layers/workshop";
|
||||
|
@ -499,10 +500,11 @@ export const main = createLayer("main", function (this: BaseLayer) {
|
|||
createDay(() => ({
|
||||
day: 23,
|
||||
shouldNotify: false,
|
||||
layer: null, // "distribution route planning"
|
||||
layer: "routing",
|
||||
symbol: "",
|
||||
story: "",
|
||||
completedStory: "",
|
||||
story: "You're almost ready for the big day! The next step is to find an optimal route to ensure you can get all the presents delivered before kids start waking up! This is like the travelling salesman problem on steroids. Good Luck!",
|
||||
completedStory:
|
||||
"Take that, math majors! Optimal route planned with time to spare. Good Job!",
|
||||
masteredStory: ""
|
||||
})),
|
||||
createDay(() => ({
|
||||
|
@ -625,7 +627,8 @@ export const getInitialLayers = (
|
|||
toys,
|
||||
factory,
|
||||
reindeer,
|
||||
sleigh
|
||||
sleigh,
|
||||
routing
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue