
processor ucavFire WSF_TASK_PROCESSOR
   track_update_interval 5.0 sec

   script_variables
      string SENSOR_NAME;
      string WEAPON_NAME;
      int SALVO_SIZE = 2;
      string mTrackTaskStr = "Track";
      string mShootTaskStr = "Shoot";
   end_script_variables

   script bool FiringNow()
      return (WeaponsActiveFor(TRACK.TrackId()) > 0);
   end_script

   script bool InInterceptEnvelopeOf(WsfPlatform shooter)
      if (shooter.IsNull() || !shooter.IsValid()) return false;
      WsfWeapon w = shooter.Weapon(WEAPON_NAME);
      if (!w.IsValid()) return false;
      if (w.LaunchComputer().IsValid())
         return w.CanIntercept(TRACK);
      return (shooter.GroundRangeTo(TRACK) <= 150000.0);
   end_script

   script bool InInterceptEnvelopeOfAnySub()
      foreach (WsfPlatform sub in PLATFORM.Subordinates())
      {
         if (InInterceptEnvelopeOf(sub)) return true;
      }
      return false;
   end_script

   script bool TrackingNow()
      return (TasksAssignedFor(TRACK.TrackId(), mTrackTaskStr) > 0);
   end_script

   script void ReleaseResources()
      CancelTask(TRACK.TrackId());
   end_script

   script bool LaunchWeapon()
      WsfWeapon weapon;
      WsfPlatform platform;
      foreach (WsfPlatform sub in PLATFORM.Subordinates())
      {
         weapon = sub.Weapon(WEAPON_NAME);
         if (weapon.IsTurnedOn() &&
             (weapon.QuantityRemaining() >= SALVO_SIZE) &&
             (TasksAssignedTo(sub, "", weapon.Name()) < 1) &&
             InInterceptEnvelopeOf(sub))
         {
            platform = sub;
            break;
         }
      }
      bool launched = false;
      if (weapon.IsValid() && !platform.IsNull())
         launched = Fire(TRACK, mShootTaskStr, weapon.Name(), SALVO_SIZE, platform);
      return launched;
   end_script

   script bool IsAssignable()
      return ((PLATFORM == PLATFORM.Commander()) ||
              (TasksReceivedFor(TRACK.TrackId()) > 0) ||
              (OperatingLevelFor("ENGAGE") > 0));
   end_script

   script int WeaponsAvailable(WsfPlatform aAssignee)
      WsfWeapon weapon = aAssignee.Weapon(WEAPON_NAME);
      if (!weapon.IsTurnedOn()) return 0;
      return weapon.QuantityRemaining();
   end_script

   script int TotalWeaponsAvailable()
      int quantity = 0;
      foreach (WsfPlatform sub in PLATFORM.Subordinates())
         quantity = quantity + WeaponsAvailable(sub);
      return quantity;
   end_script

   script bool Engage()
      if (TotalWeaponsAvailable() < 1)
      {
         writeln("Engage: no weapons available");
         return false;
      }
      foreach (WsfPlatform sub in PLATFORM.Subordinates())
      {
         if (TasksAssignedTo(sub, TRACK.TrackId(), mTrackTaskStr) > 0) continue;
         WsfSensor sensor = sub.Sensor(SENSOR_NAME);
         if (!sensor.IsValid())
         {
            writeln("Engage: sub ", sub.Name(), " sensor ", SENSOR_NAME, " invalid");
            continue;
         }
         int maxTrack = sensor.MaximumRequestCount("TRACK");
         if (maxTrack > 0 && sensor.ActiveRequestCount("TRACK") >= maxTrack)
         {
            writeln("Engage: sub ", sub.Name(), " TRACK slots full");
            continue;
         }
         if (TRACK.BelievedAlive() && InInterceptEnvelopeOf(sub))
         {
            bool ok = StartTracking(TRACK, mTrackTaskStr, sensor.Name(), "default", sub);
            writeln("Engage: StartTracking ", sub.Name(), " -> ", ok);
            if (ok) return true;
         }
      }
      writeln("Engage: no sub assigned for ", TRACK.TargetName());
      return false;
   end_script

   evaluation_interval DETECTED 2.0 sec
   state DETECTED
      next_state DETECTED
         if (TRACK.IFF_Friend()) { CancelTask(TRACK.TrackId()); return true; }
         return false;
      end_next_state
      next_state ENGAGEABLE
         return (TRACK.BelievedAlive() && !TRACK.IFF_Friend() && IsAssignable());
      end_next_state
   end_state

   evaluation_interval ENGAGEABLE 2.0 sec
   state ENGAGEABLE
      next_state DETECTED
         if (TRACK.BelievedDead() || TRACK.IFF_Friend() ||
             ((TasksAssignedFor(TRACK.TrackId(), "ENGAGE") < 1) && !IsAssignable()))
         { ReleaseResources(); return true; }
         return false;
      end_next_state
      next_state ENGAGE
         bool didEngage = Engage();
         bool trackNow = TrackingNow();
         int assigned = TasksAssignedFor(TRACK.TrackId(), mTrackTaskStr);
         writeln("ENGAGEABLE->ENGAGE: didEngage=", didEngage, " trackNow=", trackNow, " assigned=", assigned, " target=", TRACK.TargetName());
         return (trackNow && (assigned > 0));
      end_next_state
   end_state

   evaluation_interval ENGAGE 2.0 sec
   state ENGAGE
      next_state ENGAGEABLE
         if (TRACK.BelievedDead() || !TrackingNow() || !IsAssignable())
         { ReleaseResources(); return true; }
         return false;
      end_next_state
      next_state FIRING
         if (TRACK.BelievedAlive() && TrackingNow() && !FiringNow() && InInterceptEnvelopeOfAnySub())
            return LaunchWeapon();
         return false;
      end_next_state
   end_state

   evaluation_interval FIRING 2.0 sec
   state FIRING
      next_state ENGAGE
         return (TRACK.BelievedDead() || !InInterceptEnvelopeOfAnySub() ||
                 (!FiringNow() && (TasksAssignedFor(TRACK.TrackId(), mShootTaskStr) < 1)) ||
                 (TotalWeaponsAvailable() < 1) || !TrackingNow());
      end_next_state
   end_state
end_processor
