

#process "follower"

class auto_att_fwd;
class auto_move;

core_quad_A, 0, 
  {object_pulse:auto_att_fwd, 0},
  {object_move:auto_move, 2048},
  {object_none, 0},
  {object_move:auto_move, -2048},

#code




// Process AI modes (these reflect the capabilities of the process)
enum
{
  MODE_GUARD, // process is circling builder, waiting for something to follow
  MODE_FOLLOW, // process is following a leader
  MODE_ATTACK, // process is attacking something
  MODE_WANDER, // wandering randomly
  MODES
};


// Targetting information
// Targetting memory allows processes to track targets (enemy or friend)
// The following enums are used as indices in the process' targetting memory
enum
{
  TARGET_PARENT, // a newly built process starts with its builder as entry 0
  TARGET_LEADER, // leader this process has found  
  TARGET_MAIN, // main target
  TARGET_BACK, // target of directional backwards attack
};

int attacking_back; // is set to 1 if backwards directional attack objects have a target
int other_target_x, other_target_y;
int angle_to_leader;

// Variable declaration and initialisation
//  (note that declaration and initialisation cannot be combined)
//  (also, variables retain their values between execution cycles)
int core_x, core_y; // location of core
core_x = get_core_x(); // location is updated each cycle
core_y = get_core_y();
int angle; // direction process is pointing
 // angles are in integer degrees from 0 to 8192, with 0 being right,
 // 2048 down, 4096 left and 6144 up.
angle = get_core_angle(); // angle is updated each cycle

int mode; // what is the process doing? (should be one of the MODE enums)

int move_x, move_y; // destination

int scan_result; // used to hold the results of a scan of nearby processes

int initialised;
if (initialised == 0)
{
 initialised = 1;
 mode = MODE_GUARD;
 target_copy(TARGET_LEADER, TARGET_PARENT); // copies parent to address TARGET_LEADER
 listen_channel(5); // processes in this mission use channel 5 for leader/follower messages
}


// What the process does next depends on its current mode
switch(mode)
{


 case MODE_WANDER:
  if (distance_from_xy(move_x, move_y) < 300)
  {
   gosub start_wandering;
   break;
  }
  if (scan_for_threat(cos(angle, 400), sin(angle, 400), TARGET_MAIN))
  {
   mode = MODE_ATTACK;
  }
// Now listen for a broadcast from a leader process
  if (check_messages() // returns 1 if message received since previous cycle
   && read_message() == 40) // returns contents of message in sequence - 40 is message code for "please follow me"
  {
   mode = MODE_FOLLOW;
   get_message_source(TARGET_LEADER); // saves sender into targetting memory
  }
  auto_move.move_to(move_x, move_y);
  break;
  
// MODE_GUARD is like MODE_FOLLOW but the process will follow a new leader if one is built
 case MODE_GUARD:
  if (check_messages() // returns 1 if message received since previous cycle
   && read_message() == 40) // returns contents of message in sequence - 40 is message code for "please follow me"
  {
   mode = MODE_FOLLOW;
   get_message_source(TARGET_LEADER); // saves sender into targetting memory
  }
// fall-through:
 case MODE_FOLLOW:
  if (process[TARGET_LEADER].visible() <= 0) // leader doesn't exist
  {
   mode = MODE_WANDER;
   gosub start_wandering;
   break;
  }
  if (process[TARGET_LEADER].distance() < 700
   && scan_for_threat(cos(angle, 400), sin(angle, 400), TARGET_MAIN))
  {
   mode = MODE_ATTACK;
  }
  angle_to_leader = atan2(process[TARGET_LEADER].get_core_y() - core_y, process[TARGET_LEADER].get_core_x() - core_x);
  angle_to_leader += 1024;
  move_x = process[TARGET_LEADER].get_core_x() - cos(angle_to_leader, 400);
  move_y = process[TARGET_LEADER].get_core_y() - sin(angle_to_leader, 400);
  auto_move.move_to(move_x, move_y); // approach_target(TARGET_LEADER, 0, 500);
  break;
  
 case MODE_ATTACK: // attacking something it found itself
  if (process[TARGET_LEADER].visible() > 0 // leader exists (friendly targets are always visible if they exist)
   && process[TARGET_LEADER].distance() > 1200) // don't stray too far from leader
  {
   gosub guard_or_follow;
   break;
  }
  if (process[TARGET_MAIN].visible() <= 0) // target no longer exists, or is out of range
  {
   gosub guard_or_follow;
   break;
  }
  auto_move.approach_target(TARGET_MAIN, 0, 300);
  if (process[TARGET_MAIN].distance() < 1000
   && arc_length(angle, atan2(process[TARGET_MAIN].get_core_y() - core_y, process[TARGET_MAIN].get_core_x() - core_x)) <= 2048)
   auto_att_fwd.fire_at(TARGET_MAIN, 0);
  break;
  

} // end of mode switch




exit;


start_wandering:
 mode = MODE_WANDER;
 move_x = 800 + random(world_x() - 1600);
 move_y = 800 + random(world_y() - 1600);
 return;

guard_or_follow:
 if (target_compare(TARGET_PARENT, TARGET_LEADER))
  mode = MODE_GUARD;
   else
    mode = MODE_FOLLOW;
 auto_att_fwd.no_target();
 return;
