diff --git a/addons/spike/$PBOPREFIX$ b/addons/spike/$PBOPREFIX$ new file mode 100644 index 00000000000..ac6ebcf95df --- /dev/null +++ b/addons/spike/$PBOPREFIX$ @@ -0,0 +1 @@ +z\ace\addons\spike \ No newline at end of file diff --git a/addons/spike/ACE_GuidanceConfig.hpp b/addons/spike/ACE_GuidanceConfig.hpp new file mode 100644 index 00000000000..50a7e62ba4b --- /dev/null +++ b/addons/spike/ACE_GuidanceConfig.hpp @@ -0,0 +1,11 @@ +class EGVAR(missileguidance,SeekerTypes) { + class SPIKE { + functionName = QFUNC(seeker); + }; +}; +class EGVAR(missileguidance,NavigationTypes) { + class SPIKE { + functionName = QFUNC(navigation); + onFired = ""; + }; +}; diff --git a/addons/spike/CfgAmmo.hpp b/addons/spike/CfgAmmo.hpp new file mode 100644 index 00000000000..9b1793ba689 --- /dev/null +++ b/addons/spike/CfgAmmo.hpp @@ -0,0 +1,103 @@ +class CfgAmmo { + class MissileBase; + class M_Titan_AT: MissileBase {}; + class GVAR(lr): M_Titan_AT { + timeToLive = 120; + + manualControl = 0; + initTime = 0.5; + + class ace_missileguidance { + enabled = 1; + + pitchRate = 30; + yawRate = 30; + + canVanillaLock = 0; + + defaultSeekerType = "SPIKE"; + seekerTypes[] = {"SPIKE"}; + + defaultSeekerLockMode = "LOAL"; + seekerLockModes[] = { "LOAL" }; + + defaultNavigationType = "SPIKE"; + navigationTypes[] = { "SPIKE", "ZeroEffortMiss" }; + + seekLastTargetPos = 0; + seekerAngle = 20; + seekerAccuracy = 1; + + seekerMinRange = 50; + seekerMaxRange = 2500; + + defaultAttackProfile = "DIR"; + attackProfiles[] = {"DIR"}; + useModeForAttackProfile = 0; + + onFired = QFUNC(onFired); + + class navigationStates { + class initial { + transitionCondition = QFUNC(midCourseTransition); + navigationType = "SPIKE"; + }; + class terminal { + transitionCondition = ""; + navigationType = "ZeroEffortMiss"; + }; + // transitions from initial -> termimal + states[] = {"initial", "terminal"}; + }; + + // TV Guided projectiles have extra data that is irrelevant to most missiles + class camera { + enabled = 1; + switchOnFire = 1; // switch to the camera view immediately upon firing + + class gimbal { + enabled = 1; + + gimbalAngleX = 25; // how far left/right can this look in degrees + gimbalAngleY = 60; // how far up/down can this look in degrees + gimbalSpeedX = 20; // how many fast we can look left and right + gimbalSpeedY = 20; // how many fast we can look up and down + + fovGimbalSpeedModifiers[] = { 1, 0.1 }; // the modifier for gimbal speed when at the zoom level + + gimbalInitOffsetX = 0; + gimbalInitOffsetY = -20; + + stabilizeWhenMoving = 1; + trackLockedPosition = 1; + designateWhenStationary = 0; // designate when camera doenst have any inputs + }; + + fovLevels[] = { 0.2, 0.05 }; // levels of zoom this has + initialFOV = 0.2; + lerpFOV = 0; + fovChangeTime = 1; + + alwaysDesignate = 0; // always designate + canStopDesignating = 0; + + enabledThermalTypes[] = { "white_hot_black_cold", "black_hot_white_cold" }; + initialThermalType = "white_hot_black_cold"; + + class reticle { + titleRsc = "ACE_guidance_spike"; + centerReticle = 242000; + controlsToDisappearOnLock[] = { 241000 }; + controlsToAppearOnLock[] = { 243101, 243201, 243301 }; + leftGate = 243200; + rightGate = 243300; + topGate = 0; + bottomGate = 243100; + uiNamespaceDialogVariable = "ACE_guidance_camera_reticle"; + reticleMovesWithTrack = 1; + }; + }; + }; + }; +}; + diff --git a/addons/spike/CfgEventHandlers.hpp b/addons/spike/CfgEventHandlers.hpp new file mode 100644 index 00000000000..35dcb3a0290 --- /dev/null +++ b/addons/spike/CfgEventHandlers.hpp @@ -0,0 +1,18 @@ +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/spike/CfgMagazines.hpp b/addons/spike/CfgMagazines.hpp new file mode 100644 index 00000000000..01ee40d776e --- /dev/null +++ b/addons/spike/CfgMagazines.hpp @@ -0,0 +1,12 @@ +class CfgMagazines { + class Titan_AT; + class GVAR(lr): Titan_AT { + author = "Dani (TCVM)"; + ammo = QGVAR(lr); + + displayName = CSTRING(LR); + displayNameShort = CSTRING(LR_Short); + mass = 300; + }; +}; + diff --git a/addons/spike/CfgWeapons.hpp b/addons/spike/CfgWeapons.hpp new file mode 100644 index 00000000000..89f658ee82c --- /dev/null +++ b/addons/spike/CfgWeapons.hpp @@ -0,0 +1,29 @@ +class CfgWeapons { + class launch_Titan_short_base; + class launch_B_Titan_short_F: launch_Titan_short_base { + class Single; + }; + class GVAR(base): launch_B_Titan_short_F { + class Single: Single { + EGVAR(missileGuidance,attackProfile) = "SPIKE_TOP"; + }; + }; + class GVAR(launcher): GVAR(base) { + scope = 2; + GVAR(enabled) = 1; + weaponInfoType = "ACE_RscOptics_spike"; + modelOptics = QPATHTOF(data\reticle_titan.p3d); + + canLock = 0; + + lockingTargetSound[] = {"",0,1}; + lockedTargetSound[] = {"",0,1}; + + displayName = CSTRING(LR); + displayNameShort = CSTRING(LR_Short); + + magazines[] = {QGVAR(lr)}; + magazineWell[] = {}; + }; +}; + diff --git a/addons/spike/README.md b/addons/spike/README.md new file mode 100644 index 00000000000..9576740729c --- /dev/null +++ b/addons/spike/README.md @@ -0,0 +1,4 @@ +ace_spike +=================== + +Adds SPIKE LR guided missile diff --git a/addons/spike/RscInGameUI.hpp b/addons/spike/RscInGameUI.hpp new file mode 100644 index 00000000000..907c1016368 --- /dev/null +++ b/addons/spike/RscInGameUI.hpp @@ -0,0 +1,134 @@ + +// Taken from AGM for optics management. + +class RscInGameUI { + class ACE_RscOptics_spike { + idd = 141000; + controls[] = { "reticle", "manualText", QGVAR(mapHelper) }; + onLoad = QUOTE(with uiNamespace do {ACE_RscOptics_spike = _this select 0;};); + + class GVAR(mapHelper): RscMapControl { + onDraw = QUOTE(_this call FUNC(mapHelperDraw);); + x = 0; + y = 0; + w = 0; + h = 0; + }; + + class manualText: RscText { + idc = 241000; + x = "safeZoneX + safeZoneW * 0.425"; + y = "safeZoneY + safeZoneH * 0.2"; + w = "safeZoneW * 0.15"; + h = "safeZoneH * 0.05"; + style = 0 + 2; + text = CSTRING(Manual); + colorBackground[] = {0,0,0,0}; + colorText[] = COLOR_WHITE; + font = "LucidaConsoleB"; + sizeEx = QUOTE(((((safezoneW / safezoneH) min 1.2) / 1.2) / 25) * 2.2); + enabled = 1; + show = 1; + }; + + class reticle: RscControlsGroupNoScrollbars { + idc = 242000; + x = "safeZoneX"; + y = "safeZoneY"; + w = "safeZoneW-safeZoneX"; + h = "safeZoneH-safeZoneY"; + enabled = 1; + show = 0; + class controls { + class lineV: RscControlsGroupNoScrollbars { + idc = 243100; + enabled = 1; + show = 1; + class Controls { + class lineBlack: RscText { + x = "safeZoneX + (SafeZoneW * 0.501)"; + y = "safeZoneY + (SafeZoneH * 0.53)"; + w = "safeZoneW * 0.0025"; + h = "safeZoneH * 0.1"; + colorBackground[] = COLOR_BLACK; + }; + class lineWhite: RscText { + x = "safeZoneX + (SafeZoneW * 0.504)"; + y = "safeZoneY + (SafeZoneH * 0.53)"; + w = "safeZoneW * 0.0025"; + h = "safeZoneH * 0.1"; + colorBackground[] = COLOR_WHITE; + }; + class squareB: RscText { + idc = 243101; + x = "safeZoneX + safeZoneW * 0.499"; + y = "safeZoneY + safeZoneH * 0.52"; + w = "safeZoneH * 0.006"; + h = "safeZoneW * 0.006"; + colorBackground[] = COLOR_BLACK; + }; + }; + }; + class lineHL: RscControlsGroupNoScrollbars { + idc = 243200; + enabled = 1; + show = 1; + class Controls { + class lineBlack: RscText { + x = "safeZoneY + (SafeZoneH * 0.37)"; + y = "safeZoneX + (SafeZoneW * 0.5)"; + w = "safeZoneH * 0.1"; + h = "safeZoneW * 0.003"; + colorBackground[] = COLOR_BLACK; + }; + class lineWhite: RscText { + x = "safeZoneY + (SafeZoneH * 0.37)"; + y = "safeZoneX + (SafeZoneW * 0.504)"; + w = "safeZoneH * 0.1"; + h = "safeZoneW * 0.0023"; + colorBackground[] = COLOR_WHITE; + }; + class squareL: RscText { + idc = 243201; + x = "safeZoneX + (SafeZoneW * 0.485)"; + y = "safeZoneY + safeZoneH * 0.5"; + w = "safeZoneH * 0.006"; + h = "safeZoneW * 0.006"; + colorBackground[] = COLOR_BLACK; + }; + }; + }; + class lineHR: RscControlsGroupNoScrollbars { + idc = 243300; + enabled = 1; + show = 1; + class Controls { + class lineBlack: RscText { + x = "safeZoneY + (SafeZoneH * 0.53)"; + y = "safeZoneX + (SafeZoneW * 0.5)"; + w = "safeZoneH * 0.1"; + h = "safeZoneW * 0.003"; + colorBackground[] = COLOR_BLACK; + }; + class lineWhite: RscText { + x = "safeZoneY + (SafeZoneH * 0.53)"; + y = "safeZoneX + (SafeZoneW * 0.504)"; + w = "safeZoneH * 0.1"; + h = "safeZoneW * 0.0023"; + colorBackground[] = COLOR_WHITE; + }; + class squareR: RscText { + idc = 243301; + x = "safeZoneX + (SafeZoneW * 0.515)"; + y = "safeZoneY + safeZoneH * 0.5"; + w = "safeZoneH * 0.006"; + h = "safeZoneW * 0.006"; + colorBackground[] = COLOR_BLACK; + }; + }; + }; + }; + }; + }; +}; + diff --git a/addons/spike/RscTitles.hpp b/addons/spike/RscTitles.hpp new file mode 100644 index 00000000000..7eec820cd4b --- /dev/null +++ b/addons/spike/RscTitles.hpp @@ -0,0 +1,128 @@ + +class RscTitles { + class ACE_guidance_spike { + idd = 1299230000; + controls[] = { "reticle", "manualText" }; + duration = 100000; + fadein = 0; + fadeout = 0; + name = "spike_reticle"; + onLoad = QUOTE(with uiNamespace do {ACE_guidance_camera_reticle = _this select 0;};); + + class manualText: RscText { + idc = 241000; + x = "safeZoneX + safeZoneW * 0.425"; + y = "safeZoneY + safeZoneH * 0.2"; + w = "safeZoneW * 0.15"; + h = "safeZoneH * 0.05"; + style = 0 + 2; + text = CSTRING(Manual); + colorBackground[] = {0,0,0,0}; + colorText[] = COLOR_WHITE; + font = "LucidaConsoleB"; + sizeEx = QUOTE(((((safezoneW / safezoneH) min 1.2) / 1.2) / 25) * 2.2); + enabled = 1; + show = 1; + }; + + class reticle: RscControlsGroupNoScrollbars { + idc = 242000; + x = "safeZoneX"; + y = "safeZoneY"; + w = "safeZoneW-safeZoneX"; + h = "safeZoneH-safeZoneY"; + enabled = 1; + show = 1; + class controls { + class lineV: RscControlsGroupNoScrollbars { + idc = 243100; + enabled = 1; + show = 1; + class Controls { + class lineBlack: RscText { + x = "safeZoneX + (SafeZoneW * 0.501)"; + y = "safeZoneY + (SafeZoneH * 0.53)"; + w = "safeZoneW * 0.0025"; + h = "safeZoneH * 0.1"; + colorBackground[] = COLOR_BLACK; + }; + class lineWhite: RscText { + x = "safeZoneX + (SafeZoneW * 0.504)"; + y = "safeZoneY + (SafeZoneH * 0.53)"; + w = "safeZoneW * 0.0025"; + h = "safeZoneH * 0.1"; + colorBackground[] = COLOR_WHITE; + }; + class squareB: RscText { + idc = 243101; + x = "safeZoneX + safeZoneW * 0.499"; + y = "safeZoneY + safeZoneH * 0.52"; + w = "safeZoneH * 0.006"; + h = "safeZoneW * 0.006"; + colorBackground[] = COLOR_BLACK; + }; + }; + }; + class lineHL: RscControlsGroupNoScrollbars { + idc = 243200; + enabled = 1; + show = 1; + class Controls { + class lineBlack: RscText { + x = "safeZoneY + (SafeZoneH * 0.37)"; + y = "safeZoneX + (SafeZoneW * 0.5)"; + w = "safeZoneH * 0.1"; + h = "safeZoneW * 0.003"; + colorBackground[] = COLOR_BLACK; + }; + class lineWhite: RscText { + x = "safeZoneY + (SafeZoneH * 0.37)"; + y = "safeZoneX + (SafeZoneW * 0.504)"; + w = "safeZoneH * 0.1"; + h = "safeZoneW * 0.0023"; + colorBackground[] = COLOR_WHITE; + }; + class squareL: RscText { + idc = 243201; + x = "safeZoneX + (SafeZoneW * 0.485)"; + y = "safeZoneY + safeZoneH * 0.5"; + w = "safeZoneH * 0.006"; + h = "safeZoneW * 0.006"; + colorBackground[] = COLOR_BLACK; + }; + }; + }; + class lineHR: RscControlsGroupNoScrollbars { + idc = 243300; + enabled = 1; + show = 1; + class Controls { + class lineBlack: RscText { + x = "safeZoneY + (SafeZoneH * 0.53)"; + y = "safeZoneX + (SafeZoneW * 0.5)"; + w = "safeZoneH * 0.1"; + h = "safeZoneW * 0.003"; + colorBackground[] = COLOR_BLACK; + }; + class lineWhite: RscText { + x = "safeZoneY + (SafeZoneH * 0.53)"; + y = "safeZoneX + (SafeZoneW * 0.504)"; + w = "safeZoneH * 0.1"; + h = "safeZoneW * 0.0023"; + colorBackground[] = COLOR_WHITE; + }; + class squareR: RscText { + idc = 243301; + x = "safeZoneX + (SafeZoneW * 0.515)"; + y = "safeZoneY + safeZoneH * 0.5"; + w = "safeZoneH * 0.006"; + h = "safeZoneW * 0.006"; + colorBackground[] = COLOR_BLACK; + }; + }; + }; + }; + }; + }; +}; + diff --git a/addons/spike/XEH_PREP.hpp b/addons/spike/XEH_PREP.hpp new file mode 100644 index 00000000000..366d886cdb6 --- /dev/null +++ b/addons/spike/XEH_PREP.hpp @@ -0,0 +1,20 @@ +PREP(camera_changeZoom); +PREP(camera_cycleViewMode); +PREP(camera_destroy); +PREP(camera_handleKeyPress); +PREP(camera_init); +PREP(camera_setViewMode); +PREP(camera_setZoom); +PREP(camera_switchAway); +PREP(camera_switchTo); +PREP(camera_update); +PREP(camera_updateTargetingGate); +PREP(camera_userInCamera); +PREP(getTargetPosition); +PREP(keyDown); +PREP(mapHelperDraw); +PREP(midCourseTransition); +PREP(navigation); +PREP(onFired); +PREP(seeker); + diff --git a/addons/spike/XEH_postInit.sqf b/addons/spike/XEH_postInit.sqf new file mode 100644 index 00000000000..ae629ec91ba --- /dev/null +++ b/addons/spike/XEH_postInit.sqf @@ -0,0 +1,23 @@ +#include "script_component.hpp" + +if (hasInterface) then { + #include "initKeybinds.inc.sqf" + + // add camera interactions + private _switchToCameraAction = ["SwitchToCamera", "Switch To Missile Camera", "", { + // statement + params ["_target", "_player", "_params"]; + private _camera = _player getVariable [QGVAR(missileCamera), objNull]; + [_camera] call FUNC(camera_switchTo); + }, { + // condition + params ["_target", "_player", "_params"]; + private _camera = _player getVariable [QGVAR(missileCamera), objNull]; + private _projectile = _camera getVariable [QGVAR(missile), objNull]; + !([] call FUNC(camera_userInCamera)) && { !isNull _camera } && { !isNull _projectile } + }] call EFUNC(interact_menu,createAction); + ["CAManBase", 1, ["ACE_SelfActions"], _switchToCameraAction, true] call EFUNC(interact_menu,addActionToClass); + + GVAR(activeCamera) = objNull; + GVAR(projectileHashMap) = createHashMap; // used in the seeker to get the namespace for the associated projectile +}; diff --git a/addons/spike/XEH_preInit.sqf b/addons/spike/XEH_preInit.sqf new file mode 100644 index 00000000000..b47cf6628db --- /dev/null +++ b/addons/spike/XEH_preInit.sqf @@ -0,0 +1,9 @@ +#include "script_component.hpp" + +ADDON = false; + +PREP_RECOMPILE_START; +#include "XEH_PREP.hpp" +PREP_RECOMPILE_END; + +ADDON = true; diff --git a/addons/spike/XEH_preStart.sqf b/addons/spike/XEH_preStart.sqf new file mode 100644 index 00000000000..022888575ed --- /dev/null +++ b/addons/spike/XEH_preStart.sqf @@ -0,0 +1,3 @@ +#include "script_component.hpp" + +#include "XEH_PREP.hpp" diff --git a/addons/spike/config.cpp b/addons/spike/config.cpp new file mode 100644 index 00000000000..9c3ae0a724a --- /dev/null +++ b/addons/spike/config.cpp @@ -0,0 +1,34 @@ +#include "script_component.hpp" + +class CfgPatches { + class ADDON { + name = COMPONENT_NAME; + units[] = {}; + weapons[] = {}; + requiredVersion = REQUIRED_VERSION; + requiredAddons[] = {"ace_common"}; + author = ECSTRING(common,ACETeam); + authors[] = {"Dani (TCVM)"}; + url = ECSTRING(main,URL); + VERSION_CONFIG; + }; +}; + +class RscOpticsValue; +class RscControlsGroupNoScrollbars; +class RscPicture; +class RscLine; +class RscMapControl; +class RscText; + +#define COLOR_WHITE {0.8745,0.8745,0.8745,1} +#define COLOR_BLACK {0,0,0,1} + +#include "ACE_GuidanceConfig.hpp" +#include "RscTitles.hpp" +#include "RscInGameUI.hpp" +#include "CfgEventHandlers.hpp" +#include "CfgAmmo.hpp" +#include "CfgMagazines.hpp" +#include "CfgWeapons.hpp" + diff --git a/addons/spike/data/reticle_titan.p3d b/addons/spike/data/reticle_titan.p3d new file mode 100644 index 00000000000..682296f0c82 Binary files /dev/null and b/addons/spike/data/reticle_titan.p3d differ diff --git a/addons/spike/functions/fnc_camera_changeZoom.sqf b/addons/spike/functions/fnc_camera_changeZoom.sqf new file mode 100644 index 00000000000..6586b2aa937 --- /dev/null +++ b/addons/spike/functions/fnc_camera_changeZoom.sqf @@ -0,0 +1,35 @@ +#include "..\script_component.hpp" +/* + * Author: Dani (TCVM) + * Decreases zoom of current camera + * + * Arguments: + * 0: Camera + * 1: Increase + * + * Return Value: + * None + * + * Example: + * [cam] call ace_spike_fnc_camera_cycleViewMode + * + * Public: No + */ +params ["_cameraNamespace"]; + +private _tiIndex = _cameraNamespace getVariable [QGVAR(currentTIModeIndex), 0]; +private _tiArray = _cameraNamespace getVariable [QGVAR(thermalTypes), []]; + +if (_tiArray isEqualTo []) exitWith {}; + +if ((_tiIndex + 1) >= count _tiArray) then { + _tiIndex = 0; +} else { + _tiIndex = _tiIndex + 1; +}; + +_cameraNamespace setVariable [QGVAR(currentTIModeIndex), _tiIndex]; + +[_cameraNamespace, _tiArray select _tiIndex] call FUNC(camera_setViewMode); diff --git a/addons/spike/functions/fnc_camera_destroy.sqf b/addons/spike/functions/fnc_camera_destroy.sqf new file mode 100644 index 00000000000..d1d143735f5 --- /dev/null +++ b/addons/spike/functions/fnc_camera_destroy.sqf @@ -0,0 +1,36 @@ +#include "..\script_component.hpp" +/* + * Author: Dani (TCVM) + * Destroys camera attaches to projectile + * + * Arguments: + * 0: Camera + * + * Return Value: + * None + * + * Example: + * [cam] call ace_spike_fnc_camera_destroy + * + * Public: No + */ +params ["_cameraNamespace"]; + +private _userInThisCamera = [_cameraNamespace] call FUNC(camera_userInCamera); +private _userCamera = ACE_player getVariable [QGVAR(missileCamera), objNull]; + +if (_userInThisCamera || { _userCamera isEqualTo _cameraNamespace }) then { + ACE_player setVariable [QGVAR(missileCamera), objNull]; +}; + +[_cameraNamespace] call FUNC(camera_switchAway); + +private _key = _cameraNamespace getVariable [QGVAR(missile), objNull]; + +private _logic = _cameraNamespace getVariable [QGVAR(logic), objNull]; +deleteVehicle _logic; + +private _camera = _cameraNamespace getVariable QGVAR(camera); +camDestroy _camera; + +_cameraNamespace call CBA_fnc_deleteNamespace; diff --git a/addons/spike/functions/fnc_camera_handleKeyPress.sqf b/addons/spike/functions/fnc_camera_handleKeyPress.sqf new file mode 100644 index 00000000000..a173a542193 --- /dev/null +++ b/addons/spike/functions/fnc_camera_handleKeyPress.sqf @@ -0,0 +1,95 @@ +#include "..\script_component.hpp" +/* + * Author: Dani (TCVM) + * Updates camera with inputs + * + * Arguments: + * 0: Key Pressed + * 1: Down + * + * Return Value: + * None + * + * Example: + * [5, true] call ace_spike_fnc_camera_handleKeyPress + * + * Public: No + */ +params ["_key", "_down"]; + +if !([objNull] call FUNC(camera_userInCamera)) exitWith {}; + +private _return = false; +private _lookInput = GVAR(activeCamera) getVariable [QGVAR(lookInput), [0, 0, 0, 0]]; +private _designateInput = GVAR(activeCamera) getVariable [QGVAR(designateInput), [0]]; +switch (_key) do { + case SPIKE_KEY_DESIGNATE: { + // designate whatever. depends on seeker to implement + if (_down) then { + _designateInput set [0, 1]; + } else { + _designateInput set [0, 0]; + }; + _return = true; + }; + case SPIKE_KEY_LEAVE: { + [] call FUNC(camera_switchAway); + }; + case CAMERA_KEY_UP: { + if (_down) then { + _lookInput set [0, 1]; + } else { + _lookInput set [0, 0]; + }; + _return = true; + }; //Up + + case CAMERA_KEY_LEFT: { + if (_down) then { + _lookInput set [2, 1]; + } else { + _lookInput set [2, 0]; + }; + _return = true; + }; //Left + + case CAMERA_KEY_RIGHT: { + if (_down) then { + _lookInput set [3, 1]; + } else { + _lookInput set [3, 0]; + }; + _return = true; + }; //Right + + case CAMERA_KEY_DOWN: { + if (_down) then { + _lookInput set [1, 1]; + } else { + _lookInput set [1, 0]; + }; + _return = true; + }; //Down + case CAMERA_KEY_CHANGE_VISION_MODE: { + if(_down) then { + [GVAR(activeCamera)] call FUNC(camera_cycleViewMode); + }; + _return = true; + }; //N + case CAMERA_KEY_ZOOM_IN: { + if(_down) then { + [GVAR(activeCamera), true] call FUNC(camera_changeZoom); + }; + _return = true; + }; // Num+ + case CAMERA_KEY_ZOOM_OUT: { + if(_down) then { + [GVAR(activeCamera), false] call FUNC(camera_changeZoom); + }; + _return = true; + }; // Num- +}; +GVAR(activeCamera) setVariable [QGVAR(designateInput), _designateInput]; +GVAR(activeCamera) setVariable [QGVAR(lookInput), _lookInput]; + +_return diff --git a/addons/spike/functions/fnc_camera_init.sqf b/addons/spike/functions/fnc_camera_init.sqf new file mode 100644 index 00000000000..eb6bbdf4b2a --- /dev/null +++ b/addons/spike/functions/fnc_camera_init.sqf @@ -0,0 +1,87 @@ +#include "..\script_component.hpp" +/* + * Author: Dani (TCVM) + * Initializes camera for player to view missile from its nose + * + * Arguments: + * 0: Missile + * 1: Cam Array + * 2: Shooter + * 3: Switch On Fire + * + * Return Value: + * The camera + * + * Example: + * [m, [], player, false] call ace_spike_fnc_camera_init + * + * Public: No + */ +params ["_projectile", "_cameraArray", "_shooter", "_switchOnFireInit"]; +_cameraArray params ["_enabled", "_fovLevels", "_initialFOV", "_thermalTypes", "_initialThermalType", "_switchOnFire", "_lerpFOV", "_fovChangeTime", "", "_gimbalData", "_reticleData", "_designating"]; +_gimbalData params ["_hasGimbal", "_maxGimbalX", "_maxGimbalY", "_gimbalSpeedX", "_gimbalSpeedY", "_initGimbalAngleX", "_initGimbalAngleY", "_gimbalZoomSpeedModifiers"]; + +if !(_enabled) exitWith {}; + +private _activeCameraNamespace = [] call CBA_fnc_createNamespace; +_activeCameraNamespace setVariable [QGVAR(fovLevels), _fovLevels]; +_activeCameraNamespace setVariable [QGVAR(thermalTypes), _thermalTypes]; + +_activeCameraNamespace setVariable [QGVAR(lerpFOVChange), _lerpFOV == 1]; +_activeCameraNamespace setVariable [QGVAR(targetFOV), _initialFOV]; +_activeCameraNamespace setVariable [QGVAR(currentFOV), _initialFOV]; +_activeCameraNamespace setVariable [QGVAR(fovChanged), false]; +_activeCameraNamespace setVariable [QGVAR(fovChangeTime), _fovChangeTime]; +_activeCameraNamespace setVariable [QGVAR(lastMovedGroundPos), [0, 0, 0]]; +private _currentZoomIndex = _fovLevels findIf { _x isEqualTo _initialFOV }; +if (_currentZoomIndex < 0) then { _currentZoomIndex = 0 }; + +private _currentTIIndex = _thermalTypes findIf { _x isEqualTo _initialThermalType }; +if (_currentTIIndex < 0) then { _currentTIIndex = 0 }; + +_activeCameraNamespace setVariable [QGVAR(currentZoomIndex), _currentZoomIndex]; +_activeCameraNamespace setVariable [QGVAR(currentTIModeIndex), _currentTIIndex]; + +if (_designating) then { + _activeCameraNamespace setVariable [QGVAR(alwaysDesignate), true]; +}; +_activeCameraNamespace setVariable [QGVAR(reticleData), _reticleData]; +_shooter setVariable [QGVAR(missileCamera), _activeCameraNamespace]; +_activeCameraNamespace setVariable [QGVAR(shooter), _shooter]; +[_activeCameraNamespace, _initialThermalType] call FUNC(camera_setViewMode); + +private _pos = getPosASL _projectile; + +private _camera = "camera" camCreate _pos; +private _logic = "Logic" createVehicleLocal _pos; + +private _initPosX = (tan _initGimbalAngleX) * GIMBAL_LOGIC_OFFSET; +private _initPosY = (tan _initGimbalAngleY) * GIMBAL_LOGIC_OFFSET; + +private _projectileBounds = 1 boundingBoxReal _projectile; +private _projectileSize = (_projectileBounds#1) vectorDiff (_projectileBounds#0); +_activeCameraNamespace setVariable [QGVAR(projectileSize), _projectileSize#1]; + +private _logicPosition = [_initPosX, GIMBAL_LOGIC_OFFSET, _initPosY] vectorAdd [0, _projectileSize#1, 0]; + +_logic setPosASL (_projectile modelToWorldVisualWorld _logicPosition); + +_camera camSetTarget _logic; +_camera setPos (_projectile modelToWorldVisual ((_projectile worldToModelVisual (ASLToATL getPosASL _projectile)) vectorAdd [0, _projectileSize#1, 0])); +_camera camSetFov _initialFOV; + +_camera camCommit 0; +showCinemaBorder false; +camUseNVG false; + +_activeCameraNamespace setVariable [QGVAR(lastMissileOrientation), vectorNormalized velocity _projectile]; +_activeCameraNamespace setVariable [QGVAR(camera), _camera]; +_activeCameraNamespace setVariable [QGVAR(logic), _logic]; +_activeCameraNamespace setVariable [QGVAR(missile), _projectile]; +_activeCameraNamespace setVariable [QGVAR(logicPos), _projectile vectorModelToWorldVisual _logicPosition]; + +if (_switchOnFire && _switchOnFireInit) then { + [_activeCameraNamespace] call FUNC(camera_switchTo); +}; + +_activeCameraNamespace diff --git a/addons/spike/functions/fnc_camera_setViewMode.sqf b/addons/spike/functions/fnc_camera_setViewMode.sqf new file mode 100644 index 00000000000..55073068f21 --- /dev/null +++ b/addons/spike/functions/fnc_camera_setViewMode.sqf @@ -0,0 +1,39 @@ +#include "..\script_component.hpp" +/* + * Author: Dani (TCVM) + * Cycles camera thermal mode (if avaliable) + * + * Arguments: + * 0: Camera + * 1: View Mode + * + * Return Value: + * None + * + * Example: + * [cam, ""] call ace_spike_fnc_camera_setViewMode + * + * Public: No + */ +params ["_cameraNamespace", "_viewMode"]; + +private _userInCamera = [] call FUNC(camera_userInCamera); + +if (_userInCamera) then { + camUseNVG false; + false setCamUseTI (_cameraNamespace getVariable [QGVAR(tiMode), 0]); +}; + +private _thermalMode = ["normal", "nvg", "white_hot_black_cold", "black_hot_white_cold", "light_green_hot_dark_green_cold", "black_hot_green_cold", "light_red_hot_dark_red_cold", "black_hot_red_cold", "white_hot_red_cold", "predator"] findIf { _viewMode isEqualTo _x }; +if (_thermalMode > 1) then { + if (_userInCamera) then { + true setCamUseTI (_thermalMode - 2); + }; +}; +if (_thermalMode == 1) then { + if (_userInCamera) then { + camUseNVG true; + }; +}; +_cameraNamespace setVariable [QGVAR(tiMode), _thermalMode - 2]; +_cameraNamespace setVariable [QGVAR(tiModeString), _viewMode]; diff --git a/addons/spike/functions/fnc_camera_setZoom.sqf b/addons/spike/functions/fnc_camera_setZoom.sqf new file mode 100644 index 00000000000..e5d080a7522 --- /dev/null +++ b/addons/spike/functions/fnc_camera_setZoom.sqf @@ -0,0 +1,24 @@ +#include "..\script_component.hpp" +/* + * Author: Dani (TCVM) + * Decreases zoom of current camera + * + * Arguments: + * 0: Camera + * 1: Zoom Index + * + * Return Value: + * None + * + * Example: + * [cam, 1] call ace_spike_fnc_camera_setZoom + * + * Public: No + */ +params ["_cameraNamespace", "_zoomIndex"]; +private _zoomArray = _cameraNamespace getVariable [QGVAR(fovLevels), []]; +_cameraNamespace setVariable [QGVAR(currentZoomIndex), _zoomIndex]; +_cameraNamespace setVariable [QGVAR(targetFOV), _zoomArray select _zoomIndex]; +_cameraNamespace setVariable [QGVAR(fovChanged), true]; +_cameraNamespace setVariable [QGVAR(fovChangedTime), CBA_missionTime]; +_cameraNamespace setVariable [QGVAR(startingFov), _cameraNamespace getVariable QGVAR(currentFOV)]; diff --git a/addons/spike/functions/fnc_camera_switchAway.sqf b/addons/spike/functions/fnc_camera_switchAway.sqf new file mode 100644 index 00000000000..bcb001a579c --- /dev/null +++ b/addons/spike/functions/fnc_camera_switchAway.sqf @@ -0,0 +1,25 @@ +#include "..\script_component.hpp" +/* + * Author: Dani (TCVM) + * Switches away from the currently controlled camera + * + * Arguments: + * 0: Camera + * + * Return Value: + * None + * + * Example: + * [cam] call ace_spike_fnc_camera_switchAway + * + * Public: No + */ +params [["_cameraNamespace", objNull]]; + +if !([_cameraNamespace] call FUNC(camera_userInCamera)) exitWith {}; + +private _camera = GVAR(activeCamera) getVariable QGVAR(camera); +_camera cameraEffect ["terminate", "back"]; +GVAR(activeCamera) = objNull; + +QGVAR(camera_hud) cutText ["", "PLAIN"]; diff --git a/addons/spike/functions/fnc_camera_switchTo.sqf b/addons/spike/functions/fnc_camera_switchTo.sqf new file mode 100644 index 00000000000..b984f5d6e4b --- /dev/null +++ b/addons/spike/functions/fnc_camera_switchTo.sqf @@ -0,0 +1,51 @@ +#include "..\script_component.hpp" +/* + * Author: Dani (TCVM) + * Switches to the currently controlled camera + * + * Arguments: + * 0: Camera + * + * Return Value: + * None + * + * Example: + * [cam] call ace_spike_fnc_camera_switchTo + * + * Public: No + */ +if ([] call FUNC(camera_userInCamera)) exitWith {}; + +params ["_cameraNamespace"]; +private _camera = _cameraNamespace getVariable QGVAR(camera); +private _projectile = _cameraNamespace getVariable QGVAR(missile); +_camera cameraEffect ["internal", "BACK"]; +GVAR(activeCamera) = _cameraNamespace; +[_cameraNamespace, _cameraNamespace getVariable [QGVAR(tiModeString), "normal"]] call FUNC(camera_setViewMode); +[_cameraNamespace, _cameraNamespace getVariable [QGVAR(currentZoomIndex), 0]] call FUNC(camera_setZoom); + + +private _reticleData = _cameraNamespace getVariable QGVAR(reticleData); +_reticleData params ["_titleRsc", "_centerReticle", "_controlsToDisappearOnLock", "_controlsToAppearOnLock", "_leftGate", "_rightGate", "_topGate", "_bottomGate", "_uiNamespaceDialogVariable"]; + +// setup the HUD reticle +QGVAR(camera_hud) cutRsc [_titleRsc, "PLAIN"]; + +private _rscDissapearOnLock = []; +private _rscAppearOnLock = []; + +{ + _rscDissapearOnLock pushBack ((uiNamespace getVariable _uiNamespaceDialogVariable) displayCtrl _x); +} forEach _controlsToDisappearOnLock; + +{ + _rscAppearOnLock pushBack ((uiNamespace getVariable _uiNamespaceDialogVariable) displayCtrl _x); +} forEach _controlsToAppearOnLock; + +_cameraNamespace setVariable [QGVAR(reticleCenter), (uiNamespace getVariable _uiNamespaceDialogVariable) displayCtrl _centerReticle]; +_cameraNamespace setVariable [QGVAR(disappearOnLock), _rscDissapearOnLock]; +_cameraNamespace setVariable [QGVAR(appearOnLock), _rscAppearOnLock]; +_cameraNamespace setVariable [QGVAR(reticleLeft), (uiNamespace getVariable _uiNamespaceDialogVariable) displayCtrl _leftGate]; +_cameraNamespace setVariable [QGVAR(reticleRight), (uiNamespace getVariable _uiNamespaceDialogVariable) displayCtrl _rightGate]; +_cameraNamespace setVariable [QGVAR(reticleTop), (uiNamespace getVariable _uiNamespaceDialogVariable) displayCtrl _topGate]; +_cameraNamespace setVariable [QGVAR(reticleBottom), (uiNamespace getVariable _uiNamespaceDialogVariable) displayCtrl _bottomGate]; diff --git a/addons/spike/functions/fnc_camera_update.sqf b/addons/spike/functions/fnc_camera_update.sqf new file mode 100644 index 00000000000..65ca6c99d27 --- /dev/null +++ b/addons/spike/functions/fnc_camera_update.sqf @@ -0,0 +1,241 @@ +#include "..\script_component.hpp" +/* + * Author: Dani (TCVM) + * Updates camera to be on a fixed point + * + * Arguments: + * 0: Guidance Arg Array + * 1: PFID + * + * Return Value: + * None + * + * Example: + * [[], 0] call ace_spike_fnc_camera_update + * + * Public: No + */ +params ["_cameraArray", "_projectile", "_deltaTime", "_cameraNamespace"]; +_cameraArray params ["_hasCamera", "", "", "", "", "", "", "", "_viewData", "_gimbalData", "", "_designating", "_canStopDesignating"]; +_viewData params ["_lookDir", "_groundPos", "_pointPos", "_movingCameraX", "_movingCameraY"]; +_gimbalData params ["_hasGimbal", "_maxGimbalX", "_maxGimbalY", "_gimbalSpeedX", "_gimbalSpeedY", "", "", "_gimbalZoomSpeedModifiers", "_stabilizeWhenMoving", "_designateWhenStationary", "_trackLockedPosition"]; + +if (!_hasCamera || { isNull _cameraNamespace }) exitWith {}; + +if ([_cameraNamespace] call FUNC(camera_userInCamera)) then { + cameraEffectEnableHUD true; +}; + +private _camera = _cameraNamespace getVariable [QGVAR(camera), nil]; +private _logic = _cameraNamespace getVariable [QGVAR(logic), objNull]; + +private _fovChanged = _cameraNamespace getVariable [QGVAR(fovChanged), false]; +private _cameraOffset = _cameraNamespace getVariable [QGVAR(projectileSize), 0]; +private _missileDirection = vectorNormalized velocity _projectile; +private _cameraPosASL = (getPosASLVisual _projectile) vectorAdd (_missileDirection vectorMultiply _cameraOffset); + +private _designatedLastFrame = _cameraNamespace getVariable [QGVAR(designatedLastFrame), false]; +if (_designatedLastFrame && !_canStopDesignating && { !(_groundPos isEqualTo [0, 0, 0] && _pointPos isEqualTo [0, 0, 0]) }) then { + _designating = true; +}; + +if (!_designating && _designatedLastFrame) then { + _designatedLastFrame = false; + _cameraNamespace setVariable [QGVAR(designatedLastFrame), _designatedLastFrame]; +}; + +if (_fovChanged) then { + private _lerpFovEnabled = _cameraNamespace getVariable [QGVAR(lerpFOVChange), false]; + private _targetFOV = _cameraNamespace getVariable [QGVAR(targetFOV), 1]; + private _currentFOV = _cameraNamespace getVariable [QGVAR(currentFOV), 1]; + private _fovChangeStart = _cameraNamespace getVariable [QGVAR(fovChangedTime), 0]; + private _startingFOV = _cameraNamespace getVariable [QGVAR(startingFov), 1]; + private _fovChangeTime = _cameraNamespace getVariable [QGVAR(fovChangeTime), 0]; + + private _setFOV = _targetFOV; + if (_lerpFovEnabled) then { + _setFOV = linearConversion [0, _fovChangeTime, CBA_missionTime - _fovChangeStart, _startingFOV, _targetFOV, true]; + } else { + _fovChanged = false; + }; + + // if the FOV is near enough to the target FOV stop the lerp + if (abs(_setFOV - _targetFOV) == 0 || ((CBA_missionTime - _fovChangeStart) > _fovChangeTime + 2)) then { + _setFOV = _targetFOV; + _fovChanged = false; + }; + + _camera camSetFov _setFOV; + _cameraNamespace setVariable [QGVAR(fovChanged), _fovChanged]; + _cameraNamespace setVariable [QGVAR(currentFOV), _setFOV]; +}; + +_movingCameraX = false; +_movingCameraY = false; + +private _relativePos = _cameraNamespace getVariable [QGVAR(logicPos), _missileDirection vectorMultiply GIMBAL_LOGIC_OFFSET]; +private _expectedPos = _relativePos; +if (_hasGimbal) then { + private _lookInput = _cameraNamespace getVariable [QGVAR(lookInput), [0, 0, 0, 0]]; + _lookInput params ["_up", "_down", "_left", "_right"]; + + _movingCameraX = (_right - _left) != 0; + _movingCameraY = (_up - _down) != 0; + + private _lastGroundPos = _cameraNamespace getVariable [QGVAR(lastMovedGroundPos), [0, 0, 0]]; + + if !(_movingCameraX || _movingCameraY) then { + // ToDo: Unreachable code, var known undefined //IGNORE_PRIVATE_WARNING ["_seekerTargetPos"]; + // If we designate a target set the current tracking point to the current ground point to avoid unwanted behavior from static cameras + if (_designating && !_designatedLastFrame) then { + _designatedLastFrame = true; + _cameraNamespace setVariable [QGVAR(designatedLastFrame), _designatedLastFrame]; + _lastGroundPos = _groundPos; + _cameraNamespace setVariable [QGVAR(lastMovedGroundPos), _lastGroundPos]; + }; + + if (_trackLockedPosition && _canStopDesignating && { _seekerTargetPos isNotEqualTo [0, 0, 0] }) then { + _lastGroundPos = _seekerTargetPos; + }; + + // lock the camera and dont gimbal with missile rotation + if (_lastGroundPos isNotEqualTo [0, 0, 0]) then { + #ifdef DEBUG_MODE_FULL + drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [1, 1, 1, 1], ASLToATL (_lastGroundPos), 0.75, 0.75, 0, "Last Camera Ground Position", 1, 0.025, "TahomaB"]; + #endif + private _directionToGround = _cameraPosASL vectorFromTo _lastGroundPos; + (_directionToGround call CBA_fnc_vect2polar) params ["", "_azimuth", "_elevation"]; + + _expectedPos = _directionToGround vectorMultiply GIMBAL_LOGIC_OFFSET; + }; + } else { + private _speedModifier = 1; + if (_gimbalZoomSpeedModifiers isNotEqualTo []) then { + _speedModifier = (_gimbalZoomSpeedModifiers select (_cameraNamespace getVariable [QGVAR(currentZoomIndex), 0])); + }; + + private _offsetX = (_speedModifier * _gimbalSpeedX * _deltaTime * (_right - _left)); + private _offsetY = (_speedModifier * _gimbalSpeedY * _deltaTime * (_up - _down)); + + private _directionToLast = _relativePos; + if (_groundPos isNotEqualTo [0, 0, 0]) then { + private _lastLogicPos = getPosASLVisual _logic; + _directionToLast = _cameraPosASL vectorFromTo _groundPos; + }; + + (_directionToLast call CBA_fnc_vect2polar) params ["", "_azimuth", "_elevation"]; + + _expectedPos = [GIMBAL_LOGIC_OFFSET, _azimuth + _offsetX, _elevation + _offsetY] call CBA_fnc_polar2vect; + }; +}; + +_relativePos = _expectedPos; +(_relativePos call CBA_fnc_vect2polar) params ["", "_azimuth", "_elevation"]; +(_missileDirection call CBA_fnc_vect2polar) params ["", "", "_missilePitch"]; + +private _projectileDir = vectorDirVisual _projectile; +_projectileDir set [2, 0]; + +private _2dRelativePos = [_relativePos#0, _relativePos#1, 0]; + +private _cameraAzimuth = acos (_projectileDir vectorCos _2dRelativePos); +private _cameraElevation = _missilePitch - _elevation; + +if (_cameraAzimuth > _maxGimbalX) then { + private _maxDirection = (direction _projectile) + _maxGimbalX; + + private _crossProductSign = (_2dRelativePos vectorCrossProduct _projectileDir)#2; + if (_crossProductSign < 0) then { + _maxDirection = (direction _projectile) - _maxGimbalX; + }; + + if (_maxDirection >= 360) then { + _maxDirection = _maxDirection - 360; + }; + + if (_maxDirection < 0) then { + _maxDirection = 360 + _maxDirection; + }; + + private _maxVec = [GIMBAL_LOGIC_OFFSET, _maxDirection, _elevation] call CBA_fnc_polar2vect; + _relativePos set [0, _maxVec select 0]; + _relativePos set [1, _maxVec select 1]; + + _azimuth = _maxDirection; +}; + +if (abs(_cameraElevation) >= _maxGimbalY) then { + private _maxElevation = _missilePitch + _maxGimbalY; + if (_cameraElevation > 0) then { + _maxElevation = _missilePitch - _maxGimbalY; + }; + + if (_maxElevation >= 360) then { + _maxElevation = 360 - _maxElevation; + }; + + if (_maxElevation < 0) then { + _maxElevation = 360 + _maxElevation; + }; + + private _maxVec = [GIMBAL_LOGIC_OFFSET, _azimuth, _maxElevation] call CBA_fnc_polar2vect; + _relativePos set [2, _maxVec select 2]; +}; + +_designating = (!_canStopDesignating && _designating) || { _cameraNamespace getVariable [QGVAR(alwaysDesignate), false] || { (_cameraNamespace getVariable [QGVAR(designateInput), [0]])#0 == 1 } }; +if (_designateWhenStationary && !(_movingCameraX || _movingCameraY)) then { + _designating = true; +}; +_cameraNamespace setVariable [QGVAR(logicPos), _relativePos]; +_cameraNamespace setVariable [QGVAR(cameraPos), _cameraPosASL]; + +private _posAddedASL = _cameraPosASL vectorAdd _relativePos; + +_logic setPosASL _posAddedASL; + +_camera camSetTarget _logic; +_camera setPosASL _cameraPosASL; + +_lookDir = _cameraPosASL vectorFromTo (getPosASL _logic); + +private _projectedPos = _cameraPosASL vectorAdd (_lookDir vectorMultiply 10000); + +private _surfaceIntersections = lineIntersectsSurfaces [_cameraPosASL, _projectedPos, _projectile, _logic]; + +private _pointPos = [0, 0, 0]; +private _groundPos = terrainIntersectAtASL [_cameraPosASL, _projectedPos]; + +if (_surfaceIntersections isNotEqualTo []) then { + _pointPos = (_surfaceIntersections select 0) select 0; +}; + +if (_movingCameraX) then { + _cameraNamespace setVariable [QGVAR(lastMovedGroundPosX), _groundPos]; +}; +if (_movingCameraY) then { + _cameraNamespace setVariable [QGVAR(lastMovedGroundPosY), _groundPos]; +}; +if (_movingCameraX || _movingCameraY) then { + _cameraNamespace setVariable [QGVAR(lastMovedGroundPos), _groundPos]; +}; + +_cameraArray set [11, _designating]; + +#ifdef DEBUG_MODE_FULL +drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [1, 0.5, 1, 1], ASLToAGL _cameraPosASL, 0.75, 0.75, 0, "Camera Pos", 1, 0.025, "TahomaB"]; +drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [1, 0.5, 1, 1], getPosATL _logic, 0.75, 0.75, 0, "Logic Pos", 1, 0.025, "TahomaB"]; + +drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [0, 1, 1, 1], ASLToAGL (_groundPos), 0.75, 0.75, 0, "Camera Ground Position", 1, 0.025, "TahomaB"]; +drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [1, 1, 0, 1], ASLToAGL (_pointPos), 0.75, 0.75, 0, "Camera Point Position", 1, 0.025, "TahomaB"]; +#endif + +_viewData set [0, _lookDir]; +_viewData set [1, _groundPos]; +_viewData set [2, _pointPos]; +_viewData set [3, _movingCameraX]; +_viewData set [4, _movingCameraY]; +_cameraArray set [8, _viewData]; + +_camera camCommit 0; + +[_cameraNamespace, _cameraArray] call FUNC(camera_updateTargetingGate); diff --git a/addons/spike/functions/fnc_camera_updateTargetingGate.sqf b/addons/spike/functions/fnc_camera_updateTargetingGate.sqf new file mode 100644 index 00000000000..6d192e7ea14 --- /dev/null +++ b/addons/spike/functions/fnc_camera_updateTargetingGate.sqf @@ -0,0 +1,107 @@ +#include "..\script_component.hpp" +/* + * Author: Dani (TCVM) + * Switches away from the currently controlled camera + * + * Arguments: + * 0: Camera + * 0: Camera Array + * + * Return Value: + * None + * + * Example: + * [cam, []] call ace_spike_fnc_camera_updateTargetingGate + * + * Public: No + */ +params ["_cameraNamespace", "_cameraArray"]; +_cameraArray params ["", "", "", "", "", "", "", "", "_viewData", "_gimbalData", "_reticleData", "_designating"]; +_viewData params ["_lookDir", "_groundPos", "_pointPos", "_movingCameraX", "_movingCameraY", "_stabilizeWhenMoving"]; +_gimbalData params ["_hasGimbal", "_maxGimbalX", "_maxGimbalY", "_gimbalSpeedX", "_gimbalSpeedY", "", "", "_gimbalZoomSpeedModifiers"]; +_reticleData params ["", "", "", "", "", "", "", "", "", "_reticleMovesWithTrack"]; + +if !([_cameraNamespace] call FUNC(camera_userInCamera)) exitWith {}; + +private _seekerTargetPos = _cameraNamespace getVariable [QGVAR(seekerTargetPos), [0, 0, 0]]; +private _seekerTargetInfo = _cameraNamespace getVariable [QGVAR(seekerTargetInfo), [false, [0, 0], [0, 0]]]; + +private _seekerPositionScreen = [0, 0]; +private _locked = false; + +if (_seekerTargetPos isEqualTo [0, 0, 0]) then { + { + _x ctrlShow false; + } forEach (_cameraNamespace getVariable QGVAR(appearOnLock)); + { + _x ctrlShow true; + } forEach (_cameraNamespace getVariable QGVAR(disappearOnLock)); +} else { + // seeker has target - enable relevant data + _locked = true; + { + _x ctrlShow true; + } forEach (_cameraNamespace getVariable QGVAR(appearOnLock)); + { + _x ctrlShow false; + } forEach (_cameraNamespace getVariable QGVAR(disappearOnLock)); + + if (_reticleMovesWithTrack) then { + _seekerPositionScreen = worldToScreen ASLToAGL _seekerTargetPos; + if (_seekerPositionScreen isEqualTo []) then { + _seekerPositionScreen = [0, 0]; + }; + _seekerPositionScreen set [0, _seekerPositionScreen#0 - 0.5]; + _seekerPositionScreen set [1, _seekerPositionScreen#1 - 0.5]; + }; +}; + +if (_seekerPositionScreen isEqualTo []) then { + _seekerPositionScreen = [0, 0]; +}; + +(_cameraNamespace getVariable QGVAR(reticleCenter)) ctrlSetPosition _seekerPositionScreen; + +if (_locked) then { + _seekerTargetInfo params ["_lockedObject", "_boundsTL", "_boundsBR"]; + if (_lockedObject) then { + private _missile = _cameraNamespace getVariable QGVAR(missile); + + private _avgDistance = (_boundsTL#1 + _boundsBR#1) / 2; + + private _topCenter = [((_boundsTL#0) + (_boundsBR#0)) / 2, _avgDistance, _boundsBR#2]; + private _bottomCenter = [((_boundsTL#0) + (_boundsBR#0)) / 2, _avgDistance, _boundsTL#2]; + private _leftCenter = [_boundsTL#0, _avgDistance, ((_boundsTL#2) + (_boundsBR#2)) / 2]; + private _rightCenter = [_boundsBR#0, _avgDistance, ((_boundsTL#2) + (_boundsBR#2)) / 2]; + + private _ctrlPos = worldToScreen (_missile modelToWorldVisual _leftCenter); + if (_ctrlPos isEqualTo []) then { + _ctrlPos set [0, 0.5]; + _ctrlPos set [1, 0.5]; + }; + (_cameraNamespace getVariable QGVAR(reticleLeft)) ctrlSetPosition [(_ctrlPos#0 - _seekerPositionScreen#0) - 0.5, (_ctrlPos#1 - _seekerPositionScreen#1) - 0.5]; + + _ctrlPos = worldToScreen (_missile modelToWorldVisual _rightCenter); + if (_ctrlPos isEqualTo []) then { + _ctrlPos set [0, 0.5]; + _ctrlPos set [1, 0.5]; + }; + (_cameraNamespace getVariable QGVAR(reticleRight)) ctrlSetPosition [(_ctrlPos#0 - _seekerPositionScreen#0) - 0.5, (_ctrlPos#1 - _seekerPositionScreen#1) - 0.5]; + + _ctrlPos = worldToScreen (_missile modelToWorldVisual _topCenter); + if (_ctrlPos isEqualTo []) then { + _ctrlPos set [0, 0.5]; + _ctrlPos set [1, 0.5]; + }; + (_cameraNamespace getVariable QGVAR(reticleTop)) ctrlSetPosition [(_ctrlPos#0 - _seekerPositionScreen#0) - 0.5, (_ctrlPos#1 - _seekerPositionScreen#1) - 0.5]; + + _ctrlPos = worldToScreen (_missile modelToWorldVisual _bottomCenter); + if (_ctrlPos isEqualTo []) then { + _ctrlPos set [0, 0.5]; + _ctrlPos set [1, 0.5]; + }; + (_cameraNamespace getVariable QGVAR(reticleBottom)) ctrlSetPosition [(_ctrlPos#0 - _seekerPositionScreen#0) - 0.5, (_ctrlPos#1 - _seekerPositionScreen#1) - 0.5]; + }; +}; + +{ _x ctrlCommit 0 } forEach [_cameraNamespace getVariable QGVAR(reticleCenter), _cameraNamespace getVariable QGVAR(reticleLeft), _cameraNamespace getVariable QGVAR(reticleRight), _cameraNamespace getVariable QGVAR(reticleTop), _cameraNamespace getVariable QGVAR(reticleBottom)]; diff --git a/addons/spike/functions/fnc_camera_userInCamera.sqf b/addons/spike/functions/fnc_camera_userInCamera.sqf new file mode 100644 index 00000000000..924294cab95 --- /dev/null +++ b/addons/spike/functions/fnc_camera_userInCamera.sqf @@ -0,0 +1,21 @@ +#include "..\script_component.hpp" +/* + * Author: Dani (TCVM) + * Switches away from the currently controlled camera + * + * Arguments: + * 0: Camera + * + * Return Value: + * + * + * Example: + * [cam] call ace_spike_fnc_camera_userInCamera + * + * Public: No + */ +if (isNil QGVAR(activeCamera)) exitWith { false }; + +params [["_cameraNamespace", objNull]]; + +(!isNull GVAR(activeCamera)) && { isNull _cameraNamespace || (_cameraNamespace isEqualTo GVAR(activeCamera)) }; diff --git a/addons/spike/functions/fnc_getTargetPosition.sqf b/addons/spike/functions/fnc_getTargetPosition.sqf new file mode 100644 index 00000000000..1244bb7f538 --- /dev/null +++ b/addons/spike/functions/fnc_getTargetPosition.sqf @@ -0,0 +1,124 @@ +#include "..\script_component.hpp" +/* + * Author: Dani (TCVM) + * Return the position of a potential EO target via a "edge detection" algorithm. Compares object bounding boxes to see what we are most likely hitting + * + * Arguments: + * 1: Origin + * 2: Direction + * 3: If we are designating + * + * Return Value: + * Missile Aim PosASL + * + * Example: + * [[], [], []] call ace_spike_fnc_getTargetPosition + * + * Public: No + */ +#define CHECK_DISTANCE 10 +#define SEEKER_BIAS 50 +#define SEEKER_GRID_RADIUS 2 +#define MAX_RANGE 4000 +params ["_origin", "_direction", "_designateInput", "_seekerTargetPos", "_seeker", ["_ignoreObject", objNull]]; + +scopeName "main"; +private _nearObjects = []; + +private _desiredObject = objNull; +private _intersections = []; +if (_designateInput == 1) then { + for "_xOffset" from -SEEKER_GRID_RADIUS to SEEKER_GRID_RADIUS do { + for "_yOffset" from -SEEKER_GRID_RADIUS to SEEKER_GRID_RADIUS do { + private _testPosASL = AGLToASL (positionCameraToWorld [_xOffset, _yOffset, MAX_RANGE]); + private _intersectionsToCursorTarget = lineIntersectsSurfaces [_origin, _testPosASL, _ignoreObject, objNull, true, 1, "FIRE", "VIEW", true]; + #ifdef DEBUG_MODE_FULL + drawIcon3D ["\A3\ui_f\data\map\markers\military\dot_CA.paa", [0,1,0,1], ASLToAGL _testPosASL, 0.25, 0.25, 0, "", 0.5, 0.025, "TahomaB"]; + #endif + if (_intersectionsToCursorTarget isNotEqualTo []) then { + _intersections append _intersectionsToCursorTarget; + }; + }; + }; +} else { + _intersections = lineIntersectsSurfaces [_origin, _origin vectorAdd (_direction vectorMultiply MAX_RANGE), _ignoreObject, objNull, true, 1, "FIRE", "VIEW", true] +}; + +if (_intersections isNotEqualTo []) then { + (_intersections select 0) params ["_intersectPos", "", "_object"]; + + if (_designateInput == 1) then { + _seekerTargetPos = _intersectPos; + }; + + _nearObjects = (ASLToAGL _seekerTargetPos) nearObjects ["AllVehicles", CHECK_DISTANCE]; + + if (_designateInput == 1 && { !isNull _object } && { _object isKindOf "Building" }) then { + _nearObjects pushBack _object; + if (isNull _desiredObject) then { + _desiredObject = _object; + }; + }; +}; + +if (_nearObjects isNotEqualTo []) then { + // I want to prefer the designated position on the object moreso than the bounds of the object + private _averagePosition = _seekerTargetPos vectorMultiply SEEKER_BIAS; + private _averagePositionCounter = SEEKER_BIAS; + + private _bestScore = 0; + private _bestObject = objNull; + + { + private _tiMagnitude = (vectorMagnitude getVehicleTIPars _x) / 1.74; // 1.74 = sqrt(3) = max magnitude of [1, 1, 1] + private _distance = (getPosASLVisual _x) vectorDistanceSqr _seekerTargetPos; + + private _score = 4 * _tiMagnitude + (_distance / (CHECK_DISTANCE * CHECK_DISTANCE)); + if (_desiredObject isEqualTo _x) then { + _score = _score * 2; + }; + if (_score > _bestScore) then { + _bestScore = _score; + _bestObject = _x; + }; + } forEach _nearObjects; + + private _boundingBox = 0 boundingBoxReal _bestObject; + + // Project target bounding box onto screen and do a real bad edge detection check + _boundingBox params ["_min", "_max"]; + _min params ["_x0", "_y0", "_z0"]; + _max params ["_x1", "_y1", "_z1"]; + + private _utl = _bestObject modelToWorldVisualWorld [_x0, _y0, _z0]; + private _utr = _bestObject modelToWorldVisualWorld [_x1, _y0, _z0]; + private _ubr = _bestObject modelToWorldVisualWorld [_x1, _y1, _z0]; + private _ubl = _bestObject modelToWorldVisualWorld [_x0, _y1, _z0]; + + private _dtl = _bestObject modelToWorldVisualWorld [_x0, _y0, _z1]; + private _dtr = _bestObject modelToWorldVisualWorld [_x1, _y0, _z1]; + private _dbr = _bestObject modelToWorldVisualWorld [_x1, _y1, _z1]; + private _dbl = _bestObject modelToWorldVisualWorld [_x0, _y1, _z1]; + + { + private _intersections = lineIntersectsSurfaces [_origin, _x, _ignoreObject, objNull, false, 16]; + if (_intersections isEqualTo []) then { + _averagePosition = _averagePosition vectorAdd _x; + _averagePositionCounter = _averagePositionCounter + 1; + } else { + { + _x params ["_surfacePosition"]; + _averagePosition = _averagePosition vectorAdd _surfacePosition; + _averagePositionCounter = _averagePositionCounter + 1; + } forEach _intersections; + } + } forEach [_utl, _utr, _ubr, _ubl, _dtl, _dtr, _dbr, _dbl]; + + _seekerTargetPos = _averagePosition vectorMultiply (1 / _averagePositionCounter); +} else { + if (_designateInput == 1) then { + _seekerTargetPos = [0, 0, 0]; + }; +}; + +_seekerTargetPos diff --git a/addons/spike/functions/fnc_keyDown.sqf b/addons/spike/functions/fnc_keyDown.sqf new file mode 100644 index 00000000000..0b1e7f55ab1 --- /dev/null +++ b/addons/spike/functions/fnc_keyDown.sqf @@ -0,0 +1,35 @@ +#include "..\script_component.hpp" +/* + * Author: Dani (TCVM) + * Handle key presses + * + * Arguments: + * 0: Key Pressed + * 1: Down + * + * Return Value: + * None + * + * Example: + * [5, true] call ace_spike_fnc_keyDown + * + * Public: No + */ +if ((currentWeapon ACE_player) != QGVAR(launcher)) exitWith {}; + +params ["_key", "_down"]; + +if (_key == SPIKE_KEY_DESIGNATE) then { + if (cameraView == "GUNNER") then { + playSound "ACE_Sound_Click"; + }; + private _designateInput = 0; + if (_down) then { + _designateInput = 1; + } else { + _designateInput = 0; + }; + (uiNamespace getVariable "ACE_RscOptics_spike") setVariable [QGVAR(designate), _designateInput]; +}; + +call FUNC(camera_handleKeyPress); diff --git a/addons/spike/functions/fnc_mapHelperDraw.sqf b/addons/spike/functions/fnc_mapHelperDraw.sqf new file mode 100644 index 00000000000..c9d24d386a5 --- /dev/null +++ b/addons/spike/functions/fnc_mapHelperDraw.sqf @@ -0,0 +1,99 @@ +#include "..\script_component.hpp" +/* + * Author: Dani (TCVM) + * Handles the map helper's draw event + * Resets arguments if not run recently and starts a watchdog to detect when weapon display unloaded + * + * Arguments: + * None + * + * Return Value: + * None + * + * Example: + * [] call ace_spike_fnc_mapHelperDraw + * + * Public: No + */ +#define __SPIKE_DISPLAY (uiNamespace getVariable "ACE_RscOptics_spike") +#define __SPIKE_RETICLE (__SPIKE_DISPLAY displayCtrl 242000) + +private _currentShooter = if (ACE_player call CBA_fnc_canUseWeapon) then {ACE_player} else {vehicle ACE_player}; +if (isNil QGVAR(arguments)) then { + TRACE_1("Starting optic draw",_this); + + // reset shooter var: + _currentShooter setVariable ["ace_missileguidance_target", nil, false]; + + GVAR(arguments) = [ + diag_frameNo, // Last run frame + [0, 0, 0] // currentTargetObject + ]; + + // Start up a watchdog for when the display is no longer shown (but might not be unloaded or null) + [{ + if (isNull (uiNamespace getVariable ["ACE_RscOptics_spike", displayNull])) exitWith {true}; + GVAR(arguments) params ["_lastRunFrame"]; + (diag_frameNo < _lastRunFrame) || {diag_frameNo > (_lastRunFrame + 1)} + }, { + TRACE_1("old/null display - ending optic draw",_this); + GVAR(arguments) = nil; + }, []] call CBA_fnc_waitUntilAndExecute; +}; + + +if (cameraView isEqualTo "GUNNER") then { + GVAR(arguments) set [0, diag_frameNo]; + + __SPIKE_RETICLE ctrlShow true; + GVAR(arguments) params ["", "_targetPosition"]; + + private _currentAmmo = _currentShooter ammo currentWeapon _currentShooter; + + private _designating = __SPIKE_DISPLAY getVariable [QGVAR(designate), 0]; + if (_currentAmmo != 0 && { _designating == 1 || _targetPosition isNotEqualTo [0, 0, 0] }) then { + private _viewASL = AGLToASL positionCameraToWorld [0,0,0]; + private _viewDir = _viewASL vectorFromTo (AGLToASL positionCameraToWorld [0,0,1]); + _targetPosition = [_viewASL, _viewDir, _designating, _targetPosition, _currentShooter, _currentShooter] call FUNC(getTargetPosition); + GVAR(arguments) set [1, _targetPosition]; + }; + + if (_currentAmmo == 0) then { + __SPIKE_RETICLE ctrlShow false; + } else { + if (_targetPosition isEqualTo [0, 0, 0]) then { + __SPIKE_RETICLE ctrlSetPosition [0, 0]; + + (__SPIKE_DISPLAY displayCtrl 243101) ctrlShow false; + (__SPIKE_DISPLAY displayCtrl 243201) ctrlShow false; + (__SPIKE_DISPLAY displayCtrl 243301) ctrlShow false; + + (__SPIKE_DISPLAY displayCtrl 241000) ctrlShow true; + } else { + (__SPIKE_DISPLAY displayCtrl 243101) ctrlShow true; + (__SPIKE_DISPLAY displayCtrl 243201) ctrlShow true; + (__SPIKE_DISPLAY displayCtrl 243301) ctrlShow true; + + (__SPIKE_DISPLAY displayCtrl 241000) ctrlShow false; + + private _seekerPositionScreen = worldToScreen ASLToAGL _targetPosition; + if (_seekerPositionScreen isEqualTo []) then { + _seekerPositionScreen = [0, 0]; + }; + _seekerPositionScreen set [0, _seekerPositionScreen#0 - 0.5]; + _seekerPositionScreen set [1, _seekerPositionScreen#1 - 0.5]; + + __SPIKE_RETICLE ctrlSetPosition _seekerPositionScreen; + + if (abs (_seekerPositionScreen#0) > 0.2 || abs (_seekerPositionScreen#1) > 0.2) then { + GVAR(arguments) set [1, [0, 0, 0]]; + }; + }; + _currentShooter setVariable [QGVAR(target), _targetPosition]; + }; + + __SPIKE_RETICLE ctrlCommit 0; +} else { + __SPIKE_RETICLE ctrlShow false; + (__SPIKE_DISPLAY displayCtrl 241000) ctrlShow false; +}; diff --git a/addons/spike/functions/fnc_midCourseTransition.sqf b/addons/spike/functions/fnc_midCourseTransition.sqf new file mode 100644 index 00000000000..0bcda540c2b --- /dev/null +++ b/addons/spike/functions/fnc_midCourseTransition.sqf @@ -0,0 +1,22 @@ +#include "..\script_component.hpp" +/* + * Author: Dani (TCVM) + * Condition to switch to next navigation profile + * + * Arguments: + * 0: Guidance Arg Array + * + * Return Value: + * None + * + * Example: + * [] call ace_spike_fnc_midCourseTransition + * + * Public: No + */ +//IGNORE_PRIVATE_WARNING ["_args"] - from upper scope +_args params ["", "", "", "", "_stateParams"]; +_stateParams params ["", "", "", "","_navigationParams"]; + +_navigationParams params ["_state"]; +_state isEqualTo STAGE_TERMINAL diff --git a/addons/spike/functions/fnc_navigation.sqf b/addons/spike/functions/fnc_navigation.sqf new file mode 100644 index 00000000000..a82a7eb4f43 --- /dev/null +++ b/addons/spike/functions/fnc_navigation.sqf @@ -0,0 +1,115 @@ +#include "..\script_component.hpp" +/* + * Author: Dani (TCVM) + * Sets up a top-attack profile. If we don't have a target: coast + * + * Arguments: + * Guidance Arg Array + * + * Return Value: + * Commanded acceleration normal to LOS in world space + * + * Example: + * [] call ace_spike_fnc_navigation + * + * Public: No + */ +#define ARM_TIME 0.5 +params ["_args", "_timestep", "", "_profileAdjustedTargetPos", "", "_navigationParams"]; +_args params ["_firedEH", "_launchParams", "_flightParams", "_seekerParams", "_stateParams", "_targetData", "_navigationStateData"]; +_firedEH params ["_shooter","","","","_ammo","","_projectile"]; +_launchParams params ["_shooter","_targetLaunchParams","_seekerType","_attackProfile","_lockMode","_laserInfo","_navigationType"]; +_targetLaunchParams params ["_target", "_targetPos", "_launchPos", "_launchDir", "_launchTime"]; +_flightParams params ["_pitchRate", "_yawRate", "_isBangBangGuidance"]; +_stateParams params ["_lastRunTime", "_seekerStateParams", "_attackProfileStateParams", "_lastKnownPosState", "", "_guidanceParameters"]; +_seekerParams params ["_seekerAngle", "_seekerAccuracy", "_seekerMaxRange", "_seekerMinRange"]; +_targetData params ["_targetDirection", "_attackProfileDirection", "_targetRange", "_targetVelocity", "_targetAcceleration"]; + +((velocity _projectile) call CBA_fnc_vect2polar) params ["", "_currentYaw", "_currentPitch"]; +private _projectilePos = getPosASLVisual _projectile; + +if (_navigationParams isEqualTo []) then { + _navigationParams set [0, STAGE_LAUNCH]; + _navigationParams set [1, CBA_missionTime]; + _navigationParams set [2, 0]; + _navigationParams set [3, 0]; +}; +_navigationParams params ["_stage", "_launchTime", "_coastAltitude", "_lastAltitudeError"]; +private _met = CBA_missionTime - _launchTime; + +if (_met >= BATTERY_LIFE) exitWith { + [0, 0, -_pitchRate] +}; + +private _navigationDebugStr = ""; +private _cmdAccel = [0, 0, 0]; + +private _2dVelocity = (velocity _projectile); +_2dVelocity set [2, 0]; + +private _directionToTarget = _projectilePos vectorFromTo _profileAdjustedTargetPos; +private _2dDirectionToTarget = [_directionToTarget#0, _directionToTarget#1, 0]; + +switch (_stage) do { + case STAGE_LAUNCH: { + private _error = LAUNCH_PITCH - _currentPitch; + _cmdAccel set [2, _error * 3]; + + if (_met >= LAUNCH_TIME && abs (_error) <= 5) then { + _navigationParams set [0, STAGE_CLIMB]; + }; + _navigationDebugStr = format ["SPIKE LAUNCH [Pitch - %1 Error - %2 MET - %3]", _currentPitch, _error, _met]; + }; + case STAGE_CLIMB: { + private _error = CLIMB_PITCH - _currentPitch; + _cmdAccel set [2, _error]; + + if (_met >= LAUNCH_TIME + CLIMB_TIME && abs (_error) <= 1) then { + _navigationParams set [0, STAGE_CRUISE]; + _navigationParams set [2, _projectilePos#2]; + }; + + _navigationDebugStr = format ["SPIKE CLIMB [Pitch - %1 Error - %2 MET - %3]", _currentPitch, _error, _met]; + }; + case STAGE_CRUISE: { + private _altitudeError = _coastAltitude - _projectilePos#2; + + private _pY = CRUISE_PRO_GAIN * _altitudeError; + private _dY = if (_timestep != 0) then { + CRUISE_DER_GAIN * (_altitudeError - _lastAltitudeError) / _timestep + } else { + 0 + }; + _navigationParams set [3, _altitudeError]; + + _cmdAccel set [2, (_pY + _dY) * _pitchRate]; + + if (_profileAdjustedTargetPos isNotEqualTo [0, 0, 0]) then { + (_2dDirectionToTarget call CBA_fnc_vect2polar) params ["", "_azimuthToTarget"]; + private _yawError = 4 * (_azimuthToTarget - _currentYaw) / 90; + _yawError = _yawError * _yawRate; + _cmdAccel set [0, _yawError]; + }; + + _navigationDebugStr = format ["SPIKE CRUISE [Altitude - %1 Error - %2 MET - %3]", _projectilePos#2, _altitudeError, _met]; + }; + default {}; +}; + +private _distance = _launchPos vectorDistance _profileAdjustedTargetPos; + +private _los = _projectilePos vectorFromTo _profileAdjustedTargetPos; +// subtract 500 so we dont get a perfect top-attack angle +private _ttgo = ((_projectilePos distance2D _profileAdjustedTargetPos) - 500) / (vectorMagnitude velocity _projectile); +private _angleToTarget = acos ((vectorDir _projectile) vectorCos _los); +private _atMinRotationAngle = _angleToTarget >= (_pitchRate * _ttgo); + +if (_met >= ARM_TIME && { _stage >= STAGE_LAUNCH } && { _profileAdjustedTargetPos isNotEqualTo [0, 0, 0] } && { _atMinRotationAngle }) then { + _navigationParams set [0, STAGE_TERMINAL]; +}; + +#ifdef DEBUG_MODE_FULL +drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [1,1,0,1], (ASLToAGL _projectilePos) vectorAdd [0, 0, 5], 0.75, 0.75, 0, _navigationDebugStr, 1, 0.025, "TahomaB"]; +#endif + +_projectile vectorModelToWorldVisual _cmdAccel diff --git a/addons/spike/functions/fnc_onFired.sqf b/addons/spike/functions/fnc_onFired.sqf new file mode 100644 index 00000000000..aef3da605e3 --- /dev/null +++ b/addons/spike/functions/fnc_onFired.sqf @@ -0,0 +1,92 @@ +#include "..\script_component.hpp" +/* + * Author: Dani (TCVM) + * Initialises SPIKE camera + * + * Arguments: + * 0: Fired EH + * + * Return Value: + * None + * + * Example: + * [firedEH] call ace_spike_fnc_onFired + * + * Public: No + */ + +params ["_firedEH"]; +_firedEH params ["_shooter","_weapon","","_mode","_ammo","","_projectile"]; + +private _missileGuidanceConfig = (configOf _projectile) >> "ace_missileguidance"; +// Setup camera array +private _cameraConfig = _missileGuidanceConfig >> "camera"; +private _cameraArray = [false]; +if (!isNull _cameraConfig && { (getNumber (_cameraConfig >> "enabled")) == 1 }) then { + _cameraArray set [0, true]; + _cameraArray set [1, getArray (_cameraConfig >> "fovLevels")]; + _cameraArray set [2, getNumber (_cameraConfig >> "initialFOV")]; + + _cameraArray set [3, getArray (_cameraConfig >> "enabledThermalTypes")]; + _cameraArray set [4, getText (_cameraConfig >> "initialThermalType")]; + + _cameraArray set [5, (getNumber (_cameraConfig >> "switchOnFire")) == 1]; + + _cameraArray set [6, getNumber (_cameraConfig >> "lerpFOV")]; + _cameraArray set [7, getNumber (_cameraConfig >> "fovChangeTime")]; + + _cameraArray set [8, [[0, 0, 0], [0, 0, 0], [0, 0, 0], false, false]]; // camera view data. [look direction, ground pos, point pos, moving camera x, moving camera y] + + private _cameraConfigGimbal = _cameraConfig >> "gimbal"; + _cameraArray set [9, [ + getNumber (_cameraConfigGimbal >> "enabled") == 1, + getNumber (_cameraConfigGimbal >> "gimbalAngleX"), + getNumber (_cameraConfigGimbal >> "gimbalAngleY"), + getNumber (_cameraConfigGimbal >> "gimbalSpeedX"), + getNumber (_cameraConfigGimbal >> "gimbalSpeedY"), + getNumber (_cameraConfigGimbal >> "gimbalInitOffsetX"), + getNumber (_cameraConfigGimbal >> "gimbalInitOffsetY"), + getArray (_cameraConfigGimbal >> "fovGimbalSpeedModifiers"), + getNumber (_cameraConfigGimbal >> "stabilizeWhenMoving") == 1, + getNumber (_cameraConfigGimbal >> "designateWhenStationary") == 1, + getNumber (_cameraConfigGimbal >> "trackLockedPosition") == 1 + ]]; + + private _cameraConfigReticle = _cameraConfig >> "reticle"; + _cameraArray set [10, [ + getText (_cameraConfigReticle >> "titleRsc"), + getNumber (_cameraConfigReticle >> "centerReticle"), + getArray (_cameraConfigReticle >> "controlsToDisappearOnLock"), + getArray (_cameraConfigReticle >> "controlsToAppearOnLock"), + getNumber (_cameraConfigReticle >> "leftGate"), + getNumber (_cameraConfigReticle >> "rightGate"), + getNumber (_cameraConfigReticle >> "topGate"), + getNumber (_cameraConfigReticle >> "bottomGate"), + getText (_cameraConfigReticle >> "uiNamespaceDialogVariable"), + getNumber (_cameraConfigReticle >> "reticleMovesWithTrack") == 1 + ]]; + + _cameraArray set [11, (getNumber (_cameraConfig >> "alwaysDesignate")) == 1]; + _cameraArray set [12, (getNumber (_cameraConfig >> "canStopDesignating")) == 1]; +}; + +private _preTarget = +(ACE_player getVariable [QGVAR(target), [0, 0, 0]]); +ACE_player setVariable [QGVAR(target), [0, 0, 0]]; +private _camera = [_projectile, _cameraArray, _shooter, _preTarget isEqualTo [0, 0, 0]] call FUNC(camera_init); +GVAR(projectileHashMap) set [hashValue _projectile, [_camera, _preTarget]]; +[{ + params ["_args", "_pfID"]; + _args params ["_firedEH", "_cameraArray", "_lastUpdate", "_camera", "_projectileHash"]; + + _firedEH params ["_shooter","_weapon","_muzzle","_mode","_ammo","_magazine","_projectile"]; + + if (!alive _projectile || isNull _projectile || isNull _shooter) exitWith { + GVAR(projectileHashMap) deleteAt _projectileHash; + [_camera] call FUNC(camera_destroy); + [_pfID] call CBA_fnc_removePerFrameHandler; + }; + + [_cameraArray, _projectile, CBA_missionTime - _lastUpdate, _camera] call FUNC(camera_update); + + _args set [2, CBA_missionTime]; +}, 0, [_firedEH, _cameraArray, CBA_missionTime, _camera, hashValue _projectile]] call CBA_fnc_addPerFrameHandler; diff --git a/addons/spike/functions/fnc_seeker.sqf b/addons/spike/functions/fnc_seeker.sqf new file mode 100644 index 00000000000..3425a56ae6a --- /dev/null +++ b/addons/spike/functions/fnc_seeker.sqf @@ -0,0 +1,76 @@ +#include "..\script_component.hpp" +/* + * Author: Dani (TCVM) + * Seeker Type: Spike Optical + * + * Arguments: + * 1: Guidance Arg Array + * 2: Seeker State + * + * Return Value: + * Missile Aim PosASL + * + * Example: + * [[], [], []] call ace_spike_fnc_seeker + * + * Public: No + */ + +params ["", "_args", "_seekerStateParams", "", "_timestep"]; +_args params ["_firedEH", "", "", "_seekerParams", "", "_targetData"]; +_seekerParams params ["_seekerAngle", "", "_seekerMaxRange", "_seekerMinRange"]; +_firedEH params ["","","","","","","_projectile"]; + +(GVAR(projectileHashMap) get hashValue _projectile) params ["_cameraNamespace", "_preTarget"]; + +private _seekerTargetPos = _cameraNamespace getVariable [QGVAR(seekerTargetPos), [0, 0, 0]]; +private _cameraPos = _cameraNamespace getVariable [QGVAR(cameraPos), [0, 0, 0]]; +private _logicPos = _cameraNamespace getVariable [QGVAR(logicPos), [0, 0, 0]]; + +private _seekerTargetInfo = _cameraNamespace getVariable [QGVAR(seekerTargetInfo), [false, [0, 0, 0], [0, 0, 0]]]; + +private _intersectObject = objNull; +private _designateInput = (_cameraNamespace getVariable [QGVAR(designateInput), [0]]) select 0; + +if (_seekerTargetPos isEqualTo [0, 0, 0]) then { + _seekerTargetPos = _preTarget; +}; + +if ((_seekerTargetPos isNotEqualTo [0, 0, 0]) || { (_designateInput == 1) }) then { + _seekerTargetPos = [_cameraPos, vectorNormalized _logicPos, _designateInput, _seekerTargetPos, _projectile] call FUNC(getTargetPosition); +}; + +_cameraNamespace setVariable [QGVAR(seekerTargetPos), _seekerTargetPos]; +_cameraNamespace setVariable [QGVAR(seekerTargetInfo), _seekerTargetInfo]; + +_seekerStateParams params [["_lastPositions", []], ["_lastAveragePosition", [0, 0, 0]], ["_velocity", [0, 0, 0]], ["_lastTimeCalculated", 0]]; +if (5 < count _lastPositions) then { + private _averagePosition = [0, 0, 0]; + { + _averagePosition = _averagePosition vectorAdd _x; + } forEach _lastPositions; + + _averagePosition = _averagePosition vectorMultiply (1 / count _lastPositions); + + if (_lastAveragePosition isNotEqualTo [0, 0, 0]) then { + private _dt = CBA_missionTime - _lastTimeCalculated; + if (_dt == 0) then { + _velocity = [0, 0, 0]; + } else { + _velocity = (_averagePosition vectorDiff _lastAveragePosition) vectorMultiply (1 / _dt); + _seekerStateParams set [3, CBA_missionTime]; + } + }; + _seekerStateParams set [1, _averagePosition]; + _lastPositions = []; +}; + +_targetData set [0, (getPosASLVisual _projectile) vectorFromTo _seekerTargetPos]; +_targetData set [2, (getPosASLVisual _projectile) vectorDistance _seekerTargetPos]; +_targetData set [3, _velocity]; + +_lastPositions pushBack _seekerTargetPos; +_seekerStateParams set [0, _lastPositions]; +_seekerStateParams set [2, _velocity]; + +_seekerTargetPos diff --git a/addons/spike/initKeybinds.inc.sqf b/addons/spike/initKeybinds.inc.sqf new file mode 100644 index 00000000000..0f4e622f645 --- /dev/null +++ b/addons/spike/initKeybinds.inc.sqf @@ -0,0 +1,74 @@ +#include "\a3\ui_f\hpp\defineDIKCodes.inc" + +["ACE3 Weapons", QGVAR(Designate), LLSTRING(Designate), +{ + [SPIKE_KEY_DESIGNATE, true] call FUNC(keyDown); +}, +{ + [SPIKE_KEY_DESIGNATE, false] call FUNC(keyDown); +}, +[DIK_TAB, [false, false, false]], false] call CBA_fnc_addKeybind; // Tab + +["ACE3 Weapons", QGVAR(LeaveCamera), LLSTRING(LeaveCamera), +{ + [SPIKE_KEY_LEAVE, true] call FUNC(keyDown); +}, +{ + [SPIKE_KEY_LEAVE, false] call FUNC(keyDown); +}, +[DIK_TAB, [false, true, false]], false] call CBA_fnc_addKeybind; // Ctrl+Tab + +["up", { + [CAMERA_KEY_UP, true] call FUNC(keyDown); +}, +{ + [CAMERA_KEY_UP, false] call FUNC(keyDown); +}] call EFUNC(missileguidance,keybind_add); + +["left", { + [CAMERA_KEY_LEFT, true] call FUNC(keyDown); +}, +{ + [CAMERA_KEY_LEFT, false] call FUNC(keyDown); +}] call EFUNC(missileguidance,keybind_add); + +["right", { + [CAMERA_KEY_RIGHT, true] call FUNC(keyDown); +}, +{ + [CAMERA_KEY_RIGHT, false] call FUNC(keyDown); +}] call EFUNC(missileguidance,keybind_add); + +["down", { + [CAMERA_KEY_DOWN, true] call FUNC(keyDown); +}, +{ + [CAMERA_KEY_DOWN, false] call FUNC(keyDown); +}] call EFUNC(missileguidance,keybind_add); + +["ACE3 Weapons", QGVAR(CycleVisionMode), LLSTRING(Camera_ViewModeCycle), +{ + [CAMERA_KEY_CHANGE_VISION_MODE, true] call FUNC(keyDown); +}, +{ + [CAMERA_KEY_CHANGE_VISION_MODE, false] call FUNC(keyDown); +}, +[DIK_N, [false, false, false]], false] call CBA_fnc_addKeybind; //N + +["ACE3 Weapons", QGVAR(ZoomIn), LLSTRING(ZoomIn), +{ + [CAMERA_KEY_ZOOM_IN, true] call FUNC(keyDown); +}, +{ + [CAMERA_KEY_ZOOM_IN, false] call FUNC(keyDown); +}, +[DIK_ADD, [false, false, false]], false] call CBA_fnc_addKeybind; //Keypad+ + +["ACE3 Weapons", QGVAR(ZoomOut), LLSTRING(ZoomOut), +{ + [CAMERA_KEY_ZOOM_OUT, true] call FUNC(keyDown); +}, +{ + [CAMERA_KEY_ZOOM_OUT, false] call FUNC(keyDown); +}, +[DIK_SUBTRACT, [false, false, false]], false] call CBA_fnc_addKeybind; //Keypad- diff --git a/addons/spike/script_component.hpp b/addons/spike/script_component.hpp new file mode 100644 index 00000000000..8ca996617f3 --- /dev/null +++ b/addons/spike/script_component.hpp @@ -0,0 +1,45 @@ +#define COMPONENT spike +#define COMPONENT_BEAUTIFIED SPIKE +#include "\z\ace\addons\main\script_mod.hpp" + +// #define DEBUG_MODE_FULL +// #define DISABLE_COMPILE_CACHE +// #define ENABLE_PERFORMANCE_COUNTERS + +#ifdef DEBUG_ENABLED_SPIKE + #define DEBUG_MODE_FULL +#endif + +#ifdef DEBUG_SETTINGS_SPIKE + #define DEBUG_SETTINGS DEBUG_SETTINGS_SPIKE +#endif + +#include "\z\ace\addons\main\script_macros.hpp" + +#define CAMERA_KEY_LEFT 0 +#define CAMERA_KEY_RIGHT 1 +#define CAMERA_KEY_UP 2 +#define CAMERA_KEY_DOWN 3 +#define CAMERA_KEY_ZOOM_IN 4 +#define CAMERA_KEY_ZOOM_OUT 5 +#define CAMERA_KEY_CHANGE_VISION_MODE 6 +#define SPIKE_KEY_DESIGNATE 7 +#define SPIKE_KEY_LEAVE 8 + +#define STAGE_LAUNCH 0 +#define STAGE_CLIMB 1 +#define STAGE_CRUISE 2 +#define STAGE_TERMINAL 3 + +#define LAUNCH_TIME 3 +#define CLIMB_TIME 5 + +#define LAUNCH_PITCH 45 +#define CLIMB_PITCH 5 + +#define CRUISE_PRO_GAIN 0.3 +#define CRUISE_DER_GAIN 3 + +#define BATTERY_LIFE (43 + random 10) + +#define GIMBAL_LOGIC_OFFSET 10 diff --git a/addons/spike/stringtable.xml b/addons/spike/stringtable.xml new file mode 100644 index 00000000000..0d293b9eb33 --- /dev/null +++ b/addons/spike/stringtable.xml @@ -0,0 +1,53 @@ + + + + + Cycle Camera Vision Mode + カメラの映像モードを切り替え + + + Spike LR + スパイク LR + + + LR + LR + + + Leave Camera + カメラから離れる + + + MANUAL + マニュアル + + + Pan Camera Down + カメラを下に向ける + + + Pan Camera Left + カメラを左に向ける + + + Pan Camera Right + カメラを右に向ける + + + Pan Camera Up + カメラを上に向ける + + + Zoom Camera In + カメラをズームイン + + + Zoom Camera Out + カメラをズームアウト + + + Designate + 目標を指定 + + +