Merge branch 'develop' into feature-nested-sets
This commit is contained in:
@@ -1,11 +1,18 @@
|
||||
import { CreatureLogSchema, insertCreatureLogWork } from '/imports/api/creature/log/CreatureLogs';
|
||||
import {
|
||||
getCreature, getVariables, getPropertiesOfType
|
||||
getCreature, getVariables, getPropertiesOfType, replaceLinkedVariablesWithProps
|
||||
} from '/imports/api/engine/loadCreatures';
|
||||
import { groupBy, remove } from 'lodash';
|
||||
|
||||
export default class ActionContext {
|
||||
constructor(creatureId, targetIds = [], method) {
|
||||
creature: any;
|
||||
log: any;
|
||||
scope: any;
|
||||
targets: Array<any>;
|
||||
triggers: Array<any>;
|
||||
method: any;
|
||||
|
||||
constructor(creatureId, targetIds: string[] = [], method) {
|
||||
// Get the creature
|
||||
this.creature = getCreature(creatureId)
|
||||
|
||||
@@ -20,6 +27,7 @@ export default class ActionContext {
|
||||
|
||||
// Get the variables of the acting creature
|
||||
this.creature.variables = getVariables(creatureId);
|
||||
replaceLinkedVariablesWithProps(this.creature.variables);
|
||||
delete this.creature.variables._id;
|
||||
delete this.creature.variables._creatureId;
|
||||
// Alias as scope
|
||||
@@ -52,10 +60,10 @@ export default class ActionContext {
|
||||
// Group the triggers into triggers.<event>.<timing> or
|
||||
// triggers.doActionProperty.<propertyType>.<timing>
|
||||
this.triggers = groupBy(this.triggers, 'event');
|
||||
for (let event in this.triggers) {
|
||||
for (const event in this.triggers) {
|
||||
if (event === 'doActionProperty') {
|
||||
this.triggers[event] = groupBy(this.triggers[event], 'actionPropertyType');
|
||||
for (let propertyType in this.triggers[event]) {
|
||||
for (const propertyType in this.triggers[event]) {
|
||||
this.triggers[event][propertyType] = groupBy(this.triggers[event][propertyType], 'timing');
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import {
|
||||
setLineageOfDocs,
|
||||
renewDocIds
|
||||
renewDocIds,
|
||||
} from '/imports/api/parenting/parentingFunctions';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import computedSchemas from '/imports/api/properties/computedPropertySchemasIndex';
|
||||
import applyFnToKey from '/imports/api/engine/computation/utility/applyFnToKey';
|
||||
import { get } from 'lodash';
|
||||
import resolve, { map, toString } from '/imports/parser/resolve';
|
||||
import symbol from '/imports/parser/parseTree/symbol';
|
||||
import accessor from '/imports/parser/parseTree/accessor';
|
||||
import logErrors from './shared/logErrors';
|
||||
import { insertCreatureLog } from '/imports/api/creature/log/CreatureLogs';
|
||||
import cyrb53 from '/imports/api/engine/computation/utility/cyrb53';
|
||||
@@ -25,7 +24,7 @@ export default function applyBuff(node, actionContext) {
|
||||
|
||||
// Then copy the descendants of the buff to the targets
|
||||
let propList = [prop];
|
||||
function addChildrenToPropList(children, { skipCrystalize } = {}) {
|
||||
function addChildrenToPropList(children, { skipCrystalize } = { skipCrystalize: false }) {
|
||||
children.forEach(child => {
|
||||
if (skipCrystalize) child.node._skipCrystalize = true;
|
||||
propList.push(child.node);
|
||||
@@ -40,13 +39,20 @@ export default function applyBuff(node, actionContext) {
|
||||
crystalizeVariables({ propList, actionContext });
|
||||
}
|
||||
|
||||
let oldParent = {
|
||||
id: prop.parent.id,
|
||||
collection: prop.parent.collection,
|
||||
};
|
||||
buffTargets.forEach(target => {
|
||||
const targetPropList = EJSON.clone(propList);
|
||||
// Move the properties to the target by replacing the old subtree parent and root with the '
|
||||
// target id
|
||||
renewDocIds({
|
||||
docArray: targetPropList,
|
||||
idMap: {
|
||||
[prop.parentId]: target._id,
|
||||
[prop.root.id]: target._id,
|
||||
},
|
||||
collectionMap: { [prop.root.collection]: 'creatures' }
|
||||
});
|
||||
// Apply the buff
|
||||
copyNodeListToTarget(propList, target, oldParent);
|
||||
CreatureProperties.batchInsert(targetPropList);
|
||||
|
||||
//Log the buff
|
||||
let logValue = prop.description?.value
|
||||
@@ -81,25 +87,6 @@ export default function applyBuff(node, actionContext) {
|
||||
// Don't apply the children of the buff, they get copied to the target instead
|
||||
}
|
||||
|
||||
function copyNodeListToTarget(propList, target, oldParent) {
|
||||
let ancestry = [{ collection: 'creatures', id: target._id }];
|
||||
setLineageOfDocs({
|
||||
docArray: propList,
|
||||
newAncestry: ancestry,
|
||||
oldParent,
|
||||
});
|
||||
renewDocIds({
|
||||
docArray: propList,
|
||||
});
|
||||
/*
|
||||
setDocToLastOrder({
|
||||
collection: CreatureProperties,
|
||||
doc: propList[0],
|
||||
});
|
||||
*/
|
||||
CreatureProperties.batchInsert(propList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all variables with their resolved values
|
||||
* except variables of the form `~target.thing.total` become `thing.total`
|
||||
@@ -118,7 +105,7 @@ function crystalizeVariables({ propList, actionContext }) {
|
||||
calcObj.parseNode = map(calcObj.parseNode, node => {
|
||||
// Skip nodes that aren't symbols or accessors
|
||||
if (
|
||||
node.parseType !== 'accessor' && node.parseType !== 'symbol'
|
||||
node.parseType !== 'accessor'
|
||||
) return node;
|
||||
// Handle variables
|
||||
if (node.name === '~target') {
|
||||
@@ -126,7 +113,7 @@ function crystalizeVariables({ propList, actionContext }) {
|
||||
if (node.parseType === 'accessor') {
|
||||
node.name = node.path.shift();
|
||||
if (!node.path.length) {
|
||||
return symbol.create({ name: node.name })
|
||||
return accessor.create({ name: node.name })
|
||||
}
|
||||
} else {
|
||||
// Can't strip symbols
|
||||
|
||||
@@ -3,7 +3,7 @@ import applyChildren from '/imports/api/engine/actions/applyPropertyByType/share
|
||||
import { insertCreatureLog } from '/imports/api/creature/log/CreatureLogs';
|
||||
import resolve, { Context, toString } from '/imports/parser/resolve';
|
||||
import logErrors from './shared/logErrors';
|
||||
import applyEffectsToCalculationParseNode from '/imports/api/engine/actions/applyPropertyByType/shared/applyEffectsToCalculationParseNode';
|
||||
import recalculateCalculation from '/imports/api/engine/actions/applyPropertyByType/shared/recalculateCalculation'
|
||||
import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty';
|
||||
import {
|
||||
getPropertiesOfType
|
||||
@@ -37,8 +37,8 @@ export default function applyDamage(node, actionContext) {
|
||||
const logName = prop.damageType === 'healing' ? 'Healing' : 'Damage';
|
||||
|
||||
// roll the dice only and store that string
|
||||
applyEffectsToCalculationParseNode(prop.amount, actionContext);
|
||||
const { result: rolled } = resolve('roll', prop.amount.parseNode, scope, context);
|
||||
recalculateCalculation(prop.amount, actionContext, undefined, 'compile');
|
||||
const { result: rolled } = resolve('roll', prop.amount.valueNode, scope, context);
|
||||
if (rolled.parseType !== 'constant') {
|
||||
logValue.push(toString(rolled));
|
||||
}
|
||||
@@ -88,8 +88,8 @@ export default function applyDamage(node, actionContext) {
|
||||
let damageOnSave, saveNode, saveRoll;
|
||||
if (prop.save) {
|
||||
if (prop.save.damageFunction?.calculation) {
|
||||
applyEffectsToCalculationParseNode(prop.save.damageFunction, actionContext);
|
||||
let { result: saveDamageRolled } = resolve('roll', prop.save.damageFunction.parseNode, scope, context);
|
||||
recalculateCalculation(prop.save.damageFunction, actionContext, undefined, 'compile');
|
||||
let { result: saveDamageRolled } = resolve('roll', prop.save.damageFunction.valueNode, scope, context);
|
||||
saveRoll = toString(saveDamageRolled);
|
||||
let { result: saveDamageResult } = resolve('reduce', saveDamageRolled, scope, context);
|
||||
// If we didn't end up with a constant of finite amount, give up
|
||||
@@ -167,7 +167,7 @@ export default function applyDamage(node, actionContext) {
|
||||
creatureId: target._id,
|
||||
content: [{
|
||||
name,
|
||||
value: `Recieved **${damageDealt}** ${suffix}`,
|
||||
value: `Received **${damageDealt}** ${suffix}`,
|
||||
}],
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren';
|
||||
import logErrors from './shared/logErrors';
|
||||
import applyEffectsToCalculationParseNode from '/imports/api/engine/actions/applyPropertyByType/shared/applyEffectsToCalculationParseNode';
|
||||
import recalculateCalculation from '/imports/api/engine/actions/applyPropertyByType/shared/recalculateCalculation';
|
||||
import resolve, { toString } from '/imports/parser/resolve';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers';
|
||||
|
||||
@@ -12,8 +12,8 @@ export default function applyRoll(node, actionContext) {
|
||||
const logValue = [];
|
||||
|
||||
// roll the dice only and store that string
|
||||
applyEffectsToCalculationParseNode(prop.roll, actionContext);
|
||||
const { result: rolled, context } = resolve('roll', prop.roll.parseNode, actionContext.scope);
|
||||
recalculateCalculation(prop.roll, actionContext, undefined, 'compile');
|
||||
const { result: rolled, context } = resolve('roll', prop.roll.valueNode, actionContext.scope);
|
||||
if (rolled.parseType !== 'constant') {
|
||||
logValue.push(toString(rolled));
|
||||
}
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
import operator from '/imports/parser/parseTree/operator';
|
||||
import { parse } from '/imports/parser/parser';
|
||||
import logErrors from './logErrors';
|
||||
|
||||
export default function applyEffectsToCalculationParseNode(calcObj, actionContext) {
|
||||
calcObj.effects?.forEach(effect => {
|
||||
if (effect.operation !== 'add') return;
|
||||
if (!effect.amount) return;
|
||||
if (effect.amount.value === null) return;
|
||||
let effectParseNode;
|
||||
try {
|
||||
effectParseNode = parse(effect.amount.value.toString());
|
||||
calcObj.parseNode = operator.create({
|
||||
left: calcObj.parseNode,
|
||||
right: effectParseNode,
|
||||
operator: '+',
|
||||
fn: 'add'
|
||||
});
|
||||
} catch (e) {
|
||||
logErrors([e], actionContext)
|
||||
}
|
||||
});
|
||||
// Add the highest proficiency as well
|
||||
let highestProficiency;
|
||||
calcObj.proficiencies?.forEach(proficiency => {
|
||||
if (
|
||||
proficiency.value > highestProficiency
|
||||
|| (highestProficiency === undefined && Number.isFinite(proficiency.value))
|
||||
) {
|
||||
highestProficiency = proficiency.value;
|
||||
}
|
||||
});
|
||||
if (highestProficiency) {
|
||||
try {
|
||||
let profParseNode = parse(highestProficiency.toString());
|
||||
calcObj.parseNode = operator.create({
|
||||
left: calcObj.parseNode,
|
||||
right: profParseNode,
|
||||
operator: '+',
|
||||
fn: 'add'
|
||||
});
|
||||
} catch (e) {
|
||||
logErrors([e], actionContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,59 @@
|
||||
import evaluateCalculation from '/imports/api/engine/computation/utility/evaluateCalculation';
|
||||
import applyEffectsToCalculationParseNode from '/imports/api/engine/actions/applyPropertyByType/shared/applyEffectsToCalculationParseNode';
|
||||
import logErrors from './logErrors';
|
||||
import { toPrimitiveOrString } from '/imports/parser/resolve';
|
||||
import {
|
||||
aggregateCalculationEffects,
|
||||
aggregateCalculationProficiencies,
|
||||
resolveCalculationNode,
|
||||
} from '/imports/api/engine/computation/computeComputation/computeByType/computeCalculation';
|
||||
import { getSingleProperty } from '/imports/api/engine/loadCreatures';
|
||||
import resolve from '/imports/parser/resolve';
|
||||
|
||||
export default function recalculateCalculation(calc, actionContext, context) {
|
||||
if (!calc?.parseNode) return;
|
||||
calc._parseLevel = 'reduce';
|
||||
applyEffectsToCalculationParseNode(calc, actionContext);
|
||||
evaluateCalculation(calc, actionContext.scope, context);
|
||||
logErrors(calc.errors, actionContext);
|
||||
// Redo the work of imports/api/engine/computation/computeComputation/computeByType/computeCalculation.js
|
||||
// But in the action scope
|
||||
export default function recalculateCalculation(calcObj, actionContext, context, parseLevel = 'reduce') {
|
||||
if (!calcObj?.parseNode) return;
|
||||
calcObj._parseLevel = parseLevel;
|
||||
// Re-resolve the parse node
|
||||
resolveCalculationNode(calcObj, calcObj.parseNode, actionContext.scope, context);
|
||||
// store the unaffected value
|
||||
if (calcObj.effectIds || calcObj.proficiencyIds) {
|
||||
calcObj.unaffected = toPrimitiveOrString(calcObj.valueNode);
|
||||
}
|
||||
// Apply all the effects and proficiencies
|
||||
aggregateCalculationEffects(
|
||||
calcObj,
|
||||
id => getSingleProperty(actionContext.creature._id, id)
|
||||
);
|
||||
aggregateCalculationProficiencies(
|
||||
calcObj,
|
||||
id => getSingleProperty(actionContext.creature._id, id),
|
||||
actionContext.scope['proficiencyBonus']?.value || 0
|
||||
);
|
||||
// Resolve the modified valueNode
|
||||
resolveCalculationNode(calcObj, calcObj.valueNode, actionContext.scope, context);
|
||||
|
||||
// Store the primitive value
|
||||
calcObj.value = toPrimitiveOrString(calcObj.valueNode);
|
||||
|
||||
logErrors(calcObj.errors, actionContext);
|
||||
}
|
||||
|
||||
export function rollAndReduceCalculation(calcObj, actionContext, context) {
|
||||
// Compile
|
||||
recalculateCalculation(calcObj, actionContext, context, 'compile');
|
||||
const compiled = calcObj.valueNode;
|
||||
const compileErrors = context.errors;
|
||||
|
||||
// Roll
|
||||
context.errors = [];
|
||||
const { result: rolled } = resolve('roll', calcObj.valueNode, actionContext.scope, context);
|
||||
const rollErrors = context.errors;
|
||||
|
||||
// Reduce
|
||||
context.errors = [];
|
||||
const { result: reduced } = resolve('reduce', rolled, actionContext.scope, context);
|
||||
const reduceErrors = context.errors;
|
||||
|
||||
// Return
|
||||
return { compiled, compileErrors, rolled, rollErrors, reduced, reduceErrors };
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ export function applyTrigger(trigger, prop, actionContext) {
|
||||
// Prevent triggers from firing if their condition is false
|
||||
if (trigger.condition?.parseNode) {
|
||||
recalculateCalculation(trigger.condition, actionContext);
|
||||
if (!trigger.condition.value) return;
|
||||
if (!trigger.condition.value?.value) return;
|
||||
}
|
||||
|
||||
// Prevent triggers from firing themselves in a loop
|
||||
|
||||
@@ -7,7 +7,7 @@ import rollDice from '/imports/parser/rollDice';
|
||||
import numberToSignedString from '/imports/api/utility/numberToSignedString';
|
||||
import { applyTriggers } from '/imports/api/engine/actions/applyTriggers';
|
||||
import ActionContext from '/imports/api/engine/actions/ActionContext';
|
||||
import evaluateCalculation from '/imports/api/engine/computation/utility/evaluateCalculation';
|
||||
import recalculateCalculation from '/imports/api/engine/actions/applyPropertyByType/shared/recalculateCalculation';
|
||||
|
||||
const doCheck = new ValidatedMethod({
|
||||
name: 'creatureProperties.doCheck',
|
||||
@@ -77,7 +77,7 @@ function rollCheck(prop, actionContext) {
|
||||
|
||||
let rollModifierText = numberToSignedString(rollModifier, true);
|
||||
|
||||
const { effectBonus, effectString } = applyUnresolvedEffects(prop, scope)
|
||||
const { effectBonus, effectString } = applyUnresolvedEffects(prop, actionContext)
|
||||
rollModifierText += effectString;
|
||||
rollModifier += effectBonus;
|
||||
|
||||
@@ -117,7 +117,8 @@ function rollCheck(prop, actionContext) {
|
||||
});
|
||||
}
|
||||
|
||||
export function applyUnresolvedEffects(prop, scope) {
|
||||
// TODO replace this with recalculating and then rolling/reducing the value node
|
||||
export function applyUnresolvedEffects(prop, actionContext) {
|
||||
let effectBonus = 0;
|
||||
let effectString = '';
|
||||
if (!prop.effects) {
|
||||
@@ -126,8 +127,7 @@ export function applyUnresolvedEffects(prop, scope) {
|
||||
prop.effects.forEach(effect => {
|
||||
if (!effect.amount?.parseNode) return;
|
||||
if (effect.operation !== 'add') return;
|
||||
effect.amount._parseLevel = 'reduce';
|
||||
evaluateCalculation(effect.amount, scope);
|
||||
recalculateCalculation(effect.amount, actionContext, context, 'reduce');
|
||||
if (typeof effect.amount?.value !== 'number') return;
|
||||
effectBonus += effect.amount.value;
|
||||
effectString += ` ${effect.amount.value < 0 ? '-' : '+'} [${effect.amount.calculation}] ${Math.abs(effect.amount.value)}`
|
||||
|
||||
Reference in New Issue
Block a user