Files
roll20-character-sheets/rapidfire_system/scaffold/attribute_proxy.js
T

152 lines
5.4 KiB
JavaScript

/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
/*jshint -W014,-W084,-W030,-W033*/
//# Attribute Obj Proxy handler
const createAttrProxy = function(attrs){
//creates a proxy for the attributes object so that values can be worked with more easily.
const getCascObj = function(event,casc){
let typePrefix = event.htmlAttributes ? 'act_' : 'attr_';
let cascName = `${typePrefix}${event.sourceAttribute}`;
let cascObj = casc[cascName];
return cascObj;
};
const triggerFunctions = function(obj,attributes,sections){
if(obj.triggeredFuncs && obj.triggeredFuncs.length){
debug(`triggering functions for ${obj.name}`);
obj.triggeredFuncs && obj.triggeredFuncs.forEach(func=>funcs[func] ?
funcs[func]({trigger:obj,attributes,sections}) :
debug(`!!!Warning!!! no function named ${func} found. Triggered function not called for ${obj.name}`,true));
}
};
const initialFunction = function(obj,attributes,sections){
if(obj.initialFunc){
debug(`initial functions for ${obj.name}`);
funcs[obj.initialFunc] ?
funcs[obj.initialFunc]({trigger:obj,attributes,sections}) :
debug(`!!!Warning!!! no function named ${obj.initialFunc} found. Initial function not called for ${obj.name}`,true);
}
};
const processChange = function({event,trigger,attributes,sections,casc}){
debug({trigger});
if(event && !trigger){
debug('initial change detected. No trigger found');
return;
}
if(!attributes || !sections || !casc){
debug(`!!! Insufficient arguments || attributes > ${!!attributes} | sections > ${!!sections} | casc > ${!!casc} !!!`);
return;
}
//store the queue in attributes.
if(event){
debug('checking for initial functions');
initialFunction(trigger,attributes,sections);//functions that should only be run if the attribute was the thing changed by the user
attributes.queue = [...trigger.affects];
}
if(trigger){
debug(`processing ${trigger.name}`);
triggerFunctions(trigger,attributes,sections);
if(!event && trigger.calculation){
attributes[trigger.name] = funcs[trigger.calculation]({trigger,attributes,sections});
}
}
if(event || attributes.updates.hasOwnProperty(trigger.name)){
attributes.queue.push(...trigger.affects);
}
attributes.set({attributes,sections,casc});
};
const attrTarget = {
updates:{},
attributes:{...attrs},
repOrders:{},
queue: [],
casc:{},
processChange,
triggerFunctions,
initialFunction,
getCascObj
};
const attrHandler = {
get:function(obj,prop){//gets the most value of the attribute.
//If it is a repeating order, returns the array, otherwise returns the update value or the original value
if(prop === 'set'){
return function(){
let {attributes,sections,casc,callback,vocal} = arguments[0] ? arguments[0] : {};
if(attributes && attributes.queue.length && sections && casc){
let triggerName = attributes.queue.shift();
let trigger = getCascObj({sourceAttribute:triggerName},casc);
attributes.processChange({trigger,attributes,sections,casc});
}else{
debug('sheet accessed, all changes handled, setting attributes');
set(obj.updates,vocal,callback);
}
}
}else if(Object.keys(obj).some(key=>key===prop)){
return Reflect.get(...arguments)
}else{
let retValue;
switch(true){
case obj.repOrders.hasOwnProperty(prop):
retValue = obj.repOrders[prop];
break;
case obj.updates.hasOwnProperty(prop):
retValue = obj.updates[prop];
break;
default:
retValue = obj.attributes[prop];
break;
}
let cascRef = `attr_${prop.replace(/(repeating_[^_]+_)[^_]+/,'$1\$x')}`;
let numRetVal = +retValue;
if(!Number.isNaN(numRetVal)){
retValue = numRetVal;
}else if(cascades[cascRef] && typeof cascades[cascRef].defaultValue === 'number'){
retValue = cascades[cascRef].defaultValue;
}
return retValue;
}
},
set:function(obj,prop,value){
//Sets the value. Also verifies that the value is a valid attribute value
//e.g. not undefined, null, or NaN
if(value || value===0 || value===''){
if(/reporder|^repeating_[^_]+$/.test(prop)){
let section = prop.replace(/_reporder_/,'');
obj.repOrders[section] = value;
}else if(`${obj.attributes}` !== `${value}` ||
(obj.updates[prop] && `${obj.updates}` !== `${value}`)
){
obj.updates[prop] = value;
}
}else{
debug(`!!!Warning: Attempted to set ${prop} to an invalid value:${value}; value not stored!!!`);
}
return true;
},
deleteProperty(obj,prop){
//removes the property from the original attributes, updates, and the reporders
Object.keys(obj).forEach((key)=>{
delete obj[key][prop.toLowerCase()];
});
}
};
return new Proxy(attrTarget,attrHandler);
};
const funcs = new Proxy({},{
set:function(obj,prop,value){
if(obj[prop]){
debug(`!!! Duplicate function name for ${prop} !!!`);
return false;
}else{
obj[prop] = value;
return true;
}
},
get:function(obj,prop){
return function(){
debug(`running ${prop}`);
return obj[prop](...arguments);
};
}
});