@reldens/modifiers Quick Reference
Fast lookup for the @reldens/modifiers package: usage patterns, operations, comparison operators, state and type constants, common mistakes and debugging tips.
Package Structure
- lib/modifier.js - Modifier class (apply, revert, conditions, limits).
- lib/calculator.js - math operations and their inverses.
- lib/condition.js - Condition class and comparison methods.
- lib/property-manager.js - deep property access.
- lib/constants.js - all constants (ModifierConst).
- tests/unit/ - unit tests; tests/fixtures/test-helpers.js - shared mocks.
Quick Patterns
Basic modifier
const { Modifier, Condition, ModifierConst } = require('@reldens/modifiers');
let mod = new Modifier({
key: 'attack-boost',
propertyKey: 'attack',
operation: ModifierConst.OPS.INC,
value: 20
});
mod.apply(target);
mod.revert(target);
With conditions
let condition = new Condition({
key: 'level-check',
propertyKey: 'level',
conditional: ModifierConst.COMPARE.GE,
value: 10
});
let mod = new Modifier({
key: 'bonus',
propertyKey: 'strength',
operation: ModifierConst.OPS.INC,
value: 25,
conditions: [condition]
});
Deep property access
let mod = new Modifier({
key: 'nested-boost',
propertyKey: 'stats/combat/attack',
operation: ModifierConst.OPS.INC,
value: 30
});
Base property operations
// read from maxHealth, apply to currentHealth
let mod = new Modifier({
key: 'percentage-heal',
propertyKey: 'currentHealth',
basePropertyKey: 'maxHealth',
operation: ModifierConst.OPS.INC_P,
value: 50
});
mod.apply(target, true, false);
With limits
let mod = new Modifier({
key: 'heal',
propertyKey: 'health',
operation: ModifierConst.OPS.INC,
value: 100,
minValue: 0,
maxProperty: 'maxHealth'
});
String values
let statusEffect = new Modifier({
key: 'poison-status',
propertyKey: 'status',
operation: ModifierConst.OPS.SET,
type: ModifierConst.TYPES.STRING,
value: 'poisoned'
});
Operations
ModifierConst.OPS, where a is the current value and b the modifier value:
- INC (1) - apply a + b, revert a - b. Flat increase.
- DEC (2) - apply a - b, revert a + b. Flat decrease.
- DIV (3) - apply a / b, revert a * b. Division.
- MUL (4) - apply a * b, revert a / b. Multiplication.
- INC_P (5) - apply a + round(a * b / 100), revert round(a / (1 + b / 100)). Percentage increase.
- DEC_P (6) - apply a - round(a * b / 100), revert round(a / (1 - b / 100)). Percentage decrease.
- SET (7) - apply sets b, revert sets false.
- METHOD (8) - custom method named by value, called with (modifier, currentValue).
- SET_N (9) - same as SET (alternative id).
Comparison Operators
ModifierConst.COMPARE, used as the Condition conditional:
- EQ = 'eq' - strict equal (===).
- NE = 'ne' - strict not equal (!==).
- LT = 'lt' - less than (<).
- GT = 'gt' - greater than (>).
- LE = 'le' - less than or equal (<=).
- GE = 'ge' - greater than or equal (>=).
State Constants
Values stored in modifier.state:
- MOD_MISSING_KEY = 'mk' - no key provided.
- MOD_MISSING_PROPERTY_KEY = 'mpk' - no propertyKey provided.
- MOD_MISSING_OPERATION = 'mo' - no operation provided.
- MOD_MISSING_VALUE = 'mv' - no value provided.
- MOD_READY = 'mre' - ready to apply.
- MOD_APPLIED = 'ma' - applied successfully.
- MOD_REVERTED = 'mr' - reverted successfully.
- MOD_UNDEFINED_TARGET = 'mut' - no target object.
- MOD_INVALID_CONDITIONS = 'mic' - conditions failed.
- MOD_MISSING_CONDITION_INSTANCE = 'mmci' - a condition is not a Condition instance.
- MOD_MODIFIER_ERROR = 'me' - modifier error (for example, METHOD without a valid method).
Type Constants
- ModifierConst.TYPES.INT = 'int' (default, values converted with Number()).
- ModifierConst.TYPES.STRING = 'string' (values converted with String()).
Base Property Combinations
Arguments apply(target, useBasePropertyToGetValue, applyOnBaseProperty):
- false, false - read propertyKey, write propertyKey. Normal modification.
- true, false - read basePropertyKey, write propertyKey. Percentage of max applied to current.
- false, true - read propertyKey, write basePropertyKey. Current to base.
- true, true - read basePropertyKey, write basePropertyKey. Base to base.
Limit Application Order
- minValue (if it is a number).
- maxValue (if it is a number).
- minProperty (if defined and the property value is not 0).
- maxProperty (if defined and the property value is not 0).
Example with a calculated value of 200: minValue: 0 leaves 200, maxValue: 150 clamps to 150, maxProperty: 'maxHealth' with a max health of 100 clamps again to 100.
PropertyManager Details
- Paths use / as separator: stats/combat/attack.
- extractChildPropertyOwner() returns the parent object of the final property, not its value: for stats/attack it returns player.stats.
- Reading a missing final property throws an error.
- Writing a missing final property creates it.
- Every intermediate segment must exist, both for reads and writes.
const { PropertyManager } = require('@reldens/modifiers');
let propertyManager = new PropertyManager();
propertyManager.getPropertyValue(player, 'stats/attack'); // throws if stats.attack does not exist
propertyManager.setOwnerProperty(player, 'stats/attack', 75); // creates stats.attack if missing
Common Mistakes
1. Not setting basePropertyKey
// WRONG - basePropertyKey defaults to propertyKey, so this reads currentHealth
modifier.apply(target, true, false);
// RIGHT - set basePropertyKey explicitly
let modifier = new Modifier({
key: 'percentage-heal',
propertyKey: 'currentHealth',
basePropertyKey: 'maxHealth',
operation: ModifierConst.OPS.INC_P,
value: 50
});
2. Property path typos
// WRONG - writing creates a NEW property stats.atk
propertyKey: 'stats/atk'
// RIGHT - use the existing property
propertyKey: 'stats/attack'
3. Type mismatches in conditions
// WRONG - player.level (number 10) === '10' (string) is false
type: ModifierConst.TYPES.STRING,
value: '10'
// RIGHT - matching types
type: ModifierConst.TYPES.INT,
value: 10
4. Forgetting to revert
// WRONG - the stats stay changed forever
modifier.apply(player);
// RIGHT - always pair apply and revert
modifier.apply(player);
// later, on unequip or when the effect ends:
modifier.revert(player);
5. METHOD declared on the prototype
The METHOD operation looks up the method as an own property of the modifier instance. Assign it in the constructor (this.myMethod = (modifier, currentValue) => ...) instead of declaring it only in the class body.
6. Numeric strings as limits
minValue and maxValue are only applied when they are numbers. Convert values coming from configuration before creating the modifier.
Debug Tips
Check the modifier state
const { ModifierConst } = require('@reldens/modifiers');
modifier.apply(target);
if(ModifierConst.MOD_APPLIED !== modifier.state){
console.log('Modifier not applied, state:', modifier.state);
}
Test a condition
const { PropertyManager } = require('@reldens/modifiers');
let currentValue = new PropertyManager().getPropertyValue(target, condition.propertyKey);
console.log('Valid:', condition.isValidOn(target));
console.log('Target value:', currentValue, 'Condition value:', condition.value, 'Comparison:', condition.conditional);
Preview a calculation without writing it
modifier.target = target;
// calculated value with the limits applied, the target is not modified:
console.log('Calculated:', modifier.getModifiedValue(false, false));
Testing Commands
# run all tests
npm test
# run the test files whose path contains the filter
npm test -- --filter=calculator
# watch mode
npm run test:watch
# coverage
npm run test:coverage
Dependencies and Integration
- Only dependency: @reldens/utils - ErrorManager (errors), Logger (logging) and sc Shortcuts (helpers).
- Used by @reldens/items-system - item stat modifiers on equip and use.
- Used by @reldens/skills - level modifiers, owner conditions, skill effects.
- Used by the Reldens platform - character stats, equipment, buffs and debuffs.
Important Reminders
- basePropertyKey defaults to propertyKey - set it explicitly when you need it.
- extractChildPropertyOwner() returns the parent object, not the property value.
- Reads validate the final property; writes create it when missing.
- All conditions must pass - a single failed condition stops the modifier.
- Limits are applied after the calculation and before the value is written.
- Percentage operations round the result to avoid decimal drift.
- modifier.state tracks the lifecycle - check it when debugging.
- Modifiers are reusable - the same instance can be applied to several targets.
Related Documentation
- @reldens/modifiers Architecture - Execution flow, limits, conditions and integration in detail.
- Level Modifiers Entity - Modifier fields configured per level.
- Item Entity - Item modifiers configuration.
- Stats Entity - The player stats modifiers usually target.
reldens