diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 55cc97dd81..9a0b4d2d41 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -1187,6 +1187,14 @@ AiPlayerbot.PvpProhibitedZoneIds = "2255,656,2361,2362,2363,976,35,2268,3425,392 # PvP Restricted Areas (bots don't pvp) AiPlayerbot.PvpProhibitedAreaIds = "976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080,3938,3754,3786,3973" +# PvP restricted distance from innkeepers (in yards) +# Default: 0 (disabled) +AiPlayerbot.PvpProhibitedInnkeeperDistance = 0 + +# PvP restricted distance from flight masters (in yards) +# Default: 0 (disabled) +AiPlayerbot.PvpProhibitedFlightMasterDistance = 0 + # Improve reaction speeds in battlegrounds and arenas (may cause lag) AiPlayerbot.FastReactInBG = 1 diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index 6ceb924124..baa52a7d75 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -167,6 +167,8 @@ bool PlayerbotAIConfig::Initialize() sConfigMgr->GetOption("AiPlayerbot.PvpProhibitedAreaIds", "976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080,3938,3754,3786,3973"), pvpProhibitedAreaIds); + pvpProhibitedFlightMasterDistance = sConfigMgr->GetOption("AiPlayerbot.PvpProhibitedFlightMasterDistance", 0.0f); + pvpProhibitedInnkeeperDistance = sConfigMgr->GetOption("AiPlayerbot.PvpProhibitedInnkeeperDistance", 0.0f); fastReactInBG = sConfigMgr->GetOption("AiPlayerbot.FastReactInBG", true); LoadList>( sConfigMgr->GetOption("AiPlayerbot.RandomBotQuestIds", "3802,5505,6502,7761,7848,10277,10285,11492,13188,13189,24499,24511,24710,24712"), @@ -694,9 +696,15 @@ bool PlayerbotAIConfig::IsInRandomQuestItemList(uint32 id) return find(randomBotQuestItems.begin(), randomBotQuestItems.end(), id) != randomBotQuestItems.end(); } -bool PlayerbotAIConfig::IsPvpProhibited(uint32 zoneId, uint32 areaId) +bool PlayerbotAIConfig::IsPvpProhibited(uint32 zoneId, uint32 areaId, Player* player) { - return IsInPvpProhibitedZone(zoneId) || IsInPvpProhibitedArea(areaId) || IsInPvpProhibitedZone(areaId); + if (IsInPvpProhibitedZone(zoneId) || IsInPvpProhibitedArea(areaId) || IsInPvpProhibitedZone(areaId)) + return true; + + if (player && IsNearProtectedNPC(player)) + return true; + + return false; } bool PlayerbotAIConfig::IsInPvpProhibitedZone(uint32 id) @@ -709,6 +717,46 @@ bool PlayerbotAIConfig::IsInPvpProhibitedArea(uint32 id) return find(pvpProhibitedAreaIds.begin(), pvpProhibitedAreaIds.end(), id) != pvpProhibitedAreaIds.end(); } +bool PlayerbotAIConfig::IsNearProtectedNPC(Player* player) +{ + if (!player) + return false; + + if (pvpProhibitedInnkeeperDistance <= 0.0f && pvpProhibitedFlightMasterDistance <= 0.0f) + return false; + + WorldPosition botPos(player); + uint32 mapId = player->GetMapId(); + + if (pvpProhibitedFlightMasterDistance > 0.0f) + { + auto it = sRandomPlayerbotMgr->flightMastersByMap.find(mapId); + if (it != sRandomPlayerbotMgr->flightMastersByMap.end()) + { + for (const WorldPosition& fmPos : it->second) + { + if (botPos.distance(fmPos) <= pvpProhibitedFlightMasterDistance) + return true; + } + } + } + + if (pvpProhibitedInnkeeperDistance > 0.0f) + { + auto it = sRandomPlayerbotMgr->innkeepersByMap.find(mapId); + if (it != sRandomPlayerbotMgr->innkeepersByMap.end()) + { + for (const WorldPosition& innkeeperPos : it->second) + { + if (botPos.distance(innkeeperPos) <= pvpProhibitedInnkeeperDistance) + return true; + } + } + } + + return false; +} + bool PlayerbotAIConfig::IsRestrictedHealerDPSMap(uint32 mapId) const { return restrictHealerDPS && diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 83a6a20b9a..857a0270f0 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -72,9 +72,10 @@ class PlayerbotAIConfig bool Initialize(); bool IsInRandomAccountList(uint32 id); bool IsInRandomQuestItemList(uint32 id); - bool IsPvpProhibited(uint32 zoneId, uint32 areaId); + bool IsPvpProhibited(uint32 zoneId, uint32 areaId, Player* player = nullptr); bool IsInPvpProhibitedZone(uint32 id); bool IsInPvpProhibitedArea(uint32 id); + bool IsNearProtectedNPC(Player* player); bool enabled; bool disabledWithoutRealPlayer; @@ -272,6 +273,8 @@ class PlayerbotAIConfig std::vector randomBotGuilds; std::vector pvpProhibitedZoneIds; std::vector pvpProhibitedAreaIds; + float pvpProhibitedFlightMasterDistance; + float pvpProhibitedInnkeeperDistance; bool fastReactInBG; bool randombotsWalkingRPG; diff --git a/src/RandomPlayerbotMgr.cpp b/src/RandomPlayerbotMgr.cpp index 1f94e35e13..d95a2b01ae 100644 --- a/src/RandomPlayerbotMgr.cpp +++ b/src/RandomPlayerbotMgr.cpp @@ -2007,7 +2007,11 @@ void RandomPlayerbotMgr::PrepareTeleportCache() { allianceFlightMasterCache.push_back(guid); } + flightMastersByMap[mapId].push_back(WorldPosition(mapId, x, y, z)); } + if (tNpcflag & UNIT_NPC_FLAG_INNKEEPER) + innkeepersByMap[mapId].push_back(WorldPosition(mapId, x, y, z)); + const AreaTableEntry* area = sAreaTableStore.LookupEntry(map->GetAreaId(PHASEMASK_NORMAL, x, y, z)); uint32 zoneId = area->zone ? area->zone : area->ID; if (zone2LevelBracket.find(zoneId) == zone2LevelBracket.end()) diff --git a/src/RandomPlayerbotMgr.h b/src/RandomPlayerbotMgr.h index be624328d6..352e3acda7 100644 --- a/src/RandomPlayerbotMgr.h +++ b/src/RandomPlayerbotMgr.h @@ -177,6 +177,8 @@ class RandomPlayerbotMgr : public PlayerbotHolder std::map> hordeStarterPerLevelCache; std::vector allianceFlightMasterCache; std::vector hordeFlightMasterCache; + std::unordered_map> flightMastersByMap; + std::unordered_map> innkeepersByMap; struct LevelBracket { uint32 low; uint32 high; diff --git a/src/strategy/actions/AttackAction.cpp b/src/strategy/actions/AttackAction.cpp index d72b18c553..e963d9bb61 100644 --- a/src/strategy/actions/AttackAction.cpp +++ b/src/strategy/actions/AttackAction.cpp @@ -84,11 +84,10 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/) return false; } - // Check if bot OR target is in prohibited zone/area (skip for duels) + // Check if bot is in prohibited zone/area (skip for duels) if ((target->IsPlayer() || target->IsPet()) && (!bot->duel || bot->duel->Opponent != target) && - (sPlayerbotAIConfig->IsPvpProhibited(bot->GetZoneId(), bot->GetAreaId()) || - sPlayerbotAIConfig->IsPvpProhibited(target->GetZoneId(), target->GetAreaId()))) + sPlayerbotAIConfig->IsPvpProhibited(bot->GetZoneId(), bot->GetAreaId(), bot)) { if (verbose) botAI->TellError("I cannot attack other players in PvP prohibited areas."); @@ -96,6 +95,34 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/) return false; } + // Check if target is in prohibited zone/area (skip for duels) + if (target->IsPlayer() || target->IsPet()) + { + Player* targetPlayer = nullptr; + + if (target->IsPlayer()) + targetPlayer = target->ToPlayer(); + + else if (target->IsPet()) + { + Pet* pet = target->ToPet(); + if (pet) + { + Unit* owner = pet->GetOwner(); + if (owner && owner->IsPlayer()) + targetPlayer = owner->ToPlayer(); + } + } + + if (targetPlayer && (!bot->duel || bot->duel->Opponent != target) && + sPlayerbotAIConfig->IsPvpProhibited(target->GetZoneId(), target->GetAreaId(), targetPlayer)) + { + if (verbose) + botAI->TellError("I cannot attack players who are in PvP prohibited areas."); + return false; + } + } + if (bot->IsFriendlyTo(target)) { if (verbose) diff --git a/src/strategy/actions/ChooseTargetActions.cpp b/src/strategy/actions/ChooseTargetActions.cpp index f98360ff7d..7206ef3e8a 100644 --- a/src/strategy/actions/ChooseTargetActions.cpp +++ b/src/strategy/actions/ChooseTargetActions.cpp @@ -19,7 +19,7 @@ bool AttackEnemyPlayerAction::isUseful() if (PlayerHasFlag::IsCapturingFlag(bot)) return false; - return !sPlayerbotAIConfig->IsPvpProhibited(bot->GetZoneId(), bot->GetAreaId()); + return !sPlayerbotAIConfig->IsPvpProhibited(bot->GetZoneId(), bot->GetAreaId(), bot); } bool AttackEnemyFlagCarrierAction::isUseful() diff --git a/src/strategy/actions/PetsAction.cpp b/src/strategy/actions/PetsAction.cpp index cb9dc93957..18ec15c95c 100644 --- a/src/strategy/actions/PetsAction.cpp +++ b/src/strategy/actions/PetsAction.cpp @@ -152,7 +152,7 @@ bool PetsAction::Execute(Event event) botAI->TellError(text); return false; } - if (sPlayerbotAIConfig->IsPvpProhibited(bot->GetZoneId(), bot->GetAreaId()) && + if (sPlayerbotAIConfig->IsPvpProhibited(bot->GetZoneId(), bot->GetAreaId(), bot) && (targetUnit->IsPlayer() || targetUnit->IsPet()) && (!bot->duel || bot->duel->Opponent != targetUnit)) { diff --git a/src/strategy/values/AttackersValue.cpp b/src/strategy/values/AttackersValue.cpp index 3c9685895a..e08035b9c3 100644 --- a/src/strategy/values/AttackersValue.cpp +++ b/src/strategy/values/AttackersValue.cpp @@ -176,7 +176,7 @@ bool AttackersValue::IsPossibleTarget(Unit* attacker, Player* bot, float /*range // PvP prohibition checks (skip for duels) if ((attacker->GetGUID().IsPlayer() || attacker->GetGUID().IsPet()) && (!bot->duel || bot->duel->Opponent != attacker) && - (sPlayerbotAIConfig->IsPvpProhibited(attacker->GetZoneId(), attacker->GetAreaId()) || + (sPlayerbotAIConfig->IsPvpProhibited(attacker->GetZoneId(), attacker->GetAreaId(), bot) || sPlayerbotAIConfig->IsPvpProhibited(bot->GetZoneId(), bot->GetAreaId()))) { // This will stop aggresive pets from starting an attack. diff --git a/src/strategy/values/EnemyPlayerValue.cpp b/src/strategy/values/EnemyPlayerValue.cpp index 2325c9c09b..76116655e3 100644 --- a/src/strategy/values/EnemyPlayerValue.cpp +++ b/src/strategy/values/EnemyPlayerValue.cpp @@ -14,7 +14,7 @@ bool NearestEnemyPlayersValue::AcceptUnit(Unit* unit) bool inCannon = botAI->IsInVehicle(false, true); Player* enemy = dynamic_cast(unit); if (enemy && botAI->IsOpposing(enemy) && enemy->IsPvP() && - !sPlayerbotAIConfig->IsPvpProhibited(enemy->GetZoneId(), enemy->GetAreaId()) && + !sPlayerbotAIConfig->IsPvpProhibited(enemy->GetZoneId(), enemy->GetAreaId(), enemy) && !enemy->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NON_ATTACKABLE_2) && ((inCannon || !enemy->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))) && /*!enemy->HasStealthAura() && !enemy->HasInvisibilityAura()*/ enemy->CanSeeOrDetect(bot) &&