# **************************************************************************** # 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 mMustShootAtCategories = Array(); 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 ThreatTypeRequiredTrackQuality = Map(); ThreatTypeRequiredTrackQuality["bomber"] = 0.49; ThreatTypeRequiredTrackQuality["fighter"] = 0.49; //fire off different salvos at different types of threats int DefaultAirSalvo = 1; int DefaultGndSalvo = 1; Map ThreatTypeSalvo = Map(); 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 ThreatTypeWeapon = Map(); //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> WeaponThreatRmaxMap = Map>(); WeaponThreatRmaxMap["base_weapon"] = Map(); WeaponThreatRmaxMap["base_weapon"].Set("fighter", 0.80); //specify max firing angles based on weapon used and threat engaged double DefaultMaxFiringAngle = 45.0; Map> WeaponThreatAngleMap = Map>(); WeaponThreatAngleMap["base_weapon"] = Map(); 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 mTrackWeaponActiveMap = Map(); Map mRoundsFiredAtMap = Map(); //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 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 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); 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> mWeaponArray; foreach (Map 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 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 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> mWeaponArray; #extern int GetWeaponNumberActiveMax(string, Array>); 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