From 88e176fc52defc1164612188240ccb03bce2f43d Mon Sep 17 00:00:00 2001 From: trickerer Date: Fri, 8 Nov 2024 18:33:12 +0700 Subject: [PATCH] NPCBots: Implement bots participating in non-rated arena matches. Allow owned bots to retaliate to non-melee hostile actions if within attack radius (cherry picked from commit 392ec0fa06de7f9e3eba311d098204d6dd434582) # Conflicts: # src/server/game/Battlegrounds/Arena.cpp # src/server/game/Battlegrounds/Arena.h # src/server/game/Battlegrounds/Battleground.cpp # src/server/game/Groups/Group.cpp # src/server/game/Handlers/BattleGroundHandler.cpp --- src/server/game/AI/NpcBots/bot_ai.cpp | 49 ++++++++--- src/server/game/AI/NpcBots/botmgr.cpp | 28 ++++-- src/server/game/AI/NpcBots/botspell.h | 4 + src/server/game/AI/NpcBots/bpet_ai.cpp | 11 ++- src/server/game/Battlegrounds/Arena.cpp | 85 +++++++++++++++++++ src/server/game/Battlegrounds/Arena.h | 10 +++ .../game/Battlegrounds/Battleground.cpp | 2 +- .../game/Entities/Creature/Creature.cpp | 5 ++ src/server/game/Groups/Group.cpp | 8 ++ .../game/Handlers/BattleGroundHandler.cpp | 51 +++++++++++ 10 files changed, 223 insertions(+), 30 deletions(-) diff --git a/src/server/game/AI/NpcBots/bot_ai.cpp b/src/server/game/AI/NpcBots/bot_ai.cpp index 801624622903ee..a7ac857aabee02 100644 --- a/src/server/game/AI/NpcBots/bot_ai.cpp +++ b/src/server/game/AI/NpcBots/bot_ai.cpp @@ -5334,15 +5334,17 @@ void bot_ai::_extendAttackRange(float& dist) const } bool bot_ai::_canSwitchToTarget(Unit const* from, Unit const* newTarget, int8 byspell) const { - if (newTarget) + if (newTarget && newTarget != me->GetVictim()) { if (IAmFree()) { - if (newTarget != me->GetVictim() && - (!from || me->GetDistance(newTarget) < me->GetDistance(from) - 10.0f || newTarget->GetHealth() < from->GetHealth()) && + if ((!from || me->GetDistance(newTarget) < me->GetDistance(from) - 10.0f || newTarget->GetHealth() < from->GetHealth()) && CanBotAttack(newTarget, byspell)) return true; } + else if (!from && me->GetDistance(newTarget) < 0.75f * _getAttackDistance(float(master->GetBotMgr()->GetBotFollowDist())) && + CanBotAttack(newTarget, byspell)) + return true; } return false; @@ -17681,13 +17683,17 @@ bool bot_ai::GlobalUpdate(uint32 diff) //Faction //ensure master is not controlled ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(master->GetRace()); - uint32 fac = rEntry ? rEntry->FactionID : 0; - if (me->GetFaction() != master->GetFaction() && master->GetFaction() == fac) + uint32 fac_orig = rEntry ? rEntry->FactionID : 0; + if (master->GetFaction() == fac_orig) { - //std::ostringstream msg; - //msg << "Something changed my faction (now " << me->GetFaction() << "), changing back to " << fac << "!"; - //BotWhisper(msg.str().c_str()); - me->SetFaction(fac); + uint32 fac = (!IAmFree() && me->GetMap()->IsBattleArena()) ? FACTION_MONSTER : fac_orig; + if (me->GetFaction() != fac) + { + //std::ostringstream msg; + //msg << "Something changed my faction (now " << me->GetFaction() << "), changing back to " << fac << "!"; + //BotWhisper(msg.str().c_str()); + me->SetFaction(fac); + } } //Visibility if (!me->IsVisible() && master->IsVisible()) @@ -18683,6 +18689,16 @@ bool bot_ai::FinishTeleport(bool reset) } //me->CastSpell(me, HONORLESS_TARGET, true); + //Arena flags + Battleground const* bg = GetBG(); + if (bg && bg->isArena()) + { + TeamId teamId = bg->GetBotTeamId(me->GetGUID()); + uint32 flag_spell = teamId == TEAM_ALLIANCE ? master->GetTeamId() == TEAM_HORDE ? ARENA_FLAG_TEAM_H_GOLD : ARENA_FLAG_TEAM_A_GOLD : + master->GetTeamId() == TEAM_HORDE ? ARENA_FLAG_TEAM_H_GREEN : ARENA_FLAG_TEAM_A_GREEN; + me->CastSpell(me, flag_spell, true); + } + //update group member online state if (Group* gr = master->GetGroup()) if (gr->IsMember(me->GetGUID())) @@ -20015,18 +20031,23 @@ void bot_ai::OnBotEnterBattleground() if (bg->GetStatus() != STATUS_IN_PROGRESS && IsWanderer()) { BotWPFlags myTeamSpawnFlags; - switch (bg->GetBotTeamId(me->GetGUID())) + if (bg->isArena()) + myTeamSpawnFlags = BotWPFlags::BOTWP_FLAG_SPAWN; + else { - case TEAM_ALLIANCE: myTeamSpawnFlags = BotWPFlags::BOTWP_FLAG_ALLIANCE_SPAWN_POINT; break; - case TEAM_HORDE: myTeamSpawnFlags = BotWPFlags::BOTWP_FLAG_HORDE_SPAWN_POINT; break; - default: myTeamSpawnFlags = BotWPFlags::BOTWP_FLAG_SPAWN; break; + switch (bg->GetBotTeamId(me->GetGUID())) + { + case TEAM_ALLIANCE: myTeamSpawnFlags = BotWPFlags::BOTWP_FLAG_ALLIANCE_SPAWN_POINT; break; + case TEAM_HORDE: myTeamSpawnFlags = BotWPFlags::BOTWP_FLAG_HORDE_SPAWN_POINT; break; + default: myTeamSpawnFlags = BotWPFlags::BOTWP_FLAG_SPAWN; break; + } } uint32 mapId = bg->GetBgMap()->GetId(); float mindist = 50000.0f; WanderNode const* startNode = nullptr; WanderNode::DoForAllMapWPs(mapId, [pos = me->GetPosition(), spawnFlags = myTeamSpawnFlags, &mindist, &startNode](WanderNode const* wp) { - if (wp->HasFlag(spawnFlags)) + if (wp->HasAllFlags(spawnFlags)) { float dist = pos.GetExactDist2d(wp); if (dist < mindist) diff --git a/src/server/game/AI/NpcBots/botmgr.cpp b/src/server/game/AI/NpcBots/botmgr.cpp index 2f72b51c759498..63ab63fb8f8ac6 100644 --- a/src/server/game/AI/NpcBots/botmgr.cpp +++ b/src/server/game/AI/NpcBots/botmgr.cpp @@ -1189,9 +1189,8 @@ void BotMgr::Update(uint32 diff) if (ai->GetReviveTimer() <= diff) { if (bot->IsInMap(_owner) && !bot->IsAlive() && !ai->IsDuringTeleport() && _owner->IsAlive() && !_owner->IsInCombat() && - !_owner->IsBeingTeleported() && !_owner->InArena() && !_owner->IsInFlight() && - !_owner->HasUnitFlag2(UNIT_FLAG2_FEIGN_DEATH) && - !_owner->HasInvisibilityAura() && !_owner->HasStealthAura()) + !_owner->IsBeingTeleported() && !_owner->GetMap()->IsBattleArena() && !_owner->IsInFlight() && + !_owner->HasUnitFlag2(UNIT_FLAG2_FEIGN_DEATH) && !_owner->HasInvisibilityAura() && !_owner->HasStealthAura()) { _reviveBot(bot); continue; @@ -1253,10 +1252,11 @@ bool BotMgr::RestrictBots(Creature const* bot, bool add) const if (LimitBots(currMap)) { + Group const* gr = _owner->GetGroup(); + //if bot is not in instance group - deny (only if trying to teleport to instance) if (add) { - Group const* gr = _owner->GetGroup(); if (!gr || !gr->IsMember(bot->GetGUID())) return true; @@ -1293,13 +1293,23 @@ bool BotMgr::RestrictBots(Creature const* bot, bool add) const uint32 max_players = 0; if (currMap->IsDungeon()) max_players = currMap->ToInstanceMap()->GetMaxPlayers(); - else if (currMap->IsBattleground()) + else if (currMap->IsBattleground() || currMap->IsBattleArena()) max_players = _owner->GetBattleground()->GetMaxPlayersPerTeam(); - else if (currMap->IsBattleArena()) - max_players = _owner->GetBattleground()->GetArenaType(); - if (max_players && currMap->GetPlayersCountExceptGMs() + uint32(add) > max_players) - return true; + if (max_players) + { + uint32 curPlayers; + if (gr && currMap->IsBattlegroundOrArena()) + { + curPlayers = std::ranges::count_if(GetAllGroupMembers(gr), [this](Unit const* u) { + return u->IsInWorld() && u->IsInMap(_owner) && !(u->IsNPCBot() && u->ToCreature()->IsTempBot()); + }); + } + else + curPlayers = currMap->GetPlayersCountExceptGMs(); + if (curPlayers + uint32(add) > max_players) + return true; + } } return false; diff --git a/src/server/game/AI/NpcBots/botspell.h b/src/server/game/AI/NpcBots/botspell.h index 9e5d64fd9e80af..e34677929376b9 100644 --- a/src/server/game/AI/NpcBots/botspell.h +++ b/src/server/game/AI/NpcBots/botspell.h @@ -23,6 +23,10 @@ enum BotSpells : uint32 SUMMONING_STONE_EFFECT = 59782,//Cast time 5s + Channeled 2m SHOOT_WAND = 5019, OPEN_FLAG_BG = 21651, + ARENA_FLAG_TEAM_A_GOLD = 32724, + ARENA_FLAG_TEAM_A_GREEN = 32725, + ARENA_FLAG_TEAM_H_GOLD = 35774, + ARENA_FLAG_TEAM_H_GREEN = 35775, ///Portals PORTAL_STORMWIND = 10059, PORTAL_IRONFORGE = 11416, diff --git a/src/server/game/AI/NpcBots/bpet_ai.cpp b/src/server/game/AI/NpcBots/bpet_ai.cpp index 31eb0066bd787e..b26bb9b3cc0a29 100644 --- a/src/server/game/AI/NpcBots/bpet_ai.cpp +++ b/src/server/game/AI/NpcBots/bpet_ai.cpp @@ -2437,13 +2437,12 @@ bool bot_pet_ai::GlobalUpdate(uint32 diff) //Faction //ensure master is not controlled ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(petOwner->GetBotOwner()->GetRace()); - uint32 fac = rEntry ? rEntry->FactionID : 0; - if (me->GetFaction() != petOwner->GetBotOwner()->GetFaction() && petOwner->GetBotOwner()->GetFaction() == fac) + uint32 fac_orig = rEntry ? rEntry->FactionID : 0; + if (petOwner->GetBotOwner()->GetFaction() == fac_orig) { - //std::ostringstream msg; - //msg << "Something changed my faction (now " << me->GetFaction() << "), changing back to " << fac << "!"; - //BotWhisper(msg.str().c_str()); - me->SetFaction(fac); + uint32 fac = (!IAmFree() && me->GetMap()->IsBattleArena()) ? FACTION_MONSTER : fac_orig; + if (me->GetFaction() != fac) + me->SetFaction(fac); } //Visibility if (!me->IsVisible() && petOwner->GetBotOwner()->IsVisible()) diff --git a/src/server/game/Battlegrounds/Arena.cpp b/src/server/game/Battlegrounds/Arena.cpp index b0ecc3ab5a1e62..6f9d014085f47e 100644 --- a/src/server/game/Battlegrounds/Arena.cpp +++ b/src/server/game/Battlegrounds/Arena.cpp @@ -137,6 +137,24 @@ void Arena::AddPlayer(Player* player) } } +//npcbot +void Arena::AddBot(Creature* bot) +{ + ASSERT(bot->IsNPCBot() && !bot->IsFreeBot()); + + bool const isInBattleground = IsPlayerInBattleground(bot->GetGUID()); + Battleground::AddBot(bot); + TeamId botteamid = bot->GetBotOwner()->GetBgTeamId(); + + if (!isInBattleground) + BotScores[bot->GetEntry()] = new ArenaScore(bot->GetGUID(), botteamid); + + //No flags - handled by AI + + UpdateArenaWorldState(); +} +//end npcbot + void Arena::RemovePlayer(Player* /*player*/) { if (GetStatus() == STATUS_WAIT_LEAVE) @@ -146,6 +164,17 @@ void Arena::RemovePlayer(Player* /*player*/) CheckWinConditions(); } +//npcbot +void Arena::RemoveBot(ObjectGuid /*guid*/) +{ + if (GetStatus() == STATUS_WAIT_LEAVE) + return; + + UpdateArenaWorldState(); + CheckWinConditions(); +} +//end npcbot + void Arena::FillInitialWorldStates(WorldPacket& data) { data << uint32(ARENA_WORLD_STATE_ALIVE_PLAYERS_GREEN) << uint32(GetAlivePlayersCountByTeam(TEAM_HORDE)); @@ -169,6 +198,33 @@ void Arena::HandleKillPlayer(Player* player, Player* killer) CheckWinConditions(); } +//npcbot +void Arena::HandleBotKillPlayer(Creature* killer, Player* victim) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return; + Battleground::HandleBotKillPlayer(killer, victim); + UpdateArenaWorldState(); + CheckWinConditions(); +} +void Arena::HandleBotKillBot(Creature* killer, Creature* victim) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return; + Battleground::HandleBotKillBot(killer, victim); + UpdateArenaWorldState(); + CheckWinConditions(); +} +void Arena::HandlePlayerKillBot(Creature* victim, Player* killer) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return; + Battleground::HandlePlayerKillBot(victim, killer); + UpdateArenaWorldState(); + CheckWinConditions(); +} +//end npcbot + void Arena::RemovePlayerAtLeave(Player* player) { if (isRated() && GetStatus() == STATUS_IN_PROGRESS) @@ -190,6 +246,35 @@ void Arena::RemovePlayerAtLeave(Player* player) Battleground::RemovePlayerAtLeave(player); } +//npcbot +void Arena::RemoveBotAtLeave(ObjectGuid guid) +{ + //if (isRated() && GetStatus() == STATUS_IN_PROGRESS) + //{ + // BattlegroundBotMap::const_iterator itr = m_Bots.find(guid); + // if (itr != m_Bots.end()) // check if the player was a participant of the match, or only entered through gm command (appear) + // { + // // if the player was a match participant, calculate rating + // uint32 team = itr->second.Team; + + // ArenaTeam* winnerArenaTeam = sArenaTeamMgr->GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team))); + // ArenaTeam* loserArenaTeam = sArenaTeamMgr->GetArenaTeamById(GetArenaTeamIdForTeam(team)); + + // // left a rated match while the encounter was in progress, consider as loser + // if (winnerArenaTeam && loserArenaTeam && winnerArenaTeam != loserArenaTeam) + // { + // if (Player* player = _GetPlayer(itr->first, itr->second.OfflineRemoveTime != 0, "Arena::RemovePlayerAtLeave")) + // loserArenaTeam->MemberLost(player, GetArenaMatchmakerRating(GetOtherTeam(team))); + // else + // loserArenaTeam->OfflineMemberLost(guid, GetArenaMatchmakerRating(GetOtherTeam(team))); + // } + // } + //} + + Battleground::RemoveBotAtLeave(guid); +} +//end npcbot + void Arena::CheckWinConditions() { if (!sScriptMgr->OnBeforeArenaCheckWinConditions(this)) diff --git a/src/server/game/Battlegrounds/Arena.h b/src/server/game/Battlegrounds/Arena.h index fd7460080301e6..963fc06e9e3e34 100644 --- a/src/server/game/Battlegrounds/Arena.h +++ b/src/server/game/Battlegrounds/Arena.h @@ -53,6 +53,16 @@ class AC_GAME_API Arena : public Battleground void RemovePlayer(Player* /*player*/) override; void FillInitialWorldStates(WorldPacket& data) override; + + //npcbot + void AddBot(Creature* bot) override; + void RemoveBotAtLeave(ObjectGuid guid) override; + void RemoveBot(ObjectGuid /*guid*/) override; + void HandleBotKillPlayer(Creature* killer, Player* victim) override; + void HandleBotKillBot(Creature* killer, Creature* victim) override; + void HandlePlayerKillBot(Creature* victim, Player* killer) override; + //end npcbot + void UpdateArenaWorldState(); void HandleKillPlayer(Player* player, Player* killer) override; diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index 69eebcabd22b5a..8e33138b16203f 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -1394,7 +1394,7 @@ void Battleground::AddPlayer(Player* player) void Battleground::AddBot(Creature* bot) { ObjectGuid guid = bot->GetGUID(); - TeamId teamId = BotDataMgr::GetTeamIdForFaction(bot->GetFaction()); + TeamId teamId = !bot->IsFreeBot() ? bot->GetBotOwner()->GetBgTeamId() : BotDataMgr::GetTeamIdForFaction(bot->GetFaction()); // Add to list/maps BattlegroundBot bb; diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 6f46f82d382216..1a7871a99427f7 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -1454,6 +1454,11 @@ void Creature::SetLootRecipient(Unit* unit, bool withGroup) else m_lootRecipientGroup = 0; + //npcbot: prevent visual tap on owned bots + if (IsNPCBotOrPet() && !IsFreeBot()) + return; + //end npcbot + SetDynamicFlag(UNIT_DYNFLAG_TAPPED); } diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index 9742c957564cbd..5fe3e17e1b6a98 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -2331,6 +2331,14 @@ GroupJoinBattlegroundResult Group::CanJoinBattlegroundQueue(Battleground const* } } + //npcbot + for (GroupBotReference* bitr = GetFirstBotMember(); bitr != nullptr; bitr = bitr->next(), ++memberscount) + { + if (!bitr->GetSource()) + return ERR_BATTLEGROUND_JOIN_FAILED; + } + //end npcbot + // for arenas: check party size is proper if (bgTemplate->isArena() && memberscount != MinPlayerCount) return ERR_ARENA_TEAM_PARTY_SIZE; diff --git a/src/server/game/Handlers/BattleGroundHandler.cpp b/src/server/game/Handlers/BattleGroundHandler.cpp index aa9de0fea74c0f..fa53ac9dc9dee6 100644 --- a/src/server/game/Handlers/BattleGroundHandler.cpp +++ b/src/server/game/Handlers/BattleGroundHandler.cpp @@ -842,6 +842,21 @@ void WorldSession::HandleBattlemasterJoinArena(WorldPacket& recvData) return; } + //npcbot + bool have_bots_in_group = false; + if (_player->GetGroup() && _player->HaveBot()) + { + for (auto const& mslot : _player->GetGroup()->GetMemberSlots()) + { + if (mslot.guid.IsCreature() && _player->GetBotMgr()->GetBot(mslot.guid)) + { + have_bots_in_group = true; + break; + } + } + } + //end npcbot + BattlegroundQueueTypeId bgQueueTypeId = BattlegroundMgr::BGQueueTypeId(bgTypeId, arenatype); BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId); @@ -875,6 +890,16 @@ void WorldSession::HandleBattlemasterJoinArena(WorldPacket& recvData) if (!_player->HasFreeBattlegroundQueueId()) return; + //npcbot: do not allow entering as group if there are bots in group + if (have_bots_in_group) + { + WorldPacket data; + sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, ERR_BATTLEGROUND_JOIN_FAILED); + _player->SendDirectMessage(&data); + return; + } + //end npcbot + GroupQueueInfo* ginfo = bgQueue.AddGroup(_player, nullptr, bgTypeId, bracketEntry, arenatype, isRated != 0, false, arenaRating, matchmakerRating, ateamId, previousOpponents); uint32 avgWaitTime = bgQueue.GetAverageQueueWaitTime(ginfo); uint32 queueSlot = _player->AddBattlegroundQueueId(bgQueueTypeId); @@ -912,6 +937,16 @@ void WorldSession::HandleBattlemasterJoinArena(WorldPacket& recvData) return; } + //npcbot: do not allow bots in rated matches + if (have_bots_in_group) + { + WorldPacket data; + sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, ERR_BATTLEGROUND_JOIN_TIMED_OUT); + _player->SendDirectMessage(&data); + return; + } + //end npcbot + // get team rating for queueing arenaRating = at->GetRating(); matchmakerRating = at->GetAverageMMR(grp); @@ -980,6 +1015,22 @@ void WorldSession::HandleBattlemasterJoinArena(WorldPacket& recvData) LOG_DEBUG("bg.battleground", "Battleground: player joined queue for arena as group bg queue type {} bg type {}: {}, NAME {}", bgQueueTypeId, bgTypeId, member->GetGUID().ToString(), member->GetName()); sScriptMgr->OnPlayerJoinArena(member); + + //npcbot: list bots + if (!member->HaveBot()) + continue; + + BotMap const* map = member->GetBotMgr()->GetBotMap(); + for (BotMap::const_iterator itr = map->begin(); itr != map->end(); ++itr) + { + Creature const* bot = itr->second; + if (!bot || !grp->IsMember(bot->GetGUID())) + continue; + + LOG_DEBUG("bg.battleground", "Battleground: NPCBot joined queue for arena bg queue type {} bg type {}: GUID {}, NAME {} (owner: {})", + bgQueueTypeId, bgTypeId, bot->GetGUID().ToString(), bot->GetName(), member->GetName()); + } + //end npcbot } }