Skip to content

Cost curves

Cost curves price one or more resources from an owned count.

ts
import { costCurve, flat, geometric } from "@idlekitjs/economy/cost-curves";

Lines

flat(resourceId, amount, options?) creates a fixed per-unit price.

ts
flat("currency:coins", 10);

geometric(resourceId, { baseAmount, growth, round? }) creates a per-unit price that grows with the owned count.

ts
geometric("currency:coins", {
  baseAmount: 100,
  growth: 1.15,
  round: "ceil",
});

growth === 1 is linear. Invalid growth and negative amounts throw when the line or curve is created.

Curve

ts
const labCost = costCurve<State>({
  getOwned: (state) => state.labsOwned,
  lines: [
    flat("currency:gems", 1),
    geometric("currency:coins", { baseAmount: 100, growth: 1.15, round: "ceil" }),
  ],
});

The curve exposes three methods:

MethodBehavior
costFor(state, quantity)Total cost for quantity consecutive units
next(state)Cost for the next single unit
maxAffordable(state, economy)Largest whole quantity affordable right now

costFor floors quantity. Non-finite or non-positive quantities return an empty cost.

maxAffordable reads spendable balances through the economy. Spendable means the resource balance minus its min.

Rounding

round is optional and belongs to each curve line:

ts
type Rounding = "none" | "floor" | "ceil" | "round";

Rounding applies to each unit price before summing. This keeps the displayed unit price and the total for a bulk purchase consistent.

Rounded decreasing geometric curves (growth < 1 with per-unit rounding) throw when the curve is created.

Transaction usage

ts
const quantity = labCost.maxAffordable(state, economy);

const result = economy.execute(state, {
  id: "buy:labs",
  cost: labCost.costFor(state, quantity),
  apply: (state) => {
    state.labsOwned += quantity;
  },
});

The curve prices the action. The transaction settles it.