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