question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Computed Attributes RFC

See original GitHub issue

Scenario

I have a health stat. I want equipment to be able to increase this stat by a static amount (+5 health) or by a percentage (+10% health)

Current Solution

You can currently accomplish the intended outcome by having two stats: health and health_percent the static bonus would apply to health then +10% health would be a static +10 to health_percent. You would then create two helper methods somewhere in your bundles to get the intended value:

/*
 * Example:
 * Base health 100
 * I equip a helm with +5 health
 * I wear a ring with +10% health
 *   115 = (100 + 5) * (1 + 10 / 100)
 */
function getEffectiveMaxHealth(player) {
  // default getMaxAttribute automatically calculates baseHealth + staticHealthBonus
  const health = player.getMaxAttribute('health');
  // same for health_percent
  const health_perc = player.getMaxAttribute('health_percent');
  return Math.floor(health * (1 + (health_perc / 100));
}

function getEffectiveHealth(player) {
  const max = getEffectMaxHealth(player);
  const delta = player.attributes.get('health').delta;
  return max + delta;
}

Now this isn’t the end of the world but you’d have to import these functions anywhere you wanted to get the effective health of the player. I think there may be a better way.

Proposal

It would be really cool if you could say something like this:

// glaring flaw with this approach explained in the Hurdles section

Attribute({
  name: 'health_percent',
  base: 0,
});

Attribute({
  name: 'health',
  base: 100,
  computed: {
    requires: ['health_percent'],
    formula: function (health, healthPercent) {
      return Math.floor(health * (1 + healthPercent / 100));
    }
  }
})

And then when you called player.getMaxAttribute('health') it would do something like:

Character.getMaxAttribute(attr, formulate = false) {
  const attribute = this.attributes.get(attr);
  const effectiveValue = this.effects.evaluateAttribute(attribute);

  if (!attribute.isComputed() || !formulate) {
    return effectiveValue;
  }

  const { computed } = attribute;
  // possibility of stack overflow here if there's a circular dependency
  const deps = computed.requires.map(
    reqAttr => this.getMaxAttribute(reqAttr, true)
  );

  return computed.formula.apply(null, [effectiveValue, ...deps]);
}

Character.getAttribute(attr, formulate = false) {
  const attribute = this.attributes.get(attr);
  return this.getMaxAttribute(attr, formulate) + attribute.delta;
}

Let’s say the player has base 100 health, is wearing a Helm of +5 Health, and just used a skill that increases max health by 10% so his attributes would look like this:

health: 100 base, with effects 105
health_percent: 0 base, with effects 10

player.getMaxAttribute('health');
// 105, didn't formulate

player.getMaxAttribute('health', true);
// 115, added +10% to 105

let's say they take 20 points of damage

health: 100 base, -20 delta, with effects 105
health_percent: same

player.getAttribute('health');
// 85 = 100 + 5 - 20

player.getAttribute('health', true);
// 95 = floor(((100 + 5) * 1.1) - 20)

Hurdles

Attributes right now are a hydrated property meaning that when the player is saved and reloaded we have to instantiate the original attribute object with the appropriate values. Currently that’s trivial, attributes consist of 3 scalar properties: name, base, delta.

So how would the developer specify this formula, where would they specify it, how would it get saved, and how would it get loaded?

Possible solutions

Maybe there is an attributes definition file similar to the channels definition file and all Character’s in the game (NPCs/Players) can only use attributes from that? What are the pros/cons there?

… that’s it, I have no idea how else we could solve that.

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:9 (9 by maintainers)

github_iconTop GitHub Comments

1reaction
shawncpluscommented, Nov 7, 2018

An added benefit of the attribute definition file is that I would be able to detect circularly dependent computed attributes at server startup, effectively at “compile” time" instead of having shit hit the fan while the game as running.

1reaction
seanohuecommented, Nov 7, 2018

I like the idea of having the attributes definition file be the source of truth. It would be relatively simple and match up with other filesystem-based customization such as player-events and so on.

It could even just be a simple map of attribute names to formulas. Honestly, the rest of the attributes is serializable as is. The downside to that is that the attribute information would then be in two places.

I guess what I am saying is that my vote is for attributes to be in a separate definition file, and that it should define the whole thing. This would also be more extensible.

After all, as it is, I think they are defined in the middle of an input bundle, which is a bit odd.

Read more comments on GitHub >

github_iconTop Results From Across the Web

RFC 3852: Cryptographic Message Syntax (CMS) - IETF
The message digest is computed on either the content being signed or the content together with the signed attributes using the process described...
Read more >
17 Managing Computed Attributes
This chapter describes computed attributes and how to manage computed attributes by configuring the ... A standard LDAP URI, as defined in RFC...
Read more >
RFC 5792: PA-TNC: A Posture Attribute (PA) Protocol ...
1 Attribute Request Contains a list of attribute type values defining the attributes desired from the Posture Collectors. · 2 Product Information Manufacturer ......
Read more >
Calculated Attributes | Haiku Project - Haiku OS
Extending the OpenBFS/BFS via the usage of calculated attributes for arbitrary files and file types would provide a useful means of dynamic and...
Read more >
No support for RFC 4530 entryUUID attribute #137 - GitHub
The ticket is about having support of RFC 4530 to creates entryUUID operational attribute and define syntax and matchingRules. Support of this ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found