From 04de76d20e37916b4f60245ade3ae0762cdb00a1 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Wed, 14 Jun 2023 13:56:44 +0200 Subject: [PATCH] Skills can now apply to calcs by tag --- .../buildComputation/linkTypeDependencies.js | 16 ++++++- .../computeByType/computeCalculation.js | 47 ++++++++++++------- app/imports/api/properties/Actions.js | 1 - app/imports/api/properties/Skills.js | 45 ++++++++++++++++++ .../proficiencies/InlineProficiency.vue | 23 +++------ .../client/ui/properties/forms/ActionForm.vue | 5 +- .../client/ui/properties/forms/SkillForm.vue | 20 ++++++++ .../properties/forms/shared/ComputedField.vue | 12 ++++- .../viewers/shared/PropertyField.vue | 2 +- 9 files changed, 131 insertions(+), 40 deletions(-) diff --git a/app/imports/api/engine/computation/buildComputation/linkTypeDependencies.js b/app/imports/api/engine/computation/buildComputation/linkTypeDependencies.js index 33034cf3..4913dc7d 100644 --- a/app/imports/api/engine/computation/buildComputation/linkTypeDependencies.js +++ b/app/imports/api/engine/computation/buildComputation/linkTypeDependencies.js @@ -306,7 +306,7 @@ function linkSavingThrow(dependencyGraph, prop) { dependOnCalc({ dependencyGraph, prop, key: 'dc' }); } -function linkSkill(dependencyGraph, prop) { +function linkSkill(dependencyGraph, prop, computation) { // Depends on base value dependOnCalc({ dependencyGraph, prop, key: 'baseValue' }); // Link dependents @@ -318,6 +318,20 @@ function linkSkill(dependencyGraph, prop) { } // Skills depend on the creature's proficiencyBonus dependencyGraph.addLink(prop._id, 'proficiencyBonus', 'skillProficiencyBonus'); + + // Skills can apply their value as a proficiency bonus to calculations based on tag + if (prop.targetByTags) { + getEffectTagTargets(prop, computation).forEach(targetId => { + const targetProp = computation.propsById[targetId]; + // Always target a field on the target property, applying a skill to an attribute or + // other skill isn't supported + const key = prop.targetField || getDefaultCalculationField(targetProp); + const calcObj = get(targetProp, key); + if (calcObj && calcObj.calculation) { + dependencyGraph.addLink(`${targetProp._id}.${key}`, prop._id, 'proficiency'); + } + }); + } } function linkSlot(dependencyGraph, prop) { diff --git a/app/imports/api/engine/computation/computeComputation/computeByType/computeCalculation.js b/app/imports/api/engine/computation/computeComputation/computeByType/computeCalculation.js index 3f6367c3..f695b25d 100644 --- a/app/imports/api/engine/computation/computeComputation/computeByType/computeCalculation.js +++ b/app/imports/api/engine/computation/computeComputation/computeByType/computeCalculation.js @@ -1,4 +1,5 @@ import evaluateCalculation from '../../utility/evaluateCalculation.js'; +import { getPropertyName } from '/imports/constants/PROPERTIES.js'; export default function computeCalculation(computation, node) { const calcObj = node.data; @@ -54,6 +55,9 @@ function aggregateCalculationProficiencies(node, computation) { const calcObj = node.data; delete calcObj.proficiencies; delete calcObj.proficiency; + let profBonus = computation.scope['proficiencyBonus']?.value || 0; + + // Go through all the links and collect them on the calculation computation.dependencyGraph.forEachLinkedNode( node.id, (linkedNode, link) => { @@ -61,40 +65,51 @@ function aggregateCalculationProficiencies(node, computation) { if (link.data !== 'proficiency') return; // That have data if (!linkedNode.data) return; - // Ignore inactive props + // Ignoring inactive props if (linkedNode.data.inactive) return; + // Compute the proficiency and value + let proficiency, value; + if (linkedNode.data.type === 'proficiency') { + proficiency = linkedNode.data.value || 0; + // Multiply the proficiency bonus by the actual proficiency + if (proficiency === 0.49) { + // Round down proficiency bonus in the special case + value = Math.floor(profBonus * 0.5); + } else { + value = Math.ceil(profBonus * proficiency); + } + } else if (linkedNode.data.type === 'skill') { + value = linkedNode.data.value || 0; + proficiency = linkedNode.data.proficiency || 0; + } // Collate proficiencies calcObj.proficiencies = calcObj.proficiencies || []; calcObj.proficiencies.push({ _id: linkedNode.data._id, name: linkedNode.data.name, - value: linkedNode.data.value, + type: linkedNode.data.type, + proficiency, + value, }); }, true // enumerate only outbound links ); + + // Apply the highest proficiency, marking all others as overridden if (calcObj.proficiencies && typeof calcObj.value === 'number') { calcObj.proficiency = 0; + calcObj.proficiencyBonus = 0; let currentProf; calcObj.proficiencies.forEach(prof => { - if (prof.value > calcObj.proficiency) { + if (prof.value > calcObj.proficiencyBonus) { if (currentProf) currentProf.overridden = true; - calcObj.proficiency = prof.value; + calcObj.proficiencyBonus = prof.value; + calcObj.proficiency = prof.proficiency; + currentProf = prof; } else { prof.overridden = true; } }); - // Get the character's proficiency bonus to apply - let profBonus = computation.scope['proficiencyBonus']?.value || 0; - calcObj.proficiencyBonus = profBonus; - let totalBonus; - // Multiply the proficiency bonus by the actual proficiency - if (calcObj.proficiency === 0.49) { - // Round down proficiency bonus in the special case - totalBonus = Math.floor(profBonus * 0.5); - } else { - totalBonus = Math.ceil(profBonus * calcObj.proficiency); - } - calcObj.value += totalBonus; + calcObj.value += calcObj.proficiencyBonus; } } diff --git a/app/imports/api/properties/Actions.js b/app/imports/api/properties/Actions.js index d4a5c4b4..3de84914 100644 --- a/app/imports/api/properties/Actions.js +++ b/app/imports/api/properties/Actions.js @@ -50,7 +50,6 @@ let ActionSchema = createPropertySchema({ attackRoll: { type: 'fieldToCompute', optional: true, - defaultValue: 'strength.modifier + proficiencyBonus', }, // Calculation of how many times this action can be used uses: { diff --git a/app/imports/api/properties/Skills.js b/app/imports/api/properties/Skills.js index a2083cdf..ecd9dd20 100644 --- a/app/imports/api/properties/Skills.js +++ b/app/imports/api/properties/Skills.js @@ -59,6 +59,51 @@ let SkillSchema = createPropertySchema({ type: 'inlineCalculationFieldToCompute', optional: true, }, + // Skills can apply their value to other calculations as a proficiency + // True when applying skill to tagged props + targetByTags: { + type: Boolean, + optional: true, + }, + // Which tags the proficiency is applied to + targetTags: { + type: Array, + optional: true, + maxCount: STORAGE_LIMITS.tagCount, + }, + 'targetTags.$': { + type: String, + max: STORAGE_LIMITS.tagLength, + }, + extraTags: { + type: Array, + optional: true, + maxCount: STORAGE_LIMITS.extraTagsCount, + }, + 'extraTags.$': { + type: Object, + }, + 'extraTags.$._id': { + type: String, + regEx: SimpleSchema.RegEx.Id, + autoValue() { + if (!this.isSet) return Random.id(); + } + }, + 'extraTags.$.operation': { + type: String, + allowedValues: ['OR', 'NOT'], + defaultValue: 'OR', + }, + 'extraTags.$.tags': { + type: Array, + defaultValue: [], + maxCount: STORAGE_LIMITS.tagCount, + }, + 'extraTags.$.tags.$': { + type: String, + max: STORAGE_LIMITS.tagLength, + }, }); let ComputedOnlySkillSchema = createPropertySchema({ diff --git a/app/imports/client/ui/properties/components/proficiencies/InlineProficiency.vue b/app/imports/client/ui/properties/components/proficiencies/InlineProficiency.vue index 72ee4c01..7af9155c 100644 --- a/app/imports/client/ui/properties/components/proficiencies/InlineProficiency.vue +++ b/app/imports/client/ui/properties/components/proficiencies/InlineProficiency.vue @@ -7,7 +7,7 @@ >
@@ -16,7 +16,7 @@ - {{ proficiencyValue }} + {{ displayedValue }} {{ displayedText }} @@ -26,34 +26,25 @@