diff --git a/lethal-fantasy.mjs b/lethal-fantasy.mjs index 910a70a..6db12b5 100644 --- a/lethal-fantasy.mjs +++ b/lethal-fantasy.mjs @@ -265,6 +265,10 @@ Hooks.on(hookName, (message, html, data) => { } // Préparer le message de demande de défense + // Récupérer l'acteur attaquant pour vérifier le mode d'attaque + const attacker = game.actors.get(attackerId) + const isRangedAttack = attacker?.type === "monster" && attacker.system.attackMode === "ranged" + const defenseMsg = { type: "requestDefense", attackerName: attackerName, @@ -279,7 +283,8 @@ Hooks.on(hookName, (message, html, data) => { attackD30message: attackD30message, attackRerollContext: attackRerollContext, combatantId: combatantId, - tokenId: tokenId + tokenId: tokenId, + isRanged: isRangedAttack } // Envoyer le message socket à l'utilisateur contrôlant le combatant @@ -292,7 +297,6 @@ Hooks.on(hookName, (message, html, data) => { } // Récupérer l'acteur attaquant pour vérifier qui l'a lancé - const attacker = game.actors.get(attackerId) const attackerOwners = attacker ? game.users.filter(u => attacker.testUserPermission(u, "OWNER")).map(u => u.id) : [] // Monsters always need their owner (usually the GM) to roll a save/defense, diff --git a/module/config/d30_results_tables.json b/module/config/d30_results_tables.json index c8b6741..3fdffe7 100644 --- a/module/config/d30_results_tables.json +++ b/module/config/d30_results_tables.json @@ -4,70 +4,171 @@ "melee_attack": { "type": "choice", "choices": [ - { "type": "special_strike", "options": ["lethal", "vital"] }, - { "type": "bonus_dice", "dice": "D20E", "target": "attack" } + { + "type": "special_strike", + "options": [ + "lethal", + "vital" + ] + }, + { + "type": "bonus_dice", + "dice": "D20E", + "target": "attack" + } ], "description": "Possible Lethal or Vital Strike or Add D20E to Attack" }, "ranged_attack": { "type": "choice", "choices": [ - { "type": "special_strike", "options": ["lethal", "vital"] }, - { "type": "bonus_dice", "dice": "D20E", "target": "attack" } + { + "type": "special_strike", + "options": [ + "lethal", + "vital" + ] + }, + { + "type": "bonus_dice", + "dice": "D20E", + "target": "attack" + } ], "description": "Possible Lethal or Vital Strike or Add D20E to Attack" }, "melee_defense": { "type": "choice", "choices": [ - { "type": "special_defense", "options": ["flawless", "legendary"] }, - { "type": "bonus_dice", "dice": "D20E", "target": "defense" } + { + "type": "special_defense", + "options": [ + "flawless", + "legendary" + ] + }, + { + "type": "bonus_dice", + "dice": "D20E", + "target": "defense" + } ], "description": "Possible Flawless or Legendary Defense or Add D20E to Defense" }, "arcane_spell_attack": { "type": "choice", "choices": [ - { "type": "special_strike", "options": ["lethal_magical", "vital_magical"] }, - { "type": "bonus_dice", "dice": "D20E", "target": "spell_attack" } + { + "type": "special_strike", + "options": [ + "lethal_magical", + "vital_magical" + ] + }, + { + "type": "bonus_dice", + "dice": "D20E", + "target": "spell_attack" + } ], "description": "Possible Lethal or Vital Magical Strike or Add D20E to Spell Attack" }, "arcane_spell_defense": { "type": "choice", "choices": [ - { "type": "spell_calamity" }, - { "type": "bonus_dice", "dice": "D20E", "target": "spell_defense" } + { + "type": "spell_calamity" + }, + { + "type": "bonus_dice", + "dice": "D20E", + "target": "spell_defense" + } ], "description": "Possible Spell Catastrophe or adds D20E to Spell Defense" }, "skill_rolls": { "type": "skill_auto_success", "description": "Skill Succeeds Regardless of Opposing Roll" + }, + "ranged_defense": { + "type": "choice", + "choices": [ + { + "type": "special_defense", + "options": [ + "flawless", + "legendary" + ] + }, + { + "type": "bonus_dice", + "dice": "D20E", + "target": "defense" + } + ], + "description": "Possible Flawless or Legendary Defense or Add D20E to Defense" } }, "29": { - "melee_attack": { "type": "gain_grit", "amount": 1, "description": "Gain 1 Grit" }, - "ranged_attack": { "type": "gain_grit", "amount": 1, "description": "Gain 1 Grit" }, - "melee_defense": { "type": "gain_grit", "amount": 1, "description": "Gain 1 Grit" }, - "arcane_spell_attack": { "type": "gain_grit", "amount": 1, "description": "Gain 1 Grit" }, - "arcane_spell_defense": { "type": "gain_grit", "amount": 1, "description": "Gain 1 Grit" }, - "skill_rolls": { "type": "gain_grit", "amount": 1, "description": "Gain 1 Grit" } + "melee_attack": { + "type": "gain_grit", + "amount": 1, + "description": "Gain 1 Grit" + }, + "ranged_attack": { + "type": "gain_grit", + "amount": 1, + "description": "Gain 1 Grit" + }, + "melee_defense": { + "type": "gain_grit", + "amount": 1, + "description": "Gain 1 Grit" + }, + "arcane_spell_attack": { + "type": "gain_grit", + "amount": 1, + "description": "Gain 1 Grit" + }, + "arcane_spell_defense": { + "type": "gain_grit", + "amount": 1, + "description": "Gain 1 Grit" + }, + "skill_rolls": { + "type": "gain_grit", + "amount": 1, + "description": "Gain 1 Grit" + }, + "ranged_defense": { + "type": "gain_grit", + "amount": 1, + "description": "Gain 1 Grit" + } }, "28": { - "melee_attack": { "type": "shield_destruction", "description": "Shield Destruction" } + "melee_attack": { + "type": "shield_destruction", + "description": "Shield Destruction" + } }, "27": { "melee_attack": { - "type": "bonus_dice", "dice": "D6", "target": "attack", + "type": "bonus_dice", + "dice": "D6", + "target": "attack", "description": "Granted D6 (1-6) Attack Modifier for This Melee Attack" }, "ranged_attack": { - "type": "bonus_dice", "dice": "D6", "target": "attack", + "type": "bonus_dice", + "dice": "D6", + "target": "attack", "description": "Granted D6 (1-6) Attack Modifier for This Ranged Attack" }, "melee_defense": { - "type": "luck_die", "scope": "combat", + "type": "luck_die", + "scope": "combat", "description": "Granted 1 Luck dice for Use in This Combat Only" }, "arcane_spell_attack": { @@ -75,26 +176,42 @@ "description": "No Spell Lethargy the Aether Approves of Characters Efforts" }, "arcane_spell_defense": { - "type": "flash_of_pain", "duration_dice": "1D6E", "target": "caster", + "type": "flash_of_pain", + "duration_dice": "1D6E", + "target": "caster", "description": "Caster Suffers Severe pain and will be under a flash of pain for 1D6E seconds" + }, + "ranged_defense": { + "type": "luck_die", + "scope": "combat", + "description": "Granted 1 Luck dice for Use in This Combat Only" } }, "26": { - "melee_attack": { "type": "shield_destruction", "description": "Shield Destruction" } + "melee_attack": { + "type": "shield_destruction", + "description": "Shield Destruction" + } }, "25": { "skill_rolls": { - "type": "bonus_flat", "amount": 1, "target": "skill", + "type": "bonus_flat", + "amount": 1, + "target": "skill", "description": "Add 1 to Skill Roll" } }, "21": { "melee_attack": { - "type": "flash_of_pain", "duration_dice": "1D6E", "target": "defender", + "type": "flash_of_pain", + "duration_dice": "1D6E", + "target": "defender", "description": "Hit Inflicts Flash of Pain 1D6E seconds" }, "ranged_attack": { - "type": "flash_of_pain", "duration_dice": "1D6E", "target": "defender", + "type": "flash_of_pain", + "duration_dice": "1D6E", + "target": "defender", "description": "Hit Inflicts Flash of Pain 1D6E seconds" }, "melee_defense": { @@ -102,176 +219,391 @@ "description": "Defender Recovers or ignores any flash of pain" }, "arcane_spell_attack": { - "type": "flash_of_pain", "duration_dice": "1D6E", "target": "defender", + "type": "flash_of_pain", + "duration_dice": "1D6E", + "target": "defender", "description": "Magical Damage inflicts Flash of pain 1D6E seconds" }, "arcane_spell_defense": { - "type": "flash_of_pain", "duration_dice": "1D6E", "target": "caster", + "type": "flash_of_pain", + "duration_dice": "1D6E", + "target": "caster", "description": "Caster Suffers Severe pain and will be under a flash of pain for 1D6E seconds" }, "skill_rolls": { - "type": "bonus_dice", "dice": "D6", "target": "skill", + "type": "bonus_dice", + "dice": "D6", + "target": "skill", "description": "Granted D6 (1-6) Skill Modifier for this Skill Attempt" + }, + "ranged_defense": { + "type": "recover_pain", + "description": "Defender Recovers or ignores any flash of pain" } }, "20": { "melee_attack": { "type": "choice", "choices": [ - { "type": "special_strike", "options": ["vicious"] }, - { "type": "bonus_dice", "dice": "D12", "target": "attack" } + { + "type": "special_strike", + "options": [ + "vicious" + ] + }, + { + "type": "bonus_dice", + "dice": "D12", + "target": "attack" + } ], "description": "Possible Vicious Strike or Add D12 to attack" }, "ranged_attack": { "type": "choice", "choices": [ - { "type": "special_strike", "options": ["vicious"] }, - { "type": "bonus_dice", "dice": "D12", "target": "attack" } + { + "type": "special_strike", + "options": [ + "vicious" + ] + }, + { + "type": "bonus_dice", + "dice": "D12", + "target": "attack" + } ], "description": "Possible Vicious Strike or add D12 to attack" }, "melee_defense": { "type": "choice", "choices": [ - { "type": "special_defense", "options": ["perfect"] }, - { "type": "bonus_dice", "dice": "D12", "target": "defense" } + { + "type": "special_defense", + "options": [ + "perfect" + ] + }, + { + "type": "bonus_dice", + "dice": "D12", + "target": "defense" + } ], "description": "Possible 20/20 defense that avoids Any Attack Except a Lethal Strike or adds D12 to defense" }, "arcane_spell_attack": { "type": "choice", "choices": [ - { "type": "special_strike", "options": ["vicious_magical"] }, - { "type": "bonus_dice", "dice": "D12", "target": "spell_attack" } + { + "type": "special_strike", + "options": [ + "vicious_magical" + ] + }, + { + "type": "bonus_dice", + "dice": "D12", + "target": "spell_attack" + } ], "description": "Possible Vicious Application of a Magical Attack or add D12 to attack" }, "arcane_spell_defense": { "type": "choice", "choices": [ - { "type": "special_defense", "options": ["perfect_spell"] }, - { "type": "bonus_dice", "dice": "D12", "target": "spell_defense" } + { + "type": "special_defense", + "options": [ + "perfect_spell" + ] + }, + { + "type": "bonus_dice", + "dice": "D12", + "target": "spell_defense" + } ], "description": "Possible 20/20 Spell defense that Saves Against Any Magical Attack Except a Lethal Magical Strike or add D12 to defense" }, "skill_rolls": { - "type": "bonus_flat", "amount": 20, "target": "skill", + "type": "bonus_flat", + "amount": 20, + "target": "skill", "description": "20 Added to Skill Roll" + }, + "ranged_defense": { + "type": "choice", + "choices": [ + { + "type": "special_defense", + "options": [ + "perfect" + ] + }, + { + "type": "bonus_dice", + "dice": "D12", + "target": "defense" + } + ], + "description": "Possible 20/20 defense that avoids Any Attack Except a Lethal Strike or adds D12 to defense" } }, "15": { "melee_attack": { "type": "combo", "effects": [ - { "type": "bleed" }, - { "type": "knockback" } + { + "type": "bleed" + }, + { + "type": "knockback" + } ], "description": "Bleed, Knock-back on Hit" }, - "ranged_attack": { "type": "bleed", "description": "Bleed" }, + "ranged_attack": { + "type": "bleed", + "description": "Bleed" + }, "melee_defense": { "type": "counter_attack", - "options": ["kick", "punch", "shield_bash"], + "options": [ + "kick", + "punch", + "shield_bash" + ], "description": "Kick, Punch or Shield Bash" }, "skill_rolls": { - "type": "bonus_flat", "amount": 1, "target": "skill", + "type": "bonus_flat", + "amount": 1, + "target": "skill", "description": "Add 1 to Skill Roll" + }, + "ranged_defense": { + "type": "counter_attack", + "options": [ + "kick", + "punch", + "shield_bash" + ], + "description": "Kick, Punch or Shield Bash" } }, "13": {}, "11": { "melee_attack": { - "type": "flurry", "condition": "hit_or_miss", + "type": "flurry", + "condition": "hit_or_miss", "description": "Flurry Attack or Hit to Miss" }, - "ranged_attack": { "type": "double_damage_dice", "description": "Roll 2x Damage Dice" } + "ranged_attack": { + "type": "double_damage_dice", + "description": "Roll 2x Damage Dice" + } }, "10": { "melee_attack": { "type": "combo", "effects": [ - { "type": "bleed" }, - { "type": "knockback" } + { + "type": "bleed" + }, + { + "type": "knockback" + } ], "description": "Bleed, Knock-back on Hit" }, - "ranged_attack": { "type": "bleed", "description": "Bleed" }, + "ranged_attack": { + "type": "bleed", + "description": "Bleed" + }, "melee_defense": { "type": "counter_attack", - "options": ["kick", "punch", "shield_bash"], + "options": [ + "kick", + "punch", + "shield_bash" + ], "description": "Kick, Punch or Shield Bash" }, "skill_rolls": { - "type": "bonus_flat", "amount": 1, "target": "skill", + "type": "bonus_flat", + "amount": 1, + "target": "skill", "description": "Add 1 to Skill Roll" + }, + "ranged_defense": { + "type": "counter_attack", + "options": [ + "kick", + "punch", + "shield_bash" + ], + "description": "Kick, Punch or Shield Bash" } }, "8": { - "melee_attack": { "type": "mulligan", "description": "Mulligan, Can Choose to Re-roll This Attack" }, - "ranged_attack": { "type": "mulligan", "description": "Mulligan, Can Choose to Re-Roll This Attack" }, - "melee_defense": { "type": "mulligan", "description": "Mulligan, Can Choose to Re-Roll This Defense" }, - "arcane_spell_attack": { "type": "mulligan", "description": "Mulligan, Can Re-Roll This Spell Attack" }, - "arcane_spell_defense": { "type": "mulligan", "description": "Mulligan, Can Re-Roll This Spell Defense" }, - "skill_rolls": { "type": "mulligan", "description": "Mulligan, Can Re-Roll This Skill roll" } + "melee_attack": { + "type": "mulligan", + "description": "Mulligan, Can Choose to Re-roll This Attack" + }, + "ranged_attack": { + "type": "mulligan", + "description": "Mulligan, Can Choose to Re-Roll This Attack" + }, + "melee_defense": { + "type": "mulligan", + "description": "Mulligan, Can Choose to Re-Roll This Defense" + }, + "arcane_spell_attack": { + "type": "mulligan", + "description": "Mulligan, Can Re-Roll This Spell Attack" + }, + "arcane_spell_defense": { + "type": "mulligan", + "description": "Mulligan, Can Re-Roll This Spell Defense" + }, + "skill_rolls": { + "type": "mulligan", + "description": "Mulligan, Can Re-Roll This Skill roll" + }, + "ranged_defense": { + "type": "mulligan", + "description": "Mulligan, Can Choose to Re-Roll This Defense" + } }, "7": { "melee_attack": { - "type": "flurry", "condition": "hit_or_miss", + "type": "flurry", + "condition": "hit_or_miss", "description": "Flurry Attack on Hit or Miss" }, - "ranged_attack": { "type": "double_damage_dice", "description": "Roll 2x Damage Dice" } + "ranged_attack": { + "type": "double_damage_dice", + "description": "Roll 2x Damage Dice" + } }, "5": { "melee_attack": { "type": "combo", "effects": [ - { "type": "bleed" }, - { "type": "knockback" } + { + "type": "bleed" + }, + { + "type": "knockback" + } ], "description": "Bleed, Knock-back on Hit" }, - "ranged_attack": { "type": "bleed", "description": "Bleed" }, + "ranged_attack": { + "type": "bleed", + "description": "Bleed" + }, "melee_defense": { "type": "counter_attack", - "options": ["kick", "punch", "shield_bash"], + "options": [ + "kick", + "punch", + "shield_bash" + ], "description": "Kick, Punch, or Shield Bash" }, "skill_rolls": { - "type": "bonus_flat", "amount": 1, "target": "skill", + "type": "bonus_flat", + "amount": 1, + "target": "skill", "description": "Add 1 to Skill Roll" + }, + "ranged_defense": { + "type": "counter_attack", + "options": [ + "kick", + "punch", + "shield_bash" + ], + "description": "Kick, Punch, or Shield Bash" } }, "3": { - "melee_attack": { "type": "damage_multiplier", "multiplier": 3, "description": "Triple Damage" }, - "ranged_attack": { "type": "damage_multiplier", "multiplier": 3, "description": "Triple Damage" }, + "melee_attack": { + "type": "damage_multiplier", + "multiplier": 3, + "description": "Triple Damage" + }, + "ranged_attack": { + "type": "damage_multiplier", + "multiplier": 3, + "description": "Triple Damage" + }, "melee_defense": { - "type": "dr_multiplier", "multiplier": 3, "includes_shield": true, + "type": "dr_multiplier", + "multiplier": 3, + "includes_shield": true, "description": "DR Tripled including Shield" }, - "arcane_spell_attack": { "type": "damage_multiplier", "multiplier": 3, "description": "Triple Damage on Spell Damage" }, + "arcane_spell_attack": { + "type": "damage_multiplier", + "multiplier": 3, + "description": "Triple Damage on Spell Damage" + }, "arcane_spell_defense": { - "type": "bonus_dice", "dice": "D12", "target": "spell_defense", + "type": "bonus_dice", + "dice": "D12", + "target": "spell_defense", "description": "D12 Added to Spell Defense Modifier" + }, + "ranged_defense": { + "type": "dr_multiplier", + "multiplier": 3, + "includes_shield": true, + "description": "DR Tripled including Shield" } }, "2": { - "melee_attack": { "type": "damage_multiplier", "multiplier": 2, "description": "Double Damage" }, - "ranged_attack": { "type": "damage_multiplier", "multiplier": 2, "description": "Double Damage" }, + "melee_attack": { + "type": "damage_multiplier", + "multiplier": 2, + "description": "Double Damage" + }, + "ranged_attack": { + "type": "damage_multiplier", + "multiplier": 2, + "description": "Double Damage" + }, "melee_defense": { - "type": "dr_multiplier", "multiplier": 2, "includes_shield": true, + "type": "dr_multiplier", + "multiplier": 2, + "includes_shield": true, "description": "DR Doubled including Shield" }, - "arcane_spell_attack": { "type": "damage_multiplier", "multiplier": 2, "description": "Double Damage on Spell Damage" }, + "arcane_spell_attack": { + "type": "damage_multiplier", + "multiplier": 2, + "description": "Double Damage on Spell Damage" + }, "arcane_spell_defense": { - "type": "bonus_dice", "dice": "D6", "target": "spell_defense", + "type": "bonus_dice", + "dice": "D6", + "target": "spell_defense", "description": "D6 Added to Spell Defense Modifier" + }, + "ranged_defense": { + "type": "dr_multiplier", + "multiplier": 2, + "includes_shield": true, + "description": "DR Doubled including Shield" } }, "1": { "ranged_attack": { - "type": "fumble", "detail": "ranged_ammo_broken", + "type": "fumble", + "detail": "ranged_ammo_broken", "description": "Possible Fumble Ranged ammo is broken unrecoverable" }, "arcane_spell_attack": { @@ -286,4 +618,4 @@ "matching_30s": "Matching 30s on skill rolls cancel each other out and is resolved by the skill roll.", "skill_roll_30": "A 30 on a skill roll indicates success at highest level of the skill involved." } -} +} \ No newline at end of file diff --git a/module/config/system.mjs b/module/config/system.mjs index 5671633..c853014 100644 --- a/module/config/system.mjs +++ b/module/config/system.mjs @@ -193,6 +193,7 @@ export const DICE_VALUES = { export const CHARACTERISTIC_ATTACK = ["str", "int", "wis", "dex"] export const CHARACTERISTIC_RANGED_ATTACK = ["int", "wis", "dex"] export const CHARACTERISTIC_DEFENSE = ["int", "wis", "dex"] +export const CHARACTERISTIC_RANGED_DEFENSE = ["int", "wis", "dex"] export const CHARACTERISTIC_DAMAGE = ["str"] export const DEFENSE_DICE_VALUES = { @@ -304,6 +305,7 @@ export const SYSTEM = { CHARACTERISTIC_ATTACK, CHARACTERISTIC_RANGED_ATTACK, CHARACTERISTIC_DEFENSE, + CHARACTERISTIC_RANGED_DEFENSE, CHARACTERISTIC_DAMAGE, INITIATIVE_DICE_CHOICES_PER_CLASS, CHAR_CLASSES, diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index a62fd8f..52e1090 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -256,6 +256,8 @@ export default class LethalFantasyActor extends Actor { if (rollType === "weapon-defense") { rollTarget.armorDefense = this.getArmorDefenseValue() rollTarget.grantedDice = this.system.granted.defenseDice + // Check if this is a ranged defense + rollTarget.isRangedDefense = game.lethalFantasy?.nextDefenseData?.isRanged ?? false } } break diff --git a/module/documents/d30-roll.mjs b/module/documents/d30-roll.mjs index 984c48f..950dc89 100644 --- a/module/documents/d30-roll.mjs +++ b/module/documents/d30-roll.mjs @@ -22,6 +22,7 @@ export default class D30Roll { MELEE_ATTACK: "melee_attack", RANGED_ATTACK: "ranged_attack", MELEE_DEFENSE: "melee_defense", + RANGED_DEFENSE: "ranged_defense", ARCANE_SPELL_ATTACK: "arcane_spell_attack", ARCANE_SPELL_DEFENSE: "arcane_spell_defense", SKILL_ROLLS: "skill_rolls" @@ -51,9 +52,11 @@ export default class D30Roll { * @param {number} diceValue La valeur du dé (1-30) * @param {string} rollType Le type de jet externe (ex: "weapon-attack", "spell-attack", etc.) * @param {Object} weapon L'arme ou l'objet utilisé (optionnel, nécessaire pour certains types) + * @param {Object} options Options supplémentaires (optionnel) + * @param {boolean} options.isRanged Si true, utilise ranged_defense au lieu de melee_defense * @returns {Object|null} L'objet effet `{ type, description, ...fields }` ou null si aucun effet */ - static getResult(diceValue, rollType, weapon = null) { + static getResult(diceValue, rollType, weapon = null, options = {}) { if (!this.resultsTable) { console.warn("D30Roll | Results table is not initialized. Call D30Roll.initialize() first.") return null @@ -64,7 +67,7 @@ export default class D30Roll { return null } - const internalType = this.convertToInternalType(rollType, weapon) + const internalType = this.convertToInternalType(rollType, weapon, options) if (!internalType) { console.warn(`D30Roll | Could not convert roll type: ${rollType}`) @@ -99,9 +102,11 @@ export default class D30Roll { * Convertit un rollType externe en rollType interne * @param {string} externalType Le type de jet externe (ex: "weapon-attack") * @param {Object} weapon L'arme ou l'objet utilisé (optionnel) + * @param {Object} options Options supplémentaires (optionnel) + * @param {boolean} options.isRanged Si true, utilise ranged_defense au lieu de melee_defense * @returns {string|null} Le type interne correspondant ou null */ - static convertToInternalType(externalType, weapon = null) { + static convertToInternalType(externalType, weapon = null, options = {}) { // Attack types - need weapon to determine if melee or ranged if (externalType === "weapon-attack") { if (!weapon) { @@ -124,7 +129,7 @@ export default class D30Roll { // Defense types if (externalType === "weapon-defense" || externalType === "monster-defense") { - return this.ROLL_TYPES.MELEE_DEFENSE + return options.isRanged ? this.ROLL_TYPES.RANGED_DEFENSE : this.ROLL_TYPES.MELEE_DEFENSE } // Spell types @@ -190,11 +195,12 @@ export default class D30Roll { * @param {number} diceValue La valeur du dé (1-30) * @param {string} rollType Le type de jet externe * @param {Object} weapon L'arme ou l'objet utilisé (optionnel) + * @param {Object} options Options supplémentaires (optionnel) * @returns {Object} Un objet avec le résultat et des informations de formatage */ - static getFormattedResult(diceValue, rollType, weapon = null) { - const result = this.getResult(diceValue, rollType, weapon) - const internalType = this.convertToInternalType(rollType, weapon) + static getFormattedResult(diceValue, rollType, weapon = null, options = {}) { + const result = this.getResult(diceValue, rollType, weapon, options) + const internalType = this.convertToInternalType(rollType, weapon, options) return { value: diceValue, diff --git a/module/documents/roll.mjs b/module/documents/roll.mjs index ff4cf20..29eb8a2 100644 --- a/module/documents/roll.mjs +++ b/module/documents/roll.mjs @@ -182,6 +182,7 @@ export default class LethalFantasyRoll extends Roll { } else { options.rollTarget.value = options.rollTarget.defenseModifier options.rollTarget.charModifier = 0 + options.isRangedDefense = options.rollTarget.isRangedDefense ?? false } } else if (options.rollType === "monster-skill") { @@ -219,8 +220,14 @@ export default class LethalFantasyRoll extends Roll { options.rollTarget.charModifier = options.rollTarget.combat.rangedAttackModifier } } else { - options.rollTarget.value = options.rollTarget.combat.defenseModifier + options.rollTarget.weaponSkillModifier + options.rollTarget.weapon.system.bonuses.defenseBonus + options.rollTarget.armorDefense - options.rollTarget.charModifier = options.rollTarget.combat.defenseModifier + // For defense, check if it's a ranged defense + const defenseModifier = options.rollTarget.isRangedDefense + ? options.rollTarget.combat.rangedDefenseModifier + : options.rollTarget.combat.defenseModifier + options.rollTarget.value = defenseModifier + options.rollTarget.weaponSkillModifier + options.rollTarget.weapon.system.bonuses.defenseBonus + options.rollTarget.armorDefense + options.rollTarget.charModifier = defenseModifier + // Store isRanged flag for D30 lookup + options.isRangedDefense = options.rollTarget.isRangedDefense } } else if (options.rollType === "spell" || options.rollType === "spell-attack" || options.rollType === "spell-power") { @@ -577,7 +584,8 @@ export default class LethalFantasyRoll extends Roll { const d30Message = D30Roll.getResult( rollD30.total, options.rollType, - options.rollTarget?.weapon + options.rollTarget?.weapon, + { isRanged: options.isRangedDefense } ) options.D30message = d30Message } @@ -1113,7 +1121,7 @@ export default class LethalFantasyRoll extends Roll { await rollBase.evaluate() let rollD30 = await new Roll("1D30").evaluate() options.D30result = rollD30.total - options.D30message = D30Roll.getResult(rollD30.total, options.rollType, options.rollTarget?.weapon) + options.D30message = D30Roll.getResult(rollD30.total, options.rollType, options.rollTarget?.weapon, { isRanged: true }) let badResult = 0 if (rollContext.movement.includes("kh")) { diff --git a/module/models/character.mjs b/module/models/character.mjs index 86ac573..d114d53 100644 --- a/module/models/character.mjs +++ b/module/models/character.mjs @@ -152,6 +152,7 @@ export default class LethalFantasyCharacter extends foundry.abstract.TypeDataMod attackModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), rangedAttackModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), defenseModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), + rangedDefenseModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), defenseBonus: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), damageModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), armorHitPoints: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), @@ -259,6 +260,12 @@ export default class LethalFantasyCharacter extends foundry.abstract.TypeDataMod this.combat.defenseModifier += chaDef.defense } + this.combat.rangedDefenseModifier = this.combat.defenseBonus + for (let chaKey of SYSTEM.CHARACTERISTIC_RANGED_DEFENSE) { + let chaDef = SYSTEM.CHARACTERISTICS_TABLES[chaKey].find(s => s.value === this.characteristics[chaKey].value) + this.combat.rangedDefenseModifier += chaDef.defense + } + this.combat.damageModifier = 0 for (let chaKey of SYSTEM.CHARACTERISTIC_DAMAGE) { let chaDef = SYSTEM.CHARACTERISTICS_TABLES[chaKey].find(s => s.value === this.characteristics[chaKey].value) diff --git a/module/models/monster.mjs b/module/models/monster.mjs index ca5060c..60ce584 100644 --- a/module/models/monster.mjs +++ b/module/models/monster.mjs @@ -182,6 +182,9 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel const attacksSet = this.attackMode === "ranged" ? this.rangedAttacks : this.attacks rollTarget = foundry.utils.duplicate(attacksSet[rollKey]) rollTarget.rollKey = rollKey + if (rollType === "monster-defense") { + rollTarget.isRangedDefense = game.lethalFantasy?.nextDefenseData?.isRanged ?? false + } // Si damageModifier est fourni (depuis le chat), l'utiliser au lieu de celui de la fiche if (damageModifier !== undefined && rollType === "monster-damage") { rollTarget.damageModifier = damageModifier diff --git a/module/utils.mjs b/module/utils.mjs index 0a9ec7b..9e1d568 100644 --- a/module/utils.mjs +++ b/module/utils.mjs @@ -366,7 +366,8 @@ export default class LethalFantasyUtils { attackD30message, attackRerollContext, defenderId: defender.id, - defenderTokenId + defenderTokenId, + isRanged: msg.isRanged } defender.system.prepareMonsterRoll("monster-defense", result) @@ -438,7 +439,8 @@ export default class LethalFantasyUtils { attackD30message, attackRerollContext, defenderId: defender.id, - defenderTokenId + defenderTokenId, + isRanged: msg.isRanged } console.log("Storing defense data for character:", defender.id)