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" %}