RanvierMUD/core

Cannot bring a attribute with formula below it's formulated result via modifiers.

Closed this issue · 1 comments

In Character.js#getMaxAttribute

getMaxAttribute(attr) {
    if (!this.hasAttribute(attr)) {
      throw new RangeError(`Character does not have attribute [${attr}]`);
    }

    const attribute = this.attributes.get(attr);
    const currentVal = this.effects.evaluateAttribute(attribute);

    if (!attribute.formula) {
      return currentVal;
    }

    const { formula } = attribute;

    const requiredValues = formula.requires.map(
      reqAttr => this.getMaxAttribute(reqAttr)
    );

    return formula.evaluate.apply(formula, [attribute, this, currentVal, ...requiredValues]);
  }

evaluateAttribute (where modifiers are applied) is happening before the formula calculation. Coincidently, an attribute's value can never be lowered below the level calculated by the formula when using modifiers. For instance, having a calculated attribute called dodge, that requires reflexes, dexterity. With a certain set of stats, it has a base value of 7. If you were to apply this modifier on a stun effect to it.

      attributes: {
          dodge: function(current){
            return current * 0;
          }

      }

getMaxAttribute('dodge') will still return a value of 7. Seems like it would be better to calculate the formula first, and then evaluateAttribute? Maybe there's repercussions of that I'm not seeing?

Let's assume we have a computed "speed" attribute with the formula: (dex * 2).

Evaluate first:

dex: 5
dex effect of +2 dex
evaluate:  (5 + 2) = 7
formulate: (7 * 2) = 14

Formulate first:

dex: 5
dex effect of +2 dex
formulate: (5 * 2)  = 10
evaluate:  (10 + 2) = 12

That's the difference. But it's kind of irrelevant to the actual heart of the problem.

Using an effect like stunned to try to zero out a value is a dangerous game. Effects do not have priorities which means that even if your stunned effect modifier worked to zero out the value another effect could come along and just add +10 to it. Which, if the character is stunned, doesn't make much sense that they're stunned but still have 10 dodge.

Stunned in this case is a modal state which should be its own separate attribute and its on/off state should be used in the dodge formula. Then you can do something like:

fn: function (character, dodge, dexterity, stunned) {
  return stunned ? 0 : (dexterity * 2) + dodge;
}

Attribute formulas are supposed to be ran after effects are evaluated for the reason that a computed attribute is defined by its formula, the formula is the ultimate source of truth on the value.

I'd say the real takeaway from this is that the attribute documentation needs a section on modal character states like stunned. And also making it absolutely clear that effect attribute modifiers should not use multiplication for this exact reason.