Core: Change Handlers location
This commit is contained in:
Executable
+142
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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 "zlib.h"
|
||||
#include "AddonHandler.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Log.h"
|
||||
|
||||
AddonHandler::AddonHandler()
|
||||
{
|
||||
}
|
||||
|
||||
AddonHandler::~AddonHandler()
|
||||
{
|
||||
}
|
||||
|
||||
bool AddonHandler::BuildAddonPacket(WorldPacket* Source, WorldPacket* Target)
|
||||
{
|
||||
ByteBuffer AddOnPacked;
|
||||
uLongf AddonRealSize;
|
||||
uint32 CurrentPosition;
|
||||
uint32 TempValue;
|
||||
|
||||
unsigned char tdata[256] =
|
||||
{
|
||||
0xC3, 0x5B, 0x50, 0x84, 0xB9, 0x3E, 0x32, 0x42, 0x8C, 0xD0, 0xC7, 0x48, 0xFA, 0x0E, 0x5D, 0x54,
|
||||
0x5A, 0xA3, 0x0E, 0x14, 0xBA, 0x9E, 0x0D, 0xB9, 0x5D, 0x8B, 0xEE, 0xB6, 0x84, 0x93, 0x45, 0x75,
|
||||
0xFF, 0x31, 0xFE, 0x2F, 0x64, 0x3F, 0x3D, 0x6D, 0x07, 0xD9, 0x44, 0x9B, 0x40, 0x85, 0x59, 0x34,
|
||||
0x4E, 0x10, 0xE1, 0xE7, 0x43, 0x69, 0xEF, 0x7C, 0x16, 0xFC, 0xB4, 0xED, 0x1B, 0x95, 0x28, 0xA8,
|
||||
0x23, 0x76, 0x51, 0x31, 0x57, 0x30, 0x2B, 0x79, 0x08, 0x50, 0x10, 0x1C, 0x4A, 0x1A, 0x2C, 0xC8,
|
||||
0x8B, 0x8F, 0x05, 0x2D, 0x22, 0x3D, 0xDB, 0x5A, 0x24, 0x7A, 0x0F, 0x13, 0x50, 0x37, 0x8F, 0x5A,
|
||||
0xCC, 0x9E, 0x04, 0x44, 0x0E, 0x87, 0x01, 0xD4, 0xA3, 0x15, 0x94, 0x16, 0x34, 0xC6, 0xC2, 0xC3,
|
||||
0xFB, 0x49, 0xFE, 0xE1, 0xF9, 0xDA, 0x8C, 0x50, 0x3C, 0xBE, 0x2C, 0xBB, 0x57, 0xED, 0x46, 0xB9,
|
||||
0xAD, 0x8B, 0xC6, 0xDF, 0x0E, 0xD6, 0x0F, 0xBE, 0x80, 0xB3, 0x8B, 0x1E, 0x77, 0xCF, 0xAD, 0x22,
|
||||
0xCF, 0xB7, 0x4B, 0xCF, 0xFB, 0xF0, 0x6B, 0x11, 0x45, 0x2D, 0x7A, 0x81, 0x18, 0xF2, 0x92, 0x7E,
|
||||
0x98, 0x56, 0x5D, 0x5E, 0x69, 0x72, 0x0A, 0x0D, 0x03, 0x0A, 0x85, 0xA2, 0x85, 0x9C, 0xCB, 0xFB,
|
||||
0x56, 0x6E, 0x8F, 0x44, 0xBB, 0x8F, 0x02, 0x22, 0x68, 0x63, 0x97, 0xBC, 0x85, 0xBA, 0xA8, 0xF7,
|
||||
0xB5, 0x40, 0x68, 0x3C, 0x77, 0x86, 0x6F, 0x4B, 0xD7, 0x88, 0xCA, 0x8A, 0xD7, 0xCE, 0x36, 0xF0,
|
||||
0x45, 0x6E, 0xD5, 0x64, 0x79, 0x0F, 0x17, 0xFC, 0x64, 0xDD, 0x10, 0x6F, 0xF3, 0xF5, 0xE0, 0xA6,
|
||||
0xC3, 0xFB, 0x1B, 0x8C, 0x29, 0xEF, 0x8E, 0xE5, 0x34, 0xCB, 0xD1, 0x2A, 0xCE, 0x79, 0xC3, 0x9A,
|
||||
0x0D, 0x36, 0xEA, 0x01, 0xE0, 0xAA, 0x91, 0x20, 0x54, 0xF0, 0x72, 0xD8, 0x1E, 0xC7, 0x89, 0xD2
|
||||
};
|
||||
|
||||
// broken addon packet, can't be received from real client
|
||||
if (Source->rpos() + 4 > Source->size())
|
||||
return false;
|
||||
|
||||
*Source >> TempValue; // get real size of the packed structure
|
||||
|
||||
// empty addon packet, nothing process, can't be received from real client
|
||||
if (!TempValue)
|
||||
return false;
|
||||
|
||||
AddonRealSize = TempValue; // temp value because ZLIB only excepts uLongf
|
||||
|
||||
CurrentPosition = Source->rpos(); // get the position of the pointer in the structure
|
||||
|
||||
AddOnPacked.resize(AddonRealSize); // resize target for zlib action
|
||||
|
||||
if (!uncompress(const_cast<uint8*>(AddOnPacked.contents()), &AddonRealSize, const_cast<uint8*>((*Source).contents() + CurrentPosition), (*Source).size() - CurrentPosition)!= Z_OK)
|
||||
{
|
||||
Target->Initialize(SMSG_ADDON_INFO);
|
||||
|
||||
uint32 addonsCount;
|
||||
AddOnPacked >> addonsCount; // addons count?
|
||||
|
||||
for (uint32 i = 0; i < addonsCount; ++i)
|
||||
{
|
||||
std::string addonName;
|
||||
uint8 enabled;
|
||||
uint32 crc, unk2;
|
||||
|
||||
// check next addon data format correctness
|
||||
if (AddOnPacked.rpos()+1 > AddOnPacked.size())
|
||||
return false;
|
||||
|
||||
AddOnPacked >> addonName;
|
||||
|
||||
// recheck next addon data format correctness
|
||||
if (AddOnPacked.rpos()+1+4+4 > AddOnPacked.size())
|
||||
return false;
|
||||
|
||||
AddOnPacked >> enabled >> crc >> unk2;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "ADDON: Name: %s, Enabled: 0x%x, CRC: 0x%x, Unknown2: 0x%x", addonName.c_str(), enabled, crc, unk2);
|
||||
|
||||
uint8 state = (enabled ? 2 : 1);
|
||||
*Target << uint8(state);
|
||||
|
||||
uint8 unk1 = (enabled ? 1 : 0);
|
||||
*Target << uint8(unk1);
|
||||
if (unk1)
|
||||
{
|
||||
uint8 unk = (crc != 0x4c1c776d); // If addon is Standard addon CRC
|
||||
*Target << uint8(unk);
|
||||
if (unk)
|
||||
Target->append(tdata, sizeof(tdata));
|
||||
|
||||
*Target << uint32(0);
|
||||
}
|
||||
|
||||
uint8 unk3 = (enabled ? 0 : 1);
|
||||
*Target << uint8(unk3);
|
||||
if (unk3)
|
||||
{
|
||||
// String, 256 (null terminated?)
|
||||
*Target << uint8(0);
|
||||
}
|
||||
}
|
||||
|
||||
uint32 unk4;
|
||||
AddOnPacked >> unk4;
|
||||
|
||||
uint32 count = 0;
|
||||
*Target << uint32(count);
|
||||
|
||||
if (AddOnPacked.rpos() != AddOnPacked.size())
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "packet under read!");
|
||||
}
|
||||
else
|
||||
{
|
||||
sLog->outError("Addon packet uncompress error :(");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef __ADDONHANDLER_H
|
||||
#define __ADDONHANDLER_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "Config.h"
|
||||
#include <ace/Singleton.h>
|
||||
#include "WorldPacket.h"
|
||||
|
||||
class AddonHandler
|
||||
{
|
||||
/* Construction */
|
||||
friend class ACE_Singleton<AddonHandler, ACE_Null_Mutex>;
|
||||
AddonHandler();
|
||||
|
||||
public:
|
||||
~AddonHandler();
|
||||
//build addon packet
|
||||
bool BuildAddonPacket(WorldPacket* Source, WorldPacket* Target);
|
||||
};
|
||||
#define sAddOnHandler ACE_Singleton<AddonHandler, ACE_Null_Mutex>::instance()
|
||||
#endif
|
||||
|
||||
+408
@@ -0,0 +1,408 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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 "Player.h"
|
||||
#include "World.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
#include "DatabaseEnv.h"
|
||||
|
||||
#include "ArenaTeam.h"
|
||||
#include "Log.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "SocialMgr.h"
|
||||
#include "ArenaTeamMgr.h"
|
||||
|
||||
void WorldSession::HandleInspectArenaTeamsOpcode(WorldPacket & recvData)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "MSG_INSPECT_ARENA_TEAMS");
|
||||
|
||||
uint64 guid;
|
||||
recvData >> guid;
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Inspect Arena stats (GUID: %u TypeId: %u)", GUID_LOPART(guid), GuidHigh2TypeId(GUID_HIPART(guid)));
|
||||
|
||||
if (Player* player = ObjectAccessor::FindPlayer(guid))
|
||||
{
|
||||
for (uint8 i = 0; i < MAX_ARENA_SLOT; ++i)
|
||||
{
|
||||
if (uint32 a_id = player->GetArenaTeamId(i))
|
||||
{
|
||||
if (ArenaTeam* arenaTeam = sArenaTeamMgr->GetArenaTeamById(a_id))
|
||||
arenaTeam->Inspect(this, player->GetGUID());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleArenaTeamQueryOpcode(WorldPacket & recvData)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_ARENA_TEAM_QUERY");
|
||||
|
||||
uint32 arenaTeamId;
|
||||
recvData >> arenaTeamId;
|
||||
|
||||
if (ArenaTeam* arenaTeam = sArenaTeamMgr->GetArenaTeamById(arenaTeamId))
|
||||
{
|
||||
arenaTeam->Query(this);
|
||||
arenaTeam->SendStats(this);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleArenaTeamRosterOpcode(WorldPacket & recvData)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_ARENA_TEAM_ROSTER");
|
||||
|
||||
uint32 arenaTeamId; // arena team id
|
||||
recvData >> arenaTeamId;
|
||||
|
||||
if (ArenaTeam* arenaTeam = sArenaTeamMgr->GetArenaTeamById(arenaTeamId))
|
||||
arenaTeam->Roster(this);
|
||||
}
|
||||
|
||||
void WorldSession::HandleArenaTeamInviteOpcode(WorldPacket & recvData)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_ARENA_TEAM_INVITE");
|
||||
|
||||
uint32 arenaTeamId; // arena team id
|
||||
std::string invitedName;
|
||||
|
||||
Player* player = NULL;
|
||||
|
||||
recvData >> arenaTeamId >> invitedName;
|
||||
|
||||
if (!invitedName.empty())
|
||||
{
|
||||
if (!normalizePlayerName(invitedName))
|
||||
return;
|
||||
|
||||
player = sObjectAccessor->FindPlayerByName(invitedName.c_str());
|
||||
}
|
||||
|
||||
if (!player)
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", invitedName, ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S);
|
||||
return;
|
||||
}
|
||||
|
||||
if (player->getLevel() < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", player->GetName(), ERR_ARENA_TEAM_TARGET_TOO_LOW_S);
|
||||
return;
|
||||
}
|
||||
|
||||
ArenaTeam* arenaTeam = sArenaTeamMgr->GetArenaTeamById(arenaTeamId);
|
||||
if (!arenaTeam)
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM);
|
||||
return;
|
||||
}
|
||||
|
||||
// OK result but don't send invite
|
||||
if (player->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow()))
|
||||
return;
|
||||
|
||||
if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && player->GetTeam() != GetPlayer()->GetTeam())
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (player->GetArenaTeamId(arenaTeam->GetSlot()))
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", player->GetName(), ERR_ALREADY_IN_ARENA_TEAM_S);
|
||||
return;
|
||||
}
|
||||
|
||||
if (player->GetArenaTeamIdInvited())
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", player->GetName(), ERR_ALREADY_INVITED_TO_ARENA_TEAM_S);
|
||||
return;
|
||||
}
|
||||
|
||||
if (arenaTeam->GetMembersSize() >= arenaTeam->GetType() * 2)
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, arenaTeam->GetName(), "", ERR_ARENA_TEAM_TOO_MANY_MEMBERS_S);
|
||||
return;
|
||||
}
|
||||
|
||||
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "Player %s Invited %s to Join his ArenaTeam", GetPlayer()->GetName(), invitedName.c_str());
|
||||
|
||||
player->SetArenaTeamIdInvited(arenaTeam->GetId());
|
||||
|
||||
WorldPacket data(SMSG_ARENA_TEAM_INVITE, (8+10));
|
||||
data << GetPlayer()->GetName();
|
||||
data << arenaTeam->GetName();
|
||||
player->GetSession()->SendPacket(&data);
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_ARENA_TEAM_INVITE");
|
||||
}
|
||||
|
||||
void WorldSession::HandleArenaTeamAcceptOpcode(WorldPacket & /*recv_data*/)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_ARENA_TEAM_ACCEPT"); // empty opcode
|
||||
|
||||
ArenaTeam* arenaTeam = sArenaTeamMgr->GetArenaTeamById(_player->GetArenaTeamIdInvited());
|
||||
if (!arenaTeam)
|
||||
return;
|
||||
|
||||
// Check if player is already in another team of the same size
|
||||
if (_player->GetArenaTeamId(arenaTeam->GetSlot()))
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ALREADY_IN_ARENA_TEAM);
|
||||
return;
|
||||
}
|
||||
|
||||
// Only allow members of the other faction to join the team if cross faction interaction is enabled
|
||||
if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && _player->GetTeam() != sObjectMgr->GetPlayerTeamByGUID(arenaTeam->GetCaptain()))
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_NOT_ALLIED);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add player to team
|
||||
if (!arenaTeam->AddMember(_player->GetGUID()))
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_INTERNAL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Broadcast event
|
||||
arenaTeam->BroadcastEvent(ERR_ARENA_TEAM_JOIN_SS, _player->GetGUID(), 2, _player->GetName(), arenaTeam->GetName(), "");
|
||||
}
|
||||
|
||||
void WorldSession::HandleArenaTeamDeclineOpcode(WorldPacket & /*recv_data*/)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_ARENA_TEAM_DECLINE"); // empty opcode
|
||||
|
||||
// Remove invite from player
|
||||
_player->SetArenaTeamIdInvited(0);
|
||||
}
|
||||
|
||||
void WorldSession::HandleArenaTeamLeaveOpcode(WorldPacket & recvData)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_ARENA_TEAM_LEAVE");
|
||||
|
||||
uint32 arenaTeamId;
|
||||
recvData >> arenaTeamId;
|
||||
|
||||
ArenaTeam* arenaTeam = sArenaTeamMgr->GetArenaTeamById(arenaTeamId);
|
||||
if (!arenaTeam)
|
||||
return;
|
||||
|
||||
// Disallow leave team while in arena
|
||||
if (_player->InArena())
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, "", "", ERR_ARENA_TEAM_INTERNAL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Team captain can't leave the team if other members are still present
|
||||
if (_player->GetGUID() == arenaTeam->GetCaptain() && arenaTeam->GetMembersSize() > 1)
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, "", "", ERR_ARENA_TEAM_LEADER_LEAVE_S);
|
||||
return;
|
||||
}
|
||||
|
||||
// If team consists only of the captain, disband the team
|
||||
if (_player->GetGUID() == arenaTeam->GetCaptain())
|
||||
{
|
||||
arenaTeam->Disband(this);
|
||||
delete arenaTeam;
|
||||
return;
|
||||
}
|
||||
else
|
||||
arenaTeam->DelMember(_player->GetGUID(), true);
|
||||
|
||||
// Broadcast event
|
||||
arenaTeam->BroadcastEvent(ERR_ARENA_TEAM_LEAVE_SS, _player->GetGUID(), 2, _player->GetName(), arenaTeam->GetName(), "");
|
||||
|
||||
// Inform player who left
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, arenaTeam->GetName(), "", 0);
|
||||
}
|
||||
|
||||
void WorldSession::HandleArenaTeamDisbandOpcode(WorldPacket & recvData)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_ARENA_TEAM_DISBAND");
|
||||
|
||||
uint32 arenaTeamId;
|
||||
recvData >> arenaTeamId;
|
||||
|
||||
if (ArenaTeam* arenaTeam = sArenaTeamMgr->GetArenaTeamById(arenaTeamId))
|
||||
{
|
||||
// Only captain can disband the team
|
||||
if (arenaTeam->GetCaptain() != _player->GetGUID())
|
||||
return;
|
||||
|
||||
// Teams cannot be disbanded during fights
|
||||
if (arenaTeam->IsFighting())
|
||||
return;
|
||||
|
||||
arenaTeam->Disband(this);
|
||||
delete arenaTeam;
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleArenaTeamRemoveOpcode(WorldPacket & recvData)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_ARENA_TEAM_REMOVE");
|
||||
|
||||
uint32 arenaTeamId;
|
||||
std::string name;
|
||||
|
||||
recvData >> arenaTeamId;
|
||||
recvData >> name;
|
||||
|
||||
// Check for valid arena team
|
||||
ArenaTeam* arenaTeam = sArenaTeamMgr->GetArenaTeamById(arenaTeamId);
|
||||
if (!arenaTeam)
|
||||
return;
|
||||
|
||||
// Only captain can remove members
|
||||
if (arenaTeam->GetCaptain() != _player->GetGUID())
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!normalizePlayerName(name))
|
||||
return;
|
||||
|
||||
// Check if team member exists
|
||||
ArenaTeamMember* member = arenaTeam->GetMember(name);
|
||||
if (!member)
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", name, ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S);
|
||||
return;
|
||||
}
|
||||
|
||||
// Captain cannot be removed
|
||||
if (arenaTeam->GetCaptain() == member->Guid)
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, "", "", ERR_ARENA_TEAM_LEADER_LEAVE_S);
|
||||
return;
|
||||
}
|
||||
|
||||
arenaTeam->DelMember(member->Guid, true);
|
||||
|
||||
// Broadcast event
|
||||
arenaTeam->BroadcastEvent(ERR_ARENA_TEAM_REMOVE_SSS, 0, 3, name, arenaTeam->GetName(), _player->GetName());
|
||||
}
|
||||
|
||||
void WorldSession::HandleArenaTeamLeaderOpcode(WorldPacket & recvData)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_ARENA_TEAM_LEADER");
|
||||
|
||||
uint32 arenaTeamId;
|
||||
std::string name;
|
||||
|
||||
recvData >> arenaTeamId;
|
||||
recvData >> name;
|
||||
|
||||
// Check for valid arena team
|
||||
ArenaTeam* arenaTeam = sArenaTeamMgr->GetArenaTeamById(arenaTeamId);
|
||||
if (!arenaTeam)
|
||||
return;
|
||||
|
||||
// Only captain can pass leadership
|
||||
if (arenaTeam->GetCaptain() != _player->GetGUID())
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!normalizePlayerName(name))
|
||||
return;
|
||||
|
||||
// Check if team member exists
|
||||
ArenaTeamMember* member = arenaTeam->GetMember(name);
|
||||
if (!member)
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", name, ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the target is already team captain
|
||||
if (arenaTeam->GetCaptain() == member->Guid)
|
||||
return;
|
||||
|
||||
arenaTeam->SetCaptain(member->Guid);
|
||||
|
||||
// Broadcast event
|
||||
arenaTeam->BroadcastEvent(ERR_ARENA_TEAM_LEADER_CHANGED_SSS, 0, 3, _player->GetName(), name, arenaTeam->GetName());
|
||||
}
|
||||
|
||||
void WorldSession::SendArenaTeamCommandResult(uint32 teamAction, const std::string& team, const std::string& player, uint32 errorId)
|
||||
{
|
||||
WorldPacket data(SMSG_ARENA_TEAM_COMMAND_RESULT, 4+team.length()+1+player.length()+1+4);
|
||||
data << uint32(teamAction);
|
||||
data << team;
|
||||
data << player;
|
||||
data << uint32(errorId);
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::SendNotInArenaTeamPacket(uint8 type)
|
||||
{
|
||||
WorldPacket data(SMSG_ARENA_ERROR, 4+1); // 886 - You are not in a %uv%u arena team
|
||||
uint32 unk = 0;
|
||||
data << uint32(unk); // unk(0)
|
||||
if (!unk)
|
||||
data << uint8(type); // team type (2=2v2, 3=3v3, 5=5v5), can be used for custom types...
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
/*
|
||||
+ERR_ARENA_NO_TEAM_II "You are not in a %dv%d arena team"
|
||||
|
||||
+ERR_ARENA_TEAM_CREATE_S "%s created. To disband, use /teamdisband [2v2, 3v3, 5v5]."
|
||||
+ERR_ARENA_TEAM_INVITE_SS "You have invited %s to join %s"
|
||||
+ERR_ARENA_TEAM_QUIT_S "You are no longer a member of %s"
|
||||
ERR_ARENA_TEAM_FOUNDER_S "Congratulations, you are a founding member of %s! To leave, use /teamquit [2v2, 3v3, 5v5]."
|
||||
|
||||
+ERR_ARENA_TEAM_INTERNAL "Internal arena team error"
|
||||
+ERR_ALREADY_IN_ARENA_TEAM "You are already in an arena team of that size"
|
||||
+ERR_ALREADY_IN_ARENA_TEAM_S "%s is already in an arena team of that size"
|
||||
+ERR_INVITED_TO_ARENA_TEAM "You have already been invited into an arena team"
|
||||
+ERR_ALREADY_INVITED_TO_ARENA_TEAM_S "%s has already been invited to an arena team"
|
||||
+ERR_ARENA_TEAM_NAME_INVALID "That name contains invalid characters, please enter a new name"
|
||||
+ERR_ARENA_TEAM_NAME_EXISTS_S "There is already an arena team named \"%s\""
|
||||
+ERR_ARENA_TEAM_LEADER_LEAVE_S "You must promote a new team captain using /teamcaptain before leaving the team"
|
||||
+ERR_ARENA_TEAM_PERMISSIONS "You don't have permission to do that"
|
||||
+ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM "You are not in an arena team of that size"
|
||||
+ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM_SS "%s is not in %s"
|
||||
+ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S "\"%s\" not found"
|
||||
+ERR_ARENA_TEAM_NOT_ALLIED "You cannot invite players from the opposing alliance"
|
||||
|
||||
+ERR_ARENA_TEAM_JOIN_SS "%s has joined %s"
|
||||
+ERR_ARENA_TEAM_YOU_JOIN_S "You have joined %s. To leave, use /teamquit [2v2, 3v3, 5v5]."
|
||||
|
||||
+ERR_ARENA_TEAM_LEAVE_SS "%s has left %s"
|
||||
|
||||
+ERR_ARENA_TEAM_LEADER_IS_SS "%s is the captain of %s"
|
||||
+ERR_ARENA_TEAM_LEADER_CHANGED_SSS "%s has made %s the new captain of %s"
|
||||
|
||||
+ERR_ARENA_TEAM_REMOVE_SSS "%s has been kicked out of %s by %s"
|
||||
|
||||
+ERR_ARENA_TEAM_DISBANDED_S "%s has disbanded %s"
|
||||
|
||||
ERR_ARENA_TEAM_TARGET_TOO_LOW_S "%s is not high enough level to join your team"
|
||||
|
||||
ERR_ARENA_TEAM_TOO_MANY_MEMBERS_S "%s is full"
|
||||
|
||||
ERR_ARENA_TEAM_LEVEL_TOO_LOW_I "You must be level %d to form an arena team"
|
||||
*/
|
||||
+639
@@ -0,0 +1,639 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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 "ObjectMgr.h"
|
||||
#include "Player.h"
|
||||
#include "World.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
|
||||
#include "AuctionHouseMgr.h"
|
||||
#include "Log.h"
|
||||
#include "Opcodes.h"
|
||||
#include "UpdateMask.h"
|
||||
#include "Util.h"
|
||||
#include "AccountMgr.h"
|
||||
|
||||
//please DO NOT use iterator++, because it is slower than ++iterator!!!
|
||||
//post-incrementation is always slower than pre-incrementation !
|
||||
|
||||
//void called when player click on auctioneer npc
|
||||
void WorldSession::HandleAuctionHelloOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
uint64 guid; //NPC guid
|
||||
recv_data >> guid;
|
||||
|
||||
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_AUCTIONEER);
|
||||
if (!unit)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleAuctionHelloOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)));
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
SendAuctionHello(guid, unit);
|
||||
}
|
||||
|
||||
//this void causes that auction window is opened
|
||||
void WorldSession::SendAuctionHello(uint64 guid, Creature* unit)
|
||||
{
|
||||
if (GetPlayer()->getLevel() < sWorld->getIntConfig(CONFIG_AUCTION_LEVEL_REQ))
|
||||
{
|
||||
SendNotification(GetTrinityString(LANG_AUCTION_REQ), sWorld->getIntConfig(CONFIG_AUCTION_LEVEL_REQ));
|
||||
return;
|
||||
}
|
||||
|
||||
AuctionHouseEntry const* ahEntry = AuctionHouseMgr::GetAuctionHouseEntry(unit->getFaction());
|
||||
if (!ahEntry)
|
||||
return;
|
||||
|
||||
WorldPacket data(MSG_AUCTION_HELLO, 12);
|
||||
data << uint64(guid);
|
||||
data << uint32(ahEntry->houseId);
|
||||
data << uint8(1); // 3.3.3: 1 - AH enabled, 0 - AH disabled
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
//call this method when player bids, creates, or deletes auction
|
||||
void WorldSession::SendAuctionCommandResult(uint32 auctionId, uint32 Action, uint32 ErrorCode, uint32 bidError)
|
||||
{
|
||||
WorldPacket data(SMSG_AUCTION_COMMAND_RESULT, 16);
|
||||
data << auctionId;
|
||||
data << Action;
|
||||
data << ErrorCode;
|
||||
if (!ErrorCode && Action)
|
||||
data << bidError; //when bid, then send 0, once...
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
//this function sends notification, if bidder is online
|
||||
void WorldSession::SendAuctionBidderNotification(uint32 location, uint32 auctionId, uint64 bidder, uint32 bidSum, uint32 diff, uint32 item_template)
|
||||
{
|
||||
WorldPacket data(SMSG_AUCTION_BIDDER_NOTIFICATION, (8*4));
|
||||
data << uint32(location);
|
||||
data << uint32(auctionId);
|
||||
data << uint64(bidder);
|
||||
data << uint32(bidSum);
|
||||
data << uint32(diff);
|
||||
data << uint32(item_template);
|
||||
data << uint32(0);
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
//this void causes on client to display: "Your auction sold"
|
||||
void WorldSession::SendAuctionOwnerNotification(AuctionEntry* auction)
|
||||
{
|
||||
WorldPacket data(SMSG_AUCTION_OWNER_NOTIFICATION, (8*4));
|
||||
data << uint32(auction->Id);
|
||||
data << uint32(auction->bid);
|
||||
data << uint32(0); //unk
|
||||
data << uint64(0); //unk (bidder guid?)
|
||||
data << uint32(auction->item_template);
|
||||
data << uint32(0); //unk
|
||||
data << float(0); //unk (time?)
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
//this void creates new auction and adds auction to some auctionhouse
|
||||
void WorldSession::HandleAuctionSellItem(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_AUCTION_SELL_ITEM");
|
||||
|
||||
uint64 auctioneer, item;
|
||||
uint32 etime, bid, buyout, count;
|
||||
recv_data >> auctioneer;
|
||||
recv_data.read_skip<uint32>(); // const 1?
|
||||
recv_data >> item;
|
||||
recv_data >> count; // 3.2.2, number of items being auctioned
|
||||
recv_data >> bid;
|
||||
recv_data >> buyout;
|
||||
recv_data >> etime;
|
||||
|
||||
Player* player = GetPlayer();
|
||||
|
||||
if (!item || !bid || !etime)
|
||||
return; //check for cheaters
|
||||
|
||||
Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(auctioneer, UNIT_NPC_FLAG_AUCTIONEER);
|
||||
if (!creature)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleAuctionSellItem - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(auctioneer)));
|
||||
return;
|
||||
}
|
||||
|
||||
AuctionHouseEntry const* auctionHouseEntry = AuctionHouseMgr::GetAuctionHouseEntry(creature->getFaction());
|
||||
if (!auctionHouseEntry)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleAuctionSellItem - Unit (GUID: %u) has wrong faction.", uint32(GUID_LOPART(auctioneer)));
|
||||
return;
|
||||
}
|
||||
|
||||
// client send time in minutes, convert to common used sec time
|
||||
etime *= MINUTE;
|
||||
|
||||
// client understand only 3 auction time
|
||||
switch (etime)
|
||||
{
|
||||
case 1*MIN_AUCTION_TIME:
|
||||
case 2*MIN_AUCTION_TIME:
|
||||
case 4*MIN_AUCTION_TIME:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
Item* it = player->GetItemByGuid(item);
|
||||
//do not allow to sell already auctioned items
|
||||
if (sAuctionMgr->GetAItem(GUID_LOPART(item)))
|
||||
{
|
||||
sLog->outError("AuctionError, player %s is sending item id: %u, but item is already in another auction", player->GetName(), GUID_LOPART(item));
|
||||
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
// prevent sending bag with items (cheat: can be placed in bag after adding equiped empty bag to auction)
|
||||
if (!it)
|
||||
{
|
||||
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_ITEM_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!it->CanBeTraded())
|
||||
{
|
||||
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (it->GetTemplate()->Flags & ITEM_PROTO_FLAG_CONJURED || it->GetUInt32Value(ITEM_FIELD_DURATION))
|
||||
{
|
||||
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (it->IsNotEmptyBag())
|
||||
{
|
||||
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction());
|
||||
|
||||
//we have to take deposit :
|
||||
uint32 deposit = sAuctionMgr->GetAuctionDeposit(auctionHouseEntry, etime, it, count);
|
||||
if (!player->HasEnoughMoney(deposit))
|
||||
{
|
||||
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_NOT_ENOUGHT_MONEY);
|
||||
return;
|
||||
}
|
||||
|
||||
if (AccountMgr::IsGMAccount(GetSecurity()) && sWorld->getBoolConfig(CONFIG_GM_LOG_TRADE))
|
||||
{
|
||||
sLog->outCommand(GetAccountId(), "GM %s (Account: %u) create auction: %s (Entry: %u Count: %u)",
|
||||
GetPlayerName(), GetAccountId(), it->GetTemplate()->Name1.c_str(), it->GetEntry(), count);
|
||||
}
|
||||
|
||||
player->ModifyMoney(-int32(deposit));
|
||||
|
||||
uint32 auction_time = uint32(etime * sWorld->getRate(RATE_AUCTION_TIME));
|
||||
|
||||
AuctionEntry* AH = new AuctionEntry;
|
||||
AH->Id = sObjectMgr->GenerateAuctionID();
|
||||
if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION))
|
||||
AH->auctioneer = 23442;
|
||||
else
|
||||
AH->auctioneer = GUID_LOPART(auctioneer);
|
||||
AH->item_guidlow = GUID_LOPART(item);
|
||||
AH->item_template = it->GetEntry();
|
||||
AH->owner = player->GetGUIDLow();
|
||||
AH->startbid = bid;
|
||||
AH->bidder = 0;
|
||||
AH->bid = 0;
|
||||
AH->buyout = buyout;
|
||||
AH->expire_time = time(NULL) + auction_time;
|
||||
AH->deposit = deposit;
|
||||
AH->auctionHouseEntry = auctionHouseEntry;
|
||||
|
||||
sLog->outDetail("selling item %u to auctioneer %u with initial bid %u with buyout %u and with time %u (in sec) in auctionhouse %u", GUID_LOPART(item), AH->auctioneer, bid, buyout, auction_time, AH->GetHouseId());
|
||||
sAuctionMgr->AddAItem(it);
|
||||
auctionHouse->AddAuction(AH);
|
||||
|
||||
player->MoveItemFromInventory(it->GetBagSlot(), it->GetSlot(), true);
|
||||
|
||||
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
it->DeleteFromInventoryDB(trans);
|
||||
it->SaveToDB(trans); // recursive and not have transaction guard into self, not in inventiory and can be save standalone
|
||||
AH->SaveToDB(trans);
|
||||
player->SaveInventoryAndGoldToDB(trans);
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
|
||||
SendAuctionCommandResult(AH->Id, AUCTION_SELL_ITEM, AUCTION_OK);
|
||||
|
||||
GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION, 1);
|
||||
}
|
||||
|
||||
//this function is called when client bids or buys out auction
|
||||
void WorldSession::HandleAuctionPlaceBid(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_AUCTION_PLACE_BID");
|
||||
|
||||
uint64 auctioneer;
|
||||
uint32 auctionId;
|
||||
uint32 price;
|
||||
recv_data >> auctioneer;
|
||||
recv_data >> auctionId >> price;
|
||||
|
||||
if (!auctionId || !price)
|
||||
return; //check for cheaters
|
||||
|
||||
Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(auctioneer, UNIT_NPC_FLAG_AUCTIONEER);
|
||||
if (!creature)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleAuctionPlaceBid - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(auctioneer)));
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction());
|
||||
|
||||
AuctionEntry* auction = auctionHouse->GetAuction(auctionId);
|
||||
Player* player = GetPlayer();
|
||||
|
||||
if (!auction || auction->owner == player->GetGUIDLow())
|
||||
{
|
||||
//you cannot bid your own auction:
|
||||
SendAuctionCommandResult(0, AUCTION_PLACE_BID, CANNOT_BID_YOUR_AUCTION_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// impossible have online own another character (use this for speedup check in case online owner)
|
||||
Player* auction_owner = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER));
|
||||
if (!auction_owner && sObjectMgr->GetPlayerAccountIdByGUID(MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER)) == player->GetSession()->GetAccountId())
|
||||
{
|
||||
//you cannot bid your another character auction:
|
||||
SendAuctionCommandResult(0, AUCTION_PLACE_BID, CANNOT_BID_YOUR_AUCTION_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// cheating
|
||||
if (price <= auction->bid || price < auction->startbid)
|
||||
return;
|
||||
|
||||
// price too low for next bid if not buyout
|
||||
if ((price < auction->buyout || auction->buyout == 0) &&
|
||||
price < auction->bid + auction->GetAuctionOutBid())
|
||||
{
|
||||
//auction has already higher bid, client tests it!
|
||||
return;
|
||||
}
|
||||
|
||||
if (!player->HasEnoughMoney(price))
|
||||
{
|
||||
//you don't have enought money!, client tests!
|
||||
//SendAuctionCommandResult(auction->auctionId, AUCTION_PLACE_BID, ???);
|
||||
return;
|
||||
}
|
||||
|
||||
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
|
||||
if (price < auction->buyout || auction->buyout == 0)
|
||||
{
|
||||
if (auction->bidder > 0)
|
||||
{
|
||||
if (auction->bidder == player->GetGUIDLow())
|
||||
player->ModifyMoney(-int32(price - auction->bid));
|
||||
else
|
||||
{
|
||||
// mail to last bidder and return money
|
||||
sAuctionMgr->SendAuctionOutbiddedMail(auction, price, GetPlayer(), trans);
|
||||
player->ModifyMoney(-int32(price));
|
||||
}
|
||||
}
|
||||
else
|
||||
player->ModifyMoney(-int32(price));
|
||||
|
||||
auction->bidder = player->GetGUIDLow();
|
||||
auction->bid = price;
|
||||
GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, price);
|
||||
|
||||
trans->PAppend("UPDATE auctionhouse SET buyguid = '%u', lastbid = '%u' WHERE id = '%u'", auction->bidder, auction->bid, auction->Id);
|
||||
|
||||
SendAuctionCommandResult(auction->Id, AUCTION_PLACE_BID, AUCTION_OK, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
//buyout:
|
||||
if (player->GetGUIDLow() == auction->bidder)
|
||||
player->ModifyMoney(-int32(auction->buyout - auction->bid));
|
||||
else
|
||||
{
|
||||
player->ModifyMoney(-int32(auction->buyout));
|
||||
if (auction->bidder) //buyout for bidded auction ..
|
||||
sAuctionMgr->SendAuctionOutbiddedMail(auction, auction->buyout, GetPlayer(), trans);
|
||||
}
|
||||
auction->bidder = player->GetGUIDLow();
|
||||
auction->bid = auction->buyout;
|
||||
GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, auction->buyout);
|
||||
|
||||
//- Mails must be under transaction control too to prevent data loss
|
||||
sAuctionMgr->SendAuctionSalePendingMail(auction, trans);
|
||||
sAuctionMgr->SendAuctionSuccessfulMail(auction, trans);
|
||||
sAuctionMgr->SendAuctionWonMail(auction, trans);
|
||||
|
||||
SendAuctionCommandResult(auction->Id, AUCTION_PLACE_BID, AUCTION_OK);
|
||||
|
||||
auction->DeleteFromDB(trans);
|
||||
|
||||
uint32 item_template = auction->item_template;
|
||||
sAuctionMgr->RemoveAItem(auction->item_guidlow);
|
||||
auctionHouse->RemoveAuction(auction, item_template);
|
||||
}
|
||||
player->SaveInventoryAndGoldToDB(trans);
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
}
|
||||
|
||||
//this void is called when auction_owner cancels his auction
|
||||
void WorldSession::HandleAuctionRemoveItem(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_AUCTION_REMOVE_ITEM");
|
||||
|
||||
uint64 auctioneer;
|
||||
uint32 auctionId;
|
||||
recv_data >> auctioneer;
|
||||
recv_data >> auctionId;
|
||||
//sLog->outDebug("Cancel AUCTION AuctionID: %u", auctionId);
|
||||
|
||||
Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(auctioneer, UNIT_NPC_FLAG_AUCTIONEER);
|
||||
if (!creature)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleAuctionRemoveItem - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(auctioneer)));
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction());
|
||||
|
||||
AuctionEntry* auction = auctionHouse->GetAuction(auctionId);
|
||||
Player* player = GetPlayer();
|
||||
|
||||
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
if (auction && auction->owner == player->GetGUIDLow())
|
||||
{
|
||||
Item* pItem = sAuctionMgr->GetAItem(auction->item_guidlow);
|
||||
if (pItem)
|
||||
{
|
||||
if (auction->bidder > 0) // If we have a bidder, we have to send him the money he paid
|
||||
{
|
||||
uint32 auctionCut = auction->GetAuctionCut();
|
||||
if (!player->HasEnoughMoney(auctionCut)) //player doesn't have enough money, maybe message needed
|
||||
return;
|
||||
//some auctionBidderNotification would be needed, but don't know that parts..
|
||||
sAuctionMgr->SendAuctionCancelledToBidderMail(auction, trans);
|
||||
player->ModifyMoney(-int32(auctionCut));
|
||||
}
|
||||
// Return the item by mail
|
||||
std::ostringstream msgAuctionCanceledOwner;
|
||||
msgAuctionCanceledOwner << auction->item_template << ":0:" << AUCTION_CANCELED << ":0:0";
|
||||
|
||||
// item will deleted or added to received mail list
|
||||
MailDraft(msgAuctionCanceledOwner.str(), "") // TODO: fix body
|
||||
.AddItem(pItem)
|
||||
.SendMailTo(trans, player, auction, MAIL_CHECK_MASK_COPIED);
|
||||
}
|
||||
else
|
||||
{
|
||||
sLog->outError("Auction id: %u has non-existed item (item guid : %u)!!!", auction->Id, auction->item_guidlow);
|
||||
SendAuctionCommandResult(0, AUCTION_CANCEL, AUCTION_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SendAuctionCommandResult(0, AUCTION_CANCEL, AUCTION_INTERNAL_ERROR);
|
||||
//this code isn't possible ... maybe there should be assert
|
||||
sLog->outError("CHEATER : %u, he tried to cancel auction (id: %u) of another player, or auction is NULL", player->GetGUIDLow(), auctionId);
|
||||
return;
|
||||
}
|
||||
|
||||
//inform player, that auction is removed
|
||||
SendAuctionCommandResult(auction->Id, AUCTION_CANCEL, AUCTION_OK);
|
||||
|
||||
// Now remove the auction
|
||||
|
||||
player->SaveInventoryAndGoldToDB(trans);
|
||||
auction->DeleteFromDB(trans);
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
|
||||
uint32 item_template = auction->item_template;
|
||||
sAuctionMgr->RemoveAItem(auction->item_guidlow);
|
||||
auctionHouse->RemoveAuction(auction, item_template);
|
||||
}
|
||||
|
||||
//called when player lists his bids
|
||||
void WorldSession::HandleAuctionListBidderItems(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_AUCTION_LIST_BIDDER_ITEMS");
|
||||
|
||||
uint64 guid; //NPC guid
|
||||
uint32 listfrom; //page of auctions
|
||||
uint32 outbiddedCount; //count of outbidded auctions
|
||||
|
||||
recv_data >> guid;
|
||||
recv_data >> listfrom; // not used in fact (this list not have page control in client)
|
||||
recv_data >> outbiddedCount;
|
||||
if (recv_data.size() != (16 + outbiddedCount * 4))
|
||||
{
|
||||
sLog->outError("Client sent bad opcode!!! with count: %u and size : %lu (must be: %u)", outbiddedCount, (unsigned long)recv_data.size(), (16 + outbiddedCount * 4));
|
||||
outbiddedCount = 0;
|
||||
}
|
||||
|
||||
Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_AUCTIONEER);
|
||||
if (!creature)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleAuctionListBidderItems - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)));
|
||||
recv_data.rfinish();
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction());
|
||||
|
||||
WorldPacket data(SMSG_AUCTION_BIDDER_LIST_RESULT, (4+4+4));
|
||||
Player* player = GetPlayer();
|
||||
data << (uint32) 0; //add 0 as count
|
||||
uint32 count = 0;
|
||||
uint32 totalcount = 0;
|
||||
while (outbiddedCount > 0) //add all data, which client requires
|
||||
{
|
||||
--outbiddedCount;
|
||||
uint32 outbiddedAuctionId;
|
||||
recv_data >> outbiddedAuctionId;
|
||||
AuctionEntry* auction = auctionHouse->GetAuction(outbiddedAuctionId);
|
||||
if (auction && auction->BuildAuctionInfo(data))
|
||||
{
|
||||
++totalcount;
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
auctionHouse->BuildListBidderItems(data, player, count, totalcount);
|
||||
data.put<uint32>(0, count); // add count to placeholder
|
||||
data << totalcount;
|
||||
data << (uint32)300; //unk 2.3.0
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
//this void sends player info about his auctions
|
||||
void WorldSession::HandleAuctionListOwnerItems(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_AUCTION_LIST_OWNER_ITEMS");
|
||||
|
||||
uint32 listfrom;
|
||||
uint64 guid;
|
||||
|
||||
recv_data >> guid;
|
||||
recv_data >> listfrom; // not used in fact (this list not have page control in client)
|
||||
|
||||
Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_AUCTIONEER);
|
||||
if (!creature)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleAuctionListOwnerItems - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)));
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction());
|
||||
|
||||
WorldPacket data(SMSG_AUCTION_OWNER_LIST_RESULT, (4+4+4));
|
||||
data << (uint32) 0; // amount place holder
|
||||
|
||||
uint32 count = 0;
|
||||
uint32 totalcount = 0;
|
||||
|
||||
auctionHouse->BuildListOwnerItems(data, _player, count, totalcount);
|
||||
data.put<uint32>(0, count);
|
||||
data << (uint32) totalcount;
|
||||
data << (uint32) 0;
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
//this void is called when player clicks on search button
|
||||
void WorldSession::HandleAuctionListItems(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_AUCTION_LIST_ITEMS");
|
||||
|
||||
std::string searchedname;
|
||||
uint8 levelmin, levelmax, usable;
|
||||
uint32 listfrom, auctionSlotID, auctionMainCategory, auctionSubCategory, quality;
|
||||
uint64 guid;
|
||||
|
||||
recv_data >> guid;
|
||||
recv_data >> listfrom; // start, used for page control listing by 50 elements
|
||||
recv_data >> searchedname;
|
||||
|
||||
recv_data >> levelmin >> levelmax;
|
||||
recv_data >> auctionSlotID >> auctionMainCategory >> auctionSubCategory;
|
||||
recv_data >> quality >> usable;
|
||||
|
||||
recv_data.read_skip<uint8>(); // unk
|
||||
|
||||
// this block looks like it uses some lame byte packing or similar...
|
||||
uint8 unkCnt;
|
||||
recv_data >> unkCnt;
|
||||
for (uint8 i = 0; i < unkCnt; i++)
|
||||
{
|
||||
recv_data.read_skip<uint8>();
|
||||
recv_data.read_skip<uint8>();
|
||||
}
|
||||
|
||||
Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_AUCTIONEER);
|
||||
if (!creature)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleAuctionListItems - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)));
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction());
|
||||
|
||||
//sLog->outDebug("Auctionhouse search (GUID: %u TypeId: %u)",, list from: %u, searchedname: %s, levelmin: %u, levelmax: %u, auctionSlotID: %u, auctionMainCategory: %u, auctionSubCategory: %u, quality: %u, usable: %u",
|
||||
// GUID_LOPART(guid), GuidHigh2TypeId(GUID_HIPART(guid)), listfrom, searchedname.c_str(), levelmin, levelmax, auctionSlotID, auctionMainCategory, auctionSubCategory, quality, usable);
|
||||
|
||||
WorldPacket data(SMSG_AUCTION_LIST_RESULT, (4+4+4));
|
||||
uint32 count = 0;
|
||||
uint32 totalcount = 0;
|
||||
data << (uint32) 0;
|
||||
|
||||
// converting string that we try to find to lower case
|
||||
std::wstring wsearchedname;
|
||||
if (!Utf8toWStr(searchedname, wsearchedname))
|
||||
return;
|
||||
|
||||
wstrToLower(wsearchedname);
|
||||
|
||||
auctionHouse->BuildListAuctionItems(data, _player,
|
||||
wsearchedname, listfrom, levelmin, levelmax, usable,
|
||||
auctionSlotID, auctionMainCategory, auctionSubCategory, quality,
|
||||
count, totalcount);
|
||||
|
||||
data.put<uint32>(0, count);
|
||||
data << (uint32) totalcount;
|
||||
data << (uint32) 300; // unk 2.3.0 const?
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleAuctionListPendingSales(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_AUCTION_LIST_PENDING_SALES");
|
||||
|
||||
recv_data.read_skip<uint64>();
|
||||
|
||||
uint32 count = 0;
|
||||
|
||||
WorldPacket data(SMSG_AUCTION_LIST_PENDING_SALES, 4);
|
||||
data << uint32(count); // count
|
||||
/*for (uint32 i = 0; i < count; ++i)
|
||||
{
|
||||
data << ""; // string
|
||||
data << ""; // string
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << float(0);
|
||||
}*/
|
||||
SendPacket(&data);
|
||||
}
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
*
|
||||
* 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 "Opcodes.h"
|
||||
#include "WorldSession.h"
|
||||
#include "WorldPacket.h"
|
||||
|
||||
void WorldSession::SendAuthResponse(uint8 code, bool shortForm, uint32 queuePos)
|
||||
{
|
||||
WorldPacket packet(SMSG_AUTH_RESPONSE, 1 + 4 + 1 + 4 + 1 + (shortForm ? 0 : (4 + 1)));
|
||||
packet << uint8(code);
|
||||
packet << uint32(0); // BillingTimeRemaining
|
||||
packet << uint8(0); // BillingPlanFlags
|
||||
packet << uint32(0); // BillingTimeRested
|
||||
packet << uint8(Expansion()); // 0 - normal, 1 - TBC, 2 - WOTLK, must be set in database manually for each account
|
||||
|
||||
if (!shortForm)
|
||||
{
|
||||
packet << uint32(queuePos); // Queue position
|
||||
packet << uint8(0); // Unk 3.3.0
|
||||
}
|
||||
|
||||
SendPacket(&packet);
|
||||
}
|
||||
|
||||
void WorldSession::SendClientCacheVersion(uint32 version)
|
||||
{
|
||||
WorldPacket data(SMSG_CLIENTCACHE_VERSION, 4);
|
||||
data << uint32(version);
|
||||
SendPacket(&data);
|
||||
}
|
||||
+790
@@ -0,0 +1,790 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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 "Common.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "ArenaTeamMgr.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
|
||||
#include "ArenaTeam.h"
|
||||
#include "BattlegroundMgr.h"
|
||||
#include "Battleground.h"
|
||||
#include "Chat.h"
|
||||
#include "Language.h"
|
||||
#include "Log.h"
|
||||
#include "Player.h"
|
||||
#include "Object.h"
|
||||
#include "Opcodes.h"
|
||||
#include "DisableMgr.h"
|
||||
#include "Group.h"
|
||||
|
||||
void WorldSession::HandleBattlemasterHelloOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
uint64 guid;
|
||||
recv_data >> guid;
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_BATTLEMASTER_HELLO Message from (GUID: %u TypeId:%u)", GUID_LOPART(guid), GuidHigh2TypeId(GUID_HIPART(guid)));
|
||||
|
||||
Creature* unit = GetPlayer()->GetMap()->GetCreature(guid);
|
||||
if (!unit)
|
||||
return;
|
||||
|
||||
if (!unit->isBattleMaster()) // it's not battlemaster
|
||||
return;
|
||||
|
||||
// Stop the npc if moving
|
||||
unit->StopMoving();
|
||||
|
||||
BattlegroundTypeId bgTypeId = sBattlegroundMgr->GetBattleMasterBG(unit->GetEntry());
|
||||
|
||||
if (!_player->GetBGAccessByLevel(bgTypeId))
|
||||
{
|
||||
// temp, must be gossip message...
|
||||
SendNotification(LANG_YOUR_BG_LEVEL_REQ_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
SendBattlegGroundList(guid, bgTypeId);
|
||||
}
|
||||
|
||||
void WorldSession::SendBattlegGroundList(uint64 guid, BattlegroundTypeId bgTypeId)
|
||||
{
|
||||
WorldPacket data;
|
||||
sBattlegroundMgr->BuildBattlegroundListPacket(&data, guid, _player, bgTypeId, 0);
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
uint64 guid;
|
||||
uint32 bgTypeId_;
|
||||
uint32 instanceId;
|
||||
uint8 joinAsGroup;
|
||||
bool isPremade = false;
|
||||
Group* grp = NULL;
|
||||
|
||||
recv_data >> guid; // battlemaster guid
|
||||
recv_data >> bgTypeId_; // battleground type id (DBC id)
|
||||
recv_data >> instanceId; // instance id, 0 if First Available selected
|
||||
recv_data >> joinAsGroup; // join as group
|
||||
|
||||
if (!sBattlemasterListStore.LookupEntry(bgTypeId_))
|
||||
{
|
||||
sLog->outError("Battleground: invalid bgtype (%u) received. possible cheater? player guid %u", bgTypeId_, _player->GetGUIDLow());
|
||||
return;
|
||||
}
|
||||
|
||||
if (DisableMgr::IsDisabledFor(DISABLE_TYPE_BATTLEGROUND, bgTypeId_, NULL))
|
||||
{
|
||||
ChatHandler(this).PSendSysMessage(LANG_BG_DISABLED);
|
||||
return;
|
||||
}
|
||||
|
||||
BattlegroundTypeId bgTypeId = BattlegroundTypeId(bgTypeId_);
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_BATTLEMASTER_JOIN Message from (GUID: %u TypeId:%u)", GUID_LOPART(guid), GuidHigh2TypeId(GUID_HIPART(guid)));
|
||||
|
||||
// can do this, since it's battleground, not arena
|
||||
BattlegroundQueueTypeId bgQueueTypeId = BattlegroundMgr::BGQueueTypeId(bgTypeId, 0);
|
||||
BattlegroundQueueTypeId bgQueueTypeIdRandom = BattlegroundMgr::BGQueueTypeId(BATTLEGROUND_RB, 0);
|
||||
|
||||
// ignore if player is already in BG
|
||||
if (_player->InBattleground())
|
||||
return;
|
||||
|
||||
// get bg instance or bg template if instance not found
|
||||
Battleground* bg = NULL;
|
||||
if (instanceId)
|
||||
bg = sBattlegroundMgr->GetBattlegroundThroughClientInstance(instanceId, bgTypeId);
|
||||
|
||||
if (!bg)
|
||||
bg = sBattlegroundMgr->GetBattlegroundTemplate(bgTypeId);
|
||||
if (!bg)
|
||||
return;
|
||||
|
||||
// expected bracket entry
|
||||
PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bg->GetMapId(), _player->getLevel());
|
||||
if (!bracketEntry)
|
||||
return;
|
||||
|
||||
GroupJoinBattlegroundResult err;
|
||||
|
||||
// check queue conditions
|
||||
if (!joinAsGroup)
|
||||
{
|
||||
if (GetPlayer()->isUsingLfg())
|
||||
{
|
||||
// player is using dungeon finder or raid finder
|
||||
WorldPacket data;
|
||||
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, ERR_LFG_CANT_USE_BATTLEGROUND);
|
||||
GetPlayer()->GetSession()->SendPacket(&data);
|
||||
return;
|
||||
}
|
||||
|
||||
// check Deserter debuff
|
||||
if (!_player->CanJoinToBattleground())
|
||||
{
|
||||
WorldPacket data;
|
||||
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, ERR_GROUP_JOIN_BATTLEGROUND_DESERTERS);
|
||||
_player->GetSession()->SendPacket(&data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_player->GetBattlegroundQueueIndex(bgQueueTypeIdRandom) < PLAYER_MAX_BATTLEGROUND_QUEUES)
|
||||
{
|
||||
//player is already in random queue
|
||||
WorldPacket data;
|
||||
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, ERR_IN_RANDOM_BG);
|
||||
_player->GetSession()->SendPacket(&data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_player->InBattlegroundQueue() && bgTypeId == BATTLEGROUND_RB)
|
||||
{
|
||||
//player is already in queue, can't start random queue
|
||||
WorldPacket data;
|
||||
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, ERR_IN_NON_RANDOM_BG);
|
||||
_player->GetSession()->SendPacket(&data);
|
||||
return;
|
||||
}
|
||||
|
||||
// check if already in queue
|
||||
if (_player->GetBattlegroundQueueIndex(bgQueueTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES)
|
||||
//player is already in this queue
|
||||
return;
|
||||
|
||||
// check if has free queue slots
|
||||
if (!_player->HasFreeBattlegroundQueueId())
|
||||
{
|
||||
WorldPacket data;
|
||||
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, ERR_BATTLEGROUND_TOO_MANY_QUEUES);
|
||||
_player->GetSession()->SendPacket(&data);
|
||||
return;
|
||||
}
|
||||
|
||||
BattlegroundQueue& bgQueue = sBattlegroundMgr->m_BattlegroundQueues[bgQueueTypeId];
|
||||
|
||||
GroupQueueInfo* ginfo = bgQueue.AddGroup(_player, NULL, bgTypeId, bracketEntry, 0, false, isPremade, 0, 0);
|
||||
uint32 avgTime = bgQueue.GetAverageQueueWaitTime(ginfo, bracketEntry->GetBracketId());
|
||||
// already checked if queueSlot is valid, now just get it
|
||||
uint32 queueSlot = _player->AddBattlegroundQueueId(bgQueueTypeId);
|
||||
|
||||
WorldPacket data;
|
||||
// send status packet (in queue)
|
||||
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0, ginfo->ArenaType);
|
||||
SendPacket(&data);
|
||||
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "Battleground: player joined queue for bg queue type %u bg type %u: GUID %u, NAME %s", bgQueueTypeId, bgTypeId, _player->GetGUIDLow(), _player->GetName());
|
||||
}
|
||||
else
|
||||
{
|
||||
grp = _player->GetGroup();
|
||||
// no group found, error
|
||||
if (!grp)
|
||||
return;
|
||||
if (grp->GetLeaderGUID() != _player->GetGUID())
|
||||
return;
|
||||
err = grp->CanJoinBattlegroundQueue(bg, bgQueueTypeId, 0, bg->GetMaxPlayersPerTeam(), false, 0);
|
||||
isPremade = (grp->GetMembersCount() >= bg->GetMinPlayersPerTeam());
|
||||
|
||||
BattlegroundQueue& bgQueue = sBattlegroundMgr->m_BattlegroundQueues[bgQueueTypeId];
|
||||
GroupQueueInfo* ginfo = NULL;
|
||||
uint32 avgTime = 0;
|
||||
|
||||
if (err > 0)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "Battleground: the following players are joining as group:");
|
||||
ginfo = bgQueue.AddGroup(_player, grp, bgTypeId, bracketEntry, 0, false, isPremade, 0, 0);
|
||||
avgTime = bgQueue.GetAverageQueueWaitTime(ginfo, bracketEntry->GetBracketId());
|
||||
}
|
||||
|
||||
for (GroupReference* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
|
||||
{
|
||||
Player* member = itr->getSource();
|
||||
if (!member) continue; // this should never happen
|
||||
|
||||
WorldPacket data;
|
||||
|
||||
if (err <= 0)
|
||||
{
|
||||
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, err);
|
||||
member->GetSession()->SendPacket(&data);
|
||||
continue;
|
||||
}
|
||||
|
||||
// add to queue
|
||||
uint32 queueSlot = member->AddBattlegroundQueueId(bgQueueTypeId);
|
||||
|
||||
// send status packet (in queue)
|
||||
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0, ginfo->ArenaType);
|
||||
member->GetSession()->SendPacket(&data);
|
||||
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, err);
|
||||
member->GetSession()->SendPacket(&data);
|
||||
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "Battleground: player joined queue for bg queue type %u bg type %u: GUID %u, NAME %s", bgQueueTypeId, bgTypeId, member->GetGUIDLow(), member->GetName());
|
||||
}
|
||||
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "Battleground: group end");
|
||||
|
||||
}
|
||||
sBattlegroundMgr->ScheduleQueueUpdate(0, 0, bgQueueTypeId, bgTypeId, bracketEntry->GetBracketId());
|
||||
}
|
||||
|
||||
void WorldSession::HandleBattlegroundPlayerPositionsOpcode(WorldPacket & /*recv_data*/)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd MSG_BATTLEGROUND_PLAYER_POSITIONS Message");
|
||||
|
||||
Battleground* bg = _player->GetBattleground();
|
||||
if (!bg) // can't be received if player not in battleground
|
||||
return;
|
||||
|
||||
uint32 count = 0;
|
||||
Player* aplr = NULL;
|
||||
Player* hplr = NULL;
|
||||
|
||||
if (uint64 guid = bg->GetFlagPickerGUID(BG_TEAM_ALLIANCE))
|
||||
{
|
||||
aplr = ObjectAccessor::FindPlayer(guid);
|
||||
if (aplr)
|
||||
++count;
|
||||
}
|
||||
|
||||
if (uint64 guid = bg->GetFlagPickerGUID(BG_TEAM_HORDE))
|
||||
{
|
||||
hplr = ObjectAccessor::FindPlayer(guid);
|
||||
if (hplr)
|
||||
++count;
|
||||
}
|
||||
|
||||
WorldPacket data(MSG_BATTLEGROUND_PLAYER_POSITIONS, 4 + 4 + 16 * count);
|
||||
data << 0;
|
||||
data << count;
|
||||
if (aplr)
|
||||
{
|
||||
data << uint64(aplr->GetGUID());
|
||||
data << float(aplr->GetPositionX());
|
||||
data << float(aplr->GetPositionY());
|
||||
}
|
||||
|
||||
if (hplr)
|
||||
{
|
||||
data << uint64(hplr->GetGUID());
|
||||
data << float(hplr->GetPositionX());
|
||||
data << float(hplr->GetPositionY());
|
||||
}
|
||||
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandlePVPLogDataOpcode(WorldPacket & /*recv_data*/)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd MSG_PVP_LOG_DATA Message");
|
||||
|
||||
Battleground* bg = _player->GetBattleground();
|
||||
if (!bg)
|
||||
return;
|
||||
|
||||
// Prevent players from sending BuildPvpLogDataPacket in an arena except for when sent in BattleGround::EndBattleGround.
|
||||
if (bg->isArena())
|
||||
return;
|
||||
|
||||
WorldPacket data;
|
||||
sBattlegroundMgr->BuildPvpLogDataPacket(&data, bg);
|
||||
SendPacket(&data);
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent MSG_PVP_LOG_DATA Message");
|
||||
}
|
||||
|
||||
void WorldSession::HandleBattlefieldListOpcode(WorldPacket &recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_BATTLEFIELD_LIST Message");
|
||||
|
||||
uint32 bgTypeId;
|
||||
recv_data >> bgTypeId; // id from DBC
|
||||
|
||||
uint8 fromWhere;
|
||||
recv_data >> fromWhere; // 0 - battlemaster (lua: ShowBattlefieldList), 1 - UI (lua: RequestBattlegroundInstanceInfo)
|
||||
|
||||
uint8 unk1;
|
||||
recv_data >> unk1; // Unknown 3.2.2
|
||||
|
||||
BattlemasterListEntry const* bl = sBattlemasterListStore.LookupEntry(bgTypeId);
|
||||
if (!bl)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "BattlegroundHandler: invalid bgtype (%u) with player (Name: %s, GUID: %u) received.", bgTypeId, _player->GetName(), _player->GetGUIDLow());
|
||||
return;
|
||||
}
|
||||
|
||||
WorldPacket data;
|
||||
sBattlegroundMgr->BuildBattlegroundListPacket(&data, 0, _player, BattlegroundTypeId(bgTypeId), fromWhere);
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleBattleFieldPortOpcode(WorldPacket &recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_BATTLEFIELD_PORT Message");
|
||||
|
||||
uint8 type; // arenatype if arena
|
||||
uint8 unk2; // unk, can be 0x0 (may be if was invited?) and 0x1
|
||||
uint32 bgTypeId_; // type id from dbc
|
||||
uint16 unk; // 0x1F90 constant?
|
||||
uint8 action; // enter battle 0x1, leave queue 0x0
|
||||
|
||||
recv_data >> type >> unk2 >> bgTypeId_ >> unk >> action;
|
||||
|
||||
if (!sBattlemasterListStore.LookupEntry(bgTypeId_))
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "BattlegroundHandler: invalid bgtype (%u) with player (Name: %s, GUID: %u) received.", bgTypeId_, _player->GetName(), _player->GetGUIDLow());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_player->InBattlegroundQueue())
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "BattlegroundHandler: Invalid CMSG_BATTLEFIELD_PORT received from player (Name: %s, GUID: %u), he is not in bg_queue.", _player->GetName(), _player->GetGUIDLow());
|
||||
return;
|
||||
}
|
||||
|
||||
//get GroupQueueInfo from BattlegroundQueue
|
||||
BattlegroundTypeId bgTypeId = BattlegroundTypeId(bgTypeId_);
|
||||
BattlegroundQueueTypeId bgQueueTypeId = BattlegroundMgr::BGQueueTypeId(bgTypeId, type);
|
||||
BattlegroundQueue& bgQueue = sBattlegroundMgr->m_BattlegroundQueues[bgQueueTypeId];
|
||||
//we must use temporary variable, because GroupQueueInfo pointer can be deleted in BattlegroundQueue::RemovePlayer() function
|
||||
GroupQueueInfo ginfo;
|
||||
if (!bgQueue.GetPlayerGroupInfoData(_player->GetGUID(), &ginfo))
|
||||
{
|
||||
sLog->outError("BattlegroundHandler: itrplayerstatus not found.");
|
||||
return;
|
||||
}
|
||||
// if action == 1, then instanceId is required
|
||||
if (!ginfo.IsInvitedToBGInstanceGUID && action == 1)
|
||||
{
|
||||
sLog->outError("BattlegroundHandler: instance not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
Battleground* bg = sBattlegroundMgr->GetBattleground(ginfo.IsInvitedToBGInstanceGUID, bgTypeId);
|
||||
|
||||
// bg template might and must be used in case of leaving queue, when instance is not created yet
|
||||
if (!bg && action == 0)
|
||||
bg = sBattlegroundMgr->GetBattlegroundTemplate(bgTypeId);
|
||||
if (!bg)
|
||||
{
|
||||
sLog->outError("BattlegroundHandler: bg_template not found for type id %u.", bgTypeId);
|
||||
return;
|
||||
}
|
||||
|
||||
// expected bracket entry
|
||||
PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bg->GetMapId(), _player->getLevel());
|
||||
if (!bracketEntry)
|
||||
return;
|
||||
|
||||
//some checks if player isn't cheating - it is not exactly cheating, but we cannot allow it
|
||||
if (action == 1 && ginfo.ArenaType == 0)
|
||||
{
|
||||
//if player is trying to enter battleground (not arena!) and he has deserter debuff, we must just remove him from queue
|
||||
if (!_player->CanJoinToBattleground())
|
||||
{
|
||||
//send bg command result to show nice message
|
||||
WorldPacket data2;
|
||||
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data2, ERR_GROUP_JOIN_BATTLEGROUND_DESERTERS);
|
||||
_player->GetSession()->SendPacket(&data2);
|
||||
action = 0;
|
||||
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "Battleground: player %s (%u) has a deserter debuff, do not port him to battleground!", _player->GetName(), _player->GetGUIDLow());
|
||||
}
|
||||
//if player don't match battleground max level, then do not allow him to enter! (this might happen when player leveled up during his waiting in queue
|
||||
if (_player->getLevel() > bg->GetMaxLevel())
|
||||
{
|
||||
sLog->outError("Battleground: Player %s (%u) has level (%u) higher than maxlevel (%u) of battleground (%u)! Do not port him to battleground!",
|
||||
_player->GetName(), _player->GetGUIDLow(), _player->getLevel(), bg->GetMaxLevel(), bg->GetTypeID());
|
||||
action = 0;
|
||||
}
|
||||
}
|
||||
uint32 queueSlot = _player->GetBattlegroundQueueIndex(bgQueueTypeId);
|
||||
WorldPacket data;
|
||||
switch (action)
|
||||
{
|
||||
case 1: // port to battleground
|
||||
if (!_player->IsInvitedForBattlegroundQueueType(bgQueueTypeId))
|
||||
return; // cheating?
|
||||
|
||||
if (!_player->InBattleground())
|
||||
_player->SetBattlegroundEntryPoint();
|
||||
|
||||
// resurrect the player
|
||||
if (!_player->isAlive())
|
||||
{
|
||||
_player->ResurrectPlayer(1.0f);
|
||||
_player->SpawnCorpseBones();
|
||||
}
|
||||
// stop taxi flight at port
|
||||
if (_player->isInFlight())
|
||||
{
|
||||
_player->GetMotionMaster()->MovementExpired();
|
||||
_player->CleanupAfterTaxiFlight();
|
||||
}
|
||||
|
||||
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime(), bg->GetArenaType());
|
||||
_player->GetSession()->SendPacket(&data);
|
||||
// remove battleground queue status from BGmgr
|
||||
bgQueue.RemovePlayer(_player->GetGUID(), false);
|
||||
// this is still needed here if battleground "jumping" shouldn't add deserter debuff
|
||||
// also this is required to prevent stuck at old battleground after SetBattlegroundId set to new
|
||||
if (Battleground* currentBg = _player->GetBattleground())
|
||||
currentBg->RemovePlayerAtLeave(_player->GetGUID(), false, true);
|
||||
|
||||
// set the destination instance id
|
||||
_player->SetBattlegroundId(bg->GetInstanceID(), bgTypeId);
|
||||
// set the destination team
|
||||
_player->SetBGTeam(ginfo.Team);
|
||||
// bg->HandleBeforeTeleportToBattleground(_player);
|
||||
sBattlegroundMgr->SendToBattleground(_player, ginfo.IsInvitedToBGInstanceGUID, bgTypeId);
|
||||
// add only in HandleMoveWorldPortAck()
|
||||
// bg->AddPlayer(_player, team);
|
||||
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "Battleground: player %s (%u) joined battle for bg %u, bgtype %u, queue type %u.", _player->GetName(), _player->GetGUIDLow(), bg->GetInstanceID(), bg->GetTypeID(), bgQueueTypeId);
|
||||
break;
|
||||
case 0: // leave queue
|
||||
// if player leaves rated arena match before match start, it is counted as he played but he lost
|
||||
if (ginfo.IsRated && ginfo.IsInvitedToBGInstanceGUID)
|
||||
{
|
||||
ArenaTeam* at = sArenaTeamMgr->GetArenaTeamById(ginfo.Team);
|
||||
if (at)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "UPDATING memberLost's personal arena rating for %u by opponents rating: %u, because he has left queue!", GUID_LOPART(_player->GetGUID()), ginfo.OpponentsTeamRating);
|
||||
at->MemberLost(_player, ginfo.OpponentsMatchmakerRating);
|
||||
at->SaveToDB();
|
||||
}
|
||||
}
|
||||
_player->RemoveBattlegroundQueueId(bgQueueTypeId); // must be called this way, because if you move this call to queue->removeplayer, it causes bugs
|
||||
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0);
|
||||
bgQueue.RemovePlayer(_player->GetGUID(), true);
|
||||
// player left queue, we should update it - do not update Arena Queue
|
||||
if (!ginfo.ArenaType)
|
||||
sBattlegroundMgr->ScheduleQueueUpdate(ginfo.ArenaMatchmakerRating, ginfo.ArenaType, bgQueueTypeId, bgTypeId, bracketEntry->GetBracketId());
|
||||
SendPacket(&data);
|
||||
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "Battleground: player %s (%u) left queue for bgtype %u, queue type %u.", _player->GetName(), _player->GetGUIDLow(), bg->GetTypeID(), bgQueueTypeId);
|
||||
break;
|
||||
default:
|
||||
sLog->outError("Battleground port: unknown action %u", action);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleLeaveBattlefieldOpcode(WorldPacket& recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_LEAVE_BATTLEFIELD Message");
|
||||
|
||||
recv_data.read_skip<uint8>(); // unk1
|
||||
recv_data.read_skip<uint8>(); // unk2
|
||||
recv_data.read_skip<uint32>(); // BattlegroundTypeId
|
||||
recv_data.read_skip<uint16>(); // unk3
|
||||
|
||||
// not allow leave battleground in combat
|
||||
if (_player->isInCombat())
|
||||
if (Battleground* bg = _player->GetBattleground())
|
||||
if (bg->GetStatus() != STATUS_WAIT_LEAVE)
|
||||
return;
|
||||
|
||||
_player->LeaveBattleground();
|
||||
}
|
||||
|
||||
void WorldSession::HandleBattlefieldStatusOpcode(WorldPacket & /*recv_data*/)
|
||||
{
|
||||
// empty opcode
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Battleground status");
|
||||
|
||||
WorldPacket data;
|
||||
// we must update all queues here
|
||||
Battleground* bg = NULL;
|
||||
for (uint8 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
|
||||
{
|
||||
BattlegroundQueueTypeId bgQueueTypeId = _player->GetBattlegroundQueueTypeId(i);
|
||||
if (!bgQueueTypeId)
|
||||
continue;
|
||||
BattlegroundTypeId bgTypeId = BattlegroundMgr::BGTemplateId(bgQueueTypeId);
|
||||
uint8 arenaType = BattlegroundMgr::BGArenaType(bgQueueTypeId);
|
||||
if (bgTypeId == _player->GetBattlegroundTypeId())
|
||||
{
|
||||
bg = _player->GetBattleground();
|
||||
//i cannot check any variable from player class because player class doesn't know if player is in 2v2 / 3v3 or 5v5 arena
|
||||
//so i must use bg pointer to get that information
|
||||
if (bg && bg->GetArenaType() == arenaType)
|
||||
{
|
||||
// this line is checked, i only don't know if GetStartTime is changing itself after bg end!
|
||||
// send status in Battleground
|
||||
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, i, STATUS_IN_PROGRESS, bg->GetEndTime(), bg->GetStartTime(), arenaType);
|
||||
SendPacket(&data);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
//we are sending update to player about queue - he can be invited there!
|
||||
//get GroupQueueInfo for queue status
|
||||
BattlegroundQueue& bgQueue = sBattlegroundMgr->m_BattlegroundQueues[bgQueueTypeId];
|
||||
GroupQueueInfo ginfo;
|
||||
if (!bgQueue.GetPlayerGroupInfoData(_player->GetGUID(), &ginfo))
|
||||
continue;
|
||||
if (ginfo.IsInvitedToBGInstanceGUID)
|
||||
{
|
||||
bg = sBattlegroundMgr->GetBattleground(ginfo.IsInvitedToBGInstanceGUID, bgTypeId);
|
||||
if (!bg)
|
||||
continue;
|
||||
uint32 remainingTime = getMSTimeDiff(getMSTime(), ginfo.RemoveInviteTime);
|
||||
// send status invited to Battleground
|
||||
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, i, STATUS_WAIT_JOIN, remainingTime, 0, arenaType);
|
||||
SendPacket(&data);
|
||||
}
|
||||
else
|
||||
{
|
||||
bg = sBattlegroundMgr->GetBattlegroundTemplate(bgTypeId);
|
||||
if (!bg)
|
||||
continue;
|
||||
|
||||
// expected bracket entry
|
||||
PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bg->GetMapId(), _player->getLevel());
|
||||
if (!bracketEntry)
|
||||
continue;
|
||||
|
||||
uint32 avgTime = bgQueue.GetAverageQueueWaitTime(&ginfo, bracketEntry->GetBracketId());
|
||||
// send status in Battleground Queue
|
||||
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, i, STATUS_WAIT_QUEUE, avgTime, getMSTimeDiff(ginfo.JoinTime, getMSTime()), arenaType);
|
||||
SendPacket(&data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleAreaSpiritHealerQueryOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_AREA_SPIRIT_HEALER_QUERY");
|
||||
|
||||
Battleground* bg = _player->GetBattleground();
|
||||
|
||||
uint64 guid;
|
||||
recv_data >> guid;
|
||||
|
||||
Creature* unit = GetPlayer()->GetMap()->GetCreature(guid);
|
||||
if (!unit)
|
||||
return;
|
||||
|
||||
if (!unit->isSpiritService()) // it's not spirit service
|
||||
return;
|
||||
|
||||
if (bg)
|
||||
sBattlegroundMgr->SendAreaSpiritHealerQueryOpcode(_player, bg, guid);
|
||||
}
|
||||
|
||||
void WorldSession::HandleAreaSpiritHealerQueueOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_AREA_SPIRIT_HEALER_QUEUE");
|
||||
|
||||
Battleground* bg = _player->GetBattleground();
|
||||
|
||||
uint64 guid;
|
||||
recv_data >> guid;
|
||||
|
||||
Creature* unit = GetPlayer()->GetMap()->GetCreature(guid);
|
||||
if (!unit)
|
||||
return;
|
||||
|
||||
if (!unit->isSpiritService()) // it's not spirit service
|
||||
return;
|
||||
|
||||
if (bg)
|
||||
bg->AddPlayerToResurrectQueue(guid, _player->GetGUID());
|
||||
}
|
||||
|
||||
void WorldSession::HandleBattlemasterJoinArena(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_BATTLEMASTER_JOIN_ARENA");
|
||||
|
||||
uint64 guid; // arena Battlemaster guid
|
||||
uint8 arenaslot; // 2v2, 3v3 or 5v5
|
||||
uint8 asGroup; // asGroup
|
||||
uint8 isRated; // isRated
|
||||
Group* grp = NULL;
|
||||
|
||||
recv_data >> guid >> arenaslot >> asGroup >> isRated;
|
||||
|
||||
// ignore if we already in BG or BG queue
|
||||
if (_player->InBattleground())
|
||||
return;
|
||||
|
||||
Creature* unit = GetPlayer()->GetMap()->GetCreature(guid);
|
||||
if (!unit)
|
||||
return;
|
||||
|
||||
if (!unit->isBattleMaster()) // it's not battle master
|
||||
return;
|
||||
|
||||
uint8 arenatype = 0;
|
||||
uint32 arenaRating = 0;
|
||||
uint32 matchmakerRating = 0;
|
||||
|
||||
switch (arenaslot)
|
||||
{
|
||||
case 0:
|
||||
arenatype = ARENA_TYPE_2v2;
|
||||
break;
|
||||
case 1:
|
||||
arenatype = ARENA_TYPE_3v3;
|
||||
break;
|
||||
case 2:
|
||||
arenatype = ARENA_TYPE_5v5;
|
||||
break;
|
||||
default:
|
||||
sLog->outError("Unknown arena slot %u at HandleBattlemasterJoinArena()", arenaslot);
|
||||
return;
|
||||
}
|
||||
|
||||
//check existance
|
||||
Battleground* bg = sBattlegroundMgr->GetBattlegroundTemplate(BATTLEGROUND_AA);
|
||||
if (!bg)
|
||||
{
|
||||
sLog->outError("Battleground: template bg (all arenas) not found");
|
||||
return;
|
||||
}
|
||||
|
||||
if (DisableMgr::IsDisabledFor(DISABLE_TYPE_BATTLEGROUND, BATTLEGROUND_AA, NULL))
|
||||
{
|
||||
ChatHandler(this).PSendSysMessage(LANG_ARENA_DISABLED);
|
||||
return;
|
||||
}
|
||||
|
||||
BattlegroundTypeId bgTypeId = bg->GetTypeID();
|
||||
BattlegroundQueueTypeId bgQueueTypeId = BattlegroundMgr::BGQueueTypeId(bgTypeId, arenatype);
|
||||
PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bg->GetMapId(), _player->getLevel());
|
||||
if (!bracketEntry)
|
||||
return;
|
||||
|
||||
GroupJoinBattlegroundResult err = ERR_GROUP_JOIN_BATTLEGROUND_FAIL;
|
||||
|
||||
if (!asGroup)
|
||||
{
|
||||
// check if already in queue
|
||||
if (_player->GetBattlegroundQueueIndex(bgQueueTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES)
|
||||
//player is already in this queue
|
||||
return;
|
||||
// check if has free queue slots
|
||||
if (!_player->HasFreeBattlegroundQueueId())
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
grp = _player->GetGroup();
|
||||
// no group found, error
|
||||
if (!grp)
|
||||
return;
|
||||
if (grp->GetLeaderGUID() != _player->GetGUID())
|
||||
return;
|
||||
err = grp->CanJoinBattlegroundQueue(bg, bgQueueTypeId, arenatype, arenatype, (bool)isRated, arenaslot);
|
||||
}
|
||||
|
||||
uint32 ateamId = 0;
|
||||
|
||||
if (isRated)
|
||||
{
|
||||
ateamId = _player->GetArenaTeamId(arenaslot);
|
||||
// check real arenateam existence only here (if it was moved to group->CanJoin .. () then we would ahve to get it twice)
|
||||
ArenaTeam* at = sArenaTeamMgr->GetArenaTeamById(ateamId);
|
||||
if (!at)
|
||||
{
|
||||
_player->GetSession()->SendNotInArenaTeamPacket(arenatype);
|
||||
return;
|
||||
}
|
||||
// get the team rating for queueing
|
||||
arenaRating = at->GetRating();
|
||||
matchmakerRating = at->GetAverageMMR(grp);
|
||||
// the arenateam id must match for everyone in the group
|
||||
|
||||
if (arenaRating <= 0)
|
||||
arenaRating = 1;
|
||||
}
|
||||
|
||||
BattlegroundQueue &bgQueue = sBattlegroundMgr->m_BattlegroundQueues[bgQueueTypeId];
|
||||
if (asGroup)
|
||||
{
|
||||
uint32 avgTime = 0;
|
||||
|
||||
if (err > 0)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "Battleground: arena join as group start");
|
||||
if (isRated)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "Battleground: arena team id %u, leader %s queued with matchmaker rating %u for type %u", _player->GetArenaTeamId(arenaslot), _player->GetName(), matchmakerRating, arenatype);
|
||||
bg->SetRated(true);
|
||||
}
|
||||
else
|
||||
bg->SetRated(false);
|
||||
|
||||
GroupQueueInfo* ginfo = bgQueue.AddGroup(_player, grp, bgTypeId, bracketEntry, arenatype, isRated, false, arenaRating, matchmakerRating, ateamId);
|
||||
avgTime = bgQueue.GetAverageQueueWaitTime(ginfo, bracketEntry->GetBracketId());
|
||||
}
|
||||
|
||||
for (GroupReference* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
|
||||
{
|
||||
Player* member = itr->getSource();
|
||||
if (!member)
|
||||
continue;
|
||||
|
||||
WorldPacket data;
|
||||
|
||||
if (err <= 0)
|
||||
{
|
||||
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, err);
|
||||
member->GetSession()->SendPacket(&data);
|
||||
continue;
|
||||
}
|
||||
|
||||
// add to queue
|
||||
uint32 queueSlot = member->AddBattlegroundQueueId(bgQueueTypeId);
|
||||
|
||||
// send status packet (in queue)
|
||||
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0, arenatype);
|
||||
member->GetSession()->SendPacket(&data);
|
||||
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, err);
|
||||
member->GetSession()->SendPacket(&data);
|
||||
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "Battleground: player joined queue for arena as group bg queue type %u bg type %u: GUID %u, NAME %s", bgQueueTypeId, bgTypeId, member->GetGUIDLow(), member->GetName());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GroupQueueInfo* ginfo = bgQueue.AddGroup(_player, NULL, bgTypeId, bracketEntry, arenatype, isRated, false, arenaRating, matchmakerRating, ateamId);
|
||||
uint32 avgTime = bgQueue.GetAverageQueueWaitTime(ginfo, bracketEntry->GetBracketId());
|
||||
uint32 queueSlot = _player->AddBattlegroundQueueId(bgQueueTypeId);
|
||||
|
||||
WorldPacket data;
|
||||
// send status packet (in queue)
|
||||
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0, arenatype);
|
||||
SendPacket(&data);
|
||||
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "Battleground: player joined queue for arena, skirmish, bg queue type %u bg type %u: GUID %u, NAME %s", bgQueueTypeId, bgTypeId, _player->GetGUIDLow(), _player->GetName());
|
||||
}
|
||||
sBattlegroundMgr->ScheduleQueueUpdate(matchmakerRating, arenatype, bgQueueTypeId, bgTypeId, bracketEntry->GetBracketId());
|
||||
}
|
||||
|
||||
void WorldSession::HandleReportPvPAFK(WorldPacket & recv_data)
|
||||
{
|
||||
uint64 playerGuid;
|
||||
recv_data >> playerGuid;
|
||||
Player* reportedPlayer = ObjectAccessor::FindPlayer(playerGuid);
|
||||
|
||||
if (!reportedPlayer)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "WorldSession::HandleReportPvPAFK: player not found");
|
||||
return;
|
||||
}
|
||||
|
||||
sLog->outDebug(LOG_FILTER_BATTLEGROUND, "WorldSession::HandleReportPvPAFK: %s reported %s", _player->GetName(), reportedPlayer->GetName());
|
||||
|
||||
reportedPlayer->ReportedAfkBy(_player);
|
||||
}
|
||||
Executable
+309
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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 "Common.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
|
||||
#include "InstanceSaveMgr.h"
|
||||
#include "Log.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Player.h"
|
||||
|
||||
void WorldSession::HandleCalendarGetCalendar(WorldPacket& /*recv_data*/)
|
||||
{
|
||||
uint64 guid = _player->GetGUID();
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_CALENDAR_GET_CALENDAR [" UI64FMTD "]", guid);
|
||||
|
||||
time_t cur_time = time_t(time(NULL));
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_CALENDAR_SEND_CALENDAR [" UI64FMTD "]", guid);
|
||||
WorldPacket data(SMSG_CALENDAR_SEND_CALENDAR, 4+4*0+4+4*0+4+4);
|
||||
|
||||
data << uint32(0); // invite count
|
||||
/*
|
||||
for (;;)
|
||||
{
|
||||
uint64 inviteId;
|
||||
uint64 unkGuid0;
|
||||
uint8 unk1, unk2, unk3;
|
||||
uint64 creatorGuid;
|
||||
}
|
||||
*/
|
||||
|
||||
data << uint32(0); // event count
|
||||
/*
|
||||
for (;;)
|
||||
{
|
||||
uint64 eventId;
|
||||
std::string title; // 128 chars
|
||||
uint32 type;
|
||||
uint32 occurrenceTime;
|
||||
uint32 flags;
|
||||
uint32 unk4; -- possibly mapid for dungeon/raid
|
||||
uint64 creatorGuid;
|
||||
}
|
||||
*/
|
||||
|
||||
data << uint32(cur_time); // server time
|
||||
data << uint32(secsToTimeBitFields(cur_time)); // server time
|
||||
|
||||
uint32 counter = 0;
|
||||
size_t p_counter = data.wpos();
|
||||
data << uint32(counter); // instance save count
|
||||
|
||||
for (uint8 i = 0; i < MAX_DIFFICULTY; ++i)
|
||||
for (Player::BoundInstancesMap::const_iterator itr = _player->m_boundInstances[i].begin(); itr != _player->m_boundInstances[i].end(); ++itr)
|
||||
if (itr->second.perm)
|
||||
{
|
||||
InstanceSave const* save = itr->second.save;
|
||||
data << uint32(save->GetMapId());
|
||||
data << uint32(save->GetDifficulty());
|
||||
data << uint32(save->GetResetTime() - cur_time);
|
||||
data << uint64(save->GetInstanceId()); // instance save id as unique instance copy id
|
||||
++counter;
|
||||
}
|
||||
|
||||
data.put<uint32>(p_counter, counter);
|
||||
|
||||
data << uint32(1135753200); // unk (28.12.2005 07:00)
|
||||
|
||||
counter = 0;
|
||||
p_counter = data.wpos();
|
||||
data << uint32(counter); // raid reset count
|
||||
|
||||
std::set<uint32> sentMaps;
|
||||
|
||||
ResetTimeByMapDifficultyMap const& resets = sInstanceSaveMgr->GetResetTimeMap();
|
||||
for (ResetTimeByMapDifficultyMap::const_iterator itr = resets.begin(); itr != resets.end(); ++itr)
|
||||
{
|
||||
uint32 mapId = PAIR32_LOPART(itr->first);
|
||||
|
||||
if (sentMaps.find(mapId) != sentMaps.end())
|
||||
continue;
|
||||
|
||||
MapEntry const* mapEntry = sMapStore.LookupEntry(mapId);
|
||||
if (!mapEntry || !mapEntry->IsRaid())
|
||||
continue;
|
||||
|
||||
sentMaps.insert(mapId);
|
||||
|
||||
data << uint32(mapId);
|
||||
data << uint32(itr->second - cur_time);
|
||||
data << uint32(mapEntry->unk_time);
|
||||
++counter;
|
||||
}
|
||||
|
||||
data.put<uint32>(p_counter, counter);
|
||||
|
||||
data << uint32(0); // holiday count?
|
||||
/*
|
||||
for (;;)
|
||||
{
|
||||
uint32 unk5, unk6, unk7, unk8, unk9;
|
||||
for (uint32 j = 0; j < 26; ++j)
|
||||
{
|
||||
uint32 unk10;
|
||||
}
|
||||
for (uint32 j = 0; j < 10; ++j)
|
||||
{
|
||||
uint32 unk11;
|
||||
}
|
||||
for (uint32 j = 0; j < 10; ++j)
|
||||
{
|
||||
uint32 unk12;
|
||||
}
|
||||
std::string holidayName; // 64 chars
|
||||
}
|
||||
*/
|
||||
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarGetEvent(WorldPacket& recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_GET_EVENT");
|
||||
recv_data.read_skip<uint64>(); // unk
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarGuildFilter(WorldPacket& recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_GUILD_FILTER");
|
||||
recv_data.read_skip<uint32>(); // unk1
|
||||
recv_data.read_skip<uint32>(); // unk2
|
||||
recv_data.read_skip<uint32>(); // unk3
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarArenaTeam(WorldPacket& recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_ARENA_TEAM");
|
||||
recv_data.read_skip<uint32>(); // unk
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarAddEvent(WorldPacket& recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_ADD_EVENT");
|
||||
recv_data.rfinish(); // set to end to avoid warnings spam
|
||||
|
||||
//std::string unk1, unk2;
|
||||
//recv_data >> (std::string)unk1;
|
||||
//recv_data >> (std::string)unk2;
|
||||
|
||||
//uint8 unk3, unk4;
|
||||
//uint32 unk5, unk6, unk7, unk8, unk9, count = 0;
|
||||
//recv_data >> (uint8)unk3;
|
||||
//recv_data >> (uint8)unk4;
|
||||
//recv_data >> (uint32)unk5;
|
||||
//recv_data >> (uint32)unk6;
|
||||
//recv_data >> (uint32)unk7;
|
||||
//recv_data >> (uint32)unk8;
|
||||
//recv_data >> (uint32)unk9;
|
||||
//if (!((unk9 >> 6) & 1))
|
||||
//{
|
||||
// recv_data >> (uint32)count;
|
||||
// if (count)
|
||||
// {
|
||||
// uint8 unk12, unk13;
|
||||
// uint64 guid;
|
||||
// for (int i=0; i<count; i++)
|
||||
// {
|
||||
// recv_data.readPackGUID(guid);
|
||||
// recv_data >> (uint8)unk12;
|
||||
// recv_data >> (uint8)unk13;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarUpdateEvent(WorldPacket& recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_UPDATE_EVENT");
|
||||
recv_data.rfinish(); // set to end to avoid warnings spam
|
||||
|
||||
//recv_data >> uint64
|
||||
//recv_data >> uint64
|
||||
//recv_data >> std::string
|
||||
//recv_data >> std::string
|
||||
//recv_data >> uint8
|
||||
//recv_data >> uint8
|
||||
//recv_data >> uint32
|
||||
//recv_data >> uint32
|
||||
//recv_data >> uint32
|
||||
//recv_data >> uint32
|
||||
//recv_data >> uint32
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarRemoveEvent(WorldPacket& recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_REMOVE_EVENT");
|
||||
recv_data.rfinish(); // set to end to avoid warnings spam
|
||||
|
||||
//recv_data >> uint64
|
||||
//recv_data >> uint64
|
||||
//recv_data >> uint32
|
||||
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarCopyEvent(WorldPacket& recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_COPY_EVENT");
|
||||
recv_data.rfinish(); // set to end to avoid warnings spam
|
||||
|
||||
//recv_data >> uint64
|
||||
//recv_data >> uint64
|
||||
//recv_data >> uint32
|
||||
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarEventInvite(WorldPacket& recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_EVENT_INVITE");
|
||||
recv_data.rfinish(); // set to end to avoid warnings spam
|
||||
|
||||
//recv_data >> uint64
|
||||
//recv_data >> uint64
|
||||
//recv_data >> std::string
|
||||
//recv_data >> uint8
|
||||
//recv_data >> uint8
|
||||
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarEventRsvp(WorldPacket& recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_EVENT_RSVP");
|
||||
recv_data.rfinish(); // set to end to avoid warnings spam
|
||||
|
||||
//recv_data >> uint64
|
||||
//recv_data >> uint64
|
||||
//recv_data >> uint32
|
||||
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarEventRemoveInvite(WorldPacket& recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_EVENT_REMOVE_INVITE");
|
||||
recv_data.rfinish(); // set to end to avoid warnings spam
|
||||
|
||||
//recv_data.readPackGUID(guid)
|
||||
//recv_data >> uint64
|
||||
//recv_data >> uint64
|
||||
//recv_data >> uint64
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarEventStatus(WorldPacket& recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_EVENT_STATUS");
|
||||
recv_data.rfinish(); // set to end to avoid warnings spam
|
||||
|
||||
//recv_data.readPackGUID(guid)
|
||||
//recv_data >> uint64
|
||||
//recv_data >> uint64
|
||||
//recv_data >> uint64
|
||||
//recv_data >> uint32
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarEventModeratorStatus(WorldPacket& recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_EVENT_MODERATOR_STATUS");
|
||||
recv_data.rfinish(); // set to end to avoid warnings spam
|
||||
|
||||
//recv_data.readPackGUID(guid)
|
||||
//recv_data >> uint64
|
||||
//recv_data >> uint64
|
||||
//recv_data >> uint64
|
||||
//recv_data >> uint32
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarComplain(WorldPacket& recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_COMPLAIN");
|
||||
recv_data.rfinish(); // set to end to avoid warnings spam
|
||||
|
||||
//recv_data >> uint64
|
||||
//recv_data >> uint64
|
||||
//recv_data >> uint64
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarGetNumPending(WorldPacket& /*recv_data*/)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CALENDAR_GET_NUM_PENDING"); // empty
|
||||
|
||||
WorldPacket data(SMSG_CALENDAR_SEND_NUM_PENDING, 4);
|
||||
data << uint32(0); // 0 - no pending invites, 1 - some pending invites
|
||||
SendPacket(&data);
|
||||
}
|
||||
Executable
+302
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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 "ObjectMgr.h" // for normalizePlayerName
|
||||
#include "ChannelMgr.h"
|
||||
|
||||
void WorldSession::HandleJoinChannel(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
|
||||
|
||||
uint32 channel_id;
|
||||
uint8 unknown1, unknown2;
|
||||
std::string channelname, pass;
|
||||
|
||||
recvPacket >> channel_id;
|
||||
recvPacket >> unknown1 >> unknown2;
|
||||
recvPacket >> channelname;
|
||||
recvPacket >> pass;
|
||||
|
||||
if (channel_id)
|
||||
{
|
||||
ChatChannelsEntry const* channel = sChatChannelsStore.LookupEntry(channel_id);
|
||||
if (!channel)
|
||||
return;
|
||||
|
||||
AreaTableEntry const* current_zone = GetAreaEntryByAreaID(_player->GetZoneId());
|
||||
if (!current_zone)
|
||||
return;
|
||||
|
||||
if (!_player->CanJoinConstantChannelInZone(channel, current_zone))
|
||||
return;
|
||||
}
|
||||
|
||||
if (channelname.empty())
|
||||
return;
|
||||
|
||||
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
|
||||
{
|
||||
cMgr->team = _player->GetTeam();
|
||||
if (Channel* chn = cMgr->GetJoinChannel(channelname, channel_id))
|
||||
chn->Join(_player->GetGUID(), pass.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleLeaveChannel(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
|
||||
|
||||
uint32 unk;
|
||||
std::string channelname;
|
||||
recvPacket >> unk; // channel id?
|
||||
recvPacket >> channelname;
|
||||
|
||||
if (channelname.empty())
|
||||
return;
|
||||
|
||||
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
|
||||
{
|
||||
if (Channel* chn = cMgr->GetChannel(channelname, _player))
|
||||
chn->Leave(_player->GetGUID(), true);
|
||||
cMgr->LeftChannel(channelname);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelList(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
|
||||
std::string channelname;
|
||||
recvPacket >> channelname;
|
||||
|
||||
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
|
||||
if (Channel* chn = cMgr->GetChannel(channelname, _player))
|
||||
chn->List(_player);
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelPassword(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
|
||||
std::string channelname, pass;
|
||||
recvPacket >> channelname;
|
||||
|
||||
recvPacket >> pass;
|
||||
|
||||
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
|
||||
if (Channel* chn = cMgr->GetChannel(channelname, _player))
|
||||
chn->Password(_player->GetGUID(), pass.c_str());
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelSetOwner(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
|
||||
std::string channelname, newp;
|
||||
recvPacket >> channelname;
|
||||
|
||||
recvPacket >> newp;
|
||||
|
||||
if (!normalizePlayerName(newp))
|
||||
return;
|
||||
|
||||
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
|
||||
if (Channel* chn = cMgr->GetChannel(channelname, _player))
|
||||
chn->SetOwner(_player->GetGUID(), newp.c_str());
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelOwner(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
|
||||
std::string channelname;
|
||||
recvPacket >> channelname;
|
||||
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
|
||||
if (Channel* chn = cMgr->GetChannel(channelname, _player))
|
||||
chn->SendWhoOwner(_player->GetGUID());
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelModerator(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
|
||||
std::string channelname, otp;
|
||||
recvPacket >> channelname;
|
||||
|
||||
recvPacket >> otp;
|
||||
|
||||
if (!normalizePlayerName(otp))
|
||||
return;
|
||||
|
||||
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
|
||||
if (Channel* chn = cMgr->GetChannel(channelname, _player))
|
||||
chn->SetModerator(_player->GetGUID(), otp.c_str());
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelUnmoderator(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
|
||||
std::string channelname, otp;
|
||||
recvPacket >> channelname;
|
||||
|
||||
recvPacket >> otp;
|
||||
|
||||
if (!normalizePlayerName(otp))
|
||||
return;
|
||||
|
||||
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
|
||||
if (Channel* chn = cMgr->GetChannel(channelname, _player))
|
||||
chn->UnsetModerator(_player->GetGUID(), otp.c_str());
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelMute(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
|
||||
std::string channelname, otp;
|
||||
recvPacket >> channelname;
|
||||
|
||||
recvPacket >> otp;
|
||||
|
||||
if (!normalizePlayerName(otp))
|
||||
return;
|
||||
|
||||
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
|
||||
if (Channel* chn = cMgr->GetChannel(channelname, _player))
|
||||
chn->SetMute(_player->GetGUID(), otp.c_str());
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelUnmute(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
|
||||
|
||||
std::string channelname, otp;
|
||||
recvPacket >> channelname;
|
||||
|
||||
recvPacket >> otp;
|
||||
|
||||
if (!normalizePlayerName(otp))
|
||||
return;
|
||||
|
||||
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
|
||||
if (Channel* chn = cMgr->GetChannel(channelname, _player))
|
||||
chn->UnsetMute(_player->GetGUID(), otp.c_str());
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelInvite(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
|
||||
std::string channelname, otp;
|
||||
recvPacket >> channelname;
|
||||
|
||||
recvPacket >> otp;
|
||||
|
||||
if (!normalizePlayerName(otp))
|
||||
return;
|
||||
|
||||
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
|
||||
if (Channel* chn = cMgr->GetChannel(channelname, _player))
|
||||
chn->Invite(_player->GetGUID(), otp.c_str());
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelKick(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
|
||||
std::string channelname, otp;
|
||||
recvPacket >> channelname;
|
||||
|
||||
recvPacket >> otp;
|
||||
if (!normalizePlayerName(otp))
|
||||
return;
|
||||
|
||||
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
|
||||
if (Channel* chn = cMgr->GetChannel(channelname, _player))
|
||||
chn->Kick(_player->GetGUID(), otp.c_str());
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelBan(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
|
||||
std::string channelname, otp;
|
||||
recvPacket >> channelname;
|
||||
|
||||
recvPacket >> otp;
|
||||
|
||||
if (!normalizePlayerName(otp))
|
||||
return;
|
||||
|
||||
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
|
||||
if (Channel* chn = cMgr->GetChannel(channelname, _player))
|
||||
chn->Ban(_player->GetGUID(), otp.c_str());
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelUnban(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
|
||||
|
||||
std::string channelname, otp;
|
||||
recvPacket >> channelname;
|
||||
|
||||
recvPacket >> otp;
|
||||
|
||||
if (!normalizePlayerName(otp))
|
||||
return;
|
||||
|
||||
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
|
||||
if (Channel* chn = cMgr->GetChannel(channelname, _player))
|
||||
chn->UnBan(_player->GetGUID(), otp.c_str());
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelAnnouncements(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
|
||||
std::string channelname;
|
||||
recvPacket >> channelname;
|
||||
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
|
||||
if (Channel* chn = cMgr->GetChannel(channelname, _player))
|
||||
chn->Announce(_player->GetGUID());
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelDisplayListQuery(WorldPacket &recvPacket)
|
||||
{
|
||||
// this should be OK because the 2 function _were_ the same
|
||||
HandleChannelList(recvPacket);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGetChannelMemberCount(WorldPacket &recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
|
||||
std::string channelname;
|
||||
recvPacket >> channelname;
|
||||
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
|
||||
{
|
||||
if (Channel* chn = cMgr->GetChannel(channelname, _player))
|
||||
{
|
||||
WorldPacket data(SMSG_CHANNEL_MEMBER_COUNT, chn->GetName().size()+1+1+4);
|
||||
data << chn->GetName();
|
||||
data << uint8(chn->GetFlags());
|
||||
data << uint32(chn->GetNumPlayers());
|
||||
SendPacket(&data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleSetChannelWatch(WorldPacket &recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
|
||||
std::string channelname;
|
||||
recvPacket >> channelname;
|
||||
/*if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
|
||||
if (Channel* chn = cMgr->GetChannel(channelname, _player))
|
||||
chn->JoinNotify(_player->GetGUID());*/
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Executable
+635
@@ -0,0 +1,635 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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 "Common.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "GuildMgr.h"
|
||||
#include "World.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
#include "DatabaseEnv.h"
|
||||
|
||||
#include "CellImpl.h"
|
||||
#include "Chat.h"
|
||||
#include "ChannelMgr.h"
|
||||
#include "GridNotifiersImpl.h"
|
||||
#include "Group.h"
|
||||
#include "Guild.h"
|
||||
#include "Language.h"
|
||||
#include "Log.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Player.h"
|
||||
#include "SpellAuras.h"
|
||||
#include "SpellAuraEffects.h"
|
||||
#include "Util.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "AccountMgr.h"
|
||||
|
||||
bool WorldSession::processChatmessageFurtherAfterSecurityChecks(std::string& msg, uint32 lang)
|
||||
{
|
||||
if (lang != LANG_ADDON)
|
||||
{
|
||||
// strip invisible characters for non-addon messages
|
||||
if (sWorld->getBoolConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
|
||||
stripLineInvisibleChars(msg);
|
||||
|
||||
if (sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY) && AccountMgr::IsPlayerAccount(GetSecurity())
|
||||
&& !ChatHandler(this).isValidChatMessage(msg.c_str()))
|
||||
{
|
||||
sLog->outError("Player %s (GUID: %u) sent a chatmessage with an invalid link: %s", GetPlayer()->GetName(),
|
||||
GetPlayer()->GetGUIDLow(), msg.c_str());
|
||||
if (sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_KICK))
|
||||
KickPlayer();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WorldSession::HandleMessagechatOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
uint32 type;
|
||||
uint32 lang;
|
||||
|
||||
recv_data >> type;
|
||||
recv_data >> lang;
|
||||
|
||||
if (type >= MAX_CHAT_MSG_TYPE)
|
||||
{
|
||||
sLog->outError("CHAT: Wrong message type received: %u", type);
|
||||
recv_data.rfinish();
|
||||
return;
|
||||
}
|
||||
|
||||
Player* sender = GetPlayer();
|
||||
|
||||
//sLog->outDebug("CHAT: packet received. type %u, lang %u", type, lang);
|
||||
|
||||
// prevent talking at unknown language (cheating)
|
||||
LanguageDesc const* langDesc = GetLanguageDescByID(lang);
|
||||
if (!langDesc)
|
||||
{
|
||||
SendNotification(LANG_UNKNOWN_LANGUAGE);
|
||||
recv_data.rfinish();
|
||||
return;
|
||||
}
|
||||
if (langDesc->skill_id != 0 && !sender->HasSkill(langDesc->skill_id))
|
||||
{
|
||||
// also check SPELL_AURA_COMPREHEND_LANGUAGE (client offers option to speak in that language)
|
||||
Unit::AuraEffectList const& langAuras = sender->GetAuraEffectsByType(SPELL_AURA_COMPREHEND_LANGUAGE);
|
||||
bool foundAura = false;
|
||||
for (Unit::AuraEffectList::const_iterator i = langAuras.begin(); i != langAuras.end(); ++i)
|
||||
{
|
||||
if ((*i)->GetMiscValue() == int32(lang))
|
||||
{
|
||||
foundAura = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundAura)
|
||||
{
|
||||
SendNotification(LANG_NOT_LEARNED_LANGUAGE);
|
||||
recv_data.rfinish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (lang == LANG_ADDON)
|
||||
{
|
||||
if (sWorld->getBoolConfig(CONFIG_CHATLOG_ADDON))
|
||||
{
|
||||
std::string msg = "";
|
||||
recv_data >> msg;
|
||||
|
||||
if (msg.empty())
|
||||
return;
|
||||
|
||||
sScriptMgr->OnPlayerChat(sender, uint32(CHAT_MSG_ADDON), lang, msg);
|
||||
}
|
||||
|
||||
// Disabled addon channel?
|
||||
if (!sWorld->getBoolConfig(CONFIG_ADDON_CHANNEL))
|
||||
return;
|
||||
}
|
||||
// LANG_ADDON should not be changed nor be affected by flood control
|
||||
else
|
||||
{
|
||||
// send in universal language if player in .gmon mode (ignore spell effects)
|
||||
if (sender->isGameMaster())
|
||||
lang = LANG_UNIVERSAL;
|
||||
else
|
||||
{
|
||||
// send in universal language in two side iteration allowed mode
|
||||
if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT))
|
||||
lang = LANG_UNIVERSAL;
|
||||
else
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case CHAT_MSG_PARTY:
|
||||
case CHAT_MSG_PARTY_LEADER:
|
||||
case CHAT_MSG_RAID:
|
||||
case CHAT_MSG_RAID_LEADER:
|
||||
case CHAT_MSG_RAID_WARNING:
|
||||
// allow two side chat at group channel if two side group allowed
|
||||
if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP))
|
||||
lang = LANG_UNIVERSAL;
|
||||
break;
|
||||
case CHAT_MSG_GUILD:
|
||||
case CHAT_MSG_OFFICER:
|
||||
// allow two side chat at guild channel if two side guild allowed
|
||||
if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD))
|
||||
lang = LANG_UNIVERSAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// but overwrite it by SPELL_AURA_MOD_LANGUAGE auras (only single case used)
|
||||
Unit::AuraEffectList const& ModLangAuras = sender->GetAuraEffectsByType(SPELL_AURA_MOD_LANGUAGE);
|
||||
if (!ModLangAuras.empty())
|
||||
lang = ModLangAuras.front()->GetMiscValue();
|
||||
}
|
||||
|
||||
if (!sender->CanSpeak())
|
||||
{
|
||||
std::string timeStr = secsToTimeString(m_muteTime - time(NULL));
|
||||
SendNotification(GetTrinityString(LANG_WAIT_BEFORE_SPEAKING), timeStr.c_str());
|
||||
recv_data.rfinish(); // Prevent warnings
|
||||
return;
|
||||
}
|
||||
|
||||
if (type != CHAT_MSG_AFK && type != CHAT_MSG_DND)
|
||||
sender->UpdateSpeakTime();
|
||||
}
|
||||
|
||||
if (sender->HasAura(1852) && type != CHAT_MSG_WHISPER)
|
||||
{
|
||||
std::string msg="";
|
||||
recv_data >> msg;
|
||||
|
||||
SendNotification(GetTrinityString(LANG_GM_SILENCE), sender->GetName());
|
||||
return;
|
||||
}
|
||||
|
||||
std::string to, channel, msg;
|
||||
bool ignoreChecks = false;
|
||||
switch (type)
|
||||
{
|
||||
case CHAT_MSG_SAY:
|
||||
case CHAT_MSG_EMOTE:
|
||||
case CHAT_MSG_YELL:
|
||||
case CHAT_MSG_PARTY:
|
||||
case CHAT_MSG_PARTY_LEADER:
|
||||
case CHAT_MSG_GUILD:
|
||||
case CHAT_MSG_OFFICER:
|
||||
case CHAT_MSG_RAID:
|
||||
case CHAT_MSG_RAID_LEADER:
|
||||
case CHAT_MSG_RAID_WARNING:
|
||||
case CHAT_MSG_BATTLEGROUND:
|
||||
case CHAT_MSG_BATTLEGROUND_LEADER:
|
||||
recv_data >> msg;
|
||||
break;
|
||||
case CHAT_MSG_WHISPER:
|
||||
recv_data >> to;
|
||||
recv_data >> msg;
|
||||
break;
|
||||
case CHAT_MSG_CHANNEL:
|
||||
recv_data >> channel;
|
||||
recv_data >> msg;
|
||||
break;
|
||||
case CHAT_MSG_AFK:
|
||||
case CHAT_MSG_DND:
|
||||
recv_data >> msg;
|
||||
ignoreChecks = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ignoreChecks)
|
||||
{
|
||||
if (msg.empty())
|
||||
return;
|
||||
|
||||
if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
|
||||
return;
|
||||
|
||||
if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
|
||||
return;
|
||||
|
||||
if (msg.empty())
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case CHAT_MSG_SAY:
|
||||
case CHAT_MSG_EMOTE:
|
||||
case CHAT_MSG_YELL:
|
||||
{
|
||||
if (sender->getLevel() < sWorld->getIntConfig(CONFIG_CHAT_SAY_LEVEL_REQ))
|
||||
{
|
||||
SendNotification(GetTrinityString(LANG_SAY_REQ), sWorld->getIntConfig(CONFIG_CHAT_SAY_LEVEL_REQ));
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == CHAT_MSG_SAY)
|
||||
sender->Say(msg, lang);
|
||||
else if (type == CHAT_MSG_EMOTE)
|
||||
sender->TextEmote(msg);
|
||||
else if (type == CHAT_MSG_YELL)
|
||||
sender->Yell(msg, lang);
|
||||
} break;
|
||||
case CHAT_MSG_WHISPER:
|
||||
{
|
||||
if (sender->getLevel() < sWorld->getIntConfig(CONFIG_CHAT_WHISPER_LEVEL_REQ))
|
||||
{
|
||||
SendNotification(GetTrinityString(LANG_WHISPER_REQ), sWorld->getIntConfig(CONFIG_CHAT_WHISPER_LEVEL_REQ));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!normalizePlayerName(to))
|
||||
{
|
||||
SendPlayerNotFoundNotice(to);
|
||||
break;
|
||||
}
|
||||
|
||||
Player* receiver = sObjectAccessor->FindPlayerByName(to.c_str());
|
||||
bool senderIsPlayer = AccountMgr::IsPlayerAccount(GetSecurity());
|
||||
bool receiverIsPlayer = AccountMgr::IsPlayerAccount(receiver ? receiver->GetSession()->GetSecurity() : SEC_PLAYER);
|
||||
if (!receiver || (senderIsPlayer && !receiverIsPlayer && !receiver->isAcceptWhispers() && !receiver->IsInWhisperWhiteList(sender->GetGUID())))
|
||||
{
|
||||
SendPlayerNotFoundNotice(to);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT) && senderIsPlayer && receiverIsPlayer)
|
||||
if (GetPlayer()->GetTeam() != receiver->GetTeam())
|
||||
{
|
||||
SendWrongFactionNotice();
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetPlayer()->HasAura(1852) && !receiver->isGameMaster())
|
||||
{
|
||||
SendNotification(GetTrinityString(LANG_GM_SILENCE), GetPlayer()->GetName());
|
||||
return;
|
||||
}
|
||||
|
||||
// If player is a Gamemaster and doesn't accept whisper, we auto-whitelist every player that the Gamemaster is talking to
|
||||
if (!senderIsPlayer && !sender->isAcceptWhispers() && !sender->IsInWhisperWhiteList(receiver->GetGUID()))
|
||||
sender->AddWhisperWhiteList(receiver->GetGUID());
|
||||
|
||||
GetPlayer()->Whisper(msg, lang, receiver->GetGUID());
|
||||
} break;
|
||||
case CHAT_MSG_PARTY:
|
||||
case CHAT_MSG_PARTY_LEADER:
|
||||
{
|
||||
// if player is in battleground, he cannot say to battleground members by /p
|
||||
Group* group = GetPlayer()->GetOriginalGroup();
|
||||
if (!group)
|
||||
{
|
||||
group = _player->GetGroup();
|
||||
if (!group || group->isBGGroup())
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == CHAT_MSG_PARTY_LEADER && !group->IsLeader(_player->GetGUID()))
|
||||
return;
|
||||
|
||||
sScriptMgr->OnPlayerChat(GetPlayer(), type, lang, msg, group);
|
||||
|
||||
WorldPacket data;
|
||||
ChatHandler::FillMessageData(&data, this, uint8(type), lang, NULL, 0, msg.c_str(), NULL);
|
||||
group->BroadcastPacket(&data, false, group->GetMemberGroup(GetPlayer()->GetGUID()));
|
||||
} break;
|
||||
case CHAT_MSG_GUILD:
|
||||
{
|
||||
if (GetPlayer()->GetGuildId())
|
||||
{
|
||||
if (Guild* guild = sGuildMgr->GetGuildById(GetPlayer()->GetGuildId()))
|
||||
{
|
||||
sScriptMgr->OnPlayerChat(GetPlayer(), type, lang, msg, guild);
|
||||
|
||||
guild->BroadcastToGuild(this, false, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case CHAT_MSG_OFFICER:
|
||||
{
|
||||
if (GetPlayer()->GetGuildId())
|
||||
{
|
||||
if (Guild* guild = sGuildMgr->GetGuildById(GetPlayer()->GetGuildId()))
|
||||
{
|
||||
sScriptMgr->OnPlayerChat(GetPlayer(), type, lang, msg, guild);
|
||||
|
||||
guild->BroadcastToGuild(this, true, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case CHAT_MSG_RAID:
|
||||
{
|
||||
// if player is in battleground, he cannot say to battleground members by /ra
|
||||
Group* group = GetPlayer()->GetOriginalGroup();
|
||||
if (!group)
|
||||
{
|
||||
group = GetPlayer()->GetGroup();
|
||||
if (!group || group->isBGGroup() || !group->isRaidGroup())
|
||||
return;
|
||||
}
|
||||
|
||||
sScriptMgr->OnPlayerChat(GetPlayer(), type, lang, msg, group);
|
||||
|
||||
WorldPacket data;
|
||||
ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID, lang, "", 0, msg.c_str(), NULL);
|
||||
group->BroadcastPacket(&data, false);
|
||||
} break;
|
||||
case CHAT_MSG_RAID_LEADER:
|
||||
{
|
||||
// if player is in battleground, he cannot say to battleground members by /ra
|
||||
Group* group = GetPlayer()->GetOriginalGroup();
|
||||
if (!group)
|
||||
{
|
||||
group = GetPlayer()->GetGroup();
|
||||
if (!group || group->isBGGroup() || !group->isRaidGroup() || !group->IsLeader(_player->GetGUID()))
|
||||
return;
|
||||
}
|
||||
|
||||
sScriptMgr->OnPlayerChat(GetPlayer(), type, lang, msg, group);
|
||||
|
||||
WorldPacket data;
|
||||
ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID_LEADER, lang, "", 0, msg.c_str(), NULL);
|
||||
group->BroadcastPacket(&data, false);
|
||||
} break;
|
||||
case CHAT_MSG_RAID_WARNING:
|
||||
{
|
||||
Group* group = GetPlayer()->GetGroup();
|
||||
if (!group || !group->isRaidGroup() || !(group->IsLeader(GetPlayer()->GetGUID()) || group->IsAssistant(GetPlayer()->GetGUID())) || group->isBGGroup())
|
||||
return;
|
||||
|
||||
sScriptMgr->OnPlayerChat(GetPlayer(), type, lang, msg, group);
|
||||
|
||||
WorldPacket data;
|
||||
//in battleground, raid warning is sent only to players in battleground - code is ok
|
||||
ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID_WARNING, lang, "", 0, msg.c_str(), NULL);
|
||||
group->BroadcastPacket(&data, false);
|
||||
} break;
|
||||
case CHAT_MSG_BATTLEGROUND:
|
||||
{
|
||||
//battleground raid is always in Player->GetGroup(), never in GetOriginalGroup()
|
||||
Group* group = GetPlayer()->GetGroup();
|
||||
if (!group || !group->isBGGroup())
|
||||
return;
|
||||
|
||||
sScriptMgr->OnPlayerChat(GetPlayer(), type, lang, msg, group);
|
||||
|
||||
WorldPacket data;
|
||||
ChatHandler::FillMessageData(&data, this, CHAT_MSG_BATTLEGROUND, lang, "", 0, msg.c_str(), NULL);
|
||||
group->BroadcastPacket(&data, false);
|
||||
} break;
|
||||
case CHAT_MSG_BATTLEGROUND_LEADER:
|
||||
{
|
||||
// battleground raid is always in Player->GetGroup(), never in GetOriginalGroup()
|
||||
Group* group = GetPlayer()->GetGroup();
|
||||
if (!group || !group->isBGGroup() || !group->IsLeader(GetPlayer()->GetGUID()))
|
||||
return;
|
||||
|
||||
sScriptMgr->OnPlayerChat(GetPlayer(), type, lang, msg, group);
|
||||
|
||||
WorldPacket data;
|
||||
ChatHandler::FillMessageData(&data, this, CHAT_MSG_BATTLEGROUND_LEADER, lang, "", 0, msg.c_str(), NULL);
|
||||
group->BroadcastPacket(&data, false);
|
||||
} break;
|
||||
case CHAT_MSG_CHANNEL:
|
||||
{
|
||||
if (AccountMgr::IsPlayerAccount(GetSecurity()))
|
||||
{
|
||||
if (_player->getLevel() < sWorld->getIntConfig(CONFIG_CHAT_CHANNEL_LEVEL_REQ))
|
||||
{
|
||||
SendNotification(GetTrinityString(LANG_CHANNEL_REQ), sWorld->getIntConfig(CONFIG_CHAT_CHANNEL_LEVEL_REQ));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
|
||||
{
|
||||
|
||||
if (Channel* chn = cMgr->GetChannel(channel, _player))
|
||||
{
|
||||
sScriptMgr->OnPlayerChat(_player, type, lang, msg, chn);
|
||||
|
||||
chn->Say(_player->GetGUID(), msg.c_str(), lang);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case CHAT_MSG_AFK:
|
||||
{
|
||||
if ((msg.empty() || !_player->isAFK()) && !_player->isInCombat())
|
||||
{
|
||||
if (!_player->isAFK())
|
||||
{
|
||||
if (msg.empty())
|
||||
msg = GetTrinityString(LANG_PLAYER_AFK_DEFAULT);
|
||||
_player->afkMsg = msg;
|
||||
}
|
||||
|
||||
sScriptMgr->OnPlayerChat(_player, type, lang, msg);
|
||||
|
||||
_player->ToggleAFK();
|
||||
if (_player->isAFK() && _player->isDND())
|
||||
_player->ToggleDND();
|
||||
}
|
||||
} break;
|
||||
case CHAT_MSG_DND:
|
||||
{
|
||||
if (msg.empty() || !_player->isDND())
|
||||
{
|
||||
if (!_player->isDND())
|
||||
{
|
||||
if (msg.empty())
|
||||
msg = GetTrinityString(LANG_PLAYER_DND_DEFAULT);
|
||||
_player->dndMsg = msg;
|
||||
}
|
||||
|
||||
sScriptMgr->OnPlayerChat(_player, type, lang, msg);
|
||||
|
||||
_player->ToggleDND();
|
||||
if (_player->isDND() && _player->isAFK())
|
||||
_player->ToggleAFK();
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
sLog->outError("CHAT: unknown message type %u, lang: %u", type, lang);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleEmoteOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
if (!GetPlayer()->isAlive() || GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
return;
|
||||
|
||||
uint32 emote;
|
||||
recv_data >> emote;
|
||||
sScriptMgr->OnPlayerEmote(GetPlayer(), emote);
|
||||
GetPlayer()->HandleEmoteCommand(emote);
|
||||
}
|
||||
|
||||
namespace Trinity
|
||||
{
|
||||
class EmoteChatBuilder
|
||||
{
|
||||
public:
|
||||
EmoteChatBuilder(Player const& player, uint32 text_emote, uint32 emote_num, Unit const* target)
|
||||
: i_player(player), i_text_emote(text_emote), i_emote_num(emote_num), i_target(target) {}
|
||||
|
||||
void operator()(WorldPacket& data, LocaleConstant loc_idx)
|
||||
{
|
||||
char const* nam = i_target ? i_target->GetNameForLocaleIdx(loc_idx) : NULL;
|
||||
uint32 namlen = (nam ? strlen(nam) : 0) + 1;
|
||||
|
||||
data.Initialize(SMSG_TEXT_EMOTE, (20+namlen));
|
||||
data << i_player.GetGUID();
|
||||
data << (uint32)i_text_emote;
|
||||
data << i_emote_num;
|
||||
data << (uint32)namlen;
|
||||
if (namlen > 1)
|
||||
data.append(nam, namlen);
|
||||
else
|
||||
data << (uint8)0x00;
|
||||
}
|
||||
|
||||
private:
|
||||
Player const& i_player;
|
||||
uint32 i_text_emote;
|
||||
uint32 i_emote_num;
|
||||
Unit const* i_target;
|
||||
};
|
||||
} // namespace Trinity
|
||||
|
||||
void WorldSession::HandleTextEmoteOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
if (!GetPlayer()->isAlive())
|
||||
return;
|
||||
|
||||
if (!GetPlayer()->CanSpeak())
|
||||
{
|
||||
std::string timeStr = secsToTimeString(m_muteTime - time(NULL));
|
||||
SendNotification(GetTrinityString(LANG_WAIT_BEFORE_SPEAKING), timeStr.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 text_emote, emoteNum;
|
||||
uint64 guid;
|
||||
|
||||
recv_data >> text_emote;
|
||||
recv_data >> emoteNum;
|
||||
recv_data >> guid;
|
||||
|
||||
sScriptMgr->OnPlayerTextEmote(GetPlayer(), text_emote, emoteNum, guid);
|
||||
|
||||
EmotesTextEntry const* em = sEmotesTextStore.LookupEntry(text_emote);
|
||||
if (!em)
|
||||
return;
|
||||
|
||||
uint32 emote_anim = em->textid;
|
||||
|
||||
switch (emote_anim)
|
||||
{
|
||||
case EMOTE_STATE_SLEEP:
|
||||
case EMOTE_STATE_SIT:
|
||||
case EMOTE_STATE_KNEEL:
|
||||
case EMOTE_ONESHOT_NONE:
|
||||
break;
|
||||
default:
|
||||
// Only allow text-emotes for "dead" entities (feign death included)
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
break;
|
||||
GetPlayer()->HandleEmoteCommand(emote_anim);
|
||||
break;
|
||||
}
|
||||
|
||||
Unit* unit = ObjectAccessor::GetUnit(*_player, guid);
|
||||
|
||||
CellCoord p = Trinity::ComputeCellCoord(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY());
|
||||
|
||||
Cell cell(p);
|
||||
cell.SetNoCreate();
|
||||
|
||||
Trinity::EmoteChatBuilder emote_builder(*GetPlayer(), text_emote, emoteNum, unit);
|
||||
Trinity::LocalizedPacketDo<Trinity::EmoteChatBuilder > emote_do(emote_builder);
|
||||
Trinity::PlayerDistWorker<Trinity::LocalizedPacketDo<Trinity::EmoteChatBuilder > > emote_worker(GetPlayer(), sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE), emote_do);
|
||||
TypeContainerVisitor<Trinity::PlayerDistWorker<Trinity::LocalizedPacketDo<Trinity::EmoteChatBuilder> >, WorldTypeMapContainer> message(emote_worker);
|
||||
cell.Visit(p, message, *GetPlayer()->GetMap(), *GetPlayer(), sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE));
|
||||
|
||||
GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE, text_emote, 0, unit);
|
||||
|
||||
//Send scripted event call
|
||||
if (unit && unit->GetTypeId() == TYPEID_UNIT && ((Creature*)unit)->AI())
|
||||
((Creature*)unit)->AI()->ReceiveEmote(GetPlayer(), text_emote);
|
||||
}
|
||||
|
||||
void WorldSession::HandleChatIgnoredOpcode(WorldPacket& recv_data)
|
||||
{
|
||||
uint64 iguid;
|
||||
uint8 unk;
|
||||
//sLog->outDebug(LOG_FILTER_PACKETIO, "WORLD: Received CMSG_CHAT_IGNORED");
|
||||
|
||||
recv_data >> iguid;
|
||||
recv_data >> unk; // probably related to spam reporting
|
||||
|
||||
Player* player = ObjectAccessor::FindPlayer(iguid);
|
||||
if (!player || !player->GetSession())
|
||||
return;
|
||||
|
||||
WorldPacket data;
|
||||
ChatHandler::FillMessageData(&data, this, CHAT_MSG_IGNORED, LANG_UNIVERSAL, NULL, GetPlayer()->GetGUID(), GetPlayer()->GetName(), NULL);
|
||||
player->GetSession()->SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelDeclineInvite(WorldPacket &recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
|
||||
}
|
||||
|
||||
void WorldSession::SendPlayerNotFoundNotice(std::string name)
|
||||
{
|
||||
WorldPacket data(SMSG_CHAT_PLAYER_NOT_FOUND, name.size()+1);
|
||||
data << name;
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::SendPlayerAmbiguousNotice(std::string name)
|
||||
{
|
||||
WorldPacket data(SMSG_CHAT_PLAYER_AMBIGUOUS, name.size()+1);
|
||||
data << name;
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::SendWrongFactionNotice()
|
||||
{
|
||||
WorldPacket data(SMSG_CHAT_WRONG_FACTION, 0);
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::SendChatRestrictedNotice(ChatRestrictionType restriction)
|
||||
{
|
||||
WorldPacket data(SMSG_CHAT_RESTRICTED, 1);
|
||||
data << uint8(restriction);
|
||||
SendPacket(&data);
|
||||
}
|
||||
Executable
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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 "Common.h"
|
||||
#include "Log.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "CreatureAI.h"
|
||||
#include "ObjectDefines.h"
|
||||
#include "Vehicle.h"
|
||||
#include "VehicleDefines.h"
|
||||
|
||||
void WorldSession::HandleAttackSwingOpcode(WorldPacket& recv_data)
|
||||
{
|
||||
uint64 guid;
|
||||
recv_data >> guid;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_ATTACKSWING Message guidlow:%u guidhigh:%u", GUID_LOPART(guid), GUID_HIPART(guid));
|
||||
|
||||
Unit* pEnemy = ObjectAccessor::GetUnit(*_player, guid);
|
||||
|
||||
if (!pEnemy)
|
||||
{
|
||||
// stop attack state at client
|
||||
SendAttackStop(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_player->IsValidAttackTarget(pEnemy))
|
||||
{
|
||||
// stop attack state at client
|
||||
SendAttackStop(pEnemy);
|
||||
return;
|
||||
}
|
||||
|
||||
//! Client explicitly checks the following before sending CMSG_ATTACKSWING packet,
|
||||
//! so we'll place the same check here. Note that it might be possible to reuse this snippet
|
||||
//! in other places as well.
|
||||
if (Vehicle* vehicle = _player->GetVehicle())
|
||||
{
|
||||
VehicleSeatEntry const* seat = vehicle->GetSeatForPassenger(_player);
|
||||
ASSERT(seat);
|
||||
if (!(seat->m_flags & VEHICLE_SEAT_FLAG_CAN_ATTACK))
|
||||
{
|
||||
SendAttackStop(pEnemy);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_player->Attack(pEnemy, true);
|
||||
}
|
||||
|
||||
void WorldSession::HandleAttackStopOpcode(WorldPacket & /*recv_data*/)
|
||||
{
|
||||
GetPlayer()->AttackStop();
|
||||
}
|
||||
|
||||
void WorldSession::HandleSetSheathedOpcode(WorldPacket& recv_data)
|
||||
{
|
||||
uint32 sheathed;
|
||||
recv_data >> sheathed;
|
||||
|
||||
//sLog->outDebug(LOG_FILTER_PACKETIO, "WORLD: Recvd CMSG_SETSHEATHED Message guidlow:%u value1:%u", GetPlayer()->GetGUIDLow(), sheathed);
|
||||
|
||||
if (sheathed >= MAX_SHEATH_STATE)
|
||||
{
|
||||
sLog->outError("Unknown sheath state %u ??", sheathed);
|
||||
return;
|
||||
}
|
||||
|
||||
GetPlayer()->SetSheath(SheathState(sheathed));
|
||||
}
|
||||
|
||||
void WorldSession::SendAttackStop(Unit const* enemy)
|
||||
{
|
||||
WorldPacket data(SMSG_ATTACKSTOP, (8+8+4)); // we guess size
|
||||
data.append(GetPlayer()->GetPackGUID());
|
||||
data.append(enemy ? enemy->GetPackGUID() : 0); // must be packed guid
|
||||
data << uint32(0); // unk, can be 1 also
|
||||
SendPacket(&data);
|
||||
}
|
||||
Executable
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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 "Common.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
#include "Log.h"
|
||||
#include "Opcodes.h"
|
||||
#include "UpdateData.h"
|
||||
#include "Player.h"
|
||||
|
||||
void WorldSession::HandleDuelAcceptedOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
uint64 guid;
|
||||
Player* player;
|
||||
Player* plTarget;
|
||||
|
||||
recvPacket >> guid;
|
||||
|
||||
if (!GetPlayer()->duel) // ignore accept from duel-sender
|
||||
return;
|
||||
|
||||
player = GetPlayer();
|
||||
plTarget = player->duel->opponent;
|
||||
|
||||
if (player == player->duel->initiator || !plTarget || player == plTarget || player->duel->startTime != 0 || plTarget->duel->startTime != 0)
|
||||
return;
|
||||
|
||||
//sLog->outDebug(LOG_FILTER_PACKETIO, "WORLD: Received CMSG_DUEL_ACCEPTED");
|
||||
sLog->outStaticDebug("Player 1 is: %u (%s)", player->GetGUIDLow(), player->GetName());
|
||||
sLog->outStaticDebug("Player 2 is: %u (%s)", plTarget->GetGUIDLow(), plTarget->GetName());
|
||||
|
||||
time_t now = time(NULL);
|
||||
player->duel->startTimer = now;
|
||||
plTarget->duel->startTimer = now;
|
||||
|
||||
player->SendDuelCountdown(3000);
|
||||
plTarget->SendDuelCountdown(3000);
|
||||
}
|
||||
|
||||
void WorldSession::HandleDuelCancelledOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_DUEL_CANCELLED");
|
||||
uint64 guid;
|
||||
recvPacket >> guid;
|
||||
|
||||
// no duel requested
|
||||
if (!GetPlayer()->duel)
|
||||
return;
|
||||
|
||||
// player surrendered in a duel using /forfeit
|
||||
if (GetPlayer()->duel->startTime != 0)
|
||||
{
|
||||
GetPlayer()->CombatStopWithPets(true);
|
||||
if (GetPlayer()->duel->opponent)
|
||||
GetPlayer()->duel->opponent->CombatStopWithPets(true);
|
||||
|
||||
GetPlayer()->CastSpell(GetPlayer(), 7267, true); // beg
|
||||
GetPlayer()->DuelComplete(DUEL_WON);
|
||||
return;
|
||||
}
|
||||
|
||||
GetPlayer()->DuelComplete(DUEL_INTERRUPTED);
|
||||
}
|
||||
Executable
+996
@@ -0,0 +1,996 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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 "Common.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Log.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
#include "World.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "GroupMgr.h"
|
||||
#include "Player.h"
|
||||
#include "Group.h"
|
||||
#include "SocialMgr.h"
|
||||
#include "Util.h"
|
||||
#include "SpellAuras.h"
|
||||
#include "Vehicle.h"
|
||||
|
||||
class Aura;
|
||||
|
||||
/* differeces from off:
|
||||
-you can uninvite yourself - is is useful
|
||||
-you can accept invitation even if leader went offline
|
||||
*/
|
||||
/* todo:
|
||||
-group_destroyed msg is sent but not shown
|
||||
-reduce xp gaining when in raid group
|
||||
-quest sharing has to be corrected
|
||||
-FIX sending PartyMemberStats
|
||||
*/
|
||||
|
||||
void WorldSession::SendPartyResult(PartyOperation operation, const std::string& member, PartyResult res, uint32 val /* = 0 */)
|
||||
{
|
||||
WorldPacket data(SMSG_PARTY_COMMAND_RESULT, 4 + member.size() + 1 + 4 + 4);
|
||||
data << uint32(operation);
|
||||
data << member;
|
||||
data << uint32(res);
|
||||
data << uint32(val); // LFD cooldown related (used with ERR_PARTY_LFG_BOOT_COOLDOWN_S and ERR_PARTY_LFG_BOOT_NOT_ELIGIBLE_S)
|
||||
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGroupInviteOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GROUP_INVITE");
|
||||
|
||||
std::string membername;
|
||||
recv_data >> membername;
|
||||
recv_data.read_skip<uint32>();
|
||||
|
||||
// attempt add selected player
|
||||
|
||||
// cheating
|
||||
if (!normalizePlayerName(membername))
|
||||
{
|
||||
SendPartyResult(PARTY_OP_INVITE, membername, ERR_BAD_PLAYER_NAME_S);
|
||||
return;
|
||||
}
|
||||
|
||||
Player* player = sObjectAccessor->FindPlayerByName(membername.c_str());
|
||||
|
||||
// no player
|
||||
if (!player)
|
||||
{
|
||||
SendPartyResult(PARTY_OP_INVITE, membername, ERR_BAD_PLAYER_NAME_S);
|
||||
return;
|
||||
}
|
||||
|
||||
// restrict invite to GMs
|
||||
if (!sWorld->getBoolConfig(CONFIG_ALLOW_GM_GROUP) && !GetPlayer()->isGameMaster() && player->isGameMaster())
|
||||
return;
|
||||
|
||||
// can't group with
|
||||
if (!GetPlayer()->isGameMaster() && !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP) && GetPlayer()->GetTeam() != player->GetTeam())
|
||||
{
|
||||
SendPartyResult(PARTY_OP_INVITE, membername, ERR_PLAYER_WRONG_FACTION);
|
||||
return;
|
||||
}
|
||||
if (GetPlayer()->GetInstanceId() != 0 && player->GetInstanceId() != 0 && GetPlayer()->GetInstanceId() != player->GetInstanceId() && GetPlayer()->GetMapId() == player->GetMapId())
|
||||
{
|
||||
SendPartyResult(PARTY_OP_INVITE, membername, ERR_TARGET_NOT_IN_INSTANCE_S);
|
||||
return;
|
||||
}
|
||||
// just ignore us
|
||||
if (player->GetInstanceId() != 0 && player->GetDungeonDifficulty() != GetPlayer()->GetDungeonDifficulty())
|
||||
{
|
||||
SendPartyResult(PARTY_OP_INVITE, membername, ERR_IGNORING_YOU_S);
|
||||
return;
|
||||
}
|
||||
|
||||
if (player->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow()))
|
||||
{
|
||||
SendPartyResult(PARTY_OP_INVITE, membername, ERR_IGNORING_YOU_S);
|
||||
return;
|
||||
}
|
||||
|
||||
Group* group = GetPlayer()->GetGroup();
|
||||
if (group && group->isBGGroup())
|
||||
group = GetPlayer()->GetOriginalGroup();
|
||||
|
||||
Group* group2 = player->GetGroup();
|
||||
if (group2 && group2->isBGGroup())
|
||||
group2 = player->GetOriginalGroup();
|
||||
// player already in another group or invited
|
||||
if (group2 || player->GetGroupInvite())
|
||||
{
|
||||
SendPartyResult(PARTY_OP_INVITE, membername, ERR_ALREADY_IN_GROUP_S);
|
||||
|
||||
if (group2)
|
||||
{
|
||||
// tell the player that they were invited but it failed as they were already in a group
|
||||
WorldPacket data(SMSG_GROUP_INVITE, 10); // guess size
|
||||
data << uint8(0); // invited/already in group flag
|
||||
data << GetPlayer()->GetName(); // max len 48
|
||||
data << uint32(0); // unk
|
||||
data << uint8(0); // count
|
||||
data << uint32(0); // unk
|
||||
player->GetSession()->SendPacket(&data);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (group)
|
||||
{
|
||||
// not have permissions for invite
|
||||
if (!group->IsLeader(GetPlayer()->GetGUID()) && !group->IsAssistant(GetPlayer()->GetGUID()))
|
||||
{
|
||||
SendPartyResult(PARTY_OP_INVITE, "", ERR_NOT_LEADER);
|
||||
return;
|
||||
}
|
||||
// not have place
|
||||
if (group->IsFull())
|
||||
{
|
||||
SendPartyResult(PARTY_OP_INVITE, "", ERR_GROUP_FULL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// ok, but group not exist, start a new group
|
||||
// but don't create and save the group to the DB until
|
||||
// at least one person joins
|
||||
if (!group)
|
||||
{
|
||||
group = new Group;
|
||||
// new group: if can't add then delete
|
||||
if (!group->AddLeaderInvite(GetPlayer()))
|
||||
{
|
||||
delete group;
|
||||
return;
|
||||
}
|
||||
if (!group->AddInvite(player))
|
||||
{
|
||||
delete group;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// already existed group: if can't add then just leave
|
||||
if (!group->AddInvite(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// ok, we do it
|
||||
WorldPacket data(SMSG_GROUP_INVITE, 10); // guess size
|
||||
data << uint8(1); // invited/already in group flag
|
||||
data << GetPlayer()->GetName(); // max len 48
|
||||
data << uint32(0); // unk
|
||||
data << uint8(0); // count
|
||||
data << uint32(0); // unk
|
||||
player->GetSession()->SendPacket(&data);
|
||||
|
||||
SendPartyResult(PARTY_OP_INVITE, membername, ERR_PARTY_RESULT_OK);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGroupAcceptOpcode(WorldPacket& recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GROUP_ACCEPT");
|
||||
|
||||
recv_data.read_skip<uint32>();
|
||||
Group* group = GetPlayer()->GetGroupInvite();
|
||||
|
||||
if (!group)
|
||||
return;
|
||||
|
||||
// Remove player from invitees in any case
|
||||
group->RemoveInvite(GetPlayer());
|
||||
|
||||
if (group->GetLeaderGUID() == GetPlayer()->GetGUID())
|
||||
{
|
||||
sLog->outError("HandleGroupAcceptOpcode: player %s(%d) tried to accept an invite to his own group", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow());
|
||||
return;
|
||||
}
|
||||
|
||||
// Group is full
|
||||
if (group->IsFull())
|
||||
{
|
||||
SendPartyResult(PARTY_OP_INVITE, "", ERR_GROUP_FULL);
|
||||
return;
|
||||
}
|
||||
|
||||
Player* leader = ObjectAccessor::FindPlayer(group->GetLeaderGUID());
|
||||
|
||||
// Forming a new group, create it
|
||||
if (!group->IsCreated())
|
||||
{
|
||||
// This can happen if the leader is zoning. To be removed once delayed actions for zoning are implemented
|
||||
if (!leader)
|
||||
{
|
||||
group->RemoveAllInvites();
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're about to create a group there really should be a leader present
|
||||
ASSERT(leader);
|
||||
group->RemoveInvite(leader);
|
||||
group->Create(leader);
|
||||
sGroupMgr->AddGroup(group);
|
||||
}
|
||||
|
||||
// Everything is fine, do it, PLAYER'S GROUP IS SET IN ADDMEMBER!!!
|
||||
if (!group->AddMember(GetPlayer()))
|
||||
return;
|
||||
|
||||
group->BroadcastGroupUpdate();
|
||||
}
|
||||
|
||||
void WorldSession::HandleGroupDeclineOpcode(WorldPacket & /*recv_data*/)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GROUP_DECLINE");
|
||||
|
||||
Group *group = GetPlayer()->GetGroupInvite();
|
||||
if (!group) return;
|
||||
|
||||
// Remember leader if online (group pointer will be invalid if group gets disbanded)
|
||||
Player* leader = ObjectAccessor::FindPlayer(group->GetLeaderGUID());
|
||||
|
||||
// uninvite, group can be deleted
|
||||
GetPlayer()->UninviteFromGroup();
|
||||
|
||||
if (!leader || !leader->GetSession())
|
||||
return;
|
||||
|
||||
// report
|
||||
std::string name = std::string(GetPlayer()->GetName());
|
||||
WorldPacket data(SMSG_GROUP_DECLINE, name.length());
|
||||
data << name.c_str();
|
||||
leader->GetSession()->SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGroupUninviteGuidOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GROUP_UNINVITE_GUID");
|
||||
|
||||
uint64 guid;
|
||||
std::string reason;
|
||||
recv_data >> guid;
|
||||
recv_data >> reason;
|
||||
|
||||
//can't uninvite yourself
|
||||
if (guid == GetPlayer()->GetGUID())
|
||||
{
|
||||
sLog->outError("WorldSession::HandleGroupUninviteGuidOpcode: leader %s(%d) tried to uninvite himself from the group.", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow());
|
||||
return;
|
||||
}
|
||||
|
||||
PartyResult res = GetPlayer()->CanUninviteFromGroup();
|
||||
if (res != ERR_PARTY_RESULT_OK)
|
||||
{
|
||||
SendPartyResult(PARTY_OP_UNINVITE, "", res);
|
||||
return;
|
||||
}
|
||||
|
||||
Group* grp = GetPlayer()->GetGroup();
|
||||
if (!grp)
|
||||
return;
|
||||
|
||||
if (grp->IsLeader(guid))
|
||||
{
|
||||
SendPartyResult(PARTY_OP_UNINVITE, "", ERR_NOT_LEADER);
|
||||
return;
|
||||
}
|
||||
|
||||
if (grp->IsMember(guid))
|
||||
{
|
||||
Player::RemoveFromGroup(grp, guid, GROUP_REMOVEMETHOD_KICK, GetPlayer()->GetGUID(), reason.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (Player* player = grp->GetInvited(guid))
|
||||
{
|
||||
player->UninviteFromGroup();
|
||||
return;
|
||||
}
|
||||
|
||||
SendPartyResult(PARTY_OP_UNINVITE, "", ERR_TARGET_NOT_IN_GROUP_S);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGroupUninviteOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GROUP_UNINVITE");
|
||||
|
||||
std::string membername;
|
||||
recv_data >> membername;
|
||||
|
||||
// player not found
|
||||
if (!normalizePlayerName(membername))
|
||||
return;
|
||||
|
||||
// can't uninvite yourself
|
||||
if (GetPlayer()->GetName() == membername)
|
||||
{
|
||||
sLog->outError("WorldSession::HandleGroupUninviteOpcode: leader %s(%d) tried to uninvite himself from the group.", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow());
|
||||
return;
|
||||
}
|
||||
|
||||
PartyResult res = GetPlayer()->CanUninviteFromGroup();
|
||||
if (res != ERR_PARTY_RESULT_OK)
|
||||
{
|
||||
SendPartyResult(PARTY_OP_UNINVITE, "", res);
|
||||
return;
|
||||
}
|
||||
|
||||
Group* grp = GetPlayer()->GetGroup();
|
||||
if (!grp)
|
||||
return;
|
||||
|
||||
if (uint64 guid = grp->GetMemberGUID(membername))
|
||||
{
|
||||
Player::RemoveFromGroup(grp, guid, GROUP_REMOVEMETHOD_KICK, GetPlayer()->GetGUID());
|
||||
return;
|
||||
}
|
||||
|
||||
if (Player* player = grp->GetInvited(membername))
|
||||
{
|
||||
player->UninviteFromGroup();
|
||||
return;
|
||||
}
|
||||
|
||||
SendPartyResult(PARTY_OP_UNINVITE, membername, ERR_TARGET_NOT_IN_GROUP_S);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGroupSetLeaderOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GROUP_SET_LEADER");
|
||||
|
||||
uint64 guid;
|
||||
recv_data >> guid;
|
||||
|
||||
Player* player = ObjectAccessor::FindPlayer(guid);
|
||||
Group* group = GetPlayer()->GetGroup();
|
||||
|
||||
if (!group || !player)
|
||||
return;
|
||||
|
||||
if (!group->IsLeader(GetPlayer()->GetGUID()) || player->GetGroup() != group)
|
||||
return;
|
||||
|
||||
// Everything's fine, accepted.
|
||||
group->ChangeLeader(guid);
|
||||
group->SendUpdate();
|
||||
}
|
||||
|
||||
void WorldSession::HandleGroupDisbandOpcode(WorldPacket & /*recv_data*/)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GROUP_DISBAND");
|
||||
|
||||
Group* grp = GetPlayer()->GetGroup();
|
||||
if (!grp)
|
||||
return;
|
||||
|
||||
if (_player->InBattleground())
|
||||
{
|
||||
SendPartyResult(PARTY_OP_INVITE, "", ERR_INVITE_RESTRICTED);
|
||||
return;
|
||||
}
|
||||
|
||||
/** error handling **/
|
||||
/********************/
|
||||
|
||||
// everything's fine, do it
|
||||
SendPartyResult(PARTY_OP_LEAVE, GetPlayer()->GetName(), ERR_PARTY_RESULT_OK);
|
||||
|
||||
GetPlayer()->RemoveFromGroup(GROUP_REMOVEMETHOD_LEAVE);
|
||||
}
|
||||
|
||||
void WorldSession::HandleLootMethodOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_LOOT_METHOD");
|
||||
|
||||
uint32 lootMethod;
|
||||
uint64 lootMaster;
|
||||
uint32 lootThreshold;
|
||||
recv_data >> lootMethod >> lootMaster >> lootThreshold;
|
||||
|
||||
Group* group = GetPlayer()->GetGroup();
|
||||
if (!group)
|
||||
return;
|
||||
|
||||
/** error handling **/
|
||||
if (!group->IsLeader(GetPlayer()->GetGUID()))
|
||||
return;
|
||||
/********************/
|
||||
|
||||
// everything's fine, do it
|
||||
group->SetLootMethod((LootMethod)lootMethod);
|
||||
group->SetLooterGuid(lootMaster);
|
||||
group->SetLootThreshold((ItemQualities)lootThreshold);
|
||||
group->SendUpdate();
|
||||
}
|
||||
|
||||
void WorldSession::HandleLootRoll(WorldPacket &recv_data)
|
||||
{
|
||||
if (!GetPlayer()->GetGroup())
|
||||
{
|
||||
recv_data.rfinish();
|
||||
return;
|
||||
}
|
||||
|
||||
uint64 Guid;
|
||||
uint32 NumberOfPlayers;
|
||||
uint8 rollType;
|
||||
recv_data >> Guid; //guid of the item rolled
|
||||
recv_data >> NumberOfPlayers;
|
||||
recv_data >> rollType; //0: pass, 1: need, 2: greed
|
||||
|
||||
//sLog->outDebug("WORLD RECIEVE CMSG_LOOT_ROLL, From:%u, Numberofplayers:%u, Choise:%u", (uint32)Guid, NumberOfPlayers, Choise);
|
||||
|
||||
Group* group = GetPlayer()->GetGroup();
|
||||
if (!group)
|
||||
return;
|
||||
|
||||
// everything's fine, do it
|
||||
group->CountRollVote(GetPlayer()->GetGUID(), Guid, NumberOfPlayers, rollType);
|
||||
|
||||
switch (rollType)
|
||||
{
|
||||
case ROLL_NEED:
|
||||
GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED, 1);
|
||||
break;
|
||||
case ROLL_GREED:
|
||||
GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleMinimapPingOpcode(WorldPacket& recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received MSG_MINIMAP_PING");
|
||||
|
||||
if (!GetPlayer()->GetGroup())
|
||||
return;
|
||||
|
||||
float x, y;
|
||||
recv_data >> x;
|
||||
recv_data >> y;
|
||||
|
||||
//sLog->outDebug("Received opcode MSG_MINIMAP_PING X: %f, Y: %f", x, y);
|
||||
|
||||
/** error handling **/
|
||||
/********************/
|
||||
|
||||
// everything's fine, do it
|
||||
WorldPacket data(MSG_MINIMAP_PING, (8+4+4));
|
||||
data << uint64(GetPlayer()->GetGUID());
|
||||
data << float(x);
|
||||
data << float(y);
|
||||
GetPlayer()->GetGroup()->BroadcastPacket(&data, true, -1, GetPlayer()->GetGUID());
|
||||
}
|
||||
|
||||
void WorldSession::HandleRandomRollOpcode(WorldPacket& recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received MSG_RANDOM_ROLL");
|
||||
|
||||
uint32 minimum, maximum, roll;
|
||||
recv_data >> minimum;
|
||||
recv_data >> maximum;
|
||||
|
||||
/** error handling **/
|
||||
if (minimum > maximum || maximum > 10000) // < 32768 for urand call
|
||||
return;
|
||||
/********************/
|
||||
|
||||
// everything's fine, do it
|
||||
roll = urand(minimum, maximum);
|
||||
|
||||
//sLog->outDebug("ROLL: MIN: %u, MAX: %u, ROLL: %u", minimum, maximum, roll);
|
||||
|
||||
WorldPacket data(MSG_RANDOM_ROLL, 4+4+4+8);
|
||||
data << uint32(minimum);
|
||||
data << uint32(maximum);
|
||||
data << uint32(roll);
|
||||
data << uint64(GetPlayer()->GetGUID());
|
||||
if (GetPlayer()->GetGroup())
|
||||
GetPlayer()->GetGroup()->BroadcastPacket(&data, false);
|
||||
else
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleRaidTargetUpdateOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received MSG_RAID_TARGET_UPDATE");
|
||||
|
||||
Group* group = GetPlayer()->GetGroup();
|
||||
if (!group)
|
||||
return;
|
||||
|
||||
uint8 x;
|
||||
recv_data >> x;
|
||||
|
||||
/** error handling **/
|
||||
/********************/
|
||||
|
||||
// everything's fine, do it
|
||||
if (x == 0xFF) // target icon request
|
||||
{
|
||||
group->SendTargetIconList(this);
|
||||
}
|
||||
else // target icon update
|
||||
{
|
||||
if (!group->IsLeader(GetPlayer()->GetGUID()) && !group->IsAssistant(GetPlayer()->GetGUID()))
|
||||
return;
|
||||
|
||||
uint64 guid;
|
||||
recv_data >> guid;
|
||||
group->SetTargetIcon(x, _player->GetGUID(), guid);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleGroupRaidConvertOpcode(WorldPacket & /*recv_data*/)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GROUP_RAID_CONVERT");
|
||||
|
||||
Group* group = GetPlayer()->GetGroup();
|
||||
if (!group)
|
||||
return;
|
||||
|
||||
if (_player->InBattleground())
|
||||
return;
|
||||
|
||||
/** error handling **/
|
||||
if (!group->IsLeader(GetPlayer()->GetGUID()) || group->GetMembersCount() < 2)
|
||||
return;
|
||||
/********************/
|
||||
|
||||
// everything's fine, do it (is it 0 (PARTY_OP_INVITE) correct code)
|
||||
SendPartyResult(PARTY_OP_INVITE, "", ERR_PARTY_RESULT_OK);
|
||||
group->ConvertToRaid();
|
||||
}
|
||||
|
||||
void WorldSession::HandleGroupChangeSubGroupOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GROUP_CHANGE_SUB_GROUP");
|
||||
|
||||
// we will get correct pointer for group here, so we don't have to check if group is BG raid
|
||||
Group* group = GetPlayer()->GetGroup();
|
||||
if (!group)
|
||||
return;
|
||||
|
||||
std::string name;
|
||||
uint8 groupNr;
|
||||
recv_data >> name;
|
||||
recv_data >> groupNr;
|
||||
|
||||
if (groupNr >= MAX_RAID_SUBGROUPS)
|
||||
return;
|
||||
|
||||
uint64 senderGuid = GetPlayer()->GetGUID();
|
||||
if (!group->IsLeader(senderGuid) && !group->IsAssistant(senderGuid))
|
||||
return;
|
||||
|
||||
if (!group->HasFreeSlotSubGroup(groupNr))
|
||||
return;
|
||||
|
||||
Player* movedPlayer = sObjectAccessor->FindPlayerByName(name.c_str());
|
||||
uint64 guid;
|
||||
if (movedPlayer)
|
||||
{
|
||||
guid = movedPlayer->GetGUID();
|
||||
}
|
||||
else
|
||||
{
|
||||
CharacterDatabase.EscapeString(name);
|
||||
guid = sObjectMgr->GetPlayerGUIDByName(name.c_str());
|
||||
}
|
||||
|
||||
group->ChangeMembersGroup(guid, groupNr);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGroupAssistantLeaderOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GROUP_ASSISTANT_LEADER");
|
||||
|
||||
Group* group = GetPlayer()->GetGroup();
|
||||
if (!group)
|
||||
return;
|
||||
|
||||
if (!group->IsLeader(GetPlayer()->GetGUID()))
|
||||
return;
|
||||
|
||||
uint64 guid;
|
||||
bool apply;
|
||||
recv_data >> guid;
|
||||
recv_data >> apply;
|
||||
|
||||
group->SetGroupMemberFlag(guid, apply, MEMBER_FLAG_ASSISTANT);
|
||||
|
||||
group->SendUpdate();
|
||||
}
|
||||
|
||||
void WorldSession::HandlePartyAssignmentOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received MSG_PARTY_ASSIGNMENT");
|
||||
|
||||
Group* group = GetPlayer()->GetGroup();
|
||||
if (!group)
|
||||
return;
|
||||
|
||||
uint64 senderGuid = GetPlayer()->GetGUID();
|
||||
if (!group->IsLeader(senderGuid) && !group->IsAssistant(senderGuid))
|
||||
return;
|
||||
|
||||
uint8 assignment;
|
||||
bool apply;
|
||||
uint64 guid;
|
||||
recv_data >> assignment >> apply;
|
||||
recv_data >> guid;
|
||||
|
||||
switch (assignment)
|
||||
{
|
||||
case GROUP_ASSIGN_MAINASSIST:
|
||||
group->RemoveUniqueGroupMemberFlag(MEMBER_FLAG_MAINASSIST);
|
||||
group->SetGroupMemberFlag(guid, apply, MEMBER_FLAG_MAINASSIST);
|
||||
break;
|
||||
case GROUP_ASSIGN_MAINTANK:
|
||||
group->RemoveUniqueGroupMemberFlag(MEMBER_FLAG_MAINTANK); // Remove main assist flag from current if any.
|
||||
group->SetGroupMemberFlag(guid, apply, MEMBER_FLAG_MAINTANK);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
group->SendUpdate();
|
||||
}
|
||||
|
||||
void WorldSession::HandleRaidReadyCheckOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received MSG_RAID_READY_CHECK");
|
||||
|
||||
Group* group = GetPlayer()->GetGroup();
|
||||
if (!group)
|
||||
return;
|
||||
|
||||
if (recv_data.empty()) // request
|
||||
{
|
||||
/** error handling **/
|
||||
if (!group->IsLeader(GetPlayer()->GetGUID()) && !group->IsAssistant(GetPlayer()->GetGUID()))
|
||||
return;
|
||||
/********************/
|
||||
|
||||
// everything's fine, do it
|
||||
WorldPacket data(MSG_RAID_READY_CHECK, 8);
|
||||
data << GetPlayer()->GetGUID();
|
||||
group->BroadcastPacket(&data, false, -1);
|
||||
|
||||
group->OfflineReadyCheck();
|
||||
}
|
||||
else // answer
|
||||
{
|
||||
uint8 state;
|
||||
recv_data >> state;
|
||||
|
||||
// everything's fine, do it
|
||||
WorldPacket data(MSG_RAID_READY_CHECK_CONFIRM, 9);
|
||||
data << uint64(GetPlayer()->GetGUID());
|
||||
data << uint8(state);
|
||||
group->BroadcastReadyCheck(&data);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleRaidReadyCheckFinishedOpcode(WorldPacket & /*recv_data*/)
|
||||
{
|
||||
//Group* group = GetPlayer()->GetGroup();
|
||||
//if (!group)
|
||||
// return;
|
||||
|
||||
//if (!group->IsLeader(GetPlayer()->GetGUID()) && !group->IsAssistant(GetPlayer()->GetGUID()))
|
||||
// return;
|
||||
|
||||
// Is any reaction need?
|
||||
}
|
||||
|
||||
void WorldSession::BuildPartyMemberStatsChangedPacket(Player* player, WorldPacket* data)
|
||||
{
|
||||
uint32 mask = player->GetGroupUpdateFlag();
|
||||
|
||||
if (mask == GROUP_UPDATE_FLAG_NONE)
|
||||
return;
|
||||
|
||||
if (mask & GROUP_UPDATE_FLAG_POWER_TYPE) // if update power type, update current/max power also
|
||||
mask |= (GROUP_UPDATE_FLAG_CUR_POWER | GROUP_UPDATE_FLAG_MAX_POWER);
|
||||
|
||||
if (mask & GROUP_UPDATE_FLAG_PET_POWER_TYPE) // same for pets
|
||||
mask |= (GROUP_UPDATE_FLAG_PET_CUR_POWER | GROUP_UPDATE_FLAG_PET_MAX_POWER);
|
||||
|
||||
uint32 byteCount = 0;
|
||||
for (int i = 1; i < GROUP_UPDATE_FLAGS_COUNT; ++i)
|
||||
if (mask & (1 << i))
|
||||
byteCount += GroupUpdateLength[i];
|
||||
|
||||
data->Initialize(SMSG_PARTY_MEMBER_STATS, 8 + 4 + byteCount);
|
||||
data->append(player->GetPackGUID());
|
||||
*data << (uint32) mask;
|
||||
|
||||
if (mask & GROUP_UPDATE_FLAG_STATUS)
|
||||
{
|
||||
if (player)
|
||||
{
|
||||
if (player->IsPvP())
|
||||
*data << (uint16) (MEMBER_STATUS_ONLINE | MEMBER_STATUS_PVP);
|
||||
else
|
||||
*data << (uint16) MEMBER_STATUS_ONLINE;
|
||||
}
|
||||
else
|
||||
*data << (uint16) MEMBER_STATUS_OFFLINE;
|
||||
}
|
||||
|
||||
if (mask & GROUP_UPDATE_FLAG_CUR_HP)
|
||||
*data << (uint32) player->GetHealth();
|
||||
|
||||
if (mask & GROUP_UPDATE_FLAG_MAX_HP)
|
||||
*data << (uint32) player->GetMaxHealth();
|
||||
|
||||
Powers powerType = player->getPowerType();
|
||||
if (mask & GROUP_UPDATE_FLAG_POWER_TYPE)
|
||||
*data << (uint8) powerType;
|
||||
|
||||
if (mask & GROUP_UPDATE_FLAG_CUR_POWER)
|
||||
*data << (uint16) player->GetPower(powerType);
|
||||
|
||||
if (mask & GROUP_UPDATE_FLAG_MAX_POWER)
|
||||
*data << (uint16) player->GetMaxPower(powerType);
|
||||
|
||||
if (mask & GROUP_UPDATE_FLAG_LEVEL)
|
||||
*data << (uint16) player->getLevel();
|
||||
|
||||
if (mask & GROUP_UPDATE_FLAG_ZONE)
|
||||
*data << (uint16) player->GetZoneId();
|
||||
|
||||
if (mask & GROUP_UPDATE_FLAG_POSITION)
|
||||
*data << (uint16) player->GetPositionX() << (uint16) player->GetPositionY();
|
||||
|
||||
if (mask & GROUP_UPDATE_FLAG_AURAS)
|
||||
{
|
||||
uint64 auramask = player->GetAuraUpdateMaskForRaid();
|
||||
*data << uint64(auramask);
|
||||
for (uint32 i = 0; i < MAX_AURAS; ++i)
|
||||
{
|
||||
if (auramask & (uint64(1) << i))
|
||||
{
|
||||
AuraApplication const* aurApp = player->GetVisibleAura(i);
|
||||
*data << uint32(aurApp ? aurApp->GetBase()->GetId() : 0);
|
||||
*data << uint8(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Pet* pet = player->GetPet();
|
||||
if (mask & GROUP_UPDATE_FLAG_PET_GUID)
|
||||
{
|
||||
if (pet)
|
||||
*data << (uint64) pet->GetGUID();
|
||||
else
|
||||
*data << (uint64) 0;
|
||||
}
|
||||
|
||||
if (mask & GROUP_UPDATE_FLAG_PET_NAME)
|
||||
{
|
||||
if (pet)
|
||||
*data << pet->GetName();
|
||||
else
|
||||
*data << (uint8) 0;
|
||||
}
|
||||
|
||||
if (mask & GROUP_UPDATE_FLAG_PET_MODEL_ID)
|
||||
{
|
||||
if (pet)
|
||||
*data << (uint16) pet->GetDisplayId();
|
||||
else
|
||||
*data << (uint16) 0;
|
||||
}
|
||||
|
||||
if (mask & GROUP_UPDATE_FLAG_PET_CUR_HP)
|
||||
{
|
||||
if (pet)
|
||||
*data << (uint32) pet->GetHealth();
|
||||
else
|
||||
*data << (uint32) 0;
|
||||
}
|
||||
|
||||
if (mask & GROUP_UPDATE_FLAG_PET_MAX_HP)
|
||||
{
|
||||
if (pet)
|
||||
*data << (uint32) pet->GetMaxHealth();
|
||||
else
|
||||
*data << (uint32) 0;
|
||||
}
|
||||
|
||||
if (mask & GROUP_UPDATE_FLAG_PET_POWER_TYPE)
|
||||
{
|
||||
if (pet)
|
||||
*data << (uint8) pet->getPowerType();
|
||||
else
|
||||
*data << (uint8) 0;
|
||||
}
|
||||
|
||||
if (mask & GROUP_UPDATE_FLAG_PET_CUR_POWER)
|
||||
{
|
||||
if (pet)
|
||||
*data << (uint16) pet->GetPower(pet->getPowerType());
|
||||
else
|
||||
*data << (uint16) 0;
|
||||
}
|
||||
|
||||
if (mask & GROUP_UPDATE_FLAG_PET_MAX_POWER)
|
||||
{
|
||||
if (pet)
|
||||
*data << (uint16) pet->GetMaxPower(pet->getPowerType());
|
||||
else
|
||||
*data << (uint16) 0;
|
||||
}
|
||||
|
||||
if (mask & GROUP_UPDATE_FLAG_VEHICLE_SEAT)
|
||||
{
|
||||
if (Vehicle* veh = player->GetVehicle())
|
||||
*data << (uint32) veh->GetVehicleInfo()->m_seatID[player->m_movementInfo.t_seat];
|
||||
}
|
||||
|
||||
if (mask & GROUP_UPDATE_FLAG_PET_AURAS)
|
||||
{
|
||||
if (pet)
|
||||
{
|
||||
uint64 auramask = pet->GetAuraUpdateMaskForRaid();
|
||||
*data << uint64(auramask);
|
||||
for (uint32 i = 0; i < MAX_AURAS; ++i)
|
||||
{
|
||||
if (auramask & (uint64(1) << i))
|
||||
{
|
||||
AuraApplication const* aurApp = player->GetVisibleAura(i);
|
||||
*data << uint32(aurApp ? aurApp->GetBase()->GetId() : 0);
|
||||
*data << uint8(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
*data << (uint64) 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*this procedure handles clients CMSG_REQUEST_PARTY_MEMBER_STATS request*/
|
||||
void WorldSession::HandleRequestPartyMemberStatsOpcode(WorldPacket &recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_REQUEST_PARTY_MEMBER_STATS");
|
||||
uint64 Guid;
|
||||
recv_data >> Guid;
|
||||
|
||||
Player* player = HashMapHolder<Player>::Find(Guid);
|
||||
if (!player)
|
||||
{
|
||||
WorldPacket data(SMSG_PARTY_MEMBER_STATS_FULL, 3+4+2);
|
||||
data << uint8(0); // only for SMSG_PARTY_MEMBER_STATS_FULL, probably arena/bg related
|
||||
data.appendPackGUID(Guid);
|
||||
data << (uint32) GROUP_UPDATE_FLAG_STATUS;
|
||||
data << (uint16) MEMBER_STATUS_OFFLINE;
|
||||
SendPacket(&data);
|
||||
return;
|
||||
}
|
||||
|
||||
Pet* pet = player->GetPet();
|
||||
|
||||
WorldPacket data(SMSG_PARTY_MEMBER_STATS_FULL, 4+2+2+2+1+2*6+8+1+8);
|
||||
data << uint8(0); // only for SMSG_PARTY_MEMBER_STATS_FULL, probably arena/bg related
|
||||
data.append(player->GetPackGUID());
|
||||
|
||||
uint32 mask1 = 0x00040BFF; // common mask, real flags used 0x000040BFF
|
||||
if (pet)
|
||||
mask1 = 0x7FFFFFFF; // for hunters and other classes with pets
|
||||
|
||||
Powers powerType = player->getPowerType();
|
||||
data << (uint32) mask1; // group update mask
|
||||
data << (uint16) MEMBER_STATUS_ONLINE; // member's online status
|
||||
data << (uint32) player->GetHealth(); // GROUP_UPDATE_FLAG_CUR_HP
|
||||
data << (uint32) player->GetMaxHealth(); // GROUP_UPDATE_FLAG_MAX_HP
|
||||
data << (uint8) powerType; // GROUP_UPDATE_FLAG_POWER_TYPE
|
||||
data << (uint16) player->GetPower(powerType); // GROUP_UPDATE_FLAG_CUR_POWER
|
||||
data << (uint16) player->GetMaxPower(powerType); // GROUP_UPDATE_FLAG_MAX_POWER
|
||||
data << (uint16) player->getLevel(); // GROUP_UPDATE_FLAG_LEVEL
|
||||
data << (uint16) player->GetZoneId(); // GROUP_UPDATE_FLAG_ZONE
|
||||
data << (uint16) player->GetPositionX(); // GROUP_UPDATE_FLAG_POSITION
|
||||
data << (uint16) player->GetPositionY(); // GROUP_UPDATE_FLAG_POSITION
|
||||
|
||||
uint64 auramask = 0;
|
||||
size_t maskPos = data.wpos();
|
||||
data << (uint64) auramask; // placeholder
|
||||
for (uint8 i = 0; i < MAX_AURAS; ++i)
|
||||
{
|
||||
if (AuraApplication * aurApp = player->GetVisibleAura(i))
|
||||
{
|
||||
auramask |= (uint64(1) << i);
|
||||
data << (uint32) aurApp->GetBase()->GetId();
|
||||
data << (uint8) 1;
|
||||
}
|
||||
}
|
||||
data.put<uint64>(maskPos, auramask); // GROUP_UPDATE_FLAG_AURAS
|
||||
|
||||
if (pet)
|
||||
{
|
||||
Powers petpowertype = pet->getPowerType();
|
||||
data << (uint64) pet->GetGUID(); // GROUP_UPDATE_FLAG_PET_GUID
|
||||
data << pet->GetName(); // GROUP_UPDATE_FLAG_PET_NAME
|
||||
data << (uint16) pet->GetDisplayId(); // GROUP_UPDATE_FLAG_PET_MODEL_ID
|
||||
data << (uint32) pet->GetHealth(); // GROUP_UPDATE_FLAG_PET_CUR_HP
|
||||
data << (uint32) pet->GetMaxHealth(); // GROUP_UPDATE_FLAG_PET_MAX_HP
|
||||
data << (uint8) petpowertype; // GROUP_UPDATE_FLAG_PET_POWER_TYPE
|
||||
data << (uint16) pet->GetPower(petpowertype); // GROUP_UPDATE_FLAG_PET_CUR_POWER
|
||||
data << (uint16) pet->GetMaxPower(petpowertype); // GROUP_UPDATE_FLAG_PET_MAX_POWER
|
||||
|
||||
uint64 petauramask = 0;
|
||||
size_t petMaskPos = data.wpos();
|
||||
data << (uint64) petauramask; // placeholder
|
||||
for (uint8 i = 0; i < MAX_AURAS; ++i)
|
||||
{
|
||||
if (AuraApplication * auraApp = pet->GetVisibleAura(i))
|
||||
{
|
||||
petauramask |= (uint64(1) << i);
|
||||
data << (uint32) auraApp->GetBase()->GetId();
|
||||
data << (uint8) 1;
|
||||
}
|
||||
}
|
||||
data.put<uint64>(petMaskPos, petauramask); // GROUP_UPDATE_FLAG_PET_AURAS
|
||||
}
|
||||
else
|
||||
{
|
||||
data << (uint8) 0; // GROUP_UPDATE_FLAG_PET_NAME
|
||||
data << (uint64) 0; // GROUP_UPDATE_FLAG_PET_AURAS
|
||||
}
|
||||
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
/*!*/void WorldSession::HandleRequestRaidInfoOpcode(WorldPacket & /*recv_data*/)
|
||||
{
|
||||
// every time the player checks the character screen
|
||||
_player->SendRaidInfo();
|
||||
}
|
||||
|
||||
/*void WorldSession::HandleGroupCancelOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug("WORLD: got CMSG_GROUP_CANCEL.");
|
||||
}*/
|
||||
|
||||
void WorldSession::HandleOptOutOfLootOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_OPT_OUT_OF_LOOT");
|
||||
|
||||
uint32 passOnLoot;
|
||||
recv_data >> passOnLoot; // 1 always pass, 0 do not pass
|
||||
|
||||
// ignore if player not loaded
|
||||
if (!GetPlayer()) // needed because STATUS_AUTHED
|
||||
{
|
||||
if (passOnLoot != 0)
|
||||
sLog->outError("CMSG_OPT_OUT_OF_LOOT value<>0 for not-loaded character!");
|
||||
return;
|
||||
}
|
||||
|
||||
GetPlayer()->SetPassOnGroupLoot(passOnLoot);
|
||||
}
|
||||
Executable
+570
@@ -0,0 +1,570 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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 "Common.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
#include "World.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "GuildMgr.h"
|
||||
#include "Log.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Guild.h"
|
||||
#include "GossipDef.h"
|
||||
#include "SocialMgr.h"
|
||||
|
||||
// Helper for getting guild object of session's player.
|
||||
// If guild does not exist, sends error (if necessary).
|
||||
inline Guild* _GetPlayerGuild(WorldSession* session, bool sendError = false)
|
||||
{
|
||||
if (uint32 guildId = session->GetPlayer()->GetGuildId()) // If guild id = 0, player is not in guild
|
||||
if (Guild* guild = sGuildMgr->GetGuildById(guildId)) // Find guild by id
|
||||
return guild;
|
||||
if (sendError)
|
||||
Guild::SendCommandResult(session, GUILD_CREATE_S, ERR_GUILD_PLAYER_NOT_IN_GUILD);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildQueryOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_QUERY");
|
||||
|
||||
uint32 guildId;
|
||||
recvPacket >> guildId;
|
||||
// Use received guild id to access guild method (not player's guild id)
|
||||
if (Guild* guild = sGuildMgr->GetGuildById(guildId))
|
||||
guild->HandleQuery(this);
|
||||
else
|
||||
Guild::SendCommandResult(this, GUILD_CREATE_S, ERR_GUILD_PLAYER_NOT_IN_GUILD);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildCreateOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_CREATE");
|
||||
|
||||
std::string name;
|
||||
recvPacket >> name;
|
||||
|
||||
if (!GetPlayer()->GetGuildId()) // Player cannot be in guild
|
||||
{
|
||||
Guild* guild = new Guild();
|
||||
if (guild->Create(GetPlayer(), name))
|
||||
sGuildMgr->AddGuild(guild);
|
||||
else
|
||||
delete guild;
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildInviteOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_INVITE");
|
||||
|
||||
std::string invitedName;
|
||||
recvPacket >> invitedName;
|
||||
|
||||
if (normalizePlayerName(invitedName))
|
||||
if (Guild* guild = _GetPlayerGuild(this, true))
|
||||
guild->HandleInviteMember(this, invitedName);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildRemoveOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_REMOVE");
|
||||
|
||||
std::string playerName;
|
||||
recvPacket >> playerName;
|
||||
|
||||
if (normalizePlayerName(playerName))
|
||||
if (Guild* guild = _GetPlayerGuild(this, true))
|
||||
guild->HandleRemoveMember(this, playerName);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildAcceptOpcode(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_ACCEPT");
|
||||
// Player cannot be in guild
|
||||
if (!GetPlayer()->GetGuildId())
|
||||
// Guild where player was invited must exist
|
||||
if (Guild* guild = sGuildMgr->GetGuildById(GetPlayer()->GetGuildIdInvited()))
|
||||
guild->HandleAcceptMember(this);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildDeclineOpcode(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_DECLINE");
|
||||
|
||||
GetPlayer()->SetGuildIdInvited(0);
|
||||
GetPlayer()->SetInGuild(0);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildInfoOpcode(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_INFO");
|
||||
|
||||
if (Guild* guild = _GetPlayerGuild(this, true))
|
||||
guild->SendInfo(this);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildRosterOpcode(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_ROSTER");
|
||||
|
||||
if (Guild* guild = _GetPlayerGuild(this))
|
||||
guild->HandleRoster(this);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildPromoteOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_PROMOTE");
|
||||
|
||||
std::string playerName;
|
||||
recvPacket >> playerName;
|
||||
|
||||
if (normalizePlayerName(playerName))
|
||||
if (Guild* guild = _GetPlayerGuild(this, true))
|
||||
guild->HandleUpdateMemberRank(this, playerName, false);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildDemoteOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_DEMOTE");
|
||||
|
||||
std::string playerName;
|
||||
recvPacket >> playerName;
|
||||
|
||||
if (normalizePlayerName(playerName))
|
||||
if (Guild* guild = _GetPlayerGuild(this, true))
|
||||
guild->HandleUpdateMemberRank(this, playerName, true);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildLeaveOpcode(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_LEAVE");
|
||||
|
||||
if (Guild* guild = _GetPlayerGuild(this, true))
|
||||
guild->HandleLeaveMember(this);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildDisbandOpcode(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_DISBAND");
|
||||
|
||||
if (Guild* guild = _GetPlayerGuild(this, true))
|
||||
guild->HandleDisband(this);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildLeaderOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_LEADER");
|
||||
|
||||
std::string name;
|
||||
recvPacket >> name;
|
||||
|
||||
if (normalizePlayerName(name))
|
||||
if (Guild* guild = _GetPlayerGuild(this, true))
|
||||
guild->HandleSetLeader(this, name);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildMOTDOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_MOTD");
|
||||
|
||||
std::string motd; // Empty by default
|
||||
if (!recvPacket.empty())
|
||||
recvPacket >> motd;
|
||||
|
||||
if (Guild* guild = _GetPlayerGuild(this, true))
|
||||
guild->HandleSetMOTD(this, motd);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildSetPublicNoteOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_SET_PUBLIC_NOTE");
|
||||
|
||||
std::string playerName;
|
||||
recvPacket >> playerName;
|
||||
|
||||
std::string publicNote;
|
||||
recvPacket >> publicNote;
|
||||
|
||||
if (normalizePlayerName(playerName))
|
||||
if (Guild* guild = _GetPlayerGuild(this, true))
|
||||
guild->HandleSetMemberNote(this, playerName, publicNote, false);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildSetOfficerNoteOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_SET_OFFICER_NOTE");
|
||||
|
||||
std::string playerName;
|
||||
recvPacket >> playerName;
|
||||
|
||||
std::string officerNote;
|
||||
recvPacket >> officerNote;
|
||||
|
||||
if (normalizePlayerName(playerName))
|
||||
if (Guild* guild = _GetPlayerGuild(this, true))
|
||||
guild->HandleSetMemberNote(this, playerName, officerNote, true);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildRankOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_RANK");
|
||||
|
||||
Guild* guild = _GetPlayerGuild(this, true);
|
||||
if (!guild)
|
||||
{
|
||||
recvPacket.rpos(recvPacket.wpos());
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 rankId;
|
||||
recvPacket >> rankId;
|
||||
|
||||
uint32 rights;
|
||||
recvPacket >> rights;
|
||||
|
||||
std::string rankName;
|
||||
recvPacket >> rankName;
|
||||
|
||||
uint32 money;
|
||||
recvPacket >> money;
|
||||
|
||||
GuildBankRightsAndSlotsVec rightsAndSlots(GUILD_BANK_MAX_TABS);
|
||||
for (uint8 tabId = 0; tabId < GUILD_BANK_MAX_TABS; ++tabId)
|
||||
{
|
||||
uint32 bankRights;
|
||||
uint32 slots;
|
||||
|
||||
recvPacket >> bankRights;
|
||||
recvPacket >> slots;
|
||||
|
||||
rightsAndSlots[tabId] = GuildBankRightsAndSlots(uint8(bankRights), slots);
|
||||
}
|
||||
|
||||
guild->HandleSetRankInfo(this, rankId, rankName, rights, money, rightsAndSlots);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildAddRankOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_ADD_RANK");
|
||||
|
||||
std::string rankName;
|
||||
recvPacket >> rankName;
|
||||
|
||||
if (Guild* guild = _GetPlayerGuild(this, true))
|
||||
guild->HandleAddNewRank(this, rankName);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildDelRankOpcode(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_DEL_RANK");
|
||||
|
||||
if (Guild* guild = _GetPlayerGuild(this, true))
|
||||
guild->HandleRemoveLowestRank(this);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildChangeInfoTextOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GUILD_INFO_TEXT");
|
||||
|
||||
std::string info;
|
||||
recvPacket >> info;
|
||||
|
||||
if (Guild* guild = _GetPlayerGuild(this, true))
|
||||
guild->HandleSetInfo(this, info);
|
||||
}
|
||||
|
||||
void WorldSession::HandleSaveGuildEmblemOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received MSG_SAVE_GUILD_EMBLEM");
|
||||
|
||||
uint64 vendorGuid;
|
||||
recvPacket >> vendorGuid;
|
||||
|
||||
EmblemInfo emblemInfo;
|
||||
emblemInfo.ReadPacket(recvPacket);
|
||||
|
||||
if (GetPlayer()->GetNPCIfCanInteractWith(vendorGuid, UNIT_NPC_FLAG_TABARDDESIGNER))
|
||||
{
|
||||
// Remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
if (Guild* guild = _GetPlayerGuild(this))
|
||||
guild->HandleSetEmblem(this, emblemInfo);
|
||||
else
|
||||
// "You are not part of a guild!";
|
||||
Guild::SendSaveEmblemResult(this, ERR_GUILDEMBLEM_NOGUILD);
|
||||
}
|
||||
else
|
||||
{
|
||||
// "That's not an emblem vendor!"
|
||||
Guild::SendSaveEmblemResult(this, ERR_GUILDEMBLEM_INVALIDVENDOR);
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleSaveGuildEmblemOpcode - Unit (GUID: %u) not found or you can't interact with him.", GUID_LOPART(vendorGuid));
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildEventLogQueryOpcode(WorldPacket& /* recvPacket */)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received (MSG_GUILD_EVENT_LOG_QUERY)");
|
||||
|
||||
if (Guild* guild = _GetPlayerGuild(this))
|
||||
guild->SendEventLog(this);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildBankMoneyWithdrawn(WorldPacket & /* recv_data */)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received (MSG_GUILD_BANK_MONEY_WITHDRAWN)");
|
||||
|
||||
if (Guild* guild = _GetPlayerGuild(this))
|
||||
guild->SendMoneyInfo(this);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildPermissions(WorldPacket& /* recv_data */)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received (MSG_GUILD_PERMISSIONS)");
|
||||
|
||||
if (Guild* guild = _GetPlayerGuild(this))
|
||||
guild->SendPermissions(this);
|
||||
}
|
||||
|
||||
// Called when clicking on Guild bank gameobject
|
||||
void WorldSession::HandleGuildBankerActivate(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received (CMSG_GUILD_BANKER_ACTIVATE)");
|
||||
|
||||
uint64 GoGuid;
|
||||
recv_data >> GoGuid;
|
||||
|
||||
uint8 unk;
|
||||
recv_data >> unk;
|
||||
|
||||
if (GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK))
|
||||
{
|
||||
if (Guild* guild = _GetPlayerGuild(this))
|
||||
guild->SendBankTabsInfo(this);
|
||||
else
|
||||
Guild::SendCommandResult(this, GUILD_UNK1, ERR_GUILD_PLAYER_NOT_IN_GUILD);
|
||||
}
|
||||
}
|
||||
|
||||
// Called when opening guild bank tab only (first one)
|
||||
void WorldSession::HandleGuildBankQueryTab(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received (CMSG_GUILD_BANK_QUERY_TAB)");
|
||||
|
||||
uint64 GoGuid;
|
||||
recv_data >> GoGuid;
|
||||
|
||||
uint8 tabId;
|
||||
recv_data >> tabId;
|
||||
|
||||
uint8 unk1;
|
||||
recv_data >> unk1;
|
||||
|
||||
if (GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK))
|
||||
if (Guild* guild = _GetPlayerGuild(this))
|
||||
guild->SendBankTabData(this, tabId);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildBankDepositMoney(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received (CMSG_GUILD_BANK_DEPOSIT_MONEY)");
|
||||
|
||||
uint64 GoGuid;
|
||||
recv_data >> GoGuid;
|
||||
|
||||
uint32 money;
|
||||
recv_data >> money;
|
||||
|
||||
if (GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK))
|
||||
if (money && GetPlayer()->HasEnoughMoney(money))
|
||||
if (Guild* guild = _GetPlayerGuild(this))
|
||||
guild->HandleMemberDepositMoney(this, money);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildBankWithdrawMoney(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received (CMSG_GUILD_BANK_WITHDRAW_MONEY)");
|
||||
|
||||
uint64 GoGuid;
|
||||
recv_data >> GoGuid;
|
||||
|
||||
uint32 money;
|
||||
recv_data >> money;
|
||||
|
||||
if (money)
|
||||
if (GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK))
|
||||
if (Guild* guild = _GetPlayerGuild(this))
|
||||
guild->HandleMemberWithdrawMoney(this, money);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildBankSwapItems(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received (CMSG_GUILD_BANK_SWAP_ITEMS)");
|
||||
|
||||
uint64 GoGuid;
|
||||
recv_data >> GoGuid;
|
||||
|
||||
if (!GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK))
|
||||
{
|
||||
recv_data.rfinish(); // Prevent additional spam at rejected packet
|
||||
return;
|
||||
}
|
||||
|
||||
Guild* guild = _GetPlayerGuild(this);
|
||||
if (!guild)
|
||||
{
|
||||
recv_data.rfinish(); // Prevent additional spam at rejected packet
|
||||
return;
|
||||
}
|
||||
|
||||
uint8 bankToBank;
|
||||
recv_data >> bankToBank;
|
||||
|
||||
uint8 tabId;
|
||||
uint8 slotId;
|
||||
uint32 itemEntry;
|
||||
uint32 splitedAmount = 0;
|
||||
|
||||
if (bankToBank)
|
||||
{
|
||||
uint8 destTabId;
|
||||
recv_data >> destTabId;
|
||||
|
||||
uint8 destSlotId;
|
||||
recv_data >> destSlotId;
|
||||
recv_data.read_skip<uint32>(); // Always 0
|
||||
|
||||
recv_data >> tabId;
|
||||
recv_data >> slotId;
|
||||
recv_data >> itemEntry;
|
||||
recv_data.read_skip<uint8>(); // Always 0
|
||||
|
||||
recv_data >> splitedAmount;
|
||||
|
||||
guild->SwapItems(GetPlayer(), tabId, slotId, destTabId, destSlotId, splitedAmount);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8 playerBag = NULL_BAG;
|
||||
uint8 playerSlotId = NULL_SLOT;
|
||||
uint8 toChar = 1;
|
||||
|
||||
recv_data >> tabId;
|
||||
recv_data >> slotId;
|
||||
recv_data >> itemEntry;
|
||||
|
||||
uint8 autoStore;
|
||||
recv_data >> autoStore;
|
||||
if (autoStore)
|
||||
{
|
||||
recv_data.read_skip<uint32>(); // autoStoreCount
|
||||
recv_data.read_skip<uint8>(); // ToChar (?), always and expected to be 1 (autostore only triggered in Bank -> Char)
|
||||
recv_data.read_skip<uint32>(); // Always 0
|
||||
}
|
||||
else
|
||||
{
|
||||
recv_data >> playerBag;
|
||||
recv_data >> playerSlotId;
|
||||
recv_data >> toChar;
|
||||
recv_data >> splitedAmount;
|
||||
}
|
||||
|
||||
// Player <-> Bank
|
||||
// Allow to work with inventory only
|
||||
if (!Player::IsInventoryPos(playerBag, playerSlotId) && !(playerBag == NULL_BAG && playerSlotId == NULL_SLOT))
|
||||
GetPlayer()->SendEquipError(EQUIP_ERR_NONE, NULL);
|
||||
else
|
||||
guild->SwapItemsWithInventory(GetPlayer(), toChar, tabId, slotId, playerBag, playerSlotId, splitedAmount);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildBankBuyTab(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received (CMSG_GUILD_BANK_BUY_TAB)");
|
||||
|
||||
uint64 GoGuid;
|
||||
recv_data >> GoGuid;
|
||||
|
||||
uint8 tabId;
|
||||
recv_data >> tabId;
|
||||
|
||||
if (GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK))
|
||||
if (Guild* guild = _GetPlayerGuild(this))
|
||||
guild->HandleBuyBankTab(this, tabId);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildBankUpdateTab(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received (CMSG_GUILD_BANK_UPDATE_TAB)");
|
||||
|
||||
uint64 GoGuid;
|
||||
recv_data >> GoGuid;
|
||||
|
||||
uint8 tabId;
|
||||
recv_data >> tabId;
|
||||
|
||||
std::string name;
|
||||
recv_data >> name;
|
||||
|
||||
std::string icon;
|
||||
recv_data >> icon;
|
||||
|
||||
if (!name.empty() && !icon.empty())
|
||||
if (GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK))
|
||||
if (Guild* guild = _GetPlayerGuild(this))
|
||||
guild->HandleSetBankTabInfo(this, tabId, name, icon);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildBankLogQuery(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received (MSG_GUILD_BANK_LOG_QUERY)");
|
||||
|
||||
uint8 tabId;
|
||||
recv_data >> tabId;
|
||||
|
||||
if (Guild* guild = _GetPlayerGuild(this))
|
||||
guild->SendBankLog(this, tabId);
|
||||
}
|
||||
|
||||
void WorldSession::HandleQueryGuildBankTabText(WorldPacket &recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received MSG_QUERY_GUILD_BANK_TEXT");
|
||||
|
||||
uint8 tabId;
|
||||
recv_data >> tabId;
|
||||
|
||||
if (Guild* guild = _GetPlayerGuild(this))
|
||||
guild->SendBankTabText(this, tabId);
|
||||
}
|
||||
|
||||
void WorldSession::HandleSetGuildBankTabText(WorldPacket &recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_SET_GUILD_BANK_TEXT");
|
||||
|
||||
uint8 tabId;
|
||||
recv_data >> tabId;
|
||||
|
||||
std::string text;
|
||||
recv_data >> text;
|
||||
|
||||
if (Guild* guild = _GetPlayerGuild(this))
|
||||
guild->SetBankTabText(tabId, text);
|
||||
}
|
||||
Executable
+1432
File diff suppressed because it is too large
Load Diff
Executable
+664
@@ -0,0 +1,664 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
*
|
||||
* 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 "WorldSession.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "DBCStores.h"
|
||||
#include "Player.h"
|
||||
#include "Group.h"
|
||||
#include "LFGMgr.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "GroupMgr.h"
|
||||
#include "InstanceScript.h"
|
||||
|
||||
void BuildPlayerLockDungeonBlock(WorldPacket& data, const LfgLockMap& lock)
|
||||
{
|
||||
data << uint32(lock.size()); // Size of lock dungeons
|
||||
for (LfgLockMap::const_iterator it = lock.begin(); it != lock.end(); ++it)
|
||||
{
|
||||
data << uint32(it->first); // Dungeon entry (id + type)
|
||||
data << uint32(it->second); // Lock status
|
||||
}
|
||||
}
|
||||
|
||||
void BuildPartyLockDungeonBlock(WorldPacket& data, const LfgLockPartyMap& lockMap)
|
||||
{
|
||||
data << uint8(lockMap.size());
|
||||
for (LfgLockPartyMap::const_iterator it = lockMap.begin(); it != lockMap.end(); ++it)
|
||||
{
|
||||
data << uint64(it->first); // Player guid
|
||||
BuildPlayerLockDungeonBlock(data, it->second);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleLfgJoinOpcode(WorldPacket& recv_data)
|
||||
{
|
||||
if (!sWorld->getBoolConfig(CONFIG_DUNGEON_FINDER_ENABLE) ||
|
||||
(GetPlayer()->GetGroup() && GetPlayer()->GetGroup()->GetLeaderGUID() != GetPlayer()->GetGUID() &&
|
||||
(GetPlayer()->GetGroup()->GetMembersCount() == MAXGROUPSIZE || !GetPlayer()->GetGroup()->isLFGGroup())))
|
||||
{
|
||||
recv_data.rfinish();
|
||||
return;
|
||||
}
|
||||
|
||||
uint8 numDungeons;
|
||||
uint32 dungeon;
|
||||
uint32 roles;
|
||||
|
||||
recv_data >> roles;
|
||||
recv_data.read_skip<uint16>(); // uint8 (always 0) - uint8 (always 0)
|
||||
recv_data >> numDungeons;
|
||||
if (!numDungeons)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_JOIN [" UI64FMTD "] no dungeons selected", GetPlayer()->GetGUID());
|
||||
recv_data.rfinish();
|
||||
return;
|
||||
}
|
||||
|
||||
LfgDungeonSet newDungeons;
|
||||
for (int8 i = 0 ; i < numDungeons; ++i)
|
||||
{
|
||||
recv_data >> dungeon;
|
||||
newDungeons.insert((dungeon & 0x00FFFFFF)); // remove the type from the dungeon entry
|
||||
}
|
||||
|
||||
recv_data.read_skip<uint32>(); // for 0..uint8 (always 3) { uint8 (always 0) }
|
||||
|
||||
std::string comment;
|
||||
recv_data >> comment;
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_JOIN [" UI64FMTD "] roles: %u, Dungeons: %u, Comment: %s", GetPlayer()->GetGUID(), roles, uint8(newDungeons.size()), comment.c_str());
|
||||
sLFGMgr->Join(GetPlayer(), uint8(roles), newDungeons, comment);
|
||||
}
|
||||
|
||||
void WorldSession::HandleLfgLeaveOpcode(WorldPacket& /*recv_data*/)
|
||||
{
|
||||
Group* grp = GetPlayer()->GetGroup();
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_LEAVE [" UI64FMTD "] in group: %u", GetPlayer()->GetGUID(), grp ? 1 : 0);
|
||||
|
||||
// Check cheating - only leader can leave the queue
|
||||
if (!grp || grp->GetLeaderGUID() == GetPlayer()->GetGUID())
|
||||
sLFGMgr->Leave(GetPlayer(), grp);
|
||||
}
|
||||
|
||||
void WorldSession::HandleLfgProposalResultOpcode(WorldPacket& recv_data)
|
||||
{
|
||||
uint32 lfgGroupID; // Internal lfgGroupID
|
||||
bool accept; // Accept to join?
|
||||
recv_data >> lfgGroupID;
|
||||
recv_data >> accept;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_PROPOSAL_RESULT [" UI64FMTD "] proposal: %u accept: %u", GetPlayer()->GetGUID(), lfgGroupID, accept ? 1 : 0);
|
||||
sLFGMgr->UpdateProposal(lfgGroupID, GetPlayer()->GetGUID(), accept);
|
||||
}
|
||||
|
||||
void WorldSession::HandleLfgSetRolesOpcode(WorldPacket& recv_data)
|
||||
{
|
||||
uint8 roles;
|
||||
recv_data >> roles; // Player Group Roles
|
||||
uint64 guid = GetPlayer()->GetGUID();
|
||||
Group* grp = GetPlayer()->GetGroup();
|
||||
if (!grp)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_SET_ROLES [" UI64FMTD "] Not in group", guid);
|
||||
return;
|
||||
}
|
||||
uint64 gguid = grp->GetGUID();
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_SET_ROLES: Group [" UI64FMTD "], Player [" UI64FMTD "], Roles: %u", gguid, guid, roles);
|
||||
sLFGMgr->UpdateRoleCheck(gguid, guid, roles);
|
||||
}
|
||||
|
||||
void WorldSession::HandleLfgSetCommentOpcode(WorldPacket& recv_data)
|
||||
{
|
||||
std::string comment;
|
||||
recv_data >> comment;
|
||||
uint64 guid = GetPlayer()->GetGUID();
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_SET_LFG_COMMENT [" UI64FMTD "] comment: %s", guid, comment.c_str());
|
||||
|
||||
sLFGMgr->SetComment(guid, comment);
|
||||
}
|
||||
|
||||
void WorldSession::HandleLfgSetBootVoteOpcode(WorldPacket& recv_data)
|
||||
{
|
||||
bool agree; // Agree to kick player
|
||||
recv_data >> agree;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_SET_BOOT_VOTE [" UI64FMTD "] agree: %u", GetPlayer()->GetGUID(), agree ? 1 : 0);
|
||||
sLFGMgr->UpdateBoot(GetPlayer(), agree);
|
||||
}
|
||||
|
||||
void WorldSession::HandleLfgTeleportOpcode(WorldPacket& recv_data)
|
||||
{
|
||||
bool out;
|
||||
recv_data >> out;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_TELEPORT [" UI64FMTD "] out: %u", GetPlayer()->GetGUID(), out ? 1 : 0);
|
||||
sLFGMgr->TeleportPlayer(GetPlayer(), out, true);
|
||||
}
|
||||
|
||||
void WorldSession::HandleLfgPlayerLockInfoRequestOpcode(WorldPacket& /*recv_data*/)
|
||||
{
|
||||
uint64 guid = GetPlayer()->GetGUID();
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFD_PLAYER_LOCK_INFO_REQUEST [" UI64FMTD "]", guid);
|
||||
|
||||
// Get Random dungeons that can be done at a certain level and expansion
|
||||
// FIXME - Should return seasonals (when not disabled)
|
||||
LfgDungeonSet randomDungeons;
|
||||
uint8 level = GetPlayer()->getLevel();
|
||||
uint8 expansion = GetPlayer()->GetSession()->Expansion();
|
||||
for (uint32 i = 0; i < sLFGDungeonStore.GetNumRows(); ++i)
|
||||
{
|
||||
LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(i);
|
||||
if (dungeon && dungeon->type == LFG_TYPE_RANDOM && dungeon->expansion <= expansion &&
|
||||
dungeon->minlevel <= level && level <= dungeon->maxlevel)
|
||||
randomDungeons.insert(dungeon->Entry());
|
||||
}
|
||||
|
||||
// Get player locked Dungeons
|
||||
LfgLockMap lock = sLFGMgr->GetLockedDungeons(guid);
|
||||
uint32 rsize = uint32(randomDungeons.size());
|
||||
uint32 lsize = uint32(lock.size());
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_PLAYER_INFO [" UI64FMTD "]", guid);
|
||||
WorldPacket data(SMSG_LFG_PLAYER_INFO, 1 + rsize * (4 + 1 + 4 + 4 + 4 + 4 + 1 + 4 + 4 + 4) + 4 + lsize * (1 + 4 + 4 + 4 + 4 + 1 + 4 + 4 + 4));
|
||||
|
||||
data << uint8(randomDungeons.size()); // Random Dungeon count
|
||||
for (LfgDungeonSet::const_iterator it = randomDungeons.begin(); it != randomDungeons.end(); ++it)
|
||||
{
|
||||
data << uint32(*it); // Dungeon Entry (id + type)
|
||||
LfgReward const* reward = sLFGMgr->GetRandomDungeonReward(*it, level);
|
||||
Quest const* qRew = NULL;
|
||||
uint8 done = 0;
|
||||
if (reward)
|
||||
{
|
||||
qRew = sObjectMgr->GetQuestTemplate(reward->reward[0].questId);
|
||||
if (qRew)
|
||||
{
|
||||
done = !GetPlayer()->CanRewardQuest(qRew, false);
|
||||
if (done)
|
||||
qRew = sObjectMgr->GetQuestTemplate(reward->reward[1].questId);
|
||||
}
|
||||
}
|
||||
if (qRew)
|
||||
{
|
||||
data << uint8(done);
|
||||
data << uint32(qRew->GetRewOrReqMoney());
|
||||
data << uint32(qRew->XPValue(GetPlayer()));
|
||||
data << uint32(reward->reward[done].variableMoney);
|
||||
data << uint32(reward->reward[done].variableXP);
|
||||
data << uint8(qRew->GetRewItemsCount());
|
||||
if (qRew->GetRewItemsCount())
|
||||
{
|
||||
ItemTemplate const* iProto = NULL;
|
||||
for (uint8 i = 0; i < QUEST_REWARDS_COUNT; ++i)
|
||||
{
|
||||
if (!qRew->RewardItemId[i])
|
||||
continue;
|
||||
|
||||
iProto = sObjectMgr->GetItemTemplate(qRew->RewardItemId[i]);
|
||||
|
||||
data << uint32(qRew->RewardItemId[i]);
|
||||
data << uint32(iProto ? iProto->DisplayInfoID : 0);
|
||||
data << uint32(qRew->RewardItemIdCount[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
data << uint8(0);
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint8(0);
|
||||
}
|
||||
}
|
||||
BuildPlayerLockDungeonBlock(data, lock);
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleLfgPartyLockInfoRequestOpcode(WorldPacket& /*recv_data*/)
|
||||
{
|
||||
uint64 guid = GetPlayer()->GetGUID();
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFD_PARTY_LOCK_INFO_REQUEST [" UI64FMTD "]", guid);
|
||||
|
||||
Group* grp = GetPlayer()->GetGroup();
|
||||
if (!grp)
|
||||
return;
|
||||
|
||||
// Get the locked dungeons of the other party members
|
||||
LfgLockPartyMap lockMap;
|
||||
for (GroupReference* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
|
||||
{
|
||||
Player* plrg = itr->getSource();
|
||||
if (!plrg)
|
||||
continue;
|
||||
|
||||
uint64 pguid = plrg->GetGUID();
|
||||
if (pguid == guid)
|
||||
continue;
|
||||
|
||||
lockMap[pguid] = sLFGMgr->GetLockedDungeons(pguid);
|
||||
}
|
||||
|
||||
uint32 size = 0;
|
||||
for (LfgLockPartyMap::const_iterator it = lockMap.begin(); it != lockMap.end(); ++it)
|
||||
size += 8 + 4 + uint32(it->second.size()) * (4 + 4);
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_PARTY_INFO [" UI64FMTD "]", guid);
|
||||
WorldPacket data(SMSG_LFG_PARTY_INFO, 1 + size);
|
||||
BuildPartyLockDungeonBlock(data, lockMap);
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleLfrSearchOpcode(WorldPacket& recv_data)
|
||||
{
|
||||
uint32 entry; // Raid id to search
|
||||
recv_data >> entry;
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_SEARCH_LFG_JOIN [" UI64FMTD "] dungeon entry: %u", GetPlayer()->GetGUID(), entry);
|
||||
//SendLfrUpdateListOpcode(entry);
|
||||
}
|
||||
|
||||
void WorldSession::HandleLfrLeaveOpcode(WorldPacket& recv_data)
|
||||
{
|
||||
uint32 dungeonId; // Raid id queue to leave
|
||||
recv_data >> dungeonId;
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_SEARCH_LFG_LEAVE [" UI64FMTD "] dungeonId: %u", GetPlayer()->GetGUID(), dungeonId);
|
||||
//sLFGMgr->LeaveLfr(GetPlayer(), dungeonId);
|
||||
}
|
||||
|
||||
void WorldSession::SendLfgUpdatePlayer(const LfgUpdateData& updateData)
|
||||
{
|
||||
bool queued = false;
|
||||
bool extrainfo = false;
|
||||
|
||||
switch (updateData.updateType)
|
||||
{
|
||||
case LFG_UPDATETYPE_JOIN_PROPOSAL:
|
||||
case LFG_UPDATETYPE_ADDED_TO_QUEUE:
|
||||
queued = true;
|
||||
extrainfo = true;
|
||||
break;
|
||||
//case LFG_UPDATETYPE_CLEAR_LOCK_LIST: // TODO: Sometimes has extrainfo - Check ocurrences...
|
||||
case LFG_UPDATETYPE_PROPOSAL_BEGIN:
|
||||
extrainfo = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
uint64 guid = GetPlayer()->GetGUID();
|
||||
uint8 size = uint8(updateData.dungeons.size());
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_UPDATE_PLAYER [" UI64FMTD "] updatetype: %u", guid, updateData.updateType);
|
||||
WorldPacket data(SMSG_LFG_UPDATE_PLAYER, 1 + 1 + (extrainfo ? 1 : 0) * (1 + 1 + 1 + 1 + size * 4 + updateData.comment.length()));
|
||||
data << uint8(updateData.updateType); // Lfg Update type
|
||||
data << uint8(extrainfo); // Extra info
|
||||
if (extrainfo)
|
||||
{
|
||||
data << uint8(queued); // Join the queue
|
||||
data << uint8(0); // unk - Always 0
|
||||
data << uint8(0); // unk - Always 0
|
||||
|
||||
data << uint8(size);
|
||||
if (size)
|
||||
for (LfgDungeonSet::const_iterator it = updateData.dungeons.begin(); it != updateData.dungeons.end(); ++it)
|
||||
data << uint32(*it);
|
||||
data << updateData.comment;
|
||||
}
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::SendLfgUpdateParty(const LfgUpdateData& updateData)
|
||||
{
|
||||
bool join = false;
|
||||
bool extrainfo = false;
|
||||
bool queued = false;
|
||||
|
||||
switch (updateData.updateType)
|
||||
{
|
||||
case LFG_UPDATETYPE_JOIN_PROPOSAL:
|
||||
extrainfo = true;
|
||||
break;
|
||||
case LFG_UPDATETYPE_ADDED_TO_QUEUE:
|
||||
extrainfo = true;
|
||||
join = true;
|
||||
queued = true;
|
||||
break;
|
||||
case LFG_UPDATETYPE_CLEAR_LOCK_LIST:
|
||||
// join = true; // TODO: Sometimes queued and extrainfo - Check ocurrences...
|
||||
queued = true;
|
||||
break;
|
||||
case LFG_UPDATETYPE_PROPOSAL_BEGIN:
|
||||
extrainfo = true;
|
||||
join = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
uint64 guid = GetPlayer()->GetGUID();
|
||||
uint8 size = uint8(updateData.dungeons.size());
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_UPDATE_PARTY [" UI64FMTD "] updatetype: %u", guid, updateData.updateType);
|
||||
WorldPacket data(SMSG_LFG_UPDATE_PARTY, 1 + 1 + (extrainfo ? 1 : 0) * (1 + 1 + 1 + 1 + 1 + size * 4 + updateData.comment.length()));
|
||||
data << uint8(updateData.updateType); // Lfg Update type
|
||||
data << uint8(extrainfo); // Extra info
|
||||
if (extrainfo)
|
||||
{
|
||||
data << uint8(join); // LFG Join
|
||||
data << uint8(queued); // Join the queue
|
||||
data << uint8(0); // unk - Always 0
|
||||
data << uint8(0); // unk - Always 0
|
||||
for (uint8 i = 0; i < 3; ++i)
|
||||
data << uint8(0); // unk - Always 0
|
||||
|
||||
data << uint8(size);
|
||||
if (size)
|
||||
for (LfgDungeonSet::const_iterator it = updateData.dungeons.begin(); it != updateData.dungeons.end(); ++it)
|
||||
data << uint32(*it);
|
||||
data << updateData.comment;
|
||||
}
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::SendLfgRoleChosen(uint64 guid, uint8 roles)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_ROLE_CHOSEN [" UI64FMTD "] guid: [" UI64FMTD "] roles: %u", GetPlayer()->GetGUID(), guid, roles);
|
||||
|
||||
WorldPacket data(SMSG_LFG_ROLE_CHOSEN, 8 + 1 + 4);
|
||||
data << uint64(guid); // Guid
|
||||
data << uint8(roles > 0); // Ready
|
||||
data << uint32(roles); // Roles
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::SendLfgRoleCheckUpdate(const LfgRoleCheck* pRoleCheck)
|
||||
{
|
||||
ASSERT(pRoleCheck);
|
||||
LfgDungeonSet dungeons;
|
||||
if (pRoleCheck->rDungeonId)
|
||||
dungeons.insert(pRoleCheck->rDungeonId);
|
||||
else
|
||||
dungeons = pRoleCheck->dungeons;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_ROLE_CHECK_UPDATE [" UI64FMTD "]", GetPlayer()->GetGUID());
|
||||
WorldPacket data(SMSG_LFG_ROLE_CHECK_UPDATE, 4 + 1 + 1 + dungeons.size() * 4 + 1 + pRoleCheck->roles.size() * (8 + 1 + 4 + 1));
|
||||
|
||||
data << uint32(pRoleCheck->state); // Check result
|
||||
data << uint8(pRoleCheck->state == LFG_ROLECHECK_INITIALITING);
|
||||
data << uint8(dungeons.size()); // Number of dungeons
|
||||
if (!dungeons.empty())
|
||||
{
|
||||
for (LfgDungeonSet::iterator it = dungeons.begin(); it != dungeons.end(); ++it)
|
||||
{
|
||||
LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(*it);
|
||||
data << uint32(dungeon ? dungeon->Entry() : 0); // Dungeon
|
||||
}
|
||||
}
|
||||
|
||||
data << uint8(pRoleCheck->roles.size()); // Players in group
|
||||
if (!pRoleCheck->roles.empty())
|
||||
{
|
||||
// Leader info MUST be sent 1st :S
|
||||
uint64 guid = pRoleCheck->leader;
|
||||
uint8 roles = pRoleCheck->roles.find(guid)->second;
|
||||
data << uint64(guid); // Guid
|
||||
data << uint8(roles > 0); // Ready
|
||||
data << uint32(roles); // Roles
|
||||
Player* player = ObjectAccessor::FindPlayer(guid);
|
||||
data << uint8(player ? player->getLevel() : 0); // Level
|
||||
|
||||
for (LfgRolesMap::const_iterator it = pRoleCheck->roles.begin(); it != pRoleCheck->roles.end(); ++it)
|
||||
{
|
||||
if (it->first == pRoleCheck->leader)
|
||||
continue;
|
||||
|
||||
guid = it->first;
|
||||
roles = it->second;
|
||||
data << uint64(guid); // Guid
|
||||
data << uint8(roles > 0); // Ready
|
||||
data << uint32(roles); // Roles
|
||||
player = ObjectAccessor::FindPlayer(guid);
|
||||
data << uint8(player ? player->getLevel() : 0); // Level
|
||||
}
|
||||
}
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::SendLfgJoinResult(const LfgJoinResultData& joinData)
|
||||
{
|
||||
uint32 size = 0;
|
||||
for (LfgLockPartyMap::const_iterator it = joinData.lockmap.begin(); it != joinData.lockmap.end(); ++it)
|
||||
size += 8 + 4 + uint32(it->second.size()) * (4 + 4);
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_JOIN_RESULT [" UI64FMTD "] checkResult: %u checkValue: %u", GetPlayer()->GetGUID(), joinData.result, joinData.state);
|
||||
WorldPacket data(SMSG_LFG_JOIN_RESULT, 4 + 4 + size);
|
||||
data << uint32(joinData.result); // Check Result
|
||||
data << uint32(joinData.state); // Check Value
|
||||
if (!joinData.lockmap.empty())
|
||||
BuildPartyLockDungeonBlock(data, joinData.lockmap);
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::SendLfgQueueStatus(uint32 dungeon, int32 waitTime, int32 avgWaitTime, int32 waitTimeTanks, int32 waitTimeHealer, int32 waitTimeDps, uint32 queuedTime, uint8 tanks, uint8 healers, uint8 dps)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_QUEUE_STATUS [" UI64FMTD "] dungeon: %u - waitTime: %d - avgWaitTime: %d - waitTimeTanks: %d - waitTimeHealer: %d - waitTimeDps: %d - queuedTime: %u - tanks: %u - healers: %u - dps: %u", GetPlayer()->GetGUID(), dungeon, waitTime, avgWaitTime, waitTimeTanks, waitTimeHealer, waitTimeDps, queuedTime, tanks, healers, dps);
|
||||
|
||||
WorldPacket data(SMSG_LFG_QUEUE_STATUS, 4 + 4 + 4 + 4 + 4 +4 + 1 + 1 + 1 + 4);
|
||||
data << uint32(dungeon); // Dungeon
|
||||
data << int32(avgWaitTime); // Average Wait time
|
||||
data << int32(waitTime); // Wait Time
|
||||
data << int32(waitTimeTanks); // Wait Tanks
|
||||
data << int32(waitTimeHealer); // Wait Healers
|
||||
data << int32(waitTimeDps); // Wait Dps
|
||||
data << uint8(tanks); // Tanks needed
|
||||
data << uint8(healers); // Healers needed
|
||||
data << uint8(dps); // Dps needed
|
||||
data << uint32(queuedTime); // Player wait time in queue
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::SendLfgPlayerReward(uint32 rdungeonEntry, uint32 sdungeonEntry, uint8 done, const LfgReward* reward, const Quest* qRew)
|
||||
{
|
||||
if (!rdungeonEntry || !sdungeonEntry || !qRew)
|
||||
return;
|
||||
|
||||
uint8 itemNum = uint8(qRew ? qRew->GetRewItemsCount() : 0);
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_PLAYER_REWARD [" UI64FMTD "] rdungeonEntry: %u - sdungeonEntry: %u - done: %u", GetPlayer()->GetGUID(), rdungeonEntry, sdungeonEntry, done);
|
||||
WorldPacket data(SMSG_LFG_PLAYER_REWARD, 4 + 4 + 1 + 4 + 4 + 4 + 4 + 4 + 1 + itemNum * (4 + 4 + 4));
|
||||
data << uint32(rdungeonEntry); // Random Dungeon Finished
|
||||
data << uint32(sdungeonEntry); // Dungeon Finished
|
||||
data << uint8(done);
|
||||
data << uint32(1);
|
||||
data << uint32(qRew->GetRewOrReqMoney());
|
||||
data << uint32(qRew->XPValue(GetPlayer()));
|
||||
data << uint32(reward->reward[done].variableMoney);
|
||||
data << uint32(reward->reward[done].variableXP);
|
||||
data << uint8(itemNum);
|
||||
if (itemNum)
|
||||
{
|
||||
ItemTemplate const* iProto = NULL;
|
||||
for (uint8 i = 0; i < QUEST_REWARDS_COUNT; ++i)
|
||||
{
|
||||
if (!qRew->RewardItemId[i])
|
||||
continue;
|
||||
|
||||
iProto = sObjectMgr->GetItemTemplate(qRew->RewardItemId[i]);
|
||||
|
||||
data << uint32(qRew->RewardItemId[i]);
|
||||
data << uint32(iProto ? iProto->DisplayInfoID : 0);
|
||||
data << uint32(qRew->RewardItemIdCount[i]);
|
||||
}
|
||||
}
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::SendLfgBootPlayer(const LfgPlayerBoot* pBoot)
|
||||
{
|
||||
uint64 guid = GetPlayer()->GetGUID();
|
||||
LfgAnswer playerVote = pBoot->votes.find(guid)->second;
|
||||
uint8 votesNum = 0;
|
||||
uint8 agreeNum = 0;
|
||||
uint32 secsleft = uint8((pBoot->cancelTime - time(NULL)) / 1000);
|
||||
for (LfgAnswerMap::const_iterator it = pBoot->votes.begin(); it != pBoot->votes.end(); ++it)
|
||||
{
|
||||
if (it->second != LFG_ANSWER_PENDING)
|
||||
{
|
||||
++votesNum;
|
||||
if (it->second == LFG_ANSWER_AGREE)
|
||||
++agreeNum;
|
||||
}
|
||||
}
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_BOOT_PLAYER [" UI64FMTD "] inProgress: %u - didVote: %u - agree: %u - victim: [" UI64FMTD "] votes: %u - agrees: %u - left: %u - needed: %u - reason %s",
|
||||
guid, uint8(pBoot->inProgress), uint8(playerVote != LFG_ANSWER_PENDING), uint8(playerVote == LFG_ANSWER_AGREE), pBoot->victim, votesNum, agreeNum, secsleft, pBoot->votedNeeded, pBoot->reason.c_str());
|
||||
WorldPacket data(SMSG_LFG_BOOT_PLAYER, 1 + 1 + 1 + 8 + 4 + 4 + 4 + 4 + pBoot->reason.length());
|
||||
data << uint8(pBoot->inProgress); // Vote in progress
|
||||
data << uint8(playerVote != LFG_ANSWER_PENDING); // Did Vote
|
||||
data << uint8(playerVote == LFG_ANSWER_AGREE); // Agree
|
||||
data << uint64(pBoot->victim); // Victim GUID
|
||||
data << uint32(votesNum); // Total Votes
|
||||
data << uint32(agreeNum); // Agree Count
|
||||
data << uint32(secsleft); // Time Left
|
||||
data << uint32(pBoot->votedNeeded); // Needed Votes
|
||||
data << pBoot->reason.c_str(); // Kick reason
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::SendLfgUpdateProposal(uint32 proposalId, const LfgProposal* pProp)
|
||||
{
|
||||
if (!pProp)
|
||||
return;
|
||||
|
||||
uint64 guid = GetPlayer()->GetGUID();
|
||||
LfgProposalPlayerMap::const_iterator itPlayer = pProp->players.find(guid);
|
||||
if (itPlayer == pProp->players.end()) // Player MUST be in the proposal
|
||||
return;
|
||||
|
||||
LfgProposalPlayer* ppPlayer = itPlayer->second;
|
||||
uint32 pLowGroupGuid = ppPlayer->groupLowGuid;
|
||||
uint32 dLowGuid = pProp->groupLowGuid;
|
||||
uint32 dungeonId = pProp->dungeonId;
|
||||
bool isSameDungeon = false;
|
||||
bool isContinue = false;
|
||||
Group* grp = dLowGuid ? sGroupMgr->GetGroupByGUID(dLowGuid) : NULL;
|
||||
uint32 completedEncounters = 0;
|
||||
if (grp)
|
||||
{
|
||||
uint64 gguid = grp->GetGUID();
|
||||
isContinue = grp->isLFGGroup() && sLFGMgr->GetState(gguid) != LFG_STATE_FINISHED_DUNGEON;
|
||||
isSameDungeon = GetPlayer()->GetGroup() == grp && isContinue;
|
||||
}
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_PROPOSAL_UPDATE [" UI64FMTD "] state: %u", GetPlayer()->GetGUID(), pProp->state);
|
||||
WorldPacket data(SMSG_LFG_PROPOSAL_UPDATE, 4 + 1 + 4 + 4 + 1 + 1 + pProp->players.size() * (4 + 1 + 1 + 1 + 1 +1));
|
||||
|
||||
if (!isContinue) // Only show proposal dungeon if it's continue
|
||||
{
|
||||
LfgDungeonSet playerDungeons = sLFGMgr->GetSelectedDungeons(guid);
|
||||
if (playerDungeons.size() == 1)
|
||||
dungeonId = (*playerDungeons.begin());
|
||||
}
|
||||
|
||||
if (LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(dungeonId))
|
||||
{
|
||||
dungeonId = dungeon->Entry();
|
||||
|
||||
// Select a player inside to be get completed encounters from
|
||||
if (grp)
|
||||
{
|
||||
for (GroupReference* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
|
||||
{
|
||||
Player* groupMember = itr->getSource();
|
||||
if (groupMember && groupMember->GetMapId() == uint32(dungeon->map))
|
||||
{
|
||||
if (InstanceScript* instance = groupMember->GetInstanceScript())
|
||||
completedEncounters = instance->GetCompletedEncounterMask();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data << uint32(dungeonId); // Dungeon
|
||||
data << uint8(pProp->state); // Result state
|
||||
data << uint32(proposalId); // Internal Proposal ID
|
||||
data << uint32(completedEncounters); // Bosses killed
|
||||
data << uint8(isSameDungeon); // Silent (show client window)
|
||||
data << uint8(pProp->players.size()); // Group size
|
||||
|
||||
for (itPlayer = pProp->players.begin(); itPlayer != pProp->players.end(); ++itPlayer)
|
||||
{
|
||||
ppPlayer = itPlayer->second;
|
||||
data << uint32(ppPlayer->role); // Role
|
||||
data << uint8(itPlayer->first == guid); // Self player
|
||||
if (!ppPlayer->groupLowGuid) // Player not it a group
|
||||
{
|
||||
data << uint8(0); // Not in dungeon
|
||||
data << uint8(0); // Not same group
|
||||
}
|
||||
else
|
||||
{
|
||||
data << uint8(ppPlayer->groupLowGuid == dLowGuid); // In dungeon (silent)
|
||||
data << uint8(ppPlayer->groupLowGuid == pLowGroupGuid); // Same Group than player
|
||||
}
|
||||
data << uint8(ppPlayer->accept != LFG_ANSWER_PENDING); // Answered
|
||||
data << uint8(ppPlayer->accept == LFG_ANSWER_AGREE); // Accepted
|
||||
}
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::SendLfgUpdateSearch(bool update)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_UPDATE_SEARCH [" UI64FMTD "] update: %u", GetPlayer()->GetGUID(), update ? 1 : 0);
|
||||
WorldPacket data(SMSG_LFG_UPDATE_SEARCH, 1);
|
||||
data << uint8(update); // In Lfg Queue?
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::SendLfgDisabled()
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_DISABLED [" UI64FMTD "]", GetPlayer()->GetGUID());
|
||||
WorldPacket data(SMSG_LFG_DISABLED, 0);
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::SendLfgOfferContinue(uint32 dungeonEntry)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_OFFER_CONTINUE [" UI64FMTD "] dungeon entry: %u", GetPlayer()->GetGUID(), dungeonEntry);
|
||||
WorldPacket data(SMSG_LFG_OFFER_CONTINUE, 4);
|
||||
data << uint32(dungeonEntry);
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::SendLfgTeleportError(uint8 err)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_TELEPORT_DENIED [" UI64FMTD "] reason: %u", GetPlayer()->GetGUID(), err);
|
||||
WorldPacket data(SMSG_LFG_TELEPORT_DENIED, 4);
|
||||
data << uint32(err); // Error
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
/*
|
||||
void WorldSession::SendLfrUpdateListOpcode(uint32 dungeonEntry)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_PACKETIO, "SMSG_UPDATE_LFG_LIST [" UI64FMTD "] dungeon entry: %u", GetPlayer()->GetGUID(), dungeonEntry);
|
||||
WorldPacket data(SMSG_UPDATE_LFG_LIST);
|
||||
SendPacket(&data);
|
||||
}
|
||||
*/
|
||||
Executable
+508
@@ -0,0 +1,508 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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 "Common.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "Log.h"
|
||||
#include "Corpse.h"
|
||||
#include "GameObject.h"
|
||||
#include "Player.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "WorldSession.h"
|
||||
#include "LootMgr.h"
|
||||
#include "Object.h"
|
||||
#include "Group.h"
|
||||
#include "World.h"
|
||||
#include "Util.h"
|
||||
|
||||
void WorldSession::HandleAutostoreLootItemOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_AUTOSTORE_LOOT_ITEM");
|
||||
Player* player = GetPlayer();
|
||||
uint64 lguid = player->GetLootGUID();
|
||||
Loot* loot = NULL;
|
||||
uint8 lootSlot = 0;
|
||||
|
||||
recv_data >> lootSlot;
|
||||
|
||||
if (IS_GAMEOBJECT_GUID(lguid))
|
||||
{
|
||||
GameObject* go = player->GetMap()->GetGameObject(lguid);
|
||||
|
||||
// not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO
|
||||
if (!go || ((go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player, INTERACTION_DISTANCE)))
|
||||
{
|
||||
player->SendLootRelease(lguid);
|
||||
return;
|
||||
}
|
||||
|
||||
loot = &go->loot;
|
||||
}
|
||||
else if (IS_ITEM_GUID(lguid))
|
||||
{
|
||||
Item* pItem = player->GetItemByGuid(lguid);
|
||||
|
||||
if (!pItem)
|
||||
{
|
||||
player->SendLootRelease(lguid);
|
||||
return;
|
||||
}
|
||||
|
||||
loot = &pItem->loot;
|
||||
}
|
||||
else if (IS_CORPSE_GUID(lguid))
|
||||
{
|
||||
Corpse* bones = ObjectAccessor::GetCorpse(*player, lguid);
|
||||
if (!bones)
|
||||
{
|
||||
player->SendLootRelease(lguid);
|
||||
return;
|
||||
}
|
||||
|
||||
loot = &bones->loot;
|
||||
}
|
||||
else
|
||||
{
|
||||
Creature* creature = GetPlayer()->GetMap()->GetCreature(lguid);
|
||||
|
||||
bool ok_loot = creature && creature->isAlive() == (player->getClass() == CLASS_ROGUE && creature->lootForPickPocketed);
|
||||
|
||||
if (!ok_loot || !creature->IsWithinDistInMap(_player, INTERACTION_DISTANCE))
|
||||
{
|
||||
player->SendLootRelease(lguid);
|
||||
return;
|
||||
}
|
||||
|
||||
loot = &creature->loot;
|
||||
}
|
||||
|
||||
player->StoreLootItem(lootSlot, loot);
|
||||
}
|
||||
|
||||
void WorldSession::HandleLootMoneyOpcode(WorldPacket & /*recv_data*/)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_LOOT_MONEY");
|
||||
|
||||
Player* player = GetPlayer();
|
||||
uint64 guid = player->GetLootGUID();
|
||||
if (!guid)
|
||||
return;
|
||||
|
||||
Loot* loot = NULL;
|
||||
bool shareMoney = true;
|
||||
|
||||
switch (GUID_HIPART(guid))
|
||||
{
|
||||
case HIGHGUID_GAMEOBJECT:
|
||||
{
|
||||
GameObject* go = GetPlayer()->GetMap()->GetGameObject(guid);
|
||||
|
||||
// do not check distance for GO if player is the owner of it (ex. fishing bobber)
|
||||
if (go && ((go->GetOwnerGUID() == player->GetGUID() || go->IsWithinDistInMap(player, INTERACTION_DISTANCE))))
|
||||
loot = &go->loot;
|
||||
|
||||
break;
|
||||
}
|
||||
case HIGHGUID_CORPSE: // remove insignia ONLY in BG
|
||||
{
|
||||
Corpse* bones = ObjectAccessor::GetCorpse(*player, guid);
|
||||
|
||||
if (bones && bones->IsWithinDistInMap(player, INTERACTION_DISTANCE))
|
||||
{
|
||||
loot = &bones->loot;
|
||||
shareMoney = false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case HIGHGUID_ITEM:
|
||||
{
|
||||
if (Item* item = player->GetItemByGuid(guid))
|
||||
{
|
||||
loot = &item->loot;
|
||||
shareMoney = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case HIGHGUID_UNIT:
|
||||
case HIGHGUID_VEHICLE:
|
||||
{
|
||||
Creature* creature = player->GetMap()->GetCreature(guid);
|
||||
bool lootAllowed = creature && creature->isAlive() == (player->getClass() == CLASS_ROGUE && creature->lootForPickPocketed);
|
||||
if (lootAllowed && creature->IsWithinDistInMap(player, INTERACTION_DISTANCE))
|
||||
{
|
||||
loot = &creature->loot;
|
||||
if (creature->isAlive())
|
||||
shareMoney = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return; // unlootable type
|
||||
}
|
||||
|
||||
if (loot)
|
||||
{
|
||||
loot->NotifyMoneyRemoved();
|
||||
if (shareMoney && player->GetGroup()) //item, pickpocket and players can be looted only single player
|
||||
{
|
||||
Group* group = player->GetGroup();
|
||||
|
||||
std::vector<Player*> playersNear;
|
||||
for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
|
||||
{
|
||||
Player* member = itr->getSource();
|
||||
if (!member)
|
||||
continue;
|
||||
|
||||
if (player->IsWithinDistInMap(member, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false))
|
||||
playersNear.push_back(member);
|
||||
}
|
||||
|
||||
uint32 goldPerPlayer = uint32((loot->gold) / (playersNear.size()));
|
||||
|
||||
for (std::vector<Player*>::const_iterator i = playersNear.begin(); i != playersNear.end(); ++i)
|
||||
{
|
||||
(*i)->ModifyMoney(goldPerPlayer);
|
||||
(*i)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY, goldPerPlayer);
|
||||
|
||||
WorldPacket data(SMSG_LOOT_MONEY_NOTIFY, 4 + 1);
|
||||
data << uint32(goldPerPlayer);
|
||||
data << uint8(playersNear.size() > 1 ? 0 : 1); // Controls the text displayed in chat. 0 is "Your share is..." and 1 is "You loot..."
|
||||
(*i)->GetSession()->SendPacket(&data);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
player->ModifyMoney(loot->gold);
|
||||
player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY, loot->gold);
|
||||
|
||||
WorldPacket data(SMSG_LOOT_MONEY_NOTIFY, 4 + 1);
|
||||
data << uint32(loot->gold);
|
||||
data << uint8(1); // "You loot..."
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
loot->gold = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleLootOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_LOOT");
|
||||
|
||||
uint64 guid;
|
||||
recv_data >> guid;
|
||||
|
||||
// Check possible cheat
|
||||
if (!_player->isAlive())
|
||||
return;
|
||||
|
||||
GetPlayer()->SendLoot(guid, LOOT_CORPSE);
|
||||
|
||||
// interrupt cast
|
||||
if (GetPlayer()->IsNonMeleeSpellCasted(false))
|
||||
GetPlayer()->InterruptNonMeleeSpells(false);
|
||||
}
|
||||
|
||||
void WorldSession::HandleLootReleaseOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_LOOT_RELEASE");
|
||||
|
||||
// cheaters can modify lguid to prevent correct apply loot release code and re-loot
|
||||
// use internal stored guid
|
||||
recv_data.read_skip<uint64>(); // guid;
|
||||
|
||||
if (uint64 lguid = GetPlayer()->GetLootGUID())
|
||||
DoLootRelease(lguid);
|
||||
}
|
||||
|
||||
void WorldSession::DoLootRelease(uint64 lguid)
|
||||
{
|
||||
Player *player = GetPlayer();
|
||||
Loot *loot;
|
||||
|
||||
player->SetLootGUID(0);
|
||||
player->SendLootRelease(lguid);
|
||||
|
||||
player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_LOOTING);
|
||||
|
||||
if (!player->IsInWorld())
|
||||
return;
|
||||
|
||||
if (IS_GAMEOBJECT_GUID(lguid))
|
||||
{
|
||||
GameObject* go = GetPlayer()->GetMap()->GetGameObject(lguid);
|
||||
|
||||
// not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO
|
||||
if (!go || ((go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player, INTERACTION_DISTANCE)))
|
||||
return;
|
||||
|
||||
loot = &go->loot;
|
||||
|
||||
if (go->GetGoType() == GAMEOBJECT_TYPE_DOOR)
|
||||
{
|
||||
// locked doors are opened with spelleffect openlock, prevent remove its as looted
|
||||
go->UseDoorOrButton();
|
||||
}
|
||||
else if (loot->isLooted() || go->GetGoType() == GAMEOBJECT_TYPE_FISHINGNODE)
|
||||
{
|
||||
// GO is mineral vein? so it is not removed after its looted
|
||||
if (go->GetGoType() == GAMEOBJECT_TYPE_CHEST)
|
||||
{
|
||||
uint32 go_min = go->GetGOInfo()->chest.minSuccessOpens;
|
||||
uint32 go_max = go->GetGOInfo()->chest.maxSuccessOpens;
|
||||
|
||||
// only vein pass this check
|
||||
if (go_min != 0 && go_max > go_min)
|
||||
{
|
||||
float amount_rate = sWorld->getRate(RATE_MINING_AMOUNT);
|
||||
float min_amount = go_min*amount_rate;
|
||||
float max_amount = go_max*amount_rate;
|
||||
|
||||
go->AddUse();
|
||||
float uses = float(go->GetUseCount());
|
||||
|
||||
if (uses < max_amount)
|
||||
{
|
||||
if (uses >= min_amount)
|
||||
{
|
||||
float chance_rate = sWorld->getRate(RATE_MINING_NEXT);
|
||||
|
||||
int32 ReqValue = 175;
|
||||
LockEntry const* lockInfo = sLockStore.LookupEntry(go->GetGOInfo()->chest.lockId);
|
||||
if (lockInfo)
|
||||
ReqValue = lockInfo->Skill[0];
|
||||
float skill = float(player->GetSkillValue(SKILL_MINING))/(ReqValue+25);
|
||||
double chance = pow(0.8*chance_rate, 4*(1/double(max_amount))*double(uses));
|
||||
if (roll_chance_f((float)(100*chance+skill)))
|
||||
{
|
||||
go->SetLootState(GO_READY);
|
||||
}
|
||||
else // not have more uses
|
||||
go->SetLootState(GO_JUST_DEACTIVATED);
|
||||
}
|
||||
else // 100% chance until min uses
|
||||
go->SetLootState(GO_READY);
|
||||
}
|
||||
else // max uses already
|
||||
go->SetLootState(GO_JUST_DEACTIVATED);
|
||||
}
|
||||
else // not vein
|
||||
go->SetLootState(GO_JUST_DEACTIVATED);
|
||||
}
|
||||
else if (go->GetGoType() == GAMEOBJECT_TYPE_FISHINGHOLE)
|
||||
{ // The fishing hole used once more
|
||||
go->AddUse(); // if the max usage is reached, will be despawned in next tick
|
||||
if (go->GetUseCount() >= urand(go->GetGOInfo()->fishinghole.minSuccessOpens, go->GetGOInfo()->fishinghole.maxSuccessOpens))
|
||||
{
|
||||
go->SetLootState(GO_JUST_DEACTIVATED);
|
||||
}
|
||||
else
|
||||
go->SetLootState(GO_READY);
|
||||
}
|
||||
else // not chest (or vein/herb/etc)
|
||||
go->SetLootState(GO_JUST_DEACTIVATED);
|
||||
|
||||
loot->clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
// not fully looted object
|
||||
go->SetLootState(GO_ACTIVATED, player);
|
||||
|
||||
// if the round robin player release, reset it.
|
||||
if (player->GetGUID() == loot->roundRobinPlayer)
|
||||
{
|
||||
if (Group* group = player->GetGroup())
|
||||
{
|
||||
if (group->GetLootMethod() != MASTER_LOOT)
|
||||
{
|
||||
loot->roundRobinPlayer = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
loot->roundRobinPlayer = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (IS_CORPSE_GUID(lguid)) // ONLY remove insignia at BG
|
||||
{
|
||||
Corpse* corpse = ObjectAccessor::GetCorpse(*player, lguid);
|
||||
if (!corpse || !corpse->IsWithinDistInMap(_player, INTERACTION_DISTANCE))
|
||||
return;
|
||||
|
||||
loot = &corpse->loot;
|
||||
|
||||
if (loot->isLooted())
|
||||
{
|
||||
loot->clear();
|
||||
corpse->RemoveFlag(CORPSE_FIELD_DYNAMIC_FLAGS, CORPSE_DYNFLAG_LOOTABLE);
|
||||
}
|
||||
}
|
||||
else if (IS_ITEM_GUID(lguid))
|
||||
{
|
||||
Item* pItem = player->GetItemByGuid(lguid);
|
||||
if (!pItem)
|
||||
return;
|
||||
|
||||
ItemTemplate const* proto = pItem->GetTemplate();
|
||||
|
||||
// destroy only 5 items from stack in case prospecting and milling
|
||||
if (proto->Flags & (ITEM_PROTO_FLAG_PROSPECTABLE | ITEM_PROTO_FLAG_MILLABLE))
|
||||
{
|
||||
pItem->m_lootGenerated = false;
|
||||
pItem->loot.clear();
|
||||
|
||||
uint32 count = pItem->GetCount();
|
||||
|
||||
// >=5 checked in spell code, but will work for cheating cases also with removing from another stacks.
|
||||
if (count > 5)
|
||||
count = 5;
|
||||
|
||||
player->DestroyItemCount(pItem, count, true);
|
||||
}
|
||||
else
|
||||
// FIXME: item must not be deleted in case not fully looted state. But this pre-request implement loot saving in DB at item save. Or cheating possible.
|
||||
player->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true);
|
||||
return; // item can be looted only single player
|
||||
}
|
||||
else
|
||||
{
|
||||
Creature* creature = GetPlayer()->GetMap()->GetCreature(lguid);
|
||||
|
||||
bool ok_loot = creature && creature->isAlive() == (player->getClass() == CLASS_ROGUE && creature->lootForPickPocketed);
|
||||
if (!ok_loot || !creature->IsWithinDistInMap(_player, INTERACTION_DISTANCE))
|
||||
return;
|
||||
|
||||
loot = &creature->loot;
|
||||
if (loot->isLooted())
|
||||
{
|
||||
// skip pickpocketing loot for speed, skinning timer reduction is no-op in fact
|
||||
if (!creature->isAlive())
|
||||
creature->AllLootRemovedFromCorpse();
|
||||
|
||||
creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
|
||||
loot->clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
// if the round robin player release, reset it.
|
||||
if (player->GetGUID() == loot->roundRobinPlayer)
|
||||
{
|
||||
if (Group* group = player->GetGroup())
|
||||
{
|
||||
if (group->GetLootMethod() != MASTER_LOOT)
|
||||
{
|
||||
loot->roundRobinPlayer = 0;
|
||||
group->SendLooter(creature, NULL);
|
||||
|
||||
// force update of dynamic flags, otherwise other group's players still not able to loot.
|
||||
creature->ForceValuesUpdateAtIndex(UNIT_DYNAMIC_FLAGS);
|
||||
}
|
||||
}
|
||||
else
|
||||
loot->roundRobinPlayer = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Player is not looking at loot list, he doesn't need to see updates on the loot list
|
||||
loot->RemoveLooter(player->GetGUID());
|
||||
}
|
||||
|
||||
void WorldSession::HandleLootMasterGiveOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
uint8 slotid;
|
||||
uint64 lootguid, target_playerguid;
|
||||
|
||||
recv_data >> lootguid >> slotid >> target_playerguid;
|
||||
|
||||
if (!_player->GetGroup() || _player->GetGroup()->GetLooterGuid() != _player->GetGUID())
|
||||
{
|
||||
_player->SendLootRelease(GetPlayer()->GetLootGUID());
|
||||
return;
|
||||
}
|
||||
|
||||
Player* target = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(target_playerguid, 0, HIGHGUID_PLAYER));
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WorldSession::HandleLootMasterGiveOpcode (CMSG_LOOT_MASTER_GIVE, 0x02A3) Target = [%s].", target->GetName());
|
||||
|
||||
if (_player->GetLootGUID() != lootguid)
|
||||
return;
|
||||
|
||||
Loot* pLoot = NULL;
|
||||
|
||||
if (IS_CRE_OR_VEH_GUID(GetPlayer()->GetLootGUID()))
|
||||
{
|
||||
Creature* creature = GetPlayer()->GetMap()->GetCreature(lootguid);
|
||||
if (!creature)
|
||||
return;
|
||||
|
||||
pLoot = &creature->loot;
|
||||
}
|
||||
else if (IS_GAMEOBJECT_GUID(GetPlayer()->GetLootGUID()))
|
||||
{
|
||||
GameObject* pGO = GetPlayer()->GetMap()->GetGameObject(lootguid);
|
||||
if (!pGO)
|
||||
return;
|
||||
|
||||
pLoot = &pGO->loot;
|
||||
}
|
||||
|
||||
if (!pLoot)
|
||||
return;
|
||||
|
||||
if (slotid > pLoot->items.size())
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_LOOT, "MasterLootItem: Player %s might be using a hack! (slot %d, size %lu)", GetPlayer()->GetName(), slotid, (unsigned long)pLoot->items.size());
|
||||
return;
|
||||
}
|
||||
|
||||
LootItem& item = pLoot->items[slotid];
|
||||
|
||||
ItemPosCountVec dest;
|
||||
InventoryResult msg = target->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, item.itemid, item.count);
|
||||
if (msg != EQUIP_ERR_OK)
|
||||
{
|
||||
target->SendEquipError(msg, NULL, NULL, item.itemid);
|
||||
// send duplicate of error massage to master looter
|
||||
_player->SendEquipError(msg, NULL, NULL, item.itemid);
|
||||
return;
|
||||
}
|
||||
|
||||
// list of players allowed to receive this item in trade
|
||||
AllowedLooterSet looters = item.GetAllowedLooters();
|
||||
|
||||
// not move item from loot to target inventory
|
||||
Item* newitem = target->StoreNewItem(dest, item.itemid, true, item.randomPropertyId, looters);
|
||||
target->SendNewItem(newitem, uint32(item.count), false, false, true);
|
||||
target->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM, item.itemid, item.count);
|
||||
target->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE, pLoot->loot_type, item.count);
|
||||
target->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM, item.itemid, item.count);
|
||||
|
||||
// mark as looted
|
||||
item.count=0;
|
||||
item.is_looted=true;
|
||||
|
||||
pLoot->NotifyItemRemoved(slotid);
|
||||
--pLoot->unlootedCount;
|
||||
}
|
||||
|
||||
Executable
+773
@@ -0,0 +1,773 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
*
|
||||
* 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 "DatabaseEnv.h"
|
||||
#include "Mail.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Log.h"
|
||||
#include "World.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Player.h"
|
||||
#include "Language.h"
|
||||
#include "DBCStores.h"
|
||||
#include "Item.h"
|
||||
#include "AccountMgr.h"
|
||||
|
||||
void WorldSession::HandleSendMail(WorldPacket & recv_data)
|
||||
{
|
||||
uint64 mailbox, unk3;
|
||||
std::string receiver, subject, body;
|
||||
uint32 unk1, unk2, money, COD;
|
||||
uint8 unk4;
|
||||
recv_data >> mailbox;
|
||||
recv_data >> receiver;
|
||||
|
||||
recv_data >> subject;
|
||||
|
||||
recv_data >> body;
|
||||
|
||||
recv_data >> unk1; // stationery?
|
||||
recv_data >> unk2; // 0x00000000
|
||||
|
||||
uint8 items_count;
|
||||
recv_data >> items_count; // attached items count
|
||||
|
||||
if (items_count > MAX_MAIL_ITEMS) // client limit
|
||||
{
|
||||
GetPlayer()->SendMailResult(0, MAIL_SEND, MAIL_ERR_TOO_MANY_ATTACHMENTS);
|
||||
recv_data.rfinish(); // set to end to avoid warnings spam
|
||||
return;
|
||||
}
|
||||
|
||||
uint64 itemGUIDs[MAX_MAIL_ITEMS];
|
||||
|
||||
for (uint8 i = 0; i < items_count; ++i)
|
||||
{
|
||||
recv_data.read_skip<uint8>(); // item slot in mail, not used
|
||||
recv_data >> itemGUIDs[i];
|
||||
}
|
||||
|
||||
recv_data >> money >> COD; // money and cod
|
||||
recv_data >> unk3; // const 0
|
||||
recv_data >> unk4; // const 0
|
||||
|
||||
// packet read complete, now do check
|
||||
|
||||
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
|
||||
return;
|
||||
|
||||
if (receiver.empty())
|
||||
return;
|
||||
|
||||
Player* player = _player;
|
||||
|
||||
if (player->getLevel() < sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ))
|
||||
{
|
||||
SendNotification(GetTrinityString(LANG_MAIL_SENDER_REQ), sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ));
|
||||
return;
|
||||
}
|
||||
|
||||
uint64 rc = 0;
|
||||
if (normalizePlayerName(receiver))
|
||||
rc = sObjectMgr->GetPlayerGUIDByName(receiver);
|
||||
|
||||
if (!rc)
|
||||
{
|
||||
sLog->outDetail("Player %u is sending mail to %s (GUID: not existed!) with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u",
|
||||
player->GetGUIDLow(), receiver.c_str(), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2);
|
||||
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
sLog->outDetail("Player %u is sending mail to %s (GUID: %u) with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u", player->GetGUIDLow(), receiver.c_str(), GUID_LOPART(rc), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2);
|
||||
|
||||
if (player->GetGUID() == rc)
|
||||
{
|
||||
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANNOT_SEND_TO_SELF);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 cost = items_count ? 30 * items_count : 30; // price hardcoded in client
|
||||
|
||||
uint32 reqmoney = cost + money;
|
||||
|
||||
if (!player->HasEnoughMoney(reqmoney))
|
||||
{
|
||||
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_ENOUGH_MONEY);
|
||||
return;
|
||||
}
|
||||
|
||||
Player* receive = ObjectAccessor::FindPlayer(rc);
|
||||
|
||||
uint32 rc_team = 0;
|
||||
uint8 mails_count = 0; //do not allow to send to one player more than 100 mails
|
||||
uint8 receiveLevel = 0;
|
||||
|
||||
if (receive)
|
||||
{
|
||||
rc_team = receive->GetTeam();
|
||||
mails_count = receive->GetMailSize();
|
||||
receiveLevel = receive->getLevel();
|
||||
}
|
||||
else
|
||||
{
|
||||
rc_team = sObjectMgr->GetPlayerTeamByGUID(rc);
|
||||
if (QueryResult result = CharacterDatabase.PQuery("SELECT COUNT(*) FROM mail WHERE receiver = '%u'", GUID_LOPART(rc)))
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
mails_count = fields[0].GetUInt32();
|
||||
}
|
||||
if (QueryResult result = CharacterDatabase.PQuery("SELECT level FROM characters WHERE guid = '%u'", GUID_LOPART(rc)))
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
receiveLevel = fields[0].GetUInt8();
|
||||
}
|
||||
}
|
||||
//do not allow to have more than 100 mails in mailbox.. mails count is in opcode uint8!!! - so max can be 255..
|
||||
if (mails_count > 100)
|
||||
{
|
||||
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_CAP_REACHED);
|
||||
return;
|
||||
}
|
||||
// test the receiver's Faction... or all items are account bound
|
||||
bool accountBound = items_count ? true : false;
|
||||
for (uint8 i = 0; i < items_count; ++i)
|
||||
{
|
||||
Item* item = player->GetItemByGuid(itemGUIDs[i]);
|
||||
if (item)
|
||||
{
|
||||
ItemTemplate const* itemProto = item->GetTemplate();
|
||||
if (!itemProto || !(itemProto->Flags & ITEM_PROTO_FLAG_BIND_TO_ACCOUNT))
|
||||
{
|
||||
accountBound = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!accountBound && !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL) && player->GetTeam() != rc_team && AccountMgr::IsPlayerAccount(GetSecurity()))
|
||||
{
|
||||
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_YOUR_TEAM);
|
||||
return;
|
||||
}
|
||||
|
||||
if (receiveLevel < sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ))
|
||||
{
|
||||
SendNotification(GetTrinityString(LANG_MAIL_RECEIVER_REQ), sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ));
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 rc_account = receive
|
||||
? receive->GetSession()->GetAccountId()
|
||||
: sObjectMgr->GetPlayerAccountIdByGUID(rc);
|
||||
|
||||
Item* items[MAX_MAIL_ITEMS];
|
||||
|
||||
for (uint8 i = 0; i < items_count; ++i)
|
||||
{
|
||||
if (!itemGUIDs[i])
|
||||
{
|
||||
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID);
|
||||
return;
|
||||
}
|
||||
|
||||
Item* item = player->GetItemByGuid(itemGUIDs[i]);
|
||||
|
||||
// prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail)
|
||||
if (!item)
|
||||
{
|
||||
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!item->CanBeTraded(true))
|
||||
{
|
||||
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM);
|
||||
return;
|
||||
}
|
||||
|
||||
if (item->IsBoundAccountWide() && item->IsSoulBound() && player->GetSession()->GetAccountId() != rc_account)
|
||||
{
|
||||
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_ARTEFACTS_ONLY_FOR_OWN_CHARACTERS);
|
||||
return;
|
||||
}
|
||||
|
||||
if (item->GetTemplate()->Flags & ITEM_PROTO_FLAG_CONJURED || item->GetUInt32Value(ITEM_FIELD_DURATION))
|
||||
{
|
||||
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM);
|
||||
return;
|
||||
}
|
||||
|
||||
if (COD && item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED))
|
||||
{
|
||||
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD);
|
||||
return;
|
||||
}
|
||||
|
||||
if (item->IsNotEmptyBag())
|
||||
{
|
||||
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_CAN_ONLY_DO_WITH_EMPTY_BAGS);
|
||||
return;
|
||||
}
|
||||
|
||||
items[i] = item;
|
||||
}
|
||||
|
||||
player->SendMailResult(0, MAIL_SEND, MAIL_OK);
|
||||
|
||||
player->ModifyMoney(-int32(reqmoney));
|
||||
player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL, cost);
|
||||
|
||||
bool needItemDelay = false;
|
||||
|
||||
MailDraft draft(subject, body);
|
||||
|
||||
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
|
||||
if (items_count > 0 || money > 0)
|
||||
{
|
||||
if (items_count > 0)
|
||||
{
|
||||
for (uint8 i = 0; i < items_count; ++i)
|
||||
{
|
||||
Item* item = items[i];
|
||||
if (!AccountMgr::IsPlayerAccount(GetSecurity()) && sWorld->getBoolConfig(CONFIG_GM_LOG_TRADE))
|
||||
{
|
||||
sLog->outCommand(GetAccountId(), "GM %s (Account: %u) mail item: %s (Entry: %u Count: %u) to player: %s (Account: %u)",
|
||||
GetPlayerName(), GetAccountId(), item->GetTemplate()->Name1.c_str(), item->GetEntry(), item->GetCount(), receiver.c_str(), rc_account);
|
||||
}
|
||||
|
||||
item->SetNotRefundable(GetPlayer()); // makes the item no longer refundable
|
||||
player->MoveItemFromInventory(items[i]->GetBagSlot(), item->GetSlot(), true);
|
||||
|
||||
item->DeleteFromInventoryDB(trans); // deletes item from character's inventory
|
||||
item->SetOwnerGUID(rc);
|
||||
item->SaveToDB(trans); // recursive and not have transaction guard into self, item not in inventory and can be save standalone
|
||||
|
||||
draft.AddItem(item);
|
||||
}
|
||||
|
||||
// if item send to character at another account, then apply item delivery delay
|
||||
needItemDelay = player->GetSession()->GetAccountId() != rc_account;
|
||||
}
|
||||
|
||||
if (money > 0 && !AccountMgr::IsPlayerAccount(GetSecurity()) && sWorld->getBoolConfig(CONFIG_GM_LOG_TRADE))
|
||||
{
|
||||
sLog->outCommand(GetAccountId(), "GM %s (Account: %u) mail money: %u to player: %s (Account: %u)",
|
||||
GetPlayerName(), GetAccountId(), money, receiver.c_str(), rc_account);
|
||||
}
|
||||
}
|
||||
|
||||
// If theres is an item, there is a one hour delivery delay if sent to another account's character.
|
||||
uint32 deliver_delay = needItemDelay ? sWorld->getIntConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0;
|
||||
|
||||
// will delete item or place to receiver mail list
|
||||
draft
|
||||
.AddMoney(money)
|
||||
.AddCOD(COD)
|
||||
.SendMailTo(trans, MailReceiver(receive, GUID_LOPART(rc)), MailSender(player), body.empty() ? MAIL_CHECK_MASK_COPIED : MAIL_CHECK_MASK_HAS_BODY, deliver_delay);
|
||||
|
||||
player->SaveInventoryAndGoldToDB(trans);
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
}
|
||||
|
||||
//called when mail is read
|
||||
void WorldSession::HandleMailMarkAsRead(WorldPacket & recv_data)
|
||||
{
|
||||
uint64 mailbox;
|
||||
uint32 mailId;
|
||||
recv_data >> mailbox;
|
||||
recv_data >> mailId;
|
||||
|
||||
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
|
||||
return;
|
||||
|
||||
Player* player = _player;
|
||||
Mail* m = player->GetMail(mailId);
|
||||
if (m)
|
||||
{
|
||||
if (player->unReadMails)
|
||||
--player->unReadMails;
|
||||
m->checked = m->checked | MAIL_CHECK_MASK_READ;
|
||||
player->m_mailsUpdated = true;
|
||||
m->state = MAIL_STATE_CHANGED;
|
||||
}
|
||||
}
|
||||
|
||||
//called when client deletes mail
|
||||
void WorldSession::HandleMailDelete(WorldPacket & recv_data)
|
||||
{
|
||||
uint64 mailbox;
|
||||
uint32 mailId;
|
||||
recv_data >> mailbox;
|
||||
recv_data >> mailId;
|
||||
recv_data.read_skip<uint32>(); // mailTemplateId
|
||||
|
||||
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
|
||||
return;
|
||||
|
||||
Mail* m = _player->GetMail(mailId);
|
||||
Player* player = _player;
|
||||
player->m_mailsUpdated = true;
|
||||
if (m)
|
||||
{
|
||||
// delete shouldn't show up for COD mails
|
||||
if (m->COD)
|
||||
{
|
||||
player->SendMailResult(mailId, MAIL_DELETED, MAIL_ERR_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
m->state = MAIL_STATE_DELETED;
|
||||
}
|
||||
player->SendMailResult(mailId, MAIL_DELETED, MAIL_OK);
|
||||
}
|
||||
|
||||
void WorldSession::HandleMailReturnToSender(WorldPacket & recv_data)
|
||||
{
|
||||
uint64 mailbox;
|
||||
uint32 mailId;
|
||||
recv_data >> mailbox;
|
||||
recv_data >> mailId;
|
||||
recv_data.read_skip<uint64>(); // original sender GUID for return to, not used
|
||||
|
||||
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
|
||||
return;
|
||||
|
||||
Player* player = _player;
|
||||
Mail* m = player->GetMail(mailId);
|
||||
if (!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL))
|
||||
{
|
||||
player->SendMailResult(mailId, MAIL_RETURNED_TO_SENDER, MAIL_ERR_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
//we can return mail now
|
||||
//so firstly delete the old one
|
||||
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
trans->PAppend("DELETE FROM mail WHERE id = '%u'", mailId); // needed?
|
||||
trans->PAppend("DELETE FROM mail_items WHERE mail_id = '%u'", mailId);
|
||||
player->RemoveMail(mailId);
|
||||
|
||||
// only return mail if the player exists (and delete if not existing)
|
||||
if (m->messageType == MAIL_NORMAL && m->sender)
|
||||
{
|
||||
MailDraft draft(m->subject, m->body);
|
||||
if (m->mailTemplateId)
|
||||
draft = MailDraft(m->mailTemplateId, false); // items already included
|
||||
|
||||
if (m->HasItems())
|
||||
{
|
||||
for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
|
||||
{
|
||||
Item* item = player->GetMItem(itr2->item_guid);
|
||||
if (item)
|
||||
draft.AddItem(item);
|
||||
else
|
||||
{
|
||||
//WTF?
|
||||
}
|
||||
|
||||
player->RemoveMItem(itr2->item_guid);
|
||||
}
|
||||
}
|
||||
draft.AddMoney(m->money).SendReturnToSender(GetAccountId(), m->receiver, m->sender, trans);
|
||||
}
|
||||
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
|
||||
delete m; //we can deallocate old mail
|
||||
player->SendMailResult(mailId, MAIL_RETURNED_TO_SENDER, MAIL_OK);
|
||||
}
|
||||
|
||||
//called when player takes item attached in mail
|
||||
void WorldSession::HandleMailTakeItem(WorldPacket & recv_data)
|
||||
{
|
||||
uint64 mailbox;
|
||||
uint32 mailId;
|
||||
uint32 itemId;
|
||||
recv_data >> mailbox;
|
||||
recv_data >> mailId;
|
||||
recv_data >> itemId; // item guid low
|
||||
|
||||
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
|
||||
return;
|
||||
|
||||
Player* player = _player;
|
||||
|
||||
Mail* m = player->GetMail(mailId);
|
||||
if (!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL))
|
||||
{
|
||||
player->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// prevent cheating with skip client money check
|
||||
if (!player->HasEnoughMoney(m->COD))
|
||||
{
|
||||
player->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_NOT_ENOUGH_MONEY);
|
||||
return;
|
||||
}
|
||||
|
||||
Item* it = player->GetMItem(itemId);
|
||||
|
||||
ItemPosCountVec dest;
|
||||
uint8 msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, dest, it, false);
|
||||
if (msg == EQUIP_ERR_OK)
|
||||
{
|
||||
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
m->RemoveItem(itemId);
|
||||
m->removedItems.push_back(itemId);
|
||||
|
||||
if (m->COD > 0) //if there is COD, take COD money from player and send them to sender by mail
|
||||
{
|
||||
uint64 sender_guid = MAKE_NEW_GUID(m->sender, 0, HIGHGUID_PLAYER);
|
||||
Player* receive = ObjectAccessor::FindPlayer(sender_guid);
|
||||
|
||||
uint32 sender_accId = 0;
|
||||
|
||||
if (!AccountMgr::IsPlayerAccount(GetSecurity()) && sWorld->getBoolConfig(CONFIG_GM_LOG_TRADE))
|
||||
{
|
||||
std::string sender_name;
|
||||
if (receive)
|
||||
{
|
||||
sender_accId = receive->GetSession()->GetAccountId();
|
||||
sender_name = receive->GetName();
|
||||
}
|
||||
else
|
||||
{
|
||||
// can be calculated early
|
||||
sender_accId = sObjectMgr->GetPlayerAccountIdByGUID(sender_guid);
|
||||
|
||||
if (!sObjectMgr->GetPlayerNameByGUID(sender_guid, sender_name))
|
||||
sender_name = sObjectMgr->GetTrinityStringForDBCLocale(LANG_UNKNOWN);
|
||||
}
|
||||
sLog->outCommand(GetAccountId(), "GM %s (Account: %u) receive mail item: %s (Entry: %u Count: %u) and send COD money: %u to player: %s (Account: %u)",
|
||||
GetPlayerName(), GetAccountId(), it->GetTemplate()->Name1.c_str(), it->GetEntry(), it->GetCount(), m->COD, sender_name.c_str(), sender_accId);
|
||||
}
|
||||
else if (!receive)
|
||||
sender_accId = sObjectMgr->GetPlayerAccountIdByGUID(sender_guid);
|
||||
|
||||
// check player existence
|
||||
if (receive || sender_accId)
|
||||
{
|
||||
MailDraft(m->subject, "")
|
||||
.AddMoney(m->COD)
|
||||
.SendMailTo(trans, MailReceiver(receive, m->sender), MailSender(MAIL_NORMAL, m->receiver), MAIL_CHECK_MASK_COD_PAYMENT);
|
||||
}
|
||||
|
||||
player->ModifyMoney(-int32(m->COD));
|
||||
}
|
||||
m->COD = 0;
|
||||
m->state = MAIL_STATE_CHANGED;
|
||||
player->m_mailsUpdated = true;
|
||||
player->RemoveMItem(it->GetGUIDLow());
|
||||
|
||||
uint32 count = it->GetCount(); // save counts before store and possible merge with deleting
|
||||
player->MoveItemToInventory(dest, it, true);
|
||||
|
||||
player->SaveInventoryAndGoldToDB(trans);
|
||||
player->_SaveMail(trans);
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
|
||||
player->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_OK, 0, itemId, count);
|
||||
}
|
||||
else
|
||||
player->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_EQUIP_ERROR, msg);
|
||||
}
|
||||
|
||||
void WorldSession::HandleMailTakeMoney(WorldPacket & recv_data)
|
||||
{
|
||||
uint64 mailbox;
|
||||
uint32 mailId;
|
||||
recv_data >> mailbox;
|
||||
recv_data >> mailId;
|
||||
|
||||
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
|
||||
return;
|
||||
|
||||
Player* player = _player;
|
||||
|
||||
Mail* m = player->GetMail(mailId);
|
||||
if (!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL))
|
||||
{
|
||||
player->SendMailResult(mailId, MAIL_MONEY_TAKEN, MAIL_ERR_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
player->SendMailResult(mailId, MAIL_MONEY_TAKEN, MAIL_OK);
|
||||
|
||||
player->ModifyMoney(m->money);
|
||||
m->money = 0;
|
||||
m->state = MAIL_STATE_CHANGED;
|
||||
player->m_mailsUpdated = true;
|
||||
|
||||
// save money and mail to prevent cheating
|
||||
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
player->SaveGoldToDB(trans);
|
||||
player->_SaveMail(trans);
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
}
|
||||
|
||||
//called when player lists his received mails
|
||||
void WorldSession::HandleGetMailList(WorldPacket & recv_data)
|
||||
{
|
||||
uint64 mailbox;
|
||||
recv_data >> mailbox;
|
||||
|
||||
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
|
||||
return;
|
||||
|
||||
Player* player = _player;
|
||||
|
||||
//load players mails, and mailed items
|
||||
if (!player->m_mailsLoaded)
|
||||
player ->_LoadMail();
|
||||
|
||||
// client can't work with packets > max int16 value
|
||||
const uint32 maxPacketSize = 32767;
|
||||
|
||||
uint32 mailsCount = 0; // real send to client mails amount
|
||||
uint32 realCount = 0; // real mails amount
|
||||
|
||||
WorldPacket data(SMSG_MAIL_LIST_RESULT, (200)); // guess size
|
||||
data << uint32(0); // real mail's count
|
||||
data << uint8(0); // mail's count
|
||||
time_t cur_time = time(NULL);
|
||||
|
||||
for (PlayerMails::iterator itr = player->GetMailBegin(); itr != player->GetMailEnd(); ++itr)
|
||||
{
|
||||
// packet send mail count as uint8, prevent overflow
|
||||
if (mailsCount >= 254)
|
||||
{
|
||||
realCount += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip deleted or not delivered (deliver delay not expired) mails
|
||||
if ((*itr)->state == MAIL_STATE_DELETED || cur_time < (*itr)->deliver_time)
|
||||
continue;
|
||||
|
||||
uint8 item_count = (*itr)->items.size(); // max count is MAX_MAIL_ITEMS (12)
|
||||
|
||||
size_t next_mail_size = 2+4+1+((*itr)->messageType == MAIL_NORMAL ? 8 : 4)+4*8+((*itr)->subject.size()+1)+((*itr)->body.size()+1)+1+item_count*(1+4+4+7*3*4+4+4+4+4+4+4+1);
|
||||
|
||||
if (data.wpos()+next_mail_size > maxPacketSize)
|
||||
{
|
||||
realCount += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
data << uint16(next_mail_size); // Message size
|
||||
data << uint32((*itr)->messageID); // Message ID
|
||||
data << uint8((*itr)->messageType); // Message Type
|
||||
|
||||
switch ((*itr)->messageType)
|
||||
{
|
||||
case MAIL_NORMAL: // sender guid
|
||||
data << uint64(MAKE_NEW_GUID((*itr)->sender, 0, HIGHGUID_PLAYER));
|
||||
break;
|
||||
case MAIL_CREATURE:
|
||||
case MAIL_GAMEOBJECT:
|
||||
case MAIL_AUCTION:
|
||||
data << uint32((*itr)->sender); // creature/gameobject entry, auction id
|
||||
break;
|
||||
case MAIL_ITEM: // item entry (?) sender = "Unknown", NYI
|
||||
data << uint32(0); // item entry
|
||||
break;
|
||||
}
|
||||
|
||||
data << uint32((*itr)->COD); // COD
|
||||
data << uint32(0); // probably changed in 3.3.3
|
||||
data << uint32((*itr)->stationery); // stationery (Stationery.dbc)
|
||||
data << uint32((*itr)->money); // Gold
|
||||
data << uint32((*itr)->checked); // flags
|
||||
data << float(((*itr)->expire_time-time(NULL))/DAY); // Time
|
||||
data << uint32((*itr)->mailTemplateId); // mail template (MailTemplate.dbc)
|
||||
data << (*itr)->subject; // Subject string - once 00, when mail type = 3, max 256
|
||||
data << (*itr)->body; // message? max 8000
|
||||
data << uint8(item_count); // client limit is 0x10
|
||||
|
||||
for (uint8 i = 0; i < item_count; ++i)
|
||||
{
|
||||
Item* item = player->GetMItem((*itr)->items[i].item_guid);
|
||||
// item index (0-6?)
|
||||
data << uint8(i);
|
||||
// item guid low?
|
||||
data << uint32((item ? item->GetGUIDLow() : 0));
|
||||
// entry
|
||||
data << uint32((item ? item->GetEntry() : 0));
|
||||
for (uint8 j = 0; j < MAX_INSPECTED_ENCHANTMENT_SLOT; ++j)
|
||||
{
|
||||
data << uint32((item ? item->GetEnchantmentId((EnchantmentSlot)j) : 0));
|
||||
data << uint32((item ? item->GetEnchantmentDuration((EnchantmentSlot)j) : 0));
|
||||
data << uint32((item ? item->GetEnchantmentCharges((EnchantmentSlot)j) : 0));
|
||||
}
|
||||
// can be negative
|
||||
data << int32((item ? item->GetItemRandomPropertyId() : 0));
|
||||
// unk
|
||||
data << uint32((item ? item->GetItemSuffixFactor() : 0));
|
||||
// stack count
|
||||
data << uint32((item ? item->GetCount() : 0));
|
||||
// charges
|
||||
data << uint32((item ? item->GetSpellCharges() : 0));
|
||||
// durability
|
||||
data << uint32((item ? item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY) : 0));
|
||||
// durability
|
||||
data << uint32((item ? item->GetUInt32Value(ITEM_FIELD_DURABILITY) : 0));
|
||||
// unknown wotlk
|
||||
data << uint8(0);
|
||||
}
|
||||
|
||||
realCount += 1;
|
||||
mailsCount += 1;
|
||||
}
|
||||
|
||||
data.put<uint32>(0, realCount); // this will display warning about undelivered mail to player if realCount > mailsCount
|
||||
data.put<uint8>(4, mailsCount); // set real send mails to client
|
||||
SendPacket(&data);
|
||||
|
||||
// recalculate m_nextMailDelivereTime and unReadMails
|
||||
_player->UpdateNextMailTimeAndUnreads();
|
||||
}
|
||||
|
||||
//used when player copies mail body to his inventory
|
||||
void WorldSession::HandleMailCreateTextItem(WorldPacket & recv_data)
|
||||
{
|
||||
uint64 mailbox;
|
||||
uint32 mailId;
|
||||
|
||||
recv_data >> mailbox;
|
||||
recv_data >> mailId;
|
||||
|
||||
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
|
||||
return;
|
||||
|
||||
Player* player = _player;
|
||||
|
||||
Mail* m = player->GetMail(mailId);
|
||||
if (!m || (m->body.empty() && !m->mailTemplateId) || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL))
|
||||
{
|
||||
player->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_ERR_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
Item* bodyItem = new Item; // This is not bag and then can be used new Item.
|
||||
if (!bodyItem->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_ITEM), MAIL_BODY_ITEM_TEMPLATE, player))
|
||||
{
|
||||
delete bodyItem;
|
||||
return;
|
||||
}
|
||||
|
||||
// in mail template case we need create new item text
|
||||
if (m->mailTemplateId)
|
||||
{
|
||||
MailTemplateEntry const* mailTemplateEntry = sMailTemplateStore.LookupEntry(m->mailTemplateId);
|
||||
if (!mailTemplateEntry)
|
||||
{
|
||||
player->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_ERR_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
bodyItem->SetText(mailTemplateEntry->content[GetSessionDbcLocale()]);
|
||||
}
|
||||
else
|
||||
bodyItem->SetText(m->body);
|
||||
|
||||
bodyItem->SetUInt32Value(ITEM_FIELD_CREATOR, m->sender);
|
||||
bodyItem->SetFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_MAIL_TEXT_MASK);
|
||||
|
||||
sLog->outDetail("HandleMailCreateTextItem mailid=%u", mailId);
|
||||
|
||||
ItemPosCountVec dest;
|
||||
uint8 msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, dest, bodyItem, false);
|
||||
if (msg == EQUIP_ERR_OK)
|
||||
{
|
||||
m->checked = m->checked | MAIL_CHECK_MASK_COPIED;
|
||||
m->state = MAIL_STATE_CHANGED;
|
||||
player->m_mailsUpdated = true;
|
||||
|
||||
player->StoreItem(dest, bodyItem, true);
|
||||
player->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_OK);
|
||||
}
|
||||
else
|
||||
{
|
||||
player->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_ERR_EQUIP_ERROR, msg);
|
||||
delete bodyItem;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO Fix me! ... this void has probably bad condition, but good data are sent
|
||||
void WorldSession::HandleQueryNextMailTime(WorldPacket & /*recv_data*/)
|
||||
{
|
||||
WorldPacket data(MSG_QUERY_NEXT_MAIL_TIME, 8);
|
||||
|
||||
if (!_player->m_mailsLoaded)
|
||||
_player->_LoadMail();
|
||||
|
||||
if (_player->unReadMails > 0)
|
||||
{
|
||||
data << uint32(0); // float
|
||||
data << uint32(0); // count
|
||||
|
||||
uint32 count = 0;
|
||||
time_t now = time(NULL);
|
||||
for (PlayerMails::iterator itr = _player->GetMailBegin(); itr != _player->GetMailEnd(); ++itr)
|
||||
{
|
||||
Mail* m = (*itr);
|
||||
// must be not checked yet
|
||||
if (m->checked & MAIL_CHECK_MASK_READ)
|
||||
continue;
|
||||
|
||||
// and already delivered
|
||||
if (now < m->deliver_time)
|
||||
continue;
|
||||
|
||||
if (m->messageType)
|
||||
data << uint64(m->sender); // player guid
|
||||
else
|
||||
data << uint32(m->sender); // creature entry
|
||||
|
||||
switch (m->messageType)
|
||||
{
|
||||
case MAIL_AUCTION:
|
||||
data << uint32(2);
|
||||
data << uint32(2);
|
||||
data << uint32(m->stationery);
|
||||
break;
|
||||
default:
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint32(m->stationery);
|
||||
break;
|
||||
}
|
||||
data << uint32(0xC6000000); // float unk, time or something
|
||||
|
||||
++count;
|
||||
if (count == 2) // do not display more than 2 mails
|
||||
break;
|
||||
}
|
||||
data.put<uint32>(4, count);
|
||||
}
|
||||
else
|
||||
{
|
||||
data << uint32(0xC7A8C000);
|
||||
data << uint32(0x00000000);
|
||||
}
|
||||
SendPacket(&data);
|
||||
}
|
||||
Executable
+1729
File diff suppressed because it is too large
Load Diff
Executable
+573
@@ -0,0 +1,573 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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 "Common.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Log.h"
|
||||
#include "Corpse.h"
|
||||
#include "Player.h"
|
||||
#include "SpellAuras.h"
|
||||
#include "MapManager.h"
|
||||
#include "Transport.h"
|
||||
#include "Battleground.h"
|
||||
#include "WaypointMovementGenerator.h"
|
||||
#include "InstanceSaveMgr.h"
|
||||
#include "ObjectMgr.h"
|
||||
|
||||
void WorldSession::HandleMoveWorldportAckOpcode(WorldPacket & /*recv_data*/)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: got MSG_MOVE_WORLDPORT_ACK.");
|
||||
HandleMoveWorldportAckOpcode();
|
||||
}
|
||||
|
||||
void WorldSession::HandleMoveWorldportAckOpcode()
|
||||
{
|
||||
// ignore unexpected far teleports
|
||||
if (!GetPlayer()->IsBeingTeleportedFar())
|
||||
return;
|
||||
|
||||
GetPlayer()->SetSemaphoreTeleportFar(false);
|
||||
|
||||
// get the teleport destination
|
||||
WorldLocation &loc = GetPlayer()->GetTeleportDest();
|
||||
|
||||
// possible errors in the coordinate validity check
|
||||
if (!MapManager::IsValidMapCoord(loc))
|
||||
{
|
||||
LogoutPlayer(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// get the destination map entry, not the current one, this will fix homebind and reset greeting
|
||||
MapEntry const* mEntry = sMapStore.LookupEntry(loc.GetMapId());
|
||||
InstanceTemplate const* mInstance = sObjectMgr->GetInstanceTemplate(loc.GetMapId());
|
||||
|
||||
// reset instance validity, except if going to an instance inside an instance
|
||||
if (GetPlayer()->m_InstanceValid == false && !mInstance)
|
||||
GetPlayer()->m_InstanceValid = true;
|
||||
|
||||
Map* oldMap = GetPlayer()->GetMap();
|
||||
ASSERT(oldMap);
|
||||
if (GetPlayer()->IsInWorld())
|
||||
{
|
||||
sLog->outCrash("Player (Name %s) is still in world when teleported from map %u to new map %u", GetPlayer()->GetName(), oldMap->GetId(), loc.GetMapId());
|
||||
oldMap->RemovePlayerFromMap(GetPlayer(), false);
|
||||
}
|
||||
|
||||
// relocate the player to the teleport destination
|
||||
Map* newMap = sMapMgr->CreateMap(loc.GetMapId(), GetPlayer());
|
||||
// the CanEnter checks are done in TeleporTo but conditions may change
|
||||
// while the player is in transit, for example the map may get full
|
||||
if (!newMap || !newMap->CanEnter(GetPlayer()))
|
||||
{
|
||||
sLog->outError("Map %d could not be created for player %d, porting player to homebind", loc.GetMapId(), GetPlayer()->GetGUIDLow());
|
||||
GetPlayer()->TeleportTo(GetPlayer()->m_homebindMapId, GetPlayer()->m_homebindX, GetPlayer()->m_homebindY, GetPlayer()->m_homebindZ, GetPlayer()->GetOrientation());
|
||||
return;
|
||||
}
|
||||
else
|
||||
GetPlayer()->Relocate(&loc);
|
||||
|
||||
GetPlayer()->ResetMap();
|
||||
GetPlayer()->SetMap(newMap);
|
||||
|
||||
GetPlayer()->SendInitialPacketsBeforeAddToMap();
|
||||
if (!GetPlayer()->GetMap()->AddPlayerToMap(GetPlayer()))
|
||||
{
|
||||
sLog->outError("WORLD: failed to teleport player %s (%d) to map %d because of unknown reason!", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow(), loc.GetMapId());
|
||||
GetPlayer()->ResetMap();
|
||||
GetPlayer()->SetMap(oldMap);
|
||||
GetPlayer()->TeleportTo(GetPlayer()->m_homebindMapId, GetPlayer()->m_homebindX, GetPlayer()->m_homebindY, GetPlayer()->m_homebindZ, GetPlayer()->GetOrientation());
|
||||
return;
|
||||
}
|
||||
|
||||
// battleground state prepare (in case join to BG), at relogin/tele player not invited
|
||||
// only add to bg group and object, if the player was invited (else he entered through command)
|
||||
if (_player->InBattleground())
|
||||
{
|
||||
// cleanup setting if outdated
|
||||
if (!mEntry->IsBattlegroundOrArena())
|
||||
{
|
||||
// We're not in BG
|
||||
_player->SetBattlegroundId(0, BATTLEGROUND_TYPE_NONE);
|
||||
// reset destination bg team
|
||||
_player->SetBGTeam(0);
|
||||
}
|
||||
// join to bg case
|
||||
else if (Battleground* bg = _player->GetBattleground())
|
||||
{
|
||||
if (_player->IsInvitedForBattlegroundInstance(_player->GetBattlegroundId()))
|
||||
bg->AddPlayer(_player);
|
||||
}
|
||||
}
|
||||
|
||||
GetPlayer()->SendInitialPacketsAfterAddToMap();
|
||||
|
||||
// flight fast teleport case
|
||||
if (GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE)
|
||||
{
|
||||
if (!_player->InBattleground())
|
||||
{
|
||||
// short preparations to continue flight
|
||||
FlightPathMovementGenerator* flight = (FlightPathMovementGenerator*)(GetPlayer()->GetMotionMaster()->top());
|
||||
flight->Initialize(*GetPlayer());
|
||||
return;
|
||||
}
|
||||
|
||||
// battleground state prepare, stop flight
|
||||
GetPlayer()->GetMotionMaster()->MovementExpired();
|
||||
GetPlayer()->CleanupAfterTaxiFlight();
|
||||
}
|
||||
|
||||
// resurrect character at enter into instance where his corpse exist after add to map
|
||||
Corpse* corpse = GetPlayer()->GetCorpse();
|
||||
if (corpse && corpse->GetType() != CORPSE_BONES && corpse->GetMapId() == GetPlayer()->GetMapId())
|
||||
{
|
||||
if (mEntry->IsDungeon())
|
||||
{
|
||||
GetPlayer()->ResurrectPlayer(0.5f, false);
|
||||
GetPlayer()->SpawnCorpseBones();
|
||||
}
|
||||
}
|
||||
|
||||
bool allowMount = !mEntry->IsDungeon() || mEntry->IsBattlegroundOrArena();
|
||||
if (mInstance)
|
||||
{
|
||||
Difficulty diff = GetPlayer()->GetDifficulty(mEntry->IsRaid());
|
||||
if (MapDifficulty const* mapDiff = GetMapDifficultyData(mEntry->MapID, diff))
|
||||
{
|
||||
if (mapDiff->resetTime)
|
||||
{
|
||||
if (time_t timeReset = sInstanceSaveMgr->GetResetTimeFor(mEntry->MapID, diff))
|
||||
{
|
||||
uint32 timeleft = uint32(timeReset - time(NULL));
|
||||
GetPlayer()->SendInstanceResetWarning(mEntry->MapID, diff, timeleft);
|
||||
}
|
||||
}
|
||||
}
|
||||
allowMount = mInstance->AllowMount;
|
||||
}
|
||||
|
||||
// mount allow check
|
||||
if (!allowMount)
|
||||
_player->RemoveAurasByType(SPELL_AURA_MOUNTED);
|
||||
|
||||
// update zone immediately, otherwise leave channel will cause crash in mtmap
|
||||
uint32 newzone, newarea;
|
||||
GetPlayer()->GetZoneAndAreaId(newzone, newarea);
|
||||
GetPlayer()->UpdateZone(newzone, newarea);
|
||||
|
||||
// honorless target
|
||||
if (GetPlayer()->pvpInfo.inHostileArea)
|
||||
GetPlayer()->CastSpell(GetPlayer(), 2479, true);
|
||||
|
||||
// in friendly area
|
||||
else if (GetPlayer()->IsPvP() && !GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP))
|
||||
GetPlayer()->UpdatePvP(false, false);
|
||||
|
||||
// resummon pet
|
||||
GetPlayer()->ResummonPetTemporaryUnSummonedIfAny();
|
||||
|
||||
//lets process all delayed operations on successful teleport
|
||||
GetPlayer()->ProcessDelayedOperations();
|
||||
}
|
||||
|
||||
void WorldSession::HandleMoveTeleportAck(WorldPacket& recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "MSG_MOVE_TELEPORT_ACK");
|
||||
uint64 guid;
|
||||
|
||||
recv_data.readPackGUID(guid);
|
||||
|
||||
uint32 flags, time;
|
||||
recv_data >> flags >> time;
|
||||
sLog->outStaticDebug("Guid " UI64FMTD, guid);
|
||||
sLog->outStaticDebug("Flags %u, time %u", flags, time/IN_MILLISECONDS);
|
||||
|
||||
Unit* mover = _player->m_mover;
|
||||
Player* plMover = mover->GetTypeId() == TYPEID_PLAYER ? (Player*)mover : NULL;
|
||||
|
||||
if (!plMover || !plMover->IsBeingTeleportedNear())
|
||||
return;
|
||||
|
||||
if (guid != plMover->GetGUID())
|
||||
return;
|
||||
|
||||
plMover->SetSemaphoreTeleportNear(false);
|
||||
|
||||
uint32 old_zone = plMover->GetZoneId();
|
||||
|
||||
WorldLocation const& dest = plMover->GetTeleportDest();
|
||||
|
||||
plMover->UpdatePosition(dest, true);
|
||||
|
||||
uint32 newzone, newarea;
|
||||
plMover->GetZoneAndAreaId(newzone, newarea);
|
||||
plMover->UpdateZone(newzone, newarea);
|
||||
|
||||
// new zone
|
||||
if (old_zone != newzone)
|
||||
{
|
||||
// honorless target
|
||||
if (plMover->pvpInfo.inHostileArea)
|
||||
plMover->CastSpell(plMover, 2479, true);
|
||||
|
||||
// in friendly area
|
||||
else if (plMover->IsPvP() && !plMover->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP))
|
||||
plMover->UpdatePvP(false, false);
|
||||
}
|
||||
|
||||
// resummon pet
|
||||
GetPlayer()->ResummonPetTemporaryUnSummonedIfAny();
|
||||
|
||||
//lets process all delayed operations on successful teleport
|
||||
GetPlayer()->ProcessDelayedOperations();
|
||||
}
|
||||
|
||||
void WorldSession::HandleMovementOpcodes(WorldPacket & recv_data)
|
||||
{
|
||||
uint16 opcode = recv_data.GetOpcode();
|
||||
|
||||
Unit* mover = _player->m_mover;
|
||||
|
||||
ASSERT(mover != NULL); // there must always be a mover
|
||||
|
||||
Player* plMover = mover->GetTypeId() == TYPEID_PLAYER ? (Player*)mover : NULL;
|
||||
|
||||
// ignore, waiting processing in WorldSession::HandleMoveWorldportAckOpcode and WorldSession::HandleMoveTeleportAck
|
||||
if (plMover && plMover->IsBeingTeleported())
|
||||
{
|
||||
recv_data.rfinish(); // prevent warnings spam
|
||||
return;
|
||||
}
|
||||
|
||||
/* extract packet */
|
||||
uint64 guid;
|
||||
|
||||
recv_data.readPackGUID(guid);
|
||||
|
||||
MovementInfo movementInfo;
|
||||
movementInfo.guid = guid;
|
||||
ReadMovementInfo(recv_data, &movementInfo);
|
||||
|
||||
recv_data.rfinish(); // prevent warnings spam
|
||||
|
||||
// prevent tampered movement data
|
||||
if (guid != mover->GetGUID())
|
||||
return;
|
||||
|
||||
if (!movementInfo.pos.IsPositionValid())
|
||||
{
|
||||
recv_data.rfinish(); // prevent warnings spam
|
||||
return;
|
||||
}
|
||||
|
||||
/* handle special cases */
|
||||
if (movementInfo.flags & MOVEMENTFLAG_ONTRANSPORT)
|
||||
{
|
||||
// transports size limited
|
||||
// (also received at zeppelin leave by some reason with t_* as absolute in continent coordinates, can be safely skipped)
|
||||
if (movementInfo.t_pos.GetPositionX() > 50 || movementInfo.t_pos.GetPositionY() > 50 || movementInfo.t_pos.GetPositionZ() > 50)
|
||||
{
|
||||
recv_data.rfinish(); // prevent warnings spam
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Trinity::IsValidMapCoord(movementInfo.pos.GetPositionX() + movementInfo.t_pos.GetPositionX(), movementInfo.pos.GetPositionY() + movementInfo.t_pos.GetPositionY(),
|
||||
movementInfo.pos.GetPositionZ() + movementInfo.t_pos.GetPositionZ(), movementInfo.pos.GetOrientation() + movementInfo.t_pos.GetOrientation()))
|
||||
{
|
||||
recv_data.rfinish(); // prevent warnings spam
|
||||
return;
|
||||
}
|
||||
|
||||
// if we boarded a transport, add us to it
|
||||
if (plMover && !plMover->GetTransport())
|
||||
{
|
||||
// elevators also cause the client to send MOVEMENTFLAG_ONTRANSPORT - just dismount if the guid can be found in the transport list
|
||||
for (MapManager::TransportSet::const_iterator iter = sMapMgr->m_Transports.begin(); iter != sMapMgr->m_Transports.end(); ++iter)
|
||||
{
|
||||
if ((*iter)->GetGUID() == movementInfo.t_guid)
|
||||
{
|
||||
plMover->m_transport = (*iter);
|
||||
(*iter)->AddPassenger(plMover);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!mover->GetTransport() && !mover->GetVehicle())
|
||||
{
|
||||
GameObject* go = mover->GetMap()->GetGameObject(movementInfo.t_guid);
|
||||
if (!go || go->GetGoType() != GAMEOBJECT_TYPE_TRANSPORT)
|
||||
movementInfo.flags &= ~MOVEMENTFLAG_ONTRANSPORT;
|
||||
}
|
||||
}
|
||||
else if (plMover && plMover->GetTransport()) // if we were on a transport, leave
|
||||
{
|
||||
plMover->m_transport->RemovePassenger(plMover);
|
||||
plMover->m_transport = NULL;
|
||||
movementInfo.t_pos.Relocate(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
movementInfo.t_time = 0;
|
||||
movementInfo.t_seat = -1;
|
||||
}
|
||||
|
||||
// fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map).
|
||||
if (opcode == MSG_MOVE_FALL_LAND && plMover && !plMover->isInFlight())
|
||||
plMover->HandleFall(movementInfo);
|
||||
|
||||
if (plMover && ((movementInfo.flags & MOVEMENTFLAG_SWIMMING) != 0) != plMover->IsInWater())
|
||||
{
|
||||
// now client not include swimming flag in case jumping under water
|
||||
plMover->SetInWater(!plMover->IsInWater() || plMover->GetBaseMap()->IsUnderWater(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY(), movementInfo.pos.GetPositionZ()));
|
||||
}
|
||||
|
||||
/*----------------------*/
|
||||
|
||||
/* process position-change */
|
||||
WorldPacket data(opcode, recv_data.size());
|
||||
movementInfo.time = getMSTime();
|
||||
movementInfo.guid = mover->GetGUID();
|
||||
WriteMovementInfo(&data, &movementInfo);
|
||||
mover->SendMessageToSet(&data, _player);
|
||||
|
||||
mover->m_movementInfo = movementInfo;
|
||||
|
||||
// this is almost never true (not sure why it is sometimes, but it is), normally use mover->IsVehicle()
|
||||
if (mover->GetVehicle())
|
||||
{
|
||||
mover->SetOrientation(movementInfo.pos.GetOrientation());
|
||||
return;
|
||||
}
|
||||
|
||||
mover->UpdatePosition(movementInfo.pos);
|
||||
|
||||
if (plMover) // nothing is charmed, or player charmed
|
||||
{
|
||||
plMover->UpdateFallInformationIfNeed(movementInfo, opcode);
|
||||
|
||||
if (movementInfo.pos.GetPositionZ() < -500.0f)
|
||||
{
|
||||
if (!(plMover->InBattleground()
|
||||
&& plMover->GetBattleground()
|
||||
&& plMover->GetBattleground()->HandlePlayerUnderMap(_player)))
|
||||
{
|
||||
// NOTE: this is actually called many times while falling
|
||||
// even after the player has been teleported away
|
||||
// TODO: discard movement packets after the player is rooted
|
||||
if (plMover->isAlive())
|
||||
{
|
||||
plMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth());
|
||||
// player can be alive if GM/etc
|
||||
// change the death state to CORPSE to prevent the death timer from
|
||||
// starting in the next player update
|
||||
if (!plMover->isAlive())
|
||||
plMover->KillPlayer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recv_data)
|
||||
{
|
||||
uint32 opcode = recv_data.GetOpcode();
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd %s (%u, 0x%X) opcode", LookupOpcodeName(opcode), opcode, opcode);
|
||||
|
||||
/* extract packet */
|
||||
uint64 guid;
|
||||
uint32 unk1;
|
||||
float newspeed;
|
||||
|
||||
recv_data.readPackGUID(guid);
|
||||
|
||||
// now can skip not our packet
|
||||
if (_player->GetGUID() != guid)
|
||||
{
|
||||
recv_data.rfinish(); // prevent warnings spam
|
||||
return;
|
||||
}
|
||||
|
||||
// continue parse packet
|
||||
|
||||
recv_data >> unk1; // counter or moveEvent
|
||||
|
||||
MovementInfo movementInfo;
|
||||
movementInfo.guid = guid;
|
||||
ReadMovementInfo(recv_data, &movementInfo);
|
||||
|
||||
recv_data >> newspeed;
|
||||
/*----------------*/
|
||||
|
||||
// client ACK send one packet for mounted/run case and need skip all except last from its
|
||||
// in other cases anti-cheat check can be fail in false case
|
||||
UnitMoveType move_type;
|
||||
UnitMoveType force_move_type;
|
||||
|
||||
static char const* move_type_name[MAX_MOVE_TYPE] = { "Walk", "Run", "RunBack", "Swim", "SwimBack", "TurnRate", "Flight", "FlightBack", "PitchRate" };
|
||||
|
||||
switch (opcode)
|
||||
{
|
||||
case CMSG_FORCE_WALK_SPEED_CHANGE_ACK: move_type = MOVE_WALK; force_move_type = MOVE_WALK; break;
|
||||
case CMSG_FORCE_RUN_SPEED_CHANGE_ACK: move_type = MOVE_RUN; force_move_type = MOVE_RUN; break;
|
||||
case CMSG_FORCE_RUN_BACK_SPEED_CHANGE_ACK: move_type = MOVE_RUN_BACK; force_move_type = MOVE_RUN_BACK; break;
|
||||
case CMSG_FORCE_SWIM_SPEED_CHANGE_ACK: move_type = MOVE_SWIM; force_move_type = MOVE_SWIM; break;
|
||||
case CMSG_FORCE_SWIM_BACK_SPEED_CHANGE_ACK: move_type = MOVE_SWIM_BACK; force_move_type = MOVE_SWIM_BACK; break;
|
||||
case CMSG_FORCE_TURN_RATE_CHANGE_ACK: move_type = MOVE_TURN_RATE; force_move_type = MOVE_TURN_RATE; break;
|
||||
case CMSG_FORCE_FLIGHT_SPEED_CHANGE_ACK: move_type = MOVE_FLIGHT; force_move_type = MOVE_FLIGHT; break;
|
||||
case CMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE_ACK: move_type = MOVE_FLIGHT_BACK; force_move_type = MOVE_FLIGHT_BACK; break;
|
||||
case CMSG_FORCE_PITCH_RATE_CHANGE_ACK: move_type = MOVE_PITCH_RATE; force_move_type = MOVE_PITCH_RATE; break;
|
||||
default:
|
||||
sLog->outError("WorldSession::HandleForceSpeedChangeAck: Unknown move type opcode: %u", opcode);
|
||||
return;
|
||||
}
|
||||
|
||||
// skip all forced speed changes except last and unexpected
|
||||
// in run/mounted case used one ACK and it must be skipped.m_forced_speed_changes[MOVE_RUN} store both.
|
||||
if (_player->m_forced_speed_changes[force_move_type] > 0)
|
||||
{
|
||||
--_player->m_forced_speed_changes[force_move_type];
|
||||
if (_player->m_forced_speed_changes[force_move_type] > 0)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_player->GetTransport() && fabs(_player->GetSpeed(move_type) - newspeed) > 0.01f)
|
||||
{
|
||||
if (_player->GetSpeed(move_type) > newspeed) // must be greater - just correct
|
||||
{
|
||||
sLog->outError("%sSpeedChange player %s is NOT correct (must be %f instead %f), force set to correct value",
|
||||
move_type_name[move_type], _player->GetName(), _player->GetSpeed(move_type), newspeed);
|
||||
_player->SetSpeed(move_type, _player->GetSpeedRate(move_type), true);
|
||||
}
|
||||
else // must be lesser - cheating
|
||||
{
|
||||
sLog->outBasic("Player %s from account id %u kicked for incorrect speed (must be %f instead %f)",
|
||||
_player->GetName(), _player->GetSession()->GetAccountId(), _player->GetSpeed(move_type), newspeed);
|
||||
_player->GetSession()->KickPlayer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleSetActiveMoverOpcode(WorldPacket &recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_SET_ACTIVE_MOVER");
|
||||
|
||||
uint64 guid;
|
||||
recv_data >> guid;
|
||||
|
||||
if (GetPlayer()->IsInWorld())
|
||||
{
|
||||
if (_player->m_mover->GetGUID() != guid)
|
||||
sLog->outError("HandleSetActiveMoverOpcode: incorrect mover guid: mover is " UI64FMTD " (%s - Entry: %u) and should be " UI64FMTD, guid, GetLogNameForGuid(guid), GUID_ENPART(guid), _player->m_mover->GetGUID());
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleMoveNotActiveMover(WorldPacket &recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_MOVE_NOT_ACTIVE_MOVER");
|
||||
|
||||
uint64 old_mover_guid;
|
||||
recv_data.readPackGUID(old_mover_guid);
|
||||
|
||||
MovementInfo mi;
|
||||
mi.guid = old_mover_guid;
|
||||
ReadMovementInfo(recv_data, &mi);
|
||||
|
||||
_player->m_movementInfo = mi;
|
||||
}
|
||||
|
||||
void WorldSession::HandleMountSpecialAnimOpcode(WorldPacket& /*recv_data*/)
|
||||
{
|
||||
WorldPacket data(SMSG_MOUNTSPECIAL_ANIM, 8);
|
||||
data << uint64(GetPlayer()->GetGUID());
|
||||
|
||||
GetPlayer()->SendMessageToSet(&data, false);
|
||||
}
|
||||
|
||||
void WorldSession::HandleMoveKnockBackAck(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_MOVE_KNOCK_BACK_ACK");
|
||||
|
||||
uint64 guid;
|
||||
recv_data.readPackGUID(guid);
|
||||
|
||||
if (_player->m_mover->GetGUID() != guid)
|
||||
return;
|
||||
|
||||
recv_data.read_skip<uint32>(); // unk
|
||||
|
||||
MovementInfo movementInfo;
|
||||
ReadMovementInfo(recv_data, &movementInfo);
|
||||
_player->m_movementInfo = movementInfo;
|
||||
|
||||
WorldPacket data(MSG_MOVE_KNOCK_BACK, 66);
|
||||
data.appendPackGUID(guid);
|
||||
_player->BuildMovementPacket(&data);
|
||||
|
||||
// knockback specific info
|
||||
data << movementInfo.j_sinAngle;
|
||||
data << movementInfo.j_cosAngle;
|
||||
data << movementInfo.j_xyspeed;
|
||||
data << movementInfo.j_zspeed;
|
||||
|
||||
_player->SendMessageToSet(&data, false);
|
||||
}
|
||||
|
||||
void WorldSession::HandleMoveHoverAck(WorldPacket& recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_MOVE_HOVER_ACK");
|
||||
|
||||
uint64 guid; // guid - unused
|
||||
recv_data.readPackGUID(guid);
|
||||
|
||||
recv_data.read_skip<uint32>(); // unk
|
||||
|
||||
MovementInfo movementInfo;
|
||||
ReadMovementInfo(recv_data, &movementInfo);
|
||||
|
||||
recv_data.read_skip<uint32>(); // unk2
|
||||
}
|
||||
|
||||
void WorldSession::HandleMoveWaterWalkAck(WorldPacket& recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_MOVE_WATER_WALK_ACK");
|
||||
|
||||
uint64 guid; // guid - unused
|
||||
recv_data.readPackGUID(guid);
|
||||
|
||||
recv_data.read_skip<uint32>(); // unk
|
||||
|
||||
MovementInfo movementInfo;
|
||||
ReadMovementInfo(recv_data, &movementInfo);
|
||||
|
||||
recv_data.read_skip<uint32>(); // unk2
|
||||
}
|
||||
|
||||
void WorldSession::HandleSummonResponseOpcode(WorldPacket& recv_data)
|
||||
{
|
||||
if (!_player->isAlive() || _player->isInCombat())
|
||||
return;
|
||||
|
||||
uint64 summoner_guid;
|
||||
bool agree;
|
||||
recv_data >> summoner_guid;
|
||||
recv_data >> agree;
|
||||
|
||||
_player->SummonIfPossible(agree);
|
||||
}
|
||||
|
||||
Executable
+909
@@ -0,0 +1,909 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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 "Common.h"
|
||||
#include "Language.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Log.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "SpellMgr.h"
|
||||
#include "Player.h"
|
||||
#include "GossipDef.h"
|
||||
#include "UpdateMask.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "Creature.h"
|
||||
#include "Pet.h"
|
||||
#include "BattlegroundMgr.h"
|
||||
#include "Battleground.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "CreatureAI.h"
|
||||
#include "SpellInfo.h"
|
||||
|
||||
enum StableResultCode
|
||||
{
|
||||
STABLE_ERR_MONEY = 0x01, // "you don't have enough money"
|
||||
STABLE_ERR_STABLE = 0x06, // currently used in most fail cases
|
||||
STABLE_SUCCESS_STABLE = 0x08, // stable success
|
||||
STABLE_SUCCESS_UNSTABLE = 0x09, // unstable/swap success
|
||||
STABLE_SUCCESS_BUY_SLOT = 0x0A, // buy slot success
|
||||
STABLE_ERR_EXOTIC = 0x0C, // "you are unable to control exotic creatures"
|
||||
};
|
||||
|
||||
void WorldSession::HandleTabardVendorActivateOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
uint64 guid;
|
||||
recv_data >> guid;
|
||||
|
||||
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_TABARDDESIGNER);
|
||||
if (!unit)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleTabardVendorActivateOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(guid)));
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
SendTabardVendorActivate(guid);
|
||||
}
|
||||
|
||||
void WorldSession::SendTabardVendorActivate(uint64 guid)
|
||||
{
|
||||
WorldPacket data(MSG_TABARDVENDOR_ACTIVATE, 8);
|
||||
data << guid;
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleBankerActivateOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
uint64 guid;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_BANKER_ACTIVATE");
|
||||
|
||||
recv_data >> guid;
|
||||
|
||||
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_BANKER);
|
||||
if (!unit)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleBankerActivateOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(guid)));
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
SendShowBank(guid);
|
||||
}
|
||||
|
||||
void WorldSession::SendShowBank(uint64 guid)
|
||||
{
|
||||
WorldPacket data(SMSG_SHOW_BANK, 8);
|
||||
data << guid;
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleTrainerListOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
uint64 guid;
|
||||
|
||||
recv_data >> guid;
|
||||
SendTrainerList(guid);
|
||||
}
|
||||
|
||||
void WorldSession::SendTrainerList(uint64 guid)
|
||||
{
|
||||
std::string str = GetTrinityString(LANG_NPC_TAINER_HELLO);
|
||||
SendTrainerList(guid, str);
|
||||
}
|
||||
|
||||
void WorldSession::SendTrainerList(uint64 guid, const std::string& strTitle)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: SendTrainerList");
|
||||
|
||||
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_TRAINER);
|
||||
if (!unit)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: SendTrainerList - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(guid)));
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
// trainer list loaded at check;
|
||||
if (!unit->isCanTrainingOf(_player, true))
|
||||
return;
|
||||
|
||||
CreatureTemplate const* ci = unit->GetCreatureInfo();
|
||||
|
||||
if (!ci)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: SendTrainerList - (GUID: %u) NO CREATUREINFO!", GUID_LOPART(guid));
|
||||
return;
|
||||
}
|
||||
|
||||
TrainerSpellData const* trainer_spells = unit->GetTrainerSpells();
|
||||
if (!trainer_spells)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: SendTrainerList - Training spells not found for creature (GUID: %u Entry: %u)",
|
||||
GUID_LOPART(guid), unit->GetEntry());
|
||||
return;
|
||||
}
|
||||
|
||||
WorldPacket data(SMSG_TRAINER_LIST, 8+4+4+trainer_spells->spellList.size()*38 + strTitle.size()+1);
|
||||
data << guid;
|
||||
data << uint32(trainer_spells->trainerType);
|
||||
|
||||
size_t count_pos = data.wpos();
|
||||
data << uint32(trainer_spells->spellList.size());
|
||||
|
||||
// reputation discount
|
||||
float fDiscountMod = _player->GetReputationPriceDiscount(unit);
|
||||
bool can_learn_primary_prof = GetPlayer()->GetFreePrimaryProfessionPoints() > 0;
|
||||
|
||||
uint32 count = 0;
|
||||
for (TrainerSpellMap::const_iterator itr = trainer_spells->spellList.begin(); itr != trainer_spells->spellList.end(); ++itr)
|
||||
{
|
||||
TrainerSpell const* tSpell = &itr->second;
|
||||
|
||||
bool valid = true;
|
||||
bool primary_prof_first_rank = false;
|
||||
for (uint8 i = 0; i < MAX_SPELL_EFFECTS ; ++i)
|
||||
{
|
||||
if (!tSpell->learnedSpell[i])
|
||||
continue;
|
||||
if (!_player->IsSpellFitByClassAndRace(tSpell->learnedSpell[i]))
|
||||
{
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
SpellInfo const* learnedSpellInfo = sSpellMgr->GetSpellInfo(tSpell->learnedSpell[i]);
|
||||
if (learnedSpellInfo && learnedSpellInfo->IsPrimaryProfessionFirstRank())
|
||||
primary_prof_first_rank = true;
|
||||
}
|
||||
if (!valid)
|
||||
continue;
|
||||
|
||||
TrainerSpellState state = _player->GetTrainerSpellState(tSpell);
|
||||
|
||||
data << uint32(tSpell->spell); // learned spell (or cast-spell in profession case)
|
||||
data << uint8(state == TRAINER_SPELL_GREEN_DISABLED ? TRAINER_SPELL_GREEN : state);
|
||||
data << uint32(floor(tSpell->spellCost * fDiscountMod));
|
||||
|
||||
data << uint32(primary_prof_first_rank && can_learn_primary_prof ? 1 : 0);
|
||||
// primary prof. learn confirmation dialog
|
||||
data << uint32(primary_prof_first_rank ? 1 : 0); // must be equal prev. field to have learn button in enabled state
|
||||
data << uint8(tSpell->reqLevel);
|
||||
data << uint32(tSpell->reqSkill);
|
||||
data << uint32(tSpell->reqSkillValue);
|
||||
//prev + req or req + 0
|
||||
uint8 maxReq = 0;
|
||||
for (uint8 i = 0; i < MAX_SPELL_EFFECTS ; ++i)
|
||||
{
|
||||
if (!tSpell->learnedSpell[i])
|
||||
continue;
|
||||
if (uint32 prevSpellId = sSpellMgr->GetPrevSpellInChain(tSpell->learnedSpell[i]))
|
||||
{
|
||||
data << uint32(prevSpellId);
|
||||
++maxReq;
|
||||
}
|
||||
if (maxReq == 3)
|
||||
break;
|
||||
SpellsRequiringSpellMapBounds spellsRequired = sSpellMgr->GetSpellsRequiredForSpellBounds(tSpell->learnedSpell[i]);
|
||||
for (SpellsRequiringSpellMap::const_iterator itr2 = spellsRequired.first; itr2 != spellsRequired.second && maxReq < 3; ++itr2)
|
||||
{
|
||||
data << uint32(itr2->second);
|
||||
++maxReq;
|
||||
}
|
||||
if (maxReq == 3)
|
||||
break;
|
||||
}
|
||||
while (maxReq < 3)
|
||||
{
|
||||
data << uint32(0);
|
||||
++maxReq;
|
||||
}
|
||||
|
||||
++count;
|
||||
}
|
||||
|
||||
data << strTitle;
|
||||
|
||||
data.put<uint32>(count_pos, count);
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleTrainerBuySpellOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
uint64 guid;
|
||||
uint32 spellId = 0;
|
||||
|
||||
recv_data >> guid >> spellId;
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_TRAINER_BUY_SPELL NpcGUID=%u, learn spell id is: %u", uint32(GUID_LOPART(guid)), spellId);
|
||||
|
||||
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_TRAINER);
|
||||
if (!unit)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleTrainerBuySpellOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(guid)));
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
if (!unit->isCanTrainingOf(_player, true))
|
||||
return;
|
||||
|
||||
// check present spell in trainer spell list
|
||||
TrainerSpellData const* trainer_spells = unit->GetTrainerSpells();
|
||||
if (!trainer_spells)
|
||||
return;
|
||||
|
||||
// not found, cheat?
|
||||
TrainerSpell const* trainer_spell = trainer_spells->Find(spellId);
|
||||
if (!trainer_spell)
|
||||
return;
|
||||
|
||||
// can't be learn, cheat? Or double learn with lags...
|
||||
if (_player->GetTrainerSpellState(trainer_spell) != TRAINER_SPELL_GREEN)
|
||||
return;
|
||||
|
||||
// apply reputation discount
|
||||
uint32 nSpellCost = uint32(floor(trainer_spell->spellCost * _player->GetReputationPriceDiscount(unit)));
|
||||
|
||||
// check money requirement
|
||||
if (!_player->HasEnoughMoney(nSpellCost))
|
||||
return;
|
||||
|
||||
_player->ModifyMoney(-int32(nSpellCost));
|
||||
|
||||
unit->SendPlaySpellVisual(179); // 53 SpellCastDirected
|
||||
unit->SendPlaySpellImpact(_player->GetGUID(), 362); // 113 EmoteSalute
|
||||
|
||||
// learn explicitly or cast explicitly
|
||||
if (trainer_spell->IsCastable())
|
||||
_player->CastSpell(_player, trainer_spell->spell, true);
|
||||
else
|
||||
_player->learnSpell(spellId, false);
|
||||
|
||||
WorldPacket data(SMSG_TRAINER_BUY_SUCCEEDED, 12);
|
||||
data << uint64(guid);
|
||||
data << uint32(spellId); // should be same as in packet from client
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGossipHelloOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GOSSIP_HELLO");
|
||||
|
||||
uint64 guid;
|
||||
recv_data >> guid;
|
||||
|
||||
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE);
|
||||
if (!unit)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleGossipHelloOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(guid)));
|
||||
return;
|
||||
}
|
||||
|
||||
// set faction visible if needed
|
||||
if (FactionTemplateEntry const* factionTemplateEntry = sFactionTemplateStore.LookupEntry(unit->getFaction()))
|
||||
_player->GetReputationMgr().SetVisible(factionTemplateEntry);
|
||||
|
||||
GetPlayer()->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TALK);
|
||||
// remove fake death
|
||||
//if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
// GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
if (unit->isArmorer() || unit->isCivilian() || unit->isQuestGiver() || unit->isServiceProvider() || unit->isGuard())
|
||||
{
|
||||
unit->StopMoving();
|
||||
}
|
||||
|
||||
// If spiritguide, no need for gossip menu, just put player into resurrect queue
|
||||
if (unit->isSpiritGuide())
|
||||
{
|
||||
Battleground* bg = _player->GetBattleground();
|
||||
if (bg)
|
||||
{
|
||||
bg->AddPlayerToResurrectQueue(unit->GetGUID(), _player->GetGUID());
|
||||
sBattlegroundMgr->SendAreaSpiritHealerQueryOpcode(_player, bg, unit->GetGUID());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sScriptMgr->OnGossipHello(_player, unit))
|
||||
{
|
||||
// _player->TalkedToCreature(unit->GetEntry(), unit->GetGUID());
|
||||
_player->PrepareGossipMenu(unit, unit->GetCreatureInfo()->GossipMenuId, true);
|
||||
_player->SendPreparedGossip(unit);
|
||||
}
|
||||
unit->AI()->sGossipHello(_player);
|
||||
}
|
||||
|
||||
/*void WorldSession::HandleGossipSelectOptionOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_PACKETIO, "WORLD: CMSG_GOSSIP_SELECT_OPTION");
|
||||
|
||||
uint32 option;
|
||||
uint32 unk;
|
||||
uint64 guid;
|
||||
std::string code = "";
|
||||
|
||||
recv_data >> guid >> unk >> option;
|
||||
|
||||
if (_player->PlayerTalkClass->GossipOptionCoded(option))
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_PACKETIO, "reading string");
|
||||
recv_data >> code;
|
||||
sLog->outDebug(LOG_FILTER_PACKETIO, "string read: %s", code.c_str());
|
||||
}
|
||||
|
||||
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE);
|
||||
if (!unit)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_PACKETIO, "WORLD: HandleGossipSelectOptionOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)));
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
if (!code.empty())
|
||||
{
|
||||
if (!Script->GossipSelectWithCode(_player, unit, _player->PlayerTalkClass->GossipOptionSender (option), _player->PlayerTalkClass->GossipOptionAction(option), code.c_str()))
|
||||
unit->OnGossipSelect (_player, option);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Script->OnGossipSelect (_player, unit, _player->PlayerTalkClass->GossipOptionSender (option), _player->PlayerTalkClass->GossipOptionAction (option)))
|
||||
unit->OnGossipSelect (_player, option);
|
||||
}
|
||||
}*/
|
||||
|
||||
void WorldSession::HandleSpiritHealerActivateOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_SPIRIT_HEALER_ACTIVATE");
|
||||
|
||||
uint64 guid;
|
||||
|
||||
recv_data >> guid;
|
||||
|
||||
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_SPIRITHEALER);
|
||||
if (!unit)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleSpiritHealerActivateOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(guid)));
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
SendSpiritResurrect();
|
||||
}
|
||||
|
||||
void WorldSession::SendSpiritResurrect()
|
||||
{
|
||||
_player->ResurrectPlayer(0.5f, true);
|
||||
|
||||
_player->DurabilityLossAll(0.25f, true);
|
||||
|
||||
// get corpse nearest graveyard
|
||||
WorldSafeLocsEntry const* corpseGrave = NULL;
|
||||
Corpse* corpse = _player->GetCorpse();
|
||||
if (corpse)
|
||||
corpseGrave = sObjectMgr->GetClosestGraveYard(
|
||||
corpse->GetPositionX(), corpse->GetPositionY(), corpse->GetPositionZ(), corpse->GetMapId(), _player->GetTeam());
|
||||
|
||||
// now can spawn bones
|
||||
_player->SpawnCorpseBones();
|
||||
|
||||
// teleport to nearest from corpse graveyard, if different from nearest to player ghost
|
||||
if (corpseGrave)
|
||||
{
|
||||
WorldSafeLocsEntry const* ghostGrave = sObjectMgr->GetClosestGraveYard(
|
||||
_player->GetPositionX(), _player->GetPositionY(), _player->GetPositionZ(), _player->GetMapId(), _player->GetTeam());
|
||||
|
||||
if (corpseGrave != ghostGrave)
|
||||
_player->TeleportTo(corpseGrave->map_id, corpseGrave->x, corpseGrave->y, corpseGrave->z, _player->GetOrientation());
|
||||
// or update at original position
|
||||
else
|
||||
_player->UpdateObjectVisibility();
|
||||
}
|
||||
// or update at original position
|
||||
else
|
||||
_player->UpdateObjectVisibility();
|
||||
}
|
||||
|
||||
void WorldSession::HandleBinderActivateOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
uint64 npcGUID;
|
||||
recv_data >> npcGUID;
|
||||
|
||||
if (!GetPlayer()->IsInWorld() || !GetPlayer()->isAlive())
|
||||
return;
|
||||
|
||||
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(npcGUID, UNIT_NPC_FLAG_INNKEEPER);
|
||||
if (!unit)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleBinderActivateOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(npcGUID)));
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
SendBindPoint(unit);
|
||||
}
|
||||
|
||||
void WorldSession::SendBindPoint(Creature* npc)
|
||||
{
|
||||
// prevent set homebind to instances in any case
|
||||
if (GetPlayer()->GetMap()->Instanceable())
|
||||
return;
|
||||
|
||||
uint32 bindspell = 3286;
|
||||
|
||||
// update sql homebind
|
||||
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_PLAYER_HOMEBIND);
|
||||
stmt->setUInt16(0, _player->GetMapId());
|
||||
stmt->setUInt16(1, _player->GetAreaId());
|
||||
stmt->setFloat (2, _player->GetPositionX());
|
||||
stmt->setFloat (3, _player->GetPositionY());
|
||||
stmt->setFloat (4, _player->GetPositionZ());
|
||||
stmt->setUInt32(5, _player->GetGUIDLow());
|
||||
CharacterDatabase.Execute(stmt);
|
||||
|
||||
_player->m_homebindMapId = _player->GetMapId();
|
||||
_player->m_homebindAreaId = _player->GetAreaId();
|
||||
_player->m_homebindX = _player->GetPositionX();
|
||||
_player->m_homebindY = _player->GetPositionY();
|
||||
_player->m_homebindZ = _player->GetPositionZ();
|
||||
|
||||
// send spell for homebinding (3286)
|
||||
npc->CastSpell(_player, bindspell, true);
|
||||
|
||||
WorldPacket data(SMSG_TRAINER_BUY_SUCCEEDED, (8+4));
|
||||
data << uint64(npc->GetGUID());
|
||||
data << uint32(bindspell);
|
||||
SendPacket(&data);
|
||||
|
||||
_player->PlayerTalkClass->SendCloseGossip();
|
||||
}
|
||||
|
||||
void WorldSession::HandleListStabledPetsOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recv MSG_LIST_STABLED_PETS");
|
||||
uint64 npcGUID;
|
||||
|
||||
recv_data >> npcGUID;
|
||||
|
||||
if (!CheckStableMaster(npcGUID))
|
||||
return;
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
// remove mounts this fix bug where getting pet from stable while mounted deletes pet.
|
||||
if (GetPlayer()->IsMounted())
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_MOUNTED);
|
||||
|
||||
SendStablePet(npcGUID);
|
||||
}
|
||||
|
||||
void WorldSession::SendStablePet(uint64 guid)
|
||||
{
|
||||
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOTS_DETAIL);
|
||||
|
||||
stmt->setUInt32(0, _player->GetGUIDLow());
|
||||
stmt->setUInt8(1, PET_SAVE_FIRST_STABLE_SLOT);
|
||||
stmt->setUInt8(2, PET_SAVE_LAST_STABLE_SLOT);
|
||||
|
||||
_sendStabledPetCallback.SetParam(guid);
|
||||
_sendStabledPetCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt));
|
||||
}
|
||||
|
||||
void WorldSession::SendStablePetCallback(PreparedQueryResult result, uint64 guid)
|
||||
{
|
||||
if (!GetPlayer())
|
||||
return;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recv MSG_LIST_STABLED_PETS Send.");
|
||||
|
||||
WorldPacket data(MSG_LIST_STABLED_PETS, 200); // guess size
|
||||
|
||||
data << uint64 (guid);
|
||||
|
||||
Pet* pet = _player->GetPet();
|
||||
|
||||
size_t wpos = data.wpos();
|
||||
data << uint8(0); // place holder for slot show number
|
||||
|
||||
data << uint8(GetPlayer()->m_stableSlots);
|
||||
|
||||
uint8 num = 0; // counter for place holder
|
||||
|
||||
// not let move dead pet in slot
|
||||
if (pet && pet->isAlive() && pet->getPetType() == HUNTER_PET)
|
||||
{
|
||||
data << uint32(pet->GetCharmInfo()->GetPetNumber());
|
||||
data << uint32(pet->GetEntry());
|
||||
data << uint32(pet->getLevel());
|
||||
data << pet->GetName(); // petname
|
||||
data << uint8(1); // 1 = current, 2/3 = in stable (any from 4, 5, ... create problems with proper show)
|
||||
++num;
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
|
||||
data << uint32(fields[1].GetUInt32()); // petnumber
|
||||
data << uint32(fields[2].GetUInt32()); // creature entry
|
||||
data << uint32(fields[3].GetUInt16()); // level
|
||||
data << fields[4].GetString(); // name
|
||||
data << uint8(2); // 1 = current, 2/3 = in stable (any from 4, 5, ... create problems with proper show)
|
||||
|
||||
++num;
|
||||
}
|
||||
while (result->NextRow());
|
||||
}
|
||||
|
||||
data.put<uint8>(wpos, num); // set real data to placeholder
|
||||
SendPacket(&data);
|
||||
|
||||
}
|
||||
|
||||
void WorldSession::SendStableResult(uint8 res)
|
||||
{
|
||||
WorldPacket data(SMSG_STABLE_RESULT, 1);
|
||||
data << uint8(res);
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleStablePet(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recv CMSG_STABLE_PET");
|
||||
uint64 npcGUID;
|
||||
|
||||
recv_data >> npcGUID;
|
||||
|
||||
if (!GetPlayer()->isAlive())
|
||||
{
|
||||
SendStableResult(STABLE_ERR_STABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CheckStableMaster(npcGUID))
|
||||
{
|
||||
SendStableResult(STABLE_ERR_STABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
Pet* pet = _player->GetPet();
|
||||
|
||||
// can't place in stable dead pet
|
||||
if (!pet||!pet->isAlive()||pet->getPetType() != HUNTER_PET)
|
||||
{
|
||||
SendStableResult(STABLE_ERR_STABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOTS);
|
||||
|
||||
stmt->setUInt32(0, _player->GetGUIDLow());
|
||||
stmt->setUInt8(1, PET_SAVE_FIRST_STABLE_SLOT);
|
||||
stmt->setUInt8(2, PET_SAVE_LAST_STABLE_SLOT);
|
||||
|
||||
_stablePetCallback = CharacterDatabase.AsyncQuery(stmt);
|
||||
}
|
||||
|
||||
void WorldSession::HandleStablePetCallback(PreparedQueryResult result)
|
||||
{
|
||||
if (!GetPlayer())
|
||||
return;
|
||||
|
||||
uint8 freeSlot = 1;
|
||||
if (result)
|
||||
{
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
|
||||
uint8 slot = fields[1].GetUInt8();
|
||||
|
||||
// slots ordered in query, and if not equal then free
|
||||
if (slot != freeSlot)
|
||||
break;
|
||||
|
||||
// this slot not free, skip
|
||||
++freeSlot;
|
||||
}
|
||||
while (result->NextRow());
|
||||
}
|
||||
|
||||
WorldPacket data(SMSG_STABLE_RESULT, 1);
|
||||
if (freeSlot > 0 && freeSlot <= GetPlayer()->m_stableSlots)
|
||||
{
|
||||
_player->RemovePet(_player->GetPet(), PetSaveMode(freeSlot));
|
||||
SendStableResult(STABLE_SUCCESS_STABLE);
|
||||
}
|
||||
else
|
||||
SendStableResult(STABLE_ERR_STABLE);
|
||||
}
|
||||
|
||||
void WorldSession::HandleUnstablePet(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recv CMSG_UNSTABLE_PET.");
|
||||
uint64 npcGUID;
|
||||
uint32 petnumber;
|
||||
|
||||
recv_data >> npcGUID >> petnumber;
|
||||
|
||||
if (!CheckStableMaster(npcGUID))
|
||||
{
|
||||
SendStableResult(STABLE_ERR_STABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_ENTRY);
|
||||
|
||||
stmt->setUInt32(0, _player->GetGUIDLow());
|
||||
stmt->setUInt32(1, petnumber);
|
||||
stmt->setUInt8(2, PET_SAVE_FIRST_STABLE_SLOT);
|
||||
stmt->setUInt8(3, PET_SAVE_LAST_STABLE_SLOT);
|
||||
|
||||
_unstablePetCallback.SetParam(petnumber);
|
||||
_unstablePetCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt));
|
||||
}
|
||||
|
||||
void WorldSession::HandleUnstablePetCallback(PreparedQueryResult result, uint32 petId)
|
||||
{
|
||||
if (!GetPlayer())
|
||||
return;
|
||||
|
||||
uint32 petEntry = 0;
|
||||
if (result)
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
petEntry = fields[0].GetUInt32();
|
||||
}
|
||||
|
||||
if (!petEntry)
|
||||
{
|
||||
SendStableResult(STABLE_ERR_STABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(petEntry);
|
||||
if (!creatureInfo || !creatureInfo->isTameable(_player->CanTameExoticPets()))
|
||||
{
|
||||
// if problem in exotic pet
|
||||
if (creatureInfo && creatureInfo->isTameable(true))
|
||||
SendStableResult(STABLE_ERR_EXOTIC);
|
||||
else
|
||||
SendStableResult(STABLE_ERR_STABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
Pet* pet = _player->GetPet();
|
||||
if (pet && pet->isAlive())
|
||||
{
|
||||
SendStableResult(STABLE_ERR_STABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
// delete dead pet
|
||||
if (pet)
|
||||
_player->RemovePet(pet, PET_SAVE_AS_DELETED);
|
||||
|
||||
Pet* newPet = new Pet(_player, HUNTER_PET);
|
||||
if (!newPet->LoadPetFromDB(_player, petEntry, petId))
|
||||
{
|
||||
delete newPet;
|
||||
newPet = NULL;
|
||||
SendStableResult(STABLE_ERR_STABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
SendStableResult(STABLE_SUCCESS_UNSTABLE);
|
||||
}
|
||||
|
||||
void WorldSession::HandleBuyStableSlot(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recv CMSG_BUY_STABLE_SLOT.");
|
||||
uint64 npcGUID;
|
||||
|
||||
recv_data >> npcGUID;
|
||||
|
||||
if (!CheckStableMaster(npcGUID))
|
||||
{
|
||||
SendStableResult(STABLE_ERR_STABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
if (GetPlayer()->m_stableSlots < MAX_PET_STABLES)
|
||||
{
|
||||
StableSlotPricesEntry const* SlotPrice = sStableSlotPricesStore.LookupEntry(GetPlayer()->m_stableSlots+1);
|
||||
if (_player->HasEnoughMoney(SlotPrice->Price))
|
||||
{
|
||||
++GetPlayer()->m_stableSlots;
|
||||
_player->ModifyMoney(-int32(SlotPrice->Price));
|
||||
SendStableResult(STABLE_SUCCESS_BUY_SLOT);
|
||||
}
|
||||
else
|
||||
SendStableResult(STABLE_ERR_MONEY);
|
||||
}
|
||||
else
|
||||
SendStableResult(STABLE_ERR_STABLE);
|
||||
}
|
||||
|
||||
void WorldSession::HandleStableRevivePet(WorldPacket &/* recv_data */)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "HandleStableRevivePet: Not implemented");
|
||||
}
|
||||
|
||||
void WorldSession::HandleStableSwapPet(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recv CMSG_STABLE_SWAP_PET.");
|
||||
uint64 npcGUID;
|
||||
uint32 petId;
|
||||
|
||||
recv_data >> npcGUID >> petId;
|
||||
|
||||
if (!CheckStableMaster(npcGUID))
|
||||
{
|
||||
SendStableResult(STABLE_ERR_STABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
Pet* pet = _player->GetPet();
|
||||
|
||||
if (!pet || pet->getPetType() != HUNTER_PET)
|
||||
{
|
||||
SendStableResult(STABLE_ERR_STABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Find swapped pet slot in stable
|
||||
|
||||
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOT_BY_ID);
|
||||
|
||||
stmt->setUInt32(0, _player->GetGUIDLow());
|
||||
stmt->setUInt32(1, petId);
|
||||
|
||||
_stableSwapCallback.SetParam(petId);
|
||||
_stableSwapCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt));
|
||||
}
|
||||
|
||||
void WorldSession::HandleStableSwapPetCallback(PreparedQueryResult result, uint32 petId)
|
||||
{
|
||||
if (!GetPlayer())
|
||||
return;
|
||||
|
||||
if (!result)
|
||||
{
|
||||
SendStableResult(STABLE_ERR_STABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
Field* fields = result->Fetch();
|
||||
|
||||
uint32 slot = fields[0].GetUInt8();
|
||||
uint32 petEntry = fields[1].GetUInt32();
|
||||
|
||||
if (!petEntry)
|
||||
{
|
||||
SendStableResult(STABLE_ERR_STABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(petEntry);
|
||||
if (!creatureInfo || !creatureInfo->isTameable(_player->CanTameExoticPets()))
|
||||
{
|
||||
// if problem in exotic pet
|
||||
if (creatureInfo && creatureInfo->isTameable(true))
|
||||
SendStableResult(STABLE_ERR_EXOTIC);
|
||||
else
|
||||
SendStableResult(STABLE_ERR_STABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
// move alive pet to slot or delete dead pet
|
||||
Pet* pet = _player->GetPet();
|
||||
|
||||
_player->RemovePet(pet, pet->isAlive() ? PetSaveMode(slot) : PET_SAVE_AS_DELETED);
|
||||
|
||||
// summon unstabled pet
|
||||
Pet* newpet = new Pet(_player);
|
||||
if (!newpet->LoadPetFromDB(_player, petEntry, petId))
|
||||
{
|
||||
delete newpet;
|
||||
SendStableResult(STABLE_ERR_STABLE);
|
||||
}
|
||||
else
|
||||
SendStableResult(STABLE_SUCCESS_UNSTABLE);
|
||||
}
|
||||
|
||||
void WorldSession::HandleRepairItemOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_REPAIR_ITEM");
|
||||
|
||||
uint64 npcGUID, itemGUID;
|
||||
uint8 guildBank; // new in 2.3.2, bool that means from guild bank money
|
||||
|
||||
recv_data >> npcGUID >> itemGUID >> guildBank;
|
||||
|
||||
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(npcGUID, UNIT_NPC_FLAG_REPAIR);
|
||||
if (!unit)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleRepairItemOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(npcGUID)));
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
// reputation discount
|
||||
float discountMod = _player->GetReputationPriceDiscount(unit);
|
||||
|
||||
if (itemGUID)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "ITEM: Repair item, itemGUID = %u, npcGUID = %u", GUID_LOPART(itemGUID), GUID_LOPART(npcGUID));
|
||||
|
||||
Item* item = _player->GetItemByGuid(itemGUID);
|
||||
if (item)
|
||||
_player->DurabilityRepair(item->GetPos(), true, discountMod, guildBank);
|
||||
}
|
||||
else
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "ITEM: Repair all items, npcGUID = %u", GUID_LOPART(npcGUID));
|
||||
_player->DurabilityRepairAll(true, discountMod, guildBank);
|
||||
}
|
||||
}
|
||||
|
||||
Executable
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef __NPCHANDLER_H
|
||||
#define __NPCHANDLER_H
|
||||
|
||||
struct QEmote
|
||||
{
|
||||
uint32 _Emote;
|
||||
uint32 _Delay;
|
||||
};
|
||||
|
||||
#define MAX_GOSSIP_TEXT_EMOTES 3
|
||||
|
||||
struct GossipTextOption
|
||||
{
|
||||
std::string Text_0;
|
||||
std::string Text_1;
|
||||
uint32 Language;
|
||||
float Probability;
|
||||
QEmote Emotes[MAX_GOSSIP_TEXT_EMOTES];
|
||||
};
|
||||
|
||||
#define MAX_GOSSIP_TEXT_OPTIONS 8
|
||||
|
||||
struct GossipText
|
||||
{
|
||||
GossipTextOption Options[MAX_GOSSIP_TEXT_OPTIONS];
|
||||
};
|
||||
|
||||
struct PageTextLocale
|
||||
{
|
||||
StringVector Text;
|
||||
};
|
||||
|
||||
struct NpcTextLocale
|
||||
{
|
||||
NpcTextLocale() { Text_0.resize(8); Text_1.resize(8); }
|
||||
|
||||
std::vector<StringVector> Text_0;
|
||||
std::vector<StringVector> Text_1;
|
||||
};
|
||||
#endif
|
||||
|
||||
Executable
+879
@@ -0,0 +1,879 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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 "Common.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "SpellMgr.h"
|
||||
#include "Log.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Spell.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "CreatureAI.h"
|
||||
#include "Util.h"
|
||||
#include "Pet.h"
|
||||
#include "World.h"
|
||||
#include "Group.h"
|
||||
#include "SpellInfo.h"
|
||||
|
||||
void WorldSession::HandleDismissCritter(WorldPacket &recv_data)
|
||||
{
|
||||
uint64 guid;
|
||||
recv_data >> guid;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_DISMISS_CRITTER for GUID " UI64FMTD, guid);
|
||||
|
||||
Unit* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid);
|
||||
|
||||
if (!pet)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Vanitypet (guid: %u) does not exist - player '%s' (guid: %u / account: %u) attempted to dismiss it (possibly lagged out)",
|
||||
uint32(GUID_LOPART(guid)), GetPlayer()->GetName(), GetPlayer()->GetGUIDLow(), GetAccountId());
|
||||
return;
|
||||
}
|
||||
|
||||
if (_player->GetCritterGUID() == pet->GetGUID())
|
||||
{
|
||||
if (pet->GetTypeId() == TYPEID_UNIT && pet->ToCreature()->isSummon())
|
||||
pet->ToTempSummon()->UnSummon();
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandlePetAction(WorldPacket & recv_data)
|
||||
{
|
||||
uint64 guid1;
|
||||
uint32 data;
|
||||
uint64 guid2;
|
||||
recv_data >> guid1; //pet guid
|
||||
recv_data >> data;
|
||||
recv_data >> guid2; //tag guid
|
||||
|
||||
uint32 spellid = UNIT_ACTION_BUTTON_ACTION(data);
|
||||
uint8 flag = UNIT_ACTION_BUTTON_TYPE(data); //delete = 0x07 CastSpell = C1
|
||||
|
||||
// used also for charmed creature
|
||||
Unit* pet= ObjectAccessor::GetUnit(*_player, guid1);
|
||||
sLog->outDetail("HandlePetAction: Pet %u - flag: %u, spellid: %u, target: %u.", uint32(GUID_LOPART(guid1)), uint32(flag), spellid, uint32(GUID_LOPART(guid2)));
|
||||
|
||||
if (!pet)
|
||||
{
|
||||
sLog->outError("HandlePetAction: Pet (GUID: %u) doesn't exist for player '%s'", uint32(GUID_LOPART(guid1)), GetPlayer()->GetName());
|
||||
return;
|
||||
}
|
||||
|
||||
if (pet != GetPlayer()->GetFirstControlled())
|
||||
{
|
||||
sLog->outError("HandlePetAction: Pet (GUID: %u) does not belong to player '%s'", uint32(GUID_LOPART(guid1)), GetPlayer()->GetName());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pet->isAlive())
|
||||
{
|
||||
SpellInfo const* spell = (flag == ACT_ENABLED || flag == ACT_PASSIVE) ? sSpellMgr->GetSpellInfo(spellid) : NULL;
|
||||
if (!spell)
|
||||
return;
|
||||
if (!(spell->Attributes & SPELL_ATTR0_CASTABLE_WHILE_DEAD))
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO: allow control charmed player?
|
||||
if (pet->GetTypeId() == TYPEID_PLAYER && !(flag == ACT_COMMAND && spellid == COMMAND_ATTACK))
|
||||
return;
|
||||
|
||||
if (GetPlayer()->m_Controlled.size() == 1)
|
||||
HandlePetActionHelper(pet, guid1, spellid, flag, guid2);
|
||||
else
|
||||
{
|
||||
//If a pet is dismissed, m_Controlled will change
|
||||
std::vector<Unit*> controlled;
|
||||
for (Unit::ControlList::iterator itr = GetPlayer()->m_Controlled.begin(); itr != GetPlayer()->m_Controlled.end(); ++itr)
|
||||
if ((*itr)->GetEntry() == pet->GetEntry() && (*itr)->isAlive())
|
||||
controlled.push_back(*itr);
|
||||
for (std::vector<Unit*>::iterator itr = controlled.begin(); itr != controlled.end(); ++itr)
|
||||
HandlePetActionHelper(*itr, guid1, spellid, flag, guid2);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandlePetStopAttack(WorldPacket &recv_data)
|
||||
{
|
||||
uint64 guid;
|
||||
recv_data >> guid;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_PET_STOP_ATTACK for GUID " UI64FMTD "", guid);
|
||||
|
||||
Unit* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid);
|
||||
|
||||
if (!pet)
|
||||
{
|
||||
sLog->outError("HandlePetStopAttack: Pet %u does not exist", uint32(GUID_LOPART(guid)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharm())
|
||||
{
|
||||
sLog->outError("HandlePetStopAttack: Pet GUID %u isn't a pet or charmed creature of player %s", uint32(GUID_LOPART(guid)), GetPlayer()->GetName());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pet->isAlive())
|
||||
return;
|
||||
|
||||
pet->AttackStop();
|
||||
}
|
||||
|
||||
void WorldSession::HandlePetActionHelper(Unit* pet, uint64 guid1, uint16 spellid, uint16 flag, uint64 guid2)
|
||||
{
|
||||
CharmInfo* charmInfo = pet->GetCharmInfo();
|
||||
if (!charmInfo)
|
||||
{
|
||||
sLog->outError("WorldSession::HandlePetAction(petGuid: " UI64FMTD ", tagGuid: " UI64FMTD ", spellId: %u, flag: %u): object (entry: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!",
|
||||
guid1, guid2, spellid, flag, pet->GetGUIDLow(), pet->GetTypeId());
|
||||
return;
|
||||
}
|
||||
|
||||
switch (flag)
|
||||
{
|
||||
case ACT_COMMAND: //0x07
|
||||
switch (spellid)
|
||||
{
|
||||
case COMMAND_STAY: //flat=1792 //STAY
|
||||
pet->AttackStop();
|
||||
pet->InterruptNonMeleeSpells(false);
|
||||
pet->GetMotionMaster()->Clear(false);
|
||||
pet->GetMotionMaster()->MoveIdle();
|
||||
charmInfo->SetCommandState(COMMAND_STAY);
|
||||
|
||||
charmInfo->SetIsCommandAttack(false);
|
||||
charmInfo->SetIsAtStay(true);
|
||||
charmInfo->SetIsFollowing(false);
|
||||
charmInfo->SetIsReturning(false);
|
||||
charmInfo->SaveStayPosition();
|
||||
break;
|
||||
case COMMAND_FOLLOW: //spellid=1792 //FOLLOW
|
||||
pet->AttackStop();
|
||||
pet->InterruptNonMeleeSpells(false);
|
||||
pet->GetMotionMaster()->MoveFollow(_player, PET_FOLLOW_DIST, pet->GetFollowAngle());
|
||||
charmInfo->SetCommandState(COMMAND_FOLLOW);
|
||||
|
||||
charmInfo->SetIsCommandAttack(false);
|
||||
charmInfo->SetIsAtStay(false);
|
||||
charmInfo->SetIsReturning(true);
|
||||
charmInfo->SetIsFollowing(false);
|
||||
break;
|
||||
case COMMAND_ATTACK: //spellid=1792 //ATTACK
|
||||
{
|
||||
// Can't attack if owner is pacified
|
||||
if (_player->HasAuraType(SPELL_AURA_MOD_PACIFY))
|
||||
{
|
||||
//pet->SendPetCastFail(spellid, SPELL_FAILED_PACIFIED);
|
||||
//TODO: Send proper error message to client
|
||||
return;
|
||||
}
|
||||
|
||||
// only place where pet can be player
|
||||
Unit* TargetUnit = ObjectAccessor::GetUnit(*_player, guid2);
|
||||
if (!TargetUnit)
|
||||
return;
|
||||
|
||||
if (Unit* owner = pet->GetOwner())
|
||||
if (!owner->IsValidAttackTarget(TargetUnit))
|
||||
return;
|
||||
|
||||
// Not let attack through obstructions
|
||||
if (sWorld->getBoolConfig(CONFIG_PET_LOS))
|
||||
{
|
||||
if (!pet->IsWithinLOSInMap(TargetUnit))
|
||||
return;
|
||||
}
|
||||
|
||||
pet->ClearUnitState(UNIT_STAT_FOLLOW);
|
||||
// This is true if pet has no target or has target but targets differs.
|
||||
if (pet->getVictim() != TargetUnit || (pet->getVictim() == TargetUnit && !pet->GetCharmInfo()->IsCommandAttack()))
|
||||
{
|
||||
if (pet->getVictim())
|
||||
pet->AttackStop();
|
||||
|
||||
if (pet->GetTypeId() != TYPEID_PLAYER && pet->ToCreature()->IsAIEnabled)
|
||||
{
|
||||
charmInfo->SetIsCommandAttack(true);
|
||||
charmInfo->SetIsAtStay(false);
|
||||
charmInfo->SetIsFollowing(false);
|
||||
charmInfo->SetIsReturning(false);
|
||||
|
||||
pet->ToCreature()->AI()->AttackStart(TargetUnit);
|
||||
|
||||
//10% chance to play special pet attack talk, else growl
|
||||
if (pet->ToCreature()->isPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != TargetUnit && urand(0, 100) < 10)
|
||||
pet->SendPetTalk((uint32)PET_TALK_ATTACK);
|
||||
else
|
||||
{
|
||||
// 90% chance for pet and 100% chance for charmed creature
|
||||
pet->SendPetAIReaction(guid1);
|
||||
}
|
||||
}
|
||||
else // charmed player
|
||||
{
|
||||
if (pet->getVictim() && pet->getVictim() != TargetUnit)
|
||||
pet->AttackStop();
|
||||
|
||||
charmInfo->SetIsCommandAttack(true);
|
||||
charmInfo->SetIsAtStay(false);
|
||||
charmInfo->SetIsFollowing(false);
|
||||
charmInfo->SetIsReturning(false);
|
||||
|
||||
pet->Attack(TargetUnit, true);
|
||||
pet->SendPetAIReaction(guid1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet)
|
||||
if (pet->GetCharmerGUID() == GetPlayer()->GetGUID())
|
||||
_player->StopCastingCharm();
|
||||
else if (pet->GetOwnerGUID() == GetPlayer()->GetGUID())
|
||||
{
|
||||
ASSERT(pet->GetTypeId() == TYPEID_UNIT);
|
||||
if (pet->isPet())
|
||||
{
|
||||
if (((Pet*)pet)->getPetType() == HUNTER_PET)
|
||||
GetPlayer()->RemovePet((Pet*)pet, PET_SAVE_AS_DELETED);
|
||||
else
|
||||
//dismissing a summoned pet is like killing them (this prevents returning a soulshard...)
|
||||
pet->setDeathState(CORPSE);
|
||||
}
|
||||
else if (pet->HasUnitTypeMask(UNIT_MASK_MINION))
|
||||
{
|
||||
((Minion*)pet)->UnSummon();
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
sLog->outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid);
|
||||
}
|
||||
break;
|
||||
case ACT_REACTION: // 0x6
|
||||
switch (spellid)
|
||||
{
|
||||
case REACT_PASSIVE: //passive
|
||||
pet->AttackStop();
|
||||
|
||||
case REACT_DEFENSIVE: //recovery
|
||||
case REACT_AGGRESSIVE: //activete
|
||||
if (pet->GetTypeId() == TYPEID_UNIT)
|
||||
pet->ToCreature()->SetReactState(ReactStates(spellid));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ACT_DISABLED: // 0x81 spell (disabled), ignore
|
||||
case ACT_PASSIVE: // 0x01
|
||||
case ACT_ENABLED: // 0xC1 spell
|
||||
{
|
||||
Unit* unit_target = NULL;
|
||||
|
||||
if (guid2)
|
||||
unit_target = ObjectAccessor::GetUnit(*_player, guid2);
|
||||
|
||||
// do not cast unknown spells
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellid);
|
||||
if (!spellInfo)
|
||||
{
|
||||
sLog->outError("WORLD: unknown PET spell id %i", spellid);
|
||||
return;
|
||||
}
|
||||
|
||||
if (spellInfo->StartRecoveryCategory > 0)
|
||||
if (pet->GetCharmInfo() && pet->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo))
|
||||
return;
|
||||
|
||||
for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
||||
{
|
||||
if (spellInfo->Effects[i].TargetA.GetTarget() == TARGET_UNIT_SRC_AREA_ENEMY || spellInfo->Effects[i].TargetA.GetTarget() == TARGET_UNIT_DEST_AREA_ENEMY || spellInfo->Effects[i].TargetA.GetTarget() == TARGET_DEST_DYNOBJ_ENEMY)
|
||||
return;
|
||||
}
|
||||
|
||||
// do not cast not learned spells
|
||||
if (!pet->HasSpell(spellid) || spellInfo->IsPassive())
|
||||
return;
|
||||
|
||||
// Clear the flags as if owner clicked 'attack'. AI will reset them
|
||||
// after AttackStart, even if spell failed
|
||||
if (pet->GetCharmInfo())
|
||||
{
|
||||
pet->GetCharmInfo()->SetIsAtStay(false);
|
||||
pet->GetCharmInfo()->SetIsCommandAttack(true);
|
||||
pet->GetCharmInfo()->SetIsReturning(false);
|
||||
pet->GetCharmInfo()->SetIsFollowing(false);
|
||||
}
|
||||
|
||||
Spell* spell = new Spell(pet, spellInfo, TRIGGERED_NONE);
|
||||
|
||||
SpellCastResult result = spell->CheckPetCast(unit_target);
|
||||
|
||||
//auto turn to target unless possessed
|
||||
if (result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->isPossessed() && !pet->IsVehicle())
|
||||
{
|
||||
if (unit_target)
|
||||
{
|
||||
pet->SetInFront(unit_target);
|
||||
if (unit_target->GetTypeId() == TYPEID_PLAYER)
|
||||
pet->SendUpdateToPlayer((Player*)unit_target);
|
||||
}
|
||||
else if (Unit* unit_target2 = spell->m_targets.GetUnitTarget())
|
||||
{
|
||||
pet->SetInFront(unit_target2);
|
||||
if (unit_target2->GetTypeId() == TYPEID_PLAYER)
|
||||
pet->SendUpdateToPlayer((Player*)unit_target2);
|
||||
}
|
||||
if (Unit* powner = pet->GetCharmerOrOwner())
|
||||
if (powner->GetTypeId() == TYPEID_PLAYER)
|
||||
pet->SendUpdateToPlayer(powner->ToPlayer());
|
||||
result = SPELL_CAST_OK;
|
||||
}
|
||||
|
||||
if (result == SPELL_CAST_OK)
|
||||
{
|
||||
pet->ToCreature()->AddCreatureSpellCooldown(spellid);
|
||||
|
||||
unit_target = spell->m_targets.GetUnitTarget();
|
||||
|
||||
//10% chance to play special pet attack talk, else growl
|
||||
//actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
|
||||
if (pet->ToCreature()->isPet() && (((Pet*)pet)->getPetType() == SUMMON_PET) && (pet != unit_target) && (urand(0, 100) < 10))
|
||||
pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL);
|
||||
else
|
||||
{
|
||||
pet->SendPetAIReaction(guid1);
|
||||
}
|
||||
|
||||
if (unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->isPossessed() && !pet->IsVehicle())
|
||||
{
|
||||
// This is true if pet has no target or has target but targets differs.
|
||||
if (pet->getVictim() != unit_target)
|
||||
{
|
||||
if (pet->getVictim())
|
||||
pet->AttackStop();
|
||||
pet->GetMotionMaster()->Clear();
|
||||
if (pet->ToCreature()->IsAIEnabled)
|
||||
pet->ToCreature()->AI()->AttackStart(unit_target);
|
||||
}
|
||||
}
|
||||
|
||||
spell->prepare(&(spell->m_targets));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pet->isPossessed() || pet->IsVehicle())
|
||||
Spell::SendCastResult(GetPlayer(), spellInfo, 0, result);
|
||||
else
|
||||
pet->SendPetCastFail(spellid, result);
|
||||
|
||||
if (!pet->ToCreature()->HasSpellCooldown(spellid))
|
||||
GetPlayer()->SendClearCooldown(spellid, pet);
|
||||
|
||||
spell->finish(false);
|
||||
delete spell;
|
||||
|
||||
// reset specific flags in case of spell fail. AI will reset other flags
|
||||
if (pet->GetCharmInfo())
|
||||
pet->GetCharmInfo()->SetIsCommandAttack(false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
sLog->outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandlePetNameQuery(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDetail("HandlePetNameQuery. CMSG_PET_NAME_QUERY");
|
||||
|
||||
uint32 petnumber;
|
||||
uint64 petguid;
|
||||
|
||||
recv_data >> petnumber;
|
||||
recv_data >> petguid;
|
||||
|
||||
SendPetNameQuery(petguid, petnumber);
|
||||
}
|
||||
|
||||
void WorldSession::SendPetNameQuery(uint64 petguid, uint32 petnumber)
|
||||
{
|
||||
Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, petguid);
|
||||
if (!pet)
|
||||
{
|
||||
WorldPacket data(SMSG_PET_NAME_QUERY_RESPONSE, (4+1+4+1));
|
||||
data << uint32(petnumber);
|
||||
data << uint8(0);
|
||||
data << uint32(0);
|
||||
data << uint8(0);
|
||||
_player->GetSession()->SendPacket(&data);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string name = pet->GetName();
|
||||
|
||||
WorldPacket data(SMSG_PET_NAME_QUERY_RESPONSE, (4+4+name.size()+1));
|
||||
data << uint32(petnumber);
|
||||
data << name.c_str();
|
||||
data << uint32(pet->GetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP));
|
||||
|
||||
if (pet->isPet() && ((Pet*)pet)->GetDeclinedNames())
|
||||
{
|
||||
data << uint8(1);
|
||||
for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
|
||||
data << ((Pet*)pet)->GetDeclinedNames()->name[i];
|
||||
}
|
||||
else
|
||||
data << uint8(0);
|
||||
|
||||
_player->GetSession()->SendPacket(&data);
|
||||
}
|
||||
|
||||
bool WorldSession::CheckStableMaster(uint64 guid)
|
||||
{
|
||||
// spell case or GM
|
||||
if (guid == GetPlayer()->GetGUID())
|
||||
{
|
||||
if (!GetPlayer()->isGameMaster() && !GetPlayer()->HasAuraType(SPELL_AURA_OPEN_STABLE))
|
||||
{
|
||||
sLog->outStaticDebug("Player (GUID:%u) attempt open stable in cheating way.", GUID_LOPART(guid));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// stable master case
|
||||
else
|
||||
{
|
||||
if (!GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_STABLEMASTER))
|
||||
{
|
||||
sLog->outStaticDebug("Stablemaster (GUID:%u) not found or you can't interact with him.", GUID_LOPART(guid));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void WorldSession::HandlePetSetAction(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDetail("HandlePetSetAction. CMSG_PET_SET_ACTION");
|
||||
|
||||
uint64 petguid;
|
||||
uint8 count;
|
||||
|
||||
recv_data >> petguid;
|
||||
|
||||
Unit* pet = ObjectAccessor::GetUnit(*_player, petguid);
|
||||
|
||||
if (!pet || pet != _player->GetFirstControlled())
|
||||
{
|
||||
sLog->outError("HandlePetSetAction: Unknown pet (GUID: %u) or pet owner (GUID: %u)", GUID_LOPART(petguid), _player->GetGUIDLow());
|
||||
return;
|
||||
}
|
||||
|
||||
CharmInfo* charmInfo = pet->GetCharmInfo();
|
||||
if (!charmInfo)
|
||||
{
|
||||
sLog->outError("WorldSession::HandlePetSetAction: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet->GetGUIDLow(), pet->GetTypeId());
|
||||
return;
|
||||
}
|
||||
|
||||
count = (recv_data.size() == 24) ? 2 : 1;
|
||||
|
||||
uint32 position[2];
|
||||
uint32 data[2];
|
||||
bool move_command = false;
|
||||
|
||||
for (uint8 i = 0; i < count; ++i)
|
||||
{
|
||||
recv_data >> position[i];
|
||||
recv_data >> data[i];
|
||||
|
||||
uint8 act_state = UNIT_ACTION_BUTTON_TYPE(data[i]);
|
||||
|
||||
//ignore invalid position
|
||||
if (position[i] >= MAX_UNIT_ACTION_BAR_INDEX)
|
||||
return;
|
||||
|
||||
// in the normal case, command and reaction buttons can only be moved, not removed
|
||||
// at moving count == 2, at removing count == 1
|
||||
// ignore attempt to remove command|reaction buttons (not possible at normal case)
|
||||
if (act_state == ACT_COMMAND || act_state == ACT_REACTION)
|
||||
{
|
||||
if (count == 1)
|
||||
return;
|
||||
|
||||
move_command = true;
|
||||
}
|
||||
}
|
||||
|
||||
// check swap (at command->spell swap client remove spell first in another packet, so check only command move correctness)
|
||||
if (move_command)
|
||||
{
|
||||
uint8 act_state_0 = UNIT_ACTION_BUTTON_TYPE(data[0]);
|
||||
if (act_state_0 == ACT_COMMAND || act_state_0 == ACT_REACTION)
|
||||
{
|
||||
uint32 spell_id_0 = UNIT_ACTION_BUTTON_ACTION(data[0]);
|
||||
UnitActionBarEntry const* actionEntry_1 = charmInfo->GetActionBarEntry(position[1]);
|
||||
if (!actionEntry_1 || spell_id_0 != actionEntry_1->GetAction() ||
|
||||
act_state_0 != actionEntry_1->GetType())
|
||||
return;
|
||||
}
|
||||
|
||||
uint8 act_state_1 = UNIT_ACTION_BUTTON_TYPE(data[1]);
|
||||
if (act_state_1 == ACT_COMMAND || act_state_1 == ACT_REACTION)
|
||||
{
|
||||
uint32 spell_id_1 = UNIT_ACTION_BUTTON_ACTION(data[1]);
|
||||
UnitActionBarEntry const* actionEntry_0 = charmInfo->GetActionBarEntry(position[0]);
|
||||
if (!actionEntry_0 || spell_id_1 != actionEntry_0->GetAction() ||
|
||||
act_state_1 != actionEntry_0->GetType())
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8 i = 0; i < count; ++i)
|
||||
{
|
||||
uint32 spell_id = UNIT_ACTION_BUTTON_ACTION(data[i]);
|
||||
uint8 act_state = UNIT_ACTION_BUTTON_TYPE(data[i]);
|
||||
|
||||
sLog->outDetail("Player %s has changed pet spell action. Position: %u, Spell: %u, State: 0x%X", _player->GetName(), position[i], spell_id, uint32(act_state));
|
||||
|
||||
//if it's act for spell (en/disable/cast) and there is a spell given (0 = remove spell) which pet doesn't know, don't add
|
||||
if (!((act_state == ACT_ENABLED || act_state == ACT_DISABLED || act_state == ACT_PASSIVE) && spell_id && !pet->HasSpell(spell_id)))
|
||||
{
|
||||
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell_id))
|
||||
{
|
||||
//sign for autocast
|
||||
if (act_state == ACT_ENABLED)
|
||||
{
|
||||
if (pet->GetTypeId() == TYPEID_UNIT && pet->ToCreature()->isPet())
|
||||
((Pet*)pet)->ToggleAutocast(spellInfo, true);
|
||||
else
|
||||
for (Unit::ControlList::iterator itr = GetPlayer()->m_Controlled.begin(); itr != GetPlayer()->m_Controlled.end(); ++itr)
|
||||
if ((*itr)->GetEntry() == pet->GetEntry())
|
||||
(*itr)->GetCharmInfo()->ToggleCreatureAutocast(spellInfo, true);
|
||||
}
|
||||
//sign for no/turn off autocast
|
||||
else if (act_state == ACT_DISABLED)
|
||||
{
|
||||
if (pet->GetTypeId() == TYPEID_UNIT && pet->ToCreature()->isPet())
|
||||
((Pet*)pet)->ToggleAutocast(spellInfo, false);
|
||||
else
|
||||
for (Unit::ControlList::iterator itr = GetPlayer()->m_Controlled.begin(); itr != GetPlayer()->m_Controlled.end(); ++itr)
|
||||
if ((*itr)->GetEntry() == pet->GetEntry())
|
||||
(*itr)->GetCharmInfo()->ToggleCreatureAutocast(spellInfo, false);
|
||||
}
|
||||
}
|
||||
|
||||
charmInfo->SetActionBar(position[i], spell_id, ActiveStates(act_state));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandlePetRename(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDetail("HandlePetRename. CMSG_PET_RENAME");
|
||||
|
||||
uint64 petguid;
|
||||
uint8 isdeclined;
|
||||
|
||||
std::string name;
|
||||
DeclinedName declinedname;
|
||||
|
||||
recv_data >> petguid;
|
||||
recv_data >> name;
|
||||
recv_data >> isdeclined;
|
||||
|
||||
Pet* pet = ObjectAccessor::FindPet(petguid);
|
||||
// check it!
|
||||
if (!pet || !pet->isPet() || ((Pet*)pet)->getPetType()!= HUNTER_PET ||
|
||||
!pet->HasByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED) ||
|
||||
pet->GetOwnerGUID() != _player->GetGUID() || !pet->GetCharmInfo())
|
||||
return;
|
||||
|
||||
PetNameInvalidReason res = ObjectMgr::CheckPetName(name);
|
||||
if (res != PET_NAME_SUCCESS)
|
||||
{
|
||||
SendPetNameInvalid(res, name, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sObjectMgr->IsReservedName(name))
|
||||
{
|
||||
SendPetNameInvalid(PET_NAME_RESERVED, name, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
pet->SetName(name);
|
||||
|
||||
Unit* owner = pet->GetOwner();
|
||||
if (owner && (owner->GetTypeId() == TYPEID_PLAYER) && owner->ToPlayer()->GetGroup())
|
||||
owner->ToPlayer()->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_NAME);
|
||||
|
||||
pet->RemoveByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED);
|
||||
|
||||
if (isdeclined)
|
||||
{
|
||||
for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
|
||||
{
|
||||
recv_data >> declinedname.name[i];
|
||||
}
|
||||
|
||||
std::wstring wname;
|
||||
Utf8toWStr(name, wname);
|
||||
if (!ObjectMgr::CheckDeclinedNames(wname, declinedname))
|
||||
{
|
||||
SendPetNameInvalid(PET_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME, name, &declinedname);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
if (isdeclined)
|
||||
{
|
||||
for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
|
||||
CharacterDatabase.EscapeString(declinedname.name[i]);
|
||||
trans->PAppend("DELETE FROM character_pet_declinedname WHERE owner = '%u' AND id = '%u'", _player->GetGUIDLow(), pet->GetCharmInfo()->GetPetNumber());
|
||||
trans->PAppend("INSERT INTO character_pet_declinedname (id, owner, genitive, dative, accusative, instrumental, prepositional) VALUES ('%u', '%u', '%s', '%s', '%s', '%s', '%s')",
|
||||
pet->GetCharmInfo()->GetPetNumber(), _player->GetGUIDLow(), declinedname.name[0].c_str(), declinedname.name[1].c_str(), declinedname.name[2].c_str(), declinedname.name[3].c_str(), declinedname.name[4].c_str());
|
||||
}
|
||||
|
||||
CharacterDatabase.EscapeString(name);
|
||||
trans->PAppend("UPDATE character_pet SET name = '%s', renamed = '1' WHERE owner = '%u' AND id = '%u'", name.c_str(), _player->GetGUIDLow(), pet->GetCharmInfo()->GetPetNumber());
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
|
||||
pet->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, uint32(time(NULL))); // cast can't be helped
|
||||
}
|
||||
|
||||
void WorldSession::HandlePetAbandon(WorldPacket & recv_data)
|
||||
{
|
||||
uint64 guid;
|
||||
recv_data >> guid; //pet guid
|
||||
sLog->outDetail("HandlePetAbandon. CMSG_PET_ABANDON pet guid is %u", GUID_LOPART(guid));
|
||||
|
||||
if (!_player->IsInWorld())
|
||||
return;
|
||||
|
||||
// pet/charmed
|
||||
Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid);
|
||||
if (pet)
|
||||
{
|
||||
if (pet->isPet())
|
||||
{
|
||||
if (pet->GetGUID() == _player->GetPetGUID())
|
||||
{
|
||||
uint32 feelty = pet->GetPower(POWER_HAPPINESS);
|
||||
pet->SetPower(POWER_HAPPINESS, feelty > 50000 ? (feelty-50000) : 0);
|
||||
}
|
||||
|
||||
_player->RemovePet((Pet*)pet, PET_SAVE_AS_DELETED);
|
||||
}
|
||||
else if (pet->GetGUID() == _player->GetCharmGUID())
|
||||
_player->StopCastingCharm();
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandlePetSpellAutocastOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDetail("CMSG_PET_SPELL_AUTOCAST");
|
||||
uint64 guid;
|
||||
uint32 spellid;
|
||||
uint8 state; //1 for on, 0 for off
|
||||
recvPacket >> guid >> spellid >> state;
|
||||
|
||||
if (!_player->GetGuardianPet() && !_player->GetCharm())
|
||||
return;
|
||||
|
||||
if (ObjectAccessor::FindPlayer(guid))
|
||||
return;
|
||||
|
||||
Creature* pet=ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid);
|
||||
|
||||
if (!pet || (pet != _player->GetGuardianPet() && pet != _player->GetCharm()))
|
||||
{
|
||||
sLog->outError("HandlePetSpellAutocastOpcode.Pet %u isn't pet of player %s .", uint32(GUID_LOPART(guid)), GetPlayer()->GetName());
|
||||
return;
|
||||
}
|
||||
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellid);
|
||||
// do not add not learned spells/ passive spells
|
||||
if (!pet->HasSpell(spellid) || spellInfo->IsAutocastable())
|
||||
return;
|
||||
|
||||
CharmInfo* charmInfo = pet->GetCharmInfo();
|
||||
if (!charmInfo)
|
||||
{
|
||||
sLog->outError("WorldSession::HandlePetSpellAutocastOpcod: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet->GetGUIDLow(), pet->GetTypeId());
|
||||
return;
|
||||
}
|
||||
|
||||
if (pet->isPet())
|
||||
((Pet*)pet)->ToggleAutocast(spellInfo, state);
|
||||
else
|
||||
pet->GetCharmInfo()->ToggleCreatureAutocast(spellInfo, state);
|
||||
|
||||
charmInfo->SetSpellAutocast(spellInfo, state);
|
||||
}
|
||||
|
||||
void WorldSession::HandlePetCastSpellOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDetail("WORLD: CMSG_PET_CAST_SPELL");
|
||||
|
||||
uint64 guid;
|
||||
uint8 castCount;
|
||||
uint32 spellId;
|
||||
uint8 castFlags;
|
||||
|
||||
recvPacket >> guid >> castCount >> spellId >> castFlags;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_PET_CAST_SPELL, guid: " UI64FMTD ", castCount: %u, spellId %u, castFlags %u", guid, castCount, spellId, castFlags);
|
||||
|
||||
// This opcode is also sent from charmed and possessed units (players and creatures)
|
||||
if (!_player->GetGuardianPet() && !_player->GetCharm())
|
||||
return;
|
||||
|
||||
Unit* caster = ObjectAccessor::GetUnit(*_player, guid);
|
||||
|
||||
if (!caster || (caster != _player->GetGuardianPet() && caster != _player->GetCharm()))
|
||||
{
|
||||
sLog->outError("HandlePetCastSpellOpcode: Pet %u isn't pet of player %s .", uint32(GUID_LOPART(guid)), GetPlayer()->GetName());
|
||||
return;
|
||||
}
|
||||
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||
if (!spellInfo)
|
||||
{
|
||||
sLog->outError("WORLD: unknown PET spell id %i", spellId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (spellInfo->StartRecoveryCategory > 0) // Check if spell is affected by GCD
|
||||
if (caster->GetTypeId() == TYPEID_UNIT && caster->GetCharmInfo() && caster->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo))
|
||||
{
|
||||
caster->SendPetCastFail(spellId, SPELL_FAILED_NOT_READY);
|
||||
return;
|
||||
}
|
||||
|
||||
// do not cast not learned spells
|
||||
if (!caster->HasSpell(spellId) || spellInfo->IsPassive())
|
||||
return;
|
||||
|
||||
SpellCastTargets targets;
|
||||
targets.Read(recvPacket, caster);
|
||||
HandleClientCastFlags(recvPacket, castFlags, targets);
|
||||
|
||||
caster->ClearUnitState(UNIT_STAT_FOLLOW);
|
||||
|
||||
Spell* spell = new Spell(caster, spellInfo, TRIGGERED_NONE);
|
||||
spell->m_cast_count = castCount; // probably pending spell cast
|
||||
spell->m_targets = targets;
|
||||
|
||||
// TODO: need to check victim?
|
||||
SpellCastResult result;
|
||||
if (caster->m_movedPlayer)
|
||||
result = spell->CheckPetCast(caster->m_movedPlayer->GetSelectedUnit());
|
||||
else
|
||||
result = spell->CheckPetCast(NULL);
|
||||
if (result == SPELL_CAST_OK)
|
||||
{
|
||||
if (caster->GetTypeId() == TYPEID_UNIT)
|
||||
{
|
||||
Creature* pet = caster->ToCreature();
|
||||
pet->AddCreatureSpellCooldown(spellId);
|
||||
if (pet->isPet())
|
||||
{
|
||||
Pet* p = (Pet*)pet;
|
||||
// 10% chance to play special pet attack talk, else growl
|
||||
// actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
|
||||
if (p->getPetType() == SUMMON_PET && (urand(0, 100) < 10))
|
||||
pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL);
|
||||
else
|
||||
pet->SendPetAIReaction(guid);
|
||||
}
|
||||
}
|
||||
|
||||
spell->prepare(&(spell->m_targets));
|
||||
}
|
||||
else
|
||||
{
|
||||
caster->SendPetCastFail(spellId, result);
|
||||
if (caster->GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
if (!caster->ToPlayer()->HasSpellCooldown(spellId))
|
||||
GetPlayer()->SendClearCooldown(spellId, caster);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!caster->ToCreature()->HasSpellCooldown(spellId))
|
||||
GetPlayer()->SendClearCooldown(spellId, caster);
|
||||
}
|
||||
|
||||
spell->finish(false);
|
||||
delete spell;
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::SendPetNameInvalid(uint32 error, const std::string& name, DeclinedName *declinedName)
|
||||
{
|
||||
WorldPacket data(SMSG_PET_NAME_INVALID, 4 + name.size() + 1 + 1);
|
||||
data << uint32(error);
|
||||
data << name;
|
||||
if (declinedName)
|
||||
{
|
||||
data << uint8(1);
|
||||
for (uint32 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
|
||||
data << declinedName->name[i];
|
||||
}
|
||||
else
|
||||
data << uint8(0);
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandlePetLearnTalent(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_PET_LEARN_TALENT");
|
||||
|
||||
uint64 guid;
|
||||
uint32 talent_id, requested_rank;
|
||||
recv_data >> guid >> talent_id >> requested_rank;
|
||||
|
||||
_player->LearnPetTalent(guid, talent_id, requested_rank);
|
||||
_player->SendTalentsInfoData(true);
|
||||
}
|
||||
|
||||
void WorldSession::HandleLearnPreviewTalentsPet(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LEARN_PREVIEW_TALENTS_PET");
|
||||
|
||||
uint64 guid;
|
||||
recv_data >> guid;
|
||||
|
||||
uint32 talentsCount;
|
||||
recv_data >> talentsCount;
|
||||
|
||||
uint32 talentId, talentRank;
|
||||
|
||||
for (uint32 i = 0; i < talentsCount; ++i)
|
||||
{
|
||||
recv_data >> talentId >> talentRank;
|
||||
|
||||
_player->LearnPetTalent(guid, talentId, talentRank);
|
||||
}
|
||||
|
||||
_player->SendTalentsInfoData(true);
|
||||
}
|
||||
+937
@@ -0,0 +1,937 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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 "Common.h"
|
||||
#include "Language.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
#include "World.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "ArenaTeamMgr.h"
|
||||
#include "GuildMgr.h"
|
||||
#include "Log.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Guild.h"
|
||||
#include "ArenaTeam.h"
|
||||
#include "GossipDef.h"
|
||||
#include "SocialMgr.h"
|
||||
|
||||
#define CHARTER_DISPLAY_ID 16161
|
||||
|
||||
/*enum PetitionType // dbc data
|
||||
{
|
||||
PETITION_TYPE_GUILD = 1,
|
||||
PETITION_TYPE_ARENA_TEAM = 3
|
||||
};*/
|
||||
|
||||
// Charters ID in item_template
|
||||
enum CharterItemIDs
|
||||
{
|
||||
GUILD_CHARTER = 5863,
|
||||
ARENA_TEAM_CHARTER_2v2 = 23560,
|
||||
ARENA_TEAM_CHARTER_3v3 = 23561,
|
||||
ARENA_TEAM_CHARTER_5v5 = 23562
|
||||
};
|
||||
|
||||
enum CharterCosts
|
||||
{
|
||||
GUILD_CHARTER_COST = 1000,
|
||||
ARENA_TEAM_CHARTER_2v2_COST = 800000,
|
||||
ARENA_TEAM_CHARTER_3v3_COST = 1200000,
|
||||
ARENA_TEAM_CHARTER_5v5_COST = 2000000
|
||||
};
|
||||
|
||||
void WorldSession::HandlePetitionBuyOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Received opcode CMSG_PETITION_BUY");
|
||||
|
||||
uint64 guidNPC;
|
||||
uint32 clientIndex; // 1 for guild and arenaslot+1 for arenas in client
|
||||
std::string name;
|
||||
|
||||
recv_data >> guidNPC; // NPC GUID
|
||||
recv_data.read_skip<uint32>(); // 0
|
||||
recv_data.read_skip<uint64>(); // 0
|
||||
recv_data >> name; // name
|
||||
recv_data.read_skip<std::string>(); // some string
|
||||
recv_data.read_skip<uint32>(); // 0
|
||||
recv_data.read_skip<uint32>(); // 0
|
||||
recv_data.read_skip<uint32>(); // 0
|
||||
recv_data.read_skip<uint32>(); // 0
|
||||
recv_data.read_skip<uint32>(); // 0
|
||||
recv_data.read_skip<uint32>(); // 0
|
||||
recv_data.read_skip<uint32>(); // 0
|
||||
recv_data.read_skip<uint16>(); // 0
|
||||
recv_data.read_skip<uint32>(); // 0
|
||||
recv_data.read_skip<uint32>(); // 0
|
||||
recv_data.read_skip<uint32>(); // 0
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
recv_data.read_skip<std::string>();
|
||||
|
||||
recv_data >> clientIndex; // index
|
||||
recv_data.read_skip<uint32>(); // 0
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Petitioner with GUID %u tried sell petition: name %s", GUID_LOPART(guidNPC), name.c_str());
|
||||
|
||||
// prevent cheating
|
||||
Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(guidNPC, UNIT_NPC_FLAG_PETITIONER);
|
||||
if (!creature)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandlePetitionBuyOpcode - Unit (GUID: %u) not found or you can't interact with him.", GUID_LOPART(guidNPC));
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
uint32 charterid = 0;
|
||||
uint32 cost = 0;
|
||||
uint32 type = 0;
|
||||
if (creature->isTabardDesigner())
|
||||
{
|
||||
// if tabard designer, then trying to buy a guild charter.
|
||||
// do not let if already in guild.
|
||||
if (_player->GetGuildId())
|
||||
return;
|
||||
|
||||
charterid = GUILD_CHARTER;
|
||||
cost = GUILD_CHARTER_COST;
|
||||
type = GUILD_CHARTER_TYPE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: find correct opcode
|
||||
if (_player->getLevel() < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
|
||||
{
|
||||
SendNotification(LANG_ARENA_ONE_TOOLOW, sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (clientIndex) // arenaSlot+1 as received from client (1 from 3 case)
|
||||
{
|
||||
case 1:
|
||||
charterid = ARENA_TEAM_CHARTER_2v2;
|
||||
cost = ARENA_TEAM_CHARTER_2v2_COST;
|
||||
type = ARENA_TEAM_CHARTER_2v2_TYPE;
|
||||
break;
|
||||
case 2:
|
||||
charterid = ARENA_TEAM_CHARTER_3v3;
|
||||
cost = ARENA_TEAM_CHARTER_3v3_COST;
|
||||
type = ARENA_TEAM_CHARTER_3v3_TYPE;
|
||||
break;
|
||||
case 3:
|
||||
charterid = ARENA_TEAM_CHARTER_5v5;
|
||||
cost = ARENA_TEAM_CHARTER_5v5_COST;
|
||||
type = ARENA_TEAM_CHARTER_5v5_TYPE;
|
||||
break;
|
||||
default:
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "unknown selection at buy arena petition: %u", clientIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_player->GetArenaTeamId(clientIndex - 1)) // arenaSlot+1 as received from client
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ALREADY_IN_ARENA_TEAM);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (type == GUILD_CHARTER_TYPE)
|
||||
{
|
||||
if (sGuildMgr->GetGuildByName(name))
|
||||
{
|
||||
Guild::SendCommandResult(this, GUILD_CREATE_S, ERR_GUILD_NAME_EXISTS_S, name);
|
||||
return;
|
||||
}
|
||||
if (sObjectMgr->IsReservedName(name) || !ObjectMgr::IsValidCharterName(name))
|
||||
{
|
||||
Guild::SendCommandResult(this, GUILD_CREATE_S, ERR_GUILD_NAME_INVALID, name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sArenaTeamMgr->GetArenaTeamByName(name))
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_EXISTS_S);
|
||||
return;
|
||||
}
|
||||
if (sObjectMgr->IsReservedName(name) || !ObjectMgr::IsValidCharterName(name))
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_INVALID);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(charterid);
|
||||
if (!pProto)
|
||||
{
|
||||
_player->SendBuyError(BUY_ERR_CANT_FIND_ITEM, NULL, charterid, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_player->HasEnoughMoney(cost))
|
||||
{ //player hasn't got enough money
|
||||
_player->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, creature, charterid, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
ItemPosCountVec dest;
|
||||
InventoryResult msg = _player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, charterid, pProto->BuyCount);
|
||||
if (msg != EQUIP_ERR_OK)
|
||||
{
|
||||
_player->SendEquipError(msg, NULL, NULL, charterid);
|
||||
return;
|
||||
}
|
||||
|
||||
_player->ModifyMoney(-(int32)cost);
|
||||
Item* charter = _player->StoreNewItem(dest, charterid, true);
|
||||
if (!charter)
|
||||
return;
|
||||
|
||||
charter->SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1, charter->GetGUIDLow());
|
||||
// ITEM_FIELD_ENCHANTMENT_1_1 is guild/arenateam id
|
||||
// ITEM_FIELD_ENCHANTMENT_1_1+1 is current signatures count (showed on item)
|
||||
charter->SetState(ITEM_CHANGED, _player);
|
||||
_player->SendNewItem(charter, 1, true, false);
|
||||
|
||||
// a petition is invalid, if both the owner and the type matches
|
||||
// we checked above, if this player is in an arenateam, so this must be
|
||||
// datacorruption
|
||||
QueryResult result = CharacterDatabase.PQuery("SELECT petitionguid FROM petition WHERE ownerguid = '%u' AND type = '%u'", _player->GetGUIDLow(), type);
|
||||
|
||||
std::ostringstream ssInvalidPetitionGUIDs;
|
||||
|
||||
if (result)
|
||||
{
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
ssInvalidPetitionGUIDs << '\'' << fields[0].GetUInt32() << "', ";
|
||||
} while (result->NextRow());
|
||||
}
|
||||
|
||||
// delete petitions with the same guid as this one
|
||||
ssInvalidPetitionGUIDs << '\'' << charter->GetGUIDLow() << '\'';
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Invalid petition GUIDs: %s", ssInvalidPetitionGUIDs.str().c_str());
|
||||
CharacterDatabase.EscapeString(name);
|
||||
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
trans->PAppend("DELETE FROM petition WHERE petitionguid IN (%s)", ssInvalidPetitionGUIDs.str().c_str());
|
||||
trans->PAppend("DELETE FROM petition_sign WHERE petitionguid IN (%s)", ssInvalidPetitionGUIDs.str().c_str());
|
||||
trans->PAppend("INSERT INTO petition (ownerguid, petitionguid, name, type) VALUES ('%u', '%u', '%s', '%u')",
|
||||
_player->GetGUIDLow(), charter->GetGUIDLow(), name.c_str(), type);
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
}
|
||||
|
||||
void WorldSession::HandlePetitionShowSignOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
// ok
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Received opcode CMSG_PETITION_SHOW_SIGNATURES");
|
||||
|
||||
uint8 signs = 0;
|
||||
uint64 petitionguid;
|
||||
recv_data >> petitionguid; // petition guid
|
||||
|
||||
// solve (possible) some strange compile problems with explicit use GUID_LOPART(petitionguid) at some GCC versions (wrong code optimization in compiler?)
|
||||
uint32 petitionguid_low = GUID_LOPART(petitionguid);
|
||||
|
||||
QueryResult result = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", petitionguid_low);
|
||||
if (!result)
|
||||
{
|
||||
sLog->outError("Petition %u is not found for player %u %s", GUID_LOPART(petitionguid), GetPlayer()->GetGUIDLow(), GetPlayer()->GetName());
|
||||
return;
|
||||
}
|
||||
Field* fields = result->Fetch();
|
||||
uint32 type = fields[0].GetUInt8();
|
||||
|
||||
// if guild petition and has guild => error, return;
|
||||
if (type == GUILD_CHARTER_TYPE && _player->GetGuildId())
|
||||
return;
|
||||
|
||||
result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE petitionguid = '%u'", petitionguid_low);
|
||||
|
||||
// result == NULL also correct in case no sign yet
|
||||
if (result)
|
||||
signs = uint8(result->GetRowCount());
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_PETITION_SHOW_SIGNATURES petition entry: '%u'", petitionguid_low);
|
||||
|
||||
WorldPacket data(SMSG_PETITION_SHOW_SIGNATURES, (8+8+4+1+signs*12));
|
||||
data << uint64(petitionguid); // petition guid
|
||||
data << uint64(_player->GetGUID()); // owner guid
|
||||
data << uint32(petitionguid_low); // guild guid
|
||||
data << uint8(signs); // sign's count
|
||||
|
||||
for (uint8 i = 1; i <= signs; ++i)
|
||||
{
|
||||
Field* fields2 = result->Fetch();
|
||||
uint64 plguid = fields2[0].GetUInt64();
|
||||
|
||||
data << uint64(plguid); // Player GUID
|
||||
data << uint32(0); // there 0 ...
|
||||
|
||||
result->NextRow();
|
||||
}
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandlePetitionQueryOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Received opcode CMSG_PETITION_QUERY"); // ok
|
||||
|
||||
uint32 guildguid;
|
||||
uint64 petitionguid;
|
||||
recv_data >> guildguid; // in Trinity always same as GUID_LOPART(petitionguid)
|
||||
recv_data >> petitionguid; // petition guid
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_PETITION_QUERY Petition GUID %u Guild GUID %u", GUID_LOPART(petitionguid), guildguid);
|
||||
|
||||
SendPetitionQueryOpcode(petitionguid);
|
||||
}
|
||||
|
||||
void WorldSession::SendPetitionQueryOpcode(uint64 petitionguid)
|
||||
{
|
||||
uint64 ownerguid = 0;
|
||||
uint32 type;
|
||||
std::string name = "NO_NAME_FOR_GUID";
|
||||
|
||||
// TODO: Use CHAR_LOAD_PETITION PS
|
||||
QueryResult result = CharacterDatabase.PQuery("SELECT ownerguid, name, type "
|
||||
"FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
|
||||
|
||||
if (result)
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
ownerguid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
|
||||
name = fields[1].GetString();
|
||||
type = fields[2].GetUInt32();
|
||||
}
|
||||
else
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_PETITION_QUERY failed for petition (GUID: %u)", GUID_LOPART(petitionguid));
|
||||
return;
|
||||
}
|
||||
|
||||
WorldPacket data(SMSG_PETITION_QUERY_RESPONSE, (4+8+name.size()+1+1+4*12+2+10));
|
||||
data << uint32(GUID_LOPART(petitionguid)); // guild/team guid (in Trinity always same as GUID_LOPART(petition guid)
|
||||
data << uint64(ownerguid); // charter owner guid
|
||||
data << name; // name (guild/arena team)
|
||||
data << uint8(0); // some string
|
||||
if (type == GUILD_CHARTER_TYPE)
|
||||
{
|
||||
data << uint32(9);
|
||||
data << uint32(9);
|
||||
data << uint32(0); // bypass client - side limitation, a different value is needed here for each petition
|
||||
}
|
||||
else
|
||||
{
|
||||
data << uint32(type-1);
|
||||
data << uint32(type-1);
|
||||
data << uint32(type); // bypass client - side limitation, a different value is needed here for each petition
|
||||
}
|
||||
data << uint32(0); // 5
|
||||
data << uint32(0); // 6
|
||||
data << uint32(0); // 7
|
||||
data << uint32(0); // 8
|
||||
data << uint16(0); // 9 2 bytes field
|
||||
data << uint32(0); // 10
|
||||
data << uint32(0); // 11
|
||||
data << uint32(0); // 13 count of next strings?
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
data << uint8(0); // some string
|
||||
|
||||
data << uint32(0); // 14
|
||||
|
||||
if (type == GUILD_CHARTER_TYPE)
|
||||
data << uint32(0); // 15 0 - guild, 1 - arena team
|
||||
else
|
||||
data << uint32(1);
|
||||
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandlePetitionRenameOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Received opcode MSG_PETITION_RENAME"); // ok
|
||||
|
||||
uint64 petitionGuid;
|
||||
uint32 type;
|
||||
std::string newName;
|
||||
|
||||
recv_data >> petitionGuid; // guid
|
||||
recv_data >> newName; // new name
|
||||
|
||||
Item* item = _player->GetItemByGuid(petitionGuid);
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
QueryResult result = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionGuid));
|
||||
|
||||
if (result)
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
type = fields[0].GetUInt8();
|
||||
}
|
||||
else
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_PETITION_QUERY failed for petition (GUID: %u)", GUID_LOPART(petitionGuid));
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == GUILD_CHARTER_TYPE)
|
||||
{
|
||||
if (sGuildMgr->GetGuildByName(newName))
|
||||
{
|
||||
Guild::SendCommandResult(this, GUILD_CREATE_S, ERR_GUILD_NAME_EXISTS_S, newName);
|
||||
return;
|
||||
}
|
||||
if (sObjectMgr->IsReservedName(newName) || !ObjectMgr::IsValidCharterName(newName))
|
||||
{
|
||||
Guild::SendCommandResult(this, GUILD_CREATE_S, ERR_GUILD_NAME_INVALID, newName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sArenaTeamMgr->GetArenaTeamByName(newName))
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, newName, "", ERR_ARENA_TEAM_NAME_EXISTS_S);
|
||||
return;
|
||||
}
|
||||
if (sObjectMgr->IsReservedName(newName) || !ObjectMgr::IsValidCharterName(newName))
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, newName, "", ERR_ARENA_TEAM_NAME_INVALID);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_PETITION_NAME);
|
||||
|
||||
stmt->setString(0, newName);
|
||||
stmt->setUInt32(1, GUID_LOPART(petitionGuid));
|
||||
|
||||
CharacterDatabase.Execute(stmt);
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Petition (GUID: %u) renamed to '%s'", GUID_LOPART(petitionGuid), newName.c_str());
|
||||
WorldPacket data(MSG_PETITION_RENAME, (8+newName.size()+1));
|
||||
data << uint64(petitionGuid);
|
||||
data << newName;
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandlePetitionSignOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Received opcode CMSG_PETITION_SIGN"); // ok
|
||||
|
||||
Field* fields;
|
||||
uint64 petitionGuid;
|
||||
uint8 unk;
|
||||
recv_data >> petitionGuid; // petition guid
|
||||
recv_data >> unk;
|
||||
|
||||
QueryResult result = CharacterDatabase.PQuery(
|
||||
"SELECT ownerguid, "
|
||||
" (SELECT COUNT(playerguid) FROM petition_sign WHERE petition_sign.petitionguid = '%u') AS signs, "
|
||||
" type "
|
||||
"FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionGuid), GUID_LOPART(petitionGuid));
|
||||
|
||||
if (!result)
|
||||
{
|
||||
sLog->outError("Petition %u is not found for player %u %s", GUID_LOPART(petitionGuid), GetPlayer()->GetGUIDLow(), GetPlayer()->GetName());
|
||||
return;
|
||||
}
|
||||
|
||||
fields = result->Fetch();
|
||||
uint64 ownerGuid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
|
||||
uint8 signs = fields[1].GetUInt8();
|
||||
uint32 type = fields[2].GetUInt32();
|
||||
|
||||
uint32 playerGuid = _player->GetGUIDLow();
|
||||
if (GUID_LOPART(ownerGuid) == playerGuid)
|
||||
return;
|
||||
|
||||
// not let enemies sign guild charter
|
||||
if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && GetPlayer()->GetTeam() != sObjectMgr->GetPlayerTeamByGUID(ownerGuid))
|
||||
{
|
||||
if (type != GUILD_CHARTER_TYPE)
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED);
|
||||
else
|
||||
Guild::SendCommandResult(this, GUILD_CREATE_S, ERR_GUILD_NOT_ALLIED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type != GUILD_CHARTER_TYPE)
|
||||
{
|
||||
if (_player->getLevel() < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", _player->GetName(), ERR_ARENA_TEAM_TARGET_TOO_LOW_S);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8 slot = ArenaTeam::GetSlotByType(type);
|
||||
if (slot >= MAX_ARENA_SLOT)
|
||||
return;
|
||||
|
||||
if (_player->GetArenaTeamId(slot))
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", _player->GetName(), ERR_ALREADY_IN_ARENA_TEAM_S);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_player->GetArenaTeamIdInvited())
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", _player->GetName(), ERR_ALREADY_INVITED_TO_ARENA_TEAM_S);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_player->GetGuildId())
|
||||
{
|
||||
Guild::SendCommandResult(this, GUILD_INVITE_S, ERR_ALREADY_IN_GUILD_S, _player->GetName());
|
||||
return;
|
||||
}
|
||||
if (_player->GetGuildIdInvited())
|
||||
{
|
||||
Guild::SendCommandResult(this, GUILD_INVITE_S, ERR_ALREADY_INVITED_TO_GUILD_S, _player->GetName());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (++signs > type) // client signs maximum
|
||||
return;
|
||||
|
||||
//client doesn't allow to sign petition two times by one character, but not check sign by another character from same account
|
||||
//not allow sign another player from already sign player account
|
||||
result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE player_account = '%u' AND petitionguid = '%u'", GetAccountId(), GUID_LOPART(petitionGuid));
|
||||
|
||||
if (result)
|
||||
{
|
||||
WorldPacket data(SMSG_PETITION_SIGN_RESULTS, (8+8+4));
|
||||
data << uint64(petitionGuid);
|
||||
data << uint64(_player->GetGUID());
|
||||
data << (uint32)PETITION_SIGN_ALREADY_SIGNED;
|
||||
|
||||
// close at signer side
|
||||
SendPacket(&data);
|
||||
|
||||
// update for owner if online
|
||||
if (Player* owner = ObjectAccessor::FindPlayer(ownerGuid))
|
||||
owner->GetSession()->SendPacket(&data);
|
||||
return;
|
||||
}
|
||||
|
||||
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PETITION_SIGNATURE);
|
||||
|
||||
stmt->setUInt32(0, GUID_LOPART(ownerGuid));
|
||||
stmt->setUInt32(1, GUID_LOPART(petitionGuid));
|
||||
stmt->setUInt32(2, playerGuid);
|
||||
stmt->setUInt32(3, GetAccountId());
|
||||
|
||||
CharacterDatabase.Execute(stmt);
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "PETITION SIGN: GUID %u by player: %s (GUID: %u Account: %u)", GUID_LOPART(petitionGuid), _player->GetName(), playerGuid, GetAccountId());
|
||||
|
||||
WorldPacket data(SMSG_PETITION_SIGN_RESULTS, (8+8+4));
|
||||
data << uint64(petitionGuid);
|
||||
data << uint64(_player->GetGUID());
|
||||
data << uint32(PETITION_SIGN_OK);
|
||||
|
||||
// close at signer side
|
||||
SendPacket(&data);
|
||||
|
||||
// update signs count on charter, required testing...
|
||||
//Item* item = _player->GetItemByGuid(petitionguid));
|
||||
//if (item)
|
||||
// item->SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1+1, signs);
|
||||
|
||||
// update for owner if online
|
||||
if (Player* owner = ObjectAccessor::FindPlayer(ownerGuid))
|
||||
owner->GetSession()->SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandlePetitionDeclineOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Received opcode MSG_PETITION_DECLINE"); // ok
|
||||
|
||||
uint64 petitionguid;
|
||||
uint64 ownerguid;
|
||||
recv_data >> petitionguid; // petition guid
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Petition %u declined by %u", GUID_LOPART(petitionguid), _player->GetGUIDLow());
|
||||
|
||||
QueryResult result = CharacterDatabase.PQuery("SELECT ownerguid FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
|
||||
if (!result)
|
||||
return;
|
||||
|
||||
Field* fields = result->Fetch();
|
||||
ownerguid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
|
||||
|
||||
Player* owner = ObjectAccessor::FindPlayer(ownerguid);
|
||||
if (owner) // petition owner online
|
||||
{
|
||||
WorldPacket data(MSG_PETITION_DECLINE, 8);
|
||||
data << uint64(_player->GetGUID());
|
||||
owner->GetSession()->SendPacket(&data);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleOfferPetitionOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Received opcode CMSG_OFFER_PETITION"); // ok
|
||||
|
||||
uint8 signs = 0;
|
||||
uint64 petitionguid, plguid;
|
||||
uint32 type, junk;
|
||||
Player* player;
|
||||
recv_data >> junk; // this is not petition type!
|
||||
recv_data >> petitionguid; // petition guid
|
||||
recv_data >> plguid; // player guid
|
||||
|
||||
player = ObjectAccessor::FindPlayer(plguid);
|
||||
if (!player)
|
||||
return;
|
||||
|
||||
QueryResult result = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
|
||||
if (!result)
|
||||
return;
|
||||
|
||||
Field* fields = result->Fetch();
|
||||
type = fields[0].GetUInt8();
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "OFFER PETITION: type %u, GUID1 %u, to player id: %u", type, GUID_LOPART(petitionguid), GUID_LOPART(plguid));
|
||||
|
||||
if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && GetPlayer()->GetTeam() != player->GetTeam())
|
||||
{
|
||||
if (type != GUILD_CHARTER_TYPE)
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED);
|
||||
else
|
||||
Guild::SendCommandResult(this, GUILD_CREATE_S, ERR_GUILD_NOT_ALLIED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type != GUILD_CHARTER_TYPE)
|
||||
{
|
||||
if (player->getLevel() < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
|
||||
{
|
||||
// player is too low level to join an arena team
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, player->GetName(), "", ERR_ARENA_TEAM_TARGET_TOO_LOW_S);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8 slot = ArenaTeam::GetSlotByType(type);
|
||||
if (slot >= MAX_ARENA_SLOT)
|
||||
return;
|
||||
|
||||
if (player->GetArenaTeamId(slot))
|
||||
{
|
||||
// player is already in an arena team
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, player->GetName(), "", ERR_ALREADY_IN_ARENA_TEAM_S);
|
||||
return;
|
||||
}
|
||||
|
||||
if (player->GetArenaTeamIdInvited())
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", _player->GetName(), ERR_ALREADY_INVITED_TO_ARENA_TEAM_S);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (player->GetGuildId())
|
||||
{
|
||||
Guild::SendCommandResult(this, GUILD_INVITE_S, ERR_ALREADY_IN_GUILD_S, _player->GetName());
|
||||
return;
|
||||
}
|
||||
|
||||
if (player->GetGuildIdInvited())
|
||||
{
|
||||
Guild::SendCommandResult(this, GUILD_INVITE_S, ERR_ALREADY_INVITED_TO_GUILD_S, _player->GetName());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
|
||||
// result == NULL also correct charter without signs
|
||||
if (result)
|
||||
signs = uint8(result->GetRowCount());
|
||||
|
||||
WorldPacket data(SMSG_PETITION_SHOW_SIGNATURES, (8+8+4+signs+signs*12));
|
||||
data << uint64(petitionguid); // petition guid
|
||||
data << uint64(_player->GetGUID()); // owner guid
|
||||
data << uint32(GUID_LOPART(petitionguid)); // guild guid
|
||||
data << uint8(signs); // sign's count
|
||||
|
||||
for (uint8 i = 1; i <= signs; ++i)
|
||||
{
|
||||
Field* fields2 = result->Fetch();
|
||||
plguid = fields2[0].GetUInt64();
|
||||
|
||||
data << uint64(plguid); // Player GUID
|
||||
data << uint32(0); // there 0 ...
|
||||
|
||||
result->NextRow();
|
||||
}
|
||||
|
||||
player->GetSession()->SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleTurnInPetitionOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Received opcode CMSG_TURN_IN_PETITION");
|
||||
|
||||
// Get petition guid from packet
|
||||
WorldPacket data;
|
||||
uint64 petitionGuid;
|
||||
|
||||
recv_data >> petitionGuid;
|
||||
|
||||
// Check if player really has the required petition charter
|
||||
Item* item = _player->GetItemByGuid(petitionGuid);
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Petition %u turned in by %u", GUID_LOPART(petitionGuid), _player->GetGUIDLow());
|
||||
|
||||
// Get petition data from db
|
||||
uint32 ownerguidlo;
|
||||
uint32 type;
|
||||
std::string name;
|
||||
|
||||
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PETITION);
|
||||
stmt->setUInt32(0, GUID_LOPART(petitionGuid));
|
||||
PreparedQueryResult result = CharacterDatabase.Query(stmt);
|
||||
|
||||
if (result)
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
ownerguidlo = fields[0].GetUInt32();
|
||||
name = fields[1].GetString();
|
||||
type = fields[2].GetUInt8();
|
||||
}
|
||||
else
|
||||
{
|
||||
sLog->outError("Player %s (guid: %u) tried to turn in petition (guid: %u) that is not present in the database", _player->GetName(), _player->GetGUIDLow(), GUID_LOPART(petitionGuid));
|
||||
return;
|
||||
}
|
||||
|
||||
// Only the petition owner can turn in the petition
|
||||
if (_player->GetGUIDLow() != ownerguidlo)
|
||||
return;
|
||||
|
||||
// Petition type (guild/arena) specific checks
|
||||
if (type == GUILD_CHARTER_TYPE)
|
||||
{
|
||||
// Check if player is already in a guild
|
||||
if (_player->GetGuildId())
|
||||
{
|
||||
data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4);
|
||||
data << (uint32)PETITION_TURN_ALREADY_IN_GUILD;
|
||||
_player->GetSession()->SendPacket(&data);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if guild name is already taken
|
||||
if (sGuildMgr->GetGuildByName(name))
|
||||
{
|
||||
Guild::SendCommandResult(this, GUILD_CREATE_S, ERR_GUILD_NAME_EXISTS_S, name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check for valid arena bracket (2v2, 3v3, 5v5)
|
||||
uint8 slot = ArenaTeam::GetSlotByType(type);
|
||||
if (slot >= MAX_ARENA_SLOT)
|
||||
return;
|
||||
|
||||
// Check if player is already in an arena team
|
||||
if (_player->GetArenaTeamId(slot))
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ALREADY_IN_ARENA_TEAM);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if arena team name is already taken
|
||||
if (sArenaTeamMgr->GetArenaTeamByName(name))
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_EXISTS_S);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Get petition signatures from db
|
||||
uint8 signatures;
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PETITION_SIGNATURE);
|
||||
stmt->setUInt32(0, GUID_LOPART(petitionGuid));
|
||||
result = CharacterDatabase.Query(stmt);
|
||||
|
||||
if (result)
|
||||
signatures = uint8(result->GetRowCount());
|
||||
else
|
||||
signatures = 0;
|
||||
|
||||
uint32 requiredSignatures;
|
||||
if (type == GUILD_CHARTER_TYPE)
|
||||
requiredSignatures = sWorld->getIntConfig(CONFIG_MIN_PETITION_SIGNS);
|
||||
else
|
||||
requiredSignatures = type-1;
|
||||
|
||||
// Notify player if signatures are missing
|
||||
if (signatures < requiredSignatures)
|
||||
{
|
||||
data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4);
|
||||
data << (uint32)PETITION_TURN_NEED_MORE_SIGNATURES;
|
||||
SendPacket(&data);
|
||||
return;
|
||||
}
|
||||
|
||||
// Proceed with guild/arena team creation
|
||||
|
||||
// Delete charter item
|
||||
_player->DestroyItem(item->GetBagSlot(), item->GetSlot(), true);
|
||||
|
||||
if (type == GUILD_CHARTER_TYPE)
|
||||
{
|
||||
// Create guild
|
||||
Guild* guild = new Guild;
|
||||
|
||||
if (!guild->Create(_player, name))
|
||||
{
|
||||
delete guild;
|
||||
return;
|
||||
}
|
||||
|
||||
// Register guild and add guild master
|
||||
sGuildMgr->AddGuild(guild);
|
||||
|
||||
// Add members from signatures
|
||||
for (uint8 i = 0; i < signatures; ++i)
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
guild->AddMember(MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER));
|
||||
result->NextRow();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Receive the rest of the packet in arena team creation case
|
||||
uint32 background, icon, iconcolor, border, bordercolor;
|
||||
recv_data >> background >> icon >> iconcolor >> border >> bordercolor;
|
||||
|
||||
// Create arena team
|
||||
ArenaTeam* arenaTeam = new ArenaTeam();
|
||||
|
||||
if (!arenaTeam->Create(_player->GetGUID(), type, name, background, icon, iconcolor, border, bordercolor))
|
||||
{
|
||||
delete arenaTeam;
|
||||
return;
|
||||
}
|
||||
|
||||
// Register arena team
|
||||
sArenaTeamMgr->AddArenaTeam(arenaTeam);
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "PetitonsHandler: Arena team (guid: %u) added to ObjectMgr", arenaTeam->GetId());
|
||||
|
||||
// Add members
|
||||
for (uint8 i = 0; i < signatures; ++i)
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
uint32 memberGUID = fields[0].GetUInt32();
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "PetitionsHandler: Adding arena team (guid: %u) member %u", arenaTeam->GetId(), memberGUID);
|
||||
arenaTeam->AddMember(MAKE_NEW_GUID(memberGUID, 0, HIGHGUID_PLAYER));
|
||||
result->NextRow();
|
||||
}
|
||||
}
|
||||
|
||||
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
trans->PAppend("DELETE FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionGuid));
|
||||
trans->PAppend("DELETE FROM petition_sign WHERE petitionguid = '%u'", GUID_LOPART(petitionGuid));
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
|
||||
// created
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "TURN IN PETITION GUID %u", GUID_LOPART(petitionGuid));
|
||||
|
||||
data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4);
|
||||
data << (uint32)PETITION_TURN_OK;
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandlePetitionShowListOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Received CMSG_PETITION_SHOWLIST");
|
||||
|
||||
uint64 guid;
|
||||
recv_data >> guid;
|
||||
|
||||
SendPetitionShowList(guid);
|
||||
}
|
||||
|
||||
void WorldSession::SendPetitionShowList(uint64 guid)
|
||||
{
|
||||
Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_PETITIONER);
|
||||
if (!creature)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandlePetitionShowListOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)));
|
||||
return;
|
||||
}
|
||||
|
||||
WorldPacket data(SMSG_PETITION_SHOWLIST, 8+1+4*6);
|
||||
data << guid; // npc guid
|
||||
|
||||
if (creature->isTabardDesigner())
|
||||
{
|
||||
data << uint8(1); // count
|
||||
data << uint32(1); // index
|
||||
data << uint32(GUILD_CHARTER); // charter entry
|
||||
data << uint32(CHARTER_DISPLAY_ID); // charter display id
|
||||
data << uint32(GUILD_CHARTER_COST); // charter cost
|
||||
data << uint32(0); // unknown
|
||||
data << uint32(9); // required signs?
|
||||
}
|
||||
else
|
||||
{
|
||||
data << uint8(3); // count
|
||||
// 2v2
|
||||
data << uint32(1); // index
|
||||
data << uint32(ARENA_TEAM_CHARTER_2v2); // charter entry
|
||||
data << uint32(CHARTER_DISPLAY_ID); // charter display id
|
||||
data << uint32(ARENA_TEAM_CHARTER_2v2_COST); // charter cost
|
||||
data << uint32(2); // unknown
|
||||
data << uint32(2); // required signs?
|
||||
// 3v3
|
||||
data << uint32(2); // index
|
||||
data << uint32(ARENA_TEAM_CHARTER_3v3); // charter entry
|
||||
data << uint32(CHARTER_DISPLAY_ID); // charter display id
|
||||
data << uint32(ARENA_TEAM_CHARTER_3v3_COST); // charter cost
|
||||
data << uint32(3); // unknown
|
||||
data << uint32(3); // required signs?
|
||||
// 5v5
|
||||
data << uint32(3); // index
|
||||
data << uint32(ARENA_TEAM_CHARTER_5v5); // charter entry
|
||||
data << uint32(CHARTER_DISPLAY_ID); // charter display id
|
||||
data << uint32(ARENA_TEAM_CHARTER_5v5_COST); // charter cost
|
||||
data << uint32(5); // unknown
|
||||
data << uint32(5); // required signs?
|
||||
}
|
||||
|
||||
SendPacket(&data);
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "Sent SMSG_PETITION_SHOWLIST");
|
||||
}
|
||||
Executable
+477
@@ -0,0 +1,477 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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 "Common.h"
|
||||
#include "Language.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Log.h"
|
||||
#include "World.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Player.h"
|
||||
#include "UpdateMask.h"
|
||||
#include "NPCHandler.h"
|
||||
#include "Pet.h"
|
||||
#include "MapManager.h"
|
||||
|
||||
void WorldSession::SendNameQueryOpcode(uint64 guid)
|
||||
{
|
||||
Player* player = NULL;
|
||||
const CharacterNameData* nameData = sWorld->GetCharacterNameData(GUID_LOPART(guid));
|
||||
if (nameData)
|
||||
player = ObjectAccessor::FindPlayer(guid);
|
||||
|
||||
// guess size
|
||||
WorldPacket data(SMSG_NAME_QUERY_RESPONSE, (8+1+1+1+1+1+10));
|
||||
data.appendPackGUID(guid);
|
||||
data << uint8(0); // added in 3.1
|
||||
if (nameData)
|
||||
{
|
||||
data << nameData->m_name; // played name
|
||||
data << uint8(0); // realm name for cross realm BG usage
|
||||
data << uint8(nameData->m_race);
|
||||
data << uint8(nameData->m_gender);
|
||||
data << uint8(nameData->m_class);
|
||||
}
|
||||
else
|
||||
{
|
||||
data << std::string(GetTrinityString(LANG_NON_EXIST_CHARACTER));
|
||||
data << uint32(0);
|
||||
}
|
||||
|
||||
if (player)
|
||||
{
|
||||
if (DeclinedName const* names = player->GetDeclinedNames())
|
||||
{
|
||||
data << uint8(1); // is declined
|
||||
for (int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
|
||||
data << names->name[i];
|
||||
}
|
||||
else
|
||||
data << uint8(0); // is not declined
|
||||
}
|
||||
else //TODO: decline names may also need to be stored in char name data
|
||||
data << uint8(0);
|
||||
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleNameQueryOpcode(WorldPacket& recv_data)
|
||||
{
|
||||
uint64 guid;
|
||||
|
||||
recv_data >> guid;
|
||||
|
||||
// This is disable by default to prevent lots of console spam
|
||||
// sLog->outString("HandleNameQueryOpcode %u", guid);
|
||||
|
||||
SendNameQueryOpcode(guid);
|
||||
}
|
||||
|
||||
void WorldSession::HandleQueryTimeOpcode(WorldPacket & /*recv_data*/)
|
||||
{
|
||||
SendQueryTimeResponse();
|
||||
}
|
||||
|
||||
void WorldSession::SendQueryTimeResponse()
|
||||
{
|
||||
WorldPacket data(SMSG_QUERY_TIME_RESPONSE, 4+4);
|
||||
data << uint32(time(NULL));
|
||||
data << uint32(sWorld->GetNextDailyQuestsResetTime() - time(NULL));
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
/// Only _static_ data is sent in this packet !!!
|
||||
void WorldSession::HandleCreatureQueryOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
uint32 entry;
|
||||
recv_data >> entry;
|
||||
uint64 guid;
|
||||
recv_data >> guid;
|
||||
|
||||
CreatureTemplate const* ci = sObjectMgr->GetCreatureTemplate(entry);
|
||||
if (ci)
|
||||
{
|
||||
|
||||
std::string Name, SubName;
|
||||
Name = ci->Name;
|
||||
SubName = ci->SubName;
|
||||
|
||||
int loc_idx = GetSessionDbLocaleIndex();
|
||||
if (loc_idx >= 0)
|
||||
{
|
||||
if (CreatureLocale const* cl = sObjectMgr->GetCreatureLocale(entry))
|
||||
{
|
||||
ObjectMgr::GetLocaleString(cl->Name, loc_idx, Name);
|
||||
ObjectMgr::GetLocaleString(cl->SubName, loc_idx, SubName);
|
||||
}
|
||||
}
|
||||
sLog->outDetail("WORLD: CMSG_CREATURE_QUERY '%s' - Entry: %u.", ci->Name.c_str(), entry);
|
||||
// guess size
|
||||
WorldPacket data(SMSG_CREATURE_QUERY_RESPONSE, 100);
|
||||
data << uint32(entry); // creature entry
|
||||
data << Name;
|
||||
data << uint8(0) << uint8(0) << uint8(0); // name2, name3, name4, always empty
|
||||
data << SubName;
|
||||
data << ci->IconName; // "Directions" for guard, string for Icons 2.3.0
|
||||
data << uint32(ci->type_flags); // flags
|
||||
data << uint32(ci->type); // CreatureType.dbc
|
||||
data << uint32(ci->family); // CreatureFamily.dbc
|
||||
data << uint32(ci->rank); // Creature Rank (elite, boss, etc)
|
||||
data << uint32(ci->KillCredit[0]); // new in 3.1, kill credit
|
||||
data << uint32(ci->KillCredit[1]); // new in 3.1, kill credit
|
||||
data << uint32(ci->Modelid1); // Modelid1
|
||||
data << uint32(ci->Modelid2); // Modelid2
|
||||
data << uint32(ci->Modelid3); // Modelid3
|
||||
data << uint32(ci->Modelid4); // Modelid4
|
||||
data << float(ci->ModHealth); // dmg/hp modifier
|
||||
data << float(ci->ModMana); // dmg/mana modifier
|
||||
data << uint8(ci->RacialLeader);
|
||||
for (uint32 i = 0; i < MAX_CREATURE_QUEST_ITEMS; ++i)
|
||||
data << uint32(ci->questItems[i]); // itemId[6], quest drop
|
||||
data << uint32(ci->movementId); // CreatureMovementInfo.dbc
|
||||
SendPacket(&data);
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE");
|
||||
}
|
||||
else
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CREATURE_QUERY - NO CREATURE INFO! (GUID: %u, ENTRY: %u)",
|
||||
GUID_LOPART(guid), entry);
|
||||
WorldPacket data(SMSG_CREATURE_QUERY_RESPONSE, 4);
|
||||
data << uint32(entry | 0x80000000);
|
||||
SendPacket(&data);
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE");
|
||||
}
|
||||
}
|
||||
|
||||
/// Only _static_ data is sent in this packet !!!
|
||||
void WorldSession::HandleGameObjectQueryOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
uint32 entry;
|
||||
recv_data >> entry;
|
||||
uint64 guid;
|
||||
recv_data >> guid;
|
||||
|
||||
const GameObjectTemplate* info = sObjectMgr->GetGameObjectTemplate(entry);
|
||||
if (info)
|
||||
{
|
||||
std::string Name;
|
||||
std::string IconName;
|
||||
std::string CastBarCaption;
|
||||
|
||||
Name = info->name;
|
||||
IconName = info->IconName;
|
||||
CastBarCaption = info->castBarCaption;
|
||||
|
||||
int loc_idx = GetSessionDbLocaleIndex();
|
||||
if (loc_idx >= 0)
|
||||
{
|
||||
if (GameObjectLocale const* gl = sObjectMgr->GetGameObjectLocale(entry))
|
||||
{
|
||||
ObjectMgr::GetLocaleString(gl->Name, loc_idx, Name);
|
||||
ObjectMgr::GetLocaleString(gl->CastBarCaption, loc_idx, CastBarCaption);
|
||||
}
|
||||
}
|
||||
sLog->outDetail("WORLD: CMSG_GAMEOBJECT_QUERY '%s' - Entry: %u. ", info->name.c_str(), entry);
|
||||
WorldPacket data (SMSG_GAMEOBJECT_QUERY_RESPONSE, 150);
|
||||
data << uint32(entry);
|
||||
data << uint32(info->type);
|
||||
data << uint32(info->displayId);
|
||||
data << Name;
|
||||
data << uint8(0) << uint8(0) << uint8(0); // name2, name3, name4
|
||||
data << IconName; // 2.0.3, string. Icon name to use instead of default icon for go's (ex: "Attack" makes sword)
|
||||
data << CastBarCaption; // 2.0.3, string. Text will appear in Cast Bar when using GO (ex: "Collecting")
|
||||
data << info->unk1; // 2.0.3, string
|
||||
data.append(info->raw.data, 24);
|
||||
data << float(info->size); // go size
|
||||
for (uint32 i = 0; i < MAX_GAMEOBJECT_QUEST_ITEMS; ++i)
|
||||
data << uint32(info->questItems[i]); // itemId[6], quest drop
|
||||
SendPacket(&data);
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_GAMEOBJECT_QUERY_RESPONSE");
|
||||
}
|
||||
else
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_GAMEOBJECT_QUERY - Missing gameobject info for (GUID: %u, ENTRY: %u)",
|
||||
GUID_LOPART(guid), entry);
|
||||
WorldPacket data (SMSG_GAMEOBJECT_QUERY_RESPONSE, 4);
|
||||
data << uint32(entry | 0x80000000);
|
||||
SendPacket(&data);
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_GAMEOBJECT_QUERY_RESPONSE");
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleCorpseQueryOpcode(WorldPacket & /*recv_data*/)
|
||||
{
|
||||
sLog->outDetail("WORLD: Received MSG_CORPSE_QUERY");
|
||||
|
||||
Corpse* corpse = GetPlayer()->GetCorpse();
|
||||
|
||||
if (!corpse)
|
||||
{
|
||||
WorldPacket data(MSG_CORPSE_QUERY, 1);
|
||||
data << uint8(0); // corpse not found
|
||||
SendPacket(&data);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 mapid = corpse->GetMapId();
|
||||
float x = corpse->GetPositionX();
|
||||
float y = corpse->GetPositionY();
|
||||
float z = corpse->GetPositionZ();
|
||||
uint32 corpsemapid = mapid;
|
||||
|
||||
// if corpse at different map
|
||||
if (mapid != _player->GetMapId())
|
||||
{
|
||||
// search entrance map for proper show entrance
|
||||
if (MapEntry const* corpseMapEntry = sMapStore.LookupEntry(mapid))
|
||||
{
|
||||
if (corpseMapEntry->IsDungeon() && corpseMapEntry->entrance_map >= 0)
|
||||
{
|
||||
// if corpse map have entrance
|
||||
if (Map const* entranceMap = sMapMgr->CreateBaseMap(corpseMapEntry->entrance_map))
|
||||
{
|
||||
mapid = corpseMapEntry->entrance_map;
|
||||
x = corpseMapEntry->entrance_x;
|
||||
y = corpseMapEntry->entrance_y;
|
||||
z = entranceMap->GetHeight(x, y, MAX_HEIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WorldPacket data(MSG_CORPSE_QUERY, 1+(6*4));
|
||||
data << uint8(1); // corpse found
|
||||
data << int32(mapid);
|
||||
data << float(x);
|
||||
data << float(y);
|
||||
data << float(z);
|
||||
data << int32(corpsemapid);
|
||||
data << uint32(0); // unknown
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleNpcTextQueryOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
uint32 textID;
|
||||
uint64 guid;
|
||||
|
||||
recv_data >> textID;
|
||||
sLog->outDetail("WORLD: CMSG_NPC_TEXT_QUERY ID '%u'", textID);
|
||||
|
||||
recv_data >> guid;
|
||||
GetPlayer()->SetSelection(guid);
|
||||
|
||||
GossipText const* pGossip = sObjectMgr->GetGossipText(textID);
|
||||
|
||||
WorldPacket data(SMSG_NPC_TEXT_UPDATE, 100); // guess size
|
||||
data << textID;
|
||||
|
||||
if (!pGossip)
|
||||
{
|
||||
for (uint32 i = 0; i < MAX_GOSSIP_TEXT_OPTIONS; ++i)
|
||||
{
|
||||
data << float(0);
|
||||
data << "Greetings $N";
|
||||
data << "Greetings $N";
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string Text_0[MAX_LOCALES], Text_1[MAX_LOCALES];
|
||||
for (int i = 0; i < MAX_GOSSIP_TEXT_OPTIONS; ++i)
|
||||
{
|
||||
Text_0[i]=pGossip->Options[i].Text_0;
|
||||
Text_1[i]=pGossip->Options[i].Text_1;
|
||||
}
|
||||
|
||||
int loc_idx = GetSessionDbLocaleIndex();
|
||||
if (loc_idx >= 0)
|
||||
{
|
||||
if (NpcTextLocale const* nl = sObjectMgr->GetNpcTextLocale(textID))
|
||||
{
|
||||
for (int i = 0; i < MAX_LOCALES; ++i)
|
||||
{
|
||||
ObjectMgr::GetLocaleString(nl->Text_0[i], loc_idx, Text_0[i]);
|
||||
ObjectMgr::GetLocaleString(nl->Text_1[i], loc_idx, Text_1[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_GOSSIP_TEXT_OPTIONS; ++i)
|
||||
{
|
||||
data << pGossip->Options[i].Probability;
|
||||
|
||||
if (Text_0[i].empty())
|
||||
data << Text_1[i];
|
||||
else
|
||||
data << Text_0[i];
|
||||
|
||||
if (Text_1[i].empty())
|
||||
data << Text_0[i];
|
||||
else
|
||||
data << Text_1[i];
|
||||
|
||||
data << pGossip->Options[i].Language;
|
||||
|
||||
for (int j = 0; j < MAX_GOSSIP_TEXT_EMOTES; ++j)
|
||||
{
|
||||
data << pGossip->Options[i].Emotes[j]._Delay;
|
||||
data << pGossip->Options[i].Emotes[j]._Emote;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SendPacket(&data);
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_NPC_TEXT_UPDATE");
|
||||
}
|
||||
|
||||
/// Only _static_ data is sent in this packet !!!
|
||||
void WorldSession::HandlePageTextQueryOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDetail("WORLD: Received CMSG_PAGE_TEXT_QUERY");
|
||||
|
||||
uint32 pageID;
|
||||
recv_data >> pageID;
|
||||
recv_data.read_skip<uint64>(); // guid
|
||||
|
||||
while (pageID)
|
||||
{
|
||||
PageText const* pageText = sObjectMgr->GetPageText(pageID);
|
||||
// guess size
|
||||
WorldPacket data(SMSG_PAGE_TEXT_QUERY_RESPONSE, 50);
|
||||
data << pageID;
|
||||
|
||||
if (!pageText)
|
||||
{
|
||||
data << "Item page missing.";
|
||||
data << uint32(0);
|
||||
pageID = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string Text = pageText->Text;
|
||||
|
||||
int loc_idx = GetSessionDbLocaleIndex();
|
||||
if (loc_idx >= 0)
|
||||
if (PageTextLocale const* player = sObjectMgr->GetPageTextLocale(pageID))
|
||||
ObjectMgr::GetLocaleString(player->Text, loc_idx, Text);
|
||||
|
||||
data << Text;
|
||||
data << uint32(pageText->NextPage);
|
||||
pageID = pageText->NextPage;
|
||||
}
|
||||
SendPacket(&data);
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_PAGE_TEXT_QUERY_RESPONSE");
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleCorpseMapPositionQuery(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recv CMSG_CORPSE_MAP_POSITION_QUERY");
|
||||
|
||||
uint32 unk;
|
||||
recv_data >> unk;
|
||||
|
||||
WorldPacket data(SMSG_CORPSE_MAP_POSITION_QUERY_RESPONSE, 4+4+4+4);
|
||||
data << float(0);
|
||||
data << float(0);
|
||||
data << float(0);
|
||||
data << float(0);
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleQuestPOIQuery(WorldPacket& recv_data)
|
||||
{
|
||||
uint32 count;
|
||||
recv_data >> count; // quest count, max=25
|
||||
|
||||
if (count >= MAX_QUEST_LOG_SIZE)
|
||||
{
|
||||
recv_data.rfinish();
|
||||
return;
|
||||
}
|
||||
|
||||
WorldPacket data(SMSG_QUEST_POI_QUERY_RESPONSE, 4+(4+4)*count);
|
||||
data << uint32(count); // count
|
||||
|
||||
for (uint32 i = 0; i < count; ++i)
|
||||
{
|
||||
uint32 questId;
|
||||
recv_data >> questId; // quest id
|
||||
|
||||
bool questOk = false;
|
||||
|
||||
uint16 questSlot = _player->FindQuestSlot(questId);
|
||||
|
||||
if (questSlot != MAX_QUEST_LOG_SIZE)
|
||||
questOk =_player->GetQuestSlotQuestId(questSlot) == questId;
|
||||
|
||||
if (questOk)
|
||||
{
|
||||
QuestPOIVector const* POI = sObjectMgr->GetQuestPOIVector(questId);
|
||||
|
||||
if (POI)
|
||||
{
|
||||
data << uint32(questId); // quest ID
|
||||
data << uint32(POI->size()); // POI count
|
||||
|
||||
for (QuestPOIVector::const_iterator itr = POI->begin(); itr != POI->end(); ++itr)
|
||||
{
|
||||
data << uint32(itr->Id); // POI index
|
||||
data << int32(itr->ObjectiveIndex); // objective index
|
||||
data << uint32(itr->MapId); // mapid
|
||||
data << uint32(itr->AreaId); // areaid
|
||||
data << uint32(itr->Unk2); // unknown
|
||||
data << uint32(itr->Unk3); // unknown
|
||||
data << uint32(itr->Unk4); // unknown
|
||||
data << uint32(itr->points.size()); // POI points count
|
||||
|
||||
for (std::vector<QuestPOIPoint>::const_iterator itr2 = itr->points.begin(); itr2 != itr->points.end(); ++itr2)
|
||||
{
|
||||
data << int32(itr2->x); // POI point x
|
||||
data << int32(itr2->y); // POI point y
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
data << uint32(questId); // quest ID
|
||||
data << uint32(0); // POI count
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
data << uint32(questId); // quest ID
|
||||
data << uint32(0); // POI count
|
||||
}
|
||||
}
|
||||
|
||||
SendPacket(&data);
|
||||
}
|
||||
Executable
+779
@@ -0,0 +1,779 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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 "Common.h"
|
||||
#include "Log.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
#include "Opcodes.h"
|
||||
#include "World.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Player.h"
|
||||
#include "GossipDef.h"
|
||||
#include "QuestDef.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "Group.h"
|
||||
#include "Battleground.h"
|
||||
#include "BattlegroundAV.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "GameObjectAI.h"
|
||||
|
||||
void WorldSession::HandleQuestgiverStatusQueryOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
uint64 guid;
|
||||
recv_data >> guid;
|
||||
uint8 questStatus = DIALOG_STATUS_NONE;
|
||||
uint8 defstatus = DIALOG_STATUS_NONE;
|
||||
|
||||
Object* questgiver = ObjectAccessor::GetObjectByTypeMask(*_player, guid, TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT);
|
||||
if (!questgiver)
|
||||
{
|
||||
sLog->outDetail("Error in CMSG_QUESTGIVER_STATUS_QUERY, called for not found questgiver (Typeid: %u GUID: %u)", GuidHigh2TypeId(GUID_HIPART(guid)), GUID_LOPART(guid));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (questgiver->GetTypeId())
|
||||
{
|
||||
case TYPEID_UNIT:
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_STATUS_QUERY for npc, guid = %u", uint32(GUID_LOPART(guid)));
|
||||
Creature* cr_questgiver=questgiver->ToCreature();
|
||||
if (!cr_questgiver->IsHostileTo(_player)) // not show quest status to enemies
|
||||
{
|
||||
questStatus = sScriptMgr->GetDialogStatus(_player, cr_questgiver);
|
||||
if (questStatus > 6)
|
||||
questStatus = getDialogStatus(_player, cr_questgiver, defstatus);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TYPEID_GAMEOBJECT:
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_STATUS_QUERY for GameObject guid = %u", uint32(GUID_LOPART(guid)));
|
||||
GameObject* go_questgiver=(GameObject*)questgiver;
|
||||
questStatus = sScriptMgr->GetDialogStatus(_player, go_questgiver);
|
||||
if (questStatus > 6)
|
||||
questStatus = getDialogStatus(_player, go_questgiver, defstatus);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
sLog->outError("QuestGiver called for unexpected type %u", questgiver->GetTypeId());
|
||||
break;
|
||||
}
|
||||
|
||||
//inform client about status of quest
|
||||
_player->PlayerTalkClass->SendQuestGiverStatus(questStatus, guid);
|
||||
}
|
||||
|
||||
void WorldSession::HandleQuestgiverHelloOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
uint64 guid;
|
||||
recv_data >> guid;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_HELLO npc = %u", GUID_LOPART(guid));
|
||||
|
||||
Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE);
|
||||
if (!creature)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleQuestgiverHelloOpcode - Unit (GUID: %u) not found or you can't interact with him.",
|
||||
GUID_LOPART(guid));
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
// Stop the npc if moving
|
||||
creature->StopMoving();
|
||||
|
||||
if (sScriptMgr->OnGossipHello(_player, creature))
|
||||
return;
|
||||
|
||||
_player->PrepareGossipMenu(creature, creature->GetCreatureInfo()->GossipMenuId, true);
|
||||
_player->SendPreparedGossip(creature);
|
||||
|
||||
creature->AI()->sGossipHello(_player);
|
||||
}
|
||||
|
||||
void WorldSession::HandleQuestgiverAcceptQuestOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
uint64 guid;
|
||||
uint32 quest;
|
||||
uint32 unk1;
|
||||
recv_data >> guid >> quest >> unk1;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_ACCEPT_QUEST npc = %u, quest = %u, unk1 = %u", uint32(GUID_LOPART(guid)), quest, unk1);
|
||||
|
||||
Object* pObject = ObjectAccessor::GetObjectByTypeMask(*_player, guid, TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT|TYPEMASK_ITEM|TYPEMASK_PLAYER);
|
||||
|
||||
// no or incorrect quest giver
|
||||
if (!pObject || (pObject->GetTypeId() != TYPEID_PLAYER && !pObject->hasQuest(quest)) ||
|
||||
(pObject->GetTypeId() == TYPEID_PLAYER && !pObject->ToPlayer()->CanShareQuest(quest)))
|
||||
{
|
||||
_player->PlayerTalkClass->SendCloseGossip();
|
||||
_player->SetDivider(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// some kind of WPE protection
|
||||
if (!_player->CanInteractWithQuestGiver(pObject))
|
||||
return;
|
||||
|
||||
Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest);
|
||||
if (qInfo)
|
||||
{
|
||||
// prevent cheating
|
||||
if (!GetPlayer()->CanTakeQuest(qInfo, true))
|
||||
{
|
||||
_player->PlayerTalkClass->SendCloseGossip();
|
||||
_player->SetDivider(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_player->GetDivider() != 0)
|
||||
{
|
||||
Player* player = ObjectAccessor::FindPlayer(_player->GetDivider());
|
||||
if (player)
|
||||
{
|
||||
player->SendPushToPartyResponse(_player, QUEST_PARTY_MSG_ACCEPT_QUEST);
|
||||
_player->SetDivider(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (_player->CanAddQuest(qInfo, true))
|
||||
{
|
||||
_player->AddQuest(qInfo, pObject);
|
||||
|
||||
if (qInfo->HasFlag(QUEST_FLAGS_PARTY_ACCEPT))
|
||||
{
|
||||
if (Group* group = _player->GetGroup())
|
||||
{
|
||||
for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
|
||||
{
|
||||
Player* player = itr->getSource();
|
||||
|
||||
if (!player || player == _player) // not self
|
||||
continue;
|
||||
|
||||
if (player->CanTakeQuest(qInfo, true))
|
||||
{
|
||||
player->SetDivider(_player->GetGUID());
|
||||
|
||||
//need confirmation that any gossip window will close
|
||||
player->PlayerTalkClass->SendCloseGossip();
|
||||
|
||||
_player->SendQuestConfirmAccept(qInfo, player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_player->CanCompleteQuest(quest))
|
||||
_player->CompleteQuest(quest);
|
||||
|
||||
switch (pObject->GetTypeId())
|
||||
{
|
||||
case TYPEID_UNIT:
|
||||
sScriptMgr->OnQuestAccept(_player, (pObject->ToCreature()), qInfo);
|
||||
(pObject->ToCreature())->AI()->sQuestAccept(_player, qInfo);
|
||||
break;
|
||||
case TYPEID_ITEM:
|
||||
case TYPEID_CONTAINER:
|
||||
{
|
||||
sScriptMgr->OnQuestAccept(_player, ((Item*)pObject), qInfo);
|
||||
|
||||
// destroy not required for quest finish quest starting item
|
||||
bool destroyItem = true;
|
||||
for (int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
|
||||
{
|
||||
if ((qInfo->RequiredItemId[i] == ((Item*)pObject)->GetEntry()) && (((Item*)pObject)->GetTemplate()->MaxCount > 0))
|
||||
{
|
||||
destroyItem = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (destroyItem)
|
||||
_player->DestroyItem(((Item*)pObject)->GetBagSlot(), ((Item*)pObject)->GetSlot(), true);
|
||||
|
||||
break;
|
||||
}
|
||||
case TYPEID_GAMEOBJECT:
|
||||
sScriptMgr->OnQuestAccept(_player, ((GameObject*)pObject), qInfo);
|
||||
(pObject->ToGameObject())->AI()->QuestAccept(_player, qInfo);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_player->PlayerTalkClass->SendCloseGossip();
|
||||
|
||||
if (qInfo->GetSrcSpell() > 0)
|
||||
_player->CastSpell(_player, qInfo->GetSrcSpell(), true);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_player->PlayerTalkClass->SendCloseGossip();
|
||||
}
|
||||
|
||||
void WorldSession::HandleQuestgiverQueryQuestOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
uint64 guid;
|
||||
uint32 questId;
|
||||
uint8 unk1;
|
||||
recv_data >> guid >> questId >> unk1;
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_QUERY_QUEST npc = %u, quest = %u, unk1 = %u", uint32(GUID_LOPART(guid)), questId, unk1);
|
||||
|
||||
// Verify that the guid is valid and is a questgiver or involved in the requested quest
|
||||
Object* object = ObjectAccessor::GetObjectByTypeMask(*_player, guid, TYPEMASK_UNIT | TYPEMASK_GAMEOBJECT | TYPEMASK_ITEM);
|
||||
if (!object || (!object->hasQuest(questId) && !object->hasInvolvedQuest(questId)))
|
||||
{
|
||||
_player->PlayerTalkClass->SendCloseGossip();
|
||||
return;
|
||||
}
|
||||
|
||||
Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
|
||||
if (quest)
|
||||
{
|
||||
// not sure here what should happen to quests with QUEST_FLAGS_AUTOCOMPLETE
|
||||
// if this breaks them, add && object->GetTypeId() == TYPEID_ITEM to this check
|
||||
// item-started quests never have that flag
|
||||
if (!_player->CanTakeQuest(quest, true))
|
||||
return;
|
||||
|
||||
if (quest->IsAutoAccept() && _player->CanAddQuest(quest, true))
|
||||
{
|
||||
_player->AddQuest(quest, object);
|
||||
if (_player->CanCompleteQuest(questId))
|
||||
_player->CompleteQuest(questId);
|
||||
}
|
||||
|
||||
if (quest->HasFlag(QUEST_FLAGS_AUTOCOMPLETE))
|
||||
_player->PlayerTalkClass->SendQuestGiverRequestItems(quest, object->GetGUID(), _player->CanCompleteQuest(quest->GetQuestId()), true);
|
||||
else
|
||||
_player->PlayerTalkClass->SendQuestGiverQuestDetails(quest, object->GetGUID(), true);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleQuestQueryOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
if (!_player)
|
||||
return;
|
||||
|
||||
uint32 quest;
|
||||
recv_data >> quest;
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUEST_QUERY quest = %u", quest);
|
||||
|
||||
Quest const* pQuest = sObjectMgr->GetQuestTemplate(quest);
|
||||
if (pQuest)
|
||||
{
|
||||
_player->PlayerTalkClass->SendQuestQueryResponse(pQuest);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleQuestgiverChooseRewardOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
uint32 questId, reward;
|
||||
uint64 guid;
|
||||
recv_data >> guid >> questId >> reward;
|
||||
|
||||
if (reward >= QUEST_REWARD_CHOICES_COUNT)
|
||||
{
|
||||
sLog->outError("Error in CMSG_QUESTGIVER_CHOOSE_REWARD: player %s (guid %d) tried to get invalid reward (%u) (probably packet hacking)", _player->GetName(), _player->GetGUIDLow(), reward);
|
||||
return;
|
||||
}
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_CHOOSE_REWARD npc = %u, quest = %u, reward = %u", uint32(GUID_LOPART(guid)), questId, reward);
|
||||
|
||||
Object* object = ObjectAccessor::GetObjectByTypeMask(*_player, guid, TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT);
|
||||
if (!object || !object->hasInvolvedQuest(questId))
|
||||
return;
|
||||
|
||||
// some kind of WPE protection
|
||||
if (!_player->CanInteractWithQuestGiver(object))
|
||||
return;
|
||||
|
||||
if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId))
|
||||
{
|
||||
if ((!_player->CanSeeStartQuest(quest) && _player->GetQuestStatus(questId) == QUEST_STATUS_NONE) ||
|
||||
(_player->GetQuestStatus(questId) != QUEST_STATUS_COMPLETE && !quest->IsAutoComplete()))
|
||||
{
|
||||
sLog->outError("HACK ALERT: Player %s (guid: %u) is trying to complete quest (id: %u) but he has no right to do it!",
|
||||
_player->GetName(), _player->GetGUIDLow(), questId);
|
||||
return;
|
||||
}
|
||||
if (_player->CanRewardQuest(quest, reward, true))
|
||||
{
|
||||
_player->RewardQuest(quest, reward, object);
|
||||
|
||||
switch (object->GetTypeId())
|
||||
{
|
||||
case TYPEID_UNIT:
|
||||
if (!(sScriptMgr->OnQuestReward(_player, (object->ToCreature()), quest, reward)))
|
||||
{
|
||||
// Send next quest
|
||||
if (Quest const* nextQuest = _player->GetNextQuest(guid, quest))
|
||||
{
|
||||
if (nextQuest->IsAutoAccept() && _player->CanAddQuest(nextQuest, true) && _player->CanTakeQuest(quest, true))
|
||||
{
|
||||
_player->AddQuest(nextQuest, object);
|
||||
if (_player->CanCompleteQuest(nextQuest->GetQuestId()))
|
||||
_player->CompleteQuest(nextQuest->GetQuestId());
|
||||
}
|
||||
|
||||
_player->PlayerTalkClass->SendQuestGiverQuestDetails(nextQuest, guid, true);
|
||||
}
|
||||
|
||||
(object->ToCreature())->AI()->sQuestReward(_player, quest, reward);
|
||||
}
|
||||
break;
|
||||
case TYPEID_GAMEOBJECT:
|
||||
if (!sScriptMgr->OnQuestReward(_player, ((GameObject*)object), quest, reward))
|
||||
{
|
||||
// Send next quest
|
||||
if (Quest const* nextQuest = _player->GetNextQuest(guid, quest))
|
||||
{
|
||||
if (nextQuest->IsAutoAccept() && _player->CanAddQuest(nextQuest, true) && _player->CanTakeQuest(quest, true))
|
||||
{
|
||||
_player->AddQuest(nextQuest, object);
|
||||
if (_player->CanCompleteQuest(nextQuest->GetQuestId()))
|
||||
_player->CompleteQuest(nextQuest->GetQuestId());
|
||||
}
|
||||
|
||||
_player->PlayerTalkClass->SendQuestGiverQuestDetails(nextQuest, guid, true);
|
||||
}
|
||||
|
||||
object->ToGameObject()->AI()->QuestReward(_player, quest, reward);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
_player->PlayerTalkClass->SendQuestGiverOfferReward(quest, guid, true);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleQuestgiverRequestRewardOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
uint32 quest;
|
||||
uint64 guid;
|
||||
recv_data >> guid >> quest;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_REQUEST_REWARD npc = %u, quest = %u", uint32(GUID_LOPART(guid)), quest);
|
||||
|
||||
Object* pObject = ObjectAccessor::GetObjectByTypeMask(*_player, guid, TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT);
|
||||
if (!pObject || !pObject->hasInvolvedQuest(quest))
|
||||
return;
|
||||
|
||||
// some kind of WPE protection
|
||||
if (!_player->CanInteractWithQuestGiver(pObject))
|
||||
return;
|
||||
|
||||
if (_player->CanCompleteQuest(quest))
|
||||
_player->CompleteQuest(quest);
|
||||
|
||||
if (_player->GetQuestStatus(quest) != QUEST_STATUS_COMPLETE)
|
||||
return;
|
||||
|
||||
if (Quest const* pQuest = sObjectMgr->GetQuestTemplate(quest))
|
||||
_player->PlayerTalkClass->SendQuestGiverOfferReward(pQuest, guid, true);
|
||||
}
|
||||
|
||||
void WorldSession::HandleQuestgiverCancel(WorldPacket& /*recv_data*/)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_CANCEL");
|
||||
|
||||
_player->PlayerTalkClass->SendCloseGossip();
|
||||
}
|
||||
|
||||
void WorldSession::HandleQuestLogSwapQuest(WorldPacket& recv_data)
|
||||
{
|
||||
uint8 slot1, slot2;
|
||||
recv_data >> slot1 >> slot2;
|
||||
|
||||
if (slot1 == slot2 || slot1 >= MAX_QUEST_LOG_SIZE || slot2 >= MAX_QUEST_LOG_SIZE)
|
||||
return;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTLOG_SWAP_QUEST slot 1 = %u, slot 2 = %u", slot1, slot2);
|
||||
|
||||
GetPlayer()->SwapQuestSlot(slot1, slot2);
|
||||
}
|
||||
|
||||
void WorldSession::HandleQuestLogRemoveQuest(WorldPacket& recv_data)
|
||||
{
|
||||
uint8 slot;
|
||||
recv_data >> slot;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTLOG_REMOVE_QUEST slot = %u", slot);
|
||||
|
||||
if (slot < MAX_QUEST_LOG_SIZE)
|
||||
{
|
||||
if (uint32 quest = _player->GetQuestSlotQuestId(slot))
|
||||
{
|
||||
if (!_player->TakeQuestSourceItem(quest, true))
|
||||
return; // can't un-equip some items, reject quest cancel
|
||||
|
||||
if (const Quest *pQuest = sObjectMgr->GetQuestTemplate(quest))
|
||||
{
|
||||
if (pQuest->HasFlag(QUEST_TRINITY_FLAGS_TIMED))
|
||||
_player->RemoveTimedQuest(quest);
|
||||
}
|
||||
|
||||
_player->TakeQuestSourceItem(quest, true); // remove quest src item from player
|
||||
_player->RemoveActiveQuest(quest);
|
||||
_player->GetAchievementMgr().RemoveTimedAchievement(ACHIEVEMENT_TIMED_TYPE_QUEST, quest);
|
||||
|
||||
sLog->outDetail("Player %u abandoned quest %u", _player->GetGUIDLow(), quest);
|
||||
}
|
||||
|
||||
_player->SetQuestSlot(slot, 0);
|
||||
|
||||
_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleQuestConfirmAccept(WorldPacket& recv_data)
|
||||
{
|
||||
uint32 quest;
|
||||
recv_data >> quest;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUEST_CONFIRM_ACCEPT quest = %u", quest);
|
||||
|
||||
if (const Quest* pQuest = sObjectMgr->GetQuestTemplate(quest))
|
||||
{
|
||||
if (!pQuest->HasFlag(QUEST_FLAGS_PARTY_ACCEPT))
|
||||
return;
|
||||
|
||||
Player* pOriginalPlayer = ObjectAccessor::FindPlayer(_player->GetDivider());
|
||||
|
||||
if (!pOriginalPlayer)
|
||||
return;
|
||||
|
||||
if (pQuest->IsRaidQuest())
|
||||
{
|
||||
if (!_player->IsInSameRaidWith(pOriginalPlayer))
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_player->IsInSameGroupWith(pOriginalPlayer))
|
||||
return;
|
||||
}
|
||||
|
||||
if (_player->CanAddQuest(pQuest, true))
|
||||
_player->AddQuest(pQuest, NULL); // NULL, this prevent DB script from duplicate running
|
||||
|
||||
_player->SetDivider(0);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleQuestgiverCompleteQuest(WorldPacket& recv_data)
|
||||
{
|
||||
uint32 quest;
|
||||
uint64 guid;
|
||||
recv_data >> guid >> quest;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_COMPLETE_QUEST npc = %u, quest = %u", uint32(GUID_LOPART(guid)), quest);
|
||||
|
||||
Object* pObject = ObjectAccessor::GetObjectByTypeMask(*_player, guid, TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT);
|
||||
if (!pObject || !pObject->hasInvolvedQuest(quest))
|
||||
return;
|
||||
|
||||
// some kind of WPE protection
|
||||
if (!_player->CanInteractWithQuestGiver(pObject))
|
||||
return;
|
||||
|
||||
Quest const* pQuest = sObjectMgr->GetQuestTemplate(quest);
|
||||
if (pQuest)
|
||||
{
|
||||
if (!_player->CanSeeStartQuest(pQuest) && _player->GetQuestStatus(quest)==QUEST_STATUS_NONE)
|
||||
{
|
||||
sLog->outError("Possible hacking attempt: Player %s [guid: %u] tried to complete quest [entry: %u] without being in possession of the quest!",
|
||||
_player->GetName(), _player->GetGUIDLow(), quest);
|
||||
return;
|
||||
}
|
||||
// TODO: need a virtual function
|
||||
if (_player->InBattleground())
|
||||
if (Battleground* bg = _player->GetBattleground())
|
||||
if (bg->GetTypeID() == BATTLEGROUND_AV)
|
||||
((BattlegroundAV*)bg)->HandleQuestComplete(quest, _player);
|
||||
|
||||
if (_player->GetQuestStatus(quest) != QUEST_STATUS_COMPLETE)
|
||||
{
|
||||
if (pQuest->IsRepeatable())
|
||||
_player->PlayerTalkClass->SendQuestGiverRequestItems(pQuest, guid, _player->CanCompleteRepeatableQuest(pQuest), false);
|
||||
else
|
||||
_player->PlayerTalkClass->SendQuestGiverRequestItems(pQuest, guid, _player->CanRewardQuest(pQuest, false), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pQuest->GetReqItemsCount()) // some items required
|
||||
_player->PlayerTalkClass->SendQuestGiverRequestItems(pQuest, guid, _player->CanRewardQuest(pQuest, false), false);
|
||||
else // no items required
|
||||
_player->PlayerTalkClass->SendQuestGiverOfferReward(pQuest, guid, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleQuestgiverQuestAutoLaunch(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_QUEST_AUTOLAUNCH");
|
||||
}
|
||||
|
||||
void WorldSession::HandlePushQuestToParty(WorldPacket& recvPacket)
|
||||
{
|
||||
uint32 questId;
|
||||
recvPacket >> questId;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_PUSHQUESTTOPARTY quest = %u", questId);
|
||||
|
||||
if (Quest const* pQuest = sObjectMgr->GetQuestTemplate(questId))
|
||||
{
|
||||
if (Group* group = _player->GetGroup())
|
||||
{
|
||||
for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
|
||||
{
|
||||
Player* player = itr->getSource();
|
||||
|
||||
if (!player || player == _player) // skip self
|
||||
continue;
|
||||
|
||||
_player->SendPushToPartyResponse(player, QUEST_PARTY_MSG_SHARING_QUEST);
|
||||
|
||||
if (!player->SatisfyQuestStatus(pQuest, false))
|
||||
{
|
||||
_player->SendPushToPartyResponse(player, QUEST_PARTY_MSG_HAVE_QUEST);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (player->GetQuestStatus(questId) == QUEST_STATUS_COMPLETE)
|
||||
{
|
||||
_player->SendPushToPartyResponse(player, QUEST_PARTY_MSG_FINISH_QUEST);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!player->CanTakeQuest(pQuest, false))
|
||||
{
|
||||
_player->SendPushToPartyResponse(player, QUEST_PARTY_MSG_CANT_TAKE_QUEST);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!player->SatisfyQuestLog(false))
|
||||
{
|
||||
_player->SendPushToPartyResponse(player, QUEST_PARTY_MSG_LOG_FULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (player->GetDivider() != 0)
|
||||
{
|
||||
_player->SendPushToPartyResponse(player, QUEST_PARTY_MSG_BUSY);
|
||||
continue;
|
||||
}
|
||||
|
||||
player->PlayerTalkClass->SendQuestGiverQuestDetails(pQuest, _player->GetGUID(), true);
|
||||
player->SetDivider(_player->GetGUID());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleQuestPushResult(WorldPacket& recvPacket)
|
||||
{
|
||||
uint64 guid;
|
||||
uint8 msg;
|
||||
recvPacket >> guid >> msg;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received MSG_QUEST_PUSH_RESULT");
|
||||
|
||||
if (_player->GetDivider() != 0)
|
||||
{
|
||||
Player* player = ObjectAccessor::FindPlayer(_player->GetDivider());
|
||||
if (player)
|
||||
{
|
||||
WorldPacket data(MSG_QUEST_PUSH_RESULT, (8+1));
|
||||
data << uint64(guid);
|
||||
data << uint8(msg); // valid values: 0-8
|
||||
player->GetSession()->SendPacket(&data);
|
||||
_player->SetDivider(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32 WorldSession::getDialogStatus(Player* player, Object* questgiver, uint32 defstatus)
|
||||
{
|
||||
uint32 result = defstatus;
|
||||
|
||||
QuestRelationBounds qr;
|
||||
QuestRelationBounds qir;
|
||||
|
||||
switch (questgiver->GetTypeId())
|
||||
{
|
||||
case TYPEID_GAMEOBJECT:
|
||||
{
|
||||
qr = sObjectMgr->GetGOQuestRelationBounds(questgiver->GetEntry());
|
||||
qir = sObjectMgr->GetGOQuestInvolvedRelationBounds(questgiver->GetEntry());
|
||||
break;
|
||||
}
|
||||
case TYPEID_UNIT:
|
||||
{
|
||||
qr = sObjectMgr->GetCreatureQuestRelationBounds(questgiver->GetEntry());
|
||||
qir = sObjectMgr->GetCreatureQuestInvolvedRelationBounds(questgiver->GetEntry());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
//its imposible, but check ^)
|
||||
sLog->outError("Warning: GetDialogStatus called for unexpected type %u", questgiver->GetTypeId());
|
||||
return DIALOG_STATUS_NONE;
|
||||
}
|
||||
|
||||
for (QuestRelations::const_iterator i = qir.first; i != qir.second; ++i)
|
||||
{
|
||||
uint32 result2 = 0;
|
||||
uint32 quest_id = i->second;
|
||||
Quest const* pQuest = sObjectMgr->GetQuestTemplate(quest_id);
|
||||
if (!pQuest) continue;
|
||||
|
||||
ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_QUEST_SHOW_MARK, pQuest->GetQuestId());
|
||||
if (!sConditionMgr->IsPlayerMeetToConditions(player, conditions))
|
||||
continue;
|
||||
|
||||
QuestStatus status = player->GetQuestStatus(quest_id);
|
||||
if ((status == QUEST_STATUS_COMPLETE && !player->GetQuestRewardStatus(quest_id)) ||
|
||||
(pQuest->IsAutoComplete() && player->CanTakeQuest(pQuest, false)))
|
||||
{
|
||||
if (pQuest->IsAutoComplete() && pQuest->IsRepeatable())
|
||||
result2 = DIALOG_STATUS_REWARD_REP;
|
||||
else
|
||||
result2 = DIALOG_STATUS_REWARD;
|
||||
}
|
||||
else if (status == QUEST_STATUS_INCOMPLETE)
|
||||
result2 = DIALOG_STATUS_INCOMPLETE;
|
||||
|
||||
if (result2 > result)
|
||||
result = result2;
|
||||
}
|
||||
|
||||
for (QuestRelations::const_iterator i = qr.first; i != qr.second; ++i)
|
||||
{
|
||||
uint32 result2 = 0;
|
||||
uint32 quest_id = i->second;
|
||||
Quest const* pQuest = sObjectMgr->GetQuestTemplate(quest_id);
|
||||
if (!pQuest)
|
||||
continue;
|
||||
|
||||
ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_QUEST_SHOW_MARK, pQuest->GetQuestId());
|
||||
if (!sConditionMgr->IsPlayerMeetToConditions(player, conditions))
|
||||
continue;
|
||||
|
||||
QuestStatus status = player->GetQuestStatus(quest_id);
|
||||
if (status == QUEST_STATUS_NONE)
|
||||
{
|
||||
if (player->CanSeeStartQuest(pQuest))
|
||||
{
|
||||
if (player->SatisfyQuestLevel(pQuest, false))
|
||||
{
|
||||
if (pQuest->IsAutoComplete() || (pQuest->IsRepeatable() && player->IsQuestRewarded(quest_id)))
|
||||
result2 = DIALOG_STATUS_REWARD_REP;
|
||||
else if (player->getLevel() <= ((player->GetQuestLevel(pQuest) == -1) ? player->getLevel() : player->GetQuestLevel(pQuest) + sWorld->getIntConfig(CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF)))
|
||||
{
|
||||
if (pQuest->HasFlag(QUEST_FLAGS_DAILY) || pQuest->HasFlag(QUEST_FLAGS_WEEKLY))
|
||||
result2 = DIALOG_STATUS_AVAILABLE_REP;
|
||||
else
|
||||
result2 = DIALOG_STATUS_AVAILABLE;
|
||||
}
|
||||
else
|
||||
result2 = DIALOG_STATUS_LOW_LEVEL_AVAILABLE;
|
||||
}
|
||||
else
|
||||
result2 = DIALOG_STATUS_UNAVAILABLE;
|
||||
}
|
||||
}
|
||||
|
||||
if (result2 > result)
|
||||
result = result2;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void WorldSession::HandleQuestgiverStatusMultipleQuery(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY");
|
||||
|
||||
uint32 count = 0;
|
||||
|
||||
WorldPacket data(SMSG_QUESTGIVER_STATUS_MULTIPLE, 4);
|
||||
data << uint32(count); // placeholder
|
||||
|
||||
for (Player::ClientGUIDs::const_iterator itr = _player->m_clientGUIDs.begin(); itr != _player->m_clientGUIDs.end(); ++itr)
|
||||
{
|
||||
uint8 questStatus = DIALOG_STATUS_NONE;
|
||||
uint8 defstatus = DIALOG_STATUS_NONE;
|
||||
|
||||
if (IS_CRE_OR_VEH_OR_PET_GUID(*itr))
|
||||
{
|
||||
// need also pet quests case support
|
||||
Creature* questgiver = ObjectAccessor::GetCreatureOrPetOrVehicle(*GetPlayer(), *itr);
|
||||
if (!questgiver || questgiver->IsHostileTo(_player))
|
||||
continue;
|
||||
if (!questgiver->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER))
|
||||
continue;
|
||||
questStatus = sScriptMgr->GetDialogStatus(_player, questgiver);
|
||||
if (questStatus > 6)
|
||||
questStatus = getDialogStatus(_player, questgiver, defstatus);
|
||||
|
||||
data << uint64(questgiver->GetGUID());
|
||||
data << uint8(questStatus);
|
||||
++count;
|
||||
}
|
||||
else if (IS_GAMEOBJECT_GUID(*itr))
|
||||
{
|
||||
GameObject* questgiver = GetPlayer()->GetMap()->GetGameObject(*itr);
|
||||
if (!questgiver)
|
||||
continue;
|
||||
if (questgiver->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER)
|
||||
continue;
|
||||
questStatus = sScriptMgr->GetDialogStatus(_player, questgiver);
|
||||
if (questStatus > 6)
|
||||
questStatus = getDialogStatus(_player, questgiver, defstatus);
|
||||
|
||||
data << uint64(questgiver->GetGUID());
|
||||
data << uint8(questStatus);
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
data.put<uint32>(0, count); // write real count
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleQueryQuestsCompleted(WorldPacket & /*recv_data*/)
|
||||
{
|
||||
size_t rew_count = _player->GetRewardedQuestCount();
|
||||
|
||||
WorldPacket data(SMSG_QUERY_QUESTS_COMPLETED_RESPONSE, 4 + 4 * rew_count);
|
||||
data << uint32(rew_count);
|
||||
|
||||
const RewardedQuestSet &rewQuests = _player->getRewardedQuests();
|
||||
for (RewardedQuestSet::const_iterator itr = rewQuests.begin(); itr != rewQuests.end(); ++itr)
|
||||
data << uint32(*itr);
|
||||
|
||||
SendPacket(&data);
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2011 TrinityCore <http://www.trinitycore.org/>
|
||||
*
|
||||
* 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 "WorldSession.h"
|
||||
#include "Player.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Log.h"
|
||||
|
||||
void WorldSession::HandleGrantLevel(WorldPacket& recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_GRANT_LEVEL");
|
||||
|
||||
uint64 guid;
|
||||
recv_data.readPackGUID(guid);
|
||||
|
||||
Player* target = ObjectAccessor::GetObjectInWorld(guid, _player);
|
||||
|
||||
// check cheating
|
||||
uint8 levels = _player->GetGrantableLevels();
|
||||
uint8 error = 0;
|
||||
if (!target)
|
||||
error = ERR_REFER_A_FRIEND_NO_TARGET;
|
||||
else if (levels == 0)
|
||||
error = ERR_REFER_A_FRIEND_INSUFFICIENT_GRANTABLE_LEVELS;
|
||||
else if (GetRecruiterId() != target->GetSession()->GetAccountId())
|
||||
error = ERR_REFER_A_FRIEND_NOT_REFERRED_BY;
|
||||
else if (target->GetTeamId() != _player->GetTeamId())
|
||||
error = ERR_REFER_A_FRIEND_DIFFERENT_FACTION;
|
||||
else if (target->getLevel() >= _player->getLevel())
|
||||
error = ERR_REFER_A_FRIEND_TARGET_TOO_HIGH;
|
||||
else if (target->getLevel() >= sWorld->getIntConfig(CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL))
|
||||
error = ERR_REFER_A_FRIEND_GRANT_LEVEL_MAX_I;
|
||||
else if (target->GetGroup() != _player->GetGroup())
|
||||
error = ERR_REFER_A_FRIEND_NOT_IN_GROUP;
|
||||
|
||||
if (error) {
|
||||
WorldPacket data(SMSG_REFER_A_FRIEND_FAILURE, 24);
|
||||
data << uint32(error);
|
||||
if (error == ERR_REFER_A_FRIEND_NOT_IN_GROUP)
|
||||
data << target->GetName();
|
||||
|
||||
SendPacket(&data);
|
||||
return;
|
||||
}
|
||||
|
||||
WorldPacket data2(SMSG_PROPOSE_LEVEL_GRANT, 8);
|
||||
data2.append(_player->GetPackGUID());
|
||||
target->GetSession()->SendPacket(&data2);
|
||||
}
|
||||
|
||||
void WorldSession::HandleAcceptGrantLevel(WorldPacket& recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_ACCEPT_LEVEL_GRANT");
|
||||
|
||||
uint64 guid;
|
||||
recv_data.readPackGUID(guid);
|
||||
|
||||
Player* other = ObjectAccessor::GetObjectInWorld(guid, _player);
|
||||
if (!(other && other->GetSession()))
|
||||
return;
|
||||
|
||||
if (GetAccountId() != other->GetSession()->GetRecruiterId())
|
||||
return;
|
||||
|
||||
if (other->GetGrantableLevels())
|
||||
other->SetGrantableLevels(other->GetGrantableLevels() - 1);
|
||||
else
|
||||
return;
|
||||
|
||||
_player->GiveLevel(_player->getLevel() + 1);
|
||||
}
|
||||
Executable
+93
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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 "Common.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Log.h"
|
||||
#include "Player.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "UpdateMask.h"
|
||||
|
||||
void WorldSession::HandleLearnTalentOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
uint32 talent_id, requested_rank;
|
||||
recv_data >> talent_id >> requested_rank;
|
||||
|
||||
_player->LearnTalent(talent_id, requested_rank);
|
||||
_player->SendTalentsInfoData(false);
|
||||
}
|
||||
|
||||
void WorldSession::HandleLearnPreviewTalents(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LEARN_PREVIEW_TALENTS");
|
||||
|
||||
uint32 talentsCount;
|
||||
recvPacket >> talentsCount;
|
||||
|
||||
uint32 talentId, talentRank;
|
||||
|
||||
for (uint32 i = 0; i < talentsCount; ++i)
|
||||
{
|
||||
recvPacket >> talentId >> talentRank;
|
||||
|
||||
_player->LearnTalent(talentId, talentRank);
|
||||
}
|
||||
|
||||
_player->SendTalentsInfoData(false);
|
||||
}
|
||||
|
||||
void WorldSession::HandleTalentWipeConfirmOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "MSG_TALENT_WIPE_CONFIRM");
|
||||
uint64 guid;
|
||||
recv_data >> guid;
|
||||
|
||||
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_TRAINER);
|
||||
if (!unit)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleTalentWipeConfirmOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)));
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
if (!(_player->resetTalents()))
|
||||
{
|
||||
WorldPacket data(MSG_TALENT_WIPE_CONFIRM, 8+4); //you have not any talent
|
||||
data << uint64(0);
|
||||
data << uint32(0);
|
||||
SendPacket(&data);
|
||||
return;
|
||||
}
|
||||
|
||||
_player->SendTalentsInfoData(false);
|
||||
unit->CastSpell(_player, 14867, true); //spell: "Untalent Visual Effect"
|
||||
}
|
||||
|
||||
void WorldSession::HandleUnlearnSkillOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
uint32 skill_id;
|
||||
recv_data >> skill_id;
|
||||
GetPlayer()->SetSkill(skill_id, 0, 0, 0);
|
||||
}
|
||||
|
||||
Executable
+686
@@ -0,0 +1,686 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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 "Common.h"
|
||||
#include "DBCStores.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "SpellMgr.h"
|
||||
#include "Log.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Spell.h"
|
||||
#include "Totem.h"
|
||||
#include "TemporarySummon.h"
|
||||
#include "SpellAuras.h"
|
||||
#include "CreatureAI.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "GameObjectAI.h"
|
||||
#include "SpellAuraEffects.h"
|
||||
|
||||
void WorldSession::HandleClientCastFlags(WorldPacket& recvPacket, uint8 castFlags, SpellCastTargets& targets)
|
||||
{
|
||||
// some spell cast packet including more data (for projectiles?)
|
||||
if (castFlags & 0x02)
|
||||
{
|
||||
// not sure about these two
|
||||
float elevation, speed;
|
||||
recvPacket >> elevation;
|
||||
recvPacket >> speed;
|
||||
|
||||
targets.SetElevation(elevation);
|
||||
targets.SetSpeed(speed);
|
||||
|
||||
uint8 hasMovementData;
|
||||
recvPacket >> hasMovementData;
|
||||
if (hasMovementData)
|
||||
{
|
||||
recvPacket.rfinish();
|
||||
// movement packet for caster of the spell
|
||||
/*recvPacket.read_skip<uint32>(); // MSG_MOVE_STOP - hardcoded in client
|
||||
uint64 guid;
|
||||
recvPacket.readPackGUID(guid);
|
||||
|
||||
MovementInfo movementInfo;
|
||||
movementInfo.guid = guid;
|
||||
ReadMovementInfo(recvPacket, &movementInfo);*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
// TODO: add targets.read() check
|
||||
Player* pUser = _player;
|
||||
|
||||
// ignore for remote control state
|
||||
if (pUser->m_mover != pUser)
|
||||
return;
|
||||
|
||||
uint8 bagIndex, slot, castFlags;
|
||||
uint8 castCount; // next cast if exists (single or not)
|
||||
uint64 itemGUID;
|
||||
uint32 glyphIndex; // something to do with glyphs?
|
||||
uint32 spellId; // casted spell id
|
||||
|
||||
recvPacket >> bagIndex >> slot >> castCount >> spellId >> itemGUID >> glyphIndex >> castFlags;
|
||||
|
||||
if (glyphIndex >= MAX_GLYPH_SLOT_INDEX)
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
Item* pItem = pUser->GetUseableItemByPos(bagIndex, slot);
|
||||
if (!pItem)
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pItem->GetGUID() != itemGUID)
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
sLog->outDetail("WORLD: CMSG_USE_ITEM packet, bagIndex: %u, slot: %u, castCount: %u, spellId: %u, Item: %u, glyphIndex: %u, data length = %i", bagIndex, slot, castCount, spellId, pItem->GetEntry(), glyphIndex, (uint32)recvPacket.size());
|
||||
|
||||
ItemTemplate const* proto = pItem->GetTemplate();
|
||||
if (!proto)
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
// some item classes can be used only in equipped state
|
||||
if (proto->InventoryType != INVTYPE_NON_EQUIP && !pItem->IsEquipped())
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
InventoryResult msg = pUser->CanUseItem(pItem);
|
||||
if (msg != EQUIP_ERR_OK)
|
||||
{
|
||||
pUser->SendEquipError(msg, pItem, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
// only allow conjured consumable, bandage, poisons (all should have the 2^21 item flag set in DB)
|
||||
if (proto->Class == ITEM_CLASS_CONSUMABLE && !(proto->Flags & ITEM_PROTO_FLAG_USEABLE_IN_ARENA) && pUser->InArena())
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_NOT_DURING_ARENA_MATCH, pItem, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
// don't allow items banned in arena
|
||||
if (proto->Flags & ITEM_PROTO_FLAG_NOT_USEABLE_IN_ARENA && pUser->InArena())
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_NOT_DURING_ARENA_MATCH, pItem, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pUser->isInCombat())
|
||||
{
|
||||
for (int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
|
||||
{
|
||||
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(proto->Spells[i].SpellId))
|
||||
{
|
||||
if (!spellInfo->CanBeUsedInCombat())
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_NOT_IN_COMBAT, pItem, NULL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check also BIND_WHEN_PICKED_UP and BIND_QUEST_ITEM for .additem or .additemset case by GM (not binded at adding to inventory)
|
||||
if (pItem->GetTemplate()->Bonding == BIND_WHEN_USE || pItem->GetTemplate()->Bonding == BIND_WHEN_PICKED_UP || pItem->GetTemplate()->Bonding == BIND_QUEST_ITEM)
|
||||
{
|
||||
if (!pItem->IsSoulBound())
|
||||
{
|
||||
pItem->SetState(ITEM_CHANGED, pUser);
|
||||
pItem->SetBinding(true);
|
||||
}
|
||||
}
|
||||
|
||||
SpellCastTargets targets;
|
||||
targets.Read(recvPacket, pUser);
|
||||
HandleClientCastFlags(recvPacket, castFlags, targets);
|
||||
|
||||
if (!pItem->IsTargetValidForItemUse(targets.GetUnitTarget()))
|
||||
{
|
||||
// free gray item after use fail
|
||||
pUser->SendEquipError(EQUIP_ERR_NONE, pItem, NULL);
|
||||
|
||||
// send spell error
|
||||
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId))
|
||||
{
|
||||
// for implicit area/coord target spells
|
||||
if (!targets.GetUnitTarget())
|
||||
Spell::SendCastResult(_player, spellInfo, castCount, SPELL_FAILED_NO_VALID_TARGETS);
|
||||
// for explicit target spells
|
||||
else
|
||||
Spell::SendCastResult(_player, spellInfo, castCount, SPELL_FAILED_BAD_TARGETS);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: If script stop casting it must send appropriate data to client to prevent stuck item in gray state.
|
||||
if (!sScriptMgr->OnItemUse(pUser, pItem, targets))
|
||||
{
|
||||
// no script or script not process request by self
|
||||
pUser->CastItemUseSpell(pItem, targets, castCount, glyphIndex);
|
||||
}
|
||||
}
|
||||
|
||||
#define OPEN_CHEST 11437
|
||||
#define OPEN_SAFE 11535
|
||||
#define OPEN_CAGE 11792
|
||||
#define OPEN_BOOTY_CHEST 5107
|
||||
#define OPEN_STRONGBOX 8517
|
||||
|
||||
void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDetail("WORLD: CMSG_OPEN_ITEM packet, data length = %i", (uint32)recvPacket.size());
|
||||
|
||||
Player* pUser = _player;
|
||||
|
||||
// ignore for remote control state
|
||||
if (pUser->m_mover != pUser)
|
||||
return;
|
||||
|
||||
uint8 bagIndex, slot;
|
||||
|
||||
recvPacket >> bagIndex >> slot;
|
||||
|
||||
sLog->outDetail("bagIndex: %u, slot: %u", bagIndex, slot);
|
||||
|
||||
Item* item = pUser->GetItemByPos(bagIndex, slot);
|
||||
if (!item)
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
ItemTemplate const* proto = item->GetTemplate();
|
||||
if (!proto)
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, item, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify that the bag is an actual bag or wrapped item that can be used "normally"
|
||||
if (!(proto->Flags & ITEM_PROTO_FLAG_OPENABLE) && !item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED))
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW, item, NULL);
|
||||
sLog->outError("Possible hacking attempt: Player %s [guid: %u] tried to open item [guid: %u, entry: %u] which is not openable!",
|
||||
pUser->GetName(), pUser->GetGUIDLow(), item->GetGUIDLow(), proto->ItemId);
|
||||
return;
|
||||
}
|
||||
|
||||
// locked item
|
||||
uint32 lockId = proto->LockID;
|
||||
if (lockId)
|
||||
{
|
||||
LockEntry const* lockInfo = sLockStore.LookupEntry(lockId);
|
||||
|
||||
if (!lockInfo)
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_ITEM_LOCKED, item, NULL);
|
||||
sLog->outError("WORLD::OpenItem: item [guid = %u] has an unknown lockId: %u!", item->GetGUIDLow(), lockId);
|
||||
return;
|
||||
}
|
||||
|
||||
// was not unlocked yet
|
||||
if (item->IsLocked())
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_ITEM_LOCKED, item, NULL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED))// wrapped?
|
||||
{
|
||||
QueryResult result = CharacterDatabase.PQuery("SELECT entry, flags FROM character_gifts WHERE item_guid = '%u'", item->GetGUIDLow());
|
||||
if (result)
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
uint32 entry = fields[0].GetUInt32();
|
||||
uint32 flags = fields[1].GetUInt32();
|
||||
|
||||
item->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, 0);
|
||||
item->SetEntry(entry);
|
||||
item->SetUInt32Value(ITEM_FIELD_FLAGS, flags);
|
||||
item->SetState(ITEM_CHANGED, pUser);
|
||||
}
|
||||
else
|
||||
{
|
||||
sLog->outError("Wrapped item %u don't have record in character_gifts table and will deleted", item->GetGUIDLow());
|
||||
pUser->DestroyItem(item->GetBagSlot(), item->GetSlot(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GIFT);
|
||||
|
||||
stmt->setUInt32(0, item->GetGUIDLow());
|
||||
|
||||
CharacterDatabase.Execute(stmt);
|
||||
}
|
||||
else
|
||||
pUser->SendLoot(item->GetGUID(), LOOT_CORPSE);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGameObjectUseOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
uint64 guid;
|
||||
|
||||
recv_data >> guid;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_GAMEOBJ_USE Message [guid=%u]", GUID_LOPART(guid));
|
||||
|
||||
// ignore for remote control state
|
||||
if (_player->m_mover != _player)
|
||||
return;
|
||||
|
||||
if (GameObject* obj = GetPlayer()->GetMap()->GetGameObject(guid))
|
||||
obj->Use(_player);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGameobjectReportUse(WorldPacket& recvPacket)
|
||||
{
|
||||
uint64 guid;
|
||||
recvPacket >> guid;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_GAMEOBJ_REPORT_USE Message [in game guid: %u]", GUID_LOPART(guid));
|
||||
|
||||
// ignore for remote control state
|
||||
if (_player->m_mover != _player)
|
||||
return;
|
||||
|
||||
GameObject* go = GetPlayer()->GetMap()->GetGameObject(guid);
|
||||
if (!go)
|
||||
return;
|
||||
|
||||
if (!go->IsWithinDistInMap(_player, INTERACTION_DISTANCE))
|
||||
return;
|
||||
|
||||
go->AI()->GossipHello(_player);
|
||||
|
||||
_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT, go->GetEntry());
|
||||
}
|
||||
|
||||
void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
uint32 spellId;
|
||||
uint8 castCount, castFlags;
|
||||
recvPacket >> castCount >> spellId >> castFlags;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: got cast spell packet, castCount: %u, spellId: %u, castFlags: %u, data length = %u", castCount, spellId, castFlags, (uint32)recvPacket.size());
|
||||
|
||||
// ignore for remote control state (for player case)
|
||||
Unit* mover = _player->m_mover;
|
||||
if (mover != _player && mover->GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
recvPacket.rfinish(); // prevent spam at ignore packet
|
||||
return;
|
||||
}
|
||||
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||
|
||||
if (!spellInfo)
|
||||
{
|
||||
sLog->outError("WORLD: unknown spell id %u", spellId);
|
||||
recvPacket.rfinish(); // prevent spam at ignore packet
|
||||
return;
|
||||
}
|
||||
|
||||
if (mover->GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
// not have spell in spellbook or spell passive and not casted by client
|
||||
if (!mover->ToPlayer()->HasActiveSpell (spellId) || spellInfo->IsPassive())
|
||||
{
|
||||
//cheater? kick? ban?
|
||||
recvPacket.rfinish(); // prevent spam at ignore packet
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// not have spell in spellbook or spell passive and not casted by client
|
||||
if ((mover->GetTypeId() == TYPEID_UNIT && !mover->ToCreature()->HasSpell(spellId)) || spellInfo->IsPassive())
|
||||
{
|
||||
//cheater? kick? ban?
|
||||
recvPacket.rfinish(); // prevent spam at ignore packet
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Client is resending autoshot cast opcode when other spell is casted during shoot rotation
|
||||
// Skip it to prevent "interrupt" message
|
||||
if (spellInfo->IsAutoRepeatRangedSpell() && _player->GetCurrentSpell(CURRENT_AUTOREPEAT_SPELL)
|
||||
&& _player->GetCurrentSpell(CURRENT_AUTOREPEAT_SPELL)->m_spellInfo == spellInfo)
|
||||
{
|
||||
recvPacket.rfinish();
|
||||
return;
|
||||
}
|
||||
|
||||
// can't use our own spells when we're in possession of another unit,
|
||||
if (_player->isPossessing())
|
||||
{
|
||||
recvPacket.rfinish(); // prevent spam at ignore packet
|
||||
return;
|
||||
}
|
||||
|
||||
// client provided targets
|
||||
SpellCastTargets targets;
|
||||
targets.Read(recvPacket, mover);
|
||||
HandleClientCastFlags(recvPacket, castFlags, targets);
|
||||
|
||||
// auto-selection buff level base at target level (in spellInfo)
|
||||
if (targets.GetUnitTarget())
|
||||
{
|
||||
SpellInfo const* actualSpellInfo = spellInfo->GetAuraRankForLevel(targets.GetUnitTarget()->getLevel());
|
||||
|
||||
// if rank not found then function return NULL but in explicit cast case original spell can be casted and later failed with appropriate error message
|
||||
if (actualSpellInfo)
|
||||
spellInfo = actualSpellInfo;
|
||||
}
|
||||
|
||||
Spell* spell = new Spell(mover, spellInfo, TRIGGERED_NONE, 0, false);
|
||||
spell->m_cast_count = castCount; // set count of casts
|
||||
spell->prepare(&targets);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCancelCastOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
uint32 spellId;
|
||||
|
||||
recvPacket.read_skip<uint8>(); // counter, increments with every CANCEL packet, don't use for now
|
||||
recvPacket >> spellId;
|
||||
|
||||
if (_player->IsNonMeleeSpellCasted(false))
|
||||
_player->InterruptNonMeleeSpells(false, spellId, false);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCancelAuraOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
uint32 spellId;
|
||||
recvPacket >> spellId;
|
||||
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||
if (!spellInfo)
|
||||
return;
|
||||
|
||||
// not allow remove spells with attr SPELL_ATTR0_CANT_CANCEL
|
||||
if (spellInfo->Attributes & SPELL_ATTR0_CANT_CANCEL)
|
||||
return;
|
||||
|
||||
// channeled spell case (it currently casted then)
|
||||
if (spellInfo->IsChanneled())
|
||||
{
|
||||
if (Spell* curSpell = _player->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
|
||||
if (curSpell->m_spellInfo->Id == spellId)
|
||||
_player->InterruptSpell(CURRENT_CHANNELED_SPELL);
|
||||
return;
|
||||
}
|
||||
|
||||
// non channeled case:
|
||||
// don't allow remove non positive spells
|
||||
// don't allow cancelling passive auras (some of them are visible)
|
||||
if (!spellInfo->IsPositive() || spellInfo->IsPassive())
|
||||
return;
|
||||
|
||||
// maybe should only remove one buff when there are multiple?
|
||||
_player->RemoveOwnedAura(spellId, 0, 0, AURA_REMOVE_BY_CANCEL);
|
||||
}
|
||||
|
||||
void WorldSession::HandlePetCancelAuraOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
uint64 guid;
|
||||
uint32 spellId;
|
||||
|
||||
recvPacket >> guid;
|
||||
recvPacket >> spellId;
|
||||
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||
if (!spellInfo)
|
||||
{
|
||||
sLog->outError("WORLD: unknown PET spell id %u", spellId);
|
||||
return;
|
||||
}
|
||||
|
||||
Creature* pet=ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid);
|
||||
|
||||
if (!pet)
|
||||
{
|
||||
sLog->outError("HandlePetCancelAura: Attempt to cancel an aura for non-existant pet %u by player '%s'", uint32(GUID_LOPART(guid)), GetPlayer()->GetName());
|
||||
return;
|
||||
}
|
||||
|
||||
if (pet != GetPlayer()->GetGuardianPet() && pet != GetPlayer()->GetCharm())
|
||||
{
|
||||
sLog->outError("HandlePetCancelAura: Pet %u is not a pet of player '%s'", uint32(GUID_LOPART(guid)), GetPlayer()->GetName());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pet->isAlive())
|
||||
{
|
||||
pet->SendPetActionFeedback(FEEDBACK_PET_DEAD);
|
||||
return;
|
||||
}
|
||||
|
||||
pet->RemoveOwnedAura(spellId, 0, 0, AURA_REMOVE_BY_CANCEL);
|
||||
|
||||
pet->AddCreatureSpellCooldown(spellId);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCancelGrowthAuraOpcode(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
}
|
||||
|
||||
void WorldSession::HandleCancelAutoRepeatSpellOpcode(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
// may be better send SMSG_CANCEL_AUTO_REPEAT?
|
||||
// cancel and prepare for deleting
|
||||
_player->InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCancelChanneling(WorldPacket & recv_data)
|
||||
{
|
||||
recv_data.read_skip<uint32>(); // spellid, not used
|
||||
|
||||
// ignore for remote control state (for player case)
|
||||
Unit* mover = _player->m_mover;
|
||||
if (mover != _player && mover->GetTypeId() == TYPEID_PLAYER)
|
||||
return;
|
||||
|
||||
mover->InterruptSpell(CURRENT_CHANNELED_SPELL);
|
||||
}
|
||||
|
||||
void WorldSession::HandleTotemDestroyed(WorldPacket& recvPacket)
|
||||
{
|
||||
// ignore for remote control state
|
||||
if (_player->m_mover != _player)
|
||||
return;
|
||||
|
||||
uint8 slotId;
|
||||
|
||||
recvPacket >> slotId;
|
||||
|
||||
++slotId;
|
||||
if (slotId >= MAX_TOTEM_SLOT)
|
||||
return;
|
||||
|
||||
if (!_player->m_SummonSlot[slotId])
|
||||
return;
|
||||
|
||||
Creature* totem = GetPlayer()->GetMap()->GetCreature(_player->m_SummonSlot[slotId]);
|
||||
// Don't unsummon sentry totem
|
||||
if (totem && totem->isTotem() && totem->GetEntry() != SENTRY_TOTEM_ENTRY)
|
||||
totem->ToTotem()->UnSummon();
|
||||
}
|
||||
|
||||
void WorldSession::HandleSelfResOpcode(WorldPacket & /*recv_data*/)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_SELF_RES"); // empty opcode
|
||||
|
||||
if (_player->HasAuraType(SPELL_AURA_PREVENT_RESURRECTION))
|
||||
return; // silent return, client should display error by itself and not send this opcode
|
||||
|
||||
if (_player->GetUInt32Value(PLAYER_SELF_RES_SPELL))
|
||||
{
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(_player->GetUInt32Value(PLAYER_SELF_RES_SPELL));
|
||||
if (spellInfo)
|
||||
_player->CastSpell(_player, spellInfo, false, 0);
|
||||
|
||||
_player->SetUInt32Value(PLAYER_SELF_RES_SPELL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleSpellClick(WorldPacket& recv_data)
|
||||
{
|
||||
uint64 guid;
|
||||
recv_data >> guid;
|
||||
|
||||
// this will get something not in world. crash
|
||||
Creature* unit = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid);
|
||||
|
||||
if (!unit)
|
||||
return;
|
||||
|
||||
// TODO: Unit::SetCharmedBy: 28782 is not in world but 0 is trying to charm it! -> crash
|
||||
if (!unit->IsInWorld())
|
||||
return;
|
||||
|
||||
unit->HandleSpellClick(_player);
|
||||
}
|
||||
|
||||
void WorldSession::HandleMirrorImageDataRequest(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_GET_MIRRORIMAGE_DATA");
|
||||
uint64 guid;
|
||||
recv_data >> guid;
|
||||
|
||||
// Get unit for which data is needed by client
|
||||
Unit* unit = ObjectAccessor::GetObjectInWorld(guid, (Unit*)NULL);
|
||||
if (!unit)
|
||||
return;
|
||||
|
||||
if (!unit->HasAuraType(SPELL_AURA_CLONE_CASTER))
|
||||
return;
|
||||
|
||||
// Get creator of the unit (SPELL_AURA_CLONE_CASTER does not stack)
|
||||
Unit* creator = unit->GetAuraEffectsByType(SPELL_AURA_CLONE_CASTER).front()->GetCaster();
|
||||
if (!creator)
|
||||
return;
|
||||
|
||||
WorldPacket data(SMSG_MIRRORIMAGE_DATA, 68);
|
||||
data << uint64(guid);
|
||||
data << uint32(creator->GetDisplayId());
|
||||
data << uint8(creator->getRace());
|
||||
data << uint8(creator->getGender());
|
||||
data << uint8(creator->getClass());
|
||||
|
||||
if (creator->GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
Player* player = creator->ToPlayer();
|
||||
data << uint8(player->GetByteValue(PLAYER_BYTES, 0)); // skin
|
||||
data << uint8(player->GetByteValue(PLAYER_BYTES, 1)); // face
|
||||
data << uint8(player->GetByteValue(PLAYER_BYTES, 2)); // hair
|
||||
data << uint8(player->GetByteValue(PLAYER_BYTES, 3)); // haircolor
|
||||
data << uint8(player->GetByteValue(PLAYER_BYTES_2, 0)); // facialhair
|
||||
data << uint32(player->GetGuildId()); // unk
|
||||
|
||||
static EquipmentSlots const itemSlots[] =
|
||||
{
|
||||
EQUIPMENT_SLOT_HEAD,
|
||||
EQUIPMENT_SLOT_SHOULDERS,
|
||||
EQUIPMENT_SLOT_BODY,
|
||||
EQUIPMENT_SLOT_CHEST,
|
||||
EQUIPMENT_SLOT_WAIST,
|
||||
EQUIPMENT_SLOT_LEGS,
|
||||
EQUIPMENT_SLOT_FEET,
|
||||
EQUIPMENT_SLOT_WRISTS,
|
||||
EQUIPMENT_SLOT_HANDS,
|
||||
EQUIPMENT_SLOT_BACK,
|
||||
EQUIPMENT_SLOT_TABARD,
|
||||
EQUIPMENT_SLOT_END
|
||||
};
|
||||
|
||||
// Display items in visible slots
|
||||
for (EquipmentSlots const* itr = &itemSlots[0]; *itr != EQUIPMENT_SLOT_END; ++itr)
|
||||
{
|
||||
if (*itr == EQUIPMENT_SLOT_HEAD && player->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM))
|
||||
data << uint32(0);
|
||||
else if (*itr == EQUIPMENT_SLOT_BACK && player->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK))
|
||||
data << uint32(0);
|
||||
else if (Item const* item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, *itr))
|
||||
data << uint32(item->GetTemplate()->DisplayInfoID);
|
||||
else
|
||||
data << uint32(0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Skip player data for creatures
|
||||
data << uint8(0);
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
}
|
||||
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleUpdateProjectilePosition(WorldPacket& recvPacket)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_UPDATE_PROJECTILE_POSITION");
|
||||
|
||||
uint64 casterGuid;
|
||||
uint32 spellId;
|
||||
uint8 castCount;
|
||||
float x, y, z; // Position of missile hit
|
||||
|
||||
recvPacket.readPackGUID(casterGuid);
|
||||
recvPacket >> spellId;
|
||||
recvPacket >> castCount;
|
||||
recvPacket >> x;
|
||||
recvPacket >> y;
|
||||
recvPacket >> z;
|
||||
|
||||
WorldPacket data(SMSG_SET_PROJECTILE_POSITION, 21);
|
||||
data << uint64(casterGuid);
|
||||
data << uint8(castCount);
|
||||
data << float(x);
|
||||
data << float(y);
|
||||
data << float(z);
|
||||
SendPacket(&data);
|
||||
}
|
||||
Executable
+294
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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 "Common.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Log.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Player.h"
|
||||
#include "UpdateMask.h"
|
||||
#include "Path.h"
|
||||
#include "WaypointMovementGenerator.h"
|
||||
|
||||
void WorldSession::HandleTaxiNodeStatusQueryOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_TAXINODE_STATUS_QUERY");
|
||||
|
||||
uint64 guid;
|
||||
|
||||
recv_data >> guid;
|
||||
SendTaxiStatus(guid);
|
||||
}
|
||||
|
||||
void WorldSession::SendTaxiStatus(uint64 guid)
|
||||
{
|
||||
// cheating checks
|
||||
Creature* unit = GetPlayer()->GetMap()->GetCreature(guid);
|
||||
if (!unit)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WorldSession::SendTaxiStatus - Unit (GUID: %u) not found.", uint32(GUID_LOPART(guid)));
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 curloc = sObjectMgr->GetNearestTaxiNode(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ(), unit->GetMapId(), GetPlayer()->GetTeam());
|
||||
|
||||
// not found nearest
|
||||
if (curloc == 0)
|
||||
return;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: current location %u ", curloc);
|
||||
|
||||
WorldPacket data(SMSG_TAXINODE_STATUS, 9);
|
||||
data << guid;
|
||||
data << uint8(GetPlayer()->m_taxi.IsTaximaskNodeKnown(curloc) ? 1 : 0);
|
||||
SendPacket(&data);
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_TAXINODE_STATUS");
|
||||
}
|
||||
|
||||
void WorldSession::HandleTaxiQueryAvailableNodes(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_TAXIQUERYAVAILABLENODES");
|
||||
|
||||
uint64 guid;
|
||||
recv_data >> guid;
|
||||
|
||||
// cheating checks
|
||||
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_FLIGHTMASTER);
|
||||
if (!unit)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleTaxiQueryAvailableNodes - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)));
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
// unknown taxi node case
|
||||
if (SendLearnNewTaxiNode(unit))
|
||||
return;
|
||||
|
||||
// known taxi node case
|
||||
SendTaxiMenu(unit);
|
||||
}
|
||||
|
||||
void WorldSession::SendTaxiMenu(Creature* unit)
|
||||
{
|
||||
// find current node
|
||||
uint32 curloc = sObjectMgr->GetNearestTaxiNode(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ(), unit->GetMapId(), GetPlayer()->GetTeam());
|
||||
|
||||
if (curloc == 0)
|
||||
return;
|
||||
|
||||
bool lastTaxiCheaterState = GetPlayer()->isTaxiCheater();
|
||||
if (unit->GetEntry() == 29480) GetPlayer()->SetTaxiCheater(true); // Grimwing in Ebon Hold, special case. NOTE: Not perfect, Zul'Aman should not be included according to WoWhead, and I think taxicheat includes it.
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_TAXINODE_STATUS_QUERY %u ", curloc);
|
||||
|
||||
WorldPacket data(SMSG_SHOWTAXINODES, (4+8+4+8*4));
|
||||
data << uint32(1);
|
||||
data << uint64(unit->GetGUID());
|
||||
data << uint32(curloc);
|
||||
GetPlayer()->m_taxi.AppendTaximaskTo(data, GetPlayer()->isTaxiCheater());
|
||||
SendPacket(&data);
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_SHOWTAXINODES");
|
||||
|
||||
GetPlayer()->SetTaxiCheater(lastTaxiCheaterState);
|
||||
}
|
||||
|
||||
void WorldSession::SendDoFlight(uint32 mountDisplayId, uint32 path, uint32 pathNode)
|
||||
{
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
while (GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE)
|
||||
GetPlayer()->GetMotionMaster()->MovementExpired(false);
|
||||
|
||||
if (mountDisplayId)
|
||||
GetPlayer()->Mount(mountDisplayId);
|
||||
|
||||
GetPlayer()->GetMotionMaster()->MoveTaxiFlight(path, pathNode);
|
||||
}
|
||||
|
||||
bool WorldSession::SendLearnNewTaxiNode(Creature* unit)
|
||||
{
|
||||
// find current node
|
||||
uint32 curloc = sObjectMgr->GetNearestTaxiNode(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ(), unit->GetMapId(), GetPlayer()->GetTeam());
|
||||
|
||||
if (curloc == 0)
|
||||
return true; // `true` send to avoid WorldSession::SendTaxiMenu call with one more curlock seartch with same false result.
|
||||
|
||||
if (GetPlayer()->m_taxi.SetTaximaskNode(curloc))
|
||||
{
|
||||
WorldPacket msg(SMSG_NEW_TAXI_PATH, 0);
|
||||
SendPacket(&msg);
|
||||
|
||||
WorldPacket update(SMSG_TAXINODE_STATUS, 9);
|
||||
update << uint64(unit->GetGUID());
|
||||
update << uint8(1);
|
||||
SendPacket(&update);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void WorldSession::SendDiscoverNewTaxiNode(uint32 nodeid)
|
||||
{
|
||||
if (GetPlayer()->m_taxi.SetTaximaskNode(nodeid))
|
||||
{
|
||||
WorldPacket msg(SMSG_NEW_TAXI_PATH, 0);
|
||||
SendPacket(&msg);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleActivateTaxiExpressOpcode (WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_ACTIVATETAXIEXPRESS");
|
||||
|
||||
uint64 guid;
|
||||
uint32 node_count;
|
||||
|
||||
recv_data >> guid >> node_count;
|
||||
|
||||
Creature* npc = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_FLIGHTMASTER);
|
||||
if (!npc)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleActivateTaxiExpressOpcode - Unit (GUID: %u) not found or you can't interact with it.", uint32(GUID_LOPART(guid)));
|
||||
return;
|
||||
}
|
||||
std::vector<uint32> nodes;
|
||||
|
||||
for (uint32 i = 0; i < node_count; ++i)
|
||||
{
|
||||
uint32 node;
|
||||
recv_data >> node;
|
||||
nodes.push_back(node);
|
||||
}
|
||||
|
||||
if (nodes.empty())
|
||||
return;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_ACTIVATETAXIEXPRESS from %d to %d", nodes.front(), nodes.back());
|
||||
|
||||
GetPlayer()->ActivateTaxiPathTo(nodes, npc);
|
||||
}
|
||||
|
||||
void WorldSession::HandleMoveSplineDoneOpcode(WorldPacket& recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_MOVE_SPLINE_DONE");
|
||||
|
||||
uint64 guid; // used only for proper packet read
|
||||
recv_data.readPackGUID(guid);
|
||||
|
||||
MovementInfo movementInfo; // used only for proper packet read
|
||||
ReadMovementInfo(recv_data, &movementInfo);
|
||||
|
||||
recv_data.read_skip<uint32>(); // unk
|
||||
|
||||
// in taxi flight packet received in 2 case:
|
||||
// 1) end taxi path in far (multi-node) flight
|
||||
// 2) switch from one map to other in case multim-map taxi path
|
||||
// we need process only (1)
|
||||
|
||||
uint32 curDest = GetPlayer()->m_taxi.GetTaxiDestination();
|
||||
if (!curDest)
|
||||
return;
|
||||
|
||||
TaxiNodesEntry const* curDestNode = sTaxiNodesStore.LookupEntry(curDest);
|
||||
|
||||
// far teleport case
|
||||
if (curDestNode && curDestNode->map_id != GetPlayer()->GetMapId())
|
||||
{
|
||||
if (GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE)
|
||||
{
|
||||
// short preparations to continue flight
|
||||
FlightPathMovementGenerator* flight = (FlightPathMovementGenerator*)(GetPlayer()->GetMotionMaster()->top());
|
||||
|
||||
flight->SetCurrentNodeAfterTeleport();
|
||||
TaxiPathNodeEntry const& node = flight->GetPath()[flight->GetCurrentNode()];
|
||||
flight->SkipCurrentNode();
|
||||
|
||||
GetPlayer()->TeleportTo(curDestNode->map_id, node.x, node.y, node.z, GetPlayer()->GetOrientation());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 destinationnode = GetPlayer()->m_taxi.NextTaxiDestination();
|
||||
if (destinationnode > 0) // if more destinations to go
|
||||
{
|
||||
// current source node for next destination
|
||||
uint32 sourcenode = GetPlayer()->m_taxi.GetTaxiSource();
|
||||
|
||||
// Add to taximask middle hubs in taxicheat mode (to prevent having player with disabled taxicheat and not having back flight path)
|
||||
if (GetPlayer()->isTaxiCheater())
|
||||
{
|
||||
if (GetPlayer()->m_taxi.SetTaximaskNode(sourcenode))
|
||||
{
|
||||
WorldPacket data(SMSG_NEW_TAXI_PATH, 0);
|
||||
_player->GetSession()->SendPacket(&data);
|
||||
}
|
||||
}
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Taxi has to go from %u to %u", sourcenode, destinationnode);
|
||||
|
||||
uint32 mountDisplayId = sObjectMgr->GetTaxiMountDisplayId(sourcenode, GetPlayer()->GetTeam());
|
||||
|
||||
uint32 path, cost;
|
||||
sObjectMgr->GetTaxiPath(sourcenode, destinationnode, path, cost);
|
||||
|
||||
if (path && mountDisplayId)
|
||||
SendDoFlight(mountDisplayId, path, 1); // skip start fly node
|
||||
else
|
||||
GetPlayer()->m_taxi.ClearTaxiDestinations(); // clear problematic path and next
|
||||
return;
|
||||
}
|
||||
else
|
||||
GetPlayer()->m_taxi.ClearTaxiDestinations(); // not destinations, clear source node
|
||||
|
||||
GetPlayer()->CleanupAfterTaxiFlight();
|
||||
GetPlayer()->SetFallInformation(0, GetPlayer()->GetPositionZ());
|
||||
if (GetPlayer()->pvpInfo.inHostileArea)
|
||||
GetPlayer()->CastSpell(GetPlayer(), 2479, true);
|
||||
}
|
||||
|
||||
void WorldSession::HandleActivateTaxiOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_ACTIVATETAXI");
|
||||
|
||||
uint64 guid;
|
||||
std::vector<uint32> nodes;
|
||||
nodes.resize(2);
|
||||
|
||||
recv_data >> guid >> nodes[0] >> nodes[1];
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_ACTIVATETAXI from %d to %d", nodes[0], nodes[1]);
|
||||
Creature* npc = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_FLIGHTMASTER);
|
||||
if (!npc)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleActivateTaxiOpcode - Unit (GUID: %u) not found or you can't interact with it.", uint32(GUID_LOPART(guid)));
|
||||
return;
|
||||
}
|
||||
|
||||
GetPlayer()->ActivateTaxiPathTo(nodes, npc);
|
||||
}
|
||||
Executable
+202
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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 "Language.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "Common.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "TicketMgr.h"
|
||||
#include "Player.h"
|
||||
#include "World.h"
|
||||
#include "WorldSession.h"
|
||||
#include "Util.h"
|
||||
|
||||
void WorldSession::HandleGMTicketCreateOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
// Don't accept tickets if the ticket queue is disabled. (Ticket UI is greyed out but not fully dependable)
|
||||
if (sTicketMgr->GetStatus() == GMTICKET_QUEUE_STATUS_DISABLED)
|
||||
return;
|
||||
|
||||
if (GetPlayer()->getLevel() < sWorld->getIntConfig(CONFIG_TICKET_LEVEL_REQ))
|
||||
{
|
||||
SendNotification(GetTrinityString(LANG_TICKET_REQ), sWorld->getIntConfig(CONFIG_TICKET_LEVEL_REQ));
|
||||
return;
|
||||
}
|
||||
|
||||
GMTicketResponse response = GMTICKET_RESPONSE_FAILURE;
|
||||
// Player must not have ticket
|
||||
if (!sTicketMgr->GetTicketByPlayer(GetPlayer()->GetGUID()))
|
||||
{
|
||||
GmTicket* ticket = new GmTicket(GetPlayer(), recv_data);
|
||||
sTicketMgr->AddTicket(ticket);
|
||||
sTicketMgr->UpdateLastChange();
|
||||
|
||||
sWorld->SendGMText(LANG_COMMAND_TICKETNEW, GetPlayer()->GetName(), ticket->GetId());
|
||||
|
||||
response = GMTICKET_RESPONSE_SUCCESS;
|
||||
}
|
||||
|
||||
WorldPacket data(SMSG_GMTICKET_CREATE, 4);
|
||||
data << uint32(response);
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGMTicketUpdateOpcode(WorldPacket & recv_data)
|
||||
{
|
||||
std::string message;
|
||||
recv_data >> message;
|
||||
|
||||
GMTicketResponse response = GMTICKET_RESPONSE_FAILURE;
|
||||
if (GmTicket *ticket = sTicketMgr->GetTicketByPlayer(GetPlayer()->GetGUID()))
|
||||
{
|
||||
SQLTransaction trans = SQLTransaction(NULL);
|
||||
ticket->SetMessage(message);
|
||||
ticket->SaveToDB(trans);
|
||||
|
||||
sWorld->SendGMText(LANG_COMMAND_TICKETUPDATED, GetPlayer()->GetName(), ticket->GetId());
|
||||
|
||||
response = GMTICKET_RESPONSE_SUCCESS;
|
||||
}
|
||||
|
||||
WorldPacket data(SMSG_GMTICKET_UPDATETEXT, 4);
|
||||
data << uint32(response);
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGMTicketDeleteOpcode(WorldPacket & /*recv_data*/)
|
||||
{
|
||||
if (GmTicket* ticket = sTicketMgr->GetTicketByPlayer(GetPlayer()->GetGUID()))
|
||||
{
|
||||
WorldPacket data(SMSG_GMTICKET_DELETETICKET, 4);
|
||||
data << uint32(GMTICKET_RESPONSE_TICKET_DELETED);
|
||||
SendPacket(&data);
|
||||
|
||||
sWorld->SendGMText(LANG_COMMAND_TICKETPLAYERABANDON, GetPlayer()->GetName(), ticket->GetId());
|
||||
|
||||
sTicketMgr->CloseTicket(ticket->GetId(), GetPlayer()->GetGUID());
|
||||
sTicketMgr->SendTicket(this, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleGMTicketGetTicketOpcode(WorldPacket & /*recv_data*/)
|
||||
{
|
||||
SendQueryTimeResponse();
|
||||
|
||||
if (GmTicket* ticket = sTicketMgr->GetTicketByPlayer(GetPlayer()->GetGUID()))
|
||||
{
|
||||
if (ticket->IsCompleted())
|
||||
ticket->SendResponse(this);
|
||||
else
|
||||
sTicketMgr->SendTicket(this, ticket);
|
||||
}
|
||||
else
|
||||
sTicketMgr->SendTicket(this, NULL);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGMTicketSystemStatusOpcode(WorldPacket & /*recv_data*/)
|
||||
{
|
||||
// Note: This only disables the ticket UI at client side and is not fully reliable
|
||||
// are we sure this is a uint32? Should ask Zor
|
||||
WorldPacket data(SMSG_GMTICKET_SYSTEMSTATUS, 4);
|
||||
data << uint32(sTicketMgr->GetStatus() ? GMTICKET_QUEUE_STATUS_ENABLED : GMTICKET_QUEUE_STATUS_DISABLED);
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGMSurveySubmit(WorldPacket& recv_data)
|
||||
{
|
||||
uint32 nextSurveyID = sTicketMgr->GetNextSurveyID();
|
||||
// just put the survey into the database
|
||||
uint32 mainSurvey; // GMSurveyCurrentSurvey.dbc, column 1 (all 9) ref to GMSurveySurveys.dbc
|
||||
recv_data >> mainSurvey;
|
||||
|
||||
// sub_survey1, r1, comment1, sub_survey2, r2, comment2, sub_survey3, r3, comment3, sub_survey4, r4, comment4, sub_survey5, r5, comment5, sub_survey6, r6, comment6, sub_survey7, r7, comment7, sub_survey8, r8, comment8, sub_survey9, r9, comment9, sub_survey10, r10, comment10,
|
||||
for (uint8 i = 0; i < 10; i++)
|
||||
{
|
||||
uint32 subSurveyId; // ref to i'th GMSurveySurveys.dbc field (all fields in that dbc point to fields in GMSurveyQuestions.dbc)
|
||||
recv_data >> subSurveyId;
|
||||
if (!subSurveyId)
|
||||
break;
|
||||
|
||||
uint8 rank; // probably some sort of ref to GMSurveyAnswers.dbc
|
||||
recv_data >> rank;
|
||||
std::string comment; // comment ("Usage: GMSurveyAnswerSubmit(question, rank, comment)")
|
||||
recv_data >> comment;
|
||||
|
||||
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GM_SUBSURVEY);
|
||||
stmt->setUInt32(0, nextSurveyID);
|
||||
stmt->setUInt32(1, subSurveyId);
|
||||
stmt->setUInt32(2, rank);
|
||||
stmt->setString(3, comment);
|
||||
CharacterDatabase.Execute(stmt);
|
||||
}
|
||||
|
||||
std::string comment; // just a guess
|
||||
recv_data >> comment;
|
||||
|
||||
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GM_SURVEY);
|
||||
stmt->setUInt32(0, GUID_LOPART(GetPlayer()->GetGUID()));
|
||||
stmt->setUInt32(1, nextSurveyID);
|
||||
stmt->setUInt32(2, mainSurvey);
|
||||
stmt->setString(3, comment);
|
||||
|
||||
CharacterDatabase.Execute(stmt);
|
||||
}
|
||||
|
||||
void WorldSession::HandleReportLag(WorldPacket& recv_data)
|
||||
{
|
||||
// just put the lag report into the database...
|
||||
// can't think of anything else to do with it
|
||||
uint32 lagType, mapId;
|
||||
recv_data >> lagType;
|
||||
recv_data >> mapId;
|
||||
float x, y, z;
|
||||
recv_data >> x;
|
||||
recv_data >> y;
|
||||
recv_data >> z;
|
||||
|
||||
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_LAG_REPORT);
|
||||
stmt->setUInt32(0, GUID_LOPART(GetPlayer()->GetGUID()));
|
||||
stmt->setUInt8 (1, lagType);
|
||||
stmt->setUInt16(2, mapId);
|
||||
stmt->setFloat (3, x);
|
||||
stmt->setFloat (4, y);
|
||||
stmt->setFloat (5, z);
|
||||
CharacterDatabase.Execute(stmt);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGMResponseResolve(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
// empty packet
|
||||
if (GmTicket* ticket = sTicketMgr->GetTicketByPlayer(GetPlayer()->GetGUID()))
|
||||
{
|
||||
uint8 getSurvey = 0;
|
||||
if (float(rand_chance()) < sWorld->getFloatConfig(CONFIG_CHANCE_OF_GM_SURVEY))
|
||||
getSurvey = 1;
|
||||
|
||||
WorldPacket data(SMSG_GMRESPONSE_STATUS_UPDATE, 4);
|
||||
data << uint8(getSurvey);
|
||||
SendPacket(&data);
|
||||
|
||||
WorldPacket data2(SMSG_GMTICKET_DELETETICKET, 4);
|
||||
data2 << uint32(GMTICKET_RESPONSE_TICKET_DELETED);
|
||||
SendPacket(&data2);
|
||||
|
||||
sTicketMgr->CloseTicket(ticket->GetId(), GetPlayer()->GetGUID());
|
||||
sTicketMgr->SendTicket(this, NULL);
|
||||
}
|
||||
}
|
||||
Executable
+731
@@ -0,0 +1,731 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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 "Common.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
#include "World.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "Log.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Player.h"
|
||||
#include "Item.h"
|
||||
#include "Spell.h"
|
||||
#include "SocialMgr.h"
|
||||
#include "Language.h"
|
||||
#include "AccountMgr.h"
|
||||
|
||||
void WorldSession::SendTradeStatus(TradeStatus status)
|
||||
{
|
||||
WorldPacket data;
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case TRADE_STATUS_BEGIN_TRADE:
|
||||
data.Initialize(SMSG_TRADE_STATUS, 4+8);
|
||||
data << uint32(status);
|
||||
data << uint64(0);
|
||||
break;
|
||||
case TRADE_STATUS_OPEN_WINDOW:
|
||||
data.Initialize(SMSG_TRADE_STATUS, 4+4);
|
||||
data << uint32(status);
|
||||
data << uint32(0); // added in 2.4.0
|
||||
break;
|
||||
case TRADE_STATUS_CLOSE_WINDOW:
|
||||
data.Initialize(SMSG_TRADE_STATUS, 4+4+1+4);
|
||||
data << uint32(status);
|
||||
data << uint32(0);
|
||||
data << uint8(0);
|
||||
data << uint32(0);
|
||||
break;
|
||||
case TRADE_STATUS_ONLY_CONJURED:
|
||||
case TRADE_STATUS_NOT_ELIGIBLE:
|
||||
data.Initialize(SMSG_TRADE_STATUS, 4+1);
|
||||
data << uint32(status);
|
||||
data << uint8(0);
|
||||
break;
|
||||
default:
|
||||
data.Initialize(SMSG_TRADE_STATUS, 4);
|
||||
data << uint32(status);
|
||||
break;
|
||||
}
|
||||
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleIgnoreTradeOpcode(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Ignore Trade %u", _player->GetGUIDLow());
|
||||
// recvPacket.print_storage();
|
||||
}
|
||||
|
||||
void WorldSession::HandleBusyTradeOpcode(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Busy Trade %u", _player->GetGUIDLow());
|
||||
// recvPacket.print_storage();
|
||||
}
|
||||
|
||||
void WorldSession::SendUpdateTrade(bool trader_data /*= true*/)
|
||||
{
|
||||
TradeData* view_trade = trader_data ? _player->GetTradeData()->GetTraderData() : _player->GetTradeData();
|
||||
|
||||
WorldPacket data(SMSG_TRADE_STATUS_EXTENDED, 1+4+4+4+4+4+7*(1+4+4+4+4+8+4+4+4+4+8+4+4+4+4+4+4));
|
||||
data << uint8(trader_data); // 1 means traders data, 0 means own
|
||||
data << uint32(0); // added in 2.4.0, this value must be equal to value from TRADE_STATUS_OPEN_WINDOW status packet (different value for different players to block multiple trades?)
|
||||
data << uint32(TRADE_SLOT_COUNT); // trade slots count/number?, = next field in most cases
|
||||
data << uint32(TRADE_SLOT_COUNT); // trade slots count/number?, = prev field in most cases
|
||||
data << uint32(view_trade->GetMoney()); // trader gold
|
||||
data << uint32(view_trade->GetSpell()); // spell casted on lowest slot item
|
||||
|
||||
for (uint8 i = 0; i < TRADE_SLOT_COUNT; ++i)
|
||||
{
|
||||
data << uint8(i); // trade slot number, if not specified, then end of packet
|
||||
|
||||
if (Item* item = view_trade->GetItem(TradeSlots(i)))
|
||||
{
|
||||
data << uint32(item->GetTemplate()->ItemId); // entry
|
||||
data << uint32(item->GetTemplate()->DisplayInfoID);// display id
|
||||
data << uint32(item->GetCount()); // stack count
|
||||
// wrapped: hide stats but show giftcreator name
|
||||
data << uint32(item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED) ? 1 : 0);
|
||||
data << uint64(item->GetUInt64Value(ITEM_FIELD_GIFTCREATOR));
|
||||
// perm. enchantment and gems
|
||||
data << uint32(item->GetEnchantmentId(PERM_ENCHANTMENT_SLOT));
|
||||
for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+MAX_GEM_SOCKETS; ++enchant_slot)
|
||||
data << uint32(item->GetEnchantmentId(EnchantmentSlot(enchant_slot)));
|
||||
// creator
|
||||
data << uint64(item->GetUInt64Value(ITEM_FIELD_CREATOR));
|
||||
data << uint32(item->GetSpellCharges()); // charges
|
||||
data << uint32(item->GetItemSuffixFactor()); // SuffixFactor
|
||||
data << uint32(item->GetItemRandomPropertyId());// random properties id
|
||||
data << uint32(item->GetTemplate()->LockID); // lock id
|
||||
// max durability
|
||||
data << uint32(item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY));
|
||||
// durability
|
||||
data << uint32(item->GetUInt32Value(ITEM_FIELD_DURABILITY));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (uint8 j = 0; j < 18; ++j)
|
||||
data << uint32(0);
|
||||
}
|
||||
}
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
//==============================================================
|
||||
// transfer the items to the players
|
||||
|
||||
void WorldSession::moveItems(Item* myItems[], Item* hisItems[])
|
||||
{
|
||||
Player* trader = _player->GetTrader();
|
||||
if (!trader)
|
||||
return;
|
||||
|
||||
for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i)
|
||||
{
|
||||
ItemPosCountVec traderDst;
|
||||
ItemPosCountVec playerDst;
|
||||
bool traderCanTrade = (myItems[i] == NULL || trader->CanStoreItem(NULL_BAG, NULL_SLOT, traderDst, myItems[i], false) == EQUIP_ERR_OK);
|
||||
bool playerCanTrade = (hisItems[i] == NULL || _player->CanStoreItem(NULL_BAG, NULL_SLOT, playerDst, hisItems[i], false) == EQUIP_ERR_OK);
|
||||
if (traderCanTrade && playerCanTrade)
|
||||
{
|
||||
// Ok, if trade item exists and can be stored
|
||||
// If we trade in both directions we had to check, if the trade will work before we actually do it
|
||||
// A roll back is not possible after we stored it
|
||||
if (myItems[i])
|
||||
{
|
||||
// logging
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "partner storing: %u", myItems[i]->GetGUIDLow());
|
||||
if (!AccountMgr::IsPlayerAccount(_player->GetSession()->GetSecurity()) && sWorld->getBoolConfig(CONFIG_GM_LOG_TRADE))
|
||||
{
|
||||
sLog->outCommand(_player->GetSession()->GetAccountId(), "GM %s (Account: %u) trade: %s (Entry: %d Count: %u) to player: %s (Account: %u)",
|
||||
_player->GetName(), _player->GetSession()->GetAccountId(),
|
||||
myItems[i]->GetTemplate()->Name1.c_str(), myItems[i]->GetEntry(), myItems[i]->GetCount(),
|
||||
trader->GetName(), trader->GetSession()->GetAccountId());
|
||||
}
|
||||
|
||||
// adjust time (depends on /played)
|
||||
if (myItems[i]->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_BOP_TRADEABLE))
|
||||
myItems[i]->SetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME, trader->GetTotalPlayedTime()-(_player->GetTotalPlayedTime()-myItems[i]->GetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME)));
|
||||
// store
|
||||
trader->MoveItemToInventory(traderDst, myItems[i], true, true);
|
||||
}
|
||||
if (hisItems[i])
|
||||
{
|
||||
// logging
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "player storing: %u", hisItems[i]->GetGUIDLow());
|
||||
if (!AccountMgr::IsPlayerAccount(trader->GetSession()->GetSecurity()) && sWorld->getBoolConfig(CONFIG_GM_LOG_TRADE))
|
||||
{
|
||||
sLog->outCommand(trader->GetSession()->GetAccountId(), "GM %s (Account: %u) trade: %s (Entry: %d Count: %u) to player: %s (Account: %u)",
|
||||
trader->GetName(), trader->GetSession()->GetAccountId(),
|
||||
hisItems[i]->GetTemplate()->Name1.c_str(), hisItems[i]->GetEntry(), hisItems[i]->GetCount(),
|
||||
_player->GetName(), _player->GetSession()->GetAccountId());
|
||||
}
|
||||
|
||||
// adjust time (depends on /played)
|
||||
if (hisItems[i]->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_BOP_TRADEABLE))
|
||||
hisItems[i]->SetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME, _player->GetTotalPlayedTime()-(trader->GetTotalPlayedTime()-hisItems[i]->GetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME)));
|
||||
// store
|
||||
_player->MoveItemToInventory(playerDst, hisItems[i], true, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// in case of fatal error log error message
|
||||
// return the already removed items to the original owner
|
||||
if (myItems[i])
|
||||
{
|
||||
if (!traderCanTrade)
|
||||
sLog->outError("trader can't store item: %u", myItems[i]->GetGUIDLow());
|
||||
if (_player->CanStoreItem(NULL_BAG, NULL_SLOT, playerDst, myItems[i], false) == EQUIP_ERR_OK)
|
||||
_player->MoveItemToInventory(playerDst, myItems[i], true, true);
|
||||
else
|
||||
sLog->outError("player can't take item back: %u", myItems[i]->GetGUIDLow());
|
||||
}
|
||||
// return the already removed items to the original owner
|
||||
if (hisItems[i])
|
||||
{
|
||||
if (!playerCanTrade)
|
||||
sLog->outError("player can't store item: %u", hisItems[i]->GetGUIDLow());
|
||||
if (trader->CanStoreItem(NULL_BAG, NULL_SLOT, traderDst, hisItems[i], false) == EQUIP_ERR_OK)
|
||||
trader->MoveItemToInventory(traderDst, hisItems[i], true, true);
|
||||
else
|
||||
sLog->outError("trader can't take item back: %u", hisItems[i]->GetGUIDLow());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================
|
||||
|
||||
static void setAcceptTradeMode(TradeData* myTrade, TradeData* hisTrade, Item* *myItems, Item* *hisItems)
|
||||
{
|
||||
myTrade->SetInAcceptProcess(true);
|
||||
hisTrade->SetInAcceptProcess(true);
|
||||
|
||||
// store items in local list and set 'in-trade' flag
|
||||
for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i)
|
||||
{
|
||||
if (Item* item = myTrade->GetItem(TradeSlots(i)))
|
||||
{
|
||||
sLog->outStaticDebug("player trade item %u bag: %u slot: %u", item->GetGUIDLow(), item->GetBagSlot(), item->GetSlot());
|
||||
//Can return NULL
|
||||
myItems[i] = item;
|
||||
myItems[i]->SetInTrade();
|
||||
}
|
||||
|
||||
if (Item* item = hisTrade->GetItem(TradeSlots(i)))
|
||||
{
|
||||
sLog->outStaticDebug("partner trade item %u bag: %u slot: %u", item->GetGUIDLow(), item->GetBagSlot(), item->GetSlot());
|
||||
hisItems[i] = item;
|
||||
hisItems[i]->SetInTrade();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void clearAcceptTradeMode(TradeData* myTrade, TradeData* hisTrade)
|
||||
{
|
||||
myTrade->SetInAcceptProcess(false);
|
||||
hisTrade->SetInAcceptProcess(false);
|
||||
}
|
||||
|
||||
static void clearAcceptTradeMode(Item* *myItems, Item* *hisItems)
|
||||
{
|
||||
// clear 'in-trade' flag
|
||||
for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i)
|
||||
{
|
||||
if (myItems[i])
|
||||
myItems[i]->SetInTrade(false);
|
||||
if (hisItems[i])
|
||||
hisItems[i]->SetInTrade(false);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
TradeData* my_trade = _player->m_trade;
|
||||
if (!my_trade)
|
||||
return;
|
||||
|
||||
Player* trader = my_trade->GetTrader();
|
||||
|
||||
TradeData* his_trade = trader->m_trade;
|
||||
if (!his_trade)
|
||||
return;
|
||||
|
||||
Item* myItems[TRADE_SLOT_TRADED_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL };
|
||||
Item* hisItems[TRADE_SLOT_TRADED_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL };
|
||||
bool myCanCompleteTrade = true, hisCanCompleteTrade = true;
|
||||
|
||||
// set before checks for propertly undo at problems (it already set in to client)
|
||||
my_trade->SetAccepted(true);
|
||||
|
||||
// not accept case incorrect money amount
|
||||
if (!_player->HasEnoughMoney(my_trade->GetMoney()))
|
||||
{
|
||||
SendNotification(LANG_NOT_ENOUGH_GOLD);
|
||||
my_trade->SetAccepted(false, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// not accept case incorrect money amount
|
||||
if (!trader->HasEnoughMoney(his_trade->GetMoney()))
|
||||
{
|
||||
trader->GetSession()->SendNotification(LANG_NOT_ENOUGH_GOLD);
|
||||
his_trade->SetAccepted(false, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// not accept if some items now can't be trade (cheating)
|
||||
for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i)
|
||||
{
|
||||
if (Item* item = my_trade->GetItem(TradeSlots(i)))
|
||||
{
|
||||
if (!item->CanBeTraded(false, true))
|
||||
{
|
||||
SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
|
||||
return;
|
||||
}
|
||||
if (item->IsBindedNotWith(trader))
|
||||
{
|
||||
SendTradeStatus(TRADE_STATUS_NOT_ELIGIBLE);
|
||||
SendTradeStatus(TRADE_STATUS_CLOSE_WINDOW/*TRADE_STATUS_TRADE_CANCELED*/);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Item* item = his_trade->GetItem(TradeSlots(i)))
|
||||
{
|
||||
if (!item->CanBeTraded(false, true))
|
||||
{
|
||||
SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
|
||||
return;
|
||||
}
|
||||
//if (item->IsBindedNotWith(_player)) // dont mark as invalid when his item isnt good (not exploitable because if item is invalid trade will fail anyway later on the same check)
|
||||
//{
|
||||
// SendTradeStatus(TRADE_STATUS_NOT_ELIGIBLE);
|
||||
// his_trade->SetAccepted(false, true);
|
||||
// return;
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
if (his_trade->IsAccepted())
|
||||
{
|
||||
setAcceptTradeMode(my_trade, his_trade, myItems, hisItems);
|
||||
|
||||
Spell* my_spell = NULL;
|
||||
SpellCastTargets my_targets;
|
||||
|
||||
Spell* his_spell = NULL;
|
||||
SpellCastTargets his_targets;
|
||||
|
||||
// not accept if spell can't be casted now (cheating)
|
||||
if (uint32 my_spell_id = my_trade->GetSpell())
|
||||
{
|
||||
SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(my_spell_id);
|
||||
Item* castItem = my_trade->GetSpellCastItem();
|
||||
|
||||
if (!spellEntry || !his_trade->GetItem(TRADE_SLOT_NONTRADED) ||
|
||||
(my_trade->HasSpellCastItem() && !castItem))
|
||||
{
|
||||
clearAcceptTradeMode(my_trade, his_trade);
|
||||
clearAcceptTradeMode(myItems, hisItems);
|
||||
|
||||
my_trade->SetSpell(0);
|
||||
return;
|
||||
}
|
||||
|
||||
my_spell = new Spell(_player, spellEntry, TRIGGERED_FULL_MASK);
|
||||
my_spell->m_CastItem = castItem;
|
||||
my_targets.SetTradeItemTarget(_player);
|
||||
my_spell->m_targets = my_targets;
|
||||
|
||||
SpellCastResult res = my_spell->CheckCast(true);
|
||||
if (res != SPELL_CAST_OK)
|
||||
{
|
||||
my_spell->SendCastResult(res);
|
||||
|
||||
clearAcceptTradeMode(my_trade, his_trade);
|
||||
clearAcceptTradeMode(myItems, hisItems);
|
||||
|
||||
delete my_spell;
|
||||
my_trade->SetSpell(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// not accept if spell can't be casted now (cheating)
|
||||
if (uint32 his_spell_id = his_trade->GetSpell())
|
||||
{
|
||||
SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(his_spell_id);
|
||||
Item* castItem = his_trade->GetSpellCastItem();
|
||||
|
||||
if (!spellEntry || !my_trade->GetItem(TRADE_SLOT_NONTRADED) || (his_trade->HasSpellCastItem() && !castItem))
|
||||
{
|
||||
delete my_spell;
|
||||
his_trade->SetSpell(0);
|
||||
|
||||
clearAcceptTradeMode(my_trade, his_trade);
|
||||
clearAcceptTradeMode(myItems, hisItems);
|
||||
return;
|
||||
}
|
||||
|
||||
his_spell = new Spell(trader, spellEntry, TRIGGERED_FULL_MASK);
|
||||
his_spell->m_CastItem = castItem;
|
||||
his_targets.SetTradeItemTarget(trader);
|
||||
his_spell->m_targets = his_targets;
|
||||
|
||||
SpellCastResult res = his_spell->CheckCast(true);
|
||||
if (res != SPELL_CAST_OK)
|
||||
{
|
||||
his_spell->SendCastResult(res);
|
||||
|
||||
clearAcceptTradeMode(my_trade, his_trade);
|
||||
clearAcceptTradeMode(myItems, hisItems);
|
||||
|
||||
delete my_spell;
|
||||
delete his_spell;
|
||||
|
||||
his_trade->SetSpell(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// inform partner client
|
||||
trader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT);
|
||||
|
||||
// test if item will fit in each inventory
|
||||
hisCanCompleteTrade = (trader->CanStoreItems(myItems, TRADE_SLOT_TRADED_COUNT) == EQUIP_ERR_OK);
|
||||
myCanCompleteTrade = (_player->CanStoreItems(hisItems, TRADE_SLOT_TRADED_COUNT) == EQUIP_ERR_OK);
|
||||
|
||||
clearAcceptTradeMode(myItems, hisItems);
|
||||
|
||||
// in case of missing space report error
|
||||
if (!myCanCompleteTrade)
|
||||
{
|
||||
clearAcceptTradeMode(my_trade, his_trade);
|
||||
|
||||
SendNotification(LANG_NOT_FREE_TRADE_SLOTS);
|
||||
trader->GetSession()->SendNotification(LANG_NOT_PARTNER_FREE_TRADE_SLOTS);
|
||||
my_trade->SetAccepted(false);
|
||||
his_trade->SetAccepted(false);
|
||||
return;
|
||||
}
|
||||
else if (!hisCanCompleteTrade)
|
||||
{
|
||||
clearAcceptTradeMode(my_trade, his_trade);
|
||||
|
||||
SendNotification(LANG_NOT_PARTNER_FREE_TRADE_SLOTS);
|
||||
trader->GetSession()->SendNotification(LANG_NOT_FREE_TRADE_SLOTS);
|
||||
my_trade->SetAccepted(false);
|
||||
his_trade->SetAccepted(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// execute trade: 1. remove
|
||||
for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i)
|
||||
{
|
||||
if (myItems[i])
|
||||
{
|
||||
myItems[i]->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, _player->GetGUID());
|
||||
_player->MoveItemFromInventory(myItems[i]->GetBagSlot(), myItems[i]->GetSlot(), true);
|
||||
}
|
||||
if (hisItems[i])
|
||||
{
|
||||
hisItems[i]->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, trader->GetGUID());
|
||||
trader->MoveItemFromInventory(hisItems[i]->GetBagSlot(), hisItems[i]->GetSlot(), true);
|
||||
}
|
||||
}
|
||||
|
||||
// execute trade: 2. store
|
||||
moveItems(myItems, hisItems);
|
||||
|
||||
// logging money
|
||||
if (sWorld->getBoolConfig(CONFIG_GM_LOG_TRADE))
|
||||
{
|
||||
if (!AccountMgr::IsPlayerAccount(_player->GetSession()->GetSecurity()) && my_trade->GetMoney() > 0)
|
||||
{
|
||||
sLog->outCommand(_player->GetSession()->GetAccountId(), "GM %s (Account: %u) give money (Amount: %u) to player: %s (Account: %u)",
|
||||
_player->GetName(), _player->GetSession()->GetAccountId(),
|
||||
my_trade->GetMoney(),
|
||||
trader->GetName(), trader->GetSession()->GetAccountId());
|
||||
}
|
||||
if (!AccountMgr::IsPlayerAccount(trader->GetSession()->GetSecurity()) && his_trade->GetMoney() > 0)
|
||||
{
|
||||
sLog->outCommand(trader->GetSession()->GetAccountId(), "GM %s (Account: %u) give money (Amount: %u) to player: %s (Account: %u)",
|
||||
trader->GetName(), trader->GetSession()->GetAccountId(),
|
||||
his_trade->GetMoney(),
|
||||
_player->GetName(), _player->GetSession()->GetAccountId());
|
||||
}
|
||||
}
|
||||
|
||||
// update money
|
||||
_player->ModifyMoney(-int32(my_trade->GetMoney()));
|
||||
_player->ModifyMoney(his_trade->GetMoney());
|
||||
trader->ModifyMoney(-int32(his_trade->GetMoney()));
|
||||
trader->ModifyMoney(my_trade->GetMoney());
|
||||
|
||||
if (my_spell)
|
||||
my_spell->prepare(&my_targets);
|
||||
|
||||
if (his_spell)
|
||||
his_spell->prepare(&his_targets);
|
||||
|
||||
// cleanup
|
||||
clearAcceptTradeMode(my_trade, his_trade);
|
||||
delete _player->m_trade;
|
||||
_player->m_trade = NULL;
|
||||
delete trader->m_trade;
|
||||
trader->m_trade = NULL;
|
||||
|
||||
// desynchronized with the other saves here (SaveInventoryAndGoldToDB() not have own transaction guards)
|
||||
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
_player->SaveInventoryAndGoldToDB(trans);
|
||||
trader->SaveInventoryAndGoldToDB(trans);
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
|
||||
trader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE);
|
||||
SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE);
|
||||
}
|
||||
else
|
||||
{
|
||||
trader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleUnacceptTradeOpcode(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
TradeData* my_trade = _player->GetTradeData();
|
||||
if (!my_trade)
|
||||
return;
|
||||
|
||||
my_trade->SetAccepted(false, true);
|
||||
}
|
||||
|
||||
void WorldSession::HandleBeginTradeOpcode(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
TradeData* my_trade = _player->m_trade;
|
||||
if (!my_trade)
|
||||
return;
|
||||
|
||||
my_trade->GetTrader()->GetSession()->SendTradeStatus(TRADE_STATUS_OPEN_WINDOW);
|
||||
SendTradeStatus(TRADE_STATUS_OPEN_WINDOW);
|
||||
}
|
||||
|
||||
void WorldSession::SendCancelTrade()
|
||||
{
|
||||
if (m_playerRecentlyLogout)
|
||||
return;
|
||||
|
||||
SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCancelTradeOpcode(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
// sended also after LOGOUT COMPLETE
|
||||
if (_player) // needed because STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT
|
||||
_player->TradeCancel(true);
|
||||
}
|
||||
|
||||
void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
if (GetPlayer()->m_trade)
|
||||
{
|
||||
recvPacket.rfinish();
|
||||
return;
|
||||
}
|
||||
|
||||
uint64 ID;
|
||||
recvPacket >> ID;
|
||||
|
||||
if (!GetPlayer()->isAlive())
|
||||
{
|
||||
SendTradeStatus(TRADE_STATUS_YOU_DEAD);
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetPlayer()->HasUnitState(UNIT_STAT_STUNNED))
|
||||
{
|
||||
SendTradeStatus(TRADE_STATUS_YOU_STUNNED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isLogingOut())
|
||||
{
|
||||
SendTradeStatus(TRADE_STATUS_YOU_LOGOUT);
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetPlayer()->isInFlight())
|
||||
{
|
||||
SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetPlayer()->getLevel() < sWorld->getIntConfig(CONFIG_TRADE_LEVEL_REQ))
|
||||
{
|
||||
SendNotification(GetTrinityString(LANG_TRADE_REQ), sWorld->getIntConfig(CONFIG_TRADE_LEVEL_REQ));
|
||||
return;
|
||||
}
|
||||
|
||||
Player* pOther = ObjectAccessor::FindPlayer(ID);
|
||||
|
||||
if (!pOther)
|
||||
{
|
||||
SendTradeStatus(TRADE_STATUS_NO_TARGET);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pOther == GetPlayer() || pOther->m_trade)
|
||||
{
|
||||
SendTradeStatus(TRADE_STATUS_BUSY);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pOther->isAlive())
|
||||
{
|
||||
SendTradeStatus(TRADE_STATUS_TARGET_DEAD);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pOther->isInFlight())
|
||||
{
|
||||
SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pOther->HasUnitState(UNIT_STAT_STUNNED))
|
||||
{
|
||||
SendTradeStatus(TRADE_STATUS_TARGET_STUNNED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pOther->GetSession()->isLogingOut())
|
||||
{
|
||||
SendTradeStatus(TRADE_STATUS_TARGET_LOGOUT);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pOther->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow()))
|
||||
{
|
||||
SendTradeStatus(TRADE_STATUS_IGNORE_YOU);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_TRADE) && pOther->GetTeam() !=_player->GetTeam())
|
||||
{
|
||||
SendTradeStatus(TRADE_STATUS_WRONG_FACTION);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pOther->IsWithinDistInMap(_player, 10.0f, false))
|
||||
{
|
||||
SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pOther->getLevel() < sWorld->getIntConfig(CONFIG_TRADE_LEVEL_REQ))
|
||||
{
|
||||
SendNotification(GetTrinityString(LANG_TRADE_OTHER_REQ), sWorld->getIntConfig(CONFIG_TRADE_LEVEL_REQ));
|
||||
return;
|
||||
}
|
||||
|
||||
// OK start trade
|
||||
_player->m_trade = new TradeData(_player, pOther);
|
||||
pOther->m_trade = new TradeData(pOther, _player);
|
||||
|
||||
WorldPacket data(SMSG_TRADE_STATUS, 12);
|
||||
data << uint32(TRADE_STATUS_BEGIN_TRADE);
|
||||
data << uint64(_player->GetGUID());
|
||||
pOther->GetSession()->SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleSetTradeGoldOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
uint32 gold;
|
||||
recvPacket >> gold;
|
||||
|
||||
TradeData* my_trade = _player->GetTradeData();
|
||||
if (!my_trade)
|
||||
return;
|
||||
|
||||
// gold can be incorrect, but this is checked at trade finished.
|
||||
my_trade->SetMoney(gold);
|
||||
}
|
||||
|
||||
void WorldSession::HandleSetTradeItemOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
// send update
|
||||
uint8 tradeSlot;
|
||||
uint8 bag;
|
||||
uint8 slot;
|
||||
|
||||
recvPacket >> tradeSlot;
|
||||
recvPacket >> bag;
|
||||
recvPacket >> slot;
|
||||
|
||||
TradeData* my_trade = _player->GetTradeData();
|
||||
if (!my_trade)
|
||||
return;
|
||||
|
||||
// invalid slot number
|
||||
if (tradeSlot >= TRADE_SLOT_COUNT)
|
||||
{
|
||||
SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
|
||||
return;
|
||||
}
|
||||
|
||||
// check cheating, can't fail with correct client operations
|
||||
Item* item = _player->GetItemByPos(bag, slot);
|
||||
if (!item || (tradeSlot != TRADE_SLOT_NONTRADED && !item->CanBeTraded(false, true)))
|
||||
{
|
||||
SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
|
||||
return;
|
||||
}
|
||||
|
||||
uint64 iGUID = item->GetGUID();
|
||||
|
||||
// prevent place single item into many trade slots using cheating and client bugs
|
||||
if (my_trade->HasItem(iGUID))
|
||||
{
|
||||
// cheating attempt
|
||||
SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
|
||||
return;
|
||||
}
|
||||
|
||||
my_trade->SetItem(TradeSlots(tradeSlot), item);
|
||||
}
|
||||
|
||||
void WorldSession::HandleClearTradeItemOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
uint8 tradeSlot;
|
||||
recvPacket >> tradeSlot;
|
||||
|
||||
TradeData* my_trade = _player->m_trade;
|
||||
if (!my_trade)
|
||||
return;
|
||||
|
||||
// invalid slot number
|
||||
if (tradeSlot >= TRADE_SLOT_COUNT)
|
||||
return;
|
||||
|
||||
my_trade->SetItem(TradeSlots(tradeSlot), NULL);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
*
|
||||
* 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 "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Vehicle.h"
|
||||
#include "Player.h"
|
||||
#include "Log.h"
|
||||
#include "ObjectAccessor.h"
|
||||
|
||||
void WorldSession::HandleDismissControlledVehicle(WorldPacket &recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_DISMISS_CONTROLLED_VEHICLE");
|
||||
|
||||
uint64 vehicleGUID = _player->GetCharmGUID();
|
||||
|
||||
if (!vehicleGUID) // something wrong here...
|
||||
{
|
||||
recv_data.rfinish(); // prevent warnings spam
|
||||
return;
|
||||
}
|
||||
|
||||
uint64 guid;
|
||||
|
||||
recv_data.readPackGUID(guid);
|
||||
|
||||
MovementInfo mi;
|
||||
mi.guid = guid;
|
||||
ReadMovementInfo(recv_data, &mi);
|
||||
|
||||
_player->m_movementInfo = mi;
|
||||
|
||||
_player->ExitVehicle();
|
||||
}
|
||||
|
||||
void WorldSession::HandleChangeSeatsOnControlledVehicle(WorldPacket &recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_CHANGE_SEATS_ON_CONTROLLED_VEHICLE");
|
||||
|
||||
Unit* vehicle_base = GetPlayer()->GetVehicleBase();
|
||||
if (!vehicle_base)
|
||||
{
|
||||
recv_data.rfinish(); // prevent warnings spam
|
||||
return;
|
||||
}
|
||||
|
||||
VehicleSeatEntry const* seat = GetPlayer()->GetVehicle()->GetSeatForPassenger(GetPlayer());
|
||||
if (!seat->CanSwitchFromSeat())
|
||||
{
|
||||
recv_data.rfinish(); // prevent warnings spam
|
||||
sLog->outError("HandleChangeSeatsOnControlledVehicle, Opcode: %u, Player %u tried to switch seats but current seatflags %u don't permit that.",
|
||||
recv_data.GetOpcode(), GetPlayer()->GetGUIDLow(), seat->m_flags);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (recv_data.GetOpcode())
|
||||
{
|
||||
case CMSG_REQUEST_VEHICLE_PREV_SEAT:
|
||||
GetPlayer()->ChangeSeat(-1, false);
|
||||
break;
|
||||
case CMSG_REQUEST_VEHICLE_NEXT_SEAT:
|
||||
GetPlayer()->ChangeSeat(-1, true);
|
||||
break;
|
||||
case CMSG_CHANGE_SEATS_ON_CONTROLLED_VEHICLE:
|
||||
{
|
||||
uint64 guid; // current vehicle guid
|
||||
recv_data.readPackGUID(guid);
|
||||
|
||||
ReadMovementInfo(recv_data, &vehicle_base->m_movementInfo);
|
||||
|
||||
uint64 accessory; // accessory guid
|
||||
recv_data.readPackGUID(accessory);
|
||||
|
||||
int8 seatId;
|
||||
recv_data >> seatId;
|
||||
|
||||
if (vehicle_base->GetGUID() != guid)
|
||||
return;
|
||||
|
||||
if (!accessory)
|
||||
GetPlayer()->ChangeSeat(-1, seatId > 0); // prev/next
|
||||
else if (Unit* vehUnit = Unit::GetUnit(*GetPlayer(), accessory))
|
||||
{
|
||||
if (Vehicle* vehicle = vehUnit->GetVehicleKit())
|
||||
if (vehicle->HasEmptySeat(seatId))
|
||||
vehUnit->HandleSpellClick(GetPlayer(), seatId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CMSG_REQUEST_VEHICLE_SWITCH_SEAT:
|
||||
{
|
||||
uint64 guid; // current vehicle guid
|
||||
recv_data.readPackGUID(guid);
|
||||
|
||||
int8 seatId;
|
||||
recv_data >> seatId;
|
||||
|
||||
if (vehicle_base->GetGUID() == guid)
|
||||
GetPlayer()->ChangeSeat(seatId);
|
||||
else if (Unit* vehUnit = Unit::GetUnit(*GetPlayer(), guid))
|
||||
if (Vehicle* vehicle = vehUnit->GetVehicleKit())
|
||||
if (vehicle->HasEmptySeat(seatId))
|
||||
vehUnit->HandleSpellClick(GetPlayer(), seatId);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleEnterPlayerVehicle(WorldPacket &data)
|
||||
{
|
||||
// Read guid
|
||||
uint64 guid;
|
||||
data >> guid;
|
||||
|
||||
if (Player* player = ObjectAccessor::FindPlayer(guid))
|
||||
{
|
||||
if (!player->GetVehicleKit())
|
||||
return;
|
||||
if (!player->IsInRaidWith(_player))
|
||||
return;
|
||||
if (!player->IsWithinDistInMap(_player, INTERACTION_DISTANCE))
|
||||
return;
|
||||
|
||||
_player->EnterVehicle(player);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleEjectPassenger(WorldPacket &data)
|
||||
{
|
||||
Vehicle* vehicle = _player->GetVehicleKit();
|
||||
if (!vehicle)
|
||||
{
|
||||
data.rfinish(); // prevent warnings spam
|
||||
sLog->outError("HandleEjectPassenger: Player %u is not in a vehicle!", GetPlayer()->GetGUIDLow());
|
||||
return;
|
||||
}
|
||||
|
||||
uint64 guid;
|
||||
data >> guid;
|
||||
|
||||
if (IS_PLAYER_GUID(guid))
|
||||
{
|
||||
Player* player = ObjectAccessor::FindPlayer(guid);
|
||||
if (!player)
|
||||
{
|
||||
sLog->outError("Player %u tried to eject player %u from vehicle, but the latter was not found in world!", GetPlayer()->GetGUIDLow(), GUID_LOPART(guid));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!player->IsOnVehicle(vehicle->GetBase()))
|
||||
{
|
||||
sLog->outError("Player %u tried to eject player %u, but they are not in the same vehicle", GetPlayer()->GetGUIDLow(), GUID_LOPART(guid));
|
||||
return;
|
||||
}
|
||||
|
||||
VehicleSeatEntry const* seat = vehicle->GetSeatForPassenger(player);
|
||||
ASSERT(seat);
|
||||
if (seat->IsEjectable())
|
||||
player->ExitVehicle();
|
||||
else
|
||||
sLog->outError("Player %u attempted to eject player %u from non-ejectable seat.", GetPlayer()->GetGUIDLow(), GUID_LOPART(guid));
|
||||
}
|
||||
|
||||
else if (IS_CREATURE_GUID(guid))
|
||||
{
|
||||
Unit* unit = ObjectAccessor::GetUnit(*_player, guid);
|
||||
if (!unit) // creatures can be ejected too from player mounts
|
||||
{
|
||||
sLog->outError("Player %u tried to eject creature guid %u from vehicle, but the latter was not found in world!", GetPlayer()->GetGUIDLow(), GUID_LOPART(guid));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!unit->IsOnVehicle(vehicle->GetBase()))
|
||||
{
|
||||
sLog->outError("Player %u tried to eject unit %u, but they are not in the same vehicle", GetPlayer()->GetGUIDLow(), GUID_LOPART(guid));
|
||||
return;
|
||||
}
|
||||
|
||||
VehicleSeatEntry const* seat = vehicle->GetSeatForPassenger(unit);
|
||||
ASSERT(seat);
|
||||
if (seat->IsEjectable())
|
||||
{
|
||||
ASSERT(GetPlayer() == vehicle->GetBase());
|
||||
unit->ExitVehicle();
|
||||
}
|
||||
else
|
||||
sLog->outError("Player %u attempted to eject creature GUID %u from non-ejectable seat.", GetPlayer()->GetGUIDLow(), GUID_LOPART(guid));
|
||||
}
|
||||
else
|
||||
sLog->outError("HandleEjectPassenger: Player %u tried to eject invalid GUID "UI64FMTD, GetPlayer()->GetGUIDLow(), guid);
|
||||
}
|
||||
|
||||
void WorldSession::HandleRequestVehicleExit(WorldPacket& /*recv_data*/)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_REQUEST_VEHICLE_EXIT");
|
||||
|
||||
if (Vehicle* vehicle = GetPlayer()->GetVehicle())
|
||||
{
|
||||
if (VehicleSeatEntry const* seat = vehicle->GetSeatForPassenger(GetPlayer()))
|
||||
{
|
||||
if (seat->CanEnterOrExit())
|
||||
GetPlayer()->ExitVehicle();
|
||||
else
|
||||
sLog->outError("Player %u tried to exit vehicle, but seatflags %u (ID: %u) don't permit that.",
|
||||
GetPlayer()->GetGUIDLow(), seat->m_ID, seat->m_flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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 "Common.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Log.h"
|
||||
|
||||
void WorldSession::HandleVoiceSessionEnableOpcode(WorldPacket& recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_VOICE_SESSION_ENABLE");
|
||||
// uint8 isVoiceEnabled, uint8 isMicrophoneEnabled
|
||||
recv_data.read_skip<uint8>();
|
||||
recv_data.read_skip<uint8>();
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelVoiceOnOpcode(WorldPacket& /*recv_data*/)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CHANNEL_VOICE_ON");
|
||||
// Enable Voice button in channel context menu
|
||||
}
|
||||
|
||||
void WorldSession::HandleSetActiveVoiceChannel(WorldPacket& recv_data)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_SET_ACTIVE_VOICE_CHANNEL");
|
||||
recv_data.read_skip<uint32>();
|
||||
recv_data.read_skip<char*>();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user