diff --git a/addons/missile_sdb/$PBOPREFIX$ b/addons/missile_sdb/$PBOPREFIX$
new file mode 100644
index 00000000000..c17a81095cb
--- /dev/null
+++ b/addons/missile_sdb/$PBOPREFIX$
@@ -0,0 +1 @@
+z\ace\addons\missile_sdb
\ No newline at end of file
diff --git a/addons/missile_sdb/CfgAmmo.hpp b/addons/missile_sdb/CfgAmmo.hpp
new file mode 100644
index 00000000000..10bd2262c87
--- /dev/null
+++ b/addons/missile_sdb/CfgAmmo.hpp
@@ -0,0 +1,12 @@
+class EGVAR(missileguidance,type_Jdam);
+class CfgAmmo {
+ class ammo_Bomb_SDB;
+ class GVAR(sdb): ammo_Bomb_SDB {
+ author = "Dani (TCVM)";
+ maneuvrability = 0; // no maneuvrability so that default guidance doesnt work
+ class ace_missileguidance: EGVAR(missileguidance,type_Jdam) {
+ enabled = 1;
+ };
+ };
+};
+
diff --git a/addons/missile_sdb/CfgMagazines.hpp b/addons/missile_sdb/CfgMagazines.hpp
new file mode 100644
index 00000000000..e0a88c9758b
--- /dev/null
+++ b/addons/missile_sdb/CfgMagazines.hpp
@@ -0,0 +1,17 @@
+class CfgMagazines {
+ class magazine_Bomb_SDB_x1;
+ class PylonRack_Bomb_SDB_x4;
+ class GVAR(magazine_bomb_SDB_x1): magazine_Bomb_SDB_x1 {
+ displayName = CSTRING(39_1x);
+ author = "Dani (TCVM)";
+ ammo = QGVAR(sdb);
+ };
+
+ class GVAR(PylonRack_bomb_SDB_x4): PylonRack_Bomb_SDB_x4 {
+ displayName = CSTRING(39_4x);
+ author = "Dani (TCVM)";
+ ammo = QGVAR(sdb);
+ pylonWeapon = QGVAR(sdb);
+ };
+};
+
diff --git a/addons/missile_sdb/CfgWeapons.hpp b/addons/missile_sdb/CfgWeapons.hpp
new file mode 100644
index 00000000000..1d50c487a0d
--- /dev/null
+++ b/addons/missile_sdb/CfgWeapons.hpp
@@ -0,0 +1,12 @@
+class CfgWeapons {
+ class weapon_SDBLauncher;
+ class GVAR(sdb): weapon_SDBLauncher {
+ author = "Dani (TCVM)";
+ displayName = CSTRING(39);
+ magazines[] = {
+ QGVAR(magazine_bomb_SDB_x1),
+ QGVAR(PylonRack_bomb_SDB_x4)
+ };
+ };
+};
+
diff --git a/addons/missile_sdb/README.md b/addons/missile_sdb/README.md
new file mode 100644
index 00000000000..5ac322e77b4
--- /dev/null
+++ b/addons/missile_sdb/README.md
@@ -0,0 +1,4 @@
+ace_missile_sdb
+===================
+
+Adds GBU-39 SDB
diff --git a/addons/missile_sdb/config.cpp b/addons/missile_sdb/config.cpp
new file mode 100644
index 00000000000..2313ddc12c5
--- /dev/null
+++ b/addons/missile_sdb/config.cpp
@@ -0,0 +1,20 @@
+#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[] = {"tcvm"};
+ url = ECSTRING(main,URL);
+ VERSION_CONFIG;
+ };
+};
+
+#include "CfgAmmo.hpp"
+#include "CfgMagazines.hpp"
+#include "CfgWeapons.hpp"
+
diff --git a/addons/missile_sdb/script_component.hpp b/addons/missile_sdb/script_component.hpp
new file mode 100644
index 00000000000..726ffa5592e
--- /dev/null
+++ b/addons/missile_sdb/script_component.hpp
@@ -0,0 +1,18 @@
+#define COMPONENT missile_sdb
+#define COMPONENT_BEAUTIFIED Small Diameter Bomb
+#include "\z\ace\addons\main\script_mod.hpp"
+
+// #define DEBUG_MODE_FULL
+// #define DISABLE_COMPILE_CACHE
+// #define ENABLE_PERFORMANCE_COUNTERS
+
+#ifdef DEBUG_ENABLED_MISSILE_SDB
+ #define DEBUG_MODE_FULL
+#endif
+
+#ifdef DEBUG_SETTINGS_MISSILE_SDB
+ #define DEBUG_SETTINGS DEBUG_SETTINGS_MISSILE_SDB
+#endif
+
+#include "\z\ace\addons\main\script_macros.hpp"
+
diff --git a/addons/missile_sdb/stringtable.xml b/addons/missile_sdb/stringtable.xml
new file mode 100644
index 00000000000..5e2e326cebd
--- /dev/null
+++ b/addons/missile_sdb/stringtable.xml
@@ -0,0 +1,14 @@
+
+
+
+
+ GBU-39 [ACE]
+
+
+ 1x GBU-39 [ACE]
+
+
+ 4x GBU-39 [ACE]
+
+
+
diff --git a/addons/missileguidance/ACE_GuidanceConfig.hpp b/addons/missileguidance/ACE_GuidanceConfig.hpp
index f3c3b4b0c3e..2fff1d1ad5e 100644
--- a/addons/missileguidance/ACE_GuidanceConfig.hpp
+++ b/addons/missileguidance/ACE_GuidanceConfig.hpp
@@ -55,6 +55,14 @@ class GVAR(AttackProfiles) {
functionName = QFUNC(attackProfile_BEAM);
onFired = QFUNC(wire_onFired); // since Beam guidance is pretty much the same as Wire guidance, we can reuse this
};
+ class JDAM {
+ name = "";
+ visualName = "";
+ description = "";
+
+ functionName = QFUNC(attackProfile_JDAM);
+ onFired = QFUNC(gps_attackOnFired);
+ };
};
class GVAR(SeekerTypes) {
@@ -96,6 +104,14 @@ class GVAR(SeekerTypes) {
functionName = QFUNC(seekerType_MWR);
onFired = QFUNC(mwr_onFired);
};
+ class GPS {
+ name = "";
+ visualName = "";
+ description = "";
+
+ functionName = QFUNC(seekerType_GPS);
+ onFired = QFUNC(gps_seekerOnFired);
+ };
class IR {
name = "";
visualName = "";
diff --git a/addons/missileguidance/GPSDialog.hpp b/addons/missileguidance/GPSDialog.hpp
new file mode 100644
index 00000000000..e6d383d38d6
--- /dev/null
+++ b/addons/missileguidance/GPSDialog.hpp
@@ -0,0 +1,247 @@
+#include "\a3\ui_f\hpp\defineCommonGrids.inc"
+#include "\a3\ui_f\hpp\defineCommonColors.inc"
+#include "idc_defines.hpp"
+
+class RscText;
+class RscEdit;
+class RscPicture;
+class RscButton;
+class ctrlXSliderH;
+
+class GVAR(gpsAttackOptionsUI) {
+ idd = -1;
+ movingEnable = 1;
+ enableSimulation = 1;
+ onLoad = QUOTE(_this call FUNC(gps_onLoad));
+ onUnload = QUOTE(call FUNC(gps_onUnload));
+ class controlsBackground {
+ class Header: RscText {
+ idc = -1;
+ text = CSTRING(GPS_ui_header);
+ x = QUOTE(13.5 * GUI_GRID_W + GUI_GRID_CENTER_X);
+ y = QUOTE(5 * GUI_GRID_H + GUI_GRID_CENTER_Y);
+ w = QUOTE(18 * GUI_GRID_W);
+ h = QUOTE(GUI_GRID_H);
+ colorBackground[] = GUI_BCG_COLOR;
+ moving = 1;
+ };
+ class Background: RscText {
+ idd = -1;
+ x = QUOTE(13.5 * GUI_GRID_W + GUI_GRID_CENTER_X);
+ y = QUOTE(6.1 * GUI_GRID_H + GUI_GRID_CENTER_Y);
+ w = QUOTE(18 * GUI_GRID_W);
+ h = QUOTE(8.0 * GUI_GRID_H);
+ colorBackground[] = {0, 0, 0, 0.8};
+ };
+ };
+ class controls {
+ class TOOButton: RscButton {
+ idc = GPS_UI_TOO;
+ text = CSTRING(GPS_ui_too);
+ onButtonClick = QUOTE([GPS_UI_TOO] call FUNC(gps_modeSelect));
+ x = QUOTE(14 * GUI_GRID_W + GUI_GRID_CENTER_X);
+ y = QUOTE(6.3 * GUI_GRID_H + GUI_GRID_CENTER_Y);
+ w = QUOTE(10 * GUI_GRID_W);
+ h = QUOTE(GUI_GRID_H);
+ colorActive[] = {0, 0, 0, 1};
+ colorBackground[] = GUI_BCG_COLOR;
+ colorFocused[] = {0, 0, 0, 0.8};
+ };
+
+ class PPButton: RscButton {
+ idc = GPS_UI_PB;
+ text = CSTRING(GPS_ui_pp);
+ onButtonClick = QUOTE([GPS_UI_PB] call FUNC(gps_modeSelect));
+ x = QUOTE(26 * GUI_GRID_W + GUI_GRID_CENTER_X);
+ y = QUOTE(6.3 * GUI_GRID_H + GUI_GRID_CENTER_Y);
+ w = QUOTE(5 * GUI_GRID_W);
+ h = QUOTE(GUI_GRID_H);
+ colorActive[] = {0, 0, 0, 1};
+ colorBackground[] = GUI_BCG_COLOR;
+ colorFocused[] = {0, 0, 0, 0.8};
+ };
+
+ class PPMission: RscText {
+ idc = GPS_UI_PB_MISSION;
+ text = CSTRING(GPS_ui_pp_short);
+ onButtonClick = "";
+ x = QUOTE(27.5 * GUI_GRID_W + GUI_GRID_CENTER_X);
+ y = QUOTE(7.5 * GUI_GRID_H + GUI_GRID_CENTER_Y);
+ w = QUOTE(2 * GUI_GRID_W);
+ h = QUOTE(GUI_GRID_H);
+ colorActive[] = {0, 0, 0, 1};
+ colorBackground[] = {0, 0, 0, 1};
+ colorFocused[] = {0, 0, 0, 0.8};
+ align = QUOTE(CENTER);
+ style = 2;
+ };
+
+ class PPMissionBackward: RscButton {
+ idc = GPS_UI_PB_MISSION_BACKWARD;
+ text = "<<";
+ onButtonClick = QUOTE([-1] call FUNC(gps_pbModeCycle));
+ x = QUOTE(26 * GUI_GRID_W + GUI_GRID_CENTER_X);
+ y = QUOTE(7.5 * GUI_GRID_H + GUI_GRID_CENTER_Y);
+ w = QUOTE(1 * GUI_GRID_W);
+ h = QUOTE(GUI_GRID_H);
+ colorActive[] = {0, 0, 0, 1};
+ colorBackground[] = {0, 0, 0, 0.8};
+ colorFocused[] = {0, 0, 0, 0.8};
+ };
+
+ class PPMissionForward: RscButton {
+ idc = GPS_UI_PB_MISSION_FORWARD;
+ text = ">>";
+ onButtonClick = QUOTE([1] call FUNC(gps_pbModeCycle));
+ x = QUOTE(30 * GUI_GRID_W + GUI_GRID_CENTER_X);
+ y = QUOTE(7.5 * GUI_GRID_H + GUI_GRID_CENTER_Y);
+ w = QUOTE(1 * GUI_GRID_W);
+ h = QUOTE(GUI_GRID_H);
+ colorActive[] = {0, 0, 0, 1};
+ colorBackground[] = {0, 0, 0, 0.8};
+ colorFocused[] = {0, 0, 0, 0.8};
+ };
+
+ class TargetPosXText: RscText {
+ idc = -1;
+ text = CSTRING(GPS_ui_easting);
+ x = QUOTE(14 * GUI_GRID_W + GUI_GRID_CENTER_X);
+ y = QUOTE(8.5 * GUI_GRID_H + GUI_GRID_CENTER_Y);
+ w = QUOTE(3 * GUI_GRID_W);
+ h = QUOTE(GUI_GRID_H);
+ colorActive[] = {0, 0, 0, 1};
+ colorBackground[] = {0, 0, 0, 0};
+ colorFocused[] = {0, 0, 0, 0.8};
+ maxChars = 10;
+ };
+ class TargetPosX: RscEdit {
+ idc = GPS_UI_EASTING;
+ text = "";
+ x = QUOTE(17.3 * GUI_GRID_W + GUI_GRID_CENTER_X);
+ y = QUOTE(8.5 * GUI_GRID_H + GUI_GRID_CENTER_Y);
+ w = QUOTE(2.5 * GUI_GRID_W);
+ h = QUOTE(GUI_GRID_H);
+ colorActive[] = {0, 0, 0, 1};
+ colorBackground[] = {0, 0, 0, 0.8};
+ colorFocused[] = {0, 0, 0, 0.8};
+ maxChars = 5;
+ };
+
+ class TargetPosYText: RscText {
+ idc = -1;
+ text = CSTRING(GPS_ui_northing);
+ x = QUOTE(14 * GUI_GRID_W + GUI_GRID_CENTER_X);
+ y = QUOTE(9.7 * GUI_GRID_H + GUI_GRID_CENTER_Y);
+ w = QUOTE(3 * GUI_GRID_W);
+ h = QUOTE(GUI_GRID_H);
+ colorActive[] = {0, 0, 0, 1};
+ colorBackground[] = {0, 0, 0, 0};
+ colorFocused[] = {0, 0, 0, 0.8};
+ maxChars = 5;
+ };
+ class TargetPosY: TargetPosX {
+ idc = GPS_UI_NORTHING;
+ x = QUOTE(17.3 * GUI_GRID_W + GUI_GRID_CENTER_X);
+ y = QUOTE(9.7 * GUI_GRID_H + GUI_GRID_CENTER_Y);
+ };
+
+ class TargetPosHeightText: RscText {
+ idc = -1;
+ text = CSTRING(GPS_ui_altitude);
+ x = QUOTE(14 * GUI_GRID_W + GUI_GRID_CENTER_X);
+ y = QUOTE(11.7 * GUI_GRID_H + GUI_GRID_CENTER_Y);
+ w = QUOTE(3 * GUI_GRID_W);
+ h = QUOTE(GUI_GRID_H);
+ colorActive[] = {0, 0, 0, 1};
+ colorBackground[] = {0, 0, 0, 0};
+ colorFocused[] = {0, 0, 0, 0.8};
+ maxChars = 5;
+ };
+ class TargetPosHeight: TargetPosX {
+ idc = GPS_UI_HEIGHT;
+ x = QUOTE(17.3 * GUI_GRID_W + GUI_GRID_CENTER_X);
+ y = QUOTE(11.7 * GUI_GRID_H + GUI_GRID_CENTER_Y);
+ };
+ class TargetPosHeightUnits: RscText {
+ idc = -1;
+ text = "m";
+ x = QUOTE(19.6 * GUI_GRID_W + GUI_GRID_CENTER_X);
+ y = QUOTE(11.7 * GUI_GRID_H + GUI_GRID_CENTER_Y);
+ w = QUOTE(1 * GUI_GRID_W);
+ h = QUOTE(GUI_GRID_H);
+ colorActive[] = {0, 0, 0, 1};
+ colorBackground[] = {0, 0, 0, 0};
+ colorFocused[] = {0, 0, 0, 0.8};
+ maxChars = 5;
+ };
+
+ class ImpactAngleText: RscText {
+ idc = -1;
+ text = CSTRING(GPS_ui_impact_angle);
+ x = QUOTE(21 * GUI_GRID_W + GUI_GRID_CENTER_X);
+ y = QUOTE(9.7 * GUI_GRID_H + GUI_GRID_CENTER_Y);
+ w = QUOTE(4 * GUI_GRID_W);
+ h = QUOTE(GUI_GRID_H);
+ colorActive[] = {0, 0, 0, 1};
+ colorBackground[] = {0, 0, 0, 0};
+ colorFocused[] = {0, 0, 0, 0.8};
+ maxChars = 5;
+ };
+ class ImpactAngle: RscEdit {
+ idc = GPS_UI_ANGLE;
+ x = QUOTE(26.3 * GUI_GRID_W + GUI_GRID_CENTER_X);
+ y = QUOTE(9.7 * GUI_GRID_H + GUI_GRID_CENTER_Y);
+ w = QUOTE(2 * GUI_GRID_W);
+ h = QUOTE(GUI_GRID_H);
+ text = "";
+ colorActive[] = {0, 0, 0, 1};
+ colorBackground[] = {0, 0, 0, 0.8};
+ colorFocused[] = {0, 0, 0, 0.8};
+ maxChars = 2;
+ };
+
+ class AttackHeadingText: RscText {
+ idc = -1;
+ text = CSTRING(GPS_ui_attack_heading);
+ x = QUOTE(21 * GUI_GRID_W + GUI_GRID_CENTER_X);
+ y = QUOTE(11 * GUI_GRID_H + GUI_GRID_CENTER_Y);
+ w = QUOTE(5 * GUI_GRID_W);
+ h = QUOTE(GUI_GRID_H);
+ colorActive[] = {0, 0, 0, 1};
+ colorBackground[] = {0, 0, 0, 0};
+ colorFocused[] = {0, 0, 0, 0.8};
+ maxChars = 5;
+ };
+ class AttackHeading: RscEdit {
+ idc = GPS_UI_HEADING;
+ x = QUOTE(26.3 * GUI_GRID_W + GUI_GRID_CENTER_X);
+ y = QUOTE(11 * GUI_GRID_H + GUI_GRID_CENTER_Y);
+ w = QUOTE(2 * GUI_GRID_W);
+ h = QUOTE(GUI_GRID_H);
+ text = "";
+ colorActive[] = {0, 0, 0, 1};
+ colorBackground[] = {0, 0, 0, 0.8};
+ colorFocused[] = {0, 0, 0, 0.8};
+ maxChars = 3;
+ };
+
+ class CancelButton: RscButton {
+ idc = -1;
+ text = ECSTRING(common,cancel);
+ onButtonClick = QUOTE(closeDialog 0);
+ x = QUOTE(13.5 * GUI_GRID_W + GUI_GRID_CENTER_X);
+ y = QUOTE(14.2 * GUI_GRID_H + GUI_GRID_CENTER_Y);
+ w = QUOTE(5 * GUI_GRID_W);
+ h = QUOTE(GUI_GRID_H);
+ colorActive[] = {0, 0, 0, 1};
+ colorBackground[] = {0, 0, 0, 0.8};
+ colorFocused[] = {0, 0, 0, 0.8};
+ };
+ class ConfirmButton: CancelButton {
+ idc = IDC_TIMER_CONFIRM;
+ text = ECSTRING(common,confirm);
+ onButtonClick = QUOTE([] call FUNC(gps_confirm));
+ x = QUOTE(27.5 * GUI_GRID_W + GUI_GRID_CENTER_X);
+ };
+ };
+};
diff --git a/addons/missileguidance/XEH_PREP.hpp b/addons/missileguidance/XEH_PREP.hpp
index 8e6cda1373c..66e4d2ce9b8 100644
--- a/addons/missileguidance/XEH_PREP.hpp
+++ b/addons/missileguidance/XEH_PREP.hpp
@@ -30,7 +30,7 @@ PREP(attackProfile_LIN);
PREP(attackProfile_LOFT);
PREP(attackProfile_WIRE);
PREP(attackProfile_BEAM);
-//re-enable after feature merge - PREP(attackProfile_JDAM);
+PREP(attackProfile_JDAM);
// Javelin profiles
PREP(attackProfile_JAV_DIR);
@@ -52,19 +52,33 @@ PREP(seekerType_SACLOS);
PREP(seekerType_MCLOS);
PREP(seekerType_Doppler);
PREP(seekerType_MWR);
+PREP(seekerType_GPS);
PREP(seekerType_IR);
// Attack Profiles OnFired
PREP(wire_onFired);
+PREP(gps_attackOnFired);
// Seeker OnFired
PREP(doppler_onFired);
PREP(SACLOS_onFired);
PREP(MCLOS_onFired);
PREP(mwr_onFired);
+PREP(gps_seekerOnFired);
PREP(IR_onFired);
// Navigation OnFired
PREP(proNav_onFired);
PREP(line_onFired);
+// GPS ui
+PREP(gps_onLoad);
+PREP(gps_onUnload);
+PREP(gps_pbModeCycle);
+PREP(gps_confirm);
+PREP(gps_modeSelect);
+PREP(gps_saveAttackSettings);
+PREP(gps_loadAttackSettings);
+PREP(gps_getAttackData);
+PREP(gps_setupVehicle);
+
diff --git a/addons/missileguidance/XEH_postInit.sqf b/addons/missileguidance/XEH_postInit.sqf
index 1a8b37b5eb4..c85bd5aac29 100644
--- a/addons/missileguidance/XEH_postInit.sqf
+++ b/addons/missileguidance/XEH_postInit.sqf
@@ -6,9 +6,7 @@
["ACE3 Weapons", QGVAR(cycleFireMode), LLSTRING(CycleFireMode), {
[] call FUNC(cycleAttackProfileKeyDown)
}, {
-},
-[DIK_TAB, [false, true, false]], false] call CBA_fnc_addKeybind; //Ctrl+Tab Key
-
+}, [DIK_TAB, [false, true, false]], false] call CBA_fnc_addKeybind; //Ctrl+Tab Key
// Each MCLOS argument is the vector which acceleration will be applied
["ACE3 Weapons", QGVAR(mclosUp), LLSTRING(mclosUp), {
@@ -38,3 +36,19 @@
[[1, 0, 0], ACE_player] call FUNC(MCLOS_buttonPressed)
},
[DIK_NUMPAD4, [false, false, false]], false, 0] call CBA_fnc_addKeybind; // Numpad 4
+
+if (!hasInterface) exitWith {};
+
+["ace_settingsInitialized", {
+ ["turret", LINKFUNC(gps_setupVehicle), false] call CBA_fnc_addPlayerEventHandler;
+ ["vehicle", LINKFUNC(gps_setupVehicle), true] call CBA_fnc_addPlayerEventHandler; // only one of these needs the retro flag
+
+ // Add UAV Control Compatibility
+ ["ACE_controlledUAV", {
+ params ["_UAV", "_seatAI", "_turret", "_position"];
+ TRACE_4("ACE_controlledUAV EH",_UAV,_seatAI,_turret,_position);
+ if (!isNull _seatAI) then {
+ [_seatAI] call FUNC(gps_setupVehicle);
+ };
+ }] call CBA_fnc_addEventHandler;
+}] call CBA_fnc_addEventHandler;
diff --git a/addons/missileguidance/XEH_preInit.sqf b/addons/missileguidance/XEH_preInit.sqf
index 9b1a85e37c7..b6a465d9451 100644
--- a/addons/missileguidance/XEH_preInit.sqf
+++ b/addons/missileguidance/XEH_preInit.sqf
@@ -11,6 +11,21 @@ PREP_RECOMPILE_END;
// As weapons take config changes, there is little point in being able to disable guidance
if (isNil QGVAR(enabled)) then { GVAR(enabled) = 2; };
+GVAR(gps_currentSettings) = [
+ [0, 0, 0], // attack position
+ -1, // impact angle
+ -1 // attack heading
+];
+
+GVAR(gps_pbMode) = 0;
+GVAR(gps_settings) = createHashMap;
+for "_i" from 0 to MAX_PB_MODES do {
+ GVAR(gps_settings) set [_i, GVAR(currentSettings)];
+};
+
+GVAR(gps_mode) = "pb";
+GVAR(gps_weapons) = createHashMap;
+
GVAR(debug_enableMissileCamera) = false;
GVAR(debug_drawGuidanceInfo) = false;
diff --git a/addons/missileguidance/config.cpp b/addons/missileguidance/config.cpp
index 2b4a597c476..bab2502fb6d 100644
--- a/addons/missileguidance/config.cpp
+++ b/addons/missileguidance/config.cpp
@@ -21,7 +21,7 @@ class CfgPatches {
#include "CfgAmmo.hpp"
#include "CfgMagazines.hpp"
#include "CfgWeapons.hpp"
+#include "GPSDialog.hpp"
#ifdef CREATE_MOCK_PLATFORMS
#include "dev\mock_vehicles.hpp"
-#endif
-
+#endif
\ No newline at end of file
diff --git a/addons/missileguidance/functions/fnc_attackProfile_JDAM.sqf b/addons/missileguidance/functions/fnc_attackProfile_JDAM.sqf
new file mode 100644
index 00000000000..b10ece08dc4
--- /dev/null
+++ b/addons/missileguidance/functions/fnc_attackProfile_JDAM.sqf
@@ -0,0 +1,60 @@
+#include "..\script_component.hpp"
+/*
+ * Author: tcvm
+ * Attack profile: JDAM
+ * Uses LINE navigation type to guide projectile onto target
+ *
+ * Arguments:
+ * 0: Seeker Target PosASL
+ * 1: Guidance Arg Array
+ * 2: Seeker State
+ *
+ * Return Value:
+ * Missile Aim PosASL
+ *
+ * Example:
+ * [[1,2,3], [], []] call ace_missileguidance_fnc_attackProfile_jdam;
+ *
+ * Public: No
+ */
+params ["_seekerTargetPos", "_args", "_attackProfileStateParams", "_timestep"];
+_args params ["_firedEH", "", "_flightParams", "", "", "_targetData"];
+_firedEH params ["_shooter","","","","","","_projectile"];
+_attackProfileStateParams params ["_gpsData", "_initialProjectileHeight", "_terminal"];
+_gpsData params ["", "_impactAngle", "_attackDirection"];
+_targetData params ["_directionToTarget", "", "_distanceToTarget"];
+_flightParams params ["_pitchRate", "_yawRate"];
+
+if (_impactAngle <= 0) then {
+ _impactAngle = 45; // immediate pitch over to attack
+};
+
+if (_attackDirection < 0) then {
+ _attackDirection = direction _projectile;
+ _gpsData set [2, _attackDirection];
+};
+
+private _projectilePos = getPosASLVisual _projectile;
+private _lineDir = [1, 180 + _attackDirection, _impactAngle] call CBA_fnc_polar2vect;
+
+private _leadDistance = linearConversion [0, 1000, _projectilePos vectorDistance _seekerTargetPos, 5, 500, true];
+
+private _missilePosProjected = _seekerTargetPos vectorAdd (_lineDir vectorMultiply ((_projectilePos vectorDistance _seekerTargetPos) - _leadDistance));
+// don't climb
+if (_missilePosProjected#2 > _projectilePos#2) then {
+ _missilePosProjected set [2, _projectilePos#2];
+};
+_targetData set [2, (_projectilePos vectorDistance _missilePosProjected)];
+
+if (GVAR(debug_drawGuidanceInfo)) then {
+ private _projectilePitch = ((vectorDir _projectile) call CBA_fnc_vect2polar) select 2;
+ //IGNORE_PRIVATE_WARNING ["_attackProfileName"];
+ _attackProfileName = format ["JDAM [Pitch - %1 Impact Pitch - %2]", _projectilePitch, _impactAngle];
+ drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [1,0,0,1], ASLToAGL _missilePosProjected, 0.75, 0.75, 0, "P", 1, 0.025, "TahomaB"];
+ private _desiredAngle = _lineDir vectorMultiply 5000;
+ private _targetPosAGL = ASLToAGL _seekerTargetPos;
+ drawLine3D [_targetPosAGL, _targetPosAGL vectorAdd _desiredAngle, [1, 1, 1, 1]];
+};
+
+_missilePosProjected
+
diff --git a/addons/missileguidance/functions/fnc_gps_attackOnFired.sqf b/addons/missileguidance/functions/fnc_gps_attackOnFired.sqf
new file mode 100644
index 00000000000..677148e4fbb
--- /dev/null
+++ b/addons/missileguidance/functions/fnc_gps_attackOnFired.sqf
@@ -0,0 +1,23 @@
+#include "..\script_component.hpp"
+/*
+ * Author: tcvm
+ * Sets up wireGuided state arrays (called from missileGuidance's onFired).
+ *
+ * Arguments:
+ * Guidance Arg Array
+ *
+ * Return Value:
+ * None
+ *
+ * Example:
+ * [] call ace_missileguidance_fnc_gps_attackOnFired
+ *
+ * Public: No
+ */
+params ["_firedEH", "", "", "", "_stateParams", "", ""];
+_stateParams params ["", "", "_attackProfileStateParams"];
+_firedEH params ["_shooter","","","","_ammo","","_projectile"];
+
+_attackProfileStateParams set [0, [] call FUNC(gps_getAttackData)];
+_attackProfileStateParams set [1, (getPosASL _projectile) select 2];
+_attackProfileStateParams set [2, false];
diff --git a/addons/missileguidance/functions/fnc_gps_confirm.sqf b/addons/missileguidance/functions/fnc_gps_confirm.sqf
new file mode 100644
index 00000000000..eeb440587bc
--- /dev/null
+++ b/addons/missileguidance/functions/fnc_gps_confirm.sqf
@@ -0,0 +1,21 @@
+#include "..\script_component.hpp"
+/*
+ * Author: tcvm
+ * Confirm GPS weapon settings and serialize to variables
+ *
+ * Arguments:
+ * Guidance Arg Array
+ *
+ * Return Value:
+ * None
+ *
+ * Example:
+ * [] call ace_missileguidance_fnc_gps_confirm
+ *
+ * Public: No
+ */
+if (GVAR(gps_mode) isEqualTo "pb") then {
+ [GVAR(gps_pbMode)] call FUNC(gps_saveAttackSettings);
+};
+closeDialog 0;
+
diff --git a/addons/missileguidance/functions/fnc_gps_getAttackData.sqf b/addons/missileguidance/functions/fnc_gps_getAttackData.sqf
new file mode 100644
index 00000000000..7101cefd9fb
--- /dev/null
+++ b/addons/missileguidance/functions/fnc_gps_getAttackData.sqf
@@ -0,0 +1,26 @@
+#include "..\script_component.hpp"
+/*
+ * Author: tcvm
+ * Returns attack data for GPS guided bomb
+ *
+ * Arguments:
+ * None
+ *
+ * Return Value:
+ * None
+ *
+ * Example:
+ * [] call ace_missileguidance_fnc_gps_getAttackData
+ *
+ * Public: No
+ */
+
+if (GVAR(gps_mode) isEqualTo "too") then {
+ private _target = getPilotCameraTarget (vehicle ACE_PLAYER);
+ _target params ["_tracking", "_position", "_object"];
+ GVAR(gps_currentSettings) set [0, _position]
+};
+
+// create a copy of this array to make sure values are not overwritten
++GVAR(gps_currentSettings) // return
+
diff --git a/addons/missileguidance/functions/fnc_gps_loadAttackSettings.sqf b/addons/missileguidance/functions/fnc_gps_loadAttackSettings.sqf
new file mode 100644
index 00000000000..adff47c376f
--- /dev/null
+++ b/addons/missileguidance/functions/fnc_gps_loadAttackSettings.sqf
@@ -0,0 +1,53 @@
+#include "..\script_component.hpp"
+/*
+ * Author: tcvm
+ * Saves all textboxes to whatever settings say for PB mode
+ *
+ * Arguments:
+ * Current PB mode
+ *
+ * Return Value:
+ * None
+ *
+ * Example:
+ * [] call ace_missileguidance_fnc_gps_saveAttackSettings
+ *
+ * Public: No
+ */
+params ["_mode"];
+private _display = uiNamespace getVariable QGVAR(gpsAttackOptionDisplay);
+
+private _settings = GVAR(gps_settings) get _mode;
+
+_settings params ["_position", "_angle", "_heading"];
+private _height = _position select 2;
+
+private _grid = [_position] call EFUNC(common,getMapGridFromPos);
+_grid params ["_easting", "_northing"];
+
+if (_angle < 0) then {
+ _angle = "";
+} else {
+ _angle = str _angle;
+};
+
+if (_heading < 0) then {
+ _heading = "";
+} else {
+ _heading = str _heading;
+};
+
+if (0 == parseNumber _easting) then {
+ _easting = "";
+};
+
+if (0 == parseNumber _northing) then {
+ _northing = "";
+};
+
+(_display displayCtrl GPS_UI_EASTING) ctrlSetText _easting;
+(_display displayCtrl GPS_UI_NORTHING) ctrlSetText _northing;
+(_display displayCtrl GPS_UI_HEIGHT) ctrlSetText str _height;
+(_display displayCtrl GPS_UI_ANGLE) ctrlSetText _angle;
+(_display displayCtrl GPS_UI_HEADING) ctrlSetText _heading;
+
diff --git a/addons/missileguidance/functions/fnc_gps_modeSelect.sqf b/addons/missileguidance/functions/fnc_gps_modeSelect.sqf
new file mode 100644
index 00000000000..ef7d4ecbe75
--- /dev/null
+++ b/addons/missileguidance/functions/fnc_gps_modeSelect.sqf
@@ -0,0 +1,56 @@
+#include "..\script_component.hpp"
+#include "\a3\ui_f\hpp\defineCommonColors.inc"
+/*
+ * Author: tcvm
+ * Select either TOO or PB mode. Disable anything that shouldnt be touched in TOO and renable in PB
+ *
+ * Arguments:
+ * Mode to switch to
+ *
+ * Return Value:
+ * None
+ *
+ * Example:
+ * [0] call ace_missileguidance_fnc_gps_modeSelect
+ *
+ * Public: No
+ */
+params ["_mode", ["_onLoad", false]];
+private _display = uiNamespace getVariable QGVAR(gpsAttackOptionDisplay);
+
+{
+ // only TOO and PB modes modelled
+ if (_mode == GPS_UI_TOO) then {
+ // disable
+ ctrlEnable [_x, false];
+ } else {
+ // enable
+ ctrlEnable [_x, true];
+ };
+} forEach CONTROLS_DISABLED_IN_TOO;
+
+private _backgroundColour = [
+ GUI_BCG_RGB_R call BIS_fnc_parseNumber,
+ GUI_BCG_RGB_G call BIS_fnc_parseNumber,
+ GUI_BCG_RGB_B call BIS_fnc_parseNumber,
+ GUI_BCG_ALPHA call BIS_fnc_parseNumber
+];
+
+private _selectedColour = [
+ 0,
+ 0,
+ 0,
+ 1
+];
+
+ctrlSetFocus (_display displayCtrl _mode);
+if (_mode == GPS_UI_TOO) then {
+ GVAR(gps_mode) = "too";
+ if !(_onLoad) then {
+ [GVAR(gps_pbMode)] call FUNC(gps_saveAttackSettings);
+ };
+} else {
+ GVAR(gps_mode) = "pb";
+ [GVAR(gps_pbMode)] call FUNC(gps_loadAttackSettings);
+};
+
diff --git a/addons/missileguidance/functions/fnc_gps_onLoad.sqf b/addons/missileguidance/functions/fnc_gps_onLoad.sqf
new file mode 100644
index 00000000000..a35e1d4cdaa
--- /dev/null
+++ b/addons/missileguidance/functions/fnc_gps_onLoad.sqf
@@ -0,0 +1,50 @@
+#include "..\script_component.hpp"
+/*
+ * Author: tcvm
+ * Called on load of GPS UI
+ *
+ * Arguments:
+ * Display
+ *
+ * Return Value:
+ * None
+ *
+ * Example:
+ * [1] call ace_missileguidance_fnc_gps_onLoad
+ *
+ * Public: No
+ */
+[{
+ params ["_display"];
+ TRACE_1("gps_onLoad",_display);
+ uiNamespace setVariable [QGVAR(gpsAttackOptionDisplay), _display];
+
+ private _mode = [GPS_UI_PB, GPS_UI_TOO] select (GVAR(gps_mode) isEqualTo "too");
+
+ [_mode, true] call FUNC(gps_modeSelect);
+ (_display displayCtrl GPS_UI_PB_MISSION) ctrlSetText format ["%1 %2", localize LSTRING(GPS_ui_pp_short), GVAR(gps_pbMode) + 1];
+
+ // update current settings
+ GVAR(gps_uiPerFrameHandler) = [{
+ if (GVAR(gps_mode) isEqualTo "too") then {
+ // update coordinates based on TGP position
+ private _target = getPilotCameraTarget (vehicle ACE_PLAYER);
+ _target params ["_tracking", "_position", "_object"];
+
+ if (_position isNotEqualTo [0, 0, 0]) then {
+ private _mapGrid = [_position] call EFUNC(common,getMapGridFromPos);
+ _mapGrid params ["_easting", "_northing"];
+ private _height = _position#2;
+
+ private _display = uiNamespace getVariable QGVAR(gpsAttackOptionDisplay);
+ (_display displayCtrl GPS_UI_EASTING) ctrlSetText _easting;
+ (_display displayCtrl GPS_UI_NORTHING) ctrlSetText _northing;
+ (_display displayCtrl GPS_UI_HEIGHT) ctrlSetText str _height;
+ };
+ };
+
+ // info is read from text boxes, so if boxes are update this will be updated
+ GVAR(gps_currentSettings) = [-1] call FUNC(gps_saveAttackSettings);
+ }] call CBA_fnc_addPerFrameHandler;
+}, _this] call CBA_fnc_execNextFrame;
+
diff --git a/addons/missileguidance/functions/fnc_gps_onUnload.sqf b/addons/missileguidance/functions/fnc_gps_onUnload.sqf
new file mode 100644
index 00000000000..fbde5fbaef1
--- /dev/null
+++ b/addons/missileguidance/functions/fnc_gps_onUnload.sqf
@@ -0,0 +1,18 @@
+#include "..\script_component.hpp"
+/*
+ * Author: tcvm
+ * Called on unload of GPS UI
+ *
+ * Arguments:
+ * Display
+ *
+ * Return Value:
+ * None
+ *
+ * Example:
+ * [] call ace_missileguidance_fnc_gps_onUnload
+ *
+ * Public: No
+ */
+[GVAR(gps_uiPerFrameHandler)] call CBA_fnc_removePerFrameHandler;
+
diff --git a/addons/missileguidance/functions/fnc_gps_pbModeCycle.sqf b/addons/missileguidance/functions/fnc_gps_pbModeCycle.sqf
new file mode 100644
index 00000000000..ba7256b3212
--- /dev/null
+++ b/addons/missileguidance/functions/fnc_gps_pbModeCycle.sqf
@@ -0,0 +1,36 @@
+#include "..\script_component.hpp"
+/*
+ * Author: tcvm
+ * Cycle pre-briefed mission mode
+ *
+ * Arguments:
+ * Direction
+ *
+ * Return Value:
+ * None
+ *
+ * Example:
+ * [1] call ace_missileguidance_fnc_gps_pbModeCycle
+ *
+ * Public: No
+ */
+params ["_direction"];
+private _display = uiNamespace getVariable QGVAR(gpsAttackOptionDisplay);
+
+[GVAR(gps_pbMode)] call FUNC(gps_saveAttackSettings);
+
+if (_direction > 0) then {
+ // right
+ GVAR(gps_pbMode) = (GVAR(gps_pbMode) + 1) % MAX_PB_MODES
+} else {
+ // left
+ GVAR(gps_pbMode) = (GVAR(gps_pbMode) - 1);
+ if (GVAR(gps_pbMode) < 0) then {
+ GVAR(gps_pbMode) = MAX_PB_MODES - 1;
+ };
+ GVAR(gps_pbMode) = GVAR(gps_pbMode) % MAX_PB_MODES;
+};
+
+[GVAR(gps_pbMode)] call FUNC(gps_loadAttackSettings);
+(_display displayCtrl GPS_UI_PB_MISSION) ctrlSetText format ["%1 %2", localize LSTRING(GPS_ui_pp_short), GVAR(gps_pbMode) + 1];
+
diff --git a/addons/missileguidance/functions/fnc_gps_saveAttackSettings.sqf b/addons/missileguidance/functions/fnc_gps_saveAttackSettings.sqf
new file mode 100644
index 00000000000..d25ff1b8ccb
--- /dev/null
+++ b/addons/missileguidance/functions/fnc_gps_saveAttackSettings.sqf
@@ -0,0 +1,65 @@
+#include "..\script_component.hpp"
+/*
+ * Author: tcvm
+ * Saves all textboxes to whatever settings say for PB mode
+ *
+ * Arguments:
+ * Current PB mode. If -1 no mode is used
+ *
+ * Return Value:
+ * current settings
+ *
+ * Example:
+ * [-1] call ace_missileguidance_fnc_gps_saveAttackSettings
+ *
+ * Public: No
+ */
+params ["_mode"];
+private _display = uiNamespace getVariable QGVAR(gpsAttackOptionDisplay);
+
+private _easting = ctrlText (_display displayCtrl GPS_UI_EASTING);
+private _northing = ctrlText (_display displayCtrl GPS_UI_NORTHING);
+private _height = ctrlText (_display displayCtrl GPS_UI_HEIGHT);
+private _angle = ctrlText (_display displayCtrl GPS_UI_ANGLE);
+private _heading = ctrlText (_display displayCtrl GPS_UI_HEADING);
+
+if (_height isEqualTo "") then {
+ _height = 0
+} else {
+ _height = parseNumber _height;
+};
+
+if (_angle isEqualTo "") then {
+ _angle = -1
+} else {
+ _angle = parseNumber _angle;
+};
+
+if (_heading isEqualTo "") then {
+ _heading = -1
+} else {
+ _heading = parseNumber _heading;
+};
+
+private _minGridCount = (count _easting) min (count _northing);
+
+private _grid = (_easting select [0, _minGridCount]) + (_northing select [0, _minGridCount]);
+private _position = if (_grid isEqualTo "") then {
+ [0, 0, 0]
+} else {
+ [_grid, false] call EFUNC(common,getMapPosFromGrid);
+};
+_position set [2, _height];
+
+TRACE_3("settings gps pos",_position,_angle,_heading);
+
+private _settings = [
+ _position, // attack position
+ _angle, // impact angle
+ _heading // attack heading
+];
+
+if (_mode != -1) then {
+ GVAR(gps_settings) set [_mode, _settings];
+};
+_settings
diff --git a/addons/missileguidance/functions/fnc_gps_seekerOnFired.sqf b/addons/missileguidance/functions/fnc_gps_seekerOnFired.sqf
new file mode 100644
index 00000000000..575df64d9a5
--- /dev/null
+++ b/addons/missileguidance/functions/fnc_gps_seekerOnFired.sqf
@@ -0,0 +1,20 @@
+#include "..\script_component.hpp"
+/*
+ * Author: tcvm
+ * Sets up wireGuided state arrays (called from missileGuidance's onFired).
+ *
+ * Arguments:
+ * Guidance Arg Array
+ *
+ * Return Value:
+ * None
+ *
+ * Example:
+ * [] call ace_missileguidance_fnc_gps_seekerOnFired
+ *
+ * Public: No
+ */
+params ["", "", "", "", "_stateParams", "", ""];
+_stateParams params ["", "_seekerStateParams"];
+
+_seekerStateParams set [0, [] call FUNC(gps_getAttackData)];
diff --git a/addons/missileguidance/functions/fnc_gps_setupVehicle.sqf b/addons/missileguidance/functions/fnc_gps_setupVehicle.sqf
new file mode 100644
index 00000000000..72dffe06492
--- /dev/null
+++ b/addons/missileguidance/functions/fnc_gps_setupVehicle.sqf
@@ -0,0 +1,47 @@
+#include "..\script_component.hpp"
+/*
+ * Author: tcvm
+ * Adds interaction menu actions to configure GPS bombs
+ *
+ * Arguments:
+ * 0: Player