1155 lines
39 KiB
Plaintext
1155 lines
39 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.
|
|
# ****************************************************************************
|
|
|
|
# * * ************************************** * *
|
|
# * ****** Demonstration input file ****** *
|
|
# * ****** UNCLASSIFIED ****** *
|
|
# * * ************************************** * *
|
|
|
|
# --------------------------------------------------------------
|
|
# Common Script Methods
|
|
# --------------------------------------------------------------
|
|
|
|
script_interface
|
|
|
|
script_debug_writes off
|
|
|
|
script_variables
|
|
Array<string> mCategories = Array<string>();
|
|
mCategories.PushBack("primary_target");
|
|
mCategories.PushBack("secondary_target");
|
|
mCategories.PushBack("fighter");
|
|
mCategories.PushBack("bomber");
|
|
mCategories.PushBack("jammer");
|
|
mCategories.PushBack("uav");
|
|
mCategories.PushBack("awacs");
|
|
mCategories.PushBack("ship");
|
|
mCategories.PushBack("sam");
|
|
mCategories.PushBack("missile");
|
|
mCategories.PushBack("acquisition");
|
|
mCategories.PushBack("cruise-missile");
|
|
mCategories.PushBack("insurgent");
|
|
mCategories.PushBack("ambush_site");
|
|
|
|
|
|
Map<string, Map<string, double>> mWeaponsEnvelope = Map<string, Map<string, double>>();
|
|
mWeaponsEnvelope["red"] = Map<string, double>();
|
|
mWeaponsEnvelope["blue"] = Map<string, double>();
|
|
mWeaponsEnvelope["red"].Set( "fighter", 40 * 1852); //40 nm (units of meters)
|
|
mWeaponsEnvelope["red"].Set( "bomber" , 40 * 1852); //40 nm (units of meters)
|
|
mWeaponsEnvelope["red"].Set( "ship" , 40 * 1852); //40 nm (units of meters)
|
|
mWeaponsEnvelope["red"].Set( "sam" , 40 * 1852); //40 nm (units of meters)
|
|
mWeaponsEnvelope["blue"].Set("fighter", 50 * 1852); //50 nm (units of meters)
|
|
mWeaponsEnvelope["blue"].Set("bomber" , 50 * 1852); //50 nm (units of meters)
|
|
mWeaponsEnvelope["blue"].Set("ship" , 50 * 1852); //50 nm (units of meters)
|
|
mWeaponsEnvelope["blue"].Set("sam" , 50 * 1852); //50 nm (units of meters)
|
|
double mDefaultWeaponsEnvelope = 40 * 1852; //40 nm (units of meters)
|
|
|
|
|
|
#flag variables for evading (both by diving/climbing and banking left/right)
|
|
Map<string,bool> diveDownMap = Map<string,bool>();
|
|
Map<string,bool> bankLeftMap = Map<string,bool>();
|
|
|
|
|
|
Array<string> mBasicColors = Array<string>();
|
|
mBasicColors.Set(0, "Red");
|
|
mBasicColors.Set(1, "Green");
|
|
mBasicColors.Set(2, "Blue");
|
|
mBasicColors.Set(3, "Yellow");
|
|
mBasicColors.Set(4, "Teal");
|
|
mBasicColors.Set(5, "Violet");
|
|
|
|
Map<string, double> mRedColorValue = Map<string, double>();
|
|
mRedColorValue.Set("Red", 1.0);
|
|
mRedColorValue.Set("Green", 0.0);
|
|
mRedColorValue.Set("Blue", 0.0);
|
|
mRedColorValue.Set("Yellow",1.0);
|
|
mRedColorValue.Set("Teal", 0.0);
|
|
mRedColorValue.Set("Violet",1.0);
|
|
|
|
Map<string, double> mGreenColorValue = Map<string, double>();
|
|
mGreenColorValue.Set("Red", 0.0);
|
|
mGreenColorValue.Set("Green", 1.0);
|
|
mGreenColorValue.Set("Blue", 0.0);
|
|
mGreenColorValue.Set("Yellow",1.0);
|
|
mGreenColorValue.Set("Teal", 1.0);
|
|
mGreenColorValue.Set("Violet",0.0);
|
|
|
|
Map<string, double> mBlueColorValue = Map<string, double>();
|
|
mBlueColorValue.Set("Red", 0.0);
|
|
mBlueColorValue.Set("Green", 0.0);
|
|
mBlueColorValue.Set("Blue", 1.0);
|
|
mBlueColorValue.Set("Yellow",0.0);
|
|
mBlueColorValue.Set("Teal", 1.0);
|
|
mBlueColorValue.Set("Violet",1.0);
|
|
|
|
Array<string> ttrs = {"red_target_tracking_radar_1",
|
|
"red_target_tracking_radar_2",
|
|
"red_target_tracking_radar_3",
|
|
"etc..."};
|
|
|
|
Array<string> aiais = {"blue_striker_1",
|
|
"blue_striker_2",
|
|
"blue_striker_3",
|
|
"etc..."};
|
|
end_script_variables
|
|
|
|
|
|
|
|
/* #put this script somewhere in the simulation input if you want the aiai to be reactive to certain target tracking radars
|
|
#goal: determine when a target tracking radar is active on an AIAI
|
|
#observer callback for new sensor tracks
|
|
script void MySensorTrackInitiated(WsfPlatform aPlatform, WsfSensor aSensor, WsfTrack aTrack)
|
|
foreach(string aiai in aiais)
|
|
{
|
|
if (aiai == aTrack.TargetName()) #an aiai is being tracked
|
|
{
|
|
foreach(string ttr in ttrs)
|
|
{
|
|
if (ttr == aPlatform.Name()) #a ttr of interest is tracking him
|
|
{
|
|
WsfPlatform aiaiPlatform = WsfSimulation.FindPlatform(aiai);
|
|
if (aiaiPlatform.IsValid())
|
|
{
|
|
aiaiPlatform.SetAuxData(ttr, true);
|
|
writeln_d(ttr, " is tracking ", aiai);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
end_script
|
|
|
|
observer
|
|
enable SENSOR_TRACK_INITIATED MySensorTrackInitiated
|
|
end_observer
|
|
*/
|
|
|
|
script string GetNextBasicColor()
|
|
static int mNextColor = 0;
|
|
if (mNextColor >= mBasicColors.Size())
|
|
{
|
|
mNextColor = 0;
|
|
}
|
|
string color = mBasicColors.Get(mNextColor);
|
|
mNextColor = mNextColor + 1;
|
|
return color;
|
|
end_script
|
|
|
|
script double RedVal(string color)
|
|
return mRedColorValue.Get(color);
|
|
end_script
|
|
|
|
script double GrnVal(string color)
|
|
return mGreenColorValue.Get(color);
|
|
end_script
|
|
|
|
script double BluVal(string color)
|
|
return mBlueColorValue.Get(color);
|
|
end_script
|
|
|
|
|
|
script bool FlyHold (WsfPlatform flyer, double heading, double altitude, double speed)
|
|
if (flyer.Mover().IsValid())
|
|
{
|
|
if (flyer.Mover().IsA_TypeOf("WSF_6DOF_MOVER"))
|
|
{
|
|
|
|
# flyer.GoToSpeed(speed);
|
|
# WsfGeoPoint pt = WsfGeoPoint.Construct(flyer.Latitude(), flyer.Longitude(), altitude);
|
|
# pt.Offset(heading, 18520, 0, 0); //10 nm away
|
|
# flyer.GoToLocation(pt);
|
|
# return true;
|
|
|
|
flyer.GoToSpeed(speed);
|
|
flyer.GoToAltitude(altitude);
|
|
flyer.TurnToHeading(heading, 500);
|
|
return true;
|
|
|
|
}
|
|
else if (flyer.Mover().IsA_TypeOf("WSF_AIR_MOVER"))
|
|
{
|
|
flyer.GoToSpeed(speed);
|
|
flyer.GoToAltitude(altitude);
|
|
flyer.TurnToHeading(heading);
|
|
return true;
|
|
}
|
|
}
|
|
writeln_d(flyer.Name(), " aiai error: unknown or invalid mover for flight");
|
|
return false;
|
|
end_script
|
|
|
|
|
|
script bool FlyTarget (WsfPlatform flyer, WsfGeoPoint location, double speed)
|
|
if (flyer.Mover().IsValid())
|
|
{
|
|
if (flyer.Mover().IsA_TypeOf("WSF_6DOF_MOVER"))
|
|
{
|
|
flyer.GoToSpeed(speed);
|
|
flyer.GoToLocation(location);
|
|
return true;
|
|
}
|
|
else if (flyer.Mover().IsA_TypeOf("WSF_AIR_MOVER"))
|
|
{
|
|
flyer.GoToSpeed(speed);
|
|
double altRateOfChange = flyer.Speed() * MATH.Sin(35.0);
|
|
flyer.GoToAltitude(location.Altitude(), altRateOfChange);
|
|
flyer.TurnToHeading(flyer.TrueBearingTo(location));
|
|
return true;
|
|
}
|
|
}
|
|
writeln_d(flyer.Name(), " aiai error: unknown or invalid mover for flight");
|
|
return false;
|
|
end_script
|
|
|
|
|
|
script string DeterminePlatformCategory(WsfPlatform aPlatform)
|
|
if( aPlatform.IsValid() )
|
|
{
|
|
foreach( string aCategory in mCategories )
|
|
{
|
|
if( aPlatform.CategoryMemberOf( aCategory ) )
|
|
{
|
|
return aCategory;
|
|
}
|
|
}
|
|
}
|
|
return "unknown";
|
|
end_script
|
|
|
|
script string DetermineTrackCategory(WsfTrack aTrack)
|
|
if (!aTrack.IsValid())
|
|
{
|
|
writeln_d("DetermineTrackCategory() invalid track!");
|
|
return "unknown";
|
|
}
|
|
|
|
int ind = aTrack.TargetIndex();
|
|
WsfPlatform plat;
|
|
plat = WsfSimulation.FindPlatform( ind );
|
|
|
|
if (!plat.IsValid())
|
|
{
|
|
writeln_d("DetermineTrackCategory() invalid platform from track!");
|
|
return "unknown";
|
|
}
|
|
|
|
string cat = DeterminePlatformCategory( plat );
|
|
return cat;
|
|
end_script
|
|
|
|
script string DetermineSensorCategory(WsfSensor aSensor)
|
|
if( aSensor.IsValid() )
|
|
{
|
|
foreach( string aCategory in mCategories )
|
|
{
|
|
if( aSensor.CategoryMemberOf( aCategory ) )
|
|
{
|
|
return aCategory;
|
|
}
|
|
}
|
|
}
|
|
return "unknown";
|
|
end_script
|
|
|
|
script bool TestPlatformCategory(WsfPlatform aPlatform, string aCategory)
|
|
if (!aPlatform.IsValid())
|
|
{
|
|
writeln_d("--- TestPlatformCategory(), aPlatform is not valid!");
|
|
return (aCategory == "unknown");
|
|
}
|
|
else if( aPlatform.CategoryMemberOf( aCategory ) )
|
|
{
|
|
writeln_d("--- TestPlatformCategory(), aCategory, ", aCategory, ", MATCH with platform, ", aPlatform.Name(), "!");
|
|
return true;
|
|
}
|
|
else if (aCategory == DeterminePlatformCategory(aPlatform))
|
|
{
|
|
writeln_d("--- TestPlatformCategory(), aCategory, ", aCategory, ", MATCH with platform, ", aPlatform.Name(), "!");
|
|
return true;
|
|
}
|
|
|
|
#writeln_d("--- TestPlatformCategory(), aCategory, ", aCategory, ", NOT A MATCH with platform, ", aPlatform.Name(), "!");
|
|
return false;
|
|
end_script
|
|
|
|
script bool TestTrackCategory(WsfTrack aTrack, string aCategory)
|
|
if (!aTrack.IsValid())
|
|
{
|
|
writeln_d("--- TestTrackCategory(), aTrack is not valid!");
|
|
// "unknown" returns true when aTrack is invalid
|
|
return (aCategory == "unknown");
|
|
}
|
|
|
|
int ind = aTrack.TargetIndex();
|
|
//writeln_d("--- TestTrackCategory(), target index: ", ind);
|
|
WsfPlatform plat;
|
|
plat = WsfSimulation.FindPlatform(ind);
|
|
|
|
if (!plat.IsValid())
|
|
{
|
|
writeln_d("--- TestTrackCategory(), plat is not valid!");
|
|
// "unknown" returns true when aTrack is invalid
|
|
return (aCategory == "unknown");
|
|
}
|
|
|
|
bool match = TestPlatformCategory(plat, aCategory);
|
|
return match;
|
|
end_script
|
|
|
|
script bool TestSensorCategory(WsfSensor aSensor, string aCategory)
|
|
if (!aSensor.IsValid())
|
|
{
|
|
writeln_d("--- TestSensorCategory(), aSensor is not valid!");
|
|
return (aCategory == "unknown");
|
|
}
|
|
else if( aSensor.CategoryMemberOf( aCategory ) )
|
|
{
|
|
writeln_d("--- TestSensorCategory(), aCategory, ", aCategory, ", is a match!");
|
|
return true;
|
|
}
|
|
else if (aCategory == DetermineSensorCategory(aSensor))
|
|
{
|
|
writeln_d("--- TestSensorCategory(), aCategory, ", aCategory, ", is a match!");
|
|
return true;
|
|
}
|
|
|
|
writeln_d("--- TestSensorCategory(), aCategory, ", aCategory, ", is NOT a match with sensor, ", aSensor.Name(), "!");
|
|
return false;
|
|
end_script
|
|
|
|
|
|
|
|
script double GetWeaponsEnvelope(WsfPlatform aThreatPlatform)
|
|
if (aThreatPlatform.IsValid() && mWeaponsEnvelope.Exists(aThreatPlatform.Side()))
|
|
{
|
|
Map<string, double> categoryRangeMap = mWeaponsEnvelope.Get(aThreatPlatform.Side());
|
|
foreach (string aCategory : double range in categoryRangeMap)
|
|
{
|
|
if( aThreatPlatform.CategoryMemberOf( aCategory ) )
|
|
{
|
|
return range;
|
|
}
|
|
}
|
|
}
|
|
return mDefaultWeaponsEnvelope;
|
|
end_script
|
|
|
|
|
|
|
|
//script double GetWeaponsEnvelope(WsfTrack aThreat)
|
|
// return GetWeaponsEvelope(aThreat.Target());
|
|
//end_script
|
|
|
|
|
|
|
|
// converts the actual groundRange into an effectiveRange that indicates "how far" aPlatform and aTrack
|
|
// are from each other given their closing speed and the thrust duration of the missle
|
|
// VERY HACKY
|
|
script double EffectiveRange (WsfPlatform aPlatform, WsfTrack aTarget)
|
|
double groundRange = aPlatform.GroundRangeTo(aTarget);
|
|
double closingSpeed = aPlatform.ClosingSpeedOf(aTarget);
|
|
double relativeAltitude = aPlatform.RelativeAltitudeOf(aTarget);
|
|
|
|
#double relativeAltitudeMod = aPlatform.RelativeAltitudeOf(aTarget) * 2;
|
|
#// mach 3 = 1 020.87 meters / second
|
|
#double closingSpeedMod = ((closingSpeed / 2 + 1520) / 1520);
|
|
#double effectiveRange = (groundRange + (relativeAltitudeMod)) / closingSpeedMod;
|
|
#writeln_d(" ", effectiveRange, " (effectiveRange) == ", groundRange, " ((groundRange) + ", relativeAltitudeMod, " (relativeAltitudeMod)) / ", closingSpeedMod, " (speedThrustMod), closingSpeed: ", closingSpeed);
|
|
|
|
double effectiveRange = (groundRange + relativeAltitude) + closingSpeed * 15; //look ahead 15 seconds
|
|
return effectiveRange;
|
|
end_script
|
|
|
|
#######
|
|
script double TargetAspect (WsfPlatform aPlatform, WsfTrack aTarget)
|
|
WsfGeoPoint platformLoc = aPlatform.Location();
|
|
WsfGeoPoint targetLoc = aTarget.CurrentLocation(); #extrapolated to current time
|
|
|
|
# all calculataions are in WCS
|
|
double dLosX = targetLoc.X() - platformLoc.X();
|
|
double dLosY = targetLoc.Y() - platformLoc.Y();
|
|
double dLosZ = targetLoc.Z() - platformLoc.Z();
|
|
double dLosMag = MATH.Sqrt( dLosX*dLosX + dLosY*dLosY + dLosZ*dLosZ );
|
|
|
|
dLosX = dLosX/dLosMag;
|
|
dLosY = dLosY/dLosMag;
|
|
dLosZ = dLosZ/dLosMag;
|
|
|
|
double dTgtVx = aTarget.Vx();
|
|
double dTgtVy = aTarget.Vy();
|
|
double dTgtVz = aTarget.Vz();
|
|
double dTgtVmag = MATH.Sqrt( dTgtVx*dTgtVx + dTgtVy*dTgtVy + dTgtVz*dTgtVz );
|
|
|
|
dTgtVx = dTgtVx/dTgtVmag;
|
|
dTgtVy = dTgtVy/dTgtVmag;
|
|
dTgtVz = dTgtVz/dTgtVmag;
|
|
|
|
double dotProd = dLosX*dTgtVx + dLosY*dTgtVy + dLosZ*dTgtVz;
|
|
|
|
return MATH.ACos(dotProd);
|
|
end_script
|
|
|
|
#######
|
|
script double GetMaxLaunchRange (WsfPlatform aPlatform,
|
|
WsfTrack aTarget,
|
|
string aWeaponName,
|
|
Array<Map<string, Object>> aWeaponArray) #double aMaxTOF, double aMaxFlyoutRange)
|
|
|
|
double dSlantRange = aPlatform.SlantRangeTo(aTarget);
|
|
double dTgtSpeed = aTarget.Speed();
|
|
double dTgtAspectAngle = TargetAspect( aPlatform, aTarget);
|
|
double dMaxTOF = 0;
|
|
double dMaxFlyoutRange = 0;
|
|
double dMaxLaunchRange = 0;
|
|
|
|
foreach (Map<string, Object> curWeapon in aWeaponArray)
|
|
{
|
|
if ( (string)curWeapon["name"] == aWeaponName )
|
|
{
|
|
if (curWeapon.Exists("maxFlyoutRange") &&
|
|
curWeapon.Exists("maxTimeOfFlight"))
|
|
{
|
|
dMaxFlyoutRange = (double)curWeapon["maxFlyoutRange"];
|
|
dMaxTOF = (double)curWeapon["maxTimeOfFlight"];
|
|
}
|
|
else
|
|
{
|
|
dMaxFlyoutRange = (double)curWeapon.Get("rangeMax");
|
|
dMaxTOF = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
dMaxLaunchRange = dMaxFlyoutRange;
|
|
if ( dTgtSpeed > 5*MATH.MPS_PER_NMPH() )
|
|
{
|
|
dMaxLaunchRange = dMaxFlyoutRange - dTgtSpeed * MATH.Cos(dTgtAspectAngle) * dMaxTOF;
|
|
}
|
|
|
|
#writeln_d("$$$$ ", dSlantRange*(1/MATH.M_PER_NM()), ", ", dTgtAspectAngle ", ", dEffectiveRange*(1/MATH.M_PER_NM()));
|
|
|
|
#double effectiveRange = (groundRange + (relativeAltitudeMod)) / closingSpeedMod;
|
|
|
|
#writeln_d(" ", effectiveRange, " (effectiveRange) == ", groundRange, " ((groundRange) + ", relativeAltitudeMod, " (relativeAltitudeMod)) / ", closingSpeedMod, " (speedThrustMod), closingSpeed: ", closingSpeed);
|
|
|
|
return dMaxLaunchRange;
|
|
end_script
|
|
|
|
/*
|
|
* bool LaunchWeapon ()
|
|
*
|
|
* Launch a missile at the tracked target.
|
|
*/
|
|
|
|
script bool LaunchWeapon (WsfPlatform aPlatform, WsfTrack aTarget, WsfWeapon aWeapon, int salvo)
|
|
bool launched = false;
|
|
|
|
if (!aTarget.IsValid() || !aWeapon.IsValid())
|
|
{
|
|
writeln_d("!!! LaunchWeapon(): Invalid aWeapon or aTarget");
|
|
return launched;
|
|
}
|
|
|
|
// We can select the weapon only if it exists, has enough rounds and is not already busy launching
|
|
// (i.e.: it doesn't currently have a task assigned to it...)
|
|
if (aWeapon.IsTurnedOn() && (aWeapon.QuantityRemaining() >= 1 ))
|
|
{
|
|
writeln_d(" Attempting launch at ", aTarget.TargetName());
|
|
launched = aWeapon.FireSalvo(aTarget, salvo);
|
|
}
|
|
|
|
return launched;
|
|
end_script
|
|
|
|
|
|
script int GetWeaponNumberActiveMax( string weaponName, Array<Map<string, Object>> aWeaponArray )
|
|
foreach (Map<string, Object> curWeapon in aWeaponArray)
|
|
{
|
|
string curWeaponName = (string)curWeapon["name"];
|
|
#writeln_d("curWeapon[name] = ", curWeaponName, ". searching for: ", weaponName);
|
|
if( curWeaponName == weaponName )
|
|
{
|
|
#writeln_d("searched for weapon ", weaponName, ". found ", curWeaponName);
|
|
if( curWeapon.Exists("numActiveMax") )
|
|
{
|
|
return (int)curWeapon["numActiveMax"];
|
|
}
|
|
}
|
|
}
|
|
return 1;
|
|
end_script
|
|
|
|
|
|
script string GetTargetDomain(WsfTrack aTarget)
|
|
|
|
if (aTarget.AirDomain())
|
|
{
|
|
return "AIR";
|
|
}
|
|
if (aTarget.LandDomain())
|
|
{
|
|
return "LAND";
|
|
}
|
|
if (aTarget.SurfaceDomain())
|
|
{
|
|
return "SURFACE";
|
|
}
|
|
if (aTarget.SubsurfaceDomain())
|
|
{
|
|
return "SUBSURFACE";
|
|
}
|
|
if (aTarget.SpaceDomain())
|
|
{
|
|
return "SPACE";
|
|
}
|
|
if ( (aTarget.LocationValid() && aTarget.Altitude() <= 500.0) || aTarget.Speed() <= 50.0 )
|
|
{
|
|
return "LAND";
|
|
}
|
|
if ((aTarget.LocationValid() && aTarget.Altitude() > 500.0) || aTarget.Speed() > 50.0 )
|
|
{
|
|
return "AIR";
|
|
}
|
|
return "UNKNOWN";
|
|
|
|
end_script
|
|
|
|
|
|
|
|
// return true or false depending on if the given weapon can be fired on the
|
|
//given track (which represents a target of a particular domain)
|
|
script bool IsWeaponDomainCapable(WsfTrack aTarget, Map<string, Object> aWeaponMap)
|
|
string domain = GetTargetDomain(aTarget);
|
|
if (domain!="UNKNOWN")
|
|
{
|
|
if (domain=="AIR")
|
|
{
|
|
//AIR is a special case that defaults to true, when it doens't exist
|
|
if ((!aWeaponMap.Exists(domain)) || ((int)aWeaponMap[domain] == 1))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else // LAND, SPACE, SURFACE, SUBSURFACE
|
|
{
|
|
if (aWeaponMap.Exists(domain) && ((int)aWeaponMap[domain] == 1))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
//else, no domains matched
|
|
return false;
|
|
end_script
|
|
|
|
|
|
|
|
|
|
// return the name of the first weapon from aWeaponArray that can hit aTarget
|
|
script string GetBestWeapon(WsfPlatform aPlatform,
|
|
WsfTrack aTarget,
|
|
Array<Map<string, Object>> aWeaponArray,
|
|
double aLaunchRangeMaxFraction)
|
|
|
|
double desiredRange = EffectiveRange(aPlatform, aTarget);
|
|
|
|
#writeln_d(" effective range from ", aPlatform.Name(), " to ", aTarget.TargetName(), " = ", desiredRange);
|
|
|
|
foreach (Map<string, Object> curWeapon in aWeaponArray)
|
|
{
|
|
if (((WsfWeapon)curWeapon["weapon"]).QuantityRemaining() <= 0)
|
|
{
|
|
continue;
|
|
}
|
|
if (!IsWeaponDomainCapable(aTarget,curWeapon))
|
|
{
|
|
continue;
|
|
}
|
|
if ((double)curWeapon["rangeMin"] > desiredRange)
|
|
{
|
|
continue;
|
|
}
|
|
if ((double)curWeapon["rangeMax"] * aLaunchRangeMaxFraction < desiredRange)
|
|
{
|
|
continue;
|
|
}
|
|
#writeln_d(" returning best weapon: ", (string)curWeapon["name"]);
|
|
return (string)curWeapon["name"];
|
|
}
|
|
return "";
|
|
end_script
|
|
|
|
// return the range of the longest-ranged weapon with ammo remaining
|
|
script double GetWeaponRangeMax(WsfPlatform aPlatform, Array<Map<string, Object>> aWeaponArray)
|
|
double rangeMax = 0;
|
|
|
|
foreach (Map<string, Object> curWeapon in aWeaponArray)
|
|
{
|
|
if (((WsfWeapon)curWeapon["weapon"]).QuantityRemaining() <= 0)
|
|
{
|
|
continue;
|
|
}
|
|
if ((double)curWeapon["rangeMax"] > rangeMax)
|
|
{
|
|
rangeMax = (double)curWeapon["rangeMax"];
|
|
}
|
|
}
|
|
return rangeMax;
|
|
end_script
|
|
|
|
// return the range of the weapon with smallest min range and with ammo remaining
|
|
script double GetWeaponRangeMin(WsfPlatform aPlatform, Array<Map<string, Object>> aWeaponArray)
|
|
double rangeMin = 99999999999.0;
|
|
|
|
foreach (Map<string, Object> curWeapon in aWeaponArray)
|
|
{
|
|
if (((WsfWeapon)curWeapon["weapon"]).QuantityRemaining() <= 0)
|
|
{
|
|
continue;
|
|
}
|
|
if ((double)curWeapon["rangeMin"] < rangeMin)
|
|
{
|
|
rangeMin = (double)curWeapon["rangeMin"];
|
|
}
|
|
}
|
|
return rangeMin;
|
|
end_script
|
|
|
|
// return the amount of ammo remaining for the weapon with the most ammo
|
|
script double GetWeaponQuantityRemainingMax(WsfPlatform aPlatform, Array<Map<string, Object>> aWeaponArray)
|
|
double quant = 0.0;
|
|
|
|
foreach (Map<string, Object> curWeapon in aWeaponArray)
|
|
{
|
|
if (((WsfWeapon)curWeapon["weapon"]).QuantityRemaining() > quant)
|
|
{
|
|
quant = ((WsfWeapon)curWeapon["weapon"]).QuantityRemaining();
|
|
}
|
|
}
|
|
return quant;
|
|
end_script
|
|
|
|
script int GetBucket (double x, double aBucketBase)
|
|
if (x == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
double abs_x = MATH.Fabs(x);
|
|
|
|
// convert the log base-10 to a log with the base
|
|
int temp = MATH.Log10(abs_x) / MATH.Log10(aBucketBase);
|
|
|
|
// return a negative bucket if our source number is negative
|
|
if (x > 0)
|
|
{
|
|
return temp;
|
|
}
|
|
else
|
|
{
|
|
return -temp;
|
|
}
|
|
end_script
|
|
|
|
script WsfTrack GetTrackByName(WsfPlatform aPlatform, string trackName)
|
|
WsfLocalTrackList trackList = aPlatform.MasterTrackList();
|
|
foreach (WsfTrack track in trackList)
|
|
{
|
|
if (track.TargetName() == trackName)
|
|
{
|
|
return track;
|
|
}
|
|
}
|
|
WsfTrack x; // since we can't return null
|
|
return x;
|
|
end_script
|
|
|
|
script WsfTrack GetTrackById(WsfPlatform aPlatform, WsfTrackId trackId)
|
|
WsfLocalTrackList trackList = aPlatform.MasterTrackList();
|
|
|
|
foreach (WsfTrack track in trackList)
|
|
{
|
|
if (track.TrackId() == trackId )
|
|
{
|
|
return track;
|
|
}
|
|
}
|
|
|
|
// since we can't return null
|
|
WsfTrack x;
|
|
return x;
|
|
end_script
|
|
|
|
|
|
// Calculate the relative positioning of aPlatform and aTrack
|
|
script string CalculatePositioning (WsfPlatform aPlatform, WsfTrack aTrack, double aAngleTolerance)
|
|
// Are we heading the same direction?
|
|
bool sameHeading = false;
|
|
bool oppHeading = false;
|
|
bool oppHeadingValid = aTrack.HeadingValid();
|
|
double headingDiff = MATH.Fabs(MATH.NormalizeAngleMinus180_180(aPlatform.Heading() - aTrack.Heading()));
|
|
|
|
if (oppHeadingValid && headingDiff < aAngleTolerance)
|
|
{
|
|
sameHeading = true;
|
|
}
|
|
if (oppHeadingValid && headingDiff > (180 - aAngleTolerance))
|
|
{
|
|
oppHeading = true;
|
|
}
|
|
|
|
// Is either one of us pointing at the other?
|
|
// Apparently bearing is always valid (?)
|
|
bool pointingMeYou = false;
|
|
bool pointingYouMe = false;
|
|
|
|
double pMeYou = MATH.Fabs(aPlatform.RelativeBearingTo(aTrack));
|
|
double pYouMe = MATH.Fabs(aTrack.RelativeBearingTo(aPlatform));
|
|
|
|
if (pMeYou < aAngleTolerance)
|
|
{
|
|
pointingMeYou = true;
|
|
}
|
|
if (pYouMe < aAngleTolerance)
|
|
{
|
|
pointingYouMe = true;
|
|
}
|
|
|
|
// Put them together and we've got relative positioning
|
|
string positioning = "";
|
|
if (sameHeading && pointingMeYou)
|
|
{
|
|
positioning = "head-to-tail";
|
|
}
|
|
else if (sameHeading && pointingYouMe)
|
|
{
|
|
positioning = "tail-to-head";
|
|
}
|
|
else if (oppHeading && (pointingMeYou || pointingYouMe))
|
|
{
|
|
positioning = "head-to-head";
|
|
}
|
|
else if (pointingMeYou && pointingYouMe)
|
|
{
|
|
positioning = "head-to-head";
|
|
}
|
|
else if (oppHeading)
|
|
{
|
|
positioning = "tail-to-tail";
|
|
}
|
|
else if (pointingMeYou)
|
|
{
|
|
positioning = "me-facing-target";
|
|
}
|
|
else if (pointingYouMe)
|
|
{
|
|
positioning = "target-facing-me";
|
|
}
|
|
else
|
|
{
|
|
positioning = "none";
|
|
}
|
|
|
|
// Debug, in case my angle math is bad
|
|
// writeln_d(" ", aPlatform.Name(), " to ", aTrack.TargetName(), ", headingDiff: ", headingDiff, ", pMeYou: ", pMeYou, ", pYouMe: ", pYouMe);
|
|
// writeln_d(" meHeading: ", aPlatform.Heading(), ", youHeading: ", aTrack.Heading());
|
|
// writeln_d(" sameHeading: ", sameHeading, ", oppHeading: ", oppHeading,
|
|
// ", pointingMeYou: ", pointingMeYou, ", pointingYouMe: ", pointingYouMe,
|
|
// ", positioning: ", positioning);
|
|
|
|
return positioning;
|
|
end_script
|
|
|
|
// Calculate a heading to evade all incoming missiles, weighted by distance, and then turn aPlatform to it
|
|
script bool EvadeIncoming(WsfPlatform aPlatform, WsfRIPRProcessor aProc, Array<WsfPlatform> aIncoming, double originalAltitude, double diveAltitude, double weightPeers)
|
|
double evadeHeading = 0;
|
|
double evadeDivisor = 0;
|
|
double distMod = 0;
|
|
double bearingMod = 0;
|
|
int incomingCount = aIncoming.Size();
|
|
|
|
writeln_d(aPlatform.Name(), " evade. dive/climb between altitudes: ", originalAltitude, " <-> ", diveAltitude);
|
|
writeln_d("evading ", incomingCount, " threats");
|
|
|
|
double bankAngle = 40; //degrees
|
|
if (!diveDownMap.Exists(aPlatform.Name()))
|
|
{
|
|
diveDownMap[aPlatform.Name()] = true;
|
|
}
|
|
if (!bankLeftMap.Exists(aPlatform.Name()))
|
|
{
|
|
bankLeftMap[aPlatform.Name()] = true;
|
|
}
|
|
|
|
#extern Map<string, Array<WsfPlatform>> mEveryonesIncomingThreatsMap;
|
|
#if ( !mEveryonesIncomingThreatsMap.Empty() )
|
|
#{
|
|
# if ( mEveryonesIncomingThreatsMap.Exists(aPlatform.Name()) )
|
|
# {
|
|
# incoming = mEveryonesIncomingThreatsMap.Get(aPlatform.Name());
|
|
# if ( !incoming.Empty() )
|
|
# {
|
|
# incomingCount = incoming.Size();
|
|
# }
|
|
# }
|
|
#}
|
|
|
|
if (incomingCount == 0)
|
|
{
|
|
writeln_d(" no incoming weapons");
|
|
return false;
|
|
}
|
|
|
|
double y = 0;
|
|
double x = 0;
|
|
// calculate an average heading to incoming platforms weighted by distance
|
|
for (int i = 0; i < incomingCount; i = i + 1)
|
|
{
|
|
WsfPlatform temp = (WsfPlatform)(aIncoming[i]);
|
|
if (!temp.IsValid() || (temp.Index() == aPlatform.Index()))
|
|
{
|
|
continue;
|
|
}
|
|
writeln_d("calculating danger from: ", temp.Name());
|
|
double range = aPlatform.SlantRangeTo(temp);
|
|
if (range > 0)
|
|
{
|
|
distMod = 1 / range;
|
|
}
|
|
else
|
|
{
|
|
distMod = 1000000;
|
|
}
|
|
bearingMod = MATH.NormalizeAngle0_360(aPlatform.TrueBearingTo(temp));
|
|
x = x + MATH.Sin(bearingMod) * distMod;
|
|
y = y + MATH.Cos(bearingMod) * distMod;
|
|
|
|
writeln_d(" Incoming ", temp.Name(), " at distance: ", 1 / distMod, ", bearing: ", bearingMod, ", x: ", MATH.Sin(bearingMod), ", y: ", MATH.Cos(bearingMod), ", (", temp.Latitude(), ", ", temp.Longitude(), ", ", temp.Altitude(), ") @ ", temp.Speed(), "m/s");
|
|
evadeDivisor = evadeDivisor + distMod;
|
|
evadeHeading = evadeHeading + bearingMod * distMod;
|
|
}
|
|
|
|
// get the GCI if there is one
|
|
WsfRIPRProcessor aiflProc = aProc.GetRIPRCommanderProcessor();
|
|
WsfRIPRProcessor gciProc = null;
|
|
WsfPlatform aiflPlat;// = null;
|
|
WsfPlatform gciPlat;// = null;
|
|
|
|
|
|
if (aiflProc.IsValid())
|
|
{
|
|
gciProc = aiflProc.GetRIPRCommanderProcessor();
|
|
aiflPlat = aiflProc.Platform();
|
|
}
|
|
else
|
|
{
|
|
//writeln_d("!!! aiflProc not valid");
|
|
}
|
|
|
|
if (gciProc.IsValid())
|
|
{
|
|
gciPlat = gciProc.Platform();
|
|
}
|
|
else
|
|
{
|
|
//writeln_d("!!! gciProc not valid");
|
|
}
|
|
|
|
// if there's a gci, avoid centroids of all his subs
|
|
if (gciPlat.IsValid())
|
|
{
|
|
foreach (WsfPlatform aifl in gciPlat.Subordinates())
|
|
{
|
|
if (!aifl.IsValid() || aifl == aPlatform || aifl == aiflPlat)
|
|
{
|
|
continue;
|
|
}
|
|
WsfGeoPoint centroid = aifl.GetSubsCentroid();
|
|
|
|
if (centroid.X() == 0 && centroid.Y() == 0 && centroid.Z() == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//writeln_d("!!! centroid: (", centroid.X(), ", ", centroid.Y(), ", ", centroid.Z(), "), dist: ", (aPlatform.SlantRangeTo(centroid)));
|
|
distMod = 1 / (aPlatform.SlantRangeTo(centroid));
|
|
bearingMod = MATH.NormalizeAngle0_360(aPlatform.TrueBearingTo(centroid));
|
|
x = x + MATH.Sin(bearingMod) * distMod * weightPeers;
|
|
y = y + MATH.Cos(bearingMod) * distMod * weightPeers;
|
|
|
|
writeln_d(" AIFL ", aifl.Name(), " at distance: ", 1 / distMod, ", bearing: ", bearingMod, ", x: ", MATH.Sin(bearingMod), ", y: ", MATH.Cos(bearingMod), ", (", centroid.Latitude(), ", ", centroid.Longitude(), ", ", centroid.Altitude(), ")");
|
|
|
|
// but we'll avoid peers with less strength than we avoid incoming missiles
|
|
// so let's divide the distMod by 2
|
|
evadeDivisor = evadeDivisor + distMod;
|
|
evadeHeading = evadeHeading + bearingMod * distMod;
|
|
}
|
|
}
|
|
// otherwise, avoid members in your own squadron
|
|
else if (aiflPlat.IsValid())
|
|
{
|
|
foreach (WsfPlatform sub in aiflPlat.Subordinates())
|
|
{
|
|
if (!sub.IsValid() || sub == aPlatform)
|
|
{
|
|
continue;
|
|
}
|
|
distMod = 1 / (aPlatform.SlantRangeTo(sub));
|
|
bearingMod = MATH.NormalizeAngle0_360(aPlatform.TrueBearingTo(sub));
|
|
x = x + MATH.Sin(bearingMod) * distMod * weightPeers;
|
|
y = y + MATH.Cos(bearingMod) * distMod * weightPeers;
|
|
|
|
writeln_d(" Peer ", sub.Name(), " at distance: ", 1 / distMod, ", bearing: ", bearingMod, ", x: ", MATH.Sin(bearingMod), ", y: ", MATH.Cos(bearingMod), ", (", sub.Latitude(), ", ", sub.Longitude(), ", ", sub.Altitude(), ") @ ", sub.Speed(), "m/s");
|
|
|
|
// but we'll avoid peers with less strength than we avoid incoming missiles
|
|
// so let's divide the distMod by 2
|
|
evadeDivisor = evadeDivisor + distMod;
|
|
evadeHeading = evadeHeading + bearingMod * distMod;
|
|
}
|
|
}
|
|
|
|
if (evadeDivisor == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// writeln_d(" evadeHeading: ", evadeHeading, ", evadeDivisor: ", evadeDivisor, ", eH/eD: ", evadeHeading / evadeDivisor, ", x: ", x, ", y: ", y);
|
|
// evadeHeading = evadeHeading / evadeDivisor;
|
|
// evadeHeading = MATH.NormalizeAngle0_360(evadeHeading - 180);
|
|
|
|
// correct for quadrant
|
|
double theta = MATH.NormalizeAngle0_360(MATH.ATan(x/y));
|
|
if (y < 0)
|
|
{
|
|
theta = MATH.NormalizeAngle0_360(theta - 180);
|
|
}
|
|
|
|
writeln_d(" x: ", x, ", y: ", y, ", theta: ", theta);
|
|
|
|
//turn to angle to evade
|
|
if (bankLeftMap[aPlatform.Name()] == true)
|
|
{
|
|
evadeHeading = MATH.NormalizeAngle0_360(theta - 180 - bankAngle);
|
|
double headDiff = MATH.NormalizeAngleMinus180_180( evadeHeading - MATH.NormalizeAngle0_360(aPlatform.Heading()) );
|
|
if (MATH.Fabs(headDiff) < 2.0)
|
|
{
|
|
bankLeftMap[aPlatform.Name()] = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
evadeHeading = MATH.NormalizeAngle0_360(theta - 180 + bankAngle);
|
|
double headDiff = MATH.NormalizeAngleMinus180_180(evadeHeading - MATH.NormalizeAngle0_360(aPlatform.Heading()));
|
|
if (MATH.Fabs(headDiff) < 2.0)
|
|
{
|
|
bankLeftMap[aPlatform.Name()] = true;
|
|
}
|
|
}
|
|
|
|
writeln_d(" Evading incoming at heading ", evadeHeading);
|
|
aPlatform.TurnToHeading(evadeHeading);
|
|
|
|
//do the dive or climb now
|
|
if (diveDownMap[aPlatform.Name()] == true)
|
|
{
|
|
writeln_d(aPlatform.Name(), " diving to ", diveAltitude);
|
|
aPlatform.GoToAltitude(diveAltitude, 200, true);
|
|
if (aPlatform.Altitude() <= (originalAltitude - 0.95*(originalAltitude-diveAltitude)))
|
|
{
|
|
diveDownMap[aPlatform.Name()] = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
writeln_d(aPlatform.Name(), " climbing to ", originalAltitude);
|
|
aPlatform.GoToAltitude(originalAltitude, 200, true);
|
|
if (aPlatform.Altitude() >= (diveAltitude + 0.95*(originalAltitude-diveAltitude)))
|
|
{
|
|
diveDownMap[aPlatform.Name()] = true;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
end_script
|
|
|
|
script bool UpdatePointInterceptLocation (WsfPlatform aPlatform, WsfGeoPoint aPoint)
|
|
double interceptAltitude = aPoint.Altitude();
|
|
double interceptHeading = aPlatform.TrueBearingTo(aPoint);
|
|
double interceptRange = aPlatform.SlantRangeTo(aPoint);
|
|
|
|
writeln_d(" T=", TIME_NOW, ", range: ", interceptRange, " true-bearing: ", interceptHeading);
|
|
|
|
aPlatform.TurnToHeading(interceptHeading);
|
|
|
|
if (MATH.Fabs(interceptAltitude - aPlatform.Altitude()) > 5)
|
|
{
|
|
writeln_d("GoToAltitude: ",interceptAltitude);
|
|
aPlatform.GoToAltitude(interceptAltitude, 200);
|
|
}
|
|
return true;
|
|
end_script
|
|
|
|
script bool UpdatePointInterceptLocationWithPathing(WsfPlatform aPlatform, WsfGeoPoint aPoint)
|
|
double interceptAltitude = aPoint.Altitude();
|
|
double interceptHeading = aPlatform.TrueBearingTo(aPoint);
|
|
double interceptRange = aPlatform.SlantRangeTo(aPoint);
|
|
|
|
writeln_d(" T=", TIME_NOW, ", range: ", interceptRange, " true-bearing: ", interceptHeading);
|
|
|
|
WsfGeoPoint startGeoPoint = aPlatform.Location();
|
|
aPlatform.FindAndSetPath(startGeoPoint, aPoint);
|
|
|
|
if (MATH.Fabs (interceptAltitude - aPlatform.Altitude()) > 5)
|
|
{
|
|
writeln_d("GoToAltitude: ",interceptAltitude);
|
|
aPlatform.GoToAltitude(interceptAltitude, 200);
|
|
}
|
|
return true;
|
|
end_script
|
|
|
|
// Attempt to maximize our distance from aTarget at the time of weapon detonation
|
|
script double MaximizeFPole(WsfPlatform aPlatform, WsfTrack aTarget, double aFPoleAngle)
|
|
writeln_d(" f-pole angle ", aFPoleAngle, " on: ", aTarget.TargetName()); //Try this
|
|
double allowedError = 5.0; // allow for this many degrees of error
|
|
|
|
// Normalize within +/-180 so absolute value calculations make sense
|
|
double relativeBearing = aPlatform.RelativeBearingTo(aTarget);
|
|
//writeln_d("--- relative bearing: ", relativeBearing);
|
|
|
|
// lean in the direction we're closest to already satisfying
|
|
double desiredBearing = aFPoleAngle;
|
|
if (relativeBearing > 0)
|
|
{
|
|
desiredBearing = desiredBearing * (double) -1.0;
|
|
}
|
|
//writeln_d("--- desired bearing: ", desiredBearing);
|
|
|
|
// calculate the difference between our desired and current bearings
|
|
double currentError = MATH.Fabs(MATH.NormalizeAngleMinus180_180(desiredBearing - relativeBearing));
|
|
//writeln_d("--- current error: ", currentError);
|
|
|
|
// if we're within the error tolerance, do nothing
|
|
if (currentError <= allowedError)
|
|
{
|
|
return aPlatform.Heading();
|
|
}
|
|
|
|
// otherwise, normalize to 0-360 and calculate the change in bearing required
|
|
double fPoleHeading = MATH.NormalizeAngle0_360(aPlatform.TrueBearingTo(aTarget) + desiredBearing);
|
|
//writeln_d("--- f-pole heading: ", fPoleHeading);
|
|
|
|
// do the turn
|
|
return fPoleHeading;
|
|
// aPlatform.TurnToHeading(fPoleHeading);
|
|
end_script
|
|
|
|
// test if the platform has enough fuel to travel the given distance
|
|
script bool HasEnoughFuelToTravel(WsfPlatform aPlatform,
|
|
double aDistance)
|
|
bool result = true;
|
|
WsfFuel fuelObj = aPlatform.Fuel();
|
|
if (fuelObj.IsValid())
|
|
{
|
|
double fuelRemaining = fuelObj.QuantityRemaining();
|
|
double bingoQuantity = fuelObj.BingoQuantity();
|
|
double distQuantity = fuelObj.QuantityRequired(aDistance);
|
|
|
|
//writeln("HasEnoughFuelToTravel(", aPlatform.Name(), ",", aDistance," m) - fuelRemaining=", fuelRemaining, " | distQuantity=", distQuantity, " | bingoQuantity=", bingoQuantity);
|
|
if ((fuelRemaining - distQuantity) < bingoQuantity)
|
|
{
|
|
result = false;
|
|
}
|
|
}
|
|
return result;
|
|
end_script
|
|
|
|
// test if the platform has enough fuel to travel the given route
|
|
script bool HasEnoughFuelToTravelRoute(WsfPlatform aPlatform,
|
|
WsfRoute aRoute)
|
|
bool result = false;
|
|
|
|
WsfFuel fuelObj = aPlatform.Fuel();
|
|
|
|
double fuelRemaining = fuelObj.QuantityRemaining();
|
|
double bingoQuantity = fuelObj.BingoQuantity();
|
|
double distQuantity = fuelObj.QuantityRequired(aRoute);
|
|
|
|
//writeln("HasEnoughFuelToTravelRoute(",aPlatform.Name(),") - fuelRemaining=", fuelRemaining, " | distQuantity=", distQuantity, " | bingoQuantity=", bingoQuantity);
|
|
if ((fuelRemaining - distQuantity) > bingoQuantity)
|
|
{
|
|
result = true;
|
|
}
|
|
|
|
return result;
|
|
end_script
|
|
|
|
|
|
|
|
script bool ReEnterRoute(WsfPlatform aPlatform)
|
|
#only command the platform to do something different if its not currently flying a route
|
|
WsfMover aMover = aPlatform.Mover();
|
|
if (aMover.IsValid())
|
|
{
|
|
if (aMover.IsExtrapolating())
|
|
{
|
|
WsfGeoPoint pt = aPlatform.Location();
|
|
#WsfRoute ro = aPlatform.Route().Copy(); #now we have a modifiable route
|
|
WsfRoute ro = aMover.DefaultRoute().Copy(); #now we have a modifiable route
|
|
if (!ro.IsValid())
|
|
{
|
|
return false;
|
|
}
|
|
WsfGeoPoint close = ro.LocationAtDistance(ro.DistanceAlongRoute(pt));
|
|
if (!close.IsValid())
|
|
{
|
|
return false;
|
|
}
|
|
close.SetAltitudeAGL(pt.Altitude());
|
|
double d1 = ro.DistanceFromRoute(pt);
|
|
double d2 = pt.GroundRangeTo(close);
|
|
double d3 = -1;
|
|
Array<double> turnRad = aMover.PropertyDouble("turn_radius");
|
|
if (turnRad.Size() > 0)
|
|
{
|
|
d3 = 2*turnRad[0];
|
|
}
|
|
for (int i = 0; i < ro.Size(); i = i+1)
|
|
{
|
|
WsfWaypoint wpt = ro.Waypoint(i);
|
|
WsfGeoPoint rpt = wpt.Location();
|
|
|
|
//first check if we are close enough (half mile)
|
|
if (rpt.GroundRangeTo(close) < 926)
|
|
{
|
|
return aPlatform.FollowRoute(ro, i);
|
|
}
|
|
|
|
double dist = ro.DistanceAlongRoute(rpt);
|
|
if (dist > d1)
|
|
{
|
|
if (d2 > d3)
|
|
{
|
|
ro.Insert(i, WsfWaypoint.Create(close, wpt.Speed()));
|
|
}
|
|
return aPlatform.FollowRoute(ro, i);
|
|
}
|
|
}
|
|
return aPlatform.FollowRoute(ro, (ro.Size()-1));
|
|
}
|
|
}
|
|
return true;
|
|
end_script
|
|
|
|
|
|
|
|
|
|
end_script_interface
|