161 lines
5.9 KiB
TypeScript
161 lines
5.9 KiB
TypeScript
import { EngineAction } from '/imports/api/engine/action/EngineActions';
|
|
import { DamagePropTask } from '/imports/api/engine/action/tasks/Task';
|
|
import TaskResult from '/imports/api/engine/action/tasks/TaskResult';
|
|
import { applyTriggers } from '/imports/api/engine/action/functions/applyTaskGroups';
|
|
import { getEffectiveActionScope } from '/imports/api/engine/action/functions/getEffectiveActionScope';
|
|
import getPropertyTitle from '/imports/api/utility/getPropertyTitle';
|
|
import { getSingleProperty } from '/imports/api/engine/loadCreatures';
|
|
import numberToSignedString from '/imports/api/utility/numberToSignedString';
|
|
|
|
export default async function applyDamagePropTask(
|
|
task: DamagePropTask, action: EngineAction, result: TaskResult, userInput
|
|
): Promise<number> {
|
|
if (task.targetIds.length > 1) {
|
|
throw 'This subtask can only be called on a single target';
|
|
}
|
|
const targetId = task.targetIds[0];
|
|
|
|
let { value } = task.params;
|
|
const { title, operation } = task.params;
|
|
let targetProp = task.params.targetProp;
|
|
|
|
if (!targetProp) throw new Meteor.Error('not-found', 'Target property is required')
|
|
|
|
// Set the scope properties
|
|
result.pushScope = {};
|
|
if (operation === 'increment') {
|
|
if (value >= 0) {
|
|
result.pushScope['~damage'] = { value };
|
|
} else {
|
|
result.pushScope['~healing'] = { value: -value };
|
|
}
|
|
} else {
|
|
result.pushScope['~set'] = { value };
|
|
}
|
|
// Store which property we're targeting
|
|
if (targetId === action.creatureId) {
|
|
result.pushScope['~attributeDamaged'] = { _propId: targetProp._id };
|
|
} else {
|
|
result.pushScope['~attributeDamaged'] = targetProp;
|
|
}
|
|
|
|
// Run the before triggers which may change scope properties
|
|
await applyTriggers(action, targetProp, [targetId], 'damageTriggerIds.before', userInput);
|
|
|
|
// Create a new result after triggers have run
|
|
result = new TaskResult(task.targetIds);
|
|
action.results.push(result);
|
|
|
|
// Refetch the scope properties
|
|
const scope = await getEffectiveActionScope(action);
|
|
result.popScope = {
|
|
'~damage': 1, '~healing': 1, '~set': 1, '~attributeDamaged': 1,
|
|
};
|
|
value = +value;
|
|
if (operation === 'increment') {
|
|
if (value >= 0) {
|
|
value = scope['~damage']?.value;
|
|
} else {
|
|
value = -scope['~healing']?.value;
|
|
}
|
|
} else {
|
|
value = scope['~set']?.value;
|
|
}
|
|
const targetPropId = scope['~attributeDamaged']?._propId ??
|
|
scope['~attributeDamaged']?._id;
|
|
|
|
// If there are no targets, just log the result that would apply and end
|
|
if (!task.targetIds?.length) {
|
|
// Get the locally equivalent stat with the same variable name
|
|
const statName = getPropertyTitle(targetProp);
|
|
result.appendLog({
|
|
name: title,
|
|
value: `${statName}${operation === 'set' ? ' set to' : ''}` +
|
|
` ${value}`,
|
|
inline: true,
|
|
silenced: task.silent ?? false,
|
|
}, task.targetIds);
|
|
}
|
|
|
|
let damage, newValue, increment;
|
|
targetProp = await getSingleProperty(targetId, targetPropId);
|
|
|
|
if (!targetProp) return value;
|
|
|
|
if (operation === 'set') {
|
|
const total = targetProp.total || 0;
|
|
// Set represents what we want the value to be after damage
|
|
// So we need the actual damage to get to that value
|
|
damage = total - value;
|
|
// Damage can't exceed total value
|
|
if (damage > total && !targetProp.ignoreLowerLimit) damage = total;
|
|
// Damage must be positive
|
|
if (damage < 0 && !targetProp.ignoreUpperLimit) damage = 0;
|
|
newValue = targetProp.total - damage;
|
|
// Write the results
|
|
result.mutations.push({
|
|
targetIds: [targetId],
|
|
updates: [{
|
|
propId: targetProp._id,
|
|
set: { damage, value: newValue },
|
|
type: targetProp.type,
|
|
}],
|
|
contents: [{
|
|
name: title,
|
|
value: `${getPropertyTitle(targetProp)} set from ${targetProp.value} to ${value}`,
|
|
inline: true,
|
|
...task.silent && { silenced: true },
|
|
}]
|
|
});
|
|
if (targetId === action.creatureId) setScope(result, targetProp, newValue, damage);
|
|
} else if (operation === 'increment') {
|
|
const currentValue = targetProp.value || 0;
|
|
const currentDamage = targetProp.damage || 0;
|
|
increment = value;
|
|
// Can't increase damage above the remaining value
|
|
if (increment > currentValue && !targetProp.ignoreLowerLimit) increment = currentValue;
|
|
// Can't decrease damage below zero
|
|
if (-increment > currentDamage && !targetProp.ignoreUpperLimit) increment = -currentDamage;
|
|
damage = currentDamage + increment;
|
|
newValue = targetProp.total - damage;
|
|
// Write the results
|
|
result.mutations.push({
|
|
targetIds: [targetId],
|
|
updates: [{
|
|
propId: targetProp._id,
|
|
inc: { damage: increment, value: -increment },
|
|
type: targetProp.type,
|
|
}],
|
|
contents: [{
|
|
name: increment >= 0 ? 'Attribute damaged' : 'Attribute restored',
|
|
value: `${numberToSignedString(-increment)} ${getPropertyTitle(targetProp)}`,
|
|
inline: true,
|
|
...task.silent && { silenced: true },
|
|
}]
|
|
});
|
|
if (targetId === action.creatureId) setScope(result, targetProp, newValue, damage);
|
|
}
|
|
await applyTriggers(action, targetProp, [targetId], 'damageTriggerIds.after', userInput);
|
|
await applyTriggers(action, targetProp, [targetId], 'damageTriggerIds.afterChildren', userInput);
|
|
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,
|
|
};
|
|
}
|