Files
felwell-core/src/server/game/Server/PlayerBotSession.cpp
T
BlaMacfly 65d5978f4a Mercenaires : trois correctifs issus de l audit du module
1. La re-resolution des sorts passait par une file qui la jetait.

Le correctif 19c0cf03 poussait un BGSType_DelayLevelup apres la creation
de l IA de groupe. Mesure sur quatre mercenaires : deux repares, deux
non. En cause PushScheduleToQueue, qui jette silencieusement une tache
si une du meme type est deja en file, et -- pour ce type precisement --
si IsSettingFinish() est faux. La reparation ne partait qu au hasard.

On appelle desormais OnLevelupToBotAI() directement. L IA de groupe
vient d etre creee : le dynamic_cast qui l ouvre aboutit forcement.

2. Le raccourci de mise en place renvoyait des mercenaires nus.

Signale en jeu : « Kaerbrus n a meme pas d equipement ». Constate en
base : niveau 110, 76 sorts, zero piece portee.

ProcessSetting court-circuite le re-level d un bot deja au bon niveau,
avec ses talents et la bonne specialisation -- et saute du meme coup les
etapes d habillage. Un bot ayant perdu son equipement ne le retrouvait
jamais : a chaque embauche le raccourci concluait que tout allait bien.

On verifie donc qu il porte au moins dix pieces avant de couper.

3. Le rappel n intervenait qu au-dela de 160 metres.

Mesure : au moment d un « !attack », deux des quatre mercenaires se
trouvaient a 100 et 106 metres de leur employeur. Sous le seuil de
rappel, ils acceptaient l ordre et partaient A PIED -- une quinzaine de
secondes de marche, le combat fini avant leur arrivee. Vu du joueur, le
mercenaire « refuse d attaquer ».

Ce seuil convient a un bot de terrain, pas a un membre de groupe. Ramene
a deux fois la portee de recherche, soit 64 metres : bien au-dela des 32
ou l IA engage et des 28 de portee des sorts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 13:25:34 +02:00

841 lines
23 KiB
C++

