worldbuilding/module/actor.js
Matt Smith 00dfec6014 8: Refactor formula attributes
- Divided getRollData() into several helper methods to make it more
  organized.
- Cleaned up usage of shorthand checks, but haven't entirely
  consolidated them yet. There's room to improve this, but further
  consolidation would likely require a regex to handle mapping inline
  roll syntax in the formulas to use the `@attr.KEY.value` format when
  `@KEY` is detected. This seemed like a solid compromise for now.
- Refactored the _replaceData() helper method to match the Roll class'
  upcoming syntax, and added TODO comments to replace them once it's
  ready.
2020-08-09 15:30:04 -05:00

152 lines
5.6 KiB
JavaScript

/**
* Extend the base Actor entity by defining a custom roll data structure which is ideal for the Simple system.
* @extends {Actor}
*/
export class SimpleActor extends Actor {
/** @override */
getRollData() {
const data = super.getRollData();
const shorthand = game.settings.get("worldbuilding", "macroShorthand");
const formulaAttributes = [];
// Handle formula attributes when the short syntax is disabled.
this._applyShorthand(data, formulaAttributes, shorthand);
// Map all items data using their slugified names
this._applyItems(data, shorthand);
// Evaluate formula attributes after all other attributes have been handled,
// including items.
this._applyFormulaReplacements(data, formulaAttributes, shorthand);
// Remove the attributes if necessary.
if ( !!shorthand ) {
delete data.attributes;
delete data.attr;
delete data.abil;
}
return data;
}
/**
* Apply shorthand syntax to actor roll data.
* @param {Object} data The actor's data object.
* @param {Array} formulaAttributes Array of attributes that are derived formulas.
* @param {Boolean} shorthand Whether or not the shorthand syntax is used.
*/
_applyShorthand(data, formulaAttributes, shorthand) {
// Handle formula attributes when the short syntax is disabled.
for ( let [k, v] of Object.entries(data.attributes) ) {
// Make an array of formula attributes for later reference.
if ( v.dtype == "Formula" ) formulaAttributes.push(k);
// Add shortened version of the attributes.
if ( !!shorthand ) {
if ( !(k in data) ) {
data[k] = v.value;
}
}
}
}
/**
* Add items to the actor roll data object. Handles regular and shorthand
* syntax, and calculates derived formula attributes on the items.
* @param {Object} data The actor's data object.
* @param {Boolean} shorthand Whether or not the shorthand syntax is used.
*/
_applyItems(data, shorthand) {
// Map all items data using their slugified names
data.items = this.data.items.reduce((obj, i) => {
let key = i.name.slugify({strict: true});
let itemData = duplicate(i.data);
const itemAttributes = [];
// Add items to shorthand and note which ones are formula attributes.
for ( let [k, v] of Object.entries(itemData.attributes) ) {
if ( v.dtype == "Formula" ) itemAttributes.push(k);
// Add shortened version of the attributes.
if ( !!shorthand ) {
if ( !(k in itemData) ) {
itemData[k] = v.value;
}
}
}
// Evaluate formula attributes after all other attributes have been handled.
for ( let k of itemAttributes ) {
if ( itemData.attributes[k].value ) {
itemData.attributes[k].value = this._replaceData(itemData.attributes[k].value, itemData);
itemData.attributes[k].value = this._replaceData(itemData.attributes[k].value, data, {missing: "0"});
// TODO: Replace with:
// itemData.attributes[k].value = Roll.replaceFormulaData(itemData.attributes[k].value, itemData);
// itemData.attributes[k].value = Roll.replaceFormulaData(itemData.attributes[k].value, data, {missing: "0"});
}
// Duplicate values to shorthand.
if ( !!shorthand ) {
itemData[k] = itemData.attributes[k].value;
}
}
// Delete the original attributes key if using the shorthand syntax.
if ( !!shorthand ) {
delete itemData.attributes;
}
obj[key] = itemData;
return obj;
}, {});
}
/**
* Apply replacements for derived formula attributes.
* @param {Object} data The actor's data object.
* @param {Array} formulaAttributes Array of attributes that are derived formulas.
* @param {Boolean} shorthand Whether or not the shorthand syntax is used.
*/
_applyFormulaReplacements(data, formulaAttributes, shorthand) {
// Evaluate formula attributes after all other attributes have been handled,
// including items.
for ( let k of formulaAttributes ) {
if ( data.attributes[k].value ) {
data.attributes[k].value = this._replaceData(data.attributes[k].value, data, {missing: "0"});
// TODO: Replace with:
// data.attributes[k].value = Roll.replaceFormulaData(data.attributes[k].value, data, {missing: "0"});
}
// Duplicate values to shorthand.
if ( !!shorthand ) {
data[k] = data.attributes[k].value;
}
}
}
/**
* Replace referenced data attributes in the roll formula with the syntax `@attr` with the corresponding key from
* the provided `data` object. This is a temporary helper function that will be replaced with Roll.replaceFormulaData()
* in Foundry 0.7.1.
*
* @param {String} formula The original formula within which to replace.
* @param {Object} data Data object to use for value replacements.
* @param {Object} missing Value to use as missing replacements, such as {missing: "0"}.
* @return {String} The formula with attributes replaced with values.
*/
_replaceData(formula, data, {missing=null}={}) {
// Exit early if the formula is invalid.
if ( typeof formula != "string" ) {
return 0;
}
// Replace attributes with their numeric equivalents.
let dataRgx = new RegExp(/@([a-z.0-9_\-]+)/gi);
let rollFormula = formula.replace(dataRgx, (match, term) => {
// Replace matches with the value, or the missing value.
let value = getProperty(data, term);
return value ? String(value).trim() : (missing != null ? missing : `@${term}`);
});
return rollFormula;
}
}