//============================================================================== /* aiEcon.xs Contains economic routines for the AI */ //============================================================================== //============================================================================== // Constants //============================================================================== extern const int cNumResourceTypes = 4; // Gold, food, wood, stone. //============================================================================== // Economy globals //============================================================================== extern int gGatherGoal = -1; // Stores all top-level gatherer data extern int gFarmBaseID = -1; // Current operating bases for each resource extern int gFoodBaseID = -1; extern int gGoldBaseID = -1; extern int gWoodBaseID = -1; extern int gStoneBaseID = -1; extern int gNextFarmBaseID = -1; // Overflow operating bases for each resource extern int gNextFoodBaseID = -1; extern int gNextGoldBaseID = -1; extern int gNextWoodBaseID = -1; extern int gNextStoneBaseID = -1; extern int gPrevFarmBaseID = -1; // Phasing-out bases for each resource extern int gPrevFoodBaseID = -1; extern int gPrevGoldBaseID = -1; extern int gPrevWoodBaseID = -1; extern int gPrevStoneBaseID = -1; extern bool gTimeToFarm = false; // Should we be farming? extern bool cOutOfGold = true; // used in deciding econ priorities, like caravans extern int gMaxFarms = 35; // The maximum # of farms the AI is allowed to have EDIT: old 15 //============================================================================== // Caravan globals //============================================================================== extern int gCaravanCount = 0; extern int gCaravanCountNorm = 40; // EDIT: old 6 extern int gCaravanCountNoGold = 55; // EDIT: old 16 //============================================================================== // Unit Queries //============================================================================== extern int gEconTCQuery = -1; extern int gHerdableQuery = -1; extern int gCaravanQuery = -1; //============================================================================== // Age up techs //============================================================================== extern int gAge2Tech = -1;//cTechGreekCapAge2; extern int gAge3Tech = -1;//cTechGreekCapAge3; extern int gAge4Tech = -1;//cTechGreekCapAge4; extern int gAge2Building = -1;//Temple level 1 extern int gAge3Building = -1;//Temple level 2 extern int gAge4Building = -1;//Temple level 3 extern int gAge2HouseCount = -1;//Celtic house count extern int gAge3HouseCount = -1;//Celtic house count extern int gAge4HouseCount = -1;//Celtic house count extern int gGreekCivID = 1; // Yep, the greeks are civ 1 extern int gEgyptianCivID = 3; // Pretty sure egypt is 3 extern int gCelticCivID = 6; // Celts are 6 extern int gPersianCivID = 8; // Persia is 8 extern int gCT_INVCivID = 11; // CT_INV is 11 extern int gBabylonianCivID = 22; // BOOM! All the way up to 22 for Babylon extern int gNorseCivID = 24; // These comments are pretty unnecessary //============================================================================== // Plan IDs //============================================================================== extern int gVillagerMaintainPlan = -1; // Plan to build vills as needed extern int gAgeUpgradePlan = -1; // Plan for aging up extern int gLandExplorePlan = -1; // Primary land exploration extern int gEconUpgradePlan = -1; // Plan for upgrading economy related units extern int gEmergencyHouseBuildPlan = -1; // Plan for house required to age up extern int gCaravanMaintainPlan = -1; // Plan to maintain caravans //============================================================================== // Global arrays //============================================================================== extern int gForecasts = -1; // Forecast float array initialized below. extern int gEconForecasts = -1; // Economy Forecast float array initialized below. This is a subset of gForecasts. extern int gMiliForecasts = -1; // Military Forecast float array initialized below. This is a subset of gForecasts. extern int gTargetVillagerCounts = -1; // How many Villagers do we want per age? (array) extern int gTargetGathererPercents = -1; // Percentage of gatherers assigned. Array. //============================================================================== // Age up building info arrays //============================================================================== extern const int gMaxBuildings = 30; // The maxiumum number of buildings we'll have. Bump this up if you need more EDIT: old 20 extern int gBuildings = -1; // Int array. All the buildings we want for this age. extern int gBuildingCount = -1; // Int array. Number of each building we want for this age. extern int gBuildingEscrow = -1; // Int array. The type of escrow (economic or military) to use for this building. extern int gBuildingBuild = -1; // Bool array. Should this building be automatically built? extern int gBuildingAgeState = -1; // Age state array. What part of the age should this be built in (early, middle, or late)? //============================================================================== // Globals for tracking data //============================================================================== extern float gEscrowEconPercent = 1.0; extern float gEscrowMiliPercent = 0.0; //============================================================================== // Age state constants //============================================================================== extern const int cAgeStateEarly = 0; extern const int cAgeStateMid = 1; extern const int cAgeStateLate = 2; //============================================================================== // Base Building Default Distances //============================================================================== extern float gHouseNearTCMin = 12.0; extern float gHouseNearTCMax = 30.0; extern float gHouseNearHouseMax = 25.0; extern float gFarmNearHouseMin = 5.0; extern float gTCNearTCMin = 40.0; extern float gMarketNearMainBaseMin = 80.0; extern float gMarketNearTowerMax = 0.0; extern float gWonderNearMainBaseMin = 30.0; extern float gBuildingNearTCMin = 25.0; extern int gOxCartQuery = -1; extern int gOxCartMaintainPlan = -1; extern int gCurrentOxCartsArray = -1; //============================================================================== // Method prototypes //============================================================================== mutable void forecastMilitaryUnits() {} mutable void forecastStrategicGoals() {} //============================================================================== // getAgeState() // // Figure out where we are in the age: // Early (= 0)--just started the age // Middle (= 1)--Between 50 and 75% of our total villagers built // Late (= 2)--More than 75% of our total villagers built //============================================================================== int getAgeState() { int curAge = kbGetAge(); float curVills = kbUnitCount(cMyID, gEconUnit, cUnitStateAlive); float minVills = 0; float maxVills = 0; if (curAge > cAge1) minVills = xsArrayGetInt(gTargetVillagerCounts, curAge - 1); else minVills = 0; maxVills = xsArrayGetInt(gTargetVillagerCounts, curAge); maxVills = maxVills - minVills; curVills = curVills - minVills; float villPercent = curVills / maxVills; if (villPercent < 0.5) return(cAgeStateEarly); else if (villPercent < 0.75) return(cAgeStateMid); else return(cAgeStateLate); // Because XS doesn't understand that the above if statement will always return... return (cAgeStateMid); } //============================================================================== // addBuildingToList() // // Add the given building to the building list arrays // BF 2/18/2012: Updated from Skirmish: // including additional error handling //============================================================================== mutable int addBuildingToList(int buildingType=-1, int count=1, int escrow=cEconomyEscrowID, int ageState=cAgeStateMid, bool build=false, int curOffset=0) { if (curOffset >= gMaxBuildings) { if(bDebugMessages == true){ aiEcho("*** ERROR: TOO MANY BUILDINGS IN AI BUILD LIST! PLEASE FIX!"); } return(curOffset); } if(buildingType == -1) { if(bDebugMessages == true){ aiEcho("*** THIS AI IS TRYING TO BUILD SOMETHING IT CAN'T"); } return(curOffset); } if(count == 0) { if(bDebugMessages == true){ aiEcho("*** THIS AI IS TRYING TO BUILD ZERO OF A CERTAIN BUILDING"); } return(curOffset); } xsArraySetInt(gBuildings, curOffset, buildingType); xsArraySetInt(gBuildingCount, curOffset, count); xsArraySetInt(gBuildingEscrow, curOffset, escrow); xsArraySetBool(gBuildingBuild, curOffset, build); xsArraySetInt(gBuildingAgeState, curOffset, ageState); return(curOffset+1); } //============================================================================== // agingUp() // // Are we currently in the process of aging up? //============================================================================== bool agingUp() { int curCiv = kbGetCiv(); bool retVal = false; if (curCiv != gEgyptianCivID) { if (aiPlanGetState(gAgeUpgradePlan) == cPlanStateResearch) retVal = true; } else { if (aiPlanGetState(gAgeUpgradePlan) == cPlanStateBuild) retVal = true; } //aiEcho("**** agingup is "+retVal); return(retVal); } //============================================================================== // createSimpleMaintainPlan() // // Create a plan to build the given type of unit //============================================================================== int createSimpleMaintainPlan(int puid=-1, int number=1, bool economy=true, int baseID=-1, int batchSize=1) { //Create a the plan name. string planName="Military"; if (economy == true) planName="Economy"; planName=planName+kbGetProtoUnitName(puid)+"Maintain"; int planID=aiPlanCreate(planName, cPlanTrain); if (planID < 0) return(-1); //Economy or Military. if (economy == true) { aiPlanSetEconomy(planID, true); aiPlanSetEscrowID(planID, cEconomyEscrowID); } else { aiPlanSetMilitary(planID, true); aiPlanSetEscrowID(planID, cMilitaryEscrowID); } //Unit type. aiPlanSetVariableInt(planID, cTrainPlanUnitType, 0, puid); //Number. aiPlanSetVariableInt(planID, cTrainPlanNumberToMaintain, 0, number); // Batch size aiPlanSetVariableInt(planID, cTrainPlanBatchSize, 0, batchSize); //If we have a base ID, use it. //aiEcho("Maintain plan base: "+baseID); if (baseID >= 0) { aiPlanSetBaseID(planID, baseID); if (economy == false) aiPlanSetVariableVector(planID, cTrainPlanGatherPoint, 0, kbBaseGetMilitaryGatherPoint(cMyID, baseID)); } aiPlanSetActive(planID); //Done. return(planID); } //============================================================================== // createSimpleBuildPlan() // // Create a plan to build the given type of building // BF 2/18/2012: Updated from Skirmish: // tuning edits, ally handling, age up based improvements //============================================================================== mutable int createSimpleBuildPlan(int puid=-1, int number=1, int pri=100, bool economy=true, int escrowID=-1, int baseID=-1, int numberBuilders=1) { if (cvOkToBuild == false) return(-1); //aiEcho("Creating build plan for " + number + "" + kbGetUnitTypeName(puid)); //Create the right number of plans. for (i=0; < number) { int planID=aiPlanCreate("Simple Build Plan, "+number+" "+kbGetUnitTypeName(puid), cPlanBuild); if (planID < 0) return(-1); // What to build aiPlanSetVariableInt(planID, cBuildPlanBuildingTypeID, 0, puid); // EDIT: Ace, Build Distance float _inflate_dis = 2; // 3 meter separation and far away (behind) TC if (puid == gHouse) { // Build away from the center of the base aiPlanSetVariableFloat(planID, cBuildPlanBuildingBufferSpace, 0, 0 + _inflate_dis); // old 0 aiPlanSetVariableVector(planID, cBuildPlanCenterPosition, 0, kbBaseGetLocation(cMyID, baseID)); //aiEcho("Base ID: "+baseID); aiPlanSetVariableFloat(planID, cBuildPlanCenterPositionDistance, 0, 50.0); aiPlanSetNumberVariableValues(planID, cBuildPlanInfluenceUnitTypeID, 3, false); aiPlanSetNumberVariableValues(planID, cBuildPlanInfluenceUnitDistance, 3, false); aiPlanSetNumberVariableValues(planID, cBuildPlanInfluenceUnitValue, 3, false); aiPlanSetNumberVariableValues(planID, cBuildPlanInfluenceUnitFalloff, 3, false); aiPlanSetNumberVariableValues(planID, cBuildPlanInfluenceUnitCap, 3, false); aiPlanSetVariableInt(planID, cBuildPlanInfluenceUnitTypeID, 0 , gTownCenter); aiPlanSetVariableFloat(planID, cBuildPlanInfluenceUnitDistance, 0 , gHouseNearTCMin); aiPlanSetVariableFloat(planID, cBuildPlanInfluenceUnitValue, 0 , 100.0); aiPlanSetVariableInt(planID, cBuildPlanInfluenceUnitFalloff, 0, cBPIFalloffLinearInverse); aiPlanSetVariableInt(planID, cBuildPlanInfluenceUnitCap, 0, -1); aiPlanSetVariableInt(planID, cBuildPlanInfluenceUnitTypeID, 1 , gTownCenter); aiPlanSetVariableFloat(planID, cBuildPlanInfluenceUnitDistance, 1 , gHouseNearTCMax); aiPlanSetVariableFloat(planID, cBuildPlanInfluenceUnitValue, 1 , 100.0); aiPlanSetVariableInt(planID, cBuildPlanInfluenceUnitFalloff, 1 , cBPIFalloffLinear); aiPlanSetVariableInt(planID, cBuildPlanInfluenceUnitCap, 1, -1); // Build near a max of 4 other houses aiPlanSetVariableInt(planID, cBuildPlanInfluenceUnitTypeID, 2, gHouse +_inflate_dis); aiPlanSetVariableFloat(planID, cBuildPlanInfluenceUnitDistance, 2, gHouseNearHouseMax+_inflate_dis); aiPlanSetVariableFloat(planID, cBuildPlanInfluenceUnitValue, 2, 200.0+_inflate_dis); aiPlanSetVariableInt(planID, cBuildPlanInfluenceUnitFalloff, 2, cBPIFalloffLinear); aiPlanSetVariableInt(planID, cBuildPlanInfluenceUnitCap, 2, 4); } // Keep less space around farms and try to build near TC else if (puid == gFarmUnit) { aiPlanSetVariableFloat(planID, cBuildPlanBuildingBufferSpace, 0, 0.1); // Stay away from houses aiPlanSetVariableInt(planID, cBuildPlanInfluenceUnitTypeID, 0, gHouse); aiPlanSetVariableFloat(planID, cBuildPlanInfluenceUnitDistance, 0, gFarmNearHouseMin); aiPlanSetVariableFloat(planID, cBuildPlanInfluenceUnitValue, 0, 100.0); aiPlanSetVariableInt(planID, cBuildPlanInfluenceUnitFalloff, 0, cBPIFalloffLinear); aiPlanSetVariableInt(planID, cBuildPlanInfluenceUnitCap, 0, -1); } // TCs should be placed as far away from other TCs as possible else if (puid == gTownCenter) { aiPlanSetVariableFloat(planID, cBuildPlanBuildingBufferSpace, 0, 25.0 +_inflate_dis); //EDIT: ace old 15 aiPlanSetVariableInt(planID, cBuildPlanInfluenceUnitTypeID, 0 , gTownCenter+_inflate_dis); aiPlanSetVariableFloat(planID, cBuildPlanInfluenceUnitDistance, 0 , gTCNearTCMin+_inflate_dis); aiPlanSetVariableFloat(planID, cBuildPlanInfluenceUnitValue, 0 , 100.0+_inflate_dis); aiPlanSetVariableInt(planID, cBuildPlanInfluenceUnitFalloff, 0 , cBPIFalloffLinearInverse); aiPlanSetVariableInt(planID, cBuildPlanInfluenceUnitCap, 0, -1); } else if(puid == kbFindBuilding(cUnitTypeUnitTypeBldgMarket)) { aiPlanSetVariableFloat(planID, cBuildPlanBuildingBufferSpace, 0 , 120.0 +_inflate_dis); // EDIT: old 20 aiPlanSetVariableVector(planID, cBuildPlanCenterPosition, 0, kbBaseGetLocation(cMyID, baseID)); aiPlanSetVariableFloat(planID, cBuildPlanCenterPositionDistance, 0 , gMarketNearMainBaseMin+_inflate_dis); if (gMarketNearTowerMax > 0.0) { aiPlanSetVariableFloat(planID, cBuildPlanBuildingBufferSpace, 0, 53.0); // EDIT: old 3 aiPlanSetVariableInt(planID, cBuildPlanInfluenceUnitTypeID, 1 , gTower+_inflate_dis); aiPlanSetVariableFloat(planID, cBuildPlanInfluenceUnitDistance, 1 , gMarketNearTowerMax+_inflate_dis); aiPlanSetVariableFloat(planID, cBuildPlanInfluenceUnitValue, 1, 100.0+_inflate_dis); aiPlanSetVariableInt(planID, cBuildPlanInfluenceUnitFalloff, 1, cBPIFalloffLinear); aiPlanSetVariableInt(planID, cBuildPlanInfluenceUnitCap, 1, -1); } else { aiPlanSetVariableVector(planID, cBuildPlanInfluencePosition, 0 , kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID))); aiPlanSetVariableFloat(planID, cBuildPlanInfluencePositionDistance, 0, gMarketNearMainBaseMin); aiPlanSetVariableFloat(planID, cBuildPlanInfluencePositionValue, 0, 100.0); aiPlanSetVariableInt(planID, cBuildPlanInfluencePositionFalloff, 0, cBPIFalloffLinearInverse); } } else if(puid == gAge2Building) { aiPlanSetVariableFloat(planID, cBuildPlanBuildingBufferSpace, 0, 6.0+_inflate_dis); } else if(puid == gAge4Building) { aiPlanSetVariableFloat(planID, cBuildPlanBuildingBufferSpace, 0, 2.0+_inflate_dis); aiPlanSetVariableVector(planID, cBuildPlanCenterPosition, 0, kbBaseGetLocation(cMyID, baseID)); //aiEcho("Base ID: "+baseID); aiPlanSetVariableFloat(planID, cBuildPlanCenterPositionDistance, 0, 50.0+_inflate_dis); } else if(puid == kbFindBuilding(cUnitTypeUnitTypeBldgWonder)) { calculateEnemyDirection(); vector center = kbBaseGetLocation(cMyID, baseID); vector diff = cInvalidVector; diff = xsVectorSetX(diff, xsVectorGetX(center) - (xsVectorGetX(cEnemyDirection) * gWonderNearMainBaseMin)); diff = xsVectorSetZ(diff, xsVectorGetZ(center) - (xsVectorGetZ(cEnemyDirection) * gWonderNearMainBaseMin)); aiPlanSetVariableVector(planID, cBuildPlanInfluencePosition, 0, diff); aiPlanSetVariableVector(planID, cBuildPlanCenterPosition, 0, kbBaseGetLocation(cMyID, baseID)); aiPlanSetVariableFloat(planID, cBuildPlanCenterPositionDistance, 0, 52.0); // old 50 } else { /*aiPlanSetVariableFloat(planID, cBuildPlanBuildingBufferSpace, 0, 5.0); // Get AWAY from the TC aiPlanSetVariableInt(planID, cBuildPlanInfluenceUnitTypeID, 0, gTownCenter); aiPlanSetVariableFloat(planID, cBuildPlanInfluenceUnitDistance, 0, 25.0); aiPlanSetVariableFloat(planID, cBuildPlanInfluenceUnitValue, 0, 1000.0); aiPlanSetVariableInt(planID, cBuildPlanInfluenceUnitFalloff, 0, cBPIFalloffLinear); aiPlanSetVariableInt(planID, cBuildPlanInfluenceUnitCap, 0, -1);*/ aiPlanSetVariableFloat(planID, cBuildPlanBuildingBufferSpace, 0, 3.0 +_inflate_dis); aiPlanSetVariableVector(planID, cBuildPlanCenterPosition, 0, kbBaseGetLocation(cMyID, baseID)); //aiEcho("Base ID: "+baseID); aiPlanSetVariableFloat(planID, cBuildPlanCenterPositionDistance, 0, 50.0 +_inflate_dis); aiPlanSetVariableInt(planID, cBuildPlanInfluenceUnitTypeID, 0, gTownCenter + _inflate_dis); aiPlanSetVariableFloat(planID, cBuildPlanInfluenceUnitDistance, 0, gBuildingNearTCMin + _inflate_dis); aiPlanSetVariableFloat(planID, cBuildPlanInfluenceUnitValue, 0, 100.0); aiPlanSetVariableInt(planID, cBuildPlanInfluenceUnitFalloff, 0, cBPIFalloffLinearInverse); aiPlanSetVariableInt(planID, cBuildPlanInfluenceUnitCap, 0, -1); } // Docks need at least a dock land point, but ideally a water point too if (puid == gDock) { aiPlanSetNumberVariableValues(planID, cBuildPlanDockPlacementPoint, 2, true); aiPlanSetVariableVector(planID, cBuildPlanDockPlacementPoint, 0, kbBaseGetLocation(cMyID, baseID)); // One point at main base //aiPlanSetVariableVector(planID, cBuildPlanDockPlacementPoint, 1, gNavyVec); // One point in water } aiPlanSetVariableBool(planID, cBuildPlanNoFailOutOfUnits, 0, true); aiPlanSetAllowHandleDamage(planID, false); //Priority. aiPlanSetDesiredPriority(planID, pri); //Mil vs. Econ. if (economy == true) aiPlanSetMilitary(planID, false); else aiPlanSetMilitary(planID, true); aiPlanSetEconomy(planID, economy); //Escrow. aiPlanSetEscrowID(planID, escrowID); //Builders. aiPlanAddUnitType(planID, gEconUnit, numberBuilders, numberBuilders, numberBuilders); //Base ID. aiPlanSetBaseID(planID, baseID); //Go. aiPlanSetActive(planID); } return(planID); // Only really useful if number == 1, otherwise returns last value. } //============================================================================== // createSimpleResearchPlan() // // Create a plan to research the given tech //============================================================================== int createSimpleResearchPlan(int techID=-1, int buildingID=-1, int escrowID=cRootEscrowID, int pri = 50) { int planID=aiPlanCreate("Research "+kbGetTechName(techID), cPlanResearch); if (planID < 0){ if(bDebugMessages == true){ aiEcho("Failed to create simple research plan for "+techID); } } else { aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, techID); aiPlanSetVariableInt(planID, cResearchPlanBuildingID, 0, buildingID); aiPlanSetDesiredPriority(planID, pri); aiPlanSetEscrowID(planID, escrowID); aiPlanSetActive(planID); } return(planID); } //============================================================================== // validateGatherPlanForOxCart() // // Checks to see if the given gather plan has villagers on it and isn't a // herdable or farm gather plan. //============================================================================== bool validateGatherPlanForOxCart(int planID = -1) { return(aiPlanGetNumberUnits(planID, cUnitTypeUnitTypeVillager1) > 0 && (aiPlanGetVariableInt(planID, cGatherPlanResourceType, 0) != cResourceFood || (aiPlanGetVariableInt(planID, cGatherPlanResourceSubType, 0) != cAIResourceSubTypeFarm && aiPlanGetVariableInt(planID, cGatherPlanResourceSubType, 0) != cAIResourceSubTypeHerdable))); } //============================================================================== // addOxCartsToGatherPlans // // This rule will move ox carts to the locations of resources that the AI are // gathering from. //============================================================================== rule addOxCartsToGatherPlans minInterval 11 active { // first of all, check to see if we have a mobile storehouse and DON'T have a regular storehouse if(kbFindUnit(cUnitTypeUnitTypeMobileStorehouse1) == -1 || kbFindBuilding(cUnitTypeUnitTypeBldgStorehouse) != -1) { if(bDebugMessages == true){ aiEcho("addOxCartsToGatherPlans: this AI either has no moble storehouses OR has a regular storehouse"); } xsDisableSelf(); return; } if(gOxCartQuery == -1) { gOxCartQuery = kbUnitQueryCreate("Ox Cart Query"); kbUnitQuerySetPlayerRelation(gOxCartQuery, cPlayerRelationSelf); kbUnitQuerySetUnitType(gOxCartQuery, cUnitTypeUnitTypeMobileStorehouse1); kbUnitQuerySetState(gOxCartQuery, cUnitStateAlive); } kbUnitQueryResetResults(gOxCartQuery); kbUnitQueryExecute(gOxCartQuery); int cartCount = kbUnitQueryNumberResults(gOxCartQuery); if(bDebugMessages == true){ aiEcho("addOxCartsToGatherPlans: ox carts " + cartCount); } int plans = aiPlanGetNumber(cPlanGather, cPlanStateGatherResources, true); if(bDebugMessages == true){ aiEcho("addOxCartsToGatherPlans: gather plans " + plans); } int planIndex = 0; int validPlanCount = 0; for(i = 0; < plans) { if(validateGatherPlanForOxCart(aiPlanGetIDByIndex(cPlanGather, cPlanStateGatherResources, true, i))) { validPlanCount = validPlanCount + 1; } } // create or update the maintain count. also train one more than we actually need if(gOxCartMaintainPlan == -1) { gOxCartMaintainPlan = createSimpleMaintainPlan(kbFindUnit(cUnitTypeUnitTypeMobileStorehouse1), validPlanCount + 1, true, kbBaseGetMainID(cMyID)); } else { aiPlanSetVariableInt(gOxCartMaintainPlan, cTrainPlanNumberToMaintain, 0, validPlanCount + 1); } // initialize or reset the array of ox cart IDs if(gCurrentOxCartsArray == -1) { gCurrentOxCartsArray = xsArrayCreateInt(20, -1, "Ox Carts"); } else { for(i = 0; < 20) { xsArraySetInt(gCurrentOxCartsArray, i, -1); } } int arrayIndex = 0; int cartIndex = 0; for(i = 0; < plans) { // get the plan int plan = aiPlanGetIDByIndex(cPlanGather, cPlanStateGatherResources, true, i); //aiPlanAddUnitType(plan, kbFindUnit(cUnitTypeUnitTypeMobileStorehouse1), 1, 1, 1); // see if it needs an ox cart if(validateGatherPlanForOxCart(plan)) { // get the location of the resource this plan is gathering from vector resLoc = kbResourceGetLocation(aiPlanGetVariableInt(plan, cGatherPlanKBResourceID, 0)); // find the closest ox cart float minDistance = 100000; int closestID = -1; for(j = 0; < cartCount) { int tempID = kbUnitQueryGetResult(gOxCartQuery, j); vector tempLoc = kbUnitGetPosition(tempID); float dist = xsVectorLength(tempLoc - resLoc); // check for any ox cart very close to this resource if(dist <= 10) { closestID = tempID; break; } // check already used ox cart array bool valid = true; for(k = 0; < 20) { if(xsArrayGetInt(gCurrentOxCartsArray, k) == tempID) { valid = false; } } // update closest if necessary if(valid && dist < minDistance) { minDistance = dist; closestID = tempID; } } if(closestID != -1) { // add ox cart ID to list of 'used' ox carts int oxID = closestID; xsArraySetInt(gCurrentOxCartsArray, arrayIndex, oxID); arrayIndex = arrayIndex + 1; if(bDebugMessages == true){ aiEcho("addOxCartsToGatherPlans: plan ID " + plan + " ox cart ID " + oxID); } //vector planLoc = aiPlanGetLocation(plan); vector cartLoc = kbUnitGetPosition(oxID); vector loc = resLoc;// - planLoc; //loc = planLoc + (loc * 0.5); int closestBase = kbFindClosestBase(cPlayerRelationSelf, loc); // make a new base if(xsVectorLength(loc - kbBaseGetLocation(cMyID, closestBase)) > 30) { int newBase = kbBaseCreate(cMyID, "Ox Cart Base " + oxID, loc, 30); if(bDebugMessages == true){ aiEcho("addOxCartsToGatherPlans: made new base ID " + newBase); } } // if the cart is far enough to away to warrant moving if(xsVectorLength(loc - cartLoc) > 10) { if(bDebugMessages == true){ aiEcho("addOxCartsToGatherPlans: location " + loc); } aiTaskUnitMove(oxID, loc); } else { if(bDebugMessages == true){ aiEcho("addOxCartsToGatherPlans: no need to move"); } } } } } /*for(i = cartIndex; < cartCount) { aiTaskUnitMove(kbUnitQueryGetResult(gOxCartQuery, i), kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID)) + vector(-20,0,-20)); }*/ } //============================================================================== // initGatherers() // // Initialize the gatherer targets and gatherer percentage arrays // BF 2/18/2012: Updated from Skirmish: // tuning edits // Age 2 Vil Count is now 4/9 instead of 1/3 // Age 3 Vil Count is now 7/9 instead of 2/3 //============================================================================== mutable void initGatherers() { gForecasts = xsArrayCreateFloat(cNumResourceTypes, 0.0, "Forecasts"); gEconForecasts = xsArrayCreateFloat(cNumResourceTypes, 0.0, "EconForecasts"); gMiliForecasts = xsArrayCreateFloat(cNumResourceTypes, 0.0, "MillForecasts"); gTargetGathererPercents = xsArrayCreateFloat(cNumResourceTypes, -1.0, "Gatherer Percents"); gTargetVillagerCounts = xsArrayCreateInt(cAge4+1, 0, "Target Settler Counts"); xsArraySetInt(gTargetVillagerCounts, cAge1, btTargetAge1VilCount); xsArraySetInt(gTargetVillagerCounts, cAge2, btTargetAge1VilCount + (4 * (btTargetAge4VilCount - btTargetAge1VilCount)) / 9); xsArraySetInt(gTargetVillagerCounts, cAge3, btTargetAge1VilCount + (7 * (btTargetAge4VilCount - btTargetAge1VilCount)) / 9); xsArraySetInt(gTargetVillagerCounts, cAge4, btTargetAge4VilCount); // Setup the plan to create vills //aiEcho("Maintaining " + xsArrayGetInt(gTargetVillagerCounts, kbGetAge()) + " vills"); gVillagerMaintainPlan = createSimpleMaintainPlan(gEconUnit, xsArrayGetInt(gTargetVillagerCounts, kbGetAge()), true, kbBaseGetMainID(cMyID), 1); aiPlanSetVariableBool(gVillagerMaintainPlan, cTrainPlanCountExisting, 0, true); } //============================================================================== // createEscrows() // // Setup the default escrows and add one for aging up // BF 2/18/2012: Updated from Skirmish: // Increasing the initial escrow cap for military //============================================================================== mutable void createEscrows() { // Set up the escrows kbEscrowSetPercentage(cEconomyEscrowID, cResourceFood, .70); kbEscrowSetPercentage(cEconomyEscrowID, cResourceWood, .50); kbEscrowSetPercentage(cEconomyEscrowID, cResourceGold, .30); kbEscrowSetPercentage(cEconomyEscrowID, cResourceStone, .30); kbEscrowSetPercentage(cEconomyEscrowID, cResourceShips, 0.0); kbEscrowSetCap(cEconomyEscrowID, cResourceFood, 200); kbEscrowSetCap(cEconomyEscrowID, cResourceWood, 200); kbEscrowSetCap(cEconomyEscrowID, cResourceGold, 200); kbEscrowSetCap(cEconomyEscrowID, cResourceStone, 200); kbEscrowSetPercentage(cMilitaryEscrowID, cResourceFood, .0); kbEscrowSetPercentage(cMilitaryEscrowID, cResourceWood, .0); kbEscrowSetPercentage(cMilitaryEscrowID, cResourceGold, .0); kbEscrowSetPercentage(cMilitaryEscrowID, cResourceStone, .0); kbEscrowSetPercentage(cMilitaryEscrowID, cResourceShips, 0.0); kbEscrowSetCap(cMilitaryEscrowID, cResourceFood, 16000); kbEscrowSetCap(cMilitaryEscrowID, cResourceWood, 16000); kbEscrowSetCap(cMilitaryEscrowID, cResourceGold, 24000); kbEscrowSetCap(cMilitaryEscrowID, cResourceStone, 16000); gEscrowEconPercent = 1.0; gEscrowMiliPercent = 0.0; kbEscrowAllocateCurrentResources(); } //============================================================================== // initGatherGoal() // // Setup the initial gathering plan for the vills // BF 2/18/2012: Updated from Skirmish: // setting TargetSelectFactor // BF 7/16/2012: Updated from Skirmish: // setting TargetSelectFactor values to reflect the tuning that went live with P8 //============================================================================== mutable void initGatherGoal() { kbSetTargetSelectorFactor(cTSFactorDistance, -240); kbSetTargetSelectorFactor(cTSFactorPoint, 0); kbSetTargetSelectorFactor(cTSFactorBase, -50); //Set the RGP weights. Script in charge. aiSetResourceGathererPercentageWeight(cRGPScript, 1.0); // Portion driven by forecast aiSetResourceGathererPercentageWeight(cRGPCost, 0.0); // Portion driven by exchange rates //Set initial gatherer percentages. aiSetResourceGathererPercentage(cResourceFood, 0.8, false, cRGPScript); aiSetResourceGathererPercentage(cResourceWood, 0.1, false, cRGPScript); // EDIT: ACE old 0.2 aiSetResourceGathererPercentage(cResourceGold, 0.0, false, cRGPScript); aiSetResourceGathererPercentage(cResourceStone, 0.0, false, cRGPScript); // [5/4/2009 CJS] Initialize any civ-specific resource needs here aiNormalizeResourceGathererPercentages(cRGPScript); //Set up the initial resource breakdowns. int numFoodEasyPlans=1; int numFoodHuntEasyPlans=1; int numFoodHuntAggressivePlans=0; int numFishPlans=0; int numFarmPlans=0; int numWoodPlans=1; int numGoldPlans=0; int numStonePlans=0; if (cvOkToGatherFood == true) { aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeEasy, numFoodEasyPlans, 49, 0.5, kbBaseGetMainID(cMyID)); // All on easy and huntable food at start, EDIT old 0.8 aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeHunt, numFoodHuntEasyPlans, 51, 0.5, kbBaseGetMainID(cMyID)); // EDIT: old 0.2 aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeHuntAggressive, numFoodHuntAggressivePlans, 49, 0.0, kbBaseGetMainID(cMyID)); aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeFish, numFishPlans, 49, 0.0, kbBaseGetMainID(cMyID)); aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeFarm, numFarmPlans, 52, 0.0, kbBaseGetMainID(cMyID)); } if (cvOkToGatherWood == true) aiSetResourceBreakdown(cResourceWood, cAIResourceSubTypeEasy, numWoodPlans, 50, 1.0, kbBaseGetMainID(cMyID)); if (cvOkToGatherGold == true) aiSetResourceBreakdown(cResourceGold, cAIResourceSubTypeEasy, numGoldPlans, 55, 1.0, kbBaseGetMainID(cMyID)); if (cvOkToGatherStone == true) aiSetResourceBreakdown(cResourceGold, cAIResourceSubTypeEasy, numStonePlans, 55, 1.0, kbBaseGetMainID(cMyID)); } //============================================================================== // spewForecasts //============================================================================== void spewForecasts() { // Debug aid, dump forecast contents int gold = xsArrayGetFloat(gForecasts, cResourceGold); int wood = xsArrayGetFloat(gForecasts, cResourceWood); int food = xsArrayGetFloat(gForecasts, cResourceFood); int stone = xsArrayGetFloat(gForecasts, cResourceStone); if(bDebugMessages == true){ aiEcho("Forecast Gold: "+gold+", Wood: "+wood+", Food: "+food+", Stone"+stone); } } //============================================================================== // addItemToForecasts() // // Add the given item to the forecasts //============================================================================== void addItemToForecasts(int protoUnit = -1, int qty = -1, bool isMilitary = false) { // Add qty of item protoUnit to the global forecast arrays if (protoUnit < 0) return; if (qty < 1) { if(bDebugMessages == true){ aiEcho("Not adding "+kbUnitGetProtoName(protoUnit)+" to forecast because an invalid quantity was specified"); } return; } //aiEcho("FORECAST: Adding " + qty + " of unit " + kbUnitGetProtoName(protoUnit) + " (id=" + protoUnit + ")"); int i=0; for (i=0; 5000) { int foodAmount = kbGetAmountValidResources( mainBaseID, cResourceFood , cAIResourceSubTypeEasy, 60.0 ); // Subtract mills at 999 each. foodAmount = foodAmount - (getUnitCountByLocation(gFarmUnit, cMyID, cUnitStateAlive, loc, 60.0) * 999); float percentOnFood = aiGetResourceGathererPercentage( cResourceFood, cRGPActual ); int numFoodGatherers = percentOnFood * kbUnitCount(cMyID, gEconUnit, cUnitStateAlive); if (numFoodGatherers < 1) numFoodGatherers = 1; int foodPerGatherer = foodAmount / numFoodGatherers; if ( (xsGetTime() > 120000) && (gTimeToFarm == false) && (foodPerGatherer < cMinResourcePerGatherer) ) { //aiEcho(" **** It's time to start farming!"); gTimeToFarm = true; } } } //============================================================================== // updateForecasts() // // Create 3-minute forecast of resource needs for food, wood, gold, and stone // BF 2/18/2012: Updated from Skirmish: // Caps the amount of villagers used in the forecast // Adds some handling for forecasting "Strategic Goals" (like Wonders) // MC 5/4/2012: Instead of referencing specific CivIDs, we now assume that // AgeXBuilding will take precedence over an AgeXTech. //============================================================================== mutable void updateForecasts() { int numVills = 0; int numUnits=0; int currentVillCount = kbUnitCount(cMyID, gEconUnit, cUnitStateAlive); int effectiveAge = kbGetAge(); int villTarget = 0; if (agingUp() == true) { effectiveAge = effectiveAge + 1; villTarget = xsArrayGetInt(gTargetVillagerCounts, effectiveAge); } else{ villTarget = aiPlanGetVariableInt(gVillagerMaintainPlan, cTrainPlanNumberToMaintain, 0); } numVills = villTarget - currentVillCount; if(numVills > 10) { numVills = 10; } //aiEcho("Adding " + numVills + " vills. (current vills: "+currentVillCount+" target vills: "+villTarget); // Clear out any existing forecasts clearForecasts(); if (cvOkToBuild == true) { int i=0; //aiEcho("MaxBuildings: "+gMaxBuildings); for (i = 0; < gMaxBuildings) { int buildingID = xsArrayGetInt(gBuildings, i); int buildingCount = xsArrayGetInt(gBuildingCount, i); int curBuildingCount = kbUnitCount(cMyID, buildingID, cUnitStateABQ); int buildingAgeState = xsArrayGetInt(gBuildingAgeState, i); //aiEcho("Building ID: "+buildingID); if (buildingID == -1) break; // Make sure we're at the right point in the age to build this building if (buildingAgeState > getAgeState()) continue; //TD: limiting forecasts to 1 building to keep it to near future. //aiEcho("Building Count: "+buildingCount+" CurBuildingCount: "+curBuildingCount); if ((buildingCount - curBuildingCount) > 0) { int buildingEscrow = xsArrayGetInt(gBuildingEscrow, i); bool isMilitary = (buildingEscrow == cMilitaryEscrowID); addItemToForecasts(buildingID, 1, isMilitary); //aiEcho("Adding 1" + kbUnitGetProtoName(buildingID) + ". (military flag"+isMilitary); } } // allow for strategic goals to be forecast forecastStrategicGoals(); int houseCount = 0; // Calculate how many houses we need to build if (kbGetBuildLimit(cMyID, gHouse) >= kbUnitCount(cMyID, gHouse, cUnitStateAlive)) { int milCount = 2 * kbUnitPickGetMaximumNumberUnits(gLandUnitPicker) - kbUnitCount(cMyID, cUnitTypeLogicalTypeLandMilitary, cUnitStateAlive); if (milCount < 0) milCount = 0; //TD: Forecasting by additional houses actually required now. int currentHousePop = kbUnitCount(cMyID, gHouse, cUnitStateAlive) * aiGetPopCapValue(gHouse); int currentTcPop = kbUnitCount(cMyID, gTownCenter, cUnitStateAlive) * aiGetPopCapValue(gTownCenter); int popRequired = currentVillCount + milCount - currentHousePop - currentTcPop; //aiEcho("For pop there are "+currentVillCount+" villagers and "+milCount+" military and "+currentHousePop+" pop in houses and "+currentTcPop+" pop in TC's"); int numHouses = 1; //TD: always forecasting 1 house min if (popRequired > 0) numHouses = (((popRequired) / aiGetPopCapValue(gHouse)) / 2) + 1; //forecasting for us to build half the houses we need soon addItemToForecasts(gHouse, numHouses, false); //aiEcho("Adding " + numHouses + " houses to support an additonal :"+popRequired); } // Calculate how many towers we need to build once we're in or past Age 2. //if (effectiveAge > cAge1) if (kbGetAge() > cAge1) { int numTowers = cvNumTowers - kbUnitCount(cMyID, gTower, cUnitStateABQ); //TD: Only forecast up to 2 towers at a time to keep it near future forecasting if (numTowers > 2) numTowers = 2; if (numTowers > 0) addItemToForecasts(gTower, numTowers, false); //aiEcho("Adding " + numTowers + " towers."); } // Calculate how many farms we need to build if (kbGetAge() >= cAge3) { int numFarms = kbUnitCount(cMyID, gFarmUnit, cUnitStateABQ); int villCount = kbUnitCount(cMyID, gEconUnit, cUnitStateAlive); int numGatherers = villCount * aiGetResourceGathererPercentage(cResourceFood); int calcFarms = numGatherers - numFarms; if (numFarms < gMaxFarms && (calcFarms) > 0) { //Only forecast 1 farm at a time in age 2 since we may or may not build farms anytime soon if (calcFarms == 1 || (kbGetAge() == cAge2 && numVills > 0)) calcFarms = 1; else calcFarms = 2; addItemToForecasts(gFarmUnit, calcFarms, false); //aiEcho("Adding " + calcFarms + " farms."); } } } // Add all military units to the forecast on quest maps or if we're past age 1 if (cvOkToTrainArmy == true && (btMilitaryInAge1 || kbGetAge() > cAge1)) forecastMilitaryUnits(); // Add everything else int villTargetPercentReached = (currentVillCount * 100) / villTarget; //aiEcho("I have "+currentVillCount+" vills of my target "+villTarget+" giving a percent of "+villTargetPercentReached+"%"); switch (kbGetAge()) { case cAge1: { if (numVills > 6) numVills = 6; addItemToForecasts(gEconUnit, numVills); //aiEcho("Adding " + numVills + " vills."); // Age upgrade //TD: Removed villager assumptions, and we add age up forecast at 50% vill target if (agingUp() == false && cvMaxAge > cAge1 && villTargetPercentReached >= btAge2VilReqPercent) { if(gAge2Building != -1) { if(bDebugMessages == true){ aiEcho("updateForecast: age 2 building added to forecast"); } addItemToForecasts(gAge2Building, 1); } else if(kbTechGetStatus(gAge2Tech) == cTechStatusObtainable) { if(bDebugMessages == true){ aiEcho("updateForecast: age 2 tech added to forecast"); } addTechToForecasts(gAge2Tech); } } break; } case cAge2: { addItemToForecasts(gEconUnit, numVills); //aiEcho("Adding " + numVills + " vills."); // Add age upgrade if (agingUp() == false && cvMaxAge > cAge2 && villTargetPercentReached >= btAge3VilReqPercent) { if(gAge3Building != -1) { if(bDebugMessages == true){ aiEcho("updateForecast: age 3 building added to forecast"); } addItemToForecasts(gAge3Building, 1); } else if(kbTechGetStatus(gAge3Tech) == cTechStatusObtainable) { if(bDebugMessages == true){ aiEcho("updateForecast: age 3 tech added to forecast"); } addTechToForecasts(gAge3Tech); } } break; } case cAge3: { addItemToForecasts(gEconUnit, numVills); //aiEcho("Adding " + numVills + " vills."); // Age upgrade if (agingUp() == false && cvMaxAge > cAge3 && villTargetPercentReached >= btAge4VilReqPercent) { if(gAge4Building != -1) { if(bDebugMessages == true){ aiEcho("updateForecast: age 4 building added to forecast"); } addItemToForecasts(gAge4Building, 1); } else if(kbTechGetStatus(gAge4Tech) == cTechStatusObtainable) { if(bDebugMessages == true){ aiEcho("updateForecast: age 4 tech added to forecast"); } addTechToForecasts(gAge4Tech); } } break; } case cAge4: { addItemToForecasts(gEconUnit, numVills); //aiEcho("Adding " + numVills + " vills."); break; } } //spewForecasts(); } //============================================================================== // updateFoodPlans() // // Update the number of active plans for each food type // BF 2/18/2012: Updated from Skirmish: // Allows for berries beyond age 1 and herdable gathering //============================================================================== mutable void updateFoodPlans() { //aiEcho("updateFoodPlans"); const int cMaxSettlersPerPlan = 8; const int cMinSettlersPerPlan = 2; // Must be much less than Max/2 to avoid thrashing static int totalFoodPlans = 0; // How many are currently requested? Try to avoid thrashing this number // Final plan numbers int easyFoodPlans = 0; int herdFoodPlans = 0; int huntFoodPlans = 0; int farmFoodPlans = 0; int numVills = kbUnitCount(cMyID, gEconUnit, cUnitStateAlive); // Round to the closest villager instead of truncating float floatVills = numVills; int numGatherers = (floatVills * aiGetResourceGathererPercentage(cResourceFood)) + 0.5; //aiEcho("numVills = " + numVills); //aiEcho("Food% = " + aiGetResourceGathererPercentage(cResourceFood)); //aiEcho("numGatherers1 = " + numGatherers); // See how many farms we have int numFarms = kbUnitCount(cMyID, gFarmUnit, cUnitStateABQ); farmFoodPlans = numFarms; //aiEcho("numFarms = " + numFarms); // Make sure we don't have more plans than gatherers if (numFarms > numGatherers) farmFoodPlans = numGatherers; numGatherers = numGatherers - numFarms; //aiEcho("numGatherers2 = " + numGatherers); // Do we have anyone left after farms? if (numGatherers > 0) { int closeEasyFoodAmount = kbGetAmountValidResources(kbBaseGetMainID(cMyID), cResourceFood , cAIResourceSubTypeEasy, 30.0); int herdableFoodAmount = kbGetAmountValidResources(kbBaseGetMainID(cMyID), cResourceFood, cAIResourceSubTypeHerdable, 20.0); int easyFoodAmount = kbGetAmountValidResources(kbBaseGetMainID(cMyID), cResourceFood , cAIResourceSubTypeEasy, 60.0); int huntFoodAmount = kbGetAmountValidResources(kbBaseGetMainID(cMyID), cResourceFood , cAIResourceSubTypeHunt, 60.0); //aiEcho("close Easy food : " + closeEasyFoodAmount); //aiEcho("Easy food : " + easyFoodAmount); //aiEcho("Hunt food : " + huntFoodAmount); // Figure out how many plans we should have int otherPlans = totalFoodPlans - farmFoodPlans; // Let's try to preserve the total number of plans if (otherPlans < 1) otherPlans = 1; while ((numGatherers / otherPlans) < cMinSettlersPerPlan) // Too many plans, not enough gatherers otherPlans = otherPlans - 1; while ((numGatherers / otherPlans) > cMaxSettlersPerPlan) // Too many gatherers, not enough plans otherPlans = otherPlans + 1; totalFoodPlans = otherPlans + farmFoodPlans; //aiEcho("Food plans: " + totalFoodPlans); // check if we have herdables to gather from if (herdableFoodAmount > 0) { //aiEcho("I have easy food! " + otherPlans); herdFoodPlans = otherPlans; otherPlans = otherPlans - herdFoodPlans; } // See if we have any "easy" food nearby in age 1. if (closeEasyFoodAmount > 0) { //aiEcho("I have easy food! " + otherPlans); if(otherPlans > 1) easyFoodPlans = otherPlans / 2; else easyFoodPlans = otherPlans; otherPlans = otherPlans - easyFoodPlans; } // Nope, see if we have any huntable food nearby if (otherPlans > 0 && huntFoodAmount > 0) { //aiEcho("I have huntable food! " + otherPlans); huntFoodPlans = otherPlans; //Age 3 and 4 we cap at 1 hunting group if (kbGetAge() > cAge2 && otherPlans > 1) huntFoodPlans = 1; //Age 2 we cap at 2 hunting groups else if (kbGetAge() == cAge2 && otherPlans > 2) huntFoodPlans = 2; otherPlans = otherPlans - huntFoodPlans; } if (huntFoodPlans == 0 && easyFoodPlans == 0 && easyFoodAmount > 0) { //aiEcho("I have easy food! " + otherPlans); if (otherPlans > 1) easyFoodPlans = 1; } // As we get past 1 plan for easy or hunting, or were out of easy or hunting start farming if (cvOkToBuildFarms && numFarms < gMaxFarms && kbGetAge() > cAge1 && (otherPlans > 0 || (huntFoodPlans == 0 && easyFoodPlans == 0 && herdFoodPlans == 0))) { //aiEcho("I need to build farms (maybe)"); //aiEcho("I have "+numFarms+" farms right now."); //aiEcho("I have "+numVills+" villagers right now."); //aiEcho(""+aiGetResourceGathererPercentage(cResourceFood)+"% should be getting food, so that means "+numGatherers+" are gathering food."); //aiEcho("Therefore, I need "+(numGatherers - numFarms)+" more farms to support all of them."); // Make sure we don't already have a farm queued int planID = aiPlanGetIDByTypeAndVariableType(cPlanBuild, cBuildPlanBuildingTypeID, gFarmUnit); //We only want to build farms for villagers we are not hunting with. int calcFarms = 1;//numGatherers - numFarms - ((huntFoodPlans + easyFoodPlans + herdFoodPlans) * cMaxSettlersPerPlan); if (planID == -1 && numFarms < gMaxFarms && calcFarms > 0) { //aiEcho("I'M BUILDING "+calcFarms+" NEW FARMs!"); //aiEcho("Farm debug: hunt plans: "+huntFoodPlans); //aiEcho("Farm debug: easy food plans: "+easyFoodPlans); createSimpleBuildPlan(gFarmUnit, calcFarms, 90, true, cEconomyEscrowID, kbBaseGetMainID(cMyID), 1); } // Everyone else should keep hunting //huntFoodPlans = otherPlans - easyFoodPlans; } } //aiEcho("Easy food plans: "+easyFoodPlans); //aiEcho("Hunt food plans: "+huntFoodPlans); //aiEcho("Farm food plans: "+numFarms); aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeEasy, easyFoodPlans, 79, 1.0, kbBaseGetMainID(cMyID)); aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeHerdable, herdFoodPlans, 82, 1.0, kbBaseGetMainID(cMyID)); aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeHunt, huntFoodPlans, 51, 1.0, kbBaseGetMainID(cMyID)); aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeHuntAggressive, 0, 79, 0.0, kbBaseGetMainID(cMyID)); aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeFish, 0, 79, 0.0, kbBaseGetMainID(cMyID)); aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeFarm, numFarms, 81, 1.0, kbBaseGetMainID(cMyID)); } //============================================================================== // updateResourcePlans() // //============================================================================== int updateResourcePlans(int resourceType=-1, int min=-1, int max=-1, int totalPlans=-1) { // Sanity if (resourceType < 0 || min < 0 || max < 0 || totalPlans < 0) { return(totalPlans); } if (min > max) { int temp = max; max = min; min = temp; } int numVills = kbUnitCount(cMyID, gEconUnit, cUnitStateAlive); // Round to the closest villager instead of truncating float floatVills = numVills; int numGatherers = (floatVills * aiGetResourceGathererPercentage(resourceType)) + 0.5; if (totalPlans < 1) totalPlans = 1; while ((numGatherers / totalPlans) < min) // Too many plans, not enough gatherers totalPlans = totalPlans - 1; while ((numGatherers / totalPlans) > max) // Too many gatherers, not enough plans totalPlans = totalPlans + 1; //aiEcho("Wood plans: "+totalPlans); aiSetResourceBreakdown(resourceType, cAIResourceSubTypeEasy, totalPlans, 82, 1.0, kbBaseGetMainID(cMyID)); return(totalPlans); } //============================================================================== // updateGatherers() // // Given the desired allocation of gatherers, set the desired number // of gatherers, and the breakdown between resources. // BF 2/18/2012: Updated from Skirmish: // increases the number of resource plans after Age 2 // increases the default number of Gold plans //============================================================================== mutable void updateGatherers(void) { //aiEcho("------------------------------------------------------"); //aiEcho("Gold forecast "+xsArrayGetFloat(gForecasts, cResourceGold)); //aiEcho("Wood forecast "+xsArrayGetFloat(gForecasts, cResourceWood)); //aiEcho("Food forecast "+xsArrayGetFloat(gForecasts, cResourceFood)); //aiEcho("Stone forecast "+xsArrayGetFloat(gForecasts, cResourceStone)); //aiEcho(" "); // Calculate the total forecast and total shortfall for all resource types float totalShortfall = 0.0; float shortfall = 0.0; float totalForecast = 0.0; float thisForecast = 0.0; int i = 0; int currentVillCount = kbUnitCount(cMyID, gEconUnit, cUnitStateAlive); // EDIT: ACE int gold = kbResourceGet(cResourceGold); // Gets Current Amount of Resource like gold,wood etc int wood = kbResourceGet(cResourceWood);// Gets Current Amount of Resource like gold,wood etc int food = kbResourceGet(cResourceFood);// Gets Current Amount of Resource like gold,wood etc int stone = kbResourceGet(cResourceStone); // Gets Current Amount of Resource like gold,wood etc float wood_percent = 0.0; float food_percent = 0.0; float gold_percent = 0.0; float stone_percent = 0.0; // Teach the ediat AI how to use villagers if(currentVillCount >= 1 && currentVillCount <= 11) { wood_percent = 0.3; food_percent = 0.7; gold_percent = 0; stone_percent = 0; } if(currentVillCount >= 12 && currentVillCount <= 14) { wood_percent = 0.3; food_percent = 0.4; gold_percent = 0; stone_percent = 0.3; } if(currentVillCount >= 15 && currentVillCount <= 16) { wood_percent = 0.35; food_percent = 0.35; gold_percent = 0; stone_percent = 0.3; } if(currentVillCount >= 17 && currentVillCount <= 18) { wood_percent = 0.35; food_percent = 0.64; gold_percent = 0.1; stone_percent = 0.01; } if(currentVillCount >= 19 && currentVillCount <= 21) { wood_percent = 0.35; food_percent = 0.54; gold_percent = 0.1; stone_percent = 0.01; } if(currentVillCount >= 22) { wood_percent = 0.25; food_percent = 0.45; gold_percent = 0.2; stone_percent = 0.1; } // if vills are less than 7 then collect food if(currentVillCount < 7){ //wood_percent = 0; //stone_percent = 0; //gold_percent = 0; //gTimeToFarm = false; } // Split villagers evenly kinda //TD: If we have less than 5 villagers put them all on food as a flat rule. EDIT: Ace if (currentVillCount < 80) { if(wood_percent > 0){ aiSetResourceGathererPercentage(cResourceWood, wood_percent, false, cRGPScript); aiSetResourceGathererPercentage(cResourceWood, wood_percent, false, cRGPCost); xsArraySetFloat(gTargetGathererPercents,cResourceWood,wood_percent); } if(gold_percent > 0){ aiSetResourceGathererPercentage(cResourceGold, gold_percent, false, cRGPScript); aiSetResourceGathererPercentage(cResourceGold, gold_percent, false, cRGPCost); xsArraySetFloat(gTargetGathererPercents,cResourceGold,gold_percent); } if(food_percent > 0){ aiSetResourceGathererPercentage(cResourceFood, food_percent, false, cRGPScript); aiSetResourceGathererPercentage(cResourceFood, food_percent, false, cRGPCost); xsArraySetFloat(gTargetGathererPercents,cResourceFood,food_percent); } if(stone_percent > 0){ aiSetResourceGathererPercentage(cResourceStone, stone_percent, false, cRGPScript); aiSetResourceGathererPercentage(cResourceStone, stone_percent, false, cRGPCost); xsArraySetFloat(gTargetGathererPercents,cResourceStone,stone_percent); } } else { for (i = 0; < cNumResourceTypes) { // Get forecast and add to total //thisForecast = xsArrayGetFloat(gForecasts, i); //TD: Taking percent focus on military and economy as a factor for forecasts thisForecast = (xsArrayGetFloat(gEconForecasts, i) * gEscrowEconPercent) + (xsArrayGetFloat(gMiliForecasts, i) * gEscrowMiliPercent); totalForecast = totalForecast + thisForecast; // This ensures that the shortfall is always <= forecast, so that // we always get a range for the weights between 0 and 1. shortfall = thisForecast - kbResourceGet(i); if (shortfall > 0.0) totalShortfall = totalShortfall + shortfall; else shortfall = 0.0; /*if (i == 0) aiEcho("Gold shortfall "+shortfall); else if (i == 1) aiEcho("Wood shortfall "+shortfall); else if (i == 2) aiEcho("Food shortfall "+shortfall); else if (i == 3) aiEcho("Stone shortfall "+shortfall); else aiEcho("Unknown resource shortfall "+shortfall);*/ } //aiEcho(" "); //TD: Based on number of villagers we have decide on how important this shortfall is, note the 120 is based on 2 minutes of vill production //int currentVillCount = kbUnitCount(cMyID, gEconUnit, cUnitStateAlive); if (currentVillCount <= 0) currentVillCount = 1; float percentShortfallGatherers = totalShortfall / (120.0 * currentVillCount); if (percentShortfallGatherers > 1.0) percentShortfallGatherers = 1.0; float percentForecastGatherers = 1.0 - percentShortfallGatherers; //aiEcho("Percent shortfall: "+percentShortfallGatherers+" percent forecast: "+percentForecastGatherers); // Avoid division by 0 if (totalShortfall <= 0.0) totalShortfall = 1.0; // Calculate gatherer percentages based off of our weights float forecast = 0.0; shortfall = 0.0; float scratch = 0.0; float delta = 0.0; float currentResourcePercent = 0.0; for (i = 0; < cNumResourceTypes) { //thisForecast = xsArrayGetFloat(gForecasts, i); //TD: Taking percent focus on military and economy as a factor for forecasts thisForecast = (xsArrayGetFloat(gEconForecasts, i) * gEscrowEconPercent) + (xsArrayGetFloat(gMiliForecasts, i) * gEscrowMiliPercent); shortfall = (thisForecast - kbResourceGet(i)); if (shortfall < 0.0) { //TD: As we gain excess resources above our forecast, start reducing gathering rate on this for forecast gathering. if (thisForecast > 0.0) { thisForecast = thisForecast + (shortfall * 2); if (thisForecast < 1.0) thisForecast = 1.0; } shortfall = 0.0; } //TD: split villagers based on forecast and shortfall weighted by how large the shortfall total is. scratch = (shortfall / totalShortfall) * percentShortfallGatherers; scratch = scratch + ((thisForecast / totalForecast) * percentForecastGatherers); //scratch = (shortfall / totalShortfall); // old method - purely shortfall based percentage //TD: gonna make transitions take a little more time as these percents change, which smooths out villager usage currentResourcePercent = xsArrayGetFloat(gTargetGathererPercents,i); if (currentResourcePercent >= 0.0) { delta = scratch - currentResourcePercent; if (delta < -0.05) scratch = currentResourcePercent - 0.05; else if (delta > 0.05) scratch = currentResourcePercent + 0.05; } aiSetResourceGathererPercentage(i, scratch, false, cRGPScript); aiSetResourceGathererPercentage(i, scratch, false, cRGPCost); xsArraySetFloat(gTargetGathererPercents,i,scratch); //aiEcho("Resource: "+i+" has a shortfall of: "+shortfall+" a forecast of : "+thisForecast+" and is at percentage"+scratch); } } aiNormalizeResourceGathererPercentages(cRGPScript); // Set them to 1.0 total, just in case these don't add up. aiNormalizeResourceGathererPercentages(cRGPCost); // Set them to 1.0 total, just in case these don't add up. // We don't want these numbers to fluctuate wildly, so we modify them every frame // instead of recalculating them. static int numWoodPlans = 0; static int numGoldPlans = 0; static int numStonePlans = 0; updateFoodPlans(); if(kbGetAge() > cAge2) { if (cvOkToGatherWood == true) numWoodPlans = updateResourcePlans(cResourceWood, 4, 12, numWoodPlans); if (cvOkToGatherGold == true) numGoldPlans = updateResourcePlans(cResourceGold, 6, 16, numGoldPlans); if (cvOkToGatherStone == true) numStonePlans = updateResourcePlans(cResourceStone, 6, 14, numStonePlans); } else { if (cvOkToGatherWood == true) numWoodPlans = updateResourcePlans(cResourceWood, 2, 8, numWoodPlans); if (cvOkToGatherGold == true) numGoldPlans = updateResourcePlans(cResourceGold, 2, 16, numGoldPlans); if (cvOkToGatherStone == true) numStonePlans = updateResourcePlans(cResourceStone, 2, 8, numStonePlans); } //aiEcho("Wood plans: "+numWoodPlans); //aiEcho("Gold plans: "+numGoldPlans); //aiEcho("Stone plans: "+numStonePlans); } //============================================================================== // updateEscrows() // // Set the econ/mil escrow balances based on age, personality and our current // villager pop compared to what we want to have. // When we lose a lot of villagers, the economy escrow is expanded and the // military escrow is reduced until the econ recovers. // BF 2/18/2012: Updated from Skirmish: // No longer changing the military escrow cap each update // allow for High Resource handling //============================================================================== mutable void updateEscrows(void) { float econPercent = 0.0; float milPercent = 0.0; float villTarget = aiPlanGetVariableInt(gVillagerMaintainPlan, cTrainPlanNumberToMaintain, 0); // How many do we want? float villCount = kbUnitCount(cMyID, gEconUnit, cUnitStateABQ); // How many do we have? float villRatio = 1.00; if (villTarget > 0.0) villRatio = villCount / villTarget; // Actual over desired. float villShortfall = 1.0 - villRatio; // 0.0 means at target, 0.3 means 30% short of target // If we hit our capped age, tilt more towards military int curAge = kbGetAge(); if (curAge == cvMaxAge) curAge = cAge4; switch(curAge) { //Adding an additonal focus on econ that fades as we reach villager target case cAge1: { if (btMilitaryInAge1 == false) econPercent = 1.0; else { econPercent = 1.00 + (0.6 * btRushBoom); // 40% rushers, 100% balanced, 100% boomers if (econPercent > 1.0) //Possible to go over 100% if AI has any boom component econPercent = 1.0; econPercent = econPercent + ((1.0 - econPercent) * villShortfall); } break; } case cAge2: { econPercent = 0.7 + (0.4 * btRushBoom); // 30% rushers, 70% balanced, 100% boomers if (econPercent > 1.0) //Possible to go over 100% if AI has a high boom component econPercent = 1.0; econPercent = econPercent + ((1.0 - econPercent) * villShortfall); break; } case cAge3: { econPercent = 0.40 + (0.30 * btRushBoom); // 10% rushers, 40% balanced, 70% boomers econPercent = econPercent + ((1.0 - econPercent) * villShortfall); break; } case cAge4: { econPercent = 0.2 + (0.1 * btRushBoom); // 10% rushers, 20% balanced, 30% boomers econPercent = econPercent + ((1.0 - econPercent) * villShortfall); break; } } //aiEcho("Econ: " + econPercent + " Mil: " + (1.0 - econPercent) + ". (btRushBoom = "+btRushBoom); if(btHighStartingResources) { econPercent = 0.2; } milPercent = 1.0 - econPercent; gEscrowEconPercent = econPercent; gEscrowMiliPercent = milPercent; //aiEcho("Updating escrows:"); //aiEcho(" Econ: "+econPercent); //aiEcho(" Mil: "+milPercent); //float econResourceValue = 800 + (600 * btRushBoom); kbEscrowSetPercentage(cEconomyEscrowID, cResourceFood, econPercent); kbEscrowSetPercentage(cEconomyEscrowID, cResourceWood, econPercent); kbEscrowSetPercentage(cEconomyEscrowID, cResourceGold, econPercent); kbEscrowSetPercentage(cEconomyEscrowID, cResourceStone, econPercent); kbEscrowSetPercentage(cEconomyEscrowID, cResourceSkillPoints, 0.0); // Set escrow caps based on forecasts instead of gueesed values kbEscrowSetCap(cEconomyEscrowID, cResourceFood, xsArrayGetFloat(gEconForecasts, cResourceFood)); kbEscrowSetCap(cEconomyEscrowID, cResourceWood, xsArrayGetFloat(gEconForecasts, cResourceWood)); kbEscrowSetCap(cEconomyEscrowID, cResourceGold, xsArrayGetFloat(gEconForecasts, cResourceGold)); kbEscrowSetCap(cEconomyEscrowID, cResourceStone, xsArrayGetFloat(gEconForecasts, cResourceStone)); //kbEscrowSetCap(cEconomyEscrowID, cResourceFood, econResourceValue); //kbEscrowSetCap(cEconomyEscrowID, cResourceWood, econResourceValue); //kbEscrowSetCap(cEconomyEscrowID, cResourceGold, econResourceValue); //kbEscrowSetCap(cEconomyEscrowID, cResourceStone, econResourceValue); kbEscrowSetPercentage(cMilitaryEscrowID, cResourceFood, milPercent); kbEscrowSetPercentage(cMilitaryEscrowID, cResourceWood, milPercent); kbEscrowSetPercentage(cMilitaryEscrowID, cResourceGold, milPercent); kbEscrowSetPercentage(cMilitaryEscrowID, cResourceStone, milPercent); kbEscrowSetPercentage(cMilitaryEscrowID, cResourceSkillPoints, 0.0); // Set escrow caps based on forecasts instead of gueesed values //kbEscrowSetCap(cMilitaryEscrowID, cResourceFood, xsArrayGetFloat(gMiliForecasts, cResourceFood)); //kbEscrowSetCap(cMilitaryEscrowID, cResourceWood, xsArrayGetFloat(gMiliForecasts, cResourceWood)); //kbEscrowSetCap(cMilitaryEscrowID, cResourceGold, xsArrayGetFloat(gMiliForecasts, cResourceGold)); //kbEscrowSetCap(cMilitaryEscrowID, cResourceStone, xsArrayGetFloat(gMiliForecasts, cResourceStone)); //kbEscrowSetCap(cMilitaryEscrowID, cResourceFood, 600); //kbEscrowSetCap(cMilitaryEscrowID, cResourceWood, 400); //kbEscrowSetCap(cMilitaryEscrowID, cResourceGold, 800); //kbEscrowSetCap(cMilitaryEscrowID, cResourceStone, 600); } //============================================================================== // updateVillagerCounts() // // Set the villager maintain plan according to age and our behavior traits. // BF 2/18/2012: Updated from Skirmish: // Allows for Egypt to train villagers while it ages up //============================================================================== mutable void updateVillagerCounts(void) { int age = kbGetAge(); if(agingUp()) { age = age + 1; } int normalTarget = xsArrayGetInt(gTargetVillagerCounts, age); //aiEcho("Now maintaining " + normalTarget + " vills"); aiPlanSetVariableInt(gVillagerMaintainPlan, cTrainPlanNumberToMaintain, 0, normalTarget); } //============================================================================== // updateEcon(int mode, int value) // // Performs top-level economic analysis and direction. Generally called // by the updateEconRule, it can be called directly for special-event processing. // updateEconRule calls it with default parameters, directing it to do a full // reanalysis. //============================================================================== void updateEcon() { static bool first = true; // Wait to start the econ so we can find stuff if (first == true) { initGatherGoal(); first = false; } // Monitor main base supply of food, activate farming when resources run low if (gTimeToFarm == false) { updateNeedToFarm(); } // Update forecasts for economic and military expenses. Set resource // exchange rates. updateForecasts(); // Set desired gatherer ratios. Spread them out per base, set per-base // resource breakdowns. updateGatherers(); // Update our villager maintain targets, based on age, personality. updateVillagerCounts(); // Maintain escrow balance based on age, personality, actual vs. desired villager pop. updateEscrows(); } //============================================================================== // rule updateEconRule // // This rule calls the econMaster() function on a regular basis. The // function is separate so that it may be called with a parameter for // unscheduled processing based on unexpected events. // // BF 2/18/2012: Updated from Skirmish: // extended minInterval //============================================================================== rule updateEconRule inactive group econ minInterval 9 { updateEcon(); } //============================================================================== // checkAndBuildEconBuilding() // // If there are less than count of the given building type, build one. // Return true if anything was built, //============================================================================== bool checkAndBuildEconBuilding(int count=1, int buildingType=-1, int escrowID=cEconomyEscrowID) { int planID = -1; if (buildingType == -1) return(false); // Always have a market built planID = aiPlanGetIDByTypeAndVariableType(cPlanBuild, cBuildPlanBuildingTypeID, buildingType); if (planID >= 0) return(false); if (kbUnitCount(cMyID, buildingType, cUnitStateAlive) < count) { createSimpleBuildPlan(buildingType, 1, 96, true, escrowID, kbBaseGetMainID(cMyID), 1); return (true); } return(false); } //============================================================================== // rule econBuildMonitor // // Make sure the appropriate econ buildings are built in each age //============================================================================== rule econBuildMonitor inactive group econ mininterval 1 { bool stop=false; /*if (cvOKToBuild == false) return;*/ //aiEcho("Building stuff!"); // Make sure we're good on pop //if (kbGetPopCap() - kbGetPop() < 10) //return; // Build any buildings in the building list int i=0; for (i = 0; < gMaxBuildings) { int buildingID = xsArrayGetInt(gBuildings, i); int buildingCount = xsArrayGetInt(gBuildingCount, i); int buildingEscrow = xsArrayGetInt(gBuildingEscrow, i); bool buildBuilding = xsArrayGetBool(gBuildingBuild, i); int buildingAgeState = xsArrayGetInt(gBuildingAgeState, i); if (buildingID == -1) break; if(buildingEscrow != cEconomyEscrowID) continue; if (buildBuilding == false) continue; //aiEcho("" + kbUnitGetProtoName(buildingID) + " " + buildingAgeState + " vs. " + getAgeState()); if (buildingAgeState > getAgeState()) continue; checkAndBuildEconBuilding(buildingCount, buildingID, buildingEscrow); } } //============================================================================== // rule HouseMonitor // // Check for the civ's max houses, and stop if we have max. Otherwise, build // enough houses to house our current age's pop. //============================================================================== rule houseMonitor inactive group econ minInterval 1 { // Don't build if we're not allowed if (cvOkToBuildHouses == false) return; // Don't build if we're at limit. if (kbGetBuildLimit(cMyID, gHouse) <= kbUnitCount(cMyID, gHouse, cUnitStateAlive)) return; int houseBuildPlanID = aiPlanGetIDByTypeAndVariableType(cPlanBuild, cBuildPlanBuildingTypeID, gHouse); if ( (houseBuildPlanID < 0) && ( (kbGetPopCap()-kbGetPop()) < (5 + (8*kbGetAge())) ) ) // None in progress, and pop headroom < 15 in cAge1, etc. { // Start a new one createSimpleBuildPlan(gHouse, 1, 95, true, cEconomyEscrowID, kbBaseGetMainID(cMyID), 1); //aiEcho("Starting a new house build plan."); } } //============================================================================== // rule ageUpgradeCheck // // Decide if it makes sense to upgrade an age. When it is, shift escrow accounts // to 0, set the upgrade account to 100% and reallocate. This causes everything // to be applied to aging up until the upgrade completes. When the next age // triggers, the age monitor rule resets the escrows. // BF 2/18/2012: Updated from Skirmish: // adds check that handles having high resources // raises the default intervale from 10 to 37 (different usage cases change this) // sets the rule as active to begin with // ungroups the rule from group econ // MC 5/7/2012: Instead of referencing specific CivIDs, we now assume that // AgeXBuilding will take precedence over an AgeXTech. //============================================================================== rule ageUpgradeCheck active minInterval 5 { //aiEcho("Ageup check"); // We're all aged up, stop if (kbGetAge() >= cAge4) { //aiEcho("At age 4"); xsDisableSelf(); return; } // We've reached the age cap. Keep running though, as the designers might // change that cap. if (kbGetAge() >= cvMaxAge) { //aiEcho("At age cap"); return; } // Are we already aging up? if (gAgeUpgradePlan >= 0) { //aiEcho("Age up plan is running"); if (aiPlanGetState(gAgeUpgradePlan) >= 0) { //aiEcho("and is good, stopping check"); // Plan is good, bail return; } else { //aiEcho("Plan not running, restarting"); // Plan isn't running, so reset and try again. aiPlanDestroy(gAgeUpgradePlan); gAgeUpgradePlan = -1; } } int curCiv = kbGetCiv(); int ageTech = -1; int houseCount = -1; int ageBldg = -1; // check for both building and tech age up mechanics switch (kbGetAge()) { case cAge1: { ageBldg = gAge2Building; ageTech = gAge2Tech; houseCount = gAge2HouseCount; break; } case cAge2: { ageBldg = gAge3Building; ageTech = gAge3Tech; houseCount = gAge3HouseCount; break; } case cAge3: { ageBldg = gAge4Building; ageTech = gAge4Tech; houseCount = gAge4HouseCount; break; } // We shouldn't get here, but just in case case cAge4: { return; } } if(bDebugMessages == true){ aiEcho("ageUpgradeCheck: Current Age Building " + ageBldg + ", Current Age Tech " + ageTech); } // Can we afford to upgrade (and do we have the tech)? if (ageBldg != -1) { if (kbCanAffordUnit(ageBldg, cEmergencyEscrowID)) { int currentVillCount2 = kbUnitCount(cMyID, gEconUnit, cUnitStateAlive); int targetVillCount2 = aiPlanGetVariableInt(gVillagerMaintainPlan, cTrainPlanNumberToMaintain, 0); int villTargetPercentReached2 = (currentVillCount2 * 100) / targetVillCount2; //aiEcho("I have "+currentVillCount2+" vills of my target "+targetVillCount2+" giving a percent of "+villTargetPercentReached2+"%"); //## EDIT: ACE AGE2 // force ai to make the age 2 upgrade if we have enough res if(ageBldg == gAge2Building){ if (gAgeUpgradePlan >= 0 && aiPlanGetState(gAgeUpgradePlan) >= 0){ return; } int wood = kbResourceGet(cResourceWood);// Gets Current Amount of Resource like gold,wood etc int food = kbResourceGet(cResourceFood);// Gets Current Amount of Resource like gold,wood etc if(wood > 400 && food > 400){ // use 3 villagers to age 2 gAgeUpgradePlan = createSimpleBuildPlan(ageBldg, 1, 99, false, cEmergencyEscrowID, kbBaseGetMainID(cMyID), 3); }else if(wood >= 200 && food >= 200 && currentVillCount2 > 13){ // age 2 at 13 vills gAgeUpgradePlan = createSimpleBuildPlan(ageBldg, 1, 99, false, cEmergencyEscrowID, kbBaseGetMainID(cMyID), 1); } } // EDIT: Age3 egypt if(ageBldg == gAge3Building){ if(currentVillCount2 > 30){ villTargetPercentReached2 = 91; } } // EDIT: Age4 egypt if(ageBldg == gAge4Building){ if(currentVillCount2 > 50){ villTargetPercentReached2 = 91; } } //TD: check if we have met enough (90%) of the villager target level if (villTargetPercentReached2 >= 90 || (btHighStartingResources && kbGetAge() == cAge1)) { if (gAgeUpgradePlan >= 0 && aiPlanGetState(gAgeUpgradePlan) >= 0){ return; } gAgeUpgradePlan = createSimpleBuildPlan(ageBldg, 1, 99, false, cEmergencyEscrowID, kbBaseGetMainID(cMyID), 5); if(bDebugMessages == true){ aiEcho("Creating plan #"+gAgeUpgradePlan+" to get age upgrade with building "+kbGetUnitTypeName(ageBldg)); } } } } else if (ageTech != -1) { if (kbCanAffordTech(ageTech, cEmergencyEscrowID)) { //aiEcho("Can afford ageup"); if ((kbTechGetStatus(ageTech) == cTechStatusObtainable) && (gAgeUpgradePlan < 0)) { //TD: adding a check for if we have built enough villagers to age up safely int currentVillCount = kbUnitCount(cMyID, gEconUnit, cUnitStateAlive); int targetVillCount = aiPlanGetVariableInt(gVillagerMaintainPlan, cTrainPlanNumberToMaintain, 0); int villTargetPercentReached = (currentVillCount * 100) / targetVillCount; //aiEcho("I have "+currentVillCount+" vills of my target "+targetVillCount+" giving a percent of "+villTargetPercentReached+"%"); int currentHouseCount = kbUnitCount(cMyID, gHouse, cUnitStateAlive); int xwood = kbResourceGet(cResourceWood);// Gets Current Amount of Resource like gold,wood etc int xfood = kbResourceGet(cResourceFood);// Gets Current Amount of Resource like gold,wood etc // EDIT: ACE, celts handle if (curCiv == gCelticCivID && ageTech == gAge2Tech && xfood >= 400){ if(currentHouseCount < 3){ // enforce house placement gEmergencyHouseBuildPlan = createSimpleBuildPlan(gHouse, 1, 95, true, cEconomyEscrowID, kbBaseGetMainID(cMyID), 1); gEmergencyHouseBuildPlan = createSimpleBuildPlan(gHouse, 1, 95, true, cEconomyEscrowID, kbBaseGetMainID(cMyID), 1); }else{ villTargetPercentReached = 91; // makes it over 90% to enforce age up } } // we have alot of res, age up if (curCiv != gCelticCivID && ageTech == gAge2Tech && xfood >= 400 && xwood >= 400){ villTargetPercentReached = 91; } if (curCiv != gCelticCivID && ageTech == gAge2Tech && xfood > 200 && xwood > 200 && currentVillCount > 13){ villTargetPercentReached = 91; } // EDIT: Age3 if(ageTech == gAge3Tech){ if(currentVillCount2 > 30){ villTargetPercentReached = 91; } } // EDIT: Age4 if(ageTech == gAge4Tech){ if(currentVillCount2 > 50){ villTargetPercentReached = 91; } } //TD: check if we have met enough (90%) of the villager target level if (villTargetPercentReached >= 90 || (btHighStartingResources && kbGetAge() == cAge1)) { //SEM: If we are not Celts or we have enough houses to age up, then do so if (curCiv != gCelticCivID || currentHouseCount >= houseCount) { gAgeUpgradePlan = createSimpleResearchPlan(ageTech, -1, cEmergencyEscrowID, 99); if(bDebugMessages == true){ aiEcho("Creating plan #"+gAgeUpgradePlan+" to get age upgrade with tech "+kbGetTechName(ageTech)); } } //SEM: If Celts do not have enough houses and are ready to age up, then make housing a priority else if (cvOkToBuildHouses && curCiv == gCelticCivID && (gEmergencyHouseBuildPlan == -1 || aiPlanGetState(gEmergencyHouseBuildPlan) == -1)) { gEmergencyHouseBuildPlan = createSimpleBuildPlan(gHouse, 1, 95, true, cEconomyEscrowID, kbBaseGetMainID(cMyID), 1); } } } } } } //============================================================================== // rule exploreMonitor // // Used to correctly implement changes in cvOkToExplore. // If it's off, make sure the explore plan is killed. // If it's on, make sure the explore plan is active. // BF 2/18/2012: Updated from Skirmish: // These edits make sure we grab the scout, since // we have starting army units that can be considered. // also adjusts the interval to a prime number //============================================================================== rule exploreMonitor inactive group econ minInterval 13 { const int cExploreModeStart = 0; // Initial setting, when first starting out const int cExploreModeStaff = 1; // Restaffing the plan, active for 10 seconds to let the plan grab 1 more unit. const int cExploreModeExplore = 2; // Normal...explore until this unit dies, check again in 5 minutes. static int exploreMode = cExploreModeStart; static int nextStaffTime = -1; // Prevent the explore plan from constantly sucking in units. //aiEcho("ExploreMonitor " + gLandExplorePlan + " " + cvOkToExplore); // Check for a failed plan if ( (gLandExplorePlan >= 0) && (aiPlanGetState(gLandExplorePlan) < 0) ) { // Somehow, the plan has died. Reset it to start up again if allowed. //aiEcho("No explore plan, resetting"); gLandExplorePlan = -1; exploreMode = cExploreModeStart; nextStaffTime = -1; } // SEM: Check to see if this enemy has no more Villagers or Town Centers. int VillCount = kbUnitCount(cMyID, gEconUnit, cUnitStateAlive); int TCCount = kbUnitCount(cMyID, gTownCenter, cUnitStateAlive); // SEM: If the enemy has no more Vills and TCs, shut down the explore plan and keep it from retriggering. if ( (VillCount == 0) && (TCCount == 0 ) ) { //aiEcho("SHUTTING DOWN SCOUTING"); cvOkToExplore = false; if (aiPlanGetState(gLandExplorePlan) > 0) { aiPlanDestroy(gLandExplorePlan); } } // SEM: Otherwise, the enemy has it, so go to the default status for the AI scouting behavior. // We ask the game about the default status in case this enemy was never supposed to scout. else { //aiEcho("TURNING ON SCOUTING"); cvOkToExplore = aiGetOKToScout(); } // First, check the control variable and react appropriately if ( cvOkToExplore == true ) { if (aiPlanGetActive(gLandExplorePlan) == false) if (gLandExplorePlan >= 0) aiPlanSetActive(gLandExplorePlan); // Reactivate if we were shut off switch(exploreMode) { case cExploreModeStart: { if (aiPlanGetState(gLandExplorePlan) < 0) { // Need to create it. //aiEcho("Creating explore plan"); gLandExplorePlan=aiPlanCreate("Land Explore", cPlanExplore); aiPlanSetDesiredPriority(gLandExplorePlan, 100); aiPlanAddUnitType(gLandExplorePlan, cUnitTypeUnitTypeScout1, 1, 2, 2); aiPlanSetVariableBool(gLandExplorePlan, cExplorePlanOkToGatherNuggets, 0, false); exploreMode = cExploreModeStaff; nextStaffTime = xsGetTime() + 60000; // One minute from now, let it get another soldier if it loses this one. } aiPlanSetEscrowID(gLandExplorePlan, cEconomyEscrowID); aiPlanSetBaseID(gLandExplorePlan, kbBaseGetMainID(cMyID)); aiPlanSetInitialPosition(gLandExplorePlan, kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID))); aiPlanSetVariableBool(gLandExplorePlan, cExplorePlanDoLoops, 0, true); aiPlanSetVariableInt(gLandExplorePlan, cExplorePlanExploreDistance, 0, 300); aiPlanSetVariableInt(gLandExplorePlan, cExplorePlanPointsInLoop, 0, 8); aiPlanSetVariableBool(gLandExplorePlan, cExplorePlanAvoidingAttackedAreas, 0, false); aiPlanSetActive(gLandExplorePlan); //aiEcho("Starting explore plan"); break; } case cExploreModeStaff: { // We've been staffing for 10 seconds, set no more units to true aiPlanAddUnitType(gLandExplorePlan, cUnitTypeLogicalTypeExplorer, 1, 1, 1); aiPlanSetNoMoreUnits(gLandExplorePlan, true); exploreMode = cExploreModeExplore; //aiEcho("Setting the explore plan to 'noMoreUnits'"); break; } case cExploreModeExplore: { // See if we're allowed to add another unit if (xsGetTime() > nextStaffTime) { aiPlanSetNoMoreUnits(gLandExplorePlan, false); // Let it grab a unit //aiEcho("Setting the explore plan to grab a unit if needed."); nextStaffTime = xsGetTime() + 60000; exploreMode = cExploreModeStaff; } break; } } } else // cvOkToExplore = false { aiPlanSetActive(gLandExplorePlan, false); } } //============================================================================== // updateMaxGatherDistance() // // BF 2/18/2012: Updated from Skirmish: // reducing distances across the board // MC 6/18/2012: Turns out the MaximumResourceDistance for a base was being multiplied by // 2.5 under the hood. That has been removed, but for now, we will use the // multiplied value. //============================================================================== mutable void updateMaxGatherDistance() { // Update our max gather distance //EDIT: ACE float offset = -5; int age = kbGetAge(); if (age == cvMaxAge) age = cAge4; switch (age) { case cAge1: { kbBaseSetMaximumResourceDistance(cMyID, kbBaseGetMainID(cMyID), (70.0 + offset) * 2.5); aiSetMaxGatherDistance(70.0 + offset ); break; } case cAge2: { kbBaseSetMaximumResourceDistance(cMyID, kbBaseGetMainID(cMyID), (100.0 + offset) * 2.5); aiSetMaxGatherDistance(100.0 + offset); break; } case cAge3: { kbBaseSetMaximumResourceDistance(cMyID, kbBaseGetMainID(cMyID), (130.0 + offset) * 2.5); aiSetMaxGatherDistance(130.0 + offset); break; } case cAge4: { // Unlimited //aiEcho("GATHER DISTANCE : " + kbGetMapXSize() / 2); //kbBaseSetMaximumResourceDistance(cMyID, kbBaseGetMainID(cMyID), kbGetMapXSize() / 2); //aiSetMaxGatherDistance(kbGetMapXSize() / 2); kbBaseSetMaximumResourceDistance(cMyID, kbBaseGetMainID(cMyID), (160.0 +offset) * 2.5); aiSetMaxGatherDistance(160.0 + offset); break; } } aiSetExploreDangerThreshold(150.0 + offset); } //============================================================================== // rule researchEconUpgrades // // Check to see if we can research any econ upgrades for the units we're building. //============================================================================== // EDIT: Ace, old 30 rule researchEconUpgrades inactive group econ minInterval 15 { if (cvOKToResearchEcon == false) { xsDisableSelf(); return; } int techToGet = -1; float lowestCost = 1000000.0; static int gatherTargets = -1; // Array to hold the list of things we gather from, i.e. mill, tree, etc. static int gatherTargetTypes = -1; // Array. If gatherTargets(x) == mill, then gatherTargetTypes(x) = cResourceFood. int target = -1; // Index used to step through arrays static int startTime = -1; // Time last plan was started, to make sure we're not waiting on an obsolete tech. if (gatherTargets < 0) // Array not initialized { // Set up our list of target units (what we gather from) and their resource categories. gatherTargets = xsArrayCreateInt(6, -1, "Gather Targets"); gatherTargetTypes = xsArrayCreateInt(6, -1, "Gather Target Types"); xsArraySetInt(gatherTargets, 0, gFarmUnit); // Farms generate food xsArraySetInt(gatherTargetTypes, 0, cResourceFood); xsArraySetInt(gatherTargets, 1, cUnitTypeTree); // Trees generate wood xsArraySetInt(gatherTargetTypes, 1, cResourceWood); xsArraySetInt(gatherTargets, 2, cUnitTypeMineStone); xsArraySetInt(gatherTargetTypes, 2, cResourceStone); xsArraySetInt(gatherTargets, 3, cUnitTypeHuntable); // Huntables generate food xsArraySetInt(gatherTargetTypes, 3, cResourceFood); xsArraySetInt(gatherTargets, 4, cUnitTypeMineGold); // GoldMine generate gold xsArraySetInt(gatherTargetTypes, 4, cResourceGold); xsArraySetInt(gatherTargets, 5, cUnitTypeBerryBush); // berry generate food xsArraySetInt(gatherTargetTypes, 5, cResourceFood); // EDIT: ACE, old cResourceGold } startTime = -1; int techID = -1; // The cheapest tech for the current target unit type float rawCost = -1.0; // The cost of the upgrade float relCost = -1.0; // The cost, relative to some estimate of the number of gatherers float numGatherers = -1.0; // Number of gatherers assigned to the resource type (i.e food) /* Step through the array of gather targets. For each, calculate the cost of the upgrade relative to the number of gatherers that would benefit. Choose the one with the best payoff. */ //TD: Go through techs and look for the tech that gives a better result than building another villager int currentVillCount = kbUnitCount(cMyID, gEconUnit, cUnitStateAlive); int villTarget = xsArrayGetInt(gTargetVillagerCounts, kbGetAge()); for (target=0; < 6) { if (xsArrayGetInt(gatherTargets, target) < 0) // No target specified continue; techID = kbTechTreeGetCheapestEconUpgrade(xsArrayGetInt(gatherTargets, target)); if (techID < 0) // No tech available for this target type continue; rawCost = kbGetTechAICost(techID); if (rawCost == 0.0) rawCost = -1.0; // Percent of gatherers assigned to this resource, times the number of econ units. numGatherers = aiGetResourceGathererPercentage(xsArrayGetInt(gatherTargetTypes, target), cRGPActual) * kbUnitCount(cMyID, gEconUnit, cUnitStateAlive); // Calculate the relative cost if (numGatherers > 0.0) { //TD: modified this so that if we are not at max villagers we will only get techs that are a better value than building villagers if (currentVillCount < villTarget) { relCost = (numGatherers * .1) * 60; //TD: assuming 10% tech effect and a 60 vill cost (cost of vill + cost of 1/5 a house) if (rawCost > relCost) { //aiEcho("**** rejected upgrade tech(new)with calculated value of " + relCost+" with a cost of "+rawCost); relCost = -1.0; } //else //aiEcho("**** valid upgrade tech(new)with calculated value of " + relCost+" with a cost of "+rawCost); } else { relCost = rawCost / numGatherers; if (relCost >= 40) //TD: techs that cost more than 40 per vill we discard relCost = -1.0; //else //aiEcho("**** valid upgrade tech(old)with calculated cost per cill is " + relCost); } } else{ relCost = -1.0; } // We now have the relative cost for the cheapest tech that gathers from this target type. // See if it's > 0, and the cheapest so far. If so, save the stats, as long as it's obtainable. if ((techID >= 0) && (relCost < lowestCost) && (relCost > 0.0) && (kbTechGetStatus(techID) == cTechStatusObtainable)) { lowestCost = relCost; techToGet = techID; } } if (techToGet >= 0) // We have a tech { // If a plan has been running for 3 minutes... if ((startTime > 0) && (xsGetTime() > (startTime + 180000))) { // If it's still the tech we want, reset the start time counter and quit out. Otherwise, kill it. if (aiPlanGetVariableInt(gEconUpgradePlan, cProgressionPlanGoalTechID, 0) == techToGet) { startTime = xsGetTime(); return; } else { if(bDebugMessages == true){ aiEcho("***** Destroying econ upgrade plan # "+gEconUpgradePlan+" because it has been running more than 3 minutes."); } aiPlanDestroy(gEconUpgradePlan); } } // Plan doesn't exist, or we just killed it due to timeout.... gEconUpgradePlan = aiPlanCreate("Econ upgrade tech " + techToGet, cPlanProgression); aiPlanSetVariableInt(gEconUpgradePlan, cProgressionPlanGoalTechID, 0, techToGet); aiPlanSetDesiredPriority(gEconUpgradePlan, 74); // Edit: ace , old 75 aiPlanSetEscrowID(gEconUpgradePlan, cEconomyEscrowID); aiPlanSetBaseID(gEconUpgradePlan, kbBaseGetMainID(cMyID)); aiPlanSetActive(gEconUpgradePlan); startTime = xsGetTime(); //aiEcho("**** econ plan cost per vill"+lowestCost); if(bDebugMessages == true){ aiEcho("**** Creating upgrade plan for "+kbGetTechName(techToGet)+" is " + gEconUpgradePlan); } } } //============================================================================== // getHerdables // // BF 2/18/2012: Updated from Skirmish: // manage herdables // Find all the herdables we have and send them to the TC. //============================================================================== rule getHerdables active minInterval 9 { if (cvOkToHerd == false) { xsDisableSelf(); return; } int base = kbBaseGetMainID(cMyID); // return early if we don't have a valid base if(base == -1) { return; } if(gHerdableQuery == -1) { gHerdableQuery = kbUnitQueryCreate("Herdable Query"); kbUnitQuerySetPlayerID(gHerdableQuery, cMyID); kbUnitQuerySetUnitType(gHerdableQuery, cUnitTypeHerdable); kbUnitQuerySetState(gHerdableQuery, cUnitStateAlive); } kbUnitQueryResetResults(gHerdableQuery); int count = kbUnitQueryExecute(gHerdableQuery); //aiEcho("we have " + count + " herdables"); int i = 0; for(i = 0; < count) { // get the first one int ID = kbUnitQueryGetResult(gHerdableQuery, i); vector center = kbBaseGetLocation(cMyID, base); vector spot = cInvalidVector; spot = xsVectorSetX(spot, xsVectorGetX(center) - 10); spot = xsVectorSetZ(spot, xsVectorGetZ(center)); aiTaskUnitMove(ID, spot); } } //============================================================================== // trainHerdables // // BF 2/18/2012: Updated from Skirmish: // adding this rule for training herdables (civ specific) //============================================================================== rule trainHerdables active minInterval 11 { if(kbGetCiv() == gCelticCivID) { int deer = kbFindUnit(cUnitTypeHerdable); if(deer == -1) { if(bDebugMessages == true){ aiEcho("trainHerdables: herdable ID is invalid"); } xsDisableSelf(); return; } createSimpleMaintainPlan(deer, 5, true, kbBaseGetMainID(cMyID), 1); } xsDisableSelf(); } //============================================================================== // updateCaravanCount // // BF 2/18/2012: Updated from Skirmish: // update the caravan count very infrequently //============================================================================== // EDIT: Ace , old 240 rule updateCaravanCount active minInterval 1 { if (cvOkToCaravan == false) { xsDisableSelf(); return; } // only add caravans once in age 2. if(kbGetAge() >= cAge2) { gCaravanCount = gCaravanCount + 1; // cap at 6 under normal circumstances if(gCaravanCount > gCaravanCountNorm) { gCaravanCount = gCaravanCountNorm; } } } //============================================================================== // makeCaravansWork // // BF 2/18/2012: Updated from Skirmish: // Create the caravan maintain plan and find all the // idle caravans we have and make them trade with an ally TC or our TC // if we have no allies. //============================================================================== // EDIT: ACE old 29 rule makeCaravansWork active minInterval 3 { if (cvOkToCaravan == false) { xsDisableSelf(); return; } if(gCaravanMaintainPlan == -1) { gCaravanMaintainPlan = createSimpleMaintainPlan(kbFindUnit(cUnitTypeUnitTypeCaravan1), 2, true, kbBaseGetMainID(cMyID), 1); } if(cOutOfGold == true) { aiPlanSetVariableInt(gCaravanMaintainPlan, cTrainPlanNumberToMaintain, 0, gCaravanCountNoGold); } else { aiPlanSetVariableInt(gCaravanMaintainPlan, cTrainPlanNumberToMaintain, 0, gCaravanCount); } if(gCaravanQuery == -1) { gCaravanQuery = kbUnitQueryCreate("Caravan Query"); kbUnitQuerySetPlayerID(gCaravanQuery, cMyID); kbUnitQuerySetUnitType(gCaravanQuery, cUnitTypeUnitTypeCaravan1); kbUnitQuerySetState(gCaravanQuery, cUnitStateAlive); } kbUnitQueryResetResults(gCaravanQuery); int count = kbUnitQueryExecute(gCaravanQuery); if(count < 1) return; int marketID = getUnit(kbFindBuilding(cUnitTypeUnitTypeBldgMarket), cMyID, cUnitStateAlive); if(marketID == -1) return; vector marketLoc = kbUnitGetPosition(marketID); //aiEcho("MARKET IS AT " + marketLoc); vector dist = xsVectorSet(xsVectorGetX(marketLoc) - xsVectorGetX(kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID))), 0, xsVectorGetZ(marketLoc) - xsVectorGetZ(kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID)))); float closest = xsVectorLength(dist); //aiEcho("DISTANCE FROM TOWN CENTER" + closest); int tcID = -1; // 7/19/2012 MCC: Only allowing trading with allies if we aren't on a water map if(isCurrentMapNaval() == false) { for (i = 0; closest && length < 250.0) { int temp = getUnitByLocation(cUnitTypeUnitTypeBldgTownCenter, i, cUnitStateAlive, allyLoc, 20, true); if(temp != -1) { tcID = temp; closest = length; //aiEcho("TC " + tcID + " IS THIS FAR AWAY " + closest); } } } } } // no allied TCs if (tcID == -1) { if(gEconTCQuery == -1) { gEconTCQuery = kbUnitQueryCreate("AI Econ TC Query"); kbUnitQuerySetIgnoreKnockedOutUnits(gEconTCQuery, true); kbUnitQuerySetPlayerID(gEconTCQuery, cMyID); kbUnitQuerySetUnitType(gEconTCQuery, gTownCenter); kbUnitQuerySetState(gEconTCQuery, cUnitStateAlive); } kbUnitQueryResetResults(gEconTCQuery); int tcs = kbUnitQueryExecute(gEconTCQuery); if(tcs < 1) return; tcID = kbUnitQueryGetResult(gEconTCQuery, 0); } for(i = 0; < count) { // get the first one int ID = kbUnitQueryGetResult(gCaravanQuery, i); if(kbUnitGetTargetUnitID(ID) == tcID) { //aiEcho("ALREADY ON THIS TC"); continue; } aiTaskUnitWork(ID, tcID); } } //============================================================================== // checkForGold // // BF 2/18/2012: Updated from Skirmish: // See if we are out of gold mines on the map, so we can hopefully prevent the // AI from stalling (by adding more emphasis on caravans) //============================================================================== // EDIT: Ace gold, old 29 rule checkForGold active minInterval 10 { // Nope, check any secondary bases int numBases = kbBaseGetNumber(cMyID); int i = 0; int baseID = -1; int gold = 0; for (i = 0; < numBases) { baseID = kbBaseGetIDByIndex(cMyID, i); int count = kbGetNumberValidResources(baseID, cResourceGold, cAIResourceSubTypeEasy, -1.0); gold = gold + count; } // EDIT: ACE int currentVillCount = kbUnitCount(cMyID, gEconUnit, cUnitStateAlive); int _max_vills = 39; // only do this if user has more than 30 villagers if(gCaravanCount < gCaravanCountNorm && currentVillCount > _max_vills){ // force the ai to make caravans gold = 0; } if(gold == 0) { cOutOfGold = true; if(bDebugMessages == true){ aiEcho("checkForGold: No gold on the map, flagging that gold is needed"); } } else { cOutOfGold = false; } //aiEcho("We have a total of " + gold + " gold mines near our " + numBases + " bases"); } //============================================================================== // repairBuilding // // BF 2/18/2012: Updated from Skirmish: // find the best building to repair and try to get a villagers to repair it. //============================================================================== rule repairBuilding active minInterval 22 { if (cvOkToRepair == false) { xsDisableSelf(); return; } int building = kbFindBestBuildingToRepair(kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID)), 50, 0.9, -1); if(building < 0) { //aiEcho("no building to repair"); return; } int vil = getUnitByLocation(gEconUnit, cMyID, cUnitStateAlive, kbUnitGetRealPosition(building), 30, false); if(vil < 0) { //aiEcho("couldn't find villager to repair"); return; } aiTaskUnitRepair(vil, building); if(bDebugMessages == true){ aiEcho("villager " + vil + " is repairing building " + building); } } //============================================================================== // makePriestessWork // // BF 2/18/2012: Updated from Skirmish: // Get Egypt's starting Priestess of Ra and have her empower the TC. //============================================================================== void makePriestessWork() { int raQuery = kbUnitQueryCreate("Starting Priestess Query"); kbUnitQuerySetPlayerID(raQuery, cMyID); kbUnitQuerySetUnitType(raQuery, kbFindUnit(cUnitTypeUnitTypePriest3)); kbUnitQuerySetState(raQuery, cUnitStateAlive); kbUnitQueryResetResults(raQuery); int count = kbUnitQueryExecute(raQuery); if(count < 1) return; // get the first one int raID = kbUnitQueryGetResult(raQuery, 0); if(gEconTCQuery == -1) { gEconTCQuery = kbUnitQueryCreate("AI Econ TC Query"); kbUnitQuerySetIgnoreKnockedOutUnits(gEconTCQuery, true); kbUnitQuerySetPlayerID(gEconTCQuery, cMyID); kbUnitQuerySetUnitType(gEconTCQuery, gTownCenter); kbUnitQuerySetState(gEconTCQuery, cUnitStateAlive); } kbUnitQueryResetResults(gEconTCQuery); count = kbUnitQueryExecute(gEconTCQuery); if ((count < 1)) return; int tcID = kbUnitQueryGetResult(gEconTCQuery, 0); /*int storehouseQuery = kbUnitQueryCreate("Starting sadfjlksdjf Query"); kbUnitQuerySetPlayerID(storehouseQuery, cMyID); kbUnitQuerySetUnitType(storehouseQuery, gStorehouse); kbUnitQuerySetState(storehouseQuery, cUnitStateAlive); kbUnitQueryResetResults(storehouseQuery); count = kbUnitQueryExecute(storehouseQuery); if(count < 1) return; // get the first one int shID = kbUnitQueryGetResult(storehouseQuery, 0); aiEcho("Priestess ID :"+raID+" is empowering TC ID: "+shID);*/ aiTaskUnitEmpower(raID, tcID); } //============================================================================== // firstInitGatherers // // BF 2/18/2012: Updated from Skirmish: // Make our villagers gather sooner than the first updateEcon update. //============================================================================== rule firstInitGatherers active minInterval 1 { updateEcon(); makePriestessWork(); xsDisableSelf(); } //============================================================================== // initializeAndClearBuildingArrays // // BF 2/18/2012: Updated from Skirmish: // utility function //============================================================================== void initializeAndClearBuildingArrays() { // Create arrays if needed if (gBuildings == -1) { gBuildings = xsArrayCreateInt(gMaxBuildings, -1, "Building list"); gBuildingCount = xsArrayCreateInt(gMaxBuildings, 1, "Building count"); gBuildingEscrow = xsArrayCreateInt(gMaxBuildings, cEconomyEscrowID, "Building escrow"); gBuildingBuild = xsArrayCreateBool(gMaxBuildings, false, "Building build flag"); gBuildingAgeState = xsArrayCreateInt(gMaxBuildings, cAgeStateMid, "Building age state"); } // Clear arrays (inefficient, but saves us potential errors if something doesn't get cleared) int i=0; for (i = 0; < gMaxBuildings) { xsArraySetInt(gBuildings, i, -1); xsArraySetInt(gBuildingCount, i, 1); xsArraySetInt(gBuildingEscrow, i, cEconomyEscrowID); xsArraySetBool(gBuildingBuild, i, false); xsArraySetInt(gBuildingAgeState, i, cAgeStateMid); } }