From 1511ecc1c0211d814e7564578497a36e43cf159e Mon Sep 17 00:00:00 2001 From: Brandon Danyluk Date: Thu, 6 Dec 2018 19:27:30 -0700 Subject: [PATCH] SACLOS and HOT Missiles (#6708) * abc * Revert "abc" This reverts commit bcb4214bd99bba3fec692efa4dca950323da582d. * Update to current commit * Added HOT1 Missile and SACLOS/Wire guidance * Added all HOT variants. Added polish to code * Fixed bug with pylons * Changed how seeker angle is calculated. When the wire snaps the missile goes haywire. Fixed bug where HOT2/3 missiles weren't getting missile guidance * Replaced Wiesel FireFIST launcher with HOT Launcher * Remove debug defines * Tweak thrust * Fix formatting issues. Added true randomness. Added ACE prefixes. Added string table. Tweaked missile dynamics * Fix bug where attack profile correction was wrong due to magnitude always being 50. Add stringtable values for relevant strings. Added reload time to Wiesel ATGM. Added "onFired" to initialize values * Moved wire-snapping logic to attack profile * Missile flight dynamics tweaked * Add a crosshair offset. The missile sits in this offset relative to the crosshair * Add LOS checks. Fix bug where wire-cutting didnt work. * Tweak explosive range for a kill radius of ~20m. Add fragmentation * Add AI Flags * Person in control of missile may not be the shooter * Fix RPT spam on missile out of LOS. Tweak missile dynamics. Add wire break sound cue * Fix bug where missile didn't go to a fake target in front of it when out of LOS * Use a better, more generic way to calculate direction camera is facing * Use ACE Macros for frag values. Get config entry with CBA * Add Wiki entry * Add new lines to wiki. Allow for SQF expressions in config for maxCorrectableDistance * Add CPP code tag * Fix wiki grammer error * Re-convert back to CBA_fnc_getConfigEntry * UAV Gunner support, cleanup * Fix bug where SACLOS for launcher guided weapons was off * Add the ability to define how far ahead of the missile the attack profile will seek toward --- addons/hot/$PBOPREFIX$ | 2 + addons/hot/ACE_GuidanceConfig.hpp | 19 ++ addons/hot/CfgAmmo.hpp | 140 ++++++++++++ addons/hot/CfgEventHandlers.hpp | 12 + addons/hot/CfgMagazines.hpp | 205 ++++++++++++++++++ addons/hot/CfgVehicles.hpp | 19 ++ addons/hot/CfgWeapons.hpp | 62 ++++++ addons/hot/README.md | 12 + addons/hot/XEH_PREP.hpp | 4 + addons/hot/XEH_preInit.sqf | 10 + addons/hot/XEH_preStart.sqf | 4 + addons/hot/config.cpp | 23 ++ .../hot/functions/fnc_attackProfile_WIRE.sqf | 60 +++++ addons/hot/functions/fnc_onFired.sqf | 53 +++++ .../hot/functions/fnc_seekerType_SACLOS.sqf | 50 +++++ addons/hot/functions/script_component.hpp | 2 + addons/hot/script_component.hpp | 28 +++ addons/hot/stringtable.xml | 71 ++++++ docs/wiki/feature/hot.md | 66 ++++++ 19 files changed, 842 insertions(+) create mode 100644 addons/hot/$PBOPREFIX$ create mode 100644 addons/hot/ACE_GuidanceConfig.hpp create mode 100644 addons/hot/CfgAmmo.hpp create mode 100644 addons/hot/CfgEventHandlers.hpp create mode 100644 addons/hot/CfgMagazines.hpp create mode 100644 addons/hot/CfgVehicles.hpp create mode 100644 addons/hot/CfgWeapons.hpp create mode 100644 addons/hot/README.md create mode 100644 addons/hot/XEH_PREP.hpp create mode 100644 addons/hot/XEH_preInit.sqf create mode 100644 addons/hot/XEH_preStart.sqf create mode 100644 addons/hot/config.cpp create mode 100644 addons/hot/functions/fnc_attackProfile_WIRE.sqf create mode 100644 addons/hot/functions/fnc_onFired.sqf create mode 100644 addons/hot/functions/fnc_seekerType_SACLOS.sqf create mode 100644 addons/hot/functions/script_component.hpp create mode 100644 addons/hot/script_component.hpp create mode 100644 addons/hot/stringtable.xml create mode 100644 docs/wiki/feature/hot.md diff --git a/addons/hot/$PBOPREFIX$ b/addons/hot/$PBOPREFIX$ new file mode 100644 index 0000000000..291b6fb1a7 --- /dev/null +++ b/addons/hot/$PBOPREFIX$ @@ -0,0 +1,2 @@ +z\ace\addons\hot + diff --git a/addons/hot/ACE_GuidanceConfig.hpp b/addons/hot/ACE_GuidanceConfig.hpp new file mode 100644 index 0000000000..81f5c69261 --- /dev/null +++ b/addons/hot/ACE_GuidanceConfig.hpp @@ -0,0 +1,19 @@ +class EGVAR(missileguidance,AttackProfiles) { + class WIRE { + name = CSTRING(missileType); + visualName = CSTRING(missileType); + description = CSTRING(missileType_Description); + + functionName = QFUNC(attackProfile_WIRE); + }; +}; +class EGVAR(missileguidance,SeekerTypes) { + class SACLOS { + name = "SACLOS"; + visualName = "SACLOS"; + description = CSTRING(SACLOS_Description); + + functionName = QFUNC(seekerType_SACLOS); + }; +}; + diff --git a/addons/hot/CfgAmmo.hpp b/addons/hot/CfgAmmo.hpp new file mode 100644 index 0000000000..168386d47f --- /dev/null +++ b/addons/hot/CfgAmmo.hpp @@ -0,0 +1,140 @@ +class CfgAmmo { + class M_Scalpel_AT; + class ammo_Penetrator_Base; + + class GVAR(ammo_Penetrator_HOT1): ammo_Penetrator_Base { + caliber = 60; + warheadName = "HEAT"; + hit = 720; + }; + + class GVAR(ammo_Penetrator_HOT2): ammo_Penetrator_Base { + caliber = 65; + warheadName = "HEAT"; + hit = 900; + }; + + class GVAR(ammo_Penetrator_HOT3): ammo_Penetrator_Base { + caliber = 80; + warheadName = "TandemHEAT"; + hit = 1000; + }; + + class GVAR(HOT1): M_Scalpel_AT { + aiAmmoUsageFlags = "128+512"; + model = "\A3\Weapons_F_Tank\Launchers\Vorona\Vorona_missile_heat_fly"; + proxyShape = "\A3\Weapons_F\Ammo\Missile_AT_03_F"; + submunitionAmmo = QGVAR(ammo_Penetrator_HOT1); + submunitionDirectionType = "SubmunitionModelDirection"; + submunitionInitSpeed = 1000; + submunitionParentSpeedCoef = 0; + submunitionInitialOffset[] = { 0, 0, -0.2 }; + hit = 150; + warheadName = "HEAT"; + indirectHit = 25; + indirectHitRange = 3.5; + explosive = 0.8; + + displayName = CSTRING(hot1); + displayNameShort = CSTRING(hot1); + description = CSTRING(missileType_Description); + descriptionShort = CSTRING(missileType); + + effectsMissile = "missile2"; + + irLock = 0; + laserLock = 0; + manualControl = 0; + maxSpeed = 240; + + thrustTime = 17; + thrust = 125; + timeToLive = 40; + initTime = 0.3; + + EGVAR(rearm,caliber) = 178; + + class ace_missileguidance { + enabled = 1; + + minDeflection = 0; // Minium flap deflection for guidance + maxDeflection = 0.0030; // Maximum flap deflection for guidance + incDeflection = 0.0005; // The incrmeent in which deflection adjusts. + + canVanillaLock = 0; // Can this default vanilla lock? Only applicable to non-cadet mode + + // Guidance type for munitions + defaultSeekerType = "SACLOS"; + seekerTypes[] = { "SACLOS" }; + + defaultSeekerLockMode = "LOAL"; + seekerLockModes[] = { "LOAL", "LOBL" }; + + onFired = QFUNC(onFired); + + seekLastTargetPos = 0; // seek last target position [if seeker loses LOS of target, continue to last known pos] + seekerAngle = 30; // Angle from the shooter's view that can track the missile + seekerAccuracy = 1; // seeker accuracy multiplier + + seekerMinRange = 75; + seekerMaxRange = 4000; // Range from the missile which the seeker can visually search + + correctionDistance = 15; // distance from center of crosshair where missile slows down + offsetFromCrosshair[] = { 0, 0, 0.5 }; // where the missile wants to stay in relation to the center of the crosshair. + + // Attack profile type selection + defaultAttackProfile = "WIRE"; + attackProfiles[] = {"WIRE"}; + }; + }; + + class GVAR(HOT2): GVAR(HOT1) { + submunitionAmmo = QGVAR(ammo_Penetrator_HOT2); + displayName = CSTRING(hot2); + displayNameShort = CSTRING(hot2); + + class ace_missileguidance: ace_missileguidance { + enabled = 1; + }; + }; + + class GVAR(HOT2MP): GVAR(HOT2) { + aiAmmoUsageFlags = "64+128"; + submunitionAmmo = ""; + warheadName = "HE"; + allowAgainstInfantry = 1; + hit = 200; + indirectHit = 200; + indirectHitRange = 5; + explosionEffects = "BombExplosion"; + explosive = 0.7; + + EGVAR(frag,enabled) = 1; + EGVAR(frag,metal) = 7100; // 1000 steel balls + EGVAR(frag,charge) = 4100; + EGVAR(frag,gurney_c) = 2700; + EGVAR(frag,gurney_k) = 3/5; + EGVAR(frag,classes)[] = {"ACE_frag_small"}; + + displayName = CSTRING(hot2mp); + displayNameShort = CSTRING(hot2mp); + description = CSTRING(missileType_Description_AP); + + class ace_missileguidance: ace_missileguidance { + enabled = 1; + }; + }; + + class GVAR(HOT3): GVAR(HOT2) { + submunitionAmmo = QGVAR(ammo_Penetrator_HOT3); + warheadName = "TandemHEAT"; + displayName = CSTRING(hot3); + displayNameShort = CSTRING(hot3); + + class ace_missileguidance: ace_missileguidance { + enabled = 1; + seekerMaxRange = 4300; + }; + }; +}; + diff --git a/addons/hot/CfgEventHandlers.hpp b/addons/hot/CfgEventHandlers.hpp new file mode 100644 index 0000000000..755e0552c5 --- /dev/null +++ b/addons/hot/CfgEventHandlers.hpp @@ -0,0 +1,12 @@ +class Extended_PreStart_EventHandlers { + class ADDON { + init = QUOTE(call COMPILE_FILE(XEH_preStart)); + }; +}; + +class Extended_PreInit_EventHandlers { + class ADDON { + init = QUOTE(call COMPILE_FILE(XEH_preInit)); + }; +}; + diff --git a/addons/hot/CfgMagazines.hpp b/addons/hot/CfgMagazines.hpp new file mode 100644 index 0000000000..ab51548f7b --- /dev/null +++ b/addons/hot/CfgMagazines.hpp @@ -0,0 +1,205 @@ +class CfgMagazines { + class 12Rnd_PG_missiles; + + // HOT1 - HEAT (anti-tank) + class GVAR(1_6Rnd): 12Rnd_PG_missiles { // Old style vehicle magazine + count = 6; + initSpeed = 100; + ammo = QGVAR(HOT1); + displayName = CSTRING(hot1); + displayNameShort = CSTRING(hot1); + descriptionShort = CSTRING(missileType); + }; + + class GVAR(1_2Rnd): GVAR(1_6Rnd) { + count = 2; + }; + + // 1.70 pylon magazines: + class GVAR(1_PylonMissile_1Rnd): GVAR(1_6Rnd) { // Bare missle + displayName = CSTRING(hot1_1); + count = 1; + mass = 70; + pylonWeapon = QGVAR(1_launcher); + hardpoints[] = {"SCALPEL_1RND"}; + model = "\A3\Weapons_F\DynamicLoadout\PylonMissile_1x_Bomb_04_F.p3d"; + }; + class GVAR(1_PylonRack_1Rnd): GVAR(1_6Rnd) { // 1x Launcher Support Rack + displayName = CSTRING(hot1_1); + count = 1; + mass = 85; + pylonWeapon = QGVAR(1_launcher); + hardpoints[] = {"B_MISSILE_PYLON", "SCALPEL_1RND_EJECTOR", "B_ASRRAM_EJECTOR", "UNI_SCALPEL", "CUP_NATO_HELO_SMALL", "CUP_NATO_HELO_LARGE", "RHS_HP_MELB"}; + model = "\A3\Weapons_F\DynamicLoadout\PylonPod_1x_Missile_AA_04_F.p3d"; + }; + class GVAR(1_PylonRack_3Rnd): GVAR(1_6Rnd) { // 3x Launcher Support Rack + displayName = CSTRING(hot1_3); + count = 3; + mass = 250; + pylonWeapon = QGVAR(1_launcher); + hardpoints[] = {"B_MISSILE_PYLON", "UNI_SCALPEL", "CUP_NATO_HELO_LARGE", "RHS_HP_LONGBOW_RACK"}; + model = "\A3\Weapons_F\DynamicLoadout\PylonPod_3x_Missile_LG_scalpel_F.p3d"; + mirrorMissilesIndexes[] = {2, 1, 3}; + }; + class GVAR(1_PylonRack_4Rnd): GVAR(1_6Rnd) { // 4x Launcher Support Rack + displayName = CSTRING(hot1_4); + count = 4; + mass = 340; + pylonWeapon = QGVAR(1_launcher); + hardpoints[] = {"UNI_SCALPEL", "CUP_NATO_HELO_LARGE", "RHS_HP_HELLFIRE_RACK", "RHS_HP_LONGBOW_RACK"}; + model = "\A3\Weapons_F\DynamicLoadout\PylonPod_4x_Missile_LG_scalpel_F.p3d"; + mirrorMissilesIndexes[] = {2, 1, 4, 3}; + }; + + // HOT2 - HEAT (anti-tank) + class GVAR(2_6Rnd): 12Rnd_PG_missiles { // Old style vehicle magazine + count = 6; + initSpeed = 100; + ammo = QGVAR(HOT2); + displayName = CSTRING(hot2); + displayNameShort = CSTRING(hot2); + descriptionShort = CSTRING(missileType); + }; + + class GVAR(2_2Rnd): GVAR(2_6Rnd) { + count = 2; + }; + + // 1.70 pylon magazines: + class GVAR(2_PylonMissile_1Rnd): GVAR(2_6Rnd) { // Bare missle + displayName = CSTRING(hot2_1); + pylonWeapon = QGVAR(2_launcher); + count = 1; + mass = 70; + hardpoints[] = {"SCALPEL_1RND"}; + model = "\A3\Weapons_F\DynamicLoadout\PylonMissile_1x_Bomb_04_F.p3d"; + }; + class GVAR(2_PylonRack_1Rnd): GVAR(2_6Rnd) { // 1x Launcher Support Rack + displayName = CSTRING(hot2_1); + pylonWeapon = QGVAR(2_launcher); + count = 1; + mass = 85; + hardpoints[] = {"B_MISSILE_PYLON", "SCALPEL_1RND_EJECTOR", "B_ASRRAM_EJECTOR", "UNI_SCALPEL", "CUP_NATO_HELO_SMALL", "CUP_NATO_HELO_LARGE", "RHS_HP_MELB"}; + model = "\A3\Weapons_F\DynamicLoadout\PylonPod_1x_Missile_AA_04_F.p3d"; + }; + class GVAR(2_PylonRack_3Rnd): GVAR(2_6Rnd) { // 3x Launcher Support Rack + displayName = CSTRING(hot2_3); + pylonWeapon = QGVAR(2_launcher); + count = 3; + mass = 250; + hardpoints[] = {"B_MISSILE_PYLON", "UNI_SCALPEL", "CUP_NATO_HELO_LARGE", "RHS_HP_LONGBOW_RACK"}; + model = "\A3\Weapons_F\DynamicLoadout\PylonPod_3x_Missile_LG_scalpel_F.p3d"; + mirrorMissilesIndexes[] = {2, 1, 3}; + }; + class GVAR(2_PylonRack_4Rnd): GVAR(2_6Rnd) { // 4x Launcher Support Rack + displayName = CSTRING(hot2_4); + pylonWeapon = QGVAR(2_launcher); + count = 4; + mass = 340; + hardpoints[] = {"UNI_SCALPEL", "CUP_NATO_HELO_LARGE", "RHS_HP_HELLFIRE_RACK", "RHS_HP_LONGBOW_RACK"}; + model = "\A3\Weapons_F\DynamicLoadout\PylonPod_4x_Missile_LG_scalpel_F.p3d"; + mirrorMissilesIndexes[] = {2, 1, 4, 3}; + }; + + // HOT2MP - HE Anti-Infantry + class GVAR(2MP_6Rnd): 12Rnd_PG_missiles { // Old style vehicle magazine + count = 6; + initSpeed = 100; + ammo = QGVAR(HOT2MP); + displayName = CSTRING(hot2mp); + displayNameShort = CSTRING(hot2mp); + descriptionShort = CSTRING(missileType); + }; + + class GVAR(2MP_2Rnd): GVAR(2MP_6Rnd) { + count = 2; + }; + + // 1.70 pylon magazines: + class GVAR(2MP_PylonMissile_1Rnd): GVAR(2MP_6Rnd) { // Bare missle + displayName = CSTRING(hot2mp_1); + pylonWeapon = QGVAR(2mp_launcher); + count = 1; + mass = 70; + hardpoints[] = {"SCALPEL_1RND"}; + model = "\A3\Weapons_F\DynamicLoadout\PylonMissile_1x_Bomb_04_F.p3d"; + }; + class GVAR(2MP_PylonRack_1Rnd): GVAR(2MP_6Rnd) { // 1x Launcher Support Rack + displayName = CSTRING(hot2mp_1); + pylonWeapon = QGVAR(2mp_launcher); + count = 1; + mass = 85; + hardpoints[] = {"B_MISSILE_PYLON", "SCALPEL_1RND_EJECTOR", "B_ASRRAM_EJECTOR", "UNI_SCALPEL", "CUP_NATO_HELO_SMALL", "CUP_NATO_HELO_LARGE", "RHS_HP_MELB"}; + model = "\A3\Weapons_F\DynamicLoadout\PylonPod_1x_Missile_AA_04_F.p3d"; + }; + class GVAR(2MP_PylonRack_3Rnd): GVAR(2MP_6Rnd) { // 3x Launcher Support Rack + displayName = CSTRING(hot2mp_3); + pylonWeapon = QGVAR(2mp_launcher); + count = 3; + mass = 250; + hardpoints[] = {"B_MISSILE_PYLON", "UNI_SCALPEL", "CUP_NATO_HELO_LARGE", "RHS_HP_LONGBOW_RACK"}; + model = "\A3\Weapons_F\DynamicLoadout\PylonPod_3x_Missile_LG_scalpel_F.p3d"; + mirrorMissilesIndexes[] = {2, 1, 3}; + }; + class GVAR(2MP_PylonRack_4Rnd): GVAR(2MP_6Rnd) { // 4x Launcher Support Rack + displayName = CSTRING(hot2mp_4); + pylonWeapon = QGVAR(2mp_launcher); + count = 4; + mass = 340; + hardpoints[] = {"UNI_SCALPEL", "CUP_NATO_HELO_LARGE", "RHS_HP_HELLFIRE_RACK", "RHS_HP_LONGBOW_RACK"}; + model = "\A3\Weapons_F\DynamicLoadout\PylonPod_4x_Missile_LG_scalpel_F.p3d"; + mirrorMissilesIndexes[] = {2, 1, 4, 3}; + }; + + // HOT3 - tandem shaped charge HEAT (anti-tank) + class GVAR(3_6Rnd): 12Rnd_PG_missiles { // Old style vehicle magazine + count = 6; + initSpeed = 100; + ammo = QGVAR(HOT3); + displayName = CSTRING(hot3); + displayNameShort = CSTRING(hot3); + descriptionShort = CSTRING(missileType); + }; + + class GVAR(3_2Rnd): GVAR(3_6Rnd) { // Old style vehicle magazine + count = 2; + }; + + // 1.70 pylon magazines: + class GVAR(3_PylonMissile_1Rnd): GVAR(3_6Rnd) { // Bare missle + displayName = CSTRING(hot3_1); + pylonWeapon = QGVAR(3_launcher); + count = 1; + mass = 70; + hardpoints[] = {"SCALPEL_1RND"}; + model = "\A3\Weapons_F\DynamicLoadout\PylonMissile_1x_Bomb_04_F.p3d"; + }; + + class GVAR(3_PylonRack_1Rnd): GVAR(3_6Rnd) { // 1x Launcher Support Rack + displayName = CSTRING(hot3_1); + pylonWeapon = QGVAR(3_launcher); + count = 1; + mass = 85; + hardpoints[] = {"B_MISSILE_PYLON", "SCALPEL_1RND_EJECTOR", "B_ASRRAM_EJECTOR", "UNI_SCALPEL", "CUP_NATO_HELO_SMALL", "CUP_NATO_HELO_LARGE", "RHS_HP_MELB"}; + model = "\A3\Weapons_F\DynamicLoadout\PylonPod_1x_Missile_AA_04_F.p3d"; + }; + class GVAR(3_PylonRack_3Rnd): GVAR(3_6Rnd) { // 3x Launcher Support Rack + displayName = CSTRING(hot3_3); + pylonWeapon = QGVAR(3_launcher); + count = 3; + mass = 250; + hardpoints[] = {"B_MISSILE_PYLON", "UNI_SCALPEL", "CUP_NATO_HELO_LARGE", "RHS_HP_LONGBOW_RACK"}; + model = "\A3\Weapons_F\DynamicLoadout\PylonPod_3x_Missile_LG_scalpel_F.p3d"; + mirrorMissilesIndexes[] = {2, 1, 3}; + }; + class GVAR(3_PylonRack_4Rnd): GVAR(3_6Rnd) { // 4x Launcher Support Rack + displayName = CSTRING(hot3_4); + pylonWeapon = QGVAR(3_launcher); + count = 4; + mass = 340; + hardpoints[] = {"UNI_SCALPEL", "CUP_NATO_HELO_LARGE", "RHS_HP_HELLFIRE_RACK", "RHS_HP_LONGBOW_RACK"}; + model = "\A3\Weapons_F\DynamicLoadout\PylonPod_4x_Missile_LG_scalpel_F.p3d"; + mirrorMissilesIndexes[] = {2, 1, 4, 3}; + }; +}; + diff --git a/addons/hot/CfgVehicles.hpp b/addons/hot/CfgVehicles.hpp new file mode 100644 index 0000000000..db5b8b5262 --- /dev/null +++ b/addons/hot/CfgVehicles.hpp @@ -0,0 +1,19 @@ +class CfgVehicles { + class Tank; + class Tank_F: Tank { + class Turrets; + }; + class LT_01_base_F: Tank_F { + class Turrets: Turrets { + class MainTurret; + }; + }; + class LT_01_AT_base_F: LT_01_base_F { + class Turrets: Turrets { + class MainTurret: MainTurret { + weapons[] = {"SmokeLauncher","HMG_127",QGVAR(generic_launcher)}; + magazines[] = {"SmokeLauncherMag",QGVAR(2_2Rnd),QGVAR(2_2Rnd),QGVAR(2_2Rnd),QGVAR(2MP_2Rnd),"100Rnd_127x99_mag_Tracer_Red","100Rnd_127x99_mag_Tracer_Red","100Rnd_127x99_mag_Tracer_Red","100Rnd_127x99_mag_Tracer_Red"}; + }; + }; + }; +}; diff --git a/addons/hot/CfgWeapons.hpp b/addons/hot/CfgWeapons.hpp new file mode 100644 index 0000000000..1263ef8dea --- /dev/null +++ b/addons/hot/CfgWeapons.hpp @@ -0,0 +1,62 @@ +class CfgWeapons { + class RocketPods; + class MissileLauncher; + class GVAR(1_launcher): RocketPods { + displayName = CSTRING(hot1); + magazines[] = {QGVAR(1_6Rnd), QGVAR(1_2Rnd), QGVAR(1_PylonMissile_1Rnd), QGVAR(1_PylonRack_1Rnd), QGVAR(1_PylonRack_3Rnd), QGVAR(1_PylonRack_4Rnd)}; + initSpeed = 100; + autoFire = 0; + canLock = 0; + weaponLockSystem = 0; + lockingTargetSound[] = {"",0,1}; + lockedTargetSound[] = {"",0,1}; + soundFly[] = {"A3\Sounds_F\weapons\Rockets\rocket_fly_1",1,1.1,700}; + nameSound = "MissileLauncher"; + sounds[] = {"StandardSound"}; + class StandardSound { + begin1[] = {"A3\Sounds_F\weapons\Rockets\missile_1",1.12202,1.3,1000}; + soundBegin[] = {"begin1",1}; + soundsetshot[] = {"RocketsMedium_Shot_SoundSet"}; + }; + cursor = "EmptyCursor"; + cursorAim = "missile"; + showAimCursorInternal = 0; + }; + class GVAR(2_launcher): GVAR(1_launcher) { + displayName = CSTRING(hot2); + magazines[] = {QGVAR(2_6Rnd), QGVAR(2_2Rnd), QGVAR(2_PylonMissile_1Rnd), QGVAR(2_PylonRack_1Rnd), QGVAR(2_PylonRack_3Rnd), QGVAR(2_PylonRack_4Rnd)}; + }; + class GVAR(2mp_launcher): GVAR(1_launcher) { + displayName = CSTRING(hot2mp); + magazines[] = {QGVAR(2MP_6Rnd), QGVAR(2MP_2Rnd), QGVAR(2MP_PylonMissile_1Rnd), QGVAR(2MP_PylonRack_1Rnd), QGVAR(2MP_PylonRack_3Rnd), QGVAR(2MP_PylonRack_4Rnd)}; + }; + class GVAR(3_launcher): GVAR(1_launcher) { + displayName = CSTRING(hot3); + magazines[] = {QGVAR(3_6Rnd), QGVAR(3_2Rnd), QGVAR(3_PylonMissile_1Rnd), QGVAR(3_PylonRack_1Rnd), QGVAR(3_PylonRack_3Rnd), QGVAR(3_PylonRack_4Rnd)}; + }; + class GVAR(generic_launcher): MissileLauncher { + displayName = CSTRING(hotMissile); + magazines[] = {QGVAR(1_6Rnd), QGVAR(1_2Rnd), QGVAR(2_6Rnd), QGVAR(2_2Rnd), QGVAR(2MP_6Rnd), QGVAR(2MP_2Rnd), QGVAR(3_6Rnd), QGVAR(3_2Rnd)}; + initSpeed = 100; + autoFire = 0; + canLock = 0; + weaponLockSystem = 0; + lockingTargetSound[] = {"",0,1}; + lockedTargetSound[] = {"",0,1}; + soundFly[] = {"A3\Sounds_F\weapons\Rockets\rocket_fly_1",1,1.1,700}; + nameSound = "MissileLauncher"; + sounds[] = {"StandardSound"}; + class StandardSound { + begin1[] = {"A3\Sounds_F\weapons\Rockets\missile_1",1.12202,1.3,1000}; + soundBegin[] = {"begin1",1}; + soundsetshot[] = {"RocketsMedium_Shot_SoundSet"}; + }; + cursor = "EmptyCursor"; + cursorAim = "missile"; + showAimCursorInternal = 0; + + autoReload = 1; + magazineReloadTime = 20; + }; +}; + diff --git a/addons/hot/README.md b/addons/hot/README.md new file mode 100644 index 0000000000..217dfc2f6f --- /dev/null +++ b/addons/hot/README.md @@ -0,0 +1,12 @@ +ace_hot +=================== + +Adds HOT1/2/3 Missiles + + +## Maintainers + +The people responsible for merging changes to this component or answering potential questions. + +- [Brandon-TCVM](https://github.com/TheCandianVendingMachine) + diff --git a/addons/hot/XEH_PREP.hpp b/addons/hot/XEH_PREP.hpp new file mode 100644 index 0000000000..5942a97017 --- /dev/null +++ b/addons/hot/XEH_PREP.hpp @@ -0,0 +1,4 @@ +PREP(seekerType_SACLOS); +PREP(attackProfile_WIRE); +PREP(onFired); + diff --git a/addons/hot/XEH_preInit.sqf b/addons/hot/XEH_preInit.sqf new file mode 100644 index 0000000000..29cc0a7f24 --- /dev/null +++ b/addons/hot/XEH_preInit.sqf @@ -0,0 +1,10 @@ +#include "script_component.hpp" + +ADDON = false; + +PREP_RECOMPILE_START; +#include "XEH_PREP.hpp" +PREP_RECOMPILE_END; + +ADDON = true; + diff --git a/addons/hot/XEH_preStart.sqf b/addons/hot/XEH_preStart.sqf new file mode 100644 index 0000000000..76b104a5bc --- /dev/null +++ b/addons/hot/XEH_preStart.sqf @@ -0,0 +1,4 @@ +#include "script_component.hpp" + +#include "XEH_PREP.hpp" + diff --git a/addons/hot/config.cpp b/addons/hot/config.cpp new file mode 100644 index 0000000000..52f9f5ef6e --- /dev/null +++ b/addons/hot/config.cpp @@ -0,0 +1,23 @@ +#include "script_component.hpp" + +class CfgPatches { + class ADDON { + name = COMPONENT_NAME; + units[] = {}; + weapons[] = {}; + requiredVersion = REQUIRED_VERSION; + requiredAddons[] = {"ace_common","ace_missileguidance"}; + author = ECSTRING(common,ACETeam); + authors[] = {"Brandon (TCVM)"}; + url = ECSTRING(main,URL); + VERSION_CONFIG; + }; +}; + +#include "ACE_GuidanceConfig.hpp" +#include "CfgEventHandlers.hpp" +#include "CfgAmmo.hpp" +#include "CfgMagazines.hpp" +#include "CfgWeapons.hpp" +#include "CfgVehicles.hpp" + diff --git a/addons/hot/functions/fnc_attackProfile_WIRE.sqf b/addons/hot/functions/fnc_attackProfile_WIRE.sqf new file mode 100644 index 0000000000..8aef8a702b --- /dev/null +++ b/addons/hot/functions/fnc_attackProfile_WIRE.sqf @@ -0,0 +1,60 @@ +#include "script_component.hpp" +/* + * Author: Brandon (TCVM) + * Attack profile: Wire guided + * + * Arguments: + * 0: Seeker Target PosASL + * 1: Guidance Arg Array + * 2: Attack Profile State + * + * Return Value: + * Missile Aim PosASL + * + * Example: + * [[1,2,3], [], []] call ace_hot_fnc_attackProfile_WIRE; + * + * Public: No + */ +params ["_seekerTargetPos", "_args", "_attackProfileStateParams"]; +_args params ["_firedEH"]; +_firedEH params ["_shooter","","","","","","_projectile"]; +_attackProfileStateParams params["_maxCorrectableDistance", "_wireCut", "_randomVector", "_crosshairOffset", "_seekerMaxRangeSqr", "_wireCutSource"]; + +private _projectilePos = getPosASL _projectile; + +if ((((getPosASL _shooter) vectorDistanceSqr _projectilePos) > _seekerMaxRangeSqr) || { _wireCut }) exitWith { + // wire snap, random direction + if (_randomVector isEqualTo [0, 0, 0]) then { + _randomVector = RANDOM_VECTOR_3D vectorMultiply 300; + _attackProfileStateParams set [1, true]; + _attackProfileStateParams set [2, _randomVector]; + + playSound3D ["a3\sounds_f\air\sfx\SL_rope_break.wss", objNull, false, AGLtoASL (_shooter modelToWorld _wireCutSource), 150, 1, 25]; + }; + _projectilePos vectorAdd _randomVector +}; + +if (_seekerTargetPos isEqualTo [0, 0, 0]) exitWith { + // cut wire if its caught on terrain + /*if !(lineIntersectsSurfaces [getPosASL _shooter, _projectilePos, _shooter] isEqualTo []) then { + _attackProfileStateParams set [1, true]; + };*/ + // return position 50m infront of projectile + _projectilePos vectorAdd (_projectile vectorModelToWorld [0, 50, 0]) +}; + +private _relativeCorrection = _projectile vectorWorldToModel (_projectilePos vectorDiff _seekerTargetPos); +_relativeCorrection = _relativeCorrection vectorDiff _crosshairOffset; + +private _magnitude = vectorMagnitude [_relativeCorrection select 0, 0, _relativeCorrection select 2]; + +private _fovImpulse = 1 min (_magnitude / _maxCorrectableDistance); // the simulated impulse for the missile being close to the center of the crosshair + +// Adjust the impulse due to near-zero values creating wobbly missiles? +private _correction = _fovImpulse; + +_relativeCorrection = (vectorNormalized _relativeCorrection) vectorMultiply _correction; + +_projectilePos vectorDiff (_projectile vectorModelToWorld _relativeCorrection); + diff --git a/addons/hot/functions/fnc_onFired.sqf b/addons/hot/functions/fnc_onFired.sqf new file mode 100644 index 0000000000..38599f9b6a --- /dev/null +++ b/addons/hot/functions/fnc_onFired.sqf @@ -0,0 +1,53 @@ +#include "script_component.hpp" +/* + * Author: Brandon (TCVM) + * Sets up missile guidance state arrays (called from missileGuidance's onFired). + * + * Arguments: + * Guidance Arg Array + * + * Return Value: + * None + * + * Example: + * [] call ace_hot_fnc_onFired + * + * Public: No + */ +params ["_firedEH", "", "", "_seekerParams", "_stateParams"]; +_firedEH params ["_shooter","_weapon","","","","","_projectile", "_gunner"]; +_stateParams params ["", "_seekerStateParams", "_attackProfileStateParams"]; +_seekerParams params ["", "", "_seekerMaxRange"]; + +private _config = ([_projectile] call CBA_fnc_getObjectConfig) >> "ace_missileguidance"; +private _maxCorrectableDistance = [_config >> "correctionDistance", "NUMBER", DEFAULT_CORRECTION_DISTANCE] call CBA_fnc_getConfigEntry; +private _crosshairOffset = [_config >> "offsetFromCrosshair", "ARRAY", [0, 0, 0]] call CBA_fnc_getConfigEntry; +private _maxDistanceSqr = _seekerMaxRange * _seekerMaxRange; +private _distanceAheadOfMissile = [_config >> "missileLeadDistance", "NUMBER", DEFAULT_LEAD_DISTANCE] call CBA_fnc_getConfigEntry; + +// AI don't know how to use the crosshair offset becauze they dum dum +if ((_gunner != ACE_PLAYER) && {_gunner != (ACE_controlledUAV select 1)}) then { + _crosshairOffset = [0, 0, 0]; +}; +if (_shooter isKindOf "Plane") then {WARNING("SACLOS fired from planes unsupported");}; + +private _turretPath = [_shooter, _weapon] call CBA_fnc_turretPathWeapon; +private _turretConfig = [_shooter, _turretPath] call CBA_fnc_getTurret; + +private _wireCutSource = _shooter selectionPosition getText(_turretConfig >> "missileEnd"); + +_attackProfileStateParams set [0, _maxCorrectableDistance]; +_attackProfileStateParams set [1, false]; // _wireCut +_attackProfileStateParams set [2, [0, 0, 0]]; // _randomVector +_attackProfileStateParams set [3, _crosshairOffset]; // crosshair offset +_attackProfileStateParams set [4, _maxDistanceSqr]; // max distance squared used for wire cut +_attackProfileStateParams set [5, _wireCutSource]; + +private _memoryPointGunnerOptics = getText(_turretConfig >> "memoryPointGunnerOptics"); +private _animationSourceBody = getText(_turretConfig >> "animationSourceBody"); +private _animationSourceGun = getText(_turretConfig >> "animationSourceGun"); +_seekerStateParams set [0, _memoryPointGunnerOptics]; +_seekerStateParams set [1, _animationSourceBody]; +_seekerStateParams set [2, _animationSourceGun]; +_seekerStateParams set [3, _distanceAheadOfMissile]; + diff --git a/addons/hot/functions/fnc_seekerType_SACLOS.sqf b/addons/hot/functions/fnc_seekerType_SACLOS.sqf new file mode 100644 index 0000000000..89ee38b696 --- /dev/null +++ b/addons/hot/functions/fnc_seekerType_SACLOS.sqf @@ -0,0 +1,50 @@ +#include "script_component.hpp" +/* + * Author: Brandon (TCVM) + * SACLOS seeker + * + * Arguments: + * 1: Guidance Arg Array + * 2: Seeker State + * + * Return Value: + * Position of wanted missile pos relative to the camera direction + * + * Example: + * [] call ace_hot_fnc_seekerType_SACLOS + * + * Public: No + */ +params ["", "_args"]; +_args params ["_firedEH", "", "", "_seekerParams", "_stateParams"]; +_firedEH params ["_shooter","_weapon","","","","","_projectile"]; +_seekerParams params ["_seekerAngle"]; +_stateParams params ["", "_seekerStateParams"]; +_seekerStateParams params ["_memoryPointGunnerOptics", "_animationSourceBody", "_animationSourceGun", "_distanceAheadOfMissile"]; + +private _shooterPos = AGLToASL (_shooter modelToWorld(_shooter selectionPosition _memoryPointGunnerOptics)); +private _projPos = getPosASL _projectile; + +private _lookDirection = if !(_shooter isKindOf "CAManBase") then { + private _gBody = -deg(_shooter animationPhase _animationSourceBody); + private _gGun = deg(_shooter animationPhase _animationSourceGun); + + _shooter vectorModelToWorld ([1, _gBody, _gGun] call CBA_fnc_polar2vect); +} else { + _shooterPos = eyePos _shooter; + _shooter weaponDirection _weapon +}; + +private _distanceToProj = _shooterPos vectorDistance _projPos; +private _testPointVector = vectorNormalized (_projPos vectorDiff _shooterPos); +private _testDotProduct = (_lookDirection vectorDotProduct _testPointVector); + +private _testIntersections = lineIntersectsSurfaces [_shooterPos, _projPos, _shooter]; + +if ((_testDotProduct < (cos _seekerAngle)) || { !(_testIntersections isEqualTo []) }) exitWith { + // out of LOS of seeker + [0, 0, 0] +}; + +_shooterPos vectorAdd (_lookDirection vectorMultiply (_distanceToProj + _distanceAheadOfMissile)); + diff --git a/addons/hot/functions/script_component.hpp b/addons/hot/functions/script_component.hpp new file mode 100644 index 0000000000..fbebb0b9a3 --- /dev/null +++ b/addons/hot/functions/script_component.hpp @@ -0,0 +1,2 @@ +#include "\z\ace\addons\hot\script_component.hpp" + diff --git a/addons/hot/script_component.hpp b/addons/hot/script_component.hpp new file mode 100644 index 0000000000..fd5475f15f --- /dev/null +++ b/addons/hot/script_component.hpp @@ -0,0 +1,28 @@ +#define COMPONENT hot +#define COMPONENT_BEAUTIFIED HOT +#include "\z\ace\addons\main\script_mod.hpp" + +// #define DEBUG_MODE_FULL +// #define DISABLE_COMPILE_CACHE +// #define ENABLE_PERFORMANCE_COUNTERS + +#ifdef DEBUG_ENABLED_HOT + #define DEBUG_MODE_FULL +#endif + +#ifdef DEBUG_SETTINGS_HOT + #define DEBUG_SETTINGS DEBUG_SETTINGS_HOT +#endif + +#include "\z\ace\addons\main\script_macros.hpp" + +#define RANDOM_VECTOR_3D (call {\ + private _z = random 2 - 1;\ + private _r = sqrt (1 - _z^2);\ + private _theta = random 360;\ + [_r * cos _theta, _r * sin _theta, _z]\ +}) + +#define DEFAULT_CORRECTION_DISTANCE 10 +#define DEFAULT_LEAD_DISTANCE 50 + diff --git a/addons/hot/stringtable.xml b/addons/hot/stringtable.xml new file mode 100644 index 0000000000..07744dd2d0 --- /dev/null +++ b/addons/hot/stringtable.xml @@ -0,0 +1,71 @@ + + + + + Wire-Guided + Drahtgelenkt + + + Semi-automatic command to line of sight + halbautomatische Steuerung über Sichtverbindung (SACLOS) + + + Wire-Guided Missile + Drahtgelenkte Rakete + + + HOT Missile + + + HOT 1 + + + HOT 2 + + + HOT 2MP + + + HOT 3 + + + Wire-Guided Missile (Anti-Personnel) + + + 1x HOT 1 [ACE] + + + 3x HOT 1 [ACE] + + + 4x HOT 1 [ACE] + + + 1x HOT 2 [ACE] + + + 3x HOT 2 [ACE] + + + 4x HOT 2 [ACE] + + + 1x HOT 2MP [ACE] + + + 3x HOT 2MP [ACE] + + + 4x HOT 2MP [ACE] + + + 1x HOT 3 [ACE] + + + 4x HOT 3 [ACE] + + + 3x HOT 3 [ACE] + + + diff --git a/docs/wiki/feature/hot.md b/docs/wiki/feature/hot.md new file mode 100644 index 0000000000..30a512342f --- /dev/null +++ b/docs/wiki/feature/hot.md @@ -0,0 +1,66 @@ +--- +layout: wiki +title: HOT +description: HOT 1/2/3 Missiles +group: feature +category: equipment +parent: wiki +mod: ace +version: + major: 3 + minor: 13 + patch: 0 +--- + +## 1. Overview + +### 1.1 Guidance + +HOT missile is a wire-guided SACLOS weapon. It requires the shooter to provide constant aiming in order to hit its target. + +### 1.2 Attack profiles + +The missile requires line of sight to the target at all times. Keep your crosshair on the target as the missile cruises toward it until impact. +If you lose line of sight the missile will stop tracking toward your crosshair and will fly on its current trajectory. You can regain control if the missile re-enters line of sight. +If the missile exceeds its maximum range the wire will snap and it will fly off in a random direction. + +### 1.3 Missile Types + +There are 4 HOT missiles included +- HOT 1: Able to penetrate around 800mm RHA. Max range is 4000 meters. HEAT +- HOT 2: Able to penetrate around 900mm RHA. Max range is 4000 meters. HEAT +- HOT 2MP: Anti-Infantry. Contains 1000 steel balls that scatter on impact. Also contains a chemical incendiary device which has a kill radius of around 20 meters. Able to penetrate around 350mm RHA. Max range is 4000 meters. HE +- HOT 3: Able to penetrate around 1250mm RHA. Max range is 4300 meters. Tandem HEAT + +## 2. Usage + +- Select the HOT launcher and aim toward your target. Once you fire the weapon you must keep your crosshair on the target until impact. Note: The HOT missile sits 0.5 meters above the crosshair +- If the missile exceeds 4000 meters (4300 meters for the HOT 3) the wire will snap and you will lose control over the missile. +- If the missile is 30 degrees outside of your current crosshair position the seeker will not be able to find the missile and you will lose control over it. + +## 3 Adding to vehicles + +- Easiest way to add is via the 1.70 Pylons system. +- HOT missiles can also be added to other vehicles via config or script. + +### 3.1 Classnames + +- Weapons: `ace_hot_1_launcher`, `2_launcher`, `ace_hot_2mp_launcher`, `ace_hot_3_launcher`, `ace_hot_generic_launcher` +- Magazines: `ace_hot_1_6Rnd`, `ace_hot_1_2Rnd`, `ace_hot_2_6Rnd`, `ace_hot_2_2Rnd`, `ace_hot_2mp_6Rnd`, `ace_hot_2mp_2Rnd`, `ace_hot_3_6Rnd`, `ace_hot_3_2Rnd` +- Pylon Magazines: `ace_hot_1_PylonMissile_1Rnd`, `ace_hot_1_PylonRack_1Rnd`, `ace_hot_1_PylonRack_3Rnd`, `ace_hot_1_PylonRack_4Rnd`, `ace_hot_2_PylonMissile_1Rnd`, `ace_hot_2_PylonRack_1Rnd`, `ace_hot_2_PylonRack_3Rnd`, `ace_hot_2_PylonRack_4Rnd`, `ace_hot_2mp_PylonMissile_1Rnd`, `ace_hot_2mp_PylonRack_1Rnd`, `ace_hot_2mp_PylonRack_3Rnd`, `ace_hot_2mp_PylonRack_4Rnd`, `ace_hot_3_PylonMissile_1Rnd`, `ace_hot_3_PylonRack_1Rnd`, `ace_hot_3_PylonRack_3Rnd`, `ace_hot_3_PylonRack_4Rnd` + +### 3.2 Script Example + +- Adding HOT to e.g. a Cessna Civilian Plane: + +```cpp +if (local this) then { + this addWeaponTurret ["ace_hot_generic_launcher", [-1]]; + this addMagazineTurret ["ace_hot_2mp_6Rnd", [-1]]; + this addMagazineTurret ["ace_hot_3_6Rnd", [-1]]; +}; +``` + +## 4. Dependencies + +{% include dependencies_list.md component="hot" %}