Change buyMax to maximize on repeatables, and make requirements report ability to maximize

Also fixes up some of the requirements tests that weren't actually asserting
This commit is contained in:
thepaperpilot 2023-02-14 22:29:31 -06:00
parent 4fb2d90dbb
commit dcb3bc949d
2 changed files with 37 additions and 39 deletions

View file

@ -11,7 +11,12 @@ import {
import { createLazyProxy } from "util/proxies"; import { createLazyProxy } from "util/proxies";
import { joinJSX, renderJSX } from "util/vue"; import { joinJSX, renderJSX } from "util/vue";
import { computed, unref } from "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. * 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<boolean>; requiresPay: ProcessedComputable<boolean>;
/** /**
* 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<boolean>; canMaximize?: ProcessedComputable<boolean>;
/** /**
* Perform any effects to the game state that should happen when the requirement gets triggered. * 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. * @param amount The amount of levels of requirements to pay for.
@ -61,7 +66,7 @@ export interface CostRequirementOptions {
*/ */
resource: Resource; 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<DecimalSource> | GenericFormula; cost: Computable<DecimalSource> | 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}. * 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<boolean>; requiresPay?: Computable<boolean>;
/**
* Pass-through to {@link Requirement.buyMax}.
* @see {@link cost} for restrictions on buying max support.
*/
buyMax?: Computable<boolean>;
/** /**
* 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. * 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} * @see {Formula}
*/ */
spendResources?: Computable<boolean>; spendResources?: Computable<boolean>;
/** /**
* Pass-through to {@link Requirement.pay}. May be required for buying max support. * Pass-through to {@link Requirement.pay}. May be required for maximizing support.
* @see {@link cost} for restrictions on buying max support. * @see {@link cost} for restrictions on maximizing support.
*/ */
pay?: (amount?: DecimalSource) => void; pay?: (amount?: DecimalSource) => void;
} }
@ -159,16 +159,12 @@ export function createCostRequirement<T extends CostRequirementOptions>(
: unref(req.cost as ProcessedComputable<DecimalSource>); : unref(req.cost as ProcessedComputable<DecimalSource>);
req.resource.value = Decimal.sub(req.resource.value, cost).max(0); req.resource.value = Decimal.sub(req.resource.value, cost).max(0);
}); });
processComputable(req as T, "buyMax");
if ( req.canMaximize = req.cost instanceof Formula && req.cost.isInvertible();
"buyMax" in req &&
req.buyMax !== false && if (req.canMaximize) {
req.cost instanceof Formula &&
req.cost.isInvertible()
) {
req.requirementMet = calculateMaxAffordable( req.requirementMet = calculateMaxAffordable(
req.cost, req.cost as InvertibleFormula,
req.resource, req.resource,
unref(req.spendResources as ProcessedComputable<boolean> | undefined) ?? true unref(req.spendResources as ProcessedComputable<boolean> | undefined) ?? true
); );
@ -244,6 +240,8 @@ export function maxRequirementsMet(requirements: Requirements): DecimalSource {
const reqsMet = unref(requirements.requirementMet); const reqsMet = unref(requirements.requirementMet);
if (typeof reqsMet === "boolean") { if (typeof reqsMet === "boolean") {
return reqsMet ? Infinity : 0; return reqsMet ? Infinity : 0;
} else if (Decimal.gt(reqsMet, 1) && unref(requirements.canMaximize) !== true) {
return 1;
} }
return reqsMet; return reqsMet;
} }

View file

@ -27,23 +27,23 @@ describe("Creating cost requirement", () => {
}); });
// eslint-disable-next-line @typescript-eslint/no-explicit-any // 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 // 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", () => test("partialDisplay exists", () =>
requirement.partialDisplay != null && typeof requirement.partialDisplay === "function"); expect(typeof requirement.partialDisplay).toBe("function"));
test("display exists", () => test("display exists", () => expect(typeof requirement.display).toBe("function"));
requirement.display != null && typeof requirement.display === "function"); test("pay exists", () => expect(typeof requirement.pay).toBe("function"));
test("pay exists", () => requirement.pay != null && typeof requirement.pay === "function"); test("requirementMet exists", () => {
test("requirementMet exists", () => expect(requirement.requirementMet).not.toBeNull();
requirement.requirementMet != null && isRef(requirement.requirementMet)); expect(isRef(requirement.requirementMet)).toBe(true);
test("is visible", () => requirement.visibility === Visibility.Visible); });
test("requires pay", () => requirement.requiresPay === 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 // eslint-disable-next-line @typescript-eslint/no-explicit-any
test("spends resources", () => (requirement as any).spendResources !== false); test("spends resources", () => expect((requirement as any).spendResources).toBe(true));
// eslint-disable-next-line @typescript-eslint/no-explicit-any test("cannot maximize", () => expect(unref(requirement.canMaximize)).toBe(false));
test("does not buy max", () => (requirement as any).buyMax !== true);
}); });
describe("Fully customized", () => { describe("Fully customized", () => {
@ -56,7 +56,7 @@ describe("Creating cost requirement", () => {
cost: 10, cost: 10,
visibility: Visibility.None, visibility: Visibility.None,
requiresPay: false, requiresPay: false,
buyMax: true, maximize: true,
spendResources: false, spendResources: false,
// eslint-disable-next-line @typescript-eslint/no-empty-function // eslint-disable-next-line @typescript-eslint/no-empty-function
pay() {} pay() {}
@ -67,12 +67,12 @@ describe("Creating cost requirement", () => {
requirement.pay != null && requirement.pay != null &&
typeof requirement.pay === "function" && typeof requirement.pay === "function" &&
requirement.pay.length === 1); requirement.pay.length === 1);
test("is not visible", () => requirement.visibility === Visibility.None); test("is not visible", () => expect(requirement.visibility).toBe(Visibility.None));
test("does not require pay", () => requirement.requiresPay === false); test("does not require pay", () => expect(requirement.requiresPay).toBe(false));
// eslint-disable-next-line @typescript-eslint/no-explicit-any test("does not spend resources", () =>
test("does not spend resources", () => (requirement as any).spendResources); // eslint-disable-next-line @typescript-eslint/no-explicit-any
// eslint-disable-next-line @typescript-eslint/no-explicit-any expect((requirement as any).spendResources).toBe(false));
test("buys max", () => (requirement as any).buyMax); test("can maximize", () => expect(unref(requirement.canMaximize)).toBe(true));
}); });
test("Requirement met when meeting the cost", () => { test("Requirement met when meeting the cost", () => {