mardi 5 avril 2016

Building complex calculated model in Javascript

I'm working on a tabletop RPG character builder as a hobby/to expand my skillset. Part of the challenge is the complex model needed to represent the character's stats, feats, attacks, etc., most of which can affect each other.

For example, given the simplified character below, it's easy to calculate what the "attack" value should be.

character = {
   strength: 14,
   dexterity: 16,
   base_attack_bonus: 3,
   attack: function() { return this.strength + this.base_attack_bonus; }
}

However, to be complete, the character builder has to account for feats like "Weapon Finesse," which make a character's attacks use dexterity instead of strength to calculate the attack value.

I began this project when Object.observe() was still in the future for Javascript, so for maximum flexibility, I opted for an event-driven web of dependencies (with precautions to prevent loops). I created a "Data" object which could depend() its properties on the properties of another object.

Something like this:

character = new Data({
   strength: 14,
   dexterity: 16,
   attack_modifier: 0,
   base_attack_bonus: 3,
   attack: function() { return this.attack_modifier + this.base_attack_bonus; }
});
character.depend("attack_modifier", character, "strength")
// Weapon Finesse feat rules:
character.undepend("attack_modifier", character, "strength")
character.depend("attack_modifier", character, "dexterity")

If the strength or dexterity scores changed at some later point, the attack_modifier property that depended on them would be updated instantaneously (thanks to Object.observe()) as well, trickling on down the chain. If a property is depend()ed on more than one property, by default, it updates to the sum of all its dependencies - but the update() function is customizable, so you could return a list of all its dependencies, or the largest, etc.

This lets me create complex unordered dependency chains, and yet allows feats and abilities to be created after the fact to change nearly any aspect of the character without altering the model's code. Theoretically, it should also improve performance over recalculating every part of the model when something changes.

I'm now weighing my options since Object.observe() and Array.observe() have been outmoded. I could use a shim and keep going. But if I'm revisiting the situation anyway, I thought I'd cast around and see if there's a better design pattern for this kind of work.

I guess there are two questions here:

  1. Is it better (for the above case) to use an Object.observe() and Array.observe() shim; to develop my own Observer implementation; or to use another third-party option?
  2. Is the dependency web described above a reasonable design pattern for a complex yet flexible model like this, or am I making things way too difficult for myself? Is there a better way?

Aucun commentaire:

Enregistrer un commentaire