diff --git a/src/cs_playerbots.cpp b/src/cs_playerbots.cpp index dc316019..987cf0cb 100644 --- a/src/cs_playerbots.cpp +++ b/src/cs_playerbots.cpp @@ -19,6 +19,7 @@ #include "PlayerbotMgr.h" #include "RandomPlayerbotMgr.h" #include "ScriptMgr.h" +#include "BattleGroundTactics.h" using namespace Acore::ChatCommands; @@ -29,12 +30,17 @@ public: ChatCommandTable GetCommands() const override { + static ChatCommandTable playerbotsDebugCommandTable = + { + { "bg", HandleDebugBGCommand, SEC_GAMEMASTER, Console::Yes }, + }; static ChatCommandTable playerbotsCommandTable = { - { "bot", HandlePlayerbotCommand, SEC_PLAYER, Console::No }, + { "bot", HandlePlayerbotCommand, SEC_PLAYER, Console::No }, { "gtask", HandleGuildTaskCommand, SEC_GAMEMASTER, Console::Yes }, { "pmon", HandlePerfMonCommand, SEC_GAMEMASTER, Console::Yes }, - { "rndbot", HandleRandomPlayerbotCommand, SEC_GAMEMASTER, Console::Yes } + { "rndbot", HandleRandomPlayerbotCommand, SEC_GAMEMASTER, Console::Yes }, + { "debug", playerbotsDebugCommandTable }, }; static ChatCommandTable commandTable = @@ -83,6 +89,11 @@ public: sPerformanceMonitor->PrintStats(); return true; } + + static bool HandleDebugBGCommand(ChatHandler* handler, char const* args) + { + return BGTactics::HandleConsoleCommand(handler, args); + } }; void AddSC_playerbots_commandscript() diff --git a/src/strategy/actions/BattleGroundTactics.cpp b/src/strategy/actions/BattleGroundTactics.cpp index 4b1337bb..e2ec787c 100644 --- a/src/strategy/actions/BattleGroundTactics.cpp +++ b/src/strategy/actions/BattleGroundTactics.cpp @@ -44,12 +44,20 @@ std::vector const WS_FLAG_HIDE_ALLIANCE = { WS_FLAG_HIDE_ALLIANCE_1 , Position const AB_WAITING_POS_HORDE = { 702.884f, 703.045f, -16.115f, 0.77f }; Position const AB_WAITING_POS_ALLIANCE = { 1286.054f, 1282.500f, -15.697f, 3.95f }; +// the captains aren't the actual creatures but invisible trigger creatures - they still have correct death state and location (unless they move) +uint32 const AV_CREATURE_A_CAPTAIN = AV_CPLACE_TRIGGER16; +uint32 const AV_CREATURE_A_BOSS = AV_CPLACE_A_BOSS; +uint32 const AV_CREATURE_H_CAPTAIN = AV_CPLACE_TRIGGER18; +uint32 const AV_CREATURE_H_BOSS = AV_CPLACE_H_BOSS; + +Position const AV_CAVE_SPAWN_ALLIANCE = { 872.460f, -491.571f, 96.546f, 0.0f }; +Position const AV_CAVE_SPAWN_HORDE = { -1437.127f, -608.382f, 51.185f, 0.0f }; Position const AV_WAITING_POS_ALLIANCE = { 793.627f, -493.814f, 99.689f, 3.09f }; Position const AV_WAITING_POS_HORDE = { -1381.865f, -544.872f, 54.773f, 0.76f }; -Position const AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE = { -523.105f, -182.178f, 57.956f, 2.77f }; -Position const AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE = { -545.288f, -167.932f, 57.012f, 2.77f }; -Position const AV_STONEHEARTH_WAITING_HORDE = { -36.399f, -306.403f, 15.565f, 2.96f }; -Position const AV_STONEHEARTH_ATTACKING_HORDE = { -55.210f, -288.546f, 15.578f, 2.96f }; +Position const AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE = { -523.105f, -182.178f, 57.956f, 0.0f }; +Position const AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE = { -545.288f, -167.932f, 57.012f, 0.0f }; +Position const AV_STONEHEARTH_WAITING_HORDE = { -36.399f, -306.403f, 15.565f, 0.0f }; +Position const AV_STONEHEARTH_ATTACKING_HORDE = { -55.210f, -288.546f, 15.578f, 0.0f }; Position const EY_WAITING_POS_HORDE = { 1809.102f, 1540.854f, 1267.142f, 6.18f }; Position const EY_WAITING_POS_ALLIANCE = { 2526.020f, 1596.787f, 1270.127f, 3.14f }; @@ -1622,6 +1630,60 @@ BattleBotPath vPath_AV_Frostdagger_Pass_Lower_to_Iceblood_Garrison = { -492.17f, -187.077f, 57.1342f, nullptr }, }; +// path that allows alliance to bypass the mid on way to horde captain +BattleBotPath vPath_AV_Icewing_Bunker_Crossroad_to_Frostdagger_Pass_Lower = +{ + //these are to cause bot to pick this when resurrecting at stonehearth (not really needed anymore as they get captain down in first wave since uneeded dismounting was fixed) + //{ 68.793f, -396.742f, 45.299f, nullptr }, + //{ 99.042f, -389.310f, 45.101f, nullptr }, + //{ 123.787f, -373.551f, 42.893f, nullptr }, + { 119.693f, -351.311f, 42.728f, nullptr }, + { 107.710f, -321.162f, 37.168f, nullptr }, + { 84.953f, -273.434f, 23.944f, nullptr }, + { 47.073f, -245.696f, 15.417f, nullptr }, + { 2.455f, -240.346f, 11.867f, nullptr }, + { -38.451f, -231.493f, 10.284f, nullptr }, + { -70.606f, -230.758f, 9.919f, nullptr }, + { -83.955f, -214.666f, 12.873f, nullptr }, + { -111.412f, -175.575f, 13.515f, nullptr }, + { -129.634f, -141.359f, 20.921f, nullptr }, + { -140.904f, -105.104f, 28.097f, nullptr }, + { -167.552f, -74.891f, 31.524f, nullptr }, + { -210.442f, -54.664f, 37.525f, nullptr }, + { -247.372f, -38.489f, 36.438f, nullptr }, + { -280.423f, -48.095f, 32.709f, nullptr }, + { -309.757f, -58.032f, 28.778f, nullptr }, + { -345.274f, -71.566f, 28.005f, nullptr }, +}; + +// path for horde to get from cave to their own base (without this they try and do impossible climb into base) +BattleBotPath vPath_AV_Horde_Cave_to_Horde_Base_DrekThar = +{ + { -1361.209f, -529.061f, 52.447f, nullptr }, + { -1342.169f, -520.541f, 51.460f, nullptr }, + { -1312.566f, -504.299f, 51.962f, nullptr }, + { -1257.378f, -483.990f, 49.967f, nullptr }, + { -1203.739f, -470.903f, 53.064f, nullptr }, + { -1178.448f, -463.916f, 53.604f, nullptr }, + { -1145.804f, -442.760f, 56.724f, nullptr }, + { -1136.175f, -403.217f, 56.216f, nullptr }, + { -1134.374f, -381.372f, 54.052f, nullptr }, + { -1133.174f, -349.631f, 51.297f, nullptr }, + { -1162.345f, -352.746f, 51.798f, nullptr }, + { -1200.468f, -367.018f, 54.048f, nullptr }, + { -1245.986f, -361.634f, 59.862f, nullptr }, + { -1242.319f, -333.333f, 59.469f, nullptr }, + { -1218.883f, -301.577f, 68.204f, nullptr }, + { -1198.963f, -277.565f, 72.121f, nullptr }, + { -1202.834f, -256.893f, 72.455f, nullptr }, + { -1236.450f, -249.518f, 73.326f, nullptr }, + { -1249.806f, -261.023f, 73.298f, nullptr }, + { -1272.039f, -284.608f, 81.547f, nullptr }, + { -1287.808f, -289.069f, 89.560f, nullptr }, + { -1308.419f, -289.375f, 90.714f, nullptr }, + { -1321.996f, -289.660f, 90.585f, nullptr }, +}; + BattleBotPath vPath_EY_Horde_Spawn_to_Crossroad1Horde = { { 1809.102f, 1540.854f, 1267.142f, nullptr }, @@ -2189,6 +2251,8 @@ std::vector const vPaths_AV = &vPath_AV_Coldtooth_Mine_Entrance_to_Coldtooth_Mine_Boss, &vPath_AV_Stormpike_Crossroad_to_Irontooth_Mine_Entrance, &vPath_AV_Irontooth_Mine_Entrance_to_Irontooth_Mine_Boss, + &vPath_AV_Icewing_Bunker_Crossroad_to_Frostdagger_Pass_Lower, + &vPath_AV_Horde_Cave_to_Horde_Base_DrekThar, }; std::vector const vPaths_EY = @@ -2277,31 +2341,33 @@ std::vector const vPaths_HordeMine = static std::pair AV_HordeAttackObjectives[] = { - // Attack - { BG_AV_NODES_STONEHEART_BUNKER, BG_AV_OBJECT_FLAG_A_STONEHEART_BUNKER }, + // Attack - these are in order they should be attacked { BG_AV_NODES_STONEHEART_GRAVE, BG_AV_OBJECT_FLAG_A_STONEHEART_GRAVE }, + { BG_AV_NODES_STONEHEART_BUNKER, BG_AV_OBJECT_FLAG_A_STONEHEART_BUNKER }, { BG_AV_NODES_ICEWING_BUNKER, BG_AV_OBJECT_FLAG_A_ICEWING_BUNKER }, { BG_AV_NODES_STORMPIKE_GRAVE, BG_AV_OBJECT_FLAG_A_STORMPIKE_GRAVE }, - { BG_AV_NODES_DUNBALDAR_SOUTH, BG_AV_OBJECT_FLAG_A_DUNBALDAR_SOUTH }, { BG_AV_NODES_DUNBALDAR_NORTH, BG_AV_OBJECT_FLAG_A_DUNBALDAR_NORTH }, - { BG_AV_NODES_FIRSTAID_STATION, BG_AV_OBJECT_FLAG_A_FIRSTAID_STATION } + { BG_AV_NODES_DUNBALDAR_SOUTH, BG_AV_OBJECT_FLAG_A_DUNBALDAR_SOUTH }, + { BG_AV_NODES_FIRSTAID_STATION, BG_AV_OBJECT_FLAG_A_FIRSTAID_STATION }, }; static std::pair AV_HordeDefendObjectives[] = { - // Defend - { BG_AV_NODES_FROSTWOLF_GRAVE, BG_AV_OBJECT_FLAG_H_FROSTWOLF_GRAVE }, - { BG_AV_NODES_FROSTWOLF_ETOWER, BG_AV_OBJECT_FLAG_H_FROSTWOLF_ETOWER }, + // Defend - these are in order they should be protected + { BG_AV_NODES_FROSTWOLF_HUT, BG_AV_OBJECT_FLAG_H_FROSTWOLF_HUT }, { BG_AV_NODES_FROSTWOLF_WTOWER, BG_AV_OBJECT_FLAG_H_FROSTWOLF_WTOWER }, + { BG_AV_NODES_FROSTWOLF_ETOWER, BG_AV_OBJECT_FLAG_H_FROSTWOLF_ETOWER }, + { BG_AV_NODES_FROSTWOLF_GRAVE, BG_AV_OBJECT_FLAG_H_FROSTWOLF_GRAVE }, { BG_AV_NODES_TOWER_POINT, BG_AV_OBJECT_FLAG_H_TOWER_POINT }, { BG_AV_NODES_ICEBLOOD_TOWER, BG_AV_OBJECT_FLAG_H_ICEBLOOD_TOWER }, + //{ BG_AV_NODES_ICEBLOOD_GRAVE, BG_AV_OBJECT_FLAG_H_ICEBLOOD_GRAVE},//defending this makes it more likely for game to end due to loss of supplies }; static std::pair AV_AllianceAttackObjectives[] = { - // Attack - { BG_AV_NODES_ICEBLOOD_TOWER, BG_AV_OBJECT_FLAG_H_ICEBLOOD_TOWER}, + // Attack - these are in order they should be attacked { BG_AV_NODES_ICEBLOOD_GRAVE, BG_AV_OBJECT_FLAG_H_ICEBLOOD_GRAVE}, + { BG_AV_NODES_ICEBLOOD_TOWER, BG_AV_OBJECT_FLAG_H_ICEBLOOD_TOWER}, { BG_AV_NODES_TOWER_POINT, BG_AV_OBJECT_FLAG_H_TOWER_POINT }, { BG_AV_NODES_FROSTWOLF_GRAVE, BG_AV_OBJECT_FLAG_H_FROSTWOLF_GRAVE }, { BG_AV_NODES_FROSTWOLF_ETOWER, BG_AV_OBJECT_FLAG_H_FROSTWOLF_ETOWER }, @@ -2311,12 +2377,14 @@ static std::pair AV_AllianceAttackObjectives[] = static std::pair AV_AllianceDefendObjectives[] = { - // Defend - { BG_AV_NODES_STORMPIKE_GRAVE, BG_AV_OBJECT_FLAG_A_STORMPIKE_GRAVE }, + // Defend - these are in order they should be protected + { BG_AV_NODES_FIRSTAID_STATION, BG_AV_OBJECT_FLAG_A_FIRSTAID_STATION }, { BG_AV_NODES_DUNBALDAR_SOUTH, BG_AV_OBJECT_FLAG_A_DUNBALDAR_SOUTH }, { BG_AV_NODES_DUNBALDAR_NORTH, BG_AV_OBJECT_FLAG_A_DUNBALDAR_NORTH }, + { BG_AV_NODES_STORMPIKE_GRAVE, BG_AV_OBJECT_FLAG_A_STORMPIKE_GRAVE }, { BG_AV_NODES_ICEWING_BUNKER, BG_AV_OBJECT_FLAG_A_ICEWING_BUNKER }, { BG_AV_NODES_STONEHEART_BUNKER, BG_AV_OBJECT_FLAG_A_STONEHEART_BUNKER }, + //{ BG_AV_NODES_STONEHEART_GRAVE, BG_AV_OBJECT_FLAG_A_STONEHEART_GRAVE },//defending this makes it more likely for game to end due to loss of supplies }; static uint32 AB_AttackObjectives[] = @@ -2337,12 +2405,128 @@ static uint32 EY_AttackObjectives[] = { POINT_MAGE_TOWER } }; +// useful commands for fixing BG bugs and checking waypoints/paths +bool BGTactics::HandleConsoleCommand(ChatHandler* handler, char const* args) +{ + if (!sPlayerbotAIConfig->enabled) + { + handler->PSendSysMessage("|cffff0000Playerbot system is currently disabled!"); + return true; + } + WorldSession* session = handler->GetSession(); + if (!session) + { + handler->PSendSysMessage("Command can only be used from an active session"); + return true; + } + std::string const commandOutput = HandleConsoleCommandPrivate(session, args); + if (!commandOutput.empty()) + handler->PSendSysMessage(commandOutput.c_str()); + return true; +} + +// has different name to above as some compilers get confused +std::string const BGTactics::HandleConsoleCommandPrivate(WorldSession* session, char const* args) +{ + Player* player = session->GetPlayer(); + if (!player) + return "Error - session player not found"; + if (player->GetSession()->GetSecurity() < SEC_GAMEMASTER) + return "Command can only be used by a GM"; + Battleground* bg = player->GetBattleground(); + if (!bg) + return "Command can only be used within a battleground"; + BattlegroundTypeId bgType = bg->GetBgTypeID(); + if (bgType == BATTLEGROUND_RB) + bgType = bg->GetBgTypeID(true); + char* cmd = strtok((char*)args, " "); + //char* charname = strtok(nullptr, " "); + + if (!strncmp(cmd, "showpath", 8)) + { + int num = -1; + if (!strncmp(cmd, "showpath=", 9)) + { + if (sscanf(cmd, "showpath=%d", &num) == -1 || num < 0) + return "Bad showpath parameter"; + } + std::vector const* vPaths; + switch (bgType) + { + case BATTLEGROUND_AB: vPaths = &vPaths_AB; break; + case BATTLEGROUND_AV: vPaths = &vPaths_AV; break; + case BATTLEGROUND_WS: vPaths = &vPaths_WS; break; + case BATTLEGROUND_EY: vPaths = &vPaths_EY; break; + case BATTLEGROUND_IC: vPaths = &vPaths_IC; break; + default: vPaths = nullptr; break; + } + if (!vPaths) + return "This battleground has no paths and is unsupported"; + if (num == -1) { + float closestPoint = FLT_MAX; + for (uint32 j = 0; j < vPaths->size(); j++) + { + auto const& path = (*vPaths)[j]; + for (uint32 i = 0; i < path->size(); i++) + { + BattleBotWaypoint& waypoint = ((*path)[i]); + float dist = player->GetDistance(waypoint.x, waypoint.y, waypoint.z); + if (closestPoint > dist) { + closestPoint = dist; + num = j; + } + } + } + } + if (num >= vPaths->size()) + return fmt::format("Path out of range of 0 - {}", vPaths->size() - 1); + auto const& path = (*vPaths)[num]; + for (uint32 i = 0; i < path->size(); i++) + { + BattleBotWaypoint& waypoint = ((*path)[i]); + Creature* wpCreature = player->SummonCreature(15631, waypoint.x, waypoint.y, waypoint.z, 0, TEMPSUMMON_TIMED_DESPAWN, 15000u); + wpCreature->SetOwnerGUID(player->GetGUID()); + } + return fmt::format("Showing path {}", num); + } + + if (!strncmp(cmd, "showcreature=", 13)) + { + uint32 num; + if (sscanf(cmd, "showcreature=%u", &num) == -1) + return "Bad showcreature parameter"; + if (num >= bg->BgCreatures.size()) + return fmt::format("Creature out of range of 0 - {}", bg->BgCreatures.size() - 1); + Creature* c = bg->GetBGCreature(num); + if (!c) + return "Creature not found"; + Creature* wpCreature = player->SummonCreature(15631, c->GetPositionX(), c->GetPositionY(), c->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 15000u); + wpCreature->SetOwnerGUID(player->GetGUID()); + return fmt::format("Showing location of Creature {}", num); + } + + if (!strncmp(cmd, "showobject=", 11)) + { + uint32 num; + if (sscanf(cmd, "showobject=%u", &num) == -1) + return "Bad showobject parameter"; + if (num >= bg->BgObjects.size()) + return fmt::format("Object out of range of 0 - {}", bg->BgObjects.size() - 1); + GameObject* o = bg->GetBGObject(num); + if (!o) + return "GameObject not found"; + Creature* wpCreature = player->SummonCreature(15631, o->GetPositionX(), o->GetPositionY(), o->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 15000u); + wpCreature->SetOwnerGUID(player->GetGUID()); + return fmt::format("Showing location of GameObject {}", num); + } + + return "usage: showpath(=[num]) / showcreature=[num] / showobject=[num]"; +} + // // old wsg waypoints // - //cross the Battleground to get to flags or flag carriers - bool BGTactics::wsgPaths() { Battleground* bg = bot->GetBattleground(); @@ -2652,7 +2836,6 @@ bool BGTactics::wsgPaths() // // actual bg tactics below // - bool BGTactics::Execute(Event event) { Battleground* bg = bot->GetBattleground(); @@ -2929,140 +3112,145 @@ bool BGTactics::selectObjective(bool reset) BattlegroundTypeId bgType = bg->GetBgTypeID(); if (bgType == BATTLEGROUND_RB) bgType = bg->GetBgTypeID(true); - switch (bgType) { case BATTLEGROUND_AV: { - // Alliance and Horde code is intentionally different. - // Horde bots are more united and always go together. - // Alliance bots can pick random objective. - BattlegroundAV* alterValleyBG = (BattlegroundAV*)bg; + uint32 role = context->GetValue("bg role")->Get(); + bool supportDefense = role < 3;//defensive role and mine capture (mine capture disabled for now) + bool advancedAttack = role > 5;//doesnt wait for point to be fully captured before moving on + + // some of the code below is a bit inefficent (lots of rechecking same variables, could be made more efficient with a refactor) + // but it's been left this way so it can be easily reordered. in future we could implement different strategies (eg: focus on + // defense, rush to boss, etc), by re-ordering at runtime if (bot->GetTeamId() == TEAM_HORDE) { - bool endBoss = false; + bool enemyTowersDown = alterValleyBG->GetAVNodeInfo(BG_AV_NODES_DUNBALDAR_NORTH).State == POINT_DESTROYED && + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_DUNBALDAR_SOUTH).State == POINT_DESTROYED && + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_ICEWING_BUNKER).State == POINT_DESTROYED && + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_STONEHEART_BUNKER).State == POINT_DESTROYED; // End Boss - if (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_DUNBALDAR_SOUTH).TotalOwnerId != TEAM_ALLIANCE && - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_DUNBALDAR_NORTH).TotalOwnerId != TEAM_ALLIANCE && - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_ICEWING_BUNKER).TotalOwnerId != TEAM_ALLIANCE && - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_STONEHEART_BUNKER).TotalOwnerId != TEAM_ALLIANCE && + if (enemyTowersDown && + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FIRSTAID_STATION).OwnerId != TEAM_ALLIANCE && alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FIRSTAID_STATION).TotalOwnerId != TEAM_ALLIANCE) { - if (Creature* pVanndar = bg->GetBGCreature(AV_CPLACE_TRIGGER17)) + if (Creature* enemyBoss = bg->GetBGCreature(AV_CREATURE_A_BOSS)) { - BgObjective = pVanndar; - endBoss = true; - std::ostringstream out; - out << "Attacking Vanndar!"; - //bot->Say(out.str(), LANG_UNIVERSAL); + BgObjective = enemyBoss; } } - uint32 role = context->GetValue("bg role")->Get(); - bool supporter = role < 3; // first bunker strike team - - // Only go to Snowfall Graveyard if already close to it. - // Need to fix AV script - if (!BgObjective && supporter && - (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_ALLIANCE || - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_OTHER)) + // if towers all down only worry about attacking/holding final GY (this prevents them running all the way back to recap previous GY's when they're close to boss) + if (!BgObjective && enemyTowersDown) { - if (GameObject* pGO = bg->GetBGObject(BG_AV_NODES_SNOWFALL_GRAVE)) - if (bot->IsWithinDist(pGO, 200.f)) + if (GameObject* pGO = bg->GetBGObject(BG_AV_OBJECT_FLAG_A_FIRSTAID_STATION)) + { + if (!supportDefense || bot->IsWithinDist(pGO, 200.f)) + BgObjective = pGO; + } + } + + // (supportDefense) Only go to Snowfall Graveyard if already close to it. + if (!BgObjective && supportDefense && + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId != TEAM_HORDE) + { + if (GameObject* pGO = bg->GetBGObject(BG_AV_OBJECT_FLAG_N_SNOWFALL_GRAVE)) + if (bot->IsWithinDist(pGO, 100.f)) { BgObjective = pGO; - std::ostringstream out; - out << "Attacking Snowfall GY!"; - //bot->Say(out.str(), LANG_UNIVERSAL); } } - if (!BgObjective && alterValleyBG->IsCaptainAlive(0)) + // (supportDefense) defend objectives under attack + if (!BgObjective && supportDefense) { - if (Creature* pBalinda = bg->GetBGCreature(AV_CPLACE_TRIGGER16)) + // go to first defence objective under attack (the one closest to boss) + for (const auto& objective : AV_HordeDefendObjectives) { - if (pBalinda->getDeathState() != DeathState::Dead) + if (alterValleyBG->GetAVNodeInfo(objective.first).State != POINT_DESTROYED && + alterValleyBG->GetAVNodeInfo(objective.first).OwnerId == TEAM_ALLIANCE) { - uint32 attackCount = getDefendersCount(AV_STONEHEARTH_WAITING_HORDE, 10.0f, false) + getDefendersCount(AV_STONEHEARTH_ATTACKING_HORDE, 10.0f, false); + if (GameObject* pGO = bg->GetBGObject(objective.second)) + { + BgObjective = pGO; + break; + } + } + } + } + // attack or prepare to attack enemy captain if alive (this takes priority over other supportDefense tasks to avoid endless middle fighting and/or one side losing too many GY's before getting captain down) + if (!BgObjective && alterValleyBG->IsCaptainAlive(TEAM_ALLIANCE)) + { + if (Creature* enemyCaptain = bg->GetBGCreature(AV_CREATURE_A_CAPTAIN)) + { + if (enemyCaptain->getDeathState() != DeathState::Dead) + { + BgObjective = enemyCaptain; + uint32 attackCount = getDefendersCount(AV_STONEHEARTH_WAITING_HORDE, 10.0f, false) + getDefendersCount(AV_STONEHEARTH_ATTACKING_HORDE, 10.0f, false); // prepare to attack Captain - if (attackCount < 10 && !pBalinda->IsInCombat()) + if (attackCount < 15 && !enemyCaptain->IsInCombat()) { // get in position to attack Captain - pos.Set(AV_STONEHEARTH_WAITING_HORDE.GetPositionX(), - AV_STONEHEARTH_WAITING_HORDE.GetPositionY(), - AV_STONEHEARTH_WAITING_HORDE.GetPositionZ(), - bg->GetMapId()); - - std::ostringstream out; - out << "Taking position at Stonehearth!"; - //bot->Say(out.str(), LANG_UNIVERSAL); + pos.Set(AV_STONEHEARTH_WAITING_HORDE.GetPositionX(), AV_STONEHEARTH_WAITING_HORDE.GetPositionY(), AV_STONEHEARTH_WAITING_HORDE.GetPositionZ(), bg->GetMapId()); } else { // they need help getting there (or did before I fixed the target creature, will leave in anyway, as it probably makes it more robust) - pos.Set(AV_STONEHEARTH_ATTACKING_HORDE.GetPositionX(), - AV_STONEHEARTH_ATTACKING_HORDE.GetPositionY(), - AV_STONEHEARTH_ATTACKING_HORDE.GetPositionZ(), - bg->GetMapId()); - - std::ostringstream out; - out << "Attacking Balinda!"; - //bot->Say(out.str(), LANG_UNIVERSAL); + pos.Set(AV_STONEHEARTH_ATTACKING_HORDE.GetPositionX(), AV_STONEHEARTH_ATTACKING_HORDE.GetPositionY(), AV_STONEHEARTH_ATTACKING_HORDE.GetPositionZ(), bg->GetMapId()); } - - BgObjective = pBalinda; - - // set skull on captain - //if (bot->IsWithinDistInMap(pBalinda, 50.0f)) - //{ - // bot->GetGroup()->SetTargetIcon(7, pBalinda->GetGUID()); - //} } } } - if (!BgObjective) + // (supportDefense) defend our captain if alive + if (!BgObjective && supportDefense && alterValleyBG->IsCaptainAlive(TEAM_HORDE)) { - for (const auto& objective : AV_HordeDefendObjectives) + if (Creature* myCaptain = bg->GetBGCreature(AV_CREATURE_H_CAPTAIN)) { - if (!BgObjective && alterValleyBG->GetAVNodeInfo(objective.first).OwnerId == TEAM_ALLIANCE) + if (myCaptain->getDeathState() != DeathState::Dead && bot->IsWithinDist(myCaptain, 200.f)) + BgObjective = myCaptain; + } + } + + // (supportDefense) go to last defence objective that isn't destroyed (ie: next objective they'll attack) + if (!BgObjective && supportDefense) + { + for (const auto& objective : AV_HordeDefendObjectives)//TODO go reverse instead of not breaking + { + if (alterValleyBG->GetAVNodeInfo(objective.first).State != POINT_DESTROYED) { if (GameObject* pGO = bg->GetBGObject(objective.second)) - if (bot->IsWithinDist(pGO, 400.0f)) - { - BgObjective = pGO; - //std::ostringstream out; - // out << "Defending Node #" << objective.first; - //bot->Say(out.str(), LANG_UNIVERSAL); - } + BgObjective = pGO; } } } - // Mine capture (need paths & script fix) - if (!BgObjective && supporter && !endBoss && - (alterValleyBG->GetMineOwner(AV_NORTH_MINE) == TEAM_ALLIANCE || alterValleyBG->GetMineOwner(AV_NORTH_MINE) == TEAM_OTHER) && + // (supportDefense) Mine capture (disabled for now) + if (false && !BgObjective && supportDefense && + (alterValleyBG->GetMineOwner(AV_NORTH_MINE) == TEAM_ALLIANCE || alterValleyBG->GetMineOwner(AV_NORTH_MINE) == TEAM_NEUTRAL) && alterValleyBG->GetAVNodeInfo(BG_AV_NODES_STORMPIKE_GRAVE).OwnerId != TEAM_ALLIANCE) { if (Creature* mBossNeutral = bg->GetBGCreature(AV_CPLACE_MINE_N_3)) { - if (bot->IsWithinDist(mBossNeutral, 400.0f) && mBossNeutral->getDeathState() != DeathState::Dead && alterValleyBG->GetMineOwner(AV_NORTH_MINE) == TEAM_OTHER) + if (bot->IsWithinDist(mBossNeutral, 400.0f) && mBossNeutral->getDeathState() != DeathState::Dead && alterValleyBG->GetMineOwner(AV_NORTH_MINE) == TEAM_NEUTRAL) { BgObjective = mBossNeutral; + LOG_INFO("playerbots", "HORDE bot={} Attacking Neutral Mine Boss!", bot->GetName()); //std::ostringstream out; //out << "Attacking Neutral Mine Boss!"; //bot->Say(out.str(), LANG_UNIVERSAL); } } - + // um isnt this checking same creature twice? if (Creature* mBossAlly = bg->GetBGCreature(AV_CPLACE_MINE_N_3)) { - if (!BgObjective && bot->IsWithinDist(mBossAlly, 400.0f) && mBossAlly->getDeathState() != DeathState::Dead && alterValleyBG->GetMineOwner(AV_NORTH_MINE) == TEAM_ALLIANCE) + if (!BgObjective && bot->IsWithinDist(mBossAlly, 400.0f) && mBossAlly->getDeathState() != DeathState::Dead && alterValleyBG->GetMineOwner(AV_NORTH_MINE) == TEAM_NEUTRAL) { BgObjective = mBossAlly; + LOG_INFO("playerbots", "HORDE bot={} Attacking Ally Mine Boss!", bot->GetName()); //std::ostringstream out; //out << "Attacking Ally Mine Boss!"; //bot->Say(out.str(), LANG_UNIVERSAL); @@ -3070,102 +3258,164 @@ bool BGTactics::selectObjective(bool reset) } } - //bool strifeTime = bg->GetStartTime() < (uint32)(5 * MINUTE * IN_MILLISECONDS); - // small strike team to first bunker - if (!BgObjective/* || (!endBoss && supporter)*/) + // Attack first objective if close (this is mostly to help ally out by steering them toward getting GY first) + if (!BgObjective && alterValleyBG->GetAVNodeInfo(AV_HordeAttackObjectives[0].first).OwnerId == TEAM_ALLIANCE) { + if (GameObject* pGO = bg->GetBGObject(AV_HordeAttackObjectives[0].second)) { + float const distance = sqrt(bot->GetDistance(pGO)); + if (distance < 20.0f) + BgObjective = pGO; + } + } + + // Attack closest objective + if (!BgObjective) + { + float attackObjectiveDistance = FLT_MAX; for (const auto& objective : AV_HordeAttackObjectives) { - if ((!BgObjective/* || (supporter && objective.first == BG_AV_NODES_STONEHEART_BUNKER && !bg->IsActiveEvent(BG_AV_NODE_CAPTAIN_DEAD_A, 0))*/) && - alterValleyBG->GetAVNodeInfo(objective.first).TotalOwnerId == TEAM_ALLIANCE)//need to check TotalOwnerId for attack objectives + if ((!advancedAttack && alterValleyBG->GetAVNodeInfo(objective.first).TotalOwnerId == TEAM_ALLIANCE) || + (alterValleyBG->GetAVNodeInfo(objective.first).OwnerId == TEAM_ALLIANCE))//OwnerId changes when objective partially captured, TotalOwnerId only changes when its fully captured/destroyed (so this code will allow advancedAttack bots move on to next point) { if (GameObject* pGO = bg->GetBGObject(objective.second)) { - BgObjective = pGO; - //std::ostringstream out; - //out << "Attacking Node #" << objective.first; - //bot->Say(out.str(), LANG_UNIVERSAL); + float const distance = sqrt(bot->GetDistance(pGO)); + if (attackObjectiveDistance > distance) + { + BgObjective = pGO; + attackObjectiveDistance = distance; + } } } } } } - else // ALLIANCE + else //TEAM_ALLIANCE { - bool endBoss = false; - // End boss - if (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_ICEBLOOD_TOWER).TotalOwnerId != TEAM_HORDE && - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_TOWER_POINT).TotalOwnerId != TEAM_HORDE && - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_ETOWER).TotalOwnerId != TEAM_HORDE && - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_WTOWER).TotalOwnerId != TEAM_HORDE && + bool enemyTowersDown = alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_WTOWER).State == POINT_DESTROYED && + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_ETOWER).State == POINT_DESTROYED && + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_TOWER_POINT).State == POINT_DESTROYED && + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_ICEBLOOD_TOWER).State == POINT_DESTROYED; + // End Boss + if (enemyTowersDown && + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_HUT).OwnerId != TEAM_HORDE && alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_HUT).TotalOwnerId != TEAM_HORDE) { - if (Creature* pDrek = bg->GetBGCreature(AV_CPLACE_TRIGGER19)) + if (Creature* enemyBoss = bg->GetBGCreature(AV_CREATURE_H_BOSS)) { - BgObjective = pDrek; - endBoss = true; - std::ostringstream out; - out << "Attacking DrekThar!"; - //bot->Say(out.str(), LANG_UNIVERSAL); + BgObjective = enemyBoss; } } - uint32 role = context->GetValue("bg role")->Get(); - bool supporter = role < 3; - - // Only go to Snowfall Graveyard if already close to it. - if (!BgObjective && supporter && - (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_HORDE || - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_OTHER)) + // if towers all down only worry about attacking/holding final GY (this prevents them running all the way back to recap previous GY's when they're close to boss) + if (!BgObjective && enemyTowersDown) { - if (GameObject* pGO = bg->GetBGObject(BG_AV_NODES_SNOWFALL_GRAVE)) - if (bot->IsWithinDist(pGO, 200.f)) + if (GameObject* pGO = bg->GetBGObject(BG_AV_OBJECT_FLAG_H_FROSTWOLF_HUT)) + { + if (!supportDefense || bot->IsWithinDist(pGO, 200.f)) + BgObjective = pGO; + } + } + + // (supportDefense) Only go to Snowfall Graveyard if already close to it. + if (!BgObjective && supportDefense && + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId != TEAM_ALLIANCE) + { + if (GameObject* pGO = bg->GetBGObject(BG_AV_OBJECT_FLAG_N_SNOWFALL_GRAVE)) + if (bot->IsWithinDist(pGO, 100.f)) { BgObjective = pGO; - std::ostringstream out; - out << "Attacking Snowfall GY!"; - //bot->Say(out.str(), LANG_UNIVERSAL); } } - // Chance to defend. - if (!BgObjective && (!endBoss && supporter)) + // (supportDefense) defend objectives under attack + if (!BgObjective && supportDefense) { + // go to first defence objective under attack (the one closest to boss) for (const auto& objective : AV_AllianceDefendObjectives) { - if (!BgObjective && alterValleyBG->GetAVNodeInfo(objective.first).OwnerId == TEAM_HORDE) + if (alterValleyBG->GetAVNodeInfo(objective.first).State != POINT_DESTROYED && + alterValleyBG->GetAVNodeInfo(objective.first).OwnerId == TEAM_HORDE) { if (GameObject* pGO = bg->GetBGObject(objective.second)) { BgObjective = pGO; - //std::ostringstream out; out << "Defending Node #" << objective.first; - //bot->Say(out.str(), LANG_UNIVERSAL); + break; } } } } - // Mine capture (need paths & script fix) - if (!BgObjective && supporter && !endBoss && - (alterValleyBG->GetMineOwner(AV_SOUTH_MINE) == TEAM_HORDE || alterValleyBG->GetMineOwner(AV_SOUTH_MINE) == TEAM_OTHER) && + // attack or prepare to attack enemy captain if alive (this takes priority over other supportDefense tasks to avoid endless middle fighting and/or one side losing too many GY's before getting captain down) + if (!BgObjective && alterValleyBG->IsCaptainAlive(TEAM_HORDE)) + { + if (Creature* enemyCaptain = bg->GetBGCreature(AV_CREATURE_H_CAPTAIN)) + { + if (enemyCaptain->getDeathState() != DeathState::Dead) + { + BgObjective = enemyCaptain; + uint32 attackCount = getDefendersCount(AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE, 10.0f, false) + getDefendersCount(AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE, 10.0f, false); + // prepare to attack Captain + if (attackCount < 15 && !enemyCaptain->IsInCombat()) + { + // get in position to attack Captain + pos.Set(AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE.GetPositionX(), AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE.GetPositionY(), AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE.GetPositionZ(), bg->GetMapId()); + } + else + { + // they need help getting there (or did before I fixed the target creature, will leave in anyway, as it probably makes it more robust) + pos.Set(AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE.GetPositionX(), AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE.GetPositionY(), AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE.GetPositionZ(), bg->GetMapId()); + } + } + } + } + + // (supportDefense) defend our captain if alive + if (!BgObjective && supportDefense && alterValleyBG->IsCaptainAlive(TEAM_ALLIANCE)) + { + if (Creature* myCaptain = bg->GetBGCreature(AV_CREATURE_A_CAPTAIN)) + { + if (myCaptain->getDeathState() != DeathState::Dead && bot->IsWithinDist(myCaptain, 200.f)) + BgObjective = myCaptain; + } + } + + // (supportDefense) go to last defence objective that isn't destroyed (ie: next objective they'll attack) + if (!BgObjective && supportDefense) + { + for (const auto& objective : AV_AllianceDefendObjectives)//TODO go reverse instead of not breaking + { + if (alterValleyBG->GetAVNodeInfo(objective.first).State != POINT_DESTROYED) + { + if (GameObject* pGO = bg->GetBGObject(objective.second)) + BgObjective = pGO; + } + } + } + + // (supportDefense) Mine capture (disabled for now) + if (false && !BgObjective && supportDefense && + (alterValleyBG->GetMineOwner(AV_SOUTH_MINE) == TEAM_HORDE || alterValleyBG->GetMineOwner(AV_SOUTH_MINE) == TEAM_NEUTRAL) && alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_GRAVE).TotalOwnerId != TEAM_HORDE) { if (Creature* mBossNeutral = bg->GetBGCreature(AV_CPLACE_MINE_S_3)) { - if (bot->IsWithinDist(mBossNeutral, 400.0f) && mBossNeutral->getDeathState() != DeathState::Dead && alterValleyBG->GetMineOwner(AV_SOUTH_MINE) == TEAM_OTHER) + if (bot->IsWithinDist(mBossNeutral, 400.0f) && mBossNeutral->getDeathState() != DeathState::Dead && alterValleyBG->GetMineOwner(AV_SOUTH_MINE) == TEAM_NEUTRAL) { BgObjective = mBossNeutral; + LOG_INFO("playerbots", "ALLIANCE bot={} Attacking Neutral Mine Boss!", bot->GetName()); //std::ostringstream out; //out << "Attacking Neutral Mine Boss!"; //bot->Say(out.str(), LANG_UNIVERSAL); } } - + // um isnt this checking same creature twice? if (Creature* mBossHorde = bg->GetBGCreature(AV_CPLACE_MINE_S_3)) { if (!BgObjective && bot->IsWithinDist(mBossHorde, 400.0f) && mBossHorde->getDeathState() != DeathState::Dead && alterValleyBG->GetMineOwner(AV_SOUTH_MINE) == TEAM_HORDE) { BgObjective = mBossHorde; + LOG_INFO("playerbots", "ALLIANCE bot={} Attacking Horde Mine Boss!", bot->GetName()); //std::ostringstream out; //out << "Attacking Horde Mine Boss!"; //bot->Say(out.str(), LANG_UNIVERSAL); @@ -3173,71 +3423,36 @@ bool BGTactics::selectObjective(bool reset) } } + // Attack first objective if close (this is mostly to help ally out by steering them toward getting GY first) + if (!BgObjective && alterValleyBG->GetAVNodeInfo(AV_AllianceAttackObjectives[0].first).OwnerId == TEAM_HORDE) + { + if (GameObject* pGO = bg->GetBGObject(AV_AllianceAttackObjectives[0].second)) { + float const distance = sqrt(bot->GetDistance(pGO)); + if (distance < 20.0f) + BgObjective = pGO; + } + } + + // Attack closest objective if (!BgObjective) { - // Attack closest objective. - WorldObject* pAttackObjectiveObject = nullptr; float attackObjectiveDistance = FLT_MAX; - - if (alterValleyBG->IsCaptainAlive(1)) - { - if (Creature* pGalvangar = bg->GetBGCreature(AV_CPLACE_TRIGGER18)) - { - uint32 attackCount = getDefendersCount(AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE, 10.0f, false) + getDefendersCount(AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE, 10.0f, false); - - // prepare to attack Captain - if (attackCount < 10 && !pGalvangar->IsInCombat()) - { - // get in position to attack Captain - pos.Set(AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE.GetPositionX(), - AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE.GetPositionY(), - AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE.GetPositionZ(), - bg->GetMapId()); - - //std::ostringstream out; - //out << "Taking position at Iceblood Outpost!"; - //bot->Say(out.str(), LANG_UNIVERSAL); - } - else - { - // they need help getting there (or did before I fixed the target creature, will leave in anyway, as it probably makes it more robust) - pos.Set(AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE.GetPositionX(), - AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE.GetPositionY(), - AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE.GetPositionZ(), - bg->GetMapId()); - - //std::ostringstream out; - // out << "Attacking Galvangar!"; - //bot->Say(out.str(), LANG_UNIVERSAL); - } - pAttackObjectiveObject = pGalvangar; - attackObjectiveDistance = sqrt(bot->GetDistance(pGalvangar)); - } - } - for (const auto& objective : AV_AllianceAttackObjectives) { - if (alterValleyBG->GetAVNodeInfo(objective.first).TotalOwnerId == TEAM_HORDE)//need to check TotalOwnerId for attack objectives + if ((!advancedAttack && alterValleyBG->GetAVNodeInfo(objective.first).TotalOwnerId == TEAM_HORDE) || + (alterValleyBG->GetAVNodeInfo(objective.first).OwnerId == TEAM_HORDE))//OwnerId changes when objective partially captured, TotalOwnerId only changes when its fully captured/destroyed (so this code will allow advancedAttack bots move on to next point) { if (GameObject* pGO = bg->GetBGObject(objective.second)) { float const distance = sqrt(bot->GetDistance(pGO)); if (attackObjectiveDistance > distance) { - pAttackObjectiveObject = pGO; + BgObjective = pGO; attackObjectiveDistance = distance; - //std::ostringstream out; - // out << "Attacking Node #" << objective.first; - //bot->Say(out.str(), LANG_UNIVERSAL); } } } } - - if (pAttackObjectiveObject) - { - BgObjective = pAttackObjectiveObject; - } } } if (BgObjective) @@ -3246,10 +3461,6 @@ bool BGTactics::selectObjective(bool reset) pos.Set(BgObjective->GetPositionX(), BgObjective->GetPositionY(), BgObjective->GetPositionZ(), BgObjective->GetMapId()); posMap["bg objective"] = pos; - - //std::ostringstream out; - //out << "BG objective set to " << BgObjective->GetName() << " " << pos.x << " " << pos.y; - //bot->Say(out.str(), LANG_UNIVERSAL); return true; } break; @@ -4132,6 +4343,15 @@ bool BGTactics::moveToObjective() if (bgType == BATTLEGROUND_RB) bgType = bg->GetBgTypeID(true); + // get bots out of cave when respawned there (otherwise path selection happens while they're deep within cave and the results arent good) + if (bgType == BATTLEGROUND_AV) + { + Position const caveSpawn = bot->GetTeamId() == TEAM_ALLIANCE ? AV_CAVE_SPAWN_ALLIANCE : AV_CAVE_SPAWN_HORDE; + if (sqrt(bot->GetDistance(caveSpawn)) < 4.0f) { + return moveToStart(true); + } + } + PositionInfo pos = context->GetValue("position")->Get()["bg objective"]; if (!pos.isSet()) return selectObjective(); @@ -4208,8 +4428,11 @@ bool BGTactics::selectObjectiveWp(std::vector const& vPaths) botDistanceScoreMultiply = 4.0f; } + //uint32 index = -1; + //uint32 chosenPathIndex = -1; for (auto const& path : vPaths) { + //index++; // skip mine paths of own faction if (bot->GetTeamId() == TEAM_ALLIANCE && std::find(vPaths_AllyMine.begin(), vPaths_AllyMine.end(), path) != vPaths_AllyMine.end()) continue; @@ -4249,7 +4472,7 @@ bool BGTactics::selectObjectiveWp(std::vector const& vPaths) // creates a score based on dist-to-bot and dist-to-destination, where lower is better, and dist-to-bot is more important (when its beyond a certain distance) // dist-to-bot is more important because otherwise they cant reach it at all (or will fly through air with MM::MovePoint()), also bot may need to use multiple - // paths (one after another) anyway + // paths (one after another) anyway float distToDestination = reverse ? startPointDistToDestination : endPointDistToDestination; float pathScore = (closestPointDistToBot < botDistanceScoreSubtract ? 0.0f : ((closestPointDistToBot - botDistanceScoreSubtract) * botDistanceScoreMultiply)) + distToDestination; @@ -4260,13 +4483,14 @@ bool BGTactics::selectObjectiveWp(std::vector const& vPaths) chosenPath = path; chosenPathPoint = closestPointIndex; chosenPathReverse = reverse; + //chosenPathIndex = index; } } if (!chosenPath) return false; - //LOG_INFO("playerbots", "bot={} {}", bot->GetName(), vPaths_AB_name[chosenPathNum]); + //LOG_INFO("playerbots", "{} bot={} path={}", (bot->GetTeamId() == TEAM_HORDE ? "HORDE" : "ALLIANCE"), bot->GetName(), chosenPathIndex); return moveToObjectiveWp(chosenPath, chosenPathPoint, chosenPathReverse); @@ -4279,8 +4503,9 @@ bool BGTactics::resetObjective() if (!bg) return false; - // sometimes change role - if (!urand(0, 5) && !(bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) || bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG) || bot->HasAura(BG_EY_NETHERSTORM_FLAG_SPELL))) + // sometimes change role - should do so less often on larger BG's otherwise bots will spend too much time running around map instead of doing something useful + uint32 rollChangeOdds = BATTLEGROUND_AV == bg->GetBgTypeID() ? 63 : 5; + if (!urand(0, rollChangeOdds) && !(bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) || bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG) || bot->HasAura(BG_EY_NETHERSTORM_FLAG_SPELL))) context->GetValue("bg role")->Set(urand(0, 9)); PositionMap& posMap = context->GetValue("position")->Get(); @@ -4514,11 +4739,7 @@ bool BGTactics::atFlag(std::vector const& vPaths, std::vectorIsWithinDistInMap(go, flagRange)) continue; - bool atBase = go->GetEntry() == vFlagsWS[bot->GetTeamId()]; - if (bgType == BATTLEGROUND_EY) - { - atBase = go->GetEntry() == vFlagsEY[0]; - } + bool atBase = bgType == BATTLEGROUND_WS ? go->GetEntry() == vFlagsWS[bot->GetTeamId()] : bgType == BATTLEGROUND_EY ? go->GetEntry() == vFlagsEY[0] : false; if (atBase && bgType == BATTLEGROUND_WS && !(bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) || bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG))) continue; diff --git a/src/strategy/actions/BattleGroundTactics.h b/src/strategy/actions/BattleGroundTactics.h index 90e0afc4..db280fff 100644 --- a/src/strategy/actions/BattleGroundTactics.h +++ b/src/strategy/actions/BattleGroundTactics.h @@ -7,6 +7,7 @@ #include "MovementActions.h" +class ChatHandler; class Battleground; class PlayerbotAI; struct Position; @@ -36,11 +37,14 @@ extern std::vector const vPaths_IC; class BGTactics : public MovementAction { public: + static bool HandleConsoleCommand(ChatHandler* handler, char const* args); + BGTactics(PlayerbotAI* botAI, std::string const name = "bg tactics") : MovementAction(botAI, name) { } bool Execute(Event event) override; private: + static std::string const HandleConsoleCommandPrivate(WorldSession* session, char const* args); bool moveToStart(bool force = false); bool selectObjective(bool reset = false); bool moveToObjective(); diff --git a/src/strategy/actions/CheckMountStateAction.cpp b/src/strategy/actions/CheckMountStateAction.cpp index a32a02e0..6e4e84d1 100644 --- a/src/strategy/actions/CheckMountStateAction.cpp +++ b/src/strategy/actions/CheckMountStateAction.cpp @@ -13,7 +13,8 @@ bool CheckMountStateAction::Execute(Event event) { bool noattackers = AI_VALUE2(bool, "combat", "self target") ? (AI_VALUE(uint8, "attacker count") > 0 ? false : true) : true; bool enemy = AI_VALUE(Unit*, "enemy player target"); - bool dps = (AI_VALUE(Unit*, "dps target") || AI_VALUE(Unit*, "grind target")); + // ignore grind target in BG or bots will dismount near any creature (eg: the rams in AV) + bool dps = (AI_VALUE(Unit*, "dps target") || (!bot->InBattleground() && AI_VALUE(Unit*, "grind target"))); bool fartarget = (enemy && sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "enemy player target"), 40.0f)) || (dps && sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "dps target"), 50.0f)); bool attackdistance = false;