/* * 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 . */ #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 lock(m_optQueueLock); CastSchedule(CAST_SCHEDULE_TICK); m_LastCastTime = CAST_SCHEDULE_TICK; } return updateResult; } void PlayerBotSession::PushScheduleToQueue(BotGlobleSchedule& schedule) { //std::lock_guard 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(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(player->GetAI())) { if (pGroupAI->HasTeleport()) pGroupAI->UpdateTeleport(diff); else pGroupAI->SetTeleportToMaster(); m_NoWorldTick = 500; } else if (BotBGAI* pGroupAI = dynamic_cast(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(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(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; }