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 = { module.exports = {
root: true, root: true,
env: { env: {
node: true, node: true
'vue/setup-compiler-macros': true
}, },
extends: [ extends: [
"plugin:vue/vue3-essential", "plugin:vue/vue3-essential",
"eslint:recommended", "@vue/eslint-config-typescript/recommended",
"@vue/typescript/recommended", "@vue/eslint-config-prettier"
"@vue/prettier",
"@vue/prettier/@typescript-eslint"
], ],
parserOptions: { parserOptions: {
ecmaVersion: 2020 ecmaVersion: 2020
}, },
ignorePatterns: ["src/lib"],
rules: { rules: {
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off", "no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
"no-debugger": 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" "vue/no-mutating-props": "off"
},
globals: {
defineProps: "readonly",
defineEmits: "readonly"
} }
}; };

View file

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

20968
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

@ -12,12 +12,14 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script lang="ts">
import { FeatureComponent, Visibility } from "@/features/feature"; import { FeatureComponent, Visibility } from "@/features/feature";
import { GenericGrid } from "@/features/grid"; import { GenericGrid } from "@/features/grid";
import { defineComponent } from "vue";
import GridCell from "./GridCell.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> </script>
<style scoped></style>

View file

@ -14,7 +14,7 @@
:disabled="!canClick" :disabled="!canClick"
> >
<div v-if="title"><component :is="titleComponent" /></div> <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" /> <LinkNode :id="id" />
</button> </button>
</template> </template>

View file

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

View file

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

View file

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

View file

@ -20,12 +20,16 @@
</span> </span>
</template> </template>
<script setup lang="ts"> <script lang="ts">
import { FeatureComponent, wrapFeature } from "@/features/feature"; import { FeatureComponent, wrapFeature } from "@/features/feature";
import { GenericTree } from "@/features/tree"; import { GenericTree } from "@/features/tree";
import { defineComponent } from "vue";
import TreeNode from "./TreeNode.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> </script>
<style scoped> <style scoped>

View file

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

View file

@ -1,7 +1,9 @@
<template> <template>
<form @submit.prevent="submit"> <form @submit.prevent="submit">
<div class="field"> <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 <VueTextareaAutosize
v-if="textArea" v-if="textArea"
v-model="value" v-model="value"

View file

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

View file

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

View file

