//==============================================================================
// Scn17p2: AI Scenario Script for scenario 17 player 2
//==============================================================================
/*
   AI owner:  Mike Kidd
   Scenario owner: Jeff Brown
   With hacker by: Greg Street

   Overview:
   The player needs to break in and destroy a fortified CP base.

   The CP attacks with waves of units, with a special focus on including some
   priests to counter the player's myth-heavy armies.

   If the player ignores the objectives and moves a significant army to the
   area (west) near the prison, the CP swarms his armies over there.

   What Greg tried to add: When CP Migdol is lost, pull in everyone and play
   defensive so that HP can head to the prison.
     
   God Powers: Rain and Locust Swarm
   Minor gods: Ptah, Hathor, Osiris

   Difficulty:  Not implemented.
*/
//==============================================================================


include "scn lib.xs";


// *****************************************************************************
//
// Globals
//
// *****************************************************************************

// Attack routes and queries
int   routeLeft = -1;
int   routeCenter = -1;
int   routeRight = -1;

int   queryP1Units = -1;
int   queryP1Buildings = -1;
int   queryP1Farms = -1;
int   queryP1Monuments = -1;
int   queryP1Citadels = -1;

// Army control
int   lastAttackPlan = -1;       // Used to find "my army" for god power position info
int   nextAttackTime = 240000;   // Will be adjusted for the wakeup time
int   attackInterval = 225000;   // Attack every 3:45
int   attackSize = 4;
int   attackSizeIncrement = 1;
int   maxAttackSize = 25;
int   defendPlan = -1;

int   barracksMaintainID = -1;      // Plan for maintaining population of barracks units
int   barracksMaintainQty = 20;     // How many to maintain
int   barracksUnit = -1;            // Which unit type
int   barracksMaintainDelay = 15;   // How many seconds between training units

int   migdolMaintainID = -1;
int   migdolMaintainQty = 10;
int   migdolMaintainDelay = 30;
int   migdolUnit = -1;

int   priestMaintainID = -1;
int   priestMaintainQty = 4;
int   priestUnit = cUnitTypePriest;
int   priestMaintainDelay = 60;

int   mythMaintainID = -1;
int   mythMaintainQty = 5;
int   mythUnit = -1;
int   mythMaintainDelay = 75;


// Cinematic blocks
const string cbAttackGather = "7959";
const string cbTownLocation = "7960";
const string cbRouteLeft1 = "7961";
const string cbRouteLeft2 = "7962";
const string cbRouteCenter1 = "7963";
const string cbRouteCenter2 = "7964";
const string cbRouteRight1 = "7965";
const string cbRouteRight2 = "7966";
const string cbNearPrison = "7967";
const string cbPriestGather = "7968";
const string cbMythGather = "7969";
const string cbMigdolGather = "7970";
const string cbBarracksGather = "7971";
const string cbNearMigdol = "1081";


// Misc.
int   age2Time = 420000;    // 7 min
int   age3Time = 960000;   // 16 min
int   age4Time = 1620000;  // 27 min
int   startTime = -1;      // Time of the wakeup() function...will be added to the age times.



// *****************************************************************************
//
//                                FUNCTIONS
//
// *****************************************************************************


