home *** CD-ROM | disk | FTP | other *** search
Wrap
//============================================================================== // Scn05p2: AI Scenario Script for p2 //============================================================================== /* AI owner: Dave Leary Scenario owner: Dave Leary The AI for the enemy (pirate camp) in scenario 5. Fairly simple. Maintains axemen and slingers. Periodically sends out small groups to attack the player, choosing one of two directions (north or south) for axemen groups. Sends slingers to the cliff above the player's first gold mining area (but don't be surprised if they retarget and move once they get there). Standard attack grower code is in place to increase the size of the groups over time. One large attack is launched with the "doBigAttack" rule late in the game (currently 1320 seconds in); this grabs up a random selection of other units preplaced on the map (typically catapults and minotaurs). Naval stuff has currently been taken out of the scenario, but may be added back in as time allows. The AI is cheating like mad. Don't look for it to gather resources; it has plenty of everything it might need. *** DIFFICULTY LEVEL NOTES *** Easy level - no towers at all. Slingers do not attack. Large attack never comes. Enemy does not research. Moderate level - a couple of towers; no towers at the back entrance. Difficult level - additional towers. Attack group sizes grow over time. Fewer resources from shipwreck event. Kebenits attack player's ships and buildings. Nightmare - additional towers, including a forward-built tower. Reduced starting food sources and gold. Attack group sizes grow over time. Larger starting attack groups. Attack groups continue to grow every fifteen minutes until player is overwhelmed. Kebenits attack player's ships and buildings; more kebenits than on difficult. Fewer resources from shipwreck event. */ //============================================================================== // Set Town Location //============================================================================== void setTownLocation(void) { //Look for the "Town Location" marker. kbSetTownLocation(kbGetBlockPosition("569")); } //============================================================================== // miscStartup //============================================================================== void miscStartup(void) { // Difficulty Level check. int difflevel=-1; difflevel=aiGetWorldDifficulty(); //Startup message(s). aiEcho(""); aiEcho(""); aiEcho("Scn05P2 AI Start, filename='"+cFilename+"'."); //Spit out the map size. aiEcho(" Map size is ("+kbGetMapXSize()+", "+kbGetMapZSize()+")."); //Cheat like a bastard. Once only, though. kbLookAtAllUnitsOnMap(); //Calculate some areas. kbAreaCalculate(1200.0); //Set our town location. setTownLocation(); //Reset random seed aiRandSetSeed(); //Allocate all resources to the root escrow. kbEscrowAllocateCurrentResources(); // Drop the AI attack response distance for this player to 20 meters. aiSetAttackResponseDistance(20.0); } //============================================================================== //============================================================================== // Attack stuff. //============================================================================== //============================================================================== //Shared variables. int numberAttacks=0; int attackPlayerID=-1; //TODO: Decide how to rep attack group size. int attackMinimumGroupSize=3; int attackMaximumGroupSize=4; //Attack 1 vars. int attackPlan1ID=-1; int maintainPlan1ID=-1; int attackerUnitTypeID1=cUnitTypeAxeman; //Attack 2 vars. int attackPlan2ID=-1; int maintainPlan2ID=-1; int attackerUnitTypeID2=cUnitTypeSlinger; // Boat attack vars int attackPlan3ID=-1; int maintainPlan3ID=-1; int attackerUnitTypeID3=cUnitTypeKebenit; // Other unit types int attackerUnitTypeID4=cUnitTypeSpearman; int attackerUnitTypeID5=cUnitTypeCatapult; int attackerUnitTypeID6=cUnitTypeAnubite; // 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; // A check for launching a single big attack (several minutes in). bool launchBigAttack=false; //========================================================================================= // 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); } //============================================================================== // 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; aiPlanDestroy(attackPlan3ID); attackPlan3ID=-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 gatherPoint=kbGetBlockPosition("1954"); //Setup attack path 1 - the northern route attackPath1ID=kbPathCreate("Attack Path 1"); kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("1955")); kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("1956")); kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("1957")); //Create attack route 1. attackRoute1ID=kbCreateAttackRouteWithPath("Attack Route 1", kbGetBlockPosition("1954"), kbGetBlockPosition("1958")); if (attackRoute1ID >= 0) kbAttackRouteAddPath(attackRoute1ID, attackPath1ID); //Setup attack path 2 - the southern route attackPath2ID=kbPathCreate("Attack Path 2"); kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("1961")); kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("1962")); kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("1963")); //Create attack route 2. attackRoute2ID=kbCreateAttackRouteWithPath("Attack Route 2", kbGetBlockPosition("1954"), kbGetBlockPosition("1964")); if (attackRoute2ID >= 0) kbAttackRouteAddPath(attackRoute2ID, attackPath2ID); // Create attack route 3 (ship route) attackPath3ID=kbPathCreate("Attack Path 3"); kbPathAddWaypoint(attackPath3ID, kbGetBlockPosition("1967")); kbPathAddWaypoint(attackPath3ID, kbGetBlockPosition("1968")); //Create attack route 3. attackRoute3ID=kbCreateAttackRouteWithPath("Attack Route 3", kbGetBlockPosition("1966"), kbGetBlockPosition("1969")); if (attackRoute3ID >= 0) kbAttackRouteAddPath(attackRoute3ID, attackPath3ID); // Create attack route 4 (slinger route) attackPath4ID=kbPathCreate("Attack Path 4"); kbPathAddWaypoint(attackPath4ID, kbGetBlockPosition("1959")); //Create attack route 4 attackRoute4ID=kbCreateAttackRouteWithPath("Attack Route 4", kbGetBlockPosition("1954"), kbGetBlockPosition("1960")); if (attackRoute4ID >= 0) kbAttackRouteAddPath(attackRoute4ID, attackPath4ID); } //============================================================================== // setupAxemenAttack //============================================================================== bool setupAxemenAttack(int playerID=-1) { int randomPath=aiRandInt(2); //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 gatherPoint=kbGetBlockPosition("1954"); //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); //Attack route. if (randomPath == 0) { aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute1ID); aiEcho("AI Attack Path: chose northern route."); } else { aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute2ID); aiEcho("AI Attack Path: chose southern route."); } //Set the gather point and gather point distance. aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPoint); 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. aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID1, attackMinimumGroupSize, attackMaximumGroupSize, attackMaximumGroupSize); // Is this the big attack? If so, throw in a random mix of "extra" units. if ( launchBigAttack == true ) { int randomGroup=aiRandInt(5); aiEcho("AI Attack: Big attack heading in!"); switch(randomGroup) { case 0: { aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID4, 0, 3, 3); aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID6, 0, 2, 2); break; } case 1: { aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID4, 0, 4, 4); aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID5, 0, 1, 1); break; } case 2: { aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID4, 0, 2, 2); aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID5, 0, 1, 1); aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID6, 0, 4, 4); break; } case 3: { aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID4, 0, 4, 4); aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID5, 0, 1, 1); aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID6, 0, 2, 2); break; } case 4: { aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID4, 0, 5, 5); aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID5, 0, 1, 1); aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID6, 0, 1, 1); break; } } launchBigAttack=false; } //Set the initial position. aiPlanSetInitialPosition(newAttackPlanID, gatherPoint); //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++; } //============================================================================== // setupSlinger //============================================================================== bool setupSlingerAttack(int playerID=-1) { //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 gatherPoint=kbGetBlockPosition("1954"); //Set the target type. This must work. if (aiPlanSetNumberVariableValues(newAttackPlanID, cAttackPlanTargetTypeID, 3, true) == false) return(false); //Unit types to attack - slingers go for your villagers, to piss you off. aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 0, cUnitTypeVillagerGreek); aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 1, cUnitTypeUnit); aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 2, cUnitTypeBuilding); // Route to take aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute4ID); //Set the gather point and gather point distance. aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPoint); 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. aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID2, attackMinimumGroupSize, attackMaximumGroupSize, attackMaximumGroupSize); //Set the initial position. aiPlanSetInitialPosition(newAttackPlanID, gatherPoint); //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++; } //============================================================================== // setupBoatAttack (on Hard or better only) //============================================================================== bool setupBoatAttack(int playerID=-1) { // Difficulty Level check int difflevel=-1; difflevel=aiGetWorldDifficulty(); //Info. aiEcho("SHIPS 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 gatherPoint=kbGetBlockPosition("1966"); //Set the target type. This must work. if (aiPlanSetNumberVariableValues(newAttackPlanID, cAttackPlanTargetTypeID, 2, true) == false) return(false); //Unit types to attack - slingers go for your villagers, to piss you off. aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 0, cUnitTypeShip); aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 1, cUnitTypeBuilding); // Route to take aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute3ID); //Set the gather point and gather point distance. aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPoint); 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. if ( difflevel < 3 ) { aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID3, 1, 2, 2); } else { aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID3, 1, 4, 4); } //Set the initial position. aiPlanSetInitialPosition(newAttackPlanID, gatherPoint); //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(attackPlan3ID, true); attackPlan3ID=newAttackPlanID; //Increment our overall number of attacks. numberAttacks++; } //============================================================================== // Queryin' for enemies, for casting Serpents (cool!) //============================================================================== int checkForEnemies(void) { static int enemyQueryID=-1; vector homeBase=kbGetBlockPosition("569"); int enemyCount=-1; if (enemyQueryID < 0) { // Doesn't exist, set it up enemyQueryID = kbUnitQueryCreate("Enemy Query"); // Get the number if ( configQuery( enemyQueryID, cUnitTypeUnit, -1, cUnitStateAlive, 1, homeBase, false, 15 ) == false ) return(-1); } kbUnitQueryResetResults(enemyQueryID); enemyCount = kbUnitQueryExecute(enemyQueryID); return(enemyCount); } //============================================================================== // Attack Generator 1 - Axemen and friends //============================================================================== rule attackGenerator1 minInterval 90 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; //If we have enough unassigned military units, create a new attack plan. int numberAvailableUnits=aiNumberUnassignedUnits(attackerUnitTypeID1); aiEcho("There are "+numberAvailableUnits+" axemen available for a new attack."); if (numberAvailableUnits >= attackMinimumGroupSize) setupAxemenAttack(1); } //============================================================================== // Attack Generator 2 - Slingers //============================================================================== rule attackGenerator2 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; //If we have enough unassigned military units, create a new attack plan. int numberAvailableUnits=aiNumberUnassignedUnits(attackerUnitTypeID2); aiEcho("There are "+numberAvailableUnits+" slingers available for a new attack."); if (numberAvailableUnits >= attackMinimumGroupSize) setupSlingerAttack(1); } //============================================================================== // Attack Generator 3 - Boats, every seven minutes. //============================================================================== rule attackGenerator3 minInterval 420 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; //If we have enough unassigned military units, create a new attack plan. int numberAvailableUnits=aiNumberUnassignedUnits(attackerUnitTypeID3); aiEcho("There are "+numberAvailableUnits+" kebenits available for a boat attack."); if (numberAvailableUnits >= 1) setupBoatAttack(1); } //============================================================================== // Attack enablers - enable attacks after initial timers expire // Lots of delay, giving time for newbies to screw around //============================================================================== // Axemen rule attack1Enabler minInterval 420 active group AttackRules { xsEnableRule("attackGenerator1"); xsDisableSelf(); } // Hard or higher attack enabler - three minutes in rule attack1EnablerHard minInterval 180 active group AttackRules { int difflevel=-1; difflevel=aiGetWorldDifficulty(); if ( difflevel > 1 ) { xsEnableRule("attackGenerator1"); xsDisableRule("attack1Enabler"); } xsDisableSelf(); } // Slingers rule attack2Enabler minInterval 420 active group ModerateAttackRules { xsEnableRule("attackGenerator2"); xsDisableSelf(); } // Titan or higher attack enabler 2 - slinger pain rule attack2EnablerNightmare minInterval 200 active group AttackRules { int difflevel=-1; difflevel=aiGetWorldDifficulty(); if ( difflevel > 2 ) { xsEnableRule("attackGenerator2"); xsDisableRule("attack2Enabler"); } xsDisableSelf(); } //============================================================================== // Attack size grower: Slightly grows attack sizes of slinger and axemen groups // at the 15-minute mark. //============================================================================== rule attackGrower1 minInterval 900 active group DifficultAttackRules { // Difficulty Level check int difflevel=-1; difflevel=aiGetWorldDifficulty(); //Increase our attack group sizes by one. attackMinimumGroupSize++; attackMaximumGroupSize++; aiEcho("Attack group size grown: Min="+attackMinimumGroupSize+", Max="+attackMaximumGroupSize+"."); //Bump up our maintain size. int numberToMaintain=attackMinimumGroupSize*2; aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanNumberToMaintain, 0, numberToMaintain); aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanNumberToMaintain, 0, numberToMaintain); // Scary - we don't disable this on nightmare until attack groups are really big. :). if ( difflevel <= 2 || attackMinimumGroupSize > 15 ) { xsDisableSelf(); } } //============================================================================== // Attack size grower 2: Slightly grows attack sizes of slinger and axemen groups // a second time at the 20-minute mark. // // This rule is disabled on normal and easy levels. //============================================================================== rule attackGrower2 minInterval 1200 active group DifficultAttackRules { //Increase our attack group sizes by one. attackMinimumGroupSize++; attackMaximumGroupSize++; aiEcho("Attack group size grown: Min="+attackMinimumGroupSize+", Max="+attackMaximumGroupSize+"."); //Bump up our maintain size. int numberToMaintain=attackMinimumGroupSize*2; aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanNumberToMaintain, 0, numberToMaintain); aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanNumberToMaintain, 0, numberToMaintain); xsDisableSelf(); } //============================================================================== // Tech Researching Rules - medium axemen, medium slingers, medium spearmen //============================================================================== rule researchMediumAxemen minInterval 1000 active group ModerateResearchRules { int planID=aiPlanCreate("Medium Axemen research at ten minutes.", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechMediumAxemen); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeBarracks); aiPlanSetActive(planID); //Done. xsDisableSelf(); } rule researchMediumSpearman minInterval 1300 active group ModerateResearchRules { int planID=aiPlanCreate("Medium Spearmen research at twelve minutes.", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechMediumSpearmen); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeBarracks); aiPlanSetActive(planID); //Done. xsDisableSelf(); } rule researchMediumSlinger minInterval 1600 active group ModerateResearchRules { int planID=aiPlanCreate("Medium Slinger research at fourteen minutes.", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechMediumSlingers); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeBarracks); aiPlanSetActive(planID); //Done. xsDisableSelf(); } //============================================================================== // At twenty minutes, crank out axemen every five seconds //============================================================================== rule increaseAxemanTrainFrequency minInterval 1200 active group ModerateAttackRules { aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanFrequency, 0, 5); aiEcho("Rule: Axemen Training Frequency Increased."); xsDisableSelf(); } //============================================================================== // Set the "launchBigAttack" flag - 22 minutes in // This makes the AI send a big mixed group next time the axemen attack //============================================================================== rule doBigAttack minInterval 1320 active group ModerateAttackRules { launchBigAttack=true; xsDisableSelf(); } //============================================================================== // Rule to check for enemies close to the TC - if so, use Serpents. //============================================================================== rule enemiesCloseToTC minInterval 15 active group ModerateAttackRules { int numEnemies=-1; vector serpentPoint=kbGetBlockPosition("569"); numEnemies=checkForEnemies(); if (numEnemies > 5) { aiEcho("Enemies close to TC, serpentize 'em."); if ( aiCastGodPowerAtPosition(cTechSerpents, serpentPoint) == true ) xsDisableSelf(); else aiEcho("Serpents failed - try again later."); } } //============================================================================== // Difficulty level adjustments. //============================================================================== void difficultyLevelAdjustments(void) { // Difficulty Level check int difflevel=-1; difflevel=aiGetWorldDifficulty(); switch(difflevel) { case 0: // Easy { xsDisableRuleGroup("ModerateAttackRules"); xsDisableRuleGroup("DifficultAttackRules"); xsDisableRuleGroup("ModerateResearchRules"); break; } case 1: // Moderate { xsDisableRuleGroup("DifficultAttackRules"); break; } case 2: // Difficult { attackMinimumGroupSize=6; attackMaximumGroupSize=8; xsEnableRule("attackGenerator3"); break; } case 3: // Nightmare { attackMinimumGroupSize=9; attackMaximumGroupSize=11; xsEnableRule("attackGenerator3"); break; } default: { break; } } // On hard/titan difficulty levels, explore plan with the Hyena o' Set. // This should bug the player a little. if ( difflevel > 1 ) { int exploreID = aiPlanCreate("Explore", cPlanExplore); if(exploreID >= 0) { aiPlanAddUnitType(exploreID, cUnitTypeHyenaofSet, 1, 1); aiPlanSetActive(exploreID); } } } //============================================================================== // MAIN. //============================================================================== void main(void) { // Difficulty Level check int difflevel=-1; difflevel=aiGetWorldDifficulty(); //Startup. miscStartup(); difficultyLevelAdjustments(); //Share the number to maintain. int numberToMaintain=attackMinimumGroupSize*2; //Share a common gather point. vector gatherPoint=kbGetBlockPosition("569"); vector gatherPoint2=kbGetBlockPosition("2487"); //Except for the ships. vector shipGatherPoint=kbGetBlockPosition("1966"); //Create a simple plan to maintain X axemen. 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 too fast if ( difflevel < 2 ) { aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanFrequency, 0, 25); } else if ( difflevel == 2 ) { aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanFrequency, 0, 15); } else { aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanFrequency, 0, 10); } //Turn off training from multiple buildings. //aiPlanSetVariableBool(maintainPlanID, cTrainPlanUseMultipleBuildings, 0, false); //Set a gather target ID. //aiPlanSetVariableInt(maintainPlanID, cTrainPlanGatherTargetID, 0, 1234); //Set a gather point. aiPlanSetVariableVector(maintainPlan1ID, cTrainPlanGatherPoint, 0, gatherPoint); //Activate the plan. aiPlanSetActive(maintainPlan1ID); } 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 too fast if ( difflevel < 3 ) { aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanFrequency, 0, 60); } else { aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanFrequency, 0, 30); } //Turn off training from multiple buildings. //aiPlanSetVariableBool(maintainPlanID, cTrainPlanUseMultipleBuildings, 0, false); //Set a gather target ID. //aiPlanSetVariableInt(maintainPlanID, cTrainPlanGatherTargetID, 0, 1234); //Set a gather point. aiPlanSetVariableVector(maintainPlan2ID, cTrainPlanGatherPoint, 0, gatherPoint2); //Activate the plan. aiPlanSetActive(maintainPlan2ID); } // Maintain up to 2 Kebenits, int maintainPlan3ID=aiPlanCreate("Maintain 2 "+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 - more if nightmare... if ( difflevel < 3 ) { aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanNumberToMaintain, 0, 2); } else { aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanNumberToMaintain, 0, 4); } //Don't train units faster than every 90 seconds aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanFrequency, 0, 90); //Turn off training from multiple buildings. //aiPlanSetVariableBool(maintainPlanID, cTrainPlanUseMultipleBuildings, 0, false); //Set a gather target ID. //aiPlanSetVariableInt(maintainPlanID, cTrainPlanGatherTargetID, 0, 1234); //Set a gather point. aiPlanSetVariableVector(maintainPlan3ID, cTrainPlanGatherPoint, 0, shipGatherPoint); //Activate the plan. aiPlanSetActive(maintainPlan3ID); } }