diff --git a/src/PlayerbotFactory.cpp b/src/PlayerbotFactory.cpp index 4591471b..84812822 100644 --- a/src/PlayerbotFactory.cpp +++ b/src/PlayerbotFactory.cpp @@ -55,6 +55,15 @@ PlayerbotFactory::PlayerbotFactory(Player* bot, uint32 level, uint32 itemQuality : level(level), itemQuality(itemQuality), gearScoreLimit(gearScoreLimit), bot(bot) { botAI = GET_PLAYERBOT_AI(bot); + if (!this->itemQuality) + { + uint32 gs = sPlayerbotAIConfig->randomGearScoreLimit == 0 + ? 0 + : PlayerbotFactory::CalcMixedGearScore(sPlayerbotAIConfig->randomGearScoreLimit, + sPlayerbotAIConfig->randomGearQualityLimit); + this->itemQuality = sPlayerbotAIConfig->randomGearQualityLimit; + this->gearScoreLimit = gs; + } } void PlayerbotFactory::Init() @@ -149,16 +158,6 @@ void PlayerbotFactory::Init() void PlayerbotFactory::Prepare() { - if (!itemQuality) - { - uint32 gs = sPlayerbotAIConfig->randomGearScoreLimit == 0 - ? 0 - : PlayerbotFactory::CalcMixedGearScore(sPlayerbotAIConfig->randomGearScoreLimit, - sPlayerbotAIConfig->randomGearQualityLimit); - itemQuality = sPlayerbotAIConfig->randomGearQualityLimit; - gearScoreLimit = gs; - } - if (bot->isDead()) bot->ResurrectPlayer(1.0f, false); diff --git a/src/RandomPlayerbotMgr.cpp b/src/RandomPlayerbotMgr.cpp index e6704884..2655052c 100644 --- a/src/RandomPlayerbotMgr.cpp +++ b/src/RandomPlayerbotMgr.cpp @@ -1815,8 +1815,7 @@ void RandomPlayerbotMgr::Refresh(Player* bot) bot->DurabilityRepairAll(false, 1.0f, false); bot->SetFullHealth(); bot->SetPvP(true); - - PlayerbotFactory factory(bot, bot->GetLevel(), ITEM_QUALITY_RARE); + PlayerbotFactory factory(bot, bot->GetLevel()); factory.Refresh(); if (bot->GetMaxPower(POWER_MANA) > 0) diff --git a/src/strategy/actions/AutoLearnSpellAction.cpp b/src/strategy/actions/AutoLearnSpellAction.cpp index e75945f6..4415eb5d 100644 --- a/src/strategy/actions/AutoLearnSpellAction.cpp +++ b/src/strategy/actions/AutoLearnSpellAction.cpp @@ -210,7 +210,7 @@ bool AutoUpgradeEquipAction::Execute(Event event) { return false; } - PlayerbotFactory factory(bot, bot->GetLevel(), ITEM_QUALITY_RARE); + PlayerbotFactory factory(bot, bot->GetLevel()); if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel) { factory.InitEquipment(true); diff --git a/src/strategy/actions/BattleGroundTactics.cpp b/src/strategy/actions/BattleGroundTactics.cpp index 03b838d5..e7f90ea9 100644 --- a/src/strategy/actions/BattleGroundTactics.cpp +++ b/src/strategy/actions/BattleGroundTactics.cpp @@ -22,6 +22,7 @@ #include "Event.h" #include "Playerbots.h" #include "PositionValue.h" +#include "PvpTriggers.h" #include "ServerFacade.h" #include "Vehicle.h" @@ -66,18 +67,22 @@ Position const EY_WAITING_POS_ALLIANCE = {2526.020f, 1596.787f, 1270.127f, 3.14f Position const EY_FLAG_RETURN_POS_RETREAT_HORDE = {1885.529f, 1532.157f, 1200.635f, 0.0f}; Position const EY_FLAG_RETURN_POS_RETREAT_ALLIANCE = {2452.253f, 1602.356f, 1203.617f, 0.0f}; -Position const IC_WAITING_POS_HORDE = {1166.322f, -762.402f, 48.628f, 3.14f}; -Position const IC_WAITING_POS_ALLIANCE = {387.893f, -833.384f, 48.714f, 6.28f}; +Position const IC_WAITING_POS_HORDE = {1166.322f, -762.402f, 48.628f}; +Position const IC_WEST_WAITING_POS_HORDE = {1217.666f, -685.449f, 48.915f}; +Position const IC_EAST_WAITING_POS_HORDE = {1219.068f, -838.791f, 48.916f}; -Position const IC_WEST_WAITING_POS_HORDE = {1217.666f, -685.449f, 48.915f, 1.54f}; -Position const IC_EAST_WAITING_POS_HORDE = {1219.068f, -838.791f, 48.916f, 4.72f}; -Position const IC_SIDE_WAITING_POS_ALLIANCE = {351.517f, -882.477f, 48.916f, 4.68f}; +Position const IC_WAITING_POS_ALLIANCE = {387.893f, -833.384f, 48.714f}; +Position const IC_WEST_WAITING_POS_ALLIANCE = {352.129f, -788.029f, 48.916f}; +Position const IC_EAST_WAITING_POS_ALLIANCE = {351.517f, -882.477f, 48.916f}; Position const IC_CANNON_POS_HORDE1 = {1140.938f, -838.685f, 88.124f, 2.30f}; Position const IC_CANNON_POS_HORDE2 = {1139.695f, -686.574f, 88.173f, 3.95f}; Position const IC_CANNON_POS_ALLIANCE1 = {424.860f, -855.795f, 87.96f, 0.44f}; Position const IC_CANNON_POS_ALLIANCE2 = {425.525f, -779.538f, 87.717f, 5.88f}; +Position const IC_GATE_ATTACK_POS_HORDE = {506.782f, -828.594f, 24.313f, 0.0f}; +Position const IC_GATE_ATTACK_POS_ALLIANCE = {1091.273f, -763.619f, 42.352f, 0.0f}; + enum BattleBotWsgWaitSpot { BB_WSG_WAIT_SPOT_SPAWN, @@ -100,9 +105,41 @@ std::vector const vFlagsWS = {BG_OBJECT_A_FLAG_WS_ENTRY, BG_OBJECT_H_FLA std::vector const vFlagsEY = {BG_OBJECT_FLAG2_EY_ENTRY, BG_OBJECT_FLAG3_EY_ENTRY}; -std::vector const vFlagsIC = {BG_IC_GO_HORDE_BANNER, BG_IC_GO_ALLIANCE_BANNER, BG_IC_GO_WORKSHOP_BANNER, - BG_IC_GO_DOCKS_BANNER, BG_IC_GO_HANGAR_BANNER, BG_IC_GO_QUARRY_BANNER, - BG_IC_GO_REFINERY_BANNER}; +std::vector const vFlagsIC = {GO_HORDE_BANNER, + GO_ALLIANCE_BANNER, + GO_WORKSHOP_BANNER, + GO_DOCKS_BANNER, + GO_HANGAR_BANNER, + GO_QUARRY_BANNER, + GO_REFINERY_BANNER, + GO_ALLIANCE_BANNER_DOCK, + GO_ALLIANCE_BANNER_DOCK_CONT, + GO_HORDE_BANNER_DOCK, + GO_HORDE_BANNER_DOCK_CONT, + GO_HORDE_BANNER_HANGAR, + GO_HORDE_BANNER_HANGAR_CONT, + GO_ALLIANCE_BANNER_HANGAR, + GO_ALLIANCE_BANNER_HANGAR_CONT, + GO_ALLIANCE_BANNER_QUARRY, + GO_ALLIANCE_BANNER_QUARRY_CONT, + GO_HORDE_BANNER_QUARRY, + GO_HORDE_BANNER_QUARRY_CONT, + GO_ALLIANCE_BANNER_REFINERY, + GO_ALLIANCE_BANNER_REFINERY_CONT, + GO_HORDE_BANNER_REFINERY, + GO_HORDE_BANNER_REFINERY_CONT, + GO_ALLIANCE_BANNER_WORKSHOP, + GO_ALLIANCE_BANNER_WORKSHOP_CONT, + GO_HORDE_BANNER_WORKSHOP, + GO_HORDE_BANNER_WORKSHOP_CONT, + GO_ALLIANCE_BANNER_GRAVEYARD_A, + GO_ALLIANCE_BANNER_GRAVEYARD_A_CONT, + GO_HORDE_BANNER_GRAVEYARD_A, + GO_HORDE_BANNER_GRAVEYARD_A_CONT, + GO_ALLIANCE_BANNER_GRAVEYARD_H, + GO_ALLIANCE_BANNER_GRAVEYARD_H_CONT, + GO_HORDE_BANNER_GRAVEYARD_H, + GO_HORDE_BANNER_GRAVEYARD_H_CONT}; // BG Waypoints (vmangos) @@ -1024,8 +1061,9 @@ BattleBotPath vPath_AV_Frostdagger_Pass_Lower_to_Iceblood_Garrison = { // 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) + // the first 3 are to cause bot to pick this when resurrecting at stonehearth - seems to be key thing to + // stopping alliance getting bogged down in mid fighting, away from their objective (enemy captain) but close to + // horde objectives (advantaging horde significantly) {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}, @@ -1328,6 +1366,57 @@ BattleBotPath vPath_IC_Workshop_to_Workshop_Keep = {{773.792f, -825.637f, 8.127f {772.706f, -841.881f, 11.622f, nullptr}, {773.057f, -859.936f, 12.418f, nullptr}}; +BattleBotPath vPath_IC_Alliance_Base = { + {402.020f, -832.289f, 48.627f, nullptr}, {384.784f, -832.551f, 48.830f, nullptr}, + {369.413f, -832.480f, 48.916f, nullptr}, {346.677f, -832.355f, 48.916f, nullptr}, + {326.296f, -832.189f, 48.916f, nullptr}, {311.174f, -832.204f, 48.916f, nullptr}, +}; + +BattleBotPath vPath_IC_Horde_Base = { + {1158.652f, -762.680f, 48.628f, nullptr}, {1171.598f, -762.628f, 48.649f, nullptr}, + {1189.102f, -763.484f, 48.915f, nullptr}, {1208.599f, -764.332f, 48.915f, nullptr}, + {1227.592f, -764.782f, 48.915f, nullptr}, {1253.676f, -765.441f, 48.915f, nullptr}, +}; + +BattleBotPath vPath_IC_Workshop_to_North_West = { + {786.051f, -801.798f, 5.968f, nullptr}, {801.767f, -800.845f, 6.201f, nullptr}, + {830.190f, -800.059f, 5.163f, nullptr}, {858.827f, -797.691f, 5.602f, nullptr}, + {881.579f, -795.190f, 6.441f, nullptr}, {905.796f, -792.252f, 8.008f, nullptr}, + {929.874f, -788.912f, 10.674f, nullptr}, {960.001f, -784.233f, 15.521f, nullptr}, + {991.890f, -780.605f, 22.402f, nullptr}, {1014.062f, -761.863f, 28.672f, nullptr}, + {1019.925f, -730.822f, 27.421f, nullptr}, {1022.320f, -698.581f, 25.993f, nullptr}, + {1022.539f, -665.405f, 24.574f, nullptr}, {1016.279f, -632.780f, 24.487f, nullptr}, + {1004.839f, -602.680f, 24.501f, nullptr}, {992.826f, -567.833f, 24.558f, nullptr}, + {984.998f, -535.303f, 24.485f, nullptr}, +}; + +BattleBotPath vPath_IC_South_West_Crossroads = { + {528.932f, -667.953f, 25.413f, nullptr}, {514.800f, -650.381f, 26.171f, nullptr}, + {488.367f, -621.869f, 25.820f, nullptr}, {479.491f, -594.284f, 26.095f, nullptr}, + {498.094f, -557.031f, 26.015f, nullptr}, {528.272f, -528.761f, 26.015f, nullptr}, + {595.746f, -480.009f, 26.007f, nullptr}, {632.156f, -458.182f, 27.416f, nullptr}, + {656.013f, -446.685f, 28.003f, nullptr}, +}; + +BattleBotPath vPath_IC_Hanger_to_Workshop = { + {808.923f, -1003.441f, 132.380f, nullptr}, {804.031f, -1002.785f, 132.382f, nullptr}, + {798.466f, -1021.472f, 132.292f, nullptr}, {795.472f, -1031.693f, 130.232f, nullptr}, + {804.631f, -1042.672f, 125.310f, nullptr}, {827.549f, -1061.778f, 111.276f, nullptr}, + {847.424f, -1077.930f, 98.061f, nullptr}, {868.225f, -1091.563f, 83.794f, nullptr}, + {894.100f, -1104.828f, 66.570f, nullptr}, {923.615f, -1117.689f, 47.391f, nullptr}, + {954.614f, -1119.716f, 27.880f, nullptr}, {970.397f, -1116.281f, 22.124f, nullptr}, + {985.906f, -1105.714f, 18.572f, nullptr}, {996.308f, -1090.605f, 17.669f, nullptr}, + {1003.693f, -1072.916f, 16.583f, nullptr}, {1008.660f, -1051.310f, 15.970f, nullptr}, + {1008.437f, -1026.678f, 15.623f, nullptr}, {1000.103f, -999.047f, 16.487f, nullptr}, + {988.412f, -971.096f, 17.796f, nullptr}, {971.503f, -945.742f, 14.438f, nullptr}, + {947.420f, -931.306f, 13.209f, nullptr}, {922.920f, -916.960f, 10.859f, nullptr}, + {901.607f, -902.203f, 9.854f, nullptr}, {881.932f, -882.226f, 7.848f, nullptr}, + {861.795f, -862.477f, 6.731f, nullptr}, {844.186f, -845.952f, 6.192f, nullptr}, + {826.565f, -826.551f, 5.171f, nullptr}, {809.497f, -815.351f, 6.179f, nullptr}, + {790.787f, -809.678f, 6.450f, nullptr}, +}; + + std::vector const vPaths_WS = { &vPath_WSG_HordeFlagRoom_to_HordeGraveyard, &vPath_WSG_HordeGraveyard_to_HordeTunnel, &vPath_WSG_HordeTunnel_to_HordeFlagRoom, &vPath_WSG_HordeTunnel_to_AllianceTunnel_1, @@ -1463,6 +1552,11 @@ std::vector const vPaths_IC = { &vPath_IC_Central_Graveyard_to_Workshop, &vPath_IC_Horde_East_Gate_to_Horde_Keep, &vPath_IC_Workshop_to_Workshop_Keep, + &vPath_IC_Alliance_Base, + &vPath_IC_Horde_Base, + &vPath_IC_Workshop_to_North_West, + &vPath_IC_South_West_Crossroads, + &vPath_IC_Hanger_to_Workshop, }; std::vector const vPaths_NoReverseAllowed = { @@ -1535,7 +1629,6 @@ static std::pair AV_AllianceDefendObjectives[] = { }; static uint32 AB_AttackObjectives[] = { - // Attack BG_AB_NODE_STABLES, BG_AB_NODE_BLACKSMITH, BG_AB_NODE_FARM, BG_AB_NODE_LUMBER_MILL, BG_AB_NODE_GOLD_MINE}; static std::tuple EY_AttackObjectives[] = { @@ -1544,6 +1637,12 @@ static std::tuple EY_AttackObjectives[] = { {POINT_DRAENEI_RUINS, BG_EY_OBJECT_FLAG_DRAENEI_RUINS, AT_DRAENEI_RUINS_POINT}, {POINT_MAGE_TOWER, BG_EY_OBJECT_FLAG_MAGE_TOWER, AT_MAGE_TOWER_POINT}}; +static std::pair IC_AttackObjectives[] = { + {NODE_TYPE_WORKSHOP, BG_IC_GO_WORKSHOP_BANNER}, + {NODE_TYPE_DOCKS, BG_IC_GO_DOCKS_BANNER}, + {NODE_TYPE_HANGAR, BG_IC_GO_HANGAR_BANNER}, +}; + // useful commands for fixing BG bugs and checking waypoints/paths bool BGTactics::HandleConsoleCommand(ChatHandler* handler, char const* args) { @@ -1584,7 +1683,11 @@ std::string const BGTactics::HandleConsoleCommandPrivate(WorldSession* session, if (!strncmp(cmd, "showpath", 8)) { int num = -1; - if (!strncmp(cmd, "showpath=", 9)) + if (!strncmp(cmd, "showpath=all", 12)) + { + num = -2; + } + else if (!strncmp(cmd, "showpath=", 9)) { if (sscanf(cmd, "showpath=%d", &num) == -1 || num < 0) return "Bad showpath parameter"; @@ -1631,17 +1734,29 @@ std::string const BGTactics::HandleConsoleCommandPrivate(WorldSession* session, } } } - 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++) + uint32 min = 0u; + uint32 max = vPaths->size() - 1; + if (num >= 0) // num specified or found { - BattleBotWaypoint& waypoint = ((*path)[i]); - Creature* wpCreature = - player->SummonCreature(15631, waypoint.x, waypoint.y, waypoint.z, 0, TEMPSUMMON_TIMED_DESPAWN, 15000u); - wpCreature->SetOwnerGUID(player->GetGUID()); + if (num > max) + return fmt::format("Path {} of range of 0 - {}", num, max); + min = num; + max = num; } - return fmt::format("Showing path {}", num); + for (uint32 j = min; j <= max; j++) + { + auto const& path = (*vPaths)[j]; + 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()); + } + } + if (num >= 0) + return fmt::format("Showing path {}", num); + return fmt::format("Showing paths 0 - {}", max); } if (!strncmp(cmd, "showcreature=", 13)) @@ -1657,7 +1772,10 @@ std::string const BGTactics::HandleConsoleCommandPrivate(WorldSession* session, 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); + float distance = player->GetDistance(c); + float exactDistance = player->GetExactDist(c); + return fmt::format("Showing Creature {} location={:.3f},{:.3f},{:.3f} distance={} exactDistance={}", + num, c->GetPositionX(), c->GetPositionY(), c->GetPositionZ(), distance, exactDistance); } if (!strncmp(cmd, "showobject=", 11)) @@ -1673,7 +1791,10 @@ std::string const BGTactics::HandleConsoleCommandPrivate(WorldSession* session, 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); + float distance = player->GetDistance(o); + float exactDistance = player->GetExactDist(o); + return fmt::format("Showing GameObject {} location={:.3f},{:.3f},{:.3f} distance={} exactDistance={}", + num, o->GetPositionX(), o->GetPositionY(), o->GetPositionZ(), distance, exactDistance); } return "usage: showpath(=[num]) / showcreature=[num] / showobject=[num]"; @@ -1939,13 +2060,13 @@ bool BGTactics::wsgPaths() return true; } - if (bot->GetPositionX() > 1071.f) // move the ramp up a piece + if (bot->GetPositionX() > 1059.f) // move the ramp up a piece { - MoveTo(bg->GetMapId(), 1070.089478f, 1538.054443f, 332.460388f); + MoveTo(bg->GetMapId(), 1057.551f, 1546.271f, 326.864f); return true; } - if (bot->GetPositionX() > 1050.2f) // move the ramp up a piece + if (bot->GetPositionX() > 1051.2f) // move the ramp up a piece { MoveTo(bg->GetMapId(), 1050.089478f, 1538.054443f, 332.460388f); return true; @@ -2150,9 +2271,9 @@ bool BGTactics::Execute(Event event) if (useBuff()) return true; - if (bot->IsInCombat() && - !(bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) || bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG) || - bot->HasAura(BG_EY_NETHERSTORM_FLAG_SPELL))) + // NOTE: can't use IsInCombat() when in vehicle as player is stuck in combat forever while in vehicle (ac bug?) + bool inCombat = bot->GetVehicle() ? (bool)AI_VALUE(Unit*, "enemy player target") : bot->IsInCombat(); + if (inCombat && !PlayerHasFlag::IsCapturingFlag(bot)) { // bot->GetMotionMaster()->MovementExpired(); return false; @@ -2274,53 +2395,33 @@ bool BGTactics::moveToStart(bool force) if (bot->GetTeamId() == TEAM_HORDE) { - if (role < 3) - { - if (urand(0, 1)) - MoveTo(bg->GetMapId(), IC_WEST_WAITING_POS_HORDE.GetPositionX() + frand(-5.0f, 5.0f), - IC_WEST_WAITING_POS_HORDE.GetPositionY() + frand(-5.0f, 5.0f), - IC_WEST_WAITING_POS_HORDE.GetPositionZ()); - else - MoveTo(bg->GetMapId(), IC_WAITING_POS_HORDE.GetPositionX() + frand(-5.0f, 5.0f), - IC_WAITING_POS_HORDE.GetPositionY() + frand(-5.0f, 5.0f), - IC_WAITING_POS_HORDE.GetPositionZ()); - } - else - { - if (urand(0, 1)) - MoveTo(bg->GetMapId(), IC_EAST_WAITING_POS_HORDE.GetPositionX() + frand(-5.0f, 5.0f), - IC_EAST_WAITING_POS_HORDE.GetPositionY() + frand(-5.0f, 5.0f), - IC_EAST_WAITING_POS_HORDE.GetPositionZ()); - else - MoveTo(bg->GetMapId(), IC_WAITING_POS_HORDE.GetPositionX() + frand(-5.0f, 5.0f), - IC_WAITING_POS_HORDE.GetPositionY() + frand(-5.0f, 5.0f), - IC_WAITING_POS_HORDE.GetPositionZ()); - } + if (role == 9) // refinery + MoveTo(bg->GetMapId(), IC_WEST_WAITING_POS_HORDE.GetPositionX() + frand(-5.0f, 5.0f), + IC_WEST_WAITING_POS_HORDE.GetPositionY() + frand(-5.0f, 5.0f), + IC_WEST_WAITING_POS_HORDE.GetPositionZ()); + else if (role >= 3 && role < 6) // hanger + MoveTo(bg->GetMapId(), IC_EAST_WAITING_POS_HORDE.GetPositionX() + frand(-5.0f, 5.0f), + IC_EAST_WAITING_POS_HORDE.GetPositionY() + frand(-5.0f, 5.0f), + IC_EAST_WAITING_POS_HORDE.GetPositionZ()); + else // everything else + MoveTo(bg->GetMapId(), IC_WAITING_POS_HORDE.GetPositionX() + frand(-5.0f, 5.0f), + IC_WAITING_POS_HORDE.GetPositionY() + frand(-5.0f, 5.0f), IC_WAITING_POS_HORDE.GetPositionZ()); } else { - if (role < 3) - { - if (urand(0, 1)) - MoveTo(bg->GetMapId(), IC_SIDE_WAITING_POS_ALLIANCE.GetPositionX() + frand(-5.0f, 5.0f), - IC_SIDE_WAITING_POS_ALLIANCE.GetPositionY() + frand(-5.0f, 5.0f), - IC_SIDE_WAITING_POS_ALLIANCE.GetPositionZ()); - else - MoveTo(bg->GetMapId(), IC_WAITING_POS_ALLIANCE.GetPositionX() + frand(-5.0f, 5.0f), - IC_WAITING_POS_ALLIANCE.GetPositionY() + frand(-5.0f, 5.0f), - IC_WAITING_POS_ALLIANCE.GetPositionZ()); - } - else - { - if (urand(0, 1)) - MoveTo(bg->GetMapId(), IC_SIDE_WAITING_POS_ALLIANCE.GetPositionX() + frand(-5.0f, 5.0f), - IC_SIDE_WAITING_POS_ALLIANCE.GetPositionY() + frand(-5.0f, 5.0f), - IC_SIDE_WAITING_POS_ALLIANCE.GetPositionZ()); - else - MoveTo(bg->GetMapId(), IC_WAITING_POS_ALLIANCE.GetPositionX() + frand(-5.0f, 5.0f), - IC_WAITING_POS_ALLIANCE.GetPositionY() + frand(-5.0f, 5.0f), - IC_WAITING_POS_ALLIANCE.GetPositionZ()); - } + if (role < 3) // docks + MoveTo( + bg->GetMapId(), IC_WAITING_POS_ALLIANCE.GetPositionX() + frand(-5.0f, 5.0f), + IC_WAITING_POS_ALLIANCE.GetPositionY() + frand(-5.0f, 5.0f), + IC_WAITING_POS_ALLIANCE.GetPositionZ()); // dont bother using west, there's no paths to use anyway + else if (role == 9 || (role >= 3 && role < 6)) // quarry and hanger + MoveTo(bg->GetMapId(), IC_EAST_WAITING_POS_ALLIANCE.GetPositionX() + frand(-5.0f, 5.0f), + IC_EAST_WAITING_POS_ALLIANCE.GetPositionY() + frand(-5.0f, 5.0f), + IC_EAST_WAITING_POS_ALLIANCE.GetPositionZ()); + else // everything else + MoveTo(bg->GetMapId(), IC_WAITING_POS_ALLIANCE.GetPositionX() + frand(-5.0f, 5.0f), + IC_WAITING_POS_ALLIANCE.GetPositionY() + frand(-5.0f, 5.0f), + IC_WAITING_POS_ALLIANCE.GetPositionZ()); } } @@ -3238,7 +3339,6 @@ bool BGTactics::selectObjective(bool reset) { BattlegroundIC* isleOfConquestBG = (BattlegroundIC*)bg; - uint32 currentObjective = MAX_NODE_TYPES; uint32 role = context->GetValue("bg role")->Get(); bool inVehicle = botAI->IsInVehicle(); bool controlsVehicle = botAI->IsInVehicle(true); @@ -3251,421 +3351,306 @@ bool BGTactics::selectObjective(bool reset) /* TACTICS */ if (bot->GetTeamId() == TEAM_HORDE) // HORDE { - // If all bases are captured, go to enemy boss - bool allCaptured = true; - for (uint8 i = 0; i < MAX_NODE_TYPES; ++i) + bool gateOpen = false; + if (GameObject* pGO = bg->GetBGObject(BG_IC_GO_DOODAD_PORTCULLISACTIVE02)) { - // skip refinery and keep - if (i == NODE_TYPE_REFINERY || i == NODE_TYPE_GRAVEYARD_H) - continue; - - ICNodePoint const& nodePoint = isleOfConquestBG->GetICNodePoint(i); - if (nodePoint.nodeState != NODE_STATE_CONFLICT_H && nodePoint.nodeState != NODE_STATE_CONTROLLED_H) + if (pGO->isSpawned() && pGO->getLootState() == GO_ACTIVATED) { - allCaptured = false; - break; - } - } - - if (allCaptured) // target enemy boss - { - if (Creature* allyboss = bg->GetBGCreature(BG_IC_NPC_HIGH_COMMANDER_HALFORD_WYRMBANE)) - { - if (allyboss->IsVisible()) - { - BgObjective = allyboss; - // ostringstream out; - // out << "Attackign BOSS! BG objective set to " << BgObjective->GetName(); - // bot->Say(out.str(), LANG_UNIVERSAL); - } - } - } - - // If main bases are not captured, split tasks - if (!BgObjective) - { - bool foundTask = false; - // mount defensive cannons - if (role > 10) // disabled - { - uint32 firstTower = getPlayersInArea(bot->GetTeamId(), IC_CANNON_POS_HORDE1, 10.0f); - uint32 secondTower = getPlayersInArea(bot->GetTeamId(), IC_CANNON_POS_HORDE2, 10.0f); - - if (firstTower < 2) - { - pos.Set(IC_CANNON_POS_HORDE1.GetPositionX(), IC_CANNON_POS_HORDE1.GetPositionY(), - IC_CANNON_POS_HORDE1.GetPositionZ(), bg->GetMapId()); - posMap["bg objective"] = pos; - return true; - } - if (secondTower < 2) - { - pos.Set(IC_CANNON_POS_HORDE2.GetPositionX(), IC_CANNON_POS_HORDE2.GetPositionY(), - IC_CANNON_POS_HORDE2.GetPositionZ(), bg->GetMapId()); - posMap["bg objective"] = pos; - return true; - } - } - if (role < 3) // Capture Side base or Docks - { - // Capture Refinery - ICNodePoint const& nodePoint = isleOfConquestBG->GetICNodePoint(NODE_TYPE_REFINERY); - if (nodePoint.nodeState != NODE_STATE_CONFLICT_H && - nodePoint.nodeState != NODE_STATE_CONTROLLED_H) - { - BgObjective = bg->GetBGObject(BG_IC_GO_REFINERY_BANNER); - currentObjective = BG_IC_GO_REFINERY_BANNER; - foundTask = true; - } - } - - if (!BgObjective && role < 6 && urand(0, 1)) // Capture Docks - { - // Capture Docks - ICNodePoint const& nodePoint = isleOfConquestBG->GetICNodePoint(NODE_TYPE_DOCKS); - if (nodePoint.nodeState != NODE_STATE_CONFLICT_H && - nodePoint.nodeState != NODE_STATE_CONTROLLED_H) - { - if (GameObject* pGO = bg->GetBGObject(BG_IC_GO_DOCKS_BANNER)) - { - BgObjective = pGO; - currentObjective = BG_IC_GO_DOCKS_BANNER; - foundTask = true; - - // ostringstream out; - // out << "DOCKS! BG objective set to " << BgObjective->GetName(); - // bot->Say(out.str(), LANG_UNIVERSAL); - } - } - } - // If docks/side capped, help capture workshop - if (!BgObjective && role < 3) - { - ICNodePoint const& nodePoint = isleOfConquestBG->GetICNodePoint(NODE_TYPE_WORKSHOP); - if (nodePoint.nodeState != NODE_STATE_CONFLICT_H && - nodePoint.nodeState != NODE_STATE_CONTROLLED_H) - { - if (GameObject* pGO = bg->GetBGObject(BG_IC_GO_WORKSHOP_BANNER)) - { - BgObjective = pGO; - currentObjective = BG_IC_GO_WORKSHOP_BANNER; - foundTask = true; - - // ostringstream out; - // out << "WORKSHOP! BG objective set to " << BgObjective->GetName(); - // bot->Say(out.str(), LANG_UNIVERSAL); - } - } - } - if (!BgObjective && role < 6) // Capture Hangar - { - ICNodePoint const& nodePoint = isleOfConquestBG->GetICNodePoint(NODE_TYPE_HANGAR); - if (nodePoint.nodeState != NODE_STATE_CONFLICT_H && - nodePoint.nodeState != NODE_STATE_CONTROLLED_H) - { - if (GameObject* pGO = bg->GetBGObject(BG_IC_GO_HANGAR_BANNER)) - { - BgObjective = pGO; - currentObjective = BG_IC_GO_HANGAR_BANNER; - foundTask = true; - // ostringstream out; - // out << "HANGAR! BG objective set to " << BgObjective->GetName(); - // bot->Say(out.str(), LANG_UNIVERSAL); - } - } - } - bool gateOpen = false; - if (!BgObjective || controlsVehicle) // Check gates - { - // Keep Gates open if any wall is destroyed, check it - if (GameObject* pGO = bg->GetBGObject(BG_IC_GO_DOODAD_PORTCULLISACTIVE01)) - { - if (pGO->isSpawned() && pGO->getLootState() == GO_ACTIVATED) - { - gateOpen = true; - } - else - { - if (GameObject* gate = bg->GetBGObject(BG_IC_GO_ALLIANCE_GATE_3)) - { - if (controlsVehicle) - { - // come close to gate if siege engine - if (vehicleId == NPC_SIEGE_ENGINE_H) - { - BgObjective = gate; - } - else - { - // take a siege position - pos.Set(506.782f + frand(-5, +5), -828.594f + frand(-5, +5), 24.313f, - bot->GetMapId()); - posMap["bg objective"] = pos; - - // set siege position - PositionInfo siegePos = - context->GetValue("position")->Get()["bg siege"]; - siegePos.Set(gate->GetPositionX(), gate->GetPositionY(), - gate->GetPositionZ(), bot->GetMapId()); - posMap["bg siege"] = siegePos; - return true; - } - } - else - { - pos.Set(506.782f + frand(-5, +5), -828.594f + frand(-5, +5), 24.313f, - bot->GetMapId()); - posMap["bg objective"] = pos; - return true; - // BgObjective = gate; - } - } - } - } - } - if (!BgObjective && gateOpen) // Capture Keep - { - // reset siege position + gateOpen = true; PositionInfo siegePos = context->GetValue("position")->Get()["bg siege"]; siegePos.Reset(); posMap["bg siege"] = siegePos; - - ICNodePoint const& nodePoint = isleOfConquestBG->GetICNodePoint(NODE_TYPE_GRAVEYARD_A); - if (nodePoint.nodeState != NODE_STATE_CONFLICT_H && - nodePoint.nodeState != NODE_STATE_CONTROLLED_H) + } + } + if (gateOpen && !controlsVehicle && + isleOfConquestBG->GetICNodePoint(NODE_TYPE_GRAVEYARD_A).nodeState == + NODE_STATE_CONTROLLED_H) // target enemy boss + { + if (Creature* enemyBoss = bg->GetBGCreature(BG_IC_NPC_HIGH_COMMANDER_HALFORD_WYRMBANE)) + { + if (enemyBoss->IsVisible()) { - if (GameObject* pGO = bg->GetBGObject(BG_IC_GO_ALLIANCE_BANNER)) + BgObjective = enemyBoss; + // LOG_INFO("playerbots", "bot={} attack boss", bot->GetName()); + } + } + } + + if (!BgObjective && gateOpen && !controlsVehicle) + { + if (GameObject* pGO = bg->GetBGObject(BG_IC_GO_ALLIANCE_BANNER)) // capture flag within keep + { + BgObjective = pGO; + // LOG_INFO("playerbots", "bot={} attack keep", bot->GetName()); + } + } + + if (!BgObjective && !gateOpen && controlsVehicle) // attack gates + { + // TODO: check for free vehicles + if (GameObject* gate = bg->GetBGObject(BG_IC_GO_ALLIANCE_GATE_3)) + { + if (vehicleId == NPC_SIEGE_ENGINE_H) // target gate directly if siege engine + { + BgObjective = gate; + // LOG_INFO("playerbots", "bot={} (in siege-engine) attack gate", bot->GetName()); + } + else // target gate directly at range if other vehicle + { + // take a siege position + if (sqrt(bot->GetDistance(IC_GATE_ATTACK_POS_HORDE)) < + 5.0f) // just make bot stay where it is (stops them shifting around to the random + // spots) + pos.Set(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetMapId()); + else + pos.Set(IC_GATE_ATTACK_POS_HORDE.GetPositionX() + frand(-5.0f, +5.0f), + IC_GATE_ATTACK_POS_HORDE.GetPositionY() + frand(-5.0f, +5.0f), + IC_GATE_ATTACK_POS_HORDE.GetPositionZ(), bot->GetMapId()); + posMap["bg objective"] = pos; + // set siege position + PositionInfo siegePos = context->GetValue("position")->Get()["bg siege"]; + siegePos.Set(gate->GetPositionX(), gate->GetPositionY(), gate->GetPositionZ(), + bot->GetMapId()); + posMap["bg siege"] = siegePos; + // LOG_INFO("playerbots", "bot={} (in vehicle={}) attack gate", bot->GetName(), vehicleId); + return true; + } + } + } + + // If gates arent down and not in vehicle, split tasks + if (!BgObjective && !controlsVehicle && role == 9) // Capture Side base + { + // Capture Refinery + ICNodePoint const& nodePoint = isleOfConquestBG->GetICNodePoint(NODE_TYPE_REFINERY); + if (nodePoint.nodeState != NODE_STATE_CONFLICT_H && nodePoint.nodeState != NODE_STATE_CONTROLLED_H) + { + BgObjective = bg->GetBGObject(BG_IC_GO_REFINERY_BANNER); + // LOG_INFO("playerbots", "bot={} attack refinery", bot->GetName()); + } + } + + if (!BgObjective && !controlsVehicle && role < 3) // Capture Docks + { + ICNodePoint const& nodePoint = isleOfConquestBG->GetICNodePoint(NODE_TYPE_DOCKS); + if (nodePoint.nodeState != NODE_STATE_CONFLICT_H && nodePoint.nodeState != NODE_STATE_CONTROLLED_H) + { + if (GameObject* pGO = bg->GetBGObject(BG_IC_GO_DOCKS_BANNER)) + { + BgObjective = pGO; + // LOG_INFO("playerbots", "bot={} attack docks", bot->GetName()); + } + } + } + if (!BgObjective && !controlsVehicle && role < 6) // Capture Hangar + { + ICNodePoint const& nodePoint = isleOfConquestBG->GetICNodePoint(NODE_TYPE_HANGAR); + if (nodePoint.nodeState != NODE_STATE_CONFLICT_H && nodePoint.nodeState != NODE_STATE_CONTROLLED_H) + { + if (GameObject* pGO = bg->GetBGObject(BG_IC_GO_HANGAR_BANNER)) + { + BgObjective = pGO; + // LOG_INFO("playerbots", "bot={} attack hangar", bot->GetName()); + } + } + } + if (!BgObjective && !controlsVehicle) // Capture Workshop + { + ICNodePoint const& nodePoint = isleOfConquestBG->GetICNodePoint(NODE_TYPE_WORKSHOP); + if (nodePoint.nodeState != NODE_STATE_CONFLICT_H && nodePoint.nodeState != NODE_STATE_CONTROLLED_H) + { + if (GameObject* pGO = bg->GetBGObject(BG_IC_GO_WORKSHOP_BANNER)) + { + BgObjective = pGO; + // LOG_INFO("playerbots", "bot={} attack workshop", bot->GetName()); + } + } + } + if (!BgObjective) // Guard point that's not fully capped (also gets them in place to board vehicle) + { + uint32 len = end(IC_AttackObjectives) - begin(IC_AttackObjectives); + for (uint32 i = 0; i < len; i++) + { + const auto& objective = + IC_AttackObjectives[(i + role) % + len]; // use role to determine which objective checked first + if (isleOfConquestBG->GetICNodePoint(objective.first).nodeState != NODE_STATE_CONTROLLED_H) + { + if (GameObject* pGO = bg->GetBGObject(objective.second)) { BgObjective = pGO; - currentObjective = BG_IC_GO_ALLIANCE_BANNER; - foundTask = true; - - // ostringstream out; - // out << "ALLY KEEP! BG objective set to " << BgObjective->GetName(); - // bot->Say(out.str(), LANG_UNIVERSAL); + // LOG_INFO("playerbots", "bot={} guard point while it captures", bot->GetName()); + break; } } } } + if (!BgObjective) // guard vehicles as they seige + + { + if (sqrt(bot->GetDistance(IC_GATE_ATTACK_POS_HORDE)) < + 5.0f) // just make bot stay where it is (stops them shifting around to the random spots) + pos.Set(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetMapId()); + else + pos.Set(IC_GATE_ATTACK_POS_HORDE.GetPositionX() + frand(-5.0f, +5.0f), + IC_GATE_ATTACK_POS_HORDE.GetPositionY() + frand(-5.0f, +5.0f), + IC_GATE_ATTACK_POS_HORDE.GetPositionZ(), bot->GetMapId()); + posMap["bg objective"] = pos; + // LOG_INFO("playerbots", "bot={} guard vehicles as they attack gate", bot->GetName()); + return true; + } } if (bot->GetTeamId() == TEAM_ALLIANCE) // ALLIANCE { - // If all bases are captured, go to enemy boss - bool allCaptured = true; - for (uint8 i = 0; i < MAX_NODE_TYPES; ++i) + bool gateOpen = false; + if (GameObject* pGO = bg->GetBGObject(BG_IC_GO_HORDE_KEEP_PORTCULLIS)) { - // skip quarry and keep - if (i == NODE_TYPE_QUARRY || i == NODE_TYPE_GRAVEYARD_A) - continue; - - ICNodePoint const& nodePoint = isleOfConquestBG->GetICNodePoint(i); - if (nodePoint.nodeState != NODE_STATE_CONFLICT_A && nodePoint.nodeState != NODE_STATE_CONTROLLED_A) + if (pGO->isSpawned() && pGO->getLootState() == GO_ACTIVATED) { - allCaptured = false; - break; - } - } - - if (allCaptured) // target enemy boss - { - if (Creature* hordeboss = bg->GetBGCreature(BG_IC_NPC_OVERLORD_AGMAR)) - { - if (!hordeboss->IsVisible()) - { - BgObjective = hordeboss; - // ostringstream out; - // out << "HORDE BOSS! BG objective set to " << BgObjective->GetName(); - // bot->Say(out.str(), LANG_UNIVERSAL); - } - } - } - - // If main bases are not captured, split tasks - if (!BgObjective) - { - bool foundTask = false; - // mount defensive cannons - if (role > 10) // disabled - { - uint32 firstTower = getPlayersInArea(bot->GetTeamId(), IC_CANNON_POS_ALLIANCE1, 10.0f); - uint32 secondTower = getPlayersInArea(bot->GetTeamId(), IC_CANNON_POS_ALLIANCE2, 10.0f); - - if (firstTower < 3) - { - pos.Set(IC_CANNON_POS_ALLIANCE1.GetPositionX(), IC_CANNON_POS_ALLIANCE1.GetPositionY(), - IC_CANNON_POS_ALLIANCE1.GetPositionZ(), bg->GetMapId()); - posMap["bg objective"] = pos; - return true; - } - if (secondTower < 3) - { - pos.Set(IC_CANNON_POS_ALLIANCE2.GetPositionX(), IC_CANNON_POS_ALLIANCE2.GetPositionY(), - IC_CANNON_POS_ALLIANCE2.GetPositionZ(), bg->GetMapId()); - posMap["bg objective"] = pos; - return true; - } - } - - if (role < 3) // Capture Side base or Docks - { - // Capture Quarry - ICNodePoint const& nodePoint = isleOfConquestBG->GetICNodePoint(NODE_TYPE_QUARRY); - if (nodePoint.nodeState != NODE_STATE_CONFLICT_A && - nodePoint.nodeState != NODE_STATE_CONTROLLED_A) - { - if (GameObject* pGO = bg->GetBGObject(BG_IC_GO_QUARRY_BANNER)) - { - BgObjective = pGO; - currentObjective = BG_IC_GO_QUARRY_BANNER; - foundTask = true; - - // ostringstream out; - // out << "QUARRY! BG objective set to " << BgObjective->GetName(); - // bot->Say(out.str(), LANG_UNIVERSAL); - } - } - } - // take position at cannon - /*if (!BgObjective) - { - if (GameObject* pGO = bg->interactwith(BG_IC_VEHICLE_KEEP_CANNON)) - { - if (sServerFacade->isSpawned(pGO) && pGO->GetLootState() == GO_READY) - isCapping = true; - } - }*/ - if (!BgObjective && role < 6 && urand(0, 1)) - { - // Capture Docks - ICNodePoint const& nodePoint = isleOfConquestBG->GetICNodePoint(NODE_TYPE_DOCKS); - if (nodePoint.nodeState != NODE_STATE_CONFLICT_A && - nodePoint.nodeState != NODE_STATE_CONTROLLED_A) - { - if (GameObject* pGO = bg->GetBGObject(BG_IC_GO_DOCKS_BANNER)) - { - BgObjective = pGO; - currentObjective = BG_IC_GO_DOCKS_BANNER; - foundTask = true; - - // ostringstream out; - // out << "DOCKS! BG objective set to " << BgObjective->GetName(); - // bot->Say(out.str(), LANG_UNIVERSAL); - } - } - } - // If docks/side capped, help capture workshop - if (!BgObjective && role < 3) - { - ICNodePoint const& nodePoint = isleOfConquestBG->GetICNodePoint(NODE_TYPE_WORKSHOP); - if (nodePoint.nodeState != NODE_STATE_CONFLICT_A && - nodePoint.nodeState != NODE_STATE_CONTROLLED_A) - { - if (GameObject* pGO = bg->GetBGObject(BG_IC_GO_WORKSHOP_BANNER)) - { - BgObjective = pGO; - currentObjective = BG_IC_GO_WORKSHOP_BANNER; - foundTask = true; - - // ostringstream out; - // out << "WORKSHOP! BG objective set to " << BgObjective->GetName(); - // bot->Say(out.str(), LANG_UNIVERSAL); - } - } - } - if (!BgObjective && role < 6) // Capture Hangar - { - ICNodePoint const& nodePoint = isleOfConquestBG->GetICNodePoint(NODE_TYPE_HANGAR); - if (nodePoint.nodeState != NODE_STATE_CONFLICT_A && - nodePoint.nodeState != NODE_STATE_CONTROLLED_A) - { - if (GameObject* pGO = bg->GetBGObject(BG_IC_GO_HANGAR_BANNER)) - { - BgObjective = pGO; - currentObjective = BG_IC_GO_HANGAR_BANNER; - foundTask = true; - - // ostringstream out; - // out << "HANGAR! BG objective set to " << BgObjective->GetName(); - // bot->Say(out.str(), LANG_UNIVERSAL); - } - } - } - - bool gateOpen = false; - if (!BgObjective || controlsVehicle) // Check gates - { - // Keep Gates open if any wall is destroyed, check it - if (GameObject* pGO = bg->GetBGObject(BG_IC_GO_HORDE_KEEP_PORTCULLIS)) - { - if (pGO->isSpawned() && pGO->getLootState() == GO_ACTIVATED) - { - gateOpen = true; - } - else - { - if (GameObject* gate = bg->GetBGObject(BG_IC_GO_HORDE_GATE_3)) - { - if (controlsVehicle) - { - // come close to gate if siege engine - if (vehicleId == NPC_SIEGE_ENGINE_A) - { - BgObjective = gate; - } - else - { - // take a siege position - pos.Set(1091.273f + frand(-5, +5), -763.619f + frand(-5, +5), 42.352f, - bot->GetMapId()); - posMap["bg objective"] = pos; - - // set siege position - PositionInfo siegePos = - context->GetValue("position")->Get()["bg siege"]; - siegePos.Set(gate->GetPositionX(), gate->GetPositionY(), - gate->GetPositionZ(), bot->GetMapId()); - posMap["bg siege"] = siegePos; - return true; - } - } - else - { - pos.Set(1091.273f + frand(-5, +5), -763.619f + frand(-5, +5), 42.352f, - bot->GetMapId()); - posMap["bg objective"] = pos; - return true; - // take a siege position - // BgObjective = gate; - } - } - } - } - } - if (!BgObjective && gateOpen) // Capture Keep - { - // reset siege position + gateOpen = true; PositionInfo siegePos = context->GetValue("position")->Get()["bg siege"]; siegePos.Reset(); posMap["bg siege"] = siegePos; + } + } - ICNodePoint const& nodePoint = isleOfConquestBG->GetICNodePoint(NODE_TYPE_GRAVEYARD_H); - if (nodePoint.nodeState != NODE_STATE_CONFLICT_A && - nodePoint.nodeState != NODE_STATE_CONTROLLED_A) + if (gateOpen && !controlsVehicle && + isleOfConquestBG->GetICNodePoint(NODE_TYPE_GRAVEYARD_H).nodeState == + NODE_STATE_CONTROLLED_A) // target enemy boss + { + if (Creature* enemyBoss = bg->GetBGCreature(BG_IC_NPC_OVERLORD_AGMAR)) + { + if (enemyBoss->IsVisible()) { - if (GameObject* pGO = bg->GetBGObject(BG_IC_GO_HORDE_BANNER)) + BgObjective = enemyBoss; + // LOG_INFO("playerbots", "bot={} attack boss", bot->GetName()); + } + } + } + + if (!BgObjective && gateOpen && !controlsVehicle) + { + if (GameObject* pGO = bg->GetBGObject(BG_IC_GO_HORDE_BANNER)) // capture flag within keep + { + BgObjective = pGO; + // LOG_INFO("playerbots", "bot={} attack keep", bot->GetName()); + } + } + + if (!BgObjective && !gateOpen && controlsVehicle) // attack gates + { + // TODO: check for free vehicles + if (GameObject* gate = bg->GetBGObject(BG_IC_GO_HORDE_GATE_1)) + { + if (vehicleId == NPC_SIEGE_ENGINE_A) // target gate directly if siege engine + { + BgObjective = gate; + // LOG_INFO("playerbots", "bot={} (in siege-engine) attack gate", bot->GetName()); + } + else // target gate directly at range if other vehicle + { + // take a siege position + if (sqrt(bot->GetDistance(IC_GATE_ATTACK_POS_ALLIANCE)) < + 5.0f) // just make bot stay where it is (stops them shifting around to the random + // spots) + pos.Set(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetMapId()); + else + pos.Set(IC_GATE_ATTACK_POS_ALLIANCE.GetPositionX() + frand(-5.0f, +5.0f), + IC_GATE_ATTACK_POS_ALLIANCE.GetPositionY() + frand(-5.0f, +5.0f), + IC_GATE_ATTACK_POS_ALLIANCE.GetPositionZ(), bot->GetMapId()); + posMap["bg objective"] = pos; + // set siege position + PositionInfo siegePos = context->GetValue("position")->Get()["bg siege"]; + siegePos.Set(gate->GetPositionX(), gate->GetPositionY(), gate->GetPositionZ(), + bot->GetMapId()); + posMap["bg siege"] = siegePos; + // LOG_INFO("playerbots", "bot={} (in vehicle={}) attack gate", bot->GetName(), vehicleId); + return true; + } + } + } + + // If gates arent down and not in vehicle, split tasks + if (!BgObjective && !controlsVehicle && role == 9) // Capture Side base + { + // Capture Refinery + ICNodePoint const& nodePoint = isleOfConquestBG->GetICNodePoint(NODE_TYPE_QUARRY); + if (nodePoint.nodeState != NODE_STATE_CONFLICT_A && nodePoint.nodeState != NODE_STATE_CONTROLLED_A) + { + BgObjective = bg->GetBGObject(BG_IC_GO_QUARRY_BANNER); + // LOG_INFO("playerbots", "bot={} attack quarry", bot->GetName()); + } + } + + if (!BgObjective && !controlsVehicle && role < 3) // Capture Docks + { + ICNodePoint const& nodePoint = isleOfConquestBG->GetICNodePoint(NODE_TYPE_DOCKS); + if (nodePoint.nodeState != NODE_STATE_CONFLICT_A && nodePoint.nodeState != NODE_STATE_CONTROLLED_A) + { + if (GameObject* pGO = bg->GetBGObject(BG_IC_GO_DOCKS_BANNER)) + { + BgObjective = pGO; + // LOG_INFO("playerbots", "bot={} attack docks", bot->GetName()); + } + } + } + if (!BgObjective && !controlsVehicle && role < 6) // Capture Hangar + { + ICNodePoint const& nodePoint = isleOfConquestBG->GetICNodePoint(NODE_TYPE_HANGAR); + if (nodePoint.nodeState != NODE_STATE_CONFLICT_A && nodePoint.nodeState != NODE_STATE_CONTROLLED_A) + { + if (GameObject* pGO = bg->GetBGObject(BG_IC_GO_HANGAR_BANNER)) + { + BgObjective = pGO; + // LOG_INFO("playerbots", "bot={} attack hangar", bot->GetName()); + } + } + } + if (!BgObjective && !controlsVehicle) // Capture Workshop + { + ICNodePoint const& nodePoint = isleOfConquestBG->GetICNodePoint(NODE_TYPE_WORKSHOP); + if (nodePoint.nodeState != NODE_STATE_CONFLICT_A && nodePoint.nodeState != NODE_STATE_CONTROLLED_A) + { + if (GameObject* pGO = bg->GetBGObject(BG_IC_GO_WORKSHOP_BANNER)) + { + BgObjective = pGO; + // LOG_INFO("playerbots", "bot={} attack workshop", bot->GetName()); + } + } + } + if (!BgObjective) // Guard point that's not fully capped (also gets them in place to board vehicle) + { + uint32 len = end(IC_AttackObjectives) - begin(IC_AttackObjectives); + for (uint32 i = 0; i < len; i++) + { + const auto& objective = + IC_AttackObjectives[(i + role) % + len]; // use role to determine which objective checked first + if (isleOfConquestBG->GetICNodePoint(objective.first).nodeState != NODE_STATE_CONTROLLED_H) + { + if (GameObject* pGO = bg->GetBGObject(objective.second)) { BgObjective = pGO; - currentObjective = BG_IC_GO_HORDE_BANNER; - foundTask = true; - - // ostringstream out; - // out << "HORDE KEEP! BG objective set to " << BgObjective->GetName(); - // bot->Say(out.str(), LANG_UNIVERSAL); + // LOG_INFO("playerbots", "bot={} guard point while it captures", bot->GetName()); + break; } } } } + if (!BgObjective) // guard vehicles as they seige + { + if (sqrt(bot->GetDistance(IC_GATE_ATTACK_POS_ALLIANCE)) < + 5.0f) // just make bot stay where it is (stops them shifting around to the random spots) + pos.Set(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetMapId()); + else + pos.Set(IC_GATE_ATTACK_POS_ALLIANCE.GetPositionX() + frand(-5.0f, +5.0f), + IC_GATE_ATTACK_POS_ALLIANCE.GetPositionY() + frand(-5.0f, +5.0f), + IC_GATE_ATTACK_POS_ALLIANCE.GetPositionZ(), bot->GetMapId()); + posMap["bg objective"] = pos; + // LOG_INFO("playerbots", "bot={} guard vehicles as they attack gate", bot->GetName()); + return true; + } } if (BgObjective) @@ -3784,7 +3769,10 @@ bool BGTactics::selectObjectiveWp(std::vector const& vPaths) // distance from bot - makes distance from bot more signifcant than distance from destination) if (bgType == BATTLEGROUND_IC) - botDistanceLimit = 80.0f; + { + // botDistanceLimit = 80.0f; + botDistanceScoreMultiply = 8.0f; + } else if (bgType == BATTLEGROUND_AB || bgType == BATTLEGROUND_EY) { botDistanceScoreSubtract = 2.0f; @@ -3879,7 +3867,9 @@ bool BGTactics::resetObjective() // 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 : BATTLEGROUND_EY == bg->GetBgTypeID() ? 31 : 5; + uint32 rollChangeOdds = BATTLEGROUND_AV == bg->GetBgTypeID() ? 63 + : BATTLEGROUND_EY == bg->GetBgTypeID() || BATTLEGROUND_IC == 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))) @@ -3900,10 +3890,9 @@ bool BGTactics::moveToObjectiveWp(BattleBotPath* const& currentPath, uint32 curr uint32 const lastPointInPath = reverse ? 0 : ((*currentPath).size() - 1); - if ((currentPoint == lastPointInPath) || - (bot->IsInCombat() && !(bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) || bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG) || - bot->HasAura(BG_EY_NETHERSTORM_FLAG_SPELL))) || - !bot->IsAlive()) + // NOTE: can't use IsInCombat() when in vehicle as player is stuck in combat forever while in vehicle (ac bug?) + bool inCombat = bot->GetVehicle() ? (bool)AI_VALUE(Unit*, "enemy player target") : bot->IsInCombat(); + if (currentPoint == lastPointInPath || (inCombat && !PlayerHasFlag::IsCapturingFlag(bot)) || !bot->IsAlive()) { // Path is over. // std::ostringstream out; diff --git a/src/strategy/actions/ChooseTargetActions.cpp b/src/strategy/actions/ChooseTargetActions.cpp index 54bf0b1c..dddb3627 100644 --- a/src/strategy/actions/ChooseTargetActions.cpp +++ b/src/strategy/actions/ChooseTargetActions.cpp @@ -11,11 +11,11 @@ #include "Playerbots.h" #include "PossibleRpgTargetsValue.h" #include "ServerFacade.h" +#include "PvpTriggers.h" bool AttackEnemyPlayerAction::isUseful() { - // if carry flag, do not start fight - if (bot->HasAura(23333) || bot->HasAura(23335) || bot->HasAura(34976)) + if (PlayerHasFlag::IsCapturingFlag(bot)) return false; return !sPlayerbotAIConfig->IsPvpProhibited(bot->GetZoneId(), bot->GetAreaId()); @@ -25,7 +25,7 @@ bool AttackEnemyFlagCarrierAction::isUseful() { Unit* target = context->GetValue("enemy flag carrier")->Get(); return target && sServerFacade->IsDistanceLessOrEqualThan(sServerFacade->GetDistance2d(bot, target), 75.0f) && - (bot->HasAura(23333) || bot->HasAura(23335) || bot->HasAura(34976)); + PlayerHasFlag::IsCapturingFlag(bot); } bool AttackAnythingAction::isUseful() @@ -124,8 +124,7 @@ bool AttackAnythingAction::isPossible() { return AttackAction::isPossible() && G bool DpsAssistAction::isUseful() { - // if carry flag, do not start fight - if (bot->HasAura(23333) || bot->HasAura(23335) || bot->HasAura(34976)) + if (PlayerHasFlag::IsCapturingFlag(bot)) return false; return true; diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index 52310f76..98018f6d 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -177,6 +177,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, { return false; } + // if (bot->Unit::IsFalling()) { // bot->Say("I'm falling!, flag:" + std::to_string(bot->m_movementInfo.GetMovementFlags()), LANG_UNIVERSAL); // return false; @@ -190,7 +191,28 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, bool generatePath = !bot->IsFlying() && !bot->isSwimming(); bool disableMoveSplinePath = sPlayerbotAIConfig->disableMoveSplinePath >= 2 || (sPlayerbotAIConfig->disableMoveSplinePath == 1 && bot->InBattleground()); - if (exact_waypoint || disableMoveSplinePath || !generatePath) + if (Vehicle* vehicle = bot->GetVehicle()) + { + VehicleSeatEntry const* seat = vehicle->GetSeatForPassenger(bot); + Unit* vehicleBase = vehicle->GetBase(); + if (!vehicleBase || !seat || !seat->CanControl()) // is passenger and cant move anyway + return false; + + float distance = vehicleBase->GetExactDist(x, y, z); // use vehicle distance, not bot + if (distance > sPlayerbotAIConfig->contactDistance) + { + MotionMaster& mm = *vehicleBase->GetMotionMaster(); // need to move vehicle, not bot + mm.Clear(); + mm.MovePoint(mapId, x, y, z, generatePath); + float delay = 1000.0f * (distance / vehicleBase->GetSpeed(MOVE_RUN)) - sPlayerbotAIConfig->reactDelay; + delay = std::max(.0f, delay); + delay = std::min((float)sPlayerbotAIConfig->maxWaitForMove, delay); + AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation(), delay); + // TODO: is botAI->SetNextCheckDelay() meant to go here or is setting "last movement" value enough? (same question goes for below) + return true; + } + } + else if (exact_waypoint || disableMoveSplinePath || !generatePath) { float distance = bot->GetExactDist(x, y, z); if (distance > sPlayerbotAIConfig->contactDistance) diff --git a/src/strategy/actions/ReleaseSpiritAction.cpp b/src/strategy/actions/ReleaseSpiritAction.cpp index 64521c4e..0d1ad8b6 100644 --- a/src/strategy/actions/ReleaseSpiritAction.cpp +++ b/src/strategy/actions/ReleaseSpiritAction.cpp @@ -119,7 +119,11 @@ bool AutoReleaseSpiritAction::Execute(Event event) } if (bot->GetDistance(unit) >= INTERACTION_DISTANCE) { - bot->GetMotionMaster()->MoveChase(unit); + // bot needs to actually click spirit-healer in BG to get res timer going + // and in IOC it's not within clicking range when they res in own base + MotionMaster& mm = *bot->GetMotionMaster(); + mm.Clear(); + mm.MovePoint(bot->GetMapId(), unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ(), true); } else { diff --git a/src/strategy/actions/VehicleActions.cpp b/src/strategy/actions/VehicleActions.cpp index b036a036..4683ba7a 100644 --- a/src/strategy/actions/VehicleActions.cpp +++ b/src/strategy/actions/VehicleActions.cpp @@ -5,11 +5,16 @@ #include "VehicleActions.h" +#include "BattlegroundIC.h" #include "ItemVisitors.h" #include "Playerbots.h" #include "ServerFacade.h" #include "Vehicle.h" +// TODO methods to enter/exit vehicle should be added to BGTactics or MovementAction (so that we can better control +// whether bot is in vehicle, eg: get out of vehicle to cap flag, if we're down to final boss, etc), +// right now they will enter vehicle based only what's available here, then they're stuck in vehicle until they die +// (LeaveVehicleAction doesnt do much seeing as they, or another bot, will get in immediately after exit) bool EnterVehicleAction::Execute(Event event) { // do not switch vehicles yet @@ -21,21 +26,30 @@ bool EnterVehicleAction::Execute(Event event) { Unit* vehicleBase = botAI->GetUnit(*i); if (!vehicleBase) - return false; + continue; + + // dont let them get in the cannons as they'll stay forever and do nothing useful + // dont let them in catapult they cant use them at all + if (NPC_KEEP_CANNON == vehicleBase->GetEntry() || NPC_CATAPULT == vehicleBase->GetEntry()) + continue; if (!vehicleBase->IsFriendlyTo(bot)) - return false; + continue; if (!vehicleBase->GetVehicleKit()->GetAvailableSeatCount()) - return false; + continue; - if (fabs(bot->GetPositionZ() - vehicleBase->GetPositionZ()) < 20.0f) + // this will avoid adding passengers (which dont really do much for the IOC vehicles which is the only place + // this code is used) + if (vehicleBase->GetVehicleKit()->IsVehicleInUse()) + continue; - // if (sServerFacade->GetDistance2d(bot, vehicle) > 100.0f) - // continue; + float dist = sServerFacade->GetDistance2d(bot, vehicleBase); + if (dist > 40.0f) + continue; - if (sServerFacade->GetDistance2d(bot, vehicleBase) > 10.0f) - return MoveTo(vehicleBase, INTERACTION_DISTANCE); + if (dist > INTERACTION_DISTANCE) + return MoveTo(vehicleBase); bot->EnterVehicle(vehicleBase); diff --git a/src/strategy/generic/BattlegroundStrategy.cpp b/src/strategy/generic/BattlegroundStrategy.cpp index 2a74b2f0..5a99cedd 100644 --- a/src/strategy/generic/BattlegroundStrategy.cpp +++ b/src/strategy/generic/BattlegroundStrategy.cpp @@ -99,8 +99,9 @@ void IsleStrategy::InitTriggers(std::vector& triggers) new TriggerNode("in vehicle", NextAction::array(0, new NextAction("incendiary rocket", 70.0f), nullptr))); triggers.push_back( new TriggerNode("in vehicle", NextAction::array(0, new NextAction("rocket blast", 70.0f), nullptr))); - triggers.push_back( - new TriggerNode("in vehicle", NextAction::array(0, new NextAction("blade salvo", 71.0f), nullptr))); + // this is bugged: it doesn't work, and stops glaive throw working (which is needed to take down gate) + // triggers.push_back( + // new TriggerNode("in vehicle", NextAction::array(0, new NextAction("blade salvo", 71.0f), nullptr))); triggers.push_back( new TriggerNode("in vehicle", NextAction::array(0, new NextAction("glaive throw", 70.0f), nullptr))); } diff --git a/src/strategy/triggers/PvpTriggers.cpp b/src/strategy/triggers/PvpTriggers.cpp index 30d47404..b4a3edfb 100644 --- a/src/strategy/triggers/PvpTriggers.cpp +++ b/src/strategy/triggers/PvpTriggers.cpp @@ -117,22 +117,53 @@ bool PlayerIsInBattlegroundWithoutFlag::IsActive() } bool PlayerHasFlag::IsActive() +{ + return IsCapturingFlag(bot); +} + +bool PlayerHasFlag::IsCapturingFlag(Player* bot) { if (bot->InBattleground()) { if (bot->GetBattlegroundTypeId() == BATTLEGROUND_WS) { - BattlegroundWS* bg = (BattlegroundWS*)botAI->GetBot()->GetBattleground(); - if (bot->GetGUID() == bg->GetFlagPickerGUID(TEAM_ALLIANCE) || - bot->GetGUID() == bg->GetFlagPickerGUID(TEAM_HORDE)) + BattlegroundWS* bg = (BattlegroundWS*)bot->GetBattleground(); + // bot is horde and has ally flag + if (bot->GetGUID() == bg->GetFlagPickerGUID(TEAM_ALLIANCE)) { - return true; + if (bg->GetFlagPickerGUID(TEAM_HORDE)) // enemy has flag too + { + if (GameObject* go = bg->GetBGObject(BG_WS_OBJECT_H_FLAG)) + { + // only indicate capturing if signicant distance from own flag + // (otherwise allow bot to defend itself) + return bot->GetDistance(go) > 36.0f; + } + } + return true; // enemy doesnt have flag so we can cap immediately } + // bot is ally and has horde flag + if (bot->GetGUID() == bg->GetFlagPickerGUID(TEAM_HORDE)) + { + if (bg->GetFlagPickerGUID(TEAM_ALLIANCE)) // enemy has flag too + { + if (GameObject* go = bg->GetBGObject(BG_WS_OBJECT_A_FLAG)) + { + // only indicate capturing if signicant distance from own flag + // (otherwise allow bot to defend itself) + return bot->GetDistance(go) > 36.0f; + } + } + return true; // enemy doesnt have flag so we can cap immediately + } + return false; // bot doesn't have flag } if (bot->GetBattlegroundTypeId() == BATTLEGROUND_EY) { - BattlegroundEY* bg = (BattlegroundEY*)botAI->GetBot()->GetBattleground(); + // TODO we should probably add similiar logic as WSG to allow combat + // when bot has flag but no bases are available to take it to + BattlegroundEY* bg = (BattlegroundEY*)bot->GetBattleground(); return bot->GetGUID() == bg->GetFlagPickerGUID(); } diff --git a/src/strategy/triggers/PvpTriggers.h b/src/strategy/triggers/PvpTriggers.h index 7633c654..34019a67 100644 --- a/src/strategy/triggers/PvpTriggers.h +++ b/src/strategy/triggers/PvpTriggers.h @@ -26,12 +26,15 @@ public: bool IsActive() override; }; +// NOTE this trigger is only active when bot is actively returning flag +// (not when hiding in base because enemy has flag too) class PlayerHasFlag : public Trigger { public: PlayerHasFlag(PlayerbotAI* botAI) : Trigger(botAI, "player has flag") {} bool IsActive() override; + static bool IsCapturingFlag(Player* bot); }; class EnemyFlagCarrierNear : public Trigger diff --git a/src/strategy/values/EnemyPlayerValue.cpp b/src/strategy/values/EnemyPlayerValue.cpp index ba346a47..5302bde2 100644 --- a/src/strategy/values/EnemyPlayerValue.cpp +++ b/src/strategy/values/EnemyPlayerValue.cpp @@ -7,6 +7,7 @@ #include "Playerbots.h" #include "ServerFacade.h" +#include "Vehicle.h" bool NearestEnemyPlayersValue::AcceptUnit(Unit* unit) { @@ -25,7 +26,19 @@ bool NearestEnemyPlayersValue::AcceptUnit(Unit* unit) Unit* EnemyPlayerValue::Calculate() { - bool inCannon = botAI->IsInVehicle(false, true); + bool controllingCannon = false; + bool controllingVehicle = false; + if (Vehicle* vehicle = bot->GetVehicle()) + { + VehicleSeatEntry const* seat = vehicle->GetSeatForPassenger(bot); + if (!seat || !seat->CanControl()) // not in control of vehicle so cant attack anyone + return nullptr; + VehicleEntry const* vi = vehicle->GetVehicleInfo(); + if (vi && vi->m_flags & VEHICLE_FLAG_FIXED_POSITION) + controllingCannon = true; + else + controllingVehicle = true; + } // 1. Check units we are currently in combat with. std::vector targets; @@ -95,12 +108,15 @@ Unit* EnemyPlayerValue::Calculate() } // Aggro weak enemies from further away. - uint32 const aggroDistance = (inCannon || bot->GetHealth() > pTarget->GetHealth()) ? maxAggroDistance : 20.0f; + // If controlling mobile vehicle only agro close enemies (otherwise will never reach objective) + uint32 const aggroDistance = controllingVehicle ? 5.0f + : (controllingCannon || bot->GetHealth() > pTarget->GetHealth()) ? maxAggroDistance + : 20.0f; if (!bot->IsWithinDist(pTarget, aggroDistance)) continue; if (bot->IsWithinLOSInMap(pTarget) && - (inCannon || (fabs(bot->GetPositionZ() - pTarget->GetPositionZ()) < 30.0f))) + (controllingCannon || (fabs(bot->GetPositionZ() - pTarget->GetPositionZ()) < 30.0f))) return pTarget; } diff --git a/src/strategy/values/NearestNpcsValue.cpp b/src/strategy/values/NearestNpcsValue.cpp index c0c21509..cbbc8894 100644 --- a/src/strategy/values/NearestNpcsValue.cpp +++ b/src/strategy/values/NearestNpcsValue.cpp @@ -32,7 +32,7 @@ bool NearestVehiclesValue::AcceptUnit(Unit* unit) if (!unit || !unit->IsVehicle() || !unit->IsAlive()) return false; - Vehicle* veh = unit->GetVehicle(); + Vehicle* veh = unit->GetVehicleKit(); if (!veh || !veh->GetAvailableSeatCount()) return false;