// Called by trigger when the cinematics are done
void wakeup(int parm=-1)
{
   static bool alreadyRun = false;
   xsDisableRule("spoofWakeup");       // No need for rule to fire, the trigger woke us up.
   aiEcho("Wakeup running at "+timeString()+".");
   if (alreadyRun == true)
      return;
   alreadyRun = true;

   startTime = xsGetTime();
   age2Time = age2Time + startTime;
   xsEnableRule("goToAge2");
   age3Time = age3Time + startTime;    // Adjust for delay in wakeup. 
   age4Time = age4Time + startTime;
   nextAttackTime = nextAttackTime + startTime;

   // Init age 1 maintain plan
   priestMaintainID = maintainUnit(cUnitTypePriest, priestMaintainQty, kbGetBlockPosition(cbPriestGather), priestMaintainDelay);

   xsEnableRule("useRain");
   xsEnableRule("scout");
   xsEnableRule("attackGenerator");
   xsEnableRule("armyNearPrison");

   // Init low-priority defend plan to manage all extra mil units
   defendPlan =aiPlanCreate("Defend Plan", cPlanDefend);
   if (defendPlan >= 0)
   {
      aiPlanAddUnitType(defendPlan, cUnitTypeMilitary, 0, 200, 200);    // All unassigned mil units
      aiPlanSetDesiredPriority(defendPlan, 10);                       // Way low, below scouting and attack
      aiPlanSetVariableVector(defendPlan, cDefendPlanDefendPoint, 0, kbGetBlockPosition(cbTownLocation));
//      aiPlanSetVariableInt(defendPlan, cDefendPlanDefendTargetID, 0, 1410);
      aiPlanSetVariableFloat(defendPlan, cDefendPlanEngageRange, 0, 40);
      aiPlanSetVariableBool(defendPlan, cDefendPlanPatrol, 0, false);
      aiPlanSetVariableFloat(defendPlan, cDefendPlanGatherDistance, 0, 20.0);
//      vector pointA=gTownLocation;
//      pointA=xsVectorSetX(pointA, xsVectorGetX(gTownLocation)-50.0);
//      vector pointB=gTownLocation;
//      pointB=xsVectorSetZ(pointB, xsVectorGetZ(gTownLocation)-50.0);
      //aiPlanSetNumberVariableValues(defendPlan, cDefendPlanPatrolWaypoint, 2, true);
      //aiPlanSetVariableVector(defendPlan, cDefendPlanPatrolWaypoint, 0, pointA);
      //aiPlanSetVariableVector(defendPlan, cDefendPlanPatrolWaypoint, 1, pointB);
      aiPlanSetInitialPosition(defendPlan, kbGetBlockPosition(cbAttackGather));
      aiPlanSetActive(defendPlan); 
      aiEcho("Creating defend plan");
   }
}




// Used to delete units that are being replaced by a new type.
void deleteObsoleteUnits(int unitType=cUnitTypeUnit, int player=2, vector center=vector(-1,-1,-1), float radius = 20.0, float percent=1.00)
{
   // Make query
   int query = -1;
   int count = -1;

   query = kbUnitQueryCreate("Unit deletion query");
   if ( configQuery(query, unitType, -1, cUnitStateAlive, player, center, false, radius) == false)
      return;
   kbUnitQueryResetResults(query);
   count = kbUnitQueryExecute(query);
   
   // Iterate list, deleting percentage indicated
   float remainder=0.0; // Used to handle percentages, when this gets >= 1, it's time to delete a unit.
   
   for (i=0; <count)
   {
      remainder = remainder + percent;
      if (remainder >= 1.0)   // time to delete one
      {
         aiTaskUnitDelete(kbUnitQueryGetResult(query,i));
         remainder = remainder - 1.0;
      }
   }
}


void age2EventHandler(int bogus=-1)
{
   xsEnableRule("goToAge3");
   xsEnableRule("getAge2UnitUpgrades");
   xsEnableRule("getAge2ArmoryUpgrades");
   int oldUnit = cUnitTypeSpearman;

   switch(aiRandInt(3)) // Select a rax unit
   {
   case 0:
      {
         barracksUnit = cUnitTypeSlinger;
         break;
      }
   case 1:
      {
         barracksUnit = cUnitTypeSpearman;
         break;
      }
   case 2:
      {
         barracksUnit = cUnitTypeAxeman;
         break;
      }
   }
   aiEcho("Barracks unit is "+kbGetProtoUnitName(barracksUnit));
   barracksMaintainID = maintainUnit(barracksUnit, barracksMaintainQty, kbGetBlockPosition(cbBarracksGather), barracksMaintainDelay);
   if (barracksUnit != oldUnit)
   {
      deleteObsoleteUnits(oldUnit, 2, kbGetBlockPosition(cbBarracksGather), 20, .5);   // Delete half of the old units
      aiEcho("Deleting units: "+kbGetProtoUnitName(oldUnit));
   }
}



