//============================================================================== // File: aiIslandCaptureCore.xs // Author: Matt Craver // // This file extends the hunter AI to allow the takeover of islands. This // includes building on the island, gathering, military patrol and defending. //============================================================================== include "aiHunterCore.xs"; extern const int cEconBuildOrder = 0; extern const int cMilitaryBuildOrder = 1; extern const int cCapturedVillageConstant = -555; extern int gBuildOrder = -1; extern int gNumberOfBuildings = -1; extern int gMaxNumberOfBuildings = 20; extern int gBuildingIndex = -1; extern int gBuildPlanIDs = -1; extern int gBuildingSmallIndex = -1; extern int gCenterBuilding = -1; extern vector gBuildLocation = cInvalidVector; extern bool gCurrentlyBuilding = false; extern int gSmallIslandStartIndex = -1; extern int gStorehouseQuery = -1; extern int gNumberOfBuildOrderTypes = -1; extern bool gCurrentIslandIsSmall = false; extern int gCurrentIslandType = -1; extern int gPatrolPlanArray = -1; extern int gPatrolLocationIndexArray = -1; extern int gDefensePlanArray = -1; extern int gDefenseQuery = -1; extern int gPlanMilitaryUnitType = -1; extern int gSmallIslandVillagers = 3; extern int gSmallIslandMilitary = 5; extern int gLargeIslandVillagers = 7; extern int gLargeIslandMilitary = 5; extern int gOpponentPlayerID = -1; extern int gValidAttackQuery = -1; extern bool gSendDefendAlways = false; extern bool gDefendUncapturedLocations = false; extern int gIslandPatrolPlan = -1; extern int gIslandPatrolIndex = -1; extern int gIslandPatrolMaxUnits = 3; extern float gMinDefenseSize = 0.5; extern float gMaxDefenseSize = 1.5; extern float gDefenseSizeRate = 10; extern int gNumDefenseAttacks = 0; //============================================================================== // setSmallIslandStartIndex // // Call this to tell the AI when the location IDs will start referencing // small islands. //============================================================================== void setSmallIslandStartIndex(int val = -1) { aiEcho("Small Island Starting Index" + val); gSmallIslandStartIndex = val; } //============================================================================== // setOpponentPlayerID // // Call this to tell the AI what player it is playing against. //============================================================================== void setOpponentPlayerID(int val = -1) { aiEcho("Opponent ID" + val); gOpponentPlayerID = val; } //============================================================================== // setMinDefenseSize // // Call this to set the starting percentage the AI will use to determine how // many units to send to defend an island. Since we can't pass decimal values // from RM, this number is divided by 100. //============================================================================== void setMinDefenseSize(int val = -1) { gMinDefenseSize = val / 100.0; } //============================================================================== // setMaxDefenseSize // // Call this to set the maximum percentage the AI will use to determine how // many units to send to defend an island. Since we can't pass decimal values // from RM, this number is divided by 100. //============================================================================== void setMaxDefenseSize(int val = -1) { gMaxDefenseSize = val / 100.0; } //============================================================================== // setDefenseSizeRate // // Call this to set the number of attacks it will take for the AI to go from its // minimum defense size to its maximum. //============================================================================== void setDefenseSizeRate(int val = -1) { gDefenseSizeRate = val; } //============================================================================== // beginIslandAttacks // // Call this from RM after setting all the desired coordinates. //============================================================================== void beginIslandAttacks(int index=-1) { aiEcho("beginIslandAttacks: enabling attack rule"); gCurrentIndex = index; gPlanUnitType = cUnitTypeUnitTypeVillager1; if(gMyUnitQuery == -1) { gMyUnitQuery = kbUnitQueryCreate("My Units Query"); kbUnitQuerySetPlayerID(gMyUnitQuery, cMyID); kbUnitQuerySetUnitType(gMyUnitQuery, cUnitTypeUnitTypeVillager1); kbUnitQuerySetState(gMyUnitQuery, cUnitStateAlive); } gPatrolPlanArray = xsArrayCreateInt(gNumberOfAttacks, -1, "Patrol Plans"); gPatrolLocationIndexArray = xsArrayCreateInt(gNumberOfAttacks, -1, "Patrol Location Index"); gDefensePlanArray = xsArrayCreateInt(gNumberOfAttacks, -1, "Defense Plans"); gIgnoreArmySize = true; gUseAttackPlanArray = true; gIgnoreEmptyLocations = true; xsEnableRule("parallelAttacksRule"); } //============================================================================== // createIslandAttackPlan // // Create a plan to attack one of the specific coords. //============================================================================== bool createIslandAttackPlan(int index=-1) { if(index == -1) return(false); if(gValidAttackQuery == -1) { gValidAttackQuery = kbUnitQueryCreate("Valid Attack Query"); kbUnitQuerySetPlayerID(gValidAttackQuery, gPlayerIDToAttack); kbUnitQuerySetUnitType(gValidAttackQuery, gAttackPlanTargetUnitType); kbUnitQuerySetState(gValidAttackQuery, cUnitStateAlive); } vector point = getPosition(index); kbUnitQuerySetPosition(gValidAttackQuery, point); kbUnitQuerySetMaximumDistance(gValidAttackQuery, 30); kbUnitQueryResetResults(gValidAttackQuery); if(kbUnitQueryExecute(gValidAttackQuery) == 0) { aiEcho("no valid target, attack failed for position[" + point + "]"); return(false); } gAttackPlan = aiPlanCreate("Attack Plan", cPlanAttack); aiEcho("ATTACK PLAN " + gAttackPlan + " index " + index); //Priority. aiPlanSetDesiredPriority(gAttackPlan, 100); //Attack player ID. aiPlanSetVariableInt(gAttackPlan, cAttackPlanPlayerID, 0, gPlayerIDToAttack); //Attack. aiPlanSetAttack(gAttackPlan, true); if(gArmyLocation == cInvalidVector || gUseAttackPlanArray == true) { aiPlanSetInitialPosition(gAttackPlan, kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID))); } else { aiPlanSetInitialPosition(gAttackPlan, gArmyLocation); } aiPlanSetVariableVector(gAttackPlan, cAttackPlanAttackPoint, 0, point); aiPlanSetVariableInt(gAttackPlan, cAttackPlanTargetAreaGroups, 0, kbAreaGroupGetIDByPosition(point)); //aiPlanSetVariableInt(gAttackPlan, cAttackPlanBaseAttackMode, 0, 1); aiPlanSetVariableInt(gAttackPlan, cAttackPlanTargetTypeID, 0, gAttackPlanTargetUnitType); aiPlanSetBaseID(gAttackPlan, kbBaseGetMainID(cMyID)); //Military. aiPlanSetMilitary(gAttackPlan, true); aiPlanSetEscrowID(gAttackPlan, cMilitaryEscrowID); aiPlanSetRequiresAllNeedUnits(gAttackPlan, true); // set the units based on the size of the island if(index >= gSmallIslandStartIndex) { aiPlanAddUnitType(gAttackPlan, gPlanUnitType , gSmallIslandVillagers, gSmallIslandVillagers, gSmallIslandVillagers); aiPlanAddUnitType(gAttackPlan, gPlanMilitaryUnitType, gSmallIslandMilitary, gSmallIslandMilitary, gSmallIslandMilitary); } else { aiPlanAddUnitType(gAttackPlan, gPlanUnitType , gLargeIslandVillagers, gLargeIslandVillagers, gLargeIslandVillagers); aiPlanAddUnitType(gAttackPlan, gPlanMilitaryUnitType, gLargeIslandMilitary, gLargeIslandMilitary, gLargeIslandMilitary); } aiPlanSetActive(gAttackPlan); xsArraySetInt(gAttackPlanArray, index, gAttackPlan); return(true); } //============================================================================== // setUpBuildOrder // // This function needs to be overridden so that the AI will build things on the // island once they have taken it over. //============================================================================== mutable void setUpBuildOrder(int type=-1, bool smallIsland=false) { } //============================================================================== // buildIslandBuilding // // Determines how to place buildings on the island. Can be overridden, but this // one works pretty well. //============================================================================== mutable int buildIslandBuilding(int puid=-1, vector location=cInvalidVector) { gBuildPlan = aiPlanCreate("Island Build Plan" + puid, cPlanBuild); aiEcho("building id " + puid); aiPlanSetVariableInt(gBuildPlan, cBuildPlanBuildingTypeID, 0, puid); if(puid == gCenterBuilding) { aiPlanSetVariableFloat(gBuildPlan, cBuildPlanBuildingBufferSpace, 0, 2.0); aiPlanSetVariableVector(gBuildPlan, cBuildPlanCenterPosition, 0, location); aiPlanSetVariableFloat(gBuildPlan, cBuildPlanCenterPositionDistance, 0, 10.0); } else if(puid == kbFindBuilding(cUnitTypeUnitTypeBldgFarm)) { if(gStorehouseQuery == -1) { gStorehouseQuery = kbUnitQueryCreate("Storehouse Query"); kbUnitQuerySetPlayerID(gStorehouseQuery, cMyID); kbUnitQuerySetUnitType(gStorehouseQuery, cUnitTypeUnitTypeBldgStorehouse); kbUnitQuerySetState(gStorehouseQuery, cUnitStateABQ); } kbUnitQuerySetPosition(gStorehouseQuery, location); kbUnitQuerySetMaximumDistance(gStorehouseQuery, 30); kbUnitQueryResetResults(gStorehouseQuery); kbUnitQueryExecute(gStorehouseQuery); aiPlanSetVariableFloat(gBuildPlan, cBuildPlanBuildingBufferSpace, 0, 1.0); aiPlanSetVariableVector(gBuildPlan, cBuildPlanCenterPosition, 0, kbUnitGetPosition(kbUnitQueryGetResult(gStorehouseQuery, 0))); aiPlanSetVariableFloat(gBuildPlan, cBuildPlanCenterPositionDistance, 0, 10.0); aiPlanSetVariableInt(gBuildPlan, cBuildPlanInfluenceUnitTypeID, 0, kbFindBuilding(cUnitTypeUnitTypeBldgStorehouse)); aiPlanSetVariableFloat(gBuildPlan, cBuildPlanInfluenceUnitDistance, 0, 0.0); aiPlanSetVariableFloat(gBuildPlan, cBuildPlanInfluenceUnitValue, 0, 100.0); aiPlanSetVariableInt(gBuildPlan, cBuildPlanInfluenceUnitFalloff, 0, cBPIFalloffLinearInverse); aiPlanSetVariableInt(gBuildPlan, cBuildPlanInfluenceUnitCap, 0, 3); } else if(puid == kbFindBuilding(cUnitTypeUnitTypeBldgTower) || puid == kbFindBuilding(cUnitTypeUnitTypeBldgWatchPost)) { aiPlanSetVariableFloat(gBuildPlan, cBuildPlanBuildingBufferSpace, 0, 10.0); aiPlanSetVariableVector(gBuildPlan, cBuildPlanCenterPosition, 0, location); aiPlanSetVariableFloat(gBuildPlan, cBuildPlanCenterPositionDistance, 0, 100.0); aiPlanSetVariableInt(gBuildPlan, cBuildPlanInfluenceUnitTypeID, 0, puid); aiPlanSetVariableFloat(gBuildPlan, cBuildPlanInfluenceUnitDistance, 0, 80.0); aiPlanSetVariableFloat(gBuildPlan, cBuildPlanInfluenceUnitValue, 0, 120.0); aiPlanSetVariableInt(gBuildPlan, cBuildPlanInfluenceUnitFalloff, 0, cBPIFalloffLinearInverse); aiPlanSetVariableInt(gBuildPlan, cBuildPlanInfluenceUnitCap, 0, -1); } else { aiPlanSetVariableFloat(gBuildPlan, cBuildPlanBuildingBufferSpace, 0, 10.0); aiPlanSetVariableVector(gBuildPlan, cBuildPlanCenterPosition, 0, location); aiPlanSetVariableFloat(gBuildPlan, cBuildPlanCenterPositionDistance, 0, 25.0); } aiPlanSetInitialPosition(gBuildPlan, location); aiPlanSetMilitary(gBuildPlan, true); aiPlanAddUnitType(gBuildPlan, cUnitTypeUnitTypeVillager1, 1,1,1); aiPlanSetActive(gBuildPlan); return(gBuildPlan); } //============================================================================== // createIslandBuildPlan // // Get the current build order and enable the rule to create the build plans. //============================================================================== void createIslandBuildPlan(vector loc=cInvalidVector, bool smallIsland=false) { gBuildLocation = loc; // repeat the build orders in order, instead of randomly picking, since we can't have random gCurrentIslandType = gCurrentIslandType + 1; if(gCurrentIslandType >= gNumberOfBuildOrderTypes) { gCurrentIslandType = 0; } gCurrentIslandIsSmall = smallIsland; setUpBuildOrder(gCurrentIslandType, gCurrentIslandIsSmall); gBuildingIndex = 0; xsEnableRule("createBuildPlanRule"); gCurrentlyBuilding = true; } //============================================================================== // getClosestBase // // Get the ID of the base closest to the given location. //============================================================================== int getClosestBase(vector loc=cInvalidVector) { float closestDist = 5555; int closestBase = -1; for(i = 0; < kbBaseGetNumber(cMyID)) { int id = kbBaseGetIDByIndex(cMyID, i); vector basePos = kbBaseGetLocation(cMyID, id); vector dist = xsVectorSet(xsVectorGetX(loc) - xsVectorGetX(basePos), 0, xsVectorGetZ(loc) - xsVectorGetZ(basePos)); float len = xsVectorLength(dist); if(closestDist > len) { closestBase = id; closestDist = len; } } return(closestBase); } //============================================================================== // createBuildPlanRule // // Once createIslandBuildPlan enables this rule, it will create a build plan for // each building in the build order. It only creates one plan each time it // updates. Once it has created all the build plans, it will create some gather // plans, so the villagers don't just stand around. //============================================================================== rule createBuildPlanRule inactive minInterval 5 { if(gBuildingIndex >= gNumberOfBuildings) { xsDisableSelf(); gCurrentlyBuilding = false; int baseID = getClosestBase(gBuildLocation); if(gCurrentIslandType == cEconBuildOrder) { if(gCurrentIslandIsSmall == true) { aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeFarm, 1, 55, 1.0, baseID); } else { aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeFarm, 3, 55, 1.0, baseID); } } aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeHunt, 1, 55, 1.0, baseID); aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeEasy, 1, 55, 1.0, baseID); aiSetResourceBreakdown(cResourceGold, cAIResourceSubTypeEasy, 1, 55, 1.0, baseID); aiSetResourceBreakdown(cResourceWood, cAIResourceSubTypeEasy, 1, 55, 1.0, baseID); aiSetResourceBreakdown(cResourceStone, cAIResourceSubTypeEasy, 1, 55, 1.0, baseID); } else { int id = xsArrayGetInt(gBuildOrder, gBuildingIndex); gBuildPlan = buildIslandBuilding(id, gBuildLocation); xsArraySetInt(gBuildPlanIDs, gBuildingIndex, gBuildPlan); gBuildingIndex = gBuildingIndex + 1; } } //============================================================================== // allBuildPlansInactive // // Check if all of the build plans have been created and are finished. //============================================================================== bool allBuildPlansInactive() { bool ret = true; for(i = 0; < gNumberOfBuildings) { int planID = xsArrayGetInt(gBuildPlanIDs, i); if(planID != -1 && aiPlanGetActive(planID) == true) { ret = false; } } return(ret); } //============================================================================== // resetBuildPlanIDs // // Destroy the build plans and reinitialize the build plan array to -1 //============================================================================== void resetBuildPlanIDs() { for(i = 0; < gNumberOfBuildings) { int planID = xsArrayGetInt(gBuildPlanIDs, i); aiPlanDestroy(planID); xsArraySetInt(gBuildPlanIDs, i, -1); } } //============================================================================== // createPatrolPlan // // Create a defend plan that will grab some military units and patrol the area. //============================================================================== void createPatrolPlan(int index=-1) { int plan = aiPlanCreate("Island Patrol " + index, cPlanDefend); aiPlanAddUnitType(plan, cUnitTypeLogicalTypeLandMilitary , 3, 3, 10); // All mil units, high MAX value to suck up all excess //aiPlanSetVariableInt(gStartingArmyPlan, cDefendPlanDefendBaseID, 0, kbBaseGetMainID(cMyID)); aiPlanSetVariableVector(plan, cDefendPlanDefendPoint, 0, getPosition(index)); aiPlanSetVariableFloat(plan, cDefendPlanEngageRange, 0, 50.0); // Loose aiPlanSetRequiresAllNeedUnits(plan, false); aiPlanSetVariableFloat(plan, cDefendPlanGatherDistance, 0, 20.0); aiPlanSetInitialPosition(plan, getPosition(index)); aiPlanSetUnitStance(plan, cUnitStanceAggressive); aiPlanSetVariableInt(plan, cDefendPlanRefreshFrequency, 0, 5); aiPlanSetVariableInt(plan, cDefendPlanAttackTypeID, 0, cUnitTypeMilitary); // Only units aiPlanSetDesiredPriority(plan, 70); // Very very low priority, gather unused units. aiPlanSetActive(plan); xsArraySetInt(gPatrolPlanArray, index, plan); xsArraySetInt(gPatrolLocationIndexArray, index, 0); } //============================================================================== // updatePatrolLocations // // Update the defend point of each active patrol plan based on the current // value in the corresponding gPatrolLocationIndex. //============================================================================== rule updatePatrolLocations active minInterval 15 { for(i = 0; < gNumberOfAttacks) { int plan = xsArrayGetInt(gPatrolPlanArray, i); if(plan != -1) { int waypointIndex = xsArrayGetInt(gPatrolLocationIndexArray, i); vector center = getPosition(i); vector testVec = center; switch(waypointIndex) { case 0: { testVec = xsVectorSetX(testVec, xsVectorGetX(center) + 25); testVec = xsVectorSetZ(testVec, xsVectorGetZ(center) - 25); aiPlanSetVariableVector(plan, cDefendPlanDefendPoint, 0, testVec); break; } case 1: { testVec = xsVectorSetX(testVec, xsVectorGetX(center) + 25); testVec = xsVectorSetZ(testVec, xsVectorGetZ(center) + 25); aiPlanSetVariableVector(plan, cDefendPlanDefendPoint, 0, testVec); break; } case 2: { testVec = xsVectorSetX(testVec, xsVectorGetX(center) - 25); testVec = xsVectorSetZ(testVec, xsVectorGetZ(center) + 25); aiPlanSetVariableVector(plan, cDefendPlanDefendPoint, 0, testVec); break; } case 3: { testVec = xsVectorSetX(testVec, xsVectorGetX(center) - 25); testVec = xsVectorSetZ(testVec, xsVectorGetZ(center) - 25); aiPlanSetVariableVector(plan, cDefendPlanDefendPoint, 0, testVec); break; } } waypointIndex = waypointIndex + 1; if(waypointIndex > 3) waypointIndex = 0; xsArraySetInt(gPatrolLocationIndexArray, i, waypointIndex); } } } //============================================================================== // checkForVillageAttackPlans // // This is the linear version of the Island Capture routine. The AI will go // from island to island, destroying whatever is there and then building its // build order before moving on. //============================================================================== rule checkForIslandAttackPlans inactive minInterval 1 { // attempt to handle a fail case. if(gActionlessUpdates > 15) { aiEcho("checkForIslandAttackPlans: too many updates without doing something"); gAttackPlan = -1; resetBuildPlanIDs(); gArmyLocation = cInvalidVector; } if(gAttackPlan == -1) { if(gCurrentIndex != -1) { createIslandAttackPlan(gCurrentIndex); gCurrentIndex = -1; } else { createIslandAttackPlan(getVillageToAttack(true)); } gActionlessUpdates = 0; gBuildPlan = -1; } else if(aiPlanGetActive(gAttackPlan) == false) { if(gBuildPlan == -1) { createIslandBuildPlan(gArmyLocation); gActionlessUpdates = 0; } else if(allBuildPlansInactive() && gBuildingIndex >= gNumberOfBuildings) { gAttackPlan = -1; resetBuildPlanIDs(); gActionlessUpdates = 0; } else { gActionlessUpdates = gActionlessUpdates + 1; } } xsSetRuleMinInterval("checkForIslandAttackPlans", gAttackCheckInterval); } //============================================================================== // allBuildPlansInactive // // This version of Island Capture will start each attack from the main base and // it doesn't need to wait until a previous island's attack plan is done. //============================================================================== rule parallelAttacksRule inactive minInterval 5 { if(gActionlessUpdates > gAttackCheckInterval / 5) { bool success = createIslandAttackPlan(getVillageToAttack(true)); if(success == true) { gActionlessUpdates = 0; } } else { gActionlessUpdates = gActionlessUpdates + 1; } for(i = 0; < gNumberOfAttacks) { int plan = xsArrayGetInt(gAttackPlanArray, i); if(gCurrentlyBuilding == false && plan != -1 && plan != cCapturedVillageConstant && aiPlanGetActive(plan) == false) { aiEcho("building on island " + i + " loc " + getPosition(i) + " plan " + plan); createIslandBuildPlan(getPosition(i), i >= gSmallIslandStartIndex); xsArraySetInt(gAttackPlanArray, i, cCapturedVillageConstant); createPatrolPlan(i); } } } //============================================================================== // defenseRule // // Check if any islands are under attack and send some units to help defend. // Currently, I am unable to get units back to the main island. //============================================================================== rule defenseRule active minInterval 10 { // make the query if it doesn't already exist if(gDefenseQuery == -1) { gDefenseQuery = kbUnitQueryCreate("Enemy Query"); kbUnitQuerySetPlayerRelation(gDefenseQuery, cPlayerRelationEnemyNotGaia); kbUnitQuerySetUnitType(gDefenseQuery, cUnitTypeMilitary); kbUnitQuerySetState(gDefenseQuery, cUnitStateAlive); } kbLookAtAllUnitsOnMap(); for(i = 0; < gNumberOfAttacks) { int plan = xsArrayGetInt(gDefensePlanArray, i); vector point = getPosition(i); // only send defense to if(plan == -1 && (gDefendUncapturedLocations || xsArrayGetInt(gAttackPlanArray, i) == cCapturedVillageConstant)) { kbUnitQuerySetPosition(gDefenseQuery, point); kbUnitQuerySetMaximumDistance(gDefenseQuery, 50); kbUnitQuerySetAreaGroupID(gDefenseQuery, kbAreaGroupGetIDByPosition(point)); kbUnitQueryResetResults(gDefenseQuery); kbUnitQueryExecute(gDefenseQuery); int count = kbUnitQueryNumberResults(gDefenseQuery); int enemies = 0; for(j = 0; < count) { if(kbUnitGetPlayerID(kbUnitQueryGetResult(gDefenseQuery, j)) != gPlayerIDToAttack) { enemies = enemies + 1; point = kbUnitGetPosition(kbUnitQueryGetResult(gDefenseQuery, j)); } } int playerToAttack = -1; if(gSendDefendAlways == true && getVillageSize(i) > 0) { playerToAttack = gPlayerIDToAttack; } else if(enemies >= 1) { playerToAttack = kbUnitGetPlayerID(kbUnitQueryGetResult(gDefenseQuery, 0)); } if(playerToAttack != -1) { int defensePlan = aiPlanCreate("Island Defend Plan", cPlanAttack); //Priority. aiPlanSetDesiredPriority(defensePlan, 100); //Attack player ID. aiPlanSetVariableInt(defensePlan, cAttackPlanPlayerID, 0, playerToAttack); //Attack. aiPlanSetAttack(defensePlan, true); aiPlanSetInitialPosition(defensePlan, kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID))); aiPlanSetVariableVector(defensePlan, cAttackPlanAttackPoint, 0, point); aiPlanSetVariableInt(defensePlan, cAttackPlanTargetAreaGroups, 0, kbAreaGroupGetIDByPosition(point)); aiPlanSetVariableInt(defensePlan, cAttackPlanTargetTypeID, 0, cUnitTypeMilitary); aiPlanSetBaseID(defensePlan, kbBaseGetMainID(cMyID)); aiPlanSetRequiresAllNeedUnits(defensePlan, true); aiPlanSetVariableFloat(defensePlan, cAttackPlanAttackPointEngageRange, 0, 50); //Military. aiPlanSetMilitary(defensePlan, true); aiPlanSetEscrowID(defensePlan, cMilitaryEscrowID); float denom = gNumDefenseAttacks / gDefenseSizeRate; float percent = gMinDefenseSize + ((gMaxDefenseSize - gMinDefenseSize) * denom); if(gNumDefenseAttacks > gDefenseSizeRate) { percent = gMaxDefenseSize; } int max = enemies * percent; if(max < 5) { max = 5; } gNumDefenseAttacks = gNumDefenseAttacks + 1; aiPlanAddUnitType(defensePlan, cUnitTypeLogicalTypeLandPickerChoice, max, max, max); aiPlanSetActive(defensePlan); xsArraySetInt(gDefensePlanArray, i, defensePlan); aiEcho("Defending at " + point + " with " + max + " units."); } } else if(aiPlanGetActive(plan) == false) { /*int returnPlan = aiPlanCreate("Island Return Plan", cPlanAttack); //Priority. aiPlanSetDesiredPriority(returnPlan, 100); //Attack player ID. aiPlanSetVariableInt(returnPlan, cAttackPlanPlayerID, 0, gPlayerIDToAttack); //Attack. aiPlanSetAttack(returnPlan, true); aiPlanSetInitialPosition(returnPlan, point); aiPlanSetVariableVector(returnPlan, cAttackPlanAttackPoint, 0, kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID))); aiPlanSetVariableInt(returnPlan, cAttackPlanTargetAreaGroups, 0, kbAreaGroupGetIDByPosition(kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID)))); aiPlanSetVariableInt(returnPlan, cAttackPlanTargetTypeID, 0, cUnitTypeLogicalTypeLandPickerTarget); aiPlanSetBaseID(returnPlan, kbBaseGetMainID(cMyID)); //Military. aiPlanSetMilitary(returnPlan, true); aiPlanSetEscrowID(returnPlan, cMilitaryEscrowID); aiPlanAddUnitType(returnPlan, cUnitTypeLogicalTypeLandPickerChoice, 5, 5, 20); aiPlanSetActive(returnPlan);*/ xsArraySetInt(gDefensePlanArray, i, -1); } } } rule navalPatrol inactive minInterval 2 { if(gIslandPatrolPlan == -1) { gIslandPatrolIndex = 0; gIslandPatrolPlan = aiPlanCreate("Naval Patrol", cPlanDefend); aiPlanAddUnitType(gIslandPatrolPlan, cUnitTypeAbstractWarShip, gIslandPatrolMaxUnits, gIslandPatrolMaxUnits, gIslandPatrolMaxUnits); // All mil units, high MAX value to suck up all excess //aiPlanSetVariableInt(gStartingArmyPlan, cDefendPlanDefendBaseID, 0, kbBaseGetMainID(cMyID)); aiPlanSetVariableVector(gIslandPatrolPlan, cDefendPlanDefendPoint, 0, getPosition(gIslandPatrolIndex)); aiPlanSetVariableFloat(gIslandPatrolPlan, cDefendPlanEngageRange, 0, 50.0); // Loose aiPlanSetRequiresAllNeedUnits(gIslandPatrolPlan, false); aiPlanSetVariableFloat(gIslandPatrolPlan, cDefendPlanGatherDistance, 0, 20.0); aiPlanSetInitialPosition(gIslandPatrolPlan, gNavyVec); aiPlanSetUnitStance(gIslandPatrolPlan, cUnitStanceAggressive); aiPlanSetVariableInt(gIslandPatrolPlan, cDefendPlanRefreshFrequency, 0, 5); aiPlanSetVariableInt(gIslandPatrolPlan, cDefendPlanAttackTypeID, 0, cUnitTypeMilitary); // Only units aiPlanSetDesiredPriority(gIslandPatrolPlan, 70); // Very very low priority, gather unused units. aiPlanSetActive(gIslandPatrolPlan); xsSetRuleMinIntervalSelf(30); } else { gIslandPatrolIndex = gIslandPatrolIndex + 1; if(gIslandPatrolIndex >= gNumberOfAttacks) { gIslandPatrolIndex = 0; } aiPlanSetVariableVector(gIslandPatrolPlan, cDefendPlanDefendPoint, 0, getPosition(gIslandPatrolIndex)); } }