diff --git a/src/game/requirements.tsx b/src/game/requirements.tsx index ca68843..780afca 100644 --- a/src/game/requirements.tsx +++ b/src/game/requirements.tsx @@ -11,7 +11,12 @@ import { import { createLazyProxy } from "util/proxies"; import { joinJSX, renderJSX } from "util/vue"; import { computed, unref } from "vue"; -import Formula, { calculateCost, calculateMaxAffordable, GenericFormula } from "./formulas"; +import Formula, { + calculateCost, + calculateMaxAffordable, + GenericFormula, + InvertibleFormula +} from "./formulas"; /** * An object that can be used to describe a requirement to perform some purchase or other action. @@ -39,9 +44,9 @@ export interface Requirement { */ requiresPay: ProcessedComputable; /** - * Whether or not this requirement can have multiple levels of requirements that can be completed at once. + * Whether or not this requirement can have multiple levels of requirements that can be met at once. Requirement is assumed to not have multiple levels if this property not present. */ - buyMax?: ProcessedComputable; + canMaximize?: ProcessedComputable; /** * Perform any effects to the game state that should happen when the requirement gets triggered. * @param amount The amount of levels of requirements to pay for. @@ -61,7 +66,7 @@ export interface CostRequirementOptions { */ resource: Resource; /** - * The amount of {@link resource} that must be met for this requirement. You can pass a formula, in which case {@link buyMax} will work out of the box (assuming its invertible and, for more accurate calculations, its integral is invertible). If you don't pass a formula then you can still support buyMax by passing a custom {@link pay} function. + * The amount of {@link resource} that must be met for this requirement. You can pass a formula, in which case maximizing will work out of the box (assuming its invertible and, for more accurate calculations, its integral is invertible). If you don't pass a formula then you can still support maximizing by passing a custom {@link pay} function. */ cost: Computable | GenericFormula; /** @@ -72,19 +77,14 @@ export interface CostRequirementOptions { * Pass-through to {@link Requirement.requiresPay}. If not set to false, the default {@link pay} function will remove {@link cost} from {@link resource}. */ requiresPay?: Computable; - /** - * Pass-through to {@link Requirement.buyMax}. - * @see {@link cost} for restrictions on buying max support. - */ - buyMax?: Computable; /** * When calculating multiple levels to be handled at once, whether it should consider resources used for each level as spent. Setting this to false causes calculations to be faster with larger numbers and supports more math functions. * @see {Formula} */ spendResources?: Computable; /** - * Pass-through to {@link Requirement.pay}. May be required for buying max support. - * @see {@link cost} for restrictions on buying max support. + * Pass-through to {@link Requirement.pay}. May be required for maximizing support. + * @see {@link cost} for restrictions on maximizing support. */ pay?: (amount?: DecimalSource) => void; } @@ -159,16 +159,12 @@ export function createCostRequirement( : unref(req.cost as ProcessedComputable); req.resource.value = Decimal.sub(req.resource.value, cost).max(0); }); - processComputable(req as T, "buyMax"); - if ( - "buyMax" in req && - req.buyMax !== false && - req.cost instanceof Formula && - req.cost.isInvertible() - ) { + req.canMaximize = req.cost instanceof Formula && req.cost.isInvertible(); + + if (req.canMaximize) { req.requirementMet = calculateMaxAffordable( - req.cost, + req.cost as InvertibleFormula, req.resource, unref(req.spendResources as ProcessedComputable | undefined) ?? true ); @@ -244,6 +240,8 @@ export function maxRequirementsMet(requirements: Requirements): DecimalSource { const reqsMet = unref(requirements.requirementMet); if (typeof reqsMet === "boolean") { return reqsMet ? Infinity : 0; + } else if (Decimal.gt(reqsMet, 1) && unref(requirements.canMaximize) !== true) { + return 1; } return reqsMet; } diff --git a/tests/game/requirements.test.ts b/tests/game/requirements.test.ts index 73d539f..fa9ae5e 100644 --- a/tests/game/requirements.test.ts +++ b/tests/game/requirements.test.ts @@ -27,23 +27,23 @@ describe("Creating cost requirement", () => { }); // eslint-disable-next-line @typescript-eslint/no-explicit-any - test("resource pass-through", () => (requirement as any).resource === resource); + test("resource pass-through", () => expect((requirement as any).resource).toBe(resource)); // eslint-disable-next-line @typescript-eslint/no-explicit-any - test("cost pass-through", () => (requirement as any).cost === 10); + test("cost pass-through", () => expect((requirement as any).cost).toBe(10)); test("partialDisplay exists", () => - requirement.partialDisplay != null && typeof requirement.partialDisplay === "function"); - test("display exists", () => - requirement.display != null && typeof requirement.display === "function"); - test("pay exists", () => requirement.pay != null && typeof requirement.pay === "function"); - test("requirementMet exists", () => - requirement.requirementMet != null && isRef(requirement.requirementMet)); - test("is visible", () => requirement.visibility === Visibility.Visible); - test("requires pay", () => requirement.requiresPay === true); + expect(typeof requirement.partialDisplay).toBe("function")); + test("display exists", () => expect(typeof requirement.display).toBe("function")); + test("pay exists", () => expect(typeof requirement.pay).toBe("function")); + test("requirementMet exists", () => { + expect(requirement.requirementMet).not.toBeNull(); + expect(isRef(requirement.requirementMet)).toBe(true); + }); + test("is visible", () => expect(requirement.visibility).toBe(Visibility.Visible)); + test("requires pay", () => expect(requirement.requiresPay).toBe(true)); // eslint-disable-next-line @typescript-eslint/no-explicit-any - test("spends resources", () => (requirement as any).spendResources !== false); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - test("does not buy max", () => (requirement as any).buyMax !== true); + test("spends resources", () => expect((requirement as any).spendResources).toBe(true)); + test("cannot maximize", () => expect(unref(requirement.canMaximize)).toBe(false)); }); describe("Fully customized", () => { @@ -56,7 +56,7 @@ describe("Creating cost requirement", () => { cost: 10, visibility: Visibility.None, requiresPay: false, - buyMax: true, + maximize: true, spendResources: false, // eslint-disable-next-line @typescript-eslint/no-empty-function pay() {} @@ -67,12 +67,12 @@ describe("Creating cost requirement", () => { requirement.pay != null && typeof requirement.pay === "function" && requirement.pay.length === 1); - test("is not visible", () => requirement.visibility === Visibility.None); - test("does not require pay", () => requirement.requiresPay === false); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - test("does not spend resources", () => (requirement as any).spendResources); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - test("buys max", () => (requirement as any).buyMax); + test("is not visible", () => expect(requirement.visibility).toBe(Visibility.None)); + test("does not require pay", () => expect(requirement.requiresPay).toBe(false)); + test("does not spend resources", () => + // eslint-disable-next-line @typescript-eslint/no-explicit-any + expect((requirement as any).spendResources).toBe(false)); + test("can maximize", () => expect(unref(requirement.canMaximize)).toBe(true)); }); test("Requirement met when meeting the cost", () => {