void age3EventHandler(int bogus=-1)
{
   xsEnableRule("useLocustSwarm");
   xsEnableRule("goToAge4");
   xsEnableRule("getAge3UnitUpgrades");
   xsEnableRule("getAge3ArmoryUpgrades");

   int oldUnit = barracksUnit;
   switch(aiRandInt(3)) // Re-select a rax unit
   {
   case 0:
      {
         barracksUnit = cUnitTypeSlinger;
         break;
      }
   case 1:
      {
         barracksUnit = cUnitTypeSpearman;
         break;
      }
   case 2:
      {
         barracksUnit = cUnitTypeAxeman;
         break;
      }
   }
   aiEcho("Barracks unit is "+kbGetProtoUnitName(barracksUnit));
	aiPlanSetVariableInt(barracksMaintainID, cTrainPlanUnitType, 0, barracksUnit);
   if (barracksUnit != oldUnit)
   {
      deleteObsoleteUnits(oldUnit, 2, kbGetBlockPosition(cbBarracksGather), 20, .5);   // Delete half of the old units
      aiEcho("Deleting units: "+kbGetProtoUnitName(oldUnit));
   }

   switch(aiRandInt(3)) // Select a migdol unit
   {
   case 0:
      {
         migdolUnit = cUnitTypeCamelry;
         break;
      }
   case 1:
      {
         migdolUnit = cUnitTypeChariotArcher;
         break;
      }
   case 2:
      {
         migdolUnit = cUnitTypeElephant;
         break;
      }
   }
   aiEcho("Migdol unit is "+kbGetProtoUnitName(migdolUnit));
   migdolMaintainID = maintainUnit(migdolUnit, migdolMaintainQty, kbGetBlockPosition(cbMigdolGather), migdolMaintainDelay);

   switch(aiRandInt(2)) // Select a myth unit
   {
   case 0:
      {
         mythUnit = cUnitTypePetsuchos;
         break;
      }
   case 1:
      {
         mythUnit = cUnitTypeWadjet;
         break;
      }
   }
   aiEcho("Myth unit is "+kbGetProtoUnitName(mythUnit));
   mythMaintainID = maintainUnit(mythUnit, mythMaintainQty, kbGetBlockPosition(cbMythGather), mythMaintainDelay);


   attackSizeIncrement = attackSizeIncrement + 1;
}



void age4EventHandler(int bogus=-1)
{
   xsEnableRule("getAge4UnitUpgrades");
   xsEnableRule("getAge4ArmoryUpgrades");

   mythUnit = cUnitTypeMummy;
   aiPlanSetVariableInt(mythMaintainID, cTrainPlanUnitType, 0, mythUnit);
   aiEcho("Myth unit is Wadjet.");

   int oldUnit = barracksUnit;
   switch(aiRandInt(3)) // Re-select a rax unit
   {
   case 0:
      {
         barracksUnit = cUnitTypeSlinger;
         break;
      }
   case 1:
      {
         barracksUnit = cUnitTypeSpearman;
         break;
      }
   case 2:
      {
         barracksUnit = cUnitTypeAxeman;
         break;
      }
   }
   aiEcho("Barracks unit is "+kbGetProtoUnitName(barracksUnit));
	aiPlanSetVariableInt(barracksMaintainID, cTrainPlanUnitType, 0, barracksUnit);
   if (barracksUnit != oldUnit)
   {
      deleteObsoleteUnits(oldUnit, 2, kbGetBlockPosition(cbBarracksGather), 20, .5);   // Delete half of the old units
      aiEcho("Deleting units: "+kbGetProtoUnitName(oldUnit));
   }

   oldUnit = migdolUnit;
   switch(aiRandInt(3)) // Re-select a migdol unit
   {
   case 0:
      {
         migdolUnit = cUnitTypeCamelry;
         break;
      }
   case 1:
      {
         migdolUnit = cUnitTypeChariotArcher;
         break;
      }
   case 2:
      {
         migdolUnit = cUnitTypeElephant;
         break;
      }
   }
   aiEcho("Migdol unit is "+kbGetProtoUnitName(migdolUnit));
   aiPlanSetVariableInt(migdolMaintainID, cTrainPlanUnitType, 0, migdolUnit);
   if (migdolUnit != oldUnit)
   {
      deleteObsoleteUnits(oldUnit, 2, kbGetBlockPosition(cbBarracksGather), 20, .5);   // Delete half of the old units
      aiEcho("Deleting units: "+kbGetProtoUnitName(oldUnit));
   }
   attackSizeIncrement = attackSizeIncrement + 1;
}




