//============================================================================== /* aiUtility.xs Contains utility methods for the AI */ //============================================================================== //============================================================================== // Global unit IDs //============================================================================== extern int gEconUnit = -1; // Our basic economic unit extern int gFarmUnit = -1; // Farms extern int gTownCenter = -1; // The town center extern int gHouse = -1; // Houses extern int gTower = -1; // Towers extern int gDock = -1; // docks extern int gStorehouse = -1; // storehouse //============================================================================== // Opportunities //============================================================================== extern int gMostRecentAllyOpportunityID = -1; // Which opportunity (if any) was created by an ally? (Only one at a time allowed.) extern int gMostRecentTriggerOpportunityID = -1; // Which opportunity (if any) was created by a trigger? (Only one at a time allowed.) //============================================================================== // Unit queries //============================================================================== extern int gRandomUnitQuery=-1; extern int gUnitLocationQuery=-1; extern int gUnitCountLocationQuery=-1; extern int gAllyBaseQuery = -1; extern int gEnemyPointQuery = -1; extern int gAllyPointQuery = -1; extern int gAllyQuery = -1; extern int gEnemyQuery = -1; extern int gAttackQueryID = -1; //============================================================================== //============================================================================== int getMapID() { return(aiGenerateMapID(cRandomMapName)); } //============================================================================== // isCurrentMapNaval() // // See if the current map is a navy map //============================================================================== bool isCurrentMapNaval() { if (aiIsMapType("water") == true) { return(true); } return(false); } //============================================================================== // getAllyCount() // // Returns number of allies EXCLUDING self //============================================================================== int getAllyCount() { int retVal = 0; int playerIdx = 0; for (playerIdx=1; < cNumberPlayers) { if (playerIdx == cMyID) continue; if (kbIsPlayerAlly(playerIdx) == true) retVal = retVal + 1; } return(retVal); } //============================================================================== // getEnemyCount() // // Returns number of enemies excluding gaia //============================================================================== int getEnemyCount() { int retVal = 0; int playerIdx = 0; for (playerIdx=1; < cNumberPlayers) { if (playerIdx == cMyID) continue; if (kbIsPlayerEnemy(playerIdx) == true) retVal = retVal + 1; } return(retVal); } //============================================================================== // autoSave // // Auto-save the game automatically on an interval //============================================================================== rule autoSave inactive minInterval 5 { int interval = 2; // Interval in minutes static int nextTime = 0; // First, do an auto save game if needed //Dont save if we are told not to. if (aiGetAutosaveOn() == true) { int firstCPPlayerID = -1; for(i=0; < cNumberPlayers) { if(kbIsPlayerHuman(i) == true) continue; firstCPPlayerID = i; } if ( (cMyID == firstCPPlayerID) && (xsGetTime() >= nextTime) && (cvDoAutoSaves == true)) { // We're the first CP, it's our job to do the save, and it's time to do it. //Create the savegame name. static int psCount = 0; //Save it. if (cvDoAutoSaves == true) { aiQueueAutoSavegame(psCount); //Inc our count. psCount=psCount+interval; // Count roughly matches game time in minutes while (psCount < (xsGetTime()/60000) ) psCount = psCount+interval; // Handle reloading of save games from machines that had saves off... nextTime = psCount * 60 * 1000; } } } } //============================================================================== // getUnit // // Will return a random unit matching the parameters //============================================================================== int getUnit(int unitTypeID=-1, int playerRelationOrID=cMyID, int state=cUnitStateAlive, int RandomSeed = 0) { int count=-1; //If we don't have the query yet, create one. if (gRandomUnitQuery < 0) { gRandomUnitQuery=kbUnitQueryCreate("miscGetUnitQuery"); kbUnitQuerySetIgnoreKnockedOutUnits(gRandomUnitQuery, true); } //Define a query to get all matching units if (gRandomUnitQuery != -1) { if (playerRelationOrID > 1000) // Too big for player ID number { kbUnitQuerySetPlayerID(gRandomUnitQuery, -1); // Clear the player ID, so playerRelation takes precedence. kbUnitQuerySetPlayerRelation(gRandomUnitQuery, playerRelationOrID); } else { kbUnitQuerySetPlayerRelation(gRandomUnitQuery, -1); kbUnitQuerySetPlayerID(gRandomUnitQuery, playerRelationOrID); } kbUnitQuerySetUnitType(gRandomUnitQuery, unitTypeID); kbUnitQuerySetState(gRandomUnitQuery, state); } else return(-1); kbUnitQueryResetResults(gRandomUnitQuery); int numberFound=kbUnitQueryExecute(gRandomUnitQuery); if (numberFound > 0) { int result = 0; if (RandomSeed > 0) { // force the seed to be a number in the range we are using result = RandomSeed; while (result >= numberFound) { result = result - numberFound; } } int unitID = kbUnitQueryGetResult(gRandomUnitQuery, result); //aiEcho("aiUtil: --- getUnit: Seed[" + RandomSeed + "] Count[" + numberFound + "] Result[" + result + "] UnitID[" + unitID + "]"); return(unitID); } return(-1); } //============================================================================== // getUnitByLocation // // Will return a random unit matching the parameters. If the closest parameter // is set to true, it will return the closest to the location instead of a random. //============================================================================== int getUnitByLocation(int unitTypeID=-1, int playerRelationOrID=cMyID, int state=cUnitStateAlive, vector location = cInvalidVector, float radius = 20.0, bool returnClosest = false, int RandomSeed = 0) { int count=-1; //If we don't have the query yet, create one. if (gUnitLocationQuery < 0) { gUnitLocationQuery=kbUnitQueryCreate("miscGetUnitLocationQuery"); kbUnitQuerySetIgnoreKnockedOutUnits(gUnitLocationQuery, true); } //Define a query to get all matching units if (gUnitLocationQuery != -1) { if (playerRelationOrID > 1000) // Too big for player ID number { kbUnitQuerySetPlayerID(gUnitLocationQuery, -1); kbUnitQuerySetPlayerRelation(gUnitLocationQuery, playerRelationOrID); } else { kbUnitQuerySetPlayerRelation(gUnitLocationQuery, -1); kbUnitQuerySetPlayerID(gUnitLocationQuery, playerRelationOrID); } kbUnitQuerySetUnitType(gUnitLocationQuery, unitTypeID); kbUnitQuerySetState(gUnitLocationQuery, state); kbUnitQuerySetPosition(gUnitLocationQuery, location); kbUnitQuerySetMaximumDistance(gUnitLocationQuery, radius); // Turn on distance sorting if desired kbUnitQuerySetAscendingSort(gUnitLocationQuery, returnClosest); } else return(-1); kbUnitQueryResetResults(gUnitLocationQuery); int numberFound=kbUnitQueryExecute(gUnitLocationQuery); if (numberFound > 0) { int result = 0; if ( (returnClosest == false) && (RandomSeed > 0) ) { // force the seed to be a number in the range we are using result = RandomSeed; while (result >= numberFound) { result = result - numberFound; } } int unitID = kbUnitQueryGetResult(gUnitLocationQuery, result); //aiEcho("aiUtil: --- getUnitByLocation: Seed[" + RandomSeed + "] Count[" + numberFound + "] Result[" + result + "] UnitID[" + unitID + "]"); return(unitID); } return(-1); } //============================================================================== // getUnitCountByLocation // // Returns the number of matching units in the point/radius specified //============================================================================== int getUnitCountByLocation(int unitTypeID=-1, int playerRelationOrID=cMyID, int state=cUnitStateAlive, vector location = cInvalidVector, float radius = 20.0) { int count=-1; //If we don't have the query yet, create one. if (gUnitCountLocationQuery < 0) { gUnitCountLocationQuery=kbUnitQueryCreate("miscGetUnitCountLocationQuery"); kbUnitQuerySetIgnoreKnockedOutUnits(gUnitCountLocationQuery, true); } //Define a query to get all matching units if (gUnitCountLocationQuery != -1) { if (playerRelationOrID > 1000) // Too big for player ID number { kbUnitQuerySetPlayerID(gUnitCountLocationQuery, -1); kbUnitQuerySetPlayerRelation(gUnitCountLocationQuery, playerRelationOrID); } else { kbUnitQuerySetPlayerRelation(gUnitCountLocationQuery, -1); kbUnitQuerySetPlayerID(gUnitCountLocationQuery, playerRelationOrID); } kbUnitQuerySetUnitType(gUnitCountLocationQuery, unitTypeID); kbUnitQuerySetState(gUnitCountLocationQuery, state); kbUnitQuerySetPosition(gUnitCountLocationQuery, location); kbUnitQuerySetMaximumDistance(gUnitCountLocationQuery, radius); } else return(-1); kbUnitQueryResetResults(gUnitCountLocationQuery); //aiEcho("GetUnitCountByLocation"); return(kbUnitQueryExecute(gUnitCountLocationQuery)); } //============================================================================== // getEnemyPlayerByTeamPosition() // // Returns the ID of the Nth player on the enemy team, returns -1 if // there aren't that many players. // // Excludes resigned players. //============================================================================== int getEnemyPlayerByTeamPosition(int position = -1) { int matchCount = 0; int index = -1; // Used for traversal int playerToGet = -1; // i.e. get the 2nd matching playe // Traverse list of players, return when we find the matching player for (index = 1; < cNumberPlayers) { if ( (kbHasPlayerLost(index) == false) && (kbGetPlayerTeam(cMyID) != kbGetPlayerTeam(index)) ) matchCount = matchCount + 1; // Enemy player, add to the count if ( matchCount == position ) return(index); } return(-1); } //============================================================================== // getTeamPosition() // // Returns the player's position in his/her team, i.e. in a 123 vs 456 game, // player 5's team position is 2, player 3 is 3, player 4 is 1. // // Excludes resigned players. //============================================================================== int getTeamPosition(int playerID = -1) { int index = -1; // Used for traversal int playerToGet = -1; // i.e. get the 2nd matching playe // Traverse list of players, increment when we find a teammate, return when we find my number. int retVal = 0; // Zero if I don't exist... for (index = 1; < cNumberPlayers) { if ( (kbHasPlayerLost(index) == false) && (kbGetPlayerTeam(playerID) == kbGetPlayerTeam(index)) ) retVal = retVal + 1; // That's another match if ( index == playerID ) return(retVal); } return(-1); } //============================================================================== // getBaseEnemyStrength() // // Calculate an approximate rating for enemy strength in/near this base. //============================================================================== float getBaseEnemyStrength(int baseID = -1) { float retVal = 0.0; int owner = kbBaseGetOwner(baseID); if (gAllyBaseQuery < 0) { gAllyBaseQuery = kbUnitQueryCreate("Ally Base query"); kbUnitQuerySetIgnoreKnockedOutUnits(gAllyBaseQuery, true); kbUnitQuerySetPlayerRelation(gAllyBaseQuery, cPlayerRelationEnemyNotGaia); kbUnitQuerySetState(gAllyBaseQuery, cUnitStateABQ); kbUnitQuerySetUnitType(gAllyBaseQuery, cUnitTypeLogicalTypeLandMilitary); } if (baseID < 0) return(-1.0); if (owner <= 0) return(-1.0); if (kbIsPlayerEnemy(owner) == true) { /* // Enemy base, add up military factors normally retVal = retVal + (5.0 * kbBaseGetNumberUnits(owner, baseID, cPlayerRelationEnemyNotGaia, cUnitTypeUnitTypeBldgTownCenter)); // 5 points per TC //retVal = retVal + (10.0 * kbBaseGetNumberUnits(owner, baseID, cPlayerRelationEnemy, cUnitTypeFortFrontier)); // 10 points per fort retVal = retVal + kbBaseGetNumberUnits(owner, baseID, cPlayerRelationEnemyNotGaia, cUnitTypeLogicalTypeLandMilitary); // 1 point per soldier retVal = retVal + (3.0 * kbBaseGetNumberUnits(owner, baseID, cPlayerRelationEnemyNotGaia, cUnitTypeUnitTypeBldgTower)); // 3 points per tower*/ retVal = kbBaseGetDefenseRating(owner, baseID); } else { // Ally base, we're considering defending. Count enemy units present kbUnitQuerySetUnitType(gAllyBaseQuery, cUnitTypeLogicalTypeLandMilitary); kbUnitQuerySetPosition(gAllyBaseQuery, kbBaseGetLocation(owner, baseID)); kbUnitQuerySetMaximumDistance(gAllyBaseQuery, 50.0); kbUnitQueryResetResults(gAllyBaseQuery); //aiEcho("getBaseEnemyStrength"); retVal = kbUnitQueryExecute(gAllyBaseQuery); } if (retVal < 1.0) retVal = 1.0; // Return at least 1. return(retVal); } //============================================================================== // getPointEnemyStrength() // // Calculate an approximate strength rating for the enemy units/buildings near this point. //============================================================================== float getPointEnemyStrength(vector loc = cInvalidVector) { float retVal = 0.0; if (gEnemyPointQuery < 0) { gEnemyPointQuery = kbUnitQueryCreate("Enemy Point query"); kbUnitQuerySetIgnoreKnockedOutUnits(gEnemyPointQuery, true); kbUnitQuerySetPlayerRelation(gEnemyPointQuery, cPlayerRelationEnemyNotGaia); kbUnitQuerySetState(gEnemyPointQuery, cUnitStateABQ); kbUnitQuerySetUnitType(gEnemyPointQuery, cUnitTypeLogicalTypeLandMilitary); } kbUnitQuerySetUnitType(gEnemyPointQuery, cUnitTypeLogicalTypeLandMilitary); kbUnitQuerySetPosition(gEnemyPointQuery, loc); kbUnitQuerySetMaximumDistance(gEnemyPointQuery, 50.0); kbUnitQueryResetResults(gEnemyPointQuery); retVal = kbUnitQueryExecute(gEnemyPointQuery); //aiEcho("getPointEnemyStrength"); /*kbUnitQuerySetUnitType(gEnemyPointQuery, cUnitTypeFortFrontier); kbUnitQueryResetResults(gEnemyPointQuery); retVal = retVal + 10.0 * kbUnitQueryExecute(gEnemyPointQuery);*/ // Each fort counts as 10 units kbUnitQuerySetUnitType(gEnemyPointQuery, gTownCenter); kbUnitQueryResetResults(gEnemyPointQuery); retVal = retVal + 5.0 * kbUnitQueryExecute(gEnemyPointQuery); // Each TC counts as 5 units /*kbUnitQuerySetUnitType(gEnemyPointQuery, cUnitTypeOutpost); kbUnitQueryResetResults(gEnemyPointQuery); retVal = retVal + 3.0 * kbUnitQueryExecute(gEnemyPointQuery);*/ // Each tower counts as 3 units if (retVal < 1.0) retVal = 1.0; // Return at least 1. return(retVal); } //============================================================================== // getPointAllyStrength() // // Calculate an approximate strength rating for the allied units/buildings near this point. //============================================================================== float getPointAllyStrength(vector loc = cInvalidVector) { float retVal = 0.0; if (gAllyPointQuery < 0) { gAllyPointQuery = kbUnitQueryCreate("Ally Point query 2"); kbUnitQuerySetIgnoreKnockedOutUnits(gAllyPointQuery, true); kbUnitQuerySetPlayerRelation(gAllyPointQuery, cPlayerRelationAlly); kbUnitQuerySetState(gAllyPointQuery, cUnitStateABQ); kbUnitQuerySetUnitType(gAllyPointQuery, cUnitTypeLogicalTypeLandMilitary); } kbUnitQuerySetUnitType(gAllyPointQuery, cUnitTypeLogicalTypeLandMilitary); kbUnitQuerySetPosition(gAllyPointQuery, loc); kbUnitQuerySetMaximumDistance(gAllyPointQuery, 50.0); kbUnitQueryResetResults(gAllyPointQuery); retVal = kbUnitQueryExecute(gAllyPointQuery); /*kbUnitQuerySetUnitType(gAllyPointQuery, cUnitTypeFortFrontier); kbUnitQueryResetResults(gAllyPointQuery); retVal = retVal + 10.0 * kbUnitQueryExecute(gAllyPointQuery);*/ // Each fort counts as 10 units kbUnitQuerySetUnitType(gAllyPointQuery, gTownCenter); kbUnitQueryResetResults(gAllyPointQuery); retVal = retVal + 5.0 * kbUnitQueryExecute(gAllyPointQuery); // Each TC counts as 5 units /*kbUnitQuerySetUnitType(gAllyPointQuery, cUnitTypeOutpost); kbUnitQueryResetResults(gAllyPointQuery); retVal = retVal + 3.0 * kbUnitQueryExecute(gAllyPointQuery);*/ // Each tower counts as 3 units if (retVal < 1.0) retVal = 1.0; // Return at least 1. return(retVal); } //============================================================================== // getPointValue() // // Calculate an approximate value for the playerRelation units/buildings near this point. // I.e. if playerRelation is enemy, calculate strength of enemy units and buildings. //============================================================================== float getPointValue(vector loc = cInvalidVector, int relation = cPlayerRelationEnemyNotGaia) { float retVal = 0.0; int queryID = -1; // Use either enemy or ally query as needed. if (gAllyQuery < 0) { gAllyQuery = kbUnitQueryCreate("Ally point value query"); kbUnitQuerySetIgnoreKnockedOutUnits(gAllyQuery, true); kbUnitQuerySetPlayerRelation(gAllyQuery, cPlayerRelationAlly); kbUnitQuerySetState(gAllyQuery, cUnitStateABQ); } if (gEnemyQuery < 0) { gEnemyQuery = kbUnitQueryCreate("Enemy point value query"); kbUnitQuerySetIgnoreKnockedOutUnits(gEnemyQuery, true); kbUnitQuerySetPlayerRelation(gEnemyQuery, cPlayerRelationEnemyNotGaia); kbUnitQuerySetSeeableOnly(gEnemyQuery, true); kbUnitQuerySetState(gEnemyQuery, cUnitStateAlive); } if ( (relation == cPlayerRelationEnemy) || (relation == cPlayerRelationEnemyNotGaia) ) queryID = gEnemyQuery; else queryID = gAllyQuery; kbUnitQueryResetResults(queryID); kbUnitQuerySetUnitType(queryID, cUnitTypeLogicalTypeBuildingsNotWalls); kbUnitQueryResetResults(queryID); retVal = 200.0 * kbUnitQueryExecute(queryID); // 200 points per building kbUnitQuerySetUnitType(queryID, gTownCenter); kbUnitQueryResetResults(queryID); retVal = retVal + 1000.0 * kbUnitQueryExecute(queryID); // Extra 1000 per TC kbUnitQuerySetUnitType(queryID, cUnitTypeUnit); kbUnitQueryResetResults(queryID); retVal = retVal + 200.0 * kbUnitQueryExecute(queryID); // 200 per unit. if (retVal < 1.0) retVal = 1.0; return(retVal); } //============================================================================== // getBaseValue() // // Calculate an approximate value for this base. //============================================================================== float getBaseValue(int baseID = -1) { float retVal = 0.0; int owner = kbBaseGetOwner(baseID); int relation = -1; if (baseID < 0) return(-1.0); if (owner <= 0) return(-1.0); if (kbIsPlayerAlly(owner) == true) relation = cPlayerRelationAlly; else relation = cPlayerRelationEnemyNotGaia; retVal = retVal + (200.0 * kbBaseGetNumberUnits(owner, baseID, relation, cUnitTypeLogicalTypeBuildingsNotWalls)); retVal = retVal + (1000.0 * kbBaseGetNumberUnits(owner, baseID, relation, gTownCenter)); // 1000 points extra per TC //retVal = retVal + (600.0 * kbBaseGetNumberUnits(owner, baseID, relation, cUnitTypePlantation)); // 600 points per plantation //retVal = retVal + (2000.0 * kbBaseGetNumberUnits(owner, baseID, relation, cUnitTypeFortFrontier)); // 2000 points per fort retVal = retVal + (150.0 * kbBaseGetNumberUnits(owner, baseID, relation, cUnitTypeLogicalTypeLandMilitary)); // 150 points per soldier retVal = retVal + (200.0 * kbBaseGetNumberUnits(owner, baseID, relation, gEconUnit)); // 200 points per settler //retVal = retVal + (200.0 * kbBaseGetNumberUnits(owner, baseID, relation, cUnitTypeTradingPost)); // 1000 points per trading post if (retVal < 1.0) retVal = 1.0; // Return at least 1. return(retVal); } //============================================================================== // distance() // // Return a float with the 3D distance between two vectors //============================================================================== float distance(vector v1=cInvalidVector, vector v2=cInvalidVector) { vector delta = v1 - v2; return (xsVectorLength(delta)); } //============================================================================== // createOpportunity(type, targetType, targetID, targetPlayerID, source) // // A wrapper function for aiCreateOpportunity(), to permit centralized tracking // of the most recently created ally-generated and trigger-generated // opportunities. This info is needed so that a cancel command can // efficiently deactivate the previous (and possibly current) opportunity before // creating the new one. //============================================================================== int createOpportunity(int type = -1, int targetType = -1, int targetID = -1, int targetPlayerID = -1, int source = -1) { int oppID = aiCreateOpportunity(type, targetType, targetID, targetPlayerID, source); if (source == cOpportunitySourceAllyRequest) gMostRecentAllyOpportunityID = oppID; // Remember which ally opp we're doing else if (source == cOpportunitySourceTrigger) gMostRecentTriggerOpportunityID = oppID; return(oppID); } //============================================================================== // chooseAttackPlayerID() // // Given a point/radius, look for enemy units, and choose the owner of one // as an appropriate player to attack. // // If none found, return mostHatedEnemy. //============================================================================== int chooseAttackPlayerID(vector point=cInvalidVector, float radius = 50.0) { int retVal = aiGetMostHatedPlayerID(); if (point == cInvalidVector) return(retVal); if (gAttackQueryID < 0) { gAttackQueryID = kbUnitQueryCreate("Choose attack player"); kbUnitQuerySetPlayerRelation(gAttackQueryID, cPlayerRelationEnemyNotGaia); // Any enemy units in point/radius kbUnitQuerySetIgnoreKnockedOutUnits(gAttackQueryID, true); kbUnitQuerySetUnitType(gAttackQueryID, cUnitTypeUnit); kbUnitQuerySetState(gAttackQueryID, cUnitStateAlive); } kbUnitQuerySetPosition(gAttackQueryID, point); kbUnitQuerySetMaximumDistance(gAttackQueryID, radius); kbUnitQueryResetResults(gAttackQueryID); int count = kbUnitQueryExecute(gAttackQueryID); int index = 0; int unitID = 0; for (index = 0; < count) { unitID = kbUnitQueryGetResult(gAttackQueryID, index); if (kbUnitGetPlayerID(unitID) > 0) // Not Gaia { retVal = kbUnitGetPlayerID(unitID); // Owner of first (random) non-gaia unit break; } } return(retVal); } //============================================================================== // calculateEnemyDirection // // BF 2/18/2012: Updated from Skirmish: // Figure out where the AI enemy is and get the direction. //============================================================================== void calculateEnemyDirection() { if(xsVectorIsValid(cEnemyDirection) == false) { vector center = kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID)); // Start with base location vector enemy = kbBaseGetLocation(aiGetMostHatedPlayerID(), kbBaseGetMainID(aiGetMostHatedPlayerID())); // this is likely an ally of the human who doesn't know where the enemy is, so // just have them aim for the middle of the map. if(xsVectorIsValid(enemy) == false) { enemy = kbGetMapCenter(); } if(bDebugMessages == true){ aiEcho("ENEMY BASE" + enemy); } vector diff = cInvalidVector; diff = xsVectorSetX(diff, xsVectorGetX(enemy) - xsVectorGetX(center)); diff = xsVectorSetZ(diff, xsVectorGetZ(enemy) - xsVectorGetZ(center)); cEnemyDirection = xsVectorNormalize(diff); } } //============================================================================== // getNumberOfAIsOnHumanTeam // // BF 2/18/2012: Updated from Skirmish: // count humans //============================================================================== int getNumberOfAIsOnHumanTeam() { int count = 0; for (i = 0; < cNumberPlayers) { if(kbGetPlayerTeam(i) == 1 && kbIsPlayerHuman(i) == false) { count = count + 1; } } if(bDebugMessages == true){ aiEcho("THERE ARE " + count + " AI ON THE HUMAN TEAM"); } return (count); }