Reworked flow for converting modifiers to formulas, and renamed revert to invert

This commit is contained in:
thepaperpilot 2023-04-15 09:39:16 -05:00
parent 8c0a0c4410
commit c65dc777cc
4 changed files with 33 additions and 42 deletions

View file

@ -12,7 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **BREAKING** Formulas, which can be used to calculate buy max for you - **BREAKING** Formulas, which can be used to calculate buy max for you
- Requirements can use them so repeatables and challenges can be "buy max" without any extra effort - Requirements can use them so repeatables and challenges can be "buy max" without any extra effort
- Conversions now use formulas instead of the old scaling functions system, allowing for arbitrary functions that are much easier to follow - Conversions now use formulas instead of the old scaling functions system, allowing for arbitrary functions that are much easier to follow
- There's a utility for converting modifiers to formulas, thus replacing things like the gain modifier on conversions - Modifiers have a new getFormula property
- Action feature, which is a clickable with a cooldown - Action feature, which is a clickable with a cooldown
- ETA util (calculates time until a specific amount of a resource, based on its current gain rate) - ETA util (calculates time until a specific amount of a resource, based on its current gain rate)
- createCollapsibleAchievements util - createCollapsibleAchievements util

View file

@ -491,29 +491,3 @@ export function createFormulaPreview(
return formatSmall(formula.evaluate()); return formatSmall(formula.evaluate());
}); });
} }
/**
* Utility for converting a modifier into a formula. Takes the input for this formula as the base parameter.
* @param modifier The modifier to convert to the formula
* @param base An existing formula or processed DecimalSource that will be the input to the formula
*/
export function modifierToFormula<T extends GenericFormula>(
modifier: WithRequired<Modifier, "revert">,
base: T
): T;
export function modifierToFormula(modifier: Modifier, base: FormulaSource): GenericFormula;
export function modifierToFormula(modifier: Modifier, base: FormulaSource) {
return new Formula({
inputs: [base],
evaluate: val => modifier.apply(val),
invert:
"revert" in modifier && modifier.revert != null
? (val, lhs) => {
if (lhs instanceof Formula && lhs.hasVariable()) {
return lhs.invert(modifier.revert!(val));
}
throw new Error("Could not invert due to no input being a variable");
}
: undefined
});
}

View file

