From 500de1de10f4b3fd90b01e3231ba3da5dea6cb64 Mon Sep 17 00:00:00 2001 From: Fuzz Date: Wed, 10 Jul 2024 22:13:12 +1000 Subject: [PATCH 1/7] optionally disable bots using MotionMaster::MoveSplitPath() for BG/Arena or everywhere (so that stuns stun/snare/root/etc can work against bots) --- conf/playerbots.conf.dist | 7 +++++++ src/PlayerbotAIConfig.cpp | 1 + src/PlayerbotAIConfig.h | 4 ++-- src/strategy/actions/MovementActions.cpp | 4 +++- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 3a3c2736..016e322a 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -248,6 +248,13 @@ AiPlayerbot.GlobalCooldown = 500 # Max wait time when moving AiPlayerbot.MaxWaitForMove = 5000 +# Disables use of MoveSplinePath for bot movement, will result in more erratic bot movement but means stun/snare/root/etc +# will work on bots (they wont reliably work when MoveSplinePath is enabled, though slowing effects still work ok) +# Default: 0 - MoveSplinePath enabled +# 1 - MoveSplinePath disabled in BG/Arena only +# 2 - MoveSplinePath disabled everywhere +AiPlayerbot.DisableMoveSplinePath = 0 + # Max search time for movement (higher for better movement on slopes) # default: 3 AiPlayerbot.MaxMovementSearchTime = 3 diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index ed840e9c..f0921224 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -53,6 +53,7 @@ bool PlayerbotAIConfig::Initialize() globalCoolDown = sConfigMgr->GetOption("AiPlayerbot.GlobalCooldown", 1500); maxWaitForMove = sConfigMgr->GetOption("AiPlayerbot.MaxWaitForMove", 5000); + disableMoveSplinePath = sConfigMgr->GetOption("AiPlayerbot.DisableMoveSplinePath", 0); maxMovementSearchTime = sConfigMgr->GetOption("AiPlayerbot.MaxMovementSearchTime", 3); expireActionTime = sConfigMgr->GetOption("AiPlayerbot.ExpireActionTime", 5000); dispelAuraDuration = sConfigMgr->GetOption("AiPlayerbot.DispelAuraDuration", 7000); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 40a66034..477f9c51 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -54,8 +54,8 @@ class PlayerbotAIConfig bool enabled; bool allowGuildBots, allowPlayerBots; - uint32 globalCoolDown, reactDelay, maxWaitForMove, maxMovementSearchTime, expireActionTime, - dispelAuraDuration, passiveDelay, repeatDelay, + uint32 globalCoolDown, reactDelay, maxWaitForMove, disableMoveSplinePath, maxMovementSearchTime, + expireActionTime, dispelAuraDuration, passiveDelay, repeatDelay, errorDelay, rpgDelay, sitDelay, returnDelay, lootDelay; float sightDistance, spellDistance, reactDistance, grindDistance, lootDistance, shootDistance, fleeDistance, tooCloseDistance, meleeDistance, followDistance, whisperDistance, contactDistance, diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index f86d539e..6a67e01b 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -163,7 +163,9 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // } bool generatePath = !bot->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) && !bot->IsFlying() && !bot->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING) && !bot->IsInWater(); - if (!generatePath) { + bool disableMoveSplinePath = sPlayerbotAIConfig->disableMoveSplinePath >= 2 || + (sPlayerbotAIConfig->disableMoveSplinePath == 1 && bot->InBattleground()); + if (disableMoveSplinePath || !generatePath) { float distance = bot->GetExactDist(x, y, z); if (distance > sPlayerbotAIConfig->contactDistance) { From 06379b1ad028f432c5585acf3987252fa7b900d1 Mon Sep 17 00:00:00 2001 From: Fuzz Date: Wed, 10 Jul 2024 22:25:59 +1000 Subject: [PATCH 2/7] improved path selection for non-WSG BG's (improvements to pathing in AB and AV especially) --- src/strategy/actions/BattleGroundTactics.cpp | 125 ++++++++----------- 1 file changed, 55 insertions(+), 70 deletions(-) diff --git a/src/strategy/actions/BattleGroundTactics.cpp b/src/strategy/actions/BattleGroundTactics.cpp index 226cce6f..919c5f71 100644 --- a/src/strategy/actions/BattleGroundTactics.cpp +++ b/src/strategy/actions/BattleGroundTactics.cpp @@ -4162,99 +4162,84 @@ bool BGTactics::selectObjectiveWp(std::vector const& vPaths) if (bgType == BATTLEGROUND_WS /* && (bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) || bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG))*/) return wsgPaths(); - BattleBotPath* pClosestPath = nullptr; - uint32 closestPoint = 0; - float closestDistanceToTarget = FLT_MAX; - bool reverse = false; - float maxDistanceToPoint = 50.0f; - if (bgType == BATTLEGROUND_IC) - maxDistanceToPoint = 80.0f; + float chosenPathScore = FLT_MAX;//lower score is better + BattleBotPath* chosenPath = nullptr; + uint32 chosenPathPoint = 0; + bool chosenPathReverse = false; - for (auto const& pPath : vPaths) + float botDistanceLimit = 50.0f; // limit for how far path can be from bot + float botDistanceScoreSubtract = 8.0f; // path score modifier - lower = less likely to chose a further path (it's basically the distance from bot that's ignored) + float botDistanceScoreMultiply = 3.0f; // path score modifier - higher = less likely to chose a further path (it's basically a multiplier on distance from bot - makes distance from bot more signifcant than distance from destination) + + if (bgType == BATTLEGROUND_IC) + botDistanceLimit = 80.0f; + else if (bgType == BATTLEGROUND_AB) + { + botDistanceScoreSubtract = 2.0f; + botDistanceScoreMultiply = 4.0f; + } + + for (auto const& path : vPaths) { // skip mine paths of own faction - if (bot->GetTeamId() == TEAM_ALLIANCE && std::find(vPaths_AllyMine.begin(), vPaths_AllyMine.end(), pPath) != vPaths_AllyMine.end()) + if (bot->GetTeamId() == TEAM_ALLIANCE && std::find(vPaths_AllyMine.begin(), vPaths_AllyMine.end(), path) != vPaths_AllyMine.end()) continue; - if (bot->GetTeamId() == TEAM_HORDE && std::find(vPaths_HordeMine.begin(), vPaths_HordeMine.end(), pPath) != vPaths_HordeMine.end()) + if (bot->GetTeamId() == TEAM_HORDE && std::find(vPaths_HordeMine.begin(), vPaths_HordeMine.end(), path) != vPaths_HordeMine.end()) continue; - BattleBotWaypoint& lastPoint = ((*pPath)[pPath->size() - 1]); - float const distanceFromPathEndToTarget = sqrt(Position(pos.x, pos.y, pos.z, 0.f).GetExactDist(lastPoint.x, lastPoint.y, lastPoint.z)); - if (closestDistanceToTarget > distanceFromPathEndToTarget) + BattleBotWaypoint& startPoint = ((*path)[0]); + float const startPointDistToDestination = sqrt(Position(pos.x, pos.y, pos.z, 0.f).GetExactDist(startPoint.x, startPoint.y, startPoint.z)); + BattleBotWaypoint& endPoint = ((*path)[path->size() - 1]); + float const endPointDistToDestination = sqrt(Position(pos.x, pos.y, pos.z, 0.f).GetExactDist(endPoint.x, endPoint.y, endPoint.z)); + + bool reverse = startPointDistToDestination < endPointDistToDestination; + + // dont travel reverse if it's a reverse paths + if (reverse && std::find(vPaths_NoReverseAllowed.begin(), vPaths_NoReverseAllowed.end(), path) != vPaths_NoReverseAllowed.end()) + continue; + + int closestPointIndex = -1; + float closestPointDistToBot = FLT_MAX; + for (uint32 i = 0; i < path->size(); i++) { - float closestDistanceFromMeToPoint = FLT_MAX; - - for (uint32 i = 0; i < pPath->size(); i++) + BattleBotWaypoint& waypoint = ((*path)[i]); + float const distToBot = sqrt(bot->GetDistance(waypoint.x, waypoint.y, waypoint.z)); + if (closestPointDistToBot > distToBot) { - BattleBotWaypoint& waypoint = ((*pPath)[i]); - float const distanceFromMeToPoint = sqrt(bot->GetDistance(waypoint.x, waypoint.y, waypoint.z)); - if (distanceFromMeToPoint < maxDistanceToPoint && closestDistanceFromMeToPoint > distanceFromMeToPoint) - { - reverse = false; - pClosestPath = pPath; - closestPoint = i; - closestDistanceToTarget = distanceFromPathEndToTarget; - closestDistanceFromMeToPoint = distanceFromMeToPoint; - } + closestPointDistToBot = distToBot; + closestPointIndex = i; } } - // skip no reverse paths - if (std::find(vPaths_NoReverseAllowed.begin(), vPaths_NoReverseAllowed.end(), pPath) != vPaths_NoReverseAllowed.end()) + // don't pick path where bot is already closest to the paths closest point to target (it means path cant lead it anywhere) + // don't pick path where closest point is too far away + if (closestPointIndex == (reverse ? 0 : path->size() - 1) || closestPointDistToBot > botDistanceLimit) continue; - // skip mine paths of own faction - if (bot->GetTeamId() == TEAM_ALLIANCE && std::find(vPaths_AllyMine.begin(), vPaths_AllyMine.end(), pPath) != vPaths_AllyMine.end()) - continue; - if (bot->GetTeamId() == TEAM_HORDE && std::find(vPaths_HordeMine.begin(), vPaths_HordeMine.end(), pPath) != vPaths_HordeMine.end()) - continue; + // 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 + float distToDestination = reverse ? startPointDistToDestination : endPointDistToDestination; + float pathScore = (closestPointDistToBot < botDistanceScoreSubtract ? 0.0f : ((closestPointDistToBot - botDistanceScoreSubtract) * botDistanceScoreMultiply)) + distToDestination; - { - BattleBotWaypoint& firstPoint = ((*pPath)[0]); - float const distanceFromPathBeginToTarget = sqrt(Position(pos.x, pos.y, pos.z, 0).GetExactDist(firstPoint.x, firstPoint.y, firstPoint.z)); - if (closestDistanceToTarget > distanceFromPathBeginToTarget) - { - float closestDistanceFromMeToPoint = FLT_MAX; + //LOG_INFO("playerbots", "bot={}\t{:6.1f}\t{:4.1f}\t{:4.1f}\t{}", bot->GetName(), pathScore, closestPointDistToBot, distToDestination, vPaths_AB_name[pathNum]); - for (uint32 i = 0; i < pPath->size(); i++) - { - BattleBotWaypoint& waypoint = ((*pPath)[i]); - float const distanceFromMeToPoint = sqrt(bot->GetDistance(waypoint.x, waypoint.y, waypoint.z)); - if (distanceFromMeToPoint < maxDistanceToPoint && closestDistanceFromMeToPoint > distanceFromMeToPoint) - { - reverse = true; - pClosestPath = pPath; - closestPoint = i; - closestDistanceToTarget = distanceFromPathBeginToTarget; - closestDistanceFromMeToPoint = distanceFromMeToPoint; - } - } - } + if (chosenPathScore > pathScore) { + chosenPathScore = pathScore; + chosenPath = path; + chosenPathPoint = closestPointIndex; + chosenPathReverse = reverse; } } - if (!pClosestPath) + if (!chosenPath) return false; - // Prevent picking last point of path. - // It means we are already there. - if (reverse) - { - if (closestPoint == 0) - return false; - } - else - { - if (closestPoint == pClosestPath->size() - 1) - return false; - } + //LOG_INFO("playerbots", "bot={} {}", bot->GetName(), vPaths_AB_name[chosenPathNum]); - BattleBotPath* currentPath = pClosestPath; - uint32 currentPoint = reverse ? closestPoint + 1 : closestPoint - 1; - - return moveToObjectiveWp(currentPath, currentPoint, reverse); + return moveToObjectiveWp(chosenPath, chosenPathPoint, chosenPathReverse); return false; } From 3316490f5dc6db63ac9a08186dab8bad02e5db05 Mon Sep 17 00:00:00 2001 From: Fuzz Date: Wed, 10 Jul 2024 22:36:22 +1000 Subject: [PATCH 3/7] many many fixes for AV - fixed main path from horde cave starting too far away (flying bots, or bots going nowhere), fixed wrong objects targetted for objective-nodes (bots hanging around already captured points), wrong creatures targetted for boss and captain (bot running toward and away from captain, bot capturing every point then running home), wrong flag state checked for certain points (bot waiting forever in burning tower), fixed bots not moving close enough to flags in towers (bots sitting in towers forever), and more --- src/strategy/actions/BattleGroundTactics.cpp | 168 +++++++++++-------- 1 file changed, 98 insertions(+), 70 deletions(-) diff --git a/src/strategy/actions/BattleGroundTactics.cpp b/src/strategy/actions/BattleGroundTactics.cpp index 919c5f71..80eff17b 100644 --- a/src/strategy/actions/BattleGroundTactics.cpp +++ b/src/strategy/actions/BattleGroundTactics.cpp @@ -40,12 +40,16 @@ Position const WS_FLAG_HIDE_ALLIANCE_2 = { 1540.286f, 1476.026f, 352.692f, 2.91f Position const WS_FLAG_HIDE_ALLIANCE_3 = { 1495.807f, 1466.774f, 352.350f, 1.50f }; std::vector const WS_FLAG_HIDE_HORDE = { WS_FLAG_HIDE_HORDE_1 , WS_FLAG_HIDE_HORDE_2, WS_FLAG_HIDE_HORDE_3 }; std::vector const WS_FLAG_HIDE_ALLIANCE = { WS_FLAG_HIDE_ALLIANCE_1 , WS_FLAG_HIDE_ALLIANCE_2, WS_FLAG_HIDE_ALLIANCE_3 }; + 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 }; + 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 = { -492.17f, -187.077f, 57.1342f, 2.77f }; -Position const AV_STONEHEARTH_WAITING_HORDE = { 28.1264f, -302.593f, 15.076f, 2.96f }; +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 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 }; @@ -799,7 +803,16 @@ BattleBotPath vPath_AB_Farm_to_LumberMill = BattleBotPath vPath_AV_Horde_Cave_to_Tower_Point_Crossroad = { - { -885.928f, -536.612f, 55.1936f, nullptr }, + { -1362.395f, -529.615f, 52.636f, nullptr }, + { -1327.036f, -511.374f, 51.138f, nullptr }, + { -1277.047f, -516.327f, 50.667f, nullptr }, + { -1214.901f, -529.350f, 52.251f, nullptr }, + { -1151.129f, -545.598f, 51.990f, nullptr }, + { -1085.775f, -538.719f, 47.905f, nullptr }, + { -1038.552f, -517.946f, 43.876f, nullptr }, + { -981.371f, -494.593f, 41.127f, nullptr }, + { -930.598f, -463.751f, 43.060f, nullptr }, + { -887.138f, -475.816f, 44.374f, nullptr }, { -880.957f, -525.119f, 53.6791f, nullptr }, { -839.408f, -499.746f, 49.7505f, nullptr }, { -820.21f, -469.193f, 49.4085f, nullptr }, @@ -2262,48 +2275,48 @@ std::vector const vPaths_HordeMine = &vPath_AV_Coldtooth_Mine_Entrance_to_Coldtooth_Mine_Boss, }; -static uint32 AV_HordeAttackObjectives[] = +static std::pair AV_HordeAttackObjectives[] = { // Attack - { BG_AV_NODES_STONEHEART_BUNKER }, - { BG_AV_NODES_STONEHEART_GRAVE }, - { BG_AV_NODES_ICEWING_BUNKER }, - { BG_AV_NODES_STORMPIKE_GRAVE }, - { BG_AV_NODES_DUNBALDAR_SOUTH }, - { BG_AV_NODES_DUNBALDAR_NORTH }, - { BG_AV_NODES_FIRSTAID_STATION } + { BG_AV_NODES_STONEHEART_BUNKER, BG_AV_OBJECT_FLAG_A_STONEHEART_BUNKER }, + { BG_AV_NODES_STONEHEART_GRAVE, BG_AV_OBJECT_FLAG_A_STONEHEART_GRAVE }, + { 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 } }; -static uint32 AV_HordeDefendObjectives[] = +static std::pair AV_HordeDefendObjectives[] = { // Defend - { BG_AV_NODES_FROSTWOLF_GRAVE }, - { BG_AV_NODES_FROSTWOLF_ETOWER }, - { BG_AV_NODES_FROSTWOLF_WTOWER }, - { BG_AV_NODES_TOWER_POINT }, - { BG_AV_NODES_ICEBLOOD_TOWER }, + { BG_AV_NODES_FROSTWOLF_GRAVE, BG_AV_OBJECT_FLAG_H_FROSTWOLF_GRAVE }, + { BG_AV_NODES_FROSTWOLF_ETOWER, BG_AV_OBJECT_FLAG_H_FROSTWOLF_ETOWER }, + { BG_AV_NODES_FROSTWOLF_WTOWER, BG_AV_OBJECT_FLAG_H_FROSTWOLF_WTOWER }, + { BG_AV_NODES_TOWER_POINT, BG_AV_OBJECT_FLAG_H_TOWER_POINT }, + { BG_AV_NODES_ICEBLOOD_TOWER, BG_AV_OBJECT_FLAG_H_ICEBLOOD_TOWER }, }; -static uint32 AV_AllianceAttackObjectives[] = +static std::pair AV_AllianceAttackObjectives[] = { // Attack - { BG_AV_NODES_ICEBLOOD_TOWER }, - { BG_AV_NODES_ICEBLOOD_GRAVE }, - { BG_AV_NODES_TOWER_POINT }, - { BG_AV_NODES_FROSTWOLF_GRAVE }, - { BG_AV_NODES_FROSTWOLF_ETOWER }, - { BG_AV_NODES_FROSTWOLF_WTOWER }, - { BG_AV_NODES_FROSTWOLF_HUT }, + { BG_AV_NODES_ICEBLOOD_TOWER, BG_AV_OBJECT_FLAG_H_ICEBLOOD_TOWER}, + { BG_AV_NODES_ICEBLOOD_GRAVE, BG_AV_OBJECT_FLAG_H_ICEBLOOD_GRAVE}, + { 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 }, + { BG_AV_NODES_FROSTWOLF_WTOWER, BG_AV_OBJECT_FLAG_H_FROSTWOLF_WTOWER }, + { BG_AV_NODES_FROSTWOLF_HUT, BG_AV_OBJECT_FLAG_H_FROSTWOLF_HUT }, }; -static uint32 AV_AllianceDefendObjectives[] = +static std::pair AV_AllianceDefendObjectives[] = { // Defend - { BG_AV_NODES_STORMPIKE_GRAVE }, - { BG_AV_NODES_DUNBALDAR_SOUTH }, - { BG_AV_NODES_DUNBALDAR_NORTH }, - { BG_AV_NODES_ICEWING_BUNKER }, - { BG_AV_NODES_STONEHEART_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_ICEWING_BUNKER, BG_AV_OBJECT_FLAG_A_ICEWING_BUNKER }, + { BG_AV_NODES_STONEHEART_BUNKER, BG_AV_OBJECT_FLAG_A_STONEHEART_BUNKER }, }; static uint32 AB_AttackObjectives[] = @@ -2937,7 +2950,7 @@ bool BGTactics::selectObjective(bool reset) alterValleyBG->GetAVNodeInfo(BG_AV_NODES_STONEHEART_BUNKER).TotalOwnerId != TEAM_ALLIANCE && alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FIRSTAID_STATION).TotalOwnerId != TEAM_ALLIANCE) { - if (Creature* pVanndar = bg->GetBGCreature(AV_NPC_A_BOSS)) + if (Creature* pVanndar = bg->GetBGCreature(AV_CPLACE_TRIGGER17)) { BgObjective = pVanndar; endBoss = true; @@ -2952,8 +2965,9 @@ bool BGTactics::selectObjective(bool reset) // 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_HORDE || alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_OTHER)) + if (!BgObjective && supporter && + (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_ALLIANCE || + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_OTHER)) { if (GameObject* pGO = bg->GetBGObject(BG_AV_NODES_SNOWFALL_GRAVE)) if (bot->IsWithinDist(pGO, 200.f)) @@ -2967,19 +2981,20 @@ bool BGTactics::selectObjective(bool reset) if (!BgObjective && alterValleyBG->IsCaptainAlive(0)) { - if (Creature* pBalinda = bg->GetBGCreature(AV_NPC_A_CAPTAIN)) + if (Creature* pBalinda = bg->GetBGCreature(AV_CPLACE_TRIGGER16)) { if (pBalinda->getDeathState() != DeathState::Dead) { - uint32 attackCount = 0; - attackCount += getDefendersCount(AV_STONEHEARTH_WAITING_HORDE, 10.0f, false); + 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()) { // 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()); + 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!"; @@ -2987,6 +3002,12 @@ bool BGTactics::selectObjective(bool reset) } 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); @@ -3007,9 +3028,9 @@ bool BGTactics::selectObjective(bool reset) { for (const auto& objective : AV_HordeDefendObjectives) { - if (!BgObjective && alterValleyBG->GetAVNodeInfo(objective).OwnerId == TEAM_ALLIANCE) + if (!BgObjective && alterValleyBG->GetAVNodeInfo(objective.first).OwnerId == TEAM_ALLIANCE) { - if (GameObject* pGO = bg->GetBGObject(objective)) + if (GameObject* pGO = bg->GetBGObject(objective.second)) if (bot->IsWithinDist(pGO, 400.0f)) { BgObjective = pGO; @@ -3022,7 +3043,8 @@ bool BGTactics::selectObjective(bool reset) } // Mine capture (need paths & script fix) - if (!BgObjective && supporter && !endBoss && (alterValleyBG->GetMineOwner(AV_NORTH_MINE) == TEAM_ALLIANCE || alterValleyBG->GetMineOwner(AV_NORTH_MINE) == TEAM_OTHER) && + if (!BgObjective && supporter && !endBoss && + (alterValleyBG->GetMineOwner(AV_NORTH_MINE) == TEAM_ALLIANCE || alterValleyBG->GetMineOwner(AV_NORTH_MINE) == TEAM_OTHER) && alterValleyBG->GetAVNodeInfo(BG_AV_NODES_STORMPIKE_GRAVE).OwnerId != TEAM_ALLIANCE) { if (Creature* mBossNeutral = bg->GetBGCreature(AV_CPLACE_MINE_N_3)) @@ -3055,10 +3077,9 @@ bool BGTactics::selectObjective(bool reset) 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).OwnerId == TEAM_ALLIANCE || alterValleyBG->GetAVNodeInfo(objective).TotalOwnerId == TEAM_ALLIANCE || - alterValleyBG->GetAVNodeInfo(objective).OwnerId == TEAM_OTHER)) + alterValleyBG->GetAVNodeInfo(objective.first).TotalOwnerId == TEAM_ALLIANCE)//need to check TotalOwnerId for attack objectives { - if (GameObject* pGO = bg->GetBGObject(objective)) + if (GameObject* pGO = bg->GetBGObject(objective.second)) { BgObjective = pGO; //std::ostringstream out; @@ -3073,11 +3094,13 @@ bool BGTactics::selectObjective(bool reset) { bool endBoss = false; // End boss - if (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_ICEBLOOD_TOWER).OwnerId != TEAM_HORDE && alterValleyBG->GetAVNodeInfo(BG_AV_NODES_TOWER_POINT).OwnerId != TEAM_HORDE && - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_ETOWER).OwnerId != TEAM_HORDE && alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_WTOWER).OwnerId != TEAM_HORDE && - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_HUT).OwnerId != TEAM_HORDE) + if (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_ICEBLOOD_TOWER).TotalOwnerId != TEAM_HORDE && + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_TOWER_POINT).TotalOwnerId != TEAM_HORDE && + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_ETOWER).TotalOwnerId != TEAM_HORDE && + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_WTOWER).TotalOwnerId != TEAM_HORDE && + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_HUT).TotalOwnerId != TEAM_HORDE) { - if (Creature* pDrek = bg->GetBGCreature(AV_NPC_H_BOSS)) + if (Creature* pDrek = bg->GetBGCreature(AV_CPLACE_TRIGGER19)) { BgObjective = pDrek; endBoss = true; @@ -3091,9 +3114,9 @@ bool BGTactics::selectObjective(bool reset) 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).TotalOwnerId == TEAM_HORDE || - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).TotalOwnerId == TEAM_OTHER)) + if (!BgObjective && supporter && + (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_HORDE || + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_OTHER)) { if (GameObject* pGO = bg->GetBGObject(BG_AV_NODES_SNOWFALL_GRAVE)) if (bot->IsWithinDist(pGO, 200.f)) @@ -3110,9 +3133,9 @@ bool BGTactics::selectObjective(bool reset) { for (const auto& objective : AV_AllianceDefendObjectives) { - if (!BgObjective && alterValleyBG->GetAVNodeInfo(objective).OwnerId == TEAM_HORDE) + if (!BgObjective && alterValleyBG->GetAVNodeInfo(objective.first).OwnerId == TEAM_HORDE) { - if (GameObject* pGO = bg->GetBGObject(objective)) + if (GameObject* pGO = bg->GetBGObject(objective.second)) { BgObjective = pGO; //std::ostringstream out; out << "Defending Node #" << objective.first; @@ -3123,8 +3146,8 @@ bool BGTactics::selectObjective(bool reset) } // Mine capture (need paths & script fix) - if (!BgObjective && supporter && !endBoss && (alterValleyBG->GetMineOwner(AV_SOUTH_MINE) == TEAM_HORDE || - alterValleyBG->GetMineOwner(AV_SOUTH_MINE) == TEAM_OTHER) && + if (!BgObjective && supporter && !endBoss && + (alterValleyBG->GetMineOwner(AV_SOUTH_MINE) == TEAM_HORDE || alterValleyBG->GetMineOwner(AV_SOUTH_MINE) == TEAM_OTHER) && alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_GRAVE).TotalOwnerId != TEAM_HORDE) { if (Creature* mBossNeutral = bg->GetBGCreature(AV_CPLACE_MINE_S_3)) @@ -3158,17 +3181,18 @@ bool BGTactics::selectObjective(bool reset) if (alterValleyBG->IsCaptainAlive(1)) { - if (Creature* pGalvangar = bg->GetBGCreature(AV_NPC_H_CAPTAIN)) + if (Creature* pGalvangar = bg->GetBGCreature(AV_CPLACE_TRIGGER18)) { - uint32 attackCount = 0; - attackCount += getDefendersCount(AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE, 10.0f, false); + 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()); + 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!"; @@ -3176,6 +3200,12 @@ bool BGTactics::selectObjective(bool reset) } 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); @@ -3187,11 +3217,9 @@ bool BGTactics::selectObjective(bool reset) for (const auto& objective : AV_AllianceAttackObjectives) { - if (alterValleyBG->GetAVNodeInfo(objective).OwnerId == TEAM_HORDE || - alterValleyBG->GetAVNodeInfo(objective).TotalOwnerId == TEAM_HORDE || - alterValleyBG->GetAVNodeInfo(objective).TotalOwnerId == TEAM_OTHER) + if (alterValleyBG->GetAVNodeInfo(objective.first).TotalOwnerId == TEAM_HORDE)//need to check TotalOwnerId for attack objectives { - if (GameObject* pGO = bg->GetBGObject(objective)) + if (GameObject* pGO = bg->GetBGObject(objective.second)) { float const distance = sqrt(bot->GetDistance(pGO)); if (attackObjectiveDistance > distance) @@ -4124,7 +4152,7 @@ bool BGTactics::moveToObjective() } // don't try to move if already close - if (sqrt(bot->GetDistance(pos.x, pos.y, pos.z)) < 5.0f) + if (sqrt(bot->GetDistance(pos.x, pos.y, pos.z)) < 2.0f) { resetObjective(); @@ -4134,8 +4162,8 @@ bool BGTactics::moveToObjective() //std::ostringstream out; out << "Moving to objective " << pos.x << ", " << pos.y << ", Distance: " << sServerFacade->GetDistance2d(bot, pos.x, pos.y); //bot->Say(out.str(), LANG_UNIVERSAL); - // more precise position for wsg - if (bgType == BATTLEGROUND_WS) + // more precise position for wsg and AV (flags in AV towers require precision) + if (bgType == BATTLEGROUND_WS || bgType == BATTLEGROUND_AV) return MoveTo(bot->GetMapId(), pos.x, pos.y, pos.z); else return MoveNear(bot->GetMapId(), pos.x, pos.y, pos.z, 3.0f); @@ -4946,9 +4974,9 @@ bool ArenaTactics::moveToCenter(Battleground* bg) case BATTLEGROUND_DS: if (!MoveTo(bg->GetMapId(), 1291.58f + frand(-5, +5), 790.87f + frand(-5, +5), 7.8f, false, true)) { // they like to hang around at the tip of the pipes doing nothing, so we just teleport them down - if (bot->GetDistance(1333.07f, 817.18f, 13.35f) < 2) + if (bot->GetDistance(1333.07f, 817.18f, 13.35f) < 4) bot->TeleportTo(bg->GetMapId(), 1330.96f, 816.75f, 3.2f, bot->GetOrientation()); - if (bot->GetDistance(1250.13f, 764.79f, 13.34f) < 2) + if (bot->GetDistance(1250.13f, 764.79f, 13.34f) < 4) bot->TeleportTo(bg->GetMapId(), 1252.19f, 765.41f, 3.2f, bot->GetOrientation()); } break; From 9accee0a64604909885cca01c5578d3c0e1c3740 Mon Sep 17 00:00:00 2001 From: Revision Date: Wed, 10 Jul 2024 19:20:43 +0200 Subject: [PATCH 4/7] Fix Draenei being created with expansion set to 0 --- src/RandomPlayerbotFactory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/RandomPlayerbotFactory.cpp b/src/RandomPlayerbotFactory.cpp index 20ef853b..6721c8e4 100644 --- a/src/RandomPlayerbotFactory.cpp +++ b/src/RandomPlayerbotFactory.cpp @@ -55,9 +55,9 @@ RandomPlayerbotFactory::RandomPlayerbotFactory(uint32 accountId) : accountId(acc availableRaces[CLASS_PRIEST].push_back(RACE_NIGHTELF); availableRaces[CLASS_PRIEST].push_back(RACE_TROLL); availableRaces[CLASS_PRIEST].push_back(RACE_UNDEAD_PLAYER); - availableRaces[CLASS_PRIEST].push_back(RACE_DRAENEI); if(expansion >= EXPANSION_THE_BURNING_CRUSADE) { + availableRaces[CLASS_PRIEST].push_back(RACE_DRAENEI); availableRaces[CLASS_PRIEST].push_back(RACE_BLOODELF); } From 72eb38af88d9146b446bf12792fa591620c39f5a Mon Sep 17 00:00:00 2001 From: Fuzz Date: Thu, 11 Jul 2024 13:06:17 +1000 Subject: [PATCH 5/7] fixed bots of same faction not fighting in arena until player triggers it --- src/strategy/values/AttackersValue.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/strategy/values/AttackersValue.cpp b/src/strategy/values/AttackersValue.cpp index 527b8def..cae2223a 100644 --- a/src/strategy/values/AttackersValue.cpp +++ b/src/strategy/values/AttackersValue.cpp @@ -47,8 +47,21 @@ GuidVector AttackersValue::Calculate() if (bot->duel && bot->duel->Opponent) result.push_back(bot->duel->Opponent->GetGUID()); - - return result; + + // workaround for bots of same faction not fighting in arena + if (bot->InArena()) + { + GuidVector possibleTargets = AI_VALUE(GuidVector, "possible targets"); + for (ObjectGuid const guid : possibleTargets) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->IsPlayer() && IsValidTarget(unit, bot)) { + result.push_back(unit->GetGUID()); + } + } + } + + return result; } void AttackersValue::AddAttackersOf(Group* group, std::unordered_set& targets) From 7309d8f3c9fb14cb5665b5b496a97bdb60a969e2 Mon Sep 17 00:00:00 2001 From: Revision Date: Thu, 11 Jul 2024 23:46:59 +0200 Subject: [PATCH 6/7] Add config options for the new summon conditions --- conf/playerbots.conf.dist | 14 +++++++- src/PlayerbotAIConfig.cpp | 3 ++ src/PlayerbotAIConfig.h | 3 ++ .../actions/UseMeetingStoneAction.cpp | 35 +++++++++---------- 4 files changed, 35 insertions(+), 20 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 8e91ec1d..b81cc222 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -127,7 +127,19 @@ AiPlayerbot.EquipmentPersistenceLevel = 80 # default: 1 (accept based on level) AiPlayerbot.GroupInvitationPermission = 1 -# Enable/Disable bot revive when summon (0 = never, 1 = enable when non-combat and alive, 2 = enable always) +# Enable/Disable summoning bots when the master is in combat +# default: 1 (enabled) +AiPlayerbot.AllowSummonInCombat = 1 + +# Enable/Disable summoning bots when the master is dead +# default: 1 (enabled) +AiPlayerbot.AllowSummonWhenMasterIsDead = 1 + +# Enable/Disable summoning bots when they are dead (0 = only when the bots are ghosts, 1 = always) +# default: 1 (enabled) +AiPlayerbot.AllowSummonWhenBotIsDead = 1 + +# Enable/Disable bot revive when summon (0 = never, 1 = enable when non-combat, 2 = enable always) # default: 1 (enable for non-combat) AiPlayerbot.BotReviveWhenSummon = 1 diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index 85ee2d56..79531200 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -273,6 +273,9 @@ bool PlayerbotAIConfig::Initialize() equipmentPersistence = sConfigMgr->GetOption("AiPlayerbot.EquipmentPersistence", false); equipmentPersistenceLevel = sConfigMgr->GetOption("AiPlayerbot.EquipmentPersistenceLevel", 80); groupInvitationPermission = sConfigMgr->GetOption("AiPlayerbot.GroupInvitationPermission", 1); + allowSummonInCombat = sConfigMgr->GetOption("AiPlayerbot.AllowSummonInCombat", true); + allowSummonWhenMasterIsDead = sConfigMgr->GetOption("AiPlayerbot.AllowSummonWhenMasterIsDead", true); + allowSummonWhenBotIsDead = sConfigMgr->GetOption("AiPlayerbot.AllowSummonWhenBotIsDead", true); botReviveWhenSummon = sConfigMgr->GetOption("AiPlayerbot.BotReviveWhenSummon", 1); botRepairWhenSummon = sConfigMgr->GetOption("AiPlayerbot.BotRepairWhenSummon", true); autoInitOnly = sConfigMgr->GetOption("AiPlayerbot.AutoInitOnly", false); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 5e63c745..3fb31410 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -214,6 +214,9 @@ class PlayerbotAIConfig bool equipmentPersistence; int32 equipmentPersistenceLevel; int32 groupInvitationPermission; + bool allowSummonInCombat; + bool allowSummonWhenMasterIsDead; + bool allowSummonWhenBotIsDead; int32 botReviveWhenSummon; bool botRepairWhenSummon; bool autoInitOnly; diff --git a/src/strategy/actions/UseMeetingStoneAction.cpp b/src/strategy/actions/UseMeetingStoneAction.cpp index 0f908530..f07ca217 100644 --- a/src/strategy/actions/UseMeetingStoneAction.cpp +++ b/src/strategy/actions/UseMeetingStoneAction.cpp @@ -179,28 +179,25 @@ bool SummonAction::Teleport(Player* summoner, Player* player) if (sPlayerbotAIConfig->botRepairWhenSummon) // .conf option to repair bot gear when summoned 0 = off, 1 = on bot->DurabilityRepairAll(false, 1.0f, false); - if (sPlayerbotAIConfig->botReviveWhenSummon < 2) + if (master->IsInCombat() && !sPlayerbotAIConfig->allowSummonInCombat) { - if (master->IsInCombat()) - { - botAI->TellError("You cannot summon me while you're in combat"); - return false; - } - - if (!master->IsAlive()) - { - botAI->TellError("You cannot summon me while you're dead"); - return false; - } - - if (bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST)) - { - botAI->TellError("You cannot summon me while I'm dead, you need to release my spirit first"); - return false; - } + botAI->TellError("You cannot summon me while you're in combat"); + return false; } - if (sPlayerbotAIConfig->botReviveWhenSummon > 0 && bot->isDead()) + if (!master->IsAlive() && !sPlayerbotAIConfig->allowSummonWhenMasterIsDead) + { + botAI->TellError("You cannot summon me while you're dead"); + return false; + } + + if (bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST) && !sPlayerbotAIConfig->allowSummonWhenBotIsDead) + { + botAI->TellError("You cannot summon me while I'm dead, you need to release my spirit first"); + return false; + } + + if (sPlayerbotAIConfig->botReviveWhenSummon == 2 || (sPlayerbotAIConfig->botReviveWhenSummon == 1 && !master->IsInCombat() && master->IsAlive())) { bot->ResurrectPlayer(1.0f, false); botAI->TellMasterNoFacing("I live, again!"); From 5362668690a33d2fc675b7573db22ae1c21afa4d Mon Sep 17 00:00:00 2001 From: Revision Date: Thu, 11 Jul 2024 23:50:24 +0200 Subject: [PATCH 7/7] Correction for the default value info --- conf/playerbots.conf.dist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index b81cc222..561bea76 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -136,7 +136,7 @@ AiPlayerbot.AllowSummonInCombat = 1 AiPlayerbot.AllowSummonWhenMasterIsDead = 1 # Enable/Disable summoning bots when they are dead (0 = only when the bots are ghosts, 1 = always) -# default: 1 (enabled) +# default: 1 (always) AiPlayerbot.AllowSummonWhenBotIsDead = 1 # Enable/Disable bot revive when summon (0 = never, 1 = enable when non-combat, 2 = enable always)