void attack(int size=0)
{
   int   attackID=aiPlanCreate("Attack at "+timeString(true)+" ", cPlanAttack);
   if (attackID < 0)
      return;

   if (aiPlanSetVariableInt(attackID, cAttackPlanPlayerID, 0, 1) == false)
      return;

   if (aiPlanSetNumberVariableValues(attackID, cAttackPlanTargetTypeID, 2, true) == false)
      return;

//   aiPlanSetVariableInt(attackID, cAttackPlanTargetTypeID, 0, cUnitTypeUnit);
//   aiPlanSetVariableInt(attackID, cAttackPlanTargetTypeID, 1, cUnitTypeBuilding);

   switch(aiRandInt(3))    // Pick a random path
   {
   case 0:
      {
         aiPlanSetVariableInt(attackID, cAttackPlanAttackRouteID, 0, routeLeft);
         break;
      }
   case 1:
      {
         aiPlanSetVariableInt(attackID, cAttackPlanAttackRouteID, 0, routeCenter);
         break;
      }
   case 2:
      {
         aiPlanSetVariableInt(attackID, cAttackPlanAttackRouteID, 0, routeRight);
         break;
      }
   }
 
   aiPlanSetVariableVector(attackID, cAttackPlanGatherPoint, 0, kbGetBlockPosition(cbAttackGather));
   aiPlanSetVariableFloat(attackID, cAttackPlanGatherDistance, 0, 10.0);

   switch(kbGetAge())      // Set the targets and unit composition
   {
   case cAge1:
      {  // Spearmen and one priest, to attack units
         aiPlanAddUnitType(attackID, cUnitTypeSpearman, 1, size-1, size-1);
         aiPlanAddUnitType(attackID, cUnitTypePriest, 1, 1, 1);
         aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 0, queryP1Units);
         break;
      }
   case cAge2:
      {  // Rax unit and priests, to attack farms, units
         aiPlanAddUnitType(attackID, barracksUnit, 1, (3*size)/4, (3*size)/4);
         aiPlanAddUnitType(attackID, cUnitTypePriest, 1, size/4, size/4);
         aiPlanSetNumberVariableValues(attackID, cAttackPlanQueryID, 2);
         aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 0, queryP1Farms);
         aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 1, queryP1Units);
         break;
      }
   case cAge3:
      {  // Rax, one priest, myth, midgol, to attack monuments, units, buildings
         aiPlanAddUnitType(attackID, barracksUnit, 1, size/2, size/2);
         aiPlanAddUnitType(attackID, migdolUnit, 1, size/4, size/4);
         aiPlanAddUnitType(attackID, cUnitTypePriest, 0, 1, 1);
         aiPlanAddUnitType(attackID, cUnitTypeMythUnit, 0, size/4, size/4);
         aiPlanSetNumberVariableValues(attackID, cAttackPlanQueryID, 3);
         aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 0, queryP1Monuments);
         aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 1, queryP1Units);        
         aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 2, queryP1Buildings); 
         break;
      }
   case cAge4:
      {  // Rax, few priests, myth, migdol, to attack Citadel, units, buildings
         aiPlanAddUnitType(attackID, barracksUnit, 1, size/3, size/3);
         aiPlanAddUnitType(attackID, migdolUnit, 1, size/3, size/3);
         aiPlanAddUnitType(attackID, cUnitTypePriest, 0, size/10, size/10);
         aiPlanAddUnitType(attackID, cUnitTypeMythUnit, 0, size/4, size/4);
         aiPlanSetNumberVariableValues(attackID, cAttackPlanQueryID, 3);
         aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 0, queryP1Citadels);
         aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 1, queryP1Units);        
         aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 2, queryP1Buildings); 
         break;
      }
   }

   aiPlanSetInitialPosition(attackID, kbGetBlockPosition(cbAttackGather));
   aiPlanSetRequiresAllNeedUnits(attackID, true);
   aiPlanSetActive(attackID);
   aiEcho("Activating attack plan "+attackID+" with appx "+size+" units.");
   lastAttackPlan = attackID; // update the global var
}




