mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-02-01 10:03:47 +00:00
First Commit
For Azeroth!
This commit is contained in:
143
src/server/game/Handlers/AddonHandler.cpp
Normal file
143
src/server/game/Handlers/AddonHandler.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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;
|
||||
|
||||
// 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)
|
||||
{
|
||||
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
|
||||
};
|
||||
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;
|
||||
}
|
||||
40
src/server/game/Handlers/AddonHandler.h
Normal file
40
src/server/game/Handlers/AddonHandler.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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
|
||||
|
||||
419
src/server/game/Handlers/ArenaTeamHandler.cpp
Normal file
419
src/server/game/Handlers/ArenaTeamHandler.cpp
Normal file
@@ -0,0 +1,419 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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"
|
||||
#include "Opcodes.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 = ObjectAccessor::FindPlayerByName(invitedName, false);
|
||||
}
|
||||
|
||||
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, "", invitedName, 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;
|
||||
}
|
||||
|
||||
if (GetPlayer()->GetArenaTeamId(arenaTeam->GetSlot()) != arenaTeamId)
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS);
|
||||
return;
|
||||
}
|
||||
|
||||
// OK result but don't send invite
|
||||
if (player->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow()))
|
||||
return;
|
||||
|
||||
if (player->GetTeamId() != GetPlayer()->GetTeamId())
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (player->GetArenaTeamId(arenaTeam->GetSlot()))
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", invitedName, ERR_ALREADY_IN_ARENA_TEAM_S);
|
||||
return;
|
||||
}
|
||||
|
||||
if (player->GetArenaTeamIdInvited())
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", invitedName, 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().c_str(), 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 & /*recvData*/)
|
||||
{
|
||||
;//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 (_player->GetTeamId() != sObjectMgr->GetPlayerTeamIdByGUID(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().c_str(), arenaTeam->GetName(), "");
|
||||
}
|
||||
|
||||
void WorldSession::HandleArenaTeamDeclineOpcode(WorldPacket & /*recvData*/)
|
||||
{
|
||||
;//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 (arenaTeam->IsFighting())
|
||||
{
|
||||
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().c_str(), 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;
|
||||
}
|
||||
|
||||
// Player cannot be removed during fights
|
||||
if (arenaTeam->IsFighting())
|
||||
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().c_str(), 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"
|
||||
*/
|
||||
750
src/server/game/Handlers/AuctionHouseHandler.cpp
Normal file
750
src/server/game/Handlers/AuctionHouseHandler.cpp
Normal file
@@ -0,0 +1,750 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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 "Language.h"
|
||||
#include "Opcodes.h"
|
||||
#include "UpdateMask.h"
|
||||
#include "Util.h"
|
||||
#include "AccountMgr.h"
|
||||
#include "Chat.h"
|
||||
#include "AsyncAuctionListing.h"
|
||||
|
||||
//void called when player click on auctioneer npc
|
||||
void WorldSession::HandleAuctionHelloOpcode(WorldPacket & recvData)
|
||||
{
|
||||
uint64 guid; //NPC guid
|
||||
recvData >> 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_STATE_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 & recvData)
|
||||
{
|
||||
uint64 auctioneer;
|
||||
uint32 itemsCount, etime, bid, buyout;
|
||||
recvData >> auctioneer;
|
||||
recvData >> itemsCount;
|
||||
|
||||
uint64 itemGUIDs[MAX_AUCTION_ITEMS]; // 160 slot = 4x 36 slot bag + backpack 16 slot
|
||||
memset(itemGUIDs, 0, sizeof(itemGUIDs));
|
||||
uint32 count[MAX_AUCTION_ITEMS];
|
||||
memset(count, 0, sizeof(count));
|
||||
|
||||
if (itemsCount > MAX_AUCTION_ITEMS)
|
||||
{
|
||||
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, ERR_AUCTION_DATABASE_ERROR);
|
||||
recvData.rfinish();
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32 i = 0; i < itemsCount; ++i)
|
||||
{
|
||||
recvData >> itemGUIDs[i];
|
||||
recvData >> count[i];
|
||||
|
||||
if (!itemGUIDs[i] || !count[i] || count[i] > 1000)
|
||||
{
|
||||
recvData.rfinish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
recvData >> bid;
|
||||
recvData >> buyout;
|
||||
recvData >> etime;
|
||||
|
||||
if (!bid || !etime)
|
||||
return;
|
||||
|
||||
if (bid > MAX_MONEY_AMOUNT || buyout > MAX_MONEY_AMOUNT)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleAuctionSellItem - Player %s (GUID %u) attempted to sell item with higher price than max gold amount.", _player->GetName().c_str(), _player->GetGUIDLow());
|
||||
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, ERR_AUCTION_DATABASE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
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.", 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.", GUID_LOPART(auctioneer));
|
||||
return;
|
||||
}
|
||||
|
||||
etime *= MINUTE;
|
||||
|
||||
switch (etime)
|
||||
{
|
||||
case 1*MIN_AUCTION_TIME:
|
||||
case 2*MIN_AUCTION_TIME:
|
||||
case 4*MIN_AUCTION_TIME:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
Item* items[MAX_AUCTION_ITEMS];
|
||||
|
||||
uint32 finalCount = 0;
|
||||
uint32 itemEntry = 0;
|
||||
|
||||
for (uint32 i = 0; i < itemsCount; ++i)
|
||||
{
|
||||
Item* item = _player->GetItemByGuid(itemGUIDs[i]);
|
||||
|
||||
if (!item)
|
||||
{
|
||||
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, ERR_AUCTION_ITEM_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
if (itemEntry == 0)
|
||||
itemEntry = item->GetTemplate()->ItemId;
|
||||
|
||||
if (sAuctionMgr->GetAItem(item->GetGUIDLow()) || !item->CanBeTraded() || item->IsNotEmptyBag() ||
|
||||
item->GetTemplate()->Flags & ITEM_PROTO_FLAG_CONJURED || item->GetUInt32Value(ITEM_FIELD_DURATION) ||
|
||||
item->GetCount() < count[i] || itemEntry != item->GetTemplate()->ItemId)
|
||||
{
|
||||
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, ERR_AUCTION_DATABASE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
items[i] = item;
|
||||
finalCount += count[i];
|
||||
}
|
||||
|
||||
if (!finalCount)
|
||||
{
|
||||
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, ERR_AUCTION_DATABASE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// check if there are 2 identical guids, in this case user is most likely cheating
|
||||
for (uint32 i = 0; i < itemsCount - 1; ++i)
|
||||
{
|
||||
for (uint32 j = i + 1; j < itemsCount; ++j)
|
||||
{
|
||||
if (itemGUIDs[i] == itemGUIDs[j])
|
||||
{
|
||||
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, ERR_AUCTION_DATABASE_ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32 i = 0; i < itemsCount; ++i)
|
||||
{
|
||||
Item* item = items[i];
|
||||
|
||||
if (item->GetMaxStackCount() < finalCount)
|
||||
{
|
||||
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, ERR_AUCTION_DATABASE_ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32 i = 0; i < itemsCount; ++i)
|
||||
{
|
||||
Item* item = items[i];
|
||||
|
||||
uint32 auctionTime = uint32(etime * sWorld->getRate(RATE_AUCTION_TIME));
|
||||
AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction());
|
||||
|
||||
uint32 deposit = sAuctionMgr->GetAuctionDeposit(auctionHouseEntry, etime, item, finalCount);
|
||||
if (!_player->HasEnoughMoney(deposit))
|
||||
{
|
||||
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, ERR_AUCTION_NOT_ENOUGHT_MONEY);
|
||||
return;
|
||||
}
|
||||
|
||||
_player->ModifyMoney(-int32(deposit));
|
||||
|
||||
AuctionEntry* AH = new AuctionEntry;
|
||||
AH->Id = sObjectMgr->GenerateAuctionID();
|
||||
AH->auctioneer = GUID_LOPART(auctioneer);
|
||||
|
||||
// Required stack size of auction matches to current item stack size, just move item to auctionhouse
|
||||
if (itemsCount == 1 && item->GetCount() == count[i])
|
||||
{
|
||||
AH->item_guidlow = item->GetGUIDLow();
|
||||
AH->item_template = item->GetEntry();
|
||||
AH->itemCount = item->GetCount();
|
||||
AH->owner = _player->GetGUIDLow();
|
||||
AH->startbid = bid;
|
||||
AH->bidder = 0;
|
||||
AH->bid = 0;
|
||||
AH->buyout = buyout;
|
||||
AH->expire_time = time(NULL) + auctionTime;
|
||||
AH->deposit = deposit;
|
||||
AH->auctionHouseEntry = auctionHouseEntry;
|
||||
|
||||
;//sLog->outDetail("CMSG_AUCTION_SELL_ITEM: Player %s (guid %d) is selling item %s entry %u (guid %d) to auctioneer %u with count %u with initial bid %u with buyout %u and with time %u (in sec) in auctionhouse %u", _player->GetName().c_str(), _player->GetGUIDLow(), item->GetTemplate()->Name1.c_str(), item->GetEntry(), item->GetGUIDLow(), AH->auctioneer, item->GetCount(), bid, buyout, auctionTime, AH->GetHouseId());
|
||||
sAuctionMgr->AddAItem(item);
|
||||
auctionHouse->AddAuction(AH);
|
||||
|
||||
_player->MoveItemFromInventory(item->GetBagSlot(), item->GetSlot(), true);
|
||||
|
||||
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
item->DeleteFromInventoryDB(trans);
|
||||
item->SaveToDB(trans);
|
||||
AH->SaveToDB(trans);
|
||||
_player->SaveInventoryAndGoldToDB(trans);
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
|
||||
SendAuctionCommandResult(AH->Id, AUCTION_SELL_ITEM, ERR_AUCTION_OK);
|
||||
|
||||
GetPlayer()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION, 1);
|
||||
return;
|
||||
}
|
||||
else // Required stack size of auction does not match to current item stack size, clone item and set correct stack size
|
||||
{
|
||||
Item* newItem = item->CloneItem(finalCount, _player);
|
||||
if (!newItem)
|
||||
{
|
||||
sLog->outError("CMSG_AUCTION_SELL_ITEM: Could not create clone of item %u", item->GetEntry());
|
||||
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, ERR_AUCTION_DATABASE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
AH->item_guidlow = newItem->GetGUIDLow();
|
||||
AH->item_template = newItem->GetEntry();
|
||||
AH->itemCount = newItem->GetCount();
|
||||
AH->owner = _player->GetGUIDLow();
|
||||
AH->startbid = bid;
|
||||
AH->bidder = 0;
|
||||
AH->bid = 0;
|
||||
AH->buyout = buyout;
|
||||
AH->expire_time = time(NULL) + auctionTime;
|
||||
AH->deposit = deposit;
|
||||
AH->auctionHouseEntry = auctionHouseEntry;
|
||||
|
||||
;//sLog->outDetail("CMSG_AUCTION_SELL_ITEM: Player %s (guid %d) is selling item %s entry %u (guid %d) to auctioneer %u with count %u with initial bid %u with buyout %u and with time %u (in sec) in auctionhouse %u", _player->GetName().c_str(), _player->GetGUIDLow(), newItem->GetTemplate()->Name1.c_str(), newItem->GetEntry(), newItem->GetGUIDLow(), AH->auctioneer, newItem->GetCount(), bid, buyout, auctionTime, AH->GetHouseId());
|
||||
sAuctionMgr->AddAItem(newItem);
|
||||
auctionHouse->AddAuction(AH);
|
||||
|
||||
for (uint32 j = 0; j < itemsCount; ++j)
|
||||
{
|
||||
Item* item2 = items[j];
|
||||
|
||||
// Item stack count equals required count, ready to delete item - cloned item will be used for auction
|
||||
if (item2->GetCount() == count[j])
|
||||
{
|
||||
_player->MoveItemFromInventory(item2->GetBagSlot(), item2->GetSlot(), true);
|
||||
|
||||
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
item2->DeleteFromInventoryDB(trans);
|
||||
item2->DeleteFromDB(trans);
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
delete item2;
|
||||
}
|
||||
else // Item stack count is bigger than required count, update item stack count and save to database - cloned item will be used for auction
|
||||
{
|
||||
item2->SetCount(item2->GetCount() - count[j]);
|
||||
item2->SetState(ITEM_CHANGED, _player);
|
||||
_player->ItemRemovedQuestCheck(item2->GetEntry(), count[j]);
|
||||
item2->SendUpdateToPlayer(_player);
|
||||
|
||||
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
item2->SaveToDB(trans);
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
}
|
||||
}
|
||||
|
||||
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
newItem->SaveToDB(trans);
|
||||
AH->SaveToDB(trans);
|
||||
_player->SaveInventoryAndGoldToDB(trans);
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
|
||||
SendAuctionCommandResult(AH->Id, AUCTION_SELL_ITEM, ERR_AUCTION_OK);
|
||||
|
||||
GetPlayer()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//this function is called when client bids or buys out auction
|
||||
void WorldSession::HandleAuctionPlaceBid(WorldPacket & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_AUCTION_PLACE_BID");
|
||||
|
||||
uint64 auctioneer;
|
||||
uint32 auctionId;
|
||||
uint32 price;
|
||||
recvData >> auctioneer;
|
||||
recvData >> 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_STATE_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, ERR_AUCTION_BID_OWN);
|
||||
return;
|
||||
}
|
||||
|
||||
// impossible have online own another character (use this for speedup check in case online owner)
|
||||
Player* auction_owner = ObjectAccessor::FindPlayerInOrOutOfWorld(MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER));
|
||||
if (!auction_owner && sObjectMgr->GetPlayerAccountIdByGUID(MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER)) == GetAccountId())
|
||||
{
|
||||
//you cannot bid your another character auction:
|
||||
SendAuctionCommandResult(0, AUCTION_PLACE_BID, ERR_AUCTION_BID_OWN);
|
||||
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()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, price);
|
||||
|
||||
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_AUCTION_BID);
|
||||
stmt->setUInt32(0, auction->bidder);
|
||||
stmt->setUInt32(1, auction->bid);
|
||||
stmt->setUInt32(2, auction->Id);
|
||||
trans->Append(stmt);
|
||||
|
||||
SendAuctionCommandResult(auction->Id, AUCTION_PLACE_BID, ERR_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()->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, ERR_AUCTION_OK);
|
||||
|
||||
auction->DeleteFromDB(trans);
|
||||
|
||||
sAuctionMgr->RemoveAItem(auction->item_guidlow);
|
||||
auctionHouse->RemoveAuction(auction);
|
||||
}
|
||||
player->SaveInventoryAndGoldToDB(trans);
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
}
|
||||
|
||||
//this void is called when auction_owner cancels his auction
|
||||
void WorldSession::HandleAuctionRemoveItem(WorldPacket & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_AUCTION_REMOVE_ITEM");
|
||||
|
||||
uint64 auctioneer;
|
||||
uint32 auctionId;
|
||||
recvData >> auctioneer;
|
||||
recvData >> 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_STATE_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));
|
||||
}
|
||||
|
||||
// item will deleted or added to received mail list
|
||||
MailDraft(auction->BuildAuctionMailSubject(AUCTION_CANCELED), AuctionEntry::BuildAuctionMailBody(0, 0, auction->buyout, auction->deposit, 0))
|
||||
.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, ERR_AUCTION_DATABASE_ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SendAuctionCommandResult(0, AUCTION_CANCEL, ERR_AUCTION_DATABASE_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, ERR_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);
|
||||
}
|
||||
|
||||
//called when player lists his bids
|
||||
void WorldSession::HandleAuctionListBidderItems(WorldPacket & recvData)
|
||||
{
|
||||
;//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
|
||||
|
||||
recvData >> guid;
|
||||
recvData >> listfrom; // not used in fact (this list not have page control in client)
|
||||
recvData >> outbiddedCount;
|
||||
if (recvData.size() != (16 + outbiddedCount * 4))
|
||||
{
|
||||
sLog->outError("Client sent bad opcode!!! with count: %u and size : %lu (must be: %u)", outbiddedCount, (unsigned long)recvData.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)));
|
||||
recvData.rfinish();
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction());
|
||||
|
||||
WorldPacket data(SMSG_AUCTION_BIDDER_LIST_RESULT, (4+4+4)+30000); // pussywizard: ensure there is enough memory
|
||||
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;
|
||||
recvData >> 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 & recvData)
|
||||
{
|
||||
// pussywizard:
|
||||
const uint32 delay = 4500;
|
||||
const uint32 now = World::GetGameTimeMS();
|
||||
if (_lastAuctionListOwnerItemsMSTime > now) // list is pending
|
||||
return;
|
||||
uint32 diff = getMSTimeDiff(_lastAuctionListOwnerItemsMSTime, now);
|
||||
if (diff > delay)
|
||||
diff = delay;
|
||||
|
||||
_lastAuctionListOwnerItemsMSTime = now + delay; // set longest possible here, actual exectuing will change this to getMSTime of that moment
|
||||
_player->m_Events.AddEvent(new AuctionListOwnerItemsDelayEvent(recvData, _player->GetGUID(), true), _player->m_Events.CalculateTime(delay-diff));
|
||||
}
|
||||
|
||||
void WorldSession::HandleAuctionListOwnerItemsEvent(WorldPacket & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_AUCTION_LIST_OWNER_ITEMS");
|
||||
|
||||
_lastAuctionListOwnerItemsMSTime = World::GetGameTimeMS(); // pussywizard
|
||||
|
||||
uint32 listfrom;
|
||||
uint64 guid;
|
||||
|
||||
recvData >> guid;
|
||||
recvData >> 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_STATE_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction());
|
||||
|
||||
WorldPacket data(SMSG_AUCTION_OWNER_LIST_RESULT, (4+4+4)+60000); // pussywizard: ensure there is enough memory
|
||||
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 & recvData)
|
||||
{
|
||||
;//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;
|
||||
|
||||
recvData >> guid;
|
||||
recvData >> listfrom; // start, used for page control listing by 50 elements
|
||||
recvData >> searchedname;
|
||||
|
||||
recvData >> levelmin >> levelmax;
|
||||
recvData >> auctionSlotID >> auctionMainCategory >> auctionSubCategory;
|
||||
recvData >> quality >> usable;
|
||||
|
||||
//recvData.read_skip<uint8>(); // pussywizard: this is the getAll option
|
||||
uint8 getAll;
|
||||
recvData >> getAll;
|
||||
|
||||
// this block looks like it uses some lame byte packing or similar...
|
||||
uint8 unkCnt;
|
||||
recvData >> unkCnt;
|
||||
for (uint8 i = 0; i < unkCnt; i++)
|
||||
{
|
||||
recvData.read_skip<uint8>();
|
||||
recvData.read_skip<uint8>();
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (_player->HasUnitState(UNIT_STATE_DIED))
|
||||
_player->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
// pussywizard:
|
||||
const uint32 delay = 2000;
|
||||
const uint32 now = World::GetGameTimeMS();
|
||||
uint32 diff = getMSTimeDiff(_lastAuctionListItemsMSTime, now);
|
||||
if (diff > delay)
|
||||
diff = delay;
|
||||
_lastAuctionListItemsMSTime = now + delay - diff;
|
||||
TRINITY_GUARD(ACE_Thread_Mutex, AsyncAuctionListingMgr::GetTempLock());
|
||||
AsyncAuctionListingMgr::GetTempList().push_back( AuctionListItemsDelayEvent(delay-diff, _player->GetGUID(), guid, searchedname, listfrom, levelmin, levelmax, usable, auctionSlotID, auctionMainCategory, auctionSubCategory, quality, getAll) );
|
||||
}
|
||||
|
||||
void WorldSession::HandleAuctionListPendingSales(WorldPacket & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_AUCTION_LIST_PENDING_SALES");
|
||||
|
||||
recvData.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);
|
||||
}
|
||||
45
src/server/game/Handlers/AuthHandler.cpp
Normal file
45
src/server/game/Handlers/AuthHandler.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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); // Realm has a free character migration - bool
|
||||
}
|
||||
|
||||
SendPacket(&packet);
|
||||
}
|
||||
|
||||
void WorldSession::SendClientCacheVersion(uint32 version)
|
||||
{
|
||||
WorldPacket data(SMSG_CLIENTCACHE_VERSION, 4);
|
||||
data << uint32(version);
|
||||
SendPacket(&data);
|
||||
}
|
||||
773
src/server/game/Handlers/BattleGroundHandler.cpp
Normal file
773
src/server/game/Handlers/BattleGroundHandler.cpp
Normal file
@@ -0,0 +1,773 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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 & recvData)
|
||||
{
|
||||
uint64 guid;
|
||||
recvData >> 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;
|
||||
}
|
||||
|
||||
SendBattleGroundList(guid, bgTypeId);
|
||||
}
|
||||
|
||||
void WorldSession::SendBattleGroundList(uint64 guid, BattlegroundTypeId bgTypeId)
|
||||
{
|
||||
WorldPacket data;
|
||||
sBattlegroundMgr->BuildBattlegroundListPacket(&data, guid, _player, bgTypeId, 0);
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket & recvData)
|
||||
{
|
||||
uint64 guid;
|
||||
uint32 bgTypeId_;
|
||||
uint32 instanceId; // sent to queue for particular bg from battlemaster's list, currently not used
|
||||
uint8 joinAsGroup;
|
||||
|
||||
recvData >> guid; // battlemaster guid
|
||||
recvData >> bgTypeId_; // battleground type id (DBC id)
|
||||
recvData >> instanceId; // instance id, 0 if First Available selected
|
||||
recvData >> joinAsGroup; // join as group
|
||||
|
||||
// entry not found
|
||||
if (!sBattlemasterListStore.LookupEntry(bgTypeId_))
|
||||
return;
|
||||
|
||||
// chosen battleground type is disabled
|
||||
if (DisableMgr::IsDisabledFor(DISABLE_TYPE_BATTLEGROUND, bgTypeId_, NULL))
|
||||
{
|
||||
ChatHandler(this).PSendSysMessage(LANG_BG_DISABLED);
|
||||
return;
|
||||
}
|
||||
|
||||
// get queue typeid and random typeid to check if already queued for them
|
||||
BattlegroundTypeId bgTypeId = BattlegroundTypeId(bgTypeId_);
|
||||
BattlegroundQueueTypeId bgQueueTypeId = BattlegroundMgr::BGQueueTypeId(bgTypeId, 0);
|
||||
BattlegroundQueueTypeId bgQueueTypeIdRandom = BattlegroundMgr::BGQueueTypeId(BATTLEGROUND_RB, 0);
|
||||
|
||||
// safety check - bgQueueTypeId == BATTLEGROUND_QUEUE_NONE if tried to queue for arena using this function
|
||||
if (bgQueueTypeId == BATTLEGROUND_QUEUE_NONE)
|
||||
return;
|
||||
|
||||
// get bg template
|
||||
Battleground* bgt = sBattlegroundMgr->GetBattlegroundTemplate(bgTypeId);
|
||||
if (!bgt)
|
||||
return;
|
||||
|
||||
// expected bracket entry
|
||||
PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bgt->GetMapId(), _player->getLevel());
|
||||
if (!bracketEntry)
|
||||
return;
|
||||
|
||||
// pussywizard: if trying to queue for already queued
|
||||
// just remove from queue and it will requeue!
|
||||
uint32 qSlot = _player->GetBattlegroundQueueIndex(bgQueueTypeId);
|
||||
if (qSlot < PLAYER_MAX_BATTLEGROUND_QUEUES)
|
||||
{
|
||||
BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId);
|
||||
|
||||
if (bgQueue.IsPlayerInvitedToRatedArena(_player->GetGUID()))
|
||||
{
|
||||
WorldPacket data;
|
||||
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, ERR_BATTLEGROUND_JOIN_FAILED);
|
||||
SendPacket(&data);
|
||||
return;
|
||||
}
|
||||
|
||||
bgQueue.RemovePlayer(_player->GetGUID(), false, qSlot);
|
||||
_player->RemoveBattlegroundQueueId(bgQueueTypeId);
|
||||
}
|
||||
|
||||
// must have free queue slot
|
||||
if (!_player->HasFreeBattlegroundQueueId())
|
||||
{
|
||||
WorldPacket data;
|
||||
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, ERR_BATTLEGROUND_TOO_MANY_QUEUES);
|
||||
SendPacket(&data);
|
||||
return;
|
||||
}
|
||||
|
||||
// queue result (default ok)
|
||||
GroupJoinBattlegroundResult err = GroupJoinBattlegroundResult(bgt->GetBgTypeID());
|
||||
|
||||
// check if player can queue:
|
||||
if (!joinAsGroup)
|
||||
{
|
||||
if (GetPlayer()->InBattleground()) // currently in battleground
|
||||
err = ERR_BATTLEGROUND_NOT_IN_BATTLEGROUND;
|
||||
else if (GetPlayer()->isUsingLfg()) // using lfg system
|
||||
err = ERR_LFG_CANT_USE_BATTLEGROUND;
|
||||
else if (!_player->CanJoinToBattleground()) // has deserter debuff
|
||||
err = ERR_GROUP_JOIN_BATTLEGROUND_DESERTERS;
|
||||
else if (_player->InBattlegroundQueueForBattlegroundQueueType(bgQueueTypeIdRandom)) // queued for random bg, so can't queue for anything else
|
||||
err = ERR_IN_RANDOM_BG;
|
||||
else if (_player->InBattlegroundQueue() && bgTypeId == BATTLEGROUND_RB) // already in queue, so can't queue for random
|
||||
err = ERR_IN_NON_RANDOM_BG;
|
||||
else if (_player->InBattlegroundQueueForBattlegroundQueueType(BATTLEGROUND_QUEUE_2v2) ||
|
||||
_player->InBattlegroundQueueForBattlegroundQueueType(BATTLEGROUND_QUEUE_3v3) ||
|
||||
_player->InBattlegroundQueueForBattlegroundQueueType(BATTLEGROUND_QUEUE_5v5)) // can't be already queued for arenas
|
||||
err = ERR_BATTLEGROUND_QUEUED_FOR_RATED;
|
||||
|
||||
if (err <= 0)
|
||||
{
|
||||
WorldPacket data;
|
||||
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, err);
|
||||
SendPacket(&data);
|
||||
return;
|
||||
}
|
||||
|
||||
BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId);
|
||||
GroupQueueInfo* ginfo = bgQueue.AddGroup(_player, NULL, bracketEntry, false, false, 0, 0, 0);
|
||||
uint32 avgWaitTime = bgQueue.GetAverageQueueWaitTime(ginfo);
|
||||
|
||||
uint32 queueSlot = _player->AddBattlegroundQueueId(bgQueueTypeId);
|
||||
|
||||
// send status packet
|
||||
WorldPacket data;
|
||||
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bgt, queueSlot, STATUS_WAIT_QUEUE, avgWaitTime, 0, 0, TEAM_NEUTRAL);
|
||||
SendPacket(&data);
|
||||
}
|
||||
// check if group can queue:
|
||||
else
|
||||
{
|
||||
Group* grp = _player->GetGroup();
|
||||
// no group or not a leader
|
||||
if (!grp || grp->GetLeaderGUID() != _player->GetGUID())
|
||||
return;
|
||||
|
||||
// pussywizard: for party members - remove queues for which leader is not queued to!
|
||||
std::set<uint32> leaderQueueTypeIds;
|
||||
for (uint32 i=0; i<PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
|
||||
leaderQueueTypeIds.insert((uint32)_player->GetBattlegroundQueueTypeId(i));
|
||||
for (GroupReference* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
|
||||
if (Player* member = itr->GetSource())
|
||||
for (uint32 i=0; i<PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
|
||||
if (BattlegroundQueueTypeId mqtid = member->GetBattlegroundQueueTypeId(i))
|
||||
if (leaderQueueTypeIds.count((uint32)mqtid) == 0)
|
||||
{
|
||||
BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(mqtid);
|
||||
|
||||
if (bgQueue.IsPlayerInvitedToRatedArena(member->GetGUID()))
|
||||
{
|
||||
WorldPacket data;
|
||||
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, ERR_BATTLEGROUND_JOIN_FAILED);
|
||||
SendPacket(&data);
|
||||
return;
|
||||
}
|
||||
|
||||
bgQueue.RemovePlayer(member->GetGUID(), false, i);
|
||||
member->RemoveBattlegroundQueueId(mqtid);
|
||||
}
|
||||
|
||||
if (_player->InBattlegroundQueueForBattlegroundQueueType(bgQueueTypeIdRandom)) // queued for random bg, so can't queue for anything else
|
||||
err = ERR_IN_RANDOM_BG;
|
||||
else if (_player->InBattlegroundQueue() && bgTypeId == BATTLEGROUND_RB) // already in queue, so can't queue for random
|
||||
err = ERR_IN_NON_RANDOM_BG;
|
||||
else if (_player->InBattlegroundQueueForBattlegroundQueueType(BATTLEGROUND_QUEUE_2v2) ||
|
||||
_player->InBattlegroundQueueForBattlegroundQueueType(BATTLEGROUND_QUEUE_3v3) ||
|
||||
_player->InBattlegroundQueueForBattlegroundQueueType(BATTLEGROUND_QUEUE_5v5)) // can't be already queued for arenas
|
||||
err = ERR_BATTLEGROUND_QUEUED_FOR_RATED;
|
||||
|
||||
if (err > 0)
|
||||
err = grp->CanJoinBattlegroundQueue(bgt, bgQueueTypeId, 0, bgt->GetMaxPlayersPerTeam(), false, 0);
|
||||
|
||||
bool isPremade = (grp->GetMembersCount() >= bgt->GetMinPlayersPerTeam() && bgTypeId != BATTLEGROUND_RB);
|
||||
uint32 avgWaitTime = 0;
|
||||
|
||||
if (err > 0)
|
||||
{
|
||||
BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId);
|
||||
GroupQueueInfo* ginfo = bgQueue.AddGroup(_player, grp, bracketEntry, false, isPremade, 0, 0, 0);
|
||||
avgWaitTime = bgQueue.GetAverageQueueWaitTime(ginfo);
|
||||
}
|
||||
|
||||
WorldPacket data;
|
||||
for (GroupReference* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
|
||||
{
|
||||
Player* member = itr->GetSource();
|
||||
if (!member)
|
||||
continue;
|
||||
|
||||
if (err <= 0)
|
||||
{
|
||||
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, err);
|
||||
member->GetSession()->SendPacket(&data);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32 queueSlot = member->AddBattlegroundQueueId(bgQueueTypeId);
|
||||
|
||||
// send status packet
|
||||
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bgt, queueSlot, STATUS_WAIT_QUEUE, avgWaitTime, 0, 0, TEAM_NEUTRAL);
|
||||
member->GetSession()->SendPacket(&data);
|
||||
|
||||
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, err);
|
||||
member->GetSession()->SendPacket(&data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleBattlegroundPlayerPositionsOpcode(WorldPacket& /*recvData*/)
|
||||
{
|
||||
;//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 flagCarrierCount = 0;
|
||||
Player* allianceFlagCarrier = NULL;
|
||||
Player* hordeFlagCarrier = NULL;
|
||||
|
||||
if (uint64 guid = bg->GetFlagPickerGUID(TEAM_ALLIANCE))
|
||||
{
|
||||
allianceFlagCarrier = ObjectAccessor::FindPlayer(guid);
|
||||
if (allianceFlagCarrier)
|
||||
++flagCarrierCount;
|
||||
}
|
||||
|
||||
if (uint64 guid = bg->GetFlagPickerGUID(TEAM_HORDE))
|
||||
{
|
||||
hordeFlagCarrier = ObjectAccessor::FindPlayer(guid);
|
||||
if (hordeFlagCarrier)
|
||||
++flagCarrierCount;
|
||||
}
|
||||
|
||||
WorldPacket data(MSG_BATTLEGROUND_PLAYER_POSITIONS, 4 + 4 + 16 * flagCarrierCount);
|
||||
// Used to send several player positions (found used in AV)
|
||||
data << 0; // CGBattlefieldInfo__m_numPlayerPositions
|
||||
/*
|
||||
for (CGBattlefieldInfo__m_numPlayerPositions)
|
||||
data << guid << posx << posy;
|
||||
*/
|
||||
data << flagCarrierCount;
|
||||
if (allianceFlagCarrier)
|
||||
{
|
||||
data << uint64(allianceFlagCarrier->GetGUID());
|
||||
data << float(allianceFlagCarrier->GetPositionX());
|
||||
data << float(allianceFlagCarrier->GetPositionY());
|
||||
}
|
||||
|
||||
if (hordeFlagCarrier)
|
||||
{
|
||||
data << uint64(hordeFlagCarrier->GetGUID());
|
||||
data << float(hordeFlagCarrier->GetPositionX());
|
||||
data << float(hordeFlagCarrier->GetPositionY());
|
||||
}
|
||||
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandlePVPLogDataOpcode(WorldPacket & /*recvData*/)
|
||||
{
|
||||
;//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 &recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_BATTLEFIELD_LIST Message");
|
||||
|
||||
uint32 bgTypeId;
|
||||
recvData >> bgTypeId; // id from DBC
|
||||
|
||||
uint8 fromWhere;
|
||||
recvData >> fromWhere; // 0 - battlemaster (lua: ShowBattlefieldList), 1 - UI (lua: RequestBattlegroundInstanceInfo)
|
||||
|
||||
uint8 canGainXP;
|
||||
recvData >> canGainXP; // players with locked xp have their own bg queue on retail
|
||||
|
||||
BattlemasterListEntry const* bl = sBattlemasterListStore.LookupEntry(bgTypeId);
|
||||
if (!bl)
|
||||
return;
|
||||
|
||||
WorldPacket data;
|
||||
sBattlegroundMgr->BuildBattlegroundListPacket(&data, 0, _player, BattlegroundTypeId(bgTypeId), fromWhere);
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleBattleFieldPortOpcode(WorldPacket &recvData)
|
||||
{
|
||||
uint8 arenaType; // 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
|
||||
|
||||
recvData >> arenaType >> unk2 >> bgTypeId_ >> unk >> action;
|
||||
|
||||
// bgTypeId not valid
|
||||
if (!sBattlemasterListStore.LookupEntry(bgTypeId_))
|
||||
return;
|
||||
|
||||
// player not in any queue, so can't really answer
|
||||
if (!_player->InBattlegroundQueue())
|
||||
return;
|
||||
|
||||
// get BattlegroundQueue for received
|
||||
BattlegroundTypeId bgTypeId = BattlegroundTypeId(bgTypeId_);
|
||||
BattlegroundQueueTypeId bgQueueTypeId = BattlegroundMgr::BGQueueTypeId(bgTypeId, arenaType);
|
||||
BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId);
|
||||
|
||||
// get group info from queue
|
||||
GroupQueueInfo ginfo;
|
||||
if (!bgQueue.GetPlayerGroupInfoData(_player->GetGUID(), &ginfo))
|
||||
return;
|
||||
|
||||
// to accept, player must be invited to particular battleground id
|
||||
if (!ginfo.IsInvitedToBGInstanceGUID && action == 1)
|
||||
return;
|
||||
|
||||
Battleground* bg = sBattlegroundMgr->GetBattleground(ginfo.IsInvitedToBGInstanceGUID);
|
||||
|
||||
// use template if leaving queue (instance might not be created yet)
|
||||
if (!bg && action == 0)
|
||||
bg = sBattlegroundMgr->GetBattlegroundTemplate(bgTypeId);
|
||||
|
||||
if (!bg)
|
||||
return;
|
||||
|
||||
// expected bracket entry
|
||||
PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bg->GetMapId(), _player->getLevel());
|
||||
if (!bracketEntry)
|
||||
return;
|
||||
|
||||
// safety checks
|
||||
if (action == 1 && ginfo.ArenaType == 0)
|
||||
{
|
||||
// can't join with deserter, check it here right before joining to be sure
|
||||
if (!_player->CanJoinToBattleground())
|
||||
{
|
||||
WorldPacket data;
|
||||
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, ERR_GROUP_JOIN_BATTLEGROUND_DESERTERS);
|
||||
SendPacket(&data);
|
||||
action = 0;
|
||||
}
|
||||
|
||||
if (_player->getLevel() > bg->GetMaxLevel())
|
||||
action = 0;
|
||||
}
|
||||
|
||||
// get player queue slot index for this bg (can be in up to 2 queues at the same time)
|
||||
uint32 queueSlot = _player->GetBattlegroundQueueIndex(bgQueueTypeId);
|
||||
|
||||
WorldPacket data;
|
||||
switch (action)
|
||||
{
|
||||
case 1: // accept
|
||||
{
|
||||
// set entry point if not in battleground
|
||||
if (!_player->InBattleground())
|
||||
_player->SetEntryPoint();
|
||||
|
||||
// resurrect the player
|
||||
if (!_player->IsAlive())
|
||||
{
|
||||
_player->ResurrectPlayer(1.0f);
|
||||
_player->SpawnCorpseBones();
|
||||
}
|
||||
|
||||
// remove player from all bg queues
|
||||
for (uint32 qslot = 0; qslot < PLAYER_MAX_BATTLEGROUND_QUEUES; ++qslot)
|
||||
if (BattlegroundQueueTypeId q = _player->GetBattlegroundQueueTypeId(qslot))
|
||||
{
|
||||
BattlegroundQueue& queue = sBattlegroundMgr->GetBattlegroundQueue(q);
|
||||
queue.RemovePlayer(_player->GetGUID(), (bgQueueTypeId == q), qslot);
|
||||
_player->RemoveBattlegroundQueueId(q);
|
||||
}
|
||||
|
||||
// send status packet
|
||||
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime(), bg->GetArenaType(), ginfo.teamId);
|
||||
SendPacket(&data);
|
||||
|
||||
_player->SetBattlegroundId(bg->GetInstanceID(), bg->GetBgTypeID(), queueSlot, true, bgTypeId == BATTLEGROUND_RB, ginfo.teamId);
|
||||
|
||||
sBattlegroundMgr->SendToBattleground(_player, ginfo.IsInvitedToBGInstanceGUID, bgTypeId);
|
||||
}
|
||||
break;
|
||||
case 0: // leave queue
|
||||
{
|
||||
bgQueue.RemovePlayer(_player->GetGUID(), false, queueSlot);
|
||||
_player->RemoveBattlegroundQueueId(bgQueueTypeId);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleBattlefieldLeaveOpcode(WorldPacket& recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_LEAVE_BATTLEFIELD Message");
|
||||
|
||||
recvData.read_skip<uint8>(); // unk1
|
||||
recvData.read_skip<uint8>(); // unk2
|
||||
recvData.read_skip<uint32>(); // BattlegroundTypeId
|
||||
recvData.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 & /*recvData*/)
|
||||
{
|
||||
// requested at login and on map change
|
||||
// send status for current queues and current bg
|
||||
|
||||
WorldPacket data;
|
||||
|
||||
// for current bg send STATUS_IN_PROGRESS
|
||||
if (Battleground* bg = _player->GetBattleground())
|
||||
if (bg->GetPlayers().count(_player->GetGUID()))
|
||||
{
|
||||
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, _player->GetCurrentBattlegroundQueueSlot(), STATUS_IN_PROGRESS, bg->GetEndTime(), bg->GetStartTime(), bg->GetArenaType(), _player->GetBgTeamId());
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
// for queued bgs send STATUS_WAIT_JOIN or STATUS_WAIT_QUEUE
|
||||
for (uint8 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
|
||||
{
|
||||
// check if in queue
|
||||
BattlegroundQueueTypeId bgQueueTypeId = _player->GetBattlegroundQueueTypeId(i);
|
||||
if (!bgQueueTypeId)
|
||||
continue;
|
||||
|
||||
// get group info from queue
|
||||
BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId);
|
||||
GroupQueueInfo ginfo;
|
||||
if (!bgQueue.GetPlayerGroupInfoData(_player->GetGUID(), &ginfo))
|
||||
continue;
|
||||
|
||||
BattlegroundTypeId bgTypeId = BattlegroundMgr::BGTemplateId(bgQueueTypeId);
|
||||
|
||||
// if invited - send STATUS_WAIT_JOIN
|
||||
if (ginfo.IsInvitedToBGInstanceGUID)
|
||||
{
|
||||
Battleground* bg = sBattlegroundMgr->GetBattleground(ginfo.IsInvitedToBGInstanceGUID);
|
||||
if (!bg)
|
||||
continue;
|
||||
|
||||
uint32 remainingTime = (World::GetGameTimeMS() < ginfo.RemoveInviteTime ? getMSTimeDiff(World::GetGameTimeMS(), ginfo.RemoveInviteTime) : 1);
|
||||
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, i, STATUS_WAIT_JOIN, remainingTime, 0, ginfo.ArenaType, TEAM_NEUTRAL, bg->isRated(), ginfo.BgTypeId);
|
||||
SendPacket(&data);
|
||||
}
|
||||
// if not invited - send STATUS_WAIT_QUEUE
|
||||
else
|
||||
{
|
||||
Battleground* bgt = sBattlegroundMgr->GetBattlegroundTemplate(bgTypeId);
|
||||
if (!bgt)
|
||||
continue;
|
||||
|
||||
// expected bracket entry
|
||||
PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bgt->GetMapId(), _player->getLevel());
|
||||
if (!bracketEntry)
|
||||
continue;
|
||||
|
||||
uint32 avgWaitTime = bgQueue.GetAverageQueueWaitTime(&ginfo);
|
||||
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bgt, i, STATUS_WAIT_QUEUE, avgWaitTime, getMSTimeDiff(ginfo.JoinTime, World::GetGameTimeMS()), ginfo.ArenaType, TEAM_NEUTRAL, ginfo.IsRated);
|
||||
SendPacket(&data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleBattlemasterJoinArena(WorldPacket & recvData)
|
||||
{
|
||||
uint64 guid; // arena Battlemaster guid
|
||||
uint8 arenaslot; // 2v2, 3v3 or 5v5
|
||||
uint8 asGroup; // asGroup
|
||||
uint8 isRated; // isRated
|
||||
|
||||
recvData >> guid >> arenaslot >> asGroup >> isRated;
|
||||
|
||||
// can't queue for rated without a group
|
||||
if (isRated && !asGroup)
|
||||
return;
|
||||
|
||||
// find creature by guid
|
||||
Creature* unit = GetPlayer()->GetMap()->GetCreature(guid);
|
||||
if (!unit || !unit->IsBattleMaster())
|
||||
return;
|
||||
|
||||
// get arena type
|
||||
uint8 arenatype = 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:
|
||||
return;
|
||||
}
|
||||
|
||||
// get template for all arenas
|
||||
Battleground* bgt = sBattlegroundMgr->GetBattlegroundTemplate(BATTLEGROUND_AA);
|
||||
if (!bgt)
|
||||
return;
|
||||
|
||||
// arenas disabled
|
||||
if (DisableMgr::IsDisabledFor(DISABLE_TYPE_BATTLEGROUND, BATTLEGROUND_AA, NULL))
|
||||
{
|
||||
ChatHandler(this).PSendSysMessage(LANG_ARENA_DISABLED);
|
||||
return;
|
||||
}
|
||||
|
||||
BattlegroundTypeId bgTypeId = bgt->GetBgTypeID();
|
||||
BattlegroundQueueTypeId bgQueueTypeId = BattlegroundMgr::BGQueueTypeId(bgTypeId, arenatype);
|
||||
|
||||
// expected bracket entry
|
||||
PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bgt->GetMapId(), _player->getLevel());
|
||||
if (!bracketEntry)
|
||||
return;
|
||||
|
||||
// pussywizard: if trying to queue for already queued
|
||||
// just remove from queue and it will requeue!
|
||||
uint32 qSlot = _player->GetBattlegroundQueueIndex(bgQueueTypeId);
|
||||
if (qSlot < PLAYER_MAX_BATTLEGROUND_QUEUES)
|
||||
{
|
||||
BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId);
|
||||
|
||||
if (bgQueue.IsPlayerInvitedToRatedArena(_player->GetGUID()))
|
||||
{
|
||||
WorldPacket data;
|
||||
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, ERR_BATTLEGROUND_JOIN_FAILED);
|
||||
SendPacket(&data);
|
||||
return;
|
||||
}
|
||||
|
||||
bgQueue.RemovePlayer(_player->GetGUID(), false, qSlot);
|
||||
_player->RemoveBattlegroundQueueId(bgQueueTypeId);
|
||||
}
|
||||
|
||||
// must have free queue slot
|
||||
// pussywizard: allow being queued only in one arena queue, and it even cannot be together with bg queues
|
||||
if (_player->InBattlegroundQueue())
|
||||
{
|
||||
WorldPacket data;
|
||||
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, ERR_BATTLEGROUND_CANNOT_QUEUE_FOR_RATED);
|
||||
SendPacket(&data);
|
||||
return;
|
||||
}
|
||||
|
||||
// queue result (default ok)
|
||||
GroupJoinBattlegroundResult err = GroupJoinBattlegroundResult(bgt->GetBgTypeID());
|
||||
|
||||
// check if player can queue:
|
||||
if (!asGroup)
|
||||
{
|
||||
if (GetPlayer()->InBattleground()) // currently in battleground
|
||||
err = ERR_BATTLEGROUND_NOT_IN_BATTLEGROUND;
|
||||
else if (GetPlayer()->isUsingLfg()) // using lfg system
|
||||
err = ERR_LFG_CANT_USE_BATTLEGROUND;
|
||||
|
||||
if (err <= 0)
|
||||
{
|
||||
WorldPacket data;
|
||||
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, err);
|
||||
SendPacket(&data);
|
||||
return;
|
||||
}
|
||||
|
||||
BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId);
|
||||
GroupQueueInfo* ginfo = bgQueue.AddGroup(_player, NULL, bracketEntry, false, false, 0, 0, 0);
|
||||
uint32 avgWaitTime = bgQueue.GetAverageQueueWaitTime(ginfo);
|
||||
|
||||
uint32 queueSlot = _player->AddBattlegroundQueueId(bgQueueTypeId);
|
||||
|
||||
WorldPacket data;
|
||||
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bgt, queueSlot, STATUS_WAIT_QUEUE, avgWaitTime, 0, arenatype, TEAM_NEUTRAL);
|
||||
SendPacket(&data);
|
||||
}
|
||||
// check if group can queue:
|
||||
else
|
||||
{
|
||||
Group* grp = _player->GetGroup();
|
||||
// no group or not a leader
|
||||
if (!grp || grp->GetLeaderGUID() != _player->GetGUID())
|
||||
return;
|
||||
|
||||
// pussywizard: for party members - remove queues for which leader is not queued to!
|
||||
std::set<uint32> leaderQueueTypeIds;
|
||||
for (uint32 i=0; i<PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
|
||||
leaderQueueTypeIds.insert((uint32)_player->GetBattlegroundQueueTypeId(i));
|
||||
for (GroupReference* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
|
||||
if (Player* member = itr->GetSource())
|
||||
for (uint32 i=0; i<PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
|
||||
if (BattlegroundQueueTypeId mqtid = member->GetBattlegroundQueueTypeId(i))
|
||||
if (leaderQueueTypeIds.count((uint32)mqtid) == 0)
|
||||
{
|
||||
BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(mqtid);
|
||||
|
||||
if (bgQueue.IsPlayerInvitedToRatedArena(member->GetGUID()))
|
||||
{
|
||||
WorldPacket data;
|
||||
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, ERR_BATTLEGROUND_JOIN_FAILED);
|
||||
SendPacket(&data);
|
||||
return;
|
||||
}
|
||||
|
||||
bgQueue.RemovePlayer(member->GetGUID(), false, i);
|
||||
member->RemoveBattlegroundQueueId(mqtid);
|
||||
}
|
||||
|
||||
|
||||
uint32 ateamId = 0;
|
||||
uint32 arenaRating = 0;
|
||||
uint32 matchmakerRating = 0;
|
||||
|
||||
// additional checks for rated arenas
|
||||
if (isRated)
|
||||
{
|
||||
// pussywizard: for rated matches check if season is in progress!
|
||||
if (!sWorld->getBoolConfig(CONFIG_ARENA_SEASON_IN_PROGRESS))
|
||||
return;
|
||||
|
||||
ateamId = _player->GetArenaTeamId(arenaslot);
|
||||
|
||||
// check team existence
|
||||
ArenaTeam* at = sArenaTeamMgr->GetArenaTeamById(ateamId);
|
||||
if (!at)
|
||||
{
|
||||
SendNotInArenaTeamPacket(arenatype);
|
||||
return;
|
||||
}
|
||||
|
||||
// get team rating for queueing
|
||||
arenaRating = at->GetRating();
|
||||
matchmakerRating = at->GetAverageMMR(grp);
|
||||
if (arenaRating <= 0)
|
||||
arenaRating = 1;
|
||||
}
|
||||
|
||||
err = grp->CanJoinBattlegroundQueue(bgt, bgQueueTypeId, arenatype, arenatype, (bool)isRated, arenaslot);
|
||||
|
||||
uint32 avgWaitTime = 0;
|
||||
if (err > 0)
|
||||
{
|
||||
BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId);
|
||||
GroupQueueInfo* ginfo = bgQueue.AddGroup(_player, grp, bracketEntry, isRated, false, arenaRating, matchmakerRating, ateamId);
|
||||
avgWaitTime = bgQueue.GetAverageQueueWaitTime(ginfo);
|
||||
}
|
||||
|
||||
WorldPacket data;
|
||||
for (GroupReference* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
|
||||
{
|
||||
Player* member = itr->GetSource();
|
||||
if (!member)
|
||||
continue;
|
||||
|
||||
if (err <= 0)
|
||||
{
|
||||
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, err);
|
||||
member->GetSession()->SendPacket(&data);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32 queueSlot = member->AddBattlegroundQueueId(bgQueueTypeId);
|
||||
|
||||
// send status packet
|
||||
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bgt, queueSlot, STATUS_WAIT_QUEUE, avgWaitTime, 0, arenatype, TEAM_NEUTRAL, isRated);
|
||||
member->GetSession()->SendPacket(&data);
|
||||
|
||||
sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, err);
|
||||
member->GetSession()->SendPacket(&data);
|
||||
}
|
||||
|
||||
// pussywizard: schedule update for rated arena
|
||||
if (ateamId)
|
||||
sBattlegroundMgr->ScheduleArenaQueueUpdate(ateamId, bgQueueTypeId, bracketEntry->GetBracketId());
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleReportPvPAFK(WorldPacket & recvData)
|
||||
{
|
||||
uint64 playerGuid;
|
||||
recvData >> 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().c_str(), reportedPlayer->GetName().c_str());
|
||||
|
||||
reportedPlayer->ReportedAfkBy(_player);
|
||||
}
|
||||
756
src/server/game/Handlers/CalendarHandler.cpp
Normal file
756
src/server/game/Handlers/CalendarHandler.cpp
Normal file
@@ -0,0 +1,756 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
----- Opcodes Not Used yet -----
|
||||
|
||||
SMSG_CALENDAR_EVENT_INVITE_NOTES [ packguid(Invitee), uint64(inviteId), string(Text), Boolean(Unk) ]
|
||||
?CMSG_CALENDAR_EVENT_INVITE_NOTES [ uint32(unk1), uint32(unk2), uint32(unk3), uint32(unk4), uint32(unk5) ]
|
||||
SMSG_CALENDAR_EVENT_INVITE_NOTES_ALERT [ uint64(inviteId), string(Text) ]
|
||||
SMSG_CALENDAR_EVENT_INVITE_STATUS_ALERT [ uint64(eventId), uint32(eventTime), uint32(unkFlag), uint8(deletePending) ]
|
||||
|
||||
----- TODO -----
|
||||
|
||||
Finish complains' handling - what to do with received complains and how to respond?
|
||||
Find out what to do with all "not used yet" opcodes
|
||||
Correct errors sending (event/invite not found, invites exceeded, event already passed, permissions etc.)
|
||||
Fix locked events to be displayed properly and response time shouldn't be shown for people that haven't respond yet
|
||||
Copied events should probably have a new owner
|
||||
|
||||
*/
|
||||
|
||||
#include "InstanceSaveMgr.h"
|
||||
#include "Log.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Player.h"
|
||||
#include "SocialMgr.h"
|
||||
#include "CalendarMgr.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "GuildMgr.h"
|
||||
#include "ArenaTeamMgr.h"
|
||||
#include "WorldSession.h"
|
||||
|
||||
void WorldSession::HandleCalendarGetCalendar(WorldPacket& /*recvData*/)
|
||||
{
|
||||
uint64 guid = _player->GetGUID();
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_CALENDAR_GET_CALENDAR [" UI64FMTD "]", guid);
|
||||
|
||||
time_t currTime = time(NULL);
|
||||
|
||||
WorldPacket data(SMSG_CALENDAR_SEND_CALENDAR, 1000); // Average size if no instance
|
||||
|
||||
CalendarInviteStore invites = sCalendarMgr->GetPlayerInvites(guid);
|
||||
data << uint32(invites.size());
|
||||
for (CalendarInviteStore::const_iterator itr = invites.begin(); itr != invites.end(); ++itr)
|
||||
{
|
||||
data << uint64((*itr)->GetEventId());
|
||||
data << uint64((*itr)->GetInviteId());
|
||||
data << uint8((*itr)->GetStatus());
|
||||
data << uint8((*itr)->GetRank());
|
||||
|
||||
if (CalendarEvent* calendarEvent = sCalendarMgr->GetEvent((*itr)->GetEventId()))
|
||||
{
|
||||
data << uint8(calendarEvent->IsGuildEvent());
|
||||
data.appendPackGUID(calendarEvent->GetCreatorGUID());
|
||||
}
|
||||
else
|
||||
{
|
||||
data << uint8(0);
|
||||
data.appendPackGUID((*itr)->GetSenderGUID());
|
||||
}
|
||||
}
|
||||
|
||||
CalendarEventStore playerEvents = sCalendarMgr->GetPlayerEvents(guid);
|
||||
data << uint32(playerEvents.size());
|
||||
for (CalendarEventStore::const_iterator itr = playerEvents.begin(); itr != playerEvents.end(); ++itr)
|
||||
{
|
||||
CalendarEvent* calendarEvent = *itr;
|
||||
|
||||
data << uint64(calendarEvent->GetEventId());
|
||||
data << calendarEvent->GetTitle();
|
||||
data << uint32(calendarEvent->GetType());
|
||||
data.AppendPackedTime(calendarEvent->GetEventTime());
|
||||
data << uint32(calendarEvent->GetFlags());
|
||||
data << int32(calendarEvent->GetDungeonId());
|
||||
data.appendPackGUID(calendarEvent->GetCreatorGUID());
|
||||
}
|
||||
|
||||
data << uint32(currTime); // server time
|
||||
data.AppendPackedTime(currTime); // zone time
|
||||
|
||||
ByteBuffer dataBuffer;
|
||||
uint32 boundCounter = 0;
|
||||
for (uint8 i = 0; i < MAX_DIFFICULTY; ++i)
|
||||
{
|
||||
BoundInstancesMap const& m_boundInstances = sInstanceSaveMgr->PlayerGetBoundInstances(_player->GetGUIDLow(), Difficulty(i));
|
||||
for (BoundInstancesMap::const_iterator itr = m_boundInstances.begin(); itr != m_boundInstances.end(); ++itr)
|
||||
{
|
||||
if (itr->second.perm)
|
||||
{
|
||||
InstanceSave const* save = itr->second.save;
|
||||
time_t resetTime = itr->second.extended ? save->GetExtendedResetTime() : save->GetResetTime();
|
||||
dataBuffer << uint32(save->GetMapId());
|
||||
dataBuffer << uint32(save->GetDifficulty());
|
||||
dataBuffer << uint32(resetTime >= currTime ? resetTime-currTime : 0);
|
||||
dataBuffer << uint64(MAKE_NEW_GUID(save->GetInstanceId(), 0, HIGHGUID_INSTANCE)); // instance save id as unique instance copy id
|
||||
++boundCounter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data << uint32(boundCounter);
|
||||
data.append(dataBuffer);
|
||||
|
||||
// pussywizard
|
||||
uint32 relationTime = sWorld->getIntConfig(CONFIG_INSTANCE_RESET_TIME_RELATIVE_TIMESTAMP) + sWorld->getIntConfig(CONFIG_INSTANCE_RESET_TIME_HOUR) * HOUR; // set point in time (default 29.12.2005) + X hours
|
||||
data << uint32(relationTime);
|
||||
|
||||
// Reuse variables
|
||||
boundCounter = 0;
|
||||
std::set<uint32> sentMaps;
|
||||
dataBuffer.clear();
|
||||
|
||||
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);
|
||||
|
||||
dataBuffer << int32(mapId);
|
||||
time_t period = sInstanceSaveMgr->GetExtendedResetTimeFor(mapId, (Difficulty)PAIR32_HIPART(itr->first)) - itr->second;
|
||||
dataBuffer << int32(period); // pussywizard: reset time period
|
||||
dataBuffer << int32(0); // pussywizard: reset time offset, needed for other than 7-day periods if not aligned with relationTime
|
||||
++boundCounter;
|
||||
}
|
||||
|
||||
data << uint32(boundCounter);
|
||||
data.append(dataBuffer);
|
||||
|
||||
// TODO: Fix this, how we do know how many and what holidays to send?
|
||||
uint32 holidayCount = 0;
|
||||
data << uint32(holidayCount);
|
||||
for (uint32 i = 0; i < holidayCount; ++i)
|
||||
{
|
||||
HolidaysEntry const* holiday = sHolidaysStore.LookupEntry(666);
|
||||
|
||||
data << uint32(holiday->Id); // m_ID
|
||||
data << uint32(holiday->Region); // m_region, might be looping
|
||||
data << uint32(holiday->Looping); // m_looping, might be region
|
||||
data << uint32(holiday->Priority); // m_priority
|
||||
data << uint32(holiday->CalendarFilterType); // m_calendarFilterType
|
||||
|
||||
for (uint8 j = 0; j < MAX_HOLIDAY_DATES; ++j)
|
||||
data << uint32(holiday->Date[j]); // 26 * m_date -- WritePackedTime ?
|
||||
|
||||
for (uint8 j = 0; j < MAX_HOLIDAY_DURATIONS; ++j)
|
||||
data << uint32(holiday->Duration[j]); // 10 * m_duration
|
||||
|
||||
for (uint8 j = 0; j < MAX_HOLIDAY_FLAGS; ++j)
|
||||
data << uint32(holiday->CalendarFlags[j]); // 10 * m_calendarFlags
|
||||
|
||||
data << holiday->TextureFilename; // m_textureFilename (holiday name)
|
||||
}
|
||||
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarGetEvent(WorldPacket& recvData)
|
||||
{
|
||||
uint64 eventId;
|
||||
recvData >> eventId;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_CALENDAR_GET_EVENT. Player ["
|
||||
UI64FMTD "] Event [" UI64FMTD "]", _player->GetGUID(), eventId);
|
||||
|
||||
if (CalendarEvent* calendarEvent = sCalendarMgr->GetEvent(eventId))
|
||||
sCalendarMgr->SendCalendarEvent(_player->GetGUID(), *calendarEvent, CALENDAR_SENDTYPE_GET);
|
||||
else
|
||||
sCalendarMgr->SendCalendarCommandResult(_player->GetGUID(), CALENDAR_ERROR_EVENT_INVALID);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarGuildFilter(WorldPacket& recvData)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_CALENDAR_GUILD_FILTER [" UI64FMTD "]", _player->GetGUID());
|
||||
|
||||
uint32 minLevel;
|
||||
uint32 maxLevel;
|
||||
uint32 minRank;
|
||||
|
||||
recvData >> minLevel >> maxLevel >> minRank;
|
||||
|
||||
if (Guild* guild = sGuildMgr->GetGuildById(_player->GetGuildId()))
|
||||
guild->MassInviteToEvent(this, minLevel, maxLevel, minRank);
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_CALENDAR_GUILD_FILTER: Min level [%d], Max level [%d], Min rank [%d]", minLevel, maxLevel, minRank);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarArenaTeam(WorldPacket& recvData)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_CALENDAR_ARENA_TEAM [" UI64FMTD "]", _player->GetGUID());
|
||||
|
||||
uint32 arenaTeamId;
|
||||
recvData >> arenaTeamId;
|
||||
|
||||
if (ArenaTeam* team = sArenaTeamMgr->GetArenaTeamById(arenaTeamId))
|
||||
team->MassInviteToEvent(this);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarAddEvent(WorldPacket& recvData)
|
||||
{
|
||||
uint64 guid = _player->GetGUID();
|
||||
|
||||
std::string title;
|
||||
std::string description;
|
||||
uint8 type;
|
||||
uint8 repeatable;
|
||||
uint32 maxInvites;
|
||||
int32 dungeonId;
|
||||
uint32 eventPackedTime;
|
||||
uint32 unkPackedTime;
|
||||
uint32 flags;
|
||||
|
||||
recvData >> title >> description >> type >> repeatable >> maxInvites >> dungeonId;
|
||||
recvData.ReadPackedTime(eventPackedTime);
|
||||
recvData.ReadPackedTime(unkPackedTime);
|
||||
recvData >> flags;
|
||||
|
||||
// prevent events in the past
|
||||
// To Do: properly handle timezones and remove the "- time_t(86400L)" hack
|
||||
if (time_t(eventPackedTime) < (time(NULL) - time_t(86400L)))
|
||||
{
|
||||
recvData.rfinish();
|
||||
return;
|
||||
}
|
||||
|
||||
CalendarEvent* calendarEvent = new CalendarEvent(sCalendarMgr->GetFreeEventId(), guid, 0, CalendarEventType(type), dungeonId,
|
||||
time_t(eventPackedTime), flags, time_t(unkPackedTime), title, description);
|
||||
|
||||
if (calendarEvent->IsGuildEvent() || calendarEvent->IsGuildAnnouncement())
|
||||
if (Player* creator = ObjectAccessor::FindPlayerInOrOutOfWorld(guid))
|
||||
calendarEvent->SetGuildId(creator->GetGuildId());
|
||||
|
||||
if (calendarEvent->IsGuildAnnouncement())
|
||||
{
|
||||
// 946684800 is 01/01/2000 00:00:00 - default response time
|
||||
CalendarInvite* invite = new CalendarInvite(0, calendarEvent->GetEventId(), 0, guid, 946684800, CALENDAR_STATUS_NOT_SIGNED_UP, CALENDAR_RANK_PLAYER, "");
|
||||
sCalendarMgr->AddInvite(calendarEvent, invite);
|
||||
}
|
||||
else
|
||||
{
|
||||
// client limits the amount of players to be invited to 100
|
||||
const uint32 MaxPlayerInvites = 100;
|
||||
|
||||
uint32 inviteCount;
|
||||
uint64 invitee[MaxPlayerInvites];
|
||||
uint8 status[MaxPlayerInvites];
|
||||
uint8 rank[MaxPlayerInvites];
|
||||
|
||||
memset(invitee, 0, sizeof(invitee));
|
||||
memset(status, 0, sizeof(status));
|
||||
memset(rank, 0, sizeof(rank));
|
||||
|
||||
try
|
||||
{
|
||||
recvData >> inviteCount;
|
||||
|
||||
for (uint32 i = 0; i < inviteCount && i < MaxPlayerInvites; ++i)
|
||||
{
|
||||
recvData.readPackGUID(invitee[i]);
|
||||
recvData >> status[i] >> rank[i];
|
||||
}
|
||||
}
|
||||
catch (ByteBufferException const&)
|
||||
{
|
||||
delete calendarEvent;
|
||||
calendarEvent = NULL;
|
||||
throw;
|
||||
}
|
||||
|
||||
SQLTransaction trans;
|
||||
if (inviteCount > 1)
|
||||
trans = CharacterDatabase.BeginTransaction();
|
||||
|
||||
for (uint32 i = 0; i < inviteCount && i < MaxPlayerInvites; ++i)
|
||||
{
|
||||
// 946684800 is 01/01/2000 00:00:00 - default response time
|
||||
CalendarInvite* invite = new CalendarInvite(sCalendarMgr->GetFreeInviteId(), calendarEvent->GetEventId(), invitee[i], guid, 946684800, CalendarInviteStatus(status[i]), CalendarModerationRank(rank[i]), "");
|
||||
sCalendarMgr->AddInvite(calendarEvent, invite, trans);
|
||||
}
|
||||
|
||||
if (inviteCount > 1)
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
}
|
||||
|
||||
sCalendarMgr->AddEvent(calendarEvent, CALENDAR_SENDTYPE_ADD);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarUpdateEvent(WorldPacket& recvData)
|
||||
{
|
||||
uint64 guid = _player->GetGUID();
|
||||
time_t oldEventTime;
|
||||
|
||||
uint64 eventId;
|
||||
uint64 inviteId;
|
||||
std::string title;
|
||||
std::string description;
|
||||
uint8 type;
|
||||
uint8 repetitionType;
|
||||
uint32 maxInvites;
|
||||
int32 dungeonId;
|
||||
uint32 eventPackedTime;
|
||||
uint32 timeZoneTime;
|
||||
uint32 flags;
|
||||
|
||||
recvData >> eventId >> inviteId >> title >> description >> type >> repetitionType >> maxInvites >> dungeonId;
|
||||
recvData.ReadPackedTime(eventPackedTime);
|
||||
recvData.ReadPackedTime(timeZoneTime);
|
||||
recvData >> flags;
|
||||
|
||||
// prevent events in the past
|
||||
// To Do: properly handle timezones and remove the "- time_t(86400L)" hack
|
||||
if (time_t(eventPackedTime) < (time(NULL) - time_t(86400L)))
|
||||
{
|
||||
recvData.rfinish();
|
||||
return;
|
||||
}
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_CALENDAR_UPDATE_EVENT [" UI64FMTD "] EventId [" UI64FMTD
|
||||
"], InviteId [" UI64FMTD "] Title %s, Description %s, type %u "
|
||||
"Repeatable %u, MaxInvites %u, Dungeon ID %d, Time %u "
|
||||
"Time2 %u, Flags %u", guid, eventId, inviteId, title.c_str(),
|
||||
description.c_str(), type, repetitionType, maxInvites, dungeonId,
|
||||
eventPackedTime, timeZoneTime, flags);
|
||||
|
||||
if (CalendarEvent* calendarEvent = sCalendarMgr->GetEvent(eventId))
|
||||
{
|
||||
oldEventTime = calendarEvent->GetEventTime();
|
||||
|
||||
calendarEvent->SetType(CalendarEventType(type));
|
||||
calendarEvent->SetFlags(flags);
|
||||
calendarEvent->SetEventTime(time_t(eventPackedTime));
|
||||
calendarEvent->SetTimeZoneTime(time_t(timeZoneTime)); // Not sure, seems constant from the little sniffs we have
|
||||
calendarEvent->SetDungeonId(dungeonId);
|
||||
calendarEvent->SetTitle(title);
|
||||
calendarEvent->SetDescription(description);
|
||||
|
||||
sCalendarMgr->UpdateEvent(calendarEvent);
|
||||
sCalendarMgr->SendCalendarEventUpdateAlert(*calendarEvent, oldEventTime);
|
||||
}
|
||||
else
|
||||
sCalendarMgr->SendCalendarCommandResult(guid, CALENDAR_ERROR_EVENT_INVALID);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarRemoveEvent(WorldPacket& recvData)
|
||||
{
|
||||
uint64 guid = _player->GetGUID();
|
||||
uint64 eventId;
|
||||
|
||||
recvData >> eventId;
|
||||
recvData.rfinish(); // Skip flags & invite ID, we don't use them
|
||||
|
||||
sCalendarMgr->RemoveEvent(eventId, guid);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarCopyEvent(WorldPacket& recvData)
|
||||
{
|
||||
uint64 guid = _player->GetGUID();
|
||||
uint64 eventId;
|
||||
uint64 inviteId;
|
||||
uint32 eventTime;
|
||||
|
||||
recvData >> eventId >> inviteId;
|
||||
recvData.ReadPackedTime(eventTime);
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_CALENDAR_COPY_EVENT [" UI64FMTD "], EventId [" UI64FMTD
|
||||
"] inviteId [" UI64FMTD "] Time: %u", guid, eventId, inviteId, eventTime);
|
||||
|
||||
// prevent events in the past
|
||||
// To Do: properly handle timezones and remove the "- time_t(86400L)" hack
|
||||
if (time_t(eventTime) < (time(NULL) - time_t(86400L)))
|
||||
{
|
||||
recvData.rfinish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (CalendarEvent* oldEvent = sCalendarMgr->GetEvent(eventId))
|
||||
{
|
||||
CalendarEvent* newEvent = new CalendarEvent(*oldEvent, sCalendarMgr->GetFreeEventId());
|
||||
newEvent->SetEventTime(time_t(eventTime));
|
||||
sCalendarMgr->AddEvent(newEvent, CALENDAR_SENDTYPE_COPY);
|
||||
|
||||
CalendarInviteStore invites = sCalendarMgr->GetEventInvites(eventId);
|
||||
SQLTransaction trans;
|
||||
if (invites.size() > 1)
|
||||
trans = CharacterDatabase.BeginTransaction();
|
||||
|
||||
for (CalendarInviteStore::const_iterator itr = invites.begin(); itr != invites.end(); ++itr)
|
||||
sCalendarMgr->AddInvite(newEvent, new CalendarInvite(**itr, sCalendarMgr->GetFreeInviteId(), newEvent->GetEventId()), trans);
|
||||
|
||||
if (invites.size() > 1)
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
// should we change owner when somebody makes a copy of event owned by another person?
|
||||
}
|
||||
else
|
||||
sCalendarMgr->SendCalendarCommandResult(guid, CALENDAR_ERROR_EVENT_INVALID);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarEventInvite(WorldPacket& recvData)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_CALENDAR_EVENT_INVITE");
|
||||
|
||||
uint64 playerGuid = _player->GetGUID();
|
||||
|
||||
uint64 eventId;
|
||||
uint64 inviteId;
|
||||
std::string name;
|
||||
bool isPreInvite;
|
||||
bool isGuildEvent;
|
||||
|
||||
uint64 inviteeGuid = 0;
|
||||
uint32 inviteeTeamId = TEAM_NEUTRAL;
|
||||
uint32 inviteeGuildId = 0;
|
||||
|
||||
recvData >> eventId >> inviteId >> name >> isPreInvite >> isGuildEvent;
|
||||
|
||||
if (Player* player = ObjectAccessor::FindPlayerByName(name.c_str(), false))
|
||||
{
|
||||
// Invitee is online
|
||||
inviteeGuid = player->GetGUID();
|
||||
inviteeTeamId = player->GetTeamId();
|
||||
inviteeGuildId = player->GetGuildId();
|
||||
}
|
||||
else
|
||||
{
|
||||
// xinef: Get Data From global storage
|
||||
if (uint32 guidLow = sWorld->GetGlobalPlayerGUID(name))
|
||||
{
|
||||
if (GlobalPlayerData const* playerData = sWorld->GetGlobalPlayerData(guidLow))
|
||||
{
|
||||
inviteeGuid = MAKE_NEW_GUID(guidLow, 0, HIGHGUID_PLAYER);
|
||||
inviteeTeamId = Player::TeamIdForRace(playerData->race);
|
||||
inviteeGuildId = playerData->guildId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!inviteeGuid)
|
||||
{
|
||||
sCalendarMgr->SendCalendarCommandResult(playerGuid, CALENDAR_ERROR_PLAYER_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_player->GetTeamId() != inviteeTeamId /*&& !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CALENDAR)*/)
|
||||
{
|
||||
sCalendarMgr->SendCalendarCommandResult(playerGuid, CALENDAR_ERROR_NOT_ALLIED);
|
||||
return;
|
||||
}
|
||||
|
||||
// xinef: zomg! sync query
|
||||
if (QueryResult result = CharacterDatabase.PQuery("SELECT flags FROM character_social WHERE guid = " UI64FMTD " AND friend = " UI64FMTD, inviteeGuid, playerGuid))
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
if (fields[0].GetUInt8() & SOCIAL_FLAG_IGNORED)
|
||||
{
|
||||
sCalendarMgr->SendCalendarCommandResult(playerGuid, CALENDAR_ERROR_IGNORING_YOU_S, name.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isPreInvite)
|
||||
{
|
||||
if (CalendarEvent* calendarEvent = sCalendarMgr->GetEvent(eventId))
|
||||
{
|
||||
if (calendarEvent->IsGuildEvent() && calendarEvent->GetGuildId() == inviteeGuildId)
|
||||
{
|
||||
// we can't invite guild members to guild events
|
||||
sCalendarMgr->SendCalendarCommandResult(playerGuid, CALENDAR_ERROR_NO_GUILD_INVITES);
|
||||
return;
|
||||
}
|
||||
|
||||
// 946684800 is 01/01/2000 00:00:00 - default response time
|
||||
CalendarInvite* invite = new CalendarInvite(sCalendarMgr->GetFreeInviteId(), eventId, inviteeGuid, playerGuid, 946684800, CALENDAR_STATUS_INVITED, CALENDAR_RANK_PLAYER, "");
|
||||
sCalendarMgr->AddInvite(calendarEvent, invite);
|
||||
}
|
||||
else
|
||||
sCalendarMgr->SendCalendarCommandResult(playerGuid, CALENDAR_ERROR_EVENT_INVALID);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isGuildEvent && inviteeGuildId == _player->GetGuildId())
|
||||
{
|
||||
sCalendarMgr->SendCalendarCommandResult(playerGuid, CALENDAR_ERROR_NO_GUILD_INVITES);
|
||||
return;
|
||||
}
|
||||
|
||||
// 946684800 is 01/01/2000 00:00:00 - default response time
|
||||
CalendarInvite* invite = new CalendarInvite(inviteId, 0, inviteeGuid, playerGuid, 946684800, CALENDAR_STATUS_INVITED, CALENDAR_RANK_PLAYER, "");
|
||||
sCalendarMgr->SendCalendarEventInvite(*invite);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarEventSignup(WorldPacket& recvData)
|
||||
{
|
||||
uint64 guid = _player->GetGUID();
|
||||
uint64 eventId;
|
||||
bool tentative;
|
||||
|
||||
recvData >> eventId >> tentative;
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_CALENDAR_EVENT_SIGNUP [" UI64FMTD "] EventId [" UI64FMTD "] Tentative %u", guid, eventId, tentative);
|
||||
|
||||
if (CalendarEvent* calendarEvent = sCalendarMgr->GetEvent(eventId))
|
||||
{
|
||||
if (calendarEvent->IsGuildEvent() && calendarEvent->GetGuildId() != _player->GetGuildId())
|
||||
{
|
||||
sCalendarMgr->SendCalendarCommandResult(guid, CALENDAR_ERROR_GUILD_PLAYER_NOT_IN_GUILD);
|
||||
return;
|
||||
}
|
||||
|
||||
CalendarInviteStatus status = tentative ? CALENDAR_STATUS_TENTATIVE : CALENDAR_STATUS_SIGNED_UP;
|
||||
CalendarInvite* invite = new CalendarInvite(sCalendarMgr->GetFreeInviteId(), eventId, guid, guid, time(NULL), status, CALENDAR_RANK_PLAYER, "");
|
||||
sCalendarMgr->AddInvite(calendarEvent, invite);
|
||||
sCalendarMgr->SendCalendarClearPendingAction(guid);
|
||||
}
|
||||
else
|
||||
sCalendarMgr->SendCalendarCommandResult(guid, CALENDAR_ERROR_EVENT_INVALID);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarEventRsvp(WorldPacket& recvData)
|
||||
{
|
||||
uint64 guid = _player->GetGUID();
|
||||
uint64 eventId;
|
||||
uint64 inviteId;
|
||||
uint32 status;
|
||||
|
||||
recvData >> eventId >> inviteId >> status;
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_CALENDAR_EVENT_RSVP [" UI64FMTD"] EventId ["
|
||||
UI64FMTD "], InviteId [" UI64FMTD "], status %u", guid, eventId,
|
||||
inviteId, status);
|
||||
|
||||
if (CalendarEvent* calendarEvent = sCalendarMgr->GetEvent(eventId))
|
||||
{
|
||||
// i think we still should be able to remove self from locked events
|
||||
if (status != CALENDAR_STATUS_REMOVED && calendarEvent->GetFlags() & CALENDAR_FLAG_INVITES_LOCKED)
|
||||
{
|
||||
sCalendarMgr->SendCalendarCommandResult(guid, CALENDAR_ERROR_EVENT_LOCKED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (CalendarInvite* invite = sCalendarMgr->GetInvite(inviteId))
|
||||
{
|
||||
invite->SetStatus(CalendarInviteStatus(status));
|
||||
invite->SetStatusTime(time(NULL));
|
||||
|
||||
sCalendarMgr->UpdateInvite(invite);
|
||||
sCalendarMgr->SendCalendarEventStatus(*calendarEvent, *invite);
|
||||
sCalendarMgr->SendCalendarClearPendingAction(guid);
|
||||
}
|
||||
else
|
||||
sCalendarMgr->SendCalendarCommandResult(guid, CALENDAR_ERROR_NO_INVITE); // correct?
|
||||
}
|
||||
else
|
||||
sCalendarMgr->SendCalendarCommandResult(guid, CALENDAR_ERROR_EVENT_INVALID);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarEventRemoveInvite(WorldPacket& recvData)
|
||||
{
|
||||
uint64 guid = _player->GetGUID();
|
||||
uint64 invitee;
|
||||
uint64 eventId;
|
||||
uint64 ownerInviteId; // isn't it sender's inviteId?
|
||||
uint64 inviteId;
|
||||
|
||||
recvData.readPackGUID(invitee);
|
||||
recvData >> inviteId >> ownerInviteId >> eventId;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_CALENDAR_EVENT_REMOVE_INVITE ["
|
||||
UI64FMTD "] EventId [" UI64FMTD "], ownerInviteId ["
|
||||
UI64FMTD "], Invitee ([" UI64FMTD "] id: [" UI64FMTD "])",
|
||||
guid, eventId, ownerInviteId, invitee, inviteId);
|
||||
|
||||
if (CalendarEvent* calendarEvent = sCalendarMgr->GetEvent(eventId))
|
||||
{
|
||||
if (calendarEvent->GetCreatorGUID() == invitee)
|
||||
{
|
||||
sCalendarMgr->SendCalendarCommandResult(guid, CALENDAR_ERROR_DELETE_CREATOR_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
sCalendarMgr->RemoveInvite(inviteId, eventId, guid);
|
||||
}
|
||||
else
|
||||
sCalendarMgr->SendCalendarCommandResult(guid, CALENDAR_ERROR_NO_INVITE);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarEventStatus(WorldPacket& recvData)
|
||||
{
|
||||
uint64 guid = _player->GetGUID();
|
||||
uint64 invitee;
|
||||
uint64 eventId;
|
||||
uint64 inviteId;
|
||||
uint64 ownerInviteId; // isn't it sender's inviteId?
|
||||
uint8 status;
|
||||
|
||||
recvData.readPackGUID(invitee);
|
||||
recvData >> eventId >> inviteId >> ownerInviteId >> status;
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_CALENDAR_EVENT_STATUS [" UI64FMTD"] EventId ["
|
||||
UI64FMTD "] ownerInviteId [" UI64FMTD "], Invitee ([" UI64FMTD "] id: ["
|
||||
UI64FMTD "], status %u", guid, eventId, ownerInviteId, invitee, inviteId, status);
|
||||
|
||||
if (CalendarEvent* calendarEvent = sCalendarMgr->GetEvent(eventId))
|
||||
{
|
||||
if (CalendarInvite* invite = sCalendarMgr->GetInvite(inviteId))
|
||||
{
|
||||
invite->SetStatus((CalendarInviteStatus)status);
|
||||
// not sure if we should set response time when moderator changes invite status
|
||||
//invite->SetStatusTime(time(NULL));
|
||||
|
||||
sCalendarMgr->UpdateInvite(invite);
|
||||
sCalendarMgr->SendCalendarEventStatus(*calendarEvent, *invite);
|
||||
sCalendarMgr->SendCalendarClearPendingAction(invitee);
|
||||
}
|
||||
else
|
||||
sCalendarMgr->SendCalendarCommandResult(guid, CALENDAR_ERROR_NO_INVITE); // correct?
|
||||
}
|
||||
else
|
||||
sCalendarMgr->SendCalendarCommandResult(guid, CALENDAR_ERROR_EVENT_INVALID);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarEventModeratorStatus(WorldPacket& recvData)
|
||||
{
|
||||
uint64 guid = _player->GetGUID();
|
||||
uint64 invitee;
|
||||
uint64 eventId;
|
||||
uint64 inviteId;
|
||||
uint64 ownerInviteId; // isn't it sender's inviteId?
|
||||
uint8 rank;
|
||||
|
||||
recvData.readPackGUID(invitee);
|
||||
recvData >> eventId >> inviteId >> ownerInviteId >> rank;
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_CALENDAR_EVENT_MODERATOR_STATUS [" UI64FMTD "] EventId ["
|
||||
UI64FMTD "] ownerInviteId [" UI64FMTD "], Invitee ([" UI64FMTD "] id: ["
|
||||
UI64FMTD "], rank %u", guid, eventId, ownerInviteId, invitee, inviteId, rank);
|
||||
|
||||
if (CalendarEvent* calendarEvent = sCalendarMgr->GetEvent(eventId))
|
||||
{
|
||||
if (CalendarInvite* invite = sCalendarMgr->GetInvite(inviteId))
|
||||
{
|
||||
invite->SetRank(CalendarModerationRank(rank));
|
||||
sCalendarMgr->UpdateInvite(invite);
|
||||
sCalendarMgr->SendCalendarEventModeratorStatusAlert(*calendarEvent, *invite);
|
||||
}
|
||||
else
|
||||
sCalendarMgr->SendCalendarCommandResult(guid, CALENDAR_ERROR_NO_INVITE); // correct?
|
||||
}
|
||||
else
|
||||
sCalendarMgr->SendCalendarCommandResult(guid, CALENDAR_ERROR_EVENT_INVALID);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarComplain(WorldPacket& recvData)
|
||||
{
|
||||
uint64 guid = _player->GetGUID();
|
||||
uint64 eventId;
|
||||
uint64 complainGUID;
|
||||
|
||||
recvData >> eventId >> complainGUID;
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_CALENDAR_COMPLAIN [" UI64FMTD "] EventId ["
|
||||
UI64FMTD "] guid [" UI64FMTD "]", guid, eventId, complainGUID);
|
||||
|
||||
// what to do with complains?
|
||||
}
|
||||
|
||||
void WorldSession::HandleCalendarGetNumPending(WorldPacket& /*recvData*/)
|
||||
{
|
||||
uint64 guid = _player->GetGUID();
|
||||
uint32 pending = sCalendarMgr->GetPlayerNumPending(guid);
|
||||
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_CALENDAR_GET_NUM_PENDING: [" UI64FMTD
|
||||
"] Pending: %u", guid, pending);
|
||||
|
||||
WorldPacket data(SMSG_CALENDAR_SEND_NUM_PENDING, 4);
|
||||
data << uint32(pending);
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleSetSavedInstanceExtend(WorldPacket& recvData)
|
||||
{
|
||||
uint32 mapId, difficulty;
|
||||
uint8 toggleExtendOn;
|
||||
recvData >> mapId >> difficulty>> toggleExtendOn;
|
||||
|
||||
const MapEntry* entry = sMapStore.LookupEntry(mapId);
|
||||
if (!entry || !entry->IsRaid())
|
||||
return;
|
||||
|
||||
InstancePlayerBind* instanceBind = sInstanceSaveMgr->PlayerGetBoundInstance(GetPlayer()->GetGUIDLow(), mapId, Difficulty(difficulty));
|
||||
if (!instanceBind || !instanceBind->perm || (bool)toggleExtendOn == instanceBind->extended)
|
||||
return;
|
||||
|
||||
instanceBind->extended = (bool)toggleExtendOn;
|
||||
|
||||
// update in db
|
||||
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_INSTANCE_EXTENDED);
|
||||
stmt->setUInt8(0, toggleExtendOn ? 1 : 0);
|
||||
stmt->setUInt32(1, GetPlayer()->GetGUIDLow());
|
||||
stmt->setUInt32(2, instanceBind->save->GetInstanceId());
|
||||
CharacterDatabase.Execute(stmt);
|
||||
|
||||
SendCalendarRaidLockoutUpdated(instanceBind->save, (bool)toggleExtendOn);
|
||||
}
|
||||
|
||||
// ----------------------------------- SEND ------------------------------------
|
||||
|
||||
void WorldSession::SendCalendarRaidLockout(InstanceSave const* save, bool add)
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_NETWORKIO, "%s", add ? "SMSG_CALENDAR_RAID_LOCKOUT_ADDED" : "SMSG_CALENDAR_RAID_LOCKOUT_REMOVED");
|
||||
time_t currTime = time(NULL);
|
||||
|
||||
WorldPacket data(SMSG_CALENDAR_RAID_LOCKOUT_REMOVED, (add ? 4 : 0) + 4 + 4 + 4 + 8);
|
||||
if (add)
|
||||
{
|
||||
data.SetOpcode(SMSG_CALENDAR_RAID_LOCKOUT_ADDED);
|
||||
data.AppendPackedTime(currTime);
|
||||
}
|
||||
|
||||
data << uint32(save->GetMapId());
|
||||
data << uint32(save->GetDifficulty());
|
||||
data << uint32(save->GetResetTime() >= currTime ? save->GetResetTime()-currTime : 0);
|
||||
data << uint64(MAKE_NEW_GUID(save->GetInstanceId(), 0, HIGHGUID_INSTANCE));
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::SendCalendarRaidLockoutUpdated(InstanceSave const* save, bool isExtended)
|
||||
{
|
||||
time_t currTime = time(NULL);
|
||||
time_t resetTime = isExtended ? save->GetExtendedResetTime() : save->GetResetTime();
|
||||
time_t resetTimeOp = isExtended ? save->GetResetTime() : save->GetExtendedResetTime();
|
||||
WorldPacket data(SMSG_CALENDAR_RAID_LOCKOUT_UPDATED, 4 + 4 + 4 + 4 + 8);
|
||||
data.AppendPackedTime(currTime);
|
||||
data << uint32(save->GetMapId());
|
||||
data << uint32(save->GetDifficulty());
|
||||
data << uint32(resetTimeOp >= currTime ? resetTimeOp-currTime : resetTimeOp); // pussywizard: old time in secs to reset
|
||||
data << uint32(resetTime >= currTime ? resetTime-currTime : 0); // pussywizard: new time in secs to reset
|
||||
SendPacket(&data);
|
||||
}
|
||||
340
src/server/game/Handlers/ChannelHandler.cpp
Normal file
340
src/server/game/Handlers/ChannelHandler.cpp
Normal file
@@ -0,0 +1,340 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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"
|
||||
#include "Player.h"
|
||||
|
||||
#include <cctype>
|
||||
|
||||
void WorldSession::HandleJoinChannel(WorldPacket& recvPacket)
|
||||
{
|
||||
uint32 channelId;
|
||||
uint8 unknown1, unknown2;
|
||||
std::string channelName, password;
|
||||
|
||||
recvPacket >> channelId >> unknown1 >> unknown2 >> channelName >> password;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_CHATSYS, "CMSG_JOIN_CHANNEL %s Channel: %u, unk1: %u, unk2: %u, channel: %s, password: %s",
|
||||
// GetPlayerInfo().c_str(), channelId, unknown1, unknown2, channelName.c_str(), password.c_str());
|
||||
|
||||
if (channelId)
|
||||
{
|
||||
ChatChannelsEntry const* channel = sChatChannelsStore.LookupEntry(channelId);
|
||||
if (!channel)
|
||||
return;
|
||||
|
||||
AreaTableEntry const* zone = GetAreaEntryByAreaID(GetPlayer()->GetZoneId());
|
||||
if (!zone || !GetPlayer()->CanJoinConstantChannelInZone(channel, zone))
|
||||
return;
|
||||
}
|
||||
|
||||
if (channelName.empty())
|
||||
return;
|
||||
|
||||
if (isdigit(channelName[0]))
|
||||
return;
|
||||
|
||||
// pussywizard: restrict allowed characters in channel name to avoid |0 and possibly other exploits
|
||||
//if (!ObjectMgr::IsValidChannelName(channelName))
|
||||
if (channelName.find("|") != std::string::npos || channelName.size() >= 100)
|
||||
return;
|
||||
|
||||
if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeamId()))
|
||||
{
|
||||
if (Channel* channel = cMgr->GetJoinChannel(channelName, channelId))
|
||||
channel->JoinChannel(GetPlayer(), password);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleLeaveChannel(WorldPacket& recvPacket)
|
||||
{
|
||||
uint32 unk;
|
||||
std::string channelName;
|
||||
recvPacket >> unk >> channelName;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_CHATSYS, "CMSG_LEAVE_CHANNEL %s Channel: %s, unk1: %u",
|
||||
// GetPlayerInfo().c_str(), channelName.c_str(), unk);
|
||||
|
||||
if (channelName.empty())
|
||||
return;
|
||||
|
||||
if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeamId()))
|
||||
{
|
||||
if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer()))
|
||||
channel->LeaveChannel(GetPlayer(), true);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelList(WorldPacket& recvPacket)
|
||||
{
|
||||
std::string channelName;
|
||||
recvPacket >> channelName;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_CHATSYS, "%s %s Channel: %s",
|
||||
// recvPacket.GetOpcode() == CMSG_CHANNEL_DISPLAY_LIST ? "CMSG_CHANNEL_DISPLAY_LIST" : "CMSG_CHANNEL_LIST",
|
||||
// GetPlayerInfo().c_str(), channelName.c_str());
|
||||
|
||||
if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeamId()))
|
||||
if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer()))
|
||||
channel->List(GetPlayer());
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelPassword(WorldPacket& recvPacket)
|
||||
{
|
||||
std::string channelName, password;
|
||||
recvPacket >> channelName >> password;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_CHATSYS, "CMSG_CHANNEL_PASSWORD %s Channel: %s, Password: %s",
|
||||
// GetPlayerInfo().c_str(), channelName.c_str(), password.c_str());
|
||||
|
||||
if (password.length() > MAX_CHANNEL_PASS_STR)
|
||||
return;
|
||||
|
||||
if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeamId()))
|
||||
if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer()))
|
||||
channel->Password(GetPlayer(), password);
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelSetOwner(WorldPacket& recvPacket)
|
||||
{
|
||||
std::string channelName, targetName;
|
||||
recvPacket >> channelName >> targetName;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_CHATSYS, "CMSG_CHANNEL_SET_OWNER %s Channel: %s, Target: %s",
|
||||
// GetPlayerInfo().c_str(), channelName.c_str(), targetName.c_str());
|
||||
|
||||
if (!normalizePlayerName(targetName))
|
||||
return;
|
||||
|
||||
if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeamId()))
|
||||
if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer()))
|
||||
channel->SetOwner(GetPlayer(), targetName);
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelOwner(WorldPacket& recvPacket)
|
||||
{
|
||||
std::string channelName;
|
||||
recvPacket >> channelName;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_CHATSYS, "CMSG_CHANNEL_OWNER %s Channel: %s",
|
||||
// GetPlayerInfo().c_str(), channelName.c_str());
|
||||
|
||||
if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeamId()))
|
||||
if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer()))
|
||||
channel->SendWhoOwner(GetPlayer()->GetGUID());
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelModerator(WorldPacket& recvPacket)
|
||||
{
|
||||
std::string channelName, targetName;
|
||||
recvPacket >> channelName >> targetName;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_CHATSYS, "CMSG_CHANNEL_MODERATOR %s Channel: %s, Target: %s",
|
||||
// GetPlayerInfo().c_str(), channelName.c_str(), targetName.c_str());
|
||||
|
||||
if (!normalizePlayerName(targetName))
|
||||
return;
|
||||
|
||||
if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeamId()))
|
||||
if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer()))
|
||||
channel->SetModerator(GetPlayer(), targetName);
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelUnmoderator(WorldPacket& recvPacket)
|
||||
{
|
||||
std::string channelName, targetName;
|
||||
recvPacket >> channelName >> targetName;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_CHATSYS, "CMSG_CHANNEL_UNMODERATOR %s Channel: %s, Target: %s",
|
||||
// GetPlayerInfo().c_str(), channelName.c_str(), targetName.c_str());
|
||||
|
||||
if (!normalizePlayerName(targetName))
|
||||
return;
|
||||
|
||||
if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeamId()))
|
||||
if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer()))
|
||||
channel->UnsetModerator(GetPlayer(), targetName);
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelMute(WorldPacket& recvPacket)
|
||||
{
|
||||
std::string channelName, targetName;
|
||||
recvPacket >> channelName >> targetName;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_CHATSYS, "CMSG_CHANNEL_MUTE %s Channel: %s, Target: %s",
|
||||
// GetPlayerInfo().c_str(), channelName.c_str(), targetName.c_str());
|
||||
|
||||
if (!normalizePlayerName(targetName))
|
||||
return;
|
||||
|
||||
if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeamId()))
|
||||
if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer()))
|
||||
channel->SetMute(GetPlayer(), targetName);
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelUnmute(WorldPacket& recvPacket)
|
||||
{
|
||||
std::string channelName, targetName;
|
||||
recvPacket >> channelName >> targetName;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_CHATSYS, "CMSG_CHANNEL_UNMUTE %s Channel: %s, Target: %s",
|
||||
// GetPlayerInfo().c_str(), channelName.c_str(), targetName.c_str());
|
||||
|
||||
if (!normalizePlayerName(targetName))
|
||||
return;
|
||||
|
||||
if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeamId()))
|
||||
if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer()))
|
||||
channel->UnsetMute(GetPlayer(), targetName);
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelInvite(WorldPacket& recvPacket)
|
||||
{
|
||||
std::string channelName, targetName;
|
||||
recvPacket >> channelName >> targetName;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_CHATSYS, "CMSG_CHANNEL_INVITE %s Channel: %s, Target: %s",
|
||||
// GetPlayerInfo().c_str(), channelName.c_str(), targetName.c_str());
|
||||
|
||||
if (!normalizePlayerName(targetName))
|
||||
return;
|
||||
|
||||
if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeamId()))
|
||||
if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer()))
|
||||
channel->Invite(GetPlayer(), targetName);
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelKick(WorldPacket& recvPacket)
|
||||
{
|
||||
std::string channelName, targetName;
|
||||
recvPacket >> channelName >> targetName;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_CHATSYS, "CMSG_CHANNEL_KICK %s Channel: %s, Target: %s",
|
||||
// GetPlayerInfo().c_str(), channelName.c_str(), targetName.c_str());
|
||||
|
||||
if (!normalizePlayerName(targetName))
|
||||
return;
|
||||
|
||||
if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeamId()))
|
||||
if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer()))
|
||||
channel->Kick(GetPlayer(), targetName);
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelBan(WorldPacket& recvPacket)
|
||||
{
|
||||
std::string channelName, targetName;
|
||||
recvPacket >> channelName >> targetName;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_CHATSYS, "CMSG_CHANNEL_BAN %s Channel: %s, Target: %s",
|
||||
// GetPlayerInfo().c_str(), channelName.c_str(), targetName.c_str());
|
||||
|
||||
if (!normalizePlayerName(targetName))
|
||||
return;
|
||||
|
||||
if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeamId()))
|
||||
if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer()))
|
||||
channel->Ban(GetPlayer(), targetName);
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelUnban(WorldPacket& recvPacket)
|
||||
{
|
||||
std::string channelName, targetName;
|
||||
recvPacket >> channelName >> targetName;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_CHATSYS, "CMSG_CHANNEL_UNBAN %s Channel: %s, Target: %s",
|
||||
// GetPlayerInfo().c_str(), channelName.c_str(), targetName.c_str());
|
||||
|
||||
if (!normalizePlayerName(targetName))
|
||||
return;
|
||||
|
||||
if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeamId()))
|
||||
if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer()))
|
||||
channel->UnBan(GetPlayer(), targetName);
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelAnnouncements(WorldPacket& recvPacket)
|
||||
{
|
||||
std::string channelName;
|
||||
recvPacket >> channelName;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_CHATSYS, "CMSG_CHANNEL_ANNOUNCEMENTS %s Channel: %s",
|
||||
// GetPlayerInfo().c_str(), channelName.c_str());
|
||||
|
||||
if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeamId()))
|
||||
if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer()))
|
||||
channel->Announce(GetPlayer());
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelDisplayListQuery(WorldPacket &recvPacket)
|
||||
{
|
||||
// this should be OK because the 2 function _were_ the same
|
||||
HandleChannelList(recvPacket);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGetChannelMemberCount(WorldPacket &recvPacket)
|
||||
{
|
||||
std::string channelName;
|
||||
recvPacket >> channelName;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_CHATSYS, "CMSG_GET_CHANNEL_MEMBER_COUNT %s Channel: %s",
|
||||
// GetPlayerInfo().c_str(), channelName.c_str());
|
||||
|
||||
if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeamId()))
|
||||
{
|
||||
if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer()))
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_CHATSYS, "SMSG_CHANNEL_MEMBER_COUNT %s Channel: %s Count: %u",
|
||||
GetPlayerInfo().c_str(), channelName.c_str(), channel->GetNumPlayers());
|
||||
|
||||
WorldPacket data(SMSG_CHANNEL_MEMBER_COUNT, channel->GetName().size() + 1 + 4);
|
||||
data << channel->GetName();
|
||||
data << uint8(channel->GetFlags());
|
||||
data << uint32(channel->GetNumPlayers());
|
||||
SendPacket(&data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleSetChannelWatch(WorldPacket &recvPacket)
|
||||
{
|
||||
std::string channelName;
|
||||
recvPacket >> channelName;
|
||||
|
||||
GetPlayer()->ClearChannelWatch();
|
||||
|
||||
if (channelName.empty())
|
||||
return;
|
||||
|
||||
if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeamId()))
|
||||
if (Channel* channel = cMgr->GetChannel(channelName, NULL, false))
|
||||
channel->AddWatching(GetPlayer());
|
||||
}
|
||||
|
||||
void WorldSession::HandleClearChannelWatch(WorldPacket &recvPacket)
|
||||
{
|
||||
std::string channelName;
|
||||
recvPacket >> channelName;
|
||||
|
||||
if (channelName.empty())
|
||||
return;
|
||||
|
||||
if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeamId()))
|
||||
if (Channel* channel = cMgr->GetChannel(channelName, NULL, false))
|
||||
channel->RemoveWatching(GetPlayer());
|
||||
}
|
||||
2610
src/server/game/Handlers/CharacterHandler.cpp
Normal file
2610
src/server/game/Handlers/CharacterHandler.cpp
Normal file
File diff suppressed because it is too large
Load Diff
690
src/server/game/Handlers/ChatHandler.cpp
Normal file
690
src/server/game/Handlers/ChatHandler.cpp
Normal file
@@ -0,0 +1,690 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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"
|
||||
|
||||
void WorldSession::HandleMessagechatOpcode(WorldPacket & recvData)
|
||||
{
|
||||
uint32 type;
|
||||
uint32 lang;
|
||||
|
||||
recvData >> type;
|
||||
recvData >> lang;
|
||||
|
||||
if (type >= MAX_CHAT_MSG_TYPE)
|
||||
{
|
||||
sLog->outError("CHAT: Wrong message type received: %u", type);
|
||||
recvData.rfinish();
|
||||
return;
|
||||
}
|
||||
|
||||
Player* sender = GetPlayer();
|
||||
|
||||
//sLog->outDebug("CHAT: packet received. type %u, lang %u", type, lang);
|
||||
|
||||
// pussywizard: chatting on most chat types requires 2 hours played to prevent spam/abuse
|
||||
if (AccountMgr::IsPlayerAccount(GetSecurity()))
|
||||
switch (type)
|
||||
{
|
||||
case CHAT_MSG_ADDON:
|
||||
case CHAT_MSG_PARTY:
|
||||
case CHAT_MSG_RAID:
|
||||
case CHAT_MSG_GUILD:
|
||||
case CHAT_MSG_OFFICER:
|
||||
case CHAT_MSG_AFK:
|
||||
case CHAT_MSG_DND:
|
||||
case CHAT_MSG_RAID_LEADER:
|
||||
case CHAT_MSG_RAID_WARNING:
|
||||
case CHAT_MSG_BATTLEGROUND:
|
||||
case CHAT_MSG_BATTLEGROUND_LEADER:
|
||||
case CHAT_MSG_PARTY_LEADER:
|
||||
break;
|
||||
default:
|
||||
if (sender->GetTotalPlayedTime() < 2*HOUR)
|
||||
{
|
||||
SendNotification("Speaking is allowed after playing for at least 2 hours. You may use party and guild chat.");
|
||||
recvData.rfinish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// pussywizard:
|
||||
switch (type)
|
||||
{
|
||||
case CHAT_MSG_SAY:
|
||||
case CHAT_MSG_YELL:
|
||||
case CHAT_MSG_EMOTE:
|
||||
case CHAT_MSG_TEXT_EMOTE:
|
||||
case CHAT_MSG_AFK:
|
||||
case CHAT_MSG_DND:
|
||||
if (sender->IsSpectator())
|
||||
{
|
||||
recvData.rfinish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// prevent talking at unknown language (cheating)
|
||||
LanguageDesc const* langDesc = GetLanguageDescByID(lang);
|
||||
if (!langDesc)
|
||||
{
|
||||
SendNotification(LANG_UNKNOWN_LANGUAGE);
|
||||
recvData.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);
|
||||
recvData.rfinish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (lang == LANG_ADDON)
|
||||
{
|
||||
// LANG_ADDON is only valid for the following message types
|
||||
switch (type)
|
||||
{
|
||||
case CHAT_MSG_PARTY:
|
||||
case CHAT_MSG_RAID:
|
||||
case CHAT_MSG_GUILD:
|
||||
case CHAT_MSG_BATTLEGROUND:
|
||||
case CHAT_MSG_WHISPER:
|
||||
// check if addon messages are disabled
|
||||
if (!sWorld->getBoolConfig(CONFIG_ADDON_CHANNEL))
|
||||
{
|
||||
recvData.rfinish();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
sLog->outError("Player %s (GUID: %u) sent a chatmessage with an invalid language/message type combination",
|
||||
GetPlayer()->GetName().c_str(), GetPlayer()->GetGUIDLow());
|
||||
|
||||
recvData.rfinish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// LANG_ADDON should not be changed nor be affected by flood control
|
||||
else
|
||||
{
|
||||
uint32 specialMessageLimit = 0;
|
||||
// send in universal language if player in .gmon mode (ignore spell effects)
|
||||
if (sender->IsGameMaster())
|
||||
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:
|
||||
specialMessageLimit = 35;
|
||||
break;
|
||||
case CHAT_MSG_GUILD:
|
||||
case CHAT_MSG_OFFICER:
|
||||
specialMessageLimit = 15;
|
||||
break;
|
||||
case CHAT_MSG_WHISPER:
|
||||
if (sender->getLevel() >= 80)
|
||||
specialMessageLimit = 15;
|
||||
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 (type != CHAT_MSG_AFK && type != CHAT_MSG_DND)
|
||||
sender->UpdateSpeakTime(specialMessageLimit);
|
||||
}
|
||||
|
||||
// pussywizard: optimization
|
||||
/*if (sender->HasAura(1852) && type != CHAT_MSG_WHISPER)
|
||||
{
|
||||
SendNotification(GetTrinityString(LANG_GM_SILENCE), sender->GetName().c_str());
|
||||
recvData.rfinish();
|
||||
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:
|
||||
recvData >> msg;
|
||||
break;
|
||||
case CHAT_MSG_WHISPER:
|
||||
recvData >> to;
|
||||
recvData >> msg;
|
||||
break;
|
||||
case CHAT_MSG_CHANNEL:
|
||||
recvData >> channel;
|
||||
recvData >> msg;
|
||||
break;
|
||||
case CHAT_MSG_AFK:
|
||||
case CHAT_MSG_DND:
|
||||
recvData >> msg;
|
||||
ignoreChecks = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Strip invisible characters for non-addon messages
|
||||
if (lang != LANG_ADDON && sWorld->getBoolConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
|
||||
stripLineInvisibleChars(msg);
|
||||
|
||||
// pussywizard:
|
||||
if (lang != LANG_ADDON && msg.find("|0") != std::string::npos)
|
||||
return;
|
||||
|
||||
if (!ignoreChecks)
|
||||
{
|
||||
if (msg.empty())
|
||||
return;
|
||||
|
||||
if (ChatHandler(this).ParseCommands(msg.c_str()))
|
||||
return;
|
||||
|
||||
if (!_player->CanSpeak())
|
||||
{
|
||||
std::string timeStr = secsToTimeString(m_muteTime - time(NULL));
|
||||
SendNotification(GetTrinityString(LANG_WAIT_BEFORE_SPEAKING), timeStr.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (lang != LANG_ADDON)
|
||||
{
|
||||
if (sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY) && !ChatHandler(this).isValidChatMessage(msg.c_str()))
|
||||
{
|
||||
//sLog->outError("Player %s (GUID: %u) sent a chatmessage with an invalid link: %s", GetPlayer()->GetName().c_str(),
|
||||
// GetPlayer()->GetGUIDLow(), msg.c_str());
|
||||
|
||||
if (sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_KICK))
|
||||
KickPlayer();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// exploit
|
||||
size_t found1 = msg.find("|Hquest");
|
||||
if (found1 != std::string::npos)
|
||||
{
|
||||
size_t found2 = msg.find(":", found1+8);
|
||||
size_t found3 = msg.find("|", found1+8);
|
||||
if (found3 != std::string::npos)
|
||||
{
|
||||
if (found2 == std::string::npos)
|
||||
return;
|
||||
if (found2 > found3)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case CHAT_MSG_SAY:
|
||||
case CHAT_MSG_EMOTE:
|
||||
case CHAT_MSG_YELL:
|
||||
{
|
||||
// Prevent cheating
|
||||
if (!sender->IsAlive())
|
||||
return;
|
||||
|
||||
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 = ObjectAccessor::FindPlayerByName(to, false);
|
||||
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 (senderIsPlayer && receiverIsPlayer)
|
||||
if (GetPlayer()->GetTeamId() != receiver->GetTeamId())
|
||||
{
|
||||
SendWrongFactionNotice();
|
||||
return;
|
||||
}
|
||||
|
||||
// pussywizard: optimization
|
||||
/*if (GetPlayer()->HasAura(1852) && !receiver->IsGameMaster())
|
||||
{
|
||||
SendNotification(GetTrinityString(LANG_GM_SILENCE), GetPlayer()->GetName().c_str());
|
||||
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 = sender->GetGroup();
|
||||
if (!group || group->isBGGroup())
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == CHAT_MSG_PARTY_LEADER && !group->IsLeader(sender->GetGUID()))
|
||||
return;
|
||||
|
||||
WorldPacket data;
|
||||
ChatHandler::BuildChatPacket(data, ChatMsg(type), Language(lang), sender, NULL, msg);
|
||||
group->BroadcastPacket(&data, false, group->GetMemberGroup(GetPlayer()->GetGUID()));
|
||||
} break;
|
||||
case CHAT_MSG_GUILD:
|
||||
{
|
||||
if (GetPlayer()->GetGuildId())
|
||||
{
|
||||
if (Guild* guild = sGuildMgr->GetGuildById(GetPlayer()->GetGuildId()))
|
||||
{
|
||||
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()))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
WorldPacket data;
|
||||
ChatHandler::BuildChatPacket(data, CHAT_MSG_RAID, Language(lang), sender, NULL, msg);
|
||||
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(sender->GetGUID()))
|
||||
return;
|
||||
}
|
||||
|
||||
WorldPacket data;
|
||||
ChatHandler::BuildChatPacket(data, CHAT_MSG_RAID_LEADER, Language(lang), sender, NULL, msg);
|
||||
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;
|
||||
|
||||
WorldPacket data;
|
||||
//in battleground, raid warning is sent only to players in battleground - code is ok
|
||||
ChatHandler::BuildChatPacket(data, CHAT_MSG_RAID_WARNING, Language(lang), sender, NULL, msg);
|
||||
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;
|
||||
|
||||
WorldPacket data;
|
||||
ChatHandler::BuildChatPacket(data, CHAT_MSG_BATTLEGROUND, Language(lang), sender, NULL, msg);
|
||||
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;
|
||||
|
||||
WorldPacket data;
|
||||
ChatHandler::BuildChatPacket(data, CHAT_MSG_BATTLEGROUND_LEADER, Language(lang), sender, NULL, msg);
|
||||
group->BroadcastPacket(&data, false);
|
||||
} break;
|
||||
case CHAT_MSG_CHANNEL:
|
||||
{
|
||||
if (AccountMgr::IsPlayerAccount(GetSecurity()))
|
||||
{
|
||||
if (sender->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::forTeam(sender->GetTeamId()))
|
||||
{
|
||||
if (Channel* chn = cMgr->GetChannel(channel, sender))
|
||||
{
|
||||
chn->Say(sender->GetGUID(), msg.c_str(), lang);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case CHAT_MSG_AFK:
|
||||
{
|
||||
if (!sender->IsInCombat())
|
||||
{
|
||||
if (sender->isAFK()) // Already AFK
|
||||
{
|
||||
if (msg.empty())
|
||||
sender->ToggleAFK(); // Remove AFK
|
||||
else
|
||||
sender->autoReplyMsg = msg; // Update message
|
||||
}
|
||||
else // New AFK mode
|
||||
{
|
||||
sender->autoReplyMsg = msg.empty() ? GetTrinityString(LANG_PLAYER_AFK_DEFAULT) : msg;
|
||||
|
||||
if (sender->isDND())
|
||||
sender->ToggleDND();
|
||||
|
||||
sender->ToggleAFK();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CHAT_MSG_DND:
|
||||
{
|
||||
if (sender->isDND()) // Already DND
|
||||
{
|
||||
if (msg.empty())
|
||||
sender->ToggleDND(); // Remove DND
|
||||
else
|
||||
sender->autoReplyMsg = msg; // Update message
|
||||
}
|
||||
else // New DND mode
|
||||
{
|
||||
sender->autoReplyMsg = msg.empty() ? GetTrinityString(LANG_PLAYER_DND_DEFAULT) : msg;
|
||||
|
||||
if (sender->isAFK())
|
||||
sender->ToggleAFK();
|
||||
|
||||
sender->ToggleDND();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
sLog->outError("CHAT: unknown message type %u, lang: %u", type, lang);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleEmoteOpcode(WorldPacket & recvData)
|
||||
{
|
||||
if (!GetPlayer()->IsAlive() || GetPlayer()->HasUnitState(UNIT_STATE_DIED))
|
||||
return;
|
||||
|
||||
if (GetPlayer()->IsSpectator())
|
||||
return;
|
||||
|
||||
uint32 emote;
|
||||
recvData >> 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)
|
||||
{
|
||||
std::string const name(i_target ? i_target->GetNameForLocaleIdx(loc_idx) : "");
|
||||
uint32 namlen = name.size();
|
||||
|
||||
data.Initialize(SMSG_TEXT_EMOTE, 20 + namlen);
|
||||
data << i_player.GetGUID();
|
||||
data << uint32(i_text_emote);
|
||||
data << uint32(i_emote_num);
|
||||
data << uint32(namlen);
|
||||
if (namlen > 1)
|
||||
data << name;
|
||||
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 & recvData)
|
||||
{
|
||||
if (!GetPlayer()->IsAlive())
|
||||
return;
|
||||
|
||||
GetPlayer()->UpdateSpeakTime();
|
||||
|
||||
if (!GetPlayer()->CanSpeak())
|
||||
{
|
||||
std::string timeStr = secsToTimeString(m_muteTime - time(NULL));
|
||||
SendNotification(GetTrinityString(LANG_WAIT_BEFORE_SPEAKING), timeStr.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetPlayer()->IsSpectator())
|
||||
return;
|
||||
|
||||
uint32 text_emote, emoteNum;
|
||||
uint64 guid;
|
||||
|
||||
recvData >> text_emote;
|
||||
recvData >> emoteNum;
|
||||
recvData >> 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_STATE_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()->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& recvData)
|
||||
{
|
||||
uint64 iguid;
|
||||
uint8 unk;
|
||||
//sLog->outDebug(LOG_FILTER_PACKETIO, "WORLD: Received CMSG_CHAT_IGNORED");
|
||||
|
||||
recvData >> iguid;
|
||||
recvData >> unk; // probably related to spam reporting
|
||||
|
||||
Player* player = ObjectAccessor::FindPlayerInOrOutOfWorld(iguid);
|
||||
if (!player)
|
||||
return;
|
||||
|
||||
WorldPacket data;
|
||||
ChatHandler::BuildChatPacket(data, CHAT_MSG_IGNORED, LANG_UNIVERSAL, _player, _player, GetPlayer()->GetName());
|
||||
player->GetSession()->SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelDeclineInvite(WorldPacket &recvPacket)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "Opcode %u", recvPacket.GetOpcode());
|
||||
}
|
||||
|
||||
void WorldSession::SendPlayerNotFoundNotice(std::string const& name)
|
||||
{
|
||||
WorldPacket data(SMSG_CHAT_PLAYER_NOT_FOUND, name.size()+1);
|
||||
data << name;
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::SendPlayerAmbiguousNotice(std::string const& 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);
|
||||
}
|
||||
99
src/server/game/Handlers/CombatHandler.cpp
Normal file
99
src/server/game/Handlers/CombatHandler.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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"
|
||||
#include "Player.h"
|
||||
#include "Opcodes.h"
|
||||
|
||||
void WorldSession::HandleAttackSwingOpcode(WorldPacket& recvData)
|
||||
{
|
||||
uint64 guid;
|
||||
recvData >> 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 & /*recvData*/)
|
||||
{
|
||||
GetPlayer()->AttackStop();
|
||||
}
|
||||
|
||||
void WorldSession::HandleSetSheathedOpcode(WorldPacket& recvData)
|
||||
{
|
||||
uint32 sheathed;
|
||||
recvData >> 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);
|
||||
}
|
||||
79
src/server/game/Handlers/DuelHandler.cpp
Normal file
79
src/server/game/Handlers/DuelHandler.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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().c_str());
|
||||
;//sLog->outStaticDebug("Player 2 is: %u (%s)", plTarget->GetGUIDLow(), plTarget->GetName().c_str());
|
||||
|
||||
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);
|
||||
}
|
||||
1094
src/server/game/Handlers/GroupHandler.cpp
Normal file
1094
src/server/game/Handlers/GroupHandler.cpp
Normal file
File diff suppressed because it is too large
Load Diff
534
src/server/game/Handlers/GuildHandler.cpp
Normal file
534
src/server/game/Handlers/GuildHandler.cpp
Normal file
@@ -0,0 +1,534 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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"
|
||||
|
||||
void WorldSession::HandleGuildQueryOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
uint32 guildId;
|
||||
recvPacket >> guildId;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "CMSG_GUILD_QUERY [%s]: Guild: %u", GetPlayerInfo().c_str(), guildId);
|
||||
if (!guildId)
|
||||
return;
|
||||
|
||||
if (Guild* guild = sGuildMgr->GetGuildById(guildId))
|
||||
guild->HandleQuery(this);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildCreateOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
std::string name;
|
||||
recvPacket >> name;
|
||||
|
||||
sLog->outError("CMSG_GUILD_CREATE: Possible hacking-attempt: %s tried to create a guild [Name: %s] using cheats", GetPlayerInfo().c_str(), name.c_str());
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildInviteOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
std::string invitedName;
|
||||
recvPacket >> invitedName;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "CMSG_GUILD_INVITE [%s]: Invited: %s", GetPlayerInfo().c_str(), invitedName.c_str());
|
||||
if (normalizePlayerName(invitedName))
|
||||
if (Guild* guild = GetPlayer()->GetGuild())
|
||||
guild->HandleInviteMember(this, invitedName);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildRemoveOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
std::string playerName;
|
||||
recvPacket >> playerName;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "CMSG_GUILD_REMOVE [%s]: Target: %s", GetPlayerInfo().c_str(), playerName.c_str());
|
||||
|
||||
if (normalizePlayerName(playerName))
|
||||
if (Guild* guild = GetPlayer()->GetGuild())
|
||||
guild->HandleRemoveMember(this, playerName);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildAcceptOpcode(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "CMSG_GUILD_ACCEPT [%s]", GetPlayer()->GetName().c_str());
|
||||
|
||||
if (!GetPlayer()->GetGuildId())
|
||||
if (Guild* guild = sGuildMgr->GetGuildById(GetPlayer()->GetGuildIdInvited()))
|
||||
guild->HandleAcceptMember(this);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildDeclineOpcode(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "CMSG_GUILD_DECLINE [%s]", GetPlayerInfo().c_str());
|
||||
|
||||
GetPlayer()->SetGuildIdInvited(0);
|
||||
GetPlayer()->SetInGuild(0);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildInfoOpcode(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "CMSG_GUILD_INFO [%s]", GetPlayerInfo().c_str());
|
||||
|
||||
if (Guild* guild = GetPlayer()->GetGuild())
|
||||
guild->SendInfo(this);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildRosterOpcode(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "CMSG_GUILD_ROSTER [%s]", GetPlayerInfo().c_str());
|
||||
|
||||
if (Guild* guild = GetPlayer()->GetGuild())
|
||||
guild->HandleRoster(this);
|
||||
else
|
||||
Guild::SendCommandResult(this, GUILD_COMMAND_ROSTER, ERR_GUILD_PLAYER_NOT_IN_GUILD);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildPromoteOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
std::string playerName;
|
||||
recvPacket >> playerName;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "CMSG_GUILD_PROMOTE [%s]: Target: %s", GetPlayerInfo().c_str(), playerName.c_str());
|
||||
|
||||
if (normalizePlayerName(playerName))
|
||||
if (Guild* guild = GetPlayer()->GetGuild())
|
||||
guild->HandleUpdateMemberRank(this, playerName, false);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildDemoteOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
std::string playerName;
|
||||
recvPacket >> playerName;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "CMSG_GUILD_DEMOTE [%s]: Target: %s", GetPlayerInfo().c_str(), playerName.c_str());
|
||||
|
||||
if (normalizePlayerName(playerName))
|
||||
if (Guild* guild = GetPlayer()->GetGuild())
|
||||
guild->HandleUpdateMemberRank(this, playerName, true);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildLeaveOpcode(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "CMSG_GUILD_LEAVE [%s]", GetPlayerInfo().c_str());
|
||||
|
||||
if (Guild* guild = GetPlayer()->GetGuild())
|
||||
guild->HandleLeaveMember(this);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildDisbandOpcode(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "CMSG_GUILD_DISBAND [%s]", GetPlayerInfo().c_str());
|
||||
|
||||
if (Guild* guild = GetPlayer()->GetGuild())
|
||||
guild->HandleDisband(this);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildLeaderOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
std::string name;
|
||||
recvPacket >> name;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "CMSG_GUILD_LEADER [%s]: Target: %s", GetPlayerInfo().c_str(), name.c_str());
|
||||
|
||||
if (normalizePlayerName(name))
|
||||
if (Guild* guild = GetPlayer()->GetGuild())
|
||||
guild->HandleSetLeader(this, name);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildMOTDOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
std::string motd;
|
||||
recvPacket >> motd;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "CMSG_GUILD_MOTD [%s]: MOTD: %s", GetPlayerInfo().c_str(), motd.c_str());
|
||||
|
||||
if (Guild* guild = GetPlayer()->GetGuild())
|
||||
guild->HandleSetMOTD(this, motd);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildSetPublicNoteOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
std::string playerName;
|
||||
std::string note;
|
||||
recvPacket >> playerName >> note;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "CMSG_GUILD_SET_PUBLIC_NOTE [%s]: Target: %s, Note: %s",
|
||||
// GetPlayerInfo().c_str(), playerName.c_str(), note.c_str());
|
||||
|
||||
if (normalizePlayerName(playerName))
|
||||
if (Guild* guild = GetPlayer()->GetGuild())
|
||||
guild->HandleSetMemberNote(this, playerName, note, true);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildSetOfficerNoteOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
std::string playerName;
|
||||
std::string note;
|
||||
recvPacket >> playerName >> note;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "CMSG_GUILD_SET_OFFICER_NOTE [%s]: Target: %s, Note: %s",
|
||||
// GetPlayerInfo().c_str(), playerName.c_str(), note.c_str());
|
||||
|
||||
if (normalizePlayerName(playerName))
|
||||
if (Guild* guild = GetPlayer()->GetGuild())
|
||||
guild->HandleSetMemberNote(this, playerName, note, false);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildRankOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
uint32 rankId;
|
||||
recvPacket >> rankId;
|
||||
|
||||
uint32 rights;
|
||||
recvPacket >> rights;
|
||||
|
||||
std::string rankName;
|
||||
recvPacket >> rankName;
|
||||
|
||||
uint32 money;
|
||||
recvPacket >> money;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "CMSG_GUILD_RANK [%s]: Rank: %s (%u)", GetPlayerInfo().c_str(), rankName.c_str(), rankId);
|
||||
|
||||
Guild* guild = GetPlayer()->GetGuild();
|
||||
if (!guild)
|
||||
{
|
||||
recvPacket.rpos(recvPacket.wpos());
|
||||
return;
|
||||
}
|
||||
|
||||
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(tabId, bankRights, slots);
|
||||
}
|
||||
|
||||
guild->HandleSetRankInfo(this, rankId, rankName, rights, money, rightsAndSlots);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildAddRankOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
std::string rankName;
|
||||
recvPacket >> rankName;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "CMSG_GUILD_ADD_RANK [%s]: Rank: %s", GetPlayerInfo().c_str(), rankName.c_str());
|
||||
|
||||
if (Guild* guild = GetPlayer()->GetGuild())
|
||||
guild->HandleAddNewRank(this, rankName);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildDelRankOpcode(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "CMSG_GUILD_DEL_RANK [%s]", GetPlayerInfo().c_str());
|
||||
|
||||
if (Guild* guild = GetPlayer()->GetGuild())
|
||||
guild->HandleRemoveLowestRank(this);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildChangeInfoTextOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
std::string info;
|
||||
recvPacket >> info;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "CMSG_GUILD_INFO_TEXT [%s]: %s", GetPlayerInfo().c_str(), info.c_str());
|
||||
|
||||
if (Guild* guild = GetPlayer()->GetGuild())
|
||||
guild->HandleSetInfo(this, info);
|
||||
}
|
||||
|
||||
void WorldSession::HandleSaveGuildEmblemOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
uint64 vendorGuid;
|
||||
recvPacket >> vendorGuid;
|
||||
|
||||
EmblemInfo emblemInfo;
|
||||
emblemInfo.ReadPacket(recvPacket);
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "MSG_SAVE_GUILD_EMBLEM [%s]: Guid: [" UI64FMTD
|
||||
// "] Style: %d, Color: %d, BorderStyle: %d, BorderColor: %d, BackgroundColor: %d"
|
||||
// , GetPlayerInfo().c_str(), vendorGuid, emblemInfo.GetStyle()
|
||||
// , emblemInfo.GetColor(), emblemInfo.GetBorderStyle()
|
||||
// , emblemInfo.GetBorderColor(), emblemInfo.GetBackgroundColor());
|
||||
|
||||
if (GetPlayer()->GetNPCIfCanInteractWith(vendorGuid, UNIT_NPC_FLAG_TABARDDESIGNER))
|
||||
{
|
||||
// Remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
if (Guild* guild = GetPlayer()->GetGuild())
|
||||
guild->HandleSetEmblem(this, emblemInfo);
|
||||
else
|
||||
Guild::SendSaveEmblemResult(this, ERR_GUILDEMBLEM_NOGUILD); // "You are not part of a guild!";
|
||||
}
|
||||
else
|
||||
Guild::SendSaveEmblemResult(this, ERR_GUILDEMBLEM_INVALIDVENDOR); // "That's not an emblem vendor!"
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildEventLogQueryOpcode(WorldPacket& /* recvPacket */)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "MSG_GUILD_EVENT_LOG_QUERY [%s]", GetPlayerInfo().c_str());
|
||||
|
||||
if (Guild* guild = GetPlayer()->GetGuild())
|
||||
guild->SendEventLog(this);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildBankMoneyWithdrawn(WorldPacket & /* recvData */)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "MSG_GUILD_BANK_MONEY_WITHDRAWN [%s]", GetPlayerInfo().c_str());
|
||||
|
||||
if (Guild* guild = GetPlayer()->GetGuild())
|
||||
guild->SendMoneyInfo(this);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildPermissions(WorldPacket& /* recvData */)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "MSG_GUILD_PERMISSIONS [%s]", GetPlayerInfo().c_str());
|
||||
|
||||
if (Guild* guild = GetPlayer()->GetGuild())
|
||||
guild->SendPermissions(this);
|
||||
}
|
||||
|
||||
// Called when clicking on Guild bank gameobject
|
||||
void WorldSession::HandleGuildBankerActivate(WorldPacket& recvData)
|
||||
{
|
||||
uint64 guid;
|
||||
bool sendAllSlots;
|
||||
recvData >> guid >> sendAllSlots;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "CMSG_GUILD_BANKER_ACTIVATE [%s]: Go: [" UI64FMTD "] AllSlots: %u"
|
||||
// , GetPlayerInfo().c_str(), guid, sendAllSlots);
|
||||
|
||||
Guild * const guild = GetPlayer()->GetGuild();
|
||||
if (!guild)
|
||||
{
|
||||
Guild::SendCommandResult(this, GUILD_COMMAND_VIEW_TAB, ERR_GUILD_PLAYER_NOT_IN_GUILD);
|
||||
return;
|
||||
}
|
||||
|
||||
guild->SendBankTabsInfo(this, sendAllSlots);
|
||||
}
|
||||
|
||||
// Called when opening guild bank tab only (first one)
|
||||
void WorldSession::HandleGuildBankQueryTab(WorldPacket& recvData)
|
||||
{
|
||||
uint64 guid;
|
||||
uint8 tabId;
|
||||
bool full;
|
||||
|
||||
recvData >> guid >> tabId >> full;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "CMSG_GUILD_BANK_QUERY_TAB [%s]: Go: [" UI64FMTD "], TabId: %u, ShowTabs: %u"
|
||||
// , GetPlayerInfo().c_str(), guid, tabId, full);
|
||||
|
||||
if (GetPlayer()->GetGameObjectIfCanInteractWith(guid, GAMEOBJECT_TYPE_GUILD_BANK))
|
||||
if (Guild* guild = GetPlayer()->GetGuild())
|
||||
guild->SendBankTabData(this, tabId);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildBankDepositMoney(WorldPacket& recvData)
|
||||
{
|
||||
uint64 guid;
|
||||
uint32 money;
|
||||
recvData >> guid >> money;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "CMSG_GUILD_BANK_DEPOSIT_MONEY [%s]: Go: [" UI64FMTD "], money: %u",
|
||||
// GetPlayerInfo().c_str(), guid, money);
|
||||
|
||||
if (GetPlayer()->GetGameObjectIfCanInteractWith(guid, GAMEOBJECT_TYPE_GUILD_BANK))
|
||||
if (money && GetPlayer()->HasEnoughMoney(money))
|
||||
if (Guild* guild = GetPlayer()->GetGuild())
|
||||
guild->HandleMemberDepositMoney(this, money);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildBankWithdrawMoney(WorldPacket& recvData)
|
||||
{
|
||||
uint64 guid;
|
||||
uint32 money;
|
||||
recvData >> guid >> money;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "CMSG_GUILD_BANK_WITHDRAW_MONEY [%s]: Go: [" UI64FMTD "], money: %u",
|
||||
// GetPlayerInfo().c_str(), guid, money);
|
||||
|
||||
if (money && GetPlayer()->GetGameObjectIfCanInteractWith(guid, GAMEOBJECT_TYPE_GUILD_BANK))
|
||||
if (Guild* guild = GetPlayer()->GetGuild())
|
||||
guild->HandleMemberWithdrawMoney(this, money);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildBankSwapItems(WorldPacket& recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "CMSG_GUILD_BANK_SWAP_ITEMS [%s]", GetPlayerInfo().c_str());
|
||||
|
||||
uint64 GoGuid;
|
||||
recvData >> GoGuid;
|
||||
|
||||
if (!GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK))
|
||||
{
|
||||
recvData.rfinish(); // Prevent additional spam at rejected packet
|
||||
return;
|
||||
}
|
||||
|
||||
Guild* guild = GetPlayer()->GetGuild();
|
||||
if (!guild)
|
||||
{
|
||||
recvData.rfinish(); // Prevent additional spam at rejected packet
|
||||
return;
|
||||
}
|
||||
|
||||
uint8 bankToBank;
|
||||
recvData >> bankToBank;
|
||||
|
||||
uint8 tabId;
|
||||
uint8 slotId;
|
||||
uint32 itemEntry;
|
||||
uint32 splitedAmount = 0;
|
||||
|
||||
if (bankToBank)
|
||||
{
|
||||
uint8 destTabId;
|
||||
recvData >> destTabId;
|
||||
|
||||
uint8 destSlotId;
|
||||
recvData >> destSlotId;
|
||||
recvData.read_skip<uint32>(); // Always 0
|
||||
|
||||
recvData >> tabId;
|
||||
recvData >> slotId;
|
||||
recvData >> itemEntry;
|
||||
recvData.read_skip<uint8>(); // Always 0
|
||||
|
||||
recvData >> splitedAmount;
|
||||
|
||||
guild->SwapItems(GetPlayer(), tabId, slotId, destTabId, destSlotId, splitedAmount);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8 playerBag = NULL_BAG;
|
||||
uint8 playerSlotId = NULL_SLOT;
|
||||
uint8 toChar = 1;
|
||||
|
||||
recvData >> tabId;
|
||||
recvData >> slotId;
|
||||
recvData >> itemEntry;
|
||||
|
||||
uint8 autoStore;
|
||||
recvData >> autoStore;
|
||||
if (autoStore)
|
||||
{
|
||||
recvData.read_skip<uint32>(); // autoStoreCount
|
||||
recvData.read_skip<uint8>(); // ToChar (?), always and expected to be 1 (autostore only triggered in Bank -> Char)
|
||||
recvData.read_skip<uint32>(); // Always 0
|
||||
}
|
||||
else
|
||||
{
|
||||
recvData >> playerBag;
|
||||
recvData >> playerSlotId;
|
||||
recvData >> toChar;
|
||||
recvData >> 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& recvData)
|
||||
{
|
||||
uint64 guid;
|
||||
uint8 tabId;
|
||||
|
||||
recvData >> guid >> tabId;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "CMSG_GUILD_BANK_BUY_TAB [%s]: Go: [" UI64FMTD "], TabId: %u", GetPlayerInfo().c_str(), guid, tabId);
|
||||
|
||||
|
||||
if (GetPlayer()->GetGameObjectIfCanInteractWith(guid, GAMEOBJECT_TYPE_GUILD_BANK))
|
||||
if (Guild* guild = GetPlayer()->GetGuild())
|
||||
guild->HandleBuyBankTab(this, tabId);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildBankUpdateTab(WorldPacket& recvData)
|
||||
{
|
||||
uint64 guid;
|
||||
uint8 tabId;
|
||||
std::string name, icon;
|
||||
|
||||
recvData >> guid >> tabId >> name >> icon;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "CMSG_GUILD_BANK_UPDATE_TAB [%s]: Go: [" UI64FMTD "], TabId: %u, Name: %s, Icon: %s"
|
||||
// , GetPlayerInfo().c_str(), guid, tabId, name.c_str(), icon.c_str());
|
||||
|
||||
if (!name.empty() && !icon.empty())
|
||||
if (GetPlayer()->GetGameObjectIfCanInteractWith(guid, GAMEOBJECT_TYPE_GUILD_BANK))
|
||||
if (Guild* guild = GetPlayer()->GetGuild())
|
||||
guild->HandleSetBankTabInfo(this, tabId, name, icon);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGuildBankLogQuery(WorldPacket& recvData)
|
||||
{
|
||||
uint8 tabId;
|
||||
recvData >> tabId;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "MSG_GUILD_BANK_LOG_QUERY [%s]: TabId: %u", GetPlayerInfo().c_str(), tabId);
|
||||
|
||||
if (Guild* guild = GetPlayer()->GetGuild())
|
||||
guild->SendBankLog(this, tabId);
|
||||
}
|
||||
|
||||
void WorldSession::HandleQueryGuildBankTabText(WorldPacket &recvData)
|
||||
{
|
||||
uint8 tabId;
|
||||
recvData >> tabId;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "MSG_QUERY_GUILD_BANK_TEXT [%s]: TabId: %u", GetPlayerInfo().c_str(), tabId);
|
||||
|
||||
if (Guild* guild = GetPlayer()->GetGuild())
|
||||
guild->SendBankTabText(this, tabId);
|
||||
}
|
||||
|
||||
void WorldSession::HandleSetGuildBankTabText(WorldPacket &recvData)
|
||||
{
|
||||
uint8 tabId;
|
||||
std::string text;
|
||||
recvData >> tabId >> text;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_GUILD, "CMSG_SET_GUILD_BANK_TEXT [%s]: TabId: %u, Text: %s", GetPlayerInfo().c_str(), tabId, text.c_str());
|
||||
|
||||
if (Guild* guild = GetPlayer()->GetGuild())
|
||||
guild->SetBankTabText(tabId, text);
|
||||
}
|
||||
1494
src/server/game/Handlers/ItemHandler.cpp
Normal file
1494
src/server/game/Handlers/ItemHandler.cpp
Normal file
File diff suppressed because it is too large
Load Diff
624
src/server/game/Handlers/LFGHandler.cpp
Normal file
624
src/server/game/Handlers/LFGHandler.cpp
Normal file
@@ -0,0 +1,624 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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 "LFGMgr.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Group.h"
|
||||
#include "Player.h"
|
||||
#include "Opcodes.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
|
||||
void BuildPlayerLockDungeonBlock(WorldPacket& data, lfg::LfgLockMap const& lock)
|
||||
{
|
||||
data << uint32(lock.size()); // Size of lock dungeons
|
||||
for (lfg::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 lfg::LfgLockPartyMap& lockMap)
|
||||
{
|
||||
data << uint8(lockMap.size());
|
||||
for (lfg::LfgLockPartyMap::const_iterator it = lockMap.begin(); it != lockMap.end(); ++it)
|
||||
{
|
||||
data << uint64(it->first); // Player guid
|
||||
BuildPlayerLockDungeonBlock(data, it->second);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleLfgJoinOpcode(WorldPacket& recvData)
|
||||
{
|
||||
if (!sLFGMgr->isOptionEnabled(lfg::LFG_OPTION_ENABLE_DUNGEON_FINDER | lfg::LFG_OPTION_ENABLE_RAID_BROWSER))
|
||||
{
|
||||
recvData.rfinish();
|
||||
return;
|
||||
}
|
||||
|
||||
// pussywizard:
|
||||
if (Group* g = GetPlayer()->GetGroup())
|
||||
if (g->isLFGGroup() ? g->GetMembersCount() == MAXGROUPSIZE : g->GetLeaderGUID() != GetPlayer()->GetGUID())
|
||||
{
|
||||
recvData.rfinish();
|
||||
return;
|
||||
}
|
||||
|
||||
uint8 numDungeons;
|
||||
uint32 roles;
|
||||
|
||||
recvData >> roles;
|
||||
recvData.read_skip<uint16>(); // uint8 (always 0) - uint8 (always 0)
|
||||
recvData >> numDungeons;
|
||||
if (!numDungeons)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_JOIN [" UI64FMTD "] no dungeons selected", GetPlayer()->GetGUID());
|
||||
recvData.rfinish();
|
||||
return;
|
||||
}
|
||||
|
||||
lfg::LfgDungeonSet newDungeons;
|
||||
for (int8 i = 0; i < numDungeons; ++i)
|
||||
{
|
||||
uint32 dungeon;
|
||||
recvData >> dungeon;
|
||||
dungeon &= 0x00FFFFFF; // remove the type from the dungeon entry
|
||||
if (dungeon)
|
||||
newDungeons.insert(dungeon);
|
||||
}
|
||||
|
||||
recvData.read_skip<uint32>(); // for 0..uint8 (always 3) { uint8 (always 0) }
|
||||
|
||||
std::string comment;
|
||||
recvData >> 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->JoinLfg(GetPlayer(), uint8(roles), newDungeons, comment);
|
||||
}
|
||||
|
||||
void WorldSession::HandleLfgLeaveOpcode(WorldPacket& /*recvData*/)
|
||||
{
|
||||
Group* group = GetPlayer()->GetGroup();
|
||||
uint64 guid = GetPlayer()->GetGUID();
|
||||
uint64 gguid = group ? group->GetGUID() : guid;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_LEAVE [" UI64FMTD "] in group: %u", guid, grp ? 1 : 0);
|
||||
|
||||
// Check cheating - only leader can leave the queue
|
||||
if (!group || group->GetLeaderGUID() == guid)
|
||||
{
|
||||
sLFGMgr->LeaveLfg(sLFGMgr->GetState(guid) == lfg::LFG_STATE_RAIDBROWSER ? guid : gguid);
|
||||
sLFGMgr->LeaveAllLfgQueues(guid, true, group ? group->GetGUID() : 0);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleLfgProposalResultOpcode(WorldPacket& recvData)
|
||||
{
|
||||
uint32 proposalID; // Internal lfgGroupID
|
||||
bool accept; // Accept to join?
|
||||
recvData >> proposalID;
|
||||
recvData >> accept;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_PROPOSAL_RESULT [" UI64FMTD "] proposal: %u accept: %u", GetPlayer()->GetGUID(), lfgGroupID, accept ? 1 : 0);
|
||||
sLFGMgr->UpdateProposal(proposalID, GetPlayer()->GetGUID(), accept);
|
||||
}
|
||||
|
||||
void WorldSession::HandleLfgSetRolesOpcode(WorldPacket& recvData)
|
||||
{
|
||||
uint8 roles;
|
||||
recvData >> roles; // Player Group Roles
|
||||
uint64 guid = GetPlayer()->GetGUID();
|
||||
Group* group = GetPlayer()->GetGroup();
|
||||
if (!group)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_SET_ROLES [" UI64FMTD "] Not in group", guid);
|
||||
return;
|
||||
}
|
||||
uint64 gguid = group->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& recvData)
|
||||
{
|
||||
std::string comment;
|
||||
recvData >> comment;
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_SET_COMMENT [" UI64FMTD "] comment: %s", guid, comment.c_str());
|
||||
|
||||
sLFGMgr->SetComment(GetPlayer()->GetGUID(), comment);
|
||||
sLFGMgr->LfrSetComment(GetPlayer(), comment);
|
||||
}
|
||||
|
||||
void WorldSession::HandleLfgSetBootVoteOpcode(WorldPacket& recvData)
|
||||
{
|
||||
bool agree; // Agree to kick player
|
||||
recvData >> agree;
|
||||
|
||||
uint64 guid = GetPlayer()->GetGUID();
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_SET_BOOT_VOTE [" UI64FMTD "] agree: %u", guid, agree ? 1 : 0);
|
||||
sLFGMgr->UpdateBoot(guid, agree);
|
||||
}
|
||||
|
||||
void WorldSession::HandleLfgTeleportOpcode(WorldPacket& recvData)
|
||||
{
|
||||
bool out;
|
||||
recvData >> 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& /*recvData*/)
|
||||
{
|
||||
uint64 guid = GetPlayer()->GetGUID();
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_PLAYER_LOCK_INFO_REQUEST [" UI64FMTD "]", guid);
|
||||
|
||||
// Get Random dungeons that can be done at a certain level and expansion
|
||||
uint8 level = GetPlayer()->getLevel();
|
||||
lfg::LfgDungeonSet const& randomDungeons =
|
||||
sLFGMgr->GetRandomAndSeasonalDungeons(level, GetPlayer()->GetSession()->Expansion());
|
||||
|
||||
// Get player locked Dungeons
|
||||
sLFGMgr->InitializeLockedDungeons(GetPlayer()); // pussywizard
|
||||
lfg::LfgLockMap const& 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 (lfg::LfgDungeonSet::const_iterator it = randomDungeons.begin(); it != randomDungeons.end(); ++it)
|
||||
{
|
||||
data << uint32(*it); // Dungeon Entry (id + type)
|
||||
lfg::LfgReward const* reward = sLFGMgr->GetRandomDungeonReward(*it, level);
|
||||
Quest const* quest = NULL;
|
||||
bool done = false;
|
||||
if (reward)
|
||||
{
|
||||
quest = sObjectMgr->GetQuestTemplate(reward->firstQuest);
|
||||
if (quest)
|
||||
{
|
||||
done = !GetPlayer()->CanRewardQuest(quest, false);
|
||||
if (done)
|
||||
quest = sObjectMgr->GetQuestTemplate(reward->otherQuest);
|
||||
}
|
||||
}
|
||||
|
||||
if (quest)
|
||||
{
|
||||
data << uint8(done);
|
||||
data << uint32(quest->GetRewOrReqMoney());
|
||||
data << uint32(quest->XPValue(GetPlayer()));
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint8(quest->GetRewItemsCount());
|
||||
if (quest->GetRewItemsCount())
|
||||
{
|
||||
for (uint8 i = 0; i < QUEST_REWARDS_COUNT; ++i)
|
||||
if (uint32 itemId = quest->RewardItemId[i])
|
||||
{
|
||||
ItemTemplate const* item = sObjectMgr->GetItemTemplate(itemId);
|
||||
data << uint32(itemId);
|
||||
data << uint32(item ? item->DisplayInfoID : 0);
|
||||
data << uint32(quest->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& /*recvData*/)
|
||||
{
|
||||
uint64 guid = GetPlayer()->GetGUID();
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_PARTY_LOCK_INFO_REQUEST [" UI64FMTD "]", guid);
|
||||
|
||||
Group* group = GetPlayer()->GetGroup();
|
||||
if (!group)
|
||||
return;
|
||||
|
||||
// Get the locked dungeons of the other party members
|
||||
lfg::LfgLockPartyMap lockMap;
|
||||
for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
|
||||
{
|
||||
Player* plrg = itr->GetSource();
|
||||
if (!plrg)
|
||||
continue;
|
||||
|
||||
uint64 pguid = plrg->GetGUID();
|
||||
if (pguid == guid)
|
||||
continue;
|
||||
|
||||
sLFGMgr->InitializeLockedDungeons(plrg); // pussywizard
|
||||
lockMap[pguid] = sLFGMgr->GetLockedDungeons(pguid);
|
||||
}
|
||||
|
||||
uint32 size = 0;
|
||||
for (lfg::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::HandleLfrSearchJoinOpcode(WorldPacket& recvData)
|
||||
{
|
||||
uint32 dungeonId;
|
||||
recvData >> dungeonId;
|
||||
dungeonId = (dungeonId & 0x00FFFFFF); // remove the type from the dungeon entry
|
||||
sLFGMgr->LfrSearchAdd(GetPlayer(), dungeonId);
|
||||
sLFGMgr->SendRaidBrowserCachedList(GetPlayer(), dungeonId);
|
||||
}
|
||||
|
||||
void WorldSession::HandleLfrSearchLeaveOpcode(WorldPacket& recvData)
|
||||
{
|
||||
uint32 dungeonId;
|
||||
recvData >> dungeonId;
|
||||
sLFGMgr->LfrSearchRemove(GetPlayer());
|
||||
}
|
||||
|
||||
void WorldSession::HandleLfgGetStatus(WorldPacket& /*recvData*/)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_LFG, "CMSG_LFG_GET_STATUS %s", GetPlayerInfo().c_str());
|
||||
|
||||
uint64 guid = GetPlayer()->GetGUID();
|
||||
lfg::LfgUpdateData updateData = sLFGMgr->GetLfgStatus(guid);
|
||||
|
||||
if (GetPlayer()->GetGroup())
|
||||
{
|
||||
SendLfgUpdateParty(updateData);
|
||||
updateData.dungeons.clear();
|
||||
SendLfgUpdatePlayer(updateData);
|
||||
}
|
||||
else
|
||||
{
|
||||
SendLfgUpdatePlayer(updateData);
|
||||
updateData.dungeons.clear();
|
||||
SendLfgUpdateParty(updateData);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::SendLfgUpdatePlayer(lfg::LfgUpdateData const& updateData)
|
||||
{
|
||||
bool queued = false;
|
||||
uint8 size = uint8(updateData.dungeons.size());
|
||||
|
||||
switch (updateData.updateType)
|
||||
{
|
||||
case lfg::LFG_UPDATETYPE_JOIN_QUEUE:
|
||||
case lfg::LFG_UPDATETYPE_ADDED_TO_QUEUE:
|
||||
queued = true;
|
||||
break;
|
||||
case lfg::LFG_UPDATETYPE_UPDATE_STATUS:
|
||||
queued = updateData.state == lfg::LFG_STATE_QUEUED;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_LFG, "SMSG_LFG_UPDATE_PLAYER %s updatetype: %u",
|
||||
// GetPlayerInfo().c_str(), updateData.updateType);
|
||||
WorldPacket data(SMSG_LFG_UPDATE_PLAYER, 1 + 1 + (size > 0 ? 1 : 0) * (1 + 1 + 1 + 1 + size * 4 + updateData.comment.length()));
|
||||
data << uint8(updateData.updateType); // Lfg Update type
|
||||
data << uint8(size > 0); // Extra info
|
||||
if (size)
|
||||
{
|
||||
data << uint8(queued); // Join the queue
|
||||
data << uint8(0); // unk - Always 0
|
||||
data << uint8(0); // unk - Always 0
|
||||
|
||||
data << uint8(size);
|
||||
for (lfg::LfgDungeonSet::const_iterator it = updateData.dungeons.begin(); it != updateData.dungeons.end(); ++it)
|
||||
data << uint32(*it);
|
||||
data << updateData.comment;
|
||||
}
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::SendLfgUpdateParty(lfg::LfgUpdateData const& updateData)
|
||||
{
|
||||
bool join = false;
|
||||
bool queued = false;
|
||||
uint8 size = uint8(updateData.dungeons.size());
|
||||
|
||||
switch (updateData.updateType)
|
||||
{
|
||||
case lfg::LFG_UPDATETYPE_ADDED_TO_QUEUE: // Rolecheck Success
|
||||
queued = true;
|
||||
// no break on purpose
|
||||
case lfg::LFG_UPDATETYPE_PROPOSAL_BEGIN:
|
||||
join = true;
|
||||
break;
|
||||
case lfg::LFG_UPDATETYPE_UPDATE_STATUS:
|
||||
join = updateData.state != lfg::LFG_STATE_ROLECHECK && updateData.state != lfg::LFG_STATE_NONE;
|
||||
queued = updateData.state == lfg::LFG_STATE_QUEUED;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_LFG, "SMSG_LFG_UPDATE_PARTY %s updatetype: %u",
|
||||
// GetPlayerInfo().c_str(), updateData.updateType);
|
||||
WorldPacket data(SMSG_LFG_UPDATE_PARTY, 1 + 1 + (size > 0 ? 1 : 0) * (1 + 1 + 1 + 1 + 1 + size * 4 + updateData.comment.length()));
|
||||
data << uint8(updateData.updateType); // Lfg Update type
|
||||
data << uint8(size > 0); // Extra info
|
||||
if (size)
|
||||
{
|
||||
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);
|
||||
for (lfg::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(lfg::LfgRoleCheck const& roleCheck)
|
||||
{
|
||||
lfg::LfgDungeonSet dungeons;
|
||||
if (roleCheck.rDungeonId)
|
||||
dungeons.insert(roleCheck.rDungeonId);
|
||||
else
|
||||
dungeons = roleCheck.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 + roleCheck.roles.size() * (8 + 1 + 4 + 1));
|
||||
|
||||
data << uint32(roleCheck.state); // Check result
|
||||
data << uint8(roleCheck.state == lfg::LFG_ROLECHECK_INITIALITING);
|
||||
data << uint8(dungeons.size()); // Number of dungeons
|
||||
if (!dungeons.empty())
|
||||
for (lfg::LfgDungeonSet::iterator it = dungeons.begin(); it != dungeons.end(); ++it)
|
||||
data << uint32(sLFGMgr->GetLFGDungeonEntry(*it)); // Dungeon
|
||||
|
||||
data << uint8(roleCheck.roles.size()); // Players in group
|
||||
if (!roleCheck.roles.empty())
|
||||
{
|
||||
// Leader info MUST be sent 1st :S
|
||||
uint64 guid = roleCheck.leader;
|
||||
uint8 roles = roleCheck.roles.find(guid)->second;
|
||||
data << uint64(guid); // Guid
|
||||
data << uint8(roles > 0); // Ready
|
||||
data << uint32(roles); // Roles
|
||||
Player* player = ObjectAccessor::FindPlayerInOrOutOfWorld(guid);
|
||||
data << uint8(player ? player->getLevel() : 0); // Level
|
||||
|
||||
for (lfg::LfgRolesMap::const_iterator it = roleCheck.roles.begin(); it != roleCheck.roles.end(); ++it)
|
||||
{
|
||||
if (it->first == roleCheck.leader)
|
||||
continue;
|
||||
|
||||
guid = it->first;
|
||||
roles = it->second;
|
||||
data << uint64(guid); // Guid
|
||||
data << uint8(roles > 0); // Ready
|
||||
data << uint32(roles); // Roles
|
||||
player = ObjectAccessor::FindPlayerInOrOutOfWorld(guid);
|
||||
data << uint8(player ? player->getLevel() : 0);// Level
|
||||
}
|
||||
}
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::SendLfgJoinResult(lfg::LfgJoinResultData const& joinData)
|
||||
{
|
||||
uint32 size = 0;
|
||||
for (lfg::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(lfg::LfgQueueStatusData const& queueData)
|
||||
{
|
||||
;//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(), queueData.dungeonId, queueData.waitTime, queueData.waitTimeAvg, queueData.waitTimeTank, queueData.waitTimeHealer, queueData.waitTimeDps, queueData.queuedTime, queueData.tanks, queueData.healers, queueData.dps);
|
||||
|
||||
WorldPacket data(SMSG_LFG_QUEUE_STATUS, 4 + 4 + 4 + 4 + 4 +4 + 1 + 1 + 1 + 4);
|
||||
data << uint32(queueData.dungeonId); // Dungeon
|
||||
data << int32(queueData.waitTimeAvg); // Average Wait time
|
||||
data << int32(queueData.waitTime); // Wait Time
|
||||
data << int32(queueData.waitTimeTank); // Wait Tanks
|
||||
data << int32(queueData.waitTimeHealer); // Wait Healers
|
||||
data << int32(queueData.waitTimeDps); // Wait Dps
|
||||
data << uint8(queueData.tanks); // Tanks needed
|
||||
data << uint8(queueData.healers); // Healers needed
|
||||
data << uint8(queueData.dps); // Dps needed
|
||||
data << uint32(queueData.queuedTime); // Player wait time in queue
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::SendLfgPlayerReward(lfg::LfgPlayerRewardData const& rewardData)
|
||||
{
|
||||
if (!rewardData.rdungeonEntry || !rewardData.sdungeonEntry || !rewardData.quest)
|
||||
return;
|
||||
|
||||
sLog->outDebug(LOG_FILTER_LFG, "SMSG_LFG_PLAYER_REWARD %s rdungeonEntry: %u, sdungeonEntry: %u, done: %u",
|
||||
GetPlayerInfo().c_str(), rewardData.rdungeonEntry, rewardData.sdungeonEntry, rewardData.done);
|
||||
|
||||
uint8 itemNum = rewardData.quest->GetRewItemsCount();
|
||||
|
||||
WorldPacket data(SMSG_LFG_PLAYER_REWARD, 4 + 4 + 1 + 4 + 4 + 4 + 4 + 4 + 1 + itemNum * (4 + 4 + 4));
|
||||
data << uint32(rewardData.rdungeonEntry); // Random Dungeon Finished
|
||||
data << uint32(rewardData.sdungeonEntry); // Dungeon Finished
|
||||
data << uint8(rewardData.done);
|
||||
data << uint32(1);
|
||||
data << uint32(rewardData.quest->GetRewOrReqMoney());
|
||||
data << uint32(rewardData.quest->XPValue(GetPlayer()));
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
data << uint8(itemNum);
|
||||
if (itemNum)
|
||||
{
|
||||
for (uint8 i = 0; i < QUEST_REWARDS_COUNT; ++i)
|
||||
if (uint32 itemId = rewardData.quest->RewardItemId[i])
|
||||
{
|
||||
ItemTemplate const* item = sObjectMgr->GetItemTemplate(itemId);
|
||||
data << uint32(itemId);
|
||||
data << uint32(item ? item->DisplayInfoID : 0);
|
||||
data << uint32(rewardData.quest->RewardItemIdCount[i]);
|
||||
}
|
||||
}
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::SendLfgBootProposalUpdate(lfg::LfgPlayerBoot const& boot)
|
||||
{
|
||||
uint64 guid = GetPlayer()->GetGUID();
|
||||
lfg::LfgAnswer playerVote = boot.votes.find(guid)->second;
|
||||
uint8 votesNum = 0;
|
||||
uint8 agreeNum = 0;
|
||||
uint32 secsleft = boot.cancelTime - time(NULL);
|
||||
for (lfg::LfgAnswerContainer::const_iterator it = boot.votes.begin(); it != boot.votes.end(); ++it)
|
||||
{
|
||||
if (it->second != lfg::LFG_ANSWER_PENDING)
|
||||
{
|
||||
++votesNum;
|
||||
if (it->second == lfg::LFG_ANSWER_AGREE)
|
||||
++agreeNum;
|
||||
}
|
||||
}
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_BOOT_PROPOSAL_UPDATE [" UI64FMTD "] inProgress: %u - didVote: %u - agree: %u - victim: [" UI64FMTD "] votes: %u - agrees: %u - left: %u - needed: %u - reason %s",
|
||||
// guid, uint8(boot.inProgress), uint8(playerVote != lfg::LFG_ANSWER_PENDING), uint8(playerVote == lfg::LFG_ANSWER_AGREE), boot.victim, votesNum, agreeNum, secsleft, lfg::LFG_GROUP_KICK_VOTES_NEEDED, boot.reason.c_str());
|
||||
WorldPacket data(SMSG_LFG_BOOT_PROPOSAL_UPDATE, 1 + 1 + 1 + 8 + 4 + 4 + 4 + 4 + boot.reason.length());
|
||||
data << uint8(boot.inProgress); // Vote in progress
|
||||
data << uint8(playerVote != lfg::LFG_ANSWER_PENDING); // Did Vote
|
||||
data << uint8(playerVote == lfg::LFG_ANSWER_AGREE); // Agree
|
||||
data << uint64(boot.victim); // Victim GUID
|
||||
data << uint32(votesNum); // Total Votes
|
||||
data << uint32(agreeNum); // Agree Count
|
||||
data << uint32(secsleft); // Time Left
|
||||
data << uint32(lfg::LFG_GROUP_KICK_VOTES_NEEDED); // Needed Votes
|
||||
data << boot.reason.c_str(); // Kick reason
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::SendLfgUpdateProposal(lfg::LfgProposal const& proposal)
|
||||
{
|
||||
uint64 guid = GetPlayer()->GetGUID();
|
||||
uint64 gguid = proposal.players.find(guid)->second.group;
|
||||
bool silent = !proposal.isNew && gguid == proposal.group;
|
||||
uint32 dungeonEntry = proposal.dungeonId;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_PROPOSAL_UPDATE [" UI64FMTD "] state: %u", guid, proposal.state);
|
||||
|
||||
// show random dungeon if player selected random dungeon and it's not lfg group
|
||||
if (!silent)
|
||||
{
|
||||
lfg::LfgDungeonSet const& playerDungeons = sLFGMgr->GetSelectedDungeons(guid);
|
||||
if (playerDungeons.find(proposal.dungeonId) == playerDungeons.end())
|
||||
dungeonEntry = (*playerDungeons.begin());
|
||||
}
|
||||
|
||||
dungeonEntry = sLFGMgr->GetLFGDungeonEntry(dungeonEntry);
|
||||
|
||||
WorldPacket data(SMSG_LFG_PROPOSAL_UPDATE, 4 + 1 + 4 + 4 + 1 + 1 + proposal.players.size() * (4 + 1 + 1 + 1 + 1 +1));
|
||||
data << uint32(dungeonEntry); // Dungeon
|
||||
data << uint8(proposal.state); // Proposal state
|
||||
data << uint32(proposal.id); // Proposal ID
|
||||
data << uint32(proposal.encounters); // encounters done
|
||||
data << uint8(silent); // Show proposal window
|
||||
data << uint8(proposal.players.size()); // Group size
|
||||
|
||||
for (lfg::LfgProposalPlayerContainer::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it)
|
||||
{
|
||||
lfg::LfgProposalPlayer const& player = it->second;
|
||||
data << uint32(player.role); // Role
|
||||
data << uint8(it->first == guid); // Self player
|
||||
if (!player.group) // Player not it a group
|
||||
{
|
||||
data << uint8(0); // Not in dungeon
|
||||
data << uint8(0); // Not same group
|
||||
}
|
||||
else
|
||||
{
|
||||
data << uint8(player.group == proposal.group); // In dungeon (silent)
|
||||
data << uint8(player.group == gguid); // Same Group than player
|
||||
}
|
||||
data << uint8(player.accept != lfg::LFG_ANSWER_PENDING);// Answered
|
||||
data << uint8(player.accept == lfg::LFG_ANSWER_AGREE); // Accepted
|
||||
}
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::SendLfgLfrList(bool update)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_LFR_LIST [" 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);
|
||||
}
|
||||
499
src/server/game/Handlers/LootHandler.cpp
Normal file
499
src/server/game/Handlers/LootHandler.cpp
Normal file
@@ -0,0 +1,499 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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 "Corpse.h"
|
||||
#include "Creature.h"
|
||||
#include "GameObject.h"
|
||||
#include "Group.h"
|
||||
#include "LootItemStorage.h"
|
||||
#include "LootMgr.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "Object.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Player.h"
|
||||
#include "World.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
|
||||
void WorldSession::HandleAutostoreLootItemOpcode(WorldPacket& recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_AUTOSTORE_LOOT_ITEM");
|
||||
Player* player = GetPlayer();
|
||||
uint64 lguid = player->GetLootGUID();
|
||||
Loot* loot = NULL;
|
||||
uint8 lootSlot = 0;
|
||||
|
||||
recvData >> lootSlot;
|
||||
|
||||
if (IS_GAMEOBJECT_GUID(lguid))
|
||||
{
|
||||
GameObject* go = player->GetMap()->GetGameObject(lguid);
|
||||
// xinef: cheating protection
|
||||
//if (player->GetGroup() && player->GetGroup()->GetLootMethod() == MASTER_LOOT && player->GetGUID() != player->GetGroup()->GetMasterLooterGuid())
|
||||
// go = NULL;
|
||||
|
||||
// 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 lootAllowed = creature && creature->IsAlive() == (player->getClass() == CLASS_ROGUE && creature->loot.loot_type == LOOT_PICKPOCKETING);
|
||||
if (!lootAllowed || !creature->IsWithinDistInMap(_player, INTERACTION_DISTANCE))
|
||||
{
|
||||
player->SendLootError(lguid, lootAllowed ? LOOT_ERROR_TOO_FAR : LOOT_ERROR_DIDNT_KILL);
|
||||
return;
|
||||
}
|
||||
|
||||
loot = &creature->loot;
|
||||
}
|
||||
|
||||
player->StoreLootItem(lootSlot, loot);
|
||||
|
||||
// If player is removing the last LootItem, delete the empty container.
|
||||
if (loot->isLooted() && IS_ITEM_GUID(lguid))
|
||||
DoLootRelease(lguid);
|
||||
}
|
||||
|
||||
void WorldSession::HandleLootMoneyOpcode(WorldPacket & /*recvData*/)
|
||||
{
|
||||
;//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->loot.loot_type == LOOT_PICKPOCKETING);
|
||||
if (lootAllowed && creature->IsWithinDistInMap(player, INTERACTION_DISTANCE))
|
||||
{
|
||||
loot = &creature->loot;
|
||||
if (creature->IsAlive())
|
||||
shareMoney = false;
|
||||
}
|
||||
else
|
||||
player->SendLootError(guid, lootAllowed ? LOOT_ERROR_TOO_FAR : LOOT_ERROR_DIDNT_KILL);
|
||||
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->IsAtGroupRewardDistance(member))
|
||||
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)->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->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;
|
||||
|
||||
// Delete the money loot record from the DB
|
||||
if (loot->containerId > 0)
|
||||
sLootItemStorage->RemoveStoredLootMoney(loot->containerId);
|
||||
|
||||
// Delete container if empty
|
||||
if (loot->isLooted() && IS_ITEM_GUID(guid))
|
||||
DoLootRelease(guid);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleLootOpcode(WorldPacket& recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_LOOT");
|
||||
|
||||
uint64 guid;
|
||||
recvData >> guid;
|
||||
|
||||
// Check possible cheat
|
||||
if (!GetPlayer()->IsAlive() || !IS_CRE_OR_VEH_GUID(guid))
|
||||
return;
|
||||
|
||||
GetPlayer()->SendLoot(guid, LOOT_CORPSE);
|
||||
|
||||
// interrupt cast
|
||||
if (GetPlayer()->IsNonMeleeSpellCast(false))
|
||||
GetPlayer()->InterruptNonMeleeSpells(false);
|
||||
}
|
||||
|
||||
void WorldSession::HandleLootReleaseOpcode(WorldPacket& recvData)
|
||||
{
|
||||
;//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
|
||||
uint64 guid;
|
||||
recvData >> guid;
|
||||
|
||||
if (uint64 lguid = GetPlayer()->GetLootGUID())
|
||||
if (lguid == guid)
|
||||
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)
|
||||
{
|
||||
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() >= go->GetGOValue()->FishingHole.MaxOpens)
|
||||
go->SetLootState(GO_JUST_DEACTIVATED);
|
||||
else
|
||||
go->SetLootState(GO_READY);
|
||||
}
|
||||
else
|
||||
{
|
||||
go->SetLootState(GO_JUST_DEACTIVATED);
|
||||
|
||||
// Xinef: moved event execution to loot release (after everything is looted)
|
||||
// Xinef: 99% sure that this worked like this on blizz
|
||||
// Xinef: prevents exploits with just opening GO and spawning bilions of npcs, which can crash core if you know what you're doin ;)
|
||||
if (go->GetGoType() == GAMEOBJECT_TYPE_CHEST && go->GetGOInfo()->chest.eventId)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "Chest ScriptStart id %u for GO %u", gameObjTarget->GetGOInfo()->chest.eventId, gameObjTarget->GetDBTableGUIDLow());
|
||||
player->GetMap()->ScriptsStart(sEventScripts, go->GetGOInfo()->chest.eventId, player, go);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
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;
|
||||
|
||||
// Xinef: Buggs client? (Opening loot after closing)
|
||||
//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;
|
||||
|
||||
loot = &pItem->loot;
|
||||
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 if (pItem->loot.isLooted() || !(proto->Flags & ITEM_PROTO_FLAG_OPENABLE))
|
||||
{
|
||||
player->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Creature* creature = GetPlayer()->GetMap()->GetCreature(lguid);
|
||||
|
||||
bool lootAllowed = creature && creature->IsAlive() == (player->getClass() == CLASS_ROGUE && creature->loot.loot_type == LOOT_PICKPOCKETING);
|
||||
if (!lootAllowed || !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)
|
||||
{
|
||||
loot->roundRobinPlayer = 0;
|
||||
|
||||
if (Group* group = player->GetGroup())
|
||||
{
|
||||
group->SendLooter(creature, NULL);
|
||||
|
||||
// force update of dynamic flags, otherwise other group's players still not able to loot.
|
||||
creature->ForceValuesUpdateAtIndex(UNIT_DYNAMIC_FLAGS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//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& recvData)
|
||||
{
|
||||
uint8 slotid;
|
||||
uint64 lootguid, target_playerguid;
|
||||
|
||||
recvData >> lootguid >> slotid >> target_playerguid;
|
||||
|
||||
if (!_player->GetGroup() || _player->GetGroup()->GetMasterLooterGuid() != _player->GetGUID() || _player->GetGroup()->GetLootMethod() != MASTER_LOOT)
|
||||
{
|
||||
_player->SendLootError(lootguid, LOOT_ERROR_DIDNT_KILL);
|
||||
return;
|
||||
}
|
||||
|
||||
Player* target = ObjectAccessor::GetPlayer(*_player, MAKE_NEW_GUID(target_playerguid, 0, HIGHGUID_PLAYER));
|
||||
if (!target)
|
||||
{
|
||||
_player->SendLootError(lootguid, LOOT_ERROR_PLAYER_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WorldSession::HandleLootMasterGiveOpcode (CMSG_LOOT_MASTER_GIVE, 0x02A3) Target = [%s].", target->GetName().c_str());
|
||||
|
||||
if (_player->GetLootGUID() != lootguid)
|
||||
{
|
||||
_player->SendLootError(lootguid, LOOT_ERROR_DIDNT_KILL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_player->IsInRaidWith(target))
|
||||
{
|
||||
_player->SendLootError(lootguid, LOOT_ERROR_MASTER_OTHER);
|
||||
//sLog->outDebug(LOG_FILTER_NETWORKIO, "MasterLootItem: Player %s tried to give an item to ineligible player %s !", GetPlayer()->GetName().c_str(), target->GetName().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
Loot* loot = NULL;
|
||||
|
||||
if (IS_CRE_OR_VEH_GUID(GetPlayer()->GetLootGUID()))
|
||||
{
|
||||
Creature* creature = GetPlayer()->GetMap()->GetCreature(lootguid);
|
||||
if (!creature)
|
||||
return;
|
||||
|
||||
loot = &creature->loot;
|
||||
}
|
||||
else if (IS_GAMEOBJECT_GUID(GetPlayer()->GetLootGUID()))
|
||||
{
|
||||
GameObject* pGO = GetPlayer()->GetMap()->GetGameObject(lootguid);
|
||||
if (!pGO)
|
||||
return;
|
||||
|
||||
loot = &pGO->loot;
|
||||
}
|
||||
|
||||
if (!loot)
|
||||
return;
|
||||
|
||||
if (slotid >= loot->items.size() + loot->quest_items.size())
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_LOOT, "MasterLootItem: Player %s might be using a hack! (slot %d, size %lu)", GetPlayer()->GetName().c_str(), slotid, (unsigned long)loot->items.size());
|
||||
return;
|
||||
}
|
||||
|
||||
LootItem& item = slotid >= loot->items.size() ? loot->quest_items[slotid - loot->items.size()] : loot->items[slotid];
|
||||
|
||||
ItemPosCountVec dest;
|
||||
InventoryResult msg = target->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, item.itemid, item.count);
|
||||
if (item.follow_loot_rules && !item.AllowedForPlayer(target))
|
||||
msg = EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
|
||||
if (msg != EQUIP_ERR_OK)
|
||||
{
|
||||
if (msg == EQUIP_ERR_CANT_CARRY_MORE_OF_THIS)
|
||||
_player->SendLootError(lootguid, LOOT_ERROR_MASTER_UNIQUE_ITEM);
|
||||
else if (msg == EQUIP_ERR_INVENTORY_FULL)
|
||||
_player->SendLootError(lootguid, LOOT_ERROR_MASTER_INV_FULL);
|
||||
else
|
||||
_player->SendLootError(lootguid, LOOT_ERROR_MASTER_OTHER);
|
||||
|
||||
target->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->UpdateLootAchievements(&item, loot);
|
||||
|
||||
// mark as looted
|
||||
item.count=0;
|
||||
item.is_looted=true;
|
||||
|
||||
loot->NotifyItemRemoved(slotid);
|
||||
--loot->unlootedCount;
|
||||
}
|
||||
804
src/server/game/Handlers/MailHandler.cpp
Normal file
804
src/server/game/Handlers/MailHandler.cpp
Normal file
@@ -0,0 +1,804 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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"
|
||||
|
||||
bool WorldSession::CanOpenMailBox(uint64 guid)
|
||||
{
|
||||
if (guid == _player->GetGUID())
|
||||
{
|
||||
sLog->outError("%s attempt open mailbox in cheating way.", _player->GetName().c_str());
|
||||
return false;
|
||||
}
|
||||
else if (IS_GAMEOBJECT_GUID(guid))
|
||||
{
|
||||
if (!_player->GetGameObjectIfCanInteractWith(guid, GAMEOBJECT_TYPE_MAILBOX))
|
||||
return false;
|
||||
}
|
||||
else if (IS_CRE_OR_VEH_OR_PET_GUID(guid))
|
||||
{
|
||||
if (!_player->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_MAILBOX))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WorldSession::HandleSendMail(WorldPacket & recvData)
|
||||
{
|
||||
uint64 mailbox, unk3;
|
||||
std::string receiver, subject, body;
|
||||
uint32 unk1, unk2, money, COD;
|
||||
uint8 unk4;
|
||||
recvData >> mailbox;
|
||||
recvData >> receiver;
|
||||
|
||||
recvData >> subject;
|
||||
|
||||
recvData >> body;
|
||||
|
||||
recvData >> unk1; // stationery?
|
||||
recvData >> unk2; // 0x00000000
|
||||
|
||||
uint8 items_count;
|
||||
recvData >> items_count; // attached items count
|
||||
|
||||
if (items_count > MAX_MAIL_ITEMS) // client limit
|
||||
{
|
||||
GetPlayer()->SendMailResult(0, MAIL_SEND, MAIL_ERR_TOO_MANY_ATTACHMENTS);
|
||||
recvData.rfinish(); // set to end to avoid warnings spam
|
||||
return;
|
||||
}
|
||||
|
||||
uint64 itemGUIDs[MAX_MAIL_ITEMS];
|
||||
|
||||
for (uint8 i = 0; i < items_count; ++i)
|
||||
{
|
||||
recvData.read_skip<uint8>(); // item slot in mail, not used
|
||||
recvData >> itemGUIDs[i];
|
||||
}
|
||||
|
||||
recvData >> money >> COD; // money and cod
|
||||
recvData >> unk3; // const 0
|
||||
recvData >> unk4; // const 0
|
||||
|
||||
// packet read complete, now do check
|
||||
|
||||
if (!CanOpenMailBox(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;
|
||||
|
||||
// Check for overflow
|
||||
if (reqmoney < money)
|
||||
{
|
||||
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_ENOUGH_MONEY);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!player->HasEnoughMoney(reqmoney))
|
||||
{
|
||||
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_ENOUGH_MONEY);
|
||||
return;
|
||||
}
|
||||
|
||||
Player* receive = ObjectAccessor::FindPlayerInOrOutOfWorld(rc);
|
||||
|
||||
uint32 rc_teamId = TEAM_NEUTRAL;
|
||||
uint16 mails_count = 0; //do not allow to send to one player more than 100 mails
|
||||
|
||||
if (receive)
|
||||
{
|
||||
rc_teamId = receive->GetTeamId();
|
||||
mails_count = receive->GetMailSize();
|
||||
}
|
||||
else
|
||||
{
|
||||
// xinef: get data from global storage
|
||||
if (GlobalPlayerData const* playerData = sWorld->GetGlobalPlayerData(GUID_LOPART(rc)))
|
||||
{
|
||||
rc_teamId = Player::TeamIdForRace(playerData->race);
|
||||
mails_count = playerData->mailCount;
|
||||
}
|
||||
}
|
||||
//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
|
||||
// Xinef: check for boa items, not used currently
|
||||
/*bool accountBound = items_count && !money && !COD ? 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;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
uint32 rc_account = receive
|
||||
? receive->GetSession()->GetAccountId()
|
||||
: sObjectMgr->GetPlayerAccountIdByGUID(rc);
|
||||
|
||||
if (/*!accountBound*/ GetAccountId() != rc_account && player->GetTeamId() != rc_teamId && AccountMgr::IsPlayerAccount(GetSecurity()))
|
||||
{
|
||||
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_YOUR_TEAM);
|
||||
return;
|
||||
}
|
||||
|
||||
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() && 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->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];
|
||||
|
||||
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
|
||||
if (item->GetState() == ITEM_UNCHANGED)
|
||||
item->FSetState(ITEM_CHANGED); // pussywizard: so the item will be saved and owner will be updated in database
|
||||
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 = GetAccountId() != rc_account;
|
||||
}
|
||||
|
||||
if( money >= 10*GOLD )
|
||||
{
|
||||
CleanStringForMysqlQuery(subject);
|
||||
CharacterDatabase.PExecute("INSERT INTO log_money VALUES(%u, %u, \"%s\", \"%s\", %u, \"%s\", %u, \"<MAIL> %s\", NOW())", GetAccountId(), player->GetGUIDLow(), player->GetName().c_str(), player->GetSession()->GetRemoteAddress().c_str(), rc_account, receiver.c_str(), money, subject.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// don't ask for COD if there are no items
|
||||
if (items_count == 0)
|
||||
COD = 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 & recvData)
|
||||
{
|
||||
uint64 mailbox;
|
||||
uint32 mailId;
|
||||
recvData >> mailbox;
|
||||
recvData >> mailId;
|
||||
|
||||
if (!CanOpenMailBox(mailbox))
|
||||
return;
|
||||
|
||||
Player* player = _player;
|
||||
Mail* m = player->GetMail(mailId);
|
||||
if (m && m->state != MAIL_STATE_DELETED)
|
||||
{
|
||||
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 & recvData)
|
||||
{
|
||||
uint64 mailbox;
|
||||
uint32 mailId;
|
||||
recvData >> mailbox;
|
||||
recvData >> mailId;
|
||||
recvData.read_skip<uint32>(); // mailTemplateId
|
||||
|
||||
if (!CanOpenMailBox(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;
|
||||
// xinef: update global data
|
||||
sWorld->UpdateGlobalPlayerMails(player->GetGUIDLow(), -1);
|
||||
}
|
||||
player->SendMailResult(mailId, MAIL_DELETED, MAIL_OK);
|
||||
}
|
||||
|
||||
void WorldSession::HandleMailReturnToSender(WorldPacket & recvData)
|
||||
{
|
||||
uint64 mailbox;
|
||||
uint32 mailId;
|
||||
recvData >> mailbox;
|
||||
recvData >> mailId;
|
||||
recvData.read_skip<uint64>(); // original sender GUID for return to, not used
|
||||
|
||||
if (!CanOpenMailBox(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();
|
||||
|
||||
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_BY_ID);
|
||||
stmt->setUInt32(0, mailId);
|
||||
trans->Append(stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM_BY_ID);
|
||||
stmt->setUInt32(0, mailId);
|
||||
trans->Append(stmt);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
// xinef: update global data
|
||||
sWorld->UpdateGlobalPlayerMails(player->GetGUIDLow(), -1);
|
||||
}
|
||||
|
||||
//called when player takes item attached in mail
|
||||
void WorldSession::HandleMailTakeItem(WorldPacket & recvData)
|
||||
{
|
||||
uint64 mailbox;
|
||||
uint32 mailId;
|
||||
uint32 itemId;
|
||||
recvData >> mailbox;
|
||||
recvData >> mailId;
|
||||
recvData >> itemId; // item guid low
|
||||
|
||||
if (!CanOpenMailBox(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;
|
||||
}
|
||||
|
||||
// verify that the mail has the item to avoid cheaters taking COD items without paying
|
||||
bool foundItem = false;
|
||||
for (std::vector<MailItemInfo>::const_iterator itr = m->items.begin(); itr != m->items.end(); ++itr)
|
||||
if (itr->item_guid == itemId)
|
||||
{
|
||||
foundItem = true;
|
||||
break;
|
||||
}
|
||||
if (!foundItem)
|
||||
{
|
||||
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);
|
||||
uint32 sender_accId = 0;
|
||||
Player* sender = ObjectAccessor::FindPlayerInOrOutOfWorld(sender_guid);
|
||||
if (sender)
|
||||
sender_accId = sender->GetSession()->GetAccountId();
|
||||
else
|
||||
sender_accId = sObjectMgr->GetPlayerAccountIdByGUID(sender_guid);
|
||||
|
||||
// check player existence
|
||||
if (sender || sender_accId)
|
||||
{
|
||||
MailDraft(m->subject, "")
|
||||
.AddMoney(m->COD)
|
||||
.SendMailTo(trans, MailReceiver(sender, m->sender), MailSender(MAIL_NORMAL, m->receiver), MAIL_CHECK_MASK_COD_PAYMENT);
|
||||
|
||||
if( m->COD >= 10*GOLD )
|
||||
{
|
||||
std::string senderName;
|
||||
if (!sObjectMgr->GetPlayerNameByGUID(sender_guid, senderName))
|
||||
senderName = sObjectMgr->GetTrinityStringForDBCLocale(LANG_UNKNOWN);
|
||||
std::string subj = m->subject;
|
||||
CleanStringForMysqlQuery(subj);
|
||||
CharacterDatabase.PExecute("INSERT INTO log_money VALUES(%u, %u, \"%s\", \"%s\", %u, \"%s\", %u, \"<COD> %s\", NOW())", GetAccountId(), player->GetGUIDLow(), player->GetName().c_str(), player->GetSession()->GetRemoteAddress().c_str(), sender_accId, senderName.c_str(), m->COD, subj.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
it->SetState(ITEM_UNCHANGED); // need to set this state, otherwise item cannot be removed later, if neccessary
|
||||
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& recvData)
|
||||
{
|
||||
uint64 mailbox;
|
||||
uint32 mailId;
|
||||
recvData >> mailbox;
|
||||
recvData >> mailId;
|
||||
|
||||
if (!CanOpenMailBox(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;
|
||||
}
|
||||
|
||||
if (!player->ModifyMoney(m->money, false))
|
||||
{
|
||||
player->SendMailResult(mailId, MAIL_MONEY_TAKEN, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_TOO_MUCH_GOLD);
|
||||
return;
|
||||
}
|
||||
|
||||
m->money = 0;
|
||||
m->state = MAIL_STATE_CHANGED;
|
||||
player->m_mailsUpdated = true;
|
||||
|
||||
player->SendMailResult(mailId, MAIL_MONEY_TAKEN, MAIL_OK);
|
||||
|
||||
// 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 & recvData)
|
||||
{
|
||||
uint64 mailbox;
|
||||
recvData >> mailbox;
|
||||
|
||||
if (!CanOpenMailBox(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)
|
||||
{
|
||||
// Only first 50 mails are displayed
|
||||
if (mailsCount >= 50)
|
||||
{
|
||||
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+MAX_INSPECTED_ENCHANTMENT_SLOT*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:
|
||||
case MAIL_CALENDAR:
|
||||
data << uint32((*itr)->sender); // creature/gameobject entry, auction id, calendar event id?
|
||||
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(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;
|
||||
++mailsCount;
|
||||
}
|
||||
|
||||
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 & recvData)
|
||||
{
|
||||
uint64 mailbox;
|
||||
uint32 mailId;
|
||||
|
||||
recvData >> mailbox;
|
||||
recvData >> mailId;
|
||||
|
||||
if (!CanOpenMailBox(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) || (m->checked & MAIL_CHECK_MASK_COPIED))
|
||||
{
|
||||
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 & /*recvData*/)
|
||||
{
|
||||
WorldPacket data(MSG_QUERY_NEXT_MAIL_TIME, 8);
|
||||
|
||||
if (!_player->m_mailsLoaded)
|
||||
_player->_LoadMail();
|
||||
|
||||
if (_player->unReadMails > 0)
|
||||
{
|
||||
data << float(0); // float
|
||||
data << uint32(0); // count
|
||||
|
||||
uint32 count = 0;
|
||||
time_t now = time(NULL);
|
||||
std::set<uint32> sentSenders;
|
||||
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;
|
||||
|
||||
// only send each mail sender once
|
||||
if (sentSenders.count(m->sender))
|
||||
continue;
|
||||
|
||||
data << uint64(m->messageType == MAIL_NORMAL ? m->sender : 0); // player guid
|
||||
data << uint32(m->messageType != MAIL_NORMAL ? m->sender : 0); // non-player entries
|
||||
data << uint32(m->messageType);
|
||||
data << uint32(m->stationery);
|
||||
data << float(m->deliver_time - now);
|
||||
|
||||
sentSenders.insert(m->sender);
|
||||
++count;
|
||||
if (count == 2) // do not display more than 2 mails
|
||||
break;
|
||||
}
|
||||
|
||||
data.put<uint32>(4, count);
|
||||
}
|
||||
else
|
||||
{
|
||||
data << float(-DAY);
|
||||
data << uint32(0);
|
||||
}
|
||||
|
||||
SendPacket(&data);
|
||||
}
|
||||
1814
src/server/game/Handlers/MiscHandler.cpp
Normal file
1814
src/server/game/Handlers/MiscHandler.cpp
Normal file
File diff suppressed because it is too large
Load Diff
704
src/server/game/Handlers/MovementHandler.cpp
Normal file
704
src/server/game/Handlers/MovementHandler.cpp
Normal file
@@ -0,0 +1,704 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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"
|
||||
#include "CellImpl.h"
|
||||
#include "Pet.h"
|
||||
#include "ArenaSpectator.h"
|
||||
#include "Chat.h"
|
||||
#include "BattlegroundMgr.h"
|
||||
|
||||
#define MOVEMENT_PACKET_TIME_DELAY 0
|
||||
|
||||
void WorldSession::HandleMoveWorldportAckOpcode(WorldPacket & /*recvData*/)
|
||||
{
|
||||
;//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(0);
|
||||
|
||||
// get the teleport destination
|
||||
WorldLocation const& loc = GetPlayer()->GetTeleportDest();
|
||||
|
||||
// possible errors in the coordinate validity check
|
||||
if (!MapManager::IsValidMapCoord(loc))
|
||||
{
|
||||
KickPlayer();
|
||||
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());
|
||||
|
||||
Map* oldMap = GetPlayer()->GetMap();
|
||||
if (GetPlayer()->IsInWorld())
|
||||
{
|
||||
sLog->outError("Player (Name %s) is still in world when teleported from map %u to new map %u", GetPlayer()->GetName().c_str(), oldMap->GetId(), loc.GetMapId());
|
||||
oldMap->RemovePlayerFromMap(GetPlayer(), false);
|
||||
}
|
||||
|
||||
// reset instance validity, except if going to an instance inside an instance
|
||||
if (GetPlayer()->m_InstanceValid == false && !mInstance)
|
||||
{
|
||||
GetPlayer()->m_InstanceValid = true;
|
||||
// pussywizard: m_InstanceValid can be false only by leaving a group in an instance => so remove temp binds that could not be removed because player was still on the map!
|
||||
if (!sInstanceSaveMgr->PlayerIsPermBoundToInstance(GetPlayer()->GetGUIDLow(), oldMap->GetId(), oldMap->GetDifficulty()))
|
||||
sInstanceSaveMgr->PlayerUnbindInstance(GetPlayer()->GetGUIDLow(), oldMap->GetId(), oldMap->GetDifficulty(), true);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
GetPlayer()->Relocate(loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ(), loc.GetOrientation());
|
||||
|
||||
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().c_str(), 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;
|
||||
}
|
||||
|
||||
oldMap->AfterPlayerUnlinkFromMap();
|
||||
|
||||
// pussywizard: transport teleport couldn't teleport us to the same map (some other teleport pending, reqs not met, etc.), but we still have transport set until player moves! clear it if map differs (crashfix)
|
||||
if (Transport* t = _player->GetTransport())
|
||||
if (!t->IsInMap(_player))
|
||||
{
|
||||
t->RemovePassenger(_player);
|
||||
_player->m_transport = NULL;
|
||||
_player->m_movementInfo.transport.Reset();
|
||||
_player->m_movementInfo.RemoveMovementFlag(MOVEMENTFLAG_ONTRANSPORT);
|
||||
}
|
||||
|
||||
if (!_player->getHostileRefManager().isEmpty())
|
||||
_player->getHostileRefManager().deleteReferences(); // pussywizard: multithreading crashfix
|
||||
|
||||
CellCoord pair(Trinity::ComputeCellCoord(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY()));
|
||||
Cell cell(pair);
|
||||
if (!GridCoord(cell.GridX(), cell.GridY()).IsCoordValid())
|
||||
{
|
||||
KickPlayer();
|
||||
return;
|
||||
}
|
||||
newMap->LoadGrid(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY());
|
||||
|
||||
// pussywizard: player supposed to enter bg map
|
||||
if (_player->InBattleground())
|
||||
{
|
||||
// but landed on another map, cleanup data
|
||||
if (!mEntry->IsBattlegroundOrArena())
|
||||
_player->SetBattlegroundId(0, BATTLEGROUND_TYPE_NONE, PLAYER_MAX_BATTLEGROUND_QUEUES, false, false, TEAM_NEUTRAL);
|
||||
// everything ok
|
||||
else if (Battleground* bg = _player->GetBattleground())
|
||||
{
|
||||
if (_player->IsInvitedForBattlegroundInstance()) // GMs are not invited, so they are not added to participants
|
||||
bg->AddPlayer(_player);
|
||||
}
|
||||
}
|
||||
|
||||
// pussywizard: arena spectator stuff
|
||||
{
|
||||
if (newMap->IsBattleArena() && ((BattlegroundMap*)newMap)->GetBG() && _player->HasPendingSpectatorForBG(((BattlegroundMap*)newMap)->GetInstanceId()))
|
||||
{
|
||||
_player->ClearReceivedSpectatorResetFor();
|
||||
_player->SetIsSpectator(true);
|
||||
ArenaSpectator::SendCommand(_player, "%sENABLE", SPECTATOR_ADDON_PREFIX);
|
||||
((BattlegroundMap*)newMap)->GetBG()->AddSpectator(_player);
|
||||
ArenaSpectator::HandleResetCommand(_player);
|
||||
}
|
||||
else
|
||||
_player->SetIsSpectator(false);
|
||||
|
||||
GetPlayer()->SetPendingSpectatorForBG(0);
|
||||
timeWhoCommandAllowed = time(NULL) + sWorld->GetNextWhoListUpdateDelaySecs() + 1; // after exiting arena Subscribe will scan for a player and cached data says he is still in arena, so disallow until next update
|
||||
|
||||
if (uint32 inviteInstanceId = _player->GetPendingSpectatorInviteInstanceId())
|
||||
{
|
||||
if (Battleground* tbg = sBattlegroundMgr->GetBattleground(inviteInstanceId))
|
||||
tbg->RemoveToBeTeleported(_player->GetGUID());
|
||||
_player->SetPendingSpectatorInviteInstanceId(0);
|
||||
}
|
||||
}
|
||||
|
||||
// xinef: do this again, player can be teleported inside bg->AddPlayer(_player)!!!!
|
||||
CellCoord pair2(Trinity::ComputeCellCoord(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY()));
|
||||
Cell cell2(pair2);
|
||||
if (!GridCoord(cell2.GridX(), cell2.GridY()).IsCoordValid())
|
||||
{
|
||||
KickPlayer();
|
||||
return;
|
||||
}
|
||||
newMap->LoadGrid(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY());
|
||||
|
||||
GetPlayer()->SendInitialPacketsAfterAddToMap();
|
||||
|
||||
// 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, true);
|
||||
}
|
||||
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, true);
|
||||
GetPlayer()->UpdateZone(newzone, newarea);
|
||||
|
||||
// honorless target
|
||||
if (GetPlayer()->pvpInfo.IsHostile)
|
||||
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& recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "MSG_MOVE_TELEPORT_ACK");
|
||||
uint64 guid;
|
||||
|
||||
recvData.readPackGUID(guid);
|
||||
|
||||
uint32 flags, time;
|
||||
recvData >> flags >> time; // unused
|
||||
;//sLog->outStaticDebug("Guid " UI64FMTD, guid);
|
||||
;//sLog->outStaticDebug("Flags %u, time %u", flags, time/IN_MILLISECONDS);
|
||||
|
||||
Player* plMover = _player->m_mover->ToPlayer();
|
||||
|
||||
if (!plMover || !plMover->IsBeingTeleportedNear())
|
||||
return;
|
||||
|
||||
if (guid != plMover->GetGUID())
|
||||
return;
|
||||
|
||||
plMover->SetSemaphoreTeleportNear(0);
|
||||
|
||||
uint32 old_zone = plMover->GetZoneId();
|
||||
|
||||
WorldLocation const& dest = plMover->GetTeleportDest();
|
||||
Position oldPos(*plMover);
|
||||
|
||||
plMover->UpdatePosition(dest, true);
|
||||
|
||||
// xinef: teleport pets if they are not unsummoned
|
||||
if (Pet* pet = plMover->GetPet())
|
||||
{
|
||||
if (!pet->IsWithinDist3d(plMover, plMover->GetMap()->GetVisibilityRange()-5.0f))
|
||||
pet->NearTeleportTo(plMover->GetPositionX(), plMover->GetPositionY(), plMover->GetPositionZ(), pet->GetOrientation());
|
||||
}
|
||||
|
||||
if (oldPos.GetExactDist2d(plMover) > 100.0f)
|
||||
{
|
||||
uint32 newzone, newarea;
|
||||
plMover->GetZoneAndAreaId(newzone, newarea, true);
|
||||
plMover->UpdateZone(newzone, newarea);
|
||||
|
||||
// new zone
|
||||
if (old_zone != newzone)
|
||||
{
|
||||
// honorless target
|
||||
if (plMover->pvpInfo.IsHostile)
|
||||
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();
|
||||
|
||||
plMover->GetMotionMaster()->ReinitializeMovement();
|
||||
|
||||
// pussywizard: client forgets about losing control, resend it
|
||||
if (plMover->HasUnitState(UNIT_STATE_FLEEING|UNIT_STATE_CONFUSED) || plMover->IsCharmed()) // only in such cases SetClientControl(self, false) is sent
|
||||
plMover->SetClientControl(plMover, false, true);
|
||||
}
|
||||
|
||||
void WorldSession::HandleMovementOpcodes(WorldPacket & recvData)
|
||||
{
|
||||
uint16 opcode = recvData.GetOpcode();
|
||||
|
||||
Unit* mover = _player->m_mover;
|
||||
|
||||
ASSERT(mover != NULL); // there must always be a mover
|
||||
|
||||
Player* plrMover = mover->ToPlayer();
|
||||
|
||||
// ignore, waiting processing in WorldSession::HandleMoveWorldportAckOpcode and WorldSession::HandleMoveTeleportAck
|
||||
if (plrMover && plrMover->IsBeingTeleported())
|
||||
{
|
||||
recvData.rfinish(); // prevent warnings spam
|
||||
return;
|
||||
}
|
||||
|
||||
/* extract packet */
|
||||
uint64 guid;
|
||||
|
||||
recvData.readPackGUID(guid);
|
||||
|
||||
MovementInfo movementInfo;
|
||||
movementInfo.guid = guid;
|
||||
ReadMovementInfo(recvData, &movementInfo);
|
||||
|
||||
recvData.rfinish(); // prevent warnings spam
|
||||
|
||||
// pussywizard: typical check for incomming movement packets
|
||||
if (!mover || !mover->IsInWorld() || mover->IsDuringRemoveFromWorld() || guid != mover->GetGUID())
|
||||
return;
|
||||
|
||||
if (!movementInfo.pos.IsPositionValid())
|
||||
{
|
||||
recvData.rfinish(); // prevent warnings spam
|
||||
return;
|
||||
}
|
||||
|
||||
if (movementInfo.flags & MOVEMENTFLAG_ONTRANSPORT)
|
||||
{
|
||||
// T_POS ON VEHICLES!
|
||||
if (mover->GetVehicle())
|
||||
movementInfo.transport.pos = mover->m_movementInfo.transport.pos;
|
||||
|
||||
// transports size limited
|
||||
// (also received at zeppelin leave by some reason with t_* as absolute in continent coordinates, can be safely skipped)
|
||||
if (movementInfo.transport.pos.GetPositionX() > 75.0f || movementInfo.transport.pos.GetPositionY() > 75.0f || movementInfo.transport.pos.GetPositionZ() > 75.0f ||
|
||||
movementInfo.transport.pos.GetPositionX() < -75.0f || movementInfo.transport.pos.GetPositionY() < -75.0f || movementInfo.transport.pos.GetPositionZ() < -75.0f)
|
||||
{
|
||||
recvData.rfinish(); // prevent warnings spam
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Trinity::IsValidMapCoord(movementInfo.pos.GetPositionX() + movementInfo.transport.pos.GetPositionX(), movementInfo.pos.GetPositionY() + movementInfo.transport.pos.GetPositionY(),
|
||||
movementInfo.pos.GetPositionZ() + movementInfo.transport.pos.GetPositionZ(), movementInfo.pos.GetOrientation() + movementInfo.transport.pos.GetOrientation()))
|
||||
{
|
||||
recvData.rfinish(); // prevent warnings spam
|
||||
return;
|
||||
}
|
||||
|
||||
// if we boarded a transport, add us to it
|
||||
if (plrMover)
|
||||
{
|
||||
if (!plrMover->GetTransport())
|
||||
{
|
||||
if (Transport* transport = plrMover->GetMap()->GetTransport(movementInfo.transport.guid))
|
||||
{
|
||||
plrMover->m_transport = transport;
|
||||
transport->AddPassenger(plrMover);
|
||||
}
|
||||
}
|
||||
else if (plrMover->GetTransport()->GetGUID() != movementInfo.transport.guid)
|
||||
{
|
||||
bool foundNewTransport = false;
|
||||
plrMover->m_transport->RemovePassenger(plrMover);
|
||||
if (Transport* transport = plrMover->GetMap()->GetTransport(movementInfo.transport.guid))
|
||||
{
|
||||
foundNewTransport = true;
|
||||
plrMover->m_transport = transport;
|
||||
transport->AddPassenger(plrMover);
|
||||
}
|
||||
|
||||
if (!foundNewTransport)
|
||||
{
|
||||
plrMover->m_transport = NULL;
|
||||
movementInfo.transport.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!mover->GetTransport() && !mover->GetVehicle())
|
||||
movementInfo.flags &= ~MOVEMENTFLAG_ONTRANSPORT;
|
||||
}
|
||||
else if (plrMover && plrMover->GetTransport()) // if we were on a transport, leave
|
||||
{
|
||||
plrMover->m_transport->RemovePassenger(plrMover);
|
||||
plrMover->m_transport = NULL;
|
||||
movementInfo.transport.Reset();
|
||||
}
|
||||
|
||||
if (plrMover && ((movementInfo.flags & MOVEMENTFLAG_SWIMMING) != 0) != plrMover->IsInWater())
|
||||
{
|
||||
// now client not include swimming flag in case jumping under water
|
||||
plrMover->SetInWater(!plrMover->IsInWater() || plrMover->GetBaseMap()->IsUnderWater(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY(), movementInfo.pos.GetPositionZ()));
|
||||
}
|
||||
// Dont allow to turn on walking if charming other player
|
||||
if (mover->GetGUID() != _player->GetGUID())
|
||||
movementInfo.flags &= ~MOVEMENTFLAG_WALKING;
|
||||
|
||||
uint32 mstime = World::GetGameTimeMS();
|
||||
/*----------------------*/
|
||||
if(m_clientTimeDelay == 0)
|
||||
m_clientTimeDelay = mstime > movementInfo.time ? std::min(mstime - movementInfo.time, (uint32)100) : 0;
|
||||
|
||||
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
|
||||
if (mover->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
|
||||
{
|
||||
// Xinef: skip moving packets
|
||||
if (movementInfo.HasMovementFlag(MOVEMENTFLAG_MASK_MOVING))
|
||||
return;
|
||||
movementInfo.pos.Relocate(mover->GetPositionX(), mover->GetPositionY(), mover->GetPositionZ());
|
||||
|
||||
if (mover->GetTypeId() == TYPEID_UNIT)
|
||||
{
|
||||
movementInfo.transport.guid = mover->m_movementInfo.transport.guid;
|
||||
movementInfo.transport.pos.Relocate(mover->m_movementInfo.transport.pos.GetPositionX(), mover->m_movementInfo.transport.pos.GetPositionY(), mover->m_movementInfo.transport.pos.GetPositionZ());
|
||||
movementInfo.transport.seat = mover->m_movementInfo.transport.seat;
|
||||
}
|
||||
}
|
||||
|
||||
/* process position-change */
|
||||
WorldPacket data(opcode, recvData.size());
|
||||
//movementInfo.time = movementInfo.time + m_clientTimeDelay + MOVEMENT_PACKET_TIME_DELAY;
|
||||
movementInfo.time = mstime; // pussywizard: set to time of relocation (server time), constant addition may smoothen movement clientside, but client sees target on different position than the real serverside position
|
||||
|
||||
movementInfo.guid = mover->GetGUID();
|
||||
WriteMovementInfo(&data, &movementInfo);
|
||||
mover->SendMessageToSet(&data, _player);
|
||||
|
||||
mover->m_movementInfo = movementInfo;
|
||||
|
||||
// this is almost never true (pussywizard: only one packet when entering vehicle), normally use mover->IsVehicle()
|
||||
if (mover->GetVehicle())
|
||||
{
|
||||
mover->SetOrientation(movementInfo.pos.GetOrientation());
|
||||
mover->UpdatePosition(movementInfo.pos);
|
||||
return;
|
||||
}
|
||||
|
||||
// pussywizard: previously always mover->UpdatePosition(movementInfo.pos);
|
||||
if (movementInfo.flags & MOVEMENTFLAG_ONTRANSPORT && mover->GetTransport())
|
||||
{
|
||||
float x, y, z, o;
|
||||
movementInfo.transport.pos.GetPosition(x, y, z, o);
|
||||
mover->GetTransport()->CalculatePassengerPosition(x, y, z, &o);
|
||||
mover->UpdatePosition(x, y, z, o);
|
||||
}
|
||||
else
|
||||
mover->UpdatePosition(movementInfo.pos);
|
||||
|
||||
// fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map).
|
||||
// Xinef: moved it here, previously StopMoving function called when player died relocated him to last saved coordinates (which were in air)
|
||||
if (opcode == MSG_MOVE_FALL_LAND && plrMover && !plrMover->IsInFlight() && (!plrMover->GetTransport() || plrMover->GetTransport()->IsStaticTransport()))
|
||||
plrMover->HandleFall(movementInfo);
|
||||
// Xinef: interrupt parachutes upon falling or landing in water
|
||||
if (opcode == MSG_MOVE_FALL_LAND || opcode == MSG_MOVE_START_SWIM)
|
||||
mover->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_LANDING); // Parachutes
|
||||
|
||||
|
||||
if (plrMover) // nothing is charmed, or player charmed
|
||||
{
|
||||
if (plrMover->IsSitState() && (movementInfo.flags & (MOVEMENTFLAG_MASK_MOVING | MOVEMENTFLAG_MASK_TURNING)))
|
||||
plrMover->SetStandState(UNIT_STAND_STATE_STAND);
|
||||
|
||||
plrMover->UpdateFallInformationIfNeed(movementInfo, opcode);
|
||||
|
||||
if (movementInfo.pos.GetPositionZ() < -500.0f)
|
||||
if (!plrMover->GetBattleground() || !plrMover->GetBattleground()->HandlePlayerUnderMap(_player))
|
||||
{
|
||||
if (plrMover->IsAlive())
|
||||
{
|
||||
plrMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth());
|
||||
// player can be alive if GM
|
||||
if (plrMover->IsAlive())
|
||||
plrMover->KillPlayer();
|
||||
}
|
||||
plrMover->StopMovingOnCurrentPos(); // pussywizard: moving corpse can't release spirit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recvData)
|
||||
{
|
||||
uint32 opcode = recvData.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;
|
||||
|
||||
recvData.readPackGUID(guid);
|
||||
|
||||
// pussywizard: special check, only player mover allowed here
|
||||
if (guid != _player->m_mover->GetGUID() || guid != _player->GetGUID())
|
||||
{
|
||||
recvData.rfinish(); // prevent warnings spam
|
||||
return;
|
||||
}
|
||||
|
||||
// continue parse packet
|
||||
|
||||
recvData >> unk1; // counter or moveEvent
|
||||
|
||||
MovementInfo movementInfo;
|
||||
movementInfo.guid = guid;
|
||||
ReadMovementInfo(recvData, &movementInfo);
|
||||
|
||||
recvData >> 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().c_str(), _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().c_str(), GetAccountId(), _player->GetSpeed(move_type), newspeed);
|
||||
KickPlayer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleSetActiveMoverOpcode(WorldPacket &recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_SET_ACTIVE_MOVER");
|
||||
|
||||
uint64 guid;
|
||||
recvData >> guid;
|
||||
|
||||
if (GetPlayer()->IsInWorld() && _player->m_mover && _player->m_mover->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 &recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_MOVE_NOT_ACTIVE_MOVER");
|
||||
|
||||
uint64 old_mover_guid;
|
||||
recvData.readPackGUID(old_mover_guid);
|
||||
|
||||
// pussywizard: typical check for incomming movement packets
|
||||
if (!_player->m_mover || !_player->m_mover->IsInWorld() || _player->m_mover->IsDuringRemoveFromWorld() || old_mover_guid != _player->m_mover->GetGUID())
|
||||
{
|
||||
recvData.rfinish(); // prevent warnings spam
|
||||
return;
|
||||
}
|
||||
|
||||
MovementInfo mi;
|
||||
mi.guid = old_mover_guid;
|
||||
ReadMovementInfo(recvData, &mi);
|
||||
|
||||
_player->m_mover->m_movementInfo = mi;
|
||||
}
|
||||
|
||||
void WorldSession::HandleMountSpecialAnimOpcode(WorldPacket& /*recvData*/)
|
||||
{
|
||||
WorldPacket data(SMSG_MOUNTSPECIAL_ANIM, 8);
|
||||
data << uint64(GetPlayer()->GetGUID());
|
||||
|
||||
GetPlayer()->SendMessageToSet(&data, false);
|
||||
}
|
||||
|
||||
void WorldSession::HandleMoveKnockBackAck(WorldPacket & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_MOVE_KNOCK_BACK_ACK");
|
||||
|
||||
uint64 guid;
|
||||
recvData.readPackGUID(guid);
|
||||
|
||||
// pussywizard: typical check for incomming movement packets
|
||||
if (!_player->m_mover || !_player->m_mover->IsInWorld() || _player->m_mover->IsDuringRemoveFromWorld() || guid != _player->m_mover->GetGUID())
|
||||
{
|
||||
recvData.rfinish(); // prevent warnings spam
|
||||
return;
|
||||
}
|
||||
|
||||
recvData.read_skip<uint32>(); // unk
|
||||
|
||||
MovementInfo movementInfo;
|
||||
movementInfo.guid = guid;
|
||||
ReadMovementInfo(recvData, &movementInfo);
|
||||
|
||||
_player->m_mover->m_movementInfo = movementInfo;
|
||||
|
||||
WorldPacket data(MSG_MOVE_KNOCK_BACK, 66);
|
||||
data.appendPackGUID(guid);
|
||||
_player->m_mover->BuildMovementPacket(&data);
|
||||
|
||||
// knockback specific info
|
||||
data << movementInfo.jump.sinAngle;
|
||||
data << movementInfo.jump.cosAngle;
|
||||
data << movementInfo.jump.xyspeed;
|
||||
data << movementInfo.jump.zspeed;
|
||||
|
||||
_player->SendMessageToSet(&data, false);
|
||||
}
|
||||
|
||||
void WorldSession::HandleMoveHoverAck(WorldPacket& recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_MOVE_HOVER_ACK");
|
||||
|
||||
uint64 guid; // guid - unused
|
||||
recvData.readPackGUID(guid);
|
||||
|
||||
recvData.read_skip<uint32>(); // unk
|
||||
|
||||
MovementInfo movementInfo;
|
||||
movementInfo.guid = guid;
|
||||
ReadMovementInfo(recvData, &movementInfo);
|
||||
|
||||
recvData.read_skip<uint32>(); // unk2
|
||||
}
|
||||
|
||||
void WorldSession::HandleMoveWaterWalkAck(WorldPacket& recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_MOVE_WATER_WALK_ACK");
|
||||
|
||||
uint64 guid; // guid - unused
|
||||
recvData.readPackGUID(guid);
|
||||
|
||||
recvData.read_skip<uint32>(); // unk
|
||||
|
||||
MovementInfo movementInfo;
|
||||
movementInfo.guid = guid;
|
||||
ReadMovementInfo(recvData, &movementInfo);
|
||||
|
||||
recvData.read_skip<uint32>(); // unk2
|
||||
}
|
||||
|
||||
void WorldSession::HandleSummonResponseOpcode(WorldPacket& recvData)
|
||||
{
|
||||
if (!_player->IsAlive() || _player->IsInCombat())
|
||||
return;
|
||||
|
||||
uint64 summoner_guid;
|
||||
bool agree;
|
||||
recvData >> summoner_guid;
|
||||
recvData >> agree;
|
||||
|
||||
if (agree && _player->IsSummonAsSpectator())
|
||||
{
|
||||
ChatHandler chc(this);
|
||||
if (Player* summoner = ObjectAccessor::FindPlayer(summoner_guid))
|
||||
ArenaSpectator::HandleSpectatorSpectateCommand(&chc, summoner->GetName().c_str());
|
||||
else
|
||||
chc.PSendSysMessage("Requested player not found.");
|
||||
|
||||
agree = false;
|
||||
}
|
||||
_player->SetSummonAsSpectator(false);
|
||||
_player->SummonIfPossible(agree);
|
||||
}
|
||||
902
src/server/game/Handlers/NPCHandler.cpp
Normal file
902
src/server/game/Handlers/NPCHandler.cpp
Normal file
@@ -0,0 +1,902 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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 "ReputationMgr.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 & recvData)
|
||||
{
|
||||
uint64 guid;
|
||||
recvData >> 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_STATE_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 & recvData)
|
||||
{
|
||||
uint64 guid;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_BANKER_ACTIVATE");
|
||||
|
||||
recvData >> 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_STATE_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
SendShowBank(guid);
|
||||
}
|
||||
|
||||
void WorldSession::SendShowBank(uint64 guid)
|
||||
{
|
||||
WorldPacket data(SMSG_SHOW_BANK, 8);
|
||||
data << guid;
|
||||
m_currentBankerGUID = guid;
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::SendShowMailBox(uint64 guid)
|
||||
{
|
||||
WorldPacket data(SMSG_SHOW_MAILBOX, 8);
|
||||
data << guid;
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleTrainerListOpcode(WorldPacket & recvData)
|
||||
{
|
||||
uint64 guid;
|
||||
|
||||
recvData >> 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_STATE_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
CreatureTemplate const* ci = unit->GetCreatureTemplate();
|
||||
|
||||
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 & recvData)
|
||||
{
|
||||
uint64 guid;
|
||||
uint32 spellId = 0;
|
||||
|
||||
recvData >> 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_STATE_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
// 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);
|
||||
|
||||
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 & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GOSSIP_HELLO");
|
||||
|
||||
uint64 guid;
|
||||
recvData >> 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;
|
||||
}
|
||||
|
||||
// xinef: check if we have ANY npc flags
|
||||
if (unit->GetUInt32Value(UNIT_NPC_FLAGS) == UNIT_NPC_FLAG_NONE)
|
||||
return;
|
||||
|
||||
// xinef: do not allow to open gossip when npc is in combat
|
||||
if (unit->GetUInt32Value(UNIT_NPC_FLAGS) == UNIT_NPC_FLAG_GOSSIP && unit->IsInCombat()) // should work on all flags?
|
||||
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_STATE_DIED))
|
||||
// GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
// xinef: and if he has pure gossip or is banker and moves or is tabard designer?
|
||||
//if (unit->IsArmorer() || unit->IsCivilian() || unit->IsQuestGiver() || unit->IsServiceProvider() || unit->IsGuard())
|
||||
{
|
||||
//if (!unit->GetTransport()) // pussywizard: reverted with new spline (old: without this check, npc would stay in place and the transport would continue moving, so the npc falls off. NPCs on transports don't have waypoints, so stopmoving is not needed)
|
||||
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->GetCreatureTemplate()->GossipMenuId, true);
|
||||
_player->SendPreparedGossip(unit);
|
||||
}
|
||||
unit->AI()->sGossipHello(_player);
|
||||
}
|
||||
|
||||
/*void WorldSession::HandleGossipSelectOptionOpcode(WorldPacket & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_PACKETIO, "WORLD: CMSG_GOSSIP_SELECT_OPTION");
|
||||
|
||||
uint32 option;
|
||||
uint32 unk;
|
||||
uint64 guid;
|
||||
std::string code = "";
|
||||
|
||||
recvData >> guid >> unk >> option;
|
||||
|
||||
if (_player->PlayerTalkClass->GossipOptionCoded(option))
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_PACKETIO, "reading string");
|
||||
recvData >> 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_STATE_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 & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_SPIRIT_HEALER_ACTIVATE");
|
||||
|
||||
uint64 guid;
|
||||
|
||||
recvData >> 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_STATE_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->GetTeamId());
|
||||
|
||||
// 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->GetTeamId());
|
||||
|
||||
if (corpseGrave != ghostGrave)
|
||||
_player->TeleportTo(corpseGrave->map_id, corpseGrave->x, corpseGrave->y, corpseGrave->z, _player->GetOrientation());
|
||||
// or update at original position
|
||||
//else
|
||||
// _player->UpdateObjectVisibility(); // xinef: not needed, called in ResurrectPlayer
|
||||
}
|
||||
// or update at original position
|
||||
//else
|
||||
// _player->UpdateObjectVisibility(); // xinef: not needed, called in ResurrectPlayer
|
||||
}
|
||||
|
||||
void WorldSession::HandleBinderActivateOpcode(WorldPacket & recvData)
|
||||
{
|
||||
uint64 npcGUID;
|
||||
recvData >> 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_STATE_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;
|
||||
|
||||
// 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 & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recv MSG_LIST_STABLED_PETS");
|
||||
uint64 npcGUID;
|
||||
|
||||
recvData >> npcGUID;
|
||||
|
||||
if (!CheckStableMaster(npcGUID))
|
||||
return;
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STATE_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 & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recv CMSG_STABLE_PET");
|
||||
uint64 npcGUID;
|
||||
|
||||
recvData >> npcGUID;
|
||||
|
||||
if (!GetPlayer()->IsAlive())
|
||||
{
|
||||
SendStableResult(STABLE_ERR_STABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CheckStableMaster(npcGUID))
|
||||
{
|
||||
SendStableResult(STABLE_ERR_STABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STATE_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 & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recv CMSG_UNSTABLE_PET.");
|
||||
uint64 npcGUID;
|
||||
uint32 petnumber;
|
||||
|
||||
recvData >> npcGUID >> petnumber;
|
||||
|
||||
if (!CheckStableMaster(npcGUID))
|
||||
{
|
||||
SendStableResult(STABLE_ERR_STABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STATE_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);
|
||||
|
||||
if (!Pet::LoadPetFromDB(_player, PET_LOAD_HANDLE_UNSTABLE_CALLBACK, petEntry, petId))
|
||||
{
|
||||
SendStableResult(STABLE_ERR_STABLE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleBuyStableSlot(WorldPacket & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recv CMSG_BUY_STABLE_SLOT.");
|
||||
uint64 npcGUID;
|
||||
|
||||
recvData >> npcGUID;
|
||||
|
||||
if (!CheckStableMaster(npcGUID))
|
||||
{
|
||||
SendStableResult(STABLE_ERR_STABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STATE_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 &/* recvData */)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "HandleStableRevivePet: Not implemented");
|
||||
}
|
||||
|
||||
void WorldSession::HandleStableSwapPet(WorldPacket & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recv CMSG_STABLE_SWAP_PET.");
|
||||
uint64 npcGUID;
|
||||
uint32 petId;
|
||||
|
||||
recvData >> npcGUID >> petId;
|
||||
|
||||
if (!CheckStableMaster(npcGUID))
|
||||
{
|
||||
SendStableResult(STABLE_ERR_STABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STATE_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;
|
||||
}
|
||||
|
||||
Pet* pet = _player->GetPet();
|
||||
// The player's pet could have been removed during the delay of the DB callback
|
||||
if (!pet)
|
||||
{
|
||||
SendStableResult(STABLE_ERR_STABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
// move alive pet to slot or delete dead pet
|
||||
_player->RemovePet(pet, pet->IsAlive() ? PetSaveMode(slot) : PET_SAVE_AS_DELETED);
|
||||
|
||||
// summon unstabled pet
|
||||
if (!Pet::LoadPetFromDB(_player, PET_LOAD_HANDLE_UNSTABLE_CALLBACK, petEntry, petId))
|
||||
{
|
||||
SendStableResult(STABLE_ERR_STABLE);
|
||||
}
|
||||
else
|
||||
SendStableResult(STABLE_SUCCESS_UNSTABLE);
|
||||
}
|
||||
|
||||
void WorldSession::HandleRepairItemOpcode(WorldPacket & recvData)
|
||||
{
|
||||
;//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
|
||||
|
||||
recvData >> 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_STATE_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);
|
||||
}
|
||||
}
|
||||
|
||||
59
src/server/game/Handlers/NPCHandler.h
Normal file
59
src/server/game/Handlers/NPCHandler.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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
|
||||
|
||||
1297
src/server/game/Handlers/PetHandler.cpp
Normal file
1297
src/server/game/Handlers/PetHandler.cpp
Normal file
File diff suppressed because it is too large
Load Diff
891
src/server/game/Handlers/PetitionsHandler.cpp
Normal file
891
src/server/game/Handlers/PetitionsHandler.cpp
Normal file
@@ -0,0 +1,891 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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"
|
||||
#include "PetitionMgr.h"
|
||||
|
||||
#define CHARTER_DISPLAY_ID 16161
|
||||
|
||||
// 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 & recvData)
|
||||
{
|
||||
;//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;
|
||||
|
||||
recvData >> guidNPC; // NPC GUID
|
||||
recvData.read_skip<uint32>(); // 0
|
||||
recvData.read_skip<uint64>(); // 0
|
||||
recvData >> name; // name
|
||||
recvData.read_skip<std::string>(); // some string
|
||||
recvData.read_skip<uint32>(); // 0
|
||||
recvData.read_skip<uint32>(); // 0
|
||||
recvData.read_skip<uint32>(); // 0
|
||||
recvData.read_skip<uint32>(); // 0
|
||||
recvData.read_skip<uint32>(); // 0
|
||||
recvData.read_skip<uint32>(); // 0
|
||||
recvData.read_skip<uint32>(); // 0
|
||||
recvData.read_skip<uint16>(); // 0
|
||||
recvData.read_skip<uint32>(); // 0
|
||||
recvData.read_skip<uint32>(); // 0
|
||||
recvData.read_skip<uint32>(); // 0
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
recvData.read_skip<std::string>();
|
||||
|
||||
recvData >> clientIndex; // index
|
||||
recvData.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_STATE_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_COMMAND_CREATE, ERR_GUILD_NAME_EXISTS_S, name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sObjectMgr->IsReservedName(name) || !ObjectMgr::IsValidCharterName(name))
|
||||
{
|
||||
Guild::SendCommandResult(this, GUILD_COMMAND_CREATE, 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
|
||||
Petition const* petition = sPetitionMgr->GetPetitionByOwnerWithType(_player->GetGUIDLow(), type);
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "Invalid petition GUIDs: %s", ssInvalidPetitionGUIDs.str().c_str());
|
||||
CharacterDatabase.EscapeString(name);
|
||||
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
|
||||
if (petition)
|
||||
{
|
||||
trans->PAppend("DELETE FROM petition WHERE petitionguid = %u", petition->petitionGuid);
|
||||
trans->PAppend("DELETE FROM petition_sign WHERE petitionguid = %u", petition->petitionGuid);
|
||||
// xinef: clear petition store
|
||||
sPetitionMgr->RemovePetition(petition->petitionGuid);
|
||||
}
|
||||
|
||||
// xinef: petition pointer is invalid from now on
|
||||
|
||||
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PETITION);
|
||||
stmt->setUInt32(0, _player->GetGUIDLow());
|
||||
stmt->setUInt32(1, charter->GetGUIDLow());
|
||||
stmt->setString(2, name);
|
||||
stmt->setUInt8(3, uint8(type));
|
||||
trans->Append(stmt);
|
||||
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
|
||||
// xinef: fill petition store
|
||||
sPetitionMgr->AddPetition(charter->GetGUIDLow(), _player->GetGUIDLow(), name, uint8(type));
|
||||
}
|
||||
|
||||
void WorldSession::HandlePetitionShowSignOpcode(WorldPacket& recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "Received opcode CMSG_PETITION_SHOW_SIGNATURES");
|
||||
|
||||
uint64 petitionguid;
|
||||
recvData >> 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 petitionGuidLow = GUID_LOPART(petitionguid);
|
||||
|
||||
Petition const* petition = sPetitionMgr->GetPetition(petitionGuidLow);
|
||||
if (!petition)
|
||||
return;
|
||||
|
||||
uint32 type = petition->petitionType;
|
||||
|
||||
// if guild petition and has guild => error, return;
|
||||
if (type == GUILD_CHARTER_TYPE && _player->GetGuildId())
|
||||
return;
|
||||
|
||||
Signatures const* signatures = sPetitionMgr->GetSignature(petitionGuidLow);
|
||||
uint8 signs = signatures ? signatures->signatureMap.size() : 0;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_PETITION_SHOW_SIGNATURES petition entry: '%u'", petitionGuidLow);
|
||||
|
||||
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(petitionGuidLow); // guild guid
|
||||
data << uint8(signs); // sign's count
|
||||
|
||||
if (signs)
|
||||
for (SignatureMap::const_iterator itr = signatures->signatureMap.begin(); itr != signatures->signatureMap.begin(); ++itr)
|
||||
{
|
||||
data << uint64(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER)); // Player GUID
|
||||
data << uint32(0); // there 0 ...
|
||||
}
|
||||
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandlePetitionQueryOpcode(WorldPacket & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "Received opcode CMSG_PETITION_QUERY"); // ok
|
||||
|
||||
uint32 guildguid;
|
||||
uint64 petitionguid;
|
||||
recvData >> guildguid; // in Trinity always same as GUID_LOPART(petitionguid)
|
||||
recvData >> 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)
|
||||
{
|
||||
Petition const* petition = sPetitionMgr->GetPetition(GUID_LOPART(petitionguid));
|
||||
if (!petition)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_PETITION_QUERY failed for petition (GUID: %u)", GUID_LOPART(petitionguid));
|
||||
return;
|
||||
}
|
||||
|
||||
uint8 type = petition->petitionType;
|
||||
WorldPacket data(SMSG_PETITION_QUERY_RESPONSE, (4+8+petition->petitionName.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 << MAKE_NEW_GUID(petition->ownerGuid, 0, HIGHGUID_PLAYER); // charter owner guid
|
||||
data << petition->petitionName; // name (guild/arena team)
|
||||
data << uint8(0); // some string
|
||||
if (type == GUILD_CHARTER_TYPE)
|
||||
{
|
||||
uint32 needed = sWorld->getIntConfig(CONFIG_MIN_PETITION_SIGNS);
|
||||
data << uint32(needed);
|
||||
data << uint32(needed);
|
||||
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
|
||||
|
||||
data << uint32(type != GUILD_CHARTER_TYPE); // 15 0 - guild, 1 - arena team
|
||||
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandlePetitionRenameOpcode(WorldPacket & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "Received opcode MSG_PETITION_RENAME"); // ok
|
||||
|
||||
uint64 petitionGuid;
|
||||
std::string newName;
|
||||
|
||||
recvData >> petitionGuid; // guid
|
||||
recvData >> newName; // new name
|
||||
|
||||
Item* item = _player->GetItemByGuid(petitionGuid);
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
Petition const* petition = sPetitionMgr->GetPetition(GUID_LOPART(petitionGuid));
|
||||
if (!petition)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_PETITION_QUERY failed for petition (GUID: %u)", GUID_LOPART(petitionGuid));
|
||||
return;
|
||||
}
|
||||
|
||||
if (petition->petitionType == GUILD_CHARTER_TYPE)
|
||||
{
|
||||
if (sGuildMgr->GetGuildByName(newName))
|
||||
{
|
||||
Guild::SendCommandResult(this, GUILD_COMMAND_CREATE, ERR_GUILD_NAME_EXISTS_S, newName);
|
||||
return;
|
||||
}
|
||||
if (sObjectMgr->IsReservedName(newName) || !ObjectMgr::IsValidCharterName(newName))
|
||||
{
|
||||
Guild::SendCommandResult(this, GUILD_COMMAND_CREATE, 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);
|
||||
|
||||
// xinef: update petition container
|
||||
const_cast<Petition*>(petition)->petitionName = newName;
|
||||
|
||||
;//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 & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "Received opcode CMSG_PETITION_SIGN"); // ok
|
||||
|
||||
uint64 petitionGuid;
|
||||
uint8 unk;
|
||||
recvData >> petitionGuid; // petition guid
|
||||
recvData >> unk;
|
||||
|
||||
|
||||
Petition const* petition = sPetitionMgr->GetPetition(GUID_LOPART(petitionGuid));
|
||||
if (!petition)
|
||||
{
|
||||
sLog->outError("Petition %u is not found for player %u %s", GUID_LOPART(petitionGuid), GetPlayer()->GetGUIDLow(), GetPlayer()->GetName().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
uint64 ownerGuid = MAKE_NEW_GUID(petition->ownerGuid, 0, HIGHGUID_PLAYER);
|
||||
uint8 type = petition->petitionType;
|
||||
|
||||
uint32 playerGuid = _player->GetGUIDLow();
|
||||
if (petition->ownerGuid == playerGuid)
|
||||
return;
|
||||
|
||||
Signatures const* signatures = sPetitionMgr->GetSignature(GUID_LOPART(petitionGuid));
|
||||
if (!signatures)
|
||||
return;
|
||||
|
||||
// not let enemies sign guild charter
|
||||
if (GetPlayer()->GetTeamId() != sObjectMgr->GetPlayerTeamIdByGUID(ownerGuid))
|
||||
{
|
||||
if (type != GUILD_CHARTER_TYPE)
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED);
|
||||
else
|
||||
Guild::SendCommandResult(this, GUILD_COMMAND_CREATE, 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().c_str(), 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().c_str(), ERR_ALREADY_IN_ARENA_TEAM_S);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_player->GetArenaTeamIdInvited())
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", _player->GetName().c_str(), ERR_ALREADY_INVITED_TO_ARENA_TEAM_S);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_player->GetGuildId())
|
||||
{
|
||||
Guild::SendCommandResult(this, GUILD_COMMAND_INVITE, ERR_ALREADY_IN_GUILD_S, _player->GetName());
|
||||
return;
|
||||
}
|
||||
if (_player->GetGuildIdInvited())
|
||||
{
|
||||
Guild::SendCommandResult(this, GUILD_COMMAND_INVITE, ERR_ALREADY_INVITED_TO_GUILD_S, _player->GetName());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint32 signs = signatures->signatureMap.size();
|
||||
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
|
||||
|
||||
bool found = false;
|
||||
for (SignatureMap::const_iterator itr = signatures->signatureMap.begin(); itr != signatures->signatureMap.end(); ++itr)
|
||||
if (itr->second == GetAccountId())
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
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::FindPlayerInOrOutOfWorld(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);
|
||||
|
||||
// xinef: fill petition store
|
||||
sPetitionMgr->AddSignature(GUID_LOPART(petitionGuid), GetAccountId(), playerGuid);
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "PETITION SIGN: GUID %u by player: %s (GUID: %u Account: %u)", GUID_LOPART(petitionGuid), _player->GetName().c_str(), 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::FindPlayerInOrOutOfWorld(ownerGuid))
|
||||
owner->GetSession()->SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandlePetitionDeclineOpcode(WorldPacket & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "Received opcode MSG_PETITION_DECLINE"); // ok
|
||||
|
||||
uint64 petitionguid;
|
||||
uint64 ownerguid;
|
||||
recvData >> petitionguid; // petition guid
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "Petition %u declined by %u", GUID_LOPART(petitionguid), _player->GetGUIDLow());
|
||||
|
||||
Petition const* petition = sPetitionMgr->GetPetition(GUID_LOPART(petitionguid));
|
||||
if (!petition)
|
||||
return;
|
||||
|
||||
ownerguid = MAKE_NEW_GUID(petition->ownerGuid, 0, HIGHGUID_PLAYER);
|
||||
if (Player* owner = ObjectAccessor::FindPlayerInOrOutOfWorld(ownerguid)) // petition owner online
|
||||
{
|
||||
WorldPacket data(MSG_PETITION_DECLINE, 8);
|
||||
data << uint64(_player->GetGUID());
|
||||
owner->GetSession()->SendPacket(&data);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleOfferPetitionOpcode(WorldPacket & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "Received opcode CMSG_OFFER_PETITION"); // ok
|
||||
|
||||
uint64 petitionguid, plguid;
|
||||
uint32 junk;
|
||||
Player* player;
|
||||
recvData >> junk; // this is not petition type!
|
||||
recvData >> petitionguid; // petition guid
|
||||
recvData >> plguid; // player guid
|
||||
|
||||
player = ObjectAccessor::FindPlayerInOrOutOfWorld(plguid);
|
||||
if (!player)
|
||||
return;
|
||||
|
||||
Petition const* petition = sPetitionMgr->GetPetition(GUID_LOPART(petitionguid));
|
||||
if (!petition)
|
||||
return;
|
||||
|
||||
if (GetPlayer()->GetTeamId() != player->GetTeamId())
|
||||
{
|
||||
if (petition->petitionType != GUILD_CHARTER_TYPE)
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED);
|
||||
else
|
||||
Guild::SendCommandResult(this, GUILD_COMMAND_CREATE, ERR_GUILD_NOT_ALLIED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (petition->petitionType != 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().c_str(), "", ERR_ARENA_TEAM_TARGET_TOO_LOW_S);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8 slot = ArenaTeam::GetSlotByType(petition->petitionType);
|
||||
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().c_str(), "", ERR_ALREADY_IN_ARENA_TEAM_S);
|
||||
return;
|
||||
}
|
||||
|
||||
if (player->GetArenaTeamIdInvited())
|
||||
{
|
||||
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", _player->GetName().c_str(), ERR_ALREADY_INVITED_TO_ARENA_TEAM_S);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (player->GetGuildId())
|
||||
{
|
||||
Guild::SendCommandResult(this, GUILD_COMMAND_INVITE, ERR_ALREADY_IN_GUILD_S, _player->GetName());
|
||||
return;
|
||||
}
|
||||
|
||||
if (player->GetGuildIdInvited())
|
||||
{
|
||||
Guild::SendCommandResult(this, GUILD_COMMAND_INVITE, ERR_ALREADY_INVITED_TO_GUILD_S, _player->GetName());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Signatures const* signatures = sPetitionMgr->GetSignature(GUID_LOPART(petitionguid));
|
||||
uint8 signs = signatures ? signatures->signatureMap.size() : 0;
|
||||
|
||||
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
|
||||
|
||||
if (signs)
|
||||
for (SignatureMap::const_iterator itr = signatures->signatureMap.begin(); itr != signatures->signatureMap.end(); ++itr)
|
||||
{
|
||||
data << uint64(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER)); // Player GUID
|
||||
data << uint32(0); // there 0 ...
|
||||
}
|
||||
|
||||
player->GetSession()->SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleTurnInPetitionOpcode(WorldPacket & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "Received opcode CMSG_TURN_IN_PETITION");
|
||||
|
||||
// Get petition guid from packet
|
||||
WorldPacket data;
|
||||
uint64 petitionGuid;
|
||||
|
||||
recvData >> 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());
|
||||
|
||||
Petition const* petition = sPetitionMgr->GetPetition(GUID_LOPART(petitionGuid));
|
||||
if (!petition)
|
||||
{
|
||||
sLog->outError("Player %s (guid: %u) tried to turn in petition (guid: %u) that is not present in the database", _player->GetName().c_str(), _player->GetGUIDLow(), GUID_LOPART(petitionGuid));
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 ownerguidlo = petition->ownerGuid;
|
||||
uint8 type = petition->petitionType;
|
||||
std::string name = petition->petitionName;
|
||||
|
||||
// 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;
|
||||
SendPacket(&data);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if guild name is already taken
|
||||
if (sGuildMgr->GetGuildByName(name))
|
||||
{
|
||||
Guild::SendCommandResult(this, GUILD_COMMAND_CREATE, 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
|
||||
Signatures const* signatures = sPetitionMgr->GetSignature(GUID_LOPART(petitionGuid));
|
||||
uint8 signs = signatures ? signatures->signatureMap.size() : 0;
|
||||
SignatureMap signatureCopy;
|
||||
if (signs)
|
||||
signatureCopy = signatures->signatureMap;
|
||||
|
||||
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 (signs < 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);
|
||||
|
||||
Guild::SendCommandResult(this, GUILD_COMMAND_CREATE, ERR_GUILD_COMMAND_SUCCESS, name);
|
||||
|
||||
// Add members from signatures
|
||||
if (signs)
|
||||
for (SignatureMap::const_iterator itr = signatureCopy.begin(); itr != signatureCopy.end(); ++itr)
|
||||
guild->AddMember(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Receive the rest of the packet in arena team creation case
|
||||
uint32 background, icon, iconcolor, border, bordercolor;
|
||||
recvData >> 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
|
||||
if (signs)
|
||||
for (SignatureMap::const_iterator itr = signatureCopy.begin(); itr != signatureCopy.end(); ++itr)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "PetitionsHandler: Adding arena team (guid: %u) member %u", arenaTeam->GetId(), memberGUID);
|
||||
arenaTeam->AddMember(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER));
|
||||
}
|
||||
}
|
||||
|
||||
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
|
||||
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_BY_GUID);
|
||||
stmt->setUInt32(0, GUID_LOPART(petitionGuid));
|
||||
trans->Append(stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_SIGNATURE_BY_GUID);
|
||||
stmt->setUInt32(0, GUID_LOPART(petitionGuid));
|
||||
trans->Append(stmt);
|
||||
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
|
||||
// xinef: clear petition store (petition and signatures)
|
||||
sPetitionMgr->RemovePetition(GUID_LOPART(petitionGuid));
|
||||
|
||||
// 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 & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "Received CMSG_PETITION_SHOWLIST");
|
||||
|
||||
uint64 guid;
|
||||
recvData >> 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(sWorld->getIntConfig(CONFIG_MIN_PETITION_SIGNS)); // 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");
|
||||
}
|
||||
385
src/server/game/Handlers/QueryHandler.cpp
Normal file
385
src/server/game/Handlers/QueryHandler.cpp
Normal file
@@ -0,0 +1,385 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
GlobalPlayerData const* playerData = sWorld->GetGlobalPlayerData(GUID_LOPART(guid));
|
||||
|
||||
WorldPacket data(SMSG_NAME_QUERY_RESPONSE, (8+1+1+1+1+1+10));
|
||||
data.appendPackGUID(guid);
|
||||
if (!playerData)
|
||||
{
|
||||
data << uint8(1); // name unknown
|
||||
SendPacket(&data);
|
||||
return;
|
||||
}
|
||||
|
||||
data << uint8(0); // name known
|
||||
data << playerData->name; // played name
|
||||
data << uint8(0); // realm name - only set for cross realm interaction (such as Battlegrounds)
|
||||
data << uint8(playerData->race);
|
||||
data << uint8(playerData->gender);
|
||||
data << uint8(playerData->playerClass);
|
||||
|
||||
// pussywizard: optimization
|
||||
/*Player* player = ObjectAccessor::FindPlayerInOrOutOfWorld(guid);
|
||||
if (DeclinedName const* names = (player ? player->GetDeclinedNames() : NULL))
|
||||
{
|
||||
data << uint8(1); // Name is declined
|
||||
for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
|
||||
data << names->name[i];
|
||||
}
|
||||
else*/
|
||||
data << uint8(0); // Name is not declined
|
||||
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleNameQueryOpcode(WorldPacket& recvData)
|
||||
{
|
||||
uint64 guid;
|
||||
recvData >> guid;
|
||||
|
||||
// This is disable by default to prevent lots of console spam
|
||||
// sLog->outString("HandleNameQueryOpcode %u", guid);
|
||||
|
||||
SendNameQueryOpcode(guid);
|
||||
}
|
||||
|
||||
void WorldSession::HandleQueryTimeOpcode(WorldPacket & /*recvData*/)
|
||||
{
|
||||
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 & recvData)
|
||||
{
|
||||
uint32 entry;
|
||||
recvData >> entry;
|
||||
uint64 guid;
|
||||
recvData >> guid;
|
||||
|
||||
CreatureTemplate const* ci = sObjectMgr->GetCreatureTemplate(entry);
|
||||
if (ci)
|
||||
SendPacket(&ci->queryData);
|
||||
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 & recvData)
|
||||
{
|
||||
uint32 entry;
|
||||
recvData >> entry;
|
||||
uint64 guid;
|
||||
recvData >> guid;
|
||||
|
||||
const GameObjectTemplate* info = sObjectMgr->GetGameObjectTemplate(entry);
|
||||
if (info)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "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 << info->name;
|
||||
data << uint8(0) << uint8(0) << uint8(0); // name2, name3, name4
|
||||
data << info->IconName; // 2.0.3, string. Icon name to use instead of default icon for go's (ex: "Attack" makes sword)
|
||||
data << info->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, MAX_GAMEOBJECT_DATA);
|
||||
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 & /*recvData*/)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "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(GetPlayer()->GetPhaseMask(), 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 & recvData)
|
||||
{
|
||||
uint32 textID;
|
||||
uint64 guid;
|
||||
|
||||
recvData >> textID;
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_NPC_TEXT_QUERY ID '%u'", textID);
|
||||
|
||||
recvData >> 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
|
||||
{
|
||||
for (int i = 0; i < MAX_GOSSIP_TEXT_OPTIONS; ++i)
|
||||
{
|
||||
data << pGossip->Options[i].Probability;
|
||||
|
||||
if (pGossip->Options[i].Text_0.empty())
|
||||
data << pGossip->Options[i].Text_1;
|
||||
else
|
||||
data << pGossip->Options[i].Text_0;
|
||||
|
||||
if (pGossip->Options[i].Text_1.empty())
|
||||
data << pGossip->Options[i].Text_0;
|
||||
else
|
||||
data << pGossip->Options[i].Text_1;
|
||||
|
||||
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 & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_PAGE_TEXT_QUERY");
|
||||
|
||||
uint32 pageID;
|
||||
recvData >> pageID;
|
||||
recvData.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
|
||||
{
|
||||
data << pageText->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 & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recv CMSG_CORPSE_MAP_POSITION_QUERY");
|
||||
|
||||
uint32 unk;
|
||||
recvData >> 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& recvData)
|
||||
{
|
||||
uint32 count;
|
||||
recvData >> count; // quest count, max=25
|
||||
|
||||
if (count > MAX_QUEST_LOG_SIZE)
|
||||
{
|
||||
recvData.rfinish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Read quest ids and add the in a unordered_set so we don't send POIs for the same quest multiple times
|
||||
UNORDERED_SET<uint32> questIds;
|
||||
for (uint32 i = 0; i < count; ++i)
|
||||
questIds.insert(recvData.read<uint32>()); // quest id
|
||||
|
||||
WorldPacket data(SMSG_QUEST_POI_QUERY_RESPONSE, 4 + (4 + 4)*questIds.size());
|
||||
data << uint32(questIds.size()); // count
|
||||
|
||||
for (UNORDERED_SET<uint32>::const_iterator itr = questIds.begin(); itr != questIds.end(); ++itr)
|
||||
{
|
||||
uint32 questId = *itr;
|
||||
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->FloorId); // floorid
|
||||
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);
|
||||
}
|
||||
647
src/server/game/Handlers/QuestHandler.cpp
Normal file
647
src/server/game/Handlers/QuestHandler.cpp
Normal file
@@ -0,0 +1,647 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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 & recvData)
|
||||
{
|
||||
uint64 guid;
|
||||
recvData >> guid;
|
||||
uint32 questStatus = 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)));
|
||||
if (!questGiver->ToCreature()->IsHostileTo(_player)) // do not show quest status to enemies
|
||||
questStatus = _player->GetQuestDialogStatus(questGiver);
|
||||
break;
|
||||
}
|
||||
case TYPEID_GAMEOBJECT:
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_STATUS_QUERY for GameObject guid = %u", uint32(GUID_LOPART(guid)));
|
||||
questStatus = _player->GetQuestDialogStatus(questGiver);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
sLog->outError("QuestGiver called for unexpected type %u", questGiver->GetTypeId());
|
||||
break;
|
||||
}
|
||||
|
||||
// inform client about status of quest
|
||||
_player->PlayerTalkClass->SendQuestGiverStatus(uint8(questStatus), guid);
|
||||
}
|
||||
|
||||
void WorldSession::HandleQuestgiverHelloOpcode(WorldPacket & recvData)
|
||||
{
|
||||
uint64 guid;
|
||||
recvData >> 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_STATE_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
// Stop the npc if moving
|
||||
//if (!creature->GetTransport()) // pussywizard: reverted with new spline (old: without this check, npc would stay in place and the transport would continue moving, so the npc falls off. NPCs on transports don't have waypoints, so stopmoving is not needed)
|
||||
creature->StopMoving();
|
||||
|
||||
if (sScriptMgr->OnGossipHello(_player, creature))
|
||||
return;
|
||||
|
||||
_player->PrepareGossipMenu(creature, creature->GetCreatureTemplate()->GossipMenuId, true);
|
||||
_player->SendPreparedGossip(creature);
|
||||
|
||||
creature->AI()->sGossipHello(_player);
|
||||
}
|
||||
|
||||
void WorldSession::HandleQuestgiverAcceptQuestOpcode(WorldPacket & recvData)
|
||||
{
|
||||
uint64 guid;
|
||||
uint32 questId;
|
||||
uint32 unk1;
|
||||
recvData >> guid >> questId >> unk1;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_ACCEPT_QUEST npc = %u, quest = %u, unk1 = %u", uint32(GUID_LOPART(guid)), questId, unk1);
|
||||
|
||||
Object* object = ObjectAccessor::GetObjectByTypeMask(*_player, guid, TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT|TYPEMASK_ITEM|TYPEMASK_PLAYER);
|
||||
|
||||
// no or incorrect quest giver
|
||||
if (!object || object == _player || (object->GetTypeId() != TYPEID_PLAYER && !object->hasQuest(questId)) ||
|
||||
(object->GetTypeId() == TYPEID_PLAYER && !object->ToPlayer()->CanShareQuest(questId)))
|
||||
{
|
||||
_player->PlayerTalkClass->SendCloseGossip();
|
||||
_player->SetDivider(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// some kind of WPE protection
|
||||
if (!_player->CanInteractWithQuestGiver(object))
|
||||
return;
|
||||
|
||||
if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId))
|
||||
{
|
||||
// pussywizard: exploit fix, can't share quests that give items to be sold
|
||||
if (object->GetTypeId() == TYPEID_PLAYER)
|
||||
if (uint32 itemId = quest->GetSrcItemId())
|
||||
if (const ItemTemplate* srcItem = sObjectMgr->GetItemTemplate(itemId))
|
||||
if (srcItem->SellPrice > 0)
|
||||
return;
|
||||
|
||||
// prevent cheating
|
||||
if (!GetPlayer()->CanTakeQuest(quest, true))
|
||||
{
|
||||
_player->PlayerTalkClass->SendCloseGossip();
|
||||
_player->SetDivider(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_player->GetDivider() != 0)
|
||||
{
|
||||
Player* player = ObjectAccessor::GetPlayer(*_player, _player->GetDivider());
|
||||
if (player)
|
||||
{
|
||||
player->SendPushToPartyResponse(_player, QUEST_PARTY_MSG_ACCEPT_QUEST);
|
||||
_player->SetDivider(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (_player->CanAddQuest(quest, true))
|
||||
{
|
||||
_player->AddQuestAndCheckCompletion(quest, object);
|
||||
|
||||
if (quest->HasFlag(QUEST_FLAGS_PARTY_ACCEPT))
|
||||
{
|
||||
if (Group* group = _player->GetGroup())
|
||||
{
|
||||
for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
|
||||
{
|
||||
Player* itrPlayer = itr->GetSource();
|
||||
if (!itrPlayer || itrPlayer == _player || !itrPlayer->IsAtGroupRewardDistance(_player) || itrPlayer->HasPendingBind()) // xinef: check range
|
||||
continue;
|
||||
|
||||
if (itrPlayer->CanTakeQuest(quest, false))
|
||||
{
|
||||
itrPlayer->SetDivider(_player->GetGUID());
|
||||
|
||||
// need confirmation that any gossip window will close
|
||||
itrPlayer->PlayerTalkClass->SendCloseGossip();
|
||||
|
||||
_player->SendQuestConfirmAccept(quest, itrPlayer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_player->PlayerTalkClass->SendCloseGossip();
|
||||
|
||||
if (quest->GetSrcSpell() > 0)
|
||||
_player->CastSpell(_player, quest->GetSrcSpell(), true);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_player->PlayerTalkClass->SendCloseGossip();
|
||||
}
|
||||
|
||||
void WorldSession::HandleQuestgiverQueryQuestOpcode(WorldPacket & recvData)
|
||||
{
|
||||
uint64 guid;
|
||||
uint32 questId;
|
||||
uint8 unk1;
|
||||
recvData >> 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;
|
||||
}
|
||||
|
||||
if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId))
|
||||
{
|
||||
// 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->AddQuestAndCheckCompletion(quest, object);
|
||||
|
||||
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 & recvData)
|
||||
{
|
||||
if (!_player)
|
||||
return;
|
||||
|
||||
uint32 questId;
|
||||
recvData >> questId;
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUEST_QUERY quest = %u", questId);
|
||||
|
||||
if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId))
|
||||
_player->PlayerTalkClass->SendQuestQueryResponse(quest);
|
||||
}
|
||||
|
||||
void WorldSession::HandleQuestgiverChooseRewardOpcode(WorldPacket & recvData)
|
||||
{
|
||||
uint32 questId, reward;
|
||||
uint64 guid;
|
||||
recvData >> 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().c_str(), _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().c_str(), _player->GetGUIDLow(), questId);
|
||||
return;
|
||||
}
|
||||
if (_player->CanRewardQuest(quest, reward, true))
|
||||
{
|
||||
_player->RewardQuest(quest, reward, object);
|
||||
|
||||
switch (object->GetTypeId())
|
||||
{
|
||||
case TYPEID_UNIT:
|
||||
{
|
||||
Creature* questgiver = object->ToCreature();
|
||||
if (!sScriptMgr->OnQuestReward(_player, questgiver, quest, reward))
|
||||
{
|
||||
// Send next quest
|
||||
if (Quest const* nextQuest = _player->GetNextQuest(guid, quest))
|
||||
{
|
||||
if (_player->CanAddQuest(nextQuest, false) && _player->CanTakeQuest(nextQuest, false))
|
||||
{
|
||||
if (nextQuest->IsAutoAccept())
|
||||
_player->AddQuestAndCheckCompletion(nextQuest, object);
|
||||
_player->PlayerTalkClass->SendQuestGiverQuestDetails(nextQuest, guid, true);
|
||||
}
|
||||
}
|
||||
|
||||
questgiver->AI()->sQuestReward(_player, quest, reward);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TYPEID_GAMEOBJECT:
|
||||
{
|
||||
GameObject* questGiver = object->ToGameObject();
|
||||
if (!sScriptMgr->OnQuestReward(_player, questGiver, quest, reward))
|
||||
{
|
||||
// Send next quest
|
||||
if (Quest const* nextQuest = _player->GetNextQuest(guid, quest))
|
||||
{
|
||||
if (_player->CanAddQuest(nextQuest, false) && _player->CanTakeQuest(quest, false))
|
||||
{
|
||||
if (nextQuest->IsAutoAccept())
|
||||
_player->AddQuestAndCheckCompletion(nextQuest, object);
|
||||
_player->PlayerTalkClass->SendQuestGiverQuestDetails(nextQuest, guid, true);
|
||||
}
|
||||
}
|
||||
|
||||
questGiver->AI()->QuestReward(_player, quest, reward);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
_player->PlayerTalkClass->SendQuestGiverOfferReward(quest, guid, true);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleQuestgiverRequestRewardOpcode(WorldPacket & recvData)
|
||||
{
|
||||
uint32 questId;
|
||||
uint64 guid;
|
||||
recvData >> guid >> questId;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_REQUEST_REWARD npc = %u, quest = %u", uint32(GUID_LOPART(guid)), questId);
|
||||
|
||||
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 (_player->CanCompleteQuest(questId))
|
||||
_player->CompleteQuest(questId);
|
||||
|
||||
if (_player->GetQuestStatus(questId) != QUEST_STATUS_COMPLETE)
|
||||
return;
|
||||
|
||||
if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId))
|
||||
_player->PlayerTalkClass->SendQuestGiverOfferReward(quest, guid, true);
|
||||
}
|
||||
|
||||
void WorldSession::HandleQuestgiverCancel(WorldPacket& /*recvData*/)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_CANCEL");
|
||||
|
||||
_player->PlayerTalkClass->SendCloseGossip();
|
||||
}
|
||||
|
||||
void WorldSession::HandleQuestLogSwapQuest(WorldPacket& recvData)
|
||||
{
|
||||
uint8 slot1, slot2;
|
||||
recvData >> 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& recvData)
|
||||
{
|
||||
uint8 slot;
|
||||
recvData >> slot;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTLOG_REMOVE_QUEST slot = %u", slot);
|
||||
|
||||
if (slot < MAX_QUEST_LOG_SIZE)
|
||||
{
|
||||
if (uint32 questId = _player->GetQuestSlotQuestId(slot))
|
||||
{
|
||||
if (!_player->TakeQuestSourceItem(questId, true))
|
||||
return; // can't un-equip some items, reject quest cancel
|
||||
|
||||
if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId))
|
||||
{
|
||||
if (quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_TIMED))
|
||||
_player->RemoveTimedQuest(questId);
|
||||
|
||||
if (quest->HasFlag(QUEST_FLAGS_FLAGS_PVP))
|
||||
{
|
||||
_player->pvpInfo.IsHostile = _player->pvpInfo.IsInHostileArea || _player->HasPvPForcingQuest();
|
||||
_player->UpdatePvPState();
|
||||
}
|
||||
}
|
||||
|
||||
_player->TakeQuestSourceItem(questId, true); // remove quest src item from player
|
||||
_player->RemoveActiveQuest(questId);
|
||||
_player->RemoveTimedAchievement(ACHIEVEMENT_TIMED_TYPE_QUEST, questId);
|
||||
|
||||
;//sLog->outDetail("Player %u abandoned quest %u", _player->GetGUIDLow(), questId);
|
||||
}
|
||||
|
||||
_player->SetQuestSlot(slot, 0);
|
||||
|
||||
_player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleQuestConfirmAccept(WorldPacket& recvData)
|
||||
{
|
||||
uint32 questId;
|
||||
recvData >> questId;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUEST_CONFIRM_ACCEPT quest = %u", questId);
|
||||
|
||||
if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId))
|
||||
{
|
||||
if (!quest->HasFlag(QUEST_FLAGS_PARTY_ACCEPT))
|
||||
return;
|
||||
|
||||
Player* originalPlayer = ObjectAccessor::GetPlayer(*_player, _player->GetDivider());
|
||||
if (!originalPlayer)
|
||||
return;
|
||||
|
||||
if (!_player->IsInSameRaidWith(originalPlayer) || !_player->IsAtGroupRewardDistance(originalPlayer))
|
||||
return;
|
||||
|
||||
if (!_player->CanTakeQuest(quest, true) || _player->HasPendingBind())
|
||||
return;
|
||||
|
||||
// pussywizard: exploit fix, can't share quests that give items to be sold
|
||||
if (uint32 itemId = quest->GetSrcItemId())
|
||||
if (const ItemTemplate* srcItem = sObjectMgr->GetItemTemplate(itemId))
|
||||
if (srcItem->SellPrice > 0)
|
||||
return;
|
||||
|
||||
if (_player->CanAddQuest(quest, true))
|
||||
_player->AddQuestAndCheckCompletion(quest, NULL); // NULL, this prevent DB script from duplicate running
|
||||
|
||||
_player->SetDivider(0);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleQuestgiverCompleteQuest(WorldPacket& recvData)
|
||||
{
|
||||
uint32 questId;
|
||||
uint64 guid;
|
||||
|
||||
recvData >> guid >> questId;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_COMPLETE_QUEST npc = %u, quest = %u", uint32(GUID_LOPART(guid)), questId);
|
||||
|
||||
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)
|
||||
{
|
||||
sLog->outError("Possible hacking attempt: Player %s [guid: %u] tried to complete quest [entry: %u] without being in possession of the quest!",
|
||||
_player->GetName().c_str(), _player->GetGUIDLow(), questId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Battleground* bg = _player->GetBattleground())
|
||||
if (bg->GetBgTypeID() == BATTLEGROUND_AV)
|
||||
bg->ToBattlegroundAV()->HandleQuestComplete(questId, _player);
|
||||
|
||||
if (_player->GetQuestStatus(questId) != QUEST_STATUS_COMPLETE)
|
||||
{
|
||||
if (quest->IsRepeatable())
|
||||
_player->PlayerTalkClass->SendQuestGiverRequestItems(quest, guid, _player->CanCompleteRepeatableQuest(quest), false);
|
||||
else
|
||||
_player->PlayerTalkClass->SendQuestGiverRequestItems(quest, guid, _player->CanRewardQuest(quest, false), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (quest->GetReqItemsCount()) // some items required
|
||||
_player->PlayerTalkClass->SendQuestGiverRequestItems(quest, guid, _player->CanRewardQuest(quest, false), false);
|
||||
else // no items required
|
||||
_player->PlayerTalkClass->SendQuestGiverOfferReward(quest, 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;
|
||||
|
||||
if (!_player->CanShareQuest(questId))
|
||||
return;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_PUSHQUESTTOPARTY quest = %u", questId);
|
||||
|
||||
if (Quest const* quest = 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 || !player->IsInMap(_player)) // skip self
|
||||
continue;
|
||||
|
||||
if (!player->SatisfyQuestStatus(quest, 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(quest, 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->SendPushToPartyResponse(player, QUEST_PARTY_MSG_SHARING_QUEST);
|
||||
|
||||
if (quest->IsAutoAccept() && player->CanAddQuest(quest, true) && player->CanTakeQuest(quest, true))
|
||||
player->AddQuestAndCheckCompletion(quest, _player);
|
||||
|
||||
if ((quest->IsAutoComplete() && quest->IsRepeatable() && !quest->IsDailyOrWeekly()) || quest->HasFlag(QUEST_FLAGS_AUTOCOMPLETE))
|
||||
player->PlayerTalkClass->SendQuestGiverRequestItems(quest, _player->GetGUID(), player->CanCompleteRepeatableQuest(quest), true);
|
||||
else
|
||||
{
|
||||
player->SetDivider(_player->GetGUID());
|
||||
player->PlayerTalkClass->SendQuestGiverQuestDetails(quest, player->GetGUID(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleQuestPushResult(WorldPacket& recvPacket)
|
||||
{
|
||||
uint64 guid;
|
||||
uint32 questId;
|
||||
uint8 msg;
|
||||
recvPacket >> guid >> questId >> msg;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received MSG_QUEST_PUSH_RESULT");
|
||||
|
||||
if (_player->GetDivider() && _player->GetDivider() == guid)
|
||||
{
|
||||
if (Player* player = ObjectAccessor::GetPlayer(*_player, _player->GetDivider()))
|
||||
{
|
||||
WorldPacket data(MSG_QUEST_PUSH_RESULT, 8 + 4 + 1);
|
||||
data << uint64(_player->GetGUID());
|
||||
data << uint8(msg); // valid values: 0-8
|
||||
player->GetSession()->SendPacket(&data);
|
||||
_player->SetDivider(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
uint32 questStatus = 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 = _player->GetQuestDialogStatus(questgiver);
|
||||
|
||||
data << uint64(questgiver->GetGUID());
|
||||
data << uint8(questStatus);
|
||||
++count;
|
||||
}
|
||||
else if (IS_GAMEOBJECT_GUID(*itr))
|
||||
{
|
||||
GameObject* questgiver = GetPlayer()->GetMap()->GetGameObject(*itr);
|
||||
if (!questgiver || questgiver->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER)
|
||||
continue;
|
||||
|
||||
questStatus = _player->GetQuestDialogStatus(questgiver);
|
||||
|
||||
data << uint64(questgiver->GetGUID());
|
||||
data << uint8(questStatus);
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
data.put<uint32>(0, count); // write real count
|
||||
SendPacket(&data);
|
||||
}
|
||||
|
||||
void WorldSession::HandleQueryQuestsCompleted(WorldPacket & /*recvData*/)
|
||||
{
|
||||
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);
|
||||
}
|
||||
86
src/server/game/Handlers/ReferAFriendHandler.cpp
Normal file
86
src/server/game/Handlers/ReferAFriendHandler.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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& recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_GRANT_LEVEL");
|
||||
|
||||
uint64 guid;
|
||||
recvData.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& recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_ACCEPT_LEVEL_GRANT");
|
||||
|
||||
uint64 guid;
|
||||
recvData.readPackGUID(guid);
|
||||
|
||||
Player* other = ObjectAccessor::GetObjectInWorld(guid, _player);
|
||||
if (!other)
|
||||
return;
|
||||
|
||||
if (GetAccountId() != other->GetSession()->GetRecruiterId())
|
||||
return;
|
||||
|
||||
if (other->GetGrantableLevels())
|
||||
other->SetGrantableLevels(other->GetGrantableLevels() - 1);
|
||||
else
|
||||
return;
|
||||
|
||||
_player->GiveLevel(_player->getLevel() + 1);
|
||||
}
|
||||
105
src/server/game/Handlers/SkillHandler.cpp
Normal file
105
src/server/game/Handlers/SkillHandler.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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 "Log.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Player.h"
|
||||
#include "Pet.h"
|
||||
#include "UpdateMask.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
|
||||
void WorldSession::HandleLearnTalentOpcode(WorldPacket & recvData)
|
||||
{
|
||||
uint32 talent_id, requested_rank;
|
||||
recvData >> 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;
|
||||
|
||||
// Client has max 44 talents for tree for 3 trees, rounded up : 150
|
||||
uint32 const MaxTalentsCount = 150;
|
||||
|
||||
for (uint32 i = 0; i < talentsCount && i < MaxTalentsCount; ++i)
|
||||
{
|
||||
recvPacket >> talentId >> talentRank;
|
||||
|
||||
_player->LearnTalent(talentId, talentRank);
|
||||
}
|
||||
|
||||
_player->SendTalentsInfoData(false);
|
||||
|
||||
recvPacket.rfinish();
|
||||
}
|
||||
|
||||
void WorldSession::HandleTalentWipeConfirmOpcode(WorldPacket & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "MSG_TALENT_WIPE_CONFIRM");
|
||||
uint64 guid;
|
||||
recvData >> 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;
|
||||
}
|
||||
|
||||
if (!unit->isCanTrainingAndResetTalentsOf(_player))
|
||||
return;
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STATE_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& recvData)
|
||||
{
|
||||
uint32 skillId;
|
||||
recvData >> skillId;
|
||||
|
||||
if (!IsPrimaryProfessionSkill(skillId))
|
||||
return;
|
||||
|
||||
GetPlayer()->SetSkill(skillId, 0, 0, 0);
|
||||
}
|
||||
732
src/server/game/Handlers/SpellHandler.cpp
Normal file
732
src/server/game/Handlers/SpellHandler.cpp
Normal file
@@ -0,0 +1,732 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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 "Vehicle.h"
|
||||
#include "Totem.h"
|
||||
#include "TemporarySummon.h"
|
||||
#include "SpellAuras.h"
|
||||
#include "CreatureAI.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "GameObjectAI.h"
|
||||
#include "SpellAuraEffects.h"
|
||||
#include "Player.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.SetOpcode(recvPacket.read<uint32>());
|
||||
HandleMovementOpcodes(recvPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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->outDebug(LOG_FILTER_NETWORKIO, "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);
|
||||
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "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;
|
||||
|
||||
// xinef: additional check, client outputs message on its own
|
||||
if (!pUser->IsAlive())
|
||||
{
|
||||
pUser->SendEquipError(EQUIP_ERR_YOU_ARE_DEAD, NULL, NULL);
|
||||
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().c_str(), 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?
|
||||
{
|
||||
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_GIFT_BY_ITEM);
|
||||
|
||||
stmt->setUInt32(0, item->GetGUIDLow());
|
||||
|
||||
_openWrappedItemCallback.SetFirstParam(bagIndex);
|
||||
_openWrappedItemCallback.SetSecondParam(slot);
|
||||
_openWrappedItemCallback.SetThirdParam(item->GetGUIDLow());
|
||||
_openWrappedItemCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt));
|
||||
}
|
||||
else
|
||||
pUser->SendLoot(item->GetGUID(), LOOT_CORPSE);
|
||||
}
|
||||
|
||||
void WorldSession::HandleOpenWrappedItemCallback(PreparedQueryResult result, uint8 bagIndex, uint8 slot, uint32 itemLowGUID)
|
||||
{
|
||||
if (!GetPlayer())
|
||||
return;
|
||||
|
||||
Item* item = GetPlayer()->GetItemByPos(bagIndex, slot);
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
if (item->GetGUIDLow() != itemLowGUID || !item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED)) // during getting result, gift was swapped with another item
|
||||
return;
|
||||
|
||||
|
||||
if (!result)
|
||||
{
|
||||
sLog->outError("Wrapped item %u don't have record in character_gifts table and will deleted", item->GetGUIDLow());
|
||||
GetPlayer()->DestroyItem(item->GetBagSlot(), item->GetSlot(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
|
||||
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->SetUInt32Value(ITEM_FIELD_MAXDURABILITY, item->GetTemplate()->MaxDurability);
|
||||
|
||||
item->SetState(ITEM_CHANGED, GetPlayer());
|
||||
GetPlayer()->SaveInventoryAndGoldToDB(trans);
|
||||
|
||||
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GIFT);
|
||||
stmt->setUInt32(0, item->GetGUIDLow());
|
||||
trans->Append(stmt);
|
||||
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
}
|
||||
|
||||
void WorldSession::HandleGameObjectUseOpcode(WorldPacket & recvData)
|
||||
{
|
||||
uint64 guid;
|
||||
recvData >> guid;
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_GAMEOBJ_USE Message [guid=%u]", GUID_LOPART(guid));
|
||||
|
||||
if (GameObject* obj = GetPlayer()->GetMap()->GetGameObject(guid))
|
||||
{
|
||||
if (!obj->IsWithinDistInMap(GetPlayer(), obj->GetInteractionDistance()))
|
||||
return;
|
||||
|
||||
// ignore for remote control state
|
||||
if (GetPlayer()->m_mover != GetPlayer())
|
||||
if (!(GetPlayer()->IsOnVehicle(GetPlayer()->m_mover) || GetPlayer()->IsMounted()) && !obj->GetGOInfo()->IsUsableMounted())
|
||||
return;
|
||||
|
||||
obj->Use(GetPlayer());
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (go->AI()->GossipHello(_player, true))
|
||||
return;
|
||||
|
||||
_player->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( !(spellInfo->Targets & TARGET_FLAG_GAMEOBJECT_ITEM) && (!mover->ToPlayer()->HasActiveSpell(spellId) || spellInfo->IsPassive()) )
|
||||
{
|
||||
//cheater? kick? ban?
|
||||
recvPacket.rfinish(); // prevent spam at ignore packet
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// pussywizard: casting player's spells from vehicle when seat allows it
|
||||
// if ANYTHING CHANGES in this function, INFORM ME BEFORE applying!!!
|
||||
if (Vehicle* veh = mover->GetVehicleKit())
|
||||
if (const VehicleSeatEntry* seat = veh->GetSeatForPassenger(_player))
|
||||
if (seat->m_flags & VEHICLE_SEAT_FLAG_CAN_ATTACK || spellInfo->Effects[EFFECT_0].Effect == SPELL_EFFECT_OPEN_LOCK /*allow looting from vehicle, but only if player has required spell (all necessary opening spells are in playercreateinfo_spell)*/)
|
||||
if ((mover->GetTypeId() == TYPEID_UNIT && !mover->ToCreature()->HasSpell(spellId)) || spellInfo->IsPassive()) // the creature can't cast that spell, check player instead
|
||||
{
|
||||
if( !(spellInfo->Targets & TARGET_FLAG_GAMEOBJECT_ITEM) && (!_player->HasActiveSpell (spellId) || spellInfo->IsPassive()) )
|
||||
{
|
||||
//cheater? kick? ban?
|
||||
recvPacket.rfinish(); // prevent spam at ignore packet
|
||||
return;
|
||||
}
|
||||
|
||||
// at this point, player is a valid caster
|
||||
// swapping the mover will stop the check below at == TYPEID_UNIT, so everything works fine
|
||||
mover = _player;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// pussywizard: HandleClientCastFlags calls HandleMovementOpcodes, which can result in pretty much anything. Caster not in map will crash at GetMap() for spell difficulty in Spell constructor.
|
||||
if (!mover->FindMap())
|
||||
{
|
||||
recvPacket.rfinish(); // prevent spam at ignore packet
|
||||
return;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
_player->InterruptSpell(CURRENT_MELEE_SPELL);
|
||||
if (_player->IsNonMeleeSpellCast(false))
|
||||
_player->InterruptNonMeleeSpells(false, spellId, false, true);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCancelAuraOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
uint32 spellId;
|
||||
recvPacket >> spellId;
|
||||
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||
if (!spellInfo)
|
||||
return;
|
||||
|
||||
// not allow remove non positive spells and spells with attr SPELL_ATTR0_CANT_CANCEL
|
||||
if ((!spellInfo->IsPositive() || spellInfo->HasAttribute(SPELL_ATTR0_CANT_CANCEL) || spellInfo->IsPassive()) && spellId != 605)
|
||||
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;
|
||||
}
|
||||
|
||||
// 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().c_str());
|
||||
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().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pet->IsAlive())
|
||||
{
|
||||
pet->SendPetActionFeedback(FEEDBACK_PET_DEAD);
|
||||
return;
|
||||
}
|
||||
|
||||
pet->RemoveOwnedAura(spellId, 0, 0, AURA_REMOVE_BY_CANCEL);
|
||||
|
||||
pet->AddSpellCooldown(spellId, 0, 0);
|
||||
}
|
||||
|
||||
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 & recvData)
|
||||
{
|
||||
recvData.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, true, true, true);
|
||||
}
|
||||
|
||||
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->ToTotem()->UnSummon();
|
||||
}
|
||||
|
||||
void WorldSession::HandleSelfResOpcode(WorldPacket & /*recvData*/)
|
||||
{
|
||||
;//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, true, 0);
|
||||
_player->AddSpellAndCategoryCooldowns(spellInfo, 0);
|
||||
}
|
||||
|
||||
_player->SetUInt32Value(PLAYER_SELF_RES_SPELL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleSpellClick(WorldPacket& recvData)
|
||||
{
|
||||
uint64 guid;
|
||||
recvData >> 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 & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_GET_MIRRORIMAGE_DATA");
|
||||
uint64 guid;
|
||||
recvData >> 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 >> casterGuid;
|
||||
recvPacket >> spellId;
|
||||
recvPacket >> castCount;
|
||||
recvPacket >> x;
|
||||
recvPacket >> y;
|
||||
recvPacket >> z;
|
||||
|
||||
Unit* caster = ObjectAccessor::GetUnit(*_player, casterGuid);
|
||||
if (!caster)
|
||||
return;
|
||||
|
||||
Spell* spell = caster->FindCurrentSpellBySpellId(spellId);
|
||||
if (!spell || !spell->m_targets.HasDst())
|
||||
return;
|
||||
|
||||
Position pos = *spell->m_targets.GetDstPos();
|
||||
pos.Relocate(x, y, z);
|
||||
spell->m_targets.ModDst(pos);
|
||||
|
||||
WorldPacket data(SMSG_SET_PROJECTILE_POSITION, 21);
|
||||
data << uint64(casterGuid);
|
||||
data << uint8(castCount);
|
||||
data << float(x);
|
||||
data << float(y);
|
||||
data << float(z);
|
||||
caster->SendMessageToSet(&data, true);
|
||||
}
|
||||
255
src/server/game/Handlers/TaxiHandler.cpp
Normal file
255
src/server/game/Handlers/TaxiHandler.cpp
Normal file
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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 "WaypointMovementGenerator.h"
|
||||
|
||||
void WorldSession::HandleTaxiNodeStatusQueryOpcode(WorldPacket & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_TAXINODE_STATUS_QUERY");
|
||||
|
||||
uint64 guid;
|
||||
|
||||
recvData >> 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()->GetTeamId());
|
||||
|
||||
// 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 & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_TAXIQUERYAVAILABLENODES");
|
||||
|
||||
uint64 guid;
|
||||
recvData >> 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_STATE_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()->GetTeamId());
|
||||
|
||||
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_STATE_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()->GetTeamId());
|
||||
|
||||
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 & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_ACTIVATETAXIEXPRESS");
|
||||
|
||||
uint64 guid;
|
||||
uint32 node_count;
|
||||
|
||||
recvData >> 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;
|
||||
recvData >> node;
|
||||
|
||||
if (!GetPlayer()->m_taxi.IsTaximaskNodeKnown(node) && !GetPlayer()->isTaxiCheater())
|
||||
{
|
||||
SendActivateTaxiReply(ERR_TAXINOTVISITED);
|
||||
recvData.rfinish();
|
||||
return;
|
||||
}
|
||||
|
||||
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, 0);
|
||||
}
|
||||
|
||||
void WorldSession::HandleMoveSplineDoneOpcode(WorldPacket& recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_MOVE_SPLINE_DONE");
|
||||
|
||||
uint64 guid; // used only for proper packet read
|
||||
recvData.readPackGUID(guid);
|
||||
|
||||
MovementInfo movementInfo; // used only for proper packet read
|
||||
movementInfo.guid = guid;
|
||||
ReadMovementInfo(recvData, &movementInfo);
|
||||
|
||||
recvData.read_skip<uint32>(); // spline id
|
||||
}
|
||||
|
||||
void WorldSession::HandleActivateTaxiOpcode(WorldPacket & recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_ACTIVATETAXI");
|
||||
|
||||
uint64 guid;
|
||||
std::vector<uint32> nodes;
|
||||
nodes.resize(2);
|
||||
|
||||
recvData >> 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;
|
||||
}
|
||||
|
||||
if (!GetPlayer()->isTaxiCheater())
|
||||
{
|
||||
if (!GetPlayer()->m_taxi.IsTaximaskNodeKnown(nodes[0]) || !GetPlayer()->m_taxi.IsTaximaskNodeKnown(nodes[1]))
|
||||
{
|
||||
SendActivateTaxiReply(ERR_TAXINOTVISITED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
GetPlayer()->ActivateTaxiPathTo(nodes, npc, 0);
|
||||
}
|
||||
|
||||
void WorldSession::SendActivateTaxiReply(ActivateTaxiReply reply)
|
||||
{
|
||||
WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
|
||||
data << uint32(reply);
|
||||
SendPacket(&data);
|
||||
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_ACTIVATETAXIREPLY");
|
||||
}
|
||||
276
src/server/game/Handlers/TicketHandler.cpp
Normal file
276
src/server/game/Handlers/TicketHandler.cpp
Normal file
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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 "Common.h"
|
||||
#include "Language.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Player.h"
|
||||
#include "TicketMgr.h"
|
||||
#include "Util.h"
|
||||
#include "World.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "Chat.h"
|
||||
#include "WorldSession.h"
|
||||
|
||||
void WorldSession::HandleGMTicketCreateOpcode(WorldPacket& recvData)
|
||||
{
|
||||
// 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_CREATE_ERROR;
|
||||
GmTicket* ticket = sTicketMgr->GetTicketByPlayer(GetPlayer()->GetGUID());
|
||||
|
||||
if (ticket && ticket->IsCompleted())
|
||||
sTicketMgr->CloseTicket(ticket->GetId(), GetPlayer()->GetGUID());;
|
||||
|
||||
// Player must not have ticket
|
||||
if (!ticket || ticket->IsClosed())
|
||||
{
|
||||
uint32 mapId;
|
||||
float x, y, z;
|
||||
std::string message;
|
||||
uint32 needResponse;
|
||||
bool needMoreHelp;
|
||||
uint32 count;
|
||||
std::list<uint32> times;
|
||||
uint32 decompressedSize;
|
||||
std::string chatLog;
|
||||
|
||||
recvData >> mapId;
|
||||
recvData >> x >> y >> z;
|
||||
recvData >> message;
|
||||
|
||||
recvData >> needResponse;
|
||||
recvData >> needMoreHelp;
|
||||
|
||||
recvData >> count;
|
||||
|
||||
for (uint32 i = 0; i < count; i++)
|
||||
{
|
||||
uint32 time;
|
||||
recvData >> time;
|
||||
times.push_back(time);
|
||||
}
|
||||
|
||||
recvData >> decompressedSize;
|
||||
|
||||
if (count && decompressedSize && decompressedSize < 0xFFFF)
|
||||
{
|
||||
uint32 pos = recvData.rpos();
|
||||
ByteBuffer dest;
|
||||
dest.resize(decompressedSize);
|
||||
|
||||
uLongf realSize = decompressedSize;
|
||||
if (uncompress(dest.contents(), &realSize, recvData.contents() + pos, recvData.size() - pos) == Z_OK)
|
||||
{
|
||||
dest >> chatLog;
|
||||
}
|
||||
else
|
||||
{
|
||||
sLog->outError("CMSG_GMTICKET_CREATE possibly corrupt. Uncompression failed.");
|
||||
recvData.rfinish();
|
||||
return;
|
||||
}
|
||||
|
||||
recvData.rfinish(); // Will still have compressed data in buffer.
|
||||
}
|
||||
|
||||
ticket = new GmTicket(GetPlayer());
|
||||
ticket->SetPosition(mapId, x, y, z);
|
||||
ticket->SetMessage(message);
|
||||
ticket->SetGmAction(needResponse, needMoreHelp);
|
||||
|
||||
if (!chatLog.empty())
|
||||
ticket->SetChatLog(times, chatLog);
|
||||
|
||||
sTicketMgr->AddTicket(ticket);
|
||||
sTicketMgr->UpdateLastChange();
|
||||
|
||||
sWorld->SendGMText(LANG_COMMAND_TICKETNEW, GetPlayer()->GetName().c_str(), ticket->GetId());
|
||||
|
||||
response = GMTICKET_RESPONSE_CREATE_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_UPDATE_ERROR;
|
||||
if (GmTicket* ticket = sTicketMgr->GetTicketByPlayer(GetPlayer()->GetGUID()))
|
||||
{
|
||||
SQLTransaction trans = SQLTransaction(NULL);
|
||||
ticket->SetMessage(message);
|
||||
ticket->SaveToDB(trans);
|
||||
|
||||
sWorld->SendGMText(LANG_COMMAND_TICKETUPDATED, GetPlayer()->GetName().c_str(), ticket->GetId());
|
||||
|
||||
response = GMTICKET_RESPONSE_UPDATE_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().c_str(), 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;
|
||||
|
||||
UNORDERED_SET<uint32> surveyIds;
|
||||
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
// 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;
|
||||
|
||||
// make sure the same sub survey is not added to DB twice
|
||||
if (!surveyIds.insert(subSurveyId).second)
|
||||
continue;
|
||||
|
||||
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GM_SUBSURVEY);
|
||||
stmt->setUInt32(0, nextSurveyID);
|
||||
stmt->setUInt32(1, subSurveyId);
|
||||
stmt->setUInt32(2, rank);
|
||||
stmt->setString(3, comment);
|
||||
trans->Append(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);
|
||||
|
||||
trans->Append(stmt);
|
||||
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
}
|
||||
|
||||
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);
|
||||
stmt->setUInt32(6, GetLatency());
|
||||
stmt->setUInt32(7, time(NULL));
|
||||
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);
|
||||
}
|
||||
}
|
||||
718
src/server/game/Handlers/TradeHandler.cpp
Normal file
718
src/server/game/Handlers/TradeHandler.cpp
Normal file
@@ -0,0 +1,718 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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());
|
||||
|
||||
// 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());
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
if (_player->GetMoney() >= uint32(MAX_MONEY_AMOUNT) - his_trade->GetMoney())
|
||||
{
|
||||
_player->SendEquipError(EQUIP_ERR_TOO_MUCH_GOLD, NULL, NULL);
|
||||
my_trade->SetAccepted(false, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (trader->GetMoney() >= uint32(MAX_MONEY_AMOUNT) - my_trade->GetMoney())
|
||||
{
|
||||
trader->SendEquipError(EQUIP_ERR_TOO_MUCH_GOLD, NULL, NULL);
|
||||
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);
|
||||
|
||||
if( my_trade->GetMoney() >= 10*GOLD )
|
||||
{
|
||||
CharacterDatabase.PExecute("INSERT INTO log_money VALUES(%u, %u, \"%s\", \"%s\", %u, \"%s\", %u, \"<TRADE>\", NOW())", GetAccountId(), _player->GetGUIDLow(), _player->GetName().c_str(), GetRemoteAddress().c_str(), trader->GetSession()->GetAccountId(), trader->GetName().c_str(), my_trade->GetMoney());
|
||||
}
|
||||
if( his_trade->GetMoney() >= 10*GOLD )
|
||||
{
|
||||
CharacterDatabase.PExecute("INSERT INTO log_money VALUES(%u, %u, \"%s\", \"%s\", %u, \"%s\", %u, \"<TRADE>\", NOW())", trader->GetSession()->GetAccountId(), trader->GetGUIDLow(), trader->GetName().c_str(), trader->GetSession()->GetRemoteAddress().c_str(), GetAccountId(), _player->GetName().c_str(), his_trade->GetMoney());
|
||||
}
|
||||
|
||||
// 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 (PlayerLogout())
|
||||
return;
|
||||
|
||||
SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCancelTradeOpcode(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
_player->TradeCancel(true);
|
||||
}
|
||||
|
||||
void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket)
|
||||
{
|
||||
uint64 ID;
|
||||
recvPacket >> ID;
|
||||
|
||||
if (GetPlayer()->m_trade)
|
||||
return;
|
||||
|
||||
if (!GetPlayer()->IsAlive())
|
||||
{
|
||||
SendTradeStatus(TRADE_STATUS_YOU_DEAD);
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetPlayer()->HasUnitState(UNIT_STATE_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;
|
||||
}
|
||||
|
||||
if (GetPlayer()->IsSpectator())
|
||||
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_STATE_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 (pOther->GetTeamId() !=_player->GetTeamId())
|
||||
{
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
243
src/server/game/Handlers/VehicleHandler.cpp
Normal file
243
src/server/game/Handlers/VehicleHandler.cpp
Normal file
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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 &recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_DISMISS_CONTROLLED_VEHICLE");
|
||||
|
||||
uint64 vehicleGUID = _player->GetCharmGUID();
|
||||
|
||||
if (!vehicleGUID) // something wrong here...
|
||||
{
|
||||
recvData.rfinish(); // prevent warnings spam
|
||||
return;
|
||||
}
|
||||
|
||||
uint64 guid;
|
||||
|
||||
recvData.readPackGUID(guid);
|
||||
|
||||
// pussywizard: typical check for incomming movement packets
|
||||
if (!_player->m_mover || !_player->m_mover->IsInWorld() || _player->m_mover->IsDuringRemoveFromWorld() || guid != _player->m_mover->GetGUID())
|
||||
{
|
||||
recvData.rfinish(); // prevent warnings spam
|
||||
_player->ExitVehicle();
|
||||
return;
|
||||
}
|
||||
|
||||
MovementInfo mi;
|
||||
mi.guid = guid;
|
||||
ReadMovementInfo(recvData, &mi);
|
||||
|
||||
_player->m_mover->m_movementInfo = mi;
|
||||
|
||||
_player->ExitVehicle();
|
||||
}
|
||||
|
||||
void WorldSession::HandleChangeSeatsOnControlledVehicle(WorldPacket &recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_CHANGE_SEATS_ON_CONTROLLED_VEHICLE");
|
||||
|
||||
Unit* vehicle_base = GetPlayer()->GetVehicleBase();
|
||||
if (!vehicle_base)
|
||||
{
|
||||
recvData.rfinish(); // prevent warnings spam
|
||||
return;
|
||||
}
|
||||
|
||||
VehicleSeatEntry const* seat = GetPlayer()->GetVehicle()->GetSeatForPassenger(GetPlayer());
|
||||
if (!seat->CanSwitchFromSeat())
|
||||
{
|
||||
recvData.rfinish(); // prevent warnings spam
|
||||
sLog->outError("HandleChangeSeatsOnControlledVehicle, Opcode: %u, Player %u tried to switch seats but current seatflags %u don't permit that.",
|
||||
recvData.GetOpcode(), GetPlayer()->GetGUIDLow(), seat->m_flags);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (recvData.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
|
||||
recvData.readPackGUID(guid);
|
||||
|
||||
// pussywizard:
|
||||
if (vehicle_base->GetGUID() != guid)
|
||||
{
|
||||
recvData.rfinish(); // prevent warnings spam
|
||||
return;
|
||||
}
|
||||
|
||||
MovementInfo movementInfo;
|
||||
movementInfo.guid = guid;
|
||||
ReadMovementInfo(recvData, &movementInfo);
|
||||
vehicle_base->m_movementInfo = movementInfo;
|
||||
|
||||
uint64 accessory; // accessory guid
|
||||
recvData.readPackGUID(accessory);
|
||||
|
||||
int8 seatId;
|
||||
recvData >> seatId;
|
||||
|
||||
if (!accessory)
|
||||
GetPlayer()->ChangeSeat(-1, seatId > 0); // prev/next
|
||||
else if (Unit* vehUnit = ObjectAccessor::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
|
||||
recvData.readPackGUID(guid);
|
||||
|
||||
int8 seatId;
|
||||
recvData >> seatId;
|
||||
|
||||
if (vehicle_base->GetGUID() == guid)
|
||||
GetPlayer()->ChangeSeat(seatId);
|
||||
else if (Unit* vehUnit = ObjectAccessor::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::GetPlayer(*_player, guid))
|
||||
{
|
||||
if (!player->GetVehicleKit())
|
||||
return;
|
||||
if (!player->IsInRaidWith(_player))
|
||||
return;
|
||||
if (!player->IsWithinDistInMap(_player, INTERACTION_DISTANCE))
|
||||
return;
|
||||
// Xinef:
|
||||
if (!_player->FindMap() || _player->FindMap()->IsBattleArena())
|
||||
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::GetPlayer(*_player, 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& /*recvData*/)
|
||||
{
|
||||
;//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
src/server/game/Handlers/VoiceChatHandler.cpp
Normal file
45
src/server/game/Handlers/VoiceChatHandler.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C)
|
||||
* Copyright (C)
|
||||
*
|
||||
* 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& recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_VOICE_SESSION_ENABLE");
|
||||
// uint8 isVoiceEnabled, uint8 isMicrophoneEnabled
|
||||
recvData.read_skip<uint8>();
|
||||
recvData.read_skip<uint8>();
|
||||
}
|
||||
|
||||
void WorldSession::HandleChannelVoiceOnOpcode(WorldPacket& /*recvData*/)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CHANNEL_VOICE_ON");
|
||||
// Enable Voice button in channel context menu
|
||||
}
|
||||
|
||||
void WorldSession::HandleSetActiveVoiceChannel(WorldPacket& recvData)
|
||||
{
|
||||
;//sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_SET_ACTIVE_VOICE_CHANNEL");
|
||||
recvData.read_skip<uint32>();
|
||||
recvData.read_skip<char*>();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user