From f854726934febfc879a8ec1c3c8f83acc8d6542d Mon Sep 17 00:00:00 2001 From: Fuzz Date: Mon, 15 Jul 2024 20:44:36 +1000 Subject: [PATCH 1/6] fixed defenders sitting in burning building lamenting their failures, some attackers now advance to next objective without waiting for full capture, added new path for alliance to bypass mid on the way to galv, made objective selection logic same for both teams --- src/strategy/actions/BattleGroundTactics.cpp | 375 +++++++++++-------- 1 file changed, 214 insertions(+), 161 deletions(-) diff --git a/src/strategy/actions/BattleGroundTactics.cpp b/src/strategy/actions/BattleGroundTactics.cpp index a9e916d3..bf99fc07 100644 --- a/src/strategy/actions/BattleGroundTactics.cpp +++ b/src/strategy/actions/BattleGroundTactics.cpp @@ -1622,6 +1622,27 @@ BattleBotPath vPath_AV_Frostdagger_Pass_Lower_to_Iceblood_Garrison = { -492.17f, -187.077f, 57.1342f, nullptr }, }; +BattleBotPath vPath_AV_Icewing_Bunker_Crossroad_to_Frostdagger_Pass_Lower = +{ + { 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 }, +}; + BattleBotPath vPath_EY_Horde_Spawn_to_Crossroad1Horde = { { 1809.102f, 1540.854f, 1267.142f, nullptr }, @@ -2189,6 +2210,7 @@ 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, }; std::vector const vPaths_EY = @@ -2934,52 +2956,84 @@ bool BGTactics::selectObjective(bool reset) { 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; + bool advancedAttack = role < 7; if (bot->GetTeamId() == TEAM_HORDE) { - bool endBoss = false; // End Boss - if (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_DUNBALDAR_SOUTH).TotalOwnerId != TEAM_ALLIANCE && + if (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FIRSTAID_STATION).TotalOwnerId != TEAM_ALLIANCE && alterValleyBG->GetAVNodeInfo(BG_AV_NODES_DUNBALDAR_NORTH).TotalOwnerId != TEAM_ALLIANCE && + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_DUNBALDAR_SOUTH).TotalOwnerId != TEAM_ALLIANCE && alterValleyBG->GetAVNodeInfo(BG_AV_NODES_ICEWING_BUNKER).TotalOwnerId != TEAM_ALLIANCE && - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_STONEHEART_BUNKER).TotalOwnerId != TEAM_ALLIANCE && - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FIRSTAID_STATION).TotalOwnerId != TEAM_ALLIANCE) + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_STONEHEART_BUNKER).TotalOwnerId != TEAM_ALLIANCE) { if (Creature* pVanndar = bg->GetBGCreature(AV_CPLACE_TRIGGER17)) { BgObjective = pVanndar; - endBoss = true; - std::ostringstream out; - out << "Attacking Vanndar!"; + //std::ostringstream out; + //out << "Attacking Vanndar!"; //bot->Say(out.str(), LANG_UNIVERSAL); } } - 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 && + if (!BgObjective && supportDefense && (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_ALLIANCE || - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_OTHER)) + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_NEUTRAL)) { - 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_N_SNOWFALL_GRAVE)) + if (bot->IsWithinDist(pGO, 100.f)) { BgObjective = pGO; - std::ostringstream out; - out << "Attacking Snowfall GY!"; + //std::ostringstream out; + //out << "Attacking Snowfall GY!"; //bot->Say(out.str(), LANG_UNIVERSAL); } } - if (!BgObjective && alterValleyBG->IsCaptainAlive(0)) + // defend + if (!BgObjective && supportDefense) + { + for (const auto& objective : AV_HordeDefendObjectives) + { + if (!BgObjective && + alterValleyBG->GetAVNodeInfo(objective.first).State != POINT_DESTROYED && + alterValleyBG->GetAVNodeInfo(objective.first).OwnerId == TEAM_ALLIANCE) + { + if (GameObject* pGO = bg->GetBGObject(objective.second)) + { + BgObjective = pGO; + //std::ostringstream out; + //out << "Defending Node #" << objective.first; + //bot->Say(out.str(), LANG_UNIVERSAL); + } + } + } + // defend captain or boss if close + if (!BgObjective && alterValleyBG->IsCaptainAlive(TEAM_HORDE)) + { + if (Creature* myNpc = bg->GetBGCreature(AV_CPLACE_TRIGGER18))//galvangar + { + if (myNpc->getDeathState() != DeathState::Dead && bot->IsWithinDist(myNpc, 100.f)) + BgObjective = myNpc; + + } + } + if (!BgObjective) + { + if (Creature* myNpc = bg->GetBGCreature(AV_CPLACE_TRIGGER19))//drek + { + if (myNpc->getDeathState() != DeathState::Dead && bot->IsWithinDist(myNpc, 300.f)) + BgObjective = myNpc; + + } + } + } + + if (!BgObjective && alterValleyBG->IsCaptainAlive(TEAM_ALLIANCE)) { if (Creature* pBalinda = bg->GetBGCreature(AV_CPLACE_TRIGGER16)) { @@ -2995,9 +3049,8 @@ bool BGTactics::selectObjective(bool reset) AV_STONEHEARTH_WAITING_HORDE.GetPositionY(), AV_STONEHEARTH_WAITING_HORDE.GetPositionZ(), bg->GetMapId()); - - std::ostringstream out; - out << "Taking position at Stonehearth!"; + //std::ostringstream out; + //out << "Taking position at Stonehearth!"; //bot->Say(out.str(), LANG_UNIVERSAL); } else @@ -3007,51 +3060,26 @@ bool BGTactics::selectObjective(bool reset) AV_STONEHEARTH_ATTACKING_HORDE.GetPositionY(), AV_STONEHEARTH_ATTACKING_HORDE.GetPositionZ(), bg->GetMapId()); - - std::ostringstream out; - out << "Attacking Balinda!"; + //std::ostringstream out; + //out << "Attacking Balinda!"; //bot->Say(out.str(), LANG_UNIVERSAL); } - BgObjective = pBalinda; - - // set skull on captain - //if (bot->IsWithinDistInMap(pBalinda, 50.0f)) - //{ - // bot->GetGroup()->SetTargetIcon(7, pBalinda->GetGUID()); - //} } } } - if (!BgObjective) - { - for (const auto& objective : AV_HordeDefendObjectives) - { - if (!BgObjective && alterValleyBG->GetAVNodeInfo(objective.first).OwnerId == TEAM_ALLIANCE) - { - 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); - } - } - } - } - - // Mine capture (need paths & script fix) - if (!BgObjective && supporter && !endBoss && - (alterValleyBG->GetMineOwner(AV_NORTH_MINE) == TEAM_ALLIANCE || alterValleyBG->GetMineOwner(AV_NORTH_MINE) == TEAM_OTHER) && + // 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); @@ -3060,9 +3088,10 @@ bool BGTactics::selectObjective(bool reset) 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,118 +3099,111 @@ bool BGTactics::selectObjective(bool reset) } } - //bool strifeTime = bg->GetStartTime() < (uint32)(5 * MINUTE * IN_MILLISECONDS); - // small strike team to first bunker - if (!BgObjective/* || (!endBoss && supporter)*/) + if (!BgObjective) { + // Attack closest objective + WorldObject* pAttackObjectiveObject = nullptr; + 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))//TotalOwnerId only changes when its fully captured/destroyed, OwnerId changes when it's assaulted (meaning 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; + attackObjectiveDistance = distance; + //std::ostringstream out; + //out << "Attacking Node #" << objective.first; + //bot->Say(out.str(), LANG_UNIVERSAL); + } + } + } + } + if (pAttackObjectiveObject) + BgObjective = pAttackObjectiveObject; + } + } + else //TEAM_ALLIANCE + { + // End Boss + if (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_HUT).TotalOwnerId != TEAM_HORDE && + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_WTOWER).TotalOwnerId != TEAM_HORDE && + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_ETOWER).TotalOwnerId != TEAM_HORDE && + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_TOWER_POINT).TotalOwnerId != TEAM_HORDE && + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_ICEBLOOD_TOWER).TotalOwnerId != TEAM_HORDE) + { + if (Creature* pDrek = bg->GetBGCreature(AV_CPLACE_TRIGGER19)) + { + BgObjective = pDrek; + //std::ostringstream out; + //out << "Attacking DrekThar!"; + //bot->Say(out.str(), LANG_UNIVERSAL); + } + } + + // Only go to Snowfall Graveyard if already close to it. + if (!BgObjective && supportDefense && + (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_HORDE || + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_NEUTRAL)) + { + 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); + } + } + + // defend + if (!BgObjective && supportDefense) + { + for (const auto& objective : AV_AllianceDefendObjectives) + { + if (!BgObjective && + 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 << "Attacking Node #" << objective.first; + //out << "Defending Node #" << objective.first; //bot->Say(out.str(), LANG_UNIVERSAL); } } } - } - } - else // 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 && - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_HUT).TotalOwnerId != TEAM_HORDE) - { - if (Creature* pDrek = bg->GetBGCreature(AV_CPLACE_TRIGGER19)) + // defend captain or boss if close + if (!BgObjective && alterValleyBG->IsCaptainAlive(TEAM_ALLIANCE)) { - BgObjective = pDrek; - endBoss = true; - std::ostringstream out; - out << "Attacking DrekThar!"; - //bot->Say(out.str(), LANG_UNIVERSAL); - } - } - - 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 (GameObject* pGO = bg->GetBGObject(BG_AV_NODES_SNOWFALL_GRAVE)) - if (bot->IsWithinDist(pGO, 200.f)) + if (Creature* myNpc = bg->GetBGCreature(AV_CPLACE_TRIGGER16))//balinda { - BgObjective = pGO; - std::ostringstream out; - out << "Attacking Snowfall GY!"; - //bot->Say(out.str(), LANG_UNIVERSAL); + if (myNpc->getDeathState() != DeathState::Dead && bot->IsWithinDist(myNpc, 100.f)) + BgObjective = myNpc; + } - } - - // Chance to defend. - if (!BgObjective && (!endBoss && supporter)) - { - for (const auto& objective : AV_AllianceDefendObjectives) + } + if (!BgObjective) { - if (!BgObjective && alterValleyBG->GetAVNodeInfo(objective.first).OwnerId == TEAM_HORDE) + if (Creature* myNpc = bg->GetBGCreature(AV_CPLACE_TRIGGER16))//vanndar { - if (GameObject* pGO = bg->GetBGObject(objective.second)) - { - BgObjective = pGO; - //std::ostringstream out; out << "Defending Node #" << objective.first; - //bot->Say(out.str(), LANG_UNIVERSAL); - } + if (myNpc->getDeathState() != DeathState::Dead && bot->IsWithinDist(myNpc, 300.f)) + BgObjective = myNpc; + } } } - // Mine capture (need paths & script fix) - if (!BgObjective && supporter && !endBoss && - (alterValleyBG->GetMineOwner(AV_SOUTH_MINE) == TEAM_HORDE || alterValleyBG->GetMineOwner(AV_SOUTH_MINE) == TEAM_OTHER) && - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_GRAVE).TotalOwnerId != TEAM_HORDE) + if (!BgObjective && alterValleyBG->IsCaptainAlive(TEAM_HORDE)) { - if (Creature* mBossNeutral = bg->GetBGCreature(AV_CPLACE_MINE_S_3)) + if (Creature* pGalvangar = bg->GetBGCreature(AV_CPLACE_TRIGGER18)) { - if (bot->IsWithinDist(mBossNeutral, 400.0f) && mBossNeutral->getDeathState() != DeathState::Dead && alterValleyBG->GetMineOwner(AV_SOUTH_MINE) == TEAM_OTHER) - { - BgObjective = mBossNeutral; - //std::ostringstream out; - //out << "Attacking Neutral Mine Boss!"; - //bot->Say(out.str(), LANG_UNIVERSAL); - } - } - - 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; - //std::ostringstream out; - //out << "Attacking Horde Mine Boss!"; - //bot->Say(out.str(), LANG_UNIVERSAL); - } - } - } - - if (!BgObjective) - { - // Attack closest objective. - WorldObject* pAttackObjectiveObject = nullptr; - float attackObjectiveDistance = FLT_MAX; - - if (alterValleyBG->IsCaptainAlive(1)) - { - if (Creature* pGalvangar = bg->GetBGCreature(AV_CPLACE_TRIGGER18)) + if (pGalvangar->getDeathState() != DeathState::Dead) { uint32 attackCount = getDefendersCount(AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE, 10.0f, false) + getDefendersCount(AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE, 10.0f, false); @@ -3193,9 +3215,8 @@ bool BGTactics::selectObjective(bool reset) AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE.GetPositionY(), AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE.GetPositionZ(), bg->GetMapId()); - //std::ostringstream out; - //out << "Taking position at Iceblood Outpost!"; + //out << "Taking position at Stonehearth!"; //bot->Say(out.str(), LANG_UNIVERSAL); } else @@ -3205,19 +3226,54 @@ bool BGTactics::selectObjective(bool reset) AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE.GetPositionY(), AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE.GetPositionZ(), bg->GetMapId()); - //std::ostringstream out; - // out << "Attacking Galvangar!"; + //out << "Attacking Galvangar!"; //bot->Say(out.str(), LANG_UNIVERSAL); } - pAttackObjectiveObject = pGalvangar; - attackObjectiveDistance = sqrt(bot->GetDistance(pGalvangar)); + BgObjective = pGalvangar; + } + } + } + + // 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_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); } } + 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); + } + } + } + + if (!BgObjective) + { + // Attack closest objective + WorldObject* pAttackObjectiveObject = nullptr; + float attackObjectiveDistance = FLT_MAX; 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))//TotalOwnerId only changes when its fully captured/destroyed, OwnerId changes when it's assaulted (meaning advancedAttack bots move on to next point) { if (GameObject* pGO = bg->GetBGObject(objective.second)) { @@ -3227,17 +3283,14 @@ bool BGTactics::selectObjective(bool reset) pAttackObjectiveObject = pGO; attackObjectiveDistance = distance; //std::ostringstream out; - // out << "Attacking Node #" << objective.first; + //out << "Attacking Node #" << objective.first; //bot->Say(out.str(), LANG_UNIVERSAL); } } } } - if (pAttackObjectiveObject) - { BgObjective = pAttackObjectiveObject; - } } } if (BgObjective) @@ -3246,7 +3299,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); @@ -4249,7 +4301,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; @@ -4280,7 +4332,8 @@ bool BGTactics::resetObjective() 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))) + uint32 rollChangeOdds = BATTLEGROUND_AV == bg->GetBgTypeID() ? 31 : 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(); From 75daad5bec0e2d304d0a7711ea83af6084b4fab6 Mon Sep 17 00:00:00 2001 From: Fuzz Date: Tue, 16 Jul 2024 20:23:10 +1000 Subject: [PATCH 2/6] fix wrong NPC for vanndar, upped bots gathered before taking out captain because alliance was struggling --- src/strategy/actions/BattleGroundTactics.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/strategy/actions/BattleGroundTactics.cpp b/src/strategy/actions/BattleGroundTactics.cpp index bf99fc07..d2325e18 100644 --- a/src/strategy/actions/BattleGroundTactics.cpp +++ b/src/strategy/actions/BattleGroundTactics.cpp @@ -3042,7 +3042,7 @@ bool BGTactics::selectObjective(bool reset) 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 && !pBalinda->IsInCombat()) { // get in position to attack Captain pos.Set(AV_STONEHEARTH_WAITING_HORDE.GetPositionX(), @@ -3190,7 +3190,7 @@ bool BGTactics::selectObjective(bool reset) } if (!BgObjective) { - if (Creature* myNpc = bg->GetBGCreature(AV_CPLACE_TRIGGER16))//vanndar + if (Creature* myNpc = bg->GetBGCreature(AV_CPLACE_TRIGGER17))//vanndar { if (myNpc->getDeathState() != DeathState::Dead && bot->IsWithinDist(myNpc, 300.f)) BgObjective = myNpc; @@ -3208,7 +3208,7 @@ bool BGTactics::selectObjective(bool reset) 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()) + if (attackCount < 15 && !pGalvangar->IsInCombat()) { // get in position to attack Captain pos.Set(AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE.GetPositionX(), @@ -4260,8 +4260,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; @@ -4312,13 +4315,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); From 6417e836e16a16bff72eadcc5d390641e0753f56 Mon Sep 17 00:00:00 2001 From: Fuzz Date: Mon, 22 Jul 2024 09:02:00 +1000 Subject: [PATCH 3/6] [BattleGrounds] Bots no longer needlessly dismount in BG's when near a creature (eg: AV rams). AV: attacking bots now concentrate on final GY and boss when all enemy towers are down (prevents them running back to previous GY's when they should just down the boss), attacking bots now try to get first enemy GY before first tower (helps alliance), bots respawning in cave starting area's now leave cave before selecting path (they were making bad selections due to all paths starting too far away from spawn point, defending horde bots would try to clip through walls and go directly to horde base GY), new path to get from horde cave to horde base (prevents horde defenders trying to scale the hills/walls to get in), overall balance between teams much improved. new playerbots debug bg commands (that I used to develop/test these changes) --- src/cs_playerbots.cpp | 16 +- src/strategy/actions/BattleGroundTactics.cpp | 531 ++++++++++++------ src/strategy/actions/BattleGroundTactics.h | 3 + .../actions/CheckMountStateAction.cpp | 3 +- 4 files changed, 377 insertions(+), 176 deletions(-) diff --git a/src/cs_playerbots.cpp b/src/cs_playerbots.cpp index 49db6fa3..54296900 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" class playerbots_commandscript : public CommandScript { @@ -27,17 +28,23 @@ public: std::vector GetCommands() const override { + static std::vector playerbotsDebugCommandTable = + { + { "bg", SEC_GAMEMASTER, true, &HandleDebugBGCommand, nullptr }, + }; + static std::vector playerbotsCommandTable = { { "bot", SEC_PLAYER, false, &HandlePlayerbotCommand, nullptr }, { "gtask", SEC_GAMEMASTER, true, &HandleGuildTaskCommand, nullptr }, { "pmon", SEC_GAMEMASTER, true, &HandlePerfMonCommand, nullptr }, - { "rndbot", SEC_GAMEMASTER, true, &HandleRandomPlayerbotCommand, nullptr } + { "rndbot", SEC_GAMEMASTER, true, &HandleRandomPlayerbotCommand, nullptr }, + { "debug", SEC_GAMEMASTER, true, nullptr, "", playerbotsDebugCommandTable}, }; static std::vector commandTable = { - { "playerbots", SEC_PLAYER, true, nullptr, "", playerbotsCommandTable }, + { "playerbots", SEC_PLAYER, true, nullptr, "", playerbotsCommandTable }, }; return commandTable; @@ -81,6 +88,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 d2325e18..c126c68f 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,8 +1630,13 @@ 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 }, @@ -1643,6 +1656,34 @@ BattleBotPath vPath_AV_Icewing_Bunker_Crossroad_to_Frostdagger_Pass_Lower = { -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 }, @@ -2211,6 +2252,7 @@ std::vector const vPaths_AV = &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 = @@ -2299,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 }, @@ -2333,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[] = @@ -2359,12 +2405,148 @@ 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* m_session = handler->GetSession(); + if (!m_session) + { + handler->PSendSysMessage("Command can only be used from an active session"); + return true; + } + Player* player = m_session->GetPlayer(); + if (!player) + { + handler->PSendSysMessage("Error - session player not found"); + return true; + } + if (player->GetSession()->GetSecurity() < SEC_GAMEMASTER) { + handler->PSendSysMessage("Command can only be used by a GM"); + return true; + } + Battleground* bg = player->GetBattleground(); + if (!bg) + { + handler->PSendSysMessage("Command can only be used within a battleground"); + return true; + } + 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) + { + handler->PSendSysMessage("Bad showpath parameter"); + return true; + } + } + 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) + { + handler->PSendSysMessage("This battleground has no paths and is unsupported"); + return true; + } + 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()) + { + handler->PSendSysMessage(fmt::format("Path out of range of 0 - {}", vPaths->size() - 1).c_str()); + return true; + } + 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()); + } + handler->PSendSysMessage(fmt::format("Showing path {}", num).c_str()); + return true; + } + + if (!strncmp(cmd, "showcreature=", 13)) + { + uint32 num; + if (sscanf(cmd, "showcreature=%u", &num) == -1) + { + handler->PSendSysMessage("Bad showcreature parameter"); + return true; + } + Creature* c = bg->GetBGCreature(num); + if (!c) + { + handler->PSendSysMessage("Creature not found"); + return true; + } + Creature* wpCreature = player->SummonCreature(15631, c->GetPositionX(), c->GetPositionY(), c->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 15000u); + wpCreature->SetOwnerGUID(player->GetGUID()); + handler->PSendSysMessage(fmt::format("Showing location of Creature {}", num).c_str()); + return true; + } + + if (!strncmp(cmd, "showobject=", 11)) + { + uint32 num; + if (sscanf(cmd, "showobject=%u", &num) == -1) + { + handler->PSendSysMessage("Bad showobject parameter"); + return true; + } + GameObject* o = bg->GetBGObject(num); + if (!o) + { + handler->PSendSysMessage("GameObject not found"); + return true; + } + Creature* wpCreature = player->SummonCreature(15631, o->GetPositionX(), o->GetPositionY(), o->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 15000u); + wpCreature->SetOwnerGUID(player->GetGUID()); + handler->PSendSysMessage(fmt::format("Showing location of GameObject {}", num).c_str()); + return true; + } + + handler->PSendSysMessage("Unknown command"); + return true; +} + // // old wsg waypoints // - //cross the Battleground to get to flags or flag carriers - bool BGTactics::wsgPaths() { Battleground* bg = bot->GetBattleground(); @@ -2674,7 +2856,6 @@ bool BGTactics::wsgPaths() // // actual bg tactics below // - bool BGTactics::Execute(Event event) { Battleground* bg = bot->GetBattleground(); @@ -2951,125 +3132,124 @@ bool BGTactics::selectObjective(bool reset) BattlegroundTypeId bgType = bg->GetBgTypeID(); if (bgType == BATTLEGROUND_RB) bgType = bg->GetBgTypeID(true); - switch (bgType) { case BATTLEGROUND_AV: { BattlegroundAV* alterValleyBG = (BattlegroundAV*)bg; uint32 role = context->GetValue("bg role")->Get(); - bool supportDefense = role < 3; - bool advancedAttack = role < 7; + bool supportDefense = role < 3;//defensive role and mine capture (mine capture disabled for now) + // TODO fix advancedAttack - its supposed to be mutually exclusive with above (ie: a > check). leaving as-is for now as it seems to effect horde vs ally balance + bool advancedAttack = role < 7;//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 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_FIRSTAID_STATION).TotalOwnerId != TEAM_ALLIANCE && - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_DUNBALDAR_NORTH).TotalOwnerId != TEAM_ALLIANCE && - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_DUNBALDAR_SOUTH).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; - //std::ostringstream out; - //out << "Attacking Vanndar!"; - //bot->Say(out.str(), LANG_UNIVERSAL); + BgObjective = enemyBoss; } } - // Only go to Snowfall Graveyard if already close to it. + // 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_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_ALLIANCE || - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_NEUTRAL)) + 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); } } - // defend - if (!BgObjective && supportDefense) + // (supportDefense) defend objectives under attack + if (!BgObjective && supportDefense) { + // go to first defend objective under attack (the one closest to boss) for (const auto& objective : AV_HordeDefendObjectives) { - if (!BgObjective && - alterValleyBG->GetAVNodeInfo(objective.first).State != POINT_DESTROYED && + if (alterValleyBG->GetAVNodeInfo(objective.first).State != POINT_DESTROYED && alterValleyBG->GetAVNodeInfo(objective.first).OwnerId == TEAM_ALLIANCE) { if (GameObject* pGO = bg->GetBGObject(objective.second)) { BgObjective = pGO; - //std::ostringstream out; - //out << "Defending Node #" << objective.first; - //bot->Say(out.str(), LANG_UNIVERSAL); + break; } } } - // defend captain or boss if close - if (!BgObjective && alterValleyBG->IsCaptainAlive(TEAM_HORDE)) - { - if (Creature* myNpc = bg->GetBGCreature(AV_CPLACE_TRIGGER18))//galvangar - { - if (myNpc->getDeathState() != DeathState::Dead && bot->IsWithinDist(myNpc, 100.f)) - BgObjective = myNpc; - - } - } - if (!BgObjective) - { - if (Creature* myNpc = bg->GetBGCreature(AV_CPLACE_TRIGGER19))//drek - { - if (myNpc->getDeathState() != DeathState::Dead && bot->IsWithinDist(myNpc, 300.f)) - BgObjective = myNpc; - - } - } } + // 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* pBalinda = bg->GetBGCreature(AV_CPLACE_TRIGGER16)) + if (Creature* enemyCaptain = bg->GetBGCreature(AV_CREATURE_A_CAPTAIN)) { - if (pBalinda->getDeathState() != DeathState::Dead) + 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 < 15 && !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; } } } - // Mine capture (disabled for now) + // (supportDefense) defend our captain if alive + if (!BgObjective && supportDefense && alterValleyBG->IsCaptainAlive(TEAM_HORDE)) + { + if (Creature* myCaptain = bg->GetBGCreature(AV_CREATURE_H_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_HordeDefendObjectives)//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_NORTH_MINE) == TEAM_ALLIANCE || alterValleyBG->GetMineOwner(AV_NORTH_MINE) == TEAM_NEUTRAL) && alterValleyBG->GetAVNodeInfo(BG_AV_NODES_STORMPIKE_GRAVE).OwnerId != TEAM_ALLIANCE) @@ -3085,7 +3265,7 @@ bool BGTactics::selectObjective(bool reset) //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_NEUTRAL) @@ -3099,143 +3279,142 @@ 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_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) { - // Attack closest objective - WorldObject* pAttackObjectiveObject = nullptr; float attackObjectiveDistance = FLT_MAX; for (const auto& objective : AV_HordeAttackObjectives) { if ((!advancedAttack && alterValleyBG->GetAVNodeInfo(objective.first).TotalOwnerId == TEAM_ALLIANCE) || - (alterValleyBG->GetAVNodeInfo(objective.first).OwnerId == TEAM_ALLIANCE))//TotalOwnerId only changes when its fully captured/destroyed, OwnerId changes when it's assaulted (meaning advancedAttack bots move on to next point) + (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)) { 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; } } else //TEAM_ALLIANCE { + 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 (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_HUT).TotalOwnerId != TEAM_HORDE && - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_WTOWER).TotalOwnerId != TEAM_HORDE && - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_ETOWER).TotalOwnerId != TEAM_HORDE && - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_TOWER_POINT).TotalOwnerId != TEAM_HORDE && - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_ICEBLOOD_TOWER).TotalOwnerId != TEAM_HORDE) + 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; - //std::ostringstream out; - //out << "Attacking DrekThar!"; - //bot->Say(out.str(), LANG_UNIVERSAL); + BgObjective = enemyBoss; } } - // Only go to Snowfall Graveyard if already close to it. + // 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_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_HORDE || - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_NEUTRAL)) + 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); } } - // defend + // (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).State != POINT_DESTROYED && + 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; } } } - // defend captain or boss if close - if (!BgObjective && alterValleyBG->IsCaptainAlive(TEAM_ALLIANCE)) - { - if (Creature* myNpc = bg->GetBGCreature(AV_CPLACE_TRIGGER16))//balinda - { - if (myNpc->getDeathState() != DeathState::Dead && bot->IsWithinDist(myNpc, 100.f)) - BgObjective = myNpc; - - } - } - if (!BgObjective) - { - if (Creature* myNpc = bg->GetBGCreature(AV_CPLACE_TRIGGER17))//vanndar - { - if (myNpc->getDeathState() != DeathState::Dead && bot->IsWithinDist(myNpc, 300.f)) - BgObjective = myNpc; - - } - } } + // 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* pGalvangar = bg->GetBGCreature(AV_CPLACE_TRIGGER18)) + if (Creature* enemyCaptain = bg->GetBGCreature(AV_CREATURE_H_CAPTAIN)) { - if (pGalvangar->getDeathState() != DeathState::Dead) + 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 && !pGalvangar->IsInCombat()) + 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()); - //std::ostringstream out; - //out << "Taking position at Stonehearth!"; - //bot->Say(out.str(), LANG_UNIVERSAL); + 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()); - //std::ostringstream out; - //out << "Attacking Galvangar!"; - //bot->Say(out.str(), LANG_UNIVERSAL); + pos.Set(AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE.GetPositionX(), AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE.GetPositionY(), AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE.GetPositionZ(), bg->GetMapId()); } - BgObjective = pGalvangar; } } } - // Mine capture (disabled for now) + // (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) @@ -3251,7 +3430,7 @@ bool BGTactics::selectObjective(bool reset) //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) @@ -3265,32 +3444,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; for (const auto& objective : AV_AllianceAttackObjectives) { if ((!advancedAttack && alterValleyBG->GetAVNodeInfo(objective.first).TotalOwnerId == TEAM_HORDE) || - (alterValleyBG->GetAVNodeInfo(objective.first).OwnerId == TEAM_HORDE))//TotalOwnerId only changes when its fully captured/destroyed, OwnerId changes when it's assaulted (meaning advancedAttack bots move on to next point) + (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) @@ -3299,9 +3482,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; @@ -4184,6 +4364,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(); @@ -4336,7 +4525,7 @@ bool BGTactics::resetObjective() return false; // sometimes change role - uint32 rollChangeOdds = BATTLEGROUND_AV == bg->GetBgTypeID() ? 31 : 5; + 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)); @@ -4571,11 +4760,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..298072b3 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,6 +37,8 @@ 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; diff --git a/src/strategy/actions/CheckMountStateAction.cpp b/src/strategy/actions/CheckMountStateAction.cpp index a4f7ef9c..bc6774ba 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; From be551a5618e4217ec70653211248d7a2a97a0a79 Mon Sep 17 00:00:00 2001 From: Fuzz Date: Mon, 22 Jul 2024 15:50:05 +1000 Subject: [PATCH 4/6] [BattleGrounds] just a few little fixes before PR --- src/strategy/actions/BattleGroundTactics.cpp | 86 +++++++------------- 1 file changed, 30 insertions(+), 56 deletions(-) diff --git a/src/strategy/actions/BattleGroundTactics.cpp b/src/strategy/actions/BattleGroundTactics.cpp index dabf8455..37581e0e 100644 --- a/src/strategy/actions/BattleGroundTactics.cpp +++ b/src/strategy/actions/BattleGroundTactics.cpp @@ -2413,33 +2413,33 @@ bool BGTactics::HandleConsoleCommand(ChatHandler* handler, char const* args) handler->PSendSysMessage("|cffff0000Playerbot system is currently disabled!"); return true; } - WorldSession* m_session = handler->GetSession(); - if (!m_session) + WorldSession* session = handler->GetSession(); + if (!session) { handler->PSendSysMessage("Command can only be used from an active session"); return true; } - Player* player = m_session->GetPlayer(); + std::string const commandOutput = HandleConsoleCommand(session, args); + if (!commandOutput.empty()) + handler->PSendSysMessage(commandOutput.c_str()); + return true; +} + +std::string const BGTactics::HandleConsoleCommand(WorldSession* session, char const* args) +{ + Player* player = session->GetPlayer(); if (!player) - { - handler->PSendSysMessage("Error - session player not found"); - return true; - } - if (player->GetSession()->GetSecurity() < SEC_GAMEMASTER) { - handler->PSendSysMessage("Command can only be used by a GM"); - return true; - } + 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) - { - handler->PSendSysMessage("Command can only be used within a battleground"); - return true; - } + 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, " "); + //char* charname = strtok(nullptr, " "); if (!strncmp(cmd, "showpath", 8)) { @@ -2447,10 +2447,7 @@ bool BGTactics::HandleConsoleCommand(ChatHandler* handler, char const* args) if (!strncmp(cmd, "showpath=", 9)) { if (sscanf(cmd, "showpath=%d", &num) == -1 || num < 0) - { - handler->PSendSysMessage("Bad showpath parameter"); - return true; - } + return "Bad showpath parameter"; } std::vector const* vPaths; switch (bgType) @@ -2463,10 +2460,7 @@ bool BGTactics::HandleConsoleCommand(ChatHandler* handler, char const* args) default: vPaths = nullptr; break; } if (!vPaths) - { - handler->PSendSysMessage("This battleground has no paths and is unsupported"); - return true; - } + return "This battleground has no paths and is unsupported"; if (num == -1) { float closestPoint = FLT_MAX; for (uint32 j = 0; j < vPaths->size(); j++) @@ -2484,10 +2478,7 @@ bool BGTactics::HandleConsoleCommand(ChatHandler* handler, char const* args) } } if (num >= (*vPaths).size()) - { - handler->PSendSysMessage(fmt::format("Path out of range of 0 - {}", vPaths->size() - 1).c_str()); - return true; - } + 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++) { @@ -2495,52 +2486,36 @@ bool BGTactics::HandleConsoleCommand(ChatHandler* handler, char const* args) Creature* wpCreature = player->SummonCreature(15631, waypoint.x, waypoint.y, waypoint.z, 0, TEMPSUMMON_TIMED_DESPAWN, 15000u); wpCreature->SetOwnerGUID(player->GetGUID()); } - handler->PSendSysMessage(fmt::format("Showing path {}", num).c_str()); - return true; + return fmt::format("Showing path {}", num); } if (!strncmp(cmd, "showcreature=", 13)) { uint32 num; if (sscanf(cmd, "showcreature=%u", &num) == -1) - { - handler->PSendSysMessage("Bad showcreature parameter"); - return true; - } + return "Bad showcreature parameter"; Creature* c = bg->GetBGCreature(num); if (!c) - { - handler->PSendSysMessage("Creature not found"); - return true; - } + return "Creature not found"; Creature* wpCreature = player->SummonCreature(15631, c->GetPositionX(), c->GetPositionY(), c->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 15000u); wpCreature->SetOwnerGUID(player->GetGUID()); - handler->PSendSysMessage(fmt::format("Showing location of Creature {}", num).c_str()); - return true; + return fmt::format("Showing location of Creature {}", num); } if (!strncmp(cmd, "showobject=", 11)) { uint32 num; if (sscanf(cmd, "showobject=%u", &num) == -1) - { - handler->PSendSysMessage("Bad showobject parameter"); - return true; - } + return "Bad showobject parameter"; GameObject* o = bg->GetBGObject(num); if (!o) - { - handler->PSendSysMessage("GameObject not found"); - return true; - } + return "GameObject not found"; Creature* wpCreature = player->SummonCreature(15631, o->GetPositionX(), o->GetPositionY(), o->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 15000u); wpCreature->SetOwnerGUID(player->GetGUID()); - handler->PSendSysMessage(fmt::format("Showing location of GameObject {}", num).c_str()); - return true; + return fmt::format("Showing location of GameObject {}", num); } - handler->PSendSysMessage("Unknown command"); - return true; + return "usage: showpath(=[num]) / showcreature=[num] / showobject=[num]"; } // @@ -3139,8 +3114,7 @@ bool BGTactics::selectObjective(bool reset) 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) - // TODO fix advancedAttack - its supposed to be mutually exclusive with above (ie: a > check). leaving as-is for now as it seems to effect horde vs ally balance - bool advancedAttack = role < 7;//doesnt wait for point to be fully captured before moving on + 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 @@ -3187,7 +3161,7 @@ bool BGTactics::selectObjective(bool reset) // (supportDefense) defend objectives under attack if (!BgObjective && supportDefense) { - // go to first defend objective under attack (the one closest to boss) + // go to first defence objective under attack (the one closest to boss) for (const auto& objective : AV_HordeDefendObjectives) { if (alterValleyBG->GetAVNodeInfo(objective.first).State != POINT_DESTROYED && @@ -4524,7 +4498,7 @@ bool BGTactics::resetObjective() if (!bg) return false; - // sometimes change role + // 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)); From d1f8b2f9ddf6dee2a7572f322821f8da9a268b82 Mon Sep 17 00:00:00 2001 From: Fuzz Date: Mon, 22 Jul 2024 16:03:12 +1000 Subject: [PATCH 5/6] [BattleGrounds] fix for mac/linux build failing --- src/strategy/actions/BattleGroundTactics.cpp | 11 ++++++++--- src/strategy/actions/BattleGroundTactics.h | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/strategy/actions/BattleGroundTactics.cpp b/src/strategy/actions/BattleGroundTactics.cpp index 37581e0e..e2ec787c 100644 --- a/src/strategy/actions/BattleGroundTactics.cpp +++ b/src/strategy/actions/BattleGroundTactics.cpp @@ -2419,13 +2419,14 @@ bool BGTactics::HandleConsoleCommand(ChatHandler* handler, char const* args) handler->PSendSysMessage("Command can only be used from an active session"); return true; } - std::string const commandOutput = HandleConsoleCommand(session, args); + std::string const commandOutput = HandleConsoleCommandPrivate(session, args); if (!commandOutput.empty()) handler->PSendSysMessage(commandOutput.c_str()); return true; } -std::string const BGTactics::HandleConsoleCommand(WorldSession* session, char const* args) +// 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) @@ -2477,7 +2478,7 @@ std::string const BGTactics::HandleConsoleCommand(WorldSession* session, char co } } } - if (num >= (*vPaths).size()) + 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++) @@ -2494,6 +2495,8 @@ std::string const BGTactics::HandleConsoleCommand(WorldSession* session, char co 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"; @@ -2507,6 +2510,8 @@ std::string const BGTactics::HandleConsoleCommand(WorldSession* session, char co 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"; diff --git a/src/strategy/actions/BattleGroundTactics.h b/src/strategy/actions/BattleGroundTactics.h index 298072b3..db280fff 100644 --- a/src/strategy/actions/BattleGroundTactics.h +++ b/src/strategy/actions/BattleGroundTactics.h @@ -44,6 +44,7 @@ class BGTactics : public MovementAction 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(); From b61690ce7a526a1eb5c93668478bfefc75123970 Mon Sep 17 00:00:00 2001 From: Mikhail <5454720+pilovm@users.noreply.github.com> Date: Mon, 22 Jul 2024 11:17:36 +0200 Subject: [PATCH 6/6] Does not reassign castCount so it being casted only once --- src/strategy/actions/CastCustomSpellAction.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/strategy/actions/CastCustomSpellAction.cpp b/src/strategy/actions/CastCustomSpellAction.cpp index a3914ea3..deb65abb 100644 --- a/src/strategy/actions/CastCustomSpellAction.cpp +++ b/src/strategy/actions/CastCustomSpellAction.cpp @@ -74,8 +74,7 @@ bool CastCustomSpellAction::Execute(Event event) itemTarget = *items.begin(); else { - castCount = atoi(param.c_str()); - if (castCount > 0) + if (atoi(param.c_str()) > 0) text = text.substr(0, pos); } }