329 lines
13 KiB
Plaintext
329 lines
13 KiB
Plaintext
|
|
# ****************************************************************************
|
||
|
|
# CUI
|
||
|
|
#
|
||
|
|
# The Advanced Framework for Simulation, Integration, and Modeling (AFSIM)
|
||
|
|
#
|
||
|
|
# The use, dissemination or disclosure of data in this file is subject to
|
||
|
|
# limitation or restriction. See accompanying README and LICENSE for details.
|
||
|
|
# ****************************************************************************
|
||
|
|
|
||
|
|
include_once behavior_timeline_reposition.txt #drag to restart further back on timeline
|
||
|
|
include_once behavior_timeline_delay.txt #maneuver to delay enemy's engagement
|
||
|
|
include_once behavior_timeline_engage.txt #intercept/shoot timeline targets
|
||
|
|
#include_once behavior_timeline_support.txt #fly support role on a timeline (usually behind lead shooter role)
|
||
|
|
include_once behavior_weapon_support.txt #crank & support weapon
|
||
|
|
include_once behavior_planned_route.txt #no timeline tasks, just return to route
|
||
|
|
include_once behavior_evade.txt #evade iminent threats (missiles)
|
||
|
|
include_once behavior_go_home.txt #disengage, go back to initial location
|
||
|
|
|
||
|
|
|
||
|
|
#include_once behavior_fly_timeline.txt
|
||
|
|
|
||
|
|
processor TIMELINE_PROC WSF_QUANTUM_TASKER_PROCESSOR
|
||
|
|
update_interval 2 sec
|
||
|
|
script_variables
|
||
|
|
double mGeesToTurnWith = 6.0; #update this however intelligently you can (based on risk level?)
|
||
|
|
#double mOffsetAngle = 45.0; #TODO - calculate half sensor angle?
|
||
|
|
double mOffsetAngle = 30.0; #TODO - calculate half sensor angle?
|
||
|
|
|
||
|
|
double mDelaySpeed = 204; #0.6
|
||
|
|
double mEngageSpeed = 237; #0.8
|
||
|
|
double mDragSpeed = 309; #1.2
|
||
|
|
|
||
|
|
double mDefaultAltitude = 35000 * MATH.M_PER_FT();
|
||
|
|
#double mDragAltitude = 30000 * MATH.M_PER_FT();
|
||
|
|
double mDragAltitude = 35000 * MATH.M_PER_FT(); #testing
|
||
|
|
|
||
|
|
bool mConsiderPeersWeapons = true; #do not shoot if peers already are
|
||
|
|
#bool mRequiredToSupportWeapons = true; #TODO - update dynamically somehow?
|
||
|
|
bool mRequiredToSupportWeapons = true; #TODO - update dynamically somehow?
|
||
|
|
|
||
|
|
double DefaultPercentRangeMax = 0.80; # don't launch unless within this percent of Rmax
|
||
|
|
double DefaultPercentRangeMin = 1.20; # don't launch unless beyond this percent of Rmin
|
||
|
|
##weapon + threat specific shooting parameters
|
||
|
|
##specify an Rmax based on which weapon used and which threat engaged
|
||
|
|
#Map<string, Map<string, double>> WeaponThreatRmaxMap = Map<string, Map<string, double>>();
|
||
|
|
#WeaponThreatRmaxMap["base_weapon"] = Map<string, double>();
|
||
|
|
#WeaponThreatRmaxMap["base_weapon"].Set("fighter", 0.80);
|
||
|
|
|
||
|
|
bool mDrawSeparationData = true;
|
||
|
|
string mSeparateSide = "LEFT"; #this is dynamically updated based on engagement axis
|
||
|
|
|
||
|
|
#do not edit these:
|
||
|
|
bool mSeparateEngagement = false;
|
||
|
|
double mEngagementTrueBearing = -1.0; #valid if in [0,360], update based on engagement axis
|
||
|
|
WsfDraw mDraw = WsfDraw();
|
||
|
|
|
||
|
|
Array<WsfLocalTrack> mTimeLineLocalTargets;
|
||
|
|
WsfLocalTrack mNearestTimeLineTarget;
|
||
|
|
|
||
|
|
Array<WsfLocalTrack> mTimeLineLocalTargetsToShoot = Array<WsfLocalTrack>();
|
||
|
|
|
||
|
|
end_script_variables
|
||
|
|
|
||
|
|
on_update
|
||
|
|
#update these if necessary
|
||
|
|
mSeparateEngagement = false;
|
||
|
|
mEngagementTrueBearing = -1.0;
|
||
|
|
|
||
|
|
#populate shared variables
|
||
|
|
WsfTask timeline = GetTimeLineTask(PROCESSOR);
|
||
|
|
mTimeLineLocalTargets = GetTimeLineTaskLocalTargets(PLATFORM, timeline);
|
||
|
|
Array<double> times = Array<double>();
|
||
|
|
foreach(WsfLocalTrack target in mTimeLineLocalTargets)
|
||
|
|
{
|
||
|
|
double time = TimeToReachPoint(target, PLATFORM.Location(), mGeesToTurnWith);
|
||
|
|
times.PushBack(time);
|
||
|
|
}
|
||
|
|
SortLocalTracksByValue(mTimeLineLocalTargets, times);
|
||
|
|
mNearestTimeLineTarget = GetNearestTimeLineTaskLocalTarget(PLATFORM, timeline);
|
||
|
|
|
||
|
|
|
||
|
|
if (timeline.IsValid() &&
|
||
|
|
timeline.AuxDataExists("point_a") &&
|
||
|
|
timeline.AuxDataExists("point_b") )
|
||
|
|
{
|
||
|
|
WsfGeoPoint SeparateA = (WsfGeoPoint)timeline.AuxDataObject("point_a");
|
||
|
|
WsfGeoPoint SeparateB = (WsfGeoPoint)timeline.AuxDataObject("point_b");
|
||
|
|
if (SeparateA.IsValid() && SeparateB.IsValid())
|
||
|
|
{
|
||
|
|
WsfGeoPoint near;
|
||
|
|
WsfGeoPoint far;
|
||
|
|
if (PLATFORM.GroundRangeTo(SeparateA) < PLATFORM.GroundRangeTo(SeparateB))
|
||
|
|
{
|
||
|
|
near = SeparateA;
|
||
|
|
far = SeparateB;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
near = SeparateB;
|
||
|
|
far = SeparateA;
|
||
|
|
}
|
||
|
|
mDraw.SetLayer("timeline_proc");
|
||
|
|
mDraw.SetId("timeline_proc");
|
||
|
|
mDraw.SetDuration(PROCESSOR.UpdateInterval());
|
||
|
|
mDraw.SetLineSize(1);
|
||
|
|
mDraw.SetLineStyle("solid");
|
||
|
|
|
||
|
|
Array<WsfTrack> targets = (Array<WsfTrack>)timeline.AuxDataObject("targets");
|
||
|
|
#check if platform is on the correct side of separation line
|
||
|
|
WsfGeoPoint clusterMeanLocation = (WsfGeoPoint)timeline.AuxDataObject("cluster_mean");
|
||
|
|
double refAz = near.TrueBearingTo(far);
|
||
|
|
|
||
|
|
#save this engagement axis true bearing!
|
||
|
|
mSeparateEngagement = true;
|
||
|
|
mEngagementTrueBearing = MATH.NormalizeAngle0_360(refAz);
|
||
|
|
|
||
|
|
double ownAz = near.TrueBearingTo(PLATFORM.Location()) - refAz;
|
||
|
|
double tgtAz = near.TrueBearingTo(clusterMeanLocation) - refAz;
|
||
|
|
ownAz = MATH.NormalizeAngleMinus180_180(ownAz);
|
||
|
|
tgtAz = MATH.NormalizeAngleMinus180_180(tgtAz);
|
||
|
|
|
||
|
|
#save off turn direction
|
||
|
|
if (tgtAz < 0) {
|
||
|
|
mSeparateSide = "LEFT";
|
||
|
|
} else {
|
||
|
|
mSeparateSide = "RIGHT";
|
||
|
|
}
|
||
|
|
|
||
|
|
bool CorrectSide = ((ownAz * tgtAz) > 0);
|
||
|
|
|
||
|
|
#draw lines for separation divide
|
||
|
|
mDraw.SetColor(0.0, 0.0, 0.0); #black
|
||
|
|
mDraw.BeginLines();
|
||
|
|
mDraw.Vertex(near);
|
||
|
|
mDraw.Vertex(far);
|
||
|
|
mDraw.End();
|
||
|
|
|
||
|
|
if (CorrectSide) {
|
||
|
|
mDraw.SetColor(0.0, 1.0, 1.0); #aqua
|
||
|
|
} else {
|
||
|
|
mDraw.SetColor(1.0, 0.0, 0.0); #red
|
||
|
|
}
|
||
|
|
|
||
|
|
#draw line from me to target center of gravity
|
||
|
|
mDraw.BeginLines();
|
||
|
|
mDraw.Vertex(PLATFORM);
|
||
|
|
#mDraw.Vertex(targetCluster.MeanLocation());
|
||
|
|
mDraw.Vertex(mTimeLineLocalTargets.Front().CurrentLocation());
|
||
|
|
mDraw.End();
|
||
|
|
|
||
|
|
# mDraw.SetEllipseMode("line");
|
||
|
|
# mDraw.BeginCircle(0.0, 4.0 * MATH.M_PER_NM());
|
||
|
|
# mDraw.Vertex(PLATFORM);
|
||
|
|
# mDraw.End();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
#update the "mTimeLineLocalTargetsToShoot" list
|
||
|
|
mTimeLineLocalTargetsToShoot.Clear();
|
||
|
|
if (timeline.IsValid())
|
||
|
|
{
|
||
|
|
|
||
|
|
string RISK = timeline.AuxDataString("RISK");
|
||
|
|
|
||
|
|
foreach(WsfLocalTrack target in mTimeLineLocalTargets)
|
||
|
|
{
|
||
|
|
writeln_d("T=",TIME_NOW," ", PLATFORM.Name(), " checking if shootable: ", target.TargetName());
|
||
|
|
|
||
|
|
if (FiredOn(PLATFORM, target, mConsiderPeersWeapons))
|
||
|
|
{
|
||
|
|
writeln_d(" NO - already fired on!");
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (mRequiredToSupportWeapons == true && RISK != "HIGH") #can support any shot when risk is high (won't be dragging)
|
||
|
|
{
|
||
|
|
double gate = timeline.AuxDataDouble("DOR"); #range (meters)
|
||
|
|
if (RISK == "MEDIUM")
|
||
|
|
{
|
||
|
|
gate = timeline.AuxDataDouble("MAR"); #range (meters)
|
||
|
|
}
|
||
|
|
|
||
|
|
#calculate if we have time or range to support a shot to this target:
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
###################################################################
|
||
|
|
###################################################################
|
||
|
|
###################################################################
|
||
|
|
|
||
|
|
|
||
|
|
#figure out weapon range & time of flight
|
||
|
|
double weaponMaxRange = 0.0;
|
||
|
|
WsfWeapon maxRangeWeapon;
|
||
|
|
for (int i=0; i < PLATFORM.WeaponCount(); i+=1)
|
||
|
|
{
|
||
|
|
WsfWeapon weapon = PLATFORM.WeaponEntry(i);
|
||
|
|
if (WeaponCapableAvailableAgainstThreat(weapon, target))
|
||
|
|
{
|
||
|
|
double weaponRange = MaxRange(PLATFORM,weapon,target);
|
||
|
|
if (weaponRange > weaponMaxRange)
|
||
|
|
{
|
||
|
|
weaponMaxRange = weaponRange;
|
||
|
|
maxRangeWeapon = weapon;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (!maxRangeWeapon.IsValid())
|
||
|
|
{
|
||
|
|
writeln_d(" NO - do not have a weapon for the target");
|
||
|
|
continue; #no weapon for this target
|
||
|
|
}
|
||
|
|
#estimate time before we can shoot
|
||
|
|
#hypothetical shot
|
||
|
|
#estimate time cranking (based on weapon travel time)
|
||
|
|
#does this leave us enough time to turn to drag?
|
||
|
|
double weaponTOF = TimeToHit(PLATFORM, maxRangeWeapon, target);
|
||
|
|
|
||
|
|
if (weaponTOF <= 0)
|
||
|
|
{
|
||
|
|
writeln_d(" NO - could not find TOF data for weapon");
|
||
|
|
#good weapon data no found
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
double firingRange = DefaultPercentRangeMax * weaponMaxRange;
|
||
|
|
|
||
|
|
writeln_d(" INFO - weapon \"", maxRangeWeapon.Name(), "\" firing range = ", firingRange, " TOF = ", weaponTOF);
|
||
|
|
|
||
|
|
double targetRange = PLATFORM.GroundRangeTo(target.CurrentLocation());
|
||
|
|
double rangeToGo = targetRange - firingRange;
|
||
|
|
double timeTilShot = 0.0;
|
||
|
|
|
||
|
|
double range = PLATFORM.GroundRangeTo(mNearestTimeLineTarget.CurrentLocation());
|
||
|
|
|
||
|
|
if (rangeToGo > 0)
|
||
|
|
{
|
||
|
|
#assume target continues on its current heading
|
||
|
|
#assume nearest threat closes towards you
|
||
|
|
#assume we close towards target
|
||
|
|
double myRelativeBearingToThreat = MATH.Fabs(MATH.NormalizeAngleMinus180_180(PLATFORM.TrueBearingTo(mNearestTimeLineTarget) - PLATFORM.TrueBearingTo(target)));
|
||
|
|
double targetRelativeBearingToMe = target.RelativeBearingTo(PLATFORM);
|
||
|
|
double closingSpeedToTarget = PLATFORM.Speed() + MATH.Cos(targetRelativeBearingToMe) * target.Speed();
|
||
|
|
double closingSpeedOfThreat = mNearestTimeLineTarget.Speed() + MATH.Cos(myRelativeBearingToThreat) * PLATFORM.Speed();
|
||
|
|
|
||
|
|
if (closingSpeedToTarget <= 0)
|
||
|
|
{
|
||
|
|
writeln_d(" NO - negative closing speed to target with range remaining to shoot");
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
writeln_d(" INFO - closing to target = ", closingSpeedToTarget, " closing of threat = ", closingSpeedOfThreat);
|
||
|
|
|
||
|
|
#subtract off closing distance to nearest threat during the time before shot
|
||
|
|
timeTilShot = rangeToGo / closingSpeedToTarget;
|
||
|
|
|
||
|
|
double temp = range;
|
||
|
|
range -= (timeTilShot * closingSpeedOfThreat);
|
||
|
|
|
||
|
|
writeln_d(" INFO - starting threat range = ", temp, " range after closing to shot loc = ", range);
|
||
|
|
}
|
||
|
|
|
||
|
|
#subtract off closing distance to nearest target during the time of weapon's flight
|
||
|
|
double closingSpeedOfThreat = (mDelaySpeed * MATH.Cos(mOffsetAngle)) + mNearestTimeLineTarget.Speed();
|
||
|
|
double temp = range;
|
||
|
|
range -= (weaponTOF * closingSpeedOfThreat);
|
||
|
|
|
||
|
|
writeln_d(" INFO - range at shot = ", temp, " range after supporting shot = ", range);
|
||
|
|
|
||
|
|
# #calculate time to turn to drag (after cranking)
|
||
|
|
# Array<double> vals = Array<double>();
|
||
|
|
# double turnAngle = 180 - mOffsetAngle;
|
||
|
|
# TurnAngleTimeAndDistance(turnAngle, mEngageSpeed, mGeesToTurnWith, vals);
|
||
|
|
# double turnTime = vals[0];
|
||
|
|
# double distSep = vals[1];
|
||
|
|
# #subtract off closing distance to nearest target during the time of turning to drag
|
||
|
|
# range += distSep;
|
||
|
|
# range -= (turnTime*mNearestTimeLineTarget.Speed());
|
||
|
|
|
||
|
|
if (range >= gate)
|
||
|
|
{
|
||
|
|
writeln_d(" YES - have time & range to support the shot.");
|
||
|
|
|
||
|
|
mTimeLineLocalTargetsToShoot.PushBack(target);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
writeln_d(" NO - do not have time & range to support the shot.");
|
||
|
|
##TODO - should we break here?
|
||
|
|
## its highly unlikely futher away targets will be shootable
|
||
|
|
#break;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
###################################################################
|
||
|
|
###################################################################
|
||
|
|
###################################################################
|
||
|
|
|
||
|
|
|
||
|
|
}
|
||
|
|
else if (mRequiredToSupportWeapons == false || RISK == "HIGH")
|
||
|
|
{
|
||
|
|
writeln_d(" YES - risk is high or not required to support.");
|
||
|
|
|
||
|
|
#all targets are for shooting (if not already shot on)
|
||
|
|
mTimeLineLocalTargetsToShoot.PushBack(target);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
end_on_update
|
||
|
|
|
||
|
|
behavior_tree
|
||
|
|
selector
|
||
|
|
behavior_node evade #
|
||
|
|
behavior_node go_home #
|
||
|
|
behavior_node timeline_reposition #
|
||
|
|
#behavior_node timeline_support #TODO - integrate kyle's grinder behavior support stuff
|
||
|
|
behavior_node timeline_delay #TODO - integrate nick's delay script
|
||
|
|
behavior_node timeline_engage #TODO - only shoot if there is time to support it
|
||
|
|
behavior_node weapon_support #
|
||
|
|
behavior_node planned_route #
|
||
|
|
end_selector
|
||
|
|
end_behavior_tree
|
||
|
|
end_processor
|