home *** CD-ROM | disk | FTP | other *** search
Wrap
Text File | 2002-09-23 | 47.5 KB | 1,462 lines
//============================================================================== // Scn21p2: AI Scenario Script for scenario 21 player 2 //============================================================================== /* AI owner: Dave Leary Scenario owner: Dave Leary The AI for the red enemy in scenario 21. Produces fairly balanced mixed Egyptian groups that choose randomly between three attack routes (left, right, and down the middle). Cheats for resources. Performs researches over time. Uses god powers - Vision at the start of the game, Serpents near the Tamarisk tree when the player gets several villagers close to it, and Tornado during a late-game attack on the player's town. *** DIFFICULTY LEVEL NOTES *** Easy level - Extra towers. Fewer researches and AI does not advance to Age 4. Elephants and scarabs not included in attack groups. Since AI does not advance to Age 4, AI never uses Tornado. Slower initial attacks. Moderate level - base level. Difficult level - fewer sheep close to player town. AI defends the tree with anubites/priests when the damage is at 25%. Nightmare - Two enemy towers at the Tamarisk Tree. Less gold and trees on the map, especially near player starting town. Southern gold mine is guarded by a tower and two scorpion men. One less unclaimed settlement. AI defends the tree with anubites/priests when the damage is at 2%. Anubites start with Feet of the Jackal. */ // Difficulty Level check predeclared. int difflevel=-1; int treethreat=-1; // Variable for main base. int gMainBaseID=-1; // Initial gather percentages float totalFoodGathererPercentage = 0.8; float totalWoodGathererPercentage = 0.0; float totalGoldGathererPercentage = 0.2; float totalFavorGathererPercentage = 0.0; //============================================================================== // Set Town Location //============================================================================== void setTownLocation(void) { //Look for the "Town Location" marker. kbSetTownLocation(kbGetBlockPosition("924")); } //============================================================================== // miscStartup //============================================================================== void miscStartup(void) { // Get the difficulty level. difflevel=aiGetWorldDifficulty(); //Startup message(s). aiEcho(""); aiEcho(""); aiEcho("Scn21P2 AI Start, filename='"+cFilename+"'."); //Spit out the map size. aiEcho("Map size is ("+kbGetMapXSize()+", "+kbGetMapZSize()+")."); aiEcho("Difficulty Level="+difflevel+"."); //Cheat like a bastard. Once only, though. kbLookAtAllUnitsOnMap(); //Calculate some areas. kbAreaCalculate(1200.0); //Set our town location. setTownLocation(); //Reset random seed aiRandSetSeed(); } //============================================================================== // initEcon // // Set Up the initial Economy. //============================================================================== void initEcon() { aiEcho("Economy Init."); /* Don't need this for what we're doing here. // Set our update resource handler. aiSetUpdateResourceEventHandler("updateResourceHandler"); */ //-- Setup AI Cost weights. kbSetAICostWeight(cResourceFood, 1.0); kbSetAICostWeight(cResourceWood, 0.5); kbSetAICostWeight(cResourceGold, 1.0); kbSetAICostWeight(cResourceFavor, 10.0); //-- Dont auto gather favor //totalFavorGathererPercentage = 0; //-- Set initial gatherer percentages. aiSetResourceGathererPercentage(cResourceFood, totalFoodGathererPercentage, false, cRGPScript); aiSetResourceGathererPercentage(cResourceWood, totalWoodGathererPercentage, false, cRGPScript); aiSetResourceGathererPercentage(cResourceGold, totalGoldGathererPercentage, false, cRGPScript); aiSetResourceGathererPercentage(cResourceFavor, totalFavorGathererPercentage, false, cRGPScript); aiNormalizeResourceGathererPercentages(cRGPScript); aiSetResourceGathererPercentageWeight(cRGPScript, 1); aiSetResourceGathererPercentageWeight(cRGPCost, 0); //-- Set up the initial resource subtype breakdowns - all farming, all the time. // aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeEasy, 1, 50, 0.0, gMainBaseID); // aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeHunt, 1, 50, 0.1, gMainBaseID); aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeFarm, 1, 50, 1.0, gMainBaseID); aiSetResourceBreakdown(cResourceWood, cAIResourceSubTypeEasy, 1, 50, 1.0, gMainBaseID); aiSetResourceBreakdown(cResourceGold, cAIResourceSubTypeEasy, 1, 50, 1.0, gMainBaseID); aiSetResourceBreakdown(cResourceFavor, cAIResourceSubTypeEasy, 1, 50, 1.0, gMainBaseID); //-- Set up auto-gather escrows aiSetAutoGatherEscrowID(cRootEscrowID); aiSetAutoFarmEscrowID(cRootEscrowID); //Allocate all resources to the root escrow by setting percentage of military/economy to 0. kbEscrowSetPercentage( cEconomyEscrowID, cAllResources, 0.0 ); kbEscrowSetPercentage( cMilitaryEscrowID, cAllResources, 0.0 ); //Allocate all resources kbEscrowAllocateCurrentResources(); } //============================================================================== //============================================================================== // Attack stuff. //============================================================================== //============================================================================== //Shared variables. int numberAttacks=0; int attackPlayerID=-1; //TODO: Decide how to rep attack group size. int attackMinimumGroupSize=4; int attackMaximumGroupSize=6; //Attack 1 vars. int attackPlan1ID=-1; int maintainPlan1ID=-1; //Attack 2 vars. int attackPlan2ID=-1; int maintainPlan2ID=-1; //Defend Plan variable. int defendPlan1ID=-1; // Route and path vars. int attackRoute1ID=-1; int attackPath1ID=-1; int attackRoute2ID=-1; int attackPath2ID=-1; int attackRoute3ID=-1; int attackPath3ID=-1; int attackRoute4ID=-1; int attackPath4ID=-1; // Unit types int attackerUnitTypeID1=cUnitTypeSpearman; int attackerUnitTypeID2=cUnitTypeAxeman; int attackerUnitTypeID3=cUnitTypeSlinger; int attackerUnitTypeID4=cUnitTypeWarElephant; int attackerUnitTypeID5=cUnitTypeCamelry; int attackerUnitTypeID6=cUnitTypeChariotArcher; int attackerUnitTypeID7=cUnitTypeSiegeTower; int attackerUnitTypeID8=cUnitTypeAnubite; int attackerUnitTypeID9=cUnitTypeScarab; int attackerUnitTypeID10=cUnitTypeAvenger; int attackerUnitTypeID11=cUnitTypePriest; int randomUpgradeLine=-1; // Quicky function to set up a random upgrade line. void evaluateRandomUpgrade( void ) { int whichOne=aiRandInt(2); randomUpgradeLine=whichOne; } //========================================================================================= // Kidd's cool configQuery function: used to create attack routes, etc. Oooh, lovin' that! //========================================================================================= bool configQuery( int queryID = -1, int unitType = -1, int action = -1, int state = -1, int player = -1, vector center = vector(-1,-1,-1), bool sort = false, float radius = -1 ) { if ( queryID == -1) { return(false); } if (player != -1) kbUnitQuerySetPlayerID(queryID, player); if (unitType != -1) kbUnitQuerySetUnitType(queryID, unitType); if (action != -1) kbUnitQuerySetActionType(queryID, action); if (state != -1) kbUnitQuerySetState(queryID, state); if (center != vector(-1,-1,-1)) { kbUnitQuerySetPosition(queryID, center); if (sort == true) kbUnitQuerySetAscendingSort(queryID, true); if (radius != -1) kbUnitQuerySetMaximumDistance(queryID, radius); } return(true); } //============================================================================== // Queryin' for villagers near the Tamarisk Tree //============================================================================== int checkForVillagers(void) { static int villagerQueryID=-1; vector tamariskTree=kbGetBlockPosition("934"); int villagerCount=-1; if (villagerQueryID < 0) { // Doesn't exist, set it up villagerQueryID = kbUnitQueryCreate("Villager Query"); // Get the number if ( configQuery( villagerQueryID, cUnitTypeVillagerEgyptian, -1, cUnitStateAlive, 1, tamariskTree, false, 30 ) == false ) return(-1); } kbUnitQueryResetResults(villagerQueryID); villagerCount = kbUnitQueryExecute(villagerQueryID); return(villagerCount); } //============================================================================== // Queryin' for "my" units near the enemy's town - for tornado action. //============================================================================== int checkForTornado(void) { static int soldierQueryID=-1; vector enemyTown=kbGetBlockPosition("942"); int soldierCount=-1; if (soldierQueryID < 0) { // Doesn't exist, set it up soldierQueryID = kbUnitQueryCreate("Soldier Query"); // Get the number if ( configQuery( soldierQueryID, cUnitTypeUnit, -1, cUnitStateAlive, 2, enemyTown, false, 30 ) == false ) return(-1); } kbUnitQueryResetResults(soldierQueryID); soldierCount = kbUnitQueryExecute(soldierQueryID); return(soldierCount); } //============================================================================== // initAttack: Creates attack routes, etc. //============================================================================== void initAttack(int playerID=-1) { //Destroy all previous attacks (if this isn't the player we're already attacking. if (playerID != attackPlayerID) { //Reset the attack player ID. attackPlayerID=-1; //Destroy any previous attack plan. aiPlanDestroy(attackPlan1ID); attackPlan1ID=-1; aiPlanDestroy(attackPlan2ID); attackPlan2ID=-1; //Destroy our previous attack paths. kbPathDestroy(attackPath1ID); attackPath1ID=-1; kbPathDestroy(attackPath2ID); attackPath2ID=-1; kbPathDestroy(attackPath3ID); attackPath3ID=-1; kbPathDestroy(attackPath4ID); attackPath4ID=-1; //Destroy our previous attack routes. attackRoute1ID=-1; attackRoute2ID=-1; attackRoute3ID=-1; attackRoute4ID=-1; //Reset the number of attacks. numberAttacks=0; } //Save the player to attack. attackPlayerID=playerID; vector gatherPointLeft=kbGetBlockPosition("925"); vector gatherPointMid=kbGetBlockPosition("924"); vector gatherPointRight=kbGetBlockPosition("937"); //Setup attack path 1 - go left attackPath1ID=kbPathCreate("Attack Path 1"); kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("926")); kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("927")); kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("928")); kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("929")); kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("930")); kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("931")); //Create attack route 1. attackRoute1ID=kbCreateAttackRouteWithPath("Attack Route 1", gatherPointLeft, kbGetBlockPosition("932")); if (attackRoute1ID >= 0) kbAttackRouteAddPath(attackRoute1ID, attackPath1ID); //Setup attack path 2 - go down the middle attackPath2ID=kbPathCreate("Attack Path 2"); kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("933")); kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("934")); kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("935")); kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("936")); //Create attack route 2. attackRoute2ID=kbCreateAttackRouteWithPath("Attack Route 2", gatherPointMid, kbGetBlockPosition("932")); if (attackRoute2ID >= 0) kbAttackRouteAddPath(attackRoute2ID, attackPath2ID); //Setup attack path 3 - go right attackPath3ID=kbPathCreate("Attack Path 3"); kbPathAddWaypoint(attackPath3ID, kbGetBlockPosition("938")); kbPathAddWaypoint(attackPath3ID, kbGetBlockPosition("939")); kbPathAddWaypoint(attackPath3ID, kbGetBlockPosition("940")); //Create attack route 3. attackRoute3ID=kbCreateAttackRouteWithPath("Attack Route 3", gatherPointRight, kbGetBlockPosition("941")); //Setup attack path 4 - shortest route to Tamarisk tree. attackPath4ID=kbPathCreate("Attack Path 4"); kbPathAddWaypoint(attackPath4ID, kbGetBlockPosition("933")); //Create attack route 4. attackRoute4ID=kbCreateAttackRouteWithPath("Attack Route 3", gatherPointMid, kbGetBlockPosition("934")); if (attackRoute3ID >= 0) kbAttackRouteAddPath(attackRoute3ID, attackPath3ID); } //============================================================================== // setupBaseAttack - the primary attack setup. // Prioritizes enemy units instead of buildings. //============================================================================== bool setupBaseAttack(int playerID=-1) { int randomPath=aiRandInt(3); int randomCoreUnit=aiRandInt(2); int randomAnubite=aiRandInt(3); // If we have enough unassigned military units of either core type, create a new attack plan. int numberAvailableUnits1=aiNumberUnassignedUnits(attackerUnitTypeID1); int numberAvailableUnits2=aiNumberUnassignedUnits(attackerUnitTypeID2); // Bail if things ain't looking right, to avoid nasty idle attack plans. if ( randomCoreUnit == 0 ) { aiEcho("There are "+numberAvailableUnits1+" spearmen available for a new attack."); if (numberAvailableUnits1 < attackMinimumGroupSize) return( false ); } else { aiEcho("There are "+numberAvailableUnits2+" axemen available for a new attack."); if (numberAvailableUnits2 < attackMinimumGroupSize) return( false ); } //Info. aiEcho("Attacking Player "+playerID+"."); //If the player to attack doesn't match, init the attack. if (attackPlayerID != playerID) { initAttack(playerID); if (attackPlayerID < 0) return(false); } //Create an attack plan. int newAttackPlanID=aiPlanCreate("Attack Player"+attackPlayerID+" Attempt"+numberAttacks, cPlanAttack); if (newAttackPlanID < 0) return(false); //Target player (required). This must work. if (aiPlanSetVariableInt(newAttackPlanID, cAttackPlanPlayerID, 0, attackPlayerID) == false) return(false); //Gather point. vector gatherPointLeft=kbGetBlockPosition("925"); vector gatherPointMid=kbGetBlockPosition("933"); vector gatherPointRight=kbGetBlockPosition("937"); //Set the target type. This must work. if (aiPlanSetNumberVariableValues(newAttackPlanID, cAttackPlanTargetTypeID, 2, true) == false) return(false); //Unit types to attack. aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 0, cUnitTypeUnit); aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 1, cUnitTypeBuilding); switch(randomPath) { case 0: { aiEcho("Next attack going left."); aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute1ID); aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPointLeft); aiPlanSetInitialPosition(newAttackPlanID, gatherPointLeft); break; } case 1: { aiEcho("Next attack going down the middle."); aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute2ID); aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPointMid); aiPlanSetInitialPosition(newAttackPlanID, gatherPointMid); break; } case 2: { aiEcho("Next attack going right."); aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute3ID); aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPointRight); aiPlanSetInitialPosition(newAttackPlanID, gatherPointRight); break; } } //Set the gather point distance aiPlanSetVariableFloat(newAttackPlanID, cAttackPlanGatherDistance, 0, 30.0); //Set up the attack route usage pattern aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRoutePattern, 0, cAttackPlanAttackRoutePatternRandom); //Add the unit types to the plan - first, the "core unit." if ( randomCoreUnit == 0 ) { aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID1, attackMinimumGroupSize, attackMaximumGroupSize, attackMaximumGroupSize); } else { aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID2, attackMinimumGroupSize, attackMaximumGroupSize, attackMaximumGroupSize); } // Next, slingers. int slingerMin=attackMinimumGroupSize-1; int slingerMax=attackMaximumGroupSize-2; aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID3, slingerMin, slingerMax, slingerMax); // Finally, a possible random anubite (about a third of the time). if ( randomAnubite == 0 ) { aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID8, 0, 1, 1); } //Plan requires all need units to work (can be false) aiPlanSetRequiresAllNeedUnits(newAttackPlanID, true); //Activate the plan. aiPlanSetActive(newAttackPlanID); //Now, save the attack plan ID appropriately aiPlanSetOrphan(attackPlan1ID, true); attackPlan1ID=newAttackPlanID; //Increment our overall number of attacks numberAttacks++; } //============================================================================== // setupLargeAttack - a larger and more dangerous attack, sent more rarely. // // This attack scoops up whatever's sitting around (within reason) and sends it // in. The only attack to include siege stuff and priests. //============================================================================== bool setupLargeAttack(int playerID=-1) { // Get the difficulty level. difflevel=aiGetWorldDifficulty(); int randomPath=aiRandInt(3); int randomElephants=aiRandInt(4)+1; int numberAvailableUnits=aiNumberUnassignedUnits(attackerUnitTypeID3); // If the "tree is threatened" always go down the middle. if ( treethreat == 1 ) { randomPath = 1; } if ( difflevel == 3 ) { randomElephants = 8; } //Info. aiEcho("Attacking Player "+playerID+"."); //If the player to attack doesn't match, init the attack. if (attackPlayerID != playerID) { initAttack(playerID); if (attackPlayerID < 0) return(false); } // Bail if things ain't looking right, to avoid nasty idle attack plans. aiEcho("There are "+numberAvailableUnits+" slingers available for the large attack."); if (numberAvailableUnits < 1) return( false ); //Create an attack plan. int newAttackPlanID=aiPlanCreate("Attack Player"+attackPlayerID+" Attempt"+numberAttacks, cPlanAttack); if (newAttackPlanID < 0) return(false); //Target player (required). This must work. if (aiPlanSetVariableInt(newAttackPlanID, cAttackPlanPlayerID, 0, attackPlayerID) == false) return(false); //Gather point. vector gatherPointLeft=kbGetBlockPosition("925"); vector gatherPointMid=kbGetBlockPosition("933"); vector gatherPointRight=kbGetBlockPosition("937"); //Set the target type. This must work. if (aiPlanSetNumberVariableValues(newAttackPlanID, cAttackPlanTargetTypeID, 2, true) == false) return(false); //Unit types to attack. aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 0, cUnitTypeBuilding); aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 1, cUnitTypeUnit); switch(randomPath) { case 0: { aiEcho("Large attack going left."); aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute1ID); aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPointLeft); aiPlanSetInitialPosition(newAttackPlanID, gatherPointLeft); break; } case 1: { aiEcho("Large attack going down the middle."); aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute2ID); aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPointMid); aiPlanSetInitialPosition(newAttackPlanID, gatherPointMid); break; } case 2: { aiEcho("Large attack going right."); aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute3ID); aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPointRight); aiPlanSetInitialPosition(newAttackPlanID, gatherPointRight); break; } } //Set the gather point distance aiPlanSetVariableFloat(newAttackPlanID, cAttackPlanGatherDistance, 0, 30.0); //Set up the attack route usage pattern aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRoutePattern, 0, cAttackPlanAttackRoutePatternRandom); // Base guys - scoop up whatever we have aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID1, 0, attackMaximumGroupSize, attackMaximumGroupSize); aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID2, 0, attackMaximumGroupSize, attackMaximumGroupSize); // At least one skirmisher aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID3, 1, 2, 2); // Elephants, of course - a random number of 'em unless it's easy level. On Titan, this can be up to 8. if ( difflevel >= 1 ) { aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID4, 0, randomElephants, randomElephants); } // Siege if we have it if ( difflevel >= 1 ) { aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID7, 0, 2, 4); } else { aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID7, 0, 1, 1); } if ( difflevel >= 1 ) { aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID9, 0, 2, 2); } // Priests for healing and such aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID11, 0, 2, 2); //Plan requires all need units to work (can be false) aiPlanSetRequiresAllNeedUnits(newAttackPlanID, true); //Activate the plan. aiPlanSetActive(newAttackPlanID); //Now, save the attack plan ID appropriately aiPlanSetOrphan(attackPlan2ID, true); attackPlan2ID=newAttackPlanID; //Increment our overall number of attacks numberAttacks++; } //============================================================================== // Attack Generator 1 - Base attack, every two minutes, once activated. //============================================================================== rule attackGenerator1 minInterval 120 inactive group AttackRules runImmediately { // See how many "idle" attack plans we have. Don't create any more if we have // idle plans. int numberIdleAttackPlans=aiGetNumberIdlePlans(cPlanAttack); if (numberIdleAttackPlans > 0) return; setupBaseAttack(1); } //============================================================================== // Attack Generator 1 Easy - Base attack, a little slower and starts later. //============================================================================== rule attackGenerator1Easy minInterval 180 inactive group AttackRules { // See how many "idle" attack plans we have. Don't create any more if we have // idle plans. int numberIdleAttackPlans=aiGetNumberIdlePlans(cPlanAttack); if (numberIdleAttackPlans > 0) return; setupBaseAttack(1); } //============================================================================== // Attack Generator 2 - Crazy large attack // Do it randomly, 2/3rds of the time, every three and a half minutes // Doesn't start until about 10-12 minutes into the game. //============================================================================== rule attackGenerator2 minInterval 210 inactive group AttackRules { int attackNow=aiRandInt(3); if ( attackNow < 2 ) { // See how many "idle" attack plans we have. Don't create any more if we have // idle plans. int numberIdleAttackPlans=aiGetNumberIdlePlans(cPlanAttack); if (numberIdleAttackPlans > 0) return; setupLargeAttack(1); } } //============================================================================== // Attack Generator 2 Easy - Crazy large attack, easy version. Slows down things // a little, and reduces probability of a large attack going. //============================================================================== rule attackGenerator2Easy minInterval 240 inactive group AttackRules { int attackNow=aiRandInt(4); if ( attackNow < 2 ) { // See how many "idle" attack plans we have. Don't create any more if we have // idle plans. int numberIdleAttackPlans=aiGetNumberIdlePlans(cPlanAttack); if (numberIdleAttackPlans > 0) return; setupLargeAttack(1); } } //============================================================================== // Attack enablers - enable attacks after initial timer expires //============================================================================== rule baseAttackEnabler minInterval 180 active group AttackRules { // Get the difficulty level. difflevel=aiGetWorldDifficulty(); // On Easy, don't attack for a little while. if ( difflevel == 0 ) { xsEnableRule("attackGenerator1Easy"); } else { xsEnableRule("attackGenerator1"); } xsDisableSelf(); } //============================================================================== // Tech Researching Rules - Age 2/3 //============================================================================== rule researchMediumUpgrade minInterval 420 active { int planID=aiPlanCreate("Medium Infantry research", cPlanResearch); if (planID < 0) return; if ( randomUpgradeLine == 0 ) { aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechMediumAxemen); } else { aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechMediumSpearmen); } aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeBarracks); aiPlanSetActive(planID); //Done. xsDisableSelf(); } rule researchSerpentSpear minInterval 480 active { if ( randomUpgradeLine == 0 ) { int planID=aiPlanCreate("Serpent Spear research", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechSerpentSpear); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeBarracks); aiPlanSetActive(planID); } //Done. xsDisableSelf(); } rule researchMediumSlingers minInterval 540 active { int planID=aiPlanCreate("Medium Slingers research", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechMediumSlingers); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeBarracks); aiPlanSetActive(planID); //Done. xsDisableSelf(); } rule researchHeavyUpgrade minInterval 720 active group ModerateResearchRules { int planID=aiPlanCreate("Heavy Infantry research", cPlanResearch); if (planID < 0) return; if ( randomUpgradeLine == 0 ) { aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechHeavyAxemen); } else { aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechHeavySpearmen); } aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeBarracks); aiPlanSetActive(planID); //Done. xsDisableSelf(); } rule researchHeavySlingers minInterval 1000 active group ModerateResearchRules { int planID=aiPlanCreate("Heavy Slingers research", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechHeavySlingers); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeBarracks); aiPlanSetActive(planID); //Done. xsDisableSelf(); } rule researchCopperWeapons minInterval 540 active group ModerateResearchRules { int planID=aiPlanCreate("Copper weapons research", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechCopperWeapons); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeArmory); aiPlanSetActive(planID); //Done. xsDisableSelf(); } rule researchCopperShields minInterval 600 active group ModerateResearchRules { int planID=aiPlanCreate("Copper shields research", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechCopperShields); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeArmory); aiPlanSetActive(planID); //Done. xsDisableSelf(); } rule researchBronzeWeapons minInterval 780 active group ModerateResearchRules { int planID=aiPlanCreate("Bronze weapons research", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechBronzeWeapons); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeArmory); aiPlanSetActive(planID); //Done. xsDisableSelf(); } rule researchBronzeShields minInterval 900 active group ModerateResearchRules { int planID=aiPlanCreate("Bronze shields research", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechBronzeShields); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeArmory); aiPlanSetActive(planID); //Done. xsDisableSelf(); } rule researchCopperMail minInterval 1100 active group ModerateResearchRules { int planID=aiPlanCreate("Copper mail research", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechCopperMail); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeArmory); aiPlanSetActive(planID); //Done. xsDisableSelf(); } rule researchHeavyElephants minInterval 1260 active group ModerateResearchRules { int planID=aiPlanCreate("Heavy Elephants research", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechHeavyElephants); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeMigdolStronghold); aiPlanSetActive(planID); //Done. xsDisableSelf(); } rule researchBronzeMail minInterval 1650 active group ModerateResearchRules { int planID=aiPlanCreate("Bronze mail research", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechBronzeMail); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeArmory); aiPlanSetActive(planID); //Done. xsDisableSelf(); } //============================================================================== // Age 4 researches - activated by the "researchingAge4" thing. //============================================================================== rule researchChampionUpgrade minInterval 420 inactive { int planID=aiPlanCreate("Medium Infantry research", cPlanResearch); if (planID < 0) return; if ( randomUpgradeLine == 0 ) { aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechChampionAxemen); } else { aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechChampionSpearmen); } aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeBarracks); aiPlanSetActive(planID); //Done. xsDisableSelf(); } rule researchMythTechUpgrade minInterval 660 inactive { int planID=aiPlanCreate("Odd myth tech research", cPlanResearch); if (planID < 0) return; if ( randomUpgradeLine == 0 ) { aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechSpearontheHorizon); } else { aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechAxeofVengeance); } aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeBarracks); aiPlanSetActive(planID); //Done. xsDisableSelf(); } rule researchEngineers minInterval 750 inactive { int planID=aiPlanCreate("Engineers research", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechEngineers); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeSiegeCamp); aiPlanSetActive(planID); //Done. xsDisableSelf(); } rule researchSlingsOfTheSun minInterval 900 inactive { int planID=aiPlanCreate("Slings of the Sun research", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, ); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeBarracks); aiPlanSetActive(planID); //Done. xsDisableSelf(); } rule researchIronWeapons minInterval 1200 inactive { int planID=aiPlanCreate("Iron weapons research", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechIronWeapons); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeArmory); aiPlanSetActive(planID); //Done. xsDisableSelf(); } //============================================================================== // More Maintaining - starts at the twelve-minute mark //============================================================================== rule moreMaintaining minInterval 720 active { // Get the difficulty level. difflevel=aiGetWorldDifficulty(); aiEcho("*** NOW MAINTAINING LOTS MORE UNIT TYPES ***"); int numElephants = 4; int numSiegeTowers = 2; int numScarabs = 2; int numPriests = 4; if ( difflevel == 3 ) { numElephants = 8; } //Three gather points vector gatherPoint=kbGetBlockPosition("924"); vector gatherPoint2=kbGetBlockPosition("1175"); vector gatherPoint3=kbGetBlockPosition("1176"); int maintainPlan4ID=aiPlanCreate("Maintain "+numElephants+" "+kbGetProtoUnitName(attackerUnitTypeID4), cPlanTrain); // Maintain war elephants if (maintainPlan4ID >= 0) { //Must set the type of unit to train. aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanUnitType, 0, attackerUnitTypeID4); //Set the number of units to maintain in the world at one time. aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanNumberToMaintain, 0, numElephants); //Don't train units faster than every 90 seconds if ( difflevel == 3 ) { aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanFrequency, 0, 45); } else { aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanFrequency, 0, 90); } //Set a gather point. aiPlanSetVariableVector(maintainPlan4ID, cTrainPlanGatherPoint, 0, gatherPoint); //Activate the plan. aiPlanSetActive(maintainPlan4ID); } int maintainPlan7ID=aiPlanCreate("Maintain "+numSiegeTowers+" "+kbGetProtoUnitName(attackerUnitTypeID7), cPlanTrain); // Maintain siege towers. if (maintainPlan7ID >= 0) { //Must set the type of unit to train. aiPlanSetVariableInt(maintainPlan7ID, cTrainPlanUnitType, 0, attackerUnitTypeID7); //Set the number of units to maintain in the world at one time. aiPlanSetVariableInt(maintainPlan7ID, cTrainPlanNumberToMaintain, 0, numSiegeTowers); //Don't train units faster than every two minutes aiPlanSetVariableInt(maintainPlan7ID, cTrainPlanFrequency, 0, 240); //Set a gather point. aiPlanSetVariableVector(maintainPlan7ID, cTrainPlanGatherPoint, 0, gatherPoint2); //Activate the plan. aiPlanSetActive(maintainPlan7ID); } int maintainPlan9ID=aiPlanCreate("Maintain "+numScarabs+" "+kbGetProtoUnitName(attackerUnitTypeID9), cPlanTrain); // Maintain scarabs. if (maintainPlan9ID >= 0) { //Must set the type of unit to train. aiPlanSetVariableInt(maintainPlan9ID, cTrainPlanUnitType, 0, attackerUnitTypeID9); //Set the number of units to maintain in the world at one time. aiPlanSetVariableInt(maintainPlan9ID, cTrainPlanNumberToMaintain, 0, numScarabs); //Don't train units too fast aiPlanSetVariableInt(maintainPlan9ID, cTrainPlanFrequency, 0, 250); //Set a gather point. aiPlanSetVariableVector(maintainPlan9ID, cTrainPlanGatherPoint, 0, gatherPoint3); //Activate the plan. aiPlanSetActive(maintainPlan9ID); } int maintainPlan11ID=aiPlanCreate("Maintain "+numScarabs+" "+kbGetProtoUnitName(attackerUnitTypeID11), cPlanTrain); // Maintain scarabs. if (maintainPlan11ID >= 0) { //Must set the type of unit to train. aiPlanSetVariableInt(maintainPlan11ID, cTrainPlanUnitType, 0, attackerUnitTypeID11); //Set the number of units to maintain in the world at one time. aiPlanSetVariableInt(maintainPlan11ID, cTrainPlanNumberToMaintain, 0, numPriests); //Don't train units too fast aiPlanSetVariableInt(maintainPlan11ID, cTrainPlanFrequency, 0, 275); //Set a gather point. aiPlanSetVariableVector(maintainPlan11ID, cTrainPlanGatherPoint, 0, gatherPoint); //Activate the plan. aiPlanSetActive(maintainPlan11ID); } // Enable the large attack generator if ( difflevel == 0 ) { xsEnableRule("attackGenerator2Easy"); } else { xsEnableRule("attackGenerator2"); } xsDisableSelf(); } //============================================================================== // GOD POWERS // Vision two minutes in on enemy TC (he guesses good). // Serpents when the Tamarisk Tree is being worked on. // Tornado in an attack post-Age 4 that gets close to the player's TC. //============================================================================== rule fireVision minInterval 120 active group GodPowers { vector visionPoint=kbGetBlockPosition("942"); aiEcho("Casting Vision."); if ( aiCastGodPowerAtPosition(cTechVision, visionPoint) == true ) xsDisableSelf(); else aiEcho("Vision failed - try again later."); } // "Time for Serpents" also builds a wall and a Migdol. rule timeForSerpents minInterval 15 active group GodPowers { int numVillagers=-1; vector serpentPoint=kbGetBlockPosition("934"); vector MigdolPoint=kbGetBlockPosition("1037"); vector WallPointA=kbGetBlockPosition("1035"); vector WallPointB=kbGetBlockPosition("1036"); vector WallPointC=kbGetBlockPosition("1065"); vector WallPointD=kbGetBlockPosition("1066"); numVillagers=checkForVillagers(); if (numVillagers > 3) { aiEcho("Villagers near Tamarisk Tree, cast Serpents and do other nasty things."); if ( aiCastGodPowerAtPosition(cTechSerpents, serpentPoint) == true ) aiEcho("Serpents cast."); else aiEcho("Serpents failed."); // Put up a couple of walls. aiWallFromAToB("Build Wall 1", WallPointA, WallPointB, 1, 1, 1, cRootEscrowID, 1 ); aiWallFromAToB("Build Wall 2", WallPointC, WallPointD, 1, 1, 1, cRootEscrowID, 1 ); //-- Build a migdol to cover the tower.. int buildMigdol = aiPlanCreate("Build Migdol", cPlanBuild); if(buildMigdol >= 0) { //BP Type and Priority. aiPlanSetVariableInt(buildMigdol, cBuildPlanBuildingTypeID, 0, cUnitTypeTower); aiPlanSetDesiredPriority(buildMigdol, 80); aiPlanSetVariableVector(buildMigdol, cBuildPlanCenterPosition, 0, MigdolPoint); aiPlanSetVariableFloat(buildMigdol, cBuildPlanCenterPositionDistance, 0, 5); aiPlanAddUnitType(buildMigdol, cUnitTypeVillagerEgyptian, 4, 4, 4); aiPlanSetEscrowID(buildMigdol, cRootEscrowID); aiPlanSetActive(buildMigdol); } xsDisableSelf(); } } rule timeForTornado minInterval 15 inactive group GodPowers { int numAttackers=-1; vector tornadoPoint=kbGetBlockPosition("942"); numAttackers=checkForTornado(); if (numAttackers > 0 && numAttackers < 8 ) { aiEcho("Approaching enemy town, cast Tornado."); if ( aiCastGodPowerAtPosition(cTechTornado, tornadoPoint) == true ) xsDisableSelf(); else aiEcho("Tornado failed - try again later."); } } //============================================================================== // Advance to Age 4 //============================================================================== rule researchAge4 minInterval 1600 active group ModerateResearchRules { int planID=aiPlanCreate("Age 4 Horus Advance", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechAge4Horus); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeSettlementLevel1); aiPlanSetActive(planID); // Enable the large attack generator xsEnableRule("researchChampionUpgrade"); xsEnableRule("researchMythTechUpgrade"); xsEnableRule("researchEngineers"); xsEnableRule("timeForTornado"); xsEnableRule("researchIronWeapons"); xsEnableRule("researchSlingsOfTheSun"); aiEcho("*** ADVANCING TO AGE 4 ***"); //Done. xsDisableSelf(); } // Scary Rule, uh oh...gets villagers to go away after doing their thing (in theory) rule taskBuilders minInterval 20 active { static int villQuery = -1; int enemyCount=-1; vector forwardPosition=kbGetBlockPosition("1037"); int villCount = -1; if (villQuery < 0) // need to define query { villQuery = kbUnitQueryCreate("checkBuilders"); // Look for HP units if ( configQuery(villQuery, cUnitTypeVillagerEgyptian, cActionIdle, cUnitStateAlive, 2, forwardPosition, false, 15) == false) return; } kbUnitQueryResetResults(villQuery); villCount = kbUnitQueryExecute(villQuery); if (villCount == 0) return; int vill = -1; for (i=0; < villCount) { vill = kbUnitQueryGetResult(villQuery, i); aiTaskUnitWork(vill, kbGetBlockID("1114")); // move 'em to a farm. } } //============================================================================== // defendTree. Called with an AI FUNC when it's time to defend Mr. Tree. // // Only happens on Hard and Titan. //============================================================================== void defendTree(int scriptCall = -1) { vector treeDefendPoint=kbGetBlockPosition("934"); aiEcho("*** DEFEND THE TREE! OMG, DEFEND THE F'IN TREE!!!"); // Tree now threatened, behavior changes on hard and nightmare. treethreat=1; // Defend the tree! Do this while you're doin' other stuff... int defendPlan1ID=aiPlanCreate("Tree Defense", cPlanDefend); if (defendPlan1ID >= 0) { //Add the unit(s) - priests and anubites, the elite guard! aiPlanAddUnitType(defendPlan1ID, attackerUnitTypeID8, 6, 6, 6); aiPlanAddUnitType(defendPlan1ID, attackerUnitTypeID11, 2, 2, 2); //Setup the vars. aiPlanSetDesiredPriority(defendPlan1ID, 75); aiPlanSetVariableVector(defendPlan1ID, cDefendPlanDefendPoint, 0, treeDefendPoint); aiPlanSetVariableFloat(defendPlan1ID, cDefendPlanEngageRange, 0, 25); aiPlanSetActive(defendPlan1ID); } } //============================================================================== // MAIN //============================================================================== void main(void) { //Startup. miscStartup(); initEcon(); evaluateRandomUpgrade(); // If difficulty level is easy, kill a bunch of researches. if ( difflevel == 0 ) { xsDisableRuleGroup( "ModerateResearchRules" ); } //Share the number to maintain. int numberToMaintain=attackMinimumGroupSize*2; //Various gather points vector gatherPoint=kbGetBlockPosition("924"); vector gatherPoint2=kbGetBlockPosition("1175"); vector gatherPoint3=kbGetBlockPosition("1176"); vector gatherPointAnubite=kbGetBlockPosition("1227"); //Create a simple plan to maintain X spearmen. int maintainPlan1ID=aiPlanCreate("Maintain "+numberToMaintain+" "+kbGetProtoUnitName(attackerUnitTypeID1), cPlanTrain); if (maintainPlan1ID >= 0) { //Must set the type of unit to train. aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanUnitType, 0, attackerUnitTypeID1); //You can limit the number of units that are ever trained by this plan with this call. //aiPlanSetVariableInt(maintainPlanID, cTrainPlanNumberToTrain, 0, 25); //Set the number of units to maintain in the world at one time. aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanNumberToMaintain, 0, numberToMaintain); //Don't train units faster than every 25 seconds. aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanFrequency, 0, 25); //Set a gather point. aiPlanSetVariableVector(maintainPlan1ID, cTrainPlanGatherPoint, 0, gatherPoint3); //Activate the plan. aiPlanSetActive(maintainPlan1ID); } //Create a simple plan to maintain X axemen. int maintainPlan2ID=aiPlanCreate("Maintain "+numberToMaintain+" "+kbGetProtoUnitName(attackerUnitTypeID2), cPlanTrain); if (maintainPlan2ID >= 0) { //Must set the type of unit to train. aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanUnitType, 0, attackerUnitTypeID2); //You can limit the number of units that are ever trained by this plan with this call. //aiPlanSetVariableInt(maintainPlanID, cTrainPlanNumberToTrain, 0, 25); //Set the number of units to maintain in the world at one time. aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanNumberToMaintain, 0, numberToMaintain); //Don't train units faster than every 40 seconds aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanFrequency, 0, 40); //Set a gather point. aiPlanSetVariableVector(maintainPlan2ID, cTrainPlanGatherPoint, 0, gatherPoint2); //Activate the plan. aiPlanSetActive(maintainPlan2ID); } //Create a simple plan to maintain X slingers. int maintainPlan3ID=aiPlanCreate("Maintain "+numberToMaintain+" "+kbGetProtoUnitName(attackerUnitTypeID3), cPlanTrain); if (maintainPlan3ID >= 0) { //Must set the type of unit to train. aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanUnitType, 0, attackerUnitTypeID3); //You can limit the number of units that are ever trained by this plan with this call. //aiPlanSetVariableInt(maintainPlanID, cTrainPlanNumberToTrain, 0, 25); //Set the number of units to maintain in the world at one time. aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanNumberToMaintain, 0, numberToMaintain); //Don't train units faster than every 35 seconds aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanFrequency, 0, 35); //Set a gather point. aiPlanSetVariableVector(maintainPlan3ID, cTrainPlanGatherPoint, 0, gatherPoint); //Activate the plan. aiPlanSetActive(maintainPlan3ID); } //Anubites for the defend plan. int maintainPlan8ID=aiPlanCreate("Maintain "+numberToMaintain+" "+kbGetProtoUnitName(attackerUnitTypeID3), cPlanTrain); if (maintainPlan8ID >= 0) { //Must set the type of unit to train. aiPlanSetVariableInt(maintainPlan8ID, cTrainPlanUnitType, 0, attackerUnitTypeID8); //You can limit the number of units that are ever trained by this plan with this call. //aiPlanSetVariableInt(maintainPlanID, cTrainPlanNumberToTrain, 0, 25); //Set the number of units to maintain in the world at one time. aiPlanSetVariableInt(maintainPlan8ID, cTrainPlanNumberToMaintain, 0, 8); //These train back pretty slowly, giving you time to deal with 'em. aiPlanSetVariableInt(maintainPlan8ID, cTrainPlanFrequency, 0, 90); //Set a gather point. aiPlanSetVariableVector(maintainPlan8ID, cTrainPlanGatherPoint, 0, gatherPointAnubite); //Activate the plan. aiPlanSetActive(maintainPlan8ID); } }