

#process "harvester"

class auto_att_right;
class auto_harvest;
class auto_att_left;
class auto_move;

core_pent_B, 4096, 
  {object_repair, 0},
  {object_downlink, -205, 
    {component_prong, // component 1
      {object_interface, 0},
      {object_storage, 0},
      {object_uplink, 0},
      {object_storage, 0},
    }
  },
  {object_downlink, 205, 
    {component_prong, // component 2
      {object_harvest:auto_harvest, 0},
      {object_interface, 0},
      {object_storage, 0},
      {object_uplink, 0},
    }
  },
  {object_downlink, 1590, 
    {component_long5, // component 3
      {object_interface_depth, 0},
      {object_downlink, 1483, 
        {component_cap, // component 6
          {object_move:auto_move, 1074},
          {object_move:auto_move, 73},
          {object_move:auto_move, -1207},
          {object_uplink, 0},
        }
      },
      {object_uplink, 0},
      {object_pulse:auto_att_right, 362},
      {object_interface, 0},
    }
  },
  {object_downlink, -1590, 
    {component_long5, // component 4
      {object_interface_depth, 0},
      {object_uplink, 0},
      {object_downlink, -1483, 
        {component_cap, // component 5
          {object_uplink, 0},
          {object_move:auto_move, 1207},
          {object_move:auto_move, -73},
          {object_move:auto_move, -1074},
        }
      },
      {object_interface, 0},
      {object_pulse:auto_att_left, -362},
    }
  },

#code


enum
{
  MODE_SEEK_WELL, // process is wandering randomly looking for a well
  MODE_SEEK_ALLOCATOR, // process is looking for a new allocator to return to (usual one destroyed)
  MODE_HARVEST, // process is harvesting data from a data well (or travelling to do so)
  MODE_HARVEST_RETURN, // process has harvested data and is returning to an allocator
  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_ALLOCATOR, // process that this process will return to when finished harvesting
  TARGET_ENEMY, // any nearby enemy
  TARGET_LEFT,
  TARGET_RIGHT
};


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

int move_x, move_y; // destination

// Harvester variables
int data_well_x, data_well_y; // location of data well
int allocator_x, allocator_y; // location of process this process will return to with data
int source_well_x, source_well_y; // ignore data well near process that built this harvest
int ignore_well_x, ignore_well_y; // ignore data well with a friendly allocator near it
int well_status; // used in calling this_well_is_taken subroutine

int initialised;

if (initialised == 0)
{
 initialised = 1;
 allocator_x = process[TARGET_ALLOCATOR].get_core_x();
 allocator_y = process[TARGET_ALLOCATOR].get_core_y();
// ignore the data well that probably exists near creation point:
 source_well_x = get_well_x();
 source_well_y = get_well_y();
 mode = MODE_SEEK_WELL;
 listen_channel(3); // mission processes use channel 3 for broadcasts about data wells
 gosub start_wandering;
}

// If under attack, call for help:
if (scan_for_threat(0,0,TARGET_ENEMY))
{
   broadcast_target(3000, // range of broadcast, in pixels
                    4, // channel 4 is used for targetting broadcasts
                    1, // priority
                    TARGET_ENEMY, // the target will be broadcast so other processes can find it directly
// message contents:
                    1, // indicates target found
                    process[TARGET_ENEMY].get_core_x(),
                    process[TARGET_ENEMY].get_core_y());
}


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

case MODE_SEEK_WELL:
  if (distance_from_xy(move_x, move_y) < 300)
  {
   gosub start_wandering;
   break;
  }
  auto_move.move_to(move_x, move_y);
  if (distance_from_xy(allocator_x, allocator_y) > 1000) // don't search near parent
  {
   if (search_for_well() // found nearby well!
//    && get_well_data() > 100
    && (get_well_x() != source_well_x
     || get_well_y() != source_well_y)
    && (get_well_x() != ignore_well_x
     || get_well_y() != ignore_well_y))
   {
    mode = MODE_HARVEST;
    data_well_x = get_well_x();
    data_well_y = get_well_y();
    well_status = 0; // indicates this well taken by mobile harvester
    gosub this_well_is_taken;
   }
  }
  break;

