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

  1. //==============================================================================
  2. // Scn17p2: AI Scenario Script for scenario 17 player 2
  3. //==============================================================================
  4. /*
  5.    AI owner:  Mike Kidd
  6.    Scenario owner: Jeff Brown
  7.    With hacker by: Greg Street
  8.  
  9.    Overview:
  10.    The player needs to break in and destroy a fortified CP base.
  11.  
  12.    The CP attacks with waves of units, with a special focus on including some
  13.    priests to counter the player's myth-heavy armies.
  14.  
  15.    If the player ignores the objectives and moves a significant army to the
  16.    area (west) near the prison, the CP swarms his armies over there.
  17.  
  18.    What Greg tried to add: When CP Migdol is lost, pull in everyone and play
  19.    defensive so that HP can head to the prison.
  20.      
  21.    God Powers: Rain and Locust Swarm
  22.    Minor gods: Ptah, Hathor, Osiris
  23.  
  24.    Difficulty:  Not implemented.
  25.  
  26.    7/31/2002:  Added rule to retreat when migdol destroyed.  Beefed up attack army
  27.    sizes.
  28.  
  29.    8/01/2002:  Added difficulty levels, added patrol for defense
  30.  
  31.    8/05/2002:  Increased difficulty level by speeding unit train times, army sizes.
  32.    Greg changed CP to start in age 3.
  33.  
  34.    9/20/2002:  Expanded attack plan gather distance from 10 to 20.
  35.    Fixed Migdol rule to self-disable.
  36.    Delayed initial attack on hard from 20 seconds to 60 seconds.
  37.    
  38.  
  39. */
  40. //==============================================================================
  41.  
  42.  
  43. include "scn lib.xs";
  44.  
  45.  
  46. // *****************************************************************************
  47. //
  48. // Globals
  49. //
  50. // *****************************************************************************
  51.  
  52. // Attack routes and queries
  53. int   routeLeft = -1;
  54. int   routeCenter = -1;
  55. int   routeRight = -1;
  56.  
  57. int   queryP1Units = -1;
  58. int   queryP1Buildings = -1;
  59. int   queryP1Farms = -1;
  60. int   queryP1Monuments = -1;
  61. int   queryP1Citadels = -1;
  62.  
  63. // Army control
  64. int      nextAttackTime = 240000;   // Will be adjusted for the wakeup time
  65. int      attackInterval = 300000;   // Attack every 5:00 on easy
  66. float    attackSize = 6;
  67. float    attackSizeMultiplier = 1.2;
  68. int      maxAttackSize = 16;
  69.  
  70.  
  71.  
  72. int   lastAttackPlan = -1;          // Used to find "my army" for god power position info
  73. int   defendPlan = -1;
  74.  
  75. int   barracksMaintainID = -1;      // Plan for maintaining population of barracks units
  76. int   barracksMaintainQty = 15;     // How many to maintain
  77. int   barracksUnit = cUnitTypeSpearman;            // Which unit type
  78. int   barracksMaintainDelay = 45;   // How many seconds between training units
  79.  
  80. int   migdolMaintainID = cUnitTypeWarElephant;
  81. int   migdolMaintainQty = 6;
  82. int   migdolMaintainDelay = 90;
  83. int   migdolUnit = -1;
  84.  
  85. int   priestMaintainID = -1;
  86. int   priestMaintainQty = 1;
  87. int   priestUnit = cUnitTypePriest;
  88. int   priestMaintainDelay = 120;
  89.  
  90. int   mythMaintainID = -1;
  91. int   mythMaintainQty = 1;
  92. int   mythUnit = -1;
  93. int   mythMaintainDelay = 150;
  94.  
  95. int   siegeTowerMaintainID = cUnitTypeSiegeTower;
  96. int   siegeTowerMaintainQty = 0;
  97. int   siegeTowerUnit = -1;
  98. int   siegeTowerMaintainDelay = 150;
  99.  
  100.  
  101. // Cinematic blocks
  102. const string cbAttackGather = "7959";
  103. const string cbTownLocation = "9497";
  104. const string cbRouteLeft1 = "7961";
  105. const string cbRouteLeft2 = "7962";
  106. const string cbRouteCenter1 = "7963";
  107. const string cbRouteCenter2 = "7964";
  108. const string cbRouteRight1 = "7965";
  109. const string cbRouteRight2 = "7966";
  110. const string cbNearPrison = "7967";
  111. const string cbPriestGather = "7968";
  112. const string cbMythGather = "7969";
  113. const string cbMigdolGather = "7970";
  114. const string cbBarracksGather = "7971";
  115. //const string cbNearMigdol = "1081";
  116. const string cbRetreatCenter = "7960";
  117. const string cbPatrol0 = "7968";
  118. const string cbPatrol1 = "9497";
  119. const string cbPatrol2 = "7960";
  120.  
  121. // Misc.
  122. int   age2Time = 0;    
  123. int   age3Time = 0;  
  124. int   age4Time = 2100000;  
  125. int   startTime = -1;      // Time of the wakeup() function...will be added to the age times.
  126.  
  127.  
  128.  
  129. // *****************************************************************************
  130. //
  131. //                                FUNCTIONS
  132. //
  133. // *****************************************************************************
  134.  
  135.  
  136.  
  137. // Used to delete units that are being replaced by a new type.
  138. void deleteObsoleteUnits(int unitType=cUnitTypeUnit, int player=2, vector center=vector(-1,-1,-1), float radius = 20.0, float percent=1.00)
  139. {
  140.    // Make query
  141.    int query = -1;
  142.    int count = -1;
  143.  
  144.    query = kbUnitQueryCreate("Unit deletion query");
  145.    if ( configQuery(query, unitType, -1, cUnitStateAlive, player, center, false, radius) == false)
  146.       return;
  147.    kbUnitQueryResetResults(query);
  148.    count = kbUnitQueryExecute(query);
  149.    
  150.    // Iterate list, deleting percentage indicated
  151.    float remainder=0.0; // Used to handle percentages, when this gets >= 1, it's time to delete a unit.
  152.    
  153.    for (i=0; <count)
  154.    {
  155.       remainder = remainder + percent;
  156.       if (remainder >= 1.0)   // time to delete one
  157.       {
  158.          aiTaskUnitDelete(kbUnitQueryGetResult(query,i));
  159.          remainder = remainder - 1.0;
  160.       }
  161.    }
  162. }
  163.  
  164.  
  165. void age2EventHandler(int bogus=-1)
  166. {
  167.    xsEnableRule("goToAge3");
  168.    xsEnableRule("getAge2UnitUpgrades");
  169.    xsEnableRule("getAge2ArmoryUpgrades");
  170.    int oldUnit = cUnitTypeSpearman;
  171.  
  172.    switch(aiRandInt(3)) // Select a rax unit
  173.    {
  174.    case 0:
  175.       {
  176.          barracksUnit = cUnitTypeSlinger;
  177.          break;
  178.       }
  179.    case 1:
  180.       {
  181.          barracksUnit = cUnitTypeSpearman;
  182.          break;
  183.       }
  184.    case 2:
  185.       {
  186.          barracksUnit = cUnitTypeAxeman;
  187.          break;
  188.       }
  189.    }
  190.    aiEcho("Barracks unit is "+kbGetProtoUnitName(barracksUnit));
  191.    barracksMaintainID = maintainUnit(barracksUnit, barracksMaintainQty, kbGetBlockPosition(cbBarracksGather), barracksMaintainDelay);
  192.    if (barracksUnit != oldUnit)
  193.    {
  194.       deleteObsoleteUnits(oldUnit, 2, kbGetBlockPosition(cbTownLocation), 40, .5);   // Delete half of the old units
  195.       aiEcho("Deleting units: "+kbGetProtoUnitName(oldUnit));
  196.    }
  197. }
  198.  
  199.  
  200.  
  201. void age3EventHandler(int bogus=-1)
  202. {
  203.    xsEnableRule("useLocustSwarm");
  204.    xsEnableRule("goToAge4");
  205.    xsEnableRule("getAge2UnitUpgrades");
  206.    xsEnableRule("getAge2ArmoryUpgrades");
  207.    xsEnableRule("getAge3UnitUpgrades");
  208.    xsEnableRule("getAge3ArmoryUpgrades");
  209.  
  210.    int oldUnit = barracksUnit;
  211.    switch(aiRandInt(3)) // Re-select a rax unit
  212.    {
  213.    case 0:
  214.       {
  215.          barracksUnit = cUnitTypeSlinger;
  216.          break;
  217.       }
  218.    case 1:
  219.       {
  220.          barracksUnit = cUnitTypeSpearman;
  221.          break;
  222.       }
  223.    case 2:
  224.       {
  225.          barracksUnit = cUnitTypeAxeman;
  226.          break;
  227.       }
  228.    }
  229.    aiEcho("Barracks unit is "+kbGetProtoUnitName(barracksUnit));
  230.     aiPlanSetVariableInt(barracksMaintainID, cTrainPlanUnitType, 0, barracksUnit);
  231.    if (barracksUnit != oldUnit)
  232.    {
  233.       deleteObsoleteUnits(oldUnit, 2, kbGetBlockPosition(cbTownLocation), 40, .5);   // Delete half of the old units
  234.       aiEcho("Deleting units: "+kbGetProtoUnitName(oldUnit));
  235.    }
  236.  
  237.    switch(aiRandInt(3)) // Select a migdol unit
  238.    {
  239.    case 0:
  240.       {
  241.          migdolUnit = cUnitTypeWarElephant;
  242.          break;
  243.       }
  244.    case 1:
  245.       {
  246.          migdolUnit = cUnitTypeWarElephant;
  247.          break;
  248.       }
  249.    case 2:
  250.       {
  251.          migdolUnit = cUnitTypeWarElephant;
  252.          break;
  253.       }
  254.    }
  255.    aiEcho("Migdol unit is "+kbGetProtoUnitName(migdolUnit));
  256.    migdolMaintainID = maintainUnit(migdolUnit, migdolMaintainQty, kbGetBlockPosition(cbMigdolGather), migdolMaintainDelay);
  257.  
  258.    switch(aiRandInt(2)) // Select a myth unit
  259.    {
  260.    case 0:
  261.       {
  262.          mythUnit = cUnitTypeScorpionMan;
  263.          break;
  264.       }
  265.    case 1:
  266.       {
  267.          mythUnit = cUnitTypeWadjet;
  268.          break;
  269.       }
  270.    }
  271.    aiEcho("Myth unit is "+kbGetProtoUnitName(mythUnit));
  272.    mythMaintainID = maintainUnit(mythUnit, mythMaintainQty, kbGetBlockPosition(cbMythGather), mythMaintainDelay);
  273.  
  274. }
  275.  
  276.  
  277.  
  278. void age4EventHandler(int bogus=-1)
  279. {
  280.    xsEnableRule("getAge4UnitUpgrades");
  281.    xsEnableRule("getAge4ArmoryUpgrades");
  282.  
  283.    mythUnit = cUnitTypePhoenix;
  284.    aiPlanSetVariableInt(mythMaintainID, cTrainPlanUnitType, 0, mythUnit);
  285.    aiEcho("Myth unit is Phoenix.");
  286.  
  287.    int oldUnit = barracksUnit;
  288.    switch(aiRandInt(3)) // Re-select a rax unit
  289.    {
  290.    case 0:
  291.       {
  292.          barracksUnit = cUnitTypeSlinger;
  293.          break;
  294.       }
  295.    case 1:
  296.       {
  297.          barracksUnit = cUnitTypeSpearman;
  298.          break;
  299.       }
  300.    case 2:
  301.       {
  302.          barracksUnit = cUnitTypeAxeman;
  303.          break;
  304.       }
  305.    }
  306.    aiEcho("Barracks unit is "+kbGetProtoUnitName(barracksUnit));
  307.     aiPlanSetVariableInt(barracksMaintainID, cTrainPlanUnitType, 0, barracksUnit);
  308.    if (barracksUnit != oldUnit)
  309.    {
  310.       deleteObsoleteUnits(oldUnit, 2, kbGetBlockPosition(cbTownLocation), 40, .5);   // Delete half of the old units
  311.       aiEcho("Deleting units: "+kbGetProtoUnitName(oldUnit));
  312.    }
  313.  
  314.    oldUnit = migdolUnit;
  315.    switch(aiRandInt(3)) // Re-select a migdol unit
  316.    {
  317.    case 0:
  318.       {
  319.          migdolUnit = cUnitTypeWarElephant;
  320.          break;
  321.       }
  322.    case 1:
  323.       {
  324.          migdolUnit = cUnitTypeWarElephant;
  325.          break;
  326.       }
  327.    case 2:
  328.       {
  329.          migdolUnit = cUnitTypeWarElephant;
  330.          break;
  331.       }
  332.    }
  333.    aiEcho("Migdol unit is "+kbGetProtoUnitName(migdolUnit));
  334.    aiPlanSetVariableInt(migdolMaintainID, cTrainPlanUnitType, 0, migdolUnit);
  335.    if (migdolUnit != oldUnit)
  336.    {
  337.       deleteObsoleteUnits(oldUnit, 2, kbGetBlockPosition(cbBarracksGather), 20, .5);   // Delete half of the old units
  338.       aiEcho("Deleting units: "+kbGetProtoUnitName(oldUnit));
  339.    }
  340.  
  341.  
  342.    siegeTowerMaintainID = maintainUnit(cUnitTypeSiegeTower, siegeTowerMaintainQty, kbGetBlockPosition(cbMigdolGather), siegeTowerMaintainDelay);
  343.  
  344. }
  345.  
  346.  
  347. // Called by trigger when the cinematics are done
  348. void wakeup(int parm=-1)
  349. {
  350.    static bool alreadyRun = false;
  351.    aiEcho("Wakeup running at "+timeString()+".");
  352.    if (alreadyRun == true)
  353.       return;
  354.    alreadyRun = true;
  355.  
  356.    startTime = xsGetTime();
  357.    age2Time = age2Time + startTime;
  358.    xsEnableRule("goToAge4");
  359.    age3Time = age3Time + startTime;    // Adjust for delay in wakeup. 
  360.    age4Time = age4Time + startTime;
  361.    nextAttackTime = nextAttackTime + startTime;
  362.  
  363.    // Make sure the event handlers get called if we start in a higher age.
  364.    if (kbGetAge() >= cAge2)
  365.       age2EventHandler(0);
  366.    if (kbGetAge() >= cAge3)
  367.       age3EventHandler(0);
  368.    if (kbGetAge() >= cAge4)
  369.       age4EventHandler();
  370.  
  371.    // Init age 1 maintain plan
  372.    priestMaintainID = maintainUnit(cUnitTypePriest, priestMaintainQty, kbGetBlockPosition(cbPriestGather), priestMaintainDelay);
  373.  
  374.    xsEnableRule("useRain");
  375.    xsEnableRule("scout");
  376.    xsEnableRule("attackGenerator");
  377.    xsEnableRule("armyNearPrison");
  378.  
  379.    // Init low-priority defend plan to manage all extra mil units
  380.    defendPlan = aiPlanCreate("Defend Plan", cPlanDefend);
  381.    if (defendPlan >= 0)
  382.    {
  383.       aiPlanAddUnitType(defendPlan, cUnitTypeMilitary, 0, 200, 200);    // All unassigned mil units
  384.       aiPlanSetDesiredPriority(defendPlan, 10);                       // Way low, below scouting and attack
  385.       aiPlanSetVariableVector(defendPlan, cDefendPlanDefendPoint, 0, kbGetBlockPosition(cbTownLocation));
  386. //      aiPlanSetVariableInt(defendPlan, cDefendPlanDefendTargetID, 0, 1410);
  387.       aiPlanSetVariableFloat(defendPlan, cDefendPlanEngageRange, 0, 25);
  388.       aiPlanSetVariableBool(defendPlan, cDefendPlanPatrol, 0, false);
  389.       aiPlanSetVariableFloat(defendPlan, cDefendPlanGatherDistance, 0, 25.0);
  390.  
  391.       aiPlanSetVariableInt(defendPlan, cDefendPlanRefreshFrequency, 0, 5);
  392.       aiPlanSetNumberVariableValues(defendPlan, cDefendPlanAttackTypeID, 2, true);
  393.       aiPlanSetVariableInt(defendPlan, cDefendPlanAttackTypeID, 0, cUnitTypeUnit);
  394.       aiPlanSetVariableInt(defendPlan, cDefendPlanAttackTypeID, 1, cUnitTypeBuilding);
  395.  
  396.       // Start with a simple 3-point patrol
  397.       aiPlanSetNumberVariableValues(defendPlan, cDefendPlanPatrolWaypoint, 3, true);
  398.       aiPlanSetVariableVector(defendPlan, cDefendPlanPatrolWaypoint, 0, kbGetBlockPosition(cbPatrol0));
  399.       aiPlanSetVariableVector(defendPlan, cDefendPlanPatrolWaypoint, 1, kbGetBlockPosition(cbPatrol1));
  400.       aiPlanSetVariableVector(defendPlan, cDefendPlanPatrolWaypoint, 2, kbGetBlockPosition(cbPatrol2));
  401.       aiPlanSetVariableBool(defendPlan, cDefendPlanPatrol, 0, true);
  402.  
  403.  
  404. //      vector pointA=gTownLocation;
  405. //      pointA=xsVectorSetX(pointA, xsVectorGetX(gTownLocation)-50.0);
  406. //      vector pointB=gTownLocation;
  407. //      pointB=xsVectorSetZ(pointB, xsVectorGetZ(gTownLocation)-50.0);
  408.       //aiPlanSetNumberVariableValues(defendPlan, cDefendPlanPatrolWaypoint, 2, true);
  409.       //aiPlanSetVariableVector(defendPlan, cDefendPlanPatrolWaypoint, 0, pointA);
  410.       //aiPlanSetVariableVector(defendPlan, cDefendPlanPatrolWaypoint, 1, pointB);
  411.       aiPlanSetInitialPosition(defendPlan, kbGetBlockPosition(cbAttackGather));
  412.       aiPlanSetActive(defendPlan); 
  413.       aiEcho("Creating defend plan");
  414.    }
  415. }
  416.  
  417.  
  418.  
  419.  
  420.  
  421. void attack(int size=0)
  422. {
  423.    int   attackID=aiPlanCreate("Attack at "+timeString(true)+" ", cPlanAttack);
  424.    if (attackID < 0)
  425.       return;
  426.  
  427.    if (aiPlanSetVariableInt(attackID, cAttackPlanPlayerID, 0, 1) == false)
  428.       return;
  429.  
  430.    if (aiPlanSetNumberVariableValues(attackID, cAttackPlanTargetTypeID, 2, true) == false)
  431.       return;
  432.  
  433. //   aiPlanSetVariableInt(attackID, cAttackPlanTargetTypeID, 0, cUnitTypeUnit);
  434. //   aiPlanSetVariableInt(attackID, cAttackPlanTargetTypeID, 1, cUnitTypeBuilding);
  435.  
  436.    switch(aiRandInt(3))    // Pick a random path
  437.    {
  438.    case 0:
  439.       {
  440.          aiPlanSetVariableInt(attackID, cAttackPlanAttackRouteID, 0, routeLeft);
  441.          break;
  442.       }
  443.    case 1:
  444.       {
  445.          aiPlanSetVariableInt(attackID, cAttackPlanAttackRouteID, 0, routeCenter);
  446.          break;
  447.       }
  448.    case 2:
  449.       {
  450.          aiPlanSetVariableInt(attackID, cAttackPlanAttackRouteID, 0, routeRight);
  451.          break;
  452.       }
  453.    }
  454.  
  455.    aiPlanSetVariableVector(attackID, cAttackPlanGatherPoint, 0, kbGetBlockPosition(cbAttackGather));
  456.    aiPlanSetVariableFloat(attackID, cAttackPlanGatherDistance, 0, 20.0);
  457.  
  458.    switch(kbGetAge())      // Set the targets and unit composition
  459.    {
  460.    case cAge1:
  461.       {  // Spearmen and one priest, to attack units
  462.          aiPlanAddUnitType(attackID, cUnitTypeSpearman, 1, size-1, size-1);
  463.          aiPlanAddUnitType(attackID, cUnitTypePriest, 1, 1, 1);
  464.          aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 0, queryP1Units);
  465.          break;
  466.       }
  467.    case cAge2:
  468.       {  // Rax unit and priests, to attack farms, units
  469.          aiPlanAddUnitType(attackID, barracksUnit, 1, (3*size)/4, (3*size)/4);
  470.          aiPlanAddUnitType(attackID, cUnitTypePriest, 1, size/4, size/4);
  471.          aiPlanSetNumberVariableValues(attackID, cAttackPlanQueryID, 2);
  472.          aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 0, queryP1Farms);
  473.          aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 1, queryP1Units);
  474.          break;
  475.       }
  476.    case cAge3:
  477.       {  // Rax, one priest, myth, midgol, to attack monuments, units, buildings
  478.          aiPlanAddUnitType(attackID, barracksUnit, 1, size/2, size/2);
  479.          aiPlanAddUnitType(attackID, migdolUnit, 1, size/4, size/4);
  480.          aiPlanAddUnitType(attackID, cUnitTypePriest, 0, 1, 1);
  481.          aiPlanAddUnitType(attackID, cUnitTypeMythUnit, 0, size/4, size/4);
  482.          aiPlanSetNumberVariableValues(attackID, cAttackPlanQueryID, 3);
  483.          aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 0, queryP1Monuments);
  484.          aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 1, queryP1Units);        
  485.          aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 2, queryP1Buildings); 
  486.          break;
  487.       }
  488.    case cAge4:
  489.       {  // Rax, few priests, myth, migdol, to attack Citadel, units, buildings
  490.          aiPlanAddUnitType(attackID, barracksUnit, 1, size/3, size/3);
  491.          aiPlanAddUnitType(attackID, migdolUnit, 1, size/3, size/3);
  492.          aiPlanAddUnitType(attackID, cUnitTypePriest, 0, size/10, size/10);
  493.          aiPlanAddUnitType(attackID, cUnitTypeMythUnit, 0, size/4, size/4);
  494.          aiPlanAddUnitType(attackID, cUnitTypeSiegeTower, 0, size/2, size/10);
  495.          aiPlanSetNumberVariableValues(attackID, cAttackPlanQueryID, 3);
  496.          aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 0, queryP1Citadels);
  497.          aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 1, queryP1Units);        
  498.          aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 2, queryP1Buildings); 
  499.          break;
  500.       }
  501.    }
  502.  
  503.    aiPlanSetInitialPosition(attackID, kbGetBlockPosition(cbAttackGather));
  504.    aiPlanSetRequiresAllNeedUnits(attackID, false);
  505.    aiPlanSetActive(attackID);
  506.    aiEcho("Activating attack plan "+attackID+" with appx "+size+" units.");
  507.    lastAttackPlan = attackID; // update the global var
  508. }
  509.  
  510.  
  511.  
  512.  
  513. void main()
  514. {
  515.    aiEcho("Starting Scn17p2.xs");
  516.  
  517.  
  518.    //Calculate some areas.
  519.    kbAreaCalculate(1200.0);
  520.    aiRandSetSeed();
  521.    aiSetAttackResponseDistance(20.0);
  522.    kbSetTownLocation(kbGetBlockPosition(cbTownLocation));
  523.  
  524.    aiSetAgeEventHandler(cAge2, "age2EventHandler");
  525.    aiSetAgeEventHandler(cAge3, "age3EventHandler");
  526.    aiSetAgeEventHandler(cAge4, "age4EventHandler");
  527.  
  528.  
  529.  
  530.    // Init attack routes
  531.    routeLeft = attackRoute("Left Attack Route",cbAttackGather, cbRouteLeft1, cbRouteLeft2);
  532.    routeCenter = attackRoute("Center Attack Route",cbAttackGather, cbRouteCenter1, cbRouteCenter2);
  533.    routeRight = attackRoute("Right Attack Route",cbAttackGather, cbRouteRight1, cbRouteRight2);
  534.  
  535.    // Initialize the target queries
  536.    queryP1Units = kbUnitQueryCreate("Player 1 Units");
  537.    configQuery(queryP1Units, cUnitTypeUnit, -1, cUnitStateAlive, 1);
  538.  
  539.    queryP1Buildings = kbUnitQueryCreate("Player 1 Buildings");
  540.    configQuery(queryP1Buildings, cUnitTypeBuilding, -1, cUnitStateAliveOrBuilding, 1);
  541.  
  542.    queryP1Farms = kbUnitQueryCreate("Player 1 Farms");
  543.    configQuery(queryP1Farms, cUnitTypeFarm, -1, cUnitStateAliveOrBuilding, 1);
  544.  
  545.    queryP1Monuments = kbUnitQueryCreate("Player 1 Monuments");
  546.    configQuery(queryP1Monuments, cUnitTypeAbstractMonument, -1, cUnitStateAliveOrBuilding, 1);
  547.  
  548.    queryP1Citadels = kbUnitQueryCreate("Player 1 Citadels");
  549.    configQuery(queryP1Citadels, cUnitTypeCitadelCenter, -1, cUnitStateAliveOrBuilding, 1);
  550.  
  551.    aiEcho("Difficulty = "+aiGetWorldDifficulty());
  552.    switch(aiGetWorldDifficulty())
  553.    {
  554.    case 1:  // Moderate
  555.       {
  556.          nextAttackTime = 120000;   
  557.          attackInterval = 240000;   
  558.          attackSize = 9.0;
  559.          attackSizeMultiplier = 1.2;
  560.          maxAttackSize = 25;
  561.          barracksMaintainQty = 20;
  562.          barracksMaintainDelay = 40;
  563.          migdolMaintainQty = 9;
  564.          migdolMaintainDelay = 60;
  565.          priestMaintainQty = 3;
  566.          priestMaintainDelay = 80;
  567.          mythMaintainQty = 2;
  568.          mythMaintainDelay = 100;
  569.          siegeTowerMaintainQty = 1;
  570.          age4Time = 9*60*1000;  
  571.          break;
  572.       }
  573.    case 2:  // Hard
  574.       {
  575.          nextAttackTime = 60000;   
  576.          attackInterval = 192000;   
  577.          attackSize = 14.0;
  578.          attackSizeMultiplier = 1.2;
  579.          maxAttackSize = 40;
  580.          barracksMaintainQty = 25;
  581.          barracksMaintainDelay = 35;
  582.          migdolMaintainQty = 15;
  583.          migdolMaintainDelay = 45;
  584.          priestMaintainQty = 9;
  585.          priestMaintainDelay = 45;
  586.          mythMaintainQty = 4;
  587.          mythMaintainDelay = 45;
  588.          siegeTowerMaintainQty = 3;
  589.          age4Time = 6*60*1000;  
  590.          break;
  591.       }
  592.    case 3:  // Nightmare
  593.       {
  594.          nextAttackTime = 20000;   
  595.          attackInterval = 120000;   
  596.          attackSize = 21.0;
  597.          attackSizeMultiplier = 1.3;
  598.          maxAttackSize = 50;
  599.          barracksMaintainQty = 30;
  600.          barracksMaintainDelay = 30;
  601.          migdolMaintainQty = 25;
  602.          migdolMaintainDelay = 35;
  603.          priestMaintainQty = 12;
  604.          priestMaintainDelay = 30;
  605.          mythMaintainQty = 8;
  606.          mythMaintainDelay = 30;
  607.          siegeTowerMaintainQty = 6;
  608.          age4Time = 3*60*1000;  
  609.          break;
  610.       }
  611.    }
  612. }
  613.  
  614.  
  615.  
  616.  
  617.  
  618.  
  619.  
  620. // *****************************************************************************
  621. //
  622. // RULES
  623. //
  624. // *****************************************************************************
  625.  
  626. rule scout
  627.    inactive
  628.    minInterval 5
  629. {
  630.    // just set up an explore plan
  631.    int exploreID = aiPlanCreate("Explore", cPlanExplore);
  632.    if(exploreID >= 0)
  633.    {
  634.       aiPlanSetVariableFloat( exploreID, cExplorePlanLOSMultiplier,  0, 4.0 );
  635.       aiPlanAddUnitType(exploreID, cUnitTypeSpearman, 1, 1, 1);
  636.       aiPlanSetActive(exploreID);
  637.    }
  638.    xsDisableSelf();
  639. }
  640.  
  641. rule goToAge2
  642.    inactive
  643.    minInterval 10
  644. {
  645.    if ( xsGetTime() < age2Time)
  646.       return;
  647.    researchTech(cTechAge2Ptah);
  648.    xsDisableSelf();
  649. }
  650.  
  651.  
  652.  
  653. rule goToAge3
  654.    inactive
  655.    mininterval 20
  656. {
  657.    if ( xsGetTime() < age3Time )
  658.       return;
  659.    researchTech(cTechAge3Hathor);
  660.    xsDisableSelf();
  661. }
  662.  
  663.  
  664. rule goToAge4
  665.    inactive
  666.    mininterval 20
  667. {
  668.    if ( xsGetTime() < age4Time )
  669.       return;
  670.    researchTech(cTechAge4Thoth);
  671.    xsDisableSelf();
  672. }
  673.  
  674.  
  675.  
  676. rule getAge2UnitUpgrades
  677.    inactive
  678.    minInterval 20
  679. {
  680.    if ( xsGetTime() < (age3Time+10000) )
  681.       return;
  682.    researchTech(cTechMediumAxemen);
  683.    researchTech(cTechMediumSlingers);
  684.    researchTech(cTechMediumSpearmen);
  685.    xsDisableSelf();
  686. }
  687.  
  688. rule getAge2ArmoryUpgrades
  689.    inactive
  690.    minInterval 20
  691. {
  692.    if ( xsGetTime() < (age3Time+12000) )
  693.       return;     
  694.    aiEcho("Getting age 2 armory upgrades");
  695.    researchTech(cTechCopperWeapons);
  696.    researchTech(cTechCopperMail);
  697.    researchTech(cTechCopperShields);
  698.    xsDisableSelf();
  699. }
  700.  
  701. rule getAge3UnitUpgrades
  702.    inactive
  703.    minInterval 20
  704. {
  705.    if ( xsGetTime() < (age3Time+30000) )
  706.       return;
  707.    researchTech(cTechHeavyAxemen);
  708.    researchTech(cTechHeavySlingers);
  709.    researchTech(cTechHeavySpearmen);
  710.    researchTech(cTechHeavyElephants);
  711.    xsDisableSelf();
  712. }
  713.  
  714. rule getAge3ArmoryUpgrades
  715.    inactive
  716.    minInterval 20
  717. {
  718.    if ( xsGetTime() < (age3Time+120000) )
  719.       return;
  720.    researchTech(cTechBronzeWeapons);
  721.    researchTech(cTechBronzeMail);
  722.    researchTech(cTechBronzeShields);
  723.    xsDisableSelf();
  724. }
  725.  
  726. rule getAge4UnitUpgrades
  727.    inactive
  728.    minInterval 20
  729. {
  730.    if ( xsGetTime() < (age4Time+480000) )
  731.       return;
  732.    researchTech(cTechChampionAxemen);
  733.    researchTech(cTechChampionSlingers);
  734.    researchTech(cTechChampionSpearmen);
  735.    researchTech(cTechChampionElephants);
  736.    xsDisableSelf();
  737. }
  738.  
  739. rule getAge4ArmoryUpgrades
  740.    inactive
  741.    minInterval 20
  742. {
  743.    if ( xsGetTime() < (age4Time+720000) )
  744.       return;
  745.    researchTech(cTechIronWeapons);
  746.    researchTech(cTechIronMail);
  747.    researchTech(cTechIronShields);
  748.    xsDisableSelf();
  749. }
  750.  
  751.  
  752.  
  753. rule attackGenerator
  754.    minInterval 10
  755.    inactive
  756. {
  757.    //aiEcho("attack check running, next time is "+nextAttackTime);
  758.    if ( xsGetTime() < nextAttackTime )
  759.       return;
  760.  
  761.    attack(attackSize);
  762.    nextAttackTime = xsGetTime() + attackInterval;
  763.    attackSize = attackSize + attackSizeMultiplier;
  764.    if (attackSize > maxAttackSize)
  765.       attackSize = maxAttackSize;
  766.    aiEcho("Next attack size will be "+attackSize+".");
  767. }
  768.  
  769. // Checks to see if a large P1 army is near the prison, if so, set off a swarm attack.
  770. rule armyNearPrison
  771.    inactive
  772.    minInterval 15
  773. {
  774.    static int queryPrison=-1; // Used for unit count and targeting.
  775.    int   count=0;
  776.    int   attackID=-1;
  777.    
  778.    if (queryPrison < 0)
  779.    {
  780.       // Create query
  781.       queryPrison = kbUnitQueryCreate("Prison query");
  782.       if ( configQuery(queryPrison, cUnitTypeUnit, -1, cUnitStateAlive, 1, kbGetBlockPosition(cbNearPrison), false, 50) == false)
  783.          return;
  784.    }
  785.  
  786.    // Check for enemies
  787.    kbUnitQueryResetResults(queryPrison);
  788.    count = kbUnitQueryExecute(queryPrison);
  789.  
  790.    if (count < 6)
  791.       return;
  792.  
  793.  
  794.  
  795.    // Rally units to defend...all units within 100 meters, to a maximum of 30 units
  796.    aiEcho("Outer wall emergency.");
  797.    attackID = aiPlanCreate("Prison Attack", cPlanAttack);
  798.    count = getUnassignedUnitCount(kbGetBlockPosition(cbAttackGather), 100.0, 2, cUnitTypeMilitary);
  799.    if (count > 30)
  800.       count = 30;
  801.    
  802.    if (attackID < 0)
  803.       return;
  804.  
  805.    if (aiPlanSetVariableInt(attackID, cAttackPlanPlayerID, 0, 1) == false)
  806.       return;
  807.  
  808.    aiPlanSetNumberVariableValues(attackID, cAttackPlanQueryID, 2);
  809.    aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 0, queryPrison);     // Attack units near prison, then
  810.    aiPlanSetVariableInt(attackID, cAttackPlanQueryID, 1, queryP1Units);    // Attack any units
  811.  
  812.    aiPlanSetVariableVector(attackID, cAttackPlanGatherPoint, 0, kbGetBlockPosition(cbAttackGather));
  813.    aiPlanSetVariableFloat(attackID, cAttackPlanGatherDistance, 0, 10.0);
  814.  
  815.    aiPlanAddUnitType(attackID, cUnitTypeMilitary, 1, count, count );
  816.  
  817.    aiEcho("Responding with "+count+" units.");
  818.    aiPlanSetInitialPosition(attackID, kbGetBlockPosition(cbAttackGather));
  819.    aiPlanSetRequiresAllNeedUnits(attackID, true);
  820.    aiPlanSetActive(attackID);
  821.    xsDisableSelf();
  822. }
  823.  
  824. // Once the Migdol is destroyed, bring everyone inside the base so that HP can sneak around to prison
  825. rule migdolLost /* GTS added */
  826.    active
  827.    minInterval 15
  828. {
  829.    static int queryMigdol=-1; 
  830.    
  831.    if (queryMigdol < 0)
  832.    {
  833.       // Create query
  834.       queryMigdol = kbUnitQueryCreate("Query to see if P2 Migdol alive");
  835.       if (configQuery(queryMigdol, cUnitTypeMigdolStronghold, -1, cUnitStateAlive, cMyID) == false)
  836.          return;
  837.    }
  838.  
  839.    kbUnitQueryResetResults(queryMigdol);
  840.    int targetCount = kbUnitQueryExecute(queryMigdol);
  841.  
  842.    if (targetCount > 0)
  843.       return;
  844.    
  845.    aiEcho("Migdol lost. Move to center of town");
  846.  
  847.  
  848. /*
  849.    aiPlanSetVariableBool(defendPlan, cDefendPlanPatrol, 0, false);
  850.    aiPlanSetVariableVector(defendPlan, cDefendPlanDefendPoint, 0, kbGetBlockPosition(cbRetreatCenter));     // Move them all away
  851.    aiPlanSetVariableFloat(defendPlan, cDefendPlanEngageRange, 0, 20.0);    // Tighten the engage range
  852. */
  853.   // Destroy defend plan, create new one for retreat location
  854.    aiPlanDestroy(defendPlan);
  855.    defendPlan = aiPlanCreate("Defend Plan, Take Two", cPlanDefend);
  856.    if (defendPlan >= 0)
  857.    {
  858.       aiPlanAddUnitType(defendPlan, cUnitTypeMilitary, 0, 200, 200);    // All unassigned mil units
  859.       aiPlanSetDesiredPriority(defendPlan, 10);                       // Way low, below scouting and attack
  860.       aiPlanSetVariableVector(defendPlan, cDefendPlanDefendPoint, 0, kbGetBlockPosition(cbRetreatCenter));
  861.       aiPlanSetVariableFloat(defendPlan, cDefendPlanEngageRange, 0, 20);
  862.       aiPlanSetVariableBool(defendPlan, cDefendPlanPatrol, 0, false);
  863.       aiPlanSetVariableFloat(defendPlan, cDefendPlanGatherDistance, 0, 20.0);
  864.  
  865.       aiPlanSetVariableInt(defendPlan, cDefendPlanRefreshFrequency, 0, 5);
  866.       aiPlanSetNumberVariableValues(defendPlan, cDefendPlanAttackTypeID, 2, true);
  867.       aiPlanSetVariableInt(defendPlan, cDefendPlanAttackTypeID, 0, cUnitTypeUnit);
  868.       aiPlanSetVariableInt(defendPlan, cDefendPlanAttackTypeID, 1, cUnitTypeBuilding);
  869.  
  870.       //aiPlanSetNumberVariableValues(defendPlan, cDefendPlanPatrolWaypoint, 2, true);
  871.       //aiPlanSetVariableVector(defendPlan, cDefendPlanPatrolWaypoint, 0, pointA);
  872.       //aiPlanSetVariableVector(defendPlan, cDefendPlanPatrolWaypoint, 1, pointB);
  873.       aiPlanSetInitialPosition(defendPlan, kbGetBlockPosition(cbRetreatCenter));
  874.       aiPlanSetActive(defendPlan); 
  875.       aiEcho("Recreating defend plan");
  876.    }
  877.  
  878.    aiPlanDestroy(lastAttackPlan);      // Kill active attack plan, if any
  879.    xsDisableRule("attackGenerator");   // And no more attacks, please
  880.    xsDisableSelf();
  881. }
  882.  
  883.  
  884.  
  885. rule useRain
  886.    minInterval 10
  887.    inactive
  888. {
  889.    if (xsGetTime() < startTime+300)
  890.       return;
  891.  
  892.    if (aiRandInt(10) > 0)
  893.       return;
  894.  
  895.    aiCastGodPowerAtPosition(cTechRain, kbGetTownLocation());
  896.    aiEcho("Casting rain at "+timeString()+".");
  897.    xsDisableSelf();
  898. }
  899.  
  900.  
  901.  
  902.  
  903.  
  904. rule useLocustSwarm // Look for 6 farms near my army
  905.    minInterval 5
  906.    inactive
  907. {
  908.    int targetUnit = -1;
  909.    int attackArmyID = -1;
  910.  
  911.    if (lastAttackPlan < 0)
  912.       return;
  913.    vector pVec = aiPlanGetLocation(lastAttackPlan);
  914.    if (xsVectorGetX(pVec)<0)
  915.       return;
  916.  
  917.    static int tempQuery = -1;
  918.    if (tempQuery < 0)
  919.    {  // Doesn't exist, set it up
  920.       tempQuery = kbUnitQueryCreate("useLocustSwarm");
  921.  
  922.       if ( configQuery(tempQuery, cUnitTypeFarm, -1, cUnitStateAliveOrBuilding, 1, pVec, true, 50) == false)
  923.          return;
  924.    }
  925.    else
  926.       kbUnitQuerySetPosition(tempQuery, pVec); // Because pVec changes as army moves
  927.  
  928.    kbUnitQueryResetResults(tempQuery);
  929.    int targetCount = kbUnitQueryExecute(tempQuery);  
  930.  
  931.    if (targetCount < 6)
  932.       return;
  933.    targetUnit = kbUnitQueryGetResult(tempQuery, targetCount/2);  // grab middle farm
  934.  
  935.    // confirm LOS
  936.    if ( kbUnitVisible(targetUnit) != true )
  937.    {
  938.       aiEcho("Don't have LOS for locust swarm on unit "+targetUnit);
  939.       return;
  940.    }
  941.  
  942.    aiEcho("Using Locust Swarm");
  943.    if ( aiCastGodPowerAtPosition(cTechLocustSwarm, kbUnitGetPosition(targetUnit)) == true)
  944.       xsDisableSelf();
  945.    else 
  946.       aiEcho("Locust swarm failed at "+kbUnitGetPosition(targetUnit));
  947. }
  948.  
  949.  
  950.  
  951.  
  952.