Fixing building / cleanup

This commit is contained in:
thepaperpilot 2022-01-24 22:23:30 -06:00
parent 6f781b33fa
commit 15a460bf42
51 changed files with 5727 additions and 15879 deletions

View file

@ -1,23 +1,27 @@
require("@rushstack/eslint-patch/modern-module-resolution");
module.exports = {
root: true,
env: {
node: true,
'vue/setup-compiler-macros': true
node: true
},
extends: [
"plugin:vue/vue3-essential",
"eslint:recommended",
"@vue/typescript/recommended",
"@vue/prettier",
"@vue/prettier/@typescript-eslint"
"@vue/eslint-config-typescript/recommended",
"@vue/eslint-config-prettier"
],
parserOptions: {
ecmaVersion: 2020
},
ignorePatterns: ["src/lib"],
rules: {
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
"vue/script-setup-uses-vars": "error",
"vue/script-setup-uses-vars": "warn",
"vue/no-mutating-props": "off"
},
globals: {
defineProps: "readonly",
defineEmits: "readonly"
}
};

View file

@ -1,4 +1,7 @@
{
"printWidth": 100,
"tabWidth": 4
"arrowParens": "avoid",
"endOfLine": "auto",
"printWidth": 100,
"tabWidth": 4,
"trailingComma": "none"
}

20972
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -22,24 +22,21 @@
"devDependencies": {
"@ivanv/vue-collapse-transition": "^1.0.2",
"@jetblack/operator-overloading": "^0.2.0",
"@rushstack/eslint-patch": "^1.1.0",
"@types/lodash.clonedeep": "^4.5.6",
"@typescript-eslint/eslint-plugin": "^5.9.1",
"@typescript-eslint/parser": "^5.9.1",
"@vue/babel-plugin-jsx": "^1.1.1",
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-plugin-typescript": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/cli-plugin-babel": "~5.0.0-rc.1",
"@vue/cli-plugin-eslint": "~5.0.0-rc.1",
"@vue/cli-plugin-typescript": "~5.0.0-rc.1",
"@vue/cli-service": "~5.0.0-rc.1",
"@vue/compiler-sfc": "^3.2.26",
"@vue/eslint-config-prettier": "^6.0.0",
"@vue/eslint-config-prettier": "^7.0.0",
"@vue/eslint-config-typescript": "^10.0.0",
"babel-eslint": "^10.1.0",
"eslint": "^8.6.0",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-vue": "^8.3.0",
"prettier": "^1.19.1",
"prettier": "^2.5.1",
"raw-loader": "^4.0.2",
"sass": "^1.36.0",
"sass": "^1.48.0",
"sass-loader": "^10.2.0",
"tsconfig-paths-webpack-plugin": "^3.5.1",
"typescript": "^4.5.4"
@ -53,7 +50,7 @@
"pre-commit": "lint-staged"
},
"lint-staged": {
"*.{js,vue}": [
"*.{js,jsx,ts,tsx,vue}": [
"vue-cli-service lint",
"git add"
]

View file

@ -14,6 +14,7 @@ import { computed, toRef } from "vue";
import Game from "./components/system/Game.vue";
import GameOverScreen from "./components/system/GameOverScreen.vue";
import NaNScreen from "./components/system/NaNScreen.vue";
import Nav from "./components/system/Nav.vue";
import TPS from "./components/system/TPS.vue";
import modInfo from "./data/modInfo.json";
import themes from "./data/themes";

View file

@ -12,12 +12,14 @@
</div>
</template>
<script setup lang="ts">
<script lang="ts">
import { FeatureComponent, Visibility } from "@/features/feature";
import { GenericGrid } from "@/features/grid";
import { defineComponent } from "vue";
import GridCell from "./GridCell.vue";
defineProps<FeatureComponent<GenericGrid>>();
// https://github.com/thepaperpilot/The-Modding-Tree-X/issues/1
export default defineComponent(function Grid(props: FeatureComponent<GenericGrid>) {
return { ...props, GridCell, Visibility };
});
</script>
<style scoped></style>

View file

@ -14,7 +14,7 @@
:disabled="!canClick"
>
<div v-if="title"><component :is="titleComponent" /></div>
<component :is="component" style="white-space: pre-line;" />
<component :is="component" style="white-space: pre-line" />
<LinkNode :id="id" />
</button>
</template>

View file

@ -4,7 +4,7 @@
<ResourceVue :resource="resource" :color="color || 'white'" />
{{ resource
}}<!-- remove whitespace -->
<span v-if="effectComponent">, <component :is="effectComponent"/></span>
<span v-if="effectComponent">, <component :is="effectComponent" /></span>
<br /><br />
</div>
</template>

View file

@ -19,7 +19,7 @@
<script setup lang="ts">
import themes from "@/data/themes";
import { FeatureComponent } from "@/features/feature";
import { FeatureComponent, PersistentState } from "@/features/feature";
import { GenericTabFamily } from "@/features/tabFamily";
import settings from "@/game/settings";
import { coerceComponent, isCoercableComponent } from "@/util/vue";
@ -52,7 +52,7 @@ const style = computed(() => {
});
function selectTab(tab: string) {
props.state.value = tab;
props[PersistentState].value = tab;
}
</script>

View file

@ -52,7 +52,7 @@
<script setup lang="ts">
import { BoardNode, GenericBoard, getNodeProperty } from "@/features/board";
import { FeatureComponent, Visibility } from "@/features/feature";
import { FeatureComponent, PersistentState, Visibility } from "@/features/feature";
import { computed, ref, toRefs } from "vue";
import panZoom from "vue-panzoom";
import BoardLinkVue from "./BoardLink.vue";
@ -147,8 +147,8 @@ function mouseDown(e: MouseEvent | TouchEvent, nodeID: number | null = null, dra
}
}
if (nodeID != null) {
props.state.value.selectedNode = null;
props.state.value.selectedAction = null;
props[PersistentState].value.selectedNode = null;
props[PersistentState].value.selectedAction = null;
}
}
@ -206,8 +206,8 @@ function endDragging(nodeID: number | null) {
dragging.value = null;
} else if (!hasDragged.value) {
props.state.value.selectedNode = null;
props.state.value.selectedAction = null;
props[PersistentState].value.selectedNode = null;
props[PersistentState].value.selectedAction = null;
}
}
</script>

View file

