diff --git a/addons/frag/CfgAmmo.hpp b/addons/frag/CfgAmmo.hpp index 7a124833b3c..acad0a0ed0b 100644 --- a/addons/frag/CfgAmmo.hpp +++ b/addons/frag/CfgAmmo.hpp @@ -4,272 +4,16 @@ class CfgAmmo { - // ~~~~ Bombs: - class ammo_Bomb_LaserGuidedBase; - class Bo_GBU12_LGB: ammo_Bomb_LaserGuidedBase { - GVAR(enabled) = 1; + #include "CfgAmmoBaseClasses.hpp" - GVAR(classes)[] = {QGVAR(large), QGVAR(large), QGVAR(large_HD), QGVAR(large), QGVAR(huge), QGVAR(huge_HD), QGVAR(huge)}; - GVAR(metal) = 140000; - GVAR(charge) = 87000; - GVAR(gurney_c) = 2320; - GVAR(gurney_k) = "1/2"; - }; - class Bomb_04_F: ammo_Bomb_LaserGuidedBase { - GVAR(enabled) = 1; - - GVAR(classes)[] = {QGVAR(large), QGVAR(large), QGVAR(large_HD), QGVAR(large), QGVAR(huge), QGVAR(huge_HD), QGVAR(huge)}; - GVAR(metal) = 140000; - GVAR(charge) = 87000; - GVAR(gurney_c) = 2320; - GVAR(gurney_k) = "1/2"; - }; - class BombCore; - class Bo_Mk82: BombCore { - GVAR(enabled) = 1; - - GVAR(classes)[] = {QGVAR(large), QGVAR(large), QGVAR(large_HD), QGVAR(large), QGVAR(huge), QGVAR(huge_HD), QGVAR(huge)}; - GVAR(metal) = 140000; - GVAR(charge) = 87000; - GVAR(gurney_c) = 2320; - GVAR(gurney_k) = "1/2"; - }; - - // ~~~~ Grenades: - class GrenadeBase; - class Grenade; - class GrenadeHand: Grenade { - GVAR(enabled) = 1; - - GVAR(skip) = 0; - GVAR(force) = 1; - // This is a good high-drag frag type for grenades. - GVAR(classes)[] = {QGVAR(tiny_HD)}; - /* - These values are based on the M67 Grenade, should be tweaked for - individual grenades. - */ - GVAR(metal) = 210; // metal in grams - GVAR(charge) = 185; // explosive in grams - GVAR(gurney_c) = 2843; // Gurney velocity constant for explosive type. See: http://en.wikipedia.org/wiki/Gurney_equations - GVAR(gurney_k) = "3/5"; // Gurney shape factor, in this case a sphere. See: http://en.wikipedia.org/wiki/Gurney_equations - }; - class GrenadeHand_stone: GrenadeHand { - GVAR(skip) = 1; - }; - class SmokeShell: GrenadeHand { - GVAR(skip) = 1; - }; - class G_40mm_HE: GrenadeBase { - // Source: http://www.inetres.com/gp/military/infantry/grenade/40mm_ammo.html#M441 - GVAR(enabled) = 1; - GVAR(force) = 1; - - GVAR(classes)[] = {QGVAR(tiny_HD)}; - GVAR(metal) = 200; - GVAR(charge) = 32; - GVAR(gurney_c) = 2700; - GVAR(gurney_k) = "1/2"; - }; - class G_40mm_HEDP: G_40mm_HE { - // Source: http://www.inetres.com/gp/military/infantry/grenade/40mm_ammo.html#M433 - GVAR(enabled) = 1; - - GVAR(classes)[] = {QGVAR(tiny_HD)}; - GVAR(metal) = 200; - GVAR(charge) = 45; - GVAR(gurney_c) = 2830; - GVAR(gurney_k) = "1/2"; - }; - - class ACE_G_40mm_HEDP: G_40mm_HEDP {}; - class ACE_G_40mm_HE: G_40mm_HE {}; - class ACE_G_40mm_Practice: ACE_G_40mm_HE { - GVAR(skip) = 1; - GVAR(force) = 0; - EGVAR(vehicle_damage,incendiary) = 0; - }; - class ACE_G40mm_HE_VOG25P: G_40mm_HE { - GVAR(skip) = 0; - GVAR(force) = 1; - }; - - - // ~~~~ RPGs: - class MissileBase; - class R_PG32V_F; - class R_TBG32V_F: R_PG32V_F { // HE - GVAR(enabled) = 1; - GVAR(metal) = 400; - GVAR(charge) = 210; - GVAR(gurney_c) = 2800; - GVAR(gurney_k) = "3/5"; - GVAR(classes)[] = {"ACE_frag_medium_HD"}; - }; - class M_Titan_AA: MissileBase { - GVAR(skip) = 1; - }; - class M_Titan_AT: MissileBase { - GVAR(skip) = 1; - }; - class M_Titan_AP: M_Titan_AT { // "anti personnel" - GVAR(skip) = 0; - GVAR(enabled) = 1; - GVAR(metal) = 400; - GVAR(charge) = 210; - GVAR(gurney_c) = 2800; - GVAR(gurney_k) = "3/5"; - GVAR(classes)[] = {"ACE_frag_medium_HD"}; - }; - - // https://ofb.gov.in/product/products/product-details/84-mm-he-round-ffv-441-b - // https://armypubs.army.mil/epubs/DR_pubs/DR_a/pdf/web/ARN18072_TC%203-22x84%20FINAL%20WEB.pdf (page 99, Table A-6. HE 441D RS, 84-mm projectile) - class R_MRAAWS_HEAT_F; - class R_MRAAWS_HE_F: R_MRAAWS_HEAT_F { - GVAR(enabled) = 1; - GVAR(metal) = 2300; - GVAR(charge) = 590; - GVAR(gurney_c) = 2800; - GVAR(gurney_k) = "1/2"; - GVAR(classes)[] = {"ACE_frag_small"}; - }; - - - // ~~~~ Missiles: - class M_PG_AT; - class M_AT: M_PG_AT { // DAR (Hydra 70) - // Source: http://fas.org/man/dod-101/sys/missile/hydra-70.htm - GVAR(enabled) = 1; - - GVAR(classes)[] = {QGVAR(medium), QGVAR(medium_HD)}; - GVAR(metal) = 3850; - GVAR(charge) = 1040; - GVAR(gurney_c) = 2700; - GVAR(gurney_k) = "1/2"; - }; - class RocketBase; - class R_80mm_HE: RocketBase { - GVAR(skip) = 1; - }; - class Missile_AGM_02_F: MissileBase { - // Source: http://fas.org/man/dod-101/sys/smart/agm-65.htm - GVAR(enabled) = 1; - - GVAR(classes)[] = {QGVAR(medium), QGVAR(medium_HD)}; - GVAR(metal) = 56250; - GVAR(charge) = 39000; - GVAR(gurney_c) = 2700; - GVAR(gurney_k) = "1/2"; - }; - class Rocket_04_HE_F: MissileBase { // Shrieker (Hydra 70) - GVAR(enabled) = 1; - GVAR(classes)[] = {QGVAR(medium), QGVAR(medium_HD)}; - GVAR(metal) = 3850; - GVAR(charge) = 1040; - GVAR(gurney_c) = 2700; - GVAR(gurney_k) = "1/2"; - }; - class M_Scalpel_AT: MissileBase { // 9K121 Vikhr - GVAR(enabled) = 1; - GVAR(classes)[] = {QGVAR(medium), QGVAR(medium_HD)}; - GVAR(metal) = 10000; - GVAR(charge) = 3000; - GVAR(gurney_c) = 2700; - GVAR(gurney_k) = "1/2"; - }; - class ACE_Hellfire_AGM114K: M_Scalpel_AT { - // Source: http://www.designation-systems.net/dusrm/m-114.html - GVAR(enabled) = 1; - - GVAR(classes)[] = {QGVAR(medium), QGVAR(medium_HD)}; - GVAR(metal) = 8000; - GVAR(charge) = 2400; - GVAR(gurney_c) = 2700; - GVAR(gurney_k) = "1/2"; - }; - class M_Air_AA: MissileBase { - GVAR(skip) = 1; - }; - class Missile_AA_04_F: MissileBase { - GVAR(skip) = 1; - }; - - // curator ammo entries - class ShellBase; - class Sh_125mm_HEAT; - class Sh_155mm_AMOS: ShellBase { - // Source: http://www.globalsecurity.org/military/systems/munitions/m795.htm - GVAR(enabled) = 1; - - GVAR(classes)[] = {QGVAR(large), QGVAR(large), QGVAR(large_HD), QGVAR(large), QGVAR(huge), QGVAR(huge_HD), QGVAR(huge)}; - GVAR(metal) = 36000; - GVAR(charge) = 9979; - GVAR(gurney_c) = 2440; - GVAR(gurney_k) = "1/2"; - }; - class Sh_82mm_AMOS: Sh_155mm_AMOS { - // Source: http://www.arsenal-bg.com/defense_police/mortar_bombs_82mm.htm - GVAR(enabled) = 1; - - GVAR(classes)[] = {QGVAR(medium), QGVAR(medium_HD)}; - GVAR(metal) = 3200; - GVAR(charge) = 420; - GVAR(gurney_c) = 2440; - GVAR(gurney_k) = "1/2"; - }; - class ModuleOrdnanceMortar_F_Ammo: Sh_82mm_AMOS { - GVAR(enabled) = 1; - - GVAR(classes)[] = {QGVAR(medium), QGVAR(medium_HD)}; - GVAR(metal) = 800; - GVAR(charge) = 4200; - GVAR(gurney_c) = 2320; - GVAR(gurney_k) = "1/2"; - }; - class Sh_105mm_HEAT_MP: Sh_125mm_HEAT { - GVAR(enabled) = 1; - - GVAR(classes)[] = {QGVAR(medium), QGVAR(medium_HD)}; - GVAR(metal) = 11400; - GVAR(charge) = 7100; - GVAR(gurney_c) = 2800; - GVAR(gurney_k) = "1/2"; - }; - class Sh_120mm_HE: ShellBase { - GVAR(enabled) = 1; - - GVAR(classes)[] = {QGVAR(medium), QGVAR(medium_HD)}; - GVAR(metal) = 23000; - GVAR(charge) = 3148; - GVAR(gurney_c) = 2830; - GVAR(gurney_k) = "1/2"; - }; - class Sh_125mm_HE: Sh_120mm_HE { - GVAR(enabled) = 1; - - GVAR(classes)[] = {QGVAR(medium), QGVAR(medium_HD)}; - GVAR(metal) = 16000; - GVAR(charge) = 3200; - GVAR(gurney_c) = 2440; - GVAR(gurney_k) = "1/2"; - }; - class ModuleOrdnanceHowitzer_F_ammo: Sh_155mm_AMOS { - GVAR(enabled) = 1; - - GVAR(classes)[] = {QGVAR(large), QGVAR(large), QGVAR(large_HD), QGVAR(large), QGVAR(huge), QGVAR(huge_HD), QGVAR(huge)}; - GVAR(metal) = 1950; - GVAR(charge) = 15800; - GVAR(gurney_c) = 2320; - GVAR(gurney_k) = "1/2"; - }; - - - class B_65x39_Caseless; + class B_65x39_Caseless: BulletBase {}; class GVAR(base): B_65x39_Caseless { ACE_damageType = "grenade"; // compatibility with medical_damage, shrapnel should produce grenade wounds - timeToLive = 12; + timeToLive = 4; typicalSpeed = 1500; + maxSpeed = 1200; deflecting = 65; + GVAR(skip) = 1; }; class GVAR(tiny): GVAR(base) { @@ -317,7 +61,7 @@ class CfgAmmo { }; class GVAR(large_HD): GVAR(large) { - hit = 28; + hit = 28; indirectHit = 2; indirectHitRange = 0.25; airFriction = QUOTE(BASE_DRAG_HD*0.65); @@ -340,21 +84,6 @@ class CfgAmmo { caliber = 2.8; }; - class GVAR(spall_small): GVAR(small) { - timeToLive = 0.1; - }; - - class GVAR(spall_medium): GVAR(medium) { - timeToLive = 0.15; - }; - - class GVAR(spall_large): GVAR(large) { - timeToLive = 0.25; - }; - - class GVAR(spall_huge): GVAR(huge) { - timeToLive = 0.3; - }; - - #include "CfgAmmoReflections.hpp" + #include "CfgAmmoSpall.hpp" + #include "CfgAmmoFragParameters.hpp" }; diff --git a/addons/frag/CfgAmmoBaseClasses.hpp b/addons/frag/CfgAmmoBaseClasses.hpp new file mode 100644 index 00000000000..413dfe3dd93 --- /dev/null +++ b/addons/frag/CfgAmmoBaseClasses.hpp @@ -0,0 +1,47 @@ +// We need this since autocannons generally inherit from BulletBase +class BulletCore; +class BulletBase: BulletCore {}; + +class GrenadeCore; +class GrenadeBase: GrenadeCore {}; + +class LaserBombCore; +class ammo_Bomb_LaserGuidedBase: LaserBombCore {}; + +class MissileCore; +class MissileBase: MissileCore {}; + +class RocketCore; +class RocketBase: RocketCore { + GVAR(skip) = 1; +}; + +class ArtilleryRocketCore: RocketCore {}; + +class ShellCore; +class ShellBase: ShellCore {}; + +class ShotDeployCore; +class ShotDeployBase: ShotDeployCore { + GVAR(skip) = 1; +}; + +class ShotgunCore; +class ShotgunBase: ShotgunCore {}; + +class SubmunitionCore; +class SubmunitionBase: SubmunitionCore { + GVAR(skip) = 1; +}; + +class BoundingMineCore; +class BoundingMineBase: BoundingMineCore {}; + +class PipeBombCore; +class PipeBombBase: PipeBombCore {}; + +class DirectionalBombCore; +class DirectionalBombBase: DirectionalBombCore {}; + +class MineCore; +class MineBase: MineCore {}; diff --git a/addons/frag/CfgAmmoFragParameters.hpp b/addons/frag/CfgAmmoFragParameters.hpp new file mode 100644 index 00000000000..8d6402c3772 --- /dev/null +++ b/addons/frag/CfgAmmoFragParameters.hpp @@ -0,0 +1,796 @@ +// ~~~~ Autocannons +class B_19mm_HE: BulletBase { + GVAR(skip) = 1; +}; + +class B_20mm: BulletBase { + // Used in Weisel/AWC Nyx, which makes it a Rheinmetall Mk 20 Rh-202 + // Based on jane's ammunition handbook ~2002 (archive.org) + GVAR(skip) = 1; + GVAR(charge) = 6; + GVAR(metal) = 114; + GVAR(gurney_c) = 2830; + GVAR(gurney_k) = "1/2"; + GVAR(classes)[] = {QGVAR(tiny)}; +}; + +class B_20mm_AP: BulletBase { + GVAR(skip) = 1; +}; + +class ammo_Gun20mmAABase: BulletBase { // 20x139mm + GVAR(skip) = 1; + GVAR(charge) = 6; + GVAR(metal) = 114; + GVAR(gurney_c) = 2830; + GVAR(gurney_k) = "1/2"; + GVAR(classes)[] = {QGVAR(tiny)}; +}; + +class ammo_Gun30mmAABase: BulletBase { // 30x210mm HEI + GVAR(skip) = 0; + GVAR(charge) = 40; + GVAR(metal) = 410; + GVAR(gurney_k) = "1/2"; + GVAR(gurney_c) = 2901; + GVAR(classes)[] = {QGVAR(tiny), QGVAR(tiny), QGVAR(small)}; + +}; + +class B_30mm_HE: B_19mm_HE { + // Used in Gorgon (Pandur II), assuming it's a L21A1 RARDEN, specifically HEI-T due to tracers + // https://ordtech-industries.com/30x170-mm-ammunition-for-cannons-oerlikon-kcb-hispano-hs831l-l21-rarden/ + GVAR(skip) = 0; + GVAR(charge) = 25; + GVAR(metal) = 320; + GVAR(gurney_c) = 2552; + GVAR(gurney_k) = "1/2"; + GVAR(classes)[] = {QGVAR(tiny), QGVAR(tiny), QGVAR(small)}; +}; +class B_30mm_MP: B_30mm_HE { + // Used in Mora (FV510 Warrior), assuming it's a Mk44 Bushmaster II, specifically HEI-T due to tracers + // http://www.navweaps.com/Weapons/WNUS_30mm_BushmasterII.php + GVAR(metal) = 388; + GVAR(charge) = 56; + GVAR(gurney_c) = 2600; + GVAR(gurney_k) = "1/2"; + GVAR(classes)[] = {QGVAR(tiny), QGVAR(tiny), QGVAR(small)}; +}; + +class Gatling_30mm_HE_Plane_CAS_01_F: BulletBase { + GVAR(skip) = 0; + GVAR(classes)[] = {QGVAR(tiny), QGVAR(small)}; + GVAR(metal) = 388; + GVAR(charge) = 56; + GVAR(gurney_c) = 2600; // guessed + GVAR(gurney_k) = "1/2"; +}; + +class ammo_Gun35mmAABase: BulletBase { + // Gepard uses an Oerlikon GDF and the AA vehicles mimics it like it + // https://en.wikipedia.org/wiki/Oerlikon_GDF#Ammunition + // https://www.nammo.com/product/our-products/ammunition/medium-caliber-ammunition/35-mm-series/35-mm-x-228-hei-sd-and-hei-t-sd/ + GVAR(skip) = 0; + GVAR(charge) = 98; + GVAR(metal) = 400; + GVAR(gurney_k) = "1/2"; + GVAR(gurney_c) = 2700; + GVAR(classes)[] = {QGVAR(tiny), QGVAR(tiny), QGVAR(small)}; +}; + +class B_35mm_AA: BulletBase { + // Gepard uses an Oerlikon GDF and the AA vehicles mimics it like it + // https://en.wikipedia.org/wiki/Oerlikon_GDF#Ammunition + // https://www.nammo.com/product/our-products/ammunition/medium-caliber-ammunition/35-mm-series/35-mm-x-228-hei-sd-and-hei-t-sd/ + GVAR(skip) = 0; + GVAR(charge) = 98; + GVAR(metal) = 400; + GVAR(gurney_k) = "1/2"; + GVAR(gurney_c) = 2700; + GVAR(classes)[] = {QGVAR(tiny), QGVAR(tiny), QGVAR(small)}; +}; + +class B_40mm_GPR: B_30mm_HE { + // Based on noted 40mm Autocannons, base ROF, and ammo names, looks to be a CTAS40, specifically GPR-PD-T + // https://www.cta-international.com/ammunition/ + // https://ndiastorage.blob.core.usgovcloudapi.net/ndia/2002/gun/leslie.pdf + GVAR(classes)[] = {QGVAR(tiny), QGVAR(small)}; + GVAR(metal) = 750; + GVAR(charge) = 120; + GVAR(gurney_c) = 2700; // guessed + GVAR(gurney_k) = "1/2"; +}; +class UnderwaterMine_Range_Ammo: MineBase { + GVAR(skip) = 1; +}; + +// ~~~~ Bombs: +class Bo_GBU12_LGB: ammo_Bomb_LaserGuidedBase { + GVAR(skip) = 0; + GVAR(charge) = 87000; + GVAR(metal) = 140000; + GVAR(gurney_c) = 2320; + GVAR(gurney_k) = "1/2"; + GVAR(classes)[] = {QGVAR(large), QGVAR(large), QGVAR(large_HD), QGVAR(large), QGVAR(huge), QGVAR(huge_HD), QGVAR(huge)}; +}; +class Bomb_03_F: ammo_Bomb_LaserGuidedBase { + GVAR(skip) = 0; + GVAR(charge) = 100000; + GVAR(metal) = 150000; + GVAR(gurney_c) = 2320; + GVAR(gurney_k) = "1/2"; + GVAR(classes)[] = {QGVAR(large), QGVAR(large), QGVAR(large), QGVAR(large), QGVAR(huge), QGVAR(huge_HD), QGVAR(huge)}; +}; +class Bomb_04_F: ammo_Bomb_LaserGuidedBase { + GVAR(skip) = 0; + GVAR(charge) = 87000; + GVAR(metal) = 140000; + GVAR(gurney_c) = 2320; + GVAR(gurney_k) = "1/2"; + GVAR(classes)[] = {QGVAR(large), QGVAR(large), QGVAR(large), QGVAR(large), QGVAR(huge), QGVAR(huge_HD), QGVAR(huge)}; +}; +class BombCluster_01_Ammo_F: Bomb_04_F { + GVAR(skip) = 1; +}; +class ammo_Bomb_SmallDiameterBase: ammo_Bomb_LaserGuidedBase { + GVAR(skip) = 0; + GVAR(charge) = 160; // kg + GVAR(metal) = 113; // kg + GVAR(gurney_c) = 2830; + GVAR(gurney_k) = "1/2"; + GVAR(classes)[] = {QGVAR(medium), QGVAR(medium), QGVAR(large), QGVAR(large), QGVAR(huge), QGVAR(huge_HD)}; +}; + +class BombCore; +class Bo_Mk82: BombCore { + GVAR(classes)[] = {QGVAR(large), QGVAR(large), QGVAR(large_HD), QGVAR(large), QGVAR(huge), QGVAR(huge_HD), QGVAR(huge)}; + GVAR(metal) = 140000; + GVAR(charge) = 87000; + GVAR(gurney_c) = 2320; + GVAR(gurney_k) = "1/2"; +}; + +class Mo_cluster_AP: ShellBase { + GVAR(skip) = 1; + GVAR(force) = 0; +}; +class Mo_cluster_Bomb_01_F: Mo_cluster_AP { // Mk 118 Rockeye + GVAR(skip) = 0; + GVAR(classes)[] = {QGVAR(small)}; + GVAR(metal) = 400; + GVAR(charge) = 180; + GVAR(gurney_c) = 2700; + GVAR(gurney_k) = "1/2"; +}; +class Mo_cluster_Bomb_02_F: Mo_cluster_Bomb_01_F { // ShOAB-0.5 + GVAR(classes)[] = {QGVAR(small)}; + GVAR(metal) = 400; + GVAR(charge) = 100; + GVAR(gurney_c) = 2700; +}; +class Mo_cluster_Bomb_03_F: Mo_cluster_Bomb_01_F { // idk, @lambda.tiger on the ace discord if you find out + GVAR(classes)[] = {QGVAR(small)}; + GVAR(metal) = 400; + GVAR(charge) = 140; + GVAR(gurney_c) = 2400; +}; + +// ~~~~ Grenades: +class Grenade; +class GrenadeHand: Grenade { + GVAR(skip) = 0; + GVAR(force) = 1; + /* + These values are based on the M67 Grenade, should be tweaked for + individual grenades. + */ + GVAR(classes)[] = {QGVAR(tiny)}; + GVAR(metal) = 210; // metal in grams + GVAR(charge) = 185; // explosive in grams + GVAR(gurney_c) = 2843; // Gurney velocity constant for explosive type. See: http://en.wikipedia.org/wiki/Gurney_equations + GVAR(gurney_k) = "3/5"; // Gurney shape factor, in this case a sphere. See: http://en.wikipedia.org/wiki/Gurney_equations +}; +class mini_Grenade: GrenadeHand { + GVAR(classes)[] = {QGVAR(tiny)}; + GVAR(metal) = 104; + GVAR(charge) = 36; +}; +class GrenadeHand_stone: GrenadeHand { + GVAR(skip) = 1; +}; +class SmokeShell: GrenadeHand { + GVAR(skip) = 1; +}; +class G_40mm_HE: GrenadeBase { + // Source: http://www.inetres.com/gp/military/infantry/grenade/40mm_ammo.html#M441 + GVAR(skip) = 0; + GVAR(force) = 1; + GVAR(classes)[] = {QGVAR(small), QGVAR(tiny), QGVAR(small)}; + GVAR(metal) = 200; + GVAR(charge) = 32; + GVAR(gurney_c) = 2700; + GVAR(gurney_k) = "3/5"; // interior fragmenter/charge is a sphere +}; +class G_20mm_HE: G_40mm_HE { + GVAR(classes)[] = {QGVAR(tiny)}; + GVAR(metal) = 50; + GVAR(charge) = 8; + GVAR(gurney_c) = 2700; + GVAR(gurney_k) = "3/5"; // interior fragmenter/charge is a sphere +}; +class G_40mm_HEDP: G_40mm_HE { + // Source: http://www.inetres.com/gp/military/infantry/grenade/40mm_ammo.html#M433 + GVAR(classes)[] = {QGVAR(tiny), QGVAR(small), QGVAR(small_HD)}; + GVAR(metal) = 200; + GVAR(charge) = 45; + GVAR(gurney_c) = 2830; + GVAR(gurney_k) = "1/2"; +}; + +class ACE_G_40mm_HEDP: G_40mm_HEDP {}; +class ACE_G_40mm_HE: G_40mm_HE {}; +class ACE_G_40mm_Practice: ACE_G_40mm_HE { + GVAR(skip) = 1; + GVAR(force) = 0; + EGVAR(vehicle_damage,incendiary) = 0; +}; + +// ~~~~ Mines & UXO +class ATMine_Range_Ammo: MineBase { + GVAR(skip) = 1; +}; + +class APERSMine_Range_Ammo: MineBase { // VS-50 + GVAR(skip) = 0; + GVAR(force) = 0; + GVAR(classes)[] = {QGVAR(tiny), QGVAR(tiny), QGVAR(small)}; + GVAR(metal) = 100; + GVAR(charge) = 50; + GVAR(gurney_c) = 2700; + GVAR(gurney_k) = "1/3"; +}; + +class APERSBoundingMine_Range_Ammo: BoundingMineBase { + GVAR(skip) = 1; + GVAR(force) = 0; + GVAR(classes)[] = {QGVAR(tiny)}; + GVAR(metal) = 80; + GVAR(charge) = 170; + GVAR(gurney_c) = 2700; + GVAR(gurney_k) = "3/5"; +}; + +class TrainingMine_Ammo: APERSMine_Range_Ammo { + GVAR(skip) = 1; +}; + +class SLAMDirectionalMine_Wire_Ammo: DirectionalBombBase { + GVAR(skip) = 1; +}; + +class APERSTripMine_Wire_Ammo: DirectionalBombBase { + GVAR(skip) = 0; + GVAR(force) = 0; + GVAR(classes)[] = {QGVAR(tiny)}; + GVAR(metal) = 210; + GVAR(charge) = 185; + GVAR(gurney_c) = 2843; + GVAR(gurney_k) = "3/5"; +}; + +class IEDUrbanBig_Remote_Ammo: PipeBombBase { + GVAR(skip) = 0; + GVAR(classes)[] = {QGVAR(large), QGVAR(large), QGVAR(large_HD), QGVAR(large), QGVAR(huge), QGVAR(huge_HD), QGVAR(huge)}; + GVAR(metal) = 36000; + GVAR(charge) = 9979; + GVAR(gurney_c) = 2440; + GVAR(gurney_k) = "3/5"; +}; +class IEDLandBig_Remote_Ammo: PipeBombBase { + GVAR(skip) = 0; + GVAR(classes)[] = {QGVAR(large), QGVAR(large), QGVAR(large_HD), QGVAR(large), QGVAR(huge), QGVAR(huge_HD), QGVAR(huge)}; + GVAR(metal) = 36000; + GVAR(charge) = 9979; + GVAR(gurney_c) = 2440; + GVAR(gurney_k) = "3/5"; +}; +class IEDUrbanSmall_Remote_Ammo: PipeBombBase { + GVAR(skip) = 0; + GVAR(metal) = 23000; + GVAR(charge) = 3148; + GVAR(gurney_c) = 2830; + GVAR(gurney_k) = "3/5"; + GVAR(classes)[] = {QGVAR(small), QGVAR(medium), QGVAR(medium_HD)}; +}; +class IEDLandSmall_Remote_Ammo: PipeBombBase { + GVAR(skip) = 0; + GVAR(metal) = 23000; + GVAR(charge) = 3148; + GVAR(gurney_c) = 2830; + GVAR(gurney_k) = "3/5"; + GVAR(classes)[] = {QGVAR(small), QGVAR(medium), QGVAR(medium_HD)}; +}; + +class APERSMineDispenser_Ammo: PipeBombBase { + GVAR(skip) = 1; +}; + +class UXO1_Ammo_Base_F: APERSMine_Range_Ammo { // Mk 118 Rockeye + GVAR(skip) = 0; + GVAR(charge) = 180; + GVAR(metal) = 400; + GVAR(gurney_c) = 2700; + GVAR(gurney_k) = "1/2"; + GVAR(classes)[] = {QGVAR(small)}; +}; + +class UXO2_Ammo_Base_F: UXO1_Ammo_Base_F { + GVAR(charge) = 100; + GVAR(classes)[] = {QGVAR(small), QGVAR(tiny)}; +}; + +class APERSMineDispenser_Mine_Ammo: APERSMine_Range_Ammo { + GVAR(skip) = 1; +}; + +class UXO_deploy_base_f: SubmunitionBase {}; + +class ClaymoreDirectionalMine_Remote_Ammo: DirectionalBombBase { + GVAR(skip) = 1; +}; + +class SatchelCharge_Remote_Ammo: PipeBombBase { + GVAR(skip) = 1; +}; + +class DemoCharge_Remote_Ammo: PipeBombBase { + GVAR(skip) = 1; +}; + +class Drone_explosive_ammo: MineBase { + GVAR(skip) = 1; +}; + +class BombDemine_01_Ammo_F: BombCore { + GVAR(skip) = 1; +}; + +class BombDemine_01_SubAmmo_F: ShellBase { + GVAR(skip) = 1; +}; + +// ~~~~ Rockets: +class R_PG7_F: RocketBase { + GVAR(skip) = 0; + GVAR(force) = 0; + GVAR(classes)[] = {QGVAR(tiny), QGVAR(small)}; + GVAR(metal) = 100; + GVAR(charge) = 560; + GVAR(gurney_c) = 2730; + GVAR(gurney_k) = "1/2"; +}; + +class R_PG32V_F: RocketBase { + GVAR(skip) = 1; +}; +class R_TBG32V_F: R_PG32V_F { // Thermobaric + GVAR(skip) = 0; + GVAR(metal) = 400; + GVAR(charge) = 210; + GVAR(gurney_c) = 2800; + GVAR(gurney_k) = "3/5"; + GVAR(classes)[] = {QGVAR(medium_HD)}; +}; + +class M_SPG9_HEAT: RocketBase { + GVAR(skip) = 0; + GVAR(metal) = 4150; + GVAR(charge) = 340; + GVAR(gurney_c) = 2970; + GVAR(gurney_k) = "1/2"; + GVAR(classes)[] = {QGVAR(small_HD), QGVAR(medium_HD)}; +}; +class M_SPG9_HE: M_SPG9_HEAT { + GVAR(metal) = 4695; + GVAR(charge) = 655; + GVAR(gurney_c) = 2800; + GVAR(gurney_k) = "1/2"; + GVAR(classes)[] = {QGVAR(small), QGVAR(tiny), QGVAR(medium), QGVAR(medium)}; +}; + +// https://armypubs.army.mil/epubs/DR_pubs/DR_a/pdf/web/ARN18072_TC%203-22x84%20FINAL%20WEB.pdf +class R_MRAAWS_HEAT_F: RocketBase { // Table A-20. HEAT 751 + GVAR(skip) = 0; + GVAR(metal) = 2265; + GVAR(charge) = 635; + GVAR(gurney_c) = 2970; + GVAR(gurney_k) = "1/2"; + GVAR(classes)[] = {QGVAR(small)}; + +}; +class R_MRAAWS_HEAT55_F: R_MRAAWS_HEAT_F { // Table A-16. HEAT 551C + GVAR(metal) = 1940; + GVAR(charge) = 460; +}; +class R_MRAAWS_HE_F: R_MRAAWS_HEAT_F { // Table A-6. HE 441D RS + GVAR(metal) = 2300; + GVAR(charge) = 590; + GVAR(gurney_c) = 2800; + GVAR(gurney_k) = "3/5"; + GVAR(classes)[] = {QGVAR(small)}; +}; + +class R_80mm_HE: RocketBase { // S-8D + GVAR(skip) = 0; + GVAR(charge) = 2150; + GVAR(metal) = 1650; + GVAR(gurney_c) = 2700; + GVAR(gurney_k) = "1/2"; + GVAR(classes)[] = {QGVAR(small), QGVAR(tiny), QGVAR(tiny)}; +}; +class R_60mm_HE: R_80mm_HE { // no idea but looks like a FFAR so made it weaker + GVAR(metal) = 1040; + GVAR(charge) = 3850; +}; + +class m_70mm_saami: MissileBase { + GVAR(skip) = 0; + GVAR(charge) = 2150; + GVAR(metal) = 1650; + GVAR(gurney_c) = 2700; + GVAR(gurney_k) = "1/2"; + GVAR(classes)[] = {QGVAR(small), QGVAR(tiny), QGVAR(tiny)}; +}; + +class Rocket_04_HE_F: MissileBase { // Shrieker (Hydra 70) + GVAR(skip) = 0; + GVAR(classes)[] = {QGVAR(medium), QGVAR(medium_HD)}; + GVAR(metal) = 3850; + GVAR(charge) = 1040; + GVAR(gurney_c) = 2700; + GVAR(gurney_k) = "1/2"; +}; +class Rocket_03_HE_F: Rocket_04_HE_F { // S-8DM makes the most sense + GVAR(metal) = 1800; + GVAR(charge) = 2000; + GVAR(gurney_c) = 2300; +}; +class Rocket_04_AP_F: Rocket_04_HE_F { + GVAR(skip) = 1; +}; + +class ammo_Missile_CannonLaunchedBase: MissileBase { + GVAR(skip) = 1; +}; + +class R_230mm_fly: ShellBase { + GVAR(skip) = 0; + GVAR(charge) = 100; // kg + GVAR(metal) = 150; // kg + GVAR(gurney_c) = 2320; + GVAR(gurney_k) = "3/5"; + GVAR(classes)[] = {QGVAR(large), QGVAR(large), QGVAR(large), QGVAR(large), QGVAR(huge), QGVAR(huge_HD), QGVAR(huge)}; +}; + +class M_PG_AT: MissileBase { // DAGR M247 warhead + GVAR(skip) = 0; + GVAR(classes)[] = {QGVAR(tiny), QGVAR(small), QGVAR(small), QGVAR(medium)}; + GVAR(charge) = 910; + GVAR(metal) = 3085; + GVAR(gurney_c) = 2700; + GVAR(gurney_k) = "1/2"; +}; +class M_AT: M_PG_AT { // DAR (Hydra 70) M151 warhead + GVAR(classes)[] = {QGVAR(medium), QGVAR(medium_HD)}; + GVAR(charge) = 1040; + GVAR(metal) = 3850; + GVAR(gurney_c) = 2700; +}; + +// ~~~~ Missiles: +class Missile_AGM_02_F: MissileBase { + // Source: http://fas.org/man/dod-101/sys/smart/agm-65.htm + GVAR(skip) = 0; + GVAR(classes)[] = {QGVAR(medium), QGVAR(medium_HD)}; + GVAR(metal) = 56250; + GVAR(charge) = 39000; + GVAR(gurney_c) = 2700; + GVAR(gurney_k) = "1/2"; +}; +class Missile_AGM_01_F: Missile_AGM_02_F { // Kh-25MTP !!! fix me +}; +class M_Jian_AT: Missile_AGM_01_F { // imaginary missile? Not simiklar to any modern HJ-x or otherwise +}; + +class M_Titan_AA: MissileBase { + GVAR(skip) = 0; + GVAR(metal) = 1980; + GVAR(charge) = 1020; + GVAR(gurney_c) = 2501; + GVAR(gurney_k) = "1/2"; + GVAR(classes)[] = {QGVAR(tiny), QGVAR(tiny), QGVAR(small)}; +}; +class M_Titan_AT: MissileBase { + GVAR(skip) = 1; +}; +class M_Titan_AP: M_Titan_AT { + GVAR(skip) = 0; + GVAR(metal) = 400; + GVAR(charge) = 210; + GVAR(gurney_c) = 2800; + GVAR(gurney_k) = "3/5"; + GVAR(classes)[] = {QGVAR(medium_HD)}; +}; + +class M_Scalpel_AT: MissileBase { // 9K121 Vikhr + GVAR(skip) = 0; + GVAR(classes)[] = {QGVAR(small), QGVAR(medium), QGVAR(medium_HD)}; + GVAR(metal) = 10000; + GVAR(charge) = 3000; + GVAR(gurney_c) = 2700; + GVAR(gurney_k) = "1/2"; +}; +class ACE_Hellfire_AGM114K: M_Scalpel_AT { + // Source: http://www.designation-systems.net/dusrm/m-114.html + GVAR(skip) = 0; + + GVAR(classes)[] = {QGVAR(medium), QGVAR(medium_HD)}; + GVAR(metal) = 8000; + GVAR(charge) = 2400; + GVAR(gurney_c) = 2700; + GVAR(gurney_k) = "1/2"; +}; + +class ammo_Missile_CruiseBase: MissileBase {}; +class ammo_missile_cruise_01: ammo_Missile_CruiseBase { + GVAR(skip) = 0; + GVAR(classes)[] = {QGVAR(small), QGVAR(medium), QGVAR(large)}; + GVAR(metal) = 700000; + GVAR(charge) = 110000; + GVAR(gurney_c) = 2600; + GVAR(gurney_k) = "3/5"; +}; +class ammo_Missile_Cruise_01_Cluster: ammo_missile_cruise_01 {}; + +class ammo_Missile_AntiRadiationBase: MissileBase { + GVAR(gurney_k) = "1/2"; + GVAR(gurney_c) = 2400; +}; +class ammo_Missile_HARM: ammo_Missile_AntiRadiationBase { + GVAR(skip) = 0; + GVAR(classes)[] = {QGVAR(small), QGVAR(medium), QGVAR(large)}; + GVAR(charge) = 10000; + GVAR(metal) = 58000; +}; +class ammo_Missile_KH58: ammo_Missile_AntiRadiationBase { + GVAR(skip) = 0; + GVAR(classes)[] = {QGVAR(small), QGVAR(medium), QGVAR(large)}; + GVAR(charge) = 20000; + GVAR(metal) = 129000; +}; + +class M_Zephyr: M_Titan_AA { // model is an AMRAAM- WDU-41/B warhead + GVAR(skip) = 0; + GVAR(metal) = 12800; + GVAR(charge) = 4000; + GVAR(gurney_c) = 2900; + GVAR(gurney_k) = "1/2"; + GVAR(classes)[] = {QGVAR(tiny), QGVAR(medium), QGVAR(small), QGVAR(small)}; +}; + +class M_Air_AA: MissileBase { // Looks not real, maybe r-73 inspired? + GVAR(skip) = 0; + GVAR(charge) = 2450; + GVAR(metal) = 4950; + GVAR(gurney_c) = 2700; + GVAR(gurney_k) = "1/2"; + GVAR(classes)[] = {QGVAR(tiny), QGVAR(medium), QGVAR(small), QGVAR(small)}; +}; + +class Missile_AA_04_F: MissileBase { + GVAR(skip) = 0; + GVAR(charge) = 4400; + GVAR(metal) = 5000; + GVAR(gurney_c) = 2830; + GVAR(gurney_k) = "1/2"; +}; +class Missile_AA_03_F: Missile_AA_04_F { + GVAR(charge) = 2450; + GVAR(metal) = 4950; + GVAR(gurney_c) = 2700; +}; + +class ammo_Missile_ShortRangeAABase: MissileBase { + GVAR(gurney_k) = "1/2"; +}; +class ammo_Missile_rim116: ammo_Missile_ShortRangeAABase { + GVAR(skip) = 0; + GVAR(charge) = 400; + GVAR(metal) = 730; + GVAR(gurney_c) = 2400; + GVAR(classes)[] = {QGVAR(tiny), QGVAR(small)}; +}; +class ammo_Missile_BIM9X: ammo_Missile_ShortRangeAABase { + GVAR(skip) = 0; + GVAR(charge) = 440; + GVAR(metal) = 500; + GVAR(gurney_c) = 2900; + GVAR(classes)[] = {QGVAR(small), QGVAR(tiny)}; +}; +class ammo_Missile_AA_R73: ammo_Missile_ShortRangeAABase { + GVAR(skip) = 0; + GVAR(charge) = 2450; + GVAR(metal) = 4950; + GVAR(gurney_c) = 2700; + GVAR(classes)[] = {QGVAR(small), QGVAR(small), QGVAR(tiny)}; +}; + +class ammo_Missile_MediumRangeAABase: MissileBase { + GVAR(gurney_c) = 2900; + GVAR(gurney_k) = "1/2"; +}; +class ammo_Missile_rim162: ammo_Missile_MediumRangeAABase { + GVAR(skip) = 0; + GVAR(charge) = 138; // dg + GVAR(metal) = 252; // dg + GVAR(gurney_c) = 2400; + GVAR(classes)[] = {QGVAR(small), QGVAR(medium), QGVAR(medium), QGVAR(large)}; +}; +class ammo_Missile_AMRAAM_C: ammo_Missile_MediumRangeAABase { + GVAR(skip) = 0; + GVAR(charge) = 70; // dg + GVAR(metal) = 128; // dg + GVAR(classes)[] = {QGVAR(small), QGVAR(medium), QGVAR(medium)}; +}; +class ammo_Missile_AMRAAM_D: ammo_Missile_MediumRangeAABase { + GVAR(skip) = 0; + GVAR(charge) = 70; // dg + GVAR(metal) = 128; // dg + GVAR(classes)[] = {QGVAR(small), QGVAR(medium), QGVAR(medium)}; +}; +class ammo_Missile_AA_R77: ammo_Missile_MediumRangeAABase { + GVAR(skip) = 0; + GVAR(charge) = 80; // dg + GVAR(metal) = 145; // dg + GVAR(gurney_c) = 2700; + GVAR(classes)[] = {QGVAR(small), QGVAR(medium), QGVAR(large), QGVAR(large)}; +}; + +class M_Vorona_HEAT: MissileBase { + // tandem shaped charges + GVAR(skip) = 1; +}; +class M_Vorona_HE: M_Vorona_HEAT { + // All signs point to this being a thermobaric round so low frag count + GVAR(skip) = 0; + GVAR(metal) = 13800; + GVAR(charge) = 4950; + GVAR(gurney_c) = 2800; + GVAR(gurney_k) = "1/2"; + GVAR(classes)[] = {QGVAR(tiny)}; +}; + +class M_127mm_Firefist_AT: MissileBase { // HOT missile + GVAR(skip) = 1; +}; + +class M_NLAW_AT_F: MissileBase { + GVAR(skip) = 1; +}; + +// ~~~~ Shell +class Sh_75mm_Railgun_APFSDS: ShellBase { + GVAR(skip) = 1; +}; +class Sh_120mm_APFSDS: ShellBase { + GVAR(skip) = 1; +}; +class Sh_125mm_APFSDS: Sh_120mm_APFSDS { + GVAR(skip) = 1; +}; + +class Sh_155mm_AMOS: ShellBase { + // Source: http://www.globalsecurity.org/military/systems/munitions/m795.htm + GVAR(skip) = 0; + GVAR(charge) = 9979; + GVAR(metal) = 36000; + GVAR(gurney_c) = 2440; + GVAR(gurney_k) = "1/2"; + GVAR(classes)[] = {QGVAR(large), QGVAR(large), QGVAR(large_HD), QGVAR(large), QGVAR(huge), QGVAR(huge_HD), QGVAR(huge)}; +}; + +class Sh_82mm_AMOS: Sh_155mm_AMOS { // VO-832DU + GVAR(charge) = 420; + GVAR(metal) = 2680; + GVAR(classes)[] = {QGVAR(medium), QGVAR(medium_HD)}; +}; + +class Sh_120mm_HE: ShellBase { + GVAR(skip) = 0; + GVAR(charge) = 3148; + GVAR(metal) = 23000; + GVAR(gurney_c) = 2830; + GVAR(gurney_k) = "1/2"; + GVAR(classes)[] = {QGVAR(small), QGVAR(medium), QGVAR(medium), QGVAR(medium_HD)}; +}; +class Sh_120mm_HEAT_MP: ShellBase { + GVAR(skip) = 0; + GVAR(charge) = 2500; + GVAR(metal) = 5000; + GVAR(gurney_c) = 2500; + GVAR(gurney_k) = "1/2"; + GVAR(classes)[] = {QGVAR(small), QGVAR(medium), QGVAR(medium), QGVAR(medium_HD)}; +}; + +class ammo_ShipCannon_120mm_HE: Sh_155mm_AMOS { + GVAR(charge) = 3148; + GVAR(metal) = 23000; + GVAR(gurney_c) = 2830; + GVAR(classes)[] = {QGVAR(small), QGVAR(small), QGVAR(medium), QGVAR(large)}; +}; + +class Sh_125mm_HE: Sh_120mm_HE { + GVAR(metal) = 19900; + GVAR(charge) = 3400; + GVAR(gurney_c) = 2901; +}; + +class Sh_125mm_HEAT: Sh_125mm_HE { + GVAR(skip) = 1; + GVAR(metal) = 16760; + GVAR(charge) = 1640; + GVAR(gurney_c) = 2901; +}; + +class Sh_105mm_HEAT_MP: Sh_125mm_HEAT { + GVAR(skip) = 0; + GVAR(classes)[] = {QGVAR(small), QGVAR(medium), QGVAR(medium), QGVAR(medium_HD)}; + GVAR(metal) = 11400; + GVAR(charge) = 7100; + GVAR(gurney_c) = 2800; +}; + +class ModuleOrdnanceHowitzer_F_ammo: Sh_155mm_AMOS { + GVAR(skip) = 0; + GVAR(metal) = 1950; + GVAR(charge) = 15800; + GVAR(gurney_c) = 2320; + GVAR(gurney_k) = "1/2"; + GVAR(classes)[] = {QGVAR(large), QGVAR(large), QGVAR(large_HD), QGVAR(large), QGVAR(huge), QGVAR(huge_HD), QGVAR(huge)}; +}; +class ammo_Penetrator_Base: ShellBase { + GVAR(skip) = 1; +}; + +// ~~~~ Special +class ProbingBeam_01_F: BulletBase { + GVAR(skip) = 1; +}; + +class IRStrobeBase: GrenadeCore { + GVAR(skip) = 1; +}; +class FlareCore: GrenadeCore { // flares shouldn't have EH, but in case + GVAR(skip) = 1; +}; + +class Default; +class Laserbeam: Default { + GVAR(skip) = 1; +}; + +class FuelExplosion: Default { + GVAR(skip) = 1; +}; + +class HelicopterExploSmall: ShellBase { + GVAR(skip) = 1; +}; + +class LightningBolt: ShellBase { + GVAR(skip) = 1; +}; + +class M_Mo_82mm_AT: MissileBase { + GVAR(skip) = 1; +}; diff --git a/addons/frag/CfgAmmoSpall.hpp b/addons/frag/CfgAmmoSpall.hpp new file mode 100644 index 00000000000..b3214fc4b04 --- /dev/null +++ b/addons/frag/CfgAmmoSpall.hpp @@ -0,0 +1,15 @@ +class GVAR(spall_small): GVAR(small) { + timeToLive = 0.1; +}; + +class GVAR(spall_medium): GVAR(medium) { + timeToLive = 0.15; +}; + +class GVAR(spall_large): GVAR(large) { + timeToLive = 0.25; +}; + +class GVAR(spall_huge): GVAR(huge) { + timeToLive = 0.3; +}; diff --git a/addons/frag/README.md b/addons/frag/README.md index ca62771f00e..797e652b207 100644 --- a/addons/frag/README.md +++ b/addons/frag/README.md @@ -1,4 +1,4 @@ ace_frag ======== -Shrapnel system for explosives. +Explosive fragmentation, round spalling, and explosive reflection diff --git a/addons/frag/XEH_PREP.hpp b/addons/frag/XEH_PREP.hpp index a7fb8ff8c34..16e2a95ef7c 100644 --- a/addons/frag/XEH_PREP.hpp +++ b/addons/frag/XEH_PREP.hpp @@ -1,25 +1,18 @@ +PREP(addBlackList); +PREP(dev_clearTraces); PREP(dev_debugAmmo); - +PREP(dev_drawTrace); +PREP(dev_fragCalcDump); +PREP(dev_sphereDraw); +PREP(dev_trackHitBox); +PREP(dev_trackObj); +PREP(doExplosions); +PREP(doReflections); PREP(doSpall); +PREP(findReflections); PREP(fired); PREP(frago); -PREP(spallTrack); - -// * Other */ -PREP(addBlackList); -PREP(dev_addTrack); -PREP(dev_drawTraces); -PREP(spallHP); -PREP(dev_startTracing); -PREP(dev_stopTracing); -PREP(dev_trackTrace); - -// New tracking mechanisms -PREP(masterPFH); -PREP(pfhRound); -PREP(addPfhRound); - -// Explosive Reflection -PREP(findReflections); -PREP(doExplosions); -PREP(doReflections); +PREP(getFragInfo); +PREP(getSpallInfo); +PREP(shouldFrag); +PREP(shouldSpall); diff --git a/addons/frag/XEH_postInit.sqf b/addons/frag/XEH_postInit.sqf index cc58e1d15b2..eb270095ba9 100644 --- a/addons/frag/XEH_postInit.sqf +++ b/addons/frag/XEH_postInit.sqf @@ -1,32 +1,56 @@ #include "script_component.hpp" -if (isServer) then { - GVAR(lastFragTime) = -1; - [QGVAR(frag_eh), LINKFUNC(frago)] call CBA_fnc_addEventHandler; -}; - ["CBA_settingsInitialized", { - if (!GVAR(enabled)) exitWith {}; + [{ + params ["_projectile", "_posASL"]; + + if (_projectile getVariable [QGVAR(blacklisted), false]) exitWith {}; + + private _ammo = typeOf _projectile; + if (GVAR(reflectionsEnabled)) then { + [_posASL, _ammo] call FUNC(doReflections); + }; + if (GVAR(enabled) && _ammo call FUNC(shouldFrag)) then { + // only let a unit make a frag event once per second + private _shotParents = getShotParents _projectile; + private _instigator = _shotParents select !isNull (_shotParents#1); + if (CBA_missionTime < (_instigator getVariable [QGVAR(nextFragEvent), -1])) exitWith {}; + _instigator setVariable [QGVAR(nextFragEvent), CBA_missionTime + ACE_FRAG_FRAG_UNIT_HOLDOFF]; + + // Wait a frame to make sure it doesn't target the dead + [{ + [QGVAR(frag_eh), _this] call CBA_fnc_serverEvent + }, [_posASL, _ammo, [objNull, _instigator]]] call CBA_fnc_execNextFrame; + }; + }] call EFUNC(common,addExplosionEventHandler); - // Register fire event handler ["ace_firedPlayer", LINKFUNC(fired)] call CBA_fnc_addEventHandler; ["ace_firedNonPlayer", LINKFUNC(fired)] call CBA_fnc_addEventHandler; ["ace_firedPlayerVehicle", LINKFUNC(fired)] call CBA_fnc_addEventHandler; ["ace_firedNonPlayerVehicle", LINKFUNC(fired)] call CBA_fnc_addEventHandler; - - addMissionEventHandler ["EachFrame", {call FUNC(masterPFH)}]; -}] call CBA_fnc_addEventHandler; - -// Cache for ammo type configs -GVAR(cacheRoundsTypesToTrack) = createHashMap; - - -// Debug stuff: - -#ifdef DRAW_FRAG_INFO -[] call FUNC(dev_startTracing); +#ifdef DEBUG_MODE_DRAW + [QGVAR(dev_clearTraces), LINKFUNC(dev_clearTraces)] call CBA_fnc_addEventHandler; + + if (!hasInterface) exitWith {}; + ["ace_firedPlayerVehicleNonLocal", LINKFUNC(dev_fired)] call CBA_fnc_addEventHandler; + ["ace_firedPlayerNonLocal", LINKFUNC(dev_fired)] call CBA_fnc_addEventHandler; + GVAR(dev_drawPFEH) = [LINKFUNC(dev_drawTrace), 0] call CBA_fnc_addPerFrameHandler; + ["ace_interact_menu_newControllableObject", { + params ["_type"]; + + private _action = [ + QGVAR(debugReset), + "Reset ACE Frag traces", + "", + {[QGVAR(dev_clearTraces), []] call CBA_fnc_globalEvent;}, + {GVAR(dev_trackLines) isNotEqualTo createHashMap} + ] call EFUNC(interact_menu,createAction); + + [_type, 1, ["ACE_SelfActions"], _action, true] call EFUNC(interact_menu,addActionToClass); + }] call CBA_fnc_addEventHandler; #endif +}] call CBA_fnc_addEventHandler; -#ifdef DEBUG_MODE_FULL +#ifdef LOG_FRAG_INFO [true, true, 30] call FUNC(dev_debugAmmo); #endif diff --git a/addons/frag/XEH_preInit.sqf b/addons/frag/XEH_preInit.sqf index dc616917b88..50353d88dc2 100644 --- a/addons/frag/XEH_preInit.sqf +++ b/addons/frag/XEH_preInit.sqf @@ -6,20 +6,30 @@ PREP_RECOMPILE_START; #include "XEH_PREP.hpp" PREP_RECOMPILE_END; -GVAR(blackList) = []; -GVAR(traceFrags) = false; +GVAR(spallInfoCache) = createHashMap; +GVAR(shouldSpallCache) = createHashMap; -GVAR(spallHPData) = []; -GVAR(spallIsTrackingCount) = 0; - -GVAR(traceID) = -1; -GVAR(traces) = []; -GVAR(tracesStarted) = false; - -GVAR(lastIterationIndex) = 0; -GVAR(objects) = []; -GVAR(arguments) = []; +GVAR(shouldFragCache) = createHashMap; +GVAR(fragInfoCache) = createHashMap; +GVAR(lastFragTime) = -1; #include "initSettings.inc.sqf" +GVAR(dev_trackLines) = createHashMap; +GVAR(dev_hitBoxes) = createHashMap; +GVAR(dev_eventSpheres) = []; +GVAR(dev_drawPFEH) = -1; + +#ifdef DEBUG_MODE_DRAW +#include "initSettingsDebug.inc.sqf" +#else +GVAR(debugOptions) = false; +GVAR(dbgSphere) = false; +GVAR(drawHitBox) = false; +#endif + +if (isServer) then { + [QGVAR(frag_eh), LINKFUNC(frago)] call CBA_fnc_addEventHandler; +}; + ADDON = true; diff --git a/addons/frag/config.cpp b/addons/frag/config.cpp index f3b409dbd03..488b72c5070 100644 --- a/addons/frag/config.cpp +++ b/addons/frag/config.cpp @@ -8,7 +8,7 @@ class CfgPatches { requiredVersion = REQUIRED_VERSION; requiredAddons[] = {"ace_common"}; author = ECSTRING(common,ACETeam); - authors[] = {"Nou"}; + authors[] = {"Nou", "johnb43", "Lambda.Tiger"}; url = ECSTRING(main,URL); VERSION_CONFIG; }; diff --git a/addons/frag/functions/fnc_addBlackList.sqf b/addons/frag/functions/fnc_addBlackList.sqf deleted file mode 100644 index d0cc127d059..00000000000 --- a/addons/frag/functions/fnc_addBlackList.sqf +++ /dev/null @@ -1,21 +0,0 @@ -#include "..\script_component.hpp" -/* - * Author: Jaynus, NouberNou - * Adds a round to the blacklist (will be ignored). - * - * Arguments: - * 0: Projectile - * - * Return Value: - * None - * - * Example: - * [bullet] call ace_frag_fnc_addBlackList - * - * Public: No - */ - -params ["_round"]; -TRACE_1("addBlackList",_round); - -GVAR(blackList) pushBack _round; diff --git a/addons/frag/functions/fnc_addBlacklist.sqf b/addons/frag/functions/fnc_addBlacklist.sqf new file mode 100644 index 00000000000..323a9cf87f0 --- /dev/null +++ b/addons/frag/functions/fnc_addBlacklist.sqf @@ -0,0 +1,24 @@ +#include "..\script_component.hpp" +/* + * Author: Jaynus, NouberNou + * Adds a round to the blacklist (will be ignored) and removes any ace_frag event handlers added to it. + * + * Arguments: + * 0: Projectile + * + * Return Value: + * None + * + * Example: + * [_projectile] call ace_frag_fnc_addBlackList + * + * Public: No + */ + +params ["_projectile"]; +TRACE_2("addBlackList",_projectile,typeOf projectile); + +_projectile setVariable [QGVAR(blacklisted), true]; +_projectile removeEventHandler ["HitPart", _projectile getVariable [QGVAR(hitPartEventHandler), -1]]; + +nil // return diff --git a/addons/frag/functions/fnc_addPfhRound.sqf b/addons/frag/functions/fnc_addPfhRound.sqf deleted file mode 100644 index b795a1dff04..00000000000 --- a/addons/frag/functions/fnc_addPfhRound.sqf +++ /dev/null @@ -1,77 +0,0 @@ -#include "..\script_component.hpp" -/* - * Author: Jaynus, NouberNou - * Starts tracking a round that will frag. - * Should only be called once per round. - * - * Arguments: - * 0: Shooter - * 1: Ammo classname - * 2: Projectile - * - * Return Value: - * None - * - * Example: - * [player, "handGrenade", bullet] call ace_frag_fnc_addPfhRound - * - * Public: No - */ - -params ["_gun", "_type", "_round"]; -TRACE_3("addPfhRound",_gun,_type,_round); - -if (!GVAR(enabled)) exitWith {TRACE_1("setting disabled",_this);}; - -if (!alive _round) exitWith {TRACE_1("round dead?",_this);}; - -if (_round in GVAR(blackList)) exitWith { - TRACE_1("round in blackList",_this); - REM(GVAR(blackList),_round); -}; - -// Exit on max track -if ((count GVAR(objects)) >= GVAR(maxTrack)) exitWith {TRACE_1("maxTrack limit",count GVAR(objects));}; - -private _doSpall = false; -if (GVAR(SpallEnabled)) then { - if (GVAR(spallIsTrackingCount) <= 0) then { - GVAR(spallHPData) = []; - }; - if (GVAR(spallIsTrackingCount) > 5) then { - TRACE_1("At Spall Limit",GVAR(spallIsTrackingCount)); - } else { - _doSpall = true; - INC(GVAR(spallIsTrackingCount)); - }; - TRACE_2("",_doSpall,GVAR(spallIsTrackingCount)); -}; - -#ifdef DRAW_FRAG_INFO -[ACE_player, _round, [0, 1, 0, 1]] call FUNC(dev_addTrack); -#endif - -// We only do the single track object check here. -// We should do an {!(_round in GVAR(objects))} -// But we leave that out here for optimization. So this cannot be a framework function -// Otherwise, it should only be added once and from the FiredEH -if (alive _round) then { - private _spallTrack = []; - private _spallTrackID = []; - - private _args = [ - _round, getPosASL _round, velocity _round, _type, diag_frameNo, getPosASL _round, _doSpall, _spallTrack, _spallTrackID, - getNumber (configFile >> "CfgAmmo" >> _type >> QGVAR(skip)), - getNumber (configFile >> "CfgAmmo" >> _type >> "explosive"), - getNumber (configFile >> "CfgAmmo" >> _type >> "indirectHitRange"), - getNumber (configFile >> "CfgAmmo" >> _type >> QGVAR(force)), - getNumber (configFile >> "CfgAmmo" >> _type >> "indirecthit") * (sqrt (getNumber (configFile >> "CfgAmmo" >> _type >> "indirectHitRange"))) - ]; - TRACE_1("Initializing track",_round); - GVAR(objects) pushBack _round; - GVAR(arguments) pushBack _args; - - if (_doSpall) then { - [_round, 1, _spallTrack, _spallTrackID] call FUNC(spallTrack); - }; -}; diff --git a/addons/frag/functions/fnc_dev_addTrack.sqf b/addons/frag/functions/fnc_dev_addTrack.sqf deleted file mode 100644 index 0e75a9fb994..00000000000 --- a/addons/frag/functions/fnc_dev_addTrack.sqf +++ /dev/null @@ -1,26 +0,0 @@ -#include "..\script_component.hpp" -/* - * Author: ACE-Team - * - * - * Arguments: - * None - * - * Return Value: - * None - * - * Example: - * call ace_frag_fnc_dev_addTrack - * - * Public: No - */ - -params ["_origin", "_obj", ["_color", [1, 0, 0, 1]]]; - -private _positions = []; -private _objSpd = vectorMagnitude (velocity _obj); -_positions pushBack [getPos _obj, _objSpd]; -private _data = [_origin, typeOf _origin, typeOf _obj, _objSpd, _positions, _color]; - -private _index = GVAR(traces) pushBack _data; -[DFUNC(dev_trackTrace), 0, [_obj, _index, CBA_missionTime]] call CBA_fnc_addPerFrameHandler; diff --git a/addons/frag/functions/fnc_dev_clearTraces.sqf b/addons/frag/functions/fnc_dev_clearTraces.sqf new file mode 100644 index 00000000000..6e2b820bacb --- /dev/null +++ b/addons/frag/functions/fnc_dev_clearTraces.sqf @@ -0,0 +1,25 @@ +#include "..\script_component.hpp" +/* + * Author: Lambda.Tiger + * Clears all dev spheres and traces. + * + * Arguments: + * None + * + * Return Value: + * None + * + * Example: + * call ace_frag_fnc_dev_clearTraces + * + * Public: No + */ + +{ + deleteVehicle _x; +} forEach GVAR(dev_eventSpheres); + +GVAR(dev_eventSpheres) = []; + +GVAR(dev_trackLines) = createHashMap; +GVAR(dev_hitBoxes) = createHashMap; diff --git a/addons/frag/functions/fnc_dev_debugAmmo.sqf b/addons/frag/functions/fnc_dev_debugAmmo.sqf index 4484edbdc43..fe868df3428 100644 --- a/addons/frag/functions/fnc_dev_debugAmmo.sqf +++ b/addons/frag/functions/fnc_dev_debugAmmo.sqf @@ -1,11 +1,15 @@ -#define DEBUG_MODE_FULL #include "..\script_component.hpp" /* - * Author: ACE-Team - * + * Author: ACE-Team, Lambda.Tiger + * This function will dump every ammo config that would generate ace_frag + * fragments that could be fired from a weapon. * * Arguments: - * None + * 0: Log ammo types that wouldn't normally frag (default: false) + * 1: Only print ammo without ACE_frag entries, inherited or otherwise (default: true) + * 2: Only export ammo classes of classes referenced in CfgMagazines and their + * submunitions (default: false) + * 3: Force a CSV format on debug print. (default: false) * * Return Value: * None @@ -17,65 +21,90 @@ */ params [ - ["_debugMissing", true, [false]], - ["_debugForce", false, [false]], - ["_debugSkippedFragPower", 30, [0]] + ["_logAll", false, [false]], + ["_printOnlyIncomplete", true, [true]], + ["_onlyShotAmmoTypes", false, [false]], + ["_csvFormat", false, [false]] ]; diag_log text format ["~~~~~~~~~~~~~Start [%1]~~~~~~~~~~~~~", _this]; +if (_csvFormat) then { + diag_log text format ["ammo,gurney_c,gurney_m,gurney_k,gurney_gC,skip,Inheritance"]; +}; -private _allMagsConfigs = configProperties [configFile >> "CfgMagazines", "isClass _x", true]; -private _processedCfgAmmos = []; - -{ - private _ammo = toLowerANSI getText (_x >> "ammo"); - if (_ammo != "" && {!(_ammo in _processedCfgAmmos)}) then { - _processedCfgAmmos pushBack _ammo; - - //Ignore mines/bombs - if (_ammo isKindOf "TimeBombCore") exitWith {}; - - _ammoConfig = configFile >> "CfgAmmo" >> _ammo; - - //Read configs and test if it would actually cause a frag, using same logic as FUNC(pfhRound) - private _skip = getNumber (_ammoConfig >> QGVAR(skip)); - private _explosive = getNumber (_ammoConfig >> "explosive"); - private _indirectRange = getNumber (_ammoConfig >> "indirectHitRange"); - private _force = getNumber (_ammoConfig >> QGVAR(force)); - private _fragPower = getNumber (_ammoConfig >> "indirecthit") * (sqrt ((getNumber (_ammoConfig >> "indirectHitRange")))); +// Gather all configs, either those that could be created from firing or all classes +private _allAmmoConfigs = createHashMap; +if (_onlyShotAmmoTypes) then { + private _configSearchFunction = { + params [ + ["_ammo", "", [""]] + ]; + if (_ammo isEqualTo "" || {_ammo in _allAmmoConfigs}) exitWith {}; + _allAmmoConfigs set [_ammo, 1]; + private _cfgAmmoRoot = configFile >> "CfgAmmo"; + private _submunitionConfig = _cfgAmmoRoot >> _ammo >> "submunitionAmmo"; + if (isArray _submunitionConfig) then { + private _subMunition = getArray _submunitionConfig; + for "_i" from 0 to count _subMunition - 1 do { + if (_i mod 2 == 0) then { + configName (_cfgAmmoRoot >> (_subMunition#_i)) call _configSearchFunction; + }; + }; + } else { + private _subMunition = getText _submunitionConfig; + if (_subMunition isNotEqualTo "") then { + configName (_cfgAmmoRoot >> _subMunition) call _configSearchFunction; + }; + }; + }; + private _allMagazineConfigs = configProperties [configFile >> "CfgMagazines", "isClass _x", true]; + private _cfgAmmoCfgPath = configFile >> "CfgAmmo"; + { + private _magAmmo = getText (_x >> "ammo"); + configName (_cfgAmmoCfgPath >> _magAmmo) call _configSearchFunction; + } forEach _allMagazineConfigs; + _allAmmoConfigs = keys _allAmmoConfigs; +} else { + _allAmmoConfigs = configProperties [configFile >> "CfgAmmo", "isClass _x && !('ace_frag' in configName _x)", true] apply {configName _x}; +}; - private _shouldAdd = (_skip == 0) && {(_force == 1) || {_explosive > 0.5 && {_indirectRange >= 4.5} && {_fragPower >= 35}}}; +private _processedCfgAmmos = 0; +private _printCount = 0; +{ // Begin forEach to check each ammo type + private _ammo = _x; + if (_ammo isNotEqualTo "") then { + INC(_processedCfgAmmos); - if (_shouldAdd) then { - if (_debugForce && {((getNumber(_ammoConfig >> "hit")) < 5) || {_fragPower < 10}}) then { - diag_log text format ["Ammo [%1] from Mag [%2] - Weak but will still frag!", _ammo, configName _x]; - diag_log text format [" - _force=%1,_fragPower=%2", _force, _fragPower]; - }; + private _ammoConfig = configFile >> "CfgAmmo" >> _ammo; + _ammo call FUNC(shouldFrag) params ["_shouldFrag"]; - private _warn = false; + if (_shouldFrag || _logAll) then { - _fragTypes = getArray (_ammoConfig >> QGVAR(CLASSES)); - if (_fragTypes isEqualTo []) then {_warn = true;}; - _c = getNumber(_ammoConfig >> QGVAR(CHARGE)); - if (_c == 0) then {_warn = true;}; - _m = getNumber(_ammoConfig >> QGVAR(METAL)); - if (_m == 0) then {_warn = true;}; - _k = getNumber(_ammoConfig >> QGVAR(GURNEY_K)); - if (_k == 0) then {_warn = true;}; - _gC = getNumber(_ammoConfig >> QGVAR(GURNEY_C)); - if (_gC == 0) then {_warn = true;}; + private _print = false; + private _skip = getNumber (_ammoConfig >> QGVAR(skip)); + private _fragTypes = getArray (_ammoConfig >> QGVAR(classes)); + if (_fragTypes isEqualTo []) then {_print = true;}; + private _c = getNumber (_ammoConfig >> QGVAR(charge)); + if (_c == 0) then {_print = true;}; + private _m = getNumber (_ammoConfig >> QGVAR(metal)); + if (_m == 0) then {_print = true;}; + private _k = getNumber (_ammoConfig >> QGVAR(gurney_k)); + if (_k == 0) then {_print = true;}; + private _gC = getNumber (_ammoConfig >> QGVAR(gurney_c)); + if (_gC == 0) then {_print = true;}; - if (_debugMissing && {_warn}) then { - diag_log text format ["Ammo [%1] from Mag [%2] MISSING frag configs:", _ammo, configName _x]; - diag_log text format [" - _c=%1,_m=%2,_k=%3,_gC=%4,_fragTypes=%5", _c, _m, _k, _gC, _fragTypes]; - }; - } else { - if ((_fragPower > _debugSkippedFragPower) && {isArray (_ammoConfig >> QGVAR(CLASSES))}) then { - diag_log text format ["Ammo [%1] from Mag [%2] has frag configs but will NOT frag:", _ammo, configName _x]; - diag_log text format ["- skip=%1,explosive=%2,indirectHitRange=%3,force=%4,fragPower=%5", _skip, _explosive, _indirectRange, _force, _fragPower]; + if (!_printOnlyIncomplete || {_print && _skip != 0}) then { + INC(_printCount); + if (_csvFormat) then { + diag_log text format ["%6,%1,%2,%3,%4,%5,%8,%7", _c, _m, _k, _gC, _skip, _ammo, [_ammoConfig, true] call BIS_fnc_returnParents, _shouldFrag]; + } else { + diag_log text format ["Ammo [%1] MISSING frag configs:", _ammo]; + diag_log text format ["_c=%1,_m=%2,_k=%3,_gC=%4,_skip=%5,_fragTypes=%6", _c, _m, _k, _gC, _skip, _fragTypes]; + }; }; }; }; -} forEach _allMagsConfigs; +} forEach _allAmmoConfigs; -diag_log text format ["~~~~~~~~~~~~~End [%1-%2]~~~~~~~~~~~~~", count _allMagsConfigs, count _processedCfgAmmos]; +diag_log text format ["~~~~~~~~~~~~~~End [%1-%2]~~~~~~~~~~~~~~", count _allAmmoConfigs, _processedCfgAmmos]; +diag_log text format ["~~~~~~~~~~~~~~Printed: %1~~~~~~~~~~~", _printCount]; diff --git a/addons/frag/functions/fnc_dev_drawTrace.sqf b/addons/frag/functions/fnc_dev_drawTrace.sqf new file mode 100644 index 00000000000..8b027e1f3ca --- /dev/null +++ b/addons/frag/functions/fnc_dev_drawTrace.sqf @@ -0,0 +1,45 @@ +#include "..\script_component.hpp" +/* + * Author: Lambda.Tiger + * This function draws all development traces and is intended to be called on each frame. + * + * Arguments: + * None + * + * Return Value: + * None + * + * Example: + * call ace_frag_fnc_dev_drawTrace + * + * Public: No + */ + +#define HITBOX_DRAW_PATH [[3, 2, 1, 5, 6, 7, 3, 0, 4, 5], [0, 1], [2, 6], [7, 4]] + +if (!GVAR(debugOptions)) exitWith {}; + +{ + _y params ["_posArray", "_color"]; + if (count _posArray > 1) then { + for "_j" from 1 to count _posArray - 1 do { + drawLine3D [_posArray#(_j - 1), _posArray#_j, _color]; + }; + }; +} forEach GVAR(dev_trackLines); + +if (GVAR(drawHitBox)) then { + { + _y params ["_object", "_boxPoints", "_color"]; + if (!alive _object) then { + GVAR(dev_hitBoxes) deleteAt _x; + continue; + }; + + { + for "_i" from 1 to count _x - 1 do { + drawLine3D [_object modelToWorld (_boxPoints#(_x#_i)), _object modelToWorld (_boxPoints#(_x#(_i - 1))), _color]; + }; + } forEach HITBOX_DRAW_PATH; + } forEach GVAR(dev_hitBoxes); +}; diff --git a/addons/frag/functions/fnc_dev_drawTraces.sqf b/addons/frag/functions/fnc_dev_drawTraces.sqf deleted file mode 100644 index 7fcca3c48fe..00000000000 --- a/addons/frag/functions/fnc_dev_drawTraces.sqf +++ /dev/null @@ -1,37 +0,0 @@ -#include "..\script_component.hpp" -/* - * Author: ACE-Team - * - * - * Arguments: - * None - * - * Return Value: - * None - * - * Example: - * call ace_frag_fnc_dev_drawTraces - * - * Public: No - */ - -{ - _x params ["", "", "", "", "_positions", "_color"]; - private _index = 0; - private _max = count _positions; - // private _lastSpd = []; - private _lastPos = []; - while {_index < _max} do { - _data1 = _positions select _index; - _data2 = _positions select ([_index + ACE_TRACE_DRAW_INC, _max - 1] select (_index + ACE_TRACE_DRAW_INC >= _max)); - - _pos1 = _data1 select 0; - _pos2 = _data2 select 0; - ADD(_index,ACE_TRACE_DRAW_INC); - - drawLine3D [_pos1, _pos2, _color]; - _lastPos = _pos2; - // _lastSpd = _data1 select 1; - }; - // drawIcon3D ["", [1,0,0,1], _lastPos, 0, 0, 0, format ["%1m/s", _lastSpd], 1, 0.05, "RobotoCondensed"]; -} forEach GVAR(traces); diff --git a/addons/frag/functions/fnc_dev_fragCalcDump.sqf b/addons/frag/functions/fnc_dev_fragCalcDump.sqf new file mode 100644 index 00000000000..88be643c258 --- /dev/null +++ b/addons/frag/functions/fnc_dev_fragCalcDump.sqf @@ -0,0 +1,56 @@ +#include "..\script_component.hpp" +/* + * Author: Lambda.Tiger, based on fnc_dev_debugAmmo by "ACE-Team" + * Dumps all ammo types to see if there's any reason to spawn fragments given hit power and distance. + * Good for grasping the values used in shouldFrag to cull non-fragmenting rounds. + * + * Arguments: + * 0: Display rounds that will never frag (default: false) + * + * Return Value: + * None + * + * Example: + * false call ace_frag_fnc_dev_fragCalcDump + * + * Public: No + */ + +params [["_logAll", false, [false]]]; + +private _allAmmoConfigs = configProperties [configFile >> "CfgAmmo", "isClass _x && !('ace_frag' in configName _x)", true]; +private _processedCfgAmmos = createHashMap; + +private _numberPrinted = 0; + +diag_log text "//****************** fragCalcDump Beg ******************//"; +{ // Begin _allAmmoConfigs forEach + private _ammo = configName _x; + + if (_ammo isEqualTo "" || {_ammo in _processedCfgAmmos}) then { + continue; + }; + + _ammo call FUNC(shouldFrag) params ["_shouldFrag"]; + if (_shouldFrag || _logAll) then { + private _fragInfo = _ammo call FUNC(getFragInfo); + _fragInfo params ["_fragRange", "_fragMaxSpeed"]; + private _ammoConfig = configFile >> "CfgAmmo" >> _ammo; + private _indirectHitRange = getNumber (_ammoConfig >> "indirectHitRange"); + private _indirectHit = getNumber (_ammoConfig >> "indirectHit"); + private _fragPowerSpeedRange = [0.5, 1] vectorMultiply _fragMaxSpeed; + + diag_log text format ["Ammo type: %1 | Should frag: %2", _ammo, _shouldFrag]; + diag_log text format [" Indirect hit range: %1", _indirectHitRange]; + diag_log text format [" Indirect hit: %1", _indirectHit]; + diag_log text format [" Max frag speed: %1", _fragMaxSpeed]; + diag_log text format [" Frag range: %1", _fragRange]; + diag_log text format [" Frag speed range: %1", _fragPowerSpeedRange]; + INC(_numberPrinted); + }; + + _processedCfgAmmos set [_ammo, 1]; +} forEach _allAmmoConfigs; + +diag_log text "//****************** fragCalcDump End ******************//"; +diag_log text format ["//********************** printed %1 *********************//", _numberPrinted]; diff --git a/addons/frag/functions/fnc_dev_sphereDraw.sqf b/addons/frag/functions/fnc_dev_sphereDraw.sqf new file mode 100644 index 00000000000..31a1393991e --- /dev/null +++ b/addons/frag/functions/fnc_dev_sphereDraw.sqf @@ -0,0 +1,41 @@ +#include "..\script_component.hpp" +/* + * Author: Lambda.Tiger + * Add a colored sphere at a specified point. + * + * Arguments: + * 0: Position (posASL) to add sphere + * 1: Color of sphere (default: "blue") + * + * Return Value: + * The created sphere object + * + * Example: + * [getPosASL player, "red"] call ace_frag_fnc_dev_sphereDraw + * + * Public: No + */ + +params ["_posASL", ["_color", "blue"]]; + +if (!isServer) exitWith {}; + +if (_color select [0,1] != "(") then { + _color = switch (toLowerANSI _color) do { + case "blue": {"(0,0,0.8,0.5)"}; + case "black": {"(1,1,1,0.5)"}; + case "white": {"(0,0,0,0.5)"}; + case "red": {"(0.8,0,0,0.5)"}; + case "green": {"(0,0.8,0,0.5)"}; + case "yellow": {"(0.8,0.8,0,0.5)"}; + case "orange": {"(0.8,0.518,0,0.5)"}; + default {"(0.8,0.8,0,0.5)"}; + }; +}; +private _colorString = "#(argb,8,8,3)color" + _color; + +private _sphere = createVehicle ["Sign_Sphere10cm_F", ASLToATL _posASL, [], 0, "CAN_COLLIDE"]; +_sphere setObjectTextureGlobal [0, _colorString]; +GVAR(dev_eventSpheres) pushBack _sphere; + +_sphere // return diff --git a/addons/frag/functions/fnc_dev_startTracing.sqf b/addons/frag/functions/fnc_dev_startTracing.sqf deleted file mode 100644 index 897dde3011e..00000000000 --- a/addons/frag/functions/fnc_dev_startTracing.sqf +++ /dev/null @@ -1,23 +0,0 @@ -#include "..\script_component.hpp" -/* - * Author: ACE-Team - * - * - * Arguments: - * None - * - * Return Value: - * None - * - * Example: - * call ace_frag_fnc_dev_startTracing - * - * Public: No - */ - -if (GVAR(tracesStarted)) exitWith {}; - -INFO("Starting Trace Drawing"); - -GVAR(tracesStarted) = true; -GVAR(traceID) = [LINKFUNC(dev_drawTraces), 0, []] call CBA_fnc_addPerFrameHandler; diff --git a/addons/frag/functions/fnc_dev_stopTracing.sqf b/addons/frag/functions/fnc_dev_stopTracing.sqf deleted file mode 100644 index 949d3cd55d6..00000000000 --- a/addons/frag/functions/fnc_dev_stopTracing.sqf +++ /dev/null @@ -1,23 +0,0 @@ -#include "..\script_component.hpp" -/* - * Author: ACE-Team - * Dev things - * - * Arguments: - * None - * - * Return Value: - * None - * - * Example: - * None - * - * Public: No - */ - -if (!GVAR(tracesStarted)) exitWith {}; - -INFO("Ending Trace Drawing"); - -GVAR(tracesStarted) = false; -[GVAR(traceID)] call CBA_fnc_removePerFrameHandler; diff --git a/addons/frag/functions/fnc_dev_trackHitBox.sqf b/addons/frag/functions/fnc_dev_trackHitBox.sqf new file mode 100644 index 00000000000..327e45cf0d3 --- /dev/null +++ b/addons/frag/functions/fnc_dev_trackHitBox.sqf @@ -0,0 +1,75 @@ +#include "..\script_component.hpp" +/* + * Author: Lambda.Tiger + * Add a hit box outline to an object. + * + * Arguments: + * 0: Object that should have it's hit box drawn (default: objNull) + * 1: Add sphere at object origin (default: true) + * + * Return Value: + * None + * + * Example: + * player call ace_frag_fnc_dev_trackHitBox + * + * Public: No + */ + +params [ + ["_object", objNull], + ["_addSphere", true] +]; +TRACE_2("Adding hitbox",_object,_addSphere); + +if (isNull _object) exitWith {}; + +// Grab the right hitBox +private _boundingBox = []; +if (_object isKindOf "CAManBase") then { + if (isNull objectParent _object) then { + _boundingBox = 0 boundingBox _object; + } else { + _boundingBox = boundingBoxReal [_object, "Geometry"]; + }; +} else { + _boundingBox = boundingBoxReal [_object, "FireGeometry"]; +}; +_boundingBox params ["_lowerPoint", "_upperPoint"]; + +// adjust with stance +switch (stance _object) do { + case "STAND": {_upperPoint set [2, 1.9];}; + case "CROUCH": {_upperPoint set [2, 1.3];}; + case "PRONE": {_upperPoint set [2, 0.8];}; +}; +private _centerPoint = ASLToAGL getPosASL _object; + +if (GVAR(dbgSphere) && _addSphere && {isNull objectParent _object}) then { + private _centerSphere = [getPosASL _object, "yellow"] call FUNC(dev_sphereDraw); + _centerSphere attachTo [_object, _object worldToModel _centerPoint]; +}; + +// create an optimized outline +_upperPoint params ["_x1","_y1","_z1"]; +_lowerPoint params ["_x2","_y2","_z2"]; +private _p1 = _upperPoint; +private _p7 = _lowerPoint; +private _points = [ + _upperPoint, + [_x1, _y2, _z1], + [_x2, _y2, _z1], + [_x2, _y1, _z1], + [_x1, _y1, _z2], + [_x1, _y2, _z2], + _lowerPoint, + [_x2, _y1, _z2] +]; + +private _color = switch (side _object) do { + case east: {[0.8, 0, 0, 1]}; + case resistance: {[0, 0.8, 0, 1]}; + default {[0, 0, 0.8, 1]}; +}; + +GVAR(dev_hitBoxes) set [getObjectID _object, [_object, _points, _color]]; diff --git a/addons/frag/functions/fnc_dev_trackObj.sqf b/addons/frag/functions/fnc_dev_trackObj.sqf new file mode 100644 index 00000000000..88b298fa0b8 --- /dev/null +++ b/addons/frag/functions/fnc_dev_trackObj.sqf @@ -0,0 +1,87 @@ +#include "..\script_component.hpp" +/* + * Author: Lambda.Tiger + * This function adds an object to have its course tracked (every frame). + * + * Arguments: + * 0: Object to draw track (default: objNull) + * 1: Color of trace (default: "blue") + * 2: Whether the object is a projectile or whether to add projectile EHs (default: false) + * + * Return Value: + * None + * + * Example: + * player call ace_frag_fnc_dev_trackObj + * + * Public: No + */ + +params [ + ["_object", objNull], + ["_color", "blue"], + ["_isProj", false] +]; +TRACE_3("devDraw",_object,_color,_isProj); + +// pick color and add it to the array +private _colorArray = switch (toLowerANSI _color) do { + case "purple": {[0.8, 0, 0.8, 1]}; + case "blue": {[0, 0, 0.8, 1]}; + case "green": {[0, 0.8, 0, 1]}; + case "orange": {[0.8, 0.518, 0, 1]}; + case "yellow": {[0.8, 0.8, 0, 1]}; + case "red": {[0.8, 0, 0, 1]}; + case "black": {[1, 1, 1, 1]}; + case "white": {[0, 0, 0, 1]}; + default {[0, 0.8, 0.8, 1]}; +}; +GVAR(dev_trackLines) set [getObjectID _object, [[getPosATL _object], _colorArray]]; + +// event handler to track round and cleanup when round is "dead" +[{ + if (isGamePaused || accTime == 0) exitWith {}; + params ["_object", "_handle"]; + + if (!alive _object) exitWith { + _handle call CBA_fnc_removePerFrameHandler; + }; + + private _objectArray = GVAR(dev_trackLines) get (getObjectID _object); + + if (isNil "_objectArray") exitWith { + _handle call CBA_fnc_removePerFrameHandler; + }; + + (_objectArray#0) pushBack getPosATL _object; +}, 0, _object] call CBA_fnc_addPerFrameHandler; + +// Projectile event handlers that add spheres and points for more accurate round tracking +if (!_isProj) exitWith {}; + +_object addEventHandler ["HitPart", { + params ["_projectile", "", "", "_posASL"]; + private _posArr = (GVAR(dev_trackLines) get (getObjectID _projectile))#0; + _posArr pushBack ASLToATL _posASL; + if (GVAR(dbgSphere)) then { + [_posASL, "green"] call FUNC(dev_sphereDraw); + }; +}]; + +_object addEventHandler ["Explode", { + params ["_projectile", "_posASL"]; + private _posArr = (GVAR(dev_trackLines) get (getObjectID _projectile))#0; + _posArr pushBack ASLToATL _posASL; + if (GVAR(dbgSphere)) then { + [_posASL, "red"] call FUNC(dev_sphereDraw); + }; +}]; + +_object addEventHandler ["Deflected", { + params ["_projectile", "_posASL"]; + private _posArr = (GVAR(dev_trackLines) get (getObjectID _projectile))#0; + _posArr pushBack ASLToATL _posASL; + if (GVAR(dbgSphere)) then { + [_posASL, "blue"] call FUNC(dev_sphereDraw); + }; +}]; diff --git a/addons/frag/functions/fnc_dev_trackTrace.sqf b/addons/frag/functions/fnc_dev_trackTrace.sqf deleted file mode 100644 index 6c010bdb631..00000000000 --- a/addons/frag/functions/fnc_dev_trackTrace.sqf +++ /dev/null @@ -1,27 +0,0 @@ -#include "..\script_component.hpp" -/* - * Author: ACE-Team - * Dev things - * - * Arguments: - * None - * - * Return Value: - * None - * - * Example: - * call ace_frag_fnc_dev_trackTrace - * - * Public: No - */ - -params ["_args", "_pfhID"]; -_args params ["_tracerObj", "_index"]; - -if (alive _tracerObj && {GVAR(traces) isNotEqualTo []}) then { - private _data = GVAR(traces) select _index; - private _positions = _data select 4; - _positions pushBack [getPos _tracerObj, vectorMagnitude (velocity _tracerObj)]; -} else { - [_pfhID] call CBA_fnc_removePerFrameHandler; -}; diff --git a/addons/frag/functions/fnc_doSpall.sqf b/addons/frag/functions/fnc_doSpall.sqf index b206c701b21..0c5e7e39a1f 100644 --- a/addons/frag/functions/fnc_doSpall.sqf +++ b/addons/frag/functions/fnc_doSpall.sqf @@ -1,139 +1,121 @@ #include "..\script_component.hpp" /* - * Author: ACE-Team - * Dev things + * Author: Jaynus, NouberNou, Lambda.Tiger, + * This function check whether a spall event has occured and generates spall. * * Arguments: - * None + * 0: The object a projectile hit + * 1: The config name of the projectile + * 2: The projectile that should cause spalling + * 3: The position (ASL) the projectile hit the object + * 4: The old velocity of the projectile + * 5: The projectile's instigator, or the second argument of a projectile's shotParents * * Return Value: * None * * Example: - * call ace_frag_fnc_doSpall + * [editorPlacedHouse_0, typeOf _projectile, _projectile, [1000, 40, 60], [0, 1000, 0], [objNull, ace_player]] call ace_frag_fnc_doSpall * * Public: No */ - #define WEIGHTED_SIZE [QGVAR(spall_small), 4, QGVAR(spall_medium), 3, QGVAR(spall_large), 2, QGVAR(spall_huge), 1] +params ["_objectHit", "_roundType", "_round", "_oldPosASL", "_oldVelocity", "_instigator"]; -params ["_hitData", "_hitPartDataIndex"]; -private _initialData = GVAR(spallHPData) select (_hitData select 0); -_initialData params ["_hpId", "_object", "_roundType", "_round", "_curPos", "_velocity"]; - -private _hpData = (_hitData select 1) select _hitPartDataIndex; -private _objectHit = _hpData param [0, objNull]; -TRACE_1("",_objectHit); -if ((isNil "_objectHit") || {isNull _objectHit}) exitWith {WARNING_1("Problem with hitPart data - bad object [%1]",_objectHit);}; -_objectHit removeEventHandler ["HitPart", _hpId]; +TRACE_6("",_objectHit,_roundType,_round,_oldPosASL,_oldVelocity,_instigator); +if ((isNil "_objectHit") || {isNull _objectHit}) exitWith { + TRACE_1("Problem with hitPart data - bad object [%1]",_objectHit); +}; -private _caliber = getNumber (configFile >> "CfgAmmo" >> _roundType >> "caliber"); -private _explosive = getNumber (configFile >> "CfgAmmo" >> _roundType >> "explosive"); -private _idh = getNumber (configFile >> "CfgAmmo" >> _roundType >> "indirectHitRange"); +_roundType call FUNC(getSpallInfo) params ["_caliber", "_explosive", "_idh"]; -if !(_caliber >= 2.5 || {(_explosive > 0 && {_idh >= 1})}) exitWith {}; -// ACE_player sideChat format ["BBBB"]; private _exit = false; -private _vm = 1; +private _velocityModifier = 1; -private _oldVelocity = vectorMagnitude _velocity; -private _curVelocity = vectorMagnitude (velocity _round); +private _curVelocity = velocity _round; +private _oldSpeed = vectorMagnitude _oldVelocity; +private _curSpeed = vectorMagnitude _curVelocity; if (alive _round) then { - private _diff = _velocity vectorDiff (velocity _round); + private _diff = _oldVelocity vectorDiff _curVelocity; private _polar = _diff call CBA_fnc_vect2polar; - // ACE_player sideChat format ["polar: %1", _polar]; + if (abs (_polar select 1) > 45 || {abs (_polar select 2) > 45}) then { if (_caliber < 2.5) then { - // ACE_player sideChat format ["exit!"]; _exit = true; } else { - SUB(_vm,_curVelocity / _oldVelocity); + SUB(_velocityModifier,_curSpeed / _oldSpeed); }; }; }; -if (_exit) exitWith {}; - -private _unitDir = vectorNormalized _velocity; -private _pos = _hpData select 3; -private _spallPos = []; -if ((isNil "_pos") || {!(_pos isEqualTypeArray [0,0,0])}) exitWith {WARNING_1("Problem with hitPart data - bad pos [%1]",_pos);}; -for "_i" from 0 to 100 do { - private _pos1 = _pos vectorAdd (_unitDir vectorMultiply (0.01 * _i)); - private _pos2 = _pos vectorAdd (_unitDir vectorMultiply (0.01 * (_i + 1))); - // _data = [nil, nil, nil, 1, [[ASLtoATL _pos1, 1], [ASLtoATL _pos2, 1]]]; - // NOU_TRACES pushBack _data; - - if (!lineIntersects [_pos1, _pos2]) exitWith { - // ACE_player sideChat format ["FOUND!"]; - _spallPos = _pos2; - }; +if (_exit) exitWith { + TRACE_1("exit alive",_caliber); }; -if (_spallPos isEqualTo []) exitWith {}; -private _spallPolar = _velocity call CBA_fnc_vect2polar; + +private _unitDir = vectorNormalized _oldVelocity; + +if ((isNil "_oldPosASL") || {!(_oldPosASL isEqualTypeArray [0,0,0])}) exitWith {WARNING_1("Problem with hitPart data - bad pos [%1]",_oldPosASL);}; +private _pos1 = _oldPosASL; +private _spallPosAGL = _pos1; +private _searchStepSize = _unitDir vectorMultiply 0.05; +for "_i" from 0 to 20 do { + _spallPosAGL = _pos1 vectorAdd _searchStepSize; + if (!lineIntersects [_pos1, _spallPosAGL]) exitWith {}; + _pos1 = _spallPosAGL; +}; +if (_spallPosAGL isEqualTo _pos1) exitWith { + TRACE_1("can't find other side",_oldPosASL); +}; +_spallPosAGL = ASLToAGL _spallPosAGL; + +_instigator setVariable [QGVAR(nextSpallEvent), CBA_missionTime + ACE_FRAG_SPALL_UNIT_HOLDOFF]; +private _oldVelocitySpherical = _oldVelocity call CBA_fnc_vect2polar; if (_explosive > 0) then { - // ACE_player sideChat format ["EXPLOSIVE!"]; - private _warn = false; - private _c = getNumber (configFile >> "CfgAmmo" >> _roundType >> QGVAR(CHARGE)); - if (_c == 0) then {_c = 1; _warn = true;}; - private _m = getNumber (configFile >> "CfgAmmo" >> _roundType >> QGVAR(METAL)); - if (_m == 0) then {_m = 2; _warn = true;}; - private _k = getNumber (configFile >> "CfgAmmo" >> _roundType >> QGVAR(GURNEY_K)); - if (_k == 0) then {_k = 1 / 2; _warn = true;}; - private _gC = getNumber (configFile >> "CfgAmmo" >> _roundType >> QGVAR(GURNEY_C)); - if (_gC == 0) then {_gC = 2440; _warn = true;}; - - // if (_warn) then { - // WARNING_1("Ammo class %1 lacks proper explosive properties definitions for frag!",_roundType); //TODO: turn this off when we get closer to release - // }; - - private _fragPower = (((_m / _c) + _k) ^ - (1 / 2)) * _gC; - _spallPolar set [0, _fragPower * 0.66]; + _roundType call FUNC(getFragInfo) params ["", "_fragVelocity"]; + _oldVelocitySpherical set [0, _fragVelocity * 0.66]; }; +TRACE_2("spallPosandVel",_spallPosAGL,_oldVelocitySpherical); -// diag_log text format ["SPALL POWER: %1", _spallPolar select 0]; private _spread = 15 + (random 25); private _spallCount = 5 + (random 10); TRACE_1("",_spallCount); for "_i" from 1 to _spallCount do { - private _elev = ((_spallPolar select 2) - _spread) + (random (_spread * 2)); - private _dir = ((_spallPolar select 1) - _spread) + (random (_spread * 2)); - if (abs _elev > 90) then { - ADD(_dir,180); + private _fragmentElevation = ((_oldVelocitySpherical select 2) - _spread) + (random (_spread * 2)); + private _fragmentAzimuth = ((_oldVelocitySpherical select 1) - _spread) + (random (_spread * 2)); + if (abs _fragmentElevation > 90) then { + ADD(_fragmentAzimuth,180); }; - _dir = _dir % 360; - private _vel = (_spallPolar select 0) * 0.33 * _vm; - _vel = (_vel - (_vel * 0.25)) + (random (_vel * 0.5)); + _fragmentAzimuth = _fragmentAzimuth % 360; + private _fragmentSpeed = (_oldVelocitySpherical select 0) * 0.33 * _velocityModifier; + _fragmentSpeed = _fragmentSpeed * (0.75 + random 0.5); - private _spallFragVect = [_vel, _dir, _elev] call CBA_fnc_polar2vect; - private _fragment = (selectRandomWeighted WEIGHTED_SIZE) createVehicleLocal [0,0,10000]; - _fragment setPosASL _spallPos; + private _spallFragVect = [_fragmentSpeed, _fragmentAzimuth, _fragmentElevation] call CBA_fnc_polar2vect; + private _fragment = createVehicleLocal [selectRandomWeighted WEIGHTED_SIZE, _spallPosAGL, [], 0, "CAN_COLLIDE"]; _fragment setVelocity _spallFragVect; - #ifdef DRAW_FRAG_INFO - [ACE_player, _fragment, [1, 0.5, 0, 1]] call FUNC(dev_addTrack); + #ifdef DEBUG_MODE_DRAW + [_fragment, "orange", true] call FUNC(dev_trackObj); #endif }; _spread = 5 + (random 5); _spallCount = 3 + (random 5); for "_i" from 1 to _spallCount do { - private _elev = ((_spallPolar select 2) - _spread) + (random (_spread * 2)); - private _dir = ((_spallPolar select 1) - _spread) + (random (_spread * 2)); - if (abs _elev > 90) then { - ADD(_dir,180); + private _fragmentElevation = ((_oldVelocitySpherical select 2) - _spread) + (random (_spread * 2)); + private _fragmentAzimuth = ((_oldVelocitySpherical select 1) - _spread) + (random (_spread * 2)); + if (abs _fragmentElevation > 90) then { + ADD(_fragmentAzimuth,180); }; - _dir = _dir % 360; - private _vel = (_spallPolar select 0) * 0.55 * _vm; - _vel = (_vel - (_vel * 0.25)) + (random (_vel * 0.5)); + _fragmentAzimuth = _fragmentAzimuth % 360; + private _fragmentSpeed = (_oldVelocitySpherical select 0) * 0.55 * _velocityModifier; + _fragmentSpeed = _fragmentSpeed * (0.75 + random 0.5); - private _spallFragVect = [_vel, _dir, _elev] call CBA_fnc_polar2vect; - private _fragment = (selectRandomWeighted WEIGHTED_SIZE) createVehicleLocal [0, 0, 10000]; - _fragment setPosASL _spallPos; + private _spallFragVect = [_fragmentSpeed, _fragmentAzimuth, _fragmentElevation] call CBA_fnc_polar2vect; + private _fragment = createVehicleLocal [selectRandomWeighted WEIGHTED_SIZE, _spallPosAGL, [], 0, "CAN_COLLIDE"]; _fragment setVelocity _spallFragVect; - #ifdef DRAW_FRAG_INFO - [ACE_player, _fragment, [1, 0, 0, 1]] call FUNC(dev_addTrack); + #ifdef DEBUG_MODE_DRAW + [_fragment, "purple", true] call FUNC(dev_trackObj); #endif }; diff --git a/addons/frag/functions/fnc_fired.sqf b/addons/frag/functions/fnc_fired.sqf index 03d2fab6096..4f2a97740ab 100644 --- a/addons/frag/functions/fnc_fired.sqf +++ b/addons/frag/functions/fnc_fired.sqf @@ -1,14 +1,14 @@ #include "..\script_component.hpp" /* - * Author: nou, jaynus, PabstMirror - * Called from the unified fired EH for all. - * If spall is not enabled (default), then cache and only track those that will actually trigger fragmentation. + * Author: nou, jaynus, Lambda.Tiger, PabstMirror + * Add "Exploded" eventhandler to a projectile if it will produce fragments + * and a "HitPart" eventhandler if it will produce spall. * * Arguments: - * None. Parameters inherited from EFUNC(common,firedEH) + * Parameters inherited from EFUNC(common,firedEH) * * Return Value: - * None + * Nothing Useful * * Example: * [clientFiredBIS-XEH] call ace_frag_fnc_fired @@ -19,40 +19,41 @@ //IGNORE_PRIVATE_WARNING ["_unit", "_weapon", "_muzzle", "_mode", "_ammo", "_magazine", "_projectile", "_vehicle", "_gunner", "_turret"]; TRACE_10("firedEH:",_unit,_weapon,_muzzle,_mode,_ammo,_magazine,_projectile,_vehicle,_gunner,_turret); -private _shouldAdd = GVAR(cacheRoundsTypesToTrack) get _ammo; -if (isNil "_shouldAdd") then { - TRACE_1("no cache for round",_ammo); +if (_ammo isEqualTo "" || {isNull _projectile} || + !(if (isNil "_gunner") then {local _unit} else {local _gunner}) || + {_projectile getVariable [QGVAR(blacklisted), false]}) exitWith { + TRACE_2("bad ammo or projectile, or blackList",_ammo,_projectile); +}; - //Read configs and test if it would actually cause a frag, using same logic as FUNC(pfhRound) - private _skip = getNumber (configFile >> "CfgAmmo" >> _ammo >> QGVAR(skip)); - private _explosive = getNumber (configFile >> "CfgAmmo" >> _ammo >> "explosive"); - private _indirectRange = getNumber (configFile >> "CfgAmmo" >> _ammo >> "indirectHitRange"); - private _force = getNumber (configFile >> "CfgAmmo" >> _ammo >> QGVAR(force)); - private _fragPower = getNumber (configFile >> "CfgAmmo" >> _ammo >> "indirecthit") * (sqrt (getNumber (configFile >> "CfgAmmo" >> _ammo >> "indirectHitRange"))); +#ifdef DEBUG_MODE_DRAW +if (GVAR(debugOptions) && {_ammo call FUNC(shouldFrag) || {_ammo call FUNC(shouldSpall)}}) then { + [_projectile, "red", true] call FUNC(dev_trackObj); +}; +#endif - _shouldAdd = (_skip == 0) && {(_force == 1) || {_explosive > 0.5 && {_indirectRange >= 4.5} && {_fragPower >= 35}}}; +if (!GVAR(spallEnabled) || {!(_ammo call FUNC(shouldSpall))}) exitWith { + TRACE_2("No spall",GVAR(spallEnabled),_ammo call FUNC(shouldSpall)); +}; - if (GVAR(spallEnabled) && {!_shouldAdd}) then { - private _caliber = getNumber (configFile >> "CfgAmmo" >> _ammo >> "caliber"); - if !(_caliber >= 2.5 || {(_explosive > 0 && {_indirectRange >= 1})}) exitWith {}; // from check in doSpall: line 34 - TRACE_1("Won't frag, but will spall",_caliber); - _shouldAdd = true; - }; +private _hitPartEventHandler = _projectile addEventHandler ["HitPart", { + params ["_projectile", "_hitObject", "", "_posASL", "_velocity"]; - TRACE_6("Setting Cache",_skip,_explosive,_indirectRange,_force,_fragPower,_shouldAdd); - GVAR(cacheRoundsTypesToTrack) set [_ammo, _shouldAdd]; -}; + // get rid of _shot parents starting after v2.18 is released and instead use the instigator EH parameter + // The "explode" EH does not get the same parameter + private _instigator = (getShotParents _projectile)#1; + private _ammo = typeOf _projectile; -if (_shouldAdd) then { - // firedMan will have nil "_gunner", so just check _unit; for firedVehicle we want to check _gunner - private _localShooter = if (isNil "_gunner") then {local _unit} else {local _gunner}; - TRACE_4("",_localShooter,_unit,_ammo,_projectile); - if (!_localShooter) exitWith {}; - if (_weapon == "Put") exitWith {}; // Ignore explosives placed without ace_explosives + /* + * Wait a frame to see what happens to the round, may result in + * multiple hits / slowdowns getting shunted to the first hit + */ + [{ + // only let a unit make a spall once per ACE_FRAG_SPALL_UNIT_HOLDOFF + if (CBA_missionTime < (_this#5) getVariable [QGVAR(nextSpallEvent), -1]) exitWith {}; - // Skip if less than 0.5 second from last shot - if ((CBA_missionTime - (_unit getVariable [QGVAR(lastTrack), -1])) < 0.5) exitWith {}; - _unit setVariable [QGVAR(lastTrack), CBA_missionTime]; + _this call FUNC(doSpall); + }, [_hitObject, _ammo, _projectile, _posASL, _velocity, _instigator]] call CBA_fnc_execNextFrame; +}]; +_projectile setVariable [QGVAR(hitPartEventHandler), _hitPartEventHandler]; - [_unit, _ammo, _projectile] call FUNC(addPfhRound); -}; +TRACE_1("firedExit",_ammo); diff --git a/addons/frag/functions/fnc_frago.sqf b/addons/frag/functions/fnc_frago.sqf index 460b2e2b3a9..0284d3249ae 100644 --- a/addons/frag/functions/fnc_frago.sqf +++ b/addons/frag/functions/fnc_frago.sqf @@ -1,15 +1,15 @@ #include "..\script_component.hpp" /* - * Author: Jaynus, NouberNou + * Author: Jaynus, NouberNou, Lambda.Tiger * Server func to create the fragmentation for a round. * * Arguments: - * 0: Last Position (ASL) - * 1: Velocity - * 2: Ammo Classname + * 0: ASL position projetile is fragmenting at + * 1: Projectile ammo classname + * 2: Projectile shot parents * * Return Value: - * None + * The number of fragments created * * Example: * [[], [], "handGrenade"] call ace_frag_fnc_frago @@ -17,139 +17,109 @@ * Public: No */ -#define FRAG_VEC_VAR 0.004 -#define MAX_FRAG_COUNT 50 +#define FRAG_VEC_VAR 0.008 BEGIN_COUNTER(frago); -params ["_lastPos", "_lastVel", "_shellType"]; -TRACE_3("frago",_lastPos,_lastVel,_shellType); +params ["_fragPosASL", "_shellType", "_shotParents"]; +TRACE_3("frago",_fragPosASL,_shellType,_shotParents); // Limit max frag count if there was a recent frag -private _maxFrags = round (MAX_FRAG_COUNT * linearConversion [0.1, 1.5, (CBA_missionTime - GVAR(lastFragTime)), 0.1, 1, true]); +private _maxFrags = round linearConversion [ + ACE_FRAG_COUNT_MIN_TIME, + ACE_FRAG_COUNT_MAX_TIME, + (CBA_missionTime - GVAR(lastFragTime)), + ACE_FRAG_COUNT_MIN, + ACE_FRAG_COUNT_MAX, + true +]; TRACE_2("",_maxFrags,CBA_missionTime - GVAR(lastFragTime)); GVAR(lastFragTime) = CBA_missionTime; -private _fragTypes = [ - QGVAR(tiny), QGVAR(tiny), QGVAR(tiny), - QGVAR(tiny_HD), QGVAR(tiny_HD), QGVAR(tiny_HD), - QGVAR(small), QGVAR(small), QGVAR(small), QGVAR(small), - QGVAR(small_HD), QGVAR(small_HD), QGVAR(small_HD), QGVAR(small_HD), - QGVAR(medium_HD), QGVAR(medium_HD), QGVAR(medium_HD), QGVAR(medium_HD), QGVAR(medium_HD) -]; - -private _warn = false; -if (isArray (configFile >> "CfgAmmo" >> _shellType >> QGVAR(CLASSES))) then { - _fragTypes = getArray (configFile >> "CfgAmmo" >> _shellType >> QGVAR(CLASSES)); -} else { - _warn = true; -}; - -private _indirectHitRange = getNumber(configFile >> "CfgAmmo" >> _shellType >> "indirecthitrange"); -private _fragRange = 20 * _indirectHitRange * 4; -// _c = 185; // grams of comp-b -// _m = 210; // grams of fragmentating metal -// _k = 3/5; // spherical K factor -// _gC = 2843; // Gurney constant of comp-b in /ms - -// _c = 429; // grams of tritonal -// _m = 496; // grams of fragmentating metal -// _k = 1/2; // spherical K factor -// _gC = 2320; // Gurney constant of tritonal in /ms - -private _c = getNumber (configFile >> "CfgAmmo" >> _shellType >> QGVAR(CHARGE)); -if (_c == 0) then {_c = 1; _warn = true;}; -private _m = getNumber (configFile >> "CfgAmmo" >> _shellType >> QGVAR(METAL)); -if (_m == 0) then {_m = 2; _warn = true;}; -private _k = getNumber (configFile >> "CfgAmmo" >> _shellType >> QGVAR(GURNEY_K)); -if (_k == 0) then {_k = 0.5; _warn = true;}; -private _gC = getNumber (configFile >> "CfgAmmo" >> _shellType >> QGVAR(GURNEY_C)); -if (_gC == 0) then {_gC = 2440; _warn = true;}; - -if (_warn) then { - INFO_1("Ammo class %1 lacks proper explosive properties definitions for frag!",_shellType); -}; - -// Gunery equation is for a non-fragmenting metal, imperical value of 80% represents fragmentation -private _fragPower = 0.8 * (((_m / _c) + _k) ^ - (1 / 2)) * _gC; - -private _atlPos = ASLToATL _lastPos; +_shellType call FUNC(getFragInfo) params ["_fragRange", "_fragVelocity", "_fragTypes", "_metalMassModifier"]; -private _fragPowerRandom = _fragPower * 0.5; -if ((_atlPos select 2) < 0.5) then { - _lastPos vectorAdd [0, 0, 0.5]; +private _fragPosAGL = ASLToAGL _fragPosASL; +TRACE_5("fragValues",_fragPosASL,_fragPosAGL,_fragRange,_fragVelocity,_metalMassModifier); +// Post 2.18 change - uncomment line 43, modify lines 45, and remove lines 44, 51-57, 64-66 +// private _targets = [ASLToAGL _fragPosAGL, _fragRange, _fragRange, 0, false, _fragRange] nearEntities [["Car", "Motorcycle", "Tank", "StaticWeapon", "CAManBase", "Air", "Ship"], false, true, true]; +private _objects = _fragPosAGL nearEntities [["Car", "Motorcycle", "Tank", "StaticWeapon", "CAManBase", "Air", "Ship"], _fragRange]; +if (_objects isEqualTo []) exitWith { + TRACE_2("No nearby targets",_fragPosAGL,_fragRange); + 0 }; -private _objects = _atlPos nearEntities [["Car", "Motorcycle", "Tank", "StaticWeapon", "CAManBase", "Air", "Ship"], _fragRange]; -// Add unique crews in faster way +// grab crews and add them in so that targets stay approx. sorted by distance +TRACE_1("",_objects); +private _targets = []; { - { - _objects pushBackUnique _x; - } forEach (crew _x); + private _crew = crew _x; + _crew pushBackUnique _x; + _targets append _crew; } forEach _objects; -TRACE_2("",_fragRange,count _objects); +TRACE_2("",_fragRange,count _targets); private _fragCount = 0; private _fragArcs = []; _fragArcs set [360, 0]; -private _doRandom = true; -if (_objects isNotEqualTo []) then { +if (_targets isNotEqualTo []) then { if (GVAR(reflectionsEnabled)) then { - [_lastPos, _shellType] call FUNC(doReflections); + [_fragPosASL, _shellType] call FUNC(doReflections); }; { private _target = _x; - if (alive _target) then { + if (alive _target && {getNumber ((configOf _target) >> "isPlayableLogic") == 0}) then { (boundingBox _target) params ["_boundingBoxA", "_boundingBoxB"]; private _cubic = ((abs (_boundingBoxA select 0)) + (_boundingBoxB select 0)) * ((abs (_boundingBoxA select 1)) + (_boundingBoxB select 1)) * ((abs (_boundingBoxA select 2)) + (_boundingBoxB select 2)); if (_cubic <= 1) exitWith {}; - // _doRandom = true; private _targetVel = velocity _target; private _targetPos = getPosASL _target; - private _distance = _targetPos vectorDistance _lastPos; - private _add = ((_boundingBoxB select 2) / 2) + ((((_distance - (_fragpower / 8)) max 0) / _fragPower) * 10); + private _distance = _target distance _fragPosAGL; + private _add = ((_boundingBoxB select 2) / 2) + ((((_distance - (_fragVelocity / 8)) max 0) / _fragVelocity) * 10); _targetPos = _targetPos vectorAdd [ - (_targetVel select 0) * (_distance / _fragPower), - (_targetVel select 1) * (_distance / _fragPower), - _add + (_targetVel select 0) * (_distance / _fragVelocity), + (_targetVel select 1) * (_distance / _fragVelocity), + _add ]; - private _baseVec = _lastPos vectorFromTo _targetPos; + private _baseVec = _fragPosASL vectorFromTo _targetPos; private _dir = floor (_baseVec call CBA_fnc_vectDir); private _currentCount = RETDEF(_fragArcs select _dir,0); if (_currentCount < 10) then { - private _count = ceil (random (sqrt (_m / 1000))); + private _count = ceil (random _metalMassModifier); private _vecVar = FRAG_VEC_VAR; - if !(_target isKindOf "Man") then { + if !(_target isKindOf "CAManBase") then { ADD(_vecVar,(sqrt _cubic) / 2000); if ((crew _target) isEqualTo [] && {_count > 0}) then { _count = 0 max (_count / 2); }; }; + private _vecVarHalf = _vecVar / 2; for "_i" from 1 to _count do { - private _vec = _baseVec vectorDiff [ - (_vecVar / 2) + (random _vecVar), - (_vecVar / 2) + (random _vecVar), - (_vecVar / 2) + (random _vecVar) + private _vectorDir = _baseVec vectorDiff [ + _vecVarHalf - (random _vecVar), + _vecVarHalf - (random _vecVar), + _vecVarHalf - (random _vecVar) ]; - private _fp = _fragPower - (random (_fragPowerRandom)); - private _vel = _vec vectorMultiply _fp; - - private _fragObj = (selectRandom _fragTypes) createVehicleLocal [0,0,10000]; - // TRACE_4("targeted",_fp,typeOf _fragObj,_lastPos vectorDistance _targetPos,typeOf _x); - _fragObj setPosASL _lastPos; - _fragObj setVectorDir _vec; - _fragObj setVelocity _vel; - #ifdef DRAW_FRAG_INFO - [ACE_player, _fragObj, [1,0,0,1]] call FUNC(dev_addTrack); + private _fragObjSpeed = _fragVelocity * (1 - random 0.5); + private _fragObjVelocity = _vectorDir vectorMultiply _fragObjSpeed; + + private _fragObj = createVehicleLocal [selectRandom _fragTypes, _fragPosAGL, [], 0, "CAN_COLLIDE"]; + _fragObj setVectorDir _vectorDir; + _fragObj setVelocity _fragObjVelocity; + _fragObj setShotParents _shotParents; + #ifdef DEBUG_MODE_DRAW + [_fragObj, "green", true] call FUNC(dev_trackObj); + if (GVAR(dbgSphere)) then { + [_targetPos, "(0.88,0.36,0.92,0.8)"] call FUNC(dev_sphereDraw); + }; #endif INC(_fragCount); INC(_currentCount); @@ -158,34 +128,32 @@ if (_objects isNotEqualTo []) then { }; }; if (_fragCount > _maxFrags) exitWith {}; - } forEach _objects; + } forEach _targets; TRACE_1("targeted",_fragCount); if (_fragCount > _maxFrags) exitWith {}; private _randomCount = ceil ((_maxFrags - _fragCount) * 0.35); TRACE_1("",_randomCount); private _sectorSize = 360 / (_randomCount max 1); - if (_doRandom) then { - for "_i" from 1 to _randomCount do { - // Distribute evenly - private _sectorOffset = 360 * (_i - 1) / (_randomCount max 1); - private _randomDir = random (_sectorSize); - _vec = [cos (_sectorOffset + _randomDir), sin (_sectorOffset + _randomDir), sin (30 - (random 45))]; + for "_i" from 1 to _randomCount do { + // Distribute evenly + private _sectorOffset = 360 * (_i - 1) / (_randomCount max 1); + private _randomDir = random (_sectorSize); + private _vectorDir = [cos (_sectorOffset + _randomDir), sin (_sectorOffset + _randomDir), sin (30 - (random 45))]; - _fp = (_fragPower - (random (_fragPowerRandom))); + private _fragObjSpeed = _fragVelocity * (1 - random 0.5); - _vel = _vec vectorMultiply _fp; + _fragObjVelocity = _vectorDir vectorMultiply _fragObjSpeed; - _fragObj = (selectRandom _fragTypes) createVehicleLocal [0, 0, 10000]; - _fragObj setPosASL _lastPos; - _fragObj setVectorDir _vec; - _fragObj setVelocity _vel; + private _fragObj = createVehicleLocal [selectRandom _fragTypes, _fragPosAGL, [], 0, "CAN_COLLIDE"]; + _fragObj setVectorDir _vectorDir; + _fragObj setVelocity _fragObjVelocity; + _fragObj setShotParents _shotParents; - #ifdef DRAW_FRAG_INFO - [ACE_player, _fragObj, [1,0.5,0,1]] call FUNC(dev_addTrack); - #endif - INC(_fragCount); - }; + #ifdef DEBUG_MODE_DRAW + [_fragObj, "blue", true] call FUNC(dev_trackObj); + #endif + INC(_fragCount); }; }; diff --git a/addons/frag/functions/fnc_getFragInfo.sqf b/addons/frag/functions/fnc_getFragInfo.sqf new file mode 100644 index 00000000000..77222234235 --- /dev/null +++ b/addons/frag/functions/fnc_getFragInfo.sqf @@ -0,0 +1,96 @@ +#include "..\script_component.hpp" +/* + * Author: Jaynus, NouberNou, Lambda.Tiger + * This function returns fragmentation parameters for a specific ammo type. + * + * Arguments: + * 0: Ammo classname + * + * Return Value: + * _ammoInfo + * 0: Search range for fragments in meters + * 1: Gurney equation calculated speed + * 2: Array of fragment types + * 3: Mass of fragmenting metal modified for frag count + * + * Example: + * "B_556x45_Ball" call ace_frag_fnc_getFragInfo + * + * Public: No + */ + +params ["_ammo"]; + +GVAR(fragInfoCache) getOrDefaultCall [_ammo, { + private _ammoConfig = configFile >> "CfgAmmo" >> _ammo; + private _fragTypes = ACE_FRAG_DEFAULT_FRAG_TYPES; + private _notifyMissingEntries = false; + if (isArray (_ammoConfig >> QGVAR(classes))) then { + _fragTypes = getArray (_ammoConfig >> QGVAR(classes)); + } else { + _notifyMissingEntries = true; + }; + + /************ Gurney equation notes *****************//* + * see https://en.wikipedia.org/wiki/Gurney_equations + * + * gurney_k is the geometry constant added to _metalMass/_chargeMass + * gurney_c = sqrt(2E) + * + * _chargeMass = 185; - grams of comp-b + * _metalMass = 210; - grams of metal are accelerated by explosion + * _geometryCoefficient = 3/5; - spherical K factor + * _gurneyConstant = 2843; - Gurney constant of comp-b in /ms + * + * _chargeMass = 429; - grams of tritonal + * _metalMass = 496; - grams of metal are accelerated by explosion + * _geometryCoefficient = 1/2; - cylindrical K factor + * _gurneyConstant = 2320; - Gurney constant of tritonal in m/s + * Equation - 0.8 for empirical 80% speed + * 0.8 * (((_metalMass / _chargeMass) + _geometryCoefficient) ^ - (1 / 2)) * _gurneyConstant; + * or 0.8 * _gurneyConstant * sqrt (_chargeMass /(_metalMass + _chargeMass * _geometryCoefficient)); (slightly faster to compute) + */ + + private _chargeMass = getNumber (_ammoConfig >> QGVAR(charge)); + if (_chargeMass == 0) then { + _chargeMass = 1; + _notifyMissingEntries = true; + }; + + private _metalMass = getNumber (_ammoConfig >> QGVAR(metal)); + if (_metalMass == 0) then { + _metalMass = 2; + _notifyMissingEntries = true; + }; + + private _geometryCoefficient = getNumber (_ammoConfig >> QGVAR(gurney_k)); + if (_geometryCoefficient == 0) then { + _geometryCoefficient = 0.5; + _notifyMissingEntries = true; + }; + + private _gurneyConstant = getNumber (_ammoConfig >> QGVAR(gurney_c)); + if (_gurneyConstant == 0) then { + _gurneyConstant = 2440; + _notifyMissingEntries = true; + }; + + if (_notifyMissingEntries) then { + INFO_1("Ammo class %1 lacks proper explosive properties definitions for frag!",_ammo); + }; + + private _indirectHitRange = getNumber (_ammoConfig >> "indirecthitrange"); + + /********************** _ammoInfo format *************************//* + * 0: _fragRange - search range for fragments + * 1: _fragVelocity - gurney equation calculated velocity + * 2: _fragTypes - array of fragment types + * 3: _metalMassModified - mass of fragmenting metal modified for frag count + */ + [ + 80 * _indirectHitRange, + ACE_FRAG_IMPERIC_VELOCITY_CONSTANT * _gurneyConstant * sqrt (_chargeMass / (_metalMass + _chargeMass * _geometryCoefficient)), + _fragTypes, + sqrt (_metalMass / 1000) + ] // return +}, true] diff --git a/addons/frag/functions/fnc_getSpallInfo.sqf b/addons/frag/functions/fnc_getSpallInfo.sqf new file mode 100644 index 00000000000..7bb566064ba --- /dev/null +++ b/addons/frag/functions/fnc_getSpallInfo.sqf @@ -0,0 +1,30 @@ +#include "..\script_component.hpp" +/* + * Author: Lambda.Tiger + * This function returns spalling parameters for a specific ammo type. + * + * Arguments: + * 0: Ammo classname + * + * Return Value: + * _ammoInfo + * 0: Caliber config value + * 1: What part of the hit damage is from ballistic vs explosive energy (1 for all explosive) + * 2: Indirect hit damage + * + * Example: + * "B_556x45_Ball" call ace_frag_fnc_getSpallInfo + * + * Public: No + */ + +params ["_ammo"]; + +GVAR(spallInfoCache) getOrDefaultCall [_ammo, { + private _ammoConfig = configFile >> "CfgAmmo" >> _ammo; + private _caliber = getNumber (_ammoConfig >> "caliber"); + private _explosive = getNumber (_ammoConfig >> "explosive"); + private _indirectHit = getNumber (_ammoConfig >> "indirectHitRange"); + + [_caliber, _explosive, _indirectHit] // return +}, true] diff --git a/addons/frag/functions/fnc_masterPFH.sqf b/addons/frag/functions/fnc_masterPFH.sqf deleted file mode 100644 index 004af9a9ce5..00000000000 --- a/addons/frag/functions/fnc_masterPFH.sqf +++ /dev/null @@ -1,56 +0,0 @@ -#include "..\script_component.hpp" -/* - * Author: jaynus - * Master single PFH abstraction for all rounds being tracked by frag/spall. - * - * Arguments: - * None - * - * Return Value: - * None - * - * Example: - * call ace_frag_fnc_masterPFH - * - * Public: No - */ - -BEGIN_COUNTER(PFH); - -// Fast exit if nothing to do -if (GVAR(objects) isEqualTo []) exitWith {END_COUNTER(PFH);}; - -private _gcIndex = []; - -private _iter = 0; -private _objectCount = count GVAR(objects); -while {_objectCount > 0 && {_iter < (GVAR(maxTrackPerFrame) min _objectCount)}} do { - - if (GVAR(lastIterationIndex) >= _objectCount) then { - GVAR(lastIterationIndex) = 0; - }; - private _object = GVAR(objects) select GVAR(lastIterationIndex); - - if (!isNil "_object") then { - private _args = GVAR(arguments) select GVAR(lastIterationIndex); - - if !(_args call FUNC(pfhRound)) then { - _gcIndex pushBack GVAR(lastIterationIndex); // Add it to the GC if it returns false - }; - }; - INC(_iter); - INC(GVAR(lastIterationIndex)); -}; - -// Clean up dead object references -private _deletionCount = 0; -{ - TRACE_1("GC Projectile",_x); - private _deleteIndex = _x - _deletionCount; - GVAR(objects) deleteAt _deleteIndex; - GVAR(arguments) deleteAt _deleteIndex; - - INC(_deletionCount); -} forEach _gcIndex; - -END_COUNTER(PFH); diff --git a/addons/frag/functions/fnc_pfhRound.sqf b/addons/frag/functions/fnc_pfhRound.sqf deleted file mode 100644 index 412a734a055..00000000000 --- a/addons/frag/functions/fnc_pfhRound.sqf +++ /dev/null @@ -1,59 +0,0 @@ -#include "..\script_component.hpp" -/* - * Author: ACE-Team - * - * - * Arguments: - * None - * - * Return Value: - * None - * - * Example: - * call ace_frag_fnc_pfhRound - * - * Public: No - */ - -params ["_round", "_lastPos", "_lastVel", "_shellType", "_firedFrame", "_firedPos", "_doSpall", "_spallTrack", "_foundObjectHPIds", "_skip", "_explosive", "_indirectRange", "_force", "_fragPower"]; - -if (_round in GVAR(blackList)) exitWith { - false -}; - -if (!alive _round) exitWith { - if ((diag_frameNo - _firedFrame) > 1) then { //skip if deleted within a single frame - if (_skip == 0) then { - if ((_explosive > 0.5 && {_indirectRange >= 4.5} && {_fragPower >= 35}) || {_force == 1}) then { - // shotbullet, shotShell don't seem to explode when touching water, so don't create frags - if ((surfaceIsWater _lastPos) && {(toLowerANSI getText (configFile >> "CfgAmmo" >> _shellType >> "simulation")) in ["shotbullet", "shotshell"]}) exitWith {}; - private _fuseDist = getNumber(configFile >> "CfgAmmo" >> _shellType >> "fuseDistance"); - private _isArmed = _firedPos vectorDistance _lastPos >= _fuseDist; // rounds explode at exactly fuseDistance, so check inclusive - TRACE_2("",_fuseDist,_isArmed); - if (!_isArmed) exitWith {TRACE_1("round not armed",_this);}; - TRACE_3("Sending frag event to server",_lastPos,_lastVel,_shellType); - [QGVAR(frag_eh), [_lastPos,_lastVel,_shellType]] call CBA_fnc_serverEvent; - }; - }; - }; - if (_doSpall) then { - DEC(GVAR(spallIsTrackingCount)); - TRACE_1("doSpall",_foundObjectHPIds); - { - if (!isNil "_x") then { - _x removeEventHandler ["HitPart", _foundObjectHPIds select _forEachIndex]; - }; - } forEach _spallTrack; - }; - false -}; - -_this set [1, getPosASL _round]; -_this set [2, velocity _round]; - -if (_doSpall) then { - private _scale = ((count GVAR(objects)) / GVAR(maxTrackPerFrame)) max 0.1; - [_round, _scale, _spallTrack, _foundObjectHPIds] call FUNC(spallTrack); -}; - -true diff --git a/addons/frag/functions/fnc_shouldFrag.sqf b/addons/frag/functions/fnc_shouldFrag.sqf new file mode 100644 index 00000000000..8cc62f3dac0 --- /dev/null +++ b/addons/frag/functions/fnc_shouldFrag.sqf @@ -0,0 +1,46 @@ +#include "..\script_component.hpp" +/* + * Author: Lambda.Tiger + * This function checks whether an ammunition type should create fragments. + * + * Arguments: + * 0: Ammo classname + * + * Return Value: + * Could the ammo class generate fragments + * + * Example: + * "B_556x45_Ball" call ace_frag_fnc_shouldFrag + * + * Public: No + */ + +params ["_ammo"]; + +GVAR(shouldFragCache) getOrDefaultCall [_ammo, { + private _shouldFrag = true; + + private _ammoConfig = configFile >> "CfgAmmo" >> _ammo; + private _skip = getNumber (_ammoConfig >> QGVAR(skip)); + if (_skip == 1) then { + _shouldFrag = false; + TRACE_1("No frag: skip",_skip); + }; + + private _force = getNumber (_ammoConfig >> QGVAR(force)); + if (_shouldFrag && _force == 0) then { + private _explosive = getNumber (_ammoConfig >> "explosive"); + if (_explosive < 0.5) exitWith { + _shouldFrag = false; + TRACE_3("No frag: _explosive",_skip,_force,_explosive); + }; + private _indirectHit = getNumber (_ammoConfig >> "indirectHit"); + private _indirectRange = getNumber (_ammoConfig >> "indirectHitRange"); + if (_indirectRange < 4.5 || {_indirectHit * sqrt(_indirectRange) < 35}) then { + _shouldFrag = false; + TRACE_5("No frag",_ammo,_skip,_explosive,_indirectRange,_indirectHit); + }; + }; + + _shouldFrag +}, true] diff --git a/addons/frag/functions/fnc_shouldSpall.sqf b/addons/frag/functions/fnc_shouldSpall.sqf new file mode 100644 index 00000000000..430a4da8229 --- /dev/null +++ b/addons/frag/functions/fnc_shouldSpall.sqf @@ -0,0 +1,24 @@ +#include "..\script_component.hpp" +/* + * Author: Lambda.Tiger + * This function checks whether an ammunition type should cause spalling. + * + * Arguments: + * 0: Ammo classname + * + * Return Value: + * Whether the round type could spall when hitting an object + * + * Example: + * "B_556x45_Ball" call ace_frag_fnc_shouldSpall + * + * Public: No + */ + +params ["_ammo"]; + +GVAR(shouldSpallCache) getOrDefaultCall [_ammo, { + (_ammo call FUNC(getSpallInfo)) params ["_caliber", "_explosive", "_indirectHit"]; + + _caliber >= 2.5 || (_explosive > 0 && _indirectHit >= 1) // return +}, true] diff --git a/addons/frag/functions/fnc_spallHP.sqf b/addons/frag/functions/fnc_spallHP.sqf deleted file mode 100644 index 367bea76449..00000000000 --- a/addons/frag/functions/fnc_spallHP.sqf +++ /dev/null @@ -1,42 +0,0 @@ -#include "..\script_component.hpp" -/* - * Author: ACE-Team - * Handles the HitPart event - * - * Arguments: - * None - * - * Return Value: - * None - * - * Example: - * call ace_frag_fnc_spallHP - * - * Public: No - */ - -//player sideChat format ["f: %1 c: %2", (_this select 0), (count GVAR(spallHPData))]; - -params ["_index", "_hitPartData"]; - -private _initialData = GVAR(spallHPData) param [_index, []]; -if (_initialData isEqualTo []) exitWith {}; - -private _hpRound = (_hitPartData select 0) select 2; -private _round = _initialData select 3; -private _hpDirect = (_hitPartData select 0) select 10; - -if (_hpDirect && {_round == _hpRound}) then { - { - // diag_log text format ["HPDUMP-------------------------------------"]; - // { - // _hp = _x; - // diag_log text format ["%1 --", _forEachIndex]; - // { - // diag_log text format ["%1: %2", _forEachIndex, _x]; - // } forEach _hp; - // } forEach (_this select 1); - [DFUNC(doSpall), [_this, _forEachIndex]] call CBA_fnc_execNextFrame; - // player sideChat "WEEE"; - } forEach _hitPartData; -}; diff --git a/addons/frag/functions/fnc_spallTrack.sqf b/addons/frag/functions/fnc_spallTrack.sqf deleted file mode 100644 index 50ca64b6ec4..00000000000 --- a/addons/frag/functions/fnc_spallTrack.sqf +++ /dev/null @@ -1,39 +0,0 @@ -#include "..\script_component.hpp" -/* - * Author: ACE-Team - * Add HitPart EventHandler to objects in the projectile's path - * - * Arguments: - * None - * - * Return Value: - * None - * - * Example: - * call ace_frag_fnc_spallTrack - * - * Public: No - */ - -params ["_round", "_multiplier", "_foundObjects", "_foundObjectHPIds"]; - -private _delta = (1 / diag_fps) * _multiplier; -private _curPos = getPosASL _round; -private _velocity = velocity _round; - -private _velocityStep = _velocity vectorMultiply _delta; -private _forwardPos = _curPos vectorAdd _velocityStep; - - -private _intersectsWith = lineIntersectsWith [_curPos, _forwardPos]; - -if (_intersectsWith isEqualTo []) exitWith {}; -{ - // diag_log text format ["Adding HP: %1", _x]; - private _index = count GVAR(spallHPData); - private _hpId = _x addEventHandler ["HitPart", compile format ["[%1, _this] call " + QFUNC(spallHP), _index]]; - _foundObjects pushBack _x; - _foundObjectHPIds pushBack _hpId; - private _data = [_hpId, _x, typeOf _round, _round, _curPos, _velocity, 0, _foundObjects, _foundObjectHPIds]; - GVAR(spallHPData) pushBack _data; -} forEach (_intersectsWith select {!(_x in _foundObjects)}); diff --git a/addons/frag/initSettings.inc.sqf b/addons/frag/initSettings.inc.sqf index 421d5d45664..b05f3280dd2 100644 --- a/addons/frag/initSettings.inc.sqf +++ b/addons/frag/initSettings.inc.sqf @@ -22,19 +22,3 @@ private _category = format ["ACE %1", localize LSTRING(Module_DisplayName)]; false, 1 ] call CBA_fnc_addSetting; - -[ - QGVAR(maxTrack), "SLIDER", - [LSTRING(MaxTrack), LSTRING(MaxTrack_Desc)], - _category, - [0, 50, 10, -1], - 1 -] call CBA_fnc_addSetting; - -[ - QGVAR(maxTrackPerFrame), "SLIDER", - [LSTRING(MaxTrackPerFrame), LSTRING(MaxTrackPerFrame_Desc)], - _category, - [0, 50, 10, -1], - 1 -] call CBA_fnc_addSetting; diff --git a/addons/frag/initSettingsDebug.inc.sqf b/addons/frag/initSettingsDebug.inc.sqf new file mode 100644 index 00000000000..a1528a446cf --- /dev/null +++ b/addons/frag/initSettingsDebug.inc.sqf @@ -0,0 +1,25 @@ +private _category = format ["ACE %1", LLSTRING(Module_DisplayName)]; + +[ + QGVAR(debugOptions), + "CHECKBOX", + [LSTRING(EnableDebugTrace), LSTRING(EnableDebugTrace_Desc)], + [_category, LSTRING(Debug)], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(dbgSphere), + "CHECKBOX", + [LSTRING(HitSphereEnable), LSTRING(HitSphereEnable_Desc)], + [_category, LSTRING(Debug)], + false +] call CBA_fnc_addSetting; + +[ + QGVAR(drawHitBox), + "CHECKBOX", + [LSTRING(DrawHitBox), LSTRING(DrawHitBox_Desc)], + [_category, LSTRING(Debug)], + true +] call CBA_fnc_addSetting; diff --git a/addons/frag/script_component.hpp b/addons/frag/script_component.hpp index 0215e9f4d74..bb2476f3c3c 100644 --- a/addons/frag/script_component.hpp +++ b/addons/frag/script_component.hpp @@ -2,13 +2,15 @@ #define COMPONENT_BEAUTIFIED Frag #include "\z\ace\addons\main\script_mod.hpp" -// #define DRAW_FRAG_INFO +// #define LOG_FRAG_INFO // #define DEBUG_MODE_FULL +// #define DEBUG_MODE_DRAW // #define DISABLE_COMPILE_CACHE // #define ENABLE_PERFORMANCE_COUNTERS #ifdef DEBUG_ENABLED_FRAG #define DEBUG_MODE_FULL + #define DEBUG_MODE_DRAW #endif #ifdef DEBUG_SETTINGS_FRAG @@ -17,4 +19,19 @@ #include "\z\ace\addons\main\script_macros.hpp" -#define ACE_TRACE_DRAW_INC 1 +// Mimimum hold-off time between frag events per unit +#define ACE_FRAG_FRAG_UNIT_HOLDOFF 0.5 +#define ACE_FRAG_SPALL_UNIT_HOLDOFF 0.5 +// Scaling for the min/max # of fragments since last frag event +#define ACE_FRAG_COUNT_MIN_TIME 0.1 +#define ACE_FRAG_COUNT_MIN 5 +#define ACE_FRAG_COUNT_MAX_TIME 1.5 +#define ACE_FRAG_COUNT_MAX 50 +#define ACE_FRAG_IMPERIC_VELOCITY_CONSTANT 0.8 +#define ACE_FRAG_DEFAULT_FRAG_TYPES [\ + QGVAR(tiny), QGVAR(tiny), QGVAR(tiny),\ + QGVAR(tiny_HD), QGVAR(tiny_HD), QGVAR(tiny_HD),\ + QGVAR(small), QGVAR(small), QGVAR(small), QGVAR(small),\ + QGVAR(small_HD), QGVAR(small_HD), QGVAR(small_HD), QGVAR(small_HD),\ + QGVAR(medium_HD), QGVAR(medium_HD), QGVAR(medium_HD), QGVAR(medium_HD), QGVAR(medium_HD)\ +] diff --git a/addons/frag/stringtable.xml b/addons/frag/stringtable.xml index f88877448d8..8da69da0867 100644 --- a/addons/frag/stringtable.xml +++ b/addons/frag/stringtable.xml @@ -111,101 +111,28 @@ 啟用ACE模擬爆炸反射 Povolit ACE simulaci odrazu exploze - - Maximum Projectiles Tracked - Máximos proyectiles rastreados - Maks. liczba śledzonych pocisków - Maximalzahl der verfolgten Projektile - Maximální počet sledovaných projektilů - Máximo de projéteis rastreados - Nombre maximum de projectiles suivis - Maximum követett repeszek - Макс. количество отслеживаемых снарядов - Numero massimo di Proiettili Tracciati - 飛翔体最大追跡数 - 최대 발사체 추적수 - 最大破片粒子追踪数量 - 最大碎片/剝落粒子追蹤數量 + + Debug - - This setting controls the maximum amount of projectiles the fragmentation and spalling system will track at any given time. If more projectiles are fired, they will not be tracked. Lower this setting if you do not want FPS drops at high-count projectile scenarios ( >200 rounds in the air at once) - Este ajuste controla la cantidad máxima de proyectiles del sistema de fragmentación y astillamiento de los que se hará un seguimiento en cualquier momento dado. Si se disparan más proyectiles, no serán rastreados. Baja esta opción si no deseas una bajada de FPS en escenarios con muchos proyectiles (>200 proyectiles en el aire a la vez) - To ustawienie kontroluje maksymalną ilość pocisków, jakie fragmentacja i odpryski symulują w danym momencie. Jeżeli więcej pocisków będzie wystrzelonych, wtedy nie będą one śledzone. Zmniejsz tą opcję jeżeli nie chcesz odczuwać spadków FPS podczas ciężkiej wymiany ognia (więcej niż 200 pocisków w powietrzu na raz). - Diese Einstellung steuert die maximale Anzahl an Projektilen, die das Splitter- und Explosionssystem gleichzeitig verfolgen wird. Wenn mehr Projektile abgefeuert werden, werden sie nicht verfolgt werden. Diese Einstellung zu verringern, kann FPS-Einbrüche bei Szenarien mit vielen Projektilen verhindern (>200 Objekte gleichzeitig in der Luft) - Toto nastavení kontroluje maximální množství projektilů z fragmentace a úlomků, která jsou sledována v dané době. Pokud je vystřeleno více projektilů, tak nebudou sledovány. Snižte toto nastavení pokud si nepřejete propady FPS v situacích, kde je velké množství projektilů ( >200 nábojů najednou ve vzduchu) - Esta definição controla a quantidade máxima de projéteis que o sistema de fragmentação e estilhaçamento irá acompanhar em qualquer momento. Se mais projéteis são disparados, eles não serão rastreados. Diminua essa configuração se você não quiser que o FPS caia em cenários com alta contagem de projéteis (> 200 projéteis no ar ao mesmo tempo) - Ce paramètre contrôle le nombre maximum de projectiles et d'éclats résultant de la fragmentation, que le système peut suivre à chaque instant.\nSi plus de projectiles sont générés, ils ne seront pas pris en compte. Baissez ce réglage si vous ne voulez pas de chute de FPS en cas de nombre important de projectiles (>200 éclats en même temps). - Ez a beállítás szabályozza a repeszeződés és pattogzás által kilőtt objektumok követett számát. Ha több ez a szám, ezek az objektumok nem lesznek követve. Csökkentsd ezt a beállítást, ha nem akarsz lassulásokat magas-törmelékmennyiségű helyzetekben (200+ repesz a levegőben egyszerre) - Эта настройка контролирует максимальное количество снарядов, которок отслеживает система осколков и обломков в каждый момент времени. /nСнаряды, выстреленные сверх этого числа, отслеживаться не будут. Уменьшите это значение, если вы не хотите падения FPS при большом количестве снарядов в одной перестрелке (> 200 одновременно летящих снарядов) - Questo parametro controlla il numero massimo di proiettili che la frammentazione e il sistema di spalling tracciano in ogni momento. Se vengono sparati ulteriori proiettili, non verranno tracciati. Abbassa questo parametro se non vuoi cali di FPS in scenari con molti proiettili (>200 proiettili in aria contemporaneamente) - この設定では、断片化および剥離システムが常に追跡する飛翔体の最大量を制御します。 この値より多くの飛翔体が発射された場合、それらは追跡されません。 弾数が多いシナリオでFPSを低下させたくない場合は、この設定を下げてください。 (一度に200発以上が空中に発射されます) - 이 설정은 탄환파편 및 파편 시스템으로 인해 생긴 발사체의 수를 결정합니다. 만약 더 많은 발사체가 나올 경우 정해진 수 이외에는 추적하지 않습니다. 이 설정을 낮춤으로써 파편이 많은 시나리오를 실행할때 더욱 원활히 진행할 수 있습니다 (한 번에 200개 이하) - 设定在指定时间内,系统最大可追踪的破片粒子数量。如有更多的碎片在这之后产生,这些粒子将不会被追踪。如果你想要维持好的帧数,此设定勿调的过高。( >一次200颗粒子) - 設定在指定時間內,系統最大可追蹤的碎片/剝落粒子數量。如有更多的碎片在這之後產生,這些粒子將不會被追蹤。如果你想要維持好的幀數,此設定勿調的過高。( >一次200顆粒子) + + Frag/Spall Debug Tracing + Splitter-/Explosions-Debug-Verfolgung - - Maximum Projectiles Per Frame - Máximos proyectiles por cuadro - Maximale Anzahl an Projektilen pro Frame - Maks. liczba pocisków na klatkę - Maximální počet projektilů za jeden snímek - Projéteis máximos por quadro - Nombre maximal de projectiles par image - Maximum repesz/képkocka - Макс. количество снарядов за кадр - Numero massimo di proiettili per Frame - フレームごとの飛翔体最大数 - 프레임 당 최대 발사체 수 - 每帧最大破片粒子数量 - 每一幀數(FPS)最大碎片/剝落粒子數量 + + Enables visual tracing of fragmentation and spalling rounds. + Splitter-/Explosions-Debugging - - The number of spall track calculations to perform in any given frame. This helps spread the FPS impact of tracking spall rounds across multiple frames, limiting its impact even further. - Ilość obliczeń wykonywanych przez symulację odprysków w danej klatce. Ta opcja pomaga rozprzestrzenić obliczenia odprysków na więcej klatek, zmniejszając spadek FPS jeszcze bardziej. - Gibt die Anzahl der Explosionverfolgungsberechnungen an, die gleichzeitig ausgeführt werden. Das kann dabei helfen den FPS-Einfluss abzuschwächen, wenn Teile über mehrere Frames hinweg verfolgt werden. - El número de cálculos de esquirlas que se hará en cualquier cuadro. Esto ayuda a dispersar el impacto en FPS del seguimiento de esquirlas de balas a través de múltiples cuadros, lo que limita aún más su impacto. - Počet úlomků v daném snímku. Toto pomáhá rozšířit FPS dopad sledovaného úlomku napříč více snímky, omezuje jeho vliv ještě více. - O número de cálculos por estilhaço rastreado para executar em qualquer quadro. Isso ajuda a distribuir o impacto no FPS do rastreamento de estilhaço em vários quadros, o que limita o seu impacto ainda mais. - Le nombre de calculs de suivi à effectuer pour chaque image. Cela aide à répartir l'impact des calculs sur plusieurs images, limitant ainsi encore davantage l'impact sur les FPS. - A lepattogzási útvonalak számításának darabjai képkockánként. Ez eloszlatja az FPS-megszakadást több képkockára, ezzel csökkentve a súlyosságát. - Число обрабатываемых осколков за кадр. Это позволяет распределить нагрузку по отслеживанию осколков между несколькими кадрами, чтобы предотвратить падение FPS. - Il numero di calcoli per tracciamento di spalling ad ogni frame. Questo aiuta a distribuire l'impatto del tracciamento dello spalling su più frame, riducendolo ulteriormente. - 任意のフレームごとに追跡される剥離飛翔体の数。剥離による飛翔体を追跡することによるFPSへの影響を複数フレームに分散させ抑えることが出来ます。 - 가능한 프레임마다 파편을 추적 및 계산합니다. 여러 프레임에 걸쳐 파편난 발사체를 추적하여 FPS에 도움을 줍니다. 이를 제한함으로써 더욱 큰 효과를 볼 수 있습니다. - 设定在每一帧数内,系统最大可追踪的破片粒子数量。此设定可有效帮助系统减低计算压力。 - 設定在每一幀數內,系統最大可追蹤的碎片/剝落粒子數量。此設定可有效幫助系統減低計算壓力 + + Draw Event Spheres - - (SP Only) Frag/Spall Debug Tracing - (Solo SP) Seguimiento de depuración de Fragmentación/Astillamiento - (Tylko SP) Wizualny debug odł./odpr. - (Pouze SP) Debug sledování Frag/Úlomků - (nur SP) Splitter-/Explosions-Debug-Verfolgung - (Somente SP) Depuração de fragmentação e estilhaços traçantes - (SP uniquement) Fragmentation/éclat debug - (Csak SP) Repesz/Pattogzás debug követés - (Только для одиночной игры) Отслеживаение/отладка осколков - (Solo SP) Debug Tracciamento Frag/Spall - (SP のみ) 破片/剥離のデバッグ用表示 - (싱글플레이 전용) 탄환파편/파편 디버그 추적화 - (仅单人)追踪显示破片粒子 - (僅在單人模式) 碎片/剝落除錯追蹤 + + Draw color coded spheres at any event for tracked rounds. - - (SP Only) Requires a mission/editor restart. Enables visual tracing of fragmentation and spalling rounds in SP game mode only. - (Solo SP) Requiere un reinicio misión/editor. Permite el seguimiento visual de la fragmentación y astillamientos de los proyectiles en modo SP. - (Tylko SP) Wymaga restartu misji/edytora. Aktywuje wizualne śledzenie odłamków oraz odprysków w trybie gry Single Player. - (nur SP) Splitter-/Explosions-Debugging - (Pouze SP) Vyžaduje restart mise/editoru. Aktivuje vizuální stopování fragmentace a úlomů pouze v režimu jednoho hráče. - (Somente SP) Requer um reinício de missão / editor. Habilita o rastreamento visual de projéteis de fragmentação e estilhaçamento apenas no modo de jogo SP. - (SP seulement) Requiert un redémarrage de mission ou de l'éditeur. Active les traceurs visuels de fragmentation et d'éclats en mode solo seulement. - (Csak SP) Küldetés/Editor újraindítás szükséges. Engedélyezi a repeszek és pattogzó lövedékek vizuális nyomkövetését, csak egyjátékos módok alatt. - (Только для одиночной игры) Требует перезапуска миссии/редактора. Включает визуальные следы от осколков и обломков в режиме одиночной игры. - (Solo SP) Richiede un restart editor/missione. Abilita il tracciamento visivo di schegge da frammentazione/spalling in modalità Giocatore Singolo. - (SP のみ) ミッションとエディタの再起動が必要です。有効化すると、シングルプレイでのみ破片と剥離の飛翔体が見えるようになります。 - (仅单人)激活后,只有在单人模式下才可观察到破片粒子的移动轨迹。 - (僅在單人模式) 讓你在單人模式下可觀察到碎片/剝落粒子的移動軌跡 - (SP 전용) 임무 / 편집자가 다시 시작해야합니다. SP 게임 모드에서만 파편화 및 탄환파편의 시각적 추적을 가능하게 합니다. + + Draw Hitboxes + + + Draw hitboxes on objects that were targeted. diff --git a/docs/wiki/framework/frag-framework.md b/docs/wiki/framework/frag-framework.md index d05f5068f69..2c453eb7497 100644 --- a/docs/wiki/framework/frag-framework.md +++ b/docs/wiki/framework/frag-framework.md @@ -14,7 +14,7 @@ version: ## 1. Overview -The fragmentation system in ACE3 is a significant improvement over the fragmentation system in ACE2. Previously the system relied on fuzzy math from the values of `indirectHit` and `indirectHitRange` in `CfgAmmo` to calculate roughly the velocity and range of fragmentation. This had some serious drawbacks, especially in the case of smaller explosives such as hand grenades and 40mm grenades where casualty production was lower than desired. +The fragmentation system in ACE3 is a significant improvement over the fragmentation system in ACE2. Previously the system relied on fuzzy math from the values of `indirectHit` and `indirectHitRange` in `CfgAmmo` to calculate roughly the velocity and range of fragmentation. This had some serious drawbacks, especially in the case of smaller explosives such as hand grenades and 40mm grenades where lethality was lower than desired. In ACE3 the system has moved away from what "feels" right to actual explosive engineering equations, primarily the [Gurney equations](http://en.wikipedia.org/wiki/Gurney_equations). This allows us to get close to the actual fragmentation velocities that would be produced by an explosive configuration similar to type of ammo we are simulating. @@ -26,7 +26,7 @@ The system for the end-developer is easy to use, and only requires minimal resea ```cpp class CfgAmmo { class MyGrenade { - ace_frag_enabled = 1; // Enable fragmentation (0-disabled, 1-enabled) + ace_frag_enabled = 1; // Deprecated ace_frag_metal = 210; // Amount of metal being fragmented (grams) - information below ace_frag_charge = 185; // Amount of explosive filler (grams) - information below ace_frag_gurney_c = 2843; // Gurney velocity constant for explosive type - information below @@ -38,27 +38,27 @@ class CfgAmmo { }; ``` -### 1.1 Metal amount +### 2.1 Metal amount `ace_frag_metal` -Amount of metal being fragmented (generally taken as the entire weight of the warhead, though in some cases you might want to only include the fragmentation jacket or body. +The amount of metal being fragmented. Generally taken as the entire weight of the warhead, though in some cases you might want to only include the fragmentation jacket or body. -Dimensionless value, as long as same unit as `ace_frag_charge` (for example `kg/kg` or `g/g` or `lbs/lbs`). +`ace_frag_charge` is the total mass of fragmenting metal given in grams. -### 1.2 Explosives filler amount +### 2.2 Explosives filler amount `ace_frag_charge` -Amount of explosive filler in the warhead. `ace_frag_metal` and `ace_frag_charge` are dimensionless values, as long as they are both in the same unit (for example kg/kg g/g lbs/lbs). +The mass of explosive filler in the warhead. This may include any detonation/ignition charges, but usually such charges are relatively small. -Dimensionless value, as long as same unit as `ace_frag_metal` (for example `kg/kg` or `g/g` or `lbs/lbs`). +`ace_frag_metal` is the total mass of explosive filler given in grams. -### 1.3 Gurney velocity constant +### 2.3 Gurney velocity constant `ace_frag_gurney_c` -Gurney constant for explosive force. You can find a list of common explosive types below. If you can not find it here, or want more accurate numbers, just google the type of explosive and Gurney constant and you can find substantial information. This is **not** the detonation velocity of the explosive, do not confuse them! +The Gurney constant for explosive force. You can find a list of common explosive types below. If you can not find it here, or want more accurate numbers, just google the type of explosive and Gurney constant and you can find substantial information. This is **not** the detonation velocity of the explosive, do not confuse them†! | Type | Speed | | --------------- | -------- | @@ -77,11 +77,13 @@ Gurney constant for explosive force. You can find a list of common explosive typ | TNT | 2440 m/s | | Tritonal | 2320 m/s | -### 1.4 Gurney shape factor +†A rule of thumb from literature is that the Gurney constant is given as 0.338 times the detonation velocity. + +### 2.4 Gurney shape factor `ace_frag_gurney_k` -Shape factor for the explosive configuration. You should choose it based on the general configuration of explosives/metal in the warhead. Most grenades for example are a sphere. Artillery and aircraft bombs are a cylinder. Mines generally a flat plate. Below is a list of the three common shapes and their factors. +The shape factor for the explosive configuration. You should choose it based on the general configuration of explosives/metal in the warhead. Most grenades for example are a sphere. Artillery and aircraft bombs are a cylinder. Mines generally a flat plate. Below is a list of the three common shapes and their factors. | Shape | Factor | | -------- | ------ | @@ -91,7 +93,7 @@ Shape factor for the explosive configuration. You should choose it based on the There are other configurations but these are the most common. If you are interested in others check out the wikipedia link given above. Most of these will not correctly function in ACE3 though due to additional variables for the equation. -### 1.5 Fragments type +### 2.5 Fragments type `ace_frag_classes[]` @@ -110,16 +112,16 @@ There are different types of fragmentation fragments to choose from, and they ca | ACE_frag_huge | | ACE_frag_huge_HD | -The tinier the piece of fragmentation the shorter the distance of travel. The `_HD` variants are all even higher drag versions. Grenades generally should use the `_HD` variants. Experimentation here is important. +Tinier fragments do less damage, and generally correlate to lower mass fragments. The `_HD` variants are all higher drag versions. Higher drag version are useful for fragments that are irregular or would not fly very far. Experimentation here is important. -### 1.6 Ignore fragmentation +### 2.6 Ignore fragmentation `ace_frag_skip` -Setting this to `1` will skip fragmentation for ammo of this type. This is useful for things that might cause high network load, such as FFAR rockets, or possibly even 40mm grenades from AGLs. Experimentation under network conditions is required. +When `1`, the ammunition type will not produce fragments. `ace_frag_skip` does not stop submunitions of the ammo type from producing fragments. `ace_frag_skip` may be helpful for ammunition types that might cause high network load or for explosives that do not produce fragments. Experimentation under network conditions may be required. `ace_frag_skip` takes a higher priority than `ace_frag_force`. -### 1.7 Force fragmentation +### 2.7 Force fragmentation `ace_frag_force` -Settings this to `1` will force the fragmentation system to use frag on this ammo, ignoring internal qualifications based on hit values. +When `1`, the ammunition type will fragment, ignoring internal hit value-based qualifications. `ace_frag_force` takes a lower priority than `ace_frag_skip`.