//============================================================================== // Constants //============================================================================== //============================================================================== // Global constants //============================================================================== //============================================================================== // Global variables //============================================================================== extern int gCurrentAge = cAge1; // The age we're currently in, used by the age monitor rule extern int gPreviousAge = cAge1; // The age we were before, used by the age monitor rule extern int gCurrentCiv = -1; // The civ we're in, used for changing behavior based on civ extern int gMilitaryBuildingForInitialize = -1;// used by dedicated AIa for their primary factory type extern bool gMilitaryReady = false; //============================================================================== // Function prototypes //============================================================================== // Used in loader file to override default values, called at start of main() mutable void preInit(void) {} // Used in loader file to override initialization decisions, called at end of main() mutable void postInit(void) {} // Added to loader from tool to configure our buildings mutable void setupBuildingInfo() {} //============================================================================== // KB queries //============================================================================== extern int gTownCenterQuery = -1; //============================================================================== //rule popManager // // Set population limits based on age, difficulty and control variable settings //============================================================================== rule popManager active minInterval 15 { // TODO: Adjust due to difficulty and control variables int currentVillCount = kbUnitCount(cMyID, gEconUnit, cUnitStateAlive); int targetVillCount = xsArrayGetInt(gTargetVillagerCounts, kbGetAge()); int fishingBoatTarget = gNumFishBoats; // econ pop limit is current econ pop - vills + fishingBoats + targetVills // to account for things like preplaced econ units (aka anything which isn't military) int econLimit = (aiGetEconomyPop() - currentVillCount) + fishingBoatTarget + targetVillCount; aiSetEconomyPop(econLimit); aiSetMilitaryPop(btMilitaryCap); } //============================================================================== // updateBuildingInfo // // BF 2/18/2012: Updated from Skirmish: // continually update the building info instead of calling it only at the // start of each age. We now update building counts based on villager count. //============================================================================== rule updateBuildingInfo active minInterval 30 { setupBuildingInfo(); } //============================================================================== // rule initializeMilitaryAfterBuilding // // BF 2/18/2012: Updated from Skirmish: // allow dedicated AIs to wait for their primary // factories before starting other military tasks // this is irrelevant if btMilitaryInAge1 is true //============================================================================== rule initializeMilitaryAfterBuilding inactive minInterval 5 { if( gMilitaryBuildingForInitialize == -1 || kbUnitCount(cMyID, gMilitaryBuildingForInitialize, cUnitStateAlive) > 0) { initMilitary(); initNavy(); xsDisableSelf(); int unit = getUnit(gMilitaryBuildingForInitialize, cMyID, cUnitStateAlive); if (unit > 0) { aiEcho("initializeMilitaryAfterBuilding: now initializing military because [" + kbUnitGetName(unit) + "] was built"); } else { aiEcho("initializeMilitaryAfterBuilding: now initializing military because [" + gMilitaryBuildingForInitialize + "] was built"); } } } //============================================================================== // rule ageUpMonitor // // Watch for when we upgrade and age and update the game state accordingly // BF 2/18/2012: Updated from Skirmish: // When reaching Age 2, this override enables a rule that will check for a // specific military building before initializing military. //============================================================================== rule ageUpMonitor inactive minInterval 5 { if (kbGetAge() == gCurrentAge) return; gPreviousAge = gCurrentAge; gCurrentAge = kbGetAge(); setupBuildingInfo(); // We've hit age 2, enable the military if (gCurrentAge >= cAge2 && gMilitaryReady == false) { xsEnableRule("initializeMilitaryAfterBuilding"); xsEnableRule("militaryBuildMonitor"); gMilitaryReady = true; } updateEcon(); kbEscrowAllocateCurrentResources(); if (gLandUnitPicker != -1) setUnitPickerPreference(gLandUnitPicker); // Update preferences in case btBiasEtc vars have changed, or cvPrimaryArmyUnit has changed. btAttacksThisAge = 0; updateMilitarySize(); updateMaxGatherDistance(); // MCC: reset the house plan variable, so that next age we can make a new plan gEmergencyHouseBuildPlan = -1; // All aged up, stop watching if (gCurrentAge >= cAge4) { xsDisableSelf(); return; } } //============================================================================== // Setup event handlers for the AI //============================================================================== mutable void setupHandlers() { //Setup the resign handler aiSetHandler("resignHandler", cXSResignHandler); // Set up the age-up chat handler aiSetHandler("ageUpHandler", cXSPlayerAgeHandler); //-- set the ScoreOppHandler aiSetHandler("scoreOpportunity", cXSScoreOppHandler); // Handlers for mission start/end aiSetHandler("missionStartHandler",cXSMissionStartHandler); aiSetHandler("missionEndHandler",cXSMissionEndHandler); // Handler for WTS waves aiSetHandler("timerEffect", cXSTimerEffect); } //============================================================================== //============================================================================== void timerEffect(int numUnits=-1) { aiEcho("Waves: " + aiGetUseWaves()); // Only do this if we're using waves if (aiGetUseWaves() == true) { // Trigger an attack gHasWave = true; } } // //============================================================================== // // This function has been merged into the new function PrepareMainBase - bfricks 3/13/2012 // // OLD: createMainBase() // // // // If we don't have a TC, setup a base at the given location // //============================================================================== // int createMainBase(vector mainVec=cInvalidVector) // { // aiEcho("Creating main base at "+mainVec); // if (mainVec == cInvalidVector) // return(-1); // // int oldMainID = kbBaseGetMainID(cMyID); // int i = 0; // // int count=-1; // int buildingID = -1; // string buildingName = ""; // if (gMainBaseBuildingQueryID < 0) // { // gMainBaseBuildingQueryID=kbUnitQueryCreate("NewMainBaseBuildingQuery"); // kbUnitQuerySetIgnoreKnockedOutUnits(gMainBaseBuildingQueryID, true); // } // // //Define a query to get all matching units // if (gMainBaseBuildingQueryID != -1) // { // kbUnitQuerySetPlayerRelation(gMainBaseBuildingQueryID, -1); // kbUnitQuerySetPlayerID(gMainBaseBuildingQueryID, cMyID); // // kbUnitQuerySetUnitType(gMainBaseBuildingQueryID, cUnitTypeBuilding); // kbUnitQuerySetState(gMainBaseBuildingQueryID, cUnitStateABQ); // kbUnitQuerySetPosition(gMainBaseBuildingQueryID, mainVec); // Checking new base vector // kbUnitQuerySetMaximumDistance(gMainBaseBuildingQueryID, 50.0); // } // // kbUnitQueryResetResults(gMainBaseBuildingQueryID); // count = kbUnitQueryExecute(gMainBaseBuildingQueryID); // // while (oldMainID >= 0) // { // aiEcho("Old main base was "+oldMainID+" at "+kbBaseGetLocation(cMyID, oldMainID)); // kbUnitQuerySetPosition(gMainBaseBuildingQueryID,kbBaseGetLocation(cMyID, oldMainID)); // Checking old base location // kbUnitQueryResetResults(gMainBaseBuildingQueryID); // count = kbUnitQueryExecute(gMainBaseBuildingQueryID); // int unitID = -1; // // // // Remove old base's resource breakdowns // aiRemoveResourceBreakdown(cResourceFood, cAIResourceSubTypeEasy, oldMainID); // aiRemoveResourceBreakdown(cResourceFood, cAIResourceSubTypeHunt, oldMainID); // aiRemoveResourceBreakdown(cResourceFood, cAIResourceSubTypeHuntAggressive, oldMainID); // aiRemoveResourceBreakdown(cResourceFood, cAIResourceSubTypeFish, oldMainID); // aiRemoveResourceBreakdown(cResourceFood, cAIResourceSubTypeFarm, oldMainID); // aiRemoveResourceBreakdown(cResourceWood, cAIResourceSubTypeEasy, oldMainID); // aiRemoveResourceBreakdown(cResourceGold, cAIResourceSubTypeEasy, oldMainID); // // kbBaseDestroy(cMyID, oldMainID); // oldMainID = kbBaseGetMainID(cMyID); // } // // // int newBaseID=kbBaseCreate(cMyID, "Base"+kbBaseGetNextID(), mainVec, 50.0); // aiEcho("New main base ID is "+newBaseID); // if (newBaseID > -1) // { // //Figure out the front vector. // vector baseFront=xsVectorNormalize(kbGetMapCenter()-mainVec); // kbBaseSetFrontVector(cMyID, newBaseID, baseFront); // aiEcho("Setting front vector to "+baseFront); // //Military gather point. // float milDist = 40.0; // while ( kbAreaGroupGetIDByPosition(mainVec+(baseFront*milDist)) != kbAreaGroupGetIDByPosition(mainVec) ) // { // milDist = milDist - 5.0; // if (milDist < 6.0) // break; // } // vector militaryGatherPoint = mainVec + (baseFront * milDist); // // kbBaseSetMilitaryGatherPoint(cMyID, newBaseID, militaryGatherPoint); // //Set the other flags. // kbBaseSetMilitary(cMyID, newBaseID, true); // kbBaseSetEconomy(cMyID, newBaseID, true); // //Set the resource distance limit. // // // // 200m x 200m map, assume I'm 25 meters in, I'm 150m from enemy base. This sets the range at 80m. // kbBaseSetSettlement(cMyID, newBaseID, true); // //Set the main-ness of the base. // kbBaseSetMain(cMyID, newBaseID, true); // // // Add the TC, if any. // if (getUnit(gTownCenter, cMyID, cUnitStateABQ) >= 0) // kbBaseAddUnit(cMyID, newBaseID, getUnit(gTownCenter, cMyID, cUnitStateABQ)); // else if(getUnit(gStorehouse, cMyID, cUnitStateABQ) >= 0) // kbBaseAddUnit(cMyID, newBaseID, getUnit(gStorehouse, cMyID, cUnitStateABQ)); // else if(getUnit(gDock, cMyID, cUnitStateABQ) >= 0) // kbBaseAddUnit(cMyID, newBaseID, getUnit(gDock, cMyID, cUnitStateABQ)); // } // // // // Move the defend plan and reserve plan // // xsEnableRule("endDefenseReflexDelay"); // Delay so that new base ID will exist // // return(newBaseID); // } // //============================================================================== // // This function has been merged into the new function PrepareMainBase - bfricks 3/13/2012 // // OLD: initMainBase() // // // // Initialize the main base based off of the town center location. Move the // // base there if there was another main base was previously set. // //============================================================================== // int initMainBase() // { // // First, create a query if needed, then use it to look for a completed town center // if (gTownCenterQuery < 0) // { // gTownCenterQuery=kbUnitQueryCreate("Completed Town Center Query"); // kbUnitQuerySetIgnoreKnockedOutUnits(gTownCenterQuery, true); // if (gTownCenterQuery < 0) // aiEcho("**** Query create failed in townCenterComplete."); // //Define the query // if (gTownCenterQuery != -1) // { // kbUnitQuerySetPlayerID(gTownCenterQuery, cMyID); // kbUnitQuerySetUnitType(gTownCenterQuery, gTownCenter); // kbUnitQuerySetState(gTownCenterQuery, cUnitStateAlive); // } // } // // // Run the query // kbUnitQueryResetResults(gTownCenterQuery); // int count = kbUnitQueryExecute(gTownCenterQuery); // // //-- If our startmode is one without a TC, wait until a TC is found. // if ((count < 1)) // return(gMainBase); // // int tcID = kbUnitQueryGetResult(gTownCenterQuery, 0); // aiEcho("New TC is "+tcID+" at "+kbUnitGetPosition(tcID)); // // // if (tcID >= 0) // { // int tcBase = kbUnitGetBaseID(tcID); // gMainBase = kbBaseGetMainID(cMyID); // aiEcho(" TC base is "+tcBase+", main base is "+gMainBase); // // We have a TC. Make sure that the main base exists, and it includes the TC // if ( gMainBase < 0 ) // { // We have no main base, create one // gMainBase = createMainBase(kbUnitGetPosition(tcID)); // aiEcho(" We had no main base, so we created one: "+gMainBase); // } // tcBase = kbUnitGetBaseID(tcID); // in case base ID just changed // if ( tcBase != gMainBase ) // { // aiEcho(" TC "+tcID+" is not in the main base ("+gMainBase+")"); // aiEcho(" Setting base "+gMainBase+" to non-main, setting base "+tcBase+" to main."); // kbBaseSetMain(cMyID, gMainBase, false); // aiRemoveResourceBreakdown(cResourceFood, cAIResourceSubTypeEasy, gMainBase); // aiRemoveResourceBreakdown(cResourceFood, cAIResourceSubTypeHunt, gMainBase); // aiRemoveResourceBreakdown(cResourceFood, cAIResourceSubTypeHuntAggressive, gMainBase); // aiRemoveResourceBreakdown(cResourceFood, cAIResourceSubTypeFish, gMainBase); // aiRemoveResourceBreakdown(cResourceFood, cAIResourceSubTypeFarm, gMainBase); // aiRemoveResourceBreakdown(cResourceWood, cAIResourceSubTypeEasy, gMainBase); // aiRemoveResourceBreakdown(cResourceGold, cAIResourceSubTypeEasy, gMainBase); // aiRemoveResourceBreakdown(cResourceStone, cAIResourceSubTypeEasy, gMainBase); // kbBaseSetMain(cMyID, tcBase, true); // gMainBase = tcBase; // } // } // else // { // aiEcho("No TC, leaving main base as it is."); // } // // return(gMainBase); // } // //============================================================================== // // This function has been merged into the new function PrepareMainBase - bfricks 3/13/2012 // // OLD: setupBase() // // // // If we don't have a town center, create a base on an AI start or vill. // // Otherwise, create a base on the town center. // //============================================================================== // void setupBase() // { // vector tempBaseVec = cInvalidVector; // // int unitID = -1; // unitID = getUnit(cUnitTypeAIStart, cMyID, cUnitStateAlive); // if (unitID < 0) // unitID = getUnit(gEconUnit, cMyID, cUnitStateAlive); // // if (unitID >= 0) // tempBaseVec = kbUnitGetPosition(unitID); // // // This will create an interim main base at this location if there is no TC. // // Only done if there is no TC, otherwise we rely on the auto-created base. // if (getUnit(gTownCenter, cMyID, cUnitStateAlive) < 0) // { // gMainBase = createMainBase(tempBaseVec); // aiEcho("Main base created, id="+gMainBase); // } // else // { // gMainBase = initMainBase(); // } // // aiEcho("Main base setup, id="+gMainBase); // } //============================================================================== //============================================================================== void calculateAgeUpTechs() { gAge2Tech = kbGetAgeUpTech(2); gAge2Building = kbFindBuilding(cUnitTypeAgeUpBuildingAge2); gAge2HouseCount = 3; //aiEcho("Temple: "+cUnitTypeUnitTypeBldgTemple); gAge3Tech = kbGetAgeUpTech(3); gAge3Building = kbFindBuilding(cUnitTypeAgeUpBuildingAge3); gAge3HouseCount = 8; gAge4Tech = kbGetAgeUpTech(4); gAge4Building = kbFindBuilding(cUnitTypeAgeUpBuildingAge4); gAge4HouseCount = 20; aiEcho("Age 2 Tech: " + gAge2Tech); aiEcho("Age 2 Building: " + gAge2Building); aiEcho("Age 3 Tech: " + gAge3Tech); aiEcho("Age 3 Building: " + gAge3Building); aiEcho("Age 4 Tech: " + gAge4Tech); aiEcho("Age 4 Building: " + gAge4Building); } //============================================================================== // PrepareMainBase() // // Merged old base handling into this single function which ensures // if there is a town center, the main base will use the TC as the center point //============================================================================== void PrepareMainBase() { // NOTE: if we ever decide to call this using some sort of loop, we should switch to using cUnitStateABQ - bfricks 3/13/2012 // decide on a central unit to use in setting up the base bool UnitIsTC = false; int unitID = getUnit(gTownCenter, cMyID, cUnitStateAlive); if (unitID >= 0) { aiEcho("aiMain: --- PrepareMainBase: gTownCenter Located"); UnitIsTC = true; // determine if the TC is already the main base int TCBaseID = kbUnitGetBaseID(unitID); int MainBaseID = kbBaseGetMainID(cMyID); if ((MainBaseID >= 0) && (MainBaseID == TCBaseID)) { aiEcho("aiMain: --- PrepareMainBase: TCBaseID[" + TCBaseID + "] is already MainBaseID[" + MainBaseID + "]"); vector MainLoc = kbBaseGetLocation(cMyID, MainBaseID); vector TCLoc = kbUnitGetPosition(unitID); vector testVec = cInvalidVector; testVec = xsVectorSetX(testVec, xsVectorGetX(MainLoc) - xsVectorGetX(TCLoc)); testVec = xsVectorSetZ(testVec, xsVectorGetZ(MainLoc) - xsVectorGetZ(TCLoc)); // see if we are close enough if ( xsVectorLength(testVec) < 5 ) { aiEcho("aiMain: --- PrepareMainBase: TCLoc[" + TCLoc + "] is close enough to MainLoc[" + MainLoc + "] - calling it good..."); return; } else { aiEcho("aiMain: --- PrepareMainBase: TCLoc[" + TCLoc + "] is not close enough to MainLoc[" + MainLoc + "] - seting up a new base..."); } } } if (unitID < 0) { unitID = getUnit(cUnitTypeAIStart, cMyID, cUnitStateAlive); if (unitID >= 0) { aiEcho("aiMain: --- PrepareMainBase: cUnitTypeAIStart Located"); } } if (unitID < 0) { unitID = getUnit(gStorehouse, cMyID, cUnitStateAlive); if (unitID >= 0) { aiEcho("aiMain: --- PrepareMainBase: gStorehouse Located"); } } if (unitID < 0) { unitID = getUnit(gEconUnit, cMyID, cUnitStateAlive); if (unitID >= 0) { aiEcho("aiMain: --- PrepareMainBase: gEconUnit Located"); } } if (unitID < 0) { aiEcho("aiMain: --- PrepareMainBase: ***** FAILURE - NO UNIT LOCATED TO START BASE *****"); } else { // purge any bases that are already flagged as a main base for ( i=0; < kbBaseGetNumber(cMyID) ) { int baseID = kbBaseGetIDByIndex(cMyID, i); if ( kbBaseGetMain(cMyID, baseID) ) { aiEcho("aiMain: --- PrepareMainBase: purging obsolete Main Base ID[" + baseID + "] LOC[" + kbBaseGetLocation(cMyID, baseID) + "]"); // remove resource breakdowns aiRemoveResourceBreakdown(cResourceFood, cAIResourceSubTypeEasy, baseID); aiRemoveResourceBreakdown(cResourceFood, cAIResourceSubTypeHunt, baseID); aiRemoveResourceBreakdown(cResourceFood, cAIResourceSubTypeHuntAggressive, baseID); aiRemoveResourceBreakdown(cResourceFood, cAIResourceSubTypeFish, baseID); aiRemoveResourceBreakdown(cResourceFood, cAIResourceSubTypeFarm, baseID); aiRemoveResourceBreakdown(cResourceWood, cAIResourceSubTypeEasy, baseID); aiRemoveResourceBreakdown(cResourceGold, cAIResourceSubTypeEasy, baseID); // force this base to no longer be flagged as a main base kbBaseSetMain(cMyID, baseID, false); } } // setup the new base, given our main unit location vector mainVec = kbUnitGetPosition(unitID); gMainBase = kbBaseCreate(cMyID, "Base"+kbBaseGetNextID(), mainVec, 50.0); if (gMainBase > -1) { //Figure out the front vector. vector baseFront = xsVectorNormalize(kbGetMapCenter() - mainVec); kbBaseSetFrontVector(cMyID, gMainBase, baseFront); //Military gather point. float milDist = 40.0; while ( kbAreaGroupGetIDByPosition(mainVec + (baseFront * milDist)) != kbAreaGroupGetIDByPosition(mainVec) ) { milDist = milDist - 5.0; if (milDist < 6.0) break; } vector militaryGatherPoint = mainVec + (baseFront * milDist); kbBaseSetMilitaryGatherPoint(cMyID, gMainBase, militaryGatherPoint); //Set the other flags. kbBaseSetMilitary(cMyID, gMainBase, true); kbBaseSetEconomy(cMyID, gMainBase, true); kbBaseSetSettlement(cMyID, gMainBase, true); kbBaseSetMain(cMyID, gMainBase, true); // Add the TC if (UnitIsTC) { aiEcho("aiMain: --- PrepareMainBase: adding a TC to the base"); kbBaseAddUnit(cMyID, gMainBase, unitID); } } aiEcho("aiMain: --- PrepareMainBase: new Main Base ID[" + gMainBase + "] LOC[" + mainVec + "]"); } } //============================================================================== // Game initialization method // // BF 2/18/2012: Updated from Skirmish: // exploration on naval maps only begins when navy is ready // quest objectives are no longer enumerated //============================================================================== mutable void init() { // We're always on a random map now aiSetRandomMap(true); // Setup event handlers setupHandlers(); // Figure out our age up techs calculateAgeUpTechs(); // Check for a naval map and see if we're a good map to fish on if (aiIsMapType("AIFishingUseful") == true) gGoodFishingMap = true; else gGoodFishingMap = false; gNavyMap = isCurrentMapNaval(); aiSetWaterMap(gNavyMap); aiEcho("WATER MAP: " + gNavyMap); // Fishing is always good on navy maps if (gNavyMap) gGoodFishingMap = true; // prepare our initial base PrepareMainBase(); // Make sure we're exploring if(gNavyMap == false) exploreMonitor(); // Set our gather distance updateMaxGatherDistance(); // Setup the escrows createEscrows(); // Priority balance neutral aiSetEconomyPercentage(1.0); aiSetMilitaryPercentage(1.0); // Set up gatherer goal // [9/24/2009 CJS] Don't do this here, wait 10s to start the econ so we can find stuff around our base //initGatherGoal(); // Initialize the economy setupBuildingInfo(); //updateEcon(); xsEnableRuleGroup("econ"); xsEnableRule("ageUpMonitor"); if(gNavyMap == true) { xsDisableRule("exploreMonitor"); initNavy(); } // Immediately enable military if military in age 1 is set. if (btMilitaryInAge1 == true) { aiEcho("Quest map"); initMilitary(); } // Make sure we have a working house if (kbProtoUnitAvailable(gHouse) == false) gHouse = cUnitTypeGrCapHut; // Setup to play quest objectives if (aiIsQuestMap() == true) { // Calculate the number of defenders for objectives float rushBoom = btRushBoom; // Never go over the cap if (rushBoom > 0.0) rushBoom = 0.0; // For rushing, decrease us faster else { rushBoom = rushBoom * 2.0; if (rushBoom < -1.0) rushBoom = -1.0; } } gCurrentCiv = kbGetCiv(); aiEcho("Civ ID:"+gCurrentCiv); } //============================================================================== //============================================================================== void main() { aiEcho("aiMain: --- Starting..."); aiEcho("aiMain: --- GameType[" + aiGetGameType() + "] (0=Scn, 1=Saved, 2=Rand, 3=GC, 4=Cmpgn)"); aiEcho("aiMain: --- MapName[" + cRandomMapName + "]"); aiEcho("aiMain: --- QuestMap[" + aiIsQuestMap() + "]"); //aiRandSetSeed(555); // Set our random seed. "-1" is a random init. // Allow loader to set values preInit(); // Lookup building types gTower = kbFindBuilding(cUnitTypeUnitTypeBldgTower); gEconUnit = kbFindUnit(cUnitTypeUnitTypeVillager1); gFarmUnit = kbFindBuilding(cUnitTypeUnitTypeBldgFarm); gTownCenter = kbFindBuilding(cUnitTypeUnitTypeBldgTownCenter); gHouse = kbFindBuilding(cUnitTypeUnitTypeBldgHouse); gDock = kbFindBuilding(cUnitTypeUnitTypeBldgDock); gStorehouse = kbFindBuilding(cUnitTypeUnitTypeBldgStorehouse); aiEcho("aiMain: --- Tower[" + gTower + "] Vil[" + gEconUnit + "] Farm[" + gFarmUnit + "] TC[" + gTownCenter + "] House[" + gHouse + "] Dock[" + gDock + "]"); // Initialize global gatherer array initGatherers(); // Analyze the map, create area matrix kbAreaCalculate(); // Initialize the pop limits popManager(); // Setup handicaps float startingHandicap = kbGetPlayerHandicap(cMyID); kbSetPlayerHandicap( cMyID, startingHandicap * btBaselineHandicap ); aiEcho("aiMain: --- startingHandicap[" + startingHandicap + "] currentHandicap[" + kbGetPlayerHandicap(cMyID) + "]"); // Start up the game init(); // Let the loeader do anything else it wants postInit(); xsEnableRule("autoSave"); // Trigger first autosave immediately autoSave(); }