# PF1 System Damage Application Analysis ## 1. WHERE `applyDamage` IS DEFINED ### Primary Definition - **File**: `c:\DEV\Foundry2\Foundry_VTT\src\foundryvtt-pathfinder1-v10.8\module\documents\actor\actor-pf.mjs` - **Line**: 3780 (instance method) and 3809 (static method) ### Instance Method ```javascript // Line 3780 async applyDamage(value, options = {}) { return this.constructor.applyDamage( value, foundry.utils.mergeObject(options, { targets: [this], }) ); } ``` ### Static Method ```javascript // Line 3809 static async applyDamage( value = 0, { forceDialog = false, reductionDefault = "", asNonlethal = false, targets = null, critMult = 0, dualHeal = false, asWounds = false, instances = [], event, element, message = null, } = {} ) ``` ## 2. METHOD PARAMETERS & OPTIONS STRUCTURE ### Method Signature ```javascript static async applyDamage(value = 0, options = {}) ``` ### Parameters - **value** (number): The damage amount to apply - Positive = damage - Negative = healing - Returns warning if 0 or not finite ### Options Object Structure ```javascript { forceDialog: false, // Forces dialog even if Shift not pressed reductionDefault: "", // Default damage reduction value asNonlethal: false, // Marks damage as nonlethal targets: null, // Array of Token|Actor to apply damage to critMult: 0, // Critical multiplier (for Wounds & Vigor variant) dualHeal: false, // If healing, also heals nonlethal damage asWounds: false, // Apply to wounds directly (Wounds & Vigor variant) instances: [], // Individual damage instances (not currently processed) event, // Triggering event object element, // Triggering HTML element message: null, // ChatMessage reference (for modules) } ``` ### Return Value ```javascript Promise // False if cancelled, array of updated actors if successful ``` ## 3. HOW DAMAGE IS APPLIED FROM CHAT CARDS ### Chat Card Button Flow **Template Files**: - `simple-damage.hbs` (line 19-20): Simple damage/healing cards - `attack-roll.hbs`: Attack roll cards with damage buttons **Button HTML**: ```handlebars ``` Or inline actions: ```handlebars Apply ``` ### Click Handler Chain 1. **Item Chat Handler** (`item-pf.mjs`, line 1637): ```javascript html.on("click", ".card-buttons button, .inline-action", this._onChatCardButton.bind(this)); ``` 2. **_onChatCardButton** (`item-pf.mjs`, line 1643): - Extracts button data and message - Checks permissions - Routes to `_onChatCardAction` if action is "applyDamage" 3. **_onChatCardAction** (`item-pf.mjs`, line 1688): - Extracts damage value from `button.dataset.value` - Gets attack data from `message.systemRolls.attacks` - Builds damage instances array - **Calls**: `pf1.documents.actor.ActorPF.applyDamage(value, options)` ### Extracted Data at Application Point ```javascript // From item-pf.mjs line 1723-1731 pf1.documents.actor.ActorPF.applyDamage(value, { asNonlethal, // from button.dataset.tags event, // click event element: button, // button element message, // ChatMessage object isCritical, // attack type === "critical" critMult: isCritical ? metadata.config.critMult ?? 0 : 0, instances, // damage roll instances }); ``` ### Alternative Entry Point: Text Enrichers **File**: `text-enrichers.mjs`, line 480 ```javascript actor.applyDamage(value, { ...options, event, element: target }); ``` - Used for inline damage/heal links in chat - Creates damage instances from formula evaluation ## 4. CHAT MESSAGE FLAGS STRUCTURE ### Message Flags ```javascript message.flags.pf1.metadata = { action: itemActionId, // Line 36: ItemAction UUID item: itemId, // Line 46: ItemPF ID template: templateId, // Line 67: MeasuredTemplate ID targets: [uuid, uuid, ...], // Line 78: Target actor/token UUIDs rolls: { // Line 97: Rolls data attacks: [ { damage: [ { total: 10, damageType: "slashing" } ], critDamage: [ { total: 20, damageType: "slashing" } ], } ] }, config: { critMult: 2, // Critical multiplier } } message.flags.pf1.subject = { health: "damage" | "healing" // Line 152: Type of application } message.flags.pf1.ammoRecovery = { [attackIndex]: { [ammoId]: { recovered: count } } } ``` ### Relevant Message Properties ```javascript message.systemRolls // Chat message rolls (init'd from flags.pf1.metadata.rolls) message.targets // Property getter that extracts targets from flags (line 77) ``` ## 5. ALTERNATIVE DAMAGE APPLICATION PATHS ### Instance Method Wrapper **File**: `actor-pf.mjs`, line 3780 ```javascript async applyDamage(value, options = {}) { return this.constructor.applyDamage(value, { targets: [this], ...options }); } ``` - Used to apply damage to specific actor instance - Automatically sets targets to self - All options passed through ### Direct Actor Update Alternative Not a separate method, but damage internally calls: ```javascript a.update({ "system.attributes.hp.value": newHP, "system.attributes.hp.nonlethal": newNL, "system.attributes.hp.temp": newTemp, // Or for Wounds & Vigor: "system.attributes.vigor.value": newHP, "system.attributes.wounds.value": newWounds, }) ``` ### No Hook Integration - **Hooks searched**: None found in applyDamage - **Pre-hooks**: None (could wrap to intercept) - **Post-hooks**: None (could call custom hook after update) ## 6. KEY IMPLEMENTATION DETAILS ### Health System Support - **Normal HP Mode**: Uses `system.attributes.hp` - **Wounds & Vigor Mode**: Uses `system.attributes.vigor` + `system.attributes.wounds` - Determined by: `healthConfig.variants[actorType].useWoundsAndVigor` ### Damage Flow (Normal Mode) 1. Nonlethal damage applied first 2. Temp HP reduced before actual HP 3. Remaining damage reduces actual HP 4. Cannot go below 0 ### Damage Flow (Wounds & Vigor Mode) 1. Vigor (temp health) reduced first 2. Wounds (permanent damage) calculated 3. Critical multiplier applied if applicable 4. Wounds clamped to [0, max] ### Dialog Display - Only shown if `forceDialog` is true OR Shift key pressed - Allows user to: - Select target tokens - Enter custom damage reduction - Choose damage multiplier (normal/half) - Apply to specific targets ### Permissions Check ```javascript if (!a.isOwner) { ui.notifications.warn(`No permission for ${actor.name}`); continue; // Skip this actor } ``` ## 7. WRAPPING POINTS FOR INTERCEPTION ### Recommended Hooks to Add 1. **Pre-damage hook**: Before `actor.update()` calls ```javascript Hooks.call("pf1.preDamageApplied", actor, updateData, options) ``` 2. **Post-damage hook**: After all updates complete ```javascript Hooks.call("pf1.damageApplied", updatedActors, options) ``` ### Intercept Location - Line 3950 in actor-pf.mjs: `promises.push(a.update(updateData))` - Could wrap this to modify updateData or add custom logging ### For Gowler's Tracking Ledger - **Best approach**: Add post-damage hook - Call `actor.getFlag("pf1", "gowlers-tracking")` to get ledger - Log damage with timestamp and source - Store in actor flags or external system