Added damage saving throws to engine
This commit is contained in:
@@ -174,7 +174,11 @@ function rollAttack(attack, scope) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function applyCrits(value, scope) {
|
function applyCrits(value, scope) {
|
||||||
const criticalHitTarget = scope['~criticalHitTarget']?.value || 20;
|
let scopeCrit = scope['~criticalHitTarget']?.value;
|
||||||
|
if (scopeCrit?.parseType === 'constant') {
|
||||||
|
scopeCrit = scopeCrit.value;
|
||||||
|
}
|
||||||
|
const criticalHitTarget = scopeCrit || 20;
|
||||||
let criticalHit = value >= criticalHitTarget;
|
let criticalHit = value >= criticalHitTarget;
|
||||||
let criticalMiss;
|
let criticalMiss;
|
||||||
if (criticalHit) {
|
if (criticalHit) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
} from '/imports/api/engine/loadCreatures.js';
|
} from '/imports/api/engine/loadCreatures.js';
|
||||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||||
import getEffectivePropTags from '/imports/api/engine/computation/utility/getEffectivePropTags.js';
|
import getEffectivePropTags from '/imports/api/engine/computation/utility/getEffectivePropTags.js';
|
||||||
|
import applySavingThrow from '/imports/api/engine/actions/applyPropertyByType/applySavingThrow.js';
|
||||||
|
|
||||||
export default function applyDamage(node, actionContext) {
|
export default function applyDamage(node, actionContext) {
|
||||||
applyNodeTriggers(node, 'before', actionContext);
|
applyNodeTriggers(node, 'before', actionContext);
|
||||||
@@ -36,7 +37,7 @@ export default function applyDamage(node, actionContext) {
|
|||||||
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
|
||||||
applyEffectsToCalculationParseNode(prop.amount, actionContext.log);
|
applyEffectsToCalculationParseNode(prop.amount, actionContext);
|
||||||
const { result: rolled } = resolve('roll', prop.amount.parseNode, scope, context);
|
const { result: rolled } = resolve('roll', prop.amount.parseNode, scope, context);
|
||||||
if (rolled.parseType !== 'constant') {
|
if (rolled.parseType !== 'constant') {
|
||||||
logValue.push(toString(rolled));
|
logValue.push(toString(rolled));
|
||||||
@@ -67,6 +68,7 @@ export default function applyDamage(node, actionContext) {
|
|||||||
|
|
||||||
// Round the damage to a whole number
|
// Round the damage to a whole number
|
||||||
damage = Math.floor(damage);
|
damage = Math.floor(damage);
|
||||||
|
scope['~damage'] = damage;
|
||||||
|
|
||||||
// Convert extra damage into the stored type
|
// Convert extra damage into the stored type
|
||||||
if (prop.damageType === 'extra' && scope['~lastDamageType']?.value) {
|
if (prop.damageType === 'extra' && scope['~lastDamageType']?.value) {
|
||||||
@@ -82,24 +84,74 @@ export default function applyDamage(node, actionContext) {
|
|||||||
prop.damageType +
|
prop.damageType +
|
||||||
(prop.damageType !== 'healing' ? ' damage ' : '');
|
(prop.damageType !== 'healing' ? ' damage ' : '');
|
||||||
|
|
||||||
|
// If there is a save, calculate the save damage
|
||||||
|
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);
|
||||||
|
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
|
||||||
|
if (reduced?.parseType !== 'constant' || !isFinite(reduced.value)) {
|
||||||
|
return applyChildren(node, actionContext);
|
||||||
|
}
|
||||||
|
damageOnSave = +saveDamageResult.value;
|
||||||
|
// Round the damage to a whole number
|
||||||
|
damageOnSave = Math.floor(damageOnSave);
|
||||||
|
} else {
|
||||||
|
damageOnSave = Math.floor(damage / 2);
|
||||||
|
}
|
||||||
|
saveNode = {
|
||||||
|
node: {
|
||||||
|
...prop.save,
|
||||||
|
name: prop.save.stat,
|
||||||
|
silent: prop.silent,
|
||||||
|
},
|
||||||
|
children: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (damageTargets && damageTargets.length) {
|
if (damageTargets && damageTargets.length) {
|
||||||
// Iterate through all the targets
|
// Iterate through all the targets
|
||||||
damageTargets.forEach(target => {
|
damageTargets.forEach(target => {
|
||||||
|
actionContext.target = [target];
|
||||||
|
let damageToApply = damage;
|
||||||
|
|
||||||
|
// If there is a saving throw, apply that first
|
||||||
|
if (prop.save) {
|
||||||
|
applySavingThrow(saveNode, actionContext);
|
||||||
|
if (scope['~saveSucceeded']?.value) {
|
||||||
|
// Log the total damage
|
||||||
|
logValue.push(toString(reduced));
|
||||||
|
// Log the save damage
|
||||||
|
const damageText = damageFunctionText(prop.save);
|
||||||
|
if (damageText) {
|
||||||
|
logValue.push(damageText);
|
||||||
|
} else {
|
||||||
|
logValue.push(
|
||||||
|
'**Damage on successful save**',
|
||||||
|
prop.save.damageFunction.calculation,
|
||||||
|
saveRoll
|
||||||
|
);
|
||||||
|
}
|
||||||
|
damageToApply = damageOnSave;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Apply weaknesses/resistances/immunities
|
// Apply weaknesses/resistances/immunities
|
||||||
damage = applyDamageMultipliers({
|
damageToApply = applyDamageMultipliers({
|
||||||
target,
|
target,
|
||||||
damage,
|
damage: damageToApply,
|
||||||
damageProp: prop,
|
damageProp: prop,
|
||||||
logValue
|
logValue
|
||||||
});
|
});
|
||||||
|
|
||||||
actionContext.target = [target];
|
|
||||||
// Deal the damage to the target
|
// Deal the damage to the target
|
||||||
let damageDealt = dealDamage({
|
let damageDealt = dealDamage({
|
||||||
target,
|
target,
|
||||||
damageType: prop.damageType,
|
damageType: prop.damageType,
|
||||||
amount: damage,
|
amount: damageToApply,
|
||||||
actionContext
|
actionContext
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -124,6 +176,10 @@ export default function applyDamage(node, actionContext) {
|
|||||||
} else {
|
} else {
|
||||||
// There are no targets, just log the result
|
// There are no targets, just log the result
|
||||||
logValue.push(`**${damage}** ${suffix}`);
|
logValue.push(`**${damage}** ${suffix}`);
|
||||||
|
if (prop.save) {
|
||||||
|
applySavingThrow(saveNode, actionContext);
|
||||||
|
logValue.push(`**${damageOnSave}** ${suffix} on a successful save`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!prop.silent) actionContext.addLog({
|
if (!prop.silent) actionContext.addLog({
|
||||||
name: logName,
|
name: logName,
|
||||||
@@ -133,6 +189,16 @@ export default function applyDamage(node, actionContext) {
|
|||||||
return applyChildren(node, actionContext);
|
return applyChildren(node, actionContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function damageFunctionText(save, scope, context, actionContext) {
|
||||||
|
if (!save) return [];
|
||||||
|
if (!save.damageFunction) {
|
||||||
|
return '**Half damage on successful save**';
|
||||||
|
}
|
||||||
|
if (save.damageFunction.calculation == '0' || save.damageFunction.value === 0) {
|
||||||
|
return '**No damage on successful save**'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function applyDamageMultipliers({ target, damage, damageProp, logValue }) {
|
function applyDamageMultipliers({ target, damage, damageProp, logValue }) {
|
||||||
const damageType = damageProp?.damageType;
|
const damageType = damageProp?.damageType;
|
||||||
if (!damageType) return damage;
|
if (!damageType) return damage;
|
||||||
|
|||||||
Reference in New Issue
Block a user