@ -6,8 +6,8 @@
v-for="(link, index) in validLinks" v-for="(link, index) in validLinks"
:key="index" :key="index"
:link="link" :link="link"
:startNode="nodes[link.startNode.id]" :startNode="nodes[link.startNode.id]!"
:endNode="nodes[link.endNode.id]" :endNode="nodes[link.endNode.id]!"
/> />
</svg> </svg>
</template> </template>
@ -24,16 +24,31 @@ import LinkVue from "./Link.vue";
const props = toRefs(defineProps<{ links: Link[] }>()); 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(() => const validLinks = computed(() =>
unref(props.links.value).filter(link => { unref(props.links.value).filter(link => {
const n = nodes.value; const n = nodes.value;
return ( return (
link.startNode.id in n && n[link.startNode.id]?.x != undefined &&
link.endNode.id in n && n[link.startNode.id]?.y != undefined &&
n[link.startNode.id].x != undefined && n[link.endNode.id]?.x != undefined &&
n[link.startNode.id].y != undefined && n[link.endNode.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 => { provide(UnregisterLinkNodeInjectionKey, id => {
delete nodes.value[id]; nodes.value[id] = undefined;
}); });
function updateNodes() { function updateNodes() {
@ -64,12 +79,13 @@ function updateNodes() {
} }
function updateNode(id: string) { function updateNode(id: string) {
if (!(id in nodes.value)) { const node = nodes.value[id];
if (!node) {
return; return;
} }
const linkStartRect = nodes.value[id].element.getBoundingClientRect(); const linkStartRect = node.element.getBoundingClientRect();
nodes.value[id].x = linkStartRect.x + linkStartRect.width / 2 - boundingRect.value.x; node.x = linkStartRect.x + linkStartRect.width / 2 - boundingRect.value.x;
nodes.value[id].y = linkStartRect.y + linkStartRect.height / 2 - boundingRect.value.y; node.y = linkStartRect.y + linkStartRect.height / 2 - boundingRect.value.y;
} }
function updateBounds() { function updateBounds() {
@ -78,23 +94,6 @@ function updateBounds() {
updateNodes(); 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> </script>
<style scoped> <style scoped>

View file

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

View file

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

View file

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

View file

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

View file

@ -103,7 +103,7 @@ export function createResetButton<T extends ClickableOptions & ResetButtonOption
onClick?.(); onClick?.();
}; };
const proxy = (createClickable(options) as unknown) as ResetButton<T>; const proxy = createClickable(options) as unknown as ResetButton<T>;
return proxy; return proxy;
} }
@ -122,7 +122,7 @@ export type LayerTreeNode<T extends LayerTreeNodeOptions> = Replace<
export function createLayerTreeNode<T extends LayerTreeNodeOptions>(options: T): LayerTreeNode<T> { export function createLayerTreeNode<T extends LayerTreeNodeOptions>(options: T): LayerTreeNode<T> {
processComputable(options as T, "append"); processComputable(options as T, "append");
return (createTreeNode({ return createTreeNode({
...options, ...options,
display: options.layerID, display: options.layerID,
onClick: onClick:
@ -141,5 +141,5 @@ export function createLayerTreeNode<T extends LayerTreeNodeOptions>(options: T):
: function () { : function () {
player.tabs.splice(1, 1, options.layerID); player.tabs.splice(1, 1, options.layerID);
} }
}) as unknown) as LayerTreeNode<T>; }) as unknown as LayerTreeNode<T>;
} }

View file

@ -108,7 +108,7 @@ const funChallenge = createChallenge({
return showIf(Decimal.gt(best.value, 0)); return showIf(Decimal.gt(best.value, 0));
}, },
goal: 20, goal: 20,
resource: mainPoints, resource: () => mainPoints,
onComplete() { onComplete() {
console.log("hiii"); console.log("hiii");
}, },
@ -249,10 +249,7 @@ const longBoi = createBar({
width: 300, width: 300,
height: 30, height: 30,
progress() { progress() {
return Decimal.add(mainPoints.value, 1) return Decimal.add(mainPoints.value, 1).log(10).div(10).toNumber();
.log(10)
.div(10)
.toNumber();
}, },
display() { display() {
return format(mainPoints.value) + " / 1e10 points"; return format(mainPoints.value) + " / 1e10 points";
@ -393,16 +390,16 @@ const tree = createTree({
}, },
branches: [ branches: [
{ {
from: fNode, startNode: fNode,
to: treeNode, endNode: treeNode,
style: { style: {
strokeWidth: "25px", strokeWidth: "25px",
stroke: "blue", stroke: "blue",
filter: "blur(5px)" filter: "blur(5px)"
} }
}, },
{ from: treeNode, to: g }, { startNode: treeNode, endNode: g },
{ from: g, to: h } { 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 { createResource, trackBest, trackOOMPS, trackTotal } from "@/features/resource";
import { createTree, GenericTree } from "@/features/tree"; import { createTree, GenericTree } from "@/features/tree";
import { globalBus } from "@/game/events"; import { globalBus } from "@/game/events";
@ -5,7 +7,8 @@ import { createLayer, GenericLayer } from "@/game/layers";
import player, { PlayerData } from "@/game/player"; import player, { PlayerData } from "@/game/player";
import { DecimalSource } from "@/lib/break_eternity"; import { DecimalSource } from "@/lib/break_eternity";
import Decimal, { format, formatSmall, formatTime } from "@/util/bignum"; 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 a from "./layers/aca/a";
import c, { import c, {
generatorUpgrade, generatorUpgrade,
@ -18,6 +21,7 @@ export const points = createResource<DecimalSource>(0);
const best = trackBest(points); const best = trackBest(points);
const total = trackTotal(points); const total = trackTotal(points);
const oomps = trackOOMPS(points); const oomps = trackOOMPS(points);
const showModal = ref(false);
const pointGain = computed(() => { const pointGain = computed(() => {
if (!generatorUpgrade.bought) return new Decimal(0); if (!generatorUpgrade.bought) return new Decimal(0);
@ -73,8 +77,8 @@ export const main = createLayer({
<div v-if={Decimal.gt(pointGain.value, 0)}> <div v-if={Decimal.gt(pointGain.value, 0)}>
({oomps.value === "" ? formatSmall(pointGain.value) : oomps.value}/sec) ({oomps.value === "" ? formatSmall(pointGain.value) : oomps.value}/sec)
</div> </div>
<spacer /> <Spacer />
<modal show={false}> <Modal v-model={showModal}>
<svg style="height: 80vmin; width: 80vmin;"> <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" /> <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="128" cy="64" r="64" fill="#71368a" />
<circle cx="192" cy="128" r="64" fill="#fa8508" /> <circle cx="192" cy="128" r="64" fill="#fa8508" />
</svg> </svg>
</modal> </Modal>
<tree {...tree} /> {render(tree)}
</template> </template>
); );
}, },

View file

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

View file

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

View file

@ -17,6 +17,7 @@ import {
getUniqueID, getUniqueID,
makePersistent, makePersistent,
Persistent, Persistent,
PersistentState,
Replace, Replace,
setDefault, setDefault,
StyleValue, StyleValue,
@ -101,7 +102,7 @@ export function createBuyable<T extends BuyableOptions>(
buyable.type = BuyableType; buyable.type = BuyableType;
buyable[Component] = ClickableComponent; buyable[Component] = ClickableComponent;
buyable.amount = buyable.state; buyable.amount = buyable[PersistentState];
buyable.bought = computed(() => Decimal.gt(proxy.amount.value, 0)); buyable.bought = computed(() => Decimal.gt(proxy.amount.value, 0));
buyable.canAfford = computed( buyable.canAfford = computed(
() => () =>
@ -177,6 +178,6 @@ export function createBuyable<T extends BuyableOptions>(
processComputable(buyable as T, "mark"); processComputable(buyable as T, "mark");
processComputable(buyable as T, "small"); 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; return proxy;
} }

View file

@ -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; return proxy;
} }

View file

@ -79,6 +79,6 @@ export function createClickable<T extends ClickableOptions>(
processComputable(clickable as T, "mark"); processComputable(clickable as T, "mark");
processComputable(clickable as T, "display"); 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; return proxy;
} }

View file

@ -79,7 +79,7 @@ export function createConversion<T extends ConversionOptions>(
processComputable(conversion as T, "roundUpCost"); processComputable(conversion as T, "roundUpCost");
setDefault(conversion, "roundUpCost", true); setDefault(conversion, "roundUpCost", true);
const proxy = createProxy((conversion as unknown) as Conversion<T>); const proxy = createProxy(conversion as unknown as Conversion<T>);
return proxy; return proxy;
} }

View file

@ -3,8 +3,9 @@ import { GenericLayer } from "@/game/layers";
import Decimal, { DecimalSource } from "@/util/bignum"; import Decimal, { DecimalSource } from "@/util/bignum";
import { ProcessedComputable } from "@/util/computed"; import { ProcessedComputable } from "@/util/computed";
import { isArray } from "@vue/shared"; 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 SetupPersistence = Symbol("SetupPersistence");
export const DefaultValue = Symbol("DefaultValue"); export const DefaultValue = Symbol("DefaultValue");
export const Component = Symbol("Component"); export const Component = Symbol("Component");
@ -21,10 +22,10 @@ export type State =
| { [key: string]: State } | { [key: string]: State }
| { [key: number]: State }; | { [key: number]: State };
export type CoercableComponent = string | ComponentOptions | DefineComponent | JSX.Element; 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> = { export type Persistent<T extends State = State> = {
state: Ref<T>; [PersistentState]: Ref<T>;
[DefaultValue]: T; [DefaultValue]: T;
[SetupPersistence]: () => Ref<T>; [SetupPersistence]: () => Ref<T>;
}; };
@ -40,9 +41,9 @@ export type PersistentRef<T extends State = State> = Ref<T> & {
export type GenericComponent = DefineComponent<any, any, any>; export type GenericComponent = DefineComponent<any, any, any>;
// Example usage: `<Upgrade {...wrapComputable<GenericUpgrade>(upgrade)} />` // 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? // 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< 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> { export function persistent<T extends State>(defaultValue: T | Ref<T>): PersistentRef<T> {
const persistent = isRef(defaultValue) ? defaultValue : (ref(defaultValue) as Ref<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.value
: defaultValue; : defaultValue;
((persistent as unknown) as PersistentRef<T>)[SetupPersistence] = function() { (persistent as unknown as PersistentRef<T>)[SetupPersistence] = function () {
return persistent; return persistent;
}; };
return (persistent as unknown) as PersistentRef<T>; return persistent as unknown as PersistentRef<T>;
} }
export function makePersistent<T extends State>( export function makePersistent<T extends State>(
@ -91,7 +92,7 @@ export function makePersistent<T extends State>(
const persistent = obj as Partial<Persistent<T>>; const persistent = obj as Partial<Persistent<T>>;
const state = ref(defaultValue) as Ref<T>; const state = ref(defaultValue) as Ref<T>;
Object.defineProperty(persistent, "state", { Object.defineProperty(persistent, PersistentState, {
get: () => { get: () => {
return state.value; return state.value;
}, },
@ -136,20 +137,14 @@ export function findFeatures(obj: Record<string, unknown>, type: symbol): unknow
globalBus.on("addLayer", (layer: GenericLayer, saveData: Record<string, unknown>) => { globalBus.on("addLayer", (layer: GenericLayer, saveData: Record<string, unknown>) => {
const handleObject = ( const handleObject = (
obj: Record<string, unknown>, obj: Record<string, unknown>,
persistentState: Record<string, unknown>, persistentState: Record<string, unknown>
foundArray: boolean ): boolean => {
) => { let foundPersistent = false;
Object.keys(obj).forEach(key => { Object.keys(obj).forEach(key => {
const value = obj[key]; const value = obj[key];
if (value && typeof value === "object") { if (value && typeof value === "object") {
const warnArray = foundArray || isArray(value);
if (SetupPersistence in value) { if (SetupPersistence in value) {
if (warnArray) { foundPersistent = true;
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
);
}
const savedValue = persistentState[key]; const savedValue = persistentState[key];
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
persistentState[key] = (value as PersistentRef | Persistent)[ persistentState[key] = (value as PersistentRef | Persistent)[
@ -162,14 +157,25 @@ globalBus.on("addLayer", (layer: GenericLayer, saveData: Record<string, unknown>
if (typeof persistentState[key] !== "object") { if (typeof persistentState[key] !== "object") {
persistentState[key] = {}; persistentState[key] = {};
} }
handleObject( const foundPersistentInChild = handleObject(
value as Record<string, unknown>, value as Record<string, unknown>,
persistentState[key] as Record<string, unknown>, persistentState[key] as Record<string, unknown>
warnArray
); );
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, getUniqueID,
makePersistent, makePersistent,
Persistent, Persistent,
PersistentState,
Replace, Replace,
setDefault, setDefault,
State, State,
@ -138,7 +139,7 @@ export interface BaseGrid extends Persistent<Record<string | number, State>> {
id: string; id: string;
getID: (id: string | number, state: State) => string; getID: (id: string | number, state: State) => string;
getState: (id: string | number, state: State) => State; 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>; cells: Record<string | number, GridCell>;
type: typeof GridType; type: typeof GridType;
[Component]: typeof GridComponent; [Component]: typeof GridComponent;
@ -175,18 +176,18 @@ export function createGrid<T extends GridOptions>(options: T & ThisType<Grid<T>>
grid.id = getUniqueID("grid-"); grid.id = getUniqueID("grid-");
grid[Component] = GridComponent; grid[Component] = GridComponent;
grid.cells = createGridProxy((grid as unknown) as Record<string, unknown>); grid.cells = createGridProxy(grid as unknown as Record<string, unknown>);
grid.getID = function (this: GenericGrid, cell: string | number) { grid.getID = function (this: GenericGrid, cell: string | number) {
return grid.id + "-" + cell; return grid.id + "-" + cell;
}; };
grid.getState = function (this: GenericGrid, cell: string | number) { grid.getState = function (this: GenericGrid, cell: string | number) {
if (this.state.value[cell] != undefined) { if (this[PersistentState].value[cell] != undefined) {
return this.state.value[cell]; return this[PersistentState].value[cell];
} }
return this.cells[cell].startState; return this.cells[cell].startState;
}; };
grid.setState = function (this: GenericGrid, cell: string | number, state: State) { grid.setState = function (this: GenericGrid, cell: string | number, state: State) {
this.state.value[cell] = state; this[PersistentState].value[cell] = state;
}; };
processComputable(grid as T, "visibility"); 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, "getTitle");
processComputable(grid as T, "getDisplay"); 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; return proxy;
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -51,7 +51,7 @@ export function createReset<T extends ResetOptions>(options: T & ThisType<Reset<
if (DefaultValue in value) { if (DefaultValue in value) {
(value as PersistentRef).value = (value as PersistentRef)[DefaultValue]; (value as PersistentRef).value = (value as PersistentRef)[DefaultValue];
} else if (DefaultValue in obj) { } else if (DefaultValue in obj) {
(value as PersistentRef).value = ((obj as unknown) as Persistent)[ (value as PersistentRef).value = (obj as unknown as Persistent)[
DefaultValue DefaultValue
]; ];
} }
@ -68,7 +68,7 @@ export function createReset<T extends ResetOptions>(options: T & ThisType<Reset<
processComputable(reset as T, "thingsToReset"); 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; 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> { export function trackResetTime(layer: GenericLayer, reset: GenericReset): PersistentRef<Decimal> {
const resetTime = persistent<Decimal>(new Decimal(0)); const resetTime = persistent<Decimal>(new Decimal(0));
listeners[layer.id] = layer.on("preUpdate", (diff: Decimal) => { listeners[layer.id] = layer.on("preUpdate", (diff: Decimal) => {
@ -101,7 +101,7 @@ export function trackResetTime(layer: GenericLayer, reset: GenericReset): Persis
globalBus.on("removeLayer", layer => { globalBus.on("removeLayer", layer => {
// unsubscribe from preUpdate // unsubscribe from preUpdate
listeners[layer.id]?.(); listeners[layer.id]?.();
delete listeners[layer.id]; listeners[layer.id] = undefined;
}); });
declare module "@/game/events" { declare module "@/game/events" {

View file

@ -84,10 +84,7 @@ export function trackOOMPS(resource: Resource): Ref<string> {
oompsMag = -1; oompsMag = -1;
} else { } else {
while ( while (
Decimal.div(curr, prev) Decimal.div(curr, prev).log(10).div(diff).gte("100") &&
.log(10)
.div(diff)
.gte("100") &&
oompsMag <= 5 && oompsMag <= 5 &&
Decimal.gt(prev, 0) 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.type = TabType;
tab[Component] = TabComponent; tab[Component] = TabComponent;
const proxy = createProxy((tab as unknown) as Tab<T>); const proxy = createProxy(tab as unknown as Tab<T>);
return proxy; return proxy;
} }

View file

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

View file

@ -99,7 +99,7 @@ export function createTreeNode<T extends TreeNodeOptions>(
processComputable(treeNode as T, "style"); processComputable(treeNode as T, "style");
processComputable(treeNode as T, "mark"); 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; return proxy;
} }
@ -171,7 +171,7 @@ export function createTree<T extends TreeOptions>(options: T & ThisType<Tree<T>>
processComputable(tree as T, "rightSideNodes"); processComputable(tree as T, "rightSideNodes");
processComputable(tree as T, "branches"); 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; return proxy;
} }

View file

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

View file

@ -1,9 +1,8 @@
import { hasWon } from "@/data/mod";
import modInfo from "@/data/modInfo.json"; import modInfo from "@/data/modInfo.json";
import Decimal, { DecimalSource } from "@/util/bignum"; import Decimal, { DecimalSource } from "@/util/bignum";
import { createNanoEvents } from "nanoevents"; import { createNanoEvents } from "nanoevents";
import { App } from "vue"; import { App, Ref } from "vue";
import { GenericLayer, layers } from "./layers"; import { GenericLayer } from "./layers";
import player from "./player"; import player from "./player";
import settings, { Settings } from "./settings"; import settings, { Settings } from "./settings";
import state from "./state"; import state from "./state";
@ -16,19 +15,14 @@ export interface GlobalEvents {
setupVue: (vue: App) => void; 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>(); export const globalBus = createNanoEvents<GlobalEvents>();
let intervalID: number | null = null; 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() { function update() {
const now = Date.now(); const now = Date.now();
let diff: DecimalSource = (now - player.time) / 1e3; let diff: DecimalSource = (now - player.time) / 1e3;
@ -41,7 +35,7 @@ function update() {
} }
// Stop here if the game is paused on the win screen // Stop here if the game is paused on the win screen
if (hasWon.value && !player.keepGoing) { if (hasWon?.value && !player.keepGoing) {
return; return;
} }
// Stop here if the player had a NaN value // 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) { if (settings.unthrottled) {
requestAnimationFrame(update); requestAnimationFrame(update);
} else { } else {
intervalID = setInterval(update, 50); 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 StyleValue
} from "@/features/feature"; } from "@/features/feature";
import { Link } from "@/features/links"; import { Link } from "@/features/links";
import Decimal from "@/util/bignum";
import { import {
Computable, Computable,
GetComputableType, GetComputableType,
@ -16,10 +17,19 @@ import {
} from "@/util/computed"; } from "@/util/computed";
import { createProxy } from "@/util/proxies"; import { createProxy } from "@/util/proxies";
import { createNanoEvents, Emitter } from "nanoevents"; import { createNanoEvents, Emitter } from "nanoevents";
import { globalBus, LayerEvents } from "./events"; import { globalBus } from "./events";
import player from "./player"; 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; window.layers = layers;
export interface Position { export interface Position {
@ -75,8 +85,8 @@ export function createLayer<T extends LayerOptions>(options: T): Layer<T> {
const layer: T & Partial<BaseLayer> = options; const layer: T & Partial<BaseLayer> = options;
const emitter = (layer.emitter = createNanoEvents<LayerEvents>()); const emitter = (layer.emitter = createNanoEvents<LayerEvents>());
layer.on = emitter.on; layer.on = emitter.on.bind(emitter);
layer.emit = emitter.emit; layer.emit = emitter.emit.bind(emitter);
layer.minimized = persistent(false); layer.minimized = persistent(false);
@ -90,7 +100,7 @@ export function createLayer<T extends LayerOptions>(options: T): Layer<T> {
setDefault(layer, "minimizable", true); setDefault(layer, "minimizable", true);
processComputable(layer as T, "links"); 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; return proxy;
} }
@ -98,7 +108,8 @@ export function addLayer(
layer: GenericLayer, layer: GenericLayer,
player: { layers?: Record<string, Record<string, unknown>> } player: { layers?: Record<string, Record<string, unknown>> }
): void { ): void {
if (layer.id in layers) { console.info("Adding layer", layer.id);
if (layers[layer.id]) {
console.error( console.error(
"Attempted to add layer with same ID as existing layer", "Attempted to add layer with same ID as existing layer",
layer.id, layer.id,
@ -121,9 +132,10 @@ export function getLayer<T extends GenericLayer>(layerID: string): () => T {
} }
export function removeLayer(layer: GenericLayer): void { export function removeLayer(layer: GenericLayer): void {
console.info("Removing layer", layer.id);
globalBus.emit("removeLayer", layer); globalBus.emit("removeLayer", layer);
delete layers[layer.id]; layers[layer.id] = undefined;
} }
export function reloadLayer(layer: GenericLayer): void { export function reloadLayer(layer: GenericLayer): void {
@ -132,3 +144,15 @@ export function reloadLayer(layer: GenericLayer): void {
// Re-create layer // Re-create layer
addLayer(layer, player); 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; state.autosave = false;
transientState.hasNaN = true; transientState.hasNaN = true;
transientState.NaNPath = [...target[ProxyPath], property]; 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( console.error(
`Attempted to set NaN value`, `Attempted to set NaN value`,
[...target[ProxyPath], property], [...target[ProxyPath], property],

View file

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

View file

@ -1,4 +1,3 @@
import { fixOldSave, getInitialLayers } from "@/data/mod";
import modInfo from "@/data/modInfo.json"; import modInfo from "@/data/modInfo.json";
import player, { Player, PlayerData, stringifySave } from "@/game/player"; import player, { Player, PlayerData, stringifySave } from "@/game/player";
import settings, { loadSettings } from "@/game/settings"; import settings, { loadSettings } from "@/game/settings";
@ -48,6 +47,7 @@ export async function load(): Promise<void> {
player.id = settings.active; player.id = settings.active;
await loadSave(player); await loadSave(player);
} catch (e) { } catch (e) {
console.error("Failed to load save. Falling back to new save.\n", e);
await loadSave(newSave()); await loadSave(newSave());
} }
} }
@ -73,11 +73,15 @@ export function getUniqueID(): string {
} }
export async function loadSave(playerObj: Partial<PlayerData>): Promise<void> { 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) { 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)); getInitialLayers(playerObj).forEach(layer => addLayer(layer, playerObj));
setupInitialStore(playerObj); setupInitialStore(playerObj);

View file

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