Missileguidance -- rewrite to use real-world guidance principles, general refactor (#10019)

* update todo with list of weapons

* Add AIM-120 with ARH. Change AHR to not always seek all targets

* Implement some generic modes of flight

* implement augmented proportional navigation

* Add zero-effort miss. Seeker now feeds data to navigation

* Fix bug with ZEM

* remove placeholder

* Change out missile types to reflect navigation scheme they use IRL

* remove uneeded sets

* chane navigation profiles to go for attack direction instead of target

* multiple changes

* SALH: only update seeker pos if it within small error

* IR seeker and AIM-9M

* small tweaks

* more tweaks

* todo: fix nlaw

* update todo

* implement course state machine

* Improve radar simulation

* enable chaff decoy

* add config entries

* Implement optical mavericks

* Allow for debug info to be displayed via variable

* Change AIM-9 agility

* update dragon to not use new guidance

* Tweak flight parameters. Add R-77

* Add AIM-120A

* add ASRAAM

* add R-73/4

* Add all GBU-12s that exist for all planes

* update todo

* update todo for ground based weapons

* Add GPS selector dialog for GPS munitions

* add TOO TGP

* Add SDB and JDAM

* update todo

* fix gimbal lock via rotating with quaternions

* tweak JDAM guidance

* Better attack angle calculation. Does not get exactly right, but works for all cases.

Need to implement a PID controller navigation type to get a correct attack angle

* Fix SACLOS missiles

* Fix NLAW and PLOS

The missile pitches up due to initial angle stuff. I don't know if this is behaviour we want to fix or not?

* update todo

* update todo

* Add Vikhr ATGM

* Add long range SAMs

* fix SAM animations

* Add all ground based missiles. Tweak AIM-9 flare angle

* remove debugh print

* Fix Wiesel ATGM animation. Tweak SACLOS values

* Add action to get into jdam settings

* fix all script components

* Add paremter to allow weathervaning

Weapons usually tend toward the velocity vector due to aerodynamics - calculate side slip and use calculation to do this

* Add new navigation profile. Tweak javelin mid-course guidance

* Process all navigation onFired. Send the correct array to function

* fix missing semicolons

* Add hellfire mid course guidance

* Tweak hellfire attack profile

* change how IR seeker performs

The angle check is better now. Relative velocites guarenteed

* tabs -> spaces

* localise aim120

* localise aim9

* localise gbu

* fix IR tracker losing lock immediately due to being blocked by launcher

* localise manpad

* actually localise manpad

* localise maverick

* localise missile guidance

* localise sam

* localise sdb

* localise vikhr

* tabs -> spaces again

* init commit 9m14

* update stringtables so they are in titlecase

* crash on load

* perplexed

* fix game crashing on load

* Add joystick model

* Add realistic 9m14 behaviour

the 9m14 control joystick can attach 4 9m14 launchers at a time. Replicate that here

* add MCLOS guidance

* add the ability to see a light trail

* tweak HOT guidance params

* tweak metis guidance params

* tweak Vikhr guidance

* add trail to vikhr

* Add Iron Dome interceptor API

* Optimise iron dome

* Only launch if we are above a threshold angle

* add local event for mission makers

* change events to allow for any number of arguments

* avoid making un-needed calls to the server

* Update doppler seeker to allow the tracking and killing of projectiles

* Avoid overshooting target

* Add settings for various things

* Add missile hiding for malyutka

* init commit of cruise missile

* Various - Add missing units/weapons to cfgPatches (#8175)

* Various - Add missing units/weapons to cfgPatches

* Update config.cpp

* Documentation - Add Mikero binarization note (#8172)

* Bump is-svg from 4.2.1 to 4.3.1 in /docs/src (#8177)

Bumps [is-svg](https://github.com/sindresorhus/is-svg) from 4.2.1 to 4.3.1.
- [Release notes](https://github.com/sindresorhus/is-svg/releases)
- [Commits](https://github.com/sindresorhus/is-svg/compare/v4.2.1...v4.3.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Adjust "New issue" link to lead to template selection page (#8182)

We want people to choose a template when creating a new issue

* Update linguist settings (#8151)

* Typo in usage example (#8201)

* Mk6 Mortar - Fix README.md typo (#8205)

* Overhaul CSW docs (#8206)

* Medical - Add Alternative Survival Chance (#8192)

Co-authored-by: TyroneMF <TyroneMF@hotmail.com>
Co-authored-by: Filip Maciejewski <veteran29@users.noreply.github.com>
Co-authored-by: Kyle Mckay <5459452+kymckay@users.noreply.github.com>

* Fix make.py temp files cleanup (#8189)

* Fix Injured Sounds Not Playing At Altitudes Above 70 (#8212)

* Particles - Fix macro (#8214)

* initial push

* Update script_component.hpp

* Update script_component.hpp

* Update script_component.hpp

* Map - Fix stuck map compass size (add 0.1 zoom duration) (#8176)

Co-authored-by: jonpas <jonpas33@gmail.com>

* Medical - Allow unconscious unit in Taru pods (#8168)

Co-authored-by: Filip Maciejewski <veteran29@users.noreply.github.com>

* Name Tags - Add ability to set custom rank icon (#8174)

* Tools - Add script to update HEMTT include folder (#8134)

* Tools - Add script to check sqf/config with sqfvm (#8137)

* Medical - Add Vehicle Crashes setting (#8149)

Co-authored-by: jonpas <jonpas33@gmail.com>

* Dragging - Add new dragging animations (#7950)

* New custom animation added

* Adjusted CfgMoves and the script components

* New drop animation and key handler

* CBA settings and new ManActions added

* Adjustments to the drop animation

* Added translations and fixed some stuff

* Update CfgMovesBasic.hpp

* Fix translations

Co-authored-by: Elgin675 <elgin675@hotmail.com>
Co-authored-by: Blutze <37950828+Blutze@users.noreply.github.com>

* Use the same key to drop object

* Update addons/dragging/stringtable.xml

Co-authored-by: Jo David <github@jonathandavid.de>

* Fix French translation

Co-authored-by: Elgin675 <elgin675@hotmail.com>

* Lower the weapon accuracy of the drag animations

* Removed auto-switch to handgun

* Update fnc_startDrag.sqf

 - Holding a launcher breaks the firing animation.
 - Now the unit has to hold either a primary weapon or handgun.

* Handle the unit's current weapon

Co-authored-by: BaerMitUmlaut <BaerMitUmlaut@users.noreply.github.com>

* Update addons/dragging/functions/fnc_handlePlayerWeaponChanged.sqf

Co-authored-by: Filip Maciejewski <veteran29@users.noreply.github.com>

* Update addons/dragging/initSettings.sqf

Co-authored-by: Elgin675 <elgin675@hotmail.com>
Co-authored-by: Blutze <37950828+Blutze@users.noreply.github.com>
Co-authored-by: Jo David <github@jonathandavid.de>
Co-authored-by: BaerMitUmlaut <BaerMitUmlaut@users.noreply.github.com>
Co-authored-by: Filip Maciejewski <veteran29@users.noreply.github.com>
Co-authored-by: jonpas <jonpas33@gmail.com>

* Dragging - Fix build issue (#8219)

* Documentation - Remove DeTex requirement (#8220)

DeTex is subscriber-only now, not required for building ACE3.

* User Interface - Add setting for Development Build watermark (#8140)

Co-authored-by: jonpas <jonpas33@gmail.com>

* RHS USAF Compat - Nightforce NXS scopes updated (#8222)

* Interact Menu - Make List default (#8217)

* Markers - Add scale slider (#8059)

Co-authored-by: commy2 <commy-2@gmx.de>
Co-authored-by: PabstMirror <pabstmirror@gmail.com>
Co-authored-by: jonpas <jonpas33@gmail.com>

* Initial push (#8225)

* MicroDAGR - Add mode switching keybinds (#8216)

* Initial push

* Mode cycling

* Use defines

* RHS ARF Compat - Fix RPK74M wrong ammo bug (#8223)

* Fixed RPK74M wrong ammo bug

* Fix wrong deleted lines of code

* Edit rjs_weap_rpk74_base to rhs_weap_rpk_base

Co-authored-by: Dabako <github@dabakoworld.de>
Co-authored-by: PabstMirror <pabstmirror@gmail.com>

* Extensions - Various Improvments (#8227)

* Translations - Spanish (#8229)

Complete to date spanish translation.

* Attach - Added attached/detaching events (#8193)

* Added ace_attach attached/detach events

* Apply suggestions from code review

Co-authored-by: Jo David <github@jonathandavid.de>

* Apply suggestions from code review

Co-authored-by: jonpas <jonpas33@gmail.com>

Co-authored-by: Jo David <github@jonathandavid.de>
Co-authored-by: jonpas <jonpas33@gmail.com>

* Markers - Fix marker scale (#8233)

* Cargo - paradrop fixes (#8203)

* Small fixes in ace_cargo

Makes paradropItem and unloadItem invoke event "ace_cargoUnloaded" with parameters _itemObject, _vehicle and "paradrop" or "unload" respectivelty.
paradropItem invoked said event but if item was classname it would send classname to event which is not particularly useful.

* Update XEH_postInit.sqf

Moved event invocation to unloadItem, this provides ability to always pass object to eventhandlers.

* code refactor

refactored according to https://github.com/acemod/ACE3/pull/8203#discussion_r610394484

* Update fnc_paradropItem.sqf

apply new fix

* Revert "Update fnc_paradropItem.sqf"

This reverts commit 3db5cc1910.

* Update fnc_paradropItem.sqf

fixed hint that was broken by previous fixes

* style check

Co-authored-by: commy2 <commy-2@gmx.de>

* style check

Co-authored-by: commy2 <commy-2@gmx.de>

* naming convention

replaced object references called _item with _object.

* Update fnc_paradropItem.sqf

Co-authored-by: commy2 <commy-2@gmx.de>

* Parachute - Add failure chance (#8226)

* Initial push

* disable forgotten define

* Improve exitWith check

* Requested/suggested changes

* Requested changes

* Requested changes

* Bump lodash from 4.17.20 to 4.17.21 in /docs/src (#8244)

Bumps [lodash](https://github.com/lodash/lodash) from 4.17.20 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.20...4.17.21)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump grunt from 1.0.4 to 1.3.0 in /docs/src (#8246)

Bumps [grunt](https://github.com/gruntjs/grunt) from 1.0.4 to 1.3.0.
- [Release notes](https://github.com/gruntjs/grunt/releases)
- [Changelog](https://github.com/gruntjs/grunt/blob/main/CHANGELOG)
- [Commits](https://github.com/gruntjs/grunt/compare/v1.0.4...v1.3.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump hosted-git-info from 2.5.0 to 2.8.9 in /docs/src (#8247)

Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.5.0 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.5.0...v2.8.9)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Markers - Fix JIP scale (#8248)

* Night Vision - Add color variants to all generations (#8209)

* Fix wrong shot size in birdshot strings (#8253)

* Patachute - Add sound effect when cutting (#8239)

* Changes poisson disc to centered random. (#8257)

It looks nicer and distributes more realistically. I don't know what I was thinking when I first put in poisson disc.

* Fix tabs and manpad UBC

* fix tabs in aim9

* progress update: tercom now flies to acquisition basket

* add TERCOM state defines

* work on parsing heightmap

* Change GBU navigation

* remove testing constants

* remove TERCOM guidance

* better JDAM simulation

Attack heading and impact angle now are followed better

* Better flare decoy calculation

* Update 9m14_joystick.p3d

removed p:\ part from proxy path:
was: `\p:\a3\data_f\proxies\gunner_static_low01\gunner.p3d`

* Destroy projectile on all clients

The projectile needs to be destroyed on all clients to stop it from being simulated. If not destroyed where projectile is local, it will still explode when hitting the ground. If not destroyed on remote clients, the projectile will still be visible but deal no damage

* fix NLAW navigation profile

* fix uninitialised values

* make over-fly trajectory over-fly

* remove malyutka

* SACLOS tweaks

* small changes

* fix bug in laser

* make pronav use the correct units

* change back

* add dev function to get ammo which has MG class data

* improve lin navigation

* Minor style

* add spike ATGM

* Allow multiple cameras to exist at once. Fix script error

* fix bug with angles close to 0

* remove debug

* misc updates

* Handle pre-tracking

* Tweak target acquisition. Reset target after shooting

* Tweak how targets are acquired. Fix velocity calculation

* tweak navigation to allow for <200m shots

* enhance target designation

* Stringtable addition

* Config changes

* more SACLOS tweaks

* Some minor fixes

* fix undefined array

* Only play sound when Spike is launcher. Change onFired to missileguidance

* Tweak IR seeker. Fix MCLOS fired from vehicles

* Remove debug sets

* Update all Line guidance missiles

* remove debug sets

* tweak javelin to make minimum ranges more realistic

* cut spike

* add better error messaging

* better debug info

* Rewrite documentation

* Add ability to disable iron dome (disabled by default)

* Change name

* fix typo

* Remove null projectiles from iron dome nonTrackingProjectiles

* HEMTT v10 Compatibility

* Update addons/field_rations/CfgUIGrids.hpp

Co-authored-by: PabstMirror <pabstmirror@gmail.com>

* fix PabstMirror review

* optionals

* Change configs to enclose strings for calculations

* Fix NLAW guidance that was causing massive overfly

* make tweaks for PLOS movement path

* fix NLAW overfly

* Fix hemmit merge

* fix header

* Update fnc_seekerType_SACLOS.sqf

* Update to new include paths

* Update fnc_midCourseTransition.sqf

* initSettings.inc.sqf

* Remove weapon additions

* revert old weapons, pt1

* revert pt2

* readd javelin

* split iron dome

* cleanup laser

* remove GPS files

* remove gps definitions

* remove IR

* remove MCLOS

* remove doppler

* final cleanup

* restore final final i promise

* cleanup arbitrary files

* small cleanup

* fix hemtt warnings

* cleanup unused var

* fix preping some missing files (jdam/doppler)

* move cam func

---------

Co-authored-by: PabstMirror <pabstmirror@gmail.com>
Co-authored-by: jonpas <jonpas33@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jo David <github@jonathandavid.de>
Co-authored-by: Filip Maciejewski <veteran29@users.noreply.github.com>
Co-authored-by: Pascal Dunaj <32539404+t-zilla@users.noreply.github.com>
Co-authored-by: BaerMitUmlaut <BaerMitUmlaut@users.noreply.github.com>
Co-authored-by: Brett <brett@bmandesigns.com>
Co-authored-by: TyroneMF <TyroneMF@hotmail.com>
Co-authored-by: Kyle Mckay <5459452+kymckay@users.noreply.github.com>
Co-authored-by: Dystopian <sddex@ya.ru>
Co-authored-by: commy2 <commy-2@gmx.de>
Co-authored-by: R3voA3 <robertseibel@outlook.de>
Co-authored-by: Steve Zhao <ampersand38@gmail.com>
Co-authored-by: Kyle J. McKeown <Drift91@users.noreply.github.com>
Co-authored-by: Salluci <69561145+Salluci@users.noreply.github.com>
Co-authored-by: Dániel Boros <43353942+Malbryn@users.noreply.github.com>
Co-authored-by: Elgin675 <elgin675@hotmail.com>
Co-authored-by: Blutze <37950828+Blutze@users.noreply.github.com>
Co-authored-by: Laid3acK <bal2.chris@orange.fr>
Co-authored-by: frankplow <post@frankplowman.com>
Co-authored-by: JoramD <j.davids@hotmail.nl>
Co-authored-by: Dabako <dabako@gmx.de>
Co-authored-by: Dabako <github@dabakoworld.de>
Co-authored-by: Abogado <regiregi22@hotmail.com>
Co-authored-by: Dedmen Miller <dedmen@users.noreply.github.com>
Co-authored-by: Lupus the Canine <tymoteusz.2000.0+GitHub@gmail.com>
Co-authored-by: Drofseh <Drofseh@users.noreply.github.com>
Co-authored-by: LorenLuke <LukeLLL@aol.com>
Co-authored-by: SzwedzikPL <szwedzikpl@gmail.com>
Co-authored-by: BrettMayson <brett@mayson.io>
This commit is contained in:
Bailey Danyluk 2024-08-23 08:21:31 -06:00 committed by GitHub
parent 1f67058dda
commit 8ac2d09c31
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
37 changed files with 1030 additions and 287 deletions

View File

@ -6,6 +6,11 @@ class GVAR(AttackProfiles) {
functionName = QFUNC(attackProfile_LIN);
};
// empty classes for backwards compat
class MID: LIN {
};
class HI: LIN {
};
class DIR {
name = "";
visualName = "";
@ -13,19 +18,12 @@ class GVAR(AttackProfiles) {
functionName = QFUNC(attackProfile_DIR);
};
class MID {
class LOFT {
name = "";
visualName = "";
description = "";
functionName = QFUNC(attackProfile_MID);
};
class HI {
name = "";
visualName = "";
description = "";
functionName = QFUNC(attackProfile_HI);
functionName = QFUNC(attackProfile_LOFT);
};
class JAV_DIR {
name = "";
@ -82,12 +80,39 @@ class GVAR(SeekerTypes) {
functionName = QFUNC(seekerType_SACLOS);
onFired = QFUNC(SACLOS_onFired);
};
class ARH {
class MillimeterWaveRadar {
name = "";
visualName = "";
description = "";
functionName = QFUNC(seekerType_ARH);
onFired = QFUNC(ahr_onFired);
functionName = QFUNC(seekerType_MWR);
onFired = QFUNC(mwr_onFired);
};
};
class GVAR(NavigationTypes) {
class Direct {
functionName = QFUNC(navigationType_direct);
onFired = "";
};
class Line {
functionName = QFUNC(navigationType_line);
onFired = QFUNC(line_onFired);
};
class LineOfSight {
functionName = QFUNC(navigationType_lineOfSight);
onFired = QFUNC(proNav_onFired);
};
class ProportionalNavigation {
functionName = QFUNC(navigationType_proNav);
onFired = QFUNC(proNav_onFired);
};
class AugmentedProportionalNavigation {
functionName = QFUNC(navigationType_augmentedProNav);
onFired = QFUNC(proNav_onFired);
};
class ZeroEffortMiss {
functionName = QFUNC(navigationType_zeroEffortMiss);
onFired = QFUNC(proNav_onFired);
};
};

View File

@ -6,6 +6,8 @@ PREP(changeMissileDirection);
PREP(checkSeekerAngle);
PREP(checkLos);
PREP(dev_ProjectileCamera);
PREP(onFired);
PREP(onIncomingMissile);
@ -16,28 +18,44 @@ PREP(doSeekerSearch);
PREP(doHandoff);
PREP(handleHandoff);
//re-enable after feature merge - PREP(shouldFilterRadarHit);
// Attack Profiles
PREP(attackProfile_AIR);
PREP(attackProfile_DIR);
PREP(attackProfile_HI);
PREP(attackProfile_LIN);
PREP(attackProfile_MID);
PREP(attackProfile_LOFT);
PREP(attackProfile_WIRE);
PREP(attackProfile_BEAM);
//re-enable after feature merge - PREP(attackProfile_JDAM);
// Javelin profiles
PREP(attackProfile_JAV_DIR);
PREP(attackProfile_JAV_TOP);
// Navigation Profiles
PREP(navigationType_zeroEffortMiss);
PREP(navigationType_augmentedProNav);
PREP(navigationType_proNav);
PREP(navigationType_lineOfSight);
PREP(navigationType_line);
PREP(navigationType_direct);
// Seeker search functions
PREP(seekerType_SALH);
PREP(seekerType_Optic);
PREP(seekerType_SACLOS);
PREP(seekerType_ARH);
//re-enable after feature merge - PREP(seekerType_Doppler);
PREP(seekerType_MWR);
// Attack Profiles OnFired
PREP(wire_onFired);
// Seeker OnFired
PREP(SACLOS_onFired);
PREP(ahr_onFired);
PREP(mwr_onFired);
// Navigation OnFired
PREP(proNav_onFired);
PREP(line_onFired);

View File

@ -2,12 +2,10 @@
[QGVAR(handoff), LINKFUNC(handleHandoff)] call CBA_fnc_addEventHandler;
["ACE3 Weapons", QGVAR(cycleFireMode), localize LSTRING(CycleFireMode),
{
["ACE3 Weapons", QGVAR(cycleFireMode), localize LSTRING(CycleFireMode), {
[] call FUNC(cycleAttackProfileKeyDown);
false
},
{
}, {
false
},
[15, [false, true, false]], false] call CBA_fnc_addKeybind; //Ctrl+Tab Key

View File

@ -11,4 +11,15 @@ 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(debug_enableMissileCamera) = false;
GVAR(debug_drawGuidanceInfo) = false;
#ifdef DRAW_GUIDANCE_INFO
GVAR(debug_drawGuidanceInfo) = true;
#endif
#ifdef ENABLE_PROJECTILE_CAMERA
GVAR(debug_enableMissileCamera) = true;
#endif
ADDON = true;

View File

@ -0,0 +1,26 @@
#include "..\script_component.hpp"
private _configs = configProperties [configFile >> "CfgAmmo", QUOTE((isClass _x) && { isClass (_x >> QUOTE(QUOTE(ADDON)))})];
private _seekerTypes = createHashMap;
private _navigationTypes = createHashMap;
private _attackProfiles = createHashMap;
{
private _seekerType = getText (_x >> QUOTE(ADDON) >> "defaultSeekerType");
private _navigationType = getText (_x >> QUOTE(ADDON) >> "defaultNavigationType");
private _attackProfile = getText (_x >> QUOTE(ADDON) >> "defaultAttackProfile");
private _seekers = _seekerTypes getOrDefault [_seekerType, []];
private _navigations = _navigationTypes getOrDefault [_navigationType, []];
private _attacks = _attackProfiles getOrDefault [_attackProfile, []];
_seekers pushBack configName _x;
_navigations pushBack configName _x;
_attacks pushBack configName _x;
_seekerTypes set [_seekerType, _seekers];
_navigationTypes set [_navigationType, _navigations];
_attackProfiles set [_attackProfile, _attacks];
} forEach _configs;
[_seekerTypes, _navigationTypes, _attackProfiles]

View File

@ -32,4 +32,3 @@ _seekerStateParams set [2, _animationSourceGun];
_seekerStateParams set [3, _usePilotCamera || { (_shooter isKindOf "Plane") && hasPilotCamera _shooter }];
if ((_shooter isKindOf "Plane") && !hasPilotCamera _shooter) then { WARNING("SACLOS fired from planes without pilot camera unsupported!"); };

View File

@ -17,31 +17,26 @@
* Public: No
*/
params ["_seekerTargetPos", "_args", "_attackProfileStateParams"];
_args params ["_firedEH"];
_args params ["_firedEH", "", "", "", "", "_targetData"];
_firedEH params ["_shooter","","","","","","_projectile"];
_attackProfileStateParams params["_maxCorrectableDistance", "_wireCut", "_randomVector", "_crosshairOffset", "_seekerMaxRangeSqr", "_seekerMinRangeSqr", "_wireCutSource", "_distanceAheadOfMissile"];
_attackProfileStateParams params ["_maxCorrectableDistance", "_wireCut", "_lastInput", "_crosshairOffset", "_seekerMaxRangeSqr", "_seekerMinRangeSqr", "_wireCutSource", "_distanceAheadOfMissile"];
private _projectilePos = getPosASL _projectile;
private _shooterPos = getPosASL _shooter;
private _shooterDir = vectorNormalized(_seekerTargetPos vectorDiff _shooterPos);
private _distanceToProjectile = _shooterPos vectorDistanceSqr _projectilePos;
if (_distanceToProjectile > _seekerMaxRangeSqr || { _seekerTargetPos isEqualTo [0, 0, 0] } || { _distanceToProjectile < _seekerMinRangeSqr }) exitWith {
// return position 50m infront of projectile
_projectilePos vectorAdd (_projectile vectorModelToWorld [0, 50, 0])
if ((_distanceToProjectile > _seekerMaxRangeSqr) || { _wireCut }) exitWith {
// wire snap, random direction
if !(_wireCut) then {
_attackProfileStateParams set [1, true];
};
_lastInput
};
private _relativeCorrection = _projectile vectorWorldToModel (_projectilePos vectorDiff _seekerTargetPos);
_relativeCorrection = _relativeCorrection vectorDiff _crosshairOffset;
private _final = _seekerTargetPos vectorAdd _crosshairOffset;
_attackProfileStateParams set [2, _final];
private _magnitude = vectorMagnitude [_relativeCorrection select 0, 0, _relativeCorrection select 2];
private _fovImpulse = 1 min (_magnitude / _maxCorrectableDistance); // the simulated impulse for the missile being close to the center of the crosshair
_targetData set [0, _projectilePos vectorFromTo _final];
// Adjust the impulse due to near-zero values creating wobbly missiles?
private _correction = _fovImpulse;
_relativeCorrection = (vectorNormalized _relativeCorrection) vectorMultiply _correction;
private _returnPos = _projectilePos vectorDiff (_projectile vectorModelToWorld _relativeCorrection);
_returnPos vectorAdd (_shooterDir vectorMultiply _distanceAheadOfMissile)
_final

View File

@ -1,8 +1,8 @@
#include "..\script_component.hpp"
/*
* Author: jaynus / nou
* Author: tcvm
* Attack profile: DIR
* TODO: falls back to Linear
* Returns target position with no modifications
*
* Arguments:
* 0: Seeker Target PosASL <ARRAY>
@ -18,4 +18,5 @@
* Public: No
*/
_this call FUNC(attackProfile_LIN);
params ["_seekerTargetPos"];
_seekerTargetPos

View File

@ -1,21 +0,0 @@
#include "..\script_component.hpp"
/*
* Author: jaynus / nou
* Attack profile: HI
* TODO: falls back to Linear
*
* Arguments:
* 0: Seeker Target PosASL <ARRAY>
* 1: Guidance Arg Array <ARRAY>
* 2: Attack Profile State <ARRAY>
*
* Return Value:
* Missile Aim PosASL <ARRAY>
*
* Example:
* [[1,2,3], [], []] call ace_missileguidance_fnc_attackProfile_HI;
*
* Public: No
*/
_this call FUNC(attackProfile_LIN);

View File

@ -18,8 +18,10 @@
*/
params ["_seekerTargetPos", "_args"];
_args params ["_firedEH"];
_args params ["_firedEH", "_launchParams"];
_firedEH params ["_shooter","","","","","","_projectile"];
_launchParams params ["","_targetLaunchParams"];
_targetLaunchParams params ["", "", "_launchPos"];
if (_seekerTargetPos isEqualTo [0,0,0]) exitWith {_seekerTargetPos};
@ -30,11 +32,18 @@ private _distanceToTarget = _projectilePos vectorDistance _seekerTargetPos;
private _distanceToShooter = _projectilePos vectorDistance _shooterPos;
private _distanceShooterToTarget = _shooterPos vectorDistance _seekerTargetPos;
private _ttgo = _distanceToTarget / (vectorMagnitude velocity _projectile);
TRACE_2("",_distanceToTarget,_distanceToShooter);
// Add height depending on distance for compensate
private _addHeight = [0,0,0];
private _2dDistance = (800 + (_projectilePos distance2D _launchPos)) / (_projectilePos distance2D _seekerTargetPos);
if (_2dDistance <= 1) then {
_addHeight = [0, 0, (_projectilePos#2) + 8];
} else {
// Always climb an arc on initial launch if we are close to the round
if ((((ASLtoAGL _projectilePos) select 2) < 5) && {_distanceToShooter < 15}) then {
_addHeight = _addHeight vectorAdd [0,0,_distanceToTarget];
@ -46,11 +55,6 @@ if ((((ASLtoAGL _projectilePos) select 2) < 5) && {_distanceToShooter < 15}) the
TRACE_1("climb - below target and far",_addHeight);
};
};
// Projectile above target
if ((_projectilePos select 2) > (_seekerTargetPos select 2)) then {
TRACE_1("above - far",_addHeight);
_addHeight = _addHeight vectorAdd [0,0, _distanceToTarget / 50];
};
private _returnTargetPos = _seekerTargetPos vectorAdd _addHeight;

View File

@ -0,0 +1,53 @@
#include "..\script_component.hpp"
/*
* Author: jaynus / nou
* Attack profile: AIR
* TODO: falls back to Linear
*
* Arguments:
* 0: Seeker Target PosASL <ARRAY>
* 1: Guidance Arg Array <ARRAY>
* 2: Seeker State <ARRAY>
*
* Return Value:
* Missile Aim PosASL <ARRAY>
*
* Example:
* [[1,2,3], [], []] call ace_missileguidance_fnc_attackProfile_AIR;
*
* Public: No
*/
params ["_seekerTargetPos", "_args"];
_args params ["_firedEH", "_launchParams", "_flightParams", "_seekerParams", "_stateParams"];
_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","_navigationParams"];
_seekerParams params ["_seekerAngle", "_seekerAccuracy", "_seekerMaxRange", "_seekerMinRange"];
if (_seekerTargetPos isEqualTo [0, 0, 0]) exitWith {
_seekerTargetPos
};
private _projectilePos = getPosASLVisual _projectile;
private _distanceToTarget2d = _projectilePos distance2d _seekerTargetPos;
private _closingRate = vectorMagnitude velocity _projectile;
private _timeToGo = (_projectilePos distance _seekerTargetPos) / _closingRate;
// we could do stuff like desired attack angle, but I'm not going that far today
private _los = vectorNormalized (_seekerTargetPos vectorDiff _projectilePos);
private _angleToTarget = acos ((vectorDir _projectile) vectorCos _los);
private _atMinRotationAngle = _angleToTarget >= (0.5 * _pitchRate * _timeToGo);
private _returnTargetPos = _seekerTargetPos;
if (!_atMinRotationAngle && _distanceToTarget2d >= 500 && _timeToGo >= 10) then {
// 10 degree pitch up
_returnTargetPos = _seekerTargetPos vectorAdd [0, 0, (_projectilePos distance _seekerTargetPos) * sin 10];
};
_returnTargetPos

View File

@ -1,21 +0,0 @@
#include "..\script_component.hpp"
/*
* Author: jaynus / nou
* Attack profile: MID
* TODO: falls back to Linear
*
* Arguments:
* 0: Seeker Target PosASL <ARRAY>
* 1: Guidance Arg Array <ARRAY>
* 2: Attack Profile State <ARRAY>
*
* Return Value:
* Missile Aim PosASL <ARRAY>
*
* Example:
* [[1,2,3], [], []] call ace_missileguidance_fnc_attackProfile_MID;
*
* Public: No
*/
_this call FUNC(attackProfile_LIN);

View File

@ -17,47 +17,27 @@
* Public: No
*/
params ["_seekerTargetPos", "_args", "_attackProfileStateParams"];
_args params ["_firedEH"];
_args params ["_firedEH", "", "", "", "", "_targetData"];
_firedEH params ["_shooter","","","","","","_projectile"];
_attackProfileStateParams params["_maxCorrectableDistance", "_wireCut", "_randomVector", "_crosshairOffset", "_seekerMaxRangeSqr", "_seekerMinRangeSqr", "_wireCutSource", "_distanceAheadOfMissile"];
_attackProfileStateParams params ["_maxCorrectableDistance", "_wireCut", "_lastInput", "_crosshairOffset", "_seekerMaxRangeSqr", "_seekerMinRangeSqr", "_wireCutSource", "_distanceAheadOfMissile"];
private _projectilePos = getPosASL _projectile;
private _shooterPos = getPosASL _shooter;
private _shooterDir = vectorNormalized(_seekerTargetPos vectorDiff _shooterPos);
private _distanceToProjectile = _shooterPos vectorDistanceSqr _projectilePos;
if ((_distanceToProjectile > _seekerMaxRangeSqr) || { _wireCut }) exitWith {
// wire snap, random direction
if (_randomVector isEqualTo [0, 0, 0]) then {
_randomVector = RANDOM_VECTOR_3D vectorMultiply 300;
if !(_wireCut) then {
_attackProfileStateParams set [1, true];
_attackProfileStateParams set [2, _randomVector];
playSound3D ["a3\sounds_f\air\sfx\SL_rope_break.wss", objNull, false, AGLtoASL (_shooter modelToWorld _wireCutSource), 5, 1, 150];
};
_projectilePos vectorAdd _randomVector
_lastInput
};
if (_seekerTargetPos isEqualTo [0, 0, 0] || { _distanceToProjectile < _seekerMinRangeSqr }) exitWith {
// cut wire if its caught on terrain
/*if (lineIntersectsSurfaces [getPosASL _shooter, _projectilePos, _shooter] isNotEqualTo []) then {
_attackProfileStateParams set [1, true];
};*/
// return position 50m infront of projectile
_projectilePos vectorAdd (_projectile vectorModelToWorld [0, 50, 0])
};
private _final = _seekerTargetPos vectorAdd _crosshairOffset;
_attackProfileStateParams set [2, _final];
private _relativeCorrection = _projectile vectorWorldToModel (_projectilePos vectorDiff _seekerTargetPos);
_relativeCorrection = _relativeCorrection vectorDiff _crosshairOffset;
_targetData set [0, _projectilePos vectorFromTo _final];
private _magnitude = vectorMagnitude [_relativeCorrection select 0, 0, _relativeCorrection select 2];
private _fovImpulse = 1 min (_magnitude / _maxCorrectableDistance); // the simulated impulse for the missile being close to the center of the crosshair
// Adjust the impulse due to near-zero values creating wobbly missiles?
private _correction = _fovImpulse;
_relativeCorrection = (vectorNormalized _relativeCorrection) vectorMultiply _correction;
private _returnPos = _projectilePos vectorDiff (_projectile vectorModelToWorld _relativeCorrection);
_returnPos vectorAdd (_shooterDir vectorMultiply _distanceAheadOfMissile)
_final

View File

@ -16,10 +16,9 @@
* Public: No
*/
params ["_projectile", "_v"];
params ["_projectile", "_pitch", "_yaw", "_roll"];
private _l = sqrt ((_v select 0) ^ 2 + (_v select 1) ^ 2);
private _r = -(_v select 2) / _l;
private _dir = [sin _yaw * cos _pitch, cos _yaw * cos _pitch, sin _pitch];
private _up = [[sin _roll, -sin _pitch, cos _roll * cos _pitch], -_yaw] call BIS_fnc_rotateVector2D;
_projectile setVectorDirAndUp [ _v, [(_v select 0) * _r,(_v select 1) * _r, _l] ];
_projectile setVelocity (_v vectorMultiply (vectorMagnitude (velocity _projectile)));
_projectile setVectorDirAndUp [_dir, _up];

View File

@ -43,4 +43,3 @@ if (!((terrainIntersectASL [_seekerPos, _targetPos]) && {terrainIntersectASL [_s
};
_return

View File

@ -76,7 +76,7 @@ private _nextFireMode = _attackProfiles select _index;
TRACE_4("",_currentFireMode,_nextFireMode,_index,_attackProfiles);
private _currentFireMode = if (_useModeForAttackProfile) then {
if (_useModeForAttackProfile) then {
TRACE_2("setting fire mode",_weaponStateToken,_nextFireMode);
{
_x params ["_xIndex", "", "_xWeapon", "", "_xMode"];

View File

@ -0,0 +1,61 @@
#include "..\script_component.hpp"
/*
* Author: tcvm
* tracks a projectile until it explodes
*
* Arguments:
* None
*
* Return Value:
* None
*
* Public: No
*/
params ["_projectile"];
private _camera = "camera" camCreate getPosATL _projectile;
_camera camPrepareFOV 0.7;
_camera cameraEffect ["internal", "back"];
_camera camCommitPrepared 0;
GVAR(debug_camera_close) = false;
private _displayEH = (findDisplay 46) displayAddEventHandler ["KeyDown", {
params ["_displayorcontrol", "_key", "_shift", "_ctrl", "_alt"];
GVAR(debug_camera_close) = (_key == 1);
true
}];
[{
params ["_args", "_pfh"];
_args params ["_projectile", "_camera", "_projectilePos", "_displayEH"];
if (!alive _projectile || GVAR(debug_camera_close)) exitWith {
private _delay = 1.5;
if (GVAR(debug_camera_close)) then {
_delay = 0;
};
(findDisplay 46) displayRemoveEventHandler ["KeyDown", _displayEH];
[{
params ["_camera"];
_camera cameraEffect ["terminate", "back"];
_camera camCommitPrepared 0;
camDestroy _camera;
}, [_camera], _delay] call CBA_fnc_waitAndExecute;
_camera camPrepareTarget _projectilePos;
_camera camCommitPrepared 0;
[_pfh] call CBA_fnc_removePerFrameHandler;
};
private _currentProjectilePos = getPosATLVisual _projectile;
_camera camPrepareTarget _projectile;
_camera camPrepareRelPos [0, -5, 1];
_camera camCommitPrepared 0;
_args set [2, getPosATL _projectile];
}, 0, [_projectile, _camera, getPosATL _projectile, _displayEH]] call CBA_fnc_addPerFrameHandler;

View File

@ -22,7 +22,6 @@ _args params ["", "_launchParams"];
_launchParams params ["", "", "", "_attackProfileName"];
private _attackProfileFunction = getText (configFile >> QGVAR(AttackProfiles) >> _attackProfileName >> "functionName");
private _attackProfilePos = _this call (missionNamespace getVariable _attackProfileFunction);
if ((isNil "_attackProfilePos") || {_attackProfilePos isEqualTo [0,0,0]}) exitWith {
@ -30,10 +29,10 @@ if ((isNil "_attackProfilePos") || {_attackProfilePos isEqualTo [0,0,0]}) exitWi
[0,0,0]
};
#ifdef DRAW_GUIDANCE_INFO
if (GVAR(debug_drawGuidanceInfo)) then {
drawLine3D [(ASLtoAGL _attackProfilePos), (ASLtoAGL _seekerTargetPos), [0,1,1,1]];
drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [0,0,1,1], ASLtoAGL _attackProfilePos, 0.5, 0.5, 0, _attackProfileName, 1, 0.025, "TahomaB"];
#endif
};
TRACE_2("return",_attackProfilePos,_attackProfileName);
_attackProfilePos;

View File

@ -30,9 +30,9 @@ if ((isNil "_seekerTargetPos") || {_seekerTargetPos isEqualTo [0,0,0]}) then { /
if (_seekLastTargetPos && {_lastKnownPos isNotEqualTo [0,0,0]}) then { // if enabled for the ammo, use last known position if we have one stored
TRACE_2("seeker returned bad pos - using last known",_seekLastTargetPos,_lastKnownPos);
_seekerTargetPos = _lastKnownPos;
#ifdef DRAW_GUIDANCE_INFO
if (GVAR(debug_drawGuidanceInfo)) then {
drawIcon3D ["\A3\ui_f\data\map\markers\military\unknown_CA.paa", [1,1,0,1], ASLtoAGL _lastKnownPos, 0.25, 0.25, 0, "LastKnownPos", 1, 0.02, "TahomaB"];
#endif
};
} else {
TRACE_1("seeker returned no pos",_seekerTargetPos);
_seekerTargetPos = [0,0,0];
@ -44,9 +44,9 @@ if ((isNil "_seekerTargetPos") || {_seekerTargetPos isEqualTo [0,0,0]}) then { /
};
};
#ifdef DRAW_GUIDANCE_INFO
if (GVAR(debug_drawGuidanceInfo)) then {
drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [0,1,0,1], ASLtoAGL _seekerTargetPos, 0.5, 0.5, 0, _seekerTypeName, 1, 0.025, "TahomaB"];
#endif
};
TRACE_2("return",_seekerTargetPos,_seekerTypeName);
_seekerTargetPos;

View File

@ -15,100 +15,215 @@
*
* Public: No
*/
#define TRAIL_COLOUR(multiplier) [1 * multiplier, 1 * multiplier, 0.3 * multiplier, 0.7 * multiplier]
BEGIN_COUNTER(guidancePFH);
#define TIMESTEP_FACTOR 0.01
params ["_args", "_pfID"];
_args params ["_firedEH", "_launchParams", "_flightParams", "_seekerParams", "_stateParams"];
_args params ["_firedEH", "_launchParams", "_flightParams", "_seekerParams", "_stateParams", "_targetData", "_navigationStateParams"];
_firedEH params ["_shooter","","","","_ammo","","_projectile"];
_launchParams params ["","_targetLaunchParams"];
_stateParams params ["_lastRunTime", "_seekerStateParams", "_attackProfileStateParams", "_lastKnownPosState"];
_launchParams params ["","_targetLaunchParams","","","","","_navigationType"];
_stateParams params ["_lastRunTime", "_seekerStateParams", "_attackProfileStateParams", "_lastKnownPosState", "_navigationParameters", "_guidanceParameters"];
_navigationStateParams params ["_currentState", "_navigationStateData"];
_flightParams params ["_pitchRate", "_yawRate", "_isBangBangGuidance", "_stabilityCoefficient", "_showTrail"];
if (!alive _projectile || isNull _projectile || isNull _shooter) exitWith {
[_pfID] call CBA_fnc_removePerFrameHandler;
END_COUNTER(guidancePFH);
};
private _runtimeDelta = diag_tickTime - _lastRunTime;
private _adjustTime = 1;
if (accTime > 0) then {
_adjustTime = 1/accTime;
_adjustTime = _adjustTime * (_runtimeDelta / TIMESTEP_FACTOR);
TRACE_4("Adjust timing",1/accTime,_adjustTime,_runtimeDelta,(_runtimeDelta / TIMESTEP_FACTOR));
} else {
_adjustTime = 0;
if (_showTrail) then {
drop [
"\a3\data_f\kouleSvetlo", "", "Billboard",
100,
0.03,
_projectile modelToWorld [0, 0, 0],
[0, 0, 0],
0,
1.25,
1,
0.05,
[0.5],
[TRAIL_COLOUR(1)],
[0],
0,
0,
"",
"",
"",
0,
false,
-1,
[TRAIL_COLOUR(10000)]
];
};
private _minDeflection = ((_flightParams select 0) - ((_flightParams select 0) * _adjustTime)) max 0;
private _maxDeflection = (_flightParams select 1) * _adjustTime;
// private _incDeflection = _flightParams select 2; // todo
private _projectilePos = getPosASL _projectile;
private _timestep = diag_deltaTime * accTime;
// Run seeker function:
private _seekerTargetPos = [[0,0,0], _args, _seekerStateParams, _lastKnownPosState] call FUNC(doSeekerSearch);
private _seekerTargetPos = [[0,0,0], _args, _seekerStateParams, _lastKnownPosState, _timestep] call FUNC(doSeekerSearch);
// Run attack profile function:
private _profileAdjustedTargetPos = [_seekerTargetPos, _args, _attackProfileStateParams] call FUNC(doAttackProfile);
_seekerTargetPos = AGLtoASL ASLToAGL _seekerTargetPos;
private _profileAdjustedTargetPos = [_seekerTargetPos, _args, _attackProfileStateParams, _timestep] call FUNC(doAttackProfile);
private _projectilePos = getPosASLVisual _projectile;
_targetData set [1, _projectilePos vectorFromTo _profileAdjustedTargetPos];
// If we have no seeker target, then do not change anything
// If there is no deflection on the missile, this cannot change and therefore is redundant. Avoid calculations for missiles without any deflection
if ((_minDeflection != 0 || {_maxDeflection != 0}) && {_profileAdjustedTargetPos isNotEqualTo [0,0,0]}) then {
if ((_pitchRate != 0 || {_yawRate != 0})) then {
private _navigationFunction = getText (configFile >> QGVAR(NavigationTypes) >> _navigationType >> "functionName");
if (_navigationStateData isNotEqualTo []) then {
(_navigationStateData select _currentState) params ["_transitionCondition"];
private _transition = (_args call (missionNamespace getVariable [_transitionCondition, { false }]));
if (_transition) then {
_currentState = _currentState + 1;
_navigationStateParams set [0, _currentState];
};
private _targetVector = _projectilePos vectorFromTo _profileAdjustedTargetPos;
private _adjustVector = _targetVector vectorDiff (vectorDir _projectile);
_adjustVector params ["_adjustVectorX", "_adjustVectorY", "_adjustVectorZ"];
_navigationType = (_navigationStateData select _currentState) select 1;
_navigationFunction = getText (configFile >> QGVAR(NavigationTypes) >> _navigationType >> "functionName");
private _yaw = 0;
private _pitch = 0;
private _roll = 0;
_navigationParameters = (_navigationStateData select _currentState) select 2;
_stateParams set [4, _navigationParameters];
};
private _commandedAcceleration = [_args, _timestep, _seekerTargetPos, _profileAdjustedTargetPos, _targetData, _navigationParameters] call (missionNamespace getVariable _navigationFunction);
if (_adjustVectorX < 0) then {
_yaw = - ( (_minDeflection max ((abs _adjustVectorX) min _maxDeflection) ) );
if (isNil "_commandedAcceleration") exitWith {
systemChat format ["Error in %1 Missile Type %2 Seeker Pos %3", _navigationFunction, typeOf _projectile, _seekerTargetPos];
ERROR_MSG_3("_commandedAcceleration is nil! Guidance cancelled [%1 %2 %3]",_navigationFunction,typeOf _projectile,_seekerTargetPos);
};
if (GVAR(debug_drawGuidanceInfo)) then {
private _projectilePosAGL = ASLToAGL _projectilePos;
private _cmdAccelLocal = _projectile vectorWorldToModelVisual _commandedAcceleration;
drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [1,0,0,1], _projectilePosAGL vectorAdd [0, 0, 1], 0.75, 0.75, 0, format ["cmdPitch: %1 cmdYaw %2", _cmdAccelLocal#2, _cmdAccelLocal#0], 1, 0.025, "TahomaB"];
drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [1,1,0,1], _projectilePosAGL vectorAdd [0, 0, 2], 0.75, 0.75, 0, _navigationType, 1, 0.025, "TahomaB"];
drawLine3D [_projectilePosAGL, _projectilePosAGL vectorAdd _commandedAcceleration, [1, 0, 1, 1]];
};
// activate missile servos and change direction
if (!isGamePaused && accTime > 0) then {
_guidanceParameters params ["_yaw", "_roll", "_pitch"];
_commandedAcceleration = _projectile vectorWorldToModelVisual _commandedAcceleration;
_commandedAcceleration params ["_yawChange", "", "_pitchChange"];
if (isNil "_yawChange") then {
_yawChange = 0;
};
if (isNil "_pitchChange") then {
_pitchChange = 0;
};
private _clampedPitch = (_pitchChange min _pitchRate) max -_pitchRate;
private _clampedYaw = (_yawChange min _yawRate) max -_yawRate;
// controls are either on or off, no proportional
if (_isBangBangGuidance) then {
private _pitchSign = if (_clampedPitch == 0) then {
0
} else {
if (_adjustVectorX > 0) then {
_yaw = ( (_minDeflection max (_adjustVectorX min _maxDeflection) ) );
_clampedPitch / abs _clampedPitch
};
};
if (_adjustVectorY < 0) then {
_roll = - ( (_minDeflection max ((abs _adjustVectorY) min _maxDeflection) ) );
private _yawSign = if (_clampedYaw == 0) then {
0
} else {
if (_adjustVectorY > 0) then {
_roll = ( (_minDeflection max (_adjustVectorY min _maxDeflection) ) );
};
};
if (_adjustVectorZ < 0) then {
_pitch = - ( (_minDeflection max ((abs _adjustVectorZ) min _maxDeflection) ) );
} else {
if (_adjustVectorZ > 0) then {
_pitch = ( (_minDeflection max (_adjustVectorZ min _maxDeflection) ) );
};
};
private _finalAdjustVector = [_yaw, _roll, _pitch];
TRACE_3("",_pitch,_yaw,_roll);
TRACE_3("",_targetVector,_adjustVector,_finalAdjustVector);
if (accTime > 0) then {
private _changeVector = (vectorDir _projectile) vectorAdd _finalAdjustVector;
TRACE_2("",_projectile,_changeVector);
[_projectile, _changeVector] call FUNC(changeMissileDirection);
_clampedYaw / abs _clampedYaw
};
_clampedPitch = _pitchSign * _pitchRate;
_clampedYaw = _yawSign * _yawRate;
};
#ifdef DRAW_GUIDANCE_INFO
TRACE_9("pitch/yaw/roll",_pitch,_yaw,_roll,_yawChange,_pitchChange,_pitchRate,_yawRate,_clampedPitch,_clampedYaw);
// directional stability
private _localVelocity = _projectile vectorWorldToModelVisual (velocity _projectile);
private _velocityAngleYaw = (_localVelocity#0) atan2 (_localVelocity#1);
private _velocityAnglePitch = (_localVelocity#2) atan2 (_localVelocity#1);
// bastardized version of direction stability https://en.wikipedia.org/wiki/Directional_stability#Steering_forces
private _forceYaw = _stabilityCoefficient * _velocityAngleYaw + _clampedYaw;
private _forcePitch = _stabilityCoefficient * _velocityAnglePitch + _clampedPitch;
_pitch = _pitch + _forcePitch * _timestep;
_yaw = _yaw + _forceYaw * _timestep;
TRACE_3("new pitch/yaw/roll",_pitch,_yaw,_roll);
private _multiplyQuat = {
params ["_qLHS", "_qRHS"];
_qLHS params ["_lhsX", "_lhsY", "_lhsZ", "_lhsW"];
_qRHS params ["_rhsX", "_rhsY", "_rhsZ", "_rhsW"];
private _lhsImaginary = [_lhsX, _lhsY, _lhsZ];
private _rhsImaginary = [_rhsX, _rhsY, _rhsZ];
private _scalar = _lhsW * _rhsW - (_lhsImaginary vectorDotProduct _rhsImaginary);
private _imginary = (_rhsImaginary vectorMultiply _lhsW) vectorAdd (_lhsImaginary vectorMultiply _rhsW) vectorAdd (_lhsImaginary vectorCrossProduct _rhsImaginary);
_imginary + [_scalar]
};
private _multiplyVector = {
params ["_quaternion", "_vector"];
private _real = _quaternion#3;
private _imaginary = [
_quaternion#0,
_quaternion#1,
_quaternion#2
];
private _vectorReturn = _vector vectorAdd ((
_imaginary vectorCrossProduct (
(_imaginary vectorCrossProduct _vector) vectorAdd (
_vector vectorMultiply _real
)
)
) vectorMultiply 2);
_vectorReturn
};
private _quaternion = [0, 0, 0, 1];
private _temp = [0, 0, sin (-_yaw / 2), cos (-_yaw / 2)];
_quaternion = [_quaternion, _temp] call _multiplyQuat;
_temp = [sin (_pitch / 2), 0, 0, cos (_pitch / 2)];
_quaternion = [_quaternion, _temp] call _multiplyQuat;
private _dir = [_quaternion, [0, 1, 0]] call _multiplyVector;
private _up = [_quaternion, [0, 0, 1]] call _multiplyVector;
_projectile setVectorDirAndUp [_dir, _up];
//[_projectile, _pitch, _yaw, 0] call FUNC(changeMissileDirection);
_guidanceParameters set [0, _yaw];
_guidanceParameters set [2, _pitch];
_stateParams set [5, _guidanceParameters];
};
_stateParams set [4, _navigationParameters];
_args set [4, _stateParams];
};
if (GVAR(debug_drawGuidanceInfo)) then {
TRACE_3("",_projectilePos,_seekerTargetPos,_profileAdjustedTargetPos);
drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [1,0,0,1], ASLtoAGL _projectilePos, 0.75, 0.75, 0, _ammo, 1, 0.025, "TahomaB"];
if (!isGamePaused && accTime > 0) then {
private _ps = "#particlesource" createVehicleLocal (ASLtoAGL _projectilePos);
_PS setParticleParams [["\A3\Data_f\cl_basic", 8, 3, 1], "", "Billboard", 1, 3.0141, [0, 0, 2], [0, 0, 0], 1, 1.275, 1, 0, [1, 1], [[1, 0, 0, 1], [1, 0, 0, 1], [1, 0, 0, 1]], [1], 1, 0, "", "", nil];
_PS setDropInterval 3.0;
#endif
_PS setParticleParams [["\A3\Data_f\cl_basic", 8, 3, 1], "", "Billboard", 1, 3.0141, [0, 0, 0], [0, 0, 0], 1, 1.275, 1, 0, [1, 1], [[1, 0, 0, 1], [1, 0, 0, 1], [1, 0, 0, 1]], [1], 1, 0, "", "", nil];
_PS setDropInterval 1.0;
};
drawLine3D [ASLtoAGL _projectilePos, (ASLtoAGL _projectilePos) vectorAdd velocity _projectile, [1, 1, 1, 1]];
};
_stateParams set [0, diag_tickTime];
END_COUNTER(guidancePFH);

View File

@ -0,0 +1,35 @@
#include "..\script_component.hpp"
/*
* Author: tcvm
* Sets up line state arrays (called from missileGuidance's onFired).
*
* Arguments:
* Guidance Arg Array <ARRAY>
*
* Return Value:
* None
*
* Example:
* [] call ace_missileguidance_fnc_line_onFired
*
* Public: No
*/
params ["_firedEH", "", "", "", "_stateParams"];
_firedEH params ["","","","","","","_projectile"];
private _ammoConfig = configOf _projectile;
private _p = getNumber (_ammoConfig >> QUOTE(ADDON) >> "lineGainP");
private _d = getNumber (_ammoConfig >> QUOTE(ADDON) >> "lineGainD");
private _correctionDistance = getNumber (_ammoConfig >> QUOTE(ADDON) >> "correctionDistance");
if (_correctionDistance == 0) then {
_correctionDistance = 1;
};
private _navigationParams = [
_p, 0, _d,
0,
0,
_correctionDistance
];
_navigationParams

View File

@ -10,7 +10,7 @@
* None
*
* Example:
* [] call ace_missileguidance_fnc_ahr_onFired
* [] call ace_missileguidance_fnc_mwr_onFired
*
* Public: No
*/
@ -38,6 +38,8 @@ private _activeRadarDistance = [_config >> "activeRadarEngageDistance", "NUMBER"
private _projectileThrust = [_projectileConfig >> "thrust", "NUMBER", 0] call CBA_fnc_getConfigEntry;
private _projectileThrustTime = [_projectileConfig >> "thrustTime", "NUMBER", 0] call CBA_fnc_getConfigEntry;
private _lockTypes = [_config >> "lockableTypes", "ARRAY", ["Air", "LandVehicle", "Ship"]] call CBA_fnc_getConfigEntry;
private _velocityAtImpact = _projectileThrust * _projectileThrustTime;
private _timeToActive = 0;
if (!isNil "_target" && _velocityAtImpact > 0) then {
@ -70,4 +72,4 @@ _seekerStateParams set [6, false];
_seekerStateParams set [7, [0, 0, 0]];
_seekerStateParams set [8, CBA_missionTime];
_seekerStateParams set [9, isNull _target];
_seekerStateParams set [10, _lockTypes];

View File

@ -0,0 +1,50 @@
#include "..\script_component.hpp"
/*
* Author: tcvm
* Determine path for projectile to take in accordance to proportional navigation, takes target acceleration into account
*
* Arguments:
* Guidance Arg Array <ARRAY>
*
* Return Value:
* Commanded acceleration normal to LOS in world space <ARRAY>
*
* Example:
* [] call ace_missileguidance_fnc_navigationType_augmentedProNav
*
* Public: No
*/
params ["_args", "_timestep", "_seekerTargetPos", "_profileAdjustedTargetPos"];
_args params ["_firedEH", "", "", "", "_stateParams", "_targetData"];
_firedEH params ["","","","","","","_projectile"];
_stateParams params ["", "", "", "","_navigationParams"];
_navigationParams params ["_lastMissileFrame", "_navigationGain"];
_lastMissileFrame params ["_lastLineOfSight"];
_targetData params ["_targetDirection", "_attackProfileDirection", "", "_targetVelocity", "_targetAcceleration"];
// Proportional navigation implemented via "Fundamentals of proportional navigation" by Stephen Murtaugh and Harry Criel
private _closingVelocity = _targetVelocity vectorDiff velocity _projectile;
private _targetAccelerationProjected = _attackProfileDirection vectorMultiply (_targetAcceleration vectorDotProduct _attackProfileDirection);
_targetAcceleration = _targetAcceleration vectorDiff _targetAccelerationProjected;
private _losDelta = (vectorNormalized _attackProfileDirection) vectorDiff (vectorNormalized _lastLineOfSight);
private _losRate = if (_timestep == 0) then {
0
} else {
1 * (vectorMagnitude _losDelta) / _timestep;
};
private _lateralAcceleration = _navigationGain * _losRate;
private _commandedAcceleration = _closingVelocity vectorMultiply _lateralAcceleration;
_commandedAcceleration = _commandedAcceleration vectorAdd (_losDelta vectorMultiply (0.5 * _navigationGain * vectorMagnitude _targetAcceleration));
// we need acceleration normal to our LOS
private _commandedAccelerationProjected = _attackProfileDirection vectorMultiply (_commandedAcceleration vectorDotProduct _attackProfileDirection);
_commandedAcceleration = _commandedAcceleration vectorDiff _commandedAccelerationProjected;
if (accTime > 0) then {
_navigationParams set [0, [_seekerTargetPos, _targetVelocity, _attackProfileDirection]];
};
_commandedAcceleration vectorMultiply _timestep

View File

@ -0,0 +1,21 @@
#include "..\script_component.hpp"
/*
* Author: tcvm
* Points directly toward attack profile positon
*
* Arguments:
* Guidance Arg Array <ARRAY>
*
* Return Value:
* Commanded acceleration normal to LOS in world space <ARRAY>
*
* Example:
* [] call ace_missileguidance_fnc_navigationType_direct
*
* Public: No
*/
params ["_args", "", "", "_profileAdjustedTargetPos"];
_args params ["_firedEH"];
_firedEH params ["","","","","","","_projectile"];
_profileAdjustedTargetPos vectorDiff getPosASLVisual _projectile

View File

@ -0,0 +1,52 @@
#include "..\script_component.hpp"
/*
* Author: tcvm
* Assumes targetDir is pointing toward line we want to stay on
*
* Arguments:
* Guidance Arg Array <ARRAY>
*
* Return Value:
* Commanded acceleration normal to LOS in world space <ARRAY>
*
* Example:
* [] call ace_missileguidance_fnc_navigationType_line
*
* Public: No
*/
params ["_args", "_timestep", "_seekerTargetPos", "_profileAdjustedTargetPos", "_targetData", "_navigationParams"];
_args params ["", "", "_flightParams"];
_targetData params ["", "_targetDir", "_distance"];
_flightParams params ["_pitchRate", "_yawRate"];
_navigationParams params ["_proportionalGain", "", "_derivativeGain", "_lastErrorX", "_lastErrorY", "_correctionDistance"];
private _relativeTargetDirection = [0, (velocityModelSpace _projectile) select 1, 0] vectorAdd (_projectile vectorWorldToModelVisual (_targetDir vectorMultiply _distance));
private _angleX = ((_relativeTargetDirection select 0) atan2 (_relativeTargetDirection select 1));
private _angleY = ((_relativeTargetDirection select 2) atan2 (_relativeTargetDirection select 1));
private _pX = _proportionalGain * _angleX;
private _dX = 0;
if (_timestep > 0) then {
_dX = _derivativeGain * (_angleX - _lastErrorX) / _timestep;
};
private _pY = _proportionalGain * _angleY;
private _dY = 0;
if (_timestep > 0) then {
_dY = _derivativeGain * (_angleY - _lastErrorY) / _timestep;
};
private _accelerationX = _pX + _dX;
private _accelerationY = _pY + _dY;
private _commandedAcceleration = [
_accelerationX,
0,
_accelerationY
];
_navigationParams set [3, _angleX];
_navigationParams set [4, _angleY];
_projectile vectorModelToWorldVisual _commandedAcceleration;

View File

@ -0,0 +1,48 @@
#include "..\script_component.hpp"
/*
* Author: tcvm
* Accelerates toward LOS
*
* Arguments:
* Guidance Arg Array <ARRAY>
*
* Return Value:
* Commanded acceleration normal to LOS in world space <ARRAY>
*
* Example:
* [] call ace_missileguidance_fnc_navigationType_lineOfSight
*
* Public: No
*/
params ["_args", "_timestep", "_seekerTargetPos", "_profileAdjustedTargetPos"];
_args params ["_firedEH", "", "", "", "_stateParams", "_targetData"];
_firedEH params ["","","","","","","_projectile"];
_stateParams params ["", "", "", "","_navigationParams"];
_navigationParams params ["_onLaunch"];
_onLaunch params ["_lastLineOfSight"];
_targetData params ["_targetDirection", "_attackProfileDirection", "", "_targetVelocity", ""];
// Semi-proportional navigation implemented via "Fundamentals of proportional navigation" by Stephen Murtaugh and Harry Criel
// the los rate is tiny, so we multiply by a constant of a power of ten to get more aggressive acceleration
// this is just due to how we measure our LOS delta, the vectors involved are _tiny_
private _losDelta = _attackProfileDirection vectorDiff _lastLineOfSight;
private _losRate = if (_timestep == 0) then {
0
} else {
10 * (vectorMagnitude _losDelta) / _timestep;
};
private _closingVelocity = _targetVelocity vectorDiff (velocity _projectile);
private _commandedAcceleration = _closingVelocity vectorMultiply _losRate;
// we need acceleration normal to our LOS
private _commandedAccelerationProjected = _attackProfileDirection vectorMultiply (_commandedAcceleration vectorDotProduct _attackProfileDirection);
_commandedAcceleration = _commandedAcceleration vectorDiff _commandedAccelerationProjected;
if (accTime > 0) then {
_navigationParams set [0, [_attackProfileDirection]];
};
_targetDirection

View File

@ -0,0 +1,46 @@
#include "..\script_component.hpp"
/*
* Author: tcvm
* Determine path for projectile to take in accordance to proportional navigation
*
* Arguments:
* Guidance Arg Array <ARRAY>
*
* Return Value:
* Commanded acceleration normal to LOS in world space <ARRAY>
*
* Example:
* [] call ace_missileguidance_fnc_navigationType_proNav
*
* Public: No
*/
params ["_args", "_timestep", "_seekerTargetPos", "_profileAdjustedTargetPos"];
_args params ["_firedEH", "", "", "", "_stateParams", "_targetData"];
_firedEH params ["","","","","","","_projectile"];
_stateParams params ["", "", "", "","_navigationParams"];
_navigationParams params ["_lastMissileFrame", "_navigationGain"];
_lastMissileFrame params ["_lastLineOfSight"];
_targetData params ["_targetDirection", "_attackProfileDirection", "", "_targetVelocity", ""];
// Proportional navigation implemented via "Fundamentals of proportional navigation" by Stephen Murtaugh and Harry Criel
private _closingVelocity = _targetVelocity vectorDiff velocity _projectile;
private _losDelta = (vectorNormalized _attackProfileDirection) vectorDiff (vectorNormalized _lastLineOfSight);
private _losRate = if (_timestep == 0) then {
0
} else {
1 * (vectorMagnitude _losDelta) / _timestep;
};
private _lateralAcceleration = _navigationGain * _losRate;
private _commandedAcceleration = _closingVelocity vectorMultiply _lateralAcceleration;
// we need acceleration normal to our LOS
private _commandedAccelerationProjected = _attackProfileDirection vectorMultiply (_commandedAcceleration vectorDotProduct _attackProfileDirection);
_commandedAcceleration = _commandedAcceleration vectorDiff _commandedAccelerationProjected;
if (accTime > 0) then {
_navigationParams set [0, [_seekerTargetPos, _targetVelocity, _attackProfileDirection]];
};
_commandedAcceleration vectorMultiply _timestep

View File

@ -0,0 +1,38 @@
#include "..\script_component.hpp"
/*
* Author: tcvm
* Determine path for projectile to take in accordance to zero-effort miss pro-nav, takes target acceleration into account. Super deadly
*
* Arguments:
* Guidance Arg Array <ARRAY>
*
* Return Value:
* Commanded acceleration normal to LOS in world space <ARRAY>
*
* Example:
* [] call ace_missileguidance_fnc_navigationType_zeroEffortMiss
*
* Public: No
*/
params ["_args", "_timestep", "_seekerTargetPos", "_profileAdjustedTargetPos"];
_args params ["_firedEH", "", "", "", "_stateParams", "_targetData"];
_firedEH params ["","","","","","","_projectile"];
_stateParams params ["", "", "", "","_navigationParams"];
_navigationParams params ["", "_navigationGain"];
_targetData params ["_targetDirection", "_attackProfileDirection", "_targetRange", "_targetVelocity", "_targetAcceleration"];
private _vectorToTarget = _attackProfileDirection vectorMultiply _targetRange;
private _closingVelocity = _targetVelocity vectorDiff velocity _projectile;
private _timeToGo = _targetRange / vectorMagnitude _closingVelocity;
if (_timeToGo == 0) then {
_timeToGo = 0.001;
};
private _zeroEffortMiss = _vectorToTarget vectorAdd (_closingVelocity vectorMultiply _timeToGo);
private _zeroEffortMissProjectiled = _attackProfileDirection vectorMultiply (_zeroEffortMiss vectorDotProduct _attackProfileDirection);
private _zeroEffortMissNormal = _zeroEffortMiss vectorDiff _zeroEffortMissProjectiled;
private _commandedAcceleration = _zeroEffortMissNormal vectorMultiply (_navigationGain / (_timeToGo * _timeToGo));
_commandedAcceleration

View File

@ -19,9 +19,6 @@
params ["_shooter","_weapon","","_mode","_ammo","","_projectile"];
// Bail on not missile
if !(_ammo isKindOf "MissileBase") exitWith {};
// Bail if guidance is disabled for this ammo
if ((getNumber (configFile >> "CfgAmmo" >> _ammo >> QUOTE(ADDON) >> "enabled")) != 1) exitWith {};
@ -44,6 +41,7 @@ private _target = _shooter getVariable [QGVAR(target), nil];
private _targetPos = _shooter getVariable [QGVAR(targetPosition), nil];
private _seekerType = _shooter getVariable [QGVAR(seekerType), nil];
private _attackProfile = _shooter getVariable [QGVAR(attackProfile), nil];
private _navigationType = _shooter getVariable [QGVAR(navigationType), nil];
if ((getNumber (configFile >> "CfgAmmo" >> _ammo >> QUOTE(ADDON) >> "useModeForAttackProfile")) == 1) then {
_attackProfile = getText (configFile >> "CfgWeapons" >> _weapon >> _mode >> QGVAR(attackProfile))
};
@ -52,7 +50,7 @@ private _lockMode = _shooter getVariable [QGVAR(lockMode), nil];
private _laserCode = _shooter getVariable [QEGVAR(laser,code), ACE_DEFAULT_LASER_CODE];
private _laserInfo = [_laserCode, ACE_DEFAULT_LASER_WAVELENGTH, ACE_DEFAULT_LASER_WAVELENGTH];
TRACE_6("getVars",_target,_targetPos,_seekerType,_attackProfile,_lockMode,_laserCode);
TRACE_7("getVars",_target,_targetPos,_seekerType,_attackProfile,_lockMode,_laserCode,_navigationType);
private _launchPos = getPosASL (vehicle _shooter);
@ -65,6 +63,14 @@ if (isNil "_attackProfile" || {!(_attackProfile in (getArray (_config >> "attack
if (isNil "_lockMode" || {!(_lockMode in (getArray (_config >> "seekerLockModes")))}) then {
_lockMode = getText (_config >> "defaultSeekerLockMode");
};
if (isNil "_navigationType" || {!(_navigationType in (getArray (_config >> "navigationTypes")))}) then {
_navigationType = getText (_config >> "defaultNavigationType");
};
if (isNil "_navigationType" || _navigationType isEqualTo "") then {
// most missiles use ProNav by default
_navigationType = "ProportionalNavigation";
};
// If we didn't get a target, try to fall back on tab locking
if (isNil "_target") then {
@ -76,7 +82,7 @@ if (isNil "_target") then {
private _canUseLock = getNumber (_config >> "canVanillaLock");
// @TODO: Get vanilla target
if (_canUseLock > 0 || difficulty < 1) then {
private _vanillaTarget = cursorTarget;
private _vanillaTarget = missileTarget _projectile;
TRACE_1("Using Vanilla Locking",_vanillaTarget);
if (!isNil "_vanillaTarget") then {
@ -85,6 +91,7 @@ if (isNil "_target") then {
};
};
};
_targetPos = getPosASLVisual _target;
// Array for seek last target position
private _seekLastTargetPos = (getNumber ( _config >> "seekLastTargetPos")) == 1;
@ -95,19 +102,66 @@ if (_seekLastTargetPos && {!isNil "_target"}) then {
_lastKnownPosState set [1, [0,0,0]];
};
TRACE_4("Beginning ACE guidance system",_target,_ammo,_seekerType,_attackProfile);
private _navigationParameters = [
// set up in navigation type onFired function
];
// default config values to make sure there is backwards compat
private _pitchRate = 30;
private _yawRate = 30;
private _bangBang = false;
if (isNumber (_config >> "pitchRate")) then {
_pitchRate = getNumber ( _config >> "pitchRate" );
_yawRate = getNumber ( _config >> "yawRate" );
_bangBang = (1 == getNumber (_config >> "bangBangGuidance"));
};
// How much this projectile likes to stay toward current velocity
private _stabilityCoefficient = getNumber (_config >> "stabilityCoefficient");
// show a light trail in flight
private _showTrail = (1 == getNumber (_config >> "showTrail"));
private _navigationStateSubclass = _config >> "navigationStates";
private _states = getArray (_navigationStateSubclass >> "states");
private _navigationStateData = [];
private _initialState = "";
if (_states isNotEqualTo []) then {
_initialState = _states select 0;
{
private _stateClass = _navigationStateSubclass >> _x;
_navigationStateData pushBack [
getText (_stateClass >> "transitionCondition"),
getText (_stateClass >> "navigationType"),
[]
];
} forEach _states;
};
private _initialRoll = getNumber (_config >> "initialRoll");
private _initialYaw = getNumber (_config >> "initialYaw");
private _initialPitch = getNumber (_config >> "initialPitch");
private _yawRollPitch = (vectorDir _projectile) call CBA_fnc_vect2Polar;
TRACE_5("Beginning ACE guidance system",_target,_ammo,_seekerType,_attackProfile,_navigationType);
private _args = [_this,
[ _shooter,
[_target, _targetPos, _launchPos],
[_target, _targetPos, _launchPos, vectorDirVisual vehicle _shooter, CBA_missionTime],
_seekerType,
_attackProfile,
_lockMode,
_laserInfo
_laserInfo,
_navigationType
],
[
getNumber ( _config >> "minDeflection" ),
getNumber ( _config >> "maxDeflection" ),
getNumber ( _config >> "incDeflection" )
_pitchRate,
_yawRate,
_bangBang,
_stabilityCoefficient,
_showTrail
],
[
getNumber ( _config >> "seekerAngle" ),
@ -115,38 +169,71 @@ private _args = [_this,
getNumber ( _config >> "seekerMaxRange" ),
getNumber ( _config >> "seekerMinRange" )
],
[ diag_tickTime, [], [], _lastKnownPosState]
[ diag_tickTime, [], [], _lastKnownPosState, _navigationParameters, [_initialYaw + (_yawRollPitch select 1), _initialRoll, _initialPitch + (_yawRollPitch select 2)]],
[
// target data from missile. Must be filled by seeker for navigation to work
[0, 0, 0], // direction to target
[0, 0, 0], // direction to attack profile
0, // range to target
[0, 0, 0], // target velocity
[0, 0, 0] // target acceleration
],
[0, _navigationStateData]
];
private _onFiredFunc = getText (configFile >> QGVAR(SeekerTypes) >> _seekerType >> "onFired");
TRACE_1("",_onFiredFunc);
TRACE_1("seeker on fired",_onFiredFunc);
if (_onFiredFunc != "") then {
_args call (missionNamespace getVariable _onFiredFunc);
};
_onFiredFunc = getText (configFile >> QGVAR(AttackProfiles) >> _attackProfile >> "onFired");
TRACE_1("",_onFiredFunc);
TRACE_1("attack on fired",_onFiredFunc);
if (_onFiredFunc != "") then {
_args call (missionNamespace getVariable _onFiredFunc);
};
if (_states isEqualTo []) then {
_onFiredFunc = getText (configFile >> QGVAR(NavigationTypes) >> _navigationType >> "onFired");
TRACE_1("navigation on fired",_onFiredFunc);
if (_onFiredFunc != "") then {
private _navState = (_args call (missionNamespace getVariable _onFiredFunc));
(_args select 4) set [4, _navState];
};
} else {
{
_onFiredFunc = getText (configFile >> QGVAR(NavigationTypes) >> _x >> "onFired");
TRACE_1("navigation on fired",_onFiredFunc);
if (_onFiredFunc != "") then {
private _navState = (_args call (missionNamespace getVariable _onFiredFunc));
(_navigationStateData select _forEachIndex) set [2, _navState];
};
} forEach getArray (_config >> "navigationTypes");
};
// Run the "onFired" function passing the full guidance args array
_onFiredFunc = getText (_config >> "onFired");
TRACE_1("",_onFiredFunc);
TRACE_1("general on fired",_onFiredFunc);
if (_onFiredFunc != "") then {
_args call (missionNamespace getVariable _onFiredFunc);
};
// Reverse:
// _args params ["_firedEH", "_launchParams", "_flightParams", "_seekerParams", "_stateParams"];
// _args params ["_firedEH", "_launchParams", "_flightParams", "_seekerParams", "_stateParams", "_targetData", "_navigationStateData"];
// _firedEH params ["_shooter","","","","_ammo","","_projectile"];
// _launchParams params ["_shooter","_targetLaunchParams","_seekerType","_attackProfile","_lockMode","_laserInfo"];
// _targetLaunchParams params ["_target", "_targetPos", "_launchPos"];
// _stateParams params ["_lastRunTime", "_seekerStateParams", "_attackProfileStateParams", "_lastKnownPosState"];
// _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", "_navigationParams", "_guidanceParameters"];
// _seekerParams params ["_seekerAngle", "_seekerAccuracy", "_seekerMaxRange", "_seekerMinRange"];
// _targetData params ["_targetDirection", "_attackProfileDirection", "_targetRange", "_targetVelocity", "_targetAcceleration"];
[LINKFUNC(guidancePFH),0, _args ] call CBA_fnc_addPerFrameHandler;
if (GVAR(debug_enableMissileCamera)) then {
[_projectile] call FUNC(dev_ProjectileCamera);
};
/* Clears locking settings
(vehicle _shooter) setVariable [QGVAR(target), nil];

View File

@ -0,0 +1,36 @@
#include "..\script_component.hpp"
/*
* Author: tcvm
* Sets up proportional navigation state arrays (called from missileGuidance's onFired).
*
* Arguments:
* Guidance Arg Array <ARRAY>
*
* Return Value:
* None
*
* Example:
* [] call ace_missileguidance_fnc_proNav_onFired
*
* Public: No
*/
params ["_firedEH", "", "", "", "_stateParams"];
_firedEH params ["_shooter","","","","_ammo","","_projectile"];
_launchParams params ["_shooter","_targetLaunchParams","_seekerType","_attackProfile","_lockMode","_laserInfo","_navigationType"];
_targetLaunchParams params ["_target", "_targetPos", "_launchPos"];
_stateParams params ["_lastRunTime", "_seekerStateParams", "_attackProfileStateParams", "_lastKnownPosState"];
_seekerParams params ["_seekerAngle", "_seekerAccuracy", "_seekerMaxRange", "_seekerMinRange"];
private _ammoConfig = configOf _projectile;
private _navigationGain = getNumber (_ammoConfig >> QUOTE(ADDON) >> "navigationGain");
if (_navigationGain == 0) then {
_navigationGain = 3;
};
private _navigationParams = [
[ // Last Missile Frame
[0, 0, 0] // Last line of sight
],
_navigationGain // navigation gain of missile. Set in the navigation onFired function
];
_navigationParams

View File

@ -11,16 +11,16 @@
* Seeker Pos <ARRAY>
*
* Example:
* [] call call ace_missileguidance_fnc_seekerType_ARH;
* [] call call ace_missileguidance_fnc_seekerType_MWR;
*
* Public: No
*/
params ["", "_args", "_seekerStateParams"];
_args params ["_firedEH", "_launchParams", "", "_seekerParams", "_stateParams"];
params ["", "_args", "_seekerStateParams", "", "_timestep"];
_args params ["_firedEH", "_launchParams", "", "_seekerParams", "_stateParams", "_targetData"];
_firedEH params ["_shooter","","","","","","_projectile"];
_launchParams params ["_target","","","",""];
_seekerParams params ["_seekerAngle", "", "_seekerMaxRange"];
_seekerStateParams params ["_isActive", "_activeRadarEngageDistance", "_timeWhenActive", "_expectedTargetPos", "_lastTargetPollTime", "_shooterHasRadar", "_wasActive", "_lastKnownVelocity", "_lastTimeSeen", "_doesntHaveTarget"];
_seekerStateParams params ["_isActive", "_activeRadarEngageDistance", "_timeWhenActive", "_expectedTargetPos", "_lastTargetPollTime", "_shooterHasRadar", "_wasActive", "_lastKnownVelocity", "_lastTimeSeen", "_doesntHaveTarget", "_lockTypes"];
if (_isActive || { CBA_missionTime >= _timeWhenActive }) then {
if !(_isActive) then {
@ -60,8 +60,7 @@ if (_isActive || { CBA_missionTime >= _timeWhenActive }) then {
_seekerBaseRadiusAdjusted = _seekerBaseRadiusAtGround;
};
// Look in front of seeker for any targets
private _nearestObjects = nearestObjects [ASLtoAGL _searchPos, ["Air", "LandVehicle", "Ship"], _seekerBaseRadiusAdjusted, false];
private _nearestObjects = nearestObjects [ASLtoAGL _searchPos, _lockTypes, _seekerBaseRadiusAdjusted, false];
_nearestObjects = _nearestObjects apply {
// I check both Line of Sight versions to make sure that a single bush doesnt make the target lock dissapear but at the same time ensure that this can see through smoke. Should work 80% of the time
if ([_projectile, getPosASL _x, _seekerAngle] call FUNC(checkSeekerAngle) && { ([_projectile, _x, true] call FUNC(checkLOS)) || { ([_projectile, _x, false] call FUNC(checkLOS)) } }) then {
@ -74,6 +73,7 @@ if (_isActive || { CBA_missionTime >= _timeWhenActive }) then {
// Select closest object to the expected position to be the current radar target
if (_nearestObjects isEqualTo []) exitWith {
_projectile setMissileTarget objNull;
_seekerStateParams set [3, _searchPos];
_searchPos
};
private _closestDistance = _seekerBaseRadiusAtGround;
@ -89,9 +89,9 @@ if (_isActive || { CBA_missionTime >= _timeWhenActive }) then {
_projectile setMissileTarget _target;
} else {
#ifdef DRAW_GUIDANCE_INFO
_seekerTypeName = "AHR - EXT";
#endif
if (GVAR(debug_drawGuidanceInfo)) then {
_seekerTypeName = "MWR - EXT";
};
// External radar homing
// if the target is in the remote targets for the side, whoever the donor is will "datalink" the target for the hellfire.
private _remoteTargets = listRemoteTargets side _shooter;
@ -104,16 +104,30 @@ if (_isActive || { CBA_missionTime >= _timeWhenActive }) then {
};
};
if (GVAR(debug_drawGuidanceInfo)) then {
drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [1,0,0,1], ASLtoAGL _expectedTargetPos, 0.75, 0.75, 0, "expected target pos", 1, 0.025, "TahomaB"];
};
if !(isNull _target) then {
private _centerOfObject = getCenterOfMass _target;
private _targetAdjustedPos = _target modelToWorldWorld _centerOfObject;
private _targetAdjustedPos = _target modelToWorldVisualWorld _centerOfObject;
_expectedTargetPos = _targetAdjustedPos;
_seekerStateParams set [3, _expectedTargetPos];
_seekerStateParams set [7, velocity _target];
_seekerStateParams set [8, CBA_missionTime];
_seekerStateParams set [9, false];
_targetData set [2, _projectile distance _target];
_targetData set [3, velocity _target];
if (_timestep != 0) then {
private _acceleration = ((velocity _target) vectorDiff _lastKnownVelocity) vectorMultiply (1 / _timestep);
_targetData set [4, _acceleration];
};
};
_targetData set [0, (getPosASLVisual _projectile) vectorFromTo _expectedTargetPos];
_seekerStateParams set [3, _expectedTargetPos];
_launchParams set [0, _target];
_expectedTargetPos

View File

@ -17,7 +17,7 @@
*/
params ["", "_args"];
_args params ["_firedEH", "_launchParams", "", "_seekerParams", "_stateParams"];
_args params ["_firedEH", "_launchParams", "", "_seekerParams", "_stateParams", "_targetData"];
_firedEH params ["","","","","","","_projectile"];
_launchParams params ["", "_targetParams"];
_targetParams params ["_target"];
@ -25,7 +25,7 @@ _seekerParams params ["_seekerAngle", "", "_seekerMaxRange"];
if (isNil "_target") exitWith {[0,0,0]};
private _foundTargetPos = aimPos _target;
private _foundTargetPos = _target modelToWorldVisualWorld getCenterOfMass _target;
// @TODO: This is seeker LOS and angle checks for LOAL only; LOBL does not need visual
private _angleOkay = [_projectile, _foundTargetPos, _seekerAngle] call FUNC(checkSeekerAngle);
@ -40,14 +40,11 @@ TRACE_2("",_angleOkay,_losOkay);
if (!_angleOkay || !_losOkay) exitWith {[0,0,0]};
TRACE_2("",_target,_foundTargetPos);
// @TODO: Configurable lead for seekers
private _projectileSpeed = (vectorMagnitude velocity _projectile);
private _distanceToTarget = (getPosASL _projectile) vectorDistance _foundTargetPos;
private _eta = _distanceToTarget / _projectileSpeed;
private _adjustDistance = (velocity _target) vectorMultiply _eta;
TRACE_3("leading target",_distanceToTarget,_eta,_adjustDistance);
_foundTargetPos = _foundTargetPos vectorAdd _adjustDistance;
_targetData set [0, (getPosASL _projectile) vectorFromTo _foundTargetPos];
_targetData set [2, _distanceToTarget];
_targetData set [3, velocity _target];
TRACE_2("return",_foundTargetPos,(aimPos _target) distance _foundTargetPos);
_foundTargetPos;

View File

@ -16,13 +16,13 @@
* Public: No
*/
params ["", "_args"];
_args params ["_firedEH", "", "", "_seekerParams", "_stateParams"];
_args params ["_firedEH", "", "", "_seekerParams", "_stateParams", "_targetData"];
_firedEH params ["_shooter","_weapon","","","","","_projectile"];
_seekerParams params ["_seekerAngle"];
_stateParams params ["", "_seekerStateParams"];
_seekerStateParams params ["_memoryPointGunnerOptics", "_animationSourceBody", "_animationSourceGun", "_usePilotCamera"];
private _shooterPos = AGLToASL (_shooter modelToWorld(_shooter selectionPosition _memoryPointGunnerOptics));
private _shooterPos = AGLToASL (_shooter modelToWorldVisual (_shooter selectionPosition _memoryPointGunnerOptics));
private _projPos = getPosASL _projectile;
private _lookDirection = if !(_shooter isKindOf "CAManBase" || {_shooter isKindOf "StaticWeapon"}) then {
@ -37,8 +37,9 @@ private _lookDirection = if !(_shooter isKindOf "CAManBase" || {_shooter isKindO
_shooter vectorModelToWorldVisual getPilotCameraDirection _shooter;
};
} else {
private _gBody = -deg(_shooter animationPhase _animationSourceBody);
private _gGun = deg(_shooter animationPhase _animationSourceGun);
// use animationSourcePhase
private _gBody = -deg(_shooter animationSourcePhase _animationSourceBody);
private _gGun = deg(_shooter animationSourcePhase _animationSourceGun);
_shooter vectorModelToWorldVisual ([1, _gBody, _gGun] call CBA_fnc_polar2vect);
};
_finalLookDirection
@ -58,5 +59,9 @@ if ((_testDotProduct < (cos _seekerAngle)) || {_testIntersections isNotEqualTo [
[0, 0, 0]
};
_shooterPos vectorAdd (_lookDirection vectorMultiply _distanceToProj);
private _returnPos = _shooterPos vectorAdd (_lookDirection vectorMultiply _distanceToProj);
_targetData set [0, _projPos vectorFromTo _returnPos];
_targetData set [2, _returnPos vectorDistance getPosASLVisual _projectile];
_returnPos

View File

@ -16,17 +16,58 @@
*
* Public: No
*/
#define MAX_AVERAGES 15
#define MINIMUM_DISTANCE_UNTIL_NEW_POS 1
params ["", "_args"];
_args params ["_firedEH", "_launchParams", "", "_seekerParams"];
params ["", "_args", "", "", "_timestep"];
_args params ["_firedEH", "_launchParams", "", "_seekerParams", "", "_targetData"];
_firedEH params ["","","","","","","_projectile"];
_launchParams params ["","","","","","_laserParams"];
_seekerParams params ["_seekerAngle", "", "_seekerMaxRange"];
_seekerParams params ["_seekerAngle", "", "_seekerMaxRange", "", ["_lastPositions", []], ["_lastPositionIndex", 0], ["_lastPositionSum", [0, 0, 0]]];
_laserParams params ["_code", "_wavelengthMin", "_wavelengthMax"];
private _laserResult = [(getPosASL _projectile), (velocity _projectile), _seekerAngle, _seekerMaxRange, [_wavelengthMin, _wavelengthMax], _code, _projectile] call EFUNC(laser,seekerFindLaserSpot);
private _foundTargetPos = _laserResult select 0;
TRACE_1("Search",_laserResult);
_foundTargetPos;
if (isNil "_foundTargetPos") exitWith {
[0, 0, 0]
};
// average out any error from laser jump
private _positionSum = [0, 0, 0];
{
_positionSum = _positionSum vectorAdd _x;
} forEach _lastPositions;
if (_foundTargetPos isNotEqualTo [0, 0, 0]) then {
_lastPositions set [_lastPositionIndex % MAX_AVERAGES, _foundTargetPos];
_seekerParams set [4, _lastPositions];
_seekerParams set [5, _lastPositionIndex + 1];
};
private _aproximateVelocity = [0, 0, 0];
_positionSum = _positionSum vectorAdd _foundTargetPos;
if (MAX_AVERAGES == count _lastPositions) then {
_positionSum = _positionSum vectorMultiply (1 / (1 + count _lastPositions));
// if we are within a meter of the previous average, just use the previous average
if (_positionSum distanceSqr _lastPositionSum < MINIMUM_DISTANCE_UNTIL_NEW_POS * MINIMUM_DISTANCE_UNTIL_NEW_POS) then {
_positionSum = _lastPositionSum;
};
if (_timestep != 0) then {
_aproximateVelocity = (_positionSum vectorDiff _lastPositionSum) vectorMultiply (1 / _timestep);
};
} else {
_positionSum = _positionSum vectorMultiply (1 / count _lastPositions);
};
_seekerParams set [6, _positionSum];
_targetData set [0, (getPosASL _projectile) vectorFromTo _positionSum];
_targetData set [3, _aproximateVelocity];
TRACE_3("laser target found",_foundTargetPos,_positionSum,count _lastPositions);
_positionSum

View File

@ -45,4 +45,3 @@ _attackProfileStateParams set [4, _maxDistanceSqr]; // max distance squared used
_attackProfileStateParams set [5, _minDistanceSqr];
_attackProfileStateParams set [6, _wireCutSource];
_attackProfileStateParams set [7, _distanceAheadOfMissile];

View File

@ -3,8 +3,9 @@
#include "\z\ace\addons\main\script_mod.hpp"
// #define DRAW_GUIDANCE_INFO
// #define ENABLE_PROJECTILE_CAMERA
// #define DEBUG_MODE_FULL
// #define DISABLE_COMPILE_CACHE
#define DISABLE_COMPILE_CACHE
// #define ENABLE_PERFORMANCE_COUNTERS
#ifdef DEBUG_ENABLED_MISSILEGUIDANCE
@ -28,4 +29,3 @@
#define DEFAULT_LEAD_DISTANCE 5
#define ACTIVE_RADAR_POLL_FREQUENCY (1 / 7)
#define ACTIVE_RADAR_MINIMUM_SCAN_AREA 30

View File

@ -27,10 +27,10 @@ Finally, flight profiles and mechanics for realistic missile simulations are als
## 2. Components
The framework is broken up into 3 major components: Locking Types, Seeker Types and Attack Profiles. In combination, these components build out the entire process of launching, locking and going terminal flight against targets.
The framework is broken up into 3 major components: Navigation Types, Seeker Types and Attack Profiles. In combination, these components build out the entire process of launching, locking and going terminal flight against targets.
#### 2.1 Locking Types
Locking types provide the basic functionality of targeting which will be based to a seeker type, providing target acquisition for seekers. This provides the basic functionality for providing pre-determined targets for a seeker, or allowing the seeker to perform its own target acquisition and locking. Additionally, the seeker may reference back into the locking type in order to re-perform target acquisition.
#### 2.1 Navigation Types
Navigation types define how the missile flies to the designated target. ACE 3 implements the most common ones in use, Proportional Navigation, Augmented Proportional Navigation, and Zero-Effort Miss. These navigation types give an acceleration command to the missile who attempts to satisfy the command by adjusting its pitch and yaw to fly to the target.
#### 2.2 Seeker Types
Each seeker is generally assumed to be the logic for the seeker head unit within any given munition. Seekers within this framework provide the basic targeting functionality for the entire framework. The locking type will provide a generic target to the seeker, or the seeker may aquire a target on its own. The seeker then provides a target, either an object or a ASL position, which is then passed further into the framework. This target (or position) should be the actual current target position for the missiles flight. Seekers are required to perform all limitations and checks within their systems, although various limitations have been provided in this framework such as LOS FOV, laser guidance, etc.
@ -42,9 +42,9 @@ An attack profile adjusts the current target flight location to create the actua
## 3. How it all ties together
The system is executed in a linear series of calls to each step of the process, and feeding back the return from that step to the next step. Execution is conducted using Locking -> Seeker -> Profile, iteratively every frame of execution. Flight times are adjusted to `accTime` values and FPS lag, giving consistent flight.
The system is executed in a linear series of calls to each step of the process, and feeding back the return from that step to the next step. Execution is conducted using Seeker -> Profile -> Navigation, iteratively every frame of execution. Flight times are adjusted to `accTime` values and FPS lag, giving consistent flight.
On each step of execution, a target specification array `[targetObj, targetPos]` is passed to the locking type, which then will return a possible modified target array. Next, this modified data is passed to the seeker type - which then, in turn, returns a position vector to the current "seeked" target position (ASL). Last, this target position is passed to the attack profile, which then returns an "adjusted attack position" (ASL), which is the location the missile should *currently* be homing on for flight.
On each step of execution, the seeker finds and returns the position of its target (ASL) and, optionally, sets target data specifying the target's direction from the missile, range, velocity and acceleration (if able). Then, this data is passed to the attack profile, which then returns an "adjusted attack position" (ASL), which is the location the missile should *currently* be homing on for flight. Finally, the navigation system processes the seeker's data about the target and returns a "Commanded Acceleration" vector which the missile attempts to satisfy.
In the simplest sense, the entire system provides the flight trajectory of the missile homing directly on the "adjusted attack position"; thus, an attack profile would ajust this position to direct the missile. For example, top down attacks return the adjusted attack position high above the target, until entering their terminal stages, which then changes the position to be directly on top of the target - thus "walking the missile" along its flight path and to the impact.
@ -60,28 +60,47 @@ class CfgAmmo {
// Begin ACE guidance Configs
class ace_missileguidance {
enabled = 1; // Enable missile guidance (0-disabled, 1-enabled)
enabled = 1; // Explicit enabling of the system
minDeflection = 0.00025; // Minimum flap deflection for guidance
maxDeflection = 0.001; // Maximum flap deflection for guidance
incDeflection = 0.0005; // The increment in which deflection adjusts
pitchRate = 30; // How many degrees/second the missile can pitch
yawRate = 30; // How many degrees/second this missile can yaw
canVanillaLock = 0; // Enable vanilla lock, only applicable to non-cadet modes, 'recruit' always uses vanilla locking (0-disabled, 1-enabled)
canVanillaLock = 0; // Can this default vanilla lock? Only applicable to non-cadet mode
// Guidance type for munitions
defaultSeekerType = "SALH"; // Default seeker type
seekerTypes[] = {"SALH", "LIDAR", "SARH", "Optic", "Thermal", "GPS", "SACLOS", "MCLOS"}; // Seeker types available
seekerTypes[] = { "SALH", "LIDAR", "SARH", "Optic", "Thermal", "GPS", "SACLOS", "MCLOS" };
defaultSeekerLockMode = "LOAL"; // Default seeker lock mode
seekerLockModes[] = {"LOAL", "LOBL"}; // Seeker lock modes available
defaultSeekerLockMode = "LOAL"; // Default lock mode
seekerLockModes[] = { "LOAL", "LOBL" };
seekerAngle = 90; // Angle in front of the missile which can be searched
seekerAccuracy = 1; // Seeker accuracy multiplier
defaultNavigationType = "Direct"; // Default navigation type
navigationTypes[] = { "Direct", "ZeroEffortMiss" }; // Navigation types this missile can use
seekLastTargetPos = 1; // seek last target position [if seeker loses LOS of target, continue to last known pos]
seekerAngle = 70; // Angle in front of the missile which can be searched
seekerAccuracy = 1; // seeker accuracy multiplier
seekerMinRange = 1; // Minimum range from the missile which the seeker can visually search
seekerMaxRange = 2500; // Maximum from the missile which the seeker can visually search
seekerMaxRange = 8000; // Maximum range from the missile which the seeker can visually search
defaultAttackProfile = "LIN"; // Default attack profile
attackProfiles[] = {"LIN", "DIR", "MID", "HI"}; // Attack profiles available
// Attack profile type selection
defaultAttackProfile = "hellfire"; // Default attack profile
attackProfiles[] = {"hellfire", "hellfire_hi", "hellfire_lo"}; // Possible attack profiles
// State machine defining what navigation type to use in this missiles phase
class navigationStates {
class initial {
transitionCondition = "my_fnc_navigationTransition"; // Condition needed to transition to next state
navigationType = "Direct"; // Navigation type to use in this state
};
class terminal {
transitionCondition = "";
navigationType = "ZeroEffortMiss";
};
// transitions from initial -> termimal
states[] = {"initial", "terminal"};
};
};
```
@ -95,6 +114,7 @@ class ace_missileguidance_seekerTypes {
description = ""; // Description
functionName = "my_fnc_doSeekerType"; // Function that handles the seeker type
onFired = "my_fnc_onFired"; // Function that runs when the missile is fired using this seeker
};
};
```
@ -109,6 +129,17 @@ class ace_missileguidance_attackProfiles {
description = ""; // Description
functionName = "my_fnc_doAttackProfile"; // Function that handles the attack profile
onFired = "my_fnc_onFired"; // Function that runs when missile is fired using this attack profile
};
};
```
### 4.4 Custom Navigation Type
```cpp
class ace_missileguidance_navigationTypes {
class MyNavigationProfile {
functionName = "my_fnc_navigation"; // Function to run for navigation
onFired = "my_fnc_onFired"; // Function to run when the missile is fired with this navigation type
};
};
```