Skip to main content

Operations API

The operations module provides built-in operations and utilities for creating custom operations that modify item metrics.

Built-in Operations

sumOperation

Adds a value to the current metric.

function sumOperation<C extends ConfigSpec>(
current: number,
value: number,
context: EvalContext<C>
): number;

Formula: current + value

subtractOperation

Subtracts a value from the current metric.

function subtractOperation<C extends ConfigSpec>(
current: number,
value: number,
context: EvalContext<C>
): number;

Formula: current - value

multiplyOperation

Multiplies the current metric by a value.

function multiplyOperation<C extends ConfigSpec>(
current: number,
value: number,
context: EvalContext<C>
): number;

Formula: current * value

Operation Utilities

builtinOps

Type-safe helper for declaring built-in operations in configuration.

function builtinOps<T extends BuiltinOperation>(...ops: T[]): readonly T[];

Example

import { defineConfig, builtinOps } from "mod-engine";

const config = defineConfig({
metrics: ["Health", "Damage"] as const,
operations: builtinOps("sum", "multiply"), // Type-safe
attributes: [] as const,
});

createBuiltInOperations

Creates a map of built-in operations along with precedence values.

function createBuiltInOperations<C extends ConfigSpec>(): Map<
string,
OperationInfo<C>
>;

validateNumericResult

Validates that an operation result is a valid number.

function validateNumericResult(value: number, operationName: string): number;

Throws

OperationError if the value is NaN or Infinity.

Custom Operations

OperationImpl Type

Interface for implementing custom operations.

type OperationImpl<C extends ConfigSpec> = (
currentValue: number,
modifierValue: number,
context: EvalContext<C>
) => number;

Parameters

  • currentValue - Current metric value
  • modifierValue - Value from the modifier
  • context - Evaluation context with attributes and item

Example Custom Operations

Power Operation

const powOperation: OperationImpl<Config> = (current, value) => {
return Math.pow(current, value);
};

engine.registerOperation("pow", powOperation);

Clamp Operation

const clampOperation: OperationImpl<Config> = (current, value) => {
return Math.min(Math.max(current, 0), value);
};

engine.registerOperation("clamp", clampOperation);

Context-Aware Operation

const levelScaledAdd: OperationImpl<Config> = (current, value, context) => {
const level = context.attributes.Level ?? 1;
const scaledValue = value * (1 + level / 100);
return current + scaledValue;
};

engine.registerOperation("levelAdd", levelScaledAdd);

OperationInfo Structure

Operations are stored with metadata:

interface OperationInfo<C extends ConfigSpec> {
impl: OperationImpl<C>;
precedence: number;
}

Precedence System

Operations with higher precedence are applied later:

// Applied in order: sum (0), multiply (0), pow (100)
engine.registerOperation("pow", powOp, { precedence: 100 });

Default precedence for built-ins:

  • sum: 10
  • subtract: 10
  • multiply: 20

Registration Methods

Engine Registration

engine.registerOperation("customOp", impl, { precedence: 50 });

Engine Builder Registration

const engine = createEngineBuilder(config)
.withOperation("pow", powOperation)
.withOperations({
min: { impl: minOperation, precedence: 10 },
max: { impl: maxOperation, precedence: 10 },
})
.build();

Advanced Patterns

Mathematical Operations

const mathOperations = {
pow: (current, value) => Math.pow(current, value),
sqrt: (current, value) => Math.sqrt(current + value),
log: (current, value) => Math.log(Math.max(current + value, 1)),
abs: (current, value) => Math.abs(current + value),
round: (current, value) => Math.round(current + value),
};

// Register all at once
Object.entries(mathOperations).forEach(([name, impl]) => {
engine.registerOperation(name, impl);
});

Conditional Operations

const conditionalBonus: OperationImpl<Config> = (current, value, context) => {
const isEnchanted = context.attributes.Enchanted;
const isRare = ["Rare", "Epic", "Legendary"].includes(
context.attributes.Rarity
);

let bonus = value;
if (isEnchanted) bonus *= 1.5;
if (isRare) bonus *= 1.2;

return current + bonus;
};

Bounded Operations

const boundedAdd: OperationImpl<Config> = (current, value, context) => {
const maxValue = context.attributes.MaxHealth ?? 1000;
return Math.min(current + value, maxValue);
};

const boundedMultiply: OperationImpl<Config> = (current, value, context) => {
const result = current * value;
const cap = context.attributes.CapMultiplier ?? 10;
return Math.min(result, current * cap);
};

Percentage Operations

const percentageIncrease: OperationImpl<Config> = (current, percentage) => {
return current * (1 + percentage / 100);
};

const percentageDecrease: OperationImpl<Config> = (current, percentage) => {
return current * (1 - percentage / 100);
};

Error Handling

OperationError

Thrown when operations produce invalid results:

import { OperationError } from "mod-engine";

const safeOperation: OperationImpl<Config> = (current, value) => {
const result = current / value;

if (!isFinite(result)) {
throw new OperationError(`Division by ${value} produced invalid result`);
}

return result;
};

Validation

const validatedOperation: OperationImpl<Config> = (current, value) => {
if (value < 0) {
throw new OperationError("Negative values not allowed");
}

const result = current + value;
return validateNumericResult(result, "customAdd");
};

Type Safety

Operations are fully type-safe:

// ✅ Valid - operation in config
engine.registerOperation("declaredOp", impl);

// ❌ TypeScript error - operation not declared
engine.registerOperation("undeclaredOp", impl);

// ✅ Type-safe context access
const contextOp: OperationImpl<Config> = (current, value, context) => {
// context.attributes is fully typed
const level: number = context.attributes.Level;
return current + value * level;
};

Performance Tips

Efficient Operations

// ✅ Fast - simple arithmetic
const fastOp = (current, value) => current * value;

// ❌ Slow - complex calculations
const slowOp = (current, value, context) => {
return Math.pow(Math.sin(current), Math.cos(value));
};

Caching Context Values

const cachedOp: OperationImpl<Config> = (current, value, context) => {
// Cache expensive lookups
const multiplier =
context.cache?.multiplier ??
(context.cache = { multiplier: calculateMultiplier(context) }).multiplier;

return current + value * multiplier;
};

Context added by Giga metric-operations - using core operation types, operation registration, and stacking behavior information.