diff --git a/addons/dragon/$PBOPREFIX$ b/addons/dragon/$PBOPREFIX$ new file mode 100644 index 0000000000..41f191d39b --- /dev/null +++ b/addons/dragon/$PBOPREFIX$ @@ -0,0 +1 @@ +z\ace\addons\dragon \ No newline at end of file diff --git a/addons/dragon/ACE_GuidanceConfig.hpp b/addons/dragon/ACE_GuidanceConfig.hpp new file mode 100644 index 0000000000..18cc5dd9fe --- /dev/null +++ b/addons/dragon/ACE_GuidanceConfig.hpp @@ -0,0 +1,11 @@ +class EGVAR(missileguidance,AttackProfiles) { + class DRAGON { + name = CSTRING(dragonName); + visualName = CSTRING(dragonName); + description = CSTRING(dragonName); + + onFired = QFUNC(onFired); + functionName = QFUNC(attackProfile_DRAGON); + }; +}; + diff --git a/addons/dragon/CfgAmmo.hpp b/addons/dragon/CfgAmmo.hpp new file mode 100644 index 0000000000..faccdfb1cd --- /dev/null +++ b/addons/dragon/CfgAmmo.hpp @@ -0,0 +1,147 @@ +class CfgAmmo { + class ammo_Penetrator_Base; + class M_Scalpel_AT; + class Rocket_03_AP_F; + + class GVAR(penetrator_super): ammo_Penetrator_Base { + caliber = 60; + warheadName = "HEAT"; + hit = 460; + fuseDistance = 75; + }; + + class GVAR(dragonBase): Rocket_03_AP_F { + EGVAR(frag,skip) = 1; + scope = 1; + aiAmmoUsageFlags = "128+512"; + model = QPATHTOF(models\dragon.p3d); + maxSpeed = 200; + thrust = 300; + initTime = 0.151; + thrustTime = 0; + sideAirFriction = 0.05; + effectsMissile = "missile2"; + effectFlare = ""; + airFriction = 0.5; + fuseDistance = 75; + whistleDist = 2; + + hit = 100; + indirectHit = 9; + indirectHitRange = 1; + explosive = 0.8; + timeToLive = 60; + cost = 500; + simulationStep = 0.005; + maxControlRange = 1500; + + class ace_missileguidance { + minDeflection = 0; + maxDeflection = 0; + incDeflection = 0; + + canVanillaLock = 0; + + // Guidance type for munitions + defaultSeekerType = "SACLOS"; + seekerTypes[] = { "SACLOS" }; + + defaultSeekerLockMode = "LOAL"; + seekerLockModes[] = { "LOAL", "LOBL" }; + + seekLastTargetPos = 0; + seekerAngle = 30; + seekerAccuracy = 1; + + seekerMinRange = 65; + seekerMaxRange = 1000; + + correctionDistance = 30; + missileLeadDistance = 0; + offsetFromCrosshair[] = { 0, 0, 0 }; + + serviceInterval = 0.33; // how many seconds between pops + serviceCharges = 32; // how many charges are in this missile + serviceChargeAcceleration = 6.5; + dragonSpeed = 100; // meters per second + + defaultAttackProfile = "DRAGON"; + attackProfiles[] = {"DRAGON"}; + }; + }; + + class GVAR(super) : GVAR(dragonBase) { + submunitionAmmo = QGVAR(penetrator_super); + submunitionDirectionType = "SubmunitionModelDirection"; + submunitionInitSpeed = 1000; + submunitionParentSpeedCoef = 0; + submunitionInitialOffset[] = { 0, 0, -0.2 }; + + class ace_missileguidance { + enabled = 1; + + // Guidance type for munitions + defaultSeekerType = "SACLOS"; + seekerTypes[] = { "SACLOS" }; + + defaultSeekerLockMode = "LOAL"; + seekerLockModes[] = { "LOAL", "LOBL" }; + + seekLastTargetPos = 0; + seekerAngle = 30; + seekerAccuracy = 1; + + seekerMinRange = 30; + seekerMaxRange = 1500; + + correctionDistance = 30; + missileLeadDistance = 0; + + serviceInterval = 0.33; // how many seconds between pops + serviceCharges = 60; // how many charges are in this missile + serviceChargeAcceleration = 6.5; + dragonSpeed = 100; // meters per second + + defaultAttackProfile = "DRAGON"; + attackProfiles[] = {"DRAGON"}; + }; + }; + + class ShellBase; + class GVAR(serviceCharge) : ShellBase { + hit = 1; + indirectHit = 2; + indirectHitRange = 1; + typicalSpeed = 100; + explosive = 1; + cost = 300; + model = "\A3\Weapons_F\empty.p3d"; + airFriction = 0; + timeToLive = 1; + explosionTime = 0.001; + soundFly[] = {"",1,1}; + soundEngine[] = {"",1,4}; + CraterEffects = ""; + explosionEffects = QGVAR(serviceExplosion); + hitarmor[] = {"soundDefault1", 1}; + hitbuilding[] = {"soundDefault1", 1}; + hitconcrete[] = {"soundDefault1", 1}; + hitdefault[] = {"soundDefault1", 1}; + hitfoliage[] = {"soundDefault1", 1}; + hitglass[] = {"soundDefault1", 1}; + hitglassarmored[] = {"soundDefault1", 1}; + hitgroundhard[] = {"soundDefault1", 1}; + hitgroundsoft[] = {"soundDefault1", 1}; + hitiron[] = {"soundDefault1", 1}; + hitman[] = {"soundDefault1", 1}; + hitmetal[] = {"soundDefault1", 1}; + hitmetalplate[] = {"soundDefault1", 1}; + hitplastic[] = {"soundDefault1", 1}; + hitrubber[] = {"soundDefault1", 1}; + hitwood[] = {"soundDefault1", 1}; + sounddefault1[] = {QPATHTOF(sounds\service_charge.wss), 56.2341, 1, 1800}; + soundHit[] = {QPATHTOF(sounds\service_charge.wss),56.23413,1,1800}; + multiSoundHit[] = {"soundDefault1", 1}; + }; +}; + diff --git a/addons/dragon/CfgEventHandlers.hpp b/addons/dragon/CfgEventHandlers.hpp new file mode 100644 index 0000000000..0d3301d6e0 --- /dev/null +++ b/addons/dragon/CfgEventHandlers.hpp @@ -0,0 +1,17 @@ +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)); + }; +}; + +class Extended_PostInit_EventHandlers { + class ADDON { + init = QUOTE(call COMPILE_FILE(XEH_postInit)); + }; +}; diff --git a/addons/dragon/CfgMagazines.hpp b/addons/dragon/CfgMagazines.hpp new file mode 100644 index 0000000000..e4b59a9304 --- /dev/null +++ b/addons/dragon/CfgMagazines.hpp @@ -0,0 +1,16 @@ +class CfgMagazines { + class 1Rnd_GAA_missiles; + class GVAR(super) : 1Rnd_GAA_missiles { + sound[] = {}; + soundFly[] = {}; + soundHit[] = {}; + model = QPATHTOF(models\dragon.p3d); + ammo = QGVAR(super); + initSpeed = 120; + scope = 1; + displayName = CSTRING(dragonName); + displayNameShort = CSTRING(dragonName); + descriptionShort = CSTRING(dragonDescription); + }; +}; + diff --git a/addons/dragon/CfgVehicles.hpp b/addons/dragon/CfgVehicles.hpp new file mode 100644 index 0000000000..c1c690c52e --- /dev/null +++ b/addons/dragon/CfgVehicles.hpp @@ -0,0 +1,149 @@ +class CfgVehicles { + class LandVehicle; + class StaticWeapon: LandVehicle { + class Turrets; + class MainTurret; + class ACE_Actions { + class ACE_MainActions {}; + }; + }; + + class StaticATWeapon: StaticWeapon {}; + + class GVAR(staticBase): StaticATWeapon { + scope = 1; + author = ECSTRING(common,ACETeam); + displayname = CSTRING(dragonName); + side = 1; + faction = "BLU_F"; + crew = "B_soldier_f"; + model = QPATHTOF(models\ace_m47_static.p3d); + picture = "\A3\Static_F_Gamma\data\UI\gear_StaticTurret_AT_CA.paa"; + UiPicture = "\A3\Static_F_Gamma\data\UI\gear_StaticTurret_AT_CA.paa"; + icon = "\A3\Static_F_Gamma\data\UI\map_StaticTurret_AT_CA.paa"; + threat[] = {0.7,1.0,0.1}; + cost = 150000; + class Damage { + tex[] = {}; + mat[] = { + "a3\static_f_gamma\data\staticturret_01.rvmat", + "a3\static_f_gamma\data\staticturret_01_damage.rvmat", + "a3\static_f_gamma\data\staticturret_01_destruct.rvmat", + "a3\static_f_gamma\data\staticturret_02.rvmat", + "a3\static_f_gamma\data\staticturret_02_damage.rvmat", + "a3\static_f_gamma\data\staticturret_02_destruct.rvmat", + "a3\weapons_f_beta\launchers\titan\data\titan_launcher.rvmat", + "a3\weapons_f_beta\launchers\titan\data\titan_launcher_damage.rvmat", + "a3\weapons_f_beta\launchers\titan\data\titan_launcher_destruct.rvmat", + "a3\weapons_f_beta\launchers\titan\data\titan_mtube.rvmat", + "a3\weapons_f_beta\launchers\titan\data\titan_mtube_damage.rvmat", + "a3\weapons_f_beta\launchers\titan\data\titan_mtube_destruct.rvmat" + }; + }; + class Turrets: Turrets { + class MainTurret: MainTurret { + optics = 1; + turretInfoType = "RscWeaponEmpty"; + gunnerOpticsModel = QPATHTOF(models\optics_m47.p3d); + minElev = -30; + maxElev = 20; + weapons[] = { QGVAR(dummyStatic) }; + magazines[] = { QGVAR(super) }; + + gunnerAction = "gunner_static_low01"; + gunnergetInAction = ""; + gunnergetOutAction = ""; + + discreteDistance[] = {}; + discreteDistanceInitIndex = 0; + + displayName = CSTRING(dragonName); + + class ViewOptics { + initAngleX = 0; + minAngleX = -30; + maxAngleX = 30; + initAngleY = 5; + minAngleY = -100; + maxAngleY = 100; + initFov = 0.055; + minFov = 0.055; // 6 degree FOV + maxFov = 0.055; + visionMode[] = {"Normal"}; + thermalMode[] = {0}; + }; + gunnerRightHandAnimName = "OtocHlaven_shake"; + gunnerLeftHandAnimName = "OtocHlaven_shake"; + gunBeg = "spice rakety"; + gunEnd = "konec rakety"; + memoryPointGunnerOptics = "look"; + }; + }; + class AnimationSources { + class rest_rotate { + source="user"; + animPeriod=0.00001; + initPhase=-0.35; + maxValue="3.60"; + minValue="-3.60"; + }; + class optic_hide { + source="user"; + animPeriod=0.0001; + initPhase=1; + maxValue="1"; + minValue="0"; + }; + class missile_hide { + source="user"; + animPeriod=0.0001; + initPhase=0; + maxValue="1"; + minValue="0"; + }; + }; + soundGetOut[] = {"A3\sounds_f\dummysound",0.001,1,5}; + soundGetIn[] = {"A3\sounds_f\dummysound",0.00031622776,1,5}; + armorStructural = 10.0; + + class ACE_CSW { + disassembleTo = QGVAR(super); + }; + + class ACE_Actions: ACE_Actions { + class ACE_MainActions: ACE_MainActions { + displayName = CSTRING(dragonName); + class GVAR(pickUp) { + displayName = ECSTRING(csw,Pickup_displayName); + condition = QUOTE(call FUNC(canPickupTripod)); + statement = QUOTE(call EFUNC(csw,assemble_pickupTripod)); + }; + class GVAR(attachSight) { + displayName = CSTRING(attachSight); + condition = QUOTE(call FUNC(sightCanAttach)); + statement = QUOTE(call FUNC(sightAttach)); + }; + class GVAR(detachSight) { + displayName = CSTRING(detachSight); + condition = QUOTE(call FUNC(sightCanDetach)); + statement = QUOTE(call FUNC(sightDetach)); + }; + }; + }; + + }; + + class GVAR(staticAssembled): GVAR(staticBase) { + scope = 2; + class AnimationSources: AnimationSources { + class optic_hide: optic_hide { + initPhase = 0; + }; + }; + class Turrets: Turrets { + class MainTurret: MainTurret { + weapons[] = { QGVAR(superStatic) }; + }; + }; + }; +}; diff --git a/addons/dragon/CfgWeapons.hpp b/addons/dragon/CfgWeapons.hpp new file mode 100644 index 0000000000..f9c7f262ae --- /dev/null +++ b/addons/dragon/CfgWeapons.hpp @@ -0,0 +1,90 @@ +class CfgWeapons { + class launch_Titan_base; + class Launcher_Base_F; + class missiles_titan; + class Binocular; + class Default; + class missiles_titan_static: missiles_titan { + class WeaponSlotsInfo; + }; + class launch_Titan_F: launch_Titan_base { + class WeaponSlotsInfo; + }; + + class GVAR(super): launch_Titan_F { + model = QPATHTOF(models\ace_m47_magazine.p3d); + picture = QPATHTOF(data\m47_dragon_item_ca.paa); + magazines[] = {}; + displayName = CSTRING(dragonName); + descriptionShort = CSTRING(dragonDescription); + scope = 2; + + class ACE_CSW { + type = "mount"; + deployTime = 2; + pickupTime = 2; + deploy = QGVAR(staticBase); + }; + + class WeaponSlotsInfo: WeaponSlotsInfo { + mass = 253; + }; + modes[] = {}; + }; + + class GVAR(superStatic): missiles_titan_static { + EGVAR(overpressure,angle) = 90; + EGVAR(overpressure,range) = 30; + EGVAR(overpressure,damage) = 0.85; + + initSpeed = 120; + + displayName = CSTRING(dragonName); + descriptionShort = CSTRING(dragonDescription); + magazines[] = { QGVAR(super) }; + }; + + // need a weapon in order to rotate turret + class GVAR(dummyStatic): Default { + cursor = ""; + cursorAim = ""; + scope = 1; + displayName = CSTRING(dragonName); + reloadTime = 0; + canLock = 0; + optics = 0; + enableAttack = 0; + }; + + class GVAR(sight): Binocular { + displayName = CSTRING(sightName); + model = QPATHTOF(models\ace_m47_optic.p3d); + picture = QPATHTOF(data\m47_daysight_item_ca.paa); + optics = 1; + weaponInfoType = "RscWeaponEmpty"; + modelOptics = QPATHTOF(models\optics_m47); + reloadaction = ""; + showSwitchAction = 1; + useAsBinocular = 1; + uipicture = ""; + descriptionShort = CSTRING(sightDescription); + ace_disposable = 0; + magazines[] = {}; + type = 4096; + opticsPPEffects[] = {"OpticsCHAbera1","OpticsBlur1"}; + opticsZoomMin = 0.055; + opticsZoomMax = 0.055; + scope = 2; + class ACE_CSW { + type = "weapon"; + deployTime = 2; + pickupTime = 1; + class assembleTo { + GVAR(super_noSight) = QGVAR(super_sight); + }; + }; + class WeaponSlotsInfo { + mass = 68; + }; + }; +}; diff --git a/addons/dragon/README.md b/addons/dragon/README.md new file mode 100644 index 0000000000..84a82ad820 --- /dev/null +++ b/addons/dragon/README.md @@ -0,0 +1,12 @@ +ace_dragon +=================== + +Adds M47 Dragon Missile. + + +## Maintainers + +The people responsible for merging changes to this component or answering potential questions. + +- [Brandon-TCVM](https://github.com/TheCandianVendingMachine) + diff --git a/addons/dragon/XEH_PREP.hpp b/addons/dragon/XEH_PREP.hpp new file mode 100644 index 0000000000..148dff5cf6 --- /dev/null +++ b/addons/dragon/XEH_PREP.hpp @@ -0,0 +1,7 @@ +PREP(attackProfile_DRAGON); +PREP(canPickupTripod); +PREP(onFired); +PREP(sightAttach); +PREP(sightCanAttach); +PREP(sightCanDetach); +PREP(sightDetach); diff --git a/addons/dragon/XEH_postInit.sqf b/addons/dragon/XEH_postInit.sqf new file mode 100644 index 0000000000..690f392447 --- /dev/null +++ b/addons/dragon/XEH_postInit.sqf @@ -0,0 +1,37 @@ +#include "script_component.hpp" + +[QGVAR(detachSight), LINKFUNC(sightDetach)] call CBA_fnc_addEventHandler; +[QGVAR(attachSight), LINKFUNC(sightAttach)] call CBA_fnc_addEventHandler; + +["vehicle", { + params ["","_vehicle"]; + TRACE_2("vehicle change",_vehicle,typeOf _vehicle); + if (!(_vehicle isKindOf QGVAR(staticBase))) exitWith {}; + + _vehicle animate ["rest_rotate", 0]; + + if (isNil QGVAR(pfID)) then {GVAR(pfID) = -1}; + [GVAR(pfID)] call CBA_fnc_removePerFrameHandler; + + private _lastView = cameraView; + if (!(_lastView in ["INTERNAL", "EXTERNAL"])) then { _lastView == "INTERNAL"; }; + + GVAR(pfID) = [{ + params ["_args"]; + (_this select 0) params ["_vehicle", "_lastView"]; + + if ((!alive _vehicle) || {!alive ACE_player} || {(vehicle ACE_player) != _vehicle}) exitWith { + TRACE_1("exiting PFEH",GVAR(pfID)); + [GVAR(pfID)] call CBA_fnc_removePerFrameHandler; + }; + + if (cameraView in ["INTERNAL", "EXTERNAL"]) then { + _args set [1, cameraView]; + } else { + if ((cameraOn == _vehicle) && {!(_vehicle getVariable [QGVAR(sightAttached), ((typeOf _vehicle) == QGVAR(staticAssembled))])}) then { + _vehicle switchCamera _lastView; + }; + }; + }, 0, [_vehicle, _lastView]] call CBA_fnc_addPerFrameHandler; + TRACE_1("started PFEH",GVAR(pfID)); +}, true] call CBA_fnc_addPlayerEventHandler; diff --git a/addons/dragon/XEH_preInit.sqf b/addons/dragon/XEH_preInit.sqf new file mode 100644 index 0000000000..29cc0a7f24 --- /dev/null +++ b/addons/dragon/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/dragon/XEH_preStart.sqf b/addons/dragon/XEH_preStart.sqf new file mode 100644 index 0000000000..76b104a5bc --- /dev/null +++ b/addons/dragon/XEH_preStart.sqf @@ -0,0 +1,4 @@ +#include "script_component.hpp" + +#include "XEH_PREP.hpp" + diff --git a/addons/dragon/anim/M47.rtm b/addons/dragon/anim/M47.rtm new file mode 100644 index 0000000000..48f3dfc3c4 Binary files /dev/null and b/addons/dragon/anim/M47.rtm differ diff --git a/addons/dragon/anim/model.cfg b/addons/dragon/anim/model.cfg new file mode 100644 index 0000000000..6be7f511b5 --- /dev/null +++ b/addons/dragon/anim/model.cfg @@ -0,0 +1,119 @@ +class cfgSkeletons { + class OFP2_ManSkeleton { + isDiscrete = 0; + skeletonInherit = ""; + skeletonBones[] = { + "Pelvis","", + "Spine","Pelvis", + "Spine1","Spine", + "Spine2","Spine1", + "Spine3","Spine2", + "camera","Pelvis",// case has changed for arma3 + "weapon","Spine1", + "launcher","Spine1", + "Neck","Spine3", + "Neck1","Neck", + "Head","Neck1", //Head skeleton in hierarchy + //Left upper side + "LeftShoulder","Spine3", + "LeftArm","LeftShoulder", + "LeftArmRoll","LeftArm", + "LeftForeArm","LeftArmRoll", + "LeftForeArmRoll","LeftForeArm", + "LeftHand","LeftForeArmRoll", + "LeftHandRing","LeftHand", + "LeftHandRing1","LeftHandRing", + "LeftHandRing2","LeftHandRing1", + "LeftHandRing3","LeftHandRing2", + "LeftHandPinky1","LeftHandRing", + "LeftHandPinky2","LeftHandPinky1", + "LeftHandPinky3","LeftHandPinky2", + "LeftHandMiddle1","LeftHand", + "LeftHandMiddle2","LeftHandMiddle1", + "LeftHandMiddle3","LeftHandMiddle2", + "LeftHandIndex1","LeftHand", + "LeftHandIndex2","LeftHandIndex1", + "LeftHandIndex3","LeftHandIndex2", + "LeftHandThumb1","LeftHand", + "LeftHandThumb2","LeftHandThumb1", + "LeftHandThumb3","LeftHandThumb2", + //Right upper side + "RightShoulder","Spine3", + "RightArm","RightShoulder", + "RightArmRoll","RightArm", + "RightForeArm","RightArmRoll", + "RightForeArmRoll","RightForeArm", + "RightHand","RightForeArmRoll", + "RightHandRing","RightHand", + "RightHandRing1","RightHandRing", + "RightHandRing2","RightHandRing1", + "RightHandRing3","RightHandRing2", + "RightHandPinky1","RightHandRing", + "RightHandPinky2","RightHandPinky1", + "RightHandPinky3","RightHandPinky2", + "RightHandMiddle1","RightHand", + "RightHandMiddle2","RightHandMiddle1", + "RightHandMiddle3","RightHandMiddle2", + "RightHandIndex1","RightHand", + "RightHandIndex2","RightHandIndex1", + "RightHandIndex3","RightHandIndex2", + "RightHandThumb1","RightHand", + "RightHandThumb2","RightHandThumb1", + "RightHandThumb3","RightHandThumb2", + //Left lower side + "LeftUpLeg","Pelvis", + "LeftUpLegRoll","LeftUpLeg", + "LeftLeg","LeftUpLegRoll", + "LeftLegRoll","LeftLeg", + "LeftFoot","LeftLegRoll", + "LeftToeBase","LeftFoot", + //Right lower side + "RightUpLeg","Pelvis", + "RightUpLegRoll","RightUpLeg", + "RightLeg","RightUpLegRoll", + "RightLegRoll","RightLeg", + "RightFoot","RightLegRoll", + "RightToeBase","RightFoot", + //New facial features arma3 only + "Face_Hub","Head", + "Face_Jawbone","Face_Hub", + "Face_Jowl","Face_Jawbone", + "Face_chopRight","Face_Jawbone", + "Face_chopLeft","Face_Jawbone", + "Face_LipLowerMiddle","Face_Jawbone", + "Face_LipLowerLeft","Face_Jawbone", + "Face_LipLowerRight","Face_Jawbone", + "Face_Chin","Face_Jawbone", + "Face_Tongue","Face_Jawbone", + "Face_CornerRight","Face_Hub", + "Face_CheekSideRight","Face_CornerRight", + "Face_CornerLeft","Face_Hub", + "Face_CheekSideLeft","Face_CornerLeft", + "Face_CheekFrontRight","Face_Hub", + "Face_CheekFrontLeft","Face_Hub", + "Face_CheekUpperRight","Face_Hub", + "Face_CheekUpperLeft","Face_Hub", + "Face_LipUpperMiddle","Face_Hub", + "Face_LipUpperRight","Face_Hub", + "Face_LipUpperLeft","Face_Hub", + "Face_NostrilRight","Face_Hub", + "Face_NostrilLeft","Face_Hub", + "Face_Forehead","Face_Hub", + "Face_BrowFrontRight","Face_Forehead", + "Face_BrowFrontLeft","Face_Forehead", + "Face_BrowMiddle","Face_Forehead", + "Face_BrowSideRight","Face_Forehead", + "Face_BrowSideLeft","Face_Forehead", + "Face_Eyelids","Face_Hub", + "Face_EyelidUpperRight","Face_Hub", + "Face_EyelidUpperLeft","Face_Hub", + "Face_EyelidLowerRight","Face_Hub", + "Face_EyelidLowerLeft","Face_Hub", + "EyeLeft","Face_Hub", + "EyeRight","Face_Hub" + };// end of skeleton array + // location of pivot points (local axes) for hierarchical animation + pivotsModel="A3\anims_f\data\skeleton\SkeletonPivots.p3d"; + }; +}; + diff --git a/addons/dragon/config.cpp b/addons/dragon/config.cpp new file mode 100644 index 0000000000..10a1d285db --- /dev/null +++ b/addons/dragon/config.cpp @@ -0,0 +1,42 @@ +#include "script_component.hpp" + +class CfgPatches { + class ADDON { + name = COMPONENT_NAME; + units[] = {}; + weapons[] = {}; + requiredVersion = REQUIRED_VERSION; + requiredAddons[] = {"ace_common","ace_missileguidance","ace_hot","ace_csw"}; + author = ECSTRING(common,ACETeam); + authors[] = {"Brandon (TCVM)"}; + url = ECSTRING(main,URL); + VERSION_CONFIG; + }; +}; + +#include "ACE_GuidanceConfig.hpp" +#include "CfgEventHandlers.hpp" +#include "CfgWeapons.hpp" +#include "CfgMagazines.hpp" +#include "CfgAmmo.hpp" +#include "CfgVehicles.hpp" + +class GVAR(serviceExplosion) { + class Light1 { + simulation = "light"; + type = "GrenadeExploLight"; + position[] = {0,0,0}; + intensity = 0.005; + interval = 1; + lifeTime = 0.5; + }; + class GrenadeSmoke1 { + simulation = "particles"; + type = "ImpactSmoke2"; + position[] = {0,0,0}; + intensity = 0.15; + interval = 0.1; + lifeTime = 0.5; + }; +}; + diff --git a/addons/dragon/data/M47.rvmat b/addons/dragon/data/M47.rvmat new file mode 100644 index 0000000000..3241cd20d7 --- /dev/null +++ b/addons/dragon/data/M47.rvmat @@ -0,0 +1,81 @@ +class StageTI { + texture="z\ace\addons\dragon\data\m47_ti_ca.paa"; +}; +ambient[]={0.958,0.984,1,1}; +diffuse[]={0.958,0.984,1,1}; +forcedDiffuse[]={0,0,0,0}; +emmisive[]={0,0,0,1}; +specular[]={0.153,0.169,0.111,1}; +specularPower=90; +PixelShaderID="Super"; +VertexShaderID="Super"; +class Stage1 { + texture="z\ace\addons\dragon\data\m47_nohq.paa"; + uvSource="tex"; + class uvTransform { + aside[]={1,0,0}; + up[]={0,1,0}; + dir[]={0,0,1}; + pos[]={0,0,0}; + }; +}; +class Stage2 { + texture="a3\weapons_f\Data\DetailMaps\Metal_rough_DT.paa"; + uvSource="tex"; + class uvTransform { + aside[]={1,0,0}; + up[]={0,1,0}; + dir[]={0,0,1}; + pos[]={0,0,0}; + }; +}; +class Stage3 { + texture="#(argb,8,8,3)color(0,0,0,0,MC)"; + uvSource="tex"; + class uvTransform { + aside[]={1,0,0}; + up[]={0,1,0}; + dir[]={0,0,1}; + pos[]={0,0,0}; + }; +}; +class Stage4 { + texture="z\ace\addons\dragon\data\m47_as.paa"; + uvSource="tex"; + class uvTransform { + aside[]={1,0,0}; + up[]={0,1,0}; + dir[]={0,0,1}; + pos[]={0,0,0}; + }; +}; +class Stage5 { + texture="z\ace\addons\dragon\data\m47_smdi.paa"; + uvSource="tex"; + class uvTransform { + aside[]={1,0,0}; + up[]={0,1,0}; + dir[]={0,0,1}; + pos[]={0,0,0}; + }; +}; +class Stage6 { + texture="#(ai,32,128,1)fresnel(4.01,2.86)"; + uvSource="tex"; + class uvTransform { + aside[]={1,0,0}; + up[]={0,1,0}; + dir[]={0,0,1}; + pos[]={0,0,0}; + }; +}; +class Stage7 { + texture="a3\data_f\env_land_co.paa"; + uvSource="tex"; + class uvTransform { + aside[]={1,0,0}; + up[]={0,1,0}; + dir[]={0,0,1}; + pos[]={0,0,0}; + }; +}; diff --git a/addons/dragon/data/M47mis.rvmat b/addons/dragon/data/M47mis.rvmat new file mode 100644 index 0000000000..b530ec030b --- /dev/null +++ b/addons/dragon/data/M47mis.rvmat @@ -0,0 +1,81 @@ +class StageTI { + texture="z\ace\addons\dragon\data\m47_ti_ca.paa"; +}; +ambient[]={0.989,1,0.958,1}; +diffuse[]={0.989,1,0.958,1}; +forcedDiffuse[]={0,0,0,0}; +emmisive[]={0,0,0,1}; +specular[]={0.159,0.212,0.196,1}; +specularPower=90; +PixelShaderID="Super"; +VertexShaderID="Super"; +class Stage1 { + texture="z\ace\addons\dragon\data\m47mis_nohq.paa"; + uvSource="tex"; + class uvTransform { + aside[]={1,0,0}; + up[]={0,1,0}; + dir[]={0,0,1}; + pos[]={0,0,0}; + }; +}; +class Stage2 { + texture="a3\weapons_f\Data\DetailMaps\Metal_rough_DT.paa"; + uvSource="tex"; + class uvTransform { + aside[]={1,0,0}; + up[]={0,1,0}; + dir[]={0,0,1}; + pos[]={0,0,0}; + }; +}; +class Stage3 { + texture="#(argb,8,8,3)color(0,0,0,0,MC)"; + uvSource="tex"; + class uvTransform { + aside[]={1,0,0}; + up[]={0,1,0}; + dir[]={0,0,1}; + pos[]={0,0,0}; + }; +}; +class Stage4 { + texture="#(argb,8,8,3)color(1,1,1,1,AS)"; + uvSource="tex"; + class uvTransform { + aside[]={1,0,0}; + up[]={0,1,0}; + dir[]={0,0,1}; + pos[]={0,0,0}; + }; +}; +class Stage5 { + texture="z\ace\addons\dragon\data\m47mis_smdi.paa"; + uvSource="tex"; + class uvTransform { + aside[]={1,0,0}; + up[]={0,1,0}; + dir[]={0,0,1}; + pos[]={0,0,0}; + }; +}; +class Stage6 { + texture="#(ai,32,128,1)fresnel(4.01,2.86)"; + uvSource="tex"; + class uvTransform { + aside[]={1,0,0}; + up[]={0,1,0}; + dir[]={0,0,1}; + pos[]={0,0,0}; + }; +}; +class Stage7 { + texture="a3\data_f\env_land_co.paa"; + uvSource="tex"; + class uvTransform { + aside[]={1,0,0}; + up[]={0,1,0}; + dir[]={0,0,1}; + pos[]={0,0,0}; + }; +}; diff --git a/addons/dragon/data/dragon_text.paa b/addons/dragon/data/dragon_text.paa new file mode 100644 index 0000000000..a8a3787292 Binary files /dev/null and b/addons/dragon/data/dragon_text.paa differ diff --git a/addons/dragon/data/m47_as.paa b/addons/dragon/data/m47_as.paa new file mode 100644 index 0000000000..c561e39cf2 Binary files /dev/null and b/addons/dragon/data/m47_as.paa differ diff --git a/addons/dragon/data/m47_co.paa b/addons/dragon/data/m47_co.paa new file mode 100644 index 0000000000..5c621b5728 Binary files /dev/null and b/addons/dragon/data/m47_co.paa differ diff --git a/addons/dragon/data/m47_daysight_item_ca.paa b/addons/dragon/data/m47_daysight_item_ca.paa new file mode 100644 index 0000000000..9768f42b9c Binary files /dev/null and b/addons/dragon/data/m47_daysight_item_ca.paa differ diff --git a/addons/dragon/data/m47_dragon_item_ca.paa b/addons/dragon/data/m47_dragon_item_ca.paa new file mode 100644 index 0000000000..0e87574794 Binary files /dev/null and b/addons/dragon/data/m47_dragon_item_ca.paa differ diff --git a/addons/dragon/data/m47_nohq.paa b/addons/dragon/data/m47_nohq.paa new file mode 100644 index 0000000000..e70a9f0ff2 Binary files /dev/null and b/addons/dragon/data/m47_nohq.paa differ diff --git a/addons/dragon/data/m47_smdi.paa b/addons/dragon/data/m47_smdi.paa new file mode 100644 index 0000000000..b6cd197ea8 Binary files /dev/null and b/addons/dragon/data/m47_smdi.paa differ diff --git a/addons/dragon/data/m47_ti_ca.paa b/addons/dragon/data/m47_ti_ca.paa new file mode 100644 index 0000000000..27c24a6599 Binary files /dev/null and b/addons/dragon/data/m47_ti_ca.paa differ diff --git a/addons/dragon/data/m47mis_co.paa b/addons/dragon/data/m47mis_co.paa new file mode 100644 index 0000000000..e76bee6f81 Binary files /dev/null and b/addons/dragon/data/m47mis_co.paa differ diff --git a/addons/dragon/data/m47mis_nohq.paa b/addons/dragon/data/m47mis_nohq.paa new file mode 100644 index 0000000000..6489813b6b Binary files /dev/null and b/addons/dragon/data/m47mis_nohq.paa differ diff --git a/addons/dragon/data/m47mis_smdi.paa b/addons/dragon/data/m47mis_smdi.paa new file mode 100644 index 0000000000..833a9c19c1 Binary files /dev/null and b/addons/dragon/data/m47mis_smdi.paa differ diff --git a/addons/dragon/functions/fnc_attackProfile_DRAGON.sqf b/addons/dragon/functions/fnc_attackProfile_DRAGON.sqf new file mode 100644 index 0000000000..e717b32f70 --- /dev/null +++ b/addons/dragon/functions/fnc_attackProfile_DRAGON.sqf @@ -0,0 +1,77 @@ +#include "script_component.hpp" +/* + * Author: Brandon (TCVM) (Code inspired by NouberNou's Dragon Guidance) + * Attack profile: Dragon Guidance + * + * Arguments: + * 0: Seeker Target PosASL + * 1: Guidance Arg Array + * 2: Attack Profile State + * + * Return Value: + * Missile Aim PosASL - Unused + * + * Example: + * [[1,2,3], [], []] call ace_dragon_fnc_attackProfile_DRAGON; + * + * Public: No + */ +params ["_seekerTargetPos", "_args", "_attackProfileStateParams"]; +_args params ["_firedEH", "", "", "", "_stateParams"]; +_firedEH params ["_shooter","_weapon","","","","","_projectile"]; +_attackProfileStateParams params ["_maxCorrectableDistance", "_wireCut", "_seekerMaxRangeSqr", "_seekerMinRangeSqr", "_wireCutSource", "_lastTime", "_serviceInterval", "_serviceChargeCount", "_serviceChargeAcceleration", "_dragonSpeed"]; + +private _projectilePos = getPosASL _projectile; +private _distanceToProjectile = (getPosASL _shooter) vectorDistanceSqr _projectilePos; +private _retPos = _projectilePos vectorAdd (AGLtoASL (_projectile vectorModelToWorld [0, 50, 0])); + +// _shooter returns the vehicle that shot it. If the launcher dies, the wire would probably be cut so assume it +if ((_distanceToProjectile > _seekerMaxRangeSqr) || _wireCut || { !alive _shooter }) exitWith { + // wire snap, random direction + if (!_wireCut) then { + _attackProfileStateParams set [1, true]; + playSound3D ["a3\sounds_f\air\sfx\SL_rope_break.wss", objNull, false, AGLtoASL (_shooter modelToWorld _wireCutSource), 150, 1, 25]; + }; + + if (_serviceChargeCount > 0 && {(_lastTime - CBA_missionTime) <= 0}) then { + _attackProfileStateParams set [5, CBA_missionTime + 0.05 + random 0.1]; + private _randomVector = [(random 2) - 1, random 1, (random 2) - 1]; + _projectile setVelocityModelSpace ((velocityModelSpace _projectile) vectorAdd (_randomVector vectorMultiply _serviceChargeAcceleration)); + private _charge = createVehicle [QGVAR(serviceCharge), [0, 0, 0], [], 0, "NONE"]; + _charge setPosASL (_projectilePos vectorAdd ((_randomVector vectorMultiply -1) vectorMultiply 0.025)); + _attackProfileStateParams set [7, _serviceChargeCount - 1]; + }; + + _retPos +}; + +if (_distanceToProjectile <= _seekerMinRangeSqr || { _serviceChargeCount <= 0 } || { !(_shooter getVariable [QGVAR(sightAttached), true]) }) exitWith { _retPos }; + +// if the time between updates is less than the pop time we want to fire the rockets OR if the missile wants to make a major correction pop it rapidly +if (((_lastTime - CBA_missionTime) <= 0) || {(_lastTime - CBA_missionTime) < (_serviceInterval / 2) && (_projectilePos vectorDistance _seekerTargetPos > 1)}) then { + _attackProfileStateParams set [5, CBA_missionTime + _serviceInterval]; + + private _vectorToCrosshair = vectorNormalized (_projectile worldToModel (ASLToAGL _seekerTargetPos)); + private _vectorToPos = vectorNormalized (((_projectile vectorWorldToModelVisual (_shooter weaponDirection _weapon)) vectorMultiply (_dragonSpeed * _serviceInterval)) vectorAdd (_vectorToCrosshair vectorMultiply _maxCorrectableDistance)); + + if ((_vectorToPos select 2) < 0) then { + _vectorToPos set [2, 0]; + } else { + private _a = _vectorToPos select 1; + private _b = _vectorToPos select 2; + // The booster has some angle to it, so we introduce that axis if the angle is too low + if (abs(_a) > 0 && { abs(atan (_b / _a)) < DRAGON_BOOSTER_ANGLE }) then { + _vectorToPos set [2, abs(_a)]; + }; + }; + + _projectile setVelocityModelSpace ((velocityModelSpace _projectile) vectorAdd (_vectorToPos vectorMultiply _serviceChargeAcceleration)); + + private _charge = createVehicle [QGVAR(serviceCharge), [0, 0, 0], [], 0, "NONE"]; + _charge setPosASL (_projectilePos vectorAdd ((_vectorToCrosshair vectorMultiply -1) vectorMultiply 0.025)); + + _attackProfileStateParams set [7, _serviceChargeCount - 1]; +}; + +_retPos + diff --git a/addons/dragon/functions/fnc_canPickupTripod.sqf b/addons/dragon/functions/fnc_canPickupTripod.sqf new file mode 100644 index 0000000000..c328c66bd6 --- /dev/null +++ b/addons/dragon/functions/fnc_canPickupTripod.sqf @@ -0,0 +1,25 @@ +#include "script_component.hpp" +/* + * Author: Brandon (TCVM) + * Determines if you can pick-up the Dragon missile. If the missile was fired you will not be able to pick up the tripod. + * + * Arguments: + * 0: Target + * 1: Unit Performing Action + * + * Return Value: + * Can Pickup Tripod + * + * Example: + * [vehicle player, player] call ace_dragon_fnc_canPickupTripod; + * + * Public: No + */ +params ["_target", "_unit"]; + +(alive _target) +&& {!alive (gunner _target)} +&& {!(_target getVariable [QGVAR(fired), false])} +&& {!(_target getVariable [QGVAR(sightAttached), ((typeOf _target) == QGVAR(staticAssembled))])} +&& EFUNC(csw,assemble_canPickupTripod) + diff --git a/addons/dragon/functions/fnc_onFired.sqf b/addons/dragon/functions/fnc_onFired.sqf new file mode 100644 index 0000000000..78d9d15b91 --- /dev/null +++ b/addons/dragon/functions/fnc_onFired.sqf @@ -0,0 +1,53 @@ +#include "script_component.hpp" +/* + * Author: Brandon (TCVM) + * Runs when Dragon is fired + * + * Arguments: + * 0: None + * 1: The weapon that was fired + * + * Return Value: + * None + * + * Example: + * [any, vehicle player] call ace_dragon_fnc_onFired; + * + * Public: No + */ +params ["_firedEH", "", "", "_seekerParams", "_stateParams"]; +_firedEH params ["_shooter","_weapon","","","","","_projectile"]; +_stateParams params ["", "", "_attackProfileStateParams"]; +_seekerParams params ["", "", "_seekerMaxRange", "_seekerMinRange"]; + +_shooter setVariable [QGVAR(fired), true, true]; +_shooter animate ["missile_hide", 1]; + +private _config = ([_projectile] call CBA_fnc_getObjectConfig) >> "ace_missileguidance"; + +private _serviceInterval = [_config >> "serviceInterval", "NUMBER", 0.33] call CBA_fnc_getConfigEntry; +private _serviceChargeCount = [_config >> "serviceCharges", "NUMBER", 60] call CBA_fnc_getConfigEntry; +private _serviceChargeAcceleration = [_config >> "serviceChargeAcceleration", "NUMBER", 6.5] call CBA_fnc_getConfigEntry; +private _dragonSpeed = [_config >> "dragonSpeed", "NUMBER", 100] call CBA_fnc_getConfigEntry; +private _maxCorrectableDistance = [_config >> "correctionDistance", "NUMBER", DEFAULT_CORRECTION_DISTANCE] call CBA_fnc_getConfigEntry; +private _maxDistanceSqr = _seekerMaxRange * _seekerMaxRange; +private _minDistanceSqr = _seekerMinRange * _seekerMinRange; + +private _turretPath = [_shooter, _weapon] call CBA_fnc_turretPathWeapon; +private _turretConfig = [_shooter, _turretPath] call CBA_fnc_getTurret; + +private _wireCutSource = _shooter selectionPosition getText(_turretConfig >> "missileEnd"); + +_attackProfileStateParams append [ + _maxCorrectableDistance, + false, + _maxDistanceSqr, + _minDistanceSqr, + _wireCutSource, + CBA_missionTime, + _serviceInterval, + _serviceChargeCount, + _serviceChargeAcceleration, + _dragonSpeed +]; + diff --git a/addons/dragon/functions/fnc_sightAttach.sqf b/addons/dragon/functions/fnc_sightAttach.sqf new file mode 100644 index 0000000000..a97f160d9d --- /dev/null +++ b/addons/dragon/functions/fnc_sightAttach.sqf @@ -0,0 +1,32 @@ +#include "script_component.hpp" +/* + * Author: Brandon (TCVM) + * Attaches the sighting unit to the Dragon missile. + * + * Arguments: + * 0: Target + * 1: Unit Performing Action + * + * Return Value: + * Can Attach Sighting Unit + * + * Example: + * [cursorObject, player] call ace_dragon_fnc_sightAttach + * + * Public: No + */ + +params ["_target", "_unit", ["_event", false]]; +TRACE_3("sightAttach",_target,_unit,_event); + +if (_event isEqualTo true) then { + if (!(_target turretLocal [0])) exitWith {}; + _target setVariable [QGVAR(sightAttached), true, true]; + _target animate ["optic_hide", 0]; + _target addWeapon QGVAR(superStatic); + _target removeWeapon QGVAR(dummyStatic); + TRACE_2("added sight",_target weaponsTurret [0],_target animationPhase "optic_hide"); +} else { + _unit removeWeapon QGVAR(sight); + [QGVAR(attachSight), [_target, _unit, true]] call CBA_fnc_globalEvent; +}; diff --git a/addons/dragon/functions/fnc_sightCanAttach.sqf b/addons/dragon/functions/fnc_sightCanAttach.sqf new file mode 100644 index 0000000000..ca7d05539d --- /dev/null +++ b/addons/dragon/functions/fnc_sightCanAttach.sqf @@ -0,0 +1,24 @@ +#include "script_component.hpp" +/* + * Author: Brandon (TCVM) + * Determines if you can attach the sighting unit to the Dragon missile. + * + * Arguments: + * 0: Target + * 1: Unit Performing Action + * + * Return Value: + * Can Attach Sighting Unit + * + * Example: + * [cursorObject, player] call ace_dragon_fnc_sightCanAttach + * + * Public: No + */ + +params ["_target", "_unit"]; + +(alive _target) +// && {!(_target getVariable [QGVAR(fired), false])} +&& {!(_target getVariable [QGVAR(sightAttached), ((typeOf _target) == QGVAR(staticAssembled))])} +&& {QGVAR(sight) in (weapons _unit)} diff --git a/addons/dragon/functions/fnc_sightCanDetach.sqf b/addons/dragon/functions/fnc_sightCanDetach.sqf new file mode 100644 index 0000000000..49c7684509 --- /dev/null +++ b/addons/dragon/functions/fnc_sightCanDetach.sqf @@ -0,0 +1,23 @@ +#include "script_component.hpp" +/* + * Author: Brandon (TCVM) + * Determines if you can attach the sighting unit to the Dragon missile. + * + * Arguments: + * 0: Target + * 1: Unit Performing Action + * + * Return Value: + * Can Attach Sighting Unit + * + * Example: + * [cursorObject, player] call ace_dragon_fnc_sightCanDetach + * + * Public: No + */ + +params ["_target", "_unit"]; + +(alive _target) +// && {!(_target getVariable [QGVAR(fired), false])} +&& {_target getVariable [QGVAR(sightAttached), ((typeOf _target) == QGVAR(staticAssembled))]} diff --git a/addons/dragon/functions/fnc_sightDetach.sqf b/addons/dragon/functions/fnc_sightDetach.sqf new file mode 100644 index 0000000000..2ee9bcfa46 --- /dev/null +++ b/addons/dragon/functions/fnc_sightDetach.sqf @@ -0,0 +1,38 @@ +#include "script_component.hpp" +/* + * Author: Brandon (TCVM) + * Attaches the sighting unit to the Dragon missile. + * + * Arguments: + * 0: Target + * 1: Unit Performing Action + * + * Return Value: + * Can Attach Sighting Unit + * + * Example: + * [cursorObject, player] call ace_dragon_fnc_sightDetach; + * + * Public: No + */ + +params ["_target", "_unit"]; + +params ["_target", "_unit", ["_event", false]]; +TRACE_3("sightDetach",_target,_unit,_event); + +if (_event isEqualTo true) then { + if (!(_target turretLocal [0])) exitWith {}; + _target setVariable [QGVAR(sightAttached), false, true]; + _target animate ["optic_hide", 1]; + _target removeWeapon QGVAR(superStatic); + _target addWeapon QGVAR(dummyStatic); + TRACE_2("removed sight",_target weaponsTurret [0],_target animationPhase "optic_hide"); +} else { + if ((binocular _unit) == "") then { + _unit addWeapon QGVAR(sight); + } else { + [_unit, QGVAR(sight)] call EFUNC(common,addToInventory); + }; + [QGVAR(detachSight), [_target, _unit, true]] call CBA_fnc_globalEvent; +}; diff --git a/addons/dragon/functions/script_component.hpp b/addons/dragon/functions/script_component.hpp new file mode 100644 index 0000000000..386679ecf6 --- /dev/null +++ b/addons/dragon/functions/script_component.hpp @@ -0,0 +1 @@ +#include "\z\ace\addons\dragon\script_component.hpp" \ No newline at end of file diff --git a/addons/dragon/models/M47.p3d b/addons/dragon/models/M47.p3d new file mode 100644 index 0000000000..9554efe7f8 Binary files /dev/null and b/addons/dragon/models/M47.p3d differ diff --git a/addons/dragon/models/M47_loaded.p3d b/addons/dragon/models/M47_loaded.p3d new file mode 100644 index 0000000000..7afe797185 Binary files /dev/null and b/addons/dragon/models/M47_loaded.p3d differ diff --git a/addons/dragon/models/ace_m47_magazine.p3d b/addons/dragon/models/ace_m47_magazine.p3d new file mode 100644 index 0000000000..39a8b6d80a Binary files /dev/null and b/addons/dragon/models/ace_m47_magazine.p3d differ diff --git a/addons/dragon/models/ace_m47_optic.p3d b/addons/dragon/models/ace_m47_optic.p3d new file mode 100644 index 0000000000..a0d8af5a78 Binary files /dev/null and b/addons/dragon/models/ace_m47_optic.p3d differ diff --git a/addons/dragon/models/ace_m47_static.p3d b/addons/dragon/models/ace_m47_static.p3d new file mode 100644 index 0000000000..3579797142 Binary files /dev/null and b/addons/dragon/models/ace_m47_static.p3d differ diff --git a/addons/dragon/models/dragon.p3d b/addons/dragon/models/dragon.p3d new file mode 100644 index 0000000000..4416a2d7a9 Binary files /dev/null and b/addons/dragon/models/dragon.p3d differ diff --git a/addons/dragon/models/model.cfg b/addons/dragon/models/model.cfg new file mode 100644 index 0000000000..ab93f7e9a8 --- /dev/null +++ b/addons/dragon/models/model.cfg @@ -0,0 +1,94 @@ +class CfgSkeletons { + class Default { + isDiscrete = 1; + skeletonInherit = ""; + skeletonBones[] = {}; + }; + + class ace_m47_static_skeleton: Default { + skeletonInherit = "Default"; + skeletonBones[] = { + "bipod","", + "grav_box","bipod", + "launcher","grav_box", + "optic","launcher", + "missile","launcher" + }; + }; +}; + +class CfgModels { + class Default { + sectionsInherit = ""; + sections[] = {}; + }; + + class ace_m47_static: Default { + sectionsInherit = "Default"; + sections[] = {}; + skeletonName = "ace_m47_static_skeleton"; + class Animations { + class MainGun { + type="rotation"; + selection="launcher"; + sourceAddress = "clamp"; + source="MainGun"; + axis="elevate_axis"; + animPeriod=0.01; + initPhase=0; + maxValue="rad 360"; + minValue="rad -360"; + angle1="rad 360"; + angle0="rad -360"; + }; + class MainTurret { + type="rotation"; + source="MainTurret"; + selection="bipod"; + sourceAddress = "loop"; + axis="rotate_axis"; + animPeriod=0.005; + minValue="rad -360"; + maxValue="rad +360"; + angle0="rad -360"; + angle1="rad +360"; + }; + class rest_rotate { + type="rotation"; + selection="grav_box"; + sourceAddress = "clamp"; + source="user"; + axis="elevate_axis"; + animPeriod=0.00001; + initPhase=-0.35; + maxValue="3.60"; + minValue="-3.60"; + angle1="rad -360"; + angle0="rad 360"; + }; + class optic_hide { + type = "hide"; + source = "user"; + selection = "optic"; + animPeriod = 0.0001; + minValue = 0; + maxValue = 1; + minPhase = 0; + maxPhase = 1; + hideValue = 0.99; + initPhase = 1; + }; + class missile_hide { + type = "hide"; + source = "user"; + selection = "missile"; + animPeriod = 0.0001; + minValue = 0; + maxValue = 1; + minPhase = 0; + maxPhase = 1; + hideValue = 0.99; + }; + }; + }; +}; diff --git a/addons/dragon/models/optics_m47.p3d b/addons/dragon/models/optics_m47.p3d new file mode 100644 index 0000000000..08183a1f0e Binary files /dev/null and b/addons/dragon/models/optics_m47.p3d differ diff --git a/addons/dragon/script_component.hpp b/addons/dragon/script_component.hpp new file mode 100644 index 0000000000..77521b2c6a --- /dev/null +++ b/addons/dragon/script_component.hpp @@ -0,0 +1,18 @@ +#define COMPONENT dragon +#define COMPONENT_BEAUTIFIED M47 Dragon +#include "\z\ace\addons\main\script_mod.hpp" + +// #define DEBUG_MODE_FULL +// #define DISABLE_COMPILE_CACHE +// #define ENABLE_PERFORMANCE_COUNTERS + +#ifdef DEBUG_ENABLED_DRAGON + #define DEBUG_MODE_FULL +#endif + +#ifdef DEBUG_SETTINGS_DRAGON + #define DEBUG_SETTINGS DEBUG_SETTINGS_DRAGON +#endif + +#include "\z\ace\addons\main\script_macros.hpp" +#define DRAGON_BOOSTER_ANGLE 45 diff --git a/addons/dragon/sounds/M47_1.wss b/addons/dragon/sounds/M47_1.wss new file mode 100644 index 0000000000..4b1949aa49 Binary files /dev/null and b/addons/dragon/sounds/M47_1.wss differ diff --git a/addons/dragon/sounds/M47_2.wss b/addons/dragon/sounds/M47_2.wss new file mode 100644 index 0000000000..c630402c5b Binary files /dev/null and b/addons/dragon/sounds/M47_2.wss differ diff --git a/addons/dragon/sounds/dry.wss b/addons/dragon/sounds/dry.wss new file mode 100644 index 0000000000..e54a8ab4e9 Binary files /dev/null and b/addons/dragon/sounds/dry.wss differ diff --git a/addons/dragon/sounds/rocket_fly.wss b/addons/dragon/sounds/rocket_fly.wss new file mode 100644 index 0000000000..76264b1246 Binary files /dev/null and b/addons/dragon/sounds/rocket_fly.wss differ diff --git a/addons/dragon/sounds/rocket_reload.wss b/addons/dragon/sounds/rocket_reload.wss new file mode 100644 index 0000000000..8cdce5a7f8 Binary files /dev/null and b/addons/dragon/sounds/rocket_reload.wss differ diff --git a/addons/dragon/sounds/service_charge.wss b/addons/dragon/sounds/service_charge.wss new file mode 100644 index 0000000000..1cafccaa91 Binary files /dev/null and b/addons/dragon/sounds/service_charge.wss differ diff --git a/addons/dragon/stringtable.xml b/addons/dragon/stringtable.xml new file mode 100644 index 0000000000..e9d3ff2eb6 --- /dev/null +++ b/addons/dragon/stringtable.xml @@ -0,0 +1,29 @@ + + + + + M47 Super-Dragon + + + M47 Super-Dragon (No Sight) + + + M47 Super-Dragon (With Sight) + + + Attach Sight + + + Detach Sight + + + SU-36/P Daysight + + + A light, cheap sight used for daytime operations. Contains the guidance computer for the whole system + + + A Wire-Guided SACLOS missile with a unique flight characteristic + + + diff --git a/addons/hot/ACE_GuidanceConfig.hpp b/addons/hot/ACE_GuidanceConfig.hpp index 81f5c69261..f129a6b558 100644 --- a/addons/hot/ACE_GuidanceConfig.hpp +++ b/addons/hot/ACE_GuidanceConfig.hpp @@ -5,6 +5,7 @@ class EGVAR(missileguidance,AttackProfiles) { description = CSTRING(missileType_Description); functionName = QFUNC(attackProfile_WIRE); + onFired = QFUNC(wire_onFired); }; }; class EGVAR(missileguidance,SeekerTypes) { @@ -14,6 +15,7 @@ class EGVAR(missileguidance,SeekerTypes) { description = CSTRING(SACLOS_Description); functionName = QFUNC(seekerType_SACLOS); + onFired = QFUNC(SACLOS_onFired); }; }; diff --git a/addons/hot/CfgAmmo.hpp b/addons/hot/CfgAmmo.hpp index 168386d47f..e591d13aff 100644 --- a/addons/hot/CfgAmmo.hpp +++ b/addons/hot/CfgAmmo.hpp @@ -70,8 +70,6 @@ class CfgAmmo { 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 diff --git a/addons/hot/XEH_PREP.hpp b/addons/hot/XEH_PREP.hpp index 5942a97017..ec59d0f41f 100644 --- a/addons/hot/XEH_PREP.hpp +++ b/addons/hot/XEH_PREP.hpp @@ -1,4 +1,5 @@ PREP(seekerType_SACLOS); PREP(attackProfile_WIRE); -PREP(onFired); +PREP(wire_onFired); +PREP(SACLOS_onFired); diff --git a/addons/hot/functions/fnc_SACLOS_onFired.sqf b/addons/hot/functions/fnc_SACLOS_onFired.sqf new file mode 100644 index 0000000000..7702b330ee --- /dev/null +++ b/addons/hot/functions/fnc_SACLOS_onFired.sqf @@ -0,0 +1,35 @@ +#include "script_component.hpp" +/* + * Author: Brandon (TCVM) + * Sets up SACLOS state arrays (called from missileGuidance's onFired). + * + * Arguments: + * Guidance Arg Array + * + * Return Value: + * None + * + * Example: + * [] call ace_hot_fnc_SACLOS_onFired + * + * Public: No + */ +params ["_firedEH", "", "", "", "_stateParams"]; +_firedEH params ["_shooter","_weapon","","","","","_projectile"]; +_stateParams params ["", "_seekerStateParams"]; + +private _config = ([_projectile] call CBA_fnc_getObjectConfig) >> "ace_missileguidance"; +private _distanceAheadOfMissile = [_config >> "missileLeadDistance", "NUMBER", DEFAULT_LEAD_DISTANCE] call CBA_fnc_getConfigEntry; + +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 _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_attackProfile_WIRE.sqf b/addons/hot/functions/fnc_attackProfile_WIRE.sqf index 8aef8a702b..139939e4f7 100644 --- a/addons/hot/functions/fnc_attackProfile_WIRE.sqf +++ b/addons/hot/functions/fnc_attackProfile_WIRE.sqf @@ -19,7 +19,7 @@ params ["_seekerTargetPos", "_args", "_attackProfileStateParams"]; _args params ["_firedEH"]; _firedEH params ["_shooter","","","","","","_projectile"]; -_attackProfileStateParams params["_maxCorrectableDistance", "_wireCut", "_randomVector", "_crosshairOffset", "_seekerMaxRangeSqr", "_wireCutSource"]; +_attackProfileStateParams params["_maxCorrectableDistance", "_wireCut", "_randomVector", "_crosshairOffset", "_seekerMaxRangeSqr", "", "_wireCutSource"]; private _projectilePos = getPosASL _projectile; diff --git a/addons/hot/functions/fnc_seekerType_SACLOS.sqf b/addons/hot/functions/fnc_seekerType_SACLOS.sqf index 89ee38b696..06245d0708 100644 --- a/addons/hot/functions/fnc_seekerType_SACLOS.sqf +++ b/addons/hot/functions/fnc_seekerType_SACLOS.sqf @@ -25,7 +25,7 @@ _seekerStateParams params ["_memoryPointGunnerOptics", "_animationSourceBody", " private _shooterPos = AGLToASL (_shooter modelToWorld(_shooter selectionPosition _memoryPointGunnerOptics)); private _projPos = getPosASL _projectile; -private _lookDirection = if !(_shooter isKindOf "CAManBase") then { +private _lookDirection = if !(_shooter isKindOf "CAManBase" || {_shooter isKindOf "StaticWeapon"}) then { private _gBody = -deg(_shooter animationPhase _animationSourceBody); private _gGun = deg(_shooter animationPhase _animationSourceGun); diff --git a/addons/hot/functions/fnc_onFired.sqf b/addons/hot/functions/fnc_wire_onFired.sqf similarity index 51% rename from addons/hot/functions/fnc_onFired.sqf rename to addons/hot/functions/fnc_wire_onFired.sqf index 38599f9b6a..3e1ff2d03d 100644 --- a/addons/hot/functions/fnc_onFired.sqf +++ b/addons/hot/functions/fnc_wire_onFired.sqf @@ -1,7 +1,7 @@ #include "script_component.hpp" /* * Author: Brandon (TCVM) - * Sets up missile guidance state arrays (called from missileGuidance's onFired). + * Sets up wireGuided state arrays (called from missileGuidance's onFired). * * Arguments: * Guidance Arg Array @@ -10,26 +10,26 @@ * None * * Example: - * [] call ace_hot_fnc_onFired + * [] call ace_hot_fnc_wire_onFired * * Public: No */ params ["_firedEH", "", "", "_seekerParams", "_stateParams"]; _firedEH params ["_shooter","_weapon","","","","","_projectile", "_gunner"]; -_stateParams params ["", "_seekerStateParams", "_attackProfileStateParams"]; -_seekerParams params ["", "", "_seekerMaxRange"]; +_stateParams params ["", "", "_attackProfileStateParams"]; +_seekerParams params ["", "", "_seekerMaxRange", "_seekerMinRange"]; 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; +private _minDistanceSqr = _seekerMinRange * _seekerMinRange; // 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]; +_crosshairOffset = if ((_gunner != ACE_PLAYER) && {_gunner != (ACE_controlledUAV select 1)}) then { + [0, 0, 0]; +} else { + [_config >> "offsetFromCrosshair", "ARRAY", [0, 0, 0]] call CBA_fnc_getConfigEntry }; -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; @@ -41,13 +41,6 @@ _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]; +_attackProfileStateParams set [5, _minDistanceSqr]; +_attackProfileStateParams set [6, _wireCutSource]; diff --git a/addons/missileguidance/functions/fnc_guidancePFH.sqf b/addons/missileguidance/functions/fnc_guidancePFH.sqf index 8196de8057..e218e27e54 100644 --- a/addons/missileguidance/functions/fnc_guidancePFH.sqf +++ b/addons/missileguidance/functions/fnc_guidancePFH.sqf @@ -55,7 +55,8 @@ private _seekerTargetPos = [[0,0,0], _args, _seekerStateParams, _lastKnownPosSta private _profileAdjustedTargetPos = [_seekerTargetPos, _args, _attackProfileStateParams] call FUNC(doAttackProfile); // If we have no seeker target, then do not change anything -if (!(_profileAdjustedTargetPos isEqualTo [0,0,0])) then { +// If there is no deflection on the missile, this cannot change and therefore is redundant. Avoid calculations for missiles without any deflection +if ((_minDeflection != 0 || {_maxDeflection != 0}) && {!(_profileAdjustedTargetPos isEqualTo [0,0,0])}) then { private _targetVector = _projectilePos vectorFromTo _profileAdjustedTargetPos; private _adjustVector = _targetVector vectorDiff (vectorDir _projectile); diff --git a/addons/missileguidance/functions/fnc_onFired.sqf b/addons/missileguidance/functions/fnc_onFired.sqf index 0e690c79f4..b77911e979 100644 --- a/addons/missileguidance/functions/fnc_onFired.sqf +++ b/addons/missileguidance/functions/fnc_onFired.sqf @@ -112,19 +112,30 @@ private _args = [_this, [ getNumber ( _config >> "seekerAngle" ), getNumber ( _config >> "seekerAccuracy" ), - getNumber ( _config >> "seekerMaxRange" ) + getNumber ( _config >> "seekerMaxRange" ), + getNumber ( _config >> "seekerMinRange" ) ], [ diag_tickTime, [], [], _lastKnownPosState] ]; - -// Run the "onFired" function passing the full guidance args array -private _onFiredFunc = getText (_config >> "onFired"); +private _onFiredFunc = getText (configFile >> QGVAR(SeekerTypes) >> _seekerType >> "onFired"); TRACE_1("",_onFiredFunc); if (_onFiredFunc != "") then { _args call (missionNamespace getVariable _onFiredFunc); }; +_onFiredFunc = getText (configFile >> QGVAR(AttackProfiles) >> _attackProfile >> "onFired"); +TRACE_1("",_onFiredFunc); +if (_onFiredFunc != "") then { + _args call (missionNamespace getVariable _onFiredFunc); +}; + +// Run the "onFired" function passing the full guidance args array +_onFiredFunc = getText (_config >> "onFired"); +TRACE_1("",_onFiredFunc); +if (_onFiredFunc != "") then { + _args call (missionNamespace getVariable _onFiredFunc); +}; // Reverse: // _args params ["_firedEH", "_launchParams", "_flightParams", "_seekerParams", "_stateParams"]; @@ -134,24 +145,6 @@ if (_onFiredFunc != "") then { // _stateParams params ["_lastRunTime", "_seekerStateParams", "_attackProfileStateParams", "_lastKnownPosState"]; // _seekerParams params ["_seekerAngle", "_seekerAccuracy", "_seekerMaxRange"]; - -// Hand off to the guiding unit. We just use local player so local PFH fires for now -// Laser code needs to give us a shooter for LOBL, or the seeker unit needs to be able to shift locality -// Based on its homing laser -// Lasers need to be handled in a special LOAL/LOBL case - -//if (isPlayer _shooter) then { -// private _guidingUnit = ACE_player; -// -// if (local _guidingUnit) then { -// [FUNC(guidancePFH), 0, _args ] call CBA_fnc_addPerFrameHandler; -// } else { -// [QGVAR(handoff), [_guidingUnit, _args] ] call FUNC(doHandoff); -// }; -//} else { - // [FUNC(guidancePFH), 0, _args ] call CBA_fnc_addPerFrameHandler; -//}; - [FUNC(guidancePFH), 0, _args ] call CBA_fnc_addPerFrameHandler; diff --git a/docs/img/wiki/feature/m47_flank.png b/docs/img/wiki/feature/m47_flank.png new file mode 100644 index 0000000000..fbbf9c9403 Binary files /dev/null and b/docs/img/wiki/feature/m47_flank.png differ diff --git a/docs/img/wiki/feature/m47_fro_obl_fla.png b/docs/img/wiki/feature/m47_fro_obl_fla.png new file mode 100644 index 0000000000..bbd3013d8b Binary files /dev/null and b/docs/img/wiki/feature/m47_fro_obl_fla.png differ diff --git a/docs/img/wiki/feature/m47_frontal.png b/docs/img/wiki/feature/m47_frontal.png new file mode 100644 index 0000000000..48730b02c8 Binary files /dev/null and b/docs/img/wiki/feature/m47_frontal.png differ diff --git a/docs/img/wiki/feature/m47_oblique_45.png b/docs/img/wiki/feature/m47_oblique_45.png new file mode 100644 index 0000000000..616940aba7 Binary files /dev/null and b/docs/img/wiki/feature/m47_oblique_45.png differ diff --git a/docs/img/wiki/feature/m47_oblique_60.png b/docs/img/wiki/feature/m47_oblique_60.png new file mode 100644 index 0000000000..65c3cd23eb Binary files /dev/null and b/docs/img/wiki/feature/m47_oblique_60.png differ diff --git a/docs/wiki/feature/dragon.md b/docs/wiki/feature/dragon.md new file mode 100644 index 0000000000..2e4e30e837 --- /dev/null +++ b/docs/wiki/feature/dragon.md @@ -0,0 +1,80 @@ +--- +layout: wiki +title: M47 Dragon Missile +description: M47 Dragon +group: feature +category: equipment +parent: wiki +mod: ace +version: + major: 3 + minor: 13 + patch: 0 +--- + +## 1. Overview + +### 1.1 Guidance + +The M47 Dragon is a wire-guided SACLOS missile with a unique manuever system. It has a series of charges along the missile that fire every third of a second to adjust the missile on course + +### 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. +The missiles manuever system causes inaccuracy that cannot be corrected for by the gunner. This means that the risk of missing increases as range goes up + +## 2. Usage + +### 2.1 Placement + +- Ensure that you have the M47 Super-Dragon launcher as well as the SU-36/P sighting unit +- Use self interaction Ctrl+⊞ Win (ACE3 default key bind `Self Interaction Key`) +- Select 'Place Tripod' +- Interact with the missile ⊞ Win (ACE3 default key bind `Interaction Key`) +- Select 'Attach Sight' +- Get into the launcher +- Point at target +- Fire + +### 2.2 Tear Down +- Interact with the missile ⊞ Win (ACE3 default key bind `Interaction Key`) +- Select 'Disassemble' +- Look to the right of the launcher and pick up the sighting unit +- Move on + +## 3. Sight Ranging + +The Dragon gunners use the stadia lines in the daysight to determine if a target falls within range. Moving and stationary vehicles may present flank, oblique, and frontal or rear targets (Figure 1). +At maximum range (1,500 meters), a 7-meter (23-foot) long target completely fills the area between the stadia lines and exceeds the stadia lines at a closer range. + +**Figure 1. Frontal, oblique, and flank targets** +Frontal, Oblique, and Flank targets + +### 3.1 **Flanking Targets (Full Stadia)** + +Adjust the sight picture by moving the launcher so the target centers between the stadia lines (Figure 2). + +**Figure 2. Range determination for flank target.** +Frontal, Oblique, and Flank targets + +### 3.2 **Oblique Targets** + +If you can see more of the flank, use the full-stadia method (Figure 3.1). The vehicle should appear to fill the area between the stadia lines. If you see more of the front or rear, use the half-stadia method (Figure 3.2). The track of the vehicle should fit between one stadia line and the center. + +**Figure 3.1. Range determination for oblique target, more flank visible** +Frontal, Oblique, and Flank targets +**Figure 3.2. Range determination for oblique target, more front or rear visible** +Frontal, Oblique, and Flank targets + +### 3.3 **Frontal (Head-On) or Rear (Going Away, Half-Stadia) Targets** + +Adjust the sight picture by moving the launcher to align the vertical cross hair and one of the stadia lines on the target (Figure 4) + +**Figure 4. Range determination for frontal or rear target.** +Frontal, Oblique, and Flank targets + +## 4. Dependencies + +{% include dependencies_list.md component="dragon" %}