/*
* This file is part of the DestinyCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "PlayerBotSession.h"
#include "Player.h"
#include "BattlegroundMgr.h"
#include "BotAI.h"
#include "BotGroupAI.h"
#include "BotFieldAI.h"
#include "LFGMgr.h"
#include "CharacterPackets.h"
#include "MovementPackets.h"
#include "DB2Structure.h"
#include "LFGPackets.h"
#include "LFGPacketsCommon.h"
#include "PetitionPackets.h"
PlayerBotSession::PlayerBotSession(uint32 id, std::string& name, uint32 battlenetAccountId, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, std::string&& battlenetAccountName) :
m_LastCastTime(CAST_SCHEDULE_TICK),
m_NoWorldTick(0),
m_AccountBot(false),
WorldSession(id, std::string(name), battlenetAccountId, nullptr, SEC_PLAYER, 6, 0, "Wn64", LOCALE_deDE, 0, false, std::string(battlenetAccountName))
{
SetAddress("DestinyCore");
}
bool PlayerBotSession::IsBotSession()
{
return true;
}
bool PlayerBotSession::HasBGSchedule()
{
for (BotSchedules::iterator itSc = m_Schedules.begin(); itSc != m_Schedules.end(); itSc++)
{
BotGlobleScheduleType bgsType = (*itSc).bbgType;
if (bgsType == BGSType_EnterBG || bgsType == BGSType_InBGQueue ||
bgsType == BGSType_LeaveBG || bgsType == BGSType_OutBGQueue ||
bgsType == BGSType_EnterAA || bgsType == BGSType_InAAQueue ||
bgsType == BGSType_LeaveAA || bgsType == BGSType_OutAAQueue)
return true;
}
return false;
}
bool PlayerBotSession::Update(uint32 diff, PacketFilter& updater)
{
bool updateResult = WorldSession::Update(diff, updater);
ProcessNoWorld(diff);
m_LastCastTime -= diff;
if (m_LastCastTime <= 0)
{
//std::lock_guard<std::mutex> lock(m_optQueueLock);
CastSchedule(CAST_SCHEDULE_TICK);
m_LastCastTime = CAST_SCHEDULE_TICK;
}
return updateResult;
}
void PlayerBotSession::PushScheduleToQueue(BotGlobleSchedule& schedule)
{
//std::lock_guard<std::mutex> lock(m_optQueueLock);
for (BotSchedules::iterator itSc = m_Schedules.begin(); itSc != m_Schedules.end(); itSc++)
{
if ((*itSc).bbgType == schedule.bbgType)
return;
}
if (schedule.bbgType == BGSType_DelayLevelup)
{
if (PlayerLoading())
return;
if (Player* player = GetPlayer())
{
if (!player->IsSettingFinish())
return;
}
else
return;
}
if (schedule.bbgType == BotGlobleScheduleType::BGSType_OutBGQueue)
{
RemoveScheduleByType(BotGlobleScheduleType::BGSType_InBGQueue);
RemoveScheduleByType(BotGlobleScheduleType::BGSType_EnterBG);
}
if (schedule.bbgType == BotGlobleScheduleType::BGSType_OutAAQueue)
{
RemoveScheduleByType(BotGlobleScheduleType::BGSType_InAAQueue);
RemoveScheduleByType(BotGlobleScheduleType::BGSType_EnterAA);
}
m_Schedules.push_back(schedule);
}
void PlayerBotSession::RemoveScheduleByType(BotGlobleScheduleType eType)
{
for (BotSchedules::iterator itSc = m_Schedules.begin(); itSc != m_Schedules.end(); itSc++)
{
if ((*itSc).bbgType == eType)
{
m_Schedules.erase(itSc);
return;
}
}
}
bool PlayerBotSession::HasScheduleByType(BotGlobleScheduleType eType)
{
for (BotSchedules::iterator itSc = m_Schedules.begin(); itSc != m_Schedules.end(); itSc++)
{
if ((*itSc).bbgType == eType)
{
return true;
}
}
return false;
}
bool PlayerBotSession::HasSchedules()
{
return m_Schedules.size() > 0;
}
bool PlayerBotSession::IsAccountBotSession()
{
return m_AccountBot;
}
bool PlayerBotSession::PlayerIsReady()
{
Player* player = GetPlayer();
if (!player)
return true;
if (!player->IsSettingFinish())
return false;
BotFieldAI* pFieldAI = dynamic_cast<BotFieldAI*>(player->GetAI());
if (!pFieldAI)
return true;
if (pFieldAI->HasTeleport())
return false;
return true;
}
void PlayerBotSession::ProcessNoWorld(uint32 diff)
{
if (PlayerLoading())
return;
Player* player = GetPlayer();
if (!player)
return;
// Un client reel confirme le changement de monde par MSG_MOVE_WORLDPORT_ACK ;
// sans cette simulation, un bot en jeu vise par .tele reste bloque sur le semaphore.
if (player->IsBeingTeleportedFar())
{
HandleMoveWorldportAck();
m_NoWorldTick = 500;
return;
}
// idem pour un teleport proche (meme map) : un client reel repond CMSG_MOVE_TELEPORT_ACK
if (player->IsBeingTeleportedNear())
{
WorldPacket data(CMSG_MOVE_TELEPORT_ACK);
WorldPackets::Movement::MoveTeleportAck ack(std::move(data));
ack.MoverGUID = player->GetGUID();
HandleMoveTeleportAck(ack);
m_NoWorldTick = 500;
return;
}
if (player->IsInWorld())
{
m_NoWorldTick = 0;
return;
}
if (m_NoWorldTick == 0)
{
m_NoWorldTick = 2000;
return;
}
m_NoWorldTick -= int32(diff);
if (m_NoWorldTick > 0)
return;
if (BotGroupAI* pGroupAI = dynamic_cast<BotGroupAI*>(player->GetAI()))
{
if (pGroupAI->HasTeleport())
pGroupAI->UpdateTeleport(diff);
else
pGroupAI->SetTeleportToMaster();
m_NoWorldTick = 500;
}
else if (BotBGAI* pGroupAI = dynamic_cast<BotBGAI*>(player->GetAI()))
{
if (player->InBattleground())
{
PlayerBotMgr::SwitchPlayerBotAI(player, PlayerBotAIType::PBAIT_FIELD, true);
WorldPacket opcode(CMSG_BATTLEFIELD_LEAVE);
WorldPackets::Battleground::BattlefieldLeave battlefieldLeave(std::move(opcode));
HandleBattlefieldLeaveOpcode(battlefieldLeave);
}
HandleMoveWorldportAck();
m_NoWorldTick = 500;
}
else// if (BotFieldAI* pGroupAI = dynamic_cast<BotFieldAI*>(player->GetAI()))
{
HandleMoveWorldportAck();
m_NoWorldTick = 500;
}
}
void PlayerBotSession::CastSchedule(uint32 diff)
{
if (m_Schedules.empty())
return;
bool result = false;
BotGlobleSchedule& schedule = *m_Schedules.begin();
schedule.processTick += diff;
if (schedule.processTick > 60000 * 3)
{
m_Schedules.erase(m_Schedules.begin());
return;
}
if (!PlayerIsReady())
return;
switch (schedule.bbgType)
{
case BGSType_Online:
result = ProcessOnline(schedule);
break;
case BGSType_Online_GUID:
result = ProcessOnlineByGUID(schedule);
TC_LOG_ERROR("server.worldserver", "QA-BOTLOG: ProcessOnlineByGUID resultat=%d", (int)result); // QA-BOTLOG
break;
case BGSType_Offline:
result = ProcessOffline(schedule);
break;
case BGSType_Settting:
result = ProcessSetting(schedule);
break;
case BGSType_InBGQueue:
result = ProcessInBGQueue(schedule);
break;
case BGSType_OutBGQueue:
result = ProcessOutBGQueue(schedule);
break;
case BGSType_EnterBG:
result = ProcessEnterBG(schedule);
break;
case BGSType_LeaveBG:
result = ProcessLeaveBG(schedule);
break;
case BGSType_InAAQueue:
result = ProcessInAAQueue(schedule);
break;
case BGSType_OutAAQueue:
result = ProcessOutAAQueue(schedule);
break;
case BGSType_EnterAA:
result = ProcessEnterAA(schedule);
break;
case BGSType_LeaveAA:
result = ProcessLeaveAA(schedule);
break;
case BGSType_DelayLevelup:
result = ProcessDelayLevelup(schedule);
break;
case BGSType_InLFGQueue:
result = ProcessInLFGQueue(schedule);
break;
case BGSType_OutLFGQueue:
result = ProcessOutLFGQueue(schedule);
break;
case BGSType_AcceptLFGProposal:
result = ProcessAcceptLFGProposal(schedule);
break;
case BGSType_OfferPetitionSign:
result = ProcessOfferPetitionSign(schedule);
break;
default:
result = true;
break;
}
if (result)
{
m_Schedules.erase(m_Schedules.begin());
}
}
bool PlayerBotSession::ProcessOnline(BotGlobleSchedule& schedule)
{
if (schedule.parameter1 <= 0)
return true;
if (PlayerLoading())
return false;
if (GetPlayer())
return true;
PlayerBotBaseInfo* pInfo = sPlayerBotMgr->GetPlayerBotAccountInfo(GetAccountId());
if (!pInfo)
{
pInfo = sPlayerBotMgr->GetAccountBotAccountInfo(GetAccountId());
if (!pInfo)
{
ClearAllSchedule();
return false;
}
}
bool fuction = true;
if (schedule.parameter1 > 1)
fuction = false;
PlayerBotCharBaseInfo& charInfo = (schedule.parameter2 == 0) ? pInfo->GetRandomCharacterByFuction(fuction) : pInfo->GetCharacter(fuction, schedule.parameter2);
if (charInfo.guid == 0)
return true;
WorldPacket _worldPacket(CMSG_PLAYER_LOGIN);
WorldPackets::Character::PlayerLogin cmd(std::move(_worldPacket));
cmd.Guid = ObjectGuid::Create<HighGuid::Player>(charInfo.guid);
cmd.FarClip = 0.0f;
HandlePlayerLoginOpcode(cmd);
HandleContinuePlayerLogin();
return false;
}
bool PlayerBotSession::ProcessOnlineByGUID(BotGlobleSchedule& schedule)
{
if (schedule.playerGUID <= 0)
return true;
if (PlayerLoading())
return false;
if (GetPlayer())
return true;
WorldPacket _worldPacket(CMSG_PLAYER_LOGIN);
WorldPackets::Character::PlayerLogin cmd(std::move(_worldPacket));
cmd.Guid = schedule.playerGUID;
cmd.FarClip = 0.0f;
HandlePlayerLoginOpcode(cmd);
HandleContinuePlayerLogin();
return false;
}
bool PlayerBotSession::ProcessOffline(BotGlobleSchedule& schedule)
{
if (PlayerLoading())
return false;
Player* player = GetPlayer();
if (!player)
return true;
if (schedule.scheduleState > 0)
return false;
WorldPackets::Character::LogoutRequest logoutReq(time(NULL) - 18);
//LogoutPlayer(false);
schedule.scheduleState = 1;
return false;
}
bool PlayerBotSession::ProcessSetting(BotGlobleSchedule& schedule)
{
if (schedule.parameter3 == 0 || schedule.parameter3 > 4)
schedule.parameter3 = 4;
if (schedule.parameter2 > 110 || schedule.parameter2 == 0)
schedule.parameter2 = 110;
if (schedule.parameter1 > schedule.parameter2)
schedule.parameter2 = schedule.parameter1;
if (PlayerLoading())
return false;
Player* player = GetPlayer();
if (!player)
{
ClearAllSchedule();
return false;
}
if (!player->IsInWorld() || !player->IsSettingFinish())
return false;
if (schedule.scheduleState != 0)
return true;
bool needTenacity = (schedule.parameter4 != 0) ? true : false;
if (needTenacity)
needTenacity = player->CheckNeedTenacityFlush();
if (!needTenacity && player->CalculateTalentsTiers() < 10)
{
if (player->IsSettingFinish() && player->getLevel() >= schedule.parameter1 && player->getLevel() <= schedule.parameter2)
{
// SylvaniaCore : le raccourci ne vaut que si le bot a reellement ses
// talents. Sans ce controle, un bot deja au bon niveau et a la bonne
// specialisation sautait tout le re-level et restait avec zero talent,
// heritage de l epoque ou LearnTalents() etait un corps vide.
PlayerTalentMap const* talents = player->GetTalentMap(player->GetActiveTalentGroup());
bool const hasTalents = talents && talents->size() >= player->CalculateTalentsTiers();
// SIGNALE EN JEU : « Kaerbrus n a meme pas d equipement ».
// Constate en base : niveau 110, 76 sorts, ZERO piece portee.
//
// Ce raccourci evite un re-level complet a un bot deja au bon
// niveau, avec ses talents et la bonne specialisation. Mais il
// saute du meme coup les etapes 6 a 10 de UpdateReset(), qui sont
// justement l habillage. Un mercenaire ayant perdu son equipement
// -- contrat precedent interrompu, re-level avorte -- ne le
// retrouvait donc JAMAIS : a chaque embauche, le raccourci
// concluait que tout allait bien et le renvoyait nu.
//
// On verifie donc qu il est reellement habille avant de couper.
// Dix pieces sur les dix-neuf emplacements : de quoi distinguer un
// bot equipe d un bot depouille, sans exiger un sans-faute (la
// seconde babiole et l arme de jet manquent souvent, legitimement).
uint32 piecesPortees = 0;
for (uint8 emplacement = EQUIPMENT_SLOT_START; emplacement < EQUIPMENT_SLOT_END; ++emplacement)
if (player->GetItemByPos(INVENTORY_SLOT_BAG_0, emplacement))
++piecesPortees;
bool const correctementEquipe = (piecesPortees >= 10);
if (hasTalents && correctementEquipe &&
(schedule.parameter3 >= 4 || (player->FindTalentType() + 1 == schedule.parameter3)))
return true;
}
}
uint32 flushTalent = (schedule.parameter3 > 0 && schedule.parameter3 < 4) ? schedule.parameter3 - 1 : 3;
player->ResetPlayerToLevel(schedule.parameter2, flushTalent, needTenacity);
schedule.scheduleState = 1;
return false;
}
bool PlayerBotSession::ProcessInBGQueue(BotGlobleSchedule& schedule)
{
if (PlayerLoading())
return false;
Player* player = GetPlayer();
if (!player)
{
ClearAllSchedule();
return false;
}
if (!player->IsInWorld())
return false;
if (player->InBattlegroundQueue())
return true;
if (player->InBattleground() || player->InArena() || player->GetMap()->IsDungeon())
{
ClearAllSchedule();
return false;
}
Battleground* bg = sBattlegroundMgr->GetBattlegroundTemplate(BattlegroundTypeId(schedule.parameter1));
if (!bg)
{
ClearAllSchedule();
return false;
}
PVPDifficultyEntry const* bracketEntry = DB2Manager::GetBattlegroundBracketByLevel(bg->GetMapId(), player->getLevel());
if (!bracketEntry)
{
ClearAllSchedule();
return false;
}
WorldPacket cmd(CMSG_BATTLEMASTER_JOIN);
WorldPackets::Battleground::BattlemasterJoin packet(std::move(cmd));
// SylvaniaCore (module BG BotFill): le QueueID n etait jamais renseigne (ligne commentee
// du portage) -> le handler recevait bgtype 0 et rejetait toutes les inscriptions bots
packet.QueueID = uint64(schedule.parameter1);
HandleBattlemasterJoinOpcode(packet);
return false;
}
bool PlayerBotSession::ProcessOutBGQueue(BotGlobleSchedule& schedule)
{
if (PlayerLoading())
return false;
Player* player = GetPlayer();
if (!player)
{
ClearAllSchedule();
return false;
}
if (!player->IsInWorld() || player->InBattleground() || player->InArena() || !player->InBattlegroundQueue() || player->GetMap()->IsDungeon())
return true;
LogoutPlayer(false);
return false;
}
bool PlayerBotSession::ProcessEnterBG(BotGlobleSchedule& schedule)
{
if (PlayerLoading())
return false;
Player* player = GetPlayer();
if (!player)
{
ClearAllSchedule();
return false;
}
if (!player->IsInWorld())
return false;
if (player->IsInCombat())
player->CombatStop(true);
if (player->GetMap()->IsDungeon())
{
BotGlobleSchedule schedule1(BotGlobleScheduleType::BGSType_OutBGQueue, 0);
schedule1.parameter1 = schedule.parameter1;
ClearAllSchedule();
PushScheduleToQueue(schedule1);
return false;
}
if (player->InBattleground() || player->InArena())
return true;
if (player->InBattlegroundQueue())
{
for (uint8 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
{
BattlegroundQueueTypeId bgQueueTypeId = player->GetBattlegroundQueueTypeId(i);
if (!bgQueueTypeId)
continue;
if (player->IsInvitedForBattlegroundQueueType(bgQueueTypeId))
{
PlayerBotMgr::SwitchPlayerBotAI(player, PlayerBotAIType::PBAIT_BG, true);
WorldPacket cmd(CMSG_BATTLEFIELD_PORT);
WorldPackets::Battleground::BattlefieldPort packet(std::move(cmd));
// SylvaniaCore (module BG BotFill): le handler attend le slot de file
packet.Ticket.Id = i;
packet.Ticket.RequesterGuid = player->GetGUID();
packet.Ticket.Type = WorldPackets::LFG::RideType::Battlegrounds;
packet.Ticket.Time = time(0);
packet.AcceptedInvite = true;
HandleBattleFieldPortOpcode(packet);
//HandleWorldPortAck();
break;
}
}
}
return false;
}
bool PlayerBotSession::ProcessLeaveBG(BotGlobleSchedule& schedule)
{
if (PlayerLoading())
return false;
Player* player = GetPlayer();
if (!player)
{
ClearAllSchedule();
return false;
}
if (!player->InBattleground())
return true;
PlayerBotMgr::SwitchPlayerBotAI(player, PlayerBotAIType::PBAIT_FIELD, true);
WorldPacket opcode(CMSG_BATTLEFIELD_LEAVE);
WorldPackets::Battleground::BattlefieldLeave leave(std::move(opcode));
HandleBattlefieldLeaveOpcode(leave);
//HandleWorldPortAck();
return false;
}
bool PlayerBotSession::ProcessInAAQueue(BotGlobleSchedule& schedule)
{
if (PlayerLoading())
return false;
Player* player = GetPlayer();
if (!player)
{
ClearAllSchedule();
return false;
}
if (!player->IsInWorld())
return false;
if (player->InBattlegroundQueue())
return true;
if (player->HasAura(26013) || player->InBattleground() || player->InArena() || player->GetMap()->IsDungeon())
{
ClearAllSchedule();
return false;
}
WorldPacket packet(CMSG_BATTLEMASTER_JOIN_ARENA);
WorldPackets::Battleground::BattlemasterJoinArena cmd(std::move(packet));
//cmd.RolesMask = schedule.parameter1;
//cmd.Bracket = schedule.parameter2;
HandleBattlemasterJoinArena(cmd);
return false;
}
bool PlayerBotSession::ProcessOutAAQueue(BotGlobleSchedule& schedule)
{
if (PlayerLoading())
return false;
Player* player = GetPlayer();
if (!player)
{
ClearAllSchedule();
return false;
}
if (!player->IsInWorld() || player->InBattleground() || player->InArena() || !player->InBattlegroundQueue() || player->GetMap()->IsDungeon())
return true;
LogoutPlayer(false);
return true;
}
bool PlayerBotSession::ProcessEnterAA(BotGlobleSchedule& schedule)
{
if (PlayerLoading())
return false;
Player* player = GetPlayer();
if (!player)
{
ClearAllSchedule();
return false;
}
if (!player->IsInWorld())
return false;
if (player->GetMap()->IsDungeon())
{
BotGlobleSchedule schedule1(BotGlobleScheduleType::BGSType_OutAAQueue, 0);
schedule1.parameter1 = schedule.parameter1;
schedule1.parameter2 = schedule.parameter2;
schedule1.parameter3 = schedule.parameter3;
ClearAllSchedule();
PushScheduleToQueue(schedule1);
return false;
}
if (player->InBattleground() || player->InArena())
return true;
if (player->InBattlegroundQueue())
{
for (uint8 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
{
BattlegroundQueueTypeId bgQueueTypeId = player->GetBattlegroundQueueTypeId(i);
if (!bgQueueTypeId)
continue;
if (player->IsInvitedForBattlegroundQueueType(bgQueueTypeId))
{
PlayerBotMgr::SwitchPlayerBotAI(player, PlayerBotAIType::PBAIT_ARENA, true);
WorldPacket cmd(CMSG_BATTLEFIELD_PORT);
WorldPackets::Battleground::BattlefieldPort packet(std::move(cmd));
packet.Ticket.Id = schedule.parameter1;
packet.Ticket.RequesterGuid = schedule.playerGUID;
packet.Ticket.Type = WorldPackets::LFG::RideType::Battlegrounds;
packet.Ticket.Time = time(0);
packet.AcceptedInvite = true;
HandleBattleFieldPortOpcode(packet);
break;
}
else
{
Battleground* bg_template = sBattlegroundMgr->GetBattlegroundTemplate(BattlegroundTypeId(schedule.parameter1));
if (!bg_template)
continue;
BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId);
PVPDifficultyEntry const* bracketEntry = DB2Manager::GetBattlegroundBracketById(bg_template->GetMapId(), BattlegroundBracketId(schedule.parameter2));
if (bgQueue.ExistRealPlayer(bracketEntry, (schedule.parameter4 != 0) ? true : false))
continue;
BotGlobleSchedule schedule1(BotGlobleScheduleType::BGSType_OutAAQueue, 0);
schedule1.parameter1 = schedule.parameter1;
schedule1.parameter2 = schedule.parameter2;
schedule1.parameter3 = schedule.parameter3;
ClearAllSchedule();
PushScheduleToQueue(schedule1);
return false;
}
}
}
return false;
}
bool PlayerBotSession::ProcessLeaveAA(BotGlobleSchedule& schedule)
{
if (PlayerLoading())
return false;
Player* player = GetPlayer();
if (!player)
{
ClearAllSchedule();
return false;
}
if (!player->InBattleground())
return true;
PlayerBotMgr::SwitchPlayerBotAI(player, PlayerBotAIType::PBAIT_FIELD, true);
//uint8 un8 = 0;
//uint16 un16 = 0;
//uint32 un32 = 0;
//WorldPacket opcode(1);
//opcode << un8;
//opcode << un8;
//opcode << un32;
//opcode << un16;
//HandleBattlefieldLeaveOpcode(opcode);
//HandleMoveWorldportAckOpcode();
WorldPacket opcode(CMSG_BATTLEFIELD_LEAVE);
WorldPackets::Battleground::BattlefieldLeave leave(std::move(opcode));
HandleBattlefieldLeaveOpcode(leave);
//HandleWorldPortAck();
return false;
}
bool PlayerBotSession::ProcessDelayLevelup(BotGlobleSchedule& schedule)
{
if (PlayerLoading())
return false;
Player* player = GetPlayer();
if (!player)
{
ClearAllSchedule();
return false;
}
// Le rehabillage retire puis rend tout l equipement : hors de question de
// desarmer le bot en pleine bagarre. Renvoyer false laisse le schedule en
// tete de file, il sera retente au tick suivant.
if (player->IsInCombat())
return false;
player->OnLevelupToBotAI();
return true;
}
bool PlayerBotSession::ProcessInLFGQueue(BotGlobleSchedule& schedule)
{
if (PlayerLoading())
return false;
Player* player = GetPlayer();
if (!player)
{
ClearAllSchedule();
return false;
}
if (player->isUsingLfg())
return true;
if (schedule.parameter1 != 2 && schedule.parameter1 != 4 && schedule.parameter1 != 8)
return true;
if (schedule.parameter2 > 3 || schedule.parameter2 == 0)
return true;
WorldPacket packet(CMSG_DF_JOIN, 50);
packet << schedule.parameter1;
packet << uint16(0);
packet << uint8(schedule.parameter2);
if (schedule.parameter2 >= 1)
packet << schedule.parameter3;
if (schedule.parameter2 >= 2)
packet << schedule.parameter4;
if (schedule.parameter2 >= 3)
packet << schedule.parameter5;
packet << uint32(0);
packet << "";
WorldPackets::LFG::DFJoin joinPacket(std::move(packet));
HandleLfgJoinOpcode(joinPacket);
return true;
}
bool PlayerBotSession::ProcessOutLFGQueue(BotGlobleSchedule& schedule)
{
if (PlayerLoading())
return false;
Player* player = GetPlayer();
if (!player)
{
ClearAllSchedule();
return false;
}
if (!player->isUsingLfg())
return true;
WorldPacket packet(CMSG_DF_LEAVE, 0);
WorldPackets::LFG::DFLeave leavePacket(std::move(packet));
HandleLfgLeaveOpcode(leavePacket);
return true;
}
bool PlayerBotSession::ProcessAcceptLFGProposal(BotGlobleSchedule& schedule)
{
if (PlayerLoading())
return false;
if (schedule.parameter1 == 0)
return true;
Player* player = GetPlayer();
if (!player)
{
ClearAllSchedule();
return false;
}
if (!player->isUsingLfg())
return true;
sLFGMgr->UpdateProposal(schedule.parameter1, player->GetGUID(), (schedule.parameter2 != 0) ? true : false);
return true;
}
bool PlayerBotSession::ProcessOfferPetitionSign(BotGlobleSchedule& schedule)
{
if (PlayerLoading())
return false;
if (schedule.parameter1 == 0 && schedule.parameter2 == 0)
return true;
Player* player = GetPlayer();
if (!player)
{
ClearAllSchedule();
return false;
}
uint64 signGUID = uint64(schedule.parameter1);
uint64 highID = uint64(schedule.parameter2) << 32;
signGUID |= highID;
WorldPacket packet(CMSG_SIGN_PETITION, 0);
packet << signGUID;
packet << uint8(1);
WorldPackets::Petition::SignPetition signPacket(std::move(packet));
HandleSignPetition(signPacket);
return true;
}