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 { 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
|
// The computation memo is an in-memory data structure used only during the
|
||||||
// computation process
|
// computation process
|
||||||
@@ -141,7 +142,7 @@ export default class ComputationMemo {
|
|||||||
prop = this.registerProperty(prop);
|
prop = this.registerProperty(prop);
|
||||||
let targets = this.getEffectTargets(prop);
|
let targets = this.getEffectTargets(prop);
|
||||||
targets.forEach(target => {
|
targets.forEach(target => {
|
||||||
if (target.computationDetails.effects){
|
if (target.computationDetails && target.computationDetails.effects){
|
||||||
target.computationDetails.effects.push(prop);
|
target.computationDetails.effects.push(prop);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -153,7 +154,16 @@ export default class ComputationMemo {
|
|||||||
let targets = new Set();
|
let targets = new Set();
|
||||||
if (!prop.stats) return targets;
|
if (!prop.stats) return targets;
|
||||||
prop.stats.forEach((statName) => {
|
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;
|
if (!target) return;
|
||||||
targets.add(target);
|
targets.add(target);
|
||||||
if (isSkillOperation(prop) && isAbility(target)){
|
if (isSkillOperation(prop) && isAbility(target)){
|
||||||
|
|||||||
@@ -8,7 +8,11 @@ export default class EffectAggregator{
|
|||||||
result,
|
result,
|
||||||
context,
|
context,
|
||||||
dependencies
|
dependencies
|
||||||
} = evaluateCalculation(stat.baseValueCalculation, memo);
|
} = evaluateCalculation({
|
||||||
|
string: stat.baseValueCalculation,
|
||||||
|
prop: stat,
|
||||||
|
memo
|
||||||
|
});
|
||||||
this.statBaseValue = result.value;
|
this.statBaseValue = result.value;
|
||||||
stat.dependencies.push(...dependencies);
|
stat.dependencies.push(...dependencies);
|
||||||
if (context.errors.length){
|
if (context.errors.length){
|
||||||
|
|||||||
@@ -38,7 +38,11 @@ function combineAttribute(stat, aggregator, memo){
|
|||||||
result,
|
result,
|
||||||
context,
|
context,
|
||||||
dependencies
|
dependencies
|
||||||
} = evaluateCalculation(stat.spellSlotLevelCalculation, memo);
|
} = evaluateCalculation({
|
||||||
|
string: stat.spellSlotLevelCalculation,
|
||||||
|
memo,
|
||||||
|
prop: stat,
|
||||||
|
});
|
||||||
stat.spellSlotLevelValue = result.value;
|
stat.spellSlotLevelValue = result.value;
|
||||||
stat.spellSlotLevelErrors = context.errors;
|
stat.spellSlotLevelErrors = context.errors;
|
||||||
stat.dependencies.push(...dependencies);
|
stat.dependencies.push(...dependencies);
|
||||||
|
|||||||
@@ -38,7 +38,11 @@ export default function computeEffect(effect, memo){
|
|||||||
result,
|
result,
|
||||||
context,
|
context,
|
||||||
dependencies,
|
dependencies,
|
||||||
} = evaluateCalculation(effect.calculation, memo);
|
} = evaluateCalculation({
|
||||||
|
string: effect.calculation,
|
||||||
|
prop: effect,
|
||||||
|
memo
|
||||||
|
});
|
||||||
effect.result = result.value;
|
effect.result = result.value;
|
||||||
effect.dependencies.push(...dependencies);
|
effect.dependencies.push(...dependencies);
|
||||||
if (context.errors.length){
|
if (context.errors.length){
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ function computeAction(prop, memo){
|
|||||||
result,
|
result,
|
||||||
context,
|
context,
|
||||||
dependencies,
|
dependencies,
|
||||||
} = evaluateCalculation(prop.uses, memo);
|
} = evaluateCalculation({ string: prop.uses, prop, memo});
|
||||||
prop.usesResult = result.value;
|
prop.usesResult = result.value;
|
||||||
prop.dependencies.push(...dependencies);
|
prop.dependencies.push(...dependencies);
|
||||||
if (context.errors.length){
|
if (context.errors.length){
|
||||||
@@ -85,7 +85,7 @@ function computePropertyField(prop, memo, fieldName, fn){
|
|||||||
result,
|
result,
|
||||||
context,
|
context,
|
||||||
dependencies,
|
dependencies,
|
||||||
} = evaluateCalculation(prop[fieldName], memo, fn);
|
} = evaluateCalculation({string: prop[fieldName], prop, memo, fn});
|
||||||
if (result instanceof ConstantNode){
|
if (result instanceof ConstantNode){
|
||||||
prop[`${fieldName}Result`] = result.value;
|
prop[`${fieldName}Result`] = result.value;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ function computeInlineCalcsForField(prop, memo, field){
|
|||||||
result,
|
result,
|
||||||
context,
|
context,
|
||||||
dependencies,
|
dependencies,
|
||||||
} = evaluateCalculation(calculation, memo, 'compile');
|
} = evaluateCalculation({string: calculation, prop, memo, fn: 'compile'});
|
||||||
let computation = {
|
let computation = {
|
||||||
calculation,
|
calculation,
|
||||||
result: result.toString(),
|
result: result.toString(),
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export default function computeToggle(toggle, memo){
|
|||||||
result,
|
result,
|
||||||
context,
|
context,
|
||||||
dependencies,
|
dependencies,
|
||||||
} = evaluateCalculation(toggle.condition, memo);
|
} = evaluateCalculation({string: toggle.condition, prop: toggle, memo});
|
||||||
toggle.toggleResult = !!result.value;
|
toggle.toggleResult = !!result.value;
|
||||||
toggle.dependencies.push(...dependencies);
|
toggle.dependencies.push(...dependencies);
|
||||||
if (context.errors.length){
|
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 SymbolNode from '/imports/parser/parseTree/SymbolNode.js';
|
||||||
import AccessorNode from '/imports/parser/parseTree/AccessorNode.js';
|
import AccessorNode from '/imports/parser/parseTree/AccessorNode.js';
|
||||||
import ConstantNode from '/imports/parser/parseTree/ConstantNode.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*/
|
/* 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 dependencies = [];
|
||||||
let errors = [];
|
let errors = [];
|
||||||
if (!string) return {
|
if (!string) return {
|
||||||
@@ -38,8 +44,15 @@ export default function evaluateCalculation(string, memo, fn = 'reduce'){
|
|||||||
// Ensure all symbol nodes are defined and computed
|
// Ensure all symbol nodes are defined and computed
|
||||||
calc.traverse(node => {
|
calc.traverse(node => {
|
||||||
if (node instanceof SymbolNode || node instanceof AccessorNode){
|
if (node instanceof SymbolNode || node instanceof AccessorNode){
|
||||||
let stat = memo.statsByVariableName[node.name];
|
// References up the tree start with $
|
||||||
if (stat && !stat.computationDetails.computed){
|
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);
|
computeStat(stat, memo);
|
||||||
}
|
}
|
||||||
if (stat) dependencies.push(stat._id || node.name, ...stat.dependencies);
|
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),
|
value: s => s.slice(1, -1),
|
||||||
},
|
},
|
||||||
name: {
|
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({
|
type: moo.keywords({
|
||||||
'keywords': ['true', 'false'],
|
'keywords': ['true', 'false'],
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
value: s => s.slice(1, -1),
|
value: s => s.slice(1, -1),
|
||||||
},
|
},
|
||||||
name: {
|
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({
|
type: moo.keywords({
|
||||||
'keywords': ['true', 'false'],
|
'keywords': ['true', 'false'],
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export default class AccessorNode extends ParseNode {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
this.path = path;
|
this.path = path;
|
||||||
}
|
}
|
||||||
compile(scope){
|
compile(scope, context){
|
||||||
let value = scope && scope[this.name];
|
let value = scope && scope[this.name];
|
||||||
// For objects, get their value
|
// For objects, get their value
|
||||||
this.path.forEach(name => {
|
this.path.forEach(name => {
|
||||||
@@ -16,14 +16,21 @@ export default class AccessorNode extends ParseNode {
|
|||||||
});
|
});
|
||||||
let type = typeof value;
|
let type = typeof value;
|
||||||
if (type === 'string' || type === 'number' || type === 'boolean'){
|
if (type === 'string' || type === 'number' || type === 'boolean'){
|
||||||
return new ConstantNode({value, type, previousNodes: [this]});
|
return new ConstantNode({value, type});
|
||||||
} else if (type === 'undefined'){
|
} else if (type === 'undefined'){
|
||||||
return new AccessorNode({
|
return new AccessorNode({
|
||||||
name: this.name,
|
name: this.name,
|
||||||
path: this.path,
|
path: this.path,
|
||||||
});
|
});
|
||||||
} else {
|
} 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){
|
reduce(scope, context){
|
||||||
|
|||||||
Reference in New Issue
Block a user