Added UI backend that can do computations with context

This commit is contained in:
Thaum Rystra
2020-04-23 14:26:05 +02:00
parent 7416101a34
commit 95bfcd79c9
16 changed files with 294 additions and 40 deletions

View File

@@ -0,0 +1,107 @@
import * as math from 'mathjs';
export default function evaluateString(string, scope){
let errors = [];
if (!string){
errors.push('No string provided');
return {result: string, errors};
}
if (!scope) errors.push('No scope provided');
// Parse the string using mathjs
let calc;
try {
calc = math.parse(string);
} catch (e) {
errors.push(e);
return {result: string, errors};
}
// Replace all bare symbols with symbol.value
let transformedCalc = calc.transform(replaceBareSymbolsWithValueAccessor);
// Evaluate the expression to a number or return with substitutions
try {
let result = transformedCalc.evaluate(scope);
return {result, errors};
} catch (e1){
errors.push(e1);
try {
result = simplifyWithAccessors(calc, scope).toHTML();
return {result, errors};
} catch (e2){
errors.push(e2);
return {result: calc.toHTML(), errors};
}
}
}
function replaceBareSymbolsWithValueAccessor(node, path, parent) {
if (node.isSymbolNode && path !== 'object') {
const object = new math.SymbolNode(node.name);
const address = new math.ConstantNode('value');
const index = new math.IndexNode([address]);
return new math.AccessorNode(object, index);
} else {
return node;
}
}
function simplifyWithAccessors(calc, scope){
let noAccessorCalc = calc.transform(substituteAccessors(scope));
return math.simplify(noAccessorCalc);
}
// returns a function to replace all accessors with either their resolved value
// or a symbol to simplify with
function substituteAccessors(scope){
return function(node, path, parent){
if (node.isAccessorNode){
try {
return evaluateAccessor(node, scope);
} catch (e) {
console.log(typeof e);
return replaceAccessorWithSymbol(node);
}
} else {
return node;
}
}
}
// Throws error if symbol is undefined in scope
function evaluateAccessor(node, scope){
let value = node.evaluate(scope);
if (value === undefined){
throw 'Undefined symbol'
}
return new math.ConstantNode(value);
}
function replaceAccessorWithSymbol(node){
let symbolNode = new math.SymbolNode(node.toString());
return symbolNode;
}
function overrideSymbolNodeHTML(symbolNode){
let safeName = escape(symbolNode.name);
symbolNode.toHTML = function(){
console.log('running custom tohtml function')
return `<span class="math-symbol math-substitution-failed">${safeName}</span>`
}
return symbolNode;
}
// Escape special HTML characters
// Copied directly from math.js source to help with overriding toHTML
function escape (value) {
let text = String(value)
text = text.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
return text
}

View File

@@ -19,8 +19,9 @@ function combineAttribute(stat, aggregator){
if (!stat.decimal) result = Math.floor(result);
stat.value = result;
if (stat.attributeType === 'ability') {
stat.mod = Math.floor((result - 10) / 2);
stat.modifier = Math.floor((result - 10) / 2);
}
stat.currentValue = stat.value - (stat.damage || 0);
}
function combineSkill(stat, aggregator, memo){
@@ -30,7 +31,7 @@ function combineSkill(stat, aggregator, memo){
if (!ability.computationDetails.computed){
computeStat(ability, memo);
}
stat.abilityMod = ability.mod;
stat.abilityMod = ability.modifier;
}
// Combine all the child proficiencies
for (let i in stat.proficiencies){

View File

@@ -16,7 +16,7 @@ export default function evaluateCalculation(string, memo){
if (node.isSymbolNode) {
let val = computedValueOfVariableName(node.name, memo);
if (val === null) return node;
return new math.expression.node.ConstantNode(val);
return new math.ConstantNode(val);
}
else {
return node;

View File

@@ -11,7 +11,8 @@ export default function writeCreatureVariables(memo, creatureId) {
'reset',
'resetMultiplier',
'value',
'mod',
'currentValue',
'modifier',
'ability',
'skillType',
'baseProficiency',