Merge remote-tracking branch 'template/main'

This commit is contained in:
thepaperpilot 2022-06-26 22:16:00 -05:00
commit edb50d6c95
104 changed files with 17396 additions and 16363 deletions

19
.github/workflows/test.yml vendored Normal file
View file

@ -0,0 +1,19 @@
name: Build and Deploy
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Use Node.js 16.x
uses: actions/setup-node@v3
with:
node-version: 16.x
- run: npm ci
- run: npm run build --if-present
- run: npm test

View file

@ -6,6 +6,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [0.4.2] - 2022-05-23
### Added
- costModifier to conversions
- onConvert(amountGained) to conversions
### Changed
- **BREAKING** getFirstFeature has a new signature, that will lead to improved performance
- trackResetTime is now intended to be used with a reset button
- regularFormat handles small numbers better
- Slider tooltips now appear below the slider, not above
- Node's mutation observers now ignore attributes. This shouldn't have issues with links/particle effect positions, but prevents a _lot_ of unnecessary node updates
- OptionsFunc no longer takes its S type parameter, as it was unnecessary. Layer options functions now have proper `this` typing
- Several functions have been updated to take BaseLayer instead of GenericLayer, to allow them to work with `this` inside layer options functions
### Fixed
- Particle effects and links would not always appear on reload or when switching layers
- Particle effects and links no longer appear in wrong spot after nodes are added or removed
- Collapsibles having wrong widths on the button and collapsed content sections
- Additive modifiers with negative values appeared like "+-" instead of "-"
- Buyables' onPurchase was not being called
- Reset button would display "Next:" if the buyMax property is a ref
## [0.4.1] - 2022-05-10
### Added
- findFeatures can now accept multiple feature types

View file

