started implementing buffs in new action engine

This commit is contained in:
ThaumRystra
2024-01-02 07:47:06 +02:00
parent e886be8f04
commit 1361205a7a
2 changed files with 156 additions and 4 deletions

View File

@@ -5,12 +5,16 @@ import { getCreature, getPropertiesOfType, getPropertyChildren, getSinglePropert
import recalculateInlineCalculations from '/imports/api/engine/actions/applyPropertyByType/shared/recalculateInlineCalculations';
import recalculateCalculation, { rollAndReduceCalculation } from '/imports/api/engine/actions/applyPropertyByType/shared/recalculateCalculation';
import rollDice from '/imports/parser/rollDice';
import { toString } from '/imports/parser/resolve';
import { getFromScope } from '/imports/api/creature/creatures/CreatureVariables';
import { getPropertyName } from '/imports/constants/PROPERTIES';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
import numberToSignedString from '/imports/api/utility/numberToSignedString';
import computedSchemas from '/imports/api/properties/computedPropertySchemasIndex';
import applyFnToKey from '/imports/api/engine/computation/utility/applyFnToKey';
import resolve, { map, toString } from '/imports/parser/resolve';
import accessor from '/imports/parser/parseTree/accessor';
import cyrb53 from '/imports/api/engine/computation/utility/cyrb53';
/* eslint-disable @typescript-eslint/no-explicit-any */
@@ -756,6 +760,76 @@ const applyPropertyByType = {
}
},
async buff(task: PropTask, action: Action, userInput) {
const prop = task.prop;
const buffTargets = task.targetIds;
// Mark the buff as dirty for recalculation
prop.dirty = true;
// Then copy the descendants of the buff to the targets
const propList = [prop];
function addChildrenToPropList(children, { skipCrystalize } = { skipCrystalize: false }) {
children.forEach(child => {
if (skipCrystalize) child.node._skipCrystalize = true;
propList.push(child.node);
// recursively add the child's children, but don't crystalize nested buffs
addChildrenToPropList(child.children, {
skipCrystalize: skipCrystalize || child.node.type === 'buff'
});
});
}
addChildrenToPropList(node.children);
if (!prop.skipCrystalization) {
crystalizeVariables({ propList, actionContext });
}
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
CreatureProperties.batchInsert(targetPropList);
//Log the buff
let logValue = prop.description?.value
if (prop.description?.text) {
recalculateInlineCalculations(prop.description, actionContext);
logValue = prop.description?.value;
}
if ((prop.name || prop.description?.value) && !prop.silent) {
if (target._id === actionContext.creature._id) {
// Targeting self
actionContext.addLog({
name: prop.name,
value: logValue,
});
} else {
// Targeting other
insertCreatureLog.call({
log: {
creatureId: target._id,
content: [{
name: prop.name,
value: logValue,
}],
}
});
}
}
});
applyNodeTriggers(node, 'after', actionContext);
applyNodeTriggers(node, 'afterChildren', actionContext);
},
async folder(task: PropTask, action: Action, userInput): Promise<void> {
const prop = task.prop;
return applyDefaultAfterPropTasks(action, prop, task.targetIds, userInput);
@@ -1295,3 +1369,84 @@ async function resetProperties(action: Action, prop: any, result: TaskResult, us
});
}
}
/**
* Replaces all variables with their resolved values
* except variables of the form `~target.thing.total` become `thing.total`
*/
function crystalizeVariables({ propList, actionContext }) {
propList.forEach(prop => {
if (prop._skipCrystalize) {
delete prop._skipCrystalize;
return;
}
// Iterate through all the calculations and crystalize them
computedSchemas[prop.type].computedFields().forEach(calcKey => {
applyFnToKey(prop, calcKey, (prop, key) => {
const calcObj = get(prop, key);
if (!calcObj?.parseNode) return;
calcObj.parseNode = map(calcObj.parseNode, node => {
// Skip nodes that aren't symbols or accessors
if (
node.parseType !== 'accessor'
) return node;
// Handle variables
if (node.name === '~target') {
// strip ~target
if (node.parseType === 'accessor') {
node.name = node.path.shift();
if (!node.path.length) {
return accessor.create({ name: node.name })
}
} else {
// Can't strip symbols
actionContext.addLog({
name: 'Error',
value: 'Variable `~target` should not be used without a property: ~target.property',
});
}
return node;
} else {
// Resolve all other variables
const { result, context } = resolve('reduce', node, actionContext.scope);
logErrors(context.errors, actionContext);
return result;
}
});
calcObj.calculation = toString(calcObj.parseNode);
calcObj.hash = cyrb53(calcObj.calculation);
});
});
// For each key in the schema
computedSchemas[prop.type].inlineCalculationFields().forEach(calcKey => {
// That ends in .inlineCalculations
applyFnToKey(prop, calcKey, (prop, key) => {
const inlineCalcObj = get(prop, key);
if (!inlineCalcObj) return;
// If there is no text, skip
if (!inlineCalcObj.text) {
return;
}
// Replace all the existing calculations
let index = -1;
inlineCalcObj.text = inlineCalcObj.text.replace(INLINE_CALCULATION_REGEX, () => {
index += 1;
return `{${inlineCalcObj.inlineCalculations[index].calculation}}`;
});
// Set the value to the uncomputed string
inlineCalcObj.value = inlineCalcObj.text;
// Write a new hash
const inlineCalcHash = cyrb53(inlineCalcObj.text);
if (inlineCalcHash === inlineCalcObj.hash) {
// Skip if nothing changed
return;
}
inlineCalcObj.hash = inlineCalcHash;
});
});
});
}

View File

@@ -36,14 +36,11 @@
:class="selected && 'primary--text'"
:disabled="expanded"
/>
<!--{{node && node.order}}-->
<span>{{ node.left }}</span>
<tree-node-view
:model="node"
:selected="selected"
:show-external-details="showExternalDetails"
/>
<span>{{ node.right }}</span>
</div>
</div>
<v-expand-transition>