Fixed subtle trigger bugs that break LoV hit dice extension
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user