@ -11,9 +11,9 @@ A game engine that grows with you
npm install
```
### Compiles and hot-reloads for development
### Hosts dev server and hot-reloads modules as they're changed
```
npm run serve
npm starts
```
### Compiles and minifies for production
@ -21,6 +21,11 @@ npm run serve
npm run build
```
### Hosts the production build
```
npm run preview
```
### Lints and fixes files
```
npm run lint

View file

@ -1,12 +0,0 @@
module.exports = {
presets: ["@vue/cli-plugin-babel/preset"],
plugins: [
[
"module:@jetblack/operator-overloading",
{
enabled: true
}
],
"@vue/babel-plugin-jsx"
]
};

View file

@ -12,18 +12,19 @@
<link href="https://fonts.googleapis.com/icon?family=Material+Icons|Material+Icons+Outlined" rel="stylesheet">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/site.webmanifest">
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="alternate icon" type="image/png" sizes="48x48" href="/favicon.ico">
<meta name="theme-color" content="#2E3440">
<title><%= htmlWebpackPlugin.options.title %></title>
<title>Profectus</title>
<meta name="description" content="A project made in Profectus"/>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
<strong>We're sorry but Profectus doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
<script type="module" src="./src/main.ts"></script>
</body>
</html>

7
jest.config.js Normal file
View file

@ -0,0 +1,7 @@
module.exports = {
preset: "vite-jest",
testEnvironment: "jest-environment-jsdom",
moduleNameMapper: {
"^./../([^.].*)$": "$1"
}
};

32301
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,20 +1,26 @@
{
"name": "profectus",
"version": "0.4.1",
"version": "0.4.2",
"private": true,
"scripts": {
"start": "vue-cli-service serve",
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
"start": "vite",
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview",
"test": "vite-jest --no-cache"
},
"dependencies": {
"@pixi/particle-emitter": "^5.0.4",
"core-js": "^3.6.5",
"@vitejs/plugin-vue": "^2.3.3",
"@vitejs/plugin-vue-jsx": "^1.3.10",
"is-plain-object": "^5.0.0",
"lodash.clonedeep": "^4.5.0",
"lz-string": "^1.4.4",
"nanoevents": "^6.0.2",
"pixi.js": "^6.3.0",
"vite": "^2.9.12",
"vite-plugin-pwa": "^0.12.0",
"vite-tsconfig-paths": "^3.5.0",
"vue": "^3.2.26",
"vue-next-select": "^2.10.2",
"vue-panzoom": "^1.1.6",
@ -25,42 +31,26 @@
},
"devDependencies": {
"@ivanv/vue-collapse-transition": "^1.0.2",
"@jetblack/operator-overloading": "^0.2.0",
"@rushstack/eslint-patch": "^1.1.0",
"@types/jest": "^28.1.3",
"@types/lodash.clonedeep": "^4.5.6",
"@types/lz-string": "^1.3.34",
"@vue/babel-plugin-jsx": "^1.1.1",
"@vue/cli-plugin-babel": "^5.0.3",
"@vue/cli-plugin-eslint": "^5.0.3",
"@vue/cli-plugin-typescript": "^5.0.3",
"@vue/cli-service": "^5.0.3",
"@vue/compiler-sfc": "^3.2.26",
"@vue/eslint-config-prettier": "^7.0.0",
"@vue/eslint-config-typescript": "^10.0.0",
"babel-eslint": "^10.1.0",
"babel-jest": "^28.1.1",
"eslint": "^8.6.0",
"lint-staged": "^12.3.4",
"jest": "^27.5.1",
"jest-environment-jsdom": "^27.5.1",
"prettier": "^2.5.1",
"raw-loader": "^4.0.2",
"sass": "^1.48.0",
"sass-loader": "^10.2.0",
"tsconfig-paths-webpack-plugin": "^3.5.1",
"typescript": "~4.5.5"
"typescript": "~4.5.5",
"vite-jest": "^0.1.4",
"vue-tsc": "^0.38.1"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
],
"gitHooks": {
"pre-commit": "lint-staged"
},
"lint-staged": {
"*.{js,jsx,ts,tsx,vue}": [
"vue-cli-service lint",
"git add"
]
},
"engines": {
"node": "16.x"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 873 B

24
public/favicon.svg Normal file
View file

@ -0,0 +1,24 @@
<svg width="656" height="649" viewBox="0 0 656 649" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_65_3)">
<rect x="26" y="21" width="600" height="600" rx="30" fill="#2E3440"/>
</g>
<path d="M313 572.877C295.453 567.525 262.205 562.614 233 572.877C243.737 355.447 238.724 271.878 233 107.877C233 107.877 279.614 70.7611 313 65.8775C346.386 60.9938 399.025 76.3369 433 115.877C438 145.877 428.647 222.534 389 271.878C354.851 314.378 259 310.358 259 364.877C259 466.877 313 572.877 313 572.877Z" stroke="#A3BE8C" stroke-width="10" stroke-miterlimit="16" stroke-linecap="round"/>
<path d="M433 115.878C421.023 186.453 397.39 226.835 370.997 251.5C305.783 312.444 223.719 277.436 259 364.877" stroke="#A3BE8C" stroke-width="5" stroke-miterlimit="16" stroke-linecap="round"/>
<path d="M406.5 248C395.275 252.447 387.434 253.134 370.997 251.5" stroke="#A3BE8C" stroke-width="5" stroke-miterlimit="16" stroke-linecap="round"/>
<path d="M285.5 306.5C323.145 305.626 339.011 298.775 368.5 288" stroke="#A3BE8C" stroke-width="5" stroke-miterlimit="16" stroke-linecap="round"/>
<path d="M433 115.877C381.5 77.8262 298.094 104.947 259 167C254.257 174.528 251.896 182.373 250.674 191C247.146 215.91 253.117 247.34 238.673 296.5" stroke="#A3BE8C" stroke-width="5" stroke-miterlimit="16" stroke-linecap="round"/>
<path d="M233 107.878L250.674 191" stroke="#A3BE8C" stroke-width="5" stroke-miterlimit="16" stroke-linecap="round"/>
<path d="M215 386.5C228.178 408.708 231.486 429.334 236.7 467.5" stroke="#A3BE8C" stroke-width="5" stroke-miterlimit="16" stroke-linecap="round"/>
<defs>
<filter id="filter0_d_65_3" x="22" y="21" width="608" height="608" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="4"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_65_3"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_65_3" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View file

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

2
public/robots.txt Normal file
View file

@ -0,0 +1,2 @@
User-agent: *
Allow: /

View file

@ -1 +0,0 @@
{"name":"Profectus","short_name":"Profectus","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#2E3440","background_color":"#2E3440","display":"standalone"}

View file

@ -23,9 +23,10 @@ import projInfo from "./data/projInfo.json";
import themes from "./data/themes";
import settings, { gameComponents } from "./game/settings";
import "./main.css";
import type { CSSProperties } from "vue";
const useHeader = projInfo.useHeader;
const theme = computed(() => themes[settings.theme].variables);
const theme = computed(() => themes[settings.theme].variables as CSSProperties);
const showTPS = toRef(settings, "showTPS");
const gameComponent = computed(() => {

View file

@ -8,9 +8,9 @@ import {
RegisterNodeInjectionKey,
UnregisterNodeInjectionKey,
NodesInjectionKey,
FeatureNode,
BoundsInjectionKey
} from "game/layers";
import type { FeatureNode } from "game/layers";
import { nextTick, onMounted, provide, ref } from "vue";
const emit = defineEmits<{

View file

@ -24,7 +24,8 @@
<script setup lang="ts">
import projInfo from "data/projInfo.json";
import { GenericLayer, layers } from "game/layers";
import type { GenericLayer } from "game/layers";
import { layers } from "game/layers";
import player from "game/player";
import { computed, toRef, unref } from "vue";
import Layer from "./Layer.vue";

View file

@ -21,11 +21,7 @@
<div class="link" @click="openChangelog">Changelog</div>
<br />
<div>
<a
:href="discordLink"
v-if="discordLink !== 'https://discord.gg/WzejVAx'"
class="info-modal-discord-link"
>
<a :href="discordLink" v-if="discordLink" class="info-modal-discord-link">
<span class="material-icons info-modal-discord">discord</span>
{{ discordName }}
</a>

View file

@ -17,12 +17,13 @@
<script lang="ts">
import projInfo from "data/projInfo.json";
import { CoercableComponent } from "features/feature";
import { FeatureNode } from "game/layers";
import { Persistent } from "game/persistence";
import type { CoercableComponent } from "features/feature";
import type { FeatureNode } from "game/layers";
import type { Persistent } from "game/persistence";
import player from "game/player";
import { computeComponent, processedPropType, wrapRef } from "util/vue";
import { computed, defineComponent, nextTick, PropType, Ref, toRefs, unref, watch } from "vue";
import type { PropType, Ref } from "vue";
import { computed, defineComponent, nextTick, toRefs, unref, watch } from "vue";
import Context from "./Context.vue";
export default defineComponent({

View file

@ -39,7 +39,7 @@
</template>
<script setup lang="ts">
import { FeatureNode } from "game/layers";
import type { FeatureNode } from "game/layers";
import { computed, ref, toRefs } from "vue";
import Context from "./Context.vue";

View file

@ -47,8 +47,10 @@ import Modal from "components/Modal.vue";
import projInfo from "data/projInfo.json";
import player from "game/player";
import state from "game/state";
import Decimal, { DecimalSource, format } from "util/bignum";
import { ComponentPublicInstance, computed, ref, toRef } from "vue";
import type { DecimalSource } from "util/bignum";
import Decimal, { format } from "util/bignum";
import type { ComponentPublicInstance } from "vue";
import { computed, ref, toRef } from "vue";
import Toggle from "./fields/Toggle.vue";
import SavesManager from "./SavesManager.vue";

View file

@ -11,7 +11,7 @@
<div class="discord">
<span @click="openDiscord" class="material-icons">discord</span>
<ul class="discord-links">
<li v-if="discordLink !== 'https://discord.gg/WzejVAx'">
<li v-if="discordLink">
<a :href="discordLink" target="_blank">{{ discordName }}</a>
</li>
<li>
@ -78,7 +78,7 @@
<div class="discord">
<span @click="openDiscord" class="material-icons">discord</span>
<ul class="discord-links">
<li v-if="discordLink !== 'https://discord.gg/WzejVAx'">
<li v-if="discordLink">
<a :href="discordLink" target="_blank">{{ discordName }}</a>
</li>
<li>
@ -101,12 +101,13 @@
<script setup lang="ts">
import Changelog from "data/Changelog.vue";
import projInfo from "data/projInfo.json";
import { ComponentPublicInstance, ref } from "vue";
import Tooltip from "features/tooltips/Tooltip.vue";
import { Direction } from "util/common";
import type { ComponentPublicInstance } from "vue";
import { ref } from "vue";
import Info from "./Info.vue";
import Options from "./Options.vue";
import SavesManager from "./SavesManager.vue";
import Tooltip from "features/tooltips/Tooltip.vue";
import { Direction } from "util/common";
const info = ref<ComponentPublicInstance<typeof Info> | null>(null);
const savesManager = ref<ComponentPublicInstance<typeof SavesManager> | null>(null);

View file

@ -23,6 +23,7 @@ import Modal from "components/Modal.vue";
import projInfo from "data/projInfo.json";
import rawThemes from "data/themes";
import { jsx } from "features/feature";
import Tooltip from "features/tooltips/Tooltip.vue";
import player from "game/player";
import settings, { settingFields } from "game/settings";
import { camelToTitle } from "util/common";
@ -30,7 +31,6 @@ import { coerceComponent, render } from "util/vue";
import { computed, ref, toRefs } from "vue";
import Select from "./fields/Select.vue";
import Toggle from "./fields/Toggle.vue";
import Tooltip from "features/tooltips/Tooltip.vue";
const isOpen = ref(false);

View file

@ -77,7 +77,7 @@ import { computed, ref, toRefs, watch } from "vue";
import DangerButton from "./fields/DangerButton.vue";
import FeedbackButton from "./fields/FeedbackButton.vue";
import Text from "./fields/Text.vue";
import { LoadablePlayerData } from "./SavesManager.vue";
import type { LoadablePlayerData } from "./SavesManager.vue";
const _props = defineProps<{
save: LoadablePlayerData;

View file

@ -57,18 +57,20 @@
</template>
<script setup lang="ts">
import projInfo from "data/projInfo.json";
import Modal from "components/Modal.vue";
import player, { PlayerData, stringifySave } from "game/player";
import projInfo from "data/projInfo.json";
import type { PlayerData } from "game/player";
import player, { stringifySave } from "game/player";
import settings from "game/settings";
import { getUniqueID, loadSave, save, newSave } from "util/save";
import { ComponentPublicInstance, computed, nextTick, ref, shallowReactive, watch } from "vue";
import LZString from "lz-string";
import { ProxyState } from "util/proxies";
import { getUniqueID, loadSave, newSave, save } from "util/save";
import type { ComponentPublicInstance } from "vue";
import { computed, nextTick, ref, shallowReactive, watch } from "vue";
import Draggable from "vuedraggable";
import Select from "./fields/Select.vue";
import Text from "./fields/Text.vue";
import Save from "./Save.vue";
import Draggable from "vuedraggable";
import LZString from "lz-string";
import { ProxyState } from "util/proxies";
export type LoadablePlayerData = Omit<Partial<PlayerData>, "id"> & { id: string; error?: unknown };
@ -124,13 +126,15 @@ watch(saveToImport, importedSave => {
}
});
let bankContext = require.context("raw-loader!../../saves", true, /\.txt$/);
let bankContext = import.meta.globEager("./../../saves/*.txt", { as: "raw" });
let bank = ref(
bankContext.keys().reduce((acc: Array<{ label: string; value: string }>, curr) => {
// .slice(2, -4) strips the leading ./ and the trailing .txt
Object.keys(bankContext).reduce((acc: Array<{ label: string; value: string }>, curr) => {
acc.push({
label: curr.slice(2, -4),
value: bankContext(curr).default
// .slice(2, -4) strips the leading ./ and the trailing .txt
label: curr.split("/").slice(-1)[0].slice(0, -4),
// Have to perform this unholy cast because globEager's typing doesn't appear to know
// adding { as: "raw" } will make the object contain strings rather than modules
value: bankContext[curr] as unknown as string
});
return acc;
}, [])

View file

@ -9,7 +9,8 @@
<script setup lang="ts">
import state from "game/state";
import Decimal, { DecimalSource, formatWhole } from "util/bignum";
import type { DecimalSource } from "util/bignum";
import Decimal, { formatWhole } from "util/bignum";
import { computed, ref, watchEffect } from "vue";
const tps = computed(() =>

View file

@ -15,7 +15,7 @@ const emit = defineEmits<{
}>();
const activated = ref(false);
const activatedTimeout = ref<number | null>(null);
const activatedTimeout = ref<NodeJS.Timer | null>(null);
function click() {
emit("click");

View file

@ -15,7 +15,7 @@
<script setup lang="ts">
import "components/common/fields.css";
import { CoercableComponent } from "features/feature";
import type { CoercableComponent } from "features/feature";
import { computeOptionalComponent, unwrapRef } from "util/vue";
import { ref, toRef, watch } from "vue";
import VueNextSelect from "vue-next-select";

View file

@ -8,10 +8,10 @@
</template>
<script setup lang="ts">
import { computed, toRefs, unref } from "vue";
import Tooltip from "features/tooltips/Tooltip.vue";
import "components/common/fields.css";
import Tooltip from "features/tooltips/Tooltip.vue";
import { Direction } from "util/common";
import { computed, toRefs, unref } from "vue";
const _props = defineProps<{
title?: string;

View file

@ -26,11 +26,11 @@
</template>
<script setup lang="ts">
import { CoercableComponent } from "features/feature";
import "components/common/fields.css";
import type { CoercableComponent } from "features/feature";
import { coerceComponent } from "util/vue";
import { computed, onMounted, ref, toRefs, unref } from "vue";
import VueTextareaAutosize from "vue-textarea-autosize";
import "components/common/fields.css";
const _props = defineProps<{
title?: CoercableComponent;

View file

@ -6,10 +6,10 @@
</template>
<script setup lang="ts">
import { CoercableComponent } from "features/feature";
import "components/common/fields.css";
import type { CoercableComponent } from "features/feature";
import { coerceComponent } from "util/vue";
import { computed, unref } from "vue";
import "components/common/fields.css";
const props = defineProps<{
title?: CoercableComponent;

View file

@ -8,9 +8,10 @@
</template>
<script setup lang="ts">
import { CoercableComponent } from "features/feature";
import type { CoercableComponent } from "features/feature";
import { computeComponent } from "util/vue";
import { Ref, toRef } from "vue";
import type { Ref } from "vue";
import { toRef } from "vue";
import Col from "./Column.vue";
const props = defineProps<{

View file

@ -1,41 +1,28 @@
import {
Clickable,
ClickableOptions,
createClickable,
GenericClickable
} from "features/clickables/clickable";
import { GenericConversion } from "features/conversion";
import {
CoercableComponent,
jsx,
JSXFunction,
OptionsFunc,
Replace,
setDefault
} from "features/feature";
import type { Clickable, ClickableOptions, GenericClickable } from "features/clickables/clickable";
import { createClickable } from "features/clickables/clickable";
import type { GenericConversion } from "features/conversion";
import type { CoercableComponent, JSXFunction, OptionsFunc, Replace } from "features/feature";
import { jsx, setDefault } from "features/feature";
import { displayResource } from "features/resources/resource";
import {
createTreeNode,
GenericTree,
GenericTreeNode,
TreeNode,
TreeNodeOptions
} from "features/trees/tree";
import { Modifier } from "game/modifiers";
import { Persistent, persistent } from "game/persistence";
import type { GenericTree, GenericTreeNode, TreeNode, TreeNodeOptions } from "features/trees/tree";
import { createTreeNode } from "features/trees/tree";
import type { Modifier } from "game/modifiers";
import type { Persistent } from "game/persistence";
import { DefaultValue, persistent } from "game/persistence";
import player from "game/player";
import Decimal, { DecimalSource, format } from "util/bignum";
import { WithRequired } from "util/common";
import {
import type { DecimalSource } from "util/bignum";
import Decimal, { format } from "util/bignum";
import type { WithRequired } from "util/common";
import type {
Computable,
convertComputable,
GetComputableType,
GetComputableTypeWithDefault,
processComputable,
ProcessedComputable
} from "util/computed";
import { convertComputable, processComputable } from "util/computed";
import { renderJSX } from "util/vue";
import { computed, Ref, unref } from "vue";
import type { Ref } from "vue";
import { computed, unref } from "vue";
import "./common.css";
export interface ResetButtonOptions extends ClickableOptions {
@ -47,6 +34,7 @@ export interface ResetButtonOptions extends ClickableOptions {
display?: Computable<CoercableComponent>;
canClick?: Computable<boolean>;
minimumGain?: Computable<DecimalSource>;
resetTime?: Persistent<DecimalSource>;
}
export type ResetButton<T extends ResetButtonOptions> = Replace<
@ -136,6 +124,9 @@ export function createResetButton<T extends ClickableOptions & ResetButtonOption
}
resetButton.conversion.convert();
resetButton.tree.reset(resetButton.treeNode);
if (resetButton.resetTime) {
resetButton.resetTime.value = resetButton.resetTime[DefaultValue];
}
onClick?.();
};

View file

@ -11,7 +11,7 @@ import { createResource } from "features/resources/resource";
import { addTooltip } from "features/tooltips/tooltip";
import { createResourceTooltip } from "features/trees/tree";
import { createLayer } from "game/layers";
import { DecimalSource } from "util/bignum";
import type { DecimalSource } from "util/bignum";
import { render } from "util/vue";
import { createLayerTreeNode, createResetButton } from "../common";

View file

@ -2,11 +2,16 @@ import Profectus from "components/Profectus.vue";
import Spacer from "components/layout/Spacer.vue";
import { jsx } from "features/feature";
import { createResource, trackBest, trackOOMPS, trackTotal } from "features/resources/resource";
import { branchedResetPropagation, createTree, GenericTree } from "features/trees/tree";
import type { GenericTree } from "features/trees/tree";
import { branchedResetPropagation, createTree } from "features/trees/tree";
import { globalBus } from "game/events";
import { createLayer, GenericLayer, setupLayerModal } from "game/layers";
import player, { PlayerData } from "game/player";
import Decimal, { DecimalSource, format, formatTime } from "util/bignum";
import type { GenericLayer } from "game/layers";
import { setupLayerModal } from "game/layers";
import { createLayer } from "game/layers";
import type { PlayerData } from "game/player";
import player from "game/player";
import type { DecimalSource } from "util/bignum";
import Decimal, { format, formatTime } from "util/bignum";
import { render } from "util/vue";
import { computed, toRaw } from "vue";
import a from "./layers/aca/a";

View file

@ -1,9 +1,10 @@
{
"title": "Profectus",
"title": "Profectus Demo",
"description": "A demo project made in Profectus",
"id": "profectus-demo",
"author": "thepaperpilot",
"discordName": "The Paper Pilot Community",
"discordLink": "https://discord.gg/WzejVAx",
"author": "",
"discordName": "",
"discordLink": "",
"versionNumber": "0.0",
"versionTitle": "Initial Commit",

View file

@ -29,6 +29,10 @@ export interface Theme {
declare module "@vue/runtime-dom" {
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface CSSProperties extends Partial<ThemeVars> {}
interface HTMLAttributes {
style?: StyleValue;
}
}
const defaultTheme: Theme = {

View file

@ -23,12 +23,14 @@
</template>
<script lang="ts">
import { CoercableComponent, Visibility } from "features/feature";
import { computeOptionalComponent, processedPropType } from "util/vue";
import { defineComponent, StyleValue, toRefs, unref } from "vue";
import Node from "components/Node.vue";
import MarkNode from "components/MarkNode.vue";
import "components/common/features.css";
import MarkNode from "components/MarkNode.vue";
import Node from "components/Node.vue";
import type { CoercableComponent } from "features/feature";
import { Visibility } from "features/feature";
import { computeOptionalComponent, processedPropType } from "util/vue";
import type { StyleValue } from "vue";
import { defineComponent, toRefs, unref } from "vue";
export default defineComponent({
props: {

View file

@ -2,23 +2,24 @@ import AchievementComponent from "features/achievements/Achievement.vue";
import {
CoercableComponent,
Component,
OptionsFunc,
GatherProps,
getUniqueID,
OptionsFunc,
Replace,
setDefault,
StyleValue,
Visibility
} from "features/feature";
import "game/notifications";
import { Persistent, persistent } from "game/persistence";
import {
import type { Persistent } from "game/persistence";
import { persistent } from "game/persistence";
import type {
Computable,
GetComputableType,
GetComputableTypeWithDefault,
processComputable,
ProcessedComputable
} from "util/computed";
import { processComputable } from "util/computed";
import { createLazyProxy } from "util/proxies";
import { coerceComponent } from "util/vue";
import { unref, watchEffect } from "vue";
@ -68,7 +69,7 @@ export type GenericAchievement = Replace<
>;
export function createAchievement<T extends AchievementOptions>(
optionsFunc?: OptionsFunc<T, Achievement<T>, BaseAchievement>
optionsFunc?: OptionsFunc<T, BaseAchievement, GenericAchievement>
): Achievement<T> {
const earned = persistent<boolean>(false);
return createLazyProxy(() => {

View file

@ -45,13 +45,15 @@
</template>
<script lang="ts">
import { CoercableComponent, Visibility } from "features/feature";
import Decimal, { DecimalSource } from "util/bignum";
import { computeOptionalComponent, processedPropType, unwrapRef } from "util/vue";
import { computed, CSSProperties, defineComponent, StyleValue, toRefs, unref } from "vue";
import Node from "components/Node.vue";
import MarkNode from "components/MarkNode.vue";
import Node from "components/Node.vue";
import { CoercableComponent, Visibility } from "features/feature";
import type { DecimalSource } from "util/bignum";
import Decimal from "util/bignum";
import { Direction } from "util/common";
import { computeOptionalComponent, processedPropType, unwrapRef } from "util/vue";
import type { CSSProperties, StyleValue } from "vue";
import { computed, defineComponent, toRefs, unref } from "vue";
export default defineComponent({
props: {

View file

@ -1,24 +1,15 @@
import BarComponent from "features/bars/Bar.vue";
import {
CoercableComponent,
Component,
OptionsFunc,
GatherProps,
getUniqueID,
Replace,
setDefault,
StyleValue,
Visibility
} from "features/feature";
import { DecimalSource } from "util/bignum";
import type { CoercableComponent, OptionsFunc, Replace, StyleValue } from "features/feature";
import { Component, GatherProps, getUniqueID, setDefault, Visibility } from "features/feature";
import type { DecimalSource } from "util/bignum";
import { Direction } from "util/common";
import {
import type {
Computable,
GetComputableType,
GetComputableTypeWithDefault,
processComputable,
ProcessedComputable
} from "util/computed";
import { processComputable } from "util/computed";
import { createLazyProxy } from "util/proxies";
import { unref } from "vue";
@ -74,7 +65,7 @@ export type GenericBar = Replace<
>;
export function createBar<T extends BarOptions>(
optionsFunc: OptionsFunc<T, Bar<T>, BaseBar>
optionsFunc: OptionsFunc<T, BaseBar, GenericBar>
): Bar<T> {
return createLazyProxy(() => {
const bar = optionsFunc();

View file

@ -51,18 +51,19 @@
</template>
<script setup lang="ts">
import {
import type {
BoardData,
BoardNode,
BoardNodeLink,
GenericBoardNodeAction,
GenericNodeType,
getNodeProperty
GenericNodeType
} from "features/boards/board";
import { StyleValue, Visibility } from "features/feature";
import { getNodeProperty } from "features/boards/board";
import type { StyleValue } from "features/feature";
import { Visibility } from "features/feature";
import { PersistentState } from "game/persistence";
import { ProcessedComputable } from "util/computed";
import { computed, Ref, ref, toRefs, unref } from "vue";
import type { ProcessedComputable } from "util/computed";
import { computed, ref, Ref, toRefs, unref } from "vue";
import panZoom from "vue-panzoom";
import BoardLinkVue from "./BoardLink.vue";
import BoardNodeVue from "./BoardNode.vue";

View file

@ -11,7 +11,7 @@
</template>
<script setup lang="ts">
import { BoardNodeLink } from "features/boards/board";
import type { BoardNodeLink } from "features/boards/board";
import { computed, toRefs, unref } from "vue";
const _props = defineProps<{

View file

@ -168,14 +168,8 @@
<script setup lang="ts">
import themes from "data/themes";
import {
BoardNode,
GenericBoardNodeAction,
GenericNodeType,
getNodeProperty,
ProgressDisplay,
Shape
} from "features/boards/board";
import type { BoardNode, GenericBoardNodeAction, GenericNodeType } from "features/boards/board";
import { ProgressDisplay, getNodeProperty, Shape } from "features/boards/board";
import { Visibility } from "features/feature";
import settings from "game/settings";
import { computed, ref, toRefs, unref, watch } from "vue";

View file

@ -1,29 +1,28 @@
import BoardComponent from "features/boards/Board.vue";
import type { OptionsFunc, Replace, StyleValue } from "features/feature";
import {
Component,
OptionsFunc,
findFeatures,
GatherProps,
getUniqueID,
Replace,
setDefault,
StyleValue,
Visibility
} from "features/feature";
import { globalBus } from "game/events";
import { State, Persistent, PersistentState, persistent } from "game/persistence";
import type { Persistent, State } from "game/persistence";
import { persistent, PersistentState } from "game/persistence";
import type { Unsubscribe } from "nanoevents";
import { isFunction } from "util/common";
import {
import type {
Computable,
GetComputableType,
GetComputableTypeWithDefault,
processComputable,
ProcessedComputable
} from "util/computed";
import { processComputable } from "util/computed";
import { createLazyProxy } from "util/proxies";
import { Unsubscribe } from "nanoevents";
import { computed, Ref, unref } from "vue";
import { Link } from "../links/links";
import type { Link } from "../links/links";
export const BoardType = Symbol("Board");
@ -198,7 +197,7 @@ export type GenericBoard = Replace<
>;
export function createBoard<T extends BoardOptions>(
optionsFunc: OptionsFunc<T, Board<T>, BaseBoard>
optionsFunc: OptionsFunc<T, BaseBoard, GenericBoard>
): Board<T> {
return createLazyProxy(
persistent => {

View file

@ -1,29 +1,22 @@
import ClickableComponent from "features/clickables/Clickable.vue";
import { Resource } from "features/resources/resource";
import { Persistent, persistent } from "game/persistence";
import Decimal, { DecimalSource, format, formatWhole } from "util/bignum";
import {
import type { CoercableComponent, OptionsFunc, Replace, StyleValue } from "features/feature";
import { Component, GatherProps, getUniqueID, jsx, setDefault, Visibility } from "features/feature";
import type { Resource } from "features/resources/resource";
import type { Persistent } from "game/persistence";
import { persistent } from "game/persistence";
import type { DecimalSource } from "util/bignum";
import Decimal, { format, formatWhole } from "util/bignum";
import type {
Computable,
GetComputableType,
GetComputableTypeWithDefault,
processComputable,
ProcessedComputable
} from "util/computed";
import { processComputable } from "util/computed";
import { createLazyProxy } from "util/proxies";
import { coerceComponent, isCoercableComponent } from "util/vue";
import { computed, Ref, unref } from "vue";
import {
CoercableComponent,
Component,
OptionsFunc,
GatherProps,
getUniqueID,
jsx,
Replace,
setDefault,
StyleValue,
Visibility
} from "./feature";
import type { Ref } from "vue";
import { computed, unref } from "vue";
export const BuyableType = Symbol("Buyable");
@ -88,7 +81,7 @@ export type GenericBuyable = Replace<
>;
export function createBuyable<T extends BuyableOptions>(
optionsFunc: OptionsFunc<T, Buyable<T>, BaseBuyable>
optionsFunc: OptionsFunc<T, BaseBuyable, GenericBuyable>
): Buyable<T> {
const amount = persistent<DecimalSource>(0);
return createLazyProxy(() => {

View file

@ -32,23 +32,15 @@
<script lang="tsx">
import "components/common/features.css";
import { GenericChallenge } from "features/challenges/challenge";
import { jsx, StyleValue, Visibility } from "features/feature";
import MarkNode from "components/MarkNode.vue";
import Node from "components/Node.vue";
import type { GenericChallenge } from "features/challenges/challenge";
import type { StyleValue } from "features/feature";
import { jsx, Visibility } from "features/feature";
import { getHighNotifyStyle, getNotifyStyle } from "game/notifications";
import { coerceComponent, isCoercableComponent, processedPropType, unwrapRef } from "util/vue";
import {
Component,
computed,
defineComponent,
PropType,
shallowRef,
toRefs,
unref,
UnwrapRef,
watchEffect
} from "vue";
import Node from "components/Node.vue";
import MarkNode from "components/MarkNode.vue";
import type { Component, PropType, UnwrapRef } from "vue";
import { computed, defineComponent, shallowRef, toRefs, unref, watchEffect } from "vue";
export default defineComponent({
props: {

View file

@ -1,33 +1,26 @@
import { isArray } from "@vue/shared";
import Toggle from "components/fields/Toggle.vue";
import ChallengeComponent from "features/challenges/Challenge.vue";
import {
CoercableComponent,
Component,
OptionsFunc,
GatherProps,
getUniqueID,
jsx,
Replace,
setDefault,
StyleValue,
Visibility
} from "features/feature";
import { GenericReset } from "features/reset";
import { Resource } from "features/resources/resource";
import type { CoercableComponent, OptionsFunc, Replace, StyleValue } from "features/feature";
import { Component, GatherProps, getUniqueID, jsx, setDefault, Visibility } from "features/feature";
import type { GenericReset } from "features/reset";
import type { Resource } from "features/resources/resource";
import { globalBus } from "game/events";
import { Persistent, persistent } from "game/persistence";
import type { Persistent } from "game/persistence";
import { persistent } from "game/persistence";
import settings, { registerSettingField } from "game/settings";
import Decimal, { DecimalSource } from "util/bignum";
import {
import type { DecimalSource } from "util/bignum";
import Decimal from "util/bignum";
import type {
Computable,
GetComputableType,
GetComputableTypeWithDefault,
processComputable,
ProcessedComputable
} from "util/computed";
import { processComputable } from "util/computed";
import { createLazyProxy } from "util/proxies";
import { computed, Ref, unref, watch, WatchStopHandle } from "vue";
import type { Ref, WatchStopHandle } from "vue";
import { computed, unref, watch } from "vue";
export const ChallengeType = Symbol("ChallengeType");
@ -97,7 +90,7 @@ export type GenericChallenge = Replace<
>;
export function createChallenge<T extends ChallengeOptions>(
optionsFunc: OptionsFunc<T, Challenge<T>, BaseChallenge>
optionsFunc: OptionsFunc<T, BaseChallenge, GenericChallenge>
): Challenge<T> {
const completions = persistent(0);
const active = persistent(false);

View file

@ -29,10 +29,11 @@
<script lang="tsx">
import "components/common/features.css";
import Node from "components/Node.vue";
import MarkNode from "components/MarkNode.vue";
import { GenericClickable } from "features/clickables/clickable";
import { jsx, StyleValue, Visibility } from "features/feature";
import Node from "components/Node.vue";
import type { GenericClickable } from "features/clickables/clickable";
import type { StyleValue } from "features/feature";
import { jsx, Visibility } from "features/feature";
import {
coerceComponent,
isCoercableComponent,
@ -40,16 +41,8 @@ import {
setupHoldToClick,
unwrapRef
} from "util/vue";
import {
Component,
defineComponent,
PropType,
shallowRef,
toRefs,
unref,
UnwrapRef,
watchEffect
} from "vue";
import type { Component, PropType, UnwrapRef } from "vue";
import { defineComponent, shallowRef, toRefs, unref, watchEffect } from "vue";
export default defineComponent({
props: {

View file

@ -1,24 +1,15 @@
import ClickableComponent from "features/clickables/Clickable.vue";
import {
CoercableComponent,
Component,
OptionsFunc,
GatherProps,
getUniqueID,
Replace,
setDefault,
StyleValue,
Visibility
} from "features/feature";
import { GenericLayer } from "game/layers";
import { Unsubscribe } from "nanoevents";
import {
import type { CoercableComponent, OptionsFunc, Replace, StyleValue } from "features/feature";
import { Component, GatherProps, getUniqueID, setDefault, Visibility } from "features/feature";
import type { BaseLayer } from "game/layers";
import type { Unsubscribe } from "nanoevents";
import type {
Computable,
GetComputableType,
GetComputableTypeWithDefault,
processComputable,
ProcessedComputable
} from "util/computed";
import { processComputable } from "util/computed";
import { createLazyProxy } from "util/proxies";
import { computed, unref } from "vue";
@ -70,7 +61,7 @@ export type GenericClickable = Replace<
>;
export function createClickable<T extends ClickableOptions>(
optionsFunc?: OptionsFunc<T, Clickable<T>, BaseClickable>
optionsFunc?: OptionsFunc<T, BaseClickable, GenericClickable>
): Clickable<T> {
return createLazyProxy(() => {
const clickable = optionsFunc?.() ?? ({} as ReturnType<NonNullable<typeof optionsFunc>>);
@ -136,7 +127,7 @@ export function createClickable<T extends ClickableOptions>(
}
export function setupAutoClick(
layer: GenericLayer,
layer: BaseLayer,
clickable: GenericClickable,
autoActive: Computable<boolean> = true
): Unsubscribe {

View file

@ -1,18 +1,16 @@
import { GenericLayer } from "game/layers";
import { Modifier } from "game/modifiers";
import Decimal, { DecimalSource } from "util/bignum";
import { WithRequired } from "util/common";
import {
Computable,
convertComputable,
GetComputableTypeWithDefault,
processComputable,
ProcessedComputable
} from "util/computed";
import type { OptionsFunc, Replace } from "features/feature";
import { setDefault } from "features/feature";
import type { Resource } from "features/resources/resource";
import type { BaseLayer } from "game/layers";
import type { Modifier } from "game/modifiers";
import type { DecimalSource } from "util/bignum";
import Decimal from "util/bignum";
import type { WithRequired } from "util/common";
import type { Computable, GetComputableTypeWithDefault, ProcessedComputable } from "util/computed";
import { convertComputable, processComputable } from "util/computed";
import { createLazyProxy } from "util/proxies";
import { computed, Ref, unref } from "vue";
import { OptionsFunc, Replace, setDefault } from "./feature";
import { Resource } from "./resources/resource";
import type { Ref } from "vue";
import { computed, unref } from "vue";
/**
* An object that configures a {@link conversion}.
@ -54,6 +52,7 @@ export interface ConversionOptions {
gainResource: Resource;
/**
* Whether or not to cap the amount of the output resource gained by converting at 1.
* Defaults to true.
*/
buyMax?: Computable<boolean>;
/**
@ -66,11 +65,30 @@ export interface ConversionOptions {
*/
convert?: VoidFunction;
/**
* An addition modifier that will be applied to the gain amounts.
* The function that spends the {@link baseResource} as part of the conversion.
* Defaults to setting the {@link baseResource} amount to 0.
*/
spend?: (amountGained: DecimalSource) => void;
/**
* A callback that happens after a conversion has been completed.
* Receives the amount gained via conversion.
* This will not be called whenever using currentGain without calling convert (e.g. passive generation)
*/
onConvert?: (amountGained: DecimalSource) => void;
/**
* An additional modifier that will be applied to the gain amounts.
* Must be reversible in order to correctly calculate {@link nextAt}.
* @see {@link createSequentialModifier} if you want to apply multiple modifiers.
*/
gainModifier?: WithRequired<Modifier, "revert">;
/**
* A modifier that will be applied to the cost amounts.
* That is to say, this modifier will be applied to the amount of baseResource before going into the scaling function.
* A cost modifier of x0.5 would give gain amounts equal to the player having half the baseResource they actually have.
* Must be reversible in order to correctly calculate {@link nextAt}.
* @see {@link createSequentialModifier} if you want to apply multiple modifiers.
*/
costModifier?: WithRequired<Modifier, "revert">;
}
/**
@ -94,6 +112,7 @@ export type Conversion<T extends ConversionOptions> = Replace<
currentAt: GetComputableTypeWithDefault<T["currentAt"], Ref<DecimalSource>>;
nextAt: GetComputableTypeWithDefault<T["nextAt"], Ref<DecimalSource>>;
buyMax: GetComputableTypeWithDefault<T["buyMax"], true>;
spend: undefined extends T["spend"] ? (amountGained: DecimalSource) => void : T["spend"];
roundUpCost: GetComputableTypeWithDefault<T["roundUpCost"], true>;
}
>;
@ -109,6 +128,7 @@ export type GenericConversion = Replace<
currentAt: ProcessedComputable<DecimalSource>;
nextAt: ProcessedComputable<DecimalSource>;
buyMax: ProcessedComputable<boolean>;
spend: (amountGained: DecimalSource) => void;
roundUpCost: ProcessedComputable<boolean>;
}
>;
@ -121,7 +141,7 @@ export type GenericConversion = Replace<
* @see {@link createIndependentConversion}.
*/
export function createConversion<T extends ConversionOptions>(
optionsFunc: OptionsFunc<T, Conversion<T>, BaseConversion>
optionsFunc: OptionsFunc<T, BaseConversion, GenericConversion>
): Conversion<T> {
return createLazyProxy(() => {
const conversion = optionsFunc();
@ -161,11 +181,18 @@ export function createConversion<T extends ConversionOptions>(
if (conversion.convert == null) {
conversion.convert = function () {
const amountGained = unref((conversion as GenericConversion).currentGain);
conversion.gainResource.value = Decimal.add(
conversion.gainResource.value,
unref((conversion as GenericConversion).currentGain)
amountGained
);
// TODO just subtract cost?
(conversion as GenericConversion).spend(amountGained);
conversion.onConvert?.(amountGained);
};
}
if (conversion.spend == null) {
conversion.spend = function () {
conversion.baseResource.value = 0;
};
}
@ -234,11 +261,15 @@ export function createLinearScaling(
const processedCoefficient = convertComputable(coefficient);
return {
currentGain(conversion) {
if (Decimal.lt(conversion.baseResource.value, unref(processedBase))) {
let baseAmount: DecimalSource = unref(conversion.baseResource.value);
if (conversion.costModifier) {
baseAmount = conversion.costModifier.apply(baseAmount);
}
if (Decimal.lt(baseAmount, unref(processedBase))) {
return 0;
}
return Decimal.sub(conversion.baseResource.value, unref(processedBase))
return Decimal.sub(baseAmount, unref(processedBase))
.sub(1)
.times(unref(processedCoefficient))
.add(1);
@ -248,21 +279,29 @@ export function createLinearScaling(
if (conversion.gainModifier) {
current = conversion.gainModifier.revert(current);
}
current = Decimal.max(0, current);
return Decimal.sub(current, 1)
current = Decimal.max(0, current)
.sub(1)
.div(unref(processedCoefficient))
.add(unref(processedBase));
if (conversion.costModifier) {
current = conversion.costModifier.revert(current);
}
return current;
},
nextAt(conversion) {
let next: DecimalSource = Decimal.add(unref(conversion.currentGain), 1);
let next: DecimalSource = Decimal.add(unref(conversion.currentGain), 1).floor();
if (conversion.gainModifier) {
next = conversion.gainModifier.revert(next);
}
next = Decimal.max(0, next);
return Decimal.sub(next, 1)
next = Decimal.max(0, next)
.sub(1)
.div(unref(processedCoefficient))
.add(unref(processedBase))
.max(unref(processedBase));
if (conversion.costModifier) {
next = conversion.costModifier.revert(next);
}
return next;
}
};
}
@ -288,11 +327,15 @@ export function createPolynomialScaling(
const processedExponent = convertComputable(exponent);
return {
currentGain(conversion) {
if (Decimal.lt(conversion.baseResource.value, unref(processedBase))) {
let baseAmount: DecimalSource = unref(conversion.baseResource.value);
if (conversion.costModifier) {
baseAmount = conversion.costModifier.apply(baseAmount);
}
if (Decimal.lt(baseAmount, unref(processedBase))) {
return 0;
}
const gain = Decimal.div(conversion.baseResource.value, unref(processedBase)).pow(
const gain = Decimal.div(baseAmount, unref(processedBase)).pow(
unref(processedExponent)
);
@ -306,18 +349,27 @@ export function createPolynomialScaling(
if (conversion.gainModifier) {
current = conversion.gainModifier.revert(current);
}
current = Decimal.max(0, current);
return Decimal.root(current, unref(processedExponent)).times(unref(processedBase));
current = Decimal.max(0, current)
.root(unref(processedExponent))
.times(unref(processedBase));
if (conversion.costModifier) {
current = conversion.costModifier.revert(current);
}
return current;
},
nextAt(conversion) {
let next: DecimalSource = Decimal.add(unref(conversion.currentGain), 1);
let next: DecimalSource = Decimal.add(unref(conversion.currentGain), 1).floor();
if (conversion.gainModifier) {
next = conversion.gainModifier.revert(next);
}
next = Decimal.max(0, next);
return Decimal.root(next, unref(processedExponent))
next = Decimal.max(0, next)
.root(unref(processedExponent))
.times(unref(processedBase))
.max(unref(processedBase));
if (conversion.costModifier) {
next = conversion.costModifier.revert(next);
}
return next;
}
};
}
@ -329,7 +381,7 @@ export function createPolynomialScaling(
* @param optionsFunc Conversion options.
*/
export function createCumulativeConversion<S extends ConversionOptions>(
optionsFunc: OptionsFunc<S, Conversion<S>>
optionsFunc: OptionsFunc<S, BaseConversion, GenericConversion>
): Conversion<S> {
return createConversion(optionsFunc);
}
@ -340,7 +392,7 @@ export function createCumulativeConversion<S extends ConversionOptions>(
* @param optionsFunc Converison options.
*/
export function createIndependentConversion<S extends ConversionOptions>(
optionsFunc: OptionsFunc<S, Conversion<S>>
optionsFunc: OptionsFunc<S, BaseConversion, GenericConversion>
): Conversion<S> {
return createConversion(() => {
const conversion: S = optionsFunc();
@ -365,7 +417,7 @@ export function createIndependentConversion<S extends ConversionOptions>(
if (conversion.actualGain == null) {
conversion.actualGain = computed(() => {
let gain = Decimal.sub(
conversion.scaling.currentGain(conversion as GenericConversion),
Decimal.floor(conversion.scaling.currentGain(conversion as GenericConversion)),
conversion.gainResource.value
).max(0);
@ -376,42 +428,44 @@ export function createIndependentConversion<S extends ConversionOptions>(
});
}
setDefault(conversion, "convert", function () {
const amountGained = unref((conversion as GenericConversion).actualGain);
conversion.gainResource.value = conversion.gainModifier
? conversion.gainModifier.apply(
unref((conversion as GenericConversion).currentGain)
)
: unref((conversion as GenericConversion).currentGain);
// TODO just subtract cost?
// Maybe by adding a cost function to scaling and nextAt just calls the cost function
// with 1 + currentGain
conversion.baseResource.value = 0;
(conversion as GenericConversion).spend(amountGained);
conversion.onConvert?.(amountGained);
});
return conversion;
});
}) as Conversion<S>;
}
/**
* This will automatically increase the value of conversion.gainResource without lowering the value of the input resource.
* It will by default perform 100% of a conversion's currentGain per second.
* If you use a ref for the rate you can set it's value to 0 when passive generation should be disabled.
* @param layer The layer this passive generation will be associated with.
* @param layer The layer this passive generation will be associated with. Typically `this` when calling this function from inside a layer's options function.
* @param conversion The conversion that will determine how much generation there is.
* @param rate A multiplier to multiply against the conversion's currentGain.
* @param cap A value that should not be passed via passive generation. If null, no cap is applied.
*/
export function setupPassiveGeneration(
layer: GenericLayer,
layer: BaseLayer,
conversion: GenericConversion,
rate: Computable<DecimalSource> = 1
rate: Computable<DecimalSource> = 1,
cap: Computable<DecimalSource | null> = null
): void {
const processedRate = convertComputable(rate);
const processedCap = convertComputable(cap);
layer.on("preUpdate", diff => {
const currRate = unref(processedRate);
if (Decimal.neq(currRate, 0)) {
conversion.gainResource.value = Decimal.add(
conversion.gainResource.value,
Decimal.times(currRate, diff).times(unref(conversion.currentGain))
);
Decimal.times(currRate, diff).times(Decimal.ceil(unref(conversion.actualGain)))
).min(unref(processedCap) ?? Decimal.dInf);
}
});
}

View file

@ -1,6 +1,7 @@
import Decimal from "util/bignum";
import { DoNotCache } from "util/computed";
import { CSSProperties, DefineComponent, isRef } from "vue";
import type { CSSProperties, DefineComponent } from "vue";
import { isRef } from "vue";
/**
* A symbol to use as a key for a vue component a feature can be rendered with
@ -41,7 +42,7 @@ export type Replace<T, S> = S & Omit<T, keyof S>;
* with "this" bound to what the type will eventually be processed into.
* Intended for making lazily evaluated objects.
*/
export type OptionsFunc<T, S = T, R = Record<string, unknown>> = () => T & ThisType<S> & Partial<R>;
export type OptionsFunc<T, R = Record<string, unknown>, S = R> = () => T & Partial<R> & ThisType<S>;
let id = 0;
/**

View file

@ -20,7 +20,7 @@
import "components/common/table.css";
import themes from "data/themes";
import { Visibility } from "features/feature";
import { GridCell } from "features/grids/grid";
import type { GridCell } from "features/grids/grid";
import settings from "game/settings";
import { processedPropType } from "util/vue";
import { computed, defineComponent, unref } from "vue";

View file

@ -25,14 +25,16 @@
<script lang="ts">
import "components/common/features.css";
import Node from "components/Node.vue";
import { CoercableComponent, StyleValue, Visibility } from "features/feature";
import type { CoercableComponent, StyleValue } from "features/feature";
import { Visibility } from "features/feature";
import {
computeComponent,
computeOptionalComponent,
processedPropType,
setupHoldToClick
} from "util/vue";
import { defineComponent, PropType, toRefs, unref } from "vue";
import type { PropType } from "vue";
import { defineComponent, toRefs, unref } from "vue";
export default defineComponent({
props: {

View file

@ -1,26 +1,19 @@
import type { CoercableComponent, OptionsFunc, Replace, StyleValue } from "features/feature";
import { Component, GatherProps, getUniqueID, setDefault, Visibility } from "features/feature";
import GridComponent from "features/grids/Grid.vue";
import {
CoercableComponent,
Component,
OptionsFunc,
GatherProps,
getUniqueID,
Replace,
setDefault,
StyleValue,
Visibility
} from "features/feature";
import type { Persistent, State } from "game/persistence";
import { persistent } from "game/persistence";
import { isFunction } from "util/common";
import {
import type {
Computable,
GetComputableType,
GetComputableTypeWithDefault,
processComputable,
ProcessedComputable
} from "util/computed";
import { processComputable } from "util/computed";
import { createLazyProxy } from "util/proxies";
import { computed, Ref, unref } from "vue";
import { State, Persistent, persistent } from "game/persistence";
import type { Ref } from "vue";
import { computed, unref } from "vue";
export const GridType = Symbol("Grid");
@ -243,7 +236,7 @@ export type GenericGrid = Replace<
>;
export function createGrid<T extends GridOptions>(
optionsFunc: OptionsFunc<T, Grid<T>, BaseGrid>
optionsFunc: OptionsFunc<T, BaseGrid, GenericGrid>
): Grid<T> {
const cellState = persistent<Record<string | number, State>>({});
return createLazyProxy(() => {

View file

@ -1,17 +1,18 @@
import { hasWon } from "data/projEntry";
import type { OptionsFunc, Replace } from "features/feature";
import { findFeatures, jsx, setDefault } from "features/feature";
import { globalBus } from "game/events";
import player from "game/player";
import { registerInfoComponent } from "game/settings";
import {
import type {
Computable,
GetComputableTypeWithDefault,
GetComputableType,
ProcessedComputable,
processComputable
GetComputableTypeWithDefault,
ProcessedComputable
} from "util/computed";
import { processComputable } from "util/computed";
import { createLazyProxy } from "util/proxies";
import { shallowReactive, unref } from "vue";
import { OptionsFunc, findFeatures, jsx, Replace, setDefault } from "./feature";
export const hotkeys: Record<string, GenericHotkey | undefined> = shallowReactive({});
export const HotkeyType = Symbol("Hotkey");
@ -43,7 +44,7 @@ export type GenericHotkey = Replace<
>;
export function createHotkey<T extends HotkeyOptions>(
optionsFunc: OptionsFunc<T, Hotkey<T>, BaseHotkey>
optionsFunc: OptionsFunc<T, BaseHotkey, GenericHotkey>
): Hotkey<T> {
return createLazyProxy(() => {
const hotkey = optionsFunc();

View file

@ -29,13 +29,15 @@
</template>
<script lang="ts">
import CollapseTransition from "@ivanv/vue-collapse-transition/src/CollapseTransition.vue";
import Node from "components/Node.vue";
import themes from "data/themes";
import { CoercableComponent, Visibility } from "features/feature";
import type { CoercableComponent } from "features/feature";
import { Visibility } from "features/feature";
import settings from "game/settings";
import { computeComponent, processedPropType } from "util/vue";
import CollapseTransition from "@ivanv/vue-collapse-transition/src/CollapseTransition.vue";
import { computed, defineComponent, PropType, Ref, StyleValue, toRefs, unref } from "vue";
import type { PropType, Ref, StyleValue } from "vue";
import { computed, defineComponent, toRefs, unref } from "vue";
export default defineComponent({
props: {

View file

@ -1,23 +1,15 @@
import {
CoercableComponent,
Component,
GatherProps,
getUniqueID,
OptionsFunc,
Replace,
setDefault,
StyleValue,
Visibility
} from "features/feature";
import type { CoercableComponent, OptionsFunc, Replace, StyleValue } from "features/feature";
import { Component, GatherProps, getUniqueID, setDefault, Visibility } from "features/feature";
import InfoboxComponent from "features/infoboxes/Infobox.vue";
import { Persistent, persistent } from "game/persistence";
import {
import type { Persistent } from "game/persistence";
import { persistent } from "game/persistence";
import type {
Computable,
GetComputableType,
GetComputableTypeWithDefault,
processComputable,
ProcessedComputable
} from "util/computed";
import { processComputable } from "util/computed";
import { createLazyProxy } from "util/proxies";
import { unref } from "vue";
@ -64,7 +56,7 @@ export type GenericInfobox = Replace<
>;
export function createInfobox<T extends InfoboxOptions>(
optionsFunc: OptionsFunc<T, Infobox<T>, BaseInfobox>
optionsFunc: OptionsFunc<T, BaseInfobox, GenericInfobox>
): Infobox<T> {
const collapsed = persistent<boolean>(false);
return createLazyProxy(() => {

View file

@ -11,8 +11,8 @@
</template>
<script setup lang="ts">
import { Link } from "features/links/links";
import { FeatureNode } from "game/layers";
import type { Link } from "features/links/links";
import type { FeatureNode } from "game/layers";
import { computed, toRefs } from "vue";
const _props = defineProps<{

View file

@ -13,7 +13,7 @@
</template>
<script setup lang="ts">
import { Link } from "features/links/links";
import type { Link } from "features/links/links";
import { BoundsInjectionKey, NodesInjectionKey } from "game/layers";
import { computed, inject, ref, toRef, watch } from "vue";
import LinkVue from "./Link.vue";

View file

@ -1,14 +1,11 @@
import LinksComponent from "./Links.vue";
import { Component, OptionsFunc, GatherProps, Replace } from "features/feature";
import { Position } from "game/layers";
import {
Computable,
GetComputableType,
processComputable,
ProcessedComputable
} from "util/computed";
import type { OptionsFunc, Replace } from "features/feature";
import { GatherProps, Component } from "features/feature";
import type { Position } from "game/layers";
import type { Computable, GetComputableType, ProcessedComputable } from "util/computed";
import { processComputable } from "util/computed";
import { createLazyProxy } from "util/proxies";
import { SVGAttributes } from "vue";
import type { SVGAttributes } from "vue";
import LinksComponent from "./Links.vue";
export const LinksType = Symbol("Links");
@ -44,7 +41,7 @@ export type GenericLinks = Replace<
>;
export function createLinks<T extends LinksOptions>(
optionsFunc: OptionsFunc<T, Links<T>, BaseLinks>
optionsFunc: OptionsFunc<T, BaseLinks, GenericLinks>
): Links<T> {
return createLazyProxy(() => {
const links = optionsFunc();

View file

@ -16,11 +16,13 @@
<script lang="tsx">
import "components/common/features.css";
import { jsx, StyleValue, Visibility } from "features/feature";
import { GenericMilestone } from "features/milestones/milestone";
import Node from "components/Node.vue";
import type { StyleValue } from "features/feature";
import { jsx, Visibility } from "features/feature";
import type { GenericMilestone } from "features/milestones/milestone";
import { coerceComponent, isCoercableComponent, processedPropType, unwrapRef } from "util/vue";
import { Component, defineComponent, shallowRef, toRefs, unref, UnwrapRef, watchEffect } from "vue";
import Node from "../../components/Node.vue";
import type { Component, UnwrapRef } from "vue";
import { defineComponent, shallowRef, toRefs, unref, watchEffect } from "vue";
export default defineComponent({
props: {

View file

@ -1,29 +1,20 @@
import Select from "components/fields/Select.vue";
import {
CoercableComponent,
Component,
OptionsFunc,
GatherProps,
getUniqueID,
jsx,
Replace,
setDefault,
StyleValue,
Visibility
} from "features/feature";
import type { CoercableComponent, OptionsFunc, Replace, StyleValue } from "features/feature";
import { Component, GatherProps, getUniqueID, jsx, setDefault, Visibility } from "features/feature";
import MilestoneComponent from "features/milestones/Milestone.vue";
import { globalBus } from "game/events";
import "game/notifications";
import { persistent, Persistent } from "game/persistence";
import type { Persistent } from "game/persistence";
import { persistent } from "game/persistence";
import settings, { registerSettingField } from "game/settings";
import { camelToTitle } from "util/common";
import {
import type {
Computable,
GetComputableType,
GetComputableTypeWithDefault,
processComputable,
ProcessedComputable
} from "util/computed";
import { processComputable } from "util/computed";
import { createLazyProxy } from "util/proxies";
import { coerceComponent, isCoercableComponent } from "util/vue";
import { computed, unref, watchEffect } from "vue";
@ -84,7 +75,7 @@ export type GenericMilestone = Replace<
>;
export function createMilestone<T extends MilestoneOptions>(
optionsFunc?: OptionsFunc<T, Milestone<T>, BaseMilestone>
optionsFunc?: OptionsFunc<T, BaseMilestone, GenericMilestone>
): Milestone<T> {
const earned = persistent<boolean>(false);
return createLazyProxy(() => {

View file

@ -8,10 +8,11 @@
</template>
<script lang="tsx">
import { StyleValue } from "features/feature";
import type { StyleValue } from "features/feature";
import { Application } from "pixi.js";
import { processedPropType } from "util/vue";
import { defineComponent, nextTick, onBeforeUnmount, onMounted, PropType, ref, unref } from "vue";
import type { PropType } from "vue";
import { defineComponent, nextTick, onBeforeUnmount, onMounted, ref, unref } from "vue";
// TODO get typing support on the Particles component
export default defineComponent({

View file

@ -1,17 +1,12 @@
import type { EmitterConfigV3 } from "@pixi/particle-emitter";
import { Emitter, upgradeConfig } from "@pixi/particle-emitter";
import type { OptionsFunc, Replace, StyleValue } from "features/feature";
import { Component, GatherProps, getUniqueID } from "features/feature";
import ParticlesComponent from "features/particles/Particles.vue";
import { Ref, shallowRef, unref } from "vue";
import {
Component,
OptionsFunc,
GatherProps,
getUniqueID,
Replace,
StyleValue
} from "features/feature";
import { createLazyProxy } from "util/proxies";
import { Application } from "pixi.js";
import { Emitter, EmitterConfigV3, upgradeConfig } from "@pixi/particle-emitter";
import { Computable, GetComputableType } from "util/computed";
import type { Computable, GetComputableType } from "util/computed";
import { createLazyProxy } from "util/proxies";
import { Ref, shallowRef, unref } from "vue";
export const ParticlesType = Symbol("Particles");
@ -42,7 +37,7 @@ export type Particles<T extends ParticlesOptions> = Replace<
export type GenericParticles = Particles<ParticlesOptions>;
export function createParticles<T extends ParticlesOptions>(
optionsFunc?: OptionsFunc<T, Particles<T>, BaseParticles>
optionsFunc?: OptionsFunc<T, BaseParticles, GenericParticles>
): Particles<T> {
return createLazyProxy(() => {
const particles = optionsFunc?.() ?? ({} as ReturnType<NonNullable<typeof optionsFunc>>);

View file

@ -1,11 +1,14 @@
import { OptionsFunc, getUniqueID, Replace } from "features/feature";
import type { OptionsFunc, Replace } from "features/feature";
import { getUniqueID } from "features/feature";
import { globalBus } from "game/events";
import { GenericLayer } from "game/layers";
import { DefaultValue, Persistent, persistent, PersistentState } from "game/persistence";
import type { BaseLayer } from "game/layers";
import type { Persistent } from "game/persistence";
import { DefaultValue, persistent, PersistentState } from "game/persistence";
import type { Unsubscribe } from "nanoevents";
import Decimal from "util/bignum";
import { Computable, GetComputableType, processComputable } from "util/computed";
import type { Computable, GetComputableType } from "util/computed";
import { processComputable } from "util/computed";
import { createLazyProxy } from "util/proxies";
import { Unsubscribe } from "nanoevents";
import { isRef, unref } from "vue";
export const ResetType = Symbol("Reset");
@ -31,7 +34,7 @@ export type Reset<T extends ResetOptions> = Replace<
export type GenericReset = Reset<ResetOptions>;
export function createReset<T extends ResetOptions>(
optionsFunc: OptionsFunc<T, Reset<T>, BaseReset>
optionsFunc: OptionsFunc<T, BaseReset, GenericReset>
): Reset<T> {
return createLazyProxy(() => {
const reset = optionsFunc();
@ -64,7 +67,7 @@ export function createReset<T extends ResetOptions>(
}
const listeners: Record<string, Unsubscribe | undefined> = {};
export function trackResetTime(layer: GenericLayer, reset: GenericReset): Persistent<Decimal> {
export function trackResetTime(layer: BaseLayer, reset: GenericReset): Persistent<Decimal> {
const resetTime = persistent<Decimal>(new Decimal(0));
listeners[layer.id] = layer.on("preUpdate", diff => {
resetTime.value = Decimal.add(resetTime.value, diff);

View file

@ -10,12 +10,13 @@
</template>
<script setup lang="ts">
import { CoercableComponent } from "features/feature";
import { Resource } from "features/resources/resource";
import type { CoercableComponent } from "features/feature";
import type { Resource } from "features/resources/resource";
import ResourceVue from "features/resources/Resource.vue";
import Decimal from "util/bignum";
import { computeOptionalComponent } from "util/vue";
import { computed, Ref, StyleValue, toRefs } from "vue";
import ResourceVue from "features/resources/Resource.vue";
import type { Ref, StyleValue } from "vue";
import { computed, toRefs } from "vue";
const _props = defineProps<{
resource: Resource;
@ -34,5 +35,3 @@ const showPrefix = computed(() => {
return Decimal.lt(props.resource.value, "1e1000");
});
</script>
<style scoped></style>

View file

@ -5,7 +5,8 @@
</template>
<script setup lang="ts">
import { displayResource, Resource } from "features/resources/resource";
import type { Resource } from "features/resources/resource";
import { displayResource } from "features/resources/resource";
import { computed } from "vue";
const props = defineProps<{

View file

@ -1,7 +1,10 @@
import Decimal, { DecimalSource, format, formatWhole } from "util/bignum";
import { computed, ComputedRef, isRef, ref, Ref, watch } from "vue";
import { globalBus } from "game/events";
import { State, persistent } from "game/persistence";
import type { State } from "game/persistence";
import { persistent } from "game/persistence";
import type { DecimalSource } from "util/bignum";
import Decimal, { format, formatWhole } from "util/bignum";
import type { ComputedRef, Ref } from "vue";
import { computed, isRef, ref, watch } from "vue";
export interface Resource<T = DecimalSource> extends Ref<T> {
displayName: string;

View file

@ -3,7 +3,7 @@
</template>
<script setup lang="ts">
import { CoercableComponent } from "features/feature";
import type { CoercableComponent } from "features/feature";
import { computeComponent } from "util/vue";
import { toRefs } from "vue";

View file

@ -20,7 +20,8 @@
</template>
<script lang="ts">
import { CoercableComponent, StyleValue, Visibility } from "features/feature";
import type { CoercableComponent, StyleValue } from "features/feature";
import { Visibility } from "features/feature";
import { getNotifyStyle } from "game/notifications";
import { computeComponent, processedPropType, unwrapRef } from "util/vue";
import { computed, defineComponent, toRefs, unref } from "vue";

View file

@ -32,23 +32,15 @@
<script lang="ts">
import Sticky from "components/layout/Sticky.vue";
import themes from "data/themes";
import { CoercableComponent, StyleValue, Visibility } from "features/feature";
import { GenericTab } from "features/tabs/tab";
import type { CoercableComponent, StyleValue } from "features/feature";
import { Visibility } from "features/feature";
import type { GenericTab } from "features/tabs/tab";
import TabButton from "features/tabs/TabButton.vue";
import { GenericTabButton } from "features/tabs/tabFamily";
import type { GenericTabButton } from "features/tabs/tabFamily";
import settings from "game/settings";
import { coerceComponent, isCoercableComponent, processedPropType, unwrapRef } from "util/vue";
import {
Component,
computed,
defineComponent,
PropType,
Ref,
shallowRef,
toRefs,
unref,
watchEffect
} from "vue";
import type { Component, PropType, Ref } from "vue";
import { computed, defineComponent, shallowRef, toRefs, unref, watchEffect } from "vue";
export default defineComponent({
props: {

View file

@ -1,14 +1,7 @@
import {
CoercableComponent,
Component,
OptionsFunc,
GatherProps,
getUniqueID,
Replace,
StyleValue
} from "features/feature";
import type { CoercableComponent, OptionsFunc, Replace, StyleValue } from "features/feature";
import { Component, GatherProps, getUniqueID } from "features/feature";
import TabComponent from "features/tabs/Tab.vue";
import { Computable, GetComputableType } from "util/computed";
import type { Computable, GetComputableType } from "util/computed";
import { createLazyProxy } from "util/proxies";
export const TabType = Symbol("Tab");
@ -38,7 +31,7 @@ export type Tab<T extends TabOptions> = Replace<
export type GenericTab = Tab<TabOptions>;
export function createTab<T extends TabOptions>(
optionsFunc: OptionsFunc<T, Tab<T>, BaseTab>
optionsFunc: OptionsFunc<T, BaseTab, GenericTab>
): Tab<T> {
return createLazyProxy(() => {
const tab = optionsFunc();

View file

@ -1,27 +1,20 @@
import {
CoercableComponent,
Component,
OptionsFunc,
GatherProps,
getUniqueID,
Replace,
setDefault,
StyleValue,
Visibility
} from "features/feature";
import type { CoercableComponent, OptionsFunc, Replace, StyleValue } from "features/feature";
import { Component, GatherProps, getUniqueID, setDefault, Visibility } from "features/feature";
import TabButtonComponent from "features/tabs/TabButton.vue";
import TabFamilyComponent from "features/tabs/TabFamily.vue";
import { Persistent, persistent } from "game/persistence";
import {
import type { Persistent } from "game/persistence";
import { persistent } from "game/persistence";
import type {
Computable,
GetComputableType,
GetComputableTypeWithDefault,
processComputable,
ProcessedComputable
} from "util/computed";
import { processComputable } from "util/computed";
import { createLazyProxy } from "util/proxies";
import { computed, Ref, unref } from "vue";
import { GenericTab } from "./tab";
import type { Ref } from "vue";
import { computed, unref } from "vue";
import type { GenericTab } from "./tab";
export const TabButtonType = Symbol("TabButton");
export const TabFamilyType = Symbol("TabFamily");
@ -92,7 +85,7 @@ export type GenericTabFamily = Replace<
export function createTabFamily<T extends TabFamilyOptions>(
tabs: Record<string, () => TabButtonOptions>,
optionsFunc?: OptionsFunc<T, TabFamily<T>, BaseTabFamily>
optionsFunc?: OptionsFunc<T, BaseTabFamily, GenericTabFamily>
): TabFamily<T> {
if (Object.keys(tabs).length === 0) {
console.warn("Cannot create tab family with 0 tabs");

View file

@ -36,29 +36,21 @@
<script lang="ts">
import themes from "data/themes";
import { CoercableComponent, jsx, StyleValue } from "features/feature";
import { Persistent } from "game/persistence";
import type { CoercableComponent, StyleValue } from "features/feature";
import { jsx } from "features/feature";
import type { Persistent } from "game/persistence";
import settings from "game/settings";
import { Direction } from "util/common";
import type { VueFeature } from "util/vue";
import {
coerceComponent,
computeOptionalComponent,
processedPropType,
render,
unwrapRef,
VueFeature
unwrapRef
} from "util/vue";
import {
Component,
computed,
defineComponent,
PropType,
ref,
shallowRef,
toRefs,
unref,
watchEffect
} from "vue";
import type { Component, PropType } from "vue";
import { computed, defineComponent, ref, shallowRef, toRefs, unref, watchEffect } from "vue";
export default defineComponent({
props: {

View file

@ -1,23 +1,18 @@
import TooltipComponent from "./Tooltip.vue";
import {
CoercableComponent,
Component,
GatherProps,
Replace,
setDefault,
StyleValue
} from "features/feature";
import {
import type { CoercableComponent, Replace, StyleValue } from "features/feature";
import { Component, GatherProps, setDefault } from "features/feature";
import { persistent } from "game/persistence";
import { Direction } from "util/common";
import type {
Computable,
GetComputableType,
GetComputableTypeWithDefault,
processComputable,
ProcessedComputable
} from "util/computed";
import { VueFeature } from "util/vue";
import { nextTick, Ref, unref } from "vue";
import { persistent } from "game/persistence";
import { Direction } from "util/common";
import { processComputable } from "util/computed";
import type { VueFeature } from "util/vue";
import type { Ref } from "vue";
import { nextTick, unref } from "vue";
import TooltipComponent from "./Tooltip.vue";
declare module "@vue/runtime-dom" {
interface CSSProperties {

View file

@ -7,11 +7,12 @@
<script lang="tsx">
import "components/common/table.css";
import { GenericTreeNode, TreeBranch } from "features/trees/tree";
import { coerceComponent, processedPropType, renderJSX, unwrapRef } from "util/vue";
import { Component, defineComponent, shallowRef, toRefs, unref, watchEffect } from "vue";
import Links from "features/links/Links.vue";
import { jsx } from "features/feature";
import Links from "features/links/Links.vue";
import type { GenericTreeNode, TreeBranch } from "features/trees/tree";
import { coerceComponent, processedPropType, renderJSX, unwrapRef } from "util/vue";
import type { Component } from "vue";
import { defineComponent, shallowRef, toRefs, unref, watchEffect } from "vue";
export default defineComponent({
props: {

View file

@ -34,16 +34,18 @@
</template>
<script lang="ts">
import Node from "components/Node.vue";
import MarkNode from "components/MarkNode.vue";
import { CoercableComponent, StyleValue, Visibility } from "features/feature";
import Node from "components/Node.vue";
import type { CoercableComponent, StyleValue } from "features/feature";
import { Visibility } from "features/feature";
import {
computeOptionalComponent,
isCoercableComponent,
processedPropType,
setupHoldToClick
} from "util/vue";
import { defineComponent, PropType, toRefs, unref } from "vue";
import type { PropType } from "vue";
import { defineComponent, toRefs, unref } from "vue";
export default defineComponent({
props: {

View file

@ -1,30 +1,23 @@
import {
CoercableComponent,
Component,
OptionsFunc,
GatherProps,
getUniqueID,
Replace,
setDefault,
StyleValue,
Visibility
} from "features/feature";
import { Link } from "features/links/links";
import { GenericReset } from "features/reset";
import { displayResource, Resource } from "features/resources/resource";
import type { CoercableComponent, OptionsFunc, Replace, StyleValue } from "features/feature";
import { Component, GatherProps, getUniqueID, setDefault, Visibility } from "features/feature";
import type { Link } from "features/links/links";
import type { GenericReset } from "features/reset";
import type { Resource } from "features/resources/resource";
import { displayResource } from "features/resources/resource";
import TreeComponent from "features/trees/Tree.vue";
import TreeNodeComponent from "features/trees/TreeNode.vue";
import Decimal, { DecimalSource, format, formatWhole } from "util/bignum";
import {
import type { DecimalSource } from "util/bignum";
import Decimal, { format, formatWhole } from "util/bignum";
import type {
Computable,
convertComputable,
GetComputableType,
GetComputableTypeWithDefault,
processComputable,
ProcessedComputable
} from "util/computed";
import { convertComputable, processComputable } from "util/computed";
import { createLazyProxy } from "util/proxies";
import { computed, ref, Ref, shallowRef, unref } from "vue";
import type { Ref } from "vue";
import { computed, ref, shallowRef, unref } from "vue";
export const TreeNodeType = Symbol("TreeNode");
export const TreeType = Symbol("Tree");
@ -73,7 +66,7 @@ export type GenericTreeNode = Replace<
>;
export function createTreeNode<T extends TreeNodeOptions>(
optionsFunc?: OptionsFunc<T, TreeNode<T>, BaseTreeNode>
optionsFunc?: OptionsFunc<T, BaseTreeNode, GenericTreeNode>
): TreeNode<T> {
return createLazyProxy(() => {
const treeNode = optionsFunc?.() ?? ({} as ReturnType<NonNullable<typeof optionsFunc>>);
@ -187,7 +180,7 @@ export type GenericTree = Replace<
>;
export function createTree<T extends TreeOptions>(
optionsFunc: OptionsFunc<T, Tree<T>, BaseTree>
optionsFunc: OptionsFunc<T, BaseTree, GenericTree>
): Tree<T> {
return createLazyProxy(() => {
const tree = optionsFunc();

View file

@ -26,23 +26,17 @@
<script lang="tsx">
import "components/common/features.css";
import Node from "components/Node.vue";
import MarkNode from "components/MarkNode.vue";
import { jsx, StyleValue, Visibility } from "features/feature";
import { displayResource, Resource } from "features/resources/resource";
import { GenericUpgrade } from "features/upgrades/upgrade";
import { DecimalSource } from "util/bignum";
import Node from "components/Node.vue";
import type { StyleValue } from "features/feature";
import { jsx, Visibility } from "features/feature";
import type { Resource } from "features/resources/resource";
import { displayResource } from "features/resources/resource";
import type { GenericUpgrade } from "features/upgrades/upgrade";
import type { DecimalSource } from "util/bignum";
import { coerceComponent, isCoercableComponent, processedPropType, unwrapRef } from "util/vue";
import {
Component,
defineComponent,
PropType,
shallowRef,
toRefs,
unref,
UnwrapRef,
watchEffect
} from "vue";
import type { Component, PropType, UnwrapRef } from "vue";
import { defineComponent, shallowRef, toRefs, unref, watchEffect } from "vue";
export default defineComponent({
props: {

View file

@ -1,30 +1,30 @@
import UpgradeComponent from "features/upgrades/Upgrade.vue";
import type { CoercableComponent, OptionsFunc, Replace, StyleValue } from "features/feature";
import {
CoercableComponent,
Component,
OptionsFunc,
findFeatures,
GatherProps,
getUniqueID,
Replace,
setDefault,
StyleValue,
Visibility
} from "features/feature";
import { Resource } from "features/resources/resource";
import { GenericLayer } from "game/layers";
import Decimal, { DecimalSource } from "util/bignum";
import type { Resource } from "features/resources/resource";
import UpgradeComponent from "features/upgrades/Upgrade.vue";
import type { GenericLayer } from "game/layers";
import type { Persistent } from "game/persistence";
import { persistent } from "game/persistence";
import type { DecimalSource } from "util/bignum";
import Decimal from "util/bignum";
import { isFunction } from "util/common";
import {
import type {
Computable,
GetComputableType,
GetComputableTypeWithDefault,
processComputable,
ProcessedComputable
} from "util/computed";
import { processComputable } from "util/computed";
import { createLazyProxy } from "util/proxies";
import { computed, Ref, unref } from "vue";
import { persistent, Persistent } from "game/persistence";
import type { Ref } from "vue";
import { computed, unref } from "vue";
export const UpgradeType = Symbol("Upgrade");
@ -79,7 +79,7 @@ export type GenericUpgrade = Replace<
>;
export function createUpgrade<T extends UpgradeOptions>(
optionsFunc: OptionsFunc<T, Upgrade<T>, BaseUpgrade>
optionsFunc: OptionsFunc<T, BaseUpgrade, GenericUpgrade>
): Upgrade<T> {
const bought = persistent<boolean>(false);
return createLazyProxy(() => {

View file

@ -1,11 +1,13 @@
import projInfo from "data/projInfo.json";
import Decimal from "util/bignum";
import player from "game/player";
import type { Settings } from "game/settings";
import settings from "game/settings";
import state from "game/state";
import { createNanoEvents } from "nanoevents";
import { App, Ref, watch } from "vue";
import { GenericLayer } from "./layers";
import player from "./player";
import settings, { Settings } from "./settings";
import state from "./state";
import Decimal from "util/bignum";
import type { App, Ref } from "vue";
import { watch } from "vue";
import type { GenericLayer } from "./layers";
export interface GlobalEvents {
addLayer: (layer: GenericLayer, saveData: Record<string, unknown>) => void;
@ -18,7 +20,7 @@ export interface GlobalEvents {
export const globalBus = createNanoEvents<GlobalEvents>();
let intervalID: number | null = null;
let intervalID: NodeJS.Timer | null = null;
// Not imported immediately due to dependency cycles
// This gets set during startGameLoop(), and will only be used in the update function

View file

@ -1,26 +1,28 @@
import Modal from "components/Modal.vue";
import {
import type {
CoercableComponent,
OptionsFunc,
jsx,
JSXFunction,
OptionsFunc,
Replace,
setDefault,
StyleValue
} from "features/feature";
import {
import { jsx, setDefault } from "features/feature";
import { globalBus } from "game/events";
import type { Persistent } from "game/persistence";
import { persistent } from "game/persistence";
import player from "game/player";
import type { Emitter } from "nanoevents";
import { createNanoEvents } from "nanoevents";
import type {
Computable,
GetComputableType,
GetComputableTypeWithDefault,
processComputable,
ProcessedComputable
} from "util/computed";
import { processComputable } from "util/computed";
import { createLazyProxy } from "util/proxies";
import { createNanoEvents, Emitter } from "nanoevents";
import { InjectionKey, Ref, ref, shallowReactive, unref } from "vue";
import { globalBus } from "./events";
import { Persistent, persistent } from "./persistence";
import player from "./player";
import type { InjectionKey, Ref } from "vue";
import { ref, shallowReactive, unref } from "vue";
export interface FeatureNode {
rect: DOMRect;
@ -106,7 +108,7 @@ export const persistentRefs: Record<string, Set<Persistent>> = {};
export const addingLayers: string[] = [];
export function createLayer<T extends LayerOptions>(
id: string,
optionsFunc: OptionsFunc<T, BaseLayer, BaseLayer>
optionsFunc: OptionsFunc<T, BaseLayer>
): Layer<T> {
return createLazyProxy(() => {
const layer = {} as T & Partial<BaseLayer>;
@ -119,7 +121,7 @@ export function createLayer<T extends LayerOptions>(
addingLayers.push(id);
persistentRefs[id] = new Set();
layer.minimized = persistent(false);
Object.assign(layer, optionsFunc.call(layer));
Object.assign(layer, optionsFunc.call(layer as BaseLayer));
if (
addingLayers[addingLayers.length - 1] == null ||
addingLayers[addingLayers.length - 1] !== id

View file

@ -1,8 +1,11 @@
import "components/common/modifiers.css";
import { CoercableComponent, jsx } from "features/feature";
import Decimal, { DecimalSource, format } from "util/bignum";
import { WithRequired } from "util/common";
import { Computable, convertComputable, ProcessedComputable } from "util/computed";
import type { CoercableComponent } from "features/feature";
import { jsx } from "features/feature";
import type { DecimalSource } from "util/bignum";
import Decimal, { format } from "util/bignum";
import type { WithRequired } from "util/common";
import type { Computable, ProcessedComputable } from "util/computed";
import { convertComputable } from "util/computed";
import { renderJSX } from "util/vue";
import { computed, unref } from "vue";

View file

@ -1,9 +1,12 @@
import { globalBus } from "game/events";
import Decimal, { DecimalSource } from "util/bignum";
import { ProxyState } from "util/proxies";
import { isArray } from "@vue/shared";
import { isReactive, isRef, Ref, ref } from "vue";
import { addingLayers, GenericLayer, persistentRefs } from "./layers";
import { globalBus } from "game/events";
import type { GenericLayer } from "game/layers";
import { addingLayers, persistentRefs } from "game/layers";
import type { DecimalSource } from "util/bignum";
import Decimal from "util/bignum";
import { ProxyState } from "util/proxies";
import type { Ref } from "vue";
import { isReactive, isRef, ref } from "vue";
export const PersistentState = Symbol("PersistentState");
export const DefaultValue = Symbol("DefaultValue");

View file

@ -1,6 +1,7 @@
import { isPlainObject } from "is-plain-object";
import Decimal from "util/bignum";
import { isPlainObject } from "util/common";
import { ProxiedWithState, ProxyPath, ProxyState } from "util/proxies";
import type { ProxiedWithState } from "util/proxies";
import { ProxyPath, ProxyState } from "util/proxies";
import { reactive, unref } from "vue";
import transientState from "./state";
@ -51,11 +52,7 @@ const playerHandler: ProxyHandler<Record<PropertyKey, any>> = {
}
const value = target[ProxyState][key];
if (
key !== "value" &&
(isPlainObject(value) || Array.isArray(value)) &&
!(value instanceof Decimal)
) {
if (key !== "value" && (isPlainObject(value) || Array.isArray(value))) {
if (value !== target[key]?.[ProxyState]) {
const path = [...target[ProxyPath], key];
target[key] = new Proxy({ [ProxyState]: value, [ProxyPath]: path }, playerHandler);

View file

@ -1,6 +1,6 @@
import projInfo from "data/projInfo.json";
import { Themes } from "data/themes";
import { CoercableComponent } from "features/feature";
import type { CoercableComponent } from "features/feature";
import { globalBus } from "game/events";
import LZString from "lz-string";
import { hardReset } from "util/save";

View file

@ -1190,50 +1190,6 @@ export default class Decimal {
return cost.div(currentRpS).add(cost.div(deltaRpS));
}
public [Symbol.for("+")](other: DecimalSource): DecimalSource {
return this.add(other);
}
public [Symbol.for("-")](other: DecimalSource): DecimalSource {
return this.sub(other);
}
public [Symbol.for("*")](other: DecimalSource): DecimalSource {
return this.times(other);
}
public [Symbol.for("/")](other: DecimalSource): DecimalSource {
return this.div(other);
}
public [Symbol.for("minus")](): DecimalSource {
return this.neg();
}
public [Symbol.for("==")](other: DecimalSource): boolean {
return this.eq(other);
}
public [Symbol.for(">")](other: DecimalSource): boolean {
return this.gt(other);
}
public [Symbol.for("<")](other: DecimalSource): boolean {
return this.lt(other);
}
public [Symbol.for(">=")](other: DecimalSource): boolean {
return this.gte(other);
}
public [Symbol.for("<=")](other: DecimalSource): boolean {
return this.lte(other);
}
public [Symbol.for("!=")](other: DecimalSource): boolean {
return this.neq(other);
}
public normalize(): this {
/*
PSEUDOCODE:

1
src/lib/collapseTransition.d.ts vendored Normal file
View file

@ -0,0 +1 @@
declare module '@ivanv/vue-collapse-transition/src/CollapseTransition.vue';

17
src/lib/pwa-register.d.ts vendored Normal file
View file

@ -0,0 +1,17 @@
declare module 'virtual:pwa-register/vue' {
import type { Ref } from 'vue'
export interface RegisterSWOptions {
immediate?: boolean
onNeedRefresh?: () => void
onOfflineReady?: () => void
onRegistered?: (registration: ServiceWorkerRegistration | undefined) => void
onRegisterError?: (error: any) => void
}
export function useRegisterSW(options?: RegisterSWOptions): {
needRefresh: Ref<boolean>
offlineReady: Ref<boolean>
updateServiceWorker: (reloadPage?: boolean) => Promise<void>
}
}

View file

@ -1,12 +1,17 @@
import { App as VueApp, createApp } from "vue";
import App from "./App.vue";
import projInfo from "./data/projInfo.json";
import { GenericLayer } from "./game/layers";
import { PlayerData } from "./game/player";
import { Settings } from "./game/settings";
import { Transient } from "./game/state";
import Decimal, { DecimalSource } from "./util/bignum";
import { load } from "./util/save";
import App from "App.vue";
import projInfo from "data/projInfo.json";
import type { GenericLayer } from "game/layers";
import "game/notifications";
import type { PlayerData } from "game/player";
import type { Settings } from "game/settings";
import type { Transient } from "game/state";
import type { DecimalSource } from "util/bignum";
import Decimal from "util/bignum";
import { load } from "util/save";
import { useRegisterSW } from "virtual:pwa-register/vue";
import type { App as VueApp } from "vue";
import { createApp, nextTick } from "vue";
import { useToast } from "vue-toastification";
document.title = projInfo.title;
if (projInfo.id === "") {
@ -44,13 +49,45 @@ requestAnimationFrame(async () => {
"padding: 4px;"
);
await load();
const { globalBus, startGameLoop } = await require("./game/events");
const { globalBus, startGameLoop } = await import("./game/events");
// Create Vue
const vue = (window.vue = createApp(App));
globalBus.emit("setupVue", vue);
vue.mount("#app");
// Setup PWA update prompt
nextTick(() => {
const toast = useToast();
const { updateServiceWorker } = useRegisterSW({
onNeedRefresh() {
toast.info("New content available, click or reload to update.", {
timeout: false,
closeOnClick: false,
draggable: false,
icon: {
iconClass: "material-icons",
iconChildren: "refresh",
iconTag: "i"
},
rtl: false,
onClick() {
updateServiceWorker();
}
});
},
onOfflineReady() {
toast.info("App ready to work offline");
},
onRegisterError: console.warn,
onRegistered(r) {
if (r) {
setInterval(r.update, 60 * 60 * 1000);
}
}
});
});
startGameLoop();
});

View file

@ -1,6 +1,6 @@
// Import Decimal and numberUtils from a different file to globally change which big num library gets used
// This way switching out big number libraries just needs to happen here, not every file that needs big numbers
import { DecimalSource as RawDecimalSource } from "lib/break_eternity";
import type { DecimalSource as RawDecimalSource } from "lib/break_eternity";
import Decimal, * as numberUtils from "util/break_eternity";
export const {

View file

@ -1,5 +1,6 @@
import Decimal, { DecimalSource } from "lib/break_eternity";
import projInfo from "data/projInfo.json";
import type { DecimalSource } from "lib/break_eternity";
import Decimal from "lib/break_eternity";
export default Decimal;

View file

@ -8,10 +8,6 @@ export function camelToTitle(camel: string): string {
return title;
}
export function isPlainObject(object: unknown): boolean {
return Object.prototype.toString.call(object) === "[object Object]";
}
// eslint-disable-next-line @typescript-eslint/ban-types
export function isFunction(func: unknown): func is Function {
return typeof func === "function";

View file

@ -1,5 +1,6 @@
import { computed, Ref } from "vue";
import { isFunction } from "./common";
import type { Ref } from "vue";
import { computed } from "vue";
import { isFunction } from "util/common";
export const DoNotCache = Symbol("DoNotCache");

View file

@ -1,4 +1,4 @@
import Decimal from "./bignum";
import Decimal from "util/bignum";
export const ProxyState = Symbol("ProxyState");
export const ProxyPath = Symbol("ProxyPath");

View file

@ -1,8 +1,9 @@
import projInfo from "data/projInfo.json";
import player, { Player, PlayerData, stringifySave } from "game/player";
import type { Player, PlayerData } from "game/player";
import player, { stringifySave } from "game/player";
import settings, { loadSettings } from "game/settings";
import { ProxyState } from "./proxies";
import LZString from "lz-string";
import { ProxyState } from "util/proxies";
export function setupInitialStore(player: Partial<PlayerData> = {}): Player {
return Object.assign(

View file

@ -1,31 +1,20 @@
import Col from "components/layout/Column.vue";
import Row from "components/layout/Row.vue";
import type { CoercableComponent, GenericComponent, JSXFunction } from "features/feature";
import { Component as ComponentKey, GatherProps, jsx, Visibility } from "features/feature";
import type { ProcessedComputable } from "util/computed";
import { DoNotCache } from "util/computed";
import type { Component, ComputedRef, DefineComponent, PropType, Ref, ShallowRef } from "vue";
import {
CoercableComponent,
Component as ComponentKey,
GatherProps,
GenericComponent,
jsx,
JSXFunction,
Visibility
} from "features/feature";
import {
Component,
computed,
ComputedRef,
DefineComponent,
defineComponent,
isRef,
onUnmounted,
PropType,
ref,
Ref,
ShallowRef,
shallowRef,
unref,
watchEffect
} from "vue";
import { DoNotCache, ProcessedComputable } from "./computed";
export function coerceComponent(
component: CoercableComponent,
@ -119,7 +108,7 @@ export function setupHoldToClick(
stop: VoidFunction;
handleHolding: VoidFunction;
} {
const interval = ref<null | number>(null);
const interval = ref<NodeJS.Timer | null>(null);
const event = ref<MouseEvent | TouchEvent | undefined>(undefined);
function start(e: MouseEvent | TouchEvent) {

Some files were not shown because too many files have changed in this diff Show more