Merge branch 'main' into feat/reset-tests
All checks were successful
Run Tests / test (pull_request) Successful in 2m2s

This commit is contained in:
thepaperpilot 2024-02-21 04:08:24 +00:00
commit ccdf6f0c93
3 changed files with 55 additions and 37 deletions

View file

@ -4,7 +4,7 @@ import { jsx } from "features/feature";
import settings from "game/settings"; import settings from "game/settings";
import type { DecimalSource } from "util/bignum"; import type { DecimalSource } from "util/bignum";
import Decimal, { formatSmall } from "util/bignum"; import Decimal, { formatSmall } from "util/bignum";
import type { WithRequired } from "util/common"; import type { RequiredKeys, WithRequired } from "util/common";
import type { Computable, ProcessedComputable } from "util/computed"; import type { Computable, ProcessedComputable } from "util/computed";
import { convertComputable } from "util/computed"; import { convertComputable } from "util/computed";
import { createLazyProxy } from "util/proxies"; import { createLazyProxy } from "util/proxies";
@ -38,16 +38,11 @@ export interface Modifier {
description?: ProcessedComputable<CoercableComponent>; description?: ProcessedComputable<CoercableComponent>;
} }
/** /** Utility type that represents the output of all modifiers that represent a single operation. */
* Utility type used to narrow down a modifier type that will have a description and/or enabled property based on optional parameters, T and S (respectively). export type OperationModifier<T> = WithRequired<
*/ Modifier,
export type ModifierFromOptionalParams<T, S> = undefined extends T "invert" | "getFormula" | Extract<RequiredKeys<T>, keyof Modifier>
? undefined extends S >;
? Omit<WithRequired<Modifier, "invert" | "getFormula">, "description" | "enabled">
: Omit<WithRequired<Modifier, "invert" | "enabled" | "getFormula">, "description">
: undefined extends S
? Omit<WithRequired<Modifier, "invert" | "description" | "getFormula">, "enabled">
: WithRequired<Modifier, "invert" | "enabled" | "description" | "getFormula">;
/** An object that configures an additive modifier via {@link createAdditiveModifier}. */ /** An object that configures an additive modifier via {@link createAdditiveModifier}. */
export interface AdditiveModifierOptions { export interface AdditiveModifierOptions {
@ -65,9 +60,9 @@ export interface AdditiveModifierOptions {
* Create a modifier that adds some value to the input value. * Create a modifier that adds some value to the input value.
* @param optionsFunc Additive modifier options. * @param optionsFunc Additive modifier options.
*/ */
export function createAdditiveModifier<T extends AdditiveModifierOptions>( export function createAdditiveModifier<T extends AdditiveModifierOptions, S = OperationModifier<T>>(
optionsFunc: OptionsFunc<T> optionsFunc: OptionsFunc<T>
): ModifierFromOptionalParams<T["description"], T["enabled"]> { ) {
return createLazyProxy(feature => { return createLazyProxy(feature => {
const { addend, description, enabled, smallerIsBetter } = optionsFunc.call( const { addend, description, enabled, smallerIsBetter } = optionsFunc.call(
feature, feature,
@ -111,7 +106,7 @@ export function createAdditiveModifier<T extends AdditiveModifierOptions>(
</div> </div>
)) ))
}; };
}) as unknown as ModifierFromOptionalParams<T["description"], T["enabled"]>; }) as S;
} }
/** An object that configures an multiplicative modifier via {@link createMultiplicativeModifier}. */ /** An object that configures an multiplicative modifier via {@link createMultiplicativeModifier}. */
@ -130,9 +125,10 @@ export interface MultiplicativeModifierOptions {
* Create a modifier that multiplies the input value by some value. * Create a modifier that multiplies the input value by some value.
* @param optionsFunc Multiplicative modifier options. * @param optionsFunc Multiplicative modifier options.
*/ */
export function createMultiplicativeModifier<T extends MultiplicativeModifierOptions>( export function createMultiplicativeModifier<
optionsFunc: OptionsFunc<T> T extends MultiplicativeModifierOptions,
): ModifierFromOptionalParams<T["description"], T["enabled"]> { S = OperationModifier<T>
>(optionsFunc: OptionsFunc<T>) {
return createLazyProxy(feature => { return createLazyProxy(feature => {
const { multiplier, description, enabled, smallerIsBetter } = optionsFunc.call( const { multiplier, description, enabled, smallerIsBetter } = optionsFunc.call(
feature, feature,
@ -175,7 +171,7 @@ export function createMultiplicativeModifier<T extends MultiplicativeModifierOpt
</div> </div>
)) ))
}; };
}) as unknown as ModifierFromOptionalParams<T["description"], T["enabled"]>; }) as S;
} }
/** An object that configures an exponential modifier via {@link createExponentialModifier}. */ /** An object that configures an exponential modifier via {@link createExponentialModifier}. */
@ -196,9 +192,10 @@ export interface ExponentialModifierOptions {
* Create a modifier that raises the input value to the power of some value. * Create a modifier that raises the input value to the power of some value.
* @param optionsFunc Exponential modifier options. * @param optionsFunc Exponential modifier options.
*/ */
export function createExponentialModifier<T extends ExponentialModifierOptions>( export function createExponentialModifier<
optionsFunc: OptionsFunc<T> T extends ExponentialModifierOptions,
): ModifierFromOptionalParams<T["description"], T["enabled"]> { S = OperationModifier<T>
>(optionsFunc: OptionsFunc<T>) {
return createLazyProxy(feature => { return createLazyProxy(feature => {
const { exponent, description, enabled, supportLowNumbers, smallerIsBetter } = const { exponent, description, enabled, supportLowNumbers, smallerIsBetter } =
optionsFunc.call(feature, feature); optionsFunc.call(feature, feature);
@ -263,7 +260,7 @@ export function createExponentialModifier<T extends ExponentialModifierOptions>(
</div> </div>
)) ))
}; };
}) as unknown as ModifierFromOptionalParams<T["description"], T["enabled"]>; }) as S;
} }
/** /**
@ -274,11 +271,9 @@ export function createExponentialModifier<T extends ExponentialModifierOptions>(
* @see {@link createModifierSection}. * @see {@link createModifierSection}.
*/ */
export function createSequentialModifier< export function createSequentialModifier<
T extends Modifier[], T extends Modifier,
S = T extends WithRequired<Modifier, "invert">[] S = WithRequired<Modifier, Extract<RequiredKeys<T>, keyof Modifier>>
? WithRequired<Modifier, "description" | "invert"> >(modifiersFunc: () => T[]) {
: Omit<WithRequired<Modifier, "description">, "invert">
>(modifiersFunc: () => T): S {
return createLazyProxy(() => { return createLazyProxy(() => {
const modifiers = modifiersFunc(); const modifiers = modifiersFunc();
@ -296,10 +291,14 @@ export function createSequentialModifier<
: undefined, : undefined,
getFormula: modifiers.every(m => m.getFormula != null) getFormula: modifiers.every(m => m.getFormula != null)
? (gain: FormulaSource) => ? (gain: FormulaSource) =>
modifiers modifiers.reduce((acc, curr) => {
if (curr.enabled == null || curr.enabled === true) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return curr.getFormula!(acc);
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
.reduce((acc, curr) => Formula.if(acc, curr.enabled ?? true, return Formula.if(acc, curr.enabled, acc => curr.getFormula!(acc));
acc => curr.getFormula!(acc), acc => acc), gain) }, gain)
: undefined, : undefined,
enabled: modifiers.some(m => m.enabled != null) enabled: modifiers.some(m => m.enabled != null)
? computed(() => modifiers.filter(m => unref(m.enabled) !== false).length > 0) ? computed(() => modifiers.filter(m => unref(m.enabled) !== false).length > 0)
@ -317,7 +316,7 @@ export function createSequentialModifier<
)) ))
: undefined : undefined
}; };
}) as unknown as S; }) as S;
} }
/** An object that configures a modifier section via {@link createModifierSection}. */ /** An object that configures a modifier section via {@link createModifierSection}. */

