Files
lab1/processors/ripr_agents/aiai/behavior_engage-target.txt

630 lines
25 KiB
Plaintext
Raw Permalink Normal View History

2025-09-12 15:20:28 +08:00
# ****************************************************************************
# 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.
# ****************************************************************************
##############################################################################
### assumes exists: #extern string GetWeaponForThreat(WsfPlatform, WsfTrack);
##############################################################################
behavior engage-target
script_debug_writes off
script_variables
//**********************************************************************//
//** platform / agent specific shooting parameters **//
//**********************************************************************//
bool mCoopEngageOne = false;
bool mCoopEngageOneFlightOnly = false;
//specify any targets here that you will always shoot at...
//even disregarding other targets to do so. (use types or names)
Array<string> mMustShootAtCategories = Array<string>();
mMustShootAtCategories[0] = "sam";
mMustShootAtCategories[1] = "FIRE_CONTROL";
double mDegradedFiringAngle = 55.0; //negative if not valid
double mDegradedPercentRange = 0.50; //range constraint if past degraded firing angle
//specify orientation limits for shooting
double mMaxFiringRollAngle = 10.0; //dont shoot if rolled more/less than this
double mMaxFiringPitchAngle = 15.0; //dont shoot if pitched more than this
double mMinFiringPitchAngle = -10.0; //dont shoot if pitched less than this
//**********************************************************************//
//** threat specific shooting parameters **//
//**********************************************************************//
//require different track qualities to fire on different kinds of threats
double DefaultRequiredTrackQuality = 0.49;
Map<string, double> ThreatTypeRequiredTrackQuality = Map<string, double>();
ThreatTypeRequiredTrackQuality["bomber"] = 0.49;
ThreatTypeRequiredTrackQuality["fighter"] = 0.49;
//fire off different salvos at different types of threats
int DefaultAirSalvo = 1;
int DefaultGndSalvo = 1;
Map<string, int> ThreatTypeSalvo = Map<string, int>();
ThreatTypeSalvo["sam"] = 2;
ThreatTypeSalvo["ship"] = 2;
ThreatTypeSalvo["bomber"] = 2;
ThreatTypeSalvo["fighter"] = 1;
ThreatTypeSalvo["FIRE_CONTROL"] = 1;
ThreatTypeSalvo["primary_target"] = 2;
ThreatTypeSalvo["secondary_target"] = 2;
//force a specific weapon for use against given threat type
Map<string, string> ThreatTypeWeapon = Map<string, string>();
//ThreatTypeWeapon["uav"] = "srm";
//ThreatTypeWeapon["fighter"] = "lrm";
//**********************************************************************//
//** weapon + threat specific shooting parameters **//
//**********************************************************************//
//specify an Rmax based on which weapon used and which threat engaged
double DefaultPercentRangeMax = 0.80; // don't launch unless within this percent of Rmax
double DefaultPercentRangeMin = 1.20; // don't launch unless beyond this percent of Rmin
Map<string, Map<string, double>> WeaponThreatRmaxMap = Map<string, Map<string, double>>();
WeaponThreatRmaxMap["base_weapon"] = Map<string, double>();
WeaponThreatRmaxMap["base_weapon"].Set("fighter", 0.80);
//specify max firing angles based on weapon used and threat engaged
double DefaultMaxFiringAngle = 45.0;
Map<string, Map<string, double>> WeaponThreatAngleMap = Map<string, Map<string, double>>();
WeaponThreatAngleMap["base_weapon"] = Map<string, double>();
WeaponThreatAngleMap["base_weapon"].Set("fighter", 45.0);
WeaponThreatAngleMap["base_weapon"].Set("missile", 30.0);
//**********************************************************************//
//********* VARIABLES BELOW THIS LINE ARE NOT FOR USER EDITING *********//
//**********************************************************************//
double mInactiveLaunchDelay = 1.0; // wait 1 seconds after your last missile detonated.
// hacky, useful for weapons that dont show active
Map<string,double> mTrackWeaponActiveMap = Map<string,double>();
Map<WsfTrack, double> mRoundsFiredAtMap = Map<WsfTrack, double>(); //useful when weapons don't show as active
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)
#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 DefaultGndSalvo;
}
return DefaultAirSalvo;
end_script
script double GetRequiredTrackQualityForThreat(WsfTrack threat)
writeln_d("checking required TQ for track: ", threat.TargetName());
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;
}
}
}
return DefaultRequiredTrackQuality;
end_script
script double GetLaunchPercentRangeMaxOnThreat(string weaponName, WsfTrack threat)
WsfPlatform plat = WsfSimulation.FindPlatform( threat.TargetName() );
if (plat.IsValid())
{
if (WeaponThreatRmaxMap.Exists(weaponName))
{
Map<string, double> categoryRangeMap = WeaponThreatRmaxMap.Get(weaponName);
foreach (string aCategory : double percent in categoryRangeMap)
{
if( plat.CategoryMemberOf( aCategory ) )
{
return percent;
}
}
}
}
return DefaultPercentRangeMax;
end_script
script double GetMaxFiringAngleForWeapon(string weaponName, WsfTrack threat)
WsfPlatform plat = WsfSimulation.FindPlatform( threat.TargetName() );
if (plat.IsValid())
{
if (WeaponThreatAngleMap.Exists(weaponName))
{
Map<string, double> threatAngleMap = WeaponThreatAngleMap.Get(weaponName);
foreach (string aCategory : double angle in threatAngleMap)
{
if( plat.CategoryMemberOf( aCategory ) )
{
return angle;
}
}
}
}
return DefaultMaxFiringAngle;
end_script
script string GetWeaponForThreat(WsfPlatform platform, WsfTrack track)
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(" Time= ", TIME_NOW, " ", PLATFORM.Name(), " searching only for weapon category: ", name, " (for use against ", plat.Name(), ")");
break;
}
}
}
else
{
writeln_d("platform for target ", track.TargetName()," is not valid!");
}
extern Array<Map<string, Object>> mWeaponArray;
foreach (Map<string, Object> curWeapon in mWeaponArray)
{
string weaponName = (string)curWeapon["name"];
WsfWeapon weapon = (WsfWeapon)curWeapon["weapon"];
writeln_d(" checking weapon ", weaponName, " valid=", weapon.IsValid());
if (checkName && weaponName != name)
{
continue;
}
if (weapon.QuantityRemaining() <= 0)
{
continue;
}
if (!IsWeaponDomainCapable(track,curWeapon))
{
continue;
}
WsfLaunchComputer lcPtr = weapon.LaunchComputer();
if (lcPtr.IsValid() &&
lcPtr.IsA_TypeOf("WSF_AIR_TO_AIR_LAUNCH_COMPUTER"))
{
writeln_d(" using air-to-air launch computer");
// The returned array contains: Rmax, RmaxTOF, Rne, RneTOF, Rmin, RminTOF
// in that order. -1.0 means "not valid".
Array<double> returnedValues = lcPtr.LookupResult(track);
// Now have to consider whether we have enough information to continue with a weapon shot:
double theRmax = returnedValues[0]; //"Rmax";
double theRmaxTOF = returnedValues[1]; //"RmaxTOF";
double theRne = returnedValues[2]; //"Rne";
double theRneTOF = returnedValues[3]; //"RneTOF";
double theRmin = returnedValues[4]; //"Rmin";
double theRminTOF = returnedValues[5]; //"RminTOF";
double range = 0.0;
if (track.RangeValid())
{
range = track.Range(); #is this the range from you to the track, or the range from the sensor to the track?
}
else
{
range = track.GroundRangeTo(PLATFORM);
}
// Check for track range less than Rmin * scaleFactor, if not, return.
// But do not check for min range constraint at all unless we are likely to be needing it.
if (range < 5000)
{
if (theRmin == -1.0)
{
writeln_d(" Engagement did not shoot since Rmin was not valid.");
continue;
#return;
}
double RminConstraint = theRmin * DefaultPercentRangeMin;
if (range < RminConstraint)
{
writeln_d(" Engagement did not shoot since inside the k * Rmin constraint distance.");
writeln_d(" Range versus Rmin constraint = ", range, ", ", RminConstraint);
continue;
#return;
}
}
// Check for track range less than Rne, if so, FORCE a weapon fire.
bool forceWeaponFire = false;
if (range < theRne)
{
writeln_d(" Engagement is forcing a weapon fire due to inside Rne.");
writeln_d(" Range versus Rne constraint = ", range, ", ", theRne);
forceWeaponFire = true;
}
if (forceWeaponFire == false)
{
######################################TRY THIS######################################
WsfPlatform plat = WsfSimulation.FindPlatform( track.TargetName() );
if (plat.IsValid() && plat.CategoryMemberOf("fighter"))
{
#theRmax = theRne;
theRmax = (theRmax + theRne)/2.0; //for highly maneuverable fighter targets
}
####################################END TRY THIS####################################
// Check for track range less than k * Rmax, if not, return.
if (theRmax == -1.0)
{
writeln_d(" Engagement did not shoot since Rmax was not valid.");
continue;
#return;
}
//double RmaxConstraint = theRmax * DefaultPercentRangeMax;
double percentRMax = GetLaunchPercentRangeMaxOnThreat(weaponName, track);
double RmaxConstraint = theRmax * percentRMax;
if (range > RmaxConstraint)
{
writeln_d(" Engagement did not shoot since outside the k * Rmax constraint distance.");
writeln_d(" Range versus Rne constraint = ", range, ", ", theRne);
continue;
#return;
}
}
writeln_d(" Engagement meets constraints for firing a weapon (continue).");
}
else if (lcPtr.IsValid() &&
lcPtr.IsA_TypeOf("WSF_ATG_LAUNCH_COMPUTER"))
{
writeln_d(" using air-to-ground launch computer");
if (lcPtr.CanIntercept(track))
{
//intercept works, this weapon is a candidate
}
else
{
continue;
}
}
else
{
writeln_d(" using script input mWeaponArray array range values");
//check our own ranges & angles --> hacky!!!
#extern double EffectiveRange(WsfPlatform, WsfTrack);
double effectiveRange = EffectiveRange(platform, track);
double absRelativeBearing = MATH.Fabs(PLATFORM.RelativeBearingTo( track ));
double rangeScale = GetLaunchPercentRangeMaxOnThreat(weaponName, track);
if (absRelativeBearing > mDegradedFiringAngle)
{
rangeScale = MATH.Min(mDegradedPercentRange, rangeScale);
}
if ((double)curWeapon["rangeMin"] > effectiveRange)
{
writeln_d(" target too close");
continue;
}
if (absRelativeBearing > GetMaxFiringAngleForWeapon(weaponName, track))
{
writeln_d(" target firing angle too large");
continue;
}
if ((double)curWeapon["rangeMax"] * rangeScale < effectiveRange)
{
writeln_d(" target too far away");
continue;
}
double TOF = 180;
double missileAvgSpeed = 1000;
if (curWeapon.Exists("avgSpeed"))
{
missileAvgSpeed = (double)curWeapon["avgSpeed"];
}
if (curWeapon.Exists("TOF"))
{
TOF = (double)curWeapon["TOF"];
}
double range = PLATFORM.SlantRangeTo(track);
//double closingSpeed = PLATFORM.ClosingSpeedOf(track);
double relBearing = track.RelativeBearingTo(PLATFORM);
if (relBearing > 90.0)
{
if (track.Speed() > missileAvgSpeed)
{
continue;
}
double speedDiff = missileAvgSpeed - track.Speed();
if ((range/speedDiff) > TOF)
{
continue;
}
}
}
return (string)curWeapon["name"];
}
return "";
end_script
#on_init
#end_on_init
precondition
writeln_d("precondition engage-target");
if (!PROCESSOR.IsA_TypeOf("WSF_RIPR_PROCESSOR"))
{
return Failure("behavior not attached to a RIPR processor!");
}
#extern WsfTrack GetTrackByName(WsfPlatform, string);
double pitch = PLATFORM.Pitch();
if (MATH.Fabs(PLATFORM.Roll()) > mMaxFiringRollAngle ||
pitch > mMaxFiringPitchAngle ||
pitch < mMinFiringPitchAngle)
{
string msgStr = write_str(" ", PLATFORM.Name(), " orientation too far off to fire! (roll or pitch)");
writeln_d(msgStr);
//PLATFORM.Comment(msgStr);
return Failure(msgStr);
}
WsfRIPRProcessor commander = ((WsfRIPRProcessor)PROCESSOR).GetRIPRCommanderProcessor();
if (commander.IsValid())
{
#check all job channels, not just the main target
for (int channel = 0; channel < ((WsfRIPRProcessor)PROCESSOR).GetNumJobChannels(); channel = channel + 1)
{
WsfRIPRJob aJob = commander.GetJobFor(((WsfRIPRProcessor)PROCESSOR), channel);
if (aJob.IsValid() &&
aJob.Name() == "pursue-target")
{
string targetName = (string)aJob.GetData("targetTrackName");
WsfTrack targetTrack = GetTrackByName(PLATFORM, targetName);
if (!targetTrack.IsNull() &&
targetTrack.IsValid())
{
#extern bool TestTrackCategory( WsfTrack, string );
if (!TestTrackCategory(targetTrack, "unknown") &&
targetTrack.BelievedAlive())
{
return true;
}
}
}
}
}
return Failure("no acceptable target to shoot at!");
end_precondition
script bool FireWeapon(WsfTrack targetTrack)
bool launched = false;
writeln_d(" Time= ", TIME_NOW, " Attempting a shot against: ", targetTrack.TargetName(), " Index: ", targetTrack.TargetIndex(), " Type: ", targetTrack.TargetType());
#extern bool TestTrackCategory(WsfTrack, string);
if (!TestTrackCategory(targetTrack, "unknown") &&
targetTrack.BelievedAlive())
{
if ((((WsfRIPRProcessor)PROCESSOR).WeaponsActive(targetTrack) > 0) ||
(((WsfRIPRProcessor)PROCESSOR).PeersWeaponsActive(targetTrack) > 0))
{
mTrackWeaponActiveMap[targetTrack.TargetName()] = TIME_NOW;
writeln_d(" FAIL: Weapons already active against ", targetTrack.TargetName());
return launched;
}
else
{
writeln_d("no weapons active against target ", targetTrack.TargetName());
}
if ((TIME_NOW - mTrackWeaponActiveMap[targetTrack.TargetName()]) < mInactiveLaunchDelay)
{
writeln_d(" FAIL: Waiting to see what last active weapon did, score a kill?");
return launched;
}
// if this is a ttr, it should ignore the check for coop engage one
if (targetTrack.CheckAuxData("notlocal") == false)
{
if (mCoopEngageOne == false)
{
WsfLocalTrack targetLocalTrack = (WsfLocalTrack)targetTrack;
if (targetLocalTrack.IsValid())
{
if(!targetLocalTrack.ContributorOf(PLATFORM) &&
!targetLocalTrack.IsPredefined())
{
writeln_d(" FAIL: Not able to coop engage! ", PLATFORM.Name(), " targeting ",targetTrack.TargetName(), ". NumContributors: ", targetLocalTrack.NumContributors() );
return launched;
}
}
}
else if (mCoopEngageOneFlightOnly == true)
{
//check if this platform needs somebody in his flight to have track on the target
WsfLocalTrack targetLocalTrack = (WsfLocalTrack)targetTrack;
if (targetLocalTrack.IsValid())
{
if (targetLocalTrack.IsPredefined())
{
//its fine, go ahead & shoot
}
else
{
bool OkToShoot = false;
Array<WsfPlatform> peers = ((WsfRIPRProcessor)PROCESSOR).GetRIPRCommanderProcessor().SubordinatePlatforms();
foreach (WsfPlatform peer in peers)
{
if(targetLocalTrack.ContributorOf(peer))
{
OkToShoot = true;
break;
}
}
if (!OkToShoot)
{
writeln_d(" FAIL: Cant engage tracks not supported by flight group! ", PLATFORM.Name(), " targeting ", targetTrack.TargetName(), ".");
return launched;
}
}
}
}
}
// We can launch once we have a target-quality track.
//TODO probably need something to test for failure to lockon
writeln_d (" targetTrack.TrackQuality == ", targetTrack.TrackQuality());
if (targetTrack.TrackQuality() < GetRequiredTrackQualityForThreat(targetTrack))
{
writeln_d(" FAIL: track quality not good enough to fire on target");
return launched;
}
string selectedWeapon = GetWeaponForThreat(PLATFORM, targetTrack); #checks domain & kinematic capability, & valid quantity remaining
if (selectedWeapon == "")
{
writeln_d(" FAIL: No domain capable weapon within range available!");
return launched;
}
WsfWeapon weaponToLaunch = PLATFORM.Weapon(selectedWeapon);
//writeln_d(" using a conventional weapon!");
#extern Array<Map<string, Object>> mWeaponArray;
#extern int GetWeaponNumberActiveMax(string, Array<Map<string, Object>>);
int maxActiveOfType = GetWeaponNumberActiveMax(selectedWeapon,mWeaponArray);
int curActiveOfType = ((WsfRIPRProcessor)PROCESSOR).WeaponsActiveOfType(weaponToLaunch);
if (MustShootAt(targetTrack))
{
curActiveOfType = maxActiveOfType - 1;
}
if (curActiveOfType >= maxActiveOfType)
{
writeln_d(" FAIL: Max number(", maxActiveOfType,") of ", selectedWeapon, " weapon type are already active! (", curActiveOfType,")");
return launched;
}
int salvoCount = GetSalvoForThreat(targetTrack);
writeln_d(" salvo count for ", targetTrack, " is: ", salvoCount);
#extern bool LaunchWeapon(WsfPlatform, WsfTrack, WsfWeapon, int);
launched = LaunchWeapon(PLATFORM, targetTrack, weaponToLaunch, salvoCount);
writeln_d(" launched == ", launched, ", weapon: ", selectedWeapon);
if (launched == true)
{
double dPreviousRoundsFiredAt = mRoundsFiredAtMap.Get(targetTrack);
mRoundsFiredAtMap.Set(targetTrack,dPreviousRoundsFiredAt + salvoCount);
string msg = "Shot at: " + targetTrack.TargetName();
PLATFORM.Comment(msg);
writeln_d(" SUCCESS: ", PLATFORM.Name(), " ", msg);
}
return launched;
}
return launched;
end_script
execute
writeln_d(PLATFORM.Name(), " executing engage-target, T=", TIME_NOW);
#extern WsfTrack GetTrackByName(WsfPlatform, string);
#check all possible targets on all channels
########################################################################
### fire on any pursue-target jobs we are assigned to
########################################################################
WsfRIPRProcessor commander = ((WsfRIPRProcessor)PROCESSOR).GetRIPRCommanderProcessor();
for (int channel = 0; channel < ((WsfRIPRProcessor)PROCESSOR).GetNumJobChannels(); channel = channel + 1)
{
WsfRIPRJob aJob = commander.GetJobFor(((WsfRIPRProcessor)PROCESSOR), channel);
if (aJob.IsValid() &&
aJob.Name() == "pursue-target")
{
string targetName = (string)aJob.GetData("targetTrackName");
WsfTrack targetTrack = GetTrackByName(PLATFORM, targetName);
if (!targetTrack.IsNull() &&
targetTrack.IsValid())
{
writeln_d(" ", PLATFORM.Name(), " trying to shoot at job track: ", targetName, " at time: ", TIME_NOW);
if(FireWeapon(targetTrack) == false)
{
writeln_d(" ", PLATFORM.Name(), " could NOT fire at track: ", targetTrack.TargetName(), " at time: ", TIME_NOW);
}
}
}
}
end_execute
end_behavior