void main()
{
   aiEcho("Starting Scn17p2.xs");


   //Calculate some areas.
   kbAreaCalculate(1200.0);
   aiRandSetSeed();
   kbSetTownLocation(kbGetBlockPosition(cbTownLocation));

   aiSetAgeEventHandler(cAge2, "age2EventHandler");
   aiSetAgeEventHandler(cAge3, "age3EventHandler");
   aiSetAgeEventHandler(cAge4, "age4EventHandler");

   // Init attack routes
   routeLeft = attackRoute("Left Attack Route",cbAttackGather, cbRouteLeft1, cbRouteLeft2);
   routeCenter = attackRoute("Center Attack Route",cbAttackGather, cbRouteCenter1, cbRouteCenter2);
   routeRight = attackRoute("Right Attack Route",cbAttackGather, cbRouteRight1, cbRouteRight2);

   // Initialize the target queries
   queryP1Units = kbUnitQueryCreate("Player 1 Units");
   configQuery(queryP1Units, cUnitTypeUnit, -1, cUnitStateAlive, 1);

   queryP1Buildings = kbUnitQueryCreate("Player 1 Buildings");
   configQuery(queryP1Buildings, cUnitTypeBuilding, -1, cUnitStateAliveOrBuilding, 1);

   queryP1Farms = kbUnitQueryCreate("Player 1 Farms");
   configQuery(queryP1Farms, cUnitTypeFarm, -1, cUnitStateAliveOrBuilding, 1);

   queryP1Monuments = kbUnitQueryCreate("Player 1 Monuments");
   configQuery(queryP1Monuments, cUnitTypeAbstractMonument, -1, cUnitStateAliveOrBuilding, 1);

   queryP1Citadels = kbUnitQueryCreate("Player 1 Citadels");
   configQuery(queryP1Citadels, cUnitTypeCitadelCenter, -1, cUnitStateAliveOrBuilding, 1);




   aiEcho("Difficulty = "+aiGetWorldDifficulty());
}







// *****************************************************************************
//
// RULES
//
// *****************************************************************************

rule scout
   inactive
   minInterval 5
{
   // just set up an explore plan
   int exploreID = aiPlanCreate("Explore", cPlanExplore);
   if(exploreID >= 0)
   {
      aiPlanSetVariableFloat( exploreID, cExplorePlanLOSMultiplier,  0, 4.0 );
      aiPlanAddUnitType(exploreID, cUnitTypeSpearman, 1, 1, 1);
      aiPlanSetActive(exploreID);
   }
   xsDisableSelf();
}

rule goToAge2
   inactive
   minInterval 10
{
   if ( xsGetTime() < age2Time)
      return;
   researchTech(cTechAge2Ptah);
   xsDisableSelf();
}



rule goToAge3
   inactive
   mininterval 20
{
   if ( xsGetTime() < age3Time )
      return;
   researchTech(cTechAge3Hathor);
   xsDisableSelf();
}


rule goToAge4
   inactive
   mininterval 20
{
   if ( xsGetTime() < age4Time )
      return;
   researchTech(cTechAge4Osiris);
   xsDisableSelf();
}



rule getAge2UnitUpgrades
   inactive
   minInterval 20
{
   if ( xsGetTime() < (age2Time + age2Time + age3Time)/3 )
      return;     // Wait till 1/3 to age3
   researchTech(cTechMediumAxemen);
   researchTech(cTechMediumSlingers);
   researchTech(cTechMediumSpearmen);
   xsDisableSelf();
}

