'use strict';
/**
* Representation of an "Attribute" which is any value that has a base amount and depleted/restored
* safely. Where safely means without being destructive to the base value.
*
* An attribute on its own cannot be raised above its base value. To raise attributes above their
* base temporarily see the {@link http://ranviermud.com/extending/effects|Effect guide}.
*
* @property {string} name
* @property {number} base
* @property {number} delta Current difference from the base
* @property {AttributeFormula} formula
* @property {object} metadata any custom info for this attribute
*/
class Attribute {
/**
* @param {string} name
* @param {number} base
* @param {number} delta=0
* @param {AttributeFormula} formula=null
* @param {object} metadata={}
*/
constructor(name, base, delta = 0, formula = null, metadata = {}) {
if (isNaN(base)) {
throw new TypeError(`Base attribute must be a number, got ${base}.`);
}
if (isNaN(delta)) {
throw new TypeError(`Attribute delta must be a number, got ${delta}.`);
}
if (formula && !(formula instanceof AttributeFormula)) {
throw new TypeError('Attribute formula must be instance of AttributeFormula');
}
this.name = name;
this.base = base;
this.delta = delta;
this.formula = formula;
this.metadata = metadata;
}
/**
* Lower current value
* @param {number} amount
*/
lower(amount) {
this.raise(-amount);
}
/**
* Raise current value
* @param {number} amount
*/
raise(amount) {
const newDelta = Math.min(this.delta + amount, 0);
this.delta = newDelta;
}
/**
* Change the base value
* @param {number} amount
*/
setBase(amount) {
this.base = Math.max(amount, 0);
}
/**
* Bypass raise/lower, directly setting the delta
* @param {amount}
*/
setDelta(amount) {
this.delta = Math.min(amount, 0);
}
serialize() {
const { delta, base } = this;
return { delta, base };
}
}
/**
* @property {Array<string>} requires Array of attributes required for this formula to run
* @property {function (...number) : number} formula
*/
class AttributeFormula
{
constructor(requires, fn) {
if (!Array.isArray(requires)) {
throw new TypeError('requires not an array');
}
if (typeof fn !== 'function') {
throw new TypeError('Formula function is not a function');
}
this.requires = requires;
this.formula = fn;
}
evaluate(attribute, ...args) {
if (typeof this.formula !== 'function') {
throw new Error(`Formula is not callable ${this.formula}`);
return;
}
return this.formula.bind(attribute)(...args);
}
}
module.exports = {
Attribute,
AttributeFormula,
};