home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2003 January / maximum-cd-2003-01.iso / Software / Games / AoM / mtrial.exe / AOM / AI / SCN21P2.XS < prev    next >
Encoding:
Text File  |  2002-09-23  |  47.5 KB  |  1,462 lines

  1. //==============================================================================
  2. // Scn21p2: AI Scenario Script for scenario 21 player 2 
  3. //==============================================================================
  4. /*
  5.    AI owner:  Dave Leary
  6.    Scenario owner: Dave Leary
  7.  
  8.     The AI for the red enemy in scenario 21.  Produces fairly balanced mixed
  9.     Egyptian groups that choose randomly between three attack routes (left, right,
  10.     and down the middle).
  11.     
  12.     Cheats for resources. Performs researches over time. Uses god powers - Vision at the
  13.     start of the game, Serpents near the Tamarisk tree when the player gets several
  14.     villagers close to it, and Tornado during a late-game attack on the player's
  15.     town.
  16.  
  17.                                 *** DIFFICULTY LEVEL NOTES ***
  18.  
  19.    Easy level - Extra towers.  Fewer researches and AI does not advance to Age 4.
  20.     Elephants and scarabs not included in attack groups.  Since AI does not advance 
  21.     to Age 4,  AI never uses Tornado.  Slower initial attacks.
  22.  
  23.    Moderate level - base level.
  24.  
  25.    Difficult level - fewer sheep close to player town.  AI defends the tree with
  26.     anubites/priests when the damage is at 25%.
  27.  
  28.    Nightmare - Two enemy towers at the Tamarisk Tree.  Less gold and trees on the
  29.     map, especially near player starting town.  Southern gold mine is guarded by a
  30.     tower and two scorpion men.  One less unclaimed settlement.  AI defends the tree
  31.     with anubites/priests when the damage is at 2%.  Anubites start with Feet of the
  32.     Jackal.
  33. */
  34.  
  35. // Difficulty Level check predeclared.
  36. int difflevel=-1;
  37.  
  38. int treethreat=-1;
  39.  
  40. // Variable for main base.
  41. int gMainBaseID=-1;
  42.  
  43. // Initial gather percentages
  44. float totalFoodGathererPercentage  = 0.8;
  45. float totalWoodGathererPercentage  = 0.0;
  46. float totalGoldGathererPercentage  = 0.2;
  47. float totalFavorGathererPercentage = 0.0;
  48.  
  49. //==============================================================================
  50. // Set Town Location
  51. //==============================================================================
  52. void setTownLocation(void)
  53. {
  54.    //Look for the "Town Location" marker.
  55.    kbSetTownLocation(kbGetBlockPosition("924"));
  56. }
  57.  
  58. //==============================================================================
  59. // miscStartup
  60. //==============================================================================
  61. void miscStartup(void)
  62. {
  63.     // Get the difficulty level.
  64.     difflevel=aiGetWorldDifficulty();
  65.  
  66.    //Startup message(s).
  67.    aiEcho("");
  68.    aiEcho("");
  69.    aiEcho("Scn21P2 AI Start, filename='"+cFilename+"'.");
  70.    //Spit out the map size.
  71.    aiEcho("Map size is ("+kbGetMapXSize()+", "+kbGetMapZSize()+").");
  72.     aiEcho("Difficulty Level="+difflevel+".");
  73.  
  74.    //Cheat like a bastard.  Once only, though.
  75.    kbLookAtAllUnitsOnMap();
  76.    //Calculate some areas.
  77.    kbAreaCalculate(1200.0);
  78.    //Set our town location.
  79.    setTownLocation();
  80.     //Reset random seed
  81.     aiRandSetSeed();
  82. }
  83.  
  84.  
  85. //==============================================================================
  86. // initEcon
  87. //
  88. // Set Up the initial Economy.
  89. //==============================================================================
  90. void initEcon()
  91. {
  92.    aiEcho("Economy Init.");
  93.  
  94.     /* Don't need this for what we're doing here.
  95.    // Set our update resource handler.
  96.    aiSetUpdateResourceEventHandler("updateResourceHandler");
  97.     */
  98.  
  99.    //-- Setup AI Cost weights.
  100.    kbSetAICostWeight(cResourceFood, 1.0);
  101.    kbSetAICostWeight(cResourceWood, 0.5);
  102.    kbSetAICostWeight(cResourceGold, 1.0);
  103.    kbSetAICostWeight(cResourceFavor, 10.0);
  104.  
  105.    //-- Dont auto gather favor
  106.    //totalFavorGathererPercentage = 0;
  107.  
  108.    //-- Set initial gatherer percentages.
  109.    aiSetResourceGathererPercentage(cResourceFood, totalFoodGathererPercentage, false, cRGPScript);
  110.    aiSetResourceGathererPercentage(cResourceWood, totalWoodGathererPercentage, false, cRGPScript);
  111.    aiSetResourceGathererPercentage(cResourceGold, totalGoldGathererPercentage, false, cRGPScript);
  112.    aiSetResourceGathererPercentage(cResourceFavor, totalFavorGathererPercentage, false, cRGPScript);
  113.    aiNormalizeResourceGathererPercentages(cRGPScript);
  114.  
  115.    aiSetResourceGathererPercentageWeight(cRGPScript, 1);
  116.    aiSetResourceGathererPercentageWeight(cRGPCost, 0);
  117.  
  118.    //-- Set up the initial resource subtype breakdowns - all farming, all the time.
  119.     // aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeEasy, 1, 50, 0.0, gMainBaseID);
  120.     // aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeHunt, 1, 50, 0.1, gMainBaseID);
  121.     aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeFarm, 1, 50, 1.0, gMainBaseID);
  122.     aiSetResourceBreakdown(cResourceWood, cAIResourceSubTypeEasy, 1, 50, 1.0, gMainBaseID);
  123.     aiSetResourceBreakdown(cResourceGold, cAIResourceSubTypeEasy, 1, 50, 1.0, gMainBaseID);
  124.    aiSetResourceBreakdown(cResourceFavor, cAIResourceSubTypeEasy, 1, 50, 1.0, gMainBaseID);
  125.     
  126.    //-- Set up auto-gather escrows
  127.    aiSetAutoGatherEscrowID(cRootEscrowID);
  128.    aiSetAutoFarmEscrowID(cRootEscrowID);
  129.  
  130.     //Allocate all resources to the root escrow by setting percentage of military/economy to 0.
  131.     kbEscrowSetPercentage( cEconomyEscrowID, cAllResources, 0.0 );
  132.     kbEscrowSetPercentage( cMilitaryEscrowID, cAllResources, 0.0 );
  133.  
  134.     //Allocate all resources
  135.    kbEscrowAllocateCurrentResources();
  136. }
  137.  
  138. //==============================================================================
  139. //==============================================================================
  140. // Attack stuff.
  141. //==============================================================================
  142. //==============================================================================
  143. //Shared variables.
  144. int numberAttacks=0;
  145. int attackPlayerID=-1;
  146.  
  147. //TODO: Decide how to rep attack group size.
  148. int attackMinimumGroupSize=4;
  149. int attackMaximumGroupSize=6;
  150.  
  151. //Attack 1 vars.
  152. int attackPlan1ID=-1;
  153. int maintainPlan1ID=-1;
  154.  
  155. //Attack 2 vars.
  156. int attackPlan2ID=-1;
  157. int maintainPlan2ID=-1;
  158.  
  159. //Defend Plan variable.
  160. int defendPlan1ID=-1;
  161.  
  162. // Route and path vars.
  163. int attackRoute1ID=-1;
  164. int attackPath1ID=-1;
  165. int attackRoute2ID=-1;
  166. int attackPath2ID=-1;
  167. int attackRoute3ID=-1;
  168. int attackPath3ID=-1;
  169. int attackRoute4ID=-1;
  170. int attackPath4ID=-1;
  171.  
  172. // Unit types
  173. int attackerUnitTypeID1=cUnitTypeSpearman;
  174. int attackerUnitTypeID2=cUnitTypeAxeman;
  175. int attackerUnitTypeID3=cUnitTypeSlinger;
  176.  
  177. int attackerUnitTypeID4=cUnitTypeWarElephant;
  178. int attackerUnitTypeID5=cUnitTypeCamelry;
  179. int attackerUnitTypeID6=cUnitTypeChariotArcher;
  180. int attackerUnitTypeID7=cUnitTypeSiegeTower;
  181.  
  182. int attackerUnitTypeID8=cUnitTypeAnubite;
  183. int attackerUnitTypeID9=cUnitTypeScarab;
  184. int attackerUnitTypeID10=cUnitTypeAvenger;
  185. int attackerUnitTypeID11=cUnitTypePriest;
  186. int randomUpgradeLine=-1;
  187.  
  188. // Quicky function to set up a random upgrade line.
  189. void evaluateRandomUpgrade( void )
  190. {
  191.     int whichOne=aiRandInt(2);
  192.     randomUpgradeLine=whichOne;
  193. }
  194.  
  195. //=========================================================================================
  196. // Kidd's cool configQuery function: used to create attack routes, etc.  Oooh, lovin' that!
  197. //=========================================================================================
  198. bool configQuery( int queryID = -1, int unitType = -1, int action = -1, int state = -1, int player = -1, vector center = vector(-1,-1,-1), bool sort = false, float radius = -1 )
  199. {
  200.    if ( queryID == -1)
  201.    {
  202.       return(false);
  203.    }
  204.  
  205.    if (player != -1)
  206.       kbUnitQuerySetPlayerID(queryID, player);
  207.    
  208.    if (unitType != -1)
  209.       kbUnitQuerySetUnitType(queryID, unitType);
  210.  
  211.    if (action != -1)
  212.       kbUnitQuerySetActionType(queryID, action);
  213.  
  214.    if (state != -1)
  215.       kbUnitQuerySetState(queryID, state);
  216.  
  217.    if (center != vector(-1,-1,-1))
  218.    {
  219.       kbUnitQuerySetPosition(queryID, center);
  220.       if (sort == true)
  221.          kbUnitQuerySetAscendingSort(queryID, true);
  222.       if (radius != -1)
  223.          kbUnitQuerySetMaximumDistance(queryID, radius);
  224.    }
  225.    return(true);
  226. }
  227.  
  228. //==============================================================================
  229. // Queryin' for villagers near the Tamarisk Tree
  230. //==============================================================================
  231. int checkForVillagers(void)
  232. {
  233.     static int villagerQueryID=-1;
  234.     vector tamariskTree=kbGetBlockPosition("934");
  235.     int villagerCount=-1;
  236.  
  237.    if (villagerQueryID < 0)
  238.    {  
  239.         // Doesn't exist, set it up
  240.       villagerQueryID = kbUnitQueryCreate("Villager Query");
  241.         
  242.       // Get the number
  243.       if ( configQuery( villagerQueryID, cUnitTypeVillagerEgyptian, -1, cUnitStateAlive, 1, tamariskTree, false, 30 ) == false )
  244.          return(-1);
  245.    }
  246.  
  247.    kbUnitQueryResetResults(villagerQueryID);
  248.    villagerCount = kbUnitQueryExecute(villagerQueryID);
  249.     return(villagerCount);
  250. }
  251.  
  252.  
  253. //==============================================================================
  254. // Queryin' for "my" units near the enemy's town - for tornado action.
  255. //==============================================================================
  256. int checkForTornado(void)
  257. {
  258.     static int soldierQueryID=-1;
  259.     vector enemyTown=kbGetBlockPosition("942");
  260.     int soldierCount=-1;
  261.  
  262.    if (soldierQueryID < 0)
  263.    {  
  264.         // Doesn't exist, set it up
  265.       soldierQueryID = kbUnitQueryCreate("Soldier Query");
  266.         
  267.       // Get the number
  268.       if ( configQuery( soldierQueryID, cUnitTypeUnit, -1, cUnitStateAlive, 2, enemyTown, false, 30 ) == false )
  269.          return(-1);
  270.    }
  271.  
  272.    kbUnitQueryResetResults(soldierQueryID);
  273.    soldierCount = kbUnitQueryExecute(soldierQueryID);
  274.     return(soldierCount);
  275. }
  276.  
  277. //==============================================================================
  278. // initAttack: Creates attack routes, etc.
  279. //==============================================================================
  280. void initAttack(int playerID=-1)
  281. {
  282.    //Destroy all previous attacks (if this isn't the player we're already attacking.
  283.    if (playerID != attackPlayerID)
  284.    {
  285.       //Reset the attack player ID.
  286.       attackPlayerID=-1;
  287.       //Destroy any previous attack plan.
  288.       aiPlanDestroy(attackPlan1ID);
  289.       attackPlan1ID=-1;
  290.       aiPlanDestroy(attackPlan2ID);
  291.       attackPlan2ID=-1;
  292.   
  293.       //Destroy our previous attack paths.
  294.       kbPathDestroy(attackPath1ID);
  295.       attackPath1ID=-1;
  296.       kbPathDestroy(attackPath2ID);
  297.       attackPath2ID=-1;
  298.       kbPathDestroy(attackPath3ID);
  299.       attackPath3ID=-1;
  300.       kbPathDestroy(attackPath4ID);
  301.       attackPath4ID=-1;
  302.  
  303.  
  304.       //Destroy our previous attack routes.
  305.       attackRoute1ID=-1;
  306.       attackRoute2ID=-1;
  307.         attackRoute3ID=-1;
  308.         attackRoute4ID=-1;
  309.  
  310.       //Reset the number of attacks.
  311.       numberAttacks=0;
  312.    }
  313.  
  314.    //Save the player to attack.
  315.    attackPlayerID=playerID;
  316.  
  317.    vector gatherPointLeft=kbGetBlockPosition("925");
  318.     vector gatherPointMid=kbGetBlockPosition("924");
  319.     vector gatherPointRight=kbGetBlockPosition("937");
  320.        
  321.     //Setup attack path 1 - go left
  322.    attackPath1ID=kbPathCreate("Attack Path 1");
  323.    kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("926"));
  324.     kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("927"));
  325.     kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("928"));
  326.     kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("929"));
  327.     kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("930"));
  328.     kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("931"));
  329.    //Create attack route 1.
  330.    attackRoute1ID=kbCreateAttackRouteWithPath("Attack Route 1", gatherPointLeft, kbGetBlockPosition("932"));
  331.    
  332.     if (attackRoute1ID >= 0)
  333.       kbAttackRouteAddPath(attackRoute1ID, attackPath1ID);
  334.  
  335.    //Setup attack path 2 - go down the middle 
  336.    attackPath2ID=kbPathCreate("Attack Path 2");
  337.     kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("933"));
  338.    kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("934"));
  339.     kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("935"));
  340.     kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("936"));
  341.     //Create attack route 2.
  342.    attackRoute2ID=kbCreateAttackRouteWithPath("Attack Route 2", gatherPointMid, kbGetBlockPosition("932"));
  343.    
  344.     if (attackRoute2ID >= 0)
  345.       kbAttackRouteAddPath(attackRoute2ID, attackPath2ID);
  346.  
  347.     //Setup attack path 3 - go right
  348.    attackPath3ID=kbPathCreate("Attack Path 3");
  349.    kbPathAddWaypoint(attackPath3ID, kbGetBlockPosition("938"));
  350.     kbPathAddWaypoint(attackPath3ID, kbGetBlockPosition("939"));
  351.     kbPathAddWaypoint(attackPath3ID, kbGetBlockPosition("940"));
  352.     //Create attack route 3.
  353.    attackRoute3ID=kbCreateAttackRouteWithPath("Attack Route 3", gatherPointRight, kbGetBlockPosition("941"));
  354.  
  355.       //Setup attack path 4 - shortest route to Tamarisk tree.
  356.    attackPath4ID=kbPathCreate("Attack Path 4");
  357.    kbPathAddWaypoint(attackPath4ID, kbGetBlockPosition("933"));
  358.     
  359.     //Create attack route 4.
  360.    attackRoute4ID=kbCreateAttackRouteWithPath("Attack Route 3", gatherPointMid, kbGetBlockPosition("934"));
  361.  
  362.     if (attackRoute3ID >= 0)
  363.       kbAttackRouteAddPath(attackRoute3ID, attackPath3ID);
  364. }
  365.  
  366. //==============================================================================
  367. // setupBaseAttack - the primary attack setup.
  368. // Prioritizes enemy units instead of buildings.
  369. //==============================================================================
  370. bool setupBaseAttack(int playerID=-1)
  371. {
  372.     int randomPath=aiRandInt(3);
  373.     int randomCoreUnit=aiRandInt(2);
  374.     int randomAnubite=aiRandInt(3);
  375.  
  376.    // If we have enough unassigned military units of either core type, create a new attack plan.
  377.    int numberAvailableUnits1=aiNumberUnassignedUnits(attackerUnitTypeID1);
  378.     int numberAvailableUnits2=aiNumberUnassignedUnits(attackerUnitTypeID2);
  379.    
  380.     // Bail if things ain't looking right, to avoid nasty idle attack plans.
  381.     if ( randomCoreUnit == 0 )
  382.     {
  383.         aiEcho("There are "+numberAvailableUnits1+" spearmen available for a new attack.");
  384.         if (numberAvailableUnits1 < attackMinimumGroupSize)
  385.             return( false );
  386.     }
  387.     else
  388.     {
  389.         aiEcho("There are "+numberAvailableUnits2+" axemen available for a new attack.");
  390.         if (numberAvailableUnits2 < attackMinimumGroupSize)
  391.             return( false );
  392.     }
  393.        
  394.     //Info.
  395.     aiEcho("Attacking Player "+playerID+".");
  396.  
  397.    //If the player to attack doesn't match, init the attack.
  398.    if (attackPlayerID != playerID)
  399.    {
  400.       initAttack(playerID);
  401.       if (attackPlayerID < 0)
  402.          return(false);
  403.    }
  404.  
  405.    //Create an attack plan.
  406.    int newAttackPlanID=aiPlanCreate("Attack Player"+attackPlayerID+" Attempt"+numberAttacks, cPlanAttack);
  407.    if (newAttackPlanID < 0)
  408.       return(false);
  409.  
  410.    //Target player (required).  This must work.
  411.    if (aiPlanSetVariableInt(newAttackPlanID, cAttackPlanPlayerID, 0, attackPlayerID) == false)
  412.       return(false);
  413.  
  414.    //Gather point.
  415.    vector gatherPointLeft=kbGetBlockPosition("925");
  416.     vector gatherPointMid=kbGetBlockPosition("933");
  417.     vector gatherPointRight=kbGetBlockPosition("937");
  418.  
  419.     //Set the target type.  This must work.
  420.    if (aiPlanSetNumberVariableValues(newAttackPlanID, cAttackPlanTargetTypeID, 2, true) == false)
  421.       return(false);
  422.  
  423.    //Unit types to attack.
  424.    aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 0, cUnitTypeUnit);
  425.     aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 1, cUnitTypeBuilding);
  426.  
  427.    switch(randomPath)
  428.    {
  429.        case 0:
  430.       {
  431.             aiEcho("Next attack going left.");
  432.             aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute1ID);
  433.             aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPointLeft);
  434.            aiPlanSetInitialPosition(newAttackPlanID, gatherPointLeft);
  435.          break;
  436.       }
  437.         case 1:
  438.       {
  439.             aiEcho("Next attack going down the middle.");
  440.             aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute2ID);
  441.             aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPointMid);
  442.             aiPlanSetInitialPosition(newAttackPlanID, gatherPointMid);
  443.          break;
  444.       }
  445.         case 2:
  446.       {
  447.             aiEcho("Next attack going right.");
  448.             aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute3ID);
  449.             aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPointRight);
  450.             aiPlanSetInitialPosition(newAttackPlanID, gatherPointRight);
  451.          break;
  452.       }
  453.     }
  454.  
  455.    //Set the gather point distance
  456.    aiPlanSetVariableFloat(newAttackPlanID, cAttackPlanGatherDistance, 0, 30.0);
  457.  
  458.    //Set up the attack route usage pattern
  459.    aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRoutePattern, 0, cAttackPlanAttackRoutePatternRandom);
  460.    
  461.     //Add the unit types to the plan - first, the "core unit."
  462.     if ( randomCoreUnit == 0 )
  463.     {
  464.         aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID1, attackMinimumGroupSize, attackMaximumGroupSize, attackMaximumGroupSize);
  465.     }
  466.     else
  467.     {
  468.         aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID2, attackMinimumGroupSize, attackMaximumGroupSize, attackMaximumGroupSize);
  469.     }
  470.  
  471.     // Next, slingers.
  472.     int slingerMin=attackMinimumGroupSize-1;
  473.     int slingerMax=attackMaximumGroupSize-2;
  474.     aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID3, slingerMin, slingerMax, slingerMax);
  475.  
  476.     // Finally, a possible random anubite (about a third of the time).
  477.     if ( randomAnubite == 0 )
  478.     {
  479.         aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID8, 0, 1, 1);
  480.     }
  481.     
  482.    //Plan requires all need units to work (can be false)
  483.    aiPlanSetRequiresAllNeedUnits(newAttackPlanID, true);
  484.    //Activate the plan.
  485.    aiPlanSetActive(newAttackPlanID);
  486.  
  487.    //Now, save the attack plan ID appropriately
  488.    aiPlanSetOrphan(attackPlan1ID, true);
  489.    attackPlan1ID=newAttackPlanID;
  490.  
  491.    //Increment our overall number of attacks
  492.    numberAttacks++;
  493. }
  494.  
  495.  
  496. //==============================================================================
  497. // setupLargeAttack - a larger and more dangerous attack, sent more rarely.
  498. //
  499. // This attack scoops up whatever's sitting around (within reason) and sends it
  500. // in.  The only attack to include siege stuff and priests.
  501. //==============================================================================
  502. bool setupLargeAttack(int playerID=-1)
  503. {
  504.     // Get the difficulty level.
  505.     difflevel=aiGetWorldDifficulty();
  506.  
  507.     int randomPath=aiRandInt(3);
  508.     int randomElephants=aiRandInt(4)+1;
  509.     int numberAvailableUnits=aiNumberUnassignedUnits(attackerUnitTypeID3);
  510.  
  511.     // If the "tree is threatened" always go down the middle.
  512.     if ( treethreat == 1 )
  513.     {
  514.         randomPath = 1;
  515.     }
  516.  
  517.     if ( difflevel == 3 )
  518.     {
  519.         randomElephants = 8;
  520.     }
  521.     
  522.     //Info.
  523.     aiEcho("Attacking Player "+playerID+".");
  524.  
  525.    //If the player to attack doesn't match, init the attack.
  526.    if (attackPlayerID != playerID)
  527.    {
  528.       initAttack(playerID);
  529.       if (attackPlayerID < 0)
  530.          return(false);
  531.    }
  532.     
  533.     // Bail if things ain't looking right, to avoid nasty idle attack plans.
  534.     aiEcho("There are "+numberAvailableUnits+" slingers available for the large attack.");
  535.     if (numberAvailableUnits < 1)
  536.         return( false );
  537.  
  538.    //Create an attack plan.
  539.    int newAttackPlanID=aiPlanCreate("Attack Player"+attackPlayerID+" Attempt"+numberAttacks, cPlanAttack);
  540.    if (newAttackPlanID < 0)
  541.       return(false);
  542.  
  543.    //Target player (required).  This must work.
  544.    if (aiPlanSetVariableInt(newAttackPlanID, cAttackPlanPlayerID, 0, attackPlayerID) == false)
  545.       return(false);
  546.  
  547.    //Gather point.
  548.    vector gatherPointLeft=kbGetBlockPosition("925");
  549.     vector gatherPointMid=kbGetBlockPosition("933");
  550.     vector gatherPointRight=kbGetBlockPosition("937");
  551.  
  552.     //Set the target type.  This must work.
  553.    if (aiPlanSetNumberVariableValues(newAttackPlanID, cAttackPlanTargetTypeID, 2, true) == false)
  554.       return(false);
  555.  
  556.    //Unit types to attack.
  557.    aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 0, cUnitTypeBuilding);
  558.     aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 1, cUnitTypeUnit);
  559.  
  560.    switch(randomPath)
  561.    {
  562.        case 0:
  563.       {
  564.             aiEcho("Large attack going left.");
  565.             aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute1ID);
  566.             aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPointLeft);
  567.            aiPlanSetInitialPosition(newAttackPlanID, gatherPointLeft);
  568.          break;
  569.       }
  570.         case 1:
  571.       {
  572.             aiEcho("Large attack going down the middle.");
  573.             aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute2ID);
  574.             aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPointMid);
  575.             aiPlanSetInitialPosition(newAttackPlanID, gatherPointMid);
  576.          break;
  577.       }
  578.         case 2:
  579.       {
  580.             aiEcho("Large attack going right.");
  581.             aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute3ID);
  582.             aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPointRight);
  583.             aiPlanSetInitialPosition(newAttackPlanID, gatherPointRight);
  584.          break;
  585.       }
  586.     }
  587.  
  588.    //Set the gather point distance
  589.    aiPlanSetVariableFloat(newAttackPlanID, cAttackPlanGatherDistance, 0, 30.0);
  590.  
  591.    //Set up the attack route usage pattern
  592.    aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRoutePattern, 0, cAttackPlanAttackRoutePatternRandom);
  593.    
  594.     // Base guys - scoop up whatever we have
  595.     aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID1, 0, attackMaximumGroupSize, attackMaximumGroupSize);
  596.     aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID2, 0, attackMaximumGroupSize, attackMaximumGroupSize);
  597.  
  598.     // At least one skirmisher
  599.     aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID3, 1, 2, 2);
  600.  
  601.     // Elephants, of course - a random number of 'em unless it's easy level.  On Titan, this can be up to 8.
  602.     if ( difflevel >= 1 )
  603.     {
  604.         aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID4, 0, randomElephants, randomElephants);
  605.     }
  606.     
  607.     // Siege if we have it
  608.     if ( difflevel >= 1 )
  609.     {
  610.         aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID7, 0, 2, 4);
  611.     }
  612.     else
  613.     {
  614.         aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID7, 0, 1, 1);
  615.     }
  616.  
  617.     if ( difflevel >= 1 )
  618.     {
  619.         aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID9, 0, 2, 2);
  620.     }
  621.  
  622.     // Priests for healing and such
  623.     aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID11, 0, 2, 2);
  624.     
  625.    //Plan requires all need units to work (can be false)
  626.    aiPlanSetRequiresAllNeedUnits(newAttackPlanID, true);
  627.  
  628.    //Activate the plan.
  629.    aiPlanSetActive(newAttackPlanID);
  630.  
  631.    //Now, save the attack plan ID appropriately
  632.    aiPlanSetOrphan(attackPlan2ID, true);
  633.    attackPlan2ID=newAttackPlanID;
  634.  
  635.    //Increment our overall number of attacks
  636.    numberAttacks++;
  637. }
  638.  
  639. //==============================================================================
  640. // Attack Generator 1 - Base attack, every two minutes, once activated.
  641. //==============================================================================
  642. rule attackGenerator1
  643.    minInterval 120
  644.    inactive
  645.    group AttackRules
  646.    runImmediately
  647. {
  648.    // See how many "idle" attack plans we have.  Don't create any more if we have
  649.    // idle plans.
  650.    int numberIdleAttackPlans=aiGetNumberIdlePlans(cPlanAttack);
  651.  
  652.    if (numberIdleAttackPlans > 0)
  653.       return;
  654.  
  655.     setupBaseAttack(1);
  656. }
  657.  
  658.  
  659. //==============================================================================
  660. // Attack Generator 1 Easy - Base attack, a little slower and starts later.
  661. //==============================================================================
  662. rule attackGenerator1Easy
  663.    minInterval 180
  664.    inactive
  665.    group AttackRules
  666. {
  667.    // See how many "idle" attack plans we have.  Don't create any more if we have
  668.    // idle plans.
  669.    int numberIdleAttackPlans=aiGetNumberIdlePlans(cPlanAttack);
  670.  
  671.    if (numberIdleAttackPlans > 0)
  672.       return;
  673.  
  674.     setupBaseAttack(1);
  675. }
  676.  
  677. //==============================================================================
  678. // Attack Generator 2 - Crazy large attack
  679. // Do it randomly, 2/3rds of the time, every three and a half minutes
  680. // Doesn't start until about 10-12 minutes into the game.
  681. //==============================================================================
  682. rule attackGenerator2
  683.    minInterval 210
  684.    inactive
  685.    group AttackRules
  686. {
  687.     int attackNow=aiRandInt(3);
  688.    
  689.     if ( attackNow < 2 )
  690.     {
  691.         // See how many "idle" attack plans we have.  Don't create any more if we have
  692.         // idle plans.
  693.         int numberIdleAttackPlans=aiGetNumberIdlePlans(cPlanAttack);
  694.  
  695.         if (numberIdleAttackPlans > 0)
  696.             return;
  697.  
  698.         setupLargeAttack(1);
  699.     }
  700. }
  701.  
  702. //==============================================================================
  703. // Attack Generator 2 Easy - Crazy large attack, easy version.  Slows down things
  704. // a little, and reduces probability of a large attack going.
  705. //==============================================================================
  706. rule attackGenerator2Easy
  707.    minInterval 240
  708.    inactive
  709.    group AttackRules
  710. {
  711.     int attackNow=aiRandInt(4);
  712.    
  713.     if ( attackNow < 2 )
  714.     {
  715.         // See how many "idle" attack plans we have.  Don't create any more if we have
  716.         // idle plans.
  717.         int numberIdleAttackPlans=aiGetNumberIdlePlans(cPlanAttack);
  718.  
  719.         if (numberIdleAttackPlans > 0)
  720.             return;
  721.  
  722.         setupLargeAttack(1);
  723.     }
  724. }
  725.  
  726. //==============================================================================
  727. // Attack enablers - enable attacks after initial timer expires
  728. //==============================================================================
  729. rule baseAttackEnabler
  730.    minInterval 180
  731.    active
  732.    group AttackRules
  733. {
  734.     // Get the difficulty level.
  735.     difflevel=aiGetWorldDifficulty();
  736.  
  737.     // On Easy, don't attack for a little while.
  738.     if ( difflevel == 0 )
  739.     {
  740.         xsEnableRule("attackGenerator1Easy");
  741.     }
  742.     else
  743.     {
  744.         xsEnableRule("attackGenerator1");
  745.     }
  746.    xsDisableSelf();
  747. }
  748.  
  749. //==============================================================================
  750. // Tech Researching Rules - Age 2/3
  751. //==============================================================================
  752. rule researchMediumUpgrade
  753.    minInterval 420
  754.    active
  755. {
  756.    int planID=aiPlanCreate("Medium Infantry research", cPlanResearch);
  757.    if (planID < 0)
  758.       return;
  759.  
  760.     if ( randomUpgradeLine == 0 )
  761.     {
  762.         aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechMediumAxemen);
  763.     }
  764.     else
  765.     {
  766.         aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechMediumSpearmen);
  767.     }
  768.  
  769.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeBarracks);
  770.    aiPlanSetActive(planID);
  771.    
  772.     //Done.
  773.    xsDisableSelf();
  774. }
  775.  
  776. rule researchSerpentSpear
  777.    minInterval 480
  778.    active
  779. {
  780.     if ( randomUpgradeLine == 0 )
  781.     {
  782.         int planID=aiPlanCreate("Serpent Spear research", cPlanResearch);
  783.        if (planID < 0)
  784.            return;
  785.  
  786.         aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechSerpentSpear);
  787.         aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeBarracks);
  788.         aiPlanSetActive(planID);
  789.     }
  790.   
  791.     //Done.
  792.    xsDisableSelf();
  793. }
  794.  
  795. rule researchMediumSlingers
  796.    minInterval 540
  797.    active
  798. {
  799.    int planID=aiPlanCreate("Medium Slingers research", cPlanResearch);
  800.    if (planID < 0)
  801.       return;
  802.  
  803.     aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechMediumSlingers);
  804.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeBarracks);
  805.    aiPlanSetActive(planID);
  806.    
  807.     //Done.
  808.    xsDisableSelf();
  809. }
  810.  
  811.  
  812. rule researchHeavyUpgrade
  813.    minInterval 720
  814.    active
  815.     group ModerateResearchRules
  816. {
  817.    int planID=aiPlanCreate("Heavy Infantry research", cPlanResearch);
  818.    if (planID < 0)
  819.       return;
  820.  
  821.     if ( randomUpgradeLine == 0 )
  822.     {
  823.         aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechHeavyAxemen);
  824.     }
  825.     else
  826.     {
  827.         aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechHeavySpearmen);
  828.     }
  829.  
  830.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeBarracks);
  831.    aiPlanSetActive(planID);
  832.    
  833.     //Done.
  834.    xsDisableSelf();
  835. }
  836.  
  837.  
  838. rule researchHeavySlingers
  839.    minInterval 1000
  840.    active
  841.     group ModerateResearchRules
  842. {
  843.    int planID=aiPlanCreate("Heavy Slingers research", cPlanResearch);
  844.    if (planID < 0)
  845.       return;
  846.  
  847.     aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechHeavySlingers);
  848.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeBarracks);
  849.    aiPlanSetActive(planID);
  850.    
  851.     //Done.
  852.    xsDisableSelf();
  853. }
  854.  
  855. rule researchCopperWeapons
  856.    minInterval 540
  857.    active
  858.     group ModerateResearchRules
  859. {
  860.    int planID=aiPlanCreate("Copper weapons research", cPlanResearch);
  861.    if (planID < 0)
  862.       return;
  863.  
  864.     aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechCopperWeapons);
  865.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeArmory);
  866.    aiPlanSetActive(planID);
  867.    
  868.     //Done.
  869.    xsDisableSelf();
  870. }
  871.  
  872. rule researchCopperShields
  873.    minInterval 600
  874.    active
  875.     group ModerateResearchRules
  876. {
  877.    int planID=aiPlanCreate("Copper shields research", cPlanResearch);
  878.    if (planID < 0)
  879.       return;
  880.  
  881.     aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechCopperShields);
  882.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeArmory);
  883.    aiPlanSetActive(planID);
  884.    
  885.     //Done.
  886.    xsDisableSelf();
  887. }
  888.  
  889. rule researchBronzeWeapons
  890.    minInterval 780
  891.    active
  892.     group ModerateResearchRules
  893. {
  894.    int planID=aiPlanCreate("Bronze weapons research", cPlanResearch);
  895.    if (planID < 0)
  896.       return;
  897.  
  898.     aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechBronzeWeapons);
  899.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeArmory);
  900.    aiPlanSetActive(planID);
  901.    
  902.     //Done.
  903.    xsDisableSelf();
  904. }
  905.  
  906. rule researchBronzeShields
  907.    minInterval 900
  908.    active
  909.     group ModerateResearchRules
  910. {
  911.    int planID=aiPlanCreate("Bronze shields research", cPlanResearch);
  912.    if (planID < 0)
  913.       return;
  914.  
  915.     aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechBronzeShields);
  916.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeArmory);
  917.    aiPlanSetActive(planID);
  918.    
  919.     //Done.
  920.    xsDisableSelf();
  921. }
  922.  
  923. rule researchCopperMail
  924.    minInterval 1100
  925.    active
  926.     group ModerateResearchRules
  927. {
  928.    int planID=aiPlanCreate("Copper mail research", cPlanResearch);
  929.    if (planID < 0)
  930.       return;
  931.  
  932.     aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechCopperMail);
  933.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeArmory);
  934.    aiPlanSetActive(planID);
  935.    
  936.     //Done.
  937.    xsDisableSelf();
  938. }
  939.  
  940.  
  941. rule researchHeavyElephants
  942.    minInterval 1260
  943.    active
  944.     group ModerateResearchRules
  945. {
  946.    int planID=aiPlanCreate("Heavy Elephants research", cPlanResearch);
  947.    if (planID < 0)
  948.       return;
  949.  
  950.     aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechHeavyElephants);
  951.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeMigdolStronghold);
  952.    aiPlanSetActive(planID);
  953.    
  954.     //Done.
  955.    xsDisableSelf();
  956. }
  957.  
  958. rule researchBronzeMail
  959.    minInterval 1650
  960.    active
  961.     group ModerateResearchRules
  962. {
  963.    int planID=aiPlanCreate("Bronze mail research", cPlanResearch);
  964.    if (planID < 0)
  965.       return;
  966.  
  967.     aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechBronzeMail);
  968.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeArmory);
  969.    aiPlanSetActive(planID);
  970.    
  971.     //Done.
  972.    xsDisableSelf();
  973. }
  974.  
  975.  
  976. //==============================================================================
  977. // Age 4 researches - activated by the "researchingAge4" thing.
  978. //==============================================================================
  979. rule researchChampionUpgrade
  980.    minInterval 420
  981.    inactive
  982. {
  983.    int planID=aiPlanCreate("Medium Infantry research", cPlanResearch);
  984.    if (planID < 0)
  985.       return;
  986.  
  987.     if ( randomUpgradeLine == 0 )
  988.     {
  989.         aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechChampionAxemen);
  990.     }
  991.     else
  992.     {
  993.         aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechChampionSpearmen);
  994.     }
  995.  
  996.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeBarracks);
  997.    aiPlanSetActive(planID);
  998.    
  999.     //Done.
  1000.    xsDisableSelf();
  1001. }
  1002.  
  1003. rule researchMythTechUpgrade
  1004.    minInterval 660
  1005.    inactive
  1006. {
  1007.    int planID=aiPlanCreate("Odd myth tech research", cPlanResearch);
  1008.    if (planID < 0)
  1009.       return;
  1010.  
  1011.     if ( randomUpgradeLine == 0 )
  1012.     {
  1013.         aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechSpearontheHorizon);
  1014.     }
  1015.     else
  1016.     {
  1017.         aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechAxeofVengeance);
  1018.     }
  1019.  
  1020.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeBarracks);
  1021.    aiPlanSetActive(planID);
  1022.    
  1023.     //Done.
  1024.    xsDisableSelf();
  1025. }
  1026.  
  1027. rule researchEngineers
  1028.    minInterval 750
  1029.    inactive
  1030. {
  1031.    int planID=aiPlanCreate("Engineers research", cPlanResearch);
  1032.    if (planID < 0)
  1033.       return;
  1034.  
  1035.     aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechEngineers);
  1036.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeSiegeCamp);
  1037.    aiPlanSetActive(planID);
  1038.    
  1039.     //Done.
  1040.    xsDisableSelf();
  1041. }
  1042.  
  1043. rule researchSlingsOfTheSun
  1044.    minInterval 900
  1045.    inactive
  1046. {
  1047.    int planID=aiPlanCreate("Slings of the Sun research", cPlanResearch);
  1048.    if (planID < 0)
  1049.       return;
  1050.  
  1051.     aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, );
  1052.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeBarracks);
  1053.    aiPlanSetActive(planID);
  1054.    
  1055.     //Done.
  1056.    xsDisableSelf();
  1057. }
  1058.  
  1059. rule researchIronWeapons
  1060.    minInterval 1200
  1061.    inactive
  1062. {
  1063.    int planID=aiPlanCreate("Iron weapons research", cPlanResearch);
  1064.    if (planID < 0)
  1065.       return;
  1066.  
  1067.     aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechIronWeapons);
  1068.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeArmory);
  1069.    aiPlanSetActive(planID);
  1070.    
  1071.     //Done.
  1072.    xsDisableSelf();
  1073. }
  1074.  
  1075. //==============================================================================
  1076. // More Maintaining - starts at the twelve-minute mark
  1077. //==============================================================================
  1078. rule moreMaintaining
  1079.    minInterval 720
  1080.    active
  1081. {
  1082.     // Get the difficulty level.
  1083.     difflevel=aiGetWorldDifficulty();
  1084.  
  1085.    aiEcho("*** NOW MAINTAINING LOTS MORE UNIT TYPES ***");
  1086.     
  1087.     int numElephants = 4;
  1088.     int numSiegeTowers = 2;
  1089.     int numScarabs = 2;
  1090.     int numPriests = 4;
  1091.  
  1092.     if ( difflevel == 3 )
  1093.     {
  1094.         numElephants = 8;
  1095.     }
  1096.  
  1097.    //Three gather points
  1098.    vector gatherPoint=kbGetBlockPosition("924");
  1099.     vector gatherPoint2=kbGetBlockPosition("1175");
  1100.     vector gatherPoint3=kbGetBlockPosition("1176");
  1101.  
  1102.     int maintainPlan4ID=aiPlanCreate("Maintain "+numElephants+" "+kbGetProtoUnitName(attackerUnitTypeID4), cPlanTrain);
  1103.    
  1104.     // Maintain war elephants
  1105.    if (maintainPlan4ID >= 0)
  1106.    {
  1107.         //Must set the type of unit to train.
  1108.       aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanUnitType, 0, attackerUnitTypeID4);
  1109.       //Set the number of units to maintain in the world at one time.
  1110.       aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanNumberToMaintain, 0, numElephants);
  1111.       //Don't train units faster than every 90 seconds
  1112.         if ( difflevel == 3 )
  1113.         {
  1114.             aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanFrequency, 0, 45);
  1115.         }
  1116.         else
  1117.         {
  1118.             aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanFrequency, 0, 90);
  1119.         }
  1120.       //Set a gather point.
  1121.       aiPlanSetVariableVector(maintainPlan4ID, cTrainPlanGatherPoint, 0, gatherPoint);
  1122.       //Activate the plan.
  1123.       aiPlanSetActive(maintainPlan4ID);
  1124.    }
  1125.  
  1126.     int maintainPlan7ID=aiPlanCreate("Maintain "+numSiegeTowers+" "+kbGetProtoUnitName(attackerUnitTypeID7), cPlanTrain);
  1127.    // Maintain siege towers.
  1128.    if (maintainPlan7ID >= 0)
  1129.    {
  1130.         //Must set the type of unit to train.
  1131.       aiPlanSetVariableInt(maintainPlan7ID, cTrainPlanUnitType, 0, attackerUnitTypeID7);
  1132.       //Set the number of units to maintain in the world at one time.
  1133.       aiPlanSetVariableInt(maintainPlan7ID, cTrainPlanNumberToMaintain, 0, numSiegeTowers);
  1134.       //Don't train units faster than every two minutes
  1135.       aiPlanSetVariableInt(maintainPlan7ID, cTrainPlanFrequency, 0, 240);
  1136.       //Set a gather point.
  1137.       aiPlanSetVariableVector(maintainPlan7ID, cTrainPlanGatherPoint, 0, gatherPoint2);
  1138.       //Activate the plan.
  1139.       aiPlanSetActive(maintainPlan7ID);
  1140.    }
  1141.  
  1142.     int maintainPlan9ID=aiPlanCreate("Maintain "+numScarabs+" "+kbGetProtoUnitName(attackerUnitTypeID9), cPlanTrain);
  1143.     // Maintain scarabs.
  1144.    if (maintainPlan9ID >= 0)
  1145.    {
  1146.         //Must set the type of unit to train.
  1147.       aiPlanSetVariableInt(maintainPlan9ID, cTrainPlanUnitType, 0, attackerUnitTypeID9);
  1148.       //Set the number of units to maintain in the world at one time.
  1149.       aiPlanSetVariableInt(maintainPlan9ID, cTrainPlanNumberToMaintain, 0, numScarabs);
  1150.       //Don't train units too fast
  1151.       aiPlanSetVariableInt(maintainPlan9ID, cTrainPlanFrequency, 0, 250);
  1152.       //Set a gather point.
  1153.       aiPlanSetVariableVector(maintainPlan9ID, cTrainPlanGatherPoint, 0, gatherPoint3);
  1154.       //Activate the plan.
  1155.       aiPlanSetActive(maintainPlan9ID);
  1156.    }
  1157.  
  1158.     int maintainPlan11ID=aiPlanCreate("Maintain "+numScarabs+" "+kbGetProtoUnitName(attackerUnitTypeID11), cPlanTrain);
  1159.     // Maintain scarabs.
  1160.    if (maintainPlan11ID >= 0)
  1161.    {
  1162.         //Must set the type of unit to train.
  1163.       aiPlanSetVariableInt(maintainPlan11ID, cTrainPlanUnitType, 0, attackerUnitTypeID11);
  1164.       //Set the number of units to maintain in the world at one time.
  1165.       aiPlanSetVariableInt(maintainPlan11ID, cTrainPlanNumberToMaintain, 0, numPriests);
  1166.       //Don't train units too fast
  1167.       aiPlanSetVariableInt(maintainPlan11ID, cTrainPlanFrequency, 0, 275);
  1168.       //Set a gather point.
  1169.       aiPlanSetVariableVector(maintainPlan11ID, cTrainPlanGatherPoint, 0, gatherPoint);
  1170.       //Activate the plan.
  1171.       aiPlanSetActive(maintainPlan11ID);
  1172.    }
  1173.  
  1174.     // Enable the large attack generator
  1175.     if ( difflevel == 0 )
  1176.     {
  1177.         xsEnableRule("attackGenerator2Easy");
  1178.     }
  1179.     else
  1180.     {
  1181.         xsEnableRule("attackGenerator2");
  1182.     }
  1183.    xsDisableSelf();
  1184. }
  1185.  
  1186. //==============================================================================
  1187. // GOD POWERS
  1188. // Vision two minutes in on enemy TC (he guesses good).
  1189. // Serpents when the Tamarisk Tree is being worked on.
  1190. // Tornado in an attack post-Age 4 that gets close to the player's TC.
  1191. //==============================================================================
  1192. rule fireVision
  1193.     minInterval 120
  1194.     active
  1195.     group GodPowers
  1196. {
  1197.  
  1198.     vector visionPoint=kbGetBlockPosition("942");
  1199.     aiEcho("Casting Vision.");
  1200.     if ( aiCastGodPowerAtPosition(cTechVision, visionPoint) == true )
  1201.         xsDisableSelf();
  1202.     else
  1203.         aiEcho("Vision failed - try again later.");
  1204. }
  1205.  
  1206. // "Time for Serpents" also builds a wall and a Migdol.
  1207. rule timeForSerpents
  1208.     minInterval 15
  1209.     active
  1210.     group GodPowers
  1211. {
  1212.     int numVillagers=-1;
  1213.     vector serpentPoint=kbGetBlockPosition("934");
  1214.  
  1215.     vector MigdolPoint=kbGetBlockPosition("1037");
  1216.  
  1217.     vector WallPointA=kbGetBlockPosition("1035");
  1218.     vector WallPointB=kbGetBlockPosition("1036");
  1219.     
  1220.     vector WallPointC=kbGetBlockPosition("1065");
  1221.     vector WallPointD=kbGetBlockPosition("1066");
  1222.  
  1223.     numVillagers=checkForVillagers();
  1224.  
  1225.     if (numVillagers > 3)
  1226.     {
  1227.         aiEcho("Villagers near Tamarisk Tree, cast Serpents and do other nasty things.");
  1228.         if ( aiCastGodPowerAtPosition(cTechSerpents, serpentPoint) == true )
  1229.             aiEcho("Serpents cast.");
  1230.             
  1231.         else
  1232.             aiEcho("Serpents failed.");
  1233.  
  1234.         // Put up a couple of walls.
  1235.         aiWallFromAToB("Build Wall 1", WallPointA, WallPointB, 1, 1, 1, cRootEscrowID, 1 );
  1236.         aiWallFromAToB("Build Wall 2", WallPointC, WallPointD, 1, 1, 1, cRootEscrowID, 1 );
  1237.  
  1238.        //-- Build a migdol to cover the tower..
  1239.         int buildMigdol = aiPlanCreate("Build Migdol", cPlanBuild);
  1240.         if(buildMigdol >= 0)
  1241.         {
  1242.             //BP Type and Priority. 
  1243.             aiPlanSetVariableInt(buildMigdol, cBuildPlanBuildingTypeID, 0, cUnitTypeTower);
  1244.             aiPlanSetDesiredPriority(buildMigdol, 80);
  1245.             aiPlanSetVariableVector(buildMigdol, cBuildPlanCenterPosition, 0, MigdolPoint);
  1246.             aiPlanSetVariableFloat(buildMigdol, cBuildPlanCenterPositionDistance, 0, 5);
  1247.             aiPlanAddUnitType(buildMigdol, cUnitTypeVillagerEgyptian, 4, 4, 4);
  1248.             aiPlanSetEscrowID(buildMigdol, cRootEscrowID);
  1249.             aiPlanSetActive(buildMigdol);
  1250.         }
  1251.         xsDisableSelf();
  1252.     }
  1253. }
  1254.  
  1255. rule timeForTornado
  1256.     minInterval 15
  1257.     inactive
  1258.     group GodPowers
  1259. {
  1260.     int numAttackers=-1;
  1261.     vector tornadoPoint=kbGetBlockPosition("942");
  1262.     numAttackers=checkForTornado();
  1263.  
  1264.     if (numAttackers > 0 && numAttackers < 8 )
  1265.     {
  1266.         aiEcho("Approaching enemy town, cast Tornado.");
  1267.         if ( aiCastGodPowerAtPosition(cTechTornado, tornadoPoint) == true )
  1268.             xsDisableSelf();
  1269.         else
  1270.             aiEcho("Tornado failed - try again later.");
  1271.     }
  1272. }
  1273.  
  1274. //==============================================================================
  1275. // Advance to Age 4
  1276. //==============================================================================
  1277. rule researchAge4
  1278.    minInterval 1600
  1279.    active
  1280.     group ModerateResearchRules
  1281. {
  1282.    int planID=aiPlanCreate("Age 4 Horus Advance", cPlanResearch);
  1283.    if (planID < 0)
  1284.       return;
  1285.  
  1286.     aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechAge4Horus);
  1287.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeSettlementLevel1);
  1288.    aiPlanSetActive(planID);
  1289.  
  1290.       // Enable the large attack generator
  1291.     xsEnableRule("researchChampionUpgrade");
  1292.     xsEnableRule("researchMythTechUpgrade");
  1293.     xsEnableRule("researchEngineers");
  1294.     xsEnableRule("timeForTornado");
  1295.     xsEnableRule("researchIronWeapons");
  1296.     xsEnableRule("researchSlingsOfTheSun");
  1297.  
  1298.     aiEcho("*** ADVANCING TO AGE 4 ***");
  1299.  
  1300.     //Done.
  1301.    xsDisableSelf();
  1302. }
  1303.  
  1304. // Scary Rule, uh oh...gets villagers to go away after doing their thing (in theory)
  1305. rule taskBuilders
  1306.    minInterval 20
  1307.     active
  1308. {
  1309.  
  1310.    static int villQuery = -1;
  1311.     int enemyCount=-1;
  1312.     vector forwardPosition=kbGetBlockPosition("1037");
  1313.  
  1314.    int villCount = -1;
  1315.  
  1316.    if (villQuery < 0) // need to define query
  1317.    {
  1318.       villQuery = kbUnitQueryCreate("checkBuilders");    // Look for HP units
  1319.       if ( configQuery(villQuery, cUnitTypeVillagerEgyptian, cActionIdle, cUnitStateAlive, 2, forwardPosition, false, 15) == false)
  1320.          return;
  1321.    }
  1322.    kbUnitQueryResetResults(villQuery);
  1323.    villCount = kbUnitQueryExecute(villQuery);
  1324.  
  1325.    if (villCount == 0)
  1326.       return;
  1327.  
  1328.    int vill = -1;
  1329.    for (i=0; < villCount)
  1330.    {
  1331.       vill = kbUnitQueryGetResult(villQuery, i);
  1332.         aiTaskUnitWork(vill, kbGetBlockID("1114"));     // move 'em to a farm.
  1333.    }
  1334. }
  1335.  
  1336. //==============================================================================
  1337. // defendTree.  Called with an AI FUNC when it's time to defend Mr. Tree.
  1338. //
  1339. // Only happens on Hard and Titan.
  1340. //==============================================================================
  1341. void defendTree(int scriptCall = -1)
  1342. {
  1343.     vector treeDefendPoint=kbGetBlockPosition("934");
  1344.     aiEcho("*** DEFEND THE TREE!  OMG, DEFEND THE F'IN TREE!!!");
  1345.  
  1346.     // Tree now threatened, behavior changes on hard and nightmare.
  1347.     treethreat=1;
  1348.  
  1349.     // Defend the tree!  Do this while you're doin' other stuff...
  1350.    int defendPlan1ID=aiPlanCreate("Tree Defense", cPlanDefend);
  1351.    if (defendPlan1ID >= 0)
  1352.    {
  1353.       //Add the unit(s) - priests and anubites, the elite guard!
  1354.       aiPlanAddUnitType(defendPlan1ID, attackerUnitTypeID8, 6, 6, 6);
  1355.         aiPlanAddUnitType(defendPlan1ID, attackerUnitTypeID11, 2, 2, 2);
  1356.         
  1357.       //Setup the vars.
  1358.       aiPlanSetDesiredPriority(defendPlan1ID, 75);
  1359.       aiPlanSetVariableVector(defendPlan1ID, cDefendPlanDefendPoint, 0, treeDefendPoint);
  1360.       aiPlanSetVariableFloat(defendPlan1ID, cDefendPlanEngageRange, 0, 25);
  1361.       aiPlanSetActive(defendPlan1ID);
  1362.     }
  1363. }
  1364.  
  1365. //==============================================================================
  1366. // MAIN
  1367. //==============================================================================
  1368. void main(void)
  1369. {
  1370.    //Startup.
  1371.    miscStartup();
  1372.     initEcon();
  1373.     evaluateRandomUpgrade();
  1374.  
  1375.     // If difficulty level is easy, kill a bunch of researches.
  1376.     if ( difflevel == 0 )
  1377.     {
  1378.         xsDisableRuleGroup( "ModerateResearchRules" );
  1379.     }
  1380.  
  1381.    //Share the number to maintain.
  1382.    int numberToMaintain=attackMinimumGroupSize*2;
  1383.  
  1384.    //Various gather points
  1385.    vector gatherPoint=kbGetBlockPosition("924");
  1386.     vector gatherPoint2=kbGetBlockPosition("1175");
  1387.     vector gatherPoint3=kbGetBlockPosition("1176");
  1388.  
  1389.     vector gatherPointAnubite=kbGetBlockPosition("1227");
  1390.  
  1391.    //Create a simple plan to maintain X spearmen.
  1392.    int maintainPlan1ID=aiPlanCreate("Maintain "+numberToMaintain+" "+kbGetProtoUnitName(attackerUnitTypeID1), cPlanTrain);
  1393.    if (maintainPlan1ID >= 0)
  1394.    {
  1395.         //Must set the type of unit to train.
  1396.       aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanUnitType, 0, attackerUnitTypeID1);
  1397.       //You can limit the number of units that are ever trained by this plan with this call.
  1398.       //aiPlanSetVariableInt(maintainPlanID, cTrainPlanNumberToTrain, 0, 25);
  1399.       //Set the number of units to maintain in the world at one time.
  1400.       aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanNumberToMaintain, 0, numberToMaintain);
  1401.       //Don't train units faster than every 25 seconds.
  1402.       aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanFrequency, 0, 25);
  1403.       //Set a gather point.
  1404.       aiPlanSetVariableVector(maintainPlan1ID, cTrainPlanGatherPoint, 0, gatherPoint3);
  1405.       //Activate the plan.
  1406.       aiPlanSetActive(maintainPlan1ID);
  1407.    }
  1408.  
  1409.     //Create a simple plan to maintain X axemen.
  1410.    int maintainPlan2ID=aiPlanCreate("Maintain "+numberToMaintain+" "+kbGetProtoUnitName(attackerUnitTypeID2), cPlanTrain);
  1411.    if (maintainPlan2ID >= 0)
  1412.    {
  1413.         //Must set the type of unit to train.
  1414.       aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanUnitType, 0, attackerUnitTypeID2);
  1415.       //You can limit the number of units that are ever trained by this plan with this call.
  1416.       //aiPlanSetVariableInt(maintainPlanID, cTrainPlanNumberToTrain, 0, 25);
  1417.       //Set the number of units to maintain in the world at one time.
  1418.       aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanNumberToMaintain, 0, numberToMaintain);
  1419.       //Don't train units faster than every 40 seconds
  1420.       aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanFrequency, 0, 40);
  1421.       //Set a gather point.
  1422.       aiPlanSetVariableVector(maintainPlan2ID, cTrainPlanGatherPoint, 0, gatherPoint2);
  1423.       //Activate the plan.
  1424.       aiPlanSetActive(maintainPlan2ID);
  1425.    }
  1426.  
  1427.     //Create a simple plan to maintain X slingers.
  1428.    int maintainPlan3ID=aiPlanCreate("Maintain "+numberToMaintain+" "+kbGetProtoUnitName(attackerUnitTypeID3), cPlanTrain);
  1429.    if (maintainPlan3ID >= 0)
  1430.    {
  1431.         //Must set the type of unit to train.
  1432.       aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanUnitType, 0, attackerUnitTypeID3);
  1433.       //You can limit the number of units that are ever trained by this plan with this call.
  1434.       //aiPlanSetVariableInt(maintainPlanID, cTrainPlanNumberToTrain, 0, 25);
  1435.       //Set the number of units to maintain in the world at one time.
  1436.       aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanNumberToMaintain, 0, numberToMaintain);
  1437.       //Don't train units faster than every 35 seconds
  1438.       aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanFrequency, 0, 35);
  1439.       //Set a gather point.
  1440.       aiPlanSetVariableVector(maintainPlan3ID, cTrainPlanGatherPoint, 0, gatherPoint);
  1441.       //Activate the plan.
  1442.       aiPlanSetActive(maintainPlan3ID);
  1443.    }
  1444.  
  1445.     //Anubites for the defend plan.
  1446.    int maintainPlan8ID=aiPlanCreate("Maintain "+numberToMaintain+" "+kbGetProtoUnitName(attackerUnitTypeID3), cPlanTrain);
  1447.    if (maintainPlan8ID >= 0)
  1448.    {
  1449.         //Must set the type of unit to train.
  1450.       aiPlanSetVariableInt(maintainPlan8ID, cTrainPlanUnitType, 0, attackerUnitTypeID8);
  1451.       //You can limit the number of units that are ever trained by this plan with this call.
  1452.       //aiPlanSetVariableInt(maintainPlanID, cTrainPlanNumberToTrain, 0, 25);
  1453.       //Set the number of units to maintain in the world at one time.
  1454.       aiPlanSetVariableInt(maintainPlan8ID, cTrainPlanNumberToMaintain, 0, 8);
  1455.       //These train back pretty slowly, giving you time to deal with 'em.
  1456.       aiPlanSetVariableInt(maintainPlan8ID, cTrainPlanFrequency, 0, 90);
  1457.       //Set a gather point.
  1458.       aiPlanSetVariableVector(maintainPlan8ID, cTrainPlanGatherPoint, 0, gatherPointAnubite);
  1459.       //Activate the plan.
  1460.       aiPlanSetActive(maintainPlan8ID);
  1461.    }
  1462. }