rule getAge2ArmoryUpgrades
   inactive
   minInterval 20
{
   if ( xsGetTime() < (age2Time + age3Time + age3Time)/3 )
      return;     // Wait till 2/3 to age3
   aiEcho("Getting age 2 armory upgrades");
   researchTech(cTechCopperWeapons);
   researchTech(cTechCopperMail);
   researchTech(cTechCopperShields);
   xsDisableSelf();
}

rule getAge3UnitUpgrades
   inactive
   minInterval 20
{
   if ( xsGetTime() < (age3Time+180000) )
      return;
   researchTech(cTechHeavyAxemen);
   researchTech(cTechHeavySlingers);
   researchTech(cTechHeavySpearmen);
   researchTech(cTechHeavyChariots);
   researchTech(cTechHeavyElephants);
   researchTech(cTechHeavyCamelry);
   xsDisableSelf();
}

rule getAge3ArmoryUpgrades
   inactive
   minInterval 20
{
   if ( xsGetTime() < (age3Time+300000) )
      return;
   researchTech(cTechBronzeWeapons);
   researchTech(cTechBronzeMail);
   researchTech(cTechBronzeShields);
   xsDisableSelf();
}

rule getAge4UnitUpgrades
   inactive
   minInterval 20
{
   if ( xsGetTime() < (age4Time+300000) )
      return;
   researchTech(cTechChampionAxemen);
   researchTech(cTechChampionSlingers);
   researchTech(cTechChampionSpearmen);
   researchTech(cTechChampionChariots);
   researchTech(cTechChampionElephants);
   researchTech(cTechChampionCamelry);
   xsDisableSelf();
}

rule getAge4ArmoryUpgrades
   inactive
   minInterval 20
{
   if ( xsGetTime() < (age4Time+600000) )
      return;
   researchTech(cTechIronWeapons);
   researchTech(cTechIronMail);
   researchTech(cTechIronShields);
   xsDisableSelf();
}



rule attackGenerator
   minInterval 10
   inactive
{
   //aiEcho("attack check running, next time is "+nextAttackTime);
   if ( xsGetTime() < nextAttackTime )
      return;

   attack(attackSize);
   nextAttackTime = xsGetTime() + attackInterval;
   attackSize = attackSize + attackSizeIncrement;
   if (attackSize > maxAttackSize)
      attackSize = maxAttackSize;
   aiEcho("Next attack size will be "+attackSize+".");
}

// Checks to see if a large P1 army is near the prison, if so, set off a swarm attack.
rule armyNearPrison
   inactive
   minInterval 15
{
   static int queryPrison=-1; // Used for unit count and targeting.
   int   count=0;
   int   attackID=-1;
   
   if (queryPrison < 0)
   {
      // Create query
      queryPrison = kbUnitQueryCreate("Prison query");
      if ( configQuery(queryPrison, cUnitTypeUnit, -1, cUnitStateAlive, 1, kbGetBlockPosition(cbNearPrison), false, 50) == false)
         return;
   }

   // Check for enemies
   kbUnitQueryResetResults(queryPrison);
   count = kbUnitQueryExecute(queryPrison);

   if (count < 6)
      return;



   // Rally units to defend...all units within 100 meters, to a maximum of 30 units
   aiEcho("Outer wall emergency.");
   attackID = aiPlanCreate("Prison Attack", cPlanAttack);
   count = getUnassignedUnitCount(kbGetBlockPosition(cbAttackGather), 100.0, 2, cUnitTypeMilitary);
   if (count > 30)
      count = 30;
   
   if (attackID < 0)
      return;

   if (aiPlanSetVariableInt(attackID, cAttackPlanPlayerID, 0, 1) == false)
      return;

   aiPlanSetNumberVariableValues(attackID, cAttackPlanQueryID, 2);
   aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 0, queryPrison);     // Attack units near prison, then
   aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 1, queryP1Units);    // Attack any units

   aiPlanSetVariableVector(attackID, cAttackPlanGatherPoint, 0, kbGetBlockPosition(cbAttackGather));
   aiPlanSetVariableFloat(attackID, cAttackPlanGatherDistance, 0, 10.0);

   aiPlanAddUnitType(attackID, cUnitTypeMilitary, 1, count, count );

   aiEcho("Responding with "+count+" units.");
   aiPlanSetInitialPosition(attackID, kbGetBlockPosition(cbAttackGather));
   aiPlanSetRequiresAllNeedUnits(attackID, true);
   aiPlanSetActive(attackID);
   xsDisableSelf();
}

