2008 lines
74 KiB
Plaintext
2008 lines
74 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.
|
|
# ****************************************************************************
|
|
|
|
|
|
#always requires a commander
|
|
#bids on "pursue-target" and "pursue-point" jobs
|
|
#performs "pursue-target" and "pursue-point" jobs
|
|
#does not create any jobs
|
|
|
|
|
|
include_once processors/ripr_agents/common/common_platform_script.txt
|
|
|
|
|
|
processor AIAI-thinker WSF_AIAI_PROCESSOR
|
|
|
|
update_interval 2.0 sec
|
|
|
|
#script_debug_writes on
|
|
script_debug_writes off
|
|
|
|
script_variables
|
|
|
|
//radar control
|
|
bool mTurnOnFiringRadarInRange = false;
|
|
string mFiringRadarName = "firing_radar"; // replace with sensor name on platform
|
|
double mFiringRadarRange = 1852 * 50; // 50 nautical miles (units: meters)
|
|
|
|
bool mTurnOnFiringRadarIfRWR = false;
|
|
string mRWR_RadarType = "ESM_RADAR"; // replace with sensor name on platform
|
|
|
|
//agent constants
|
|
double cLAUNCH_TQ = 0.49;
|
|
double cFAST_UPDATE_RATE = 1.0;
|
|
double cSLOW_UPDATE_RATE = 3.0;
|
|
double cTHREAT_TOLERANCE = 0;
|
|
double cTHREAT_GONE = 0;
|
|
double cMAX_DIST = MATH.DOUBLE_MAX();
|
|
double cBUCKET_BASE = 1.2;
|
|
double cWAIT_SPEED = 200; # m/s
|
|
double cINTERCEPT_SPEED = 1000; # m/s
|
|
###double cDEFAULT_ALTITUDE = 12049; # approximately 40,000 feet
|
|
double cDEFAULT_ALTITUDE = 9144; # approximately 30,000 feet
|
|
double cCLOSING_SPEED_MIN = -100;
|
|
double cCLOSING_SPEED_MAX = 1000;
|
|
double cMIN_JOB_BID = -MATH.DOUBLE_MAX();
|
|
###int cMAX_ACTIVE_WEAPONS = 4;
|
|
int cMAX_ACTIVE_WEAPONS = 1;
|
|
|
|
int cAirSalvoCount = 1;
|
|
int cGndSalvoCount = 1;
|
|
|
|
//type of mover
|
|
bool mUsePathFinder = false;
|
|
|
|
bool mUseMoverDuringPursuit = true; #set to false to force aiai to follow route during pursuit of target
|
|
|
|
|
|
int cROUTE_WAYPOINT_INDEX = -1; # use negative for "CLOSEST_POINT"
|
|
|
|
// input weights
|
|
double cWEIGHT_BEING_TARGETED = 100.0;
|
|
double cWEIGHT_CURRENT_TARGET = 100.0;
|
|
double cWEIGHT_CLOSING_SPEED_OF = 1.0;
|
|
double cWEIGHT_SLANT_RANGE_TO = -3.0;
|
|
double cWEIGHT_OTHERS_TARGETING = 0.0;
|
|
double cWEIGHT_WEAPONS_IN_FLIGHT = 50.0;
|
|
double cWEIGHT_WEAPONS_FIRED_AT = 0.0;
|
|
#double cWEIGHT_ANY_WEAPONS_ACTIVE = -50000;
|
|
double cWEIGHT_ANY_WEAPONS_ACTIVE = 100;
|
|
|
|
double cBASE_SLANT_RANGE_CONSTANT = 600000 * MATH.Fabs(cWEIGHT_SLANT_RANGE_TO); #over 300 nm away
|
|
double cBASE_CLOSING_SPEED_CONSTANT = 1050 * MATH.Fabs(cWEIGHT_CLOSING_SPEED_OF); #over 2000 kts closing speed
|
|
|
|
string mFireControlRadarCategory = "FIRE_CONTROL";
|
|
|
|
//include types or names
|
|
Array<string> mTargetTrackerTaskManagerStrings = Array<string>();
|
|
mTargetTrackerTaskManagerStrings[0] = "WSF_TASK_MANAGER";
|
|
mTargetTrackerTaskManagerStrings[1] = "RED_SAM_RADAR_TASK_MANAGER";
|
|
mTargetTrackerTaskManagerStrings[2] = "task_manager";
|
|
mTargetTrackerTaskManagerStrings[3] = "task_mgr";
|
|
|
|
Array<string> mMustShootAtCategories = Array<string>();
|
|
mMustShootAtCategories[0] = "sam";
|
|
mMustShootAtCategories[1] = "FIRE_CONTROL";
|
|
|
|
|
|
###double mEngagementAggressiveness = 0.5; // value between 0 and 1. 1 being suicidal kamikaze, and 0 being cautious pansy
|
|
double mEngagementAggressiveness = 0.7; // value between 0 and 1. 1 being suicidal kamikaze, and 0 being cautious pansy
|
|
|
|
bool mCoopEngageOne = false;
|
|
bool mEscortFollowing = false;
|
|
double mGoodFormationRatio = 0.15; #percentage of total offset
|
|
double mFormationLookAhead = 30.0; #seconds
|
|
bool mLastInPosition = false; #boolean flag
|
|
double mFormationPositionX = 0; #meters in front of of package
|
|
double mFormationPositionY = 0; #meters off right wing of package
|
|
|
|
## double escortProtectDistance = 125.0 * MATH.M_PER_NM(); #should try to stay within this range of the escort package
|
|
double escortProtectDistance = 100.0 * MATH.M_PER_NM(); #engage threats within this range of the escort package
|
|
double escortChaseDistance = 15.0 * MATH.M_PER_NM(); #allowed to chase a threat this far out of protect area if it entered
|
|
bool escortAddMissileProtectRange = false;
|
|
|
|
string mEscortName = "";
|
|
Array<string> mEscortNames = Array<string>();
|
|
|
|
Map<string, double> ThreatTypePriority = Map<string, double>();
|
|
|
|
# the interceptor uses these threat priority pairs to
|
|
# determine who is the most important threat of concern
|
|
# category string threat priority
|
|
ThreatTypePriority["unknown"] = 0.0;
|
|
ThreatTypePriority["uav"] = 2.0;
|
|
ThreatTypePriority["sam"] = 4.0;
|
|
ThreatTypePriority["ship"] = 4.0;
|
|
ThreatTypePriority["awacs"] = -500.0;
|
|
ThreatTypePriority["bomber"] = 2.0;
|
|
ThreatTypePriority["jammer"] = 0.0;
|
|
ThreatTypePriority["fighter"] = 50000.0;
|
|
ThreatTypePriority["missile"] = 9.0;
|
|
ThreatTypePriority["missile_fast"] = 9.0; // try new category for cmd
|
|
## let the strike aircraft set these individually
|
|
#ThreatTypePriority["strike_target"] = 100.0;
|
|
#ThreatTypePriority["primary_target"] = 100.0;
|
|
#ThreatTypePriority["secondary_target"] = 50.0;
|
|
|
|
#ThreatTypePriority["2D_RADAR"] = 0.0;
|
|
#ThreatTypePriority["3D_RADAR"] = 0.0;
|
|
#ThreatTypePriority["ACQUISITION"] = 0.0;
|
|
#ThreatTypePriority["HF_RADAR"] = 0.0;
|
|
|
|
//fly different offset angles, depending on the type or category of threat
|
|
Map<string, double> ThreatTypeOffsetAngle = Map<string, double>();
|
|
ThreatTypeOffsetAngle["unknown"] = 45.0;
|
|
ThreatTypeOffsetAngle["uav"] = 15.0;
|
|
ThreatTypeOffsetAngle["sam"] = 50.0;
|
|
ThreatTypeOffsetAngle["ship"] = 45.0;
|
|
ThreatTypeOffsetAngle["awacs"] = 15.0;
|
|
ThreatTypeOffsetAngle["bomber"] = 25.0;
|
|
ThreatTypeOffsetAngle["jammer"] = 50.0;
|
|
ThreatTypeOffsetAngle["fighter"] = 35.0;
|
|
ThreatTypeOffsetAngle["missile"] = 20.0;
|
|
ThreatTypeOffsetAngle["missile_fast"] = 15.0; // try new category for cmd
|
|
|
|
|
|
//fire off different kinds of salvos at different types of threats
|
|
Map<string, int> ThreatTypeSalvo = Map<string, int>();
|
|
ThreatTypeSalvo["unknown"] = 1;
|
|
ThreatTypeSalvo["uav"] = 1;
|
|
ThreatTypeSalvo["sam"] = 1;
|
|
ThreatTypeSalvo["ship"] = 1;
|
|
ThreatTypeSalvo["awacs"] = 1;
|
|
ThreatTypeSalvo["bomber"] = 1;
|
|
ThreatTypeSalvo["jammer"] = 1;
|
|
ThreatTypeSalvo["fighter"] = 1;
|
|
ThreatTypeSalvo["missile"] = 1;
|
|
ThreatTypeSalvo["missile_fast"] = 1; // try new category for cmd
|
|
ThreatTypeSalvo["FIRE_CONTROL"] = 2;
|
|
|
|
|
|
//require different track qualities to fire on different kinds of threats
|
|
Map<string, double> ThreatTypeRequiredTrackQuality = Map<string, double>();
|
|
ThreatTypeRequiredTrackQuality["unknown"] = 0.49;
|
|
ThreatTypeRequiredTrackQuality["uav"] = 0.49;
|
|
ThreatTypeRequiredTrackQuality["sam"] = 0.49;
|
|
ThreatTypeRequiredTrackQuality["ship"] = 0.49;
|
|
ThreatTypeRequiredTrackQuality["awacs"] = 0.49;
|
|
ThreatTypeRequiredTrackQuality["bomber"] = 0.49;
|
|
ThreatTypeRequiredTrackQuality["jammer"] = 0.49;
|
|
ThreatTypeRequiredTrackQuality["fighter"] = 0.49;
|
|
ThreatTypeRequiredTrackQuality["missile"] = 0.49;
|
|
ThreatTypeRequiredTrackQuality["missile_fast"] = 0.49; // try new category for cmd
|
|
ThreatTypeRequiredTrackQuality["FIRE_CONTROL"] = 0.49;
|
|
|
|
|
|
|
|
//fly different offset angles, depending on the type or category of threat
|
|
Map<string, string> ThreatTypeWeapon = Map<string, string>();
|
|
ThreatTypeWeapon["unknown"] = "";
|
|
ThreatTypeWeapon["uav"] = "";
|
|
ThreatTypeWeapon["sam"] = "";
|
|
ThreatTypeWeapon["ship"] = "";
|
|
ThreatTypeWeapon["awacs"] = "";
|
|
ThreatTypeWeapon["bomber"] = "";
|
|
ThreatTypeWeapon["jammer"] = "";
|
|
ThreatTypeWeapon["fighter"] = "";
|
|
ThreatTypeWeapon["missile"] = "";
|
|
ThreatTypeWeapon["missile_fast"] = ""; // try new category for cmd
|
|
#ThreatTypeWeapon["FIRE_CONTROL"] = "";
|
|
|
|
double mMaxFiringAngle = 45.0; // this should be missile specific
|
|
|
|
double mDegradedFiringAngle = 55.0; //negative if not valid
|
|
double mDegradedPercentRange = 0.50; //additional range constraint on launch if past degraded firing angle
|
|
|
|
Map<string, double> WeaponTypeMaxFiringAngle = Map<string, double>();
|
|
WeaponTypeMaxFiringAngle["sdb"] = 45.0; // Need target specific option
|
|
WeaponTypeMaxFiringAngle["srm"] = 90.0; // Need target specific option
|
|
WeaponTypeMaxFiringAngle["mrm"] = 60.0; // Need target specific option
|
|
WeaponTypeMaxFiringAngle["lrm"] = 180.0; // Need target specific option
|
|
|
|
double mLaunchPercentRangeMax = 0.80; // don't launch until X fraction of Rmax
|
|
|
|
Map<string, double> ThreatTypeLaunchPercentRangeMax = Map<string, double>();
|
|
ThreatTypeLaunchPercentRangeMax["sam"] = 0.95;
|
|
ThreatTypeLaunchPercentRangeMax["FIRE_CONTROL"] = 0.95;
|
|
ThreatTypeLaunchPercentRangeMax["ship"] = 0.95;
|
|
ThreatTypeLaunchPercentRangeMax["unknown"] = 0.80;
|
|
ThreatTypeLaunchPercentRangeMax["uav"] = 0.80;
|
|
ThreatTypeLaunchPercentRangeMax["awacs"] = 0.80;
|
|
ThreatTypeLaunchPercentRangeMax["bomber"] = 0.80;
|
|
ThreatTypeLaunchPercentRangeMax["jammer"] = 0.80;
|
|
ThreatTypeLaunchPercentRangeMax["fighter"] = 0.80;
|
|
ThreatTypeLaunchPercentRangeMax["missile"] = 0.80;
|
|
ThreatTypeLaunchPercentRangeMax["missile_fast"] = 0.80; // try new category for cmd
|
|
|
|
//use this offset angle if a category specific offset isn't found
|
|
double mFPoleAngle = 45.0; // this should be radar-specific
|
|
|
|
// hacks
|
|
double weightPeersForEvade = 0.25; //percentage to scale peers influence for evasion vector
|
|
double mAltitudeMax = 20000; // meters, hack
|
|
double mAltitudeMin = 1000;
|
|
# double mAltitudeToDiveEvade = 10000; //distance to dive (meters)
|
|
double mAltitudeToDiveEvade = 5000; //distance to dive (try this instead)
|
|
|
|
double mAltitudeBeforeEvade = -1.0; //utility variable (don't adjust)
|
|
double mSafeAltitudeToDiveTo = 1000; //utility variable (don't adjust)
|
|
|
|
double mLastWeaponFiredTime = 0.0;
|
|
double mLaunchWeaponDelay = 3.0;
|
|
double mLastActiveWeaponTime = 0.0;
|
|
#double mInactiveLaunchDelay = 3.0; // Wait 3 seconds after your last missile detonated.
|
|
double mInactiveLaunchDelay = 1.0; // Wait 3 seconds after your last missile detonated.
|
|
int mPreviousJobId = -1;
|
|
|
|
|
|
#double mLastTimeSoarCalled = 0.0;
|
|
|
|
|
|
Array<Map<string, Object>> mWeaponArray = Array<Map<string, Object>>();
|
|
|
|
//example weapon input block for a ripr aiai platform:
|
|
//
|
|
// weapon blue_lr_a2a_rf_missile quantity 8 end_weapon
|
|
//
|
|
// processor int-thinker AIAI-thinker
|
|
// script_variables
|
|
// mWeaponArray[0] = Map<string, Object>();
|
|
// mWeaponArray[0].Set("name", "blue_lr_a2a_rf_missile");
|
|
// mWeaponArray[0].Set("weapon", PLATFORM.Weapon("blue_lr_a2a_rf_missile"));
|
|
// mWeaponArray[0].Set("rangeMin", 1000);
|
|
// mWeaponArray[0].Set("rangeMax", 111120); // ~60 miles
|
|
// mWeaponArray[0].Set("numActiveMax", 1); // how many weapons of this type can be in play simultaneously
|
|
// mWeaponArray[0].Set("AIR", 1); // DOMAIN CAPABLE, yes, this weapon can hit air (default true)
|
|
// mWeaponArray[0].Set("LAND", 0); // DOMAIN CAPABLE, no, this weapon can NOT hit land (default false)
|
|
// end_script_variables
|
|
// end_processor
|
|
|
|
|
|
// Inputs to RIPR
|
|
double ownshipSpeed = 0;
|
|
double ownshipAmmo = 0;
|
|
double ownshipEngagementRangeMin = 0; // set later GetWeaponRangeMin(PLATFORM, mWeaponArray);
|
|
double ownshipEngagementRangeMax = 0; // set later GetWeaponRangeMax(PLATFORM, mWeaponArray);
|
|
int ownshipWeaponsIncoming = 0;
|
|
|
|
Array<WsfPlatform> mIncoming = Array<WsfPlatform>();
|
|
string mState = "";
|
|
string mRouteType = "";
|
|
string mPursuitMode = "";
|
|
string mOldCommandComment = "no command";
|
|
string mOldTOOComment = "Targets of opportunity: ";
|
|
WsfRIPRJob mCurrentJob;
|
|
WsfGeoPoint mCurrentPointIntercept;
|
|
|
|
Map<string,double> mTrackWeaponActiveMap = Map<string,double>();
|
|
Map<string,bool> mTrackMinRangeOkMap = Map<string,bool>();
|
|
|
|
Array<string> mZoneNames = Array<string>(); #dlc
|
|
Map<WsfTrack, double> mRoundsFiredAtMap = Map<WsfTrack, double>(); #useful for bombs, that don't show up as active weapons
|
|
#must keep track of number fired
|
|
|
|
end_script_variables
|
|
|
|
|
|
script bool MustShootAt(WsfTrack track)
|
|
WsfPlatform plat = WsfSimulation.FindPlatform( track.TargetName() );
|
|
if (plat.IsValid())
|
|
{
|
|
foreach( string aCategory in mMustShootAtCategories )
|
|
{
|
|
if( plat.CategoryMemberOf( aCategory ) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
end_script
|
|
|
|
|
|
script int GetSalvoForThreat(WsfTrack track)
|
|
|
|
#extern string DetermineTrackCategory(WsfTrack);
|
|
#string category = DetermineTrackCategory(track);
|
|
#writeln_d("checking salvo size for category: ", category);
|
|
|
|
#WsfPlatform plat = WsfSimulation.FindPlatform( track.TargetIndex() );
|
|
WsfPlatform plat = WsfSimulation.FindPlatform( track.TargetName() );
|
|
if (plat.IsValid())
|
|
{
|
|
foreach( string aCategory : int salvo in ThreatTypeSalvo )
|
|
{
|
|
if( plat.CategoryMemberOf( aCategory ) )
|
|
{
|
|
|
|
writeln_d("salvo for type ", aCategory, " = ", salvo);
|
|
|
|
return salvo;
|
|
}
|
|
}
|
|
}
|
|
#extern string GetTargetDomain(WsfTrack);
|
|
string sTargetDomain = GetTargetDomain(track);
|
|
if ( (sTargetDomain == "LAND") || (sTargetDomain == "SURFACE") )
|
|
{
|
|
return cGndSalvoCount;
|
|
}
|
|
return cAirSalvoCount;
|
|
end_script
|
|
|
|
script string GetWeaponForThreat(WsfPlatform platform, WsfTrack track, Array<Map<string, Object>> weapons, double percent)
|
|
bool checkName = false;
|
|
string name = "";
|
|
#extern bool IsWeaponDomainCapable(WsfTrack, Map<string, Object>);
|
|
|
|
WsfPlatform plat = WsfSimulation.FindPlatform( track.TargetName() );
|
|
if (plat.IsValid())
|
|
{
|
|
foreach( string aCategory : string wpnStr in ThreatTypeWeapon )
|
|
{
|
|
|
|
#writeln_d("checking if ", plat.Name(), " is category: ", aCategory);
|
|
if( wpnStr.Length()>0 && plat.CategoryMemberOf( aCategory ) )
|
|
{
|
|
checkName = true;
|
|
name = wpnStr;
|
|
writeln_d(PLATFORM.Name(), " searching only for weapon: ", name, " (for use against ", plat.Name(), ")");
|
|
writeln_d("weapon for type ", aCategory, " = ", wpnStr);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
writeln_d("platform for target ", track.TargetName()," is not valid!");
|
|
}
|
|
|
|
#extern double EffectiveRange(WsfPlatform, WsfTrack);
|
|
double desiredRange = EffectiveRange(platform, track);
|
|
#writeln_d(" effective range from ", platform.Name(), " to ", aTarget.TargetName(), " = ", desiredRange);
|
|
foreach (Map<string, Object> curWeapon in weapons)
|
|
{
|
|
if (checkName && ((string)(curWeapon["name"]))!=name)
|
|
{
|
|
continue;
|
|
}
|
|
if (((WsfWeapon)curWeapon["weapon"]).QuantityRemaining() <= 0)
|
|
{
|
|
continue;
|
|
}
|
|
if (!IsWeaponDomainCapable(track,curWeapon))
|
|
{
|
|
continue;
|
|
}
|
|
if ((double)curWeapon["rangeMin"] > desiredRange)
|
|
{
|
|
continue;
|
|
}
|
|
if ((double)curWeapon["rangeMax"] * percent < desiredRange)
|
|
{
|
|
continue;
|
|
}
|
|
#writeln_d(" returning best weapon: ", (string)curWeapon["name"]);
|
|
return (string)curWeapon["name"];
|
|
}
|
|
return "";
|
|
end_script
|
|
|
|
|
|
|
|
script double GetOffsetAngleOnThreat(WsfTrack threat)
|
|
#extern string DetermineTrackCategory(WsfTrack);
|
|
#string category = DetermineTrackCategory(threat);
|
|
#if (ThreatTypeOffsetAngle.Exists(category))
|
|
#{
|
|
# return ThreatTypeOffsetAngle[category];
|
|
#}
|
|
|
|
#WsfPlatform plat = WsfSimulation.FindPlatform( threat.TargetIndex() );
|
|
WsfPlatform plat = WsfSimulation.FindPlatform( threat.TargetName() );
|
|
if (plat.IsValid())
|
|
{
|
|
foreach( string aCategory : double angle in ThreatTypeOffsetAngle )
|
|
{
|
|
if( plat.CategoryMemberOf( aCategory ) )
|
|
{
|
|
writeln_d("offset angle for type ", aCategory, " = ", angle);
|
|
return angle;
|
|
}
|
|
}
|
|
}
|
|
return mFPoleAngle;
|
|
end_script
|
|
|
|
|
|
script double GetRequiredTrackQualityForThreat(WsfTrack threat)
|
|
#extern string DetermineTrackCategory(WsfTrack);
|
|
#string category = DetermineTrackCategory(threat);
|
|
|
|
writeln_d("checking required TQ for track: ", threat.TargetName());
|
|
|
|
#WsfPlatform plat = WsfSimulation.FindPlatform( threat.TargetIndex() );
|
|
WsfPlatform plat = WsfSimulation.FindPlatform( threat.TargetName() );
|
|
if (plat.IsValid())
|
|
{
|
|
foreach( string aCategory : double quality in ThreatTypeRequiredTrackQuality )
|
|
{
|
|
if( plat.CategoryMemberOf( aCategory ) )
|
|
{
|
|
writeln_d("TQ for type ", aCategory, " = ", quality);
|
|
return quality;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if (ThreatTypeRequiredTrackQuality.Exists(category))
|
|
#{
|
|
# return ThreatTypeRequiredTrackQuality[category];
|
|
#}
|
|
return cLAUNCH_TQ;
|
|
|
|
end_script
|
|
|
|
// currently only considers weapon in firing angle; needs to determine firing angle for weapon/target pairing
|
|
script double GetMaxFiringAngleForWeapon(WsfWeapon weapon)
|
|
if (weapon.IsValid())
|
|
{
|
|
foreach( string aName : double angle in WeaponTypeMaxFiringAngle )
|
|
{
|
|
if( weapon.Name() == aName )
|
|
{
|
|
writeln_d("firing angle for weapon/target pairing ", aName, " = ", angle);
|
|
return angle;
|
|
}
|
|
}
|
|
}
|
|
return mMaxFiringAngle;
|
|
end_script
|
|
|
|
|
|
script double GetLaunchPercentRangeMaxOnThreat(WsfTrack threat)
|
|
WsfPlatform plat = WsfSimulation.FindPlatform( threat.TargetName() );
|
|
if (plat.IsValid())
|
|
{
|
|
foreach( string aCategory : double percent in ThreatTypeLaunchPercentRangeMax )
|
|
{
|
|
if( plat.CategoryMemberOf( aCategory ) )
|
|
{
|
|
return percent;
|
|
}
|
|
}
|
|
}
|
|
return mLaunchPercentRangeMax;
|
|
end_script
|
|
|
|
|
|
|
|
script WsfProcessor GetPlatformsTaskManager(WsfPlatform aPlat)
|
|
for (int i=0; i < aPlat.ProcessorCount(); i=i+1)
|
|
{
|
|
WsfProcessor proc = aPlat.ProcessorEntry(i);
|
|
|
|
foreach (string procString in mTargetTrackerTaskManagerStrings)
|
|
{
|
|
if (proc.Type() == procString || proc.Name() == procString)
|
|
{
|
|
return proc;
|
|
}
|
|
}
|
|
}
|
|
end_script
|
|
|
|
|
|
script WsfGeoPoint GetFormationPoint( WsfPlatform reference, double xOff, double yOff )
|
|
WsfGeoPoint formationPoint = reference.Location();
|
|
formationPoint.Offset(reference.Heading(), xOff, yOff, 0);
|
|
return formationPoint;
|
|
end_script
|
|
|
|
script bool AmInFormationPosition(WsfGeoPoint formationPoint, double radius)
|
|
//make it fly to the inner 50% of the acceptable radius on re-entry
|
|
if (!mLastInPosition)
|
|
{
|
|
radius = 0.5 * radius;
|
|
}
|
|
mLastInPosition = false;
|
|
double range = PLATFORM.GroundRangeTo(formationPoint);
|
|
if (range <= radius)
|
|
{
|
|
mLastInPosition = true;
|
|
}
|
|
return mLastInPosition;
|
|
end_script
|
|
|
|
script bool FlyEscortFormation()
|
|
if (!mEscortFollowing)
|
|
{
|
|
return false;
|
|
}
|
|
//make sure escorted platform exists, find it, save it
|
|
WsfPlatform escortPlatform;
|
|
#if ( !mEscortNames.Empty() ) #{
|
|
foreach ( string sEscortName in mEscortNames )
|
|
{
|
|
escortPlatform = WsfSimulation.FindPlatform(sEscortName);
|
|
if (escortPlatform.IsValid() )
|
|
{
|
|
mEscortName = sEscortName;
|
|
writeln_d("--- fighter ", PLATFORM.Name(), " is escorting ", mEscortName);
|
|
break;
|
|
}
|
|
} #}
|
|
|
|
if ( escortPlatform.IsValid() )
|
|
{
|
|
//check if I am approximately in my formation position
|
|
WsfGeoPoint formationPoint = GetFormationPoint( escortPlatform, mFormationPositionX, mFormationPositionY );
|
|
if (!formationPoint.IsValid())
|
|
{
|
|
writeln_d("--- fighter ", PLATFORM.Name(), " invalid formation point from ", escortPlatform.Name());
|
|
return false;
|
|
}
|
|
double radius = mGoodFormationRatio * escortPlatform.SlantRangeTo(formationPoint);
|
|
|
|
bool bAlt = true;
|
|
if (escortPlatform.Altitude() > PLATFORM.Altitude())
|
|
{
|
|
bAlt = PLATFORM.GoToAltitude ( escortPlatform.Altitude(), 200 );
|
|
}
|
|
if (AmInFormationPosition(formationPoint, radius))
|
|
{
|
|
//match speed and heading of escorted platform
|
|
# bool GoToAltitude (double aAlt, double aAltRateOfChange);
|
|
# bool GoToSpeed (double aSpeed, double aLinearAccel );
|
|
# bool TurnToHeading(double aHeading, double aRadialAccel );
|
|
bool bVel = PLATFORM.GoToSpeed ( escortPlatform.Speed() );
|
|
bool bYaw = PLATFORM.TurnToHeading( escortPlatform.Heading() );
|
|
#PLATFORM.Comment(write_str("formation: alt=", bAlt, " vel=", bVel, " yaw=",bYaw));
|
|
}
|
|
else
|
|
{
|
|
//fly towards formation position, extrapolated ahead X seconds
|
|
double lookAheadDistance = mFormationLookAhead * escortPlatform.Speed();
|
|
formationPoint.Offset(escortPlatform.Heading(), lookAheadDistance, 0, 0);
|
|
double myDistanceToExtrap = PLATFORM.GroundRangeTo(formationPoint);
|
|
double mySpeedToExtrap = myDistanceToExtrap / mFormationLookAhead;
|
|
|
|
#bool bVel = PLATFORM.GoToSpeed(cINTERCEPT_SPEED);
|
|
bool bVel = PLATFORM.GoToSpeed(mySpeedToExtrap);
|
|
bool bYaw = PLATFORM.TurnToHeading( PLATFORM.TrueBearingTo(formationPoint) );
|
|
#PLATFORM.Comment(write_str("re-enter: vel=", bVel, " yaw=",bYaw));
|
|
}
|
|
return true;
|
|
}
|
|
writeln_d("--- fighter ", PLATFORM.Name(), " no valid platform to escort!");
|
|
return false;
|
|
end_script
|
|
|
|
|
|
#######
|
|
script bool RejoinEscort()
|
|
bool bEscortRejoined = false;
|
|
if ( !mEscortFollowing || (mEscortName != "") )
|
|
{
|
|
WsfPlatform escortPlatform = WsfSimulation.FindPlatform(mEscortName);
|
|
if ( escortPlatform.IsValid() )
|
|
{
|
|
WsfGeoPoint joinGeoPoint0 = PLATFORM.Location();
|
|
WsfGeoPoint joinGeoPoint1 = escortPlatform.Location();
|
|
double dDeltaTime = PROCESSOR.UpdateInterval();
|
|
double dRejoinSpeed = escortPlatform.Speed();
|
|
double dRejoinSpeedFast = dRejoinSpeed*1.2;
|
|
double dRejoinSpeedSlow = dRejoinSpeed*0.8;
|
|
double dRejoinHeadingTolerance = 15;
|
|
double dRejoinTimeTolerance = 15;
|
|
|
|
#writeln_d("$$$1 ", TIME_NOW, " ", PLATFORM.Name() , " ", mFormationPositionX, " ", mFormationPositionY);
|
|
joinGeoPoint1.Offset(escortPlatform.Heading(), mFormationPositionX, mFormationPositionY, 0);
|
|
|
|
double dRejoinDistance = joinGeoPoint0.SlantRangeTo(joinGeoPoint1);
|
|
double dRejoinTime = dRejoinDistance/dRejoinSpeed;
|
|
double dRejoinDownrange = PLATFORM.DownRangeTo(joinGeoPoint1);
|
|
double dHeadingDiff = MATH.Fabs(PLATFORM.Heading()-escortPlatform.Heading());
|
|
|
|
#if (dRejoinDownrange > 0)
|
|
#{
|
|
if ( dRejoinTime > (dDeltaTime + dRejoinTimeTolerance) )
|
|
{
|
|
dRejoinSpeed = Math.Min(dRejoinSpeedFast,cINTERCEPT_SPEED);
|
|
}
|
|
else if ( dRejoinTime < dDeltaTime )
|
|
{
|
|
joinGeoPoint1.Extrapolate(escortPlatform.Heading(), (dDeltaTime - dRejoinTime)*dRejoinSpeed);
|
|
dRejoinSpeed = dRejoinSpeedSlow;
|
|
}
|
|
#}
|
|
#else
|
|
#{
|
|
# writeln_d("$$ $$ $$1");
|
|
# dRejoinSpeed = dRejoinSpeedSlow;
|
|
# if ( (dHeadingDiff < dRejoinHeadingTolerance) && (dRejoinDistance < 10*MATH.M_PER_NM()) )
|
|
# {
|
|
# joinGeoPoint1 = joinGeoPoint0;
|
|
# joinGeoPoint1.Extrapolate(escortPlatform.Heading(), (dDeltaTime*1.01)*dRejoinSpeed);
|
|
# writeln_d("$$ $$ $$2");
|
|
# }
|
|
#}
|
|
#writeln_d("$$$2 ", dRejoinSpeed*MATH.NMPH_PER_MPS());
|
|
|
|
#For some reason the GoToLocation and GoToSpeed commands never work so I resort to making a route...
|
|
WsfRoute joinRoute = WsfRoute().Create("joinRouteName");
|
|
WsfWaypoint joinWaypoint1 = WsfWaypoint().Create(joinGeoPoint1.Latitude(), joinGeoPoint1.Longitude(), cDEFAULT_ALTITUDE, dRejoinSpeed, "LATITUDE_AND_LONGITUDE", "EXTRAPOLATE");
|
|
joinRoute.Append(joinWaypoint1);
|
|
PLATFORM.FollowRoute(joinRoute);
|
|
|
|
bEscortRejoined = true;
|
|
}
|
|
}
|
|
return bEscortRejoined;
|
|
end_script
|
|
|
|
|
|
script bool UpdatePointInterceptLocation (WsfPlatform aPlatform, WsfGeoPoint aPoint)
|
|
if (!aPoint.IsValid())
|
|
{
|
|
PLATFORM.Comment("ignoring invalid point");
|
|
return false;
|
|
}
|
|
double interceptAltitude = aPoint.Altitude();
|
|
|
|
if (interceptAltitude < 0)
|
|
{
|
|
PLATFORM.Comment("ignoring invalid point");
|
|
return false;
|
|
}
|
|
|
|
double interceptRange = aPlatform.SlantRangeTo(aPoint);
|
|
double interceptHeading = aPlatform.TrueBearingTo(aPoint);
|
|
aPlatform.TurnToHeading(interceptHeading);
|
|
|
|
writeln_d(" T=", TIME_NOW, ", range: ", interceptRange, " true-bearing: ", interceptHeading);
|
|
|
|
# #if ( (interceptAltitude - aPlatform.Altitude()) > 5)
|
|
# if ( (interceptAltitude - aPlatform.Altitude()) > 10000*MATH.M_PER_FT())
|
|
# {
|
|
# writeln_d("GoToAltitude: ",interceptAltitude);
|
|
# #aPlatform.GoToAltitude(interceptAltitude);
|
|
# }
|
|
|
|
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();
|
|
|
|
if (!aPlatform.FindAndSetPath(startGeoPoint, aPoint) && mRouteType != "DEFAULT")
|
|
{
|
|
if ( cROUTE_WAYPOINT_INDEX < 0)
|
|
{
|
|
PLATFORM.FollowRoute("DEFAULT_ROUTE","CLOSEST_POINT");
|
|
}
|
|
else
|
|
{
|
|
PLATFORM.FollowRoute("DEFAULT_ROUTE",cROUTE_WAYPOINT_INDEX);
|
|
}
|
|
PLATFORM.Comment("Route: DEFAULT");
|
|
mRouteType = "DEFAULT";
|
|
}
|
|
else if(mRouteType != "PATH")
|
|
{
|
|
PLATFORM.Comment("Route: PATH");
|
|
mRouteType = "PATH";
|
|
}
|
|
if ( (interceptAltitude - PLATFORM.Altitude()) > 10000*MATH.M_PER_FT())
|
|
#if ( (interceptAltitude - aPlatform.Altitude()) > 5)
|
|
{
|
|
writeln_d("GoToAltitude: ",interceptAltitude);
|
|
#aPlatform.GoToAltitude(interceptAltitude);
|
|
}
|
|
return true;
|
|
end_script
|
|
|
|
|
|
|
|
/*
|
|
* bool UpdateInterceptLocationWithPathing ()
|
|
*
|
|
* Intercept aTarget. This should eventually be replaced by methods in the mover.
|
|
*/
|
|
|
|
script bool UpdateInterceptLocationWithPathing (WsfTrack aTarget, string pursuitMode)
|
|
|
|
if (aTarget.IsNull())
|
|
{
|
|
writeln_d(" UpdateInterceptLocationWithPathing, aTarget is null");
|
|
return false;
|
|
}
|
|
|
|
// If we got the altitude from the TRACK, match it
|
|
double interceptAltitude = cDEFAULT_ALTITUDE;
|
|
//double interceptAltitude = PLATFORM.Altitude();
|
|
double interceptHeading = PLATFORM.Heading();
|
|
double distanceToTarget = PLATFORM.SlantRangeTo(aTarget);
|
|
|
|
double desiredSpeed = cINTERCEPT_SPEED;
|
|
if (aTarget.VelocityValid() && aTarget.AirDomain())
|
|
{
|
|
#extern double EffectiveRange(WsfPlatform, WsfTrack);
|
|
|
|
#extern double GetWeaponRangeMax (WsfPlatform, Array<Map<string, Object>>);
|
|
#extern double GetWeaponRangeMin (WsfPlatform, Array<Map<string, Object>>);
|
|
|
|
double rangeMin = GetWeaponRangeMin(PLATFORM, mWeaponArray);
|
|
double rangeMax = GetWeaponRangeMax(PLATFORM, mWeaponArray);
|
|
|
|
double speedOfTarget = aTarget.Speed();
|
|
double effRange = EffectiveRange(PLATFORM, aTarget);
|
|
double missileWindow = rangeMax - rangeMin;
|
|
double speedWindow = cINTERCEPT_SPEED - speedOfTarget;
|
|
|
|
|
|
if(effRange < rangeMax && effRange > rangeMin)
|
|
{
|
|
double rangeScale = (effRange - rangeMin) / missileWindow;
|
|
desiredSpeed = speedOfTarget + (speedWindow * rangeScale);
|
|
}
|
|
else if (effRange <= rangeMin)
|
|
{
|
|
desiredSpeed = speedOfTarget;
|
|
}
|
|
|
|
if (desiredSpeed < cWAIT_SPEED)
|
|
{
|
|
desiredSpeed = cWAIT_SPEED;
|
|
}
|
|
}
|
|
PLATFORM.GoToSpeed(desiredSpeed);
|
|
|
|
WsfGeoPoint startGeoPoint = PLATFORM.Location();
|
|
WsfGeoPoint endGeoPoint = aTarget.ReportedLocation();
|
|
|
|
if (pursuitMode == "lead")
|
|
{
|
|
double timeToIntercept = 0.0;
|
|
WsfWaypoint interceptPoint = WsfWaypoint();
|
|
|
|
timeToIntercept = PLATFORM.InterceptLocation3D(aTarget, interceptPoint);
|
|
// If timeToIntercept is positive then we know intercept is possible
|
|
if (timeToIntercept > 0.0)
|
|
{
|
|
writeln_d(" T=", TIME_NOW, " TTI=", timeToIntercept, " range: ", PLATFORM.SlantRangeTo(aTarget), " true-bearing: ", interceptPoint.Heading());
|
|
writeln_d(" lead pursuit: ", interceptPoint.Latitude(), ", ", interceptPoint.Longitude(), ", ", interceptPoint.Altitude());
|
|
|
|
startGeoPoint = PLATFORM.Location();
|
|
endGeoPoint = aTarget.ReportedLocation();
|
|
}
|
|
else
|
|
{
|
|
writeln_d(" lead pursuit attempt with timeToIntercept < 0");
|
|
}
|
|
}
|
|
else if (pursuitMode == "f-pole")
|
|
{
|
|
#extern double MaximizeFPole(WsfPlatform, WsfTrack, double);
|
|
interceptHeading = MaximizeFPole(PLATFORM, aTarget, GetOffsetAngleOnThreat(aTarget));
|
|
startGeoPoint = PLATFORM.Location();
|
|
endGeoPoint = PLATFORM.Location();
|
|
//f-pole towards a point 30 miles away in the correct direction
|
|
endGeoPoint.Extrapolate(interceptHeading, 55560);
|
|
|
|
writeln_d(" T=", TIME_NOW, ", range: ", PLATFORM.SlantRangeTo(aTarget), " true-bearing: ", PLATFORM.TrueBearingTo(aTarget));
|
|
writeln_d(" ", pursuitMode, " pursuit");
|
|
}
|
|
else
|
|
{
|
|
//otherwise... just fly at the target
|
|
startGeoPoint = PLATFORM.Location();
|
|
endGeoPoint = aTarget.ReportedLocation();
|
|
|
|
writeln_d(" T=", TIME_NOW, ", range: ", PLATFORM.SlantRangeTo(aTarget), " true-bearing: ", PLATFORM.TrueBearingTo(aTarget),
|
|
" (", endGeoPoint.Latitude(), "/", aTarget.Latitude(), ",", endGeoPoint.Longitude(), "/", aTarget.Longitude(), ")");
|
|
writeln_d(" ", pursuitMode, " pursuit");
|
|
}
|
|
|
|
//make sure we don't dive, staying above the target is fine
|
|
if (endGeoPoint.Altitude() < interceptAltitude)
|
|
{
|
|
endGeoPoint.Set(endGeoPoint.Latitude(), endGeoPoint.Longitude(), interceptAltitude);
|
|
}
|
|
|
|
if (!PLATFORM.FindAndSetPath(startGeoPoint, endGeoPoint) && mRouteType != "DEFAULT")
|
|
{
|
|
if ( cROUTE_WAYPOINT_INDEX < 0)
|
|
{
|
|
PLATFORM.FollowRoute("DEFAULT_ROUTE","CLOSEST_POINT");
|
|
}
|
|
else
|
|
{
|
|
PLATFORM.FollowRoute("DEFAULT_ROUTE",cROUTE_WAYPOINT_INDEX);
|
|
}
|
|
PLATFORM.Comment("Route: DEFAULT");
|
|
mRouteType = "DEFAULT";
|
|
}
|
|
else if(mRouteType != "PATH")
|
|
{
|
|
PLATFORM.Comment("Route: PATH");
|
|
mRouteType = "PATH";
|
|
}
|
|
return true;
|
|
end_script
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* bool UpdateInterceptLocation ()
|
|
*
|
|
* Intercept aTarget. This should eventually be replaced by methods in the mover.
|
|
*/
|
|
|
|
script bool UpdateInterceptLocation (WsfTrack aTarget, string pursuitMode)
|
|
|
|
if (aTarget.IsNull())
|
|
{
|
|
writeln_d(" UpdateInterceptLocation, aTarget is null");
|
|
return false;
|
|
}
|
|
|
|
// If we got the altitude from the TRACK, match it
|
|
double interceptAltitude = cDEFAULT_ALTITUDE;
|
|
//double interceptAltitude = PLATFORM.Altitude();
|
|
double interceptHeading = PLATFORM.Heading();
|
|
double distanceToTarget = PLATFORM.SlantRangeTo(aTarget);
|
|
|
|
if (aTarget.ElevationValid() || aTarget.LocationValid())
|
|
{
|
|
#writeln_d("dlc ", aTarget.Altitude()*MATH.FT_PER_M(), ", ", interceptAltitude*MATH.FT_PER_M());
|
|
if (aTarget.Altitude() > interceptAltitude)
|
|
{
|
|
interceptAltitude = aTarget.Altitude();
|
|
}
|
|
}
|
|
|
|
|
|
double desiredSpeed = cINTERCEPT_SPEED;
|
|
if (aTarget.VelocityValid() && aTarget.AirDomain())
|
|
{
|
|
#extern double EffectiveRange(WsfPlatform, WsfTrack);
|
|
|
|
#extern double GetWeaponRangeMax (WsfPlatform, Array<Map<string, Object>>);
|
|
#extern double GetWeaponRangeMin (WsfPlatform, Array<Map<string, Object>>);
|
|
|
|
double rangeMin = GetWeaponRangeMin(PLATFORM, mWeaponArray);
|
|
double rangeMax = GetWeaponRangeMax(PLATFORM, mWeaponArray);
|
|
|
|
double speedOfTarget = aTarget.Speed();
|
|
double effRange = EffectiveRange(PLATFORM, aTarget);
|
|
double missileWindow = rangeMax - rangeMin;
|
|
double speedWindow = cINTERCEPT_SPEED - speedOfTarget;
|
|
|
|
|
|
if(effRange < rangeMax && effRange > rangeMin)
|
|
{
|
|
double rangeScale = (effRange - rangeMin) / missileWindow;
|
|
desiredSpeed = speedOfTarget + (speedWindow * rangeScale);
|
|
}
|
|
else if (effRange <= rangeMin)
|
|
{
|
|
desiredSpeed = speedOfTarget;
|
|
}
|
|
|
|
if (desiredSpeed < cWAIT_SPEED)
|
|
{
|
|
desiredSpeed = cWAIT_SPEED;
|
|
}
|
|
}
|
|
PLATFORM.GoToSpeed(desiredSpeed);
|
|
|
|
if (pursuitMode == "lead")
|
|
{
|
|
double timeToIntercept = 0.0;
|
|
WsfWaypoint interceptPoint = WsfWaypoint();
|
|
|
|
timeToIntercept = PLATFORM.InterceptLocation3D(aTarget, interceptPoint);
|
|
// If timeToIntercept is positive then we know intercept is possible
|
|
if (timeToIntercept > 0.0)
|
|
{
|
|
writeln_d(" T=", TIME_NOW, " TTI=", timeToIntercept, " range: ", PLATFORM.SlantRangeTo(aTarget), " true-bearing: ", interceptPoint.Heading());
|
|
writeln_d(" lead pursuit: ", interceptPoint.Latitude(), ", ", interceptPoint.Longitude(), ", ", interceptPoint.Altitude());
|
|
|
|
interceptHeading = interceptPoint.Heading();
|
|
}
|
|
else
|
|
{
|
|
writeln_d(" lead pursuit attempt with timeToIntercept < 0, flying straight at target for now");
|
|
interceptHeading = PLATFORM.TrueBearingTo(aTarget);
|
|
}
|
|
}
|
|
else if (pursuitMode == "f-pole")
|
|
{
|
|
#extern double MaximizeFPole(WsfPlatform, WsfTrack, double);
|
|
interceptHeading = MaximizeFPole(PLATFORM, aTarget, GetOffsetAngleOnThreat(aTarget));
|
|
writeln_d(" T=", TIME_NOW, ", range: ", PLATFORM.SlantRangeTo(aTarget), " true-bearing: ", PLATFORM.TrueBearingTo(aTarget));
|
|
writeln_d(" ", pursuitMode, " pursuit");
|
|
}
|
|
else
|
|
{
|
|
interceptHeading = PLATFORM.TrueBearingTo(aTarget);
|
|
|
|
//writeln_d(" T=", TIME_NOW, ", range: ", PLATFORM.SlantRangeTo(aTarget), " true-bearing: ", PLATFORM.TrueBearingTo(aTarget),
|
|
// " (", endGeoPoint.Latitude(), "/", aTarget.Latitude(), ",", endGeoPoint.Longitude(), "/", aTarget.Longitude(), ")");
|
|
writeln_d(" ", pursuitMode, " pursuit");
|
|
}
|
|
|
|
//PLATFORM.Comment("USING TURNTOHEADING");
|
|
// Head towards the target using the interceptHeading instead of Pathfinder
|
|
PLATFORM.TurnToHeading(interceptHeading);
|
|
|
|
###if ( (interceptAltitude - PLATFORM.Altitude()) > 10000*MATH.M_PER_FT())
|
|
if ( (interceptAltitude - PLATFORM.Altitude()) > 100)
|
|
{
|
|
writeln_d("GoToAltitude: ",interceptAltitude);
|
|
PLATFORM.GoToAltitude(interceptAltitude, 200);
|
|
}
|
|
return true;
|
|
|
|
end_script
|
|
|
|
|
|
|
|
|
|
script string Think()
|
|
|
|
//always evade incoming weapons
|
|
//mIncoming = Array<WsfPlatform>(); // clear the existing incoming weapons
|
|
mIncoming.Clear();
|
|
|
|
### use darin's custom threat processor instead
|
|
###
|
|
###ownshipWeaponsIncoming = WeaponsIncoming(mIncoming);
|
|
###
|
|
|
|
|
|
# WsfProcessor proc = PLATFORM.Processor("incoming_threats");
|
|
# if (proc.IsValid())
|
|
# {
|
|
# writeln_d("using incoming_threats threat processor!!!");
|
|
# mIncoming = (Array<WsfPlatform>)(proc.Execute("ThreatProcessorWeaponsIncoming"));
|
|
# ownshipWeaponsIncoming = mIncoming.Size();
|
|
# }
|
|
# else
|
|
# {
|
|
# writeln_d("using standard WSF_THREAT_PROCESSOR!!!");
|
|
ownshipWeaponsIncoming = WeaponsIncoming(mIncoming);
|
|
# }
|
|
|
|
|
|
if (ownshipWeaponsIncoming > 0)
|
|
{
|
|
if (mState != "EVADE_INCOMING")
|
|
{
|
|
writeln_d("EVASION BLOCK : ", PLATFORM.Name(), " :");
|
|
foreach(WsfPlatform p in mIncoming)
|
|
{
|
|
string msg = write_str("Evading: ", p.Name());
|
|
PLATFORM.Comment(msg);
|
|
writeln_d(msg);
|
|
}
|
|
}
|
|
|
|
|
|
//we now have weapons incoming, check to see if we are currently guiding any missiles with an uplink
|
|
//basically: check if we have any active weapons
|
|
int WeaponCount = WeaponsActive();
|
|
if (WeaponCount > 0)
|
|
{
|
|
writeln_d(PLATFORM.Name(), " weapons incoming, checking weapons active, count: ", WeaponCount);
|
|
double threat = 1000.0;
|
|
double asset = 1000.0;
|
|
|
|
//find most threatening incoming weapon
|
|
foreach(WsfPlatform p in mIncoming)
|
|
{
|
|
double range = PLATFORM.SlantRangeTo(p); # meters
|
|
double speed = p.Speed(); # meters/sec
|
|
double time = range / speed; # seconds
|
|
if (time < threat)
|
|
{
|
|
threat = time;
|
|
}
|
|
}
|
|
|
|
//find most valuable launched weapon (most threatening to enemy)
|
|
for (int i=0; i< WeaponCount; i=i+1)
|
|
{
|
|
WsfPlatform w = ActiveWeaponPlatform(i);
|
|
WsfTrack t = w.CurrentTargetTrack();
|
|
if (t.IsValid())
|
|
{
|
|
double range = w.SlantRangeTo(t); # meters
|
|
double speed = w.Speed(); # meters/sec
|
|
double time = range / speed; # seconds
|
|
if (time < asset)
|
|
{
|
|
asset = time;
|
|
}
|
|
}
|
|
}
|
|
|
|
//factor in our aggressiveness, and decide whether or not we should evade yet
|
|
double requiredAggressiveness = threat / (threat + asset);
|
|
writeln_d(PLATFORM.Name(), " threat: ", threat, ", asset: ", asset, ", required aggressiveness: ", requiredAggressiveness);
|
|
if (mEngagementAggressiveness < requiredAggressiveness)
|
|
{
|
|
return "EVADE_INCOMING";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return "EVADE_INCOMING";
|
|
}
|
|
}
|
|
|
|
// if we're low on fuel, go home
|
|
if (PLATFORM.FuelRemaining() < PLATFORM.FuelBingoQuantity())
|
|
{
|
|
return "BINGO_FUEL";
|
|
}
|
|
|
|
// if no weapons are available to use, don't fight
|
|
# #extern double GetWeaponQuantityRemainingMax(WsfPlatform,Array<Map<string, Object>>);
|
|
# double wRemaining = GetWeaponQuantityRemainingMax(PLATFORM, mWeaponArray);
|
|
# if (wRemaining < 1)
|
|
# {
|
|
# writeln_d(" no weapons left, WAITing");
|
|
# return "WAIT";
|
|
# }
|
|
# else
|
|
# {
|
|
# writeln_d(" weapons left: ", wRemaining);
|
|
# }
|
|
|
|
#if (PLATFORM.WeaponEntry(0).QuantityRemaining() < 1)
|
|
#{
|
|
# return "WAIT";
|
|
#}
|
|
|
|
bool wait = true;
|
|
foreach( WsfPlatform sub in GetRIPRCommanderPlatform().Subordinates() )
|
|
{
|
|
if (sub.WeaponEntry(0).QuantityRemaining() > 0)
|
|
{
|
|
wait = false;
|
|
}
|
|
}
|
|
|
|
if (wait == true)
|
|
{
|
|
return "WAIT";
|
|
}
|
|
|
|
if (GetRIPRCommanderProcessor().IsBidWindowOpen())
|
|
{
|
|
writeln_d(" Jobs:");
|
|
|
|
Array<WsfRIPRJob> jobs = GetRIPRCommanderProcessor().GetJobs();
|
|
writeln_d(" (jobs fetched): ", jobs.Size());
|
|
|
|
// bid on jobs
|
|
foreach (WsfRIPRJob aJob in jobs)
|
|
{
|
|
double bidVal = QueryBid(aJob);
|
|
|
|
#writeln_d(PLATFORM.Name(), " bid val for ", aJob.GetDescription(), " = ", bidVal);
|
|
#writeln_d(aJob.Name(), " priority: ", aJob.GetPriority());
|
|
|
|
writeln_d(PLATFORM.Name(), " bid on job ", aJob.GetDescription(), " == ", bidVal, ", priority = ", aJob.GetPriority());
|
|
|
|
if (bidVal < 0)
|
|
{
|
|
#unbid all jobs
|
|
for (int channel = 0; channel < GetNumJobChannels(); channel = channel + 1)
|
|
{
|
|
aJob.UnbidJob( PROCESSOR, channel);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//bid normal value for primary job (channel zero)
|
|
aJob.BidJob( PROCESSOR, bidVal );
|
|
//writeln_d("Main Channel\'s bid value: ", bidVal);
|
|
|
|
for (int channel = 1; channel < GetNumJobChannels(); channel = channel + 1)
|
|
{
|
|
//scale down bid value to 25% of max for all other channels
|
|
// or to 250% of max if bid is negative
|
|
//basically, ensure that half the magnitude of the default channel can
|
|
//beat the full magnitude of the sub channels
|
|
double scaledBidValue = bidVal * 0.25;
|
|
if (bidVal < 0.0)
|
|
{
|
|
scaledBidValue = bidVal * 2.5;
|
|
}
|
|
double progress = 0.0;
|
|
aJob.BidJob( PROCESSOR, (int)channel, scaledBidValue, progress );
|
|
//writeln_d("Channel ", channel, "\'s bid value: ", scaledBidValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// win and execute primary job
|
|
if (GetRIPRCommanderProcessor().IsJobWindowOpen())
|
|
{
|
|
// now check the board to see what we've won!
|
|
mCurrentJob = GetRIPRCommanderProcessor().GetJobFor(TIME_NOW, PROCESSOR);
|
|
}
|
|
|
|
if (!(mCurrentJob.IsValid()))
|
|
{
|
|
writeln_d(" no valid job from job board, WAITing");
|
|
return "WAIT";
|
|
}
|
|
|
|
string commandComment = "Job: " + mCurrentJob.Name() + ", " + mCurrentJob.GetDescription(); // + " pursuit-mode: " + mPursuitMode;
|
|
writeln_d(" - ", commandComment);
|
|
|
|
if (commandComment != mOldCommandComment)
|
|
{
|
|
PLATFORM.Comment(commandComment);
|
|
mOldCommandComment = commandComment;
|
|
}
|
|
|
|
// Clear the variables we'll use to store the agent's output
|
|
ClearTarget();
|
|
|
|
if (mCurrentJob.Name() == "pursue-target")
|
|
{
|
|
#extern int GetBucket (double, double);
|
|
#extern WsfTrack GetTrackByName (WsfPlatform, string);
|
|
#extern bool TestTrackCategory (WsfTrack, string);
|
|
#extern string CalculatePositioning (WsfPlatform, WsfTrack, double);
|
|
#extern string DetermineTrackCategory(WsfTrack);
|
|
|
|
Map<string, Object> tempData = mCurrentJob.GetData();
|
|
string targetName = (string)tempData["targetTrackName"];
|
|
WsfTrack targetTrack = GetTrackByName(PLATFORM, targetName);
|
|
|
|
if (targetTrack.IsValid())
|
|
{
|
|
//writeln_d(" Setting target to ", targetName, ", type: ", DetermineTrackCategory(targetTrack));
|
|
SetTarget(targetName);
|
|
}
|
|
else
|
|
{
|
|
writeln_d(" No track found for target named ", targetName);
|
|
ClearTarget();
|
|
}
|
|
|
|
//get target from ripr processor, to be sure
|
|
targetTrack = GetTarget();
|
|
|
|
if (!targetTrack.IsValid() ||
|
|
#!targetTrack.IFF_Foe() || // Was commented out - see if this helps.
|
|
TestTrackCategory(targetTrack, "unknown"))
|
|
{
|
|
writeln_d(" No valid target set yet, go to WAIT mode.");
|
|
return "WAIT";
|
|
}
|
|
|
|
double ownSpeed = GetBucket(PLATFORM.Speed(), cBUCKET_BASE);
|
|
double engageRangeMin = GetBucket(ownshipEngagementRangeMin, cBUCKET_BASE);
|
|
double engageRangeMax = GetBucket(ownshipEngagementRangeMax, cBUCKET_BASE);
|
|
double slantRangeTo = GetBucket(PLATFORM.SlantRangeTo(targetTrack), cBUCKET_BASE);
|
|
double targetSpeed = GetBucket(targetTrack.Speed(), cBUCKET_BASE);
|
|
string positioning = CalculatePositioning(PLATFORM, targetTrack, GetOffsetAngleOnThreat(targetTrack));
|
|
int weaponsActive = WeaponsActive(targetTrack);
|
|
|
|
if (weaponsActive > 0)
|
|
{
|
|
mPursuitMode = "f-pole";
|
|
}
|
|
else if (targetTrack.AirDomain())
|
|
{
|
|
if (slantRangeTo >= engageRangeMax &&
|
|
positioning != "head-to-head" &&
|
|
positioning != "head-to-tail" &&
|
|
targetSpeed >= ownSpeed)
|
|
{
|
|
mPursuitMode = "lead";
|
|
}
|
|
else if (slantRangeTo <= engageRangeMax &&
|
|
positioning != "head-to-head" &&
|
|
positioning != "head-to-tail")
|
|
{
|
|
mPursuitMode = "lag";
|
|
}
|
|
else if (slantRangeTo > engageRangeMax ||
|
|
(slantRangeTo <= engageRangeMax &&
|
|
(positioning == "head-to-head" ||
|
|
positioning == "head-to-tail")))
|
|
{
|
|
mPursuitMode = "pure";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mPursuitMode = "pure";
|
|
}
|
|
|
|
//target is valid if this line is reached
|
|
//pursuit mode should be set, its a go
|
|
return "INTERCEPT";
|
|
}
|
|
|
|
// Clear the variables we'll use to store the agent's output
|
|
ClearTarget();
|
|
|
|
// If we're supposed to fly towards a point, go for it!
|
|
if (mCurrentJob.Name() == "pursue-point")
|
|
{
|
|
Map<string,Object> tempData = mCurrentJob.GetData();
|
|
WsfGeoPoint temp = (WsfGeoPoint)tempData["targetPoint"];
|
|
if (temp.IsValid())
|
|
{
|
|
mCurrentPointIntercept = temp;
|
|
}
|
|
return "POINT";
|
|
}
|
|
|
|
// If we don't have a target, just keep waiting
|
|
return "WAIT";
|
|
end_script
|
|
|
|
|
|
|
|
|
|
script bool CheckAndFire (WsfTrack aTarget)
|
|
|
|
bool launched = false;
|
|
|
|
if (aTarget.IsNull() || !aTarget.BelievedAlive())
|
|
{
|
|
writeln_d(" No target to fire on");
|
|
return launched;
|
|
}
|
|
|
|
if ( (WeaponsActive(aTarget) > 0) || (PeersWeaponsActive(aTarget) > 0) )
|
|
#if (WeaponsActive(aTarget) > 0)
|
|
{
|
|
mLastActiveWeaponTime = TIME_NOW;
|
|
mTrackWeaponActiveMap[aTarget.TargetName()] = TIME_NOW;
|
|
writeln_d(" Weapons already active against ", aTarget.TargetName());
|
|
return launched;
|
|
}
|
|
|
|
if ((TIME_NOW - mTrackWeaponActiveMap[aTarget.TargetName()]) < mInactiveLaunchDelay)
|
|
{
|
|
writeln_d(" Waiting to see what last active weapon did, score a kill?");
|
|
return launched;
|
|
}
|
|
|
|
writeln_d(" CheckAndFire() against: ", aTarget.TargetName(), " Index: ", aTarget.TargetIndex(), " Type: ", aTarget.TargetType());
|
|
|
|
if (!mCoopEngageOne)
|
|
{
|
|
if (aTarget.AuxDataString("notlocal") != "true")
|
|
{
|
|
WsfLocalTrack targetLocalTrack = (WsfLocalTrack)aTarget;
|
|
if (targetLocalTrack.IsValid())
|
|
{
|
|
if(!targetLocalTrack.ContributorOf(PLATFORM) && !targetLocalTrack.IsPredefined())
|
|
{
|
|
writeln_d(" Not able to coop engage! ", PLATFORM.Name(), " targeting ",aTarget.TargetName(), ". NumContributors: ", targetLocalTrack.NumContributors() );
|
|
return launched;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// We can launch once we have a target-quality track.
|
|
//TODO probably need something to test for failure to lockon
|
|
writeln_d (" aTarget.TrackQuality == ", aTarget.TrackQuality());
|
|
|
|
|
|
double dRelativeBearing = MATH.Fabs(PLATFORM.RelativeBearingTo( aTarget ));
|
|
double dSlantRange = PLATFORM.SlantRangeTo(aTarget);
|
|
|
|
writeln_d("abs relative bearing: ", dRelativeBearing, ", slant range: ", dSlantRange);
|
|
|
|
if (aTarget.TrackQuality() < GetRequiredTrackQualityForThreat(aTarget))
|
|
{
|
|
writeln_d("track quality not good enough to fire on target");
|
|
return launched;
|
|
}
|
|
|
|
|
|
// Launch the weapon. Returns false if not launched.
|
|
#extern bool LaunchWeapon(WsfPlatform, WsfTrack, WsfWeapon, int);
|
|
#extern double GetMaxLaunchRange (WsfPlatform, WsfTrack, string, Array<Map<string, Object>>);
|
|
#extern double EffectiveRange (WsfPlatform, WsfTrack);
|
|
#extern string GetTargetDomain(WsfTrack);
|
|
double dLaunchRangePercentage = GetLaunchPercentRangeMaxOnThreat(aTarget);
|
|
string sTargetDomain = GetTargetDomain(aTarget);
|
|
#int salvoCount = 1;
|
|
int salvoCount = GetSalvoForThreat(aTarget);
|
|
|
|
writeln_d("salvo count: ", salvoCount);
|
|
|
|
string selectedWeapon = GetWeaponForThreat(PLATFORM, aTarget, mWeaponArray, dLaunchRangePercentage);
|
|
if (selectedWeapon == "")
|
|
{
|
|
writeln_d(" No domain capable weapon within range available!");
|
|
return launched;
|
|
}
|
|
|
|
WsfWeapon weaponToLaunch = PLATFORM.Weapon(selectedWeapon);
|
|
|
|
if (dRelativeBearing > GetMaxFiringAngleForWeapon( weaponToLaunch ))
|
|
{
|
|
writeln_d("relative bearing towards ", aTarget.TargetName(), " is too high for selected weapon ", weaponToLaunch.Name());
|
|
return launched;
|
|
}
|
|
|
|
#extern int GetWeaponNumberActiveMax(string, Array<Map<string, Object>>);
|
|
int maxActiveOfType = GetWeaponNumberActiveMax(selectedWeapon,mWeaponArray);
|
|
int curActiveOfType = WeaponsActiveOfType(weaponToLaunch);
|
|
|
|
if (MustShootAt(aTarget))
|
|
{
|
|
curActiveOfType = maxActiveOfType - 1;
|
|
}
|
|
|
|
if (curActiveOfType >= maxActiveOfType)
|
|
{
|
|
writeln_d(" Max number (", maxActiveOfType,")of ", selectedWeapon, " weapon type are already active! (", curActiveOfType,")");
|
|
return launched;
|
|
}
|
|
|
|
double effectiveRange = EffectiveRange(PLATFORM, aTarget);
|
|
double dMaxLaunchRange = GetMaxLaunchRange(PLATFORM, aTarget, selectedWeapon, mWeaponArray);
|
|
|
|
double rangeScale = dLaunchRangePercentage; //no change to scaling yet (use normal launch percent)
|
|
if (dRelativeBearing > mDegradedFiringAngle)
|
|
{
|
|
rangeScale = MATH.Min(mDegradedPercentRange, dLaunchRangePercentage);
|
|
}
|
|
|
|
if ( effectiveRange > (dMaxLaunchRange*rangeScale) )
|
|
{
|
|
//writeln_d(" Slantrange, ", dSlantRange*(1/MATH.M_PER_NM()), " nmi, To Target Greater Than Weapon Max Launch Range, ", dMaxLaunchRange*(1/MATH.M_PER_NM()), " nmi!");
|
|
|
|
writeln_d(PLATFORM.Name(), " failed weapon range check against ", aTarget.TargetName());
|
|
return launched;
|
|
}
|
|
|
|
launched = LaunchWeapon(PLATFORM, aTarget, weaponToLaunch, salvoCount);
|
|
writeln_d(" launched == ", launched, ", weapon: ", selectedWeapon);
|
|
|
|
if (launched == true)
|
|
{
|
|
mLastWeaponFiredTime = TIME_NOW;
|
|
|
|
#######
|
|
double dPreviousRoundsFiredAt = mRoundsFiredAtMap.Get(aTarget);
|
|
mRoundsFiredAtMap.Set(aTarget,dPreviousRoundsFiredAt + salvoCount);
|
|
writeln_d(" F-pole after shot fired");
|
|
PLATFORM.Comment("Shot at: " + aTarget.TargetName());
|
|
//PLATFORM.Comment("Fired " + selectedWeapon + " at " + aTarget.TargetName());
|
|
mPursuitMode = "f-pole";
|
|
writeln_d(" mPursuitMode == ", mPursuitMode);
|
|
}
|
|
|
|
return launched;
|
|
end_script
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// These Transition methods are intended to handle any bookkeeping required when changing
|
|
// into a certain state.
|
|
|
|
script void TransitionToWAIT(bool resumeDefaultRoute)
|
|
|
|
PLATFORM.GoToSpeed(cWAIT_SPEED); //stay at wait speed
|
|
|
|
if (resumeDefaultRoute)
|
|
{
|
|
bool success = false;
|
|
if ( cROUTE_WAYPOINT_INDEX < 0)
|
|
{
|
|
success = PLATFORM.FollowRoute("DEFAULT_ROUTE", "CLOSEST_POINT");
|
|
}
|
|
else
|
|
{
|
|
success = PLATFORM.FollowRoute("DEFAULT_ROUTE", cROUTE_WAYPOINT_INDEX);
|
|
}
|
|
PLATFORM.GoToAltitude(cDEFAULT_ALTITUDE, 200, true);
|
|
|
|
if (!success)
|
|
{
|
|
PLATFORM.Comment("ERROR: transition to wait, could not follow default route!");
|
|
}
|
|
if (mRouteType != "DEFAULT")
|
|
{
|
|
PLATFORM.Comment("Route: DEFAULT");
|
|
mRouteType = "DEFAULT";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
### PLATFORM.GoToAltitude(PLATFORM.Altitude(), 100); //stay at same altitude
|
|
PLATFORM.TurnToHeading(PLATFORM.Heading()); //stay at same heading
|
|
}
|
|
|
|
### PLATFORM.GoToAltitude(cDEFAULT_ALTITUDE);
|
|
PROCESSOR.SetUpdateInterval(cSLOW_UPDATE_RATE);
|
|
|
|
writeln_d(" Transition to WAIT due to Think()");
|
|
end_script
|
|
|
|
|
|
|
|
script void TransitionToINTERCEPT(WsfTrack aTarget, string pursuitMode)
|
|
PLATFORM.GoToSpeed(cINTERCEPT_SPEED);
|
|
|
|
if (mUseMoverDuringPursuit)
|
|
{
|
|
#nothing
|
|
PLATFORM.GoToAltitude(cDEFAULT_ALTITUDE, 200);
|
|
}
|
|
else
|
|
{
|
|
#TransitionToWAIT(true); #FAIL
|
|
#PLATFORM.FollowRoute("DEFAULT_ROUTE", "CLOSEST_POINT"); #better
|
|
PLATFORM.ReturnToRoute();
|
|
PLATFORM.GoToAltitude(cDEFAULT_ALTITUDE, 200, true);
|
|
}
|
|
PROCESSOR.SetUpdateInterval(cFAST_UPDATE_RATE);
|
|
writeln_d(" Transition to INTERCEPT due to pursuing ", aTarget.TargetName(), " mode ", pursuitMode);
|
|
end_script
|
|
|
|
|
|
|
|
script void TransitionToPOINT()
|
|
PROCESSOR.SetUpdateInterval(cSLOW_UPDATE_RATE);
|
|
PLATFORM.GoToSpeed(cINTERCEPT_SPEED);
|
|
PLATFORM.GoToAltitude(cDEFAULT_ALTITUDE, 200);
|
|
writeln_d(" Transition to pursue POINT!");
|
|
end_script
|
|
|
|
|
|
|
|
script void TransitionToEVADE_INCOMING()
|
|
PROCESSOR.SetUpdateInterval(cFAST_UPDATE_RATE);
|
|
PLATFORM.GoToSpeed(cINTERCEPT_SPEED);
|
|
#PLATFORM.GoToAltitude(cDEFAULT_ALTITUDE, 200, true);
|
|
writeln_d(" Transition to EVADE_INCOMING");
|
|
end_script
|
|
|
|
|
|
|
|
|
|
|
|
|
|
on_initialize
|
|
|
|
#extern double GetWeaponRangeMax (WsfPlatform, Array<Map<string, Object>>);
|
|
#extern double GetWeaponRangeMin (WsfPlatform, Array<Map<string, Object>>);
|
|
|
|
ownshipEngagementRangeMin = GetWeaponRangeMin(PLATFORM, mWeaponArray);
|
|
ownshipEngagementRangeMax = GetWeaponRangeMax(PLATFORM, mWeaponArray);
|
|
|
|
mTrackWeaponActiveMap = Map<string,double>();
|
|
|
|
#can win a number of jobs, not just one
|
|
SetNumJobChannels(cMAX_ACTIVE_WEAPONS);
|
|
|
|
Array<WsfZone> zoneList = PLATFORM.Zones();
|
|
foreach( WsfZone z in zoneList)
|
|
{
|
|
mZoneNames.PushBack(z.Name());
|
|
}
|
|
foreach( string x in mZoneNames )
|
|
{
|
|
writeln_d("--- Aircraft ", PLATFORM.Name(), " is now monitoring retrieved zone: ", x );
|
|
}
|
|
|
|
if( mZoneNames.Size() == 0 )
|
|
{
|
|
writeln_d( "--- Aircraft ", PLATFORM.Name(), " has no zones defined." );
|
|
}
|
|
|
|
end_on_initialize
|
|
|
|
|
|
|
|
|
|
|
|
query_bid
|
|
|
|
if (!JOB.IsValid())
|
|
{
|
|
return cMIN_JOB_BID;
|
|
}
|
|
|
|
double current_bid = 10000.0;
|
|
//writeln_d(" - bidding on ", JOB.Name(), ", desc: ", JOB.GetDescription());
|
|
|
|
if (JOB.Name() == "pursue-target")
|
|
{
|
|
Map<string, Object>tempData = JOB.GetData();
|
|
string name = (string)tempData["targetTrackName"];
|
|
#extern WsfTrack GetTrackByName(WsfPlatform, string);
|
|
WsfTrack track = GetTrackByName(PLATFORM, name);
|
|
|
|
// if the track is not a foe or is damaged, we'll ignore it
|
|
if (!(track.IsValid())) // || !(track.IFF_Foe()))
|
|
{
|
|
writeln_d("!!! No track for JOB: ", JOB.Name(), ", ", JOB.GetDescription(), ", ", name);
|
|
return cMIN_JOB_BID;
|
|
}
|
|
|
|
#extern double EffectiveRange (WsfPlatform, WsfTrack);
|
|
double effRange = EffectiveRange(PLATFORM, track);
|
|
double slantRange = PLATFORM.SlantRangeTo(track);
|
|
|
|
bool isCapable = false;
|
|
string tName = track.TargetName();
|
|
if (!mTrackMinRangeOkMap.Exists(tName))
|
|
{
|
|
mTrackMinRangeOkMap.Set(tName,true);
|
|
}
|
|
|
|
double maxCapableWeaponRange = 0.0;
|
|
|
|
//determine if this agent has any weapons that are capable of engaging this kind of target (domain check)
|
|
#extern bool IsWeaponDomainCapable(WsfTrack,Map<string, Object>);
|
|
foreach (Map<string, Object> curWeapon in mWeaponArray)
|
|
{
|
|
#writeln_d(" ", (string)curWeapon["name"], " weapon is domain capable: ", IsWeaponDomainCapable(track,curWeapon));
|
|
#writeln_d(" ", (string)curWeapon["name"], " weapons remaining : ", ((WsfWeapon)curWeapon["weapon"]).QuantityRemaining());
|
|
|
|
if ( IsWeaponDomainCapable(track,curWeapon) &&
|
|
(((WsfWeapon)curWeapon["weapon"]).QuantityRemaining() > 0))
|
|
{
|
|
isCapable = true;
|
|
double curRange = (double)curWeapon["rangeMax"];
|
|
if (curRange > maxCapableWeaponRange) maxCapableWeaponRange = curRange;
|
|
|
|
if (mTrackMinRangeOkMap.Get(tName)==true &&
|
|
(double)curWeapon["rangeMin"] < effRange )
|
|
{
|
|
mTrackMinRangeOkMap.Set(tName, true);
|
|
}
|
|
else if (mTrackMinRangeOkMap.Get(tName)==false &&
|
|
((double)curWeapon["rangeMin"] * 2.0) < slantRange )
|
|
{
|
|
mTrackMinRangeOkMap.Set(tName, true);
|
|
}
|
|
else
|
|
{
|
|
mTrackMinRangeOkMap.Set(tName, false);
|
|
|
|
writeln_d(" weapon min range failure");
|
|
}
|
|
}
|
|
}
|
|
if (isCapable==false)
|
|
{
|
|
#extern string GetTargetDomain(WsfTrack);
|
|
writeln_d("!!! No weapon capable in the ", GetTargetDomain(track)," domain and in range for: ", JOB.GetDescription());
|
|
return cMIN_JOB_BID;
|
|
}
|
|
|
|
if (mTrackMinRangeOkMap.Get(tName)==false)
|
|
{
|
|
writeln_d("!!! TrackMinRangeOk == false");
|
|
return cMIN_JOB_BID;
|
|
}
|
|
|
|
double currentlyTargeted = 0;
|
|
if (GetTargetName() == name)
|
|
{
|
|
currentlyTargeted = 1.0;
|
|
}
|
|
//writeln_d(" Currently Targeted: ", currentlyTargeted );
|
|
|
|
|
|
|
|
//determine if the missile range is added to the escort protect radius
|
|
if (!escortAddMissileProtectRange)
|
|
{
|
|
maxCapableWeaponRange = 0.0;
|
|
}
|
|
// if escoring a package, determine if the threat is engageable
|
|
// check if it lies within the protect radius
|
|
if (mEscortFollowing)
|
|
{
|
|
#if already engaging the threat, allow a little chase
|
|
if (slantRange > (escortProtectDistance + maxCapableWeaponRange + (currentlyTargeted * escortChaseDistance)))
|
|
{
|
|
#writeln_d(" Target not a threat to escort package!");
|
|
return cMIN_JOB_BID;
|
|
}
|
|
}
|
|
|
|
#extern string DetermineTrackCategory (WsfTrack);
|
|
string targetType = DetermineTrackCategory(track);
|
|
//writeln_d(" track category: ",targetType);
|
|
#extern bool TestTrackCategory(WsfTrack, string);
|
|
if (TestTrackCategory(track, "unknown"))
|
|
{
|
|
writeln_d("!!! Target type unknown for current job. ");
|
|
return cMIN_JOB_BID;
|
|
}
|
|
|
|
int othersTargeting = GetRIPRCommanderProcessor().SubsTargeting( track, PLATFORM );
|
|
//writeln_d(" Others Targeting: ", othersTargeting );
|
|
|
|
// set the target type
|
|
//writeln_d(" -Creating track record for ", name, " -- ", targetType);
|
|
//writeln_d(" track closing speed: ", PLATFORM.ClosingSpeedOf(track) );
|
|
//writeln_d(" track slant range : ", PLATFORM.SlantRangeTo(track) );
|
|
|
|
// account for bidding parameters
|
|
#extern int GetBucket(double, double);
|
|
//writeln_d(" GetBucket(track closing speed): ", GetBucket(PLATFORM.ClosingSpeedOf(track), cBUCKET_BASE) );
|
|
//writeln_d(" GetBucket(track slant range): ", GetBucket(PLATFORM.SlantRangeTo(track), cBUCKET_BASE) );
|
|
|
|
//current_bid = current_bid + cWEIGHT_BEING_TARGETED * beingTargeted;
|
|
current_bid = current_bid + cWEIGHT_CURRENT_TARGET * currentlyTargeted;
|
|
current_bid = current_bid + cWEIGHT_CLOSING_SPEED_OF * GetBucket(PLATFORM.ClosingSpeedOf(track), cBUCKET_BASE) + GetBucket(cBASE_CLOSING_SPEED_CONSTANT, cBUCKET_BASE);
|
|
current_bid = current_bid + cWEIGHT_SLANT_RANGE_TO * GetBucket(PLATFORM.SlantRangeTo(track), cBUCKET_BASE) + GetBucket(cBASE_SLANT_RANGE_CONSTANT, cBUCKET_BASE);
|
|
current_bid = current_bid + cWEIGHT_OTHERS_TARGETING * othersTargeting;
|
|
current_bid = current_bid + cWEIGHT_WEAPONS_IN_FLIGHT * PROCESSOR.WeaponsActive(track);
|
|
current_bid = current_bid + cWEIGHT_WEAPONS_FIRED_AT * mRoundsFiredAtMap.Get(track);
|
|
current_bid = current_bid + cWEIGHT_ANY_WEAPONS_ACTIVE * (WeaponsActive(track) + PeersWeaponsActive(track));
|
|
|
|
### if ( (WeaponsActive(aTarget) > 0) || (PeersWeaponsActive(aTarget) > 0) )
|
|
|
|
|
|
|
|
if( ThreatTypePriority.Exists( targetType ) )
|
|
{
|
|
current_bid = current_bid + ThreatTypePriority.Get( targetType );
|
|
}
|
|
}
|
|
else if( JOB.Name() == "pursue-point" )
|
|
{
|
|
Map<string, Object>tempData = JOB.GetData();
|
|
string name = (string)tempData["targetTrackName"];
|
|
|
|
WsfGeoPoint point = (WsfGeoPoint)tempData["targetPoint"];
|
|
|
|
if (!point.IsValid())
|
|
{
|
|
writeln_d("!!! Invalid point for current job: ",JOB.Name(), ", ", JOB.GetDescription() );
|
|
return cMIN_JOB_BID;
|
|
}
|
|
|
|
// account for bidding parameters
|
|
#extern int GetBucket(double, double);
|
|
current_bid = current_bid + cWEIGHT_SLANT_RANGE_TO * GetBucket(PLATFORM.SlantRangeTo(point), cBUCKET_BASE) + GetBucket(cBASE_SLANT_RANGE_CONSTANT, cBUCKET_BASE);
|
|
}
|
|
|
|
//writeln_d(" calculated bid: ", current_bid);
|
|
return current_bid;
|
|
|
|
end_query_bid
|
|
|
|
|
|
|
|
on_update
|
|
|
|
|
|
|
|
int WeaponCount = WeaponsActive();
|
|
for (int i=0; i< WeaponCount; i=i+1)
|
|
{
|
|
WsfPlatform w = ActiveWeaponPlatform(i);
|
|
if (IsUplinkingTo(w))
|
|
{
|
|
foreach( WsfRIPRProcessor proc in GetRIPRCommanderProcessor().GetRIPRSubordinateProcessors() )
|
|
{
|
|
if ( ! proc.IsUplinkingTo(w))
|
|
{
|
|
proc.StartUplinking(w);
|
|
PROCESSOR.StopUplinking(w);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
#extern Array<string> ttrs;
|
|
foreach(string ttr in ttrs)
|
|
{
|
|
if (PLATFORM.AuxDataBool(ttr)) #shoot at this guy if we can
|
|
{
|
|
writeln_d(PLATFORM.Name(), " trying to shoot at ttr: ", ttr);
|
|
WsfPlatform ttrPlatform = WsfSimulation.FindPlatform(ttr);
|
|
if (ttrPlatform.IsValid()) #take a shot
|
|
{
|
|
WsfTrack ttrTrack = ttrPlatform.MakeTrack();
|
|
ttrTrack.SetAuxData("notlocal", "true");
|
|
if (CheckAndFire(ttrTrack))
|
|
{
|
|
writeln_d(PLATFORM.Name(), " shot at the ttr: ", ttr);
|
|
PLATFORM.SetAuxData(ttr, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#extern bool TestTrackCategory(WsfTrack, string);
|
|
|
|
double duration = 0.0;
|
|
double lStartTime = GetWallClockTime();
|
|
|
|
writeln_d("--- on_update Platform: ", PLATFORM.Name(), ", State: ", mState, ", Time: ", TIME_NOW);
|
|
|
|
string oldState = mState;
|
|
string oldPursuitMode = mPursuitMode;
|
|
|
|
mState = Think();
|
|
|
|
WsfTrack curTarget = GetTarget();
|
|
string targetType;
|
|
|
|
bool turnOnFiringRadar = false;
|
|
//turn on firing radar if in range
|
|
if (mTurnOnFiringRadarInRange && !curTarget.IsNull() && curTarget.IsValid())
|
|
{
|
|
//check range against radar range
|
|
if (PLATFORM.SlantRangeTo(curTarget) < mFiringRadarRange)
|
|
{
|
|
#writeln_d(PLATFORM.Name()," within range of curTarget!");
|
|
|
|
turnOnFiringRadar = true;
|
|
}
|
|
else
|
|
{
|
|
#writeln_d(PLATFORM.Name()," NOT within range of curTarget!");
|
|
}
|
|
}
|
|
//turn on firing radar if being tracked
|
|
if (mTurnOnFiringRadarIfRWR && !turnOnFiringRadar)
|
|
{
|
|
WsfLocalTrackList localTracks = PLATFORM.MasterTrackList();
|
|
foreach (WsfLocalTrack t in localTracks)
|
|
{
|
|
if (t.SensorTypeContributor(mRWR_RadarType))
|
|
{
|
|
turnOnFiringRadar = true;
|
|
#writeln_d(PLATFORM.Name(), " is being tracked! RWR signal from ", mRWR_RadarType);
|
|
break;
|
|
}
|
|
}
|
|
if (!turnOnFiringRadar)
|
|
{
|
|
#writeln_d(PLATFORM.Name(), " not being tracked. No RWR signal from ", mRWR_RadarType);
|
|
}
|
|
}
|
|
if (turnOnFiringRadar)
|
|
{
|
|
//make sure radar is on
|
|
WsfSensor firingRadar = PLATFORM.Sensor(mFiringRadarName);
|
|
if (firingRadar.IsValid())
|
|
{
|
|
if (!firingRadar.IsTurnedOn())
|
|
{
|
|
firingRadar.TurnOn();
|
|
#writeln_d(PLATFORM.Name(), " turned on fire control radar ", mFiringRadarName);
|
|
}
|
|
else
|
|
{
|
|
#writeln_d(PLATFORM.Name(), " has fire control radar ", mFiringRadarName, " already on!");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#writeln_d("fire control radar ", mFiringRadarName," not found on ", PLATFORM.Name());
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (oldState != mState)
|
|
{
|
|
PLATFORM.Comment("State: " + mState);
|
|
}
|
|
|
|
if (mState == "POINT")
|
|
{
|
|
if (oldState != "POINT")
|
|
{
|
|
TransitionToPOINT();
|
|
}
|
|
|
|
if (mUsePathFinder)
|
|
{
|
|
//extern bool UpdatePointInterceptLocationWithPathing(WsfPlatform, WsfGeoPoint);
|
|
UpdatePointInterceptLocationWithPathing (PLATFORM, mCurrentPointIntercept);
|
|
}
|
|
else
|
|
{
|
|
//extern bool UpdatePointInterceptLocation(WsfPlatform, WsfGeoPoint);
|
|
UpdatePointInterceptLocation (PLATFORM, mCurrentPointIntercept);
|
|
}
|
|
|
|
duration = GetWallClockTime() - lStartTime;
|
|
writeln_d("--- on_update Platform: ", PLATFORM.Name(), ", Process Time: ", duration);
|
|
return;
|
|
}
|
|
|
|
// Process our state
|
|
if (mState == "EVADE_INCOMING")
|
|
{
|
|
if (oldState != "EVADE_INCOMING")
|
|
{
|
|
TransitionToEVADE_INCOMING();
|
|
#mAltitudeBeforeEvade = PLATFORM.Altitude();
|
|
mAltitudeBeforeEvade = cDEFAULT_ALTITUDE;
|
|
mSafeAltitudeToDiveTo = MATH.Max(mAltitudeMin, (mAltitudeBeforeEvade - mAltitudeToDiveEvade));
|
|
}
|
|
CheckAndFire(curTarget);
|
|
writeln_d(" Evading incoming");
|
|
#extern bool EvadeIncoming(WsfPlatform, WsfAIAIProcessor, Array<WsfPlatform>, double, double, double);
|
|
###EvadeIncoming(PLATFORM, PROCESSOR, mIncoming, mAltitudeBeforeEvade, mSafeAltitudeToDiveTo, weightPeersForEvade);
|
|
EvadeIncoming(PLATFORM, PROCESSOR, mIncoming, mAltitudeBeforeEvade, mAltitudeBeforeEvade, weightPeersForEvade);
|
|
duration = GetWallClockTime() - lStartTime;
|
|
writeln_d("--- on_update Platform: ", PLATFORM.Name(), ", Process Time: ", duration);
|
|
return;
|
|
}
|
|
|
|
if (TestTrackCategory(curTarget, "unknown"))
|
|
{
|
|
writeln_d("Platform: ", PLATFORM.Name(), " current target: ", curTarget, " unknown or not valid, default mode to WAIT.");
|
|
mState = "WAIT";
|
|
}
|
|
|
|
if (mState == "INTERCEPT")
|
|
{
|
|
if (oldState != "INTERCEPT")
|
|
{
|
|
TransitionToINTERCEPT( curTarget, mPursuitMode );
|
|
}
|
|
#######
|
|
PLATFORM.SetCurrentTarget(curTarget);
|
|
CheckAndFire(curTarget);
|
|
|
|
if (mUseMoverDuringPursuit)
|
|
{
|
|
// Our track quality may not be good enough yet, so keep moving towards the target.
|
|
if (mUsePathFinder)
|
|
{
|
|
UpdateInterceptLocationWithPathing (curTarget, mPursuitMode);
|
|
}
|
|
else
|
|
{
|
|
UpdateInterceptLocation (curTarget, mPursuitMode);
|
|
}
|
|
}
|
|
|
|
if (mPursuitMode != oldPursuitMode)
|
|
{
|
|
//PLATFORM.Comment("Pursuit Mode: " + mPursuitMode);
|
|
}
|
|
|
|
//writeln_d(" Heading: ", PLATFORM.Heading (), " Altitude: ", PLATFORM.Altitude (), " Speed: ", PLATFORM.Speed());
|
|
//writeln_d(" (", PLATFORM.Latitude(), ", ", PLATFORM.Longitude(), ", ", PLATFORM.Altitude(), ")");
|
|
//duration = GetWallClockTime() - lStartTime;
|
|
//writeln_d("--- on_update Platform: ", PLATFORM.Name(), ", Process Time: ", duration);
|
|
//return;
|
|
}
|
|
else if (mState == "BINGO_FUEL")
|
|
{
|
|
writeln_d(" bingo_fuel, return to base or something");
|
|
PLATFORM.Comment("Bingo fuel");
|
|
PLATFORM.GoToSpeed(0);
|
|
}
|
|
else //if (mState == "WAIT")
|
|
{
|
|
mState = "WAIT";
|
|
|
|
bool flyingEscort = false;
|
|
if (mUseMoverDuringPursuit)
|
|
{
|
|
flyingEscort = FlyEscortFormation();
|
|
}
|
|
|
|
if (flyingEscort)
|
|
{
|
|
writeln_d("flying escort formation!");
|
|
}
|
|
else
|
|
{
|
|
writeln_d("Platform: ", PLATFORM.Name(), " not flying escort, flying regular WAIT state.");
|
|
if (oldState != "WAIT")
|
|
{
|
|
TransitionToWAIT(true); //TransitionToWAIT(false); //LBM try this
|
|
}
|
|
}
|
|
}
|
|
|
|
//give attention to secondary jobs
|
|
|
|
string tooComment = "Targets of opportunity: ";
|
|
|
|
for (int channel = 1; channel < GetNumJobChannels(); channel = channel + 1)
|
|
{
|
|
//writeln_d("Getting job for channel: ", channel);
|
|
|
|
WsfRIPRJob aJob = GetRIPRCommanderProcessor().GetJobFor(TIME_NOW, PROCESSOR, channel);
|
|
if (!aJob.IsValid())
|
|
{
|
|
//writeln_d("Job NOT VALID for channel: ", channel);
|
|
continue;
|
|
}
|
|
if (aJob.Name() == "pursue-target")
|
|
{
|
|
string targetName = (string)aJob.GetData("targetTrackName");
|
|
#extern WsfTrack GetTrackByName(WsfPlatform, string);
|
|
WsfTrack targetTrack = GetTrackByName(PLATFORM, targetName);
|
|
|
|
if (!targetTrack.IsValid() || !targetTrack.BelievedAlive()) // || !targetTrack.IFF_Foe())
|
|
{
|
|
writeln_d("Job TRACK is NOT valid for channel: ", channel, " job: ", aJob.GetDescription());
|
|
continue;
|
|
}
|
|
|
|
tooComment = (tooComment + targetName + " ");
|
|
|
|
//don't fire last weapon at target thats not primary target
|
|
#extern double GetWeaponQuantityRemainingMax(WsfPlatform, Array<Map<string, Object>>);
|
|
if (GetWeaponQuantityRemainingMax(PLATFORM, mWeaponArray) > 1)
|
|
{
|
|
writeln_d("Platform: ", PLATFORM.Name(), " Looking to fire at target of opportunity: ", targetName, " for channel: ", channel);
|
|
CheckAndFire(targetTrack);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//writeln_d("Job is NOT pursue-target for channel: ", channel);
|
|
}
|
|
}
|
|
|
|
if (mOldTOOComment != tooComment)
|
|
{
|
|
//PLATFORM.Comment(tooComment);
|
|
mOldTOOComment = tooComment;
|
|
}
|
|
|
|
duration = GetWallClockTime() - lStartTime;
|
|
writeln_d("--- on_update Platform: ", PLATFORM.Name(), ", Process Time: ", duration);
|
|
|
|
end_on_update
|
|
|
|
|
|
|
|
end_processor
|
|
|