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

  1. //==============================================================================
  2. // Scn06p2: AI Scenario Script for scenario 6 Trojan player
  3. //==============================================================================
  4. /*
  5.    AI owner:  Dave Leary
  6.    Scenario owner: Dave Leary
  7.  
  8.     AI for "Troy."  Maintains hoplites, hippikons, and minotaurs at the start of
  9.     the scenario, and adds peltasts and nemean lions after advancing to Age 3.
  10.     Researches several techs as the scenario progresses.  Sends two main attack
  11.     group types - one with a primary unit of hoplites, and a raiding attack of
  12.     hippikons which should prioritize villagers unless it runs into the player's
  13.     army.
  14.  
  15.    A defend group of Colossi for the second dock is created once the player 
  16.     destroys the first dock, and ships are collected in a defend group in the 
  17.     harbor near the second dock.
  18.     
  19.     The hoplite groups randomly include the other maintained unit types.
  20.  
  21.    Attacks are enabled from the scenario once the player's first settlement has
  22.     been claimed (the second scenario objective).
  23.  
  24.    The AI is cheating like mad.  Don't look for it to gather resources; it has
  25.     plenty of everything it might need.
  26.  
  27.                                 *** DIFFICULTY LEVEL NOTES ***
  28.  
  29.    Easy level - most towers removed.  Two less colossi. Half as many units at the
  30.     docks.  Player starts with heavy infantry.  Fewer units on patrol at game
  31.     start. Cavalry raiding attacks never come.
  32.  
  33.    Moderate level - base difficulty level.  HP starts with medium infantry and
  34.     medium archers.  First cavalry attack delayed an extra two minutes.
  35.  
  36.    Difficult level - enemy starts with boiling oil tech. More towers along main
  37.     wall.  Player does not have medium archers.  Potential for Troy to do
  38.     additional researches at the game progresses.  Attack group sizes are larger.
  39.     Colossi are slightly upgraded.  Extra hypaspists at the docks.
  40.  
  41.    Nightmare - two towers at the second unoccupied settlement. One extra tower
  42.     at each dock.  Attack group sizes are larger.  Colossus are very upgraded.
  43.     Extra hypaspists at the docks.  Walls around docks are upgraded to stone.
  44. */
  45. //
  46.  
  47. // Shared difficulty level variable.
  48. int difflevel=-1;        
  49.  
  50. //==============================================================================
  51. // Set Town Location 
  52. //==============================================================================
  53. void setTownLocation(void)
  54. {
  55.    //Look for the "Town Location" marker.
  56.    kbSetTownLocation(kbGetBlockPosition("3989"));
  57. }
  58.  
  59. //==============================================================================
  60. // miscStartup
  61. //==============================================================================
  62. void miscStartup(void)
  63. {
  64.     // Get the difficulty level.
  65.     difflevel=aiGetWorldDifficulty();
  66.  
  67.    //Startup message(s).
  68.    aiEcho("");
  69.    aiEcho("");
  70.    aiEcho("Scn06P2 AI Start, filename='"+cFilename+"'.");
  71.     aiEcho("Difficulty Level="+difflevel+".");
  72.     //Spit out the map size.
  73.    aiEcho("  Map size is ("+kbGetMapXSize()+", "+kbGetMapZSize()+").");
  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 seeds
  81.     aiRandSetSeed();
  82.  
  83.     //Allocate all resources to the root escrow by setting percentage of military/economy to 0.
  84.     kbEscrowSetPercentage( cEconomyEscrowID, cAllResources, 0.0 );
  85.     kbEscrowSetPercentage( cMilitaryEscrowID, cAllResources, 0.0 );
  86.    //Allocate all resources to the root escrow.
  87.    kbEscrowAllocateCurrentResources();
  88.     
  89.     // Drop the AI attack response distance for this player to 25 meters.
  90.     aiSetAttackResponseDistance(25.0);
  91. }
  92.  
  93. //==============================================================================
  94. // Attack stuff.
  95. //==============================================================================
  96.  
  97. //Shared variables.
  98. int numberAttacks=0;
  99. int attackPlayerID=-1;
  100. bool makingAge3Units=false;
  101.  
  102. //TODO: Decide how to rep attack group size.
  103. int attackMinimumGroupSize=4;
  104. int attackMaximumGroupSize=6;
  105.  
  106. //Attack 1 vars.
  107. int attackPlan1ID=-1;
  108. int maintainPlan1ID=-1;
  109.  
  110. //Attack 2 vars.
  111. int attackPlan2ID=-1;
  112. int maintainPlan2ID=-1;
  113.  
  114. // Unit types
  115. int attackerUnitTypeID1=cUnitTypeHoplite;
  116. int attackerUnitTypeID2=cUnitTypeHippikon;
  117. int attackerUnitTypeID3=cUnitTypeToxotes;
  118. int attackerUnitTypeID4=cUnitTypePeltast;
  119. int attackerUnitTypeID5=cUnitTypeMinotaur;
  120. int attackerUnitTypeID6=cUnitTypeNemeanLion;
  121. int attackerUnitTypeID7=cUnitTypeShadeofHades;
  122.  
  123. // Route and path vars
  124. int attackRoute1ID=-1;
  125. int attackPath1ID=-1;
  126. int attackRoute2ID=-1;
  127. int attackPath2ID=-1;
  128.  
  129. //=========================================================================================
  130. // Kidd's cool configQuery function: used to create attack routes, etc.  Oooh, lovin' that!
  131. //=========================================================================================
  132. 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 )
  133. {
  134.    if ( queryID == -1)
  135.    {
  136.       return(false);
  137.    }
  138.  
  139.    if (player != -1)
  140.       kbUnitQuerySetPlayerID(queryID, player);
  141.    
  142.    if (unitType != -1)
  143.       kbUnitQuerySetUnitType(queryID, unitType);
  144.  
  145.    if (action != -1)
  146.       kbUnitQuerySetActionType(queryID, action);
  147.  
  148.    if (state != -1)
  149.       kbUnitQuerySetState(queryID, state);
  150.  
  151.    if (center != vector(-1,-1,-1))
  152.    {
  153.       kbUnitQuerySetPosition(queryID, center);
  154.       if (sort == true)
  155.          kbUnitQuerySetAscendingSort(queryID, true);
  156.       if (radius != -1)
  157.          kbUnitQuerySetMaximumDistance(queryID, radius);
  158.    }
  159.    return(true);
  160. }
  161.  
  162. //==============================================================================
  163. // Queryin' for enemies near the TC to the left.
  164. // This is used to drop sentinel on it if the player goes near it.
  165. //==============================================================================
  166. int checkForEnemies(void)
  167. {
  168.     static int enemyQueryID=-1;
  169.     vector leftTC=kbGetBlockPosition("4044");
  170.     int enemyCount=-1;
  171.  
  172.    if (enemyQueryID < 0)
  173.    {  
  174.         // Doesn't exist, set it up
  175.       enemyQueryID = kbUnitQueryCreate("Enemy Query");
  176.         
  177.       // Get the number
  178.       if ( configQuery( enemyQueryID, cUnitTypeUnit, -1, cUnitStateAlive, 1, leftTC, false, 20 ) == false )
  179.          return(-1);
  180.    }
  181.  
  182.    kbUnitQueryResetResults(enemyQueryID);
  183.    enemyCount = kbUnitQueryExecute(enemyQueryID);
  184.     return(enemyCount);
  185. }
  186.  
  187. //==============================================================================
  188. // initAttack: Creates attack routes, etc.
  189. //==============================================================================
  190. void initAttack(int playerID=-1)
  191. {
  192.    //Destroy all previous attacks (if this isn't the player we're already attacking.
  193.    if (playerID != attackPlayerID)
  194.    {
  195.       //Reset the attack player ID.
  196.       attackPlayerID=-1;
  197.       //Destroy any previous attack plan.
  198.       aiPlanDestroy(attackPlan1ID);
  199.       attackPlan1ID=-1;
  200.       aiPlanDestroy(attackPlan2ID);
  201.       attackPlan2ID=-1;
  202.   
  203.       //Destroy our previous attack paths.
  204.       kbPathDestroy(attackPath1ID);
  205.       attackPath1ID=-1;
  206.       kbPathDestroy(attackPath2ID);
  207.       attackPath2ID=-1;
  208.  
  209.       //Destroy our previous attack routes.
  210.       attackRoute1ID=-1;
  211.       attackRoute2ID=-1;
  212.  
  213.       //Reset the number of attacks.
  214.       numberAttacks=0;
  215.    }
  216.  
  217.    //Save the player to attack.
  218.    attackPlayerID=playerID;
  219.  
  220.    vector gatherPoint=kbGetBlockPosition("3989");
  221.        
  222.     //Setup attack path 1 - go left
  223.    attackPath1ID=kbPathCreate("Attack Path 1");
  224.    kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("3990"));
  225.     kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("3991"));
  226.     kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("3992"));
  227.    
  228.     //Create attack route 1.
  229.    attackRoute1ID=kbCreateAttackRouteWithPath("Attack Route 1", gatherPoint, kbGetBlockPosition("3995"));
  230.    
  231.     if (attackRoute1ID >= 0)
  232.       kbAttackRouteAddPath(attackRoute1ID, attackPath1ID);
  233.  
  234.    //Setup attack path 2 - go right
  235.    attackPath2ID=kbPathCreate("Attack Path 2");
  236.    kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("3990"));
  237.     kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("3991"));
  238.     kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("3993"));
  239.     //Create attack route 2.
  240.    attackRoute2ID=kbCreateAttackRouteWithPath("Attack Route 2", gatherPoint, kbGetBlockPosition("3994"));
  241.    
  242.     if (attackRoute2ID >= 0)
  243.       kbAttackRouteAddPath(attackRoute2ID, attackPath2ID);
  244. }
  245.  
  246. //==============================================================================
  247. // setupHopliteAttack
  248. //
  249. // Hoplites head out, and sometimes bring their friends.
  250. // They go one of two routes.
  251. // They sometimes take Peltasts with 'em if it's Age 3.
  252. //==============================================================================
  253. bool setupHopliteAttack(int playerID=-1)
  254. {
  255.     int randomPath=aiRandInt(2);
  256.     int randomUnitTypes=aiRandInt(4);
  257.  
  258.     difflevel=aiGetWorldDifficulty();
  259.    
  260.     //Info.
  261.     aiEcho("Hoplite attack: attacking Player "+playerID+".");
  262.  
  263.    //If the player to attack doesn't match, init the attack.
  264.    if (attackPlayerID != playerID)
  265.    {
  266.       initAttack(playerID);
  267.       if (attackPlayerID < 0)
  268.          return(false);
  269.    }
  270.  
  271.    //Create an attack plan.
  272.    int newAttackPlanID=aiPlanCreate("Attack Player"+attackPlayerID+" Attempt"+numberAttacks, cPlanAttack);
  273.    if (newAttackPlanID < 0)
  274.       return(false);
  275.  
  276.    //Target player (required).  This must work.
  277.    if (aiPlanSetVariableInt(newAttackPlanID, cAttackPlanPlayerID, 0, attackPlayerID) == false)
  278.       return(false);
  279.  
  280.    //Gather point.
  281.     vector gatherPoint=kbGetBlockPosition("3989");
  282.  
  283.     //Set the target type.  This must work.
  284.    if (aiPlanSetNumberVariableValues(newAttackPlanID, cAttackPlanTargetTypeID, 2, true) == false)
  285.       return(false);
  286.  
  287.    //Unit types to attack.
  288.     aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 0, cUnitTypeBuilding);
  289.    aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 1, cUnitTypeUnit);
  290.     
  291.    //Attack route.
  292.    if (randomPath == 0)
  293.     {
  294.         aiEcho("Attack going left.");
  295.       aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute1ID);
  296.     }
  297.    else
  298.     {
  299.         aiEcho("Attack going right.");
  300.       aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute2ID);
  301.     }
  302.  
  303.    //Set the gather point and gather point distance.
  304.    aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPoint);
  305.    aiPlanSetVariableFloat(newAttackPlanID, cAttackPlanGatherDistance, 0, 40.0);
  306.  
  307.    //Set up the attack route usage pattern.
  308.    aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRoutePattern, 0, cAttackPlanAttackRoutePatternRandom);
  309.    
  310.     //Add the basic unit type to the plan.
  311.    aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID1, attackMinimumGroupSize, attackMaximumGroupSize, attackMaximumGroupSize);
  312.  
  313.     //Add some extra guys if the random numbers come up right
  314.     switch(randomUnitTypes)
  315.    {
  316.         // A couple of cavalry, if they're free.  Take at least one, willya?
  317.        case 0:
  318.       {
  319.             aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID2, 1, 2, 2);
  320.             break;
  321.       }
  322.         
  323.         // Throw in a single toxotes.  There should be a bunch standing around somewhere.
  324.         case 1:
  325.       {
  326.             aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID3, 0, 1, 1);
  327.             break;
  328.       }
  329.         
  330.         // A minotaur?  Sure!  We probably made one by now!  But only if difflevel > easy.
  331.         case 2:
  332.         {
  333.             if ( difflevel > 0 )
  334.             {
  335.                 aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID5, 0, 1, 2);
  336.             }
  337.             break;
  338.         }
  339.         
  340.         // Oooh, ooh - or how about nothing?  Those hoplites are fun enough by themselves!
  341.         default:
  342.             break;
  343.     }
  344.  
  345.     // Shades - who doesn't love shades?  If we're not sending much else, throw 'em in if you got 'em
  346.     // (provided diff level is hard or better)
  347.     if ( difflevel > 1 )
  348.     {
  349.         if ( randomUnitTypes > 1 )
  350.         {
  351.             aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID7, 0, 3, 3);
  352.         }
  353.     }
  354.  
  355.     // It's time for serious pain if peltasts are being made.
  356.     // Two or three get tossed in the mix, plus a Nemean lion if one's around.  Eat that!
  357.     if (makingAge3Units)
  358.     {
  359.         aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID4, 0, 2, 3);
  360.         aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID6, 0, 1, 1);
  361.     }
  362.  
  363.    //Set the initial position.
  364.    aiPlanSetInitialPosition(newAttackPlanID, gatherPoint);
  365.    //Plan requires all need units to work (can be false).
  366.    aiPlanSetRequiresAllNeedUnits(newAttackPlanID, true);
  367.    //Activate the plan.
  368.    aiPlanSetActive(newAttackPlanID);
  369.  
  370.    //Now, save the attack plan ID appropriately.
  371.    aiPlanSetOrphan(attackPlan1ID, true);
  372.    attackPlan1ID=newAttackPlanID;
  373.  
  374.    //Increment our overall number of attacks.
  375.    numberAttacks++;
  376. }
  377.  
  378. //==============================================================================
  379. // setupRaidingAttack
  380. //
  381. // A couple of Hippikons head out to bash on villagers.
  382. //==============================================================================
  383. bool setupRaidingAttack(int playerID=-1)
  384. {
  385.     difflevel=aiGetWorldDifficulty();
  386.  
  387.     int randomPath=aiRandInt(2);
  388.    
  389.     //Info.
  390.     aiEcho("Raiding attack: attacking Player "+playerID+".");
  391.  
  392.    //If the player to attack doesn't match, init the attack.
  393.    if (attackPlayerID != playerID)
  394.    {
  395.       initAttack(playerID);
  396.       if (attackPlayerID < 0)
  397.          return(false);
  398.    }
  399.  
  400.    //Create an attack plan.
  401.    int newAttackPlanID=aiPlanCreate("Attack Player"+attackPlayerID+" Attempt"+numberAttacks, cPlanAttack);
  402.    if (newAttackPlanID < 0)
  403.       return(false);
  404.  
  405.    //Target player (required).  This must work.
  406.    if (aiPlanSetVariableInt(newAttackPlanID, cAttackPlanPlayerID, 0, attackPlayerID) == false)
  407.       return(false);
  408.  
  409.    //Gather point.
  410.     vector gatherPoint=kbGetBlockPosition("3989");
  411.  
  412.     //Set the target type.  This must work.
  413.    if (aiPlanSetNumberVariableValues(newAttackPlanID, cAttackPlanTargetTypeID, 2, true) == false)
  414.       return(false);
  415.  
  416.    //Unit types to attack - buildings...or, villagers on nightmare.
  417.     if ( difflevel == 3 )
  418.     {
  419.         aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 0, cUnitTypeVillagerGreek);
  420.     }
  421.     else
  422.     {
  423.         aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 0, cUnitTypeBuilding);
  424.     }
  425.     aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 1, cUnitTypeUnit);
  426.     
  427.    //Attack route.
  428.    if (randomPath == 0)
  429.     {
  430.         aiEcho("Attack going left.");
  431.       aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute1ID);
  432.     }
  433.    else
  434.     {
  435.         aiEcho("Attack going right.");
  436.       aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute2ID);
  437.     }
  438.  
  439.    //Set the gather point and gather point distance.
  440.    aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPoint);
  441.    aiPlanSetVariableFloat(newAttackPlanID, cAttackPlanGatherDistance, 0, 40.0);
  442.  
  443.    //Set up the attack route usage pattern.
  444.    aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRoutePattern, 0, cAttackPlanAttackRoutePatternRandom);
  445.    
  446.     //Add the basic unit type to the plan.
  447.    aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID2, attackMinimumGroupSize, attackMaximumGroupSize, attackMaximumGroupSize);
  448.  
  449.     //Set the initial position.
  450.    aiPlanSetInitialPosition(newAttackPlanID, gatherPoint);
  451.    
  452.     //Plan requires all need units to work (can be false).
  453.    aiPlanSetRequiresAllNeedUnits(newAttackPlanID, true);
  454.  
  455.    //Cheat like a bastard when we go out raiding.  'Cause we can.  :)
  456.    kbLookAtAllUnitsOnMap();
  457.  
  458.    //Activate the plan.
  459.    aiPlanSetActive(newAttackPlanID);
  460.  
  461.    //Now, save the attack plan ID appropriately.
  462.    aiPlanSetOrphan(attackPlan2ID, true);
  463.    attackPlan2ID=newAttackPlanID;
  464.  
  465.    //Increment our overall number of attacks.
  466.    numberAttacks++;
  467. }
  468.  
  469.  
  470. //==============================================================================
  471. // Droppin' some sentinel action.
  472. // If three enemies are close to the TC.
  473. //==============================================================================
  474. rule dropSentinel
  475.     minInterval 15
  476.     active
  477.     group ModerateAttackRules
  478. {
  479.     int numEnemies=-1;
  480.     vector leftTC=kbGetBlockPosition("4044");
  481.     numEnemies=checkForEnemies();
  482.  
  483.     if (numEnemies > 3)
  484.     {
  485.         aiEcho("Casting Sentinel.");
  486.         if ( aiCastGodPowerAtPosition(cTechSentinel, leftTC) == true )
  487.             xsDisableSelf();
  488.         else
  489.             aiEcho("Sentinel failed - try again later.");
  490.     }
  491. }
  492.  
  493. //==============================================================================
  494. // Attack Generator 1 - Hoplites and friends
  495. //==============================================================================
  496. rule attackGenerator1
  497.    minInterval 300
  498.    inactive
  499.    group AttackRules
  500. {
  501.    //See how many "idle" attack plans we have.  Don't create any more if we have
  502.    //idle plans.
  503.    int numberIdleAttackPlans=aiGetNumberIdlePlans(cPlanAttack);
  504.  
  505.    if (numberIdleAttackPlans > 0)
  506.       return;
  507.  
  508.    //If we have enough unassigned military units, create a new attack plan,
  509.    int numberAvailableUnits=aiNumberUnassignedUnits(attackerUnitTypeID1);
  510.    aiEcho("There are "+numberAvailableUnits+" hoplites available for a new attack.");
  511.    
  512.     if (numberAvailableUnits >= attackMinimumGroupSize)
  513.         setupHopliteAttack(1);
  514. }
  515.  
  516.  
  517. //==============================================================================
  518. // Attack Generator 2 - cavalry fun
  519. // They go after the villagers if they can
  520. //==============================================================================
  521. rule attackGenerator2
  522.    minInterval 360
  523.    inactive
  524.    group AttackRules
  525. {
  526.    //See how many "idle" attack plans we have.  Don't create any more if we have
  527.    //idle plans.
  528.    int numberIdleAttackPlans=aiGetNumberIdlePlans(cPlanAttack);
  529.  
  530.    if (numberIdleAttackPlans > 0)
  531.       return;
  532.  
  533.    //If we have enough unassigned military units, create a new attack plan,
  534.    int numberAvailableUnits=aiNumberUnassignedUnits(attackerUnitTypeID2);
  535.    aiEcho("There are "+numberAvailableUnits+" hippikon available for a new raiding attack.");
  536.    
  537.     if (numberAvailableUnits >= attackMinimumGroupSize)
  538.         setupRaidingAttack(1);
  539. }
  540.  
  541. //=====================================================================================
  542. // 90 seconds after starting to research Age 3.
  543. // ...start maintaining Peltasts, Nemean Lions, and Siege Ships.
  544. //=====================================================================================
  545. rule age3MaintainSetup
  546.     minInterval 90
  547.     inactive
  548.     group AttackRules
  549. {
  550.     int age3Active = kbGetTechStatus( cTechAge3Aphrodite );
  551.     
  552.     int peltastsMaintained = 4;
  553.     int lionsMaintained = 1;
  554.  
  555.     vector gatherPoint=kbGetBlockPosition("3989");
  556.     vector shipGatherPoint=kbGetBlockPosition("4446");
  557.     vector shipGatherPoint2=kbGetBlockPosition("4445");
  558.  
  559.    // Verify we're actually where we need to be, then maintain away.
  560.     if (age3Active == cTechStatusActive )
  561.     {
  562.         int maintainPlan1ID=aiPlanCreate("Maintain "+peltastsMaintained+" "+kbGetProtoUnitName(attackerUnitTypeID4), cPlanTrain);
  563.         if (maintainPlan1ID >= 0)
  564.         {
  565.             //Must set the type of unit to train.
  566.             aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanUnitType, 0, attackerUnitTypeID4);
  567.             //Makes total of fifteen, then stops.  Player deserves a break.
  568.             aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanNumberToTrain, 0, 15);
  569.             //Set the number of units to maintain in the world at one time.
  570.             aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanNumberToMaintain, 0, peltastsMaintained);
  571.             //Don't train units faster than every 30 seconds
  572.             aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanFrequency, 0, 30);
  573.             //Set a gather point.
  574.             aiPlanSetVariableVector(maintainPlan1ID, cTrainPlanGatherPoint, 0, gatherPoint);
  575.             //Activate the plan.
  576.             aiPlanSetActive(maintainPlan1ID);
  577.         }
  578.  
  579.         int maintainPlan2ID=aiPlanCreate("Maintain "+lionsMaintained+" "+kbGetProtoUnitName(attackerUnitTypeID6), cPlanTrain);
  580.         if (maintainPlan2ID >= 0)
  581.         {
  582.             //Must set the type of unit to train.
  583.             aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanUnitType, 0, attackerUnitTypeID6 );
  584.             //Makes total of six, then stops.  Player deserves a break.
  585.             aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanNumberToTrain, 0, 6);
  586.             //Set the number of units to maintain in the world at one time.
  587.             aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanNumberToMaintain, 0, lionsMaintained);
  588.             //Don't train units faster than every 90 seconds
  589.             aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanFrequency, 0, 90);
  590.             //Set a gather point.
  591.             aiPlanSetVariableVector(maintainPlan2ID, cTrainPlanGatherPoint, 0, gatherPoint);
  592.             //Activate the plan.
  593.             aiPlanSetActive(maintainPlan2ID);
  594.         }
  595.  
  596.         // Maintain plan for the siege ships, in case the player gets frisky.
  597.         int shipMaintain1ID=aiPlanCreate("Maintain 3 "+kbGetProtoUnitName( cUnitTypeSiegeShipGreek ), cPlanTrain);
  598.         if (shipMaintain1ID >= 0)
  599.         {
  600.             //Must set the type of unit to train.
  601.             aiPlanSetVariableInt(shipMaintain1ID, cTrainPlanUnitType, 0, cUnitTypeSiegeShipGreek);
  602.       
  603.             //Set the number of units to maintain in the world at one time.  Only one if less than hard, else three.
  604.             if ( difflevel >= 2 )
  605.             {
  606.                 aiPlanSetVariableInt(shipMaintain1ID, cTrainPlanNumberToMaintain, 0, 3);
  607.             }
  608.             else
  609.             {
  610.                 aiPlanSetVariableInt(shipMaintain1ID, cTrainPlanNumberToMaintain, 0, 1);
  611.             }
  612.       
  613.             // Slow training.
  614.             aiPlanSetVariableInt(shipMaintain1ID, cTrainPlanFrequency, 0, 105);
  615.             //Set a gather point.
  616.             aiPlanSetVariableVector(shipMaintain1ID, cTrainPlanGatherPoint, 0, shipGatherPoint);
  617.             //Activate the plan.
  618.             aiPlanSetActive(shipMaintain1ID);
  619.         }
  620.  
  621.         // Maintain plan for the ramming ships.
  622.         int shipMaintain2ID=aiPlanCreate("Maintain 4 "+kbGetProtoUnitName( cUnitTypeRammingShipGreek ), cPlanTrain);
  623.         if (shipMaintain2ID >= 0)
  624.         {
  625.             //Must set the type of unit to train.
  626.             aiPlanSetVariableInt(shipMaintain2ID, cTrainPlanUnitType, 0, cUnitTypeRammingShipGreek);
  627.             //Set the number of units to maintain in the world at one time.
  628.             aiPlanSetVariableInt(shipMaintain2ID, cTrainPlanNumberToMaintain, 0, 4);
  629.       
  630.             // Slow training.
  631.             aiPlanSetVariableInt(shipMaintain2ID, cTrainPlanFrequency, 0, 105);
  632.             //Set a gather point.
  633.             aiPlanSetVariableVector(shipMaintain2ID, cTrainPlanGatherPoint, 0, shipGatherPoint2);
  634.             //Activate the plan.
  635.             aiPlanSetActive(shipMaintain2ID);
  636.         }
  637.  
  638.  
  639.         aiEcho("*** Now maintaining some Age 3 units ***");
  640.         
  641.         // Set a shared boolean so that we can add Peltasts and Nemean Lions to the groups o' fun.
  642.         makingAge3Units = true;
  643.  
  644.         //Done, disable.
  645.         xsDisableSelf();    
  646.     }
  647. }
  648.  
  649. //==============================================================================
  650. // Tech Researching Rules - Age 2
  651. //==============================================================================
  652.  
  653. // Copper shields or copper mail?  
  654. // AI picks one, randomly, at about the six-minute mark
  655. rule researchCopperSomething
  656.    minInterval 360
  657.    active
  658.     group ModerateResearches
  659. {
  660.     int randomResearch=aiRandInt(2);
  661.    int planID=aiPlanCreate("Doing some random copper thing research (five minutes).", cPlanResearch);
  662.     
  663.    if (planID < 0)
  664.       return;
  665.  
  666.     if ( randomResearch == 1 )
  667.     {
  668.         aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechCopperShields);
  669.     }
  670.     else
  671.         aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechCopperMail);
  672.  
  673.    aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeAcademy);
  674.  
  675.     aiPlanSetActive(planID);
  676.     aiEcho("Researching some copper thing.");
  677.    //Done.
  678.    xsDisableSelf();
  679. }
  680.  
  681. // Medium Cavalry at about eight minutes.
  682. rule researchMediumCavalry
  683.    minInterval 480
  684.    active
  685.     group ModerateResearches
  686. {
  687.     int planID=aiPlanCreate("Researching Medium Cavalry.", cPlanResearch);
  688.     
  689.    if (planID < 0)
  690.       return;
  691.  
  692.     aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechMediumCavalry);
  693.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeStable);
  694.  
  695.     aiPlanSetActive(planID);
  696.     aiEcho("Researching medium cavalry.");
  697.    
  698.     //Done.
  699.    xsDisableSelf();
  700. }
  701.  
  702. // Try for Sarissa once, five minutes after starting on Age 3.
  703. // If you don't get it because of lack of favor, don't sweat it...
  704. // ...'cause you'll probably be getting a Nemean lion instead.
  705. rule researchSarissa
  706.    minInterval 300
  707.    inactive
  708.     group HardResearches
  709. {
  710.     int planID=aiPlanCreate("Researching Sarissa.", cPlanResearch);
  711.     
  712.    if (planID < 0)
  713.       return;
  714.  
  715.     aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechSarissa);
  716.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeArmory);
  717.  
  718.     aiPlanSetActive(planID);
  719.     aiEcho("Researching Sarissa.");
  720.    
  721.     //Done.
  722.    xsDisableSelf();
  723. }
  724.  
  725. // Heavy infantry or heavy archers?  One of 'em, seven minutes after starting on Age 3.
  726. rule researchHeavySomething
  727.    minInterval 420
  728.    inactive
  729.     group HardResearches
  730. {
  731.     int randomResearch=aiRandInt(2);
  732.    int planID=aiPlanCreate("Doing some random heavy research (seven minutes).", cPlanResearch);
  733.     
  734.    if (planID < 0)
  735.       return;
  736.  
  737.     if ( randomResearch == 1 )
  738.     {
  739.         aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechHeavyInfantry);
  740.     }
  741.     else
  742.         aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechHeavyArchers);
  743.  
  744.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeAcademy);
  745.  
  746.     aiPlanSetActive(planID);
  747.     aiEcho("Researching some heavy thing (either archers or infantry).");
  748.    //Done.
  749.    xsDisableSelf();
  750. }
  751.  
  752. //====================================================================================
  753. // Advance to Age 3 (Aphrodite) twelve minutes after the player builds his settlement.
  754. // Note he does not do this if the difficulty is Easy.
  755. //====================================================================================
  756. rule researchAge3
  757.     minInterval 720
  758.     inactive
  759.     group ModerateResearches
  760. {
  761.     int planID=aiPlanCreate("Advancing to Age 3 Aphrodite.", cPlanResearch);
  762.    
  763.     if (planID < 0)
  764.         return;
  765.     
  766.     aiEcho("*** ADVANCING TO AGE 3 ***");
  767.     aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechAge3Aphrodite);
  768.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeSettlementLevel1);
  769.     aiPlanSetActive(planID);
  770.     
  771.     // Start makin' Age 3 stuff.
  772.     xsEnableRule("age3MaintainSetup");
  773.  
  774.     // Some Age 3 researching - only if enemy is "hard" or better.
  775.     if ( difflevel >= 2 )
  776.     {
  777.         xsEnableRule("researchSarissa");
  778.         xsEnableRule("researchHeavySomething");
  779.     }
  780.  
  781.     // Done.
  782.     xsDisableSelf();
  783. }
  784.  
  785.  
  786. // Delay on the initial cavalry attack, for moderate level.
  787. // This holds off on cavalry attacks for an extra two minutes on moderate only.
  788. rule fireCavalryRule
  789.     minInterval 120
  790.     inactive
  791. {
  792.     xsEnableRule("attackGenerator2");
  793.     xsDisableSelf();
  794. }
  795.  
  796. //==============================================================================
  797. // Tech Researching Timing Randomization - taken out for now.
  798. //==============================================================================
  799. /*
  800. void randomizeResearchTiming(void)
  801. {
  802.     xsSetRuleMinInterval("researchCopperSomething", (aiRandInt(60) + 270));
  803.     xsSetRuleMinInterval("researchMediumCavalry", (aiRandInt(60) + 330));
  804. }
  805. */
  806.  
  807. //=====================================================================================
  808. // Attack Enabler.  This is called from the scenario with an AI Func effect
  809. // when the player claims his first settlement.
  810. //=====================================================================================
  811. void attackEnabler(int scriptCall = -1)
  812. {
  813.     aiEcho("Settlement claimed, enemy attacks and other fun enabled.");
  814.  
  815.     xsEnableRule("attackGenerator1");
  816.     xsEnableRule("researchAge3");
  817.     
  818.     // Enable cavalry attacks on all diff levels except easy.
  819.     if ( difflevel > 1 )
  820.     {
  821.         xsEnableRule("attackGenerator2");
  822.     }
  823.     else if ( difflevel == 1 )
  824.     {
  825.         xsEnableRule("fireCavalryRule");
  826.     }
  827. }
  828.  
  829. //=====================================================================================
  830. // defendDock.  This is called from the scenario when the player destroys the first
  831. // dock. It sets up a stiff defense at the other dock.
  832. //=====================================================================================
  833. void defendDock(int scriptCall = -1)
  834. {
  835.     aiEcho("*** FIRST DOCK DESTROYED - COLOSSI/SIEGE SHIPS GOING TO DEFEND OTHER DOCK ***");
  836.  
  837.     vector colossusDefense=kbGetBlockPosition("4346");
  838.     vector shipDefense=kbGetBlockPosition("4407");
  839.  
  840.     vector playerTown=kbGetBlockPosition("4429");
  841.  
  842.     int defendPlan1ID=aiPlanCreate("Colossus Defense", cPlanDefend);
  843.    if (defendPlan1ID >= 0)
  844.    {
  845.       // Defend point
  846.         if ( scriptCall == 0 )
  847.         {
  848.             colossusDefense=kbGetBlockPosition("4346");
  849.         }
  850.         else
  851.         {
  852.             colossusDefense=kbGetBlockPosition("4345");
  853.         }
  854.  
  855.       // Add the unit(s).
  856.       aiPlanAddUnitType(defendPlan1ID, cUnitTypeColossus, 0, 3, 3);
  857.                 
  858.       // Setup the vars.
  859.       aiPlanSetDesiredPriority(defendPlan1ID, 50);
  860.       aiPlanSetVariableVector(defendPlan1ID, cDefendPlanDefendPoint, 0, colossusDefense);
  861.       aiPlanSetVariableFloat(defendPlan1ID, cDefendPlanEngageRange, 0, 45);
  862.  
  863.         // aiPlanSetUnitStance(defendPlan1ID, cUnitStanceDefensive);
  864.         // aiPlanSetAllowUnderAttackResponse(defendPlan1ID, false);
  865.  
  866.       aiPlanSetActive(defendPlan1ID);
  867.     }
  868.  
  869.     // On levels greater than easy, siege ships go attack the player's town (via a defense plan).
  870.     if ( difflevel > 0 )
  871.     {
  872.         int defendPlan2ID=aiPlanCreate("Siege Ship Defense 1", cPlanDefend);
  873.         if (defendPlan2ID >= 0)
  874.         {
  875.             // Add the unit(s).
  876.             aiPlanAddUnitType(defendPlan2ID, cUnitTypeSiegeShipGreek, 0, 3, 3);
  877.                     
  878.             // Setup the vars.
  879.             aiPlanSetDesiredPriority(defendPlan2ID, 40);
  880.             aiPlanSetVariableVector(defendPlan2ID, cDefendPlanDefendPoint, 0, playerTown);
  881.             aiPlanSetVariableFloat(defendPlan2ID, cDefendPlanEngageRange, 0, 20);
  882.             aiPlanSetActive(defendPlan2ID);
  883.         }
  884.     }
  885.  
  886.     int defendPlan3ID=aiPlanCreate("Siege Ship Defense 2", cPlanDefend);
  887.    if (defendPlan3ID >= 0)
  888.    {
  889.       // Defend point.
  890.         if ( scriptCall == 0 )
  891.         {
  892.             shipDefense=kbGetBlockPosition("4407");
  893.         }
  894.         else
  895.         {
  896.             shipDefense=kbGetBlockPosition("4408");
  897.         }
  898.  
  899.       // Add the unit(s).
  900.       aiPlanAddUnitType(defendPlan3ID, cUnitTypeSiegeShipGreek, 0, 2, 2);
  901.         aiPlanAddUnitType(defendPlan3ID, cUnitTypeRammingShipGreek, 0, 4, 4);
  902.                 
  903.       // Setup the vars.
  904.       aiPlanSetDesiredPriority(defendPlan3ID, 60);
  905.       aiPlanSetVariableVector(defendPlan3ID, cDefendPlanDefendPoint, 0, shipDefense);
  906.       aiPlanSetVariableFloat(defendPlan3ID, cDefendPlanEngageRange, 0, 20);
  907.       aiPlanSetActive(defendPlan3ID);
  908.     }
  909. }
  910.  
  911. //==============================================================================
  912. // MAIN.
  913. //==============================================================================
  914. void main(void)
  915. {
  916.    //Startup.
  917.    miscStartup();
  918.     //DAL cut - randomizeResearchTiming();
  919.  
  920.     // Difficulty level adjustments - disable some researching and Sentinel usage.
  921.     if ( difflevel == 0 )
  922.     {
  923.         xsDisableRuleGroup("ModerateResearches");
  924.         xsDisableRuleGroup("ModerateAttackRules");
  925.     }
  926.  
  927.     if ( difflevel == 2 )
  928.     {
  929.         attackMinimumGroupSize=6;
  930.         attackMaximumGroupSize=8;
  931.     }
  932.  
  933.     if ( difflevel == 3 )
  934.     {
  935.         attackMinimumGroupSize=8;
  936.         attackMaximumGroupSize=11;
  937.     }
  938.  
  939.    //Share the number to maintain.
  940.    int numberToMaintain=attackMinimumGroupSize*2;
  941.  
  942.    //Share a common gather point.
  943.    vector gatherPoint=kbGetBlockPosition("3989");
  944.     vector gatherPoint2=kbGetBlockPosition("5593");
  945.  
  946.    //Create a simple plan to maintain X hoplites.
  947.    int maintainPlan1ID=aiPlanCreate("Maintain "+numberToMaintain+" "+kbGetProtoUnitName(attackerUnitTypeID1), cPlanTrain);
  948.    if (maintainPlan1ID >= 0)
  949.    {
  950.         //Must set the type of unit to train.
  951.       aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanUnitType, 0, attackerUnitTypeID1);
  952.       //You can limit the number of units that are ever trained by this plan with this call.
  953.       //aiPlanSetVariableInt(maintainPlanID, cTrainPlanNumberToTrain, 0, 25);
  954.       //Set the number of units to maintain in the world at one time.
  955.       aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanNumberToMaintain, 0, numberToMaintain);
  956.       //Don't train units faster than every 20 seconds
  957.       aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanFrequency, 0, 20);
  958.       //Set a gather point.
  959.       aiPlanSetVariableVector(maintainPlan1ID, cTrainPlanGatherPoint, 0, gatherPoint);
  960.       //Activate the plan.
  961.       aiPlanSetActive(maintainPlan1ID);
  962.    }
  963.  
  964.     //Create a simple plan to maintain X hippikons
  965.    int maintainPlan2ID=aiPlanCreate("Maintain "+numberToMaintain+" "+kbGetProtoUnitName(attackerUnitTypeID2), cPlanTrain);
  966.    if (maintainPlan2ID >= 0)
  967.    {
  968.         //Must set the type of unit to train.
  969.       aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanUnitType, 0, attackerUnitTypeID2);
  970.       //You can limit the number of units that are ever trained by this plan with this call.
  971.       //aiPlanSetVariableInt(maintainPlanID, cTrainPlanNumberToTrain, 0, 25);
  972.       //Set the number of units to maintain in the world at one time.
  973.       aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanNumberToMaintain, 0, numberToMaintain);
  974.       //Don't train units faster than every 45 seconds
  975.       aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanFrequency, 0, 45);
  976.       //Set a gather point.
  977.       aiPlanSetVariableVector(maintainPlan2ID, cTrainPlanGatherPoint, 0, gatherPoint2);
  978.       //Activate the plan.
  979.       aiPlanSetActive(maintainPlan2ID);
  980.    }
  981.  
  982.     //Create a simple plan to maintain a couple of minotaurs.
  983.    int maintainPlan3ID=aiPlanCreate("Maintain "+numberToMaintain+" "+kbGetProtoUnitName(attackerUnitTypeID5), cPlanTrain);
  984.    if (maintainPlan3ID >= 0)
  985.    {
  986.         //Must set the type of unit to train.
  987.       aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanUnitType, 0, attackerUnitTypeID5);
  988.       //Set the number of units to maintain in the world at one time.
  989.       aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanNumberToMaintain, 0, 3);
  990.       
  991.         //These things train slowly.  Like, every two minutes at best.
  992.         //That said, favor will probably be the limiter.  He's got two villagers makin' favor
  993.         //and I don't give him any extra.
  994.       aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanFrequency, 0, 120);
  995.       //Set a gather point.
  996.       aiPlanSetVariableVector(maintainPlan3ID, cTrainPlanGatherPoint, 0, gatherPoint);
  997.       //Activate the plan.
  998.       aiPlanSetActive(maintainPlan3ID);
  999.    }
  1000. }