Allowed effects and calculations to target nearest ancestors of #type
This commit is contained in:
0
app/grammar.js
Normal file
0
app/grammar.js
Normal file
@@ -1,4 +1,5 @@
|
||||
import { includes, cloneDeep } from 'lodash';
|
||||
import findAncestorByType from '/imports/api/creature/computation/findAncestorByType.js';
|
||||
|
||||
// The computation memo is an in-memory data structure used only during the
|
||||
// computation process
|
||||
@@ -141,7 +142,7 @@ export default class ComputationMemo {
|
||||
prop = this.registerProperty(prop);
|
||||
let targets = this.getEffectTargets(prop);
|
||||
targets.forEach(target => {
|
||||
if (target.computationDetails.effects){
|
||||
if (target.computationDetails && target.computationDetails.effects){
|
||||
target.computationDetails.effects.push(prop);
|
||||
}
|
||||
});
|
||||
@@ -153,7 +154,16 @@ export default class ComputationMemo {
|
||||
let targets = new Set();
|
||||
if (!prop.stats) return targets;
|
||||
prop.stats.forEach((statName) => {
|
||||
let target = this.statsByVariableName[statName];
|
||||
let target;
|
||||
if (statName[0] === '#'){
|
||||
target = findAncestorByType({
|
||||
type: statName.slice(1),
|
||||
prop,
|
||||
memo: this
|
||||
});
|
||||
} else {
|
||||
target = this.statsByVariableName[statName];
|
||||
}
|
||||
if (!target) return;
|
||||
targets.add(target);
|
||||
if (isSkillOperation(prop) && isAbility(target)){
|
||||
|
||||
@@ -8,7 +8,11 @@ export default class EffectAggregator{
|
||||
result,
|
||||
context,
|
||||
dependencies
|
||||
} = evaluateCalculation(stat.baseValueCalculation, memo);
|
||||
} = evaluateCalculation({
|
||||
string: stat.baseValueCalculation,
|
||||
prop: stat,
|
||||
memo
|
||||
});
|
||||
this.statBaseValue = result.value;
|
||||
stat.dependencies.push(...dependencies);
|
||||
if (context.errors.length){
|
||||
|
||||
@@ -38,7 +38,11 @@ function combineAttribute(stat, aggregator, memo){
|
||||
result,
|
||||
context,
|
||||
dependencies
|
||||
} = evaluateCalculation(stat.spellSlotLevelCalculation, memo);
|
||||
} = evaluateCalculation({
|
||||
string: stat.spellSlotLevelCalculation,
|
||||
memo,
|
||||
prop: stat,
|
||||
});
|
||||
stat.spellSlotLevelValue = result.value;
|
||||
stat.spellSlotLevelErrors = context.errors;
|
||||
stat.dependencies.push(...dependencies);
|
||||
|
||||
@@ -38,7 +38,11 @@ export default function computeEffect(effect, memo){
|
||||
result,
|
||||
context,
|
||||
dependencies,
|
||||
} = evaluateCalculation(effect.calculation, memo);
|
||||
} = evaluateCalculation({
|
||||
string: effect.calculation,
|
||||
prop: effect,
|
||||
memo
|
||||
});
|
||||
effect.result = result.value;
|
||||
effect.dependencies.push(...dependencies);
|
||||
if (context.errors.length){
|
||||
|
||||
@@ -33,7 +33,7 @@ function computeAction(prop, memo){
|
||||
result,
|
||||
context,
|
||||
dependencies,
|
||||
} = evaluateCalculation(prop.uses, memo);
|
||||
} = evaluateCalculation({ string: prop.uses, prop, memo});
|
||||
prop.usesResult = result.value;
|
||||
prop.dependencies.push(...dependencies);
|
||||
if (context.errors.length){
|
||||
@@ -85,7 +85,7 @@ function computePropertyField(prop, memo, fieldName, fn){
|
||||
result,
|
||||
context,
|
||||
dependencies,
|
||||
} = evaluateCalculation(prop[fieldName], memo, fn);
|
||||
} = evaluateCalculation({string: prop[fieldName], prop, memo, fn});
|
||||
if (result instanceof ConstantNode){
|
||||
prop[`${fieldName}Result`] = result.value;
|
||||
} else {
|
||||
|
||||
@@ -19,7 +19,7 @@ function computeInlineCalcsForField(prop, memo, field){
|
||||
result,
|
||||
context,
|
||||
dependencies,
|
||||
} = evaluateCalculation(calculation, memo, 'compile');
|
||||
} = evaluateCalculation({string: calculation, prop, memo, fn: 'compile'});
|
||||
let computation = {
|
||||
calculation,
|
||||
result: result.toString(),
|
||||
|
||||
@@ -30,7 +30,7 @@ export default function computeToggle(toggle, memo){
|
||||
result,
|
||||
context,
|
||||
dependencies,
|
||||
} = evaluateCalculation(toggle.condition, memo);
|
||||
} = evaluateCalculation({string: toggle.condition, prop: toggle, memo});
|
||||
toggle.toggleResult = !!result.value;
|
||||
toggle.dependencies.push(...dependencies);
|
||||
if (context.errors.length){
|
||||
|
||||
@@ -3,9 +3,15 @@ import { parse, CompilationContext } from '/imports/parser/parser.js';
|
||||
import SymbolNode from '/imports/parser/parseTree/SymbolNode.js';
|
||||
import AccessorNode from '/imports/parser/parseTree/AccessorNode.js';
|
||||
import ConstantNode from '/imports/parser/parseTree/ConstantNode.js';
|
||||
import findAncestorByType from '/imports/api/creature/computation/findAncestorByType.js';
|
||||
|
||||
/* Convert a calculation into a constant output and errors*/
|
||||
export default function evaluateCalculation(string, memo, fn = 'reduce'){
|
||||
export default function evaluateCalculation({
|
||||
string,
|
||||
prop,
|
||||
memo,
|
||||
fn = 'reduce',
|
||||
}){
|
||||
let dependencies = [];
|
||||
let errors = [];
|
||||
if (!string) return {
|
||||
@@ -38,8 +44,15 @@ export default function evaluateCalculation(string, memo, fn = 'reduce'){
|
||||
// Ensure all symbol nodes are defined and computed
|
||||
calc.traverse(node => {
|
||||
if (node instanceof SymbolNode || node instanceof AccessorNode){
|
||||
let stat = memo.statsByVariableName[node.name];
|
||||
if (stat && !stat.computationDetails.computed){
|
||||
// References up the tree start with $
|
||||
let stat;
|
||||
if (node.name[0] === '#'){
|
||||
stat = findAncestorByType({type: node.name.slice(1), prop, memo});
|
||||
memo.statsByVariableName[node.name] = stat;
|
||||
} else {
|
||||
stat = memo.statsByVariableName[node.name];
|
||||
}
|
||||
if (stat && stat.computationDetails && !stat.computationDetails.computed){
|
||||
computeStat(stat, memo);
|
||||
}
|
||||
if (stat) dependencies.push(stat._id || node.name, ...stat.dependencies);
|
||||
|
||||
10
app/imports/api/creature/computation/findAncestorByType.js
Normal file
10
app/imports/api/creature/computation/findAncestorByType.js
Normal file
@@ -0,0 +1,10 @@
|
||||
export default function findAncestorByType({type, prop, memo}){
|
||||
if (!prop || !prop.ancestors) return;
|
||||
let ancestor;
|
||||
for (let i = prop.ancestors.length - 1; i >= 0; i--){
|
||||
ancestor = memo.propsById[prop.ancestors[i].id];
|
||||
if (ancestor && ancestor.type === type){
|
||||
return ancestor;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ function id(x) { return x[0]; }
|
||||
value: s => s.slice(1, -1),
|
||||
},
|
||||
name: {
|
||||
match: /[a-zA-Z_]*[a-ce-zA-Z_][a-zA-Z0-9_]*/,
|
||||
match: /[a-zA-Z_#]*[a-ce-zA-Z_#][a-zA-Z0-9_#]*/,
|
||||
type: moo.keywords({
|
||||
'keywords': ['true', 'false'],
|
||||
}),
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
value: s => s.slice(1, -1),
|
||||
},
|
||||
name: {
|
||||
match: /[a-zA-Z_]*[a-ce-zA-Z_][a-zA-Z0-9_]*/,
|
||||
match: /[a-zA-Z_#]*[a-ce-zA-Z_#][a-zA-Z0-9_#]*/,
|
||||
type: moo.keywords({
|
||||
'keywords': ['true', 'false'],
|
||||
}),
|
||||
|
||||
@@ -7,7 +7,7 @@ export default class AccessorNode extends ParseNode {
|
||||
this.name = name;
|
||||
this.path = path;
|
||||
}
|
||||
compile(scope){
|
||||
compile(scope, context){
|
||||
let value = scope && scope[this.name];
|
||||
// For objects, get their value
|
||||
this.path.forEach(name => {
|
||||
@@ -16,14 +16,21 @@ export default class AccessorNode extends ParseNode {
|
||||
});
|
||||
let type = typeof value;
|
||||
if (type === 'string' || type === 'number' || type === 'boolean'){
|
||||
return new ConstantNode({value, type, previousNodes: [this]});
|
||||
return new ConstantNode({value, type});
|
||||
} else if (type === 'undefined'){
|
||||
return new AccessorNode({
|
||||
name: this.name,
|
||||
path: this.path,
|
||||
});
|
||||
} else {
|
||||
throw new Meteor.Error(`Unexpected case: ${this.name} resolved to ${value}`);
|
||||
if (context) context.storeError({
|
||||
type: 'error',
|
||||
message: `${this.name} returned an unexpected type`
|
||||
});
|
||||
return new AccessorNode({
|
||||
name: this.name,
|
||||
path: this.path,
|
||||
});
|
||||
}
|
||||
}
|
||||
reduce(scope, context){
|
||||
|
||||
Reference in New Issue
Block a user