//============================================================================== // File: aiHunterCore.xs // Author: Matt Craver // // This file contains the main functionality of the Hunter AI. By calling // functions in this file via the AI Func Trigger, you can set up various // locations you want the AI to attack. //============================================================================== extern int gAttackPlan = -1; extern int gAttackPlanArray = -1; extern bool gUseAttackPlanArray = false; extern int gXCoords = -1; extern int gZCoords = -1; extern int gConquerable = -1; extern int gNumberOfAttacks = -1; extern int gCurrentIndex = 0; extern int gPlayerIDToAttack = 3; extern bool gUseEnemyRelationForVillageCount = false; extern int gVillageUnitQuery = -1; extern int gMyUnitQuery = -1; extern bool gCheckForBuildings = true; extern bool gCheckForUnits = true; extern vector gArmyLocation = cInvalidVector; extern int gPlanUnitType = -1; extern int gAttackPlanTargetUnitType = -1; extern bool gIgnoreArmySize = false; extern bool gIgnoreEmptyLocations = true; extern int gMaxVillageDistance = 1000; extern int gFailedAttacks = 0; extern int gMaxArmySize = 200; extern int gAttackCheckInterval = 10; extern int gBuildPlan = -1; extern int gActionlessUpdates = 0; extern int gRandomIndexArray = -1; extern int gCurrentRandomIndex = 0; extern int gAttackPlanGatherTime = 60000; extern int gObjectiveVillageID = -1; extern int gRequiredMilitaryPopForObjective = -1; //============================================================================== // initializeArrays // // Call this function first to initialize the number of points you'll want. //============================================================================== void initializeArrays(int count=-1) { aiEcho("initializeArrays: count " + count); gXCoords = xsArrayCreateInt(count, -1, "X Coords"); gZCoords = xsArrayCreateInt(count, -1, "Z Coords"); gConquerable = xsArrayCreateBool(count, true, "Is Village Conquerable"); gAttackPlanArray = xsArrayCreateInt(count, -1, "Attack Plans"); gRandomIndexArray = xsArrayCreateInt(count, -1, "Random Indices"); // set defaults for the random index array in case the user forgets to set it up. for(i = 0; < count) { xsArraySetInt(gRandomIndexArray, i, i); } gNumberOfAttacks = count; } //============================================================================== // addXCoord // // Always set the X coord before setting the Z. //============================================================================== void addXCoord(int x=-1) { aiEcho("X: " + x); xsArraySetInt(gXCoords, gCurrentIndex, x); } //============================================================================== // addZCoord // // Always call this function after you set the X coord. //============================================================================== void addZCoord(int z=-1) { aiEcho("Z: " + z); xsArraySetInt(gZCoords, gCurrentIndex, z); gCurrentIndex = gCurrentIndex + 1; } //============================================================================== // setPlayerToAttack // // Call this to tell the AI what ID the village player is. //============================================================================== void setPlayerToAttack(int pID = -1) { aiEcho("Attacking player " + pID); gPlayerIDToAttack = pID; } //============================================================================== // setMaxVillageDistance // // Call this to set the max distance of villages to consider. //============================================================================== void setMaxVillageDistance(int dist = -1) { aiEcho("Max Village Distance" + dist); gMaxVillageDistance = dist; } //============================================================================== // setAttackCheckInterval // // Call this to set how often the attack check rule will update. //============================================================================== void setAttackCheckInterval(int val = -1) { aiEcho("Attack Check Interval" + val); gAttackCheckInterval = val; } //============================================================================== // setAttackPlanGatherTime // // Call this to set the gather time the attack plans will use. A low gather time // (1 minute) by default, may cause the AI to lose interest on larger maps. //============================================================================== void setAttackPlanGatherTime(int time = -1) { gAttackPlanGatherTime = time; } //============================================================================== // addRandomIndex // // Since random numbers can't be generated by the AI, we need to pass a // randomized list of attack location indices. //============================================================================== void addRandomIndex(int val = -1) { xsArraySetInt(gRandomIndexArray, gCurrentRandomIndex, val); gCurrentRandomIndex = gCurrentRandomIndex + 1; } //============================================================================== // setObjectiveVillageID // // Set the ID of the village that is our current goal. //============================================================================== void setObjectiveVillageID(int id = -1) { gObjectiveVillageID = id; } //============================================================================== // setRequiredMilitaryPopForObjective // // Set the military population required to take the objective village. //============================================================================== void setRequiredMilitaryPopForObjective(int pop = -1) { gRequiredMilitaryPopForObjective = pop; } //============================================================================== // setUseEnemyRelationForVillageCount // // Set whether or not to check for a specific player for village count or to // check all enemies. //============================================================================== void setUseEnemyRelationForVillageCount(int val = -1) { if(val >= 1) { gUseEnemyRelationForVillageCount = true; } } //============================================================================== // beginVillageAttacks // // Call this from RM after setting all the desired coordinates. //============================================================================== void beginVillageAttacks(int index=-1) { aiEcho("beginVillageAttacks: enabling attack rule"); gCurrentIndex = index; gPlanUnitType = cUnitTypeLogicalTypeLandMilitary; if(gMyUnitQuery == -1) { gMyUnitQuery = kbUnitQueryCreate("My Units Query"); kbUnitQuerySetPlayerID(gMyUnitQuery, cMyID); kbUnitQuerySetUnitType(gMyUnitQuery, cUnitTypeLogicalTypeLandMilitary); kbUnitQuerySetState(gMyUnitQuery, cUnitStateAlive); } gAttackPlanTargetUnitType = cUnitTypeMilitary; xsEnableRule("checkForAttackPlans"); } //============================================================================== // beginWonderAttacks // // Call this from RM after setting all the desired coordinates. //============================================================================== void beginWonderAttacks(int index=-1) { aiEcho("beginWonderAttacks: enabling attack rule"); gCurrentIndex = index; gPlanUnitType = cUnitTypeAbstractPriest; gCheckForBuildings = false; if(gMyUnitQuery == -1) { gMyUnitQuery = kbUnitQueryCreate("My Units Query"); kbUnitQuerySetPlayerID(gMyUnitQuery, cMyID); kbUnitQuerySetUnitType(gMyUnitQuery, cUnitTypeAbstractPriest); kbUnitQuerySetState(gMyUnitQuery, cUnitStateAlive); } gIgnoreArmySize = true; gAttackPlanTargetUnitType = cUnitTypeUnitTypeVillager1; gAttackPlanGatherTime = 600000; xsEnableRule("checkForAttackPlans"); } //============================================================================== // getPosition // // Return a vector from the X and Z coords passed in. //============================================================================== vector getPosition(int index=-1) { vector point = cInvalidVector; point = xsVectorSetX(point, xsArrayGetInt(gXCoords, index)); point = xsVectorSetY(point, 0); point = xsVectorSetZ(point, xsArrayGetInt(gZCoords, index)); return(point); } int getNextRandomIndex() { gCurrentRandomIndex = gCurrentRandomIndex + 1; if(gCurrentRandomIndex >= gNumberOfAttacks) { gCurrentRandomIndex = 0; } return(xsArrayGetInt(gRandomIndexArray, gCurrentRandomIndex)); } //============================================================================== // createAttackPlan // // Create a plan to attack one of the specific coords. //============================================================================== void createAttackPlan(int index=-1) { if(index == -1) return(); vector point = getPosition(index); gAttackPlan = aiPlanCreate("Attack Plan", cPlanAttack); aiEcho("ATTACK PLAN " + gAttackPlan); //Priority. aiPlanSetDesiredPriority(gAttackPlan, 90); //Attack player ID. aiPlanSetVariableInt(gAttackPlan, cAttackPlanPlayerID, 0, gPlayerIDToAttack); aiPlanSetVariableInt(gAttackPlan, cAttackPlanTargetTypeID, 0, gAttackPlanTargetUnitType); aiPlanSetVariableInt(gAttackPlan, cAttackPlanGatherWaitTime, 0, gAttackPlanGatherTime); //Attack. aiPlanSetAttack(gAttackPlan, true); aiPlanSetInitialPosition(gAttackPlan, point); aiPlanSetVariableVector(gAttackPlan, cAttackPlanAttackPoint, 0, point); gArmyLocation = point; //Military. aiPlanSetMilitary(gAttackPlan, true); aiPlanSetEscrowID(gAttackPlan, cMilitaryEscrowID); aiPlanAddUnitType(gAttackPlan, gPlanUnitType , 5, 5, gMaxArmySize); aiPlanSetActive(gAttackPlan); } //============================================================================== // getVillageSize // // Find out how many 'villagers' are in a village. //============================================================================== int getVillageSize(int index=-1) { if(gVillageUnitQuery == -1) { gVillageUnitQuery = kbUnitQueryCreate("Village Units Query"); if(gUseEnemyRelationForVillageCount == true) { kbUnitQuerySetPlayerRelation(gVillageUnitQuery, cPlayerRelationEnemyNotGaia); } else { kbUnitQuerySetPlayerID(gVillageUnitQuery, gPlayerIDToAttack); } kbUnitQuerySetUnitType(gVillageUnitQuery, cUnitTypeUnit); kbUnitQuerySetState(gVillageUnitQuery, cUnitStateAlive); } kbUnitQuerySetPosition(gVillageUnitQuery, getPosition(index)); kbUnitQuerySetMaximumDistance(gVillageUnitQuery, 35.0); int count = 0; if(gCheckForUnits == true) { kbUnitQuerySetUnitType(gVillageUnitQuery, cUnitTypeUnit); kbUnitQueryResetResults(gVillageUnitQuery); count = kbUnitQueryExecute(gVillageUnitQuery); } if(gCheckForBuildings == true) { kbUnitQuerySetUnitType(gVillageUnitQuery, cUnitTypeLogicalTypeBuildingsNotWalls); kbUnitQueryResetResults(gVillageUnitQuery); count = count + kbUnitQueryExecute(gVillageUnitQuery); } if(gAttackPlanTargetUnitType != -1) { kbUnitQuerySetUnitType(gVillageUnitQuery, gAttackPlanTargetUnitType); kbUnitQueryResetResults(gVillageUnitQuery); count = count + kbUnitQueryExecute(gVillageUnitQuery); } return(count); } //============================================================================== // getVillageToAttack // // This function finds out which villages can be conquered and then returns // a random ID. //============================================================================== int getVillageToAttack(bool buildConquerableList=false) { if(buildConquerableList == true) { kbLookAtAllUnitsOnMap(); kbUnitQueryResetResults(gMyUnitQuery); int armySize = kbUnitQueryExecute(gMyUnitQuery); int maxVillageDistance = gMaxVillageDistance; if(gObjectiveVillageID != -1 && armySize >= gRequiredMilitaryPopForObjective) { aiEcho("Objective population reached; increasing attack distance"); return(gObjectiveVillageID); } if(gUseAttackPlanArray == false) { gArmyLocation = kbUnitGetPosition(kbUnitQueryGetResult(gMyUnitQuery, 0)); } else if(gArmyLocation == cInvalidVector) { gArmyLocation = kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID)); } bool fail = true; for(i=0; < gNumberOfAttacks) { int villageUnitCount = getVillageSize(i); vector temp = getPosition(i); temp = xsVectorSetX(temp, xsVectorGetX(temp) - xsVectorGetX(gArmyLocation)); temp = xsVectorSetZ(temp, xsVectorGetZ(temp) - xsVectorGetZ(gArmyLocation)); aiEcho("INDEX: " + i + " UNITS :" + villageUnitCount + " DIST: " + xsVectorLength(temp)); if((gIgnoreEmptyLocations == false || villageUnitCount > 0) && xsVectorLength(temp) < maxVillageDistance + (gFailedAttacks * 50) && (gIgnoreArmySize || armySize >= villageUnitCount + 4) && (gUseAttackPlanArray == false || xsArrayGetInt(gAttackPlanArray, i) == -1)) { xsArraySetBool(gConquerable, i, true); gFailedAttacks = 0; fail = false; } else { xsArraySetBool(gConquerable, i, false); } } if(fail == true) { gFailedAttacks = gFailedAttacks + 1; aiEcho("getVillageToAttack: no valid villages, " + gFailedAttacks + " failed attack(s)"); return(-1); } } int index = getNextRandomIndex(); if(xsArrayGetBool(gConquerable, index) == true) { return(index); } else { return(getVillageToAttack(false)); } return(-1); } //============================================================================== // checkForVillageAttackPlans // // Rule that checks if the AI currently has any attack plans. If they don't, // create a one for a new location. //============================================================================== rule checkForAttackPlans inactive minInterval 1 { int planCount = aiPlanGetNumber(cPlanAttack, cPlanStateGather, true); if(planCount == 0) { if(gCurrentIndex != -1) { createAttackPlan(gCurrentIndex); gCurrentIndex = -1; } else { createAttackPlan(getVillageToAttack(true)); } } xsSetRuleMinInterval("checkForAttackPlans", gAttackCheckInterval); } //============================================================================== // ForcedResign // //============================================================================== void ForcedResign( int val = -1) { aiResign(); return; }