diff --git a/CHANGELOG.md b/CHANGELOG.md index f01665b..03f3cf5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 - 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 - - 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 - ETA util (calculates time until a specific amount of a resource, based on its current gain rate) - createCollapsibleAchievements util diff --git a/src/data/common.tsx b/src/data/common.tsx index 3246135..8050818 100644 --- a/src/data/common.tsx +++ b/src/data/common.tsx @@ -491,29 +491,3 @@ export function createFormulaPreview( 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( - modifier: WithRequired, - 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 - }); -} diff --git a/src/features/action.tsx b/src/features/action.tsx index afd8021..46f0e72 100644 --- a/src/features/action.tsx +++ b/src/features/action.tsx @@ -36,7 +36,7 @@ import { ClickableOptions } from "./clickables/clickable"; export const ActionType = Symbol("Action"); /** - * An object that configures a {@link Action}. + * An object that configures an {@link Action}. */ export interface ActionOptions extends Omit { /** The cooldown during which the action cannot be performed again, in seconds. */ @@ -71,7 +71,7 @@ export interface BaseAction { [GatherProps]: () => Record; } -/** 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 = Replace< T & BaseAction, { diff --git a/src/game/modifiers.tsx b/src/game/modifiers.tsx index fd96c77..c17422b 100644 --- a/src/game/modifiers.tsx +++ b/src/game/modifiers.tsx @@ -10,6 +10,8 @@ import { convertComputable } from "util/computed"; import { createLazyProxy } from "util/proxies"; import { renderJSX } from "util/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. @@ -21,7 +23,9 @@ export interface Modifier { /** Applies some operation on the input and returns the result. */ apply: (gain: DecimalSource) => DecimalSource; /** 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. * Typically for use with modifiers passed into {@link createSequentialModifier}. @@ -39,11 +43,11 @@ export interface Modifier { */ export type ModifierFromOptionalParams = T extends undefined ? S extends undefined - ? Omit, "description" | "enabled"> - : Omit, "description"> + ? Omit, "description" | "enabled"> + : Omit, "description"> : S extends undefined - ? Omit, "enabled"> - : WithRequired; + ? Omit, "enabled"> + : WithRequired; /** An object that configures an additive modifier via {@link createAdditiveModifier}. */ export interface AdditiveModifierOptions { @@ -72,7 +76,8 @@ export function createAdditiveModifier( const processedEnabled = enabled == null ? undefined : convertComputable(enabled); return { 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, description: description == null @@ -133,7 +138,8 @@ export function createMultiplicativeModifier 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, description: description == null @@ -206,7 +212,7 @@ export function createExponentialModifier( } return result; }, - revert: (gain: DecimalSource) => { + invert: (gain: DecimalSource) => { let result = gain; if (supportLowNumbers) { result = Decimal.add(result, 1); @@ -217,6 +223,10 @@ export function createExponentialModifier( } return result; }, + getFormula: (gain: FormulaSource) => + supportLowNumbers + ? Formula.add(gain, 1).pow(processedExponent).sub(1) + : Formula.pow(gain, processedExponent), enabled: processedEnabled, description: description == null @@ -259,9 +269,9 @@ export function createExponentialModifier( */ export function createSequentialModifier< T extends Modifier[], - S = T extends WithRequired[] - ? WithRequired - : Omit, "revert"> + S = T extends WithRequired[] + ? WithRequired + : Omit, "invert"> >(modifiersFunc: () => T): S { return createLazyProxy(() => { const modifiers = modifiersFunc(); @@ -271,12 +281,19 @@ export function createSequentialModifier< modifiers .filter(m => unref(m.enabled) !== false) .reduce((gain, modifier) => modifier.apply(gain), gain), - revert: modifiers.every(m => m.revert != null) + invert: modifiers.every(m => m.invert != null) ? (gain: DecimalSource) => modifiers .filter(m => unref(m.enabled) !== false) // 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, enabled: computed(() => modifiers.filter(m => unref(m.enabled) !== false).length > 0), description: jsx(() => (