Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal 4 physical damage each. Combine this damage.
", + "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
", "resource": null, "actions": { "vgguNWz8vG8aoLXR": { diff --git a/src/packs/adversaries/adversary_Archer_Squadron_0ts6CGd93lLqGZI5.json b/src/packs/adversaries/adversary_Archer_Squadron_0ts6CGd93lLqGZI5.json index 55229040..5b15bc09 100644 --- a/src/packs/adversaries/adversary_Archer_Squadron_0ts6CGd93lLqGZI5.json +++ b/src/packs/adversaries/adversary_Archer_Squadron_0ts6CGd93lLqGZI5.json @@ -218,10 +218,10 @@ }, "items": [ { - "name": "Horde (1d6+3)", + "name": "Horde", "type": "feature", "system": { - "description": "When the @Lookup[@name] has marked half or more of their HP, their standard attack deals 1d6+3 physical damage instead.
", + "description": "When the @Lookup[@name] has marked half or more of their HP, their standard attack deals @Lookup[@system.attack.altDamageFormula] physical damage instead.
", "resource": null, "actions": {}, "originItemType": null, diff --git a/src/packs/adversaries/adversary_Conscript_99TqczuQipBmaB8i.json b/src/packs/adversaries/adversary_Conscript_99TqczuQipBmaB8i.json index c5b4357d..35c43a3b 100644 --- a/src/packs/adversaries/adversary_Conscript_99TqczuQipBmaB8i.json +++ b/src/packs/adversaries/adversary_Conscript_99TqczuQipBmaB8i.json @@ -239,7 +239,7 @@ "name": "Group Attack", "type": "feature", "system": { - "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal 6 physical damage each. Combine this damage.
", + "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
", "resource": null, "actions": { "cbAvPSIhwBMBTI3D": { diff --git a/src/packs/adversaries/adversary_Cult_Initiate_zx99sOGTXicP4SSD.json b/src/packs/adversaries/adversary_Cult_Initiate_zx99sOGTXicP4SSD.json index 0e14a661..a0c0713d 100644 --- a/src/packs/adversaries/adversary_Cult_Initiate_zx99sOGTXicP4SSD.json +++ b/src/packs/adversaries/adversary_Cult_Initiate_zx99sOGTXicP4SSD.json @@ -239,7 +239,7 @@ "name": "Group Attack", "type": "feature", "system": { - "description": "Spend a Fear to choose a target and spotlight all Cult @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal 5 physical damage each. Combine this damage.
", + "description": "Spend a Fear to choose a target and spotlight all Cult @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
", "resource": null, "actions": { "EH1preaTWBD4rOvx": { diff --git a/src/packs/adversaries/adversary_Demonic_Hound_Pack_NoRZ1PqB8N5wcIw0.json b/src/packs/adversaries/adversary_Demonic_Hound_Pack_NoRZ1PqB8N5wcIw0.json index 2947b7a1..7482c734 100644 --- a/src/packs/adversaries/adversary_Demonic_Hound_Pack_NoRZ1PqB8N5wcIw0.json +++ b/src/packs/adversaries/adversary_Demonic_Hound_Pack_NoRZ1PqB8N5wcIw0.json @@ -224,10 +224,10 @@ }, "items": [ { - "name": "Horde (2d4+1)", + "name": "Horde", "type": "feature", "system": { - "description": "When the @Lookup[@name] has marked half or more of their HP, their standard attack deals 2d4+1 physical damage instead.
", + "description": "When the @Lookup[@name] has marked half or more of their HP, their standard attack deals @Lookup[@system.attack.altDamageFormula] physical damage instead.
", "resource": null, "actions": {}, "originItemType": null, diff --git a/src/packs/adversaries/adversary_Electric_Eels_TLzY1nDw0Bu9Ud40.json b/src/packs/adversaries/adversary_Electric_Eels_TLzY1nDw0Bu9Ud40.json index 7b41b9e5..9386944f 100644 --- a/src/packs/adversaries/adversary_Electric_Eels_TLzY1nDw0Bu9Ud40.json +++ b/src/packs/adversaries/adversary_Electric_Eels_TLzY1nDw0Bu9Ud40.json @@ -218,10 +218,10 @@ }, "items": [ { - "name": "Horde (2d4+1)", + "name": "Horde", "type": "feature", "system": { - "description": "When the @Lookup[@name] have marked half or more of their HP, their standard attack deals 2d4+1 physical damage instead.
", + "description": "When the @Lookup[@name] have marked half or more of their HP, their standard attack deals @Lookup[@system.attack.altDamageFormula] physical damage instead.
", "resource": null, "actions": {}, "originItemType": null, diff --git a/src/packs/adversaries/adversary_Elemental_Spark_P7h54ZePFPHpYwvB.json b/src/packs/adversaries/adversary_Elemental_Spark_P7h54ZePFPHpYwvB.json index b17cae1c..5c25f63e 100644 --- a/src/packs/adversaries/adversary_Elemental_Spark_P7h54ZePFPHpYwvB.json +++ b/src/packs/adversaries/adversary_Elemental_Spark_P7h54ZePFPHpYwvB.json @@ -239,7 +239,7 @@ "name": "Group Attack", "type": "feature", "system": { - "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal 5 physical damage each. Combine this damage.
", + "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
", "resource": null, "actions": { "vXHZVb0Y7Hqu3uso": { diff --git a/src/packs/adversaries/adversary_Fallen_Shock_Troop_OsLG2BjaEdTZUJU9.json b/src/packs/adversaries/adversary_Fallen_Shock_Troop_OsLG2BjaEdTZUJU9.json index 163c61f7..931e4c0a 100644 --- a/src/packs/adversaries/adversary_Fallen_Shock_Troop_OsLG2BjaEdTZUJU9.json +++ b/src/packs/adversaries/adversary_Fallen_Shock_Troop_OsLG2BjaEdTZUJU9.json @@ -317,7 +317,7 @@ "name": "Group Attack", "type": "feature", "system": { - "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal 12 physical damage each. Combine this damage.
", + "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
", "resource": null, "actions": { "QHNRSEQmqOcaoXq4": { diff --git a/src/packs/adversaries/adversary_Giant_Mosquitoes_IIWV4ysJPFPnTP7W.json b/src/packs/adversaries/adversary_Giant_Mosquitoes_IIWV4ysJPFPnTP7W.json index 54f12efa..fbb30d40 100644 --- a/src/packs/adversaries/adversary_Giant_Mosquitoes_IIWV4ysJPFPnTP7W.json +++ b/src/packs/adversaries/adversary_Giant_Mosquitoes_IIWV4ysJPFPnTP7W.json @@ -229,7 +229,7 @@ "_id": "9RduwBLYcBaiouYk", "img": "icons/creatures/magical/humanoid-silhouette-aliens-green.webp", "system": { - "description": "When the @Lookup[@name] have marked half or more of their HP, their standard attack deals 1d4+1 physical damage instead.
", + "description": "When the @Lookup[@name] have marked half or more of their HP, their standard attack deals @Lookup[@system.attack.altDamageFormula] physical damage instead.
", "resource": null, "actions": {}, "originItemType": null, diff --git a/src/packs/adversaries/adversary_Giant_Rat_4PfLnaCrOcMdb4dK.json b/src/packs/adversaries/adversary_Giant_Rat_4PfLnaCrOcMdb4dK.json index d4655880..d1df6b57 100644 --- a/src/packs/adversaries/adversary_Giant_Rat_4PfLnaCrOcMdb4dK.json +++ b/src/packs/adversaries/adversary_Giant_Rat_4PfLnaCrOcMdb4dK.json @@ -248,7 +248,7 @@ "_id": "fsaBlCjTdq1jM23G", "img": "icons/creatures/abilities/tail-strike-bone-orange.webp", "system": { - "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal 1 physical damage each. Combine this damage.
", + "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
", "resource": null, "actions": { "q8chow47nQLR9qeF": { diff --git a/src/packs/adversaries/adversary_Giant_Recruit_5s8wSvpyC5rxY5aD.json b/src/packs/adversaries/adversary_Giant_Recruit_5s8wSvpyC5rxY5aD.json index 75da96b2..ebdea711 100644 --- a/src/packs/adversaries/adversary_Giant_Recruit_5s8wSvpyC5rxY5aD.json +++ b/src/packs/adversaries/adversary_Giant_Recruit_5s8wSvpyC5rxY5aD.json @@ -239,7 +239,7 @@ "name": "Group Attack", "type": "feature", "system": { - "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal 5 physical damage each. Combine this damage.
", + "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
", "resource": null, "actions": { "DjbPQowW1OdBD9Zn": { diff --git a/src/packs/adversaries/adversary_Hallowed_Soldier_VENwg7xEFcYObjmT.json b/src/packs/adversaries/adversary_Hallowed_Soldier_VENwg7xEFcYObjmT.json index cceed989..96107752 100644 --- a/src/packs/adversaries/adversary_Hallowed_Soldier_VENwg7xEFcYObjmT.json +++ b/src/packs/adversaries/adversary_Hallowed_Soldier_VENwg7xEFcYObjmT.json @@ -294,7 +294,7 @@ "name": "Group Attack", "type": "feature", "system": { - "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal 10 physical damage each. Combine this damage.
", + "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
", "resource": null, "actions": { "eo7J0v1B5zPHul1M": { diff --git a/src/packs/adversaries/adversary_Jagged_Knife_Lackey_C0OMQqV7pN6t7ouR.json b/src/packs/adversaries/adversary_Jagged_Knife_Lackey_C0OMQqV7pN6t7ouR.json index 1a95bf87..a52ec1c9 100644 --- a/src/packs/adversaries/adversary_Jagged_Knife_Lackey_C0OMQqV7pN6t7ouR.json +++ b/src/packs/adversaries/adversary_Jagged_Knife_Lackey_C0OMQqV7pN6t7ouR.json @@ -248,7 +248,7 @@ "_id": "1k5TmQIAunM7Bv32", "img": "icons/creatures/abilities/tail-strike-bone-orange.webp", "system": { - "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name] within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal 2 physical damage each. Combine this damage.
", + "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name] within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
", "resource": null, "actions": { "aoQDb2m32NDxE6ZP": { diff --git a/src/packs/adversaries/adversary_Minor_Treant_G62k4oSkhkoXEs2D.json b/src/packs/adversaries/adversary_Minor_Treant_G62k4oSkhkoXEs2D.json index 0f1e7ded..f05ba5fc 100644 --- a/src/packs/adversaries/adversary_Minor_Treant_G62k4oSkhkoXEs2D.json +++ b/src/packs/adversaries/adversary_Minor_Treant_G62k4oSkhkoXEs2D.json @@ -242,7 +242,7 @@ "_id": "K08WlZwGqzEo4idT", "img": "icons/creatures/abilities/tail-strike-bone-orange.webp", "system": { - "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal 4 physical damage each. Combine this damage.
", + "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
", "resource": null, "actions": { "xTMNAHcoErKuR6TZ": { diff --git a/src/packs/adversaries/adversary_Outer_Realms_Thrall_moJhHgKqTKPS2WYS.json b/src/packs/adversaries/adversary_Outer_Realms_Thrall_moJhHgKqTKPS2WYS.json index 370182a5..276dd3ed 100644 --- a/src/packs/adversaries/adversary_Outer_Realms_Thrall_moJhHgKqTKPS2WYS.json +++ b/src/packs/adversaries/adversary_Outer_Realms_Thrall_moJhHgKqTKPS2WYS.json @@ -239,7 +239,7 @@ "name": "Group Attack", "type": "feature", "system": { - "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal 11 physical damage each. Combine this damage.
", + "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
", "resource": null, "actions": { "tvQetauskZoHDR5y": { diff --git a/src/packs/adversaries/adversary_Pirate_Raiders_5YgEajn0wa4i85kC.json b/src/packs/adversaries/adversary_Pirate_Raiders_5YgEajn0wa4i85kC.json index 7d3733ce..41f79b49 100644 --- a/src/packs/adversaries/adversary_Pirate_Raiders_5YgEajn0wa4i85kC.json +++ b/src/packs/adversaries/adversary_Pirate_Raiders_5YgEajn0wa4i85kC.json @@ -229,7 +229,7 @@ "_id": "Q7DRbWjHl64CNwag", "img": "icons/creatures/magical/humanoid-silhouette-aliens-green.webp", "system": { - "description": "When the @Lookup[@name] have marked half or more of their HP, their standard attack deals 1d4+1 physical damage instead.
", + "description": "When the @Lookup[@name] have marked half or more of their HP, their standard attack deals @Lookup[@system.attack.altDamageFormula] physical damage instead.
", "resource": null, "actions": {}, "originItemType": null, diff --git a/src/packs/adversaries/adversary_Rotted_Zombie_gP3fWTLzSFnpA8EJ.json b/src/packs/adversaries/adversary_Rotted_Zombie_gP3fWTLzSFnpA8EJ.json index a9bf3a67..7672961c 100644 --- a/src/packs/adversaries/adversary_Rotted_Zombie_gP3fWTLzSFnpA8EJ.json +++ b/src/packs/adversaries/adversary_Rotted_Zombie_gP3fWTLzSFnpA8EJ.json @@ -242,7 +242,7 @@ "_id": "R9vrwFNl5BD1YXJo", "img": "icons/creatures/abilities/tail-strike-bone-orange.webp", "system": { - "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal 2 physical damage each. Combine this damage.
", + "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
", "resource": null, "actions": { "DJBNtd3hWjwsjPwq": { diff --git a/src/packs/adversaries/adversary_Sellsword_bgreCaQ6ap2DVpCr.json b/src/packs/adversaries/adversary_Sellsword_bgreCaQ6ap2DVpCr.json index e26b48eb..514be8f5 100644 --- a/src/packs/adversaries/adversary_Sellsword_bgreCaQ6ap2DVpCr.json +++ b/src/packs/adversaries/adversary_Sellsword_bgreCaQ6ap2DVpCr.json @@ -242,7 +242,7 @@ "_id": "CQZQiEiRH70Br5Ge", "img": "icons/creatures/abilities/tail-strike-bone-orange.webp", "system": { - "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal 3 physical damage each. Combine this damage.
", + "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
", "resource": null, "actions": { "ghgFZskDiizJDjcn": { diff --git a/src/packs/adversaries/adversary_Skeleton_Dredge_6l1a3Fazq8BoKIcc.json b/src/packs/adversaries/adversary_Skeleton_Dredge_6l1a3Fazq8BoKIcc.json index 5a973b17..4013d7fe 100644 --- a/src/packs/adversaries/adversary_Skeleton_Dredge_6l1a3Fazq8BoKIcc.json +++ b/src/packs/adversaries/adversary_Skeleton_Dredge_6l1a3Fazq8BoKIcc.json @@ -242,7 +242,7 @@ "_id": "wl9KKEpVWDBu62hU", "img": "icons/creatures/abilities/tail-strike-bone-orange.webp", "system": { - "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal 1 physical damage each. Combine this damage.
", + "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
", "resource": null, "actions": { "Sz55uB8xkoNytLwJ": { diff --git a/src/packs/adversaries/adversary_Swarm_of_Rats_qNgs3AbLyJrY19nt.json b/src/packs/adversaries/adversary_Swarm_of_Rats_qNgs3AbLyJrY19nt.json index 33fe06d7..014b3dc6 100644 --- a/src/packs/adversaries/adversary_Swarm_of_Rats_qNgs3AbLyJrY19nt.json +++ b/src/packs/adversaries/adversary_Swarm_of_Rats_qNgs3AbLyJrY19nt.json @@ -223,7 +223,7 @@ "_id": "9Zuu892SO5NmtI4w", "img": "icons/creatures/magical/humanoid-silhouette-aliens-green.webp", "system": { - "description": "When the @Lookup[@name] has marked half or more of their HP, their standard attack deals 1d4+1 physical damage instead.
", + "description": "When the @Lookup[@name] has marked half or more of their HP, their standard attack deals @Lookup[@system.attack.altDamageFormula] physical damage instead.
", "resource": null, "actions": {}, "originItemType": null, diff --git a/src/packs/adversaries/adversary_Tangle_Bramble_Swarm_PKSXFuaIHUCoH63A.json b/src/packs/adversaries/adversary_Tangle_Bramble_Swarm_PKSXFuaIHUCoH63A.json index 639fa956..40297eb6 100644 --- a/src/packs/adversaries/adversary_Tangle_Bramble_Swarm_PKSXFuaIHUCoH63A.json +++ b/src/packs/adversaries/adversary_Tangle_Bramble_Swarm_PKSXFuaIHUCoH63A.json @@ -254,12 +254,12 @@ }, "items": [ { - "name": "Horde (1d4+2)", + "name": "Horde", "type": "feature", "_id": "4dSzqtYvH385r9Ng", "img": "icons/creatures/magical/humanoid-silhouette-aliens-green.webp", "system": { - "description": "When the @Lookup[@name] has marked half or more of their HP, their standard attack deals 1d4+2 physical damage instead.
", + "description": "When the @Lookup[@name] has marked half or more of their HP, their standard attack deals @Lookup[@system.attack.altDamageFormula] physical damage instead.
", "resource": null, "actions": {}, "originItemType": null, diff --git a/src/packs/adversaries/adversary_Tangle_Bramble_XcAGOSmtCFLT1unN.json b/src/packs/adversaries/adversary_Tangle_Bramble_XcAGOSmtCFLT1unN.json index 0f1ba28f..33afaa3a 100644 --- a/src/packs/adversaries/adversary_Tangle_Bramble_XcAGOSmtCFLT1unN.json +++ b/src/packs/adversaries/adversary_Tangle_Bramble_XcAGOSmtCFLT1unN.json @@ -281,7 +281,7 @@ "_id": "WiobzuyvJ46zfsOv", "img": "icons/creatures/abilities/tail-strike-bone-orange.webp", "system": { - "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal 2 physical damage each. Combine this damage.
", + "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
", "resource": null, "actions": { "ZC5pKIb9N82vgMWu": { diff --git a/src/packs/adversaries/adversary_Treant_Sapling_o63nS0k3wHu6EgKP.json b/src/packs/adversaries/adversary_Treant_Sapling_o63nS0k3wHu6EgKP.json index 8959f78a..c9ca695e 100644 --- a/src/packs/adversaries/adversary_Treant_Sapling_o63nS0k3wHu6EgKP.json +++ b/src/packs/adversaries/adversary_Treant_Sapling_o63nS0k3wHu6EgKP.json @@ -239,7 +239,7 @@ "name": "Group Attack", "type": "feature", "system": { - "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal 8 physical damage each. Combine this damage.
", + "description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
", "resource": null, "actions": { "euP8VA4wvfsCpwN1": { diff --git a/src/packs/adversaries/adversary_Zombie_Legion_YhJrP7rTBiRdX5Fp.json b/src/packs/adversaries/adversary_Zombie_Legion_YhJrP7rTBiRdX5Fp.json index 1b2cce2a..a6a488e9 100644 --- a/src/packs/adversaries/adversary_Zombie_Legion_YhJrP7rTBiRdX5Fp.json +++ b/src/packs/adversaries/adversary_Zombie_Legion_YhJrP7rTBiRdX5Fp.json @@ -218,10 +218,10 @@ }, "items": [ { - "name": "Horde (2d6+5)", + "name": "Horde", "type": "feature", "system": { - "description": "When the @Lookup[@name] has marked half or more of their HP, their standard attack deals 2d6+5 physical damage instead.
", + "description": "When the @Lookup[@name] has marked half or more of their HP, their standard attack deals @Lookup[@system.attack.altDamageFormula] physical damage instead.
", "resource": null, "actions": {}, "originItemType": null, diff --git a/src/packs/adversaries/adversary_Zombie_Pack_Nf0v43rtflV56V2T.json b/src/packs/adversaries/adversary_Zombie_Pack_Nf0v43rtflV56V2T.json index 32519ac6..017537ad 100644 --- a/src/packs/adversaries/adversary_Zombie_Pack_Nf0v43rtflV56V2T.json +++ b/src/packs/adversaries/adversary_Zombie_Pack_Nf0v43rtflV56V2T.json @@ -218,12 +218,12 @@ }, "items": [ { - "name": "Horde (1d4+2)", + "name": "Horde", "type": "feature", "_id": "nNJGAhWu0IuS2ybn", "img": "icons/creatures/magical/humanoid-silhouette-aliens-green.webp", "system": { - "description": "When the @Lookup[@name] have marked half or more of their HP, their standard attack deals 1d4+2 physical damage instead.
", + "description": "When the @Lookup[@name] have marked half or more of their HP, their standard attack deals @Lookup[@system.attack.altDamageFormula] physical damage instead.
", "resource": null, "actions": {}, "originItemType": null, diff --git a/tools/analyze-damage.mjs b/tools/analyze-damage.mjs new file mode 100644 index 00000000..6d5da3de --- /dev/null +++ b/tools/analyze-damage.mjs @@ -0,0 +1,137 @@ +/** + * Internal script to analyze damage and spit out results. + * There isn't enough entries in the database to make a full analysis, some tiers miss some types. + * This script only checks for "minions" and "everything else". + * Maybe if future book monsters can be part of what we release, we can analyze those too. + */ + +import fs from "fs/promises"; +import path from "path"; + +const allData = []; + +// Read adversary pack data for average damage for attacks +const adversariesDirectory = path.join("src/packs/adversaries"); +for (const basefile of await fs.readdir(adversariesDirectory)) { + if (!basefile.endsWith(".json")) continue; + const filepath = path.join(adversariesDirectory, basefile); + const data = JSON.parse(await fs.readFile(filepath, "utf8")); + if (data?.type !== "adversary" || data.system.type === "social") continue; + + allData.push({ + name: data.name, + tier: data.system.tier, + adversaryType: data.system.type, + damage: parseDamage(data.system.attack.damage), + }); +} + +const adversaryTypes = new Set(allData.map(a => a.adversaryType)); +for (const type of [...adversaryTypes].toSorted()) { + const perTier = Object.groupBy(allData.filter(a => a.adversaryType === type), a => a.tier); + console.log(`${type} per Tier: ${[1, 2, 3, 4].map(t => perTier[t]?.length ?? 0).join(" ")}`) +} + +const result = { + basic: compileData(allData.filter(d => d.adversaryType !== "minion")), + solos_and_bruisers: compileData(allData.filter(d => ["solo", "bruiser"].includes(d.adversaryType))), + leader_and_ranged: compileData(allData.filter(d => ["leader", "ranged"].includes(d.adversaryType))), + minion: compileData(allData.filter(d => d.adversaryType === "minion")), +}; + +console.log(result); + +/** Compiles all data for an adversary type (or all entries) */ +function compileData(entries) { + // Note: sorting numbers sorts by their string version by default + const results = {}; + for (const tier of [1, 2, 3, 4]) { + const tierEntries = entries.filter(e => e.tier === tier); + const allDamage = removeOutliers(tierEntries.map(d => d.damage).sort((a, b) => a - b)); + const mean = getMean(allDamage); + if (tier === 4) console.log(allDamage); + results[tier] = { + mean, + deviation: getStandardDeviation(allDamage, { mean }), + }; + } + + return results; +} + +function removeOutliers(data) { + if (data.length <= 4) return data; + const startIdx = Math.floor(data.length * 0.25); + const endIdx = Math.ceil(data.length * 0.75); + const iqrBound = (data[endIdx] - data[startIdx]) * 1.25; + return data.filter((d) => d >= data[startIdx] - iqrBound && d <= data[endIdx] + iqrBound); +} + +function getMedian(numbers) { + numbers = numbers.toSorted((a, b) => a - b); + const medianIdx = numbers.length / 2; + return medianIdx % 1 ? numbers[Math.floor(medianIdx)] : (numbers[medianIdx] + numbers[medianIdx - 1]) / 2; +} + +function getMean(numbers) { + if (numbers.length === 0) return NaN; + return numbers.reduce((r, a) => r + a, 0) / numbers.length; +} + +function getMedianAverageDeviation(numbers, { median }) { + const residuals = allDamage.map(d => Math.abs(d - median)); + return getMedian(residuals); +} + +function getStandardDeviation(numbers, { mean }) { + const deviations = numbers.map((r) => r - mean); + return Math.sqrt(deviations.reduce((r, d) => r + d * d, 0) / (numbers.length - 1)); +} + +function parseDamage(damage) { + const formula = damage.parts + .filter(p => p.applyTo === 'hitPoints') + .map(p => + p.value.custom.enabled + ? p.value.custom.formula + : [p.value.flatMultiplier ? `${p.value.flatMultiplier}${p.value.dice}` : 0, p.value.bonus ?? 0] + .filter(p => !!p) + .join('+') + ) + .join('+'); + return getExpectedDamage(formula); +} + +/** + * Given a simple flavor-less formula with only +/- operators, returns a list of damage partial terms. + * All subtracted terms become negative terms. + */ +function getExpectedDamage(formula) { + const terms = formula.replace("+", " + ").replace("-", " - ").split(" ").map(t => t.trim()); + let multiplier = 1; + return terms.reduce((total, term) => { + if (term === "-") { + multiplier = -1; + return total; + } else if (term === "+") { + return total; + } + + const currentMultiplier = multiplier; + multiplier = 1; + + const number = Number(term); + if (!Number.isNaN(number)) { + return total + currentMultiplier * number; + } + + const dieMatch = term.match(/(\d+)d(\d+)/); + if (dieMatch) { + const numDice = Number(dieMatch[1]); + const faces = Number(dieMatch[2]); + return total + currentMultiplier * numDice * ((faces + 1) / 2); + } + + throw Error(`Unexpected term ${term} in formula ${formula}`); + }, 0); +}