// Once the Migdol is destroyed, bring everyone inside the base so that HP can sneak around to prison
rule migdolLost /* GTS added */
   active
   minInterval 15
{
   static int queryMigdol=-1; // Used for unit count and targeting.
   
   if (queryMigdol < 0)
   {
      // Create query
      queryMigdol = kbUnitQueryCreate("Query to see if P2 Migdol alive");
      if (configQuery(queryMigdol, cUnitTypeMigdolStronghold, -1, cUnitStateAlive, 2) == false)
         return;
   }

   kbUnitQueryResetResults(queryMigdol);
   int targetCount = kbUnitQueryExecute(queryMigdol);

   if (targetCount < 1)
      return;
   
   aiEcho("Migdol lost. Move to center of town");

	int defendPlan1ID=aiPlanCreate("Migdol defense", cPlanDefend);
   if (defendPlan1ID >= 0)
   {
      migdolDefense=kbGetBlockPosition(cbNearMigdol);
		
      // Add the unit(s).
      aiPlanAddUnitType(defendPlan1ID, cUnitTypeMilitary, 10, 100, 100);
				
      // Setup the vars.
      aiPlanSetDesiredPriority(defendPlan1ID, 70);
      aiPlanSetVariableVector(defendPlan1ID, cDefendPlanDefendPoint, 0, migdolDefense);
      aiPlanSetVariableFloat(defendPlan1ID, cDefendPlanEngageRange, 0, 20);
      aiPlanSetActive(defendPlan1ID);
	}
}



rule useRain
   minInterval 10
   inactive
{
   if (xsGetTime() < startTime+300)
      return;

   if (aiRandInt(10) > 0)
      return;

   aiCastGodPowerAtPosition(cTechRain, kbGetTownLocation());
   aiEcho("Casting rain at "+timeString()+".");
   xsDisableSelf();
}





rule useLocustSwarm // Look for 6 farms near my army
   minInterval 5
   inactive
{
   int targetUnit = -1;
   int attackArmyID = -1;

   if (lastAttackPlan < 0)
      return;
   vector pVec = aiPlanGetLocation(lastAttackPlan);
   if (xsVectorGetX(pVec)<0)
      return;

   static int tempQuery = -1;
   if (tempQuery < 0)
   {  // Doesn't exist, set it up
      tempQuery = kbUnitQueryCreate("useLocustSwarm");

      if ( configQuery(tempQuery, cUnitTypeFarm, -1, cUnitStateAliveOrBuilding, 1, pVec, true, 50) == false)
         return;
   }
   else
      kbUnitQuerySetPosition(tempQuery, pVec); // Because pVec changes as army moves

   kbUnitQueryResetResults(tempQuery);
   int targetCount = kbUnitQueryExecute(tempQuery);  

   if (targetCount < 6)
      return;
   targetUnit = kbUnitQueryGetResult(tempQuery, targetCount/2);  // grab middle farm

   // confirm LOS
   if ( kbUnitVisible(targetUnit) != true )
   {
      aiEcho("Don't have LOS for locust swarm on unit "+targetUnit);
      return;
   }

   aiEcho("Using Locust Swarm");
   if ( aiCastGodPowerAtPosition(cTechLocustSwarm, kbUnitGetPosition(targetUnit)) == true)
      xsDisableSelf();
   else 
      aiEcho("Locust swarm failed at "+kbUnitGetPosition(targetUnit));
}




