WIP: Feature Rewrite #87

Draft
thepaperpilot wants to merge 35 commits from feat/ure-rewrite into main
18 changed files with 81 additions and 73 deletions
Showing only changes of commit 88abd53faf - Show all commits

1
.eslintignore Normal file
View file

@ -0,0 +1 @@
.eslintrc.cjs

View file

@ -5,6 +5,11 @@ module.exports = {
env: { env: {
node: true node: true
}, },
parser: '@typescript-eslint/parser',
plugins: ["@typescript-eslint"],
overrides: [
{
files: ['*.ts', '*.tsx'],
extends: [ extends: [
"plugin:vue/vue3-essential", "plugin:vue/vue3-essential",
"@vue/eslint-config-typescript/recommended", "@vue/eslint-config-typescript/recommended",
@ -12,8 +17,10 @@ module.exports = {
], ],
parserOptions: { parserOptions: {
ecmaVersion: 2020, ecmaVersion: 2020,
project: "tsconfig.json" project: "./tsconfig.json"
}, },
}
],
ignorePatterns: ["src/lib"], ignorePatterns: ["src/lib"],
rules: { rules: {
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off", "no-console": process.env.NODE_ENV === "production" ? "warn" : "off",

View file

@ -43,13 +43,14 @@
}, },
"devDependencies": { "devDependencies": {
"@ivanv/vue-collapse-transition": "^1.0.2", "@ivanv/vue-collapse-transition": "^1.0.2",
"@rushstack/eslint-patch": "^1.1.0", "@rushstack/eslint-patch": "^1.7.2",
"@types/lz-string": "^1.5.0", "@types/lz-string": "^1.5.0",
"@vue/eslint-config-prettier": "^7.0.0", "@typescript-eslint/parser": "^7.2.0",
"@vue/eslint-config-typescript": "^10.0.0", "@vue/eslint-config-prettier": "^9.0.0",
"eslint": "^8.6.0", "@vue/eslint-config-typescript": "^13.0.0",
"eslint": "^8.57.0",
"jsdom": "^24.0.0", "jsdom": "^24.0.0",
"prettier": "^2.5.1", "prettier": "^3.2.5",
"typescript": "^5.4.2", "typescript": "^5.4.2",
"vitest": "^1.4.0", "vitest": "^1.4.0",
"vue-tsc": "^2.0.6" "vue-tsc": "^2.0.6"

View file

@ -3,7 +3,7 @@
"title": "Profectus", "title": "Profectus",
"description": "A project made in Profectus", "description": "A project made in Profectus",
"id": "", "id": "321",
"author": "", "author": "",
"discordName": "", "discordName": "",
"discordLink": "", "discordLink": "",

View file

@ -1,5 +1,4 @@
import { computed } from "@vue/reactivity"; import { computed } from "vue";
import { isArray } from "@vue/shared";
import Select from "components/fields/Select.vue"; import Select from "components/fields/Select.vue";
import AchievementComponent from "features/achievements/Achievement.vue"; import AchievementComponent from "features/achievements/Achievement.vue";
import { GenericDecorator } from "features/decorators/common"; import { GenericDecorator } from "features/decorators/common";
@ -275,7 +274,7 @@ export function createAchievement<T extends AchievementOptions>(
const requirements = [ const requirements = [
createVisibilityRequirement(genericAchievement), createVisibilityRequirement(genericAchievement),
createBooleanRequirement(() => !genericAchievement.earned.value), createBooleanRequirement(() => !genericAchievement.earned.value),
...(isArray(achievement.requirements) ...(Array.isArray(achievement.requirements)
? achievement.requirements ? achievement.requirements
: [achievement.requirements]) : [achievement.requirements])
]; ];

View file

@ -1,4 +1,3 @@
import { isArray } from "@vue/shared";
import ClickableComponent from "features/clickables/Clickable.vue"; import ClickableComponent from "features/clickables/Clickable.vue";
import { import {
Component, Component,
@ -157,7 +156,7 @@ export function createAction<T extends ActionOptions>(
} }
]; ];
const originalStyle = unref(style); const originalStyle = unref(style);
if (isArray(originalStyle)) { if (Array.isArray(originalStyle)) {
currStyle.push(...originalStyle); currStyle.push(...originalStyle);
} else if (originalStyle != null) { } else if (originalStyle != null) {
currStyle.push(originalStyle); currStyle.push(originalStyle);

View file

@ -1,4 +1,3 @@
import { isArray } from "@vue/shared";
import Toggle from "components/fields/Toggle.vue"; import Toggle from "components/fields/Toggle.vue";
import ChallengeComponent from "features/challenges/Challenge.vue"; import ChallengeComponent from "features/challenges/Challenge.vue";
import { GenericDecorator } from "features/decorators/common"; import { GenericDecorator } from "features/decorators/common";
@ -348,7 +347,7 @@ export function createActiveChallenge(
export function isAnyChallengeActive( export function isAnyChallengeActive(
challenges: GenericChallenge[] | Ref<GenericChallenge | null> challenges: GenericChallenge[] | Ref<GenericChallenge | null>
): Ref<boolean> { ): Ref<boolean> {
if (isArray(challenges)) { if (Array.isArray(challenges)) {
challenges = createActiveChallenge(challenges); challenges = createActiveChallenge(challenges);
} }
return computed(() => (challenges as Ref<GenericChallenge | null>).value != null); return computed(() => (challenges as Ref<GenericChallenge | null>).value != null);

View file

@ -1,4 +1,3 @@
import { isArray } from "@vue/shared";
import ClickableComponent from "features/clickables/Clickable.vue"; import ClickableComponent from "features/clickables/Clickable.vue";
import type { import type {
CoercableComponent, CoercableComponent,
@ -162,7 +161,7 @@ export function createRepeatable<T extends RepeatableOptions>(
canMaximize: true canMaximize: true
} as const; } as const;
const visibilityRequirement = createVisibilityRequirement(repeatable as GenericRepeatable); const visibilityRequirement = createVisibilityRequirement(repeatable as GenericRepeatable);
if (isArray(repeatable.requirements)) { if (Array.isArray(repeatable.requirements)) {
repeatable.requirements.unshift(visibilityRequirement); repeatable.requirements.unshift(visibilityRequirement);
repeatable.requirements.push(limitRequirement); repeatable.requirements.push(limitRequirement);
} else { } else {

View file

@ -1,4 +1,3 @@
import { isArray } from "@vue/shared";
import { GenericDecorator } from "features/decorators/common"; import { GenericDecorator } from "features/decorators/common";
import type { import type {
CoercableComponent, CoercableComponent,
@ -151,7 +150,7 @@ export function createUpgrade<T extends UpgradeOptions>(
}; };
const visibilityRequirement = createVisibilityRequirement(upgrade as GenericUpgrade); const visibilityRequirement = createVisibilityRequirement(upgrade as GenericUpgrade);
if (isArray(upgrade.requirements)) { if (Array.isArray(upgrade.requirements)) {
upgrade.requirements.unshift(visibilityRequirement); upgrade.requirements.unshift(visibilityRequirement);
} else { } else {
upgrade.requirements = [visibilityRequirement, upgrade.requirements]; upgrade.requirements = [visibilityRequirement, upgrade.requirements];

View file

@ -48,6 +48,7 @@ export interface InternalFormula<T extends [FormulaSource] | FormulaSource[]> {
invertIntegral?(value: DecimalSource): DecimalSource; invertIntegral?(value: DecimalSource): DecimalSource;
} }
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
export abstract class InternalFormula<T extends [FormulaSource] | FormulaSource[]> { export abstract class InternalFormula<T extends [FormulaSource] | FormulaSource[]> {
readonly inputs: T; readonly inputs: T;

View file

@ -1,4 +1,3 @@
import { isArray } from "@vue/shared";
import { globalBus } from "game/events"; import { globalBus } from "game/events";
import type { GenericLayer } from "game/layers"; import type { GenericLayer } from "game/layers";
import { addingLayers, persistentRefs } from "game/layers"; import { addingLayers, persistentRefs } from "game/layers";
@ -341,7 +340,7 @@ globalBus.on("addLayer", (layer: GenericLayer, saveData: Record<string, unknown>
// Show warning for persistent values inside arrays // Show warning for persistent values inside arrays
// TODO handle arrays better // TODO handle arrays better
if (foundPersistentInChild) { if (foundPersistentInChild) {
if (isArray(value) && !isArray(obj)) { if (Array.isArray(value) && !Array.isArray(obj)) {
console.warn( 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.", "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.",
ProxyState in obj ProxyState in obj

View file

@ -1,4 +1,3 @@
import { isArray } from "@vue/shared";
import { import {
CoercableComponent, CoercableComponent,
isVisible, isVisible,
@ -270,7 +269,7 @@ export function createBooleanRequirement(
* @param requirements The 1+ requirements to check * @param requirements The 1+ requirements to check
*/ */
export function requirementsMet(requirements: Requirements): boolean { export function requirementsMet(requirements: Requirements): boolean {
if (isArray(requirements)) { if (Array.isArray(requirements)) {
return requirements.every(requirementsMet); return requirements.every(requirementsMet);
} }
const reqsMet = unref(requirements.requirementMet); const reqsMet = unref(requirements.requirementMet);
@ -282,7 +281,7 @@ export function requirementsMet(requirements: Requirements): boolean {
* @param requirements The 1+ requirements to check * @param requirements The 1+ requirements to check
*/ */
export function maxRequirementsMet(requirements: Requirements): DecimalSource { export function maxRequirementsMet(requirements: Requirements): DecimalSource {
if (isArray(requirements)) { if (Array.isArray(requirements)) {
return requirements.map(maxRequirementsMet).reduce(Decimal.min); return requirements.map(maxRequirementsMet).reduce(Decimal.min);
} }
const reqsMet = unref(requirements.requirementMet); const reqsMet = unref(requirements.requirementMet);
@ -300,13 +299,13 @@ export function maxRequirementsMet(requirements: Requirements): DecimalSource {
* @param amount The amount of levels earned to be displayed * @param amount The amount of levels earned to be displayed
*/ */
export function displayRequirements(requirements: Requirements, amount: DecimalSource = 1) { export function displayRequirements(requirements: Requirements, amount: DecimalSource = 1) {
if (isArray(requirements)) { if (Array.isArray(requirements)) {
requirements = requirements.filter(r => isVisible(r.visibility)); requirements = requirements.filter(r => isVisible(r.visibility));
if (requirements.length === 1) { if (requirements.length === 1) {
requirements = requirements[0]; requirements = requirements[0];
} }
} }
if (isArray(requirements)) { if (Array.isArray(requirements)) {
requirements = requirements.filter(r => "partialDisplay" in r); requirements = requirements.filter(r => "partialDisplay" in r);
const withCosts = requirements.filter(r => unref(r.requiresPay)); const withCosts = requirements.filter(r => unref(r.requiresPay));
const withoutCosts = requirements.filter(r => !unref(r.requiresPay)); const withoutCosts = requirements.filter(r => !unref(r.requiresPay));
@ -344,7 +343,7 @@ export function displayRequirements(requirements: Requirements, amount: DecimalS
* @param amount How many levels to pay for * @param amount How many levels to pay for
*/ */
export function payRequirements(requirements: Requirements, amount: DecimalSource = 1) { export function payRequirements(requirements: Requirements, amount: DecimalSource = 1) {
if (isArray(requirements)) { if (Array.isArray(requirements)) {
requirements.filter(r => unref(r.requiresPay)).forEach(r => r.pay?.(amount)); requirements.filter(r => unref(r.requiresPay)).forEach(r => r.pay?.(amount));
} else if (unref(requirements.requiresPay)) { } else if (unref(requirements.requiresPay)) {
requirements.pay?.(amount); requirements.pay?.(amount);

View file

@ -8,9 +8,8 @@ export type OptionalKeys<T> = {
export type OmitOptional<T> = Pick<T, RequiredKeys<T>>; export type OmitOptional<T> = Pick<T, RequiredKeys<T>>;
export type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] }; export type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };
export type ArrayElements<T extends ReadonlyArray<unknown>> = T extends ReadonlyArray<infer S> export type ArrayElements<T extends ReadonlyArray<unknown>> =
? S T extends ReadonlyArray<infer S> ? S : never;
: never;
// Reference: // Reference:
// https://stackoverflow.com/questions/7225407/convert-camelcasetext-to-sentence-case-text // https://stackoverflow.com/questions/7225407/convert-camelcasetext-to-sentence-case-text
@ -36,5 +35,6 @@ export enum Direction {
Down = "Down", Down = "Down",
Left = "Left", Left = "Left",
Right = "Right", Right = "Right",
// eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values
Default = "Up" Default = "Up"
} }

View file

@ -5,7 +5,8 @@ import Decimal from "util/bignum";
export const ProxyState = Symbol("ProxyState"); export const ProxyState = Symbol("ProxyState");
export const ProxyPath = Symbol("ProxyPath"); export const ProxyPath = Symbol("ProxyPath");
export type ProxiedWithState<T> = NonNullable<T> extends Record<PropertyKey, unknown> export type ProxiedWithState<T> =
NonNullable<T> extends Record<PropertyKey, unknown>
? NonNullable<T> extends Decimal ? NonNullable<T> extends Decimal
? T ? T
: { : {
@ -16,7 +17,8 @@ export type ProxiedWithState<T> = NonNullable<T> extends Record<PropertyKey, unk
} }
: T; : T;
export type Proxied<T> = NonNullable<T> extends Record<PropertyKey, unknown> export type Proxied<T> =
NonNullable<T> extends Record<PropertyKey, unknown>
? NonNullable<T> extends Persistent<infer S> ? NonNullable<T> extends Persistent<infer S>
? NonPersistent<S> ? NonPersistent<S>
: NonNullable<T> extends Decimal : NonNullable<T> extends Decimal

View file

@ -245,8 +245,11 @@ export function trackHover(element: VueFeature): Ref<boolean> {
} }
export function kebabifyObject(object: Record<string, unknown>) { export function kebabifyObject(object: Record<string, unknown>) {
return Object.keys(object).reduce((acc, curr) => { return Object.keys(object).reduce(
(acc, curr) => {
acc[camelToKebab(curr)] = object[curr]; acc[camelToKebab(curr)] = object[curr];
return acc; return acc;
}, {} as Record<string, unknown>); },
{} as Record<string, unknown>
);
} }