init
This commit is contained in:
683
processors/timeline_agents/air_combat_maneuvers.txt
Normal file
683
processors/timeline_agents/air_combat_maneuvers.txt
Normal file
@@ -0,0 +1,683 @@
|
||||
# ****************************************************************************
|
||||
# CUI
|
||||
#
|
||||
# The Advanced Framework for Simulation, Integration, and Modeling (AFSIM)
|
||||
#
|
||||
# The use, dissemination or disclosure of data in this file is subject to
|
||||
# limitation or restriction. See accompanying README and LICENSE for details.
|
||||
# ****************************************************************************
|
||||
# * * ************************************** * *
|
||||
# * ****** Demonstration input file ****** *
|
||||
# * ****** UNCLASSIFIED ****** *
|
||||
# * * ************************************** * *
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# This files defines air combat maneuvers to be used
|
||||
# with a timeline agent. Maenuvers defined are:
|
||||
# - engage/re-engage/pursue/push maneuver
|
||||
# - beam maneuver
|
||||
# - crank maneuver
|
||||
# - drag maneuver
|
||||
# Also included is a TurnHeading method that provides a new
|
||||
# relative heading to turn to based on maneuver conditions.
|
||||
# --------------------------------------------------------------
|
||||
|
||||
script_interface
|
||||
|
||||
script_debug_writes off
|
||||
|
||||
# Returns a relative heading (degrees) to turn
|
||||
# relative to a track based on inputs
|
||||
# aPlatform - the platform to get the new relative heading for
|
||||
# aTrack - the track the new relative heading is in relation to
|
||||
# aTurnAngle - the angle in degrees to turn relative to the track
|
||||
# aTolerance - the values in degrees (+/-) within which the Platform will
|
||||
# already be considered to be aTurnAngle from aTrack
|
||||
script double TurnHeading(WsfPlatform aPlatform,
|
||||
WsfTrack aTrack,
|
||||
double aTurnAngle,
|
||||
double aTolerance)
|
||||
# Variable to store new relative heading return value
|
||||
# Defualt to 0 degrees, no turn
|
||||
double heading = 0;
|
||||
|
||||
if (aPlatform.IsValid() && aTrack.IsValid())
|
||||
{
|
||||
double relativeBearing = aPlatform.RelativeBearingTo(aTrack);
|
||||
writeln_d("T= ", TIME_NOW);
|
||||
writeln_d(" ", aPlatform.Name(), " heading: ", aPlatform.Heading(), " deg.");
|
||||
writeln_d(" ", aPlatform.Name(), " relative bearing to track: ", relativeBearing, " deg.");
|
||||
|
||||
# Check that relative bearing is not already within tolerance of
|
||||
# aTurnAngle and no need to change heading
|
||||
if (!Math.AngleIsBetween(Math.Fabs(relativeBearing), aTurnAngle - aTolerance, aTurnAngle + aTolerance))
|
||||
{
|
||||
if (relativeBearing > 0 )
|
||||
{
|
||||
# Turn left
|
||||
heading = -aTurnAngle + relativeBearing;
|
||||
}
|
||||
else
|
||||
{
|
||||
# Turn right
|
||||
heading = aTurnAngle + relativeBearing;
|
||||
}
|
||||
|
||||
# Because we are adding angle, make sure relative heading is valid
|
||||
heading = Math.NormalizeAngleMinus180_180(heading);
|
||||
writeln_d(" ", aPlatform.Name(), " new relative heading: ", heading, " deg.");
|
||||
}
|
||||
else
|
||||
{
|
||||
writeln_d(" ", aPlatform.Name(), " already within ",
|
||||
aTolerance, " deg of ", aTurnAngle, " deg to track." );
|
||||
}
|
||||
}
|
||||
|
||||
return heading;
|
||||
end_script
|
||||
|
||||
# Calculate the relative positioning of aPlatform and aTrack
|
||||
# Borrowed from common_platform_script
|
||||
# aPlatform - the platform of interest for relative position
|
||||
# aTrack - the track to determine the relative position to
|
||||
# with respect to the platform
|
||||
# aAngleTolerance - angle in degrees used to bound how close
|
||||
# two heading are to be considered the same
|
||||
# returns a string indicating the relative positioning
|
||||
# (head-to-head, head-to-tail, etc.)
|
||||
script string GetPositioning(WsfPlatform aPlatform, WsfTrack aTrack, double aAngleTolerance)
|
||||
# Are we heading the same direction?
|
||||
bool sameHeading = false;
|
||||
bool oppHeading = false;
|
||||
bool oppHeadingValid = aTrack.HeadingValid();
|
||||
double headingDiff = MATH.Fabs(MATH.NormalizeAngleMinus180_180(aPlatform.Heading() - aTrack.Heading()));
|
||||
|
||||
if (oppHeadingValid && headingDiff < aAngleTolerance)
|
||||
{
|
||||
sameHeading = true;
|
||||
}
|
||||
if (oppHeadingValid && headingDiff > (180 - aAngleTolerance))
|
||||
{
|
||||
oppHeading = true;
|
||||
}
|
||||
|
||||
# Is either one of us pointing at the other?
|
||||
# Apparently bearing is always valid (?)
|
||||
bool pointingMeYou = false;
|
||||
bool pointingYouMe = false;
|
||||
|
||||
double pMeYou = MATH.Fabs(aPlatform.RelativeBearingTo(aTrack));
|
||||
double pYouMe = MATH.Fabs(aTrack.RelativeBearingTo(aPlatform));
|
||||
|
||||
if (pMeYou < aAngleTolerance)
|
||||
{
|
||||
pointingMeYou = true;
|
||||
}
|
||||
if (pYouMe < aAngleTolerance)
|
||||
{
|
||||
pointingYouMe = true;
|
||||
}
|
||||
|
||||
# Put them together and we've got relative positioning
|
||||
string positioning = "";
|
||||
if (sameHeading && pointingMeYou)
|
||||
{
|
||||
positioning = "head-to-tail";
|
||||
}
|
||||
else if (sameHeading && pointingYouMe)
|
||||
{
|
||||
positioning = "tail-to-head";
|
||||
}
|
||||
else if (oppHeading && (pointingMeYou || pointingYouMe))
|
||||
{
|
||||
positioning = "head-to-head";
|
||||
}
|
||||
else if (pointingMeYou && pointingYouMe)
|
||||
{
|
||||
positioning = "head-to-head";
|
||||
}
|
||||
else if (oppHeading)
|
||||
{
|
||||
positioning = "tail-to-tail";
|
||||
}
|
||||
else if (pointingMeYou)
|
||||
{
|
||||
positioning = "me-facing-target";
|
||||
}
|
||||
else if (pointingYouMe)
|
||||
{
|
||||
positioning = "target-facing-me";
|
||||
}
|
||||
else
|
||||
{
|
||||
positioning = "none";
|
||||
}
|
||||
# Debug, in case angle math is bad
|
||||
# writeln_d(" ", aPlatform.Name(), " to ", aTrack.TargetName(), ", headingDiff: ", headingDiff, ", pMeYou: ", pMeYou, ", pYouMe: ", pYouMe);
|
||||
# writeln_d(" meHeading: ", aPlatform.Heading(), ", youHeading: ", aTrack.Heading());
|
||||
# writeln_d(" sameHeading: ", sameHeading, ", oppHeading: ", oppHeading,
|
||||
# ", pointingMeYou: ", pointingMeYou, ", pointingYouMe: ", pointingYouMe,
|
||||
# ", positioning: ", positioning);
|
||||
|
||||
return positioning;
|
||||
end_script
|
||||
|
||||
|
||||
# Commands passed in platform to do an engage maneuver
|
||||
# Relative to passed in track
|
||||
# Mode (pure, lead) input string determines if mode will be
|
||||
# pure pursuit or lead to predicted intercept point
|
||||
# Return true if engage maneuver command is successful
|
||||
script bool Engage(WsfPlatform aPlatform, WsfTrack aTrack, string aMode, double aSpeed, double aAlt, double aGs)
|
||||
# Variables used in the script to generate the maneuver.
|
||||
# Change these to modify the behavior.
|
||||
|
||||
double leadTime = 15.0; #sec
|
||||
|
||||
WsfGeoPoint targetPoint = WsfGeoPoint();
|
||||
|
||||
if (!aPlatform.IsValid() || !aTrack.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
# Determine location to fly toward based on pursuit mode
|
||||
if (aMode == "intercept")
|
||||
{
|
||||
WsfWaypoint wpt = WsfWaypoint();
|
||||
double tti = aPlatform.InterceptLocation3D(aTrack, wpt);
|
||||
if (tti > 0.0)
|
||||
{
|
||||
targetPoint = wpt.Location();
|
||||
}
|
||||
else
|
||||
{
|
||||
targetPoint = aTrack.LocationAtTime(TIME_NOW + leadTime);
|
||||
}
|
||||
}
|
||||
else if (aMode == "lead")
|
||||
{
|
||||
targetPoint = aTrack.LocationAtTime(TIME_NOW + leadTime);
|
||||
}
|
||||
else #if (aMode == "pure")
|
||||
{
|
||||
# All other input will be considered 'pure' pursuit
|
||||
targetPoint = aTrack.LocationAtTime(TIME_NOW);
|
||||
}
|
||||
|
||||
double relativeBearing = aPlatform.RelativeBearingTo(targetPoint);
|
||||
|
||||
# Fly determined course
|
||||
double radialAccel = aGs * Earth.ACCEL_OF_GRAVITY();
|
||||
bool ok = aPlatform.TurnToRelativeHeading(relativeBearing, radialAccel);
|
||||
ok = ok && aPlatform.GoToSpeed(aSpeed);
|
||||
ok = ok && aPlatform.GoToAltitude(aAlt);
|
||||
return ok;
|
||||
end_script
|
||||
|
||||
# Commands passed in platform to do a fire maneuver
|
||||
# Relative to passed in track
|
||||
# Return true if fire maneuver command is successful
|
||||
script bool FireManeuver(WsfPlatform aPlatform, WsfTrack aTrack)
|
||||
|
||||
# Variables used in the script to generate the maneuver.
|
||||
# Change these to modify the behavior.
|
||||
# TODO make method arguments?
|
||||
double fireMach = 1.5; # Speed to fire weapon
|
||||
double pitchAngle = 15.0; # Degrees of pitch up (loft shot)
|
||||
double distance = 100000.0; # Distance in meters to new altitude point
|
||||
|
||||
if (!aPlatform.IsValid() || !aTrack.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
# Determine heading toward track
|
||||
double relativeBearing = aPlatform.RelativeBearingTo(aTrack);
|
||||
|
||||
# Determine new altitude to give desired pitch angle
|
||||
# Arbitrary point that is pitch angle up, 100 km distant
|
||||
double altitudeDelta = distance * Math.Tan(pitchAngle);
|
||||
double newAltitude = aPlatform.Altitude() + altitudeDelta;
|
||||
|
||||
# Determine an approximate climb rate based on
|
||||
# desired angle and new point
|
||||
double speed = aPlatform.Speed();
|
||||
double time = distance / speed;
|
||||
double climbRate = altitudeDelta / time;
|
||||
|
||||
# Fly determined course
|
||||
bool ok = aPlatform.TurnToRelativeHeading(relativeBearing);
|
||||
ok = ok && aPlatform.GoToMachNumber(fireMach);
|
||||
ok = ok && aPlatform.GoToAltitude(newAltitude, climbRate);
|
||||
return ok;
|
||||
end_script
|
||||
|
||||
# Commands passed in platform to do a beam maneuver
|
||||
# Relative to passed in track
|
||||
# Return final heading if crank maneuver command is successful
|
||||
# or -1 if unsuccessful
|
||||
script double Beam(WsfPlatform aPlatform, WsfTrack aTrack)
|
||||
|
||||
# Variables used in the script to generate the maneuver.
|
||||
# Change these to modify the behavior.
|
||||
# Angle in degrees to turn to get to "beam" on track.
|
||||
double beamAngle = 90;
|
||||
# Min/max angle in degrees that aircraft needs to be
|
||||
# between relative to track to be considered on "beam"
|
||||
double beamTolerance = 1.5;
|
||||
|
||||
if (!aPlatform.IsValid() || !aTrack.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
# Heading when we have finished maneuver
|
||||
double finalHeading = 0;
|
||||
|
||||
# Determine new heading
|
||||
if (aTrack.HeadingValid())
|
||||
{
|
||||
# Check for condition when we know track's heading
|
||||
# this gives a more accurate "beam" to threat course
|
||||
if (aPlatform.RelativeBearingTo(aTrack) > 0)
|
||||
{
|
||||
# Turn left
|
||||
finalHeading = aTrack.Heading() + beamAngle;
|
||||
}
|
||||
else
|
||||
{
|
||||
# Turn right
|
||||
finalHeading = aTrack.Heading() - beamAngle;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
# Determine a relative heading based only on the track's location
|
||||
double relativeHeading = TurnHeading(aPlatform, aTrack, beamAngle, beamTolerance);
|
||||
finalHeading = Math.NormalizeAngle0_360(aPlatform.Heading() + relativeHeading);
|
||||
}
|
||||
|
||||
# Turn to new heading, maintaining speed and altitude
|
||||
bool ok = aPlatform.TurnToHeading(finalHeading);
|
||||
if (!ok)
|
||||
{
|
||||
finalHeading = -1.0;
|
||||
}
|
||||
return finalHeading;
|
||||
end_script
|
||||
|
||||
# Commands passed in platform to do a crank maneuver
|
||||
# Relative to passed in track
|
||||
# Return final heading if drag maneuver command is successful
|
||||
# or -1 if unsuccessful
|
||||
script double Crank(WsfPlatform aPlatform, WsfTrack aTrack, double crankAngle, double aSpeed)
|
||||
|
||||
# Variables used in the script to generate the maneuver.
|
||||
# Change these to modify the behavior.
|
||||
# TODO make method arguments?
|
||||
# Angle in degrees to offset track. Should be close to,
|
||||
# but not at, max radar azimuth
|
||||
#double crankAngle = 35;
|
||||
# Min/max angle in degrees that aircraft needs to be
|
||||
# between relative to track to be considered cranking
|
||||
double crankTolerance = 1.5;
|
||||
|
||||
if (!aPlatform.IsValid() || !aTrack.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
double relativeHeading = TurnHeading(aPlatform, aTrack, crankAngle, crankTolerance);
|
||||
|
||||
# Heading when we have finished maneuver
|
||||
double finalHeading = Math.NormalizeAngleMinus180_180(aPlatform.Heading() + relativeHeading);
|
||||
|
||||
# Turn to new heading, maintaining speed and altitude
|
||||
bool ok = aPlatform.TurnToRelativeHeading(relativeHeading);
|
||||
ok = ok && aPlatform.GoToSpeed(aSpeed);
|
||||
if (!ok)
|
||||
{
|
||||
finalHeading = -1.0;
|
||||
}
|
||||
return finalHeading;
|
||||
end_script
|
||||
|
||||
# Commands passed in platform to do a drag maneuver
|
||||
# Relative to passed in track
|
||||
# aSpeed - speed (m/s) to drag at
|
||||
# aAlt - Altitude to offset from current altitude
|
||||
# Return final heading if drag maneuver command is successful
|
||||
# or -1 if unsuccessful
|
||||
script double Drag(WsfPlatform aPlatform, WsfTrack aTrack, double aSpeed, double aAlt, double aGs)
|
||||
|
||||
# Variables used in the script to generate the maneuver.
|
||||
# Change these to modify the behavior.
|
||||
# TODO make method arguments?
|
||||
# Flight parameters when dragging for speed (Mach) and altitude offest (meters) from start
|
||||
double dragMach = 1.1;
|
||||
# Angle in degrees to turn to drag on track.
|
||||
double dragAngle = 179;
|
||||
# Min/max angle in degrees that aircraft needs to be
|
||||
# between relative to track to be considered dragging
|
||||
double dragTolerance = 5;
|
||||
|
||||
if (!aPlatform.IsValid() || !aTrack.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
# Heading when we have finished maneuver
|
||||
double finalHeading = 0;
|
||||
# Determine new heading for drag maneuver
|
||||
string positioning = GetPositioning(aPlatform, aTrack, dragTolerance);
|
||||
if (aTrack.HeadingValid() &&
|
||||
(positioning == "tail-to-head" ||
|
||||
positioning == "head-to-head" ||
|
||||
positioning == "target-facing-me")
|
||||
)
|
||||
{
|
||||
# Check for condition when we know track's heading and
|
||||
# track is facing platform.
|
||||
# This gives a more accurate "drag" to threat course
|
||||
finalHeading = aTrack.Heading();
|
||||
}
|
||||
else
|
||||
{
|
||||
# Don't know track heading or not facing platform, turn
|
||||
# based on relative bearing to track location
|
||||
double relativeHeading = TurnHeading(aPlatform, aTrack, dragAngle, dragTolerance);
|
||||
finalHeading = Math.NormalizeAngle0_360(aPlatform.Heading() + relativeHeading);
|
||||
}
|
||||
|
||||
# Fly new course
|
||||
double radialAccel = aGs * Earth.ACCEL_OF_GRAVITY();
|
||||
bool ok = aPlatform.TurnToHeading(finalHeading, radialAccel);
|
||||
ok = ok && aPlatform.GoToSpeed(aSpeed);
|
||||
ok = ok && aPlatform.GoToAltitude(aAlt);
|
||||
if (!ok)
|
||||
{
|
||||
finalHeading = -1.0;
|
||||
}
|
||||
return finalHeading;
|
||||
end_script
|
||||
|
||||
# Do a 180 degree turn in the specified direction,
|
||||
# at specified mach, maintaining altitude
|
||||
script double TurnDirection(WsfPlatform aPlatform, string aDirection, double aSpeed, double aGs)
|
||||
# Angle in degrees to turn.
|
||||
double turnAngle = 179.5;
|
||||
# Heading when we have finished maneuver
|
||||
double finalHeading = 0;
|
||||
|
||||
if (aDirection == "left")
|
||||
{
|
||||
finalHeading = Math.NormalizeAngle0_360(aPlatform.Heading() - turnAngle);
|
||||
}
|
||||
else
|
||||
{ # "right"
|
||||
finalHeading = Math.NormalizeAngle0_360(aPlatform.Heading() + turnAngle);
|
||||
}
|
||||
|
||||
# Fly new course
|
||||
double radialAccel = aGs * Earth.ACCEL_OF_GRAVITY();
|
||||
bool ok = aPlatform.TurnToHeading(finalHeading, radialAccel);
|
||||
ok = ok && aPlatform.GoToSpeed(aSpeed);
|
||||
ok = ok && aPlatform.GoToAltitude(aPlatform.Altitude());
|
||||
if (!ok)
|
||||
{
|
||||
finalHeading = -1.0;
|
||||
}
|
||||
return finalHeading;
|
||||
end_script
|
||||
|
||||
# Commands passed in platform to do a post hole maneuver
|
||||
# Direction (left/right) controls direction of post hole
|
||||
# Return final heading if drag maneuver command is successful
|
||||
# or -1 if unsuccessful
|
||||
script double PostHole(WsfPlatform aPlatform, string aDirection)
|
||||
# Distance in meters to offset the range to the post hole waypoint
|
||||
double offsetRange = 3000;
|
||||
# Angle down (deg) to offset post hole waypoint
|
||||
double offsetElevation = -75.0;
|
||||
|
||||
if (!aPlatform.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
# Use left/right direction to determine the offset bearing for
|
||||
# the post hole waypoint. Heading + 120 deg will put the point
|
||||
# slightly behind current location
|
||||
double offsetBearing = 0;
|
||||
if (aDirection == "left")
|
||||
{
|
||||
offsetBearing = Math.NormalizeAngleMinus180_180(aPlatform.Heading() + 120);
|
||||
}
|
||||
else
|
||||
{
|
||||
offsetBearing = Math.NormalizeAngleMinus180_180(aPlatform.Heading() - 120);
|
||||
}
|
||||
|
||||
# Build point to fly to for post hole
|
||||
WsfGeoPoint pt = aPlatform.Location();
|
||||
pt.OffsetRBE(offsetRange, offsetBearing, offsetElevation);
|
||||
|
||||
# Create waypoint so we can specify final heading/speed
|
||||
WsfWaypoint waypoint = WsfWaypoint();
|
||||
waypoint.SetHeading(aPlatform.Heading());
|
||||
waypoint.SetSpeed(aPlatform.Speed());
|
||||
waypoint.SetLocation(pt);
|
||||
|
||||
# Build route from post hole waypoint
|
||||
WsfRoute newRoute = WsfRoute();
|
||||
newRoute.Append(waypoint);
|
||||
|
||||
# Heading when we have finished maneuver
|
||||
double finalHeading = aPlatform.Heading();
|
||||
|
||||
bool ok = aPlatform.FollowRoute(newRoute);
|
||||
if (!ok)
|
||||
{
|
||||
finalHeading = -1.0;
|
||||
}
|
||||
return finalHeading;
|
||||
end_script
|
||||
|
||||
|
||||
|
||||
script double GetTurnRateLimit(WsfPlatform aPlatform)
|
||||
Array<double> turnRadiusArray = aPlatform.Mover().PropertyDouble("turn_radius");
|
||||
|
||||
double turnRateLimit = 0;
|
||||
if (turnRadiusArray.Size() > 0)
|
||||
{
|
||||
double turnRate = turnRadiusArray[0];
|
||||
double speed = aPlatform.Speed();
|
||||
turnRateLimit = speed/turnRate;
|
||||
turnRateLimit = turnRateLimit * MATH.DEG_PER_RAD();
|
||||
}
|
||||
return turnRateLimit;
|
||||
end_script
|
||||
|
||||
script double GetDefaultG(WsfPlatform aPlatform)
|
||||
Array<double> defaultAccelArray = aPlatform.Mover().PropertyDouble("default_radial_acceleration");
|
||||
if (defaultAccelArray.Size() > 0)
|
||||
{
|
||||
return defaultAccelArray[0];
|
||||
}
|
||||
return 0.0;
|
||||
end_script
|
||||
|
||||
script double TimeToPerformBeam(WsfPlatform aPlatform, WsfTrack aTrack)
|
||||
|
||||
# Variables used in the script to generate the maneuver.
|
||||
# Change these to modify the behavior.
|
||||
# Angle in degrees to turn to get to "beam" on track.
|
||||
double beamAngle = 90;
|
||||
# Min/max angle in degrees that aircraft needs to be
|
||||
# between relative to track to be considered on "beam"
|
||||
double beamTolerance = 1.5;
|
||||
|
||||
if (!aPlatform.IsValid() || !aTrack.IsValid())
|
||||
{
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
# Heading when we have finished maneuver
|
||||
double finalHeading = 0;
|
||||
|
||||
# Determine new heading
|
||||
if (aTrack.HeadingValid())
|
||||
{
|
||||
# Check for condition when we know track's heading
|
||||
# this gives a more accurate "beam" to threat course
|
||||
if (aPlatform.RelativeBearingTo(aTrack) > 0)
|
||||
{
|
||||
# Turn left
|
||||
finalHeading = aTrack.Heading() + beamAngle;
|
||||
}
|
||||
else
|
||||
{
|
||||
# Turn right
|
||||
finalHeading = aTrack.Heading() - beamAngle;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
# Determine a relative heading based only on the track's location
|
||||
double relativeHeading = TurnHeading(aPlatform, aTrack, beamAngle, beamTolerance);
|
||||
finalHeading = Math.NormalizeAngle0_360(aPlatform.Heading() + relativeHeading);
|
||||
}
|
||||
|
||||
|
||||
//calculate time to perform the beam given the calculated heading
|
||||
|
||||
#RadialAccel = Speed^2/Radius
|
||||
|
||||
double RadialAccel = GetDefaultG(aPlatform) * Earth.ACCEL_OF_GRAVITY();
|
||||
if (RadialAccel < 2.0)
|
||||
{
|
||||
RadialAccel = 2.0 * Earth.ACCEL_OF_GRAVITY();
|
||||
}
|
||||
|
||||
double TurnAzimuth = MATH.Fabs(finalHeading);
|
||||
double Speed = aPlatform.Speed();
|
||||
double Radius = Speed * Speed / RadialAccel;
|
||||
double TurningCircleCircumference = 2.0 * MATH.PI() * Radius;
|
||||
double TurningArc = (TurnAzimuth/360) * TurningCircleCircumference;
|
||||
double TimeToTurn = TurningArc / Speed;
|
||||
return TimeToTurn;
|
||||
|
||||
|
||||
end_script
|
||||
|
||||
script double TimeToPerformCrank(WsfPlatform aPlatform, WsfTrack aTrack)
|
||||
|
||||
# Variables used in the script to generate the maneuver.
|
||||
# Change these to modify the behavior.
|
||||
# TODO make method arguments?
|
||||
# Angle in degrees to offset track. Should be close to,
|
||||
# but not at, max radar azimuth
|
||||
double crankAngle = 35;
|
||||
# Min/max angle in degrees that aircraft needs to be
|
||||
# between relative to track to be considered cranking
|
||||
double crankTolerance = 1.5;
|
||||
|
||||
if (!aPlatform.IsValid() || !aTrack.IsValid())
|
||||
{
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
double relativeHeading = TurnHeading(aPlatform, aTrack, crankAngle, crankTolerance);
|
||||
|
||||
# Heading when we have finished maneuver
|
||||
double finalHeading = Math.NormalizeAngleMinus180_180(aPlatform.Heading() + relativeHeading);
|
||||
|
||||
double RadialAccel = GetDefaultG(aPlatform) * Earth.ACCEL_OF_GRAVITY();
|
||||
if (RadialAccel < 2.0)
|
||||
{
|
||||
RadialAccel = 2.0 * Earth.ACCEL_OF_GRAVITY();
|
||||
}
|
||||
|
||||
double TurnAzimuth = MATH.Fabs(finalHeading);
|
||||
double Speed = aPlatform.Speed();
|
||||
double Radius = Speed * Speed / RadialAccel;
|
||||
double TurningCircleCircumference = 2.0 * MATH.PI() * Radius;
|
||||
double TurningArc = (TurnAzimuth/360) * TurningCircleCircumference;
|
||||
double TimeToTurn = TurningArc / Speed;
|
||||
return TimeToTurn;
|
||||
|
||||
end_script
|
||||
|
||||
script double TimeToPerformDrag(WsfPlatform aPlatform, WsfTrack aTrack)
|
||||
|
||||
# Variables used in the script to generate the maneuver.
|
||||
# Change these to modify the behavior.
|
||||
# TODO make method arguments?
|
||||
# Flight parameters when dragging for speed (Mach) and altitude offest (meters) from start
|
||||
double dragMach = 1.1;
|
||||
double dragAltitudeOffset = -1000;
|
||||
# Angle in degrees to turn to drag on track.
|
||||
double dragAngle = 179;
|
||||
# Min/max angle in degrees that aircraft needs to be
|
||||
# between relative to track to be considered dragging
|
||||
double dragTolerance = 5;
|
||||
|
||||
if (!aPlatform.IsValid() || !aTrack.IsValid())
|
||||
{
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
# Heading when we have finished maneuver
|
||||
double finalHeading = 0;
|
||||
# Determine new heading for drag maneuver
|
||||
string positioning = GetPositioning(aPlatform, aTrack, dragTolerance);
|
||||
if (aTrack.HeadingValid() &&
|
||||
(positioning == "tail-to-head" ||
|
||||
positioning == "head-to-head" ||
|
||||
positioning == "target-facing-me")
|
||||
)
|
||||
{
|
||||
# Check for condition when we know track's heading and
|
||||
# track is facing platform.
|
||||
# This gives a more accurate "drag" to threat course
|
||||
finalHeading = aTrack.Heading();
|
||||
}
|
||||
else
|
||||
{
|
||||
# Don't know track heading or not facing platform, turn
|
||||
# based on relative bearing to track location
|
||||
double relativeHeading = TurnHeading(aPlatform, aTrack, dragAngle, dragTolerance);
|
||||
finalHeading = Math.NormalizeAngle0_360(aPlatform.Heading() + relativeHeading);
|
||||
}
|
||||
|
||||
double RadialAccel = GetDefaultG(aPlatform) * Earth.ACCEL_OF_GRAVITY();
|
||||
if (RadialAccel < 2.0)
|
||||
{
|
||||
RadialAccel = 2.0 * Earth.ACCEL_OF_GRAVITY();
|
||||
}
|
||||
double TurnAzimuth = MATH.Fabs(finalHeading);
|
||||
double Speed = aPlatform.Speed();
|
||||
double Radius = Speed * Speed / RadialAccel;
|
||||
double TurningCircleCircumference = 2.0 * MATH.PI() * Radius;
|
||||
double TurningArc = (TurnAzimuth/360) * TurningCircleCircumference;
|
||||
double TimeToTurn = TurningArc / Speed;
|
||||
return TimeToTurn;
|
||||
|
||||
end_script
|
||||
|
||||
|
||||
script void Offset(WsfPlatform flyer, WsfGeoPoint target, double angle, double alt, double speed, double gees)
|
||||
angle = MATH.NormalizeAngleMinus180_180(angle);
|
||||
double offsetTrueBearing = flyer.TrueBearingTo(target) + angle;
|
||||
double radialAccel = gees * Earth.ACCEL_OF_GRAVITY();
|
||||
double climbRate = MATH.Sin(30) * speed;
|
||||
bool ok = flyer.GoToSpeed(speed, 9999, true);
|
||||
ok = ok && flyer.GoToAltitude(alt, climbRate, true);
|
||||
ok = ok && flyer.TurnToHeading(offsetTrueBearing, radialAccel);
|
||||
end_script
|
||||
|
||||
|
||||
end_script_interface
|
||||
271
processors/timeline_agents/behavior_engage_on_timeline.txt
Normal file
271
processors/timeline_agents/behavior_engage_on_timeline.txt
Normal file
@@ -0,0 +1,271 @@
|
||||
# ****************************************************************************
|
||||
# CUI
|
||||
#
|
||||
# The Advanced Framework for Simulation, Integration, and Modeling (AFSIM)
|
||||
#
|
||||
# The use, dissemination or disclosure of data in this file is subject to
|
||||
# limitation or restriction. See accompanying README and LICENSE for details.
|
||||
# ****************************************************************************
|
||||
|
||||
include_once weapon_defs.txt
|
||||
|
||||
behavior engage_on_timeline
|
||||
|
||||
script_debug_writes off
|
||||
|
||||
script_variables
|
||||
//**********************************************************************//
|
||||
//** platform / agent specific shooting parameters **//
|
||||
//**********************************************************************//
|
||||
#bool mCoopEngageOne = false;
|
||||
#bool mCoopEngageOneFlightOnly = false;
|
||||
# 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;
|
||||
|
||||
//**********************************************************************//
|
||||
//** 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);
|
||||
end_script_variables
|
||||
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
#on_init
|
||||
#end_on_init
|
||||
|
||||
|
||||
precondition
|
||||
#writeln_d("precondition engage-target");
|
||||
if (!PROCESSOR.IsA_TypeOf("WSF_QUANTUM_TASKER_PROCESSOR"))
|
||||
{
|
||||
writeln_d("behavior engage... not on quantum tasker processor");
|
||||
return Failure("behavior not attached to a quantum tasker processor!");
|
||||
}
|
||||
|
||||
# todo - try using a state machine to setup the shot ??
|
||||
# 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);
|
||||
# }
|
||||
|
||||
WsfQuantumTaskerProcessor proc = (WsfQuantumTaskerProcessor)PROCESSOR;
|
||||
# WsfTaskList tasks = proc.TasksReceivedOfType("WEAPON");
|
||||
# if (tasks.Count() > 0)
|
||||
# {
|
||||
# writeln_d("behavior engage precondition passes!");
|
||||
# return true;
|
||||
# }
|
||||
# writeln_d("no weapon task target to shoot at!");
|
||||
|
||||
if (GetTimelineTask(proc).IsValid())
|
||||
{
|
||||
writeln_d("behavior engage precondition passes!");
|
||||
return true;
|
||||
}
|
||||
writeln_d("no timeline task to engage!");
|
||||
return Failure("no timeline task to engage!");
|
||||
end_precondition
|
||||
|
||||
|
||||
|
||||
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
|
||||
########################################################################
|
||||
WsfQuantumTaskerProcessor proc = (WsfQuantumTaskerProcessor)PROCESSOR;
|
||||
|
||||
WsfTask timeline = GetTimelineTask(proc);
|
||||
if (timeline.IsValid() && timeline.AuxDataExists("targets"))
|
||||
{
|
||||
Array<WsfTrack> targets = (Array<WsfTrack>)timeline.AuxDataObject("targets");
|
||||
foreach (WsfTrack target in targets)
|
||||
{
|
||||
if (!target.IsValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
#do we need master track?
|
||||
WsfLocalTrack masterTarget = GetMasterTrackByName(PLATFORM, target.TargetName());
|
||||
if (!masterTarget.IsValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
# 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;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# writeln_d (" targetTrack.TrackQuality == ", targetTrack.TrackQuality());
|
||||
# if (targetTrack.TrackQuality() < GetRequiredTrackQualityForThreat(targetTrack))
|
||||
# {
|
||||
# writeln_d(" FAIL: track quality not good enough to fire on target");
|
||||
# return;
|
||||
# }
|
||||
|
||||
if ((PLATFORM.WeaponsPendingFor(masterTarget.TrackId()) + PLATFORM.WeaponsActiveFor(masterTarget.TrackId())) > 0)
|
||||
{
|
||||
writeln_d("already have weapons assigned for target: ", masterTarget.TargetName());
|
||||
continue;
|
||||
}
|
||||
|
||||
WsfWeapon weapon;
|
||||
bool weaponUsable = false;
|
||||
#first weapon found will be used
|
||||
for (int i=0; i < PLATFORM.WeaponCount(); i+=1)
|
||||
{
|
||||
weapon = PLATFORM.WeaponEntry(i);
|
||||
writeln_d("checking if weapon ", weapon.Name(), " is usable.");
|
||||
if (WeaponCapableAvailableAgainstThreat(weapon, masterTarget) &&
|
||||
InRangeToFire(PLATFORM, weapon, masterTarget, GetLaunchPercentRangeMaxOnThreat(weapon.Name(), masterTarget), DefaultPercentRangeMin))
|
||||
{
|
||||
weaponUsable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (weaponUsable == false)
|
||||
{
|
||||
writeln_d("no usable weapon found!");
|
||||
continue;
|
||||
}
|
||||
|
||||
#int salvoCount = GetSalvoForThreat(targetTrack);
|
||||
#writeln_d(" salvo count for ", targetTrack, " is: ", salvoCount);
|
||||
int salvoCount = 1;
|
||||
bool launched = false;
|
||||
if (weapon.IsTurnedOn())
|
||||
{
|
||||
writeln_d(" Attempting launch at ", masterTarget.TargetName());
|
||||
# if (salvoCount > 1)
|
||||
# {
|
||||
# writeln_d("FIRING SALVO AT ", masterTarget.TargetName());
|
||||
# launched = weapon.FireSalvo(masterTarget, salvoCount);
|
||||
# }
|
||||
# else
|
||||
# {
|
||||
# writeln_d("FIRING AT ", masterTarget.TargetName());
|
||||
# launched = weapon.Fire(masterTarget);
|
||||
# }
|
||||
launched = weapon.FireSalvo(masterTarget, salvoCount);
|
||||
}
|
||||
writeln_d(" launched == ", launched, ", weapon: ", weapon.Name());
|
||||
if(launched == false)
|
||||
{
|
||||
writeln_d(" ", PLATFORM.Name(), " could NOT fire at track: ", masterTarget.TargetName(), " at time: ", TIME_NOW);
|
||||
}
|
||||
}
|
||||
}
|
||||
end_execute
|
||||
|
||||
end_behavior
|
||||
470
processors/timeline_agents/behavior_evade.txt
Normal file
470
processors/timeline_agents/behavior_evade.txt
Normal file
@@ -0,0 +1,470 @@
|
||||
# ****************************************************************************
|
||||
# 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.
|
||||
# ****************************************************************************
|
||||
|
||||
|
||||
|
||||
behavior evade
|
||||
|
||||
script_debug_writes off
|
||||
|
||||
script_variables
|
||||
|
||||
double cDEFAULT_ALTITUDE = 9144; // ~30,000 feet
|
||||
double mEngagementAggressiveness = 0.4; // value in range [0, 1]. 1 is suicidal, 0 is very cautious.
|
||||
// used by behavior_in_danger, behavior_evade, & behavior_disengage.
|
||||
//**********************************************************************//
|
||||
//** debugging parameters **//
|
||||
//**********************************************************************//
|
||||
bool mDrawNearestThreat = false;
|
||||
bool mDrawEvasionVector = false;
|
||||
|
||||
//**********************************************************************//
|
||||
//** control / mode of operation parameters **//
|
||||
//**********************************************************************//
|
||||
double weightPeersForEvade = 0.25; //percentage to scale peers influence for evasion vector
|
||||
bool mWobbleLeftRight = true;
|
||||
bool mWobbleUpDown = true;
|
||||
double cFAST_UPDATE_INTERVAL = 0.25;
|
||||
double cSLOW_UPDATE_INTERVAL = 2.0;
|
||||
|
||||
//**********************************************************************//
|
||||
//** flying parameters, for for evasive manuevering **//
|
||||
//**********************************************************************//
|
||||
double cNORMAL_SPEED = 200.0; // m/s
|
||||
double cEVADE_SPEED = 1000.0; // m/s (faster than a speeding bullet...M3.0+)
|
||||
double mAltitudeMin = 1000.0; //meters
|
||||
double mAltitudeMax = 20000.0; //meters
|
||||
double mAltitudeToDiveEvade = 1500.0; //distance to dive (meters) (~5000 ft)
|
||||
double mBankAngleForEvading = 45.0; //banks left & right, back & forth, at this angle, while evading
|
||||
double mMaxClimbRate = 500.0; // meters/second
|
||||
double mVerticalAccel = 1.5 * Earth.ACCEL_OF_GRAVITY(); // meters/second^2
|
||||
|
||||
//**********************************************************************//
|
||||
//********* VARIABLES BELOW THIS LINE ARE NOT FOR USER EDITING *********//
|
||||
//**********************************************************************//
|
||||
double mLastTime = 0.0;
|
||||
double mClimbRate = 0.0;
|
||||
Array<WsfPlatform> mIncoming = Array<WsfPlatform>();
|
||||
WsfDraw mDraw = WsfDraw();
|
||||
bool mDiveDownFlag = true;
|
||||
bool mBankLeftFlag = true;
|
||||
end_script_variables
|
||||
|
||||
script WsfThreatProcessor GetThreatProcessor()
|
||||
for (int i=0; i<PLATFORM.ProcessorCount(); i+=1)
|
||||
{
|
||||
WsfProcessor proc = PLATFORM.ProcessorEntry(i);
|
||||
if (proc.IsValid() && proc.IsA_TypeOf("WSF_THREAT_PROCESSOR"))
|
||||
{
|
||||
return (WsfThreatProcessor)proc;
|
||||
}
|
||||
}
|
||||
WsfThreatProcessor empty;
|
||||
return empty;
|
||||
end_script
|
||||
|
||||
script bool Fly(WsfPlatform flyer, double heading, double altitude, double speed)
|
||||
WsfGeoPoint pt = flyer.Location();
|
||||
pt.Extrapolate(heading, 1852*3);
|
||||
pt.Set(pt.Latitude(), pt.Longitude(), MATH.Max(altitude, mAltitudeMin));
|
||||
if (mDrawEvasionVector == true)
|
||||
{
|
||||
mDraw.SetLayer("behavior_evade");
|
||||
mDraw.SetDuration(PROCESSOR.UpdateInterval());
|
||||
mDraw.SetColor(0,1,0);
|
||||
mDraw.SetLineSize(1);
|
||||
mDraw.SetLineStyle("dash_dot");
|
||||
mDraw.BeginLines();
|
||||
mDraw.Vertex(PLATFORM.Location());
|
||||
mDraw.Vertex(pt);
|
||||
mDraw.End();
|
||||
}
|
||||
if (flyer.Mover().IsValid())
|
||||
{
|
||||
if (flyer.Mover().IsA_TypeOf("WSF_6DOF_MOVER"))
|
||||
{
|
||||
##flyer.GoToSpeed(speed);
|
||||
##flyer.GoToAltitude(MATH.Max(altitude, mAltitudeMin), 200);
|
||||
##flyer.TurnToHeading(heading, 500);
|
||||
#flyer.GoToSpeed(speed);
|
||||
#flyer.GoToLocation(pt);
|
||||
flyer.GoToSpeed(speed);
|
||||
double altRate = flyer.Speed() * MATH.Sin(40.0);
|
||||
flyer.GoToAltitude(pt.Altitude(), altRate);
|
||||
flyer.TurnToHeading(flyer.TrueBearingTo(pt), 500);
|
||||
return true;
|
||||
}
|
||||
else if (flyer.Mover().IsA_TypeOf("WSF_AIR_MOVER"))
|
||||
{
|
||||
double delta = TIME_NOW - mLastTime;
|
||||
mLastTime = TIME_NOW;
|
||||
if (delta > cSLOW_UPDATE_INTERVAL)
|
||||
{
|
||||
delta = cSLOW_UPDATE_INTERVAL;
|
||||
}
|
||||
double deltaV = mVerticalAccel * delta;
|
||||
if (altitude > PLATFORM.Altitude())
|
||||
{
|
||||
if (mClimbRate < 0.0)
|
||||
{
|
||||
//increase climb rate to a positive value (must keep pursuing a lower altitude though)
|
||||
altitude = mAltitudeMin;
|
||||
mClimbRate = mClimbRate + deltaV;
|
||||
}
|
||||
else if (mClimbRate < mMaxClimbRate)
|
||||
{
|
||||
//increase climb rate to max value (can pursue target altitude now)
|
||||
mClimbRate = mClimbRate + deltaV;
|
||||
}
|
||||
}
|
||||
else if (altitude < PLATFORM.Altitude())
|
||||
{
|
||||
if (mClimbRate > 0.0)
|
||||
{
|
||||
//decrease climb rate to a negative value (must keep pursuing a higher altitude though)
|
||||
altitude = 9999999;
|
||||
mClimbRate = mClimbRate - deltaV;
|
||||
}
|
||||
else if (mClimbRate > -mMaxClimbRate)
|
||||
{
|
||||
//decrease climb rate to max value (can pursue target altitude now)
|
||||
mClimbRate = mClimbRate - deltaV;
|
||||
}
|
||||
}
|
||||
double climbRateUsed = MATH.Fabs(mClimbRate);
|
||||
flyer.GoToSpeed(speed);
|
||||
flyer.GoToAltitude(altitude, climbRateUsed);
|
||||
flyer.TurnToHeading(heading);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
writeln_d(flyer.Name(), " aiai error: unknown or invalid mover for flight");
|
||||
return false;
|
||||
end_script
|
||||
|
||||
|
||||
precondition
|
||||
writeln_d(PLATFORM.Name(), " precondition evade, T=", TIME_NOW);
|
||||
|
||||
//always evade incoming weapons
|
||||
int ownshipWeaponsIncoming = 0;
|
||||
WsfProcessor proc = PLATFORM.Processor("incoming_threats");
|
||||
if (proc.IsValid() && proc.ScriptExists("ThreatProcessorWeaponsIncoming"))
|
||||
{
|
||||
writeln_d("using incoming_threats threat processor!!!");
|
||||
mIncoming = (Array<WsfPlatform>)(proc.Execute("ThreatProcessorWeaponsIncoming"));
|
||||
ownshipWeaponsIncoming = mIncoming.Size();
|
||||
}
|
||||
else
|
||||
{
|
||||
writeln_d("using standard WSF_THREAT_PROCESSOR!!!");
|
||||
mIncoming.Clear(); ownshipWeaponsIncoming = 0;
|
||||
|
||||
WsfThreatProcessor threatProc = GetThreatProcessor();
|
||||
if (!threatProc.IsNull() && threatProc.IsValid())
|
||||
{
|
||||
Array<WsfTrackId> threatIds = threatProc.Threats();
|
||||
foreach(WsfTrackId id in threatIds)
|
||||
{
|
||||
WsfTrack track = PLATFORM.MasterTrackList().Find(id);
|
||||
if (track.IsValid())
|
||||
{
|
||||
mIncoming.PushBack(track.Target());
|
||||
}
|
||||
}
|
||||
ownshipWeaponsIncoming = mIncoming.Size();
|
||||
}
|
||||
}
|
||||
|
||||
if (ownshipWeaponsIncoming > 0)
|
||||
{
|
||||
//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 = PLATFORM.WeaponsActiveFor(WsfTrackId());
|
||||
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)
|
||||
WsfPlatformList activeWeapons = PLATFORM.ActiveWeaponPlatformsFor(WsfTrackId());
|
||||
for (int i=0; i< activeWeapons.Count(); i=i+1)
|
||||
{
|
||||
WsfPlatform w = activeWeapons.Entry(i);
|
||||
WsfTrack t = w.CurrentTargetTrack();
|
||||
|
||||
//TODO - replace with check for weather or not the weapon has active sensors
|
||||
# // include weapons we are uplinking to.
|
||||
# if (((WsfRIPRProcessor)PROCESSOR).IsUplinkingTo(w) &&
|
||||
# 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 = asset / (threat + asset);
|
||||
writeln_d(PLATFORM.Name(), " threat: ", threat, ", asset: ", asset, ", required aggressiveness: ", requiredAggressiveness);
|
||||
if (mEngagementAggressiveness < requiredAggressiveness)
|
||||
{
|
||||
#PROCESSOR.SetUpdateInterval(cFAST_UPDATE_INTERVAL);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Failure("my active weapons are closer, hold course");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#PROCESSOR.SetUpdateInterval(cFAST_UPDATE_INTERVAL);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
writeln_d("no weapons incoming");
|
||||
Failure("no weapons incoming");
|
||||
}
|
||||
|
||||
#PROCESSOR.SetUpdateInterval(cSLOW_UPDATE_INTERVAL);
|
||||
return false;
|
||||
end_precondition
|
||||
|
||||
on_new_execute
|
||||
PLATFORM.Comment("evade");
|
||||
end_on_new_execute
|
||||
|
||||
execute
|
||||
writeln_d(PLATFORM.Name(), " executing evade, T=", TIME_NOW);
|
||||
//PLATFORM.Comment("evade");
|
||||
|
||||
### transition to evade incoming
|
||||
PLATFORM.GoToSpeed(cEVADE_SPEED);
|
||||
writeln_d(" GoToSpeed( ", cEVADE_SPEED," )");
|
||||
|
||||
double safeAltitudeToDiveTo = MATH.Max(mAltitudeMin, (cDEFAULT_ALTITUDE - mAltitudeToDiveEvade));
|
||||
|
||||
// Calculate a heading to evade all incoming missiles, weighted by distance, and then turn to it
|
||||
|
||||
double evadeHeading = 0;
|
||||
double evadeAltitude = 0;
|
||||
double evadeDivisor = 0;
|
||||
double distMod = 0;
|
||||
double bearingMod = 0;
|
||||
int incomingCount = mIncoming.Size();
|
||||
|
||||
writeln_d(PLATFORM.Name(), " evade. dive/climb between altitudes: ", cDEFAULT_ALTITUDE, " <-> ", safeAltitudeToDiveTo);
|
||||
writeln_d("evading ", incomingCount, " threats");
|
||||
|
||||
double y = 0;
|
||||
double x = 0;
|
||||
double dist = MATH.DOUBLE_MAX();
|
||||
WsfPlatform nearestThreat;
|
||||
// calculate an average heading to incoming platforms weighted by distance
|
||||
for (int i = 0; i < incomingCount; i = i + 1)
|
||||
{
|
||||
WsfPlatform temp = (WsfPlatform)(mIncoming[i]);
|
||||
if (!temp.IsValid() || (temp.Index() == PLATFORM.Index()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
double range = PLATFORM.SlantRangeTo(temp);
|
||||
if (range < dist)
|
||||
{
|
||||
dist = range;
|
||||
nearestThreat = (WsfPlatform)(mIncoming[i]);
|
||||
}
|
||||
if (range > 0)
|
||||
{
|
||||
distMod = 1 / range;
|
||||
}
|
||||
else
|
||||
{
|
||||
distMod = 1000000;
|
||||
}
|
||||
bearingMod = MATH.NormalizeAngle0_360(PLATFORM.TrueBearingTo(temp));
|
||||
x = x + MATH.Sin(bearingMod) * distMod;
|
||||
y = y + MATH.Cos(bearingMod) * distMod;
|
||||
|
||||
writeln_d(" Incoming ", temp.Name(), " at distance: ", 1 / distMod, ", bearing: ", bearingMod, ", x: ", MATH.Sin(bearingMod), ", y: ", MATH.Cos(bearingMod), ", (", temp.Latitude(), ", ", temp.Longitude(), ", ", temp.Altitude(), ") @ ", temp.Speed(), "m/s");
|
||||
evadeDivisor = evadeDivisor + distMod;
|
||||
evadeHeading = evadeHeading + bearingMod * distMod;
|
||||
}
|
||||
|
||||
# // get the GCI if there is one
|
||||
WsfPlatform aiflPlat = PLATFORM.Commander();
|
||||
WsfPlatform gciPlat;
|
||||
if (aiflPlat.IsValid())
|
||||
{
|
||||
gciPlat = aiflPlat.Commander();
|
||||
}
|
||||
|
||||
// if there's a gci, avoid centroids of all his subs
|
||||
if (gciPlat.IsValid() && gciPlat.Index() != aiflPlat.Index())
|
||||
{
|
||||
writeln_d("found gci : ", gciPlat.Name());
|
||||
foreach (WsfPlatform aifl in gciPlat.Subordinates())
|
||||
{
|
||||
if (!aifl.IsValid() || aifl == PLATFORM || aifl == aiflPlat)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
WsfGeoPoint centroid = aifl.GetSubsCentroid();
|
||||
if (centroid.X() == 0 && centroid.Y() == 0 && centroid.Z() == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
distMod = 1 / (PLATFORM.SlantRangeTo(centroid));
|
||||
bearingMod = MATH.NormalizeAngle0_360(PLATFORM.TrueBearingTo(centroid));
|
||||
x = x + MATH.Sin(bearingMod) * distMod * weightPeersForEvade;
|
||||
y = y + MATH.Cos(bearingMod) * distMod * weightPeersForEvade;
|
||||
|
||||
writeln_d(" AIFL ", aifl.Name(), " at distance: ", 1 / distMod, ", bearing: ", bearingMod, ", x: ", MATH.Sin(bearingMod), ", y: ", MATH.Cos(bearingMod), ", (", centroid.Latitude(), ", ", centroid.Longitude(), ", ", centroid.Altitude(), ")");
|
||||
|
||||
// but we'll avoid peers with less strength than we avoid incoming missiles
|
||||
// so let's divide the distMod by 2
|
||||
evadeDivisor = evadeDivisor + distMod;
|
||||
evadeHeading = evadeHeading + bearingMod * distMod;
|
||||
}
|
||||
}
|
||||
// otherwise, avoid members in your own squadron
|
||||
else if (aiflPlat.IsValid())
|
||||
{
|
||||
writeln_d("also evading flight lead subordinates (peers)");
|
||||
foreach (WsfPlatform sub in aiflPlat.Subordinates())
|
||||
{
|
||||
writeln_d("considering peer ", sub.Name());
|
||||
if (!sub.IsValid() ||
|
||||
sub == PLATFORM)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
distMod = 1 / (PLATFORM.SlantRangeTo(sub));
|
||||
bearingMod = MATH.NormalizeAngle0_360(PLATFORM.TrueBearingTo(sub));
|
||||
x = x + MATH.Sin(bearingMod) * distMod * weightPeersForEvade;
|
||||
y = y + MATH.Cos(bearingMod) * distMod * weightPeersForEvade;
|
||||
|
||||
writeln_d(" Peer ", sub.Name(), " at distance: ", 1 / distMod, ", bearing: ", bearingMod, ", x: ", MATH.Sin(bearingMod), ", y: ", MATH.Cos(bearingMod), ", (", sub.Latitude(), ", ", sub.Longitude(), ", ", sub.Altitude(), ") @ ", sub.Speed(), "m/s");
|
||||
|
||||
// but we'll avoid peers with less strength than we avoid incoming missiles
|
||||
// so let's divide the distMod by 2
|
||||
evadeDivisor = evadeDivisor + distMod;
|
||||
evadeHeading = evadeHeading + bearingMod * distMod;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
writeln_d("evade : no commanders found!!!");
|
||||
}
|
||||
|
||||
if (evadeDivisor == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// correct for quadrant
|
||||
double theta = MATH.NormalizeAngle0_360(MATH.ATan(x/y));
|
||||
if (y < 0)
|
||||
{
|
||||
theta = MATH.NormalizeAngle0_360(theta - 180);
|
||||
}
|
||||
|
||||
writeln_d(" x: ", x, ", y: ", y, ", theta: ", theta);
|
||||
|
||||
evadeHeading = MATH.NormalizeAngle0_360(theta - 180);
|
||||
|
||||
if (mWobbleLeftRight == true)
|
||||
{
|
||||
//turn to angle to evade
|
||||
if (mBankLeftFlag == true)
|
||||
{
|
||||
evadeHeading = MATH.NormalizeAngle0_360(theta - 180 - mBankAngleForEvading);
|
||||
double headDiff = MATH.NormalizeAngleMinus180_180( evadeHeading - MATH.NormalizeAngle0_360(PLATFORM.Heading()) );
|
||||
if (MATH.Fabs(headDiff) < 2.0 &&
|
||||
nearestThreat.RelativeBearingTo(PLATFORM) > 0.0)
|
||||
{
|
||||
mBankLeftFlag = false;
|
||||
}
|
||||
}
|
||||
else // bank right
|
||||
{
|
||||
evadeHeading = MATH.NormalizeAngle0_360(theta - 180 + mBankAngleForEvading);
|
||||
double headDiff = MATH.NormalizeAngleMinus180_180(evadeHeading - MATH.NormalizeAngle0_360(PLATFORM.Heading()));
|
||||
if (MATH.Fabs(headDiff) < 2.0 &&
|
||||
nearestThreat.RelativeBearingTo(PLATFORM) < 0.0)
|
||||
{
|
||||
mBankLeftFlag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
evadeAltitude = MATH.Max(safeAltitudeToDiveTo, evadeAltitude);
|
||||
if (mWobbleUpDown == true)
|
||||
{
|
||||
//compute the dive or climb
|
||||
if (mDiveDownFlag == true)
|
||||
{
|
||||
writeln_d(PLATFORM.Name(), " diving to ", safeAltitudeToDiveTo);
|
||||
evadeAltitude = safeAltitudeToDiveTo;
|
||||
if (PLATFORM.Altitude() <= (cDEFAULT_ALTITUDE - 0.95*(cDEFAULT_ALTITUDE-safeAltitudeToDiveTo)))
|
||||
{
|
||||
mDiveDownFlag = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
writeln_d(PLATFORM.Name(), " climbing to ", cDEFAULT_ALTITUDE);
|
||||
evadeAltitude = cDEFAULT_ALTITUDE;
|
||||
if (PLATFORM.Altitude() >= (safeAltitudeToDiveTo + 0.95*(cDEFAULT_ALTITUDE-safeAltitudeToDiveTo)))
|
||||
{
|
||||
mDiveDownFlag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writeln_d(" Evading incoming at heading ", evadeHeading);
|
||||
writeln_d(" Evading incoming, dive/climb to ", evadeAltitude);
|
||||
|
||||
Fly(PLATFORM, evadeHeading, evadeAltitude, cEVADE_SPEED);
|
||||
|
||||
if (mDrawNearestThreat)
|
||||
{
|
||||
mDraw.SetLayer("behavior_evade");
|
||||
mDraw.SetDuration(PROCESSOR.UpdateInterval());
|
||||
mDraw.SetColor(1,0,0);
|
||||
mDraw.SetLineSize(5);
|
||||
mDraw.SetLineStyle("dashed");
|
||||
mDraw.BeginLines();
|
||||
mDraw.Vertex(PLATFORM.Location());
|
||||
mDraw.Vertex(nearestThreat.Location());
|
||||
mDraw.End();
|
||||
}
|
||||
end_execute
|
||||
|
||||
end_behavior
|
||||
160
processors/timeline_agents/behavior_fly_timeline.txt
Normal file
160
processors/timeline_agents/behavior_fly_timeline.txt
Normal file
@@ -0,0 +1,160 @@
|
||||
# ****************************************************************************
|
||||
# CUI
|
||||
#
|
||||
# The Advanced Framework for Simulation, Integration, and Modeling (AFSIM)
|
||||
#
|
||||
# The use, dissemination or disclosure of data in this file is subject to
|
||||
# limitation or restriction. See accompanying README and LICENSE for details.
|
||||
# ****************************************************************************
|
||||
|
||||
include_once common_timeline_scripts.txt
|
||||
include_once air_combat_maneuvers.txt
|
||||
|
||||
behavior fly_timeline
|
||||
|
||||
script_debug_writes off
|
||||
|
||||
script_variables
|
||||
WsfQuantumTaskerProcessor processor;
|
||||
#***********************************************************************#
|
||||
#** debugging parameters ***#
|
||||
#***********************************************************************#
|
||||
bool mDebugDraw = true;
|
||||
#***********************************************************************#
|
||||
#********* VARIABLES BELOW THIS LINE ARE NOT FOR USER EDITING **********#
|
||||
#***********************************************************************#
|
||||
WsfDraw mDraw = WsfDraw();
|
||||
end_script_variables
|
||||
|
||||
on_init
|
||||
if (PROCESSOR.IsA_TypeOf("WSF_QUANTUM_TASKER_PROCESSOR"))
|
||||
{
|
||||
processor = (WsfQuantumTaskerProcessor)PROCESSOR;
|
||||
}
|
||||
end_on_init
|
||||
|
||||
precondition
|
||||
writeln_d(PLATFORM.Name(), " precondition fly_timeline, T=", TIME_NOW);
|
||||
#if (!PROCESSOR.IsA_TypeOf("WSF_QUANTUM_TASKER_PROCESSOR"))
|
||||
if (!processor.IsValid())
|
||||
{
|
||||
return Failure("behavior not attached to a WSF_QUANTUM_TASKER_PROCESSOR");
|
||||
}
|
||||
# WsfTaskList tasks = processor.TasksReceivedOfType("TIMELINE");
|
||||
# if (tasks.Count() <= 0)
|
||||
# {
|
||||
# return Failure("no TIMELINE tasks received yet");
|
||||
# }
|
||||
# writeln_d(PLATFORM.Name(), " received TIMELINE tasks: ", tasks.Count());
|
||||
if (!GetTimeLineTask(processor).IsValid())
|
||||
{
|
||||
return Failure("no TIMELINE tasks received yet");
|
||||
}
|
||||
return true;
|
||||
end_precondition
|
||||
|
||||
|
||||
execute
|
||||
|
||||
# WsfTrack target = GetNearestTimelineTarget(PLATFORM, processor);
|
||||
# #Engage(PLATFORM, target, "pure");
|
||||
# Engage(PLATFORM, target, "lead");
|
||||
|
||||
#look for the soonest intercepting target that we don't already have a weapon active or pending against
|
||||
#sort them
|
||||
|
||||
Array<WsfTrack> targets = GetTimeLineTargets(processor);
|
||||
Array<double> times = Array<double>();
|
||||
foreach(WsfTrack target in targets)
|
||||
{
|
||||
double time = TimeToReachPoint(target, PLATFORM.Location(), 6.0); //6 G turns
|
||||
times.PushBack(time);
|
||||
}
|
||||
SortTracksByValue(targets, times);
|
||||
|
||||
#check for no active or pending weapons
|
||||
foreach(WsfTrack target in targets)
|
||||
{
|
||||
#do we need master track?
|
||||
WsfLocalTrack masterTarget = GetMasterTrackByName(PLATFORM, target.TargetName());
|
||||
if (!masterTarget.IsValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if ((PLATFORM.WeaponsPendingFor(masterTarget.TrackId()) + PLATFORM.WeaponsActiveFor(masterTarget.TrackId())) > 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Engage(PLATFORM, target, "lead", PLATFORM.Speed(), PLATFORM.Altitude(), 6.0);
|
||||
break;
|
||||
}
|
||||
|
||||
if (mDebugDraw == true)
|
||||
{
|
||||
mDraw.SetLayer("behavior_fly_timeline");
|
||||
mDraw.SetDuration(processor.UpdateInterval());
|
||||
string side = PLATFORM.Side().Lower();
|
||||
mDraw.SetLineSize(1);
|
||||
mDraw.SetLineStyle("solid");
|
||||
WsfTaskList tasks = processor.TasksReceivedOfType("TIMELINE");
|
||||
foreach(WsfTask task in tasks)
|
||||
{
|
||||
Array<WsfTrack> targets = (Array<WsfTrack>)task.AuxDataObject("targets");
|
||||
WsfGeoPoint SeparateA;
|
||||
WsfGeoPoint SeparateB;
|
||||
if (task.AuxDataExists("point_a"))
|
||||
{
|
||||
SeparateA = (WsfGeoPoint)task.AuxDataObject("point_a");
|
||||
SeparateB = (WsfGeoPoint)task.AuxDataObject("point_b");
|
||||
}
|
||||
|
||||
#draw lines to targets
|
||||
if (side == "blue") {
|
||||
mDraw.SetColor(0.0, 0.0, 1.0); #blue
|
||||
} else if (side == "red") {
|
||||
mDraw.SetColor(1.0, 0.0, 0.0); #red
|
||||
} else {
|
||||
mDraw.SetColor(0.0, 1.0, 0.0); #green
|
||||
}
|
||||
|
||||
if (SeparateA.IsValid() && SeparateB.IsValid())
|
||||
{
|
||||
#draw lines for separation divide
|
||||
mDraw.SetColor(0.0, 0.0, 0.0); #black
|
||||
mDraw.BeginLines();
|
||||
mDraw.Vertex(SeparateA);
|
||||
mDraw.Vertex(SeparateB);
|
||||
mDraw.End();
|
||||
|
||||
#check if platform is on the correct side of separation line
|
||||
WsfCluster targetCluster = GetCluster(targets);
|
||||
double refAz = SeparateA.TrueBearingTo(SeparateB);
|
||||
double ownAz = SeparateA.TrueBearingTo(PLATFORM.Location()) - refAz;
|
||||
double tgtAz = SeparateA.TrueBearingTo(targetCluster.MeanLocation()) - refAz;
|
||||
ownAz = MATH.NormalizeAngleMinus180_180(ownAz);
|
||||
tgtAz = MATH.NormalizeAngleMinus180_180(tgtAz);
|
||||
if ((ownAz * tgtAz) > 0)
|
||||
{
|
||||
#on correct side of line
|
||||
mDraw.SetColor(1.0, 0.0, 1.0); #purple
|
||||
}
|
||||
else
|
||||
{
|
||||
#incorrect side
|
||||
mDraw.SetColor(1.0, 0.5, 0.0); #orange
|
||||
}
|
||||
}
|
||||
|
||||
mDraw.BeginLines();
|
||||
foreach(WsfTrack target in targets)
|
||||
{
|
||||
mDraw.Vertex(PLATFORM);
|
||||
mDraw.Vertex(target.CurrentLocation());
|
||||
}
|
||||
mDraw.End();
|
||||
}
|
||||
}
|
||||
end_execute
|
||||
|
||||
end_behavior
|
||||
|
||||
308
processors/timeline_agents/behavior_go_home.txt
Normal file
308
processors/timeline_agents/behavior_go_home.txt
Normal file
@@ -0,0 +1,308 @@
|
||||
# ****************************************************************************
|
||||
# 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.
|
||||
# ****************************************************************************
|
||||
|
||||
|
||||
behavior go_home
|
||||
|
||||
script_debug_writes off
|
||||
|
||||
script_variables
|
||||
|
||||
double cDEFAULT_ALTITUDE = 9144; // ~30,000 feet
|
||||
|
||||
bool mDrawRoute = false; // draw the home route
|
||||
WsfDraw mDraw = WsfDraw();
|
||||
|
||||
bool mCheckFuel = true; // check fuel levels
|
||||
bool mCheckMyWeapons = true; // check the remaining quanitity of munitions on this platform
|
||||
#bool mCheckAllWeapons = false; // check the remaining quantity of munitions on all platforms in the peer group, including my own
|
||||
#bool mCheckUplinks = false; // check if uplinks are still existent
|
||||
|
||||
#use: mRequiredToSupportWeapons instead
|
||||
#bool mWaitOnActiveWeapons = false; // don't go home as long as active weapons are being controlled
|
||||
|
||||
string mMainWeapon = ""; // main weapon of concern, not necessarily this platform's main weapon
|
||||
|
||||
bool mCheckEscorts = false; // check if escort platforms are still existent and on course
|
||||
Array<string> mEscortNames = Array<string>(); // the names of platforms this platform is responsible for escorting
|
||||
|
||||
double cBINGO_SPEED = 516 * MATH.MPS_PER_NMPH(); // ~M0.8 or 248 m/s @ 25 kft#removed 1.5 * that was added to help 6dof movers 14Oct11
|
||||
WsfGeoPoint mFirstKnownPoint = WsfGeoPoint();
|
||||
|
||||
string mRouteName = "";
|
||||
int mRouteHomeIndex = 0; // the index of the waypoint on the default route the platform should return to
|
||||
|
||||
string mLastComment = "<none yet>";
|
||||
end_script_variables
|
||||
|
||||
|
||||
script bool HasEscorts()
|
||||
WsfPlatform escortPlatform;
|
||||
foreach ( string sEscortName in mEscortNames )
|
||||
{
|
||||
escortPlatform = WsfSimulation.FindPlatform(sEscortName);
|
||||
if (escortPlatform.IsValid() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
end_script
|
||||
|
||||
script double NumWeaponsRemaining(WsfPlatform aPlatform)
|
||||
double total = 0.0;
|
||||
for (int i=0; i<aPlatform.WeaponCount(); i=i+1)
|
||||
{
|
||||
total = total + aPlatform.WeaponEntry(i).QuantityRemaining();
|
||||
}
|
||||
return total;
|
||||
end_script
|
||||
|
||||
script void DrawRoute()
|
||||
WsfRoute currRoute = PLATFORM.Route();
|
||||
|
||||
if (currRoute.IsValid())
|
||||
{
|
||||
//PLATFORM.Comment("draw current route : " + PLATFORM.Route().Name());
|
||||
mDraw.SetLayer("behavior_go_home");
|
||||
mDraw.Erase(PLATFORM.Name());
|
||||
mDraw.SetId(PLATFORM.Name());
|
||||
mDraw.SetColor(0,1,1);
|
||||
mDraw.SetLineSize(2);
|
||||
mDraw.SetLineStyle("dash_dot2");
|
||||
mDraw.BeginPolyline();
|
||||
for (int i=0; i<currRoute.Size(); i=i+1)
|
||||
{
|
||||
mDraw.Vertex(currRoute.Waypoint(i).Location());
|
||||
}
|
||||
mDraw.End();
|
||||
}
|
||||
end_script
|
||||
|
||||
on_init
|
||||
mFirstKnownPoint = PLATFORM.Location();
|
||||
end_on_init
|
||||
|
||||
|
||||
precondition
|
||||
writeln_d("precondition go_home");
|
||||
|
||||
# if (!PROCESSOR.IsA_TypeOf("WSF_RIPR_PROCESSOR"))
|
||||
# {
|
||||
# return Failure("behavior not attached to a RIPR processor!");
|
||||
# }
|
||||
|
||||
string msg = "";
|
||||
bool result = false;
|
||||
// go home if bingo on fuel
|
||||
if (mCheckFuel == true &&
|
||||
PLATFORM.FuelRemaining() < PLATFORM.FuelBingoQuantity())
|
||||
{
|
||||
msg = "GO HOME: bingo fuel";
|
||||
result = true;
|
||||
}
|
||||
// go home if all my escorts are dead or gone
|
||||
else if (mCheckEscorts == true &&
|
||||
!HasEscorts())
|
||||
{
|
||||
msg = "GO HOME: all escorts are dead or gone";
|
||||
result = true;
|
||||
}
|
||||
# // go home if there are no uplinks remaining
|
||||
# else if (mCheckUplinks == true &&
|
||||
# ((WsfRIPRProcessor)PROCESSOR).UplinkCount() <= 0)
|
||||
# {
|
||||
# msg = "GO HOME: no active uplinks";
|
||||
# result = true;
|
||||
# }
|
||||
// go home if all weapons, including my own, in the peer group are out of ammo
|
||||
# else if (mCheckAllWeapons == true &&
|
||||
# ((WsfRIPRProcessor)PROCESSOR).GetRIPRCommanderPlatform().IsValid())
|
||||
# {
|
||||
# msg = "GO HOME: all weapons in group are out of ammo";
|
||||
# result = true;
|
||||
# foreach( WsfPlatform sub in ((WsfRIPRProcessor)PROCESSOR).GetRIPRCommanderPlatform().Subordinates() )
|
||||
# {
|
||||
# if (mMainWeapon != "")
|
||||
# {
|
||||
# WsfWeapon temp = sub.Weapon(mMainWeapon);
|
||||
# if (temp.IsValid() &&
|
||||
# temp.QuantityRemaining() > 0)
|
||||
# {
|
||||
# msg = "";
|
||||
# result = false;
|
||||
# }
|
||||
# }
|
||||
# else if (NumWeaponsRemaining(sub) > 0.0)
|
||||
# {
|
||||
# msg = "";
|
||||
# result = false;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
// go home if my weapons are out of ammo
|
||||
else if (mCheckMyWeapons == true)
|
||||
{
|
||||
extern bool mRequiredToSupportWeapons;
|
||||
if (mMainWeapon != "")
|
||||
{
|
||||
WsfWeapon temp = PLATFORM.Weapon(mMainWeapon);
|
||||
if (temp.IsValid() &&
|
||||
temp.QuantityRemaining() <= 0 &&
|
||||
(mRequiredToSupportWeapons == false ||
|
||||
PLATFORM.WeaponsActiveFor(WsfTrackId()) <= 0))
|
||||
{
|
||||
msg = "GO HOME: main weapon out of ammo";
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
else if (NumWeaponsRemaining(PLATFORM) <= 0.0 &&
|
||||
(mRequiredToSupportWeapons == false ||
|
||||
PLATFORM.WeaponsActiveFor(WsfTrackId()) <= 0))
|
||||
{
|
||||
msg = "GO HOME: out of weapons";
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
// debug - why is this platform going home?
|
||||
if (result)
|
||||
{
|
||||
writeln_d(" T=", TIME_NOW, " ", PLATFORM.Name(), " ", msg);
|
||||
if (msg != mLastComment)
|
||||
{
|
||||
PLATFORM.Comment(msg);
|
||||
mLastComment = msg;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return Failure("plenty of weapons & fuel.");
|
||||
end_precondition
|
||||
|
||||
on_new_execute
|
||||
PLATFORM.Comment("home");
|
||||
end_on_new_execute
|
||||
|
||||
execute
|
||||
writeln_d(PLATFORM.Name(), " executing go_home, T=", TIME_NOW);
|
||||
//PLATFORM.Comment("go_home");
|
||||
|
||||
string msg = "";
|
||||
|
||||
# WsfRIPRProcessor commander = ((WsfRIPRProcessor)PROCESSOR).CommanderProcessor();
|
||||
# if (commander.IsValid())
|
||||
# {
|
||||
# for (int i = 0; i < ((WsfRIPRProcessor)PROCESSOR).NumJobChannels(); i = i + 1)
|
||||
# {
|
||||
# commander.ClearBidsFor(((WsfRIPRProcessor)PROCESSOR), i);
|
||||
# }
|
||||
# }
|
||||
|
||||
|
||||
//TODO - reject or cancel any received tasks, we are going home
|
||||
|
||||
|
||||
|
||||
// make sure we are at the right speed so we don't burn the fuel we have left too fast
|
||||
PLATFORM.GoToSpeed(cBINGO_SPEED);
|
||||
writeln_d(" GoToSpeed( ", cBINGO_SPEED," )");
|
||||
|
||||
bool onHomeRoute = false;
|
||||
|
||||
// if an egress route is defined, and that is the current route,
|
||||
// find the closest waypoint and fly the route from there.
|
||||
// this is mainly used to correct some bugs in the 6dof mover
|
||||
if (mRouteName != "")
|
||||
{
|
||||
WsfRoute myRoute = PLATFORM.Route();
|
||||
if(myRoute.Name() == "home_route")
|
||||
{
|
||||
int routeIndex = PLATFORM.RoutePointIndex();
|
||||
if (routeIndex < 0 || routeIndex >= myRoute.Size())
|
||||
{
|
||||
routeIndex = 0;
|
||||
}
|
||||
double distThreshold = 4.0*185.2; ## 4/10th nm
|
||||
if (myRoute.Waypoint(routeIndex).Location().GroundRangeTo(PLATFORM.Location()) < distThreshold)
|
||||
{
|
||||
routeIndex = routeIndex + 1;
|
||||
if (routeIndex >= myRoute.Size())
|
||||
{
|
||||
routeIndex = 1;
|
||||
}
|
||||
}
|
||||
onHomeRoute = PLATFORM.FollowRoute(myRoute, routeIndex);
|
||||
#string msg = write_str("FollowRoute(home_route, ", routeIndex, ")");
|
||||
#PLATFORM.Comment(msg);
|
||||
}
|
||||
|
||||
// if there is an egress route defined, follow it
|
||||
if (!onHomeRoute)
|
||||
{
|
||||
msg = write_str("attempting to construct and fly route: ", mRouteName);
|
||||
//PLATFORM.Comment(msg);
|
||||
writeln_d(" T=", TIME_NOW, " ", PLATFORM.Name(), " ", msg);
|
||||
|
||||
WsfRoute original = PLATFORM.Route();
|
||||
WsfRoute homeRoute = WsfRoute.Create("home_route");
|
||||
WsfRoute capRoute = WsfRoute.CopyGlobal(mRouteName);
|
||||
double goHomeSpeed = cBINGO_SPEED;
|
||||
|
||||
#if (capRoute.Front().IsValid() && capRoute.Front().Speed() > 0)
|
||||
#{
|
||||
# goHomeSpeed = capRoute.Front().Speed();
|
||||
#}
|
||||
|
||||
WsfGeoPoint startPoint = WsfGeoPoint.Construct(mFirstKnownPoint.Latitude(), mFirstKnownPoint.Longitude(), cDEFAULT_ALTITUDE);
|
||||
|
||||
homeRoute.Append(startPoint, goHomeSpeed);
|
||||
|
||||
capRoute.Transform(startPoint.Latitude(), startPoint.Longitude(), 0);
|
||||
homeRoute.Append(capRoute);
|
||||
|
||||
onHomeRoute = PLATFORM.FollowRoute(homeRoute);
|
||||
|
||||
#PLATFORM.Comment("go_home FollowRoute(homeRoute)");
|
||||
}
|
||||
}
|
||||
|
||||
// if the platform is still not on an egress route, then there is not one
|
||||
// defined or there was a problem with it, so one needs to be created.
|
||||
if (!onHomeRoute)
|
||||
{
|
||||
//now try going back to route
|
||||
if (PLATFORM.FollowRoute("DEFAULT_ROUTE", mRouteHomeIndex))
|
||||
{
|
||||
msg = write_str("should be flying default route");
|
||||
//PLATFORM.Comment(msg);
|
||||
writeln_d(" T=", TIME_NOW, " ", PLATFORM.Name(), " ", msg);
|
||||
//PLATFORM.Comment("FollowRoute(DEFAULT_ROUTE, 1)");
|
||||
}
|
||||
else // what if no default route is provided? (fly to first known point)
|
||||
{
|
||||
WsfGeoPoint tempPt = WsfGeoPoint.Construct(mFirstKnownPoint.Latitude(), mFirstKnownPoint.Longitude(), cDEFAULT_ALTITUDE);
|
||||
PLATFORM.TurnToHeading(PLATFORM.TrueBearingTo(tempPt), 6.0 * Earth.ACCEL_OF_GRAVITY());
|
||||
|
||||
msg = write_str("should be flying to point: ", mFirstKnownPoint.ToString());
|
||||
//PLATFORM.Comment(msg);
|
||||
writeln_d(" T=", TIME_NOW, " ", PLATFORM.Name(), " ", msg);
|
||||
//PLATFORM.Comment("GoToLocation(tempPt)");
|
||||
}
|
||||
}
|
||||
|
||||
// debug
|
||||
if (mDrawRoute)
|
||||
{
|
||||
DrawRoute();
|
||||
}
|
||||
end_execute
|
||||
|
||||
end_behavior
|
||||
|
||||
124
processors/timeline_agents/behavior_planned_route.txt
Normal file
124
processors/timeline_agents/behavior_planned_route.txt
Normal file
@@ -0,0 +1,124 @@
|
||||
# ****************************************************************************
|
||||
# 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.
|
||||
# ****************************************************************************
|
||||
|
||||
//TODO use default values from higher level context (processor or parent behavior that holds default)
|
||||
|
||||
|
||||
#assumes aDraw duration & layer is set
|
||||
script void DrawRoute(WsfDraw aDraw, WsfRoute aRoute)
|
||||
if (aRoute.IsValid())
|
||||
{
|
||||
aDraw.SetColor(0,1,1); //teal?
|
||||
aDraw.SetLineSize(2);
|
||||
aDraw.SetLineStyle("solid");
|
||||
aDraw.BeginPolyline();
|
||||
for (int i=0; i<aRoute.Size(); i=i+1)
|
||||
{
|
||||
aDraw.Vertex(aRoute.Waypoint(i).Location());
|
||||
}
|
||||
aDraw.End();
|
||||
|
||||
aDraw.SetColor(1.0,0.3,0.3); //pink?
|
||||
aDraw.SetPointSize(4);
|
||||
aDraw.BeginPoints();
|
||||
for (int i=0; i<aRoute.Size(); i=i+1)
|
||||
{
|
||||
aDraw.Vertex(aRoute.Waypoint(i).Location());
|
||||
}
|
||||
aDraw.End();
|
||||
}
|
||||
end_script
|
||||
|
||||
|
||||
behavior planned_route
|
||||
|
||||
script_debug_writes off
|
||||
|
||||
script_variables
|
||||
bool mDrawRoute = false;
|
||||
WsfDraw mDraw = WsfDraw();
|
||||
double cDEFAULT_SPEED = 450.0 * MATH.MPS_PER_NMPH();
|
||||
double cDEFAULT_ACCEL = 7.5 * Earth.ACCEL_OF_GRAVITY(); // 7.5 G (m/s^2)
|
||||
end_script_variables
|
||||
|
||||
precondition
|
||||
writeln_d("precondition planned_route");
|
||||
return true;
|
||||
end_precondition
|
||||
|
||||
on_new_execute
|
||||
PLATFORM.Comment("route");
|
||||
end_on_new_execute
|
||||
|
||||
execute
|
||||
writeln_d(PLATFORM.Name(), " executing planned_route, T=", TIME_NOW);
|
||||
#only command the platform to do something different if its not currently flying a route
|
||||
WsfMover aMover = PLATFORM.Mover();
|
||||
if (aMover.IsValid()) {
|
||||
if (aMover.IsExtrapolating()) {
|
||||
WsfGeoPoint pt = PLATFORM.Location();
|
||||
WsfRoute ro = aMover.DefaultRoute().Copy(); #now we have a modifiable route
|
||||
if (!ro.IsValid())
|
||||
return;
|
||||
|
||||
writeln_d("flying route, name: ", ro.Name(), ", type: ", ro.Type());
|
||||
WsfGeoPoint close = ro.LocationAtDistance(ro.DistanceAlongRoute(pt));
|
||||
if (!close.IsValid()) {
|
||||
return;
|
||||
}
|
||||
close.SetAltitudeAGL(pt.Altitude());
|
||||
if (mDrawRoute)
|
||||
{
|
||||
mDraw.BeginLines();
|
||||
mDraw.Vertex(pt);
|
||||
mDraw.Vertex(close);
|
||||
mDraw.End();
|
||||
}
|
||||
double d1 = ro.DistanceFromRoute(pt);
|
||||
double d2 = pt.GroundRangeTo(close);
|
||||
double d3 = -1;
|
||||
Array<double> turnRad = aMover.PropertyDouble("turn_radius");
|
||||
if (turnRad.Size() > 0) {
|
||||
d3 = 2*turnRad[0];
|
||||
}
|
||||
int i = 0;
|
||||
for (; i < ro.Size(); i = i+1)
|
||||
{
|
||||
WsfWaypoint wpt = ro.Waypoint(i);
|
||||
WsfGeoPoint rpt = wpt.Location();
|
||||
//check if we are close to an existing waypoint, if so... break & fly at that one
|
||||
if (rpt.GroundRangeTo(close) < 926) {
|
||||
break;
|
||||
}
|
||||
double dist = ro.DistanceAlongRoute(rpt);
|
||||
if (dist > d1) {
|
||||
if (d2 > d3) {
|
||||
ro.Insert(i, WsfWaypoint.Create(close, wpt.Speed()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i >= ro.Size()) {
|
||||
i = ro.Size() - 1;
|
||||
}
|
||||
//go at default speed; this gets overwritten if route waypoint has defined a speed
|
||||
PLATFORM.GoToSpeed(cDEFAULT_SPEED, cDEFAULT_ACCEL, true);
|
||||
PLATFORM.FollowRoute(ro, i);
|
||||
}
|
||||
}
|
||||
|
||||
if (mDrawRoute)
|
||||
{
|
||||
mDraw.SetDuration(PROCESSOR.UpdateInterval());
|
||||
mDraw.SetLayer("behavior_planned_route");
|
||||
DrawRoute(mDraw, PLATFORM.Route());
|
||||
}
|
||||
end_execute
|
||||
|
||||
end_behavior
|
||||
169
processors/timeline_agents/behavior_timeline_delay.txt
Normal file
169
processors/timeline_agents/behavior_timeline_delay.txt
Normal file
@@ -0,0 +1,169 @@
|
||||
# ****************************************************************************
|
||||
# CUI
|
||||
#
|
||||
# The Advanced Framework for Simulation, Integration, and Modeling (AFSIM)
|
||||
#
|
||||
# The use, dissemination or disclosure of data in this file is subject to
|
||||
# limitation or restriction. See accompanying README and LICENSE for details.
|
||||
# ****************************************************************************
|
||||
|
||||
include_once common_timeline_scripts.txt
|
||||
include_once air_combat_maneuvers.txt
|
||||
include_once weapon_defs.txt
|
||||
|
||||
behavior timeline_delay
|
||||
|
||||
script_debug_writes off
|
||||
|
||||
script_variables
|
||||
WsfQuantumTaskerProcessor processor;
|
||||
end_script_variables
|
||||
|
||||
on_init
|
||||
if (PROCESSOR.IsA_TypeOf("WSF_QUANTUM_TASKER_PROCESSOR"))
|
||||
{
|
||||
processor = (WsfQuantumTaskerProcessor)PROCESSOR;
|
||||
}
|
||||
end_on_init
|
||||
|
||||
|
||||
#returns true (to wait) if no free target can be shot at (current range)
|
||||
#if target already has weapon on it, it is ignored
|
||||
script bool WaitToTurnAndShoot(Array<WsfTrack> targets)
|
||||
#only consider free targets (a target with no weapons on it)
|
||||
#compare max weapon range to each free target
|
||||
#calculate time to turn & face for a shot
|
||||
#return false if any target can be turned on & engaged
|
||||
foreach(WsfTrack target in targets)
|
||||
{
|
||||
WsfLocalTrack masterTarget = GetMasterTrackByName(PLATFORM, target.TargetName());
|
||||
if (!masterTarget.IsValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
bool checkPeerWeapons = false;
|
||||
|
||||
if (FiredOn(PLATFORM, masterTarget, checkPeerWeapons))
|
||||
{
|
||||
#LBM TODO - figure out time until weapon terminates
|
||||
# compare that to turn time too
|
||||
# dont necessarily skip this target
|
||||
continue;
|
||||
}
|
||||
|
||||
double weaponMaxRange = 0.0;
|
||||
WsfWeapon maxRangeWeapon;
|
||||
for (int i=0; i < PLATFORM.WeaponCount(); i+=1)
|
||||
{
|
||||
WsfWeapon weapon = PLATFORM.WeaponEntry(i);
|
||||
if (WeaponCapableAvailableAgainstThreat(weapon, masterTarget))
|
||||
{
|
||||
double weaponRange = MaxRange(PLATFORM,weapon,masterTarget);
|
||||
if (weaponRange > weaponMaxRange)
|
||||
{
|
||||
weaponMaxRange = weaponRange;
|
||||
maxRangeWeapon = weapon;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (maxRangeWeapon.IsValid())
|
||||
{
|
||||
#valid weapon found with an intelligible max range
|
||||
double targetRange = PLATFORM.GroundRangeTo(masterTarget.CurrentLocation());
|
||||
#double weaponMaxRange;
|
||||
double rangeRemaining = targetRange - weaponMaxRange;
|
||||
if(rangeRemaining <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#targets speed towards me:
|
||||
double hisClosingSpeed = masterTarget.Speed() * MATH.Cos(masterTarget.RelativeBearingTo(PLATFORM));
|
||||
#my avg speed towards him (assuming an immediate turn towards him):
|
||||
double myMinClosingSpeed = PLATFORM.Speed() * MATH.Cos(PLATFORM.RelativeBearingTo(masterTarget));
|
||||
double myMaxClosingSpeed = PLATFORM.Speed(); #assumes I turn & face
|
||||
double myAvgClosingSpeed = (myMinClosingSpeed+myMaxClosingSpeed)/2.0;
|
||||
#closing speed to use:
|
||||
double closingSpeed = hisClosingSpeed + myAvgClosingSpeed;
|
||||
extern double mGeesToTurnWith;
|
||||
double turnTime = TimeToTurnAndFace(PLATFORM.MakeTrack(), masterTarget.CurrentLocation(), mGeesToTurnWith);
|
||||
if (turnTime >= rangeRemaining/closingSpeed)
|
||||
{
|
||||
return false; #dont wait, time to turn & shoot this guy!!!
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
end_script
|
||||
|
||||
|
||||
precondition
|
||||
writeln_d(PLATFORM.Name(), " precondition timeline_delay, T=", TIME_NOW);
|
||||
if (!processor.IsValid())
|
||||
{
|
||||
return Failure("behavior not attached to a WSF_QUANTUM_TASKER_PROCESSOR");
|
||||
}
|
||||
|
||||
extern bool mRequiredToSupportWeapons;
|
||||
if (mRequiredToSupportWeapons == true && PLATFORM.WeaponsActiveFor(WsfTrackId()) > 0)
|
||||
{
|
||||
return Failure("required to support active weapons - do not delay");
|
||||
}
|
||||
|
||||
WsfTask timeline = GetTimeLineTask(processor);
|
||||
if (!timeline.IsValid())
|
||||
{
|
||||
return Failure("not assigned a TIMELINE task");
|
||||
}
|
||||
|
||||
extern WsfLocalTrack mNearestTimeLineTarget;
|
||||
if (!mNearestTimeLineTarget.IsValid())
|
||||
{
|
||||
return Failure("no TIMELINE target to fly against");
|
||||
}
|
||||
|
||||
|
||||
#compare risk level with range to nearest target
|
||||
#only delay if risk MED: past DOR and (want to shoot but out of range or waiting on existing shot)
|
||||
# risk HIGH: past MAR and (want to shoot but out of range or waiting on existing shot)
|
||||
string RISK = timeline.AuxDataString("RISK");
|
||||
if (RISK == "HIGH")
|
||||
{
|
||||
double MAR = timeline.AuxDataDouble("MAR"); #range (meters)
|
||||
double slantRange = PLATFORM.GroundRangeTo(mNearestTimeLineTarget.CurrentLocation());
|
||||
if (slantRange > MAR)
|
||||
{
|
||||
return Failure("HIGH RISK agent outside of MAR, dont bother delaying");
|
||||
}
|
||||
Array<WsfTrack> targets = (Array<WsfTrack>)timeline.AuxDataObject("targets");
|
||||
return WaitToTurnAndShoot(targets);
|
||||
}
|
||||
else if (RISK == "MEDIUM")
|
||||
{
|
||||
double DOR = timeline.AuxDataDouble("DOR"); #range (meters)
|
||||
double slantRange = PLATFORM.GroundRangeTo(mNearestTimeLineTarget.CurrentLocation());
|
||||
if (slantRange > DOR)
|
||||
{
|
||||
return Failure("MEDIUM RISK agent outside of DOR, dont bother delaying");
|
||||
}
|
||||
Array<WsfTrack> targets = (Array<WsfTrack>)timeline.AuxDataObject("targets");
|
||||
return WaitToTurnAndShoot(targets);
|
||||
}
|
||||
else #if (RISK == "LOW")
|
||||
{
|
||||
return Failure("LOW RISK agent does not go past DOR, no need to delay");
|
||||
}
|
||||
end_precondition
|
||||
|
||||
on_new_execute
|
||||
PLATFORM.Comment("delay");
|
||||
end_on_new_execute
|
||||
|
||||
execute
|
||||
#TODO - select beam or posthole or whatever
|
||||
Beam(PLATFORM, mNearestTimeLineTarget);
|
||||
end_execute
|
||||
|
||||
end_behavior
|
||||
|
||||
275
processors/timeline_agents/behavior_timeline_engage.txt
Normal file
275
processors/timeline_agents/behavior_timeline_engage.txt
Normal file
@@ -0,0 +1,275 @@
|
||||
# ****************************************************************************
|
||||
# CUI
|
||||
#
|
||||
# The Advanced Framework for Simulation, Integration, and Modeling (AFSIM)
|
||||
#
|
||||
# The use, dissemination or disclosure of data in this file is subject to
|
||||
# limitation or restriction. See accompanying README and LICENSE for details.
|
||||
# ****************************************************************************
|
||||
|
||||
include_once common_timeline_scripts.txt
|
||||
include_once air_combat_maneuvers.txt
|
||||
include_once weapon_defs.txt
|
||||
|
||||
|
||||
behavior timeline_engage
|
||||
|
||||
script_debug_writes off
|
||||
|
||||
script_variables
|
||||
WsfQuantumTaskerProcessor processor;
|
||||
|
||||
bool mWaitForLatestShot = false; #if false: shoots asap once at %rmax
|
||||
bool mDontShootIfCantSupport = false; #if true: doesn't shoot unless there is time to support the shot
|
||||
|
||||
|
||||
WsfLocalTrack mEngageTarget;
|
||||
|
||||
end_script_variables
|
||||
|
||||
on_init
|
||||
if (PROCESSOR.IsA_TypeOf("WSF_QUANTUM_TASKER_PROCESSOR"))
|
||||
{
|
||||
processor = (WsfQuantumTaskerProcessor)PROCESSOR;
|
||||
}
|
||||
end_on_init
|
||||
|
||||
precondition
|
||||
writeln_d(PLATFORM.Name(), " precondition timeline_engage, T=", TIME_NOW);
|
||||
if (!processor.IsValid())
|
||||
{
|
||||
return Failure("behavior not attached to a WSF_QUANTUM_TASKER_PROCESSOR");
|
||||
}
|
||||
WsfTask timeline = GetTimeLineTask(processor);
|
||||
if (!timeline.IsValid())
|
||||
{
|
||||
return Failure("not assigned a TIMELINE task");
|
||||
}
|
||||
|
||||
string RISK = timeline.AuxDataString("RISK");
|
||||
extern WsfLocalTrack mNearestTimeLineTarget;
|
||||
extern bool mConsiderPeersWeapons;
|
||||
|
||||
#look for the soonest intercepting target that we don't already have a weapon active or pending against
|
||||
extern Array<WsfLocalTrack> mTimeLineLocalTargets; #sorted list of local track timeline targets
|
||||
#check for no active or pending weapons
|
||||
|
||||
|
||||
#pursue something, hopefully a shootable guy
|
||||
extern Array<WsfLocalTrack> mTimeLineLocalTargetsToShoot;
|
||||
if (mTimeLineLocalTargetsToShoot.Size() > 0)
|
||||
{
|
||||
mEngageTarget = mTimeLineLocalTargetsToShoot.Front();
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach(WsfLocalTrack target in mTimeLineLocalTargets)
|
||||
{
|
||||
if (FiredOn(PLATFORM, target, mConsiderPeersWeapons))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
extern double mGeesToTurnWith;
|
||||
extern double mEngageSpeed;
|
||||
|
||||
|
||||
# extern bool mRequiredToSupportWeapons;
|
||||
# if (mRequiredToSupportWeapons == true && RISK != "HIGH") #can support any shot when risk is high (won't be dragging)
|
||||
# {
|
||||
# double gate = timeline.AuxDataDouble("DOR"); #range (meters)
|
||||
# if (RISK == "MEDIUM")
|
||||
# {
|
||||
# gate = timeline.AuxDataDouble("MAR"); #range (meters)
|
||||
# }
|
||||
#
|
||||
# #calculate if we have time to support a shot to this target:
|
||||
# extern double mOffsetAngle;
|
||||
# extern double mDelaySpeed;
|
||||
#
|
||||
# #figure out weapon range & time of flight
|
||||
# double weaponMaxRange = 0.0;
|
||||
# WsfWeapon maxRangeWeapon;
|
||||
# for (int i=0; i < PLATFORM.WeaponCount(); i+=1)
|
||||
# {
|
||||
# WsfWeapon weapon = PLATFORM.WeaponEntry(i);
|
||||
# if (WeaponCapableAvailableAgainstThreat(weapon, target))
|
||||
# {
|
||||
# double weaponRange = MaxRange(PLATFORM,weapon,target);
|
||||
# if (weaponRange > weaponMaxRange)
|
||||
# {
|
||||
# weaponMaxRange = weaponRange;
|
||||
# maxRangeWeapon = weapon;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# if (!maxRangeWeapon.IsValid())
|
||||
# {
|
||||
# continue; #no weapon for this target
|
||||
# }
|
||||
# #estimate time before we can shoot
|
||||
# #hypothetical shot
|
||||
# #estimate time cranking (based on weapon travel time)
|
||||
# #does this leave us enough time to turn to drag?
|
||||
# double range = PLATFORM.GroundRangeTo(mNearestTimeLineTarget.CurrentLocation());
|
||||
#
|
||||
# double weaponTOF = TimeToHit(PLATFORM, maxRangeWeapon, target);
|
||||
# if (weaponTOF <= 0)
|
||||
# {
|
||||
# #good weapon data no found
|
||||
# continue;
|
||||
# }
|
||||
# double firingRange = DefaultPercentRangeMax * weaponMaxRange;
|
||||
# double targetRange = PLATFORM.GroundRangeTo(target.CurrentLocation());
|
||||
# double rangeToGo = targetRange - firingRange;
|
||||
# double timeTilShot = 0.0;
|
||||
# double closingSpeed = 0.0;
|
||||
# if (rangeToGo > 0)
|
||||
# {
|
||||
# #subtract off closing distance to nearest target during the time before shot
|
||||
# timeTilShot = rangeToGo / PLATFORM.ClosingSpeedOf(target);
|
||||
# closingSpeed = PLATFORM.Speed() + mNearestTimeLineTarget.Speed();
|
||||
# range -= (timeTilShot * closingSpeed);
|
||||
# }
|
||||
#
|
||||
# #subtract off closing distance to nearest target during the time of weapon's flight
|
||||
# closingSpeed = (mDelaySpeed * MATH.Cos(mOffsetAngle)) + mNearestTimeLineTarget.Speed();
|
||||
# range -= (weaponTOF * closingSpeed);
|
||||
#
|
||||
# #calculate time to turn to drag (after cranking)
|
||||
# Array<double> vals = Array<double>();
|
||||
# double turnAngle = 180 - mOffsetAngle;
|
||||
# TurnAngleTimeAndDistance(turnAngle, mEngageSpeed, mGeesToTurnWith, vals);
|
||||
# double turnTime = vals[0];
|
||||
# double distSep = vals[1];
|
||||
# #subtract off closing distance to nearest target during the time of turning to drag
|
||||
# range += distSep;
|
||||
# range -= (turnTime*mNearestTimeLineTarget.Speed());
|
||||
#
|
||||
# if (range >= gate)
|
||||
# {
|
||||
# mEngageTarget = target;
|
||||
# return true;
|
||||
# }
|
||||
# }
|
||||
# else
|
||||
{
|
||||
mEngageTarget = target;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return Failure("no timeline targets left to engage");
|
||||
end_precondition
|
||||
|
||||
# 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 bool TryToFireOn(WsfLocalTrack target)
|
||||
extern double DefaultPercentRangeMax;
|
||||
extern double DefaultPercentRangeMin;
|
||||
WsfWeapon weapon;
|
||||
bool weaponUsable = false;
|
||||
#first weapon found will be used
|
||||
for (int i=0; i < PLATFORM.WeaponCount(); i+=1)
|
||||
{
|
||||
weapon = PLATFORM.WeaponEntry(i);
|
||||
writeln_d("checking if weapon ", weapon.Name(), " is usable.");
|
||||
if (WeaponCapableAvailableAgainstThreat(weapon, target) &&
|
||||
#InRangeToFire(PLATFORM, weapon, target, GetLaunchPercentRangeMaxOnThreat(weapon.Name(), target), DefaultPercentRangeMin))
|
||||
InRangeToFire(PLATFORM, weapon, target, DefaultPercentRangeMax, DefaultPercentRangeMin))
|
||||
{
|
||||
weaponUsable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (weaponUsable == false)
|
||||
{
|
||||
writeln_d("no usable weapon found!");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool launched = false;
|
||||
if (weapon.IsTurnedOn())
|
||||
{
|
||||
writeln_d(" Attempting launch at ", target.TargetName());
|
||||
launched = weapon.Fire(target);
|
||||
}
|
||||
writeln_d(" launched == ", launched, ", weapon: ", weapon.Name());
|
||||
if(launched == false)
|
||||
{
|
||||
writeln_d(" ", PLATFORM.Name(), " could NOT fire at track: ", target.TargetName(), " at time: ", TIME_NOW);
|
||||
}
|
||||
return launched;
|
||||
end_script
|
||||
|
||||
on_new_execute
|
||||
PLATFORM.Comment("engage");
|
||||
end_on_new_execute
|
||||
|
||||
execute
|
||||
#mEngageTarget;
|
||||
extern double mDefaultAltitude;
|
||||
|
||||
#check if we need to fly offset to get on proper side of engagement separation line
|
||||
extern double mEngageSpeed;
|
||||
extern double mGeesToTurnWith;
|
||||
extern bool mSeparateEngagement;
|
||||
if (mSeparateEngagement == true)
|
||||
{
|
||||
extern string mSeparateSide; # "LEFT" or "RIGHT"
|
||||
extern double mEngagementTrueBearing;
|
||||
extern double mOffsetAngle;
|
||||
double delta = PLATFORM.TrueBearingTo(mEngageTarget) - mEngagementTrueBearing;
|
||||
if (delta < 0 && mSeparateSide == "LEFT")
|
||||
{
|
||||
#we're out of position, crank left
|
||||
Offset(PLATFORM, mEngageTarget.CurrentLocation(), -mOffsetAngle, mDefaultAltitude, mEngageSpeed, mGeesToTurnWith);
|
||||
}
|
||||
else if (delta > 0 && mSeparateSide == "RIGHT")
|
||||
{
|
||||
#we're out of position, crank right
|
||||
Offset(PLATFORM, mEngageTarget.CurrentLocation(), mOffsetAngle, mDefaultAltitude, mEngageSpeed, mGeesToTurnWith);
|
||||
}
|
||||
else
|
||||
{
|
||||
#engage straight way - intercept the target
|
||||
Engage(PLATFORM, mEngageTarget, "lead", mEngageSpeed, mDefaultAltitude, mGeesToTurnWith);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#engage straight way - intercept the target
|
||||
Engage(PLATFORM, mEngageTarget, "lead", mEngageSpeed, mDefaultAltitude, mGeesToTurnWith);
|
||||
}
|
||||
|
||||
#TODO - check for shot range, prep for shot if appropriate?
|
||||
|
||||
# #if possible, fire a weapon at the target
|
||||
# TryToFireOn(mEngageTarget);
|
||||
|
||||
#try for shots on all shootable targets
|
||||
foreach (WsfLocalTrack target in mTimeLineLocalTargetsToShoot)
|
||||
{
|
||||
TryToFireOn(target);
|
||||
}
|
||||
|
||||
end_execute
|
||||
|
||||
end_behavior
|
||||
|
||||
137
processors/timeline_agents/behavior_timeline_reposition.txt
Normal file
137
processors/timeline_agents/behavior_timeline_reposition.txt
Normal file
@@ -0,0 +1,137 @@
|
||||
# ****************************************************************************
|
||||
# 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.
|
||||
# ****************************************************************************
|
||||
|
||||
#REQUIRES defined externally: double mGeesToTurnWith;
|
||||
|
||||
include_once common_timeline_scripts.txt
|
||||
include_once air_combat_maneuvers.txt
|
||||
|
||||
behavior timeline_reposition
|
||||
|
||||
script_debug_writes off
|
||||
|
||||
script_variables
|
||||
WsfQuantumTaskerProcessor processor;
|
||||
bool mRepositioning = false;
|
||||
end_script_variables
|
||||
|
||||
on_init
|
||||
if (PROCESSOR.IsA_TypeOf("WSF_QUANTUM_TASKER_PROCESSOR"))
|
||||
{
|
||||
processor = (WsfQuantumTaskerProcessor)PROCESSOR;
|
||||
}
|
||||
end_on_init
|
||||
|
||||
on_new_execute
|
||||
mRepositioning = true;
|
||||
PLATFORM.Comment("reposition");
|
||||
end_on_new_execute
|
||||
on_new_fail
|
||||
mRepositioning = false;
|
||||
end_on_new_fail
|
||||
|
||||
#TODO - add hysteresis?
|
||||
# if already repositioning, account for turn time back to engage
|
||||
# dont reengage if we'll have to turn away before facing target
|
||||
precondition
|
||||
|
||||
#writeln_d(PLATFORM.Name(), " precondition timeline_reposition, T=", TIME_NOW);
|
||||
if (!processor.IsValid())
|
||||
{
|
||||
return Failure("behavior not attached to a WSF_QUANTUM_TASKER_PROCESSOR");
|
||||
}
|
||||
WsfTask timeline = GetTimeLineTask(processor);
|
||||
if (!timeline.IsValid())
|
||||
{
|
||||
return Failure("not assigned a TIMELINE task");
|
||||
}
|
||||
|
||||
extern WsfLocalTrack mNearestTimeLineTarget;
|
||||
if (!mNearestTimeLineTarget.IsValid())
|
||||
{
|
||||
return Failure("no local TIMELINE target to fly against");
|
||||
}
|
||||
|
||||
extern double mGeesToTurnWith;
|
||||
string RISK = timeline.AuxDataString("RISK");
|
||||
if (RISK == "HIGH")
|
||||
{
|
||||
return Failure("HIGH RISK agent does not reposition");
|
||||
}
|
||||
|
||||
extern double mDragSpeed;
|
||||
extern double mEngageSpeed;
|
||||
|
||||
if (mRepositioning == true)
|
||||
{
|
||||
writeln_d("testing conditions to CONTINUE dragging");
|
||||
#already dragging, look at conditions required to stop dragging & re-engage
|
||||
#check what distance we'd be at after a turn to face the target and another turn away
|
||||
#in other words: do we have the time to face the target & then turn to stay outside the gate?
|
||||
|
||||
#gate determined by risk level (LOW risk gate = DOR, MEDIUM risk gate = MAR)
|
||||
double gate = timeline.AuxDataDouble("DOR"); #range (meters)
|
||||
if (RISK == "MEDIUM")
|
||||
{
|
||||
gate = timeline.AuxDataDouble("MAR"); #range (meters)
|
||||
}
|
||||
|
||||
Array<double> vals = Array<double>();
|
||||
WsfGeoPoint targetLoc = mNearestTimeLineTarget.CurrentLocation();
|
||||
WsfTrack platAsTrack = PLATFORM.MakeTrack();
|
||||
platAsTrack.SetVelocity(mDragSpeed, platAsTrack.Heading());
|
||||
TurnTimeAndDistance(platAsTrack, targetLoc, mGeesToTurnWith, vals);
|
||||
double turnTime = vals[0];
|
||||
double distClosed = vals[1];
|
||||
double rangeAfterTurningIn = PLATFORM.GroundRangeTo(targetLoc) - distClosed - mNearestTimeLineTarget.Speed()*turnTime;
|
||||
double turnAwayTime = TimeToTurn(180, mEngageSpeed, mGeesToTurnWith);
|
||||
double rangeAfterTurningAway = rangeAfterTurningIn - mNearestTimeLineTarget.Speed()*turnAwayTime;
|
||||
if (rangeAfterTurningAway >= gate)
|
||||
{
|
||||
return Failure("enough time to turn & face before gate");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
writeln_d("testing conditions to START dragging");
|
||||
#not dragging yet, look at conditions required to start:
|
||||
#look at time required to turn & leave: compare to time until next gate
|
||||
|
||||
#gate determined by risk level (LOW risk gate = DOR, MEDIUM risk gate = MAR)
|
||||
double gate = timeline.AuxDataDouble("DOR"); #range (meters)
|
||||
if (RISK == "MEDIUM")
|
||||
{
|
||||
gate = timeline.AuxDataDouble("MAR"); #range (meters)
|
||||
}
|
||||
|
||||
Array<double> vals = Array<double>();
|
||||
WsfGeoPoint targetLoc = mNearestTimeLineTarget.CurrentLocation();
|
||||
double turnAngle = 180 - MATH.Fabs(PLATFORM.RelativeBearingTo(targetLoc));
|
||||
TurnAngleTimeAndDistance(turnAngle, mEngageSpeed, mGeesToTurnWith, vals);
|
||||
double turnTime = vals[0];
|
||||
double distSep = vals[1];
|
||||
double rangeAfterTurn = distSep + PLATFORM.GroundRangeTo(targetLoc) - mNearestTimeLineTarget.Speed()*turnTime;
|
||||
|
||||
if (rangeAfterTurn >= gate)
|
||||
{
|
||||
return Failure("agent not close enough to gate");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
end_precondition
|
||||
|
||||
execute
|
||||
extern double mDragSpeed;
|
||||
extern double mDragAltitude;
|
||||
Drag(PLATFORM, mNearestTimeLineTarget, mDragSpeed, mDragAltitude, mGeesToTurnWith); #max speed
|
||||
end_execute
|
||||
|
||||
end_behavior
|
||||
|
||||
92
processors/timeline_agents/behavior_weapon_support.txt
Normal file
92
processors/timeline_agents/behavior_weapon_support.txt
Normal file
@@ -0,0 +1,92 @@
|
||||
# ****************************************************************************
|
||||
# CUI
|
||||
#
|
||||
# The Advanced Framework for Simulation, Integration, and Modeling (AFSIM)
|
||||
#
|
||||
# The use, dissemination or disclosure of data in this file is subject to
|
||||
# limitation or restriction. See accompanying README and LICENSE for details.
|
||||
# ****************************************************************************
|
||||
|
||||
include_once common_timeline_scripts.txt
|
||||
include_once air_combat_maneuvers.txt
|
||||
|
||||
behavior weapon_support
|
||||
|
||||
script_debug_writes off
|
||||
|
||||
script_variables
|
||||
WsfQuantumTaskerProcessor processor;
|
||||
|
||||
Array<WsfLocalTrack> mEngaged = Array<WsfLocalTrack>();
|
||||
Array<double> mBearings = Array<double>();
|
||||
end_script_variables
|
||||
|
||||
on_init
|
||||
if (PROCESSOR.IsA_TypeOf("WSF_QUANTUM_TASKER_PROCESSOR"))
|
||||
{
|
||||
processor = (WsfQuantumTaskerProcessor)PROCESSOR;
|
||||
}
|
||||
end_on_init
|
||||
|
||||
precondition
|
||||
writeln_d(PLATFORM.Name(), " precondition weapon_support, T=", TIME_NOW);
|
||||
if (!processor.IsValid())
|
||||
{
|
||||
return Failure("behavior not attached to a WSF_QUANTUM_TASKER_PROCESSOR");
|
||||
}
|
||||
|
||||
extern bool mRequiredToSupportWeapons;
|
||||
if (mRequiredToSupportWeapons == false)
|
||||
{
|
||||
return Failure("not required to support weapons!");
|
||||
}
|
||||
|
||||
WsfTask timeline = GetTimeLineTask(processor);
|
||||
if (!timeline.IsValid())
|
||||
{
|
||||
return Failure("not assigned a TIMELINE task");
|
||||
}
|
||||
|
||||
extern Array<WsfLocalTrack> mTimeLineLocalTargets;
|
||||
mEngaged.Clear();
|
||||
mBearings.Clear();
|
||||
foreach(WsfLocalTrack t in mTimeLineLocalTargets)
|
||||
{
|
||||
if (PLATFORM.WeaponsActiveFor(t.TrackId()) > 0)
|
||||
{
|
||||
mEngaged.PushBack(t);
|
||||
mBearings.PushBack(PLATFORM.RelativeBearingTo(t.CurrentLocation()));
|
||||
}
|
||||
}
|
||||
if (mEngaged.Size() <= 0)
|
||||
{
|
||||
return Failure("no weapons to support against timeline targets");
|
||||
}
|
||||
return true;
|
||||
end_precondition
|
||||
|
||||
on_new_execute
|
||||
PLATFORM.Comment("crank");
|
||||
end_on_new_execute
|
||||
|
||||
execute
|
||||
SortLocalTracksByValue(mEngaged, mBearings);
|
||||
extern string mSeparateSide; # "LEFT" or "RIGHT" or other?
|
||||
extern double mOffsetAngle;
|
||||
extern double mDefaultAltitude;
|
||||
extern double mDelaySpeed;
|
||||
extern double mGeesToTurnWith;
|
||||
if (mSeparateSide == "RIGHT")
|
||||
{
|
||||
#crank right of left-most engaged target (front of array)
|
||||
Offset(PLATFORM, mEngaged.Front().CurrentLocation(), mOffsetAngle, mDefaultAltitude, mDelaySpeed, mGeesToTurnWith);
|
||||
}
|
||||
else #if (mSeparateSide == "LEFT")
|
||||
{
|
||||
#crank left of right-most engaged target (back of array)
|
||||
Offset(PLATFORM, mEngaged.Back().CurrentLocation(), -mOffsetAngle, mDefaultAltitude, mDelaySpeed, mGeesToTurnWith);
|
||||
}
|
||||
end_execute
|
||||
|
||||
end_behavior
|
||||
|
||||
398
processors/timeline_agents/common_timeline_scripts.txt
Normal file
398
processors/timeline_agents/common_timeline_scripts.txt
Normal file
@@ -0,0 +1,398 @@
|
||||
# ****************************************************************************
|
||||
# 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.
|
||||
# ****************************************************************************
|
||||
|
||||
script WsfLocalTrack GetMasterTrackByName(WsfPlatform aPlatform, string trackName)
|
||||
WsfLocalTrackList trackList = aPlatform.MasterTrackList();
|
||||
foreach (WsfLocalTrack track in trackList)
|
||||
{
|
||||
if (track.TargetName() == trackName)
|
||||
{
|
||||
return track;
|
||||
}
|
||||
}
|
||||
WsfLocalTrack x; # since we can't return null
|
||||
return x;
|
||||
end_script
|
||||
|
||||
|
||||
# aSpeed in meters per second, current platform speed
|
||||
# aMaxG in units of gravity (1g equivalent to 9.8 m/s^2)
|
||||
# adjust to min of 2 G if less is passed in
|
||||
script double TurnRadius(double aSpeed, double aMaxG)
|
||||
#RadialAccel = Speed^2/Radius
|
||||
double RadialAccel = aMaxG * Earth.ACCEL_OF_GRAVITY();
|
||||
if (aMaxG < 2.0)
|
||||
{
|
||||
RadialAccel = 2.0 * Earth.ACCEL_OF_GRAVITY();
|
||||
}
|
||||
double turnRadius = aSpeed * aSpeed / RadialAccel;
|
||||
return turnRadius;
|
||||
end_script
|
||||
|
||||
|
||||
#maxG in units of gravity (1g equivalent to 9.8 m/s^2)
|
||||
#adjust to min of 2 G if less is passed in
|
||||
script double TimeToTurnAndFace(WsfTrack turner, WsfGeoPoint targetPoint, double maxG)
|
||||
if (!turner.IsValid())
|
||||
{
|
||||
return MATH.DOUBLE_MAX();
|
||||
}
|
||||
double TurnAzimuth = MATH.Fabs(turner.RelativeBearingTo(targetPoint));
|
||||
double Speed = turner.Speed();
|
||||
double Radius = TurnRadius(Speed, maxG);
|
||||
double TurningCircleCircumference = 2.0 * MATH.PI() * Radius;
|
||||
double TurningArc = (TurnAzimuth/360)*TurningCircleCircumference;
|
||||
double TimeToTurn = TurningArc / Speed;
|
||||
return TimeToTurn;
|
||||
end_script
|
||||
|
||||
script double DistanceClosedDuringTurn(double turnAngle, double turnRadius)
|
||||
#lock the turn angle into [0,180]
|
||||
turnAngle = MATH.Fabs(MATH.NormalizeAngleMinus180_180(turnAngle));
|
||||
#a max turn (from relative bearing 180 back to 0 means we didn't close any distance at all
|
||||
#plot closing speed as a function of relative angle
|
||||
#this gives you a cosine curve from 0 to 180 with an amplitude of speed
|
||||
#use calculus for integrating the cosine curve from the relative angle the turner starts at back to zero
|
||||
#if solving for total area under curve, you break up an integral whenever it crosses the X axis
|
||||
#in our case, we don't want total area, we want positive area (positive closing speed)
|
||||
#so integrating cosine from [0,angle] where angle <= 180, we dont have to break it up into multiple integrals
|
||||
#integral of cosine is sin
|
||||
#showing -sin(0) for completeness when integrating
|
||||
double scale = MATH.Sin(turnAngle) - MATH.Sin(0);
|
||||
double distanceClosed = scale * turnRadius;
|
||||
return distanceClosed;
|
||||
end_script
|
||||
|
||||
script bool TurnAngleTimeAndDistance(double angle, double speed, double maxG, Array<double> TimeAndDistance)
|
||||
if (!TimeAndDistance.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
angle = MATH.Fabs(MATH.NormalizeAngleMinus180_180(angle));
|
||||
double Radius = TurnRadius(speed, maxG);
|
||||
double TurningCircleCircumference = 2.0 * MATH.PI() * Radius;
|
||||
double TurningArc = (angle/360)*TurningCircleCircumference;
|
||||
double TimeToTurn = TurningArc / speed;
|
||||
double DistanceClosed = DistanceClosedDuringTurn(angle, Radius);
|
||||
TimeAndDistance.Clear();
|
||||
TimeAndDistance.PushBack(TimeToTurn);
|
||||
TimeAndDistance.PushBack(DistanceClosed);
|
||||
return true;
|
||||
end_script
|
||||
|
||||
|
||||
#maxG in units of gravity (1g equivalent to 9.8 m/s^2)
|
||||
#adjust to min of 2 G if less is passed in
|
||||
script bool TurnTimeAndDistance(WsfTrack turner, WsfGeoPoint targetPoint, double maxG, Array<double> TimeAndDistance)
|
||||
if (!turner.IsValid() || !targetPoint.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
double angle = MATH.Fabs(turner.RelativeBearingTo(targetPoint));
|
||||
return TurnAngleTimeAndDistance(angle, turner.Speed(), maxG, TimeAndDistance);
|
||||
end_script
|
||||
|
||||
|
||||
script double TimeToTurnFrom(WsfPlatform plat, double speed, WsfTrack threat, double maxG)
|
||||
double runAwayTurn = 180 - MATH.Fabs(plat.RelativeBearingTo(threat));
|
||||
double radius = TurnRadius(speed, maxG);
|
||||
double turnCircle = 2.0 * MATH.PI() * radius;
|
||||
double turnArc = (runAwayTurn/360)*turnCircle;
|
||||
double turnTime = turnArc / speed;
|
||||
return turnTime;
|
||||
end_script
|
||||
|
||||
# Calculate how long it taks a platform to turn the specified angle
|
||||
# at specified speed (m/s) and max G
|
||||
script double TimeToTurn(double angle, double speed, double maxG)
|
||||
double radius = TurnRadius(speed, maxG);
|
||||
double turnCircle = 2.0 * MATH.PI() * radius;
|
||||
double turnArc = (angle/360)*turnCircle;
|
||||
double turnTime = turnArc / speed;
|
||||
return turnTime;
|
||||
end_script
|
||||
|
||||
#assumes current speed maintained
|
||||
script double TimeToReachPoint(WsfTrack traveler, WsfGeoPoint targetPoint, double maxG)
|
||||
if (!traveler.IsValid())
|
||||
{
|
||||
return MATH.DOUBLE_MAX();
|
||||
}
|
||||
double Speed = traveler.Speed();
|
||||
if (Speed <= 0.0)
|
||||
{
|
||||
return MATH.DOUBLE_MAX();
|
||||
}
|
||||
Array<double> vals = Array<double>();
|
||||
TurnTimeAndDistance(traveler, targetPoint, maxG, vals);
|
||||
double TurnTime = vals[0];
|
||||
double DistClosed = vals[1];
|
||||
double TravelLegDist = traveler.GroundRangeTo(targetPoint) - DistClosed;
|
||||
double TravelLegTime = TravelLegDist / traveler.Speed();
|
||||
double TimeToReach = TurnTime + TravelLegTime;
|
||||
return TimeToReach;
|
||||
end_script
|
||||
|
||||
|
||||
#bubble sort
|
||||
script void SortTracksByValue(Array<WsfTrack> tracks, Array<double> values)
|
||||
if(tracks.Size() != values.Size())
|
||||
return;
|
||||
bool swapped = true;
|
||||
int j = 0;
|
||||
WsfTrack tempTrack;
|
||||
double tempValue;
|
||||
while (swapped)
|
||||
{
|
||||
swapped = false;
|
||||
j += 1;
|
||||
for (int i=0; i<(values.Size())-j; i+=1)
|
||||
{
|
||||
if (values[i] > values[i+1])
|
||||
{
|
||||
tempValue = values[i]; tempTrack = tracks[i];
|
||||
values[i] = values[i+1]; tracks[i] = tracks[i+1];
|
||||
values[i+1] = tempValue; tracks[i+1] = tempTrack;
|
||||
swapped = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
end_script
|
||||
|
||||
script void SortLocalTracksByValue(Array<WsfLocalTrack> tracks, Array<double> values)
|
||||
if(tracks.Size() != values.Size())
|
||||
return;
|
||||
bool swapped = true;
|
||||
int j = 0;
|
||||
WsfLocalTrack tempTrack;
|
||||
double tempValue;
|
||||
while (swapped)
|
||||
{
|
||||
swapped = false;
|
||||
j += 1;
|
||||
for (int i=0; i<(values.Size())-j; i+=1)
|
||||
{
|
||||
if (values[i] > values[i+1])
|
||||
{
|
||||
tempValue = values[i]; tempTrack = tracks[i];
|
||||
values[i] = values[i+1]; tracks[i] = tracks[i+1];
|
||||
values[i+1] = tempValue; tracks[i+1] = tempTrack;
|
||||
swapped = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
end_script
|
||||
|
||||
script WsfGeoPoint GetAssetCentroid(Array<WsfAssetPerception> ASSETS)
|
||||
if (ASSETS.Size() <= 0)
|
||||
{
|
||||
return WsfGeoPoint.Construct(0,0,0);
|
||||
}
|
||||
double coeff = 1.0/((double)ASSETS.Size());
|
||||
Vec3 wcs = Vec3.Construct(0,0,0);
|
||||
foreach (WsfAssetPerception p in ASSETS)
|
||||
{
|
||||
Vec3 vec = p.Location().LocationWCS();
|
||||
vec.Scale(coeff);
|
||||
wcs = Vec3.Add(wcs, vec);
|
||||
}
|
||||
return WsfGeoPoint.ConstructWCS(wcs);
|
||||
end_script
|
||||
|
||||
script WsfTask GetTimeLineTask(WsfQuantumTaskerProcessor proc)
|
||||
WsfTaskList taskList = proc.TasksReceivedOfType("TIMELINE");
|
||||
if (taskList.Count() > 0)
|
||||
{
|
||||
return taskList.Entry(0);
|
||||
}
|
||||
WsfTask retTask; #dummy task
|
||||
return retTask;
|
||||
end_script
|
||||
|
||||
script double GetTimeLineTaskValueForKey(WsfTask timeline, string aKeyVal)
|
||||
if (timeline.IsValid())
|
||||
{
|
||||
if (timeline.AuxDataExists(aKeyVal))
|
||||
{
|
||||
return timeline.AuxDataDouble(aKeyVal);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeln_d(aKeyVal, " not found in ", timeline.TaskType() );
|
||||
}
|
||||
}
|
||||
return -1.0;
|
||||
end_script
|
||||
|
||||
script double GetTimeLineValueForKey(WsfQuantumTaskerProcessor proc, string aKeyVal)
|
||||
WsfTask timeline = GetTimeLineTask(proc);
|
||||
return GetTimeLineTaskValueForKey(timeline, aKeyVal);
|
||||
end_script
|
||||
|
||||
script WsfTrack GetTimeLineTaskTargetTrack(WsfPlatform plat, WsfTask timeline)
|
||||
WsfTrack t; #stub track
|
||||
if (timeline.IsValid())
|
||||
{
|
||||
if (timeline.AuxDataExists("targets"))
|
||||
{
|
||||
Array<WsfTrack> trkList = (Array<WsfTrack>)timeline.AuxDataObject("targets");
|
||||
foreach (WsfTrack trk in trkList)
|
||||
{
|
||||
if (trk.IsValid())
|
||||
{
|
||||
#see if the track can be found in local track list, if so return the distance
|
||||
WsfLocalTrack myTrack = GetMasterTrackByName(plat, trk.TargetName());
|
||||
if (myTrack.IsValid())
|
||||
{
|
||||
return myTrack;
|
||||
}
|
||||
#if the task assigned track isn't a track on the current platform, use the information given
|
||||
else
|
||||
{
|
||||
return trk;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return t;
|
||||
end_script
|
||||
|
||||
script WsfTrack GetTimeLineTargetTrack(WsfPlatform plat, WsfQuantumTaskerProcessor proc)
|
||||
WsfTask timeline = GetTimeLineTask(proc);
|
||||
return GetTimeLineTaskTargetTrack(plat, timeline);
|
||||
end_script
|
||||
|
||||
script Array<WsfTrack> GetTimeLineTaskTargets(WsfTask timeline)
|
||||
Array<WsfTrack> targets;
|
||||
if (timeline.IsValid() && timeline.AuxDataExists("targets"))
|
||||
{
|
||||
targets = (Array<WsfTrack>)timeline.AuxDataObject("targets");
|
||||
}
|
||||
else
|
||||
{
|
||||
targets = Array<WsfTrack>();
|
||||
}
|
||||
return targets;
|
||||
end_script
|
||||
|
||||
script Array<WsfTrack> GetTimeLineTargets(WsfQuantumTaskerProcessor proc)
|
||||
WsfTask timeline = GetTimeLineTask(proc);
|
||||
return GetTimeLineTaskTargets(timeline);
|
||||
end_script
|
||||
|
||||
script WsfTrack GetNearestTimeLineTaskTarget(WsfPlatform plat, WsfTask timeline)
|
||||
WsfTrack nearestTrack; #empty at first
|
||||
Array<WsfTrack> trkList = GetTimeLineTaskTargets(timeline);
|
||||
double nearest = MATH.DOUBLE_MAX();
|
||||
foreach (WsfTrack trk in trkList)
|
||||
{
|
||||
if (trk.IsValid())
|
||||
{
|
||||
double dist = plat.GroundRangeTo(trk.CurrentLocation());
|
||||
if (dist < nearest)
|
||||
{
|
||||
nearest = dist;
|
||||
nearestTrack = trk;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nearestTrack;
|
||||
end_script
|
||||
|
||||
script WsfTrack GetNearestTimeLineTarget(WsfPlatform plat, WsfQuantumTaskerProcessor proc)
|
||||
WsfTask timeline = GetTimeLineTask(proc);
|
||||
return GetNearestTimeLineTaskTarget(plat, timeline);
|
||||
end_script
|
||||
|
||||
script WsfLocalTrack GetNearestTimeLineTaskLocalTarget(WsfPlatform plat, WsfTask timeline)
|
||||
WsfLocalTrack nearestLocalTrack; #empty at first
|
||||
Array<WsfTrack> trkList = GetTimeLineTaskTargets(timeline);
|
||||
double nearest = MATH.DOUBLE_MAX();
|
||||
foreach (WsfTrack trk in trkList)
|
||||
{
|
||||
WsfLocalTrack lt = GetMasterTrackByName(plat,trk.TargetName());
|
||||
if (lt.IsValid())
|
||||
{
|
||||
double dist = plat.GroundRangeTo(lt.CurrentLocation());
|
||||
|
||||
if (dist < nearest)
|
||||
{
|
||||
nearest = dist;
|
||||
nearestLocalTrack = lt;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nearestLocalTrack;
|
||||
end_script
|
||||
|
||||
|
||||
script Array<WsfLocalTrack> GetTimeLineTaskLocalTargets(WsfPlatform plat, WsfTask timeline)
|
||||
Array<WsfTrack> targets = GetTimeLineTaskTargets(timeline);
|
||||
Array<WsfLocalTrack> localTargets = Array<WsfLocalTrack>();
|
||||
foreach(WsfTrack t in targets)
|
||||
{
|
||||
WsfLocalTrack lt = GetMasterTrackByName(plat, t.TargetName());
|
||||
if (lt.IsValid())
|
||||
{
|
||||
localTargets.PushBack(lt);
|
||||
}
|
||||
}
|
||||
return localTargets;
|
||||
end_script
|
||||
|
||||
script Array<WsfLocalTrack> GetTimeLineLocalTargets(WsfPlatform plat, WsfQuantumTaskerProcessor proc)
|
||||
WsfTask timeline = GetTimeLineTask(proc);
|
||||
return GetTimeLineTaskLocalTargets(plat, timeline);
|
||||
end_script
|
||||
|
||||
|
||||
script bool FiredOn(WsfPlatform shooter, WsfLocalTrack target, bool checkPeers)
|
||||
if ((shooter.WeaponsPendingFor(target.TrackId()) + shooter.WeaponsActiveFor(target.TrackId())) > 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (checkPeers)
|
||||
{
|
||||
foreach(WsfPlatform peer in shooter.Peers())
|
||||
{
|
||||
WsfLocalTrack peerTrack = GetMasterTrackByName(peer,target.TargetName());
|
||||
if (peerTrack.IsValid())
|
||||
{
|
||||
if ((peer.WeaponsPendingFor(peerTrack.TrackId()) + peer.WeaponsActiveFor(peerTrack.TrackId())) > 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
end_script
|
||||
|
||||
|
||||
script double GetTargetDistance(WsfPlatform plat, WsfQuantumTaskerProcessor proc)
|
||||
WsfTrack targetTrack = GetNearestTimeLineTarget(plat, proc);
|
||||
|
||||
if (targetTrack.IsValid())
|
||||
{
|
||||
# if (targetTrack.Target().IsValid() && PLATFORM.IsValid())
|
||||
# {
|
||||
# draw.SetLineStyle("solid");
|
||||
# draw.BeginLines();
|
||||
# draw.SetColor(1.0, 0.0, 1.0);
|
||||
# draw.Vertex(PLATFORM);
|
||||
# draw.Vertex(targetTrack.Target());
|
||||
# draw.End();
|
||||
# }
|
||||
|
||||
double dist = plat.GroundRangeTo(targetTrack.CurrentLocation());
|
||||
return dist;
|
||||
}
|
||||
return -1;
|
||||
end_script
|
||||
521
processors/timeline_agents/flight_lead_filtered_timeline.txt
Normal file
521
processors/timeline_agents/flight_lead_filtered_timeline.txt
Normal file
@@ -0,0 +1,521 @@
|
||||
# ****************************************************************************
|
||||
# CUI
|
||||
#
|
||||
# The Advanced Framework for Simulation, Integration, and Modeling (AFSIM)
|
||||
#
|
||||
# The use, dissemination or disclosure of data in this file is subject to
|
||||
# limitation or restriction. See accompanying README and LICENSE for details.
|
||||
# ****************************************************************************
|
||||
|
||||
include_once common_timeline_scripts.txt
|
||||
|
||||
processor FLIGHT_LEAD_FILTERED_TIMELINE WSF_QUANTUM_TASKER_PROCESSOR
|
||||
script_debug_writes on
|
||||
script_variables
|
||||
#asset evaluation on tasks:
|
||||
bool mCountWeaponsInEval = false;
|
||||
|
||||
#setting up the timeline:
|
||||
double FSR = 40 * MATH.M_PER_NM();
|
||||
double DOR = 35 * MATH.M_PER_NM();
|
||||
double MAR = 20 * MATH.M_PER_NM();
|
||||
|
||||
#risk level management
|
||||
bool mStaticRiskLevel = false;
|
||||
string RISK = "LOW"; #overwritten dynamically if 'mStaticRiskLevel' is true
|
||||
#based on range gates to protected location defined here
|
||||
WsfGeoPoint mProtectedLocation; #can be invalid, will just use lead's position
|
||||
double mLowRiskRange = 120 * MATH.M_PER_NM();
|
||||
double mMediumRiskRange = 90 * MATH.M_PER_NM();
|
||||
double mHighRiskRange = 60 * MATH.M_PER_NM();
|
||||
|
||||
#battle stance management
|
||||
bool mPassiveAllowed = true;
|
||||
Map<string,bool> mPassivePlayers = Map<string,bool>();
|
||||
|
||||
#values for engaging two groups
|
||||
bool mEnableTwoTimelines = true;
|
||||
double mMyWeaponRange = 40*1852; #used to figure out if threats are presenting to wide on azimuth for one shooter
|
||||
double mMaxTargetAzSeparation = 30.0; #degrees, max az separation of targets for one shooter
|
||||
|
||||
#########################################################################
|
||||
# Threat Filtering
|
||||
# Only consider known threats with soonest intercept time
|
||||
# 1000 knots closing speed = ~514 meters/sec
|
||||
# 15 nm range at 514 m/s closing speed = ~54 seconds
|
||||
#########################################################################
|
||||
double mDefaultTargetTurningGs = 6.0; # units of gravity (1 gravity = 9.8 m/s^2)
|
||||
double mInterceptTimeThreshold = 108.0; #our current calculation only accounts for threats speed, not ours
|
||||
#double mInterceptTimeThreshold = 27.0;
|
||||
#double mInterceptTimeThreshold = 54.0; # (sec) ignore all threats that will intercept us later than (the soonest threat + this time)
|
||||
Map<string,int> mKnownTargetTypes = Map<string,int>();
|
||||
mKnownTargetTypes["BLUE_STRIKER"] = 1;
|
||||
mKnownTargetTypes["RED_STRIKER"] = 1;
|
||||
mKnownTargetTypes["STRIKER"] = 1;
|
||||
|
||||
#values for support roles
|
||||
#bool mEnableSupportRoles = false;
|
||||
|
||||
|
||||
#########################################################################
|
||||
## util - dont touch
|
||||
double mSoonestIntercept = -1.0;
|
||||
WsfTrack mBiggestThreat;
|
||||
WsfClusterManager mClusterManager;
|
||||
WsfAssetPerception mShooter1;
|
||||
int mWeapons1 = 0;
|
||||
WsfAssetPerception mShooter2;
|
||||
int mWeapons2 = 0;
|
||||
WsfDraw mDraw = WsfDraw();
|
||||
end_script_variables
|
||||
|
||||
on_initialize
|
||||
mDraw.SetId("timeline_lead");
|
||||
mDraw.SetColor(1.0,0.5,0.0); #orange?
|
||||
mDraw.SetLineStyle("solid");
|
||||
mDraw.SetLineSize(1);
|
||||
mDraw.SetDuration(PROCESSOR.UpdateInterval());
|
||||
|
||||
mClusterManager = WsfClusterManager.Create();
|
||||
mClusterManager.SetClusterMethod("H_TREE_MAX");
|
||||
#if clustering is ever used in this agent, its to create two clusters for two timelines
|
||||
mClusterManager.SetDistanceFunction("POSITION_ONLY");
|
||||
mClusterManager.SetNumClustersToCreate(2);
|
||||
|
||||
if (!mProtectedLocation.IsValid())
|
||||
{
|
||||
mProtectedLocation = PLATFORM.Location();
|
||||
}
|
||||
|
||||
end_on_initialize
|
||||
|
||||
|
||||
state INITIAL
|
||||
on_entry
|
||||
PLATFORM.Comment("INITIAL state");
|
||||
end_on_entry
|
||||
|
||||
next_state BATTLE
|
||||
Map<string, bool> shots = Map<string, bool>();
|
||||
writeln_d("T=",TIME_NOW,", ", PLATFORM.Name(), " analyzing battle conditions!");
|
||||
WsfTaskList list = PROCESSOR.TasksAssignedOfType("TIMELINE");
|
||||
foreach(WsfTask task in list)
|
||||
{
|
||||
#bool roundsFired = task.Assignee().RoundsFiredAt(task.TrackId()) > 0;
|
||||
bool roundsFired = task.Assignee().RoundsCompleteFor(WsfTrackId()) > 0;
|
||||
writeln_d(" checking assigned task ", task.TrackId().ToString(), " assigned to ", task.AssigneeName(), "fired? ", roundsFired);
|
||||
shots[task.AssigneeName()] = roundsFired;
|
||||
}
|
||||
foreach (string assignee : bool shot in shots)
|
||||
{
|
||||
if (shot == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return (shots.Size() > 0);
|
||||
end_next_state
|
||||
|
||||
end_state
|
||||
|
||||
state BATTLE
|
||||
on_entry
|
||||
PLATFORM.Comment("BATTLE state");
|
||||
mPassiveAllowed = false;
|
||||
end_on_entry
|
||||
end_state
|
||||
|
||||
|
||||
script void UpdateRiskLevel(Array<WsfTrack> knownThreats)
|
||||
string oldrisk = RISK;
|
||||
double closest = MATH.DOUBLE_MAX();
|
||||
foreach(WsfTrack threat in knownThreats)
|
||||
{
|
||||
double dist = mProtectedLocation.GroundRangeTo(threat.CurrentLocation());
|
||||
closest = MATH.Min(closest, dist);
|
||||
}
|
||||
if (closest < mHighRiskRange)
|
||||
{
|
||||
RISK = "HIGH";
|
||||
}
|
||||
else if (closest < mMediumRiskRange)
|
||||
{
|
||||
RISK = "MEDIUM";
|
||||
}
|
||||
else
|
||||
{
|
||||
RISK = "LOW";
|
||||
}
|
||||
if (oldrisk != RISK)
|
||||
{
|
||||
PLATFORM.Comment(write_str(RISK," RISK"));
|
||||
}
|
||||
end_script
|
||||
|
||||
|
||||
script Array<WsfTrack> FilterTracks(Array<WsfLocalTrack> TRACKS, Array<WsfAssetPerception> ASSETS)
|
||||
Array<WsfTrack> filtered = Array<WsfTrack>();
|
||||
|
||||
#filter out threats that wont intercept us as soon as most threatening
|
||||
#find soonest intercept
|
||||
Array<double> interceptTimes = Array<double>();
|
||||
mSoonestIntercept = MATH.DOUBLE_MAX();
|
||||
for (int i=0; i<TRACKS.Size(); i=i+1)
|
||||
{
|
||||
WsfLocalTrack lt = TRACKS[i];
|
||||
writeln_d("T=", TIME_NOW, ", considering target ", lt.TargetName());
|
||||
|
||||
interceptTimes[i] = -1.0;
|
||||
|
||||
if (lt.IsValid() && !lt.TargetKilled() && (!lt.SideValid() || lt.Side() != PLATFORM.Side()))
|
||||
{
|
||||
if (mKnownTargetTypes.Size() <= 0 || mKnownTargetTypes.Exists(lt.TargetType()))
|
||||
{
|
||||
writeln_d("T=", TIME_NOW, ", did not filter target ", lt.TargetName(), " of type ", lt.TargetType());
|
||||
double time = MATH.DOUBLE_MAX();
|
||||
foreach (WsfAssetPerception p in ASSETS)
|
||||
{
|
||||
double temp = TimeToReachPoint(lt, p.Location(), mDefaultTargetTurningGs);
|
||||
if (temp < time)
|
||||
{
|
||||
time = temp;
|
||||
}
|
||||
}
|
||||
interceptTimes[i] = time;
|
||||
writeln_d("T=", TIME_NOW,", ", lt.TargetName()," time to reach = ", time);
|
||||
if (time > 0 && time < mSoonestIntercept)
|
||||
{
|
||||
mSoonestIntercept = time;
|
||||
mBiggestThreat = lt;
|
||||
}
|
||||
} else { writeln_d("UNKNOWN TARGET TYPE!"); }
|
||||
} else { writeln_d("TRACK NOT VALID (or similar error)"); }
|
||||
}
|
||||
|
||||
for (int i=0; i<TRACKS.Size(); i=i+1)
|
||||
{
|
||||
if (interceptTimes[i] > 0 && interceptTimes[i] <= (mSoonestIntercept + mInterceptTimeThreshold))
|
||||
{
|
||||
filtered.PushBack(TRACKS[i]);
|
||||
}
|
||||
# else ignore this threat for now, too far away
|
||||
}
|
||||
return filtered;
|
||||
end_script
|
||||
|
||||
|
||||
|
||||
|
||||
script Array<WsfQuantumTask> FlightLeadTaskGeneration (Array<WsfLocalTrack> TRACKS, Array<WsfAssetPerception> ASSETS )
|
||||
writeln_d("T=", TIME_NOW, ", ", PLATFORM.Name(), " generating tasks!");
|
||||
Array<WsfQuantumTask> tasks = Array<WsfQuantumTask>();
|
||||
if (ASSETS.Size() <= 0)
|
||||
{
|
||||
writeln_d("NO ASSETS!");
|
||||
return tasks;
|
||||
}
|
||||
if (TRACKS.Size() <= 0)
|
||||
{
|
||||
writeln_d("NO TRACKS!");
|
||||
}
|
||||
|
||||
#FILTER TRACKS DOWN TO ONLY TARGETS WE CARE ABOUT RIGHT NOW
|
||||
#TODO - find fastest/furthest along asset (as likliest to be first engaged by threats)
|
||||
Array<WsfTrack> filtered = FilterTracks(TRACKS, ASSETS);
|
||||
|
||||
if (mStaticRiskLevel == false)
|
||||
{
|
||||
UpdateRiskLevel(filtered);
|
||||
}
|
||||
|
||||
# mDraw.SetColor(0.6, 0.6, 0.6); #gray
|
||||
# mDraw.BeginLines();
|
||||
# foreach(WsfTrack target in filtered)
|
||||
# {
|
||||
# writeln_d("T=", TIME_NOW, " drawing filtered: ", target.TargetName());
|
||||
# mDraw.Vertex(PLATFORM);
|
||||
# mDraw.Vertex(target.CurrentLocation());
|
||||
# }
|
||||
# mDraw.End();
|
||||
|
||||
if (filtered.Size() <= 0)
|
||||
{
|
||||
return tasks;
|
||||
}
|
||||
#DEBUG - draw the filtered tracks with a purple hull
|
||||
mClusterManager.Draw(filtered, PROCESSOR.UpdateInterval(), Vec3.Construct(0.2,0.2,1.0), "hull");
|
||||
|
||||
# #DEBUG - draw line from asset centroid to filtered threats centroid
|
||||
# mDraw.SetColor(1.0,1.0,0.0); #yellow?
|
||||
# mDraw.SetLineStyle("solid");
|
||||
# mDraw.SetLineSize(1);
|
||||
# mDraw.BeginLines();
|
||||
# mDraw.Vertex(AssetCentroid);
|
||||
# mDraw.Vertex(FilteredCluster.MeanLocation());
|
||||
# mDraw.End();
|
||||
|
||||
|
||||
#CALCULATE IF THE FLIGHT EXECUTES ONE OR TWO TIMELINES
|
||||
#first find if we have two+ shooters or not
|
||||
int totalWeapons = 0;
|
||||
mWeapons1 = 0;
|
||||
mWeapons2 = 0;
|
||||
bool haveTwoShooters = false;
|
||||
foreach(WsfAssetPerception ASSET in ASSETS)
|
||||
{
|
||||
for (int i=0; i<ASSET.SystemCount(); i+=1)
|
||||
{
|
||||
#TODO - limit the consideration to only certain [better] weapons
|
||||
# instead of just first weapon in system list
|
||||
if (ASSET.SystemKind(i) == "weapon")
|
||||
{
|
||||
int weapons = ASSET.SystemQuantityRemaining(i);
|
||||
totalWeapons += weapons;
|
||||
if (mPassiveAllowed == true && mPassivePlayers[ASSET.Name()] == true)
|
||||
{
|
||||
break; #passive players will not fly "point" on a timeline nor shoot
|
||||
}
|
||||
if (weapons > mWeapons1)
|
||||
{
|
||||
if (mWeapons1 > mWeapons2)
|
||||
{
|
||||
mShooter2 = mShooter1;
|
||||
mWeapons2 = mWeapons1;
|
||||
}
|
||||
mShooter1 = ASSET;
|
||||
mWeapons1 = weapons;
|
||||
}
|
||||
else if (weapons > mWeapons2)
|
||||
{
|
||||
mShooter2 = ASSET;
|
||||
mWeapons2 = weapons;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (filtered.Size() > totalWeapons)
|
||||
{
|
||||
writeln_d(PLATFORM.Name(), ", WARNING! More threats than this flight has weapons!");
|
||||
}
|
||||
|
||||
if (filtered.Size() > (mWeapons1+mWeapons2))
|
||||
{
|
||||
writeln_d(PLATFORM.Name(), ", WARNING! More threats than two shooters have weapons! Enable all as shooters?");
|
||||
}
|
||||
|
||||
haveTwoShooters = (mWeapons1 > 0 && mWeapons2 > 0);
|
||||
|
||||
#TODO - more intelligent calculation of azimuth presentation:
|
||||
# find likely first shot location (from shooter 1 to biggest threat)
|
||||
# sort all extrapolatead threats based on relative azimuth at this shot location
|
||||
# figure out if the threats are presenting on azimuth and will require two timelines
|
||||
|
||||
#for now - sort filtered tracks on azimuth
|
||||
# compare "width" of threats (left most to right most) to "length" of weapon
|
||||
Array<double> bearings = Array<double>();
|
||||
foreach(WsfTrack t in filtered)
|
||||
{
|
||||
double bearing = ASSETS[0].RelativeBearingTo(t.CurrentLocation());
|
||||
bearings.PushBack(bearing);
|
||||
}
|
||||
SortTracksByValue(filtered, bearings);
|
||||
|
||||
#debug: print sorted (by bearing) order to screen
|
||||
mDraw.SetDuration(PROCESSOR.UpdateInterval());
|
||||
mDraw.SetColor(1.0,0.5,0.0); #orange?
|
||||
mDraw.SetLineStyle("solid");
|
||||
mDraw.SetTextSize(16);
|
||||
for (int i=0; i<filtered.Size(); i+=1)
|
||||
{
|
||||
mDraw.BeginText(write_str(i));
|
||||
mDraw.Vertex(filtered[i].CurrentLocation());
|
||||
mDraw.End();
|
||||
}
|
||||
|
||||
#calculate if the threats are presenting across a wide azimuth
|
||||
bool AzimuthPresentation = true;
|
||||
WsfTrack left = filtered.Front();
|
||||
WsfTrack right = filtered.Back();
|
||||
double width = left.CurrentLocation().GroundRangeTo(right.CurrentLocation());
|
||||
double length = mMyWeaponRange;
|
||||
#assume shooter 1 flies towards middle of angle separation
|
||||
double halfAngle = MATH.ATan2(width/2.0,length);
|
||||
if ((2.0*halfAngle) < mMaxTargetAzSeparation)
|
||||
{
|
||||
AzimuthPresentation = false;
|
||||
}
|
||||
|
||||
if (mEnableTwoTimelines &&
|
||||
haveTwoShooters && #only create two timelines if we have 2 shooters???
|
||||
filtered.Size() > 1 && #two timelines only possible if more than 1 threat exists
|
||||
( mWeapons1 < filtered.Size() || #if MUST use 2 shooters because of weapon counts
|
||||
AzimuthPresentation) #OR if must use 2 shooters because of threat presentation
|
||||
)
|
||||
{
|
||||
#create two timeline tasks
|
||||
#first make mShooter1 the left most shooter
|
||||
WsfGeoPoint p1 = filtered[0].CurrentLocation(); #left target
|
||||
double d1 = mShooter1.Location().GroundRangeTo(p1);
|
||||
double d2 = mShooter2.Location().GroundRangeTo(p1);
|
||||
if (d2 < d1)
|
||||
{
|
||||
#swap 1 and 2 so mShooter1 refers to left shooter
|
||||
int tWeapons; WsfAssetPerception tShooter;
|
||||
tShooter = mShooter1; tWeapons = mWeapons1;
|
||||
mShooter1 = mShooter2; mWeapons1 = mWeapons2;
|
||||
mShooter2 = tShooter; mWeapons2 = tWeapons;
|
||||
}
|
||||
|
||||
|
||||
#TODO - consider group up threats on azimuth???
|
||||
#TODO - consider other azimuth-based algorithms to separate the threats
|
||||
#for now - make two clusters
|
||||
WsfGeoPoint SeparateA;
|
||||
WsfGeoPoint SeparateB;
|
||||
|
||||
Array<Array<WsfTrack>> clusters = mClusterManager.GetClusters(filtered);
|
||||
#should be 2 clusters now
|
||||
if (clusters.Size() != 2)
|
||||
{
|
||||
writeln("ERROR: cluster manager did not create two clusters! ABORT task generation!");
|
||||
return tasks;
|
||||
}
|
||||
else
|
||||
{
|
||||
#draw separation lines
|
||||
WsfGeoPoint a = mClusterManager.MeanLocation(clusters[0]);
|
||||
WsfGeoPoint b = mClusterManager.MeanLocation(clusters[1]);
|
||||
double az = a.TrueBearingTo(b);
|
||||
double dist = a.GroundRangeTo(b);
|
||||
#WsfGeoPoint c = WsfGeoPoint(a);
|
||||
SeparateA = WsfGeoPoint(a);
|
||||
SeparateA.OffsetRBE(dist/2.0,az,0.0);
|
||||
#WsfGeoPoint d = WsfGeoPoint(c);
|
||||
SeparateB = WsfGeoPoint(SeparateA);
|
||||
SeparateA.OffsetRBE(DOR,az+90.0,0.0);
|
||||
SeparateB.OffsetRBE(DOR,az-90.0,0.0);
|
||||
|
||||
# mDraw.SetColor(1.0,1.0,1.0); #white
|
||||
# mDraw.SetLineStyle("solid");
|
||||
# mDraw.SetLineSize(1);
|
||||
# mDraw.BeginLines();
|
||||
# mDraw.Vertex(a);
|
||||
# mDraw.Vertex(b);
|
||||
# mDraw.Vertex(SeparateA);
|
||||
# mDraw.Vertex(SeparateB);
|
||||
# mDraw.End();
|
||||
}
|
||||
|
||||
for (int i = 0; i < clusters.Size(); i += 1 )
|
||||
{
|
||||
string shapeType = "hull";
|
||||
|
||||
if(Math.Mod(i,2) == 0)
|
||||
{
|
||||
shapeType = "star";
|
||||
}
|
||||
|
||||
#Draw a green shape around the cluster for one update cycle
|
||||
mClusterManager.Draw(clusters[i], PROCESSOR.UpdateInterval(), Vec3.Construct(0.0,1.0,0.0), shapeType);
|
||||
|
||||
WsfQuantumTask task = WsfQuantumTask.Construct(1.0, "TIMELINE");
|
||||
task.SetTaskType("TIMELINE");
|
||||
Array<WsfTrack> targets = Array<WsfTrack>();
|
||||
for (int j=0; j<clusters[i].Size(); j+=1)
|
||||
{
|
||||
targets.PushBack(WsfTrack(clusters[i][j]));
|
||||
}
|
||||
task.SetAuxData("targets",targets);
|
||||
task.SetAuxData("FSR",FSR);
|
||||
task.SetAuxData("DOR",DOR);
|
||||
task.SetAuxData("MAR",MAR);
|
||||
task.SetAuxData("RISK",RISK);
|
||||
task.SetAuxData("point_a",SeparateA);
|
||||
task.SetAuxData("point_b",SeparateB);
|
||||
task.SetAuxData("cluster_mean", mClusterManager.MeanLocation(clusters[i]));
|
||||
task.UniqueId(mClusterManager.UniqueId(targets));
|
||||
tasks.PushBack(task);
|
||||
writeln_d("timeline task generated for cluster: ", mClusterManager.UniqueId(clusters[i]));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#create just one timeline task
|
||||
WsfQuantumTask task = WsfQuantumTask.Construct(1.0, "TIMELINE");
|
||||
task.SetTaskType("TIMELINE");
|
||||
Array<WsfTrack> targets = Array<WsfTrack>();
|
||||
foreach(WsfTrack t in filtered)
|
||||
{
|
||||
targets.PushBack(WsfTrack(t));
|
||||
}
|
||||
task.SetAuxData("targets",targets);
|
||||
task.SetAuxData("FSR",FSR);
|
||||
task.SetAuxData("DOR",DOR);
|
||||
task.SetAuxData("MAR",MAR);
|
||||
task.SetAuxData("RISK",RISK);
|
||||
task.UniqueId(PLATFORM.Index()); #only timeline task this lead will generate
|
||||
tasks.PushBack(task);
|
||||
writeln_d("timeline task generated for all filtered tracks!");
|
||||
}
|
||||
|
||||
return tasks;
|
||||
end_script
|
||||
|
||||
|
||||
script double FlightLeadEvaluation ( WsfQuantumTask TASK, WsfAssetPerception ASSET)
|
||||
writeln_d(PLATFORM.Name(), " evaluating task type ", TASK.TaskType());
|
||||
#TODO - include weapon capability in evaluation
|
||||
# for now: just base value on range
|
||||
if (TASK.TaskType() == "TIMELINE")
|
||||
{
|
||||
if (mPassiveAllowed == true && mPassivePlayers[ASSET.Name()] == true)
|
||||
{
|
||||
return 0; #passive players do not do weapon tasks for now
|
||||
}
|
||||
|
||||
Array<WsfTrack> targets = (Array<WsfTrack>)TASK.AuxDataObject("targets");
|
||||
if (targets.Size() > 0)
|
||||
{
|
||||
WsfGeoPoint clusterMeanPoint = mClusterManager.MeanLocation(targets);
|
||||
double range = ASSET.Location().GroundRangeTo(clusterMeanPoint);
|
||||
double value = 1.0 / (range * range);
|
||||
if (mCountWeaponsInEval == true)
|
||||
{
|
||||
double weaponCount = 0;
|
||||
for (int i=0; i<ASSET.SystemCount(); i+=1)
|
||||
{
|
||||
if (ASSET.SystemKind(i) == "weapon")
|
||||
{
|
||||
weaponCount += ASSET.SystemQuantityRemaining(i);
|
||||
}
|
||||
}
|
||||
if (weaponCount >= targets.Size())
|
||||
{
|
||||
value *= 2.0;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
end_script
|
||||
|
||||
#show_task_messages
|
||||
#script_debug_writes on
|
||||
update_interval 10.0 sec
|
||||
asset_representation platform
|
||||
reallocation_strategy dynamic
|
||||
#reallocation_strategy event
|
||||
generator custom FlightLeadTaskGeneration
|
||||
evaluator custom FlightLeadEvaluation
|
||||
#allocator optimal_profit type TIMELINE
|
||||
#allocator greedy_profit
|
||||
allocator optimal_profit
|
||||
allocator_extra_assets greedy_profit
|
||||
#allocator_extra_tasks optimal_profit
|
||||
#allocator_extra_assets optimal_profit
|
||||
|
||||
end_processor
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
# ****************************************************************************
|
||||
# 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.
|
||||
# ****************************************************************************
|
||||
|
||||
processor FLIGHT_LEAD_DATA WSF_QUANTUM_TASKER_PROCESSOR
|
||||
|
||||
script_variables
|
||||
#########################################################################
|
||||
## for evaluating weapon tasks
|
||||
Map<string,int> mKnownTargetTypes = Map<string,int>();
|
||||
mKnownTargetTypes["STRIKER"] = 1;
|
||||
mKnownTargetTypes["BLUE_STRIKER"] = 1;
|
||||
mKnownTargetTypes["RED_STRIKER"] = 1;
|
||||
#########################################################################
|
||||
double FSR = 30 * MATH.M_PER_NM();
|
||||
double DOR = 20 * MATH.M_PER_NM();
|
||||
double MAR = 10 * MATH.M_PER_NM();
|
||||
string RISK = "LOW";
|
||||
bool mStaticRiskLevel = true;
|
||||
end_script_variables
|
||||
|
||||
#on_initialize
|
||||
#end_on_initialize
|
||||
|
||||
script Array<WsfQuantumTask> FlightLeadTaskGeneration (Array<WsfLocalTrack> TRACKS, Array<WsfAssetPerception> ASSETS )
|
||||
Array<WsfQuantumTask> tasks = Array<WsfQuantumTask>();
|
||||
|
||||
extern Map<int, int> mGrinderPairings;
|
||||
mGrinderPairings.Clear();
|
||||
|
||||
#if its us, then create weapon tasks for enemy tracks
|
||||
for (int i=0; i<TRACKS.Size(); i=i+1)
|
||||
{
|
||||
WsfLocalTrack lt = TRACKS.Get(i);
|
||||
if (lt.IsValid() && (!lt.SideValid() || lt.Side() != PLATFORM.Side()))
|
||||
{
|
||||
if (mKnownTargetTypes.Size() <= 0 || mKnownTargetTypes.Exists(lt.TargetType()))
|
||||
{
|
||||
|
||||
#WsfQuantumTask task = WsfQuantumTask.Construct(1.0, "TIMELINE");
|
||||
#task.SetUniqueId(lt.TargetIndex()); #use this for now, use cluster Id later
|
||||
#WsfQuantumTask task = WsfQuantumTask.Construct(1.0, "TIMELINE", lt);
|
||||
Array<WsfTrack> targets = Array<WsfTrack>();
|
||||
targets.PushBack(WsfTrack(lt));
|
||||
|
||||
WsfQuantumTask task = WsfQuantumTask.Construct(1.0, "TIMELINE");
|
||||
task.UniqueId(lt.TargetIndex()); #use this for now, use cluster Id later
|
||||
task.SetAuxData("targets",targets);
|
||||
task.SetAuxData("FSR",FSR);
|
||||
task.SetAuxData("DOR",DOR);
|
||||
task.SetAuxData("MAR",MAR);
|
||||
task.SetAuxData("RISK",RISK);
|
||||
|
||||
tasks.PushBack(task);
|
||||
writeln_d("timeline task generated for: ", lt.TargetName(), ", updated time: ", lt.UpdateTime());
|
||||
|
||||
# Generate a second timeline task for support
|
||||
WsfQuantumTask taskSup = WsfQuantumTask.Construct(1.0, "TIMELINE");
|
||||
taskSup.UniqueId(lt.TargetIndex() + 123456); //use this for now, use cluster Id later
|
||||
taskSup.SetAuxData("targets",targets);
|
||||
taskSup.SetAuxData("FSR",FSR);
|
||||
taskSup.SetAuxData("DOR",DOR);
|
||||
taskSup.SetAuxData("MAR",MAR);
|
||||
taskSup.SetAuxData("RISK",RISK);
|
||||
|
||||
tasks.PushBack(taskSup);
|
||||
writeln_d("timeline support task generated for: ", lt.TargetName(), ", updated time: ", lt.UpdateTime());
|
||||
|
||||
# Map task IDs to platforms doing grinder can look each other up
|
||||
mGrinderPairings.Set(lt.TargetIndex(), lt.TargetIndex() + 123456);
|
||||
mGrinderPairings.Set(lt.TargetIndex() + 123456, lt.TargetIndex());
|
||||
}
|
||||
}
|
||||
}
|
||||
return tasks;
|
||||
end_script
|
||||
|
||||
|
||||
script double FlightLeadEvaluation ( WsfQuantumTask TASK, WsfAssetPerception ASSET)
|
||||
writeln_d(PLATFORM.Name(), " evaluating task type ", TASK.TaskType());
|
||||
#TODO - include weapon capability in evaluation
|
||||
# for now: just base value on range
|
||||
if (TASK.TaskType() == "TIMELINE")
|
||||
{
|
||||
Array<WsfTrack> targets = (Array<WsfTrack>)TASK.AuxDataObject("targets");
|
||||
if (targets.Size() > 0)
|
||||
{
|
||||
WsfTrack target = targets.Get(0);
|
||||
writeln_d("evaluating timeline task on: ", target.TargetName(), ", updated time: ", target.UpdateTime());
|
||||
double value = 1.0 / target.GroundRangeTo(ASSET.Location());
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
end_script
|
||||
|
||||
#show_task_messages
|
||||
script_debug_writes off
|
||||
update_interval 10.0 sec
|
||||
asset_representation platform
|
||||
#reallocation_strategy event
|
||||
reallocation_strategy dynamic
|
||||
generator custom FlightLeadTaskGeneration
|
||||
evaluator custom FlightLeadEvaluation
|
||||
|
||||
# #allocator greedy_isolated type TIMELINE
|
||||
# #allocator greedy_isolated
|
||||
# #allocator_extra_tasks optimal_profit
|
||||
# allocator optimal_profit type TIMELINE
|
||||
# allocator_extra_assets optimal_profit type TIMELINE
|
||||
# allocator greedy_isolated
|
||||
allocator optimal_profit
|
||||
allocator_extra_assets optimal_profit
|
||||
|
||||
|
||||
end_processor
|
||||
|
||||
8
processors/timeline_agents/placeholder.txt
Normal file
8
processors/timeline_agents/placeholder.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
# ****************************************************************************
|
||||
# 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.
|
||||
# ****************************************************************************
|
||||
197
processors/timeline_agents/timeline_flyer_proc.txt
Normal file
197
processors/timeline_agents/timeline_flyer_proc.txt
Normal file
@@ -0,0 +1,197 @@
|
||||
# ****************************************************************************
|
||||
# CUI
|
||||
#
|
||||
# The Advanced Framework for Simulation, Integration, and Modeling (AFSIM)
|
||||
#
|
||||
# The use, dissemination or disclosure of data in this file is subject to
|
||||
# limitation or restriction. See accompanying README and LICENSE for details.
|
||||
# ****************************************************************************
|
||||
|
||||
#include_once processors/quantum_agents/aiai/behavior_engage_weapon_task_target.txt
|
||||
include_once processors/quantum_agents/aiai/behavior_escort.txt
|
||||
include_once processors/quantum_agents/aiai/behavior_evade.txt
|
||||
include_once processors/quantum_agents/aiai/behavior_go_home.txt
|
||||
include_once processors/quantum_agents/aiai/behavior_planned_route.txt
|
||||
#include_once processors/quantum_agents/aiai/behavior_pursue-target_route_finder.txt
|
||||
include_once processors/quantum_agents/aiai/behavior_pursue_weapon_task_target.txt
|
||||
|
||||
include_once behavior_fly_timeline.txt
|
||||
include_once behavior_engage_on_timeline.txt
|
||||
include_once air_combat_maneuvers.txt
|
||||
include_once common_timeline_scripts.txt
|
||||
|
||||
processor TIMELINE_FLYER_PROC WSF_QUANTUM_TASKER_PROCESSOR
|
||||
#show_task_messages
|
||||
script_debug_writes off
|
||||
update_interval 5 sec
|
||||
|
||||
script_variables
|
||||
bool stallForSupport = true;
|
||||
WsfDraw draw = WsfDraw();
|
||||
end_script_variables
|
||||
|
||||
|
||||
state COMMIT #should this be "SEARCH" or "MISSION"? meh...
|
||||
on_entry
|
||||
PLATFORM.Comment("COMMIT");
|
||||
end_on_entry
|
||||
behavior_tree
|
||||
selector
|
||||
behavior_node evade #YES, evade will likely be in every state
|
||||
behavior_node planned_route
|
||||
end_selector
|
||||
end_behavior_tree
|
||||
next_state APPROACH
|
||||
# #NAC+ at this point the fighter has committed to the target and received a timeline?
|
||||
# double curDist = GetTargetDistance(PLATFORM, PROCESSOR);
|
||||
# string distStr = (string)curDist;
|
||||
# #string distStr = (string)tgtDist;
|
||||
# string timelineStr = "COMMIT " + distStr;
|
||||
# PLATFORM.Comment(timelineStr);
|
||||
# double timelineVal = GetTimeLineValueForKey(PROCESSOR,"FSR");
|
||||
# writeln_d("timelineVal = ", timelineVal);
|
||||
# if (curDist != -1 && curDist <= GetTimeLineValueForKey(PROCESSOR,"FSR"))
|
||||
# {
|
||||
# return true;
|
||||
# }
|
||||
return GetTimelineTask(PROCESSOR).IsValid();
|
||||
end_next_state
|
||||
end_state
|
||||
|
||||
|
||||
state APPROACH
|
||||
on_entry
|
||||
PLATFORM.Comment("APPROACH");
|
||||
end_on_entry
|
||||
behavior_tree
|
||||
selector
|
||||
behavior_node evade
|
||||
behavior_node fly_timeline
|
||||
end_selector
|
||||
end_behavior_tree
|
||||
next_state SHOOT_MANEUVER
|
||||
#Engage(PLATFORM, GetNearestTimelineTarget(PLATFORM, PROCESSOR));
|
||||
double curDist = GetTargetDistance(PLATFORM, PROCESSOR);
|
||||
# string distStr = (string)curDist;
|
||||
# string timelineStr = "APPROACH " + distStr;
|
||||
# PLATFORM.Comment(timelineStr);
|
||||
if (curDist != -1 && curDist <= GetTimeLineValueForKey(PROCESSOR,"DOR") && !stallForSupport)
|
||||
{
|
||||
#Take the shot
|
||||
WsfTrack targetTrack = GetNearestTimelineTarget(PLATFORM, PROCESSOR);
|
||||
if (targetTrack.IsValid() && PLATFORM.WeaponCount() > 0)
|
||||
{
|
||||
WsfWeapon myWeap = PLATFORM.WeaponEntry(0);
|
||||
if (myWeap.IsValid())
|
||||
{
|
||||
myWeap.Fire(targetTrack);
|
||||
PLATFORM.Comment("Fired Shot!");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
end_next_state
|
||||
#NAC+ : Branch state here to maneuver if outside of OSR and need to slow down engagement or do something else
|
||||
next_state DELAY_MANEUVER
|
||||
double curDist = GetTargetDistance(PLATFORM, PROCESSOR);
|
||||
# string distStr = (string)curDist;
|
||||
# string timelineStr = "APPROACH delay" + distStr;
|
||||
# PLATFORM.Comment(timelineStr);
|
||||
if (curDist != -1 && curDist <= GetTimeLineValueForKey(PROCESSOR,"DOR") && stallForSupport)
|
||||
{
|
||||
#perform a delaying maneuver
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
end_next_state
|
||||
end_state
|
||||
|
||||
|
||||
state SHOOT_MANEUVER
|
||||
on_entry
|
||||
PLATFORM.Comment("SHOOT_MANEUVER");
|
||||
end_on_entry
|
||||
behavior_tree
|
||||
selector
|
||||
behavior_node evade
|
||||
behavior_node fly_timeline
|
||||
behavior_node planned_route
|
||||
end_selector
|
||||
#behavior_node engage_weapon_task_target
|
||||
end_behavior_tree
|
||||
next_state FLY_TO_DOR
|
||||
#Engage(PLATFORM, GetNearestTimelineTarget(PLATFORM, PROCESSOR));
|
||||
#Beam(PLATFORM, GetNearestTimelineTarget(PLATFORM, PROCESSOR));
|
||||
#Crank(PLATFORM, GetNearestTimelineTarget(PLATFORM, PROCESSOR));
|
||||
Drag(PLATFORM, GetNearestTimelineTarget(PLATFORM, PROCESSOR));
|
||||
#PostHole(PLATFORM, "right");
|
||||
double curDist = GetTargetDistance(PLATFORM, PROCESSOR);
|
||||
# string distStr = (string)curDist;
|
||||
# string timelineStr = "Shoot Maneuver " + distStr;
|
||||
# PLATFORM.Comment(timelineStr);
|
||||
if (curDist != -1 && curDist <= GetTimeLineValueForKey(PROCESSOR,"MAR"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
end_next_state
|
||||
end_state
|
||||
|
||||
|
||||
state DELAY_MANEUVER
|
||||
on_entry
|
||||
PLATFORM.Comment("DELAY_MANEUVER");
|
||||
end_on_entry
|
||||
next_state APPROACH
|
||||
#Engage(PLATFORM, GetNearestTimelineTarget(PLATFORM, PROCESSOR));
|
||||
#Beam(PLATFORM, GetNearestTimelineTarget(PLATFORM, PROCESSOR));
|
||||
#Crank(PLATFORM, GetNearestTimelineTarget(PLATFORM, PROCESSOR));
|
||||
Drag(PLATFORM, GetNearestTimelineTarget(PLATFORM, PROCESSOR));
|
||||
stallForSupport = false;
|
||||
#PostHole(PLATFORM, "right");
|
||||
#PLATFORM.Comment("Stall Maneuver!");
|
||||
return true;
|
||||
end_next_state
|
||||
end_state
|
||||
|
||||
|
||||
state FLY_TO_DOR
|
||||
on_entry
|
||||
PLATFORM.Comment("FLY_TO_DOR");
|
||||
end_on_entry
|
||||
behavior_tree
|
||||
#behavior_node pursue_weapon_task_target
|
||||
behavior_node fly_timeline
|
||||
end_behavior_tree
|
||||
next_state GO_HOME
|
||||
double curDist = GetTargetDistance(PLATFORM, PROCESSOR);
|
||||
# string distStr = (string)curDist;
|
||||
# string timelineStr = "FLY_TO_DOR " + distStr;
|
||||
# PLATFORM.Comment(timelineStr);
|
||||
if (curDist <= GetTimeLineValueForKey(PROCESSOR,"MAR"))
|
||||
{
|
||||
#PLATFORM.Comment("Reached MAR");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
end_next_state
|
||||
end_state
|
||||
|
||||
|
||||
state GO_HOME
|
||||
on_entry
|
||||
PLATFORM.Comment("GO_HOME");
|
||||
end_on_entry
|
||||
behavior_tree
|
||||
behavior_node planned_route
|
||||
end_behavior_tree
|
||||
next_state COMMIT
|
||||
WsfWaypoint waypt = PLATFORM.Mover().Route().Front();
|
||||
PLATFORM.GoToLocation(waypt);
|
||||
# PLATFORM.Comment("Flying Home");
|
||||
return false;
|
||||
end_next_state
|
||||
end_state
|
||||
|
||||
end_processor
|
||||
328
processors/timeline_agents/timeline_proc.txt
Normal file
328
processors/timeline_agents/timeline_proc.txt
Normal file
@@ -0,0 +1,328 @@
|
||||
# ****************************************************************************
|
||||
# CUI
|
||||
#
|
||||
# The Advanced Framework for Simulation, Integration, and Modeling (AFSIM)
|
||||
#
|
||||
# The use, dissemination or disclosure of data in this file is subject to
|
||||
# limitation or restriction. See accompanying README and LICENSE for details.
|
||||
# ****************************************************************************
|
||||
|
||||
include_once behavior_timeline_reposition.txt #drag to restart further back on timeline
|
||||
include_once behavior_timeline_delay.txt #maneuver to delay enemy's engagement
|
||||
include_once behavior_timeline_engage.txt #intercept/shoot timeline targets
|
||||
#include_once behavior_timeline_support.txt #fly support role on a timeline (usually behind lead shooter role)
|
||||
include_once behavior_weapon_support.txt #crank & support weapon
|
||||
include_once behavior_planned_route.txt #no timeline tasks, just return to route
|
||||
include_once behavior_evade.txt #evade iminent threats (missiles)
|
||||
include_once behavior_go_home.txt #disengage, go back to initial location
|
||||
|
||||
|
||||
#include_once behavior_fly_timeline.txt
|
||||
|
||||
processor TIMELINE_PROC WSF_QUANTUM_TASKER_PROCESSOR
|
||||
update_interval 2 sec
|
||||
script_variables
|
||||
double mGeesToTurnWith = 6.0; #update this however intelligently you can (based on risk level?)
|
||||
#double mOffsetAngle = 45.0; #TODO - calculate half sensor angle?
|
||||
double mOffsetAngle = 30.0; #TODO - calculate half sensor angle?
|
||||
|
||||
double mDelaySpeed = 204; #0.6
|
||||
double mEngageSpeed = 237; #0.8
|
||||
double mDragSpeed = 309; #1.2
|
||||
|
||||
double mDefaultAltitude = 35000 * MATH.M_PER_FT();
|
||||
#double mDragAltitude = 30000 * MATH.M_PER_FT();
|
||||
double mDragAltitude = 35000 * MATH.M_PER_FT(); #testing
|
||||
|
||||
bool mConsiderPeersWeapons = true; #do not shoot if peers already are
|
||||
#bool mRequiredToSupportWeapons = true; #TODO - update dynamically somehow?
|
||||
bool mRequiredToSupportWeapons = true; #TODO - update dynamically somehow?
|
||||
|
||||
double DefaultPercentRangeMax = 0.80; # don't launch unless within this percent of Rmax
|
||||
double DefaultPercentRangeMin = 1.20; # don't launch unless beyond this percent of Rmin
|
||||
##weapon + threat specific shooting parameters
|
||||
##specify an Rmax based on which weapon used and which threat engaged
|
||||
#Map<string, Map<string, double>> WeaponThreatRmaxMap = Map<string, Map<string, double>>();
|
||||
#WeaponThreatRmaxMap["base_weapon"] = Map<string, double>();
|
||||
#WeaponThreatRmaxMap["base_weapon"].Set("fighter", 0.80);
|
||||
|
||||
bool mDrawSeparationData = true;
|
||||
string mSeparateSide = "LEFT"; #this is dynamically updated based on engagement axis
|
||||
|
||||
#do not edit these:
|
||||
bool mSeparateEngagement = false;
|
||||
double mEngagementTrueBearing = -1.0; #valid if in [0,360], update based on engagement axis
|
||||
WsfDraw mDraw = WsfDraw();
|
||||
|
||||
Array<WsfLocalTrack> mTimeLineLocalTargets;
|
||||
WsfLocalTrack mNearestTimeLineTarget;
|
||||
|
||||
Array<WsfLocalTrack> mTimeLineLocalTargetsToShoot = Array<WsfLocalTrack>();
|
||||
|
||||
end_script_variables
|
||||
|
||||
on_update
|
||||
#update these if necessary
|
||||
mSeparateEngagement = false;
|
||||
mEngagementTrueBearing = -1.0;
|
||||
|
||||
#populate shared variables
|
||||
WsfTask timeline = GetTimeLineTask(PROCESSOR);
|
||||
mTimeLineLocalTargets = GetTimeLineTaskLocalTargets(PLATFORM, timeline);
|
||||
Array<double> times = Array<double>();
|
||||
foreach(WsfLocalTrack target in mTimeLineLocalTargets)
|
||||
{
|
||||
double time = TimeToReachPoint(target, PLATFORM.Location(), mGeesToTurnWith);
|
||||
times.PushBack(time);
|
||||
}
|
||||
SortLocalTracksByValue(mTimeLineLocalTargets, times);
|
||||
mNearestTimeLineTarget = GetNearestTimeLineTaskLocalTarget(PLATFORM, timeline);
|
||||
|
||||
|
||||
if (timeline.IsValid() &&
|
||||
timeline.AuxDataExists("point_a") &&
|
||||
timeline.AuxDataExists("point_b") )
|
||||
{
|
||||
WsfGeoPoint SeparateA = (WsfGeoPoint)timeline.AuxDataObject("point_a");
|
||||
WsfGeoPoint SeparateB = (WsfGeoPoint)timeline.AuxDataObject("point_b");
|
||||
if (SeparateA.IsValid() && SeparateB.IsValid())
|
||||
{
|
||||
WsfGeoPoint near;
|
||||
WsfGeoPoint far;
|
||||
if (PLATFORM.GroundRangeTo(SeparateA) < PLATFORM.GroundRangeTo(SeparateB))
|
||||
{
|
||||
near = SeparateA;
|
||||
far = SeparateB;
|
||||
}
|
||||
else
|
||||
{
|
||||
near = SeparateB;
|
||||
far = SeparateA;
|
||||
}
|
||||
mDraw.SetLayer("timeline_proc");
|
||||
mDraw.SetId("timeline_proc");
|
||||
mDraw.SetDuration(PROCESSOR.UpdateInterval());
|
||||
mDraw.SetLineSize(1);
|
||||
mDraw.SetLineStyle("solid");
|
||||
|
||||
Array<WsfTrack> targets = (Array<WsfTrack>)timeline.AuxDataObject("targets");
|
||||
#check if platform is on the correct side of separation line
|
||||
WsfGeoPoint clusterMeanLocation = (WsfGeoPoint)timeline.AuxDataObject("cluster_mean");
|
||||
double refAz = near.TrueBearingTo(far);
|
||||
|
||||
#save this engagement axis true bearing!
|
||||
mSeparateEngagement = true;
|
||||
mEngagementTrueBearing = MATH.NormalizeAngle0_360(refAz);
|
||||
|
||||
double ownAz = near.TrueBearingTo(PLATFORM.Location()) - refAz;
|
||||
double tgtAz = near.TrueBearingTo(clusterMeanLocation) - refAz;
|
||||
ownAz = MATH.NormalizeAngleMinus180_180(ownAz);
|
||||
tgtAz = MATH.NormalizeAngleMinus180_180(tgtAz);
|
||||
|
||||
#save off turn direction
|
||||
if (tgtAz < 0) {
|
||||
mSeparateSide = "LEFT";
|
||||
} else {
|
||||
mSeparateSide = "RIGHT";
|
||||
}
|
||||
|
||||
bool CorrectSide = ((ownAz * tgtAz) > 0);
|
||||
|
||||
#draw lines for separation divide
|
||||
mDraw.SetColor(0.0, 0.0, 0.0); #black
|
||||
mDraw.BeginLines();
|
||||
mDraw.Vertex(near);
|
||||
mDraw.Vertex(far);
|
||||
mDraw.End();
|
||||
|
||||
if (CorrectSide) {
|
||||
mDraw.SetColor(0.0, 1.0, 1.0); #aqua
|
||||
} else {
|
||||
mDraw.SetColor(1.0, 0.0, 0.0); #red
|
||||
}
|
||||
|
||||
#draw line from me to target center of gravity
|
||||
mDraw.BeginLines();
|
||||
mDraw.Vertex(PLATFORM);
|
||||
#mDraw.Vertex(targetCluster.MeanLocation());
|
||||
mDraw.Vertex(mTimeLineLocalTargets.Front().CurrentLocation());
|
||||
mDraw.End();
|
||||
|
||||
# mDraw.SetEllipseMode("line");
|
||||
# mDraw.BeginCircle(0.0, 4.0 * MATH.M_PER_NM());
|
||||
# mDraw.Vertex(PLATFORM);
|
||||
# mDraw.End();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#update the "mTimeLineLocalTargetsToShoot" list
|
||||
mTimeLineLocalTargetsToShoot.Clear();
|
||||
if (timeline.IsValid())
|
||||
{
|
||||
|
||||
string RISK = timeline.AuxDataString("RISK");
|
||||
|
||||
foreach(WsfLocalTrack target in mTimeLineLocalTargets)
|
||||
{
|
||||
writeln_d("T=",TIME_NOW," ", PLATFORM.Name(), " checking if shootable: ", target.TargetName());
|
||||
|
||||
if (FiredOn(PLATFORM, target, mConsiderPeersWeapons))
|
||||
{
|
||||
writeln_d(" NO - already fired on!");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mRequiredToSupportWeapons == true && RISK != "HIGH") #can support any shot when risk is high (won't be dragging)
|
||||
{
|
||||
double gate = timeline.AuxDataDouble("DOR"); #range (meters)
|
||||
if (RISK == "MEDIUM")
|
||||
{
|
||||
gate = timeline.AuxDataDouble("MAR"); #range (meters)
|
||||
}
|
||||
|
||||
#calculate if we have time or range to support a shot to this target:
|
||||
|
||||
|
||||
|
||||
###################################################################
|
||||
###################################################################
|
||||
###################################################################
|
||||
|
||||
|
||||
#figure out weapon range & time of flight
|
||||
double weaponMaxRange = 0.0;
|
||||
WsfWeapon maxRangeWeapon;
|
||||
for (int i=0; i < PLATFORM.WeaponCount(); i+=1)
|
||||
{
|
||||
WsfWeapon weapon = PLATFORM.WeaponEntry(i);
|
||||
if (WeaponCapableAvailableAgainstThreat(weapon, target))
|
||||
{
|
||||
double weaponRange = MaxRange(PLATFORM,weapon,target);
|
||||
if (weaponRange > weaponMaxRange)
|
||||
{
|
||||
weaponMaxRange = weaponRange;
|
||||
maxRangeWeapon = weapon;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!maxRangeWeapon.IsValid())
|
||||
{
|
||||
writeln_d(" NO - do not have a weapon for the target");
|
||||
continue; #no weapon for this target
|
||||
}
|
||||
#estimate time before we can shoot
|
||||
#hypothetical shot
|
||||
#estimate time cranking (based on weapon travel time)
|
||||
#does this leave us enough time to turn to drag?
|
||||
double weaponTOF = TimeToHit(PLATFORM, maxRangeWeapon, target);
|
||||
|
||||
if (weaponTOF <= 0)
|
||||
{
|
||||
writeln_d(" NO - could not find TOF data for weapon");
|
||||
#good weapon data no found
|
||||
continue;
|
||||
}
|
||||
|
||||
double firingRange = DefaultPercentRangeMax * weaponMaxRange;
|
||||
|
||||
writeln_d(" INFO - weapon \"", maxRangeWeapon.Name(), "\" firing range = ", firingRange, " TOF = ", weaponTOF);
|
||||
|
||||
double targetRange = PLATFORM.GroundRangeTo(target.CurrentLocation());
|
||||
double rangeToGo = targetRange - firingRange;
|
||||
double timeTilShot = 0.0;
|
||||
|
||||
double range = PLATFORM.GroundRangeTo(mNearestTimeLineTarget.CurrentLocation());
|
||||
|
||||
if (rangeToGo > 0)
|
||||
{
|
||||
#assume target continues on its current heading
|
||||
#assume nearest threat closes towards you
|
||||
#assume we close towards target
|
||||
double myRelativeBearingToThreat = MATH.Fabs(MATH.NormalizeAngleMinus180_180(PLATFORM.TrueBearingTo(mNearestTimeLineTarget) - PLATFORM.TrueBearingTo(target)));
|
||||
double targetRelativeBearingToMe = target.RelativeBearingTo(PLATFORM);
|
||||
double closingSpeedToTarget = PLATFORM.Speed() + MATH.Cos(targetRelativeBearingToMe) * target.Speed();
|
||||
double closingSpeedOfThreat = mNearestTimeLineTarget.Speed() + MATH.Cos(myRelativeBearingToThreat) * PLATFORM.Speed();
|
||||
|
||||
if (closingSpeedToTarget <= 0)
|
||||
{
|
||||
writeln_d(" NO - negative closing speed to target with range remaining to shoot");
|
||||
continue;
|
||||
}
|
||||
|
||||
writeln_d(" INFO - closing to target = ", closingSpeedToTarget, " closing of threat = ", closingSpeedOfThreat);
|
||||
|
||||
#subtract off closing distance to nearest threat during the time before shot
|
||||
timeTilShot = rangeToGo / closingSpeedToTarget;
|
||||
|
||||
double temp = range;
|
||||
range -= (timeTilShot * closingSpeedOfThreat);
|
||||
|
||||
writeln_d(" INFO - starting threat range = ", temp, " range after closing to shot loc = ", range);
|
||||
}
|
||||
|
||||
#subtract off closing distance to nearest target during the time of weapon's flight
|
||||
double closingSpeedOfThreat = (mDelaySpeed * MATH.Cos(mOffsetAngle)) + mNearestTimeLineTarget.Speed();
|
||||
double temp = range;
|
||||
range -= (weaponTOF * closingSpeedOfThreat);
|
||||
|
||||
writeln_d(" INFO - range at shot = ", temp, " range after supporting shot = ", range);
|
||||
|
||||
# #calculate time to turn to drag (after cranking)
|
||||
# Array<double> vals = Array<double>();
|
||||
# double turnAngle = 180 - mOffsetAngle;
|
||||
# TurnAngleTimeAndDistance(turnAngle, mEngageSpeed, mGeesToTurnWith, vals);
|
||||
# double turnTime = vals[0];
|
||||
# double distSep = vals[1];
|
||||
# #subtract off closing distance to nearest target during the time of turning to drag
|
||||
# range += distSep;
|
||||
# range -= (turnTime*mNearestTimeLineTarget.Speed());
|
||||
|
||||
if (range >= gate)
|
||||
{
|
||||
writeln_d(" YES - have time & range to support the shot.");
|
||||
|
||||
mTimeLineLocalTargetsToShoot.PushBack(target);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeln_d(" NO - do not have time & range to support the shot.");
|
||||
##TODO - should we break here?
|
||||
## its highly unlikely futher away targets will be shootable
|
||||
#break;
|
||||
}
|
||||
|
||||
|
||||
###################################################################
|
||||
###################################################################
|
||||
###################################################################
|
||||
|
||||
|
||||
}
|
||||
else if (mRequiredToSupportWeapons == false || RISK == "HIGH")
|
||||
{
|
||||
writeln_d(" YES - risk is high or not required to support.");
|
||||
|
||||
#all targets are for shooting (if not already shot on)
|
||||
mTimeLineLocalTargetsToShoot.PushBack(target);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
end_on_update
|
||||
|
||||
behavior_tree
|
||||
selector
|
||||
behavior_node evade #
|
||||
behavior_node go_home #
|
||||
behavior_node timeline_reposition #
|
||||
#behavior_node timeline_support #TODO - integrate kyle's grinder behavior support stuff
|
||||
behavior_node timeline_delay #TODO - integrate nick's delay script
|
||||
behavior_node timeline_engage #TODO - only shoot if there is time to support it
|
||||
behavior_node weapon_support #
|
||||
behavior_node planned_route #
|
||||
end_selector
|
||||
end_behavior_tree
|
||||
end_processor
|
||||
696
processors/timeline_agents/timeline_proc_fsm.txt
Normal file
696
processors/timeline_agents/timeline_proc_fsm.txt
Normal file
@@ -0,0 +1,696 @@
|
||||
# ****************************************************************************
|
||||
# CUI
|
||||
#
|
||||
# The Advanced Framework for Simulation, Integration, and Modeling (AFSIM)
|
||||
#
|
||||
# The use, dissemination or disclosure of data in this file is subject to
|
||||
# limitation or restriction. See accompanying README and LICENSE for details.
|
||||
# ****************************************************************************
|
||||
|
||||
#include_once processors/quantum_agents/aiai/behavior_engage_weapon_task_target.txt
|
||||
include_once processors/quantum_agents/aiai/behavior_escort.txt
|
||||
include_once processors/quantum_agents/aiai/behavior_evade.txt
|
||||
include_once processors/quantum_agents/aiai/behavior_go_home.txt
|
||||
include_once processors/quantum_agents/aiai/behavior_planned_route.txt
|
||||
#include_once processors/quantum_agents/aiai/behavior_pursue-target_route_finder.txt
|
||||
include_once processors/quantum_agents/aiai/behavior_pursue_weapon_task_target.txt
|
||||
|
||||
include_once behavior_fly_timeline.txt
|
||||
include_once behavior_engage_on_timeline.txt
|
||||
include_once air_combat_maneuvers.txt
|
||||
include_once common_timeline_scripts.txt
|
||||
|
||||
processor TIMELINE_PROC_FSM WSF_QUANTUM_TASKER_PROCESSOR
|
||||
#show_task_messages
|
||||
script_debug_writes off
|
||||
update_interval 5 sec
|
||||
|
||||
script_variables
|
||||
WsfDraw draw = WsfDraw();
|
||||
int delayForSupport = 0;
|
||||
double currentManeuverHeading = 0.0;
|
||||
bool delayManeuverActive = false;
|
||||
double mGeesToTurnWith = 6.0; #TODO - what to use?
|
||||
|
||||
#debug stuff:
|
||||
WsfDraw mDraw = WsfDraw();
|
||||
end_script_variables
|
||||
|
||||
|
||||
//return values: 0 = don't delay
|
||||
// 1 = perform a crank
|
||||
// 2 = perform a beam
|
||||
// 3 = perform a drag
|
||||
// 4 = perform a posthole
|
||||
script int EvaluateDelay()
|
||||
double myPos = GetTargetDistance(PLATFORM, PROCESSOR);
|
||||
WsfTrack myTrack = GetTimeLineTaskTargetTrack(PLATFORM, GetTimeLineTask(PROCESSOR));
|
||||
|
||||
//figure out if this platform is the closest to the target track
|
||||
if (myTrack)
|
||||
{
|
||||
bool amLead = true;
|
||||
Map<WsfPlatform, double> peerDistMap = Map<WsfPlatform, double>();
|
||||
WsfPlatformList peerList = PLATFORM.Peers();
|
||||
if (peerList.Count() == 0)
|
||||
{
|
||||
//find out if you can delay until the DOR, if so do that
|
||||
|
||||
double crankTime = TimeToPerformCrank(PLATFORM, myTrack);
|
||||
double beamTime = TimeToPerformBeam(PLATFORM, myTrack);
|
||||
double dragTime = TimeToPerformDrag(PLATFORM, myTrack);
|
||||
double postHoleTime = 0.0;
|
||||
|
||||
crankTime = (crankTime * 2) + TIME_NOW; //multiply by 2 to reflect going back to starting position
|
||||
beamTime = (beamTime * 2) + TIME_NOW; //multiply by 2 to reflect going back to starting position
|
||||
dragTime = (dragTime * 2) + TIME_NOW; //multiply by 2 to reflect going back to starting position
|
||||
postHoleTime = dragTime; //a 180 turn followed by a 180 turn is basically a posthole
|
||||
|
||||
Array<double> maneuverTimes = Array<double>();
|
||||
maneuverTimes.PushBack(crankTime);
|
||||
maneuverTimes.PushBack(beamTime);
|
||||
maneuverTimes.PushBack(dragTime);
|
||||
maneuverTimes.PushBack(postHoleTime);
|
||||
|
||||
double DORDist = GetTimeLineValueForKey(PROCESSOR, "DOR");
|
||||
double FSRDist = GetTimeLineValueForKey(PROCESSOR, "FSR");
|
||||
|
||||
double furthestGate;
|
||||
double closestGate;
|
||||
|
||||
if (DORDist > FSRDist)
|
||||
{
|
||||
furthestGate = FSRDist;
|
||||
closestGate = DORDist; //the terminology is tricky here, closest means closest in distance to the PLATFORM but furthest from the target
|
||||
}
|
||||
else
|
||||
{
|
||||
furthestGate = DORDist;
|
||||
closestGate = FSRDist;
|
||||
}
|
||||
int curSafeIdx = 0;
|
||||
int curIdx = 1;
|
||||
foreach(double futureTime in maneuverTimes)
|
||||
{
|
||||
WsfGeoPoint myLoc = PLATFORM.FutureLocation(futureTime);
|
||||
WsfGeoPoint targetLoc = myTrack.Target().FutureLocation(futureTime);
|
||||
|
||||
if (myLoc.IsValid() && targetLoc.IsValid())
|
||||
{
|
||||
double futureDist = myLoc.SlantRangeTo(targetLoc);
|
||||
|
||||
if (futureDist > furthestGate && futureDist < closestGate) //we're inside the DOR
|
||||
{
|
||||
curSafeIdx = curIdx;
|
||||
}
|
||||
}
|
||||
curIdx = curIdx + 1;
|
||||
}
|
||||
if (curSafeIdx == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (curSafeIdx == 3) //don't allow drag maneuver right now
|
||||
{
|
||||
curSafeIdx = 2;
|
||||
}
|
||||
return curSafeIdx;
|
||||
}
|
||||
}
|
||||
foreach (WsfPlatform curPlat in peerList)
|
||||
{
|
||||
double dist = curPlat.SlantRangeTo(myTrack);
|
||||
peerDistMap[curPlat] = dist;
|
||||
if (dist < myPos)
|
||||
{
|
||||
amLead = false;
|
||||
}
|
||||
}
|
||||
if (amLead)
|
||||
{
|
||||
//check to see if the closest follower is at least in FSR (or some other pre-determined distance on the timeline)
|
||||
double FSRDist = GetTimeLineValueForKey(PROCESSOR, "FSR");
|
||||
bool foundInFSR = false;
|
||||
//loop through the map of peers and if all of the peers are outside of the FSRDist, we need to delay
|
||||
foreach (WsfPlatform peer in peerList)
|
||||
{
|
||||
double peerDist = peerDistMap[peer];
|
||||
if (peerDist <= FSRDist)
|
||||
{
|
||||
foundInFSR = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (foundInFSR)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
//no peer close enough, evaluate which delay tactic to do
|
||||
double crankTime = TimeToPerformCrank(PLATFORM, myTrack);
|
||||
double beamTime = TimeToPerformBeam(PLATFORM, myTrack);
|
||||
double dragTime = TimeToPerformDrag(PLATFORM, myTrack);
|
||||
double postHoleTime = 0.0;
|
||||
|
||||
crankTime = (crankTime * 2) + TIME_NOW; //multiply by 2 to reflect going back to starting position
|
||||
beamTime = (beamTime * 2) + TIME_NOW; //multiply by 2 to reflect going back to starting position
|
||||
dragTime = (dragTime * 2) + TIME_NOW; //multiply by 2 to reflect going back to starting position
|
||||
postHoleTime = dragTime;
|
||||
|
||||
//figure out how long it will take for the peer to get into FSRDist
|
||||
//take into account the heading and speed of the track as well as the heading and speed of the peer
|
||||
//evaluate each platform in 1 second intervals until a peer is in the FSR
|
||||
bool peerInFSR = false;
|
||||
double peerTimeToFSR = MATH.DOUBLE_MAX();
|
||||
double futureTime = TIME_NOW + 1;
|
||||
if (peerList.Count())
|
||||
{
|
||||
while (!peerInFSR)
|
||||
{
|
||||
foreach (WsfPlatform peer in peerList)
|
||||
{
|
||||
WsfGeoPoint peerLoc = peer.FutureLocation(futureTime);
|
||||
WsfGeoPoint targetLoc = myTrack.Target().FutureLocation(futureTime);
|
||||
if (peerLoc.IsValid() && targetLoc.IsValid())
|
||||
{
|
||||
double futureDist = peerLoc.SlantRangeTo(targetLoc);
|
||||
if (futureDist <= FSRDist)
|
||||
{
|
||||
peerTimeToFSR = futureTime - TIME_NOW;
|
||||
peerInFSR = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
futureTime = futureTime + 1;
|
||||
|
||||
//if futuretime is beyond the length of this sim, stop
|
||||
if (futureTime > WsfSimulation.EndTime())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//peer never reached FSR, return 0?
|
||||
if (!peerInFSR)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (peerTimeToFSR >= postHoleTime)
|
||||
{
|
||||
return 4; //perform a posthole
|
||||
}
|
||||
else if (peerTimeToFSR < postHoleTime && peerTimeToFSR >= dragTime)
|
||||
{
|
||||
return 3; //perform a drag
|
||||
}
|
||||
else if (peerTimeToFSR < dragTime && peerTimeToFSR >= beamTime)
|
||||
{
|
||||
return 2; //perform a beam
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1; //perform a crank
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
end_script
|
||||
|
||||
script bool ShouldApproach()
|
||||
#do we have a timeline task?
|
||||
return GetTimelineTask(PROCESSOR).IsValid();
|
||||
end_script
|
||||
|
||||
script bool ShouldDelay()
|
||||
#compare risk level with range to nearest target
|
||||
#only delay if risk MED: past DOR and (want to shoot but out of range or waiting on existing shot)
|
||||
# risk HIGH: past MAR and (want to shoot but out of range or waiting on existing shot)
|
||||
WsfTask timeline = GetTimelineTask(PROCESSOR);
|
||||
if (!timeline.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
WsfTrack target = GetNearestTimelineTarget(PLATFORM, PROCESSOR);
|
||||
if (!target.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
string RISK = timeline.AuxDataString("RISK");
|
||||
if (RISK == "HIGH")
|
||||
{
|
||||
double MAR = timeline.AuxDataDouble("MAR"); #range (meters)
|
||||
double slantRange = PLATFORM.SlantRangeTo(target);
|
||||
return (slantRange <= MAR);
|
||||
}
|
||||
else if (RISK == "MEDIUM")
|
||||
{
|
||||
double DOR = timeline.AuxDataDouble("DOR"); #range (meters)
|
||||
double slantRange = PLATFORM.SlantRangeTo(target);
|
||||
return (slantRange <= DOR);
|
||||
}
|
||||
else #if (RISK == "LOW")
|
||||
{
|
||||
//check to see if there is enough time to delay before it hits the DOR
|
||||
delayForSupport = EvaluateDelay();
|
||||
double DOR = timeline.AuxDataDouble("DOR");
|
||||
if (delayForSupport > 0 && PLATFORM.SlantRangeTo(target) <= DOR)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
end_script
|
||||
|
||||
script bool ShouldShoot()
|
||||
#TODO - more work required here
|
||||
#look at time required to turn, speed up, & pitch up for shot
|
||||
#compare that to time until shot range will be reached
|
||||
#use optimal shot range somehow?
|
||||
#base this on current weapons &/or best weapon for the threat?
|
||||
#cover all timeline targets - do any not have weapons on them?
|
||||
|
||||
WsfTask timeline = GetTimelineTask(PROCESSOR);
|
||||
if (!timeline.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
WsfTrack target = GetNearestTimelineTarget(PLATFORM, PROCESSOR);
|
||||
if (!target.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
double FSR = timeline.AuxDataDouble("FSR");
|
||||
|
||||
return (PLATFORM.SlantRangeTo(target) <= FSR);
|
||||
end_script
|
||||
|
||||
script bool ShouldReset()
|
||||
//NAC+ DEBUG This still needs some work
|
||||
return false;
|
||||
//NAC-
|
||||
#look at time required to turn & leave & compare to time until next gate
|
||||
#gate determined by risk level (LOW risk gate = DOR, MEDIUM risk gate = MAR)
|
||||
WsfTask timeline = GetTimelineTask(PROCESSOR);
|
||||
if (!timeline.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
WsfTrack target = GetNearestTimelineTarget(PLATFORM, PROCESSOR);
|
||||
if (!target.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
string RISK = timeline.AuxDataString("RISK");
|
||||
if (RISK == "HIGH")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (RISK == "MEDIUM")
|
||||
{
|
||||
#calculate if its necessary to turn before the MAR now
|
||||
double turnTime = TimeToTurnFrom(PLATFORM, PLATFORM.Speed(), target, mGeesToTurnWith);
|
||||
double MAR = timeline.AuxDataDouble("MAR"); #range (meters)
|
||||
double closingSpeed = PLATFORM.ClosingSpeedOf(target);
|
||||
double slantRange = PLATFORM.SlantRangeTo(target);
|
||||
double timeUntilMAR = (slantRange-MAR)/closingSpeed;
|
||||
return (turnTime >= timeUntilMAR);
|
||||
}
|
||||
else #if (RISK == "LOW")
|
||||
{
|
||||
#calculate if its necessary to turn before the DOR now
|
||||
double turnTime = TimeToTurnFrom(PLATFORM, PLATFORM.Speed(), target, mGeesToTurnWith);
|
||||
double DOR = timeline.AuxDataDouble("DOR"); #range (meters)
|
||||
double closingSpeed = PLATFORM.ClosingSpeedOf(target);
|
||||
double slantRange = PLATFORM.SlantRangeTo(target);
|
||||
double timeUntilDOR = (slantRange-DOR)/closingSpeed;
|
||||
return (turnTime >= timeUntilDOR);
|
||||
}
|
||||
return false;
|
||||
end_script
|
||||
|
||||
script bool ShouldGoHome()
|
||||
#check weapons, fuel, force ratios, etc?
|
||||
return false;
|
||||
end_script
|
||||
|
||||
script bool ShouldSupport()
|
||||
#are there weapons to support & am I allowed to emit to support them
|
||||
return false;
|
||||
end_script
|
||||
|
||||
|
||||
script void DrawTimeLine()
|
||||
WsfTrack target = GetNearestTimeLineTarget(PLATFORM, PROCESSOR);
|
||||
|
||||
if (target.IsValid())
|
||||
{
|
||||
double FSRDist = GetTimeLineValueForKey(PROCESSOR, "FSR");
|
||||
double DORDist = GetTimeLineValueForKey(PROCESSOR, "DOR");
|
||||
double MARDist = GetTimeLineValueForKey(PROCESSOR, "MAR");
|
||||
|
||||
|
||||
WsfGeoPoint platLoc = PLATFORM.Location();
|
||||
WsfGeoPoint tgtLocFSR = target.Target().Location();
|
||||
WsfGeoPoint tgtLocDOR = target.Target().Location();
|
||||
WsfGeoPoint tgtLocMAR = target.Target().Location();
|
||||
|
||||
WsfTrack PlatformAsTrack = WsfTrack();
|
||||
PlatformAsTrack.SetTarget(PLATFORM.Name());
|
||||
PlatformAsTrack.SetLocation(platLoc);
|
||||
|
||||
//double headingFromTargetToPlatform = target.Target().RelativeHeadingOf(PlatformAsTrack);
|
||||
double headingFromTargetToPlatform = target.Target().Location().TrueBearingTo(PLATFORM.Location());
|
||||
//headingFromTargetToPlatform = MATH.Fabs(headingFromTargetToPlatform) + target.Target().Heading();
|
||||
|
||||
|
||||
|
||||
|
||||
//extrapolate the tgtLoc point a distance of FSRDist along a heading in the direction of the PLATFORM
|
||||
tgtLocFSR.Extrapolate(headingFromTargetToPlatform, FSRDist);
|
||||
tgtLocDOR.Extrapolate(headingFromTargetToPlatform, DORDist);
|
||||
tgtLocMAR.Extrapolate(headingFromTargetToPlatform, MARDist);
|
||||
|
||||
mDraw.SetDuration(5.0);
|
||||
mDraw.SetColor(1.0, 0.557, 0.0314);
|
||||
mDraw.SetLineSize(3);
|
||||
mDraw.BeginLines();
|
||||
mDraw.Vertex(PLATFORM);
|
||||
mDraw.Vertex(target.Target());
|
||||
mDraw.End();
|
||||
|
||||
//mDraw.SetPointSize(9);
|
||||
//mDraw.BeginPoints();
|
||||
//mDraw.SetColor(1.0, 0.0, 1.0);
|
||||
//mDraw.Vertex(tgtLocFSR);
|
||||
//mDraw.SetColor(0.0, 1.0, 0.0);
|
||||
//mDraw.Vertex(tgtLocDOR);
|
||||
//mDraw.SetColor(1.0, 1.0, 1.0);
|
||||
//mDraw.Vertex(tgtLocMAR);
|
||||
//mDraw.End();
|
||||
|
||||
|
||||
WsfGeoPoint FSROne = WsfGeoPoint(tgtLocFSR);
|
||||
FSROne.Extrapolate(0.0, 10000.0);
|
||||
WsfGeoPoint FSRTwo = WsfGeoPoint(tgtLocFSR);
|
||||
FSRTwo.Extrapolate(180.0, 10000.0);
|
||||
|
||||
|
||||
WsfGeoPoint DOROne = WsfGeoPoint(tgtLocDOR);
|
||||
DOROne.Extrapolate(0.0, 10000.0);
|
||||
WsfGeoPoint DORTwo = WsfGeoPoint(tgtLocDOR);
|
||||
DORTwo.Extrapolate(180.0, 10000.0);
|
||||
|
||||
WsfGeoPoint MAROne = WsfGeoPoint(tgtLocMAR);
|
||||
MAROne.Extrapolate(0.0, 10000.0);
|
||||
WsfGeoPoint MARTwo = WsfGeoPoint(tgtLocMAR);
|
||||
MARTwo.Extrapolate(180.0, 10000.0);
|
||||
|
||||
mDraw.SetLineSize(5);
|
||||
mDraw.SetColor(0.0, 1.0, 0.0);
|
||||
mDraw.BeginLines();
|
||||
mDraw.Vertex(FSROne);
|
||||
mDraw.Vertex(FSRTwo);
|
||||
mDraw.End();
|
||||
|
||||
mDraw.SetColor(1.0, 1.0, 0.0);
|
||||
mDraw.BeginLines();
|
||||
mDraw.Vertex(DOROne);
|
||||
mDraw.Vertex(DORTwo);
|
||||
mDraw.End();
|
||||
|
||||
mDraw.SetColor(1.0, 0.0, 0.0);
|
||||
mDraw.BeginLines();
|
||||
mDraw.Vertex(MAROne);
|
||||
mDraw.Vertex(MARTwo);
|
||||
mDraw.End();
|
||||
|
||||
//mDraw.SetTextSize(25);
|
||||
//mDraw.SetColor(1.0, 1.0, 1.0);
|
||||
//mDraw.SetLineStyle("solid");
|
||||
//string fsrTxt = "FSR";
|
||||
//mDraw.BeginText(fsrTxt);
|
||||
// mDraw.Vertex(PLATFORM.Location());
|
||||
//mDraw.End();
|
||||
|
||||
}
|
||||
|
||||
end_script
|
||||
|
||||
script bool ShouldSupportWeapon()
|
||||
#this (current state) contains script to perform in the state
|
||||
//if a shot has been fired, support it?
|
||||
WsfTrack target = GetTimeLineTaskTargetTrack(PLATFORM, GetTimeLineTask(PROCESSOR));
|
||||
if (target.IsValid())
|
||||
{
|
||||
if (PLATFORM.WeaponsActiveFor(target.TrackId()) > 0)
|
||||
{
|
||||
Crank(PLATFORM, target, 35, PLATFORM.Speed());
|
||||
return true;
|
||||
}
|
||||
else if (PLATFORM.WeaponsPendingFor(target.TrackId()) > 0)
|
||||
{
|
||||
Crank(PLATFORM, target, 35, PLATFORM.Speed());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
end_script
|
||||
|
||||
#TODO - how to do things like evade behavior from each state?
|
||||
# have an EVADE state? maybe?
|
||||
|
||||
on_update
|
||||
//DrawTimeLine();
|
||||
end_on_update
|
||||
|
||||
###########################################################
|
||||
## ##
|
||||
## BEGIN STATE MACHINE ##
|
||||
## ##
|
||||
###########################################################
|
||||
state COMMIT
|
||||
#picture building, getting target, tracking target
|
||||
on_entry
|
||||
PLATFORM.Comment("COMMIT");
|
||||
end_on_entry
|
||||
behavior_tree
|
||||
selector
|
||||
behavior_node evade #TODO - move to evade state???
|
||||
behavior_node planned_route #TODO - move to this state!
|
||||
end_selector
|
||||
end_behavior_tree
|
||||
next_state COMMIT
|
||||
#this (current state) contains script to perform in the state
|
||||
#fly planned route or formation
|
||||
return false;
|
||||
end_next_state
|
||||
|
||||
#best order for checking these?
|
||||
#current opinion, check: DELAY before APPROACH
|
||||
# RESET before APPROACH
|
||||
# SHOOT before DELAY
|
||||
# RESET before SHOOT?????
|
||||
next_state RESET return ShouldReset(); end_next_state
|
||||
next_state SHOOT return ShouldShoot(); end_next_state
|
||||
next_state DELAY return ShouldDelay(); end_next_state
|
||||
next_state APPROACH return ShouldApproach(); end_next_state
|
||||
end_state
|
||||
|
||||
|
||||
state APPROACH
|
||||
#main state to start in once we have a timeline task (which should contain targets)
|
||||
#bulk of this state is intercepting nearest timeline target that isn't currently fired upon
|
||||
#basic times to approach:
|
||||
# if risk low: only when outside of DOR
|
||||
# if risk med: only when outside of MAR -> chance to delay when out of weapons range or waiting on existing shot
|
||||
# if risk high: only all the time -> chance to delay when out of weapons range or waiting on existing shot
|
||||
#NOTE: switch to SHOOT when in weapons range
|
||||
on_entry
|
||||
PLATFORM.Comment("APPROACH");
|
||||
end_on_entry
|
||||
behavior_tree
|
||||
selector
|
||||
behavior_node evade
|
||||
behavior_node fly_timeline #TODO - move this script to inside this state?
|
||||
end_selector
|
||||
end_behavior_tree
|
||||
next_state APPROACH
|
||||
#this (current state) contains script to perform in the state
|
||||
return false;
|
||||
end_next_state
|
||||
next_state RESET return ShouldReset(); end_next_state
|
||||
next_state DELAY return ShouldDelay(); end_next_state
|
||||
next_state SHOOT return ShouldShoot(); end_next_state
|
||||
end_state
|
||||
|
||||
|
||||
state SHOOT
|
||||
#once entered, will setup & take a shot
|
||||
#balance this by limiting the shoot behavior:
|
||||
# add in conditions (speed+orientation) to it's precondition
|
||||
on_entry
|
||||
PLATFORM.Comment("SHOOT");
|
||||
end_on_entry
|
||||
behavior_tree
|
||||
selector
|
||||
behavior_node evade
|
||||
behavior_node fly_timeline
|
||||
behavior_node planned_route
|
||||
end_selector
|
||||
behavior_node engage_on_timeline #TODO - move this script to inside this state?
|
||||
end_behavior_tree
|
||||
next_state SHOOT
|
||||
|
||||
return false;
|
||||
end_next_state
|
||||
next_state SUPPORT_WEAPON return ShouldSupportWeapon(); end_next_state
|
||||
next_state RESET return ShouldReset(); end_next_state
|
||||
next_state DELAY return ShouldDelay(); end_next_state
|
||||
next_state APPROACH return ShouldApproach(); end_next_state
|
||||
end_state
|
||||
|
||||
state SUPPORT_WEAPON
|
||||
next_state SUPPORT_WEAPON
|
||||
return ShouldSupportWeapon();
|
||||
end_next_state
|
||||
//next_state SHOOT return ShouldShoot(); end_next_state
|
||||
//next_state DELAY return ShouldDelay(); end_next_state
|
||||
next_state APPROACH return ShouldApproach(); end_next_state
|
||||
end_state
|
||||
|
||||
state DELAY
|
||||
on_entry
|
||||
PLATFORM.Comment("DELAY");
|
||||
end_on_entry
|
||||
#only delay if risk MED: past DOR and (want to shoot but out of range or waiting on existing shot)
|
||||
# risk HIGH: past MAR and (want to shoot but out of range or waiting on existing shot)
|
||||
next_state DELAY
|
||||
if (delayForSupport != 0 && delayManeuverActive)
|
||||
{
|
||||
//check current heading vs goal heading
|
||||
double curHeading = PLATFORM.Heading();
|
||||
if (curHeading < 0)
|
||||
{
|
||||
curHeading = 360 + curHeading;
|
||||
}
|
||||
if (MATH.Fabs(curHeading - currentManeuverHeading) <= 1) //give a 1 degree margin of error
|
||||
{
|
||||
delayManeuverActive = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#this (current state) contains script to perform in the state
|
||||
if (delayForSupport == 1 && !delayManeuverActive)
|
||||
{
|
||||
PLATFORM.Comment("DELAY CRANK");
|
||||
currentManeuverHeading = Crank(PLATFORM, GetNearestTimeLineTarget(PLATFORM, PROCESSOR), 35, PLATFORM.Speed());
|
||||
delayManeuverActive = true;
|
||||
return true;
|
||||
}
|
||||
else if (delayForSupport == 2 && !delayManeuverActive)
|
||||
{
|
||||
PLATFORM.Comment("DELAY BEAM");
|
||||
currentManeuverHeading = Beam(PLATFORM, GetNearestTimeLineTarget(PLATFORM, PROCESSOR));
|
||||
delayManeuverActive = true;
|
||||
return true;
|
||||
}
|
||||
else if (delayForSupport == 3 && !delayManeuverActive)
|
||||
{
|
||||
PLATFORM.Comment("DELAY DRAG");
|
||||
currentManeuverHeading = Drag(PLATFORM, GetNearestTimeLineTarget(PLATFORM, PROCESSOR), PLATFORM.Speed(), PLATFORM.Altitude(), 6.0);
|
||||
delayManeuverActive = true;
|
||||
return true;
|
||||
}
|
||||
else if (delayForSupport == 4 && !delayManeuverActive)
|
||||
{
|
||||
PLATFORM.Comment("DELAY POSTHOLE");
|
||||
currentManeuverHeading = PostHole(PLATFORM, "right");
|
||||
delayManeuverActive = true;
|
||||
return true;
|
||||
}
|
||||
else #(delayForSupport == 0)
|
||||
{
|
||||
if (delayManeuverActive)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
PLATFORM.Comment("NO DELAY");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
end_next_state
|
||||
next_state RESET return ShouldReset(); end_next_state
|
||||
next_state SHOOT return ShouldShoot(); end_next_state
|
||||
next_state APPROACH return ShouldApproach(); end_next_state
|
||||
end_state
|
||||
|
||||
|
||||
state SUPPORT
|
||||
#this is basically a CRANK state for supporting active weapons while risk level allows it
|
||||
#is this state needed?
|
||||
on_entry
|
||||
PLATFORM.Comment("SUPPORT");
|
||||
end_on_entry
|
||||
next_state SUPPORT
|
||||
#this (current state) contains script to perform in the state
|
||||
Crank(PLATFORM, GetNearestTimelineTarget(PLATFORM,PROCESSOR));
|
||||
return false;
|
||||
end_next_state
|
||||
next_state RESET return ShouldReset(); end_next_state
|
||||
next_state SHOOT return ShouldShoot(); end_next_state
|
||||
next_state DELAY return ShouldDelay(); end_next_state
|
||||
next_state APPROACH return ShouldApproach(); end_next_state
|
||||
end_state
|
||||
|
||||
|
||||
state RESET
|
||||
#this is basically a DRAG state for getting back & ready for future shots
|
||||
on_entry
|
||||
PLATFORM.Comment("RESET");
|
||||
end_on_entry
|
||||
|
||||
next_state RESET
|
||||
#this (current state) contains script to perform in the state
|
||||
Drag(PLATFORM, GetNearestTimelineTarget(PLATFORM,PROCESSOR), PLATFORM.Speed(), 6.0);
|
||||
return false;
|
||||
end_next_state
|
||||
next_state SHOOT return ShouldShoot(); end_next_state
|
||||
next_state DELAY return ShouldDelay(); end_next_state
|
||||
next_state APPROACH return ShouldApproach(); end_next_state
|
||||
end_state
|
||||
|
||||
|
||||
state HOME
|
||||
#done, go home
|
||||
on_entry
|
||||
PLATFORM.Comment("HOME");
|
||||
end_on_entry
|
||||
next_state HOME
|
||||
#no breaking out of this state? meh...
|
||||
WsfWaypoint waypt = PLATFORM.Mover().Route().Front();
|
||||
PLATFORM.GoToLocation(waypt);
|
||||
return true;
|
||||
end_next_state
|
||||
end_state
|
||||
|
||||
end_processor
|
||||
388
processors/timeline_agents/weapon_defs.txt
Normal file
388
processors/timeline_agents/weapon_defs.txt
Normal file
@@ -0,0 +1,388 @@
|
||||
# ****************************************************************************
|
||||
# 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.
|
||||
# ****************************************************************************
|
||||
|
||||
script_struct WeaponData
|
||||
script_variables
|
||||
string type = "";
|
||||
double rangeMin = 0; #meters
|
||||
double rangeMax = 0; #meters
|
||||
bool onlyUseInRange = true;
|
||||
double averageSpeed = 0; #meters/second
|
||||
double maxTimeFlight = 0; #seconds, note: should == rangeMax / averageSpeed
|
||||
int numActiveMax = 0;
|
||||
bool domainAir = false;
|
||||
bool domainLand = false;
|
||||
double maxFiringAngle = 0;
|
||||
end_script_variables
|
||||
end_script_struct
|
||||
|
||||
script_variables
|
||||
Map<string, struct> gWeaponDefs = Map<string, struct>();
|
||||
|
||||
gWeaponDefs["MEDIUM_RANGE_MISSILE"] = struct.New("WeaponData");
|
||||
gWeaponDefs["MEDIUM_RANGE_MISSILE"]->type = "MEDIUM_RANGE_MISSILE";
|
||||
gWeaponDefs["MEDIUM_RANGE_MISSILE"]->rangeMin = 50; # (meters)
|
||||
gWeaponDefs["MEDIUM_RANGE_MISSILE"]->rangeMax = 111120; # ~60 nm (meters)
|
||||
gWeaponDefs["MEDIUM_RANGE_MISSILE"]->averageSpeed = 1657.283; #mach 5 (m/s)
|
||||
gWeaponDefs["MEDIUM_RANGE_MISSILE"]->maxTimeFlight = 67.05; #for 60 nm range (seconds)
|
||||
gWeaponDefs["MEDIUM_RANGE_MISSILE"]->numActiveMax = 2;
|
||||
gWeaponDefs["MEDIUM_RANGE_MISSILE"]->domainAir = true;
|
||||
gWeaponDefs["MEDIUM_RANGE_MISSILE"]->domainLand = false;
|
||||
gWeaponDefs["MEDIUM_RANGE_MISSILE"]->maxFiringAngle = 45.0;
|
||||
|
||||
gWeaponDefs["MEDIUM_RANGE_RADAR_MISSILE"] = struct.New("WeaponData");
|
||||
gWeaponDefs["MEDIUM_RANGE_RADAR_MISSILE"]->type = "MEDIUM_RANGE_RADAR_MISSILE";
|
||||
gWeaponDefs["MEDIUM_RANGE_RADAR_MISSILE"]->rangeMin = 50; # (meters)
|
||||
gWeaponDefs["MEDIUM_RANGE_RADAR_MISSILE"]->rangeMax = 111120; # ~60 nm (meters)
|
||||
gWeaponDefs["MEDIUM_RANGE_RADAR_MISSILE"]->averageSpeed = 1657.283; #mach 5 (m/s)
|
||||
gWeaponDefs["MEDIUM_RANGE_RADAR_MISSILE"]->maxTimeFlight = 67.05; #for 60 nm range (seconds)
|
||||
gWeaponDefs["MEDIUM_RANGE_RADAR_MISSILE"]->numActiveMax = 2;
|
||||
gWeaponDefs["MEDIUM_RANGE_RADAR_MISSILE"]->domainAir = true;
|
||||
gWeaponDefs["MEDIUM_RANGE_RADAR_MISSILE"]->domainLand = false;
|
||||
gWeaponDefs["MEDIUM_RANGE_RADAR_MISSILE"]->maxFiringAngle = 45.0;
|
||||
|
||||
end_script_variables
|
||||
|
||||
#returns a 'WeaponData' struct
|
||||
script struct GetWeaponData(string aType)
|
||||
if (gWeaponDefs.Exists(aType))
|
||||
{
|
||||
return gWeaponDefs.Get(aType);
|
||||
}
|
||||
else
|
||||
{
|
||||
return struct.New("WeaponData");
|
||||
}
|
||||
end_script
|
||||
|
||||
|
||||
include_once processors/quantum_agents/common/common_platform_script.txt
|
||||
|
||||
script_debug_writes off
|
||||
|
||||
script bool WeaponCapableAvailableAgainstThreat(WsfWeapon weapon, WsfTrack track)
|
||||
writeln_d(" checking weapon ", weapon.Name(), " valid=", weapon.IsValid());
|
||||
if (weapon.IsNull() || !weapon.IsValid() || track.IsNull() || !track.IsValid())
|
||||
{
|
||||
writeln_d("weapon or track is not valid!");
|
||||
return false;
|
||||
}
|
||||
if ((weapon.QuantityRemaining()-weapon.WeaponsPendingFor(WsfTrackId())) <= 0)
|
||||
{
|
||||
writeln_d("no unassigned weapons left to fire!");
|
||||
return false;
|
||||
}
|
||||
|
||||
#check manually input user data first
|
||||
struct weaponData = GetWeaponData(weapon.Type());
|
||||
if (weaponData->type == weapon.Type())
|
||||
{
|
||||
if ((track.AirDomain() && !weaponData->domainAir) ||
|
||||
(track.LandDomain() && !weaponData->domainLand) )
|
||||
{
|
||||
writeln_d("weapon not capable against target domain!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
writeln_d("could not find weapon type ", weapon.Type() ," in weapon database; query returned type ", weaponData->type);
|
||||
#check if it has a launch computer of the necessary type
|
||||
WsfLaunchComputer lcPtr = weapon.LaunchComputer();
|
||||
if (lcPtr.IsValid())
|
||||
{
|
||||
if (track.AirDomain() && lcPtr.IsA_TypeOf("WSF_AIR_TO_AIR_LAUNCH_COMPUTER"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (track.LandDomain() && lcPtr.IsA_TypeOf("WSF_ATG_LAUNCH_COMPUTER"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
writeln_d("nor could an applicable launch computer be found!");
|
||||
return false; #dont have weapon data
|
||||
}
|
||||
return true;
|
||||
end_script
|
||||
|
||||
|
||||
#could return -1 for an invalid max range
|
||||
#(from a launch computer or weapon struct not found)
|
||||
script double MaxRange(WsfPlatform shooter, WsfWeapon weapon, WsfTrack track)
|
||||
WsfLaunchComputer lcPtr = weapon.LaunchComputer();
|
||||
if (lcPtr.IsValid() &&
|
||||
lcPtr.IsA_TypeOf("WSF_AIR_TO_AIR_LAUNCH_COMPUTER"))
|
||||
{
|
||||
double rmax = lcPtr.LookupResult(track)[0];
|
||||
return rmax;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct weaponData = GetWeaponData(weapon.Type());
|
||||
if (weaponData->type == weapon.Type())
|
||||
{
|
||||
return weaponData->rangeMax;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1.0;
|
||||
}
|
||||
}
|
||||
end_script
|
||||
|
||||
#if beyond max range: returns time to hit if launched at max range
|
||||
#if before min range: returns time to hit if launched at min range
|
||||
#could return -1 for an invalid max range
|
||||
#(from a launch computer or weapon struct not found)
|
||||
script double TimeToHit(WsfPlatform shooter, WsfWeapon weapon, WsfTrack track)
|
||||
double range = shooter.SlantRangeTo(track);
|
||||
WsfLaunchComputer lcPtr = weapon.LaunchComputer();
|
||||
if (lcPtr.IsValid() &&
|
||||
lcPtr.IsA_TypeOf("WSF_AIR_TO_AIR_LAUNCH_COMPUTER"))
|
||||
{
|
||||
Array<double> arr = lcPtr.LookupResult(track);
|
||||
double rmax = arr[0];
|
||||
double rmaxTOF = arr[1];
|
||||
double rmin = arr[4];
|
||||
double rminTOF = arr[5];
|
||||
if (range >= rmax)
|
||||
{
|
||||
return rmaxTOF;
|
||||
}
|
||||
else if (range<=rmin)
|
||||
{
|
||||
return rminTOF;
|
||||
}
|
||||
else
|
||||
{
|
||||
# in between, interpolate
|
||||
double scale = (range-rmin)/(rmax-rmin);
|
||||
double tof = scale*(rmaxTOF-rminTOF) + rminTOF;
|
||||
return tof;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
struct weaponData = GetWeaponData(weapon.Type());
|
||||
if (weaponData->type == weapon.Type())
|
||||
{
|
||||
if (range > weaponData->rangeMax)
|
||||
{
|
||||
range = weaponData->rangeMax;
|
||||
}
|
||||
else if (range < weaponData->rangeMin)
|
||||
{
|
||||
range = weaponData->rangeMin;
|
||||
}
|
||||
double tof = range / weaponData->averageSpeed;
|
||||
return tof;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1.0;
|
||||
}
|
||||
}
|
||||
end_script
|
||||
|
||||
# Calculate time for the weapon to fly a given range
|
||||
#if beyond max range: returns time to hit if launched at max range
|
||||
#if before min range: returns time to hit if launched at min range
|
||||
#could return -1 for an invalid max range
|
||||
#(from a launch computer or weapon struct not found)
|
||||
script double TimeToRange(double range, WsfWeapon weapon, WsfTrack track)
|
||||
WsfLaunchComputer lcPtr = weapon.LaunchComputer();
|
||||
if (lcPtr.IsValid() &&
|
||||
lcPtr.IsA_TypeOf("WSF_AIR_TO_AIR_LAUNCH_COMPUTER"))
|
||||
{
|
||||
Array<double> arr = lcPtr.LookupResult(track);
|
||||
double rmax = arr[0];
|
||||
double rmaxTOF = arr[1];
|
||||
double rmin = arr[4];
|
||||
double rminTOF = arr[5];
|
||||
if (range >= rmax)
|
||||
{
|
||||
return rmaxTOF;
|
||||
}
|
||||
else if (range<=rmin)
|
||||
{
|
||||
return rminTOF;
|
||||
}
|
||||
else
|
||||
{
|
||||
# in between, interpolate
|
||||
double scale = (range-rmin)/(rmax-rmin);
|
||||
double tof = scale*(rmaxTOF-rminTOF) + rminTOF;
|
||||
return tof;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
struct weaponData = GetWeaponData(weapon.Type());
|
||||
if (weaponData->type == weapon.Type())
|
||||
{
|
||||
if (range > weaponData->rangeMax)
|
||||
{
|
||||
range = weaponData->rangeMax;
|
||||
}
|
||||
else if (range < weaponData->rangeMin)
|
||||
{
|
||||
range = weaponData->rangeMin;
|
||||
}
|
||||
double tof = range / weaponData->averageSpeed;
|
||||
return tof;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1.0;
|
||||
}
|
||||
}
|
||||
end_script
|
||||
|
||||
script bool InRangeToFire(WsfPlatform shooter, WsfWeapon weapon, WsfTrack track, double percentRangeMax, double percentRangeMin)
|
||||
|
||||
##do not check this again
|
||||
#if ( ! WeaponCapableAvailableAgainstThreat(weapon, track) )
|
||||
#{
|
||||
# return false;
|
||||
#}
|
||||
|
||||
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 = shooter.GroundRangeTo(track.CurrentLocation());
|
||||
|
||||
# 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.");
|
||||
return false;
|
||||
}
|
||||
double RminConstraint = theRmin * percentRangeMin;
|
||||
if (range < RminConstraint)
|
||||
{
|
||||
writeln_d(" Engagement did not shoot since inside the k * Rmin constraint distance.");
|
||||
writeln_d(" Range versus Rmin constraint = ", range, ", ", RminConstraint);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
# 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);
|
||||
return 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.");
|
||||
return false;
|
||||
}
|
||||
#double RmaxConstraint = theRmax * DefaultPercentRangeMax;
|
||||
if (range > (theRmax * percentRangeMax))
|
||||
{
|
||||
writeln_d(" Engagement did not shoot since outside the k * Rmax constraint distance.");
|
||||
writeln_d(" Range versus Rmax constraint = ", range, ", ", (theRmax * percentRangeMax));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
struct weaponData = GetWeaponData(weapon.Type());
|
||||
|
||||
writeln_d(" using input WeaponData struct values");
|
||||
#check our own ranges & angles --> hacky!!!
|
||||
#extern double EffectiveRange (WsfPlatform, WsfTrack);
|
||||
double effectiveRange = EffectiveRange(shooter, track);
|
||||
double absRelativeBearing = MATH.Fabs(shooter.RelativeBearingTo( track ));
|
||||
|
||||
if ((weaponData->rangeMin * percentRangeMin) > effectiveRange)
|
||||
{
|
||||
writeln_d(" target too close");
|
||||
return false;
|
||||
}
|
||||
if (absRelativeBearing > weaponData->maxFiringAngle)
|
||||
{
|
||||
writeln_d(" target firing angle too large");
|
||||
return false;
|
||||
}
|
||||
if (weaponData->rangeMax * percentRangeMax < effectiveRange)
|
||||
{
|
||||
writeln_d(" target too far away");
|
||||
return false;
|
||||
}
|
||||
|
||||
double range = shooter.SlantRangeTo(track);
|
||||
#double closingSpeed = PLATFORM.ClosingSpeedOf(track);
|
||||
double relBearing = track.RelativeBearingTo(shooter);
|
||||
if (relBearing > 90.0)
|
||||
{
|
||||
if (track.Speed() > weaponData->averageSpeed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
double speedDiff = weaponData->averageSpeed - track.Speed();
|
||||
if ((range/speedDiff) > weaponData->maxTimeFlight)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
end_script
|
||||
|
||||
Reference in New Issue
Block a user