case MODE_SEEK_ALLOCATOR: // search for a friendly allocator
 gosub listen_to_broadcasts;
 break;

case MODE_HARVEST:
// only gather if near target (avoids inadvertently gathering from another well)v
  if (distance_from_xy(data_well_x, data_well_y) < 800)
  {
// check for nearby friendly allocator. If found, ignore this well:
   scan_result = scan_single(0, // x_offset
                             0, // y_offset
                             -1, // process memory; -1 means don't save target
                             2, // 2 = accept friendly only
                             0, // components min
                             100, // components max (100 is too high so this will always pass)
                             0b0001); // bitfield for accepting processes with allocators
   if (scan_result == 1)
   {
    well_status = 1; // indicates this well taken by static harvester/allocator
    gosub this_well_is_taken;
    mode = MODE_SEEK_WELL;
    ignore_well_x = data_well_x;
    ignore_well_y = data_well_y;
    break;
   }
   auto_move.move_to(data_well_x, data_well_y); // save power while harvesting by only using two move objects.
   auto_harvest.gather_data();
  }
   else
   {
    auto_move.move_to(data_well_x, data_well_y);  
    gosub listen_to_broadcasts;
   }
  if (get_data_stored() == get_data_capacity())
  {
    mode = MODE_HARVEST_RETURN;
    well_status = 0; // indicates this well taken by mobile harvester
    gosub this_well_is_taken;
  }
  break;

case MODE_HARVEST_RETURN:
// check that allocator is still alive:
  if (process[TARGET_ALLOCATOR].visible() <= 0) // allocator no longer exists
  {
// if not, look for a new one by listening to main base broadcasts
   mode = MODE_SEEK_ALLOCATOR;
   listen_channel(1);
   break;
  }
  gosub listen_to_broadcasts;  
  auto_move.move_to(allocator_x, allocator_y);
  auto_harvest.give_data(TARGET_ALLOCATOR, 100);
  if (get_data_stored() == 0)
  {
    mode = MODE_HARVEST;
  }
  break;

} // end of mode switch


charge_interface_max();

auto_att_left.attack_scan(-3000, 400, TARGET_LEFT);
auto_att_right.attack_scan(3000, 400, TARGET_RIGHT);

restore_self();

repair_self();





exit;


start_wandering:
// doesn't set mode, as can be called from different modes.
 move_x = 800 + random(world_x() - 1600);
 move_y = 800 + random(world_y() - 1600);
 return;

this_well_is_taken:
 // tell other harvesters that this well is taken:
 broadcast(-1, // range of broadcast (in pixels). -1 means unlimited range.
           3, // channel - other processes must be listening on this channel to receive the message
           0, // priority - 0 or 1. 0 means message may be replaced by priority 1 messages
           33, // code 33 claims the data well
           well_status, // should be set by code calling this subroutine
           data_well_x, // message contents (variable number of arguments; up to 8)
           data_well_y);
 return;

listen_to_broadcasts:
// Now listen to other processes that may have announced that the data well is taken:
  while(check_messages()) // returns number of (unread) messages received since previous cycle
  {
   switch(read_message()) // first message value is the message code
   {
     case 33: // code for data well claims
      read_message(); // first value (well status) not relevant
      if (read_message() == data_well_x
       && read_message() == data_well_y)    
      {
       ignore_well_x = data_well_x;
       ignore_well_y = data_well_y;
       mode = MODE_SEEK_WELL;
       gosub start_wandering;
      }
      break;
      
     case 10: // code for "I am the main base" messages
// these messages will be broadcast by the main base, on channel 1.
// this process uses them to find an allocator to return to, if its original allocator has been destroyed
      if (mode == MODE_SEEK_ALLOCATOR) // should only be listening to channel 1 in this mode, but check anyway
      {
        get_message_source(TARGET_ALLOCATOR); // reads the source of the message into the allocator address in targetting memory
        allocator_x = process[TARGET_ALLOCATOR].get_core_x();
        allocator_y = process[TARGET_ALLOCATOR].get_core_y();
        mode = MODE_HARVEST_RETURN;      
        ignore_channel(1); // can ignore channel 1 now
      }
      break;

   } // end switch(message code)
   next_message();
  }
 return;
