mirror of
https://github.com/RoY7x/worldbuilding.git
synced 2025-04-30 02:31:41 -04:00
9: Add formula (derived) attributes
- Added a new "Formula" attribute type for both actors and items. - When iterating through attributes, formula attributes are noted in an array so that they can be iterated through in a second pass to evaluate their referenced attributes. The second iteration just handles attribute replacement rather than rolling the formula, that's deferred until the attributes are used in an actual roll formula. - Items also have the same behavior. If an attribute isn't on the item, it will fall back to check the parent actor instead. - Likely areas with issues would be formula attributes that reference other formula attributes, or item formula attributes that reference actor formula attributes.
This commit is contained in:
parent
3e2a95f5f1
commit
a490448a15
3 changed files with 131 additions and 14 deletions
|
@ -21,7 +21,7 @@ export class SimpleActorSheet extends ActorSheet {
|
||||||
/** @override */
|
/** @override */
|
||||||
getData() {
|
getData() {
|
||||||
const data = super.getData();
|
const data = super.getData();
|
||||||
data.dtypes = ["String", "Number", "Boolean"];
|
data.dtypes = ["String", "Number", "Boolean", "Formula"];
|
||||||
for ( let attr of Object.values(data.data.attributes) ) {
|
for ( let attr of Object.values(data.data.attributes) ) {
|
||||||
attr.isCheckbox = attr.dtype === "Boolean";
|
attr.isCheckbox = attr.dtype === "Boolean";
|
||||||
}
|
}
|
||||||
|
@ -112,7 +112,7 @@ export class SimpleActorSheet extends ActorSheet {
|
||||||
obj[k] = v;
|
obj[k] = v;
|
||||||
return obj;
|
return obj;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
// Remove attributes which are no longer used
|
// Remove attributes which are no longer used
|
||||||
for ( let k of Object.keys(this.object.data.data.attributes) ) {
|
for ( let k of Object.keys(this.object.data.data.attributes) ) {
|
||||||
if ( !attributes.hasOwnProperty(k) ) attributes[`-=${k}`] = null;
|
if ( !attributes.hasOwnProperty(k) ) attributes[`-=${k}`] = null;
|
||||||
|
@ -123,7 +123,7 @@ export class SimpleActorSheet extends ActorSheet {
|
||||||
obj[e[0]] = e[1];
|
obj[e[0]] = e[1];
|
||||||
return obj;
|
return obj;
|
||||||
}, {_id: this.object._id, "data.attributes": attributes});
|
}, {_id: this.object._id, "data.attributes": attributes});
|
||||||
|
|
||||||
// Update the Actor
|
// Update the Actor
|
||||||
return this.object.update(formData);
|
return this.object.update(formData);
|
||||||
}
|
}
|
||||||
|
|
135
module/actor.js
135
module/actor.js
|
@ -8,28 +8,145 @@ export class SimpleActor extends Actor {
|
||||||
getRollData() {
|
getRollData() {
|
||||||
const data = super.getRollData();
|
const data = super.getRollData();
|
||||||
const shorthand = game.settings.get("worldbuilding", "macroShorthand");
|
const shorthand = game.settings.get("worldbuilding", "macroShorthand");
|
||||||
|
const formulaAttributes = [];
|
||||||
|
|
||||||
// Re-map all attributes onto the base roll data
|
// 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 ) {
|
if ( !!shorthand ) {
|
||||||
for ( let [k, v] of Object.entries(data.attributes) ) {
|
|
||||||
if ( !(k in data) ) data[k] = v.value;
|
|
||||||
}
|
|
||||||
delete data.attributes;
|
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
|
// Map all items data using their slugified names
|
||||||
data.items = this.data.items.reduce((obj, i) => {
|
data.items = this.data.items.reduce((obj, i) => {
|
||||||
let key = i.name.slugify({strict: true});
|
let key = i.name.slugify({strict: true});
|
||||||
let itemData = duplicate(i.data);
|
let itemData = duplicate(i.data);
|
||||||
if ( !!shorthand ) {
|
const itemAttributes = [];
|
||||||
for ( let [k, v] of Object.entries(itemData.attributes) ) {
|
|
||||||
if ( !(k in itemData) ) itemData[k] = v.value;
|
// 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
delete itemData["attributes"];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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;
|
obj[key] = itemData;
|
||||||
return obj;
|
return obj;
|
||||||
}, {});
|
}, {});
|
||||||
return data;
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ export class SimpleItemSheet extends ItemSheet {
|
||||||
/** @override */
|
/** @override */
|
||||||
getData() {
|
getData() {
|
||||||
const data = super.getData();
|
const data = super.getData();
|
||||||
data.dtypes = ["String", "Number", "Boolean"];
|
data.dtypes = ["String", "Number", "Boolean", "Formula"];
|
||||||
for ( let attr of Object.values(data.data.attributes) ) {
|
for ( let attr of Object.values(data.data.attributes) ) {
|
||||||
attr.isCheckbox = attr.dtype === "Boolean";
|
attr.isCheckbox = attr.dtype === "Boolean";
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@ export class SimpleItemSheet extends ItemSheet {
|
||||||
obj[k] = v;
|
obj[k] = v;
|
||||||
return obj;
|
return obj;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
// Remove attributes which are no longer used
|
// Remove attributes which are no longer used
|
||||||
for ( let k of Object.keys(this.object.data.data.attributes) ) {
|
for ( let k of Object.keys(this.object.data.data.attributes) ) {
|
||||||
if ( !attributes.hasOwnProperty(k) ) attributes[`-=${k}`] = null;
|
if ( !attributes.hasOwnProperty(k) ) attributes[`-=${k}`] = null;
|
||||||
|
|
Loading…
Add table
Reference in a new issue