include "aiSiegeTowerCore.xs"; // strategy extern int btStrategy = 0; extern int btOldStrategy = -1; extern int cBalancedStrategy = 0; extern int cRushStrategy = 1; extern int cTechRampStrategy = 2; extern int cTurtleStrategy = 3; // difficulty extern int btDifficultyLevel = -1; extern float btDifficulty = 0; extern float cWeakDifficulty = 0; extern float cStandardDifficulty = 2; extern float cStrongDifficulty = 2.75; extern float cFierceDifficulty = 3; extern float cUnstoppableDifficulty = 4; // victory conditions extern int btVictoryCondition = 1; extern int cConquestVictory = 0; extern int cStandardVictory = 1; extern int cWonderVictory = 2; // wonder management extern bool cBuildingWonder = false; extern bool gReadyToBuildWonder = false; // town bell managements extern bool cTownBellRung = false; extern int cUpdatesWhileGarrisoned = 0; // custom strategy management // used when there is a key building needed for a strategy extern int btSecondaryFocusBuilding = -1; extern int gNumberOfTCs = 0; extern int gStartingArmyPlan = -1; extern int gEnemyVillagerQuery = -1; // norse AI stuff extern int gResourceQuery = -1; extern int gOutpostQuery = -1; extern int gNumOutposts = 0; extern int gCurrentOutpostResourceID = -1; extern int gDogDefendPlanArray = -1; extern int gDogDefendLocationArray = -1; extern int gDogDefendPatrolIndex = 0; // cached random from RM scripts extern int gRandomTowerCount = -1; extern int gRandomAttackDelay = -1; extern int gRandomHumanAllyAttackDelay = -1; extern int gRandomInfantryBias = -1; extern int gRandomCavalryBias = -1; extern int gRandomRangedBias = -1; extern int gRandomSiegeBias = -1; extern int gRandomPriestBias = -1; extern int gRandomSecondaryFocus = -1; extern int gRandomFoodAmount = -1; extern int gRandomWoodAmount = -1; extern int gRandomGoldAmount = -1; extern int gRandomStoneAmount = -1; extern int gRandomStrategy = -1; // random number functions from RM void skirmishRandomTowerCount(int towers = -1) { gRandomTowerCount = towers; } void skirmishRandomAttackDelay(int delay = -1) { gRandomAttackDelay = delay; } void skirmishRandomHumanAllyAttackDelay(int delay = -1) { gRandomHumanAllyAttackDelay = delay; } void skirmishRandomInfantryBias(int bias = -1) { gRandomInfantryBias = bias; } void skirmishRandomCavalryBias(int bias = -1) { gRandomCavalryBias = bias; } void skirmishRandomRangedBias(int bias = -1) { gRandomRangedBias = bias; } void skirmishRandomSiegeBias(int bias = -1) { gRandomSiegeBias = bias; } void skirmishRandomPriestBias(int bias = -1) { gRandomPriestBias = bias; } void skirmishRandomSecondaryFocus(int focus = -1) { gRandomSecondaryFocus = focus; } void skirmishRandomFoodAmount(int food = -1) { gRandomFoodAmount = food; } void skirmishRandomWoodAmount(int wood = -1) { gRandomWoodAmount = wood; } void skirmishRandomGoldAmount(int gold = -1) { gRandomGoldAmount = gold; } void skirmishRandomStoneAmount(int stone = -1) { gRandomStoneAmount = stone; } void skirmishRandomStrategy(int strat = -1) { gRandomStrategy = strat; } void skirmishStartWonderBuildTimer(int time = -1) { xsEnableRule("wonderBuildingTimer"); xsSetRuleMinInterval("wonderBuildingTimer", time); } void skirmishForceUnitPickerUpdate(int unused = -1) { xsEnableRule("refreshUnitPicker"); } rule refreshUnitPicker inactive minInterval 20 { setUnitPickerPreference(gLandUnitPicker); xsDisableSelf(); } //============================================================================== // marketBuySell // //============================================================================== /*rule marketBuySell active minInterval 10 { if (kbUnitCount(cMyID, kbFindBuilding(cUnitTypeUnitTypeBldgMarket), cUnitStateAlive) == 0) { aiEcho("marketBuySell: no market"); return; } int surplus = -1; int need = -1; for(i = 0; < cNumResourceTypes - 1) { if(kbResourceGet(i) > 2000 && surplus == -1) { surplus = i; aiEcho("marketBuySell: Woah! I have " + kbResourceGet(i) + " " + i); } if(kbResourceGet(i) < 500 && need == -1) { need = i; aiEcho("marketBuySell: I only have " + kbResourceGet(i) + " " + i); } } if(need == -1 || surplus == -1) { aiEcho("marketBuySell: can't trade"); return; } int sell = (kbResourceGet(surplus) - 1000) / 100; aiEcho("marketBuySell: selling " + surplus + " for " + need + " " + sell + " times."); for(j = 0; < sell) { aiSellResourceOnMarket(surplus); } for(k = 0; < sell) { aiBuyResourceOnMarket(need); } }*/ //============================================================================== // wonderBuildingTimer // // This rule is enable by RM when we want to start the timer that'll enable the // AI to build a wonder. //============================================================================== rule wonderBuildingTimer inactive minInterval 555 { aiEcho("wonderBuildingTimer: AI is now allowed to build a wonder"); gReadyToBuildWonder = true; xsDisableSelf(); } //============================================================================== // initializeSiegeTowers // // This rule will disable the siege tower rule for civs that don't have Siege // Towers that are also land transports. //============================================================================== rule initializeSiegeTowers active minInterval 5 { // we currently don't have a decent way exposed to the AI to check abstract types if(kbGetCiv() != gBabylonianCivID) { aiEcho("initializeSiegeTowers: disabling rule to garrison units in Siege Towers"); xsDisableRule("loadAllUnitsIntoAllSiegeTowers"); } xsDisableSelf(); } //============================================================================== // displayTSFactors // // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== rule displayTSFactors inactive minInterval 30 { int tsdistance = kbGetTargetSelectorFactor(cTSFactorDistance); int point = kbGetTargetSelectorFactor(cTSFactorPoint); int timeToDone = kbGetTargetSelectorFactor(cTSFactorTimeToDone); int base = kbGetTargetSelectorFactor(cTSFactorBase); int danger = kbGetTargetSelectorFactor(cTSFactorDanger); aiEcho("TS FACTORS: Distance " + tsdistance + ", Point " + point + ", TimeToDone " + timeToDone + ", Base " + base + ", Danger " + danger); } //============================================================================== // computeRushBoomFromSettings // // Set btRushBoom and gAttackMissionInterval based on the difficulty value. // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== void computeRushBoomFromSettings() { switch(btStrategy) { case cBalancedStrategy: { btRushBoom = 0; gAttackMissionInterval = (140 - (btDifficulty * 20)) * 1000; break; } case cRushStrategy: { btRushBoom = -0.2 * (btDifficulty + 1); gAttackMissionInterval = (80 - (btDifficulty * 15)) * 1000; break; } case cTechRampStrategy: { btRushBoom = 0.2 * (btDifficulty + 1); gAttackMissionInterval = (180 - (btDifficulty * 20)) * 1000; break; } case cTurtleStrategy: { btRushBoom = 0; gAttackMissionInterval = (260 - (btDifficulty * 20)) * 1000; break; } default: { btRushBoom = 0; gAttackMissionInterval = (140 - (btDifficulty * 20)) * 1000; break; } } // TODO: review this with Matt, ideally this is a per strategy thing or a global setting in AIDefaults // OLD: gNumFishBoats = (btRushBoom + 0.7) * 5.0;/ min 0, max 8 gNumFishBoats = btDifficulty + (8 * (0.5 + (btRushBoom / 2.0)));// min 0, max 12 // increase attack interval for AI allies, so they don't attack too often if(kbGetPlayerTeam(cMyID) == 1) { gAttackMissionInterval = gAttackMissionInterval + (90 * getNumberOfAIsOnHumanTeam()); } } //============================================================================== // checkForHighResources // // Check for high starting resources and update the AI so they can handle it. //============================================================================== rule checkForHighResources active minInterval 10 maxInterval 15 { if(kbResourceGet(cResourceFood) >= 600) { btHighStartingResources = true; xsEnableRule("disableHighResourceMode"); xsSetRuleMinInterval("ageUpgradeCheck", 33); setupBuildingInfo(); btMilitaryInAge1 = false; // switch to rush for a while; btOldStrategy = btStrategy; btStrategy = cRushStrategy; computeRushBoomFromSettings(); updateMilitarySize(); createSimpleBuildPlan(gHouse, 10, 100, true, cEconomyEscrowID, kbBaseGetMainID(cMyID), 1); cvNumTowers = 3; // check for AIs on human team int humanAllies = getNumberOfAIsOnHumanTeam(); int baseTime = 180; if(kbGetPlayerTeam(cMyID) == 1) { baseTime = baseTime + (humanAllies * 240); } xsSetRuleMinInterval("delayFirstAttack", baseTime - (btDifficulty * 30)); if(btBiasInf == -1) { btBiasInf = -0.5; setUnitPickerPreference(gLandUnitPicker); } aiEcho("Playing with very high starting resources!"); } else { btMilitaryInAge1 = false; xsSetRuleMinInterval("ageUpgradeCheck", 13); //initMilitary(); //initNavy(); } xsDisableSelf(); } //============================================================================== // disableHighResourceMode // // Resume normal AI aging up and building after a while. //============================================================================== rule disableHighResourceMode inactive minInterval 180 { xsSetRuleMinInterval("ageUpgradeCheck", 13); btHighStartingResources = false; btStrategy = btOldStrategy; btTargetAge2ArmyCount = 4 + (3 * btDifficulty); computeRushBoomFromSettings(); xsDisableSelf(); } //============================================================================== // forecastStrategicGoals // // Hook into the standard econ update with additional forcasting when building // a wonder. //============================================================================== void forecastStrategicGoals() { if(cBuildingWonder) { xsArraySetFloat(gForecasts, cResourceStone, kbUnitCostPerResource(kbFindBuilding(cUnitTypeUnitTypeBldgWonder), cResourceStone)); xsArraySetFloat(gMiliForecasts, cResourceStone, kbUnitCostPerResource(kbFindBuilding(cUnitTypeUnitTypeBldgWonder), cResourceStone)); } } //============================================================================== // getVillagerCountForBuildOrder // // Get the amount of villagers we have and inflate it if we are in high // resource mode. // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== int getVillagerCountForBuildOrder() { int currentVillCount = kbUnitCount(cMyID, gEconUnit, cUnitStateAlive); // fake having more villagers so that the AI will build more stuff if(btHighStartingResources) { currentVillCount = currentVillCount + 30; } // try to prevent the AI from building too much stuff and not having room // for the wonder. if(btVictoryCondition == cWonderVictory && currentVillCount > 40) { currentVillCount = 40; } return (currentVillCount); } //============================================================================== // increaseSiegeLateGame // // Increase the siege bias in age 3 and 4, capping at 1.0 // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== rule increaseSiegeLateGame active minInterval 23 { static int ageToCheck = cAge3; if(kbGetAge() == ageToCheck) { btBiasArt = btBiasArt + 0.125; if(btBiasArt > 1.0) btBiasArt = 1.0; aiEcho("Increasing Siege bias to " + btBiasArt); setUnitPickerPreference(gLandUnitPicker); ageToCheck = ageToCheck + 1; } } //============================================================================== // townBellManager // // Check for enemy units in our base and use the town bell accordingly. // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== rule townBellManager active minInterval 7 { // kill after 10 minutes only if our villagers are out if(xsGetTime() > 600000 && cTownBellRung == false) { aiEcho("killVillagersWithTC: shutting down"); xsDisableSelf(); return; } xsSetRuleMinInterval("townBellManager", 7); int enemyArmySize = numEnemiesInBase(cMyID, kbBaseGetMainID(cMyID)); //aiEcho("Enemies up in my grill: " + enemyArmySize); int tcID = getUnitByLocation(gTownCenter, cMyID, cUnitStateAlive, kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID)), 10, true); if(tcID == -1) return; //aiEcho("town center ID is: " + tcID); if(cTownBellRung == false) { float armySize = aiPlanGetNumberUnits(gLandDefensePlan, cUnitTypeLogicalTypeLandMilitary) + aiPlanGetNumberUnits(gLandReservePlan, cUnitTypeLogicalTypeLandMilitary); //aiEcho("We have this many dudes: " + armySize); float scale = 1.25; if(enemyArmySize < 10) { scale = 1.5; } if(enemyArmySize > armySize * scale && enemyArmySize > 2) { aiEcho("townBellManager: garrison villages"); aiTownBell(tcID); cTownBellRung = true; cUpdatesWhileGarrisoned = 0; } } else { cUpdatesWhileGarrisoned = cUpdatesWhileGarrisoned + 1; if(enemyArmySize < 3 || cUpdatesWhileGarrisoned > 8) { aiEcho("townBellManager: return to work"); aiReturnToWork(tcID); cTownBellRung = false; xsSetRuleMinInterval("townBellManager", 45); } } } //============================================================================== // killVillagersWithTC // // Make our TC target villagers to prevent pesky humans from trying to // exploit us. // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== rule killVillagersWithTC active minInterval 5 { // kill after 5 minutes if(xsGetTime() > 300000) { aiEcho("killVillagersWithTC: shutting down"); xsDisableSelf(); return; } if(gEnemyVillagerQuery == -1) { gEnemyVillagerQuery = kbUnitQueryCreate("Enemy villager query"); kbUnitQuerySetPlayerRelation(gEnemyVillagerQuery, cPlayerRelationEnemyNotGaia); kbUnitQuerySetUnitType(gEnemyVillagerQuery, cUnitTypeUnitTypeVillager1); kbUnitQuerySetState(gEnemyVillagerQuery, cUnitStateAlive); kbUnitQuerySetPosition(gEnemyVillagerQuery, kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID))); kbUnitQuerySetMaximumDistance(gEnemyVillagerQuery, 30); } kbUnitQueryResetResults(gEnemyVillagerQuery); kbUnitQueryExecute(gEnemyVillagerQuery ); int vil = kbUnitQueryGetResult(gEnemyVillagerQuery, 0); //int vil = getUnitByLocation(gEconUnit, cPlayerRelationEnemyNotGaia, cUnitStateAlive, kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID)), 20, true); if(vil == -1) return; int tcID = getUnitByLocation(gTownCenter, cMyID, cUnitStateAlive, kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID)), 10, true); aiTaskUnitWork(tcID, vil); aiEcho("tc " + tcID + " is attacking villager " + vil); } //============================================================================== // delayFirstAttack // // Allow the AI to attack. // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== rule delayFirstAttack active minInterval 60 { cvOkToAttack = true; xsDisableSelf(); aiEcho("delayFirstAttack: now allowing attacks"); } //============================================================================== // grantBonusResources // // Give the AI some resources in an attempt to randomize what they build/train. // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== rule grantBonusResources active minInterval 180 { kbAdjustResourceByID(cMyID, cResourceFood, gRandomFoodAmount); kbAdjustResourceByID(cMyID, cResourceWood, gRandomWoodAmount); kbAdjustResourceByID(cMyID, cResourceGold, gRandomGoldAmount); kbAdjustResourceByID(cMyID, cResourceStone, gRandomStoneAmount); aiEcho("grantBonusResources: AI receives " + gRandomFoodAmount + " food, " + gRandomWoodAmount + " wood, " + gRandomGoldAmount + " gold and " + gRandomStoneAmount + " stone"); xsDisableSelf(); } //============================================================================== // checkForWonder // // If the current victory condition is a wonder victory, get the AI to build // one in age 4. // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== rule checkForWonder active minInterval 30 { if(btVictoryCondition != cWonderVictory) { aiEcho("checkForWonder: disabling because this isn't a wonder victory condition"); xsDisableSelf(); } if(kbGetAge() == cAge4 && gReadyToBuildWonder == true) { createSimpleBuildPlan(kbFindBuilding(cUnitTypeUnitTypeBldgWonder), 1, 100, false, cMilitaryEscrowID, kbBaseGetMainID(cMyID), 8); cBuildingWonder = true; gAttackMissionInterval = gAttackMissionInterval * 2; xsEnableRule("buyStoneForWonder"); xsDisableSelf(); } } //============================================================================== // buyStoneForWonder // // Buy some stone from the market if we are building a wonder. // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== rule buyStoneForWonder inactive minInterval 3 { if (kbUnitCount(cMyID, kbFindBuilding(cUnitTypeUnitTypeBldgMarket), cUnitStateAlive) == 0) { aiEcho("buyStoneForWonder: no market"); return; } if(kbResourceGet(cResourceStone) < kbUnitCostPerResource(kbFindBuilding(cUnitTypeUnitTypeBldgWonder), cResourceStone)) { aiBuyResourceOnMarket(cResourceStone); } } rule cheatResourcesForWonder inactive minInterval 1800 { if(btVictoryCondition == cWonderVictory) { kbAdjustResourceByID(cMyID, cResourceWood, kbUnitCostPerResource(kbFindBuilding(cUnitTypeUnitTypeBldgWonder), cResourceWood)); kbAdjustResourceByID(cMyID, cResourceGold, kbUnitCostPerResource(kbFindBuilding(cUnitTypeUnitTypeBldgWonder), cResourceGold)); kbAdjustResourceByID(cMyID, cResourceStone, kbUnitCostPerResource(kbFindBuilding(cUnitTypeUnitTypeBldgWonder), cResourceStone)); } xsDisableSelf(); } //============================================================================== // buildTowersAtSecondaryBases // // Loop through all our bases and check to see if we need to build a tower near // any of them. // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== rule buildTowersAtSecondaryBases active minInterval 37 { // save stone if we need to build a wonder if(cBuildingWonder) { return; } int numBases = kbBaseGetNumber(cMyID); int i = 0; int baseID = -1; if (aiPlanGetIDByTypeAndVariableType(cPlanBuild, cBuildPlanBuildingTypeID, gTower) >= 0) return; // We're already building one. //aiEcho("I have " + numBases + " base(s)."); for (i = 0; < numBases) { baseID = kbBaseGetIDByIndex(cMyID, i); // ignore main base if (baseID == kbBaseGetMainID(cMyID)) continue; if (gTowerSearch < 0) { // init gTowerSearch = kbUnitQueryCreate("Tower placement search"); kbUnitQuerySetPlayerRelation(gTowerSearch, cPlayerRelationAny); kbUnitQuerySetUnitType(gTowerSearch, gTower); kbUnitQuerySetState(gTowerSearch, cUnitStateABQ); } kbUnitQuerySetPosition(gTowerSearch, kbBaseGetLocation(cMyID, baseID)); kbUnitQuerySetMaximumDistance(gTowerSearch, 30); kbUnitQueryResetResults(gTowerSearch); if (kbUnitQueryExecute(gTowerSearch) < 1) { //aiEcho("no towers near this base"); int buildPlan=aiPlanCreate("Tower build plan ", cPlanBuild); // What to build aiPlanSetVariableInt(buildPlan, cBuildPlanBuildingTypeID, 0, gTower); // Priority. aiPlanSetDesiredPriority(buildPlan, 95); // Econ, because mil doesn't get enough wood. aiPlanSetMilitary(buildPlan, false); aiPlanSetEconomy(buildPlan, true); // Escrow. aiPlanSetEscrowID(buildPlan, cEconomyEscrowID); // Builders. aiPlanAddUnitType(buildPlan, gEconUnit, 1, 1, 1); // Instead of base ID or areas, use a center position and falloff. aiPlanSetVariableVector(buildPlan, cBuildPlanCenterPosition, 0, kbBaseGetLocation(cMyID, baseID)); aiPlanSetVariableFloat(buildPlan, cBuildPlanCenterPositionDistance, 0, 10.0); aiPlanSetVariableBool(buildPlan, cBuildPlanNoFailOutOfUnits, 0, true); aiPlanSetAllowHandleDamage(buildPlan, false); aiPlanSetActive(buildPlan); } } } //============================================================================== // turtleBeginAttacks // // Allow a turtling AI to attack once it is age 3. // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== rule turtleBeginAttacks inactive minInterval 20 { // allow the turtle strategy to attack in Age 3 if(kbGetAge() > cAge2) { cvOkToAttack = true; xsDisableSelf(); } } //============================================================================== // setBalancedUnitPreference() // // Updates the unit picker biases. // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== void setBalancedUnitPreference(int upID = -1) { // Add the main unit lines if (upID < 0) return; switch(kbGetCiv()) { case gGreekCivID: { kbUnitPickSetPreferenceFactor(upID, cUnitTypeUnitTypeInfantryBasic1, 1); kbUnitPickSetPreferenceFactor(upID, cUnitTypeUnitTypeSiegeBallista1, 1); kbUnitPickSetPreferenceFactor(upID, cUnitTypeUnitTypeCavalryBasic1, 1); kbUnitPickSetPreferenceFactor(upID, cUnitTypeUnitTypeArcherAntiInf1, 1); break; } case gEgyptianCivID: { kbUnitPickSetPreferenceFactor(upID, cUnitTypeUnitTypeArcherAntiArc1, 1); kbUnitPickSetPreferenceFactor(upID, cUnitTypeUnitTypeArcherAntiInf1, 1); kbUnitPickSetPreferenceFactor(upID, cUnitTypeUnitTypeSiegeCatapult1, 1); kbUnitPickSetPreferenceFactor(upID, cUnitTypeUnitTypeCavalryBasic1, 1); kbUnitPickSetPreferenceFactor(upID, cUnitTypeUnitTypeArcherBasic1, 1); break; } case gCelticCivID: { kbUnitPickSetPreferenceFactor(upID, cUnitTypeUnitTypeInfantryBasic1, 1); kbUnitPickSetPreferenceFactor(upID, cUnitTypeUnitTypeInfantryHeavy1, 1); kbUnitPickSetPreferenceFactor(upID, cUnitTypeUnitTypeArcherAntiInf1, 1); kbUnitPickSetPreferenceFactor(upID, cUnitTypeUnitTypeArcherBasic1, 1); break; } case gPersianCivID: { kbUnitPickSetPreferenceFactor(upID, cUnitTypeUnitTypeInfantryBasic1, 1); kbUnitPickSetPreferenceFactor(upID, cUnitTypeUnitTypeSiegeCatapult1, 1); kbUnitPickSetPreferenceFactor(upID, cUnitTypeUnitTypeCavalryAntiInf1, 1); kbUnitPickSetPreferenceFactor(upID, cUnitTypeUnitTypeInfantryAntiInf1, 1); break; } case gBabylonianCivID: { kbUnitPickSetPreferenceFactor(upID, cUnitTypeUnitTypeInfantryAntiCav1, 1); kbUnitPickSetPreferenceFactor(upID, cUnitTypeUnitTypeInfantryBasic1, 1); kbUnitPickSetPreferenceFactor(upID, cUnitTypeUnitTypeArcherAntiArc1, 1); kbUnitPickSetPreferenceFactor(upID, cUnitTypeUnitTypeCavalryAntiInf1, 1); kbUnitPickSetPreferenceFactor(upID, cUnitTypeUnitTypeArcherAntiInf1, 1); break; } case gNorseCivID: { kbUnitPickSetPreferenceFactor(upID, cUnitTypeUnitTypeInfantryBasic1, 1); kbUnitPickSetPreferenceFactor(upID, cUnitTypeUnitTypeArcherAntiArc1, 1); kbUnitPickSetPreferenceFactor(upID, cUnitTypeUnitTypeCavalryAntiCav1, 1); kbUnitPickSetPreferenceFactor(upID, cUnitTypeUnitTypeSiegeRam1, 1); break; } default: { kbUnitPickSetPreferenceFactor(upID, cUnitTypeAbstractInfantry, 0.5 + (btBiasInf / 2.0)); // Range 0.0 to 1.0 kbUnitPickSetPreferenceFactor(upID, cUnitTypeAbstractArtillery, 0.5 + (btBiasArt / 2.0)); kbUnitPickSetPreferenceFactor(upID, cUnitTypeAbstractCavalry, 0.5 + (btBiasCav / 2.0)); kbUnitPickSetPreferenceFactor(upID, cUnitTypeAbstractArcher, 0.5 + (btBiasArcher / 2.0)); break; } } } //============================================================================== // printAISettings // // Print the AIs difficulty and strategy to the debug menu. // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== void printAISettings() { aiEcho("Current AI settings:"); if(btDifficulty == cWeakDifficulty) { aiEcho("Weak difficulty"); } else if(btDifficulty == cStandardDifficulty) { aiEcho("Standard difficulty"); } else if(btDifficulty == cStrongDifficulty) { aiEcho("Strong difficulty"); } else if(btDifficulty == cFierceDifficulty) { aiEcho("Fierce difficulty"); } else if(btDifficulty == cUnstoppableDifficulty) { aiEcho("Unstoppable difficulty"); } switch(btStrategy) { case cBalancedStrategy: { aiEcho("Balanced strategy"); break; } case cRushStrategy: { aiEcho("Rush strategy"); break; } case cTechRampStrategy: { aiEcho("TechRamp strategy"); break; } case cTurtleStrategy: { aiEcho("Turtle strategy"); break; } } } //============================================================================== // setEconHandicapFromDifficulty // // Set the economy handicap based on difficulty and then print it out. // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== void setEconHandicapFromDifficulty() { if(btDifficulty == cWeakDifficulty) { btBaselineHandicap = 0.85; } else if(btDifficulty == cStandardDifficulty) { btBaselineHandicap = 1.0; } else if(btDifficulty == cStrongDifficulty) { btBaselineHandicap = 1.125; } else if(btDifficulty == cFierceDifficulty) { btBaselineHandicap = 1.25; } else if(btDifficulty == cUnstoppableDifficulty) { btBaselineHandicap = 1.55; } aiEcho("Econ handicap: " + btBaselineHandicap); } //============================================================================== // getDataFromBiases // // Get difficulty, strategy and victory condition based on unit biases set by // the AIEntryTool. // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== void getDataFromBiases() { // 0 - 4, very easy to very hard //float diff = aiGetSiegeBias(); // this is to allow these settings to be set by the mission tool //if(diff < 1) //{ // diff = diff * 10; //} switch(btDifficultyLevel) { case 0: { btDifficulty = cWeakDifficulty; break; } case 1: { btDifficulty = cStandardDifficulty; break; } case 2: { btDifficulty = cStrongDifficulty; break; } case 3: { btDifficulty = cFierceDifficulty; break; } case 4: { btDifficulty = cUnstoppableDifficulty; break; } } // 0 = rush, 1 = balanced, 2 = techramp/room, 3 = turtle float strat = aiGetInfantryBias(); // this is to allow these settings to be set by the mission tool if(strat < 1) { strat = strat * 10; } btStrategy = strat; // 0 = conquest, 1 = standard, 2 = wonder btVictoryCondition = aiGetCavalryBias(); aiEcho("VICTORY: " + btVictoryCondition); computeRushBoomFromSettings(); printAISettings(); } //============================================================================== // setRandomTowerCount // // Get a random amount of towers to build and cap it based on difficulty. // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== rule setRandomTowerCount inactive minInterval 5 { // give a higher percentage chance of not placing any towers int towers = gRandomTowerCount; // don't let easier difficulties build more towers if(btDifficulty < cUnstoppableDifficulty && towers > btDifficulty) { towers = btDifficulty; } if(towers > 0) { cvNumTowers = towers; } else { cvNumTowers = 0; } aiEcho("setRandomTowerCount: going to attempt to build " + cvNumTowers + " this game!"); xsDisableSelf(); } //============================================================================== // setVillagerCap // // Check if we have an age cap and allow our age 4 villager count to be reached. // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== void setVillagerCap() { if(gAge2Building == -1 && kbTechGetStatus(gAge2Tech) == cTechStatusUnobtainable) { xsArraySetInt(gTargetVillagerCounts, cAge1, btTargetAge4VilCount); xsArraySetInt(gTargetVillagerCounts, cAge2, btTargetAge4VilCount); xsArraySetInt(gTargetVillagerCounts, cAge3, btTargetAge4VilCount); xsArraySetInt(gTargetVillagerCounts, cAge4, btTargetAge4VilCount); aiEcho("setVillagerCap: setting ages 1 - 4 vil cap to " + btTargetAge4VilCount); } else if(gAge3Building == -1 && kbTechGetStatus(gAge3Tech) == cTechStatusUnobtainable) { xsArraySetInt(gTargetVillagerCounts, cAge2, btTargetAge4VilCount); xsArraySetInt(gTargetVillagerCounts, cAge3, btTargetAge4VilCount); xsArraySetInt(gTargetVillagerCounts, cAge4, btTargetAge4VilCount); aiEcho("setVillagerCap: setting ages 2 - 4 vil cap to " + btTargetAge4VilCount); } else if(gAge4Building == -1 && kbTechGetStatus(gAge4Tech) == cTechStatusUnobtainable) { xsArraySetInt(gTargetVillagerCounts, cAge3, btTargetAge4VilCount); xsArraySetInt(gTargetVillagerCounts, cAge4, btTargetAge4VilCount); aiEcho("setVillagerCap: setting ages 3 - 4 vil cap to " + btTargetAge4VilCount); } } //============================================================================== // createStartingArmy // // Grab any starting units and put them in their own army. // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== rule createStartingArmy active minInterval 5 { // Create the reserve plan. This is the plan that holds all currently unused military units at our base. gStartingArmyPlan = aiPlanCreate("Starting Army Units", cPlanDefend); // can't use btDifficulty now that it isn't 0 - 4 int units = btDifficulty + 1; aiPlanAddUnitType(gStartingArmyPlan, cUnitTypeLogicalTypeLandMilitary , units, units, units); // All mil units, high MAX value to suck up all excess //aiPlanSetVariableInt(gStartingArmyPlan, cDefendPlanDefendBaseID, 0, kbBaseGetMainID(cMyID)); aiPlanSetVariableVector(gStartingArmyPlan, cDefendPlanDefendPoint, 0, kbBaseGetMilitaryGatherPoint(cMyID, kbBaseGetMainID(cMyID))); if (kbBaseGetMilitaryGatherPoint(cMyID, kbBaseGetMainID(cMyID)) == cInvalidVector) { if (getUnit(cUnitTypeAIStart, cMyID) >= 0) // If no mil gather point, but there is a start block, use it. aiPlanSetVariableVector(gStartingArmyPlan, cDefendPlanDefendPoint, 0, kbUnitGetPosition(getUnit(cUnitTypeAIStart, cMyID))); else aiPlanSetVariableVector(gStartingArmyPlan, cDefendPlanDefendPoint, 0, kbUnitGetCentroid(cUnitTypeLogicalTypeLandMilitary)); } if (aiPlanGetVariableVector(gStartingArmyPlan, cDefendPlanDefendPoint, 0) == cInvalidVector) // If all else failed, use the average location. aiPlanSetVariableVector(gStartingArmyPlan, cDefendPlanDefendPoint, 0, kbUnitGetCentroid(cUnitTypeLogicalTypeLandMilitary)); aiPlanSetVariableFloat(gStartingArmyPlan, cDefendPlanEngageRange, 0, cvDefenseReflexRadiusPassive); // Loose aiPlanSetRequiresAllNeedUnits(gStartingArmyPlan, true); aiPlanSetVariableFloat(gStartingArmyPlan, cDefendPlanGatherDistance, 0, cvDefenseReflexRadiusPassive - 10.0); aiPlanSetInitialPosition(gStartingArmyPlan, kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID))); aiPlanSetUnitStance(gStartingArmyPlan, cUnitStanceDefensive); aiPlanSetVariableInt(gStartingArmyPlan, cDefendPlanRefreshFrequency, 0, 5); aiPlanSetVariableInt(gStartingArmyPlan, cDefendPlanAttackTypeID, 0, cUnitTypeUnit); // Only units aiPlanSetDesiredPriority(gStartingArmyPlan, 100); // Very very low priority, gather unused units. aiPlanSetActive(gStartingArmyPlan); xsDisableSelf(); } //============================================================================== // startingArmNoMoreUnits // // After a while, make it so that the starting army plan can't have anymore // units. // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== rule startingArmNoMoreUnits active minInterval 10 { aiPlanSetNoMoreUnits(gStartingArmyPlan, true); //aiPlanSetActive(gStartingArmyPlan, false); xsDisableSelf(); } //============================================================================== // startingArmyPatrol // // Fake a patrol around the TC. // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== rule startingArmyPatrol active minInterval 12 maxInterval 18 { static int waypoint = 0; int enemyArmySize = numEnemiesInBase(cMyID, kbBaseGetMainID(cMyID)); if(enemyArmySize > 0) { aiPlanSetVariableVector(gStartingArmyPlan, cDefendPlanDefendPoint, 0, kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID))); return; } vector center = kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID)); vector testVec = center; switch(waypoint) { case 0: { testVec = xsVectorSetX(testVec, xsVectorGetX(center) + 25); testVec = xsVectorSetZ(testVec, xsVectorGetZ(center) - 25); aiPlanSetVariableVector(gStartingArmyPlan, cDefendPlanDefendPoint, 0, testVec); break; } case 1: { testVec = xsVectorSetX(testVec, xsVectorGetX(center) + 25); testVec = xsVectorSetZ(testVec, xsVectorGetZ(center) + 25); aiPlanSetVariableVector(gStartingArmyPlan, cDefendPlanDefendPoint, 0, testVec); break; } case 2: { testVec = xsVectorSetX(testVec, xsVectorGetX(center) - 25); testVec = xsVectorSetZ(testVec, xsVectorGetZ(center) + 25); aiPlanSetVariableVector(gStartingArmyPlan, cDefendPlanDefendPoint, 0, testVec); break; } case 3: { testVec = xsVectorSetX(testVec, xsVectorGetX(center) - 25); testVec = xsVectorSetZ(testVec, xsVectorGetZ(center) - 25); aiPlanSetVariableVector(gStartingArmyPlan, cDefendPlanDefendPoint, 0, testVec); break; } } waypoint = waypoint + 1; if(waypoint > 3) waypoint = 0; } //============================================================================== // adjustRuleIntervals // // Adjust the intervals of some original AI rules. // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== rule adjustRuleIntervals inactive minInterval 2 { xsSetRuleMinInterval("defenseReflex", 12 - (2 * btDifficulty)); int baseTime = 120; int humanAllies = getNumberOfAIsOnHumanTeam(); // increase the base attack time for AI allies, so that they can't win the game for you too quickly. if(kbGetPlayerTeam(cMyID) == 1) { baseTime = baseTime + (humanAllies * 180) + gRandomHumanAllyAttackDelay; } if(btStrategy != cTurtleStrategy) { xsSetRuleMinInterval("delayFirstAttack", baseTime + ((4 - btDifficulty) * 45) + gRandomAttackDelay); } else { // since we might potentially be capped at age 2, allow attacks after 12 minutes regardless xsSetRuleMinInterval("delayFirstAttack", 720); if(kbGetPlayerTeam(cMyID) == 1 && humanAllies < 2) { xsEnableRule("turtleBeginAttacks"); } } xsDisableSelf(); } //============================================================================== // rampUpLateGame // // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== rule rampUpLateGame active minInterval 900 { static int count = 0; if(btDifficulty == cUnstoppableDifficulty) { xsDisableSelf(); } if(count == 0) { if(btDifficulty == cWeakDifficulty) { xsSetRuleMinInterval("rampUpLateGame", 600); } else if(btDifficulty == cStandardDifficulty) { xsSetRuleMinInterval("rampUpLateGame", 300); } else if(btDifficulty == cStrongDifficulty) { xsSetRuleMinInterval("rampUpLateGame", 240); } else { xsSetRuleMinInterval("rampUpLateGame", 120); } } else if(count < 4) { if(btDifficulty == cWeakDifficulty) { gNumberOfTCs = gNumberOfTCs + 1; btTargetAge4VilCount = btTargetAge4VilCount + 1; xsArraySetInt(gTargetVillagerCounts, cAge4, btTargetAge4VilCount); btMilitaryCap = btMilitaryCap + 2; } else if(btDifficulty == cStandardDifficulty) { gNumberOfTCs = gNumberOfTCs + 1; btTargetAge4VilCount = btTargetAge4VilCount + 1; xsArraySetInt(gTargetVillagerCounts, cAge4, btTargetAge4VilCount); btMilitaryCap = btMilitaryCap + 4; } else if(btDifficulty == cStrongDifficulty) { gNumberOfTCs = gNumberOfTCs + 1; btTargetAge4VilCount = btTargetAge4VilCount + 2; xsArraySetInt(gTargetVillagerCounts, cAge4, btTargetAge4VilCount); btMilitaryCap = btMilitaryCap + 6; } else { btTargetAge4VilCount = btTargetAge4VilCount + 2; xsArraySetInt(gTargetVillagerCounts, cAge4, btTargetAge4VilCount); btMilitaryCap = btMilitaryCap + 8; } aiEcho("rampUpLateGame: updating number of TCs"); } else { btMilitaryCap = 200 - btTargetAge4VilCount; aiEcho("rampUpLateGame: setting military count to max. GO HOG WILD!"); xsDisableSelf(); aiEcho("rampUpLateGame: disabling self"); } count = count + 1; } //============================================================================== // setupStandardBuildingInfo // // Set up a bunch of things that every focus should be building. // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== int setupStandardBuildingInfo(int offset=0, int currentVillCount=0) { int bt = 0; switch (kbGetAge()) { case cAge1: { if(btHighStartingResources) { bt = kbFindBuilding(cUnitTypeUnitTypeBldgBarracks); offset = addBuildingToList(bt, 3, cEconomyEscrowID, cAgeStateEarly, true, offset); } break; } case cAge2: { if(btDifficulty > cWeakDifficulty) { bt = kbFindBuilding(cUnitTypeUnitTypeBldgArmory); offset = addBuildingToList(bt, 1, cMilitaryEscrowID, cAgeStateLate, true, offset); // get celts to build a sacred grove for deer and druids if(kbGetCiv() == gCelticCivID) { bt = kbFindBuilding(cUnitTypeUnitTypeBldgTemple); offset = addBuildingToList(bt, 1, cMilitaryEscrowID, cAgeStateMid, true, offset); } else if(kbGetCiv() == gBabylonianCivID) { bt = kbFindBuilding(cUnitTypeBaBldgGarden); offset = addBuildingToList(bt, 1, cEconomyEscrowID, cAgeStateMid, true, offset); } } // wait 10 minutes into age 2 to build a market bt = kbFindBuilding(cUnitTypeUnitTypeBldgMarket); offset = addBuildingToList(bt, 1, cEconomyEscrowID, cAgeStateLate, true, offset); bt = kbFindBuilding(cUnitTypeUnitTypeBldgTownCenter); offset = addBuildingToList(bt, 1, cEconomyEscrowID, cAgeStateEarly, true, offset); break; } default: { if(cBuildingWonder) { offset = addBuildingToList(kbFindBuilding(cUnitTypeUnitTypeBldgWonder), 1, cMilitaryEscrowID, cAgeStateEarly, true, offset); } if(btDifficulty > cStrongDifficulty) { if(btStrategy == cTurtleStrategy) { bt = kbFindBuilding(cUnitTypeUnitTypeBldgFortress); offset = addBuildingToList(bt, currentVillCount / 15, cMilitaryEscrowID, cAgeStateEarly, true, offset); } else { bt = kbFindBuilding(cUnitTypeUnitTypeBldgFortress); offset = addBuildingToList(bt, currentVillCount / 25, cMilitaryEscrowID, cAgeStateMid, true, offset); } bt = kbFindBuilding(cUnitTypeUnitTypeBldgAcademy); offset = addBuildingToList(bt, 1, cMilitaryEscrowID, cAgeStateEarly, true, offset); bt = kbFindBuilding(cUnitTypeUnitTypeBldgArmory); offset = addBuildingToList(bt, 1, cMilitaryEscrowID, cAgeStateLate, true, offset); int popcap = kbGetPopCap(); if(popcap >= 120 && btVictoryCondition != cWonderVictory) { int tc = (popcap - 100) / 20; if(tc > 3) tc = 3; bt = kbFindBuilding(cUnitTypeUnitTypeBldgTownCenter); offset = addBuildingToList(bt, tc + 1, cMilitaryEscrowID, cAgeStateEarly, true, offset); } else { bt = kbFindBuilding(cUnitTypeUnitTypeBldgTownCenter); offset = addBuildingToList(bt, 1, cMilitaryEscrowID, cAgeStateMid, true, offset); } } else { if(btDifficulty > cWeakDifficulty) { bt = kbFindBuilding(cUnitTypeUnitTypeBldgFortress); offset = addBuildingToList(bt, 1, cMilitaryEscrowID, cAgeStateMid, true, offset); bt = kbFindBuilding(cUnitTypeUnitTypeBldgArmory); offset = addBuildingToList(bt, 1, cMilitaryEscrowID, cAgeStateLate, true, offset); } bt = kbFindBuilding(cUnitTypeUnitTypeBldgTownCenter); offset = addBuildingToList(bt, 1 + gNumberOfTCs, cMilitaryEscrowID, cAgeStateMid, true, offset); } if(kbGetCiv() == gCelticCivID) { bt = kbFindBuilding(cUnitTypeUnitTypeBldgTemple); offset = addBuildingToList(bt, 1, cMilitaryEscrowID, cAgeStateMid, true, offset); } else if(kbGetCiv() == gBabylonianCivID) { bt = kbFindBuilding(cUnitTypeBaBldgGarden); offset = addBuildingToList(bt, currentVillCount / 15, cEconomyEscrowID, cAgeStateMid, true, offset); } bt = kbFindBuilding(cUnitTypeUnitTypeBldgMarket); offset = addBuildingToList(bt, 1, cEconomyEscrowID, cAgeStateMid, true, offset); break; } } return(offset); } //============================================================================== // setUpBalancedBuildOrder // // Balanced build order shared by more than one AI focus script. // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== void setUpBalancedBuildOrder() { int currentVillCount = getVillagerCountForBuildOrder(); int bt = 0; int offset = 0; switch (kbGetAge()) { case cAge1: { if(btDifficulty > cStrongDifficulty) { bt = kbFindBuilding(cUnitTypeUnitTypeBldgBarracks); offset = addBuildingToList(bt, 1, cEconomyEscrowID, cAgeStateEarly, true, offset); } break; } case cAge2: { if(btDifficulty > cStrongDifficulty) { bt = kbFindBuilding(cUnitTypeUnitTypeBldgBarracks); offset = addBuildingToList(bt, currentVillCount / 15, cMilitaryEscrowID, cAgeStateEarly, true, offset); if(kbGetCiv() != gEgyptianCivID) { bt = kbFindBuilding(cUnitTypeUnitTypeBldgArcheryRange); offset = addBuildingToList(bt, currentVillCount / 15, cMilitaryEscrowID, cAgeStateEarly, true, offset); } bt = kbFindBuilding(cUnitTypeUnitTypeBldgStables); offset = addBuildingToList(bt, currentVillCount / 15, cMilitaryEscrowID, cAgeStateEarly, true, offset); } else if(btDifficulty > cWeakDifficulty) { bt = kbFindBuilding(cUnitTypeUnitTypeBldgBarracks); offset = addBuildingToList(bt, currentVillCount / 20, cMilitaryEscrowID, cAgeStateEarly, true, offset); if(kbGetCiv() != gEgyptianCivID) { bt = kbFindBuilding(cUnitTypeUnitTypeBldgArcheryRange); offset = addBuildingToList(bt, currentVillCount / 20, cMilitaryEscrowID, cAgeStateEarly, true, offset); } bt = kbFindBuilding(cUnitTypeUnitTypeBldgStables); offset = addBuildingToList(bt, currentVillCount / 20, cMilitaryEscrowID, cAgeStateEarly, true, offset); } else { bt = kbFindBuilding(cUnitTypeUnitTypeBldgBarracks); offset = addBuildingToList(bt, 1, cMilitaryEscrowID, cAgeStateEarly, true, offset); if(kbGetCiv() != gEgyptianCivID) { bt = kbFindBuilding(cUnitTypeUnitTypeBldgArcheryRange); offset = addBuildingToList(bt, 1, cMilitaryEscrowID, cAgeStateMid, true, offset); } bt = kbFindBuilding(cUnitTypeUnitTypeBldgStables); offset = addBuildingToList(bt, 1, cMilitaryEscrowID, cAgeStateMid, true, offset); } break; } default: { if(btDifficulty > cStrongDifficulty) { bt = kbFindBuilding(cUnitTypeUnitTypeBldgBarracks); offset = addBuildingToList(bt, currentVillCount / 15, cMilitaryEscrowID, cAgeStateEarly, true, offset); if(kbGetCiv() != gEgyptianCivID) { bt = kbFindBuilding(cUnitTypeUnitTypeBldgArcheryRange); offset = addBuildingToList(bt, currentVillCount / 15, cMilitaryEscrowID, cAgeStateEarly, true, offset); } // ensure some immortal camps get placed for Persians if(kbGetCiv() == gPersianCivID) { bt = kbFindBuilding(cUnitTypeUnitTypeBldgTemple); offset = addBuildingToList(bt, currentVillCount / 15, cMilitaryEscrowID, cAgeStateEarly, true, offset); } bt = kbFindBuilding(cUnitTypeUnitTypeBldgStables); offset = addBuildingToList(bt, currentVillCount / 15, cMilitaryEscrowID, cAgeStateEarly, true, offset); bt = kbFindBuilding(cUnitTypeUnitTypeBldgSiegeWorkshop); offset = addBuildingToList(bt, currentVillCount / 20, cMilitaryEscrowID, cAgeStateMid, true, offset); } else if(btDifficulty > cWeakDifficulty) { bt = kbFindBuilding(cUnitTypeUnitTypeBldgBarracks); offset = addBuildingToList(bt, currentVillCount / 20, cMilitaryEscrowID, cAgeStateEarly, true, offset); if(kbGetCiv() != gEgyptianCivID) { bt = kbFindBuilding(cUnitTypeUnitTypeBldgArcheryRange); offset = addBuildingToList(bt, currentVillCount / 20, cMilitaryEscrowID, cAgeStateEarly, true, offset); } bt = kbFindBuilding(cUnitTypeUnitTypeBldgStables); offset = addBuildingToList(bt, currentVillCount / 20, cMilitaryEscrowID, cAgeStateEarly, true, offset); bt = kbFindBuilding(cUnitTypeUnitTypeBldgSiegeWorkshop); offset = addBuildingToList(bt, currentVillCount / 25, cMilitaryEscrowID, cAgeStateLate, true, offset); } else { bt = kbFindBuilding(cUnitTypeUnitTypeBldgBarracks); offset = addBuildingToList(bt, 1, cMilitaryEscrowID, cAgeStateEarly, true, offset); if(kbGetCiv() != gEgyptianCivID) { bt = kbFindBuilding(cUnitTypeUnitTypeBldgArcheryRange); offset = addBuildingToList(bt, 1, cMilitaryEscrowID, cAgeStateMid, true, offset); } bt = kbFindBuilding(cUnitTypeUnitTypeBldgStables); offset = addBuildingToList(bt, 1, cMilitaryEscrowID, cAgeStateMid, true, offset); bt = kbFindBuilding(cUnitTypeUnitTypeBldgSiegeWorkshop); offset = addBuildingToList(bt, 1, cMilitaryEscrowID, cAgeStateLate, true, offset); } break; } } setupStandardBuildingInfo(offset, currentVillCount); } //============================================================================== // increaseArmyCountForHumanAllies // // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== rule increaseArmyCountForHumanAllies active minInterval 17 { static bool first = true; static float oldSiegeBias = 0.0; if(kbGetPlayerTeam(cMyID) != 1) { return; } if(first == true) { // force siege bias to be low oldSiegeBias = btBiasArt; btBiasArt = -0.75; setUnitPickerPreference(gLandUnitPicker); first = false; } bool increase = true; for (i = 0; < cNumberPlayers) { if(kbGetPlayerTeam(i) == 1 && kbIsPlayerHuman(i) == true) { int count = kbUnitCount(i, cUnitTypeLogicalTypeLandPickerChoice, cUnitStateAlive); aiEcho("HUMAN " + i + " HAS " + count + " UNITS"); if(count < 30) { increase = false; } } } if(increase == true) { btTargetAge1ArmyCount = 6; btTargetAge2ArmyCount = 4 + (2 * btDifficulty); btTargetAge3ArmyCount = 6 + (4 * btDifficulty); btTargetAge4ArmyCount = 12 + (7 * btDifficulty); updateMilitarySize(); btBiasArt = oldSiegeBias; setUnitPickerPreference(gLandUnitPicker); gAttackMissionInterval = gAttackMissionInterval - (90 * getNumberOfAIsOnHumanTeam()); cvOkToAttack = true; xsDisableSelf(); } } //============================================================================== // skirmishPostInit // // Set up things that all skirmish focuses have in common // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== void skirmishPostInit() { setVillagerCap(); xsEnableRule("adjustRuleIntervals"); if(kbGetCiv() == gNorseCivID) { createSimpleMaintainPlan(kbFindUnit(cUnitTypeNoInfChief), 1, false); } } //============================================================================== // setDefaultAIVariables // // Set a bunch of variables that end up being more or less the same between // all focus scripts. // BF: 2/18/2012: this does not need to migrate outside Skirmish //============================================================================== void setDefaultAIVariables() { //if(kbGetPlayerTeam(cMyID) == 1) //{ // int humanAllies = getNumberOfAIsOnHumanTeam(); // btTargetAge2ArmyCount = btDifficulty + (3 - humanAllies); // btTargetAge3ArmyCount = btDifficulty + (10 - (humanAllies * 2)); // btTargetAge4ArmyCount = btDifficulty + (13 - (humanAllies * 3)); //} //else //{ btTargetAge1ArmyCount = 6; btTargetAge2ArmyCount = 4 + (2 * btDifficulty); btTargetAge3ArmyCount = 6 + (4 * btDifficulty); btTargetAge4ArmyCount = 12 + (7 * btDifficulty); //} btAttackRampUp = 10 - btDifficulty; btAttackGroups = 1 + btDifficulty; btMilitaryCap = 33 + (23 * btDifficulty); btMilitaryInAge1 = false; // MCC 12/9/2011: I only want this to happen for difficulties strong and higher, but I don't // have access to the skirmish difficulty from here, so I'm checking the siegeBias, which is // what determines the difficulty. btBuild2ndDockAge2 = btDifficulty > 1; btTargetAge1VilCount = 12 + btDifficulty; btTargetAge4VilCount = 43 + (8 * btDifficulty); cvMaxAge = 4 - 1; if(btDifficulty > 1) cvGuardAlliedBases = true; else cvGuardAlliedBases = false; gAge1UnitLineCap = 1; gAge2UnitLineCap = 1 + (btDifficulty / 2); gAge3UnitLineCap = 3 + (btDifficulty / 2); gAge4UnitLineCap = 5 + (btDifficulty / 2); setEconHandicapFromDifficulty(); btBiasNavy = aiGetNavyBias(); btRevealEnemyBases = aiGetOKToAutoReveal(); cvOkToAttack = false; cvOkToTrainArmy = aiGetOKToTrainArmy(); cvOkToTrainNavy = aiGetOKToTrainNavy(); cvOkToFish = aiGetOKToFish(); cvOkToResign = aiGetOKToResign(); cvOkToExplore = aiGetOKToScout(); cvOKToResearchEcon = aiGetOKToResearchEcon(); cvOKToResearchMilitary = aiGetOKToResearchMilitary(); // add towers and increase attack interval for turtle strategy if(btStrategy == cTurtleStrategy) { cvNumTowers = 3 * (btDifficulty + 1); cvOkToAttack = false; } else { xsEnableRule("setRandomTowerCount"); } // updating the extra transport count if the map is hardcoded to require transports if (aiIsMapType("AITransportRequired")) { gExtraTransportCount = 3; } // MCC 9/18/2012 Setting the hate threshold so that the AI will use the new method to update // its most hated enemy. gPlayerHateThreshold = 1.5; } //============================================================================== // initGatherGoal() // // Setup the initial gathering plan for the vills // This SKIRMISH HALL override sets specific target selector values //============================================================================== 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.2, false, cRGPScript); 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.8, kbBaseGetMainID(cMyID)); // All on easy and huntable food at start aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeHunt, numFoodHuntEasyPlans, 51, 0.2, kbBaseGetMainID(cMyID)); 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)); } //============================================================================== // updateMaxGatherDistance() // // This override uses the non-adjusted values for MaximumResourceDistance //============================================================================== mutable void updateMaxGatherDistance() { // Update our max gather distance int age = kbGetAge(); if (age == cvMaxAge) age = cAge4; switch (age) { case cAge1: { kbBaseSetMaximumResourceDistance(cMyID, kbBaseGetMainID(cMyID), 70.0); aiSetMaxGatherDistance(70.0); break; } case cAge2: { kbBaseSetMaximumResourceDistance(cMyID, kbBaseGetMainID(cMyID), 100.0); aiSetMaxGatherDistance(100.0); break; } case cAge3: { kbBaseSetMaximumResourceDistance(cMyID, kbBaseGetMainID(cMyID), 130.0); aiSetMaxGatherDistance(130.0); break; } case cAge4: { kbBaseSetMaximumResourceDistance(cMyID, kbBaseGetMainID(cMyID), 160.0); aiSetMaxGatherDistance(160.0); break; } } aiSetExploreDangerThreshold(150.0); } //============================================================================== // outpostManager // // A Norse-specific rule to build outposts and train dogs. //============================================================================== rule outpostManager active minInterval 180 { if(kbGetCiv() != gNorseCivID || gNavyMap == true || gNumOutposts >= 6) { aiEcho("outpostManager: disabling self"); xsDisableSelf(); return; } if (gResourceQuery == -1) { gResourceQuery = kbUnitQueryCreate("NorseResourceQuery"); kbUnitQuerySetPlayerRelation(gResourceQuery, cPlayerRelationAny); kbUnitQuerySetUnitType(gResourceQuery, cUnitTypeAbstractMine); kbUnitQuerySetState(gResourceQuery, cUnitStateAny); } if (gOutpostQuery == -1) { gOutpostQuery = kbUnitQueryCreate("NorseOutpostQuery"); kbUnitQuerySetPlayerRelation(gOutpostQuery, cPlayerRelationAny); kbUnitQuerySetUnitType(gOutpostQuery, cUnitTypeUnitTypeBldgWatchPost); kbUnitQuerySetState(gOutpostQuery, cUnitStateAlive); } vector baseLoc = kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID)); kbUnitQuerySetPosition(gResourceQuery, baseLoc); //kbUnitQuerySetMaximumDistance(gResourceQuery, MaxPatrolDistance); kbUnitQueryResetResults(gResourceQuery); int mineCount = kbUnitQueryExecute(gResourceQuery); float closestDistance = 5555.0; int closestID = -1; for(i = 0; < mineCount) { int id = kbUnitQueryGetResult(gResourceQuery, i); float dist = xsVectorLength(kbUnitGetPosition(id) - baseLoc); kbUnitQuerySetPosition(gOutpostQuery, kbUnitGetPosition(id)); kbUnitQuerySetMaximumDistance(gOutpostQuery, 20.0); kbUnitQueryResetResults(gOutpostQuery); if(dist < closestDistance && dist > 30.0 && kbUnitQueryExecute(gOutpostQuery) == 0) { closestDistance = dist; closestID = id; } } if(closestID != -1) { aiEcho("outpostManager: ID " + closestID); int buildPlan = aiPlanCreate("Outpost Build Plan", cPlanBuild); aiPlanSetVariableInt(buildPlan, cBuildPlanBuildingTypeID, 0, kbFindBuilding(cUnitTypeUnitTypeBldgWatchPost)); aiPlanAddUnitType(buildPlan, cUnitTypeAbstractInfantry, 1, 2, 3); aiPlanSetVariableVector(buildPlan, cBuildPlanCenterPosition, 0, kbUnitGetPosition(closestID)); aiPlanSetVariableFloat(buildPlan, cBuildPlanCenterPositionDistance, 0, 15.0); aiPlanSetActive(buildPlan, true); gCurrentOutpostResourceID = closestID; } } rule dogPlanManager active minInterval 10 { if(kbGetCiv() != gNorseCivID || gNavyMap == true) { xsDisableSelf(); return; } if(gCurrentOutpostResourceID != -1) { vector resLoc = kbUnitGetPosition(gCurrentOutpostResourceID); kbUnitQuerySetPosition(gOutpostQuery, resLoc); kbUnitQuerySetMaximumDistance(gOutpostQuery, 20.0); kbUnitQueryResetResults(gOutpostQuery); if(kbUnitQueryExecute(gOutpostQuery) > 0) { int outpost = kbUnitQueryGetResult(gOutpostQuery, 0); int defensePlan = aiPlanCreate("DogDefense" + gNumOutposts, cPlanDefend); aiPlanSetUnitStance(defensePlan, cUnitStanceDefensive); aiPlanSetVariableFloat(defensePlan, cDefendPlanEngageRange, 0, 10); aiPlanSetVariableFloat(defensePlan, cDefendPlanGatherDistance, 0, 10); aiPlanAddUnitType(defensePlan, cUnitTypeNoCavWarDog, 4, 4, 4); aiPlanSetVariableVector(defensePlan, cDefendPlanDefendPoint, 0, resLoc); aiPlanSetActive(defensePlan, true); int trainPlan = aiPlanCreate("DogTrain" + gNumOutposts, cPlanTrain); aiPlanSetVariableInt(trainPlan, cTrainPlanUnitType, 0, kbFindUnit(cUnitTypeNoCavWarDog)); aiPlanSetVariableInt(trainPlan, cTrainPlanNumberToMaintain, 0, 4/* * (gNumOutposts + 1)*/); //aiPlanSetVariableInt(trainPlan, cTrainPlanNumberToTrain, 0, 4); aiPlanSetVariableInt(trainPlan, cTrainPlanIntoPlanID, 0, defensePlan); aiPlanSetVariableInt(trainPlan, cTrainPlanBatchSize, 0, 1); aiPlanSetVariableInt(trainPlan, cTrainPlanBuildingID, 0, outpost); aiPlanSetVariableBool(trainPlan, cTrainPlanUseMultipleBuildings, 0, false); aiPlanSetVariableBool(trainPlan, cTrainPlanCountExisting, 0, false); aiPlanSetActive(trainPlan, true); if(gDogDefendPlanArray == -1) { gDogDefendPlanArray = xsArrayCreateInt(6, -1, "DogPlans"); gDogDefendLocationArray = xsArrayCreateVector(6, cInvalidVector, "DogLocs"); } xsArraySetInt(gDogDefendPlanArray, gNumOutposts, defensePlan); xsArraySetVector(gDogDefendLocationArray, gNumOutposts, resLoc); gCurrentOutpostResourceID = -1; gNumOutposts = gNumOutposts + 1; } } } rule dogPlanUpdate active minInterval 9 { if(kbGetCiv() != gNorseCivID || gNavyMap == true) { xsDisableSelf(); return; } if(gDogDefendPlanArray != -1) { for(i = 0; < 20) { vector loc = xsArrayGetVector(gDogDefendLocationArray, i); int plan = xsArrayGetInt(gDogDefendPlanArray, i); if(plan != -1) { switch(gDogDefendPatrolIndex) { case 0: { loc = xsVectorSetX(loc, xsVectorGetX(loc) + 12); loc = xsVectorSetZ(loc, xsVectorGetZ(loc) - 12); break; } case 1: { loc = xsVectorSetX(loc, xsVectorGetX(loc) + 12); loc = xsVectorSetZ(loc, xsVectorGetZ(loc) + 12); break; } case 2: { loc = xsVectorSetX(loc, xsVectorGetX(loc) - 12); loc = xsVectorSetZ(loc, xsVectorGetZ(loc) + 12); break; } case 3: { loc = xsVectorSetX(loc, xsVectorGetX(loc) - 12); loc = xsVectorSetZ(loc, xsVectorGetZ(loc) - 12); break; } } aiPlanSetVariableVector(plan, cDefendPlanDefendPoint, 0, loc); } } gDogDefendPatrolIndex = gDogDefendPatrolIndex + 1; if(gDogDefendPatrolIndex > 3) { gDogDefendPatrolIndex = 0; } } }