home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2003 January / maximum-cd-2003-01.iso / Software / Games / AoM / mtrial.exe / AOM / AI / SCN33P2.XS < prev    next >
Encoding:
Text File  |  2002-09-17  |  23.7 KB  |  800 lines

  1. //==============================================================================
  2. // Scn33p2: AI Scenario Script for scenario 33 player 2
  3. //==============================================================================
  4. /*
  5.    AI owner:  Mike Kidd
  6.    Scenario owner: Jeff Brown
  7.  
  8.    Overview:
  9.    
  10.    Player 2 is Norse, and appears to the HP to be the same as player 6 (Greek).
  11.    Player 2's role is to steadily harrass the player continuously, from the time
  12.    of the wakeup() call.
  13.      
  14. */
  15. //==============================================================================
  16.  
  17.  
  18. include "scn lib.xs";
  19.  
  20.  
  21. // *****************************************************************************
  22. //
  23. // Globals
  24. //
  25. // *****************************************************************************
  26.  
  27.  
  28. // Cinematic blocks
  29.  
  30. const string cbDefendPoint = "2883";
  31. const string cbAttackGather = "2889";
  32. const string cbTownCenter = "2886";
  33. const string cbGarg = "2898";       // Gargarensis's hideout
  34. const string cbRouteA1 = "2891";      // East side
  35. const string cbRouteA2 = "2892";
  36. const string cbRouteA3 = "2893";
  37. const string cbRouteB1 = "2891";      // Center
  38. const string cbRouteB2 = "2892";
  39. const string cbRouteB3 = "2894";
  40. const string cbRouteC1 = "2891";      // West
  41. const string cbRouteC2 = "2895";
  42. const string cbRouteC3 = "2896";
  43. const string cbUndermine = "3005";
  44.  
  45.  
  46. // Attack routes and queries
  47. int   routeA = -1;
  48. int   routeB = -1;
  49. int   routeC = -1;
  50.  
  51.  
  52. // Army control
  53. int   lastAttackPlan = -1;       // Used to find "my army" for god power position info
  54. int   penultimatePlan = -1;      // Need to track 2nd last for undermine
  55. int   defendPlan = -1;
  56. int   explorePlan = -1;
  57.  
  58. // The following are set in main() based on difficulty level.
  59. int      nextAttackTime = 180000;   // Will be adjusted for the wakeup time
  60. int      attackInterval = 240000;   // Attack every 4:00 
  61. float    attackSize = 4.0;
  62. float    attackMultiplier = 1.2;
  63. int      maxAttackSize = 8;
  64.  
  65. //    Upgrade timer control
  66. int      startTime = -1;
  67. int      endTime = -1;
  68. int      undermineTime = 1500000;      // 25 min on easy, i.e. never
  69. int      undermineInterval = 240000;   // 4 minutes
  70.  
  71.  
  72. int   maintainQty1 = -1;         // Quantity to maintain
  73. int   maintainUnit1 = -1;        // Unit type
  74. int   maintainDelay1 = -1;       // Interval between training units
  75. vector   maintainGather1 = cInvalidVector;
  76. int   maintainID1 = -1;          // Maintain plan for primary military unit
  77.  
  78. int   maintainQty2 = -1;         // Quantity to maintain
  79. int   maintainUnit2 = -1;        // Unit type
  80. int   maintainDelay2 = -1;       // Interval between training units
  81. vector   maintainGather2 = cInvalidVector;
  82. int   maintainID2 = -1;          // Maintain plan for secondary military unit
  83.  
  84. int   maintainQty3 = -1;         // Quantity to maintain
  85. int   maintainUnit3 = -1;        // Unit type
  86. int   maintainDelay3 = -1;       // Interval between training units
  87. vector   maintainGather3 = cInvalidVector;
  88. int   maintainID3 = -1;          // Maintain plan for tertiary military unit
  89.  
  90. int   maintainQty4 = -1;         // Quantity to maintain
  91. int   maintainUnit4 = -1;        // Unit type
  92. int   maintainDelay4 = -1;       // Interval between training units
  93. vector   maintainGather4 = cInvalidVector;
  94. int   maintainID4 = -1;          // Maintain plan for quatenary military unit
  95.  
  96. int   maintainQtyScout = 1;
  97. int   maintainUnitScout = cUnitTypeUlfsark;
  98. int   maintainDelayScout = 1;
  99. vector   maintainGatherScout = cInvalidVector;
  100. int   maintainIDScout = -1;      // Scout unit
  101.  
  102.  
  103.  
  104.  
  105.  
  106. // Misc.
  107. int   age2Time = -1;       // Will be adjusted in main() for difficulty
  108. int   age3Time = -1;        
  109. int   age4Time = -1;       
  110.  
  111. int   difficulty = -1;     // Global to store difficulty, set early in main.
  112.  
  113.  
  114.  
  115. // *****************************************************************************
  116. //
  117. //                                FUNCTIONS
  118. //
  119. // *****************************************************************************
  120.  
  121.  
  122. // Called by trigger when the cinematics are done
  123.  
  124. void wakeup(int parm=-1)
  125. {
  126.    static bool alreadyRun = false;
  127.    aiEcho("Wakeup running at "+timeString()+".");
  128.    if (alreadyRun == true)
  129.       return;
  130.    alreadyRun = true;
  131.  
  132.    startTime = xsGetTime();
  133.    endTime = startTime + 20*60*1000;   // When does the army arrive?
  134.  
  135.    if (age2Time > 0)
  136.       age2Time = age2Time + startTime; // Adjust for delay in wakeup.
  137.    if (age3Time > 0)
  138.       age3Time = age3Time + startTime;     
  139.    if (age4Time > 0)
  140.       age4Time = age4Time + startTime;
  141.    if (nextAttackTime  > 0)
  142.       nextAttackTime = nextAttackTime + startTime;
  143.  
  144.    undermineTime = undermineTime + startTime;
  145.  
  146.    // Init maintain plans
  147.    if (maintainUnit1 > 0)
  148.       maintainID1 = maintainUnit(maintainUnit1, maintainQty1, maintainGather1, maintainDelay1);
  149.    if (maintainUnit2 > 0)
  150.       maintainID2 = maintainUnit(maintainUnit2, maintainQty2, maintainGather2, maintainDelay2); 
  151.    if (maintainUnit3 > 0)
  152.       maintainID3 = maintainUnit(maintainUnit3, maintainQty3, maintainGather3, maintainDelay3);  
  153.    if (maintainUnit4 > 0)
  154.       maintainID4 = maintainUnit(maintainUnit4, maintainQty4, maintainGather4, maintainDelay4);  
  155.    if (maintainUnitScout > 0)
  156.       maintainIDScout = maintainUnit(maintainUnitScout, maintainQtyScout, maintainGatherScout, maintainDelayScout);
  157.  
  158.    //*** Add a few portable rams
  159.    maintainUnit(cUnitTypePortableRam, 8, cInvalidVector, 1);
  160.  
  161.    // Init low-priority defend plan to manage all extra mil units
  162.    defendPlan =aiPlanCreate("Defend Plan", cPlanDefend);
  163.    if (defendPlan >= 0)
  164.    {
  165.       aiPlanAddUnitType(defendPlan, cUnitTypeMilitary, 0, 200, 200);    // All unassigned mil units
  166.       aiPlanSetDesiredPriority(defendPlan, 10);                       // Way low, below scouting and attack
  167.       aiPlanSetVariableVector(defendPlan, cDefendPlanDefendPoint, 0, kbGetBlockPosition(cbDefendPoint));
  168.       aiPlanSetVariableFloat(defendPlan, cDefendPlanEngageRange, 0, 20);
  169.       aiPlanSetVariableBool(defendPlan, cDefendPlanPatrol, 0, false);
  170.       aiPlanSetVariableFloat(defendPlan, cDefendPlanGatherDistance, 0, 20.0);
  171.       aiPlanSetVariableInt(defendPlan, cDefendPlanRefreshFrequency, 0, 5);
  172.       aiPlanSetNumberVariableValues(defendPlan, cDefendPlanAttackTypeID, 2, true);
  173.       aiPlanSetVariableInt(defendPlan, cDefendPlanAttackTypeID, 0, cUnitTypeUnit);
  174.       aiPlanSetVariableInt(defendPlan, cDefendPlanAttackTypeID, 1, cUnitTypeBuilding);
  175.  
  176.       aiPlanSetInitialPosition(defendPlan, kbGetBlockPosition(cbAttackGather));
  177.       aiPlanSetActive(defendPlan); 
  178.       aiEcho("Creating defend plan");
  179.    }
  180.  
  181.    xsEnableRule("scout");
  182.    xsEnableRule("attackGenerator");
  183.    xsEnableRule("upgradeGenerator");
  184.    xsEnableRule("useUndermine");
  185.    //xsEnableRule("useFimbulwinter");    // Disabled because Ian can't handle it.  ;-)
  186. }
  187.  
  188.  
  189. void flood(int ignore=0)
  190. {
  191.    aiEcho("Flood called at "+timeString());
  192.    // aiFunc trigger:  Tells us to make one uber-group for attacking.
  193.    nextAttackTime = xsGetTime() + attackInterval;
  194.  
  195.    int size = -1;
  196.    size = maxAttackSize*2;
  197.  
  198.    int   attackID=aiPlanCreate("Final attack at "+timeString(true)+" ", cPlanAttack);
  199.    if (attackID < 0) 
  200.       return;
  201.  
  202.    if (aiPlanSetVariableInt(attackID, cAttackPlanPlayerID, 0, 1) == false)
  203.       return;
  204.  
  205.    if (aiPlanSetNumberVariableValues(attackID, cAttackPlanTargetTypeID, 3, true) == false)
  206.       return;
  207.  
  208.    aiPlanSetVariableInt(attackID, cAttackPlanTargetTypeID, 0, cUnitTypeUnit);
  209.    aiPlanSetVariableInt(attackID, cAttackPlanTargetTypeID, 1, cUnitTypeBuilding);
  210.    aiPlanSetVariableInt(attackID, cAttackPlanTargetTypeID, 2, cUnitTypeAbstractWall);
  211.  
  212.          aiPlanSetVariableInt(attackID, cAttackPlanAttackRouteID, 0, routeB);
  213.  
  214.    aiPlanSetVariableVector(attackID, cAttackPlanGatherPoint, 0, kbGetBlockPosition(cbAttackGather));
  215.    aiPlanSetVariableFloat(attackID, cAttackPlanGatherDistance, 0, 20.0);
  216.  
  217.  
  218.    aiPlanAddUnitType(attackID, cUnitTypeMilitary, 0, size, size);  // all
  219.  
  220.    aiPlanSetInitialPosition(attackID, kbGetBlockPosition(cbAttackGather));
  221.    aiPlanSetRequiresAllNeedUnits(attackID, false);
  222.    aiPlanSetDesiredPriority(attackID, 50);   // Less than scouting, more than defense
  223.    aiPlanSetActive(attackID);
  224.    aiEcho("Activating attack plan "+attackID+" with up to "+size+" units.");
  225.    penultimatePlan = lastAttackPlan;
  226.    lastAttackPlan = attackID; // update the global var
  227.  
  228. }
  229.  
  230.  
  231. void attack(int size=0)
  232. {
  233.  
  234.    int   attackID=aiPlanCreate("Attack at "+timeString(true)+" ", cPlanAttack);
  235.    if (attackID < 0) 
  236.       return;
  237.  
  238.    if (aiPlanSetVariableInt(attackID, cAttackPlanPlayerID, 0, 1) == false)
  239.       return;
  240.  
  241.    if (aiPlanSetNumberVariableValues(attackID, cAttackPlanTargetTypeID, 3, true) == false)
  242.       return;
  243.  
  244.    aiPlanSetVariableInt(attackID, cAttackPlanTargetTypeID, 0, cUnitTypeUnit);
  245.    aiPlanSetVariableInt(attackID, cAttackPlanTargetTypeID, 1, cUnitTypeBuilding);
  246.    aiPlanSetVariableInt(attackID, cAttackPlanTargetTypeID, 2, cUnitTypeAbstractWall);
  247.  
  248.  
  249.    
  250.    switch(aiRandInt(3))
  251.    {
  252.    case 0:
  253.       {
  254.          aiPlanSetVariableInt(attackID, cAttackPlanAttackRouteID, 0, routeA);
  255.          break;
  256.       }
  257.    case 1:
  258.       {
  259.          aiPlanSetVariableInt(attackID, cAttackPlanAttackRouteID, 0, routeB);
  260.          break;
  261.       }
  262.    case 2:
  263.       {
  264.          aiPlanSetVariableInt(attackID, cAttackPlanAttackRouteID, 0, routeC);
  265.          break;
  266.       }
  267.    }
  268.  
  269.    aiPlanSetVariableVector(attackID, cAttackPlanGatherPoint, 0, kbGetBlockPosition(cbAttackGather));
  270.    aiPlanSetVariableFloat(attackID, cAttackPlanGatherDistance, 0, 20.0);
  271.  
  272.    aiPlanAddUnitType(attackID, maintainUnit1, 0, (size+8)/10, (size+8)/10);  // boar
  273.    aiPlanAddUnitType(attackID, maintainUnit2, 0, (size+2)/10, (size+2)/10);  // wolf
  274.    aiPlanAddUnitType(attackID, maintainUnit3, 0, (4*size+5)/10, (4*size+5)/10);  // Jarl
  275.  
  276.    if ( (endTime - xsGetTime()) < 15*60*1000)
  277.    {
  278.       aiEcho("  adding seige to attack plan.");
  279.       aiPlanAddUnitType(attackID, maintainUnit4, 0, (2*size+3)/10, (2*size+3)/10);      // huskarl
  280.       aiPlanAddUnitType(attackID, cUnitTypePortableRam, 0, (2*size+7)/10, (2*size+7)/10);  // ram
  281.    }
  282.    else
  283.    {
  284.       aiPlanAddUnitType(attackID, maintainUnit4, 0, (4*size+6)/10, (4*size+6)/10);      // huskarl
  285.    }
  286.  
  287.    aiPlanSetInitialPosition(attackID, kbGetBlockPosition(cbAttackGather));
  288.    aiPlanSetRequiresAllNeedUnits(attackID, false);
  289.    aiPlanSetDesiredPriority(attackID, 50);   // Less than scouting, more than defense
  290.    aiPlanSetActive(attackID);
  291.    aiEcho("Activating attack plan "+attackID+" with appx "+size+" units.");
  292.    lastAttackPlan = attackID; // update the global var
  293. }
  294.  
  295.  
  296.  
  297.  
  298. void main()
  299. {
  300.    aiEcho("Starting Scn33p2.xs");
  301.  
  302.    //Calculate some areas.
  303.    kbAreaCalculate(1200.0);
  304.    aiRandSetSeed();
  305.    kbSetTownLocation(kbGetBlockPosition(cbTownCenter));
  306.  
  307. /*
  308.    aiSetAgeEventHandler(cAge2, "age2EventHandler");
  309.    aiSetAgeEventHandler(cAge3, "age3EventHandler");
  310.    aiSetAgeEventHandler(cAge4, "age4EventHandler");
  311. */
  312.    aiSetAttackResponseDistance(20.0);
  313.  
  314.    // Kill escrows
  315.    kbEscrowSetPercentage( cEconomyEscrowID, cAllResources, 0.0);
  316.    kbEscrowSetPercentage( cMilitaryEscrowID, cAllResources, 0.0);
  317.    kbEscrowAllocateCurrentResources();
  318.  
  319.  
  320.    int   armyReserveSize = -1;   // Target size for total reserve army, to be adjusted for difficulty
  321.    int   standardDelay = -1;     // Used to set unit-training delay times, adjusted for difficulty
  322.  
  323.    // Set difficulty vars
  324.    difficulty = aiGetWorldDifficulty();
  325.    aiEcho("Difficulty = "+difficulty);   
  326.  
  327.    switch(difficulty)      // Set up the attack control and age-up parameters
  328.    {
  329.    case 0:     // Easy
  330.       {
  331.          nextAttackTime = 120000;     // 2 min
  332.          attackInterval = 240000;   // 4 min
  333.          attackSize = 3;          
  334.          attackMultiplier = 1.3;    // 30% per interval
  335.          maxAttackSize = 8;
  336.          age2Time = -1;       // N/A
  337.          age3Time = 1200000;  // 20 min
  338.          age4Time = 40*60*1000;  
  339.          undermineTime = 25*60*1000;
  340.          armyReserveSize = 12;
  341.          standardDelay = 1;  // seconds
  342.          break;
  343.       }
  344.    case 1:     // Moderate
  345.       {
  346.          nextAttackTime = 90000;   // 1.5 min min 
  347.          attackInterval = 162000;   // 162 sec min
  348.          attackSize = 6.0;          
  349.          attackMultiplier = 1.3;    // 30% per period
  350.          maxAttackSize = 15;
  351.          age2Time = -1;       // N/A
  352.          age3Time = 720000;   // 12 min
  353.          age4Time = 20*60*1000;   
  354.          undermineTime = 15*60*1000;
  355.          armyReserveSize = 16;
  356.          standardDelay = 1;  // seconds
  357.          break;
  358.       }
  359.    case 2:     // Difficult
  360.       {
  361.          nextAttackTime = 60000;
  362.          attackInterval = 120000;
  363.          attackSize = 8.0;
  364.          attackMultiplier = 1.3;    // 30% per period
  365.          maxAttackSize = 20;
  366.          age2Time = -1;       // N/A
  367.          age3Time = 4*60*1000;   // 4 min
  368.          age4Time = 12*60*1000; 
  369.          undermineTime = 9*60*1000;
  370.          armyReserveSize = 25;
  371.          standardDelay = 1;  // seconds
  372.          break;
  373.       }
  374.    case 3:     // Nightmare
  375.       {
  376.          nextAttackTime = 60000;
  377.          attackInterval = 120000;
  378.          attackSize = 10.0;
  379.          attackMultiplier = 1.4;    // 40% per period
  380.          maxAttackSize = 30;
  381.          age2Time = -1;       // N/A
  382.          age3Time = 1*60*1000;   // 1 min
  383.          age4Time = 8*60*1000;  
  384.          undermineTime = 0;
  385.          armyReserveSize = 40;
  386.          standardDelay = 1;  // seconds
  387.          break;
  388.       }
  389.    }
  390.  
  391.  
  392.  
  393.    // Set global unit control vars
  394.    maintainQty1 = (armyReserveSize+8)/10;         // Quantity to maintain
  395.    maintainUnit1 = cUnitTypeBattleBoar;        // Unit type
  396.    maintainDelay1 = standardDelay;       // Interval between training units
  397.    maintainGather1 = cInvalidVector;
  398.  
  399.    maintainQty2 = (armyReserveSize+2)/10;         // Quantity to maintain
  400.    maintainUnit2 = cUnitTypeFenrisWolf;        // Unit type
  401.    maintainDelay2 = standardDelay;       // Interval between training units
  402.    maintainGather2 = cInvalidVector;
  403.  
  404.    maintainQty3 = (3*armyReserveSize+2)/10;         // Quantity to maintain
  405.    maintainUnit3 = cUnitTypeJarl;        // Unit type
  406.    maintainDelay3 = standardDelay;       // Interval between training units
  407.    maintainGather3 = cInvalidVector;
  408.  
  409.    maintainQty4 = (3*armyReserveSize+5)/10;         // Quantity to maintain
  410.    maintainUnit4 = cUnitTypeHuskarl;        // Unit type
  411.    maintainDelay4 = standardDelay;       // Interval between training units
  412.    maintainGather4 = cInvalidVector;
  413.  
  414.  
  415.    maintainQtyScout = 1;
  416.    maintainUnitScout = cUnitTypeUlfsark;
  417.    maintainDelayScout = 1;
  418.    maintainGatherScout = cInvalidVector;
  419.  
  420.  
  421.    // Init attack routes
  422.    routeA = attackRoute("Attack Route A", cbRouteA1, cbRouteA2, cbRouteA3);
  423.    routeB = attackRoute("Attack Route B", cbRouteB1, cbRouteB2, cbRouteB3);
  424.    routeC = attackRoute("Attack Route C", cbRouteC1, cbRouteC2, cbRouteC3);
  425.  
  426.  
  427.  
  428.  
  429.  
  430. /*
  431.    // Initialize the target queries
  432.    queryP1Units = kbUnitQueryCreate("Player 1 Units");
  433.    configQuery(queryP1Units, cUnitTypeUnit, -1, cUnitStateAlive, 1);
  434. */
  435.  
  436.  
  437. }
  438.  
  439.  
  440.  
  441.  
  442.  
  443.  
  444.  
  445. // *****************************************************************************
  446. //
  447. // RULES
  448. //
  449. // *****************************************************************************
  450. /*rule cheatScout   // Spawn a fenris wolf if none exist
  451.    active
  452.    minInterval 15
  453. {
  454.    int count = -1;
  455.    count = kbUnitCount(2, maintainUnitScout, cUnitStateAlive);
  456.    if (count < maintainQtyScout)
  457.       aiUnitCreateCheat( 2, maintainUnitScout, kbGetBlockPosition(cbAttackGather), "Scout group", maintainQtyScout - count); 
  458.  
  459. }*/
  460.  
  461.  
  462.  
  463. rule favorGenerator
  464.    active
  465.    minInterval 15
  466. {
  467.    aiResourceCheat( 2, cResourceFavor, 100.0 );    // Max out the favor every 15 seconds
  468.    kbEscrowAllocateCurrentResources();             // Make sure the escrow knows about it.
  469. }
  470.  
  471.  
  472. rule scout
  473.    inactive
  474.    minInterval 5
  475. {
  476.    // just set up an explore plan
  477.    int explorePlan = aiPlanCreate("Explore", cPlanExplore);
  478.    if(explorePlan >= 0)
  479.    {
  480.       aiPlanSetVariableFloat( explorePlan, cExplorePlanLOSMultiplier,  0, 4.0 );
  481.       aiPlanAddUnitType(explorePlan, maintainUnitScout, 1, 1, 1);
  482.       aiPlanSetDesiredPriority(explorePlan, 90);
  483.       aiPlanSetInitialPosition(explorePlan, kbGetBlockPosition(cbAttackGather));
  484.       aiPlanSetActive(explorePlan);
  485.    }
  486.    xsDisableSelf();
  487. }
  488.  
  489.  
  490.  
  491. /*
  492. rule goToAge2
  493.    inactive
  494.    minInterval 10
  495. {
  496.    if ( xsGetTime() < age2Time)
  497.       return;
  498.    researchTech(cTechAge2Heimdall);
  499.    xsDisableSelf();
  500. }
  501.  
  502.  
  503.  
  504. rule goToAge3
  505.    inactive
  506.    mininterval 20
  507. {
  508.    if ( xsGetTime() < age3Time )
  509.       return;
  510.    researchTech(cTechAge3Njord);
  511.    xsDisableSelf();
  512. }
  513.  
  514.  
  515. rule goToAge4
  516.    inactive
  517.    mininterval 20
  518. {
  519.    if ( xsGetTime() < age4Time )
  520.       return;
  521.    researchTech(cTechAge4Hel);
  522.    xsDisableSelf();
  523. }
  524. */
  525.  
  526.  
  527.  
  528. rule getAge2UnitUpgrades
  529.    inactive
  530.    minInterval 20
  531. {
  532. //   researchTech(cTechMediumInfantry);
  533. //   researchTech(cTechMediumCavalry);
  534.    xsDisableSelf();
  535. }
  536.  
  537. rule getAge2ArmoryUpgrades
  538.    inactive
  539.    minInterval 20
  540. {
  541.  
  542.    aiEcho("Getting age 2 armory upgrades");
  543.    researchTech(cTechCopperWeapons);
  544.    researchTech(cTechCopperMail);
  545.    researchTech(cTechCopperShields);
  546.    xsDisableSelf();
  547. }
  548.  
  549. rule getAge3UnitUpgrades
  550.    inactive
  551.    minInterval 20
  552. {
  553.  
  554. //   researchTech(cTechHeavyInfantry);
  555.  //  researchTech(cTechHeavyCavalry);
  556.    xsDisableSelf();
  557. }
  558.  
  559. rule getAge3ArmoryUpgrades
  560.    inactive
  561.    minInterval 20
  562. {
  563.    aiEcho("Getting age 3 armory upgrades");
  564.    researchTech(cTechBronzeWeapons);
  565.    researchTech(cTechBronzeMail);
  566.    researchTech(cTechBronzeShields);
  567.    xsDisableSelf();
  568. }
  569.  
  570. rule getAge4UnitUpgrades
  571.    inactive
  572.    minInterval 20
  573. {
  574.  
  575. //   researchTech(cTechChampionInfantry);
  576. //   researchTech(cTechChampionCavalry);
  577.    xsDisableSelf();
  578. }
  579.  
  580. rule getAge4ArmoryUpgrades
  581.    inactive
  582.    minInterval 20
  583. {
  584.    aiEcho("Getting age 4 armory upgrades");
  585.    researchTech(cTechIronWeapons);
  586.    researchTech(cTechIronMail);
  587.    researchTech(cTechIronShields);
  588.    xsDisableSelf();
  589. }
  590.  
  591.  
  592. rule upgradeGenerator
  593.    minInterval 10
  594.    inactive
  595. {
  596.    // Progressively get unit upgrades throughout
  597.    // Take the time from startTime to endTime, divide into six segments.
  598.    // At the end of each segment, get a set of unit or armory upgrades.
  599.  
  600.    static int interval = 0;
  601.    static int nextTime = 0;
  602.    static int count = 0;
  603.  
  604.    if (interval == 0) 
  605.    {
  606.       interval = (endTime - startTime)/6;
  607.       nextTime = startTime + interval;
  608.    }
  609.  
  610.    if (nextTime > xsGetTime())
  611.       return;     // It is not yet our time.
  612.  
  613.    // It is time, do something
  614.    switch( count )      // Activate the appropriate upgrade rule, reset nextTime timer.
  615.    {
  616.    case 0:
  617.       {
  618.          xsEnableRule("getAge2UnitUpgrades");
  619.          count = count + 1;
  620.          nextTime = nextTime + interval;
  621.          break;
  622.       }
  623.    case 1:
  624.       {
  625.          xsEnableRule("getAge2ArmoryUpgrades");
  626.          count = count + 1;
  627.          nextTime = nextTime + interval;
  628.          break;
  629.       }
  630.    case 2:
  631.       {
  632.          xsEnableRule("getAge3UnitUpgrades");
  633.          count = count + 1;
  634.          nextTime = nextTime + interval;
  635.          break;
  636.       }
  637.    case 3:
  638.       {
  639.          xsEnableRule("getAge3ArmoryUpgrades");
  640.          count = count + 1;
  641.          nextTime = nextTime + interval;
  642.          break;
  643.       }
  644.    case 4:
  645.       {
  646.          xsEnableRule("getAge4UnitUpgrades");
  647.          count = count + 1;
  648.          nextTime = nextTime + interval;
  649.          break;
  650.       }
  651.    case 5:
  652.       {
  653.          xsEnableRule("getAge4ArmoryUpgrades");
  654.          count = count + 1;
  655.          xsDisableSelf();
  656.          break;
  657.       }
  658.    }
  659.  
  660.  
  661. }
  662.  
  663. rule attackGenerator
  664.    minInterval 10
  665.    inactive
  666. {
  667.    //aiEcho("attack check running, next time is "+nextAttackTime);
  668.    if ( xsGetTime() < nextAttackTime )
  669.       return;
  670.  
  671.    attack(attackSize);
  672.    nextAttackTime = xsGetTime() + attackInterval;
  673.    attackSize = attackSize * attackMultiplier;
  674.    if (attackSize > maxAttackSize)
  675.       attackSize = maxAttackSize;
  676.    aiEcho("Next attack size will be "+attackSize+".");
  677. }
  678.  
  679.  
  680. rule useFimbulwinter
  681.    minInterval 5
  682.    inactive
  683. {
  684.    if ( xsGetTime() < ((12*60*1000)+startTime) )
  685.       return;
  686.  
  687.    aiCastGodPowerAtPosition(cTechSnowStorm, kbGetBlockPosition(cbTownCenter));
  688.    xsDisableSelf();
  689.    aiEcho("Invoking Fimbulwinter.");
  690. }
  691.  
  692.  
  693.  
  694. rule useUndermine // Look for 3 walls near the last attack plan
  695.    minInterval 5
  696.    inactive
  697. {
  698.    int targetUnit = -1;
  699.    int attackArmyID = -1;
  700.  
  701.    if ( xsGetTime() < undermineTime )
  702.       return;
  703.  
  704.    vector pVec = aiPlanGetLocation(penultimatePlan);     // Try the 2nd last plan, it should be to the walls by now
  705.    if (xsVectorGetX(pVec)<0)
  706.       pVec = aiPlanGetLocation(lastAttackPlan);          // It doesn't exist, so try the most recent plan
  707.  
  708.    if (xsVectorGetX(pVec)<0)
  709.       return;     // Neither plan exists, give up.
  710.  
  711.    static int tempQuery = -1;
  712.    if (tempQuery < 0)
  713.    {  // Doesn't exist, set it up
  714.       tempQuery = kbUnitQueryCreate("useUndermine");
  715.  
  716.       if ( configQuery(tempQuery, cUnitTypeAbstractWall, -1, cUnitStateAliveOrBuilding, 1, pVec, true, 20) == false)
  717.          return;
  718.    }
  719.    else
  720.       kbUnitQuerySetPosition(tempQuery, pVec); // Because pVec changes as army moves
  721.  
  722.    kbUnitQueryResetResults(tempQuery);
  723.    int targetCount = kbUnitQueryExecute(tempQuery);  
  724.  
  725.    if (targetCount < 3)
  726.       return;
  727.    targetUnit = kbUnitQueryGetResult(tempQuery, targetCount/2);  // grab middle wall
  728.  
  729.    // confirm LOS
  730.    if ( kbUnitVisible(targetUnit) != true )
  731.    {
  732.       aiEcho("Undermine: Don't have LOS for unit "+targetUnit+" "+kbGetProtoUnitName(targetUnit));
  733.       // Grab the explore unit, send it here
  734.       static int scoutQuery = -1;
  735.       if (scoutQuery < 0)
  736.       {
  737.          scoutQuery = kbUnitQueryCreate("Scout Query");
  738.          configQuery(scoutQuery, maintainUnitScout, -1, cUnitStateAlive, cMyID, aiPlanGetLocation(explorePlan), true, 10);
  739.       }
  740.       else
  741.          kbUnitQuerySetPosition( scoutQuery, aiPlanGetLocation(explorePlan));
  742.       int unit = -1;
  743.       int count = -1;
  744.       kbUnitQueryResetResults(scoutQuery);
  745.       count = kbUnitQueryExecute(scoutQuery);
  746.       if (count > 0)
  747.       {
  748.          unit = kbUnitQueryGetResult(scoutQuery, 0);
  749.          aiTaskUnitMove(unit, kbGetBlockPosition(cbUndermine));
  750.          aiEcho("Moving unit "+unit+" to location "+kbGetBlockPosition(cbUndermine));
  751.       }
  752.  
  753.       return;
  754.    }
  755.  
  756.    // Move the target point away from the wall, about 1 meter toward the army's location.
  757.    float armyX = -1;
  758.    float armyZ = -1;
  759.    float wallX = -1;
  760.    float wallZ = -1;
  761.    float targetX = -1;
  762.    float targetZ = -1;
  763.    float dx = -1;
  764.    float dz = -1;
  765.    float scale = -1;
  766.    vector targetVec = cInvalidVector;
  767.  
  768.    armyX = xsVectorGetX(pVec);
  769.    armyZ = xsVectorGetZ(pVec);
  770.    wallX = xsVectorGetX(kbUnitGetPosition(targetUnit));
  771.    wallZ = xsVectorGetZ(kbUnitGetPosition(targetUnit));
  772.    dx = wallX - armyX; 
  773.    dz = wallZ - armyZ;  // Positive if wallZ < armyZ
  774.    if (dx < 0) 
  775.       scale = -1.0 * dx;
  776.    else
  777.       scale = dx;
  778.    if (dz < 0)
  779.       scale = scale + (-1*dz);
  780.    else
  781.       scale = scale + dz;
  782.    // Scale is now sum of absolute values of dx and dz, rough indicator of range.  
  783.    
  784.    targetX = wallX - (dx / scale);
  785.    targetZ = wallZ - (dz / scale);
  786.    targetVec = pVec;
  787.    targetVec = xsVectorSetX(targetVec, targetX);
  788.    targetVec = xsVectorSetZ(targetVec, targetZ);
  789.  
  790.    aiEcho("Using Undermine at "+targetVec);
  791.    if ( aiCastGodPowerAtPosition(cTechUndermine, targetVec) == true)
  792.       aiEcho("Undermine succeeded at "+targetVec);
  793.    else 
  794.       aiEcho("Undermine failed at "+targetVec);
  795.  
  796.    undermineTime = xsGetTime() + undermineInterval;
  797. }
  798.  
  799.  
  800.