mirror of
https://github.com/thepaperpilot/Advent-Incremental.git
synced 2024-11-22 00:21:34 +00:00
Merge branch 'day-25-credits' of https://github.com/thepaperpilot/Advent-Incremental into day-25-credits
This commit is contained in:
commit
c69f1e94e5
19 changed files with 1130 additions and 119 deletions
|
@ -4,7 +4,6 @@
|
||||||
v-for="(tab, index) in tabs"
|
v-for="(tab, index) in tabs"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="tab"
|
class="tab"
|
||||||
:ref="`tab-${index}`"
|
|
||||||
:style="unref(layers[tab]?.style)"
|
:style="unref(layers[tab]?.style)"
|
||||||
:class="unref(layers[tab]?.classes)"
|
:class="unref(layers[tab]?.classes)"
|
||||||
>
|
>
|
||||||
|
@ -14,7 +13,6 @@
|
||||||
v-if="layerKeys.includes(tab)"
|
v-if="layerKeys.includes(tab)"
|
||||||
v-bind="gatherLayerProps(layers[tab]!)"
|
v-bind="gatherLayerProps(layers[tab]!)"
|
||||||
:index="index"
|
:index="index"
|
||||||
:tab="() => (($refs[`tab-${index}`] as HTMLElement[] | undefined)?.[0])"
|
|
||||||
/>
|
/>
|
||||||
<component :is="tab" :index="index" v-else />
|
<component :is="tab" :index="index" v-else />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
masteryLock,
|
masteryLock,
|
||||||
wallpaper: day < 8
|
wallpaper: day < 8
|
||||||
}"
|
}"
|
||||||
v-if="opened.value"
|
v-if="opened.value && visibility !== Visibility.None"
|
||||||
>
|
>
|
||||||
<div class="ribbon" v-if="day >= 8" />
|
<div class="ribbon" v-if="day >= 8" />
|
||||||
<Tooltip :display="layers[layer ?? '']?.name ?? ''" :direction="Direction.Up" yoffset="5px">
|
<Tooltip :display="layers[layer ?? '']?.name ?? ''" :direction="Direction.Up" yoffset="5px">
|
||||||
|
@ -26,7 +26,7 @@
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else-if="visibility !== Visibility.None"
|
||||||
class="day feature dontMerge"
|
class="day feature dontMerge"
|
||||||
:class="{ can: canOpen, locked: !canOpen, canOpen, mastered: unref(mastered) }"
|
:class="{ can: canOpen, locked: !canOpen, canOpen, mastered: unref(mastered) }"
|
||||||
@click="tryUnlock"
|
@click="tryUnlock"
|
||||||
|
@ -47,6 +47,7 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Notif from "components/Notif.vue";
|
import Notif from "components/Notif.vue";
|
||||||
|
import { Visibility } from "features/feature";
|
||||||
import Tooltip from "features/tooltips/Tooltip.vue";
|
import Tooltip from "features/tooltips/Tooltip.vue";
|
||||||
import { layers } from "game/layers";
|
import { layers } from "game/layers";
|
||||||
import Decimal from "util/bignum";
|
import Decimal from "util/bignum";
|
||||||
|
@ -66,6 +67,7 @@ const props = defineProps<{
|
||||||
recentlyUpdated: Ref<boolean>;
|
recentlyUpdated: Ref<boolean>;
|
||||||
shouldNotify: ProcessedComputable<boolean>;
|
shouldNotify: ProcessedComputable<boolean>;
|
||||||
mastered: Ref<boolean>;
|
mastered: Ref<boolean>;
|
||||||
|
visibility?: Visibility;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
|
|
@ -19,6 +19,12 @@
|
||||||
class="scene-item"
|
class="scene-item"
|
||||||
style="left: 26%; bottom: 12%; width: 40px; height: 40px"
|
style="left: 26%; bottom: 12%; width: 40px; height: 40px"
|
||||||
/>
|
/>
|
||||||
|
<img
|
||||||
|
v-if="day >= 21"
|
||||||
|
:src="sleigh"
|
||||||
|
class="scene-item"
|
||||||
|
style="left: 10%; bottom: 56%; transform: rotate(24deg); width: 100px; height: 100px"
|
||||||
|
/>
|
||||||
<img
|
<img
|
||||||
v-if="day >= 12"
|
v-if="day >= 12"
|
||||||
:src="advManagement"
|
:src="advManagement"
|
||||||
|
@ -52,6 +58,12 @@
|
||||||
class="scene-item"
|
class="scene-item"
|
||||||
style="left: 72%; bottom: 8%; width: 40px; height: 40px"
|
style="left: 72%; bottom: 8%; width: 40px; height: 40px"
|
||||||
/>
|
/>
|
||||||
|
<img
|
||||||
|
v-if="day >= 22"
|
||||||
|
:src="routing"
|
||||||
|
class="scene-item"
|
||||||
|
style="left: 76%; bottom: 4%; width: 40px; height: 40px"
|
||||||
|
/>
|
||||||
<img v-if="day >= 8" :src="oil" class="scene-item" style="left: 80%; bottom: 6%" />
|
<img v-if="day >= 8" :src="oil" class="scene-item" style="left: 80%; bottom: 6%" />
|
||||||
<div
|
<div
|
||||||
v-if="day >= 4"
|
v-if="day >= 4"
|
||||||
|
@ -102,6 +114,8 @@ import factory from "./symbols/gears.png";
|
||||||
import advFactory from "./symbols/teddyBear.png";
|
import advFactory from "./symbols/teddyBear.png";
|
||||||
import presents from "./symbols/presents.png";
|
import presents from "./symbols/presents.png";
|
||||||
import reindeer from "./symbols/reindeer.png";
|
import reindeer from "./symbols/reindeer.png";
|
||||||
|
import sleigh from "./symbols/sleigh.png";
|
||||||
|
import routing from "./symbols/gps.png";
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
day: number;
|
day: number;
|
||||||
|
|
|
@ -247,9 +247,9 @@ export function createLayerTreeNode<T extends LayerTreeNodeOptions>(
|
||||||
/** An option object for a modifier display as a single section. **/
|
/** An option object for a modifier display as a single section. **/
|
||||||
export interface Section {
|
export interface Section {
|
||||||
/** The header for this modifier. **/
|
/** The header for this modifier. **/
|
||||||
title: string;
|
title: Computable<string>;
|
||||||
/** A subtitle for this modifier, e.g. to explain the context for the modifier. **/
|
/** A subtitle for this modifier, e.g. to explain the context for the modifier. **/
|
||||||
subtitle?: string;
|
subtitle?: Computable<string>;
|
||||||
/** The modifier to be displaying in this section. **/
|
/** The modifier to be displaying in this section. **/
|
||||||
modifier: WithRequired<Modifier, "description">;
|
modifier: WithRequired<Modifier, "description">;
|
||||||
/** The base value being modified. **/
|
/** The base value being modified. **/
|
||||||
|
@ -276,6 +276,8 @@ export function createCollapsibleModifierSections(
|
||||||
base: ProcessedComputable<DecimalSource | undefined>[];
|
base: ProcessedComputable<DecimalSource | undefined>[];
|
||||||
baseText: ProcessedComputable<CoercableComponent | undefined>[];
|
baseText: ProcessedComputable<CoercableComponent | undefined>[];
|
||||||
visible: ProcessedComputable<boolean | undefined>[];
|
visible: ProcessedComputable<boolean | undefined>[];
|
||||||
|
title: ProcessedComputable<string | undefined>[];
|
||||||
|
subtitle: ProcessedComputable<string | undefined>[];
|
||||||
}
|
}
|
||||||
| Record<string, never> = {};
|
| Record<string, never> = {};
|
||||||
let calculated = false;
|
let calculated = false;
|
||||||
|
@ -285,6 +287,8 @@ export function createCollapsibleModifierSections(
|
||||||
processed.base = sections.map(s => convertComputable(s.base));
|
processed.base = sections.map(s => convertComputable(s.base));
|
||||||
processed.baseText = sections.map(s => convertComputable(s.baseText));
|
processed.baseText = sections.map(s => convertComputable(s.baseText));
|
||||||
processed.visible = sections.map(s => convertComputable(s.visible));
|
processed.visible = sections.map(s => convertComputable(s.visible));
|
||||||
|
processed.title = sections.map(s => convertComputable(s.title));
|
||||||
|
processed.subtitle = sections.map(s => convertComputable(s.subtitle));
|
||||||
calculated = true;
|
calculated = true;
|
||||||
}
|
}
|
||||||
return sections;
|
return sections;
|
||||||
|
@ -307,8 +311,10 @@ export function createCollapsibleModifierSections(
|
||||||
>
|
>
|
||||||
▼
|
▼
|
||||||
</span>
|
</span>
|
||||||
{s.title}
|
{unref(processed.title[i])}
|
||||||
{s.subtitle != null ? <span class="subtitle"> ({s.subtitle})</span> : null}
|
{unref(processed.subtitle[i]) != null ? (
|
||||||
|
<span class="subtitle"> ({unref(processed.subtitle[i])})</span>
|
||||||
|
) : null}
|
||||||
</h3>
|
</h3>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -101,9 +101,6 @@ const toyGoal = 750;
|
||||||
const advancedToyGoal = 1500;
|
const advancedToyGoal = 1500;
|
||||||
const presentsGoal = 8e9;
|
const presentsGoal = 8e9;
|
||||||
|
|
||||||
// 20x20 block size
|
|
||||||
// TODO: unhardcode stuff
|
|
||||||
|
|
||||||
function roundDownTo(num: number, multiple: number) {
|
function roundDownTo(num: number, multiple: number) {
|
||||||
return Math.floor((num + multiple / 2) / multiple) * multiple;
|
return Math.floor((num + multiple / 2) / multiple) * multiple;
|
||||||
}
|
}
|
||||||
|
@ -508,8 +505,7 @@ const factory = createLayer(id, () => {
|
||||||
plank: {
|
plank: {
|
||||||
amount: computed(() => (upgrades[0][0].bought.value ? 2 : 1))
|
amount: computed(() => (upgrades[0][0].bought.value ? 2 : 1))
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
visible: main.days[presentsDay - 1].opened
|
|
||||||
} as FactoryComponentDeclaration,
|
} as FactoryComponentDeclaration,
|
||||||
thread: {
|
thread: {
|
||||||
imageSrc: _threadMaker,
|
imageSrc: _threadMaker,
|
||||||
|
@ -671,7 +667,8 @@ const factory = createLayer(id, () => {
|
||||||
box: {
|
box: {
|
||||||
amount: 2
|
amount: 2
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
visible: main.days[presentsDay - 1].opened
|
||||||
} as FactoryComponentDeclaration,
|
} as FactoryComponentDeclaration,
|
||||||
blocks: {
|
blocks: {
|
||||||
imageSrc: _blockMaker,
|
imageSrc: _blockMaker,
|
||||||
|
@ -1641,6 +1638,7 @@ const factory = createLayer(id, () => {
|
||||||
const compData = _compData as FactoryInternalProcessor;
|
const compData = _compData as FactoryInternalProcessor;
|
||||||
// factory part
|
// factory part
|
||||||
// PRODUCTION
|
// PRODUCTION
|
||||||
|
data.ticksDone += factoryTicks;
|
||||||
if (data.ticksDone >= factoryData.tick) {
|
if (data.ticksDone >= factoryData.tick) {
|
||||||
if (compData.canProduce.value) {
|
if (compData.canProduce.value) {
|
||||||
const cyclesDone = Math.floor(data.ticksDone / factoryData.tick);
|
const cyclesDone = Math.floor(data.ticksDone / factoryData.tick);
|
||||||
|
@ -1678,8 +1676,6 @@ const factory = createLayer(id, () => {
|
||||||
if (compData.lastProdTimes.length > 10) compData.lastProdTimes.shift();
|
if (compData.lastProdTimes.length > 10) compData.lastProdTimes.shift();
|
||||||
compData.lastFactoryProd = now;
|
compData.lastFactoryProd = now;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
data.ticksDone += factoryTicks;
|
|
||||||
}
|
}
|
||||||
// now look at each component direction and see if it accepts items coming in
|
// now look at each component direction and see if it accepts items coming in
|
||||||
// components are 1x1 so simple math for now
|
// components are 1x1 so simple math for now
|
||||||
|
@ -2247,12 +2243,12 @@ const factory = createLayer(id, () => {
|
||||||
style={{
|
style={{
|
||||||
color:
|
color:
|
||||||
(compInternalHovered.value as FactoryInternalProcessor)
|
(compInternalHovered.value as FactoryInternalProcessor)
|
||||||
.average.value! > 1
|
.average.value! >= 0.995
|
||||||
? "purple"
|
? "fuchsia"
|
||||||
: (
|
: (
|
||||||
compInternalHovered.value as FactoryInternalProcessor
|
compInternalHovered.value as FactoryInternalProcessor
|
||||||
).average.value! >= 0.9
|
).average.value! >= 0.9
|
||||||
? "green"
|
? "lime"
|
||||||
: (
|
: (
|
||||||
compInternalHovered.value as FactoryInternalProcessor
|
compInternalHovered.value as FactoryInternalProcessor
|
||||||
).average.value! >= 0.5
|
).average.value! >= 0.5
|
||||||
|
|
|
@ -38,6 +38,7 @@ import workshop from "./workshop";
|
||||||
import wrappingPaper from "./wrapping-paper";
|
import wrappingPaper from "./wrapping-paper";
|
||||||
import toys from "./toys";
|
import toys from "./toys";
|
||||||
import reindeer from "./reindeer";
|
import reindeer from "./reindeer";
|
||||||
|
import sleigh from "./sleigh";
|
||||||
|
|
||||||
const id = "metal";
|
const id = "metal";
|
||||||
const day = 7;
|
const day = 7;
|
||||||
|
@ -111,6 +112,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
|
||||||
exponent: 1.1,
|
exponent: 1.1,
|
||||||
description: "Mary Level 2",
|
description: "Mary Level 2",
|
||||||
enabled: management.elfTraining.heatedPlanterElfTraining.milestones[1].earned
|
enabled: management.elfTraining.heatedPlanterElfTraining.milestones[1].earned
|
||||||
|
})),
|
||||||
|
createExponentialModifier(() => ({
|
||||||
|
exponent: 1.2,
|
||||||
|
description: "100% Sleigh Fixed",
|
||||||
|
enabled: sleigh.milestones.milestone8.earned
|
||||||
}))
|
}))
|
||||||
]);
|
]);
|
||||||
const computedOrePurity = computed(() => orePurity.apply(0.1));
|
const computedOrePurity = computed(() => orePurity.apply(0.1));
|
||||||
|
@ -182,6 +188,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
|
||||||
description: "Jazzy Wrapping Paper",
|
description: "Jazzy Wrapping Paper",
|
||||||
enabled: computed(() => Decimal.gt(wrappingPaper.boosts.jazzy1.value, 1))
|
enabled: computed(() => Decimal.gt(wrappingPaper.boosts.jazzy1.value, 1))
|
||||||
})),
|
})),
|
||||||
|
createMultiplicativeModifier(() => ({
|
||||||
|
multiplier: 2,
|
||||||
|
description: "30% Sleigh Fixed",
|
||||||
|
enabled: sleigh.milestones.milestone4.earned
|
||||||
|
})),
|
||||||
createAdditiveModifier(() => ({
|
createAdditiveModifier(() => ({
|
||||||
addend: () => Decimal.sub(lastOreGained.value, lastOreSmelted.value).max(0),
|
addend: () => Decimal.sub(lastOreGained.value, lastOreSmelted.value).max(0),
|
||||||
description: "Metal Decoration",
|
description: "Metal Decoration",
|
||||||
|
@ -291,6 +302,11 @@ const layer = createLayer(id, function (this: BaseLayer) {
|
||||||
description: "1000 Letters Processed",
|
description: "1000 Letters Processed",
|
||||||
enabled: letters.milestones.miningMilestone.earned
|
enabled: letters.milestones.miningMilestone.earned
|
||||||
})),
|
})),
|
||||||
|
createMultiplicativeModifier(() => ({
|
||||||
|
multiplier: 2,
|
||||||
|
description: "30% Sleigh Fixed",
|
||||||
|
enabled: sleigh.milestones.milestone4.earned
|
||||||
|
})),
|
||||||
createMultiplicativeModifier(() => ({
|
createMultiplicativeModifier(() => ({
|
||||||
multiplier: () => Decimal.add(toys.clothes.value, 1),
|
multiplier: () => Decimal.add(toys.clothes.value, 1),
|
||||||
description: "Give elves clothes to wear",
|
description: "Give elves clothes to wear",
|
||||||
|
|
|
@ -38,6 +38,7 @@ import paper from "./paper";
|
||||||
import workshop from "./workshop";
|
import workshop from "./workshop";
|
||||||
import toys from "./toys";
|
import toys from "./toys";
|
||||||
import reindeer from "./reindeer";
|
import reindeer from "./reindeer";
|
||||||
|
import sleigh from "./sleigh";
|
||||||
|
|
||||||
const id = "plastic";
|
const id = "plastic";
|
||||||
const day = 10;
|
const day = 10;
|
||||||
|
@ -320,6 +321,14 @@ const layer = createLayer(id, function (this: BaseLayer) {
|
||||||
description: "Oil Refinery",
|
description: "Oil Refinery",
|
||||||
enabled: () => Decimal.gt(activeRefinery.value, 0)
|
enabled: () => Decimal.gt(activeRefinery.value, 0)
|
||||||
})),
|
})),
|
||||||
|
createAdditiveModifier(() => ({
|
||||||
|
addend: () =>
|
||||||
|
management.elfTraining.oilElfTraining.milestones[3].earned.value
|
||||||
|
? Decimal.times(Decimal.div(sleigh.sleighProgress.value.value,2).floor(), 200)
|
||||||
|
: Decimal.times(activeRefinery.value, 40),
|
||||||
|
description: "75% Sleigh Fixed",
|
||||||
|
enabled: sleigh.milestones.milestone7.earned
|
||||||
|
})),
|
||||||
createMultiplicativeModifier(() => ({
|
createMultiplicativeModifier(() => ({
|
||||||
multiplier: 2,
|
multiplier: 2,
|
||||||
description: "Paper Elf Recruitment",
|
description: "Paper Elf Recruitment",
|
||||||
|
@ -385,6 +394,16 @@ const layer = createLayer(id, function (this: BaseLayer) {
|
||||||
multiplier: () => dyes.boosts.white1.value,
|
multiplier: () => dyes.boosts.white1.value,
|
||||||
description: "White Dye Boost"
|
description: "White Dye Boost"
|
||||||
})),
|
})),
|
||||||
|
createMultiplicativeModifier(() => ({
|
||||||
|
multiplier: () => Decimal.div(sleigh.sleighProgress.value.value, 5).floor().mul(0.05).add(1),
|
||||||
|
description: "20% Sleigh Fixed",
|
||||||
|
enabled: sleigh.milestones.milestone3.earned
|
||||||
|
})),
|
||||||
|
createMultiplicativeModifier(() => ({
|
||||||
|
multiplier: 4,
|
||||||
|
description: "40% Sleigh Fixed",
|
||||||
|
enabled: sleigh.milestones.milestone5.earned
|
||||||
|
})),
|
||||||
reindeer.reindeer.blitzen.modifier
|
reindeer.reindeer.blitzen.modifier
|
||||||
]);
|
]);
|
||||||
const computedPlasticGain = computed(() => plasticGain.apply(0));
|
const computedPlasticGain = computed(() => plasticGain.apply(0));
|
||||||
|
|
|
@ -74,6 +74,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
|
||||||
const computedMaxCooldown = computed(() => 10);
|
const computedMaxCooldown = computed(() => 10);
|
||||||
|
|
||||||
function focus() {
|
function focus() {
|
||||||
|
currCooldown.value = Decimal.fromValue(computedMaxCooldown.value).toNumber();
|
||||||
let targetsSelected = 0;
|
let targetsSelected = 0;
|
||||||
currTargets.value = {};
|
currTargets.value = {};
|
||||||
timeSinceFocus.value = 0;
|
timeSinceFocus.value = 0;
|
||||||
|
@ -141,7 +142,6 @@ const layer = createLayer(id, function (this: BaseLayer) {
|
||||||
},
|
},
|
||||||
canClick: () => Decimal.eq(currCooldown.value, 0),
|
canClick: () => Decimal.eq(currCooldown.value, 0),
|
||||||
onClick() {
|
onClick() {
|
||||||
currCooldown.value = Decimal.fromValue(computedMaxCooldown.value).toNumber();
|
|
||||||
focus();
|
focus();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
@ -488,7 +488,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
|
||||||
let auto = false;
|
let auto = false;
|
||||||
if (upgrade7.bought.value) {
|
if (upgrade7.bought.value) {
|
||||||
timeSinceFocus.value += diff;
|
timeSinceFocus.value += diff;
|
||||||
if (timeSinceFocus.value > 10) {
|
if (timeSinceFocus.value > 20) {
|
||||||
auto = true;
|
auto = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
870
src/data/layers/routing.tsx
Normal file
870
src/data/layers/routing.tsx
Normal file
|
@ -0,0 +1,870 @@
|
||||||
|
/**
|
||||||
|
* @module
|
||||||
|
* @hidden
|
||||||
|
*/
|
||||||
|
import Spacer from "components/layout/Spacer.vue";
|
||||||
|
import Modal from "components/Modal.vue";
|
||||||
|
import { createCollapsibleMilestones, createCollapsibleModifierSections } from "data/common";
|
||||||
|
import { main } from "data/projEntry";
|
||||||
|
import { createBar, GenericBar } from "features/bars/bar";
|
||||||
|
import { BoardNode, BoardNodeLink, createBoard, Shape } from "features/boards/board";
|
||||||
|
import { createClickable } from "features/clickables/clickable";
|
||||||
|
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 { BaseLayer, createLayer } from "game/layers";
|
||||||
|
import {
|
||||||
|
createAdditiveModifier,
|
||||||
|
createExponentialModifier,
|
||||||
|
createMultiplicativeModifier,
|
||||||
|
createSequentialModifier
|
||||||
|
} from "game/modifiers";
|
||||||
|
import { persistent } from "game/persistence";
|
||||||
|
import Decimal, { DecimalSource, format, formatWhole } from "util/bignum";
|
||||||
|
import { Direction } from "util/common";
|
||||||
|
import { render, renderRow } from "util/vue";
|
||||||
|
import { computed, ComputedRef, ref, unref, watchEffect } from "vue";
|
||||||
|
import "./styles/routing.css";
|
||||||
|
|
||||||
|
const alpha = [
|
||||||
|
"A",
|
||||||
|
"B",
|
||||||
|
"C",
|
||||||
|
"D",
|
||||||
|
"E",
|
||||||
|
"F",
|
||||||
|
"G",
|
||||||
|
"H",
|
||||||
|
"I",
|
||||||
|
"J",
|
||||||
|
"K",
|
||||||
|
"L",
|
||||||
|
"M",
|
||||||
|
"N",
|
||||||
|
"O",
|
||||||
|
"P",
|
||||||
|
"Q",
|
||||||
|
"R",
|
||||||
|
"S",
|
||||||
|
"T",
|
||||||
|
"U",
|
||||||
|
"V",
|
||||||
|
"W",
|
||||||
|
"X",
|
||||||
|
"Y",
|
||||||
|
"Z"
|
||||||
|
];
|
||||||
|
|
||||||
|
const id = "routing";
|
||||||
|
const day = 23;
|
||||||
|
const layer = createLayer(id, function (this: BaseLayer) {
|
||||||
|
const name = "Routing";
|
||||||
|
const color = "navajowhite";
|
||||||
|
|
||||||
|
const citiesGoal = 10;
|
||||||
|
|
||||||
|
const citiesCompleted = createResource<DecimalSource>(0, "countries solved");
|
||||||
|
const currentCity = persistent<number[][]>([]);
|
||||||
|
const routeIndex = persistent<number>(0);
|
||||||
|
const checkRouteProgress = persistent<number>(0);
|
||||||
|
const redundanciesRemoved = persistent<number>(0);
|
||||||
|
|
||||||
|
const currentRoutes = computed(() => {
|
||||||
|
// Manually check milestone req here due to calling generateCity() before milestones get earned
|
||||||
|
if (Decimal.gte(citiesCompleted.value, 7)) {
|
||||||
|
return Decimal.factorial(currentCity.value.length).div(2).toNumber();
|
||||||
|
}
|
||||||
|
// Permutation code from https://stackoverflow.com/a/37580979
|
||||||
|
const length = currentCity.value.length;
|
||||||
|
const permutation = new Array(length).fill(0).map((_, i) => i);
|
||||||
|
const result = [permutation.slice()];
|
||||||
|
const c = new Array(length).fill(0);
|
||||||
|
let i = 1;
|
||||||
|
|
||||||
|
while (i < length) {
|
||||||
|
if (c[i] < i) {
|
||||||
|
const k = i % 2 && c[i];
|
||||||
|
const p = permutation[i];
|
||||||
|
permutation[i] = permutation[k];
|
||||||
|
permutation[k] = p;
|
||||||
|
++c[i];
|
||||||
|
i = 1;
|
||||||
|
result.push(permutation.slice());
|
||||||
|
} else {
|
||||||
|
c[i] = 0;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
const redundantRoutes = computed(() => {
|
||||||
|
const routes = currentRoutes.value;
|
||||||
|
if (typeof routes === "number") {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const redundancies = [];
|
||||||
|
for (let i = 0; i < routes.length; i++) {
|
||||||
|
if (routes[i][0] > routes[i][1]) {
|
||||||
|
redundancies.push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return redundancies;
|
||||||
|
});
|
||||||
|
const routesToSkip = persistent<number[]>([]);
|
||||||
|
|
||||||
|
const currentRoute: ComputedRef<number[] | number | undefined> = computed(() =>
|
||||||
|
typeof currentRoutes.value === "number"
|
||||||
|
? currentCity.value.length
|
||||||
|
: currentRoutes.value[routeIndex.value]
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentRouteDuration = computed(() => {
|
||||||
|
const route = currentRoute.value;
|
||||||
|
if (route == null) {
|
||||||
|
return 0;
|
||||||
|
} else if (typeof route === "number") {
|
||||||
|
return Decimal.times(route, computedMinWeight.value).floor().toNumber();
|
||||||
|
}
|
||||||
|
let duration = 0;
|
||||||
|
for (let i = 0; i < route.length - 1; i++) {
|
||||||
|
duration += currentCity.value[route[i]][route[i + 1]];
|
||||||
|
}
|
||||||
|
return duration;
|
||||||
|
});
|
||||||
|
|
||||||
|
globalBus.on("onLoad", () => {
|
||||||
|
if (currentCity.value.length === 0) {
|
||||||
|
generateCity();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function stringifyRoute(route: number[]) {
|
||||||
|
return route
|
||||||
|
.map(h => (city.types.house.title as (node: BoardNode) => string)(city.nodes.value[h]))
|
||||||
|
.join("->");
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateCity() {
|
||||||
|
const numHouses = new Decimal(computedHouses.value).clampMin(3).toNumber();
|
||||||
|
const min = computedMinWeight.value;
|
||||||
|
const max = milestone6.earned.value ? min : computedMaxWeight.value;
|
||||||
|
const diff = Decimal.sub(max, min);
|
||||||
|
const city: number[][] = [];
|
||||||
|
for (let i = 0; i < numHouses; i++) {
|
||||||
|
const house: number[] = [];
|
||||||
|
for (let j = 0; j < numHouses; j++) {
|
||||||
|
if (i === j) {
|
||||||
|
house.push(0);
|
||||||
|
} else if (j < i) {
|
||||||
|
house.push(city[j][i]);
|
||||||
|
} else {
|
||||||
|
house.push(Decimal.times(diff, Math.random()).add(min).floor().toNumber());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
city.push(house);
|
||||||
|
}
|
||||||
|
currentCity.value = city;
|
||||||
|
routeIndex.value = 0;
|
||||||
|
redundanciesRemoved.value = Decimal.gte(citiesCompleted.value, 7)
|
||||||
|
? Decimal.factorial(currentCity.value.length).div(2).toNumber()
|
||||||
|
: 0;
|
||||||
|
routesToSkip.value = [];
|
||||||
|
getNextRoute();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNextRoute() {
|
||||||
|
const numRoutes =
|
||||||
|
typeof currentRoutes.value === "number"
|
||||||
|
? currentRoutes.value
|
||||||
|
: currentRoutes.value.length;
|
||||||
|
while (routeIndex.value <= numRoutes && routesToSkip.value.includes(routeIndex.value)) {
|
||||||
|
routeIndex.value++;
|
||||||
|
}
|
||||||
|
if (routeIndex.value >= numRoutes) {
|
||||||
|
citiesCompleted.value = Decimal.add(citiesCompleted.value, 1);
|
||||||
|
generateCity();
|
||||||
|
} else {
|
||||||
|
if (redundantRoutes.value.includes(routeIndex.value)) {
|
||||||
|
routesToSkip.value = [...routesToSkip.value, routeIndex.value];
|
||||||
|
}
|
||||||
|
checkRouteProgress.value = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const newCityProgress = persistent<DecimalSource>(0);
|
||||||
|
const newCityProgressBar = 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(newCityProgress.value, 10)
|
||||||
|
}));
|
||||||
|
const getNewCity = createClickable(() => ({
|
||||||
|
display: {
|
||||||
|
description: jsx(() => (
|
||||||
|
<>
|
||||||
|
Generate New Country
|
||||||
|
<br />
|
||||||
|
{render(newCityProgressBar)}
|
||||||
|
</>
|
||||||
|
))
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
minHeight: "40px",
|
||||||
|
"--layer-color": "var(--danger)"
|
||||||
|
},
|
||||||
|
canClick: () => Decimal.gte(newCityProgress.value, 10),
|
||||||
|
onClick() {
|
||||||
|
if (!unref(getNewCity.canClick)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
generateCity();
|
||||||
|
newCityProgress.value = 0;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
const boostProgress = persistent<DecimalSource>(0);
|
||||||
|
const boostProgressBar = 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(boostProgress.value, computedManualCooldown.value)
|
||||||
|
}));
|
||||||
|
const boost = createClickable(() => ({
|
||||||
|
display: {
|
||||||
|
description: jsx(() => (
|
||||||
|
<>
|
||||||
|
Perform {formatWhole(computedManualBoost.value)} units of work
|
||||||
|
<br />
|
||||||
|
{render(boostProgressBar)}
|
||||||
|
</>
|
||||||
|
))
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
minHeight: "40px"
|
||||||
|
},
|
||||||
|
canClick: () => Decimal.gte(boostProgress.value, computedManualCooldown.value),
|
||||||
|
onClick() {
|
||||||
|
if (!unref(boost.canClick)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
checkRouteProgress.value = Decimal.add(
|
||||||
|
checkRouteProgress.value,
|
||||||
|
computedManualBoost.value
|
||||||
|
).toNumber();
|
||||||
|
boostProgress.value = 0;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
const redundantProgress = persistent<DecimalSource>(0);
|
||||||
|
const redundantProgressBar = 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(redundantProgress.value, computedRedundantCooldown.value)
|
||||||
|
}));
|
||||||
|
const removeRedundantRoute = createClickable(() => ({
|
||||||
|
display: {
|
||||||
|
description: jsx(() => (
|
||||||
|
<>
|
||||||
|
Remove a redundant route from the list to check
|
||||||
|
<br />
|
||||||
|
{render(redundantProgressBar)}
|
||||||
|
</>
|
||||||
|
))
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
minHeight: "40px"
|
||||||
|
},
|
||||||
|
visibility: () => showIf(!milestone7.earned.value),
|
||||||
|
canClick: () =>
|
||||||
|
Decimal.gte(redundantProgress.value, computedRedundantCooldown.value) &&
|
||||||
|
routesToSkip.value.length < redundantRoutes.value.length,
|
||||||
|
onClick() {
|
||||||
|
if (!unref(removeRedundantRoute.canClick)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
routesToSkip.value = [
|
||||||
|
...routesToSkip.value,
|
||||||
|
redundantRoutes.value[routesToSkip.value.length]
|
||||||
|
];
|
||||||
|
redundantProgress.value = 0;
|
||||||
|
redundanciesRemoved.value++;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
const city = createBoard(() => ({
|
||||||
|
startNodes: () => [],
|
||||||
|
types: {
|
||||||
|
house: {
|
||||||
|
shape: Shape.Circle,
|
||||||
|
fillColor: "var(--highlighted)",
|
||||||
|
outlineColor: "var(--accent1)",
|
||||||
|
size: 20,
|
||||||
|
title(node) {
|
||||||
|
let letter = node.state as number;
|
||||||
|
let name = "";
|
||||||
|
while (true) {
|
||||||
|
if (letter < 26) {
|
||||||
|
name += alpha[letter];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let thisLetter = letter;
|
||||||
|
let iterations = 0;
|
||||||
|
while (Math.floor(thisLetter / 26) - 1 >= 0) {
|
||||||
|
thisLetter = Math.floor(thisLetter / 26) - 1;
|
||||||
|
iterations++;
|
||||||
|
}
|
||||||
|
name += alpha[thisLetter];
|
||||||
|
|
||||||
|
let amountToDecrement = thisLetter + 1;
|
||||||
|
for (let i = 0; i < iterations; i++) {
|
||||||
|
amountToDecrement *= 26;
|
||||||
|
}
|
||||||
|
letter -= amountToDecrement;
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
width: "600px",
|
||||||
|
height: "600px",
|
||||||
|
style: {
|
||||||
|
background: "var(--raised-background)",
|
||||||
|
borderRadius: "var(--border-radius) var(--border-radius) 0 0",
|
||||||
|
boxShadow: "0 2px 10px rgb(0 0 0 / 50%)"
|
||||||
|
},
|
||||||
|
state: computed(() => {
|
||||||
|
const nodes: BoardNode[] = [];
|
||||||
|
const city = currentCity.value;
|
||||||
|
const rows = Math.ceil(Math.sqrt(city.length));
|
||||||
|
const cols = Math.ceil(city.length / rows);
|
||||||
|
for (let i = 0; i < city.length; i++) {
|
||||||
|
const row = Math.floor(i / rows);
|
||||||
|
const col = Math.floor(i % rows);
|
||||||
|
const randomOffsetIndex = i + new Decimal(citiesCompleted.value).toNumber();
|
||||||
|
nodes.push({
|
||||||
|
id: i,
|
||||||
|
position: {
|
||||||
|
x: 160 * (-(cols - 1) / 2 + col) + Math.cos(randomOffsetIndex) * 40,
|
||||||
|
y: 160 * (-(rows - 1) / 2 + row) + Math.sin(randomOffsetIndex) * 40
|
||||||
|
},
|
||||||
|
type: "house",
|
||||||
|
state: i
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
nodes,
|
||||||
|
selectedNode: null,
|
||||||
|
selectedAction: null
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
links() {
|
||||||
|
const links: BoardNodeLink[] = [];
|
||||||
|
const route = currentRoute.value;
|
||||||
|
if (route == null) {
|
||||||
|
return links;
|
||||||
|
}
|
||||||
|
const citySize = currentCity.value.length;
|
||||||
|
let completedLegs = 0;
|
||||||
|
let progress = 0;
|
||||||
|
let partialLeg = 0;
|
||||||
|
if (typeof route !== "number") {
|
||||||
|
for (let i = 0; i < route.length - 1; i++) {
|
||||||
|
const weight = progress + currentCity.value[route[i]][route[i + 1]];
|
||||||
|
if (checkRouteProgress.value > weight) {
|
||||||
|
completedLegs++;
|
||||||
|
progress = weight;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
partialLeg =
|
||||||
|
(checkRouteProgress.value - progress) /
|
||||||
|
currentCity.value[route[completedLegs]][route[completedLegs + 1]];
|
||||||
|
}
|
||||||
|
for (let i = 0; i < citySize; i++) {
|
||||||
|
for (let j = 0; j < citySize; j++) {
|
||||||
|
if (i !== j) {
|
||||||
|
let progress = 0;
|
||||||
|
if (typeof route !== "number") {
|
||||||
|
const iIndex = route.indexOf(i);
|
||||||
|
const jIndex = route.indexOf(j);
|
||||||
|
if (
|
||||||
|
iIndex >= 0 &&
|
||||||
|
jIndex >= 0 &&
|
||||||
|
(route[iIndex + 1] === j || route[jIndex + 1] === i)
|
||||||
|
) {
|
||||||
|
if (jIndex < completedLegs || iIndex < completedLegs) {
|
||||||
|
progress = 1;
|
||||||
|
} else if (jIndex === completedLegs || iIndex === completedLegs) {
|
||||||
|
progress = partialLeg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
links.push({
|
||||||
|
style: "transition-duration: 0s",
|
||||||
|
startNode: city.nodes.value[i],
|
||||||
|
endNode: city.nodes.value[j],
|
||||||
|
stroke: `rgb(${progress * 255}, 0, 0)`,
|
||||||
|
"stroke-width": 2 * progress + 2,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
weight: i < j ? null : currentCity.value[i][j]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return links;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
const checkRouteProgressBar = createBar(() => ({
|
||||||
|
direction: Direction.Right,
|
||||||
|
width: 597,
|
||||||
|
height: 24,
|
||||||
|
style: {
|
||||||
|
borderRadius: "0 0 var(--border-radius) var(--border-radius)",
|
||||||
|
background: "var(--raised-background)",
|
||||||
|
marginTop: "-24px"
|
||||||
|
},
|
||||||
|
borderStyle: {
|
||||||
|
borderRadius: "0 0 var(--border-radius) var(--border-radius)",
|
||||||
|
borderColor: "var(--outline)",
|
||||||
|
marginTop: "unset"
|
||||||
|
},
|
||||||
|
fillStyle: {
|
||||||
|
background: "black",
|
||||||
|
marginTop: "unset"
|
||||||
|
},
|
||||||
|
progress() {
|
||||||
|
return Decimal.div(checkRouteProgress.value, currentRouteDuration.value);
|
||||||
|
},
|
||||||
|
display: jsx(() => (
|
||||||
|
<>
|
||||||
|
{Math.floor(checkRouteProgress.value)}/{currentRouteDuration.value}
|
||||||
|
</>
|
||||||
|
))
|
||||||
|
}));
|
||||||
|
|
||||||
|
const milestone1 = createMilestone(() => ({
|
||||||
|
display: {
|
||||||
|
requirement: "1 Country Solved",
|
||||||
|
effectDisplay: "Each country solved doubles manual and auto processing speed"
|
||||||
|
},
|
||||||
|
shouldEarn() {
|
||||||
|
return Decimal.gte(citiesCompleted.value, 1);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
const milestone2 = createMilestone(() => ({
|
||||||
|
display: {
|
||||||
|
requirement: "2 Countries Solved",
|
||||||
|
effectDisplay:
|
||||||
|
"Manually checking routes does additional work based on number of routes checked in this country"
|
||||||
|
},
|
||||||
|
shouldEarn() {
|
||||||
|
return Decimal.gte(citiesCompleted.value, 2);
|
||||||
|
},
|
||||||
|
visibility: () => showIf(milestone1.earned.value)
|
||||||
|
}));
|
||||||
|
const milestone3 = createMilestone(() => ({
|
||||||
|
display: {
|
||||||
|
requirement: "3 Countries Solved",
|
||||||
|
effectDisplay:
|
||||||
|
"Each country solved makes the cooldown for removing a redundant route 25% shorter"
|
||||||
|
},
|
||||||
|
shouldEarn() {
|
||||||
|
return Decimal.gte(citiesCompleted.value, 3);
|
||||||
|
},
|
||||||
|
visibility: () => showIf(milestone2.earned.value)
|
||||||
|
}));
|
||||||
|
const milestone4 = createMilestone(() => ({
|
||||||
|
display: {
|
||||||
|
requirement: "4 Countries Solved",
|
||||||
|
effectDisplay:
|
||||||
|
"Automatic processing speed is multiplied by the amount of redundant routes removed from this country"
|
||||||
|
},
|
||||||
|
shouldEarn() {
|
||||||
|
return Decimal.gte(citiesCompleted.value, 4);
|
||||||
|
},
|
||||||
|
visibility: () => showIf(milestone3.earned.value)
|
||||||
|
}));
|
||||||
|
const milestone5 = createMilestone(() => ({
|
||||||
|
display: {
|
||||||
|
requirement: "5 Countries Solved",
|
||||||
|
effectDisplay: "Remove 1 city"
|
||||||
|
},
|
||||||
|
shouldEarn() {
|
||||||
|
return Decimal.gte(citiesCompleted.value, 5);
|
||||||
|
},
|
||||||
|
onComplete() {
|
||||||
|
generateCity();
|
||||||
|
},
|
||||||
|
visibility: () => showIf(milestone4.earned.value)
|
||||||
|
}));
|
||||||
|
const milestone6 = createMilestone(() => ({
|
||||||
|
display: {
|
||||||
|
requirement: "6 Countries Solved",
|
||||||
|
effectDisplay:
|
||||||
|
"Lower max weight to the min weight, and uncap amount of routes that can be checked per tick"
|
||||||
|
},
|
||||||
|
shouldEarn() {
|
||||||
|
return Decimal.gte(citiesCompleted.value, 6);
|
||||||
|
},
|
||||||
|
visibility: () => showIf(milestone5.earned.value)
|
||||||
|
}));
|
||||||
|
const milestone7 = createMilestone(() => ({
|
||||||
|
display: {
|
||||||
|
requirement: "7 Countries Solved",
|
||||||
|
effectDisplay: "All redundancies are removed"
|
||||||
|
},
|
||||||
|
shouldEarn() {
|
||||||
|
return Decimal.gte(citiesCompleted.value, 7);
|
||||||
|
},
|
||||||
|
visibility: () => showIf(milestone6.earned.value)
|
||||||
|
}));
|
||||||
|
const milestones = {
|
||||||
|
milestone1,
|
||||||
|
milestone2,
|
||||||
|
milestone3,
|
||||||
|
milestone4,
|
||||||
|
milestone5,
|
||||||
|
milestone6,
|
||||||
|
milestone7
|
||||||
|
};
|
||||||
|
const { collapseMilestones, display: milestonesDisplay } =
|
||||||
|
createCollapsibleMilestones(milestones);
|
||||||
|
|
||||||
|
const houses = createSequentialModifier(() => [
|
||||||
|
createAdditiveModifier(() => ({
|
||||||
|
addend: citiesCompleted,
|
||||||
|
description: "Countries Completed"
|
||||||
|
})),
|
||||||
|
createAdditiveModifier(() => ({
|
||||||
|
addend: 1,
|
||||||
|
description: "5 Countries Completed",
|
||||||
|
enabled: milestone5.earned
|
||||||
|
}))
|
||||||
|
]);
|
||||||
|
const computedHouses = computed(() => houses.apply(3));
|
||||||
|
const maxWeight = createSequentialModifier(() => [
|
||||||
|
createAdditiveModifier(() => ({
|
||||||
|
addend: () => Decimal.pow(citiesCompleted.value, 1.1),
|
||||||
|
description: "Countries Completed"
|
||||||
|
}))
|
||||||
|
]);
|
||||||
|
const computedMaxWeight = computed(() => maxWeight.apply(10));
|
||||||
|
const minWeight = createSequentialModifier(() => [
|
||||||
|
createAdditiveModifier(() => ({
|
||||||
|
addend: citiesCompleted,
|
||||||
|
description: "Countries Completed"
|
||||||
|
})),
|
||||||
|
createExponentialModifier(() => ({
|
||||||
|
exponent: 3,
|
||||||
|
description: "Countries Completed",
|
||||||
|
enabled: milestone7.earned
|
||||||
|
}))
|
||||||
|
]);
|
||||||
|
const computedMinWeight = computed(() => minWeight.apply(2));
|
||||||
|
const manualBoost = createSequentialModifier(() => [
|
||||||
|
createAdditiveModifier(() => ({
|
||||||
|
addend: () => Decimal.add(routeIndex.value, 1).sqrt(),
|
||||||
|
description: "2 Countries Solved",
|
||||||
|
enabled: milestone2.earned
|
||||||
|
}))
|
||||||
|
]);
|
||||||
|
const computedManualBoost = computed(() => manualBoost.apply(1));
|
||||||
|
const manualCooldown = createSequentialModifier(() => [
|
||||||
|
createMultiplicativeModifier(() => ({
|
||||||
|
multiplier: () => Decimal.pow(0.5, citiesCompleted.value),
|
||||||
|
description: "1 Country Solved",
|
||||||
|
enabled: milestone1.earned
|
||||||
|
}))
|
||||||
|
]);
|
||||||
|
const computedManualCooldown = computed(() => manualCooldown.apply(1));
|
||||||
|
const redundantCooldown = createSequentialModifier(() => [
|
||||||
|
createMultiplicativeModifier(() => ({
|
||||||
|
multiplier: () => Decimal.pow(0.75, citiesCompleted.value),
|
||||||
|
description: "3 Countries Solved",
|
||||||
|
enabled: milestone3.earned
|
||||||
|
}))
|
||||||
|
]);
|
||||||
|
const computedRedundantCooldown = computed(() => redundantCooldown.apply(10));
|
||||||
|
const autoProcessing = createSequentialModifier(() => [
|
||||||
|
createMultiplicativeModifier(() => ({
|
||||||
|
multiplier: () => Decimal.pow(2, citiesCompleted.value),
|
||||||
|
description: "1 Country Solved",
|
||||||
|
enabled: milestone1.earned
|
||||||
|
})),
|
||||||
|
createMultiplicativeModifier(() => ({
|
||||||
|
multiplier: () => Decimal.add(redundanciesRemoved.value, 1),
|
||||||
|
description: "4 Countries Solved",
|
||||||
|
enabled: milestone4.earned
|
||||||
|
}))
|
||||||
|
]);
|
||||||
|
const computedAutoProcessing = computed(() => autoProcessing.apply(1));
|
||||||
|
|
||||||
|
const [generalTab, generalTabCollapsed] = createCollapsibleModifierSections(() => [
|
||||||
|
{
|
||||||
|
title: "Cities/country",
|
||||||
|
modifier: houses,
|
||||||
|
base: 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: () => (milestone6.earned.value ? "Weight" : "Minimum Weight"),
|
||||||
|
modifier: minWeight,
|
||||||
|
base: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Maximum Weight",
|
||||||
|
modifier: maxWeight,
|
||||||
|
base: 10,
|
||||||
|
visible: () => !milestone6.earned.value
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Manual Processing Amount",
|
||||||
|
modifier: manualBoost,
|
||||||
|
base: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Manual Processing Cooldown",
|
||||||
|
modifier: manualCooldown,
|
||||||
|
base: 1,
|
||||||
|
unit: "s"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Remove Redundant Route Cooldown",
|
||||||
|
modifier: redundantCooldown,
|
||||||
|
base: 10,
|
||||||
|
unit: "s"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Auto Processing Speed",
|
||||||
|
modifier: autoProcessing,
|
||||||
|
base: 1,
|
||||||
|
unit: "/s"
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
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.gte(newCityProgress.value, 10)) {
|
||||||
|
newCityProgress.value = 10;
|
||||||
|
} else {
|
||||||
|
newCityProgress.value = Decimal.add(newCityProgress.value, diff);
|
||||||
|
if (getNewCity.isHolding.value) {
|
||||||
|
getNewCity.onClick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Decimal.gte(boostProgress.value, computedManualCooldown.value)) {
|
||||||
|
boostProgress.value = computedManualCooldown.value;
|
||||||
|
} else {
|
||||||
|
boostProgress.value = Decimal.add(boostProgress.value, diff);
|
||||||
|
if (boost.isHolding.value) {
|
||||||
|
boost.onClick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Decimal.gte(redundantProgress.value, computedRedundantCooldown.value)) {
|
||||||
|
redundantProgress.value = computedRedundantCooldown.value;
|
||||||
|
} else {
|
||||||
|
redundantProgress.value = Decimal.add(redundantProgress.value, diff);
|
||||||
|
if (removeRedundantRoute.isHolding.value) {
|
||||||
|
removeRedundantRoute.onClick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkRouteProgress.value = Decimal.times(diff, computedAutoProcessing.value)
|
||||||
|
.add(checkRouteProgress.value)
|
||||||
|
.toNumber();
|
||||||
|
if (checkRouteProgress.value > currentRouteDuration.value) {
|
||||||
|
const overflow = checkRouteProgress.value - currentRouteDuration.value;
|
||||||
|
routeIndex.value++;
|
||||||
|
if (milestone6.earned.value && currentRoute.value != null) {
|
||||||
|
const length =
|
||||||
|
typeof currentRoute.value === "number"
|
||||||
|
? currentRoute.value
|
||||||
|
: currentRoute.value.length;
|
||||||
|
const extraRoutes = Decimal.div(
|
||||||
|
overflow,
|
||||||
|
Decimal.times(length, computedMinWeight.value)
|
||||||
|
)
|
||||||
|
.floor()
|
||||||
|
.toNumber();
|
||||||
|
routeIndex.value += extraRoutes;
|
||||||
|
}
|
||||||
|
getNextRoute();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const dayProgress = createBar(() => ({
|
||||||
|
direction: Direction.Right,
|
||||||
|
width: 600,
|
||||||
|
height: 25,
|
||||||
|
fillStyle: `backgroundColor: ${color}`,
|
||||||
|
textStyle: {
|
||||||
|
color: "var(--feature-foreground)"
|
||||||
|
},
|
||||||
|
progress: () =>
|
||||||
|
main.day.value === day ? Decimal.div(citiesCompleted.value, citiesGoal) : 1,
|
||||||
|
display: jsx(() =>
|
||||||
|
main.day.value === day ? (
|
||||||
|
<>
|
||||||
|
{formatWhole(citiesCompleted.value)}/{formatWhole(citiesGoal)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
})) as GenericBar;
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
if (main.day.value === day && Decimal.gte(citiesCompleted.value, citiesGoal)) {
|
||||||
|
main.completeDay();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function displayRoutes() {
|
||||||
|
if (currentRoute.value == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (typeof currentRoutes.value === "number") {
|
||||||
|
return (
|
||||||
|
<div class="routes-list">
|
||||||
|
{routeIndex.value > 0 ? (
|
||||||
|
<div class="checked">{formatWhole(routeIndex.value)} already checked</div>
|
||||||
|
) : null}
|
||||||
|
<div>
|
||||||
|
{formatWhole(currentRoutes.value - routeIndex.value)} routes left to check
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (typeof currentRoutes.value === "number") {
|
||||||
|
console.error("Something went horribly wrong");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const routes = currentRoutes.value.slice();
|
||||||
|
let showPrevious = false;
|
||||||
|
let showNext = 0;
|
||||||
|
if (routes.length > 6) {
|
||||||
|
routes.splice(0, routeIndex.value);
|
||||||
|
showPrevious = true;
|
||||||
|
if (routes.length > 6) {
|
||||||
|
showNext = routes.length - 5;
|
||||||
|
routes.splice(5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div class="routes-list">
|
||||||
|
{showPrevious && routeIndex.value > 0 ? (
|
||||||
|
<div class="checked">{formatWhole(routeIndex.value)} already checked</div>
|
||||||
|
) : null}
|
||||||
|
{routes.map((route, i) => {
|
||||||
|
const index = i + (showPrevious ? routeIndex.value : 0);
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
class={{
|
||||||
|
redundant: route[0] > route[1],
|
||||||
|
checked: routeIndex.value > index,
|
||||||
|
processing: routeIndex.value === index,
|
||||||
|
skipped:
|
||||||
|
routeIndex.value < index && routesToSkip.value.includes(index)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{stringifyRoute(route)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
{showNext > 0 ? <div>+ {formatWhole(showNext)} more</div> : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
day,
|
||||||
|
color,
|
||||||
|
citiesCompleted,
|
||||||
|
currentCity,
|
||||||
|
routeIndex,
|
||||||
|
checkRouteProgress,
|
||||||
|
newCityProgress,
|
||||||
|
boostProgress,
|
||||||
|
redundantProgress,
|
||||||
|
generalTabCollapsed,
|
||||||
|
currentRoutes,
|
||||||
|
redundantRoutes,
|
||||||
|
routesToSkip,
|
||||||
|
redundanciesRemoved,
|
||||||
|
city,
|
||||||
|
milestones,
|
||||||
|
collapseMilestones,
|
||||||
|
minWidth: 700,
|
||||||
|
display: jsx(() => (
|
||||||
|
<>
|
||||||
|
<div>
|
||||||
|
{main.day.value === day
|
||||||
|
? `Solve ${formatWhole(citiesGoal)} countries 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={citiesCompleted} color={color} />
|
||||||
|
{renderRow(getNewCity, boost, removeRedundantRoute)}
|
||||||
|
{render(city)}
|
||||||
|
{render(checkRouteProgressBar)}
|
||||||
|
<Spacer />
|
||||||
|
<h3>Checking Routes...</h3>
|
||||||
|
{displayRoutes()}
|
||||||
|
<Spacer />
|
||||||
|
{milestonesDisplay()}
|
||||||
|
</>
|
||||||
|
)),
|
||||||
|
minimizedDisplay: jsx(() => (
|
||||||
|
<div>
|
||||||
|
{name} <span class="desc">{format(citiesCompleted.value)} countries solved</span>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
export default layer;
|
|
@ -43,9 +43,9 @@ const layer = createLayer(id, function (this: BaseLayer) {
|
||||||
const sleighCost = computed(() => {
|
const sleighCost = computed(() => {
|
||||||
let v = sleighProgress.value.value;
|
let v = sleighProgress.value.value;
|
||||||
return {
|
return {
|
||||||
wood: Decimal.mul(1e60, Decimal.pow(1.2, v)),
|
wood: Decimal.mul(1e97, Decimal.pow(1.2, v)),
|
||||||
metal: Decimal.mul(1e40, Decimal.pow(1.1, v)),
|
metal: Decimal.mul(1e67, Decimal.pow(1.1, v)),
|
||||||
plastic: Decimal.mul(1e10, Decimal.pow(1.05, v))
|
plastic: Decimal.mul(1e22, Decimal.pow(1.05, v))
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const sleigh = createBuyable(() => ({
|
const sleigh = createBuyable(() => ({
|
||||||
|
@ -55,7 +55,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<span style="font-size: large">
|
<span style="font-size: large">
|
||||||
Cost: {displayCost(trees.logs, sleighCost.value.wood, "logs")},
|
Requires: {displayCost(trees.logs, sleighCost.value.wood, "logs")},
|
||||||
{displayCost(metal.metal, sleighCost.value.metal, "metal")},
|
{displayCost(metal.metal, sleighCost.value.metal, "metal")},
|
||||||
{displayCost(plastic.plastic, sleighCost.value.plastic, "plastic")}
|
{displayCost(plastic.plastic, sleighCost.value.plastic, "plastic")}
|
||||||
</span>
|
</span>
|
||||||
|
@ -111,7 +111,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
|
||||||
const milestone5 = createMilestone(() => ({
|
const milestone5 = createMilestone(() => ({
|
||||||
display: {
|
display: {
|
||||||
requirement: "40% Sleigh Fixed",
|
requirement: "40% Sleigh Fixed",
|
||||||
effectDisplay: "Plastic gain is doubled"
|
effectDisplay: "Plastic gain is quadrupled"
|
||||||
},
|
},
|
||||||
shouldEarn: () => Decimal.gte(sleighProgress.value.value, 40),
|
shouldEarn: () => Decimal.gte(sleighProgress.value.value, 40),
|
||||||
showPopups: shouldShowPopups
|
showPopups: shouldShowPopups
|
||||||
|
@ -127,7 +127,7 @@ const layer = createLayer(id, function (this: BaseLayer) {
|
||||||
const milestone7 = createMilestone(() => ({
|
const milestone7 = createMilestone(() => ({
|
||||||
display: {
|
display: {
|
||||||
requirement: "75% Sleigh Fixed",
|
requirement: "75% Sleigh Fixed",
|
||||||
effectDisplay: "Gain 10 extra refineries for every 2% of sleigh fixed"
|
effectDisplay: "Gain 40 extra refineries for every 2% of sleigh fixed"
|
||||||
},
|
},
|
||||||
shouldEarn: () => Decimal.gte(sleighProgress.value.value, 75),
|
shouldEarn: () => Decimal.gte(sleighProgress.value.value, 75),
|
||||||
showPopups: shouldShowPopups
|
showPopups: shouldShowPopups
|
||||||
|
|
20
src/data/layers/styles/routing.css
Normal file
20
src/data/layers/styles/routing.css
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
.routes-list .checked {
|
||||||
|
color: var(--bought);
|
||||||
|
}
|
||||||
|
|
||||||
|
.routes-list .processing {
|
||||||
|
color: var(--layer-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.routes-list .redundant:not(.checked):not(.processing) {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.routes-list .skipped {
|
||||||
|
text-decoration: line-through;
|
||||||
|
text-decoration-thickness: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.routes-list > * {
|
||||||
|
flex: 1 1 33%;
|
||||||
|
}
|
|
@ -41,6 +41,7 @@ import wrappingPaper from "./wrapping-paper";
|
||||||
import toys from "./toys";
|
import toys from "./toys";
|
||||||
import factory from "./factory";
|
import factory from "./factory";
|
||||||
import reindeer from "./reindeer";
|
import reindeer from "./reindeer";
|
||||||
|
import sleigh from "./sleigh";
|
||||||
const id = "trees";
|
const id = "trees";
|
||||||
const day = 1;
|
const day = 1;
|
||||||
|
|
||||||
|
@ -558,6 +559,16 @@ const layer = createLayer(id, function (this: BaseLayer) {
|
||||||
description: "Haul wood in trucks",
|
description: "Haul wood in trucks",
|
||||||
enabled: factory.upgrades[0][2].bought
|
enabled: factory.upgrades[0][2].bought
|
||||||
})),
|
})),
|
||||||
|
createMultiplicativeModifier(() => ({
|
||||||
|
multiplier: () => Decimal.div(sleigh.sleighProgress.value.value, 5).floor().mul(0.05).add(1),
|
||||||
|
description: "10% Sleigh Fixed",
|
||||||
|
enabled: sleigh.milestones.milestone2.earned
|
||||||
|
})),
|
||||||
|
createMultiplicativeModifier(() => ({
|
||||||
|
multiplier: 10,
|
||||||
|
description: "50% Sleigh Fixed",
|
||||||
|
enabled: sleigh.milestones.milestone6.earned
|
||||||
|
})),
|
||||||
reindeer.reindeer.dasher.modifier,
|
reindeer.reindeer.dasher.modifier,
|
||||||
createExponentialModifier(() => ({
|
createExponentialModifier(() => ({
|
||||||
exponent: 1.2,
|
exponent: 1.2,
|
||||||
|
|
|
@ -4,7 +4,8 @@ import {
|
||||||
Component,
|
Component,
|
||||||
GatherProps,
|
GatherProps,
|
||||||
GenericComponent,
|
GenericComponent,
|
||||||
jsx
|
jsx,
|
||||||
|
Visibility
|
||||||
} from "features/feature";
|
} from "features/feature";
|
||||||
import { BaseLayer, createLayer, GenericLayer, layers } from "game/layers";
|
import { BaseLayer, createLayer, GenericLayer, layers } from "game/layers";
|
||||||
import { isPersistent, Persistent, persistent } from "game/persistence";
|
import { isPersistent, Persistent, persistent } from "game/persistence";
|
||||||
|
@ -15,7 +16,7 @@ import { Computable, convertComputable, ProcessedComputable } from "util/compute
|
||||||
import { createLazyProxy } from "util/proxies";
|
import { createLazyProxy } from "util/proxies";
|
||||||
import { save } from "util/save";
|
import { save } from "util/save";
|
||||||
import { render, renderRow, VueFeature } from "util/vue";
|
import { render, renderRow, VueFeature } from "util/vue";
|
||||||
import type { Ref } from "vue";
|
import { Ref, watch, watchEffect } from "vue";
|
||||||
import { computed, ref, unref } from "vue";
|
import { computed, ref, unref } from "vue";
|
||||||
import "./advent.css";
|
import "./advent.css";
|
||||||
import Day from "./Day.vue";
|
import Day from "./Day.vue";
|
||||||
|
@ -33,6 +34,7 @@ import paper from "./layers/paper";
|
||||||
import plastic from "./layers/plastic";
|
import plastic from "./layers/plastic";
|
||||||
import reindeer from "./layers/reindeer";
|
import reindeer from "./layers/reindeer";
|
||||||
import ribbon from "./layers/ribbon";
|
import ribbon from "./layers/ribbon";
|
||||||
|
import routing from "./layers/routing";
|
||||||
import toys from "./layers/toys";
|
import toys from "./layers/toys";
|
||||||
import trees from "./layers/trees";
|
import trees from "./layers/trees";
|
||||||
import workshop from "./layers/workshop";
|
import workshop from "./layers/workshop";
|
||||||
|
@ -60,9 +62,11 @@ import toysSymbol from "./symbols/truck.png";
|
||||||
import advManagementSymbol from "./symbols/workshopMansion.png";
|
import advManagementSymbol from "./symbols/workshopMansion.png";
|
||||||
import wrappingPaperSymbol from "./symbols/wrappingPaper.png";
|
import wrappingPaperSymbol from "./symbols/wrappingPaper.png";
|
||||||
import snowflakeSymbol from "./symbols/snowflake.svg";
|
import snowflakeSymbol from "./symbols/snowflake.svg";
|
||||||
import presentSymbol from "./layers/factory-components/present.svg"
|
import presentSymbol from "./layers/factory-components/present.svg";
|
||||||
import { createParticles } from "features/particles/particles";
|
import { createParticles } from "features/particles/particles";
|
||||||
import { credits } from "./credits";
|
import { credits } from "./credits";
|
||||||
|
import sleighSymbol from "./symbols/sleigh.png";
|
||||||
|
import routingSymbol from "./symbols/gps.png";
|
||||||
|
|
||||||
export interface Day extends VueFeature {
|
export interface Day extends VueFeature {
|
||||||
day: number;
|
day: number;
|
||||||
|
@ -74,10 +78,13 @@ export interface Day extends VueFeature {
|
||||||
opened: Persistent<boolean>;
|
opened: Persistent<boolean>;
|
||||||
recentlyUpdated: Ref<boolean>; // Has the tab recieved an update since the player last opened it?
|
recentlyUpdated: Ref<boolean>; // Has the tab recieved an update since the player last opened it?
|
||||||
shouldNotify: ProcessedComputable<boolean>;
|
shouldNotify: ProcessedComputable<boolean>;
|
||||||
|
visibility?: Visibility;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const main = createLayer("main", function (this: BaseLayer) {
|
export const main = createLayer("main", function (this: BaseLayer) {
|
||||||
const day = persistent<number>(1);
|
const day = persistent<number>(1);
|
||||||
|
const hasWon = persistent<boolean>(false);
|
||||||
|
|
||||||
const timeUntilNewDay = computed(
|
const timeUntilNewDay = computed(
|
||||||
() => (+new Date(new Date().getFullYear(), 11, day.value) - player.time) / 1000
|
() => (+new Date(new Date().getFullYear(), 11, day.value) - player.time) / 1000
|
||||||
);
|
);
|
||||||
|
@ -96,49 +103,32 @@ export const main = createLayer("main", function (this: BaseLayer) {
|
||||||
this.boundingRect.value = boundingRect;
|
this.boundingRect.value = boundingRect;
|
||||||
},
|
},
|
||||||
style: "z-index: -1"
|
style: "z-index: -1"
|
||||||
}))
|
}));
|
||||||
|
|
||||||
particles.addEmitter({
|
const emitter = particles.addEmitter({
|
||||||
lifetime: {min: 5, max: 5},
|
emit: false,
|
||||||
pos: {x: 0, y: 0},
|
autoUpdate: true,
|
||||||
|
lifetime: { min: 5, max: 5 },
|
||||||
|
emitterLifetime: -1,
|
||||||
|
pos: { x: 0, y: 0 },
|
||||||
frequency: 0.05,
|
frequency: 0.05,
|
||||||
|
maxParticles: 1000,
|
||||||
behaviors: [
|
behaviors: [
|
||||||
{
|
{
|
||||||
type: 'alpha',
|
type: "alphaStatic",
|
||||||
config: {
|
config: {
|
||||||
alpha: {
|
alpha: 1
|
||||||
list: [
|
|
||||||
{
|
|
||||||
value: 1,
|
|
||||||
time: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 1,
|
|
||||||
time: 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'scale',
|
type: "scaleStatic",
|
||||||
config: {
|
config: {
|
||||||
scale: {
|
min: 1,
|
||||||
list: [
|
max: 1
|
||||||
{
|
|
||||||
value: 1,
|
|
||||||
time: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 1,
|
|
||||||
time: 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'color',
|
type: "color",
|
||||||
config: {
|
config: {
|
||||||
color: {
|
color: {
|
||||||
list: [
|
list: [
|
||||||
|
@ -150,12 +140,12 @@ export const main = createLayer("main", function (this: BaseLayer) {
|
||||||
value: "f5b830",
|
value: "f5b830",
|
||||||
time: 1
|
time: 1
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'moveSpeed',
|
type: "moveSpeed",
|
||||||
config: {
|
config: {
|
||||||
speed: {
|
speed: {
|
||||||
list: [
|
list: [
|
||||||
|
@ -169,39 +159,41 @@ export const main = createLayer("main", function (this: BaseLayer) {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
isStepped: false
|
isStepped: false
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'rotationStatic',
|
type: "rotationStatic",
|
||||||
config: {
|
config: {
|
||||||
min: 70,
|
min: 70,
|
||||||
max: 110
|
max: 110
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'spawnShape',
|
type: "spawnShape",
|
||||||
config: {
|
config: {
|
||||||
type: 'rect',
|
type: "rect",
|
||||||
data: {
|
data: {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
width: 800,
|
w: 800,
|
||||||
height: 10
|
h: 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'textureSingle',
|
type: "textureSingle",
|
||||||
config: {
|
config: {
|
||||||
texture: snowflakeSymbol
|
texture: snowflakeSymbol
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}).then(e => {
|
});
|
||||||
e.autoUpdate = true;
|
|
||||||
})
|
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
const shouldEmit = day.value === 25;
|
||||||
|
emitter.then(e => (e.emit = shouldEmit));
|
||||||
|
});
|
||||||
|
|
||||||
const currentlyMastering = computed(() =>
|
const currentlyMastering = computed(() =>
|
||||||
isMastery.value
|
isMastery.value
|
||||||
|
@ -285,6 +277,7 @@ export const main = createLayer("main", function (this: BaseLayer) {
|
||||||
story: string;
|
story: string;
|
||||||
completedStory: string;
|
completedStory: string;
|
||||||
masteredStory: string;
|
masteredStory: string;
|
||||||
|
visibility?: Visibility;
|
||||||
}
|
}
|
||||||
): Day {
|
): Day {
|
||||||
const opened = persistent<boolean>(false);
|
const opened = persistent<boolean>(false);
|
||||||
|
@ -314,7 +307,8 @@ export const main = createLayer("main", function (this: BaseLayer) {
|
||||||
story,
|
story,
|
||||||
completedStory,
|
completedStory,
|
||||||
masteredStory,
|
masteredStory,
|
||||||
recentlyUpdated
|
recentlyUpdated,
|
||||||
|
visibility
|
||||||
} = this;
|
} = this;
|
||||||
|
|
||||||
const mastered: Ref<boolean> =
|
const mastered: Ref<boolean> =
|
||||||
|
@ -327,6 +321,7 @@ export const main = createLayer("main", function (this: BaseLayer) {
|
||||||
recentlyUpdated,
|
recentlyUpdated,
|
||||||
shouldNotify,
|
shouldNotify,
|
||||||
mastered,
|
mastered,
|
||||||
|
visibility,
|
||||||
onOpenLore() {
|
onOpenLore() {
|
||||||
const completed = main.day.value > day;
|
const completed = main.day.value > day;
|
||||||
loreScene.value = completed ? day - 1 : -1;
|
loreScene.value = completed ? day - 1 : -1;
|
||||||
|
@ -370,7 +365,10 @@ export const main = createLayer("main", function (this: BaseLayer) {
|
||||||
opened.value = true;
|
opened.value = true;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
loreScene.value = -1;
|
loreScene.value = -1;
|
||||||
loreTitle.value = day == 25 ? "The End!" : unref(layers[layer ?? "trees"]?.name ?? "");
|
loreTitle.value =
|
||||||
|
day == 25
|
||||||
|
? "The End!"
|
||||||
|
: unref(layers[layer ?? "trees"]?.name ?? "");
|
||||||
loreBody.value = story;
|
loreBody.value = story;
|
||||||
if (player.autoPause) player.devSpeed = null;
|
if (player.autoPause) player.devSpeed = null;
|
||||||
showLoreModal.value = true;
|
showLoreModal.value = true;
|
||||||
|
@ -609,19 +607,21 @@ export const main = createLayer("main", function (this: BaseLayer) {
|
||||||
createDay(() => ({
|
createDay(() => ({
|
||||||
day: 22,
|
day: 22,
|
||||||
shouldNotify: false,
|
shouldNotify: false,
|
||||||
layer: "sleigh", // "sleigh"
|
layer: "sleigh",
|
||||||
symbol: "",
|
symbol: sleighSymbol,
|
||||||
story: "default body",
|
story: "You realize you haven't noticed a very important object since you've started working here. Where's the sleigh? You bring it up to Santa and he immediately becomes visibly stressed, mentioning it's been in disrepair and he completely forgot! You promise you'll get it back in shape in no time!",
|
||||||
completedStory: "",
|
completedStory:
|
||||||
|
"Crisis averted! The sleigh has been returned to it's full splendor. Santa is incredibly appreciative. Good Job!",
|
||||||
masteredStory: ""
|
masteredStory: ""
|
||||||
})),
|
})),
|
||||||
createDay(() => ({
|
createDay(() => ({
|
||||||
day: 23,
|
day: 23,
|
||||||
shouldNotify: false,
|
shouldNotify: false,
|
||||||
layer: null, // "distribution route planning"
|
layer: "routing",
|
||||||
symbol: "",
|
symbol: routingSymbol,
|
||||||
story: "",
|
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: "",
|
completedStory:
|
||||||
|
"Take that, math majors! Optimal route planned with time to spare. Good Job!",
|
||||||
masteredStory: ""
|
masteredStory: ""
|
||||||
})),
|
})),
|
||||||
createDay(() => ({
|
createDay(() => ({
|
||||||
|
@ -638,9 +638,10 @@ export const main = createLayer("main", function (this: BaseLayer) {
|
||||||
shouldNotify: false,
|
shouldNotify: false,
|
||||||
layer: null, // credits
|
layer: null, // credits
|
||||||
symbol: snowflakeSymbol,
|
symbol: snowflakeSymbol,
|
||||||
story: `It's Christmas. Thanks to your efforts, Santa has delivered all the presents to people all over the world. That is, all but one... <br><br> <div style='text-align: center'><img class='present-clickable' onclick ='layers.main.showLoreModal.value = false; layers.main.creditsOpen.value = true' src='${presentSymbol}' /><br>Open your present</div>`,
|
story: `It's Christmas. Thanks to your efforts, Santa has delivered all the presents to people all over the world. That is, all but one... <br><br> <div style='text-align: center'><img class='present-clickable' onclick ='layers.main.showLoreModal.value = false; layers.main.creditsOpen.value = true' src='${presentSymbol}' /><br>Open your present</div><br/>`,
|
||||||
completedStory: "",
|
completedStory: "",
|
||||||
masteredStory: ""
|
masteredStory: "",
|
||||||
|
visibility: Visibility.None
|
||||||
}))
|
}))
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -673,6 +674,16 @@ export const main = createLayer("main", function (this: BaseLayer) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
if (day.value === 25 && showLoreModal.value === false && !hasWon.value) {
|
||||||
|
loreScene.value = -1;
|
||||||
|
loreTitle.value = "Merry Christmas!";
|
||||||
|
loreBody.value = days[day.value - 1].story;
|
||||||
|
showLoreModal.value = true;
|
||||||
|
hasWon.value = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: "Calendar",
|
name: "Calendar",
|
||||||
days,
|
days,
|
||||||
|
@ -694,6 +705,7 @@ export const main = createLayer("main", function (this: BaseLayer) {
|
||||||
masteredDays,
|
masteredDays,
|
||||||
creditsOpen,
|
creditsOpen,
|
||||||
credits,
|
credits,
|
||||||
|
hasWon,
|
||||||
display: jsx(() => (
|
display: jsx(() => (
|
||||||
<>
|
<>
|
||||||
{player.devSpeed === 0 ? <div>Game Paused</div> : null}
|
{player.devSpeed === 0 ? <div>Game Paused</div> : null}
|
||||||
|
@ -724,7 +736,19 @@ export const main = createLayer("main", function (this: BaseLayer) {
|
||||||
)
|
)
|
||||||
.map((days: Day[]) => renderRow(...days))}
|
.map((days: Day[]) => renderRow(...days))}
|
||||||
</div>
|
</div>
|
||||||
{render(particles) /*creditsOpen.value || day.value == 25 ? render(particles) : null*/}
|
{hasWon.value ? (
|
||||||
|
<>
|
||||||
|
<Spacer />
|
||||||
|
<button
|
||||||
|
class="button"
|
||||||
|
style="font-size: xx-large"
|
||||||
|
onClick={() => (creditsOpen.value = true)}
|
||||||
|
>
|
||||||
|
Open Credits
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
{render(particles)}
|
||||||
</>
|
</>
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
|
@ -757,7 +781,8 @@ export const getInitialLayers = (
|
||||||
toys,
|
toys,
|
||||||
factory,
|
factory,
|
||||||
reindeer,
|
reindeer,
|
||||||
sleigh
|
sleigh,
|
||||||
|
routing
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
BIN
src/data/symbols/gps.png
Normal file
BIN
src/data/symbols/gps.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 112 KiB |
BIN
src/data/symbols/sleigh.png
Normal file
BIN
src/data/symbols/sleigh.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 67 KiB |
|
@ -8,6 +8,13 @@
|
||||||
:x2="endPosition.x"
|
:x2="endPosition.x"
|
||||||
:y2="endPosition.y"
|
:y2="endPosition.y"
|
||||||
/>
|
/>
|
||||||
|
<text
|
||||||
|
v-if="link.weight"
|
||||||
|
:x="(startPosition.x + endPosition.x) / 2"
|
||||||
|
:y="(startPosition.y + endPosition.y) / 2 + 4"
|
||||||
|
style="text-anchor: middle; fill: var(--foreground)"
|
||||||
|
>{{ link.weight }}</text
|
||||||
|
>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
@ -15,7 +22,7 @@ import type { BoardNodeLink } from "features/boards/board";
|
||||||
import { computed, toRefs, unref } from "vue";
|
import { computed, toRefs, unref } from "vue";
|
||||||
|
|
||||||
const _props = defineProps<{
|
const _props = defineProps<{
|
||||||
link: BoardNodeLink;
|
link: BoardNodeLink & { weight?: number };
|
||||||
}>();
|
}>();
|
||||||
const props = toRefs(_props);
|
const props = toRefs(_props);
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
Visibility
|
Visibility
|
||||||
} from "features/feature";
|
} from "features/feature";
|
||||||
import { globalBus } from "game/events";
|
import { globalBus } from "game/events";
|
||||||
import type { Persistent, State } from "game/persistence";
|
import { DefaultValue, deletePersistent, Persistent, State } from "game/persistence";
|
||||||
import { persistent } from "game/persistence";
|
import { persistent } from "game/persistence";
|
||||||
import type { Unsubscribe } from "nanoevents";
|
import type { Unsubscribe } from "nanoevents";
|
||||||
import { isFunction } from "util/common";
|
import { isFunction } from "util/common";
|
||||||
|
@ -167,12 +167,12 @@ export interface BoardOptions {
|
||||||
style?: Computable<StyleValue>;
|
style?: Computable<StyleValue>;
|
||||||
startNodes: () => Omit<BoardNode, "id">[];
|
startNodes: () => Omit<BoardNode, "id">[];
|
||||||
types: Record<string, NodeTypeOptions>;
|
types: Record<string, NodeTypeOptions>;
|
||||||
|
state?: Computable<BoardData>;
|
||||||
|
links?: Computable<BoardNodeLink[] | null>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BaseBoard {
|
export interface BaseBoard {
|
||||||
id: string;
|
id: string;
|
||||||
state: Persistent<BoardData>;
|
|
||||||
links: Ref<BoardNodeLink[] | null>;
|
|
||||||
nodes: Ref<BoardNode[]>;
|
nodes: Ref<BoardNode[]>;
|
||||||
selectedNode: Ref<BoardNode | null>;
|
selectedNode: Ref<BoardNode | null>;
|
||||||
selectedAction: Ref<GenericBoardNodeAction | null>;
|
selectedAction: Ref<GenericBoardNodeAction | null>;
|
||||||
|
@ -191,6 +191,8 @@ export type Board<T extends BoardOptions> = Replace<
|
||||||
width: GetComputableType<T["width"]>;
|
width: GetComputableType<T["width"]>;
|
||||||
classes: GetComputableType<T["classes"]>;
|
classes: GetComputableType<T["classes"]>;
|
||||||
style: GetComputableType<T["style"]>;
|
style: GetComputableType<T["style"]>;
|
||||||
|
state: GetComputableTypeWithDefault<T["state"], Persistent<BoardData>>;
|
||||||
|
links: GetComputableTypeWithDefault<T["links"], Ref<BoardNodeLink[] | null>>;
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
@ -198,31 +200,46 @@ export type GenericBoard = Replace<
|
||||||
Board<BoardOptions>,
|
Board<BoardOptions>,
|
||||||
{
|
{
|
||||||
visibility: ProcessedComputable<Visibility>;
|
visibility: ProcessedComputable<Visibility>;
|
||||||
|
state: ProcessedComputable<BoardData>;
|
||||||
|
links: ProcessedComputable<BoardNodeLink[] | null>;
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export function createBoard<T extends BoardOptions>(
|
export function createBoard<T extends BoardOptions>(
|
||||||
optionsFunc: OptionsFunc<T, BaseBoard, GenericBoard>
|
optionsFunc: OptionsFunc<T, BaseBoard, GenericBoard>
|
||||||
): Board<T> {
|
): Board<T> {
|
||||||
|
const state = persistent<BoardData>({
|
||||||
|
nodes: [],
|
||||||
|
selectedNode: null,
|
||||||
|
selectedAction: null
|
||||||
|
});
|
||||||
|
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(() => {
|
||||||
const board = optionsFunc();
|
const board = optionsFunc();
|
||||||
board.id = getUniqueID("board-");
|
board.id = getUniqueID("board-");
|
||||||
board.type = BoardType;
|
board.type = BoardType;
|
||||||
board[Component] = BoardComponent;
|
board[Component] = BoardComponent;
|
||||||
|
|
||||||
board.state = persistent<BoardData>({
|
if (board.state) {
|
||||||
nodes: board.startNodes().map((n, i) => {
|
deletePersistent(state);
|
||||||
(n as BoardNode).id = i;
|
processComputable(board as T, "state");
|
||||||
return n as BoardNode;
|
} else {
|
||||||
}),
|
state[DefaultValue] = {
|
||||||
selectedNode: null,
|
nodes: board.startNodes().map((n, i) => {
|
||||||
selectedAction: null
|
(n as BoardNode).id = i;
|
||||||
});
|
return n as BoardNode;
|
||||||
board.nodes = computed(() => processedBoard.state.value.nodes);
|
}),
|
||||||
|
selectedNode: null,
|
||||||
|
selectedAction: null
|
||||||
|
};
|
||||||
|
board.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
board.nodes = computed(() => unref(processedBoard.state).nodes);
|
||||||
board.selectedNode = computed(
|
board.selectedNode = computed(
|
||||||
() =>
|
() =>
|
||||||
processedBoard.nodes.value.find(
|
processedBoard.nodes.value.find(
|
||||||
node => node.id === processedBoard.state.value.selectedNode
|
node => node.id === unref(processedBoard.state).selectedNode
|
||||||
) || null
|
) || null
|
||||||
);
|
);
|
||||||
board.selectedAction = computed(() => {
|
board.selectedAction = computed(() => {
|
||||||
|
@ -236,23 +253,30 @@ export function createBoard<T extends BoardOptions>(
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
type.actions.find(
|
type.actions.find(
|
||||||
action => action.id === processedBoard.state.value.selectedAction
|
action => action.id === unref(processedBoard.state).selectedAction
|
||||||
) || null
|
) || null
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
board.mousePosition = ref(null);
|
board.mousePosition = ref(null);
|
||||||
board.links = computed(() => {
|
if (board.links) {
|
||||||
if (processedBoard.selectedAction.value == null) {
|
processComputable(board as T, "links");
|
||||||
return null;
|
} else {
|
||||||
}
|
board.links = computed(() => {
|
||||||
if (processedBoard.selectedAction.value.links && processedBoard.selectedNode.value) {
|
if (processedBoard.selectedAction.value == null) {
|
||||||
return getNodeProperty(
|
return null;
|
||||||
processedBoard.selectedAction.value.links,
|
}
|
||||||
|
if (
|
||||||
|
processedBoard.selectedAction.value.links &&
|
||||||
processedBoard.selectedNode.value
|
processedBoard.selectedNode.value
|
||||||
);
|
) {
|
||||||
}
|
return getNodeProperty(
|
||||||
return null;
|
processedBoard.selectedAction.value.links,
|
||||||
});
|
processedBoard.selectedNode.value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
processComputable(board as T, "visibility");
|
processComputable(board as T, "visibility");
|
||||||
setDefault(board, "visibility", Visibility.Visible);
|
setDefault(board, "visibility", Visibility.Visible);
|
||||||
processComputable(board as T, "width");
|
processComputable(board as T, "width");
|
||||||
|
@ -286,10 +310,10 @@ export function createBoard<T extends BoardOptions>(
|
||||||
processComputable(nodeType as NodeTypeOptions, "actionDistance");
|
processComputable(nodeType as NodeTypeOptions, "actionDistance");
|
||||||
setDefault(nodeType, "actionDistance", Math.PI / 6);
|
setDefault(nodeType, "actionDistance", Math.PI / 6);
|
||||||
nodeType.nodes = computed(() =>
|
nodeType.nodes = computed(() =>
|
||||||
processedBoard.state.value.nodes.filter(node => node.type === type)
|
unref(processedBoard.state).nodes.filter(node => node.type === type)
|
||||||
);
|
);
|
||||||
setDefault(nodeType, "onClick", function (node: BoardNode) {
|
setDefault(nodeType, "onClick", function (node: BoardNode) {
|
||||||
processedBoard.state.value.selectedNode = node.id;
|
unref(processedBoard.state).selectedNode = node.id;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (nodeType.actions) {
|
if (nodeType.actions) {
|
||||||
|
|
|
@ -156,7 +156,7 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-family-container > :nth-child(2) {
|
.tab-family-container > :nth-child(2) {
|
||||||
margin-top: 20px;
|
margin-top: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-body > .tab-family-container > :nth-child(2) {
|
.modal-body > .tab-family-container > :nth-child(2) {
|
||||||
|
@ -222,8 +222,8 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
.showGoBack
|
.showGoBack
|
||||||
> .tab-family-container
|
> .tab-family-container:first-child
|
||||||
> .tab-buttons-container:not(.floating):first-child
|
> .tab-buttons-container:not(.floating)
|
||||||
.tab-buttons {
|
.tab-buttons {
|
||||||
padding-left: 70px;
|
padding-left: 70px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -268,6 +268,9 @@ globalBus.on("addLayer", (layer: GenericLayer, saveData: Record<string, unknown>
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
handleObject((layer as any)[ProxyState]);
|
handleObject((layer as any)[ProxyState]);
|
||||||
persistentRefs[layer.id].forEach(persistent => {
|
persistentRefs[layer.id].forEach(persistent => {
|
||||||
|
if (persistent[Deleted]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
console.error(
|
console.error(
|
||||||
`Created persistent ref in ${layer.id} without registering it to the layer! Make sure to include everything persistent in the returned object`,
|
`Created persistent ref in ${layer.id} without registering it to the layer! Make sure to include everything persistent in the returned object`,
|
||||||
persistent,
|
persistent,
|
||||||
|
|
Loading…
Reference in a new issue