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

  1. //==============================================================================
  2. // Scn34p2: AI Scenario Script for scenario 34 player 2
  3. //==============================================================================
  4. /*
  5.    AI owner:  Dave Leary
  6.    Scenario owner: Joe "the Golem" Gillum
  7.  
  8.     AI for the main enemy in 34.  This AI assembles large groups of units and
  9.     sends them to attack the player's settlement (after the player claims one).
  10.  
  11.                                     *** DIFFICULTY LEVEL NOTES ***
  12.  
  13.    Easy level - Fewer units in attack groups.  Myth attacks are limited to a
  14.     couple of cyclopses.  No Polyphemus.  One less Colossi guarding the temples.
  15.  
  16.    Moderate level - Base level.
  17.  
  18.    Difficult level - Attacks can grow more over time.  Myth attacks potentially
  19.     stronger.  More base units at start.
  20.  
  21.    Nightmare - Attacks can grow more over time.  Myth attacks potentially stronger.
  22.     Even more base units at start.
  23. */
  24. //==============================================================================
  25. // Set Town Location
  26. //==============================================================================
  27. void setTownLocation(void)
  28. {
  29.    //Look for the "Town Location" marker.
  30.    kbSetTownLocation(kbGetBlockPosition("3218"));
  31. }
  32.  
  33. //==============================================================================
  34. // miscStartup
  35. //==============================================================================
  36. void miscStartup(void)
  37. {
  38.     // Difficulty Level check.
  39.     int difflevel=-1;        
  40.     difflevel=aiGetWorldDifficulty();
  41.  
  42.    //Startup message(s).
  43.    aiEcho("");
  44.    aiEcho("");
  45.    aiEcho("Scn34P2 AI Start, filename='"+cFilename+"'.");
  46.     aiEcho("Difficulty Level="+difflevel+".");
  47.    //Spit out the map size.
  48.    aiEcho("  Map size is ("+kbGetMapXSize()+", "+kbGetMapZSize()+").");
  49.    //Cheat like a bastard.  Once only, though.
  50.    kbLookAtAllUnitsOnMap();
  51.    //Calculate some areas.
  52.    kbAreaCalculate(1200.0);
  53.    //Set our town location.
  54.    setTownLocation();
  55.     //Reset random seed
  56.     aiRandSetSeed();
  57.    
  58.     //Allocate all resources to the root escrow by setting percentage of military/economy to 0.
  59.     kbEscrowSetPercentage( cEconomyEscrowID, cAllResources, 0.0 );
  60.     kbEscrowSetPercentage( cMilitaryEscrowID, cAllResources, 0.0 );
  61.  
  62.     //Allocate all resources.
  63.    kbEscrowAllocateCurrentResources();
  64. }
  65.  
  66. //==============================================================================
  67. //==============================================================================
  68. // Attack stuff 
  69. //==============================================================================
  70. //==============================================================================
  71. //Shared variables.
  72. int numberAttacks=0;
  73. int attackPlayerID=-1;
  74.  
  75. //TODO: Decide how to rep attack group size.
  76. int attackMinimumGroupSize=4;
  77. int attackMaximumGroupSize=7;
  78.  
  79. //Attack 1 vars.
  80. int attackPlan1ID=-1;
  81.  
  82. //Attack 2 vars.
  83. int attackPlan2ID=-1;
  84.  
  85. // Route and path vars
  86. int attackRoute1ID=-1;
  87. int attackRoute2ID=-1;
  88. int attackRoute3ID=-1;
  89. int attackRoute4ID=-1;
  90.  
  91. int attackPath1ID=-1;
  92. int attackPath2ID=-1;
  93. int attackPath3ID=-1;
  94.  
  95. // Predefined plans (that get modified/defined later)
  96. int maintainPlan1ID=-1;
  97. int maintainPlan2ID=-1;
  98. int maintainPlan3ID=-1;
  99. int maintainPlan4ID=-1;
  100. int maintainPlan5ID=-1;
  101. int maintainPlan6ID=-1;
  102.  
  103. int heroMaintainPlan1ID=-1;
  104. int heroMaintainPlan2ID=-1;
  105. int heroMaintainPlan3ID=-1;
  106. int heroMaintainPlan4ID=-1;
  107.  
  108. int mythMaintainPlan1ID=-1;
  109. int mythMaintainPlan2ID=-1;
  110. int mythMaintainPlan3ID=-1;
  111.  
  112. int siegeMaintainPlan1ID=-1;
  113.  
  114. int exploreID=-1;
  115.  
  116. // Unit types.
  117. int attackerUnitTypeID1=cUnitTypeHoplite;
  118. int attackerUnitTypeID2=cUnitTypeHippikon;
  119. int attackerUnitTypeID3=cUnitTypeToxotes;
  120. int attackerUnitTypeID4=cUnitTypeHypaspist;
  121. int attackerUnitTypeID5=cUnitTypePeltast;
  122. int attackerUnitTypeID6=cUnitTypeProdromos;
  123.  
  124. int attackerSiegeTypeID1=cUnitTypePetrobolos;
  125. int attackerSiegeTypeID2=cUnitTypeHelepolis;
  126.  
  127. int attackerMythTypeID1=cUnitTypeCyclops;
  128. int attackerMythTypeID2=cUnitTypeHydra;
  129. int attackerMythTypeID3=cUnitTypeChimera;
  130.  
  131. int attackerHeroTypeID1=cUnitTypeHeroGreekTheseus;
  132. int attackerHeroTypeID2=cUnitTypeHeroGreekHippolyta;
  133. int attackerHeroTypeID3=cUnitTypeHeroGreekAtalanta;
  134. int attackerHeroTypeID4=cUnitTypeHeroGreekPolyphemus;
  135.  
  136. // Shared variables to handle the various settlements being claimed.
  137. bool settlement1Claimed=false;
  138. bool settlement2Claimed=false;
  139. bool settlement3Claimed=false;
  140. bool settlement4Claimed=false;
  141.  
  142. //=========================================================================================
  143. // Kidd's cool configQuery function: used to create attack routes, etc.  Oooh, lovin' that!
  144. //=========================================================================================
  145. 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 )
  146. {
  147.    if ( queryID == -1)
  148.    {
  149.       return(false);
  150.    }
  151.  
  152.    if (player != -1)
  153.       kbUnitQuerySetPlayerID(queryID, player);
  154.    
  155.    if (unitType != -1)
  156.       kbUnitQuerySetUnitType(queryID, unitType);
  157.  
  158.    if (action != -1)
  159.       kbUnitQuerySetActionType(queryID, action);
  160.  
  161.    if (state != -1)
  162.       kbUnitQuerySetState(queryID, state);
  163.  
  164.    if (center != vector(-1,-1,-1))
  165.    {
  166.       kbUnitQuerySetPosition(queryID, center);
  167.       if (sort == true)
  168.          kbUnitQuerySetAscendingSort(queryID, true);
  169.       if (radius != -1)
  170.          kbUnitQuerySetMaximumDistance(queryID, radius);
  171.    }
  172.    return(true);
  173. }
  174.  
  175. //==============================================================================
  176. // initAttack: Creates attack routes, etc.
  177. //==============================================================================
  178. void initAttack(int playerID=-1)
  179. {
  180.    //Destroy all previous attacks (if this isn't the player we're already attacking.
  181.     if (playerID != attackPlayerID)
  182.    {
  183.       //Reset the attack player ID.
  184.       attackPlayerID=-1;
  185.       //Destroy any previous attack plan.
  186.       aiPlanDestroy(attackPlan1ID);
  187.       attackPlan1ID=-1;
  188.       aiPlanDestroy(attackPlan2ID);
  189.       attackPlan2ID=-1;
  190.   
  191.       //Destroy our previous attack paths.
  192.       kbPathDestroy(attackPath1ID);
  193.       attackPath1ID=-1;
  194.       kbPathDestroy(attackPath2ID);
  195.       attackPath2ID=-1;
  196.       kbPathDestroy(attackPath3ID);
  197.       attackPath3ID=-1;
  198.  
  199.       //Destroy our previous attack routes.
  200.       attackRoute1ID=-1;
  201.       attackRoute2ID=-1;
  202.         attackRoute3ID=-1;
  203.         attackRoute4ID=-1;
  204.         
  205.       //Reset the number of attacks.
  206.       numberAttacks=0;
  207.    }
  208.     
  209.    //Save the player to attack.
  210.    attackPlayerID=playerID;
  211.  
  212.    vector gatherPoint=kbGetBlockPosition("3219");
  213.        
  214.     //Setup attack path 1 - top route
  215.     attackPath1ID=kbPathCreate("Attack Path 1");
  216.    kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("3255"));
  217.     kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("3256"));
  218.     kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("3215"));
  219.     kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("3257"));
  220.    
  221.     //Create attack route 1.
  222.    attackRoute1ID=kbCreateAttackRouteWithPath("Attack Route 1", gatherPoint, kbGetBlockPosition("3258"));
  223.    if (attackRoute1ID >= 0)
  224.       kbAttackRouteAddPath(attackRoute1ID, attackPath1ID);
  225.  
  226.    //Setup attack path 2 - down the middle
  227.    attackPath2ID=kbPathCreate("Attack Path 2");
  228.    kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("3251"));
  229.     kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("3220"));
  230.     kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("3252"));
  231.     
  232.    //Create attack route 2.
  233.    attackRoute2ID=kbCreateAttackRouteWithPath("Attack Route 2", gatherPoint, kbGetBlockPosition("3253"));
  234.       if (attackRoute2ID >= 0)
  235.       kbAttackRouteAddPath(attackRoute2ID, attackPath2ID);
  236.    //Create attack route 3 (uses same path as 2)
  237.    attackRoute3ID=kbCreateAttackRouteWithPath("Attack Route 3", gatherPoint, kbGetBlockPosition("3254"));
  238.       if (attackRoute3ID >= 0)
  239.       kbAttackRouteAddPath(attackRoute3ID, attackPath2ID);
  240.  
  241.     //Setup attack path 3 - to the bottom
  242.    attackPath3ID=kbPathCreate("Attack Path 3");
  243.    kbPathAddWaypoint(attackPath3ID, kbGetBlockPosition("3259"));
  244.     kbPathAddWaypoint(attackPath3ID, kbGetBlockPosition("3260"));
  245.     
  246.     //Create attack route 4.
  247.    attackRoute4ID=kbCreateAttackRouteWithPath("Attack Route 4", gatherPoint, kbGetBlockPosition("3261"));
  248.    if (attackRoute4ID >= 0)
  249.       kbAttackRouteAddPath(attackRoute4ID, attackPath3ID);
  250. }
  251.  
  252. //==============================================================================
  253. // setupMainAttack - uses primarily human units.
  254. //==============================================================================
  255. bool setupMainAttack(int playerID=-1, int primaryUnit=-1)
  256. {
  257.     // Difficulty Level check.
  258.     int difflevel=-1;        
  259.     difflevel=aiGetWorldDifficulty();
  260.  
  261.     // Random number to define which heroes the main group tries to grab.
  262.     int whichHeroes=aiRandInt(4);
  263.     vector gatherPoint=kbGetBlockPosition("3219");
  264.  
  265.     //Info.
  266.     aiEcho("Attacking Player "+playerID+".");
  267.  
  268.     //If the player to attack doesn't match, init the attack.
  269.    if (attackPlayerID != playerID)
  270.    {
  271.       initAttack(playerID);
  272.       if (attackPlayerID < 0)
  273.          return(false);
  274.    }
  275.  
  276.    //Create an attack plan.
  277.    int newAttackPlanID=aiPlanCreate("Attack Player"+attackPlayerID+" Attempt"+numberAttacks, cPlanAttack);
  278.    if (newAttackPlanID < 0)
  279.       return(false);
  280.  
  281.    //Target player (required).  This must work.
  282.    if (aiPlanSetVariableInt(newAttackPlanID, cAttackPlanPlayerID, 0, attackPlayerID) == false)
  283.       return(false);
  284.  
  285.    //Gather point.
  286.     if ( primaryUnit == attackerUnitTypeID2 )
  287.     {
  288.         gatherPoint=kbGetBlockPosition("3213");
  289.     }
  290.     else
  291.     {
  292.         gatherPoint=kbGetBlockPosition("3219");
  293.     }
  294.  
  295.     //Set the target type.  This must work.
  296.    if (aiPlanSetNumberVariableValues(newAttackPlanID, cAttackPlanTargetTypeID, 2, true) == false)
  297.       return(false);
  298.  
  299.     //Attack route selection.
  300.    if (settlement1Claimed == true)
  301.     {
  302.       aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute1ID);
  303.         aiEcho("AI Attack Path: chose north settlement to attack.");
  304.     }
  305.    else if (settlement4Claimed == true)
  306.     {
  307.       aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute4ID);
  308.         aiEcho("AI Attack Path: chose southern settlement to attack.");
  309.     }
  310.     else  // go down the middle if neither north nor south claimed.
  311.     {
  312.       aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute3ID);
  313.         aiEcho("AI Attack Path: chose center settlement to attack.");
  314.     }
  315.  
  316.    // Unit types to attack
  317.     // If Hippikons...villagers first
  318.     if ( primaryUnit == attackerUnitTypeID2 )
  319.     {
  320.         aiPlanSetNumberVariableValues(newAttackPlanID, cAttackPlanTargetTypeID, 3, true);
  321.  
  322.         aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 0, cUnitTypeVillagerGreek);
  323.         aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 1, cUnitTypeUnit);
  324.         aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 2, cUnitTypeBuilding);
  325.     }
  326.     // else if Hypaspists...
  327.     else
  328.     {
  329.         aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 0, cUnitTypeUnit);
  330.         aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 1, cUnitTypeBuilding);
  331.     }
  332.    
  333.     //Set the gather point and gather point distance.
  334.    aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPoint);
  335.    aiPlanSetVariableFloat(newAttackPlanID, cAttackPlanGatherDistance, 0, 20.0);
  336.  
  337.    //Set up the attack route usage pattern.
  338.    aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRoutePattern, 0, cAttackPlanAttackRoutePatternRandom);
  339.    
  340.     //Add the primary unit type to the plan.
  341.    aiPlanAddUnitType(newAttackPlanID, primaryUnit, attackMinimumGroupSize, attackMaximumGroupSize, attackMaximumGroupSize);
  342.  
  343.     // Additional units - depends on the primary unit.
  344.     // If Hippikons...none,
  345.     if ( primaryUnit == attackerUnitTypeID2 )
  346.     {
  347.         //Prodromosi if available.  This is the fast-attack raiding bunch.
  348.         if ( difflevel > 0 )
  349.         {
  350.             aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID6, 0, 4, 6);
  351.         }
  352.         else
  353.         {
  354.             aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID6, 0, 2, 4);
  355.         }
  356.     }
  357.     // else if Hypaspists...add Peltasts and Toxotes, and possibly Petroboli.  Bring the pain!
  358.     else
  359.     {
  360.         if ( difflevel > 0 )
  361.         {
  362.             aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID3, 0, 2, 6);
  363.             aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID5, 0, 4, 8);
  364.             aiPlanAddUnitType(newAttackPlanID, attackerSiegeTypeID1, 0, 2, 3);
  365.         }
  366.         else
  367.         {
  368.             aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID3, 0, 2, 4);
  369.             aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID5, 0, 2, 4);
  370.         }
  371.     }
  372.  
  373.     // Heroes - sometimes two, sometimes four, sometimes none.  None on Easy.
  374.     if ( difflevel > 0 )
  375.     {
  376.         if (whichHeroes == 0)
  377.         {
  378.             aiPlanAddUnitType(newAttackPlanID, attackerHeroTypeID1, 0, 1, 1);
  379.             aiPlanAddUnitType(newAttackPlanID, attackerHeroTypeID4, 0, 1, 1);
  380.         }
  381.  
  382.         if (whichHeroes <= 2)
  383.         {
  384.             aiPlanAddUnitType(newAttackPlanID, attackerHeroTypeID2, 0, 1, 1);
  385.             aiPlanAddUnitType(newAttackPlanID, attackerHeroTypeID3, 0, 1, 1);
  386.         }
  387.     }
  388.     
  389.    //Set the initial position.
  390.    aiPlanSetInitialPosition(newAttackPlanID, gatherPoint);
  391.    //Plan requires all need units to work (can be false).
  392.    aiPlanSetRequiresAllNeedUnits(newAttackPlanID, true);
  393.    //Activate the plan.
  394.    aiPlanSetActive(newAttackPlanID);
  395.  
  396.    //Now, save the attack plan ID appropriately.
  397.    aiPlanSetOrphan(attackPlan1ID, true);
  398.    attackPlan1ID=newAttackPlanID;
  399.  
  400.    //Increment our overall number of attacks.
  401.    numberAttacks++;
  402. }
  403.  
  404. //==============================================================================
  405. // setupMythAttack - uses primarily human units.
  406. //==============================================================================
  407. bool setupMythAttack(int playerID=-1)
  408. {
  409.     // Difficulty Level check.
  410.     int difflevel=-1;        
  411.     difflevel=aiGetWorldDifficulty();
  412.  
  413.     // Random number to define which heroes the main group tries to grab.
  414.     int whichHeroes=aiRandInt(4);
  415.  
  416.    //Gather point.
  417.     vector gatherPoint=kbGetBlockPosition("3218");
  418.  
  419.     //Info.
  420.     aiEcho("Attacking Player "+playerID+".");
  421.  
  422.     //If the player to attack doesn't match, init the attack.
  423.    if (attackPlayerID != playerID)
  424.    {
  425.       initAttack(playerID);
  426.       if (attackPlayerID < 0)
  427.          return(false);
  428.    }
  429.  
  430.     //Info.
  431.     aiEcho("Attacking Player "+playerID+".");
  432.  
  433.    //Create an attack plan.
  434.    int newAttackPlanID=aiPlanCreate("Attack Player"+attackPlayerID+" Attempt"+numberAttacks, cPlanAttack);
  435.    if (newAttackPlanID < 0)
  436.       return(false);
  437.  
  438.    //Target player (required).  This must work.
  439.    if (aiPlanSetVariableInt(newAttackPlanID, cAttackPlanPlayerID, 0, attackPlayerID) == false)
  440.       return(false);
  441.  
  442.     //Set the target type.  This must work.
  443.    if (aiPlanSetNumberVariableValues(newAttackPlanID, cAttackPlanTargetTypeID, 2, true) == false)
  444.       return(false);
  445.  
  446.     //Attack route selection.
  447.    if (settlement1Claimed == true)
  448.     {
  449.       aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute1ID);
  450.         aiEcho("AI Attack Path: chose north settlement to attack.");
  451.     }
  452.    else if (settlement4Claimed == true)
  453.     {
  454.       aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute4ID);
  455.         aiEcho("AI Attack Path: chose southern settlement to attack.");
  456.     }
  457.     else  // go down the middle if neither north nor south claimed.
  458.     {
  459.       aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute3ID);
  460.         aiEcho("AI Attack Path: chose center settlement to attack.");
  461.     }
  462.  
  463.    // Unit types to attack 
  464.     aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 0, cUnitTypeUnit);
  465.     aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 1, cUnitTypeBuilding);
  466.    
  467.     //Set the gather point and gather point distance.
  468.    aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPoint);
  469.    aiPlanSetVariableFloat(newAttackPlanID, cAttackPlanGatherDistance, 0, 20.0);
  470.  
  471.    //Set up the attack route usage pattern.
  472.    aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRoutePattern, 0, cAttackPlanAttackRoutePatternRandom);
  473.    
  474.     //Cyclopses are the base myth unit - take up to 6, plus chimeras, if available.  Make sure you got two.  :)
  475.     if ( difflevel > 0 )
  476.     {
  477.         aiPlanAddUnitType(newAttackPlanID, attackerMythTypeID1, 2, 2, 6);
  478.         aiPlanAddUnitType(newAttackPlanID, attackerMythTypeID3, 0, 2, 3);
  479.     }
  480.     else
  481.     {
  482.         aiPlanAddUnitType(newAttackPlanID, attackerMythTypeID1, 1, 1, 1);
  483.     }
  484.  
  485.     // Heroes - half the time, the first two heros if available.  Mixed myth/heroes = pain!
  486.     if (whichHeroes <= 1)
  487.     {
  488.         aiPlanAddUnitType(newAttackPlanID, attackerHeroTypeID1, 0, 1, 1);
  489.         if ( difflevel > 0 )
  490.         {
  491.             aiPlanAddUnitType(newAttackPlanID, attackerHeroTypeID2, 0, 1, 1);
  492.         }
  493.     }
  494.  
  495.     // Once in a while, Polyphemus instead.  :)
  496.     if (whichHeroes == 4)
  497.     {
  498.         if ( difflevel > 0 )
  499.         {
  500.             aiPlanAddUnitType(newAttackPlanID, attackerHeroTypeID4, 0, 1, 1);
  501.         }
  502.     }
  503.  
  504.    //Set the initial position.
  505.    aiPlanSetInitialPosition(newAttackPlanID, gatherPoint);
  506.    //Plan requires all need units to work (can be false).
  507.    aiPlanSetRequiresAllNeedUnits(newAttackPlanID, true);
  508.    //Activate the plan.
  509.    aiPlanSetActive(newAttackPlanID);
  510.  
  511.    //Now, save the attack plan ID appropriately.
  512.    aiPlanSetOrphan(attackPlan2ID, true);
  513.    attackPlan2ID=newAttackPlanID;
  514.  
  515.    //Increment our overall number of attacks.
  516.    numberAttacks++;
  517. }
  518.  
  519. //==============================================================================
  520. // Attack Generator 1 - Primary = hippikons or hypaspists, randomly.
  521. //==============================================================================
  522. rule attackGenerator1
  523.    minInterval 235
  524.    inactive
  525.    group AttackRules
  526. {
  527.    //See how many "idle" attack plans we have.  Don't create any more if we have
  528.    //idle plans.
  529.    int numberIdleAttackPlans=aiGetNumberIdlePlans(cPlanAttack);
  530.  
  531.    if (numberIdleAttackPlans > 0)
  532.       return;
  533.     
  534.     // Random primary unit for the attack, either hippikons or hypaspists.
  535.     int attackerType=-1;
  536.     int whichAttacker=aiRandInt(2);
  537.  
  538.     if ( whichAttacker == 0 )
  539.     {
  540.         attackerType=attackerUnitTypeID2;
  541.         aiEcho("Primary Unit = Hippikons");
  542.     }
  543.     else
  544.     {
  545.         attackerType=attackerUnitTypeID4;
  546.         aiEcho("Primary Unit = Hypaspists");
  547.     }
  548.  
  549.     //If we have enough unassigned military units, create a new attack plan.
  550.    int numberAvailableUnits=aiNumberUnassignedUnits(attackerType);
  551.    aiEcho("There are "+numberAvailableUnits+" of the primary unit available for a new attack.");
  552.    
  553.     if (numberAvailableUnits >= attackMinimumGroupSize)
  554.         setupMainAttack(1, attackerType);
  555. }
  556.  
  557. //==============================================================================
  558. // Attack Generator 2 - Myth unit fun, with cyclopses and friends.
  559. //==============================================================================
  560. rule attackGenerator2
  561.    minInterval 320
  562.    inactive
  563.    group AttackRules
  564. {
  565.    //See how many "idle" attack plans we have.  Don't create any more if we have
  566.    //idle plans.
  567.     //
  568.     // DAL - screw this, we're sending them.  :)
  569.     /*
  570.    int numberIdleAttackPlans=aiGetNumberIdlePlans(cPlanAttack);
  571.  
  572.    if (numberIdleAttackPlans > 0)
  573.       return;
  574.     */
  575.     
  576.     //If we have enough unassigned military units, create a new attack plan.
  577.    int numberAvailableUnits=aiNumberUnassignedUnits(cUnitTypeCyclops);
  578.    aiEcho("There are "+numberAvailableUnits+" of the primary unit available for a new attack.");
  579.    
  580.     if (numberAvailableUnits >= 2)
  581.         setupMythAttack(1);
  582. }
  583.  
  584. // Start adding chimeras to the mix.
  585. rule chimeraEnabler
  586.     minInterval 1000
  587.     inactive
  588.     group AttackRules
  589. {
  590.     aiEcho("*** CHIMERA ENABLER FIRING ***");
  591.     vector gatherPointTemple2=kbGetBlockPosition("3217");
  592.  
  593.    //Create a simple plan to maintain cyclopses (6 of 'em).  Chimeras forthcoming.
  594.    mythMaintainPlan3ID=aiPlanCreate("Maintain "+kbGetProtoUnitName(attackerMythTypeID3), cPlanTrain);
  595.    if (mythMaintainPlan3ID >= 0)
  596.    {
  597.         //Must set the type of unit to train.
  598.       aiPlanSetVariableInt(mythMaintainPlan3ID, cTrainPlanUnitType, 0, attackerMythTypeID3);
  599.       //Set the number of units to maintain in the world at one time.
  600.       aiPlanSetVariableInt(mythMaintainPlan3ID, cTrainPlanNumberToMaintain, 0, 10);
  601.       //Don't train units too fast.
  602.       aiPlanSetVariableInt(mythMaintainPlan3ID, cTrainPlanFrequency, 0, 120);
  603.       //Set a gather point.
  604.       aiPlanSetVariableVector(mythMaintainPlan3ID, cTrainPlanGatherPoint, 0, gatherPointTemple2);
  605.       //Activate the plan.
  606.       aiPlanSetActive(mythMaintainPlan3ID);
  607.    }
  608.     xsDisableSelf();
  609. }
  610.  
  611. //==============================================================================
  612. // Favor cheat
  613. //==============================================================================
  614. rule favorCheat
  615.    minInterval 60
  616.    inactive
  617.    group AttackRules
  618. {
  619.     // Cheat for favor.  That Gargarensis is a bastard.
  620.     aiResourceCheat( 2, cResourceFavor, 80.0 );
  621. }
  622.  
  623. // Four minutes after a settlement is claimed, start maintaining myth units and
  624. // get ready to send attacks.
  625. rule mythAttackEnabler
  626.     minInterval 360
  627.     inactive
  628.     group AttackRules
  629. {
  630.     aiEcho("*** MYTH ATTACK ENABLER FIRING ***");
  631.     vector gatherPointTemple1=kbGetBlockPosition("3216");
  632.  
  633.    //Create a simple plan to maintain cyclopses (6 of 'em).  Chimeras forthcoming.
  634.    mythMaintainPlan1ID=aiPlanCreate("Maintain "+kbGetProtoUnitName(attackerMythTypeID1), cPlanTrain);
  635.    if (mythMaintainPlan1ID >= 0)
  636.    {
  637.         //Must set the type of unit to train.
  638.       aiPlanSetVariableInt(mythMaintainPlan1ID, cTrainPlanUnitType, 0, attackerMythTypeID1);
  639.       //Set the number of units to maintain in the world at one time.
  640.       aiPlanSetVariableInt(mythMaintainPlan1ID, cTrainPlanNumberToMaintain, 0, 10);
  641.       //Don't train units too fast.
  642.       aiPlanSetVariableInt(mythMaintainPlan1ID, cTrainPlanFrequency, 0, 75);
  643.       //Set a gather point.
  644.       aiPlanSetVariableVector(mythMaintainPlan1ID, cTrainPlanGatherPoint, 0, gatherPointTemple1);
  645.       //Activate the plan.
  646.       aiPlanSetActive(mythMaintainPlan1ID);
  647.    }
  648.  
  649.     xsEnableRule("attackGenerator2");
  650.     xsEnableRule("chimeraEnabler");
  651.     xsEnableRule("favorCheat");
  652.     xsDisableSelf();
  653. }
  654.  
  655. rule defendPlan1
  656.     minInterval 60
  657.     inactive
  658.     group DefendRules
  659. {
  660.    int defendPlan1ID=aiPlanCreate("Main Gate Defense", cPlanDefend);
  661.    if (defendPlan1ID >= 0)
  662.    {
  663.       //Main gate location
  664.       vector frontGate=kbGetBlockPosition("3220");
  665.  
  666.       //Add the unit(s).
  667.       aiPlanAddUnitType(defendPlan1ID, attackerUnitTypeID1, 0, 4, 8);
  668.         aiPlanAddUnitType(defendPlan1ID, attackerUnitTypeID3, 0, 2, 4);
  669.  
  670.       //Setup the vars.
  671.       aiPlanSetDesiredPriority(defendPlan1ID, 40);
  672.       aiPlanSetVariableVector(defendPlan1ID, cDefendPlanDefendPoint, 0, frontGate);
  673.       aiPlanSetVariableFloat(defendPlan1ID, cDefendPlanEngageRange, 0, 20);
  674.       aiPlanSetActive(defendPlan1ID);
  675.     }
  676.     xsDisableSelf();
  677. }
  678.  
  679. rule defendPlan2
  680.     minInterval 60
  681.     inactive
  682.     group DefendRules
  683. {
  684.    int defendPlan2ID=aiPlanCreate("North Side Defense", cPlanDefend);
  685.    if (defendPlan2ID >= 0)
  686.    {
  687.       //North side defense plan
  688.       vector northSide=kbGetBlockPosition("3257");
  689.  
  690.       //Add the unit(s).
  691.       aiPlanAddUnitType(defendPlan2ID, attackerUnitTypeID1, 0, 4, 8);
  692.         aiPlanAddUnitType(defendPlan2ID, attackerUnitTypeID3, 0, 2, 4);
  693.  
  694.       //Setup the vars.
  695.       aiPlanSetDesiredPriority(defendPlan2ID, 40);
  696.       aiPlanSetVariableVector(defendPlan2ID, cDefendPlanDefendPoint, 0, northSide);
  697.       aiPlanSetVariableFloat(defendPlan2ID, cDefendPlanEngageRange, 0, 20);
  698.       aiPlanSetActive(defendPlan2ID);
  699.     }
  700.     xsDisableSelf();
  701. }
  702.  
  703. //==============================================================================
  704. // Siege Units Fun - start addin' in Petroboli at the ten-minute mark, even
  705. // if the player hasn't landed.
  706. //==============================================================================
  707. rule maintainSiege
  708.     minInterval 600
  709.     active
  710.     group AttackRules
  711. {
  712.     // Difficulty Level check.
  713.     int difflevel=-1;        
  714.     difflevel=aiGetWorldDifficulty();
  715.  
  716.     vector gatherPointSiege=kbGetBlockPosition("3260");
  717.     vector gatherPointStables=kbGetBlockPosition("3213");
  718.     vector gatherPointStables2=kbGetBlockPosition("4105");
  719.  
  720.     //Create a simple plan to maintain petroboli.
  721.     siegeMaintainPlan1ID=aiPlanCreate("Maintain "+kbGetProtoUnitName(attackerUnitTypeID1), cPlanTrain);
  722.    if (siegeMaintainPlan1ID >= 0)
  723.    {
  724.         //Must set the type of unit to train.
  725.       aiPlanSetVariableInt(siegeMaintainPlan1ID, cTrainPlanUnitType, 0, attackerSiegeTypeID1);
  726.       //Set the number of units to maintain in the world at one time.
  727.         if ( difflevel < 2 )
  728.         {
  729.             aiPlanSetVariableInt(siegeMaintainPlan1ID, cTrainPlanNumberToMaintain, 0, 2);
  730.             aiPlanSetVariableInt(siegeMaintainPlan1ID, cTrainPlanFrequency, 0, 75);
  731.         }
  732.         else
  733.         {
  734.             aiPlanSetVariableInt(siegeMaintainPlan1ID, cTrainPlanNumberToMaintain, 0, 3);
  735.             aiPlanSetVariableInt(siegeMaintainPlan1ID, cTrainPlanFrequency, 0, 50);
  736.         }
  737.       //Set a gather point.
  738.       aiPlanSetVariableVector(siegeMaintainPlan1ID, cTrainPlanGatherPoint, 0, gatherPointSiege);
  739.       //Activate the plan.
  740.       aiPlanSetActive(siegeMaintainPlan1ID);
  741.    }
  742.  
  743.     //Time for Prodromosi things.  Train 'em slowly.
  744.     maintainPlan6ID=aiPlanCreate("Maintain "+kbGetProtoUnitName(attackerUnitTypeID6), cPlanTrain);
  745.    if (maintainPlan6ID >= 0)
  746.    {
  747.         //Must set the type of unit to train.
  748.       aiPlanSetVariableInt(maintainPlan6ID, cTrainPlanUnitType, 0, attackerUnitTypeID6);
  749.       //Set the number of units to maintain in the world at one time.
  750.       aiPlanSetVariableInt(maintainPlan6ID, cTrainPlanNumberToMaintain, 0, 6);
  751.       //Don't train units too fast
  752.       aiPlanSetVariableInt(maintainPlan6ID, cTrainPlanFrequency, 0, 75);
  753.       //Set a gather point.
  754.       aiPlanSetVariableVector(maintainPlan6ID, cTrainPlanGatherPoint, 0, gatherPointStables2);
  755.       //Activate the plan.
  756.       aiPlanSetActive(maintainPlan6ID);
  757.    }
  758.     xsDisableSelf();
  759. }
  760.  
  761.  
  762. //==============================================================================
  763. // Attack size grower.
  764. //==============================================================================
  765. rule attackGrower
  766.    minInterval 420
  767.     inactive
  768.     group AttackRules
  769. {
  770.     // Difficulty Level check.
  771.     int difflevel=-1;        
  772.     difflevel=aiGetWorldDifficulty();
  773.  
  774.     // On lower difficulties, minimum group limited to 10.
  775.     if ( difflevel < 2 )
  776.     {
  777.         //Don't grow to more than 10 for a minimum.
  778.         if (attackMinimumGroupSize >= 10)
  779.         {
  780.             xsDisableSelf();
  781.             return;
  782.         }
  783.     }
  784.     else
  785.     {
  786.         //Don't grow to more than 15 for a minimum.
  787.         if (attackMinimumGroupSize >= 15)
  788.         {
  789.             xsDisableSelf();
  790.             return;
  791.         }
  792.     }
  793.  
  794.    //Increase our attack size.
  795.    attackMinimumGroupSize++;
  796.    attackMaximumGroupSize++;
  797.    aiEcho("Attack group size grown: Min="+attackMinimumGroupSize+", Max="+attackMaximumGroupSize+".");
  798.  
  799.    //Bump up our maintain size.
  800.    int numberToMaintain=attackMinimumGroupSize*2;
  801.  
  802.    aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanNumberToMaintain, 0, numberToMaintain);
  803.    aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanNumberToMaintain, 0, numberToMaintain);
  804. }
  805.  
  806. //=====================================================================================
  807. // Settlement Selector.  This is called from the scenario with an AI Func effect
  808. // when the player claims his first settlement, which will serve as the focus of the
  809. // AI's attack routes.
  810. //=====================================================================================
  811. void settlementSelector(int whichone = -1)
  812. {
  813.     aiEcho("*** Settlement claimed, enemy attacks and other fun enabled..***");
  814.    xsEnableRule("attackGenerator1");
  815.  
  816.    switch(whichone)
  817.    {
  818.         case 0:
  819.       {
  820.             settlement1Claimed=true;
  821.             xsEnableRule("defendPlan2");
  822.          break;
  823.       }
  824.         case 1:
  825.       {
  826.             settlement2Claimed=true;
  827.             xsEnableRule("defendPlan1");
  828.          break;
  829.       }
  830.         case 2:
  831.       {
  832.             settlement3Claimed=true;
  833.             xsEnableRule("defendPlan1");
  834.          break;
  835.       }
  836.         case 3:
  837.         {
  838.             settlement4Claimed=true;
  839.             xsEnableRule("defendPlan1");
  840.             break;
  841.         }
  842.     }
  843.     // Once a settlement is claimed, myth unit attacks are prepped.
  844.     xsEnableRule("mythAttackEnabler");
  845.  
  846.     // Prepare to grow attacks.
  847.     xsEnableRule("attackGrower");
  848. }
  849.  
  850. //==============================================================================
  851. // Difficulty Level Adjustments
  852. //==============================================================================
  853. void difficultyLevelAdjustments(void)
  854. {
  855.     // Difficulty Level check.
  856.     int difflevel=-1;        
  857.     difflevel=aiGetWorldDifficulty();
  858.  
  859.    switch(difflevel)
  860.    {
  861.         case 0:
  862.       {
  863.             attackMinimumGroupSize=3;
  864.             attackMaximumGroupSize=5;
  865.          break;
  866.       }
  867.         case 1:
  868.       {
  869.          break;
  870.       }
  871.         case 2:
  872.       {
  873.             attackMinimumGroupSize=8;
  874.             attackMaximumGroupSize=10;
  875.          break;
  876.       }
  877.         case 3:
  878.         {
  879.             attackMinimumGroupSize=9;
  880.             attackMaximumGroupSize=13;
  881.             break;
  882.         }
  883.     }
  884. }
  885.  
  886. //==============================================================================
  887. // Maintain stuff - separated out for sanity's sake
  888. //==============================================================================
  889. void maintainStuff(void)
  890. {
  891.     // Difficulty Level check.
  892.     int difflevel=-1;        
  893.     difflevel=aiGetWorldDifficulty();
  894.  
  895.    //Various gather points, whee!
  896.    vector gatherPointAcademy=kbGetBlockPosition("3212");
  897.     vector gatherPointStables=kbGetBlockPosition("3213");
  898.     vector gatherPointArcheryRange=kbGetBlockPosition("3214");
  899.  
  900.     vector gatherPointTemple1=kbGetBlockPosition("3216");
  901.     vector gatherPointTemple2=kbGetBlockPosition("3217");
  902.  
  903.    //Bump up our maintain size.
  904.    int numberToMaintain=attackMinimumGroupSize*2;
  905.  
  906.    //Create a simple plan to maintain hoplites.
  907.     maintainPlan1ID=aiPlanCreate("Maintain "+kbGetProtoUnitName(attackerUnitTypeID1), cPlanTrain);
  908.    if (maintainPlan1ID >= 0)
  909.    {
  910.         //Must set the type of unit to train.
  911.       aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanUnitType, 0, attackerUnitTypeID1);
  912.       //Set the number of units to maintain in the world at one time.
  913.       aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanNumberToMaintain, 0, numberToMaintain);
  914.       //Don't train units too fast
  915.         if ( difflevel > 1 )
  916.         {
  917.             aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanFrequency, 0, 25);
  918.         }
  919.         else if ( difflevel == 0 )
  920.         {
  921.             aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanFrequency, 0, 70);
  922.         }
  923.         else 
  924.         {
  925.             aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanFrequency, 0, 45);
  926.         }
  927.       //Set a gather point.
  928.       aiPlanSetVariableVector(maintainPlan1ID, cTrainPlanGatherPoint, 0, gatherPointAcademy);
  929.       //Activate the plan.
  930.       aiPlanSetActive(maintainPlan1ID);
  931.    }
  932.     
  933.     //Create a plan to maintain hippikons.
  934.    maintainPlan2ID=aiPlanCreate("Maintain "+kbGetProtoUnitName(attackerUnitTypeID2), cPlanTrain);
  935.    if (maintainPlan2ID >= 0)
  936.    {
  937.         //Must set the type of unit to train.
  938.       aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanUnitType, 0, attackerUnitTypeID2);
  939.       //Set the number of units to maintain in the world at one time.
  940.       aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanNumberToMaintain, 0, numberToMaintain);
  941.       //Don't train units too fast
  942.         if ( difflevel > 1 )
  943.         {
  944.             aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanFrequency, 0, 30);
  945.         }
  946.         else
  947.         {
  948.             aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanFrequency, 0, 55);
  949.         }
  950.       //Set a gather point.
  951.       aiPlanSetVariableVector(maintainPlan2ID, cTrainPlanGatherPoint, 0, gatherPointStables);
  952.       //Activate the plan.
  953.       aiPlanSetActive(maintainPlan2ID);
  954.    }
  955.  
  956.     //Create a plan to maintain toxotes.
  957.    maintainPlan3ID=aiPlanCreate("Maintain "+kbGetProtoUnitName(attackerUnitTypeID3), cPlanTrain);
  958.    if (maintainPlan3ID >= 0)
  959.    {
  960.         //Must set the type of unit to train.
  961.       aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanUnitType, 0, attackerUnitTypeID3);
  962.       //Set the number of units to maintain in the world at one time.
  963.       aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanNumberToMaintain, 0, 8);
  964.       if ( difflevel == 0 )
  965.         {
  966.             aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanFrequency, 0, 70);
  967.         }
  968.         else
  969.         {
  970.             aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanFrequency, 0, 40);
  971.         }
  972.       //Set a gather point.
  973.       aiPlanSetVariableVector(maintainPlan3ID, cTrainPlanGatherPoint, 0, gatherPointArcheryRange);
  974.       //Activate the plan.
  975.       aiPlanSetActive(maintainPlan3ID);
  976.    }
  977.  
  978.     //Create a plan to maintain hypaspists
  979.    maintainPlan4ID=aiPlanCreate("Maintain "+kbGetProtoUnitName(attackerUnitTypeID4), cPlanTrain);
  980.    if (maintainPlan4ID >= 0)
  981.    {
  982.         //Must set the type of unit to train.
  983.       aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanUnitType, 0, attackerUnitTypeID4);
  984.       //Set the number of units to maintain in the world at one time.
  985.       aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanNumberToMaintain, 0, numberToMaintain);
  986.       //Don't train units too fast
  987.         if ( difflevel == 0 )
  988.         {
  989.             aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanFrequency, 0, 60);
  990.         }
  991.         else
  992.         {
  993.             aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanFrequency, 0, 40);
  994.         }
  995.       //Set a gather point.
  996.       aiPlanSetVariableVector(maintainPlan4ID, cTrainPlanGatherPoint, 0, gatherPointAcademy);
  997.       //Activate the plan.
  998.       aiPlanSetActive(maintainPlan4ID);
  999.    }
  1000.  
  1001.     //Create a plan to maintain peltasts
  1002.    maintainPlan5ID=aiPlanCreate("Maintain "+kbGetProtoUnitName(attackerUnitTypeID5), cPlanTrain);
  1003.    if (maintainPlan5ID >= 0)
  1004.    {
  1005.         //Must set the type of unit to train.
  1006.       aiPlanSetVariableInt(maintainPlan5ID, cTrainPlanUnitType, 0, attackerUnitTypeID5);
  1007.       //Set the number of units to maintain in the world at one time.
  1008.         if ( difflevel == 0 )
  1009.         {
  1010.             aiPlanSetVariableInt(maintainPlan5ID, cTrainPlanNumberToMaintain, 0, 6);
  1011.         }
  1012.         else
  1013.         {
  1014.             aiPlanSetVariableInt(maintainPlan5ID, cTrainPlanNumberToMaintain, 0, 8);
  1015.         }
  1016.       //Don't train units too fast
  1017.         if ( difflevel == 0 )
  1018.         {
  1019.             aiPlanSetVariableInt(maintainPlan5ID, cTrainPlanFrequency, 0, 70);
  1020.         }
  1021.         else
  1022.         {
  1023.             aiPlanSetVariableInt(maintainPlan5ID, cTrainPlanFrequency, 0, 45);
  1024.         }
  1025.       //Set a gather point.
  1026.       aiPlanSetVariableVector(maintainPlan5ID, cTrainPlanGatherPoint, 0, gatherPointArcheryRange);
  1027.       //Activate the plan.
  1028.       aiPlanSetActive(maintainPlan5ID);
  1029.    }
  1030.  
  1031.     // Maintain all four heroes at all times, but on long timers (180 for first three, 240 for Polyphemus)
  1032.    heroMaintainPlan1ID=aiPlanCreate("Maintain "+kbGetProtoUnitName(attackerHeroTypeID1), cPlanTrain);
  1033.    if (heroMaintainPlan1ID >= 0)
  1034.    {
  1035.         //Must set the type of unit to train.
  1036.       aiPlanSetVariableInt(heroMaintainPlan1ID, cTrainPlanUnitType, 0, attackerHeroTypeID1);
  1037.       //Set the number of units to maintain in the world at one time.
  1038.       aiPlanSetVariableInt(heroMaintainPlan1ID, cTrainPlanNumberToMaintain, 0, 1);
  1039.       //Don't train units too fast
  1040.       aiPlanSetVariableInt(heroMaintainPlan1ID, cTrainPlanFrequency, 0, 195);
  1041.         // Stop using stupid fortresses and use settlements instead.
  1042.         aiPlanSetVariableInt(heroMaintainPlan1ID, cTrainPlanBuildFromType, 0, cUnitTypeSettlementLevel1);
  1043.       //Set a gather point.
  1044.       aiPlanSetVariableVector(heroMaintainPlan1ID, cTrainPlanGatherPoint, 0, gatherPointTemple1);
  1045.       //Activate the plan.
  1046.       aiPlanSetActive(heroMaintainPlan1ID);
  1047.    }
  1048.  
  1049.    heroMaintainPlan2ID=aiPlanCreate("Maintain "+kbGetProtoUnitName(attackerHeroTypeID2), cPlanTrain);
  1050.    if (heroMaintainPlan2ID >= 0)
  1051.    {
  1052.         //Must set the type of unit to train.
  1053.       aiPlanSetVariableInt(heroMaintainPlan2ID, cTrainPlanUnitType, 0, attackerHeroTypeID2);
  1054.       //Set the number of units to maintain in the world at one time.
  1055.       aiPlanSetVariableInt(heroMaintainPlan2ID, cTrainPlanNumberToMaintain, 0, 1);
  1056.       //Don't train units too fast
  1057.       aiPlanSetVariableInt(heroMaintainPlan2ID, cTrainPlanFrequency, 0, 205);
  1058.         // Stop using stupid fortresses and use settlements instead.
  1059.         aiPlanSetVariableInt(heroMaintainPlan2ID, cTrainPlanBuildFromType, 0, cUnitTypeSettlementLevel1);
  1060.       //Set a gather point.
  1061.       aiPlanSetVariableVector(heroMaintainPlan2ID, cTrainPlanGatherPoint, 0, gatherPointTemple2);
  1062.       //Activate the plan.
  1063.       aiPlanSetActive(heroMaintainPlan2ID);
  1064.    }
  1065.  
  1066.    heroMaintainPlan3ID=aiPlanCreate("Maintain "+kbGetProtoUnitName(attackerHeroTypeID3), cPlanTrain);
  1067.    if (heroMaintainPlan3ID >= 0)
  1068.    {
  1069.         //Must set the type of unit to train.
  1070.       aiPlanSetVariableInt(heroMaintainPlan3ID, cTrainPlanUnitType, 0, attackerHeroTypeID3);
  1071.       //Set the number of units to maintain in the world at one time.
  1072.       aiPlanSetVariableInt(heroMaintainPlan3ID, cTrainPlanNumberToMaintain, 0, 1);
  1073.       //Don't train units too fast
  1074.       aiPlanSetVariableInt(heroMaintainPlan3ID, cTrainPlanFrequency, 0, 210);
  1075.         // Stop using stupid fortresses and use settlements instead.
  1076.         aiPlanSetVariableInt(heroMaintainPlan3ID, cTrainPlanBuildFromType, 0, cUnitTypeSettlementLevel1);
  1077.       //Set a gather point.
  1078.       aiPlanSetVariableVector(heroMaintainPlan3ID, cTrainPlanGatherPoint, 0, gatherPointTemple1);
  1079.       //Activate the plan. 
  1080.       aiPlanSetActive(heroMaintainPlan3ID);
  1081.    }
  1082.  
  1083.     if ( difflevel > 0 )
  1084.     {
  1085.         heroMaintainPlan4ID=aiPlanCreate("Maintain "+kbGetProtoUnitName(attackerHeroTypeID4), cPlanTrain);
  1086.         if (heroMaintainPlan4ID >= 0)
  1087.         {
  1088.             //Must set the type of unit to train.
  1089.             aiPlanSetVariableInt(heroMaintainPlan4ID, cTrainPlanUnitType, 0, attackerHeroTypeID4);
  1090.             //Set the number of units to maintain in the world at one time.
  1091.             aiPlanSetVariableInt(heroMaintainPlan4ID, cTrainPlanNumberToMaintain, 0, 1);
  1092.             //Don't train units too fast
  1093.             if ( difflevel > 1 )
  1094.             {
  1095.                 aiPlanSetVariableInt(heroMaintainPlan4ID, cTrainPlanFrequency, 0, 420);
  1096.             }
  1097.             else
  1098.             {
  1099.                 aiPlanSetVariableInt(heroMaintainPlan4ID, cTrainPlanFrequency, 0, 520);
  1100.             }
  1101.             // Stop using stupid fortresses and use settlements instead.
  1102.             aiPlanSetVariableInt(heroMaintainPlan4ID, cTrainPlanBuildFromType, 0, cUnitTypeSettlementLevel1);
  1103.             //Set a gather point.
  1104.             aiPlanSetVariableVector(heroMaintainPlan4ID, cTrainPlanGatherPoint, 0, gatherPointTemple2);
  1105.             //Activate the plan.
  1106.             aiPlanSetActive(heroMaintainPlan4ID);
  1107.         }
  1108.     }
  1109. }
  1110.  
  1111. //==============================================================================
  1112. // MAIN.
  1113. //==============================================================================
  1114. void main(void)
  1115. {
  1116.    //Startup.
  1117.    miscStartup();
  1118.  
  1119.     // Maintain a ton o' units.
  1120.     maintainStuff();
  1121.  
  1122.     difficultyLevelAdjustments();
  1123.  
  1124.     // Explore plan.  He has some extra scouts around in case the first one dies.
  1125.    exploreID = aiPlanCreate("Explore", cPlanExplore);
  1126.    if(exploreID >= 0)
  1127.    {
  1128.       aiPlanAddUnitType(exploreID, cUnitTypeScout, 1, 1);
  1129.       aiPlanSetActive(exploreID);
  1130.    }
  1131. }
  1132.