

#process "fighter2"

class auto_move;
class auto_att_fwd;

core_quad_B, 0, 
  {object_pulse:auto_att_fwd, 716},
  {object_pulse_l:auto_att_fwd, -734},
  {object_move:auto_move, -1174},
  {object_move:auto_move, 1114},

#code




// Process AI modes (these reflect the capabilities of the process)
enum
{
  MODE_GUARD_PARENT, // process is circling its parent
  MODE_GUARD_LEADER, // process is circling its leader
  MODE_WANDER, // process has nothing to do so it wanders randomly (happens if both parent and guard target destroyed)
  MODE_ATTACK, // process is attacking something
  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, // parent may have transmitted a leader
  TARGET_MAIN, // target to attack
};

int other_target_x, other_target_y;

// 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 guard_count;

int move_x, move_y; // destination
int attack_x, attack_y;

int scan_result; // used to hold the results of a scan of nearby processes
int saved_mode; // used to save current mode while temporarily attacking

int initialised;
if (initialised == 0)
{
 initialised = 1;
 listen_channel(4); // this is the channel used to broadcast "target found" and "cancel" messages
 listen_channel(5); // this is the channel used to broadcast coordination messages to attacking processes
 attack_mode(1);
 mode = MODE_GUARD_PARENT;
// parent should have transmitted a message identifying the leader:
 if (check_messages())
 {
  switch(read_message())
  {
   case 41: // guard parent - sent by parent when built
    get_message_source(TARGET_LEADER); // store the message sender as leader
    mode = MODE_GUARD_PARENT;
    break;
   case 42: // guard leader - sent by parent when built
    get_message_target(TARGET_LEADER); // store the message target as leader
    mode = MODE_GUARD_LEADER;
    break;
  }
 }
}


 while (check_messages())
 {
  switch(read_message())
  {
   case 40: // guard me please - sent by anything wanting to be guarded
    if (random(2))
    {
     get_message_source(TARGET_LEADER);
     mode = MODE_GUARD_LEADER;
    }
    break;
  }
  next_message();
 }



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

 case MODE_GUARD_PARENT:
  if (!process[TARGET_PARENT].visible()) // visible() always returns 1 for a friendly process, unless it no longer exists
  {
// guard target has been destroyed.
// check whether leader exists:
   if (process[TARGET_LEADER].visible())
   {
    mode = MODE_GUARD_LEADER;
    break;
   }
// Parent and leader both destroyed. start wandering:
   mode = MODE_WANDER;
   gosub start_wandering;
   break;
  }
  if (process[TARGET_PARENT].distance_less(1000)
   && scan_for_threat(0, 0, TARGET_MAIN))
  {
   mode = MODE_ATTACK;
   saved_mode = MODE_GUARD_PARENT;
   attack_x = process[TARGET_MAIN].get_core_x();
   attack_y = process[TARGET_MAIN].get_core_y();
// tell nearby processes about the target:
   broadcast_target(2000, // range of broadcast, in pixels
                    4, // channel
                    1, // priority
                    TARGET_MAIN, // the target will be broadcast so other processes can find it directly
// message contents:
                    1, // indicates target found
                    attack_x,
                    attack_y);
   break;
  }
  int angle_to_parent;
// find the angle to the parent, then move in a circle around it:
  angle_to_parent = atan2(process[TARGET_PARENT].get_core_y() - core_y, process[TARGET_PARENT].get_core_x() - core_x);
  angle_to_parent += 1024;
  move_x = process[TARGET_PARENT].get_core_x() - cos(angle_to_parent, 300);
  move_y = process[TARGET_PARENT].get_core_y() - sin(angle_to_parent, 300);
  auto_move.move_to(move_x, move_y);
  auto_att_fwd.attack_scan(0, 400, TARGET_MAIN);
  break;
  
 case MODE_GUARD_LEADER:
  if (!process[TARGET_LEADER].visible()) // visible() always returns 1 for a friendly process, unless it no longer exists
  {
// guard target has been destroyed.
// check whether leader exists:
   if (process[TARGET_PARENT].visible())
   {
    mode = MODE_GUARD_PARENT;
    break;
   }
// Parent and leader both destroyed. start wandering:
   mode = MODE_WANDER;
   gosub start_wandering;
   break;
  }
  if (process[TARGET_PARENT].distance_less(900)
   && scan_for_threat(0, 0, TARGET_MAIN))
  {
   mode = MODE_ATTACK;
   saved_mode = MODE_GUARD_LEADER;
   attack_x = process[TARGET_MAIN].get_core_x();
   attack_y = process[TARGET_MAIN].get_core_y();
// tell nearby processes about the target:
   broadcast_target(2000, // range of broadcast, in pixels
                    4, // channel
                    1, // priority
                    TARGET_MAIN, // the target will be broadcast so other processes can find it directly
// message contents:
                    1, // indicates target found
                    attack_x,
                    attack_y);
   break;
  }
