Updated computation engine to handle multiple attributes and skills with the same variable name
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
import combineStat from '/imports/api/creature/computation/engine/combineStat.js';
|
||||
import computeEffect from '/imports/api/creature/computation/engine/computeEffect.js';
|
||||
import EffectAggregator from '/imports/api/creature/computation/engine/EffectAggregator.js';
|
||||
import evaluateCalculation from '/imports/api/creature/computation/engine/evaluateCalculation.js';
|
||||
import applyToggles from '/imports/api/creature/computation/engine/applyToggles.js';
|
||||
import { each, union } from 'lodash';
|
||||
import { each, union, without } from 'lodash';
|
||||
|
||||
export default function computeStat(stat, memo){
|
||||
// If the stat is already computed, skip it
|
||||
@@ -19,29 +20,135 @@ export default function computeStat(stat, memo){
|
||||
}
|
||||
// Before doing any work, mark this stat as busy
|
||||
stat.computationDetails.busyComputing = true;
|
||||
// Apply any toggles
|
||||
applyToggles(stat, memo);
|
||||
|
||||
let effects = stat.computationDetails.effects;
|
||||
let proficiencies = stat.computationDetails.proficiencies;
|
||||
|
||||
// Get references to all the stats that share the variable name
|
||||
let sameNameStats = stat.computationDetails.idsOfSameName.map(
|
||||
id => memo.propsById[id]
|
||||
);
|
||||
|
||||
let allStats = [stat, ...sameNameStats];
|
||||
|
||||
// Decide which stat is the last active stat
|
||||
// The last active stat is considered the cannonical stat
|
||||
let lastActiveStat;
|
||||
allStats.forEach(candidateStat => {
|
||||
applyToggles(candidateStat, memo);
|
||||
if (!candidateStat.inactive) lastActiveStat = candidateStat;
|
||||
candidateStat.overridden = undefined;
|
||||
});
|
||||
if (!lastActiveStat){
|
||||
delete memo.statsByVariableName[stat.variableName];
|
||||
return;
|
||||
}
|
||||
// Make sure the active stat has all the effects and proficiencies
|
||||
lastActiveStat.computationDetails.effects = effects;
|
||||
lastActiveStat.computationDetails.proficiencies = proficiencies;
|
||||
|
||||
// Update the memo's stat with the chosen stat
|
||||
memo.statsByVariableName[stat.variableName] = lastActiveStat;
|
||||
|
||||
// Recreate list of the non-cannonical stats
|
||||
sameNameStats = without(allStats, lastActiveStat);
|
||||
|
||||
sameNameStats.forEach(statInstance => {
|
||||
// Mark the non-cannonical stats as overridden
|
||||
statInstance.overridden = true;
|
||||
|
||||
// Apply the cannonical damage
|
||||
statInstance.damage = lastActiveStat.damage;
|
||||
});
|
||||
|
||||
let baseDependencies = [];
|
||||
allStats.forEach(statInstance => {
|
||||
// Add this stat and its deps to the dependencies
|
||||
baseDependencies = union(
|
||||
baseDependencies,
|
||||
[statInstance._id],
|
||||
statInstance.dependencies,
|
||||
);
|
||||
|
||||
// Apply all the base proficiencies
|
||||
if (statInstance.baseProficiency && !statInstance.inactive){
|
||||
proficiencies.push({
|
||||
value: statInstance.baseProficiency,
|
||||
stats: [statInstance.variableName],
|
||||
type: 'proficiency',
|
||||
dependencies: [],
|
||||
computationDetails: {
|
||||
computed: true,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Compute each active stat's baseValue calculation and apply it
|
||||
if (statInstance.baseValueCalculation) {
|
||||
let {
|
||||
result,
|
||||
context,
|
||||
dependencies
|
||||
} = evaluateCalculation({
|
||||
string: statInstance.baseValueCalculation,
|
||||
prop: statInstance,
|
||||
memo
|
||||
});
|
||||
baseDependencies = union(baseDependencies, dependencies);
|
||||
statInstance.baseValue = +result.value;
|
||||
if (context.errors.length){
|
||||
statInstance.baseValueErrors = context.errors;
|
||||
}
|
||||
// Apply all the base values
|
||||
if (!statInstance.inactive){
|
||||
effects.push({
|
||||
operation: 'base',
|
||||
calculation: statInstance.baseValueCalculation,
|
||||
result: statInstance.baseValue,
|
||||
stats: [statInstance.variableName],
|
||||
dependencies: [],
|
||||
computationDetails: {
|
||||
computed: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Apply all the base baseDependencies
|
||||
allStats.forEach(statInstance => {
|
||||
statInstance.dependencies = union(
|
||||
statInstance.dependencies,
|
||||
without(baseDependencies, statInstance._id)
|
||||
);
|
||||
});
|
||||
|
||||
// Compute and aggregate all the effects
|
||||
let aggregator = new EffectAggregator(stat, memo)
|
||||
each(stat.computationDetails.effects, (effect) => {
|
||||
let aggregator = new EffectAggregator();
|
||||
let effectDeps = [];
|
||||
each(effects, (effect) => {
|
||||
// Compute
|
||||
computeEffect(effect, memo);
|
||||
if (effect.deactivatedByToggle) return;
|
||||
if (effect._id){
|
||||
stat.dependencies = union(
|
||||
stat.dependencies,
|
||||
[effect._id]
|
||||
);
|
||||
}
|
||||
stat.dependencies = union(
|
||||
stat.dependencies,
|
||||
effect.dependencies
|
||||
)
|
||||
|
||||
// dependencies
|
||||
if (effect._id) effectDeps = [effect._id];
|
||||
effectDeps = union(effectDeps, effect.dependencies);
|
||||
|
||||
// Add computed effect to aggregator
|
||||
aggregator.addEffect(effect);
|
||||
});
|
||||
// Conglomerate all the effects to compute the final stat values
|
||||
combineStat(stat, aggregator, memo);
|
||||
// Mark the attribute as computed
|
||||
stat.computationDetails.computed = true;
|
||||
stat.computationDetails.busyComputing = false;
|
||||
|
||||
// Combine the effects into the stats
|
||||
allStats.forEach(statInstance => {
|
||||
// Conglomerate all the effects to compute the final stat values
|
||||
combineStat(statInstance, aggregator, memo);
|
||||
// Mark the stats as computed
|
||||
statInstance.computationDetails.computed = true;
|
||||
statInstance.computationDetails.busyComputing = false;
|
||||
statInstance.dependencies = union(
|
||||
statInstance.dependencies,
|
||||
effectDeps
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user