@ -36,7 +36,7 @@ import { ClickableOptions } from "./clickables/clickable";
export const ActionType = Symbol("Action"); export const ActionType = Symbol("Action");
/** /**
* An object that configures a {@link Action}. * An object that configures an {@link Action}.
*/ */
export interface ActionOptions extends Omit<ClickableOptions, "onClick" | "onHold"> { export interface ActionOptions extends Omit<ClickableOptions, "onClick" | "onHold"> {
/** The cooldown during which the action cannot be performed again, in seconds. */ /** The cooldown during which the action cannot be performed again, in seconds. */
@ -71,7 +71,7 @@ export interface BaseAction {
[GatherProps]: () => Record<string, unknown>; [GatherProps]: () => Record<string, unknown>;
} }
/** An object that represens a feature that can be clicked upon, and then have a cooldown before they can be clicked again. */ /** An object that represents a feature that can be clicked upon, and then has a cooldown before it can be clicked again. */
export type Action<T extends ActionOptions> = Replace< export type Action<T extends ActionOptions> = Replace<
T & BaseAction, T & BaseAction,
{ {

View file

@ -10,6 +10,8 @@ import { convertComputable } from "util/computed";
import { createLazyProxy } from "util/proxies"; import { createLazyProxy } from "util/proxies";
import { renderJSX } from "util/vue"; import { renderJSX } from "util/vue";
import { computed, unref } from "vue"; import { computed, unref } from "vue";
import Formula from "./formulas/formulas";
import { FormulaSource, GenericFormula } from "./formulas/types";
/** /**
* An object that can be used to apply or unapply some modification to a number. * An object that can be used to apply or unapply some modification to a number.
@ -21,7 +23,9 @@ export interface Modifier {
/** Applies some operation on the input and returns the result. */ /** Applies some operation on the input and returns the result. */
apply: (gain: DecimalSource) => DecimalSource; apply: (gain: DecimalSource) => DecimalSource;
/** Reverses the operation applied by the apply property. Required by some features. */ /** Reverses the operation applied by the apply property. Required by some features. */
revert?: (gain: DecimalSource) => DecimalSource; invert?: (gain: DecimalSource) => DecimalSource;
/** Get a formula for this modifier. Required by some features. */
getFormula?: (gain: FormulaSource) => GenericFormula;
/** /**
* Whether or not this modifier should be considered enabled. * Whether or not this modifier should be considered enabled.
* Typically for use with modifiers passed into {@link createSequentialModifier}. * Typically for use with modifiers passed into {@link createSequentialModifier}.
@ -39,11 +43,11 @@ export interface Modifier {
*/ */
export type ModifierFromOptionalParams<T, S> = T extends undefined export type ModifierFromOptionalParams<T, S> = T extends undefined
? S extends undefined ? S extends undefined
? Omit<WithRequired<Modifier, "revert">, "description" | "enabled"> ? Omit<WithRequired<Modifier, "invert">, "description" | "enabled">
: Omit<WithRequired<Modifier, "revert" | "enabled">, "description"> : Omit<WithRequired<Modifier, "invert" | "enabled">, "description">
: S extends undefined : S extends undefined
? Omit<WithRequired<Modifier, "revert" | "description">, "enabled"> ? Omit<WithRequired<Modifier, "invert" | "description">, "enabled">
: WithRequired<Modifier, "revert" | "enabled" | "description">; : WithRequired<Modifier, "invert" | "enabled" | "description">;
/** 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 {
@ -72,7 +76,8 @@ export function createAdditiveModifier<T extends AdditiveModifierOptions>(
const processedEnabled = enabled == null ? undefined : convertComputable(enabled); const processedEnabled = enabled == null ? undefined : convertComputable(enabled);
return { return {
apply: (gain: DecimalSource) => Decimal.add(gain, unref(processedAddend)), apply: (gain: DecimalSource) => Decimal.add(gain, unref(processedAddend)),
revert: (gain: DecimalSource) => Decimal.sub(gain, unref(processedAddend)), invert: (gain: DecimalSource) => Decimal.sub(gain, unref(processedAddend)),
getFormula: (gain: FormulaSource) => Formula.add(gain, processedAddend),
enabled: processedEnabled, enabled: processedEnabled,
description: description:
description == null description == null
@ -133,7 +138,8 @@ export function createMultiplicativeModifier<T extends MultiplicativeModifierOpt
const processedEnabled = enabled == null ? undefined : convertComputable(enabled); const processedEnabled = enabled == null ? undefined : convertComputable(enabled);
return { return {
apply: (gain: DecimalSource) => Decimal.times(gain, unref(processedMultiplier)), apply: (gain: DecimalSource) => Decimal.times(gain, unref(processedMultiplier)),
revert: (gain: DecimalSource) => Decimal.div(gain, unref(processedMultiplier)), invert: (gain: DecimalSource) => Decimal.div(gain, unref(processedMultiplier)),
getFormula: (gain: FormulaSource) => Formula.times(gain, processedMultiplier),
enabled: processedEnabled, enabled: processedEnabled,
description: description:
description == null description == null
@ -206,7 +212,7 @@ export function createExponentialModifier<T extends ExponentialModifierOptions>(
} }
return result; return result;
}, },
revert: (gain: DecimalSource) => { invert: (gain: DecimalSource) => {
let result = gain; let result = gain;
if (supportLowNumbers) { if (supportLowNumbers) {
result = Decimal.add(result, 1); result = Decimal.add(result, 1);
@ -217,6 +223,10 @@ export function createExponentialModifier<T extends ExponentialModifierOptions>(
} }
return result; return result;
}, },
getFormula: (gain: FormulaSource) =>
supportLowNumbers
? Formula.add(gain, 1).pow(processedExponent).sub(1)
: Formula.pow(gain, processedExponent),
enabled: processedEnabled, enabled: processedEnabled,
description: description:
description == null description == null
@ -259,9 +269,9 @@ export function createExponentialModifier<T extends ExponentialModifierOptions>(
*/ */
export function createSequentialModifier< export function createSequentialModifier<
T extends Modifier[], T extends Modifier[],
S = T extends WithRequired<Modifier, "revert">[] S = T extends WithRequired<Modifier, "invert">[]
? WithRequired<Modifier, "description" | "revert"> ? WithRequired<Modifier, "description" | "invert">
: Omit<WithRequired<Modifier, "description">, "revert"> : Omit<WithRequired<Modifier, "description">, "invert">
>(modifiersFunc: () => T): S { >(modifiersFunc: () => T): S {
return createLazyProxy(() => { return createLazyProxy(() => {
const modifiers = modifiersFunc(); const modifiers = modifiersFunc();
@ -271,12 +281,19 @@ export function createSequentialModifier<
modifiers modifiers
.filter(m => unref(m.enabled) !== false) .filter(m => unref(m.enabled) !== false)
.reduce((gain, modifier) => modifier.apply(gain), gain), .reduce((gain, modifier) => modifier.apply(gain), gain),
revert: modifiers.every(m => m.revert != null) invert: modifiers.every(m => m.invert != null)
? (gain: DecimalSource) => ? (gain: DecimalSource) =>
modifiers modifiers
.filter(m => unref(m.enabled) !== false) .filter(m => unref(m.enabled) !== false)
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
.reduceRight((gain, modifier) => modifier.revert!(gain), gain) .reduceRight((gain, modifier) => modifier.invert!(gain), gain)
: undefined,
getFormula: modifiers.every(m => m.getFormula != null)
? (gain: FormulaSource) =>
modifiers
.filter(m => unref(m.enabled) !== false)
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
.reduce((acc, curr) => curr.getFormula!(acc), gain)
: undefined, : undefined,
enabled: computed(() => modifiers.filter(m => unref(m.enabled) !== false).length > 0), enabled: computed(() => modifiers.filter(m => unref(m.enabled) !== false).length > 0),
description: jsx(() => ( description: jsx(() => (