@ -13,14 +13,16 @@
:key="action.id"
class="action"
:class="{ selected: selectedAction?.id === action.id }"
:transform="
`translate(
${(-size - 30) *
Math.sin(((actions.length - 1) / 2 - index) * actionDistance)},
${(size + 30) *
Math.cos(((actions.length - 1) / 2 - index) * actionDistance)}
)`
"
:transform="`translate(
${
(-size - 30) *
Math.sin(((actions.length - 1) / 2 - index) * actionDistance)
},
${
(size + 30) *
Math.cos(((actions.length - 1) / 2 - index) * actionDistance)
}
)`"
@mousedown="e => performAction(e, action)"
@touchstart="e => performAction(e, action)"
@mouseup="e => actionMouseUp(e, action)"
@ -91,9 +93,9 @@
class="receiver"
:width="size * sqrtTwo + 16"
:height="size * sqrtTwo + 16"
:transform="
`translate(${-(size * sqrtTwo + 16) / 2}, ${-(size * sqrtTwo + 16) / 2})`
"
:transform="`translate(${-(size * sqrtTwo + 16) / 2}, ${
-(size * sqrtTwo + 16) / 2
})`"
:fill="backgroundColor"
:stroke="receivingNode ? '#0F0' : '#0F03'"
:stroke-width="2"
@ -114,12 +116,9 @@
class="progressFill"
:width="Math.max(size * sqrtTwo * progress - 2, 0)"
:height="Math.max(size * sqrtTwo * progress - 2, 0)"
:transform="
`translate(${-Math.max(size * sqrtTwo * progress - 2, 0) / 2}, ${-Math.max(
size * sqrtTwo * progress - 2,
0
) / 2})`
"
:transform="`translate(${-Math.max(size * sqrtTwo * progress - 2, 0) / 2}, ${
-Math.max(size * sqrtTwo * progress - 2, 0) / 2
})`"
:fill="progressColor"
/>
<rect
@ -127,9 +126,9 @@
class="progressDiamond"
:width="size * sqrtTwo + 9"
:height="size * sqrtTwo + 9"
:transform="
`translate(${-(size * sqrtTwo + 9) / 2}, ${-(size * sqrtTwo + 9) / 2})`
"
:transform="`translate(${-(size * sqrtTwo + 9) / 2}, ${
-(size * sqrtTwo + 9) / 2
})`"
fill="transparent"
:stroke-dasharray="(size * sqrtTwo + 9) * 4"
:stroke-width="5"

View file

@ -20,12 +20,16 @@
</span>
</template>
<script setup lang="ts">
<script lang="ts">
import { FeatureComponent, wrapFeature } from "@/features/feature";
import { GenericTree } from "@/features/tree";
import { defineComponent } from "vue";
import TreeNode from "./TreeNode.vue";
defineProps<FeatureComponent<GenericTree>>();
// https://github.com/thepaperpilot/The-Modding-Tree-X/issues/1
export default defineComponent(function Grid(props: FeatureComponent<GenericTree>) {
return { ...props, TreeNode, wrapFeature };
});
</script>
<style scoped>

View file

@ -2,8 +2,18 @@
<Tooltip
v-if="visibility !== Visibility.None"
v-show="visibility === Visibility.Visible"
v-bind="typeof tooltip === 'object' ? wrapFeature(tooltip) : null"
:display="typeof tooltip === 'object' ? unref(tooltip.display) : tooltip || ''"
v-bind="
typeof tooltip === 'object' && !isCoercableComponent(tooltip)
? wrapFeature(tooltip)
: null
"
:display="
typeof tooltip === 'object'
? isCoercableComponent(tooltip)
? unref(tooltip)
: tooltip.display
: tooltip || ''
"
:force="forceTooltip"
:class="{
treeNode: true,
@ -38,7 +48,7 @@
<script setup lang="ts">
import { GenericTreeNode } from "@/features/tree";
import { coerceComponent, setupHoldToClick } from "@/util/vue";
import { coerceComponent, isCoercableComponent, setupHoldToClick } from "@/util/vue";
import { computed, toRefs, unref } from "vue";
import Tooltip from "@/components/system/Tooltip.vue";
import MarkNode from "../MarkNode.vue";

View file

@ -1,6 +1,6 @@
<template>
<div class="field">
<span class="field-title" v-if="titleComponent"><component :is="titleComponent"/></span>
<span class="field-title" v-if="titleComponent"><component :is="titleComponent" /></span>
<VueNextSelect
:options="options"
v-model="value"

View file

@ -1,7 +1,9 @@
<template>
<form @submit.prevent="submit">
<div class="field">
<span class="field-title" v-if="titleComponent"><component :is="titleComponent"/></span>
<span class="field-title" v-if="titleComponent"
><component :is="titleComponent"
/></span>
<VueTextareaAutosize
v-if="textArea"
v-model="value"

View file

@ -17,9 +17,7 @@
Aarex
</div>
<br />
<div class="link" @click="openChangelog">
Changelog
</div>
<div class="link" @click="openChangelog">Changelog</div>
<br />
<div>
<a

View file

@ -1,8 +1,6 @@
<template>
<div class="layer-container">
<button v-if="showGoBack" class="goBack" @click="goBack">
</button>
<button v-if="showGoBack" class="goBack" @click="goBack"></button>
<button class="layer-tab minimized" v-if="minimized" @click="minimized = false">
<div>{{ name }}</div>
</button>
@ -12,9 +10,7 @@
</Links>
<component v-else :is="component" />
</div>
<button v-if="minimizable" class="minimize" @click="minimized = true">
</button>
<button v-if="minimizable" class="minimize" @click="minimized = true"></button>
</div>
</template>

View file

@ -6,8 +6,8 @@
v-for="(link, index) in validLinks"
:key="index"
:link="link"
:startNode="nodes[link.startNode.id]"
:endNode="nodes[link.endNode.id]"
:startNode="nodes[link.startNode.id]!"
:endNode="nodes[link.endNode.id]!"
/>
</svg>
</template>
@ -24,16 +24,31 @@ import LinkVue from "./Link.vue";
const props = toRefs(defineProps<{ links: Link[] }>());
const observer = new MutationObserver(updateNodes);
const resizeObserver = new ResizeObserver(updateBounds);
const nodes = ref<Record<string, LinkNode | undefined>>({});
const boundingRect = ref(new DOMRect());
const resizeListener = ref<Element | null>(null);
onMounted(() => {
// ResizeListener exists because ResizeObserver's don't work when told to observe an SVG element
const resListener = resizeListener.value;
if (resListener != null) {
resizeObserver.observe(resListener);
}
updateNodes();
});
const validLinks = computed(() =>
unref(props.links.value).filter(link => {
const n = nodes.value;
return (
link.startNode.id in n &&
link.endNode.id in n &&
n[link.startNode.id].x != undefined &&
n[link.startNode.id].y != undefined &&
n[link.endNode.id].x != undefined &&
n[link.endNode.id].y != undefined
n[link.startNode.id]?.x != undefined &&
n[link.startNode.id]?.y != undefined &&
n[link.endNode.id]?.x != undefined &&
n[link.endNode.id]?.y != undefined
);
})
);
@ -54,7 +69,7 @@ provide(RegisterLinkNodeInjectionKey, (id, element) => {
});
});
provide(UnregisterLinkNodeInjectionKey, id => {
delete nodes.value[id];
nodes.value[id] = undefined;
});
function updateNodes() {
@ -64,12 +79,13 @@ function updateNodes() {
}
function updateNode(id: string) {
if (!(id in nodes.value)) {
const node = nodes.value[id];
if (!node) {
return;
}
const linkStartRect = nodes.value[id].element.getBoundingClientRect();
nodes.value[id].x = linkStartRect.x + linkStartRect.width / 2 - boundingRect.value.x;
nodes.value[id].y = linkStartRect.y + linkStartRect.height / 2 - boundingRect.value.y;
const linkStartRect = node.element.getBoundingClientRect();
node.x = linkStartRect.x + linkStartRect.width / 2 - boundingRect.value.x;
node.y = linkStartRect.y + linkStartRect.height / 2 - boundingRect.value.y;
}
function updateBounds() {
@ -78,23 +94,6 @@ function updateBounds() {
updateNodes();
}
}
const observer = new MutationObserver(updateNodes);
const resizeObserver = new ResizeObserver(updateBounds);
const nodes = ref<Record<string, LinkNode>>({});
const boundingRect = ref(new DOMRect());
const resizeListener = ref<Element | null>(null);
onMounted(() => {
// ResizeListener exists because ResizeObserver's don't work when told to observe an SVG element
const resListener = resizeListener.value;
if (resListener != null) {
resizeObserver.observe(resListener);
}
updateNodes();
});
</script>
<style scoped>

View file

@ -14,16 +14,13 @@
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header" :shown="isOpen">
default header
</slot>
<slot name="header" :shown="isOpen"> default header </slot>
</div>
<div class="modal-body">
<branches>
<slot name="body" :shown="isOpen">
default body
</slot>
</branches>
<Links v-if="links" :links="links">
<slot name="body" :shown="isOpen"> default body </slot>
</Links>
<slot name="body" v-else :shown="isOpen"> default body </slot>
</div>
<div class="modal-footer">
<slot name="footer" :shown="isOpen">
@ -43,10 +40,13 @@
</template>
<script setup lang="ts">
import { Link } from "@/features/links";
import { computed, ref } from "vue";
import Links from "./Links.vue";
const props = defineProps<{
modelValue: boolean;
links?: Link[];
}>();
const emit = defineEmits<{
(e: "update:modelValue", value: boolean): void;

View file

@ -25,9 +25,7 @@
</template>
<template v-slot:footer>
<div class="nan-footer">
<button @click="savesManager?.open()" class="button">
Open Saves Manager
</button>
<button @click="savesManager?.open()" class="button">Open Saves Manager</button>
<button @click="setZero" class="button">Set to 0</button>
<button @click="setOne" class="button">Set to 1</button>
<button

View file

@ -7,7 +7,7 @@
><span>v{{ versionNumber }}</span></Tooltip
>
</div>
<div style="flex-grow: 1; cursor: unset;"></div>
<div style="flex-grow: 1; cursor: unset"></div>
<div class="discord">
<span @click="openDiscord" class="material-icons">discord</span>
<ul class="discord-links">
@ -54,7 +54,7 @@
<div @click="changelog?.open()" class="version-container">
<Tooltip display="Changelog" right xoffset="25%" class="version">
<span>v{{ versionNumber }}</span>
</tooltip>
</Tooltip>
</div>
<div @click="savesManager?.open()">
<Tooltip display="Saves" right>

View file

@ -8,12 +8,12 @@
<Save
v-for="(save, index) in saves"
:key="index"
:save="save"
@open="openSave(save.id)"
@export="exportSave(save.id)"
@editName="name => editSave(save.id, name)"
@duplicate="duplicateSave(save.id)"
@delete="deleteSave(save.id)"
:save="save!"
@open="openSave(save!.id)"
@export="exportSave(save!.id)"
@editName="name => editSave(save!.id, name)"
@duplicate="duplicateSave(save!.id)"
@delete="deleteSave(save!.id)"
/>
</div>
</template>
@ -122,7 +122,7 @@ let bank = ref(
}, [])
);
const saves = ref<Record<string, LoadablePlayerData>>({});
const saves = ref<Record<string, LoadablePlayerData | undefined>>({});
function loadSaveData() {
saves.value = settings.saves.reduce((acc: Record<string, LoadablePlayerData>, curr: string) => {
@ -178,13 +178,15 @@ function duplicateSave(id: string) {
function deleteSave(id: string) {
settings.saves = settings.saves.filter((save: string) => save !== id);
localStorage.removeItem(id);
delete saves.value[id];
saves.value[id] = undefined;
}
function openSave(id: string) {
saves.value[player.id].time = player.time;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
saves.value[player.id]!.time = player.time;
save();
loadSave(saves.value[id]);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
loadSave(saves.value[id]!);
}
function newSave() {

View file

@ -97,13 +97,13 @@ export function createResetButton<T extends ClickableOptions & ResetButtonOption
options.canClick = computed(() => Decimal.gt(unref(proxy.conversion.currentGain), 0));
}
const onClick = options.onClick;
options.onClick = function() {
options.onClick = function () {
proxy.conversion.convert();
proxy.tree.reset(proxy.treeNode);
onClick?.();
};
const proxy = (createClickable(options) as unknown) as ResetButton<T>;
const proxy = createClickable(options) as unknown as ResetButton<T>;
return proxy;
}
@ -122,12 +122,12 @@ export type LayerTreeNode<T extends LayerTreeNodeOptions> = Replace<
export function createLayerTreeNode<T extends LayerTreeNodeOptions>(options: T): LayerTreeNode<T> {
processComputable(options as T, "append");
return (createTreeNode({
return createTreeNode({
...options,
display: options.layerID,
onClick:
options.append != null && options.append
? function() {
? function () {
if (player.tabs.includes(options.layerID)) {
const index = player.tabs.lastIndexOf(options.layerID);
player.tabs = [
@ -138,8 +138,8 @@ export function createLayerTreeNode<T extends LayerTreeNodeOptions>(options: T):
player.tabs = [...player.tabs, options.layerID];
}
}
: function() {
: function () {
player.tabs.splice(1, 1, options.layerID);
}
}) as unknown) as LayerTreeNode<T>;
}) as unknown as LayerTreeNode<T>;
}

View file

@ -47,7 +47,7 @@ const ach3 = createAchievement({
display: "EIEIO",
tooltip:
"Get a farm point.\n\nReward: The dinosaur is now your friend (you can max Farm Points).",
shouldEarn: function() {
shouldEarn: function () {
return Decimal.gte(fPoints.value, 1);
},
onComplete() {

View file

@ -108,7 +108,7 @@ const funChallenge = createChallenge({
return showIf(Decimal.gt(best.value, 0));
},
goal: 20,
resource: mainPoints,
resource: () => mainPoints,
onComplete() {
console.log("hiii");
},
@ -249,10 +249,7 @@ const longBoi = createBar({
width: 300,
height: 30,
progress() {
return Decimal.add(mainPoints.value, 1)
.log(10)
.div(10)
.toNumber();
return Decimal.add(mainPoints.value, 1).log(10).div(10).toNumber();
},
display() {
return format(mainPoints.value) + " / 1e10 points";
@ -393,16 +390,16 @@ const tree = createTree({
},
branches: [
{
from: fNode,
to: treeNode,
startNode: fNode,
endNode: treeNode,
style: {
strokeWidth: "25px",
stroke: "blue",
filter: "blur(5px)"
}
},
{ from: treeNode, to: g },
{ from: g, to: h }
{ startNode: treeNode, endNode: g },
{ startNode: g, endNode: h }
]
});

View file

@ -1,3 +1,5 @@
import Modal from "@/components/system/Modal.vue";
import Spacer from "@/components/system/Spacer.vue";
import { createResource, trackBest, trackOOMPS, trackTotal } from "@/features/resource";
import { createTree, GenericTree } from "@/features/tree";
import { globalBus } from "@/game/events";
@ -5,7 +7,8 @@ import { createLayer, GenericLayer } from "@/game/layers";
import player, { PlayerData } from "@/game/player";
import { DecimalSource } from "@/lib/break_eternity";
import Decimal, { format, formatSmall, formatTime } from "@/util/bignum";
import { computed } from "vue";
import { render } from "@/util/vue";
import { computed, ref } from "vue";
import a from "./layers/aca/a";
import c, {
generatorUpgrade,
@ -18,6 +21,7 @@ export const points = createResource<DecimalSource>(0);
const best = trackBest(points);
const total = trackTotal(points);
const oomps = trackOOMPS(points);
const showModal = ref(false);
const pointGain = computed(() => {
if (!generatorUpgrade.bought) return new Decimal(0);
@ -73,8 +77,8 @@ export const main = createLayer({
<div v-if={Decimal.gt(pointGain.value, 0)}>
({oomps.value === "" ? formatSmall(pointGain.value) : oomps.value}/sec)
</div>
<spacer />
<modal show={false}>
<Spacer />
<Modal v-model={showModal}>
<svg style="height: 80vmin; width: 80vmin;">
<path d="M 32 222 Q 128 222, 128 0 Q 128 222, 224 222 L 224 224 L 32 224" />
@ -82,8 +86,8 @@ export const main = createLayer({
<circle cx="128" cy="64" r="64" fill="#71368a" />
<circle cx="192" cy="128" r="64" fill="#fa8508" />
</svg>
</modal>
<tree {...tree} />
</Modal>
{render(tree)}
</template>
);
},

View file

@ -6,6 +6,7 @@ import {
getUniqueID,
makePersistent,
Persistent,
PersistentState,
Replace,
setDefault,
StyleValue,
@ -78,9 +79,9 @@ export function createAchievement<T extends AchievementOptions>(
achievement.type = AchievementType;
achievement[Component] = AchievementComponent;
achievement.earned = achievement.state;
achievement.complete = function() {
proxy.state.value = true;
achievement.earned = achievement[PersistentState];
achievement.complete = function () {
proxy[PersistentState].value = true;
};
processComputable(achievement as T, "visibility");
@ -94,18 +95,17 @@ export function createAchievement<T extends AchievementOptions>(
processComputable(achievement as T, "tooltip");
setDefault(achievement, "tooltip", achievement.display);
const proxy = createProxy((achievement as unknown) as Achievement<T>);
const proxy = createProxy(achievement as unknown as Achievement<T>);
return proxy;
}
const toast = useToast();
const listeners: Record<string, Unsubscribe> = {};
const listeners: Record<string, Unsubscribe | undefined> = {};
globalBus.on("addLayer", layer => {
const achievements: GenericAchievement[] = (findFeatures(
layer,
AchievementType
) as GenericAchievement[]).filter(ach => ach.shouldEarn != null);
const achievements: GenericAchievement[] = (
findFeatures(layer, AchievementType) as GenericAchievement[]
).filter(ach => ach.shouldEarn != null);
if (achievements.length) {
listeners[layer.id] = layer.on("postUpdate", () => {
achievements.forEach(achievement => {
@ -114,7 +114,7 @@ globalBus.on("addLayer", layer => {
!unref(achievement.earned) &&
unref(achievement.shouldEarn)
) {
achievement.state.value = true;
achievement[PersistentState].value = true;
achievement.onComplete?.();
if (achievement.display) {
const display = unref(achievement.display);
@ -133,5 +133,5 @@ globalBus.on("addLayer", layer => {
globalBus.on("removeLayer", layer => {
// unsubscribe from postUpdate
listeners[layer.id]?.();
delete listeners[layer.id];
listeners[layer.id] = undefined;
});

View file

@ -97,6 +97,6 @@ export function createBar<T extends BarOptions>(options: T & ThisType<Bar<T>>):
processComputable(bar as T, "display");
processComputable(bar as T, "mark");
const proxy = createProxy((bar as unknown) as Bar<T>);
const proxy = createProxy(bar as unknown as Bar<T>);
return proxy;
}

View file

@ -5,6 +5,7 @@ import {
getUniqueID,
makePersistent,
Persistent,
PersistentState,
Replace,
setDefault,
State,
@ -208,9 +209,11 @@ export function createBoard<T extends BoardOptions>(options: T & ThisType<Board<
board.type = BoardType;
board[Component] = BoardComponent;
board.nodes = computed(() => proxy.state.value.nodes);
board.nodes = computed(() => proxy[PersistentState].value.nodes);
board.selectedNode = computed(
() => proxy.nodes.value.find(node => node.id === proxy.state.value.selectedNode) || null
() =>
proxy.nodes.value.find(node => node.id === proxy[PersistentState].value.selectedNode) ||
null
);
board.selectedAction = computed(() => {
if (proxy.selectedNode.value == null) {
@ -220,7 +223,11 @@ export function createBoard<T extends BoardOptions>(options: T & ThisType<Board<
if (type.actions == null) {
return null;
}
return type.actions.find(action => action.id === proxy.state.value.selectedAction) || null;
return (
type.actions.find(
action => action.id === proxy[PersistentState].value.selectedAction
) || null
);
});
board.links = computed(() => {
if (proxy.selectedAction.value == null) {
@ -263,9 +270,11 @@ export function createBoard<T extends BoardOptions>(options: T & ThisType<Board<
processComputable(nodeType, "titleColor");
processComputable(nodeType, "actionDistance");
setDefault(nodeType, "actionDistance", Math.PI / 6);
nodeType.nodes = computed(() => proxy.state.value.nodes.filter(node => node.type === type));
setDefault(nodeType, "onClick", function(node: BoardNode) {
proxy.state.value.selectedNode = node.id;
nodeType.nodes = computed(() =>
proxy[PersistentState].value.nodes.filter(node => node.type === type)
);
setDefault(nodeType, "onClick", function (node: BoardNode) {
proxy[PersistentState].value.selectedNode = node.id;
});
if (nodeType.actions) {
@ -279,10 +288,10 @@ export function createBoard<T extends BoardOptions>(options: T & ThisType<Board<
}
}
board.types[type] = createProxy((nodeType as unknown) as GenericNodeType);
board.types[type] = createProxy(nodeType as unknown as GenericNodeType);
}
const proxy = createProxy((board as unknown) as Board<T>);
const proxy = createProxy(board as unknown as Board<T>);
return proxy;
}
@ -300,7 +309,7 @@ export function getUniqueNodeID(board: GenericBoard): number {
return id;
}
const listeners: Record<string, Unsubscribe> = {};
const listeners: Record<string, Unsubscribe | undefined> = {};
globalBus.on("addLayer", layer => {
const boards: GenericBoard[] = findFeatures(layer, BoardType) as GenericBoard[];
listeners[layer.id] = layer.on("postUpdate", (diff: Decimal) => {
@ -313,6 +322,6 @@ globalBus.on("addLayer", layer => {
});
globalBus.on("removeLayer", layer => {
// unsubscribe from postUpdate
listeners[layer.id]();
delete listeners[layer.id];
listeners[layer.id]?.();
listeners[layer.id] = undefined;
});

View file

@ -17,6 +17,7 @@ import {
getUniqueID,
makePersistent,
Persistent,
PersistentState,
Replace,
setDefault,
StyleValue,
@ -101,7 +102,7 @@ export function createBuyable<T extends BuyableOptions>(
buyable.type = BuyableType;
buyable[Component] = ClickableComponent;
buyable.amount = buyable.state;
buyable.amount = buyable[PersistentState];
buyable.bought = computed(() => Decimal.gt(proxy.amount.value, 0));
buyable.canAfford = computed(
() =>
@ -121,7 +122,7 @@ export function createBuyable<T extends BuyableOptions>(
// TODO once processComputable typing works, this can be replaced
//buyable.canClick = buyable.canPurchase;
buyable.canClick = computed(() => unref(proxy.canPurchase));
buyable.onClick = buyable.purchase = function() {
buyable.onClick = buyable.purchase = function () {
if (!unref(proxy.canPurchase) || proxy.cost == null || proxy.resource == null) {
return;
}
@ -177,6 +178,6 @@ export function createBuyable<T extends BuyableOptions>(
processComputable(buyable as T, "mark");
processComputable(buyable as T, "small");
const proxy = createProxy((buyable as unknown) as Buyable<T>);
const proxy = createProxy(buyable as unknown as Buyable<T>);
return proxy;
}

View file

@ -119,7 +119,7 @@ export function createChallenge<T extends ChallengeOptions>(
challenge.maxed = computed(() =>
Decimal.gte(proxy.completions.value, unref(proxy.completionLimit))
);
challenge.toggle = function() {
challenge.toggle = function () {
if (proxy.active.value) {
if (proxy.canComplete && unref(proxy.canComplete) && !proxy.maxed.value) {
let completions: boolean | DecimalSource = unref(proxy.canComplete);
@ -188,7 +188,7 @@ export function createChallenge<T extends ChallengeOptions>(
});
}
const proxy = createProxy((challenge as unknown) as Challenge<T>);
const proxy = createProxy(challenge as unknown as Challenge<T>);
return proxy;
}

View file

@ -79,6 +79,6 @@ export function createClickable<T extends ClickableOptions>(
processComputable(clickable as T, "mark");
processComputable(clickable as T, "display");
const proxy = createProxy((clickable as unknown) as Clickable<T>);
const proxy = createProxy(clickable as unknown as Clickable<T>);
return proxy;
}

View file

@ -53,7 +53,7 @@ export function createConversion<T extends ConversionOptions>(
const conversion: T = options;
if (conversion.convert == null) {
conversion.convert = function() {
conversion.convert = function () {
unref<Resource>(proxy.gainResource).value = Decimal.add(
unref<Resource>(proxy.gainResource).value,
proxy.modifyGainAmount
@ -79,7 +79,7 @@ export function createConversion<T extends ConversionOptions>(
processComputable(conversion as T, "roundUpCost");
setDefault(conversion, "roundUpCost", true);
const proxy = createProxy((conversion as unknown) as Conversion<T>);
const proxy = createProxy(conversion as unknown as Conversion<T>);
return proxy;
}
@ -166,7 +166,7 @@ export function createIndependentConversion<S extends ConversionOptions>(
.max(1)
);
}
setDefault(conversion, "convert", function() {
setDefault(conversion, "convert", function () {
unref<Resource>(proxy.gainResource).value = proxy.modifyGainAmount
? proxy.modifyGainAmount(unref(proxy.currentGain))
: unref(proxy.currentGain);

View file

@ -3,8 +3,9 @@ import { GenericLayer } from "@/game/layers";
import Decimal, { DecimalSource } from "@/util/bignum";
import { ProcessedComputable } from "@/util/computed";
import { isArray } from "@vue/shared";
import { ComponentOptions, CSSProperties, DefineComponent, isRef, ref, Ref } from "vue";
import { ComponentOptions, CSSProperties, DefineComponent, isRef, ref, Ref, UnwrapRef } from "vue";
export const PersistentState = Symbol("PersistentState");
export const SetupPersistence = Symbol("SetupPersistence");
export const DefaultValue = Symbol("DefaultValue");
export const Component = Symbol("Component");
@ -21,10 +22,10 @@ export type State =
| { [key: string]: State }
| { [key: number]: State };
export type CoercableComponent = string | ComponentOptions | DefineComponent | JSX.Element;
export type StyleValue = string | CSSProperties | Array<StyleValue>;
export type StyleValue = string | CSSProperties | Array<string | CSSProperties>;
export type Persistent<T extends State = State> = {
state: Ref<T>;
[PersistentState]: Ref<T>;
[DefaultValue]: T;
[SetupPersistence]: () => Ref<T>;
};
@ -40,9 +41,9 @@ export type PersistentRef<T extends State = State> = Ref<T> & {
export type GenericComponent = DefineComponent<any, any, any>;
// Example usage: `<Upgrade {...wrapComputable<GenericUpgrade>(upgrade)} />`
export function wrapFeature<T>(component: T): FeatureComponent<T> {
export function wrapFeature<T>(component: T): UnwrapRef<T> {
// TODO is this okay, or do we actually need to unref each property?
return (component as unknown) as FeatureComponent<T>;
return component as unknown as UnwrapRef<T>;
}
export type FeatureComponent<T> = Omit<
@ -75,13 +76,13 @@ export function showIf(condition: boolean, otherwise = Visibility.None): Visibil
export function persistent<T extends State>(defaultValue: T | Ref<T>): PersistentRef<T> {
const persistent = isRef(defaultValue) ? defaultValue : (ref(defaultValue) as Ref<T>);
((persistent as unknown) as PersistentRef<T>)[DefaultValue] = isRef(defaultValue)
(persistent as unknown as PersistentRef<T>)[DefaultValue] = isRef(defaultValue)
? defaultValue.value
: defaultValue;
((persistent as unknown) as PersistentRef<T>)[SetupPersistence] = function() {
(persistent as unknown as PersistentRef<T>)[SetupPersistence] = function () {
return persistent;
};
return (persistent as unknown) as PersistentRef<T>;
return persistent as unknown as PersistentRef<T>;
}
export function makePersistent<T extends State>(
@ -91,7 +92,7 @@ export function makePersistent<T extends State>(
const persistent = obj as Partial<Persistent<T>>;
const state = ref(defaultValue) as Ref<T>;
Object.defineProperty(persistent, "state", {
Object.defineProperty(persistent, PersistentState, {
get: () => {
return state.value;
},
@ -100,7 +101,7 @@ export function makePersistent<T extends State>(
}
});
persistent[DefaultValue] = isRef(defaultValue) ? (defaultValue.value as T) : defaultValue;
persistent[SetupPersistence] = function() {
persistent[SetupPersistence] = function () {
return state;
};
}
@ -136,20 +137,14 @@ export function findFeatures(obj: Record<string, unknown>, type: symbol): unknow
globalBus.on("addLayer", (layer: GenericLayer, saveData: Record<string, unknown>) => {
const handleObject = (
obj: Record<string, unknown>,
persistentState: Record<string, unknown>,
foundArray: boolean
) => {
persistentState: Record<string, unknown>
): boolean => {
let foundPersistent = false;
Object.keys(obj).forEach(key => {
const value = obj[key];
if (value && typeof value === "object") {
const warnArray = foundArray || isArray(value);
if (SetupPersistence in value) {
if (warnArray) {
console.warn(
"Found persistent property inside array when adding layer. Keep in mind changing the order of persistent objects in an array will mess with existing player saves.",
layer
);
}
foundPersistent = true;
const savedValue = persistentState[key];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
persistentState[key] = (value as PersistentRef | Persistent)[
@ -162,14 +157,25 @@ globalBus.on("addLayer", (layer: GenericLayer, saveData: Record<string, unknown>
if (typeof persistentState[key] !== "object") {
persistentState[key] = {};
}
handleObject(
const foundPersistentInChild = handleObject(
value as Record<string, unknown>,
persistentState[key] as Record<string, unknown>,
warnArray
persistentState[key] as Record<string, unknown>
);
if (foundPersistentInChild) {
if (isArray(value)) {
console.warn(
"Found array that contains persistent values when adding layer. Keep in mind changing the order of elements in the array will mess with existing player saves.",
obj,
key
);
} else {
foundPersistent = true;
}
}
}
}
});
return foundPersistent;
};
handleObject(layer, saveData, false);
handleObject(layer, saveData);
});

View file

@ -5,6 +5,7 @@ import {
getUniqueID,
makePersistent,
Persistent,
PersistentState,
Replace,
setDefault,
State,
@ -138,7 +139,7 @@ export interface BaseGrid extends Persistent<Record<string | number, State>> {
id: string;
getID: (id: string | number, state: State) => string;
getState: (id: string | number, state: State) => State;
setState: (cell: string | number, state: State) => void;
setState: (id: string | number, state: State) => void;
cells: Record<string | number, GridCell>;
type: typeof GridType;
[Component]: typeof GridComponent;
@ -175,18 +176,18 @@ export function createGrid<T extends GridOptions>(options: T & ThisType<Grid<T>>
grid.id = getUniqueID("grid-");
grid[Component] = GridComponent;
grid.cells = createGridProxy((grid as unknown) as Record<string, unknown>);
grid.getID = function(this: GenericGrid, cell: string | number) {
grid.cells = createGridProxy(grid as unknown as Record<string, unknown>);
grid.getID = function (this: GenericGrid, cell: string | number) {
return grid.id + "-" + cell;
};
grid.getState = function(this: GenericGrid, cell: string | number) {
if (this.state.value[cell] != undefined) {
return this.state.value[cell];
grid.getState = function (this: GenericGrid, cell: string | number) {
if (this[PersistentState].value[cell] != undefined) {
return this[PersistentState].value[cell];
}
return this.cells[cell].startState;
};
grid.setState = function(this: GenericGrid, cell: string | number, state: State) {
this.state.value[cell] = state;
grid.setState = function (this: GenericGrid, cell: string | number, state: State) {
this[PersistentState].value[cell] = state;
};
processComputable(grid as T, "visibility");
@ -203,6 +204,6 @@ export function createGrid<T extends GridOptions>(options: T & ThisType<Grid<T>>
processComputable(grid as T, "getTitle");
processComputable(grid as T, "getDisplay");
const proxy = createProxy((grid as unknown) as Grid<T>);
const proxy = createProxy(grid as unknown as Grid<T>);
return proxy;
}

View file

@ -12,7 +12,7 @@ import { createProxy } from "@/util/proxies";
import { unref } from "vue";
import { findFeatures, Replace, setDefault } from "./feature";
export const hotkeys: Record<string, GenericHotkey> = {};
export const hotkeys: Record<string, GenericHotkey | undefined> = {};
export const HotkeyType = Symbol("Hotkey");
export interface HotkeyOptions {
@ -49,7 +49,7 @@ export function createHotkey<T extends HotkeyOptions>(options: T & ThisType<Hotk
setDefault(hotkey, "enabled", true);
processComputable(hotkey as T, "description");
const proxy = createProxy((hotkey as unknown) as Hotkey<T>);
const proxy = createProxy(hotkey as unknown as Hotkey<T>);
return proxy;
}
@ -61,11 +61,11 @@ globalBus.on("addLayer", layer => {
globalBus.on("removeLayer", layer => {
(findFeatures(layer, HotkeyType) as GenericHotkey[]).forEach(hotkey => {
delete hotkeys[hotkey.key];
hotkeys[hotkey.key] = undefined;
});
});
document.onkeydown = function(e) {
document.onkeydown = function (e) {
if ((e.target as HTMLElement | null)?.tagName === "INPUT") {
return;
}

View file

@ -5,6 +5,7 @@ import {
getUniqueID,
makePersistent,
Persistent,
PersistentState,
Replace,
setDefault,
StyleValue,
@ -70,7 +71,7 @@ export function createInfobox<T extends InfoboxOptions>(
infobox.type = InfoboxType;
infobox[Component] = InfoboxComponent;
infobox.collapsed = infobox.state;
infobox.collapsed = infobox[PersistentState];
processComputable(infobox as T, "visibility");
setDefault(infobox, "visibility", Visibility.Visible);
@ -82,6 +83,6 @@ export function createInfobox<T extends InfoboxOptions>(
processComputable(infobox as T, "title");
processComputable(infobox as T, "display");
const proxy = createProxy((infobox as unknown) as Infobox<T>);
const proxy = createProxy(infobox as unknown as Infobox<T>);
return proxy;
}

View file

@ -14,10 +14,8 @@ export interface Link extends SVGAttributes {
offsetEnd?: Position;
}
export const RegisterLinkNodeInjectionKey: InjectionKey<(
id: string,
element: HTMLElement
) => void> = Symbol("RegisterLinkNode");
export const UnregisterLinkNodeInjectionKey: InjectionKey<(id: string) => void> = Symbol(
"UnregisterLinkNode"
);
export const RegisterLinkNodeInjectionKey: InjectionKey<
(id: string, element: HTMLElement) => void
> = Symbol("RegisterLinkNode");
export const UnregisterLinkNodeInjectionKey: InjectionKey<(id: string) => void> =
Symbol("UnregisterLinkNode");

View file

@ -6,6 +6,7 @@ import {
getUniqueID,
makePersistent,
Persistent,
PersistentState,
Replace,
setDefault,
StyleValue,
@ -87,7 +88,7 @@ export function createMilestone<T extends MilestoneOptions>(
milestone.type = MilestoneType;
milestone[Component] = MilestoneComponent;
milestone.earned = milestone.state;
milestone.earned = milestone[PersistentState];
processComputable(milestone as T, "visibility");
setDefault(milestone, "visibility", Visibility.Visible);
const visibility = milestone.visibility as ProcessedComputable<Visibility>;
@ -123,18 +124,17 @@ export function createMilestone<T extends MilestoneOptions>(
processComputable(milestone as T, "classes");
processComputable(milestone as T, "display");
const proxy = createProxy((milestone as unknown) as Milestone<T>);
const proxy = createProxy(milestone as unknown as Milestone<T>);
return proxy;
}
const toast = useToast();
const listeners: Record<string, Unsubscribe> = {};
const listeners: Record<string, Unsubscribe | undefined> = {};
globalBus.on("addLayer", layer => {
const milestones: GenericMilestone[] = (findFeatures(
layer,
MilestoneType
) as GenericMilestone[]).filter(milestone => milestone.shouldEarn != null);
const milestones: GenericMilestone[] = (
findFeatures(layer, MilestoneType) as GenericMilestone[]
).filter(milestone => milestone.shouldEarn != null);
listeners[layer.id] = layer.on("postUpdate", () => {
milestones.forEach(milestone => {
if (
@ -142,7 +142,7 @@ globalBus.on("addLayer", layer => {
!milestone.earned.value &&
unref(milestone.shouldEarn)
) {
milestone.state.value = true;
milestone[PersistentState].value = true;
milestone.onComplete?.();
if (milestone.display) {
const display = unref(milestone.display);
@ -164,7 +164,7 @@ globalBus.on("addLayer", layer => {
globalBus.on("removeLayer", layer => {
// unsubscribe from postUpdate
listeners[layer.id]?.();
delete listeners[layer.id];
listeners[layer.id] = undefined;
});
declare module "@/game/settings" {

View file

@ -42,7 +42,7 @@ export function createReset<T extends ResetOptions>(options: T & ThisType<Reset<
reset.id = getUniqueID("reset-");
reset.type = ResetType;
reset.reset = function() {
reset.reset = function () {
const handleObject = (obj: Record<string, unknown>) => {
Object.keys(obj).forEach(key => {
const value = obj[key];
@ -51,7 +51,7 @@ export function createReset<T extends ResetOptions>(options: T & ThisType<Reset<
if (DefaultValue in value) {
(value as PersistentRef).value = (value as PersistentRef)[DefaultValue];
} else if (DefaultValue in obj) {
(value as PersistentRef).value = ((obj as unknown) as Persistent)[
(value as PersistentRef).value = (obj as unknown as Persistent)[
DefaultValue
];
}
@ -68,7 +68,7 @@ export function createReset<T extends ResetOptions>(options: T & ThisType<Reset<
processComputable(reset as T, "thingsToReset");
const proxy = createProxy((reset as unknown) as Reset<T>);
const proxy = createProxy(reset as unknown as Reset<T>);
return proxy;
}
@ -85,7 +85,7 @@ export function setupAutoReset(
});
}
const listeners: Record<string, Unsubscribe> = {};
const listeners: Record<string, Unsubscribe | undefined> = {};
export function trackResetTime(layer: GenericLayer, reset: GenericReset): PersistentRef<Decimal> {
const resetTime = persistent<Decimal>(new Decimal(0));
listeners[layer.id] = layer.on("preUpdate", (diff: Decimal) => {
@ -101,7 +101,7 @@ export function trackResetTime(layer: GenericLayer, reset: GenericReset): Persis
globalBus.on("removeLayer", layer => {
// unsubscribe from preUpdate
listeners[layer.id]?.();
delete listeners[layer.id];
listeners[layer.id] = undefined;
});
declare module "@/game/events" {

View file

@ -84,10 +84,7 @@ export function trackOOMPS(resource: Resource): Ref<string> {
oompsMag = -1;
} else {
while (
Decimal.div(curr, prev)
.log(10)
.div(diff)
.gte("100") &&
Decimal.div(curr, prev).log(10).div(diff).gte("100") &&
oompsMag <= 5 &&
Decimal.gt(prev, 0)
) {

View file

@ -34,6 +34,6 @@ export function createTab<T extends TabOptions>(options: T & ThisType<Tab<T>>):
tab.type = TabType;
tab[Component] = TabComponent;
const proxy = createProxy((tab as unknown) as Tab<T>);
const proxy = createProxy(tab as unknown as Tab<T>);
return proxy;
}

View file

@ -15,6 +15,7 @@ import {
getUniqueID,
makePersistent,
Persistent,
PersistentState,
Replace,
setDefault,
StyleValue,
@ -73,7 +74,7 @@ export function createTabButton<T extends TabButtonOptions>(
processComputable(tabButton as T, "style");
processComputable(tabButton as T, "glowColor");
const proxy = createProxy((tabButton as unknown) as TabButton<T>);
const proxy = createProxy(tabButton as unknown as TabButton<T>);
return proxy;
}
@ -114,10 +115,10 @@ export function createTabFamily<T extends TabFamilyOptions>(
tabFamily.activeTab = computed(() => {
const tabs = unref(proxy.tabs);
if (
proxy.state.value in tabs &&
unref(tabs[proxy.state.value].visibility) === Visibility.Visible
proxy[PersistentState].value in tabs &&
unref(tabs[proxy[PersistentState].value].visibility) === Visibility.Visible
) {
return unref(tabs[proxy.state.value].tab);
return unref(tabs[proxy[PersistentState].value].tab);
}
const firstTab = Object.values(tabs).find(
tab => unref(tab.visibility) === Visibility.Visible
@ -128,6 +129,6 @@ export function createTabFamily<T extends TabFamilyOptions>(
return null;
});
const proxy = createProxy((tabFamily as unknown) as TabFamily<T>);
const proxy = createProxy(tabFamily as unknown as TabFamily<T>);
return proxy;
}

View file

@ -99,7 +99,7 @@ export function createTreeNode<T extends TreeNodeOptions>(
processComputable(treeNode as T, "style");
processComputable(treeNode as T, "mark");
const proxy = createProxy((treeNode as unknown) as TreeNode<T>);
const proxy = createProxy(treeNode as unknown as TreeNode<T>);
return proxy;
}
@ -155,7 +155,7 @@ export function createTree<T extends TreeOptions>(options: T & ThisType<Tree<T>>
tree.isResetting = ref(false);
tree.resettingNode = ref(null);
tree.reset = function(node) {
tree.reset = function (node) {
proxy.isResetting.value = true;
proxy.resettingNode.value = node;
proxy.resetPropagation?.(proxy, node);
@ -171,7 +171,7 @@ export function createTree<T extends TreeOptions>(options: T & ThisType<Tree<T>>
processComputable(tree as T, "rightSideNodes");
processComputable(tree as T, "branches");
const proxy = createProxy((tree as unknown) as Tree<T>);
const proxy = createProxy(tree as unknown as Tree<T>);
return proxy;
}
@ -179,7 +179,7 @@ export type ResetPropagation = {
(tree: GenericTree, resettingNode: GenericTreeNode): void;
};
export const defaultResetPropagation = function(
export const defaultResetPropagation = function (
tree: GenericTree,
resettingNode: GenericTreeNode
): void {
@ -190,7 +190,7 @@ export const defaultResetPropagation = function(
}
};
export const invertedResetPropagation = function(
export const invertedResetPropagation = function (
tree: GenericTree,
resettingNode: GenericTreeNode
): void {
@ -201,7 +201,7 @@ export const invertedResetPropagation = function(
}
};
export const branchedResetPropagation = function(
export const branchedResetPropagation = function (
tree: GenericTree,
resettingNode: GenericTreeNode
): void {

View file

@ -6,6 +6,7 @@ import {
getUniqueID,
makePersistent,
Persistent,
PersistentState,
Replace,
setDefault,
StyleValue,
@ -93,7 +94,7 @@ export function createUpgrade<T extends UpgradeOptions>(
);
}
upgrade.bought = upgrade.state;
upgrade.bought = upgrade[PersistentState];
if (upgrade.canAfford == null) {
upgrade.canAfford = computed(
() =>
@ -105,7 +106,7 @@ export function createUpgrade<T extends UpgradeOptions>(
if (upgrade.canPurchase == null) {
upgrade.canPurchase = computed(() => unref(proxy.canAfford) && !unref(proxy.bought));
}
upgrade.purchase = function() {
upgrade.purchase = function () {
if (!unref(proxy.canPurchase)) {
return;
}
@ -115,7 +116,7 @@ export function createUpgrade<T extends UpgradeOptions>(
unref(proxy.cost)
);
}
proxy.state.value = true;
proxy[PersistentState].value = true;
proxy.onPurchase?.();
};
@ -129,7 +130,7 @@ export function createUpgrade<T extends UpgradeOptions>(
processComputable(upgrade as T, "resource");
processComputable(upgrade as T, "canPurchase");
const proxy = createProxy((upgrade as unknown) as Upgrade<T>);
const proxy = createProxy(upgrade as unknown as Upgrade<T>);
return proxy;
}

View file

@ -1,9 +1,8 @@
import { hasWon } from "@/data/mod";
import modInfo from "@/data/modInfo.json";
import Decimal, { DecimalSource } from "@/util/bignum";
import { createNanoEvents } from "nanoevents";
import { App } from "vue";
import { GenericLayer, layers } from "./layers";
import { App, Ref } from "vue";
import { GenericLayer } from "./layers";
import player from "./player";
import settings, { Settings } from "./settings";
import state from "./state";
@ -16,19 +15,14 @@ export interface GlobalEvents {
setupVue: (vue: App) => void;
}
export interface LayerEvents {
// Generation
preUpdate: (diff: Decimal) => void;
// Actions (e.g. automation)
update: (diff: Decimal) => void;
// Effects (e.g. milestones)
postUpdate: (diff: Decimal) => void;
}
export const globalBus = createNanoEvents<GlobalEvents>();
let intervalID: number | null = null;
// Not imported immediately due to dependency cycles
// This gets set during startGameLoop(), and will only be used in the update function
let hasWon: null | Ref<boolean> = null;
function update() {
const now = Date.now();
let diff: DecimalSource = (now - player.time) / 1e3;
@ -41,7 +35,7 @@ function update() {
}
// Stop here if the game is paused on the win screen
if (hasWon.value && !player.keepGoing) {
if (hasWon?.value && !player.keepGoing) {
return;
}
// Stop here if the player had a NaN value
@ -94,22 +88,11 @@ function update() {
}
}
export function startGameLoop(): void {
export async function startGameLoop() {
hasWon = (await import("@/data/mod")).hasWon;
if (settings.unthrottled) {
requestAnimationFrame(update);
} else {
intervalID = setInterval(update, 50);
}
}
globalBus.on("update", function updateLayers(diff) {
Object.values(layers).forEach(layer => {
layer.emit("preUpdate", diff);
});
Object.values(layers).forEach(layer => {
layer.emit("update", diff);
});
Object.values(layers).forEach(layer => {
layer.emit("postUpdate", diff);
});
});

View file

@ -7,6 +7,7 @@ import {
StyleValue
} from "@/features/feature";
import { Link } from "@/features/links";
import Decimal from "@/util/bignum";
import {
Computable,
GetComputableType,
@ -16,10 +17,19 @@ import {
} from "@/util/computed";
import { createProxy } from "@/util/proxies";
import { createNanoEvents, Emitter } from "nanoevents";
import { globalBus, LayerEvents } from "./events";
import { globalBus } from "./events";
import player from "./player";
export const layers: Record<string, Readonly<GenericLayer>> = {};
export interface LayerEvents {
// Generation
preUpdate: (diff: Decimal) => void;
// Actions (e.g. automation)
update: (diff: Decimal) => void;
// Effects (e.g. milestones)
postUpdate: (diff: Decimal) => void;
}
export const layers: Record<string, Readonly<GenericLayer> | undefined> = {};
window.layers = layers;
export interface Position {
@ -75,8 +85,8 @@ export function createLayer<T extends LayerOptions>(options: T): Layer<T> {
const layer: T & Partial<BaseLayer> = options;
const emitter = (layer.emitter = createNanoEvents<LayerEvents>());
layer.on = emitter.on;
layer.emit = emitter.emit;
layer.on = emitter.on.bind(emitter);
layer.emit = emitter.emit.bind(emitter);
layer.minimized = persistent(false);
@ -90,7 +100,7 @@ export function createLayer<T extends LayerOptions>(options: T): Layer<T> {
setDefault(layer, "minimizable", true);
processComputable(layer as T, "links");
const proxy = createProxy((layer as unknown) as Layer<T>);
const proxy = createProxy(layer as unknown as Layer<T>);
return proxy;
}
@ -98,7 +108,8 @@ export function addLayer(
layer: GenericLayer,
player: { layers?: Record<string, Record<string, unknown>> }
): void {
if (layer.id in layers) {
console.info("Adding layer", layer.id);
if (layers[layer.id]) {
console.error(
"Attempted to add layer with same ID as existing layer",
layer.id,
@ -121,9 +132,10 @@ export function getLayer<T extends GenericLayer>(layerID: string): () => T {
}
export function removeLayer(layer: GenericLayer): void {
console.info("Removing layer", layer.id);
globalBus.emit("removeLayer", layer);
delete layers[layer.id];
layers[layer.id] = undefined;
}
export function reloadLayer(layer: GenericLayer): void {
@ -132,3 +144,15 @@ export function reloadLayer(layer: GenericLayer): void {
// Re-create layer
addLayer(layer, player);
}
globalBus.on("update", function updateLayers(diff) {
Object.values(layers).forEach(layer => {
layer?.emit("preUpdate", diff);
});
Object.values(layers).forEach(layer => {
layer?.emit("update", diff);
});
Object.values(layers).forEach(layer => {
layer?.emit("postUpdate", diff);
});
});

View file

@ -97,7 +97,7 @@ const playerHandler: ProxyHandler<Record<PropertyKey, any>> = {
state.autosave = false;
transientState.hasNaN = true;
transientState.NaNPath = [...target[ProxyPath], property];
transientState.NaNReceiver = (receiver as unknown) as Record<string, unknown>;
transientState.NaNReceiver = receiver as unknown as Record<string, unknown>;
console.error(
`Attempted to set NaN value`,
[...target[ProxyPath], property],

View file

@ -1,7 +1,6 @@
import { App as VueApp, createApp } from "vue";
import App from "./App.vue";
import modInfo from "./data/modInfo.json";
import { globalBus, startGameLoop } from "./game/events";
import { GenericLayer } from "./game/layers";
import { PlayerData } from "./game/player";
import { Settings } from "./game/settings";
@ -15,7 +14,7 @@ declare global {
save: VoidFunction;
hardReset: VoidFunction;
hardResetSettings: VoidFunction;
layers: Record<string, Readonly<GenericLayer>>;
layers: Record<string, Readonly<GenericLayer> | undefined>;
player: PlayerData;
state: Transient;
settings: Settings;
@ -34,6 +33,7 @@ declare global {
requestAnimationFrame(async () => {
await load();
const { globalBus, startGameLoop } = await require("./game/events");
// Create Vue
const vue = (window.vue = createApp({

View file

@ -1,4 +1,3 @@
import { fixOldSave, getInitialLayers } from "@/data/mod";
import modInfo from "@/data/modInfo.json";
import player, { Player, PlayerData, stringifySave } from "@/game/player";
import settings, { loadSettings } from "@/game/settings";
@ -48,6 +47,7 @@ export async function load(): Promise<void> {
player.id = settings.active;
await loadSave(player);
} catch (e) {
console.error("Failed to load save. Falling back to new save.\n", e);
await loadSave(newSave());
}
}
@ -73,11 +73,15 @@ export function getUniqueID(): string {
}
export async function loadSave(playerObj: Partial<PlayerData>): Promise<void> {
const { layers, removeLayer, addLayer } = await import("../game/layers");
console.info("Loading save", playerObj);
const { layers, removeLayer, addLayer } = await import("@/game/layers");
const { fixOldSave, getInitialLayers } = await import("@/data/mod");
for (const layer in layers) {
removeLayer(layers[layer]);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
removeLayer(layers[layer]!);
}
console.log(getInitialLayers(playerObj))
getInitialLayers(playerObj).forEach(layer => addLayer(layer, playerObj));
setupInitialStore(playerObj);

View file

@ -12,6 +12,6 @@ module.exports = {
config.plugins.delete("fork-ts-checker");
},
devServer: {
disableHostCheck: true
allowedHosts: "all"
}
};