View file

@ -1,3 +1,11 @@
export type RequiredKeys<T> = {
[K in keyof T]-?: NonNullable<unknown> extends Pick<T, K> ? never : K;
}[keyof T];
export type OptionalKeys<T> = {
[K in keyof T]-?: NonNullable<unknown> extends Pick<T, K> ? K : never;
}[keyof 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>> = T extends ReadonlyArray<infer S>

View file

@ -133,14 +133,14 @@ describe("Exponential Modifiers", () =>
testModifiers(createExponentialModifier, "exponent", Decimal.pow)); testModifiers(createExponentialModifier, "exponent", Decimal.pow));
describe("Sequential Modifiers", () => { describe("Sequential Modifiers", () => {
function createModifier( function createModifier<T extends Partial<ModifierConstructorOptions>>(
value: Computable<DecimalSource>, value: Computable<DecimalSource>,
options: Partial<ModifierConstructorOptions> = {} options?: T
): WithRequired<Modifier, "invert" | "getFormula"> { ) {
return createSequentialModifier(() => [ return createSequentialModifier(() => [
createAdditiveModifier(() => ({ ...options, addend: value })), createAdditiveModifier(() => ({ ...(options ?? {}), addend: value })),
createMultiplicativeModifier(() => ({ ...options, multiplier: value })), createMultiplicativeModifier(() => ({ ...(options ?? {}), multiplier: value })),
createExponentialModifier(() => ({ ...options, exponent: value })) createExponentialModifier(() => ({ ...(options ?? {}), exponent: value }))
]); ]);
} }
@ -199,6 +199,17 @@ describe("Sequential Modifiers", () => {
// So long as one is true or undefined, enable should be true // So long as one is true or undefined, enable should be true
expect(unref(modifier.enabled)).toBe(true); expect(unref(modifier.enabled)).toBe(true);
}); });
test("respects enabled", () => {
const value = ref(10);
const enabled = ref(false);
const modifier = createSequentialModifier(() => [
createMultiplicativeModifier(() => ({ multiplier: 5, enabled }))
]);
const formula = modifier.getFormula(Formula.variable(value));
expect(formula.evaluate()).compare_tolerance(value.value);
enabled.value = true;
expect(formula.evaluate()).not.compare_tolerance(value.value);
});
}); });
describe("applies smallerIsBetter correctly", () => { describe("applies smallerIsBetter correctly", () => {