// find the angle to the eleader, then move in a circle around it:
  angle_to_parent = atan2(process[TARGET_LEADER].get_core_y() - core_y, process[TARGET_LEADER].get_core_x() - core_x);
// just use the angle_to_parent variable for this
  angle_to_parent += 1024;
  move_x = process[TARGET_LEADER].get_core_x() - cos(angle_to_parent, 500);
  move_y = process[TARGET_LEADER].get_core_y() - sin(angle_to_parent, 500);
  auto_move.move_to(move_x, move_y);
  auto_att_fwd.attack_scan(0, 400, TARGET_MAIN);
  break;
  


 case MODE_WANDER:
  if (distance_from_xy(move_x, move_y) < 300)
  {
   gosub start_wandering;
   break;
  }
  if (scan_for_threat(0, 0, TARGET_MAIN))
  {
   mode = MODE_ATTACK;
   saved_mode = MODE_WANDER;
   attack_x = process[TARGET_MAIN].get_core_x();
   attack_y = process[TARGET_MAIN].get_core_y();
// tell nearby processes about the target:
   broadcast_target(2000, // range of broadcast, in pixels
                    4, // channel
                    1, // priority
                    TARGET_MAIN, // the target will be broadcast so other processes can find it directly
// message contents:
                    1, // indicates target found
                    attack_x,
                    attack_y);
   break;
  }
  gosub listen_to_broadcasts;
  auto_move.move_to(move_x, move_y);
  break;

 case MODE_ATTACK: // attacking TARGET_MAIN
  if (!process[TARGET_MAIN].visible()) // target no longer exists, or is out of range of any friendly process
  {
// should be able to assume that attack_x, attack_y hold the target's last know location:
    if (distance_from_xy_less(attack_x, attack_y, 600)) // returns 1 if process is less than 600 pixels from attack_x,attack_y
    {
// Near enough to the target that if it existing we'd see it. So give up and go back to wandering:
      mode = saved_mode;
      break;
    }
// go to target's last known location:
    auto_move.move_to(attack_x, attack_y);
    break;
  }
  if (saved_mode == MODE_GUARD_PARENT
   && process[TARGET_PARENT].visible()
   && process[TARGET_PARENT].distance_more(1400))
  {
   mode = MODE_GUARD_PARENT;
   break;
  }
  if (saved_mode == MODE_GUARD_LEADER
   && process[TARGET_LEADER].visible()
   && process[TARGET_LEADER].distance_more(1400))
  {
   mode = MODE_GUARD_LEADER;
   break;
  }
// Target visible. So approach it, and attack if near enough:
  attack_x = process[TARGET_MAIN].get_core_x();
  attack_y = process[TARGET_MAIN].get_core_y();
  if (process[TARGET_MAIN].distance() < 1000)
  {
    if (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);
    auto_move.approach_target(TARGET_MAIN, 0, 300);
    break;
  }
  auto_move.move_to(attack_x, attack_y);
  break;
  

} // end of mode switch



exit; // finished!

// subroutine that makes process move randomly
start_wandering:
 mode = MODE_WANDER;
 move_x = 400 + random(world_x() - 800);
 move_y = 400 + random(world_y() - 800);
 auto_att_fwd.no_target();
 return;


// subroutine that listens to other scouts' broadcasts
listen_to_broadcasts:
// Listen for a broadcast on channel 3 (listen_channel(3) call above sets the process to listen to channel 3)
  while (check_messages()) // returns number of messages received since previous cycle
  {
   switch(read_message()) // read_message() returns next value in message
   {

    case 1: // another process has found a target
    case 2: // another process has found a high priority target (currently treated the same)
// expected message format is (1 [or 2], target_x, target_y), with the target attached to the broadcast
     attack_x = read_message();
     attack_y = read_message();
     get_message_target(TARGET_MAIN); // copies target attached to broadcast to target TARGET_MAIN
     mode = MODE_ATTACK;
     break;

   } // end switch(read_message())
   next_message();
  } // end if (check_messages())
 return;
