Fixing building / cleanup
This commit is contained in:
parent
6f781b33fa
commit
15a460bf42
51 changed files with 5727 additions and 15879 deletions
18
.eslintrc.js
18
.eslintrc.js
|
@ -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"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
{
|
{
|
||||||
|
"arrowParens": "avoid",
|
||||||
|
"endOfLine": "auto",
|
||||||
"printWidth": 100,
|
"printWidth": 100,
|
||||||
"tabWidth": 4
|
"tabWidth": 4,
|
||||||
|
"trailingComma": "none"
|
||||||
}
|
}
|
||||||
|
|
20968
package-lock.json
generated
20968
package-lock.json
generated
File diff suppressed because it is too large
Load diff
21
package.json
21
package.json
|
@ -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"
|
||||||
]
|
]
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -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>
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -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;
|
||||||
});
|
});
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
});
|
});
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
|
||||||
);
|
|
||||||
|
|
|
@ -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" {
|
||||||
|
|
|
@ -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" {
|
||||||
|
|
|
@ -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)
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -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],
|
||||||
|
|
|
@ -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({
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -12,6 +12,6 @@ module.exports = {
|
||||||
config.plugins.delete("fork-ts-checker");
|
config.plugins.delete("fork-ts-checker");
|
||||||
},
|
},
|
||||||
devServer: {
|
devServer: {
|
||||||
disableHostCheck: true
|
allowedHosts: "all"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue