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

  1. //==============================================================================
  2. // Scn30p2: AI Scenario Script for scenario 30 player 2
  3. //==============================================================================
  4. /*
  5.    AI owner:  Mike Kidd
  6.    Scenario owner: Jeff Brown, with a momentary assist from Jerome Jones
  7.  
  8.    Overview:
  9.    
  10.  
  11.   
  12.    9/6/2002:  Disabled Nidhogg per JB request.
  13.  
  14.    9/13/2002:  Beefed up attack on moderate, hard and titan; added circular patrol defend plan.
  15.  
  16.    9/24/2002:  Modified longhouseMaintainQty, hillfortMaintainQty and mythMaintainQty for difficulty level.
  17.  
  18. */
  19. //==============================================================================
  20.  
  21.  
  22. include "scn lib.xs";
  23.  
  24.  
  25. // *****************************************************************************
  26. //
  27. // Globals
  28. //
  29. // *****************************************************************************
  30.  
  31. // Attack routes and queries
  32. int   routeWest = -1;
  33. int   routeWestCenter = -1;
  34. int   routeEastCenter = -1;
  35. int   routeEast = -1;
  36.  
  37.  
  38.  
  39. // Army control
  40. int   lastAttackPlan = -1;       // Used to find "my army" for god power position info
  41. int   defendPlan = -1;
  42. bool  defendOnly = false;        // When true, cancels last attack plan, all military go to defense
  43.  
  44.                                  // The following are set in main() based on difficulty level.
  45. int      nextAttackTime = 240000;   // Will be adjusted for the wakeup time
  46. int      attackInterval = 240000;   // Attack every 4:00
  47. float    attackSize = 6.0;
  48. float    attackMultiplier = 1.2;
  49. int      maxAttackSize = 25;
  50.  
  51. int   longhouseMaintainID = -1;      // Plan for maintaining population of longhouse units
  52. int   longhouseMaintainQty = 20;     // How many to maintain
  53. int   longhouseUnit = -1;            // Which unit type
  54. int   longhouseMaintainDelay = 1;   // How many seconds between training units
  55.  
  56. int   hillfortMaintainID = -1;
  57. int   hillfortMaintainQty = 10;
  58. int   hillfortMaintainDelay = 1;
  59. int   hillfortUnit = -1;
  60.  
  61. int   mythMaintainID = -1;
  62. int   mythMaintainQty = 5;
  63. int   mythUnit = -1;
  64. int   mythMaintainDelay = 1;
  65.  
  66.  
  67. // Cinematic blocks
  68. const string cbFinalGate = "2782";
  69. const string cbRouteWest1 = "2783";
  70. const string cbRouteWest2 = "2784";
  71. const string cbRouteWest3 = "2785";
  72. const string cbRouteWestCenter1 = "2786";
  73. const string cbRouteWestCenter2 = "2787";
  74. const string cbRouteWestCenter3 = "2788";
  75. const string cbRouteEastCenter1 = "2789";
  76. const string cbRouteEastCenter2 = "2790";
  77. const string cbRouteEastCenter3 = "2791";
  78. const string cbRouteEast1 = "2792";
  79. const string cbRouteEast2 = "2793";
  80. const string cbRouteEast3 = "2794";
  81. const string cbHillfortGather = "2795";
  82. const string cbLonghouseGather = "2796";
  83. const string cbTempleGather = "2797";
  84. const string cbAttackGather = "2798";
  85. const string cbP1TC = "2799";
  86. const string cbP3TC = "2800";
  87. const string cbP2TC = "2801";
  88. const string cbPatrolEast = "2975";    // For use with cbFinalGate to make circular patrol for defend plan if needed
  89. const string cbPatrolSouth = "2976";
  90. const string cbPatrolWest = "2977";
  91.  
  92.  
  93.  
  94. // Misc.
  95. int   age2Time = 420000;    // 7 min....will be adjusted in main() for difficulty
  96. int   age3Time = 960000;   // 16 min
  97. int   age4Time = 1620000;  // 27 min
  98. int   startTime = -1;      // Time of the wakeup() function...will be added to the age times.
  99.  
  100. int   difficulty = -1;
  101.  
  102.  
  103. // *****************************************************************************
  104. //
  105. //                                FUNCTIONS
  106. //
  107. // *****************************************************************************
  108.  
  109.  
  110. // Called by trigger when the cinematics are done
  111. void wakeup(int parm=-1)
  112. {
  113.    static bool alreadyRun = false;
  114.    aiEcho("Wakeup running at "+timeString()+".");
  115.    if (alreadyRun == true)
  116.       return;
  117.    alreadyRun = true;
  118.  
  119.    startTime = xsGetTime();
  120.    age2Time = age2Time + startTime;
  121.    xsEnableRule("goToAge2");
  122.    age3Time = age3Time + startTime;    // Adjust for delay in wakeup. 
  123.    age4Time = age4Time + startTime;
  124.    nextAttackTime = nextAttackTime + startTime;
  125.  
  126.    // Init age 1 maintain plans
  127.    maintainUnit(cUnitTypeRaidingCavalry, 1, kbGetBlockPosition(cbAttackGather), 10);   // For scouting
  128.  
  129.  
  130.    // Init low-priority defend plan to manage all extra mil units
  131.    defendPlan =aiPlanCreate("Defend Plan", cPlanDefend);
  132.    if (defendPlan >= 0)
  133.    {
  134. //      vector pointA=gTownLocation;
  135. //      pointA=xsVectorSetX(pointA, xsVectorGetX(gTownLocation)-50.0);
  136. //      vector pointB=gTownLocation;
  137. //      pointB=xsVectorSetZ(pointB, xsVectorGetZ(gTownLocation)-50.0);
  138.  
  139.       aiPlanAddUnitType(defendPlan, cUnitTypeMilitary, 0, 200, 200);    // All unassigned mil units
  140.       aiPlanSetDesiredPriority(defendPlan, 20);                       // Way low, below scouting and attack
  141.       aiPlanSetVariableVector(defendPlan, cDefendPlanDefendPoint, 0, kbGetBlockPosition(cbFinalGate));
  142. //      aiPlanSetVariableInt(defendPlan, cDefendPlanDefendTargetID, 0, 1410);
  143.       aiPlanSetVariableFloat(defendPlan, cDefendPlanEngageRange, 0, 60);
  144.       aiPlanSetUnitStance(defendPlan, cUnitStanceDefensive);
  145.  
  146.       // Start with a simple 3-point patrol
  147.       aiPlanSetNumberVariableValues(defendPlan, cDefendPlanPatrolWaypoint, 4, true);
  148.       aiPlanSetVariableVector(defendPlan, cDefendPlanPatrolWaypoint, 0, kbGetBlockPosition("2782"));
  149.       aiPlanSetVariableVector(defendPlan, cDefendPlanPatrolWaypoint, 1, kbGetBlockPosition("2977"));
  150.       aiPlanSetVariableVector(defendPlan, cDefendPlanPatrolWaypoint, 2, kbGetBlockPosition("2797"));
  151.       aiPlanSetVariableVector(defendPlan, cDefendPlanPatrolWaypoint, 3, kbGetBlockPosition("2975"));
  152.       aiPlanSetVariableBool(defendPlan, cDefendPlanPatrol, 0, true);
  153.  
  154.       //aiPlanSetNumberVariableValues(defendPlan, cDefendPlanPatrolWaypoint, 2, true);
  155.       //aiPlanSetVariableVector(defendPlan, cDefendPlanPatrolWaypoint, 0, pointA);
  156.       //aiPlanSetVariableVector(defendPlan, cDefendPlanPatrolWaypoint, 1, pointB);
  157.  
  158.       aiPlanSetActive(defendPlan);
  159.    }
  160.  
  161.  
  162.    xsEnableRule("useSpy");
  163.    xsEnableRule("scout");
  164.    xsEnableRule("attackGenerator");
  165. }
  166.  
  167.  
  168. // Called via trigger, tells us to get wall/tower upgrades
  169. void upgrade(int parm=-1)
  170. {
  171.    aiEcho("Upgrade function called");
  172.    xsEnableRuleGroup("upgradeGroup");
  173.  
  174. }
  175.  
  176.  
  177. // Called via trigger, cancel last offensive plan and put all units on defense.
  178. void defend(int parm=-1)
  179. {
  180.    aiEcho("Defend function called.");
  181.    defendOnly = true;
  182.    aiPlanDestroy(lastAttackPlan);
  183. }
  184.  
  185.  
  186.  
  187.  
  188. // Used to delete units that are being replaced by a new type.
  189. void deleteObsoleteUnits(int unitType=cUnitTypeUnit, int player=2, vector center=vector(-1,-1,-1), float radius = 20.0, float percent=1.00)
  190. {
  191.    // Make query
  192.    int query = -1;
  193.    int count = -1;
  194.  
  195.    query = kbUnitQueryCreate("Unit deletion query");
  196.    if ( configQuery(query, unitType, -1, cUnitStateAlive, player, center, false, radius) == false)
  197.       return;
  198.    kbUnitQueryResetResults(query);
  199.    count = kbUnitQueryExecute(query);
  200.    
  201.    // Iterate list, deleting percentage indicated
  202.    float remainder=0.0; // Used to handle percentages, when this gets >= 1, it's time to delete a unit.
  203.    
  204.    for (i=0; <count)
  205.    {
  206.       remainder = remainder + percent;
  207.       if (remainder >= 1.0)   // time to delete one
  208.       {
  209.          aiTaskUnitDelete(kbUnitQueryGetResult(query,i));
  210.          remainder = remainder - 1.0;
  211.       }
  212.    }
  213. }
  214.  
  215.  
  216. void age2EventHandler(int bogus=-1)
  217. {
  218.    xsEnableRule("goToAge3");
  219.    xsEnableRule("getAge2UnitUpgrades");
  220.    xsEnableRule("getAge2ArmoryUpgrades");
  221. //   int oldUnit = cUnitTypeSpearman;
  222.  
  223.    switch(aiRandInt(3)) // Select a rax unit
  224.    {
  225.    case 0:
  226.       {
  227.          longhouseUnit = cUnitTypeUlfsark;
  228.          break;
  229.       }
  230.    case 1:
  231.       {
  232.          longhouseUnit = cUnitTypeRaidingCavalry;
  233.          break;
  234.       }
  235.    case 2:
  236.       {
  237.          longhouseUnit = cUnitTypeThrowingAxeman;
  238.          break;
  239.       }
  240.    }
  241.    aiEcho("Longhouse unit is "+kbGetProtoUnitName(longhouseUnit));
  242.    longhouseMaintainID = maintainUnit(longhouseUnit, longhouseMaintainQty, kbGetBlockPosition(cbLonghouseGather), longhouseMaintainDelay);
  243. //   if (longhouseUnit != oldUnit)
  244. //   {
  245. //      deleteObsoleteUnits(oldUnit, 2, kbGetBlockPosition(cbLonghouseGather), 20, .5);   // Delete half of the old units
  246. //      aiEcho("Deleting units: "+kbGetProtoUnitName(oldUnit));
  247. //   }
  248.  
  249.    xsEnableRule("useUndermine");
  250.  
  251. }
  252.  
  253.  
  254.  
  255. void age3EventHandler(int bogus=-1)
  256. {
  257.  
  258.    if (age4Time > 0) // May be suppressed for difficulty
  259.       xsEnableRule("goToAge4");
  260.    xsEnableRule("getAge3UnitUpgrades");
  261.    xsEnableRule("getAge3ArmoryUpgrades");
  262.  
  263.    int oldUnit = longhouseUnit;
  264.    switch(aiRandInt(3)) // Re-select a rax unit
  265.    {
  266.    case 0:
  267.       {
  268.          longhouseUnit = cUnitTypeUlfsark;
  269.          break;
  270.       }
  271.    case 1:
  272.       {
  273.          longhouseUnit = cUnitTypeRaidingCavalry;
  274.          break;
  275.       }
  276.    case 2:
  277.       {
  278.          longhouseUnit = cUnitTypeThrowingAxeman;
  279.          break;
  280.       }
  281.    }
  282.    aiEcho("Longhouse unit is "+kbGetProtoUnitName(longhouseUnit));
  283.     aiPlanSetVariableInt(longhouseMaintainID, cTrainPlanUnitType, 0, longhouseUnit);
  284.    if (longhouseUnit != oldUnit)
  285.    {
  286.       deleteObsoleteUnits(oldUnit, 2, kbGetBlockPosition(cbFinalGate), 50, .5);   // Delete half of the old units
  287.       aiEcho("Deleting units: "+kbGetProtoUnitName(oldUnit));
  288.    }
  289.  
  290.    switch(aiRandInt(3)) // Select a hillfort unit
  291.    {
  292.    case 0:
  293.       {
  294.          hillfortUnit = cUnitTypeJarl;
  295.          break;
  296.       }
  297.    case 1:
  298.       {
  299.          hillfortUnit = cUnitTypeHuskarl;
  300.          break;
  301.       }
  302.    case 2:
  303.       {
  304.          hillfortUnit = cUnitTypePortableRam;
  305.          break;
  306.       }
  307.    }
  308.    aiEcho("Hillfort unit is "+kbGetProtoUnitName(hillfortUnit));
  309.    hillfortMaintainID = maintainUnit(hillfortUnit, hillfortMaintainQty, kbGetBlockPosition(cbHillfortGather), hillfortMaintainDelay);
  310.  
  311.    switch(aiRandInt(2)) // Select a myth unit
  312.    {
  313.    case 0:
  314.       {
  315.          mythUnit = cUnitTypeEinheriar;
  316.          break;
  317.       }
  318.    case 1:
  319.       {
  320.          mythUnit = cUnitTypeBattleBoar;
  321.          break;
  322.       }
  323.    }
  324.    aiEcho("Myth unit is "+kbGetProtoUnitName(mythUnit));
  325.    mythMaintainID = maintainUnit(mythUnit, mythMaintainQty, kbGetBlockPosition(cbTempleGather), mythMaintainDelay);
  326.  
  327.    xsEnableRule("useFlamingWeapons");
  328. }
  329.  
  330.  
  331.  
  332. void age4EventHandler(int bogus=-1)
  333. {
  334.    xsEnableRule("getAge4UnitUpgrades");
  335.    xsEnableRule("getAge4ArmoryUpgrades");
  336.  
  337.  
  338.  
  339.    int oldUnit = longhouseUnit;
  340.    switch(aiRandInt(3)) // Re-select a rax unit
  341.    {
  342.    case 0:
  343.       {
  344.          longhouseUnit = cUnitTypeThrowingAxeman;
  345.          break;
  346.       }
  347.    case 1:
  348.       {
  349.          longhouseUnit = cUnitTypeUlfsark;
  350.          break;
  351.       }
  352.    case 2:
  353.       {
  354.          longhouseUnit = cUnitTypeRaidingCavalry;
  355.          break;
  356.       }
  357.    }
  358.    aiEcho("Longhouse unit is "+kbGetProtoUnitName(longhouseUnit));
  359.     aiPlanSetVariableInt(longhouseMaintainID, cTrainPlanUnitType, 0, longhouseUnit);
  360.    if (longhouseUnit != oldUnit)
  361.    {
  362.       deleteObsoleteUnits(oldUnit, 2, kbGetBlockPosition(cbFinalGate), 50, .5);   // Delete half of the old units
  363.       aiEcho("Deleting units: "+kbGetProtoUnitName(oldUnit));
  364.    }
  365.  
  366.    oldUnit = hillfortUnit;
  367.    switch(aiRandInt(3)) // Re-select a hillfort unit
  368.    {
  369.    case 0:
  370.       {
  371.          hillfortUnit = cUnitTypeJarl;
  372.          break;
  373.       }
  374.    case 1:
  375.       {
  376.          hillfortUnit = cUnitTypeHuskarl;
  377.          break;
  378.       }
  379.    case 2:
  380.       {
  381.          hillfortUnit = cUnitTypePortableRam;
  382.          break;
  383.       }
  384.    }
  385.    aiEcho("Hillfort unit is "+kbGetProtoUnitName(hillfortUnit));
  386.    aiPlanSetVariableInt(hillfortMaintainID, cTrainPlanUnitType, 0, hillfortUnit);
  387.    if (hillfortUnit != oldUnit)
  388.    {
  389.       deleteObsoleteUnits(oldUnit, 2, kbGetBlockPosition(cbFinalGate), 50, .5);   // Delete half of the old units
  390.       aiEcho("Deleting units: "+kbGetProtoUnitName(oldUnit));
  391.    }
  392.  
  393.  
  394.    oldUnit = mythUnit;
  395.    switch(aiRandInt(3)) // Re-select a myth unit
  396.    {
  397.    case 0:
  398.       {
  399.          mythUnit = cUnitTypeEinheriar;
  400.          break;
  401.       }
  402.    case 1:
  403.       {
  404.          mythUnit = cUnitTypeBattleBoar;
  405.          break;
  406.       }
  407.    case 2:
  408.       {
  409.          mythUnit = cUnitTypeFrostGiant;
  410.          break;
  411.       }
  412.    }
  413.    aiEcho("Myth unit is "+kbGetProtoUnitName(hillfortUnit));
  414.    aiPlanSetVariableInt(mythMaintainID, cTrainPlanUnitType, 0, mythUnit);
  415.    if (mythUnit != oldUnit)
  416.    {
  417.       deleteObsoleteUnits(oldUnit, 2, kbGetBlockPosition(cbFinalGate), 50, .5);   // Delete half of the old units
  418.       aiEcho("Deleting units: "+kbGetProtoUnitName(oldUnit));
  419.    }
  420.  
  421.  
  422.  
  423.    //xsEnableRule("useNidhogg");  disabled 9/6/2002
  424. }
  425.  
  426.  
  427.  
  428.  
  429. void attack(int size=0)
  430. {
  431.    if (defendOnly == true)
  432.       return;
  433.  
  434.    int   attackID=aiPlanCreate("Attack at "+timeString(true)+" ", cPlanAttack);
  435.    if (attackID < 0)
  436.       return;
  437.  
  438.    if (aiPlanSetVariableInt(attackID, cAttackPlanPlayerID, 0, 1) == false)
  439.       return;
  440.  
  441.    if (aiPlanSetNumberVariableValues(attackID, cAttackPlanTargetTypeID, 3, true) == false)
  442.       return;
  443.  
  444.    aiPlanSetVariableInt(attackID, cAttackPlanTargetTypeID, 0, cUnitTypeUnit);
  445.    aiPlanSetVariableInt(attackID, cAttackPlanTargetTypeID, 1, cUnitTypeBuilding);
  446.    aiPlanSetVariableInt(attackID, cAttackPlanTargetTypeID, 2, cUnitTypeAbstractWall);
  447.  
  448.    switch(aiRandInt(4))    // Pick a random path
  449.    {
  450.    case 0:
  451.       {
  452.          aiPlanSetVariableInt(attackID, cAttackPlanAttackRouteID, 0, routeWest);
  453.          break;
  454.       }
  455.    case 1:
  456.       {
  457.          aiPlanSetVariableInt(attackID, cAttackPlanAttackRouteID, 0, routeWestCenter);
  458.          break;
  459.       }
  460.    case 2:
  461.       {
  462.          aiPlanSetVariableInt(attackID, cAttackPlanAttackRouteID, 0, routeEastCenter);
  463.          break;
  464.       }
  465.    case 3:
  466.       {
  467.          aiPlanSetVariableInt(attackID, cAttackPlanAttackRouteID, 0, routeEast);
  468.          break;
  469.       }
  470.    }
  471.  
  472.    aiPlanSetVariableVector(attackID, cAttackPlanGatherPoint, 0, kbGetBlockPosition(cbAttackGather));
  473.    aiPlanSetVariableFloat(attackID, cAttackPlanGatherDistance, 0, 20.0);
  474.  
  475.    switch(kbGetAge())      // Set the targets and unit composition
  476.    {
  477.    case cAge1:
  478.       {  
  479.          aiPlanAddUnitType(attackID, cUnitTypeMilitary, 1, size, size);
  480. //         aiPlanAddUnitType(attackID, cUnitTypePriest, 1, 1, 1);
  481. //         aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 0, queryP1Units);
  482.          break;
  483.       }
  484.    case cAge2:
  485.       {  // Longhouse unit
  486.          aiPlanAddUnitType(attackID, longhouseUnit, 1, size, size);
  487. //         aiPlanAddUnitType(attackID, cUnitTypePriest, 1, size/4, size/4);
  488. //         aiPlanSetNumberVariableValues(attackID, cAttackPlanQueryID, 2);
  489.  //        aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 0, queryP1Farms);
  490. //         aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 1, queryP1Units);
  491.          break;
  492.       }
  493.    case cAge3:
  494.       {  // 
  495.          aiPlanAddUnitType(attackID, longhouseUnit, 1, (size+1)/2, (size+1)/2);
  496.          aiPlanAddUnitType(attackID, hillfortUnit, 1, (size+2)/4, (size+2)/4);
  497. //         aiPlanAddUnitType(attackID, cUnitTypePriest, 0, 1, 1);
  498.          aiPlanAddUnitType(attackID, cUnitTypeMythUnit, 0, (size+2)/4, (size+2)/4);
  499. //         aiPlanSetNumberVariableValues(attackID, cAttackPlanQueryID, 3);
  500. //         aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 0, queryP1Monuments);
  501. //         aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 1, queryP1Units);        
  502. //         aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 2, queryP1Buildings); 
  503.          break;
  504.       }
  505.    case cAge4:
  506.       {  
  507.          aiPlanAddUnitType(attackID, longhouseUnit, 1, (size+1)/2, (size+1)/2);
  508.          aiPlanAddUnitType(attackID, hillfortUnit, 1, (size+2)/4, (size+2)/4);
  509. //         aiPlanAddUnitType(attackID, cUnitTypePriest, 0, 1, 1);
  510.          aiPlanAddUnitType(attackID, cUnitTypeMythUnit, 0, (size+2)/4, (size+2)/4);
  511. //         aiPlanSetNumberVariableValues(attackID, cAttackPlanQueryID, 3);
  512. //         aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 0, queryP1Monuments);
  513. //         aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 1, queryP1Units);        
  514. //         aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 2, queryP1Buildings); 
  515.  
  516.          break;
  517.       }
  518.    }
  519.  
  520.    aiPlanSetUnitStance(attackID, cUnitStanceAggressive);
  521.  
  522.    aiPlanSetInitialPosition(attackID, kbGetBlockPosition(cbAttackGather));
  523.    aiPlanSetRequiresAllNeedUnits(attackID, true);
  524.    aiPlanSetDesiredPriority(attackID, 50);   // Less than scouting, more than defense
  525.    aiPlanSetActive(attackID);
  526.    aiEcho("Activating attack plan "+attackID+" with appx "+size+" units.");
  527.    lastAttackPlan = attackID; // update the global var
  528. }
  529.  
  530.  
  531.  
  532.  
  533. void main()
  534. {
  535.    aiEcho("Starting Scn30p2.xs");
  536.  
  537.    //Calculate some areas.
  538.    kbAreaCalculate(1200.0);
  539.    aiRandSetSeed();
  540.    kbSetTownLocation(kbGetBlockPosition(cbP2TC));
  541.  
  542.    aiSetAgeEventHandler(cAge2, "age2EventHandler");
  543.    aiSetAgeEventHandler(cAge3, "age3EventHandler");
  544.    aiSetAgeEventHandler(cAge4, "age4EventHandler");
  545.  
  546.    // Set difficulty vars
  547.    difficulty = aiGetWorldDifficulty();
  548.    aiEcho("Difficulty = "+difficulty);   
  549.  
  550.    switch(difficulty)      // Set up the attack control and age-up parameters
  551.    {
  552.    case 0:     // Easy
  553.       {
  554.          nextAttackTime = 300000;   // 5 min
  555.          attackInterval = 240000;   // 4 min
  556.          attackSize = 2.0;          
  557.          attackMultiplier = 1.1;    // 10% per 4 min
  558.          maxAttackSize = 7;
  559.          longhouseMaintainQty = 6;
  560.          hillfortMaintainQty = 3;
  561.          mythMaintainQty = 2;
  562.          age2Time = 900000;   // 15 min
  563.          age3Time = 1800000;  // 30 min
  564.          age4Time = -1;       // Not permitted
  565.          break;
  566.       }
  567.    case 1:     // Moderate
  568.       {
  569.          nextAttackTime = 240000;
  570.          attackInterval = 200000;
  571.          attackSize = 3.0;
  572.          attackMultiplier = 1.2;    // 20% per 4 min
  573.          maxAttackSize = 15;
  574.          longhouseMaintainQty = 10;
  575.          hillfortMaintainQty = 5;
  576.          mythMaintainQty = 3;
  577.          age2Time = 360000;   // 6 min
  578.          age3Time = 960000;  // 16 min
  579.          age4Time = 1560000;  // 26 min
  580.          break;
  581.       }
  582.    case 2:     // Difficult
  583.       {
  584.          nextAttackTime = 120000;
  585.          attackInterval = 140000;
  586.          attackSize = 6.0;
  587.          attackMultiplier = 1.3;    // 30% n
  588.          maxAttackSize = 20;
  589.          longhouseMaintainQty = 20;
  590.          hillfortMaintainQty = 10;
  591.          mythMaintainQty = 5;
  592.          age2Time = 180000;  // 3 min
  593.          age3Time = 480000;  // 8 min
  594.          age4Time = 1080000; // 18 min
  595.          break;
  596.       }
  597.    case 3:     // Nightmare
  598.       {
  599.          nextAttackTime = 60000;
  600.          attackInterval = 120000;
  601.          attackSize = 8.0;
  602.          attackMultiplier = 1.4;    // 40% per 2 min
  603.          maxAttackSize = 30;
  604.          longhouseMaintainQty = 20;
  605.          hillfortMaintainQty = 12;
  606.          mythMaintainQty = 8;
  607.          age2Time = 60000;  // 1 min
  608.          age3Time = 360000;  // 6 min
  609.          age4Time = 720000;  // 12 min
  610.          break;
  611.       }
  612.    }
  613.  
  614.    // Init attack routes
  615.    routeWest = attackRoute("West Attack Route",cbAttackGather, cbRouteWest1, cbRouteWest2, cbRouteWest3);
  616.    routeWestCenter = attackRoute("West Center Attack Route",cbAttackGather, cbRouteWestCenter1, cbRouteWestCenter2, cbRouteWestCenter3);
  617.    routeEastCenter = attackRoute("East Center Attack Route",cbAttackGather, cbRouteEastCenter1, cbRouteEastCenter2, cbRouteEastCenter3);
  618.    routeEast = attackRoute("East Attack Route",cbAttackGather, cbRouteEast1, cbRouteEast2, cbRouteEast3);
  619.  
  620.  
  621. }
  622.  
  623.  
  624.  
  625.  
  626.  
  627.  
  628.  
  629. // *****************************************************************************
  630. //
  631. // RULES
  632. //
  633. // *****************************************************************************
  634.  
  635. rule scout
  636.    inactive
  637.    minInterval 5
  638. {
  639.    // just set up an explore plan
  640.    int exploreID = aiPlanCreate("Explore", cPlanExplore);
  641.    if(exploreID >= 0)
  642.    {
  643.       aiPlanSetVariableFloat( exploreID, cExplorePlanLOSMultiplier,  0, 4.0 );
  644.       aiPlanAddUnitType(exploreID, cUnitTypeRaidingCavalry, 1, 1, 1);
  645.       aiPlanSetDesiredPriority(exploreID, 90);
  646.       aiPlanSetActive(exploreID);
  647.    }
  648.    xsDisableSelf();
  649. }
  650.  
  651.  
  652.  
  653.  
  654.  
  655. rule getWatchTower
  656.    inactive
  657.    minInterval 10
  658.    group upgradeGroup
  659. {
  660.    if (kbGetTechStatus(cTechWatchTower) == cTechStatusAvailable)
  661.    {
  662.       int x=-1;
  663.       x = aiPlanCreate("WatchTower", cPlanResearch);
  664.       aiPlanSetVariableInt(x, cResearchPlanTechID, 0, cTechWatchTower);
  665.       aiPlanSetActive(x);
  666.       xsDisableSelf();
  667.       aiEcho("Getting Watch Tower");
  668.    }
  669. }
  670.  
  671.  
  672. rule getGuardTower
  673.    inactive
  674.    minInterval 10
  675.    group upgradeGroup
  676. {
  677.    if (kbGetTechStatus(cTechGuardTower) == cTechStatusAvailable)
  678.    {
  679.       int x=-1;
  680.       x = aiPlanCreate("WatchTower", cPlanResearch);
  681.       aiPlanSetVariableInt(x, cResearchPlanTechID, 0, cTechGuardTower);
  682.       aiPlanSetActive(x);
  683.       xsDisableSelf();
  684.       aiEcho("Getting Guard Tower");
  685.    }
  686. }
  687.  
  688.  
  689.  
  690. rule getBoilingOil
  691.    inactive
  692.    minInterval 10
  693.    group upgradeGroup
  694. {
  695.    if (kbGetTechStatus(cTechBoilingOil) == cTechStatusAvailable)
  696.    {
  697.       int x=-1;
  698.       x = aiPlanCreate("WatchTower", cPlanResearch);
  699.       aiPlanSetVariableInt(x, cResearchPlanTechID, 0, cTechBoilingOil);
  700.       aiPlanSetActive(x);
  701.       xsDisableSelf();
  702.       aiEcho("Getting Boiling Oil");
  703.    }
  704. }
  705.  
  706.  
  707.  
  708. rule getSafeguard
  709.    inactive
  710.    minInterval 10
  711.    group upgradeGroup
  712. {
  713.    if (kbGetTechStatus(cTechSafeguard) == cTechStatusAvailable)
  714.    {
  715.       int x=-1;
  716.       x = aiPlanCreate("WatchTower", cPlanResearch);
  717.       aiPlanSetVariableInt(x, cResearchPlanTechID, 0, cTechSafeguard);
  718.       aiPlanSetActive(x);
  719.       xsDisableSelf();
  720.       aiEcho("Getting Safeguard");
  721.    }
  722. }
  723.  
  724.  
  725.  
  726. rule getFortifiedWall
  727.    inactive
  728.    minInterval 10
  729.    group upgradeGroup
  730. {
  731.    if (kbGetTechStatus(cTechFortifiedWall) == cTechStatusAvailable)
  732.    {
  733.       int x=-1;
  734.       x = aiPlanCreate("WatchTower", cPlanResearch);
  735.       aiPlanSetVariableInt(x, cResearchPlanTechID, 0, cTechFortifiedWall);
  736.       aiPlanSetActive(x);
  737.       xsDisableSelf();
  738.       aiEcho("Getting Fortified Wall");
  739.    }
  740. }
  741.  
  742.  
  743.  
  744. rule getMasons
  745.    inactive
  746.    minInterval 10
  747.    group upgradeGroup
  748. {
  749.    if (kbGetTechStatus(cTechMasons) == cTechStatusAvailable)
  750.    {
  751.       int x=-1;
  752.       x = aiPlanCreate("WatchTower", cPlanResearch);
  753.       aiPlanSetVariableInt(x, cResearchPlanTechID, 0, cTechMasons);
  754.       aiPlanSetActive(x);
  755.       xsDisableSelf();
  756.       aiEcho("Getting Masons");
  757.    }
  758. }
  759.  
  760.  
  761.  
  762. rule getStoneWall
  763.    inactive
  764.    minInterval 10
  765.    group upgradeGroup
  766. {
  767.    if (kbGetTechStatus(cTechStoneWall) == cTechStatusAvailable)
  768.    {
  769.       int x=-1;
  770.       x = aiPlanCreate("WatchTower", cPlanResearch);
  771.       aiPlanSetVariableInt(x, cResearchPlanTechID, 0, cTechStoneWall);
  772.       aiPlanSetActive(x);
  773.       xsDisableSelf();
  774.       aiEcho("Getting Stone Wall");
  775.    }
  776. }
  777.  
  778. rule getBallistaTower
  779.    inactive
  780.    minInterval 10
  781.    group upgradeGroup
  782. {
  783.    if (kbGetTechStatus(cTechBallistaTower) == cTechStatusAvailable)
  784.    {
  785.       int x=-1;
  786.       x = aiPlanCreate("WatchTower", cPlanResearch);
  787.       aiPlanSetVariableInt(x, cResearchPlanTechID, 0, cTechBallistaTower);
  788.       aiPlanSetActive(x);
  789.       xsDisableSelf();
  790.       aiEcho("Getting Ballista Tower");
  791.    }
  792. }
  793.  
  794.  
  795.  
  796.  
  797. rule goToAge2
  798.    inactive
  799.    minInterval 10
  800. {
  801.    if ( xsGetTime() < age2Time)
  802.       return;
  803.    researchTech(cTechAge2Heimdall);
  804.    xsDisableSelf();
  805. }
  806.  
  807.  
  808.  
  809. rule goToAge3
  810.    inactive
  811.    mininterval 20
  812. {
  813.    if ( xsGetTime() < age3Time )
  814.       return;
  815.    researchTech(cTechAge3Bragi);
  816.    xsDisableSelf();
  817. }
  818.  
  819.  
  820. rule goToAge4
  821.    inactive
  822.    mininterval 20
  823. {
  824.    if ( xsGetTime() < age4Time )
  825.       return;
  826.    researchTech(cTechAge4Hel);
  827.    xsDisableSelf();
  828. }
  829.  
  830.  
  831.  
  832. rule getAge2UnitUpgrades
  833.    inactive
  834.    minInterval 20
  835. {
  836.    if ( xsGetTime() < (age2Time + age2Time + age3Time)/3 )
  837.       return;     // Wait till 1/3 to age3
  838.    researchTech(cTechMediumInfantry);
  839.    researchTech(cTechMediumCavalry);
  840.    xsDisableSelf();
  841. }
  842.  
  843. rule getAge2ArmoryUpgrades
  844.    inactive
  845.    minInterval 20
  846. {
  847.    if ( xsGetTime() < (age2Time + age3Time + age3Time)/3 )
  848.       return;     // Wait till 2/3 to age3
  849.    aiEcho("Getting age 2 armory upgrades");
  850.    researchTech(cTechCopperWeapons);
  851.    researchTech(cTechCopperMail);
  852.    researchTech(cTechCopperShields);
  853.    xsDisableSelf();
  854. }
  855.  
  856. rule getAge3UnitUpgrades
  857.    inactive
  858.    minInterval 20
  859. {
  860.    if ( xsGetTime() < (age3Time+180000) )
  861.       return;
  862.    researchTech(cTechHeavyInfantry);
  863.    researchTech(cTechHeavyCavalry);
  864.    xsDisableSelf();
  865. }
  866.  
  867. rule getAge3ArmoryUpgrades
  868.    inactive
  869.    minInterval 20
  870. {
  871.    if ( xsGetTime() < (age3Time+300000) )
  872.       return;
  873.    researchTech(cTechBronzeWeapons);
  874.    researchTech(cTechBronzeMail);
  875.    researchTech(cTechBronzeShields);
  876.    xsDisableSelf();
  877. }
  878.  
  879. rule getAge4UnitUpgrades
  880.    inactive
  881.    minInterval 20
  882. {
  883.    if ( xsGetTime() < (age4Time+300000) )
  884.       return;
  885.    researchTech(cTechChampionInfantry);
  886.    researchTech(cTechChampionCavalry);
  887.    xsDisableSelf();
  888. }
  889.  
  890. rule getAge4ArmoryUpgrades
  891.    inactive
  892.    minInterval 20
  893. {
  894.    if ( xsGetTime() < (age4Time+600000) )
  895.       return;
  896.    researchTech(cTechIronWeapons);
  897.    researchTech(cTechIronMail);
  898.    researchTech(cTechIronShields);
  899.    xsDisableSelf();
  900. }
  901.  
  902.  
  903.  
  904. rule attackGenerator
  905.    minInterval 10
  906.    inactive
  907. {
  908.    //aiEcho("attack check running, next time is "+nextAttackTime);
  909.    if ( xsGetTime() < nextAttackTime )
  910.       return;
  911.  
  912.    attack(attackSize);
  913.    nextAttackTime = xsGetTime() + attackInterval;
  914.    attackSize = attackSize * attackMultiplier;
  915.    if (attackSize > maxAttackSize)
  916.       attackSize = maxAttackSize;
  917.    aiEcho("Next attack size will be "+attackSize+".");
  918. }
  919.  
  920.  
  921. /*
  922. // Checks to see if a large P1 army is near the prison, if so, set off a swarm attack.
  923. rule armyNearPrison
  924.    inactive
  925.    minInterval 15
  926. {
  927.    static int queryPrison=-1; // Used for unit count and targeting.
  928.    int   count=0;
  929.    int   attackID=-1;
  930.    
  931.    if (queryPrison < 0)
  932.    {
  933.       // Create query
  934.       queryPrison = kbUnitQueryCreate("Prison query");
  935.       if ( configQuery(queryPrison, cUnitTypeUnit, -1, cUnitStateAlive, 1, kbGetBlockPosition(cbNearPrison), false, 50) == false)
  936.          return;
  937.    }
  938.  
  939.    // Check for enemies
  940.    kbUnitQueryResetResults(queryPrison);
  941.    count = kbUnitQueryExecute(queryPrison);
  942.  
  943.    if (count < 6)
  944.       return;
  945.  
  946.  
  947.  
  948.    // Rally units to defend...all units within 100 meters, to a maximum of 30 units
  949.    aiEcho("Outer wall emergency.");
  950.    attackID = aiPlanCreate("Prison Attack", cPlanAttack);
  951.    count = getUnassignedUnitCount(kbGetBlockPosition(cbAttackGather), 100.0, 2, cUnitTypeMilitary);
  952.    if (count > 30)
  953.       count = 30;
  954.    
  955.    if (attackID < 0)
  956.       return;
  957.  
  958.    if (aiPlanSetVariableInt(attackID, cAttackPlanPlayerID, 0, 1) == false)
  959.       return;
  960.  
  961.    aiPlanSetNumberVariableValues(attackID, cAttackPlanQueryID, 2);
  962.    aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 0, queryPrison);     // Attack units near prison, then
  963.    aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 1, queryP1Units);    // Attack any units
  964.  
  965.    aiPlanSetVariableVector(attackID, cAttackPlanGatherPoint, 0, kbGetBlockPosition(cbAttackGather));
  966.    aiPlanSetVariableFloat(attackID, cAttackPlanGatherDistance, 0, 10.0);
  967.  
  968.    aiPlanAddUnitType(attackID, cUnitTypeMilitary, 1, count, count );
  969.  
  970.    aiEcho("Responding with "+count+" units.");
  971.    aiPlanSetInitialPosition(attackID, kbGetBlockPosition(cbAttackGather));
  972.    aiPlanSetRequiresAllNeedUnits(attackID, true);
  973.    aiPlanSetActive(attackID);
  974.    xsDisableSelf();
  975. }
  976. */
  977.  
  978.  
  979. rule useSpy
  980.    minInterval 5
  981.    inactive
  982. {
  983.    if ( kbUnitVisible(kbGetBlockID("8")) == true )
  984.    {
  985.       aiCastGodPowerAtUnit(cTechSpy, kbGetBlockID("8"));
  986.       aiEcho("Casting spy on Ajax");
  987.       xsDisableSelf();
  988.       return;
  989.    }
  990.  
  991.    if ( kbUnitVisible(kbGetBlockID("10")) == true )
  992.    {
  993.       aiCastGodPowerAtUnit(cTechSpy, kbGetBlockID("10"));
  994.       aiEcho("Casting spy on Chiron");
  995.       xsDisableSelf();
  996.       return;
  997.    }
  998.  
  999.    if ( kbUnitVisible(kbGetBlockID("7")) == true )
  1000.    {
  1001.       aiCastGodPowerAtUnit(cTechSpy, kbGetBlockID("7"));
  1002.       aiEcho("Casting spy on Arkantos");
  1003.       xsDisableSelf();
  1004.       return;
  1005.    }
  1006.  
  1007.    if ( kbUnitVisible(kbGetBlockID("9")) == true )
  1008.    {
  1009.       aiCastGodPowerAtUnit(cTechSpy, kbGetBlockID("9"));
  1010.       aiEcho("Casting spy on Amanra");
  1011.       xsDisableSelf();
  1012.       return;
  1013.    }
  1014.  
  1015.    if ( kbUnitVisible(kbGetBlockID("25")) == true )
  1016.    {
  1017.       aiCastGodPowerAtUnit(cTechSpy, kbGetBlockID("25"));
  1018.       aiEcho("Casting spy on Reginleif");
  1019.       xsDisableSelf();
  1020.       return;
  1021.    }
  1022.  
  1023. }
  1024.  
  1025.  
  1026.  
  1027. rule useUndermine // Look for 2 walls near my army
  1028.    minInterval 5
  1029.    inactive
  1030. {
  1031.    int targetUnit = -1;
  1032.    int attackArmyID = -1;
  1033.  
  1034.    if (lastAttackPlan < 0)
  1035.       return;
  1036.    vector pVec = aiPlanGetLocation(lastAttackPlan);
  1037.    if (xsVectorGetX(pVec)<0)
  1038.       return;
  1039.  
  1040.    static int tempQuery = -1;
  1041.    if (tempQuery < 0)
  1042.    {  // Doesn't exist, set it up
  1043.       tempQuery = kbUnitQueryCreate("useUndermine");
  1044.  
  1045.       if ( configQuery(tempQuery, cUnitTypeAbstractWall, -1, cUnitStateAliveOrBuilding, 1, pVec, true, 25) == false)
  1046.          return;
  1047.    }
  1048.    else
  1049.       kbUnitQuerySetPosition(tempQuery, pVec); // Because pVec changes as army moves
  1050.  
  1051.    kbUnitQueryResetResults(tempQuery);
  1052.    int targetCount = kbUnitQueryExecute(tempQuery);  
  1053.  
  1054.    if (targetCount < 2)
  1055.       return;
  1056.    targetUnit = kbUnitQueryGetResult(tempQuery, 0);  // grab first wall
  1057.  
  1058.    // confirm LOS
  1059.    if ( kbUnitVisible(targetUnit) != true )
  1060.    {
  1061.       aiEcho("Undermine: Don't have LOS for unit "+targetUnit+" "+kbGetProtoUnitName(targetUnit));
  1062.       return;
  1063.    }
  1064.  
  1065.    aiEcho("Using Undermine at "+kbUnitGetPosition(targetUnit));
  1066.    if ( aiCastGodPowerAtPosition(cTechUndermine, kbUnitGetPosition(targetUnit)) == true)
  1067.       xsDisableSelf();
  1068.    else 
  1069.       aiEcho("Undermine failed at "+kbUnitGetPosition(targetUnit));
  1070. }
  1071.  
  1072.  
  1073.  
  1074. rule useFlamingWeapons // Look for 6 military units near my army
  1075.    minInterval 5
  1076.    inactive
  1077. {
  1078.    int targetUnit = -1;
  1079.    int attackArmyID = -1;
  1080.  
  1081.    if (lastAttackPlan < 0)
  1082.       return;
  1083.    vector pVec = aiPlanGetLocation(lastAttackPlan);
  1084.    if (xsVectorGetX(pVec)<0)
  1085.       return;
  1086.  
  1087.    static int tempQuery = -1;
  1088.    if (tempQuery < 0)
  1089.    {  // Doesn't exist, set it up
  1090.       tempQuery = kbUnitQueryCreate("useFlamingWeapons");
  1091.  
  1092.       if ( configQuery(tempQuery, cUnitTypeMilitary, -1, cUnitStateAlive, 1, pVec, true, 50) == false)
  1093.          return;
  1094.    }
  1095.    else
  1096.       kbUnitQuerySetPosition(tempQuery, pVec); // Because pVec changes as army moves
  1097.  
  1098.    kbUnitQueryResetResults(tempQuery);
  1099.    int targetCount = kbUnitQueryExecute(tempQuery);  
  1100.  
  1101.    if (targetCount < 6)
  1102.       return;
  1103. //   targetUnit = kbUnitQueryGetResult(tempQuery, targetCount/2);  // grab middle farm
  1104.  
  1105. // confirm that we have at least 6 miltary units there
  1106.    static int query2 = -1;
  1107.    if (query2 < 0)
  1108.    {
  1109.       query2 = kbUnitQueryCreate("useFlamingWeapons2");
  1110.       configQuery(query2, cUnitTypeMilitary, -1, cUnitStateAlive, 2, pVec, true, 25);
  1111.    }
  1112.    kbUnitQuerySetPosition(query2, pVec);
  1113.    kbUnitQueryResetResults(query2);
  1114.    targetCount = kbUnitQueryExecute(query2);
  1115.    if (targetCount < 6)
  1116.       return;     // Army too small
  1117.  
  1118.    aiEcho("Using FlamingWeapons");
  1119.    if ( aiCastGodPowerAtPosition(cTechFlamingWeapons, kbUnitGetPosition(kbUnitQueryGetResult(query2, targetCount/2))) == true)
  1120.       xsDisableSelf();
  1121.    else 
  1122.       aiEcho("FlamingWeapons failed on "+kbUnitQueryGetResult(query2, targetCount/2));
  1123. }
  1124.  
  1125.  
  1126. rule useNidhogg
  1127.    minInterval 5
  1128.    inactive
  1129. {
  1130.    if (defendOnly == false)
  1131.       return;
  1132.  
  1133.    aiEcho("Invoking Nidhogg on final gate.");
  1134.    aiCastGodPowerAtPosition(cTechNidhogg, kbGetBlockPosition(cbFinalGate));
  1135.    xsDisableSelf();
  1136. }
  1137.  
  1138.