//============================================================================== /* aiNavy.xs Contains the naval military functionality for the AI */ //============================================================================== //============================================================================== //============================================================================== extern int gWaterExplorePlan = -1; // Plan to explore the water extern int gWaterExploreMaintain = -1; // Fishing boat maintenance plan extern int gFishingPlan = -1; extern int gFishingBoatMaintainPlan = -1; extern int gNumFishBoats = 2; // Number of fishing boats desired by the AI. extern int gNavyDefendPlan = -1; // General naval defense plan extern int gNavyAttackPlan = -1; // General naval attack plan extern int gTriremeMaintain = -1; // Maintain plans for naval units. extern int gFireshipMaintain = -1; extern int gSiegeshipMaintain = -1; extern int gTransportMaintain = -1; extern bool gEnableNavyPlan = false; // Horrible awful hack way to turn on the navy from the fishing plan extern bool gNavyInitialized = false; extern int gExtraTransportCount = 0; extern int gTriremeCount = 5; extern int gFireshipCount = 5; extern int gSiegeshipCount = 5; extern int gEnemyBoatsQuery = -1; extern int gWaterAreaID = -1; // The water area we're attempting to build the dock on extern vector gNavyVec = cInvalidVector; // Starting position for navy--place dock here, explore from here, etc. extern bool gUseCustomNavyVec = false; // when true, don't update the navy vec unless it is invalid extern float gNavyDefendGatherDist = 40.0; extern float gNavyAttackGatherDist = 30.0; // Set the unit types to use extern int gNavalMilitaryUnitType = cUnitTypeAbstractWarShip; // was cUnitTypeLogicalTypeNavalMilitary extern int gFishingBoatUnitType = cUnitTypeUnitTypeShipFishing1; // was cUnitTypeFishingBoat extern vector gLastDockLocation = cInvalidVector; //============================================================================== // Update the main navy location, updating any plans that use it // // BF 2/18/2012: Updated from Skirmish: // general improvements to the navy vec location calculations //============================================================================== void updateMainNavyLocationToUnitArea(int unitID = -1) { if (unitID < 0) return; if ( (gUseCustomNavyVec == false) || (gNavyVec == cInvalidVector) ) { // Update water area / navy location gNavyVec = kbUnitGetPosition(unitID); vector center = kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID)); // Start with base location aiEcho("AINavy: --- Base loc[" + center + "]"); aiEcho("AINavy: --- Dock loc[" + gNavyVec + "]"); vector diff = gNavyVec - center; diff = xsVectorNormalize(diff); aiEcho("AINavy: --- Dock dir[" + diff + "]"); gNavyVec = xsVectorSetX(gNavyVec, xsVectorGetX(gNavyVec) + (xsVectorGetX(diff) * 30)); gNavyVec = xsVectorSetZ(gNavyVec, xsVectorGetZ(gNavyVec) + (xsVectorGetZ(diff) * 30)); aiEcho("AINavy: --- gNavyVec[" + gNavyVec + "]"); } // Update existing plans that use the main navy location if (gNavyDefendPlan >= 0) { aiPlanSetVariableVector(gNavyDefendPlan, cDefendPlanDefendPoint, 0, gNavyVec); aiPlanSetInitialPosition(gNavyDefendPlan, gNavyVec); aiEcho("AINavy: --- Updating primary navy defend plan to [" + gNavyVec + "]"); } if (gWaterExplorePlan >= 0) { aiPlanSetInitialPosition(gWaterExplorePlan, gNavyVec); aiEcho("AINavy: --- Updating water explore plan to [" + gNavyVec + "]"); } } //============================================================================== // rule startFishing // // BF 2/18/2012: Updated from Skirmish: // stripped commented out code and reduced complexity //============================================================================== rule startFishing inactive mininterval 15 { // TODO - substitute for HomeCityWaterSpawnFlag sections below if(gGoodFishingMap == false) { aiEcho("AINavy: --- Not a good fishing map, disabling fishing."); xsDisableSelf(); return; } // If a fishing plan has already been made, this shouldn't be firing anymore, bail out if (gFishingPlan >= 0) { aiEcho("AINavy: --- *** Attemped start fishing when there's already a fishing plan ***"); xsDisableSelf(); return; } // evaluate starting and desired fishing boats aiEcho("AINavy: --- gNumFishBoats [" + gNumFishBoats + "]"); if (gNumFishBoats <= 0) { //We weren't given any, and don't plan on making any, so quit. return; } // Start up fishing aiEcho("AINavy: --- gGoodFishingMap[" + gGoodFishingMap + "]"); if ((cvOkToFish == true) && (gGoodFishingMap == true)) { // need to have one fish visible int fish = getUnit(cUnitTypeAbstractFish, 0, cUnitStateAny); aiEcho("AINavy: --- located fish[" + fish + "]"); if (fish <= 0) { return; } // Find the closest dock for a starting location int closestDock = getUnitByLocation(cUnitTypeUnitTypeBldgDock, cMyID, cUnitStateAlive, gNavyVec, 500.0, true); if (closestDock < 0) closestDock = getUnit(cUnitTypeUnitTypeBldgDock, cMyID, cUnitStateAlive); int dockType = kbFindBuilding(cUnitTypeUnitTypeBldgDock); if(closestDock < 0 && aiPlanGetIDByTypeAndVariableType(cPlanBuild, cBuildPlanBuildingTypeID, dockType) != -1) { aiEcho("AINavy: --- Waiting for a dock to be built"); return; } // Update the main navy location to this dock if not already done updateMainNavyLocationToUnitArea(closestDock); // Start fishing plan, with dock building included, use an intentionally low priority gFishingPlan = aiPlanCreate("Fishing plan", cPlanFish); aiEcho("AINavy: --- Starting fishing plan[" + gFishingPlan + "]"); aiPlanSetInitialPosition(gFishingPlan, gNavyVec); aiPlanSetDesiredPriority(gFishingPlan, 20); aiPlanAddUnitType(gFishingPlan, gFishingBoatUnitType, 1, 10, 200); aiPlanSetEscrowID(gFishingPlan, cEconomyEscrowID); aiPlanSetBaseID(gFishingPlan, kbBaseGetMainID(cMyID)); aiPlanSetVariableVector(gFishingPlan, cFishPlanLandPoint, 0, kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID))); aiPlanSetActive(gFishingPlan); // Get the baseID near the dock int baseID = kbUnitGetBaseID(closestDock); if (baseID < 0) { baseID = kbBaseGetMainID(cMyID); } // Create maintain plan for fishing boats gFishingBoatMaintainPlan = createSimpleMaintainPlan(kbFindUnit(gFishingBoatUnitType), gNumFishBoats, true, baseID, 1); aiEcho("AINavy: --- Creating maintain plan for fishing boats[" + gFishingBoatMaintainPlan + "]"); // Start up a water exploration plan if there isn't one already if(gWaterExplorePlan < 0) { vector location = kbUnitGetPosition(fish); gWaterExplorePlan=aiPlanCreate("Water Explore", cPlanExplore); aiPlanSetVariableBool(gWaterExplorePlan, cExplorePlanReExploreAreas, 0, false); aiPlanSetInitialPosition(gWaterExplorePlan, location); // set a low priority, so that transport plans can steal it as needed, but just above fishing plans. aiPlanSetDesiredPriority(gWaterExplorePlan, 45); aiPlanAddUnitType(gWaterExplorePlan, kbFindUnit(gFishingBoatUnitType), 1, 1, 1); aiPlanSetEscrowID(gWaterExplorePlan, cEconomyEscrowID); aiPlanSetVariableBool(gWaterExplorePlan, cExplorePlanDoLoops, 0, false); aiPlanSetActive(gWaterExplorePlan); } } // Done setting up fishing, turn off rule if (cvOkToFish == true) { // Normally, disable if we get here because we're done. // But if okToFish is false, keep rule active in case it gets turned true later. aiEcho("AINavy: --- Done with fishing plan setup - disabling rule"); xsDisableSelf(); } } //============================================================================== // getNavalTargetPlayer // // Find an enemy player ID to attack on the water. // Unit queries the docks, fishing boats, and warships to see what enemy // player is the biggest naval threat. //============================================================================== int getNavalTargetPlayer() { int count = 0; int retVal = -1; static int unitQueryID = -1; //If we don't have the query yet, create one. if (unitQueryID < 0) { unitQueryID=kbUnitQueryCreate("navy target count"); kbUnitQuerySetIgnoreKnockedOutUnits(unitQueryID, true); kbUnitQuerySetPlayerRelation(unitQueryID, cPlayerRelationEnemyNotGaia); } // Fishing boats kbUnitQuerySetUnitType(unitQueryID, gFishingBoatUnitType); kbUnitQuerySetState(unitQueryID, cUnitStateABQ); kbUnitQueryResetResults(unitQueryID); count = kbUnitQueryExecute(unitQueryID); // Warships kbUnitQuerySetUnitType(unitQueryID, gNavalMilitaryUnitType); kbUnitQuerySetState(unitQueryID, cUnitStateABQ); count = kbUnitQueryExecute(unitQueryID); // Cumulative, don't clear it. // Docks kbUnitQuerySetUnitType(unitQueryID, cUnitTypeUnitTypeBldgDock); kbUnitQuerySetState(unitQueryID, cUnitStateABQ); count = kbUnitQueryExecute(unitQueryID); // Cumulative, don't clear it. if (count > 0) retVal = kbUnitGetPlayerID(kbUnitQueryGetResult(unitQueryID,0)); return(retVal); } //============================================================================== // rule waterDefend // // Creates defend plans for naval units // //============================================================================== rule waterDefend inactive minInterval 15 { if(isCurrentMapNaval() == false) { aiEcho("AINavy: --- waterDefend: this isn't a water map"); xsDisableSelf(); return; } if (cvOKToDefend == false) { aiEcho("AINavy: --- waterDefend: Not allowed to defend with navy"); return; } // Get first naval unit position as the main naval position. This should probably do something smarter. int navyUnit = getUnit(gNavalMilitaryUnitType, cMyID, cUnitStateAlive); if (navyUnit < 0) return; // Init main navy location if not already done if (gNavyVec == cInvalidVector) { int closestDock = getUnit(cUnitTypeUnitTypeBldgDock, cMyID, cUnitStateAlive); updateMainNavyLocationToUnitArea(closestDock); aiEcho("AINavy: --- waterDefend: Setting navy location to unitPos in waterDefend rule gNavyVec[" + gNavyVec + "]"); } // Create defense plan if (gNavyDefendPlan < 0) { gNavyDefendPlan = aiPlanCreate("Primary Water Defend", cPlanDefend); aiPlanAddUnitType(gNavyDefendPlan, gNavalMilitaryUnitType, 1, 1, 200); aiPlanSetVariableVector(gNavyDefendPlan, cDefendPlanDefendPoint, 0, gNavyVec); aiPlanSetVariableFloat(gNavyDefendPlan, cDefendPlanEngageRange, 0, 100.0); // Loose aiPlanSetVariableBool(gNavyDefendPlan, cDefendPlanPatrol, 0, false); aiPlanSetVariableFloat(gNavyDefendPlan, cDefendPlanGatherDistance, 0, gNavyDefendGatherDist); aiPlanSetInitialPosition(gNavyDefendPlan, gNavyVec); aiPlanSetUnitStance(gNavyDefendPlan, cUnitStanceDefensive); aiPlanSetVariableInt(gNavyDefendPlan, cDefendPlanRefreshFrequency, 0, 20); aiPlanSetVariableInt(gNavyDefendPlan, cDefendPlanAttackTypeID, 0, cUnitTypeUnit); // Only units aiPlanSetDesiredPriority(gNavyDefendPlan, 20); // Very low priority, gather unused units. aiPlanSetActive(gNavyDefendPlan); aiEcho("AINavy: --- waterDefend: Creating primary navy defend plan at[" + gNavyVec + "]"); } } //============================================================================== // rule waterAttack // // Creates attack plans for naval units // //============================================================================== rule waterAttack inactive minInterval 17 { if(isCurrentMapNaval() == false) { aiEcho("AINavy: --- waterAttack: this isn't a water map"); xsDisableSelf(); return; } if (cvOkToAttack == false) { aiEcho("AINavy: --- waterAttack: Not allowed to attack with navy"); return; } // adjust the interval time for this rule based on the attack interval for the AI int interval = gAttackMissionInterval/500; xsSetRuleMinIntervalSelf(interval); // decide if an attack is warranted int navyEnemyPlayer = getNavalTargetPlayer(); int enemyNavySize = kbUnitCount(navyEnemyPlayer, gNavalMilitaryUnitType, cUnitStateAlive); int ourNavySize = aiPlanGetNumberUnits(gNavyDefendPlan, gNavalMilitaryUnitType); aiEcho("AINavy: --- waterAttack: ourNavySize[" + ourNavySize + "] enemyNavySize[" + enemyNavySize + "] interval[" + interval + "]"); if ( (ourNavySize >= 3) && (enemyNavySize >= 3) ) { // derive the attack size based on our attack settings and navy bias float adjustedBiasScale = (0.5 + (btBiasNavy / 2.0)); int navyAttackSize = ( kbGetAge() * (btTargetAge4ArmyCount * btAttackGroups)/10 * adjustedBiasScale ); // dont bother trying to build an attack greater than the number of boats we currently have if (navyAttackSize > ourNavySize) { navyAttackSize = ourNavySize; } // constrain the size based on the player naval size if (navyAttackSize > enemyNavySize) { navyAttackSize = enemyNavySize * adjustedBiasScale; // send at least one boat if (navyAttackSize <= 0 ) { navyAttackSize = 1; } } // Time to start an attack? if (getNavalTargetPlayer() > 0) { // There's something to attack int attackPlan = aiPlanCreate("Navy attack plan", cPlanAttack); aiPlanSetVariableInt(attackPlan, cAttackPlanPlayerID, 0, getNavalTargetPlayer()); aiPlanSetNumberVariableValues(attackPlan, cAttackPlanTargetTypeID, 2, true); aiPlanSetVariableInt(attackPlan, cAttackPlanTargetTypeID, 0, cUnitTypeUnit); aiPlanSetVariableInt(attackPlan, cAttackPlanTargetTypeID, 1, cUnitTypeUnitTypeBldgDock); aiPlanSetVariableVector(attackPlan, cAttackPlanGatherPoint, 0, gNavyVec); aiPlanSetVariableFloat(attackPlan, cAttackPlanGatherDistance, 0, gNavyAttackGatherDist); aiPlanSetVariableInt(attackPlan, cAttackPlanRefreshFrequency, 0, 5); // Set priority to above defend, fishing. Below explore. aiPlanSetDesiredPriority(attackPlan, 90); aiPlanAddUnitType(attackPlan, gNavalMilitaryUnitType, navyAttackSize, navyAttackSize, navyAttackSize); aiPlanSetInitialPosition(attackPlan, gNavyVec); aiPlanSetActive(attackPlan, true); aiEcho("AINavy: --- waterAttack: LAUNCHING NAVAL ATTACK, plan[" + attackPlan + "] navyAttackSize[" + navyAttackSize + "]"); } } } //============================================================================== // updateNavalMaintainPlans // // scale up maintain plans based on the naval strength of the enemy //============================================================================== void updateNavalMaintainPlans() { // review the relative size of our navy compared to the enemy // Adjust naval unit maintain plan counts based on enemy naval forces and navy bias int navyEnemyPlayer = getNavalTargetPlayer(); int enemyNavySize = kbUnitCount(navyEnemyPlayer, gNavalMilitaryUnitType, cUnitStateAlive); float adjustedBiasScale = (0.5 + (btBiasNavy / 2.0)); int minDesiredCount = enemyNavySize + (2 * adjustedBiasScale);// enemyNavySize + (0-2) int maxDesiredCount = gTriremeCount + (8 * adjustedBiasScale);// gTriremeCount + (0-8) int maintainCountTotal = 0; if (minDesiredCount < gTriremeCount) { // always maintain at least as many triremes as orginally desired by the parent AI maintainCountTotal = gTriremeCount; } else if (minDesiredCount < maxDesiredCount) { // if the enemy is strong enough, maintain even more triremes // up to the limit imposed by our starting count and our navy bias maintainCountTotal = minDesiredCount; } else { // if the enemy is strong enough, and we are at the cap, impose the cap maintainCountTotal = maxDesiredCount; } // if we have a valid trireme plan, and we have a count greater than the original value // update the plan with the new count if ( (maintainCountTotal > gTriremeCount) && (gTriremeMaintain > 0) ) { aiEcho("AINavy: --- gTriremeMaintain: plan[" + gTriremeMaintain + "] updated count[" + maintainCountTotal + "]"); aiPlanSetVariableInt(gTriremeMaintain, cTrainPlanNumberToMaintain, 0, maintainCountTotal); } else { int currentTreremeCount = kbUnitCount(cMyID, gNavalMilitaryUnitType, cUnitStateAlive); aiEcho("AINavy: --- gTriremeMaintain: plan[" + gTriremeMaintain + "] desired count[" + maintainCountTotal + "] current count[" + currentTreremeCount + "]"); } } //============================================================================== // rule navyManager // // Create maintain plans for naval units. // // BF 2/18/2012: Updated from Skirmish: // moved dock building code to new rule: buildDock // added handling to maintain more than 1 transport if the map is flagged as AITransportRequired // added handling for Siegeships // added handling for fireships //============================================================================== rule navyManager inactive group Navy minInterval 30 { //aiEcho("Running navy manager"); // Global override for disabling navy if (cvOkToTrainNavy == false) { //aiEcho("Not allowed to build a navy"); return; } // Make sure we have a dock if (kbUnitCount(cMyID, cUnitTypeUnitTypeBldgDock, cUnitStateABQ) < 1) { xsEnableRule("buildDock"); return; // Nothing else to do until dock is complete } // Find the closest dock int closestDock = getUnitByLocation(cUnitTypeUnitTypeBldgDock, cMyID, cUnitStateAlive, gNavyVec, 500.0, true); if (closestDock < 0) closestDock = getUnit(cUnitTypeUnitTypeBldgDock, cMyID, cUnitStateAlive); // Create an exploration plan to explore the water if (gWaterExplorePlan < 0) { int ut = kbFindUnit(gFishingBoatUnitType); gWaterExplorePlan = aiPlanCreate("Water Explore", cPlanExplore); aiEcho("AINavy: --- setup exploring: plan[" + gWaterExplorePlan + "]"); aiPlanSetVariableBool(gWaterExplorePlan, cExplorePlanReExploreAreas, 0, false); aiPlanSetInitialPosition(gWaterExplorePlan, gNavyVec); // Low, so that transport plans can steal it as needed, but just above fishing plans. aiPlanSetDesiredPriority(gWaterExplorePlan, 45); aiPlanAddUnitType(gWaterExplorePlan, ut, 1, 1, 1); aiPlanSetEscrowID(gWaterExplorePlan, cEconomyEscrowID); aiPlanSetVariableBool(gWaterExplorePlan, cExplorePlanDoLoops, 0, false); aiPlanSetActive(gWaterExplorePlan); } if (closestDock < 0) { // Don't fire up maintain plans until we have a base ID return; } // Update the main navy location to this dock if not already done updateMainNavyLocationToUnitArea(closestDock); int baseID = kbUnitGetBaseID(closestDock); if (baseID < 0) { // Don't fire up maintain plans until we have a base ID aiEcho("AINavy: --- We don't have a dock base yet"); return; } // Create a plan to keep a fishing boat out to explore if we haven't already if(gWaterExploreMaintain < 0) { gWaterExploreMaintain = createSimpleMaintainPlan(kbFindUnit(gFishingBoatUnitType), 1, true, baseID, 1); } //============================================================================== // Start up naval unit maintain plans if (gTransportMaintain < 0 ) { int transport = kbFindUnit(cUnitTypeUnitTypeShipUtility1); if((transport >= 0) && (gExtraTransportCount > 0)) { gTransportMaintain = createSimpleMaintainPlan(transport, gExtraTransportCount, false, baseID, 1); aiEcho("AINavy: --- gTransportMaintain: plan[" + gTransportMaintain + "] count[" + gExtraTransportCount + "] puid[" + transport + "]"); } } if (gTriremeMaintain < 0) { int triremeship = kbFindUnit(cUnitTypeUnitTypeShipBasic1); if((triremeship >= 0) && (gTriremeCount > 0)) { gTriremeMaintain = createSimpleMaintainPlan(triremeship, gTriremeCount, false, baseID, 1); aiPlanSetDesiredPriority(gTriremeMaintain, 86); aiEcho("AINavy: --- gTriremeMaintain: plan[" + gTriremeMaintain + "] count[" + gTriremeCount + "] puid[" + triremeship + "]"); } } if(gSiegeshipMaintain < 0) { int siegeship = kbFindUnit(cUnitTypeUnitTypeShipSiege1); if((siegeship >= 0) && (gSiegeshipCount > 0)) { gSiegeshipMaintain = createSimpleMaintainPlan(siegeship, gSiegeshipCount, false, baseID, 1); aiPlanSetDesiredPriority(gSiegeshipMaintain, 85); aiEcho("AINavy: --- gSiegeshipMaintain: plan[" + gSiegeshipMaintain + "] count[" + gSiegeshipCount + "] puid[" + siegeship + "]"); } } if(gFireshipMaintain < 0) { int fireship = kbFindUnit(cUnitTypeUnitTypeShipAntiShip1); if((fireship >= 0) && (gFireshipCount > 0)) { gFireshipMaintain = createSimpleMaintainPlan(fireship, gFireshipCount, false, baseID, 1); aiPlanSetDesiredPriority(gFireshipMaintain, 87); aiEcho("AINavy: --- gFireshipMaintain: plan[" + gFireshipMaintain + "] count[" + gFireshipCount + "] puid[" + fireship + "]"); } } updateNavalMaintainPlans(); } //============================================================================== // getBorderWaterAreas // // BF 2/18/2012: Updated from Skirmish: // new method used for several naval calculations //============================================================================== int getBorderWaterAreas(int area=-1) { int numAreas = kbAreaGetNumberBorderAreas(area); for (i=0; = 0) { if (kbAreaGetType(borderID) == cAreaTypeWater && kbAreaGetNumberTiles(borderID) >= 25) { return(borderID); } else { return(getBorderWaterAreas(borderID)); } } } return(-1); } //============================================================================== // getClosestConnectedWaterArea // // BF 2/18/2012: Updated from Skirmish: // new method used for several naval calculations //============================================================================== int getClosestConnectedWaterArea(int startArea=-1, int depth=1) { int currentAreaID = startArea; int previousAreaID = startArea; vector baseLocation = kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID)); vector startLocation = kbAreaGetCenter(startArea); float previousDistanceFromStart = 0; for(i=0; < depth) { int numAreas = kbAreaGetNumberBorderAreas(currentAreaID); int closestAreaID = -1; float shortestLength = 1000; float bestDistanceFromStart = -1; for(j=0; < numAreas) { int borderID = kbAreaGetBorderAreaID(currentAreaID, j); if (borderID >= 0) { if(kbAreaGetType(borderID) == cAreaTypeWater /*&& kbAreaGetNumberTiles(borderID) >= 25*/ && borderID != previousAreaID && borderID != startArea) { vector tempLocation = kbAreaGetCenter(borderID); vector baseDist = tempLocation - baseLocation; vector startDist = tempLocation - startLocation; float baseLength = xsVectorLength(baseDist); float startLength = xsVectorLength(startDist); if(baseLength < shortestLength && startLength > previousDistanceFromStart) { shortestLength = baseLength; closestAreaID = borderID; bestDistanceFromStart = startLength; } } } } if(closestAreaID == -1) { aiEcho("AINavy: --- getClosestConnectedWaterArea: returning early with area [" + currentAreaID + "]"); return(currentAreaID); } else { previousAreaID = currentAreaID; currentAreaID = closestAreaID; previousDistanceFromStart = bestDistanceFromStart; } } aiEcho("AINavy: --- getClosestConnectedWaterArea: returning area [" + currentAreaID + "]"); return(currentAreaID); } bool validateWaterArea(int areaID = -1, int desiredBorderingAreas = -1) { int numAreas = kbAreaGetNumberBorderAreas(areaID); int borderingWaterAreas = 0; for(j=0; < numAreas) { int borderID = kbAreaGetBorderAreaID(areaID, j); if (borderID >= 0 && kbAreaGetType(borderID) == cAreaTypeWater) { borderingWaterAreas = borderingWaterAreas + 1; } } return(borderingWaterAreas >= desiredBorderingAreas); } //============================================================================== // initNavy() // // Initialize the navy component of the AI and enable rules as needed // BF 2/18/2012: Updated from Skirmish: // added handling ensure InitNavy is not called twice // added a scout routine to get intel on the dock location //============================================================================== void initNavy() { // since there are cases where initNavy can be called more than once, check to ensure we do not do that if(gNavyInitialized == true) { return; } aiEcho("AINavy: --- initNavy() running"); // If we're not a navy map, bail if (isCurrentMapNaval() == false) { aiEcho("AINavy: --- Not a water map - disabling navy AI"); return; } // Find the closest water area if (gWaterAreaID == -1) { int checks = 0; int lastUsableWaterArea = -1; float minDistance = -1.0; vector baseLocation = kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID)); while(checks < 6 && gWaterAreaID == -1) { int tempArea = kbAreaGetClosestArea(baseLocation, cAreaTypeWater, -1, minDistance); if(tempArea != -1) { lastUsableWaterArea = tempArea; if(validateWaterArea(lastUsableWaterArea, 3) == false) { aiEcho("AINavy: --- Water area " + lastUsableWaterArea + " failed to have 3 neighboring water areas"); minDistance = xsVectorLength(kbAreaGetCenter(lastUsableWaterArea) - baseLocation) + 1.0; } else { aiEcho("AINavy: --- Water area " + lastUsableWaterArea + " is valid."); gWaterAreaID = lastUsableWaterArea; } } checks = checks + 1; } // in case we run out the while loop without finding a valid area, default to the last one we found. if(gWaterAreaID == -1) { aiEcho("AINavy: --- using area " + lastUsableWaterArea + " because we couldn't find a better area"); gWaterAreaID = lastUsableWaterArea; } } if (gWaterAreaID == -1) { aiEcho("AINavy: --- Can't do navy on this map, no water."); return; } int scout = getUnit(cUnitTypeUnitTypeScout1, cMyID, cUnitStateAlive); aiTaskUnitMove(scout, kbAreaGetCenter(gWaterAreaID)); xsEnableRule("buildDock"); xsEnableRule("buildDockInAge2"); if (gEnemyBoatsQuery < 0) { gEnemyBoatsQuery = kbUnitQueryCreate("Enemy boat query"); kbUnitQuerySetIgnoreKnockedOutUnits(gEnemyBoatsQuery, true); kbUnitQuerySetPlayerRelation(gEnemyBoatsQuery, cPlayerRelationEnemyNotGaia); kbUnitQuerySetUnitType(gEnemyBoatsQuery, cUnitTypeAbstractWarShip); kbUnitQuerySetState(gEnemyBoatsQuery, cUnitStateAlive); } gNavyInitialized = true; return; } //============================================================================== // buildDockAtLocation // // BF 2/18/2012: Updated from Skirmish: // new method to place first dock //============================================================================== int buildDockAtLocation(vector spot = cInvalidVector) { int dockPlan = aiPlanCreate("Military dock plan", cPlanBuild); aiPlanSetVariableInt(dockPlan, cBuildPlanBuildingTypeID, 0, kbFindBuilding(cUnitTypeUnitTypeBldgDock)); // Priority. aiPlanSetDesiredPriority(dockPlan, 80); // Mil vs. Econ. aiPlanSetMilitary(dockPlan, false); aiPlanSetEconomy(dockPlan, true); // Escrow. aiPlanSetEscrowID(dockPlan, cEconomyEscrowID); // Builders. aiPlanAddUnitType(dockPlan, gEconUnit, 1, 1, 1); aiPlanSetNumberVariableValues(dockPlan, cBuildPlanDockPlacementPoint, 2, true); aiPlanSetVariableVector(dockPlan, cBuildPlanDockPlacementPoint, 0, kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID))); // One point at main base aiPlanSetVariableVector(dockPlan, cBuildPlanDockPlacementPoint, 1, spot); // One point in water aiPlanSetVariableFloat(dockPlan, cBuildPlanBuildingBufferSpace, 0, 15.0); aiPlanSetVariableInt(dockPlan, cBuildPlanInfluenceUnitTypeID, 0, kbFindBuilding(cUnitTypeUnitTypeBldgDock)); aiPlanSetVariableFloat(dockPlan, cBuildPlanInfluenceUnitDistance, 0, 20.0); aiPlanSetVariableFloat(dockPlan, cBuildPlanInfluenceUnitValue, 0, 100.0); aiPlanSetVariableInt(dockPlan, cBuildPlanInfluenceUnitFalloff, 0, cBPIFalloffLinearInverse); aiPlanSetVariableInt(dockPlan, cBuildPlanInfluenceUnitCap, 0, -1); aiPlanSetActive(dockPlan); return (dockPlan); } //============================================================================== // updateDockLocation // // BF 2/18/2012: Updated from Skirmish: // new method to provide an improved dock location when needed //============================================================================== rule updateDockLocation active minInterval 10 { int dock = getUnit(cUnitTypeUnitTypeBldgDock, cMyID, cUnitStateAlive); if(dock != -1) { gLastDockLocation = kbUnitGetPosition(dock); } } //============================================================================== // buildDock // // BF 2/18/2012: Updated from Skirmish: // new rule to turn on exploration and get dock building underway //============================================================================== rule buildDock inactive minInterval 20 { // early out if we can not build docks at all if (cvOkToBuildDocks == false) { return; } xsEnableRule("exploreMonitor"); int dockID = getUnit(cUnitTypeUnitTypeBldgDock, cMyID, cUnitStateAlive); if(dockID >= 0) { aiEcho("AINavy: --- buildDock: GOT A DOCK"); xsDisableSelf(); xsEnableRule("navyManager"); xsEnableRule("waterDefend"); xsEnableRule("waterAttack"); xsEnableRule("startFishing"); updateMainNavyLocationToUnitArea(dockID); return; } int percentageVisible = (kbAreaGetNumberVisibleTiles(gWaterAreaID) * 100) / kbAreaGetNumberTiles(gWaterAreaID); int percentageFog = (kbAreaGetNumberFogTiles(gWaterAreaID) * 100) / kbAreaGetNumberTiles(gWaterAreaID); /*if(percentageFog < 33 && percentageVisible < 33) { aiEcho("AINavy: --- buildDock: I CANT SEE [" + gWaterAreaID + "]"); return; }*/ if (aiPlanGetIDByTypeAndVariableType(cPlanBuild, cBuildPlanBuildingTypeID, kbFindBuilding(cUnitTypeUnitTypeBldgDock)) >= 0) { aiEcho("AINavy: --- buildDock: ALREADY BUILDING"); return; } vector center = cInvalidVector; if(gLastDockLocation == cInvalidVector) { center = kbAreaGetCenter(gWaterAreaID); } else { center = gLastDockLocation; } kbUnitQuerySetPosition(gEnemyBoatsQuery, center); kbUnitQuerySetMaximumDistance(gEnemyBoatsQuery, 30); kbUnitQueryResetResults(gEnemyBoatsQuery); int enemyBoats = kbUnitQueryExecute(gEnemyBoatsQuery); if(enemyBoats > 0) { /*vector desiredLoc = center; vector baseLoc = kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID)); vector normal = desiredLoc - baseLoc; normal = xsVectorNormalize(normal); vector temp = xsVectorSet(xsVectorGetX(baseLoc) + (50 * xsVectorGetZ(normal)), 0, xsVectorGetZ(baseLoc) + (-50 * xsVectorGetX(normal))); int newAreaID = kbAreaGetClosestArea(temp, cAreaTypeWater, -1, 10); center = kbAreaGetCenter(newAreaID);*/ int newAreaID = getClosestConnectedWaterArea(kbAreaGetClosestArea(center, cAreaTypeWater, -1, 5), 4); vector temp = kbAreaGetCenter(newAreaID); aiEcho("AINavy: --- buildDock: enemy ships are blocking [" + center + "], trying to build at [" + temp + "] ("+ newAreaID + ")"); center = temp; gWaterAreaID = newAreaID; } buildDockAtLocation(center); } //============================================================================== // buildDockInAge2 // // BF 2/18/2012: Updated from Skirmish: // new rule to build a second dock in Age2, if required //============================================================================== rule buildDockInAge2 inactive minInterval 30 { // early out if we can not build docks at all if (cvOkToBuildDocks == false) { return; } if (btBuild2ndDockAge2 == true && kbGetAge() >= cAge2) { int count = getUnitCountByLocation(cUnitTypeUnitTypeBldgDock, cMyID, cUnitStateAlive); if(count <= 1) { vector center = cInvalidVector; if(gLastDockLocation == cInvalidVector) { center = kbAreaGetCenter(gWaterAreaID); } else { center = gLastDockLocation; } int newAreaID = getClosestConnectedWaterArea(kbAreaGetClosestArea(center, cAreaTypeWater, -1, 5), 3); aiEcho("AINavy: --- buildDockInAge2: building second dock in area [" + newAreaID + "]"); buildDockAtLocation(kbAreaGetCenter(newAreaID)); } xsDisableSelf(); } } //============================================================================== // checkEnableNavy // //============================================================================== rule checkEnableNavy minInterval 15 { if(gEnableNavyPlan) { if (gWaterAreaID == -1) gWaterAreaID = kbAreaGetClosestArea(kbBaseGetLocation(cMyID, kbBaseGetMainID(cMyID)), cAreaTypeWater); if (gWaterAreaID == -1) { aiEcho("AINavy: --- checkEnableNavy: Can't do navy on this map, no water."); xsDisableSelf(); return; } xsEnableRuleGroup("Navy"); navyManager(); xsDisableRule("checkEnableNavy"); } } //============================================================================== // dockBuildFailHandler() // // Callback for the dock failing to build //============================================================================== void dockBuildFailHandler(int param=-1) { aiEcho("AINavy: --- dockBuildFailHandler: ALL ABOARD THE FAILBOAT!"); // Calculate a new position for the dock to be built // Try grabbing a border area to the current area int numAreas = kbAreaGetNumberBorderAreas(gWaterAreaID); int i=-1; for (i=0; = 0) { if (kbAreaGetType(borderID) == cAreaTypeWater) { gWaterAreaID = borderID; gNavyVec = kbAreaGetCenter(gWaterAreaID); aiEcho("AINavy: --- dockBuildFailHandler: Setting navy location for dockFail gNavyVec[" + gNavyVec + "]"); } } } }