Fixed subtle trigger bugs that break LoV hit dice extension

This commit is contained in:
Thaum Rystra
2024-05-15 16:06:56 +02:00
parent 4fc897deec
commit 1266794db7
7 changed files with 83 additions and 27 deletions

View File

@@ -15,7 +15,7 @@ export default async function applyAdjustmentProperty(
const damageTargetIds = prop.target === 'self' ? [action.creatureId] : task.targetIds; const damageTargetIds = prop.target === 'self' ? [action.creatureId] : task.targetIds;
if (damageTargetIds.length > 1) { if (damageTargetIds.length > 1) {
return await applyTaskToEachTarget(action, task, damageTargetIds, userInput); return applyTaskToEachTarget(action, task, damageTargetIds, userInput);
} }
// Get the operation and value and push the damage hooks to the queue // Get the operation and value and push the damage hooks to the queue

View File

@@ -41,7 +41,7 @@ export default async function applyDamageProperty(
const logName = prop.damageType === 'healing' ? 'Healing' : 'Damage'; const logName = prop.damageType === 'healing' ? 'Healing' : 'Damage';
// roll the dice only and store that string // roll the dice only and store that string
recalculateCalculation(prop.amount, action, 'compile', inputProvider); await recalculateCalculation(prop.amount, action, 'compile', inputProvider);
const { result: rolled } = await resolve('roll', prop.amount.valueNode, scope, context, inputProvider); const { result: rolled } = await resolve('roll', prop.amount.valueNode, scope, context, inputProvider);
if (rolled.parseType !== 'constant') { if (rolled.parseType !== 'constant') {
logValue.push(toString(rolled)); logValue.push(toString(rolled));
@@ -99,7 +99,7 @@ export default async function applyDamageProperty(
let damageOnSave, saveProp, saveRoll; let damageOnSave, saveProp, saveRoll;
if (prop.save) { if (prop.save) {
if (prop.save.damageFunction?.calculation) { if (prop.save.damageFunction?.calculation) {
recalculateCalculation(prop.save.damageFunction, action, 'compile', inputProvider); await recalculateCalculation(prop.save.damageFunction, action, 'compile', inputProvider);
context.errors = []; context.errors = [];
const { result: saveDamageRolled } = await resolve( const { result: saveDamageRolled } = await resolve(
'roll', prop.save.damageFunction.valueNode, scope, context, inputProvider 'roll', prop.save.damageFunction.valueNode, scope, context, inputProvider

View File

@@ -1,16 +1,26 @@
import { EngineAction } from '/imports/api/engine/action/EngineActions'; import { EngineAction } from '/imports/api/engine/action/EngineActions';
import { applyDefaultAfterPropTasks } from '/imports/api/engine/action/functions/applyTaskGroups'; import { applyDefaultAfterPropTasks } from '/imports/api/engine/action/functions/applyTaskGroups';
import recalculateInlineCalculations from '/imports/api/engine/action/functions/recalculateInlineCalculations';
import { PropTask } from '/imports/api/engine/action/tasks/Task'; import { PropTask } from '/imports/api/engine/action/tasks/Task';
import getPropertyTitle from '/imports/api/utility/getPropertyTitle'; import getPropertyTitle from '/imports/api/utility/getPropertyTitle';
export default async function applyTriggerProperty( export default async function applyTriggerProperty(
task: PropTask, action: EngineAction, result, userInput task: PropTask, action: EngineAction, result, userInput
): Promise<void> { ): Promise<void> {
const prop = task.prop; const prop = task.prop;
result.appendLog({ const logContent = {
name: getPropertyTitle(prop), name: getPropertyTitle(prop),
...prop.silent && { silenced: true }, ...prop.silent && { silenced: true },
}) }
// Add the trigger description to the log
if (prop.description?.text) {
await recalculateInlineCalculations(prop.description, action, 'reduce', userInput);
if (prop.description.value) {
logContent.value = prop.description.value;
}
}
result.appendLog(logContent);
return applyDefaultAfterPropTasks(action, prop, task.targetIds, userInput); return applyDefaultAfterPropTasks(action, prop, task.targetIds, userInput);
} }

View File

@@ -131,7 +131,7 @@ export async function applyAfterPropTasksForSomeChildren(
export async function applyTriggers( export async function applyTriggers(
action: EngineAction, prop, targetIds: string[], triggerPath: string, inputProvider: InputProvider action: EngineAction, prop, targetIds: string[], triggerPath: string, inputProvider: InputProvider
) { ) {
const triggerIds = get(prop?.triggers, triggerPath); const triggerIds = get(prop?.triggerIds, triggerPath);
if (!triggerIds) return; if (!triggerIds) return;
for (const triggerId of triggerIds) { for (const triggerId of triggerIds) {
const trigger = await getSingleProperty(action.creatureId, triggerId); const trigger = await getSingleProperty(action.creatureId, triggerId);

View File

@@ -10,7 +10,6 @@ import numberToSignedString from '/imports/api/utility/numberToSignedString';
export default async function applyDamagePropTask( export default async function applyDamagePropTask(
task: DamagePropTask, action: EngineAction, result: TaskResult, userInput task: DamagePropTask, action: EngineAction, result: TaskResult, userInput
): Promise<number> { ): Promise<number> {
const prop = task.prop; const prop = task.prop;
if (task.targetIds.length > 1) { if (task.targetIds.length > 1) {
@@ -43,7 +42,11 @@ export default async function applyDamagePropTask(
} }
// Run the before triggers which may change scope properties // Run the before triggers which may change scope properties
await applyTriggers(action, targetProp, [action.creatureId], 'damageProperty.before', userInput); await applyTriggers(action, targetProp, [action.creatureId], 'before', userInput);
// Create a new result after triggers have run
result = new TaskResult(task.prop._id, task.targetIds);
action.results.push(result);
// Refetch the scope properties // Refetch the scope properties
const scope = await getEffectiveActionScope(action); const scope = await getEffectiveActionScope(action);
@@ -105,6 +108,7 @@ export default async function applyDamagePropTask(
...prop.silent && { silenced: true }, ...prop.silent && { silenced: true },
}] }]
}); });
setScope(result, targetProp, newValue, damage);
} else if (operation === 'increment') { } else if (operation === 'increment') {
const currentValue = targetProp.value || 0; const currentValue = targetProp.value || 0;
const currentDamage = targetProp.damage || 0; const currentDamage = targetProp.damage || 0;
@@ -130,7 +134,28 @@ export default async function applyDamagePropTask(
...prop.silent && { silenced: true }, ...prop.silent && { silenced: true },
}] }]
}); });
setScope(result, targetProp, newValue, damage);
} }
await applyTriggers(action, prop, [action.creatureId], 'damageProperty.after', userInput); await applyTriggers(action, targetProp, [action.creatureId], 'after', userInput);
await applyTriggers(action, targetProp, [action.creatureId], 'afterChildren', userInput);
return increment; return increment;
} }
// Update the scope with the attribute, but updated to the new value
// TODO ideally we re-write the getEffectiveActionScope code to be more
// getSomethingFromScope which does the same work, but for a single key, and includes all
// updates to the doc returned that are already applied in the result array
function setScope(result, targetProp, newValue, damage) {
// This isn't the defining property, don't bother
if (targetProp.overridden) return;
const key = targetProp.variableName;
// No variable name can't set scope
if (!key) return;
// Make sure scope is defined
if (!result.scope) result.scope = {};
result.scope[key] = {
...EJSON.clone(targetProp),
value: newValue,
damage,
};
}

View File

@@ -8,17 +8,32 @@ export default function computeTrigger(computation, node) {
if (prop.inactive) return; if (prop.inactive) return;
// Link triggers to all the properties that would fire them when applied // Link triggers to all the properties that would fire them when applied
if (prop.event === 'doActionProperty') { const tagTargets = getEffectTagTargets(prop, computation);
getEffectTagTargets(prop, computation).forEach(targetId => { switch (prop.event) {
const targetProp = computation.propsById[targetId]; case 'doActionProperty':
// Only apply if the trigger matches this property type tagTargets.forEach(targetId => {
if (targetProp.type !== prop.actionPropertyType) return; const targetProp = computation.propsById[targetId];
let triggerIdArray = get(targetProp, `triggerIds.${prop.timing}`); // Only apply if the trigger matches this property type
if (!triggerIdArray) { if (targetProp.type !== prop.actionPropertyType) return;
triggerIdArray = []; setTrigger(prop, targetProp);
set(targetProp, `triggerIds.${prop.timing}`, triggerIdArray); });
} break;
triggerIdArray.push(prop._id); case 'damageProperty':
}); tagTargets.forEach(targetId => {
const targetProp = computation.propsById[targetId];
// Only apply to attributes
if (targetProp.type !== 'attribute') return;
setTrigger(prop, targetProp);
});
break;
} }
} }
function setTrigger(prop, targetProp) {
let triggerIdArray = get(targetProp, `triggerIds.${prop.timing}`);
if (!triggerIdArray) {
triggerIdArray = [];
set(targetProp, `triggerIds.${prop.timing}`, triggerIdArray);
}
triggerIdArray.push(prop._id);
}

View File

@@ -19,12 +19,18 @@ export default function bulkWrite(bulkWriteOps, collection): void | Promise<any>
// latency compensation and causes flickering // latency compensation and causes flickering
function writePropertiesSequentially(bulkWriteOps: any[], collection: Mongo.Collection<any>) { function writePropertiesSequentially(bulkWriteOps: any[], collection: Mongo.Collection<any>) {
bulkWriteOps.forEach(op => { bulkWriteOps.forEach(op => {
const insertOne = op.insertOne;
if (insertOne) {
collection.insert(insertOne);
}
const updateOneOrMany = op.updateOne || op.updateMany; const updateOneOrMany = op.updateOne || op.updateMany;
collection.update(updateOneOrMany.filter, updateOneOrMany.update, { if (updateOneOrMany) {
// The bulk code is bypassing validation, so do the same here collection.update(updateOneOrMany.filter, updateOneOrMany.update, {
// @ts-expect-error Collection 2 has no typescript support // The bulk code is bypassing validation, so do the same here
bypassCollection2: true, // @ts-expect-error Collection 2 has no typescript support
}); bypassCollection2: true,
});
}
}); });
} }