diff --git a/src/data/common.tsx b/src/data/common.tsx index f392741..b421460 100644 --- a/src/data/common.tsx +++ b/src/data/common.tsx @@ -20,6 +20,7 @@ import type { ProcessedComputable } from "util/computed"; import { convertComputable, processComputable } from "util/computed"; +import { createLazyProxy } from "util/proxies"; import { renderJSX } from "util/vue"; import type { Ref } from "vue"; import { computed, unref } from "vue"; @@ -33,7 +34,7 @@ export interface ResetButtonOptions extends ClickableOptions { tree: GenericTree; /** The specific tree node associated with this reset button */ treeNode: GenericTreeNode; - /** + /** * Text to display on low conversion amounts, describing what "resetting" is in this context. * Defaults to "Reset for ". */ @@ -253,62 +254,67 @@ export interface Section { /** * Takes an array of modifier "sections", and creates a JSXFunction that can render all those sections, and allow each section to be collapsed. * Also returns a list of persistent refs that are used to control which sections are currently collapsed. + * @param sectionsFunc A function that returns the sections to display. */ export function createCollapsibleModifierSections( - sections: Section[] + sectionsFunc: () => Section[] ): [JSXFunction, Persistent[]] { - const processedBase = sections.map(s => convertComputable(s.base)); - const processedBaseText = sections.map(s => convertComputable(s.baseText)); - const processedVisible = sections.map(s => convertComputable(s.visible)); - const collapsed = sections.map(() => persistent(false)); - const jsxFunc = jsx(() => { - const sectionJSX = sections.map((s, i) => { - if (unref(processedVisible[i]) === false) return null; - const header = ( -

(collapsed[i].value = !collapsed[i].value)} - style="cursor: pointer" - > - - ▼ - - {s.title} - {s.subtitle ? ({s.subtitle}) : null} -

- ); + return createLazyProxy(() => { + const sections = sectionsFunc(); - const modifiers = unref(collapsed[i]) ? null : ( - <> -
- - {format(unref(processedBase[i]) ?? 1)} + const processedBase = sections.map(s => convertComputable(s.base)); + const processedBaseText = sections.map(s => convertComputable(s.baseText)); + const processedVisible = sections.map(s => convertComputable(s.visible)); + const collapsed = sections.map(() => persistent(false)); + const jsxFunc = jsx(() => { + const sectionJSX = sections.map((s, i) => { + if (unref(processedVisible[i]) === false) return null; + const header = ( +

(collapsed[i].value = !collapsed[i].value)} + style="cursor: pointer" + > + + ▼ + + {s.title} + {s.subtitle ? ({s.subtitle}) : null} +

+ ); + + const modifiers = unref(collapsed[i]) ? null : ( + <> +
+ + {format(unref(processedBase[i]) ?? 1)} + {s.unit} + + + {renderJSX(unref(processedBaseText[i]) ?? "Base")} + +
+ {renderJSX(unref(s.modifier.description))} + + ); + + return ( + <> + {i === 0 ? null :
} +
+ {header} +
+ {modifiers} +
+ Total: {format(s.modifier.apply(unref(processedBase[i]) ?? 1))} {s.unit} - - - {renderJSX(unref(processedBaseText[i]) ?? "Base")} - -
- {renderJSX(unref(s.modifier.description))} - - ); - - return ( - <> - {i === 0 ? null :
} -
- {header} -
- {modifiers} -
- Total: {format(s.modifier.apply(unref(processedBase[i]) ?? 1))} - {s.unit} -
- - ); +
+ + ); + }); + return <>{sectionJSX}; }); - return <>{sectionJSX}; + return [jsxFunc, collapsed]; }); - return [jsxFunc, collapsed]; } /** diff --git a/src/game/modifiers.tsx b/src/game/modifiers.tsx index 6f090c7..b476027 100644 --- a/src/game/modifiers.tsx +++ b/src/game/modifiers.tsx @@ -6,6 +6,7 @@ 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 { createLazyProxy } from "util/proxies"; import { renderJSX } from "util/vue"; import { computed, unref } from "vue"; @@ -44,112 +45,161 @@ export type ModifierFromOptionalParams = T extends undefined ? Omit, "enabled"> : WithRequired; +/** An object that configures an additive modifier via {@link createAdditiveModifier}. */ +export interface AdditiveModifierOptions< + T extends Computable | undefined, + S extends Computable | undefined +> { + /** The amount to add to the input value. */ + addend: Computable; + /** Description of what this modifier is doing. */ + description?: T; + /** A computable that will be processed and passed directly into the returned modifier. */ + enabled?: S; +} + /** * Create a modifier that adds some value to the input value. - * @param addend The amount to add to the input value. - * @param description Description of what this modifier is doing. - * @param enabled A computable that will be processed and passed directly into the returned modifier. + * @param optionsFunc Additive modifier options. */ export function createAdditiveModifier< T extends Computable | undefined, S extends Computable | undefined, R = ModifierFromOptionalParams ->(addend: Computable, description?: T, enabled?: S): R { - const processedAddend = convertComputable(addend); - const processedDescription = convertComputable(description); - const processedEnabled = enabled == null ? undefined : convertComputable(enabled); - return { - apply: (gain: DecimalSource) => Decimal.add(gain, unref(processedAddend)), - revert: (gain: DecimalSource) => Decimal.sub(gain, unref(processedAddend)), - enabled: processedEnabled, - description: - description == null - ? undefined - : jsx(() => ( -
- - {Decimal.gte(unref(processedAddend), 0) ? "+" : ""} - {format(unref(processedAddend))} - - {unref(processedDescription) ? ( - - {/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */} - {renderJSX(unref(processedDescription)!)} +>(optionsFunc: () => AdditiveModifierOptions): R { + return createLazyProxy(() => { + const { addend, description, enabled } = optionsFunc(); + + const processedAddend = convertComputable(addend); + const processedDescription = convertComputable(description); + const processedEnabled = enabled == null ? undefined : convertComputable(enabled); + return { + apply: (gain: DecimalSource) => Decimal.add(gain, unref(processedAddend)), + revert: (gain: DecimalSource) => Decimal.sub(gain, unref(processedAddend)), + enabled: processedEnabled, + description: + description == null + ? undefined + : jsx(() => ( +
+ + {Decimal.gte(unref(processedAddend), 0) ? "+" : ""} + {format(unref(processedAddend))} - ) : null} -
- )) - } as unknown as R; + {unref(processedDescription) ? ( + + {/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */} + {renderJSX(unref(processedDescription)!)} + + ) : null} +
+ )) + }; + }) as unknown as R; +} + +/** An object that configures an multiplicative modifier via {@link createMultiplicativeModifier}. */ +export interface MultiplicativeModifierOptions< + T extends Computable | undefined, + S extends Computable | undefined +> { + /** The amount to multiply the input value by. */ + multiplier: Computable; + /** Description of what this modifier is doing. */ + description?: T; + /** A computable that will be processed and passed directly into the returned modifier. */ + enabled?: S; } /** * Create a modifier that multiplies the input value by some value. - * @param multiplier The value to multiply the input value by. - * @param description Description of what this modifier is doing. - * @param enabled A computable that will be processed and passed directly into the returned modifier. + * @param optionsFunc Multiplicative modifier options. */ export function createMultiplicativeModifier< T extends Computable | undefined, S extends Computable | undefined, R = ModifierFromOptionalParams ->(multiplier: Computable, description?: T, enabled?: S): R { - const processedMultiplier = convertComputable(multiplier); - const processedDescription = convertComputable(description); - const processedEnabled = enabled == null ? undefined : convertComputable(enabled); - return { - apply: (gain: DecimalSource) => Decimal.times(gain, unref(processedMultiplier)), - revert: (gain: DecimalSource) => Decimal.div(gain, unref(processedMultiplier)), - enabled: processedEnabled, - description: - description == null - ? undefined - : jsx(() => ( -
- x{format(unref(processedMultiplier))} - {unref(processedDescription) ? ( - - {/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */} - {renderJSX(unref(processedDescription)!)} +>(optionsFunc: () => MultiplicativeModifierOptions): R { + return createLazyProxy(() => { + const { multiplier, description, enabled } = optionsFunc(); + + const processedMultiplier = convertComputable(multiplier); + const processedDescription = convertComputable(description); + const processedEnabled = enabled == null ? undefined : convertComputable(enabled); + return { + apply: (gain: DecimalSource) => Decimal.times(gain, unref(processedMultiplier)), + revert: (gain: DecimalSource) => Decimal.div(gain, unref(processedMultiplier)), + enabled: processedEnabled, + description: + description == null + ? undefined + : jsx(() => ( +
+ + x{format(unref(processedMultiplier))} - ) : null} -
- )) - } as unknown as R; + {unref(processedDescription) ? ( + + {/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */} + {renderJSX(unref(processedDescription)!)} + + ) : null} +
+ )) + }; + }) as unknown as R; +} + +/** An object that configures an exponential modifier via {@link createExponentialModifier}. */ +export interface ExponentialModifierOptions< + T extends Computable | undefined, + S extends Computable | undefined +> { + /** The amount to raise the input value to the power of. */ + exponent: Computable; + /** Description of what this modifier is doing. */ + description?: T; + /** A computable that will be processed and passed directly into the returned modifier. */ + enabled?: S; } /** * Create a modifier that raises the input value to the power of some value. - * @param exponent The value to raise the input value to the power of. - * @param description Description of what this modifier is doing. - * @param enabled A computable that will be processed and passed directly into the returned modifier. + * @param optionsFunc Exponential modifier options. */ export function createExponentialModifier< T extends Computable | undefined, S extends Computable | undefined, R = ModifierFromOptionalParams ->(exponent: Computable, description?: T, enabled?: S): R { - const processedExponent = convertComputable(exponent); - const processedDescription = convertComputable(description); - const processedEnabled = enabled == null ? undefined : convertComputable(enabled); - return { - apply: (gain: DecimalSource) => Decimal.pow(gain, unref(processedExponent)), - revert: (gain: DecimalSource) => Decimal.root(gain, unref(processedExponent)), - enabled: processedEnabled, - description: - description == null - ? undefined - : jsx(() => ( -
- ^{format(unref(processedExponent))} - {unref(processedDescription) ? ( - - {/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */} - {renderJSX(unref(processedDescription)!)} +>(optionsFunc: () => ExponentialModifierOptions): R { + return createLazyProxy(() => { + const { exponent, description, enabled } = optionsFunc(); + + const processedExponent = convertComputable(exponent); + const processedDescription = convertComputable(description); + const processedEnabled = enabled == null ? undefined : convertComputable(enabled); + return { + apply: (gain: DecimalSource) => Decimal.pow(gain, unref(processedExponent)), + revert: (gain: DecimalSource) => Decimal.root(gain, unref(processedExponent)), + enabled: processedEnabled, + description: + description == null + ? undefined + : jsx(() => ( +
+ + ^{format(unref(processedExponent))} - ) : null} -
- )) - } as unknown as R; + {unref(processedDescription) ? ( + + {/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */} + {renderJSX(unref(processedDescription)!)} + + ) : null} +
+ )) + }; + }) as unknown as R; } /**