diff --git a/src/game/Calendar.cpp b/src/game/Calendar.cpp deleted file mode 100644 index 2a0630240..000000000 --- a/src/game/Calendar.cpp +++ /dev/null @@ -1,724 +0,0 @@ -/* - * This code is part of MaNGOS. Contributor & Copyright details are in AUTHORS/THANKS. - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Calendar.h" -#include "Guild.h" -#include "Mail.h" -#include "ObjectMgr.h" -#include "ProgressBar.h" - -INSTANTIATE_SINGLETON_1(CalendarMgr); - -////////////////////////////////////////////////////////////////////////// -// CalendarEvent Class to store single event informations -////////////////////////////////////////////////////////////////////////// - -CalendarEvent::~CalendarEvent() -{ - RemoveAllInvite(); -} - -// Add an invite to internal invite map return true if success -bool CalendarEvent::AddInvite(CalendarInvite* invite) -{ - if (!invite) - return false; - - return m_Invitee.insert(CalendarInviteMap::value_type(invite->InviteId, invite)).second; -} - -CalendarInvite* CalendarEvent::GetInviteById(uint64 inviteId) -{ - CalendarInviteMap::iterator itr = m_Invitee.find(inviteId); - if (itr != m_Invitee.end()) - return itr->second; - return NULL; -} - -CalendarInvite* CalendarEvent::GetInviteByGuid(ObjectGuid const& guid) -{ - CalendarInviteMap::const_iterator inviteItr = m_Invitee.begin(); - while (inviteItr != m_Invitee.end()) - { - if (inviteItr->second->InviteeGuid == guid) - return inviteItr->second; - ++inviteItr; - } - - return NULL; -} - -// remove invite by its iterator -void CalendarEvent::RemoveInviteByItr(CalendarInviteMap::iterator inviteItr) -{ - MANGOS_ASSERT(inviteItr != m_Invitee.end()); // iterator must be valid - - if (!IsGuildEvent()) - sCalendarMgr.SendCalendarEventInviteRemoveAlert(sObjectMgr.GetPlayer(inviteItr->second->InviteeGuid), this, CALENDAR_STATUS_REMOVED); - - sCalendarMgr.SendCalendarEventInviteRemove(inviteItr->second, Flags); - - CharacterDatabase.PExecute("DELETE FROM calendar_invites WHERE inviteId=" UI64FMTD, inviteItr->second->InviteId); - - delete inviteItr->second; - m_Invitee.erase(inviteItr); -} - -// remove invite by ObjectGuid of the player (may not be found so nothing removed) -void CalendarEvent::RemoveInviteByGuid(ObjectGuid const& playerGuid) -{ - CalendarInviteMap::iterator itr = m_Invitee.begin(); - while (itr != m_Invitee.end()) - { - if (itr->second->InviteeGuid == playerGuid) - RemoveInviteByItr(itr++); - else - ++itr; - } -} - -// remove invite by invite ID (some check done before and if requirement not complete -// operation is aborted and raison is sent to client -bool CalendarEvent::RemoveInviteById(uint64 inviteId, Player* remover) -{ - CalendarInviteMap::iterator inviteItr = m_Invitee.find(inviteId); - if (inviteItr == m_Invitee.end()) - { - // invite not found - sCalendarMgr.SendCalendarCommandResult(remover, CALENDAR_ERROR_NO_INVITE); - return false; - } - - // assign a pointer to CalendarInvite class to make read more easy - CalendarInvite* invite = inviteItr->second; - - if (invite->InviteeGuid != remover->GetObjectGuid()) - { - // check if remover is an invitee - CalendarInvite* removerInvite = GetInviteByGuid(remover->GetObjectGuid()); - if (removerInvite == NULL) - { - // remover is not invitee cheat??? - sCalendarMgr.SendCalendarCommandResult(remover, CALENDAR_ERROR_NOT_INVITED); - return false; - } - - if (removerInvite->Rank != CALENDAR_RANK_MODERATOR && removerInvite->Rank != CALENDAR_RANK_OWNER) - { - // remover have not enough right to remove invite - sCalendarMgr.SendCalendarCommandResult(remover, CALENDAR_ERROR_PERMISSIONS); - return false; - } - } - - if (CreatorGuid == invite->InviteeGuid) - { - sCalendarMgr.SendCalendarCommandResult(remover, CALENDAR_ERROR_DELETE_CREATOR_FAILED); - return false; - } - - // TODO: Send mail to invitee if needed - - RemoveInviteByItr(inviteItr); - return true; -} - -// remove all invite without sending ingame mail -void CalendarEvent::RemoveAllInvite() -{ - CalendarInviteMap::iterator itr = m_Invitee.begin(); - while (itr != m_Invitee.end()) - RemoveInviteByItr(itr++); -} - -// remove all invite sending ingame mail -void CalendarEvent::RemoveAllInvite(ObjectGuid const& removerGuid) -{ - // build mail title - std::ostringstream title; - title << removerGuid << ':' << Title; - - // build mail body - std::ostringstream body; - body << secsToTimeBitFields(time(NULL)); - - // creating mail draft - MailDraft draft(title.str(), body.str()); - - CalendarInviteMap::iterator itr = m_Invitee.begin(); - while (itr != m_Invitee.end()) - { - if (removerGuid != itr->second->InviteeGuid) - draft.SendMailTo(MailReceiver(itr->second->InviteeGuid), this, MAIL_CHECK_MASK_COPIED); - RemoveInviteByItr(itr++); - } -} - -////////////////////////////////////////////////////////////////////////// -// CalendarInvite Classes store single invite information -////////////////////////////////////////////////////////////////////////// - -CalendarInvite::CalendarInvite(CalendarEvent* event, uint64 inviteId, ObjectGuid senderGuid, ObjectGuid inviteeGuid, time_t statusTime, CalendarInviteStatus status, CalendarModerationRank rank, std::string text) : - m_calendarEvent(event), InviteId(inviteId), SenderGuid(senderGuid), InviteeGuid(inviteeGuid), LastUpdateTime(statusTime), Status(status), Rank(rank), Text(text) -{ - // only for pre invite case - if (!event) - InviteId = 0; -} - -////////////////////////////////////////////////////////////////////////// -// CalendarMgr Classes handle all events and invites. -////////////////////////////////////////////////////////////////////////// - -// fill all player events in provided CalendarEventsList -void CalendarMgr::GetPlayerEventsList(ObjectGuid const& guid, CalendarEventsList& calEventList) -{ - uint32 guildId = 0; - Player* player = sObjectMgr.GetPlayer(guid); - if (player) - guildId = player->GetGuildId(); - else - guildId = Player::GetGuildIdFromDB(guid); - - for (CalendarEventStore::iterator itr = m_EventStore.begin(); itr != m_EventStore.end(); ++itr) - { - CalendarEvent* event = &itr->second; - - // add own event and same guild event or announcement - if ((event->CreatorGuid == guid) || ((event->IsGuildAnnouncement() || event->IsGuildEvent()) && event->GuildId == guildId)) - { - calEventList.push_back(event); - continue; - } - - // add all event where player is invited - if (event->GetInviteByGuid(guid)) - calEventList.push_back(event); - } -} - -// fill all player invites in provided CalendarInvitesList -void CalendarMgr::GetPlayerInvitesList(ObjectGuid const& guid, CalendarInvitesList& calInvList) -{ - for (CalendarEventStore::iterator itr = m_EventStore.begin(); itr != m_EventStore.end(); ++itr) - { - CalendarEvent* event = &itr->second; - - if (event->IsGuildAnnouncement()) - continue; - - CalendarInviteMap const* cInvMap = event->GetInviteMap(); - CalendarInviteMap::const_iterator ci_itr = cInvMap->begin(); - while (ci_itr != cInvMap->end()) - { - if (ci_itr->second->InviteeGuid == guid) - { - calInvList.push_back(ci_itr->second); - break; - } - ++ci_itr; - } - } -} - -// add single event to main events store -// some check done before so it may fail and raison is sent to client -// return value is the CalendarEvent pointer on success -CalendarEvent* CalendarMgr::AddEvent(ObjectGuid const& guid, std::string title, std::string description, uint32 type, uint32 repeatable, - uint32 maxInvites, int32 dungeonId, time_t eventTime, time_t unkTime, uint32 flags) -{ - Player* player = sObjectMgr.GetPlayer(guid); - if (!player) - return NULL; - - if (title.empty()) - { - SendCalendarCommandResult(player, CALENDAR_ERROR_NEEDS_TITLE); - return NULL; - } - - if (eventTime < time(NULL)) - { - SendCalendarCommandResult(player, CALENDAR_ERROR_INVALID_DATE); - return NULL; - } - - uint32 guildId = 0; - - if ((flags & CALENDAR_FLAG_GUILD_EVENT) || (flags & CALENDAR_FLAG_GUILD_ANNOUNCEMENT)) - { - guildId = player->GetGuildId(); - if (!guildId) - { - SendCalendarCommandResult(player, CALENDAR_ERROR_GUILD_PLAYER_NOT_IN_GUILD); - return NULL; - } - - if (!CanAddGuildEvent(guildId)) - { - SendCalendarCommandResult(player, CALENDAR_ERROR_GUILD_EVENTS_EXCEEDED); - return NULL; - } - } - else - { - if (!CanAddEvent(guid)) - { - SendCalendarCommandResult(player, CALENDAR_ERROR_EVENTS_EXCEEDED); - return NULL; - } - } - - uint64 nId = GetNewEventId(); - - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "CalendarMgr::AddEvent> ID(" UI64FMTD "), '%s', Desc > '%s', type=%u, repeat=%u, maxInvites=%u, dungeonId=%d, flags=%u", - nId, title.c_str(), description.c_str(), type, repeatable, maxInvites, dungeonId, flags); - - CalendarEvent& newEvent = m_EventStore[nId]; - - newEvent.EventId = nId; - newEvent.CreatorGuid = guid; - newEvent.Title = title; - newEvent.Description = description; - newEvent.Type = (CalendarEventType) type; - newEvent.Repeatable = (CalendarRepeatType) repeatable; - newEvent.DungeonId = dungeonId; - newEvent.EventTime = eventTime; - newEvent.Flags = flags; - newEvent.GuildId = guildId; - - CharacterDatabase.escape_string(title); - CharacterDatabase.escape_string(description); - CharacterDatabase.PExecute("INSERT INTO calendar_events VALUES (" UI64FMTD ", %u, %u, %u, %u, %d, %u, '%s', '%s')", - nId, - guid.GetCounter(), - guildId, - type, - flags, - dungeonId, - uint32(eventTime), - title.c_str(), - description.c_str()); - return &newEvent; -} - -// remove event by its id -// some check done before so it may fail and raison is sent to client -void CalendarMgr::RemoveEvent(uint64 eventId, Player* remover) -{ - CalendarEventStore::iterator citr = m_EventStore.find(eventId); - if (citr == m_EventStore.end()) - { - SendCalendarCommandResult(remover, CALENDAR_ERROR_EVENT_INVALID); - return; - } - - if (remover->GetObjectGuid() != citr->second.CreatorGuid) - { - // only creator can remove his event - SendCalendarCommandResult(remover, CALENDAR_ERROR_PERMISSIONS); - return; - } - - SendCalendarEventRemovedAlert(&citr->second); - - CharacterDatabase.PExecute("DELETE FROM calendar_events WHERE eventId=" UI64FMTD, eventId); - - // explicitly remove all invite and send mail to all invitee - citr->second.RemoveAllInvite(remover->GetObjectGuid()); - m_EventStore.erase(citr); -} - -// Add invit to an event and inform client -// some check done before so it may fail and raison is sent to client -// return value is the CalendarInvite pointer on success -CalendarInvite* CalendarMgr::AddInvite(CalendarEvent* event, ObjectGuid const& senderGuid, ObjectGuid const& inviteeGuid, CalendarInviteStatus status, CalendarModerationRank rank, std::string text, time_t statusTime) -{ - Player* sender = sObjectMgr.GetPlayer(senderGuid); - if (!event || !sender) - return NULL; - - std::string name; - sObjectMgr.GetPlayerNameByGUID(inviteeGuid, name); - - // check if invitee is not already invited - if (event->GetInviteByGuid(inviteeGuid)) - { - SendCalendarCommandResult(sender, CALENDAR_ERROR_ALREADY_INVITED_TO_EVENT_S, name.c_str()); - return NULL; - } - - // check if player can still have new invite (except for event creator) - if (!event->IsGuildAnnouncement() && event->CreatorGuid != inviteeGuid) - { - if (!CanAddInviteTo(inviteeGuid)) - { - SendCalendarCommandResult(sender, CALENDAR_ERROR_OTHER_INVITES_EXCEEDED_S, name.c_str()); - return NULL; - } - } - - CalendarInvite* invite = new CalendarInvite(event, GetNewInviteId(), senderGuid, inviteeGuid, statusTime, status, rank, text); - - if (!event->IsGuildAnnouncement()) - SendCalendarEventInvite(invite); - - if (!event->IsGuildEvent() || invite->InviteeGuid == event->CreatorGuid) - SendCalendarEventInviteAlert(invite); - - if (event->IsGuildAnnouncement()) - { - // no need to realy add invite for announcements - delete invite; - return NULL; - } - - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "Add Invite> eventId[" UI64FMTD "], senderGuid[%s], inviteGuid[%s], Status[%u], rank[%u], text[%s], time[%u]", - event->EventId, senderGuid.GetString().c_str(), inviteeGuid.GetString().c_str(), uint32(status), uint32(rank), text.c_str(), uint32(statusTime)); - - if (!event->AddInvite(invite)) - { - sLog.outError("CalendarEvent::AddInvite > Fail adding invite!"); - delete invite; - return NULL; - } - - CharacterDatabase.PExecute("INSERT INTO calendar_invites VALUES (" UI64FMTD ", " UI64FMTD ", %u, %u, %u, %u, %u)", - invite->InviteId, - event->EventId, - inviteeGuid.GetCounter(), - senderGuid.GetCounter(), - uint32(status), - uint32(statusTime), - uint32(rank)); - - return invite; -} - -// remove invit from an event and inform client -// some check done before so it may fail and raison is sent to client -// require valid eventId/inviteId and correct right for the remover. -bool CalendarMgr::RemoveInvite(uint64 eventId, uint64 inviteId, ObjectGuid const& removerGuid) -{ - Player* remover = sObjectMgr.GetPlayer(removerGuid); - CalendarEventStore::iterator citr = m_EventStore.find(eventId); - if (citr == m_EventStore.end()) - { - SendCalendarCommandResult(remover, CALENDAR_ERROR_EVENT_INVALID); - return false; - } - - CalendarEvent& event = citr->second; - - return event.RemoveInviteById(inviteId, remover); -} - -// return how many events still require some pending action -uint32 CalendarMgr::GetPlayerNumPending(ObjectGuid const& guid) -{ - CalendarInvitesList inviteList; - GetPlayerInvitesList(guid, inviteList); - - uint32 pendingNum = 0; - time_t currTime = time(NULL); - for (CalendarInvitesList::const_iterator itr = inviteList.begin(); itr != inviteList.end(); ++itr) - { - if (CalendarEvent const* event = (*itr)->GetCalendarEvent()) - { - // pass all passed events - if (event->EventTime < currTime) - continue; - - // pass all locked events - if (event->Flags & CALENDAR_FLAG_INVITES_LOCKED) - continue; - } - - // add only invite that require some action - if ((*itr)->Status == CALENDAR_STATUS_INVITED || (*itr)->Status == CALENDAR_STATUS_TENTATIVE || (*itr)->Status == CALENDAR_STATUS_NOT_SIGNED_UP) - ++pendingNum; - } - - return pendingNum; -} - -// copy event to another date (all invitee is copied too but their status are reseted) -void CalendarMgr::CopyEvent(uint64 eventId, time_t newTime, ObjectGuid const& guid) -{ - Player* player = sObjectMgr.GetPlayer(guid); - CalendarEvent* event = GetEventById(eventId); - if (!event) - { - SendCalendarCommandResult(player, CALENDAR_ERROR_EVENT_INVALID); - return; - } - - CalendarEvent* newEvent = AddEvent(guid, event->Title, event->Description, event->Type, event->Repeatable, - CALENDAR_MAX_INVITES, event->DungeonId, newTime, event->UnknownTime, event->Flags); - - if (!newEvent) - return; - - if (newEvent->IsGuildAnnouncement()) - AddInvite(newEvent, guid, guid, CALENDAR_STATUS_CONFIRMED, CALENDAR_RANK_OWNER, "", time(NULL)); - else - { - // copy all invitees, set new owner as the one who make the copy, set invitees status to invited - CalendarInviteMap const* cInvMap = event->GetInviteMap(); - CalendarInviteMap::const_iterator ci_itr = cInvMap->begin(); - - while (ci_itr != cInvMap->end()) - { - if (ci_itr->second->InviteeGuid == guid) - { - AddInvite(newEvent, guid, ci_itr->second->InviteeGuid, CALENDAR_STATUS_CONFIRMED, CALENDAR_RANK_OWNER, "", time(NULL)); - } - else - { - CalendarModerationRank rank = CALENDAR_RANK_PLAYER; - // copy moderator rank - if (ci_itr->second->Rank == CALENDAR_RANK_MODERATOR) - rank = CALENDAR_RANK_MODERATOR; - - AddInvite(newEvent, guid, ci_itr->second->InviteeGuid, CALENDAR_STATUS_INVITED, rank, "", time(NULL)); - } - ++ci_itr; - } - } - - SendCalendarEvent(player, newEvent, CALENDAR_SENDTYPE_COPY); -} - -// remove all events and invite of player -// used when player is deleted -void CalendarMgr::RemovePlayerCalendar(ObjectGuid const& playerGuid) -{ - CalendarEventStore::iterator itr = m_EventStore.begin(); - - while (itr != m_EventStore.end()) - { - if (itr->second.CreatorGuid == playerGuid) - { - // all invite will be automaticaly deleted - m_EventStore.erase(itr++); - // itr already incremented so go recheck event owner - continue; - } - // event not owned by playerGuid but an invite can still be found - CalendarEvent* event = &itr->second; - event->RemoveInviteByGuid(playerGuid); - ++itr; - } -} - -// remove all events and invite of player related to a specific guild -// used when player quit a guild -void CalendarMgr::RemoveGuildCalendar(ObjectGuid const& playerGuid, uint32 GuildId) -{ - CalendarEventStore::iterator itr = m_EventStore.begin(); - - while (itr != m_EventStore.end()) - { - CalendarEvent* event = &itr->second; - if (event->CreatorGuid == playerGuid && (event->IsGuildEvent() || event->IsGuildAnnouncement())) - { - // all invite will be automaticaly deleted - m_EventStore.erase(itr++); - // itr already incremented so go recheck event owner - continue; - } - - // event not owned by playerGuid but an guild invite can still be found - if (event->GuildId != GuildId || !(event->IsGuildEvent() || event->IsGuildAnnouncement())) - { - ++itr; - continue; - } - - event->RemoveInviteByGuid(playerGuid); - ++itr; - } -} - -// load all events and their related invites from invite -void CalendarMgr::LoadCalendarsFromDB() -{ - // in case of reload (not yet implemented) - m_MaxInviteId = 0; - m_MaxEventId = 0; - m_EventStore.clear(); - - sLog.outString("Loading Calendar Events..."); - - // 0 1 2 3 4 5 6 7 8 - QueryResult* eventsQuery = CharacterDatabase.Query("SELECT eventId, creatorGuid, guildId, type, flags, dungeonId, eventTime, title, description FROM calendar_events ORDER BY eventId"); - if (!eventsQuery) - { - BarGoLink bar(1); - bar.step(); - sLog.outString(); - sLog.outString(">> calendar_events table is empty!"); - } - else - { - BarGoLink bar(eventsQuery->GetRowCount()); - do - { - Field* field = eventsQuery->Fetch(); - bar.step(); - - uint64 eventId = field[0].GetUInt64(); - - CalendarEvent& newEvent = m_EventStore[eventId]; - - newEvent.EventId = eventId; - newEvent.CreatorGuid = ObjectGuid(HIGHGUID_PLAYER, field[1].GetUInt32()); - newEvent.GuildId = field[2].GetUInt32(); - newEvent.Type = CalendarEventType(field[3].GetUInt8()); - newEvent.Flags = field[4].GetUInt32(); - newEvent.DungeonId = field[5].GetInt32(); - newEvent.EventTime = time_t(field[6].GetUInt32()); - newEvent.Title = field[7].GetCppString(); - newEvent.Description = field[8].GetCppString(); - - m_MaxEventId = std::max(eventId, m_MaxEventId); - } - while (eventsQuery->NextRow()); - - sLog.outString(); - sLog.outString(">> Loaded %u events!", uint32(eventsQuery->GetRowCount())); - delete eventsQuery; - } - - sLog.outString("Loading Calendar invites..."); - // 0 1 2 3 4 5 6 - QueryResult* invitesQuery = CharacterDatabase.Query("SELECT inviteId, eventId, inviteeGuid, senderGuid, status, lastUpdateTime, rank FROM calendar_invites ORDER BY inviteId"); - if (!invitesQuery) - { - BarGoLink bar(1); - bar.step(); - sLog.outString(); - - if (m_MaxEventId) // An Event was loaded before - { - // delete all events (no event exist without at least one invite) - m_EventStore.clear(); - m_MaxEventId = 0; - CharacterDatabase.DirectExecute("TRUNCATE TABLE calendar_events"); - sLog.outString(">> calendar_invites table is empty, cleared calendar_events table!"); - } - else - sLog.outString(">> calendar_invite table is empty!"); - } - else - { - if (m_MaxEventId) - { - uint64 totalInvites = 0; - uint32 deletedInvites = 0; - BarGoLink bar(invitesQuery->GetRowCount()); - do - { - Field* field = invitesQuery->Fetch(); - - uint64 inviteId = field[0].GetUInt64(); - uint64 eventId = field[1].GetUInt64(); - ObjectGuid inviteeGuid = ObjectGuid(HIGHGUID_PLAYER, field[2].GetUInt32()); - ObjectGuid senderGuid = ObjectGuid(HIGHGUID_PLAYER, field[3].GetUInt32()); - CalendarInviteStatus status = CalendarInviteStatus(field[4].GetUInt8()); - time_t lastUpdateTime = time_t(field[5].GetUInt32()); - CalendarModerationRank rank = CalendarModerationRank(field[6].GetUInt8()); - - CalendarEvent* event = GetEventById(eventId); - if (!event) - { - // delete invite - CharacterDatabase.PExecute("DELETE FROM calendar_invites WHERE inviteId =" UI64FMTD, field[0].GetUInt64()); - ++deletedInvites; - continue; - } - - CalendarInvite* invite = new CalendarInvite(event, inviteId, senderGuid, inviteeGuid, lastUpdateTime, status, rank, ""); - event->AddInvite(invite); - ++totalInvites; - m_MaxInviteId = std::max(inviteId, m_MaxInviteId); - } - while (invitesQuery->NextRow()); - sLog.outString(); - sLog.outString(">> Loaded " UI64FMTD " invites! %s", totalInvites, (deletedInvites != 0) ? "(deleted some invites without corresponding event!)" : ""); - } - else - { - // delete all invites (no invites exist without events) - CharacterDatabase.DirectExecute("TRUNCATE TABLE calendar_invites"); - sLog.outString(">> calendar_invites table is cleared! (invites without events found)"); - } - delete invitesQuery; - } - sLog.outString(); -} - -// check if player have not reached event limit -bool CalendarMgr::CanAddEvent(ObjectGuid const& guid) -{ - uint32 totalEvents = 0; - // count all event created by guid - for (CalendarEventStore::iterator itr = m_EventStore.begin(); itr != m_EventStore.end(); ++itr) - if ((itr->second.CreatorGuid == guid) && (++totalEvents >= CALENDAR_MAX_EVENTS)) - return false; - return true; -} - -// check if guild have not reached event limit -bool CalendarMgr::CanAddGuildEvent(uint32 guildId) -{ - if (!guildId) - return false; - - uint32 totalEvents = 0; - // count all guild events in a guild - for (CalendarEventStore::iterator itr = m_EventStore.begin(); itr != m_EventStore.end(); ++itr) - if ((itr->second.GuildId == guildId) && (++totalEvents >= CALENDAR_MAX_GUILD_EVENTS)) - return false; - return true; -} - -// check if an invitee have not reached invite limit -bool CalendarMgr::CanAddInviteTo(ObjectGuid const& guid) -{ - uint32 totalInvites = 0; - - for (CalendarEventStore::iterator itr = m_EventStore.begin(); itr != m_EventStore.end(); ++itr) - { - CalendarEvent* event = &itr->second; - - if (event->IsGuildAnnouncement()) - continue; - - CalendarInviteMap const* cInvMap = event->GetInviteMap(); - CalendarInviteMap::const_iterator ci_itr = cInvMap->begin(); - while (ci_itr != cInvMap->end()) - { - if ((ci_itr->second->InviteeGuid == guid) && (++totalInvites >= CALENDAR_MAX_INVITES)) - return false; - ++ci_itr; - } - } - - return true; -} diff --git a/src/game/CalendarHandler.cpp b/src/game/CalendarHandler.cpp deleted file mode 100644 index 8ef85ac21..000000000 --- a/src/game/CalendarHandler.cpp +++ /dev/null @@ -1,1044 +0,0 @@ -/* - * This code is part of MaNGOS. Contributor & Copyright details are in AUTHORS/THANKS. - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Common.h" -#include "Log.h" -#include "Player.h" -#include "WorldPacket.h" -#include "WorldSession.h" -#include "Opcodes.h" -#include "MapPersistentStateMgr.h" -#include "Calendar.h" -#include "ObjectMgr.h" -#include "SocialMgr.h" -#include "World.h" -#include "Guild.h" -#include "GuildMgr.h" -#include "ArenaTeam.h" - -void WorldSession::HandleCalendarGetCalendar(WorldPacket& /*recv_data*/) -{ - ObjectGuid guid = _player->GetObjectGuid(); - DEBUG_LOG("WORLD: Received opcode CMSG_CALENDAR_GET_CALENDAR [%s]", guid.GetString().c_str()); - - time_t currTime = time(NULL); - - WorldPacket data(SMSG_CALENDAR_SEND_CALENDAR); - - CalendarInvitesList invites; - sCalendarMgr.GetPlayerInvitesList(guid, invites); - - data << uint32(invites.size()); - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "Sending > %u invites", invites.size()); - - for (CalendarInvitesList::const_iterator itr = invites.begin(); itr != invites.end(); ++itr) - { - CalendarEvent const* event = (*itr)->GetCalendarEvent(); - MANGOS_ASSERT(event); // TODO: be sure no way to have a null event - - data << uint64(event->EventId); - data << uint64((*itr)->InviteId); - data << uint8((*itr)->Status); - data << uint8((*itr)->Rank); - - data << uint8(event->IsGuildEvent()); - data << event->CreatorGuid.WriteAsPacked(); - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "invite> EventId[" UI64FMTD "], InviteId[" UI64FMTD "], status[%u], rank[%u]", - event->EventId, (*itr)->InviteId, uint32((*itr)->Status), uint32((*itr)->Rank)); - } - - CalendarEventsList events; - sCalendarMgr.GetPlayerEventsList(guid, events); - - data << uint32(events.size()); - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "Sending > %u events", events.size()); - - for (CalendarEventsList::const_iterator itr = events.begin(); itr != events.end(); ++itr) - { - CalendarEvent const* event = *itr; - - data << uint64(event->EventId); - data << event->Title; - data << uint32(event->Type); - data << secsToTimeBitFields(event->EventTime); - data << uint32(event->Flags); - data << int32(event->DungeonId); - data << event->CreatorGuid.WriteAsPacked(); - - std::string timeStr = TimeToTimestampStr(event->EventTime); - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "Events> EventId[" UI64FMTD "], Title[%s], Time[%s], Type[%u], Flag[%u], DungeonId[%d], CreatorGuid[%s]", - event->EventId, event->Title.c_str(), timeStr.c_str(), uint32(event->Type), - uint32(event->Flags), event->DungeonId, event->CreatorGuid.GetString().c_str()); - } - - data << uint32(currTime); // server time - data << secsToTimeBitFields(currTime); // zone time ?? - - ByteBuffer dataBuffer; - uint32 boundCounter = 0; - for (uint8 i = 0; i < MAX_DIFFICULTY; ++i) - { - Player::BoundInstancesMap boundInstances = _player->GetBoundInstances(Difficulty(i)); - for (Player::BoundInstancesMap::const_iterator itr = boundInstances.begin(); itr != boundInstances.end(); ++itr) - { - if (itr->second.perm) - { - DungeonPersistentState const* state = itr->second.state; - dataBuffer << uint32(state->GetMapId()); - dataBuffer << uint32(state->GetDifficulty()); - dataBuffer << uint32(state->GetResetTime() - currTime); - dataBuffer << uint64(state->GetInstanceId()); // instance save id as unique instance copy id - ++boundCounter; - } - } - } - - data << uint32(boundCounter); - data.append(dataBuffer); - - data << uint32(1135753200); // Constant date, unk (28.12.2005 07:00) - - // Reuse variables - boundCounter = 0; - std::set sentMaps; - dataBuffer.clear(); - - for (MapDifficultyMap::const_iterator itr = sMapDifficultyMap.begin(); itr != sMapDifficultyMap.end(); ++itr) - { - uint32 map_diff_pair = itr->first; - uint32 mapId = PAIR32_LOPART(map_diff_pair); - Difficulty difficulty = Difficulty(PAIR32_HIPART(map_diff_pair)); - MapDifficultyEntry const* mapDiff = itr->second; - - // skip mapDiff without global reset time - if (!mapDiff->resetTime) - continue; - - // skip non raid map - MapEntry const* mapEntry = sMapStore.LookupEntry(mapId); - if (!mapEntry || !mapEntry->IsRaid()) - continue; - - // skip already sent map (not same difficulty?) - if (sentMaps.find(mapId) != sentMaps.end()) - continue; - - uint32 resetTime = sMapPersistentStateMgr.GetScheduler().GetMaxResetTimeFor(mapDiff); - - sentMaps.insert(mapId); - dataBuffer << mapId; - dataBuffer << resetTime; - - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "MapId [%u] -> Reset Time: %u", mapId, resetTime); - dataBuffer << int32(0); // showed 68400 on map 509 must investigate more - ++boundCounter; - } - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "Map sent [%u]", 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& recv_data) -{ - ObjectGuid guid = _player->GetObjectGuid(); - DEBUG_LOG("WORLD: Received opcode CMSG_CALENDAR_GET_EVENT [%s]", guid.GetString().c_str()); - - uint64 eventId; - recv_data >> eventId; - - if (CalendarEvent* event = sCalendarMgr.GetEventById(eventId)) - sCalendarMgr.SendCalendarEvent(_player, event, CALENDAR_SENDTYPE_GET); - else - sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_EVENT_INVALID); -} - -void WorldSession::HandleCalendarGuildFilter(WorldPacket& recv_data) -{ - DEBUG_LOG("WORLD: Received opcode CMSG_CALENDAR_GUILD_FILTER [%s]", _player->GetGuidStr().c_str()); - - uint32 minLevel; - uint32 maxLevel; - uint32 minRank; - - recv_data >> minLevel >> maxLevel >> minRank; - - if (Guild* guild = sGuildMgr.GetGuildById(_player->GetGuildId())) - guild->MassInviteToEvent(this, minLevel, maxLevel, minRank); - - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "Min level [%u], Max level [%u], Min rank [%u]", minLevel, maxLevel, minRank); -} - -void WorldSession::HandleCalendarEventSignup(WorldPacket& recv_data) -{ - ObjectGuid guid = _player->GetObjectGuid(); - DEBUG_LOG("WORLD: Received opcode CMSG_CALENDAR_EVENT_SIGNUP [%s]", guid.GetString().c_str()); - - uint64 eventId; - bool tentative; - - recv_data >> eventId; - recv_data >> tentative; // uint8 == bool size in all compilator??? - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "EventId [" UI64FMTD "] Tentative %u", eventId, uint32(tentative)); - - if (CalendarEvent* event = sCalendarMgr.GetEventById(eventId)) - { - if (event->IsGuildEvent() && event->GuildId != _player->GetGuildId()) - { - sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_GUILD_PLAYER_NOT_IN_GUILD); - return; - } - - CalendarInviteStatus status = tentative ? CALENDAR_STATUS_TENTATIVE : CALENDAR_STATUS_SIGNED_UP; - sCalendarMgr.AddInvite(event, guid, guid, CalendarInviteStatus(status), CALENDAR_RANK_PLAYER, "", time(NULL)); - sCalendarMgr.SendCalendarClearPendingAction(_player); - } - else - sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_EVENT_INVALID); -} - -void WorldSession::HandleCalendarArenaTeam(WorldPacket& recv_data) -{ - DEBUG_LOG("WORLD: Received opcode CMSG_CALENDAR_ARENA_TEAM [%s]", _player->GetGuidStr().c_str()); - uint32 areanTeamId; - recv_data >> areanTeamId; - - if (ArenaTeam* team = sObjectMgr.GetArenaTeamById(areanTeamId)) - team->MassInviteToEvent(this); -} - -void WorldSession::HandleCalendarAddEvent(WorldPacket& recv_data) -{ - ObjectGuid guid = _player->GetObjectGuid(); - DEBUG_LOG("WORLD: Received opcode CMSG_CALENDAR_ADD_EVENT [%s]", guid.GetString().c_str()); - - std::string title; - std::string description; - uint8 type; - uint8 repeatable; - uint32 maxInvites; - int32 dungeonId; - uint32 eventPackedTime; - uint32 unkPackedTime; - uint32 flags; - - recv_data >> title; - recv_data >> description; - recv_data >> type; - recv_data >> repeatable; - recv_data >> maxInvites; - recv_data >> dungeonId; - recv_data >> eventPackedTime; - recv_data >> unkPackedTime; - recv_data >> flags; - - // 946684800 is 01/01/2000 00:00:00 - default response time - CalendarEvent* cal = sCalendarMgr.AddEvent(_player->GetObjectGuid(), title, description, type, repeatable, maxInvites, dungeonId, timeBitFieldsToSecs(eventPackedTime), timeBitFieldsToSecs(unkPackedTime), flags); - - if (cal) - { - if (cal->IsGuildAnnouncement()) - { - sCalendarMgr.AddInvite(cal, guid, ObjectGuid(uint64(0)), CALENDAR_STATUS_NOT_SIGNED_UP, CALENDAR_RANK_PLAYER, "", time(NULL)); - } - else - { - uint32 inviteCount; - recv_data >> inviteCount; - - for (uint32 i = 0; i < inviteCount; ++i) - { - ObjectGuid invitee; - uint8 status = 0; - uint8 rank = 0; - recv_data >> invitee.ReadAsPacked(); - recv_data >> status; - recv_data >> rank; - - sCalendarMgr.AddInvite(cal, guid, invitee, CalendarInviteStatus(status), CalendarModerationRank(rank), "", time(NULL)); - } - } - sCalendarMgr.SendCalendarEvent(_player, cal, CALENDAR_SENDTYPE_ADD); - } -} - -void WorldSession::HandleCalendarUpdateEvent(WorldPacket& recv_data) -{ - ObjectGuid guid = _player->GetObjectGuid(); - DEBUG_LOG("WORLD: Received opcode CMSG_CALENDAR_UPDATE_EVENT [%s]", guid.GetString().c_str()); - - time_t oldEventTime; - uint64 eventId; - uint64 inviteId; - std::string title; - std::string description; - uint8 type; - uint8 repetitionType; - uint32 maxInvites; - int32 dungeonId; - uint32 eventPackedTime; - uint32 UnknownPackedTime; - uint32 flags; - - recv_data >> eventId >> inviteId >> title >> description >> type >> repetitionType >> maxInvites >> dungeonId; - recv_data >> eventPackedTime; - recv_data >> UnknownPackedTime; - recv_data >> flags; - - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "EventId [" UI64FMTD "], InviteId [" UI64FMTD "] Title %s, Description %s, type %u " - "Repeatable %u, MaxInvites %u, Dungeon ID %d, Flags %u", eventId, inviteId, title.c_str(), - description.c_str(), uint32(type), uint32(repetitionType), maxInvites, dungeonId, flags); - - if (CalendarEvent* event = sCalendarMgr.GetEventById(eventId)) - { - if (guid != event->CreatorGuid) - { - CalendarInvite* updaterInvite = event->GetInviteByGuid(guid); - if (updaterInvite == NULL) - { - sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_NOT_INVITED); - return ; - } - - if (updaterInvite->Rank != CALENDAR_RANK_MODERATOR) - { - // remover have not enough right to change invite status - sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_PERMISSIONS); - return; - } - } - - oldEventTime = event->EventTime; - - event->Type = CalendarEventType(type); - event->Flags = flags; - event->EventTime = timeBitFieldsToSecs(eventPackedTime); - event->UnknownTime = timeBitFieldsToSecs(UnknownPackedTime); - event->DungeonId = dungeonId; - event->Title = title; - event->Description = description; - - sCalendarMgr.SendCalendarEventUpdateAlert(event, oldEventTime); - - // query construction - CharacterDatabase.escape_string(title); - CharacterDatabase.escape_string(description); - CharacterDatabase.PExecute("UPDATE calendar_events SET " - "type=%hu, flags=%u, dungeonId=%d, eventTime=%u, title='%s', description='%s'" - "WHERE eventid=" UI64FMTD, - type, flags, dungeonId, event->EventTime, title.c_str(), description.c_str(), eventId); - } - else - sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_EVENT_INVALID); -} - -void WorldSession::HandleCalendarRemoveEvent(WorldPacket& recv_data) -{ - DEBUG_LOG("WORLD: Received opcode CMSG_CALENDAR_REMOVE_EVENT [%s]", _player->GetGuidStr().c_str()); - - uint64 eventId; - uint64 inviteId; - uint32 Flags; - - recv_data >> eventId; - recv_data >> inviteId; - recv_data >> Flags; - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "Remove event (eventId=" UI64FMTD ", remover inviteId =" UI64FMTD ")", eventId, inviteId); - sCalendarMgr.RemoveEvent(eventId, _player); -} - -void WorldSession::HandleCalendarCopyEvent(WorldPacket& recv_data) -{ - ObjectGuid guid = _player->GetObjectGuid(); - DEBUG_LOG("WORLD: Received opcode CMSG_CALENDAR_COPY_EVENT [%s]", guid.GetString().c_str()); - - uint64 eventId; - uint64 inviteId; - uint32 packedTime; - - recv_data >> eventId >> inviteId; - recv_data >> packedTime; - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "EventId [" UI64FMTD "] inviteId [" UI64FMTD "]", - eventId, inviteId); - - sCalendarMgr.CopyEvent(eventId, timeBitFieldsToSecs(packedTime), guid); -} - -void WorldSession::HandleCalendarEventInvite(WorldPacket& recv_data) -{ - ObjectGuid playerGuid = _player->GetObjectGuid(); - DEBUG_LOG("WORLD: Received opcode CMSG_CALENDAR_EVENT_INVITE [%s]", playerGuid.GetString().c_str()); - - uint64 eventId; - - // TODO it seem its not inviteID but event->CreatorGuid - uint64 inviteId; - std::string name; - bool isPreInvite; - bool isGuildEvent; - - ObjectGuid inviteeGuid; - uint32 inviteeTeam = 0; - uint32 inviteeGuildId = 0; - bool isIgnored = false; - - recv_data >> eventId >> inviteId >> name >> isPreInvite >> isGuildEvent; - - if (Player* player = sObjectAccessor.FindPlayerByName(name.c_str())) - { - // Invitee is online - inviteeGuid = player->GetObjectGuid(); - inviteeTeam = player->GetTeam(); - inviteeGuildId = player->GetGuildId(); - if (player->GetSocial()->HasIgnore(playerGuid)) - isIgnored = true; - } - else - { - // Invitee offline, get data from database - CharacterDatabase.escape_string(name); - QueryResult* result = CharacterDatabase.PQuery("SELECT guid,race FROM characters WHERE name ='%s'", name.c_str()); - if (result) - { - Field* fields = result->Fetch(); - inviteeGuid = ObjectGuid(HIGHGUID_PLAYER, fields[0].GetUInt32()); - inviteeTeam = Player::TeamForRace(fields[1].GetUInt8()); - inviteeGuildId = Player::GetGuildIdFromDB(inviteeGuid); - delete result; - - result = CharacterDatabase.PQuery("SELECT flags FROM character_social WHERE guid = %u AND friend = %u", inviteeGuid.GetCounter(), playerGuid.GetCounter()); - if (result) - { - Field* fields = result->Fetch(); - if (fields[0].GetUInt8() & SOCIAL_FLAG_IGNORED) - isIgnored = true; - delete result; - } - } - } - - if (inviteeGuid.IsEmpty()) - { - sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_PLAYER_NOT_FOUND); - return; - } - - if (isIgnored) - { - sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_IGNORING_YOU_S, name.c_str()); - return; - } - - if (_player->GetTeam() != inviteeTeam && !sWorld.getConfig(CONFIG_BOOL_ALLOW_TWO_SIDE_INTERACTION_CALENDAR)) - { - sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_NOT_ALLIED); - return; - } - - if (!isPreInvite) - { - if (CalendarEvent* event = sCalendarMgr.GetEventById(eventId)) - { - if (event->IsGuildEvent() && event->GuildId == inviteeGuildId) - { - // we can't invite guild members to guild events - sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_NO_GUILD_INVITES); - return; - } - - sCalendarMgr.AddInvite(event, playerGuid, inviteeGuid, CALENDAR_STATUS_INVITED, CALENDAR_RANK_PLAYER, "", time(NULL)); - } - else - sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_EVENT_INVALID); - } - else - { - if (isGuildEvent && inviteeGuildId == _player->GetGuildId()) - { - sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_NO_GUILD_INVITES); - return; - } - - // create a temp invite to send it back to client - CalendarInvite invite; - invite.SenderGuid = playerGuid; - invite.InviteeGuid = inviteeGuid; - invite.Status = CALENDAR_STATUS_INVITED; - invite.Rank = CALENDAR_RANK_PLAYER; - invite.LastUpdateTime = time(NULL); - - sCalendarMgr.SendCalendarEventInvite(&invite); - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "PREINVITE> sender[%s], Invitee[%u]", playerGuid.GetString().c_str(), inviteeGuid.GetString().c_str()); - } -} - -void WorldSession::HandleCalendarEventRsvp(WorldPacket& recv_data) -{ - ObjectGuid guid = _player->GetObjectGuid(); - DEBUG_LOG("WORLD: Received opcode CMSG_CALENDAR_EVENT_RSVP [%s]", guid.GetString().c_str()); - - uint64 eventId; - uint64 inviteId; - uint32 status; - - recv_data >> eventId >> inviteId >> status; - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "EventId [" UI64FMTD "], InviteId [" UI64FMTD "], status %u", - eventId, inviteId, status); - - if (CalendarEvent* event = sCalendarMgr.GetEventById(eventId)) - { - // i think we still should be able to remove self from locked events - if (status != CALENDAR_STATUS_REMOVED && event->Flags & CALENDAR_FLAG_INVITES_LOCKED) - { - sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_EVENT_LOCKED); - return; - } - - if (CalendarInvite* invite = event->GetInviteById(inviteId)) - { - if (invite->InviteeGuid != guid) - { - CalendarInvite* updaterInvite = event->GetInviteByGuid(guid); - if (updaterInvite == NULL) - { - sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_NOT_INVITED); - return ; - } - - if (updaterInvite->Rank != CALENDAR_RANK_MODERATOR && updaterInvite->Rank != CALENDAR_RANK_OWNER) - { - // remover have not enough right to change invite status - sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_PERMISSIONS); - return; - } - } - invite->Status = CalendarInviteStatus(status); - invite->LastUpdateTime = time(NULL); - - CharacterDatabase.PExecute("UPDATE calendar_invites SET status=%u, lastUpdateTime=%u WHERE inviteId = " UI64FMTD, status, uint32(invite->LastUpdateTime), invite->InviteId); - sCalendarMgr.SendCalendarEventStatus(invite); - sCalendarMgr.SendCalendarClearPendingAction(_player); - } - else - sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_NO_INVITE); // correct? - } - else - sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_EVENT_INVALID); -} - -void WorldSession::HandleCalendarEventRemoveInvite(WorldPacket& recv_data) -{ - ObjectGuid guid = _player->GetObjectGuid(); - DEBUG_LOG("WORLD: Received opcode CMSG_CALENDAR_EVENT_REMOVE_INVITE [%s]", guid.GetString().c_str()); - - ObjectGuid invitee; - uint64 eventId; - uint64 ownerInviteId; // isn't it sender's inviteId? TODO: need more test to see what we can do with that value - uint64 inviteId; - - recv_data >> invitee.ReadAsPacked(); - recv_data >> inviteId >> ownerInviteId >> eventId; - - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "EventId [" UI64FMTD "], ownerInviteId [" UI64FMTD "], Invitee ([%s] id: [" UI64FMTD "])", - eventId, ownerInviteId, invitee.GetString().c_str(), inviteId); - - if (CalendarEvent* event = sCalendarMgr.GetEventById(eventId)) - sCalendarMgr.RemoveInvite(eventId, inviteId, guid); - else - sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_EVENT_INVALID); -} - -void WorldSession::HandleCalendarEventStatus(WorldPacket& recv_data) -{ - ObjectGuid updaterGuid = _player->GetObjectGuid(); - DEBUG_LOG("WORLD: Received opcode CMSG_CALENDAR_EVENT_STATUS [%s]", updaterGuid.GetString().c_str()); - recv_data.hexlike(); - - ObjectGuid invitee; - uint64 eventId; - uint64 inviteId; - uint64 ownerInviteId; // isn't it sender's inviteId? - uint32 status; - - recv_data >> invitee.ReadAsPacked(); - recv_data >> eventId >> inviteId >> ownerInviteId >> status; - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "EventId [" UI64FMTD "] ownerInviteId [" UI64FMTD "], Invitee ([%s] id: [" UI64FMTD "], status %u", - eventId, ownerInviteId, invitee.GetString().c_str(), inviteId, status); - - if (CalendarEvent* event = sCalendarMgr.GetEventById(eventId)) - { - if (CalendarInvite* invite = event->GetInviteById(inviteId)) - { - if (invite->InviteeGuid != updaterGuid) - { - CalendarInvite* updaterInvite = event->GetInviteByGuid(updaterGuid); - if (updaterInvite == NULL) - { - sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_NOT_INVITED); - return ; - } - - if (updaterInvite->Rank != CALENDAR_RANK_MODERATOR && updaterInvite->Rank != CALENDAR_RANK_OWNER) - { - // remover have not enough right to change invite status - sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_PERMISSIONS); - return; - } - } - invite->Status = (CalendarInviteStatus)status; - invite->LastUpdateTime = time(NULL); // not sure if we should set response time when moderator changes invite status - - CharacterDatabase.PExecute("UPDATE calendar_invites SET status=%u, lastUpdateTime=%u WHERE inviteId=" UI64FMTD, status, uint32(invite->LastUpdateTime), invite->InviteId); - sCalendarMgr.SendCalendarEventStatus(invite); - sCalendarMgr.SendCalendarClearPendingAction(sObjectMgr.GetPlayer(invitee)); - } - else - sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_NO_INVITE); - } - else - sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_EVENT_INVALID); -} - -void WorldSession::HandleCalendarEventModeratorStatus(WorldPacket& recv_data) -{ - ObjectGuid guid = _player->GetObjectGuid(); - DEBUG_LOG("WORLD: Received opcode CMSG_CALENDAR_EVENT_MODERATOR_STATUS [%s]", guid.GetString().c_str()); - - ObjectGuid invitee; - uint64 eventId; - uint64 inviteId; - uint64 ownerInviteId; // isn't it sender's inviteId? - uint32 rank; - - recv_data >> invitee.ReadAsPacked(); - recv_data >> eventId >> inviteId >> ownerInviteId >> rank; - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "EventId [" UI64FMTD "] ownerInviteId [" UI64FMTD "], Invitee ([%s] id: [" UI64FMTD "], rank %u", - eventId, ownerInviteId, invitee.GetString().c_str(), inviteId, rank); - - if (CalendarEvent* event = sCalendarMgr.GetEventById(eventId)) - { - if (CalendarInvite* invite = event->GetInviteById(inviteId)) - { - if (invite->InviteeGuid != guid) - { - CalendarInvite* updaterInvite = event->GetInviteByGuid(guid); - if (updaterInvite == NULL) - { - sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_NOT_INVITED); - return ; - } - if (updaterInvite->Rank != CALENDAR_RANK_MODERATOR && updaterInvite->Rank != CALENDAR_RANK_OWNER) - { - // remover have not enough right to change invite status - sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_PERMISSIONS); - return; - } - } - - if (CalendarModerationRank(rank) == CALENDAR_RANK_OWNER) - { - // cannot set owner - sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_PERMISSIONS); - return; - } - - CharacterDatabase.PExecute("UPDATE calendar_invites SET rank = %u WHERE inviteId=" UI64FMTD, rank, invite->InviteId); - invite->Rank = CalendarModerationRank(rank); - sCalendarMgr.SendCalendarEventModeratorStatusAlert(invite); - } - else - sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_NO_INVITE); - } - else - sCalendarMgr.SendCalendarCommandResult(_player, CALENDAR_ERROR_EVENT_INVALID); -} - -void WorldSession::HandleCalendarComplain(WorldPacket& recv_data) -{ - ObjectGuid guid = _player->GetObjectGuid(); - DEBUG_LOG("WORLD: Received opcode CMSG_CALENDAR_COMPLAIN [%s]", guid.GetString().c_str()); - - ObjectGuid badGuyGuid; - uint64 eventId; - uint64 inviteId; - - recv_data >> badGuyGuid; - recv_data >> eventId >> inviteId; - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "EventId [" UI64FMTD "], BadGuyGuid ([%s] inviteId: [" UI64FMTD "])", - eventId, badGuyGuid.GetString().c_str(), inviteId); - - // Remove the invite - if (sCalendarMgr.RemoveInvite(eventId, inviteId, guid)) - { - WorldPacket data(SMSG_COMPLAIN_RESULT, 1 + 1); - data << uint8(0); - data << uint8(0); // show complain saved. We can send 0x0C to show windows with ok button - SendPacket(&data); - } -} - -void WorldSession::HandleCalendarGetNumPending(WorldPacket& /*recv_data*/) -{ - ObjectGuid guid = _player->GetObjectGuid(); - DEBUG_LOG("WORLD: Received opcode CMSG_CALENDAR_GET_NUM_PENDING [%s]", guid.GetString().c_str()); - - uint32 pending = sCalendarMgr.GetPlayerNumPending(guid); - - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "Pending: %u", pending); - - WorldPacket data(SMSG_CALENDAR_SEND_NUM_PENDING, 4); - data << uint32(pending); - SendPacket(&data); -} - -////////////////////////////////////////////////////////////////////////// -// Send function -////////////////////////////////////////////////////////////////////////// - -void CalendarMgr::SendCalendarEventInviteAlert(CalendarInvite const* invite) -{ - DEBUG_LOG("WORLD: SMSG_CALENDAR_EVENT_INVITE_ALERT"); - - CalendarEvent const* event = invite->GetCalendarEvent(); - if (!event) - return; - - WorldPacket data(SMSG_CALENDAR_EVENT_INVITE_ALERT); - data << uint64(event->EventId); - data << event->Title; - data << secsToTimeBitFields(event->EventTime); - data << uint32(event->Flags); - data << uint32(event->Type); - data << int32(event->DungeonId); - data << uint64(invite->InviteId); - data << uint8(invite->Status); - data << uint8(invite->Rank); - data << event->CreatorGuid.WriteAsPacked(); - data << invite->SenderGuid.WriteAsPacked(); - //data.hexlike(); - - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "SendCalendarInviteAlert> senderGuid[%s], inviteeGuid[%s], EventId[" UI64FMTD "], Status[%u], InviteId[" UI64FMTD "]", - invite->SenderGuid.GetString().c_str(), invite->InviteeGuid.GetString().c_str(), event->EventId, uint32(invite->Status), invite->InviteId); - - if (event->IsGuildEvent() || event->IsGuildAnnouncement()) - { - if (Guild* guild = sGuildMgr.GetGuildById(event->GuildId)) - guild->BroadcastPacket(&data); - } - else if (Player* player = sObjectMgr.GetPlayer(invite->InviteeGuid)) - player->SendDirectMessage(&data); -} - -void CalendarMgr::SendCalendarEventInvite(CalendarInvite const* invite) -{ - CalendarEvent const* event = invite->GetCalendarEvent(); - - time_t statusTime = invite->LastUpdateTime; - bool preInvite = true; - uint64 eventId = 0; - if (event != NULL) - { - preInvite = false; - eventId = event->EventId; - } - - Player* player = sObjectMgr.GetPlayer(invite->InviteeGuid); - - uint8 level = player ? player->getLevel() : Player::GetLevelFromDB(invite->InviteeGuid); - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "SMSG_CALENDAR_EVENT_INVITE"); - WorldPacket data(SMSG_CALENDAR_EVENT_INVITE, 8 + 8 + 8 + 1 + 1 + 1 + (preInvite ? 0 : 4) + 1); - data << invite->InviteeGuid.WriteAsPacked(); - data << uint64(eventId); - data << uint64(invite->InviteId); - data << uint8(level); - data << uint8(invite->Status); - data << uint8(!preInvite); - if (!preInvite) - data << secsToTimeBitFields(statusTime); - data << uint8(invite->SenderGuid != invite->InviteeGuid); // false only if the invite is sign-up (invitee create himself his invite) - - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "SendCalendarInvit> %s senderGuid[%s], inviteeGuid[%s], EventId[" UI64FMTD "], Status[%u], InviteId[" UI64FMTD "]", - preInvite ? "is PreInvite," : "", invite->SenderGuid.GetString().c_str(), invite->InviteeGuid.GetString().c_str(), eventId, uint32(invite->Status), invite->InviteId); - - //data.hexlike(); - if (preInvite) - { - if (Player* sender = sObjectMgr.GetPlayer(invite->SenderGuid)) - sender->SendDirectMessage(&data); - } - else - SendPacketToAllEventRelatives(data, event); -} - -void CalendarMgr::SendCalendarCommandResult(Player* player, CalendarError err, char const* param /*= NULL*/) -{ - if (!player) - return; - - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "SMSG_CALENDAR_COMMAND_RESULT (%u)", err); - WorldPacket data(SMSG_CALENDAR_COMMAND_RESULT, 0); - data << uint32(0); - data << uint8(0); - switch (err) - { - case CALENDAR_ERROR_OTHER_INVITES_EXCEEDED_S: - case CALENDAR_ERROR_ALREADY_INVITED_TO_EVENT_S: - case CALENDAR_ERROR_IGNORING_YOU_S: - data << param; - break; - default: - data << uint8(0); - break; - } - - data << uint32(err); - //data.hexlike(); - player->SendDirectMessage(&data); -} - -void CalendarMgr::SendCalendarEventRemovedAlert(CalendarEvent const* event) -{ - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "SMSG_CALENDAR_EVENT_REMOVED_ALERT"); - WorldPacket data(SMSG_CALENDAR_EVENT_REMOVED_ALERT, 1 + 8 + 1); - data << uint8(1); // show pending alert? - data << uint64(event->EventId); - data << secsToTimeBitFields(event->EventTime); - //data.hexlike(); - SendPacketToAllEventRelatives(data, event); -} - -void CalendarMgr::SendCalendarEvent(Player* player, CalendarEvent const* event, uint32 sendType) -{ - if (!player || !event) - return; - - std::string timeStr = TimeToTimestampStr(event->EventTime); - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "SendCalendarEvent> sendType[%u], CreatorGuid[%s], EventId[" UI64FMTD "], Type[%u], Flags[%u], Title[%s]", - sendType, event->CreatorGuid.GetString().c_str(), event->EventId, uint32(event->Type), event->Flags, event->Title.c_str()); - - WorldPacket data(SMSG_CALENDAR_SEND_EVENT); - data << uint8(sendType); - data << event->CreatorGuid.WriteAsPacked(); - data << uint64(event->EventId); - data << event->Title; - data << event->Description; - data << uint8(event->Type); - data << uint8(event->Repeatable); - data << uint32(CALENDAR_MAX_INVITES); - data << event->DungeonId; - data << event->Flags; - data << secsToTimeBitFields(event->EventTime); - data << secsToTimeBitFields(event->UnknownTime); - data << event->GuildId; - - CalendarInviteMap const* cInvMap = event->GetInviteMap(); - data << (uint32)cInvMap->size(); - for (CalendarInviteMap::const_iterator itr = cInvMap->begin(); itr != cInvMap->end(); ++itr) - { - CalendarInvite const* invite = itr->second; - ObjectGuid inviteeGuid = invite->InviteeGuid; - Player* invitee = sObjectMgr.GetPlayer(inviteeGuid); - - uint8 inviteeLevel = invitee ? invitee->getLevel() : Player::GetLevelFromDB(inviteeGuid); - uint32 inviteeGuildId = invitee ? invitee->GetGuildId() : Player::GetGuildIdFromDB(inviteeGuid); - - data << inviteeGuid.WriteAsPacked(); - data << uint8(inviteeLevel); - data << uint8(invite->Status); - data << uint8(invite->Rank); - data << uint8(event->IsGuildEvent() && event->GuildId == inviteeGuildId); - data << uint64(itr->first); - data << secsToTimeBitFields(invite->LastUpdateTime); - data << invite->Text; - - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "Invite> InviteId[" UI64FMTD "], InviteLvl[%u], Status[%u], Rank[%u], GuildEvent[%s], Text[%s]", - invite->InviteId, uint32(inviteeLevel), uint32(invite->Status), uint32(invite->Rank), - (event->IsGuildEvent() && event->GuildId == inviteeGuildId) ? "true" : "false", invite->Text.c_str()); - } - //data.hexlike(); - player->SendDirectMessage(&data); -} - -void CalendarMgr::SendCalendarEventInviteRemove(CalendarInvite const* invite, uint32 flags) -{ - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "SMSG_CALENDAR_EVENT_INVITE_REMOVED"); - - CalendarEvent const* event = invite->GetCalendarEvent(); - - WorldPacket data(SMSG_CALENDAR_EVENT_INVITE_REMOVED, 8 + 4 + 4 + 1); - data.appendPackGUID(invite->InviteeGuid); - data << uint64(event->EventId); - data << uint32(flags); - data << uint8(1); // show pending alert? - //data.hexlike(); - SendPacketToAllEventRelatives(data, event); -} - -void CalendarMgr::SendCalendarEventInviteRemoveAlert(Player* player, CalendarEvent const* event, CalendarInviteStatus status) -{ - if (player) - { - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "SMSG_CALENDAR_EVENT_INVITE_REMOVED_ALERT"); - WorldPacket data(SMSG_CALENDAR_EVENT_INVITE_REMOVED_ALERT, 8 + 4 + 4 + 1); - data << uint64(event->EventId); - data << secsToTimeBitFields(event->EventTime); - data << uint32(event->Flags); - data << uint8(status); - //data.hexlike(); - player->SendDirectMessage(&data); - } -} - -void CalendarMgr::SendCalendarEventStatus(CalendarInvite const* invite) -{ - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "SMSG_CALENDAR_EVENT_STATUS"); - WorldPacket data(SMSG_CALENDAR_EVENT_STATUS, 8 + 8 + 4 + 4 + 1 + 1 + 4); - CalendarEvent const* event = invite->GetCalendarEvent(); - - data << invite->InviteeGuid.WriteAsPacked(); - data << uint64(event->EventId); - data << secsToTimeBitFields(event->EventTime); - data << uint32(event->Flags); - data << uint8(invite->Status); - data << uint8(invite->Rank); - data << secsToTimeBitFields(invite->LastUpdateTime); - //data.hexlike(); - SendPacketToAllEventRelatives(data, event); -} - -void CalendarMgr::SendCalendarClearPendingAction(Player* player) -{ - if (player) - { - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "SMSG_CALENDAR_CLEAR_PENDING_ACTION TO [%s]", player->GetObjectGuid().GetString().c_str()); - WorldPacket data(SMSG_CALENDAR_CLEAR_PENDING_ACTION, 0); - player->SendDirectMessage(&data); - } -} - -void CalendarMgr::SendCalendarEventModeratorStatusAlert(CalendarInvite const* invite) -{ - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "SMSG_CALENDAR_EVENT_MODERATOR_STATUS_ALERT"); - CalendarEvent const* event = invite->GetCalendarEvent(); - WorldPacket data(SMSG_CALENDAR_EVENT_MODERATOR_STATUS_ALERT, 8 + 8 + 1 + 1); - data << invite->InviteeGuid.WriteAsPacked(); - data << uint64(event->EventId); - data << uint8(invite->Rank); - data << uint8(1); // Display pending action to client? - //data.hexlike(); - SendPacketToAllEventRelatives(data, event); -} - -void CalendarMgr::SendCalendarEventUpdateAlert(CalendarEvent const* event, time_t oldEventTime) -{ - DEBUG_FILTER_LOG(LOG_FILTER_CALENDAR, "SMSG_CALENDAR_EVENT_UPDATED_ALERT"); - WorldPacket data(SMSG_CALENDAR_EVENT_UPDATED_ALERT, 1 + 8 + 4 + 4 + 4 + 1 + 4 + - event->Title.size() + event->Description.size() + 1 + 4 + 4); - data << uint8(1); // show pending alert? - data << uint64(event->EventId); - data << secsToTimeBitFields(oldEventTime); - data << uint32(event->Flags); - data << secsToTimeBitFields(event->EventTime); - data << uint8(event->Type); - data << int32(event->DungeonId); - data << event->Title; - data << event->Description; - data << uint8(event->Repeatable); - data << uint32(CALENDAR_MAX_INVITES); - data << secsToTimeBitFields(event->UnknownTime); - //data.hexlike(); - - SendPacketToAllEventRelatives(data, event); -} - -void CalendarMgr::SendPacketToAllEventRelatives(WorldPacket packet, CalendarEvent const* event) -{ - // Send packet to all guild members - if (event->IsGuildEvent() || event->IsGuildAnnouncement()) - if (Guild* guild = sGuildMgr.GetGuildById(event->GuildId)) - guild->BroadcastPacket(&packet); - - // Send packet to all invitees if event is non-guild, in other case only to non-guild invitees (packet was broadcasted for them) - CalendarInviteMap const* cInvMap = event->GetInviteMap(); - for (CalendarInviteMap::const_iterator itr = cInvMap->begin(); itr != cInvMap->end(); ++itr) - if (Player* player = sObjectMgr.GetPlayer(itr->second->InviteeGuid)) - if (!event->IsGuildEvent() || (event->IsGuildEvent() && player->GetGuildId() != event->GuildId)) - player->SendDirectMessage(&packet); -} - -void CalendarMgr::SendCalendarRaidLockoutRemove(Player* player, DungeonPersistentState const* save) -{ - if (!save || !player) - return; - - DEBUG_LOG("SMSG_CALENDAR_RAID_LOCKOUT_REMOVED [%s]", player->GetObjectGuid().GetString().c_str()); - time_t currTime = time(NULL); - - WorldPacket data(SMSG_CALENDAR_RAID_LOCKOUT_REMOVED, 4 + 4 + 4 + 8); - data << uint32(save->GetMapId()); - data << uint32(save->GetDifficulty()); - data << uint32(save->GetResetTime() - currTime); - data << uint64(save->GetInstanceId()); - //data.hexlike(); - player->SendDirectMessage(&data); -} - -void CalendarMgr::SendCalendarRaidLockoutAdd(Player* player, DungeonPersistentState const* save) -{ - if (!save || !player) - return; - - DEBUG_LOG("SMSG_CALENDAR_RAID_LOCKOUT_ADDED [%s]", player->GetObjectGuid().GetString().c_str()); - time_t currTime = time(NULL); - - WorldPacket data(SMSG_CALENDAR_RAID_LOCKOUT_ADDED, 4 + 4 + 4 + 4 + 8); - data << secsToTimeBitFields(currTime); - data << uint32(save->GetMapId()); - data << uint32(save->GetDifficulty()); - data << uint32(save->GetResetTime() - currTime); - data << uint64(save->GetInstanceId()); - //data.hexlike(); - player->SendDirectMessage(&data); -} diff --git a/src/game/MailHandler.cpp b/src/game/MailHandler.cpp deleted file mode 100644 index 825c61a75..000000000 --- a/src/game/MailHandler.cpp +++ /dev/null @@ -1,875 +0,0 @@ -/** - * This code is part of MaNGOS. Contributor & Copyright details are in AUTHORS/THANKS. - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/** - * @addtogroup mailing - * @{ - * - * @file MailHandler.cpp - * This file contains handlers for mail opcodes. - * - */ - -#include "Mail.h" -#include "Language.h" -#include "Log.h" -#include "ObjectGuid.h" -#include "ObjectMgr.h" -#include "Item.h" -#include "Player.h" -#include "World.h" -#include "WorldPacket.h" -#include "WorldSession.h" -#include "Opcodes.h" -#include "Chat.h" - -bool WorldSession::CheckMailBox(ObjectGuid guid) -{ - // GM case - if (guid == GetPlayer()->GetObjectGuid()) - { - // command case will return only if player have real access to command - if (!ChatHandler(GetPlayer()).FindCommand("mailbox")) - { - DEBUG_LOG("%s attempt open mailbox in cheating way.", guid.GetString().c_str()); - return false; - } - } - // mailbox case - else if (guid.IsGameObject()) - { - if (!GetPlayer()->GetGameObjectIfCanInteractWith(guid, GAMEOBJECT_TYPE_MAILBOX)) - { - DEBUG_LOG("Mailbox %s not found or %s can't interact with him.", guid.GetString().c_str(), GetPlayer()->GetGuidStr().c_str()); - return false; - } - } - // squire case - else if (guid.IsAnyTypeCreature()) - { - Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE); - if (!creature) - { - DEBUG_LOG("%s not found or %s can't interact with him.", creature->GetGuidStr().c_str(), GetPlayer()->GetGuidStr().c_str()); - return false; - } - - if (!(creature->GetCreatureInfo()->type_flags & CREATURE_TYPEFLAGS_SQUIRE)) - { - DEBUG_LOG("%s not have access to mailbox.", creature->GetGuidStr().c_str()); - return false; - } - - if (creature->GetOwnerGuid() != GetPlayer()->GetObjectGuid()) - { - DEBUG_LOG("%s not owned by %s for access to mailbox.", creature->GetGuidStr().c_str(), GetPlayer()->GetGuidStr().c_str()); - return false; - } - } - else - return false; - - return true; -} - -/* - * Handles the Packet sent by the client when sending a mail. - * - * This methods takes the packet sent by the client and performs the following actions: - * - Checks whether the mail is valid: i.e. can he send the selected items, - * does he have enough money, etc. - * - Creates a MailDraft and adds the needed items, money, cost data. - * - Sends the mail. - * - * Depending on the outcome of the checks performed the player will recieve a different - * MailResponseResult. - * - * @see MailResponseResult - * @see SendMailResult() - * - * @param recv_data the WorldPacket containing the data sent by the client. - */ -void WorldSession::HandleSendMail(WorldPacket& recv_data) -{ - sLog.outError("WORLD: CMSG_SEND_MAIL"); - ObjectGuid mailboxGuid; - uint64 money, COD; - std::string receiver, subject, body; - uint8 receiverLen, subjectLen, bodyLen; - uint32 unk1, unk2; - - recv_data >> unk1; // stationery? - recv_data >> unk2; // 0x00000000 - - recv_data >> COD >> money; // cod and money - - bodyLen = recv_data.ReadBits(12); - subjectLen = recv_data.ReadBits(9); - - uint8 items_count = recv_data.ReadBits(5); // attached items count - if (items_count > MAX_MAIL_ITEMS) // client limit - { - GetPlayer()->SendMailResult(0, MAIL_SEND, MAIL_ERR_TOO_MANY_ATTACHMENTS); - recv_data.rfinish(); // set to end to avoid warnings spam - return; - } - - recv_data.ReadGuidMask<0>(mailboxGuid); - - ObjectGuid itemGuids[MAX_MAIL_ITEMS]; - for (uint8 i = 0; i < items_count; ++i) - recv_data.ReadGuidMask<2, 6, 3, 7, 1, 0, 4, 5>(itemGuids[i]); - - recv_data.ReadGuidMask<3, 4>(mailboxGuid); - - receiverLen = recv_data.ReadBits(7); - - recv_data.ReadGuidMask<2, 6, 1, 7, 5>(mailboxGuid); - - recv_data.ReadGuidBytes<4>(mailboxGuid); - - for (uint8 i = 0; i < items_count; ++i) - { - recv_data.ReadGuidBytes<6, 1, 7, 2>(itemGuids[i]); - recv_data.read_skip(); // item slot in mail, not used - recv_data.ReadGuidBytes<3, 0, 4, 5>(itemGuids[i]); - } - - recv_data.ReadGuidBytes<7, 3, 6, 5>(mailboxGuid); - - subject = recv_data.ReadString(subjectLen); - receiver = recv_data.ReadString(receiverLen); - - recv_data.ReadGuidBytes<2, 0>(mailboxGuid); - - body = recv_data.ReadString(bodyLen); - - recv_data.ReadGuidBytes<1>(mailboxGuid); - - DEBUG_LOG("WORLD: CMSG_SEND_MAIL receiver '%s' subject '%s' body '%s' mailbox " UI64FMTD " money " UI64FMTD " COD " UI64FMTD " unkt1 %u unk2 %u", - receiver.c_str(), subject.c_str(), body.c_str(), mailboxGuid.GetRawValue(), money, COD, unk1, unk2); - // packet read complete, now do check - - if (!CheckMailBox(mailboxGuid)) - return; - - if (receiver.empty()) - return; - - Player* pl = _player; - - ObjectGuid rc; - if (normalizePlayerName(receiver)) - rc = sObjectMgr.GetPlayerGuidByName(receiver); - - if (!rc) - { - DEBUG_LOG("%s is sending mail to %s (GUID: nonexistent!) with subject %s and body %s includes %u items, " UI64FMTD " copper and " UI64FMTD " COD copper with unk1 = %u, unk2 = %u", - pl->GetGuidStr().c_str(), receiver.c_str(), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2); - pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_NOT_FOUND); - return; - } - - DEBUG_LOG("%s is sending mail to %s with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u", - pl->GetGuidStr().c_str(), rc.GetString().c_str(), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2); - - if (pl->GetObjectGuid() == rc) - { - pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANNOT_SEND_TO_SELF); - return; - } - - uint32 cost = items_count ? 30 * items_count : 30; // price hardcoded in client - - uint64 reqmoney = cost + money; - - if (pl->GetMoney() < reqmoney) - { - pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_ENOUGH_MONEY); - return; - } - - Player* receive = sObjectMgr.GetPlayer(rc); - - Team rc_team; - uint8 mails_count = 0; // do not allow to send to one player more than 100 mails - - if (receive) - { - rc_team = receive->GetTeam(); - mails_count = receive->GetMailSize(); - } - else - { - rc_team = sObjectMgr.GetPlayerTeamByGUID(rc); - if (QueryResult* result = CharacterDatabase.PQuery("SELECT COUNT(*) FROM mail WHERE receiver = '%u'", rc.GetCounter())) - { - Field* fields = result->Fetch(); - mails_count = fields[0].GetUInt32(); - delete result; - } - } - - // 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) - { - pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_CAP_REACHED); - return; - } - - // check the receiver's Faction... - if (!sWorld.getConfig(CONFIG_BOOL_ALLOW_TWO_SIDE_INTERACTION_MAIL) && pl->GetTeam() != rc_team && GetSecurity() == SEC_PLAYER) - { - pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_YOUR_TEAM); - return; - } - - uint32 rc_account = receive - ? receive->GetSession()->GetAccountId() - : sObjectMgr.GetPlayerAccountIdByGUID(rc); - - Item* items[MAX_MAIL_ITEMS]; - - for (uint8 i = 0; i < items_count; ++i) - { - if (!itemGuids[i].IsItem()) - { - pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); - return; - } - - Item* item = pl->GetItemByGuid(itemGuids[i]); - - // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail) - if (!item) - { - pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); - return; - } - - if (!item->CanBeTraded(true)) - { - pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); - return; - } - - if (item->IsBoundAccountWide() && item->IsSoulBound() && pl->GetSession()->GetAccountId() != rc_account) - { - pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_ARTEFACTS_ONLY_FOR_OWN_CHARACTERS); - return; - } - - if ((item->GetProto()->Flags & ITEM_FLAG_CONJURED) || item->GetUInt32Value(ITEM_FIELD_DURATION)) - { - pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); - return; - } - - if (COD && item->HasFlag(ITEM_FIELD_FLAGS, ITEM_DYNFLAG_WRAPPED)) - { - pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD); - return; - } - - items[i] = item; - } - - pl->SendMailResult(0, MAIL_SEND, MAIL_OK); - - pl->ModifyMoney(-int64(reqmoney)); - pl->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL, cost); - - bool needItemDelay = false; - - MailDraft draft(subject, body); - - if (items_count > 0 || money > 0) - { - if (items_count > 0) - { - for (uint8 i = 0; i < items_count; ++i) - { - Item* item = items[i]; - if (GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_BOOL_GM_LOG_TRADE)) - { - sLog.outCommand(GetAccountId(), "GM %s (Account: %u) mail item: %s (Entry: %u Count: %u) to player: %s (Account: %u)", - GetPlayerName(), GetAccountId(), item->GetProto()->Name1, item->GetEntry(), item->GetCount(), receiver.c_str(), rc_account); - } - - pl->MoveItemFromInventory(items[i]->GetBagSlot(), item->GetSlot(), true); - CharacterDatabase.BeginTransaction(); - item->DeleteFromInventoryDB(); // deletes item from character's inventory - item->SaveToDB(); // recursive and not have transaction guard into self, item not in inventory and can be save standalone - // owner in data will set at mail receive and item extracting - CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'", rc.GetCounter(), item->GetGUIDLow()); - CharacterDatabase.CommitTransaction(); - - draft.AddItem(item); - } - - // if item send to character at another account, then apply item delivery delay - needItemDelay = pl->GetSession()->GetAccountId() != rc_account; - } - - if (money > 0 && GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_BOOL_GM_LOG_TRADE)) - { - sLog.outCommand(GetAccountId(), "GM %s (Account: %u) mail money: " UI64FMTD " to player: %s (Account: %u)", - GetPlayerName(), GetAccountId(), money, receiver.c_str(), rc_account); - } - } - - // If theres is an item, there is a one hour delivery delay if sent to another account's character. - uint32 deliver_delay = needItemDelay ? sWorld.getConfig(CONFIG_UINT32_MAIL_DELIVERY_DELAY) : 0; - - // will delete item or place to receiver mail list - draft - .SetMoney(money) - .SetCOD(COD) - .SendMailTo(MailReceiver(receive, rc), pl, body.empty() ? MAIL_CHECK_MASK_COPIED : MAIL_CHECK_MASK_HAS_BODY, deliver_delay); - - CharacterDatabase.BeginTransaction(); - pl->SaveInventoryAndGoldToDB(); - CharacterDatabase.CommitTransaction(); -} - -/** - * Handles the Packet sent by the client when reading a mail. - * - * This method is called when a client reads a mail that was previously unread. - * It will add the MAIL_CHECK_MASK_READ flag to the mail being read. - * - * @see MailCheckMask - * - * @param recv_data the packet containing information about the mail the player read. - * - */ -void WorldSession::HandleMailMarkAsRead(WorldPacket& recv_data) -{ - ObjectGuid mailboxGuid; - uint32 mailId; - recv_data >> mailboxGuid; - recv_data >> mailId; - - if (!CheckMailBox(mailboxGuid)) - return; - - Player* pl = _player; - - if (Mail* m = pl->GetMail(mailId)) - { - if (pl->unReadMails) - --pl->unReadMails; - m->checked = m->checked | MAIL_CHECK_MASK_READ; - pl->m_mailsUpdated = true; - m->state = MAIL_STATE_CHANGED; - } -} - -/** - * Handles the Packet sent by the client when deleting a mail. - * - * This method is called when a client deletes a mail in his mailbox. - * - * @param recv_data The packet containing information about the mail being deleted. - * - */ -void WorldSession::HandleMailDelete(WorldPacket& recv_data) -{ - ObjectGuid mailboxGuid; - uint32 mailId; - recv_data >> mailboxGuid; - recv_data >> mailId; - recv_data.read_skip(); // mailTemplateId - - if (!CheckMailBox(mailboxGuid)) - return; - - Player* pl = _player; - pl->m_mailsUpdated = true; - - if (Mail* m = pl->GetMail(mailId)) - { - // delete shouldn't show up for COD mails - if (m->COD) - { - pl->SendMailResult(mailId, MAIL_DELETED, MAIL_ERR_INTERNAL_ERROR); - return; - } - - m->state = MAIL_STATE_DELETED; - } - pl->SendMailResult(mailId, MAIL_DELETED, MAIL_OK); -} -/** - * Handles the Packet sent by the client when returning a mail to sender. - * This method is called when a player chooses to return a mail to its sender. - * It will create a new MailDraft and add the items, money, etc. associated with the mail - * and then send the mail to the original sender. - * - * @param recv_data The packet containing information about the mail being returned. - * - */ -void WorldSession::HandleMailReturnToSender(WorldPacket& recv_data) -{ - ObjectGuid mailboxGuid; - uint32 mailId; - recv_data >> mailboxGuid; - recv_data >> mailId; - recv_data.read_skip(); // original sender GUID for return to, not used - - if (!CheckMailBox(mailboxGuid)) - return; - - Player* pl = _player; - Mail* m = pl->GetMail(mailId); - if (!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL)) - { - pl->SendMailResult(mailId, MAIL_RETURNED_TO_SENDER, MAIL_ERR_INTERNAL_ERROR); - return; - } - - // we can return mail now - // so firstly delete the old one - CharacterDatabase.BeginTransaction(); - CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", mailId); - // needed? - CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", mailId); - CharacterDatabase.CommitTransaction(); - pl->RemoveMail(mailId); - - // send back only to existing players and simple drop for other cases - if (m->messageType == MAIL_NORMAL && m->sender) - { - MailDraft draft; - if (m->mailTemplateId) - draft.SetMailTemplate(m->mailTemplateId, false);// items already included - else - draft.SetSubjectAndBody(m->subject, m->body); - - if (m->HasItems()) - { - for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2) - { - if (Item* item = pl->GetMItem(itr2->item_guid)) - draft.AddItem(item); - - pl->RemoveMItem(itr2->item_guid); - } - } - - draft.SetMoney(m->money).SendReturnToSender(GetAccountId(), m->receiverGuid, ObjectGuid(HIGHGUID_PLAYER, m->sender)); - } - - delete m; // we can deallocate old mail - pl->SendMailResult(mailId, MAIL_RETURNED_TO_SENDER, MAIL_OK); -} - -/** - * Handles the packet sent by the client when taking an item from the mail. - */ -void WorldSession::HandleMailTakeItem(WorldPacket& recv_data) -{ - ObjectGuid mailboxGuid; - uint32 mailId; - uint32 itemId; - recv_data >> mailboxGuid; - recv_data >> mailId; - recv_data >> itemId; // item guid low - - if (!CheckMailBox(mailboxGuid)) - return; - - Player* pl = _player; - - Mail* m = pl->GetMail(mailId); - if (!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL)) - { - pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_INTERNAL_ERROR); - return; - } - - // prevent cheating with skip client money check - if (pl->GetMoney() < m->COD) - { - pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_NOT_ENOUGH_MONEY); - return; - } - - Item* it = pl->GetMItem(itemId); - - ItemPosCountVec dest; - InventoryResult msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, dest, it, false); - if (msg == EQUIP_ERR_OK) - { - 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 - { - ObjectGuid sender_guid = ObjectGuid(HIGHGUID_PLAYER, m->sender); - Player* sender = sObjectMgr.GetPlayer(sender_guid); - - uint32 sender_accId = 0; - - if (GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_BOOL_GM_LOG_TRADE)) - { - std::string sender_name; - if (sender) - { - sender_accId = sender->GetSession()->GetAccountId(); - sender_name = sender->GetName(); - } - else if (sender_guid) - { - // can be calculated early - sender_accId = sObjectMgr.GetPlayerAccountIdByGUID(sender_guid); - - if (!sObjectMgr.GetPlayerNameByGUID(sender_guid, sender_name)) - sender_name = sObjectMgr.GetMangosStringForDBCLocale(LANG_UNKNOWN); - } - sLog.outCommand(GetAccountId(), "GM %s (Account: %u) receive mail item: %s (Entry: %u Count: %u) and send COD money: %u to player: %s (Account: %u)", - GetPlayerName(), GetAccountId(), it->GetProto()->Name1, it->GetEntry(), it->GetCount(), m->COD, sender_name.c_str(), sender_accId); - } - else if (!sender) - sender_accId = sObjectMgr.GetPlayerAccountIdByGUID(sender_guid); - - // check player existence - if (sender || sender_accId) - { - MailDraft(m->subject, "") - .SetMoney(m->COD) - .SendMailTo(MailReceiver(sender, sender_guid), _player, MAIL_CHECK_MASK_COD_PAYMENT); - } - - pl->ModifyMoney(-int64(m->COD)); - } - m->COD = 0; - m->state = MAIL_STATE_CHANGED; - pl->m_mailsUpdated = true; - pl->RemoveMItem(it->GetGUIDLow()); - - uint32 count = it->GetCount(); // save counts before store and possible merge with deleting - pl->MoveItemToInventory(dest, it, true); - - CharacterDatabase.BeginTransaction(); - pl->SaveInventoryAndGoldToDB(); - pl->_SaveMail(); - CharacterDatabase.CommitTransaction(); - - pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_OK, 0, itemId, count); - } - else - pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_EQUIP_ERROR, msg); -} -/** - * Handles the packet sent by the client when taking money from the mail. - */ -void WorldSession::HandleMailTakeMoney(WorldPacket& recv_data) -{ - ObjectGuid mailboxGuid; - uint32 mailId; - uint64 money; - recv_data >> mailboxGuid; - recv_data >> mailId; - recv_data >> money; - - if (!CheckMailBox(mailboxGuid)) - return; - - Player* pl = _player; - - Mail* m = pl->GetMail(mailId); - if (!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL)) - { - pl->SendMailResult(mailId, MAIL_MONEY_TAKEN, MAIL_ERR_INTERNAL_ERROR); - return; - } - - pl->SendMailResult(mailId, MAIL_MONEY_TAKEN, MAIL_OK); - - pl->ModifyMoney(m->money); - m->money = 0; - m->state = MAIL_STATE_CHANGED; - pl->m_mailsUpdated = true; - - // save money and mail to prevent cheating - CharacterDatabase.BeginTransaction(); - pl->SaveGoldToDB(); - pl->_SaveMail(); - CharacterDatabase.CommitTransaction(); -} - -/** - * Handles the packet sent by the client when requesting the current mail list. - * It will send a list of all available mails in the players mailbox to the client. - */ -void WorldSession::HandleGetMailList(WorldPacket& recv_data) -{ - ObjectGuid mailboxGuid; - recv_data >> mailboxGuid; - - if (!CheckMailBox(mailboxGuid)) - return; - - // client can't work with packets > max int16 value - const uint32 maxPacketSize = 32767; - - uint32 mailsCount = 0; // send to client mails amount - uint32 realCount = 0; // real mails amount - - WorldPacket data(SMSG_MAIL_LIST_RESULT, 200); // guess size - data << uint32(0); // real mail's count - data << uint8(0); // mail's count - time_t cur_time = time(NULL); - - for (PlayerMails::iterator itr = _player->GetMailBegin(); itr != _player->GetMailEnd(); ++itr) - { - // packet send mail count as uint8, prevent overflow - if (mailsCount >= 254) - { - realCount += 1; - continue; - } - - // skip deleted or not delivered (deliver delay not expired) mails - if ((*itr)->state == MAIL_STATE_DELETED || cur_time < (*itr)->deliver_time) - continue; - - uint8 item_count = (*itr)->items.size(); // max count is MAX_MAIL_ITEMS (12) - - size_t next_mail_size = 2 + 4 + 1 + ((*itr)->messageType == MAIL_NORMAL ? 8 : 4) + 4 * 8 + ((*itr)->subject.size() + 1) + ((*itr)->body.size() + 1) + 1 + item_count * (1 + 4 + 4 + 7 * 3 * 4 + 4 + 4 + 4 + 4 + 4 + 4 + 1); - - if (data.wpos() + next_mail_size > maxPacketSize) - { - realCount += 1; - continue; - } - - data << uint16(next_mail_size); // Message size - data << uint32((*itr)->messageID); // Message ID - data << uint8((*itr)->messageType); // Message Type - - switch ((*itr)->messageType) - { - case MAIL_NORMAL: // sender guid - data << ObjectGuid(HIGHGUID_PLAYER, (*itr)->sender); - break; - case MAIL_CREATURE: - case MAIL_GAMEOBJECT: - case MAIL_AUCTION: - data << uint32((*itr)->sender); // creature/gameobject entry, auction id - break; - case MAIL_CALENDAR: - data << uint32(0); // unknown - break; - } - - data << uint64((*itr)->COD); // COD - data << uint32(0); // unknown, probably changed in 3.3.3 - data << uint32((*itr)->stationery); // stationery (Stationery.dbc) - data << uint64((*itr)->money); // copper - data << uint32((*itr)->checked); // flags - data << float(float((*itr)->expire_time - time(NULL)) / float(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) - { - // unsure - data << uint32(item ? item->GetEnchantmentCharges((EnchantmentSlot)j) : 0); - // unsure - data << uint32(item ? item->GetEnchantmentDuration((EnchantmentSlot)j) : 0); - // unsure - data << uint32(item ? item->GetEnchantmentId((EnchantmentSlot)j) : 0); - } - // can be negative - data << uint32(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); - } - - mailsCount += 1; - realCount += 1; - } - - data.put(0, realCount); // this will display warning about undelivered mail to player if realCount > mailsCount - data.put(4, mailsCount); // set real send mails to client - SendPacket(&data); - - // recalculate m_nextMailDelivereTime and unReadMails - _player->UpdateNextMailTimeAndUnreads(); -} - -/* - * Handles the packet sent by the client when he copies the body a mail to his inventory. - * - * When a player copies the body of a mail to his inventory this method is called. It will create - * a new item with the text of the mail and store it in the players inventory (if possible). - * - */ -void WorldSession::HandleMailCreateTextItem(WorldPacket& recv_data) -{ - ObjectGuid mailboxGuid; - uint32 mailId; - - recv_data >> mailboxGuid; - recv_data >> mailId; - - if (!CheckMailBox(mailboxGuid)) - return; - - Player* pl = _player; - - Mail* m = pl->GetMail(mailId); - if (!m || (m->body.empty() && !m->mailTemplateId) || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL)) - { - pl->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.GenerateItemLowGuid(), MAIL_BODY_ITEM_TEMPLATE, pl)) - { - 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) - { - pl->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_ERR_INTERNAL_ERROR); - return; - } - - bodyItem->SetText(mailTemplateEntry->content[GetSessionDbcLocale()]); - } - else - bodyItem->SetText(m->body); - - bodyItem->SetGuidValue(ITEM_FIELD_CREATOR, ObjectGuid(HIGHGUID_PLAYER, m->sender)); - bodyItem->SetFlag(ITEM_FIELD_FLAGS, ITEM_DYNFLAG_READABLE | ITEM_DYNFLAG_UNK15 | ITEM_DYNFLAG_UNK16); - - DETAIL_LOG("HandleMailCreateTextItem mailid=%u", mailId); - - ItemPosCountVec dest; - InventoryResult 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; - pl->m_mailsUpdated = true; - - pl->StoreItem(dest, bodyItem, true); - pl->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_OK); - } - else - { - pl->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_ERR_EQUIP_ERROR, msg); - delete bodyItem; - } -} - -/** - * No idea when this is called. - */ -void WorldSession::HandleQueryNextMailTime(WorldPacket & /**recv_data*/) -{ - WorldPacket data(MSG_QUERY_NEXT_MAIL_TIME, 8); - - if (_player->unReadMails > 0) - { - data << uint32(0); // float - data << uint32(0); // count - - uint32 count = 0; - time_t now = time(NULL); - for (PlayerMails::iterator itr = _player->GetMailBegin(); itr != _player->GetMailEnd(); ++itr) - { - Mail* m = (*itr); - // must be not checked yet - if (m->checked & MAIL_CHECK_MASK_READ) - continue; - - // and already delivered - if (now < m->deliver_time) - continue; - - data << ObjectGuid(HIGHGUID_PLAYER, m->sender); // sender guid - - switch (m->messageType) - { - case MAIL_AUCTION: - data << uint32(m->sender); // auction house id - data << uint32(MAIL_AUCTION); // message type - break; - default: - data << uint32(0); - data << uint32(0); - break; - } - - data << uint32(m->stationery); - data << uint32(0xC6000000); // float unk, time or something - - ++count; - if (count == 2) // do not display more than 2 mails - break; - } - data.put(4, count); - } - else - { - data << uint32(0xC7A8C000); - data << uint32(0x00000000); - } - SendPacket(&data); -} - -void WorldSession::SendShowMailBox(ObjectGuid guid) -{ - WorldPacket data(SMSG_SHOW_MAILBOX, 8); - data << ObjectGuid(guid); - SendPacket(&data); -} - -/*! @} */ diff --git a/src/game/NPCHandler.cpp b/src/game/NPCHandler.cpp deleted file mode 100644 index 286d97042..000000000 --- a/src/game/NPCHandler.cpp +++ /dev/null @@ -1,895 +0,0 @@ -/** - * This code is part of MaNGOS. Contributor & Copyright details are in AUTHORS/THANKS. - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Common.h" -#include "Language.h" -#include "Database/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 "ScriptMgr.h" -#include "Creature.h" -#include "Pet.h" -#include "Guild.h" -#include "GuildMgr.h" -#include "Chat.h" - -enum StableResultCode -{ - STABLE_ERR_MONEY = 0x01, // "you don't have enough money" - STABLE_INVALID_SLOT = 0x03, - 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 = 0x0B, // "you are unable to control exotic creatures" - STABLE_ERR_INTERNAL = 0x0C, -}; - -void WorldSession::HandleTabardVendorActivateOpcode(WorldPacket& recv_data) -{ - ObjectGuid guid; - recv_data >> guid; - - Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_TABARDDESIGNER); - if (!unit) - { - DEBUG_LOG("WORLD: HandleTabardVendorActivateOpcode - %s not found or you can't interact with him.", guid.GetString().c_str()); - return; - } - - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - - SendTabardVendorActivate(guid); -} - -void WorldSession::SendTabardVendorActivate(ObjectGuid guid) -{ - WorldPacket data(MSG_TABARDVENDOR_ACTIVATE, 8); - data << ObjectGuid(guid); - SendPacket(&data); -} - -void WorldSession::HandleBankerActivateOpcode(WorldPacket& recv_data) -{ - ObjectGuid guid; - - DEBUG_LOG("WORLD: Received opcode CMSG_BANKER_ACTIVATE"); - - recv_data >> guid; - - if (!CheckBanker(guid)) - return; - - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - - SendShowBank(guid); -} - -void WorldSession::SendShowBank(ObjectGuid guid) -{ - WorldPacket data(SMSG_SHOW_BANK, 8); - data << ObjectGuid(guid); - SendPacket(&data); -} - -void WorldSession::HandleTrainerListOpcode(WorldPacket& recv_data) -{ - ObjectGuid guid; - - recv_data >> guid; - - SendTrainerList(guid); -} - -void WorldSession::SendTrainerList(ObjectGuid guid) -{ - std::string str = GetMangosString(LANG_NPC_TAINER_HELLO); - SendTrainerList(guid, str); -} - -static void SendTrainerSpellHelper(WorldPacket& data, TrainerSpell const* tSpell, TrainerSpellState state, float fDiscountMod, bool can_learn_primary_prof, uint32 reqLevel) -{ - bool primary_prof_first_rank = sSpellMgr.IsPrimaryProfessionFirstRankSpell(tSpell->learnedSpell); - - SpellChainNode const* chain_node = sSpellMgr.GetSpellChainNode(tSpell->learnedSpell); - - 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 << uint8(reqLevel); - data << uint32(tSpell->reqSkill); - data << uint32(tSpell->reqSkillValue); - 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 << uint32(!tSpell->IsCastable() && chain_node ? (chain_node->prev ? chain_node->prev : chain_node->req) : 0); - data << uint32(!tSpell->IsCastable() && chain_node && chain_node->prev ? chain_node->req : 0); -} - -void WorldSession::SendTrainerList(ObjectGuid guid, const std::string& strTitle) -{ - DEBUG_LOG("WORLD: SendTrainerList"); - - Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_TRAINER); - if (!unit) - { - DEBUG_LOG("WORLD: SendTrainerList - %s not found or you can't interact with him.", guid.GetString().c_str()); - return; - } - - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - - // trainer list loaded at check; - if (!unit->IsTrainerOf(_player, true)) - return; - - CreatureInfo const* ci = unit->GetCreatureInfo(); - if (!ci) - return; - - TrainerSpellData const* cSpells = unit->GetTrainerSpells(); - TrainerSpellData const* tSpells = unit->GetTrainerTemplateSpells(); - - if (!cSpells && !tSpells) - { - DEBUG_LOG("WORLD: SendTrainerList - Training spells not found for %s", guid.GetString().c_str()); - return; - } - - uint32 maxcount = (cSpells ? cSpells->spellList.size() : 0) + (tSpells ? tSpells->spellList.size() : 0); - uint32 trainer_type = cSpells && cSpells->trainerType ? cSpells->trainerType : (tSpells ? tSpells->trainerType : 0); - - WorldPacket data(SMSG_TRAINER_LIST, 8 + 4 + 4 + maxcount * 38 + strTitle.size() + 1); - data << ObjectGuid(guid); - data << uint32(trainer_type); - data << uint32(ci->trainerId); - - size_t count_pos = data.wpos(); - data << uint32(maxcount); - - // reputation discount - float fDiscountMod = _player->GetReputationPriceDiscount(unit); - bool can_learn_primary_prof = GetPlayer()->GetFreePrimaryProfessionPoints() > 0; - - uint32 count = 0; - - if (cSpells) - { - for (TrainerSpellMap::const_iterator itr = cSpells->spellList.begin(); itr != cSpells->spellList.end(); ++itr) - { - TrainerSpell const* tSpell = &itr->second; - - uint32 reqLevel = 0; - if (!_player->IsSpellFitByClassAndRace(tSpell->learnedSpell, &reqLevel)) - continue; - - reqLevel = tSpell->isProvidedReqLevel ? tSpell->reqLevel : std::max(reqLevel, tSpell->reqLevel); - - TrainerSpellState state = _player->GetTrainerSpellState(tSpell, reqLevel); - - SendTrainerSpellHelper(data, tSpell, state, fDiscountMod, can_learn_primary_prof, reqLevel); - - ++count; - } - } - - if (tSpells) - { - for (TrainerSpellMap::const_iterator itr = tSpells->spellList.begin(); itr != tSpells->spellList.end(); ++itr) - { - TrainerSpell const* tSpell = &itr->second; - - uint32 reqLevel = 0; - if (!_player->IsSpellFitByClassAndRace(tSpell->learnedSpell, &reqLevel)) - continue; - - reqLevel = tSpell->isProvidedReqLevel ? tSpell->reqLevel : std::max(reqLevel, tSpell->reqLevel); - - TrainerSpellState state = _player->GetTrainerSpellState(tSpell, reqLevel); - - SendTrainerSpellHelper(data, tSpell, state, fDiscountMod, can_learn_primary_prof, reqLevel); - - ++count; - } - } - - data << strTitle; - - data.put(count_pos, count); - SendPacket(&data); -} - -void WorldSession::HandleTrainerBuySpellOpcode(WorldPacket& recv_data) -{ - ObjectGuid guid; - uint32 spellId = 0, trainerId = 0; - - recv_data >> guid >> trainerId >> spellId; - DEBUG_LOG("WORLD: Received opcode CMSG_TRAINER_BUY_SPELL Trainer: %s, learn spell id is: %u", guid.GetString().c_str(), spellId); - - Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_TRAINER); - if (!unit) - { - DEBUG_LOG("WORLD: HandleTrainerBuySpellOpcode - %s not found or you can't interact with him.", guid.GetString().c_str()); - return; - } - - WorldPacket sendData(SMSG_TRAINER_SERVICE, 16); - - uint32 trainState = 2; - - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - - if (!unit->IsTrainerOf(_player, true)) - trainState = 1; - - // check present spell in trainer spell list - TrainerSpellData const* cSpells = unit->GetTrainerSpells(); - TrainerSpellData const* tSpells = unit->GetTrainerTemplateSpells(); - - if (!cSpells && !tSpells) - trainState = 1; - - // Try find spell in npc_trainer - TrainerSpell const* trainer_spell = cSpells ? cSpells->Find(spellId) : NULL; - - // Not found, try find in npc_trainer_template - if (!trainer_spell && tSpells) - trainer_spell = tSpells->Find(spellId); - - // Not found anywhere, cheating? - if (!trainer_spell) - trainState = 1; - - // can't be learn, cheat? Or double learn with lags... - uint32 reqLevel = 0; - if (!_player->IsSpellFitByClassAndRace(trainer_spell->learnedSpell, &reqLevel)) - trainState = 1; - - reqLevel = trainer_spell->isProvidedReqLevel ? trainer_spell->reqLevel : std::max(reqLevel, trainer_spell->reqLevel); - if (_player->GetTrainerSpellState(trainer_spell, reqLevel) != TRAINER_SPELL_GREEN) - trainState = 1; - - // apply reputation discount - uint32 nSpellCost = uint32(floor(trainer_spell->spellCost * _player->GetReputationPriceDiscount(unit))); - - // check money requirement - if (_player->GetMoney() < nSpellCost && trainState > 1) - trainState = 0; - - if (trainState != 2) - { - sendData << ObjectGuid(guid); - sendData << uint32(spellId); - sendData << uint32(trainState); - SendPacket(&sendData); - } - else - { - _player->ModifyMoney(-int64(nSpellCost)); - - // visual effect on trainer - WorldPacket data; - unit->BuildSendPlayVisualPacket(&data, 0xB3, false); - SendPacket(&data); - - // visual effect on player - _player->BuildSendPlayVisualPacket(&data, 0x016A, true); - SendPacket(&data); - - // learn explicitly or cast explicitly - // TODO - Are these spells really cast correctly this way? - if (trainer_spell->IsCastable()) - _player->CastSpell(_player, trainer_spell->spell, true); - else - _player->learnSpell(spellId, false); - - sendData << ObjectGuid(guid); - sendData << uint32(spellId); // should be same as in packet from client - sendData << uint32(trainState); - SendPacket(&sendData); - } -} - -void WorldSession::HandleGossipHelloOpcode(WorldPacket& recv_data) -{ - DEBUG_LOG("WORLD: Received opcode CMSG_GOSSIP_HELLO"); - - ObjectGuid guid; - recv_data >> guid; - - Creature* pCreature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE); - if (!pCreature) - { - DEBUG_LOG("WORLD: HandleGossipHelloOpcode - %s not found or you can't interact with him.", guid.GetString().c_str()); - return; - } - - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - - pCreature->StopMoving(); - - if (pCreature->isSpiritGuide()) - pCreature->SendAreaSpiritHealerQueryOpcode(_player); - - if (!sScriptMgr.OnGossipHello(_player, pCreature)) - { - _player->PrepareGossipMenu(pCreature, pCreature->GetCreatureInfo()->GossipMenuId); - _player->SendPreparedGossip(pCreature); - } -} - -void WorldSession::HandleGossipSelectOptionOpcode(WorldPacket& recv_data) -{ - DEBUG_LOG("WORLD: CMSG_GOSSIP_SELECT_OPTION"); - - uint32 gossipListId; - uint32 menuId; - ObjectGuid guid; - std::string code; - - recv_data >> guid >> menuId >> gossipListId; - - if (_player->PlayerTalkClass->GossipOptionCoded(gossipListId)) - { - recv_data >> code; - DEBUG_LOG("Gossip code: %s", code.c_str()); - } - - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - - uint32 sender = _player->PlayerTalkClass->GossipOptionSender(gossipListId); - uint32 action = _player->PlayerTalkClass->GossipOptionAction(gossipListId); - - if (guid.IsAnyTypeCreature()) - { - Creature* pCreature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE); - - if (!pCreature) - { - DEBUG_LOG("WORLD: HandleGossipSelectOptionOpcode - %s not found or you can't interact with it.", guid.GetString().c_str()); - return; - } - - if (!sScriptMgr.OnGossipSelect(_player, pCreature, sender, action, code.empty() ? NULL : code.c_str())) - _player->OnGossipSelect(pCreature, gossipListId, menuId); - } - else if (guid.IsGameObject()) - { - GameObject* pGo = GetPlayer()->GetGameObjectIfCanInteractWith(guid); - - if (!pGo) - { - DEBUG_LOG("WORLD: HandleGossipSelectOptionOpcode - %s not found or you can't interact with it.", guid.GetString().c_str()); - return; - } - - if (!sScriptMgr.OnGossipSelect(_player, pGo, sender, action, code.empty() ? NULL : code.c_str())) - _player->OnGossipSelect(pGo, gossipListId, menuId); - } -} - -void WorldSession::HandleSpiritHealerActivateOpcode(WorldPacket& recv_data) -{ - DEBUG_LOG("WORLD: CMSG_SPIRIT_HEALER_ACTIVATE"); - - ObjectGuid guid; - - recv_data >> guid; - - Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_SPIRITHEALER); - if (!unit) - { - DEBUG_LOG("WORLD: HandleSpiritHealerActivateOpcode - %s not found or you can't interact with him.", guid.GetString().c_str()); - return; - } - - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - - SendSpiritResurrect(); -} - -void WorldSession::SendSpiritResurrect() -{ - _player->ResurrectPlayer(0.5f, true); - - _player->DurabilityLossAll(0.25f, true); - - // get corpse nearest graveyard - WorldSafeLocsEntry const* corpseGrave = NULL; - Corpse* corpse = _player->GetCorpse(); - if (corpse) - corpseGrave = sObjectMgr.GetClosestGraveYard( - corpse->GetPositionX(), corpse->GetPositionY(), corpse->GetPositionZ(), corpse->GetMapId(), _player->GetTeam()); - - // now can spawn bones - _player->SpawnCorpseBones(); - - // teleport to nearest from corpse graveyard, if different from nearest to player ghost - if (corpseGrave) - { - WorldSafeLocsEntry const* ghostGrave = sObjectMgr.GetClosestGraveYard( - _player->GetPositionX(), _player->GetPositionY(), _player->GetPositionZ(), _player->GetMapId(), _player->GetTeam()); - - if (corpseGrave != ghostGrave) - _player->TeleportTo(corpseGrave->map_id, corpseGrave->x, corpseGrave->y, corpseGrave->z, _player->GetOrientation()); - // or update at original position - else - { - _player->GetCamera().UpdateVisibilityForOwner(); - _player->UpdateObjectVisibility(); - } - } - // or update at original position - else - { - _player->GetCamera().UpdateVisibilityForOwner(); - _player->UpdateObjectVisibility(); - } -} - -void WorldSession::HandleBinderActivateOpcode(WorldPacket& recv_data) -{ - ObjectGuid npcGuid; - recv_data >> npcGuid; - - if (!GetPlayer()->IsInWorld() || !GetPlayer()->isAlive()) - return; - - Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(npcGuid, UNIT_NPC_FLAG_INNKEEPER); - if (!unit) - { - DEBUG_LOG("WORLD: HandleBinderActivateOpcode - %s not found or you can't interact with him.", npcGuid.GetString().c_str()); - return; - } - - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - - SendBindPoint(unit); -} - -void WorldSession::SendBindPoint(Creature* npc) -{ - // prevent set homebind to instances in any case - if (GetPlayer()->GetMap()->Instanceable()) - return; - - // send spell for bind 3286 bind magic - npc->CastSpell(_player, 3286, true); // Bind - - WorldPacket data(SMSG_TRAINER_SERVICE, 16); - data << npc->GetObjectGuid(); - data << uint32(3286); // Bind - data << uint32(2); - SendPacket(&data); - - _player->PlayerTalkClass->CloseGossip(); -} - -void WorldSession::HandleListStabledPetsOpcode(WorldPacket& recv_data) -{ - DEBUG_LOG("WORLD: Recv MSG_LIST_STABLED_PETS"); - ObjectGuid npcGUID; - - recv_data >> npcGUID; - - if (!CheckStableMaster(npcGUID)) - { - SendStableResult(STABLE_ERR_STABLE); - return; - } - - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - - SendStablePet(npcGUID); -} - -void WorldSession::SendStablePet(ObjectGuid guid) -{ - DEBUG_LOG("WORLD: Recv MSG_LIST_STABLED_PETS Send."); - - WorldPacket data(MSG_LIST_STABLED_PETS, 200); // guess size - data << 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; - } - - // 0 1 2 3 4 - QueryResult* result = CharacterDatabase.PQuery("SELECT owner, id, entry, level, name FROM character_pet WHERE owner = '%u' AND slot >= '%u' AND slot <= '%u' ORDER BY slot", - _player->GetGUIDLow(), PET_SAVE_FIRST_STABLE_SLOT, PET_SAVE_LAST_STABLE_SLOT); - - if (result) - { - do - { - Field* fields = result->Fetch(); - - data << uint32(fields[1].GetUInt32()); // petnumber - data << uint32(fields[2].GetUInt32()); // creature entry - data << uint32(fields[3].GetUInt32()); // 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()); - - delete result; - } - - data.put(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); -} - -bool WorldSession::CheckStableMaster(ObjectGuid guid) -{ - // spell case or GM - if (guid == GetPlayer()->GetObjectGuid()) - { - // command case will return only if player have real access to command - if (!GetPlayer()->HasAuraType(SPELL_AURA_OPEN_STABLE) && !ChatHandler(GetPlayer()).FindCommand("stable")) - { - DEBUG_LOG("%s attempt open stable in cheating way.", guid.GetString().c_str()); - return false; - } - } - // stable master case - else - { - if (!GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_STABLEMASTER)) - { - DEBUG_LOG("Stablemaster %s not found or you can't interact with him.", guid.GetString().c_str()); - return false; - } - } - - return true; -} - -void WorldSession::HandleStablePet(WorldPacket& recv_data) -{ - DEBUG_LOG("WORLD: Recv CMSG_STABLE_PET"); - ObjectGuid npcGUID; - - recv_data >> npcGUID; - - if (!GetPlayer()->isAlive()) - { - SendStableResult(STABLE_ERR_STABLE); - return; - } - - if (!CheckStableMaster(npcGUID)) - { - SendStableResult(STABLE_ERR_STABLE); - return; - } - - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(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; - } - - uint32 free_slot = 1; - - QueryResult* result = CharacterDatabase.PQuery("SELECT owner,slot,id FROM character_pet WHERE owner = '%u' AND slot >= '%u' AND slot <= '%u' ORDER BY slot ", - _player->GetGUIDLow(), PET_SAVE_FIRST_STABLE_SLOT, PET_SAVE_LAST_STABLE_SLOT); - if (result) - { - do - { - Field* fields = result->Fetch(); - - uint32 slot = fields[1].GetUInt32(); - - // slots ordered in query, and if not equal then free - if (slot != free_slot) - break; - - // this slot not free, skip - ++free_slot; - } - while (result->NextRow()); - - delete result; - } - - if (free_slot > 0 && free_slot <= GetPlayer()->m_stableSlots) - { - pet->Unsummon(PetSaveMode(free_slot), _player); - SendStableResult(STABLE_SUCCESS_STABLE); - } - else - SendStableResult(STABLE_ERR_STABLE); -} - -void WorldSession::HandleUnstablePet(WorldPacket& recv_data) -{ - DEBUG_LOG("WORLD: Recv CMSG_UNSTABLE_PET."); - ObjectGuid npcGUID; - uint32 petnumber; - - recv_data >> npcGUID >> petnumber; - - if (!CheckStableMaster(npcGUID)) - { - SendStableResult(STABLE_ERR_STABLE); - return; - } - - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - - uint32 creature_id = 0; - - { - QueryResult* result = CharacterDatabase.PQuery("SELECT entry FROM character_pet WHERE owner = '%u' AND id = '%u' AND slot >='%u' AND slot <= '%u'", - _player->GetGUIDLow(), petnumber, PET_SAVE_FIRST_STABLE_SLOT, PET_SAVE_LAST_STABLE_SLOT); - if (result) - { - Field* fields = result->Fetch(); - creature_id = fields[0].GetUInt32(); - delete result; - } - } - - if (!creature_id) - { - SendStableResult(STABLE_ERR_STABLE); - return; - } - - CreatureInfo const* creatureInfo = ObjectMgr::GetCreatureTemplate(creature_id); - 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) - pet->Unsummon(PET_SAVE_AS_DELETED, _player); - - Pet* newpet = new Pet(HUNTER_PET); - if (!newpet->LoadPetFromDB(_player, creature_id, petnumber)) - { - delete newpet; - newpet = NULL; - SendStableResult(STABLE_ERR_STABLE); - return; - } - - SendStableResult(STABLE_SUCCESS_UNSTABLE); -} - -void WorldSession::HandleBuyStableSlot(WorldPacket& recv_data) -{ - DEBUG_LOG("WORLD: Recv CMSG_BUY_STABLE_SLOT."); - ObjectGuid npcGUID; - - recv_data >> npcGUID; - - if (!CheckStableMaster(npcGUID)) - { - SendStableResult(STABLE_ERR_STABLE); - return; - } - - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); -} - -void WorldSession::HandleStableRevivePet(WorldPacket &/* recv_data */) -{ - DEBUG_LOG("HandleStableRevivePet: Not implemented"); -} - -void WorldSession::HandleStableSwapPet(WorldPacket& recv_data) -{ - DEBUG_LOG("WORLD: Recv CMSG_STABLE_SWAP_PET."); - ObjectGuid npcGUID; - uint32 pet_number; - - recv_data >> npcGUID >> pet_number; - - if (!CheckStableMaster(npcGUID)) - { - SendStableResult(STABLE_ERR_STABLE); - return; - } - - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(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 - QueryResult* result = CharacterDatabase.PQuery("SELECT slot,entry FROM character_pet WHERE owner = '%u' AND id = '%u'", - _player->GetGUIDLow(), pet_number); - if (!result) - { - SendStableResult(STABLE_ERR_STABLE); - return; - } - - Field* fields = result->Fetch(); - - uint32 slot = fields[0].GetUInt32(); - uint32 creature_id = fields[1].GetUInt32(); - delete result; - - if (!creature_id) - { - SendStableResult(STABLE_ERR_STABLE); - return; - } - - CreatureInfo const* creatureInfo = ObjectMgr::GetCreatureTemplate(creature_id); - if (!creatureInfo || !creatureInfo->isTameable(_player->CanTameExoticPets())) - { - // if problem in exotic pet - if (creatureInfo && creatureInfo->isTameable(true)) - SendStableResult(STABLE_ERR_EXOTIC); - else - SendStableResult(STABLE_ERR_STABLE); - return; - } - - // move alive pet to slot or delete dead pet - pet->Unsummon(pet->isAlive() ? PetSaveMode(slot) : PET_SAVE_AS_DELETED, _player); - - // summon unstabled pet - Pet* newpet = new Pet; - if (!newpet->LoadPetFromDB(_player, creature_id, pet_number)) - { - delete newpet; - SendStableResult(STABLE_ERR_STABLE); - } - else - SendStableResult(STABLE_SUCCESS_UNSTABLE); -} - -void WorldSession::HandleRepairItemOpcode(WorldPacket& recv_data) -{ - DEBUG_LOG("WORLD: CMSG_REPAIR_ITEM"); - - ObjectGuid npcGuid; - ObjectGuid itemGuid; - uint8 guildBank; // new in 2.3.2, bool that means from guild bank money - - recv_data >> npcGuid >> itemGuid >> guildBank; - - Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(npcGuid, UNIT_NPC_FLAG_REPAIR); - if (!unit) - { - DEBUG_LOG("WORLD: HandleRepairItemOpcode - %s not found or you can't interact with him.", npcGuid.GetString().c_str()); - return; - } - - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - - // reputation discount - float discountMod = _player->GetReputationPriceDiscount(unit); - - uint32 TotalCost = 0; - if (itemGuid) - { - DEBUG_LOG("ITEM: %s repair of %s", npcGuid.GetString().c_str(), itemGuid.GetString().c_str()); - - Item* item = _player->GetItemByGuid(itemGuid); - - if (item) - TotalCost = _player->DurabilityRepair(item->GetPos(), true, discountMod, (guildBank > 0)); - } - else - { - DEBUG_LOG("ITEM: %s repair all items", npcGuid.GetString().c_str()); - - TotalCost = _player->DurabilityRepairAll(true, discountMod, (guildBank > 0)); - } - if (guildBank) - { - uint32 GuildId = _player->GetGuildId(); - if (!GuildId) - return; - Guild* pGuild = sGuildMgr.GetGuildById(GuildId); - if (!pGuild) - return; - pGuild->LogBankEvent(GUILD_BANK_LOG_REPAIR_MONEY, 0, _player->GetGUIDLow(), TotalCost); - pGuild->SendMoneyInfo(this, _player->GetGUIDLow()); - } -} diff --git a/src/game/Opcodes.cpp b/src/game/Opcodes.cpp deleted file mode 100644 index 7302895f4..000000000 --- a/src/game/Opcodes.cpp +++ /dev/null @@ -1,1416 +0,0 @@ -/** - * This code is part of MaNGOS. Contributor & Copyright details are in AUTHORS/THANKS. - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/** \file - \ingroup u2w -*/ - -#include "Opcodes.h" -#include "WorldSession.h" - -static void DefineOpcode(uint16 opcode, const char* name, SessionStatus status, PacketProcessing packetProcessing, void (WorldSession::*handler)(WorldPacket& recvPacket)) -{ - opcodeTable[opcode].name = name; - opcodeTable[opcode].status = status; - opcodeTable[opcode].packetProcessing = packetProcessing; - opcodeTable[opcode].handler = handler; -} - -#define OPCODE( name, status, packetProcessing, handler ) DefineOpcode( name, #name, status, packetProcessing, handler ) - -/// Correspondence between opcodes and their names -OpcodeHandler opcodeTable[MAX_OPCODE_TABLE_SIZE]; - -void InitializeOpcodes() -{ - for(uint16 i = 0; i < MAX_OPCODE_TABLE_SIZE; ++i) - DefineOpcode(i, "UNKNOWN", STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL); - - OPCODE(MSG_WOW_CONNECTION, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_EarlyProccess ); - OPCODE(SMSG_AUTH_CHALLENGE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_AUTH_SESSION, STATUS_NEVER, PROCESS_THREADUNSAFE, &WorldSession::Handle_EarlyProccess ); - OPCODE(SMSG_AUTH_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(MSG_NULL_ACTION, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_BOOTME, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_DBLOOKUP, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_DBLOOKUP, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_QUERY_OBJECT_POSITION, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_QUERY_OBJECT_POSITION, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_QUERY_OBJECT_ROTATION, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_QUERY_OBJECT_ROTATION, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_WORLD_TELEPORT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleWorldTeleportOpcode ); - OPCODE(CMSG_TELEPORT_TO_UNIT, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_ZONE_MAP, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_ZONE_MAP, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_DEBUG_CHANGECELLZONE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_MOVE_CHARACTER_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_MOVE_CHARACTER_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_RECHARGE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_LEARN_SPELL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_CREATEMONSTER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_DESTROYMONSTER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_CREATEITEM, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_CREATEGAMEOBJECT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_CHECK_FOR_BOTS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_MAKEMONSTERATTACKGUID, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_BOT_DETECTED2, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_FORCEACTION, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_FORCEACTIONONOTHER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_FORCEACTIONSHOW, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_FORCEACTIONSHOW, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_PETGODMODE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_PETGODMODE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_REFER_A_FRIEND_EXPIRED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_WEATHER_SPEED_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_UNDRESSPLAYER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_BEASTMASTER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_GODMODE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_GODMODE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_CHEAT_SETMONEY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_LEVEL_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_PET_LEVEL_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_SET_WORLDSTATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_COOLDOWN_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_USE_SKILL_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_FLAG_QUEST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_FLAG_QUEST_FINISH, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_CLEAR_QUEST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_SEND_EVENT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_DEBUG_AISTATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_DEBUG_AISTATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_DISABLE_PVP_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_ADVANCE_SPAWN_TIME, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_DESTRUCTIBLE_BUILDING_DAMAGE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_AUTH_SRP6_BEGIN, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_AUTH_SRP6_PROOF, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_AUTH_SRP6_RECODE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_CHAR_CREATE, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleCharCreateOpcode ); - OPCODE(CMSG_CHAR_ENUM, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleCharEnumOpcode ); - OPCODE(CMSG_CHAR_DELETE, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleCharDeleteOpcode ); - //OPCODE(SMSG_AUTH_SRP6_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_CHAR_CREATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_CHAR_ENUM, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_CHAR_DELETE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_PLAYER_LOGIN, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandlePlayerLoginOpcode ); - OPCODE(SMSG_NEW_WORLD, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_TRANSFER_PENDING, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_TRANSFER_ABORTED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_CHARACTER_LOGIN_FAILED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_LOGIN_SETTIMESPEED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_GAMETIME_UPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_GAMETIME_SET, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_GAMETIME_SET, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_GAMESPEED_SET, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_GAMESPEED_SET, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_SERVERTIME, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_SERVERTIME, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_PLAYER_LOGOUT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePlayerLogoutOpcode ); - OPCODE(CMSG_LOGOUT_REQUEST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLogoutRequestOpcode ); - OPCODE(SMSG_LOGOUT_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_LOGOUT_COMPLETE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_LOGOUT_CANCEL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLogoutCancelOpcode ); - OPCODE(SMSG_LOGOUT_CANCEL_ACK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_NAME_QUERY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleNameQueryOpcode ); - OPCODE(SMSG_NAME_QUERY_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_PET_NAME_QUERY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePetNameQueryOpcode ); - OPCODE(SMSG_PET_NAME_QUERY_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_GUILD_QUERY, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildQueryOpcode ); - OPCODE(SMSG_GUILD_QUERY_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_PAGE_TEXT_QUERY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePageTextQueryOpcode ); - OPCODE(SMSG_PAGE_TEXT_QUERY_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_QUEST_QUERY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleQuestQueryOpcode ); - OPCODE(SMSG_QUEST_QUERY_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_GAMEOBJECT_QUERY, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleGameObjectQueryOpcode ); - OPCODE(SMSG_GAMEOBJECT_QUERY_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_CREATURE_QUERY, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleCreatureQueryOpcode ); - OPCODE(SMSG_CREATURE_QUERY_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_WHO, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleWhoOpcode ); - OPCODE(SMSG_WHO, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_WHOIS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleWhoisOpcode ); - OPCODE(SMSG_WHOIS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_CONTACT_LIST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleContactListOpcode ); - OPCODE(SMSG_CONTACT_LIST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_FRIEND_STATUS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_ADD_FRIEND, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAddFriendOpcode ); - OPCODE(CMSG_DEL_FRIEND, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleDelFriendOpcode ); - OPCODE(CMSG_SET_CONTACT_NOTES, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetContactNotesOpcode ); - OPCODE(CMSG_ADD_IGNORE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAddIgnoreOpcode ); - OPCODE(CMSG_DEL_IGNORE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleDelIgnoreOpcode ); - OPCODE(CMSG_GROUP_INVITE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGroupInviteOpcode ); - OPCODE(SMSG_GROUP_INVITE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_GROUP_CANCEL, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_GROUP_CANCEL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_GROUP_INVITE_RESPONSE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGroupInviteResponseOpcode ); - OPCODE(SMSG_GROUP_DECLINE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_GROUP_UNINVITE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGroupUninviteOpcode ); - OPCODE(CMSG_GROUP_UNINVITE_GUID, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGroupUninviteGuidOpcode ); - OPCODE(SMSG_GROUP_UNINVITE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_GROUP_SET_LEADER, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGroupSetLeaderOpcode ); - OPCODE(SMSG_GROUP_SET_LEADER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_LOOT_METHOD, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLootMethodOpcode ); - OPCODE(CMSG_GROUP_DISBAND, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGroupDisbandOpcode ); - OPCODE(SMSG_GROUP_DESTROYED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_GROUP_LIST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_PARTY_MEMBER_STATS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_PARTY_COMMAND_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(UMSG_UPDATE_GROUP_MEMBERS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_GUILD_CREATE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildCreateOpcode ); - OPCODE(CMSG_GUILD_INVITE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildInviteOpcode ); - OPCODE(SMSG_GUILD_INVITE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_GUILD_ACCEPT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildAcceptOpcode ); - OPCODE(CMSG_GUILD_DECLINE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildDeclineOpcode ); - OPCODE(SMSG_GUILD_DECLINE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_GUILD_INFO, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildInfoOpcode ); - //OPCODE(SMSG_GUILD_INFO, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_GUILD_ROSTER, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildRosterOpcode ); - OPCODE(SMSG_GUILD_ROSTER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_GUILD_PROMOTE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildPromoteOpcode ); - OPCODE(CMSG_GUILD_DEMOTE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildDemoteOpcode ); - OPCODE(CMSG_GUILD_SET_RANK, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildSetRankOpcode ); - OPCODE(CMSG_GUILD_SWITCH_RANK, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildSwitchRankOpcode ); - OPCODE(CMSG_GUILD_LEAVE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildLeaveOpcode ); - OPCODE(CMSG_GUILD_REMOVE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildRemoveOpcode ); - OPCODE(CMSG_GUILD_DISBAND, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildDisbandOpcode ); - OPCODE(CMSG_GUILD_LEADER, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildLeaderOpcode ); - OPCODE(CMSG_GUILD_MOTD, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildMOTDOpcode ); - OPCODE(SMSG_GUILD_EVENT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_GUILD_COMMAND_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_GUILD_AUTO_DECLINE_TOGGLE, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleGuildAutoDeclineToggleOpcode ); - OPCODE(CMSG_GUILD_AUTO_DECLINE, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleGuildDeclineOpcode ); - OPCODE(CMSG_GUILD_QUERY_RANKS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildQueryRanksOpcode ); - OPCODE(SMSG_GUILD_QUERY_RANKS_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(UMSG_UPDATE_GUILD, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_MESSAGECHAT_ADDON_BATTLEGROUND, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAddonMessagechatOpcode ); - OPCODE(CMSG_MESSAGECHAT_ADDON_GUILD, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAddonMessagechatOpcode ); - OPCODE(CMSG_MESSAGECHAT_ADDON_OFFICER, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAddonMessagechatOpcode ); - OPCODE(CMSG_MESSAGECHAT_ADDON_PARTY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAddonMessagechatOpcode ); - OPCODE(CMSG_MESSAGECHAT_ADDON_RAID, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAddonMessagechatOpcode ); - OPCODE(CMSG_MESSAGECHAT_ADDON_WHISPER, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAddonMessagechatOpcode ); - OPCODE(CMSG_MESSAGECHAT_AFK, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMessagechatOpcode ); - OPCODE(CMSG_MESSAGECHAT_BATTLEGROUND, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMessagechatOpcode ); - OPCODE(CMSG_MESSAGECHAT_CHANNEL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMessagechatOpcode ); - OPCODE(CMSG_MESSAGECHAT_DND, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMessagechatOpcode ); - OPCODE(CMSG_MESSAGECHAT_EMOTE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMessagechatOpcode ); - OPCODE(CMSG_MESSAGECHAT_GUILD, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMessagechatOpcode ); - OPCODE(CMSG_MESSAGECHAT_OFFICER, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMessagechatOpcode ); - OPCODE(CMSG_MESSAGECHAT_PARTY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMessagechatOpcode ); - OPCODE(CMSG_MESSAGECHAT_RAID, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMessagechatOpcode ); - OPCODE(CMSG_MESSAGECHAT_RAID_WARNING, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMessagechatOpcode ); - OPCODE(CMSG_MESSAGECHAT_SAY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMessagechatOpcode ); - OPCODE(CMSG_MESSAGECHAT_WHISPER, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMessagechatOpcode ); - OPCODE(CMSG_MESSAGECHAT_YELL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMessagechatOpcode ); - OPCODE(SMSG_MESSAGECHAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_JOIN_CHANNEL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleJoinChannelOpcode ); - OPCODE(CMSG_LEAVE_CHANNEL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLeaveChannelOpcode ); - OPCODE(SMSG_CHANNEL_NOTIFY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_CHANNEL_LIST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleChannelListOpcode ); - OPCODE(SMSG_CHANNEL_LIST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_CHANNEL_PASSWORD, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleChannelPasswordOpcode ); - OPCODE(CMSG_CHANNEL_SET_OWNER, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleChannelSetOwnerOpcode ); - OPCODE(CMSG_CHANNEL_OWNER, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleChannelOwnerOpcode ); - OPCODE(CMSG_CHANNEL_MODERATOR, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleChannelModeratorOpcode ); - OPCODE(CMSG_CHANNEL_UNMODERATOR, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleChannelUnmoderatorOpcode ); - OPCODE(CMSG_CHANNEL_MUTE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleChannelMuteOpcode ); - OPCODE(CMSG_CHANNEL_UNMUTE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleChannelUnmuteOpcode ); - OPCODE(CMSG_CHANNEL_INVITE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleChannelInviteOpcode ); - OPCODE(CMSG_CHANNEL_KICK, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleChannelKickOpcode ); - OPCODE(CMSG_CHANNEL_BAN, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleChannelBanOpcode ); - OPCODE(CMSG_CHANNEL_UNBAN, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleChannelUnbanOpcode ); - OPCODE(CMSG_CHANNEL_ANNOUNCEMENTS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleChannelAnnouncementsOpcode); - //OPCODE(CMSG_CHANNEL_MODERATE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleChannelModerateOpcode ); - OPCODE(SMSG_UPDATE_OBJECT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_DESTROY_OBJECT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_USE_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleUseItemOpcode ); - OPCODE(CMSG_OPEN_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleOpenItemOpcode ); - OPCODE(CMSG_READ_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleReadItemOpcode ); - OPCODE(SMSG_READ_ITEM_OK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_READ_ITEM_FAILED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_ITEM_COOLDOWN, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_GAMEOBJ_USE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGameObjectUseOpcode ); - //OPCODE(CMSG_DESTROY_ITEMS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_GAMEOBJECT_CUSTOM_ANIM, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_AREATRIGGER, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAreaTriggerOpcode ); - OPCODE(CMSG_MOVE_START_FORWARD, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); - OPCODE(CMSG_MOVE_START_BACKWARD, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); - OPCODE(CMSG_MOVE_STOP, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); - OPCODE(CMSG_MOVE_START_STRAFE_LEFT, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); - OPCODE(CMSG_MOVE_START_STRAFE_RIGHT, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); - OPCODE(CMSG_MOVE_STOP_STRAFE, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); - OPCODE(CMSG_MOVE_JUMP, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); - OPCODE(CMSG_MOVE_START_TURN_LEFT, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); - OPCODE(CMSG_MOVE_START_TURN_RIGHT, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); - OPCODE(CMSG_MOVE_STOP_TURN, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); - OPCODE(CMSG_MOVE_START_PITCH_UP, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); - OPCODE(CMSG_MOVE_START_PITCH_DOWN, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); - OPCODE(CMSG_MOVE_STOP_PITCH, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); - OPCODE(CMSG_MOVE_SET_RUN_MODE, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); - OPCODE(CMSG_MOVE_SET_WALK_MODE, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); - //OPCODE(MSG_MOVE_TOGGLE_LOGGING, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_MOVE_TELEPORT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(MSG_MOVE_TELEPORT_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_MOVE_TELEPORT_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMoveTeleportAckOpcode ); - //OPCODE(MSG_MOVE_TOGGLE_FALL_LOGGING, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_MOVE_FALL_LAND, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); - OPCODE(CMSG_MOVE_START_SWIM, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); - OPCODE(CMSG_MOVE_STOP_SWIM, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); - //OPCODE(MSG_MOVE_SET_RUN_SPEED_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_MOVE_SET_RUN_SPEED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(MSG_MOVE_SET_RUN_BACK_SPEED_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_MOVE_SET_RUN_BACK_SPEED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(MSG_MOVE_SET_WALK_SPEED_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_MOVE_SET_WALK_SPEED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(MSG_MOVE_SET_SWIM_SPEED_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_MOVE_SET_SWIM_SPEED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(MSG_MOVE_SET_SWIM_BACK_SPEED_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_MOVE_SET_SWIM_BACK_SPEED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(MSG_MOVE_SET_ALL_SPEED_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(MSG_MOVE_SET_TURN_RATE_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_MOVE_SET_TURN_RATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(MSG_MOVE_TOGGLE_COLLISION_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_MOVE_SET_FACING, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); - OPCODE(CMSG_MOVE_SET_PITCH, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); - OPCODE(MSG_MOVE_WORLDPORT_ACK, STATUS_TRANSFER, PROCESS_THREADUNSAFE, &WorldSession::HandleMoveWorldportAckOpcode ); - OPCODE(SMSG_MONSTER_MOVE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_MOVE_WATER_WALK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_MOVE_LAND_WALK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_MOVE_CHARM_PORT_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_MOVE_SET_RAW_POSITION, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_FORCE_RUN_SPEED_CHANGE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_FORCE_RUN_SPEED_CHANGE_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleForceSpeedChangeAckOpcodes); - //OPCODE(SMSG_FORCE_RUN_BACK_SPEED_CHANGE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_FORCE_RUN_BACK_SPEED_CHANGE_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleForceSpeedChangeAckOpcodes); - //OPCODE(SMSG_FORCE_SWIM_SPEED_CHANGE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_FORCE_SWIM_SPEED_CHANGE_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleForceSpeedChangeAckOpcodes); - OPCODE(SMSG_FORCE_MOVE_ROOT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_FORCE_MOVE_ROOT_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMoveRootAck ); - OPCODE(SMSG_FORCE_MOVE_UNROOT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_FORCE_MOVE_UNROOT_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMoveUnRootAck ); - //OPCODE(MSG_MOVE_ROOT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(MSG_MOVE_UNROOT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(MSG_MOVE_HEARTBEAT, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); - OPCODE(SMSG_MOVE_KNOCK_BACK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_MOVE_KNOCK_BACK_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMoveKnockBackAck ); - OPCODE(SMSG_MOVE_UPDATE_KNOCK_BACK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_MOVE_FEATHER_FALL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_MOVE_NORMAL_FALL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_MOVE_SET_HOVER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_MOVE_UNSET_HOVER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_MOVE_HOVER_ACK, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMoveHoverAck ); - //OPCODE(MSG_MOVE_HOVER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_SPLINE_MOVE_SET_WALK_SPEED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPLINE_MOVE_SET_RUN_SPEED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPLINE_MOVE_SET_RUN_BACK_SPEED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPLINE_MOVE_SET_SWIM_SPEED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPLINE_MOVE_SET_SWIM_BACK_SPEED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPLINE_MOVE_SET_TURN_RATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPLINE_MOVE_SET_FLIGHT_SPEED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPLINE_MOVE_SET_FLIGHT_BACK_SPEED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPLINE_MOVE_SET_PITCH_RATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_TRIGGER_CINEMATIC_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_OPENING_CINEMATIC, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_TRIGGER_CINEMATIC, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_NEXT_CINEMATIC_CAMERA, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleNextCinematicCamera ); - OPCODE(CMSG_COMPLETE_CINEMATIC, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCompleteCinematic ); - OPCODE(SMSG_TUTORIAL_FLAGS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_TUTORIAL_FLAG, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTutorialFlagOpcode ); - OPCODE(CMSG_TUTORIAL_CLEAR, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTutorialClearOpcode ); - OPCODE(CMSG_TUTORIAL_RESET, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTutorialResetOpcode ); - OPCODE(CMSG_STANDSTATECHANGE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleStandStateChangeOpcode ); - OPCODE(CMSG_EMOTE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleEmoteOpcode ); - OPCODE(SMSG_EMOTE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_TEXT_EMOTE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTextEmoteOpcode ); - OPCODE(SMSG_TEXT_EMOTE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_AUTOEQUIP_GROUND_ITEM, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_AUTOSTORE_GROUND_ITEM, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_AUTOSTORE_LOOT_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAutostoreLootItemOpcode ); - OPCODE(CMSG_LOOT_CURRENCY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAutostoreLootItemOpcode ); - //OPCODE(CMSG_STORE_LOOT_IN_SLOT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_AUTOEQUIP_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAutoEquipItemOpcode ); - OPCODE(CMSG_AUTOSTORE_BAG_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAutoStoreBagItemOpcode ); - OPCODE(CMSG_SWAP_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSwapItem ); - OPCODE(CMSG_SWAP_INV_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSwapInvItemOpcode ); - OPCODE(CMSG_SPLIT_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSplitItemOpcode ); - OPCODE(CMSG_AUTOEQUIP_ITEM_SLOT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAutoEquipItemSlotOpcode ); - //OPCODE(CMSG_UNCLAIM_LICENSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_DESTROYITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleDestroyItemOpcode ); - OPCODE(SMSG_INVENTORY_CHANGE_FAILURE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_OPEN_CONTAINER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_INSPECT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleInspectOpcode ); - OPCODE(SMSG_INSPECT_RESULTS_UPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_INITIATE_TRADE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleInitiateTradeOpcode ); - OPCODE(CMSG_BEGIN_TRADE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBeginTradeOpcode ); - OPCODE(CMSG_BUSY_TRADE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBusyTradeOpcode ); - OPCODE(CMSG_IGNORE_TRADE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleIgnoreTradeOpcode ); - OPCODE(CMSG_ACCEPT_TRADE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAcceptTradeOpcode ); - OPCODE(CMSG_UNACCEPT_TRADE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleUnacceptTradeOpcode ); - OPCODE(CMSG_CANCEL_TRADE, STATUS_LOGGEDIN_OR_RECENTLY_LOGGEDOUT, PROCESS_THREADUNSAFE, &WorldSession::HandleCancelTradeOpcode); - OPCODE(CMSG_SET_TRADE_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetTradeItemOpcode ); - OPCODE(CMSG_CLEAR_TRADE_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleClearTradeItemOpcode ); - OPCODE(CMSG_SET_TRADE_GOLD, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetTradeGoldOpcode ); - OPCODE(SMSG_TRADE_STATUS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_TRADE_STATUS_EXTENDED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_INITIALIZE_FACTIONS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SET_FACTION_VISIBLE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SET_FACTION_STANDING, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_SET_FACTION_ATWAR, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetFactionAtWarOpcode ); - //OPCODE(CMSG_SET_FACTION_CHEAT, STATUS_NEVER, PROCESS_THREADUNSAFE, &WorldSession::Handle_Deprecated ); - OPCODE(SMSG_SET_PROFICIENCY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_SET_ACTION_BUTTON, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetActionButtonOpcode ); - OPCODE(SMSG_ACTION_BUTTONS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_INITIAL_SPELLS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_LEARNED_SPELL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SUPERCEDED_SPELL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_NEW_SPELL_SLOT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_CAST_SPELL, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleCastSpellOpcode ); - OPCODE(CMSG_CANCEL_CAST, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleCancelCastOpcode ); - OPCODE(SMSG_CAST_FAILED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPELL_START, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPELL_GO, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPELL_FAILURE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPELL_COOLDOWN, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_COOLDOWN_EVENT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_CANCEL_AURA, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCancelAuraOpcode ); - OPCODE(SMSG_EQUIPMENT_SET_ID, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_PET_CAST_FAILED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_CHANNEL_START, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_CHANNEL_UPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_CANCEL_CHANNELLING, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCancelChanneling ); - OPCODE(SMSG_AI_REACTION, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_SET_SELECTION, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleSetSelectionOpcode ); - OPCODE(CMSG_EQUIPMENT_SET_DELETE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleEquipmentSetDeleteOpcode ); - //OPCODE(CMSG_INSTANCE_LOCK_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_DEBUG_PASSIVE_AURA, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_ATTACKSWING, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleAttackSwingOpcode ); - OPCODE(CMSG_ATTACKSTOP, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleAttackStopOpcode ); - OPCODE(SMSG_ATTACKSTART, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_ATTACKSTOP, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_ATTACKSWING_NOTINRANGE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_ATTACKSWING_BADFACING, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_PENDING_RAID_LOCK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_ATTACKSWING_DEADTARGET, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_ATTACKSWING_CANT_ATTACK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_ATTACKERSTATEUPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_BATTLEFIELD_PORT_DENIED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_PERFORM_ACTION_SET, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_RESUME_CAST_BAR, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_CANCEL_COMBAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPELLBREAKLOG, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPELLHEALLOG, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPELLENERGIZELOG, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_BREAK_TARGET, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_SAVE_PLAYER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_SETDEATHBINDPOINT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_BINDPOINTUPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_GETDEATHBINDZONE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_BINDZONEREPLY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_PLAYERBOUND, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_CLIENT_CONTROL_UPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_REPOP_REQUEST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleRepopRequestOpcode ); - OPCODE(SMSG_RESURRECT_REQUEST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_RESURRECT_RESPONSE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleResurrectResponseOpcode ); - OPCODE(CMSG_RETURN_TO_GRAVEYARD, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleReturnToGraveyard ); - OPCODE(CMSG_LOOT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLootOpcode ); - OPCODE(CMSG_LOOT_MONEY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLootMoneyOpcode ); - OPCODE(CMSG_LOOT_RELEASE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLootReleaseOpcode ); - OPCODE(SMSG_LOOT_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_LOOT_RELEASE_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_LOOT_REMOVED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_LOOT_CURRENCY_REMOVED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_LOOT_MONEY_NOTIFY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_LOOT_ITEM_NOTIFY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_LOOT_CLEAR_MONEY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_ITEM_PUSH_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_DUEL_REQUESTED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_DUEL_OUTOFBOUNDS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_DUEL_INBOUNDS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_DUEL_COMPLETE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_DUEL_WINNER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_DUEL_ACCEPTED, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleDuelAcceptedOpcode ); - OPCODE(CMSG_DUEL_CANCELLED, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleDuelCancelledOpcode ); - OPCODE(SMSG_MOUNTRESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_DISMOUNTRESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_REMOVED_FROM_PVP_QUEUE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_MOUNTSPECIAL_ANIM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMountSpecialAnimOpcode ); - OPCODE(SMSG_MOUNTSPECIAL_ANIM, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_PET_TAME_FAILURE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_PET_SET_ACTION, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePetSetAction ); - OPCODE(CMSG_PET_ACTION, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePetAction ); - OPCODE(CMSG_PET_ABANDON, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePetAbandon ); - OPCODE(CMSG_PET_RENAME, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePetRename ); - OPCODE(SMSG_PET_NAME_INVALID, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_PET_SPELLS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_PET_MODE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_GOSSIP_HELLO, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGossipHelloOpcode ); - OPCODE(CMSG_GOSSIP_SELECT_OPTION, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGossipSelectOptionOpcode ); - OPCODE(SMSG_GOSSIP_MESSAGE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_GOSSIP_COMPLETE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_NPC_TEXT_QUERY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleNpcTextQueryOpcode ); - OPCODE(SMSG_NPC_TEXT_UPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_NPC_WONT_TALK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_QUESTGIVER_STATUS_QUERY, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleQuestgiverStatusQueryOpcode); - OPCODE(SMSG_QUESTGIVER_STATUS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_QUESTGIVER_HELLO, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleQuestgiverHelloOpcode ); - OPCODE(SMSG_QUESTGIVER_QUEST_LIST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_QUESTGIVER_QUERY_QUEST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleQuestgiverQueryQuestOpcode); - //OPCODE(CMSG_QUESTGIVER_QUEST_AUTOLAUNCH, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleQuestgiverQuestAutoLaunch ); - OPCODE(SMSG_QUESTGIVER_QUEST_DETAILS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); // + - OPCODE(CMSG_QUESTGIVER_ACCEPT_QUEST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleQuestgiverAcceptQuestOpcode); - OPCODE(CMSG_QUESTGIVER_COMPLETE_QUEST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleQuestgiverCompleteQuest ); - OPCODE(SMSG_QUESTGIVER_REQUEST_ITEMS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_QUESTGIVER_REQUEST_REWARD, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleQuestgiverRequestRewardOpcode); - OPCODE(SMSG_QUESTGIVER_OFFER_REWARD, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_QUESTGIVER_CHOOSE_REWARD, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleQuestgiverChooseRewardOpcode); - OPCODE(SMSG_QUESTGIVER_QUEST_INVALID, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_QUESTGIVER_CANCEL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleQuestgiverCancel ); - OPCODE(SMSG_QUESTGIVER_QUEST_COMPLETE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_QUESTGIVER_QUEST_FAILED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_QUESTLOG_SWAP_QUEST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleQuestLogSwapQuest ); - OPCODE(CMSG_QUESTLOG_REMOVE_QUEST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleQuestLogRemoveQuest ); - OPCODE(SMSG_QUESTLOG_FULL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_QUESTUPDATE_FAILED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_QUESTUPDATE_FAILEDTIMER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_QUESTUPDATE_COMPLETE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_QUESTUPDATE_ADD_KILL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_QUESTUPDATE_ADD_ITEM_OBSOLETE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_QUEST_CONFIRM_ACCEPT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleQuestConfirmAccept ); - OPCODE(SMSG_QUEST_CONFIRM_ACCEPT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_PUSHQUESTTOPARTY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePushQuestToParty ); - OPCODE(CMSG_LIST_INVENTORY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleListInventoryOpcode ); - OPCODE(SMSG_LIST_INVENTORY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_LOAD_SCREEN, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleLoadScreenOpcode ); - OPCODE(CMSG_SELL_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSellItemOpcode ); - OPCODE(SMSG_SELL_ITEM, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_BUY_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBuyItemOpcode ); - OPCODE(SMSG_BUY_ITEM, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_BUY_FAILED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_TAXICLEARALLNODES, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_TAXIENABLEALLNODES, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_TAXISHOWNODES, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_SHOWTAXINODES, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_TAXINODE_STATUS_QUERY, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleTaxiNodeStatusQueryOpcode ); - OPCODE(SMSG_TAXINODE_STATUS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_TAXIQUERYAVAILABLENODES, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleTaxiQueryAvailableNodes ); - OPCODE(CMSG_ACTIVATETAXI, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleActivateTaxiOpcode ); - OPCODE(SMSG_ACTIVATETAXIREPLY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_NEW_TAXI_PATH, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_TRAINER_LIST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTrainerListOpcode ); - OPCODE(SMSG_TRAINER_LIST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_TRAINER_BUY_SPELL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTrainerBuySpellOpcode ); - OPCODE(SMSG_TRAINER_SERVICE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_TRAINER_BUY_FAILED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_BINDER_ACTIVATE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBinderActivateOpcode ); - OPCODE(SMSG_PLAYERBINDERROR, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_BANKER_ACTIVATE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBankerActivateOpcode ); - OPCODE(SMSG_SHOW_BANK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_BUY_BANK_SLOT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBuyBankSlotOpcode ); - OPCODE(CMSG_PETITION_SHOWLIST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePetitionShowListOpcode ); - OPCODE(SMSG_PETITION_SHOWLIST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_PETITION_BUY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePetitionBuyOpcode ); - OPCODE(CMSG_PETITION_SHOW_SIGNATURES, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePetitionShowSignOpcode ); - OPCODE(SMSG_PETITION_SHOW_SIGNATURES, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_PETITION_SIGN, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePetitionSignOpcode ); - OPCODE(SMSG_PETITION_SIGN_RESULTS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(MSG_PETITION_DECLINE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePetitionDeclineOpcode ); - OPCODE(CMSG_OFFER_PETITION, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleOfferPetitionOpcode ); - OPCODE(CMSG_TURN_IN_PETITION, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTurnInPetitionOpcode ); - OPCODE(SMSG_TURN_IN_PETITION_RESULTS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_PETITION_QUERY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePetitionQueryOpcode ); - OPCODE(SMSG_PETITION_QUERY_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_FISH_NOT_HOOKED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_FISH_ESCAPED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_BUG, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBugOpcode ); - OPCODE(SMSG_NOTIFICATION, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_PLAYED_TIME, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePlayedTime ); - OPCODE(SMSG_PLAYED_TIME, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_QUERY_TIME, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleQueryTimeOpcode ); - OPCODE(SMSG_QUERY_TIME_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_LOG_XPGAIN, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_AURACASTLOG, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_RECLAIM_CORPSE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleReclaimCorpseOpcode ); - OPCODE(CMSG_WRAP_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleWrapItemOpcode ); - OPCODE(SMSG_LEVELUP_INFO, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(MSG_MINIMAP_PING, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMinimapPingOpcode ); - //OPCODE(SMSG_RESISTLOG, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_ENCHANTMENTLOG, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_SET_SKILL_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_START_MIRROR_TIMER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_PAUSE_MIRROR_TIMER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_STOP_MIRROR_TIMER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_PING, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_EarlyProccess ); - OPCODE(SMSG_PONG, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_CLEAR_COOLDOWNS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_GAMEOBJECT_PAGETEXT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_SETSHEATHED, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleSetSheathedOpcode ); - OPCODE(SMSG_COOLDOWN_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPELL_DELAYED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_QUEST_POI_QUERY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleQuestPOIQueryOpcode ); - OPCODE(SMSG_QUEST_POI_QUERY_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_GHOST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_GM_INVIS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_INVALID_PROMOTION_CODE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(MSG_GM_BIND_OTHER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(MSG_GM_SUMMON, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_ITEM_TIME_UPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_ITEM_ENCHANT_TIME_UPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(MSG_GM_SHOWLABEL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_PET_CAST_SPELL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePetCastSpellOpcode ); - OPCODE(MSG_SAVE_GUILD_EMBLEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSaveGuildEmblemOpcode ); - OPCODE(MSG_TABARDVENDOR_ACTIVATE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTabardVendorActivateOpcode); - OPCODE(SMSG_PLAY_SPELL_VISUAL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_ZONEUPDATE, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleZoneUpdateOpcode ); - OPCODE(SMSG_PARTYKILLLOG, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_COMPRESSED_UPDATE_OBJECT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_EXPLORATION_EXPERIENCE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_GM_SET_SECURITY_GROUP, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_GM_NUKE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(MSG_RANDOM_ROLL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleRandomRollOpcode ); - OPCODE(SMSG_ENVIRONMENTALDAMAGELOG, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_CHANGEPLAYER_DIFFICULTY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_RWHOIS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_LFG_PLAYER_REWARD, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_LFG_TELEPORT_DENIED, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_UNLEARN_SPELL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_UNLEARN_SKILL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleUnlearnSkillOpcode ); - OPCODE(SMSG_REMOVED_SPELL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_DECHARGE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_GMTICKET_CREATE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGMTicketCreateOpcode ); - OPCODE(SMSG_GMTICKET_CREATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_GMTICKET_UPDATETEXT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGMTicketUpdateTextOpcode ); - OPCODE(SMSG_GMTICKET_UPDATETEXT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_ACCOUNT_DATA_TIMES, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_REQUEST_ACCOUNT_DATA, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleRequestAccountData ); - OPCODE(CMSG_UPDATE_ACCOUNT_DATA, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleUpdateAccountData ); - OPCODE(SMSG_UPDATE_ACCOUNT_DATA, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_CLEAR_FAR_SIGHT_IMMEDIATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_CHANGEPLAYER_DIFFICULTY_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_GM_TEACH, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_GM_CREATE_ITEM_TARGET, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_GMTICKET_GETTICKET, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGMTicketGetTicketOpcode ); - OPCODE(SMSG_GMTICKET_GETTICKET, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_UNLEARN_TALENTS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_INSTANCE_ENCOUNTER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_GAMEOBJECT_DESPAWN_ANIM, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(MSG_CORPSE_QUERY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCorpseQueryOpcode ); - OPCODE(CMSG_GMTICKET_DELETETICKET, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGMTicketDeleteTicketOpcode); - OPCODE(SMSG_GMTICKET_DELETETICKET, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_CHAT_WRONG_FACTION, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_GMTICKET_SYSTEMSTATUS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGMTicketSystemStatusOpcode); - OPCODE(SMSG_GMTICKET_SYSTEMSTATUS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_SPIRIT_HEALER_ACTIVATE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSpiritHealerActivateOpcode); - //OPCODE(CMSG_SET_STAT_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_QUEST_FORCE_REMOVED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_SKILL_BUY_STEP, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_SKILL_BUY_RANK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_XP_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_SPIRIT_HEALER_CONFIRM, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_CHARACTER_POINT_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_GOSSIP_POI, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_CHAT_IGNORED, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleChatIgnoredOpcode ); - //OPCODE(CMSG_GM_VISION, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_SERVER_COMMAND, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_GM_SILENCE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_GM_REVEALTO, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_GM_RESURRECT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_GM_SUMMONMOB, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_GM_MOVECORPSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_GM_FREEZE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_GM_UBERINVIS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_GM_REQUEST_PLAYER_INFO, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_GM_PLAYER_INFO, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_GUILD_RANK, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildRankOpcode ); - OPCODE(CMSG_GUILD_ADD_RANK, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildAddRankOpcode ); - OPCODE(CMSG_GUILD_DEL_RANK, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildDelRankOpcode ); - OPCODE(CMSG_GUILD_SET_NOTE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildSetNoteOpcode ); - OPCODE(SMSG_LOGIN_VERIFY_WORLD, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_CLEAR_EXPLORATION, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_SEND_MAIL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSendMail ); - OPCODE(SMSG_SEND_MAIL_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_GET_MAIL_LIST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGetMailList ); - OPCODE(SMSG_MAIL_LIST_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_BATTLEFIELD_LIST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBattlefieldListOpcode ); - OPCODE(SMSG_BATTLEFIELD_LIST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_BATTLEFIELD_JOIN, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_FORCE_SET_VEHICLE_REC_ID, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_SET_VEHICLE_REC_ID_ACK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_TAXICLEARNODE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_TAXIENABLENODE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_ITEM_TEXT_QUERY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleItemTextQuery ); - OPCODE(SMSG_ITEM_TEXT_QUERY_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_MAIL_TAKE_MONEY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMailTakeMoney ); - OPCODE(CMSG_MAIL_TAKE_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMailTakeItem ); - OPCODE(CMSG_MAIL_MARK_AS_READ, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMailMarkAsRead ); - OPCODE(CMSG_MAIL_RETURN_TO_SENDER, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMailReturnToSender ); - OPCODE(CMSG_MAIL_DELETE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMailDelete ); - OPCODE(CMSG_MAIL_CREATE_TEXT_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMailCreateTextItem ); - OPCODE(SMSG_SPELLLOGMISS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPELLLOGEXECUTE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_DEBUGAURAPROC, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_PERIODICAURALOG, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPELLDAMAGESHIELD, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPELLNONMELEEDAMAGELOG, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_LEARN_TALENT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLearnTalentOpcode ); - //OPCODE(SMSG_RESURRECT_FAILED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_TOGGLE_PVP, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTogglePvP ); - OPCODE(SMSG_ZONE_UNDER_ATTACK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(MSG_AUCTION_HELLO, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAuctionHelloOpcode ); - OPCODE(CMSG_AUCTION_SELL_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAuctionSellItem ); - OPCODE(CMSG_AUCTION_REMOVE_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAuctionRemoveItem ); - OPCODE(CMSG_AUCTION_LIST_ITEMS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAuctionListItems ); - OPCODE(CMSG_AUCTION_LIST_OWNER_ITEMS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAuctionListOwnerItems ); - OPCODE(CMSG_AUCTION_PLACE_BID, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAuctionPlaceBid ); - OPCODE(SMSG_AUCTION_COMMAND_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_AUCTION_LIST_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_AUCTION_OWNER_LIST_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_AUCTION_BIDDER_NOTIFICATION, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_AUCTION_OWNER_NOTIFICATION, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_PROCRESIST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_COMBAT_EVENT_FAILED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_DISPEL_FAILED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPELLOGDAMAGE_IMMUNE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_AUCTION_LIST_BIDDER_ITEMS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAuctionListBidderItems ); - OPCODE(SMSG_AUCTION_BIDDER_LIST_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SET_FLAT_SPELL_MODIFIER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SET_PCT_SPELL_MODIFIER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_SET_AMMO, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetAmmoOpcode ); - OPCODE(SMSG_CORPSE_RECLAIM_DELAY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_SET_ACTIVE_MOVER, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetActiveMoverOpcode ); - OPCODE(CMSG_PET_CANCEL_AURA, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePetCancelAuraOpcode ); - //OPCODE(CMSG_PLAYER_AI_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_CANCEL_AUTO_REPEAT_SPELL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCancelAutoRepeatSpellOpcode); - //OPCODE(MSG_GM_ACCOUNT_ONLINE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(MSG_LIST_STABLED_PETS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleListStabledPetsOpcode ); - //OPCODE(CMSG_STABLE_PET, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleStablePet ); - //OPCODE(CMSG_UNSTABLE_PET, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleUnstablePet ); - //OPCODE(CMSG_BUY_STABLE_SLOT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBuyStableSlot ); - OPCODE(SMSG_STABLE_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_STABLE_REVIVE_PET, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleStableRevivePet ); - //OPCODE(CMSG_STABLE_SWAP_PET, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleStableSwapPet ); - OPCODE(MSG_QUEST_PUSH_RESULT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleQuestPushResult ); - OPCODE(SMSG_PLAY_MUSIC, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_PLAY_OBJECT_SOUND, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_PLAY_ONE_SHOT_ANIM_KIT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_REQUEST_PET_INFO, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleRequestPetInfoOpcode ); - OPCODE(CMSG_FAR_SIGHT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleFarSightOpcode ); - OPCODE(SMSG_SPELLDISPELLOG, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_DAMAGE_CALC_LOG, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_ENABLE_DAMAGE_LOG, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_GROUP_CHANGE_SUB_GROUP, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGroupChangeSubGroupOpcode ); - OPCODE(CMSG_REQUEST_PARTY_MEMBER_STATS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleRequestPartyMemberStatsOpcode); - //OPCODE(CMSG_GROUP_SWAP_SUB_GROUP, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_RESET_FACTION_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_AUTOSTORE_BANK_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAutoStoreBankItemOpcode ); - OPCODE(CMSG_AUTOBANK_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAutoBankItemOpcode ); - OPCODE(MSG_QUERY_NEXT_MAIL_TIME, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleQueryNextMailTime ); - OPCODE(SMSG_RECEIVED_MAIL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_RAID_GROUP_ONLY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_SET_DURABILITY_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_SET_PVP_RANK_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_ADD_PVP_MEDAL_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_DEL_PVP_MEDAL_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_SET_PVP_TITLE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_PVP_CREDIT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_AUCTION_REMOVED_NOTIFICATION, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_GROUP_RAID_CONVERT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGroupRaidConvertOpcode ); - OPCODE(CMSG_GROUP_ASSISTANT_LEADER, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGroupAssistantLeaderOpcode); - OPCODE(CMSG_BUYBACK_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBuybackItem ); - OPCODE(SMSG_SERVER_MESSAGE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_SET_SAVED_INSTANCE_EXTEND, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_LFG_OFFER_CONTINUE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_TEST_DROP_RATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_TEST_DROP_RATE_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_LFG_GET_STATUS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_SHOW_MAILBOX, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_RESET_RANGED_COMBAT_TIMER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_CHAT_NOT_IN_PARTY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_GMTICKETSYSTEM_TOGGLE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_CANCEL_GROWTH_AURA, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCancelGrowthAuraOpcode ); - OPCODE(SMSG_CANCEL_AUTO_REPEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_STANDSTATE_UPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_LOOT_ALL_PASSED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_LOOT_ROLL_WON, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_LOOT_ROLL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLootRoll ); - OPCODE(SMSG_LOOT_START_ROLL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_LOOT_ROLL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_LOOT_MASTER_GIVE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLootMasterGiveOpcode ); - OPCODE(SMSG_LOOT_MASTER_LIST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SET_FORCED_REACTIONS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPELL_FAILED_OTHER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_GAMEOBJECT_RESET_STATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_REPAIR_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleRepairItemOpcode ); - OPCODE(SMSG_CHAT_PLAYER_NOT_FOUND, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(MSG_TALENT_WIPE_CONFIRM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTalentWipeConfirmOpcode ); - OPCODE(SMSG_SUMMON_REQUEST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_SUMMON_RESPONSE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSummonResponseOpcode ); - //OPCODE(MSG_DEV_SHOWLABEL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_MONSTER_MOVE_TRANSPORT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_PET_BROKEN, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(MSG_MOVE_FEATHER_FALL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(MSG_MOVE_WATER_WALK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_SERVER_BROADCAST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_SELF_RES, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSelfResOpcode ); - //OPCODE(SMSG_FEIGN_DEATH_RESISTED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_RUN_SCRIPT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_SCRIPT_MESSAGE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_DUEL_COUNTDOWN, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_AREA_TRIGGER_MESSAGE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_SHOWING_HELM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleShowingHelmOpcode ); - OPCODE(CMSG_SHOWING_CLOAK, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleShowingCloakOpcode ); - //OPCODE(SMSG_ROLE_CHOSEN, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_PLAYER_SKINNED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_DURABILITY_DAMAGE_DEATH, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_SET_EXPLORATION, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_SET_ACTIONBAR_TOGGLES, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleSetActionBarTogglesOpcode ); - //OPCODE(UMSG_DELETE_GUILD_CHARTER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(MSG_PETITION_RENAME, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePetitionRenameOpcode ); - OPCODE(SMSG_INIT_WORLD_STATES, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_UPDATE_WORLD_STATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_PET_ACTION_FEEDBACK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_CHAR_RENAME, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleCharRenameOpcode ); - OPCODE(SMSG_CHAR_RENAME, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_MOVE_SPLINE_DONE, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMoveSplineDoneOpcode ); - OPCODE(CMSG_MOVE_FALL_RESET, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); - OPCODE(SMSG_INSTANCE_SAVE_CREATED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_RAID_INSTANCE_INFO, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_REQUEST_RAID_INFO, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleRequestRaidInfoOpcode ); - OPCODE(CMSG_MOVE_TIME_SKIPPED, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleMoveTimeSkippedOpcode ); - OPCODE(CMSG_MOVE_FEATHER_FALL_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleFeatherFallAck ); - OPCODE(CMSG_MOVE_WATER_WALK_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMoveWaterWalkAck ); - OPCODE(CMSG_MOVE_NOT_ACTIVE_MOVER, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMoveNotActiveMoverOpcode ); - OPCODE(SMSG_PLAY_SOUND, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_BATTLEFIELD_STATUS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBattlefieldStatusOpcode ); - OPCODE(SMSG_BATTLEFIELD_STATUS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_BATTLEFIELD_STATUS_ACTIVE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_BATTLEFIELD_STATUS_FAILED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_BATTLEFIELD_STATUS_NEEDCONFIRMATION, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_BATTLEFIELD_STATUS_QUEUED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_BATTLEFIELD_STATUS_WAITFORGROUPS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_BATTLEFIELD_PORT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBattleFieldPortOpcode ); - OPCODE(CMSG_INSPECT_HONOR_STATS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleInspectHonorStatsOpcode ); - OPCODE(SMSG_INSPECT_HONOR_STATS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_BATTLEMASTER_HELLO, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBattlemasterHelloOpcode ); - //OPCODE(CMSG_MOVE_START_SWIM_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_MOVE_STOP_SWIM_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_FORCE_WALK_SPEED_CHANGE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_FORCE_WALK_SPEED_CHANGE_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleForceSpeedChangeAckOpcodes); - //OPCODE(SMSG_FORCE_SWIM_BACK_SPEED_CHANGE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_FORCE_SWIM_BACK_SPEED_CHANGE_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleForceSpeedChangeAckOpcodes); - //OPCODE(SMSG_FORCE_TURN_RATE_CHANGE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_FORCE_TURN_RATE_CHANGE_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleForceSpeedChangeAckOpcodes); - OPCODE(CMSG_PVP_LOG_DATA, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePVPLogDataOpcode ); - OPCODE(SMSG_PVP_LOG_DATA, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_LEAVE_BATTLEFIELD, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLeaveBattlefieldOpcode ); - OPCODE(CMSG_AREA_SPIRIT_HEALER_QUERY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAreaSpiritHealerQueryOpcode); - OPCODE(CMSG_AREA_SPIRIT_HEALER_QUEUE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAreaSpiritHealerQueueOpcode); - OPCODE(SMSG_AREA_SPIRIT_HEALER_TIME, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_GM_UNTEACH, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_WARDEN_DATA, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_WARDEN_DATA, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleWardenDataOpcode ); - OPCODE(CMSG_BATTLEGROUND_PLAYER_POSITIONS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBattleGroundPlayerPositionsOpcode); - OPCODE(SMSG_BATTLEGROUND_PLAYER_POSITIONS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_PET_STOP_ATTACK, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePetStopAttack ); - OPCODE(SMSG_BINDER_CONFIRM, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_BATTLEGROUND_PLAYER_JOINED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_BATTLEGROUND_PLAYER_LEFT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_BATTLEMASTER_JOIN, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBattlemasterJoinOpcode ); - OPCODE(SMSG_ADDON_INFO, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_PET_UNLEARN, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePetUnlearnOpcode ); - //OPCODE(SMSG_PET_UNLEARN_CONFIRM, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_PARTY_MEMBER_STATS_FULL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_PET_SPELL_AUTOCAST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePetSpellAutocastOpcode ); - OPCODE(SMSG_WEATHER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_PLAY_TIME_WARNING, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_MINIGAME_SETUP, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_MINIGAME_STATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_MINIGAME_MOVE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_MINIGAME_MOVE_FAILED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_RAID_INSTANCE_MESSAGE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_COMPRESSED_MOVES, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_GUILD_INFO_TEXT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildChangeInfoTextOpcode ); - OPCODE(SMSG_CHAT_RESTRICTED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_SPLINE_SET_RUN_SPEED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_SPLINE_SET_RUN_BACK_SPEED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_SPLINE_SET_SWIM_SPEED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_SPLINE_SET_WALK_SPEED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_SPLINE_SET_SWIM_BACK_SPEED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_SPLINE_SET_TURN_RATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPLINE_MOVE_UNROOT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_SPLINE_MOVE_FEATHER_FALL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_SPLINE_MOVE_NORMAL_FALL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPLINE_MOVE_SET_HOVER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPLINE_MOVE_UNSET_HOVER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPLINE_MOVE_WATER_WALK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPLINE_MOVE_LAND_WALK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPLINE_MOVE_START_SWIM, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_SPLINE_MOVE_STOP_SWIM, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPLINE_MOVE_SET_RUN_MODE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPLINE_MOVE_SET_WALK_MODE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_GM_NUKE_ACCOUNT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(MSG_GM_DESTROY_CORPSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_GM_DESTROY_ONLINE_CORPSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_ACTIVATETAXIEXPRESS, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleActivateTaxiExpressOpcode ); - //OPCODE(SMSG_SET_FACTION_ATWAR, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_GAMETIMEBIAS_SET, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_DEBUG_ACTIONS_START, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_DEBUG_ACTIONS_STOP, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_SET_FACTION_INACTIVE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetFactionInactiveOpcode ); - OPCODE(CMSG_SET_WATCHED_FACTION, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetWatchedFactionOpcode ); - OPCODE(MSG_MOVE_TIME_SKIPPED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_SPLINE_MOVE_ROOT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_SET_EXPLORATION_ALL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_INVALIDATE_PLAYER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_RESET_INSTANCES, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleResetInstancesOpcode ); - OPCODE(SMSG_INSTANCE_RESET, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_INSTANCE_RESET_FAILED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_UPDATE_LAST_INSTANCE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(MSG_RAID_TARGET_UPDATE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleRaidTargetUpdateOpcode ); - OPCODE(MSG_RAID_READY_CHECK, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleRaidReadyCheckOpcode ); - //OPCODE(CMSG_LUA_USAGE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_PET_ACTION_SOUND, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_PET_DISMISS_SOUND, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_GHOSTEE_GONE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_GM_UPDATE_TICKET_STATUS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_GM_TICKET_STATUS_UPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(MSG_SET_DUNGEON_DIFFICULTY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetDungeonDifficultyOpcode); - OPCODE(CMSG_GMSURVEY_SUBMIT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGMSurveySubmitOpcode ); - OPCODE(SMSG_UPDATE_INSTANCE_OWNERSHIP, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_IGNORE_KNOCKBACK_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_CHAT_PLAYER_AMBIGUOUS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(MSG_DELAY_GHOST_TELEPORT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_SPELLINSTAKILLLOG, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPELL_UPDATE_CHAIN_TARGETS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_CHAT_FILTERED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_EXPECTED_SPAM_RECORDS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPELLSTEALLOG, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_LOTTERY_QUERY_OBSOLETE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_LOTTERY_QUERY_RESULT_OBSOLETE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_BUY_LOTTERY_TICKET_OBSOLETE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_LOTTERY_RESULT_OBSOLETE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_CHARACTER_PROFILE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_CHARACTER_PROFILE_REALM_CONNECTED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_DEFENSE_MESSAGE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_WORLD_SERVER_INFO, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(MSG_GM_RESETINSTANCELIMIT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_MOTD, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_MOVE_SET_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_MOVE_UNSET_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_MOVE_SET_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY_ACK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(MSG_MOVE_START_SWIM_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(MSG_MOVE_STOP_SWIM_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_MOVE_SET_CAN_FLY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_MOVE_UNSET_CAN_FLY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_MOVE_SET_CAN_FLY_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMoveSetCanFlyAckOpcode ); - //OPCODE(CMSG_MOVE_SET_FLY, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); - OPCODE(CMSG_SOCKET_GEMS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSocketOpcode ); - OPCODE(CMSG_ARENA_TEAM_CREATE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleArenaTeamCreateOpcode ); - OPCODE(SMSG_ARENA_TEAM_COMMAND_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(MSG_MOVE_UPDATE_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_ARENA_TEAM_QUERY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleArenaTeamQueryOpcode ); - OPCODE(SMSG_ARENA_TEAM_QUERY_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_ARENA_TEAM_ROSTER, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleArenaTeamRosterOpcode ); - OPCODE(SMSG_ARENA_TEAM_ROSTER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_ARENA_TEAM_INVITE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleArenaTeamInviteOpcode ); - OPCODE(SMSG_ARENA_TEAM_INVITE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_ARENA_TEAM_ACCEPT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleArenaTeamAcceptOpcode ); - OPCODE(CMSG_ARENA_TEAM_DECLINE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleArenaTeamDeclineOpcode ); - OPCODE(CMSG_ARENA_TEAM_LEAVE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleArenaTeamLeaveOpcode ); - OPCODE(CMSG_ARENA_TEAM_REMOVE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleArenaTeamRemoveOpcode ); - OPCODE(CMSG_ARENA_TEAM_DISBAND, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleArenaTeamDisbandOpcode ); - OPCODE(CMSG_ARENA_TEAM_LEADER, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleArenaTeamLeaderOpcode ); - OPCODE(SMSG_ARENA_TEAM_EVENT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_BATTLEMASTER_JOIN_ARENA, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBattlemasterJoinArena ); - OPCODE(CMSG_MOVE_START_ASCEND, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); - OPCODE(CMSG_MOVE_STOP_ASCEND, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); - OPCODE(SMSG_ARENA_TEAM_STATS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_LFG_JOIN, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLfgJoinOpcode ); - //OPCODE(CMSG_LFG_LEAVE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLfgLeaveOpcode ); - //OPCODE(CMSG_LFG_SEARCH_JOIN, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSearchLfgJoinOpcode ); - //OPCODE(CMSG_LFG_SEARCH_LEAVE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSearchLfgLeaveOpcode ); - //OPCODE(SMSG_LFG_SEARCH_RESULTS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_LFG_PROPOSAL_UPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_LFG_PROPOSAL_RESPONSE, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_LFG_ROLE_CHECK_UPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_LFG_JOIN_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_LFG_QUEUE_STATUS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_SET_LFG_COMMENT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetLfgCommentOpcode ); - //OPCODE(SMSG_LFG_UPDATE_PLAYER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_LFG_UPDATE_PARTY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_LFG_UPDATE_SEARCH, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_LFG_SET_ROLES, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_LFG_SET_NEEDS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_LFG_BOOT_PLAYER_VOTE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_LFG_BOOT_PLAYER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_LFG_GET_PLAYER_INFO, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_LFG_PLAYER_INFO, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_LFG_TELEPORT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_LFG_GET_PARTY_INFO, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_LFG_PARTY_INFO, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_TITLE_EARNED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_SET_TITLE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetTitleOpcode ); - OPCODE(CMSG_CANCEL_MOUNT_AURA, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCancelMountAuraOpcode ); - OPCODE(SMSG_ARENA_ERROR, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(MSG_INSPECT_ARENA_TEAMS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleInspectArenaTeamsOpcode ); - OPCODE(SMSG_DEATH_RELEASE_LOC, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_CANCEL_TEMP_ENCHANTMENT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCancelTempEnchantmentOpcode); - //OPCODE(SMSG_FORCED_DEATH_UPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_CHEAT_SET_HONOR_CURRENCY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_CHEAT_SET_ARENA_CURRENCY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(MSG_MOVE_SET_FLIGHT_SPEED_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_MOVE_SET_FLIGHT_SPEED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(MSG_MOVE_SET_FLIGHT_BACK_SPEED_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_MOVE_SET_FLIGHT_BACK_SPEED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_FORCE_FLIGHT_SPEED_CHANGE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_FORCE_FLIGHT_SPEED_CHANGE_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleForceSpeedChangeAckOpcodes); - //OPCODE(SMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleForceSpeedChangeAckOpcodes); - //OPCODE(SMSG_SPLINE_SET_FLIGHT_SPEED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_SPLINE_SET_FLIGHT_BACK_SPEED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_MAELSTROM_INVALIDATE_CACHE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_FLIGHT_SPLINE_SYNC, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_SET_TAXI_BENCHMARK_MODE, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleSetTaxiBenchmarkOpcode ); - //OPCODE(SMSG_JOINED_BATTLEGROUND_QUEUE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_REALM_SPLIT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_REALM_SPLIT, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleRealmSplitOpcode ); - OPCODE(CMSG_MOVE_CHNG_TRANSPORT, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); - OPCODE(MSG_PARTY_ASSIGNMENT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePartyAssignmentOpcode ); - //OPCODE(SMSG_OFFER_PETITION_ERROR, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_TIME_SYNC_REQ, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_TIME_SYNC_RESP, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleTimeSyncResp ); - //OPCODE(CMSG_SEND_LOCAL_EVENT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_SEND_GENERAL_TRIGGER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_SEND_COMBAT_TRIGGER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_MAELSTROM_GM_SENT_MAIL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_RESET_FAILED_NOTIFY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_REAL_GROUP_UPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_LFG_DISABLED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_ACTIVE_PVP_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_CHEAT_DUMP_ITEMS_DEBUG_ONLY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_CHEAT_DUMP_ITEMS_DEBUG_ONLY_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_CHEAT_DUMP_ITEMS_DEBUG_ONLY_RESPONSE_WRITE_FILE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_UPDATE_COMBO_POINTS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_VOICE_SESSION_ROSTER_UPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_VOICE_SESSION_LEAVE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_VOICE_SESSION_ADJUST_PRIORITY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_VOICE_SET_TALKER_MUTED_REQUEST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_VOICE_SET_TALKER_MUTED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_INIT_EXTRA_AURA_INFO_OBSOLETE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_SET_EXTRA_AURA_INFO_OBSOLETE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_SET_EXTRA_AURA_INFO_NEED_UPDATE_OBSOLETE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_CLEAR_EXTRA_AURA_INFO_OBSOLETE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_MOVE_START_DESCEND, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); - //OPCODE(CMSG_IGNORE_REQUIREMENTS_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_IGNORE_REQUIREMENTS_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_SPELL_CHANCE_PROC_LOG, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_MOVE_SET_RUN_SPEED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_DISMOUNT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(MSG_MOVE_UPDATE_CAN_FLY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(MSG_RAID_READY_CHECK_CONFIRM, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_VOICE_SESSION_ENABLE, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleVoiceSessionEnableOpcode ); - //OPCODE(SMSG_VOICE_SESSION_ENABLE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_VOICE_PARENTAL_CONTROLS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_GM_WHISPER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_GM_MESSAGECHAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(MSG_GM_GEARRATING, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_COMMENTATOR_ENABLE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_COMMENTATOR_STATE_CHANGED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_COMMENTATOR_GET_MAP_INFO, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_COMMENTATOR_MAP_INFO, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_COMMENTATOR_GET_PLAYER_INFO, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_COMMENTATOR_GET_PLAYER_INFO, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_COMMENTATOR_PLAYER_INFO, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_COMMENTATOR_ENTER_INSTANCE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_COMMENTATOR_EXIT_INSTANCE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_COMMENTATOR_INSTANCE_COMMAND, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_CLEAR_TARGET, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_BOT_DETECTED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_CROSSED_INEBRIATION_THRESHOLD, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_CHEAT_PLAYER_LOGIN, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_CHEAT_PLAYER_LOOKUP, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_CHEAT_PLAYER_LOOKUP, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_KICK_REASON, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(MSG_RAID_READY_CHECK_FINISHED, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleRaidReadyCheckFinishedOpcode); - OPCODE(CMSG_COMPLAIN, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleComplainOpcode ); - OPCODE(SMSG_COMPLAIN_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_FEATURE_SYSTEM_STATUS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_GM_SHOW_COMPLAINTS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_GM_UNSQUELCH, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_CHANNEL_SILENCE_VOICE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_CHANNEL_SILENCE_ALL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_CHANNEL_UNSILENCE_VOICE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_CHANNEL_UNSILENCE_ALL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_TARGET_CAST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_TARGET_SCRIPT_CAST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_CHANNEL_DISPLAY_LIST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleChannelDisplayListQueryOpcode); - OPCODE(CMSG_SET_ACTIVE_VOICE_CHANNEL, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleSetActiveVoiceChannel ); - //OPCODE(CMSG_GET_CHANNEL_MEMBER_COUNT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGetChannelMemberCountOpcode); - OPCODE(SMSG_CHANNEL_MEMBER_COUNT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_CHANNEL_VOICE_ON, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleChannelVoiceOnOpcode ); - //OPCODE(CMSG_CHANNEL_VOICE_OFF, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_DEBUG_LIST_TARGETS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_DEBUG_LIST_TARGETS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_AVAILABLE_VOICE_CHANNEL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_ADD_VOICE_IGNORE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_DEL_VOICE_IGNORE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_PARTY_SILENCE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_PARTY_UNSILENCE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(MSG_NOTIFY_PARTY_SQUELCH, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_COMSAT_RECONNECT_TRY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_COMSAT_DISCONNECT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_COMSAT_CONNECT_FAIL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_VOICE_CHAT_STATUS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_REPORT_PVP_AFK, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleReportPvPAFK ); - OPCODE(SMSG_REPORT_PVP_AFK_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_GUILD_BANKER_ACTIVATE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildBankerActivate ); - OPCODE(CMSG_GUILD_BANK_QUERY_TAB, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildBankQueryTab ); - OPCODE(SMSG_GUILD_BANK_LIST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_GUILD_BANK_SWAP_ITEMS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildBankSwapItems ); - OPCODE(CMSG_GUILD_BANK_BUY_TAB, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildBankBuyTab ); - OPCODE(CMSG_GUILD_BANK_UPDATE_TAB, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildBankUpdateTab ); - OPCODE(CMSG_GUILD_BANK_DEPOSIT_MONEY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildBankDepositMoney ); - OPCODE(CMSG_GUILD_BANK_WITHDRAW_MONEY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildBankWithdrawMoney ); - OPCODE(CMSG_GUILD_BANK_LOG_QUERY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildBankLogQuery ); - OPCODE(SMSG_GUILD_BANK_LOG_QUERY_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_SET_CHANNEL_WATCH, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetChannelWatchOpcode ); - OPCODE(SMSG_USERLIST_ADD, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_USERLIST_REMOVE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_USERLIST_UPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_CLEAR_CHANNEL_WATCH, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_INSPECT_RESULTS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_GOGOGO_OBSOLETE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_ECHO_PARTY_SQUELCH, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_SET_TITLE_SUFFIX, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_SPELLCLICK, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSpellClick ); - OPCODE(SMSG_LOOT_LIST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_GM_CHARACTER_RESTORE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_GM_CHARACTER_SAVE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_VOICESESSION_FULL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_GUILD_PERMISSIONS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildPermissions ); - OPCODE(SMSG_GUILD_PERMISSIONS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_GUILD_BANK_MONEY_WITHDRAWN, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildBankMoneyWithdrawn ); - OPCODE(SMSG_GUILD_BANK_MONEY_WITHDRAWN, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_GUILD_EVENT_LOG_QUERY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildEventLogQueryOpcode ); - OPCODE(SMSG_GUILD_EVENT_LOG, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_MAELSTROM_RENAME_GUILD, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_GET_MIRRORIMAGE_DATA, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleGetMirrorimageData ); - OPCODE(SMSG_MIRRORIMAGE_DATA, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_FORCE_DISPLAY_UPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_SPELL_CHANCE_RESIST_PUSHBACK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_IGNORE_DIMINISHING_RETURNS_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_IGNORE_DIMINISHING_RETURNS_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_KEEP_ALIVE, STATUS_NEVER, PROCESS_THREADUNSAFE, &WorldSession::Handle_EarlyProccess ); - //OPCODE(SMSG_RAID_READY_CHECK_ERROR, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_OPT_OUT_OF_LOOT, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleOptOutOfLootOpcode ); - OPCODE(CMSG_QUERY_GUILD_BANK_TEXT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleQueryGuildBankTabText ); - OPCODE(SMSG_GUILD_BANK_TEXT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_SET_GUILD_BANK_TEXT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetGuildBankTabText ); - //OPCODE(CMSG_SET_GRANTABLE_LEVELS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_GRANT_LEVEL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_REFER_A_FRIEND, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(MSG_GM_CHANGE_ARENA_RATING, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_DECLINE_CHANNEL_INVITE, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_GROUPACTION_THROTTLED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_OVERRIDE_LIGHT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_TOTEM_CREATED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_TOTEM_DESTROYED, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTotemDestroyed ); - //OPCODE(CMSG_EXPIRE_RAID_INSTANCE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_NO_SPELL_VARIANCE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleQuestgiverStatusMultipleQuery); - OPCODE(SMSG_QUESTGIVER_STATUS_MULTIPLE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_SET_PLAYER_DECLINED_NAMES, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleSetPlayerDeclinedNamesOpcode); - OPCODE(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_QUERY_SERVER_BUCK_DATA, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_CLEAR_SERVER_BUCK_DATA, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_SERVER_BUCK_DATA, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SEND_UNLEARN_SPELLS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_PROPOSE_LEVEL_GRANT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_ACCEPT_LEVEL_GRANT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_REFER_A_FRIEND_FAILURE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPLINE_MOVE_SET_FLYING, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPLINE_MOVE_UNSET_FLYING, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SUMMON_CANCEL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_CHANGE_PERSONAL_ARENA_RATING, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_ALTER_APPEARANCE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAlterAppearanceOpcode ); - OPCODE(SMSG_ENABLE_BARBER_SHOP, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_BARBER_SHOP_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_CALENDAR_GET_CALENDAR, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCalendarGetCalendar ); - //OPCODE(CMSG_CALENDAR_GET_EVENT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCalendarGetEvent ); - //OPCODE(CMSG_CALENDAR_GUILD_FILTER, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCalendarGuildFilter ); - //OPCODE(CMSG_CALENDAR_ARENA_TEAM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCalendarArenaTeam ); - OPCODE(CMSG_CALENDAR_ADD_EVENT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCalendarAddEvent ); - //OPCODE(CMSG_CALENDAR_UPDATE_EVENT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCalendarUpdateEvent ); - //OPCODE(CMSG_CALENDAR_REMOVE_EVENT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCalendarRemoveEvent ); - //OPCODE(CMSG_CALENDAR_COPY_EVENT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCalendarCopyEvent ); - OPCODE(CMSG_CALENDAR_EVENT_INVITE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCalendarEventInvite ); - //OPCODE(CMSG_CALENDAR_EVENT_RSVP, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCalendarEventRsvp ); - //OPCODE(CMSG_CALENDAR_EVENT_REMOVE_INVITE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCalendarEventRemoveInvite ); - //OPCODE(CMSG_CALENDAR_EVENT_STATUS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCalendarEventStatus ); - //OPCODE(CMSG_CALENDAR_EVENT_MODERATOR_STATUS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCalendarEventModeratorStatus); - OPCODE(SMSG_CALENDAR_SEND_CALENDAR, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_CALENDAR_SEND_EVENT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_CALENDAR_FILTER_GUILD, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_CALENDAR_ARENA_TEAM, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_CALENDAR_EVENT_INVITE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_CALENDAR_EVENT_INVITE_REMOVED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_CALENDAR_EVENT_STATUS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_CALENDAR_COMMAND_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_CALENDAR_RAID_LOCKOUT_ADDED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_CALENDAR_RAID_LOCKOUT_REMOVED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_CALENDAR_EVENT_INVITE_ALERT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_CALENDAR_EVENT_INVITE_REMOVED_ALERT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_CALENDAR_EVENT_INVITE_STATUS_ALERT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_CALENDAR_EVENT_REMOVED_ALERT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_CALENDAR_EVENT_UPDATED_ALERT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_CALENDAR_EVENT_MODERATOR_STATUS_ALERT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_CALENDAR_COMPLAIN, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCalendarComplain ); - OPCODE(CMSG_CALENDAR_GET_NUM_PENDING, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCalendarGetNumPending ); - OPCODE(SMSG_CALENDAR_SEND_NUM_PENDING, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_SAVE_DANCE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_NOTIFY_DANCE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_PLAY_DANCE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_PLAY_DANCE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_LOAD_DANCES, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_STOP_DANCE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_STOP_DANCE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_SYNC_DANCE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_DANCE_QUERY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_DANCE_QUERY_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_INVALIDATE_DANCE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_DELETE_DANCE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_LEARNED_DANCE_MOVES, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_LEARN_DANCE_MOVE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_UNLEARN_DANCE_MOVE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_SET_RUNE_COUNT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_SET_RUNE_COOLDOWN, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(MSG_MOVE_SET_PITCH_RATE_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_MOVE_SET_PITCH_RATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_FORCE_PITCH_RATE_CHANGE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_FORCE_PITCH_RATE_CHANGE_ACK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_SPLINE_SET_PITCH_RATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_CALENDAR_EVENT_INVITE_NOTES, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_CALENDAR_EVENT_INVITE_NOTES, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_CALENDAR_EVENT_INVITE_NOTES_ALERT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_UPDATE_MISSILE_TRAJECTORY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_UPDATE_ACCOUNT_DATA_COMPLETE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_TRIGGER_MOVIE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_COMPLETE_MOVIE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_SET_GLYPH_SLOT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_SET_GLYPH, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_ACHIEVEMENT_EARNED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_DYNAMIC_DROP_ROLL_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_CRITERIA_UPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_QUERY_INSPECT_ACHIEVEMENTS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleQueryInspectAchievementsOpcode); - OPCODE(SMSG_RESPOND_INSPECT_ACHIEVEMENTS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_DISMISS_CONTROLLED_VEHICLE, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleDismissControlledVehicle ); - //OPCODE(CMSG_COMPLETE_ACHIEVEMENT_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_QUESTUPDATE_ADD_PVP_KILL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_SET_CRITERIA_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_CALENDAR_RAID_LOCKOUT_UPDATED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_UNITANIMTIER_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_CHAR_CUSTOMIZE, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleCharCustomizeOpcode ); - OPCODE(SMSG_CHAR_CUSTOMIZE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_PET_RENAMEABLE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_REQUEST_VEHICLE_EXIT, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_REQUEST_VEHICLE_PREV_SEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_REQUEST_VEHICLE_NEXT_SEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_REQUEST_VEHICLE_SWITCH_SEAT, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_PET_LEARN_TALENT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePetLearnTalent ); - //OPCODE(CMSG_PET_UNLEARN_TALENTS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_SET_PHASE_SHIFT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_ALL_ACHIEVEMENT_DATA, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_FORCE_SAY_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_HEALTH_UPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_POWER_UPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_GAMEOBJ_REPORT_USE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGameobjectReportUse ); - OPCODE(SMSG_HIGHEST_THREAT_UPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_THREAT_UPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_THREAT_REMOVE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_THREAT_CLEAR, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_CONVERT_RUNE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_RESYNC_RUNES, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_ADD_RUNE_POWER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_START_QUEST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_REMOVE_GLYPH, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleRemoveGlyphOpcode ); - //OPCODE(CMSG_DUMP_OBJECTS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_DUMP_OBJECTS_DATA, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_DISMISS_CRITTER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_NOTIFY_DEST_LOC_SPELL_CAST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_AUCTION_LIST_PENDING_SALES, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAuctionListPendingSales ); - OPCODE(SMSG_AUCTION_LIST_PENDING_SALES, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_MODIFY_COOLDOWN, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_PET_UPDATE_COMBO_POINTS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_ENABLETAXI, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleTaxiQueryAvailableNodes ); - OPCODE(SMSG_PRE_RESURRECT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_AURA_UPDATE_ALL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_AURA_UPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_FLOOD_GRACE_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_SERVER_FIRST_ACHIEVEMENT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_PET_LEARNED_SPELL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_PET_REMOVED_SPELL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_CHANGE_SEATS_ON_CONTROLLED_VEHICLE, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_HEARTH_AND_RESURRECT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleHearthandResurrect ); - OPCODE(SMSG_ON_CANCEL_EXPECTED_RIDE_VEHICLE_AURA, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_CRITERIA_DELETED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_ACHIEVEMENT_DELETED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_SERVER_INFO_QUERY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_SERVER_INFO_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_CHECK_LOGIN_CRITERIA, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_SERVER_BUCK_DATA_START, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_SET_BREATH, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_QUERY_VEHICLE_STATUS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_BATTLEGROUND_INFO_THROTTLED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_SET_VEHICLE_REC_ID, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_RIDE_VEHICLE_INTERACT, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleRideVehicleInteract ); - //OPCODE(CMSG_CONTROLLER_EJECT_PASSENGER, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleEjectPassenger ); - OPCODE(SMSG_PET_GUIDS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_CLIENTCACHE_VERSION, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_CHANGE_GDF_ARENA_RATING, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_SET_ARENA_TEAM_RATING_BY_INDEX, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_SET_ARENA_TEAM_WEEKLY_GAMES, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_SET_ARENA_TEAM_SEASON_GAMES, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_SET_ARENA_MEMBER_WEEKLY_GAMES, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_SET_ARENA_MEMBER_SEASON_GAMES, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_SET_ITEM_PURCHASE_DATA, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_GET_ITEM_PURCHASE_DATA, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleItemRefundInfoRequest ); - OPCODE(CMSG_ITEM_PURCHASE_REFUND, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_ITEM_PURCHASE_REFUND_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_CORPSE_TRANSPORT_QUERY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCorpseMapPositionQueryOpcode); - OPCODE(SMSG_CORPSE_TRANSPORT_QUERY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_UNUSED5, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_UNUSED6, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_CALENDAR_EVENT_SIGNUP, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCalendarEventSignup ); - //OPCODE(SMSG_CALENDAR_CLEAR_PENDING_ACTION, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_LOAD_EQUIPMENT_SET, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_SAVE_EQUIPMENT_SET, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleEquipmentSetSaveOpcode ); - //OPCODE(CMSG_ON_MISSILE_TRAJECTORY_COLLISION, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_NOTIFY_MISSILE_TRAJECTORY_COLLISION, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_TALENT_UPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_LEARN_TALENT_GROUP, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLearnPreviewTalents ); - OPCODE(CMSG_PET_LEARN_TALENT_GROUP, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLearnPreviewTalentsPet ); - //OPCODE(CMSG_SET_ACTIVE_TALENT_GROUP_OBSOLETE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_GM_GRANT_ACHIEVEMENT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_GM_REMOVE_ACHIEVEMENT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_GM_SET_CRITERIA_FOR_PLAYER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_DESTROY_ARENA_UNIT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_ARENA_TEAM_CHANGE_FAILED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_PROFILEDATA_REQUEST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_PROFILEDATA_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_START_BATTLEFIELD_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_END_BATTLEFIELD_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_COMPOUND_MOVE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_MOVE_GRAVITY_DISABLE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_MOVE_GRAVITY_DISABLE_ACK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_MOVE_GRAVITY_ENABLE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_MOVE_GRAVITY_ENABLE_ACK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(MSG_MOVE_GRAVITY_CHNG, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPLINE_MOVE_GRAVITY_DISABLE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SPLINE_MOVE_GRAVITY_ENABLE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_USE_EQUIPMENT_SET, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleEquipmentSetUseOpcode ); - OPCODE(SMSG_USE_EQUIPMENT_SET_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_FORCE_ANIM, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_FORCE_ANIM, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_CHAR_FACTION_CHANGE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_CHAR_FACTION_CHANGE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_PVP_QUEUE_STATS_REQUEST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_PVP_QUEUE_STATS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_SET_PAID_SERVICE_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_BATTLEFIELD_MANAGER_ENTRY_INVITE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_BATTLEFIELD_MANAGER_ENTRY_INVITE_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_BATTLEFIELD_MANAGER_ENTERING, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_BATTLEFIELD_MANAGER_QUEUE_INVITE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_BATTLEFIELD_MANAGER_QUEUE_INVITE_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_BATTLEFIELD_MANAGER_QUEUE_REQUEST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_BATTLEFIELD_MANAGER_QUEUE_REQUEST_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_BATTLEFIELD_MANAGER_EJECT_PENDING, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_BATTLEFIELD_MANAGER_EJECTED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_BATTLEFIELD_MANAGER_EXIT_REQUEST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_BATTLEFIELD_MANAGER_STATE_CHANGED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_BATTLEFIELD_MANAGER_ADVANCE_STATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_BATTLEFIELD_MANAGER_SET_NEXT_TRANSITION_TIME, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(MSG_SET_RAID_DIFFICULTY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetRaidDifficultyOpcode ); - //OPCODE(CMSG_XPGAIN, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_XPGAIN, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_GMTICKET_RESPONSE_ERROR, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_GMTICKET_GET_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_GMTICKET_RESOLVE_RESPONSE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGMResponseResolveOpcode ); - OPCODE(SMSG_GMTICKET_RESOLVE_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_GMTICKET_CREATE_RESPONSE_TICKET, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_GM_CREATE_TICKET_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_SERVERINFO, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_SERVERINFO, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_UI_TIME_REQUEST, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleUITimeRequestOpcode ); - OPCODE(SMSG_UI_TIME, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_CHAR_RACE_CHANGE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(MSG_VIEW_PHASE_SHIFT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_TALENTS_INVOLUNTARILY_RESET, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_DEBUG_SERVER_GEO, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_DEBUG_SERVER_GEO, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_LOOT_UPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(UMSG_UPDATE_GROUP_INFO, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_READY_FOR_ACCOUNT_DATA_TIMES, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleReadyForAccountDataTimesOpcode); - OPCODE(CMSG_QUERY_GET_ALL_QUESTS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleQueryQuestsCompletedOpcode); - OPCODE(SMSG_ALL_QUESTS_COMPLETED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_GMLAGREPORT_SUBMIT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_AFK_MONITOR_INFO_REQUEST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_AFK_MONITOR_INFO_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_AFK_MONITOR_INFO_CLEAR, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_AREA_TRIGGER_NO_CORPSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_GM_NUKE_CHARACTER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_LOW_LEVEL_RAID, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleSetAllowLowLevelRaidOpcode); - OPCODE(CMSG_LOW_LEVEL_RAID_USER, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleSetAllowLowLevelRaidOpcode); - OPCODE(SMSG_CAMERA_SHAKE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SOCKET_GEMS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_SET_CHARACTER_MODEL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_CONNECT_TO, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_CONNECT_TO_FAILED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_SUSPEND_COMMS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_SUSPEND_COMMS_ACK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_RESUME_COMMS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_AUTH_CONTINUED_SESSION, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_DROP_NEW_CONNECTION, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_SEND_ALL_COMBAT_LOG, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_OPEN_LFG_DUNGEON_FINDER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_MOVE_SET_COLLISION_HGT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_MOVE_SET_COLLISION_HGT_ACK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(MSG_MOVE_SET_COLLISION_HGT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_CLEAR_RANDOM_BG_WIN_TIME, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_CLEAR_HOLIDAY_BG_WIN_TIME, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_COMMENTATOR_SKIRMISH_QUEUE_COMMAND, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //OPCODE(SMSG_COMMENTATOR_SKIRMISH_QUEUE_RESULT1, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_COMMENTATOR_SKIRMISH_QUEUE_RESULT2, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_COMPRESSED_UNKNOWN_1310, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_PLAYER_MOVE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_REORDER_CHARACTERS, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleReorderCharactersOpcode ); - OPCODE(SMSG_SET_CURRENCY_WEEK_LIMIT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SET_CURRENCY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_SEND_CURRENCIES, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_SET_CURRENCY_FLAGS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetCurrencyFlagsOpcode ); - OPCODE(SMSG_WEEKLY_RESET_CURRENCIES, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_INSPECT_RATED_BG_STATS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL ); - //OPCODE(CMSG_REQUEST_RATED_BG_INFO, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL ); - OPCODE(CMSG_REQUEST_RATED_BG_STATS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleRequestRatedBGStatsOpcode ); - OPCODE(SMSG_RATED_BG_STATS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_REQUEST_PVP_REWARDS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleRequestPvPRewardsOpcode ); - OPCODE(SMSG_PVP_REWARDS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_REQUEST_PVP_OPTIONS_ENABLED, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleRequestPvPOptionsEnabledOpcode ); - OPCODE(SMSG_PVP_OPTIONS_ENABLED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_REQUEST_HOTFIX, STATUS_AUTHED, PROCESS_INPLACE, &WorldSession::HandleRequestHotfix ); - OPCODE(SMSG_DB_REPLY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_OBJECT_UPDATE_FAILED, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleObjectUpdateFailedOpcode ); - OPCODE(CMSG_REFORGE_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleReforgeItemOpcode ); - OPCODE(SMSG_REFORGE_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_START_TIMER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); -}; diff --git a/src/game/Opcodes.h b/src/game/Opcodes.h deleted file mode 100644 index 6a78187ae..000000000 --- a/src/game/Opcodes.h +++ /dev/null @@ -1,1477 +0,0 @@ -/** - * This code is part of MaNGOS. Contributor & Copyright details are in AUTHORS/THANKS. - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/// \addtogroup u2w -/// @{ -/// \file - -#ifndef _OPCODES_H -#define _OPCODES_H - -#include "Common.h" - -// Note: this include need for be sure have full definition of class WorldSession -// if this class definition not complite then VS for x64 release use different size for -// struct OpcodeHandler in this header and Opcode.cpp and get totally wrong data from -// table opcodeTable in source when Opcode.h included but WorldSession.h not included -#include "WorldSession.h" - -/// List of Opcodes -/// Max Opcode value in 4.3.4.15595 is 65535 -enum Opcodes -{ - MSG_WOW_CONNECTION = 0x4F57, // 4.3.4 15595 - SMSG_AUTH_CHALLENGE = 0x4542, // 4.3.4 15595 - CMSG_AUTH_SESSION = 0x0449, // 4.3.4 15595 - SMSG_AUTH_RESPONSE = 0x5DB6, // 4.3.4 15595 - MSG_NULL_ACTION = 0x1001, - CMSG_BOOTME = 0x1002, - CMSG_DBLOOKUP = 0x1003, - SMSG_DBLOOKUP = 0x1004, - CMSG_QUERY_OBJECT_POSITION = 0x1005, - SMSG_QUERY_OBJECT_POSITION = 0x1006, - CMSG_QUERY_OBJECT_ROTATION = 0x1007, - SMSG_QUERY_OBJECT_ROTATION = 0x1008, - CMSG_WORLD_TELEPORT = 0x24B2, // 4.3.4 15595 - CMSG_TELEPORT_TO_UNIT = 0x4206, // 4.3.4 15595 - CMSG_ZONE_MAP = 0x100B, - SMSG_ZONE_MAP = 0x100C, - CMSG_DEBUG_CHANGECELLZONE = 0x100D, - CMSG_MOVE_CHARACTER_CHEAT = 0x100E, - SMSG_MOVE_CHARACTER_CHEAT = 0x100F, - CMSG_RECHARGE = 0x1010, - CMSG_LEARN_SPELL = 0x1011, - CMSG_CREATEMONSTER = 0x1012, - CMSG_DESTROYMONSTER = 0x1013, - CMSG_CREATEITEM = 0x1014, - CMSG_CREATEGAMEOBJECT = 0x1015, - SMSG_CHECK_FOR_BOTS = 0x1016, - CMSG_MAKEMONSTERATTACKGUID = 0x1017, - CMSG_BOT_DETECTED2 = 0x1018, - CMSG_FORCEACTION = 0x1019, - CMSG_FORCEACTIONONOTHER = 0x101A, - CMSG_FORCEACTIONSHOW = 0x101B, - SMSG_FORCEACTIONSHOW = 0x101C, - CMSG_PETGODMODE = 0x101D, - SMSG_PETGODMODE = 0x101E, - SMSG_REFER_A_FRIEND_EXPIRED = 0x101F, - CMSG_WEATHER_SPEED_CHEAT = 0x1020, - CMSG_UNDRESSPLAYER = 0x1021, - CMSG_BEASTMASTER = 0x1022, - CMSG_GODMODE = 0x1023, - SMSG_GODMODE = 0x1024, - CMSG_CHEAT_SETMONEY = 0x1025, - CMSG_LEVEL_CHEAT = 0x1026, - CMSG_PET_LEVEL_CHEAT = 0x1027, - CMSG_SET_WORLDSTATE = 0x1028, - CMSG_COOLDOWN_CHEAT = 0x1029, - CMSG_USE_SKILL_CHEAT = 0x102A, - CMSG_FLAG_QUEST = 0x102B, - CMSG_FLAG_QUEST_FINISH = 0x102C, - CMSG_CLEAR_QUEST = 0x102D, - CMSG_SEND_EVENT = 0x102E, - CMSG_DEBUG_AISTATE = 0x102F, - SMSG_DEBUG_AISTATE = 0x1030, - CMSG_DISABLE_PVP_CHEAT = 0x1031, - CMSG_ADVANCE_SPAWN_TIME = 0x1032, - SMSG_DESTRUCTIBLE_BUILDING_DAMAGE = 0x4825, // 4.3.4 15595 - CMSG_AUTH_SRP6_BEGIN = 0x1034, - CMSG_AUTH_SRP6_PROOF = 0x1035, - CMSG_AUTH_SRP6_RECODE = 0x1036, - CMSG_CHAR_CREATE = 0x4A36, // 4.3.4 15595 - CMSG_CHAR_ENUM = 0x0502, // 4.3.4 15595 - CMSG_CHAR_DELETE = 0x6425, // 4.3.4 15595 - SMSG_AUTH_SRP6_RESPONSE = 0x103A, - SMSG_CHAR_CREATE = 0x2D05, // 4.3.4 15595 - SMSG_CHAR_ENUM = 0x10B0, // 4.3.4 15595 - SMSG_CHAR_DELETE = 0x0304, // 4.3.4 15595 - CMSG_PLAYER_LOGIN = 0x05B1, // 4.3.4 15595 - SMSG_NEW_WORLD = 0x79B1, // 4.3.4 15595 - SMSG_TRANSFER_PENDING = 0x18A6, // 4.3.4 15595 - SMSG_TRANSFER_ABORTED = 0x0537, // 4.3.4 15595 - SMSG_CHARACTER_LOGIN_FAILED = 0x4417, // 4.3.4 15595 - SMSG_LOGIN_SETTIMESPEED = 0x4D15, // 4.3.4 15595 - SMSG_GAMETIME_UPDATE = 0x4127, // 4.3.4 15595 - CMSG_GAMETIME_SET = 0x1045, - SMSG_GAMETIME_SET = 0x0014, // 4.3.4 15595 - CMSG_GAMESPEED_SET = 0x1047, - SMSG_GAMESPEED_SET = 0x1048, - CMSG_SERVERTIME = 0x1049, - SMSG_SERVERTIME = 0x6327, // 4.3.4 15595 - CMSG_PLAYER_LOGOUT = 0x104B, - CMSG_LOGOUT_REQUEST = 0x0A25, // 4.3.4 15595 - SMSG_LOGOUT_RESPONSE = 0x0524, // 4.3.4 15595 - SMSG_LOGOUT_COMPLETE = 0x2137, // 4.3.4 15595 - CMSG_LOGOUT_CANCEL = 0x2324, // 4.3.4 15595 - SMSG_LOGOUT_CANCEL_ACK = 0x6514, // 4.3.4 15595 - CMSG_NAME_QUERY = 0x2224, // 4.3.4 15595 - SMSG_NAME_QUERY_RESPONSE = 0x6E04, // 4.3.4 15595 - CMSG_PET_NAME_QUERY = 0x6F24, // 4.3.4 15595 - SMSG_PET_NAME_QUERY_RESPONSE = 0x4C37, // 4.3.4 15595 - CMSG_GUILD_QUERY = 0x4426, // 4.3.4 15595 - SMSG_GUILD_QUERY_RESPONSE = 0x0E06, // 4.3.4 15595 - CMSG_PAGE_TEXT_QUERY = 0x6614, // 4.3.4 15595 - SMSG_PAGE_TEXT_QUERY_RESPONSE = 0x2B14, // 4.3.4 15595 - CMSG_QUEST_QUERY = 0x0D06, // 4.3.4 15595 - SMSG_QUEST_QUERY_RESPONSE = 0x6936, // 4.3.4 15595 - CMSG_GAMEOBJECT_QUERY = 0x4017, // 4.3.4 15595 - SMSG_GAMEOBJECT_QUERY_RESPONSE = 0x0915, // 4.3.4 15595 - CMSG_CREATURE_QUERY = 0x2706, // 4.3.4 15595 - SMSG_CREATURE_QUERY_RESPONSE = 0x6024, // 4.3.4 15595 - CMSG_WHO = 0x6C15, // 4.3.4 15595 - SMSG_WHO = 0x6907, // 4.3.4 15595 - CMSG_WHOIS = 0x6B05, // 4.3.4 15595 - SMSG_WHOIS = 0x6917, // 4.3.4 15595 - CMSG_CONTACT_LIST = 0x4534, // 4.3.4 15595 - SMSG_CONTACT_LIST = 0x6017, // 4.3.4 15595 - SMSG_FRIEND_STATUS = 0x0717, // 4.3.4 15595 - CMSG_ADD_FRIEND = 0x6527, // 4.3.4 15595 - CMSG_DEL_FRIEND = 0x6A15, // 4.3.4 15595 - CMSG_SET_CONTACT_NOTES = 0x6135, // 4.3.4 15595 - CMSG_ADD_IGNORE = 0x4726, // 4.3.4 15595 - CMSG_DEL_IGNORE = 0x6D26, // 4.3.4 15595 - CMSG_GROUP_INVITE = 0x0513, // 4.3.4 15595 - SMSG_GROUP_INVITE = 0x31B2, // 4.3.4 15595 - CMSG_GROUP_CANCEL = 0x1071, - SMSG_GROUP_CANCEL = 0x4D25, // 4.3.4 15595 - CMSG_GROUP_INVITE_RESPONSE = 0x0410, // 4.3.4 15595 - SMSG_GROUP_DECLINE = 0x6835, // 4.3.4 15595 - CMSG_GROUP_UNINVITE = 0x1076, - CMSG_GROUP_UNINVITE_GUID = 0x2E07, // 4.3.4 15595 - SMSG_GROUP_UNINVITE = 0x0A07, // 4.3.4 15595 - CMSG_GROUP_SET_LEADER = 0x4C17, // 4.3.4 15595 - SMSG_GROUP_SET_LEADER = 0x0526, // 4.3.4 15595 - CMSG_LOOT_METHOD = 0x2F24, // 4.3.4 15595 - CMSG_GROUP_DISBAND = 0x2804, // 4.3.4 15595 - SMSG_GROUP_DESTROYED = 0x2207, // 4.3.4 15595 - SMSG_GROUP_LIST = 0x4C24, // 4.3.4 15595 - SMSG_PARTY_MEMBER_STATS = 0x2104, // 4.3.4 15595 - SMSG_PARTY_COMMAND_RESULT = 0x6E07, // 4.3.4 15595 - UMSG_UPDATE_GROUP_MEMBERS = 0x1081, - CMSG_GUILD_CREATE = 0x1082, - CMSG_GUILD_INVITE = 0x24B0, // 4.3.4 15595 - SMSG_GUILD_INVITE = 0x14A2, // 4.3.4 15595 - CMSG_GUILD_ACCEPT = 0x2531, // 4.3.4 15595 - CMSG_GUILD_DECLINE = 0x3231, // 4.3.4 15595 - SMSG_GUILD_DECLINE = 0x2C07, // 4.3.4 15595 - CMSG_GUILD_INFO = 0x1088, - SMSG_GUILD_INFO = 0x1089, - CMSG_GUILD_ROSTER = 0x1226, // 4.3.4 15595 - SMSG_GUILD_ROSTER = 0x3DA3, // 4.3.4 15595 - CMSG_GUILD_PROMOTE = 0x1030, // 4.3.4 15595 - CMSG_GUILD_SET_RANK = 0x3032, // 4.3.4 15595 - CMSG_GUILD_SWITCH_RANK = 0x1221, // 4.3.4 15595 - CMSG_GUILD_DEMOTE = 0x1020, // 4.3.4 15595 - CMSG_GUILD_LEAVE = 0x1021, // 4.3.4 15595 - CMSG_GUILD_REMOVE = 0x1231, // 4.3.4 15595 - CMSG_GUILD_DISBAND = 0x3226, // 4.3.4 15595 - CMSG_GUILD_LEADER = 0x3034, // 4.3.4 15595 - CMSG_GUILD_MOTD = 0x1035, // 4.3.4 15595 - SMSG_GUILD_EVENT = 0x0705, // 4.3.4 15595 - SMSG_GUILD_COMMAND_RESULT = 0x7DB3, // 4.3.4 15595 - CMSG_GUILD_AUTO_DECLINE_TOGGLE = 0x2034, // 4.3.4 15595 - CMSG_GUILD_AUTO_DECLINE = 0x1234, // 4.3.4 15595 - CMSG_GUILD_QUERY_RANKS = 0x1026, // 4.3.4 15595 - SMSG_GUILD_QUERY_RANKS_RESULT = 0x30B4, // 4.3.4 15595 - UMSG_UPDATE_GUILD = 0x1095, - CMSG_MESSAGECHAT_ADDON_BATTLEGROUND = 0x0D46, // 4.3.4 15595 - CMSG_MESSAGECHAT_ADDON_GUILD = 0x0544, // 4.3.4 15595 - CMSG_MESSAGECHAT_ADDON_OFFICER = 0x3954, // 4.3.4 15595 - CMSG_MESSAGECHAT_ADDON_PARTY = 0x0546, // 4.3.4 15595 - CMSG_MESSAGECHAT_ADDON_RAID = 0x1D56, // 4.3.4 15595 - CMSG_MESSAGECHAT_ADDON_WHISPER = 0x2146, // 4.3.4 15595 - CMSG_MESSAGECHAT_AFK = 0x0D44, // 4.3.4 15595 - CMSG_MESSAGECHAT_BATTLEGROUND = 0x2156, // 4.3.4 15595 - CMSG_MESSAGECHAT_CHANNEL = 0x1D44, // 4.3.4 15595 - CMSG_MESSAGECHAT_DND = 0x2946, // 4.3.4 15595 - CMSG_MESSAGECHAT_EMOTE = 0x1156, // 4.3.4 15595 - CMSG_MESSAGECHAT_GUILD = 0x3956, // 4.3.4 15595 - CMSG_MESSAGECHAT_OFFICER = 0x1946, // 4.3.4 15595 - CMSG_MESSAGECHAT_PARTY = 0x1D46, // 4.3.4 15595 - CMSG_MESSAGECHAT_RAID = 0x2D44, // 4.3.4 15595 - CMSG_MESSAGECHAT_RAID_WARNING = 0x0944, // 4.3.4 15595 - CMSG_MESSAGECHAT_SAY = 0x1154, // 4.3.4 15595 - CMSG_MESSAGECHAT_WHISPER = 0x0D56, // 4.3.4 15595 - CMSG_MESSAGECHAT_YELL = 0x3544, // 4.3.4 15595 - SMSG_MESSAGECHAT = 0x2026, // 4.3.4 15595 - CMSG_JOIN_CHANNEL = 0x0156, // 4.3.4 15595 - CMSG_LEAVE_CHANNEL = 0x2D56, // 4.3.4 15595 - SMSG_CHANNEL_NOTIFY = 0x0825, // 4.3.4 15595 - CMSG_CHANNEL_LIST = 0x1556, // 4.3.4 15595 - SMSG_CHANNEL_LIST = 0x2214, // 4.3.4 15595 - CMSG_CHANNEL_PASSWORD = 0x2556, // 4.3.4 15595 - CMSG_CHANNEL_SET_OWNER = 0x3556, // 4.3.4 15595 - CMSG_CHANNEL_OWNER = 0x3D44, // 4.3.4 15595 - CMSG_CHANNEL_MODERATOR = 0x0146, // 4.3.4 15595 - CMSG_CHANNEL_UNMODERATOR = 0x1954, // 4.3.4 15595 - CMSG_CHANNEL_MUTE = 0x2554, // 4.3.4 15595 - CMSG_CHANNEL_UNMUTE = 0x3554, // 4.3.4 15595 - CMSG_CHANNEL_INVITE = 0x0144, // 4.3.4 15595 - CMSG_CHANNEL_KICK = 0x3156, // 4.3.4 15595 - CMSG_CHANNEL_BAN = 0x3D56, // 4.3.4 15595 - CMSG_CHANNEL_UNBAN = 0x2D46, // 4.3.4 15595 - CMSG_CHANNEL_ANNOUNCEMENTS = 0x1146, // 4.3.4 15595 - CMSG_CHANNEL_MODERATE = 0x2944, // 4.3.4 15595 - SMSG_UPDATE_OBJECT = 0x4715, // 4.3.4 15595 - SMSG_DESTROY_OBJECT = 0x4724, // 4.3.4 15595 - CMSG_USE_ITEM = 0x2C06, // 4.3.4 15595 - CMSG_OPEN_ITEM = 0x6A34, // 4.3.4 15595 - CMSG_READ_ITEM = 0x2F16, // 4.3.4 15595 - SMSG_READ_ITEM_OK = 0x2605, // 4.3.4 15595 - SMSG_READ_ITEM_FAILED = 0x0F16, // 4.3.4 15595 - SMSG_ITEM_COOLDOWN = 0x4D14, // 4.3.4 15595 - CMSG_GAMEOBJ_USE = 0x4E17, // 4.3.4 15595 - CMSG_DESTROY_ITEMS = 0x10B3, - SMSG_GAMEOBJECT_CUSTOM_ANIM = 0x4936, // 4.3.4 15595 - CMSG_AREATRIGGER = 0x0937, // 4.3.4 15595 - CMSG_MOVE_START_FORWARD = 0x7814, // 4.3.4 15595 - CMSG_MOVE_START_BACKWARD = 0x330A, // 4.3.4 15595 - CMSG_MOVE_STOP = 0x320A, // 4.3.4 15595 - CMSG_MOVE_START_STRAFE_LEFT = 0x3A16, // 4.3.4 15595 - CMSG_MOVE_START_STRAFE_RIGHT = 0x3A02, // 4.3.4 15595 - CMSG_MOVE_STOP_STRAFE = 0x3002, // 4.3.4 15595 - CMSG_MOVE_JUMP = 0x7A06, // 4.3.4 15595 - CMSG_MOVE_START_TURN_LEFT = 0x700C, // 4.3.4 15595 - CMSG_MOVE_START_TURN_RIGHT = 0x7000, // 4.3.4 15595 - CMSG_MOVE_STOP_TURN = 0x331E, // 4.3.4 15595 - CMSG_MOVE_START_PITCH_UP = 0x3304, // 4.3.4 15595 - CMSG_MOVE_START_PITCH_DOWN = 0x3908, // 4.3.4 15595 - CMSG_MOVE_STOP_PITCH = 0x7216, // 4.3.4 15595 - CMSG_MOVE_SET_RUN_MODE = 0x791A, // 4.3.4 15595 - CMSG_MOVE_SET_WALK_MODE = 0x7002, // 4.3.4 15595 - MSG_MOVE_TOGGLE_LOGGING = 0x10C5, - SMSG_MOVE_TELEPORT = 0x55A0, // 4.3.4 15595 - MSG_MOVE_TELEPORT_CHEAT = 0x10C7, - CMSG_MOVE_TELEPORT_ACK = 0x390C, // 4.3.4 15595 - MSG_MOVE_TOGGLE_FALL_LOGGING = 0x10C9, - CMSG_MOVE_FALL_LAND = 0x380A, // 4.3.4 15595 - CMSG_MOVE_START_SWIM = 0x3206, // 4.3.4 15595 - CMSG_MOVE_STOP_SWIM = 0x3802, // 4.3.4 15595 - MSG_MOVE_SET_RUN_SPEED_CHEAT = 0x10CD, - SMSG_MOVE_SET_RUN_SPEED = 0x3DB5, // 4.3.4 15595 - MSG_MOVE_SET_RUN_BACK_SPEED_CHEAT = 0x10CF, - SMSG_MOVE_SET_RUN_BACK_SPEED = 0x71B1, // 4.3.4 15595 - MSG_MOVE_SET_WALK_SPEED_CHEAT = 0x10D1, - SMSG_MOVE_SET_WALK_SPEED = 0x1DA4, // 4.3.4 15595 - MSG_MOVE_SET_SWIM_SPEED_CHEAT = 0x10D3, - SMSG_MOVE_SET_SWIM_SPEED = 0x15A7, // 4.3.4 15595 - MSG_MOVE_SET_SWIM_BACK_SPEED_CHEAT = 0x10D5, - SMSG_MOVE_SET_SWIM_BACK_SPEED = 0x5CA6, // 4.3.4 15595 - MSG_MOVE_SET_ALL_SPEED_CHEAT = 0x10D7, - MSG_MOVE_SET_TURN_RATE_CHEAT = 0x10D8, - SMSG_MOVE_SET_TURN_RATE = 0x30A5, // 4.3.4 15595 - MSG_MOVE_TOGGLE_COLLISION_CHEAT = 0x10DA, - CMSG_MOVE_SET_FACING = 0x7914, // 4.3.4 15595 - CMSG_MOVE_SET_PITCH = 0x7312, // 4.3.4 15595 - MSG_MOVE_WORLDPORT_ACK = 0x2411, // 4.3.4 15595 - SMSG_MONSTER_MOVE = 0x6E17, // 4.3.4 15595 - SMSG_MOVE_WATER_WALK = 0x75B1, // 4.3.4 15595 - SMSG_MOVE_LAND_WALK = 0x34B7, // 4.3.4 15595 - CMSG_MOVE_CHARM_PORT_CHEAT = 0x10E1, - CMSG_MOVE_SET_RAW_POSITION = 0x10E2, - SMSG_FORCE_RUN_SPEED_CHANGE = 0x10E3, - CMSG_FORCE_RUN_SPEED_CHANGE_ACK = 0x7818, // 4.3.4 15595 - SMSG_FORCE_RUN_BACK_SPEED_CHANGE = 0x10E5, - CMSG_FORCE_RUN_BACK_SPEED_CHANGE_ACK = 0x10E6, - SMSG_FORCE_SWIM_SPEED_CHANGE = 0x10E7, - CMSG_FORCE_SWIM_SPEED_CHANGE_ACK = 0x7A10, // 4.3.4 15595 - SMSG_FORCE_MOVE_ROOT = 0x7DA0, // 4.3.4 15595 - CMSG_FORCE_MOVE_ROOT_ACK = 0x701E, // 4.3.4 15595 - SMSG_FORCE_MOVE_UNROOT = 0x7DB4, // 4.3.4 15595 - CMSG_FORCE_MOVE_UNROOT_ACK = 0x7808, // 4.3.4 15595 - MSG_MOVE_ROOT = 0x10ED, - MSG_MOVE_UNROOT = 0x10EE, - MSG_MOVE_HEARTBEAT = 0x3914, // 4.3.4 15595 - SMSG_MOVE_KNOCK_BACK = 0x5CB4, // 4.3.4 15595 - CMSG_MOVE_KNOCK_BACK_ACK = 0x721C, // 4.3.4 15595 - SMSG_MOVE_UPDATE_KNOCK_BACK = 0x3DB2, // 4.3.4 15595 - SMSG_MOVE_FEATHER_FALL = 0x79B0, // 4.3.4 15595 - SMSG_MOVE_NORMAL_FALL = 0x51B6, // 4.3.4 15595 - SMSG_MOVE_SET_HOVER = 0x5CB3, // 4.3.4 15595 - SMSG_MOVE_UNSET_HOVER = 0x51B3, // 4.3.4 15595 - CMSG_MOVE_HOVER_ACK = 0x3318, // 4.3.4 15595 - MSG_MOVE_HOVER = 0x10F8, - CMSG_TRIGGER_CINEMATIC_CHEAT = 0x10F9, - CMSG_OPENING_CINEMATIC = 0x10FA, - SMSG_TRIGGER_CINEMATIC = 0x6C27, // 4.3.4 15595 - CMSG_NEXT_CINEMATIC_CAMERA = 0x10FC, - CMSG_COMPLETE_CINEMATIC = 0x2116, // 4.3.4 15595 - SMSG_TUTORIAL_FLAGS = 0x0B35, // 4.3.4 15595 - CMSG_TUTORIAL_FLAG = 0x6C26, // 4.3.4 15595 - CMSG_TUTORIAL_CLEAR = 0x6515, // 4.3.4 15595 - CMSG_TUTORIAL_RESET = 0x2726, // 4.3.4 15595 - CMSG_STANDSTATECHANGE = 0x0535, // 4.3.4 15595 - CMSG_EMOTE = 0x4C26, // 4.3.4 15595 - SMSG_EMOTE = 0x0A34, // 4.3.4 15595 - CMSG_TEXT_EMOTE = 0x2E24, // 4.3.4 15595 - SMSG_TEXT_EMOTE = 0x0B05, // 4.3.4 15595 - CMSG_AUTOEQUIP_GROUND_ITEM = 0x1107, - CMSG_AUTOSTORE_GROUND_ITEM = 0x1108, - CMSG_AUTOSTORE_LOOT_ITEM = 0x0E34, // 4.3.4 15595 - CMSG_STORE_LOOT_IN_SLOT = 0x110A, - CMSG_AUTOEQUIP_ITEM = 0x4304, // 4.3.4 15595 - CMSG_AUTOSTORE_BAG_ITEM = 0x0236, // 4.3.4 15595 - CMSG_SWAP_ITEM = 0x6326, // 4.3.4 15595 - CMSG_SWAP_INV_ITEM = 0x2614, // 4.3.4 15595 - CMSG_SPLIT_ITEM = 0x0F17, // 4.3.4 15595 - CMSG_AUTOEQUIP_ITEM_SLOT = 0x4A17, // 4.3.4 15595 - CMSG_UNCLAIM_LICENSE = 0x1111, - CMSG_DESTROYITEM = 0x4A27, // 4.3.4 15595 - SMSG_INVENTORY_CHANGE_FAILURE = 0x2236, // 4.3.4 15595 - SMSG_OPEN_CONTAINER = 0x4714, // 4.3.4 15595 - CMSG_INSPECT = 0x0927, // 4.3.4 15595 - SMSG_INSPECT_RESULTS_UPDATE = 0x0C14, // 4.3.4 15595 - CMSG_INITIATE_TRADE = 0x7916, // 4.3.4 15595 - CMSG_BEGIN_TRADE = 0x721E, // 4.3.4 15595 - CMSG_BUSY_TRADE = 0x331C, // 4.3.4 15595 - CMSG_IGNORE_TRADE = 0x7112, // 4.3.4 15595 - CMSG_ACCEPT_TRADE = 0x7110, // 4.3.4 15595 - CMSG_UNACCEPT_TRADE = 0x391A, // 4.3.4 15595 - CMSG_CANCEL_TRADE = 0x731E, // 4.3.4 15595 - CMSG_SET_TRADE_ITEM = 0x7B0C, // 4.3.4 15595 - CMSG_CLEAR_TRADE_ITEM = 0x7018, // 4.3.4 15595 - CMSG_SET_TRADE_GOLD = 0x3008, // 4.3.4 15595 - SMSG_TRADE_STATUS = 0x5CA3, // 4.3.4 15595 - SMSG_TRADE_STATUS_EXTENDED = 0x70A2, // 4.3.4 15595 - SMSG_INITIALIZE_FACTIONS = 0x4634, // 4.3.4 15595 - SMSG_SET_FACTION_VISIBLE = 0x2525, // 4.3.4 15595 - SMSG_SET_FACTION_STANDING = 0x0126, // 4.3.4 15595 - CMSG_SET_FACTION_ATWAR = 0x0706, // 4.3.4 15595 - CMSG_SET_FACTION_CHEAT = 0x1127, - SMSG_SET_PROFICIENCY = 0x6207, // 4.3.4 15595 - CMSG_SET_ACTION_BUTTON = 0x6F06, // 4.3.4 15595 - SMSG_ACTION_BUTTONS = 0x38B5, // 4.3.4 15595 - SMSG_INITIAL_SPELLS = 0x0104, // 4.3.4 15595 - SMSG_LEARNED_SPELL = 0x58A2, // 4.3.4 15595 - SMSG_SUPERCEDED_SPELL = 0x35B0, // 4.3.4 15595 - CMSG_NEW_SPELL_SLOT = 0x112E, - CMSG_CAST_SPELL = 0x4C07, // 4.3.4 15595 - CMSG_CANCEL_CAST = 0x0115, // 4.3.4 15595 - SMSG_CAST_FAILED = 0x4D16, // 4.3.4 15595 - SMSG_SPELL_START = 0x6415, // 4.3.4 15595 - SMSG_SPELL_GO = 0x6E16, // 4.3.4 15595 - SMSG_SPELL_FAILURE = 0x0C34, // 4.3.4 15595 - SMSG_SPELL_COOLDOWN = 0x4B16, // 4.3.4 15595 - SMSG_COOLDOWN_EVENT = 0x4F26, // 4.3.4 15595 - CMSG_CANCEL_AURA = 0x0E26, // 4.3.4 15595 - SMSG_EQUIPMENT_SET_ID = 0x2216, // 4.3.4 15595 - SMSG_PET_CAST_FAILED = 0x2B15, // 4.3.4 15595 - SMSG_CHANNEL_START = 0x0A15, // 4.3.4 15595 - SMSG_CHANNEL_UPDATE = 0x2417, // 4.3.4 15595 - CMSG_CANCEL_CHANNELLING = 0x6C25, // 4.3.4 15595 - SMSG_AI_REACTION = 0x0637, // 4.3.4 15595 - CMSG_SET_SELECTION = 0x0506, // 4.3.4 15595 - CMSG_EQUIPMENT_SET_DELETE = 0x4D07, // 4.3.4 15595 - CMSG_INSTANCE_LOCK_RESPONSE = 0x1140, - CMSG_DEBUG_PASSIVE_AURA = 0x1141, - CMSG_ATTACKSWING = 0x0926, // 4.3.4 15595 - CMSG_ATTACKSTOP = 0x4106, // 4.3.4 15595 - SMSG_ATTACKSTART = 0x2D15, // 4.3.4 15595 - SMSG_ATTACKSTOP = 0x0934, // 4.3.4 15595 - SMSG_ATTACKSWING_NOTINRANGE = 0x0B36, // 4.3.4 15595 - SMSG_ATTACKSWING_BADFACING = 0x6C07, // 4.3.4 15595 - SMSG_PENDING_RAID_LOCK = 0x4F17, // 4.3.4 15595 - SMSG_ATTACKSWING_DEADTARGET = 0x2B26, // 4.3.4 15595 - SMSG_ATTACKSWING_CANT_ATTACK = 0x0016, // 4.3.4 15595 - SMSG_ATTACKERSTATEUPDATE = 0x0B25, // 4.3.4 15595 - SMSG_BATTLEFIELD_PORT_DENIED = 0x35A3, // 4.3.4 15595 - CMSG_PERFORM_ACTION_SET = 0x114D, - SMSG_RESUME_CAST_BAR = 0x114E, - SMSG_CANCEL_COMBAT = 0x4F04, // 4.3.4 15595 - SMSG_SPELLBREAKLOG = 0x6B17, // 4.3.4 15595 - SMSG_SPELLHEALLOG = 0x2816, // 4.3.4 15595 - SMSG_SPELLENERGIZELOG = 0x0414, // 4.3.4 15595 - SMSG_BREAK_TARGET = 0x0105, // 4.3.4 15595 - CMSG_SAVE_PLAYER = 0x1154, - CMSG_SETDEATHBINDPOINT = 0x1155, - SMSG_BINDPOINTUPDATE = 0x0527, // 4.3.4 15595 - CMSG_GETDEATHBINDZONE = 0x1157, - SMSG_BINDZONEREPLY = 0x1158, - SMSG_PLAYERBOUND = 0x2516, // 4.3.4 15595 - SMSG_CLIENT_CONTROL_UPDATE = 0x2837, // 4.3.4 15595 - CMSG_REPOP_REQUEST = 0x6235, // 4.3.4 15595 - SMSG_RESURRECT_REQUEST = 0x2905, // 4.3.4 15595 - CMSG_RESURRECT_RESPONSE = 0x6827, // 4.3.4 15595 - CMSG_RETURN_TO_GRAVEYARD = 0x301E, // 4.3.4 15595 - CMSG_LOOT = 0x0127, // 4.3.4 15595 - CMSG_LOOT_CURRENCY = 0x781C, // 4.3.4 15595 - CMSG_LOOT_MONEY = 0x6227, // 4.3.4 15595 - CMSG_LOOT_RELEASE = 0x2007, // 4.3.4 15595 - SMSG_LOOT_RESPONSE = 0x4C16, // 4.3.4 15595 - SMSG_LOOT_RELEASE_RESPONSE = 0x6D25, // 4.3.4 15595 - SMSG_LOOT_REMOVED = 0x6817, // 4.3.4 15595 - SMSG_LOOT_CURRENCY_REMOVED = 0x1DB4, // 4.3.4 15595 - SMSG_LOOT_MONEY_NOTIFY = 0x2836, // 4.3.4 15595 - SMSG_LOOT_ITEM_NOTIFY = 0x6D15, // 4.3.4 15595 - SMSG_LOOT_CLEAR_MONEY = 0x2B37, // 4.3.4 15595 - SMSG_ITEM_PUSH_RESULT = 0x0E15, // 4.3.4 15595 - SMSG_DUEL_REQUESTED = 0x4504, // 4.3.4 15595 - SMSG_DUEL_OUTOFBOUNDS = 0x0C26, // 4.3.4 15595 - SMSG_DUEL_INBOUNDS = 0x0A27, // 4.3.4 15595 - SMSG_DUEL_COMPLETE = 0x2527, // 4.3.4 15595 - SMSG_DUEL_WINNER = 0x2D36, // 4.3.4 15595 - CMSG_DUEL_ACCEPTED = 0x2136, // 4.3.4 15595 - CMSG_DUEL_CANCELLED = 0x6624, // 4.3.4 15595 - SMSG_MOUNTRESULT = 0x2225, // 4.3.4 15595 - SMSG_DISMOUNTRESULT = 0x0D25, // 4.3.4 15595 - SMSG_REMOVED_FROM_PVP_QUEUE = 0x1171, - CMSG_MOUNTSPECIAL_ANIM = 0x2807, // 4.3.4 15595 - SMSG_MOUNTSPECIAL_ANIM = 0x0217, // 4.3.4 15595 - SMSG_PET_TAME_FAILURE = 0x6B24, // 4.3.4 15595 - CMSG_PET_SET_ACTION = 0x6904, // 4.3.4 15595 - CMSG_PET_ACTION = 0x0226, // 4.3.4 15595 - CMSG_PET_ABANDON = 0x0C24, // 4.3.4 15595 - CMSG_PET_RENAME = 0x6406, // 4.3.4 15595 - SMSG_PET_NAME_INVALID = 0x6007, // 4.3.4 15595 - SMSG_PET_SPELLS = 0x4114, // 4.3.4 15595 - SMSG_PET_MODE = 0x2235, // 4.3.4 15595 - CMSG_GOSSIP_HELLO = 0x4525, // 4.3.4 15595 - CMSG_GOSSIP_SELECT_OPTION = 0x0216, // 4.3.4 15595 - SMSG_GOSSIP_MESSAGE = 0x2035, // 4.3.4 15595 - SMSG_GOSSIP_COMPLETE = 0x0806, // 4.3.4 15595 - CMSG_NPC_TEXT_QUERY = 0x4E24, // 4.3.4 15595 - SMSG_NPC_TEXT_UPDATE = 0x4436, // 4.3.4 15595 - SMSG_NPC_WONT_TALK = 0x1182, - CMSG_QUESTGIVER_STATUS_QUERY = 0x4407, // 4.3.4 15595 - SMSG_QUESTGIVER_STATUS = 0x2115, // 4.3.4 15595 - CMSG_QUESTGIVER_HELLO = 0x0D17, // 4.3.4 15595 - SMSG_QUESTGIVER_QUEST_LIST = 0x0134, // 4.3.4 15595 - CMSG_QUESTGIVER_QUERY_QUEST = 0x2F14, // 4.3.4 15595 - CMSG_QUESTGIVER_QUEST_AUTOLAUNCH = 0x1188, - SMSG_QUESTGIVER_QUEST_DETAILS = 0x2425, // 4.3.4 15595 - CMSG_QUESTGIVER_ACCEPT_QUEST = 0x6B37, // 4.3.4 15595 - CMSG_QUESTGIVER_COMPLETE_QUEST = 0x0114, // 4.3.4 15595 - SMSG_QUESTGIVER_REQUEST_ITEMS = 0x6236, // 4.3.4 15595 - CMSG_QUESTGIVER_REQUEST_REWARD = 0x2534, // 4.3.4 15595 - SMSG_QUESTGIVER_OFFER_REWARD = 0x2427, // 4.3.4 15595 - CMSG_QUESTGIVER_CHOOSE_REWARD = 0x2125, // 4.3.4 15595 - SMSG_QUESTGIVER_QUEST_INVALID = 0x4016, // 4.3.4 15595 - CMSG_QUESTGIVER_CANCEL = 0x1191, - SMSG_QUESTGIVER_QUEST_COMPLETE = 0x55A4, // 4.3.4 15595 - SMSG_QUESTGIVER_QUEST_FAILED = 0x4236, // 4.3.4 15595 - CMSG_QUESTLOG_SWAP_QUEST = 0x1194, - CMSG_QUESTLOG_REMOVE_QUEST = 0x0D16, // 4.3.4 15595 - SMSG_QUESTLOG_FULL = 0x0E36, // 4.3.4 15595 - SMSG_QUESTUPDATE_FAILED = 0x6324, // 4.3.4 15595 - SMSG_QUESTUPDATE_FAILEDTIMER = 0x6427, // 4.3.4 15595 - SMSG_QUESTUPDATE_COMPLETE = 0x2937, // 4.3.4 15595 - SMSG_QUESTUPDATE_ADD_KILL = 0x0D27, // 4.3.4 15595 - SMSG_QUESTUPDATE_ADD_ITEM_OBSOLETE = 0x119B, - CMSG_QUEST_CONFIRM_ACCEPT = 0x0D15, // 4.3.4 15595 - SMSG_QUEST_CONFIRM_ACCEPT = 0x6F07, // 4.3.4 15595 - CMSG_PUSHQUESTTOPARTY = 0x4B14, // 4.3.4 15595 - CMSG_LIST_INVENTORY = 0x2806, // 4.3.4 15595 - SMSG_LIST_INVENTORY = 0x7CB0, // 4.3.4 15595 - CMSG_SELL_ITEM = 0x4E15, // 4.3.4 15595 - SMSG_SELL_ITEM = 0x6105, // 4.3.4 15595 - CMSG_BUY_ITEM = 0x0736, // 4.3.4 15595 - SMSG_BUY_ITEM = 0x0F26, // 4.3.4 15595 - SMSG_BUY_FAILED = 0x6435, // 4.3.4 15595 - CMSG_TAXICLEARALLNODES = 0x11A7, - CMSG_TAXIENABLEALLNODES = 0x11A8, - CMSG_TAXISHOWNODES = 0x11A9, - SMSG_SHOWTAXINODES = 0x2A36, // 4.3.4 15595 - CMSG_TAXINODE_STATUS_QUERY = 0x2F25, // 4.3.4 15595 - SMSG_TAXINODE_STATUS = 0x2936, // 4.3.4 15595 - CMSG_TAXIQUERYAVAILABLENODES = 0x6C06, // 4.3.4 15595 - CMSG_ACTIVATETAXI = 0x6E06, // 4.3.4 15595 - SMSG_ACTIVATETAXIREPLY = 0x6A37, // 4.3.4 15595 - SMSG_NEW_TAXI_PATH = 0x4B35, // 4.3.4 15595 - CMSG_TRAINER_LIST = 0x2336, // 4.3.4 15595 - SMSG_TRAINER_LIST = 0x4414, // 4.3.4 15595 - CMSG_TRAINER_BUY_SPELL = 0x4415, // 4.3.4 15595 - SMSG_TRAINER_SERVICE = 0x6A05, // 4.3.4 15595 - SMSG_TRAINER_BUY_FAILED = 0x0004, // 4.3.4 15595 - CMSG_BINDER_ACTIVATE = 0x4006, // 4.3.4 15595 - SMSG_PLAYERBINDERROR = 0x6A24, // 4.3.4 15595 - CMSG_BANKER_ACTIVATE = 0x0005, // 4.3.4 15595 - SMSG_SHOW_BANK = 0x2627, // 4.3.4 15595 - CMSG_BUY_BANK_SLOT = 0x0425, // 4.3.4 15595 - CMSG_PETITION_SHOWLIST = 0x4617, // 4.3.4 15595 - SMSG_PETITION_SHOWLIST = 0x6405, // 4.3.4 15595 - CMSG_PETITION_BUY = 0x4E05, // 4.3.4 15595 - CMSG_PETITION_SHOW_SIGNATURES = 0x4F15, // 4.3.4 15595 - SMSG_PETITION_SHOW_SIGNATURES = 0x0716, // 4.3.4 15595 - CMSG_PETITION_SIGN = 0x0E04, // 4.3.4 15595 - SMSG_PETITION_SIGN_RESULTS = 0x6217, // 4.3.4 15595 - MSG_PETITION_DECLINE = 0x4905, // 4.3.4 15595 - CMSG_OFFER_PETITION = 0x4817, // 4.3.4 15595 - CMSG_TURN_IN_PETITION = 0x0B27, // 4.3.4 15595 - SMSG_TURN_IN_PETITION_RESULTS = 0x0F07, // 4.3.4 15595 - CMSG_PETITION_QUERY = 0x4424, // 4.3.4 15595 - SMSG_PETITION_QUERY_RESPONSE = 0x4B37, // 4.3.4 15595 - SMSG_FISH_NOT_HOOKED = 0x0A17, // 4.3.4 15595 - SMSG_FISH_ESCAPED = 0x2205, // 4.3.4 15595 - CMSG_BUG = 0x4035, // 4.3.4 15595 - SMSG_NOTIFICATION = 0x14A0, // 4.3.4 15595 - CMSG_PLAYED_TIME = 0x0804, // 4.3.4 15595 - SMSG_PLAYED_TIME = 0x6037, // 4.3.4 15595 - CMSG_QUERY_TIME = 0x0A36, // 4.3.4 15595 - SMSG_QUERY_TIME_RESPONSE = 0x2124, // 4.3.4 15595 - SMSG_LOG_XPGAIN = 0x4514, // 4.3.4 15595 - SMSG_AURACASTLOG = 0x11D2, - CMSG_RECLAIM_CORPSE = 0x4036, // 4.3.4 15595 - CMSG_WRAP_ITEM = 0x4F06, // 4.3.4 15595 - SMSG_LEVELUP_INFO = 0x0435, // 4.3.4 15595 - MSG_MINIMAP_PING = 0x6635, // 4.3.4 15595 - SMSG_RESISTLOG = 0x11D7, - SMSG_ENCHANTMENTLOG = 0x6035, // 4.3.4 15595 - CMSG_SET_SKILL_CHEAT = 0x11D9, - SMSG_START_MIRROR_TIMER = 0x6824, // 4.3.4 15595 - SMSG_PAUSE_MIRROR_TIMER = 0x4015, // 4.3.4 15595 - SMSG_STOP_MIRROR_TIMER = 0x0B06, // 4.3.4 15595 - CMSG_PING = 0x444D, // 4.3.4 15595 - SMSG_PONG = 0x4D42, // 4.3.4 15595 - SMSG_CLEAR_COOLDOWNS = 0x59B4, // 4.3.4 15595 - SMSG_GAMEOBJECT_PAGETEXT = 0x2925, // 4.3.4 15595 - CMSG_SETSHEATHED = 0x4326, // 4.3.4 15595 - SMSG_COOLDOWN_CHEAT = 0x4537, // 4.3.4 15595 - SMSG_SPELL_DELAYED = 0x0715, // 4.3.4 15595 - CMSG_QUEST_POI_QUERY = 0x4037, // 4.3.4 15595 - SMSG_QUEST_POI_QUERY_RESPONSE = 0x6304, // 4.3.4 15595 - CMSG_GHOST = 0x11E6, - CMSG_GM_INVIS = 0x11E7, - SMSG_INVALID_PROMOTION_CODE = 0x6F25, // 4.3.4 15595 - MSG_GM_BIND_OTHER = 0x11E9, - MSG_GM_SUMMON = 0x11EA, - SMSG_ITEM_TIME_UPDATE = 0x2407, // 4.3.4 15595 - SMSG_ITEM_ENCHANT_TIME_UPDATE = 0x0F27, // 4.3.4 15595 - MSG_GM_SHOWLABEL = 0x11F0, - CMSG_PET_CAST_SPELL = 0x6337, // 4.3.4 15595 - MSG_SAVE_GUILD_EMBLEM = 0x2404, // 4.3.4 15595 - MSG_TABARDVENDOR_ACTIVATE = 0x6926, // 4.3.4 15595 - SMSG_PLAY_SPELL_VISUAL = 0x55A5, // 4.3.4 15595 - CMSG_ZONEUPDATE = 0x4F37, // 4.3.4 15595 - SMSG_PARTYKILLLOG = 0x4937, // 4.3.4 15595 - SMSG_COMPRESSED_UPDATE_OBJECT = 0x11F7, - SMSG_EXPLORATION_EXPERIENCE = 0x6716, // 4.3.4 15595 - CMSG_GM_SET_SECURITY_GROUP = 0x11FA, - CMSG_GM_NUKE = 0x11FB, - MSG_RANDOM_ROLL = 0x0905, // 4.3.4 15595 - SMSG_ENVIRONMENTALDAMAGELOG = 0x6C05, // 4.3.4 15595 - CMSG_CHANGEPLAYER_DIFFICULTY = 0x6107, // 4.3.4 15595 - SMSG_RWHOIS = 0x11FF, - SMSG_LFG_PLAYER_REWARD = 0x1200, - SMSG_LFG_TELEPORT_DENIED = 0x1201, - CMSG_UNLEARN_SPELL = 0x1202, - CMSG_UNLEARN_SKILL = 0x6106, // 4.3.4 15595 - SMSG_REMOVED_SPELL = 0x4804, // 4.3.4 15595 - CMSG_DECHARGE = 0x1205, - CMSG_GMTICKET_CREATE = 0x0137, // 4.3.4 15595 - SMSG_GMTICKET_CREATE = 0x2107, // 4.3.4 15595 - CMSG_GMTICKET_UPDATETEXT = 0x0636, // 4.3.4 15595 - SMSG_GMTICKET_UPDATETEXT = 0x6535, // 4.3.4 15595 - SMSG_ACCOUNT_DATA_TIMES = 0x4B05, // 4.3.4 15595 - CMSG_REQUEST_ACCOUNT_DATA = 0x6505, // 4.3.4 15595 - CMSG_UPDATE_ACCOUNT_DATA = 0x4736, // 4.3.4 15595 - SMSG_UPDATE_ACCOUNT_DATA = 0x6837, // 4.3.4 15595 - SMSG_CLEAR_FAR_SIGHT_IMMEDIATE = 0x2A04, // 4.3.4 15595 - SMSG_CHANGEPLAYER_DIFFICULTY_RESULT = 0x2217, // 4.3.4 15595 - CMSG_GM_TEACH = 0x1210, - CMSG_GM_CREATE_ITEM_TARGET = 0x1211, - CMSG_GMTICKET_GETTICKET = 0x0326, // 4.3.4 15595 - SMSG_GMTICKET_GETTICKET = 0x2C15, // 4.3.4 15595 - CMSG_UNLEARN_TALENTS = 0x1214, - SMSG_INSTANCE_ENCOUNTER = 0x1215, - SMSG_GAMEOBJECT_DESPAWN_ANIM = 0x6735, // 4.3.4 15595 - MSG_CORPSE_QUERY = 0x4336, // 4.3.4 15595 - CMSG_GMTICKET_DELETETICKET = 0x6B14, // 4.3.4 15595 - SMSG_GMTICKET_DELETETICKET = 0x6D17, // 4.3.4 15595 - SMSG_CHAT_WRONG_FACTION = 0x6724, // 4.3.4 15595 - CMSG_GMTICKET_SYSTEMSTATUS = 0x4205, // 4.3.4 15595 - SMSG_GMTICKET_SYSTEMSTATUS = 0x0D35, // 4.3.4 15595 - CMSG_SPIRIT_HEALER_ACTIVATE = 0x2E26, // 4.3.4 15595 - CMSG_SET_STAT_CHEAT = 0x121E, - SMSG_QUEST_FORCE_REMOVED = 0x121F, - CMSG_SKILL_BUY_STEP = 0x1220, - CMSG_SKILL_BUY_RANK = 0x1221, - CMSG_XP_CHEAT = 0x1222, - SMSG_SPIRIT_HEALER_CONFIRM = 0x4917, // 4.3.4 15595 - CMSG_CHARACTER_POINT_CHEAT = 0x1224, - SMSG_GOSSIP_POI = 0x4316, // 4.3.4 15595 - CMSG_CHAT_IGNORED = 0x0D54, // 4.3.4 15595 - CMSG_GM_VISION = 0x1227, - CMSG_SERVER_COMMAND = 0x1228, - CMSG_GM_SILENCE = 0x1229, - CMSG_GM_REVEALTO = 0x122A, - CMSG_GM_RESURRECT = 0x122B, - CMSG_GM_SUMMONMOB = 0x122C, - CMSG_GM_MOVECORPSE = 0x122D, - CMSG_GM_FREEZE = 0x122E, - CMSG_GM_UBERINVIS = 0x122F, - CMSG_GM_REQUEST_PLAYER_INFO = 0x1230, - SMSG_GM_PLAYER_INFO = 0x1231, - CMSG_GUILD_RANK = 0x1024, // 4.3.4 15595 - CMSG_GUILD_ADD_RANK = 0x3030, // 4.3.4 15595 - CMSG_GUILD_DEL_RANK = 0x3234, // 4.3.4 15595 - CMSG_GUILD_SET_NOTE = 0x1233, // 4.3.4 15595 - SMSG_LOGIN_VERIFY_WORLD = 0x2005, // 4.3.4 15595 - CMSG_CLEAR_EXPLORATION = 0x1238, - CMSG_SEND_MAIL = 0x0523, // 4.3.4 15595 - SMSG_SEND_MAIL_RESULT = 0x4927, // 4.3.4 15595 - CMSG_GET_MAIL_LIST = 0x4D37, // 4.3.4 15595 - SMSG_MAIL_LIST_RESULT = 0x4217, // 4.3.4 15595 - CMSG_BATTLEFIELD_LIST = 0x3814, // 4.3.4 15595 - SMSG_BATTLEFIELD_LIST = 0x71B5, // 4.3.4 15595 - CMSG_BATTLEFIELD_JOIN = 0x123F, - SMSG_FORCE_SET_VEHICLE_REC_ID = 0x1240, - CMSG_SET_VEHICLE_REC_ID_ACK = 0x1241, - CMSG_TAXICLEARNODE = 0x1242, - CMSG_TAXIENABLENODE = 0x1243, - CMSG_ITEM_TEXT_QUERY = 0x2406, // 4.3.4 15595 - SMSG_ITEM_TEXT_QUERY_RESPONSE = 0x2725, // 4.3.4 15595 - CMSG_MAIL_TAKE_MONEY = 0x4034, // 4.3.4 15595 - CMSG_MAIL_TAKE_ITEM = 0x2B06, // 4.3.4 15595 - CMSG_MAIL_MARK_AS_READ = 0x0C07, // 4.3.4 15595 - CMSG_MAIL_RETURN_TO_SENDER = 0x0816, // 4.3.4 15595 - CMSG_MAIL_DELETE = 0x6104, // 4.3.4 15595 - CMSG_MAIL_CREATE_TEXT_ITEM = 0x0B14, // 4.3.4 15595 - SMSG_SPELLLOGMISS = 0x0625, // 4.3.4 15595 - SMSG_SPELLLOGEXECUTE = 0x0626, // 4.3.4 15595 - SMSG_DEBUGAURAPROC = 0x124E, - SMSG_PERIODICAURALOG = 0x0416, // 4.3.4 15595 - SMSG_SPELLDAMAGESHIELD = 0x2927, // 4.3.4 15595 - SMSG_SPELLNONMELEEDAMAGELOG = 0x4315, // 4.3.4 15595 - CMSG_LEARN_TALENT = 0x0306, // 4.3.4 15595 - SMSG_RESURRECT_FAILED = 0x1253, - CMSG_TOGGLE_PVP = 0x6815, // 4.3.4 15595 - SMSG_ZONE_UNDER_ATTACK = 0x0A06, // 4.3.4 15595 - MSG_AUCTION_HELLO = 0x2307, // 4.3.4 15595 - CMSG_AUCTION_SELL_ITEM = 0x4A06, // 4.3.4 15595 - CMSG_AUCTION_REMOVE_ITEM = 0x6426, // 4.3.4 15595 - CMSG_AUCTION_LIST_ITEMS = 0x0324, // 4.3.4 15595 - CMSG_AUCTION_LIST_OWNER_ITEMS = 0x0206, // 4.3.4 15595 - CMSG_AUCTION_PLACE_BID = 0x2306, // 4.3.4 15595 - SMSG_AUCTION_COMMAND_RESULT = 0x4C25, // 4.3.4 15595 - SMSG_AUCTION_LIST_RESULT = 0x6637, // 4.3.4 15595 - SMSG_AUCTION_OWNER_LIST_RESULT = 0x6C34, // 4.3.4 15595 - SMSG_AUCTION_BIDDER_NOTIFICATION = 0x4E27, // 4.3.4 15595 - SMSG_AUCTION_OWNER_NOTIFICATION = 0x4116, // 4.3.4 15595 - SMSG_PROCRESIST = 0x1261, - SMSG_COMBAT_EVENT_FAILED = 0x1262, - SMSG_DISPEL_FAILED = 0x0307, // 4.3.4 15595 - SMSG_SPELLOGDAMAGE_IMMUNE = 0x4507, // 4.3.4 15595 - CMSG_AUCTION_LIST_BIDDER_ITEMS = 0x6937, // 4.3.4 15595 - SMSG_AUCTION_BIDDER_LIST_RESULT = 0x0027, // 4.3.4 15595 - SMSG_SET_FLAT_SPELL_MODIFIER = 0x2834, // 4.3.4 15595 - SMSG_SET_PCT_SPELL_MODIFIER = 0x0224, // 4.3.4 15595 - CMSG_SET_AMMO = 0x1269, - SMSG_CORPSE_RECLAIM_DELAY = 0x0D34, // 4.3.4 15595 - CMSG_SET_ACTIVE_MOVER = 0x3314, // 4.3.4 15595 - CMSG_PET_CANCEL_AURA = 0x4B25, // 4.3.4 15595 - CMSG_PLAYER_AI_CHEAT = 0x126D, - CMSG_CANCEL_AUTO_REPEAT_SPELL = 0x6C35, // 4.3.4 15595 - MSG_GM_ACCOUNT_ONLINE = 0x126F, - MSG_LIST_STABLED_PETS = 0x0834, // 4.3.4 15595 - CMSG_STABLE_PET = 0x1271, - CMSG_UNSTABLE_PET = 0x1272, - CMSG_BUY_STABLE_SLOT = 0x1273, - SMSG_STABLE_RESULT = 0x2204, // 4.3.4 15595 - CMSG_STABLE_REVIVE_PET = 0x1275, - CMSG_STABLE_SWAP_PET = 0x1276, - MSG_QUEST_PUSH_RESULT = 0x4515, // 4.3.4 15595 - SMSG_PLAY_MUSIC = 0x4B06, // 4.3.4 15595 - SMSG_PLAY_OBJECT_SOUND = 0x2635, // 4.3.4 15595 - SMSG_PLAY_ONE_SHOT_ANIM_KIT = 0x4A35, // 4.3.4 15595 - CMSG_REQUEST_PET_INFO = 0x4924, // 4.3.4 15595 - CMSG_FAR_SIGHT = 0x4835, // 4.3.4 15595 - SMSG_SPELLDISPELLOG = 0x4516, // 4.3.4 15595 - SMSG_DAMAGE_CALC_LOG = 0x2436, // 4.3.4 15595 - CMSG_ENABLE_DAMAGE_LOG = 0x127E, - CMSG_GROUP_CHANGE_SUB_GROUP = 0x4124, // 4.3.4 15595 - CMSG_REQUEST_PARTY_MEMBER_STATS = 0x0C04, // 4.3.4 15595 - CMSG_GROUP_SWAP_SUB_GROUP = 0x1281, - CMSG_RESET_FACTION_CHEAT = 0x1282, - CMSG_AUTOSTORE_BANK_ITEM = 0x0607, // 4.3.4 15595 - CMSG_AUTOBANK_ITEM = 0x2537, // 4.3.4 15595 - MSG_QUERY_NEXT_MAIL_TIME = 0x0F04, // 4.3.4 15595 - SMSG_RECEIVED_MAIL = 0x2924, // 4.3.4 15595 - SMSG_RAID_GROUP_ONLY = 0x0837, // 4.3.4 15595 - CMSG_SET_DURABILITY_CHEAT = 0x1288, - CMSG_SET_PVP_RANK_CHEAT = 0x1289, - CMSG_ADD_PVP_MEDAL_CHEAT = 0x128A, - CMSG_DEL_PVP_MEDAL_CHEAT = 0x128B, - CMSG_SET_PVP_TITLE = 0x128C, - SMSG_PVP_CREDIT = 0x6015, // 4.3.4 15595 - SMSG_AUCTION_REMOVED_NOTIFICATION = 0x2334, // 4.3.4 15595 - CMSG_GROUP_RAID_CONVERT = 0x6E27, // 4.3.4 15595 - CMSG_GROUP_REQUEST_JOIN_UPDATES = 0x2583, // 4.3.4 15595 - CMSG_GROUP_ASSISTANT_LEADER = 0x6025, // 4.3.4 15595 - CMSG_BUYBACK_ITEM = 0x6C17, // 4.3.4 15595 - SMSG_SERVER_MESSAGE = 0x6C04, // 4.3.4 15595 - CMSG_SET_SAVED_INSTANCE_EXTEND = 0x6706, // 4.3.4 15595 - SMSG_LFG_OFFER_CONTINUE = 0x1294, - CMSG_TEST_DROP_RATE = 0x1295, - SMSG_TEST_DROP_RATE_RESULT = 0x1296, - CMSG_LFG_GET_STATUS = 0x2581, // 4.3.4 15595 - SMSG_SHOW_MAILBOX = 0x2524, // 4.3.4 15595 - SMSG_RESET_RANGED_COMBAT_TIMER = 0x1299, - SMSG_CHAT_NOT_IN_PARTY = 0x6A14, // 4.3.4 15595 - CMSG_GMTICKETSYSTEM_TOGGLE = 0x129B, - CMSG_CANCEL_GROWTH_AURA = 0x0237, // 4.3.4 15595 - SMSG_CANCEL_AUTO_REPEAT = 0x6436, // 4.3.4 15595 - SMSG_STANDSTATE_UPDATE = 0x6F04, // 4.3.4 15595 - SMSG_LOOT_ALL_PASSED = 0x6237, // 4.3.4 15595 - SMSG_LOOT_ROLL_WON = 0x6617, // 4.3.4 15595 - CMSG_LOOT_ROLL = 0x6934, // 4.3.4 15595 - SMSG_LOOT_START_ROLL = 0x2227, // 4.3.4 15595 - SMSG_LOOT_ROLL = 0x6507, // 4.3.4 15595 - CMSG_LOOT_MASTER_GIVE = 0x4F35, // 4.3.4 15595 - SMSG_LOOT_MASTER_LIST = 0x0325, // 4.3.4 15595 - SMSG_SET_FORCED_REACTIONS = 0x4615, // 4.3.4 15595 - SMSG_SPELL_FAILED_OTHER = 0x4535, // 4.3.4 15595 - SMSG_GAMEOBJECT_RESET_STATE = 0x2A16, // 4.3.4 15595 - CMSG_REPAIR_ITEM = 0x2917, // 4.3.4 15595 - SMSG_CHAT_PLAYER_NOT_FOUND = 0x2526, // 4.3.4 15595 - MSG_TALENT_WIPE_CONFIRM = 0x0107, // 4.3.4 15595 - SMSG_SUMMON_REQUEST = 0x2A07, // 4.3.4 15595 - CMSG_SUMMON_RESPONSE = 0x6F27, // 4.3.4 15595 - MSG_DEV_SHOWLABEL = 0x12AE, - SMSG_MONSTER_MOVE_TRANSPORT = 0x2004, // 4.3.4 15595 - SMSG_PET_BROKEN = 0x12B0, - MSG_MOVE_FEATHER_FALL = 0x12B1, - MSG_MOVE_WATER_WALK = 0x12B2, - CMSG_SERVER_BROADCAST = 0x12B3, - CMSG_SELF_RES = 0x6115, // 4.3.4 15595 - SMSG_FEIGN_DEATH_RESISTED = 0x12B5, - CMSG_RUN_SCRIPT = 0x12B6, - SMSG_SCRIPT_MESSAGE = 0x12B7, - SMSG_DUEL_COUNTDOWN = 0x4836, // 4.3.4 15595 - SMSG_AREA_TRIGGER_MESSAGE = 0x4505, // 4.3.4 15595 - CMSG_SHOWING_HELM = 0x0735, // 4.3.4 15595 - CMSG_SHOWING_CLOAK = 0x4135, // 4.3.4 15595 - SMSG_ROLE_CHOSEN = 0x12BC, - SMSG_PLAYER_SKINNED = 0x0116, // 4.3.4 15595 - SMSG_DURABILITY_DAMAGE_DEATH = 0x4C27, // 4.3.4 15595 - CMSG_SET_EXPLORATION = 0x12BF, - CMSG_SET_ACTIONBAR_TOGGLES = 0x2506, // 4.3.4 15595 - UMSG_DELETE_GUILD_CHARTER = 0x12C1, - MSG_PETITION_RENAME = 0x4005, // 4.3.4 15595 - SMSG_INIT_WORLD_STATES = 0x4C15, // 4.3.4 15595 - SMSG_UPDATE_WORLD_STATE = 0x4816, // 4.3.4 15595 - SMSG_PET_ACTION_FEEDBACK = 0x0807, // 4.3.4 15595 - CMSG_CHAR_RENAME = 0x2327, // 4.3.4 15595 - SMSG_CHAR_RENAME = 0x2024, // 4.3.4 15595 - CMSG_MOVE_SPLINE_DONE = 0x790E, // 4.3.4 15595 - CMSG_MOVE_FALL_RESET = 0x310A, // 4.3.4 15595 - SMSG_INSTANCE_SAVE_CREATED = 0x0124, // 4.3.4 15595 - SMSG_RAID_INSTANCE_INFO = 0x6626, // 4.3.4 15595 - CMSG_REQUEST_RAID_INFO = 0x2F26, // 4.3.4 15595 - CMSG_MOVE_TIME_SKIPPED = 0x7A0A, // 4.3.4 15595 - CMSG_MOVE_FEATHER_FALL_ACK = 0x3110, // 4.3.4 15595 - CMSG_MOVE_WATER_WALK_ACK = 0x3B00, // 4.3.4 15595 - CMSG_MOVE_NOT_ACTIVE_MOVER = 0x7A1A, // 4.3.4 15595 - SMSG_PLAY_SOUND = 0x2134, // 4.3.4 15595 - CMSG_BATTLEFIELD_STATUS = 0x2500, // 4.3.4 15595 - SMSG_BATTLEFIELD_STATUS = 0x7DA1, // 4.3.4 15595 - SMSG_BATTLEFIELD_STATUS_ACTIVE = 0x74A4, // 4.3.4 15595 - SMSG_BATTLEFIELD_STATUS_FAILED = 0x71A7, // 4.3.4 15595 - SMSG_BATTLEFIELD_STATUS_QUEUED = 0x35A1, // 4.3.4 15595 - SMSG_BATTLEFIELD_STATUS_NEEDCONFIRMATION = 0x59A0, // 4.3.4 15595 - SMSG_BATTLEFIELD_STATUS_WAITFORGROUPS = 0x75A2, // 4.3.4 15595 - CMSG_BATTLEFIELD_PORT = 0x711A, // 4.3.4 15595 - CMSG_INSPECT_HONOR_STATS = 0x791E, // 4.3.4 15595 - SMSG_INSPECT_HONOR_STATS = 0x79A5, // 4.3.4 15595 - CMSG_BATTLEMASTER_HELLO = 0x0234, // 4.3.4 15595 - CMSG_MOVE_START_SWIM_CHEAT = 0x12D9, - CMSG_MOVE_STOP_SWIM_CHEAT = 0x12DA, - SMSG_FORCE_WALK_SPEED_CHANGE = 0x12DB, - CMSG_FORCE_WALK_SPEED_CHANGE_ACK = 0x12DC, - SMSG_FORCE_SWIM_BACK_SPEED_CHANGE = 0x12DD, - CMSG_FORCE_SWIM_BACK_SPEED_CHANGE_ACK = 0x12DE, - SMSG_FORCE_TURN_RATE_CHANGE = 0x12DF, - CMSG_FORCE_TURN_RATE_CHANGE_ACK = 0x12E0, - CMSG_PVP_LOG_DATA = 0x7308, // 4.3.4 15595 - SMSG_PVP_LOG_DATA = 0x5CB2, // 4.3.4 15595 - CMSG_LEAVE_BATTLEFIELD = 0x3018, // 4.3.4 15595 - CMSG_AREA_SPIRIT_HEALER_QUERY = 0x4907, // 4.3.4 15595 - CMSG_AREA_SPIRIT_HEALER_QUEUE = 0x4815, // 4.3.4 15595 - SMSG_AREA_SPIRIT_HEALER_TIME = 0x0734, // 4.3.4 15595 - CMSG_GM_UNTEACH = 0x12E6, - SMSG_WARDEN_DATA = 0x12E7, - CMSG_WARDEN_DATA = 0x12E8, - CMSG_BATTLEGROUND_PLAYER_POSITIONS = 0x3902, // 4.3.4 15595 - SMSG_BATTLEGROUND_PLAYER_POSITIONS = 0x58B4, // 4.3.4 15595 - CMSG_PET_STOP_ATTACK = 0x6C14, // 4.3.4 15595 - SMSG_BINDER_CONFIRM = 0x2835, // 4.3.4 15595 - SMSG_BATTLEGROUND_PLAYER_JOINED = 0x50B0, // 4.3.4 15595 - SMSG_BATTLEGROUND_PLAYER_LEFT = 0x59A6, // 4.3.4 15595 - CMSG_BATTLEMASTER_JOIN = 0x7902, // 4.3.4 15595 - SMSG_ADDON_INFO = 0x2C14, // 4.3.4 15595 - CMSG_PET_UNLEARN = 0x12F1, - SMSG_PET_UNLEARN_CONFIRM = 0x12F2, - SMSG_PARTY_MEMBER_STATS_FULL = 0x0215, // 4.3.4 15595 - CMSG_PET_SPELL_AUTOCAST = 0x2514, // 4.3.4 15595 - SMSG_WEATHER = 0x2904, // 4.3.4 15595 - SMSG_PLAY_TIME_WARNING = 0x12F6, - SMSG_MINIGAME_SETUP = 0x12F7, - SMSG_MINIGAME_STATE = 0x12F8, - CMSG_MINIGAME_MOVE = 0x12F9, - SMSG_MINIGAME_MOVE_FAILED = 0x12FA, - SMSG_RAID_INSTANCE_MESSAGE = 0x6E15, // 4.3.4 15595 - SMSG_COMPRESSED_MOVES = 0x0517, // 4.3.4 15595 - CMSG_GUILD_INFO_TEXT = 0x3227, // 4.3.4 15595 - SMSG_CHAT_RESTRICTED = 0x6536, // 4.3.4 15595 - SMSG_SPLINE_SET_RUN_SPEED = 0x12FF, - SMSG_SPLINE_SET_RUN_BACK_SPEED = 0x1300, - SMSG_SPLINE_SET_SWIM_SPEED = 0x1301, - SMSG_SPLINE_SET_WALK_SPEED = 0x1302, - SMSG_SPLINE_SET_SWIM_BACK_SPEED = 0x1303, - SMSG_SPLINE_SET_TURN_RATE = 0x1304, - SMSG_SPLINE_MOVE_UNROOT = 0x75B6, // 4.3.4 15595 - SMSG_SPLINE_MOVE_FEATHER_FALL = 0x1306, - SMSG_SPLINE_MOVE_NORMAL_FALL = 0x1307, - SMSG_SPLINE_MOVE_SET_HOVER = 0x14B6, // 4.3.4 15595 - SMSG_SPLINE_MOVE_UNSET_HOVER = 0x7DA5, // 4.3.4 15595 - SMSG_SPLINE_MOVE_WATER_WALK = 0x50A2, // 4.3.4 15595 - SMSG_SPLINE_MOVE_LAND_WALK = 0x3DA7, // 4.3.4 15595 - SMSG_SPLINE_MOVE_START_SWIM = 0x31A5, // 4.3.4 15595 - SMSG_SPLINE_MOVE_STOP_SWIM = 0x1DA2, // 4.3.4 15595 - SMSG_SPLINE_MOVE_SET_RUN_MODE = 0x75A7, // 4.3.4 15595 - SMSG_SPLINE_MOVE_SET_WALK_MODE = 0x54B6, // 4.3.4 15595 - CMSG_GM_NUKE_ACCOUNT = 0x1310, - MSG_GM_DESTROY_CORPSE = 0x1311, - CMSG_GM_DESTROY_ONLINE_CORPSE = 0x1312, - CMSG_ACTIVATETAXIEXPRESS = 0x0515, // 4.3.4 15595 - SMSG_SET_FACTION_ATWAR = 0x1314, - SMSG_GAMETIMEBIAS_SET = 0x1315, - CMSG_DEBUG_ACTIONS_START = 0x1316, - CMSG_DEBUG_ACTIONS_STOP = 0x1317, - CMSG_SET_FACTION_INACTIVE = 0x0E37, // 4.3.4 15595 - CMSG_SET_WATCHED_FACTION = 0x2434, // 4.3.4 15595 - MSG_MOVE_TIME_SKIPPED = 0x7A0A, // 4.3.4 15595 - SMSG_SPLINE_MOVE_ROOT = 0x51B4, // 4.3.4 15595 - CMSG_SET_EXPLORATION_ALL = 0x131C, - SMSG_INVALIDATE_PLAYER = 0x6325, // 4.3.4 15595 - CMSG_RESET_INSTANCES = 0x6E14, // 4.3.4 15595 - SMSG_INSTANCE_RESET = 0x6F05, // 4.3.4 15595 - SMSG_INSTANCE_RESET_FAILED = 0x4725, // 4.3.4 15595 - SMSG_UPDATE_LAST_INSTANCE = 0x0437, // 4.3.4 15595 - MSG_RAID_TARGET_UPDATE = 0x2C36, // 4.3.4 15595 - MSG_RAID_READY_CHECK = 0x2304, // 4.3.4 15595 - CMSG_LUA_USAGE = 0x1324, - SMSG_PET_ACTION_SOUND = 0x4324, // 4.3.4 15595 - SMSG_PET_DISMISS_SOUND = 0x2B05, // 4.3.4 15595 - SMSG_GHOSTEE_GONE = 0x1327, - CMSG_GM_UPDATE_TICKET_STATUS = 0x1328, - SMSG_GM_TICKET_STATUS_UPDATE = 0x2C25, // 4.3.4 15595 - MSG_SET_DUNGEON_DIFFICULTY = 0x4925, // 4.3.4 15595 - CMSG_GMSURVEY_SUBMIT = 0x2724, // 4.3.4 15595 - SMSG_UPDATE_INSTANCE_OWNERSHIP = 0x4915, // 4.3.4 15595 - CMSG_IGNORE_KNOCKBACK_CHEAT = 0x132D, - SMSG_CHAT_PLAYER_AMBIGUOUS = 0x2F34, // 4.3.4 15595 - MSG_DELAY_GHOST_TELEPORT = 0x132F, - SMSG_SPELLINSTAKILLLOG = 0x6216, // 4.3.4 15595 - SMSG_SPELL_UPDATE_CHAIN_TARGETS = 0x6006, // 4.3.4 15595 - CMSG_CHAT_FILTERED = 0x0946, // 4.3.4 15595 - SMSG_EXPECTED_SPAM_RECORDS = 0x4D36, // 4.3.4 15595 - SMSG_SPELLSTEALLOG = 0x4E26, // 4.3.4 15595 - CMSG_LOTTERY_QUERY_OBSOLETE = 0x1335, - SMSG_LOTTERY_QUERY_RESULT_OBSOLETE = 0x1336, - CMSG_BUY_LOTTERY_TICKET_OBSOLETE = 0x1337, - SMSG_LOTTERY_RESULT_OBSOLETE = 0x1338, - SMSG_CHARACTER_PROFILE = 0x1339, - SMSG_CHARACTER_PROFILE_REALM_CONNECTED = 0x133A, - SMSG_DEFENSE_MESSAGE = 0x0314, // 4.3.4 15595 - SMSG_WORLD_SERVER_INFO = 0x31A2, // 4.3.4 15595 - MSG_GM_RESETINSTANCELIMIT = 0x133D, - SMSG_MOTD = 0x0A35, // 4.3.4 15595 - SMSG_MOVE_SET_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY = 0x133F, - SMSG_MOVE_UNSET_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY = 0x1340, - CMSG_MOVE_SET_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY_ACK = 0x1341, - MSG_MOVE_START_SWIM_CHEAT = 0x1342, - MSG_MOVE_STOP_SWIM_CHEAT = 0x1343, - SMSG_MOVE_SET_CAN_FLY = 0x3DA1, // 4.3.4 15595 - SMSG_MOVE_UNSET_CAN_FLY = 0x15A2, // 4.3.4 15595 - CMSG_MOVE_SET_CAN_FLY_ACK = 0x790C, // 4.3.4 15595 - CMSG_MOVE_SET_FLY = 0x1347, - CMSG_SOCKET_GEMS = 0x2F04, // 4.3.4 15595 - CMSG_ARENA_TEAM_CREATE = 0x04A1, // 4.3.4 15595 - SMSG_ARENA_TEAM_COMMAND_RESULT = 0x39B3, // 4.3.4 15595 - MSG_MOVE_UPDATE_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY = 0x134B, - CMSG_ARENA_TEAM_QUERY = 0x0514, // 4.3.4 15595 - SMSG_ARENA_TEAM_QUERY_RESPONSE = 0x6336, // 4.3.4 15595 - CMSG_ARENA_TEAM_ROSTER = 0x6F37, // 4.3.4 15595 - SMSG_ARENA_TEAM_ROSTER = 0x2717, // 4.3.4 15595 - CMSG_ARENA_TEAM_INVITE = 0x2F27, // 4.3.4 15595 - SMSG_ARENA_TEAM_INVITE = 0x0F36, // 4.3.4 15595 - CMSG_ARENA_TEAM_ACCEPT = 0x2A25, // 4.3.4 15595 - CMSG_ARENA_TEAM_DECLINE = 0x6925, // 4.3.4 15595 - CMSG_ARENA_TEAM_LEAVE = 0x0E16, // 4.3.4 15595 - CMSG_ARENA_TEAM_REMOVE = 0x2F05, // 4.3.4 15595 - CMSG_ARENA_TEAM_DISBAND = 0x6504, // 4.3.4 15595 - CMSG_ARENA_TEAM_LEADER = 0x4204, // 4.3.4 15595 - SMSG_ARENA_TEAM_EVENT = 0x0617, // 4.3.4 15595 - CMSG_BATTLEMASTER_JOIN_ARENA = 0x701C, // 4.3.4 15595 - CMSG_MOVE_START_ASCEND = 0x390A, - CMSG_MOVE_STOP_ASCEND = 0x7B00, // 4.3.4 15595 - SMSG_ARENA_TEAM_STATS = 0x4425, // 4.3.4 15595 - CMSG_LFG_JOIN = 0x2430, // 4.3.4 15595 - CMSG_LFG_LEAVE = 0x135E, - CMSG_LFG_SEARCH_JOIN = 0x135F, - CMSG_LFG_SEARCH_LEAVE = 0x1360, - SMSG_LFG_SEARCH_RESULTS = 0x1361, - SMSG_LFG_PROPOSAL_UPDATE = 0x1362, - CMSG_LFG_PROPOSAL_RESPONSE = 0x1363, - SMSG_LFG_ROLE_CHECK_UPDATE = 0x1364, - SMSG_LFG_JOIN_RESULT = 0x1365, - SMSG_LFG_QUEUE_STATUS = 0x1366, - CMSG_SET_LFG_COMMENT = 0x0530, // 4.3.4 15595 - SMSG_LFG_UPDATE_PLAYER = 0x1368, - SMSG_LFG_UPDATE_PARTY = 0x1369, - SMSG_LFG_UPDATE_SEARCH = 0x136A, - CMSG_LFG_SET_ROLES = 0x136B, - CMSG_LFG_SET_NEEDS = 0x136C, - CMSG_LFG_BOOT_PLAYER_VOTE = 0x136D, - SMSG_LFG_BOOT_PLAYER = 0x136E, - CMSG_LFG_GET_PLAYER_INFO = 0x136F, - SMSG_LFG_PLAYER_INFO = 0x1370, - CMSG_LFG_TELEPORT = 0x1371, - CMSG_LFG_GET_PARTY_INFO = 0x1372, - SMSG_LFG_PARTY_INFO = 0x1373, - SMSG_TITLE_EARNED = 0x2426, // 4.3.4 15595 - CMSG_SET_TITLE = 0x2117, // 4.3.4 15595 - CMSG_CANCEL_MOUNT_AURA = 0x0635, // 4.3.4 15595 - SMSG_ARENA_ERROR = 0x2D17, // 4.3.4 15595 - MSG_INSPECT_ARENA_TEAMS = 0x2704, // 4.3.4 15595 - SMSG_DEATH_RELEASE_LOC = 0x2F07, // 4.3.4 15595 - CMSG_CANCEL_TEMP_ENCHANTMENT = 0x6C37, // 4.3.4 15595 - SMSG_FORCED_DEATH_UPDATE = 0x2606, // 4.3.4 15595 - CMSG_CHEAT_SET_HONOR_CURRENCY = 0x137C, - CMSG_CHEAT_SET_ARENA_CURRENCY = 0x137D, - MSG_MOVE_SET_FLIGHT_SPEED_CHEAT = 0x137E, - SMSG_MOVE_SET_FLIGHT_SPEED = 0x71A6, // 4.3.4 15595 - MSG_MOVE_SET_FLIGHT_BACK_SPEED_CHEAT = 0x1380, - SMSG_MOVE_SET_FLIGHT_BACK_SPEED = 0x30A2, // 4.3.4 15595 - SMSG_FORCE_FLIGHT_SPEED_CHANGE = 0x1382, - CMSG_FORCE_FLIGHT_SPEED_CHANGE_ACK = 0x7314, // 4.3.4 15595 - SMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE = 0x1384, - CMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE_ACK = 0x1385, - SMSG_SPLINE_SET_FLIGHT_SPEED = 0x1386, - SMSG_SPLINE_SET_FLIGHT_BACK_SPEED = 0x1387, - CMSG_MAELSTROM_INVALIDATE_CACHE = 0x1388, - SMSG_FLIGHT_SPLINE_SYNC = 0x1389, - CMSG_SET_TAXI_BENCHMARK_MODE = 0x4314, // 4.3.4 15595 - SMSG_JOINED_BATTLEGROUND_QUEUE = 0x138B, - SMSG_REALM_SPLIT = 0x2714, // 4.3.4 15595 - CMSG_REALM_SPLIT = 0x2906, // 4.3.4 15595 - CMSG_MOVE_CHNG_TRANSPORT = 0x3102, // 4.3.4 15595 - MSG_PARTY_ASSIGNMENT = 0x0424, // 4.3.4 15595 - SMSG_OFFER_PETITION_ERROR = 0x2716, // 4.3.4 15595 - SMSG_TIME_SYNC_REQ = 0x3CA4, // 4.3.4 15595 - CMSG_TIME_SYNC_RESP = 0x3B0C, // 4.3.4 15595 - CMSG_SEND_LOCAL_EVENT = 0x1393, - CMSG_SEND_GENERAL_TRIGGER = 0x1394, - CMSG_SEND_COMBAT_TRIGGER = 0x1395, - CMSG_MAELSTROM_GM_SENT_MAIL = 0x1396, - SMSG_RESET_FAILED_NOTIFY = 0x4616, // 4.3.4 15595 - SMSG_REAL_GROUP_UPDATE = 0x0F34, // 4.3.4 15595 - SMSG_LFG_DISABLED = 0x0815, // 4.3.4 15595 - CMSG_ACTIVE_PVP_CHEAT = 0x139A, - CMSG_CHEAT_DUMP_ITEMS_DEBUG_ONLY = 0x139B, - SMSG_CHEAT_DUMP_ITEMS_DEBUG_ONLY_RESPONSE = 0x139C, - SMSG_CHEAT_DUMP_ITEMS_DEBUG_ONLY_RESPONSE_WRITE_FILE = 0x139D, - SMSG_UPDATE_COMBO_POINTS = 0x6B34, // 4.3.4 15595 - SMSG_VOICE_SESSION_ROSTER_UPDATE = 0x139F, - SMSG_VOICE_SESSION_LEAVE = 0x13A0, - SMSG_VOICE_SESSION_ADJUST_PRIORITY = 0x13A1, - CMSG_VOICE_SET_TALKER_MUTED_REQUEST = 0x13A2, - SMSG_VOICE_SET_TALKER_MUTED = 0x13A3, - SMSG_INIT_EXTRA_AURA_INFO_OBSOLETE = 0x13A4, - SMSG_SET_EXTRA_AURA_INFO_OBSOLETE = 0x13A5, - SMSG_SET_EXTRA_AURA_INFO_NEED_UPDATE_OBSOLETE = 0x13A6, - SMSG_CLEAR_EXTRA_AURA_INFO_OBSOLETE = 0x13A7, - CMSG_MOVE_START_DESCEND = 0x3800, // 4.3.4 15595 - CMSG_IGNORE_REQUIREMENTS_CHEAT = 0x13A9, - SMSG_IGNORE_REQUIREMENTS_CHEAT = 0x13AA, - SMSG_SPELL_CHANCE_PROC_LOG = 0x13AB, - CMSG_MOVE_SET_RUN_SPEED = 0x13AC, - SMSG_DISMOUNT = 0x2135, // 4.3.4 15595 - SMSG_MOVE_UPDATE_CAN_FLY = 0x3DA1, // 4.3.4 15595 - MSG_RAID_READY_CHECK_CONFIRM = 0x4F05, // 4.3.4 15595 - CMSG_VOICE_SESSION_ENABLE = 0x2314, // 4.3.4 15595 - SMSG_VOICE_SESSION_ENABLE = 0x13B1, - SMSG_VOICE_PARENTAL_CONTROLS = 0x13B2, - CMSG_GM_WHISPER = 0x13B3, - SMSG_GM_MESSAGECHAT = 0x6434, // 4.3.4 15595 - MSG_GM_GEARRATING = 0x13B5, - CMSG_COMMENTATOR_ENABLE = 0x0B07, // 4.3.4 15595 - SMSG_COMMENTATOR_STATE_CHANGED = 0x0737, // 4.3.4 15595 - CMSG_COMMENTATOR_GET_MAP_INFO = 0x0026, // 4.3.4 15595 - SMSG_COMMENTATOR_MAP_INFO = 0x0327, // 4.3.4 15595 - CMSG_COMMENTATOR_GET_PLAYER_INFO = 0x0D14, // 4.3.4 15595 - SMSG_COMMENTATOR_GET_PLAYER_INFO = 0x13BB, - SMSG_COMMENTATOR_PLAYER_INFO = 0x2F36, // 4.3.4 15595 - CMSG_COMMENTATOR_ENTER_INSTANCE = 0x4105, // 4.3.4 15595 - CMSG_COMMENTATOR_EXIT_INSTANCE = 0x6136, // 4.3.4 15595 - CMSG_COMMENTATOR_INSTANCE_COMMAND = 0x0917, // 4.3.4 15595 - SMSG_CLEAR_TARGET = 0x4B26, // 4.3.4 15595 - CMSG_BOT_DETECTED = 0x13C1, - SMSG_CROSSED_INEBRIATION_THRESHOLD = 0x2036, // 4.3.4 15595 - CMSG_CHEAT_PLAYER_LOGIN = 0x13C3, - CMSG_CHEAT_PLAYER_LOOKUP = 0x13C4, - SMSG_CHEAT_PLAYER_LOOKUP = 0x13C5, - SMSG_KICK_REASON = 0x13C6, - MSG_RAID_READY_CHECK_FINISHED = 0x2E15, // 4.3.4 15595 - CMSG_COMPLAIN = 0x0427, // 4.3.4 15595 - SMSG_COMPLAIN_RESULT = 0x6D24, // 4.3.4 15595 - SMSG_FEATURE_SYSTEM_STATUS = 0x3DB7, // 4.3.4 15595 - CMSG_GM_SHOW_COMPLAINTS = 0x13CB, - CMSG_GM_UNSQUELCH = 0x13CC, - CMSG_CHANNEL_SILENCE_VOICE = 0x2D54, // 4.3.4 15595 - CMSG_CHANNEL_SILENCE_ALL = 0x2154, // 4.3.4 15595 - CMSG_CHANNEL_UNSILENCE_VOICE = 0x3146, // 4.3.4 15595 - CMSG_CHANNEL_UNSILENCE_ALL = 0x2546, // 4.3.4 15595 - CMSG_TARGET_CAST = 0x13D1, - CMSG_TARGET_SCRIPT_CAST = 0x13D2, - CMSG_CHANNEL_DISPLAY_LIST = 0x2144, // 4.3.4 15595 - CMSG_SET_ACTIVE_VOICE_CHANNEL = 0x4305, // 4.3.4 15595 - CMSG_GET_CHANNEL_MEMBER_COUNT = 0x13D5, - SMSG_CHANNEL_MEMBER_COUNT = 0x6414, // 4.3.4 15595 - CMSG_CHANNEL_VOICE_ON = 0x1144, // 4.3.4 15595 - CMSG_CHANNEL_VOICE_OFF = 0x13D8, - CMSG_DEBUG_LIST_TARGETS = 0x13D9, - SMSG_DEBUG_LIST_TARGETS = 0x13DA, - SMSG_AVAILABLE_VOICE_CHANNEL = 0x13DB, - CMSG_ADD_VOICE_IGNORE = 0x13DC, - CMSG_DEL_VOICE_IGNORE = 0x13DD, - CMSG_PARTY_SILENCE = 0x6B26, // 4.3.4 15595 - CMSG_PARTY_UNSILENCE = 0x4D24, // 4.3.4 15595 - MSG_NOTIFY_PARTY_SQUELCH = 0x4D06, // 4.3.4 15595 - SMSG_COMSAT_RECONNECT_TRY = 0x4D35, // 4.3.4 15595 - SMSG_COMSAT_DISCONNECT = 0x0316, // 4.3.4 15595 - SMSG_COMSAT_CONNECT_FAIL = 0x6317, // 4.3.4 15595 - SMSG_VOICE_CHAT_STATUS = 0x0F15, // 4.3.4 15595 - CMSG_REPORT_PVP_AFK = 0x6734, // 4.3.4 15595 - SMSG_REPORT_PVP_AFK_RESULT = 0x2D06, // 4.3.4 15595 - CMSG_GUILD_BANKER_ACTIVATE = 0x2E37, // 4.3.4 15595 - CMSG_GUILD_BANK_QUERY_TAB = 0x2E35, // 4.3.4 15595 - SMSG_GUILD_BANK_LIST = 0x78A5, // 4.3.4 15595 - CMSG_GUILD_BANK_SWAP_ITEMS = 0x2315, // 4.3.4 15595 - CMSG_GUILD_BANK_BUY_TAB = 0x0C37, // 4.3.4 15595 - CMSG_GUILD_BANK_UPDATE_TAB = 0x0106, // 4.3.4 15595 - CMSG_GUILD_BANK_DEPOSIT_MONEY = 0x0707, // 4.3.4 15595 - CMSG_GUILD_BANK_WITHDRAW_MONEY = 0x0037, // 4.3.4 15595 - CMSG_GUILD_BANK_LOG_QUERY = 0x3224, // 4.3.4 15595 - SMSG_GUILD_BANK_LOG_QUERY_RESULT = 0x30B2, // 4.3.4 15595 - CMSG_SET_CHANNEL_WATCH = 0x4517, // 4.3.4 15595 - SMSG_USERLIST_ADD = 0x0F37, // 4.3.4 15595 - SMSG_USERLIST_REMOVE = 0x2006, // 4.3.4 15595 - SMSG_USERLIST_UPDATE = 0x0135, // 4.3.4 15595 - CMSG_CLEAR_CHANNEL_WATCH = 0x2604, // 4.3.4 15595 - SMSG_INSPECT_RESULTS = 0x4014, // 4.3.4 15595 - SMSG_GOGOGO_OBSOLETE = 0x13F6, - SMSG_ECHO_PARTY_SQUELCH = 0x0814, // 4.3.4 15595 - CMSG_SET_TITLE_SUFFIX = 0x13F8, - CMSG_SPELLCLICK = 0x0805, // 4.3.4 15595 - SMSG_LOOT_LIST = 0x6807, // 4.3.4 15595 - CMSG_GM_CHARACTER_RESTORE = 0x13FB, - CMSG_GM_CHARACTER_SAVE = 0x13FC, - SMSG_VOICESESSION_FULL = 0x6225, // 4.3.4 15595 - CMSG_GUILD_PERMISSIONS = 0x3022, // 4.3.4 15595 - SMSG_GUILD_PERMISSIONS = 0x34A3, // 4.3.4 15595 - CMSG_GUILD_BANK_MONEY_WITHDRAWN = 0x1225, // 4.3.4 15595 - SMSG_GUILD_BANK_MONEY_WITHDRAWN = 0x5DB4, // 4.3.4 15595 - CMSG_GUILD_EVENT_LOG_QUERY = 0x1220, // 4.3.4 15595 - SMSG_GUILD_EVENT_LOG = 0x10B2, // 4.3.4 15595 - CMSG_MAELSTROM_RENAME_GUILD = 0x1401, - CMSG_GET_MIRRORIMAGE_DATA = 0x0C25, // 4.3.4 15595 - SMSG_MIRRORIMAGE_DATA = 0x2634, // 4.3.4 15595 - SMSG_FORCE_DISPLAY_UPDATE = 0x1404, - SMSG_SPELL_CHANCE_RESIST_PUSHBACK = 0x1405, - CMSG_IGNORE_DIMINISHING_RETURNS_CHEAT = 0x1406, - SMSG_IGNORE_DIMINISHING_RETURNS_CHEAT = 0x0125, // 4.3.4 15595 - CMSG_KEEP_ALIVE = 0x0015, // 4.3.4 15595 - SMSG_RAID_READY_CHECK_ERROR = 0x1409, - CMSG_OPT_OUT_OF_LOOT = 0x6B16, // 4.3.4 15595 - CMSG_QUERY_GUILD_BANK_TEXT = 0x3220, // 4.3.4 15595 - SMSG_GUILD_BANK_TEXT = 0x75A3, // 4.3.4 15595 - CMSG_SET_GUILD_BANK_TEXT = 0x3023, // 4.3.4 15595 - CMSG_SET_GRANTABLE_LEVELS = 0x140D, - CMSG_GRANT_LEVEL = 0x6D16, // 4.3.4 15595 - CMSG_REFER_A_FRIEND = 0x140F, - MSG_GM_CHANGE_ARENA_RATING = 0x1410, - CMSG_DECLINE_CHANNEL_INVITE = 0x1411, - SMSG_GROUPACTION_THROTTLED = 0x6524, // 4.3.4 15595 - SMSG_OVERRIDE_LIGHT = 0x4225, // 4.3.4 15595 - SMSG_TOTEM_CREATED = 0x2414, // 4.3.4 15595 - CMSG_TOTEM_DESTROYED = 0x4207, // 4.3.4 15595 - CMSG_EXPIRE_RAID_INSTANCE = 0x1416, - CMSG_NO_SPELL_VARIANCE = 0x1417, - CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY = 0x6305, // 4.3.4 15595 - SMSG_QUESTGIVER_STATUS_MULTIPLE = 0x4F25, // 4.3.4 15595 - CMSG_SET_PLAYER_DECLINED_NAMES = 0x6316, // 4.3.4 15595 - SMSG_SET_PLAYER_DECLINED_NAMES_RESULT = 0x2B25, // 4.3.4 15595 - CMSG_QUERY_SERVER_BUCK_DATA = 0x141C, - CMSG_CLEAR_SERVER_BUCK_DATA = 0x141D, - SMSG_SERVER_BUCK_DATA = 0x141E, - SMSG_SEND_UNLEARN_SPELLS = 0x4E25, // 4.3.4 15595 - SMSG_PROPOSE_LEVEL_GRANT = 0x6114, // 4.3.4 15595 - CMSG_ACCEPT_LEVEL_GRANT = 0x0205, // 4.3.4 15595 - SMSG_REFER_A_FRIEND_FAILURE = 0x2037, // 4.3.4 15595 - SMSG_SPLINE_MOVE_SET_FLYING = 0x31B5, // 4.3.4 15595 - SMSG_SPLINE_MOVE_UNSET_FLYING = 0x58A6, // 4.3.4 15595 - SMSG_SUMMON_CANCEL = 0x0B34, // 4.3.4 15595 - CMSG_CHANGE_PERSONAL_ARENA_RATING = 0x1426, - CMSG_ALTER_APPEARANCE = 0x0914, // 4.3.4 15595 - SMSG_ENABLE_BARBER_SHOP = 0x2D16, // 4.3.4 15595 - SMSG_BARBER_SHOP_RESULT = 0x6125, // 4.3.4 15595 - CMSG_CALENDAR_GET_CALENDAR = 0x2814, // 4.3.4 15595 - CMSG_CALENDAR_GET_EVENT = 0x142B, - CMSG_CALENDAR_GUILD_FILTER = 0x142C, - CMSG_CALENDAR_ARENA_TEAM = 0x142D, - CMSG_CALENDAR_ADD_EVENT = 0x0726, // 4.3.4 15595 - CMSG_CALENDAR_UPDATE_EVENT = 0x142F, - CMSG_CALENDAR_REMOVE_EVENT = 0x1430, - CMSG_CALENDAR_COPY_EVENT = 0x1431, - CMSG_CALENDAR_EVENT_INVITE = 0x2435, // 4.3.4 15595 - CMSG_CALENDAR_EVENT_RSVP = 0x1433, - CMSG_CALENDAR_EVENT_REMOVE_INVITE = 0x1434, - CMSG_CALENDAR_EVENT_STATUS = 0x1435, - CMSG_CALENDAR_EVENT_MODERATOR_STATUS = 0x1436, - SMSG_CALENDAR_SEND_CALENDAR = 0x6805, // 4.3.4 15595 - SMSG_CALENDAR_SEND_EVENT = 0x1438, // 4.3.4 15595 - SMSG_CALENDAR_FILTER_GUILD = 0x1439, - SMSG_CALENDAR_ARENA_TEAM = 0x143A, - SMSG_CALENDAR_EVENT_INVITE = 0x143B, - SMSG_CALENDAR_EVENT_INVITE_REMOVED = 0x143C, - SMSG_CALENDAR_EVENT_STATUS = 0x143D, - SMSG_CALENDAR_COMMAND_RESULT = 0x143E, - SMSG_CALENDAR_RAID_LOCKOUT_ADDED = 0x143F, - SMSG_CALENDAR_RAID_LOCKOUT_REMOVED = 0x1440, - SMSG_CALENDAR_EVENT_INVITE_ALERT = 0x1441, // 4.3.4 15595 - SMSG_CALENDAR_EVENT_INVITE_REMOVED_ALERT = 0x1442, - SMSG_CALENDAR_EVENT_INVITE_STATUS_ALERT = 0x1443, - SMSG_CALENDAR_EVENT_REMOVED_ALERT = 0x1444, - SMSG_CALENDAR_EVENT_UPDATED_ALERT = 0x1445, - SMSG_CALENDAR_EVENT_MODERATOR_STATUS_ALERT = 0x1446, - CMSG_CALENDAR_COMPLAIN = 0x1447, - CMSG_CALENDAR_GET_NUM_PENDING = 0x4D05, // 4.3.4 15595 - SMSG_CALENDAR_SEND_NUM_PENDING = 0x0C17, // 4.3.4 15595 - CMSG_SAVE_DANCE = 0x144A, - SMSG_NOTIFY_DANCE = 0x4904, // 4.3.4 15595 - CMSG_PLAY_DANCE = 0x6914, // 4.3.4 15595 - SMSG_PLAY_DANCE = 0x4704, // 4.3.4 15595 - CMSG_LOAD_DANCES = 0x144E, - CMSG_STOP_DANCE = 0x2907, // 4.3.4 15595 - SMSG_STOP_DANCE = 0x4637, // 4.3.4 15595 - CMSG_SYNC_DANCE = 0x0036, // 4.3.4 15595 - CMSG_DANCE_QUERY = 0x4E07, // 4.3.4 15595 - SMSG_DANCE_QUERY_RESPONSE = 0x2F06, // 4.3.4 15595 - SMSG_INVALIDATE_DANCE = 0x0E27, // 4.3.4 15595 - CMSG_DELETE_DANCE = 0x1455, - SMSG_LEARNED_DANCE_MOVES = 0x0E05, // 4.3.4 15595 - CMSG_LEARN_DANCE_MOVE = 0x1457, - CMSG_UNLEARN_DANCE_MOVE = 0x1458, - CMSG_SET_RUNE_COUNT = 0x1459, - CMSG_SET_RUNE_COOLDOWN = 0x145A, - MSG_MOVE_SET_PITCH_RATE_CHEAT = 0x145B, - SMSG_MOVE_SET_PITCH_RATE = 0x75B0, // 4.3.4 15595 - SMSG_FORCE_PITCH_RATE_CHANGE = 0x145D, - CMSG_FORCE_PITCH_RATE_CHANGE_ACK = 0x145E, - SMSG_SPLINE_SET_PITCH_RATE = 0x145F, - CMSG_CALENDAR_EVENT_INVITE_NOTES = 0x1460, - SMSG_CALENDAR_EVENT_INVITE_NOTES = 0x1461, - SMSG_CALENDAR_EVENT_INVITE_NOTES_ALERT = 0x1462, - CMSG_UPDATE_MISSILE_TRAJECTORY = 0x781E, // 4.3.4 15595 - SMSG_UPDATE_ACCOUNT_DATA_COMPLETE = 0x2015, // 4.3.4 15595 - SMSG_TRIGGER_MOVIE = 0x4625, // 4.3.4 15595 - CMSG_COMPLETE_MOVIE = 0x4136, // 4.3.4 15595 - CMSG_SET_GLYPH_SLOT = 0x1467, - CMSG_SET_GLYPH = 0x1468, - SMSG_ACHIEVEMENT_EARNED = 0x4405, // 4.3.4 15595 - SMSG_DYNAMIC_DROP_ROLL_RESULT = 0x146A, - SMSG_CRITERIA_UPDATE = 0x6E37, // 4.3.4 15595 - CMSG_QUERY_INSPECT_ACHIEVEMENTS = 0x4D27, // 4.3.4 15595 - SMSG_RESPOND_INSPECT_ACHIEVEMENTS = 0x15B0, // 4.3.4 15595 - CMSG_DISMISS_CONTROLLED_VEHICLE = 0x3218, // 4.3.4 15595 - CMSG_COMPLETE_ACHIEVEMENT_CHEAT = 0x146F, - SMSG_QUESTUPDATE_ADD_PVP_KILL = 0x4416, // 4.3.4 15595 - CMSG_SET_CRITERIA_CHEAT = 0x1471, - SMSG_CALENDAR_RAID_LOCKOUT_UPDATED = 0x1472, - CMSG_UNITANIMTIER_CHEAT = 0x1473, - CMSG_CHAR_CUSTOMIZE = 0x2C34, // 4.3.4 15595 - SMSG_CHAR_CUSTOMIZE = 0x4F16, // 4.3.4 15595 - SMSG_PET_RENAMEABLE = 0x2B27, // 4.3.4 15595 - CMSG_REQUEST_VEHICLE_EXIT = 0x2B35, // 4.3.4 15595 - CMSG_REQUEST_VEHICLE_PREV_SEAT = 0x4C04, // 4.3.4 15595 - CMSG_REQUEST_VEHICLE_NEXT_SEAT = 0x4434, // 4.3.4 15595 - CMSG_REQUEST_VEHICLE_SWITCH_SEAT = 0x4C14, // 4.3.4 15595 - CMSG_PET_LEARN_TALENT = 0x6725, // 4.3.4 15595 - CMSG_PET_UNLEARN_TALENTS = 0x147C, - SMSG_SET_PHASE_SHIFT = 0x70A0, // 4.3.4 15595 - SMSG_ALL_ACHIEVEMENT_DATA = 0x58B1, // 4.3.4 15595 - CMSG_FORCE_SAY_CHEAT = 0x147F, - SMSG_HEALTH_UPDATE = 0x4734, // 4.3.4 15595 - SMSG_POWER_UPDATE = 0x4A07, // 4.3.4 15595 - CMSG_GAMEOBJ_REPORT_USE = 0x4827, // 4.3.4 15595 - SMSG_HIGHEST_THREAT_UPDATE = 0x4104, // 4.3.4 15595 - SMSG_THREAT_UPDATE = 0x4735, // 4.3.4 15595 - SMSG_THREAT_REMOVE = 0x2E05, // 4.3.4 15595 - SMSG_THREAT_CLEAR = 0x6437, // 4.3.4 15595 - SMSG_CONVERT_RUNE = 0x4F14, // 4.3.4 15595 - SMSG_RESYNC_RUNES = 0x6224, // 4.3.4 15595 - SMSG_ADD_RUNE_POWER = 0x6915, // 4.3.4 15595 - CMSG_START_QUEST = 0x148A, - CMSG_REMOVE_GLYPH = 0x148B, - CMSG_DUMP_OBJECTS = 0x148C, - SMSG_DUMP_OBJECTS_DATA = 0x148D, - CMSG_DISMISS_CRITTER = 0x4227, - SMSG_NOTIFY_DEST_LOC_SPELL_CAST = 0x148F, - CMSG_AUCTION_LIST_PENDING_SALES = 0x2C17, // 4.3.4 15595 - SMSG_AUCTION_LIST_PENDING_SALES = 0x6A27, // 4.3.4 15595 - SMSG_MODIFY_COOLDOWN = 0x6016, // 4.3.4 15595 - SMSG_PET_UPDATE_COMBO_POINTS = 0x4325, // 4.3.4 15595 - CMSG_ENABLETAXI = 0x0C16, // 4.3.4 15595 - SMSG_PRE_RESURRECT = 0x6C36, // 4.3.4 15595 - SMSG_AURA_UPDATE_ALL = 0x6916, // 4.3.4 15595 - SMSG_AURA_UPDATE = 0x4707, // 4.3.4 15595 - CMSG_FLOOD_GRACE_CHEAT = 0x1498, - SMSG_SERVER_FIRST_ACHIEVEMENT = 0x6424, // 4.3.4 15595 - SMSG_PET_LEARNED_SPELL = 0x0507, // 4.3.4 15595 - SMSG_PET_REMOVED_SPELL = 0x6A04, // 4.3.4 15595 - CMSG_CHANGE_SEATS_ON_CONTROLLED_VEHICLE = 0x7310, // 4.3.4 15595 - CMSG_HEARTH_AND_RESURRECT = 0x4B34, // 4.3.4 15595 - SMSG_ON_CANCEL_EXPECTED_RIDE_VEHICLE_AURA = 0x4D34, // 4.3.4 15595 - SMSG_CRITERIA_DELETED = 0x2915, // 4.3.4 15595 - SMSG_ACHIEVEMENT_DELETED = 0x6A16, // 4.3.4 15595 - CMSG_SERVER_INFO_QUERY = 0x14A1, - SMSG_SERVER_INFO_RESPONSE = 0x14A2, - CMSG_CHECK_LOGIN_CRITERIA = 0x14A3, - SMSG_SERVER_BUCK_DATA_START = 0x14A4, - CMSG_SET_BREATH = 0x14A5, - CMSG_QUERY_VEHICLE_STATUS = 0x14A6, - SMSG_BATTLEGROUND_INFO_THROTTLED = 0x14A7, - SMSG_SET_VEHICLE_REC_ID = 0x14A8, - CMSG_RIDE_VEHICLE_INTERACT = 0x14A9, - CMSG_CONTROLLER_EJECT_PASSENGER = 0x14AA, - SMSG_PET_GUIDS = 0x2D26, // 4.3.4 15595 - SMSG_CLIENTCACHE_VERSION = 0x2734, // 4.3.4 15595 - CMSG_CHANGE_GDF_ARENA_RATING = 0x14AD, - CMSG_SET_ARENA_TEAM_RATING_BY_INDEX = 0x14AE, - CMSG_SET_ARENA_TEAM_WEEKLY_GAMES = 0x14AF, - CMSG_SET_ARENA_TEAM_SEASON_GAMES = 0x14B0, - CMSG_SET_ARENA_MEMBER_WEEKLY_GAMES = 0x14B1, - CMSG_SET_ARENA_MEMBER_SEASON_GAMES = 0x14B2, - SMSG_SET_ITEM_PURCHASE_DATA = 0x15A3, // 4.3.4 15595 - CMSG_GET_ITEM_PURCHASE_DATA = 0x2206, // 4.3.4 15595 - CMSG_ITEM_PURCHASE_REFUND = 0x6134, // 4.3.4 15595 - SMSG_ITEM_PURCHASE_REFUND_RESULT = 0x5DB1, // 4.3.4 15595 - CMSG_CORPSE_TRANSPORT_QUERY = 0x6205, // 4.3.4 15595 - SMSG_CORPSE_TRANSPORT_QUERY = 0x0E35, // 4.3.4 15595 - CMSG_UNUSED5 = 0x14B9, - CMSG_UNUSED6 = 0x14BA, - CMSG_CALENDAR_EVENT_SIGNUP = 0x14BB, - SMSG_CALENDAR_CLEAR_PENDING_ACTION = 0x14BC, - SMSG_LOAD_EQUIPMENT_SET = 0x2E04, // 4.3.4 15595 - CMSG_SAVE_EQUIPMENT_SET = 0x4F27, // 4.3.4 15595 - CMSG_ON_MISSILE_TRAJECTORY_COLLISION = 0x14BF, - SMSG_NOTIFY_MISSILE_TRAJECTORY_COLLISION = 0x14C0, - SMSG_TALENT_UPDATE = 0x6F26, // 4.3.4 15595 - CMSG_LEARN_TALENT_GROUP = 0x2415, // 4.3.4 15595 - CMSG_PET_LEARN_TALENT_GROUP = 0x6E24, // 4.3.4 15595 - CMSG_SET_ACTIVE_TALENT_GROUP_OBSOLETE = 0x14C4, - CMSG_GM_GRANT_ACHIEVEMENT = 0x14C5, - CMSG_GM_REMOVE_ACHIEVEMENT = 0x14C6, - CMSG_GM_SET_CRITERIA_FOR_PLAYER = 0x14C7, - SMSG_DESTROY_ARENA_UNIT = 0x2637, // 4.3.4 15595 - SMSG_ARENA_TEAM_CHANGE_FAILED = 0x6E34, // 4.3.4 15595 - CMSG_PROFILEDATA_REQUEST = 0x14CA, - SMSG_PROFILEDATA_RESPONSE = 0x14CB, - CMSG_START_BATTLEFIELD_CHEAT = 0x14CC, - CMSG_END_BATTLEFIELD_CHEAT = 0x14CD, - SMSG_COMPOUND_MOVE = 0x14CE, - SMSG_MOVE_GRAVITY_DISABLE = 0x75B2, // 4.3.4 15595 - CMSG_MOVE_GRAVITY_DISABLE_ACK = 0x3118, // 4.3.4 15595 - SMSG_MOVE_GRAVITY_ENABLE = 0x30B3, // 4.3.4 15595 - CMSG_MOVE_GRAVITY_ENABLE_ACK = 0x700A, // 4.3.4 15595 - MSG_MOVE_GRAVITY_CHNG = 0x14D3, - SMSG_SPLINE_MOVE_GRAVITY_DISABLE = 0x5DB5, // 4.3.4 15595 - SMSG_SPLINE_MOVE_GRAVITY_ENABLE = 0x3CA6, // 4.3.4 15595 - CMSG_USE_EQUIPMENT_SET = 0x0417, // 4.3.4 15595 - SMSG_USE_EQUIPMENT_SET_RESULT = 0x2424, // 4.3.4 15595 - CMSG_FORCE_ANIM = 0x14D8, - SMSG_FORCE_ANIM = 0x4C05, // 4.3.4 15595 - CMSG_CHAR_FACTION_CHANGE = 0x2735, // 4.3.4 15595 - SMSG_CHAR_FACTION_CHANGE = 0x4C06, // 4.3.4 15595 - CMSG_PVP_QUEUE_STATS_REQUEST = 0x14DC, - SMSG_PVP_QUEUE_STATS = 0x14DD, - CMSG_SET_PAID_SERVICE_CHEAT = 0x14DE, - SMSG_BATTLEFIELD_MANAGER_ENTRY_INVITE = 0x14DF, - CMSG_BATTLEFIELD_MANAGER_ENTRY_INVITE_RESPONSE = 0x14E0, - SMSG_BATTLEFIELD_MANAGER_ENTERING = 0x14E1, - SMSG_BATTLEFIELD_MANAGER_QUEUE_INVITE = 0x14E2, - CMSG_BATTLEFIELD_MANAGER_QUEUE_INVITE_RESPONSE = 0x14E3, - CMSG_BATTLEFIELD_MANAGER_QUEUE_REQUEST = 0x710C, // 4.3.4 15595 - SMSG_BATTLEFIELD_MANAGER_QUEUE_REQUEST_RESPONSE = 0x14E5, - SMSG_BATTLEFIELD_MANAGER_EJECT_PENDING = 0x14E6, - SMSG_BATTLEFIELD_MANAGER_EJECTED = 0x14E7, - CMSG_BATTLEFIELD_MANAGER_EXIT_REQUEST = 0x14E8, - SMSG_BATTLEFIELD_MANAGER_STATE_CHANGED = 0x14E9, - CMSG_BATTLEFIELD_MANAGER_ADVANCE_STATE = 0x14EA, - CMSG_BATTLEFIELD_MANAGER_SET_NEXT_TRANSITION_TIME = 0x14EB, - MSG_SET_RAID_DIFFICULTY = 0x0614, // 4.3.4 15595 - CMSG_XPGAIN = 0x14ED, - SMSG_XPGAIN = 0x14EE, - SMSG_GMTICKET_RESPONSE_ERROR = 0x14EF, - SMSG_GMTICKET_GET_RESPONSE = 0x2E34, // 4.3.4 15595 - CMSG_GMTICKET_RESOLVE_RESPONSE = 0x6506, // 4.3.4 15595 - SMSG_GMTICKET_RESOLVE_RESPONSE = 0x0A04, // 4.3.4 15595 - SMSG_GMTICKET_CREATE_RESPONSE_TICKET = 0x14F3, - CMSG_GM_CREATE_TICKET_RESPONSE = 0x14F4, - CMSG_SERVERINFO = 0x14F5, - SMSG_SERVERINFO = 0x14F6, - CMSG_UI_TIME_REQUEST = 0x4605, // 4.3.4 15595 - SMSG_UI_TIME = 0x4A14, // 4.3.4 15595 - CMSG_CHAR_RACE_CHANGE = 0x0D24, // 4.3.4 15595 - MSG_VIEW_PHASE_SHIFT = 0x14FA, - SMSG_TALENTS_INVOLUNTARILY_RESET = 0x14FB, - CMSG_DEBUG_SERVER_GEO = 0x14FC, - SMSG_DEBUG_SERVER_GEO = 0x14FD, - SMSG_LOOT_UPDATE = 0x14FE, - UMSG_UPDATE_GROUP_INFO = 0x14FF, - CMSG_READY_FOR_ACCOUNT_DATA_TIMES = 0x2B16, // 4.3.4 15595 - CMSG_QUERY_GET_ALL_QUESTS = 0x2317, // 4.3.4 15595 - SMSG_ALL_QUESTS_COMPLETED = 0x6314, // 4.3.4 15595 - CMSG_GMLAGREPORT_SUBMIT = 0x1503, - CMSG_AFK_MONITOR_INFO_REQUEST = 0x1504, - SMSG_AFK_MONITOR_INFO_RESPONSE = 0x1505, - CMSG_AFK_MONITOR_INFO_CLEAR = 0x1506, - SMSG_AREA_TRIGGER_NO_CORPSE = 0x2A14, // 4.3.4 15595 - CMSG_GM_NUKE_CHARACTER = 0x1508, - CMSG_LOW_LEVEL_RAID = 0x4435, // 4.3.4 15595 - CMSG_LOW_LEVEL_RAID_USER = 0x0536, // 4.3.4 15595 - SMSG_CAMERA_SHAKE = 0x4214, // 4.3.4 15595 - SMSG_SOCKET_GEMS = 0x6014, // 4.3.4 15595 - CMSG_SET_CHARACTER_MODEL = 0x150D, - SMSG_CONNECT_TO = 0x0942, // 4.3.4 15595 - CMSG_CONNECT_TO_FAILED = 0x2533, // 4.3.4 15595 - SMSG_SUSPEND_COMMS = 0x4140, // 4.3.4 15595 - CMSG_SUSPEND_COMMS_ACK = 0x1511, - SMSG_RESUME_COMMS = 0x1512, - CMSG_AUTH_CONTINUED_SESSION = 0x1513, - CMSG_DROP_NEW_CONNECTION = 0x1514, - SMSG_SEND_ALL_COMBAT_LOG = 0x1515, - SMSG_OPEN_LFG_DUNGEON_FINDER = 0x0412, // 4.3.4 15595 - SMSG_MOVE_SET_COLLISION_HGT = 0x11B0, // 4.3.4 15595 - CMSG_MOVE_SET_COLLISION_HGT_ACK = 0x1518, - MSG_MOVE_SET_COLLISION_HGT = 0x1519, - CMSG_CLEAR_RANDOM_BG_WIN_TIME = 0x151A, - CMSG_CLEAR_HOLIDAY_BG_WIN_TIME = 0x151B, - CMSG_COMMENTATOR_SKIRMISH_QUEUE_COMMAND = 0x0025, // 4.3.4 15595 - SMSG_COMMENTATOR_SKIRMISH_QUEUE_RESULT1 = 0x2126, // 4.3.4 15595 - SMSG_COMMENTATOR_SKIRMISH_QUEUE_RESULT2 = 0x6814, // 4.3.4 15595 - SMSG_COMPRESSED_UNKNOWN_1310 = 0x151F, - SMSG_UNKNOWN_1311 = 0x1520, - SMSG_UNKNOWN_1312 = 0x1521, - UMSG_UNKNOWN_1313 = 0x1522, - SMSG_UNKNOWN_1314 = 0x1523, - SMSG_UNKNOWN_1315 = 0x1524, - SMSG_UNKNOWN_1316 = 0x1525, - SMSG_UNKNOWN_1317 = 0x1526, - UMSG_UNKNOWN_1318 = 0x1527, - UMSG_UNKNOWN_1319 = 0x1528, - CMSG_UNKNOWN_1320 = 0x1529, - UMSG_UNKNOWN_1321 = 0x152A, - UMSG_UNKNOWN_1322 = 0x152B, - UMSG_UNKNOWN_1323 = 0x152C, - UMSG_UNKNOWN_1324 = 0x152D, - UMSG_UNKNOWN_1325 = 0x152E, - UMSG_UNKNOWN_1326 = 0x152F, - UMSG_UNKNOWN_1327 = 0x1530, - UMSG_UNKNOWN_1328 = 0x1531, - SMSG_UNKNOWN_1329 = 0x1532, - UMSG_UNKNOWN_1330 = 0x1533, - UMSG_UNKNOWN_1331 = 0x1534, - UMSG_UNKNOWN_1332 = 0x1535, - UMSG_UNKNOWN_1333 = 0x1536, - UMSG_UNKNOWN_1334 = 0x1537, - SMSG_PLAYER_MOVE = 0x79A2, // 4.3.4 15595 - SMSG_SPLINE_MOVE_SET_FLIGHT_BACK_SPEED = 0x38B3, // 4.3.4 15595 - SMSG_SPLINE_MOVE_SET_FLIGHT_SPEED = 0x39A0, // 4.3.4 15595 - SMSG_SPLINE_MOVE_SET_PITCH_RATE = 0x14B0, // 4.3.4 15595 - SMSG_SPLINE_MOVE_SET_RUN_BACK_SPEED = 0x3DB3, // 4.3.4 15595 - SMSG_SPLINE_MOVE_SET_RUN_SPEED = 0x51B7, // 4.3.4 15595 - SMSG_SPLINE_MOVE_SET_SWIM_SPEED = 0x39A4, // 4.3.4 15595 - SMSG_SPLINE_MOVE_SET_SWIM_BACK_SPEED = 0x59A1, // 4.3.4 15595 - SMSG_SPLINE_MOVE_SET_TURN_RATE = 0x78B5, // 4.3.4 15595 - SMSG_SPLINE_MOVE_SET_WALK_SPEED = 0x34A5, // 4.3.4 15595 - CMSG_REORDER_CHARACTERS = 0x0593, // 4.3.4 15595 - SMSG_SET_CURRENCY_WEEK_LIMIT = 0x70A7, // 4.3.4 15595 - SMSG_SET_CURRENCY = 0x59B0, // 4.3.4 15595 - SMSG_SEND_CURRENCIES = 0x15A5, // 4.3.4 15595 - CMSG_SET_CURRENCY_FLAGS = 0x7306, // 4.3.4 15595 - SMSG_WEEKLY_RESET_CURRENCIES = 0x3CA1, // 4.3.4 15595 - CMSG_INSPECT_RATED_BG_STATS = 0x3010, // 4.3.4 15595 - CMSG_REQUEST_RATED_BG_INFO = 0x2423, // 4.3.4 15595 - CMSG_REQUEST_RATED_BG_STATS = 0x05B3, // 4.3.4 15595 - SMSG_RATED_BG_STATS = 0x34A1, // 4.3.4 15595 - CMSG_REQUEST_PVP_REWARDS = 0x780C, // 4.3.4 15595 - SMSG_PVP_REWARDS = 0x5DA4, // 4.3.4 15595 - CMSG_REQUEST_PVP_OPTIONS_ENABLED = 0x24A1, // 4.3.4 15595 - SMSG_PVP_OPTIONS_ENABLED = 0x50A1, // 4.3.4 15595 - CMSG_REQUEST_HOTFIX = 0x2401, // 4.3.4 15595 - SMSG_DB_REPLY = 0x38A4, // 4.3.4 15595 - CMSG_OBJECT_UPDATE_FAILED = 0x3808, // 4.3.4 15595 - CMSG_REFORGE_ITEM = 0x331A, // 4.3.4 15595 - SMSG_REFORGE_RESULT = 0x58A4, // 4.3.4 15595 - CMSG_LOAD_SCREEN = 0x2422, // 4.3.4 15595 - SMSG_START_TIMER = 0x59A5, // 4.3.4 15595 -}; - -#define MAX_OPCODE_TABLE_SIZE 0xFFFF - -extern void InitializeOpcodes(); - -/// Player state -enum SessionStatus -{ - STATUS_AUTHED = 0, ///< Player authenticated (_player==NULL, m_playerRecentlyLogout = false or will be reset before handler call, m_GUID have garbage) - STATUS_LOGGEDIN, ///< Player in game (_player!=NULL, m_GUID == _player->GetGUID(), inWorld()) - STATUS_TRANSFER, ///< Player transferring to another map (_player!=NULL, m_GUID == _player->GetGUID(), !inWorld()) - STATUS_LOGGEDIN_OR_RECENTLY_LOGGEDOUT, ///< _player!= NULL or _player==NULL && m_playerRecentlyLogout, m_GUID store last _player guid) - STATUS_NEVER, ///< Opcode not accepted from client (deprecated or server side only) - STATUS_UNHANDLED ///< We don' handle this opcode yet -}; - -enum PacketProcessing -{ - PROCESS_INPLACE = 0, // process packet whenever we receive it - mostly for non-handled or non-implemented packets - PROCESS_THREADUNSAFE, // packet is not thread-safe - process it in World::UpdateSessions() - PROCESS_THREADSAFE // packet is thread-safe - process it in Map::Update() -}; - -class WorldPacket; - -struct OpcodeHandler -{ - char const* name; - SessionStatus status; - PacketProcessing packetProcessing; - void (WorldSession::*handler)(WorldPacket& recvPacket); -}; - -extern OpcodeHandler opcodeTable[MAX_OPCODE_TABLE_SIZE]; - -/// Lookup opcode name for human understandable logging -inline const char* LookupOpcodeName(uint16 id) -{ - return opcodeTable[id].name; -} -#endif -/// @} diff --git a/src/game/Player.cpp b/src/game/Player.cpp deleted file mode 100644 index dbe6a725e..000000000 --- a/src/game/Player.cpp +++ /dev/null @@ -1,24506 +0,0 @@ -/** - * This code is part of MaNGOS. Contributor & Copyright details are in AUTHORS/THANKS. - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Player.h" -#include "Language.h" -#include "Database/DatabaseEnv.h" -#include "Log.h" -#include "Opcodes.h" -#include "SpellMgr.h" -#include "World.h" -#include "WorldPacket.h" -#include "WorldSession.h" -#include "UpdateMask.h" -#include "SkillDiscovery.h" -#include "QuestDef.h" -#include "GossipDef.h" -#include "UpdateData.h" -#include "Channel.h" -#include "ChannelMgr.h" -#include "MapManager.h" -#include "MapPersistentStateMgr.h" -#include "InstanceData.h" -#include "GridNotifiers.h" -#include "GridNotifiersImpl.h" -#include "CellImpl.h" -#include "ObjectMgr.h" -#include "ObjectAccessor.h" -#include "CreatureAI.h" -#include "Formulas.h" -#include "Group.h" -#include "Guild.h" -#include "GuildMgr.h" -#include "Pet.h" -#include "Util.h" -#include "Transports.h" -#include "Weather.h" -#include "BattleGround/BattleGround.h" -#include "BattleGround/BattleGroundMgr.h" -#include "BattleGround/BattleGroundAV.h" -#include "OutdoorPvP/OutdoorPvP.h" -#include "ArenaTeam.h" -#include "Chat.h" -#include "Database/DatabaseImpl.h" -#include "Spell.h" -#include "ScriptMgr.h" -#include "SocialMgr.h" -#include "AchievementMgr.h" -#include "Mail.h" -#include "SpellAuras.h" -#include "DBCStores.h" -#include "DB2Stores.h" -#include "SQLStorages.h" -#include "Vehicle.h" -#include "Calendar.h" -#include "PhaseMgr.h" - -#include - -#define ZONE_UPDATE_INTERVAL (1*IN_MILLISECONDS) - -enum CharacterFlags -{ - CHARACTER_FLAG_NONE = 0x00000000, - CHARACTER_FLAG_UNK1 = 0x00000001, - CHARACTER_FLAG_UNK2 = 0x00000002, - CHARACTER_LOCKED_FOR_TRANSFER = 0x00000004, - CHARACTER_FLAG_UNK4 = 0x00000008, - CHARACTER_FLAG_UNK5 = 0x00000010, - CHARACTER_FLAG_UNK6 = 0x00000020, - CHARACTER_FLAG_UNK7 = 0x00000040, - CHARACTER_FLAG_UNK8 = 0x00000080, - CHARACTER_FLAG_UNK9 = 0x00000100, - CHARACTER_FLAG_UNK10 = 0x00000200, - CHARACTER_FLAG_HIDE_HELM = 0x00000400, - CHARACTER_FLAG_HIDE_CLOAK = 0x00000800, - CHARACTER_FLAG_UNK13 = 0x00001000, - CHARACTER_FLAG_GHOST = 0x00002000, - CHARACTER_FLAG_RENAME = 0x00004000, - CHARACTER_FLAG_UNK16 = 0x00008000, - CHARACTER_FLAG_UNK17 = 0x00010000, - CHARACTER_FLAG_UNK18 = 0x00020000, - CHARACTER_FLAG_UNK19 = 0x00040000, - CHARACTER_FLAG_UNK20 = 0x00080000, - CHARACTER_FLAG_UNK21 = 0x00100000, - CHARACTER_FLAG_UNK22 = 0x00200000, - CHARACTER_FLAG_UNK23 = 0x00400000, - CHARACTER_FLAG_UNK24 = 0x00800000, - CHARACTER_FLAG_LOCKED_BY_BILLING = 0x01000000, - CHARACTER_FLAG_DECLINED = 0x02000000, - CHARACTER_FLAG_UNK27 = 0x04000000, - CHARACTER_FLAG_UNK28 = 0x08000000, - CHARACTER_FLAG_UNK29 = 0x10000000, - CHARACTER_FLAG_UNK30 = 0x20000000, - CHARACTER_FLAG_UNK31 = 0x40000000, - CHARACTER_FLAG_UNK32 = 0x80000000 -}; - -enum CharacterCustomizeFlags -{ - CHAR_CUSTOMIZE_FLAG_NONE = 0x00000000, - CHAR_CUSTOMIZE_FLAG_CUSTOMIZE = 0x00000001, // name, gender, etc... - CHAR_CUSTOMIZE_FLAG_FACTION = 0x00010000, // name, gender, faction, etc... - CHAR_CUSTOMIZE_FLAG_RACE = 0x00100000 // name, gender, race, etc... -}; - -// corpse reclaim times -#define DEATH_EXPIRE_STEP (5*MINUTE) -#define MAX_DEATH_COUNT 3 - -static const uint32 corpseReclaimDelay[MAX_DEATH_COUNT] = {30, 60, 120}; - -//== PlayerTaxi ================================================ - -PlayerTaxi::PlayerTaxi() -{ - // Taxi nodes - memset(m_taximask, 0, sizeof(m_taximask)); -} - -void PlayerTaxi::InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint32 level) -{ - // class specific initial known nodes - switch (chrClass) - { - case CLASS_DEATH_KNIGHT: - { - for (int i = 0; i < TaxiMaskSize; ++i) - m_taximask[i] |= sOldContinentsNodesMask[i]; - break; - } - } - - // race specific initial known nodes: capital and taxi hub masks - switch (race) - { - case RACE_HUMAN: SetTaximaskNode(2); break; // Human - case RACE_ORC: SetTaximaskNode(23); break; // Orc - case RACE_DWARF: SetTaximaskNode(6); break; // Dwarf - case RACE_NIGHTELF: SetTaximaskNode(26); - SetTaximaskNode(27); break; // Night Elf - case RACE_UNDEAD: SetTaximaskNode(11); break; // Undead - case RACE_TAUREN: SetTaximaskNode(22); break; // Tauren - case RACE_GNOME: SetTaximaskNode(6); break; // Gnome - case RACE_TROLL: SetTaximaskNode(23); break; // Troll - case RACE_BLOODELF: SetTaximaskNode(82); break; // Blood Elf - case RACE_DRAENEI: SetTaximaskNode(94); break; // Draenei - } - - // new continent starting masks (It will be accessible only at new map) - switch (Player::TeamForRace(race)) - { - case ALLIANCE: SetTaximaskNode(100); break; - case HORDE: SetTaximaskNode(99); break; - default: break; - } - // level dependent taxi hubs - if (level >= 68) - SetTaximaskNode(213); // Shattered Sun Staging Area -} - -void PlayerTaxi::LoadTaxiMask(const char* data) -{ - Tokens tokens = StrSplit(data, " "); - - int index; - Tokens::iterator iter; - for (iter = tokens.begin(), index = 0; - (index < TaxiMaskSize) && (iter != tokens.end()); ++iter, ++index) - { - // load and set bits only for existing taxi nodes - m_taximask[index] = sTaxiNodesMask[index] & uint8(atol((*iter).c_str())); - } -} - -void PlayerTaxi::AppendTaximaskTo(ByteBuffer& data, bool all) -{ - data << uint32(TaxiMaskSize); - if (all) - { - for (uint8 i = 0; i < TaxiMaskSize; ++i) - data << uint8(sTaxiNodesMask[i]); // all existing nodes - } - else - { - for (uint8 i = 0; i < TaxiMaskSize; ++i) - data << uint8(m_taximask[i]); // known nodes - } -} - -bool PlayerTaxi::LoadTaxiDestinationsFromString(const std::string& values, Team team) -{ - ClearTaxiDestinations(); - - Tokens tokens = StrSplit(values, " "); - - for (Tokens::iterator iter = tokens.begin(); iter != tokens.end(); ++iter) - { - uint32 node = uint32(atol(iter->c_str())); - AddTaxiDestination(node); - } - - if (m_TaxiDestinations.empty()) - return true; - - // Check integrity - if (m_TaxiDestinations.size() < 2) - return false; - - for (size_t i = 1; i < m_TaxiDestinations.size(); ++i) - { - uint32 cost; - uint32 path; - sObjectMgr.GetTaxiPath(m_TaxiDestinations[i - 1], m_TaxiDestinations[i], path, cost); - if (!path) - return false; - } - - // can't load taxi path without mount set (quest taxi path?) - if (!sObjectMgr.GetTaxiMountDisplayId(GetTaxiSource(), team, true)) - return false; - - return true; -} - -std::string PlayerTaxi::SaveTaxiDestinationsToString() -{ - if (m_TaxiDestinations.empty()) - return ""; - - std::ostringstream ss; - - for (size_t i = 0; i < m_TaxiDestinations.size(); ++i) - ss << m_TaxiDestinations[i] << " "; - - return ss.str(); -} - -uint32 PlayerTaxi::GetCurrentTaxiPath() const -{ - if (m_TaxiDestinations.size() < 2) - return 0; - - uint32 path; - uint32 cost; - - sObjectMgr.GetTaxiPath(m_TaxiDestinations[0], m_TaxiDestinations[1], path, cost); - - return path; -} - -std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi) -{ - for (int i = 0; i < TaxiMaskSize; ++i) - ss << uint32(taxi.m_taximask[i]) << " "; // cast to prevent conversion to char - return ss; -} -//== TradeData ================================================= - -TradeData* TradeData::GetTraderData() const -{ - return m_trader->GetTradeData(); -} - -Item* TradeData::GetItem(TradeSlots slot) const -{ - return m_items[slot] ? m_player->GetItemByGuid(m_items[slot]) : NULL; -} - -bool TradeData::HasItem(ObjectGuid item_guid) const -{ - for (int i = 0; i < TRADE_SLOT_COUNT; ++i) - if (m_items[i] == item_guid) - return true; - return false; -} - -Item* TradeData::GetSpellCastItem() const -{ - return m_spellCastItem ? m_player->GetItemByGuid(m_spellCastItem) : NULL; -} - -void TradeData::SetItem(TradeSlots slot, Item* item) -{ - ObjectGuid itemGuid = item ? item->GetObjectGuid() : ObjectGuid(); - - if (m_items[slot] == itemGuid) - return; - - m_items[slot] = itemGuid; - - SetAccepted(false); - GetTraderData()->SetAccepted(false); - - Update(); - - // need remove possible trader spell applied to changed item - if (slot == TRADE_SLOT_NONTRADED) - GetTraderData()->SetSpell(0); - - // need remove possible player spell applied (possible move reagent) - SetSpell(0); -} - -void TradeData::SetSpell(uint32 spell_id, Item* castItem /*= NULL*/) -{ - ObjectGuid itemGuid = castItem ? castItem->GetObjectGuid() : ObjectGuid(); - - if (m_spell == spell_id && m_spellCastItem == itemGuid) - return; - - m_spell = spell_id; - m_spellCastItem = itemGuid; - - SetAccepted(false); - GetTraderData()->SetAccepted(false); - - Update(true); // send spell info to item owner - Update(false); // send spell info to caster self -} - -void TradeData::SetMoney(uint64 money) -{ - if (m_money == money) - return; - - m_money = money; - - SetAccepted(false); - GetTraderData()->SetAccepted(false); - - Update(); -} - -void TradeData::Update(bool for_trader /*= true*/) -{ - if (for_trader) - m_trader->GetSession()->SendUpdateTrade(true); // player state for trader - else - m_player->GetSession()->SendUpdateTrade(false); // player state for player -} - -void TradeData::SetAccepted(bool state, bool crosssend /*= false*/) -{ - m_accepted = state; - - if (!state) - { - if (crosssend) - m_trader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); - else - m_player->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); - } -} - -//== Player ==================================================== - -UpdateMask Player::updateVisualBits; - -Player::Player(WorldSession* session): Unit(), m_mover(this), m_camera(this), m_achievementMgr(this), m_reputationMgr(this) -{ - m_transport = 0; - - m_speakTime = 0; - m_speakCount = 0; - - m_objectType |= TYPEMASK_PLAYER; - m_objectTypeId = TYPEID_PLAYER; - - m_valuesCount = PLAYER_END; - - SetActiveObjectState(true); // player is always active object - - m_session = session; - - m_ExtraFlags = 0; - if (GetSession()->GetSecurity() >= SEC_GAMEMASTER) - SetAcceptTicket(true); - - // players always accept - if (GetSession()->GetSecurity() == SEC_PLAYER) - SetAcceptWhispers(true); - - m_comboPoints = 0; - - m_usedTalentCount = 0; - m_questRewardTalentCount = 0; - m_freeTalentPoints = 0; - - m_regenTimer = 0; - m_holyPowerRegenTimer = REGEN_TIME_HOLY_POWER; - m_weaponChangeTimer = 0; - - m_zoneUpdateId = 0; - m_zoneUpdateTimer = 0; - m_positionStatusUpdateTimer = 0; - - m_areaUpdateId = 0; - - m_nextSave = sWorld.getConfig(CONFIG_UINT32_INTERVAL_SAVE); - - // randomize first save time in range [CONFIG_UINT32_INTERVAL_SAVE] around [CONFIG_UINT32_INTERVAL_SAVE] - // this must help in case next save after mass player load after server startup - m_nextSave = urand(m_nextSave / 2, m_nextSave * 3 / 2); - - clearResurrectRequestData(); - - memset(m_items, 0, sizeof(Item*)*PLAYER_SLOTS_COUNT); - - m_social = NULL; - - // group is initialized in the reference constructor - SetGroupInvite(NULL); - m_groupUpdateMask = 0; - m_auraUpdateMask = 0; - - duel = NULL; - - m_GuildIdInvited = 0; - m_ArenaTeamIdInvited = 0; - - m_atLoginFlags = AT_LOGIN_NONE; - - mSemaphoreTeleport_Near = false; - mSemaphoreTeleport_Far = false; - - m_DelayedOperations = 0; - m_bCanDelayTeleport = false; - m_bHasDelayedTeleport = false; - m_bHasBeenAliveAtDelayedTeleport = true; // overwrite always at setup teleport data, so not used infact - m_teleport_options = 0; - - m_trade = NULL; - - m_cinematic = 0; - - PlayerTalkClass = new PlayerMenu(GetSession()); - m_currentBuybackSlot = BUYBACK_SLOT_START; - - m_DailyQuestChanged = false; - m_WeeklyQuestChanged = false; - - m_lastLiquid = NULL; - - for (int i = 0; i < MAX_TIMERS; ++i) - m_MirrorTimer[i] = DISABLED_MIRROR_TIMER; - - m_MirrorTimerFlags = UNDERWATER_NONE; - m_MirrorTimerFlagsLast = UNDERWATER_NONE; - - m_isInWater = false; - m_drunkTimer = 0; - m_restTime = 0; - m_deathTimer = 0; - m_deathExpireTime = 0; - - m_swingErrorMsg = 0; - - m_DetectInvTimer = 1 * IN_MILLISECONDS; - - for (int j = 0; j < PLAYER_MAX_BATTLEGROUND_QUEUES; ++j) - { - m_bgBattleGroundQueueID[j].bgQueueTypeId = BATTLEGROUND_QUEUE_NONE; - m_bgBattleGroundQueueID[j].invitedToInstance = 0; - } - - m_logintime = time(NULL); - m_Last_tick = m_logintime; - m_WeaponProficiency = 0; - m_ArmorProficiency = 0; - m_canParry = false; - m_canBlock = false; - m_canDualWield = false; - m_canTitanGrip = false; - m_ammoDPS = 0.0f; - - m_temporaryUnsummonedPetNumber = 0; - - //////////////////// Rest System///////////////////// - time_inn_enter = 0; - inn_trigger_id = 0; - m_rest_bonus = 0; - rest_type = REST_TYPE_NO; - //////////////////// Rest System///////////////////// - - m_mailsUpdated = false; - unReadMails = 0; - m_nextMailDelivereTime = 0; - - m_resetTalentsCost = 0; - m_resetTalentsTime = 0; - m_itemUpdateQueueBlocked = false; - - for (int i = 0; i < MAX_MOVE_TYPE; ++i) - m_forced_speed_changes[i] = 0; - - m_stableSlots = 0; - - /////////////////// Instance System ///////////////////// - - m_HomebindTimer = 0; - m_InstanceValid = true; - m_dungeonDifficulty = DUNGEON_DIFFICULTY_NORMAL; - m_raidDifficulty = RAID_DIFFICULTY_10MAN_NORMAL; - - m_lastPotionId = 0; - - m_activeSpec = 0; - m_specsCount = 1; - for (int i = 0; i < MAX_TALENT_SPEC_COUNT; ++i) - m_talentsPrimaryTree[i] = 0; - - for (int i = 0; i < BASEMOD_END; ++i) - { - m_auraBaseMod[i][FLAT_MOD] = 0.0f; - m_auraBaseMod[i][PCT_MOD] = 1.0f; - } - - for (int i = 0; i < MAX_COMBAT_RATING; ++i) - m_baseRatingValue[i] = 0; - - m_baseSpellPower = 0; - m_baseHealthRegen = 0; - m_baseManaRegen = 0; - m_armorPenetrationPct = 0.0f; - m_spellPenetrationItemMod = 0; - - m_lastHonorKillsUpdateTime = time(NULL); - - // Player summoning - m_summon_expire = 0; - m_summon_mapid = 0; - m_summon_x = 0.0f; - m_summon_y = 0.0f; - m_summon_z = 0.0f; - - m_contestedPvPTimer = 0; - - m_declinedname = NULL; - m_runes = NULL; - - m_lastFallTime = 0; - m_lastFallZ = 0; - - m_cachedGS = 0; - - m_slot = 255; - - phaseMgr = new PhaseMgr(this); -} - -Player::~Player() -{ - CleanupsBeforeDelete(); - - // it must be unloaded already in PlayerLogout and accessed only for loggined player - // m_social = NULL; - - // Note: buy back item already deleted from DB when player was saved - for (int i = 0; i < PLAYER_SLOTS_COUNT; ++i) - { - delete m_items[i]; - } - CleanupChannels(); - - // all mailed items should be deleted, also all mail should be deallocated - for (PlayerMails::const_iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr) - delete *itr; - - for (ItemMap::const_iterator iter = mMitems.begin(); iter != mMitems.end(); ++iter) - delete iter->second; // if item is duplicated... then server may crash ... but that item should be deallocated - - delete PlayerTalkClass; - - if (m_transport) - { - m_transport->RemovePassenger(this); - } - - for (size_t x = 0; x < ItemSetEff.size(); ++x) - delete ItemSetEff[x]; - - // clean up player-instance binds, may unload some instance saves - for (uint8 i = 0; i < MAX_DIFFICULTY; ++i) - for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr) - itr->second.state->RemovePlayer(this); - - delete m_declinedname; - delete m_runes; - - delete phaseMgr; -} - -void Player::CleanupsBeforeDelete() -{ - if (m_uint32Values) // only for fully created Object - { - TradeCancel(false); - DuelComplete(DUEL_INTERRUPTED); - } - - // notify zone scripts for player logout - sOutdoorPvPMgr.HandlePlayerLeaveZone(this, m_zoneUpdateId); - - Unit::CleanupsBeforeDelete(); -} - -bool Player::Create(uint32 guidlow, const std::string& name, uint8 race, uint8 class_, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair, uint8 /*outfitId */) -{ - // FIXME: outfitId not used in player creating - - Object::_Create(guidlow, 0, HIGHGUID_PLAYER); - - m_name = name; - - PlayerInfo const* info = sObjectMgr.GetPlayerInfo(race, class_); - if (!info) - { - sLog.outError("Player have incorrect race/class pair. Can't be loaded."); - return false; - } - - ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(class_); - if (!cEntry) - { - sLog.outError("Class %u not found in DBC (Wrong DBC files?)", class_); - return false; - } - - // player store gender in single bit - if (gender != uint8(GENDER_MALE) && gender != uint8(GENDER_FEMALE)) - { - sLog.outError("Invalid gender %u at player creating", uint32(gender)); - return false; - } - - for (int i = 0; i < PLAYER_SLOTS_COUNT; ++i) - m_items[i] = NULL; - - SetLocationMapId(info->mapId); - Relocate(info->positionX, info->positionY, info->positionZ, info->orientation); - - SetMap(sMapMgr.CreateMap(info->mapId, this)); - - uint8 powertype = cEntry->powerType; - - setFactionForRace(race); - - SetByteValue(UNIT_FIELD_BYTES_0, 0, race); - SetByteValue(UNIT_FIELD_BYTES_0, 1, class_); - SetByteValue(UNIT_FIELD_BYTES_0, 2, gender); - SetByteValue(UNIT_FIELD_BYTES_0, 3, powertype); - - InitDisplayIds(); // model, scale and model data - - SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_PVP); - SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); - SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_REGENERATE_POWER); - SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f); // fix cast time showed in spell tooltip on client - SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 1.0f); // default for players in 3.0.3 - - SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, -1); // -1 is default value - - SetByteValue(PLAYER_BYTES, 0, skin); - SetByteValue(PLAYER_BYTES, 1, face); - SetByteValue(PLAYER_BYTES, 2, hairStyle); - SetByteValue(PLAYER_BYTES, 3, hairColor); - - SetByteValue(PLAYER_BYTES_2, 0, facialHair); - SetByteValue(PLAYER_BYTES_2, 3, REST_STATE_NORMAL); - - SetByteValue(PLAYER_BYTES_3, 0, gender); - SetByteValue(PLAYER_BYTES_3, 3, 0); // BattlefieldArenaFaction (0 or 1) - - SetInGuild(0); - SetGuildLevel(0); - SetUInt32Value(PLAYER_GUILDRANK, 0); - SetUInt32Value(PLAYER_GUILD_TIMESTAMP, 0); - - for (int i = 0; i < KNOWN_TITLES_SIZE; ++i) - SetUInt64Value(PLAYER__FIELD_KNOWN_TITLES + i, 0); // 0=disabled - SetUInt32Value(PLAYER_CHOSEN_TITLE, 0); - - SetUInt32Value(PLAYER_FIELD_KILLS, 0); - SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS, 0); - - // set starting level - uint32 start_level = getClass() != CLASS_DEATH_KNIGHT - ? sWorld.getConfig(CONFIG_UINT32_START_PLAYER_LEVEL) - : sWorld.getConfig(CONFIG_UINT32_START_HEROIC_PLAYER_LEVEL); - - if (GetSession()->GetSecurity() >= SEC_MODERATOR) - { - uint32 gm_level = sWorld.getConfig(CONFIG_UINT32_START_GM_LEVEL); - if (gm_level > start_level) - start_level = gm_level; - } - - SetUInt32Value(UNIT_FIELD_LEVEL, start_level); - - InitRunes(); - - SetUInt64Value(PLAYER_FIELD_COINAGE, sWorld.getConfig(CONFIG_UINT64_START_PLAYER_MONEY)); - SetCurrencyCount(CURRENCY_HONOR_POINTS,sWorld.getConfig(CONFIG_UINT32_CURRENCY_START_HONOR_POINTS)); - SetCurrencyCount(CURRENCY_CONQUEST_POINTS, sWorld.getConfig(CONFIG_UINT32_CURRENCY_START_CONQUEST_POINTS)); - - // Played time - m_Last_tick = time(NULL); - m_Played_time[PLAYED_TIME_TOTAL] = 0; - m_Played_time[PLAYED_TIME_LEVEL] = 0; - - // base stats and related field values - InitStatsForLevel(); - InitTaxiNodesForLevel(); - InitGlyphsForLevel(); - InitTalentForLevel(); - InitPrimaryProfessions(); // to max set before any spell added - - // apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods() - UpdateMaxHealth(); // Update max Health (for add bonus from stamina) - SetHealth(GetMaxHealth()); - - if (getPowerType() == POWER_MANA) - { - UpdateMaxPower(POWER_MANA); // Update max Mana (for add bonus from intellect) - SetPower(POWER_MANA, GetMaxPower(POWER_MANA)); - } - - if (getPowerType() != POWER_MANA) // hide additional mana bar if we have no mana - { - SetPower(POWER_MANA, 0); - SetMaxPower(POWER_MANA, 0); - } - - // original spells - learnDefaultSpells(); - - // original action bar - for (PlayerCreateInfoActions::const_iterator action_itr = info->action.begin(); action_itr != info->action.end(); ++action_itr) - addActionButton(0, action_itr->button, action_itr->action, action_itr->type); - - // original items - uint32 raceClassGender = GetUInt32Value(UNIT_FIELD_BYTES_0) & 0x00FFFFFF; - - CharStartOutfitEntry const* oEntry = NULL; - for (uint32 i = 1; i < sCharStartOutfitStore.GetNumRows(); ++i) - { - if (CharStartOutfitEntry const* entry = sCharStartOutfitStore.LookupEntry(i)) - { - if (entry->RaceClassGender == raceClassGender) - { - oEntry = entry; - break; - } - } - } - - if (oEntry) - { - for (int j = 0; j < MAX_OUTFIT_ITEMS; ++j) - { - if (oEntry->ItemId[j] <= 0) - continue; - - uint32 item_id = oEntry->ItemId[j]; - - // just skip, reported in ObjectMgr::LoadItemPrototypes - ItemPrototype const* iProto = ObjectMgr::GetItemPrototype(item_id); - if (!iProto) - continue; - - // BuyCount by default - int32 count = iProto->BuyCount; - - // special amount for foor/drink - if (iProto->Class == ITEM_CLASS_CONSUMABLE && iProto->SubClass == ITEM_SUBCLASS_FOOD) - { - switch (iProto->Spells[0].SpellCategory) - { - case 11: // food - count = getClass() == CLASS_DEATH_KNIGHT ? 10 : 4; - break; - case 59: // drink - count = 2; - break; - } - if (iProto->Stackable < count) - count = iProto->Stackable; - } - - StoreNewItemInBestSlots(item_id, count); - } - } - - for (PlayerCreateInfoItems::const_iterator item_id_itr = info->item.begin(); item_id_itr != info->item.end(); ++item_id_itr) - StoreNewItemInBestSlots(item_id_itr->item_id, item_id_itr->item_amount); - - // bags and main-hand weapon must equipped at this moment - // now second pass for not equipped (offhand weapon/shield if it attempt equipped before main-hand weapon) - // or ammo not equipped in special bag - for (int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i) - { - if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - { - uint16 eDest; - // equip offhand weapon/shield if it attempt equipped before main-hand weapon - InventoryResult msg = CanEquipItem(NULL_SLOT, eDest, pItem, false); - if (msg == EQUIP_ERR_OK) - { - RemoveItem(INVENTORY_SLOT_BAG_0, i, true); - EquipItem(eDest, pItem, true); - } - // move other items to more appropriate slots (ammo not equipped in special bag) - else - { - ItemPosCountVec sDest; - msg = CanStoreItem(NULL_BAG, NULL_SLOT, sDest, pItem, false); - if (msg == EQUIP_ERR_OK) - { - RemoveItem(INVENTORY_SLOT_BAG_0, i, true); - pItem = StoreItem(sDest, pItem, true); - } - - // if this is ammo then use it - msg = CanUseAmmo(pItem->GetEntry()); - if (msg == EQUIP_ERR_OK) - SetAmmo(pItem->GetEntry()); - } - } - } - // all item positions resolved - - return true; -} - -bool Player::StoreNewItemInBestSlots(uint32 titem_id, uint32 titem_amount) -{ - DEBUG_LOG("STORAGE: Creating initial item, itemId = %u, count = %u", titem_id, titem_amount); - - // attempt equip by one - while (titem_amount > 0) - { - uint16 eDest; - uint8 msg = CanEquipNewItem(NULL_SLOT, eDest, titem_id, false); - if (msg != EQUIP_ERR_OK) - break; - - EquipNewItem(eDest, titem_id, true); - AutoUnequipOffhandIfNeed(); - --titem_amount; - } - - if (titem_amount == 0) - return true; // equipped - - // attempt store - ItemPosCountVec sDest; - // store in main bag to simplify second pass (special bags can be not equipped yet at this moment) - uint8 msg = CanStoreNewItem(INVENTORY_SLOT_BAG_0, NULL_SLOT, sDest, titem_id, titem_amount); - if (msg == EQUIP_ERR_OK) - { - StoreNewItem(sDest, titem_id, true, Item::GenerateItemRandomPropertyId(titem_id)); - return true; // stored - } - - // item can't be added - sLog.outError("STORAGE: Can't equip or store initial item %u for race %u class %u , error msg = %u", titem_id, getRace(), getClass(), msg); - return false; -} - -// helper function, mainly for script side, but can be used for simple task in mangos also. -Item* Player::StoreNewItemInInventorySlot(uint32 itemEntry, uint32 amount) -{ - ItemPosCountVec vDest; - - uint8 msg = CanStoreNewItem(INVENTORY_SLOT_BAG_0, NULL_SLOT, vDest, itemEntry, amount); - - if (msg == EQUIP_ERR_OK) - { - if (Item* pItem = StoreNewItem(vDest, itemEntry, true, Item::GenerateItemRandomPropertyId(itemEntry))) - return pItem; - } - - return NULL; -} - -void Player::SendMirrorTimer(MirrorTimerType Type, uint32 MaxValue, uint32 CurrentValue, int32 Regen) -{ - if (int(MaxValue) == DISABLED_MIRROR_TIMER) - { - if (int(CurrentValue) != DISABLED_MIRROR_TIMER) - StopMirrorTimer(Type); - return; - } - WorldPacket data(SMSG_START_MIRROR_TIMER, (21)); - data << (uint32)Type; - data << CurrentValue; - data << MaxValue; - data << Regen; - data << (uint8)0; - data << (uint32)0; // spell id - GetSession()->SendPacket(&data); -} - -void Player::StopMirrorTimer(MirrorTimerType Type) -{ - m_MirrorTimer[Type] = DISABLED_MIRROR_TIMER; - WorldPacket data(SMSG_STOP_MIRROR_TIMER, 4); - data << (uint32)Type; - GetSession()->SendPacket(&data); -} - -uint32 Player::EnvironmentalDamage(EnviromentalDamage type, uint32 damage) -{ - if (!isAlive() || isGameMaster()) - return 0; - - // Absorb, resist some environmental damage type - uint32 absorb = 0; - uint32 resist = 0; - if (type == DAMAGE_LAVA) - CalculateDamageAbsorbAndResist(this, SPELL_SCHOOL_MASK_FIRE, DIRECT_DAMAGE, damage, &absorb, &resist); - else if (type == DAMAGE_SLIME) - CalculateDamageAbsorbAndResist(this, SPELL_SCHOOL_MASK_NATURE, DIRECT_DAMAGE, damage, &absorb, &resist); - - damage -= absorb + resist; - - DealDamageMods(this, damage, &absorb); - - WorldPacket data(SMSG_ENVIRONMENTALDAMAGELOG, (21)); - data << GetObjectGuid(); - data << uint8(type != DAMAGE_FALL_TO_VOID ? type : DAMAGE_FALL); - data << uint32(damage); - data << uint32(absorb); - data << uint32(resist); - SendMessageToSet(&data, true); - - uint32 final_damage = DealDamage(this, damage, NULL, SELF_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); - - if (!isAlive()) - { - if (type == DAMAGE_FALL) // DealDamage not apply item durability loss at self damage - { - DEBUG_LOG("We are fall to death, loosing 10 percents durability"); - DurabilityLossAll(0.10f, false); - // durability lost message - WorldPacket data2(SMSG_DURABILITY_DAMAGE_DEATH, 0); - GetSession()->SendPacket(&data2); - } - - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM, 1, type); - } - - return final_damage; -} - -int32 Player::getMaxTimer(MirrorTimerType timer) -{ - switch (timer) - { - case FATIGUE_TIMER: - if (GetSession()->GetSecurity() >= (AccountTypes)sWorld.getConfig(CONFIG_UINT32_TIMERBAR_FATIGUE_GMLEVEL)) - return DISABLED_MIRROR_TIMER; - return sWorld.getConfig(CONFIG_UINT32_TIMERBAR_FATIGUE_MAX) * IN_MILLISECONDS; - case BREATH_TIMER: - { - if (!isAlive() || HasAuraType(SPELL_AURA_WATER_BREATHING) || - GetSession()->GetSecurity() >= (AccountTypes)sWorld.getConfig(CONFIG_UINT32_TIMERBAR_BREATH_GMLEVEL)) - return DISABLED_MIRROR_TIMER; - int32 UnderWaterTime = sWorld.getConfig(CONFIG_UINT32_TIMERBAR_BREATH_MAX) * IN_MILLISECONDS; - AuraList const& mModWaterBreathing = GetAurasByType(SPELL_AURA_MOD_WATER_BREATHING); - for (AuraList::const_iterator i = mModWaterBreathing.begin(); i != mModWaterBreathing.end(); ++i) - UnderWaterTime = uint32(UnderWaterTime * (100.0f + (*i)->GetModifier()->m_amount) / 100.0f); - return UnderWaterTime; - } - case FIRE_TIMER: - { - if (!isAlive() || GetSession()->GetSecurity() >= (AccountTypes)sWorld.getConfig(CONFIG_UINT32_TIMERBAR_FIRE_GMLEVEL)) - return DISABLED_MIRROR_TIMER; - return sWorld.getConfig(CONFIG_UINT32_TIMERBAR_FIRE_MAX) * IN_MILLISECONDS; - } - default: - return 0; - } - return 0; -} - -void Player::UpdateMirrorTimers() -{ - // Desync flags for update on next HandleDrowning - if (m_MirrorTimerFlags) - m_MirrorTimerFlagsLast = ~m_MirrorTimerFlags; -} - -void Player::HandleDrowning(uint32 time_diff) -{ - if (!m_MirrorTimerFlags) - return; - - // In water - if (m_MirrorTimerFlags & UNDERWATER_INWATER) - { - // Breath timer not activated - activate it - if (m_MirrorTimer[BREATH_TIMER] == DISABLED_MIRROR_TIMER) - { - m_MirrorTimer[BREATH_TIMER] = getMaxTimer(BREATH_TIMER); - SendMirrorTimer(BREATH_TIMER, m_MirrorTimer[BREATH_TIMER], m_MirrorTimer[BREATH_TIMER], -1); - } - else - { - m_MirrorTimer[BREATH_TIMER] -= time_diff; - // Timer limit - need deal damage - if (m_MirrorTimer[BREATH_TIMER] < 0) - { - m_MirrorTimer[BREATH_TIMER] += 2 * IN_MILLISECONDS; - // Calculate and deal damage - // TODO: Check this formula - uint32 damage = GetMaxHealth() / 5 + urand(0, getLevel() - 1); - EnvironmentalDamage(DAMAGE_DROWNING, damage); - } - else if (!(m_MirrorTimerFlagsLast & UNDERWATER_INWATER)) // Update time in client if need - SendMirrorTimer(BREATH_TIMER, getMaxTimer(BREATH_TIMER), m_MirrorTimer[BREATH_TIMER], -1); - } - } - else if (m_MirrorTimer[BREATH_TIMER] != DISABLED_MIRROR_TIMER) // Regen timer - { - int32 UnderWaterTime = getMaxTimer(BREATH_TIMER); - // Need breath regen - m_MirrorTimer[BREATH_TIMER] += 10 * time_diff; - if (m_MirrorTimer[BREATH_TIMER] >= UnderWaterTime || !isAlive()) - StopMirrorTimer(BREATH_TIMER); - else if (m_MirrorTimerFlagsLast & UNDERWATER_INWATER) - SendMirrorTimer(BREATH_TIMER, UnderWaterTime, m_MirrorTimer[BREATH_TIMER], 10); - } - - // In dark water - if (m_MirrorTimerFlags & UNDERWATER_INDARKWATER) - { - // Fatigue timer not activated - activate it - if (m_MirrorTimer[FATIGUE_TIMER] == DISABLED_MIRROR_TIMER) - { - m_MirrorTimer[FATIGUE_TIMER] = getMaxTimer(FATIGUE_TIMER); - SendMirrorTimer(FATIGUE_TIMER, m_MirrorTimer[FATIGUE_TIMER], m_MirrorTimer[FATIGUE_TIMER], -1); - } - else - { - m_MirrorTimer[FATIGUE_TIMER] -= time_diff; - // Timer limit - need deal damage or teleport ghost to graveyard - if (m_MirrorTimer[FATIGUE_TIMER] < 0) - { - m_MirrorTimer[FATIGUE_TIMER] += 2 * IN_MILLISECONDS; - if (isAlive()) // Calculate and deal damage - { - uint32 damage = GetMaxHealth() / 5 + urand(0, getLevel() - 1); - EnvironmentalDamage(DAMAGE_EXHAUSTED, damage); - } - else if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) // Teleport ghost to graveyard - RepopAtGraveyard(); - } - else if (!(m_MirrorTimerFlagsLast & UNDERWATER_INDARKWATER)) - SendMirrorTimer(FATIGUE_TIMER, getMaxTimer(FATIGUE_TIMER), m_MirrorTimer[FATIGUE_TIMER], -1); - } - } - else if (m_MirrorTimer[FATIGUE_TIMER] != DISABLED_MIRROR_TIMER) // Regen timer - { - int32 DarkWaterTime = getMaxTimer(FATIGUE_TIMER); - m_MirrorTimer[FATIGUE_TIMER] += 10 * time_diff; - if (m_MirrorTimer[FATIGUE_TIMER] >= DarkWaterTime || !isAlive()) - StopMirrorTimer(FATIGUE_TIMER); - else if (m_MirrorTimerFlagsLast & UNDERWATER_INDARKWATER) - SendMirrorTimer(FATIGUE_TIMER, DarkWaterTime, m_MirrorTimer[FATIGUE_TIMER], 10); - } - - if (m_MirrorTimerFlags & (UNDERWATER_INLAVA /*| UNDERWATER_INSLIME*/) && !(m_lastLiquid && m_lastLiquid->SpellId)) - { - // Breath timer not activated - activate it - if (m_MirrorTimer[FIRE_TIMER] == DISABLED_MIRROR_TIMER) - m_MirrorTimer[FIRE_TIMER] = getMaxTimer(FIRE_TIMER); - else - { - m_MirrorTimer[FIRE_TIMER] -= time_diff; - if (m_MirrorTimer[FIRE_TIMER] < 0) - { - m_MirrorTimer[FIRE_TIMER] += 2 * IN_MILLISECONDS; - // Calculate and deal damage - // TODO: Check this formula - uint32 damage = urand(600, 700); - if (m_MirrorTimerFlags & UNDERWATER_INLAVA) - EnvironmentalDamage(DAMAGE_LAVA, damage); - // need to skip Slime damage in Undercity, - // maybe someone can find better way to handle environmental damage - //else if (m_zoneUpdateId != 1497) - // EnvironmentalDamage(DAMAGE_SLIME, damage); - } - } - } - else - m_MirrorTimer[FIRE_TIMER] = DISABLED_MIRROR_TIMER; - - // Recheck timers flag - m_MirrorTimerFlags &= ~UNDERWATER_EXIST_TIMERS; - for (int i = 0; i < MAX_TIMERS; ++i) - if (m_MirrorTimer[i] != DISABLED_MIRROR_TIMER) - { - m_MirrorTimerFlags |= UNDERWATER_EXIST_TIMERS; - break; - } - m_MirrorTimerFlagsLast = m_MirrorTimerFlags; -} - -// The player sobers by 1% every 9 seconds -void Player::HandleSobering() -{ - uint8 currentDrunkValue = GetDrunkValue(); - if (currentDrunkValue) - { - --currentDrunkValue; - SetDrunkValue(currentDrunkValue); - } -} - -DrunkenState Player::GetDrunkenstateByValue(uint8 value) -{ - if (value >= 90) - return DRUNKEN_SMASHED; - if (value >= 50) - return DRUNKEN_DRUNK; - if (value) - return DRUNKEN_TIPSY; - return DRUNKEN_SOBER; -} - -void Player::SetDrunkValue(uint8 newDrunkValue, uint32 itemId /*= 0*/) -{ - if (newDrunkValue > 100) - newDrunkValue = 100; - - if (newDrunkValue < GetDrunkValue()) - m_drunkTimer = 0; // reset sobering timer - - uint32 oldDrunkenState = Player::GetDrunkenstateByValue(GetDrunkValue()); - - SetByteValue(PLAYER_BYTES_3, 1, newDrunkValue); - - uint32 newDrunkenState = Player::GetDrunkenstateByValue(newDrunkValue); - - // special drunk invisibility detection - if (newDrunkenState >= DRUNKEN_DRUNK) - m_detectInvisibilityMask |= (1 << 6); - else - m_detectInvisibilityMask &= ~(1 << 6); - - if (newDrunkenState == oldDrunkenState) - return; - - WorldPacket data(SMSG_CROSSED_INEBRIATION_THRESHOLD, (8 + 4 + 4)); - data << GetObjectGuid(); - data << uint32(newDrunkenState); - data << uint32(itemId); - - SendMessageToSet(&data, true); -} - -void Player::Update(uint32 update_diff, uint32 p_time) -{ - if (!IsInWorld()) - return; - - // Remove failed timed Achievements - GetAchievementMgr().DoFailedTimedAchievementCriterias(); - - // Undelivered mail - if (m_nextMailDelivereTime && m_nextMailDelivereTime <= time(NULL)) - { - SendNewMail(); - ++unReadMails; - - // It will be recalculate at mailbox open (for unReadMails important non-0 until mailbox open, it also will be recalculated) - m_nextMailDelivereTime = 0; - } - - // Used to implement delayed far teleports - SetCanDelayTeleport(true); - Unit::Update(update_diff, p_time); - SetCanDelayTeleport(false); - - // Update player only attacks - if (uint32 ranged_att = getAttackTimer(RANGED_ATTACK)) - setAttackTimer(RANGED_ATTACK, (update_diff >= ranged_att ? 0 : ranged_att - update_diff)); - - time_t now = time(NULL); - - UpdatePvPFlag(now); - - UpdateContestedPvP(update_diff); - - UpdateDuelFlag(now); - - CheckDuelDistance(now); - - UpdateAfkReport(now); - - // Update items that have just a limited lifetime - if (now > m_Last_tick) - UpdateItemDuration(uint32(now - m_Last_tick)); - - if (!m_timedquests.empty()) - { - QuestSet::iterator iter = m_timedquests.begin(); - while (iter != m_timedquests.end()) - { - QuestStatusData& q_status = mQuestStatus[*iter]; - if (q_status.m_timer <= update_diff) - { - uint32 quest_id = *iter; - ++iter; // Current iter will be removed in FailQuest - FailQuest(quest_id); - } - else - { - q_status.m_timer -= update_diff; - if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED; - ++iter; - } - } - } - - if (hasUnitState(UNIT_STAT_MELEE_ATTACKING)) - { - UpdateMeleeAttackingState(); - - Unit* pVictim = getVictim(); - if (pVictim && !IsNonMeleeSpellCasted(false)) - { - Player* vOwner = pVictim->GetCharmerOrOwnerPlayerOrPlayerItself(); - if (vOwner && vOwner->IsPvP() && !IsInDuelWith(vOwner)) - { - UpdatePvP(true); - RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); - } - } - } - - if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING)) - { - if (roll_chance_i(3) && GetTimeInnEnter() > 0) // Freeze update - { - time_t time_inn = time(NULL) - GetTimeInnEnter(); - if (time_inn >= 10) // Freeze update - { - float bubble = 0.125f * sWorld.getConfig(CONFIG_FLOAT_RATE_REST_INGAME); - // Speed collect rest bonus (section/in hour) - SetRestBonus(float(GetRestBonus() + time_inn * (GetUInt32Value(PLAYER_NEXT_LEVEL_XP) / 72000) * bubble)); - UpdateInnerTime(time(NULL)); - } - } - } - - if (m_regenTimer) - { - if (update_diff >= m_regenTimer) - m_regenTimer = 0; - else - m_regenTimer -= update_diff; - } - - if (m_positionStatusUpdateTimer) - { - if (update_diff >= m_positionStatusUpdateTimer) - m_positionStatusUpdateTimer = 0; - else - m_positionStatusUpdateTimer -= update_diff; - } - - if (m_weaponChangeTimer > 0) - { - if (update_diff >= m_weaponChangeTimer) - m_weaponChangeTimer = 0; - else - m_weaponChangeTimer -= update_diff; - } - - if (m_zoneUpdateTimer > 0) - { - if (update_diff >= m_zoneUpdateTimer) - { - uint32 newzone, newarea; - GetZoneAndAreaId(newzone, newarea); - - if (m_zoneUpdateId != newzone) - UpdateZone(newzone, newarea); // Also update area - else - { - // Use area updates as well - // Needed for free for all arenas for example - if (m_areaUpdateId != newarea) - UpdateArea(newarea); - - m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL; - } - } - else - m_zoneUpdateTimer -= update_diff; - } - - if (m_timeSyncTimer > 0) - { - if (update_diff >= m_timeSyncTimer) - SendTimeSync(); - else - m_timeSyncTimer -= update_diff; - } - - if (isAlive()) - { - if (!HasAuraType(SPELL_AURA_STOP_NATURAL_MANA_REGEN)) - SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_REGENERATE_POWER); - - if (!m_regenTimer) - RegenerateAll(); - } - - if (m_deathState == JUST_DIED) - KillPlayer(); - - if (m_nextSave > 0) - { - if (update_diff >= m_nextSave) - { - // m_nextSave reseted in SaveToDB call - SaveToDB(); - DETAIL_LOG("Player '%s' (GUID: %u) saved", GetName(), GetGUIDLow()); - } - else - m_nextSave -= update_diff; - } - - // Handle Water/drowning - HandleDrowning(update_diff); - - // Handle detect stealth players - if (m_DetectInvTimer > 0) - { - if (update_diff >= m_DetectInvTimer) - { - HandleStealthedUnitsDetection(); - m_DetectInvTimer = 3000; - } - else - m_DetectInvTimer -= update_diff; - } - - // Played time - if (now > m_Last_tick) - { - uint32 elapsed = uint32(now - m_Last_tick); - m_Played_time[PLAYED_TIME_TOTAL] += elapsed; // Total played time - m_Played_time[PLAYED_TIME_LEVEL] += elapsed; // Level played time - m_Last_tick = now; - } - - if (GetDrunkValue()) - { - m_drunkTimer += update_diff; - - if (m_drunkTimer > 9 * IN_MILLISECONDS) - HandleSobering(); - } - - // Not auto-free ghost from body in instances - if (m_deathTimer > 0 && !GetMap()->Instanceable()) - { - if (p_time >= m_deathTimer) - { - m_deathTimer = 0; - BuildPlayerRepop(); - RepopAtGraveyard(); - } - else - m_deathTimer -= p_time; - } - - UpdateEnchantTime(update_diff); - UpdateHomebindTime(update_diff); - - // Group update - SendUpdateToOutOfRangeGroupMembers(); - - Pet* pet = GetPet(); - if (pet && !pet->IsWithinDistInMap(this, GetMap()->GetVisibilityDistance()) && (GetCharmGuid() && (pet->GetObjectGuid() != GetCharmGuid()))) - pet->Unsummon(PET_SAVE_REAGENTS, this); - - if (IsHasDelayedTeleport()) - TeleportTo(m_teleport_dest, m_teleport_options); -} - -void Player::SetDeathState(DeathState s) -{ - uint32 ressSpellId = 0; - - bool cur = isAlive(); - - if (s == JUST_DIED && cur) - { - // drunken state is cleared on death - SetDrunkValue(0); - // lost combo points at any target (targeted combo points clear in Unit::SetDeathState) - ClearComboPoints(); - - clearResurrectRequestData(); - - // remove form before other mods to prevent incorrect stats calculation - RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT); - - // FIXME: is pet dismissed at dying or releasing spirit? if second, add SetDeathState(DEAD) to HandleRepopRequestOpcode and define pet unsummon here with (s == DEAD) - RemovePet(PET_SAVE_REAGENTS); - - // save value before aura remove in Unit::SetDeathState - ressSpellId = GetUInt32Value(PLAYER_SELF_RES_SPELL); - - // passive spell - if (!ressSpellId) - ressSpellId = GetResurrectionSpellId(); - - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP, 1); - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATH, 1); - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON, 1); - - if (InstanceData* mapInstance = GetInstanceData()) - mapInstance->OnPlayerDeath(this); - } - - Unit::SetDeathState(s); - - // restore resurrection spell id for player after aura remove - if (s == JUST_DIED && cur && ressSpellId) - SetUInt32Value(PLAYER_SELF_RES_SPELL, ressSpellId); - - if (isAlive() && !cur) - { - // clear aura case after resurrection by another way (spells will be applied before next death) - SetUInt32Value(PLAYER_SELF_RES_SPELL, 0); - - // restore default warrior stance - if (getClass() == CLASS_WARRIOR) - CastSpell(this, SPELL_ID_PASSIVE_BATTLE_STANCE, true); - } -} - -bool Player::BuildEnumData(QueryResult* result, ByteBuffer* data, ByteBuffer* buffer) -{ - // 0 1 2 3 4 5 6 7 - // "SELECT characters.guid, characters.name, characters.race, characters.class, characters.gender, characters.playerBytes, characters.playerBytes2, characters.level, " - // 8 9 10 11 12 13 14 - // "characters.zone, characters.map, characters.position_x, characters.position_y, characters.position_z, guild_member.guildid, characters.playerFlags, " - // 15 16 17 18 19 20 21 - // "characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level, characters.equipmentCache, characters.slot, character_declinedname.genitive " - - Field* fields = result->Fetch(); - ObjectGuid guid = ObjectGuid(HIGHGUID_PLAYER, fields[0].GetUInt32()); - uint8 pRace = fields[2].GetUInt8(); - uint8 pClass = fields[3].GetUInt8(); - - PlayerInfo const* info = sObjectMgr.GetPlayerInfo(pRace, pClass); - if (!info) - { - sLog.outError("%s has incorrect race/class pair. Don't build enum.", guid.GetString().c_str()); - return false; - } - - ObjectGuid guildGuid = ObjectGuid(HIGHGUID_GUILD, fields[13].GetUInt32()); - std::string name = fields[1].GetCppString(); - uint8 gender = fields[4].GetUInt8(); - uint32 playerBytes = fields[5].GetUInt32(); - uint8 level = fields[7].GetUInt8(); - uint32 playerFlags = fields[14].GetUInt32(); - uint32 atLoginFlags = fields[15].GetUInt32(); - uint32 zone = fields[8].GetUInt32(); - uint32 petDisplayId = 0; - uint32 petLevel = 0; - uint32 petFamily = 0; - uint32 char_flags = 0; - - data->WriteGuidMask<3>(guid); - data->WriteGuidMask<1, 7, 2>(guildGuid); - data->WriteBits(name.length(), 7); - data->WriteGuidMask<4, 7>(guid); - data->WriteGuidMask<3>(guildGuid); - data->WriteGuidMask<5>(guid); - data->WriteGuidMask<6>(guildGuid); - data->WriteGuidMask<1>(guid); - data->WriteGuidMask<5, 4>(guildGuid); - data->WriteBit(atLoginFlags & AT_LOGIN_FIRST); - data->WriteGuidMask<0, 2, 6>(guid); - data->WriteGuidMask<0>(guildGuid); - - // show pet at selection character in character list only for non-ghost character - if (result && !(playerFlags & PLAYER_FLAGS_GHOST) && (pClass == CLASS_WARLOCK || pClass == CLASS_HUNTER || pClass == CLASS_DEATH_KNIGHT)) - { - uint32 entry = fields[16].GetUInt32(); - CreatureInfo const* cInfo = sCreatureStorage.LookupEntry(entry); - if(cInfo) - { - petDisplayId = fields[17].GetUInt32(); - petLevel = fields[18].GetUInt32(); - petFamily = cInfo->family; - } - } - - if(playerFlags & PLAYER_FLAGS_HIDE_HELM) - char_flags |= CHARACTER_FLAG_HIDE_HELM; - if(playerFlags & PLAYER_FLAGS_HIDE_CLOAK) - char_flags |= CHARACTER_FLAG_HIDE_CLOAK; - if(playerFlags & PLAYER_FLAGS_GHOST) - char_flags |= CHARACTER_FLAG_GHOST; - if(atLoginFlags & AT_LOGIN_RENAME) - char_flags |= CHARACTER_FLAG_RENAME; - if(sWorld.getConfig(CONFIG_BOOL_DECLINED_NAMES_USED)) - { - if(!fields[21].GetCppString().empty()) - char_flags |= CHARACTER_FLAG_DECLINED; - } - else - char_flags |= CHARACTER_FLAG_DECLINED; - - *buffer << uint8(pClass); // class - - Tokens tdata = StrSplit(fields[19].GetCppString(), " "); - for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot) - { - uint32 visualbase = slot * 2; - uint32 item_id = GetUInt32ValueFromArray(tdata, visualbase); - const ItemPrototype * proto = ObjectMgr::GetItemPrototype(item_id); - if(!proto) - { - *buffer << uint8(0); - *buffer << uint32(0); - *buffer << uint32(0); - continue; - } - - SpellItemEnchantmentEntry const *enchant = NULL; - - uint32 enchants = GetUInt32ValueFromArray(tdata, visualbase + 1); - for(uint8 enchantSlot = PERM_ENCHANTMENT_SLOT; enchantSlot <= TEMP_ENCHANTMENT_SLOT; ++enchantSlot) - { - // values stored in 2 uint16 - uint32 enchantId = 0x0000FFFF & (enchants >> enchantSlot*16); - if(!enchantId) - continue; - - if ((enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId))) - break; - } - - *buffer << uint8(proto->InventoryType); - *buffer << uint32(proto->DisplayInfoID); - *buffer << uint32(enchant ? enchant->aura_id : 0); - } - - for (int32 i = 0; i < 4; i++) - { - *buffer << uint8(0); - *buffer << uint32(0); - *buffer << uint32(0); - } - - *buffer << uint32(petFamily); // Pet DisplayID - buffer->WriteGuidBytes<2>(guildGuid); - *buffer << uint8(fields[20].GetUInt8()); // char order id - *buffer << uint8((playerBytes >> 16) & 0xFF); // Hair style - buffer->WriteGuidBytes<3>(guildGuid); - *buffer << uint32(petDisplayId); // Pet DisplayID - *buffer << uint32(char_flags); // character flags - *buffer << uint8((playerBytes >> 24) & 0xFF); // Hair color - buffer->WriteGuidBytes<4>(guid); - *buffer << uint32(fields[9].GetUInt32()); // map - buffer->WriteGuidBytes<5>(guildGuid); - *buffer << fields[12].GetFloat(); // z - buffer->WriteGuidBytes<6>(guildGuid); - *buffer << uint32(petLevel); // pet level - buffer->WriteGuidBytes<3>(guid); - *buffer << fields[11].GetFloat(); // y - // character customize flags - *buffer << uint32(atLoginFlags & AT_LOGIN_CUSTOMIZE ? CHAR_CUSTOMIZE_FLAG_CUSTOMIZE : CHAR_CUSTOMIZE_FLAG_NONE); - - uint32 playerBytes2 = fields[6].GetUInt32(); - *buffer << uint8(playerBytes2 & 0xFF); // facial hair - buffer->WriteGuidBytes<7>(guid); - *buffer << uint8(gender); // Gender - buffer->append(name.c_str(), name.length()); - *buffer << uint8((playerBytes >> 8) & 0xFF); // face - - buffer->WriteGuidBytes<0, 2>(guid); - buffer->WriteGuidBytes<1, 7>(guildGuid); - - *buffer << fields[10].GetFloat(); // x - *buffer << uint8(playerBytes & 0xFF); // skin - *buffer << uint8(pRace); // Race - *buffer << uint8(level); // Level - buffer->WriteGuidBytes<6>(guid); - buffer->WriteGuidBytes<4, 0>(guildGuid); - buffer->WriteGuidBytes<5, 1>(guid); - - *buffer << uint32(zone); // Zone id - - return true; -} - -void Player::ToggleAFK() -{ - ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK); - - // afk player not allowed in battleground - if (isAFK() && InBattleGround() && !InArena()) - LeaveBattleground(); -} - -void Player::ToggleDND() -{ - ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_DND); -} - -uint8 Player::GetChatTag() const -{ - uint8 tag = CHAT_TAG_NONE; - - if (isAFK()) - tag |= CHAT_TAG_AFK; - if (isDND()) - tag |= CHAT_TAG_DND; - if (isGMChat()) - tag |= CHAT_TAG_GM; - if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_COMMENTATOR)) - tag |= CHAT_TAG_COM; - if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_DEVELOPER)) - tag |= CHAT_TAG_DEV; - - return tag; -} - -void Player::SendTeleportPacket(float oldX, float oldY, float oldZ, float oldO) -{ - ObjectGuid guid = GetObjectGuid(); - ObjectGuid transportGuid = m_movementInfo.GetTransportGuid(); - - WorldPacket data(SMSG_MOVE_TELEPORT, 38); - data.WriteGuidMask<6, 0, 3, 2>(guid); - data.WriteBit(0); // unknown - data.WriteBit(!transportGuid.IsEmpty()); - data.WriteGuidMask<1>(guid); - if (transportGuid) - data.WriteGuidMask<1, 3, 2, 5, 0, 7, 6, 4>(transportGuid); - - data.WriteGuidMask<4, 7, 5>(guid); - - if (transportGuid) - data.WriteGuidBytes<5, 6, 1, 7, 0, 2, 4, 3>(transportGuid); - - data << uint32(0); // counter - data.WriteGuidBytes<1, 2, 3, 5>(guid); - data << float(GetPositionX()); - data.WriteGuidBytes<4>(guid); - data << float(GetOrientation()); - data.WriteGuidBytes<7>(guid); - data << float(GetPositionZ()); - data.WriteGuidBytes<0, 6>(guid); - data << float(GetPositionY()); - - Relocate(oldX, oldY, oldZ, oldO); - SendDirectMessage(&data); -} - -bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options /*=0*/, AreaTrigger const* at /*=NULL*/) -{ - orientation = NormalizeOrientation(orientation); - - if (!MapManager::IsValidMapCoord(mapid, x, y, z, orientation)) - { - sLog.outError("TeleportTo: invalid map %d or absent instance template.", mapid); - return false; - } - - MapEntry const* mEntry = sMapStore.LookupEntry(mapid); // Validity checked in IsValidMapCoord - - // preparing unsummon pet if lost (we must get pet before teleportation or will not find it later) - Pet* pet = GetPet(); - - // don't let enter battlegrounds without assigned battleground id (for example through areatrigger)... - // don't let gm level > 1 either - if (!InBattleGround() && mEntry->IsBattleGroundOrArena()) - return false; - - // Get MapEntrance trigger if teleport to other -nonBG- map - bool assignedAreaTrigger = false; - if (GetMapId() != mapid && !mEntry->IsBattleGroundOrArena() && !at) - { - at = sObjectMgr.GetMapEntranceTrigger(mapid); - assignedAreaTrigger = true; - } - - // Check requirements for teleport - if (at) - { - uint32 miscRequirement = 0; - AreaLockStatus lockStatus = GetAreaTriggerLockStatus(at, GetDifficulty(mEntry->IsRaid()), miscRequirement); - if (lockStatus != AREA_LOCKSTATUS_OK) - { - // Teleport not requested by area-trigger - // TODO - Assume a player with expansion 0 travels from BootyBay to Ratched, and he is attempted to be teleported to outlands - // then he will repop near BootyBay instead of normally continuing his journey - // This code is probably added to catch passengers on ships to northrend who shouldn't go there - if (lockStatus == AREA_LOCKSTATUS_INSUFFICIENT_EXPANSION && !assignedAreaTrigger && GetTransport()) - RepopAtGraveyard(); // Teleport to near graveyard if on transport, looks blizz like :) - - SendTransferAbortedByLockStatus(mEntry, lockStatus, miscRequirement); - return false; - } - } - - if (Group* grp = GetGroup()) // TODO: Verify that this is correct place - grp->SetPlayerMap(GetObjectGuid(), mapid); - - // if we were on a transport, leave - if (!(options & TELE_TO_NOT_LEAVE_TRANSPORT) && m_transport) - { - m_transport->RemovePassenger(this); - m_transport = NULL; - m_movementInfo.ClearTransportData(); - } - - // The player was ported to another map and looses the duel immediately. - // We have to perform this check before the teleport, otherwise the - // ObjectAccessor won't find the flag. - if (duel && GetMapId() != mapid) - if (GetMap()->GetGameObject(GetGuidValue(PLAYER_DUEL_ARBITER))) - DuelComplete(DUEL_FLED); - - // reset movement flags at teleport, because player will continue move with these flags after teleport - m_movementInfo.SetMovementFlags(MOVEFLAG_NONE); - DisableSpline(); - - if ((GetMapId() == mapid) && (!m_transport)) // TODO the !m_transport might have unexpected effects when teleporting from transport to other place on same map - { - // lets reset far teleport flag if it wasn't reset during chained teleports - SetSemaphoreTeleportFar(false); - // setup delayed teleport flag - // if teleport spell is casted in Unit::Update() func - // then we need to delay it until update process will be finished - if (SetDelayedTeleportFlagIfCan()) - { - SetSemaphoreTeleportNear(true); - // lets save teleport destination for player - m_teleport_dest = WorldLocation(mapid, x, y, z, orientation); - m_teleport_options = options; - return true; - } - - if (!(options & TELE_TO_NOT_UNSUMMON_PET)) - { - // same map, only remove pet if out of range for new position - if (pet && !pet->IsWithinDist3d(x, y, z, GetMap()->GetVisibilityDistance())) - UnsummonPetTemporaryIfAny(); - } - - if (!(options & TELE_TO_NOT_LEAVE_COMBAT)) - CombatStop(); - - // this will be used instead of the current location in SaveToDB - m_teleport_dest = WorldLocation(mapid, x, y, z, orientation); - SetFallInformation(0, z); - - // code for finish transfer called in WorldSession::HandleMovementOpcodes() - // at client packet CMSG_MOVE_TELEPORT_ACK - SetSemaphoreTeleportNear(true); - // near teleport, triggering send CMSG_MOVE_TELEPORT_ACK from client at landing - if (!GetSession()->PlayerLogout()) - { - float oldX, oldY, oldZ; - float oldO = GetOrientation(); - GetPosition(oldX, oldY, oldZ);; - Relocate(x, y, z, orientation); - SendTeleportPacket(oldX, oldY, oldZ, oldO); - } - } - else - { - // far teleport to another map - Map* oldmap = IsInWorld() ? GetMap() : NULL; - // check if we can enter before stopping combat / removing pet / totems / interrupting spells - - // If the map is not created, assume it is possible to enter it. - // It will be created in the WorldPortAck. - DungeonPersistentState* state = GetBoundInstanceSaveForSelfOrGroup(mapid); - Map* map = sMapMgr.FindMap(mapid, state ? state->GetInstanceId() : 0); - if (!map || map->CanEnter(this)) - { - // lets reset near teleport flag if it wasn't reset during chained teleports - SetSemaphoreTeleportNear(false); - // setup delayed teleport flag - // if teleport spell is casted in Unit::Update() func - // then we need to delay it until update process will be finished - if (SetDelayedTeleportFlagIfCan()) - { - SetSemaphoreTeleportFar(true); - // lets save teleport destination for player - m_teleport_dest = WorldLocation(mapid, x, y, z, orientation); - m_teleport_options = options; - return true; - } - - SetSelectionGuid(ObjectGuid()); - - CombatStop(); - - ResetContestedPvP(); - - // remove player from battleground on far teleport (when changing maps) - if (BattleGround const* bg = GetBattleGround()) - { - // Note: at battleground join battleground id set before teleport - // and we already will found "current" battleground - // just need check that this is targeted map or leave - if (bg->GetMapId() != mapid) - LeaveBattleground(false); // don't teleport to entry point - } - - // remove pet on map change - if (pet) - UnsummonPetTemporaryIfAny(); - - // remove vehicle accessories on map change - if (IsVehicle()) - GetVehicleInfo()->RemoveAccessoriesFromMap(); - - // remove all dyn objects - RemoveAllDynObjects(); - - // stop spellcasting - // not attempt interrupt teleportation spell at caster teleport - if (!(options & TELE_TO_SPELL)) - if (IsNonMeleeSpellCasted(true)) - InterruptNonMeleeSpells(true); - - // remove auras before removing from map... - RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CHANGE_MAP | AURA_INTERRUPT_FLAG_MOVE | AURA_INTERRUPT_FLAG_TURNING); - - if (!GetSession()->PlayerLogout()) - { - // send transfer packet to display load screen - WorldPacket data(SMSG_TRANSFER_PENDING, (4 + 4 + 4)); - data.WriteBit(0); // unknown - if (m_transport) - { - data.WriteBit(1); // has transport - data << uint32(GetMapId()); - data << uint32(m_transport->GetEntry()); - } - else - data.WriteBit(0); // has transport - - data << uint32(mapid); - GetSession()->SendPacket(&data); - } - - // remove from old map now - if (oldmap) - oldmap->Remove(this, false); - - // new final coordinates - float final_x = x; - float final_y = y; - float final_z = z; - float final_o = orientation; - - if (m_transport) - { - final_x += m_movementInfo.GetTransportPos()->x; - final_y += m_movementInfo.GetTransportPos()->y; - final_z += m_movementInfo.GetTransportPos()->z; - final_o += m_movementInfo.GetTransportPos()->o; - } - - m_teleport_dest = WorldLocation(mapid, final_x, final_y, final_z, final_o); - SetFallInformation(0, final_z); - // if the player is saved before worldport ack (at logout for example) - // this will be used instead of the current location in SaveToDB - - // move packet sent by client always after far teleport - // code for finish transfer to new map called in WorldSession::HandleMoveWorldportAckOpcode at client packet - SetSemaphoreTeleportFar(true); - - if (!GetSession()->PlayerLogout()) - { - // transfer finished, inform client to start load - WorldPacket data(SMSG_NEW_WORLD, 20); - if (m_transport) - { - data << float(m_movementInfo.GetTransportPos()->x); - data << float(NormalizeOrientation(m_movementInfo.GetTransportPos()->o)); - data << float(m_movementInfo.GetTransportPos()->y); - } - else - { - data << float(final_x); - data << float(NormalizeOrientation(final_o)); - data << float(final_y); - } - - data << uint32(mapid); - - if (m_transport) - data << float(m_movementInfo.GetTransportPos()->z); - else - data << float(final_z); - - GetSession()->SendPacket(&data); - SendSavedInstances(); - } - } - else // !map->CanEnter(this) - return false; - } - return true; -} - -bool Player::TeleportToBGEntryPoint() -{ - ScheduleDelayedOperation(DELAYED_BG_MOUNT_RESTORE); - ScheduleDelayedOperation(DELAYED_BG_TAXI_RESTORE); - return TeleportTo(m_bgData.joinPos); -} - -void Player::ProcessDelayedOperations() -{ - if (m_DelayedOperations == 0) - return; - - if (m_DelayedOperations & DELAYED_RESURRECT_PLAYER) - { - ResurrectPlayer(0.0f, false); - - if (GetMaxHealth() > m_resurrectHealth) - SetHealth(m_resurrectHealth); - else - SetHealth(GetMaxHealth()); - - if (GetMaxPower(POWER_MANA) > m_resurrectMana) - SetPower(POWER_MANA, m_resurrectMana); - else - SetPower(POWER_MANA, GetMaxPower(POWER_MANA)); - - SetPower(POWER_RAGE, 0); - SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY)); - - SpawnCorpseBones(); - } - - if (m_DelayedOperations & DELAYED_SAVE_PLAYER) - { - SaveToDB(); - } - - if (m_DelayedOperations & DELAYED_SPELL_CAST_DESERTER) - { - CastSpell(this, 26013, true); // Deserter - } - - if (m_DelayedOperations & DELAYED_BG_MOUNT_RESTORE) - { - if (m_bgData.mountSpell) - { - CastSpell(this, m_bgData.mountSpell, true); - m_bgData.mountSpell = 0; - } - } - - if (m_DelayedOperations & DELAYED_BG_TAXI_RESTORE) - { - if (m_bgData.HasTaxiPath()) - { - m_taxi.AddTaxiDestination(m_bgData.taxiPath[0]); - m_taxi.AddTaxiDestination(m_bgData.taxiPath[1]); - m_bgData.ClearTaxiPath(); - - ContinueTaxiFlight(); - } - } - - // we have executed ALL delayed ops, so clear the flag - m_DelayedOperations = 0; -} - -void Player::AddToWorld() -{ - ///- Do not add/remove the player from the object storage - ///- It will crash when updating the ObjectAccessor - ///- The player should only be added when logging in - Unit::AddToWorld(); - - for (int i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; ++i) - { - if (m_items[i]) - m_items[i]->AddToWorld(); - } -} - -void Player::RemoveFromWorld() -{ - for (int i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; ++i) - { - if (m_items[i]) - m_items[i]->RemoveFromWorld(); - } - - ///- Do not add/remove the player from the object storage - ///- It will crash when updating the ObjectAccessor - ///- The player should only be removed when logging out - if (IsInWorld()) - GetCamera().ResetView(); - - Unit::RemoveFromWorld(); -} - -void Player::RewardRage(uint32 damage, uint32 weaponSpeedHitFactor, bool attacker) -{ - float addRage; - - float rageconversion = float((0.0091107836 * getLevel() * getLevel()) + 3.225598133 * getLevel()) + 4.2652911f; - - if (attacker) - { - addRage = ((damage / rageconversion * 7.5f + weaponSpeedHitFactor) / 2.0f); - - // talent who gave more rage on attack - addRage *= 1.0f + GetTotalAuraModifier(SPELL_AURA_MOD_RAGE_FROM_DAMAGE_DEALT) / 100.0f; - } - else - { - addRage = damage / rageconversion * 2.5f; - - // Berserker Rage effect - if (HasAura(18499, EFFECT_INDEX_0)) - addRage *= 1.3f; - } - - addRage *= sWorld.getConfig(CONFIG_FLOAT_RATE_POWER_RAGE_INCOME); - - ModifyPower(POWER_RAGE, uint32(addRage * 10)); -} - -void Player::RegenerateAll(uint32 diff) -{ - // Not in combat or they have regeneration - if (!isInCombat() || HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT) || - HasAuraType(SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT) || IsPolymorphed() || m_baseHealthRegen) - { - RegenerateHealth(diff); - if (!isInCombat() && !HasAuraType(SPELL_AURA_INTERRUPT_REGEN)) - { - Regenerate(POWER_RAGE, diff); - if (getClass() == CLASS_DEATH_KNIGHT) - Regenerate(POWER_RUNIC_POWER, diff); - } - } - - Regenerate(POWER_ENERGY, diff); - - Regenerate(POWER_MANA, diff); - - if (getClass() == CLASS_DEATH_KNIGHT) - Regenerate(POWER_RUNE, diff); - - if (getClass() == CLASS_HUNTER) - Regenerate(POWER_FOCUS, diff); - - if (getClass() == CLASS_PALADIN) - { - if (isInCombat()) - ResetHolyPowerRegenTimer(); - else if (m_holyPowerRegenTimer <= diff) - m_holyPowerRegenTimer = 0; - else - m_holyPowerRegenTimer -= diff; - - if (!m_holyPowerRegenTimer) - { - Regenerate(POWER_HOLY_POWER, diff); - ResetHolyPowerRegenTimer(); - } - } - - m_regenTimer = REGEN_TIME_FULL; -} - -// diff contains the time in milliseconds since last regen. -void Player::Regenerate(Powers power, uint32 diff) -{ - uint32 powerIndex = GetPowerIndex(power); - uint32 maxValue, curValue; - if (powerIndex == INVALID_POWER_INDEX) - return; - - if (power != POWER_RUNE) - { - maxValue = GetMaxPowerByIndex(powerIndex); - if (!maxValue) - return; - - curValue = GetPowerByIndex(powerIndex); - } - - float addvalue = 0.0f; - - switch (power) - { - case POWER_MANA: - { - if (HasAuraType(SPELL_AURA_STOP_NATURAL_MANA_REGEN)) - break; - float ManaIncreaseRate = sWorld.getConfig(CONFIG_FLOAT_RATE_POWER_MANA); - - if (isInCombat()) - { - // Mangos Updates Mana in intervals of 2s, which is correct - addvalue = GetFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER) * ManaIncreaseRate * 2.00f; - } - else - addvalue = GetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER) * ManaIncreaseRate * 2.00f; - - break; - } - case POWER_RAGE: // Regenerate rage - { - float RageDecreaseRate = sWorld.getConfig(CONFIG_FLOAT_RATE_POWER_RAGE_LOSS); - addvalue = 20 * RageDecreaseRate; // 2 rage by tick (= 2 seconds => 1 rage/sec) - break; - } - case POWER_FOCUS: - addvalue = 12; - break; - case POWER_HOLY_POWER: - if (!m_holyPowerRegenTimer) - addvalue = 1; - else - return; - break; - case POWER_ENERGY: // Regenerate energy - { - float EnergyRate = sWorld.getConfig(CONFIG_FLOAT_RATE_POWER_ENERGY); - addvalue = 20 * EnergyRate; - break; - } - case POWER_RUNIC_POWER: - { - float RunicPowerDecreaseRate = sWorld.getConfig(CONFIG_FLOAT_RATE_POWER_RUNICPOWER_LOSS); - addvalue = 30 * RunicPowerDecreaseRate; // 3 RunicPower by tick - break; - } - case POWER_RUNE: - { - if (getClass() != CLASS_DEATH_KNIGHT) - return; - - for (uint8 rune = 0; rune < MAX_RUNES; rune += 2) - { - uint8 runeToRegen = rune; - uint16 cd = GetRuneCooldown(rune); - uint16 secondRuneCd = GetRuneCooldown(rune + 1); - // Regenerate second rune of the same type only after first rune is off the cooldown - if (secondRuneCd && (cd > secondRuneCd || !cd)) - { - runeToRegen = rune + 1; - cd = secondRuneCd; - } - - if (cd) - { - if (cd == GetBaseRuneCooldown(runeToRegen)) - UpdateRuneRegen(runeSlotTypes[runeToRegen]); - - uint16 mod = uint32(diff * GetFloatValue(PLAYER_RUNE_REGEN_1 + uint8(GetCurrentRune(runeToRegen))) / 0.1f); - uint16 newCd = (cd > mod) ? cd - mod : 0; - SetRuneCooldown(runeToRegen, newCd); - } - } - break; - } - case POWER_HEALTH: - break; - } - - // Mana regen calculated in Player::UpdateManaRegen() - // Exist only for POWER_MANA, POWER_ENERGY, POWER_FOCUS auras - if (power != POWER_MANA) - { - AuraList const& ModPowerRegenPCTAuras = GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT); - for (AuraList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i) - if ((*i)->GetModifier()->m_miscvalue == int32(power)) - addvalue *= ((*i)->GetModifier()->m_amount + 100) / 100.0f; - } - - // addvalue computed on a 2sec basis. => update to diff time - addvalue *= float(diff) / REGEN_TIME_FULL; - - if (power != POWER_RAGE && power != POWER_RUNIC_POWER && power != POWER_HOLY_POWER) - { - curValue += uint32(addvalue); - if (curValue > maxValue) - curValue = maxValue; - } - else - { - if (curValue <= uint32(addvalue)) - curValue = 0; - else - curValue -= uint32(addvalue); - } - SetPower(power, curValue); -} - -void Player::RegenerateHealth(uint32 diff) -{ - uint32 curValue = GetHealth(); - uint32 maxValue = GetMaxHealth(); - - if (curValue >= maxValue) return; - - float HealthIncreaseRate = sWorld.getConfig(CONFIG_FLOAT_RATE_HEALTH); - - float addvalue = 0.0f; - - // polymorphed case - if (IsPolymorphed()) - addvalue = (float)GetMaxHealth() / 3; - // normal regen case (maybe partly in combat case) - else if (!isInCombat() || HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT)) - { - addvalue = HealthIncreaseRate; - if (!isInCombat()) - { - if (getLevel() < 15) - addvalue = 0.20f * GetMaxHealth() * addvalue / getLevel(); - else - addvalue = 0.015f * GetMaxHealth() * addvalue; - - AuraList const& mModHealthRegenPct = GetAurasByType(SPELL_AURA_MOD_HEALTH_REGEN_PERCENT); - for (AuraList::const_iterator i = mModHealthRegenPct.begin(); i != mModHealthRegenPct.end(); ++i) - addvalue *= (100.0f + (*i)->GetModifier()->m_amount) / 100.0f; - - addvalue += GetTotalAuraModifier(SPELL_AURA_MOD_REGEN) * 2.0f / 5.0f; - } - else if (HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT)) - addvalue *= GetTotalAuraModifier(SPELL_AURA_MOD_REGEN_DURING_COMBAT) / 100.0f; - - if (!IsStandState()) - addvalue *= 1.33f; - } - - // always regeneration bonus (including combat) - addvalue += GetTotalAuraModifier(SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT); - - addvalue += m_baseHealthRegen / 2.5f; - - if (addvalue < 0) - addvalue = 0; - - addvalue *= (float)diff / REGEN_TIME_FULL; - - ModifyHealth(int32(addvalue)); -} - -Creature* Player::GetNPCIfCanInteractWith(ObjectGuid guid, uint32 npcflagmask) -{ - // some basic checks - if (!guid || !IsInWorld() || IsTaxiFlying()) - return NULL; - - // not in interactive state - if (hasUnitState(UNIT_STAT_CAN_NOT_REACT_OR_LOST_CONTROL)) - return NULL; - - // exist (we need look pets also for some interaction (quest/etc) - Creature* unit = GetMap()->GetAnyTypeCreature(guid); - if (!unit) - return NULL; - - // appropriate npc type - if (npcflagmask && !unit->HasFlag(UNIT_NPC_FLAGS, npcflagmask)) - return NULL; - - if (npcflagmask == UNIT_NPC_FLAG_STABLEMASTER) - { - if (getClass() != CLASS_HUNTER) - return NULL; - } - - // if a dead unit should be able to talk - the creature must be alive and have special flags - if (!unit->isAlive()) - return NULL; - - if (isAlive() && unit->isInvisibleForAlive()) - return NULL; - - // not allow interaction under control, but allow with own pets - if (unit->GetCharmerGuid()) - return NULL; - - // not enemy - if (unit->IsHostileTo(this)) - return NULL; - - // not too far - if (!unit->IsWithinDistInMap(this, INTERACTION_DISTANCE)) - return NULL; - - return unit; -} - -GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid guid, uint32 gameobject_type) const -{ - // some basic checks - if (!guid || !IsInWorld() || IsTaxiFlying()) - return NULL; - - // not in interactive state - if (hasUnitState(UNIT_STAT_CAN_NOT_REACT_OR_LOST_CONTROL)) - return NULL; - - if (GameObject* go = GetMap()->GetGameObject(guid)) - { - if (uint32(go->GetGoType()) == gameobject_type || gameobject_type == MAX_GAMEOBJECT_TYPE) - { - float maxdist; - switch (go->GetGoType()) - { - // TODO: find out how the client calculates the maximal usage distance to spellless working - // gameobjects like guildbanks and mailboxes - 10.0 is a just an abitrary choosen number - case GAMEOBJECT_TYPE_GUILD_BANK: - case GAMEOBJECT_TYPE_MAILBOX: - maxdist = 10.0f; - break; - case GAMEOBJECT_TYPE_FISHINGHOLE: - maxdist = 20.0f + CONTACT_DISTANCE; // max spell range - break; - default: - maxdist = INTERACTION_DISTANCE; - break; - } - - if (go->IsWithinDistInMap(this, maxdist) && go->isSpawned()) - return go; - - sLog.outError("GetGameObjectIfCanInteractWith: GameObject '%s' [GUID: %u] is too far away from player %s [GUID: %u] to be used by him (distance=%f, maximal %f is allowed)", - go->GetGOInfo()->name, go->GetGUIDLow(), GetName(), GetGUIDLow(), go->GetDistance(this), maxdist); - } - } - return NULL; -} - -bool Player::IsUnderWater() const -{ - return GetTerrain()->IsUnderWater(GetPositionX(), GetPositionY(), GetPositionZ() + 2); -} - -void Player::SetInWater(bool apply) -{ - if (m_isInWater == apply) - return; - - // define player in water by opcodes - // move player's guid into HateOfflineList of those mobs - // which can't swim and move guid back into ThreatList when - // on surface. - // TODO: exist also swimming mobs, and function must be symmetric to enter/leave water - m_isInWater = apply; - - // remove auras that need water/land - RemoveAurasWithInterruptFlags(apply ? AURA_INTERRUPT_FLAG_NOT_ABOVEWATER : AURA_INTERRUPT_FLAG_NOT_UNDERWATER); - - getHostileRefManager().updateThreatTables(); -} - -struct SetGameMasterOnHelper -{ - explicit SetGameMasterOnHelper() {} - void operator()(Unit* unit) const - { - unit->setFaction(35); - unit->getHostileRefManager().setOnlineOfflineState(false); - } -}; - -struct SetGameMasterOffHelper -{ - explicit SetGameMasterOffHelper(uint32 _faction) : faction(_faction) {} - void operator()(Unit* unit) const - { - unit->setFaction(faction); - unit->getHostileRefManager().setOnlineOfflineState(true); - } - uint32 faction; -}; - -void Player::SetGameMaster(bool on) -{ - if (on) - { - m_ExtraFlags |= PLAYER_EXTRA_GM_ON; - setFaction(35); - SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM); - - CallForAllControlledUnits(SetGameMasterOnHelper(), CONTROLLED_PET | CONTROLLED_TOTEMS | CONTROLLED_GUARDIANS | CONTROLLED_CHARM); - - SetFFAPvP(false); - ResetContestedPvP(); - - getHostileRefManager().setOnlineOfflineState(false); - CombatStopWithPets(); - - SetPhaseMask(PHASEMASK_ANYWHERE, false); // see and visible in all phases - } - else - { - m_ExtraFlags &= ~PLAYER_EXTRA_GM_ON; - setFactionForRace(getRace()); - RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM); - - // restore phase - SetPhaseMask(phaseMgr->GetCurrentPhasemask(), false); - - CallForAllControlledUnits(SetGameMasterOffHelper(getFaction()), CONTROLLED_PET | CONTROLLED_TOTEMS | CONTROLLED_GUARDIANS | CONTROLLED_CHARM); - - // restore FFA PvP Server state - if (sWorld.IsFFAPvPRealm()) - SetFFAPvP(true); - - // restore FFA PvP area state, remove not allowed for GM mounts - UpdateArea(m_areaUpdateId); - - getHostileRefManager().setOnlineOfflineState(true); - - phaseMgr->AddUpdateFlag(PHASE_UPDATE_FLAG_SERVERSIDE_CHANGED); - phaseMgr->Update(); - } - - m_camera.UpdateVisibilityForOwner(); - UpdateObjectVisibility(); - UpdateForQuestWorldObjects(); -} - -void Player::SetGMVisible(bool on) -{ - if (on) - { - m_ExtraFlags &= ~PLAYER_EXTRA_GM_INVISIBLE; // remove flag - - // Reapply stealth/invisibility if active or show if not any - if (HasAuraType(SPELL_AURA_MOD_STEALTH)) - SetVisibility(VISIBILITY_GROUP_STEALTH); - else if (HasAuraType(SPELL_AURA_MOD_INVISIBILITY)) - SetVisibility(VISIBILITY_GROUP_INVISIBILITY); - else - SetVisibility(VISIBILITY_ON); - } - else - { - m_ExtraFlags |= PLAYER_EXTRA_GM_INVISIBLE; // add flag - - SetAcceptWhispers(false); - SetGameMaster(true); - - SetVisibility(VISIBILITY_OFF); - } -} - -bool Player::IsGroupVisibleFor(Player* p) const -{ - switch (sWorld.getConfig(CONFIG_UINT32_GROUP_VISIBILITY)) - { - default: return IsInSameGroupWith(p); - case 1: return IsInSameRaidWith(p); - case 2: return GetTeam() == p->GetTeam(); - } -} - -bool Player::IsInSameGroupWith(Player const* p) const -{ - return (p == this || (GetGroup() != NULL && - GetGroup()->SameSubGroup(this, p))); -} - -///- If the player is invited, remove him. If the group if then only 1 person, disband the group. -/// \todo Shouldn't we also check if there is no other invitees before disbanding the group? -void Player::UninviteFromGroup() -{ - Group* group = GetGroupInvite(); - if (!group) - return; - - group->RemoveInvite(this); - - if (group->GetMembersCount() <= 1) // group has just 1 member => disband - { - if (group->IsCreated()) - { - group->Disband(true); - sObjectMgr.RemoveGroup(group); - } - else - group->RemoveAllInvites(); - - delete group; - } -} - -void Player::RemoveFromGroup(Group* group, ObjectGuid guid) -{ - if (group) - { - if (group->RemoveMember(guid, 0) <= 1) - { - // group->Disband(); already disbanded in RemoveMember - sObjectMgr.RemoveGroup(group); - delete group; - // removemember sets the player's group pointer to NULL - } - } -} - -void Player::SendLogXPGain(uint32 GivenXP, Unit* victim, uint32 RestXP) -{ - WorldPacket data(SMSG_LOG_XPGAIN, 21); - data << (victim ? victim->GetObjectGuid() : ObjectGuid());// guid - data << uint32(GivenXP + RestXP); // given experience - data << uint8(victim ? 0 : 1); // 00-kill_xp type, 01-non_kill_xp type - if (victim) - { - data << uint32(GivenXP); // experience without rested bonus - data << float(1); // 1 - none 0 - 100% group bonus output - } - data << uint8(0); // new 2.4.0 - GetSession()->SendPacket(&data); -} - -void Player::GiveXP(uint32 xp, Unit* victim) -{ - if (xp < 1) - return; - - if (!isAlive()) - return; - - if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_XP_USER_DISABLED)) - return; - - uint32 level = getLevel(); - - // XP to money conversion processed in Player::RewardQuest - if (level >= sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL)) - return; - - if (victim) - { - // handle SPELL_AURA_MOD_KILL_XP_PCT auras - Unit::AuraList const& ModXPPctAuras = GetAurasByType(SPELL_AURA_MOD_KILL_XP_PCT); - for (Unit::AuraList::const_iterator i = ModXPPctAuras.begin(); i != ModXPPctAuras.end(); ++i) - xp = uint32(xp * (1.0f + (*i)->GetModifier()->m_amount / 100.0f)); - } - else - { - // handle SPELL_AURA_MOD_QUEST_XP_PCT auras - Unit::AuraList const& ModXPPctAuras = GetAurasByType(SPELL_AURA_MOD_QUEST_XP_PCT); - for (Unit::AuraList::const_iterator i = ModXPPctAuras.begin(); i != ModXPPctAuras.end(); ++i) - xp = uint32(xp * (1.0f + (*i)->GetModifier()->m_amount / 100.0f)); - } - - // XP resting bonus for kill - uint32 rested_bonus_xp = victim ? GetXPRestBonus(xp) : 0; - - SendLogXPGain(xp, victim, rested_bonus_xp); - - uint32 curXP = GetUInt32Value(PLAYER_XP); - uint32 nextLvlXP = GetUInt32Value(PLAYER_NEXT_LEVEL_XP); - uint32 newXP = curXP + xp + rested_bonus_xp; - - while (newXP >= nextLvlXP && level < sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL)) - { - newXP -= nextLvlXP; - - if (level < sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL)) - GiveLevel(level + 1); - - level = getLevel(); - nextLvlXP = GetUInt32Value(PLAYER_NEXT_LEVEL_XP); - } - - SetUInt32Value(PLAYER_XP, newXP); -} - -// Update player to next level -// Current player experience not update (must be update by caller) -void Player::GiveLevel(uint32 level) -{ - if (level == getLevel()) - return; - - PlayerLevelInfo info; - sObjectMgr.GetPlayerLevelInfo(getRace(), getClass(), level, &info); - - uint32 basehp = 0, basemana = 0; - sObjectMgr.GetPlayerClassLevelInfo(getClass(), level, basehp, basemana); - - // send levelup info to client - WorldPacket data(SMSG_LEVELUP_INFO, (4 + 4 + MAX_STORED_POWERS * 4 + MAX_STATS * 4)); - data << uint32(level); - data << uint32(int32(basehp) - int32(GetCreateHealth())); - // for(int i = 0; i < MAX_POWERS; ++i) // Powers loop (0-4) - data << uint32(int32(basemana) - int32(GetCreateMana())); - data << uint32(0); - data << uint32(0); - data << uint32(0); - data << uint32(0); - // end for - for (int i = STAT_STRENGTH; i < MAX_STATS; ++i) // Stats loop (0-4) - data << uint32(int32(info.stats[i]) - GetCreateStat(Stats(i))); - - GetSession()->SendPacket(&data); - - SetUInt32Value(PLAYER_NEXT_LEVEL_XP, sObjectMgr.GetXPForLevel(level)); - - // update level, max level of skills - m_Played_time[PLAYED_TIME_LEVEL] = 0; // Level Played Time reset - - _ApplyAllLevelScaleItemMods(false); - - SetLevel(level); - - UpdateSkillsForLevel(); - - // save base values (bonuses already included in stored stats - for (int i = STAT_STRENGTH; i < MAX_STATS; ++i) - SetCreateStat(Stats(i), info.stats[i]); - - SetCreateHealth(basehp); - SetCreateMana(basemana); - - InitTalentForLevel(); - InitTaxiNodesForLevel(); - InitGlyphsForLevel(); - - UpdateAllStats(); - - // set current level health and mana/energy to maximum after applying all mods. - if (isAlive()) - SetHealth(GetMaxHealth()); - SetPower(POWER_MANA, GetMaxPower(POWER_MANA)); - SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY)); - if (GetPower(POWER_RAGE) > GetMaxPower(POWER_RAGE)) - SetPower(POWER_RAGE, GetMaxPower(POWER_RAGE)); - SetPower(POWER_FOCUS, 0); - - _ApplyAllLevelScaleItemMods(true); - - // update level to hunter/summon pet - if (Pet* pet = GetPet()) - pet->SynchronizeLevelWithOwner(); - - if (MailLevelReward const* mailReward = sObjectMgr.GetMailLevelReward(level, getRaceMask())) - MailDraft(mailReward->mailTemplateId).SendMailTo(this, MailSender(MAIL_CREATURE, mailReward->senderEntry)); - - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL); - - PhaseUpdateData phaseUdateData; - phaseUdateData.AddConditionType(CONDITION_LEVEL); - - phaseMgr->NotifyConditionChanged(phaseUdateData); -} - -void Player::UpdateFreeTalentPoints(bool resetIfNeed) -{ - uint32 level = getLevel(); - // talents base at level diff ( talents = level - 9 but some can be used already) - if (level < 10) - { - // Remove all talent points - if (m_usedTalentCount > 0) // Free any used talents - { - if (resetIfNeed) - resetTalents(true); - SetFreeTalentPoints(0); - } - } - else - { - if (m_specsCount == 0) - { - m_specsCount = 1; - m_activeSpec = 0; - } - - uint32 talentPointsForLevel = CalculateTalentsPoints(); - - // if used more that have then reset - if (m_usedTalentCount > talentPointsForLevel) - { - if (resetIfNeed && GetSession()->GetSecurity() < SEC_ADMINISTRATOR) - resetTalents(true); - else - SetFreeTalentPoints(0); - } - // else update amount of free points - else - SetFreeTalentPoints(talentPointsForLevel - m_usedTalentCount); - } -} - -void Player::InitTalentForLevel() -{ - UpdateFreeTalentPoints(); - - if (!GetSession()->PlayerLoading()) - SendTalentsInfoData(false); // update at client -} - -void Player::InitStatsForLevel(bool reapplyMods) -{ - if (reapplyMods) // reapply stats values only on .reset stats (level) command - _RemoveAllStatBonuses(); - - uint32 basehp = 0, basemana = 0; - sObjectMgr.GetPlayerClassLevelInfo(getClass(), getLevel(), basehp, basemana); - - PlayerLevelInfo info; - sObjectMgr.GetPlayerLevelInfo(getRace(), getClass(), getLevel(), &info); - - SetUInt32Value(PLAYER_FIELD_MAX_LEVEL, sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL)); - SetUInt32Value(PLAYER_NEXT_LEVEL_XP, sObjectMgr.GetXPForLevel(getLevel())); - - // reset before any aura state sources (health set/aura apply) - SetUInt32Value(UNIT_FIELD_AURASTATE, 0); - - UpdateSkillsForLevel(); - - // set default cast time multiplier - SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f); - - // save base values (bonuses already included in stored stats - for (int i = STAT_STRENGTH; i < MAX_STATS; ++i) - SetCreateStat(Stats(i), info.stats[i]); - - for (int i = STAT_STRENGTH; i < MAX_STATS; ++i) - SetStat(Stats(i), info.stats[i]); - - SetCreateHealth(basehp); - - // set create powers - SetCreateMana(basemana); - - SetArmor(int32(m_createStats[STAT_AGILITY] * 2)); - - InitStatBuffMods(); - - // reset rating fields values - for (uint16 index = PLAYER_FIELD_COMBAT_RATING_1; index < PLAYER_FIELD_COMBAT_RATING_1 + MAX_COMBAT_RATING; ++index) - SetUInt32Value(index, 0); - - SetUInt32Value(PLAYER_FIELD_MOD_HEALING_DONE_POS, 0); - SetFloatValue(PLAYER_FIELD_MOD_HEALING_PCT, 1.0f); - SetFloatValue(PLAYER_FIELD_MOD_HEALING_DONE_PCT, 1.0f); - for (int i = 0; i < MAX_SPELL_SCHOOL; ++i) - { - SetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG + i, 0); - SetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + i, 0); - SetFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT + i, 1.00f); - } - - SetFloatValue(PLAYER_FIELD_MOD_SPELL_POWER_PCT, 1.0f); - - // reset attack power, damage and attack speed fields - SetFloatValue(UNIT_FIELD_BASEATTACKTIME, 2000.0f); - SetFloatValue(UNIT_FIELD_BASEATTACKTIME + 1, 2000.0f); // offhand attack time - SetFloatValue(UNIT_FIELD_RANGEDATTACKTIME, 2000.0f); - - SetFloatValue(UNIT_FIELD_MINDAMAGE, 0.0f); - SetFloatValue(UNIT_FIELD_MAXDAMAGE, 0.0f); - SetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE, 0.0f); - SetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE, 0.0f); - SetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE, 0.0f); - SetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE, 0.0f); - SetFloatValue(PLAYER_FIELD_WEAPON_DMG_MULTIPLIERS, 1.0f); - - SetInt32Value(UNIT_FIELD_ATTACK_POWER, 0 ); - SetInt32Value(UNIT_FIELD_ATTACK_POWER_MOD_POS, 0 ); - SetInt32Value(UNIT_FIELD_ATTACK_POWER_MOD_NEG, 0 ); - SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER,0.0f); - SetInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER, 0 ); - SetInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER_MOD_POS,0 ); - SetInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER_MOD_NEG,0 ); - SetFloatValue(UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER,0.0f); - - // Base crit values (will be recalculated in UpdateAllStats() at loading and in _ApplyAllStatBonuses() at reset - SetFloatValue(PLAYER_CRIT_PERCENTAGE, 0.0f); - SetFloatValue(PLAYER_OFFHAND_CRIT_PERCENTAGE, 0.0f); - SetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE, 0.0f); - - // Init spell schools (will be recalculated in UpdateAllStats() at loading and in _ApplyAllStatBonuses() at reset - for (uint8 i = 0; i < MAX_SPELL_SCHOOL; ++i) - SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1 + i, 0.0f); - - SetFloatValue(PLAYER_PARRY_PERCENTAGE, 0.0f); - SetFloatValue(PLAYER_BLOCK_PERCENTAGE, 0.0f); - SetUInt32Value(PLAYER_SHIELD_BLOCK, uint32(BASE_BLOCK_DAMAGE_PERCENT)); - - // Dodge percentage - SetFloatValue(PLAYER_DODGE_PERCENTAGE, 0.0f); - - // set armor (resistance 0) to original value (create_agility*2) - SetArmor(int32(m_createStats[STAT_AGILITY] * 2)); - SetResistanceBuffMods(SpellSchools(0), true, 0.0f); - SetResistanceBuffMods(SpellSchools(0), false, 0.0f); - // set other resistance to original value (0) - for (int i = 1; i < MAX_SPELL_SCHOOL; ++i) - { - SetResistance(SpellSchools(i), 0); - SetResistanceBuffMods(SpellSchools(i), true, 0.0f); - SetResistanceBuffMods(SpellSchools(i), false, 0.0f); - } - - SetUInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE, 0); - SetUInt32Value(PLAYER_FIELD_MOD_TARGET_PHYSICAL_RESISTANCE, 0); - for (int i = 0; i < MAX_SPELL_SCHOOL; ++i) - { - SetUInt32Value(UNIT_FIELD_POWER_COST_MODIFIER + i, 0); - SetFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER + i, 0.0f); - } - // Reset no reagent cost field - for (int i = 0; i < 3; ++i) - SetUInt32Value(PLAYER_NO_REAGENT_COST_1 + i, 0); - // Init data for form but skip reapply item mods for form - InitDataForForm(reapplyMods); - - // save new stats - for (int i = POWER_MANA; i < MAX_POWERS; ++i) - SetMaxPower(Powers(i), GetCreateMaxPowers(Powers(i))); - - SetMaxHealth(basehp); // stamina bonus will applied later - - // cleanup mounted state (it will set correctly at aura loading if player saved at mount. - SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, 0); - - // cleanup unit flags (will be re-applied if need at aura load). - RemoveFlag(UNIT_FIELD_FLAGS, - UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NOT_ATTACKABLE_1 | - UNIT_FLAG_OOC_NOT_ATTACKABLE | UNIT_FLAG_PASSIVE | UNIT_FLAG_LOOTING | - UNIT_FLAG_PET_IN_COMBAT | UNIT_FLAG_SILENCED | UNIT_FLAG_PACIFIED | - UNIT_FLAG_STUNNED | UNIT_FLAG_IN_COMBAT | UNIT_FLAG_DISARMED | - UNIT_FLAG_CONFUSED | UNIT_FLAG_FLEEING | UNIT_FLAG_NOT_SELECTABLE | - UNIT_FLAG_SKINNABLE | UNIT_FLAG_MOUNT | UNIT_FLAG_TAXI_FLIGHT); - SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); // must be set - - SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_REGENERATE_POWER); // must be set - - // cleanup player flags (will be re-applied if need at aura load), to avoid have ghost flag without ghost aura, for example. - RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK | PLAYER_FLAGS_DND | PLAYER_FLAGS_GM | PLAYER_FLAGS_GHOST); - - RemoveStandFlags(UNIT_STAND_FLAGS_ALL); // one form stealth modified bytes - RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP | UNIT_BYTE2_FLAG_SANCTUARY); - - // restore if need some important flags - SetUInt32Value(PLAYER_FIELD_BYTES2, 0); // flags empty by default - - if (reapplyMods) // reapply stats values only on .reset stats (level) command - _ApplyAllStatBonuses(); - - // set current level health and mana/energy to maximum after applying all mods. - SetHealth(GetMaxHealth()); - SetPower(POWER_MANA, GetMaxPower(POWER_MANA)); - SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY)); - if (GetPower(POWER_RAGE) > GetMaxPower(POWER_RAGE)) - SetPower(POWER_RAGE, GetMaxPower(POWER_RAGE)); - SetPower(POWER_FOCUS, 0); - SetPower(POWER_RUNIC_POWER, 0); - - // update level to hunter/summon pet - if (Pet* pet = GetPet()) - pet->SynchronizeLevelWithOwner(); -} - -void Player::SendInitialSpells() -{ - time_t curTime = time(NULL); - time_t infTime = curTime + infinityCooldownDelayCheck; - - uint16 spellCount = 0; - - WorldPacket data(SMSG_INITIAL_SPELLS, (1 + 2 + 4 * m_spells.size() + 2 + m_spellCooldowns.size() * (2 + 2 + 2 + 4 + 4))); - data << uint8(0); - - size_t countPos = data.wpos(); - data << uint16(spellCount); // spell count placeholder - - for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr) - { - if (itr->second.state == PLAYERSPELL_REMOVED) - continue; - - if (!itr->second.active || itr->second.disabled) - continue; - - data << uint32(itr->first); - data << uint16(0); // it's not slot id - - spellCount += 1; - } - - data.put(countPos, spellCount); // write real count value - - uint16 spellCooldowns = m_spellCooldowns.size(); - data << uint16(spellCooldowns); - for (SpellCooldowns::const_iterator itr = m_spellCooldowns.begin(); itr != m_spellCooldowns.end(); ++itr) - { - SpellEntry const* sEntry = sSpellStore.LookupEntry(itr->first); - if (!sEntry) - continue; - - data << uint32(itr->first); - - data << uint32(itr->second.itemid); // cast item id - data << uint16(sEntry->GetCategory()); // spell category - - // send infinity cooldown in special format - if (itr->second.end >= infTime) - { - data << uint32(1); // cooldown - data << uint32(0x80000000); // category cooldown - continue; - } - - time_t cooldown = itr->second.end > curTime ? (itr->second.end - curTime) * IN_MILLISECONDS : 0; - - if(sEntry->GetCategory()) // may be wrong, but anyway better than nothing... - { - data << uint32(0); // cooldown - data << uint32(cooldown); // category cooldown - } - else - { - data << uint32(cooldown); // cooldown - data << uint32(0); // category cooldown - } - } - - GetSession()->SendPacket(&data); - - DETAIL_LOG("CHARACTER: Sent Initial Spells"); -} - -void Player::RemoveMail(uint32 id) -{ - for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr) - { - if ((*itr)->messageID == id) - { - // do not delete item, because Player::removeMail() is called when returning mail to sender. - m_mail.erase(itr); - return; - } - } -} - -void Player::SendMailResult(uint32 mailId, MailResponseType mailAction, MailResponseResult mailError, uint32 equipError, uint32 item_guid, uint32 item_count) -{ - WorldPacket data(SMSG_SEND_MAIL_RESULT, (4 + 4 + 4 + (mailError == MAIL_ERR_EQUIP_ERROR ? 4 : (mailAction == MAIL_ITEM_TAKEN ? 4 + 4 : 0)))); - data << (uint32) mailId; - data << (uint32) mailAction; - data << (uint32) mailError; - if (mailError == MAIL_ERR_EQUIP_ERROR) - data << (uint32) equipError; - else if (mailAction == MAIL_ITEM_TAKEN) - { - data << (uint32) item_guid; // item guid low? - data << (uint32) item_count; // item count? - } - GetSession()->SendPacket(&data); -} - -void Player::SendNewMail() -{ - // deliver undelivered mail - WorldPacket data(SMSG_RECEIVED_MAIL, 4); - data << float(0.0f); - GetSession()->SendPacket(&data); -} - -void Player::UpdateNextMailTimeAndUnreads() -{ - // calculate next delivery time (min. from non-delivered mails - // and recalculate unReadMail - time_t cTime = time(NULL); - m_nextMailDelivereTime = 0; - unReadMails = 0; - for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr) - { - if ((*itr)->deliver_time > cTime) - { - if (!m_nextMailDelivereTime || m_nextMailDelivereTime > (*itr)->deliver_time) - m_nextMailDelivereTime = (*itr)->deliver_time; - } - else if (((*itr)->checked & MAIL_CHECK_MASK_READ) == 0) - ++unReadMails; - } -} - -void Player::AddNewMailDeliverTime(time_t deliver_time) -{ - if (deliver_time <= time(NULL)) // ready now - { - ++unReadMails; - SendNewMail(); - } - else // not ready and no have ready mails - { - if (!m_nextMailDelivereTime || m_nextMailDelivereTime > deliver_time) - m_nextMailDelivereTime = deliver_time; - } -} - -bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependent, bool disabled) -{ - SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell_id); - if (!spellInfo) - { - // do character spell book cleanup (all characters) - if (!IsInWorld() && !learning) // spell load case - { - sLog.outError("Player::addSpell: nonexistent in SpellStore spell #%u request, deleting for all characters in `character_spell`.", spell_id); - CharacterDatabase.PExecute("DELETE FROM character_spell WHERE spell = '%u'", spell_id); - } - else - sLog.outError("Player::addSpell: nonexistent in SpellStore spell #%u request.", spell_id); - - return false; - } - - if (!SpellMgr::IsSpellValid(spellInfo, this, false)) - { - // do character spell book cleanup (all characters) - if (!IsInWorld() && !learning) // spell load case - { - sLog.outError("Player::addSpell: Broken spell #%u learning not allowed, deleting for all characters in `character_spell`.", spell_id); - CharacterDatabase.PExecute("DELETE FROM character_spell WHERE spell = '%u'", spell_id); - } - else - sLog.outError("Player::addSpell: Broken spell #%u learning not allowed.", spell_id); - - return false; - } - - PlayerSpellState state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED; - - bool dependent_set = false; - bool disabled_case = false; - bool superceded_old = false; - - PlayerSpellMap::iterator itr = m_spells.find(spell_id); - if (itr != m_spells.end()) - { - uint32 next_active_spell_id = 0; - // fix activate state for non-stackable low rank (and find next spell for !active case) - if (sSpellMgr.IsRankedSpellNonStackableInSpellBook(spellInfo)) - { - SpellChainMapNext const& nextMap = sSpellMgr.GetSpellChainNext(); - for (SpellChainMapNext::const_iterator next_itr = nextMap.lower_bound(spell_id); next_itr != nextMap.upper_bound(spell_id); ++next_itr) - { - if (HasSpell(next_itr->second)) - { - // high rank already known so this must !active - active = false; - next_active_spell_id = next_itr->second; - break; - } - } - } - - // not do anything if already known in expected state - if (itr->second.state != PLAYERSPELL_REMOVED && itr->second.active == active && - itr->second.dependent == dependent && itr->second.disabled == disabled) - { - if (!IsInWorld() && !learning) // explicitly load from DB and then exist in it already and set correctly - itr->second.state = PLAYERSPELL_UNCHANGED; - - return false; - } - - // dependent spell known as not dependent, overwrite state - if (itr->second.state != PLAYERSPELL_REMOVED && !itr->second.dependent && dependent) - { - itr->second.dependent = dependent; - if (itr->second.state != PLAYERSPELL_NEW) - itr->second.state = PLAYERSPELL_CHANGED; - dependent_set = true; - } - - // update active state for known spell - if (itr->second.active != active && itr->second.state != PLAYERSPELL_REMOVED && !itr->second.disabled) - { - itr->second.active = active; - - if (!IsInWorld() && !learning && !dependent_set)// explicitly load from DB and then exist in it already and set correctly - itr->second.state = PLAYERSPELL_UNCHANGED; - else if (itr->second.state != PLAYERSPELL_NEW) - itr->second.state = PLAYERSPELL_CHANGED; - - if (active) - { - if (IsNeedCastPassiveLikeSpellAtLearn(spellInfo)) - CastSpell(this, spell_id, true); - } - else if (IsInWorld()) - { - if (next_active_spell_id) - { - // update spell ranks in spellbook and action bar - WorldPacket data(SMSG_SUPERCEDED_SPELL, 4 + 4); - data << uint32(spell_id); - data << uint32(next_active_spell_id); - GetSession()->SendPacket(&data); - } - else - { - WorldPacket data(SMSG_REMOVED_SPELL, 4); - data << uint32(spell_id); - GetSession()->SendPacket(&data); - } - } - - return active; // learn (show in spell book if active now) - } - - if (itr->second.disabled != disabled && itr->second.state != PLAYERSPELL_REMOVED) - { - if (itr->second.state != PLAYERSPELL_NEW) - itr->second.state = PLAYERSPELL_CHANGED; - itr->second.disabled = disabled; - - if (disabled) - return false; - - disabled_case = true; - } - else switch (itr->second.state) - { - case PLAYERSPELL_UNCHANGED: // known saved spell - return false; - case PLAYERSPELL_REMOVED: // re-learning removed not saved spell - { - m_spells.erase(itr); - state = PLAYERSPELL_CHANGED; - break; // need re-add - } - default: // known not saved yet spell (new or modified) - { - // can be in case spell loading but learned at some previous spell loading - if (!IsInWorld() && !learning && !dependent_set) - itr->second.state = PLAYERSPELL_UNCHANGED; - - return false; - } - } - } - - TalentSpellPos const* talentPos = GetTalentSpellPos(spell_id); - - if (!disabled_case) // skip new spell adding if spell already known (disabled spells case) - { - // talent: unlearn all other talent ranks (high and low) - if (talentPos) - { - if (TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentPos->talent_id)) - { - for (int i = 0; i < MAX_TALENT_RANK; ++i) - { - // skip learning spell and no rank spell case - uint32 rankSpellId = talentInfo->RankID[i]; - if (!rankSpellId || rankSpellId == spell_id) - continue; - - removeSpell(rankSpellId, false, false); - } - } - } - // non talent spell: learn low ranks (recursive call) - else if (uint32 prev_spell = sSpellMgr.GetPrevSpellInChain(spell_id)) - { - if (!IsInWorld() || disabled) // at spells loading, no output, but allow save - addSpell(prev_spell, active, true, true, disabled); - else // at normal learning - learnSpell(prev_spell, true); - } - - PlayerSpell newspell; - newspell.state = state; - newspell.active = active; - newspell.dependent = dependent; - newspell.disabled = disabled; - - // replace spells in action bars and spellbook to bigger rank if only one spell rank must be accessible - if (newspell.active && !newspell.disabled && sSpellMgr.IsRankedSpellNonStackableInSpellBook(spellInfo)) - { - for (PlayerSpellMap::iterator itr2 = m_spells.begin(); itr2 != m_spells.end(); ++itr2) - { - if (itr2->second.state == PLAYERSPELL_REMOVED) continue; - SpellEntry const* i_spellInfo = sSpellStore.LookupEntry(itr2->first); - if (!i_spellInfo) continue; - - if (sSpellMgr.IsRankSpellDueToSpell(spellInfo, itr2->first)) - { - if (itr2->second.active) - { - if (sSpellMgr.IsHighRankOfSpell(spell_id, itr2->first)) - { - if (IsInWorld()) // not send spell (re-/over-)learn packets at loading - { - WorldPacket data(SMSG_SUPERCEDED_SPELL, 4 + 4); - data << uint32(itr2->first); - data << uint32(spell_id); - GetSession()->SendPacket(&data); - } - - // mark old spell as disable (SMSG_SUPERCEDED_SPELL replace it in client by new) - itr2->second.active = false; - if (itr2->second.state != PLAYERSPELL_NEW) - itr2->second.state = PLAYERSPELL_CHANGED; - superceded_old = true; // new spell replace old in action bars and spell book. - } - else if (sSpellMgr.IsHighRankOfSpell(itr2->first, spell_id)) - { - if (IsInWorld()) // not send spell (re-/over-)learn packets at loading - { - WorldPacket data(SMSG_SUPERCEDED_SPELL, 4 + 4); - data << uint32(spell_id); - data << uint32(itr2->first); - GetSession()->SendPacket(&data); - } - - // mark new spell as disable (not learned yet for client and will not learned) - newspell.active = false; - if (newspell.state != PLAYERSPELL_NEW) - newspell.state = PLAYERSPELL_CHANGED; - } - } - } - } - } - - m_spells[spell_id] = newspell; - - // return false if spell disabled - if (newspell.disabled) - return false; - } - - if (talentPos) - { - // update talent map - PlayerTalentMap::iterator iter = m_talents[m_activeSpec].find(talentPos->talent_id); - if (iter != m_talents[m_activeSpec].end()) - { - // check if ranks different or removed - if ((*iter).second.state == PLAYERSPELL_REMOVED || talentPos->rank != (*iter).second.currentRank) - { - (*iter).second.currentRank = talentPos->rank; - - if ((*iter).second.state != PLAYERSPELL_NEW) - (*iter).second.state = PLAYERSPELL_CHANGED; - } - } - else - { - PlayerTalent talent; - talent.currentRank = talentPos->rank; - talent.talentEntry = sTalentStore.LookupEntry(talentPos->talent_id); - talent.state = IsInWorld() ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED; - m_talents[m_activeSpec][talentPos->talent_id] = talent; - } - - // update used talent points count - m_usedTalentCount += GetTalentSpellCost(talentPos); - UpdateFreeTalentPoints(false); - } - - // update free primary prof.points (if any, can be none in case GM .learn prof. learning) - if (uint32 freeProfs = GetFreePrimaryProfessionPoints()) - { - if (sSpellMgr.IsPrimaryProfessionFirstRankSpell(spell_id)) - SetFreePrimaryProfessions(freeProfs - 1); - } - - // cast talents with SPELL_EFFECT_LEARN_SPELL (other dependent spells will learned later as not auto-learned) - // note: all spells with SPELL_EFFECT_LEARN_SPELL isn't passive - if (talentPos && IsSpellHaveEffect(spellInfo, SPELL_EFFECT_LEARN_SPELL)) - { - // ignore stance requirement for talent learn spell (stance set for spell only for client spell description show) - CastSpell(this, spell_id, true); - } - // also cast passive (and passive like) spells (including all talents without SPELL_EFFECT_LEARN_SPELL) with additional checks - else if (IsNeedCastPassiveLikeSpellAtLearn(spellInfo)) - { - CastSpell(this, spell_id, true); - } - else if (IsSpellHaveEffect(spellInfo, SPELL_EFFECT_SKILL_STEP)) - { - CastSpell(this, spell_id, true); - return false; - } - - // add dependent skills - uint16 maxskill = GetMaxSkillValueForLevel(); - - SpellLearnSkillNode const* spellLearnSkill = sSpellMgr.GetSpellLearnSkill(spell_id); - - SkillLineAbilityMapBounds skill_bounds = sSpellMgr.GetSkillLineAbilityMapBounds(spell_id); - - if (spellLearnSkill) - { - uint32 skill_value = GetPureSkillValue(spellLearnSkill->skill); - uint32 skill_max_value = GetPureMaxSkillValue(spellLearnSkill->skill); - - if (skill_value < spellLearnSkill->value) - skill_value = spellLearnSkill->value; - - uint32 new_skill_max_value = spellLearnSkill->maxvalue == 0 ? maxskill : spellLearnSkill->maxvalue; - - if (skill_max_value < new_skill_max_value) - skill_max_value = new_skill_max_value; - - SetSkill(spellLearnSkill->skill, skill_value, skill_max_value, spellLearnSkill->step); - } - else - { - // not ranked skills - for (SkillLineAbilityMap::const_iterator _spell_idx = skill_bounds.first; _spell_idx != skill_bounds.second; ++_spell_idx) - { - SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->skillId); - if (!pSkill) - continue; - - if (HasSkill(pSkill->id)) - continue; - - if (_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL || - // lockpicking/runeforging special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL - (pSkill->id == SKILL_RUNEFORGING && _spell_idx->second->max_value == 0)) - { - switch (GetSkillRangeType(pSkill, _spell_idx->second->racemask != 0)) - { - case SKILL_RANGE_LANGUAGE: - SetSkill(pSkill->id, 300, 300, GetSkillStep(pSkill->id)); - break; - case SKILL_RANGE_LEVEL: - SetSkill(pSkill->id, 1, GetMaxSkillValueForLevel(), GetSkillStep(pSkill->id)); - break; - case SKILL_RANGE_MONO: - SetSkill(pSkill->id, 1, 1, GetSkillStep(pSkill->id)); - break; - default: - break; - } - } - } - } - - // learn dependent spells - SpellLearnSpellMapBounds spell_bounds = sSpellMgr.GetSpellLearnSpellMapBounds(spell_id); - - for (SpellLearnSpellMap::const_iterator itr2 = spell_bounds.first; itr2 != spell_bounds.second; ++itr2) - { - if (!itr2->second.autoLearned) - { - if (!IsInWorld() || !itr2->second.active) // at spells loading, no output, but allow save - addSpell(itr2->second.spell, itr2->second.active, true, true, false); - else // at normal learning - learnSpell(itr2->second.spell, true); - } - } - - if (!GetSession()->PlayerLoading()) - { - // not ranked skills - for (SkillLineAbilityMap::const_iterator _spell_idx = skill_bounds.first; _spell_idx != skill_bounds.second; ++_spell_idx) - { - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE, _spell_idx->second->skillId); - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS, _spell_idx->second->skillId); - } - - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL, spell_id); - } - - // return true (for send learn packet) only if spell active (in case ranked spells) and not replace old spell - return active && !disabled && !superceded_old; -} - -bool Player::IsNeedCastPassiveLikeSpellAtLearn(SpellEntry const* spellInfo) const -{ - ShapeshiftForm form = GetShapeshiftForm(); - - if (IsNeedCastSpellAtFormApply(spellInfo, form)) // SPELL_ATTR_PASSIVE | SPELL_ATTR_UNK7 spells - return true; // all stance req. cases, not have auarastate cases - - if (!spellInfo->HasAttribute(SPELL_ATTR_PASSIVE)) - return false; - - // note: form passives activated with shapeshift spells be implemented by HandleShapeshiftBoosts instead of spell_learn_spell - // talent dependent passives activated at form apply have proper stance data - SpellShapeshiftEntry const* shapeShift = spellInfo->GetSpellShapeshift(); - bool need_cast = (!shapeShift || !shapeShift->Stances || (!form && spellInfo->HasAttribute(SPELL_ATTR_EX2_NOT_NEED_SHAPESHIFT))); - - // Check CasterAuraStates - SpellAuraRestrictionsEntry const* auraRestrictions = spellInfo->GetSpellAuraRestrictions(); - return need_cast && (!auraRestrictions || !auraRestrictions->CasterAuraState || HasAuraState(AuraState(auraRestrictions->CasterAuraState))); -} - -void Player::learnSpell(uint32 spell_id, bool dependent) -{ - PlayerSpellMap::iterator itr = m_spells.find(spell_id); - - bool disabled = (itr != m_spells.end()) ? itr->second.disabled : false; - bool active = disabled ? itr->second.active : true; - - bool learning = addSpell(spell_id, active, true, dependent, false); - - // prevent duplicated entires in spell book, also not send if not in world (loading) - if (learning && IsInWorld()) - { - WorldPacket data(SMSG_LEARNED_SPELL, 6); - data << uint32(spell_id); - data << uint32(0); // 3.3.3 unk - GetSession()->SendPacket(&data); - } - - // learn all disabled higher ranks (recursive) - if (disabled) - { - SpellChainMapNext const& nextMap = sSpellMgr.GetSpellChainNext(); - for (SpellChainMapNext::const_iterator i = nextMap.lower_bound(spell_id); i != nextMap.upper_bound(spell_id); ++i) - { - PlayerSpellMap::iterator iter = m_spells.find(i->second); - if (iter != m_spells.end() && iter->second.disabled) - learnSpell(i->second, false); - } - } - - if (IsInWorld()) - SpellAddedQuestCheck(spell_id); -} - -void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank, bool sendUpdate) -{ - PlayerSpellMap::iterator itr = m_spells.find(spell_id); - if (itr == m_spells.end()) - return; - - if (itr->second.state == PLAYERSPELL_REMOVED || (disabled && itr->second.disabled)) - return; - - // unlearn non talent higher ranks (recursive) - SpellChainMapNext const& nextMap = sSpellMgr.GetSpellChainNext(); - for (SpellChainMapNext::const_iterator itr2 = nextMap.lower_bound(spell_id); itr2 != nextMap.upper_bound(spell_id); ++itr2) - if (HasSpell(itr2->second) && !GetTalentSpellPos(itr2->second)) - removeSpell(itr2->second, disabled, false); - - // re-search, it can be corrupted in prev loop - itr = m_spells.find(spell_id); - if (itr == m_spells.end() || itr->second.state == PLAYERSPELL_REMOVED) - return; // already unleared - - bool cur_active = itr->second.active; - bool cur_dependent = itr->second.dependent; - - if (disabled) - { - itr->second.disabled = disabled; - if (itr->second.state != PLAYERSPELL_NEW) - itr->second.state = PLAYERSPELL_CHANGED; - } - else - { - if (itr->second.state == PLAYERSPELL_NEW) - m_spells.erase(itr); - else - itr->second.state = PLAYERSPELL_REMOVED; - } - - RemoveAurasDueToSpell(spell_id); - - // remove pet auras - for (int i = 0; i < MAX_EFFECT_INDEX; ++i) - if (PetAura const* petSpell = sSpellMgr.GetPetAura(spell_id, SpellEffectIndex(i))) - RemovePetAura(petSpell); - - TalentSpellPos const* talentPos = GetTalentSpellPos(spell_id); - if (talentPos) - { - // update talent map - PlayerTalentMap::iterator iter = m_talents[m_activeSpec].find(talentPos->talent_id); - if (iter != m_talents[m_activeSpec].end()) - { - if ((*iter).second.state != PLAYERSPELL_NEW) - (*iter).second.state = PLAYERSPELL_REMOVED; - else - m_talents[m_activeSpec].erase(iter); - } - else - sLog.outError("removeSpell: Player (GUID: %u) has talent spell (id: %u) but doesn't have talent", GetGUIDLow(), spell_id); - - // free talent points - uint32 talentCosts = GetTalentSpellCost(talentPos); - - if (talentCosts < m_usedTalentCount) - m_usedTalentCount -= talentCosts; - else - m_usedTalentCount = 0; - - UpdateFreeTalentPoints(false); - } - - // update free primary prof.points (if not overflow setting, can be in case GM use before .learn prof. learning) - if (sSpellMgr.IsPrimaryProfessionFirstRankSpell(spell_id)) - { - uint32 freeProfs = GetFreePrimaryProfessionPoints() + 1; - uint32 maxProfs = GetSession()->GetSecurity() < AccountTypes(sWorld.getConfig(CONFIG_UINT32_TRADE_SKILL_GMIGNORE_MAX_PRIMARY_COUNT)) ? sWorld.getConfig(CONFIG_UINT32_MAX_PRIMARY_TRADE_SKILL) : 10; - if (freeProfs <= maxProfs) - SetFreePrimaryProfessions(freeProfs); - } - - // remove dependent skill - SpellLearnSkillNode const* spellLearnSkill = sSpellMgr.GetSpellLearnSkill(spell_id); - if (spellLearnSkill) - { - uint32 prev_spell = sSpellMgr.GetPrevSpellInChain(spell_id); - if (!prev_spell) // first rank, remove skill - SetSkill(spellLearnSkill->skill, 0, 0); - else - { - // search prev. skill setting by spell ranks chain - SpellLearnSkillNode const* prevSkill = sSpellMgr.GetSpellLearnSkill(prev_spell); - while (!prevSkill && prev_spell) - { - prev_spell = sSpellMgr.GetPrevSpellInChain(prev_spell); - prevSkill = sSpellMgr.GetSpellLearnSkill(sSpellMgr.GetFirstSpellInChain(prev_spell)); - } - - if (!prevSkill) // not found prev skill setting, remove skill - SetSkill(spellLearnSkill->skill, 0, 0); - else // set to prev. skill setting values - { - uint32 skill_value = GetPureSkillValue(prevSkill->skill); - uint32 skill_max_value = GetPureMaxSkillValue(prevSkill->skill); - - if (skill_value > prevSkill->value) - skill_value = prevSkill->value; - - uint32 new_skill_max_value = prevSkill->maxvalue == 0 ? GetMaxSkillValueForLevel() : prevSkill->maxvalue; - - if (skill_max_value > new_skill_max_value) - skill_max_value = new_skill_max_value; - - SetSkill(prevSkill->skill, skill_value, skill_max_value, prevSkill->step); - } - } - } - else - { - // not ranked skills - SkillLineAbilityMapBounds bounds = sSpellMgr.GetSkillLineAbilityMapBounds(spell_id); - - for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx) - { - SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->skillId); - if (!pSkill) - continue; - - if ((_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL && - pSkill->categoryId != SKILL_CATEGORY_CLASS) ||// not unlearn class skills (spellbook/talent pages) - // lockpicking/runeforging special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL - (pSkill->id == SKILL_RUNEFORGING && _spell_idx->second->max_value == 0)) - { - // not reset skills for professions and racial abilities - if ((pSkill->categoryId == SKILL_CATEGORY_SECONDARY || pSkill->categoryId == SKILL_CATEGORY_PROFESSION) && - (IsProfessionSkill(pSkill->id) || _spell_idx->second->racemask != 0)) - continue; - - SetSkill(pSkill->id, 0, 0, GetSkillStep(pSkill->id)); - } - } - } - - // remove dependent spells - SpellLearnSpellMapBounds spell_bounds = sSpellMgr.GetSpellLearnSpellMapBounds(spell_id); - - for (SpellLearnSpellMap::const_iterator itr2 = spell_bounds.first; itr2 != spell_bounds.second; ++itr2) - removeSpell(itr2->second.spell, disabled); - - // activate lesser rank in spellbook/action bar, and cast it if need - bool prev_activate = false; - - if (uint32 prev_id = sSpellMgr.GetPrevSpellInChain(spell_id)) - { - SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell_id); - - // if talent then lesser rank also talent and need learn - if (talentPos) - { - if (learn_low_rank) - learnSpell(prev_id, false); - } - // if ranked non-stackable spell: need activate lesser rank and update dependence state - else if (cur_active && sSpellMgr.IsRankedSpellNonStackableInSpellBook(spellInfo)) - { - // need manually update dependence state (learn spell ignore like attempts) - PlayerSpellMap::iterator prev_itr = m_spells.find(prev_id); - if (prev_itr != m_spells.end()) - { - if (prev_itr->second.dependent != cur_dependent) - { - prev_itr->second.dependent = cur_dependent; - if (prev_itr->second.state != PLAYERSPELL_NEW) - prev_itr->second.state = PLAYERSPELL_CHANGED; - } - - // now re-learn if need re-activate - if (cur_active && !prev_itr->second.active && learn_low_rank) - { - if (addSpell(prev_id, true, false, prev_itr->second.dependent, prev_itr->second.disabled)) - { - // downgrade spell ranks in spellbook and action bar - WorldPacket data(SMSG_SUPERCEDED_SPELL, 4 + 4); - data << uint32(spell_id); - data << uint32(prev_id); - GetSession()->SendPacket(&data); - prev_activate = true; - } - } - } - } - } - - // for Titan's Grip and shaman Dual-wield - if (CanDualWield() || CanTitanGrip()) - { - SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell_id); - - if (CanDualWield() && IsSpellHaveEffect(spellInfo, SPELL_EFFECT_DUAL_WIELD)) - SetCanDualWield(false); - - if (CanTitanGrip() && IsSpellHaveEffect(spellInfo, SPELL_EFFECT_TITAN_GRIP)) - { - SetCanTitanGrip(false); - // Remove Titan's Grip damage penalty now - RemoveAurasDueToSpell(49152); - } - } - - // for talents and normal spell unlearn that allow offhand use for some weapons - if (sWorld.getConfig(CONFIG_BOOL_OFFHAND_CHECK_AT_TALENTS_RESET)) - AutoUnequipOffhandIfNeed(); - - // remove from spell book if not replaced by lesser rank - if (!prev_activate && sendUpdate) - { - WorldPacket data(SMSG_REMOVED_SPELL, 4); - data << uint32(spell_id); - GetSession()->SendPacket(&data); - } - - if (IsInWorld()) - SpellRemovedQuestCheck(spell_id); -} - -void Player::RemoveSpellCooldown(uint32 spell_id, bool update /* = false */) -{ - m_spellCooldowns.erase(spell_id); - - if (update) - SendClearCooldown(spell_id, this); -} - -void Player::RemoveSpellCategoryCooldown(uint32 cat, bool update /* = false */) -{ - SpellCategoryStore::const_iterator ct = sSpellCategoryStore.find(cat); - if (ct == sSpellCategoryStore.end()) - return; - - const SpellCategorySet& ct_set = ct->second; - for (SpellCooldowns::const_iterator i = m_spellCooldowns.begin(); i != m_spellCooldowns.end();) - { - if (ct_set.find(i->first) != ct_set.end()) - RemoveSpellCooldown((i++)->first, update); - else - ++i; - } -} - -void Player::RemoveArenaSpellCooldowns() -{ - // remove cooldowns on spells that has < 15 min CD - SpellCooldowns::iterator itr, next; - // iterate spell cooldowns - for (itr = m_spellCooldowns.begin(); itr != m_spellCooldowns.end(); itr = next) - { - next = itr; - ++next; - SpellEntry const* entry = sSpellStore.LookupEntry(itr->first); - // check if spellentry is present and if the cooldown is less than 15 mins - if( entry && - entry->GetRecoveryTime() <= 15 * MINUTE * IN_MILLISECONDS && - entry->GetCategoryRecoveryTime() <= 15 * MINUTE * IN_MILLISECONDS ) - { - // remove & notify - RemoveSpellCooldown(itr->first, true); - } - } -} - -void Player::RemoveAllSpellCooldown() -{ - if (!m_spellCooldowns.empty()) - { - ObjectGuid guid = GetObjectGuid(); - - WorldPacket data(SMSG_CLEAR_COOLDOWNS, 1 + 8 + m_spellCooldowns.size() * 4); - data.WriteGuidMask<1, 3, 6>(guid); - data.WriteBits(m_spellCooldowns.size(), 24); // cooldown count - data.WriteGuidMask<7, 5, 2, 4, 0>(guid); - - data.WriteGuidBytes<7, 2, 4, 5, 1, 3>(guid); - - for (SpellCooldowns::const_iterator itr = m_spellCooldowns.begin(); itr != m_spellCooldowns.end(); ++itr) - data << uint32(itr->first); - - data.WriteGuidBytes<0, 6>(guid); - - SendDirectMessage(&data); - - m_spellCooldowns.clear(); - } -} - -void Player::_LoadSpellCooldowns(QueryResult* result) -{ - // some cooldowns can be already set at aura loading... - - // QueryResult *result = CharacterDatabase.PQuery("SELECT spell,item,time FROM character_spell_cooldown WHERE guid = '%u'",GetGUIDLow()); - - if (result) - { - time_t curTime = time(NULL); - - do - { - Field* fields = result->Fetch(); - - uint32 spell_id = fields[0].GetUInt32(); - uint32 item_id = fields[1].GetUInt32(); - time_t db_time = (time_t)fields[2].GetUInt64(); - - if (!sSpellStore.LookupEntry(spell_id)) - { - sLog.outError("Player %u has unknown spell %u in `character_spell_cooldown`, skipping.", GetGUIDLow(), spell_id); - continue; - } - - // skip outdated cooldown - if (db_time <= curTime) - continue; - - AddSpellCooldown(spell_id, item_id, db_time); - - DEBUG_LOG("Player (GUID: %u) spell %u, item %u cooldown loaded (%u secs).", GetGUIDLow(), spell_id, item_id, uint32(db_time - curTime)); - } - while (result->NextRow()); - - delete result; - } -} - -void Player::_SaveSpellCooldowns() -{ - static SqlStatementID deleteSpellCooldown ; - static SqlStatementID insertSpellCooldown ; - - SqlStatement stmt = CharacterDatabase.CreateStatement(deleteSpellCooldown, "DELETE FROM character_spell_cooldown WHERE guid = ?"); - stmt.PExecute(GetGUIDLow()); - - time_t curTime = time(NULL); - time_t infTime = curTime + infinityCooldownDelayCheck; - - // remove outdated and save active - for (SpellCooldowns::iterator itr = m_spellCooldowns.begin(); itr != m_spellCooldowns.end();) - { - if (itr->second.end <= curTime) - m_spellCooldowns.erase(itr++); - else if (itr->second.end <= infTime) // not save locked cooldowns, it will be reset or set at reload - { - stmt = CharacterDatabase.CreateStatement(insertSpellCooldown, "INSERT INTO character_spell_cooldown (guid,spell,item,time) VALUES( ?, ?, ?, ?)"); - stmt.PExecute(GetGUIDLow(), itr->first, itr->second.itemid, uint64(itr->second.end)); - ++itr; - } - else - ++itr; - } -} - -uint32 Player::resetTalentsCost() const -{ - // The first time reset costs 1 gold - if (m_resetTalentsCost < 1 * GOLD) - return 1 * GOLD; - // then 5 gold - else if (m_resetTalentsCost < 5 * GOLD) - return 5 * GOLD; - // After that it increases in increments of 5 gold - else if (m_resetTalentsCost < 10 * GOLD) - return 10 * GOLD; - else - { - time_t months = (sWorld.GetGameTime() - m_resetTalentsTime) / MONTH; - if (months > 0) - { - // This cost will be reduced by a rate of 5 gold per month - int32 new_cost = int32((m_resetTalentsCost) - 5 * GOLD * months); - // to a minimum of 10 gold. - return uint32(new_cost < 10 * GOLD ? 10 * GOLD : new_cost); - } - else - { - // After that it increases in increments of 5 gold - int32 new_cost = m_resetTalentsCost + 5 * GOLD; - // until it hits a cap of 50 gold. - if (new_cost > 50 * GOLD) - new_cost = 50 * GOLD; - return new_cost; - } - } -} - -bool Player::resetTalents(bool no_cost, bool all_specs) -{ - // not need after this call - if (HasAtLoginFlag(AT_LOGIN_RESET_TALENTS) && all_specs) - RemoveAtLoginFlag(AT_LOGIN_RESET_TALENTS, true); - - if (m_usedTalentCount == 0 && !all_specs) - { - UpdateFreeTalentPoints(false); // for fix if need counter - return false; - } - - uint32 cost = 0; - - if (!no_cost) - { - cost = resetTalentsCost(); - - if (GetMoney() < cost) - { - SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0); - return false; - } - } - - for (PlayerTalentMap::iterator iter = m_talents[m_activeSpec].begin(); iter != m_talents[m_activeSpec].end();) - { - if (iter->second.state == PLAYERSPELL_REMOVED) - { - ++iter; - continue; - } - - TalentEntry const* talentInfo = iter->second.talentEntry; - if (!talentInfo) - { - m_talents[m_activeSpec].erase(iter++); - continue; - } - - TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab); - - if (!talentTabInfo) - { - m_talents[m_activeSpec].erase(iter++); - continue; - } - - // unlearn only talents for character class - // some spell learned by one class as normal spells or know at creation but another class learn it as talent, - // to prevent unexpected lost normal learned spell skip another class talents - if ((getClassMask() & talentTabInfo->ClassMask) == 0) - { - ++iter; - continue; - } - - for (int j = 0; j < MAX_TALENT_RANK; ++j) - if (talentInfo->RankID[j]) - removeSpell(talentInfo->RankID[j], !IsPassiveSpell(talentInfo->RankID[j]), false); - - iter = m_talents[m_activeSpec].begin(); - } - - // Remove spec specific spells - for (uint32 i = 0; i < MAX_TALENT_TABS; ++i) - { - if (std::vector const* specSpells = GetTalentTreeMasterySpells(GetTalentTabPages(getClass())[i])) - for (size_t i = 0; i < specSpells->size(); ++i) - removeSpell(specSpells->at(i), true); - - if (std::vector const* specSpells = GetTalentTreePrimarySpells(GetTalentTabPages(getClass())[i])) - for (size_t i = 0; i < specSpells->size(); ++i) - removeSpell(specSpells->at(i), true); - } - - for (uint8 spec = 0; spec < MAX_TALENT_SPEC_COUNT; ++spec) - { - if (!all_specs && spec != m_activeSpec) - continue; - - m_talentsPrimaryTree[spec] = 0; - } - - // for not current spec just mark removed all saved to DB case and drop not saved - if (all_specs) - { - for (uint8 spec = 0; spec < MAX_TALENT_SPEC_COUNT; ++spec) - { - if (spec == m_activeSpec) - continue; - - for (PlayerTalentMap::iterator iter = m_talents[spec].begin(); iter != m_talents[spec].end();) - { - switch (iter->second.state) - { - case PLAYERSPELL_REMOVED: - ++iter; - break; - case PLAYERSPELL_NEW: - m_talents[spec].erase(iter++); - break; - default: - iter->second.state = PLAYERSPELL_REMOVED; - ++iter; - break; - } - } - } - } - - UpdateFreeTalentPoints(false); - - if (!no_cost) - { - ModifyMoney(-(int64)cost); - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS, cost); - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS, 1); - - m_resetTalentsCost = cost; - m_resetTalentsTime = time(NULL); - } - - // Update talent tree role-dependent mana regen - UpdateManaRegen(); - - UpdateArmorSpecializations(); - - // FIXME: remove pet before or after unlearn spells? for now after unlearn to allow removing of talent related, pet affecting auras - RemovePet(PET_SAVE_REAGENTS); - /* when prev line will dropped use next line - if(Pet* pet = GetPet()) - { - if(pet->getPetType()==HUNTER_PET && !pet->GetCreatureInfo()->isTameable(CanTameExoticPets())) - pet->Unsummon(PET_SAVE_REAGENTS, this); - } - */ - return true; -} - -Mail* Player::GetMail(uint32 id) -{ - for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr) - { - if ((*itr)->messageID == id) - { - return (*itr); - } - } - return NULL; -} - -void Player::_SetCreateBits(UpdateMask* updateMask, Player* target) const -{ - if (target == this) - { - Object::_SetCreateBits(updateMask, target); - } - else - { - for (uint16 index = 0; index < m_valuesCount; ++index) - { - if (GetUInt32Value(index) != 0 && updateVisualBits.GetBit(index)) - updateMask->SetBit(index); - } - } -} - -void Player::_SetUpdateBits(UpdateMask* updateMask, Player* target) const -{ - if (target == this) - { - Object::_SetUpdateBits(updateMask, target); - } - else - { - Object::_SetUpdateBits(updateMask, target); - *updateMask &= updateVisualBits; - } -} - -void Player::InitVisibleBits() -{ - updateVisualBits.SetCount(PLAYER_END); - - updateVisualBits.SetBit(OBJECT_FIELD_GUID + 0); - updateVisualBits.SetBit(OBJECT_FIELD_GUID + 1); - updateVisualBits.SetBit(OBJECT_FIELD_TYPE); - updateVisualBits.SetBit(OBJECT_FIELD_ENTRY); - updateVisualBits.SetBit(OBJECT_FIELD_DATA + 0); - updateVisualBits.SetBit(OBJECT_FIELD_DATA + 1); - updateVisualBits.SetBit(OBJECT_FIELD_SCALE_X); - updateVisualBits.SetBit(UNIT_FIELD_CHARM + 0); - updateVisualBits.SetBit(UNIT_FIELD_CHARM + 1); - updateVisualBits.SetBit(UNIT_FIELD_SUMMON + 0); - updateVisualBits.SetBit(UNIT_FIELD_SUMMON + 1); - updateVisualBits.SetBit(UNIT_FIELD_CHARMEDBY + 0); - updateVisualBits.SetBit(UNIT_FIELD_CHARMEDBY + 1); - updateVisualBits.SetBit(UNIT_FIELD_TARGET + 0); - updateVisualBits.SetBit(UNIT_FIELD_TARGET + 1); - updateVisualBits.SetBit(UNIT_FIELD_CHANNEL_OBJECT + 0); - updateVisualBits.SetBit(UNIT_FIELD_CHANNEL_OBJECT + 1); - updateVisualBits.SetBit(UNIT_FIELD_BYTES_0); - updateVisualBits.SetBit(UNIT_FIELD_HEALTH); - updateVisualBits.SetBit(UNIT_FIELD_POWER1); - updateVisualBits.SetBit(UNIT_FIELD_POWER2); - updateVisualBits.SetBit(UNIT_FIELD_POWER3); - updateVisualBits.SetBit(UNIT_FIELD_POWER4); - updateVisualBits.SetBit(UNIT_FIELD_POWER5); - updateVisualBits.SetBit(UNIT_FIELD_MAXHEALTH); - updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER1); - updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER2); - updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER3); - updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER4); - updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER5); - updateVisualBits.SetBit(UNIT_FIELD_LEVEL); - updateVisualBits.SetBit(UNIT_FIELD_FACTIONTEMPLATE); - updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_SLOT_ID + 0); - updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_SLOT_ID + 1); - updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_SLOT_ID + 2); - updateVisualBits.SetBit(UNIT_FIELD_FLAGS); - updateVisualBits.SetBit(UNIT_FIELD_FLAGS_2); - updateVisualBits.SetBit(UNIT_FIELD_AURASTATE); - updateVisualBits.SetBit(UNIT_FIELD_BASEATTACKTIME + 0); - updateVisualBits.SetBit(UNIT_FIELD_BASEATTACKTIME + 1); - updateVisualBits.SetBit(UNIT_FIELD_BOUNDINGRADIUS); - updateVisualBits.SetBit(UNIT_FIELD_COMBATREACH); - updateVisualBits.SetBit(UNIT_FIELD_DISPLAYID); - updateVisualBits.SetBit(UNIT_FIELD_NATIVEDISPLAYID); - updateVisualBits.SetBit(UNIT_FIELD_MOUNTDISPLAYID); - updateVisualBits.SetBit(UNIT_FIELD_BYTES_1); - updateVisualBits.SetBit(UNIT_FIELD_PETNUMBER); - updateVisualBits.SetBit(UNIT_FIELD_PET_NAME_TIMESTAMP); - updateVisualBits.SetBit(UNIT_DYNAMIC_FLAGS); - updateVisualBits.SetBit(UNIT_CHANNEL_SPELL); - updateVisualBits.SetBit(UNIT_MOD_CAST_SPEED); - updateVisualBits.SetBit(UNIT_NPC_FLAGS); - updateVisualBits.SetBit(UNIT_FIELD_BASE_MANA); - updateVisualBits.SetBit(UNIT_FIELD_BYTES_2); - updateVisualBits.SetBit(UNIT_FIELD_HOVERHEIGHT); - - updateVisualBits.SetBit(PLAYER_DUEL_ARBITER + 0); - updateVisualBits.SetBit(PLAYER_DUEL_ARBITER + 1); - updateVisualBits.SetBit(PLAYER_FLAGS); - //updateVisualBits.SetBit(PLAYER_GUILDID); - updateVisualBits.SetBit(PLAYER_GUILDRANK); - updateVisualBits.SetBit(PLAYER_GUILDLEVEL); - updateVisualBits.SetBit(PLAYER_BYTES); - updateVisualBits.SetBit(PLAYER_BYTES_2); - updateVisualBits.SetBit(PLAYER_BYTES_3); - updateVisualBits.SetBit(PLAYER_DUEL_TEAM); - updateVisualBits.SetBit(PLAYER_GUILD_TIMESTAMP); - updateVisualBits.SetBit(UNIT_NPC_FLAGS); - - // PLAYER_QUEST_LOG_x also visible bit on official (but only on party/raid)... - for (uint16 i = PLAYER_QUEST_LOG_1_1; i < PLAYER_QUEST_LOG_25_2; i += MAX_QUEST_OFFSET) - updateVisualBits.SetBit(i); - - // Players visible items are not inventory stuff - for (uint16 i = 0; i < EQUIPMENT_SLOT_END; ++i) - { - uint32 offset = i * 2; - - // item entry - updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_ENTRYID + offset); - // enchant - updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + offset); - } - - updateVisualBits.SetBit(PLAYER_CHOSEN_TITLE); -} - -void Player::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const -{ - if (target == this) - { - for (int i = 0; i < EQUIPMENT_SLOT_END; ++i) - { - if (m_items[i] == NULL) - continue; - - m_items[i]->BuildCreateUpdateBlockForPlayer(data, target); - } - for (int i = INVENTORY_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i) - { - if (m_items[i] == NULL) - continue; - - m_items[i]->BuildCreateUpdateBlockForPlayer(data, target); - } - } - - Unit::BuildCreateUpdateBlockForPlayer(data, target); -} - -void Player::DestroyForPlayer(Player* target, bool anim) const -{ - Unit::DestroyForPlayer(target, anim); - - for (int i = 0; i < INVENTORY_SLOT_BAG_END; ++i) - { - if (m_items[i] == NULL) - continue; - - m_items[i]->DestroyForPlayer(target); - } - - if (target == this) - { - for (int i = INVENTORY_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i) - { - if (m_items[i] == NULL) - continue; - - m_items[i]->DestroyForPlayer(target); - } - } -} - -bool Player::HasSpell(uint32 spell) const -{ - PlayerSpellMap::const_iterator itr = m_spells.find(spell); - return (itr != m_spells.end() && itr->second.state != PLAYERSPELL_REMOVED && - !itr->second.disabled); -} - -bool Player::HasActiveSpell(uint32 spell) const -{ - PlayerSpellMap::const_iterator itr = m_spells.find(spell); - return (itr != m_spells.end() && itr->second.state != PLAYERSPELL_REMOVED && - itr->second.active && !itr->second.disabled); -} - -TrainerSpellState Player::GetTrainerSpellState(TrainerSpell const* trainer_spell, uint32 reqLevel) const -{ - if (!trainer_spell) - return TRAINER_SPELL_RED; - - if (!trainer_spell->learnedSpell) - return TRAINER_SPELL_RED; - - // known spell - if (HasSpell(trainer_spell->learnedSpell)) - return TRAINER_SPELL_GRAY; - - // check race/class requirement - if (!IsSpellFitByClassAndRace(trainer_spell->learnedSpell)) - return TRAINER_SPELL_RED; - - bool prof = SpellMgr::IsProfessionSpell(trainer_spell->learnedSpell); - - // check level requirement - if (!prof || GetSession()->GetSecurity() < AccountTypes(sWorld.getConfig(CONFIG_UINT32_TRADE_SKILL_GMIGNORE_LEVEL))) - if (getLevel() < reqLevel) - return TRAINER_SPELL_RED; - - if (SpellChainNode const* spell_chain = sSpellMgr.GetSpellChainNode(trainer_spell->learnedSpell)) - { - // check prev.rank requirement - if (spell_chain->prev && !HasSpell(spell_chain->prev)) - return TRAINER_SPELL_RED; - - // check additional spell requirement - if (spell_chain->req && !HasSpell(spell_chain->req)) - return TRAINER_SPELL_RED; - } - - // check skill requirement - if (!prof || GetSession()->GetSecurity() < AccountTypes(sWorld.getConfig(CONFIG_UINT32_TRADE_SKILL_GMIGNORE_SKILL))) - if (trainer_spell->reqSkill && GetBaseSkillValue(trainer_spell->reqSkill) < trainer_spell->reqSkillValue) - return TRAINER_SPELL_RED; - - // exist, already checked at loading - SpellEntry const* spell = sSpellStore.LookupEntry(trainer_spell->learnedSpell); - - // secondary prof. or not prof. spell - SpellEffectEntry const* spellEffect = spell->GetSpellEffect(EFFECT_INDEX_1); - uint32 skill = spellEffect ? spellEffect->EffectMiscValue : 0; - - if(spellEffect && (spellEffect->Effect != SPELL_EFFECT_SKILL || !IsPrimaryProfessionSkill(skill))) - return TRAINER_SPELL_GREEN; - - // check primary prof. limit - if (sSpellMgr.IsPrimaryProfessionFirstRankSpell(spell->Id) && GetFreePrimaryProfessionPoints() == 0) - return TRAINER_SPELL_GREEN_DISABLED; - - return TRAINER_SPELL_GREEN; -} - -/* - * Deletes a character from the database - * - * The way, how the characters will be deleted is decided based on the config option. - * - * @see Player::DeleteOldCharacters - * - * @param playerguid the low-GUID from the player which should be deleted - * @param accountId the account id from the player - * @param updateRealmChars when this flag is set, the amount of characters on that realm will be updated in the realmlist - * @param deleteFinally if this flag is set, the config option will be ignored and the character will be permanently removed from the database - */ -void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRealmChars, bool deleteFinally) -{ - // for nonexistent account avoid update realm - if (accountId == 0) - updateRealmChars = false; - - uint32 charDelete_method = sWorld.getConfig(CONFIG_UINT32_CHARDELETE_METHOD); - uint32 charDelete_minLvl = sWorld.getConfig(CONFIG_UINT32_CHARDELETE_MIN_LEVEL); - - // if we want to finally delete the character or the character does not meet the level requirement, we set it to mode 0 - if (deleteFinally || Player::GetLevelFromDB(playerguid) < charDelete_minLvl) - charDelete_method = 0; - - uint32 lowguid = playerguid.GetCounter(); - - // convert corpse to bones if exist (to prevent exiting Corpse in World without DB entry) - // bones will be deleted by corpse/bones deleting thread shortly - sObjectAccessor.ConvertCorpseForPlayer(playerguid); - - // remove from guild - if (uint32 guildId = GetGuildIdFromDB(playerguid)) - { - if (Guild* guild = sGuildMgr.GetGuildById(guildId)) - { - if (guild->DelMember(playerguid)) - { - guild->Disband(); - delete guild; - } - } - } - - // remove from arena teams - LeaveAllArenaTeams(playerguid); - - // the player was uninvited already on logout so just remove from group - QueryResult* resultGroup = CharacterDatabase.PQuery("SELECT groupId FROM group_member WHERE memberGuid='%u'", lowguid); - if (resultGroup) - { - uint32 groupId = (*resultGroup)[0].GetUInt32(); - delete resultGroup; - if (Group* group = sObjectMgr.GetGroupById(groupId)) - RemoveFromGroup(group, playerguid); - } - - // remove signs from petitions (also remove petitions if owner); - RemovePetitionsAndSigns(playerguid); - - switch (charDelete_method) - { - // completely remove from the database - case 0: - { - // return back all mails with COD and Item 0 1 2 3 4 5 6 7 - QueryResult* resultMail = CharacterDatabase.PQuery("SELECT id,messageType,mailTemplateId,sender,subject,body,money,has_items FROM mail WHERE receiver='%u' AND has_items<>0 AND cod<>0", lowguid); - if (resultMail) - { - do - { - Field* fields = resultMail->Fetch(); - - uint32 mail_id = fields[0].GetUInt32(); - uint16 mailType = fields[1].GetUInt16(); - uint16 mailTemplateId = fields[2].GetUInt16(); - uint32 sender = fields[3].GetUInt32(); - std::string subject = fields[4].GetCppString(); - std::string body = fields[5].GetCppString(); - uint64 money = fields[6].GetUInt32(); - bool has_items = fields[7].GetBool(); - - // we can return mail now - // so firstly delete the old one - CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", mail_id); - - // mail not from player - if (mailType != MAIL_NORMAL) - { - if (has_items) - CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", mail_id); - continue; - } - - MailDraft draft; - if (mailTemplateId) - draft.SetMailTemplate(mailTemplateId, false);// items already included - else - draft.SetSubjectAndBody(subject, body); - - if (has_items) - { - // data needs to be at first place for Item::LoadFromDB - // 0 1 2 3 - QueryResult* resultItems = CharacterDatabase.PQuery("SELECT data,text,item_guid,item_template FROM mail_items JOIN item_instance ON item_guid = guid WHERE mail_id='%u'", mail_id); - if (resultItems) - { - do - { - Field* fields2 = resultItems->Fetch(); - - uint32 item_guidlow = fields2[2].GetUInt32(); - uint32 item_template = fields2[3].GetUInt32(); - - ItemPrototype const* itemProto = ObjectMgr::GetItemPrototype(item_template); - if (!itemProto) - { - CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item_guidlow); - continue; - } - - Item* pItem = NewItemOrBag(itemProto); - if (!pItem->LoadFromDB(item_guidlow, fields2, playerguid)) - { - pItem->FSetState(ITEM_REMOVED); - pItem->SaveToDB(); // it also deletes item object ! - continue; - } - - draft.AddItem(pItem); - } - while (resultItems->NextRow()); - - delete resultItems; - } - } - - CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", mail_id); - - uint32 pl_account = sObjectMgr.GetPlayerAccountIdByGUID(playerguid); - - draft.SetMoney(money).SendReturnToSender(pl_account, playerguid, ObjectGuid(HIGHGUID_PLAYER, sender)); - } - while (resultMail->NextRow()); - - delete resultMail; - } - - // unsummon and delete for pets in world is not required: player deleted from CLI or character list with not loaded pet. - // Get guids of character's pets, will deleted in transaction - QueryResult* resultPets = CharacterDatabase.PQuery("SELECT id FROM character_pet WHERE owner = '%u'", lowguid); - - // delete char from friends list when selected chars is online (non existing - error) - QueryResult* resultFriend = CharacterDatabase.PQuery("SELECT DISTINCT guid FROM character_social WHERE friend = '%u'", lowguid); - - // NOW we can finally clear other DB data related to character - CharacterDatabase.BeginTransaction(); - if (resultPets) - { - do - { - Field* fields3 = resultPets->Fetch(); - uint32 petguidlow = fields3[0].GetUInt32(); - // do not create separate transaction for pet delete otherwise we will get fatal error! - Pet::DeleteFromDB(petguidlow, false); - } - while (resultPets->NextRow()); - delete resultPets; - } - - // cleanup friends for online players, offline case will cleanup later in code - if (resultFriend) - { - do - { - Field* fieldsFriend = resultFriend->Fetch(); - if (Player* sFriend = sObjectAccessor.FindPlayer(ObjectGuid(HIGHGUID_PLAYER, fieldsFriend[0].GetUInt32()))) - { - if (sFriend->IsInWorld()) - { - sFriend->GetSocial()->RemoveFromSocialList(playerguid, false); - sSocialMgr.SendFriendStatus(sFriend, FRIEND_REMOVED, playerguid, false); - } - } - } - while (resultFriend->NextRow()); - delete resultFriend; - } - - CharacterDatabase.PExecute("DELETE FROM characters WHERE guid = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM character_account_data WHERE guid = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM character_action WHERE guid = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM character_aura WHERE guid = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM character_battleground_data WHERE guid = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE guid = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM character_glyphs WHERE guid = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM character_homebind WHERE guid = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM group_instance WHERE leaderGuid = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE guid = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM character_queststatus WHERE guid = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM character_queststatus_daily WHERE guid = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM character_queststatus_weekly WHERE guid = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM character_reputation WHERE guid = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM character_skills WHERE guid = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM character_spell WHERE guid = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM character_spell_cooldown WHERE guid = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM character_talent WHERE guid = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM character_ticket WHERE guid = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM item_instance WHERE owner_guid = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM character_social WHERE guid = '%u' OR friend='%u'", lowguid, lowguid); - CharacterDatabase.PExecute("DELETE FROM mail WHERE receiver = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM mail_items WHERE receiver = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM character_pet_declinedname WHERE owner = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE guid = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE guid = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM character_equipmentsets WHERE guid = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM guild_eventlog WHERE PlayerGuid1 = '%u' OR PlayerGuid2 = '%u'", lowguid, lowguid); - CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE PlayerGuid = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM character_currencies WHERE guid = '%u'", lowguid); - CharacterDatabase.CommitTransaction(); - break; - } - // The character gets unlinked from the account, the name gets freed up and appears as deleted ingame - case 1: - CharacterDatabase.PExecute("UPDATE characters SET deleteInfos_Name=name, deleteInfos_Account=account, deleteDate='" UI64FMTD "', name='', account=0 WHERE guid=%u", uint64(time(NULL)), lowguid); - break; - default: - sLog.outError("Player::DeleteFromDB: Unsupported delete method: %u.", charDelete_method); - } - - if (updateRealmChars) - sWorld.UpdateRealmCharCount(accountId); -} - -/* - * Characters which were kept back in the database after being deleted and are now too old (see config option "CharDelete.KeepDays"), will be completely deleted. - * - * @see Player::DeleteFromDB - */ -void Player::DeleteOldCharacters() -{ - uint32 keepDays = sWorld.getConfig(CONFIG_UINT32_CHARDELETE_KEEP_DAYS); - if (!keepDays) - return; - - Player::DeleteOldCharacters(keepDays); -} - -/* - * Characters which were kept back in the database after being deleted and are older than the specified amount of days, will be completely deleted. - * - * @see Player::DeleteFromDB - * - * @param keepDays overrite the config option by another amount of days - */ -void Player::DeleteOldCharacters(uint32 keepDays) -{ - sLog.outString("Player::DeleteOldChars: Deleting all characters which have been deleted %u days before...", keepDays); - - QueryResult* resultChars = CharacterDatabase.PQuery("SELECT guid, deleteInfos_Account FROM characters WHERE deleteDate IS NOT NULL AND deleteDate < '" UI64FMTD "'", uint64(time(NULL) - time_t(keepDays * DAY))); - if (resultChars) - { - sLog.outString("Player::DeleteOldChars: Found %u character(s) to delete", uint32(resultChars->GetRowCount())); - do - { - Field* charFields = resultChars->Fetch(); - ObjectGuid guid = ObjectGuid(HIGHGUID_PLAYER, charFields[0].GetUInt32()); - Player::DeleteFromDB(guid, charFields[1].GetUInt32(), true, true); - } - while (resultChars->NextRow()); - delete resultChars; - } -} - -void Player::SetRoot(bool enable) -{ - WorldPacket data; - BuildForceMoveRootPacket(&data, enable, 0); - GetSession()->SendPacket(&data); -} - -void Player::SetWaterWalk(bool enable) -{ - WorldPacket data; - BuildMoveWaterWalkPacket(&data, enable, 0); - GetSession()->SendPacket(&data); -} - -/* Preconditions: - - a resurrectable corpse must not be loaded for the player (only bones) - - the player must be in world -*/ -void Player::BuildPlayerRepop() -{ - WorldPacket data(SMSG_PRE_RESURRECT, GetPackGUID().size()); - data << GetPackGUID(); - GetSession()->SendPacket(&data); - - if (getRace() == RACE_NIGHTELF) - CastSpell(this, 20584, true); // auras SPELL_AURA_INCREASE_SPEED(+speed in wisp form), SPELL_AURA_INCREASE_SWIM_SPEED(+swim speed in wisp form), SPELL_AURA_TRANSFORM (to wisp form) - CastSpell(this, 8326, true); // auras SPELL_AURA_GHOST, SPELL_AURA_INCREASE_SPEED(why?), SPELL_AURA_INCREASE_SWIM_SPEED(why?) - - // there must be SMSG.FORCE_RUN_SPEED_CHANGE, SMSG.FORCE_SWIM_SPEED_CHANGE, SMSG.MOVE_WATER_WALK - // there must be SMSG.STOP_MIRROR_TIMER - // there we must send 888 opcode - - // the player cannot have a corpse already, only bones which are not returned by GetCorpse - if (GetCorpse()) - { - sLog.outError("BuildPlayerRepop: player %s(%d) already has a corpse", GetName(), GetGUIDLow()); - MANGOS_ASSERT(false); - } - - // create a corpse and place it at the player's location - Corpse* corpse = CreateCorpse(); - if (!corpse) - { - sLog.outError("Error creating corpse for Player %s [%u]", GetName(), GetGUIDLow()); - return; - } - GetMap()->Add(corpse); - - // convert player body to ghost - SetHealth(1); - - SetWaterWalk(true); - if (!GetSession()->isLogingOut()) - SetRoot(false); - - // BG - remove insignia related - RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); - - SendCorpseReclaimDelay(); - - // to prevent cheating - corpse->ResetGhostTime(); - - StopMirrorTimers(); // disable timers(bars) - - // set and clear other - SetByteValue(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND); -} - -void Player::ResurrectPlayer(float restore_percent, bool applySickness) -{ - WorldPacket data(SMSG_DEATH_RELEASE_LOC, 4 * 4); // remove spirit healer position - data << uint32(-1); - data << float(0); - data << float(0); - data << float(0); - GetSession()->SendPacket(&data); - - // speed change, land walk - - // remove death flag + set aura - SetByteValue(UNIT_FIELD_BYTES_1, 3, 0x00); - if (getRace() == RACE_NIGHTELF) - RemoveAurasDueToSpell(20584); // speed bonuses - RemoveAurasDueToSpell(8326); // SPELL_AURA_GHOST - - SetDeathState(ALIVE); - - SetWaterWalk(false); - SetRoot(false); - - m_deathTimer = 0; - - // set health/powers (0- will be set in caller) - if (restore_percent > 0.0f) - { - SetHealth(uint32(GetMaxHealth()*restore_percent)); - SetPower(POWER_MANA, uint32(GetMaxPower(POWER_MANA)*restore_percent)); - SetPower(POWER_RAGE, 0); - SetPower(POWER_ENERGY, uint32(GetMaxPower(POWER_ENERGY)*restore_percent)); - } - - // trigger update zone for alive state zone updates - uint32 newzone, newarea; - GetZoneAndAreaId(newzone, newarea); - UpdateZone(newzone, newarea); - - // update visibility of world around viewpoint - m_camera.UpdateVisibilityForOwner(); - // update visibility of player for nearby cameras - UpdateObjectVisibility(); - - if (!applySickness) - return; - - // Characters from level 1-10 are not affected by resurrection sickness. - // Characters from level 11-19 will suffer from one minute of sickness - // for each level they are above 10. - // Characters level 20 and up suffer from ten minutes of sickness. - int32 startLevel = sWorld.getConfig(CONFIG_INT32_DEATH_SICKNESS_LEVEL); - - if (int32(getLevel()) >= startLevel) - { - // set resurrection sickness - CastSpell(this, SPELL_ID_PASSIVE_RESURRECTION_SICKNESS, true); - - // not full duration - if (int32(getLevel()) < startLevel + 9) - { - int32 delta = (int32(getLevel()) - startLevel + 1) * MINUTE; - - if (SpellAuraHolder* holder = GetSpellAuraHolder(SPELL_ID_PASSIVE_RESURRECTION_SICKNESS)) - { - holder->SetAuraDuration(delta * IN_MILLISECONDS); - holder->SendAuraUpdate(false); - } - } - } -} - -void Player::KillPlayer() -{ - SetRoot(true); - - StopMirrorTimers(); // disable timers(bars) - - SetDeathState(CORPSE); - // SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_IN_PVP ); - - SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_NONE); - ApplyModByteFlag(PLAYER_FIELD_BYTES, 0, PLAYER_FIELD_BYTE_RELEASE_TIMER, !sMapStore.LookupEntry(GetMapId())->Instanceable()); - - // 6 minutes until repop at graveyard - m_deathTimer = 6 * MINUTE * IN_MILLISECONDS; - - UpdateCorpseReclaimDelay(); // dependent at use SetDeathPvP() call before kill - - // don't create corpse at this moment, player might be falling - - // update visibility - UpdateObjectVisibility(); -} - -Corpse* Player::CreateCorpse() -{ - // prevent existence 2 corpse for player - SpawnCorpseBones(); - - Corpse* corpse = new Corpse((m_ExtraFlags & PLAYER_EXTRA_PVP_DEATH) ? CORPSE_RESURRECTABLE_PVP : CORPSE_RESURRECTABLE_PVE); - SetPvPDeath(false); - - if (!corpse->Create(sObjectMgr.GenerateCorpseLowGuid(), this)) - { - delete corpse; - return NULL; - } - - uint8 skin = GetByteValue(PLAYER_BYTES, 0); - uint8 face = GetByteValue(PLAYER_BYTES, 1); - uint8 hairstyle = GetByteValue(PLAYER_BYTES, 2); - uint8 haircolor = GetByteValue(PLAYER_BYTES, 3); - uint8 facialhair = GetByteValue(PLAYER_BYTES_2, 0); - - corpse->SetByteValue(CORPSE_FIELD_BYTES_1, 1, getRace()); - corpse->SetByteValue(CORPSE_FIELD_BYTES_1, 2, getGender()); - corpse->SetByteValue(CORPSE_FIELD_BYTES_1, 3, skin); - - corpse->SetByteValue(CORPSE_FIELD_BYTES_2, 0, face); - corpse->SetByteValue(CORPSE_FIELD_BYTES_2, 1, hairstyle); - corpse->SetByteValue(CORPSE_FIELD_BYTES_2, 2, haircolor); - corpse->SetByteValue(CORPSE_FIELD_BYTES_2, 3, facialhair); - - uint32 flags = CORPSE_FLAG_UNK2; - if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM)) - flags |= CORPSE_FLAG_HIDE_HELM; - if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK)) - flags |= CORPSE_FLAG_HIDE_CLOAK; - if (InBattleGround() && !InArena()) - flags |= CORPSE_FLAG_LOOTABLE; // to be able to remove insignia - corpse->SetUInt32Value(CORPSE_FIELD_FLAGS, flags); - - corpse->SetUInt32Value(CORPSE_FIELD_DISPLAY_ID, GetNativeDisplayId()); - - uint32 iDisplayID; - uint32 iIventoryType; - uint32 _cfi; - for (int i = 0; i < EQUIPMENT_SLOT_END; ++i) - { - if (m_items[i]) - { - iDisplayID = m_items[i]->GetProto()->DisplayInfoID; - iIventoryType = m_items[i]->GetProto()->InventoryType; - - _cfi = iDisplayID | (iIventoryType << 24); - corpse->SetUInt32Value(CORPSE_FIELD_ITEM + i, _cfi); - } - } - - // we not need saved corpses for BG/arenas - if (!GetMap()->IsBattleGroundOrArena()) - corpse->SaveToDB(); - - // register for player, but not show - sObjectAccessor.AddCorpse(corpse); - return corpse; -} - -void Player::SpawnCorpseBones() -{ - if (sObjectAccessor.ConvertCorpseForPlayer(GetObjectGuid())) - if (!GetSession()->PlayerLogoutWithSave()) // at logout we will already store the player - SaveToDB(); // prevent loading as ghost without corpse -} - -Corpse* Player::GetCorpse() const -{ - return sObjectAccessor.GetCorpseForPlayerGUID(GetObjectGuid()); -} - -void Player::DurabilityLossAll(double percent, bool inventory) -{ - for (int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i) - if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - DurabilityLoss(pItem, percent); - - if (inventory) - { - // bags not have durability - // for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) - - for (int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i) - if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - DurabilityLoss(pItem, percent); - - for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) - if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - for (uint32 j = 0; j < pBag->GetBagSize(); ++j) - if (Item* pItem = GetItemByPos(i, j)) - DurabilityLoss(pItem, percent); - } -} - -void Player::DurabilityLoss(Item* item, double percent) -{ - if (!item) - return; - - uint32 pMaxDurability = item ->GetUInt32Value(ITEM_FIELD_MAXDURABILITY); - - if (!pMaxDurability) - return; - - uint32 pDurabilityLoss = uint32(pMaxDurability * percent); - - if (pDurabilityLoss < 1) - pDurabilityLoss = 1; - - DurabilityPointsLoss(item, pDurabilityLoss); -} - -void Player::DurabilityPointsLossAll(int32 points, bool inventory) -{ - for (int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i) - if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - DurabilityPointsLoss(pItem, points); - - if (inventory) - { - // bags not have durability - // for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) - - for (int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i) - if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - DurabilityPointsLoss(pItem, points); - - for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) - if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - for (uint32 j = 0; j < pBag->GetBagSize(); ++j) - if (Item* pItem = GetItemByPos(i, j)) - DurabilityPointsLoss(pItem, points); - } -} - -void Player::DurabilityPointsLoss(Item* item, int32 points) -{ - int32 pMaxDurability = item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY); - int32 pOldDurability = item->GetUInt32Value(ITEM_FIELD_DURABILITY); - int32 pNewDurability = pOldDurability - points; - - if (pNewDurability < 0) - pNewDurability = 0; - else if (pNewDurability > pMaxDurability) - pNewDurability = pMaxDurability; - - if (pOldDurability != pNewDurability) - { - // modify item stats _before_ Durability set to 0 to pass _ApplyItemMods internal check - if (pNewDurability == 0 && pOldDurability > 0 && item->IsEquipped()) - _ApplyItemMods(item, item->GetSlot(), false); - - item->SetUInt32Value(ITEM_FIELD_DURABILITY, pNewDurability); - - // modify item stats _after_ restore durability to pass _ApplyItemMods internal check - if (pNewDurability > 0 && pOldDurability == 0 && item->IsEquipped()) - _ApplyItemMods(item, item->GetSlot(), true); - - item->SetState(ITEM_CHANGED, this); - } -} - -void Player::DurabilityPointLossForEquipSlot(EquipmentSlots slot) -{ - if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, slot)) - DurabilityPointsLoss(pItem, 1); -} - -uint32 Player::DurabilityRepairAll(bool cost, float discountMod, bool guildBank) -{ - uint32 TotalCost = 0; - // equipped, backpack, bags itself - for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i) - TotalCost += DurabilityRepair(((INVENTORY_SLOT_BAG_0 << 8) | i), cost, discountMod, guildBank); - - // bank, buyback and keys not repaired - - // items in inventory bags - for (int j = INVENTORY_SLOT_BAG_START; j < INVENTORY_SLOT_BAG_END; ++j) - for (int i = 0; i < MAX_BAG_SIZE; ++i) - TotalCost += DurabilityRepair(((j << 8) | i), cost, discountMod, guildBank); - return TotalCost; -} - -uint32 Player::DurabilityRepair(uint16 pos, bool cost, float discountMod, bool guildBank) -{ - Item* item = GetItemByPos(pos); - - uint32 TotalCost = 0; - if (!item) - return TotalCost; - - uint32 maxDurability = item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY); - if (!maxDurability) - return TotalCost; - - uint32 curDurability = item->GetUInt32Value(ITEM_FIELD_DURABILITY); - - if (cost) - { - uint32 LostDurability = maxDurability - curDurability; - if (LostDurability > 0) - { - ItemPrototype const* ditemProto = item->GetProto(); - - DurabilityCostsEntry const* dcost = sDurabilityCostsStore.LookupEntry(ditemProto->ItemLevel); - if (!dcost) - { - sLog.outError("RepairDurability: Wrong item lvl %u", ditemProto->ItemLevel); - return TotalCost; - } - - uint32 dQualitymodEntryId = (ditemProto->Quality + 1) * 2; - DurabilityQualityEntry const* dQualitymodEntry = sDurabilityQualityStore.LookupEntry(dQualitymodEntryId); - if (!dQualitymodEntry) - { - sLog.outError("RepairDurability: Wrong dQualityModEntry %u", dQualitymodEntryId); - return TotalCost; - } - - uint32 dmultiplier = dcost->multiplier[ItemSubClassToDurabilityMultiplierId(ditemProto->Class, ditemProto->SubClass)]; - uint32 costs = uint32(LostDurability * dmultiplier * double(dQualitymodEntry->quality_mod)); - - costs = uint32(costs * discountMod); - - if (costs == 0) // fix for ITEM_QUALITY_ARTIFACT - costs = 1; - - if (guildBank) - { - if (GetGuildId() == 0) - { - DEBUG_LOG("You are not member of a guild"); - return TotalCost; - } - - Guild* pGuild = sGuildMgr.GetGuildById(GetGuildId()); - if (!pGuild) - return TotalCost; - - if (!pGuild->HasRankRight(GetRank(), GR_RIGHT_WITHDRAW_REPAIR)) - { - DEBUG_LOG("You do not have rights to withdraw for repairs"); - return TotalCost; - } - - if (pGuild->GetMemberMoneyWithdrawRem(GetGUIDLow()) < costs) - { - DEBUG_LOG("You do not have enough money withdraw amount remaining"); - return TotalCost; - } - - if (pGuild->GetGuildBankMoney() < costs) - { - DEBUG_LOG("There is not enough money in bank"); - return TotalCost; - } - - pGuild->MemberMoneyWithdraw(costs, GetGUIDLow()); - TotalCost = costs; - } - else if (GetMoney() < costs) - { - DEBUG_LOG("You do not have enough money"); - return TotalCost; - } - else - ModifyMoney(-int64(costs)); - } - } - - item->SetUInt32Value(ITEM_FIELD_DURABILITY, maxDurability); - item->SetState(ITEM_CHANGED, this); - - // reapply mods for total broken and repaired item if equipped - if (IsEquipmentPos(pos) && !curDurability) - _ApplyItemMods(item, pos & 255, true); - return TotalCost; -} - -void Player::RepopAtGraveyard() -{ - // note: this can be called also when the player is alive - // for example from WorldSession::HandleMovementOpcodes - - AreaTableEntry const* zone = GetAreaEntryByAreaID(GetAreaId()); - - // Such zones are considered unreachable as a ghost and the player must be automatically revived - if ((!isAlive() && zone && zone->flags & AREA_FLAG_NEED_FLY) || GetTransport()) - { - ResurrectPlayer(0.5f); - SpawnCorpseBones(); - } - - WorldSafeLocsEntry const* ClosestGrave = NULL; - - // Special handle for battleground maps - if (BattleGround* bg = GetBattleGround()) - ClosestGrave = bg->GetClosestGraveYard(this); - else - ClosestGrave = sObjectMgr.GetClosestGraveYard(GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId(), GetTeam()); - - // stop countdown until repop - m_deathTimer = 0; - - // if no grave found, stay at the current location - // and don't show spirit healer location - if (ClosestGrave) - { - bool updateVisibility = IsInWorld() && GetMapId() == ClosestGrave->map_id; - TeleportTo(ClosestGrave->map_id, ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, GetOrientation()); - if (isDead()) // not send if alive, because it used in TeleportTo() - { - WorldPacket data(SMSG_DEATH_RELEASE_LOC, 4 * 4);// show spirit healer position on minimap - data << ClosestGrave->map_id; - data << ClosestGrave->x; - data << ClosestGrave->y; - data << ClosestGrave->z; - GetSession()->SendPacket(&data); - } - if (updateVisibility && IsInWorld()) - UpdateVisibilityAndView(); - } -} - -void Player::JoinedChannel(Channel* c) -{ - m_channels.push_back(c); -} - -void Player::LeftChannel(Channel* c) -{ - m_channels.remove(c); -} - -void Player::CleanupChannels() -{ - while (!m_channels.empty()) - { - Channel* ch = *m_channels.begin(); - m_channels.erase(m_channels.begin()); // remove from player's channel list - ch->Leave(GetObjectGuid(), false); // not send to client, not remove from player's channel list - if (ChannelMgr* cMgr = channelMgr(GetTeam())) - cMgr->LeftChannel(ch->GetName()); // deleted channel if empty - } - DEBUG_LOG("Player: channels cleaned up!"); -} - -void Player::UpdateLocalChannels(uint32 newZone) -{ - if (m_channels.empty()) - return; - - AreaTableEntry const* current_zone = GetAreaEntryByAreaID(newZone); - if (!current_zone) - return; - - ChannelMgr* cMgr = channelMgr(GetTeam()); - if (!cMgr) - return; - - std::string current_zone_name = current_zone->area_name[GetSession()->GetSessionDbcLocale()]; - - for (JoinedChannelsList::iterator i = m_channels.begin(), next; i != m_channels.end(); i = next) - { - next = i; ++next; - - // skip non built-in channels - if (!(*i)->IsConstant()) - continue; - - ChatChannelsEntry const* ch = GetChannelEntryFor((*i)->GetChannelId()); - if (!ch) - continue; - - if ((ch->flags & 4) == 4) // global channel without zone name in pattern - continue; - - // new channel - char new_channel_name_buf[100]; - snprintf(new_channel_name_buf, 100, ch->pattern[m_session->GetSessionDbcLocale()], current_zone_name.c_str()); - Channel* new_channel = cMgr->GetJoinChannel(new_channel_name_buf, ch->ChannelID); - - if ((*i) != new_channel) - { - new_channel->Join(GetObjectGuid(), ""); // will output Changed Channel: N. Name - - // leave old channel - (*i)->Leave(GetObjectGuid(), false); // not send leave channel, it already replaced at client - std::string name = (*i)->GetName(); // store name, (*i)erase in LeftChannel - LeftChannel(*i); // remove from player's channel list - cMgr->LeftChannel(name); // delete if empty - } - } - DEBUG_LOG("Player: channels cleaned up!"); -} - -void Player::LeaveLFGChannel() -{ - for (JoinedChannelsList::iterator i = m_channels.begin(); i != m_channels.end(); ++i) - { - if ((*i)->IsLFG()) - { - (*i)->Leave(GetObjectGuid()); - break; - } - } -} - -void Player::HandleBaseModValue(BaseModGroup modGroup, BaseModType modType, float amount, bool apply) -{ - if (modGroup >= BASEMOD_END || modType >= MOD_END) - { - sLog.outError("ERROR in HandleBaseModValue(): nonexistent BaseModGroup of wrong BaseModType!"); - return; - } - - float val = 1.0f; - - switch (modType) - { - case FLAT_MOD: - m_auraBaseMod[modGroup][modType] += apply ? amount : -amount; - break; - case PCT_MOD: - if (amount <= -100.0f) - amount = -200.0f; - - val = (100.0f + amount) / 100.0f; - m_auraBaseMod[modGroup][modType] *= apply ? val : (1.0f / val); - break; - } - - if (!CanModifyStats()) - return; - - switch (modGroup) - { - case CRIT_PERCENTAGE: UpdateCritPercentage(BASE_ATTACK); break; - case RANGED_CRIT_PERCENTAGE: UpdateCritPercentage(RANGED_ATTACK); break; - case OFFHAND_CRIT_PERCENTAGE: UpdateCritPercentage(OFF_ATTACK); break; - case SHIELD_BLOCK_DAMAGE_VALUE: UpdateShieldBlockDamageValue(); break; - default: break; - } -} - -float Player::GetBaseModValue(BaseModGroup modGroup, BaseModType modType) const -{ - if (modGroup >= BASEMOD_END || modType > MOD_END) - { - sLog.outError("trial to access nonexistent BaseModGroup or wrong BaseModType!"); - return 0.0f; - } - - if (modType == PCT_MOD && m_auraBaseMod[modGroup][PCT_MOD] <= 0.0f) - return 0.0f; - - return m_auraBaseMod[modGroup][modType]; -} - -float Player::GetTotalBaseModValue(BaseModGroup modGroup) const -{ - if (modGroup >= BASEMOD_END) - { - sLog.outError("wrong BaseModGroup in GetTotalBaseModValue()!"); - return 0.0f; - } - - if (m_auraBaseMod[modGroup][PCT_MOD] <= 0.0f) - return 0.0f; - - return m_auraBaseMod[modGroup][FLAT_MOD] * m_auraBaseMod[modGroup][PCT_MOD]; -} - -uint32 Player::GetShieldBlockDamageValue() const -{ - float value = m_canBlock ? BASE_BLOCK_DAMAGE_PERCENT : 0; - value = (value + m_auraBaseMod[SHIELD_BLOCK_DAMAGE_VALUE][FLAT_MOD]) * m_auraBaseMod[SHIELD_BLOCK_DAMAGE_VALUE][PCT_MOD]; - - value = (value < 0) ? 0 : value; - - return uint32(value); -} - -float Player::GetMeleeCritFromAgility() -{ - uint32 level = getLevel(); - uint32 pclass = getClass(); - - if (level > GT_MAX_LEVEL) level = GT_MAX_LEVEL; - - GtChanceToMeleeCritBaseEntry const* critBase = sGtChanceToMeleeCritBaseStore.LookupEntry(pclass - 1); - GtChanceToMeleeCritEntry const* critRatio = sGtChanceToMeleeCritStore.LookupEntry((pclass - 1) * GT_MAX_LEVEL + level - 1); - if (critBase == NULL || critRatio == NULL) - return 0.0f; - - float crit = critBase->base + GetStat(STAT_AGILITY) * critRatio->ratio; - return crit * 100.0f; -} - -void Player::GetDodgeFromAgility(float& diminishing, float& nondiminishing) -{ - // 4.2.0: these classes no longer receive dodge from agility and have 5% base - if (getClass() == CLASS_WARRIOR || getClass() == CLASS_PALADIN || getClass() == CLASS_DEATH_KNIGHT) - { - nondiminishing += 5.0f; - return; - } - - // Table for base dodge values - const float dodge_base[MAX_CLASSES] = - { - 0.036640f, // Warrior - 0.034943f, // Paladin - -0.040873f, // Hunter - 0.020957f, // Rogue - 0.034178f, // Priest - 0.036640f, // DK - 0.021080f, // Shaman - 0.036587f, // Mage - 0.024211f, // Warlock - 0.0f, // ?? - 0.056097f // Druid - }; - // Crit/agility to dodge/agility coefficient multipliers; 3.2.0 increased required agility by 15% - const float crit_to_dodge[MAX_CLASSES] = - { - 0.85f / 1.15f, // Warrior - 1.00f / 1.15f, // Paladin - 1.11f / 1.15f, // Hunter - 2.00f / 1.15f, // Rogue - 1.00f / 1.15f, // Priest - 0.85f / 1.15f, // DK - 1.60f / 1.15f, // Shaman - 1.00f / 1.15f, // Mage - 0.97f / 1.15f, // Warlock (?) - 0.0f, // ?? - 2.00f / 1.15f // Druid - }; - - uint32 level = getLevel(); - uint32 pclass = getClass(); - - if (level > GT_MAX_LEVEL) level = GT_MAX_LEVEL; - - // Dodge per agility is proportional to crit per agility, which is available from DBC files - GtChanceToMeleeCritEntry const* dodgeRatio = sGtChanceToMeleeCritStore.LookupEntry((pclass - 1) * GT_MAX_LEVEL + level - 1); - if (dodgeRatio == NULL || pclass > MAX_CLASSES) - return; - - // TODO: research if talents/effects that increase total agility by x% should increase non-diminishing part - float base_agility = GetCreateStat(STAT_AGILITY) * m_auraModifiersGroup[UNIT_MOD_STAT_START + STAT_AGILITY][BASE_PCT]; - float bonus_agility = GetStat(STAT_AGILITY) - base_agility; - // calculate diminishing (green in char screen) and non-diminishing (white) contribution - diminishing += 100.0f * bonus_agility * dodgeRatio->ratio * crit_to_dodge[pclass - 1]; - nondiminishing += 100.0f * (dodge_base[pclass - 1] + base_agility * dodgeRatio->ratio * crit_to_dodge[pclass - 1]); -} - -void Player::GetParryFromStrength(float& diminishing, float& nondiminishing) -{ - // other classes than these do not receive parry bonus from strength - if (getClass() != CLASS_WARRIOR && getClass() != CLASS_PALADIN && getClass() != CLASS_DEATH_KNIGHT) - return; - - float base_strength = GetCreateStat(STAT_STRENGTH) * m_auraModifiersGroup[UNIT_MOD_STAT_START + STAT_STRENGTH][BASE_PCT]; - float bonus_strength = GetStat(STAT_STRENGTH) - base_strength; - // calculate diminishing (green in char screen) and non-diminishing (white) contribution - diminishing += bonus_strength * GetRatingMultiplier(CR_PARRY) * 0.27f; - nondiminishing += base_strength * GetRatingMultiplier(CR_PARRY) * 0.27f; -} - -float Player::GetSpellCritFromIntellect() -{ - uint32 level = getLevel(); - uint32 pclass = getClass(); - - if (level > GT_MAX_LEVEL) level = GT_MAX_LEVEL; - - GtChanceToSpellCritBaseEntry const* critBase = sGtChanceToSpellCritBaseStore.LookupEntry(pclass - 1); - GtChanceToSpellCritEntry const* critRatio = sGtChanceToSpellCritStore.LookupEntry((pclass - 1) * GT_MAX_LEVEL + level - 1); - if (critBase == NULL || critRatio == NULL) - return 0.0f; - - float crit = critBase->base + GetStat(STAT_INTELLECT) * critRatio->ratio; - return crit * 100.0f; -} - -float Player::GetRatingMultiplier(CombatRating cr) const -{ - uint32 level = getLevel(); - - if (level > GT_MAX_LEVEL) level = GT_MAX_LEVEL; - - GtCombatRatingsEntry const* Rating = sGtCombatRatingsStore.LookupEntry(cr * GT_MAX_LEVEL + level - 1); - // gtOCTClassCombatRatingScalarStore.dbc starts with 1, CombatRating with zero, so cr+1 - GtOCTClassCombatRatingScalarEntry const* classRating = sGtOCTClassCombatRatingScalarStore.LookupEntry((getClass() - 1) * GT_MAX_RATING + cr + 1); - if (!Rating || !classRating) - return 1.0f; // By default use minimum coefficient (not must be called) - - return classRating->ratio / Rating->ratio; -} - -float Player::GetRatingBonusValue(CombatRating cr) const -{ - return float(GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + cr)) * GetRatingMultiplier(cr); -} - -float Player::GetExpertiseDodgeOrParryReduction(WeaponAttackType attType) const -{ - switch (attType) - { - case BASE_ATTACK: - return GetUInt32Value(PLAYER_EXPERTISE) / 4.0f; - case OFF_ATTACK: - return GetUInt32Value(PLAYER_OFFHAND_EXPERTISE) / 4.0f; - default: - break; - } - return 0.0f; -} - -float Player::OCTRegenMPPerSpirit() -{ - // Only healers have regen bonus from spirit. Others regenerate by combat regen. - uint32 rolesMask = GetTalentTreeRolesMask(m_talentsPrimaryTree[m_activeSpec]); - if ((rolesMask & TALENT_ROLE_HEALER) == 0) - return 0.0f; - - uint32 level = getLevel(); - uint32 pclass = getClass(); - - if (level > GT_MAX_LEVEL) level = GT_MAX_LEVEL; - -// GtOCTRegenMPEntry const *baseRatio = sGtOCTRegenMPStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1); - GtRegenMPPerSptEntry const* moreRatio = sGtRegenMPPerSptStore.LookupEntry((pclass - 1) * GT_MAX_LEVEL + level - 1); - if (moreRatio == NULL) - return 0.0f; - - // Formula get from PaperDollFrame script - float spirit = GetStat(STAT_SPIRIT); - float regen = spirit * moreRatio->ratio; - return regen; -} - -void Player::ApplyRatingMod(CombatRating cr, int32 value, bool apply) -{ - m_baseRatingValue[cr] += (apply ? value : -value); - - // explicit affected values - switch (cr) - { - case CR_HASTE_MELEE: - { - float RatingChange = value * GetRatingMultiplier(cr); - ApplyAttackTimePercentMod(BASE_ATTACK, RatingChange, apply); - ApplyAttackTimePercentMod(OFF_ATTACK, RatingChange, apply); - break; - } - case CR_HASTE_RANGED: - { - float RatingChange = value * GetRatingMultiplier(cr); - ApplyAttackTimePercentMod(RANGED_ATTACK, RatingChange, apply); - break; - } - case CR_HASTE_SPELL: - { - float RatingChange = value * GetRatingMultiplier(cr); - ApplyCastTimePercentMod(RatingChange, apply); - break; - } - default: - break; - } - - UpdateRating(cr); -} - -void Player::UpdateRating(CombatRating cr) -{ - int32 amount = m_baseRatingValue[cr]; - // Apply bonus from SPELL_AURA_MOD_RATING_FROM_STAT - // stat used stored in miscValueB for this aura - AuraList const& modRatingFromStat = GetAurasByType(SPELL_AURA_MOD_RATING_FROM_STAT); - for (AuraList::const_iterator i = modRatingFromStat.begin(); i != modRatingFromStat.end(); ++i) - if ((*i)->GetMiscValue() & (1 << cr)) - amount += int32(GetStat(Stats((*i)->GetMiscBValue())) * (*i)->GetModifier()->m_amount / 100.0f); - if (amount < 0) - amount = 0; - SetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + cr, uint32(amount)); - - bool affectStats = CanModifyStats(); - - switch (cr) - { - case CR_WEAPON_SKILL: - case CR_DEFENSE_SKILL: - break; - case CR_DODGE: - UpdateDodgePercentage(); - break; - case CR_PARRY: - UpdateParryPercentage(); - break; - case CR_BLOCK: - UpdateBlockPercentage(); - break; - case CR_HIT_MELEE: - UpdateMeleeHitChances(); - break; - case CR_HIT_RANGED: - UpdateRangedHitChances(); - break; - case CR_HIT_SPELL: - UpdateSpellHitChances(); - break; - case CR_CRIT_MELEE: - if (affectStats) - { - UpdateCritPercentage(BASE_ATTACK); - UpdateCritPercentage(OFF_ATTACK); - } - break; - case CR_CRIT_RANGED: - if (affectStats) - UpdateCritPercentage(RANGED_ATTACK); - break; - case CR_CRIT_SPELL: - if (affectStats) - UpdateAllSpellCritChances(); - break; - case CR_RESILIENCE_DAMAGE_TAKEN: - break; - case CR_HASTE_MELEE: // Implemented in Player::ApplyRatingMod - case CR_HASTE_RANGED: - case CR_HASTE_SPELL: - break; - case CR_EXPERTISE: - if (affectStats) - { - UpdateExpertise(BASE_ATTACK); - UpdateExpertise(OFF_ATTACK); - } - break; - case CR_ARMOR_PENETRATION: - if (affectStats) - UpdateArmorPenetration(); - break; - case CR_MASTERY: - UpdateMasteryAuras(); - break; - // deprecated - case CR_HIT_TAKEN_MELEE: - case CR_HIT_TAKEN_RANGED: - case CR_HIT_TAKEN_SPELL: - case CR_CRIT_TAKEN_SPELL: - case CR_CRIT_TAKEN_MELEE: - case CR_WEAPON_SKILL_MAINHAND: - case CR_WEAPON_SKILL_OFFHAND: - case CR_WEAPON_SKILL_RANGED: - break; - } -} - -void Player::UpdateAllRatings() -{ - for (int cr = 0; cr < MAX_COMBAT_RATING; ++cr) - UpdateRating(CombatRating(cr)); -} - -void Player::SetRegularAttackTime() -{ - for (int i = 0; i < MAX_ATTACK; ++i) - { - Item* tmpitem = GetWeaponForAttack(WeaponAttackType(i), true, false); - if (tmpitem) - { - ItemPrototype const* proto = tmpitem->GetProto(); - if (proto->Delay) - SetAttackTime(WeaponAttackType(i), proto->Delay); - else - SetAttackTime(WeaponAttackType(i), BASE_ATTACK_TIME); - } - } -} - -// skill+step, checking for max value -bool Player::UpdateSkill(uint32 skill_id, uint32 step) -{ - if (!skill_id) - return false; - - if (skill_id == SKILL_FIST_WEAPONS) - skill_id = SKILL_UNARMED; - - SkillStatusMap::iterator itr = mSkillStatus.find(skill_id); - if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED) - return false; - - uint16 field = itr->second.pos / 2; - uint8 offset = itr->second.pos & 1; // itr->second.pos % 2 - - uint16 value = GetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset); - uint16 max = GetUInt16Value(PLAYER_SKILL_MAX_RANK_0 + field, offset); - - if (!max || !value || value >= max) - return false; - - uint32 new_value = value + step; - if (new_value > max) - new_value = max; - - SetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset, new_value); - if (itr->second.uState != SKILL_NEW) - itr->second.uState = SKILL_CHANGED; - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL, skill_id); - - return true; -} - -inline int SkillGainChance(uint32 SkillValue, uint32 GrayLevel, uint32 GreenLevel, uint32 YellowLevel) -{ - if (SkillValue >= GrayLevel) - return sWorld.getConfig(CONFIG_UINT32_SKILL_CHANCE_GREY) * 10; - if (SkillValue >= GreenLevel) - return sWorld.getConfig(CONFIG_UINT32_SKILL_CHANCE_GREEN) * 10; - if (SkillValue >= YellowLevel) - return sWorld.getConfig(CONFIG_UINT32_SKILL_CHANCE_YELLOW) * 10; - return sWorld.getConfig(CONFIG_UINT32_SKILL_CHANCE_ORANGE) * 10; -} - -bool Player::UpdateCraftSkill(uint32 spellid) -{ - DEBUG_LOG("UpdateCraftSkill spellid %d", spellid); - - SkillLineAbilityMapBounds bounds = sSpellMgr.GetSkillLineAbilityMapBounds(spellid); - - for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx) - { - if (_spell_idx->second->skillId) - { - uint32 SkillValue = GetPureSkillValue(_spell_idx->second->skillId); - - // Alchemy Discoveries here - SpellEntry const* spellEntry = sSpellStore.LookupEntry(spellid); - if (spellEntry && spellEntry->GetMechanic() == MECHANIC_DISCOVERY) - { - if (uint32 discoveredSpell = GetSkillDiscoverySpell(_spell_idx->second->skillId, spellid, this)) - learnSpell(discoveredSpell, false); - } - - uint32 craft_skill_gain = sWorld.getConfig(CONFIG_UINT32_SKILL_GAIN_CRAFTING); - - return UpdateSkillPro(_spell_idx->second->skillId, SkillGainChance(SkillValue, - _spell_idx->second->max_value, - (_spell_idx->second->max_value + _spell_idx->second->min_value) / 2, - _spell_idx->second->min_value), - craft_skill_gain); - } - } - return false; -} - -bool Player::UpdateGatherSkill(uint32 SkillId, uint32 SkillValue, uint32 RedLevel, uint32 Multiplicator) -{ - DEBUG_LOG("UpdateGatherSkill(SkillId %d SkillLevel %d RedLevel %d)", SkillId, SkillValue, RedLevel); - - uint32 gathering_skill_gain = sWorld.getConfig(CONFIG_UINT32_SKILL_GAIN_GATHERING); - - // For skinning and Mining chance decrease with level. 1-74 - no decrease, 75-149 - 2 times, 225-299 - 8 times - switch (SkillId) - { - case SKILL_HERBALISM: - case SKILL_JEWELCRAFTING: - case SKILL_INSCRIPTION: - return UpdateSkillPro(SkillId, SkillGainChance(SkillValue, RedLevel + 100, RedLevel + 50, RedLevel + 25) * Multiplicator, gathering_skill_gain); - case SKILL_SKINNING: - if (sWorld.getConfig(CONFIG_UINT32_SKILL_CHANCE_SKINNING_STEPS) == 0) - return UpdateSkillPro(SkillId, SkillGainChance(SkillValue, RedLevel + 100, RedLevel + 50, RedLevel + 25) * Multiplicator, gathering_skill_gain); - else - return UpdateSkillPro(SkillId, (SkillGainChance(SkillValue, RedLevel + 100, RedLevel + 50, RedLevel + 25) * Multiplicator) >> (SkillValue / sWorld.getConfig(CONFIG_UINT32_SKILL_CHANCE_SKINNING_STEPS)), gathering_skill_gain); - case SKILL_MINING: - if (sWorld.getConfig(CONFIG_UINT32_SKILL_CHANCE_MINING_STEPS) == 0) - return UpdateSkillPro(SkillId, SkillGainChance(SkillValue, RedLevel + 100, RedLevel + 50, RedLevel + 25) * Multiplicator, gathering_skill_gain); - else - return UpdateSkillPro(SkillId, (SkillGainChance(SkillValue, RedLevel + 100, RedLevel + 50, RedLevel + 25) * Multiplicator) >> (SkillValue / sWorld.getConfig(CONFIG_UINT32_SKILL_CHANCE_MINING_STEPS)), gathering_skill_gain); - } - return false; -} - -bool Player::UpdateFishingSkill() -{ - DEBUG_LOG("UpdateFishingSkill"); - - uint32 SkillValue = GetPureSkillValue(SKILL_FISHING); - - int32 chance = SkillValue < 75 ? 100 : 2500 / (SkillValue - 50); - - uint32 gathering_skill_gain = sWorld.getConfig(CONFIG_UINT32_SKILL_GAIN_GATHERING); - - return UpdateSkillPro(SKILL_FISHING, chance * 10, gathering_skill_gain); -} - -// levels sync. with spell requirement for skill levels to learn -// bonus abilities in sSkillLineAbilityStore -// Used only to avoid scan DBC at each skill grow -static uint32 bonusSkillLevels[] = {75, 150, 225, 300, 375, 450, 525}; - -bool Player::UpdateSkillPro(uint16 SkillId, int32 Chance, uint32 step) -{ - DEBUG_LOG("UpdateSkillPro(SkillId %d, Chance %3.1f%%)", SkillId, Chance / 10.0); - if (!SkillId) - return false; - - if (Chance <= 0) // speedup in 0 chance case - { - DEBUG_LOG("Player::UpdateSkillPro Chance=%3.1f%% missed", Chance / 10.0); - return false; - } - - SkillStatusMap::iterator itr = mSkillStatus.find(SkillId); - if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED) - return false; - - uint16 field = itr->second.pos / 2; - uint8 offset = itr->second.pos & 1; // itr->second.pos % 2 - - uint16 SkillValue = GetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset); - uint16 MaxValue = GetUInt16Value(PLAYER_SKILL_MAX_RANK_0 + field, offset); - - if (!MaxValue || !SkillValue || SkillValue >= MaxValue) - return false; - - int32 Roll = irand(1, 1000); - - if (Roll <= Chance) - { - uint16 new_value = SkillValue + step; - if (new_value > MaxValue) - new_value = MaxValue; - - SetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset, new_value); - if (itr->second.uState != SKILL_NEW) - itr->second.uState = SKILL_CHANGED; - for (uint32* bsl = &bonusSkillLevels[0]; *bsl; ++bsl) - { - if (SkillValue < *bsl && new_value >= *bsl) - { - learnSkillRewardedSpells(SkillId, new_value); - break; - } - } - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL, SkillId); - DEBUG_LOG("Player::UpdateSkillPro Chance=%3.1f%% taken", Chance / 10.0); - return true; - } - - DEBUG_LOG("Player::UpdateSkillPro Chance=%3.1f%% missed", Chance / 10.0); - return false; -} - -void Player::ModifySkillBonus(uint32 skillid, int32 val, bool talent) -{ - SkillStatusMap::const_iterator itr = mSkillStatus.find(skillid); - if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED) - return; - - uint16 field = itr->second.pos / 2 + (talent ? PLAYER_SKILL_TALENT_0 : PLAYER_SKILL_MODIFIER_0); - uint8 offset = itr->second.pos & 1; // itr->second.pos % 2 - - uint16 bonus = GetUInt16Value(field, offset); - - SetUInt16Value(field, offset, bonus + val); -} - -void Player::UpdateSkillsForLevel() -{ - uint32 maxSkill = GetMaxSkillValueForLevel(); - - for (SkillStatusMap::iterator itr = mSkillStatus.begin(); itr != mSkillStatus.end(); ++itr) - { - if (itr->second.uState == SKILL_DELETED) - continue; - - uint32 pskill = itr->first; - - SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(pskill); - if (!pSkill) - continue; - - if (GetSkillRangeType(pSkill, false) != SKILL_RANGE_LEVEL) - continue; - // only weapons skills curently have SKILL_RANGE_LEVEL - - uint16 field = itr->second.pos / 2; - uint8 offset = itr->second.pos & 1; // itr->second.pos % 2 - - uint16 max = GetUInt16Value(PLAYER_SKILL_MAX_RANK_0 + field, offset); - - /// update only level dependent max skill values - if (max != 1) - { - SetUInt16Value(PLAYER_SKILL_MAX_RANK_0 + field, offset, maxSkill); - if (itr->second.uState != SKILL_NEW) - itr->second.uState = SKILL_CHANGED; - } - } -} - -void Player::UpdateSkillsToMaxSkillsForLevel() -{ - for (SkillStatusMap::iterator itr = mSkillStatus.begin(); itr != mSkillStatus.end(); ++itr) - { - if (itr->second.uState == SKILL_DELETED) - continue; - - uint32 pskill = itr->first; - if (IsProfessionOrRidingSkill(pskill)) - continue; - uint16 field = itr->second.pos / 2; - uint8 offset = itr->second.pos & 1; // itr->second.pos % 2 - - uint16 max = GetUInt16Value(PLAYER_SKILL_MAX_RANK_0 + field, offset); - - if (max > 1) - { - SetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset, max); - if (itr->second.uState != SKILL_NEW) - itr->second.uState = SKILL_CHANGED; - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL, pskill); - } - } -} - -// This functions sets a skill line value (and adds if doesn't exist yet) -// To "remove" a skill line, set it's values to zero -void Player::SetSkill(uint16 id, uint16 currVal, uint16 maxVal, uint16 step /*=0*/) -{ - if (!id) - return; - - uint16 oldVal; - SkillStatusMap::iterator itr = mSkillStatus.find(id); - - // has skill - if (itr != mSkillStatus.end() && itr->second.uState != SKILL_DELETED) - { - uint16 field = itr->second.pos / 2; - uint8 offset = itr->second.pos & 1; // itr->second.pos % 2 - oldVal = GetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset); - if (currVal) - { - SetUInt16Value(PLAYER_SKILL_STEP_0 + field, offset, step); - // update value - SetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset, currVal); - SetUInt16Value(PLAYER_SKILL_MAX_RANK_0 + field, offset, maxVal); - if (itr->second.uState != SKILL_NEW) - itr->second.uState = SKILL_CHANGED; - learnSkillRewardedSpells(id, oldVal); - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL, id); - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL, id); - } - else // remove - { - // clear skill fields - SetUInt16Value(PLAYER_SKILL_LINEID_0 + field, offset, 0); - SetUInt16Value(PLAYER_SKILL_STEP_0 + field, offset, 0); - SetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset, 0); - SetUInt16Value(PLAYER_SKILL_MAX_RANK_0 + field, offset, 0); - SetUInt16Value(PLAYER_SKILL_MODIFIER_0 + field, offset, 0); - SetUInt16Value(PLAYER_SKILL_TALENT_0 + field, offset, 0); - - // mark as deleted or simply remove from map if not saved yet - if (itr->second.uState != SKILL_NEW) - itr->second.uState = SKILL_DELETED; - else - mSkillStatus.erase(itr); - - // remove all spells that related to this skill - for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j) - if (SkillLineAbilityEntry const* pAbility = sSkillLineAbilityStore.LookupEntry(j)) - if (pAbility->skillId == id) - removeSpell(sSpellMgr.GetFirstSpellInChain(pAbility->spellId)); - } - } - else if (currVal) // add - { - for (int i = 0; i < PLAYER_MAX_SKILLS; ++i) - { - uint16 field = i / 2; - uint8 offset = i & 1; // i % 2 - - if (!GetUInt16Value(PLAYER_SKILL_LINEID_0 + field, offset)) - { - SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(id); - if (!pSkill) - { - sLog.outError("Skill not found in SkillLineStore: skill #%u", id); - return; - } - - SetUInt16Value(PLAYER_SKILL_LINEID_0 + field, offset, id); - SetUInt16Value(PLAYER_SKILL_STEP_0 + field, offset, step); - SetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset, currVal); - SetUInt16Value(PLAYER_SKILL_MAX_RANK_0 + field, offset, maxVal); - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL, id); - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL, id); - - // insert new entry or update if not deleted old entry yet - if (itr != mSkillStatus.end()) - { - itr->second.pos = i; - itr->second.uState = SKILL_CHANGED; - } - else - mSkillStatus.insert(SkillStatusMap::value_type(id, SkillStatusData(i, SKILL_NEW))); - - // apply skill bonuses - SetUInt16Value(PLAYER_SKILL_MODIFIER_0 + field, offset, 0); - SetUInt16Value(PLAYER_SKILL_TALENT_0 + field, offset, 0); - - // temporary bonuses - AuraList const& mModSkill = GetAurasByType(SPELL_AURA_MOD_SKILL); - for (AuraList::const_iterator j = mModSkill.begin(); j != mModSkill.end(); ++j) - if ((*j)->GetModifier()->m_miscvalue == int32(id)) - (*j)->ApplyModifier(true); - - // permanent bonuses - AuraList const& mModSkillTalent = GetAurasByType(SPELL_AURA_MOD_SKILL_TALENT); - for (AuraList::const_iterator j = mModSkillTalent.begin(); j != mModSkillTalent.end(); ++j) - if ((*j)->GetModifier()->m_miscvalue == int32(id)) - (*j)->ApplyModifier(true); - - // Learn all spells for skill - learnSkillRewardedSpells(id, currVal); - return; - } - } - } -} - -bool Player::HasSkill(uint32 skill) const -{ - if (!skill) - return false; - - SkillStatusMap::const_iterator itr = mSkillStatus.find(skill); - return (itr != mSkillStatus.end() && itr->second.uState != SKILL_DELETED); -} - -uint16 Player::GetSkillStep(uint16 skill) const -{ - if (!skill) - return 0; - - SkillStatusMap::const_iterator itr = mSkillStatus.find(skill); - if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED) - return 0; - - return GetUInt16Value(PLAYER_SKILL_STEP_0 + itr->second.pos / 2, itr->second.pos & 1); -} - -uint16 Player::GetSkillValue(uint32 skill) const -{ - if (!skill) - return 0; - - SkillStatusMap::const_iterator itr = mSkillStatus.find(skill); - if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED) - return 0; - - uint16 field = itr->second.pos / 2; - uint8 offset = itr->second.pos & 1; - - int32 result = int32(GetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset)); - result += int32(GetUInt16Value(PLAYER_SKILL_MODIFIER_0 + field, offset)); - result += int32(GetUInt16Value(PLAYER_SKILL_TALENT_0 + field, offset)); - return result < 0 ? 0 : result; -} - -uint16 Player::GetMaxSkillValue(uint32 skill) const -{ - if (!skill) - return 0; - - SkillStatusMap::const_iterator itr = mSkillStatus.find(skill); - if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED) - return 0; - - uint16 field = itr->second.pos / 2; - uint8 offset = itr->second.pos & 1; - - int32 result = int32(GetUInt16Value(PLAYER_SKILL_MAX_RANK_0 + field, offset)); - result += int32(GetUInt16Value(PLAYER_SKILL_MODIFIER_0 + field, offset)); - result += int32(GetUInt16Value(PLAYER_SKILL_TALENT_0 + field, offset)); - return result < 0 ? 0 : result; -} - -uint16 Player::GetPureMaxSkillValue(uint32 skill) const -{ - if (!skill) - return 0; - - SkillStatusMap::const_iterator itr = mSkillStatus.find(skill); - if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED) - return 0; - - uint16 field = itr->second.pos / 2; - uint8 offset = itr->second.pos & 1; - - return GetUInt16Value(PLAYER_SKILL_MAX_RANK_0 + field, offset); -} - -uint16 Player::GetBaseSkillValue(uint32 skill) const -{ - if (!skill) - return 0; - - SkillStatusMap::const_iterator itr = mSkillStatus.find(skill); - if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED) - return 0; - - uint16 field = itr->second.pos / 2; - uint8 offset = itr->second.pos & 1; - - int32 result = int32(GetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset)); - result += int32(GetUInt16Value(PLAYER_SKILL_TALENT_0 + field, offset)); - return result < 0 ? 0 : result; -} - -uint16 Player::GetPureSkillValue(uint32 skill) const -{ - if (!skill) - return 0; - - SkillStatusMap::const_iterator itr = mSkillStatus.find(skill); - if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED) - return 0; - - uint16 field = itr->second.pos / 2; - uint8 offset = itr->second.pos & 1; - - return GetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset); -} - -int16 Player::GetSkillPermBonusValue(uint32 skill) const -{ - if (!skill) - return 0; - - SkillStatusMap::const_iterator itr = mSkillStatus.find(skill); - if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED) - return 0; - - uint16 field = itr->second.pos / 2; - uint8 offset = itr->second.pos & 1; - - return GetUInt16Value(PLAYER_SKILL_TALENT_0 + field, offset); -} - -int16 Player::GetSkillTempBonusValue(uint32 skill) const -{ - if (!skill) - return 0; - - SkillStatusMap::const_iterator itr = mSkillStatus.find(skill); - if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED) - return 0; - - uint16 field = itr->second.pos / 2; - uint8 offset = itr->second.pos & 1; - - return GetUInt16Value(PLAYER_SKILL_MODIFIER_0 + field, offset); -} - -void Player::SendInitialActionButtons() const -{ - DETAIL_LOG("Initializing Action Buttons for '%u' spec '%u'", GetGUIDLow(), m_activeSpec); - - WorldPacket data(SMSG_ACTION_BUTTONS, 1 + (MAX_ACTION_BUTTONS * 4)); - ActionButtonList const& currentActionButtonList = m_actionButtons[m_activeSpec]; - for (uint8 button = 0; button < MAX_ACTION_BUTTONS; ++button) - { - ActionButtonList::const_iterator itr = currentActionButtonList.find(button); - if (itr != currentActionButtonList.end() && itr->second.uState != ACTIONBUTTON_DELETED) - data << uint32(itr->second.packedData); - else - data << uint32(0); - } - data << uint8(1); // talent spec amount (in packet) - GetSession()->SendPacket(&data); - DETAIL_LOG("Action Buttons for '%u' spec '%u' Initialized", GetGUIDLow(), m_activeSpec); -} - -void Player::SendLockActionButtons() const -{ - DETAIL_LOG("Locking Action Buttons for '%u' spec '%u'", GetGUIDLow(), m_activeSpec); - WorldPacket data(SMSG_ACTION_BUTTONS, 1); - // sending 2 locks actions bars, neither user can remove buttons, nor client removes buttons at spell unlearn - // they remain locked until server sends new action buttons - data << uint8(2); - GetSession()->SendPacket(&data); -} - -bool Player::IsActionButtonDataValid(uint8 button, uint32 action, uint8 type, Player* player, bool msg) -{ - if (button >= MAX_ACTION_BUTTONS) - { - if (msg) - { - if (player) - sLog.outError("Action %u not added into button %u for player %s: button must be < %u", action, button, player->GetName(), MAX_ACTION_BUTTONS); - else - sLog.outError("Table `playercreateinfo_action` have action %u into button %u : button must be < %u", action, button, MAX_ACTION_BUTTONS); - } - return false; - } - - if (action >= MAX_ACTION_BUTTON_ACTION_VALUE) - { - if (msg) - { - if (player) - sLog.outError("Action %u not added into button %u for player %s: action must be < %u", action, button, player->GetName(), MAX_ACTION_BUTTON_ACTION_VALUE); - else - sLog.outError("Table `playercreateinfo_action` have action %u into button %u : action must be < %u", action, button, MAX_ACTION_BUTTON_ACTION_VALUE); - } - return false; - } - - switch (type) - { - case ACTION_BUTTON_SPELL: - { - SpellEntry const* spellProto = sSpellStore.LookupEntry(action); - if (!spellProto) - { - if (msg) - { - if (player) - sLog.outError("Spell action %u not added into button %u for player %s: spell not exist", action, button, player->GetName()); - else - sLog.outError("Table `playercreateinfo_action` have spell action %u into button %u: spell not exist", action, button); - } - return false; - } - - if (player) - { - if (!player->HasSpell(spellProto->Id)) - { - if (msg) - sLog.outError("Spell action %u not added into button %u for player %s: player don't known this spell", action, button, player->GetName()); - return false; - } - else if (IsPassiveSpell(spellProto)) - { - if (msg) - sLog.outError("Spell action %u not added into button %u for player %s: spell is passive", action, button, player->GetName()); - return false; - } - // current range for button of totem bar is from ACTION_BUTTON_SHAMAN_TOTEMS_BAR to (but not including) ACTION_BUTTON_SHAMAN_TOTEMS_BAR + 12 - else if (button >= ACTION_BUTTON_SHAMAN_TOTEMS_BAR && button < (ACTION_BUTTON_SHAMAN_TOTEMS_BAR + 12) - && !spellProto->HasAttribute(SPELL_ATTR_EX7_TOTEM_SPELL)) - { - if (msg) - sLog.outError("Spell action %u not added into button %u for player %s: attempt to add non totem spell to totem bar", action, button, player->GetName()); - return false; - } - } - break; - } - case ACTION_BUTTON_ITEM: - { - if (!ObjectMgr::GetItemPrototype(action)) - { - if (msg) - { - if (player) - sLog.outError("Item action %u not added into button %u for player %s: item not exist", action, button, player->GetName()); - else - sLog.outError("Table `playercreateinfo_action` have item action %u into button %u: item not exist", action, button); - } - return false; - } - break; - } - default: - break; // other cases not checked at this moment - } - - return true; -} - -ActionButton* Player::addActionButton(uint8 spec, uint8 button, uint32 action, uint8 type) -{ - // check action only for active spec (so not check at copy/load passive spec) - if (spec == GetActiveSpec() && !IsActionButtonDataValid(button, action, type, this)) - return NULL; - - // it create new button (NEW state) if need or return existing - ActionButton& ab = m_actionButtons[spec][button]; - - // set data and update to CHANGED if not NEW - ab.SetActionAndType(action, ActionButtonType(type)); - - DETAIL_LOG("Player '%u' Added Action '%u' (type %u) to Button '%u' for spec %u", GetGUIDLow(), action, uint32(type), button, spec); - return &ab; -} - -void Player::removeActionButton(uint8 spec, uint8 button) -{ - ActionButtonList& currentActionButtonList = m_actionButtons[spec]; - ActionButtonList::iterator buttonItr = currentActionButtonList.find(button); - if (buttonItr == currentActionButtonList.end() || buttonItr->second.uState == ACTIONBUTTON_DELETED) - return; - - if (buttonItr->second.uState == ACTIONBUTTON_NEW) - currentActionButtonList.erase(buttonItr); // new and not saved - else - buttonItr->second.uState = ACTIONBUTTON_DELETED; // saved, will deleted at next save - - DETAIL_LOG("Action Button '%u' Removed from Player '%u' for spec %u", button, GetGUIDLow(), spec); -} - -ActionButton const* Player::GetActionButton(uint8 button) -{ - ActionButtonList& currentActionButtonList = m_actionButtons[m_activeSpec]; - ActionButtonList::iterator buttonItr = currentActionButtonList.find(button); - if (buttonItr == currentActionButtonList.end() || buttonItr->second.uState == ACTIONBUTTON_DELETED) - return NULL; - - return &buttonItr->second; -} - -bool Player::SetPosition(float x, float y, float z, float orientation, bool teleport) -{ - // prevent crash when a bad coord is sent by the client - if (!MaNGOS::IsValidMapCoord(x, y, z, orientation)) - { - DEBUG_LOG("Player::SetPosition(%f, %f, %f, %f, %d) .. bad coordinates for player %d!", x, y, z, orientation, teleport, GetGUIDLow()); - return false; - } - - Map* m = GetMap(); - - const float old_x = GetPositionX(); - const float old_y = GetPositionY(); - const float old_z = GetPositionZ(); - const float old_r = GetOrientation(); - - if (teleport || old_x != x || old_y != y || old_z != z || old_r != orientation) - { - if (teleport || old_x != x || old_y != y || old_z != z) - RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MOVE | AURA_INTERRUPT_FLAG_TURNING); - else - RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TURNING); - - RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - - // move and update visible state if need - m->PlayerRelocation(this, x, y, z, orientation); - - // reread after Map::Relocation - m = GetMap(); - x = GetPositionX(); - y = GetPositionY(); - z = GetPositionZ(); - - // group update - if (GetGroup() && (old_x != x || old_y != y)) - SetGroupUpdateFlag(GROUP_UPDATE_FLAG_POSITION); - - if (GetTrader() && !IsWithinDistInMap(GetTrader(), INTERACTION_DISTANCE)) - GetSession()->SendCancelTrade(); // will close both side trade windows - } - - if (m_positionStatusUpdateTimer) // Update position's state only on interval - return true; - m_positionStatusUpdateTimer = 100; - - // code block for underwater state update - UpdateUnderwaterState(m, x, y, z); - - // code block for outdoor state and area-explore check - CheckAreaExploreAndOutdoor(); - - return true; -} - -void Player::SaveRecallPosition() -{ - m_recallMap = GetMapId(); - m_recallX = GetPositionX(); - m_recallY = GetPositionY(); - m_recallZ = GetPositionZ(); - m_recallO = GetOrientation(); -} - -void Player::SendMessageToSet(WorldPacket* data, bool self) const -{ - if (IsInWorld()) - GetMap()->MessageBroadcast(this, data, false); - - // if player is not in world and map in not created/already destroyed - // no need to create one, just send packet for itself! - if (self) - GetSession()->SendPacket(data); -} - -void Player::SendMessageToSetInRange(WorldPacket* data, float dist, bool self) const -{ - if (IsInWorld()) - GetMap()->MessageDistBroadcast(this, data, dist, false); - - if (self) - GetSession()->SendPacket(data); -} - -void Player::SendMessageToSetInRange(WorldPacket* data, float dist, bool self, bool own_team_only) const -{ - if (IsInWorld()) - GetMap()->MessageDistBroadcast(this, data, dist, false, own_team_only); - - if (self) - GetSession()->SendPacket(data); -} - -void Player::SendDirectMessage(WorldPacket* data) const -{ - GetSession()->SendPacket(data); -} - -void Player::SendCinematicStart(uint32 CinematicSequenceId) -{ - WorldPacket data(SMSG_TRIGGER_CINEMATIC, 4); - data << uint32(CinematicSequenceId); - SendDirectMessage(&data); -} - -void Player::SendMovieStart(uint32 MovieId) -{ - WorldPacket data(SMSG_TRIGGER_MOVIE, 4); - data << uint32(MovieId); - SendDirectMessage(&data); -} - -void Player::CheckAreaExploreAndOutdoor() -{ - if (!isAlive()) - return; - - if (IsTaxiFlying()) - return; - - bool isOutdoor; - uint16 areaFlag = GetTerrain()->GetAreaFlag(GetPositionX(), GetPositionY(), GetPositionZ(), &isOutdoor); - - if (isOutdoor) - { - if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) && GetRestType() == REST_TYPE_IN_TAVERN) - { - AreaTriggerEntry const* at = sAreaTriggerStore.LookupEntry(inn_trigger_id); - if (!at || !IsPointInAreaTriggerZone(at, GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ())) - { - // Player left inn (REST_TYPE_IN_CITY overrides REST_TYPE_IN_TAVERN, so just clear rest) - SetRestType(REST_TYPE_NO); - } - } - // Check if we need to reaply outdoor only passive spells - const PlayerSpellMap& sp_list = GetSpellMap(); - for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr) - { - if (itr->second.state == PLAYERSPELL_REMOVED) - continue; - SpellEntry const* spellInfo = sSpellStore.LookupEntry(itr->first); - if (!spellInfo || !IsNeedCastSpellAtOutdoor(spellInfo) || HasAura(itr->first)) - continue; - CastSpell(this, itr->first, true, NULL); - } - } - else if (sWorld.getConfig(CONFIG_BOOL_VMAP_INDOOR_CHECK) && !isGameMaster()) - RemoveAurasWithAttribute(SPELL_ATTR_OUTDOORS_ONLY); - - if (areaFlag == 0xffff) - return; - int offset = areaFlag / 32; - - if (offset >= PLAYER_EXPLORED_ZONES_SIZE) - { - sLog.outError("Wrong area flag %u in map data for (X: %f Y: %f) point to field PLAYER_EXPLORED_ZONES_1 + %u ( %u must be < %u ).", areaFlag, GetPositionX(), GetPositionY(), offset, offset, PLAYER_EXPLORED_ZONES_SIZE); - return; - } - - uint32 val = (uint32)(1 << (areaFlag % 32)); - uint32 currFields = GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset); - - if (!(currFields & val)) - { - SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset, (uint32)(currFields | val)); - - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA); - - AreaTableEntry const* p = GetAreaEntryByAreaFlagAndMap(areaFlag, GetMapId()); - if (!p) - { - sLog.outError("PLAYER: Player %u discovered unknown area (x: %f y: %f map: %u", GetGUIDLow(), GetPositionX(), GetPositionY(), GetMapId()); - } - else if (p->area_level > 0) - { - uint32 area = p->ID; - if (getLevel() >= sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL)) - { - SendExplorationExperience(area, 0); - } - else - { - int32 diff = int32(getLevel()) - p->area_level; - uint32 XP = 0; - if (diff < -5) - { - XP = uint32(sObjectMgr.GetBaseXP(getLevel() + 5) * sWorld.getConfig(CONFIG_FLOAT_RATE_XP_EXPLORE)); - } - else if (diff > 5) - { - int32 exploration_percent = (100 - ((diff - 5) * 5)); - if (exploration_percent > 100) - exploration_percent = 100; - else if (exploration_percent < 0) - exploration_percent = 0; - - XP = uint32(sObjectMgr.GetBaseXP(p->area_level) * exploration_percent / 100 * sWorld.getConfig(CONFIG_FLOAT_RATE_XP_EXPLORE)); - } - else - { - XP = uint32(sObjectMgr.GetBaseXP(p->area_level) * sWorld.getConfig(CONFIG_FLOAT_RATE_XP_EXPLORE)); - } - - GiveXP(XP, NULL); - SendExplorationExperience(area, XP); - } - DETAIL_LOG("PLAYER: Player %u discovered a new area: %u", GetGUIDLow(), area); - } - } -} - -Team Player::TeamForRace(uint8 race) -{ - ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(race); - if (!rEntry) - { - sLog.outError("Race %u not found in DBC: wrong DBC files?", uint32(race)); - return ALLIANCE; - } - - switch (rEntry->TeamID) - { - case 7: return ALLIANCE; - case 1: return HORDE; - } - - sLog.outError("Race %u have wrong teamid %u in DBC: wrong DBC files?", uint32(race), rEntry->TeamID); - return TEAM_NONE; -} - -uint32 Player::getFactionForRace(uint8 race) -{ - ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(race); - if (!rEntry) - { - sLog.outError("Race %u not found in DBC: wrong DBC files?", uint32(race)); - return 0; - } - - return rEntry->FactionID; -} - -void Player::setFactionForRace(uint8 race) -{ - m_team = TeamForRace(race); - setFaction(getFactionForRace(race)); -} - -ReputationRank Player::GetReputationRank(uint32 faction) const -{ - FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction); - return GetReputationMgr().GetRank(factionEntry); -} - -// Calculate total reputation percent player gain with quest/creature level -int32 Player::CalculateReputationGain(ReputationSource source, int32 rep, int32 faction, uint32 creatureOrQuestLevel, bool noAuraBonus) -{ - float percent = 100.0f; - - float repMod = noAuraBonus ? 0.0f : (float)GetTotalAuraModifier(SPELL_AURA_MOD_REPUTATION_GAIN); - - // faction specific auras only seem to apply to kills - if (source == REPUTATION_SOURCE_KILL) - repMod += GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_FACTION_REPUTATION_GAIN, faction); - - percent += rep > 0 ? repMod : -repMod; - - float rate; - switch (source) - { - case REPUTATION_SOURCE_KILL: - rate = sWorld.getConfig(CONFIG_FLOAT_RATE_REPUTATION_LOWLEVEL_KILL); - break; - case REPUTATION_SOURCE_QUEST: - rate = sWorld.getConfig(CONFIG_FLOAT_RATE_REPUTATION_LOWLEVEL_QUEST); - break; - case REPUTATION_SOURCE_SPELL: - default: - rate = 1.0f; - break; - } - - if (rate != 1.0f && creatureOrQuestLevel <= MaNGOS::XP::GetGrayLevel(getLevel())) - percent *= rate; - - if (percent <= 0.0f) - return 0; - - // Multiply result with the faction specific rate - if (const RepRewardRate* repData = sObjectMgr.GetRepRewardRate(faction)) - { - float repRate = 0.0f; - switch (source) - { - case REPUTATION_SOURCE_KILL: - repRate = repData->creature_rate; - break; - case REPUTATION_SOURCE_QUEST: - repRate = repData->quest_rate; - break; - case REPUTATION_SOURCE_SPELL: - repRate = repData->spell_rate; - break; - } - - // for custom, a rate of 0.0 will totally disable reputation gain for this faction/type - if (repRate <= 0.0f) - return 0; - - percent *= repRate; - } - - return int32(sWorld.getConfig(CONFIG_FLOAT_RATE_REPUTATION_GAIN) * rep * percent / 100.0f); -} - -// Calculates how many reputation points player gains in victim's enemy factions -void Player::RewardReputation(Unit* pVictim, float rate) -{ - if (!pVictim || pVictim->GetTypeId() == TYPEID_PLAYER) - return; - - // used current difficulty creature entry instead normal version (GetEntry()) - ReputationOnKillEntry const* Rep = sObjectMgr.GetReputationOnKillEntry(((Creature*)pVictim)->GetCreatureInfo()->Entry); - - if (!Rep) - return; - - uint32 repFaction1 = Rep->repfaction1; - uint32 repFaction2 = Rep->repfaction2; - - // Championning tabard reputation system - // Aura 57818 is a hidden aura common to tabards allowing championning. - if (pVictim->GetMap()->IsNonRaidDungeon() && HasAura(57818)) - { - MapEntry const* storedMap = sMapStore.LookupEntry(GetMapId()); - InstanceTemplate const* instance = ObjectMgr::GetInstanceTemplate(GetMapId()); - Item const* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_TABARD); - if (storedMap && instance && pItem) - { - ItemPrototype const* pProto = pItem->GetProto();// Checked on load - // The required MinLevel for the tabard to work is related to the item level of the tabard - if ((instance->levelMin + 1 >= pProto->ItemLevel || !GetMap()->IsRegularDifficulty()) - // For ItemLevel == 75 (or 85) need to check expansion - && (pProto->ItemLevel == 75 && storedMap->Expansion() == EXPANSION_WOTLK)) - { - if (uint32 tabardFactionID = pItem->GetProto()->RequiredReputationFaction) - { - repFaction1 = tabardFactionID; - repFaction2 = tabardFactionID; - } - } - } - } - - if (repFaction1 && (!Rep->team_dependent || GetTeam() == ALLIANCE)) - { - int32 donerep1 = CalculateReputationGain(REPUTATION_SOURCE_KILL, Rep->repvalue1, repFaction1, pVictim->getLevel()); - donerep1 = int32(donerep1 * rate); - FactionEntry const* factionEntry1 = sFactionStore.LookupEntry(repFaction1); - uint32 current_reputation_rank1 = GetReputationMgr().GetRank(factionEntry1); - if (factionEntry1 && current_reputation_rank1 <= Rep->reputation_max_cap1) - GetReputationMgr().ModifyReputation(factionEntry1, donerep1); - - // Wiki: Team factions value divided by 2 - if (factionEntry1 && Rep->is_teamaward1) - { - FactionEntry const* team1_factionEntry = sFactionStore.LookupEntry(factionEntry1->team); - if (team1_factionEntry) - GetReputationMgr().ModifyReputation(team1_factionEntry, donerep1 / 2); - } - } - - if (repFaction2 && (!Rep->team_dependent || GetTeam() == HORDE)) - { - int32 donerep2 = CalculateReputationGain(REPUTATION_SOURCE_KILL, Rep->repvalue2, repFaction2, pVictim->getLevel()); - donerep2 = int32(donerep2 * rate); - FactionEntry const* factionEntry2 = sFactionStore.LookupEntry(repFaction2); - uint32 current_reputation_rank2 = GetReputationMgr().GetRank(factionEntry2); - if (factionEntry2 && current_reputation_rank2 <= Rep->reputation_max_cap2) - GetReputationMgr().ModifyReputation(factionEntry2, donerep2); - - // Wiki: Team factions value divided by 2 - if (factionEntry2 && Rep->is_teamaward2) - { - FactionEntry const* team2_factionEntry = sFactionStore.LookupEntry(factionEntry2->team); - if (team2_factionEntry) - GetReputationMgr().ModifyReputation(team2_factionEntry, donerep2 / 2); - } - } -} - -// Calculate how many reputation points player gain with the quest -void Player::RewardReputation(Quest const* pQuest) -{ - // quest reputation reward/loss - for (int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i) - { - if (!pQuest->RewRepFaction[i]) - continue; - - // No diplomacy mod are applied to the final value (flat). Note the formula (finalValue = DBvalue/100) - if (pQuest->RewRepValue[i]) - { - int32 rep = CalculateReputationGain(REPUTATION_SOURCE_QUEST, pQuest->RewRepValue[i] / 100, pQuest->RewRepFaction[i], GetQuestLevelForPlayer(pQuest), true); - - if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(pQuest->RewRepFaction[i])) - GetReputationMgr().ModifyReputation(factionEntry, rep); - } - else - { - uint32 row = ((pQuest->RewRepValueId[i] < 0) ? 1 : 0) + 1; - uint32 field = abs(pQuest->RewRepValueId[i]); - - if (const QuestFactionRewardEntry* pRow = sQuestFactionRewardStore.LookupEntry(row)) - { - int32 repPoints = pRow->rewardValue[field]; - - if (!repPoints) - continue; - - repPoints = CalculateReputationGain(REPUTATION_SOURCE_QUEST, repPoints, pQuest->RewRepFaction[i], GetQuestLevelForPlayer(pQuest)); - - if (const FactionEntry* factionEntry = sFactionStore.LookupEntry(pQuest->RewRepFaction[i])) - GetReputationMgr().ModifyReputation(factionEntry, repPoints); - } - } - } - - // TODO: implement reputation spillover -} - -void Player::UpdateHonorKills() -{ - /// called when rewarding honor and at each save - time_t now = time(NULL); - time_t today = (time(NULL) / DAY) * DAY; - - if (m_lastHonorKillsUpdateTime < today) - { - time_t yesterday = today - DAY; - - uint16 kills_today = GetUInt16Value(PLAYER_FIELD_KILLS, 0); - - // update yesterday's contribution - if (m_lastHonorKillsUpdateTime >= yesterday) - { - // this is the first update today, reset today's contribution - SetUInt16Value(PLAYER_FIELD_KILLS, 0, 0); - SetUInt16Value(PLAYER_FIELD_KILLS, 1, kills_today); - } - else - { - // no honor/kills yesterday or today, reset - SetUInt32Value(PLAYER_FIELD_KILLS, 0); - } - } - - m_lastHonorKillsUpdateTime = now; -} - -/// Calculate the amount of honor gained based on the victim -/// and the size of the group for which the honor is divided -/// An exact honor value can also be given (overriding the calcs) -bool Player::RewardHonor(Unit* uVictim, uint32 groupsize, float honor) -{ - // do not reward honor in arenas, but enable onkill spellproc - if (InArena()) - { - if (!uVictim || uVictim == this || uVictim->GetTypeId() != TYPEID_PLAYER) - return false; - - if (GetBGTeam() == ((Player*)uVictim)->GetBGTeam()) - return false; - - return true; - } - - // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens - if (GetDummyAura(SPELL_AURA_PLAYER_INACTIVE)) - return false; - - ObjectGuid victim_guid; - uint32 victim_rank = 0; - - // need call before fields update to have chance move yesterday data to appropriate fields before today data change. - UpdateHonorKills(); - - if (honor <= 0) - { - if (!uVictim || uVictim == this || uVictim->HasAuraType(SPELL_AURA_NO_PVP_CREDIT)) - return false; - - victim_guid = uVictim->GetObjectGuid(); - - if (uVictim->GetTypeId() == TYPEID_PLAYER) - { - Player* pVictim = (Player*)uVictim; - - if (GetTeam() == pVictim->GetTeam() && !sWorld.IsFFAPvPRealm()) - return false; - - float f = 1; // need for total kills (?? need more info) - uint32 k_grey = 0; - uint32 k_level = getLevel(); - uint32 v_level = pVictim->getLevel(); - - { - // PLAYER_CHOSEN_TITLE VALUES DESCRIPTION - // [0] Just name - // [1..14] Alliance honor titles and player name - // [15..28] Horde honor titles and player name - // [29..38] Other title and player name - // [39+] Nothing - uint32 victim_title = pVictim->GetUInt32Value(PLAYER_CHOSEN_TITLE); - // Get Killer titles, CharTitlesEntry::bit_index - // Ranks: - // title[1..14] -> rank[5..18] - // title[15..28] -> rank[5..18] - // title[other] -> 0 - if (victim_title == 0) - victim_guid.Clear(); // Don't show HK: message, only log. - else if (victim_title < 15) - victim_rank = victim_title + 4; - else if (victim_title < 29) - victim_rank = victim_title - 14 + 4; - else - victim_guid.Clear(); // Don't show HK: message, only log. - } - - k_grey = MaNGOS::XP::GetGrayLevel(k_level); - - if (v_level <= k_grey) - return false; - - float diff_level = (k_level == k_grey) ? 1 : ((float(v_level) - float(k_grey)) / (float(k_level) - float(k_grey))); - - int32 v_rank = 1; // need more info - - honor = ((f * diff_level * (190 + v_rank * 10)) / 6); - honor *= float(k_level) / 70.0f; // factor of dependence on levels of the killer - - // count the number of playerkills in one day - ApplyModUInt32Value(PLAYER_FIELD_KILLS, 1, true); - // and those in a lifetime - ApplyModUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS, 1, true); - UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL); - UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS, pVictim->getClass()); - UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HK_RACE, pVictim->getRace()); - } - else - { - Creature* cVictim = (Creature*)uVictim; - - if (!cVictim->IsRacialLeader()) - return false; - - honor = 100; // ??? need more info - victim_rank = 19; // HK: Leader - } - } - - if (uVictim != NULL) - { - honor *= sWorld.getConfig(CONFIG_FLOAT_RATE_HONOR); - honor *= (GetMaxPositiveAuraModifier(SPELL_AURA_MOD_HONOR_GAIN) + 100.0f) / 100.0f; - - if (groupsize > 1) - honor /= groupsize; - - honor *= (((float)urand(8, 12)) / 10); // approx honor: 80% - 120% of real honor - } - - // honor - for show honor points in log - // victim_guid - for show victim name in log - // victim_rank [1..4] HK: - // victim_rank [5..19] HK: - // victim_rank [0,20+] HK: <> - WorldPacket data(SMSG_PVP_CREDIT, 4 + 8 + 4); - data << uint32(honor); - data << ObjectGuid(victim_guid); - data << uint32(victim_rank); - GetSession()->SendPacket(&data); - - // add honor points - ModifyCurrencyCount(CURRENCY_HONOR_POINTS, int32(honor)); - - return true; -} - -void Player::SetInGuild(uint32 GuildId) -{ - if (GuildId) - SetGuidValue(OBJECT_FIELD_DATA, ObjectGuid(HIGHGUID_GUILD, 0, GuildId)); - else - SetGuidValue(OBJECT_FIELD_DATA, ObjectGuid()); - - ApplyModFlag(PLAYER_FLAGS, PLAYER_FLAGS_GUILD_LEVELING_ENABLED, GuildId != 0 && sWorld.getConfig(CONFIG_BOOL_GUILD_LEVELING_ENABLED)); - SetUInt16Value(OBJECT_FIELD_TYPE, 1, GuildId != 0); -} - -std::string Player::GetGuildName() const -{ - if (Guild* guild = sGuildMgr.GetGuildById(GetGuildId())) - return guild->GetName(); - - return ""; -} - -uint32 Player::GetGuildIdFromDB(ObjectGuid guid) -{ - uint32 lowguid = guid.GetCounter(); - - QueryResult* result = CharacterDatabase.PQuery("SELECT guildid FROM guild_member WHERE guid='%u'", lowguid); - if (!result) - return 0; - - uint32 id = result->Fetch()[0].GetUInt32(); - delete result; - return id; -} - -ObjectGuid Player::GetGuildGuidFromDB(ObjectGuid guid) -{ - if (uint32 guildId = GetGuildIdFromDB(guid)) - return ObjectGuid(HIGHGUID_GUILD, GetGuildIdFromDB(guid)); - else - return ObjectGuid(); -} - -uint32 Player::GetRankFromDB(ObjectGuid guid) -{ - QueryResult* result = CharacterDatabase.PQuery("SELECT rank FROM guild_member WHERE guid='%u'", guid.GetCounter()); - if (result) - { - uint32 v = result->Fetch()[0].GetUInt32(); - delete result; - return v; - } - else - return 0; -} - -void Player::SendGuildDeclined(std::string name, bool autodecline) -{ - WorldPacket data(SMSG_GUILD_DECLINE, 10); - data << name; - data << uint8(autodecline); - GetSession()->SendPacket(&data); -} - - -uint32 Player::GetArenaTeamIdFromDB(ObjectGuid guid, ArenaType type) -{ - QueryResult* result = CharacterDatabase.PQuery("SELECT arena_team_member.arenateamid FROM arena_team_member JOIN arena_team ON arena_team_member.arenateamid = arena_team.arenateamid WHERE guid='%u' AND type='%u' LIMIT 1", guid.GetCounter(), type); - if (!result) - return 0; - - uint32 id = (*result)[0].GetUInt32(); - delete result; - return id; -} - -uint32 Player::GetZoneIdFromDB(ObjectGuid guid) -{ - uint32 lowguid = guid.GetCounter(); - QueryResult* result = CharacterDatabase.PQuery("SELECT zone FROM characters WHERE guid='%u'", lowguid); - if (!result) - return 0; - Field* fields = result->Fetch(); - uint32 zone = fields[0].GetUInt32(); - delete result; - - if (!zone) - { - // stored zone is zero, use generic and slow zone detection - result = CharacterDatabase.PQuery("SELECT map,position_x,position_y,position_z FROM characters WHERE guid='%u'", lowguid); - if (!result) - return 0; - fields = result->Fetch(); - uint32 map = fields[0].GetUInt32(); - float posx = fields[1].GetFloat(); - float posy = fields[2].GetFloat(); - float posz = fields[3].GetFloat(); - delete result; - - zone = sTerrainMgr.GetZoneId(map, posx, posy, posz); - - if (zone > 0) - CharacterDatabase.PExecute("UPDATE characters SET zone='%u' WHERE guid='%u'", zone, lowguid); - } - - return zone; -} - -uint32 Player::GetLevelFromDB(ObjectGuid guid) -{ - uint32 lowguid = guid.GetCounter(); - - QueryResult* result = CharacterDatabase.PQuery("SELECT level FROM characters WHERE guid='%u'", lowguid); - if (!result) - return 0; - - Field* fields = result->Fetch(); - uint32 level = fields[0].GetUInt32(); - delete result; - - return level; -} - -void Player::UpdateArea(uint32 newArea) -{ - m_areaUpdateId = newArea; - - AreaTableEntry const* area = GetAreaEntryByAreaID(newArea); - - // FFA_PVP flags are area and not zone id dependent - // so apply them accordingly - if (area && (area->flags & AREA_FLAG_ARENA)) - { - if (!isGameMaster()) - SetFFAPvP(true); - } - else - { - // remove ffa flag only if not ffapvp realm - // removal in sanctuaries and capitals is handled in zone update - if (IsFFAPvP() && !sWorld.IsFFAPvPRealm()) - SetFFAPvP(false); - } - - phaseMgr->AddUpdateFlag(PHASE_UPDATE_FLAG_AREA_UPDATE); - - UpdateAreaDependentAuras(); - - phaseMgr->RemoveUpdateFlag(PHASE_UPDATE_FLAG_AREA_UPDATE); -} - -bool Player::CanUseCapturePoint() -{ - return isAlive() && // living - !HasStealthAura() && // not stealthed - !HasInvisibilityAura() && // visible - (IsPvP() || sWorld.IsPvPRealm()) && - !HasMovementFlag(MOVEFLAG_FLYING) && - !IsTaxiFlying() && - !isGameMaster(); -} - -void Player::UpdateZone(uint32 newZone, uint32 newArea) -{ - AreaTableEntry const* zone = GetAreaEntryByAreaID(newZone); - if (!zone) - return; - - phaseMgr->AddUpdateFlag(PHASE_UPDATE_FLAG_ZONE_UPDATE); - - if (m_zoneUpdateId != newZone) - { - // handle outdoor pvp zones - sOutdoorPvPMgr.HandlePlayerLeaveZone(this, m_zoneUpdateId); - sOutdoorPvPMgr.HandlePlayerEnterZone(this, newZone); - - SendInitWorldStates(newZone, newArea); // only if really enters to new zone, not just area change, works strange... - - if (sWorld.getConfig(CONFIG_BOOL_WEATHER)) - { - if (Weather* wth = sWorld.FindWeather(zone->ID)) - wth->SendWeatherUpdateToPlayer(this); - else if (!sWorld.AddWeather(zone->ID)) - { - // send fine weather packet to remove old zone's weather - Weather::SendFineWeatherUpdateToPlayer(this); - } - } - } - - m_zoneUpdateId = newZone; - m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL; - - // zone changed, so area changed as well, update it - UpdateArea(newArea); - - // in PvP, any not controlled zone (except zone->team == 6, default case) - // in PvE, only opposition team capital - switch (zone->team) - { - case AREATEAM_ALLY: - pvpInfo.inHostileArea = GetTeam() != ALLIANCE && (sWorld.IsPvPRealm() || zone->flags & AREA_FLAG_CAPITAL); - break; - case AREATEAM_HORDE: - pvpInfo.inHostileArea = GetTeam() != HORDE && (sWorld.IsPvPRealm() || zone->flags & AREA_FLAG_CAPITAL); - break; - case AREATEAM_NONE: - // overwrite for battlegrounds, maybe batter some zone flags but current known not 100% fit to this - pvpInfo.inHostileArea = sWorld.IsPvPRealm() || InBattleGround(); - break; - default: // 6 in fact - pvpInfo.inHostileArea = false; - break; - } - - if (pvpInfo.inHostileArea) // in hostile area - { - if (!IsPvP() || pvpInfo.endTimer != 0) - UpdatePvP(true, true); - } - else // in friendly area - { - if (IsPvP() && !HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP) && pvpInfo.endTimer == 0) - pvpInfo.endTimer = time(0); // start toggle-off - } - - if (zone->flags & AREA_FLAG_SANCTUARY) // in sanctuary - { - SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY); - if (sWorld.IsFFAPvPRealm()) - SetFFAPvP(false); - } - else - { - RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY); - } - - if (zone->flags & AREA_FLAG_CAPITAL) // in capital city - SetRestType(REST_TYPE_IN_CITY); - else if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) && GetRestType() != REST_TYPE_IN_TAVERN) - // resting and not in tavern (leave city then); tavern leave handled in CheckAreaExploreAndOutdoor - SetRestType(REST_TYPE_NO); - - // remove items with area/map limitations (delete only for alive player to allow back in ghost mode) - // if player resurrected at teleport this will be applied in resurrect code - if (isAlive()) - DestroyZoneLimitedItem(true, newZone); - - // check some item equip limitations (in result lost CanTitanGrip at talent reset, for example) - AutoUnequipOffhandIfNeed(); - - // recent client version not send leave/join channel packets for built-in local channels - UpdateLocalChannels(newZone); - - // group update - if (GetGroup()) - SetGroupUpdateFlag(GROUP_UPDATE_FLAG_ZONE); - - UpdateZoneDependentAuras(); - UpdateZoneDependentPets(); - - phaseMgr->RemoveUpdateFlag(PHASE_UPDATE_FLAG_ZONE_UPDATE); -} - -// If players are too far way of duel flag... then player loose the duel -void Player::CheckDuelDistance(time_t currTime) -{ - if (!duel) - return; - - GameObject* obj = GetMap()->GetGameObject(GetGuidValue(PLAYER_DUEL_ARBITER)); - if (!obj) - { - // player not at duel start map - DuelComplete(DUEL_FLED); - return; - } - - if (duel->outOfBound == 0) - { - if (!IsWithinDistInMap(obj, 50)) - { - duel->outOfBound = currTime; - - WorldPacket data(SMSG_DUEL_OUTOFBOUNDS, 0); - GetSession()->SendPacket(&data); - } - } - else - { - if (IsWithinDistInMap(obj, 40)) - { - duel->outOfBound = 0; - - WorldPacket data(SMSG_DUEL_INBOUNDS, 0); - GetSession()->SendPacket(&data); - } - else if (currTime >= (duel->outOfBound + 10)) - { - DuelComplete(DUEL_FLED); - } - } -} - -void Player::DuelComplete(DuelCompleteType type) -{ - // duel not requested - if (!duel) - return; - - WorldPacket data(SMSG_DUEL_COMPLETE, (1)); - data << (uint8)((type != DUEL_INTERRUPTED) ? 1 : 0); - GetSession()->SendPacket(&data); - duel->opponent->GetSession()->SendPacket(&data); - - if (type != DUEL_INTERRUPTED) - { - data.Initialize(SMSG_DUEL_WINNER, (1 + 20)); // we guess size - data << (uint8)((type == DUEL_WON) ? 0 : 1); // 0 = just won; 1 = fled - data << duel->opponent->GetName(); - data << GetName(); - SendMessageToSet(&data, true); - } - - if (type == DUEL_WON) - { - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL, 1); - if (duel->opponent) - duel->opponent->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL, 1); - } - - // Remove Duel Flag object - if (GameObject* obj = GetMap()->GetGameObject(GetGuidValue(PLAYER_DUEL_ARBITER))) - duel->initiator->RemoveGameObject(obj, true); - - /* remove auras */ - std::vector auras2remove; - SpellAuraHolderMap const& vAuras = duel->opponent->GetSpellAuraHolderMap(); - for (SpellAuraHolderMap::const_iterator i = vAuras.begin(); i != vAuras.end(); ++i) - { - if (!i->second->IsPositive() && i->second->GetCasterGuid() == GetObjectGuid() && i->second->GetAuraApplyTime() >= duel->startTime) - auras2remove.push_back(i->second->GetId()); - } - - for (size_t i = 0; i < auras2remove.size(); ++i) - duel->opponent->RemoveAurasDueToSpell(auras2remove[i]); - - auras2remove.clear(); - SpellAuraHolderMap const& auras = GetSpellAuraHolderMap(); - for (SpellAuraHolderMap::const_iterator i = auras.begin(); i != auras.end(); ++i) - { - if (!i->second->IsPositive() && i->second->GetCasterGuid() == duel->opponent->GetObjectGuid() && i->second->GetAuraApplyTime() >= duel->startTime) - auras2remove.push_back(i->second->GetId()); - } - for (size_t i = 0; i < auras2remove.size(); ++i) - RemoveAurasDueToSpell(auras2remove[i]); - - // cleanup combo points - if (GetComboTargetGuid() == duel->opponent->GetObjectGuid()) - ClearComboPoints(); - else if (GetComboTargetGuid() == duel->opponent->GetPetGuid()) - ClearComboPoints(); - - if (duel->opponent->GetComboTargetGuid() == GetObjectGuid()) - duel->opponent->ClearComboPoints(); - else if (duel->opponent->GetComboTargetGuid() == GetPetGuid()) - duel->opponent->ClearComboPoints(); - - // cleanups - SetGuidValue(PLAYER_DUEL_ARBITER, ObjectGuid()); - SetUInt32Value(PLAYER_DUEL_TEAM, 0); - duel->opponent->SetGuidValue(PLAYER_DUEL_ARBITER, ObjectGuid()); - duel->opponent->SetUInt32Value(PLAYER_DUEL_TEAM, 0); - - delete duel->opponent->duel; - duel->opponent->duel = NULL; - delete duel; - duel = NULL; -} - -//---------------------------------------------------------// - -void Player::_ApplyItemMods(Item* item, uint8 slot, bool apply) -{ - if (slot >= INVENTORY_SLOT_BAG_END || !item) - return; - - // not apply/remove mods for broken item - if (item->IsBroken()) - return; - - ItemPrototype const* proto = item->GetProto(); - - if (!proto) - return; - - DETAIL_LOG("applying mods for item %u ", item->GetGUIDLow()); - - uint32 attacktype = Player::GetAttackBySlot(slot); - if (attacktype < MAX_ATTACK) - _ApplyWeaponDependentAuraMods(item, WeaponAttackType(attacktype), apply); - - _ApplyItemBonuses(proto, slot, apply); - - if (slot == EQUIPMENT_SLOT_RANGED) - _ApplyAmmoBonuses(); - - ApplyItemEquipSpell(item, apply); - ApplyEnchantment(item, apply); - - if (proto->Socket[0].Color) // only (un)equipping of items with sockets can influence metagems, so no need to waste time with normal items - CorrectMetaGemEnchants(slot, apply); - - DEBUG_LOG("_ApplyItemMods complete."); -} - -void Player::_ApplyItemBonuses(ItemPrototype const* proto, uint8 slot, bool apply, bool only_level_scale /*= false*/) -{ - if (slot >= INVENTORY_SLOT_BAG_END || !proto) - return; - - ScalingStatDistributionEntry const* ssd = proto->ScalingStatDistribution ? sScalingStatDistributionStore.LookupEntry(proto->ScalingStatDistribution) : NULL; - if (only_level_scale && !ssd) - return; - - // req. check at equip, but allow use for extended range if range limit max level, set proper level - uint32 ssd_level = getLevel(); - if (ssd && ssd_level > ssd->MaxLevel) - ssd_level = ssd->MaxLevel; - - ScalingStatValuesEntry const* ssv = proto->StatScalingFactor ? sScalingStatValuesStore.LookupEntry(ssd_level) : NULL; - if (only_level_scale && !ssv) - return; - - for (uint32 i = 0; i < MAX_ITEM_PROTO_STATS; ++i) - { - uint32 statType = 0; - int32 val = 0; - // If set ScalingStatDistribution need get stats and values from it - if (ssd && ssv) - { - if (ssd->StatMod[i] < 0) - continue; - statType = ssd->StatMod[i]; - val = (ssv->getssdMultiplier(proto->StatScalingFactor) * ssd->Modifier[i]) / 10000; - } - else - { - statType = proto->ItemStat[i].ItemStatType; - val = proto->ItemStat[i].ItemStatValue; - } - - if (val == 0) - continue; - - switch (statType) - { - /*case ITEM_MOD_MANA: - HandleStatModifier(UNIT_MOD_MANA, BASE_VALUE, float(val), apply); - break;*/ - case ITEM_MOD_AGILITY: // modify agility - HandleStatModifier(UNIT_MOD_STAT_AGILITY, BASE_VALUE, float(val), apply); - ApplyStatBuffMod(STAT_AGILITY, float(val), apply); - break; - case ITEM_MOD_STRENGTH: // modify strength - HandleStatModifier(UNIT_MOD_STAT_STRENGTH, BASE_VALUE, float(val), apply); - ApplyStatBuffMod(STAT_STRENGTH, float(val), apply); - break; - case ITEM_MOD_INTELLECT: // modify intellect - HandleStatModifier(UNIT_MOD_STAT_INTELLECT, BASE_VALUE, float(val), apply); - ApplyStatBuffMod(STAT_INTELLECT, float(val), apply); - break; - case ITEM_MOD_SPIRIT: // modify spirit - HandleStatModifier(UNIT_MOD_STAT_SPIRIT, BASE_VALUE, float(val), apply); - ApplyStatBuffMod(STAT_SPIRIT, float(val), apply); - break; - case ITEM_MOD_STAMINA: // modify stamina - HandleStatModifier(UNIT_MOD_STAT_STAMINA, BASE_VALUE, float(val), apply); - ApplyStatBuffMod(STAT_STAMINA, float(val), apply); - break; - case ITEM_MOD_DODGE_RATING: - ApplyRatingMod(CR_DODGE, int32(val), apply); - break; - case ITEM_MOD_PARRY_RATING: - ApplyRatingMod(CR_PARRY, int32(val), apply); - break; - case ITEM_MOD_CRIT_RANGED_RATING: - ApplyRatingMod(CR_CRIT_RANGED, int32(val), apply); - break; - case ITEM_MOD_HIT_RATING: - ApplyRatingMod(CR_HIT_MELEE, int32(val), apply); - ApplyRatingMod(CR_HIT_RANGED, int32(val), apply); - ApplyRatingMod(CR_HIT_SPELL, int32(val), apply); - break; - case ITEM_MOD_CRIT_RATING: - ApplyRatingMod(CR_CRIT_MELEE, int32(val), apply); - ApplyRatingMod(CR_CRIT_RANGED, int32(val), apply); - ApplyRatingMod(CR_CRIT_SPELL, int32(val), apply); - break; - case ITEM_MOD_RESILIENCE_RATING: - ApplyRatingMod(CR_RESILIENCE_DAMAGE_TAKEN, int32(val), apply); - break; - case ITEM_MOD_HASTE_RATING: - ApplyRatingMod(CR_HASTE_MELEE, int32(val), apply); - ApplyRatingMod(CR_HASTE_RANGED, int32(val), apply); - ApplyRatingMod(CR_HASTE_SPELL, int32(val), apply); - break; - case ITEM_MOD_EXPERTISE_RATING: - ApplyRatingMod(CR_EXPERTISE, int32(val), apply); - break; - case ITEM_MOD_ATTACK_POWER: - HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(val), apply); - HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(val), apply); - break; - case ITEM_MOD_RANGED_ATTACK_POWER: - HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(val), apply); - break; - case ITEM_MOD_SPELL_POWER: - ApplySpellPowerBonus(int32(val), apply); - break; - case ITEM_MOD_HEALTH_REGEN: - ApplyHealthRegenBonus(int32(val), apply); - break; - case ITEM_MOD_SPELL_PENETRATION: - ApplyModInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE, -int32(val), apply); - m_spellPenetrationItemMod += apply ? val : -val; - break; - case ITEM_MOD_MASTERY_RATING: - ApplyRatingMod(CR_MASTERY, int32(val), apply); - break; - // deprecated item mods - case ITEM_MOD_HEALTH: - case ITEM_MOD_DEFENSE_SKILL_RATING: - case ITEM_MOD_BLOCK_RATING: - case ITEM_MOD_HIT_MELEE_RATING: - case ITEM_MOD_HIT_RANGED_RATING: - case ITEM_MOD_HIT_SPELL_RATING: - case ITEM_MOD_CRIT_MELEE_RATING: - case ITEM_MOD_CRIT_SPELL_RATING: - case ITEM_MOD_HIT_TAKEN_MELEE_RATING: - case ITEM_MOD_HIT_TAKEN_RANGED_RATING: - case ITEM_MOD_HIT_TAKEN_SPELL_RATING: - case ITEM_MOD_CRIT_TAKEN_MELEE_RATING: - case ITEM_MOD_CRIT_TAKEN_RANGED_RATING: - case ITEM_MOD_CRIT_TAKEN_SPELL_RATING: - case ITEM_MOD_HASTE_MELEE_RATING: - case ITEM_MOD_HASTE_RANGED_RATING: - case ITEM_MOD_HASTE_SPELL_RATING: - case ITEM_MOD_HIT_TAKEN_RATING: - case ITEM_MOD_CRIT_TAKEN_RATING: - case ITEM_MOD_FERAL_ATTACK_POWER: - case ITEM_MOD_SPELL_HEALING_DONE: - case ITEM_MOD_SPELL_DAMAGE_DONE: - case ITEM_MOD_MANA_REGENERATION: - case ITEM_MOD_ARMOR_PENETRATION_RATING: - case ITEM_MOD_BLOCK_VALUE: - break; - } - } - - // Apply Spell Power from ScalingStatValue if set - if (ssv) - { - if (int32 spellbonus = ssv->getSpellBonus(proto->StatScalingFactor)) - ApplySpellPowerBonus(spellbonus, apply); - } - - // If set ScalingStatValue armor get it or use item armor - uint32 armor = proto->GetArmor(); - if (ssv) - { - if (uint32 ssvarmor = ssv->getArmorMod(proto->StatScalingFactor)) - armor = ssvarmor; - } - // Add armor bonus from ArmorDamageModifier if > 0 - if (proto->ArmorDamageModifier > 0) - armor += uint32(proto->ArmorDamageModifier); - - if (armor) - { - switch (proto->InventoryType) - { - case INVTYPE_TRINKET: - case INVTYPE_NECK: - case INVTYPE_CLOAK: - case INVTYPE_FINGER: - HandleStatModifier(UNIT_MOD_ARMOR, TOTAL_VALUE, float(armor), apply); - break; - default: - HandleStatModifier(UNIT_MOD_ARMOR, BASE_VALUE, float(armor), apply); - break; - } - } - - WeaponAttackType attType = BASE_ATTACK; - float damage = 0.0f; - - if (slot == EQUIPMENT_SLOT_RANGED && ( - proto->InventoryType == INVTYPE_RANGED || proto->InventoryType == INVTYPE_THROWN || - proto->InventoryType == INVTYPE_RANGEDRIGHT)) - { - attType = RANGED_ATTACK; - } - else if (slot == EQUIPMENT_SLOT_OFFHAND) - { - attType = OFF_ATTACK; - } - - float minDamage = proto->GetMinDamage(); - float maxDamage = proto->GetMaxDamage(); - int32 extraDPS = 0; - // If set dpsMod in ScalingStatValue use it for min (70% from average), max (130% from average) damage - if (ssv) - { - if ((extraDPS = ssv->getDPSMod(proto->StatScalingFactor))) - { - float average = extraDPS * proto->Delay / 1000.0f; - minDamage = 0.7f * average; - maxDamage = 1.3f * average; - } - } - if (minDamage > 0) - { - damage = apply ? minDamage : BASE_MINDAMAGE; - SetBaseWeaponDamage(attType, MINDAMAGE, damage); - // sLog.outError("applying mindam: assigning %f to weapon mindamage, now is: %f", damage, GetWeaponDamageRange(attType, MINDAMAGE)); - } - - if (maxDamage > 0) - { - damage = apply ? maxDamage : BASE_MAXDAMAGE; - SetBaseWeaponDamage(attType, MAXDAMAGE, damage); - } - - if (!CanUseEquippedWeapon(attType)) - return; - - if (proto->Delay) - { - if (slot == EQUIPMENT_SLOT_RANGED) - SetAttackTime(RANGED_ATTACK, apply ? proto->Delay : BASE_ATTACK_TIME); - else if (slot == EQUIPMENT_SLOT_MAINHAND) - SetAttackTime(BASE_ATTACK, apply ? proto->Delay : BASE_ATTACK_TIME); - else if (slot == EQUIPMENT_SLOT_OFFHAND) - SetAttackTime(OFF_ATTACK, apply ? proto->Delay : BASE_ATTACK_TIME); - } - - if (CanModifyStats() && (damage || proto->Delay)) - UpdateDamagePhysical(attType); -} - -void Player::_ApplyWeaponDependentAuraMods(Item* item, WeaponAttackType attackType, bool apply) -{ - AuraList const& auraCritList = GetAurasByType(SPELL_AURA_MOD_CRIT_PERCENT); - for (AuraList::const_iterator itr = auraCritList.begin(); itr != auraCritList.end(); ++itr) - _ApplyWeaponDependentAuraCritMod(item, attackType, *itr, apply); - - AuraList const& auraDamageFlatList = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE); - for (AuraList::const_iterator itr = auraDamageFlatList.begin(); itr != auraDamageFlatList.end(); ++itr) - _ApplyWeaponDependentAuraDamageMod(item, attackType, *itr, apply); - - AuraList const& auraDamagePCTList = GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE); - for (AuraList::const_iterator itr = auraDamagePCTList.begin(); itr != auraDamagePCTList.end(); ++itr) - _ApplyWeaponDependentAuraDamageMod(item, attackType, *itr, apply); -} - -void Player::_ApplyWeaponDependentAuraCritMod(Item* item, WeaponAttackType attackType, Aura* aura, bool apply) -{ - // generic not weapon specific case processes in aura code - if(aura->GetSpellProto()->GetEquippedItemClass() == -1) - return; - - BaseModGroup mod = BASEMOD_END; - switch (attackType) - { - case BASE_ATTACK: mod = CRIT_PERCENTAGE; break; - case OFF_ATTACK: mod = OFFHAND_CRIT_PERCENTAGE; break; - case RANGED_ATTACK: mod = RANGED_CRIT_PERCENTAGE; break; - default: return; - } - - if (item->IsFitToSpellRequirements(aura->GetSpellProto())) - { - HandleBaseModValue(mod, FLAT_MOD, float(aura->GetModifier()->m_amount), apply); - } -} - -void Player::_ApplyWeaponDependentAuraDamageMod(Item* item, WeaponAttackType attackType, Aura* aura, bool apply) -{ - // ignore spell mods for not wands - Modifier const* modifier = aura->GetModifier(); - if ((modifier->m_miscvalue & SPELL_SCHOOL_MASK_NORMAL) == 0 && (getClassMask() & CLASSMASK_WAND_USERS) == 0) - return; - - // generic not weapon specific case processes in aura code - if(aura->GetSpellProto()->GetEquippedItemClass() == -1) - return; - - UnitMods unitMod = UNIT_MOD_END; - switch (attackType) - { - case BASE_ATTACK: unitMod = UNIT_MOD_DAMAGE_MAINHAND; break; - case OFF_ATTACK: unitMod = UNIT_MOD_DAMAGE_OFFHAND; break; - case RANGED_ATTACK: unitMod = UNIT_MOD_DAMAGE_RANGED; break; - default: return; - } - - UnitModifierType unitModType = TOTAL_VALUE; - switch (modifier->m_auraname) - { - case SPELL_AURA_MOD_DAMAGE_DONE: unitModType = TOTAL_VALUE; break; - case SPELL_AURA_MOD_DAMAGE_PERCENT_DONE: unitModType = TOTAL_PCT; break; - default: return; - } - - if (item->IsFitToSpellRequirements(aura->GetSpellProto())) - { - HandleStatModifier(unitMod, unitModType, float(modifier->m_amount), apply); - } -} - -void Player::ApplyItemEquipSpell(Item* item, bool apply, bool form_change) -{ - if (!item) - return; - - ItemPrototype const* proto = item->GetProto(); - if (!proto) - return; - - for (int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) - { - _Spell const& spellData = proto->Spells[i]; - - // no spell - if (!spellData.SpellId) - continue; - - if (apply) - { - // apply only at-equip spells - if (spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_EQUIP) - continue; - } - else - { - // at un-apply remove all spells (not only at-apply, so any at-use active affects from item and etc) - // except with at-use with negative charges, so allow consuming item spells (including with extra flag that prevent consume really) - // applied to player after item remove from equip slot - if (spellData.SpellTrigger == ITEM_SPELLTRIGGER_ON_USE && spellData.SpellCharges < 0) - continue; - } - - // check if it is valid spell - SpellEntry const* spellproto = sSpellStore.LookupEntry(spellData.SpellId); - if (!spellproto) - continue; - - ApplyEquipSpell(spellproto, item, apply, form_change); - } -} - -void Player::ApplyEquipSpell(SpellEntry const* spellInfo, Item* item, bool apply, bool form_change) -{ - if (apply) - { - // Cannot be used in this stance/form - if (GetErrorAtShapeshiftedCast(spellInfo, GetShapeshiftForm()) != SPELL_CAST_OK) - return; - - if (form_change) // check aura active state from other form - { - SpellAuraHolderBounds spair = GetSpellAuraHolderBounds(spellInfo->Id); - for (SpellAuraHolderMap::const_iterator iter = spair.first; iter != spair.second; ++iter) - { - if (!item || iter->second->GetCastItemGuid() == item->GetObjectGuid()) - return; // and skip re-cast already active aura at form change - } - } - - DEBUG_LOG("WORLD: cast %s Equip spellId - %i", (item ? "item" : "itemset"), spellInfo->Id); - - CastSpell(this, spellInfo, true, item); - } - else - { - if (form_change) // check aura compatibility - { - // Cannot be used in this stance/form - if (GetErrorAtShapeshiftedCast(spellInfo, GetShapeshiftForm()) == SPELL_CAST_OK) - return; // and remove only not compatible at form change - } - - if (item) - RemoveAurasDueToItemSpell(item, spellInfo->Id); // un-apply all spells , not only at-equipped - else - RemoveAurasDueToSpell(spellInfo->Id); // un-apply spell (item set case) - } -} - -void Player::UpdateEquipSpellsAtFormChange() -{ - for (int i = 0; i < INVENTORY_SLOT_BAG_END; ++i) - { - if (m_items[i] && !m_items[i]->IsBroken()) - { - ApplyItemEquipSpell(m_items[i], false, true); // remove spells that not fit to form - ApplyItemEquipSpell(m_items[i], true, true); // add spells that fit form but not active - } - } - - // item set bonuses not dependent from item broken state - for (size_t setindex = 0; setindex < ItemSetEff.size(); ++setindex) - { - ItemSetEffect* eff = ItemSetEff[setindex]; - if (!eff) - continue; - - for (uint32 y = 0; y < 8; ++y) - { - SpellEntry const* spellInfo = eff->spells[y]; - if (!spellInfo) - continue; - - ApplyEquipSpell(spellInfo, NULL, false, true); // remove spells that not fit to form - ApplyEquipSpell(spellInfo, NULL, true, true); // add spells that fit form but not active - } - } -} - -/* - * (un-)Apply item spells triggered at adding item to inventory ITEM_SPELLTRIGGER_ON_STORE - * - * @param item added/removed item to/from inventory - * @param apply (un-)apply spell affects. - * - * Note: item moved from slot to slot in 2 steps RemoveItem and StoreItem/EquipItem - * In result function not called in RemoveItem for prevent unexpected re-apply auras from related spells - * with duration reset and etc. Instead unapply done in StoreItem/EquipItem and in specialized - * functions for item final remove/destroy from inventory. If new RemoveItem calls added need be sure that - * function will call after it in some way if need. - */ - -void Player::ApplyItemOnStoreSpell(Item* item, bool apply) -{ - if (!item) - return; - - ItemPrototype const* proto = item->GetProto(); - if (!proto) - return; - - for (int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) - { - _Spell const& spellData = proto->Spells[i]; - - // no spell - if (!spellData.SpellId) - continue; - - // apply/unapply only at-store spells - if (spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_STORE) - continue; - - if (apply) - { - // can be attempt re-applied at move in inventory slots - if (!HasAura(spellData.SpellId)) - CastSpell(this, spellData.SpellId, true, item); - } - else - RemoveAurasDueToItemSpell(item, spellData.SpellId); - } -} - -void Player::DestroyItemWithOnStoreSpell(Item* item, uint32 spellId) -{ - if (!item) - return; - - ItemPrototype const* proto = item->GetProto(); - if (!proto) - return; - - for (int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) - { - _Spell const& spellData = proto->Spells[i]; - - if (spellData.SpellId != spellId) - continue; - - // apply/unapply only at-store spells - if (spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_STORE) - continue; - - DestroyItem(item->GetBagSlot(), item->GetSlot(), true); - break; - } -} - -/// handles unique effect of Deadly Poison: apply poison of the other weapon when already at max. stack -void Player::_HandleDeadlyPoison(Unit* Target, WeaponAttackType attType, SpellEntry const* spellInfo) -{ - SpellAuraHolder const* dPoison = NULL; - SpellAuraHolderConstBounds holders = Target->GetSpellAuraHolderBounds(spellInfo->Id); - for (SpellAuraHolderMap::const_iterator iter = holders.first; iter != holders.second; ++iter) - { - if (iter->second->GetCaster() == this) - { - dPoison = iter->second; - break; - } - } - if (dPoison && dPoison->GetStackAmount() == spellInfo->GetStackAmount()) - { - Item* otherWeapon = GetWeaponForAttack(attType == BASE_ATTACK ? OFF_ATTACK : BASE_ATTACK); - if (!otherWeapon) - return; - - // all poison enchantments are temporary - uint32 enchant_id = otherWeapon->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT); - if (!enchant_id) - return; - - SpellItemEnchantmentEntry const* pSecondEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); - if (!pSecondEnchant) - return; - - for (int s = 0; s < 3; ++s) - { - if (pSecondEnchant->type[s] != ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL) - continue; - - if (SpellEntry const* combatEntry = sSpellStore.LookupEntry(pSecondEnchant->spellid[s])) - if (combatEntry->GetDispel() == DISPEL_POISON) - CastSpell(Target, combatEntry, true, otherWeapon); - } - } -} - -void Player::CastItemCombatSpell(Unit* Target, WeaponAttackType attType) -{ - Item* item = GetWeaponForAttack(attType, true, false); - if (!item) - return; - - ItemPrototype const* proto = item->GetProto(); - if (!proto) - return; - - if (!Target || Target == this) - return; - - for (int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) - { - _Spell const& spellData = proto->Spells[i]; - - // no spell - if (!spellData.SpellId) - continue; - - // wrong triggering type - if (spellData.SpellTrigger != ITEM_SPELLTRIGGER_CHANCE_ON_HIT) - continue; - - SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellData.SpellId); - if (!spellInfo) - { - sLog.outError("WORLD: unknown Item spellid %i", spellData.SpellId); - continue; - } - - // not allow proc extra attack spell at extra attack - if (m_extraAttacks && IsSpellHaveEffect(spellInfo, SPELL_EFFECT_ADD_EXTRA_ATTACKS)) - return; - - float chance = (float)spellInfo->GetProcChance(); - - if (spellData.SpellPPMRate) - { - uint32 WeaponSpeed = proto->Delay; - chance = GetPPMProcChance(WeaponSpeed, spellData.SpellPPMRate); - } - else if (chance > 100.0f) - { - chance = GetWeaponProcChance(); - } - - if (roll_chance_f(chance)) - CastSpell(Target, spellInfo->Id, true, item); - } - - // item combat enchantments - for (int e_slot = 0; e_slot < MAX_ENCHANTMENT_SLOT; ++e_slot) - { - if (e_slot > PRISMATIC_ENCHANTMENT_SLOT && e_slot < PROP_ENCHANTMENT_SLOT_0) - continue; - - uint32 enchant_id = item->GetEnchantmentId(EnchantmentSlot(e_slot)); - SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); - if (!pEnchant) continue; - for (int s = 0; s < 3; ++s) - { - if (pEnchant->type[s] != ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL) - continue; - - uint32 proc_spell_id = pEnchant->spellid[s]; - SpellEntry const* spellInfo = sSpellStore.LookupEntry(proc_spell_id); - if (!spellInfo) - { - sLog.outError("Player::CastItemCombatSpell Enchant %i, cast unknown spell %i", pEnchant->ID, proc_spell_id); - continue; - } - - // Use first rank to access spell item enchant procs - float ppmRate = sSpellMgr.GetItemEnchantProcChance(spellInfo->Id); - - float chance = ppmRate - ? GetPPMProcChance(proto->Delay, ppmRate) - : pEnchant->amount[s] != 0 ? float(pEnchant->amount[s]) : GetWeaponProcChance(); - - ApplySpellMod(spellInfo->Id, SPELLMOD_CHANCE_OF_SUCCESS, chance); - ApplySpellMod(spellInfo->Id, SPELLMOD_FREQUENCY_OF_SUCCESS, chance); - - if (roll_chance_f(chance)) - { - if (IsPositiveSpell(spellInfo->Id)) - CastSpell(this, spellInfo->Id, true, item); - else - { - // Deadly Poison, unique effect needs to be handled before casting triggered spell - if (spellInfo->IsFitToFamily(SPELLFAMILY_ROGUE, UI64LIT(0x0000000000010000))) - _HandleDeadlyPoison(Target, attType, spellInfo); - - CastSpell(Target, spellInfo->Id, true, item); - } - } - } - } -} - -void Player::CastItemUseSpell(Item* item, SpellCastTargets const& targets, uint8 cast_count, uint32 glyphIndex) -{ - ItemPrototype const* proto = item->GetProto(); - // special learning case - if (proto->Spells[0].SpellId == SPELL_ID_GENERIC_LEARN || proto->Spells[0].SpellId == SPELL_ID_GENERIC_LEARN_PET) - { - uint32 learn_spell_id = proto->Spells[0].SpellId; - uint32 learning_spell_id = proto->Spells[1].SpellId; - - SpellEntry const* spellInfo = sSpellStore.LookupEntry(learn_spell_id); - if (!spellInfo) - { - sLog.outError("Player::CastItemUseSpell: Item (Entry: %u) in have wrong spell id %u, ignoring ", proto->ItemId, learn_spell_id); - SendEquipError(EQUIP_ERR_NONE, item); - return; - } - - Spell* spell = new Spell(this, spellInfo, false); - spell->m_CastItem = item; - spell->m_cast_count = cast_count; // set count of casts - spell->m_currentBasePoints[EFFECT_INDEX_0] = learning_spell_id; - spell->prepare(&targets); - return; - } - - // use triggered flag only for items with many spell casts and for not first cast - int count = 0; - - // item spells casted at use - for (int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) - { - _Spell const& spellData = proto->Spells[i]; - - // no spell - if (!spellData.SpellId) - continue; - - // wrong triggering type - if (spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_USE) - continue; - - SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellData.SpellId); - if (!spellInfo) - { - sLog.outError("Player::CastItemUseSpell: Item (Entry: %u) in have wrong spell id %u, ignoring", proto->ItemId, spellData.SpellId); - continue; - } - - Spell* spell = new Spell(this, spellInfo, (count > 0)); - spell->m_CastItem = item; - spell->m_cast_count = cast_count; // set count of casts - spell->m_glyphIndex = glyphIndex; // glyph index - spell->prepare(&targets); - - ++count; - } - - // Item enchantments spells casted at use - for (int e_slot = 0; e_slot < MAX_ENCHANTMENT_SLOT; ++e_slot) - { - if (e_slot > PRISMATIC_ENCHANTMENT_SLOT && e_slot < PROP_ENCHANTMENT_SLOT_0) - continue; - - uint32 enchant_id = item->GetEnchantmentId(EnchantmentSlot(e_slot)); - SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); - if (!pEnchant) - continue; - - for (int s = 0; s < 3; ++s) - { - if (pEnchant->type[s] != ITEM_ENCHANTMENT_TYPE_USE_SPELL) - continue; - - SpellEntry const* spellInfo = sSpellStore.LookupEntry(pEnchant->spellid[s]); - if (!spellInfo) - { - sLog.outError("Player::CastItemUseSpell Enchant %i, cast unknown spell %i", pEnchant->ID, pEnchant->spellid[s]); - continue; - } - - Spell* spell = new Spell(this, spellInfo, (count > 0)); - spell->m_CastItem = item; - spell->m_cast_count = cast_count; // set count of casts - spell->m_glyphIndex = glyphIndex; // glyph index - spell->prepare(&targets); - - ++count; - } - } -} - -void Player::_RemoveAllItemMods() -{ - DEBUG_LOG("_RemoveAllItemMods start."); - - for (int i = 0; i < INVENTORY_SLOT_BAG_END; ++i) - { - if (m_items[i]) - { - ItemPrototype const* proto = m_items[i]->GetProto(); - if (!proto) - continue; - - // item set bonuses not dependent from item broken state - if (proto->ItemSet) - RemoveItemsSetItem(this, proto); - - if (m_items[i]->IsBroken()) - continue; - - ApplyItemEquipSpell(m_items[i], false); - ApplyEnchantment(m_items[i], false); - } - } - - for (int i = 0; i < INVENTORY_SLOT_BAG_END; ++i) - { - if (m_items[i]) - { - if (m_items[i]->IsBroken()) - continue; - ItemPrototype const* proto = m_items[i]->GetProto(); - if (!proto) - continue; - - uint32 attacktype = Player::GetAttackBySlot(i); - if (attacktype < MAX_ATTACK) - _ApplyWeaponDependentAuraMods(m_items[i], WeaponAttackType(attacktype), false); - - _ApplyItemBonuses(proto, i, false); - - if (i == EQUIPMENT_SLOT_RANGED) - _ApplyAmmoBonuses(); - } - } - - DEBUG_LOG("_RemoveAllItemMods complete."); -} - -void Player::_ApplyAllItemMods() -{ - DEBUG_LOG("_ApplyAllItemMods start."); - - for (int i = 0; i < INVENTORY_SLOT_BAG_END; ++i) - { - if (m_items[i]) - { - if (m_items[i]->IsBroken()) - continue; - - ItemPrototype const* proto = m_items[i]->GetProto(); - if (!proto) - continue; - - uint32 attacktype = Player::GetAttackBySlot(i); - if (attacktype < MAX_ATTACK) - _ApplyWeaponDependentAuraMods(m_items[i], WeaponAttackType(attacktype), true); - - _ApplyItemBonuses(proto, i, true); - - if (i == EQUIPMENT_SLOT_RANGED) - _ApplyAmmoBonuses(); - } - } - - for (int i = 0; i < INVENTORY_SLOT_BAG_END; ++i) - { - if (m_items[i]) - { - ItemPrototype const* proto = m_items[i]->GetProto(); - if (!proto) - continue; - - // item set bonuses not dependent from item broken state - if (proto->ItemSet) - AddItemsSetItem(this, m_items[i]); - - if (m_items[i]->IsBroken()) - continue; - - ApplyItemEquipSpell(m_items[i], true); - ApplyEnchantment(m_items[i], true); - } - } - - DEBUG_LOG("_ApplyAllItemMods complete."); -} - -void Player::_ApplyAllLevelScaleItemMods(bool apply) -{ - for (int i = 0; i < INVENTORY_SLOT_BAG_END; ++i) - { - if (m_items[i]) - { - if (m_items[i]->IsBroken()) - continue; - - ItemPrototype const* proto = m_items[i]->GetProto(); - if (!proto) - continue; - - _ApplyItemBonuses(proto, i, apply, true); - } - } -} - -void Player::_ApplyAmmoBonuses() -{ - //// check ammo - //uint32 ammo_id = GetUInt32Value(PLAYER_AMMO_ID); - //if(!ammo_id) - // return; - - //float currentAmmoDPS; - - //ItemPrototype const *ammo_proto = ObjectMgr::GetItemPrototype( ammo_id ); - //if( !ammo_proto || ammo_proto->Class!=ITEM_CLASS_PROJECTILE || !CheckAmmoCompatibility(ammo_proto)) - // currentAmmoDPS = 0.0f; - //else - // currentAmmoDPS = ammo_proto->Damage[0].DamageMin; - - //if(currentAmmoDPS == GetAmmoDPS()) - // return; - - //m_ammoDPS = currentAmmoDPS; - - //if(CanModifyStats()) - // UpdateDamagePhysical(RANGED_ATTACK); -} - -bool Player::CheckAmmoCompatibility(const ItemPrototype* ammo_proto) const -{ - if (!ammo_proto) - return false; - - // check ranged weapon - Item* weapon = GetWeaponForAttack(RANGED_ATTACK, true, false); - if (!weapon) - return false; - - ItemPrototype const* weapon_proto = weapon->GetProto(); - if (!weapon_proto || weapon_proto->Class != ITEM_CLASS_WEAPON) - return false; - - // check ammo ws. weapon compatibility - switch (weapon_proto->SubClass) - { - case ITEM_SUBCLASS_WEAPON_BOW: - case ITEM_SUBCLASS_WEAPON_CROSSBOW: - if (ammo_proto->SubClass != ITEM_SUBCLASS_ARROW) - return false; - break; - case ITEM_SUBCLASS_WEAPON_GUN: - if (ammo_proto->SubClass != ITEM_SUBCLASS_BULLET) - return false; - break; - default: - return false; - } - - return true; -} - -/* If in a battleground a player dies, and an enemy removes the insignia, the player's bones is lootable - Called by remove insignia spell effect */ -void Player::RemovedInsignia(Player* looterPlr) -{ - if (!GetBattleGroundId()) - return; - - // If not released spirit, do it ! - if (m_deathTimer > 0) - { - m_deathTimer = 0; - BuildPlayerRepop(); - RepopAtGraveyard(); - } - - Corpse* corpse = GetCorpse(); - if (!corpse) - return; - - // We have to convert player corpse to bones, not to be able to resurrect there - // SpawnCorpseBones isn't handy, 'cos it saves player while he in BG - Corpse* bones = sObjectAccessor.ConvertCorpseForPlayer(GetObjectGuid(), true); - if (!bones) - return; - - // Now we must make bones lootable, and send player loot - bones->SetFlag(CORPSE_FIELD_DYNAMIC_FLAGS, CORPSE_DYNFLAG_LOOTABLE); - - // We store the level of our player in the gold field - // We retrieve this information at Player::SendLoot() - bones->loot.gold = getLevel(); - bones->lootRecipient = looterPlr; - looterPlr->SendLoot(bones->GetObjectGuid(), LOOT_INSIGNIA); -} - -void Player::SendLootRelease(ObjectGuid guid) -{ - WorldPacket data(SMSG_LOOT_RELEASE_RESPONSE, (8 + 1)); - data << guid; - data << uint8(1); - SendDirectMessage(&data); -} - -void Player::SendLoot(ObjectGuid guid, LootType loot_type) -{ - if (ObjectGuid lootGuid = GetLootGuid()) - m_session->DoLootRelease(lootGuid); - - Loot* loot = NULL; - PermissionTypes permission = ALL_PERMISSION; - - DEBUG_LOG("Player::SendLoot"); - switch (guid.GetHigh()) - { - case HIGHGUID_GAMEOBJECT: - { - DEBUG_LOG(" IS_GAMEOBJECT_GUID(guid)"); - GameObject* go = GetMap()->GetGameObject(guid); - - // not check distance for GO in case owned GO (fishing bobber case, for example) - // And permit out of range GO with no owner in case fishing hole - if (!go || (loot_type != LOOT_FISHINGHOLE && (loot_type != LOOT_FISHING && loot_type != LOOT_FISHING_FAIL || go->GetOwnerGuid() != GetObjectGuid()) && !go->IsWithinDistInMap(this, INTERACTION_DISTANCE))) - { - SendLootRelease(guid); - return; - } - - loot = &go->loot; - - Player* recipient = go->GetLootRecipient(); - if (!recipient) - { - go->SetLootRecipient(this); - recipient = this; - } - - // generate loot only if ready for open and spawned in world - if (go->getLootState() == GO_READY && go->isSpawned()) - { - uint32 lootid = go->GetGOInfo()->GetLootId(); - if ((go->GetEntry() == BG_AV_OBJECTID_MINE_N || go->GetEntry() == BG_AV_OBJECTID_MINE_S)) - { - if (BattleGround* bg = GetBattleGround()) - if (bg->GetTypeID() == BATTLEGROUND_AV) - if (!(((BattleGroundAV*)bg)->PlayerCanDoMineQuest(go->GetEntry(), GetTeam()))) - { - SendLootRelease(guid); - return; - } - } - - loot->clear(); - switch (loot_type) - { - // Entry 0 in fishing loot template used for store junk fish loot at fishing fail it junk allowed by config option - // this is overwrite fishinghole loot for example - case LOOT_FISHING_FAIL: - loot->FillLoot(0, LootTemplates_Fishing, this, true); - break; - case LOOT_FISHING: - uint32 zone, subzone; - go->GetZoneAndAreaId(zone, subzone); - // if subzone loot exist use it - if (!loot->FillLoot(subzone, LootTemplates_Fishing, this, true, (subzone != zone)) && subzone != zone) - // else use zone loot (if zone diff. from subzone, must exist in like case) - loot->FillLoot(zone, LootTemplates_Fishing, this, true); - break; - default: - if (!lootid) - break; - DEBUG_LOG(" send normal GO loot"); - - loot->FillLoot(lootid, LootTemplates_Gameobject, this, false); - loot->generateMoneyLoot(go->GetGOInfo()->MinMoneyLoot, go->GetGOInfo()->MaxMoneyLoot); - - if (go->GetGoType() == GAMEOBJECT_TYPE_CHEST && go->GetGOInfo()->chest.groupLootRules) - { - if (Group* group = go->GetGroupLootRecipient()) - { - group->UpdateLooterGuid(go, true); - - switch (group->GetLootMethod()) - { - case GROUP_LOOT: - // GroupLoot delete items over threshold (threshold even not implemented), and roll them. Items with qualityGroupLoot(go, loot); - permission = GROUP_PERMISSION; - break; - case NEED_BEFORE_GREED: - group->NeedBeforeGreed(go, loot); - permission = GROUP_PERMISSION; - break; - case MASTER_LOOT: - group->MasterLoot(go, loot); - permission = MASTER_PERMISSION; - break; - default: - break; - } - } - } - break; - } - - go->SetLootState(GO_ACTIVATED); - } - if (go->getLootState() == GO_ACTIVATED && go->GetGoType() == GAMEOBJECT_TYPE_CHEST && go->GetGOInfo()->chest.groupLootRules) - { - if (Group* group = go->GetGroupLootRecipient()) - { - if (group == GetGroup()) - { - if (group->GetLootMethod() == FREE_FOR_ALL) - permission = ALL_PERMISSION; - else if (group->GetLooterGuid() == GetObjectGuid()) - { - if (group->GetLootMethod() == MASTER_LOOT) - permission = MASTER_PERMISSION; - else - permission = ALL_PERMISSION; - } - else - permission = GROUP_PERMISSION; - } - else - permission = NONE_PERMISSION; - } - else if (recipient == this) - permission = ALL_PERMISSION; - else - permission = NONE_PERMISSION; - } - break; - } - case HIGHGUID_ITEM: - { - Item* item = GetItemByGuid(guid); - - if (!item) - { - SendLootRelease(guid); - return; - } - - permission = OWNER_PERMISSION; - - loot = &item->loot; - - if (!item->HasGeneratedLoot()) - { - item->loot.clear(); - - switch (loot_type) - { - case LOOT_DISENCHANTING: - loot->FillLoot(item->GetProto()->DisenchantID, LootTemplates_Disenchant, this, true); - item->SetLootState(ITEM_LOOT_TEMPORARY); - break; - case LOOT_PROSPECTING: - loot->FillLoot(item->GetEntry(), LootTemplates_Prospecting, this, true); - item->SetLootState(ITEM_LOOT_TEMPORARY); - break; - case LOOT_MILLING: - loot->FillLoot(item->GetEntry(), LootTemplates_Milling, this, true); - item->SetLootState(ITEM_LOOT_TEMPORARY); - break; - default: - loot->FillLoot(item->GetEntry(), LootTemplates_Item, this, true, item->GetProto()->MaxMoneyLoot == 0); - loot->generateMoneyLoot(item->GetProto()->MinMoneyLoot, item->GetProto()->MaxMoneyLoot); - item->SetLootState(ITEM_LOOT_CHANGED); - break; - } - } - break; - } - case HIGHGUID_CORPSE: // remove insignia - { - Corpse* bones = GetMap()->GetCorpse(guid); - - if (!bones || !((loot_type == LOOT_CORPSE) || (loot_type == LOOT_INSIGNIA)) || (bones->GetType() != CORPSE_BONES)) - { - SendLootRelease(guid); - return; - } - - loot = &bones->loot; - - if (!bones->lootForBody) - { - bones->lootForBody = true; - uint32 pLevel = bones->loot.gold; - bones->loot.clear(); - if (GetBattleGround()->GetTypeID() == BATTLEGROUND_AV) - loot->FillLoot(0, LootTemplates_Creature, this, false); - // It may need a better formula - // Now it works like this: lvl10: ~6copper, lvl70: ~9silver - bones->loot.gold = (uint32)(urand(50, 150) * 0.016f * pow(((float)pLevel) / 5.76f, 2.5f) * sWorld.getConfig(CONFIG_FLOAT_RATE_DROP_MONEY)); - } - - if (bones->lootRecipient != this) - permission = NONE_PERMISSION; - else - permission = OWNER_PERMISSION; - break; - } - case HIGHGUID_UNIT: - case HIGHGUID_VEHICLE: - { - Creature* creature = GetMap()->GetCreature(guid); - - // must be in range and creature must be alive for pickpocket and must be dead for another loot - if (!creature || creature->isAlive() != (loot_type == LOOT_PICKPOCKETING) || !creature->IsWithinDistInMap(this, INTERACTION_DISTANCE)) - { - SendLootRelease(guid); - return; - } - - if (loot_type == LOOT_PICKPOCKETING && IsFriendlyTo(creature)) - { - SendLootRelease(guid); - return; - } - - loot = &creature->loot; - - if (loot_type == LOOT_PICKPOCKETING) - { - if (!creature->lootForPickPocketed) - { - creature->lootForPickPocketed = true; - loot->clear(); - - if (uint32 lootid = creature->GetCreatureInfo()->pickpocketLootId) - loot->FillLoot(lootid, LootTemplates_Pickpocketing, this, false); - - // Generate extra money for pick pocket loot - const uint32 a = urand(0, creature->getLevel() / 2); - const uint32 b = urand(0, getLevel() / 2); - loot->gold = uint32(10 * (a + b) * sWorld.getConfig(CONFIG_FLOAT_RATE_DROP_MONEY)); - permission = OWNER_PERMISSION; - } - } - else - { - // the player whose group may loot the corpse - Player* recipient = creature->GetLootRecipient(); - if (!recipient) - { - creature->SetLootRecipient(this); - recipient = this; - } - - if (creature->lootForPickPocketed) - { - creature->lootForPickPocketed = false; - loot->clear(); - } - - if (!creature->lootForBody) - { - creature->lootForBody = true; - loot->clear(); - - if (uint32 lootid = creature->GetCreatureInfo()->lootid) - loot->FillLoot(lootid, LootTemplates_Creature, recipient, false); - - loot->generateMoneyLoot(creature->GetCreatureInfo()->mingold, creature->GetCreatureInfo()->maxgold); - - if (Group* group = creature->GetGroupLootRecipient()) - { - group->UpdateLooterGuid(creature, true); - - switch (group->GetLootMethod()) - { - case GROUP_LOOT: - // GroupLoot delete items over threshold (threshold even not implemented), and roll them. Items with qualityGroupLoot(creature, loot); - break; - case NEED_BEFORE_GREED: - group->NeedBeforeGreed(creature, loot); - break; - case MASTER_LOOT: - group->MasterLoot(creature, loot); - break; - default: - break; - } - } - } - - // possible only if creature->lootForBody && loot->empty() at spell cast check - if (loot_type == LOOT_SKINNING) - { - if (!creature->lootForSkin) - { - creature->lootForSkin = true; - loot->clear(); - loot->FillLoot(creature->GetCreatureInfo()->SkinLootId, LootTemplates_Skinning, this, false); - - // let reopen skinning loot if will closed. - if (!loot->empty()) - creature->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); - - permission = OWNER_PERMISSION; - } - } - // set group rights only for loot_type != LOOT_SKINNING - else - { - if (Group* group = creature->GetGroupLootRecipient()) - { - if (group == GetGroup()) - { - if (group->GetLootMethod() == FREE_FOR_ALL) - permission = ALL_PERMISSION; - else if (group->GetLooterGuid() == GetObjectGuid()) - { - if (group->GetLootMethod() == MASTER_LOOT) - permission = MASTER_PERMISSION; - else - permission = ALL_PERMISSION; - } - else - permission = GROUP_PERMISSION; - } - else - permission = NONE_PERMISSION; - } - else if (recipient == this) - permission = OWNER_PERMISSION; - else - permission = NONE_PERMISSION; - } - } - break; - } - default: - { - sLog.outError("%s is unsupported for looting.", guid.GetString().c_str()); - return; - } - } - - SetLootGuid(guid); - - // LOOT_INSIGNIA and LOOT_FISHINGHOLE unsupported by client - switch (loot_type) - { - case LOOT_INSIGNIA: loot_type = LOOT_SKINNING; break; - case LOOT_FISHING_FAIL: loot_type = LOOT_FISHING; break; - case LOOT_FISHINGHOLE: loot_type = LOOT_FISHING; break; - default: break; - } - - // need know merged fishing/corpse loot type for achievements - loot->loot_type = loot_type; - - WorldPacket data(SMSG_LOOT_RESPONSE, (9 + 50)); // we guess size - data << ObjectGuid(guid); - data << uint8(loot_type); - data << LootView(*loot, this, permission); - SendDirectMessage(&data); - - // add 'this' player as one of the players that are looting 'loot' - if (permission != NONE_PERMISSION) - loot->AddLooter(GetObjectGuid()); - - if (loot_type == LOOT_CORPSE && !guid.IsItem()) - SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_LOOTING); -} - -void Player::SendNotifyLootMoneyRemoved() -{ - WorldPacket data(SMSG_LOOT_CLEAR_MONEY, 0); - GetSession()->SendPacket(&data); -} - -void Player::SendNotifyLootItemRemoved(uint8 lootSlot, bool currency) -{ - WorldPacket data(currency ? SMSG_LOOT_CURRENCY_REMOVED : SMSG_LOOT_REMOVED, 1); - data << uint8(lootSlot); - GetSession()->SendPacket(&data); -} - -void Player::SendUpdateWorldState(uint32 Field, uint32 Value) -{ - WorldPacket data(SMSG_UPDATE_WORLD_STATE, 8 + 1); - data << Field; - data << Value; - data << uint8(0); - GetSession()->SendPacket(&data); -} - -static WorldStatePair AV_world_states[] = -{ - { 0x7ae, 0x1 }, // 1966 7 snowfall n - { 0x532, 0x1 }, // 1330 8 frostwolfhut hc - { 0x531, 0x0 }, // 1329 9 frostwolfhut ac - { 0x52e, 0x0 }, // 1326 10 stormpike firstaid a_a - { 0x571, 0x0 }, // 1393 11 east frostwolf tower horde assaulted -unused - { 0x570, 0x0 }, // 1392 12 west frostwolf tower horde assaulted - unused - { 0x567, 0x1 }, // 1383 13 frostwolfe c - { 0x566, 0x1 }, // 1382 14 frostwolfw c - { 0x550, 0x1 }, // 1360 15 irondeep (N) ally - { 0x544, 0x0 }, // 1348 16 ice grave a_a - { 0x536, 0x0 }, // 1334 17 stormpike grave h_c - { 0x535, 0x1 }, // 1333 18 stormpike grave a_c - { 0x518, 0x0 }, // 1304 19 stoneheart grave a_a - { 0x517, 0x0 }, // 1303 20 stoneheart grave h_a - { 0x574, 0x0 }, // 1396 21 unk - { 0x573, 0x0 }, // 1395 22 iceblood tower horde assaulted -unused - { 0x572, 0x0 }, // 1394 23 towerpoint horde assaulted - unused - { 0x56f, 0x0 }, // 1391 24 unk - { 0x56e, 0x0 }, // 1390 25 iceblood a - { 0x56d, 0x0 }, // 1389 26 towerp a - { 0x56c, 0x0 }, // 1388 27 frostwolfe a - { 0x56b, 0x0 }, // 1387 28 froswolfw a - { 0x56a, 0x1 }, // 1386 29 unk - { 0x569, 0x1 }, // 1385 30 iceblood c - { 0x568, 0x1 }, // 1384 31 towerp c - { 0x565, 0x0 }, // 1381 32 stoneh tower a - { 0x564, 0x0 }, // 1380 33 icewing tower a - { 0x563, 0x0 }, // 1379 34 dunn a - { 0x562, 0x0 }, // 1378 35 duns a - { 0x561, 0x0 }, // 1377 36 stoneheart bunker alliance assaulted - unused - { 0x560, 0x0 }, // 1376 37 icewing bunker alliance assaulted - unused - { 0x55f, 0x0 }, // 1375 38 dunbaldar south alliance assaulted - unused - { 0x55e, 0x0 }, // 1374 39 dunbaldar north alliance assaulted - unused - { 0x55d, 0x0 }, // 1373 40 stone tower d - { 0x3c6, 0x0 }, // 966 41 unk - { 0x3c4, 0x0 }, // 964 42 unk - { 0x3c2, 0x0 }, // 962 43 unk - { 0x516, 0x1 }, // 1302 44 stoneheart grave a_c - { 0x515, 0x0 }, // 1301 45 stonheart grave h_c - { 0x3b6, 0x0 }, // 950 46 unk - { 0x55c, 0x0 }, // 1372 47 icewing tower d - { 0x55b, 0x0 }, // 1371 48 dunn d - { 0x55a, 0x0 }, // 1370 49 duns d - { 0x559, 0x0 }, // 1369 50 unk - { 0x558, 0x0 }, // 1368 51 iceblood d - { 0x557, 0x0 }, // 1367 52 towerp d - { 0x556, 0x0 }, // 1366 53 frostwolfe d - { 0x555, 0x0 }, // 1365 54 frostwolfw d - { 0x554, 0x1 }, // 1364 55 stoneh tower c - { 0x553, 0x1 }, // 1363 56 icewing tower c - { 0x552, 0x1 }, // 1362 57 dunn c - { 0x551, 0x1 }, // 1361 58 duns c - { 0x54f, 0x0 }, // 1359 59 irondeep (N) horde - { 0x54e, 0x0 }, // 1358 60 irondeep (N) ally - { 0x54d, 0x1 }, // 1357 61 mine (S) neutral - { 0x54c, 0x0 }, // 1356 62 mine (S) horde - { 0x54b, 0x0 }, // 1355 63 mine (S) ally - { 0x545, 0x0 }, // 1349 64 iceblood h_a - { 0x543, 0x1 }, // 1347 65 iceblod h_c - { 0x542, 0x0 }, // 1346 66 iceblood a_c - { 0x540, 0x0 }, // 1344 67 snowfall h_a - { 0x53f, 0x0 }, // 1343 68 snowfall a_a - { 0x53e, 0x0 }, // 1342 69 snowfall h_c - { 0x53d, 0x0 }, // 1341 70 snowfall a_c - { 0x53c, 0x0 }, // 1340 71 frostwolf g h_a - { 0x53b, 0x0 }, // 1339 72 frostwolf g a_a - { 0x53a, 0x1 }, // 1338 73 frostwolf g h_c - { 0x539, 0x0 }, // l33t 74 frostwolf g a_c - { 0x538, 0x0 }, // 1336 75 stormpike grave h_a - { 0x537, 0x0 }, // 1335 76 stormpike grave a_a - { 0x534, 0x0 }, // 1332 77 frostwolf hut h_a - { 0x533, 0x0 }, // 1331 78 frostwolf hut a_a - { 0x530, 0x0 }, // 1328 79 stormpike first aid h_a - { 0x52f, 0x0 }, // 1327 80 stormpike first aid h_c - { 0x52d, 0x1 }, // 1325 81 stormpike first aid a_c - { 0x0, 0x0 } -}; - -static WorldStatePair WS_world_states[] = -{ - { 0x62d, 0x0 }, // 1581 7 alliance flag captures - { 0x62e, 0x0 }, // 1582 8 horde flag captures - { 0x609, 0x0 }, // 1545 9 unk, set to 1 on alliance flag pickup... - { 0x60a, 0x0 }, // 1546 10 unk, set to 1 on horde flag pickup, after drop it's -1 - { 0x60b, 0x2 }, // 1547 11 unk - { 0x641, 0x3 }, // 1601 12 unk (max flag captures?) - { 0x922, 0x1 }, // 2338 13 horde (0 - hide, 1 - flag ok, 2 - flag picked up (flashing), 3 - flag picked up (not flashing) - { 0x923, 0x1 }, // 2339 14 alliance (0 - hide, 1 - flag ok, 2 - flag picked up (flashing), 3 - flag picked up (not flashing) - { 0x1097, 0x1 }, // 4247 15 show time limit? - { 0x1098, 0x19 }, // 4248 16 time remaining in minutes - { 0x0, 0x0 } -}; - -static WorldStatePair AB_world_states[] = -{ - { 0x6e7, 0x0 }, // 1767 7 stables alliance - { 0x6e8, 0x0 }, // 1768 8 stables horde - { 0x6e9, 0x0 }, // 1769 9 unk, ST? - { 0x6ea, 0x0 }, // 1770 10 stables (show/hide) - { 0x6ec, 0x0 }, // 1772 11 farm (0 - horde controlled, 1 - alliance controlled) - { 0x6ed, 0x0 }, // 1773 12 farm (show/hide) - { 0x6ee, 0x0 }, // 1774 13 farm color - { 0x6ef, 0x0 }, // 1775 14 gold mine color, may be FM? - { 0x6f0, 0x0 }, // 1776 15 alliance resources - { 0x6f1, 0x0 }, // 1777 16 horde resources - { 0x6f2, 0x0 }, // 1778 17 horde bases - { 0x6f3, 0x0 }, // 1779 18 alliance bases - { 0x6f4, 0x7d0 }, // 1780 19 max resources (2000) - { 0x6f6, 0x0 }, // 1782 20 blacksmith color - { 0x6f7, 0x0 }, // 1783 21 blacksmith (show/hide) - { 0x6f8, 0x0 }, // 1784 22 unk, bs? - { 0x6f9, 0x0 }, // 1785 23 unk, bs? - { 0x6fb, 0x0 }, // 1787 24 gold mine (0 - horde contr, 1 - alliance contr) - { 0x6fc, 0x0 }, // 1788 25 gold mine (0 - conflict, 1 - horde) - { 0x6fd, 0x0 }, // 1789 26 gold mine (1 - show/0 - hide) - { 0x6fe, 0x0 }, // 1790 27 gold mine color - { 0x700, 0x0 }, // 1792 28 gold mine color, wtf?, may be LM? - { 0x701, 0x0 }, // 1793 29 lumber mill color (0 - conflict, 1 - horde contr) - { 0x702, 0x0 }, // 1794 30 lumber mill (show/hide) - { 0x703, 0x0 }, // 1795 31 lumber mill color color - { 0x732, 0x1 }, // 1842 32 stables (1 - uncontrolled) - { 0x733, 0x1 }, // 1843 33 gold mine (1 - uncontrolled) - { 0x734, 0x1 }, // 1844 34 lumber mill (1 - uncontrolled) - { 0x735, 0x1 }, // 1845 35 farm (1 - uncontrolled) - { 0x736, 0x1 }, // 1846 36 blacksmith (1 - uncontrolled) - { 0x745, 0x2 }, // 1861 37 unk - { 0x7a3, 0x708 }, // 1955 38 warning limit (1800) - { 0x0, 0x0 } -}; - -static WorldStatePair EY_world_states[] = -{ - { 2753, 0 }, // WORLD_STATE_EY_TOWER_COUNT_HORDE - { 2752, 0 }, // WORLD_STATE_EY_TOWER_COUNT_ALLIANCE - { 2733, WORLD_STATE_REMOVE }, // WORLD_STATE_EY_DRAENEI_RUINS_HORDE - { 2732, WORLD_STATE_REMOVE }, // WORLD_STATE_EY_DRAENEI_RUINS_ALLIANCE - { 2731, WORLD_STATE_REMOVE }, // WORLD_STATE_EY_DRAENEI_RUINS_NEUTRAL - { 2730, WORLD_STATE_REMOVE }, // WORLD_STATE_EY_MAGE_TOWER_ALLIANCE - { 2729, WORLD_STATE_REMOVE }, // WORLD_STATE_EY_MAGE_TOWER_HORDE - { 2728, WORLD_STATE_REMOVE }, // WORLD_STATE_EY_MAGE_TOWER_NEUTRAL - { 2727, WORLD_STATE_REMOVE }, // WORLD_STATE_EY_FEL_REAVER_HORDE - { 2726, WORLD_STATE_REMOVE }, // WORLD_STATE_EY_FEL_REAVER_ALLIANCE - { 2725, WORLD_STATE_REMOVE }, // WORLD_STATE_EY_FEL_REAVER_NEUTRAL - { 2724, WORLD_STATE_REMOVE }, // WORLD_STATE_EY_BLOOD_ELF_HORDE - { 2723, WORLD_STATE_REMOVE }, // WORLD_STATE_EY_BLOOD_ELF_ALLIANCE - { 2722, WORLD_STATE_REMOVE }, // WORLD_STATE_EY_BLOOD_ELF_NEUTRAL - { 2757, WORLD_STATE_REMOVE }, // WORLD_STATE_EY_NETHERSTORM_FLAG_READY - { 2770, 1 }, // WORLD_STATE_EY_NETHERSTORM_FLAG_STATE_HORDE - { 2769, 1 }, // WORLD_STATE_EY_NETHERSTORM_FLAG_STATE_ALLIANCE - { 2750, 0 }, // WORLD_STATE_EY_RESOURCES_HORDE - { 2749, 0 }, // WORLD_STATE_EY_RESOURCES_ALLIANCE - { 2565, 0x8e }, // global unk -- TODO: move to global world state - { 3085, 0x17b } // global unk -- TODO: move to global world state -}; - -static WorldStatePair SI_world_states[] = // Silithus -{ - { 2313, 0 }, // WORLD_STATE_SI_GATHERED_A - { 2314, 0 }, // WORLD_STATE_SI_GATHERED_H - { 2317, 0 } // WORLD_STATE_SI_SILITHYST_MAX -}; - -static WorldStatePair EP_world_states[] = // Eastern Plaguelands -{ - { 2327, 0 }, // WORLD_STATE_EP_TOWER_COUNT_ALLIANCE - { 2328, 0 }, // WORLD_STATE_EP_TOWER_COUNT_HORDE - { 2355, WORLD_STATE_REMOVE }, // WORLD_STATE_EP_CROWNGUARD_NEUTRAL - { 2374, WORLD_STATE_REMOVE }, // WORLD_STATE_EP_CROWNGUARD_CONTEST_ALLIANCE - { 2375, WORLD_STATE_REMOVE }, // WORLD_STATE_EP_CROWNGUARD_CONTEST_HORDE - { 2376, WORLD_STATE_REMOVE }, // WORLD_STATE_EP_CROWNGUARD_PROGRESS_ALLIANCE - { 2377, WORLD_STATE_REMOVE }, // WORLD_STATE_EP_CROWNGUARD_PROGRESS_HORDE - { 2378, WORLD_STATE_REMOVE }, // WORLD_STATE_EP_CROWNGUARD_ALLIANCE - { 2379, WORLD_STATE_REMOVE }, // WORLD_STATE_EP_CROWNGUARD_HORDE - { 2354, WORLD_STATE_REMOVE }, // WORLD_STATE_EP_EASTWALL_ALLIANCE - { 2356, WORLD_STATE_REMOVE }, // WORLD_STATE_EP_EASTWALL_HORDE - { 2357, WORLD_STATE_REMOVE }, // WORLD_STATE_EP_EASTWALL_PROGRESS_ALLIANCE - { 2358, WORLD_STATE_REMOVE }, // WORLD_STATE_EP_EASTWALL_PROGRESS_HORDE - { 2359, WORLD_STATE_REMOVE }, // WORLD_STATE_EP_EASTWALL_CONTEST_ALLIANCE - { 2360, WORLD_STATE_REMOVE }, // WORLD_STATE_EP_EASTWALL_CONTEST_HORDE - { 2361, WORLD_STATE_REMOVE }, // WORLD_STATE_EP_EASTWALL_NEUTRAL - { 2352, WORLD_STATE_REMOVE }, // WORLD_STATE_EP_NORTHPASS_NEUTRAL - { 2362, WORLD_STATE_REMOVE }, // WORLD_STATE_EP_NORTHPASS_CONTEST_ALLIANCE - { 2363, WORLD_STATE_REMOVE }, // WORLD_STATE_EP_NORTHPASS_CONTEST_HORDE - { 2364, WORLD_STATE_REMOVE }, // WORLD_STATE_EP_NORTHPASS_PROGRESS_ALLIANCE - { 2365, WORLD_STATE_REMOVE }, // WORLD_STATE_EP_NORTHPASS_PROGRESS_HORDE - { 2372, WORLD_STATE_REMOVE }, // WORLD_STATE_EP_NORTHPASS_ALLIANCE - { 2373, WORLD_STATE_REMOVE }, // WORLD_STATE_EP_NORTHPASS_HORDE - { 2353, WORLD_STATE_REMOVE }, // WORLD_STATE_EP_PLAGUEWOOD_NEUTRAL - { 2366, WORLD_STATE_REMOVE }, // WORLD_STATE_EP_PLAGUEWOOD_CONTEST_ALLIANCE - { 2367, WORLD_STATE_REMOVE }, // WORLD_STATE_EP_PLAGUEWOOD_CONTEST_HORDE - not in dbc! sent for consistency's sake, and to match field count - { 2368, WORLD_STATE_REMOVE }, // WORLD_STATE_EP_PLAGUEWOOD_PROGRESS_ALLIANCE - { 2369, WORLD_STATE_REMOVE }, // WORLD_STATE_EP_PLAGUEWOOD_PROGRESS_HORDE - { 2370, WORLD_STATE_REMOVE }, // WORLD_STATE_EP_PLAGUEWOOD_ALLIANCE - { 2371, WORLD_STATE_REMOVE } // WORLD_STATE_EP_PLAGUEWOOD_HORDE -}; - -static WorldStatePair HP_world_states[] = // Hellfire Peninsula -{ - { 2490, WORLD_STATE_REMOVE }, // WORLD_STATE_HP_TOWER_DISPLAY_A - { 2489, WORLD_STATE_REMOVE }, // WORLD_STATE_HP_TOWER_DISPLAY_H - { 2485, WORLD_STATE_REMOVE }, // WORLD_STATE_HP_BROKEN_HILL_NEUTRAL - { 2484, WORLD_STATE_REMOVE }, // WORLD_STATE_HP_BROKEN_HILL_HORDE - { 2483, WORLD_STATE_REMOVE }, // WORLD_STATE_HP_BROKEN_HILL_ALLIANCE - { 2482, WORLD_STATE_REMOVE }, // WORLD_STATE_HP_OVERLOOK_NEUTRAL - { 2481, WORLD_STATE_REMOVE }, // WORLD_STATE_HP_OVERLOOK_HORDE - { 2480, WORLD_STATE_REMOVE }, // WORLD_STATE_HP_OVERLOOK_ALLIANCE - { 2478, 0 }, // WORLD_STATE_HP_TOWER_COUNT_HORDE - { 2476, 0 }, // WORLD_STATE_HP_TOWER_COUNT_ALLIANCE - { 2472, WORLD_STATE_REMOVE }, // WORLD_STATE_HP_STADIUM_NEUTRAL - { 2471, WORLD_STATE_REMOVE }, // WORLD_STATE_HP_STADIUM_ALLIANCE - { 2470, WORLD_STATE_REMOVE } // WORLD_STATE_HP_STADIUM_HORDE -}; - -static WorldStatePair TF_world_states[] = // Terokkar Forest -{ - { 2622, 0 }, // WORLD_STATE_TF_TOWER_COUNT_H - { 2621, 0 }, // WORLD_STATE_TF_TOWER_COUNT_A - { 2620, WORLD_STATE_REMOVE }, // WORLD_STATE_TF_TOWERS_CONTROLLED - { 2695, WORLD_STATE_REMOVE }, // WORLD_STATE_TF_SOUTH_EAST_TOWER_HORDE - { 2694, WORLD_STATE_REMOVE }, // WORLD_STATE_TF_SOUTH_EAST_TOWER_ALLIANCE - { 2693, WORLD_STATE_REMOVE }, // WORLD_STATE_TF_SOUTH_TOWER_NEUTRAL - { 2692, WORLD_STATE_REMOVE }, // WORLD_STATE_TF_SOUTH_TOWER_HORDE - { 2691, WORLD_STATE_REMOVE }, // WORLD_STATE_TF_SOUTH_TOWER_ALLIANCE - { 2690, WORLD_STATE_REMOVE }, // WORLD_STATE_TF_EAST_TOWER_NEUTRAL - { 2689, WORLD_STATE_REMOVE }, // WORLD_STATE_TF_EAST_TOWER_HORDE - { 2688, WORLD_STATE_REMOVE }, // WORLD_STATE_TF_EAST_TOWER_ALLIANCE - { 2686, WORLD_STATE_REMOVE }, // WORLD_STATE_TF_NORTH_TOWER_NEUTRAL - { 2685, WORLD_STATE_REMOVE }, // WORLD_STATE_TF_NORTH_TOWER_HORDE - { 2684, WORLD_STATE_REMOVE }, // WORLD_STATE_TF_NORTH_TOWER_ALLIANCE - { 2683, WORLD_STATE_REMOVE }, // WORLD_STATE_TF_WEST_TOWER_ALLIANCE - { 2682, WORLD_STATE_REMOVE }, // WORLD_STATE_TF_WEST_TOWER_HORDE - { 2681, WORLD_STATE_REMOVE }, // WORLD_STATE_TF_WEST_TOWER_NEUTRAL - { 2512, 0 }, // WORLD_STATE_TF_TIME_MIN_FIRST_DIGIT - { 2510, 0 }, // WORLD_STATE_TF_TIME_MIN_SECOND_DIGIT - { 2509, 0 }, // WORLD_STATE_TF_TIME_HOURS - { 2508, WORLD_STATE_REMOVE }, // WORLD_STATE_TF_LOCKED_NEUTRAL - { 2696, WORLD_STATE_REMOVE }, // WORLD_STATE_TF_SOUTH_EAST_TOWER_NEUTRAL - { 2768, WORLD_STATE_REMOVE }, // WORLD_STATE_TF_LOCKED_HORDE - { 2767, WORLD_STATE_REMOVE } // WORLD_STATE_TF_LOCKED_ALLIANCE -}; - -static WorldStatePair ZM_world_states[] = // Zangarmarsh -{ - { 2653, 0x1 }, // WORLD_STATE_ZM_UNK - { 2652, WORLD_STATE_REMOVE }, // WORLD_STATE_ZM_BEACON_EAST_NEUTRAL - { 2651, WORLD_STATE_REMOVE }, // WORLD_STATE_ZM_BEACON_EAST_HORDE - { 2650, WORLD_STATE_REMOVE }, // WORLD_STATE_ZM_BEACON_EAST_ALLIANCE - { 2649, WORLD_STATE_REMOVE }, // WORLD_STATE_ZM_GRAVEYARD_HORDE - { 2648, WORLD_STATE_REMOVE }, // WORLD_STATE_ZM_GRAVEYARD_ALLIANCE - { 2647, WORLD_STATE_REMOVE }, // WORLD_STATE_ZM_GRAVEYARD_NEUTRAL - { 2646, WORLD_STATE_REMOVE }, // WORLD_STATE_ZM_BEACON_WEST_NEUTRAL - { 2645, WORLD_STATE_REMOVE }, // WORLD_STATE_ZM_BEACON_WEST_HORDE - { 2644, WORLD_STATE_REMOVE }, // WORLD_STATE_ZM_BEACON_WEST_ALLIANCE - { 2560, WORLD_STATE_REMOVE }, // WORLD_STATE_ZM_BEACON_EAST_UI_NEUTRAL - { 2559, WORLD_STATE_REMOVE }, // WORLD_STATE_ZM_BEACON_EAST_UI_HORDE - { 2558, WORLD_STATE_REMOVE }, // WORLD_STATE_ZM_BEACON_EAST_UI_ALLIANCE - { 2557, WORLD_STATE_REMOVE }, // WORLD_STATE_ZM_BEACON_WEST_UI_NEUTRAL - { 2556, WORLD_STATE_REMOVE }, // WORLD_STATE_ZM_BEACON_WEST_UI_HORDE - { 2555, WORLD_STATE_REMOVE }, // WORLD_STATE_ZM_BEACON_WEST_UI_ALLIANCE - { 2658, WORLD_STATE_REMOVE }, // WORLD_STATE_ZM_FLAG_READY_HORDE - { 2657, WORLD_STATE_REMOVE }, // WORLD_STATE_ZM_FLAG_NOT_READY_HORDE - { 2656, WORLD_STATE_REMOVE }, // WORLD_STATE_ZM_FLAG_NOT_READY_ALLIANCE - { 2655, WORLD_STATE_REMOVE } // WORLD_STATE_ZM_FLAG_READY_ALLIANCE -}; - -static WorldStatePair NA_world_states[] = -{ - { 2503, 0 }, // WORLD_STATE_NA_GUARDS_HORDE - { 2502, 0 }, // WORLD_STATE_NA_GUARDS_ALLIANCE - { 2493, 0 }, // WORLD_STATE_NA_GUARDS_MAX - { 2491, 0 }, // WORLD_STATE_NA_GUARDS_LEFT - { 2762, WORLD_STATE_REMOVE }, // WORLD_STATE_NA_WYVERN_NORTH_NEUTRAL_H - { 2662, WORLD_STATE_REMOVE }, // WORLD_STATE_NA_WYVERN_NORTH_NEUTRAL_A - { 2663, WORLD_STATE_REMOVE }, // WORLD_STATE_NA_WYVERN_NORTH_H - { 2664, WORLD_STATE_REMOVE }, // WORLD_STATE_NA_WYVERN_NORTH_A - { 2760, WORLD_STATE_REMOVE }, // WORLD_STATE_NA_WYVERN_SOUTH_NEUTRAL_H - { 2670, WORLD_STATE_REMOVE }, // WORLD_STATE_NA_WYVERN_SOUTH_NEUTRAL_A - { 2668, WORLD_STATE_REMOVE }, // WORLD_STATE_NA_WYVERN_SOUTH_H - { 2669, WORLD_STATE_REMOVE }, // WORLD_STATE_NA_WYVERN_SOUTH_A - { 2761, WORLD_STATE_REMOVE }, // WORLD_STATE_NA_WYVERN_WEST_NEUTRAL_H - { 2667, WORLD_STATE_REMOVE }, // WORLD_STATE_NA_WYVERN_WEST_NEUTRAL_A - { 2665, WORLD_STATE_REMOVE }, // WORLD_STATE_NA_WYVERN_WEST_H - { 2666, WORLD_STATE_REMOVE }, // WORLD_STATE_NA_WYVERN_WEST_A - { 2763, WORLD_STATE_REMOVE }, // WORLD_STATE_NA_WYVERN_EAST_NEUTRAL_H - { 2659, WORLD_STATE_REMOVE }, // WORLD_STATE_NA_WYVERN_EAST_NEUTRAL_A - { 2660, WORLD_STATE_REMOVE }, // WORLD_STATE_NA_WYVERN_EAST_H - { 2661, WORLD_STATE_REMOVE }, // WORLD_STATE_NA_WYVERN_EAST_A - { 2671, WORLD_STATE_REMOVE }, // WORLD_STATE_NA_HALAA_NEUTRAL - { 2676, WORLD_STATE_REMOVE }, // WORLD_STATE_NA_HALAA_NEUTRAL_A - { 2677, WORLD_STATE_REMOVE }, // WORLD_STATE_NA_HALAA_NEUTRAL_H - { 2672, WORLD_STATE_REMOVE }, // WORLD_STATE_NA_HALAA_HORDE - { 2673, WORLD_STATE_REMOVE } // WORLD_STATE_NA_HALAA_ALLIANCE -}; - -void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid) -{ - // data depends on zoneid/mapid... - BattleGround* bg = GetBattleGround(); - uint32 mapid = GetMapId(); - - DEBUG_LOG("Sending SMSG_INIT_WORLD_STATES to Map:%u, Zone: %u", mapid, zoneid); - - uint32 count = 0; // count of world states in packet - - WorldPacket data(SMSG_INIT_WORLD_STATES, (4 + 4 + 4 + 2 + 8 * 8)); // guess - data << uint32(mapid); // mapid - data << uint32(zoneid); // zone id - data << uint32(areaid); // area id, new 2.1.0 - size_t count_pos = data.wpos(); - data << uint16(0); // count of uint64 blocks, placeholder - - // common fields - FillInitialWorldState(data, count, 0x8d8, 0x0); // 2264 1 - FillInitialWorldState(data, count, 0x8d7, 0x0); // 2263 2 - FillInitialWorldState(data, count, 0x8d6, 0x0); // 2262 3 - FillInitialWorldState(data, count, 0x8d5, 0x0); // 2261 4 - FillInitialWorldState(data, count, 0x8d4, 0x0); // 2260 5 - FillInitialWorldState(data, count, 0x8d3, 0x0); // 2259 6 - // 3191 7 Current arena season - FillInitialWorldState(data, count, 0xC77, sWorld.getConfig(CONFIG_UINT32_ARENA_SEASON_ID)); - // 3901 8 Previous arena season - FillInitialWorldState(data, count, 0xF3D, sWorld.getConfig(CONFIG_UINT32_ARENA_SEASON_PREVIOUS_ID)); - FillInitialWorldState(data, count, 0xED9, 1); // 3801 9 0 - Battle for Wintergrasp in progress, 1 - otherwise - // 4354 10 Time when next Battle for Wintergrasp starts - FillInitialWorldState(data, count, 0x1102, uint32(time(NULL) + 9000)); - - if (mapid == 530) // Outland - { - FillInitialWorldState(data, count, 0x9bf, 0x0); // 2495 - FillInitialWorldState(data, count, 0x9bd, 0xF); // 2493 - FillInitialWorldState(data, count, 0x9bb, 0xF); // 2491 - } - - switch (zoneid) - { - case 1: // Dun Morogh - case 11: // Wetlands - case 12: // Elwynn Forest - case 38: // Loch Modan - case 40: // Westfall - case 51: // Searing Gorge - case 1519: // Stormwind City - case 1537: // Ironforge - case 2257: // Deeprun Tram - case 3703: // Shattrath City - break; - case 139: // Eastern Plaguelands - if (OutdoorPvP* outdoorPvP = sOutdoorPvPMgr.GetScript(zoneid)) - outdoorPvP->FillInitialWorldStates(data, count); - else - FillInitialWorldState(data, count, EP_world_states); - break; - case 1377: // Silithus - if (OutdoorPvP* outdoorPvP = sOutdoorPvPMgr.GetScript(zoneid)) - outdoorPvP->FillInitialWorldStates(data, count); - else - FillInitialWorldState(data, count, SI_world_states); - break; - case 2597: // AV - if (bg && bg->GetTypeID() == BATTLEGROUND_AV) - bg->FillInitialWorldStates(data, count); - else - FillInitialWorldState(data, count, AV_world_states); - break; - case 3277: // WS - if (bg && bg->GetTypeID() == BATTLEGROUND_WS) - bg->FillInitialWorldStates(data, count); - else - FillInitialWorldState(data, count, WS_world_states); - break; - case 3358: // AB - if (bg && bg->GetTypeID() == BATTLEGROUND_AB) - bg->FillInitialWorldStates(data, count); - else - FillInitialWorldState(data, count, AB_world_states); - break; - case 3820: // EY - if (bg && bg->GetTypeID() == BATTLEGROUND_EY) - bg->FillInitialWorldStates(data, count); - else - FillInitialWorldState(data, count, EY_world_states); - break; - case 3483: // Hellfire Peninsula - if (OutdoorPvP* outdoorPvP = sOutdoorPvPMgr.GetScript(zoneid)) - outdoorPvP->FillInitialWorldStates(data, count); - else - FillInitialWorldState(data, count, HP_world_states); - break; - case 3518: // Nagrand - if (OutdoorPvP* outdoorPvP = sOutdoorPvPMgr.GetScript(zoneid)) - outdoorPvP->FillInitialWorldStates(data, count); - else - FillInitialWorldState(data, count, NA_world_states); - break; - case 3519: // Terokkar Forest - if (OutdoorPvP* outdoorPvP = sOutdoorPvPMgr.GetScript(zoneid)) - outdoorPvP->FillInitialWorldStates(data, count); - else - FillInitialWorldState(data, count, TF_world_states); - break; - case 3521: // Zangarmarsh - if (OutdoorPvP* outdoorPvP = sOutdoorPvPMgr.GetScript(zoneid)) - outdoorPvP->FillInitialWorldStates(data, count); - else - FillInitialWorldState(data, count, ZM_world_states); - break; - case 3698: // Nagrand Arena - if (bg && bg->GetTypeID() == BATTLEGROUND_NA) - bg->FillInitialWorldStates(data, count); - else - { - FillInitialWorldState(data, count, 0xa0f, 0x0); // 2575 7 - FillInitialWorldState(data, count, 0xa10, 0x0); // 2576 8 - FillInitialWorldState(data, count, 0xa11, 0x0); // 2577 9 show - } - break; - case 3702: // Blade's Edge Arena - if (bg && bg->GetTypeID() == BATTLEGROUND_BE) - bg->FillInitialWorldStates(data, count); - else - { - FillInitialWorldState(data, count, 0x9f0, 0x0); // 2544 7 gold - FillInitialWorldState(data, count, 0x9f1, 0x0); // 2545 8 green - FillInitialWorldState(data, count, 0x9f3, 0x0); // 2547 9 show - } - break; - case 3968: // Ruins of Lordaeron - if (bg && bg->GetTypeID() == BATTLEGROUND_RL) - bg->FillInitialWorldStates(data, count); - else - { - FillInitialWorldState(data, count, 0xbb8, 0x0); // 3000 7 gold - FillInitialWorldState(data, count, 0xbb9, 0x0); // 3001 8 green - FillInitialWorldState(data, count, 0xbba, 0x0); // 3002 9 show - } - break; - default: - FillInitialWorldState(data, count, 0x914, 0x0); // 2324 7 - FillInitialWorldState(data, count, 0x913, 0x0); // 2323 8 - FillInitialWorldState(data, count, 0x912, 0x0); // 2322 9 - FillInitialWorldState(data, count, 0x915, 0x0); // 2325 10 - break; - } - - FillBGWeekendWorldStates(data, count); - - data.put(count_pos, count); // set actual world state amount - - GetSession()->SendPacket(&data); -} - -void Player::FillBGWeekendWorldStates(WorldPacket& data, uint32& count) -{ - for (uint32 i = 1; i < sBattlemasterListStore.GetNumRows(); ++i) - { - BattlemasterListEntry const* bl = sBattlemasterListStore.LookupEntry(i); - if (bl && bl->HolidayWorldStateId) - { - if (BattleGroundMgr::IsBGWeekend(BattleGroundTypeId(bl->id))) - FillInitialWorldState(data, count, bl->HolidayWorldStateId, 1); - else - FillInitialWorldState(data, count, bl->HolidayWorldStateId, 0); - } - } -} - -uint32 Player::GetXPRestBonus(uint32 xp) -{ - uint32 rested_bonus = (uint32)GetRestBonus(); // xp for each rested bonus - - if (rested_bonus > xp) // max rested_bonus == xp or (r+x) = 200% xp - rested_bonus = xp; - - SetRestBonus(GetRestBonus() - rested_bonus); - - DETAIL_LOG("Player gain %u xp (+ %u Rested Bonus). Rested points=%f", xp + rested_bonus, rested_bonus, GetRestBonus()); - return rested_bonus; -} - -void Player::SetBindPoint(ObjectGuid guid) -{ - WorldPacket data(SMSG_BINDER_CONFIRM, 8); - data << ObjectGuid(guid); - GetSession()->SendPacket(&data); -} - -void Player::SendTalentWipeConfirm(ObjectGuid guid) -{ - WorldPacket data(MSG_TALENT_WIPE_CONFIRM, (8 + 4)); - data << ObjectGuid(guid); - data << uint32(resetTalentsCost()); - GetSession()->SendPacket(&data); -} - -void Player::SendPetSkillWipeConfirm() -{ - Pet* pet = GetPet(); - if (!pet) - return; - WorldPacket data(SMSG_PET_UNLEARN_CONFIRM, (8 + 4)); - data << ObjectGuid(pet->GetObjectGuid()); - data << uint32(pet->resetTalentsCost()); - GetSession()->SendPacket(&data); -} - -/*********************************************************/ -/*** STORAGE SYSTEM ***/ -/*********************************************************/ - -void Player::SetVirtualItemSlot(uint8 i, Item* item) -{ - MANGOS_ASSERT(i < 3); - if (i < 2 && item) - { - if (!item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT)) - return; - uint32 charges = item->GetEnchantmentCharges(TEMP_ENCHANTMENT_SLOT); - if (charges == 0) - return; - if (charges > 1) - item->SetEnchantmentCharges(TEMP_ENCHANTMENT_SLOT, charges - 1); - else if (charges <= 1) - { - ApplyEnchantment(item, TEMP_ENCHANTMENT_SLOT, false); - item->ClearEnchantment(TEMP_ENCHANTMENT_SLOT); - } - } -} - -void Player::SetSheath(SheathState sheathed) -{ - switch (sheathed) - { - case SHEATH_STATE_UNARMED: // no prepared weapon - SetVirtualItemSlot(0, NULL); - SetVirtualItemSlot(1, NULL); - SetVirtualItemSlot(2, NULL); - break; - case SHEATH_STATE_MELEE: // prepared melee weapon - { - SetVirtualItemSlot(0, GetWeaponForAttack(BASE_ATTACK, true, true)); - SetVirtualItemSlot(1, GetWeaponForAttack(OFF_ATTACK, true, true)); - SetVirtualItemSlot(2, NULL); - }; break; - case SHEATH_STATE_RANGED: // prepared ranged weapon - SetVirtualItemSlot(0, NULL); - SetVirtualItemSlot(1, NULL); - SetVirtualItemSlot(2, GetWeaponForAttack(RANGED_ATTACK, true, true)); - break; - default: - SetVirtualItemSlot(0, NULL); - SetVirtualItemSlot(1, NULL); - SetVirtualItemSlot(2, NULL); - break; - } - Unit::SetSheath(sheathed); // this must visualize Sheath changing for other players... -} - -bool Player::GetSlotsForInventoryType(uint8 invType, uint8* slots, uint32 subClass) const -{ - uint8 pClass = getClass(); - - slots[0] = NULL_SLOT; - slots[1] = NULL_SLOT; - slots[2] = NULL_SLOT; - slots[3] = NULL_SLOT; - switch (invType) - { - case INVTYPE_HEAD: - slots[0] = EQUIPMENT_SLOT_HEAD; - break; - case INVTYPE_NECK: - slots[0] = EQUIPMENT_SLOT_NECK; - break; - case INVTYPE_SHOULDERS: - slots[0] = EQUIPMENT_SLOT_SHOULDERS; - break; - case INVTYPE_BODY: - slots[0] = EQUIPMENT_SLOT_BODY; - break; - case INVTYPE_CHEST: - slots[0] = EQUIPMENT_SLOT_CHEST; - break; - case INVTYPE_ROBE: - slots[0] = EQUIPMENT_SLOT_CHEST; - break; - case INVTYPE_WAIST: - slots[0] = EQUIPMENT_SLOT_WAIST; - break; - case INVTYPE_LEGS: - slots[0] = EQUIPMENT_SLOT_LEGS; - break; - case INVTYPE_FEET: - slots[0] = EQUIPMENT_SLOT_FEET; - break; - case INVTYPE_WRISTS: - slots[0] = EQUIPMENT_SLOT_WRISTS; - break; - case INVTYPE_HANDS: - slots[0] = EQUIPMENT_SLOT_HANDS; - break; - case INVTYPE_FINGER: - slots[0] = EQUIPMENT_SLOT_FINGER1; - slots[1] = EQUIPMENT_SLOT_FINGER2; - break; - case INVTYPE_TRINKET: - slots[0] = EQUIPMENT_SLOT_TRINKET1; - slots[1] = EQUIPMENT_SLOT_TRINKET2; - break; - case INVTYPE_CLOAK: - slots[0] = EQUIPMENT_SLOT_BACK; - break; - case INVTYPE_WEAPON: - { - slots[0] = EQUIPMENT_SLOT_MAINHAND; - - // suggest offhand slot only if know dual wielding - // (this will be replace mainhand weapon at auto equip instead unwanted "you don't known dual wielding" ... - if (CanDualWield()) - slots[1] = EQUIPMENT_SLOT_OFFHAND; - break; - } - case INVTYPE_SHIELD: - slots[0] = EQUIPMENT_SLOT_OFFHAND; - break; - case INVTYPE_RANGED: - slots[0] = EQUIPMENT_SLOT_RANGED; - break; - case INVTYPE_2HWEAPON: - slots[0] = EQUIPMENT_SLOT_MAINHAND; - if (CanDualWield() && CanTitanGrip()) - slots[1] = EQUIPMENT_SLOT_OFFHAND; - break; - case INVTYPE_TABARD: - slots[0] = EQUIPMENT_SLOT_TABARD; - break; - case INVTYPE_WEAPONMAINHAND: - slots[0] = EQUIPMENT_SLOT_MAINHAND; - break; - case INVTYPE_WEAPONOFFHAND: - slots[0] = EQUIPMENT_SLOT_OFFHAND; - break; - case INVTYPE_HOLDABLE: - slots[0] = EQUIPMENT_SLOT_OFFHAND; - break; - case INVTYPE_THROWN: - slots[0] = EQUIPMENT_SLOT_RANGED; - break; - case INVTYPE_RANGEDRIGHT: - slots[0] = EQUIPMENT_SLOT_RANGED; - break; - case INVTYPE_BAG: - slots[0] = INVENTORY_SLOT_BAG_START + 0; - slots[1] = INVENTORY_SLOT_BAG_START + 1; - slots[2] = INVENTORY_SLOT_BAG_START + 2; - slots[3] = INVENTORY_SLOT_BAG_START + 3; - break; - case INVTYPE_RELIC: - { - if (subClass == ITEM_SUBCLASS_ARMOR_RELIC && - (pClass == CLASS_PALADIN || pClass == CLASS_DRUID || - pClass == CLASS_SHAMAN || pClass == CLASS_DEATH_KNIGHT)) - slots[0] = EQUIPMENT_SLOT_RANGED; - break; - } - default: - return false; - } - - return true; -} - -uint8 Player::FindEquipSlot(ItemPrototype const* proto, uint32 slot, bool swap) const -{ - uint8 slots[4]; - if (!GetSlotsForInventoryType(proto->InventoryType, slots, proto->SubClass)) - return NULL_SLOT; - - if (slot != NULL_SLOT) - { - if (swap || !GetItemByPos(INVENTORY_SLOT_BAG_0, slot)) - { - for (int i = 0; i < 4; ++i) - { - if (slots[i] == slot) - return slot; - } - } - } - else - { - // search free slot at first - for (int i = 0; i < 4; ++i) - { - if (slots[i] != NULL_SLOT && !GetItemByPos(INVENTORY_SLOT_BAG_0, slots[i])) - { - // in case 2hand equipped weapon (without titan grip) offhand slot empty but not free - if (slots[i] != EQUIPMENT_SLOT_OFFHAND || !IsTwoHandUsed()) - return slots[i]; - } - } - - // if not found free and can swap return first appropriate from used - for (int i = 0; i < 4; ++i) - { - if (slots[i] != NULL_SLOT && swap) - return slots[i]; - } - } - - // no free position - return NULL_SLOT; -} - -InventoryResult Player::CanUnequipItems(uint32 item, uint32 count) const -{ - Item* pItem; - uint32 tempcount = 0; - - InventoryResult res = EQUIP_ERR_OK; - - for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; ++i) - { - pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (pItem && pItem->GetEntry() == item) - { - InventoryResult ires = CanUnequipItem(INVENTORY_SLOT_BAG_0 << 8 | i, false); - if (ires == EQUIP_ERR_OK) - { - tempcount += pItem->GetCount(); - if (tempcount >= count) - return EQUIP_ERR_OK; - } - else - res = ires; - } - } - for (int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i) - { - pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (pItem && pItem->GetEntry() == item) - { - tempcount += pItem->GetCount(); - if (tempcount >= count) - return EQUIP_ERR_OK; - } - } - Bag* pBag; - for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) - { - pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (pBag) - { - for (uint32 j = 0; j < pBag->GetBagSize(); ++j) - { - pItem = GetItemByPos(i, j); - if (pItem && pItem->GetEntry() == item) - { - tempcount += pItem->GetCount(); - if (tempcount >= count) - return EQUIP_ERR_OK; - } - } - } - } - - // not found req. item count and have unequippable items - return res; -} - -uint32 Player::GetItemCount(uint32 item, bool inBankAlso, Item* skipItem) const -{ - uint32 count = 0; - for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i) - { - Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (pItem && pItem != skipItem && pItem->GetEntry() == item) - count += pItem->GetCount(); - } - for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) - { - Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (pBag) - count += pBag->GetItemCount(item, skipItem); - } - - if (skipItem && skipItem->GetProto()->GemProperties) - { - for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i) - { - Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (pItem && pItem != skipItem && pItem->GetProto()->Socket[0].Color) - count += pItem->GetGemCountWithID(item); - } - } - - if (inBankAlso) - { - for (int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; ++i) - { - Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (pItem && pItem != skipItem && pItem->GetEntry() == item) - count += pItem->GetCount(); - } - for (int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i) - { - Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (pBag) - count += pBag->GetItemCount(item, skipItem); - } - - if (skipItem && skipItem->GetProto()->GemProperties) - { - for (int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; ++i) - { - Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (pItem && pItem != skipItem && pItem->GetProto()->Socket[0].Color) - count += pItem->GetGemCountWithID(item); - } - } - } - - return count; -} - -uint32 Player::GetItemCountWithLimitCategory(uint32 limitCategory, Item* skipItem) const -{ - uint32 count = 0; - for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i) - if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - if (pItem->GetProto()->ItemLimitCategory == limitCategory && pItem != skipItem) - count += pItem->GetCount(); - - for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) - if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - count += pBag->GetItemCountWithLimitCategory(limitCategory, skipItem); - - for (int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; ++i) - if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - if (pItem->GetProto()->ItemLimitCategory == limitCategory && pItem != skipItem) - count += pItem->GetCount(); - - for (int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i) - if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - count += pBag->GetItemCountWithLimitCategory(limitCategory, skipItem); - - return count; -} - -Item* Player::GetItemByEntry(uint32 item) const -{ - for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i) - if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - if (pItem->GetEntry() == item) - return pItem; - - for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) - if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - if (Item* itemPtr = pBag->GetItemByEntry(item)) - return itemPtr; - - return NULL; -} - -Item* Player::GetItemByLimitedCategory(uint32 limitedCategory) const -{ - for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i) - if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - if (pItem->GetProto()->ItemLimitCategory == limitedCategory) - return pItem; - - for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) - if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - if (Item* itemPtr = pBag->GetItemByLimitedCategory(limitedCategory)) - return itemPtr; - - return NULL; -} - -Item* Player::GetItemByGuid(ObjectGuid guid) const -{ - for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i) - if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - if (pItem->GetObjectGuid() == guid) - return pItem; - - for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) - if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - for (uint32 j = 0; j < pBag->GetBagSize(); ++j) - if (Item* pItem = pBag->GetItemByPos(j)) - if (pItem->GetObjectGuid() == guid) - return pItem; - - for (int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; ++i) - if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - if (pItem->GetObjectGuid() == guid) - return pItem; - - for (int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i) - if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - for (uint32 j = 0; j < pBag->GetBagSize(); ++j) - if (Item* pItem = pBag->GetItemByPos(j)) - if (pItem->GetObjectGuid() == guid) - return pItem; - - return NULL; -} - -Item* Player::GetItemByPos(uint16 pos) const -{ - uint8 bag = pos >> 8; - uint8 slot = pos & 255; - return GetItemByPos(bag, slot); -} - -Item* Player::GetItemByPos(uint8 bag, uint8 slot) const -{ - if (bag == INVENTORY_SLOT_BAG_0 && slot < BANK_SLOT_BAG_END) - return m_items[slot]; - else if ((bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END) - || (bag >= BANK_SLOT_BAG_START && bag < BANK_SLOT_BAG_END)) - { - Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, bag); - if (pBag) - return pBag->GetItemByPos(slot); - } - return NULL; -} - -uint32 Player::GetItemDisplayIdInSlot(uint8 bag, uint8 slot) const -{ - const Item* pItem = GetItemByPos(bag, slot); - - if (!pItem) - return 0; - - return pItem->GetProto()->DisplayInfoID; -} - -Item* Player::GetWeaponForAttack(WeaponAttackType attackType, bool nonbroken, bool useable) const -{ - uint8 slot; - switch (attackType) - { - case BASE_ATTACK: slot = EQUIPMENT_SLOT_MAINHAND; break; - case OFF_ATTACK: slot = EQUIPMENT_SLOT_OFFHAND; break; - case RANGED_ATTACK: slot = EQUIPMENT_SLOT_RANGED; break; - default: return NULL; - } - - Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, slot); - if (!item || item->GetProto()->Class != ITEM_CLASS_WEAPON) - return NULL; - - if (useable && !CanUseEquippedWeapon(attackType)) - return NULL; - - if (nonbroken && item->IsBroken()) - return NULL; - - return item; -} - -Item* Player::GetShield(bool useable) const -{ - Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); - if (!item || item->GetProto()->Class != ITEM_CLASS_ARMOR) - return NULL; - - if (!useable) - return item; - - if (item->IsBroken() || !CanUseEquippedWeapon(OFF_ATTACK)) - return NULL; - - return item; -} - -uint32 Player::GetAttackBySlot(uint8 slot) -{ - switch (slot) - { - case EQUIPMENT_SLOT_MAINHAND: return BASE_ATTACK; - case EQUIPMENT_SLOT_OFFHAND: return OFF_ATTACK; - case EQUIPMENT_SLOT_RANGED: return RANGED_ATTACK; - default: return MAX_ATTACK; - } -} - -bool Player::IsInventoryPos(uint8 bag, uint8 slot) -{ - if (bag == INVENTORY_SLOT_BAG_0 && slot == NULL_SLOT) - return true; - if (bag == INVENTORY_SLOT_BAG_0 && (slot >= INVENTORY_SLOT_ITEM_START && slot < INVENTORY_SLOT_ITEM_END)) - return true; - if (bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END) - return true; - return false; -} - -bool Player::IsEquipmentPos(uint8 bag, uint8 slot) -{ - if (bag == INVENTORY_SLOT_BAG_0 && (slot < EQUIPMENT_SLOT_END)) - return true; - if (bag == INVENTORY_SLOT_BAG_0 && (slot >= INVENTORY_SLOT_BAG_START && slot < INVENTORY_SLOT_BAG_END)) - return true; - return false; -} - -bool Player::IsBankPos(uint8 bag, uint8 slot) -{ - if (bag == INVENTORY_SLOT_BAG_0 && (slot >= BANK_SLOT_ITEM_START && slot < BANK_SLOT_ITEM_END)) - return true; - if (bag == INVENTORY_SLOT_BAG_0 && (slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END)) - return true; - if (bag >= BANK_SLOT_BAG_START && bag < BANK_SLOT_BAG_END) - return true; - return false; -} - -bool Player::IsBagPos(uint16 pos) -{ - uint8 bag = pos >> 8; - uint8 slot = pos & 255; - if (bag == INVENTORY_SLOT_BAG_0 && (slot >= INVENTORY_SLOT_BAG_START && slot < INVENTORY_SLOT_BAG_END)) - return true; - if (bag == INVENTORY_SLOT_BAG_0 && (slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END)) - return true; - return false; -} - -bool Player::IsValidPos(uint8 bag, uint8 slot, bool explicit_pos) const -{ - // post selected - if (bag == NULL_BAG && !explicit_pos) - return true; - - if (bag == INVENTORY_SLOT_BAG_0) - { - // any post selected - if (slot == NULL_SLOT && !explicit_pos) - return true; - - // equipment - if (slot < EQUIPMENT_SLOT_END) - return true; - - // bag equip slots - if (slot >= INVENTORY_SLOT_BAG_START && slot < INVENTORY_SLOT_BAG_END) - return true; - - // backpack slots - if (slot >= INVENTORY_SLOT_ITEM_START && slot < INVENTORY_SLOT_ITEM_END) - return true; - - // bank main slots - if (slot >= BANK_SLOT_ITEM_START && slot < BANK_SLOT_ITEM_END) - return true; - - // bank bag slots - if (slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END) - return true; - - return false; - } - - // bag content slots - if (bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END) - { - Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, bag); - if (!pBag) - return false; - - // any post selected - if (slot == NULL_SLOT && !explicit_pos) - return true; - - return slot < pBag->GetBagSize(); - } - - // bank bag content slots - if (bag >= BANK_SLOT_BAG_START && bag < BANK_SLOT_BAG_END) - { - Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, bag); - if (!pBag) - return false; - - // any post selected - if (slot == NULL_SLOT && !explicit_pos) - return true; - - return slot < pBag->GetBagSize(); - } - - // where this? - return false; -} - -bool Player::HasItemCount(uint32 item, uint32 count, bool inBankAlso) const -{ - uint32 tempcount = 0; - for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i) - { - Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (pItem && pItem->GetEntry() == item && !pItem->IsInTrade()) - { - tempcount += pItem->GetCount(); - if (tempcount >= count) - return true; - } - } - for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) - { - if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - { - for (uint32 j = 0; j < pBag->GetBagSize(); ++j) - { - Item* pItem = GetItemByPos(i, j); - if (pItem && pItem->GetEntry() == item && !pItem->IsInTrade()) - { - tempcount += pItem->GetCount(); - if (tempcount >= count) - return true; - } - } - } - } - - if (inBankAlso) - { - for (int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; ++i) - { - Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (pItem && pItem->GetEntry() == item && !pItem->IsInTrade()) - { - tempcount += pItem->GetCount(); - if (tempcount >= count) - return true; - } - } - for (int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i) - { - if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - { - for (uint32 j = 0; j < pBag->GetBagSize(); ++j) - { - Item* pItem = GetItemByPos(i, j); - if (pItem && pItem->GetEntry() == item && !pItem->IsInTrade()) - { - tempcount += pItem->GetCount(); - if (tempcount >= count) - return true; - } - } - } - } - } - - return false; -} - -bool Player::HasItemOrGemWithIdEquipped(uint32 item, uint32 count, uint8 except_slot) const -{ - uint32 tempcount = 0; - for (int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i) - { - if (i == int(except_slot)) - continue; - - Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (pItem && pItem->GetEntry() == item) - { - tempcount += pItem->GetCount(); - if (tempcount >= count) - return true; - } - } - - ItemPrototype const* pProto = ObjectMgr::GetItemPrototype(item); - if (pProto && pProto->GemProperties) - { - for (int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i) - { - if (i == int(except_slot)) - continue; - - Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (pItem && pItem->GetProto()->Socket[0].Color) - { - tempcount += pItem->GetGemCountWithID(item); - if (tempcount >= count) - return true; - } - } - } - - return false; -} - -bool Player::HasItemOrGemWithLimitCategoryEquipped(uint32 limitCategory, uint32 count, uint8 except_slot) const -{ - uint32 tempcount = 0; - for (int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i) - { - if (i == int(except_slot)) - continue; - - Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (!pItem) - continue; - - ItemPrototype const* pProto = pItem->GetProto(); - if (!pProto) - continue; - - if (pProto->ItemLimitCategory == limitCategory) - { - tempcount += pItem->GetCount(); - if (tempcount >= count) - return true; - } - - if (pProto->Socket[0].Color) - { - tempcount += pItem->GetGemCountWithLimitCategory(limitCategory); - if (tempcount >= count) - return true; - } - } - - return false; -} - -InventoryResult Player::_CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, uint32* no_space_count) const -{ - ItemPrototype const* pProto = ObjectMgr::GetItemPrototype(entry); - if (!pProto) - { - if (no_space_count) - *no_space_count = count; - return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; - } - - // no maximum - if (pProto->MaxCount > 0) - { - uint32 curcount = GetItemCount(pProto->ItemId, true, pItem); - - if (curcount + count > uint32(pProto->MaxCount)) - { - if (no_space_count) - *no_space_count = count + curcount - pProto->MaxCount; - return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; - } - } - - // check unique-equipped limit - if (pProto->ItemLimitCategory) - { - ItemLimitCategoryEntry const* limitEntry = sItemLimitCategoryStore.LookupEntry(pProto->ItemLimitCategory); - if (!limitEntry) - { - if (no_space_count) - *no_space_count = count; - return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED; - } - - if (limitEntry->mode == ITEM_LIMIT_CATEGORY_MODE_HAVE) - { - uint32 curcount = GetItemCountWithLimitCategory(pProto->ItemLimitCategory, pItem); - - if (curcount + count > uint32(limitEntry->maxCount)) - { - if (no_space_count) - *no_space_count = count + curcount - limitEntry->maxCount; - return EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_COUNT_EXCEEDED_IS; - } - } - } - - return EQUIP_ERR_OK; -} - -InventoryResult Player::_CanStoreItem_InSpecificSlot(uint8 bag, uint8 slot, ItemPosCountVec& dest, ItemPrototype const* pProto, uint32& count, bool swap, Item* pSrcItem) const -{ - Item* pItem2 = GetItemByPos(bag, slot); - - // ignore move item (this slot will be empty at move) - if (pItem2 == pSrcItem) - pItem2 = NULL; - - uint32 need_space; - - // empty specific slot - check item fit to slot - if (!pItem2 || swap) - { - if (bag == INVENTORY_SLOT_BAG_0) - { - // prevent cheating - if ((slot >= BUYBACK_SLOT_START && slot < BUYBACK_SLOT_END) || slot >= PLAYER_SLOT_END) - return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; - } - else - { - Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, bag); - if (!pBag) - return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; - - ItemPrototype const* pBagProto = pBag->GetProto(); - if (!pBagProto) - return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; - - if (slot >= pBagProto->ContainerSlots) - return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; - - if (!ItemCanGoIntoBag(pProto, pBagProto)) - return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; - } - - // non empty stack with space - need_space = pProto->GetMaxStackSize(); - } - // non empty slot, check item type - else - { - // can be merged at least partly - InventoryResult res = pItem2->CanBeMergedPartlyWith(pProto); - if (res != EQUIP_ERR_OK) - return res; - - // free stack space or infinity - need_space = pProto->GetMaxStackSize() - pItem2->GetCount(); - } - - if (need_space > count) - need_space = count; - - ItemPosCount newPosition = ItemPosCount((bag << 8) | slot, need_space); - if (!newPosition.isContainedIn(dest)) - { - dest.push_back(newPosition); - count -= need_space; - } - return EQUIP_ERR_OK; -} - -InventoryResult Player::_CanStoreItem_InBag(uint8 bag, ItemPosCountVec& dest, ItemPrototype const* pProto, uint32& count, bool merge, bool non_specialized, Item* pSrcItem, uint8 skip_bag, uint8 skip_slot) const -{ - // skip specific bag already processed in first called _CanStoreItem_InBag - if (bag == skip_bag) - return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; - - // skip nonexistent bag or self targeted bag - Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, bag); - if (!pBag || pBag == pSrcItem) - return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; - - ItemPrototype const* pBagProto = pBag->GetProto(); - if (!pBagProto) - return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; - - // specialized bag mode or non-specilized - if (non_specialized != (pBagProto->Class == ITEM_CLASS_CONTAINER && pBagProto->SubClass == ITEM_SUBCLASS_CONTAINER)) - return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; - - if (!ItemCanGoIntoBag(pProto, pBagProto)) - return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; - - for (uint32 j = 0; j < pBag->GetBagSize(); ++j) - { - // skip specific slot already processed in first called _CanStoreItem_InSpecificSlot - if (j == skip_slot) - continue; - - Item* pItem2 = GetItemByPos(bag, j); - - // ignore move item (this slot will be empty at move) - if (pItem2 == pSrcItem) - pItem2 = NULL; - - // if merge skip empty, if !merge skip non-empty - if ((pItem2 != NULL) != merge) - continue; - - uint32 need_space = pProto->GetMaxStackSize(); - - if (pItem2) - { - // can be merged at least partly - uint8 res = pItem2->CanBeMergedPartlyWith(pProto); - if (res != EQUIP_ERR_OK) - continue; - - // decrease at current stacksize - need_space -= pItem2->GetCount(); - } - - if (need_space > count) - need_space = count; - - ItemPosCount newPosition = ItemPosCount((bag << 8) | j, need_space); - if (!newPosition.isContainedIn(dest)) - { - dest.push_back(newPosition); - count -= need_space; - - if (count == 0) - return EQUIP_ERR_OK; - } - } - return EQUIP_ERR_OK; -} - -InventoryResult Player::_CanStoreItem_InInventorySlots(uint8 slot_begin, uint8 slot_end, ItemPosCountVec& dest, ItemPrototype const* pProto, uint32& count, bool merge, Item* pSrcItem, uint8 skip_bag, uint8 skip_slot) const -{ - for (uint32 j = slot_begin; j < slot_end; ++j) - { - // skip specific slot already processed in first called _CanStoreItem_InSpecificSlot - if (INVENTORY_SLOT_BAG_0 == skip_bag && j == skip_slot) - continue; - - Item* pItem2 = GetItemByPos(INVENTORY_SLOT_BAG_0, j); - - // ignore move item (this slot will be empty at move) - if (pItem2 == pSrcItem) - pItem2 = NULL; - - // if merge skip empty, if !merge skip non-empty - if ((pItem2 != NULL) != merge) - continue; - - uint32 need_space = pProto->GetMaxStackSize(); - - if (pItem2) - { - // can be merged at least partly - uint8 res = pItem2->CanBeMergedPartlyWith(pProto); - if (res != EQUIP_ERR_OK) - continue; - - // descrease at current stacksize - need_space -= pItem2->GetCount(); - } - - if (need_space > count) - need_space = count; - - ItemPosCount newPosition = ItemPosCount((INVENTORY_SLOT_BAG_0 << 8) | j, need_space); - if (!newPosition.isContainedIn(dest)) - { - dest.push_back(newPosition); - count -= need_space; - - if (count == 0) - return EQUIP_ERR_OK; - } - } - return EQUIP_ERR_OK; -} - -InventoryResult Player::_CanStoreItem(uint8 bag, uint8 slot, ItemPosCountVec& dest, uint32 entry, uint32 count, Item* pItem, bool swap, uint32* no_space_count) const -{ - DEBUG_LOG("STORAGE: CanStoreItem bag = %u, slot = %u, item = %u, count = %u", bag, slot, entry, count); - - ItemPrototype const* pProto = ObjectMgr::GetItemPrototype(entry); - if (!pProto) - { - if (no_space_count) - *no_space_count = count; - return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_ITEM_NOT_FOUND; - } - - if (pItem) - { - // item used - if (pItem->HasTemporaryLoot()) - { - if (no_space_count) - *no_space_count = count; - return EQUIP_ERR_ALREADY_LOOTED; - } - - if (pItem->IsBindedNotWith(this)) - { - if (no_space_count) - *no_space_count = count; - return EQUIP_ERR_DONT_OWN_THAT_ITEM; - } - } - - // check count of items (skip for auto move for same player from bank) - uint32 no_similar_count = 0; // can't store this amount similar items - InventoryResult res = _CanTakeMoreSimilarItems(entry, count, pItem, &no_similar_count); - if (res != EQUIP_ERR_OK) - { - if (count == no_similar_count) - { - if (no_space_count) - *no_space_count = no_similar_count; - return res; - } - count -= no_similar_count; - } - - // in specific slot - if (bag != NULL_BAG && slot != NULL_SLOT) - { - res = _CanStoreItem_InSpecificSlot(bag, slot, dest, pProto, count, swap, pItem); - if (res != EQUIP_ERR_OK) - { - if (no_space_count) - *no_space_count = count + no_similar_count; - return res; - } - - if (count == 0) - { - if (no_similar_count == 0) - return EQUIP_ERR_OK; - - if (no_space_count) - *no_space_count = count + no_similar_count; - return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; - } - } - - // not specific slot or have space for partly store only in specific slot - - // in specific bag - if (bag != NULL_BAG) - { - // search stack in bag for merge to - if (pProto->Stackable != 1) - { - if (bag == INVENTORY_SLOT_BAG_0) // inventory - { - res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START, INVENTORY_SLOT_ITEM_END, dest, pProto, count, true, pItem, bag, slot); - if (res != EQUIP_ERR_OK) - { - if (no_space_count) - *no_space_count = count + no_similar_count; - return res; - } - - if (count == 0) - { - if (no_similar_count == 0) - return EQUIP_ERR_OK; - - if (no_space_count) - *no_space_count = count + no_similar_count; - return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; - } - } - else // equipped bag - { - // we need check 2 time (specialized/non_specialized), use NULL_BAG to prevent skipping bag - res = _CanStoreItem_InBag(bag, dest, pProto, count, true, false, pItem, NULL_BAG, slot); - if (res != EQUIP_ERR_OK) - res = _CanStoreItem_InBag(bag, dest, pProto, count, true, true, pItem, NULL_BAG, slot); - - if (res != EQUIP_ERR_OK) - { - if (no_space_count) - *no_space_count = count + no_similar_count; - return res; - } - - if (count == 0) - { - if (no_similar_count == 0) - return EQUIP_ERR_OK; - - if (no_space_count) - *no_space_count = count + no_similar_count; - return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; - } - } - } - - // search free slot in bag for place to - if (bag == INVENTORY_SLOT_BAG_0) // inventory - { - res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START, INVENTORY_SLOT_ITEM_END, dest, pProto, count, false, pItem, bag, slot); - if (res != EQUIP_ERR_OK) - { - if (no_space_count) - *no_space_count = count + no_similar_count; - return res; - } - - if (count == 0) - { - if (no_similar_count == 0) - return EQUIP_ERR_OK; - - if (no_space_count) - *no_space_count = count + no_similar_count; - return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; - } - } - else // equipped bag - { - res = _CanStoreItem_InBag(bag, dest, pProto, count, false, false, pItem, NULL_BAG, slot); - if (res != EQUIP_ERR_OK) - res = _CanStoreItem_InBag(bag, dest, pProto, count, false, true, pItem, NULL_BAG, slot); - - if (res != EQUIP_ERR_OK) - { - if (no_space_count) - *no_space_count = count + no_similar_count; - return res; - } - - if (count == 0) - { - if (no_similar_count == 0) - return EQUIP_ERR_OK; - - if (no_space_count) - *no_space_count = count + no_similar_count; - return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; - } - } - } - - // not specific bag or have space for partly store only in specific bag - - // search stack for merge to - if (pProto->Stackable != 1) - { - res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START, INVENTORY_SLOT_ITEM_END, dest, pProto, count, true, pItem, bag, slot); - if (res != EQUIP_ERR_OK) - { - if (no_space_count) - *no_space_count = count + no_similar_count; - return res; - } - - if (count == 0) - { - if (no_similar_count == 0) - return EQUIP_ERR_OK; - - if (no_space_count) - *no_space_count = count + no_similar_count; - return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; - } - - if (pProto->BagFamily) - { - for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) - { - res = _CanStoreItem_InBag(i, dest, pProto, count, true, false, pItem, bag, slot); - if (res != EQUIP_ERR_OK) - continue; - - if (count == 0) - { - if (no_similar_count == 0) - return EQUIP_ERR_OK; - - if (no_space_count) - *no_space_count = count + no_similar_count; - return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; - } - } - } - - for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) - { - res = _CanStoreItem_InBag(i, dest, pProto, count, true, true, pItem, bag, slot); - if (res != EQUIP_ERR_OK) - continue; - - if (count == 0) - { - if (no_similar_count == 0) - return EQUIP_ERR_OK; - - if (no_space_count) - *no_space_count = count + no_similar_count; - return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; - } - } - } - - // search free slot - special bag case - if (pProto->BagFamily) - { - for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) - { - res = _CanStoreItem_InBag(i, dest, pProto, count, false, false, pItem, bag, slot); - if (res != EQUIP_ERR_OK) - continue; - - if (count == 0) - { - if (no_similar_count == 0) - return EQUIP_ERR_OK; - - if (no_space_count) - *no_space_count = count + no_similar_count; - return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; - } - } - } - - // Normally it would be impossible to autostore not empty bags - if (pItem && pItem->IsBag() && !((Bag*)pItem)->IsEmpty()) - return EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG; - - // search free slot - res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START, INVENTORY_SLOT_ITEM_END, dest, pProto, count, false, pItem, bag, slot); - if (res != EQUIP_ERR_OK) - { - if (no_space_count) - *no_space_count = count + no_similar_count; - return res; - } - - if (count == 0) - { - if (no_similar_count == 0) - return EQUIP_ERR_OK; - - if (no_space_count) - *no_space_count = count + no_similar_count; - return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; - } - - for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) - { - res = _CanStoreItem_InBag(i, dest, pProto, count, false, true, pItem, bag, slot); - if (res != EQUIP_ERR_OK) - continue; - - if (count == 0) - { - if (no_similar_count == 0) - return EQUIP_ERR_OK; - - if (no_space_count) - *no_space_count = count + no_similar_count; - return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; - } - } - - if (no_space_count) - *no_space_count = count + no_similar_count; - - return EQUIP_ERR_INVENTORY_FULL; -} - -////////////////////////////////////////////////////////////////////////// -InventoryResult Player::CanStoreItems(Item** pItems, int count) const -{ - Item* pItem2; - - // fill space table - int inv_slot_items[INVENTORY_SLOT_ITEM_END - INVENTORY_SLOT_ITEM_START]; - int inv_bags[INVENTORY_SLOT_BAG_END - INVENTORY_SLOT_BAG_START][MAX_BAG_SIZE]; - - memset(inv_slot_items, 0, sizeof(int) * (INVENTORY_SLOT_ITEM_END - INVENTORY_SLOT_ITEM_START)); - memset(inv_bags, 0, sizeof(int) * (INVENTORY_SLOT_BAG_END - INVENTORY_SLOT_BAG_START)*MAX_BAG_SIZE); - - for (int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i) - { - pItem2 = GetItemByPos(INVENTORY_SLOT_BAG_0, i); - - if (pItem2 && !pItem2->IsInTrade()) - { - inv_slot_items[i - INVENTORY_SLOT_ITEM_START] = pItem2->GetCount(); - } - } - - for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) - { - if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - { - for (uint32 j = 0; j < pBag->GetBagSize(); ++j) - { - pItem2 = GetItemByPos(i, j); - if (pItem2 && !pItem2->IsInTrade()) - { - inv_bags[i - INVENTORY_SLOT_BAG_START][j] = pItem2->GetCount(); - } - } - } - } - - // check free space for all items - for (int k = 0; k < count; ++k) - { - Item* pItem = pItems[k]; - - // no item - if (!pItem) continue; - - DEBUG_LOG("STORAGE: CanStoreItems %i. item = %u, count = %u", k + 1, pItem->GetEntry(), pItem->GetCount()); - ItemPrototype const* pProto = pItem->GetProto(); - - // strange item - if (!pProto) - return EQUIP_ERR_ITEM_NOT_FOUND; - - // item used - if (pItem->HasTemporaryLoot()) - return EQUIP_ERR_ALREADY_LOOTED; - - // item it 'bind' - if (pItem->IsBindedNotWith(this)) - return EQUIP_ERR_DONT_OWN_THAT_ITEM; - - Bag* pBag; - ItemPrototype const* pBagProto; - - // item is 'one item only' - InventoryResult res = CanTakeMoreSimilarItems(pItem); - if (res != EQUIP_ERR_OK) - return res; - - // search stack for merge to - if (pProto->Stackable != 1) - { - bool b_found = false; - - for (int t = INVENTORY_SLOT_ITEM_START; t < INVENTORY_SLOT_ITEM_END; ++t) - { - pItem2 = GetItemByPos(INVENTORY_SLOT_BAG_0, t); - if (pItem2 && pItem2->CanBeMergedPartlyWith(pProto) == EQUIP_ERR_OK && inv_slot_items[t - INVENTORY_SLOT_ITEM_START] + pItem->GetCount() <= pProto->GetMaxStackSize()) - { - inv_slot_items[t - INVENTORY_SLOT_ITEM_START] += pItem->GetCount(); - b_found = true; - break; - } - } - if (b_found) continue; - - for (int t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; ++t) - { - pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, t); - if (pBag) - { - for (uint32 j = 0; j < pBag->GetBagSize(); ++j) - { - pItem2 = GetItemByPos(t, j); - if (pItem2 && pItem2->CanBeMergedPartlyWith(pProto) == EQUIP_ERR_OK && inv_bags[t - INVENTORY_SLOT_BAG_START][j] + pItem->GetCount() <= pProto->GetMaxStackSize()) - { - inv_bags[t - INVENTORY_SLOT_BAG_START][j] += pItem->GetCount(); - b_found = true; - break; - } - } - } - } - if (b_found) continue; - } - - // special bag case - if (pProto->BagFamily) - { - bool b_found = false; - - for (int t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; ++t) - { - pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, t); - if (pBag) - { - pBagProto = pBag->GetProto(); - - // not plain container check - if (pBagProto && (pBagProto->Class != ITEM_CLASS_CONTAINER || pBagProto->SubClass != ITEM_SUBCLASS_CONTAINER) && - ItemCanGoIntoBag(pProto, pBagProto)) - { - for (uint32 j = 0; j < pBag->GetBagSize(); ++j) - { - if (inv_bags[t - INVENTORY_SLOT_BAG_START][j] == 0) - { - inv_bags[t - INVENTORY_SLOT_BAG_START][j] = 1; - b_found = true; - break; - } - } - } - } - } - if (b_found) continue; - } - - // search free slot - bool b_found = false; - for (int t = INVENTORY_SLOT_ITEM_START; t < INVENTORY_SLOT_ITEM_END; ++t) - { - if (inv_slot_items[t - INVENTORY_SLOT_ITEM_START] == 0) - { - inv_slot_items[t - INVENTORY_SLOT_ITEM_START] = 1; - b_found = true; - break; - } - } - if (b_found) continue; - - // search free slot in bags - for (int t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; ++t) - { - pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, t); - if (pBag) - { - pBagProto = pBag->GetProto(); - - // special bag already checked - if (pBagProto && (pBagProto->Class != ITEM_CLASS_CONTAINER || pBagProto->SubClass != ITEM_SUBCLASS_CONTAINER)) - continue; - - for (uint32 j = 0; j < pBag->GetBagSize(); ++j) - { - if (inv_bags[t - INVENTORY_SLOT_BAG_START][j] == 0) - { - inv_bags[t - INVENTORY_SLOT_BAG_START][j] = 1; - b_found = true; - break; - } - } - } - } - - // no free slot found? - if (!b_found) - return EQUIP_ERR_INVENTORY_FULL; - } - - return EQUIP_ERR_OK; -} - -////////////////////////////////////////////////////////////////////////// -InventoryResult Player::CanEquipNewItem(uint8 slot, uint16& dest, uint32 item, bool swap) const -{ - dest = 0; - Item* pItem = Item::CreateItem(item, 1, this); - if (pItem) - { - InventoryResult result = CanEquipItem(slot, dest, pItem, swap); - delete pItem; - return result; - } - - return EQUIP_ERR_ITEM_NOT_FOUND; -} - -InventoryResult Player::CanEquipItem(uint8 slot, uint16& dest, Item* pItem, bool swap, bool direct_action) const -{ - dest = 0; - if (pItem) - { - DEBUG_LOG("STORAGE: CanEquipItem slot = %u, item = %u, count = %u", slot, pItem->GetEntry(), pItem->GetCount()); - ItemPrototype const* pProto = pItem->GetProto(); - if (pProto) - { - // item used - if (pItem->HasTemporaryLoot()) - return EQUIP_ERR_ALREADY_LOOTED; - - if (pItem->IsBindedNotWith(this)) - return EQUIP_ERR_DONT_OWN_THAT_ITEM; - - // check count of items (skip for auto move for same player from bank) - InventoryResult res = CanTakeMoreSimilarItems(pItem); - if (res != EQUIP_ERR_OK) - return res; - - // check this only in game - if (direct_action) - { - // May be here should be more stronger checks; STUNNED checked - // ROOT, CONFUSED, DISTRACTED, FLEEING this needs to be checked. - if (hasUnitState(UNIT_STAT_STUNNED)) - return EQUIP_ERR_YOU_ARE_STUNNED; - - // do not allow equipping gear except weapons, offhands, projectiles, relics in - // - combat - // - in-progress arenas - if (!pProto->CanChangeEquipStateInCombat()) - { - if (isInCombat()) - return EQUIP_ERR_NOT_IN_COMBAT; - - if (BattleGround* bg = GetBattleGround()) - if (bg->isArena() && bg->GetStatus() == STATUS_IN_PROGRESS) - return EQUIP_ERR_NOT_DURING_ARENA_MATCH; - } - - // prevent equip item in process logout - if (GetSession()->isLogingOut()) - return EQUIP_ERR_YOU_ARE_STUNNED; - - if (isInCombat() && pProto->Class == ITEM_CLASS_WEAPON && m_weaponChangeTimer != 0) - return EQUIP_ERR_CANT_DO_RIGHT_NOW; // maybe exist better err - - if (IsNonMeleeSpellCasted(false)) - return EQUIP_ERR_CANT_DO_RIGHT_NOW; - } - - ScalingStatDistributionEntry const* ssd = pProto->ScalingStatDistribution ? sScalingStatDistributionStore.LookupEntry(pProto->ScalingStatDistribution) : 0; - // check allowed level (extend range to upper values if MaxLevel more or equal max player level, this let GM set high level with 1...max range items) - if (ssd && ssd->MaxLevel < DEFAULT_MAX_LEVEL && ssd->MaxLevel < getLevel()) - return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED; - - uint8 eslot = FindEquipSlot(pProto, slot, swap); - if (eslot == NULL_SLOT) - return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED; - - InventoryResult msg = CanUseItem(pItem , direct_action); - if (msg != EQUIP_ERR_OK) - return msg; - if (!swap && GetItemByPos(INVENTORY_SLOT_BAG_0, eslot)) - return EQUIP_ERR_NO_EQUIPMENT_SLOT_AVAILABLE; - - // if swap ignore item (equipped also) - if (InventoryResult res2 = CanEquipUniqueItem(pItem, swap ? eslot : uint8(NULL_SLOT))) - return res2; - - // check unique-equipped special item classes - if (pProto->Class == ITEM_CLASS_QUIVER) - { - for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) - { - if (Item* pBag = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - { - if (pBag != pItem) - { - if (ItemPrototype const* pBagProto = pBag->GetProto()) - { - if (pBagProto->Class == pProto->Class && (!swap || pBag->GetSlot() != eslot)) - return (pBagProto->SubClass == ITEM_SUBCLASS_AMMO_POUCH) - ? EQUIP_ERR_CAN_EQUIP_ONLY1_AMMOPOUCH - : EQUIP_ERR_CAN_EQUIP_ONLY1_QUIVER; - } - } - } - } - } - - uint32 type = pProto->InventoryType; - - if (eslot == EQUIPMENT_SLOT_OFFHAND) - { - if (type == INVTYPE_WEAPON || type == INVTYPE_WEAPONOFFHAND) - { - if (!CanDualWield()) - return EQUIP_ERR_CANT_DUAL_WIELD; - } - else if (type == INVTYPE_2HWEAPON) - { - if (!CanDualWield() || !CanTitanGrip()) - return EQUIP_ERR_CANT_DUAL_WIELD; - } - - if (IsTwoHandUsed()) - return EQUIP_ERR_CANT_EQUIP_WITH_TWOHANDED; - } - - // equip two-hand weapon case (with possible unequip 2 items) - if (type == INVTYPE_2HWEAPON) - { - if (eslot == EQUIPMENT_SLOT_OFFHAND) - { - if (!CanTitanGrip()) - return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED; - } - else if (eslot != EQUIPMENT_SLOT_MAINHAND) - return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED; - - if (!CanTitanGrip()) - { - // offhand item must can be stored in inventory for offhand item and it also must be unequipped - Item* offItem = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); - ItemPosCountVec off_dest; - if (offItem && (!direct_action || - CanUnequipItem(uint16(INVENTORY_SLOT_BAG_0) << 8 | EQUIPMENT_SLOT_OFFHAND, false) != EQUIP_ERR_OK || - CanStoreItem(NULL_BAG, NULL_SLOT, off_dest, offItem, false) != EQUIP_ERR_OK)) - return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_INVENTORY_FULL; - } - } - dest = ((INVENTORY_SLOT_BAG_0 << 8) | eslot); - return EQUIP_ERR_OK; - } - } - - return !swap ? EQUIP_ERR_ITEM_NOT_FOUND : EQUIP_ERR_ITEMS_CANT_BE_SWAPPED; -} - -InventoryResult Player::CanUnequipItem(uint16 pos, bool swap) const -{ - // Applied only to equipped items and bank bags - if (!IsEquipmentPos(pos) && !IsBagPos(pos)) - return EQUIP_ERR_OK; - - Item* pItem = GetItemByPos(pos); - - // Applied only to existing equipped item - if (!pItem) - return EQUIP_ERR_OK; - - DEBUG_LOG("STORAGE: CanUnequipItem slot = %u, item = %u, count = %u", pos, pItem->GetEntry(), pItem->GetCount()); - - ItemPrototype const* pProto = pItem->GetProto(); - if (!pProto) - return EQUIP_ERR_ITEM_NOT_FOUND; - - // item used - if (pItem->HasTemporaryLoot()) - return EQUIP_ERR_ALREADY_LOOTED; - - // do not allow unequipping gear except weapons, offhands, projectiles, relics in - // - combat - // - in-progress arenas - if (!pProto->CanChangeEquipStateInCombat()) - { - if (isInCombat()) - return EQUIP_ERR_NOT_IN_COMBAT; - - if (BattleGround* bg = GetBattleGround()) - if (bg->isArena() && bg->GetStatus() == STATUS_IN_PROGRESS) - return EQUIP_ERR_NOT_DURING_ARENA_MATCH; - } - - // prevent unequip item in process logout - if (GetSession()->isLogingOut()) - return EQUIP_ERR_YOU_ARE_STUNNED; - - if (!swap && pItem->IsBag() && !((Bag*)pItem)->IsEmpty()) - return EQUIP_ERR_CAN_ONLY_DO_WITH_EMPTY_BAGS; - - return EQUIP_ERR_OK; -} - -InventoryResult Player::CanBankItem(uint8 bag, uint8 slot, ItemPosCountVec& dest, Item* pItem, bool swap, bool not_loading) const -{ - if (!pItem) - return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_ITEM_NOT_FOUND; - - uint32 count = pItem->GetCount(); - - DEBUG_LOG("STORAGE: CanBankItem bag = %u, slot = %u, item = %u, count = %u", bag, slot, pItem->GetEntry(), pItem->GetCount()); - ItemPrototype const* pProto = pItem->GetProto(); - if (!pProto) - return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_ITEM_NOT_FOUND; - - // item used - if (pItem->HasTemporaryLoot()) - return EQUIP_ERR_ALREADY_LOOTED; - - if (pItem->IsBindedNotWith(this)) - return EQUIP_ERR_DONT_OWN_THAT_ITEM; - - // check count of items (skip for auto move for same player from bank) - InventoryResult res = CanTakeMoreSimilarItems(pItem); - if (res != EQUIP_ERR_OK) - return res; - - // in specific slot - if (bag != NULL_BAG && slot != NULL_SLOT) - { - if (slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END) - { - if (!pItem->IsBag()) - return EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT; - - if (slot - BANK_SLOT_BAG_START >= GetBankBagSlotCount()) - return EQUIP_ERR_MUST_PURCHASE_THAT_BAG_SLOT; - - res = CanUseItem(pItem, not_loading); - if (res != EQUIP_ERR_OK) - return res; - } - - res = _CanStoreItem_InSpecificSlot(bag, slot, dest, pProto, count, swap, pItem); - if (res != EQUIP_ERR_OK) - return res; - - if (count == 0) - return EQUIP_ERR_OK; - } - - // not specific slot or have space for partly store only in specific slot - - // in specific bag - if (bag != NULL_BAG) - { - if (pProto->InventoryType == INVTYPE_BAG) - { - Bag* pBag = (Bag*)pItem; - if (pBag && !pBag->IsEmpty()) - return EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG; - } - - // search stack in bag for merge to - if (pProto->Stackable != 1) - { - if (bag == INVENTORY_SLOT_BAG_0) - { - res = _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START, BANK_SLOT_ITEM_END, dest, pProto, count, true, pItem, bag, slot); - if (res != EQUIP_ERR_OK) - return res; - - if (count == 0) - return EQUIP_ERR_OK; - } - else - { - res = _CanStoreItem_InBag(bag, dest, pProto, count, true, false, pItem, NULL_BAG, slot); - if (res != EQUIP_ERR_OK) - res = _CanStoreItem_InBag(bag, dest, pProto, count, true, true, pItem, NULL_BAG, slot); - - if (res != EQUIP_ERR_OK) - return res; - - if (count == 0) - return EQUIP_ERR_OK; - } - } - - // search free slot in bag - if (bag == INVENTORY_SLOT_BAG_0) - { - res = _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START, BANK_SLOT_ITEM_END, dest, pProto, count, false, pItem, bag, slot); - if (res != EQUIP_ERR_OK) - return res; - - if (count == 0) - return EQUIP_ERR_OK; - } - else - { - res = _CanStoreItem_InBag(bag, dest, pProto, count, false, false, pItem, NULL_BAG, slot); - if (res != EQUIP_ERR_OK) - res = _CanStoreItem_InBag(bag, dest, pProto, count, false, true, pItem, NULL_BAG, slot); - - if (res != EQUIP_ERR_OK) - return res; - - if (count == 0) - return EQUIP_ERR_OK; - } - } - - // not specific bag or have space for partly store only in specific bag - - // search stack for merge to - if (pProto->Stackable != 1) - { - // in slots - res = _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START, BANK_SLOT_ITEM_END, dest, pProto, count, true, pItem, bag, slot); - if (res != EQUIP_ERR_OK) - return res; - - if (count == 0) - return EQUIP_ERR_OK; - - // in special bags - if (pProto->BagFamily) - { - for (int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i) - { - res = _CanStoreItem_InBag(i, dest, pProto, count, true, false, pItem, bag, slot); - if (res != EQUIP_ERR_OK) - continue; - - if (count == 0) - return EQUIP_ERR_OK; - } - } - - for (int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i) - { - res = _CanStoreItem_InBag(i, dest, pProto, count, true, true, pItem, bag, slot); - if (res != EQUIP_ERR_OK) - continue; - - if (count == 0) - return EQUIP_ERR_OK; - } - } - - // search free place in special bag - if (pProto->BagFamily) - { - for (int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i) - { - res = _CanStoreItem_InBag(i, dest, pProto, count, false, false, pItem, bag, slot); - if (res != EQUIP_ERR_OK) - continue; - - if (count == 0) - return EQUIP_ERR_OK; - } - } - - // search free space - res = _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START, BANK_SLOT_ITEM_END, dest, pProto, count, false, pItem, bag, slot); - if (res != EQUIP_ERR_OK) - return res; - - if (count == 0) - return EQUIP_ERR_OK; - - for (int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i) - { - res = _CanStoreItem_InBag(i, dest, pProto, count, false, true, pItem, bag, slot); - if (res != EQUIP_ERR_OK) - continue; - - if (count == 0) - return EQUIP_ERR_OK; - } - return EQUIP_ERR_BANK_FULL; -} - -InventoryResult Player::CanUseItem(Item* pItem, bool direct_action) const -{ - if (pItem) - { - DEBUG_LOG("STORAGE: CanUseItem item = %u", pItem->GetEntry()); - - if (!isAlive() && direct_action) - return EQUIP_ERR_YOU_ARE_DEAD; - - // if (isStunned()) - // return EQUIP_ERR_YOU_ARE_STUNNED; - - ItemPrototype const* pProto = pItem->GetProto(); - if (pProto) - { - if (pItem->IsBindedNotWith(this)) - return EQUIP_ERR_DONT_OWN_THAT_ITEM; - - InventoryResult msg = CanUseItem(pProto); - if (msg != EQUIP_ERR_OK) - return msg; - - if (uint32 item_use_skill = pItem->GetSkill()) - { - if (GetSkillValue(item_use_skill) == 0) - { - // armor items with scaling stats can downgrade armor skill reqs if related class can learn armor use at some level - if (pProto->Class != ITEM_CLASS_ARMOR) - return EQUIP_ERR_NO_REQUIRED_PROFICIENCY; - - ScalingStatDistributionEntry const* ssd = pProto->ScalingStatDistribution ? sScalingStatDistributionStore.LookupEntry(pProto->ScalingStatDistribution) : NULL; - if (!ssd) - return EQUIP_ERR_NO_REQUIRED_PROFICIENCY; - - bool allowScaleSkill = false; - for (uint32 i = 0; i < sSkillLineAbilityStore.GetNumRows(); ++i) - { - SkillLineAbilityEntry const* skillInfo = sSkillLineAbilityStore.LookupEntry(i); - if (!skillInfo) - continue; - - if (skillInfo->skillId != item_use_skill) - continue; - - // can't learn - if (skillInfo->classmask && (skillInfo->classmask & getClassMask()) == 0) - continue; - - if (skillInfo->racemask && (skillInfo->racemask & getRaceMask()) == 0) - continue; - - allowScaleSkill = true; - break; - } - - if (!allowScaleSkill) - return EQUIP_ERR_NO_REQUIRED_PROFICIENCY; - } - } - - if (pProto->RequiredReputationFaction && uint32(GetReputationRank(pProto->RequiredReputationFaction)) < pProto->RequiredReputationRank) - return EQUIP_ERR_CANT_EQUIP_REPUTATION; - - return EQUIP_ERR_OK; - } - } - return EQUIP_ERR_ITEM_NOT_FOUND; -} - -InventoryResult Player::CanUseItem(ItemPrototype const* pProto) const -{ - // Used by group, function NeedBeforeGreed, to know if a prototype can be used by a player - - if (pProto) - { - if ((pProto->Flags2 & ITEM_FLAG2_HORDE_ONLY) && GetTeam() != HORDE) - return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM; - - if ((pProto->Flags2 & ITEM_FLAG2_ALLIANCE_ONLY) && GetTeam() != ALLIANCE) - return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM; - - if ((pProto->AllowableClass & getClassMask()) == 0 || (pProto->AllowableRace & getRaceMask()) == 0) - return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM; - - if (pProto->RequiredSkill != 0) - { - if (GetSkillValue(pProto->RequiredSkill) == 0) - return EQUIP_ERR_NO_REQUIRED_PROFICIENCY; - else if (GetSkillValue(pProto->RequiredSkill) < pProto->RequiredSkillRank) - return EQUIP_ERR_CANT_EQUIP_SKILL; - } - - if (pProto->RequiredSpell != 0 && !HasSpell(pProto->RequiredSpell)) - return EQUIP_ERR_NO_REQUIRED_PROFICIENCY; - - if (getLevel() < pProto->RequiredLevel) - return EQUIP_ERR_CANT_EQUIP_LEVEL_I; - - return EQUIP_ERR_OK; - } - return EQUIP_ERR_ITEM_NOT_FOUND; -} - -InventoryResult Player::CanUseAmmo(uint32 item) const -{ - DEBUG_LOG("STORAGE: CanUseAmmo item = %u", item); - if (!isAlive()) - return EQUIP_ERR_YOU_ARE_DEAD; - // if( isStunned() ) - // return EQUIP_ERR_YOU_ARE_STUNNED; - ItemPrototype const* pProto = ObjectMgr::GetItemPrototype(item); - if (pProto) - { - if (pProto->InventoryType != INVTYPE_AMMO) - return EQUIP_ERR_ONLY_AMMO_CAN_GO_HERE; - - InventoryResult msg = CanUseItem(pProto); - if (msg != EQUIP_ERR_OK) - return msg; - - /*if ( GetReputationMgr().GetReputation() < pProto->RequiredReputation ) - return EQUIP_ERR_CANT_EQUIP_REPUTATION; - */ - - // Requires No Ammo - if (GetDummyAura(46699)) - return EQUIP_ERR_BAG_FULL6; - - return EQUIP_ERR_OK; - } - return EQUIP_ERR_ITEM_NOT_FOUND; -} - -void Player::SetAmmo(uint32 item) -{ - //if(!item) - // return; - - //// already set - //if( GetUInt32Value(PLAYER_AMMO_ID) == item ) - // return; - - //// check ammo - //if (item) - //{ - // InventoryResult msg = CanUseAmmo( item ); - // if (msg != EQUIP_ERR_OK) - // { - // SendEquipError(msg, NULL, NULL, item); - // return; - // } - //} - - //SetUInt32Value(PLAYER_AMMO_ID, item); - - //_ApplyAmmoBonuses(); -} - -void Player::RemoveAmmo() -{ - //SetUInt32Value(PLAYER_AMMO_ID, 0); - - //m_ammoDPS = 0.0f; - - //if (CanModifyStats()) - // UpdateDamagePhysical(RANGED_ATTACK); -} - -// Return stored item (if stored to stack, it can diff. from pItem). And pItem ca be deleted in this case. -Item* Player::StoreNewItem(ItemPosCountVec const& dest, uint32 item, bool update, int32 randomPropertyId) -{ - uint32 count = 0; - for (ItemPosCountVec::const_iterator itr = dest.begin(); itr != dest.end(); ++itr) - count += itr->count; - - Item* pItem = Item::CreateItem(item, count, this, randomPropertyId); - if (pItem) - { - ItemAddedQuestCheck(item, count); - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM, item, count); - pItem = StoreItem(dest, pItem, update); - } - return pItem; -} - -Item* Player::StoreItem(ItemPosCountVec const& dest, Item* pItem, bool update) -{ - if (!pItem) - return NULL; - - Item* lastItem = pItem; - uint32 entry = pItem->GetEntry(); - - for (ItemPosCountVec::const_iterator itr = dest.begin(); itr != dest.end();) - { - uint16 pos = itr->pos; - uint32 count = itr->count; - - ++itr; - - if (itr == dest.end()) - { - lastItem = _StoreItem(pos, pItem, count, false, update); - break; - } - - lastItem = _StoreItem(pos, pItem, count, true, update); - } - - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM, entry); - return lastItem; -} - -// Return stored item (if stored to stack, it can diff. from pItem). And pItem ca be deleted in this case. -Item* Player::_StoreItem(uint16 pos, Item* pItem, uint32 count, bool clone, bool update) -{ - if (!pItem) - return NULL; - - uint8 bag = pos >> 8; - uint8 slot = pos & 255; - - DEBUG_LOG("STORAGE: StoreItem bag = %u, slot = %u, item = %u, count = %u", bag, slot, pItem->GetEntry(), count); - - Item* pItem2 = GetItemByPos(bag, slot); - - if (!pItem2) - { - if (clone) - pItem = pItem->CloneItem(count, this); - else - pItem->SetCount(count); - - if (!pItem) - return NULL; - - if (pItem->GetProto()->Bonding == BIND_WHEN_PICKED_UP || - pItem->GetProto()->Bonding == BIND_QUEST_ITEM || - (pItem->GetProto()->Bonding == BIND_WHEN_EQUIPPED && IsBagPos(pos))) - pItem->SetBinding(true); - - if (bag == INVENTORY_SLOT_BAG_0) - { - m_items[slot] = pItem; - SetGuidValue(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2), pItem->GetObjectGuid()); - pItem->SetGuidValue(ITEM_FIELD_CONTAINED, GetObjectGuid()); - pItem->SetGuidValue(ITEM_FIELD_OWNER, GetObjectGuid()); - - pItem->SetSlot(slot); - pItem->SetContainer(NULL); - - if (IsInWorld() && update) - { - pItem->AddToWorld(); - pItem->SendCreateUpdateToPlayer(this); - } - - pItem->SetState(ITEM_CHANGED, this); - } - else if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, bag)) - { - pBag->StoreItem(slot, pItem, update); - if (IsInWorld() && update) - { - pItem->AddToWorld(); - pItem->SendCreateUpdateToPlayer(this); - } - pItem->SetState(ITEM_CHANGED, this); - pBag->SetState(ITEM_CHANGED, this); - } - - AddEnchantmentDurations(pItem); - AddItemDurations(pItem); - - // at place into not appropriate slot (bank, for example) remove aura - ApplyItemOnStoreSpell(pItem, IsEquipmentPos(pItem->GetBagSlot(), pItem->GetSlot()) || IsInventoryPos(pItem->GetBagSlot(), pItem->GetSlot())); - - return pItem; - } - else - { - if (pItem2->GetProto()->Bonding == BIND_WHEN_PICKED_UP || - pItem2->GetProto()->Bonding == BIND_QUEST_ITEM || - (pItem2->GetProto()->Bonding == BIND_WHEN_EQUIPPED && IsBagPos(pos))) - pItem2->SetBinding(true); - - pItem2->SetCount(pItem2->GetCount() + count); - if (IsInWorld() && update) - pItem2->SendCreateUpdateToPlayer(this); - - if (!clone) - { - // delete item (it not in any slot currently) - if (IsInWorld() && update) - { - pItem->RemoveFromWorld(); - pItem->DestroyForPlayer(this); - } - - RemoveEnchantmentDurations(pItem); - RemoveItemDurations(pItem); - - pItem->SetOwnerGuid(GetObjectGuid()); // prevent error at next SetState in case trade/mail/buy from vendor - pItem->SetState(ITEM_REMOVED, this); - } - - // AddItemDurations(pItem2); - pItem2 already have duration listed for player - AddEnchantmentDurations(pItem2); - - pItem2->SetState(ITEM_CHANGED, this); - - return pItem2; - } -} - -Item* Player::EquipNewItem(uint16 pos, uint32 item, bool update) -{ - if (Item* pItem = Item::CreateItem(item, 1, this)) - { - ItemAddedQuestCheck(item, 1); - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM, item, 1); - return EquipItem(pos, pItem, update); - } - - return NULL; -} - -Item* Player::EquipItem(uint16 pos, Item* pItem, bool update) -{ - AddEnchantmentDurations(pItem); - AddItemDurations(pItem); - - uint8 bag = pos >> 8; - uint8 slot = pos & 255; - - Item* pItem2 = GetItemByPos(bag, slot); - if (!pItem2) - { - VisualizeItem(slot, pItem); - - if (isAlive()) - { - ItemPrototype const* pProto = pItem->GetProto(); - - // item set bonuses applied only at equip and removed at unequip, and still active for broken items - if (pProto && pProto->ItemSet) - AddItemsSetItem(this, pItem); - - _ApplyItemMods(pItem, slot, true); - - ApplyItemOnStoreSpell(pItem, true); - - // Weapons and also Totem/Relic/Sigil/etc - if (pProto && isInCombat() && (pProto->Class == ITEM_CLASS_WEAPON || pProto->InventoryType == INVTYPE_RELIC) && m_weaponChangeTimer == 0) - { - uint32 cooldownSpell = SPELL_ID_WEAPON_SWITCH_COOLDOWN_1_5s; - - if (getClass() == CLASS_ROGUE) - cooldownSpell = SPELL_ID_WEAPON_SWITCH_COOLDOWN_1_0s; - - SpellEntry const* spellProto = sSpellStore.LookupEntry(cooldownSpell); - - if (!spellProto) - sLog.outError("Weapon switch cooldown spell %u couldn't be found in Spell.dbc", cooldownSpell); - else - { - m_weaponChangeTimer = spellProto->GetStartRecoveryTime(); - - WorldPacket data(SMSG_SPELL_COOLDOWN, 8 + 1 + 4); - data << GetObjectGuid(); - data << uint8(1); - data << uint32(cooldownSpell); - data << uint32(0); - GetSession()->SendPacket(&data); - } - } - } - - if (IsInWorld() && update) - { - pItem->AddToWorld(); - pItem->SendCreateUpdateToPlayer(this); - } - - ApplyEquipCooldown(pItem); - - if (slot == EQUIPMENT_SLOT_MAINHAND) - { - UpdateExpertise(BASE_ATTACK); - UpdateArmorPenetration(); - } - else if (slot == EQUIPMENT_SLOT_OFFHAND) - { - UpdateExpertise(OFF_ATTACK); - UpdateArmorPenetration(); - } - - UpdateArmorSpecializations(); - } - else - { - pItem2->SetCount(pItem2->GetCount() + pItem->GetCount()); - if (IsInWorld() && update) - pItem2->SendCreateUpdateToPlayer(this); - - // delete item (it not in any slot currently) - // pItem->DeleteFromDB(); - if (IsInWorld() && update) - { - pItem->RemoveFromWorld(); - pItem->DestroyForPlayer(this); - } - - RemoveEnchantmentDurations(pItem); - RemoveItemDurations(pItem); - - pItem->SetOwnerGuid(GetObjectGuid()); // prevent error at next SetState in case trade/mail/buy from vendor - pItem->SetState(ITEM_REMOVED, this); - pItem2->SetState(ITEM_CHANGED, this); - - ApplyEquipCooldown(pItem2); - - return pItem2; - } - // Apply Titan's Grip damage penalty if necessary - if ((slot == EQUIPMENT_SLOT_MAINHAND || slot == EQUIPMENT_SLOT_OFFHAND) && CanTitanGrip() && HasTwoHandWeaponInOneHand() && !HasAura(49152)) - CastSpell(this, 49152, true); - - // only for full equip instead adding to stack - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM, pItem->GetEntry()); - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM, slot + 1); - - return pItem; -} - -void Player::QuickEquipItem(uint16 pos, Item* pItem) -{ - if (pItem) - { - AddEnchantmentDurations(pItem); - AddItemDurations(pItem); - ApplyItemOnStoreSpell(pItem, true); - - uint8 slot = pos & 255; - VisualizeItem(slot, pItem); - - if (IsInWorld()) - { - pItem->AddToWorld(); - pItem->SendCreateUpdateToPlayer(this); - } - // Apply Titan's Grip damage penalty if necessary - if ((slot == EQUIPMENT_SLOT_MAINHAND || slot == EQUIPMENT_SLOT_OFFHAND) && CanTitanGrip() && HasTwoHandWeaponInOneHand() && !HasAura(49152)) - CastSpell(this, 49152, true); - - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM, pItem->GetEntry()); - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM, slot + 1); - - UpdateArmorSpecializations(); - } -} - -void Player::SetVisibleItemSlot(uint8 slot, Item* pItem) -{ - if (pItem) - { - SetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID + (slot * 2), pItem->GetEntry()); - SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (slot * 2), 0, pItem->GetEnchantmentId(PERM_ENCHANTMENT_SLOT)); - SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (slot * 2), 1, pItem->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT)); - } - else - { - SetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID + (slot * 2), 0); - SetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (slot * 2), 0); - } -} - -void Player::VisualizeItem(uint8 slot, Item* pItem) -{ - if (!pItem) - 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->GetProto()->Bonding == BIND_WHEN_EQUIPPED || pItem->GetProto()->Bonding == BIND_WHEN_PICKED_UP || pItem->GetProto()->Bonding == BIND_QUEST_ITEM) - pItem->SetBinding(true); - - DEBUG_LOG("STORAGE: EquipItem slot = %u, item = %u", slot, pItem->GetEntry()); - - m_items[slot] = pItem; - SetGuidValue(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2), pItem->GetObjectGuid()); - pItem->SetGuidValue(ITEM_FIELD_CONTAINED, GetObjectGuid()); - pItem->SetGuidValue(ITEM_FIELD_OWNER, GetObjectGuid()); - pItem->SetSlot(slot); - pItem->SetContainer(NULL); - - if (slot < EQUIPMENT_SLOT_END) - SetVisibleItemSlot(slot, pItem); - - pItem->SetState(ITEM_CHANGED, this); -} - -void Player::RemoveItem(uint8 bag, uint8 slot, bool update) -{ - // note: removeitem does not actually change the item - // it only takes the item out of storage temporarily - // note2: if removeitem is to be used for delinking - // the item must be removed from the player's updatequeue - - if (Item* pItem = GetItemByPos(bag, slot)) - { - DEBUG_LOG("STORAGE: RemoveItem bag = %u, slot = %u, item = %u", bag, slot, pItem->GetEntry()); - - RemoveEnchantmentDurations(pItem); - RemoveItemDurations(pItem); - - if (bag == INVENTORY_SLOT_BAG_0) - { - if (slot < INVENTORY_SLOT_BAG_END) - { - ItemPrototype const* pProto = pItem->GetProto(); - // item set bonuses applied only at equip and removed at unequip, and still active for broken items - - if (pProto && pProto->ItemSet) - RemoveItemsSetItem(this, pProto); - - _ApplyItemMods(pItem, slot, false); - - // remove item dependent auras and casts (only weapon and armor slots) - if (slot < EQUIPMENT_SLOT_END) - { - RemoveItemDependentAurasAndCasts(pItem); - - // remove held enchantments, update expertise - if (slot == EQUIPMENT_SLOT_MAINHAND) - { - if (pItem->GetItemSuffixFactor()) - { - pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_3); - pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_4); - } - else - { - pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_0); - pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_1); - } - - UpdateExpertise(BASE_ATTACK); - UpdateArmorPenetration(); - } - else if (slot == EQUIPMENT_SLOT_OFFHAND) - { - UpdateExpertise(OFF_ATTACK); - UpdateArmorPenetration(); - } - } - } - - m_items[slot] = NULL; - SetGuidValue(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2), ObjectGuid()); - - if (slot < EQUIPMENT_SLOT_END) - { - SetVisibleItemSlot(slot, NULL); - // Remove Titan's Grip damage penalty if necessary - if ((slot == EQUIPMENT_SLOT_MAINHAND || slot == EQUIPMENT_SLOT_OFFHAND) && CanTitanGrip() && !HasTwoHandWeaponInOneHand()) - RemoveAurasDueToSpell(49152); - } - - UpdateArmorSpecializations(); - } - else - { - Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, bag); - if (pBag) - pBag->RemoveItem(slot, update); - } - pItem->SetGuidValue(ITEM_FIELD_CONTAINED, ObjectGuid()); - // pItem->SetGuidValue(ITEM_FIELD_OWNER, ObjectGuid()); not clear owner at remove (it will be set at store). This used in mail and auction code - pItem->SetSlot(NULL_SLOT); - - // ApplyItemOnStoreSpell, for avoid re-apply will remove at _adding_ to not appropriate slot - - if (IsInWorld() && update) - pItem->SendCreateUpdateToPlayer(this); - } -} - -// Common operation need to remove item from inventory without delete in trade, auction, guild bank, mail.... -void Player::MoveItemFromInventory(uint8 bag, uint8 slot, bool update) -{ - if (Item* it = GetItemByPos(bag, slot)) - { - ItemRemovedQuestCheck(it->GetEntry(), it->GetCount()); - RemoveItem(bag, slot, update); - - // item atStore spell not removed in RemoveItem (for avoid reappaly in slots changes), so do it directly - if (IsEquipmentPos(bag, slot) || IsInventoryPos(bag, slot)) - ApplyItemOnStoreSpell(it, false); - - it->RemoveFromUpdateQueueOf(this); - if (it->IsInWorld()) - { - it->RemoveFromWorld(); - it->DestroyForPlayer(this); - } - } -} - -// Common operation need to add item from inventory without delete in trade, guild bank, mail.... -void Player::MoveItemToInventory(ItemPosCountVec const& dest, Item* pItem, bool update, bool in_characterInventoryDB) -{ - // update quest counters - ItemAddedQuestCheck(pItem->GetEntry(), pItem->GetCount()); - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM, pItem->GetEntry(), pItem->GetCount()); - - // store item - Item* pLastItem = StoreItem(dest, pItem, update); - - // only set if not merged to existing stack (pItem can be deleted already but we can compare pointers any way) - if (pLastItem == pItem) - { - // update owner for last item (this can be original item with wrong owner - if (pLastItem->GetOwnerGuid() != GetObjectGuid()) - pLastItem->SetOwnerGuid(GetObjectGuid()); - - // if this original item then it need create record in inventory - // in case trade we already have item in other player inventory - pLastItem->SetState(in_characterInventoryDB ? ITEM_CHANGED : ITEM_NEW, this); - } -} - -void Player::DestroyItem(uint8 bag, uint8 slot, bool update) -{ - Item* pItem = GetItemByPos(bag, slot); - if (pItem) - { - DEBUG_LOG("STORAGE: DestroyItem bag = %u, slot = %u, item = %u", bag, slot, pItem->GetEntry()); - - // start from destroy contained items (only equipped bag can have its) - if (pItem->IsBag() && pItem->IsEquipped()) // this also prevent infinity loop if empty bag stored in bag==slot - { - for (int i = 0; i < MAX_BAG_SIZE; ++i) - DestroyItem(slot, i, update); - } - - if (pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_DYNFLAG_WRAPPED)) - { - static SqlStatementID delGifts ; - - SqlStatement stmt = CharacterDatabase.CreateStatement(delGifts, "DELETE FROM character_gifts WHERE item_guid = ?"); - stmt.PExecute(pItem->GetGUIDLow()); - } - - RemoveEnchantmentDurations(pItem); - RemoveItemDurations(pItem); - - if (IsEquipmentPos(bag, slot) || IsInventoryPos(bag, slot)) - ApplyItemOnStoreSpell(pItem, false); - - ItemRemovedQuestCheck(pItem->GetEntry(), pItem->GetCount()); - - if (bag == INVENTORY_SLOT_BAG_0) - { - SetGuidValue(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2), ObjectGuid()); - - // equipment and equipped bags can have applied bonuses - if (slot < INVENTORY_SLOT_BAG_END) - { - ItemPrototype const* pProto = pItem->GetProto(); - - // item set bonuses applied only at equip and removed at unequip, and still active for broken items - if (pProto && pProto->ItemSet) - RemoveItemsSetItem(this, pProto); - - _ApplyItemMods(pItem, slot, false); - } - - if (slot < EQUIPMENT_SLOT_END) - { - // remove item dependent auras and casts (only weapon and armor slots) - RemoveItemDependentAurasAndCasts(pItem); - - // update expertise - if (slot == EQUIPMENT_SLOT_MAINHAND) - { - UpdateExpertise(BASE_ATTACK); - UpdateArmorPenetration(); - } - else if (slot == EQUIPMENT_SLOT_OFFHAND) - { - UpdateExpertise(OFF_ATTACK); - UpdateArmorPenetration(); - } - - // equipment visual show - SetVisibleItemSlot(slot, NULL); - } - - m_items[slot] = NULL; - } - else if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, bag)) - pBag->RemoveItem(slot, update); - - if (IsInWorld() && update) - { - pItem->RemoveFromWorld(); - pItem->DestroyForPlayer(this); - } - - // pItem->SetOwnerGUID(0); - pItem->SetGuidValue(ITEM_FIELD_CONTAINED, ObjectGuid()); - pItem->SetSlot(NULL_SLOT); - pItem->SetState(ITEM_REMOVED, this); - } -} - -void Player::DestroyItemCount(uint32 item, uint32 count, bool update, bool unequip_check, bool inBankAlso) -{ - DEBUG_LOG("STORAGE: DestroyItemCount item = %u, count = %u", item, count); - uint32 remcount = 0; - - // in inventory - for (int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i) - { - if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - { - if (pItem->GetEntry() == item && !pItem->IsInTrade()) - { - if (pItem->GetCount() + remcount <= count) - { - // all items in inventory can unequipped - remcount += pItem->GetCount(); - DestroyItem(INVENTORY_SLOT_BAG_0, i, update); - - if (remcount >= count) - return; - } - else - { - ItemRemovedQuestCheck(pItem->GetEntry(), count - remcount); - pItem->SetCount(pItem->GetCount() - count + remcount); - if (IsInWorld() && update) - pItem->SendCreateUpdateToPlayer(this); - pItem->SetState(ITEM_CHANGED, this); - return; - } - } - } - } - - // in inventory bags - for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) - { - if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - { - for (uint32 j = 0; j < pBag->GetBagSize(); ++j) - { - if (Item* pItem = pBag->GetItemByPos(j)) - { - if (pItem->GetEntry() == item && !pItem->IsInTrade()) - { - // all items in bags can be unequipped - if (pItem->GetCount() + remcount <= count) - { - remcount += pItem->GetCount(); - DestroyItem(i, j, update); - - if (remcount >= count) - return; - } - else - { - ItemRemovedQuestCheck(pItem->GetEntry(), count - remcount); - pItem->SetCount(pItem->GetCount() - count + remcount); - if (IsInWorld() && update) - pItem->SendCreateUpdateToPlayer(this); - pItem->SetState(ITEM_CHANGED, this); - return; - } - } - } - } - } - } - - // in equipment and bag list - for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; ++i) - { - if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - { - if (pItem && pItem->GetEntry() == item && !pItem->IsInTrade()) - { - if (pItem->GetCount() + remcount <= count) - { - if (!unequip_check || CanUnequipItem(INVENTORY_SLOT_BAG_0 << 8 | i, false) == EQUIP_ERR_OK) - { - remcount += pItem->GetCount(); - DestroyItem(INVENTORY_SLOT_BAG_0, i, update); - - if (remcount >= count) - return; - } - } - else - { - ItemRemovedQuestCheck(pItem->GetEntry(), count - remcount); - pItem->SetCount(pItem->GetCount() - count + remcount); - if (IsInWorld() && update) - pItem->SendCreateUpdateToPlayer(this); - pItem->SetState(ITEM_CHANGED, this); - return; - } - } - } - } - - if (inBankAlso) // Remove items from bank as well - { - for (int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; ++i) - { - Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (pItem && pItem->GetEntry() == item && !pItem->IsInTrade()) - { - if (pItem->GetCount() + remcount <= count) - { - remcount += pItem->GetCount(); - DestroyItem(INVENTORY_SLOT_BAG_0, i, update); - - if (remcount >= count) - return; - } - else - { - ItemRemovedQuestCheck(pItem->GetEntry(), count - remcount); - pItem->SetCount(pItem->GetCount() - count + remcount); - if (IsInWorld() && update) - pItem->SendCreateUpdateToPlayer(this); - pItem->SetState(ITEM_CHANGED, this); - return; - } - } - } - - for (int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i) - { - if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - { - for (uint32 j = 0; j < pBag->GetBagSize(); ++j) - { - Item* pItem = pBag->GetItemByPos(j); - if (pItem && pItem->GetEntry() == item && !pItem->IsInTrade()) - { - if (pItem->GetCount() + remcount <= count) - { - remcount += pItem->GetCount(); - DestroyItem(i, j, update); - - if (remcount >= count) - return; - } - else - { - ItemRemovedQuestCheck(pItem->GetEntry(), count - remcount); - pItem->SetCount(pItem->GetCount() - count + remcount); - if (IsInWorld() && update) - pItem->SendCreateUpdateToPlayer(this); - pItem->SetState(ITEM_CHANGED, this); - return; - } - } - } - } - } - } -} - -void Player::DestroyZoneLimitedItem(bool update, uint32 new_zone) -{ - DEBUG_LOG("STORAGE: DestroyZoneLimitedItem in map %u and area %u", GetMapId(), new_zone); - - // in inventory - for (int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i) - if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(), new_zone)) - DestroyItem(INVENTORY_SLOT_BAG_0, i, update); - - // in inventory bags - for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) - if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - for (uint32 j = 0; j < pBag->GetBagSize(); ++j) - if (Item* pItem = pBag->GetItemByPos(j)) - if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(), new_zone)) - DestroyItem(i, j, update); - - // in equipment and bag list - for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; ++i) - if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(), new_zone)) - DestroyItem(INVENTORY_SLOT_BAG_0, i, update); -} - -void Player::DestroyConjuredItems(bool update) -{ - // used when entering arena - // destroys all conjured items - DEBUG_LOG("STORAGE: DestroyConjuredItems"); - - // in inventory - for (int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i) - if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - if (pItem->IsConjuredConsumable()) - DestroyItem(INVENTORY_SLOT_BAG_0, i, update); - - // in inventory bags - for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) - if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - for (uint32 j = 0; j < pBag->GetBagSize(); ++j) - if (Item* pItem = pBag->GetItemByPos(j)) - if (pItem->IsConjuredConsumable()) - DestroyItem(i, j, update); - - // in equipment and bag list - for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; ++i) - if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - if (pItem->IsConjuredConsumable()) - DestroyItem(INVENTORY_SLOT_BAG_0, i, update); -} - -void Player::DestroyItemCount(Item* pItem, uint32& count, bool update) -{ - if (!pItem) - return; - - DEBUG_LOG("STORAGE: DestroyItemCount item (GUID: %u, Entry: %u) count = %u", pItem->GetGUIDLow(), pItem->GetEntry(), count); - - if (pItem->GetCount() <= count) - { - count -= pItem->GetCount(); - - DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), update); - } - else - { - ItemRemovedQuestCheck(pItem->GetEntry(), count); - pItem->SetCount(pItem->GetCount() - count); - count = 0; - if (IsInWorld() && update) - pItem->SendCreateUpdateToPlayer(this); - pItem->SetState(ITEM_CHANGED, this); - } -} - -void Player::SplitItem(uint16 src, uint16 dst, uint32 count) -{ - uint8 srcbag = src >> 8; - uint8 srcslot = src & 255; - - uint8 dstbag = dst >> 8; - uint8 dstslot = dst & 255; - - Item* pSrcItem = GetItemByPos(srcbag, srcslot); - if (!pSrcItem) - { - SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pSrcItem, NULL); - return; - } - - if (pSrcItem->HasGeneratedLoot()) // prevent split looting item (stackable items can has only temporary loot and this meaning that loot window open) - { - // best error message found for attempting to split while looting - SendEquipError(EQUIP_ERR_COULDNT_SPLIT_ITEMS, pSrcItem, NULL); - return; - } - - // not let split all items (can be only at cheating) - if (pSrcItem->GetCount() == count) - { - SendEquipError(EQUIP_ERR_COULDNT_SPLIT_ITEMS, pSrcItem, NULL); - return; - } - - // not let split more existing items (can be only at cheating) - if (pSrcItem->GetCount() < count) - { - SendEquipError(EQUIP_ERR_TRIED_TO_SPLIT_MORE_THAN_COUNT, pSrcItem, NULL); - return; - } - - DEBUG_LOG("STORAGE: SplitItem bag = %u, slot = %u, item = %u, count = %u", dstbag, dstslot, pSrcItem->GetEntry(), count); - Item* pNewItem = pSrcItem->CloneItem(count, this); - if (!pNewItem) - { - SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pSrcItem, NULL); - return; - } - - if (IsInventoryPos(dst)) - { - // change item amount before check (for unique max count check) - pSrcItem->SetCount(pSrcItem->GetCount() - count); - - ItemPosCountVec dest; - InventoryResult msg = CanStoreItem(dstbag, dstslot, dest, pNewItem, false); - if (msg != EQUIP_ERR_OK) - { - delete pNewItem; - pSrcItem->SetCount(pSrcItem->GetCount() + count); - SendEquipError(msg, pSrcItem, NULL); - return; - } - - if (IsInWorld()) - pSrcItem->SendCreateUpdateToPlayer(this); - pSrcItem->SetState(ITEM_CHANGED, this); - StoreItem(dest, pNewItem, true); - } - else if (IsBankPos(dst)) - { - // change item amount before check (for unique max count check) - pSrcItem->SetCount(pSrcItem->GetCount() - count); - - ItemPosCountVec dest; - InventoryResult msg = CanBankItem(dstbag, dstslot, dest, pNewItem, false); - if (msg != EQUIP_ERR_OK) - { - delete pNewItem; - pSrcItem->SetCount(pSrcItem->GetCount() + count); - SendEquipError(msg, pSrcItem, NULL); - return; - } - - if (IsInWorld()) - pSrcItem->SendCreateUpdateToPlayer(this); - pSrcItem->SetState(ITEM_CHANGED, this); - BankItem(dest, pNewItem, true); - } - else if (IsEquipmentPos(dst)) - { - // change item amount before check (for unique max count check), provide space for splitted items - pSrcItem->SetCount(pSrcItem->GetCount() - count); - - uint16 dest; - InventoryResult msg = CanEquipItem(dstslot, dest, pNewItem, false); - if (msg != EQUIP_ERR_OK) - { - delete pNewItem; - pSrcItem->SetCount(pSrcItem->GetCount() + count); - SendEquipError(msg, pSrcItem, NULL); - return; - } - - if (IsInWorld()) - pSrcItem->SendCreateUpdateToPlayer(this); - pSrcItem->SetState(ITEM_CHANGED, this); - EquipItem(dest, pNewItem, true); - AutoUnequipOffhandIfNeed(); - } -} - -void Player::SwapItem(uint16 src, uint16 dst) -{ - uint8 srcbag = src >> 8; - uint8 srcslot = src & 255; - - uint8 dstbag = dst >> 8; - uint8 dstslot = dst & 255; - - Item* pSrcItem = GetItemByPos(srcbag, srcslot); - Item* pDstItem = GetItemByPos(dstbag, dstslot); - - if (!pSrcItem) - return; - - DEBUG_LOG("STORAGE: SwapItem bag = %u, slot = %u, item = %u", dstbag, dstslot, pSrcItem->GetEntry()); - - if (!isAlive()) - { - SendEquipError(EQUIP_ERR_YOU_ARE_DEAD, pSrcItem, pDstItem); - return; - } - - // SRC checks - - // check unequip potability for equipped items and bank bags - if (IsEquipmentPos(src) || IsBagPos(src)) - { - // bags can be swapped with empty bag slots, or with empty bag (items move possibility checked later) - InventoryResult msg = CanUnequipItem(src, !IsBagPos(src) || IsBagPos(dst) || (pDstItem && pDstItem->IsBag() && ((Bag*)pDstItem)->IsEmpty())); - if (msg != EQUIP_ERR_OK) - { - SendEquipError(msg, pSrcItem, pDstItem); - return; - } - } - - // prevent put equipped/bank bag in self - if (IsBagPos(src) && srcslot == dstbag) - { - SendEquipError(EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG, pSrcItem, pDstItem); - return; - } - - // prevent put equipped/bank bag in self - if (IsBagPos(dst) && dstslot == srcbag) - { - SendEquipError(EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG, pDstItem, pSrcItem); - return; - } - - // DST checks - - if (pDstItem) - { - // check unequip potability for equipped items and bank bags - if (IsEquipmentPos(dst) || IsBagPos(dst)) - { - // bags can be swapped with empty bag slots, or with empty bag (items move possibility checked later) - InventoryResult msg = CanUnequipItem(dst, !IsBagPos(dst) || IsBagPos(src) || (pSrcItem->IsBag() && ((Bag*)pSrcItem)->IsEmpty())); - if (msg != EQUIP_ERR_OK) - { - SendEquipError(msg, pSrcItem, pDstItem); - return; - } - } - } - - // NOW this is or item move (swap with empty), or swap with another item (including bags in bag possitions) - // or swap empty bag with another empty or not empty bag (with items exchange) - - // Move case - if (!pDstItem) - { - if (IsInventoryPos(dst)) - { - ItemPosCountVec dest; - InventoryResult msg = CanStoreItem(dstbag, dstslot, dest, pSrcItem, false); - if (msg != EQUIP_ERR_OK) - { - SendEquipError(msg, pSrcItem, NULL); - return; - } - - RemoveItem(srcbag, srcslot, true); - StoreItem(dest, pSrcItem, true); - } - else if (IsBankPos(dst)) - { - ItemPosCountVec dest; - InventoryResult msg = CanBankItem(dstbag, dstslot, dest, pSrcItem, false); - if (msg != EQUIP_ERR_OK) - { - SendEquipError(msg, pSrcItem, NULL); - return; - } - - RemoveItem(srcbag, srcslot, true); - BankItem(dest, pSrcItem, true); - } - else if (IsEquipmentPos(dst)) - { - uint16 dest; - InventoryResult msg = CanEquipItem(dstslot, dest, pSrcItem, false); - if (msg != EQUIP_ERR_OK) - { - SendEquipError(msg, pSrcItem, NULL); - return; - } - - RemoveItem(srcbag, srcslot, true); - EquipItem(dest, pSrcItem, true); - AutoUnequipOffhandIfNeed(); - } - - return; - } - - // attempt merge to / fill target item - if (!pSrcItem->IsBag() && !pDstItem->IsBag()) - { - InventoryResult msg; - ItemPosCountVec sDest; - uint16 eDest; - if (IsInventoryPos(dst)) - msg = CanStoreItem(dstbag, dstslot, sDest, pSrcItem, false); - else if (IsBankPos(dst)) - msg = CanBankItem(dstbag, dstslot, sDest, pSrcItem, false); - else if (IsEquipmentPos(dst)) - msg = CanEquipItem(dstslot, eDest, pSrcItem, false); - else - return; - - // can be merge/fill - if (msg == EQUIP_ERR_OK) - { - if (pSrcItem->GetCount() + pDstItem->GetCount() <= pSrcItem->GetProto()->GetMaxStackSize()) - { - RemoveItem(srcbag, srcslot, true); - - if (IsInventoryPos(dst)) - StoreItem(sDest, pSrcItem, true); - else if (IsBankPos(dst)) - BankItem(sDest, pSrcItem, true); - else if (IsEquipmentPos(dst)) - { - EquipItem(eDest, pSrcItem, true); - AutoUnequipOffhandIfNeed(); - } - } - else - { - pSrcItem->SetCount(pSrcItem->GetCount() + pDstItem->GetCount() - pSrcItem->GetProto()->GetMaxStackSize()); - pDstItem->SetCount(pSrcItem->GetProto()->GetMaxStackSize()); - pSrcItem->SetState(ITEM_CHANGED, this); - pDstItem->SetState(ITEM_CHANGED, this); - if (IsInWorld()) - { - pSrcItem->SendCreateUpdateToPlayer(this); - pDstItem->SendCreateUpdateToPlayer(this); - } - } - return; - } - } - - // impossible merge/fill, do real swap - InventoryResult msg; - - // check src->dest move possibility - ItemPosCountVec sDest; - uint16 eDest = 0; - if (IsInventoryPos(dst)) - msg = CanStoreItem(dstbag, dstslot, sDest, pSrcItem, true); - else if (IsBankPos(dst)) - msg = CanBankItem(dstbag, dstslot, sDest, pSrcItem, true); - else if (IsEquipmentPos(dst)) - { - msg = CanEquipItem(dstslot, eDest, pSrcItem, true); - if (msg == EQUIP_ERR_OK) - msg = CanUnequipItem(eDest, true); - } - - if (msg != EQUIP_ERR_OK) - { - SendEquipError(msg, pSrcItem, pDstItem); - return; - } - - // check dest->src move possibility - ItemPosCountVec sDest2; - uint16 eDest2 = 0; - if (IsInventoryPos(src)) - msg = CanStoreItem(srcbag, srcslot, sDest2, pDstItem, true); - else if (IsBankPos(src)) - msg = CanBankItem(srcbag, srcslot, sDest2, pDstItem, true); - else if (IsEquipmentPos(src)) - { - msg = CanEquipItem(srcslot, eDest2, pDstItem, true); - if (msg == EQUIP_ERR_OK) - msg = CanUnequipItem(eDest2, true); - } - - if (msg != EQUIP_ERR_OK) - { - SendEquipError(msg, pDstItem, pSrcItem); - return; - } - - // Check bag swap with item exchange (one from empty in not bag possition (equipped (not possible in fact) or store) - if (pSrcItem->IsBag() && pDstItem->IsBag()) - { - Bag* emptyBag = NULL; - Bag* fullBag = NULL; - if (((Bag*)pSrcItem)->IsEmpty() && !IsBagPos(src)) - { - emptyBag = (Bag*)pSrcItem; - fullBag = (Bag*)pDstItem; - } - else if (((Bag*)pDstItem)->IsEmpty() && !IsBagPos(dst)) - { - emptyBag = (Bag*)pDstItem; - fullBag = (Bag*)pSrcItem; - } - - // bag swap (with items exchange) case - if (emptyBag && fullBag) - { - ItemPrototype const* emotyProto = emptyBag->GetProto(); - - uint32 count = 0; - - for (uint32 i = 0; i < fullBag->GetBagSize(); ++i) - { - Item* bagItem = fullBag->GetItemByPos(i); - if (!bagItem) - continue; - - ItemPrototype const* bagItemProto = bagItem->GetProto(); - if (!bagItemProto || !ItemCanGoIntoBag(bagItemProto, emotyProto)) - { - // one from items not go to empty target bag - SendEquipError(EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG, pSrcItem, pDstItem); - return; - } - - ++count; - } - - if (count > emptyBag->GetBagSize()) - { - // too small targeted bag - SendEquipError(EQUIP_ERR_ITEMS_CANT_BE_SWAPPED, pSrcItem, pDstItem); - return; - } - - // Items swap - count = 0; // will pos in new bag - for (uint32 i = 0; i < fullBag->GetBagSize(); ++i) - { - Item* bagItem = fullBag->GetItemByPos(i); - if (!bagItem) - continue; - - fullBag->RemoveItem(i, true); - emptyBag->StoreItem(count, bagItem, true); - bagItem->SetState(ITEM_CHANGED, this); - - ++count; - } - } - } - - // now do moves, remove... - RemoveItem(dstbag, dstslot, false); - RemoveItem(srcbag, srcslot, false); - - // add to dest - if (IsInventoryPos(dst)) - StoreItem(sDest, pSrcItem, true); - else if (IsBankPos(dst)) - BankItem(sDest, pSrcItem, true); - else if (IsEquipmentPos(dst)) - EquipItem(eDest, pSrcItem, true); - - // add to src - if (IsInventoryPos(src)) - StoreItem(sDest2, pDstItem, true); - else if (IsBankPos(src)) - BankItem(sDest2, pDstItem, true); - else if (IsEquipmentPos(src)) - EquipItem(eDest2, pDstItem, true); - - AutoUnequipOffhandIfNeed(); -} - -void Player::AddItemToBuyBackSlot(Item* pItem) -{ - if (pItem) - { - uint32 slot = m_currentBuybackSlot; - // if current back slot non-empty search oldest or free - if (m_items[slot]) - { - uint32 oldest_time = GetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1); - uint32 oldest_slot = BUYBACK_SLOT_START; - - for (uint32 i = BUYBACK_SLOT_START + 1; i < BUYBACK_SLOT_END; ++i) - { - // found empty - if (!m_items[i]) - { - slot = i; - break; - } - - uint32 i_time = GetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + i - BUYBACK_SLOT_START); - - if (oldest_time > i_time) - { - oldest_time = i_time; - oldest_slot = i; - } - } - - // find oldest - slot = oldest_slot; - } - - RemoveItemFromBuyBackSlot(slot, true); - DEBUG_LOG("STORAGE: AddItemToBuyBackSlot item = %u, slot = %u", pItem->GetEntry(), slot); - - m_items[slot] = pItem; - time_t base = time(NULL); - uint32 etime = uint32(base - m_logintime + (30 * 3600)); - uint32 eslot = slot - BUYBACK_SLOT_START; - - SetGuidValue(PLAYER_FIELD_VENDORBUYBACK_SLOT_1 + (eslot * 2), pItem->GetObjectGuid()); - if (ItemPrototype const* pProto = pItem->GetProto()) - SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, pProto->SellPrice * pItem->GetCount()); - else - SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, 0); - SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + eslot, (uint32)etime); - - // move to next (for non filled list is move most optimized choice) - if (m_currentBuybackSlot < BUYBACK_SLOT_END - 1) - ++m_currentBuybackSlot; - } -} - -Item* Player::GetItemFromBuyBackSlot(uint32 slot) -{ - DEBUG_LOG("STORAGE: GetItemFromBuyBackSlot slot = %u", slot); - if (slot >= BUYBACK_SLOT_START && slot < BUYBACK_SLOT_END) - return m_items[slot]; - return NULL; -} - -void Player::RemoveItemFromBuyBackSlot(uint32 slot, bool del) -{ - DEBUG_LOG("STORAGE: RemoveItemFromBuyBackSlot slot = %u", slot); - if (slot >= BUYBACK_SLOT_START && slot < BUYBACK_SLOT_END) - { - Item* pItem = m_items[slot]; - if (pItem) - { - pItem->RemoveFromWorld(); - if (del) pItem->SetState(ITEM_REMOVED, this); - } - - m_items[slot] = NULL; - - uint32 eslot = slot - BUYBACK_SLOT_START; - SetGuidValue(PLAYER_FIELD_VENDORBUYBACK_SLOT_1 + (eslot * 2), ObjectGuid()); - SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, 0); - SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + eslot, 0); - - // if current backslot is filled set to now free slot - if (m_items[m_currentBuybackSlot]) - m_currentBuybackSlot = slot; - } -} - -void Player::SendEquipError(InventoryResult msg, Item* pItem, Item* pItem2, uint32 itemid /*= 0*/) const -{ - DEBUG_LOG("WORLD: Sent SMSG_INVENTORY_CHANGE_FAILURE (%u)", msg); - WorldPacket data(SMSG_INVENTORY_CHANGE_FAILURE, 1 + 8 + 8 + 1); - data << uint8(msg); - - if (msg != EQUIP_ERR_OK) - { - data << (pItem ? pItem->GetObjectGuid() : ObjectGuid()); - data << (pItem2 ? pItem2->GetObjectGuid() : ObjectGuid()); - data << uint8(0); // bag type subclass, used with EQUIP_ERR_EVENT_AUTOEQUIP_BIND_CONFIRM and EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG2 - - switch (msg) - { - case EQUIP_ERR_CANT_EQUIP_LEVEL_I: - case EQUIP_ERR_PURCHASE_LEVEL_TOO_LOW: - { - ItemPrototype const* proto = pItem ? pItem->GetProto() : ObjectMgr::GetItemPrototype(itemid); - data << uint32(proto ? proto->RequiredLevel : 0); - break; - } - case EQUIP_ERR_EVENT_AUTOEQUIP_BIND_CONFIRM: // no idea about this one... - { - data << uint64(0); - data << uint32(0); - data << uint64(0); - break; - } - case EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_COUNT_EXCEEDED_IS: - case EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_SOCKETED_EXCEEDED_IS: - case EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED_IS: - { - ItemPrototype const* proto = pItem ? pItem->GetProto() : ObjectMgr::GetItemPrototype(itemid); - data << uint32(proto ? proto->ItemLimitCategory : 0); - break; - } - default: - break; - } - } - GetSession()->SendPacket(&data); -} - -void Player::SendBuyError(BuyResult msg, Creature* pCreature, uint32 item, uint32 param) -{ - DEBUG_LOG("WORLD: Sent SMSG_BUY_FAILED"); - WorldPacket data(SMSG_BUY_FAILED, (8 + 4 + 4 + 1)); - data << (pCreature ? pCreature->GetObjectGuid() : ObjectGuid()); - data << uint32(item); - if (param > 0) - data << uint32(param); - data << uint8(msg); - GetSession()->SendPacket(&data); -} - -void Player::SendSellError(SellResult msg, Creature* pCreature, ObjectGuid itemGuid, uint32 param) -{ - DEBUG_LOG("WORLD: Sent SMSG_SELL_ITEM"); - WorldPacket data(SMSG_SELL_ITEM, (8 + 8 + (param ? 4 : 0) + 1)); // last check 2.0.10 - data << (pCreature ? pCreature->GetObjectGuid() : ObjectGuid()); - data << ObjectGuid(itemGuid); - if (param > 0) - data << uint32(param); - data << uint8(msg); - GetSession()->SendPacket(&data); -} - -void Player::TradeCancel(bool sendback) -{ - if (m_trade) - { - Player* trader = m_trade->GetTrader(); - - // send yellow "Trade canceled" message to both traders - if (sendback) - GetSession()->SendCancelTrade(); - - trader->GetSession()->SendCancelTrade(); - - // cleanup - delete m_trade; - m_trade = NULL; - delete trader->m_trade; - trader->m_trade = NULL; - } -} - -void Player::UpdateItemDuration(uint32 time, bool realtimeonly) -{ - if (m_itemDuration.empty()) - return; - - DEBUG_LOG("Player::UpdateItemDuration(%u,%u)", time, realtimeonly); - - for (ItemDurationList::const_iterator itr = m_itemDuration.begin(); itr != m_itemDuration.end();) - { - Item* item = *itr; - ++itr; // current element can be erased in UpdateDuration - - if ((realtimeonly && (item->GetProto()->ExtraFlags & ITEM_EXTRA_REAL_TIME_DURATION)) || !realtimeonly) - item->UpdateDuration(this, time); - } -} - -void Player::UpdateEnchantTime(uint32 time) -{ - for (EnchantDurationList::iterator itr = m_enchantDuration.begin(), next; itr != m_enchantDuration.end(); itr = next) - { - MANGOS_ASSERT(itr->item); - next = itr; - if (!itr->item->GetEnchantmentId(itr->slot)) - { - next = m_enchantDuration.erase(itr); - } - else if (itr->leftduration <= time) - { - ApplyEnchantment(itr->item, itr->slot, false, false); - itr->item->ClearEnchantment(itr->slot); - next = m_enchantDuration.erase(itr); - } - else if (itr->leftduration > time) - { - itr->leftduration -= time; - ++next; - } - } -} - -void Player::AddEnchantmentDurations(Item* item) -{ - for (int x = 0; x < MAX_ENCHANTMENT_SLOT; ++x) - { - if (x > PRISMATIC_ENCHANTMENT_SLOT && x < PROP_ENCHANTMENT_SLOT_0) - continue; - - if (!item->GetEnchantmentId(EnchantmentSlot(x))) - continue; - - uint32 duration = item->GetEnchantmentDuration(EnchantmentSlot(x)); - if (duration > 0) - AddEnchantmentDuration(item, EnchantmentSlot(x), duration); - } -} - -void Player::RemoveEnchantmentDurations(Item* item) -{ - for (EnchantDurationList::iterator itr = m_enchantDuration.begin(); itr != m_enchantDuration.end();) - { - if (itr->item == item) - { - // save duration in item - item->SetEnchantmentDuration(EnchantmentSlot(itr->slot), itr->leftduration); - itr = m_enchantDuration.erase(itr); - } - else - ++itr; - } -} - -void Player::RemoveAllEnchantments(EnchantmentSlot slot) -{ - // remove enchantments from equipped items first to clean up the m_enchantDuration list - for (EnchantDurationList::iterator itr = m_enchantDuration.begin(), next; itr != m_enchantDuration.end(); itr = next) - { - next = itr; - if (itr->slot == slot) - { - if (itr->item && itr->item->GetEnchantmentId(slot)) - { - // remove from stats - ApplyEnchantment(itr->item, slot, false, false); - // remove visual - itr->item->ClearEnchantment(slot); - } - // remove from update list - next = m_enchantDuration.erase(itr); - } - else - ++next; - } - - // remove enchants from inventory items - // NOTE: no need to remove these from stats, since these aren't equipped - // in inventory - for (int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i) - if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - if (pItem->GetEnchantmentId(slot)) - pItem->ClearEnchantment(slot); - - // in inventory bags - for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) - if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - for (uint32 j = 0; j < pBag->GetBagSize(); ++j) - if (Item* pItem = pBag->GetItemByPos(j)) - if (pItem->GetEnchantmentId(slot)) - pItem->ClearEnchantment(slot); -} - -// duration == 0 will remove item enchant -void Player::AddEnchantmentDuration(Item* item, EnchantmentSlot slot, uint32 duration) -{ - if (!item) - return; - - if (slot >= MAX_ENCHANTMENT_SLOT) - return; - - for (EnchantDurationList::iterator itr = m_enchantDuration.begin(); itr != m_enchantDuration.end(); ++itr) - { - if (itr->item == item && itr->slot == slot) - { - itr->item->SetEnchantmentDuration(itr->slot, itr->leftduration); - m_enchantDuration.erase(itr); - break; - } - } - if (item && duration > 0) - { - GetSession()->SendItemEnchantTimeUpdate(GetObjectGuid(), item->GetObjectGuid(), slot, uint32(duration / 1000)); - m_enchantDuration.push_back(EnchantDuration(item, slot, duration)); - } -} - -void Player::ApplyEnchantment(Item* item, bool apply) -{ - for (uint32 slot = 0; slot < MAX_ENCHANTMENT_SLOT; ++slot) - ApplyEnchantment(item, EnchantmentSlot(slot), apply); -} - -void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool apply_dur, bool ignore_condition) -{ - if (slot > PRISMATIC_ENCHANTMENT_SLOT && slot < PROP_ENCHANTMENT_SLOT_0) - return; - - if (!item) - return; - - if (!item->IsEquipped()) - return; - - if (slot >= MAX_ENCHANTMENT_SLOT) - return; - - if (slot == REFORGE_ENCHANTMENT_SLOT) - { - ApplyReforgeEnchantment(item, apply); - return; - } - - uint32 enchant_id = item->GetEnchantmentId(slot); - if (!enchant_id) - return; - - SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); - if (!pEnchant) - return; - - if (!ignore_condition && pEnchant->EnchantmentCondition && !EnchantmentFitsRequirements(pEnchant->EnchantmentCondition, -1)) - return; - - if (!item->IsBroken()) - { - for (int s = 0; s < 3; ++s) - { - uint32 enchant_display_type = pEnchant->type[s]; - uint32 enchant_amount = pEnchant->amount[s]; - uint32 enchant_spell_id = pEnchant->spellid[s]; - - switch (enchant_display_type) - { - case ITEM_ENCHANTMENT_TYPE_NONE: - break; - case ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL: - // processed in Player::CastItemCombatSpell - break; - case ITEM_ENCHANTMENT_TYPE_DAMAGE: - if (item->GetSlot() == EQUIPMENT_SLOT_MAINHAND) - HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_VALUE, float(enchant_amount), apply); - else if (item->GetSlot() == EQUIPMENT_SLOT_OFFHAND) - HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_VALUE, float(enchant_amount), apply); - else if (item->GetSlot() == EQUIPMENT_SLOT_RANGED) - HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_VALUE, float(enchant_amount), apply); - break; - case ITEM_ENCHANTMENT_TYPE_EQUIP_SPELL: - { - if (enchant_spell_id) - { - if (apply) - { - int32 basepoints = 0; - // Random Property Exist - try found basepoints for spell (basepoints depends from item suffix factor) - if (item->GetItemRandomPropertyId()) - { - ItemRandomSuffixEntry const* item_rand = sItemRandomSuffixStore.LookupEntry(abs(item->GetItemRandomPropertyId())); - if (item_rand) - { - // Search enchant_amount - for (int k = 0; k < 3; ++k) - { - if (item_rand->enchant_id[k] == enchant_id) - { - basepoints = int32((item_rand->prefix[k] * item->GetItemSuffixFactor()) / 10000); - break; - } - } - } - } - // Cast custom spell vs all equal basepoints got from enchant_amount - if (basepoints) - CastCustomSpell(this, enchant_spell_id, &basepoints, &basepoints, &basepoints, true, item); - else - CastSpell(this, enchant_spell_id, true, item); - } - else - RemoveAurasDueToItemSpell(item, enchant_spell_id); - } - break; - } - case ITEM_ENCHANTMENT_TYPE_RESISTANCE: - if (!enchant_amount) - { - ItemRandomSuffixEntry const* item_rand = sItemRandomSuffixStore.LookupEntry(abs(item->GetItemRandomPropertyId())); - if (item_rand) - { - for (int k = 0; k < 3; ++k) - { - if (item_rand->enchant_id[k] == enchant_id) - { - enchant_amount = uint32((item_rand->prefix[k] * item->GetItemSuffixFactor()) / 10000); - break; - } - } - } - } - - HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + enchant_spell_id), TOTAL_VALUE, float(enchant_amount), apply); - break; - case ITEM_ENCHANTMENT_TYPE_STAT: - { - if (!enchant_amount) - { - ItemRandomSuffixEntry const* item_rand_suffix = sItemRandomSuffixStore.LookupEntry(abs(item->GetItemRandomPropertyId())); - if (item_rand_suffix) - { - for (int k = 0; k < 3; ++k) - { - if (item_rand_suffix->enchant_id[k] == enchant_id) - { - enchant_amount = uint32((item_rand_suffix->prefix[k] * item->GetItemSuffixFactor()) / 10000); - break; - } - } - } - } - - DEBUG_LOG("Adding %u to stat nb %u", enchant_amount, enchant_spell_id); - switch (enchant_spell_id) - { - /*case ITEM_MOD_MANA: - DEBUG_LOG("+ %u MANA", enchant_amount); - HandleStatModifier(UNIT_MOD_MANA, BASE_VALUE, float(enchant_amount), apply); - break;*/ - case ITEM_MOD_HEALTH: - DEBUG_LOG("+ %u HEALTH", enchant_amount); - HandleStatModifier(UNIT_MOD_HEALTH, BASE_VALUE, float(enchant_amount), apply); - break; - case ITEM_MOD_AGILITY: - DEBUG_LOG("+ %u AGILITY", enchant_amount); - HandleStatModifier(UNIT_MOD_STAT_AGILITY, TOTAL_VALUE, float(enchant_amount), apply); - ApplyStatBuffMod(STAT_AGILITY, float(enchant_amount), apply); - break; - case ITEM_MOD_STRENGTH: - DEBUG_LOG("+ %u STRENGTH", enchant_amount); - HandleStatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE, float(enchant_amount), apply); - ApplyStatBuffMod(STAT_STRENGTH, float(enchant_amount), apply); - break; - case ITEM_MOD_INTELLECT: - DEBUG_LOG("+ %u INTELLECT", enchant_amount); - HandleStatModifier(UNIT_MOD_STAT_INTELLECT, TOTAL_VALUE, float(enchant_amount), apply); - ApplyStatBuffMod(STAT_INTELLECT, float(enchant_amount), apply); - break; - case ITEM_MOD_SPIRIT: - DEBUG_LOG("+ %u SPIRIT", enchant_amount); - HandleStatModifier(UNIT_MOD_STAT_SPIRIT, TOTAL_VALUE, float(enchant_amount), apply); - ApplyStatBuffMod(STAT_SPIRIT, float(enchant_amount), apply); - break; - case ITEM_MOD_STAMINA: - DEBUG_LOG("+ %u STAMINA", enchant_amount); - HandleStatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_VALUE, float(enchant_amount), apply); - ApplyStatBuffMod(STAT_STAMINA, float(enchant_amount), apply); - break; - case ITEM_MOD_DODGE_RATING: - ApplyRatingMod(CR_DODGE, enchant_amount, apply); - DEBUG_LOG("+ %u DODGE", enchant_amount); - break; - case ITEM_MOD_PARRY_RATING: - ApplyRatingMod(CR_PARRY, enchant_amount, apply); - DEBUG_LOG("+ %u PARRY", enchant_amount); - break; - case ITEM_MOD_CRIT_RANGED_RATING: - ApplyRatingMod(CR_CRIT_RANGED, enchant_amount, apply); - DEBUG_LOG("+ %u RANGED_CRIT", enchant_amount); - break; - case ITEM_MOD_HIT_RATING: - ApplyRatingMod(CR_HIT_MELEE, enchant_amount, apply); - ApplyRatingMod(CR_HIT_RANGED, enchant_amount, apply); - ApplyRatingMod(CR_HIT_SPELL, enchant_amount, apply); - DEBUG_LOG("+ %u HIT", enchant_amount); - break; - case ITEM_MOD_CRIT_RATING: - ApplyRatingMod(CR_CRIT_MELEE, enchant_amount, apply); - ApplyRatingMod(CR_CRIT_RANGED, enchant_amount, apply); - ApplyRatingMod(CR_CRIT_SPELL, enchant_amount, apply); - DEBUG_LOG("+ %u CRITICAL", enchant_amount); - break; - case ITEM_MOD_RESILIENCE_RATING: - ApplyRatingMod(CR_RESILIENCE_DAMAGE_TAKEN, enchant_amount, apply); - DEBUG_LOG("+ %u RESILIENCE", enchant_amount); - break; - case ITEM_MOD_HASTE_RATING: - ApplyRatingMod(CR_HASTE_MELEE, enchant_amount, apply); - ApplyRatingMod(CR_HASTE_RANGED, enchant_amount, apply); - ApplyRatingMod(CR_HASTE_SPELL, enchant_amount, apply); - DEBUG_LOG("+ %u HASTE", enchant_amount); - break; - case ITEM_MOD_EXPERTISE_RATING: - ApplyRatingMod(CR_EXPERTISE, enchant_amount, apply); - DEBUG_LOG("+ %u EXPERTISE", enchant_amount); - break; - case ITEM_MOD_ATTACK_POWER: - HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(enchant_amount), apply); - HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(enchant_amount), apply); - DEBUG_LOG("+ %u ATTACK_POWER", enchant_amount); - break; - case ITEM_MOD_RANGED_ATTACK_POWER: - HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(enchant_amount), apply); - DEBUG_LOG("+ %u RANGED_ATTACK_POWER", enchant_amount); - break; - case ITEM_MOD_SPELL_POWER: - ApplySpellPowerBonus(enchant_amount, apply); - DEBUG_LOG("+ %u SPELL_POWER", enchant_amount); - break; - case ITEM_MOD_HEALTH_REGEN: - ApplyHealthRegenBonus(enchant_amount, apply); - DEBUG_LOG("+ %u HEALTH_REGENERATION", enchant_amount); - break; - case ITEM_MOD_SPELL_PENETRATION: - ApplyModInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE, -int32(enchant_amount), apply); - m_spellPenetrationItemMod += apply ? enchant_amount : -int32(enchant_amount); - DEBUG_LOG("+ %u SPELL_PENETRATION", -int32(enchant_amount)); - break; - case ITEM_MOD_MASTERY_RATING: - ApplyRatingMod(CR_MASTERY, enchant_amount, apply); - DEBUG_LOG("+ %u MASTERY_RATING", enchant_amount); - break; - // deprecated - case ITEM_MOD_DEFENSE_SKILL_RATING: - case ITEM_MOD_BLOCK_RATING: - case ITEM_MOD_HIT_MELEE_RATING: - case ITEM_MOD_HIT_RANGED_RATING: - case ITEM_MOD_HIT_SPELL_RATING: - case ITEM_MOD_CRIT_MELEE_RATING: - case ITEM_MOD_CRIT_SPELL_RATING: - case ITEM_MOD_HIT_TAKEN_MELEE_RATING: - case ITEM_MOD_HIT_TAKEN_RANGED_RATING: - case ITEM_MOD_HIT_TAKEN_SPELL_RATING: - case ITEM_MOD_CRIT_TAKEN_MELEE_RATING: - case ITEM_MOD_CRIT_TAKEN_RANGED_RATING: - case ITEM_MOD_CRIT_TAKEN_SPELL_RATING: - case ITEM_MOD_HASTE_MELEE_RATING: - case ITEM_MOD_HASTE_RANGED_RATING: - case ITEM_MOD_HASTE_SPELL_RATING: - case ITEM_MOD_HIT_TAKEN_RATING: - case ITEM_MOD_CRIT_TAKEN_RATING: - case ITEM_MOD_FERAL_ATTACK_POWER: - case ITEM_MOD_SPELL_HEALING_DONE: - case ITEM_MOD_SPELL_DAMAGE_DONE: - case ITEM_MOD_MANA_REGENERATION: - case ITEM_MOD_ARMOR_PENETRATION_RATING: - case ITEM_MOD_BLOCK_VALUE: - default: - break; - } - break; - } - case ITEM_ENCHANTMENT_TYPE_TOTEM: // Shaman Rockbiter Weapon - { - if (getClass() == CLASS_SHAMAN) - { - float addValue = 0.0f; - if (item->GetSlot() == EQUIPMENT_SLOT_MAINHAND) - { - addValue = float(enchant_amount * item->GetProto()->Delay / 1000.0f); - HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_VALUE, addValue, apply); - } - else if (item->GetSlot() == EQUIPMENT_SLOT_OFFHAND) - { - addValue = float(enchant_amount * item->GetProto()->Delay / 1000.0f); - HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_VALUE, addValue, apply); - } - } - break; - } - case ITEM_ENCHANTMENT_TYPE_USE_SPELL: - // processed in Player::CastItemUseSpell - break; - case ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET: - // nothing do.. - break; - default: - sLog.outError("Unknown item enchantment (id = %d) display type: %d", enchant_id, enchant_display_type); - break; - } /*switch(enchant_display_type)*/ - } /*for*/ - } - - // visualize enchantment at player and equipped items - if (slot == PERM_ENCHANTMENT_SLOT) - SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (item->GetSlot() * 2), 0, apply ? item->GetEnchantmentId(slot) : 0); - - if (slot == TEMP_ENCHANTMENT_SLOT) - SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (item->GetSlot() * 2), 1, apply ? item->GetEnchantmentId(slot) : 0); - - if (apply_dur) - { - if (apply) - { - // set duration - uint32 duration = item->GetEnchantmentDuration(slot); - if (duration > 0) - AddEnchantmentDuration(item, slot, duration); - } - else - { - // duration == 0 will remove EnchantDuration - AddEnchantmentDuration(item, slot, 0); - } - } -} - -void Player::ApplyReforgeEnchantment(Item* item, bool apply) -{ - - if (!item) - return; - - ItemReforgeEntry const* reforge = sItemReforgeStore.LookupEntry(item->GetEnchantmentId(REFORGE_ENCHANTMENT_SLOT)); - if (!reforge) - return; - - float removeValue = item->GetReforgableStat(ItemModType(reforge->SourceStat)) * reforge->SourceMultiplier; - - float addValue = removeValue * reforge->FinalMultiplier; - - switch (reforge->SourceStat) - { - case ITEM_MOD_HEALTH: - HandleStatModifier(UNIT_MOD_HEALTH, BASE_VALUE, -removeValue, apply); - break; - case ITEM_MOD_AGILITY: - HandleStatModifier(UNIT_MOD_STAT_AGILITY, TOTAL_VALUE, -removeValue, apply); - ApplyStatBuffMod(STAT_AGILITY, -removeValue, apply); - break; - case ITEM_MOD_STRENGTH: - HandleStatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE, -removeValue, apply); - ApplyStatBuffMod(STAT_STRENGTH, -removeValue, apply); - break; - case ITEM_MOD_INTELLECT: - HandleStatModifier(UNIT_MOD_STAT_INTELLECT, TOTAL_VALUE, -removeValue, apply); - ApplyStatBuffMod(STAT_INTELLECT, -removeValue, apply); - break; - case ITEM_MOD_SPIRIT: - HandleStatModifier(UNIT_MOD_STAT_SPIRIT, TOTAL_VALUE, -removeValue, apply); - ApplyStatBuffMod(STAT_SPIRIT, -removeValue, apply); - break; - case ITEM_MOD_STAMINA: - HandleStatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_VALUE, -removeValue, apply); - ApplyStatBuffMod(STAT_STAMINA, -removeValue, apply); - break; - case ITEM_MOD_DEFENSE_SKILL_RATING: - ApplyRatingMod(CR_DEFENSE_SKILL, -int32(removeValue), apply); - break; - case ITEM_MOD_DODGE_RATING: - ApplyRatingMod(CR_DODGE, -int32(removeValue), apply); - break; - case ITEM_MOD_PARRY_RATING: - ApplyRatingMod(CR_PARRY, -int32(removeValue), apply); - break; - case ITEM_MOD_BLOCK_RATING: - ApplyRatingMod(CR_BLOCK, -int32(removeValue), apply); - break; - case ITEM_MOD_HIT_MELEE_RATING: - ApplyRatingMod(CR_HIT_MELEE, -int32(removeValue), apply); - break; - case ITEM_MOD_HIT_RANGED_RATING: - ApplyRatingMod(CR_HIT_RANGED, -int32(removeValue), apply); - break; - case ITEM_MOD_HIT_SPELL_RATING: - ApplyRatingMod(CR_HIT_SPELL, -int32(removeValue), apply); - break; - case ITEM_MOD_CRIT_MELEE_RATING: - ApplyRatingMod(CR_CRIT_MELEE, -int32(removeValue), apply); - break; - case ITEM_MOD_CRIT_RANGED_RATING: - ApplyRatingMod(CR_CRIT_RANGED, -int32(removeValue), apply); - break; - case ITEM_MOD_CRIT_SPELL_RATING: - ApplyRatingMod(CR_CRIT_SPELL, -int32(removeValue), apply); - break; - case ITEM_MOD_HASTE_SPELL_RATING: - ApplyRatingMod(CR_HASTE_SPELL, -int32(removeValue), apply); - break; - case ITEM_MOD_HIT_RATING: - ApplyRatingMod(CR_HIT_MELEE, -int32(removeValue), apply); - ApplyRatingMod(CR_HIT_RANGED, -int32(removeValue), apply); - ApplyRatingMod(CR_HIT_SPELL, -int32(removeValue), apply); - break; - case ITEM_MOD_CRIT_RATING: - ApplyRatingMod(CR_CRIT_MELEE, -int32(removeValue), apply); - ApplyRatingMod(CR_CRIT_RANGED, -int32(removeValue), apply); - ApplyRatingMod(CR_CRIT_SPELL, -int32(removeValue), apply); - break; - case ITEM_MOD_RESILIENCE_RATING: - ApplyRatingMod(CR_RESILIENCE_DAMAGE_TAKEN, -int32(removeValue), apply); - break; - case ITEM_MOD_HASTE_RATING: - ApplyRatingMod(CR_HASTE_MELEE, -int32(removeValue), apply); - ApplyRatingMod(CR_HASTE_RANGED, -int32(removeValue), apply); - ApplyRatingMod(CR_HASTE_SPELL, -int32(removeValue), apply); - break; - case ITEM_MOD_EXPERTISE_RATING: - ApplyRatingMod(CR_EXPERTISE, -int32(removeValue), apply); - break; - case ITEM_MOD_ATTACK_POWER: - HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, -removeValue, apply); - HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, -removeValue, apply); - break; - case ITEM_MOD_RANGED_ATTACK_POWER: - HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, -removeValue, apply); - break; - case ITEM_MOD_MANA_REGENERATION: - ApplyManaRegenBonus(-int32(removeValue), apply); - break; - case ITEM_MOD_ARMOR_PENETRATION_RATING: - ApplyRatingMod(CR_ARMOR_PENETRATION, -int32(removeValue), apply); - break; - case ITEM_MOD_SPELL_POWER: - ApplySpellPowerBonus(-int32(removeValue), apply); - break; - case ITEM_MOD_HEALTH_REGEN: - ApplyHealthRegenBonus(-int32(removeValue), apply); - break; - case ITEM_MOD_SPELL_PENETRATION: - ApplyModInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE, -int32(removeValue), apply); - m_spellPenetrationItemMod += apply ? -int32(removeValue) : int32(removeValue); - break; - case ITEM_MOD_BLOCK_VALUE: - break; - case ITEM_MOD_MASTERY_RATING: - ApplyRatingMod(CR_MASTERY, -int32(removeValue), apply); - break; - - } - - switch (reforge->FinalStat) - { - case ITEM_MOD_HEALTH: - HandleStatModifier(UNIT_MOD_HEALTH, BASE_VALUE, addValue, apply); - break; - case ITEM_MOD_AGILITY: - HandleStatModifier(UNIT_MOD_STAT_AGILITY, TOTAL_VALUE, addValue, apply); - ApplyStatBuffMod(STAT_AGILITY, addValue, apply); - break; - case ITEM_MOD_STRENGTH: - HandleStatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE, addValue, apply); - ApplyStatBuffMod(STAT_STRENGTH, addValue, apply); - break; - case ITEM_MOD_INTELLECT: - HandleStatModifier(UNIT_MOD_STAT_INTELLECT, TOTAL_VALUE, addValue, apply); - ApplyStatBuffMod(STAT_INTELLECT, addValue, apply); - break; - case ITEM_MOD_SPIRIT: - HandleStatModifier(UNIT_MOD_STAT_SPIRIT, TOTAL_VALUE, addValue, apply); - ApplyStatBuffMod(STAT_SPIRIT, addValue, apply); - break; - case ITEM_MOD_STAMINA: - HandleStatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_VALUE, addValue, apply); - ApplyStatBuffMod(STAT_STAMINA, addValue, apply); - break; - case ITEM_MOD_DEFENSE_SKILL_RATING: - ApplyRatingMod(CR_DEFENSE_SKILL, int32(addValue), apply); - break; - case ITEM_MOD_DODGE_RATING: - ApplyRatingMod(CR_DODGE, int32(addValue), apply); - break; - case ITEM_MOD_PARRY_RATING: - ApplyRatingMod(CR_PARRY, int32(addValue), apply); - break; - case ITEM_MOD_BLOCK_RATING: - ApplyRatingMod(CR_BLOCK, int32(addValue), apply); - break; - case ITEM_MOD_HIT_MELEE_RATING: - ApplyRatingMod(CR_HIT_MELEE, int32(addValue), apply); - break; - case ITEM_MOD_HIT_RANGED_RATING: - ApplyRatingMod(CR_HIT_RANGED, int32(addValue), apply); - break; - case ITEM_MOD_HIT_SPELL_RATING: - ApplyRatingMod(CR_HIT_SPELL, int32(addValue), apply); - break; - case ITEM_MOD_CRIT_MELEE_RATING: - ApplyRatingMod(CR_CRIT_MELEE, int32(addValue), apply); - break; - case ITEM_MOD_CRIT_RANGED_RATING: - ApplyRatingMod(CR_CRIT_RANGED, int32(addValue), apply); - break; - case ITEM_MOD_CRIT_SPELL_RATING: - ApplyRatingMod(CR_CRIT_SPELL, int32(addValue), apply); - break; - case ITEM_MOD_HASTE_SPELL_RATING: - ApplyRatingMod(CR_HASTE_SPELL, int32(addValue), apply); - break; - case ITEM_MOD_HIT_RATING: - ApplyRatingMod(CR_HIT_MELEE, int32(addValue), apply); - ApplyRatingMod(CR_HIT_RANGED, int32(addValue), apply); - ApplyRatingMod(CR_HIT_SPELL, int32(addValue), apply); - break; - case ITEM_MOD_CRIT_RATING: - ApplyRatingMod(CR_CRIT_MELEE, int32(addValue), apply); - ApplyRatingMod(CR_CRIT_RANGED, int32(addValue), apply); - ApplyRatingMod(CR_CRIT_SPELL, int32(addValue), apply); - break; - case ITEM_MOD_RESILIENCE_RATING: - ApplyRatingMod(CR_RESILIENCE_DAMAGE_TAKEN, int32(addValue), apply); - break; - case ITEM_MOD_HASTE_RATING: - ApplyRatingMod(CR_HASTE_MELEE, int32(addValue), apply); - ApplyRatingMod(CR_HASTE_RANGED, int32(addValue), apply); - ApplyRatingMod(CR_HASTE_SPELL, int32(addValue), apply); - break; - case ITEM_MOD_EXPERTISE_RATING: - ApplyRatingMod(CR_EXPERTISE, int32(addValue), apply); - break; - case ITEM_MOD_ATTACK_POWER: - HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, addValue, apply); - HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, addValue, apply); - break; - case ITEM_MOD_RANGED_ATTACK_POWER: - HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, addValue, apply); - break; - case ITEM_MOD_MANA_REGENERATION: - ApplyManaRegenBonus(int32(addValue), apply); - break; - case ITEM_MOD_ARMOR_PENETRATION_RATING: - ApplyRatingMod(CR_ARMOR_PENETRATION, int32(addValue), apply); - break; - case ITEM_MOD_SPELL_POWER: - ApplySpellPowerBonus(int32(addValue), apply); - break; - case ITEM_MOD_HEALTH_REGEN: - ApplyHealthRegenBonus(int32(addValue), apply); - break; - case ITEM_MOD_SPELL_PENETRATION: - ApplyModInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE, int32(addValue), apply); - m_spellPenetrationItemMod += apply ? int32(addValue) : -int32(addValue); - break; - case ITEM_MOD_BLOCK_VALUE: - break; - case ITEM_MOD_MASTERY_RATING: - ApplyRatingMod(CR_MASTERY, int32(addValue), apply); - break; - } -} - -void Player::SendEnchantmentDurations() -{ - for (EnchantDurationList::const_iterator itr = m_enchantDuration.begin(); itr != m_enchantDuration.end(); ++itr) - { - GetSession()->SendItemEnchantTimeUpdate(GetObjectGuid(), itr->item->GetObjectGuid(), itr->slot, uint32(itr->leftduration) / 1000); - } -} - -void Player::SendItemDurations() -{ - for (ItemDurationList::const_iterator itr = m_itemDuration.begin(); itr != m_itemDuration.end(); ++itr) - { - (*itr)->SendTimeUpdate(this); - } -} - -void Player::SendNewItem(Item* item, uint32 count, bool received, bool created, bool broadcast) -{ - if (!item) // prevent crash - return; - - // last check 2.0.10 - WorldPacket data(SMSG_ITEM_PUSH_RESULT, (8 + 4 + 4 + 4 + 1 + 4 + 4 + 4 + 4 + 4)); - data << GetObjectGuid(); // player GUID - data << uint32(received); // 0=looted, 1=from npc - data << uint32(created); // 0=received, 1=created - data << uint32(1); // IsShowChatMessage - data << uint8(item->GetBagSlot()); // bagslot - // item slot, but when added to stack: 0xFFFFFFFF - data << uint32((item->GetCount() == count) ? item->GetSlot() : -1); - data << uint32(item->GetEntry()); // item id - data << uint32(item->GetItemSuffixFactor()); // SuffixFactor - data << uint32(item->GetItemRandomPropertyId()); // random item property id - data << uint32(count); // count of items - data << uint32(GetItemCount(item->GetEntry())); // count of items in inventory - - if (broadcast && GetGroup()) - GetGroup()->BroadcastPacket(&data, true); - else - GetSession()->SendPacket(&data); -} - -/*********************************************************/ -/*** GOSSIP SYSTEM ***/ -/*********************************************************/ - -void Player::PrepareGossipMenu(WorldObject* pSource, uint32 menuId) -{ - PlayerMenu* pMenu = PlayerTalkClass; - pMenu->ClearMenus(); - - pMenu->GetGossipMenu().SetMenuId(menuId); - - GossipMenuItemsMapBounds pMenuItemBounds = sObjectMgr.GetGossipMenuItemsMapBounds(menuId); - - // prepares quest menu when true - bool canSeeQuests = menuId == GetDefaultGossipMenuForSource(pSource); - - // if canSeeQuests (the default, top level menu) and no menu options exist for this, use options from default options - if (pMenuItemBounds.first == pMenuItemBounds.second && canSeeQuests) - pMenuItemBounds = sObjectMgr.GetGossipMenuItemsMapBounds(0); - - for (GossipMenuItemsMap::const_iterator itr = pMenuItemBounds.first; itr != pMenuItemBounds.second; ++itr) - { - bool hasMenuItem = true; - bool isGMSkipConditionCheck = false; - - if (itr->second.conditionId && !sObjectMgr.IsPlayerMeetToCondition(itr->second.conditionId, this, GetMap(), pSource, CONDITION_FROM_GOSSIP_OPTION)) - { - if (isGameMaster()) // Let GM always see menu items regardless of conditions - isGMSkipConditionCheck = true; - else - { - if (itr->second.option_id == GOSSIP_OPTION_QUESTGIVER) - canSeeQuests = false; - continue; // Skip this option - } - } - - if (pSource->GetTypeId() == TYPEID_UNIT) - { - Creature* pCreature = (Creature*)pSource; - - uint32 npcflags = pCreature->GetUInt32Value(UNIT_NPC_FLAGS); - - if (!(itr->second.npc_option_npcflag & npcflags)) - continue; - - switch (itr->second.option_id) - { - case GOSSIP_OPTION_GOSSIP: - break; - case GOSSIP_OPTION_QUESTGIVER: - hasMenuItem = false; - break; - case GOSSIP_OPTION_ARMORER: - hasMenuItem = false; // added in special mode - break; - case GOSSIP_OPTION_SPIRITHEALER: - if (!isDead()) - hasMenuItem = false; - break; - case GOSSIP_OPTION_VENDOR: - { - VendorItemData const* vItems = pCreature->GetVendorItems(); - VendorItemData const* tItems = pCreature->GetVendorTemplateItems(); - if ((!vItems || vItems->Empty()) && (!tItems || tItems->Empty())) - { - sLog.outErrorDb("Creature %u (Entry: %u) have UNIT_NPC_FLAG_VENDOR but have empty trading item list.", pCreature->GetGUIDLow(), pCreature->GetEntry()); - hasMenuItem = false; - } - break; - } - case GOSSIP_OPTION_TRAINER: - // pet trainers not have spells in fact now - /* FIXME: gossip menu with single unlearn pet talents option not show by some reason - if (pCreature->GetCreatureInfo()->trainer_type == TRAINER_TYPE_PETS) - hasMenuItem = false; - else */ - if (!pCreature->IsTrainerOf(this, false)) - hasMenuItem = false; - break; - case GOSSIP_OPTION_UNLEARNTALENTS: - if (!pCreature->CanTrainAndResetTalentsOf(this)) - hasMenuItem = false; - break; - case GOSSIP_OPTION_UNLEARNPETSKILLS: - if (pCreature->GetCreatureInfo()->trainer_type != TRAINER_TYPE_PETS || pCreature->GetCreatureInfo()->trainer_class != CLASS_HUNTER) - hasMenuItem = false; - else if (Pet* pet = GetPet()) - { - if (pet->getPetType() != HUNTER_PET || pet->m_spells.size() <= 1) - hasMenuItem = false; - } - else - hasMenuItem = false; - break; - case GOSSIP_OPTION_TAXIVENDOR: - if (GetSession()->SendLearnNewTaxiNode(pCreature)) - return; - break; - case GOSSIP_OPTION_BATTLEFIELD: - if (!pCreature->CanInteractWithBattleMaster(this, false)) - hasMenuItem = false; - break; - case GOSSIP_OPTION_STABLEPET: - if (getClass() != CLASS_HUNTER) - hasMenuItem = false; - break; - case GOSSIP_OPTION_SPIRITGUIDE: - case GOSSIP_OPTION_INNKEEPER: - case GOSSIP_OPTION_BANKER: - case GOSSIP_OPTION_PETITIONER: - case GOSSIP_OPTION_TABARDDESIGNER: - case GOSSIP_OPTION_AUCTIONEER: - case GOSSIP_OPTION_MAILBOX: - break; // no checks - default: - sLog.outErrorDb("Creature entry %u have unknown gossip option %u for menu %u", pCreature->GetEntry(), itr->second.option_id, itr->second.menu_id); - hasMenuItem = false; - break; - } - } - else if (pSource->GetTypeId() == TYPEID_GAMEOBJECT) - { - GameObject* pGo = (GameObject*)pSource; - - switch (itr->second.option_id) - { - case GOSSIP_OPTION_QUESTGIVER: - hasMenuItem = false; - break; - case GOSSIP_OPTION_GOSSIP: - if (pGo->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER && pGo->GetGoType() != GAMEOBJECT_TYPE_GOOBER) - hasMenuItem = false; - break; - default: - hasMenuItem = false; - break; - } - } - - if (hasMenuItem) - { - std::string strOptionText = itr->second.option_text; - std::string strBoxText = itr->second.box_text; - - int loc_idx = GetSession()->GetSessionDbLocaleIndex(); - - if (loc_idx >= 0) - { - uint32 idxEntry = MAKE_PAIR32(menuId, itr->second.id); - - if (GossipMenuItemsLocale const* no = sObjectMgr.GetGossipMenuItemsLocale(idxEntry)) - { - if (no->OptionText.size() > (size_t)loc_idx && !no->OptionText[loc_idx].empty()) - strOptionText = no->OptionText[loc_idx]; - - if (no->BoxText.size() > (size_t)loc_idx && !no->BoxText[loc_idx].empty()) - strBoxText = no->BoxText[loc_idx]; - } - } - - if (isGMSkipConditionCheck) - { - strOptionText.append(" ("); - strOptionText.append(GetSession()->GetMangosString(LANG_GM_ON)); - strOptionText.append(")"); - } - - pMenu->GetGossipMenu().AddMenuItem(itr->second.option_icon, strOptionText, 0, itr->second.option_id, strBoxText, itr->second.box_money, itr->second.box_coded); - pMenu->GetGossipMenu().AddGossipMenuItemData(itr->second.action_menu_id, itr->second.action_poi_id, itr->second.action_script_id); - } - } - - if (canSeeQuests) - PrepareQuestMenu(pSource->GetObjectGuid()); - - // some gossips aren't handled in normal way ... so we need to do it this way .. TODO: handle it in normal way ;-) - /*if (pMenu->Empty()) - { - if (pCreature->HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_TRAINER)) - { - // output error message if need - pCreature->IsTrainerOf(this, true); - } - - if (pCreature->HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_BATTLEMASTER)) - { - // output error message if need - pCreature->CanInteractWithBattleMaster(this, true); - } - }*/ -} - -void Player::SendPreparedGossip(WorldObject* pSource) -{ - if (!pSource) - return; - - if (pSource->GetTypeId() == TYPEID_UNIT) - { - // in case no gossip flag and quest menu not empty, open quest menu (client expect gossip menu with this flag) - if (!((Creature*)pSource)->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP) && !PlayerTalkClass->GetQuestMenu().Empty()) - { - SendPreparedQuest(pSource->GetObjectGuid()); - return; - } - } - else if (pSource->GetTypeId() == TYPEID_GAMEOBJECT) - { - // probably need to find a better way here - if (!PlayerTalkClass->GetGossipMenu().GetMenuId() && !PlayerTalkClass->GetQuestMenu().Empty()) - { - SendPreparedQuest(pSource->GetObjectGuid()); - return; - } - } - - // in case non empty gossip menu (that not included quests list size) show it - // (quest entries from quest menu will be included in list) - - uint32 textId = GetGossipTextId(pSource); - - if (uint32 menuId = PlayerTalkClass->GetGossipMenu().GetMenuId()) - textId = GetGossipTextId(menuId, pSource); - - PlayerTalkClass->SendGossipMenu(textId, pSource->GetObjectGuid()); -} - -void Player::OnGossipSelect(WorldObject* pSource, uint32 gossipListId, uint32 menuId) -{ - GossipMenu& gossipmenu = PlayerTalkClass->GetGossipMenu(); - - if (gossipListId >= gossipmenu.MenuItemCount()) - return; - - // if not same, then something funky is going on - if (menuId != gossipmenu.GetMenuId()) - return; - - GossipMenuItem const& menu_item = gossipmenu.GetItem(gossipListId); - - uint32 gossipOptionId = menu_item.m_gOptionId; - ObjectGuid guid = pSource->GetObjectGuid(); - uint32 moneyTake = menu_item.m_gBoxMoney; - - // if this function called and player have money for pay MoneyTake or cheating, proccess both cases - if (moneyTake > 0) - { - if (GetMoney() >= moneyTake) - ModifyMoney(-int64(moneyTake)); - else - return; // cheating - } - - if (pSource->GetTypeId() == TYPEID_GAMEOBJECT) - { - if (gossipOptionId > GOSSIP_OPTION_QUESTGIVER) - { - sLog.outError("Player guid %u request invalid gossip option for GameObject entry %u", GetGUIDLow(), pSource->GetEntry()); - return; - } - } - - GossipMenuItemData pMenuData = gossipmenu.GetItemData(gossipListId); - - switch (gossipOptionId) - { - case GOSSIP_OPTION_GOSSIP: - { - if (pMenuData.m_gAction_poi) - PlayerTalkClass->SendPointOfInterest(pMenuData.m_gAction_poi); - - // send new menu || close gossip || stay at current menu - if (pMenuData.m_gAction_menu > 0) - { - PrepareGossipMenu(pSource, uint32(pMenuData.m_gAction_menu)); - SendPreparedGossip(pSource); - } - else if (pMenuData.m_gAction_menu < 0) - { - PlayerTalkClass->CloseGossip(); - TalkedToCreature(pSource->GetEntry(), pSource->GetObjectGuid()); - } - - break; - } - case GOSSIP_OPTION_SPIRITHEALER: - if (isDead()) - ((Creature*)pSource)->CastSpell(((Creature*)pSource), 17251, true, NULL, NULL, GetObjectGuid()); - break; - case GOSSIP_OPTION_QUESTGIVER: - PrepareQuestMenu(guid); - SendPreparedQuest(guid); - break; - case GOSSIP_OPTION_VENDOR: - case GOSSIP_OPTION_ARMORER: - GetSession()->SendListInventory(guid); - break; - case GOSSIP_OPTION_STABLEPET: - GetSession()->SendStablePet(guid); - break; - case GOSSIP_OPTION_TRAINER: - GetSession()->SendTrainerList(guid); - break; - case GOSSIP_OPTION_UNLEARNTALENTS: - PlayerTalkClass->CloseGossip(); - SendTalentWipeConfirm(guid); - break; - case GOSSIP_OPTION_UNLEARNPETSKILLS: - PlayerTalkClass->CloseGossip(); - SendPetSkillWipeConfirm(); - break; - case GOSSIP_OPTION_TAXIVENDOR: - GetSession()->SendTaxiMenu(((Creature*)pSource)); - break; - case GOSSIP_OPTION_INNKEEPER: - PlayerTalkClass->CloseGossip(); - SetBindPoint(guid); - break; - case GOSSIP_OPTION_BANKER: - GetSession()->SendShowBank(guid); - break; - case GOSSIP_OPTION_PETITIONER: - PlayerTalkClass->CloseGossip(); - GetSession()->SendPetitionShowList(guid); - break; - case GOSSIP_OPTION_TABARDDESIGNER: - PlayerTalkClass->CloseGossip(); - GetSession()->SendTabardVendorActivate(guid); - break; - case GOSSIP_OPTION_AUCTIONEER: - GetSession()->SendAuctionHello(((Creature*)pSource)); - break; - case GOSSIP_OPTION_MAILBOX: - PlayerTalkClass->CloseGossip(); - GetSession()->SendShowMailBox(guid); - break; - case GOSSIP_OPTION_SPIRITGUIDE: - PrepareGossipMenu(pSource); - SendPreparedGossip(pSource); - break; - case GOSSIP_OPTION_BATTLEFIELD: - { - BattleGroundTypeId bgTypeId = sBattleGroundMgr.GetBattleMasterBG(pSource->GetEntry()); - - if (bgTypeId == BATTLEGROUND_TYPE_NONE) - { - sLog.outError("a user (guid %u) requested battlegroundlist from a npc who is no battlemaster", GetGUIDLow()); - return; - } - - GetSession()->SendBattlegGroundList(guid, bgTypeId); - break; - } - } - - if (pMenuData.m_gAction_script) - { - if (pSource->GetTypeId() == TYPEID_UNIT) - GetMap()->ScriptsStart(sGossipScripts, pMenuData.m_gAction_script, pSource, this, Map::SCRIPT_EXEC_PARAM_UNIQUE_BY_SOURCE); - else if (pSource->GetTypeId() == TYPEID_GAMEOBJECT) - GetMap()->ScriptsStart(sGossipScripts, pMenuData.m_gAction_script, this, pSource, Map::SCRIPT_EXEC_PARAM_UNIQUE_BY_TARGET); - } -} - -uint32 Player::GetGossipTextId(WorldObject* pSource) -{ - if (!pSource || pSource->GetTypeId() != TYPEID_UNIT) - return DEFAULT_GOSSIP_MESSAGE; - - if (uint32 pos = sObjectMgr.GetNpcGossip(((Creature*)pSource)->GetGUIDLow())) - return pos; - - return DEFAULT_GOSSIP_MESSAGE; -} - -uint32 Player::GetGossipTextId(uint32 menuId, WorldObject* pSource) -{ - uint32 textId = DEFAULT_GOSSIP_MESSAGE; - - if (!menuId) - return textId; - - uint32 scriptId = 0; - uint32 lastConditionId = 0; - - GossipMenusMapBounds pMenuBounds = sObjectMgr.GetGossipMenusMapBounds(menuId); - for (GossipMenusMap::const_iterator itr = pMenuBounds.first; itr != pMenuBounds.second; ++itr) - { - // Take the text that has the highest conditionId of all fitting - // No condition and no text with condition found OR higher and fitting condition found - if ((!itr->second.conditionId && !lastConditionId) || - (itr->second.conditionId > lastConditionId && sObjectMgr.IsPlayerMeetToCondition(itr->second.conditionId, this, GetMap(), pSource, CONDITION_FROM_GOSSIP_MENU))) - { - lastConditionId = itr->second.conditionId; - textId = itr->second.text_id; - scriptId = itr->second.script_id; - } - } - - // Start related script - if (scriptId) - GetMap()->ScriptsStart(sGossipScripts, scriptId, this, pSource, Map::SCRIPT_EXEC_PARAM_UNIQUE_BY_TARGET); - - return textId; -} - -uint32 Player::GetDefaultGossipMenuForSource(WorldObject* pSource) -{ - if (pSource->GetTypeId() == TYPEID_UNIT) - return ((Creature*)pSource)->GetCreatureInfo()->GossipMenuId; - else if (pSource->GetTypeId() == TYPEID_GAMEOBJECT) - return ((GameObject*)pSource)->GetGOInfo()->GetGossipMenuId(); - - return 0; -} - -/*********************************************************/ -/*** QUEST SYSTEM ***/ -/*********************************************************/ - -void Player::PrepareQuestMenu(ObjectGuid guid) -{ - QuestRelationsMapBounds rbounds; - QuestRelationsMapBounds irbounds; - - // pets also can have quests - if (Creature* pCreature = GetMap()->GetAnyTypeCreature(guid)) - { - rbounds = sObjectMgr.GetCreatureQuestRelationsMapBounds(pCreature->GetEntry()); - irbounds = sObjectMgr.GetCreatureQuestInvolvedRelationsMapBounds(pCreature->GetEntry()); - } - else - { - // we should obtain map pointer from GetMap() in 99% of cases. Special case - // only for quests which cast teleport spells on player - Map* _map = IsInWorld() ? GetMap() : sMapMgr.FindMap(GetMapId(), GetInstanceId()); - MANGOS_ASSERT(_map); - - if (GameObject* pGameObject = _map->GetGameObject(guid)) - { - rbounds = sObjectMgr.GetGOQuestRelationsMapBounds(pGameObject->GetEntry()); - irbounds = sObjectMgr.GetGOQuestInvolvedRelationsMapBounds(pGameObject->GetEntry()); - } - else - return; - } - - QuestMenu& qm = PlayerTalkClass->GetQuestMenu(); - qm.ClearMenu(); - - for (QuestRelationsMap::const_iterator itr = irbounds.first; itr != irbounds.second; ++itr) - { - uint32 quest_id = itr->second; - - Quest const* pQuest = sObjectMgr.GetQuestTemplate(quest_id); - - if (!pQuest || !pQuest->IsActive()) - continue; - - QuestStatus status = GetQuestStatus(quest_id); - - if (status == QUEST_STATUS_COMPLETE && !GetQuestRewardStatus(quest_id)) - qm.AddMenuItem(quest_id, 4); - else if (status == QUEST_STATUS_INCOMPLETE) - qm.AddMenuItem(quest_id, 4); - else if (status == QUEST_STATUS_AVAILABLE) - qm.AddMenuItem(quest_id, 2); - } - - for (QuestRelationsMap::const_iterator itr = rbounds.first; itr != rbounds.second; ++itr) - { - uint32 quest_id = itr->second; - - Quest const* pQuest = sObjectMgr.GetQuestTemplate(quest_id); - - if (!pQuest || !pQuest->IsActive()) - continue; - - QuestStatus status = GetQuestStatus(quest_id); - - if (pQuest->IsAutoComplete() && CanTakeQuest(pQuest, false)) - qm.AddMenuItem(quest_id, 4); - else if (status == QUEST_STATUS_NONE && CanTakeQuest(pQuest, false)) - qm.AddMenuItem(quest_id, 2); - } -} - -void Player::SendPreparedQuest(ObjectGuid guid) -{ - QuestMenu& questMenu = PlayerTalkClass->GetQuestMenu(); - - if (questMenu.Empty()) - return; - - QuestMenuItem const& qmi0 = questMenu.GetItem(0); - - uint32 icon = qmi0.m_qIcon; - - // single element case - if (questMenu.MenuItemCount() == 1) - { - // Auto open -- maybe also should verify there is no greeting - uint32 quest_id = qmi0.m_qId; - Quest const* pQuest = sObjectMgr.GetQuestTemplate(quest_id); - - if (pQuest) - { - if (icon == 4 && !GetQuestRewardStatus(quest_id)) - PlayerTalkClass->SendQuestGiverRequestItems(pQuest, guid, CanRewardQuest(pQuest, false), true); - else if (icon == 4) - PlayerTalkClass->SendQuestGiverRequestItems(pQuest, guid, CanRewardQuest(pQuest, false), true); - // Send completable on repeatable and autoCompletable quest if player don't have quest - // TODO: verify if check for !pQuest->IsDaily() is really correct (possibly not) - else if (pQuest->IsAutoComplete() && pQuest->IsRepeatable() && !pQuest->IsDailyOrWeekly()) - PlayerTalkClass->SendQuestGiverRequestItems(pQuest, guid, CanCompleteRepeatableQuest(pQuest), true); - else - PlayerTalkClass->SendQuestGiverQuestDetails(pQuest, guid, true); - } - } - // multiply entries - else - { - QEmote qe; - qe._Delay = 0; - qe._Emote = 0; - std::string title = ""; - - // need pet case for some quests - if (Creature* pCreature = GetMap()->GetAnyTypeCreature(guid)) - { - uint32 textid = GetGossipTextId(pCreature); - - GossipText const* gossiptext = sObjectMgr.GetGossipText(textid); - if (!gossiptext) - { - qe._Delay = 0; // TEXTEMOTE_MESSAGE; // zyg: player emote - qe._Emote = 0; // TEXTEMOTE_HELLO; // zyg: NPC emote - title = ""; - } - else - { - qe = gossiptext->Options[0].Emotes[0]; - - int loc_idx = GetSession()->GetSessionDbLocaleIndex(); - - std::string title0 = gossiptext->Options[0].Text_0; - std::string title1 = gossiptext->Options[0].Text_1; - sObjectMgr.GetNpcTextLocaleStrings0(textid, loc_idx, &title0, &title1); - - title = !title0.empty() ? title0 : title1; - } - } - PlayerTalkClass->SendQuestGiverQuestList(qe, title, guid); - } -} - -bool Player::IsActiveQuest(uint32 quest_id) const -{ - QuestStatusMap::const_iterator itr = mQuestStatus.find(quest_id); - - return itr != mQuestStatus.end() && itr->second.m_status != QUEST_STATUS_NONE; -} - -bool Player::IsCurrentQuest(uint32 quest_id, uint8 completed_or_not) const -{ - QuestStatusMap::const_iterator itr = mQuestStatus.find(quest_id); - if (itr == mQuestStatus.end()) - return false; - - switch (completed_or_not) - { - case 1: - return itr->second.m_status == QUEST_STATUS_INCOMPLETE; - case 2: - return itr->second.m_status == QUEST_STATUS_COMPLETE && !itr->second.m_rewarded; - default: - return itr->second.m_status == QUEST_STATUS_INCOMPLETE || (itr->second.m_status == QUEST_STATUS_COMPLETE && !itr->second.m_rewarded); - } -} - -Quest const* Player::GetNextQuest(ObjectGuid guid, Quest const* pQuest) -{ - QuestRelationsMapBounds rbounds; - - if (Creature* pCreature = GetMap()->GetAnyTypeCreature(guid)) - { - rbounds = sObjectMgr.GetCreatureQuestRelationsMapBounds(pCreature->GetEntry()); - } - else - { - // we should obtain map pointer from GetMap() in 99% of cases. Special case - // only for quests which cast teleport spells on player - Map* _map = IsInWorld() ? GetMap() : sMapMgr.FindMap(GetMapId(), GetInstanceId()); - MANGOS_ASSERT(_map); - - if (GameObject* pGameObject = _map->GetGameObject(guid)) - { - rbounds = sObjectMgr.GetGOQuestRelationsMapBounds(pGameObject->GetEntry()); - } - else - return NULL; - } - - uint32 nextQuestID = pQuest->GetNextQuestInChain(); - for (QuestRelationsMap::const_iterator itr = rbounds.first; itr != rbounds.second; ++itr) - { - if (itr->second == nextQuestID) - return sObjectMgr.GetQuestTemplate(nextQuestID); - } - - return NULL; -} - -/** - * Check if a player could see a start quest - * Basic Quest-taking requirements: Class, Race, Skill, Quest-Line, ... - * Check if the quest-level is not too high (related config value CONFIG_INT32_QUEST_HIGH_LEVEL_HIDE_DIFF) - */ -bool Player::CanSeeStartQuest(Quest const* pQuest) const -{ - if (SatisfyQuestClass(pQuest, false) && SatisfyQuestRace(pQuest, false) && SatisfyQuestSkill(pQuest, false) && - SatisfyQuestExclusiveGroup(pQuest, false) && SatisfyQuestReputation(pQuest, false) && - SatisfyQuestPreviousQuest(pQuest, false) && SatisfyQuestNextChain(pQuest, false) && - SatisfyQuestPrevChain(pQuest, false) && SatisfyQuestDay(pQuest, false) && SatisfyQuestWeek(pQuest, false) && - SatisfyQuestMonth(pQuest, false) && - pQuest->IsActive()) - { - int32 highLevelDiff = sWorld.getConfig(CONFIG_INT32_QUEST_HIGH_LEVEL_HIDE_DIFF); - if (highLevelDiff < 0) - return true; - return getLevel() + uint32(highLevelDiff) >= pQuest->GetMinLevel(); - } - - return false; -} - -bool Player::CanTakeQuest(Quest const* pQuest, bool msg) const -{ - return SatisfyQuestStatus(pQuest, msg) && SatisfyQuestExclusiveGroup(pQuest, msg) && - SatisfyQuestClass(pQuest, msg) && SatisfyQuestRace(pQuest, msg) && SatisfyQuestLevel(pQuest, msg) && - SatisfyQuestSkill(pQuest, msg) && SatisfyQuestReputation(pQuest, msg) && - SatisfyQuestPreviousQuest(pQuest, msg) && SatisfyQuestTimed(pQuest, msg) && - SatisfyQuestNextChain(pQuest, msg) && SatisfyQuestPrevChain(pQuest, msg) && - SatisfyQuestDay(pQuest, msg) && SatisfyQuestWeek(pQuest, msg) && SatisfyQuestMonth(pQuest, msg) && - pQuest->IsActive(); -} - -bool Player::CanAddQuest(Quest const* pQuest, bool msg) const -{ - if (!SatisfyQuestLog(msg)) - return false; - - if (!CanGiveQuestSourceItemIfNeed(pQuest)) - return false; - - return true; -} - -bool Player::CanCompleteQuest(uint32 quest_id) const -{ - if (!quest_id) - return false; - - QuestStatusMap::const_iterator q_itr = mQuestStatus.find(quest_id); - - // some quests can be auto taken and auto completed in one step - QuestStatus status = q_itr != mQuestStatus.end() ? q_itr->second.m_status : QUEST_STATUS_NONE; - - if (status == QUEST_STATUS_COMPLETE) - return false; // not allow re-complete quest - - Quest const* qInfo = sObjectMgr.GetQuestTemplate(quest_id); - - if (!qInfo) - return false; - - // only used for "flag" quests and not real in-game quests - if (qInfo->HasQuestFlag(QUEST_FLAGS_AUTO_REWARDED)) - { - // a few checks, not all "satisfy" is needed - if (SatisfyQuestPreviousQuest(qInfo, false) && SatisfyQuestLevel(qInfo, false) && SatisfyQuestSpell(qInfo, false) && - SatisfyQuestSkill(qInfo, false) && SatisfyQuestRace(qInfo, false) && SatisfyQuestClass(qInfo, false)) - return true; - - return false; - } - - // auto complete quest - if (qInfo->IsAutoComplete() && CanTakeQuest(qInfo, false)) - return true; - - if (status != QUEST_STATUS_INCOMPLETE) - return false; - - // incomplete quest have status data - QuestStatusData const& q_status = q_itr->second; - - if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER)) - { - for (int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i) - { - if (qInfo->ReqItemCount[i] != 0 && q_status.m_itemcount[i] < qInfo->ReqItemCount[i]) - return false; - } - - for (int i = 0; i < QUEST_REQUIRED_CURRENCY_COUNT; ++i) - { - if (qInfo->ReqCurrencyId[i] && !HasCurrencyCount(qInfo->ReqCurrencyId[i], int32(qInfo->ReqCurrencyCount[i] * GetCurrencyPrecision(qInfo->ReqCurrencyId[i])))) - return false; - } - } - - if (qInfo->HasSpecialFlag(QuestSpecialFlags(QUEST_SPECIAL_FLAG_KILL_OR_CAST | QUEST_SPECIAL_FLAG_SPEAKTO))) - { - for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) - { - if (qInfo->ReqCreatureOrGOId[i] == 0) - continue; - - if (qInfo->ReqCreatureOrGOCount[i] != 0 && q_status.m_creatureOrGOcount[i] < qInfo->ReqCreatureOrGOCount[i]) - return false; - } - } - - if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT) && !q_status.m_explored) - return false; - - if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAG_TIMED) && q_status.m_timer == 0) - return false; - - if (qInfo->GetRewOrReqMoney() < 0) - { - if (GetMoney() < uint64(-qInfo->GetRewOrReqMoney())) - return false; - } - - uint32 repFacId = qInfo->GetRepObjectiveFaction(); - if (repFacId && GetReputationMgr().GetReputation(repFacId) < qInfo->GetRepObjectiveValue()) - return false; - - if (uint32 spell = qInfo->GetReqSpellLearned()) - if (!HasSpell(spell)) - return false; - - return true; -} - -bool Player::CanCompleteRepeatableQuest(Quest const* pQuest) const -{ - // Solve problem that player don't have the quest and try complete it. - // if repeatable she must be able to complete event if player don't have it. - // Seem that all repeatable quest are DELIVER Flag so, no need to add more. - if (!CanTakeQuest(pQuest, false)) - return false; - - if (pQuest->HasSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER)) - { - for (int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i) - if (pQuest->ReqItemId[i] && pQuest->ReqItemCount[i] && !HasItemCount(pQuest->ReqItemId[i], pQuest->ReqItemCount[i])) - return false; - - for (int i = 0; i < QUEST_REQUIRED_CURRENCY_COUNT; ++i) - { - if (pQuest->ReqCurrencyId[i] && !HasCurrencyCount(pQuest->ReqCurrencyId[i], int32(pQuest->ReqCurrencyCount[i] * GetCurrencyPrecision(pQuest->ReqCurrencyId[i])))) - return false; - } - } - - if (!CanRewardQuest(pQuest, false)) - return false; - - return true; -} - -bool Player::CanRewardQuest(Quest const* pQuest, bool msg) const -{ - // not auto complete quest and not completed quest (only cheating case, then ignore without message) - if (!pQuest->IsAutoComplete() && GetQuestStatus(pQuest->GetQuestId()) != QUEST_STATUS_COMPLETE) - return false; - - // daily quest can't be rewarded (25 daily quest already completed) - if (!SatisfyQuestDay(pQuest, true) || !SatisfyQuestWeek(pQuest, true) || !SatisfyQuestMonth(pQuest, true)) - return false; - - // rewarded and not repeatable quest (only cheating case, then ignore without message) - if (GetQuestRewardStatus(pQuest->GetQuestId())) - return false; - - if (pQuest->HasSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER)) - { - // prevent receive reward with quest items in bank - for (int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i) - { - if (pQuest->ReqItemCount[i] != 0 && - GetItemCount(pQuest->ReqItemId[i]) < pQuest->ReqItemCount[i]) - { - if (msg) - SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL, pQuest->ReqItemId[i]); - - return false; - } - } - - // prevent receive reward with low currency - for (int i = 0; i < QUEST_REQUIRED_CURRENCY_COUNT; ++i) - { - if (pQuest->ReqCurrencyId[i] && - !HasCurrencyCount(pQuest->ReqCurrencyId[i], int32(pQuest->ReqCurrencyCount[i] * GetCurrencyPrecision(pQuest->ReqCurrencyId[i])))) - { - if (msg) - SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL); - - return false; - } - } - } - - // prevent receive reward with low money and GetRewOrReqMoney() < 0 - if (pQuest->GetRewOrReqMoney() < 0 && GetMoney() < uint64(-pQuest->GetRewOrReqMoney())) - return false; - - if (uint32 spell = pQuest->GetReqSpellLearned()) - if (!HasSpell(spell)) - return false; - - return true; -} - -bool Player::CanRewardQuest(Quest const* pQuest, uint32 reward, bool msg) const -{ - // prevent receive reward with quest items in bank or for not completed quest - if (!CanRewardQuest(pQuest, msg)) - return false; - - if (pQuest->GetRewChoiceItemsCount() > 0) - { - if (pQuest->RewChoiceItemId[reward]) - { - ItemPosCountVec dest; - InventoryResult res = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, pQuest->RewChoiceItemId[reward], pQuest->RewChoiceItemCount[reward]); - if (res != EQUIP_ERR_OK) - { - SendEquipError(res, NULL, NULL, pQuest->RewChoiceItemId[reward]); - return false; - } - } - } - - if (pQuest->GetRewItemsCount() > 0) - { - for (uint32 i = 0; i < pQuest->GetRewItemsCount(); ++i) - { - if (pQuest->RewItemId[i]) - { - ItemPosCountVec dest; - InventoryResult res = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, pQuest->RewItemId[i], pQuest->RewItemCount[i]); - if (res != EQUIP_ERR_OK) - { - SendEquipError(res, NULL, NULL); - return false; - } - } - } - } - - return true; -} - -void Player::SendPetTameFailure(PetTameFailureReason reason) -{ - WorldPacket data(SMSG_PET_TAME_FAILURE, 1); - data << uint8(reason); - GetSession()->SendPacket(&data); -} - -void Player::AddQuest(Quest const* pQuest, Object* questGiver) -{ - uint16 log_slot = FindQuestSlot(0); - MANGOS_ASSERT(log_slot < MAX_QUEST_LOG_SIZE); - - uint32 quest_id = pQuest->GetQuestId(); - - // if not exist then created with set uState==NEW and rewarded=false - QuestStatusData& questStatusData = mQuestStatus[quest_id]; - - // check for repeatable quests status reset - questStatusData.m_status = QUEST_STATUS_INCOMPLETE; - questStatusData.m_explored = false; - - if (pQuest->HasSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER)) - { - for (int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i) - questStatusData.m_itemcount[i] = 0; - } - - if (pQuest->HasSpecialFlag(QuestSpecialFlags(QUEST_SPECIAL_FLAG_KILL_OR_CAST | QUEST_SPECIAL_FLAG_SPEAKTO))) - { - for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) - questStatusData.m_creatureOrGOcount[i] = 0; - } - - if (pQuest->GetRepObjectiveFaction()) - if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(pQuest->GetRepObjectiveFaction())) - GetReputationMgr().SetVisible(factionEntry); - - uint32 qtime = 0; - if (pQuest->HasSpecialFlag(QUEST_SPECIAL_FLAG_TIMED)) - { - uint32 limittime = pQuest->GetLimitTime(); - - // shared timed quest - if (questGiver && questGiver->GetTypeId() == TYPEID_PLAYER) - limittime = ((Player*)questGiver)->getQuestStatusMap()[quest_id].m_timer / IN_MILLISECONDS; - - AddTimedQuest(quest_id); - questStatusData.m_timer = limittime * IN_MILLISECONDS; - qtime = static_cast(time(NULL)) + limittime; - } - else - questStatusData.m_timer = 0; - - SetQuestSlot(log_slot, quest_id, qtime); - - if (questStatusData.uState != QUEST_NEW) - questStatusData.uState = QUEST_CHANGED; - - // quest accept scripts - if (questGiver) - { - switch (questGiver->GetTypeId()) - { - case TYPEID_UNIT: - sScriptMgr.OnQuestAccept(this, (Creature*)questGiver, pQuest); - break; - case TYPEID_ITEM: - case TYPEID_CONTAINER: - sScriptMgr.OnQuestAccept(this, (Item*)questGiver, pQuest); - break; - case TYPEID_GAMEOBJECT: - sScriptMgr.OnQuestAccept(this, (GameObject*)questGiver, pQuest); - break; - } - - // starting initial DB quest script - if (pQuest->GetQuestStartScript() != 0) - GetMap()->ScriptsStart(sQuestStartScripts, pQuest->GetQuestStartScript(), questGiver, this, Map::SCRIPT_EXEC_PARAM_UNIQUE_BY_SOURCE); - } - - // remove start item if not need - if (questGiver && questGiver->isType(TYPEMASK_ITEM)) - { - // destroy not required for quest finish quest starting item - bool notRequiredItem = true; - for (int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i) - { - if (pQuest->ReqItemId[i] == questGiver->GetEntry()) - { - notRequiredItem = false; - break; - } - } - - if (pQuest->GetSrcItemId() == questGiver->GetEntry()) - notRequiredItem = false; - - if (notRequiredItem) - DestroyItem(((Item*)questGiver)->GetBagSlot(), ((Item*)questGiver)->GetSlot(), true); - } - - GiveQuestSourceItemIfNeed(pQuest); - - AdjustQuestReqItemCount(pQuest, questStatusData); - - // Some spells applied at quest activation - uint32 zone, area; - GetZoneAndAreaId(zone, area); - SpellAreaForAreaMapBounds saBounds = sSpellMgr.GetSpellAreaForAreaMapBounds(zone); - for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) - itr->second->ApplyOrRemoveSpellIfCan(this, zone, area, true); - if (area != zone) - { - saBounds = sSpellMgr.GetSpellAreaForAreaMapBounds(area); - for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) - itr->second->ApplyOrRemoveSpellIfCan(this, zone, area, true); - } - saBounds = sSpellMgr.GetSpellAreaForAreaMapBounds(0); - for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) - itr->second->ApplyOrRemoveSpellIfCan(this, zone, area, true); - - UpdateForQuestWorldObjects(); -} - -void Player::CompleteQuest(uint32 quest_id) -{ - if (quest_id) - { - SetQuestStatus(quest_id, QUEST_STATUS_COMPLETE); - - uint16 log_slot = FindQuestSlot(quest_id); - if (log_slot < MAX_QUEST_LOG_SIZE) - SetQuestSlotState(log_slot, QUEST_STATE_COMPLETE); - - if (Quest const* qInfo = sObjectMgr.GetQuestTemplate(quest_id)) - { - if (qInfo->HasQuestFlag(QUEST_FLAGS_AUTO_REWARDED)) - RewardQuest(qInfo, 0, this, false); - } - } -} - -void Player::IncompleteQuest(uint32 quest_id) -{ - if (quest_id) - { - SetQuestStatus(quest_id, QUEST_STATUS_INCOMPLETE); - - uint16 log_slot = FindQuestSlot(quest_id); - if (log_slot < MAX_QUEST_LOG_SIZE) - RemoveQuestSlotState(log_slot, QUEST_STATE_COMPLETE); - } -} - -void Player::RewardQuest(Quest const* pQuest, uint32 reward, Object* questGiver, bool announce) -{ - uint32 quest_id = pQuest->GetQuestId(); - - for (int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i) - { - if (pQuest->ReqItemId[i]) - DestroyItemCount(pQuest->ReqItemId[i], pQuest->ReqItemCount[i], true); - } - - for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i) - { - if (pQuest->ReqSourceId[i]) - { - ItemPrototype const* iProto = ObjectMgr::GetItemPrototype(pQuest->ReqSourceId[i]); - if (iProto && iProto->Bonding == BIND_QUEST_ITEM) - DestroyItemCount(pQuest->ReqSourceId[i], pQuest->ReqSourceCount[i], true, false, true); - } - } - - // take currency - for (uint32 i = 0; i < QUEST_REQUIRED_CURRENCY_COUNT; ++i) - { - if (pQuest->ReqCurrencyId[i]) - ModifyCurrencyCount(pQuest->ReqCurrencyId[i], -int32(pQuest->ReqCurrencyCount[i] * GetCurrencyPrecision(pQuest->ReqCurrencyId[i]))); - } - - RemoveTimedQuest(quest_id); - - if (BattleGround* bg = GetBattleGround()) - if (bg->GetTypeID() == BATTLEGROUND_AV) - ((BattleGroundAV*)bg)->HandleQuestComplete(pQuest->GetQuestId(), this); - - if (pQuest->GetRewChoiceItemsCount() > 0) - { - if (uint32 itemId = pQuest->RewChoiceItemId[reward]) - { - ItemPosCountVec dest; - if (CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, pQuest->RewChoiceItemCount[reward]) == EQUIP_ERR_OK) - { - Item* item = StoreNewItem(dest, itemId, true, Item::GenerateItemRandomPropertyId(itemId)); - SendNewItem(item, pQuest->RewChoiceItemCount[reward], true, false); - } - } - } - - if (pQuest->GetRewItemsCount() > 0) - { - for (uint32 i = 0; i < pQuest->GetRewItemsCount(); ++i) - { - if (uint32 itemId = pQuest->RewItemId[i]) - { - ItemPosCountVec dest; - if (CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, pQuest->RewItemCount[i]) == EQUIP_ERR_OK) - { - Item* item = StoreNewItem(dest, itemId, true, Item::GenerateItemRandomPropertyId(itemId)); - SendNewItem(item, pQuest->RewItemCount[i], true, false); - } - } - } - } - - RewardReputation(pQuest); - - uint16 log_slot = FindQuestSlot(quest_id); - if (log_slot < MAX_QUEST_LOG_SIZE) - SetQuestSlot(log_slot, 0); - - QuestStatusData& q_status = mQuestStatus[quest_id]; - - // Used for client inform but rewarded only in case not max level - uint32 xp = uint32(pQuest->XPValue(this) * sWorld.getConfig(CONFIG_FLOAT_RATE_XP_QUEST)); - - if (getLevel() < sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL)) - { - GiveXP(xp , NULL); - - // Give player extra money (for max level already included in pQuest->GetRewMoneyMaxLevel()) - if (pQuest->GetRewOrReqMoney() > 0) - { - ModifyMoney(pQuest->GetRewOrReqMoney()); - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD, pQuest->GetRewOrReqMoney()); - } - } - else - { - // reward money for max level already included in pQuest->GetRewMoneyMaxLevel() - uint64 money = uint32(pQuest->GetRewMoneyMaxLevel() * sWorld.getConfig(CONFIG_FLOAT_RATE_DROP_MONEY)); - - // reward money used if > xp replacement money - if (pQuest->GetRewOrReqMoney() > int64(money)) - money = pQuest->GetRewOrReqMoney(); - - ModifyMoney(money); - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD, money); - } - - // req money case - if (pQuest->GetRewOrReqMoney() < 0) - ModifyMoney(pQuest->GetRewOrReqMoney()); - - // reward currency - for (uint32 i = 0; i < QUEST_REWARD_CURRENCY_COUNT; ++i) - { - if (pQuest->RewCurrencyId[i]) - ModifyCurrencyCount(pQuest->RewCurrencyId[i], int32(pQuest->RewCurrencyCount[i] * GetCurrencyPrecision(pQuest->RewCurrencyId[i]))); - } - - // reward skill - if (uint32 skill = pQuest->GetRewSkill()) - UpdateSkill(skill, pQuest->GetRewSkillValue()); - - // title reward - if (pQuest->GetCharTitleId()) - { - if (CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(pQuest->GetCharTitleId())) - SetTitle(titleEntry); - } - - if (pQuest->GetBonusTalents()) - { - m_questRewardTalentCount += pQuest->GetBonusTalents(); - InitTalentForLevel(); - } - - // Send reward mail - if (uint32 mail_template_id = pQuest->GetRewMailTemplateId()) - MailDraft(mail_template_id).SendMailTo(this, questGiver, MAIL_CHECK_MASK_HAS_BODY, pQuest->GetRewMailDelaySecs()); - - if (pQuest->IsDaily()) - { - SetDailyQuestStatus(quest_id); - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST, 1); - } - - if (pQuest->IsWeekly()) - SetWeeklyQuestStatus(quest_id); - - if (pQuest->IsMonthly()) - SetMonthlyQuestStatus(quest_id); - - if (!pQuest->IsRepeatable()) - SetQuestStatus(quest_id, QUEST_STATUS_COMPLETE); - else - SetQuestStatus(quest_id, QUEST_STATUS_NONE); - - q_status.m_rewarded = true; - if (q_status.uState != QUEST_NEW) - q_status.uState = QUEST_CHANGED; - - if (announce) - SendQuestReward(pQuest, xp, questGiver); - - bool handled = false; - - switch (questGiver->GetTypeId()) - { - case TYPEID_UNIT: - handled = sScriptMgr.OnQuestRewarded(this, (Creature*)questGiver, pQuest); - break; - case TYPEID_GAMEOBJECT: - handled = sScriptMgr.OnQuestRewarded(this, (GameObject*)questGiver, pQuest); - break; - } - - if (!handled && pQuest->GetQuestCompleteScript() != 0) - GetMap()->ScriptsStart(sQuestEndScripts, pQuest->GetQuestCompleteScript(), questGiver, this, Map::SCRIPT_EXEC_PARAM_UNIQUE_BY_SOURCE); - - // cast spells after mark quest complete (some spells have quest completed state reqyurements in spell_area data) - if (pQuest->GetRewSpellCast() > 0) - CastSpell(this, pQuest->GetRewSpellCast(), true); - else if (pQuest->GetRewSpell() > 0) - CastSpell(this, pQuest->GetRewSpell(), true); - - if (pQuest->GetZoneOrSort() > 0) - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE, pQuest->GetZoneOrSort()); - - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT); - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST, pQuest->GetQuestId()); - - UpdateForQuestWorldObjects(); - - // remove auras from spells with quest reward state limitations - // Some spells applied at quest reward - uint32 zone, area; - GetZoneAndAreaId(zone, area); - SpellAreaForAreaMapBounds saBounds = sSpellMgr.GetSpellAreaForAreaMapBounds(zone); - for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) - itr->second->ApplyOrRemoveSpellIfCan(this, zone, area, false); - if (area != zone) - { - saBounds = sSpellMgr.GetSpellAreaForAreaMapBounds(area); - for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) - itr->second->ApplyOrRemoveSpellIfCan(this, zone, area, false); - } - saBounds = sSpellMgr.GetSpellAreaForAreaMapBounds(0); - for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) - itr->second->ApplyOrRemoveSpellIfCan(this, zone, area, false); - - PhaseUpdateData phaseUdateData; - phaseUdateData.AddQuestUpdate(quest_id); - - phaseMgr->NotifyConditionChanged(phaseUdateData); -} - -void Player::FailQuest(uint32 questId) -{ - if (Quest const* pQuest = sObjectMgr.GetQuestTemplate(questId)) - { - SetQuestStatus(questId, QUEST_STATUS_FAILED); - - uint16 log_slot = FindQuestSlot(questId); - - if (log_slot < MAX_QUEST_LOG_SIZE) - { - SetQuestSlotTimer(log_slot, 1); - SetQuestSlotState(log_slot, QUEST_STATE_FAIL); - } - - if (pQuest->HasSpecialFlag(QUEST_SPECIAL_FLAG_TIMED)) - { - QuestStatusData& q_status = mQuestStatus[questId]; - - RemoveTimedQuest(questId); - q_status.m_timer = 0; - - SendQuestTimerFailed(questId); - } - else - SendQuestFailed(questId); - } -} - -bool Player::SatisfyQuestSkill(Quest const* qInfo, bool msg) const -{ - uint32 skill = qInfo->GetRequiredSkill(); - - // skip 0 case RequiredSkill - if (skill == 0) - return true; - - // check skill value - if (GetSkillValue(skill) < qInfo->GetRequiredSkillValue()) - { - if (msg) - SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ); - - return false; - } - - return true; -} - -bool Player::SatisfyQuestSpell(Quest const* qInfo, bool msg) const -{ - uint32 spell = qInfo->GetReqSpellLearned(); - - // skip 0 case ReqSpellLearned - if (spell == 0) - return true; - - // check spell - if (!HasSpell(spell)) - { - if (msg) - SendCanTakeQuestResponse(INVALIDREASON_QUEST_FAILED_SPELL); - - return false; - } - - return true; -} - -bool Player::SatisfyQuestLevel(Quest const* qInfo, bool msg) const -{ - if (getLevel() < qInfo->GetMinLevel()) - { - if (msg) - SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ); - - return false; - } - - return true; -} - -bool Player::SatisfyQuestLog(bool msg) const -{ - // exist free slot - if (FindQuestSlot(0) < MAX_QUEST_LOG_SIZE) - return true; - - if (msg) - { - WorldPacket data(SMSG_QUESTLOG_FULL, 0); - GetSession()->SendPacket(&data); - DEBUG_LOG("WORLD: Sent SMSG_QUESTLOG_FULL"); - } - return false; -} - -bool Player::SatisfyQuestPreviousQuest(Quest const* qInfo, bool msg) const -{ - // No previous quest (might be first quest in a series) - if (qInfo->prevQuests.empty()) - return true; - - for (Quest::PrevQuests::const_iterator iter = qInfo->prevQuests.begin(); iter != qInfo->prevQuests.end(); ++iter) - { - uint32 prevId = abs(*iter); - - QuestStatusMap::const_iterator i_prevstatus = mQuestStatus.find(prevId); - Quest const* qPrevInfo = sObjectMgr.GetQuestTemplate(prevId); - - if (qPrevInfo && i_prevstatus != mQuestStatus.end()) - { - // If any of the positive previous quests completed, return true - if (*iter > 0 && i_prevstatus->second.m_rewarded) - { - // skip one-from-all exclusive group - if (qPrevInfo->GetExclusiveGroup() >= 0) - return true; - - // each-from-all exclusive group ( < 0) - // can be start if only all quests in prev quest exclusive group completed and rewarded - ExclusiveQuestGroupsMapBounds bounds = sObjectMgr.GetExclusiveQuestGroupsMapBounds(qPrevInfo->GetExclusiveGroup()); - - MANGOS_ASSERT(bounds.first != bounds.second); // always must be found if qPrevInfo->ExclusiveGroup != 0 - - for (ExclusiveQuestGroupsMap::const_iterator iter2 = bounds.first; iter2 != bounds.second; ++iter2) - { - uint32 exclude_Id = iter2->second; - - // skip checked quest id, only state of other quests in group is interesting - if (exclude_Id == prevId) - continue; - - QuestStatusMap::const_iterator i_exstatus = mQuestStatus.find(exclude_Id); - - // alternative quest from group also must be completed and rewarded(reported) - if (i_exstatus == mQuestStatus.end() || !i_exstatus->second.m_rewarded) - { - if (msg) - SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ); - - return false; - } - } - return true; - } - // If any of the negative previous quests active, return true - if (*iter < 0 && IsCurrentQuest(prevId)) - { - // skip one-from-all exclusive group - if (qPrevInfo->GetExclusiveGroup() >= 0) - return true; - - // each-from-all exclusive group ( < 0) - // can be start if only all quests in prev quest exclusive group active - ExclusiveQuestGroupsMapBounds bounds = sObjectMgr.GetExclusiveQuestGroupsMapBounds(qPrevInfo->GetExclusiveGroup()); - - MANGOS_ASSERT(bounds.first != bounds.second); // always must be found if qPrevInfo->ExclusiveGroup != 0 - - for (ExclusiveQuestGroupsMap::const_iterator iter2 = bounds.first; iter2 != bounds.second; ++iter2) - { - uint32 exclude_Id = iter2->second; - - // skip checked quest id, only state of other quests in group is interesting - if (exclude_Id == prevId) - continue; - - // alternative quest from group also must be active - if (!IsCurrentQuest(exclude_Id)) - { - if (msg) - SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ); - - return false; - } - } - return true; - } - } - } - - // Has only positive prev. quests in non-rewarded state - // and negative prev. quests in non-active state - if (msg) - SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ); - - return false; -} - -bool Player::SatisfyQuestClass(Quest const* qInfo, bool msg) const -{ - uint32 reqClass = qInfo->GetRequiredClasses(); - - if (reqClass == 0) - return true; - - if ((reqClass & getClassMask()) == 0) - { - if (msg) - SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ); - - return false; - } - - return true; -} - -bool Player::SatisfyQuestRace(Quest const* qInfo, bool msg) const -{ - uint32 reqraces = qInfo->GetRequiredRaces(); - - if (reqraces == 0) - return true; - - if ((reqraces & getRaceMask()) == 0) - { - if (msg) - SendCanTakeQuestResponse(INVALIDREASON_QUEST_FAILED_WRONG_RACE); - - return false; - } - - return true; -} - -bool Player::SatisfyQuestReputation(Quest const* qInfo, bool msg) const -{ - uint32 fIdMin = qInfo->GetRequiredMinRepFaction(); // Min required rep - if (fIdMin && GetReputationMgr().GetReputation(fIdMin) < qInfo->GetRequiredMinRepValue()) - { - if (msg) - SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ); - - return false; - } - - uint32 fIdMax = qInfo->GetRequiredMaxRepFaction(); // Max required rep - if (fIdMax && GetReputationMgr().GetReputation(fIdMax) >= qInfo->GetRequiredMaxRepValue()) - { - if (msg) - SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ); - - return false; - } - - return true; -} - -bool Player::SatisfyQuestStatus(Quest const* qInfo, bool msg) const -{ - QuestStatusMap::const_iterator itr = mQuestStatus.find(qInfo->GetQuestId()); - - if (itr != mQuestStatus.end() && itr->second.m_status != QUEST_STATUS_NONE) - { - if (msg) - SendCanTakeQuestResponse(INVALIDREASON_QUEST_ALREADY_ON); - - return false; - } - - return true; -} - -bool Player::SatisfyQuestTimed(Quest const* qInfo, bool msg) const -{ - if (!m_timedquests.empty() && qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAG_TIMED)) - { - if (msg) - SendCanTakeQuestResponse(INVALIDREASON_QUEST_ONLY_ONE_TIMED); - - return false; - } - - return true; -} - -bool Player::SatisfyQuestExclusiveGroup(Quest const* qInfo, bool msg) const -{ - // non positive exclusive group, if > 0 then can be start if any other quest in exclusive group already started/completed - if (qInfo->GetExclusiveGroup() <= 0) - return true; - - ExclusiveQuestGroupsMapBounds bounds = sObjectMgr.GetExclusiveQuestGroupsMapBounds(qInfo->GetExclusiveGroup()); - - MANGOS_ASSERT(bounds.first != bounds.second); // must always be found if qInfo->ExclusiveGroup != 0 - - for (ExclusiveQuestGroupsMap::const_iterator iter = bounds.first; iter != bounds.second; ++iter) - { - uint32 exclude_Id = iter->second; - - // skip checked quest id, only state of other quests in group is interesting - if (exclude_Id == qInfo->GetQuestId()) - continue; - - // not allow have daily quest if daily quest from exclusive group already recently completed - Quest const* Nquest = sObjectMgr.GetQuestTemplate(exclude_Id); - if (!SatisfyQuestDay(Nquest, false) || !SatisfyQuestWeek(Nquest, false)) - { - if (msg) - SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ); - - return false; - } - - QuestStatusMap::const_iterator i_exstatus = mQuestStatus.find(exclude_Id); - - // alternative quest already started or completed - if (i_exstatus != mQuestStatus.end() && - (i_exstatus->second.m_status == QUEST_STATUS_COMPLETE || i_exstatus->second.m_status == QUEST_STATUS_INCOMPLETE)) - { - if (msg) - SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ); - - return false; - } - } - - return true; -} - -bool Player::SatisfyQuestNextChain(Quest const* qInfo, bool msg) const -{ - if (!qInfo->GetNextQuestInChain()) - return true; - - // next quest in chain already started or completed - QuestStatusMap::const_iterator itr = mQuestStatus.find(qInfo->GetNextQuestInChain()); - if (itr != mQuestStatus.end() && - (itr->second.m_status == QUEST_STATUS_COMPLETE || itr->second.m_status == QUEST_STATUS_INCOMPLETE)) - { - if (msg) - SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ); - - return false; - } - - // check for all quests further up the chain - // only necessary if there are quest chains with more than one quest that can be skipped - // return SatisfyQuestNextChain( qInfo->GetNextQuestInChain(), msg ); - return true; -} - -bool Player::SatisfyQuestPrevChain(Quest const* qInfo, bool msg) const -{ - // No previous quest in chain - if (qInfo->prevChainQuests.empty()) - return true; - - for (Quest::PrevChainQuests::const_iterator iter = qInfo->prevChainQuests.begin(); iter != qInfo->prevChainQuests.end(); ++iter) - { - uint32 prevId = *iter; - - // If any of the previous quests in chain active, return false - if (IsCurrentQuest(prevId)) - { - if (msg) - SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ); - - return false; - } - - // check for all quests further down the chain - // only necessary if there are quest chains with more than one quest that can be skipped - // if( !SatisfyQuestPrevChain( prevId, msg ) ) - // return false; - } - - // No previous quest in chain active - return true; -} - -bool Player::SatisfyQuestDay(Quest const* qInfo, bool msg) const -{ - if (!qInfo->IsDaily()) - return true; - - bool have_slot = false; - for (uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx) - { - uint32 id = GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1 + quest_daily_idx); - if (qInfo->GetQuestId() == id) - return false; - - if (!id) - have_slot = true; - } - - if (!have_slot) - { - if (msg) - SendCanTakeQuestResponse(INVALIDREASON_QUEST_FAILED_TOO_MANY_DAILY_QUESTS); - - return false; - } - - return true; -} - -bool Player::SatisfyQuestWeek(Quest const* qInfo, bool /*msg*/) const -{ - if (!qInfo->IsWeekly() || m_weeklyquests.empty()) - return true; - - // if not found in cooldown list - return m_weeklyquests.find(qInfo->GetQuestId()) == m_weeklyquests.end(); -} - -bool Player::SatisfyQuestMonth(Quest const* qInfo, bool /*msg*/) const -{ - if (!qInfo->IsMonthly() || m_monthlyquests.empty()) - return true; - - // if not found in cooldown list - return m_monthlyquests.find(qInfo->GetQuestId()) == m_monthlyquests.end(); -} - -bool Player::CanGiveQuestSourceItemIfNeed(Quest const* pQuest, ItemPosCountVec* dest) const -{ - if (uint32 srcitem = pQuest->GetSrcItemId()) - { - uint32 count = pQuest->GetSrcItemCount(); - - // player already have max amount required item (including bank), just report success - uint32 has_count = GetItemCount(srcitem, true); - if (has_count >= count) - return true; - - count -= has_count; // real need amount - - InventoryResult msg; - if (!dest) - { - ItemPosCountVec destTemp; - msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, destTemp, srcitem, count); - } - else - msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, *dest, srcitem, count); - - if (msg == EQUIP_ERR_OK) - return true; - else - SendEquipError(msg, NULL, NULL, srcitem); - return false; - } - - return true; -} - -void Player::GiveQuestSourceItemIfNeed(Quest const* pQuest) -{ - ItemPosCountVec dest; - if (CanGiveQuestSourceItemIfNeed(pQuest, &dest) && !dest.empty()) - { - uint32 count = 0; - for (ItemPosCountVec::const_iterator c_itr = dest.begin(); c_itr != dest.end(); ++c_itr) - count += c_itr->count; - - Item* item = StoreNewItem(dest, pQuest->GetSrcItemId(), true); - SendNewItem(item, count, true, false); - } -} - -bool Player::TakeQuestSourceItem(uint32 quest_id, bool msg) -{ - Quest const* qInfo = sObjectMgr.GetQuestTemplate(quest_id); - if (qInfo) - { - uint32 srcitem = qInfo->GetSrcItemId(); - if (srcitem > 0) - { - uint32 count = qInfo->GetSrcItemCount(); - if (count <= 0) - count = 1; - - // exist one case when destroy source quest item not possible: - // non un-equippable item (equipped non-empty bag, for example) - InventoryResult res = CanUnequipItems(srcitem, count); - if (res != EQUIP_ERR_OK) - { - if (msg) - SendEquipError(res, NULL, NULL, srcitem); - return false; - } - - DestroyItemCount(srcitem, count, true, true); - } - } - return true; -} - -bool Player::GetQuestRewardStatus(uint32 quest_id) const -{ - Quest const* qInfo = sObjectMgr.GetQuestTemplate(quest_id); - if (qInfo) - { - // for repeatable quests: rewarded field is set after first reward only to prevent getting XP more than once - QuestStatusMap::const_iterator itr = mQuestStatus.find(quest_id); - if (itr != mQuestStatus.end() && itr->second.m_status != QUEST_STATUS_NONE - && !qInfo->IsRepeatable()) - return itr->second.m_rewarded; - - return false; - } - return false; -} - -QuestStatus Player::GetQuestStatus(uint32 quest_id) const -{ - if (quest_id) - { - QuestStatusMap::const_iterator itr = mQuestStatus.find(quest_id); - if (itr != mQuestStatus.end()) - return itr->second.m_status; - } - return QUEST_STATUS_NONE; -} - -bool Player::CanShareQuest(uint32 quest_id) const -{ - if (Quest const* qInfo = sObjectMgr.GetQuestTemplate(quest_id)) - if (qInfo->HasQuestFlag(QUEST_FLAGS_SHARABLE)) - return IsCurrentQuest(quest_id); - - return false; -} - -void Player::SetQuestStatus(uint32 quest_id, QuestStatus status) -{ - if (sObjectMgr.GetQuestTemplate(quest_id)) - { - QuestStatusData& q_status = mQuestStatus[quest_id]; - - q_status.m_status = status; - - if (q_status.uState != QUEST_NEW) - q_status.uState = QUEST_CHANGED; - } - - PhaseUpdateData phaseUdateData; - phaseUdateData.AddQuestUpdate(quest_id); - - phaseMgr->NotifyConditionChanged(phaseUdateData); - - UpdateForQuestWorldObjects(); -} - -// not used in MaNGOS, but used in scripting code -uint32 Player::GetReqKillOrCastCurrentCount(uint32 quest_id, int32 entry) -{ - Quest const* qInfo = sObjectMgr.GetQuestTemplate(quest_id); - if (!qInfo) - return 0; - - for (int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j) - if (qInfo->ReqCreatureOrGOId[j] == entry) - return mQuestStatus[quest_id].m_creatureOrGOcount[j]; - - return 0; -} - -void Player::AdjustQuestReqItemCount(Quest const* pQuest, QuestStatusData& questStatusData) -{ - if (pQuest->HasSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER)) - { - for (int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i) - { - uint32 reqitemcount = pQuest->ReqItemCount[i]; - if (reqitemcount != 0) - { - uint32 curitemcount = GetItemCount(pQuest->ReqItemId[i], true); - - questStatusData.m_itemcount[i] = std::min(curitemcount, reqitemcount); - if (questStatusData.uState != QUEST_NEW) questStatusData.uState = QUEST_CHANGED; - } - } - } -} - -uint16 Player::FindQuestSlot(uint32 quest_id) const -{ - for (uint16 i = 0; i < MAX_QUEST_LOG_SIZE; ++i) - if (GetQuestSlotQuestId(i) == quest_id) - return i; - - return MAX_QUEST_LOG_SIZE; -} - -void Player::AreaExploredOrEventHappens(uint32 questId) -{ - if (questId) - { - uint16 log_slot = FindQuestSlot(questId); - if (log_slot < MAX_QUEST_LOG_SIZE) - { - QuestStatusData& q_status = mQuestStatus[questId]; - - if (!q_status.m_explored) - { - SetQuestSlotState(log_slot, QUEST_STATE_COMPLETE); - SendQuestCompleteEvent(questId); - q_status.m_explored = true; - - if (q_status.uState != QUEST_NEW) - q_status.uState = QUEST_CHANGED; - } - } - if (CanCompleteQuest(questId)) - CompleteQuest(questId); - } -} - -// not used in mangosd, function for external script library -void Player::GroupEventHappens(uint32 questId, WorldObject const* pEventObject) -{ - if (Group* pGroup = GetGroup()) - { - for (GroupReference* itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player* pGroupGuy = itr->getSource(); - - // for any leave or dead (with not released body) group member at appropriate distance - if (pGroupGuy && pGroupGuy->IsAtGroupRewardDistance(pEventObject) && !pGroupGuy->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) - pGroupGuy->AreaExploredOrEventHappens(questId); - } - } - else - AreaExploredOrEventHappens(questId); -} - -void Player::CurrencyAddedQuestCheck(uint32 entry) -{ - for (int i = 0; i < MAX_QUEST_LOG_SIZE; ++i) - { - uint32 questid = GetQuestSlotQuestId(i); - if (questid == 0) - continue; - - QuestStatusData& q_status = mQuestStatus[questid]; - - if (q_status.m_status != QUEST_STATUS_INCOMPLETE) - continue; - - Quest const* qInfo = sObjectMgr.GetQuestTemplate(questid); - if (!qInfo || !qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER)) - continue; - - for (int j = 0; j < QUEST_REQUIRED_CURRENCY_COUNT; ++j) - { - uint32 reqcurrency = qInfo->ReqCurrencyId[j]; - if (reqcurrency == entry) - { - if (CanCompleteQuest(questid)) - CompleteQuest(questid); - } - } - } - - UpdateForQuestWorldObjects(); -} - -void Player::CurrencyRemovedQuestCheck(uint32 entry) -{ - for (int i = 0; i < MAX_QUEST_LOG_SIZE; ++i) - { - uint32 questid = GetQuestSlotQuestId(i); - if (!questid) - continue; - Quest const* qInfo = sObjectMgr.GetQuestTemplate(questid); - if (!qInfo || !qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER)) - continue; - - for (int j = 0; j < QUEST_REQUIRED_CURRENCY_COUNT; ++j) - { - uint32 reqcurrency = qInfo->ReqCurrencyId[j]; - if (reqcurrency == entry) - { - if (!HasCurrencyCount(entry, int32(qInfo->ReqCurrencyCount[j] * GetCurrencyPrecision(entry)))) - IncompleteQuest(questid); - } - } - } - - UpdateForQuestWorldObjects(); -} - -void Player::ItemAddedQuestCheck(uint32 entry, uint32 count) -{ - for (int i = 0; i < MAX_QUEST_LOG_SIZE; ++i) - { - uint32 questid = GetQuestSlotQuestId(i); - if (questid == 0) - continue; - - QuestStatusData& q_status = mQuestStatus[questid]; - - if (q_status.m_status != QUEST_STATUS_INCOMPLETE) - continue; - - Quest const* qInfo = sObjectMgr.GetQuestTemplate(questid); - if (!qInfo || !qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER)) - continue; - - for (int j = 0; j < QUEST_ITEM_OBJECTIVES_COUNT; ++j) - { - uint32 reqitem = qInfo->ReqItemId[j]; - if (reqitem == entry) - { - uint32 reqitemcount = qInfo->ReqItemCount[j]; - uint32 curitemcount = q_status.m_itemcount[j]; - if (curitemcount < reqitemcount) - { - uint32 additemcount = (curitemcount + count <= reqitemcount ? count : reqitemcount - curitemcount); - q_status.m_itemcount[j] += additemcount; - if (q_status.uState != QUEST_NEW) - q_status.uState = QUEST_CHANGED; - } - if (CanCompleteQuest(questid)) - CompleteQuest(questid); - return; - } - } - } - UpdateForQuestWorldObjects(); -} - -void Player::ItemRemovedQuestCheck(uint32 entry, uint32 count) -{ - for (int i = 0; i < MAX_QUEST_LOG_SIZE; ++i) - { - uint32 questid = GetQuestSlotQuestId(i); - if (!questid) - continue; - Quest const* qInfo = sObjectMgr.GetQuestTemplate(questid); - if (!qInfo) - continue; - if (!qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER)) - continue; - - for (int j = 0; j < QUEST_ITEM_OBJECTIVES_COUNT; ++j) - { - uint32 reqitem = qInfo->ReqItemId[j]; - if (reqitem == entry) - { - QuestStatusData& q_status = mQuestStatus[questid]; - - uint32 reqitemcount = qInfo->ReqItemCount[j]; - uint32 curitemcount; - if (q_status.m_status != QUEST_STATUS_COMPLETE) - curitemcount = q_status.m_itemcount[j]; - else - curitemcount = GetItemCount(entry, true); - if (curitemcount < reqitemcount + count) - { - uint32 remitemcount = (curitemcount <= reqitemcount ? count : count + reqitemcount - curitemcount); - q_status.m_itemcount[j] = curitemcount - remitemcount; - if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED; - - IncompleteQuest(questid); - } - return; - } - } - } - UpdateForQuestWorldObjects(); -} - -void Player::SpellAddedQuestCheck(uint32 entry) -{ - for (int i = 0; i < MAX_QUEST_LOG_SIZE; ++i) - { - uint32 questid = GetQuestSlotQuestId(i); - if (questid == 0) - continue; - - QuestStatusData& q_status = mQuestStatus[questid]; - - if (q_status.m_status != QUEST_STATUS_INCOMPLETE) - continue; - - Quest const* qInfo = sObjectMgr.GetQuestTemplate(questid); - if (!qInfo) - continue; - - uint32 reqspelllearned = qInfo->GetReqSpellLearned(); - if (!reqspelllearned) - continue; - - if (reqspelllearned == entry) - { - if (CanCompleteQuest(questid)) - CompleteQuest(questid); - } - } - - UpdateForQuestWorldObjects(); -} - -void Player::SpellRemovedQuestCheck(uint32 entry) -{ - for (int i = 0; i < MAX_QUEST_LOG_SIZE; ++i) - { - uint32 questid = GetQuestSlotQuestId(i); - if (!questid) - continue; - Quest const* qInfo = sObjectMgr.GetQuestTemplate(questid); - if (!qInfo) - continue; - - uint32 reqspelllearned = qInfo->GetReqSpellLearned(); - if (!reqspelllearned) - continue; - - if (reqspelllearned == entry) - { - if (!HasSpell(entry)) - IncompleteQuest(questid); - } - } - - UpdateForQuestWorldObjects(); -} - -void Player::KilledMonster(CreatureInfo const* cInfo, ObjectGuid guid) -{ - if (cInfo->Entry) - KilledMonsterCredit(cInfo->Entry, guid); - - for (int i = 0; i < MAX_KILL_CREDIT; ++i) - if (cInfo->KillCredit[i]) - KilledMonsterCredit(cInfo->KillCredit[i], guid); -} - -void Player::KilledMonsterCredit(uint32 entry, ObjectGuid guid) -{ - uint32 addkillcount = 1; - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, entry, addkillcount); - - for (int i = 0; i < MAX_QUEST_LOG_SIZE; ++i) - { - uint32 questid = GetQuestSlotQuestId(i); - if (!questid) - continue; - - Quest const* qInfo = sObjectMgr.GetQuestTemplate(questid); - if (!qInfo) - continue; - // just if !ingroup || !noraidgroup || raidgroup - QuestStatusData& q_status = mQuestStatus[questid]; - if (q_status.m_status == QUEST_STATUS_INCOMPLETE && (!GetGroup() || !GetGroup()->isRaidGroup() || qInfo->IsAllowedInRaid())) - { - if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAG_KILL_OR_CAST)) - { - for (int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j) - { - // skip GO activate objective or none - if (qInfo->ReqCreatureOrGOId[j] <= 0) - continue; - - // skip Cast at creature objective - if (qInfo->ReqSpell[j] != 0) - continue; - - uint32 reqkill = qInfo->ReqCreatureOrGOId[j]; - - if (reqkill == entry) - { - uint32 reqkillcount = qInfo->ReqCreatureOrGOCount[j]; - uint32 curkillcount = q_status.m_creatureOrGOcount[j]; - if (curkillcount < reqkillcount) - { - q_status.m_creatureOrGOcount[j] = curkillcount + addkillcount; - if (q_status.uState != QUEST_NEW) - q_status.uState = QUEST_CHANGED; - - SendQuestUpdateAddCreatureOrGo(qInfo, guid, j, q_status.m_creatureOrGOcount[j]); - } - - if (CanCompleteQuest(questid)) - CompleteQuest(questid); - - // same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization). - continue; - } - } - } - } - } -} - -void Player::CastedCreatureOrGO(uint32 entry, ObjectGuid guid, uint32 spell_id, bool original_caster) -{ - bool isCreature = guid.IsCreatureOrVehicle(); - - uint32 addCastCount = 1; - for (int i = 0; i < MAX_QUEST_LOG_SIZE; ++i) - { - uint32 questid = GetQuestSlotQuestId(i); - if (!questid) - continue; - - Quest const* qInfo = sObjectMgr.GetQuestTemplate(questid); - if (!qInfo) - continue; - - if (!original_caster && !qInfo->HasQuestFlag(QUEST_FLAGS_SHARABLE)) - continue; - - if (!qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAG_KILL_OR_CAST)) - continue; - - QuestStatusData& q_status = mQuestStatus[questid]; - - if (q_status.m_status != QUEST_STATUS_INCOMPLETE) - continue; - - for (int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j) - { - // skip kill creature objective (0) or wrong spell casts - if (qInfo->ReqSpell[j] != spell_id) - continue; - - uint32 reqTarget = 0; - - if (isCreature) - { - // creature activate objectives - if (qInfo->ReqCreatureOrGOId[j] > 0) - // checked at quest_template loading - reqTarget = qInfo->ReqCreatureOrGOId[j]; - } - else - { - // GO activate objective - if (qInfo->ReqCreatureOrGOId[j] < 0) - // checked at quest_template loading - reqTarget = - qInfo->ReqCreatureOrGOId[j]; - } - - // other not this creature/GO related objectives - if (reqTarget != entry) - continue; - - uint32 reqCastCount = qInfo->ReqCreatureOrGOCount[j]; - uint32 curCastCount = q_status.m_creatureOrGOcount[j]; - if (curCastCount < reqCastCount) - { - q_status.m_creatureOrGOcount[j] = curCastCount + addCastCount; - if (q_status.uState != QUEST_NEW) - q_status.uState = QUEST_CHANGED; - - SendQuestUpdateAddCreatureOrGo(qInfo, guid, j, q_status.m_creatureOrGOcount[j]); - } - - if (CanCompleteQuest(questid)) - CompleteQuest(questid); - - // same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization). - break; - } - } -} - -void Player::TalkedToCreature(uint32 entry, ObjectGuid guid) -{ - uint32 addTalkCount = 1; - for (int i = 0; i < MAX_QUEST_LOG_SIZE; ++i) - { - uint32 questid = GetQuestSlotQuestId(i); - if (!questid) - continue; - - Quest const* qInfo = sObjectMgr.GetQuestTemplate(questid); - if (!qInfo) - continue; - - QuestStatusData& q_status = mQuestStatus[questid]; - - if (q_status.m_status == QUEST_STATUS_INCOMPLETE) - { - if (qInfo->HasSpecialFlag(QuestSpecialFlags(QUEST_SPECIAL_FLAG_KILL_OR_CAST | QUEST_SPECIAL_FLAG_SPEAKTO))) - { - for (int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j) - { - // skip spell casts and Gameobject objectives - if (qInfo->ReqSpell[j] > 0 || qInfo->ReqCreatureOrGOId[j] < 0) - continue; - - uint32 reqTarget = 0; - - if (qInfo->ReqCreatureOrGOId[j] > 0) // creature activate objectives - // checked at quest_template loading - reqTarget = qInfo->ReqCreatureOrGOId[j]; - else - continue; - - if (reqTarget == entry) - { - uint32 reqTalkCount = qInfo->ReqCreatureOrGOCount[j]; - uint32 curTalkCount = q_status.m_creatureOrGOcount[j]; - if (curTalkCount < reqTalkCount) - { - q_status.m_creatureOrGOcount[j] = curTalkCount + addTalkCount; - if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED; - - SendQuestUpdateAddCreatureOrGo(qInfo, guid, j, q_status.m_creatureOrGOcount[j]); - } - if (CanCompleteQuest(questid)) - CompleteQuest(questid); - - // same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization). - continue; - } - } - } - } - } -} - -void Player::MoneyChanged(uint32 count) -{ - for (int i = 0; i < MAX_QUEST_LOG_SIZE; ++i) - { - uint32 questid = GetQuestSlotQuestId(i); - if (!questid) - continue; - - Quest const* qInfo = sObjectMgr.GetQuestTemplate(questid); - if (qInfo && qInfo->GetRewOrReqMoney() < 0) - { - QuestStatusData& q_status = mQuestStatus[questid]; - - if (q_status.m_status == QUEST_STATUS_INCOMPLETE) - { - if (int32(count) >= -qInfo->GetRewOrReqMoney()) - { - if (CanCompleteQuest(questid)) - CompleteQuest(questid); - } - } - else if (q_status.m_status == QUEST_STATUS_COMPLETE) - { - if (int32(count) < -qInfo->GetRewOrReqMoney()) - IncompleteQuest(questid); - } - } - } -} - -void Player::ReputationChanged(FactionEntry const* factionEntry) -{ - for (int i = 0; i < MAX_QUEST_LOG_SIZE; ++i) - { - if (uint32 questid = GetQuestSlotQuestId(i)) - { - if (Quest const* qInfo = sObjectMgr.GetQuestTemplate(questid)) - { - if (qInfo->GetRepObjectiveFaction() == factionEntry->ID) - { - QuestStatusData& q_status = mQuestStatus[questid]; - if (q_status.m_status == QUEST_STATUS_INCOMPLETE) - { - if (GetReputationMgr().GetReputation(factionEntry) >= qInfo->GetRepObjectiveValue()) - if (CanCompleteQuest(questid)) - CompleteQuest(questid); - } - else if (q_status.m_status == QUEST_STATUS_COMPLETE) - { - if (GetReputationMgr().GetReputation(factionEntry) < qInfo->GetRepObjectiveValue()) - IncompleteQuest(questid); - } - } - } - } - } -} - -bool Player::HasQuestForItem(uint32 itemid) const -{ - for (int i = 0; i < MAX_QUEST_LOG_SIZE; ++i) - { - uint32 questid = GetQuestSlotQuestId(i); - if (questid == 0) - continue; - - QuestStatusMap::const_iterator qs_itr = mQuestStatus.find(questid); - if (qs_itr == mQuestStatus.end()) - continue; - - QuestStatusData const& q_status = qs_itr->second; - - if (q_status.m_status == QUEST_STATUS_INCOMPLETE) - { - Quest const* qinfo = sObjectMgr.GetQuestTemplate(questid); - if (!qinfo) - continue; - - // hide quest if player is in raid-group and quest is no raid quest - if (GetGroup() && GetGroup()->isRaidGroup() && !qinfo->IsAllowedInRaid() && !InBattleGround()) - continue; - - // There should be no mixed ReqItem/ReqSource drop - // This part for ReqItem drop - for (int j = 0; j < QUEST_ITEM_OBJECTIVES_COUNT; ++j) - { - if (itemid == qinfo->ReqItemId[j] && q_status.m_itemcount[j] < qinfo->ReqItemCount[j]) - return true; - } - // This part - for ReqSource - for (int j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j) - { - // examined item is a source item - if (qinfo->ReqSourceId[j] == itemid) - { - ItemPrototype const* pProto = ObjectMgr::GetItemPrototype(itemid); - - // 'unique' item - if (pProto->MaxCount && (int32)GetItemCount(itemid, true) < pProto->MaxCount) - return true; - - // allows custom amount drop when not 0 - if (qinfo->ReqSourceCount[j]) - { - if (GetItemCount(itemid, true) < qinfo->ReqSourceCount[j]) - return true; - } - else if ((int32)GetItemCount(itemid, true) < pProto->Stackable) - return true; - } - } - } - } - return false; -} - -// Used for quests having some event (explore, escort, "external event") as quest objective. -void Player::SendQuestCompleteEvent(uint32 quest_id) -{ - if (quest_id) - { - WorldPacket data(SMSG_QUESTUPDATE_COMPLETE, 4); - data << uint32(quest_id); - GetSession()->SendPacket(&data); - DEBUG_LOG("WORLD: Sent SMSG_QUESTUPDATE_COMPLETE quest = %u", quest_id); - } -} - -void Player::SendQuestReward(Quest const* pQuest, uint32 XP, Object* /*questGiver*/) -{ - uint32 questid = pQuest->GetQuestId(); - DEBUG_LOG("WORLD: Sent SMSG_QUESTGIVER_QUEST_COMPLETE quest = %u", questid); - WorldPacket data(SMSG_QUESTGIVER_QUEST_COMPLETE, (4 + 4 + 4 + 4 + 4)); - data << uint32(pQuest->GetBonusTalents()); - data << uint32(pQuest->GetRewSkillValue()); - - if (getLevel() < sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL)) - { - data << uint32(pQuest->GetRewOrReqMoney()); - data << uint32(XP); - } - else - { - data << uint32(pQuest->GetRewOrReqMoney() + int32(pQuest->GetRewMoneyMaxLevel() * sWorld.getConfig(CONFIG_FLOAT_RATE_DROP_MONEY))); - data << uint32(0); - } - - data << uint32(questid); - data << uint32(pQuest->GetRewSkill()); - - data.WriteBit(0); // unk - data.WriteBit(1); // unk - - GetSession()->SendPacket(&data); -} - -void Player::SendQuestFailed(uint32 quest_id, InventoryResult reason) -{ - if (quest_id) - { - WorldPacket data(SMSG_QUESTGIVER_QUEST_FAILED, 4 + 4); - data << uint32(quest_id); - data << uint32(reason); // failed reason (valid reasons: 4, 16, 50, 17, 74, other values show default message) - GetSession()->SendPacket(&data); - DEBUG_LOG("WORLD: Sent SMSG_QUESTGIVER_QUEST_FAILED"); - } -} - -void Player::SendQuestTimerFailed(uint32 quest_id) -{ - if (quest_id) - { - WorldPacket data(SMSG_QUESTUPDATE_FAILEDTIMER, 4); - data << uint32(quest_id); - GetSession()->SendPacket(&data); - DEBUG_LOG("WORLD: Sent SMSG_QUESTUPDATE_FAILEDTIMER"); - } -} - -void Player::SendCanTakeQuestResponse(uint32 msg) const -{ - WorldPacket data(SMSG_QUESTGIVER_QUEST_INVALID, 4); - data << uint32(msg); - GetSession()->SendPacket(&data); - DEBUG_LOG("WORLD: Sent SMSG_QUESTGIVER_QUEST_INVALID"); -} - -void Player::SendQuestConfirmAccept(const Quest* pQuest, Player* pReceiver) -{ - if (pReceiver) - { - int loc_idx = pReceiver->GetSession()->GetSessionDbLocaleIndex(); - std::string title = pQuest->GetTitle(); - sObjectMgr.GetQuestLocaleStrings(pQuest->GetQuestId(), loc_idx, &title); - - WorldPacket data(SMSG_QUEST_CONFIRM_ACCEPT, (4 + title.size() + 8)); - data << uint32(pQuest->GetQuestId()); - data << title; - data << GetObjectGuid(); - pReceiver->GetSession()->SendPacket(&data); - - DEBUG_LOG("WORLD: Sent SMSG_QUEST_CONFIRM_ACCEPT"); - } -} - -void Player::SendPushToPartyResponse(Player* pPlayer, uint32 msg) -{ - if (pPlayer) - { - WorldPacket data(MSG_QUEST_PUSH_RESULT, (8 + 1)); - data << pPlayer->GetObjectGuid(); - data << uint8(msg); // valid values: 0-8 - GetSession()->SendPacket(&data); - DEBUG_LOG("WORLD: Sent MSG_QUEST_PUSH_RESULT"); - } -} - -void Player::SendQuestUpdateAddCreatureOrGo(Quest const* pQuest, ObjectGuid guid, uint32 creatureOrGO_idx, uint32 count) -{ - MANGOS_ASSERT(count < 65536 && "mob/GO count store in 16 bits 2^16 = 65536 (0..65536)"); - - int32 entry = pQuest->ReqCreatureOrGOId[ creatureOrGO_idx ]; - if (entry < 0) - // client expected gameobject template id in form (id|0x80000000) - entry = (-entry) | 0x80000000; - - WorldPacket data(SMSG_QUESTUPDATE_ADD_KILL, (4 * 4 + 8)); - DEBUG_LOG("WORLD: Sent SMSG_QUESTUPDATE_ADD_KILL"); - data << uint32(pQuest->GetQuestId()); - data << uint32(entry); - data << uint32(count); - data << uint32(pQuest->ReqCreatureOrGOCount[ creatureOrGO_idx ]); - data << guid; - GetSession()->SendPacket(&data); - - uint16 log_slot = FindQuestSlot(pQuest->GetQuestId()); - if (log_slot < MAX_QUEST_LOG_SIZE) - SetQuestSlotCounter(log_slot, creatureOrGO_idx, count); -} - -/*********************************************************/ -/*** LOAD SYSTEM ***/ -/*********************************************************/ - -void Player::_LoadDeclinedNames(QueryResult* result) -{ - if (!result) - return; - - delete m_declinedname; - m_declinedname = new DeclinedName; - - Field* fields = result->Fetch(); - for (int i = 0; i < MAX_DECLINED_NAME_CASES; ++i) - m_declinedname->name[i] = fields[i].GetCppString(); - - delete result; -} - -void Player::_LoadArenaTeamInfo(QueryResult* result) -{ - // arenateamid, played_week, played_season, personal_rating - memset((void*)&m_uint32Values[PLAYER_FIELD_ARENA_TEAM_INFO_1_1], 0, sizeof(uint32) * MAX_ARENA_SLOT * ARENA_TEAM_END); - if (!result) - return; - - do - { - Field* fields = result->Fetch(); - - uint32 arenateamid = fields[0].GetUInt32(); - uint32 played_week = fields[1].GetUInt32(); - uint32 played_season = fields[2].GetUInt32(); - uint32 wons_season = fields[3].GetUInt32(); - uint32 personal_rating = fields[4].GetUInt32(); - - ArenaTeam* aTeam = sObjectMgr.GetArenaTeamById(arenateamid); - if (!aTeam) - { - sLog.outError("Player::_LoadArenaTeamInfo: couldn't load arenateam %u", arenateamid); - continue; - } - uint8 arenaSlot = aTeam->GetSlot(); - - SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_ID, arenateamid); - SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_TYPE, aTeam->GetType()); - SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_MEMBER, (aTeam->GetCaptainGuid() == GetObjectGuid()) ? 0 : 1); - SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_GAMES_WEEK, played_week); - SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_GAMES_SEASON, played_season); - SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_WINS_SEASON, wons_season); - SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_PERSONAL_RATING, personal_rating); - } - while (result->NextRow()); - delete result; -} - -void Player::_LoadEquipmentSets(QueryResult* result) -{ - // SetPQuery(PLAYER_LOGIN_QUERY_LOADEQUIPMENTSETS, "SELECT setguid, setindex, name, iconname, ignore_mask, item0, item1, item2, item3, item4, item5, item6, item7, item8, item9, item10, item11, item12, item13, item14, item15, item16, item17, item18 FROM character_equipmentsets WHERE guid = '%u' ORDER BY setindex", GUID_LOPART(m_guid)); - if (!result) - return; - - uint32 count = 0; - do - { - Field* fields = result->Fetch(); - - EquipmentSet eqSet; - - eqSet.Guid = fields[0].GetUInt64(); - uint32 index = fields[1].GetUInt32(); - eqSet.Name = fields[2].GetCppString(); - eqSet.IconName = fields[3].GetCppString(); - eqSet.IgnoreMask = fields[4].GetUInt32(); - eqSet.state = EQUIPMENT_SET_UNCHANGED; - - for (uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i) - eqSet.Items[i] = fields[5 + i].GetUInt32(); - - m_EquipmentSets[index] = eqSet; - - ++count; - - if (count >= MAX_EQUIPMENT_SET_INDEX) // client limit - break; - } - while (result->NextRow()); - delete result; -} - -void Player::_LoadBGData(QueryResult* result) -{ - if (!result) - return; - - // Expecting only one row - Field* fields = result->Fetch(); - /* bgInstanceID, bgTeam, x, y, z, o, map, taxi[0], taxi[1], mountSpell */ - m_bgData.bgInstanceID = fields[0].GetUInt32(); - m_bgData.bgTeam = Team(fields[1].GetUInt32()); - m_bgData.joinPos = WorldLocation(fields[6].GetUInt32(), // Map - fields[2].GetFloat(), // X - fields[3].GetFloat(), // Y - fields[4].GetFloat(), // Z - fields[5].GetFloat()); // Orientation - m_bgData.taxiPath[0] = fields[7].GetUInt32(); - m_bgData.taxiPath[1] = fields[8].GetUInt32(); - m_bgData.mountSpell = fields[9].GetUInt32(); - - delete result; -} - -bool Player::LoadPositionFromDB(ObjectGuid guid, uint32& mapid, float& x, float& y, float& z, float& o, bool& in_flight) -{ - QueryResult* result = CharacterDatabase.PQuery("SELECT position_x,position_y,position_z,orientation,map,taxi_path FROM characters WHERE guid = '%u'", guid.GetCounter()); - if (!result) - return false; - - Field* fields = result->Fetch(); - - x = fields[0].GetFloat(); - y = fields[1].GetFloat(); - z = fields[2].GetFloat(); - o = fields[3].GetFloat(); - mapid = fields[4].GetUInt32(); - in_flight = !fields[5].GetCppString().empty(); - - delete result; - return true; -} - -void Player::_LoadIntoDataField(const char* data, uint32 startOffset, uint32 count) -{ - if (!data) - return; - - Tokens tokens = StrSplit(data, " "); - - if (tokens.size() != count) - return; - - Tokens::iterator iter; - uint32 index; - for (iter = tokens.begin(), index = 0; index < count; ++iter, ++index) - { - m_uint32Values[startOffset + index] = atol((*iter).c_str()); - } -} - -bool Player::LoadFromDB(ObjectGuid guid, SqlQueryHolder* holder) -{ - // 0 1 2 3 4 5 6 7 8 9 10 11 - // SELECT guid, account, name, race, class, gender, level, xp, money, playerBytes, playerBytes2, playerFlags," - // 12 13 14 15 16 17 18 19 20 21 22 23 24 - //"position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost," - // 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 - //"resettalents_time, primary_trees, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty," - // 40 41 42 43 44 45 - //"totalKills, todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk," - // 46 47 48 49 50 51 52 53 54 55 56 57 58 - //"health, power1, power2, power3, power4, power5, specCount, activeSpec, exploredZones, equipmentCache, knownTitles, actionBars, slot FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid)); - QueryResult *result = holder->GetResult(PLAYER_LOGIN_QUERY_LOADFROM); - - if (!result) - { - sLog.outError("%s not found in table `characters`, can't load. ", guid.GetString().c_str()); - return false; - } - - Field* fields = result->Fetch(); - - uint32 dbAccountId = fields[1].GetUInt32(); - - // check if the character's account in the db and the logged in account match. - // player should be able to load/delete character only with correct account! - if (dbAccountId != GetSession()->GetAccountId()) - { - sLog.outError("%s loading from wrong account (is: %u, should be: %u)", - guid.GetString().c_str(), GetSession()->GetAccountId(), dbAccountId); - delete result; - return false; - } - - Object::_Create(guid.GetCounter(), 0, HIGHGUID_PLAYER); - - m_name = fields[2].GetCppString(); - - // check name limitations - if (ObjectMgr::CheckPlayerName(m_name) != CHAR_NAME_SUCCESS || - (GetSession()->GetSecurity() == SEC_PLAYER && sObjectMgr.IsReservedName(m_name))) - { - delete result; - CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '%u' WHERE guid ='%u'", - uint32(AT_LOGIN_RENAME), guid.GetCounter()); - return false; - } - - // overwrite possible wrong/corrupted guid - SetGuidValue(OBJECT_FIELD_GUID, guid); - - // overwrite some data fields - SetByteValue(UNIT_FIELD_BYTES_0, 0, fields[3].GetUInt8()); // race - SetByteValue(UNIT_FIELD_BYTES_0, 1, fields[4].GetUInt8()); // class - - uint8 gender = fields[5].GetUInt8() & 0x01; - SetByteValue(UNIT_FIELD_BYTES_0, 2, gender); // gender - - SetUInt32Value(UNIT_FIELD_LEVEL, fields[6].GetUInt8()); - SetUInt32Value(PLAYER_XP, fields[7].GetUInt32()); - - _LoadIntoDataField(fields[54].GetString(), PLAYER_EXPLORED_ZONES_1, PLAYER_EXPLORED_ZONES_SIZE); - _LoadIntoDataField(fields[56].GetString(), PLAYER__FIELD_KNOWN_TITLES, KNOWN_TITLES_SIZE*2); - - InitDisplayIds(); // model, scale and model data - - SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 1.0f); - - // just load criteria/achievement data, safe call before any load, and need, because some spell/item/quest loading - // can triggering achievement criteria update that will be lost if this call will later - m_achievementMgr.LoadFromDB(holder->GetResult(PLAYER_LOGIN_QUERY_LOADACHIEVEMENTS), holder->GetResult(PLAYER_LOGIN_QUERY_LOADCRITERIAPROGRESS)); - - uint64 money = fields[8].GetUInt64(); - if (money > MAX_MONEY_AMOUNT) - money = MAX_MONEY_AMOUNT; - SetMoney(money); - - SetUInt32Value(PLAYER_BYTES, fields[9].GetUInt32()); - SetUInt32Value(PLAYER_BYTES_2, fields[10].GetUInt32()); - - SetByteValue(PLAYER_BYTES_3, 0, gender); - SetByteValue(PLAYER_BYTES_3, 1, fields[45].GetUInt8()); - - SetUInt32Value(PLAYER_FLAGS, fields[11].GetUInt32()); - SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, fields[44].GetInt32()); - - // Action bars state - SetByteValue(PLAYER_FIELD_BYTES, 2, fields[57].GetUInt8()); - - m_slot = fields[58].GetUInt8(); - - // cleanup inventory related item value fields (its will be filled correctly in _LoadInventory) - for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot) - { - SetGuidValue(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2), ObjectGuid()); - SetVisibleItemSlot(slot, NULL); - - delete m_items[slot]; - m_items[slot] = NULL; - } - - DEBUG_FILTER_LOG(LOG_FILTER_PLAYER_STATS, "Load Basic value of player %s is: ", m_name.c_str()); - outDebugStatsValues(); - - // Need to call it to initialize m_team (m_team can be calculated from race) - // Other way is to saves m_team into characters table. - setFactionForRace(getRace()); - SetCharm(NULL); - - // load home bind and check in same time class/race pair, it used later for restore broken positions - if (!_LoadHomeBind(holder->GetResult(PLAYER_LOGIN_QUERY_LOADHOMEBIND))) - { - delete result; - return false; - } - - InitPrimaryProfessions(); // to max set before any spell loaded - - // init saved position, and fix it later if problematic - uint32 transGUID = fields[31].GetUInt32(); - Relocate(fields[12].GetFloat(), fields[13].GetFloat(), fields[14].GetFloat(), fields[16].GetFloat()); - SetLocationMapId(fields[15].GetUInt32()); - - uint32 difficulty = fields[39].GetUInt32(); - if (difficulty >= MAX_DUNGEON_DIFFICULTY || getLevel() < LEVELREQUIREMENT_HEROIC) - difficulty = DUNGEON_DIFFICULTY_NORMAL; - SetDungeonDifficulty(Difficulty(difficulty)); // may be changed in _LoadGroup - - _LoadGroup(holder->GetResult(PLAYER_LOGIN_QUERY_LOADGROUP)); - - _LoadArenaTeamInfo(holder->GetResult(PLAYER_LOGIN_QUERY_LOADARENAINFO)); - - // check arena teams integrity - for (uint32 arena_slot = 0; arena_slot < MAX_ARENA_SLOT; ++arena_slot) - { - uint32 arena_team_id = GetArenaTeamId(arena_slot); - if (!arena_team_id) - continue; - - if (ArenaTeam* at = sObjectMgr.GetArenaTeamById(arena_team_id)) - if (at->HaveMember(GetObjectGuid())) - continue; - - // arena team not exist or not member, cleanup fields - for (int j = 0; j < ARENA_TEAM_END; ++j) - SetArenaTeamInfoField(arena_slot, ArenaTeamInfoType(j), 0); - } - - SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS, fields[40].GetUInt32()); - SetUInt16Value(PLAYER_FIELD_KILLS, 0, fields[41].GetUInt16()); // today - SetUInt16Value(PLAYER_FIELD_KILLS, 1, fields[42].GetUInt16()); // yesterday - - _LoadBoundInstances(holder->GetResult(PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES)); - - if (!IsPositionValid()) - { - sLog.outError("%s have invalid coordinates (X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.", - guid.GetString().c_str(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation()); - RelocateToHomebind(); - - transGUID = 0; - - m_movementInfo.ClearTransportData(); - } - - _LoadBGData(holder->GetResult(PLAYER_LOGIN_QUERY_LOADBGDATA)); - - if (m_bgData.bgInstanceID) // saved in BattleGround - { - BattleGround* currentBg = sBattleGroundMgr.GetBattleGround(m_bgData.bgInstanceID, BATTLEGROUND_TYPE_NONE); - - bool player_at_bg = currentBg && currentBg->IsPlayerInBattleGround(GetObjectGuid()); - - if (player_at_bg && currentBg->GetStatus() != STATUS_WAIT_LEAVE) - { - BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(currentBg->GetTypeID(), currentBg->GetArenaType()); - AddBattleGroundQueueId(bgQueueTypeId); - - m_bgData.bgTypeID = currentBg->GetTypeID(); // bg data not marked as modified - - // join player to battleground group - currentBg->EventPlayerLoggedIn(this, GetObjectGuid()); - currentBg->AddOrSetPlayerToCorrectBgGroup(this, GetObjectGuid(), m_bgData.bgTeam); - - SetInviteForBattleGroundQueueType(bgQueueTypeId, currentBg->GetInstanceID()); - } - else - { - // leave bg - if (player_at_bg) - currentBg->RemovePlayerAtLeave(GetObjectGuid(), false, true); - - // move to bg enter point - const WorldLocation& _loc = GetBattleGroundEntryPoint(); - SetLocationMapId(_loc.mapid); - Relocate(_loc.coord_x, _loc.coord_y, _loc.coord_z, _loc.orientation); - - // We are not in BG anymore - SetBattleGroundId(0, BATTLEGROUND_TYPE_NONE); - // remove outdated DB data in DB - _SaveBGData(); - } - } - else - { - MapEntry const* mapEntry = sMapStore.LookupEntry(GetMapId()); - // if server restart after player save in BG or area - // player can have current coordinates in to BG/Arena map, fix this - if (!mapEntry || mapEntry->IsBattleGroundOrArena()) - { - const WorldLocation& _loc = GetBattleGroundEntryPoint(); - SetLocationMapId(_loc.mapid); - Relocate(_loc.coord_x, _loc.coord_y, _loc.coord_z, _loc.orientation); - - // We are not in BG anymore - SetBattleGroundId(0, BATTLEGROUND_TYPE_NONE); - // remove outdated DB data in DB - _SaveBGData(); - } - } - - if (transGUID != 0) - { - m_movementInfo.SetTransportData(ObjectGuid(HIGHGUID_MO_TRANSPORT, transGUID), fields[27].GetFloat(), fields[28].GetFloat(), fields[29].GetFloat(), fields[30].GetFloat(), 0, -1); - - if (!MaNGOS::IsValidMapCoord( - GetPositionX() + m_movementInfo.GetTransportPos()->x, GetPositionY() + m_movementInfo.GetTransportPos()->y, - GetPositionZ() + m_movementInfo.GetTransportPos()->z, GetOrientation() + m_movementInfo.GetTransportPos()->o) || - // transport size limited - m_movementInfo.GetTransportPos()->x > 50 || m_movementInfo.GetTransportPos()->y > 50 || m_movementInfo.GetTransportPos()->z > 50) - { - sLog.outError("%s have invalid transport coordinates (X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.", - guid.GetString().c_str(), GetPositionX() + m_movementInfo.GetTransportPos()->x, GetPositionY() + m_movementInfo.GetTransportPos()->y, - GetPositionZ() + m_movementInfo.GetTransportPos()->z, GetOrientation() + m_movementInfo.GetTransportPos()->o); - - RelocateToHomebind(); - - m_movementInfo.ClearTransportData(); - - transGUID = 0; - } - } - - if (transGUID != 0) - { - for (MapManager::TransportSet::const_iterator iter = sMapMgr.m_Transports.begin(); iter != sMapMgr.m_Transports.end(); ++iter) - { - if ((*iter)->GetGUIDLow() == transGUID) - { - MapEntry const* transMapEntry = sMapStore.LookupEntry((*iter)->GetMapId()); - // client without expansion support - if (GetSession()->Expansion() < transMapEntry->Expansion()) - { - DEBUG_LOG("Player %s using client without required expansion tried login at transport at non accessible map %u", GetName(), (*iter)->GetMapId()); - break; - } - - m_transport = *iter; - m_transport->AddPassenger(this); - SetLocationMapId(m_transport->GetMapId()); - break; - } - } - - if (!m_transport) - { - sLog.outError("%s have problems with transport guid (%u). Teleport to default race/class locations.", - guid.GetString().c_str(), transGUID); - - RelocateToHomebind(); - - m_movementInfo.ClearTransportData(); - - transGUID = 0; - } - } - else // not transport case - { - MapEntry const* mapEntry = sMapStore.LookupEntry(GetMapId()); - // client without expansion support - if (GetSession()->Expansion() < mapEntry->Expansion()) - { - DEBUG_LOG("Player %s using client without required expansion tried login at non accessible map %u", GetName(), GetMapId()); - RelocateToHomebind(); - } - } - - // player bounded instance saves loaded in _LoadBoundInstances, group versions at group loading - DungeonPersistentState* state = GetBoundInstanceSaveForSelfOrGroup(GetMapId()); - - // load the player's map here if it's not already loaded - SetMap(sMapMgr.CreateMap(GetMapId(), this)); - - // if the player is in an instance and it has been reset in the meantime teleport him to the entrance - if (GetInstanceId() && !state) - { - AreaTrigger const* at = sObjectMgr.GetMapEntranceTrigger(GetMapId()); - if (at) - Relocate(at->target_X, at->target_Y, at->target_Z, at->target_Orientation); - else - sLog.outError("Player %s(GUID: %u) logged in to a reset instance (map: %u) and there is no area-trigger leading to this map. Thus he can't be ported back to the entrance. This _might_ be an exploit attempt.", GetName(), GetGUIDLow(), GetMapId()); - } - - SaveRecallPosition(); - - time_t now = time(NULL); - time_t logoutTime = time_t(fields[22].GetUInt64()); - - // since last logout (in seconds) - uint32 time_diff = uint32(now - logoutTime); - - // set value, including drunk invisibility detection - // calculate sobering. after 15 minutes logged out, the player will be sober again - uint8 newDrunkValue = 0; - if (time_diff < uint32(GetDrunkValue()) * 9) - newDrunkValue = GetDrunkValue() - time_diff / 9; - - SetDrunkValue(newDrunkValue); - - m_cinematic = fields[18].GetUInt32(); - m_Played_time[PLAYED_TIME_TOTAL] = fields[19].GetUInt32(); - m_Played_time[PLAYED_TIME_LEVEL] = fields[20].GetUInt32(); - - m_resetTalentsCost = fields[24].GetUInt32(); - m_resetTalentsTime = time_t(fields[25].GetUInt64()); - - // reserve some flags - uint32 old_safe_flags = GetUInt32Value(PLAYER_FLAGS) & (PLAYER_FLAGS_HIDE_CLOAK | PLAYER_FLAGS_HIDE_HELM); - - if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM)) - SetUInt32Value(PLAYER_FLAGS, 0 | old_safe_flags); - - m_taxi.LoadTaxiMask(fields[17].GetString()); // must be before InitTaxiNodesForLevel - - uint32 extraflags = fields[32].GetUInt32(); - - m_stableSlots = fields[33].GetUInt32(); - if (m_stableSlots > MAX_PET_STABLES) - { - sLog.outError("Player can have not more %u stable slots, but have in DB %u", MAX_PET_STABLES, uint32(m_stableSlots)); - m_stableSlots = MAX_PET_STABLES; - } - - m_atLoginFlags = fields[34].GetUInt32(); - - // Update Honor kills data - m_lastHonorKillsUpdateTime = logoutTime; - UpdateHonorKills(); - - m_deathExpireTime = (time_t)fields[37].GetUInt64(); - if (m_deathExpireTime > now + MAX_DEATH_COUNT * DEATH_EXPIRE_STEP) - m_deathExpireTime = now + MAX_DEATH_COUNT * DEATH_EXPIRE_STEP - 1; - - std::string taxi_nodes = fields[38].GetCppString(); - - // clear channel spell data (if saved at channel spell casting) - SetChannelObjectGuid(ObjectGuid()); - SetUInt32Value(UNIT_CHANNEL_SPELL, 0); - - // clear charm/summon related fields - SetCharm(NULL); - SetPet(NULL); - SetTargetGuid(ObjectGuid()); - SetCharmerGuid(ObjectGuid()); - SetOwnerGuid(ObjectGuid()); - SetCreatorGuid(ObjectGuid()); - - // reset some aura modifiers before aura apply - - SetGuidValue(PLAYER_FARSIGHT, ObjectGuid()); - SetUInt32Value(PLAYER_TRACK_CREATURES, 0); - SetUInt32Value(PLAYER_TRACK_RESOURCES, 0); - - // cleanup aura list explicitly before skill load where some spells can be applied - RemoveAllAuras(); - - // make sure the unit is considered out of combat for proper loading - ClearInCombat(); - - // make sure the unit is considered not in duel for proper loading - SetGuidValue(PLAYER_DUEL_ARBITER, ObjectGuid()); - SetUInt32Value(PLAYER_DUEL_TEAM, 0); - - m_specsCount = fields[52].GetUInt8(); - m_activeSpec = fields[53].GetUInt8(); - - Tokens talentTrees = StrSplit(fields[26].GetString(), " "); - for (uint8 i = 0; i < MAX_TALENT_SPEC_COUNT; ++i) - { - if (i >= talentTrees.size()) - break; - - uint32 talentTree = atol(talentTrees[i].c_str()); - if (!talentTree || sTalentTabStore.LookupEntry(talentTree)) - m_talentsPrimaryTree[i] = talentTree; - else if (i == m_activeSpec) - SetAtLoginFlag(AT_LOGIN_RESET_TALENTS); // invalid tree, reset talents - } - - // reset stats before loading any modifiers - InitStatsForLevel(); - InitGlyphsForLevel(); - InitTaxiNodesForLevel(); - InitRunes(); - - // rest bonus can only be calculated after InitStatsForLevel() - m_rest_bonus = fields[21].GetFloat(); - - if (time_diff > 0) - { - // speed collect rest bonus in offline, in logout, far from tavern, city (section/in hour) - float bubble0 = 0.031f; - // speed collect rest bonus in offline, in logout, in tavern, city (section/in hour) - float bubble1 = 0.125f; - float bubble = fields[23].GetUInt32() > 0 - ? bubble1 * sWorld.getConfig(CONFIG_FLOAT_RATE_REST_OFFLINE_IN_TAVERN_OR_CITY) - : bubble0 * sWorld.getConfig(CONFIG_FLOAT_RATE_REST_OFFLINE_IN_WILDERNESS); - - SetRestBonus(GetRestBonus() + time_diff * ((float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP) / 72000)*bubble); - } - - // load skills after InitStatsForLevel because it triggering aura apply also - _LoadSkills(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSKILLS)); - - // apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods() - - // Mail - _LoadMails(holder->GetResult(PLAYER_LOGIN_QUERY_LOADMAILS)); - _LoadMailedItems(holder->GetResult(PLAYER_LOGIN_QUERY_LOADMAILEDITEMS)); - UpdateNextMailTimeAndUnreads(); - - _LoadGlyphs(holder->GetResult(PLAYER_LOGIN_QUERY_LOADGLYPHS)); - - _LoadAuras(holder->GetResult(PLAYER_LOGIN_QUERY_LOADAURAS), time_diff); - ApplyGlyphs(true); - - // add ghost flag (must be after aura load: PLAYER_FLAGS_GHOST set in aura) - if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) - m_deathState = DEAD; - - _LoadCurrencies(holder->GetResult(PLAYER_LOGIN_QUERY_LOADCURRENCIES)); - _LoadSpells(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSPELLS)); - - // after spell load, learn rewarded spell if need also - _LoadQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADQUESTSTATUS)); - _LoadDailyQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS)); - _LoadWeeklyQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADWEEKLYQUESTSTATUS)); - _LoadMonthlyQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADMONTHLYQUESTSTATUS)); - - _LoadTalents(holder->GetResult(PLAYER_LOGIN_QUERY_LOADTALENTS)); - - // after spell and quest load - InitTalentForLevel(); - learnDefaultSpells(); - - // must be before inventory (some items required reputation check) - m_reputationMgr.LoadFromDB(holder->GetResult(PLAYER_LOGIN_QUERY_LOADREPUTATION)); - - _LoadInventory(holder->GetResult(PLAYER_LOGIN_QUERY_LOADINVENTORY), time_diff); - _LoadItemLoot(holder->GetResult(PLAYER_LOGIN_QUERY_LOADITEMLOOT)); - - // update items with duration and realtime - UpdateItemDuration(time_diff, true); - - _LoadActions(holder->GetResult(PLAYER_LOGIN_QUERY_LOADACTIONS)); - - m_social = sSocialMgr.LoadFromDB(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSOCIALLIST), GetObjectGuid()); - - // check PLAYER_CHOSEN_TITLE compatibility with PLAYER__FIELD_KNOWN_TITLES - // note: PLAYER__FIELD_KNOWN_TITLES updated at quest status loaded - uint32 curTitle = fields[43].GetUInt32(); - if (curTitle && !HasTitle(curTitle)) - curTitle = 0; - - SetUInt32Value(PLAYER_CHOSEN_TITLE, curTitle); - - // Not finish taxi flight path - if (m_bgData.HasTaxiPath()) - { - m_taxi.ClearTaxiDestinations(); - for (int i = 0; i < 2; ++i) - m_taxi.AddTaxiDestination(m_bgData.taxiPath[i]); - } - else if (!m_taxi.LoadTaxiDestinationsFromString(taxi_nodes, GetTeam())) - { - // problems with taxi path loading - TaxiNodesEntry const* nodeEntry = NULL; - if (uint32 node_id = m_taxi.GetTaxiSource()) - nodeEntry = sTaxiNodesStore.LookupEntry(node_id); - - if (!nodeEntry) // don't know taxi start node, to homebind - { - sLog.outError("Character %u have wrong data in taxi destination list, teleport to homebind.", GetGUIDLow()); - RelocateToHomebind(); - } - else // have start node, to it - { - sLog.outError("Character %u have too short taxi destination list, teleport to original node.", GetGUIDLow()); - SetLocationMapId(nodeEntry->map_id); - Relocate(nodeEntry->x, nodeEntry->y, nodeEntry->z, 0.0f); - } - - // we can be relocated from taxi and still have an outdated Map pointer! - // so we need to get a new Map pointer! - SetMap(sMapMgr.CreateMap(GetMapId(), this)); - SaveRecallPosition(); // save as recall also to prevent recall and fall from sky - - m_taxi.ClearTaxiDestinations(); - } - - if (uint32 node_id = m_taxi.GetTaxiSource()) - { - // save source node as recall coord to prevent recall and fall from sky - TaxiNodesEntry const* nodeEntry = sTaxiNodesStore.LookupEntry(node_id); - MANGOS_ASSERT(nodeEntry); // checked in m_taxi.LoadTaxiDestinationsFromString - m_recallMap = nodeEntry->map_id; - m_recallX = nodeEntry->x; - m_recallY = nodeEntry->y; - m_recallZ = nodeEntry->z; - - // flight will started later - } - - // has to be called after last Relocate() in Player::LoadFromDB - SetFallInformation(0, GetPositionZ()); - - _LoadSpellCooldowns(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS)); - - // Spell code allow apply any auras to dead character in load time in aura/spell/item loading - // Do now before stats re-calculation cleanup for ghost state unexpected auras - if (!isAlive()) - RemoveAllAurasOnDeath(); - - // apply all stat bonuses from items and auras - SetCanModifyStats(true); - UpdateAllStats(); - - // restore remembered power/health values (but not more max values) - uint32 savedhealth = fields[46].GetUInt32(); - SetHealth(savedhealth > GetMaxHealth() ? GetMaxHealth() : savedhealth); - - COMPILE_ASSERT(MAX_STORED_POWERS == 5, "Query not updated."); - for (uint32 i = 0; i < MAX_STORED_POWERS; ++i) - { - uint32 savedpower = fields[47 + i].GetUInt32(); - SetPowerByIndex(i, std::min(savedpower, GetMaxPowerByIndex(i))); - } - - DEBUG_FILTER_LOG(LOG_FILTER_PLAYER_STATS, "The value of player %s after load item and aura is: ", m_name.c_str()); - outDebugStatsValues(); - - // all fields read - delete result; - - // GM state - if (GetSession()->GetSecurity() > SEC_PLAYER) - { - switch (sWorld.getConfig(CONFIG_UINT32_GM_LOGIN_STATE)) - { - default: - case 0: break; // disable - case 1: SetGameMaster(true); break; // enable - case 2: // save state - if (extraflags & PLAYER_EXTRA_GM_ON) - SetGameMaster(true); - break; - } - - switch (sWorld.getConfig(CONFIG_UINT32_GM_VISIBLE_STATE)) - { - default: - case 0: SetGMVisible(false); break; // invisible - case 1: break; // visible - case 2: // save state - if (extraflags & PLAYER_EXTRA_GM_INVISIBLE) - SetGMVisible(false); - break; - } - - switch (sWorld.getConfig(CONFIG_UINT32_GM_ACCEPT_TICKETS)) - { - default: - case 0: break; // disable - case 1: SetAcceptTicket(true); break; // enable - case 2: // save state - if (extraflags & PLAYER_EXTRA_GM_ACCEPT_TICKETS) - SetAcceptTicket(true); - break; - } - - switch (sWorld.getConfig(CONFIG_UINT32_GM_CHAT)) - { - default: - case 0: break; // disable - case 1: SetGMChat(true); break; // enable - case 2: // save state - if (extraflags & PLAYER_EXTRA_GM_CHAT) - SetGMChat(true); - break; - } - - switch (sWorld.getConfig(CONFIG_UINT32_GM_WISPERING_TO)) - { - default: - case 0: break; // disable - case 1: SetAcceptWhispers(true); break; // enable - case 2: // save state - if (extraflags & PLAYER_EXTRA_ACCEPT_WHISPERS) - SetAcceptWhispers(true); - break; - } - } - - _LoadDeclinedNames(holder->GetResult(PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES)); - - m_achievementMgr.CheckAllAchievementCriteria(); - - _LoadEquipmentSets(holder->GetResult(PLAYER_LOGIN_QUERY_LOADEQUIPMENTSETS)); - - return true; -} - -bool Player::isAllowedToLoot(Creature* creature) -{ - // never tapped by any (mob solo kill) - if (!creature->HasFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_TAPPED)) - return false; - - if (Player* recipient = creature->GetLootRecipient()) - { - if (recipient == this) - return true; - - if (Group* otherGroup = recipient->GetGroup()) - { - Group* thisGroup = GetGroup(); - if (!thisGroup) - return false; - - return thisGroup == otherGroup; - } - return false; - } - else - // prevent other players from looting if the recipient got disconnected - return !creature->HasLootRecipient(); -} - -void Player::_LoadActions(QueryResult* result) -{ - for (int i = 0; i < MAX_TALENT_SPEC_COUNT; ++i) - m_actionButtons[i].clear(); - - // QueryResult *result = CharacterDatabase.PQuery("SELECT spec, button,action,type FROM character_action WHERE guid = '%u' ORDER BY button",GetGUIDLow()); - - if (result) - { - do - { - Field* fields = result->Fetch(); - - uint8 spec = fields[0].GetUInt8(); - uint8 button = fields[1].GetUInt8(); - uint32 action = fields[2].GetUInt32(); - uint8 type = fields[3].GetUInt8(); - - if (ActionButton* ab = addActionButton(spec, button, action, type)) - ab->uState = ACTIONBUTTON_UNCHANGED; - else - { - sLog.outError(" ...at loading, and will deleted in DB also"); - - // Will deleted in DB at next save (it can create data until save but marked as deleted) - m_actionButtons[spec][button].uState = ACTIONBUTTON_DELETED; - } - } - while (result->NextRow()); - - delete result; - } -} - -void Player::_LoadAuras(QueryResult* result, uint32 timediff) -{ - // RemoveAllAuras(); -- some spells casted before aura load, for example in LoadSkills, aura list explicitly cleaned early - - // QueryResult *result = CharacterDatabase.PQuery("SELECT caster_guid,item_guid,spell,stackcount,remaincharges,basepoints0,basepoints1,basepoints2,periodictime0,periodictime1,periodictime2,maxduration,remaintime,effIndexMask FROM character_aura WHERE guid = '%u'",GetGUIDLow()); - - if (result) - { - do - { - Field* fields = result->Fetch(); - ObjectGuid caster_guid = ObjectGuid(fields[0].GetUInt64()); - uint32 item_lowguid = fields[1].GetUInt32(); - uint32 spellid = fields[2].GetUInt32(); - uint32 stackcount = fields[3].GetUInt32(); - uint32 remaincharges = fields[4].GetUInt32(); - int32 damage[MAX_EFFECT_INDEX]; - uint32 periodicTime[MAX_EFFECT_INDEX]; - - for (int32 i = 0; i < MAX_EFFECT_INDEX; ++i) - { - damage[i] = fields[i + 5].GetInt32(); - periodicTime[i] = fields[i + 8].GetUInt32(); - } - - int32 maxduration = fields[11].GetInt32(); - int32 remaintime = fields[12].GetInt32(); - uint32 effIndexMask = fields[13].GetUInt32(); - - SpellEntry const* spellproto = sSpellStore.LookupEntry(spellid); - if (!spellproto) - { - sLog.outError("Unknown spell (spellid %u), ignore.", spellid); - continue; - } - - if (remaintime != -1 && !IsPositiveSpell(spellproto)) - { - if (remaintime / IN_MILLISECONDS <= int32(timediff)) - continue; - - remaintime -= timediff * IN_MILLISECONDS; - } - - // prevent wrong values of remaincharges - if (uint32 procCharges = spellproto->GetProcCharges()) - { - if (remaincharges <= 0 || remaincharges > procCharges) - remaincharges = procCharges; - } - else - remaincharges = 0; - - uint32 defstackamount = spellproto->GetStackAmount(); - if (!defstackamount) - stackcount = 1; - else if (defstackamount < stackcount) - stackcount = defstackamount; - else if (!stackcount) - stackcount = 1; - - SpellAuraHolder* holder = CreateSpellAuraHolder(spellproto, this, NULL); - holder->SetLoadedState(caster_guid, ObjectGuid(HIGHGUID_ITEM, item_lowguid), stackcount, remaincharges, maxduration, remaintime); - - for (int32 i = 0; i < MAX_EFFECT_INDEX; ++i) - { - if ((effIndexMask & (1 << i)) == 0) - continue; - - Aura* aura = CreateAura(spellproto, SpellEffectIndex(i), NULL, holder, this); - if (!damage[i]) - damage[i] = aura->GetModifier()->m_amount; - - aura->SetLoadedState(damage[i], periodicTime[i]); - holder->AddAura(aura, SpellEffectIndex(i)); - } - - if (!holder->IsEmptyHolder()) - { - // reset stolen single target auras - if (caster_guid != GetObjectGuid() && holder->GetTrackedAuraType() == TRACK_AURA_TYPE_SINGLE_TARGET) - holder->SetTrackedAuraType(TRACK_AURA_TYPE_NOT_TRACKED); - - AddSpellAuraHolder(holder); - DETAIL_LOG("Added auras from spellid %u", spellproto->Id); - } - else - delete holder; - } - while (result->NextRow()); - delete result; - } - - if (getClass() == CLASS_WARRIOR && !HasAuraType(SPELL_AURA_MOD_SHAPESHIFT)) - CastSpell(this, SPELL_ID_PASSIVE_BATTLE_STANCE, true); -} - -void Player::_LoadGlyphs(QueryResult* result) -{ - if (!result) - return; - - // 0 1 2 - // "SELECT spec, slot, glyph FROM character_glyphs WHERE guid='%u'" - - do - { - Field* fields = result->Fetch(); - uint8 spec = fields[0].GetUInt8(); - uint8 slot = fields[1].GetUInt8(); - uint32 glyph = fields[2].GetUInt32(); - - GlyphPropertiesEntry const* gp = sGlyphPropertiesStore.LookupEntry(glyph); - if (!gp) - { - sLog.outError("Player %s has not existing glyph entry %u on index %u, spec %u", m_name.c_str(), glyph, slot, spec); - CharacterDatabase.PExecute("DELETE FROM character_glyphs WHERE glyph = %u", glyph); - continue; - } - - GlyphSlotEntry const* gs = sGlyphSlotStore.LookupEntry(GetGlyphSlot(slot)); - if (!gs) - { - sLog.outError("Player %s has not existing glyph slot entry %u on index %u, spec %u", m_name.c_str(), GetGlyphSlot(slot), slot, spec); - CharacterDatabase.PExecute("DELETE FROM character_glyphs WHERE slot = %u AND spec = %u AND guid = %u", slot, spec, GetGUIDLow()); - continue; - } - - if (gp->TypeFlags != gs->TypeFlags) - { - sLog.outError("Player %s has glyph with typeflags %u in slot with typeflags %u, removing.", m_name.c_str(), gp->TypeFlags, gs->TypeFlags); - CharacterDatabase.PExecute("DELETE FROM character_glyphs WHERE slot = %u AND spec = %u AND guid = %u", slot, spec, GetGUIDLow()); - continue; - } - - m_glyphs[spec][slot].id = glyph; - } - while (result->NextRow()); - - delete result; -} - -void Player::LoadCorpse() -{ - if (isAlive()) - { - sObjectAccessor.ConvertCorpseForPlayer(GetObjectGuid()); - } - else - { - if (Corpse* corpse = GetCorpse()) - { - ApplyModByteFlag(PLAYER_FIELD_BYTES, 0, PLAYER_FIELD_BYTE_RELEASE_TIMER, corpse && !sMapStore.LookupEntry(corpse->GetMapId())->Instanceable()); - } - else - { - // Prevent Dead Player login without corpse - ResurrectPlayer(0.5f); - } - } -} - -void Player::_LoadInventory(QueryResult* result, uint32 timediff) -{ - // QueryResult *result = CharacterDatabase.PQuery("SELECT data,text,bag,slot,item,item_template FROM character_inventory JOIN item_instance ON character_inventory.item = item_instance.guid WHERE character_inventory.guid = '%u' ORDER BY bag,slot", GetGUIDLow()); - std::map bagMap; // fast guid lookup for bags - // NOTE: the "order by `bag`" is important because it makes sure - // the bagMap is filled before items in the bags are loaded - // NOTE2: the "order by `slot`" is needed because mainhand weapons are (wrongly?) - // expected to be equipped before offhand items (TODO: fixme) - - uint32 zone = GetZoneId(); - - if (result) - { - std::list problematicItems; - - // prevent items from being added to the queue when stored - m_itemUpdateQueueBlocked = true; - do - { - Field* fields = result->Fetch(); - uint32 bag_guid = fields[2].GetUInt32(); - uint8 slot = fields[3].GetUInt8(); - uint32 item_lowguid = fields[4].GetUInt32(); - uint32 item_id = fields[5].GetUInt32(); - - ItemPrototype const* proto = ObjectMgr::GetItemPrototype(item_id); - - if (!proto) - { - CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_lowguid); - CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item_lowguid); - sLog.outError("Player::_LoadInventory: Player %s has an unknown item (id: #%u) in inventory, deleted.", GetName(), item_id); - continue; - } - - Item* item = NewItemOrBag(proto); - - if (!item->LoadFromDB(item_lowguid, fields, GetObjectGuid())) - { - sLog.outError("Player::_LoadInventory: Player %s has broken item (id: #%u) in inventory, deleted.", GetName(), item_id); - CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_lowguid); - item->FSetState(ITEM_REMOVED); - item->SaveToDB(); // it also deletes item object ! - continue; - } - - // not allow have in alive state item limited to another map/zone - if (isAlive() && item->IsLimitedToAnotherMapOrZone(GetMapId(), zone)) - { - CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_lowguid); - item->FSetState(ITEM_REMOVED); - item->SaveToDB(); // it also deletes item object ! - continue; - } - - // "Conjured items disappear if you are logged out for more than 15 minutes" - if (timediff > 15 * MINUTE && (item->GetProto()->Flags & ITEM_FLAG_CONJURED)) - { - CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_lowguid); - item->FSetState(ITEM_REMOVED); - item->SaveToDB(); // it also deletes item object ! - continue; - } - - bool success = true; - - // the item/bag is not in a bag - if (!bag_guid) - { - item->SetContainer(NULL); - item->SetSlot(slot); - - if (IsInventoryPos(INVENTORY_SLOT_BAG_0, slot)) - { - ItemPosCountVec dest; - if (CanStoreItem(INVENTORY_SLOT_BAG_0, slot, dest, item, false) == EQUIP_ERR_OK) - item = StoreItem(dest, item, true); - else - success = false; - } - else if (IsEquipmentPos(INVENTORY_SLOT_BAG_0, slot)) - { - uint16 dest; - if (CanEquipItem(slot, dest, item, false, false) == EQUIP_ERR_OK) - QuickEquipItem(dest, item); - else - success = false; - } - else if (IsBankPos(INVENTORY_SLOT_BAG_0, slot)) - { - ItemPosCountVec dest; - if (CanBankItem(INVENTORY_SLOT_BAG_0, slot, dest, item, false, false) == EQUIP_ERR_OK) - item = BankItem(dest, item, true); - else - success = false; - } - - if (success) - { - // store bags that may contain items in them - if (item->IsBag() && IsBagPos(item->GetPos())) - bagMap[item_lowguid] = (Bag*)item; - } - } - // the item/bag in a bag - else - { - item->SetSlot(NULL_SLOT); - // the item is in a bag, find the bag - std::map::const_iterator itr = bagMap.find(bag_guid); - if (itr != bagMap.end() && slot < itr->second->GetBagSize()) - { - ItemPosCountVec dest; - if (CanStoreItem(itr->second->GetSlot(), slot, dest, item, false) == EQUIP_ERR_OK) - item = StoreItem(dest, item, true); - else - success = false; - } - else - success = false; - } - - // item's state may have changed after stored - if (success) - { - item->SetState(ITEM_UNCHANGED, this); - - // restore container unchanged state also - if (item->GetContainer()) - item->GetContainer()->SetState(ITEM_UNCHANGED, this); - - // recharged mana gem - if (timediff > 15 * MINUTE && proto->ItemLimitCategory == ITEM_LIMIT_CATEGORY_MANA_GEM) - item->RestoreCharges(); - } - else - { - sLog.outError("Player::_LoadInventory: Player %s has item (GUID: %u Entry: %u) can't be loaded to inventory (Bag GUID: %u Slot: %u) by some reason, will send by mail.", GetName(), item_lowguid, item_id, bag_guid, slot); - CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_lowguid); - problematicItems.push_back(item); - } - } - while (result->NextRow()); - - delete result; - m_itemUpdateQueueBlocked = false; - - // send by mail problematic items - while (!problematicItems.empty()) - { - std::string subject = GetSession()->GetMangosString(LANG_NOT_EQUIPPED_ITEM); - - // fill mail - MailDraft draft(subject, "There's were problems with equipping item(s)."); - - for (int i = 0; !problematicItems.empty() && i < MAX_MAIL_ITEMS; ++i) - { - Item* item = problematicItems.front(); - problematicItems.pop_front(); - - draft.AddItem(item); - } - - draft.SendMailTo(this, MailSender(this, MAIL_STATIONERY_GM), MAIL_CHECK_MASK_COPIED); - } - } - - // if(isAlive()) - _ApplyAllItemMods(); -} - -void Player::_LoadItemLoot(QueryResult* result) -{ - // QueryResult *result = CharacterDatabase.PQuery("SELECT guid,itemid,amount,suffix,property FROM item_loot WHERE guid = '%u'", GetGUIDLow()); - - if (result) - { - do - { - Field* fields = result->Fetch(); - uint32 item_guid = fields[0].GetUInt32(); - - Item* item = GetItemByGuid(ObjectGuid(HIGHGUID_ITEM, item_guid)); - - if (!item) - { - CharacterDatabase.PExecute("DELETE FROM item_loot WHERE guid = '%u'", item_guid); - sLog.outError("Player::_LoadItemLoot: Player %s has loot for nonexistent item (GUID: %u) in `item_loot`, deleted.", GetName(), item_guid); - continue; - } - - item->LoadLootFromDB(fields); - } - while (result->NextRow()); - - delete result; - } -} - -// load mailed item which should receive current player -void Player::_LoadMailedItems(QueryResult* result) -{ - // data needs to be at first place for Item::LoadFromDB - // 0 1 2 3 4 - // "SELECT data, text, mail_id, item_guid, item_template FROM mail_items JOIN item_instance ON item_guid = guid WHERE receiver = '%u'", GUID_LOPART(m_guid) - if (!result) - return; - - do - { - Field* fields = result->Fetch(); - uint32 mail_id = fields[2].GetUInt32(); - uint32 item_guid_low = fields[3].GetUInt32(); - uint32 item_template = fields[4].GetUInt32(); - - Mail* mail = GetMail(mail_id); - if (!mail) - continue; - mail->AddItem(item_guid_low, item_template); - - ItemPrototype const* proto = ObjectMgr::GetItemPrototype(item_template); - - if (!proto) - { - sLog.outError("Player %u has unknown item_template (ProtoType) in mailed items(GUID: %u template: %u) in mail (%u), deleted.", GetGUIDLow(), item_guid_low, item_template, mail->messageID); - CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid = '%u'", item_guid_low); - CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item_guid_low); - continue; - } - - Item* item = NewItemOrBag(proto); - - if (!item->LoadFromDB(item_guid_low, fields, GetObjectGuid())) - { - sLog.outError("Player::_LoadMailedItems - Item in mail (%u) doesn't exist !!!! - item guid: %u, deleted from mail", mail->messageID, item_guid_low); - CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid = '%u'", item_guid_low); - item->FSetState(ITEM_REMOVED); - item->SaveToDB(); // it also deletes item object ! - continue; - } - - AddMItem(item); - } - while (result->NextRow()); - - delete result; -} - -void Player::_LoadMails(QueryResult* result) -{ - m_mail.clear(); - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 - //"SELECT id,messageType,sender,receiver,subject,body,expire_time,deliver_time,money,cod,checked,stationery,mailTemplateId,has_items FROM mail WHERE receiver = '%u' ORDER BY id DESC", GetGUIDLow() - if (!result) - return; - - do - { - Field* fields = result->Fetch(); - Mail* m = new Mail; - m->messageID = fields[0].GetUInt32(); - m->messageType = fields[1].GetUInt8(); - m->sender = fields[2].GetUInt32(); - m->receiverGuid = ObjectGuid(HIGHGUID_PLAYER, fields[3].GetUInt32()); - m->subject = fields[4].GetCppString(); - m->body = fields[5].GetCppString(); - m->expire_time = (time_t)fields[6].GetUInt64(); - m->deliver_time = (time_t)fields[7].GetUInt64(); - m->money = fields[8].GetUInt32(); - m->COD = fields[9].GetUInt32(); - m->checked = fields[10].GetUInt32(); - m->stationery = fields[11].GetUInt8(); - m->mailTemplateId = fields[12].GetInt16(); - m->has_items = fields[13].GetBool(); // true, if mail have items or mail have template and items generated (maybe none) - - if (m->mailTemplateId && !sMailTemplateStore.LookupEntry(m->mailTemplateId)) - { - sLog.outError("Player::_LoadMail - Mail (%u) have nonexistent MailTemplateId (%u), remove at load", m->messageID, m->mailTemplateId); - m->mailTemplateId = 0; - } - - m->state = MAIL_STATE_UNCHANGED; - - m_mail.push_back(m); - - if (m->mailTemplateId && !m->has_items) - m->prepareTemplateItems(this); - } - while (result->NextRow()); - delete result; -} - -void Player::LoadPet() -{ - // fixme: the pet should still be loaded if the player is not in world - // just not added to the map - if (IsInWorld()) - { - Pet* pet = new Pet; - if (!pet->LoadPetFromDB(this, 0, 0, true)) - delete pet; - } -} - -void Player::_LoadQuestStatus(QueryResult* result) -{ - mQuestStatus.clear(); - - uint32 slot = 0; - - //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 - // QueryResult *result = CharacterDatabase.PQuery("SELECT quest, status, rewarded, explored, timer, mobcount1, mobcount2, mobcount3, mobcount4, itemcount1, itemcount2, itemcount3, itemcount4, itemcount5, itemcount6 FROM character_queststatus WHERE guid = '%u'", GetGUIDLow()); - - if (result) - { - do - { - Field* fields = result->Fetch(); - - uint32 quest_id = fields[0].GetUInt32(); - // used to be new, no delete? - Quest const* pQuest = sObjectMgr.GetQuestTemplate(quest_id); - if (pQuest) - { - // find or create - QuestStatusData& questStatusData = mQuestStatus[quest_id]; - - uint32 qstatus = fields[1].GetUInt32(); - if (qstatus < MAX_QUEST_STATUS) - questStatusData.m_status = QuestStatus(qstatus); - else - { - questStatusData.m_status = QUEST_STATUS_NONE; - sLog.outError("Player %s have invalid quest %d status (%d), replaced by QUEST_STATUS_NONE(0).", GetName(), quest_id, qstatus); - } - - questStatusData.m_rewarded = (fields[2].GetUInt8() > 0); - questStatusData.m_explored = (fields[3].GetUInt8() > 0); - - time_t quest_time = time_t(fields[4].GetUInt64()); - - if (pQuest->HasSpecialFlag(QUEST_SPECIAL_FLAG_TIMED) && !GetQuestRewardStatus(quest_id) && questStatusData.m_status != QUEST_STATUS_NONE) - { - AddTimedQuest(quest_id); - - if (quest_time <= sWorld.GetGameTime()) - questStatusData.m_timer = 1; - else - questStatusData.m_timer = uint32(quest_time - sWorld.GetGameTime()) * IN_MILLISECONDS; - } - else - quest_time = 0; - - questStatusData.m_creatureOrGOcount[0] = fields[5].GetUInt32(); - questStatusData.m_creatureOrGOcount[1] = fields[6].GetUInt32(); - questStatusData.m_creatureOrGOcount[2] = fields[7].GetUInt32(); - questStatusData.m_creatureOrGOcount[3] = fields[8].GetUInt32(); - questStatusData.m_itemcount[0] = fields[9].GetUInt32(); - questStatusData.m_itemcount[1] = fields[10].GetUInt32(); - questStatusData.m_itemcount[2] = fields[11].GetUInt32(); - questStatusData.m_itemcount[3] = fields[12].GetUInt32(); - questStatusData.m_itemcount[4] = fields[13].GetUInt32(); - questStatusData.m_itemcount[5] = fields[14].GetUInt32(); - - questStatusData.uState = QUEST_UNCHANGED; - - // add to quest log - if (slot < MAX_QUEST_LOG_SIZE && - ((questStatusData.m_status == QUEST_STATUS_INCOMPLETE || - questStatusData.m_status == QUEST_STATUS_COMPLETE || - questStatusData.m_status == QUEST_STATUS_FAILED) && - (!questStatusData.m_rewarded || pQuest->IsRepeatable()))) - { - SetQuestSlot(slot, quest_id, uint32(quest_time)); - - if (questStatusData.m_explored) - SetQuestSlotState(slot, QUEST_STATE_COMPLETE); - - if (questStatusData.m_status == QUEST_STATUS_COMPLETE) - SetQuestSlotState(slot, QUEST_STATE_COMPLETE); - - if (questStatusData.m_status == QUEST_STATUS_FAILED) - SetQuestSlotState(slot, QUEST_STATE_FAIL); - - for (uint8 idx = 0; idx < QUEST_OBJECTIVES_COUNT; ++idx) - if (questStatusData.m_creatureOrGOcount[idx]) - SetQuestSlotCounter(slot, idx, questStatusData.m_creatureOrGOcount[idx]); - - ++slot; - } - - if (questStatusData.m_rewarded) - { - // learn rewarded spell if unknown - learnQuestRewardedSpells(pQuest); - - // set rewarded title if any - if (pQuest->GetCharTitleId()) - { - if (CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(pQuest->GetCharTitleId())) - SetTitle(titleEntry); - } - - if (pQuest->GetBonusTalents()) - m_questRewardTalentCount += pQuest->GetBonusTalents(); - } - - DEBUG_LOG("Quest status is {%u} for quest {%u} for player (GUID: %u)", questStatusData.m_status, quest_id, GetGUIDLow()); - } - } - while (result->NextRow()); - - delete result; - } - - // clear quest log tail - for (uint16 i = slot; i < MAX_QUEST_LOG_SIZE; ++i) - SetQuestSlot(i, 0); -} - -void Player::_LoadDailyQuestStatus(QueryResult* result) -{ - for (uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx) - SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1 + quest_daily_idx, 0); - - // QueryResult *result = CharacterDatabase.PQuery("SELECT quest FROM character_queststatus_daily WHERE guid = '%u'", GetGUIDLow()); - - if (result) - { - uint32 quest_daily_idx = 0; - - do - { - if (quest_daily_idx >= PLAYER_MAX_DAILY_QUESTS) // max amount with exist data in query - { - sLog.outError("Player (GUID: %u) have more 25 daily quest records in `charcter_queststatus_daily`", GetGUIDLow()); - break; - } - - Field* fields = result->Fetch(); - - uint32 quest_id = fields[0].GetUInt32(); - - Quest const* pQuest = sObjectMgr.GetQuestTemplate(quest_id); - if (!pQuest) - continue; - - SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1 + quest_daily_idx, quest_id); - ++quest_daily_idx; - - DEBUG_LOG("Daily quest {%u} cooldown for player (GUID: %u)", quest_id, GetGUIDLow()); - } - while (result->NextRow()); - - delete result; - } - - m_DailyQuestChanged = false; -} - -void Player::_LoadWeeklyQuestStatus(QueryResult* result) -{ - m_weeklyquests.clear(); - - // QueryResult *result = CharacterDatabase.PQuery("SELECT quest FROM character_queststatus_weekly WHERE guid = '%u'", GetGUIDLow()); - - if (result) - { - do - { - Field* fields = result->Fetch(); - - uint32 quest_id = fields[0].GetUInt32(); - - Quest const* pQuest = sObjectMgr.GetQuestTemplate(quest_id); - if (!pQuest) - continue; - - m_weeklyquests.insert(quest_id); - - DEBUG_LOG("Weekly quest {%u} cooldown for player (GUID: %u)", quest_id, GetGUIDLow()); - } - while (result->NextRow()); - - delete result; - } - m_WeeklyQuestChanged = false; -} - -void Player::_LoadMonthlyQuestStatus(QueryResult* result) -{ - m_monthlyquests.clear(); - - // QueryResult *result = CharacterDatabase.PQuery("SELECT quest FROM character_queststatus_weekly WHERE guid = '%u'", GetGUIDLow()); - - if (result) - { - do - { - Field* fields = result->Fetch(); - - uint32 quest_id = fields[0].GetUInt32(); - - Quest const* pQuest = sObjectMgr.GetQuestTemplate(quest_id); - if (!pQuest) - continue; - - m_monthlyquests.insert(quest_id); - - DEBUG_LOG("Monthly quest {%u} cooldown for player (GUID: %u)", quest_id, GetGUIDLow()); - } - while (result->NextRow()); - - delete result; - } - - m_MonthlyQuestChanged = false; -} - -void Player::_LoadSpells(QueryResult* result) -{ - // QueryResult *result = CharacterDatabase.PQuery("SELECT spell,active,disabled FROM character_spell WHERE guid = '%u'",GetGUIDLow()); - - if (result) - { - do - { - Field* fields = result->Fetch(); - - uint32 spell_id = fields[0].GetUInt32(); - - // skip talents & drop unneeded data - if (GetTalentSpellPos(spell_id)) - { - sLog.outError("Player::_LoadSpells: %s has talent spell %u in character_spell, removing it.", - GetGuidStr().c_str(), spell_id); - CharacterDatabase.PExecute("DELETE FROM character_spell WHERE spell = '%u'", spell_id); - continue; - } - - addSpell(spell_id, fields[1].GetBool(), false, false, fields[2].GetBool()); - } - while (result->NextRow()); - - delete result; - } -} - -void Player::_LoadTalents(QueryResult* result) -{ - // QueryResult *result = CharacterDatabase.PQuery("SELECT talent_id, current_rank, spec FROM character_talent WHERE guid = '%u'",GetGUIDLow()); - if (result) - { - do - { - Field* fields = result->Fetch(); - - uint32 talent_id = fields[0].GetUInt32(); - TalentEntry const* talentInfo = sTalentStore.LookupEntry(talent_id); - - if (!talentInfo) - { - sLog.outError("Player::_LoadTalents:Player (GUID: %u) has invalid talent_id: %u , this talent will be deleted from character_talent", GetGUIDLow(), talent_id); - CharacterDatabase.PExecute("DELETE FROM character_talent WHERE talent_id = '%u'", talent_id); - continue; - } - - TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab); - - if (!talentTabInfo) - { - sLog.outError("Player::_LoadTalents:Player (GUID: %u) has invalid talentTabInfo: %u for talentID: %u , this talent will be deleted from character_talent", GetGUIDLow(), talentInfo->TalentTab, talentInfo->TalentID); - CharacterDatabase.PExecute("DELETE FROM character_talent WHERE talent_id = '%u'", talent_id); - continue; - } - - // prevent load talent for different class (cheating) - if ((getClassMask() & talentTabInfo->ClassMask) == 0) - { - sLog.outError("Player::_LoadTalents:Player (GUID: %u) has talent with ClassMask: %u , but Player's ClassMask is: %u , talentID: %u , this talent will be deleted from character_talent", GetGUIDLow(), talentTabInfo->ClassMask, getClassMask() , talentInfo->TalentID); - CharacterDatabase.PExecute("DELETE FROM character_talent WHERE guid = '%u' AND talent_id = '%u'", GetGUIDLow(), talent_id); - continue; - } - - uint32 currentRank = fields[1].GetUInt32(); - - if (currentRank > MAX_TALENT_RANK || talentInfo->RankID[currentRank] == 0) - { - sLog.outError("Player::_LoadTalents:Player (GUID: %u) has invalid talent rank: %u , talentID: %u , this talent will be deleted from character_talent", GetGUIDLow(), currentRank, talentInfo->TalentID); - CharacterDatabase.PExecute("DELETE FROM character_talent WHERE guid = '%u' AND talent_id = '%u'", GetGUIDLow(), talent_id); - continue; - } - - uint32 spec = fields[2].GetUInt32(); - - if (spec > MAX_TALENT_SPEC_COUNT) - { - sLog.outError("Player::_LoadTalents:Player (GUID: %u) has invalid talent spec: %u, spec will be deleted from character_talent", GetGUIDLow(), spec); - CharacterDatabase.PExecute("DELETE FROM character_talent WHERE spec = '%u' ", spec); - continue; - } - - if (spec >= m_specsCount) - { - sLog.outError("Player::_LoadTalents:Player (GUID: %u) has invalid talent spec: %u , this spec will be deleted from character_talent.", GetGUIDLow(), spec); - CharacterDatabase.PExecute("DELETE FROM character_talent WHERE guid = '%u' AND spec = '%u' ", GetGUIDLow(), spec); - continue; - } - - if (m_activeSpec == spec) - addSpell(talentInfo->RankID[currentRank], true, false, false, false); - else - { - PlayerTalent talent; - talent.currentRank = currentRank; - talent.talentEntry = talentInfo; - talent.state = PLAYERSPELL_UNCHANGED; - m_talents[spec][talentInfo->TalentID] = talent; - } - } - while (result->NextRow()); - delete result; - } -} - -void Player::_LoadGroup(QueryResult* result) -{ - // QueryResult *result = CharacterDatabase.PQuery("SELECT groupId FROM group_member WHERE memberGuid='%u'", GetGUIDLow()); - if (result) - { - uint32 groupId = (*result)[0].GetUInt32(); - delete result; - - if (Group* group = sObjectMgr.GetGroupById(groupId)) - { - uint8 subgroup = group->GetMemberGroup(GetObjectGuid()); - SetGroup(group, subgroup); - if (getLevel() >= LEVELREQUIREMENT_HEROIC) - { - // the group leader may change the instance difficulty while the player is offline - SetDungeonDifficulty(group->GetDungeonDifficulty()); - SetRaidDifficulty(group->GetRaidDifficulty()); - } - } - } -} - -void Player::_LoadBoundInstances(QueryResult* result) -{ - for (uint8 i = 0; i < MAX_DIFFICULTY; ++i) - m_boundInstances[i].clear(); - - Group* group = GetGroup(); - - // QueryResult *result = CharacterDatabase.PQuery("SELECT id, permanent, map, difficulty, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = '%u'", GUID_LOPART(m_guid)); - if (result) - { - do - { - Field* fields = result->Fetch(); - bool perm = fields[1].GetBool(); - uint32 mapId = fields[2].GetUInt32(); - uint32 instanceId = fields[0].GetUInt32(); - uint8 difficulty = fields[3].GetUInt8(); - - time_t resetTime = (time_t)fields[4].GetUInt64(); - // the resettime for normal instances is only saved when the InstanceSave is unloaded - // so the value read from the DB may be wrong here but only if the InstanceSave is loaded - // and in that case it is not used - - MapEntry const* mapEntry = sMapStore.LookupEntry(mapId); - if (!mapEntry || !mapEntry->IsDungeon()) - { - sLog.outError("_LoadBoundInstances: player %s(%d) has bind to nonexistent or not dungeon map %d", GetName(), GetGUIDLow(), mapId); - CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%u' AND instance = '%u'", GetGUIDLow(), instanceId); - continue; - } - - if (difficulty >= MAX_DIFFICULTY) - { - sLog.outError("_LoadBoundInstances: player %s(%d) has bind to nonexistent difficulty %d instance for map %u", GetName(), GetGUIDLow(), difficulty, mapId); - CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%u' AND instance = '%u'", GetGUIDLow(), instanceId); - continue; - } - - MapDifficultyEntry const* mapDiff = GetMapDifficultyData(mapId, Difficulty(difficulty)); - if (!mapDiff) - { - sLog.outError("_LoadBoundInstances: player %s(%d) has bind to nonexistent difficulty %d instance for map %u", GetName(), GetGUIDLow(), difficulty, mapId); - CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%u' AND instance = '%u'", GetGUIDLow(), instanceId); - continue; - } - - if (!perm && group) - { - sLog.outError("_LoadBoundInstances: %s is in group (Id: %d) but has a non-permanent character bind to map %d,%d,%d", - GetGuidStr().c_str(), group->GetId(), mapId, instanceId, difficulty); - CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%u' AND instance = '%u'", - GetGUIDLow(), instanceId); - continue; - } - - // since non permanent binds are always solo bind, they can always be reset - DungeonPersistentState* state = (DungeonPersistentState*)sMapPersistentStateMgr.AddPersistentState(mapEntry, instanceId, Difficulty(difficulty), resetTime, !perm, true); - if (state) BindToInstance(state, perm, true); - } - while (result->NextRow()); - delete result; - } -} - -InstancePlayerBind* Player::GetBoundInstance(uint32 mapid, Difficulty difficulty) -{ - // some instances only have one difficulty - MapDifficultyEntry const* mapDiff = GetMapDifficultyData(mapid, difficulty); - if (!mapDiff) - return NULL; - - BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid); - if (itr != m_boundInstances[difficulty].end()) - return &itr->second; - else - return NULL; -} - -void Player::UnbindInstance(uint32 mapid, Difficulty difficulty, bool unload) -{ - BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid); - UnbindInstance(itr, difficulty, unload); -} - -void Player::UnbindInstance(BoundInstancesMap::iterator& itr, Difficulty difficulty, bool unload) -{ - if (itr != m_boundInstances[difficulty].end()) - { - if (!unload) - CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%u' AND instance = '%u'", - GetGUIDLow(), itr->second.state->GetInstanceId()); - - sCalendarMgr.SendCalendarRaidLockoutRemove(this, itr->second.state); - - itr->second.state->RemovePlayer(this); // state can become invalid - m_boundInstances[difficulty].erase(itr++); - } -} - -InstancePlayerBind* Player::BindToInstance(DungeonPersistentState* state, bool permanent, bool load) -{ - if (state) - { - InstancePlayerBind& bind = m_boundInstances[state->GetDifficulty()][state->GetMapId()]; - if (bind.state) - { - // update the state when the group kills a boss - if (permanent != bind.perm || state != bind.state) - if (!load) - CharacterDatabase.PExecute("UPDATE character_instance SET instance = '%u', permanent = '%u' WHERE guid = '%u' AND instance = '%u'", - state->GetInstanceId(), permanent, GetGUIDLow(), bind.state->GetInstanceId()); - } - else - { - if (!load) - CharacterDatabase.PExecute("INSERT INTO character_instance (guid, instance, permanent) VALUES ('%u', '%u', '%u')", - GetGUIDLow(), state->GetInstanceId(), permanent); - } - - if (bind.state != state) - { - if (bind.state) - bind.state->RemovePlayer(this); - state->AddPlayer(this); - } - - if (permanent) - state->SetCanReset(false); - - bind.state = state; - bind.perm = permanent; - if (!load) - DEBUG_LOG("Player::BindToInstance: %s(%d) is now bound to map %d, instance %d, difficulty %d", - GetName(), GetGUIDLow(), state->GetMapId(), state->GetInstanceId(), state->GetDifficulty()); - return &bind; - } - else - return NULL; -} - -DungeonPersistentState* Player::GetBoundInstanceSaveForSelfOrGroup(uint32 mapid) -{ - MapEntry const* mapEntry = sMapStore.LookupEntry(mapid); - if (!mapEntry) - return NULL; - - InstancePlayerBind* pBind = GetBoundInstance(mapid, GetDifficulty(mapEntry->IsRaid())); - DungeonPersistentState* state = pBind ? pBind->state : NULL; - - // the player's permanent player bind is taken into consideration first - // then the player's group bind and finally the solo bind. - if (!pBind || !pBind->perm) - { - InstanceGroupBind* groupBind = NULL; - // use the player's difficulty setting (it may not be the same as the group's) - if (Group* group = GetGroup()) - if (groupBind = group->GetBoundInstance(mapid, this)) - state = groupBind->state; - } - - return state; -} - -void Player::SendRaidInfo() -{ - uint32 counter = 0; - - WorldPacket data(SMSG_RAID_INSTANCE_INFO, 4); - - size_t p_counter = data.wpos(); - data << uint32(counter); // placeholder - - time_t now = time(NULL); - - for (int i = 0; i < MAX_DIFFICULTY; ++i) - { - for (BoundInstancesMap::const_iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr) - { - if (itr->second.perm) - { - DungeonPersistentState* state = itr->second.state; - data << uint32(state->GetMapId()); // map id - data << uint32(state->GetDifficulty()); // difficulty - data << ObjectGuid(state->GetInstanceGuid());// instance guid - data << uint8(1); // expired = 0 - data << uint8(0); // extended = 1 - data << uint32(state->GetResetTime() - now);// reset time - data << uint32(state->GetCompletedEncountersMask());// completed encounter mask - ++counter; - } - } - } - data.put(p_counter, counter); - GetSession()->SendPacket(&data); -} - -/* -- called on every successful teleportation to a map -*/ -void Player::SendSavedInstances() -{ - bool hasBeenSaved = false; - WorldPacket data; - - for (uint8 i = 0; i < MAX_DIFFICULTY; ++i) - { - for (BoundInstancesMap::const_iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr) - { - if (itr->second.perm) // only permanent binds are sent - { - hasBeenSaved = true; - break; - } - } - } - - // Send opcode 811. true or false means, whether you have current raid/heroic instances - data.Initialize(SMSG_UPDATE_INSTANCE_OWNERSHIP); - data << uint32(hasBeenSaved); - GetSession()->SendPacket(&data); - - if (!hasBeenSaved) - return; - - for (uint8 i = 0; i < MAX_DIFFICULTY; ++i) - { - for (BoundInstancesMap::const_iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr) - { - if (itr->second.perm) - { - data.Initialize(SMSG_UPDATE_LAST_INSTANCE); - data << uint32(itr->second.state->GetMapId()); - GetSession()->SendPacket(&data); - } - } - } -} - -/// convert the player's binds to the group -void Player::ConvertInstancesToGroup(Player* player, Group* group, ObjectGuid player_guid) -{ - bool has_binds = false; - bool has_solo = false; - - if (player) - { - player_guid = player->GetObjectGuid(); - if (!group) - group = player->GetGroup(); - } - - MANGOS_ASSERT(player_guid); - - // copy all binds to the group, when changing leader it's assumed the character - // will not have any solo binds - - if (player) - { - for (uint8 i = 0; i < MAX_DIFFICULTY; ++i) - { - for (BoundInstancesMap::iterator itr = player->m_boundInstances[i].begin(); itr != player->m_boundInstances[i].end();) - { - has_binds = true; - - if (group) - group->BindToInstance(itr->second.state, itr->second.perm, true); - - // permanent binds are not removed - if (!itr->second.perm) - { - // increments itr in call - player->UnbindInstance(itr, Difficulty(i), true); - has_solo = true; - } - else - ++itr; - } - } - } - - uint32 player_lowguid = player_guid.GetCounter(); - - // if the player's not online we don't know what binds it has - if (!player || !group || has_binds) - CharacterDatabase.PExecute("INSERT INTO group_instance SELECT guid, instance, permanent FROM character_instance WHERE guid = '%u'", player_lowguid); - - // the following should not get executed when changing leaders - if (!player || has_solo) - CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%u' AND permanent = 0", player_lowguid); -} - -bool Player::_LoadHomeBind(QueryResult* result) -{ - PlayerInfo const* info = sObjectMgr.GetPlayerInfo(getRace(), getClass()); - if (!info) - { - sLog.outError("Player have incorrect race/class pair. Can't be loaded."); - return false; - } - - bool ok = false; - // QueryResult *result = CharacterDatabase.PQuery("SELECT map,zone,position_x,position_y,position_z FROM character_homebind WHERE guid = '%u'", GUID_LOPART(playerGuid)); - if (result) - { - Field* fields = result->Fetch(); - m_homebindMapId = fields[0].GetUInt32(); - m_homebindAreaId = fields[1].GetUInt16(); - m_homebindX = fields[2].GetFloat(); - m_homebindY = fields[3].GetFloat(); - m_homebindZ = fields[4].GetFloat(); - delete result; - - MapEntry const* bindMapEntry = sMapStore.LookupEntry(m_homebindMapId); - - // accept saved data only for valid position (and non instanceable), and accessable - if (MapManager::IsValidMapCoord(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ) && - !bindMapEntry->Instanceable() && GetSession()->Expansion() >= bindMapEntry->Expansion()) - { - ok = true; - } - else - CharacterDatabase.PExecute("DELETE FROM character_homebind WHERE guid = '%u'", GetGUIDLow()); - } - - if (!ok) - { - m_homebindMapId = info->mapId; - m_homebindAreaId = info->areaId; - m_homebindX = info->positionX; - m_homebindY = info->positionY; - m_homebindZ = info->positionZ; - - CharacterDatabase.PExecute("INSERT INTO character_homebind (guid,map,zone,position_x,position_y,position_z) VALUES ('%u', '%u', '%u', '%f', '%f', '%f')", GetGUIDLow(), m_homebindMapId, (uint32)m_homebindAreaId, m_homebindX, m_homebindY, m_homebindZ); - } - - DEBUG_LOG("Setting player home position: mapid is: %u, zoneid is %u, X is %f, Y is %f, Z is %f", - m_homebindMapId, m_homebindAreaId, m_homebindX, m_homebindY, m_homebindZ); - - return true; -} - -/*********************************************************/ -/*** SAVE SYSTEM ***/ -/*********************************************************/ - -void Player::SaveToDB() -{ - // we should assure this: ASSERT((m_nextSave != sWorld.getConfig(CONFIG_UINT32_INTERVAL_SAVE))); - // delay auto save at any saves (manual, in code, or autosave) - m_nextSave = sWorld.getConfig(CONFIG_UINT32_INTERVAL_SAVE); - - // lets allow only players in world to be saved - if (IsBeingTeleportedFar()) - { - ScheduleDelayedOperation(DELAYED_SAVE_PLAYER); - return; - } - - // first save/honor gain after midnight will also update the player's honor fields - UpdateHonorKills(); - - DEBUG_FILTER_LOG(LOG_FILTER_PLAYER_STATS, "The value of player %s at save: ", m_name.c_str()); - outDebugStatsValues(); - - CharacterDatabase.BeginTransaction(); - - static SqlStatementID delChar ; - static SqlStatementID insChar ; - - SqlStatement stmt = CharacterDatabase.CreateStatement(delChar, "DELETE FROM characters WHERE guid = ?"); - stmt.PExecute(GetGUIDLow()); - - SqlStatement uberInsert = CharacterDatabase.CreateStatement(insChar, "INSERT INTO characters (guid,account,name,race,class,gender,level,xp,money,playerBytes,playerBytes2,playerFlags," - "map, dungeon_difficulty, position_x, position_y, position_z, orientation, " - "taximask, online, cinematic, " - "totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, primary_trees, " - "trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, " - "death_expire_time, taxi_path, totalKills, " - "todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk, health, power1, power2, power3, " - "power4, power5, specCount, activeSpec, exploredZones, equipmentCache, knownTitles, actionBars, slot) " - "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, " - "?, ?, ?, ?, ?, ?, " - "?, ?, ?, " - "?, ?, ?, ?, ?, ?, ?, ?, " - "?, ?, ?, ?, ?, ?, ?, ?, ?, " - "?, ?, ?, " - "?, ?, ?, ?, ?, ?, ?, ?, ?, " - "?, ?, ?, ?, ?, ?, ?, ?, ?) "); - - uberInsert.addUInt32(GetGUIDLow()); - uberInsert.addUInt32(GetSession()->GetAccountId()); - uberInsert.addString(m_name); - uberInsert.addUInt8(getRace()); - uberInsert.addUInt8(getClass()); - uberInsert.addUInt8(getGender()); - uberInsert.addUInt32(getLevel()); - uberInsert.addUInt32(GetUInt32Value(PLAYER_XP)); - uberInsert.addUInt64(GetMoney()); - uberInsert.addUInt32(GetUInt32Value(PLAYER_BYTES)); - uberInsert.addUInt32(GetUInt32Value(PLAYER_BYTES_2)); - uberInsert.addUInt32(GetUInt32Value(PLAYER_FLAGS)); - - if (!IsBeingTeleported()) - { - uberInsert.addUInt32(GetMapId()); - uberInsert.addUInt32(uint32(GetDungeonDifficulty())); - uberInsert.addFloat(finiteAlways(GetPositionX())); - uberInsert.addFloat(finiteAlways(GetPositionY())); - uberInsert.addFloat(finiteAlways(GetPositionZ())); - uberInsert.addFloat(finiteAlways(GetOrientation())); - } - else - { - uberInsert.addUInt32(GetTeleportDest().mapid); - uberInsert.addUInt32(uint32(GetDungeonDifficulty())); - uberInsert.addFloat(finiteAlways(GetTeleportDest().coord_x)); - uberInsert.addFloat(finiteAlways(GetTeleportDest().coord_y)); - uberInsert.addFloat(finiteAlways(GetTeleportDest().coord_z)); - uberInsert.addFloat(finiteAlways(GetTeleportDest().orientation)); - } - - std::ostringstream ss; - ss << m_taxi; // string with TaxiMaskSize numbers - uberInsert.addString(ss); - - uberInsert.addUInt32(IsInWorld() ? 1 : 0); - - uberInsert.addUInt32(m_cinematic); - - uberInsert.addUInt32(m_Played_time[PLAYED_TIME_TOTAL]); - uberInsert.addUInt32(m_Played_time[PLAYED_TIME_LEVEL]); - - uberInsert.addFloat(finiteAlways(m_rest_bonus)); - uberInsert.addUInt64(uint64(time(NULL))); - uberInsert.addUInt32(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) ? 1 : 0); - // save, far from tavern/city - // save, but in tavern/city - uberInsert.addUInt32(m_resetTalentsCost); - uberInsert.addUInt64(uint64(m_resetTalentsTime)); - ss.str(""); - for (int i = 0; i < MAX_TALENT_SPEC_COUNT; ++i) - ss << m_talentsPrimaryTree[i] << " "; - uberInsert.addString(ss); - - uberInsert.addFloat(finiteAlways(m_movementInfo.GetTransportPos()->x)); - uberInsert.addFloat(finiteAlways(m_movementInfo.GetTransportPos()->y)); - uberInsert.addFloat(finiteAlways(m_movementInfo.GetTransportPos()->z)); - uberInsert.addFloat(finiteAlways(m_movementInfo.GetTransportPos()->o)); - if (m_transport) - uberInsert.addUInt32(m_transport->GetGUIDLow()); - else - uberInsert.addUInt32(0); - - uberInsert.addUInt32(m_ExtraFlags); - - uberInsert.addUInt32(uint32(m_stableSlots)); // to prevent save uint8 as char - - uberInsert.addUInt32(uint32(m_atLoginFlags)); - - uberInsert.addUInt32(IsInWorld() ? GetZoneId() : GetCachedZoneId()); - - uberInsert.addUInt64(uint64(m_deathExpireTime)); - - ss << m_taxi.SaveTaxiDestinationsToString(); // string - uberInsert.addString(ss); - - uberInsert.addUInt32(GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS)); - - uberInsert.addUInt16(GetUInt16Value(PLAYER_FIELD_KILLS, 0)); - - uberInsert.addUInt16(GetUInt16Value(PLAYER_FIELD_KILLS, 1)); - - uberInsert.addUInt32(GetUInt32Value(PLAYER_CHOSEN_TITLE)); - - // FIXME: at this moment send to DB as unsigned, including unit32(-1) - uberInsert.addUInt32(GetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX)); - - uberInsert.addUInt8(GetDrunkValue()); - - uberInsert.addUInt32(GetHealth()); - - COMPILE_ASSERT(MAX_STORED_POWERS == 5, "Query not updated."); - for (uint32 i = 0; i < MAX_STORED_POWERS; ++i) - uberInsert.addUInt32(GetPowerByIndex(i)); - - uberInsert.addUInt32(uint32(m_specsCount)); - uberInsert.addUInt32(uint32(m_activeSpec)); - - for (uint32 i = 0; i < PLAYER_EXPLORED_ZONES_SIZE; ++i) // string - { - ss << GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + i) << " "; - } - uberInsert.addString(ss); - - for (uint32 i = 0; i < EQUIPMENT_SLOT_END * 2; ++i) // string - { - ss << GetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID + i) << " "; - } - uberInsert.addString(ss); - - for(uint32 i = 0; i < KNOWN_TITLES_SIZE*2; ++i ) //string - { - ss << GetUInt32Value(PLAYER__FIELD_KNOWN_TITLES + i) << " "; - } - uberInsert.addString(ss); - - uberInsert.addUInt32(uint32(GetByteValue(PLAYER_FIELD_BYTES, 2))); - - uberInsert.addUInt8(m_slot); - - uberInsert.Execute(); - - if (m_mailsUpdated) // save mails only when needed - _SaveMail(); - - _SaveBGData(); - _SaveInventory(); - _SaveQuestStatus(); - _SaveDailyQuestStatus(); - _SaveWeeklyQuestStatus(); - _SaveMonthlyQuestStatus(); - _SaveSpells(); - _SaveSpellCooldowns(); - _SaveActions(); - _SaveAuras(); - _SaveSkills(); - m_achievementMgr.SaveToDB(); - m_reputationMgr.SaveToDB(); - _SaveCurrencies(); - _SaveEquipmentSets(); - GetSession()->SaveTutorialsData(); // changed only while character in game - _SaveGlyphs(); - _SaveTalents(); - - CharacterDatabase.CommitTransaction(); - - // check if stats should only be saved on logout - // save stats can be out of transaction - if (m_session->isLogingOut() || !sWorld.getConfig(CONFIG_BOOL_STATS_SAVE_ONLY_ON_LOGOUT)) - _SaveStats(); - - // save pet (hunter pet level and experience and all type pets health/mana). - if (Pet* pet = GetPet()) - pet->SavePetToDB(PET_SAVE_AS_CURRENT); -} - -// fast save function for item/money cheating preventing - save only inventory and money state -void Player::SaveInventoryAndGoldToDB() -{ - _SaveInventory(); - SaveGoldToDB(); -} - -void Player::SaveGoldToDB() -{ - static SqlStatementID updateGold ; - - SqlStatement stmt = CharacterDatabase.CreateStatement(updateGold, "UPDATE characters SET money = ? WHERE guid = ?"); - stmt.PExecute(GetMoney(), GetGUIDLow()); -} - -void Player::_SaveActions() -{ - static SqlStatementID insertAction ; - static SqlStatementID updateAction ; - static SqlStatementID deleteAction ; - - for (int i = 0; i < MAX_TALENT_SPEC_COUNT; ++i) - { - for (ActionButtonList::iterator itr = m_actionButtons[i].begin(); itr != m_actionButtons[i].end();) - { - switch (itr->second.uState) - { - case ACTIONBUTTON_NEW: - { - SqlStatement stmt = CharacterDatabase.CreateStatement(insertAction, "INSERT INTO character_action (guid,spec, button,action,type) VALUES (?, ?, ?, ?, ?)"); - stmt.addUInt32(GetGUIDLow()); - stmt.addUInt32(i); - stmt.addUInt32(uint32(itr->first)); - stmt.addUInt32(itr->second.GetAction()); - stmt.addUInt32(uint32(itr->second.GetType())); - stmt.Execute(); - itr->second.uState = ACTIONBUTTON_UNCHANGED; - ++itr; - } - break; - case ACTIONBUTTON_CHANGED: - { - SqlStatement stmt = CharacterDatabase.CreateStatement(updateAction, "UPDATE character_action SET action = ?, type = ? WHERE guid = ? AND button = ? AND spec = ?"); - stmt.addUInt32(itr->second.GetAction()); - stmt.addUInt32(uint32(itr->second.GetType())); - stmt.addUInt32(GetGUIDLow()); - stmt.addUInt32(uint32(itr->first)); - stmt.addUInt32(i); - stmt.Execute(); - itr->second.uState = ACTIONBUTTON_UNCHANGED; - ++itr; - } - break; - case ACTIONBUTTON_DELETED: - { - SqlStatement stmt = CharacterDatabase.CreateStatement(deleteAction, "DELETE FROM character_action WHERE guid = ? AND button = ? AND spec = ?"); - stmt.addUInt32(GetGUIDLow()); - stmt.addUInt32(uint32(itr->first)); - stmt.addUInt32(i); - stmt.Execute(); - m_actionButtons[i].erase(itr++); - } - break; - default: - ++itr; - break; - } - } - } -} - -void Player::_SaveAuras() -{ - static SqlStatementID deleteAuras ; - static SqlStatementID insertAuras ; - - SqlStatement stmt = CharacterDatabase.CreateStatement(deleteAuras, "DELETE FROM character_aura WHERE guid = ?"); - stmt.PExecute(GetGUIDLow()); - - SpellAuraHolderMap const& auraHolders = GetSpellAuraHolderMap(); - - if (auraHolders.empty()) - return; - - stmt = CharacterDatabase.CreateStatement(insertAuras, "INSERT INTO character_aura (guid, caster_guid, item_guid, spell, stackcount, remaincharges, " - "basepoints0, basepoints1, basepoints2, periodictime0, periodictime1, periodictime2, maxduration, remaintime, effIndexMask) " - "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); - - for (SpellAuraHolderMap::const_iterator itr = auraHolders.begin(); itr != auraHolders.end(); ++itr) - { - SpellAuraHolder* holder = itr->second; - // skip all holders from spells that are passive or channeled - // save singleTarget auras if self cast. - bool selfCastHolder = holder->GetCasterGuid() == GetObjectGuid(); - TrackedAuraType trackedType = holder->GetTrackedAuraType(); - if (!holder->IsPassive() && !IsChanneledSpell(holder->GetSpellProto()) && - (trackedType == TRACK_AURA_TYPE_NOT_TRACKED || (trackedType == TRACK_AURA_TYPE_SINGLE_TARGET && selfCastHolder))) - { - int32 damage[MAX_EFFECT_INDEX]; - uint32 periodicTime[MAX_EFFECT_INDEX]; - uint32 effIndexMask = 0; - - for (uint32 i = 0; i < MAX_EFFECT_INDEX; ++i) - { - damage[i] = 0; - periodicTime[i] = 0; - - if (Aura* aur = holder->GetAuraByEffectIndex(SpellEffectIndex(i))) - { - // don't save not own area auras - if (aur->IsAreaAura() && holder->GetCasterGuid() != GetObjectGuid()) - continue; - - damage[i] = aur->GetModifier()->m_amount; - periodicTime[i] = aur->GetModifier()->periodictime; - effIndexMask |= (1 << i); - } - } - - if (!effIndexMask) - continue; - - stmt.addUInt32(GetGUIDLow()); - stmt.addUInt64(holder->GetCasterGuid().GetRawValue()); - stmt.addUInt32(holder->GetCastItemGuid().GetCounter()); - stmt.addUInt32(holder->GetId()); - stmt.addUInt32(holder->GetStackAmount()); - stmt.addUInt8(holder->GetAuraCharges()); - - for (uint32 i = 0; i < MAX_EFFECT_INDEX; ++i) - stmt.addInt32(damage[i]); - - for (uint32 i = 0; i < MAX_EFFECT_INDEX; ++i) - stmt.addUInt32(periodicTime[i]); - - stmt.addInt32(holder->GetAuraMaxDuration()); - stmt.addInt32(holder->GetAuraDuration()); - stmt.addUInt32(effIndexMask); - stmt.Execute(); - } - } -} - -void Player::_SaveGlyphs() -{ - static SqlStatementID insertGlyph ; - static SqlStatementID updateGlyph ; - static SqlStatementID deleteGlyph ; - - for (uint8 spec = 0; spec < m_specsCount; ++spec) - { - for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot) - { - switch (m_glyphs[spec][slot].uState) - { - case GLYPH_NEW: - { - SqlStatement stmt = CharacterDatabase.CreateStatement(insertGlyph, "INSERT INTO character_glyphs (guid, spec, slot, glyph) VALUES (?, ?, ?, ?)"); - stmt.PExecute(GetGUIDLow(), spec, slot, m_glyphs[spec][slot].GetId()); - break; - } - case GLYPH_CHANGED: - { - SqlStatement stmt = CharacterDatabase.CreateStatement(updateGlyph, "UPDATE character_glyphs SET glyph = ? WHERE guid = ? AND spec = ? AND slot = ?"); - stmt.PExecute(m_glyphs[spec][slot].GetId(), GetGUIDLow(), spec, slot); - break; - } - case GLYPH_DELETED: - { - SqlStatement stmt = CharacterDatabase.CreateStatement(deleteGlyph, "DELETE FROM character_glyphs WHERE guid = ? AND spec = ? AND slot = ?"); - stmt.PExecute(GetGUIDLow(), spec, slot); - break; - } - case GLYPH_UNCHANGED: - break; - } - m_glyphs[spec][slot].uState = GLYPH_UNCHANGED; - } - } -} - -void Player::_SaveInventory() -{ - // force items in buyback slots to new state - // and remove those that aren't already - for (uint8 i = BUYBACK_SLOT_START; i < BUYBACK_SLOT_END; ++i) - { - Item* item = m_items[i]; - if (!item || item->GetState() == ITEM_NEW) continue; - - static SqlStatementID delInv ; - static SqlStatementID delItemInst ; - - SqlStatement stmt = CharacterDatabase.CreateStatement(delInv, "DELETE FROM character_inventory WHERE item = ?"); - stmt.PExecute(item->GetGUIDLow()); - - stmt = CharacterDatabase.CreateStatement(delItemInst, "DELETE FROM item_instance WHERE guid = ?"); - stmt.PExecute(item->GetGUIDLow()); - - m_items[i]->FSetState(ITEM_NEW); - } - - // update enchantment durations - for (EnchantDurationList::const_iterator itr = m_enchantDuration.begin(); itr != m_enchantDuration.end(); ++itr) - { - itr->item->SetEnchantmentDuration(itr->slot, itr->leftduration); - } - - // if no changes - if (m_itemUpdateQueue.empty()) return; - - // do not save if the update queue is corrupt - bool error = false; - for (size_t i = 0; i < m_itemUpdateQueue.size(); ++i) - { - Item* item = m_itemUpdateQueue[i]; - if (!item || item->GetState() == ITEM_REMOVED) continue; - Item* test = GetItemByPos(item->GetBagSlot(), item->GetSlot()); - - if (test == NULL) - { - sLog.outError("Player(GUID: %u Name: %s)::_SaveInventory - the bag(%d) and slot(%d) values for the item with guid %d are incorrect, the player doesn't have an item at that position!", GetGUIDLow(), GetName(), item->GetBagSlot(), item->GetSlot(), item->GetGUIDLow()); - error = true; - } - else if (test != item) - { - sLog.outError("Player(GUID: %u Name: %s)::_SaveInventory - the bag(%d) and slot(%d) values for the item with guid %d are incorrect, the item with guid %d is there instead!", GetGUIDLow(), GetName(), item->GetBagSlot(), item->GetSlot(), item->GetGUIDLow(), test->GetGUIDLow()); - error = true; - } - } - - if (error) - { - sLog.outError("Player::_SaveInventory - one or more errors occurred save aborted!"); - ChatHandler(this).SendSysMessage(LANG_ITEM_SAVE_FAILED); - return; - } - - static SqlStatementID insertInventory ; - static SqlStatementID updateInventory ; - static SqlStatementID deleteInventory ; - - for (size_t i = 0; i < m_itemUpdateQueue.size(); ++i) - { - Item* item = m_itemUpdateQueue[i]; - if (!item) continue; - - Bag* container = item->GetContainer(); - uint32 bag_guid = container ? container->GetGUIDLow() : 0; - - switch (item->GetState()) - { - case ITEM_NEW: - { - SqlStatement stmt = CharacterDatabase.CreateStatement(insertInventory, "INSERT INTO character_inventory (guid,bag,slot,item,item_template) VALUES (?, ?, ?, ?, ?)"); - stmt.addUInt32(GetGUIDLow()); - stmt.addUInt32(bag_guid); - stmt.addUInt8(item->GetSlot()); - stmt.addUInt32(item->GetGUIDLow()); - stmt.addUInt32(item->GetEntry()); - stmt.Execute(); - } - break; - case ITEM_CHANGED: - { - SqlStatement stmt = CharacterDatabase.CreateStatement(updateInventory, "UPDATE character_inventory SET guid = ?, bag = ?, slot = ?, item_template = ? WHERE item = ?"); - stmt.addUInt32(GetGUIDLow()); - stmt.addUInt32(bag_guid); - stmt.addUInt8(item->GetSlot()); - stmt.addUInt32(item->GetEntry()); - stmt.addUInt32(item->GetGUIDLow()); - stmt.Execute(); - } - break; - case ITEM_REMOVED: - { - SqlStatement stmt = CharacterDatabase.CreateStatement(deleteInventory, "DELETE FROM character_inventory WHERE item = ?"); - stmt.PExecute(item->GetGUIDLow()); - } - break; - case ITEM_UNCHANGED: - break; - } - - item->SaveToDB(); // item have unchanged inventory record and can be save standalone - } - m_itemUpdateQueue.clear(); -} - -void Player::_SaveMail() -{ - static SqlStatementID updateMail ; - static SqlStatementID deleteMailItems ; - - static SqlStatementID deleteItem ; - static SqlStatementID deleteMain ; - static SqlStatementID deleteItems ; - - for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr) - { - Mail* m = (*itr); - if (m->state == MAIL_STATE_CHANGED) - { - SqlStatement stmt = CharacterDatabase.CreateStatement(updateMail, "UPDATE mail SET has_items = ?, expire_time = ?, deliver_time = ?, money = ?, cod = ?, checked = ? WHERE id = ?"); - stmt.addUInt32(m->HasItems() ? 1 : 0); - stmt.addUInt64(uint64(m->expire_time)); - stmt.addUInt64(uint64(m->deliver_time)); - stmt.addUInt32(m->money); - stmt.addUInt32(m->COD); - stmt.addUInt32(m->checked); - stmt.addUInt32(m->messageID); - stmt.Execute(); - - if (!m->removedItems.empty()) - { - stmt = CharacterDatabase.CreateStatement(deleteMailItems, "DELETE FROM mail_items WHERE item_guid = ?"); - - for (std::vector::const_iterator itr2 = m->removedItems.begin(); itr2 != m->removedItems.end(); ++itr2) - stmt.PExecute(*itr2); - - m->removedItems.clear(); - } - m->state = MAIL_STATE_UNCHANGED; - } - else if (m->state == MAIL_STATE_DELETED) - { - if (m->HasItems()) - { - SqlStatement stmt = CharacterDatabase.CreateStatement(deleteItem, "DELETE FROM item_instance WHERE guid = ?"); - for (MailItemInfoVec::const_iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2) - stmt.PExecute(itr2->item_guid); - } - - SqlStatement stmt = CharacterDatabase.CreateStatement(deleteMain, "DELETE FROM mail WHERE id = ?"); - stmt.PExecute(m->messageID); - - stmt = CharacterDatabase.CreateStatement(deleteItems, "DELETE FROM mail_items WHERE mail_id = ?"); - stmt.PExecute(m->messageID); - } - } - - // deallocate deleted mails... - for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end();) - { - if ((*itr)->state == MAIL_STATE_DELETED) - { - Mail* m = *itr; - m_mail.erase(itr); - delete m; - itr = m_mail.begin(); - } - else - ++itr; - } - - m_mailsUpdated = false; -} - -void Player::_SaveQuestStatus() -{ - static SqlStatementID insertQuestStatus ; - - static SqlStatementID updateQuestStatus ; - - // we don't need transactions here. - for (QuestStatusMap::iterator i = mQuestStatus.begin(); i != mQuestStatus.end(); ++i) - { - switch (i->second.uState) - { - case QUEST_NEW : - { - SqlStatement stmt = CharacterDatabase.CreateStatement(insertQuestStatus, "INSERT INTO character_queststatus (guid,quest,status,rewarded,explored,timer,mobcount1,mobcount2,mobcount3,mobcount4,itemcount1,itemcount2,itemcount3,itemcount4,itemcount5,itemcount6) " - "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); - - stmt.addUInt32(GetGUIDLow()); - stmt.addUInt32(i->first); - stmt.addUInt8(i->second.m_status); - stmt.addUInt8(i->second.m_rewarded); - stmt.addUInt8(i->second.m_explored); - stmt.addUInt64(uint64(i->second.m_timer / IN_MILLISECONDS + sWorld.GetGameTime())); - for (int k = 0; k < QUEST_OBJECTIVES_COUNT; ++k) - stmt.addUInt32(i->second.m_creatureOrGOcount[k]); - for (int k = 0; k < QUEST_ITEM_OBJECTIVES_COUNT; ++k) - stmt.addUInt32(i->second.m_itemcount[k]); - stmt.Execute(); - } - break; - case QUEST_CHANGED : - { - SqlStatement stmt = CharacterDatabase.CreateStatement(updateQuestStatus, "UPDATE character_queststatus SET status = ?,rewarded = ?,explored = ?,timer = ?," - "mobcount1 = ?,mobcount2 = ?,mobcount3 = ?,mobcount4 = ?,itemcount1 = ?,itemcount2 = ?,itemcount3 = ?,itemcount4 = ?,itemcount5 = ?,itemcount6 = ? WHERE guid = ? AND quest = ?"); - - stmt.addUInt8(i->second.m_status); - stmt.addUInt8(i->second.m_rewarded); - stmt.addUInt8(i->second.m_explored); - stmt.addUInt64(uint64(i->second.m_timer / IN_MILLISECONDS + sWorld.GetGameTime())); - for (int k = 0; k < QUEST_OBJECTIVES_COUNT; ++k) - stmt.addUInt32(i->second.m_creatureOrGOcount[k]); - for (int k = 0; k < QUEST_ITEM_OBJECTIVES_COUNT; ++k) - stmt.addUInt32(i->second.m_itemcount[k]); - stmt.addUInt32(GetGUIDLow()); - stmt.addUInt32(i->first); - stmt.Execute(); - } - break; - case QUEST_UNCHANGED: - break; - }; - i->second.uState = QUEST_UNCHANGED; - } -} - -void Player::_SaveDailyQuestStatus() -{ - if (!m_DailyQuestChanged) - return; - - // we don't need transactions here. - static SqlStatementID delQuestStatus ; - static SqlStatementID insQuestStatus ; - - SqlStatement stmtDel = CharacterDatabase.CreateStatement(delQuestStatus, "DELETE FROM character_queststatus_daily WHERE guid = ?"); - SqlStatement stmtIns = CharacterDatabase.CreateStatement(insQuestStatus, "INSERT INTO character_queststatus_daily (guid,quest) VALUES (?, ?)"); - - stmtDel.PExecute(GetGUIDLow()); - - for (uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx) - if (GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1 + quest_daily_idx)) - stmtIns.PExecute(GetGUIDLow(), GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1 + quest_daily_idx)); - - m_DailyQuestChanged = false; -} - -void Player::_SaveWeeklyQuestStatus() -{ - if (!m_WeeklyQuestChanged || m_weeklyquests.empty()) - return; - - // we don't need transactions here. - static SqlStatementID delQuestStatus ; - static SqlStatementID insQuestStatus ; - - SqlStatement stmtDel = CharacterDatabase.CreateStatement(delQuestStatus, "DELETE FROM character_queststatus_weekly WHERE guid = ?"); - SqlStatement stmtIns = CharacterDatabase.CreateStatement(insQuestStatus, "INSERT INTO character_queststatus_weekly (guid,quest) VALUES (?, ?)"); - - stmtDel.PExecute(GetGUIDLow()); - - for (QuestSet::const_iterator iter = m_weeklyquests.begin(); iter != m_weeklyquests.end(); ++iter) - { - uint32 quest_id = *iter; - stmtIns.PExecute(GetGUIDLow(), quest_id); - } - - m_WeeklyQuestChanged = false; -} - -void Player::_SaveMonthlyQuestStatus() -{ - if (!m_MonthlyQuestChanged || m_monthlyquests.empty()) - return; - - // we don't need transactions here. - static SqlStatementID deleteQuest ; - static SqlStatementID insertQuest ; - - SqlStatement stmtDel = CharacterDatabase.CreateStatement(deleteQuest, "DELETE FROM character_queststatus_monthly WHERE guid = ?"); - SqlStatement stmtIns = CharacterDatabase.CreateStatement(insertQuest, "INSERT INTO character_queststatus_monthly (guid, quest) VALUES (?, ?)"); - - stmtDel.PExecute(GetGUIDLow()); - - for (QuestSet::const_iterator iter = m_monthlyquests.begin(); iter != m_monthlyquests.end(); ++iter) - { - uint32 quest_id = *iter; - stmtIns.PExecute(GetGUIDLow(), quest_id); - } - - m_MonthlyQuestChanged = false; -} - -void Player::_SaveSkills() -{ - static SqlStatementID delSkills ; - static SqlStatementID insSkills ; - static SqlStatementID updSkills ; - - // we don't need transactions here. - for (SkillStatusMap::iterator itr = mSkillStatus.begin(); itr != mSkillStatus.end();) - { - if (itr->second.uState == SKILL_UNCHANGED) - { - ++itr; - continue; - } - - if (itr->second.uState == SKILL_DELETED) - { - SqlStatement stmt = CharacterDatabase.CreateStatement(delSkills, "DELETE FROM character_skills WHERE guid = ? AND skill = ?"); - stmt.PExecute(GetGUIDLow(), itr->first); - mSkillStatus.erase(itr++); - continue; - } - - uint16 field = itr->second.pos / 2; - uint8 offset = itr->second.pos & 1; - - uint16 value = GetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset); - uint16 max = GetUInt16Value(PLAYER_SKILL_MAX_RANK_0 + field, offset); - - switch (itr->second.uState) - { - case SKILL_NEW: - { - SqlStatement stmt = CharacterDatabase.CreateStatement(insSkills, "INSERT INTO character_skills (guid, skill, value, max) VALUES (?, ?, ?, ?)"); - stmt.PExecute(GetGUIDLow(), itr->first, value, max); - } - break; - case SKILL_CHANGED: - { - SqlStatement stmt = CharacterDatabase.CreateStatement(updSkills, "UPDATE character_skills SET value = ?, max = ? WHERE guid = ? AND skill = ?"); - stmt.PExecute(value, max, GetGUIDLow(), itr->first); - } - break; - case SKILL_UNCHANGED: - case SKILL_DELETED: - MANGOS_ASSERT(false); - break; - }; - itr->second.uState = SKILL_UNCHANGED; - - ++itr; - } -} - -void Player::_SaveSpells() -{ - static SqlStatementID delSpells ; - static SqlStatementID insSpells ; - - SqlStatement stmtDel = CharacterDatabase.CreateStatement(delSpells, "DELETE FROM character_spell WHERE guid = ? and spell = ?"); - SqlStatement stmtIns = CharacterDatabase.CreateStatement(insSpells, "INSERT INTO character_spell (guid,spell,active,disabled) VALUES (?, ?, ?, ?)"); - - for (PlayerSpellMap::iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end();) - { - uint32 talentCosts = GetTalentSpellCost(itr->first); - - if (!talentCosts) - { - if (itr->second.state == PLAYERSPELL_REMOVED || itr->second.state == PLAYERSPELL_CHANGED) - stmtDel.PExecute(GetGUIDLow(), itr->first); - - // add only changed/new not dependent spells - if (!itr->second.dependent && (itr->second.state == PLAYERSPELL_NEW || itr->second.state == PLAYERSPELL_CHANGED)) - stmtIns.PExecute(GetGUIDLow(), itr->first, uint8(itr->second.active ? 1 : 0), uint8(itr->second.disabled ? 1 : 0)); - } - - if (itr->second.state == PLAYERSPELL_REMOVED) - m_spells.erase(itr++); - else - { - itr->second.state = PLAYERSPELL_UNCHANGED; - ++itr; - } - } -} - -void Player::_SaveTalents() -{ - static SqlStatementID delTalents ; - static SqlStatementID insTalents ; - - SqlStatement stmtDel = CharacterDatabase.CreateStatement(delTalents, "DELETE FROM character_talent WHERE guid = ? and talent_id = ? and spec = ?"); - SqlStatement stmtIns = CharacterDatabase.CreateStatement(insTalents, "INSERT INTO character_talent (guid, talent_id, current_rank , spec) VALUES (?, ?, ?, ?)"); - - for (uint32 i = 0; i < MAX_TALENT_SPEC_COUNT; ++i) - { - for (PlayerTalentMap::iterator itr = m_talents[i].begin(); itr != m_talents[i].end();) - { - if (itr->second.state == PLAYERSPELL_REMOVED || itr->second.state == PLAYERSPELL_CHANGED) - stmtDel.PExecute(GetGUIDLow(), itr->first, i); - - // add only changed/new talents - if (itr->second.state == PLAYERSPELL_NEW || itr->second.state == PLAYERSPELL_CHANGED) - stmtIns.PExecute(GetGUIDLow(), itr->first, itr->second.currentRank, i); - - if (itr->second.state == PLAYERSPELL_REMOVED) - m_talents[i].erase(itr++); - else - { - itr->second.state = PLAYERSPELL_UNCHANGED; - ++itr; - } - } - } -} - -// save player stats -- only for external usage -// real stats will be recalculated on player login -void Player::_SaveStats() -{ - // check if stat saving is enabled and if char level is high enough - if (!sWorld.getConfig(CONFIG_UINT32_MIN_LEVEL_STAT_SAVE) || getLevel() < sWorld.getConfig(CONFIG_UINT32_MIN_LEVEL_STAT_SAVE)) - return; - - static SqlStatementID delStats ; - static SqlStatementID insertStats ; - - SqlStatement stmt = CharacterDatabase.CreateStatement(delStats, "DELETE FROM character_stats WHERE guid = ?"); - stmt.PExecute(GetGUIDLow()); - - stmt = CharacterDatabase.CreateStatement(insertStats, "INSERT INTO character_stats (guid, maxhealth, maxpower1, maxpower2, maxpower3, maxpower4, maxpower5," - "strength, agility, stamina, intellect, spirit, armor, resHoly, resFire, resNature, resFrost, resShadow, resArcane, " - "blockPct, dodgePct, parryPct, critPct, rangedCritPct, spellCritPct, attackPower, rangedAttackPower, spellPower) " - "VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); - - stmt.addUInt32(GetGUIDLow()); - stmt.addUInt32(GetMaxHealth()); - COMPILE_ASSERT(MAX_STORED_POWERS == 5, "Query not updated."); - for (uint32 i = 0; i < MAX_STORED_POWERS; ++i) - stmt.addUInt32(GetMaxPowerByIndex(i)); - for (int i = 0; i < MAX_STATS; ++i) - stmt.addFloat(GetStat(Stats(i))); - // armor + school resistances - for (int i = 0; i < MAX_SPELL_SCHOOL; ++i) - stmt.addUInt32(GetResistance(SpellSchools(i))); - stmt.addFloat(GetFloatValue(PLAYER_BLOCK_PERCENTAGE)); - stmt.addFloat(GetFloatValue(PLAYER_DODGE_PERCENTAGE)); - stmt.addFloat(GetFloatValue(PLAYER_PARRY_PERCENTAGE)); - stmt.addFloat(GetFloatValue(PLAYER_CRIT_PERCENTAGE)); - stmt.addFloat(GetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE)); - stmt.addFloat(GetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1)); - stmt.addUInt32(GetUInt32Value(UNIT_FIELD_ATTACK_POWER)); - stmt.addUInt32(GetUInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER)); - stmt.addUInt32(GetBaseSpellPowerBonus()); - - stmt.Execute(); -} - -void Player::outDebugStatsValues() const -{ - // optimize disabled debug output - if (!sLog.HasLogLevelOrHigher(LOG_LVL_DEBUG) || sLog.HasLogFilter(LOG_FILTER_PLAYER_STATS)) - return; - - sLog.outDebug("HP is: \t\t\t%u\t\tMP is: \t\t\t%u", GetMaxHealth(), GetMaxPower(POWER_MANA)); - sLog.outDebug("AGILITY is: \t\t%f\t\tSTRENGTH is: \t\t%f", GetStat(STAT_AGILITY), GetStat(STAT_STRENGTH)); - sLog.outDebug("INTELLECT is: \t\t%f\t\tSPIRIT is: \t\t%f", GetStat(STAT_INTELLECT), GetStat(STAT_SPIRIT)); - sLog.outDebug("STAMINA is: \t\t%f", GetStat(STAT_STAMINA)); - sLog.outDebug("Armor is: \t\t%u\t\tBlock is: \t\t%f", GetArmor(), GetFloatValue(PLAYER_BLOCK_PERCENTAGE)); - sLog.outDebug("HolyRes is: \t\t%u\t\tFireRes is: \t\t%u", GetResistance(SPELL_SCHOOL_HOLY), GetResistance(SPELL_SCHOOL_FIRE)); - sLog.outDebug("NatureRes is: \t\t%u\t\tFrostRes is: \t\t%u", GetResistance(SPELL_SCHOOL_NATURE), GetResistance(SPELL_SCHOOL_FROST)); - sLog.outDebug("ShadowRes is: \t\t%u\t\tArcaneRes is: \t\t%u", GetResistance(SPELL_SCHOOL_SHADOW), GetResistance(SPELL_SCHOOL_ARCANE)); - sLog.outDebug("MIN_DAMAGE is: \t\t%f\tMAX_DAMAGE is: \t\t%f", GetFloatValue(UNIT_FIELD_MINDAMAGE), GetFloatValue(UNIT_FIELD_MAXDAMAGE)); - sLog.outDebug("MIN_OFFHAND_DAMAGE is: \t%f\tMAX_OFFHAND_DAMAGE is: \t%f", GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE), GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE)); - sLog.outDebug("MIN_RANGED_DAMAGE is: \t%f\tMAX_RANGED_DAMAGE is: \t%f", GetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE), GetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE)); - sLog.outDebug("ATTACK_TIME is: \t%u\t\tRANGE_ATTACK_TIME is: \t%u", GetAttackTime(BASE_ATTACK), GetAttackTime(RANGED_ATTACK)); -} - -/*********************************************************/ -/*** FLOOD FILTER SYSTEM ***/ -/*********************************************************/ - -void Player::UpdateSpeakTime() -{ - // ignore chat spam protection for GMs in any mode - if (GetSession()->GetSecurity() > SEC_PLAYER) - return; - - time_t current = time(NULL); - if (m_speakTime > current) - { - uint32 max_count = sWorld.getConfig(CONFIG_UINT32_CHATFLOOD_MESSAGE_COUNT); - if (!max_count) - return; - - ++m_speakCount; - if (m_speakCount >= max_count) - { - // prevent overwrite mute time, if message send just before mutes set, for example. - time_t new_mute = current + sWorld.getConfig(CONFIG_UINT32_CHATFLOOD_MUTE_TIME); - if (GetSession()->m_muteTime < new_mute) - GetSession()->m_muteTime = new_mute; - - m_speakCount = 0; - } - } - else - m_speakCount = 0; - - m_speakTime = current + sWorld.getConfig(CONFIG_UINT32_CHATFLOOD_MESSAGE_DELAY); -} - -bool Player::CanSpeak() const -{ - return GetSession()->m_muteTime <= time(NULL); -} - -/*********************************************************/ -/*** LOW LEVEL FUNCTIONS:Notifiers ***/ -/*********************************************************/ - -void Player::SendAttackSwingNotInRange() -{ - WorldPacket data(SMSG_ATTACKSWING_NOTINRANGE, 0); - GetSession()->SendPacket(&data); -} - -void Player::SavePositionInDB(ObjectGuid guid, uint32 mapid, float x, float y, float z, float o, uint32 zone) -{ - std::ostringstream ss; - ss << "UPDATE characters SET position_x='" << x << "',position_y='" << y - << "',position_z='" << z << "',orientation='" << o << "',map='" << mapid - << "',zone='" << zone << "',trans_x='0',trans_y='0',trans_z='0'," - << "transguid='0',taxi_path='' WHERE guid='" << guid.GetCounter() << "'"; - DEBUG_LOG("%s", ss.str().c_str()); - CharacterDatabase.Execute(ss.str().c_str()); -} - -void Player::SetUInt32ValueInArray(Tokens& tokens, uint16 index, uint32 value) -{ - char buf[11]; - snprintf(buf, 11, "%u", value); - - if (index >= tokens.size()) - return; - - tokens[index] = buf; -} - -void Player::Customize(ObjectGuid guid, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair) -{ - // 0 - QueryResult* result = CharacterDatabase.PQuery("SELECT playerBytes2 FROM characters WHERE guid = '%u'", guid.GetCounter()); - if (!result) - return; - - Field* fields = result->Fetch(); - - uint32 player_bytes2 = fields[0].GetUInt32(); - player_bytes2 &= ~0xFF; - player_bytes2 |= facialHair; - - CharacterDatabase.PExecute("UPDATE characters SET gender = '%u', playerBytes = '%u', playerBytes2 = '%u' WHERE guid = '%u'", gender, skin | (face << 8) | (hairStyle << 16) | (hairColor << 24), player_bytes2, guid.GetCounter()); - - delete result; -} - -void Player::SendAttackSwingDeadTarget() -{ - WorldPacket data(SMSG_ATTACKSWING_DEADTARGET, 0); - GetSession()->SendPacket(&data); -} - -void Player::SendAttackSwingCantAttack() -{ - WorldPacket data(SMSG_ATTACKSWING_CANT_ATTACK, 0); - GetSession()->SendPacket(&data); -} - -void Player::SendAttackSwingCancelAttack() -{ - WorldPacket data(SMSG_CANCEL_COMBAT, 0); - GetSession()->SendPacket(&data); -} - -void Player::SendAttackSwingBadFacingAttack() -{ - WorldPacket data(SMSG_ATTACKSWING_BADFACING, 0); - GetSession()->SendPacket(&data); -} - -void Player::SendAutoRepeatCancel(Unit* target) -{ - WorldPacket data(SMSG_CANCEL_AUTO_REPEAT, target->GetPackGUID().size()); - data << target->GetPackGUID(); - GetSession()->SendPacket(&data); -} - -void Player::SendExplorationExperience(uint32 Area, uint32 Experience) -{ - WorldPacket data(SMSG_EXPLORATION_EXPERIENCE, 8); - data << uint32(Area); - data << uint32(Experience); - GetSession()->SendPacket(&data); -} - -void Player::SendDungeonDifficulty(bool IsInGroup) -{ - uint8 val = 0x00000001; - WorldPacket data(MSG_SET_DUNGEON_DIFFICULTY, 12); - data << uint32(GetDungeonDifficulty()); - data << uint32(val); - data << uint32(IsInGroup); - GetSession()->SendPacket(&data); -} - -void Player::SendRaidDifficulty(bool IsInGroup) -{ - uint8 val = 0x00000001; - WorldPacket data(MSG_SET_RAID_DIFFICULTY, 12); - data << uint32(GetRaidDifficulty()); - data << uint32(val); - data << uint32(IsInGroup); - GetSession()->SendPacket(&data); -} - -void Player::SendResetFailedNotify(uint32 mapid) -{ - WorldPacket data(SMSG_RESET_FAILED_NOTIFY, 4); - data << uint32(mapid); - GetSession()->SendPacket(&data); -} - -/// Reset all solo instances and optionally send a message on success for each -void Player::ResetInstances(InstanceResetMethod method, bool isRaid) -{ - // method can be INSTANCE_RESET_ALL, INSTANCE_RESET_CHANGE_DIFFICULTY, INSTANCE_RESET_GROUP_JOIN - - // we assume that when the difficulty changes, all instances that can be reset will be - Difficulty diff = GetDifficulty(isRaid); - - for (BoundInstancesMap::iterator itr = m_boundInstances[diff].begin(); itr != m_boundInstances[diff].end();) - { - DungeonPersistentState* state = itr->second.state; - const MapEntry* entry = sMapStore.LookupEntry(itr->first); - if (!entry || entry->IsRaid() != isRaid || !state->CanReset()) - { - ++itr; - continue; - } - - if (method == INSTANCE_RESET_ALL) - { - // the "reset all instances" method can only reset normal maps - if (entry->map_type == MAP_RAID || diff == DUNGEON_DIFFICULTY_HEROIC) - { - ++itr; - continue; - } - } - - // if the map is loaded, reset it - if (Map* map = sMapMgr.FindMap(state->GetMapId(), state->GetInstanceId())) - if (map->IsDungeon()) - ((DungeonMap*)map)->Reset(method); - - // since this is a solo instance there should not be any players inside - if (method == INSTANCE_RESET_ALL || method == INSTANCE_RESET_CHANGE_DIFFICULTY) - SendResetInstanceSuccess(state->GetMapId()); - - state->DeleteFromDB(); - m_boundInstances[diff].erase(itr++); - - // the following should remove the instance save from the manager and delete it as well - state->RemovePlayer(this); - } -} - -void Player::SendResetInstanceSuccess(uint32 MapId) -{ - WorldPacket data(SMSG_INSTANCE_RESET, 4); - data << uint32(MapId); - GetSession()->SendPacket(&data); -} - -void Player::SendResetInstanceFailed(uint32 reason, uint32 MapId) -{ - // TODO: find what other fail reasons there are besides players in the instance - WorldPacket data(SMSG_INSTANCE_RESET_FAILED, 4); - data << uint32(reason); - data << uint32(MapId); - GetSession()->SendPacket(&data); -} - -/*********************************************************/ -/*** Update timers ***/ -/*********************************************************/ - -/// checks the 15 afk reports per 5 minutes limit -void Player::UpdateAfkReport(time_t currTime) -{ - if (m_bgData.bgAfkReportedTimer <= currTime) - { - m_bgData.bgAfkReportedCount = 0; - m_bgData.bgAfkReportedTimer = currTime + 5 * MINUTE; - } -} - -void Player::UpdateContestedPvP(uint32 diff) -{ - if (!m_contestedPvPTimer || isInCombat()) - return; - if (m_contestedPvPTimer <= diff) - { - ResetContestedPvP(); - } - else - m_contestedPvPTimer -= diff; -} - -void Player::UpdatePvPFlag(time_t currTime) -{ - if (!IsPvP()) - return; - if (pvpInfo.endTimer == 0 || currTime < (pvpInfo.endTimer + 300)) - return; - - UpdatePvP(false); -} - -void Player::UpdateDuelFlag(time_t currTime) -{ - if (!duel || duel->startTimer == 0 || currTime < duel->startTimer + 3) - return; - - SetUInt32Value(PLAYER_DUEL_TEAM, 1); - duel->opponent->SetUInt32Value(PLAYER_DUEL_TEAM, 2); - - duel->startTimer = 0; - duel->startTime = currTime; - duel->opponent->duel->startTimer = 0; - duel->opponent->duel->startTime = currTime; -} - -void Player::RemovePet(PetSaveMode mode) -{ - if (Pet* pet = GetPet()) - pet->Unsummon(mode, this); -} - -void Player::BuildPlayerChat(WorldPacket* data, uint8 msgtype, const std::string& text, uint32 language, const char* addonPrefix) const -{ - *data << uint8(msgtype); - *data << uint32(language); - *data << GetObjectGuid(); - *data << uint32(0); // constant unknown time 4.3.4 - if (addonPrefix) - *data << addonPrefix; - else - *data << GetObjectGuid(); - *data << uint32(text.length() + 1); - *data << text; - *data << uint8(GetChatTag()); -} - -void Player::Say(const std::string& text, const uint32 language) -{ - WorldPacket data(SMSG_MESSAGECHAT, 200); - BuildPlayerChat(&data, CHAT_MSG_SAY, text, language); - SendMessageToSetInRange(&data, sWorld.getConfig(CONFIG_FLOAT_LISTEN_RANGE_SAY), true); -} - -void Player::Yell(const std::string& text, const uint32 language) -{ - WorldPacket data(SMSG_MESSAGECHAT, 200); - BuildPlayerChat(&data, CHAT_MSG_YELL, text, language); - SendMessageToSetInRange(&data, sWorld.getConfig(CONFIG_FLOAT_LISTEN_RANGE_YELL), true); -} - -void Player::TextEmote(const std::string& text) -{ - WorldPacket data(SMSG_MESSAGECHAT, 200); - BuildPlayerChat(&data, CHAT_MSG_EMOTE, text, LANG_UNIVERSAL); - SendMessageToSetInRange(&data, sWorld.getConfig(CONFIG_FLOAT_LISTEN_RANGE_TEXTEMOTE), true, !sWorld.getConfig(CONFIG_BOOL_ALLOW_TWO_SIDE_INTERACTION_CHAT)); -} - -void Player::Whisper(const std::string& text, uint32 language, ObjectGuid receiver) -{ - Player* rPlayer = sObjectMgr.GetPlayer(receiver); - - WorldPacket data(SMSG_MESSAGECHAT, 200); - BuildPlayerChat(&data, CHAT_MSG_WHISPER, text, language); - rPlayer->GetSession()->SendPacket(&data); - - data.Initialize(SMSG_MESSAGECHAT, 200); - rPlayer->BuildPlayerChat(&data, CHAT_MSG_WHISPER_INFORM, text, language); - GetSession()->SendPacket(&data); - - if (!isAcceptWhispers()) - { - SetAcceptWhispers(true); - ChatHandler(this).SendSysMessage(LANG_COMMAND_WHISPERON); - } - - // announce afk or dnd message - if (rPlayer->isAFK()) - ChatHandler(this).PSendSysMessage(LANG_PLAYER_AFK, rPlayer->GetName(), rPlayer->autoReplyMsg.c_str()); - else if (rPlayer->isDND()) - ChatHandler(this).PSendSysMessage(LANG_PLAYER_DND, rPlayer->GetName(), rPlayer->autoReplyMsg.c_str()); -} - -void Player::WhisperAddon(const std::string& text, const std::string& prefix, ObjectGuid receiver) -{ - Player* rPlayer = sObjectMgr.GetPlayer(receiver); - - std::string _text(text); - - WorldPacket data(SMSG_MESSAGECHAT, 200); - BuildPlayerChat(&data, CHAT_MSG_WHISPER, _text, LANG_UNIVERSAL, prefix.c_str()); - rPlayer->GetSession()->SendPacket(&data); -} - -void Player::PetSpellInitialize() -{ - Pet* pet = GetPet(); - - if (!pet) - return; - - DEBUG_LOG("Pet Spells Groups"); - - CharmInfo* charmInfo = pet->GetCharmInfo(); - - WorldPacket data(SMSG_PET_SPELLS, 8 + 2 + 4 + 4 + 4 * MAX_UNIT_ACTION_BAR_INDEX + 1 + 1); - data << pet->GetObjectGuid(); - data << uint16(pet->GetCreatureInfo()->family); // creature family (required for pet talents) - data << uint32(0); - data << uint8(charmInfo->GetReactState()) << uint8(charmInfo->GetCommandState()) << uint16(0); - - // action bar loop - charmInfo->BuildActionBar(&data); - - size_t spellsCountPos = data.wpos(); - - // spells count - uint8 addlist = 0; - data << uint8(addlist); // placeholder - - if (pet->IsPermanentPetFor(this)) - { - // spells loop - for (PetSpellMap::const_iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end(); ++itr) - { - if (itr->second.state == PETSPELL_REMOVED) - continue; - - data << uint32(MAKE_UNIT_ACTION_BUTTON(itr->first, itr->second.active)); - ++addlist; - } - } - - data.put(spellsCountPos, addlist); - - uint8 cooldownsCount = pet->m_CreatureSpellCooldowns.size() + pet->m_CreatureCategoryCooldowns.size(); - data << uint8(cooldownsCount); - - time_t curTime = time(NULL); - - for (CreatureSpellCooldowns::const_iterator itr = pet->m_CreatureSpellCooldowns.begin(); itr != pet->m_CreatureSpellCooldowns.end(); ++itr) - { - time_t cooldown = (itr->second > curTime) ? (itr->second - curTime) * IN_MILLISECONDS : 0; - - data << uint32(itr->first); // spellid - data << uint16(0); // spell category? - data << uint32(cooldown); // cooldown - data << uint32(0); // category cooldown - } - - for (CreatureSpellCooldowns::const_iterator itr = pet->m_CreatureCategoryCooldowns.begin(); itr != pet->m_CreatureCategoryCooldowns.end(); ++itr) - { - time_t cooldown = (itr->second > curTime) ? (itr->second - curTime) * IN_MILLISECONDS : 0; - - data << uint32(itr->first); // spellid - data << uint16(0); // spell category? - data << uint32(0); // cooldown - data << uint32(cooldown); // category cooldown - } - - GetSession()->SendPacket(&data); -} - -void Player::SendPetGUIDs() -{ - if (!GetPetGuid()) - return; - - // Later this function might get modified for multiple guids - WorldPacket data(SMSG_PET_GUIDS, 12); - data << uint32(1); // count - data << ObjectGuid(GetPetGuid()); - GetSession()->SendPacket(&data); -} - -void Player::PossessSpellInitialize() -{ - Unit* charm = GetCharm(); - - if (!charm) - return; - - CharmInfo* charmInfo = charm->GetCharmInfo(); - - if (!charmInfo) - { - sLog.outError("Player::PossessSpellInitialize(): charm (GUID: %u TypeId: %u) has no charminfo!", charm->GetGUIDLow(), charm->GetTypeId()); - return; - } - - WorldPacket data(SMSG_PET_SPELLS, 8 + 2 + 4 + 4 + 4 * MAX_UNIT_ACTION_BAR_INDEX + 1 + 1); - data << charm->GetObjectGuid(); - data << uint16(0); - data << uint32(0); - data << uint32(0); - - charmInfo->BuildActionBar(&data); - - data << uint8(0); // spells count - data << uint8(0); // cooldowns count - - GetSession()->SendPacket(&data); -} - -void Player::CharmSpellInitialize() -{ - Unit* charm = GetCharm(); - - if (!charm) - return; - - CharmInfo* charmInfo = charm->GetCharmInfo(); - if (!charmInfo) - { - sLog.outError("Player::CharmSpellInitialize(): the player's charm (GUID: %u TypeId: %u) has no charminfo!", charm->GetGUIDLow(), charm->GetTypeId()); - return; - } - - uint8 addlist = 0; - - if (charm->GetTypeId() != TYPEID_PLAYER) - { - CreatureInfo const* cinfo = ((Creature*)charm)->GetCreatureInfo(); - - if (cinfo && cinfo->type == CREATURE_TYPE_DEMON && getClass() == CLASS_WARLOCK) - { - for (uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i) - { - if (charmInfo->GetCharmSpell(i)->GetAction()) - ++addlist; - } - } - } - - WorldPacket data(SMSG_PET_SPELLS, 8 + 2 + 4 + 4 + 4 * MAX_UNIT_ACTION_BAR_INDEX + 1 + 4 * addlist + 1); - data << charm->GetObjectGuid(); - data << uint16(0); - data << uint32(0); - - if (charm->GetTypeId() != TYPEID_PLAYER) - data << uint8(charmInfo->GetReactState()) << uint8(charmInfo->GetCommandState()) << uint16(0); - else - data << uint8(0) << uint8(0) << uint16(0); - - charmInfo->BuildActionBar(&data); - - data << uint8(addlist); - - if (addlist) - { - for (uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i) - { - CharmSpellEntry* cspell = charmInfo->GetCharmSpell(i); - if (cspell->GetAction()) - data << uint32(cspell->packedData); - } - } - - data << uint8(0); // cooldowns count - - GetSession()->SendPacket(&data); -} - -void Player::RemovePetActionBar() -{ - WorldPacket data(SMSG_PET_SPELLS, 8); - data << ObjectGuid(); - SendDirectMessage(&data); -} - -void Player::AddSpellMod(Aura* aura, bool apply) -{ - Modifier const* mod = aura->GetModifier(); - Opcodes opcode = (mod->m_auraname == SPELL_AURA_ADD_FLAT_MODIFIER) ? SMSG_SET_FLAT_SPELL_MODIFIER : SMSG_SET_PCT_SPELL_MODIFIER; - - uint32 modTypeCount = 0; // count of mods per one mod->op - WorldPacket data(opcode, 4 + 4 + 1 + 1 + 4); - data << uint32(1); // count of different mod->op's in packet - size_t writePos = data.wpos(); - data << uint32(modTypeCount); - data << uint8(mod->m_miscvalue); - for (int eff = 0; eff < 96; ++eff) - { - uint64 _mask = 0; - uint32 _mask2 = 0; - - if (eff < 64) - _mask = uint64(1) << (eff - 0); - else - _mask2 = uint32(1) << (eff - 64); - - if (aura->GetAuraSpellClassMask().IsFitToFamilyMask(_mask, _mask2)) - { - int32 val = 0; - for (AuraList::const_iterator itr = m_spellMods[mod->m_miscvalue].begin(); itr != m_spellMods[mod->m_miscvalue].end(); ++itr) - { - if ((*itr)->GetModifier()->m_auraname == mod->m_auraname && ((*itr)->GetAuraSpellClassMask().IsFitToFamilyMask(_mask, _mask2))) - val += (*itr)->GetModifier()->m_amount; - } - val += apply ? mod->m_amount : -(mod->m_amount); - data << uint8(eff); - data << float(val); - ++modTypeCount; - } - } - data.put(writePos, modTypeCount); - SendDirectMessage(&data); - - if (apply) - m_spellMods[mod->m_miscvalue].push_back(aura); - else - m_spellMods[mod->m_miscvalue].remove(aura); -} - -template T Player::ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell const* /*spell*/) -{ - SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellId); - if (!spellInfo) - return 0; - - int32 totalpct = 0; - int32 totalflat = 0; - for (AuraList::iterator itr = m_spellMods[op].begin(); itr != m_spellMods[op].end(); ++itr) - { - Aura* aura = *itr; - - Modifier const* mod = aura->GetModifier(); - - if (!aura->isAffectedOnSpell(spellInfo)) - continue; - - if (mod->m_auraname == SPELL_AURA_ADD_FLAT_MODIFIER) - totalflat += mod->m_amount; - else - { - // skip percent mods for null basevalue (most important for spell mods with charges ) - if (basevalue == T(0)) - continue; - - // special case (skip >10sec spell casts for instant cast setting) - if (mod->m_miscvalue == SPELLMOD_CASTING_TIME - && basevalue >= T(10 * IN_MILLISECONDS) && mod->m_amount <= -100) - continue; - - totalpct += mod->m_amount; - } - } - - float diff = (float)basevalue * (float)totalpct / 100.0f + (float)totalflat; - basevalue = T((float)basevalue + diff); - return T(diff); -} - -template int32 Player::ApplySpellMod(uint32 spellId, SpellModOp op, int32& basevalue, Spell const* spell); -template uint32 Player::ApplySpellMod(uint32 spellId, SpellModOp op, uint32& basevalue, Spell const* spell); -template float Player::ApplySpellMod(uint32 spellId, SpellModOp op, float& basevalue, Spell const* spell); - -// send Proficiency -void Player::SendProficiency(ItemClass itemClass, uint32 itemSubclassMask) -{ - WorldPacket data(SMSG_SET_PROFICIENCY, 1 + 4); - data << uint8(itemClass) << uint32(itemSubclassMask); - GetSession()->SendPacket(&data); -} - -void Player::RemovePetitionsAndSigns(ObjectGuid guid) -{ - uint32 lowguid = guid.GetCounter(); - - QueryResult* result = NULL; - result = CharacterDatabase.PQuery("SELECT ownerguid,petitionguid FROM petition_sign WHERE playerguid = '%u'", lowguid); - if (result) - { - do // this part effectively does nothing, since the deletion / modification only takes place _after_ the PetitionQuery. Though I don't know if the result remains intact if I execute the delete query beforehand. - { - // and SendPetitionQueryOpcode reads data from the DB - Field* fields = result->Fetch(); - ObjectGuid ownerguid = ObjectGuid(HIGHGUID_PLAYER, fields[0].GetUInt32()); - ObjectGuid petitionguid = ObjectGuid(HIGHGUID_ITEM, fields[1].GetUInt32()); - - // send update if charter owner in game - Player* owner = sObjectMgr.GetPlayer(ownerguid); - if (owner) - owner->GetSession()->SendPetitionQueryOpcode(petitionguid); - } - while (result->NextRow()); - - delete result; - - CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE playerguid = '%u'", lowguid); - } - - CharacterDatabase.BeginTransaction(); - CharacterDatabase.PExecute("DELETE FROM petition WHERE ownerguid = '%u'", lowguid); - CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE ownerguid = '%u'", lowguid); - CharacterDatabase.CommitTransaction(); -} - -void Player::LeaveAllArenaTeams(ObjectGuid guid) -{ - uint32 lowguid = guid.GetCounter(); - QueryResult* result = CharacterDatabase.PQuery("SELECT arena_team_member.arenateamid FROM arena_team_member JOIN arena_team ON arena_team_member.arenateamid = arena_team.arenateamid WHERE guid='%u'", lowguid); - if (!result) - return; - - do - { - Field* fields = result->Fetch(); - if (uint32 at_id = fields[0].GetUInt32()) - if (ArenaTeam* at = sObjectMgr.GetArenaTeamById(at_id)) - at->DelMember(guid); - } - while (result->NextRow()); - - delete result; -} - -void Player::SetRestBonus(float rest_bonus_new) -{ - // Prevent resting on max level - if (getLevel() >= sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL)) - rest_bonus_new = 0; - - if (rest_bonus_new < 0) - rest_bonus_new = 0; - - float rest_bonus_max = (float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP) * 1.5f / 2.0f; - - if (rest_bonus_new > rest_bonus_max) - m_rest_bonus = rest_bonus_max; - else - m_rest_bonus = rest_bonus_new; - - // update data for client - if (m_rest_bonus > 10) - SetByteValue(PLAYER_BYTES_2, 3, REST_STATE_RESTED); - else if (m_rest_bonus <= 1) - SetByteValue(PLAYER_BYTES_2, 3, REST_STATE_NORMAL); - - // RestTickUpdate - SetUInt32Value(PLAYER_REST_STATE_EXPERIENCE, uint32(m_rest_bonus)); -} - -void Player::HandleStealthedUnitsDetection() -{ - std::list stealthedUnits; - - MaNGOS::AnyStealthedCheck u_check(this); - MaNGOS::UnitListSearcher searcher(stealthedUnits, u_check); - Cell::VisitAllObjects(this, searcher, MAX_PLAYER_STEALTH_DETECT_RANGE); - - WorldObject const* viewPoint = GetCamera().GetBody(); - - for (std::list::const_iterator i = stealthedUnits.begin(); i != stealthedUnits.end(); ++i) - { - if ((*i) == this) - continue; - - bool hasAtClient = HaveAtClient((*i)); - bool hasDetected = (*i)->isVisibleForOrDetect(this, viewPoint, true); - - if (hasDetected) - { - if (!hasAtClient) - { - ObjectGuid i_guid = (*i)->GetObjectGuid(); - (*i)->SendCreateUpdateToPlayer(this); - m_clientGUIDs.insert(i_guid); - - DEBUG_FILTER_LOG(LOG_FILTER_VISIBILITY_CHANGES, "%s is detected in stealth by player %u. Distance = %f", i_guid.GetString().c_str(), GetGUIDLow(), GetDistance(*i)); - - // target aura duration for caster show only if target exist at caster client - // send data at target visibility change (adding to client) - if ((*i) != this && (*i)->isType(TYPEMASK_UNIT)) - SendAurasForTarget(*i); - } - } - else - { - if (hasAtClient) - { - (*i)->DestroyForPlayer(this); - m_clientGUIDs.erase((*i)->GetObjectGuid()); - } - } - } -} - -bool Player::ActivateTaxiPathTo(std::vector const& nodes, Creature* npc /*= NULL*/, uint32 spellid /*= 0*/) -{ - if (nodes.size() < 2) - return false; - - // not let cheating with start flight in time of logout process || if casting not finished || while in combat || if not use Spell's with EffectSendTaxi - if (GetSession()->isLogingOut() || isInCombat()) - { - GetSession()->SendActivateTaxiReply(ERR_TAXIPLAYERBUSY); - return false; - } - - if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE)) - return false; - - // taximaster case - if (npc) - { - // not let cheating with start flight mounted - if (IsMounted()) - { - GetSession()->SendActivateTaxiReply(ERR_TAXIPLAYERALREADYMOUNTED); - return false; - } - - if (IsInDisallowedMountForm()) - { - GetSession()->SendActivateTaxiReply(ERR_TAXIPLAYERSHAPESHIFTED); - return false; - } - - // not let cheating with start flight in time of logout process || if casting not finished || while in combat || if not use Spell's with EffectSendTaxi - if (IsNonMeleeSpellCasted(false)) - { - GetSession()->SendActivateTaxiReply(ERR_TAXIPLAYERBUSY); - return false; - } - } - // cast case or scripted call case - else - { - RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); - - if (IsInDisallowedMountForm()) - RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT); - - if (Spell* spell = GetCurrentSpell(CURRENT_GENERIC_SPELL)) - if (spell->m_spellInfo->Id != spellid) - InterruptSpell(CURRENT_GENERIC_SPELL, false); - - InterruptSpell(CURRENT_AUTOREPEAT_SPELL, false); - - if (Spell* spell = GetCurrentSpell(CURRENT_CHANNELED_SPELL)) - if (spell->m_spellInfo->Id != spellid) - InterruptSpell(CURRENT_CHANNELED_SPELL, true); - } - - uint32 sourcenode = nodes[0]; - - // starting node too far away (cheat?) - TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(sourcenode); - if (!node) - { - GetSession()->SendActivateTaxiReply(ERR_TAXINOSUCHPATH); - return false; - } - - // check node starting pos data set case if provided - if (node->x != 0.0f || node->y != 0.0f || node->z != 0.0f) - { - if (node->map_id != GetMapId() || - (node->x - GetPositionX()) * (node->x - GetPositionX()) + - (node->y - GetPositionY()) * (node->y - GetPositionY()) + - (node->z - GetPositionZ()) * (node->z - GetPositionZ()) > - (2 * INTERACTION_DISTANCE) * (2 * INTERACTION_DISTANCE) * (2 * INTERACTION_DISTANCE)) - { - GetSession()->SendActivateTaxiReply(ERR_TAXITOOFARAWAY); - return false; - } - } - // node must have pos if taxi master case (npc != NULL) - else if (npc) - { - GetSession()->SendActivateTaxiReply(ERR_TAXIUNSPECIFIEDSERVERERROR); - return false; - } - - // Prepare to flight start now - - // stop combat at start taxi flight if any - CombatStop(); - - // stop trade (client cancel trade at taxi map open but cheating tools can be used for reopen it) - TradeCancel(true); - - // clean not finished taxi path if any - m_taxi.ClearTaxiDestinations(); - - // 0 element current node - m_taxi.AddTaxiDestination(sourcenode); - - // fill destinations path tail - uint32 sourcepath = 0; - uint32 totalcost = 0; - - uint32 prevnode = sourcenode; - uint32 lastnode = 0; - - for (uint32 i = 1; i < nodes.size(); ++i) - { - uint32 path, cost; - - lastnode = nodes[i]; - sObjectMgr.GetTaxiPath(prevnode, lastnode, path, cost); - - if (!path) - { - m_taxi.ClearTaxiDestinations(); - return false; - } - - totalcost += cost; - - if (prevnode == sourcenode) - sourcepath = path; - - m_taxi.AddTaxiDestination(lastnode); - - prevnode = lastnode; - } - - // get mount model (in case non taximaster (npc==NULL) allow more wide lookup) - uint32 mount_display_id = sObjectMgr.GetTaxiMountDisplayId(sourcenode, GetTeam(), npc == NULL); - - // in spell case allow 0 model - if ((mount_display_id == 0 && spellid == 0) || sourcepath == 0) - { - GetSession()->SendActivateTaxiReply(ERR_TAXIUNSPECIFIEDSERVERERROR); - - m_taxi.ClearTaxiDestinations(); - return false; - } - - uint64 money = GetMoney(); - - if (npc) - totalcost = (uint32)ceil(totalcost * GetReputationPriceDiscount(npc)); - - if (money < totalcost) - { - GetSession()->SendActivateTaxiReply(ERR_TAXINOTENOUGHMONEY); - - m_taxi.ClearTaxiDestinations(); - return false; - } - - // Checks and preparations done, DO FLIGHT - ModifyMoney(-(int64)totalcost); - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING, totalcost); - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN, 1); - - // prevent stealth flight - RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); - - GetSession()->SendActivateTaxiReply(ERR_TAXIOK); - GetSession()->SendDoFlight(mount_display_id, sourcepath); - - return true; -} - -bool Player::ActivateTaxiPathTo(uint32 taxi_path_id, uint32 spellid /*= 0*/) -{ - TaxiPathEntry const* entry = sTaxiPathStore.LookupEntry(taxi_path_id); - if (!entry) - return false; - - std::vector nodes; - - nodes.resize(2); - nodes[0] = entry->from; - nodes[1] = entry->to; - - return ActivateTaxiPathTo(nodes, NULL, spellid); -} - -void Player::ContinueTaxiFlight() -{ - uint32 sourceNode = m_taxi.GetTaxiSource(); - if (!sourceNode) - return; - - DEBUG_LOG("WORLD: Restart character %u taxi flight", GetGUIDLow()); - - uint32 mountDisplayId = sObjectMgr.GetTaxiMountDisplayId(sourceNode, GetTeam(), true); - uint32 path = m_taxi.GetCurrentTaxiPath(); - - // search appropriate start path node - uint32 startNode = 0; - - TaxiPathNodeList const& nodeList = sTaxiPathNodesByPath[path]; - - float distPrev = MAP_SIZE * MAP_SIZE; - float distNext = - (nodeList[0].x - GetPositionX()) * (nodeList[0].x - GetPositionX()) + - (nodeList[0].y - GetPositionY()) * (nodeList[0].y - GetPositionY()) + - (nodeList[0].z - GetPositionZ()) * (nodeList[0].z - GetPositionZ()); - - for (uint32 i = 1; i < nodeList.size(); ++i) - { - TaxiPathNodeEntry const& node = nodeList[i]; - TaxiPathNodeEntry const& prevNode = nodeList[i - 1]; - - // skip nodes at another map - if (node.mapid != GetMapId()) - continue; - - distPrev = distNext; - - distNext = - (node.x - GetPositionX()) * (node.x - GetPositionX()) + - (node.y - GetPositionY()) * (node.y - GetPositionY()) + - (node.z - GetPositionZ()) * (node.z - GetPositionZ()); - - float distNodes = - (node.x - prevNode.x) * (node.x - prevNode.x) + - (node.y - prevNode.y) * (node.y - prevNode.y) + - (node.z - prevNode.z) * (node.z - prevNode.z); - - if (distNext + distPrev < distNodes) - { - startNode = i; - break; - } - } - - GetSession()->SendDoFlight(mountDisplayId, path, startNode); -} - -void Player::ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs) -{ - // last check 4.3.4 - WorldPacket data(SMSG_SPELL_COOLDOWN, 8 + 1 + m_spells.size() * 8); - data << GetObjectGuid(); - data << uint8(0x0); // flags (0x1, 0x2) - time_t curTime = time(NULL); - for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr) - { - if (itr->second.state == PLAYERSPELL_REMOVED) - continue; - uint32 unSpellId = itr->first; - SpellEntry const* spellInfo = sSpellStore.LookupEntry(unSpellId); - if (!spellInfo) - { - MANGOS_ASSERT(spellInfo); - continue; - } - - // Not send cooldown for this spells - if (spellInfo->HasAttribute(SPELL_ATTR_DISABLED_WHILE_ACTIVE)) - continue; - - if ((idSchoolMask & GetSpellSchoolMask(spellInfo)) && GetSpellCooldownDelay(unSpellId) < unTimeMs) - { - data << uint32(unSpellId); - data << uint32(unTimeMs); // in m.secs - AddSpellCooldown(unSpellId, 0, curTime + unTimeMs / IN_MILLISECONDS); - } - } - GetSession()->SendPacket(&data); -} - -void Player::InitDataForForm(bool reapplyMods) -{ - ShapeshiftForm form = GetShapeshiftForm(); - - SpellShapeshiftFormEntry const* ssEntry = sSpellShapeshiftFormStore.LookupEntry(form); - if (ssEntry && ssEntry->attackSpeed) - { - SetAttackTime(BASE_ATTACK, ssEntry->attackSpeed); - SetAttackTime(OFF_ATTACK, ssEntry->attackSpeed); - SetAttackTime(RANGED_ATTACK, BASE_ATTACK_TIME); - } - else - SetRegularAttackTime(); - - switch (form) - { - case FORM_CAT: - { - if (getPowerType() != POWER_ENERGY) - setPowerType(POWER_ENERGY); - break; - } - case FORM_BEAR: - { - if (getPowerType() != POWER_RAGE) - setPowerType(POWER_RAGE); - break; - } - default: // 0, for example - { - ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(getClass()); - if (cEntry && cEntry->powerType < MAX_POWERS && uint32(getPowerType()) != cEntry->powerType) - setPowerType(Powers(cEntry->powerType)); - break; - } - } - - // update auras at form change, ignore this at mods reapply (.reset stats/etc) when form not change. - if (!reapplyMods) - UpdateEquipSpellsAtFormChange(); - - UpdateAttackPowerAndDamage(); - UpdateAttackPowerAndDamage(true); -} - -void Player::InitDisplayIds() -{ - PlayerInfo const* info = sObjectMgr.GetPlayerInfo(getRace(), getClass()); - if (!info) - { - sLog.outError("Player %u has incorrect race/class pair. Can't init display ids.", GetGUIDLow()); - return; - } - - // reset scale before reapply auras - SetObjectScale(DEFAULT_OBJECT_SCALE); - - uint8 gender = getGender(); - switch (gender) - { - case GENDER_FEMALE: - SetDisplayId(info->displayId_f); - SetNativeDisplayId(info->displayId_f); - break; - case GENDER_MALE: - SetDisplayId(info->displayId_m); - SetNativeDisplayId(info->displayId_m); - break; - default: - sLog.outError("Invalid gender %u for player", gender); - return; - } -} - -void Player::TakeExtendedCost(uint32 extendedCostId) -{ - ItemExtendedCostEntry const* extendedCost = sItemExtendedCostStore.LookupEntry(extendedCostId); - - for (uint8 i = 0; i < MAX_EXTENDED_COST_ITEMS; ++i) - { - if (extendedCost->reqitem[i]) - DestroyItemCount(extendedCost->reqitem[i], extendedCost->reqitemcount[i], true); - } - - for (int i = 0; i < MAX_EXTENDED_COST_CURRENCIES; ++i) - { - if (extendedCost->reqcur[i] == CURRENCY_NONE) - continue; - - if (extendedCost->IsSeasonCurrencyRequirement(i)) - continue; - - CurrencyTypesEntry const * entry = sCurrencyTypesStore.LookupEntry(extendedCost->reqcur[i]); - if (!entry) - continue; - - int32 cost = int32(extendedCost->reqcurrcount[i]); - ModifyCurrencyCount(entry->ID, -cost); - } -} - -// Return true is the bought item has a max count to force refresh of window by caller -bool Player::BuyItemFromVendorSlot(ObjectGuid vendorGuid, uint32 vendorslot, uint32 item, uint32 count, uint8 bag, uint8 slot) -{ - // cheating attempt - if (count < 1) count = 1; - - if (!isAlive()) - return false; - - ItemPrototype const* pProto = ObjectMgr::GetItemPrototype(item); - if (!pProto) - { - SendBuyError(BUY_ERR_CANT_FIND_ITEM, NULL, item, 0); - return false; - } - - Creature* pCreature = GetNPCIfCanInteractWith(vendorGuid, UNIT_NPC_FLAG_VENDOR); - if (!pCreature) - { - DEBUG_LOG("WORLD: BuyItemFromVendor - %s not found or you can't interact with him.", vendorGuid.GetString().c_str()); - SendBuyError(BUY_ERR_DISTANCE_TOO_FAR, NULL, item, 0); - return false; - } - - VendorItemData const* vItems = pCreature->GetVendorItems(); - VendorItemData const* tItems = pCreature->GetVendorTemplateItems(); - if ((!vItems || vItems->Empty()) && (!tItems || tItems->Empty())) - { - SendBuyError(BUY_ERR_CANT_FIND_ITEM, pCreature, item, 0); - return false; - } - - uint32 vCount = vItems ? vItems->GetItemCount() : 0; - uint32 tCount = tItems ? tItems->GetItemCount() : 0; - - if (vendorslot >= vCount + tCount) - { - SendBuyError(BUY_ERR_CANT_FIND_ITEM, pCreature, item, 0); - return false; - } - - VendorItem const* crItem = vendorslot < vCount ? vItems->GetItem(vendorslot) : tItems->GetItem(vendorslot - vCount); - if (!crItem) // store diff item (cheating) - { - SendBuyError(BUY_ERR_CANT_FIND_ITEM, pCreature, item, 0); - return false; - } - - if (crItem->item != item) // store diff item (cheating or special convert) - { - bool converted = false; - - // possible item converted for BoA case - ItemPrototype const* crProto = ObjectMgr::GetItemPrototype(crItem->item); - if (crProto->Flags & ITEM_FLAG_BOA && crProto->RequiredReputationFaction && - uint32(GetReputationRank(crProto->RequiredReputationFaction)) >= crProto->RequiredReputationRank) - converted = (sObjectMgr.GetItemConvert(crItem->item, getRaceMask()) != 0); - - if (!converted) - { - SendBuyError(BUY_ERR_CANT_FIND_ITEM, pCreature, item, 0); - return false; - } - } - - uint32 totalCount = count; - - // check current item amount if it limited - if (crItem->maxcount != 0) - { - if (pCreature->GetVendorItemCurrentCount(crItem) < totalCount) - { - SendBuyError(BUY_ERR_ITEM_ALREADY_SOLD, pCreature, item, 0); - return false; - } - } - - if (uint32(GetReputationRank(pProto->RequiredReputationFaction)) < pProto->RequiredReputationRank) - { - SendBuyError(BUY_ERR_REPUTATION_REQUIRE, pCreature, item, 0); - return false; - } - - if (uint32 extendedCostId = crItem->ExtendedCost) - { - if (pProto->BuyCount != count) - { - SendEquipError(EQUIP_ERR_CANT_BUY_QUANTITY, NULL, NULL); - return false; - } - - ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(extendedCostId); - if (!iece) - { - sLog.outError("Item %u have wrong ExtendedCost field value %u", pProto->ItemId, extendedCostId); - return false; - } - - // item base price - for (uint8 i = 0; i < MAX_EXTENDED_COST_ITEMS; ++i) - { - if (iece->reqitem[i] && !HasItemCount(iece->reqitem[i], iece->reqitemcount[i])) - { - SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL, NULL); - return false; - } - } - - // currency price - for (uint8 i = 0; i < MAX_EXTENDED_COST_CURRENCIES; ++i) - { - if (iece->reqcur[i] == CURRENCY_NONE) - continue; - - CurrencyTypesEntry const * costCurrency = sCurrencyTypesStore.LookupEntry(iece->reqcur[i]); - if (!costCurrency) - { - sLog.outError("Item %u has ExtendedCost %u with unexistent currency id %u", pProto->ItemId, extendedCostId, iece->reqcur[i]); - continue; - } - - int32 cost = int32(iece->reqcurrcount[i]); - - bool hasCount = iece->IsSeasonCurrencyRequirement(i) ? HasCurrencySeasonCount(iece->reqcur[i], cost) : HasCurrencyCount(iece->reqcur[i], cost); - if (!hasCount) - { - SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL); - return false; - } - } - - // check for personal arena rating requirement - if (GetMaxPersonalArenaRatingRequirement(iece->reqarenaslot) < iece->reqpersonalarenarating) - { - // probably not the proper equip err - SendEquipError(EQUIP_ERR_CANT_EQUIP_RANK, NULL, NULL); - return false; - } - } - - if (crItem->conditionId && !isGameMaster() && !sObjectMgr.IsPlayerMeetToCondition(crItem->conditionId, this, pCreature->GetMap(), pCreature, CONDITION_FROM_VENDOR)) - { - SendBuyError(BUY_ERR_CANT_FIND_ITEM, pCreature, item, 0); - return false; - } - - uint64 price = (crItem->ExtendedCost == 0 || pProto->Flags2 & ITEM_FLAG2_EXT_COST_REQUIRES_GOLD) ? pProto->BuyPrice * count : 0; - if (pProto->BuyCount > 1) - price = uint64(price / float(pProto->BuyCount) + 0.5f); - - // reputation discount - if (price) - price = uint64(floor(price * GetReputationPriceDiscount(pCreature))); - - if (GetMoney() < price) - { - SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, item, 0); - return false; - } - - Item* pItem = NULL; - - if ((bag == NULL_BAG && slot == NULL_SLOT) || IsInventoryPos(bag, slot)) - { - ItemPosCountVec dest; - InventoryResult msg = CanStoreNewItem(bag, slot, dest, item, totalCount); - if (msg != EQUIP_ERR_OK) - { - SendEquipError(msg, NULL, NULL, item); - return false; - } - - ModifyMoney(-int64(price)); - - if (crItem->ExtendedCost) - TakeExtendedCost(crItem->ExtendedCost); - - pItem = StoreNewItem(dest, item, true); - } - else if (IsEquipmentPos(bag, slot)) - { - if (totalCount != 1) - { - SendEquipError(EQUIP_ERR_ITEM_CANT_BE_EQUIPPED, NULL, NULL); - return false; - } - - uint16 dest; - InventoryResult msg = CanEquipNewItem(slot, dest, item, false); - if (msg != EQUIP_ERR_OK) - { - SendEquipError(msg, NULL, NULL, item); - return false; - } - - ModifyMoney(-int64(price)); - - if (crItem->ExtendedCost) - TakeExtendedCost(crItem->ExtendedCost); - - pItem = EquipNewItem(dest, item, true); - - if (pItem) - AutoUnequipOffhandIfNeed(); - } - else - { - SendEquipError(EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL); - return false; - } - - if (!pItem) - return false; - - uint32 new_count = pCreature->UpdateVendorItemCurrentCount(crItem, totalCount); - - WorldPacket data(SMSG_BUY_ITEM, 8 + 4 + 4 + 4); - data << pCreature->GetObjectGuid(); - data << uint32(vendorslot + 1); // numbered from 1 at client - data << uint32(crItem->maxcount > 0 ? new_count : 0xFFFFFFFF); - data << uint32(count); - GetSession()->SendPacket(&data); - - SendNewItem(pItem, totalCount, true, false, false); - - return crItem->maxcount != 0; -} - -bool Player::BuyCurrencyFromVendorSlot(ObjectGuid vendorGuid, uint32 vendorslot, uint32 currencyId, uint32 count) -{ - // cheating attempt - if (count < 1) count = 1; - - if (!isAlive()) - return false; - - CurrencyTypesEntry const* pCurrency = sCurrencyTypesStore.LookupEntry(currencyId); - if (!pCurrency) - return false; - - if (pCurrency->Category == CURRENCY_CATEGORY_META) - return false; - - Creature* pCreature = GetNPCIfCanInteractWith(vendorGuid, UNIT_NPC_FLAG_VENDOR); - if (!pCreature) - { - DEBUG_LOG("WORLD: BuyCurrencyFromVendorSlot - %s not found or you can't interact with him.", vendorGuid.GetString().c_str()); - return false; - } - - VendorItemData const* vItems = pCreature->GetVendorItems(); - VendorItemData const* tItems = pCreature->GetVendorTemplateItems(); - if ((!vItems || vItems->Empty()) && (!tItems || tItems->Empty())) - return false; - - uint32 vCount = vItems ? vItems->GetItemCount() : 0; - uint32 tCount = tItems ? tItems->GetItemCount() : 0; - - if (vendorslot >= vCount + tCount) - return false; - - VendorItem const* crItem = vendorslot < vCount ? vItems->GetItem(vendorslot) : tItems->GetItem(vendorslot - vCount); - if (!crItem) // store diff item (cheating) - return false; - - if (crItem->item != currencyId) // store diff item (cheating) - return false; - - if (!crItem->maxcount) - { - DEBUG_LOG("WORLD: BuyCurrencyFromVendorSlot - %s: crItem->maxcount (%u) == 0 for currency %u and player %s.", - vendorGuid.GetString().c_str(), crItem->maxcount, currencyId, GetGuidStr().c_str()); - return false; - } - - if (uint32 extendedCostId = crItem->ExtendedCost) - { - if (crItem->maxcount != count) - { - SendEquipError(EQUIP_ERR_CANT_BUY_QUANTITY, NULL, NULL); - return false; - } - - ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(extendedCostId); - if (!iece) - { - sLog.outError("WORLD: BuyCurrencyFromVendorSlot: Currency %u have wrong ExtendedCost field value %u for %s", currencyId, extendedCostId, vendorGuid.GetString().c_str()); - return false; - } - - // item base price - for (uint8 i = 0; i < MAX_EXTENDED_COST_ITEMS; ++i) - { - if (iece->reqitem[i] && !HasItemCount(iece->reqitem[i], iece->reqitemcount[i])) - { - SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL, NULL); - return false; - } - } - - // currency price - for (uint8 i = 0; i < MAX_EXTENDED_COST_CURRENCIES; ++i) - { - if (iece->reqcur[i] == CURRENCY_NONE) - continue; - - CurrencyTypesEntry const * costCurrency = sCurrencyTypesStore.LookupEntry(iece->reqcur[i]); - if (!costCurrency) - { - sLog.outError("Currency %u has ExtendedCost %u with unexistent currency id %u", currencyId, extendedCostId, iece->reqcur[i]); - continue; - } - - int32 cost = int32(iece->reqcurrcount[i]); - bool hasCount = iece->IsSeasonCurrencyRequirement(i) ? HasCurrencySeasonCount(iece->reqcur[i], cost) : HasCurrencyCount(iece->reqcur[i], cost); - if (!hasCount) - { - SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL); - return false; - } - } - - // check for personal arena rating requirement - if (GetMaxPersonalArenaRatingRequirement(iece->reqarenaslot) < iece->reqpersonalarenarating) - { - // probably not the proper equip err - SendEquipError(EQUIP_ERR_CANT_EQUIP_RANK, NULL, NULL); - return false; - } - } - else - { - SendBuyError(BUY_ERR_ITEM_SOLD_OUT, 0, 0, 0); - return false; - } - - if (uint32 totalCap = GetCurrencyTotalCap(pCurrency)) - { - if (GetCurrencyCount(currencyId) >= totalCap) - { - - SendBuyError(BUY_ERR_CANT_CARRY_MORE, 0, 0, 0); - return false; - } - } - - if (uint32 weekCap = GetCurrencyWeekCap(pCurrency)) - { - if (GetCurrencyWeekCount(currencyId) >= weekCap) - { - SendBuyError(BUY_ERR_CANT_CARRY_MORE, 0, 0, 0); - return false; - } - } - - if (crItem->ExtendedCost) - TakeExtendedCost(crItem->ExtendedCost); - - ModifyCurrencyCount(currencyId, crItem->maxcount, true, false, true); - - - DEBUG_LOG("WORLD: BuyCurrencyFromVendorSlot - %s: Player %s buys currency %u amount %u count %u.", - vendorGuid.GetString().c_str(), GetGuidStr().c_str(), currencyId, crItem->maxcount, count); - - return true; -} - -uint32 Player::GetMaxPersonalArenaRatingRequirement(uint32 minarenaslot) -{ - // returns the maximal personal arena rating that can be used to purchase items requiring this condition - // the personal rating of the arena team must match the required limit as well - // so return max[in arenateams](min(personalrating[teamtype], teamrating[teamtype])) - uint32 max_personal_rating = 0; - for (int i = minarenaslot; i < MAX_ARENA_SLOT; ++i) - { - if (ArenaTeam* at = sObjectMgr.GetArenaTeamById(GetArenaTeamId(i))) - { - uint32 p_rating = GetArenaPersonalRating(i); - uint32 t_rating = at->GetRating(); - p_rating = p_rating < t_rating ? p_rating : t_rating; - if (max_personal_rating < p_rating) - max_personal_rating = p_rating; - } - } - return max_personal_rating; -} - -void Player::UpdateHomebindTime(uint32 time) -{ - // GMs never get homebind timer online - if (m_InstanceValid || isGameMaster()) - { - if (m_HomebindTimer) // instance valid, but timer not reset - { - // hide reminder - WorldPacket data(SMSG_RAID_GROUP_ONLY, 4 + 4); - data << uint32(0); - data << uint32(ERR_RAID_GROUP_NONE); // error used only when timer = 0 - GetSession()->SendPacket(&data); - } - // instance is valid, reset homebind timer - m_HomebindTimer = 0; - } - else if (m_HomebindTimer > 0) - { - if (time >= m_HomebindTimer) - { - // teleport to nearest graveyard - RepopAtGraveyard(); - } - else - m_HomebindTimer -= time; - } - else - { - // instance is invalid, start homebind timer - m_HomebindTimer = 60000; - // send message to player - WorldPacket data(SMSG_RAID_GROUP_ONLY, 4 + 4); - data << uint32(m_HomebindTimer); - data << uint32(ERR_RAID_GROUP_NONE); // error used only when timer = 0 - GetSession()->SendPacket(&data); - DEBUG_LOG("PLAYER: Player '%s' (GUID: %u) will be teleported to homebind in 60 seconds", GetName(), GetGUIDLow()); - } -} - -void Player::UpdatePvP(bool state, bool ovrride) -{ - if (!state || ovrride) - { - SetPvP(state); - pvpInfo.endTimer = 0; - } - else - { - if (pvpInfo.endTimer != 0) - pvpInfo.endTimer = time(NULL); - else - SetPvP(state); - } -} - -void Player::AddSpellAndCategoryCooldowns(SpellEntry const* spellInfo, uint32 itemId, Spell* spell, bool infinityCooldown) -{ - // init cooldown values - uint32 cat = 0; - int32 rec = -1; - int32 catrec = -1; - - // some special item spells without correct cooldown in SpellInfo - // cooldown information stored in item prototype - - if (itemId) - { - if (ItemPrototype const* proto = ObjectMgr::GetItemPrototype(itemId)) - { - for (int idx = 0; idx < MAX_ITEM_PROTO_SPELLS; ++idx) - { - if (proto->Spells[idx].SpellId == spellInfo->Id) - { - cat = proto->Spells[idx].SpellCategory; - rec = proto->Spells[idx].SpellCooldown; - catrec = proto->Spells[idx].SpellCategoryCooldown; - break; - } - } - } - } - - // if no cooldown found above then base at DBC data - if (rec < 0 && catrec < 0) - { - cat = spellInfo->GetCategory(); - rec = spellInfo->GetRecoveryTime(); - catrec = spellInfo->GetCategoryRecoveryTime(); - } - - time_t curTime = time(NULL); - - time_t catrecTime; - time_t recTime; - - // overwrite time for selected category - if (infinityCooldown) - { - // use +MONTH as infinity mark for spell cooldown (will checked as MONTH/2 at save ans skipped) - // but not allow ignore until reset or re-login - catrecTime = catrec > 0 ? curTime + infinityCooldownDelay : 0; - recTime = rec > 0 ? curTime + infinityCooldownDelay : catrecTime; - } - else - { - // shoot spells used equipped item cooldown values already assigned in GetAttackTime(RANGED_ATTACK) - // prevent 0 cooldowns set by another way - if (rec <= 0 && catrec <= 0 && (cat == 76 || (IsAutoRepeatRangedSpell(spellInfo) && spellInfo->Id != SPELL_ID_AUTOSHOT))) - rec = GetAttackTime(RANGED_ATTACK); - - // Now we have cooldown data (if found any), time to apply mods - if (rec > 0) - ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, rec, spell); - - if (catrec > 0) - ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, catrec, spell); - - // replace negative cooldowns by 0 - if (rec < 0) rec = 0; - if (catrec < 0) catrec = 0; - - // no cooldown after applying spell mods - if (rec == 0 && catrec == 0) - return; - - catrecTime = catrec ? curTime + catrec / IN_MILLISECONDS : 0; - recTime = rec ? curTime + rec / IN_MILLISECONDS : catrecTime; - } - - // self spell cooldown - if (recTime > 0) - AddSpellCooldown(spellInfo->Id, itemId, recTime); - - // category spells - if (cat && catrec > 0) - { - SpellCategoryStore::const_iterator i_scstore = sSpellCategoryStore.find(cat); - if (i_scstore != sSpellCategoryStore.end()) - { - for (SpellCategorySet::const_iterator i_scset = i_scstore->second.begin(); i_scset != i_scstore->second.end(); ++i_scset) - { - if (*i_scset == spellInfo->Id) // skip main spell, already handled above - continue; - - AddSpellCooldown(*i_scset, itemId, catrecTime); - } - } - } -} - -void Player::AddSpellCooldown(uint32 spellid, uint32 itemid, time_t end_time) -{ - SpellCooldown sc; - sc.end = end_time; - sc.itemid = itemid; - m_spellCooldowns[spellid] = sc; -} - -void Player::SendCooldownEvent(SpellEntry const* spellInfo, uint32 itemId, Spell* spell) -{ - // start cooldowns at server side, if any - AddSpellAndCategoryCooldowns(spellInfo, itemId, spell); - - // Send activate cooldown timer (possible 0) at client side - WorldPacket data(SMSG_COOLDOWN_EVENT, (4 + 8)); - data << uint32(spellInfo->Id); - data << GetObjectGuid(); - SendDirectMessage(&data); -} - -void Player::UpdatePotionCooldown(Spell* spell) -{ - // no potion used in combat or still in combat - if (!m_lastPotionId || isInCombat()) - return; - - // Call not from spell cast, send cooldown event for item spells if no in combat - if (!spell) - { - // spell/item pair let set proper cooldown (except nonexistent charged spell cooldown spellmods for potions) - if (ItemPrototype const* proto = ObjectMgr::GetItemPrototype(m_lastPotionId)) - for (int idx = 0; idx < 5; ++idx) - if (proto->Spells[idx].SpellId && proto->Spells[idx].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE) - if (SpellEntry const* spellInfo = sSpellStore.LookupEntry(proto->Spells[idx].SpellId)) - SendCooldownEvent(spellInfo, m_lastPotionId); - } - // from spell cases (m_lastPotionId set in Spell::SendSpellCooldown) - else - SendCooldownEvent(spell->m_spellInfo, m_lastPotionId, spell); - - m_lastPotionId = 0; -} - -// slot to be excluded while counting -bool Player::EnchantmentFitsRequirements(uint32 enchantmentcondition, int8 slot) -{ - if (!enchantmentcondition) - return true; - - SpellItemEnchantmentConditionEntry const* Condition = sSpellItemEnchantmentConditionStore.LookupEntry(enchantmentcondition); - - if (!Condition) - return true; - - uint8 curcount[4] = {0, 0, 0, 0}; - - // counting current equipped gem colors - for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i) - { - if (i == slot) - continue; - Item* pItem2 = GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (pItem2 && !pItem2->IsBroken() && pItem2->GetProto()->Socket[0].Color) - { - for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT + 3; ++enchant_slot) - { - uint32 enchant_id = pItem2->GetEnchantmentId(EnchantmentSlot(enchant_slot)); - if (!enchant_id) - continue; - - SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id); - if (!enchantEntry) - continue; - - uint32 gemid = enchantEntry->GemID; - if (!gemid) - continue; - - ItemPrototype const* gemProto = sItemStorage.LookupEntry(gemid); - if (!gemProto) - continue; - - GemPropertiesEntry const* gemProperty = sGemPropertiesStore.LookupEntry(gemProto->GemProperties); - if (!gemProperty) - continue; - - uint8 GemColor = gemProperty->color; - - for (uint8 b = 0, tmpcolormask = 1; b < 4; ++b, tmpcolormask <<= 1) - { - if (tmpcolormask & GemColor) - ++curcount[b]; - } - } - } - } - - bool activate = true; - - for (int i = 0; i < 5; ++i) - { - if (!Condition->Color[i]) - continue; - - uint32 _cur_gem = curcount[Condition->Color[i] - 1]; - - // if have use them as count, else use from Condition - uint32 _cmp_gem = Condition->CompareColor[i] ? curcount[Condition->CompareColor[i] - 1] : Condition->Value[i]; - - switch (Condition->Comparator[i]) - { - case 2: // requires less than ( || ) gems - activate &= (_cur_gem < _cmp_gem) ? true : false; - break; - case 3: // requires more than ( || ) gems - activate &= (_cur_gem > _cmp_gem) ? true : false; - break; - case 5: // requires at least than ( || ) gems - activate &= (_cur_gem >= _cmp_gem) ? true : false; - break; - } - } - - DEBUG_LOG("Checking Condition %u, there are %u Meta Gems, %u Red Gems, %u Yellow Gems and %u Blue Gems, Activate:%s", enchantmentcondition, curcount[0], curcount[1], curcount[2], curcount[3], activate ? "yes" : "no"); - - return activate; -} - -void Player::CorrectMetaGemEnchants(uint8 exceptslot, bool apply) -{ - // cycle all equipped items - for (uint32 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot) - { - // enchants for the slot being socketed are handled by Player::ApplyItemMods - if (slot == exceptslot) - continue; - - Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, slot); - - if (!pItem || !pItem->GetProto()->Socket[0].Color) - continue; - - for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT + 3; ++enchant_slot) - { - uint32 enchant_id = pItem->GetEnchantmentId(EnchantmentSlot(enchant_slot)); - if (!enchant_id) - continue; - - SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id); - if (!enchantEntry) - continue; - - uint32 condition = enchantEntry->EnchantmentCondition; - if (condition) - { - // was enchant active with/without item? - bool wasactive = EnchantmentFitsRequirements(condition, apply ? exceptslot : -1); - // should it now be? - if (wasactive != EnchantmentFitsRequirements(condition, apply ? -1 : exceptslot)) - { - // ignore item gem conditions - // if state changed, (dis)apply enchant - ApplyEnchantment(pItem, EnchantmentSlot(enchant_slot), !wasactive, true, true); - } - } - } - } -} - -// if false -> then toggled off if was on| if true -> toggled on if was off AND meets requirements -void Player::ToggleMetaGemsActive(uint8 exceptslot, bool apply) -{ - // cycle all equipped items - for (int slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot) - { - // enchants for the slot being socketed are handled by WorldSession::HandleSocketOpcode(WorldPacket& recv_data) - if (slot == exceptslot) - continue; - - Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, slot); - - if (!pItem || !pItem->GetProto()->Socket[0].Color) // if item has no sockets or no item is equipped go to next item - continue; - - // cycle all (gem)enchants - for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT + 3; ++enchant_slot) - { - uint32 enchant_id = pItem->GetEnchantmentId(EnchantmentSlot(enchant_slot)); - if (!enchant_id) // if no enchant go to next enchant(slot) - continue; - - SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id); - if (!enchantEntry) - continue; - - // only metagems to be (de)activated, so only enchants with condition - uint32 condition = enchantEntry->EnchantmentCondition; - if (condition) - ApplyEnchantment(pItem, EnchantmentSlot(enchant_slot), apply); - } - } -} - -void Player::SetBattleGroundEntryPoint() -{ - // Taxi path store - if (!m_taxi.empty()) - { - m_bgData.mountSpell = 0; - m_bgData.taxiPath[0] = m_taxi.GetTaxiSource(); - m_bgData.taxiPath[1] = m_taxi.GetTaxiDestination(); - - // On taxi we don't need check for dungeon - m_bgData.joinPos = WorldLocation(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation()); - m_bgData.m_needSave = true; - return; - } - else - { - m_bgData.ClearTaxiPath(); - - // Mount spell id storing - if (IsMounted()) - { - AuraList const& auras = GetAurasByType(SPELL_AURA_MOUNTED); - if (!auras.empty()) - m_bgData.mountSpell = (*auras.begin())->GetId(); - } - else - m_bgData.mountSpell = 0; - - // If map is dungeon find linked graveyard - if (GetMap()->IsDungeon()) - { - if (const WorldSafeLocsEntry* entry = sObjectMgr.GetClosestGraveYard(GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId(), GetTeam())) - { - m_bgData.joinPos = WorldLocation(entry->map_id, entry->x, entry->y, entry->z, 0.0f); - m_bgData.m_needSave = true; - return; - } - else - sLog.outError("SetBattleGroundEntryPoint: Dungeon map %u has no linked graveyard, setting home location as entry point.", GetMapId()); - } - // If new entry point is not BG or arena set it - else if (!GetMap()->IsBattleGroundOrArena()) - { - m_bgData.joinPos = WorldLocation(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation()); - m_bgData.m_needSave = true; - return; - } - } - - // In error cases use homebind position - m_bgData.joinPos = WorldLocation(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ, 0.0f); - m_bgData.m_needSave = true; -} - -void Player::LeaveBattleground(bool teleportToEntryPoint) -{ - if (BattleGround* bg = GetBattleGround()) - { - bg->RemovePlayerAtLeave(GetObjectGuid(), teleportToEntryPoint, true); - - // call after remove to be sure that player resurrected for correct cast - if (bg->isBattleGround() && !isGameMaster() && sWorld.getConfig(CONFIG_BOOL_BATTLEGROUND_CAST_DESERTER)) - { - if (bg->GetStatus() == STATUS_IN_PROGRESS || bg->GetStatus() == STATUS_WAIT_JOIN) - { - // lets check if player was teleported from BG and schedule delayed Deserter spell cast - if (IsBeingTeleportedFar()) - { - ScheduleDelayedOperation(DELAYED_SPELL_CAST_DESERTER); - return; - } - - CastSpell(this, 26013, true); // Deserter - } - } - } -} - -bool Player::CanJoinToBattleground() const -{ - // check Deserter debuff - if (GetDummyAura(26013)) - return false; - - return true; -} - -bool Player::CanReportAfkDueToLimit() -{ - // a player can complain about 15 people per 5 minutes - if (m_bgData.bgAfkReportedCount++ >= 15) - return false; - - return true; -} - -/// This player has been blamed to be inactive in a battleground -void Player::ReportedAfkBy(Player* reporter) -{ - BattleGround* bg = GetBattleGround(); - if (!bg || bg != reporter->GetBattleGround() || GetTeam() != reporter->GetTeam()) - return; - - // check if player has 'Idle' or 'Inactive' debuff - if (m_bgData.bgAfkReporter.find(reporter->GetGUIDLow()) == m_bgData.bgAfkReporter.end() && !HasAura(43680, EFFECT_INDEX_0) && !HasAura(43681, EFFECT_INDEX_0) && reporter->CanReportAfkDueToLimit()) - { - m_bgData.bgAfkReporter.insert(reporter->GetGUIDLow()); - // 3 players have to complain to apply debuff - if (m_bgData.bgAfkReporter.size() >= 3) - { - // cast 'Idle' spell - CastSpell(this, 43680, true); - m_bgData.bgAfkReporter.clear(); - } - } -} - -bool Player::IsVisibleInGridForPlayer(Player* pl) const -{ - // gamemaster in GM mode see all, including ghosts - if (pl->isGameMaster() && GetSession()->GetSecurity() <= pl->GetSession()->GetSecurity()) - return true; - - // player see dead player/ghost from own group/raid - if (IsInSameRaidWith(pl)) - return true; - - // Live player see live player or dead player with not realized corpse - if (pl->isAlive() || pl->m_deathTimer > 0) - return isAlive() || m_deathTimer > 0; - - // Ghost see other friendly ghosts, that's for sure - if (!(isAlive() || m_deathTimer > 0) && IsFriendlyTo(pl)) - return true; - - // Dead player see live players near own corpse - if (isAlive()) - { - if (Corpse* corpse = pl->GetCorpse()) - { - // 20 - aggro distance for same level, 25 - max additional distance if player level less that creature level - if (corpse->IsWithinDistInMap(this, (20 + 25) * sWorld.getConfig(CONFIG_FLOAT_RATE_CREATURE_AGGRO))) - return true; - } - } - - // and not see any other - return false; -} - -bool Player::IsVisibleGloballyFor(Player* u) const -{ - if (!u) - return false; - - // Always can see self - if (u == this) - return true; - - // Visible units, always are visible for all players - if (GetVisibility() == VISIBILITY_ON) - return true; - - // GMs are visible for higher gms (or players are visible for gms) - if (u->GetSession()->GetSecurity() > SEC_PLAYER) - return GetSession()->GetSecurity() <= u->GetSession()->GetSecurity(); - - // non faction visibility non-breakable for non-GMs - if (GetVisibility() == VISIBILITY_OFF) - return false; - - // non-gm stealth/invisibility not hide from global player lists - return true; -} - -template -inline void BeforeVisibilityDestroy(T* /*t*/, Player* /*p*/) -{ -} - -template<> -inline void BeforeVisibilityDestroy(Creature* t, Player* p) -{ - if (p->GetPetGuid() == t->GetObjectGuid() && ((Creature*)t)->IsPet()) - ((Pet*)t)->Unsummon(PET_SAVE_REAGENTS); -} - -void Player::UpdateVisibilityOf(WorldObject const* viewPoint, WorldObject* target) -{ - if (HaveAtClient(target)) - { - if (!target->isVisibleForInState(this, viewPoint, true)) - { - ObjectGuid t_guid = target->GetObjectGuid(); - - if (target->GetTypeId() == TYPEID_UNIT) - { - BeforeVisibilityDestroy((Creature*)target, this); - - // at remove from map (destroy) show kill animation (in different out of range/stealth case) - target->DestroyForPlayer(this, !target->IsInWorld() && ((Creature*)target)->isDead()); - } - else - target->DestroyForPlayer(this); - - m_clientGUIDs.erase(t_guid); - - DEBUG_FILTER_LOG(LOG_FILTER_VISIBILITY_CHANGES, "UpdateVisibilityOf: %s out of range for player %u. Distance = %f", t_guid.GetString().c_str(), GetGUIDLow(), GetDistance(target)); - } - } - else - { - if (target->isVisibleForInState(this, viewPoint, false)) - { - target->SendCreateUpdateToPlayer(this); - if (target->GetTypeId() != TYPEID_GAMEOBJECT || !((GameObject*)target)->IsTransport()) - m_clientGUIDs.insert(target->GetObjectGuid()); - - DEBUG_FILTER_LOG(LOG_FILTER_VISIBILITY_CHANGES, "UpdateVisibilityOf: %s is visible now for player %u. Distance = %f", target->GetGuidStr().c_str(), GetGUIDLow(), GetDistance(target)); - - // target aura duration for caster show only if target exist at caster client - // send data at target visibility change (adding to client) - if (target != this && target->isType(TYPEMASK_UNIT)) - SendAurasForTarget((Unit*)target); - } - } -} - -template -inline void UpdateVisibilityOf_helper(GuidSet& s64, T* target) -{ - s64.insert(target->GetObjectGuid()); -} - -template<> -inline void UpdateVisibilityOf_helper(GuidSet& s64, GameObject* target) -{ - if (!target->IsTransport()) - s64.insert(target->GetObjectGuid()); -} - -template -void Player::UpdateVisibilityOf(WorldObject const* viewPoint, T* target, UpdateData& data, std::set& visibleNow) -{ - if (HaveAtClient(target)) - { - if (!target->isVisibleForInState(this, viewPoint, true)) - { - BeforeVisibilityDestroy(target, this); - - ObjectGuid t_guid = target->GetObjectGuid(); - - target->BuildOutOfRangeUpdateBlock(&data); - m_clientGUIDs.erase(t_guid); - - DEBUG_FILTER_LOG(LOG_FILTER_VISIBILITY_CHANGES, "UpdateVisibilityOf(TemplateV): %s is out of range for %s. Distance = %f", t_guid.GetString().c_str(), GetGuidStr().c_str(), GetDistance(target)); - } - } - else - { - if (target->isVisibleForInState(this, viewPoint, false)) - { - visibleNow.insert(target); - target->BuildCreateUpdateBlockForPlayer(&data, this); - UpdateVisibilityOf_helper(m_clientGUIDs, target); - - DEBUG_FILTER_LOG(LOG_FILTER_VISIBILITY_CHANGES, "UpdateVisibilityOf(TemplateV): %s is visible now for %s. Distance = %f", target->GetGuidStr().c_str(), GetGuidStr().c_str(), GetDistance(target)); - } - } -} - -template void Player::UpdateVisibilityOf(WorldObject const* viewPoint, Player* target, UpdateData& data, std::set& visibleNow); -template void Player::UpdateVisibilityOf(WorldObject const* viewPoint, Creature* target, UpdateData& data, std::set& visibleNow); -template void Player::UpdateVisibilityOf(WorldObject const* viewPoint, Corpse* target, UpdateData& data, std::set& visibleNow); -template void Player::UpdateVisibilityOf(WorldObject const* viewPoint, GameObject* target, UpdateData& data, std::set& visibleNow); -template void Player::UpdateVisibilityOf(WorldObject const* viewPoint, DynamicObject* target, UpdateData& data, std::set& visibleNow); - -void Player::InitPrimaryProfessions() -{ - uint32 maxProfs = GetSession()->GetSecurity() < AccountTypes(sWorld.getConfig(CONFIG_UINT32_TRADE_SKILL_GMIGNORE_MAX_PRIMARY_COUNT)) - ? sWorld.getConfig(CONFIG_UINT32_MAX_PRIMARY_TRADE_SKILL) : 10; - SetFreePrimaryProfessions(maxProfs); -} - -void Player::SendComboPoints() -{ - Unit* combotarget = ObjectAccessor::GetUnit(*this, m_comboTargetGuid); - if (combotarget) - { - WorldPacket data(SMSG_UPDATE_COMBO_POINTS, combotarget->GetPackGUID().size() + 1); - data << combotarget->GetPackGUID(); - data << uint8(m_comboPoints); - GetSession()->SendPacket(&data); - } - /*else - { - // can be NULL, and then points=0. Use unknown; to reset points of some sort? - data << PackedGuid(); - data << uint8(0); - GetSession()->SendPacket(&data); - }*/ -} - -void Player::AddComboPoints(Unit* target, int8 count) -{ - if (!count) - return; - - // without combo points lost (duration checked in aura) - RemoveSpellsCausingAura(SPELL_AURA_RETAIN_COMBO_POINTS); - - if (target->GetObjectGuid() == m_comboTargetGuid) - { - m_comboPoints += count; - } - else - { - if (m_comboTargetGuid) - if (Unit* target2 = ObjectAccessor::GetUnit(*this, m_comboTargetGuid)) - target2->RemoveComboPointHolder(GetGUIDLow()); - - m_comboTargetGuid = target->GetObjectGuid(); - m_comboPoints = count; - - target->AddComboPointHolder(GetGUIDLow()); - } - - if (m_comboPoints > 5) m_comboPoints = 5; - if (m_comboPoints < 0) m_comboPoints = 0; - - SendComboPoints(); -} - -void Player::ClearComboPoints() -{ - if (!m_comboTargetGuid) - return; - - // without combopoints lost (duration checked in aura) - RemoveSpellsCausingAura(SPELL_AURA_RETAIN_COMBO_POINTS); - - m_comboPoints = 0; - - SendComboPoints(); - - if (Unit* target = ObjectAccessor::GetUnit(*this, m_comboTargetGuid)) - target->RemoveComboPointHolder(GetGUIDLow()); - - m_comboTargetGuid.Clear(); -} - -void Player::SetGroup(Group* group, int8 subgroup) -{ - if (group == NULL) - m_group.unlink(); - else - { - // never use SetGroup without a subgroup unless you specify NULL for group - MANGOS_ASSERT(subgroup >= 0); - m_group.link(group, this); - m_group.setSubGroup((uint8)subgroup); - } -} - -void Player::SendInitialPacketsBeforeAddToMap() -{ - GetSocial()->SendSocialList(); - - // Homebind - WorldPacket data(SMSG_BINDPOINTUPDATE, 5 * 4); - data << m_homebindX << m_homebindY << m_homebindZ; - data << (uint32) m_homebindMapId; - data << (uint32) m_homebindAreaId; - GetSession()->SendPacket(&data); - - // SMSG_SET_PROFICIENCY - // SMSG_SET_PCT_SPELL_MODIFIER - // SMSG_SET_FLAT_SPELL_MODIFIER - - SendTalentsInfoData(false); - - data.Initialize(SMSG_WORLD_SERVER_INFO, 1 + 1 + 4 + 4); - data.WriteBit(0); // HasRestrictedLevel - data.WriteBit(0); // HasRestrictedMoney - data.WriteBit(0); // IneligibleForLoot - - //if (IneligibleForLoot) - // data << uint32(0); // EncounterMask - - data << uint8(0); // IsOnTournamentRealm - - //if (HasRestrictedMoney) - // data << uint32(100000); // RestrictedMoney (starter accounts) - //if (HasRestrictedLevel) - // data << uint32(20); // RestrictedLevel (starter accounts) - - data << uint32(sWorld.GetNextWeeklyQuestsResetTime() - WEEK); // LastWeeklyReset (not instance reset) - data << uint32(GetMap()->GetDifficulty()); - GetSession()->SendPacket(&data); - - SendInitialSpells(); - - data.Initialize(SMSG_SEND_UNLEARN_SPELLS, 4); - data << uint32(0); // count, for(count) uint32; - GetSession()->SendPacket(&data); - - SendInitialActionButtons(); - m_reputationMgr.SendInitialReputations(); - - if (!isAlive()) - SendCorpseReclaimDelay(true); - - SendInitWorldStates(GetZoneId(), GetAreaId()); - - SendEquipmentSetList(); - - m_achievementMgr.SendAllAchievementData(); - - data.Initialize(SMSG_LOGIN_SETTIMESPEED, 4 + 4 + 4); - data << uint32(secsToTimeBitFields(sWorld.GetGameTime())); - data << (float)0.01666667f; // game speed - data << uint32(0); // added in 3.1.2 - GetSession()->SendPacket(&data); - - // SMSG_TALENTS_INFO x 2 for pet (unspent points and talents in separate packets...) - // SMSG_PET_GUIDS - // SMSG_POWER_UPDATE - - // set fly flag if in fly form or taxi flight to prevent visually drop at ground in showup moment - if (IsFreeFlying() || IsTaxiFlying()) - m_movementInfo.AddMovementFlag(MOVEFLAG_FLYING); - - SendCurrencies(); - - SetMover(this); -} - -void Player::SendInitialPacketsAfterAddToMap() -{ - // update zone - uint32 newzone, newarea; - GetZoneAndAreaId(newzone, newarea); - UpdateZone(newzone, newarea); // also call SendInitWorldStates(); - - ResetTimeSync(); - SendTimeSync(); - - CastSpell(this, 836, true); // LOGINEFFECT - - // set some aura effects that send packet to player client after add player to map - // SendMessageToSet not send it to player not it map, only for aura that not changed anything at re-apply - // same auras state lost at far teleport, send it one more time in this case also - static const AuraType auratypes[] = - { - SPELL_AURA_MOD_FEAR, SPELL_AURA_TRANSFORM, SPELL_AURA_WATER_WALK, - SPELL_AURA_FEATHER_FALL, SPELL_AURA_HOVER, SPELL_AURA_SAFE_FALL, - SPELL_AURA_FLY, SPELL_AURA_MOD_FLIGHT_SPEED_MOUNTED, SPELL_AURA_NONE - }; - for (AuraType const* itr = &auratypes[0]; itr && itr[0] != SPELL_AURA_NONE; ++itr) - { - Unit::AuraList const& auraList = GetAurasByType(*itr); - if (!auraList.empty()) - auraList.front()->ApplyModifier(true, true); - } - - if (HasAuraType(SPELL_AURA_MOD_STUN) || HasAuraType(SPELL_AURA_MOD_ROOT)) - SetRoot(true); - - SendAurasForTarget(this); - SendEnchantmentDurations(); // must be after add to map - SendItemDurations(); // must be after add to map - - UpdateSpeed(MOVE_RUN, true, 1.0f, true); - UpdateSpeed(MOVE_SWIM, true, 1.0f, true); - UpdateSpeed(MOVE_FLIGHT, true, 1.0f, true); -} - -void Player::SendUpdateToOutOfRangeGroupMembers() -{ - if (m_groupUpdateMask == GROUP_UPDATE_FLAG_NONE) - return; - if (Group* group = GetGroup()) - group->UpdatePlayerOutOfRange(this); - - m_groupUpdateMask = GROUP_UPDATE_FLAG_NONE; - m_auraUpdateMask = 0; - if (Pet* pet = GetPet()) - pet->ResetAuraUpdateMask(); -} - -void Player::SendTransferAbortedByLockStatus(MapEntry const* mapEntry, AreaLockStatus lockStatus, uint32 miscRequirement) -{ - MANGOS_ASSERT(mapEntry); - - DEBUG_LOG("SendTransferAbortedByLockStatus: Called for %s on map %u, LockAreaStatus %u, miscRequirement %u)", GetGuidStr().c_str(), mapEntry->MapID, lockStatus, miscRequirement); - - switch (lockStatus) - { - case AREA_LOCKSTATUS_TOO_LOW_LEVEL: - GetSession()->SendAreaTriggerMessage(GetSession()->GetMangosString(LANG_LEVEL_MINREQUIRED), miscRequirement); - break; - case AREA_LOCKSTATUS_ZONE_IN_COMBAT: - GetSession()->SendTransferAborted(mapEntry->MapID, TRANSFER_ABORT_ZONE_IN_COMBAT); - break; - case AREA_LOCKSTATUS_INSTANCE_IS_FULL: - GetSession()->SendTransferAborted(mapEntry->MapID, TRANSFER_ABORT_MAX_PLAYERS); - break; - case AREA_LOCKSTATUS_QUEST_NOT_COMPLETED: - if (mapEntry->MapID == 269) // Exception for Black Morass - { - GetSession()->SendAreaTriggerMessage(GetSession()->GetMangosString(LANG_TELEREQ_QUEST_BLACK_MORASS)); - break; - } - else if (mapEntry->IsContinent()) // do not report anything for quest areatrigge - { - DEBUG_LOG("SendTransferAbortedByLockStatus: LockAreaStatus %u, do not teleport, no message sent (mapId %u)", lockStatus, mapEntry->MapID); - break; - } - // No break here! - case AREA_LOCKSTATUS_MISSING_ITEM: - GetSession()->SendTransferAborted(mapEntry->MapID, TRANSFER_ABORT_DIFFICULTY, GetDifficulty(mapEntry->IsRaid())); - break; - case AREA_LOCKSTATUS_MISSING_DIFFICULTY: - { - Difficulty difficulty = GetDifficulty(mapEntry->IsRaid()); - GetSession()->SendTransferAborted(mapEntry->MapID, TRANSFER_ABORT_DIFFICULTY, difficulty > RAID_DIFFICULTY_10MAN_HEROIC ? RAID_DIFFICULTY_10MAN_HEROIC : difficulty); - break; - } - case AREA_LOCKSTATUS_INSUFFICIENT_EXPANSION: - GetSession()->SendTransferAborted(mapEntry->MapID, TRANSFER_ABORT_INSUF_EXPAN_LVL, miscRequirement); - break; - case AREA_LOCKSTATUS_NOT_ALLOWED: - GetSession()->SendTransferAborted(mapEntry->MapID, TRANSFER_ABORT_MAP_NOT_ALLOWED); - break; - case AREA_LOCKSTATUS_RAID_LOCKED: - GetSession()->SendTransferAborted(mapEntry->MapID, TRANSFER_ABORT_NEED_GROUP); - break; - case AREA_LOCKSTATUS_UNKNOWN_ERROR: - GetSession()->SendTransferAborted(mapEntry->MapID, TRANSFER_ABORT_ERROR); - break; - case AREA_LOCKSTATUS_OK: - sLog.outError("SendTransferAbortedByLockStatus: LockAreaStatus AREA_LOCKSTATUS_OK received for %s (mapId %u)", GetGuidStr().c_str(), mapEntry->MapID); - MANGOS_ASSERT(false); - break; - default: - sLog.outError("SendTransfertAbortedByLockstatus: unhandled LockAreaStatus %u, when %s attempts to enter in map %u", lockStatus, GetGuidStr().c_str(), mapEntry->MapID); - break; - } -} - -void Player::SendInstanceResetWarning(uint32 mapid, Difficulty difficulty, uint32 time) -{ - // type of warning, based on the time remaining until reset - uint32 type; - if (time > 3600) - type = RAID_INSTANCE_WELCOME; - else if (time > 900 && time <= 3600) - type = RAID_INSTANCE_WARNING_HOURS; - else if (time > 300 && time <= 900) - type = RAID_INSTANCE_WARNING_MIN; - else - type = RAID_INSTANCE_WARNING_MIN_SOON; - - WorldPacket data(SMSG_RAID_INSTANCE_MESSAGE, 4 + 4 + 4 + 4); - data << uint32(type); - data << uint32(mapid); - data << uint32(difficulty); // difficulty - data << uint32(time); - if (type == RAID_INSTANCE_WELCOME) - { - data << uint8(0); // is your (1) - data << uint8(0); // is extended (1), ignored if prev field is 0 - } - GetSession()->SendPacket(&data); -} - -void Player::ApplyEquipCooldown(Item* pItem) -{ - if (pItem->GetProto()->Flags & ITEM_FLAG_NO_EQUIP_COOLDOWN) - return; - - for (int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) - { - _Spell const& spellData = pItem->GetProto()->Spells[i]; - - // no spell - if (!spellData.SpellId) - continue; - - // wrong triggering type (note: ITEM_SPELLTRIGGER_ON_NO_DELAY_USE not have cooldown) - if (spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_USE) - continue; - - AddSpellCooldown(spellData.SpellId, pItem->GetEntry(), time(NULL) + 30); - - WorldPacket data(SMSG_ITEM_COOLDOWN, 12); - data << pItem->GetObjectGuid(); - data << uint32(spellData.SpellId); - GetSession()->SendPacket(&data); - } -} - -void Player::resetSpells() -{ - // not need after this call - if (HasAtLoginFlag(AT_LOGIN_RESET_SPELLS)) - RemoveAtLoginFlag(AT_LOGIN_RESET_SPELLS, true); - - // make full copy of map (spells removed and marked as deleted at another spell remove - // and we can't use original map for safe iterative with visit each spell at loop end - PlayerSpellMap smap = GetSpellMap(); - - for (PlayerSpellMap::const_iterator iter = smap.begin(); iter != smap.end(); ++iter) - removeSpell(iter->first, false, false); // only iter->first can be accessed, object by iter->second can be deleted already - - learnDefaultSpells(); - learnQuestRewardedSpells(); -} - -void Player::learnDefaultSpells() -{ - // learn default race/class spells - PlayerInfo const* info = sObjectMgr.GetPlayerInfo(getRace(), getClass()); - for (PlayerCreateInfoSpells::const_iterator itr = info->spell.begin(); itr != info->spell.end(); ++itr) - { - uint32 tspell = *itr; - DEBUG_LOG("PLAYER (Class: %u Race: %u): Adding initial spell, id = %u", uint32(getClass()), uint32(getRace()), tspell); - if (!IsInWorld()) // will send in INITIAL_SPELLS in list anyway at map add - addSpell(tspell, true, true, true, false); - else // but send in normal spell in game learn case - learnSpell(tspell, true); - } -} - -void Player::learnQuestRewardedSpells(Quest const* quest) -{ - uint32 spell_id = quest->GetRewSpellCast(); - - // skip quests without rewarded spell - if (!spell_id) - return; - - SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell_id); - if (!spellInfo) - return; - - // check learned spells state - bool found = false; - for (int i = 0; i < MAX_EFFECT_INDEX; ++i) - { - if(SpellEffectEntry const* spellEffect = spellInfo->GetSpellEffect(SpellEffectIndex(i))) - { - if(spellEffect->Effect == SPELL_EFFECT_LEARN_SPELL && !HasSpell(spellEffect->EffectTriggerSpell)) - { - found = true; - break; - } - } - } - - // skip quests with not teaching spell or already known spell - if (!found) - return; - - // prevent learn non first rank unknown profession and second specialization for same profession) - SpellEffectEntry const* spellEffect = spellInfo->GetSpellEffect(EFFECT_INDEX_0); - uint32 learned_0 = spellEffect ? spellEffect->EffectTriggerSpell : 0; - - if( sSpellMgr.GetSpellRank(learned_0) > 1 && !HasSpell(learned_0) ) - { - // not have first rank learned (unlearned prof?) - uint32 first_spell = sSpellMgr.GetFirstSpellInChain(learned_0); - if (!HasSpell(first_spell)) - return; - - SpellEntry const* learnedInfo = sSpellStore.LookupEntry(learned_0); - if (!learnedInfo) - return; - - // specialization - SpellEffectEntry const* learnedSpellEffect0 = learnedInfo->GetSpellEffect(EFFECT_INDEX_0); - SpellEffectEntry const* learnedSpellEffect1 = learnedInfo->GetSpellEffect(EFFECT_INDEX_1); - if (learnedSpellEffect0 && learnedSpellEffect0->Effect == SPELL_EFFECT_TRADE_SKILL && learnedSpellEffect1 && learnedSpellEffect1->Effect == 0) - { - // search other specialization for same prof - for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr) - { - if (itr->second.state == PLAYERSPELL_REMOVED || itr->first == learned_0) - continue; - - SpellEntry const* itrInfo = sSpellStore.LookupEntry(itr->first); - if (!itrInfo) - return; - - // compare only specializations - SpellEffectEntry const* itrSpellEffect0 = learnedInfo->GetSpellEffect(EFFECT_INDEX_0); - SpellEffectEntry const* itrSpellEffect1 = learnedInfo->GetSpellEffect(EFFECT_INDEX_1); - if ((itrSpellEffect0 && itrSpellEffect0->Effect != SPELL_EFFECT_TRADE_SKILL) || (itrSpellEffect1 && itrSpellEffect1->Effect != 0)) - continue; - - // compare same chain spells - if (sSpellMgr.GetFirstSpellInChain(itr->first) != first_spell) - continue; - - // now we have 2 specialization, learn possible only if found is lesser specialization rank - if (!sSpellMgr.IsHighRankOfSpell(learned_0, itr->first)) - return; - } - } - } - - CastSpell(this, spell_id, true); -} - -void Player::learnQuestRewardedSpells() -{ - // learn spells received from quest completing - for (QuestStatusMap::const_iterator itr = mQuestStatus.begin(); itr != mQuestStatus.end(); ++itr) - { - // skip no rewarded quests - if (!itr->second.m_rewarded) - continue; - - Quest const* quest = sObjectMgr.GetQuestTemplate(itr->first); - if (!quest) - continue; - - learnQuestRewardedSpells(quest); - } -} - -void Player::learnSkillRewardedSpells(uint32 skill_id, uint32 skill_value) -{ - uint32 raceMask = getRaceMask(); - uint32 classMask = getClassMask(); - for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j) - { - SkillLineAbilityEntry const* pAbility = sSkillLineAbilityStore.LookupEntry(j); - if (!pAbility || pAbility->skillId != skill_id || pAbility->learnOnGetSkill != ABILITY_LEARNED_ON_GET_PROFESSION_SKILL) - continue; - // Check race if set - if (pAbility->racemask && !(pAbility->racemask & raceMask)) - continue; - // Check class if set - if (pAbility->classmask && !(pAbility->classmask & classMask)) - continue; - - if (sSpellStore.LookupEntry(pAbility->spellId)) - { - // need unlearn spell - if (skill_value < pAbility->req_skill_value) - removeSpell(pAbility->spellId); - // need learn - else if (!IsInWorld()) - addSpell(pAbility->spellId, true, true, true, false); - else - learnSpell(pAbility->spellId, true); - } - } -} - -void Player::SendAurasForTarget(Unit* target) -{ - Unit::VisibleAuraMap const& visibleAuras = target->GetVisibleAuras(); - if (visibleAuras.empty()) - return; - - WorldPacket data(SMSG_AURA_UPDATE_ALL); - data << target->GetPackGUID(); - - for (Unit::VisibleAuraMap::const_iterator itr = visibleAuras.begin(); itr != visibleAuras.end(); ++itr) - itr->second->BuildUpdatePacket(data); - - GetSession()->SendPacket(&data); -} - -void Player::SetDailyQuestStatus(uint32 quest_id) -{ - for (uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx) - { - if (!GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1 + quest_daily_idx)) - { - SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1 + quest_daily_idx, quest_id); - m_DailyQuestChanged = true; - break; - } - } -} - -void Player::SetWeeklyQuestStatus(uint32 quest_id) -{ - m_weeklyquests.insert(quest_id); - m_WeeklyQuestChanged = true; -} - -void Player::SetMonthlyQuestStatus(uint32 quest_id) -{ - m_monthlyquests.insert(quest_id); - m_MonthlyQuestChanged = true; -} - -void Player::ResetDailyQuestStatus() -{ - for (uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx) - SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1 + quest_daily_idx, 0); - - // DB data deleted in caller - m_DailyQuestChanged = false; -} - -void Player::ResetWeeklyQuestStatus() -{ - if (m_weeklyquests.empty()) - return; - - m_weeklyquests.clear(); - // DB data deleted in caller - m_WeeklyQuestChanged = false; -} - -void Player::ResetMonthlyQuestStatus() -{ - if (m_monthlyquests.empty()) - return; - - m_monthlyquests.clear(); - // DB data deleted in caller - m_MonthlyQuestChanged = false; -} - -BattleGround* Player::GetBattleGround() const -{ - if (GetBattleGroundId() == 0) - return NULL; - - return sBattleGroundMgr.GetBattleGround(GetBattleGroundId(), m_bgData.bgTypeID); -} - -bool Player::InArena() const -{ - BattleGround* bg = GetBattleGround(); - if (!bg || !bg->isArena()) - return false; - - return true; -} - -bool Player::GetBGAccessByLevel(BattleGroundTypeId bgTypeId) const -{ - // get a template bg instead of running one - BattleGround* bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); - if (!bg) - return false; - - // limit check leel to dbc compatible level range - uint32 level = getLevel(); - if (level > DEFAULT_MAX_LEVEL) - level = DEFAULT_MAX_LEVEL; - - if (level < bg->GetMinLevel() || level > bg->GetMaxLevel()) - return false; - - return true; -} - -float Player::GetReputationPriceDiscount(Creature const* pCreature) const -{ - FactionTemplateEntry const* vendor_faction = pCreature->getFactionTemplateEntry(); - if (!vendor_faction || !vendor_faction->faction) - return 1.0f; - - ReputationRank rank = GetReputationRank(vendor_faction->faction); - if (rank <= REP_NEUTRAL) - return 1.0f; - - return 1.0f - 0.05f * (rank - REP_NEUTRAL); -} - -/* - * Check spell availability for training base at SkillLineAbility/SkillRaceClassInfo data. - * Checked allowed race/class and dependent from race/class allowed min level - * - * @param spell_id checked spell id - * @param pReqlevel if arg provided then function work in view mode (level check not applied but detected minlevel returned to var by arg pointer. - if arg not provided then considered train action mode and level checked - * @return true if spell available for show in trainer list (with skip level check) or training. - */ -bool Player::IsSpellFitByClassAndRace(uint32 spell_id, uint32* pReqlevel /*= NULL*/) const -{ - uint32 racemask = getRaceMask(); - uint32 classmask = getClassMask(); - - SkillLineAbilityMapBounds bounds = sSpellMgr.GetSkillLineAbilityMapBounds(spell_id); - if (bounds.first == bounds.second) - return true; - - for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx) - { - SkillLineAbilityEntry const* abilityEntry = _spell_idx->second; - // skip wrong race skills - if (abilityEntry->racemask && (abilityEntry->racemask & racemask) == 0) - continue; - - // skip wrong class skills - if (abilityEntry->classmask && (abilityEntry->classmask & classmask) == 0) - continue; - - SkillRaceClassInfoMapBounds bounds = sSpellMgr.GetSkillRaceClassInfoMapBounds(abilityEntry->skillId); - for (SkillRaceClassInfoMap::const_iterator itr = bounds.first; itr != bounds.second; ++itr) - { - SkillRaceClassInfoEntry const* skillRCEntry = itr->second; - if ((skillRCEntry->raceMask & racemask) && (skillRCEntry->classMask & classmask)) - { - if (skillRCEntry->flags & ABILITY_SKILL_NONTRAINABLE) - return false; - - if (pReqlevel) // show trainers list case - { - if (skillRCEntry->reqLevel) - { - *pReqlevel = skillRCEntry->reqLevel; - return true; - } - } - else // check availble case at train - { - if (skillRCEntry->reqLevel && getLevel() < skillRCEntry->reqLevel) - return false; - } - } - } - - return true; - } - - return false; -} - -bool Player::HasQuestForGO(int32 GOId) const -{ - for (int i = 0; i < MAX_QUEST_LOG_SIZE; ++i) - { - uint32 questid = GetQuestSlotQuestId(i); - if (questid == 0) - continue; - - QuestStatusMap::const_iterator qs_itr = mQuestStatus.find(questid); - if (qs_itr == mQuestStatus.end()) - continue; - - QuestStatusData const& qs = qs_itr->second; - - if (qs.m_status == QUEST_STATUS_INCOMPLETE) - { - Quest const* qinfo = sObjectMgr.GetQuestTemplate(questid); - if (!qinfo) - continue; - - if (GetGroup() && GetGroup()->isRaidGroup() && !qinfo->IsAllowedInRaid()) - continue; - - for (int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j) - { - if (qinfo->ReqCreatureOrGOId[j] >= 0) // skip non GO case - continue; - - if ((-1)*GOId == qinfo->ReqCreatureOrGOId[j] && qs.m_creatureOrGOcount[j] < qinfo->ReqCreatureOrGOCount[j]) - return true; - } - } - } - return false; -} - -void Player::UpdateForQuestWorldObjects() -{ - if (m_clientGUIDs.empty()) - return; - - UpdateData udata(GetMapId()); - WorldPacket packet; - for (GuidSet::const_iterator itr = m_clientGUIDs.begin(); itr != m_clientGUIDs.end(); ++itr) - { - if (itr->IsGameObject()) - { - if (GameObject* obj = GetMap()->GetGameObject(*itr)) - obj->BuildValuesUpdateBlockForPlayer(&udata, this); - } - else if (itr->IsCreatureOrVehicle()) - { - Creature* obj = GetMap()->GetAnyTypeCreature(*itr); - if (!obj) - continue; - - // check if this unit requires quest specific flags - if (!obj->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK)) - continue; - - SpellClickInfoMapBounds clickPair = sObjectMgr.GetSpellClickInfoMapBounds(obj->GetEntry()); - for (SpellClickInfoMap::const_iterator _itr = clickPair.first; _itr != clickPair.second; ++_itr) - { - if (_itr->second.questStart || _itr->second.questEnd) - { - obj->BuildCreateUpdateBlockForPlayer(&udata, this); - break; - } - } - } - } - udata.BuildPacket(&packet); - GetSession()->SendPacket(&packet); -} - -void Player::SummonIfPossible(bool agree) -{ - if (!agree) - { - m_summon_expire = 0; - return; - } - - // expire and auto declined - if (m_summon_expire < time(NULL)) - return; - - // stop taxi flight at summon - if (IsTaxiFlying()) - { - GetMotionMaster()->MovementExpired(); - m_taxi.ClearTaxiDestinations(); - } - - // drop flag at summon - // this code can be reached only when GM is summoning player who carries flag, because player should be immune to summoning spells when he carries flag - if (BattleGround* bg = GetBattleGround()) - bg->EventPlayerDroppedFlag(this); - - m_summon_expire = 0; - - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS, 1); - - TeleportTo(m_summon_mapid, m_summon_x, m_summon_y, m_summon_z, GetOrientation()); -} - -void Player::RemoveItemDurations(Item* item) -{ - for (ItemDurationList::iterator itr = m_itemDuration.begin(); itr != m_itemDuration.end(); ++itr) - { - if (*itr == item) - { - m_itemDuration.erase(itr); - break; - } - } -} - -void Player::AddItemDurations(Item* item) -{ - if (item->GetUInt32Value(ITEM_FIELD_DURATION)) - { - m_itemDuration.push_back(item); - item->SendTimeUpdate(this); - } -} - -void Player::AutoUnequipOffhandIfNeed() -{ - Item* offItem = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); - if (!offItem) - return; - - // need unequip offhand for 2h-weapon without TitanGrip (in any from hands) - if ((CanDualWield() || offItem->GetProto()->InventoryType == INVTYPE_SHIELD || offItem->GetProto()->InventoryType == INVTYPE_HOLDABLE) && - (CanTitanGrip() || (offItem->GetProto()->InventoryType != INVTYPE_2HWEAPON && !IsTwoHandUsed()))) - return; - - ItemPosCountVec off_dest; - uint8 off_msg = CanStoreItem(NULL_BAG, NULL_SLOT, off_dest, offItem, false); - if (off_msg == EQUIP_ERR_OK) - { - RemoveItem(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND, true); - StoreItem(off_dest, offItem, true); - } - else - { - MoveItemFromInventory(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND, true); - CharacterDatabase.BeginTransaction(); - offItem->DeleteFromInventoryDB(); // deletes item from character's inventory - offItem->SaveToDB(); // recursive and not have transaction guard into self, item not in inventory and can be save standalone - CharacterDatabase.CommitTransaction(); - - std::string subject = GetSession()->GetMangosString(LANG_NOT_EQUIPPED_ITEM); - MailDraft(subject, "There's were problems with equipping this item.").AddItem(offItem).SendMailTo(this, MailSender(this, MAIL_STATIONERY_GM), MAIL_CHECK_MASK_COPIED); - } -} - -bool Player::HasItemFitToSpellReqirements(SpellEntry const* spellInfo, Item const* ignoreItem) -{ - int32 itemClass = spellInfo->GetEquippedItemClass(); - if(itemClass < 0) - return true; - - // scan other equipped items for same requirements (mostly 2 daggers/etc) - // for optimize check 2 used cases only - switch(itemClass) - { - case ITEM_CLASS_WEAPON: - { - for (int i = EQUIPMENT_SLOT_MAINHAND; i < EQUIPMENT_SLOT_TABARD; ++i) - if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - if (item != ignoreItem && item->IsFitToSpellRequirements(spellInfo)) - return true; - break; - } - case ITEM_CLASS_ARMOR: - { - // tabard not have dependent spells - for (int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_MAINHAND; ++i) - if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - if (item != ignoreItem && item->IsFitToSpellRequirements(spellInfo)) - return true; - - // shields can be equipped to offhand slot - if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND)) - if (item != ignoreItem && item->IsFitToSpellRequirements(spellInfo)) - return true; - - // ranged slot can have some armor subclasses - if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED)) - if (item != ignoreItem && item->IsFitToSpellRequirements(spellInfo)) - return true; - - break; - } - default: - sLog.outError("HasItemFitToSpellReqirements: Not handled spell requirement for item class %u", itemClass); - break; - } - - return false; -} - -bool Player::CanNoReagentCast(SpellEntry const* spellInfo) const -{ - // don't take reagents for spells with SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP - if (spellInfo->HasAttribute(SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP) && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION)) - return true; - - // Check no reagent use mask - uint64 noReagentMask_0_1 = GetUInt64Value(PLAYER_NO_REAGENT_COST_1); - uint32 noReagentMask_2 = GetUInt32Value(PLAYER_NO_REAGENT_COST_1 + 2); - if (spellInfo->IsFitToFamilyMask(noReagentMask_0_1, noReagentMask_2)) - return true; - - return false; -} - -void Player::RemoveItemDependentAurasAndCasts(Item* pItem) -{ - SpellAuraHolderMap& auras = GetSpellAuraHolderMap(); - for (SpellAuraHolderMap::const_iterator itr = auras.begin(); itr != auras.end();) - { - SpellAuraHolder* holder = itr->second; - - // skip passive (passive item dependent spells work in another way) and not self applied auras - SpellEntry const* spellInfo = holder->GetSpellProto(); - if (holder->IsPassive() || holder->GetCasterGuid() != GetObjectGuid()) - { - ++itr; - continue; - } - - // skip if not item dependent or have alternative item - if (HasItemFitToSpellReqirements(spellInfo, pItem)) - { - ++itr; - continue; - } - - // no alt item, remove aura, restart check - RemoveAurasDueToSpell(holder->GetId()); - itr = auras.begin(); - } - - // currently casted spells can be dependent from item - for (uint32 i = 0; i < CURRENT_MAX_SPELL; ++i) - if (Spell* spell = GetCurrentSpell(CurrentSpellTypes(i))) - if (spell->getState() != SPELL_STATE_DELAYED && !HasItemFitToSpellReqirements(spell->m_spellInfo, pItem)) - InterruptSpell(CurrentSpellTypes(i)); -} - -uint32 Player::GetResurrectionSpellId() -{ - // search priceless resurrection possibilities - uint32 prio = 0; - uint32 spell_id = 0; - AuraList const& dummyAuras = GetAurasByType(SPELL_AURA_DUMMY); - for (AuraList::const_iterator itr = dummyAuras.begin(); itr != dummyAuras.end(); ++itr) - { - // Soulstone Resurrection // prio: 3 (max, non death persistent) - if (prio < 2 && (*itr)->GetSpellProto()->SpellVisual[0] == 99 && (*itr)->GetSpellProto()->SpellIconID == 92) - { - switch ((*itr)->GetId()) - { - case 20707: spell_id = 3026; break; // rank 1 - case 20762: spell_id = 20758; break; // rank 2 - case 20763: spell_id = 20759; break; // rank 3 - case 20764: spell_id = 20760; break; // rank 4 - case 20765: spell_id = 20761; break; // rank 5 - case 27239: spell_id = 27240; break; // rank 6 - case 47883: spell_id = 47882; break; // rank 7 - default: - sLog.outError("Unhandled spell %u: S.Resurrection", (*itr)->GetId()); - continue; - } - - prio = 3; - } - // Twisting Nether // prio: 2 (max) - else if ((*itr)->GetId() == 23701 && roll_chance_i(10)) - { - prio = 2; - spell_id = 23700; - } - } - - // Reincarnation (passive spell) // prio: 1 - // Glyph of Renewed Life remove reagent requiremnnt - if (prio < 1 && HasSpell(20608) && !HasSpellCooldown(21169) && (HasItemCount(17030, 1) || HasAura(58059, EFFECT_INDEX_0))) - spell_id = 21169; - - return spell_id; -} - -// Used in triggers for check "Only to targets that grant experience or honor" req -bool Player::isHonorOrXPTarget(Unit* pVictim) const -{ - uint32 v_level = pVictim->getLevel(); - uint32 k_grey = MaNGOS::XP::GetGrayLevel(getLevel()); - - // Victim level less gray level - if (v_level <= k_grey) - return false; - - if (pVictim->GetTypeId() == TYPEID_UNIT) - { - if (((Creature*)pVictim)->IsTotem() || - ((Creature*)pVictim)->IsPet() || - ((Creature*)pVictim)->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_XP_AT_KILL) - return false; - } - return true; -} - -void Player::RewardSinglePlayerAtKill(Unit* pVictim) -{ - bool PvP = pVictim->isCharmedOwnedByPlayerOrPlayer(); - uint32 xp = PvP ? 0 : MaNGOS::XP::Gain(this, pVictim); - - // honor can be in PvP and !PvP (racial leader) cases - RewardHonor(pVictim, 1); - - // xp and reputation only in !PvP case - if (!PvP) - { - RewardReputation(pVictim, 1); - GiveXP(xp, pVictim); - - if (Pet* pet = GetPet()) - pet->GivePetXP(xp); - - // normal creature (not pet/etc) can be only in !PvP case - if (pVictim->GetTypeId() == TYPEID_UNIT) - if (CreatureInfo const* normalInfo = ObjectMgr::GetCreatureTemplate(pVictim->GetEntry())) - KilledMonster(normalInfo, pVictim->GetObjectGuid()); - } -} - -void Player::RewardPlayerAndGroupAtEvent(uint32 creature_id, WorldObject* pRewardSource) -{ - MANGOS_ASSERT((!GetGroup() || pRewardSource) && "Player::RewardPlayerAndGroupAtEvent called for Group-Case but no source for range searching provided"); - - ObjectGuid creature_guid = pRewardSource && pRewardSource->GetTypeId() == TYPEID_UNIT ? pRewardSource->GetObjectGuid() : ObjectGuid(); - - // prepare data for near group iteration - if (Group* pGroup = GetGroup()) - { - for (GroupReference* itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player* pGroupGuy = itr->getSource(); - if (!pGroupGuy) - continue; - - if (!pGroupGuy->IsAtGroupRewardDistance(pRewardSource)) - continue; // member (alive or dead) or his corpse at req. distance - - // quest objectives updated only for alive group member or dead but with not released body - if (pGroupGuy->isAlive() || !pGroupGuy->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) - pGroupGuy->KilledMonsterCredit(creature_id, creature_guid); - } - } - else // if (!pGroup) - KilledMonsterCredit(creature_id, creature_guid); -} - -void Player::RewardPlayerAndGroupAtCast(WorldObject* pRewardSource, uint32 spellid) -{ - // prepare data for near group iteration - if (Group* pGroup = GetGroup()) - { - for (GroupReference* itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player* pGroupGuy = itr->getSource(); - if (!pGroupGuy) - continue; - - if (!pGroupGuy->IsAtGroupRewardDistance(pRewardSource)) - continue; // member (alive or dead) or his corpse at req. distance - - // quest objectives updated only for alive group member or dead but with not released body - if (pGroupGuy->isAlive() || !pGroupGuy->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) - pGroupGuy->CastedCreatureOrGO(pRewardSource->GetEntry(), pRewardSource->GetObjectGuid(), spellid, pGroupGuy == this); - } - } - else // if (!pGroup) - CastedCreatureOrGO(pRewardSource->GetEntry(), pRewardSource->GetObjectGuid(), spellid); -} - -bool Player::IsAtGroupRewardDistance(WorldObject const* pRewardSource) const -{ - if (pRewardSource->IsWithinDistInMap(this, sWorld.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE))) - return true; - - if (isAlive()) - return false; - - Corpse* corpse = GetCorpse(); - if (!corpse) - return false; - - return pRewardSource->IsWithinDistInMap(corpse, sWorld.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE)); -} - -void Player::ResurectUsingRequestData() -{ - /// Teleport before resurrecting by player, otherwise the player might get attacked from creatures near his corpse - if (m_resurrectGuid.IsPlayer()) - TeleportTo(m_resurrectMap, m_resurrectX, m_resurrectY, m_resurrectZ, GetOrientation()); - - // we cannot resurrect player when we triggered far teleport - // player will be resurrected upon teleportation - if (IsBeingTeleportedFar()) - { - ScheduleDelayedOperation(DELAYED_RESURRECT_PLAYER); - return; - } - - ResurrectPlayer(0.0f, false); - - if (GetMaxHealth() > m_resurrectHealth) - SetHealth(m_resurrectHealth); - else - SetHealth(GetMaxHealth()); - - if (GetMaxPower(POWER_MANA) > m_resurrectMana) - SetPower(POWER_MANA, m_resurrectMana); - else - SetPower(POWER_MANA, GetMaxPower(POWER_MANA)); - - SetPower(POWER_RAGE, 0); - - SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY)); - - SpawnCorpseBones(); -} - -void Player::SetClientControl(Unit* target, uint8 allowMove) -{ - WorldPacket data(SMSG_CLIENT_CONTROL_UPDATE, target->GetPackGUID().size() + 1); - data << target->GetPackGUID(); - data << uint8(allowMove); - GetSession()->SendPacket(&data); -} - -void Player::UpdateZoneDependentAuras() -{ - // Some spells applied at enter into zone (with subzones), aura removed in UpdateAreaDependentAuras that called always at zone->area update - SpellAreaForAreaMapBounds saBounds = sSpellMgr.GetSpellAreaForAreaMapBounds(m_zoneUpdateId); - for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) - itr->second->ApplyOrRemoveSpellIfCan(this, m_zoneUpdateId, 0, true); -} - -void Player::UpdateAreaDependentAuras() -{ - // remove auras from spells with area limitations - for (SpellAuraHolderMap::iterator iter = m_spellAuraHolders.begin(); iter != m_spellAuraHolders.end();) - { - // use m_zoneUpdateId for speed: UpdateArea called from UpdateZone or instead UpdateZone in both cases m_zoneUpdateId up-to-date - if (sSpellMgr.GetSpellAllowedInLocationError(iter->second->GetSpellProto(), GetMapId(), m_zoneUpdateId, m_areaUpdateId, this) != SPELL_CAST_OK) - { - RemoveSpellAuraHolder(iter->second); - iter = m_spellAuraHolders.begin(); - } - else - ++iter; - } - - // some auras applied at subzone enter - SpellAreaForAreaMapBounds saBounds = sSpellMgr.GetSpellAreaForAreaMapBounds(m_areaUpdateId); - for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) - itr->second->ApplyOrRemoveSpellIfCan(this, m_zoneUpdateId, m_areaUpdateId, true); -} - -struct UpdateZoneDependentPetsHelper -{ - explicit UpdateZoneDependentPetsHelper(Player* _owner, uint32 zone, uint32 area) : owner(_owner), zone_id(zone), area_id(area) {} - void operator()(Unit* unit) const - { - if (unit->GetTypeId() == TYPEID_UNIT && ((Creature*)unit)->IsPet() && !((Pet*)unit)->IsPermanentPetFor(owner)) - if (uint32 spell_id = unit->GetUInt32Value(UNIT_CREATED_BY_SPELL)) - if (SpellEntry const* spellEntry = sSpellStore.LookupEntry(spell_id)) - if (sSpellMgr.GetSpellAllowedInLocationError(spellEntry, owner->GetMapId(), zone_id, area_id, owner) != SPELL_CAST_OK) - ((Pet*)unit)->Unsummon(PET_SAVE_AS_DELETED, owner); - } - Player* owner; - uint32 zone_id; - uint32 area_id; -}; - -void Player::UpdateZoneDependentPets() -{ - // check pet (permanent pets ignored), minipet, guardians (including protector) - CallForAllControlledUnits(UpdateZoneDependentPetsHelper(this, m_zoneUpdateId, m_areaUpdateId), CONTROLLED_PET | CONTROLLED_GUARDIANS | CONTROLLED_MINIPET); -} - -uint32 Player::GetCorpseReclaimDelay(bool pvp) const -{ - if ((pvp && !sWorld.getConfig(CONFIG_BOOL_DEATH_CORPSE_RECLAIM_DELAY_PVP)) || - (!pvp && !sWorld.getConfig(CONFIG_BOOL_DEATH_CORPSE_RECLAIM_DELAY_PVE))) - { - return corpseReclaimDelay[0]; - } - - time_t now = time(NULL); - // 0..2 full period - uint32 count = (now < m_deathExpireTime) ? uint32((m_deathExpireTime - now) / DEATH_EXPIRE_STEP) : 0; - return corpseReclaimDelay[count]; -} - -void Player::UpdateCorpseReclaimDelay() -{ - bool pvp = m_ExtraFlags & PLAYER_EXTRA_PVP_DEATH; - - if ((pvp && !sWorld.getConfig(CONFIG_BOOL_DEATH_CORPSE_RECLAIM_DELAY_PVP)) || - (!pvp && !sWorld.getConfig(CONFIG_BOOL_DEATH_CORPSE_RECLAIM_DELAY_PVE))) - return; - - time_t now = time(NULL); - if (now < m_deathExpireTime) - { - // full and partly periods 1..3 - uint32 count = uint32((m_deathExpireTime - now) / DEATH_EXPIRE_STEP + 1); - if (count < MAX_DEATH_COUNT) - m_deathExpireTime = now + (count + 1) * DEATH_EXPIRE_STEP; - else - m_deathExpireTime = now + MAX_DEATH_COUNT * DEATH_EXPIRE_STEP; - } - else - m_deathExpireTime = now + DEATH_EXPIRE_STEP; -} - -void Player::SendCorpseReclaimDelay(bool load) -{ - Corpse* corpse = GetCorpse(); - if (!corpse) - return; - - uint32 delay; - if (load) - { - if (corpse->GetGhostTime() > m_deathExpireTime) - return; - - bool pvp = corpse->GetType() == CORPSE_RESURRECTABLE_PVP; - - uint32 count; - if ((pvp && sWorld.getConfig(CONFIG_BOOL_DEATH_CORPSE_RECLAIM_DELAY_PVP)) || - (!pvp && sWorld.getConfig(CONFIG_BOOL_DEATH_CORPSE_RECLAIM_DELAY_PVE))) - { - count = uint32(m_deathExpireTime - corpse->GetGhostTime()) / DEATH_EXPIRE_STEP; - if (count >= MAX_DEATH_COUNT) - count = MAX_DEATH_COUNT - 1; - } - else - count = 0; - - time_t expected_time = corpse->GetGhostTime() + corpseReclaimDelay[count]; - - time_t now = time(NULL); - if (now >= expected_time) - return; - - delay = uint32(expected_time - now); - } - else - delay = GetCorpseReclaimDelay(corpse->GetType() == CORPSE_RESURRECTABLE_PVP); - - //! corpse reclaim delay 30 * 1000ms or longer at often deaths - WorldPacket data(SMSG_CORPSE_RECLAIM_DELAY, 4); - data << uint32(delay * IN_MILLISECONDS); - GetSession()->SendPacket(&data); -} - -Player* Player::GetNextRandomRaidMember(float radius) -{ - Group* pGroup = GetGroup(); - if (!pGroup) - return NULL; - - std::vector nearMembers; - nearMembers.reserve(pGroup->GetMembersCount()); - - for (GroupReference* itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player* Target = itr->getSource(); - - // IsHostileTo check duel and controlled by enemy - if (Target && Target != this && IsWithinDistInMap(Target, radius) && - !Target->HasInvisibilityAura() && !IsHostileTo(Target)) - nearMembers.push_back(Target); - } - - if (nearMembers.empty()) - return NULL; - - uint32 randTarget = urand(0, nearMembers.size() - 1); - return nearMembers[randTarget]; -} - -PartyResult Player::CanUninviteFromGroup() const -{ - const Group* grp = GetGroup(); - if (!grp) - return ERR_NOT_IN_GROUP; - - if (!grp->IsLeader(GetObjectGuid()) && !grp->IsAssistant(GetObjectGuid())) - return ERR_NOT_LEADER; - - if (InBattleGround()) - return ERR_INVITE_RESTRICTED; - - return ERR_PARTY_RESULT_OK; -} - -void Player::SetBattleGroundRaid(Group* group, int8 subgroup) -{ - // we must move references from m_group to m_originalGroup - SetOriginalGroup(GetGroup(), GetSubGroup()); - - m_group.unlink(); - m_group.link(group, this); - m_group.setSubGroup((uint8)subgroup); -} - -void Player::RemoveFromBattleGroundRaid() -{ - // remove existing reference - m_group.unlink(); - if (Group* group = GetOriginalGroup()) - { - m_group.link(group, this); - m_group.setSubGroup(GetOriginalSubGroup()); - } - SetOriginalGroup(NULL); -} - -void Player::SetOriginalGroup(Group* group, int8 subgroup) -{ - if (group == NULL) - m_originalGroup.unlink(); - else - { - // never use SetOriginalGroup without a subgroup unless you specify NULL for group - MANGOS_ASSERT(subgroup >= 0); - m_originalGroup.link(group, this); - m_originalGroup.setSubGroup((uint8)subgroup); - } -} - -void Player::UpdateUnderwaterState(Map* m, float x, float y, float z) -{ - GridMapLiquidData liquid_status; - GridMapLiquidStatus res = m->GetTerrain()->getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &liquid_status); - if (!res) - { - m_MirrorTimerFlags &= ~(UNDERWATER_INWATER | UNDERWATER_INLAVA | UNDERWATER_INSLIME | UNDERWATER_INDARKWATER); - if (m_lastLiquid && m_lastLiquid->SpellId) - RemoveAurasDueToSpell(m_lastLiquid->SpellId == 37025 ? 37284 : m_lastLiquid->SpellId); - m_lastLiquid = NULL; - return; - } - - if (uint32 liqEntry = liquid_status.entry) - { - LiquidTypeEntry const* liquid = sLiquidTypeStore.LookupEntry(liqEntry); - if (m_lastLiquid && m_lastLiquid->SpellId && m_lastLiquid->Id != liqEntry) - RemoveAurasDueToSpell(m_lastLiquid->SpellId); - - if (liquid && liquid->SpellId) - { - // Exception for SSC water - uint32 liquidSpellId = liquid->SpellId == 37025 ? 37284 : liquid->SpellId; - - if (res & (LIQUID_MAP_UNDER_WATER | LIQUID_MAP_IN_WATER)) - { - if (!HasAura(liquidSpellId)) - { - // Handle exception for SSC water - if (liquid->SpellId == 37025) - { - if (InstanceData* pInst = GetInstanceData()) - { - if (pInst->CheckConditionCriteriaMeet(this, INSTANCE_CONDITION_ID_LURKER, NULL, CONDITION_FROM_HARDCODED)) - { - if (pInst->CheckConditionCriteriaMeet(this, INSTANCE_CONDITION_ID_SCALDING_WATER, NULL, CONDITION_FROM_HARDCODED)) - CastSpell(this, liquidSpellId, true); - else - { - SummonCreature(21508, 0, 0, 0, 0, TEMPSUMMON_TIMED_OOC_DESPAWN, 2000); - // Special update timer for the SSC water - m_positionStatusUpdateTimer = 2000; - } - } - } - } - else - CastSpell(this, liquidSpellId, true); - } - } - else - RemoveAurasDueToSpell(liquidSpellId); - } - - m_lastLiquid = liquid; - } - else if (m_lastLiquid && m_lastLiquid->SpellId) - { - RemoveAurasDueToSpell(m_lastLiquid->SpellId == 37025 ? 37284 : m_lastLiquid->SpellId); - m_lastLiquid = NULL; - } - - // All liquids type - check under water position - if (liquid_status.type_flags & (MAP_LIQUID_TYPE_WATER | MAP_LIQUID_TYPE_OCEAN | MAP_LIQUID_TYPE_MAGMA | MAP_LIQUID_TYPE_SLIME)) - { - if (res & LIQUID_MAP_UNDER_WATER) - m_MirrorTimerFlags |= UNDERWATER_INWATER; - else - m_MirrorTimerFlags &= ~UNDERWATER_INWATER; - } - - // Allow travel in dark water on taxi or transport - if ((liquid_status.type_flags & MAP_LIQUID_TYPE_DARK_WATER) && !IsTaxiFlying() && !GetTransport()) - m_MirrorTimerFlags |= UNDERWATER_INDARKWATER; - else - m_MirrorTimerFlags &= ~UNDERWATER_INDARKWATER; - - // in lava check, anywhere in lava level - if (liquid_status.type_flags & MAP_LIQUID_TYPE_MAGMA) - { - if (res & (LIQUID_MAP_UNDER_WATER | LIQUID_MAP_IN_WATER | LIQUID_MAP_WATER_WALK)) - m_MirrorTimerFlags |= UNDERWATER_INLAVA; - else - m_MirrorTimerFlags &= ~UNDERWATER_INLAVA; - } - // in slime check, anywhere in slime level - if (liquid_status.type_flags & MAP_LIQUID_TYPE_SLIME) - { - if (res & (LIQUID_MAP_UNDER_WATER | LIQUID_MAP_IN_WATER | LIQUID_MAP_WATER_WALK)) - m_MirrorTimerFlags |= UNDERWATER_INSLIME; - else - m_MirrorTimerFlags &= ~UNDERWATER_INSLIME; - } -} - -void Player::SetCanParry(bool value) -{ - if (m_canParry == value) - return; - - m_canParry = value; - UpdateParryPercentage(); -} - -void Player::SetCanBlock(bool value) -{ - if (m_canBlock == value) - return; - - m_canBlock = value; - UpdateBlockPercentage(); - UpdateShieldBlockDamageValue(); -} - -bool ItemPosCount::isContainedIn(ItemPosCountVec const& vec) const -{ - for (ItemPosCountVec::const_iterator itr = vec.begin(); itr != vec.end(); ++itr) - if (itr->pos == pos) - return true; - - return false; -} - -bool Player::CanUseBattleGroundObject() -{ - // TODO : some spells gives player ForceReaction to one faction (ReputationMgr::ApplyForceReaction) - // maybe gameobject code should handle that ForceReaction usage - // BUG: sometimes when player clicks on flag in AB - client won't send gameobject_use, only gameobject_report_use packet - return (isAlive() && // living - // the following two are incorrect, because invisible/stealthed players should get visible when they click on flag - !HasStealthAura() && // not stealthed - !HasInvisibilityAura() && // visible - !isTotalImmune() && // vulnerable (not immune) - !HasAura(SPELL_RECENTLY_DROPPED_FLAG, EFFECT_INDEX_0)); -} - -uint32 Player::GetBarberShopCost(uint8 newhairstyle, uint8 newhaircolor, uint8 newfacialhair, uint32 newskintone) -{ - uint32 level = getLevel(); - - if (level > GT_MAX_LEVEL) - level = GT_MAX_LEVEL; // max level in this dbc - - uint8 hairstyle = GetByteValue(PLAYER_BYTES, 2); - uint8 haircolor = GetByteValue(PLAYER_BYTES, 3); - uint8 facialhair = GetByteValue(PLAYER_BYTES_2, 0); - uint8 skintone = GetByteValue(PLAYER_BYTES, 0); - - if (hairstyle == newhairstyle && haircolor == newhaircolor && facialhair == newfacialhair && - (skintone == newskintone || newskintone == -1)) - return 0; - - GtBarberShopCostBaseEntry const* bsc = sGtBarberShopCostBaseStore.LookupEntry(level - 1); - - if (!bsc) // shouldn't happen - return 0xFFFFFFFF; - - float cost = 0; - - if (hairstyle != newhairstyle) - cost += bsc->cost; // full price - - if (haircolor != newhaircolor && hairstyle == newhairstyle) - cost += bsc->cost * 0.5f; // +1/2 of price - - if (facialhair != newfacialhair) - cost += bsc->cost * 0.75f; // +3/4 of price - - if (skintone != newskintone && newskintone != -1) - cost += bsc->cost * 0.5f; // +1/2 of price - - return uint32(cost); -} - -void Player::InitGlyphsForLevel() -{ - uint32 slot = 0; - for (uint32 i = 0; i < sGlyphSlotStore.GetNumRows() && slot < MAX_GLYPH_SLOT_INDEX; ++i) - if (GlyphSlotEntry const* gs = sGlyphSlotStore.LookupEntry(i)) - SetGlyphSlot(slot++, gs->Id); - - uint32 level = getLevel(); - uint32 value = 0; - - // 0x3F = 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 for 80 level - if (level >= 25) - value |= 0x01 | 0x02 | 0x40; - if (level >= 50) - value |= 0x04 | 0x08 | 0x80; - if (level >= 75) - value |= 0x10 | 0x20 | 0x100; - - SetUInt32Value(PLAYER_GLYPHS_ENABLED, value); -} - -void Player::ApplyGlyph(uint8 slot, bool apply) -{ - if (uint32 glyph = GetGlyph(slot)) - { - if (GlyphPropertiesEntry const* gp = sGlyphPropertiesStore.LookupEntry(glyph)) - { - if (apply) - { - CastSpell(this, gp->SpellId, true); - SetUInt32Value(PLAYER_FIELD_GLYPHS_1 + slot, glyph); - } - else - { - RemoveAurasDueToSpell(gp->SpellId); - SetUInt32Value(PLAYER_FIELD_GLYPHS_1 + slot, 0); - } - } - } -} - -void Player::ApplyGlyphs(bool apply) -{ - for (uint8 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i) - ApplyGlyph(i, apply); -} - -bool Player::isTotalImmune() -{ - AuraList const& immune = GetAurasByType(SPELL_AURA_SCHOOL_IMMUNITY); - - uint32 immuneMask = 0; - for (AuraList::const_iterator itr = immune.begin(); itr != immune.end(); ++itr) - { - immuneMask |= (*itr)->GetModifier()->m_miscvalue; - if (immuneMask & SPELL_SCHOOL_MASK_ALL) // total immunity - return true; - } - return false; -} - -bool Player::HasTitle(uint32 bitIndex) const -{ - if (bitIndex > MAX_TITLE_INDEX) - return false; - - uint32 fieldIndexOffset = bitIndex / 32; - uint32 flag = 1 << (bitIndex % 32); - return HasFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag); -} - -void Player::SetTitle(CharTitlesEntry const* title, bool lost) -{ - uint32 fieldIndexOffset = title->bit_index / 32; - uint32 flag = 1 << (title->bit_index % 32); - - if (lost) - { - if (!HasFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag)) - return; - - RemoveFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag); - } - else - { - if (HasFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag)) - return; - - SetFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag); - } - - WorldPacket data(SMSG_TITLE_EARNED, 4 + 4); - data << uint32(title->bit_index); - data << uint32(lost ? 0 : 1); // 1 - earned, 0 - lost - GetSession()->SendPacket(&data); -} - -void Player::UpdateRuneRegen(RuneType rune) -{ - if (rune >= RUNE_DEATH) - return; - - RuneType actualRune = rune; - float cooldown = RUNE_BASE_COOLDOWN; - for (uint8 i = 0; i < MAX_RUNES; i += 2) - { - if (GetBaseRune(i) != rune) - continue; - - uint32 cd = GetRuneCooldown(i); - uint32 secondRuneCd = GetRuneCooldown(i + 1); - if (!cd && !secondRuneCd) - { - actualRune = GetCurrentRune(i); - } - else if (secondRuneCd && (cd > secondRuneCd || !cd)) - { - cooldown = GetBaseRuneCooldown(i + 1); - actualRune = GetCurrentRune(i + 1); - } - else - { - cooldown = GetBaseRuneCooldown(i); - actualRune = GetCurrentRune(i); - } - break; - } - - float auraMod = 1.0f; - Unit::AuraList const& regenAuras = GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT); - for (Unit::AuraList::const_iterator i = regenAuras.begin(); i != regenAuras.end(); ++i) - if ((*i)->GetMiscValue() == POWER_RUNE && (*i)->GetSpellEffect()->EffectMiscValueB == rune) - auraMod *= (100.0f + (*i)->GetModifier()->m_amount) / 100.0f; - - // Unholy Presence - if (Aura* aura = GetAura(48265, EFFECT_INDEX_0)) - auraMod *= (100.0f + aura->GetModifier()->m_amount) / 100.0f; - - // Runic Corruption - if (Aura* aura = GetAura(51460, EFFECT_INDEX_0)) - auraMod *= (100.0f + aura->GetModifier()->m_amount) / 100.0f; - - float hastePct = (100.0f - GetRatingBonusValue(CR_HASTE_MELEE)) / 100.0f; - if (hastePct < 0) - hastePct = 1.0f; - - cooldown *= hastePct / auraMod; - - float value = float(1 * IN_MILLISECONDS) / cooldown; - SetFloatValue(PLAYER_RUNE_REGEN_1 + uint8(actualRune), value); -} - -void Player::UpdateRuneRegen() -{ - for (uint8 i = 0; i < NUM_RUNE_TYPES; ++i) - UpdateRuneRegen(RuneType(i)); -} - -uint8 Player::GetRuneCooldownFraction(uint8 index) const -{ - uint16 baseCd = GetBaseRuneCooldown(index); - if (!baseCd || !GetRuneCooldown(index)) - return 255; - else if (baseCd == GetRuneCooldown(index)) - return 0; - - return uint8(float(baseCd - GetRuneCooldown(index)) / baseCd * 255); -} - -void Player::AddRuneByAuraEffect(uint8 index, RuneType newType, Aura const* aura) -{ - // Item - Death Knight T11 DPS 4P Bonus - if (newType == RUNE_DEATH && HasAura(90459)) - CastSpell(this, 90507, true); // Death Eater - - SetRuneConvertAura(index, aura); ConvertRune(index, newType); -} - -void Player::RemoveRunesByAuraEffect(Aura const* aura) -{ - for (uint8 i = 0; i < MAX_RUNES; ++i) - { - if (m_runes->runes[i].ConvertAura == aura) - { - ConvertRune(i, GetBaseRune(i)); - SetRuneConvertAura(i, NULL); - } - } -} - -void Player::RestoreBaseRune(uint8 index) -{ - Aura const* aura = m_runes->runes[index].ConvertAura; - // If rune was converted by a non-pasive aura that still active we should keep it converted - if (aura && !IsPassiveSpell(aura->GetSpellProto())) - return; - - // Blood of the North - if (aura->GetId() == 54637 && HasAura(54637)) - return; - - ConvertRune(index, GetBaseRune(index)); - SetRuneConvertAura(index, NULL); - // Don't drop passive talents providing rune convertion - if (!aura || aura->GetModifier()->m_auraname != SPELL_AURA_CONVERT_RUNE) - return; - - for (uint8 i = 0; i < MAX_RUNES; ++i) - if (aura == m_runes->runes[i].ConvertAura) - return; - - if (Unit* target = aura->GetTarget()) - target->RemoveSpellAuraHolder(const_cast(aura)->GetHolder()); -} - -void Player::ConvertRune(uint8 index, RuneType newType) -{ - SetCurrentRune(index, newType); - - WorldPacket data(SMSG_CONVERT_RUNE, 2); - data << uint8(index); - data << uint8(newType); - GetSession()->SendPacket(&data); -} - -void Player::ResyncRunes() -{ - WorldPacket data(SMSG_RESYNC_RUNES, 4 + MAX_RUNES * 2); - data << uint32(MAX_RUNES); - for (uint32 i = 0; i < MAX_RUNES; ++i) - { - data << uint8(GetCurrentRune(i)); // rune type - data << uint8(GetRuneCooldownFraction(i)); - } - GetSession()->SendPacket(&data); -} - -void Player::AddRunePower(uint8 index) -{ - WorldPacket data(SMSG_ADD_RUNE_POWER, 4); - data << uint32(1 << index); // mask (0x00-0x3F probably) - GetSession()->SendPacket(&data); -} - -void Player::InitRunes() -{ - if (getClass() != CLASS_DEATH_KNIGHT) - return; - - m_runes = new Runes; - - m_runes->runeState = 0; - - for (uint32 i = 0; i < MAX_RUNES; ++i) - { - SetBaseRune(i, runeSlotTypes[i]); // init base types - SetCurrentRune(i, runeSlotTypes[i]); // init current types - SetRuneCooldown(i, 0); // reset cooldowns - m_runes->SetRuneState(i); - } - - for (uint8 i = 0; i < NUM_RUNE_TYPES; ++i) - SetFloatValue(PLAYER_RUNE_REGEN_1 + i, 0.1f); -} - -bool Player::IsBaseRuneSlotsOnCooldown(RuneType runeType) const -{ - for (uint32 i = 0; i < MAX_RUNES; ++i) - if (GetBaseRune(i) == runeType && GetRuneCooldown(i) == 0) - return false; - - return true; -} - -void Player::AutoStoreLoot(WorldObject const* lootTarget, uint32 loot_id, LootStore const& store, bool broadcast, uint8 bag, uint8 slot) -{ - Loot loot(lootTarget); - loot.FillLoot(loot_id, store, this, true); - - AutoStoreLoot(loot, broadcast, bag, slot); -} - -void Player::AutoStoreLoot(Loot& loot, bool broadcast, uint8 bag, uint8 slot) -{ - uint32 max_slot = loot.GetMaxSlotInLootFor(this); - for (uint32 i = 0; i < max_slot; ++i) - { - LootItem* lootItem = loot.LootItemInSlot(i, this); - - ItemPosCountVec dest; - InventoryResult msg = CanStoreNewItem(bag, slot, dest, lootItem->itemid, lootItem->count); - if (msg != EQUIP_ERR_OK && slot != NULL_SLOT) - msg = CanStoreNewItem(bag, NULL_SLOT, dest, lootItem->itemid, lootItem->count); - if (msg != EQUIP_ERR_OK && bag != NULL_BAG) - msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, lootItem->itemid, lootItem->count); - if (msg != EQUIP_ERR_OK) - { - SendEquipError(msg, NULL, NULL, lootItem->itemid); - continue; - } - - Item* pItem = StoreNewItem(dest, lootItem->itemid, true, lootItem->randomPropertyId); - SendNewItem(pItem, lootItem->count, false, false, broadcast); - } -} - -Item* Player::ConvertItem(Item* item, uint32 newItemId) -{ - uint16 pos = item->GetPos(); - - Item* pNewItem = Item::CreateItem(newItemId, 1, this); - if (!pNewItem) - return NULL; - - // copy enchantments - for (uint8 j = PERM_ENCHANTMENT_SLOT; j <= TEMP_ENCHANTMENT_SLOT; ++j) - { - if (item->GetEnchantmentId(EnchantmentSlot(j))) - pNewItem->SetEnchantment(EnchantmentSlot(j), item->GetEnchantmentId(EnchantmentSlot(j)), - item->GetEnchantmentDuration(EnchantmentSlot(j)), item->GetEnchantmentCharges(EnchantmentSlot(j))); - } - - // copy durability - if (item->GetUInt32Value(ITEM_FIELD_DURABILITY) < item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY)) - { - double loosePercent = 1 - item->GetUInt32Value(ITEM_FIELD_DURABILITY) / double(item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY)); - DurabilityLoss(pNewItem, loosePercent); - } - - if (IsInventoryPos(pos)) - { - ItemPosCountVec dest; - InventoryResult msg = CanStoreItem(item->GetBagSlot(), item->GetSlot(), dest, pNewItem, true); - // ignore cast/combat time restriction - if (msg == EQUIP_ERR_OK) - { - DestroyItem(item->GetBagSlot(), item->GetSlot(), true); - return StoreItem(dest, pNewItem, true); - } - } - else if (IsBankPos(pos)) - { - ItemPosCountVec dest; - InventoryResult msg = CanBankItem(item->GetBagSlot(), item->GetSlot(), dest, pNewItem, true); - // ignore cast/combat time restriction - if (msg == EQUIP_ERR_OK) - { - DestroyItem(item->GetBagSlot(), item->GetSlot(), true); - return BankItem(dest, pNewItem, true); - } - } - else if (IsEquipmentPos(pos)) - { - uint16 dest; - InventoryResult msg = CanEquipItem(item->GetSlot(), dest, pNewItem, true, false); - // ignore cast/combat time restriction - if (msg == EQUIP_ERR_OK) - { - DestroyItem(item->GetBagSlot(), item->GetSlot(), true); - pNewItem = EquipItem(dest, pNewItem, true); - AutoUnequipOffhandIfNeed(); - return pNewItem; - } - } - - // fail - delete pNewItem; - return NULL; -} - -uint32 Player::CalculateTalentsPoints() const -{ - // this dbc file has entries only up to level 100 - NumTalentsAtLevelEntry const* count = sNumTalentsAtLevelStore.LookupEntry(std::min(getLevel(), 100)); - if (!count) - return 0; - - float baseForLevel = count->Talents; - - if (getClass() != CLASS_DEATH_KNIGHT) - return uint32(baseForLevel * sWorld.getConfig(CONFIG_FLOAT_RATE_TALENT)); - - // Death Knight starting level - // hardcoded here - number of quest awarded talents is equal to number of talents any other class would have at level 55 - if (getLevel() < 55) - return 0; - - NumTalentsAtLevelEntry const* dkBase = sNumTalentsAtLevelStore.LookupEntry(55); - if (!dkBase) - return 0; - - float talentPointsForLevel = count->Talents - dkBase->Talents; - talentPointsForLevel += float(m_questRewardTalentCount); - - if (talentPointsForLevel > baseForLevel) - talentPointsForLevel = baseForLevel; - - return uint32(talentPointsForLevel * sWorld.getConfig(CONFIG_FLOAT_RATE_TALENT)); -} - -bool Player::CanStartFlyInArea(uint32 mapid, uint32 zone, uint32 area) const -{ - if (isGameMaster()) - return true; - - // continent checked in SpellMgr::GetSpellAllowedInLocationError at cast and area update - uint32 v_map = GetVirtualMapForMapAndZone(mapid, zone); - - // switch all known flying maps - switch (v_map) - { - case 0: // Eastern Kingdoms - case 1: // Kalimdor - case 646: // Deepholm - return HasSpell(90267); - case 530: // Outland - return true; - case 571: // Northrend - // Check Cold Weather Flying - // Disallow mounting in wintergrasp when battle is in progress - return HasSpell(54197); /* && (!inWintergrasp || wg->GetState() != BF_IN_PROGRESS);*/ - } - - return false; -} - -struct DoPlayerLearnSpell -{ - DoPlayerLearnSpell(Player& _player) : player(_player) {} - void operator()(uint32 spell_id) { player.learnSpell(spell_id, false); } - Player& player; -}; - -void Player::learnSpellHighRank(uint32 spellid) -{ - learnSpell(spellid, false); - - DoPlayerLearnSpell worker(*this); - sSpellMgr.doForHighRanks(spellid, worker); -} - -void Player::_LoadSkills(QueryResult* result) -{ - // 0 1 2 - // SetPQuery(PLAYER_LOGIN_QUERY_LOADSKILLS, "SELECT skill, value, max FROM character_skills WHERE guid = '%u'", GUID_LOPART(m_guid)); - - uint32 count = 0; - uint8 professionCount = 0; - if (result) - { - do - { - Field* fields = result->Fetch(); - - uint16 skill = fields[0].GetUInt16(); - uint16 value = fields[1].GetUInt16(); - uint16 max = fields[2].GetUInt16(); - - SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(skill); - if (!pSkill) - { - sLog.outError("Character %u has skill %u that does not exist.", GetGUIDLow(), skill); - continue; - } - - // set fixed skill ranges - switch (GetSkillRangeType(pSkill, false)) - { - case SKILL_RANGE_LANGUAGE: // 300..300 - value = max = 300; - break; - case SKILL_RANGE_MONO: // 1..1, grey monolite bar - value = max = 1; - break; - default: - break; - } - - if (value == 0) - { - sLog.outError("Character %u has skill %u with value 0. Will be deleted.", GetGUIDLow(), skill); - CharacterDatabase.PExecute("DELETE FROM character_skills WHERE guid = '%u' AND skill = '%u' ", GetGUIDLow(), skill); - continue; - } - - uint16 field = count / 2; - uint8 offset = count & 1; - - SetUInt16Value(PLAYER_SKILL_LINEID_0 + field, offset, skill); - uint16 step = 0; - - if (pSkill->categoryId == SKILL_CATEGORY_SECONDARY) - step = max / 75; - - if (pSkill->categoryId == SKILL_CATEGORY_PROFESSION) - { - step = max / 75; - - if (professionCount < 2) - SetUInt32Value(PLAYER_PROFESSION_SKILL_LINE_1 + professionCount++, skill); - } - - SetUInt16Value(PLAYER_SKILL_STEP_0 + field, offset, step); - SetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset, value); - SetUInt16Value(PLAYER_SKILL_MAX_RANK_0 + field, offset, max); - SetUInt16Value(PLAYER_SKILL_MODIFIER_0 + field, offset, 0); - SetUInt16Value(PLAYER_SKILL_TALENT_0 + field, offset, 0); - - mSkillStatus.insert(SkillStatusMap::value_type(skill, SkillStatusData(count, SKILL_UNCHANGED))); - - learnSkillRewardedSpells(skill, value); - - ++count; - - if (count >= PLAYER_MAX_SKILLS) // client limit - { - sLog.outError("Character %u has more than %u skills.", GetGUIDLow(), PLAYER_MAX_SKILLS); - break; - } - } - while (result->NextRow()); - delete result; - } - - for (; count < PLAYER_MAX_SKILLS; ++count) - { - uint16 field = count / 2; - uint8 offset = count & 1; - - SetUInt16Value(PLAYER_SKILL_LINEID_0 + field, offset, 0); - SetUInt16Value(PLAYER_SKILL_STEP_0 + field, offset, 0); - SetUInt16Value(PLAYER_SKILL_RANK_0 + field, offset, 0); - SetUInt16Value(PLAYER_SKILL_MAX_RANK_0 + field, offset, 0); - SetUInt16Value(PLAYER_SKILL_MODIFIER_0 + field, offset, 0); - SetUInt16Value(PLAYER_SKILL_TALENT_0 + field, offset, 0); - } - - // special settings - if (getClass() == CLASS_DEATH_KNIGHT) - { - uint32 base_level = std::min(getLevel(), sWorld.getConfig(CONFIG_UINT32_START_HEROIC_PLAYER_LEVEL)); - if (base_level < 1) - base_level = 1; - uint32 base_skill = (base_level - 1) * 5; // 270 at starting level 55 - if (base_skill < 1) - base_skill = 1; // skill mast be known and then > 0 in any case - - if (GetPureSkillValue(SKILL_FIRST_AID) < base_skill) - SetSkill(SKILL_FIRST_AID, base_skill, base_skill); - if (GetPureSkillValue(SKILL_AXES) < base_skill) - SetSkill(SKILL_AXES, base_skill, base_skill); - if (GetPureSkillValue(SKILL_DEFENSE) < base_skill) - SetSkill(SKILL_DEFENSE, base_skill, base_skill); - if (GetPureSkillValue(SKILL_POLEARMS) < base_skill) - SetSkill(SKILL_POLEARMS, base_skill, base_skill); - if (GetPureSkillValue(SKILL_SWORDS) < base_skill) - SetSkill(SKILL_SWORDS, base_skill, base_skill); - if (GetPureSkillValue(SKILL_2H_AXES) < base_skill) - SetSkill(SKILL_2H_AXES, base_skill, base_skill); - if (GetPureSkillValue(SKILL_2H_SWORDS) < base_skill) - SetSkill(SKILL_2H_SWORDS, base_skill, base_skill); - if (GetPureSkillValue(SKILL_UNARMED) < base_skill) - SetSkill(SKILL_UNARMED, base_skill, base_skill); - } -} - -InventoryResult Player::CanEquipUniqueItem(Item* pItem, uint8 eslot, uint32 limit_count) const -{ - ItemPrototype const* pProto = pItem->GetProto(); - - // proto based limitations - if (InventoryResult res = CanEquipUniqueItem(pProto, eslot, limit_count)) - return res; - - // check unique-equipped on gems - for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT + 3; ++enchant_slot) - { - uint32 enchant_id = pItem->GetEnchantmentId(EnchantmentSlot(enchant_slot)); - if (!enchant_id) - continue; - SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id); - if (!enchantEntry) - continue; - - ItemPrototype const* pGem = ObjectMgr::GetItemPrototype(enchantEntry->GemID); - if (!pGem) - continue; - - // include for check equip another gems with same limit category for not equipped item (and then not counted) - uint32 gem_limit_count = !pItem->IsEquipped() && pGem->ItemLimitCategory - ? pItem->GetGemCountWithLimitCategory(pGem->ItemLimitCategory) : 1; - - if (InventoryResult res = CanEquipUniqueItem(pGem, eslot, gem_limit_count)) - return res; - } - - return EQUIP_ERR_OK; -} - -InventoryResult Player::CanEquipUniqueItem(ItemPrototype const* itemProto, uint8 except_slot, uint32 limit_count) const -{ - // check unique-equipped on item - if (itemProto->Flags & ITEM_FLAG_UNIQUE_EQUIPPED) - { - // there is an equip limit on this item - if (HasItemOrGemWithIdEquipped(itemProto->ItemId, 1, except_slot)) - return EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE; - } - - // check unique-equipped limit - if (itemProto->ItemLimitCategory) - { - ItemLimitCategoryEntry const* limitEntry = sItemLimitCategoryStore.LookupEntry(itemProto->ItemLimitCategory); - if (!limitEntry) - return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED; - - // NOTE: limitEntry->mode not checked because if item have have-limit then it applied and to equip case - - if (limit_count > limitEntry->maxCount) - return EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED_IS; - - // there is an equip limit on this item - if (HasItemOrGemWithLimitCategoryEquipped(itemProto->ItemLimitCategory, limitEntry->maxCount - limit_count + 1, except_slot)) - return EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED_IS; - } - - return EQUIP_ERR_OK; -} - -void Player::HandleFall(MovementInfo const& movementInfo) -{ - // calculate total z distance of the fall - float z_diff = m_lastFallZ - movementInfo.GetPos()->z; - DEBUG_LOG("zDiff = %f", z_diff); - - // Players with low fall distance, Feather Fall or physical immunity (charges used) are ignored - // 14.57 can be calculated by resolving damageperc formula below to 0 - if (z_diff >= 14.57f && !isDead() && !isGameMaster() && /*!HasMovementFlag(MOVEFLAG_ONTRANSPORT) &&*/ - !HasAuraType(SPELL_AURA_HOVER) && !HasAuraType(SPELL_AURA_FEATHER_FALL) && - !HasAuraType(SPELL_AURA_FLY) && !IsImmunedToDamage(SPELL_SCHOOL_MASK_NORMAL)) - { - // Safe fall, fall height reduction - int32 safe_fall = GetTotalAuraModifier(SPELL_AURA_SAFE_FALL); - - float damageperc = 0.018f * (z_diff - safe_fall) - 0.2426f; - - if (damageperc > 0) - { - uint32 damage = (uint32)(damageperc * GetMaxHealth() * sWorld.getConfig(CONFIG_FLOAT_RATE_DAMAGE_FALL)); - - float height = movementInfo.GetPos()->z; - UpdateAllowedPositionZ(movementInfo.GetPos()->x, movementInfo.GetPos()->y, height); - - if (damage > 0) - { - // Prevent fall damage from being more than the player maximum health - if (damage > GetMaxHealth()) - damage = GetMaxHealth(); - - // Gust of Wind - if (GetDummyAura(43621)) - damage = GetMaxHealth() / 2; - - uint32 original_health = GetHealth(); - uint32 final_damage = EnvironmentalDamage(DAMAGE_FALL, damage); - - // recheck alive, might have died of EnvironmentalDamage, avoid cases when player die in fact like Spirit of Redemption case - if (isAlive() && final_damage < original_health) - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING, uint32(z_diff * 100)); - } - - // Z given by moveinfo, LastZ, FallTime, WaterZ, MapZ, Damage, Safefall reduction - DEBUG_LOG("FALLDAMAGE z=%f sz=%f pZ=%f FallTime=%d mZ=%f damage=%d SF=%d" , movementInfo.GetPos()->z, height, GetPositionZ(), movementInfo.GetFallTime(), height, damage, safe_fall); - } - } -} - -void Player::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1/*=0*/, uint32 miscvalue2/*=0*/, Unit* unit/*=NULL*/, uint32 time/*=0*/) -{ - GetAchievementMgr().UpdateAchievementCriteria(type, miscvalue1, miscvalue2, unit, time); -} - -void Player::StartTimedAchievementCriteria(AchievementCriteriaTypes type, uint32 timedRequirementId, time_t startTime /*= 0*/) -{ - GetAchievementMgr().StartTimedAchievementCriteria(type, timedRequirementId, startTime); -} - -PlayerTalent const* Player::GetKnownTalentById(int32 talentId) const -{ - PlayerTalentMap::const_iterator itr = m_talents[m_activeSpec].find(talentId); - if (itr != m_talents[m_activeSpec].end() && itr->second.state != PLAYERSPELL_REMOVED) - return &itr->second; - else - return NULL; -} - -SpellEntry const* Player::GetKnownTalentRankById(int32 talentId) const -{ - if (PlayerTalent const* talent = GetKnownTalentById(talentId)) - return sSpellStore.LookupEntry(talent->talentEntry->RankID[talent->currentRank]); - else - return NULL; -} - -bool Player::LearnTalent(uint32 talentId, uint32 talentRank) -{ - uint32 CurTalentPoints = GetFreeTalentPoints(); - - if (CurTalentPoints == 0) - return false; - - if (talentRank >= MAX_TALENT_RANK) - return false; - - TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId); - - if (!talentInfo) - return false; - - TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab); - - if (!talentTabInfo) - return false; - - // prevent learn talent for different class (cheating) - if ((getClassMask() & talentTabInfo->ClassMask) == 0) - return false; - - // find current max talent rank - uint32 curtalent_maxrank = 0; - if (PlayerTalent const* talent = GetKnownTalentById(talentId)) - curtalent_maxrank = talent->currentRank + 1; - - // we already have same or higher talent rank learned - if (curtalent_maxrank >= (talentRank + 1)) - return false; - - // check if we have enough talent points - if (CurTalentPoints < (talentRank - curtalent_maxrank + 1)) - return false; - - // Check if it requires another talent - if (talentInfo->DependsOn > 0) - { - if (TalentEntry const* depTalentInfo = sTalentStore.LookupEntry(talentInfo->DependsOn)) - { - bool hasEnoughRank = false; - PlayerTalentMap::iterator dependsOnTalent = m_talents[m_activeSpec].find(depTalentInfo->TalentID); - if (dependsOnTalent != m_talents[m_activeSpec].end() && dependsOnTalent->second.state != PLAYERSPELL_REMOVED) - { - PlayerTalent depTalent = (*dependsOnTalent).second; - if (depTalent.currentRank >= talentInfo->DependsOnRank) - hasEnoughRank = true; - } - - if (!hasEnoughRank) - return false; - } - } - - // Find out how many points we have in this field - uint32 spentPoints = 0; - - uint32 primaryTreeTalents = 0; - uint32 tTab = talentInfo->TalentTab; - bool isMainTree = m_talentsPrimaryTree[m_activeSpec] == tTab || !m_talentsPrimaryTree[m_activeSpec]; - - if (talentInfo->Row > 0 || !isMainTree) - { - for (uint32 i = 0; i < sTalentStore.GetNumRows(); i++) // Loop through all talents. - { - if (TalentEntry const* tmpTalent = sTalentStore.LookupEntry(i)) // Someday, someone needs to revamp the way talents are tracked - { - for (uint8 rank = 0; rank < MAX_TALENT_RANK; rank++) - { - if (tmpTalent->RankID[rank] != 0) - { - if (HasSpell(tmpTalent->RankID[rank])) - { - if (tmpTalent->TalentTab == tTab) - spentPoints += (rank + 1); - if (tmpTalent->TalentTab == m_talentsPrimaryTree[m_activeSpec]) - primaryTreeTalents += (rank + 1); - } - } - } - } - } - } - - // not have required min points spent in talent tree - if (spentPoints < (talentInfo->Row * MAX_TALENT_RANK)) - return false; - - // player has not spent 31 talents in main tree before attempting to learn other tree's talents - if (!isMainTree && primaryTreeTalents < REQ_PRIMARY_TREE_TALENTS) - return false; - - // spell not set in talent.dbc - uint32 spellid = talentInfo->RankID[talentRank]; - if (spellid == 0) - { - sLog.outError("Talent.dbc have for talent: %u Rank: %u spell id = 0", talentId, talentRank); - return false; - } - - // already known - if (HasSpell(spellid)) - return false; - - // learn! (other talent ranks will unlearned at learning) - learnSpell(spellid, false); - DETAIL_LOG("TalentID: %u Rank: %u Spell: %u\n", talentId, talentRank, spellid); - - // set talent tree for player - if (!m_talentsPrimaryTree[m_activeSpec]) - { - m_talentsPrimaryTree[m_activeSpec] = talentInfo->TalentTab; - if (std::vector const* specSpells = GetTalentTreeMasterySpells(talentInfo->TalentTab)) - for (size_t i = 0; i < specSpells->size(); ++i) - learnSpell(specSpells->at(i), false); - - if (std::vector const* specSpells = GetTalentTreePrimarySpells(talentInfo->TalentTab)) - for (size_t i = 0; i < specSpells->size(); ++i) - learnSpell(specSpells->at(i), false); - - // Update talent tree role-dependent mana regen - UpdateManaRegen(); - UpdateArmorSpecializations(); - } - - return true; -} - -void Player::LearnPetTalent(ObjectGuid petGuid, uint32 talentId, uint32 talentRank) -{ - Pet* pet = GetPet(); - if (!pet) - return; - - if (petGuid != pet->GetObjectGuid()) - return; - - uint32 CurTalentPoints = pet->GetFreeTalentPoints(); - - if (CurTalentPoints == 0) - return; - - if (talentRank >= MAX_PET_TALENT_RANK) - return; - - TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId); - - if (!talentInfo) - return; - - TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab); - - if (!talentTabInfo) - return; - - CreatureInfo const* ci = pet->GetCreatureInfo(); - - if (!ci) - return; - - CreatureFamilyEntry const* pet_family = sCreatureFamilyStore.LookupEntry(ci->family); - - if (!pet_family) - return; - - if (pet_family->petTalentType < 0) // not hunter pet - return; - - // prevent learn talent for different family (cheating) - if (!((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask)) - return; - - // find current max talent rank - int32 curtalent_maxrank = 0; - for (int32 k = MAX_TALENT_RANK - 1; k > -1; --k) - { - if (talentInfo->RankID[k] && pet->HasSpell(talentInfo->RankID[k])) - { - curtalent_maxrank = k + 1; - break; - } - } - - // we already have same or higher talent rank learned - if (curtalent_maxrank >= int32(talentRank + 1)) - return; - - // check if we have enough talent points - if (CurTalentPoints < (talentRank - curtalent_maxrank + 1)) - return; - - // Check if it requires another talent - if (talentInfo->DependsOn > 0) - { - if (TalentEntry const* depTalentInfo = sTalentStore.LookupEntry(talentInfo->DependsOn)) - { - bool hasEnoughRank = false; - for (int i = talentInfo->DependsOnRank; i < MAX_TALENT_RANK; ++i) - { - if (depTalentInfo->RankID[i] != 0) - if (pet->HasSpell(depTalentInfo->RankID[i])) - hasEnoughRank = true; - } - if (!hasEnoughRank) - return; - } - } - - // Find out how many points we have in this field - uint32 spentPoints = 0; - - uint32 tTab = talentInfo->TalentTab; - if (talentInfo->Row > 0) - { - unsigned int numRows = sTalentStore.GetNumRows(); - for (unsigned int i = 0; i < numRows; ++i) // Loop through all talents. - { - // Someday, someone needs to revamp - const TalentEntry* tmpTalent = sTalentStore.LookupEntry(i); - if (tmpTalent) // the way talents are tracked - { - if (tmpTalent->TalentTab == tTab) - { - for (int j = 0; j < MAX_TALENT_RANK; ++j) - { - if (tmpTalent->RankID[j] != 0) - { - if (pet->HasSpell(tmpTalent->RankID[j])) - { - spentPoints += j + 1; - } - } - } - } - } - } - } - - // not have required min points spent in talent tree - if (spentPoints < (talentInfo->Row * MAX_PET_TALENT_RANK)) - return; - - // spell not set in talent.dbc - uint32 spellid = talentInfo->RankID[talentRank]; - if (spellid == 0) - { - sLog.outError("Talent.dbc have for talent: %u Rank: %u spell id = 0", talentId, talentRank); - return; - } - - // already known - if (pet->HasSpell(spellid)) - return; - - // learn! (other talent ranks will unlearned at learning) - pet->learnSpell(spellid); - DETAIL_LOG("PetTalentID: %u Rank: %u Spell: %u\n", talentId, talentRank, spellid); -} - -void Player::UpdateFallInformationIfNeed(MovementInfo const& minfo, uint16 opcode) -{ - if (m_lastFallTime >= minfo.GetFallTime() || m_lastFallZ <= minfo.GetPos()->z || opcode == CMSG_MOVE_FALL_LAND) - SetFallInformation(minfo.GetFallTime(), minfo.GetPos()->z); -} - -void Player::UnsummonPetTemporaryIfAny() -{ - Pet* pet = GetPet(); - if (!pet) - return; - - if (!m_temporaryUnsummonedPetNumber && pet->isControlled() && !pet->isTemporarySummoned()) - m_temporaryUnsummonedPetNumber = pet->GetCharmInfo()->GetPetNumber(); - - pet->Unsummon(PET_SAVE_AS_CURRENT, this); -} - -void Player::ResummonPetTemporaryUnSummonedIfAny() -{ - if (!m_temporaryUnsummonedPetNumber) - return; - - // not resummon in not appropriate state - if (IsPetNeedBeTemporaryUnsummoned()) - return; - - if (GetPetGuid()) - return; - - Pet* NewPet = new Pet; - if (!NewPet->LoadPetFromDB(this, 0, m_temporaryUnsummonedPetNumber, true)) - delete NewPet; - - m_temporaryUnsummonedPetNumber = 0; -} - -bool Player::canSeeSpellClickOn(Creature const* c) const -{ - if (!c->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK)) - return false; - - SpellClickInfoMapBounds clickPair = sObjectMgr.GetSpellClickInfoMapBounds(c->GetEntry()); - for (SpellClickInfoMap::const_iterator itr = clickPair.first; itr != clickPair.second; ++itr) - if (itr->second.IsFitToRequirements(this, c)) - return true; - - return false; -} - -void Player::BuildPlayerTalentsInfoData(WorldPacket* data) -{ - *data << uint32(GetFreeTalentPoints()); // unspentTalentPoints - *data << uint8(m_specsCount); // talent group count (0, 1 or 2) - *data << uint8(m_activeSpec); // talent group index (0 or 1) - - if (m_specsCount) - { - if (m_specsCount > MAX_TALENT_SPEC_COUNT) - m_specsCount = MAX_TALENT_SPEC_COUNT; - - // loop through all specs (only 1 for now) - for (uint32 specIdx = 0; specIdx < m_specsCount; ++specIdx) - { - *data << uint32(m_talentsPrimaryTree[specIdx]); - uint8 talentIdCount = 0; - size_t pos = data->wpos(); - *data << uint8(talentIdCount); // [PH], talentIdCount - - // find class talent tabs (all players have 3 talent tabs) - uint32 const* talentTabIds = GetTalentTabPages(getClass()); - - for (uint32 i = 0; i < MAX_TALENT_TABS; ++i) - { - uint32 talentTabId = talentTabIds[i]; - for (PlayerTalentMap::iterator iter = m_talents[specIdx].begin(); iter != m_talents[specIdx].end(); ++iter) - { - PlayerTalent talent = (*iter).second; - - if (talent.state == PLAYERSPELL_REMOVED) - continue; - - // skip another tab talents - if (talent.talentEntry->TalentTab != talentTabId) - continue; - - *data << uint32(talent.talentEntry->TalentID); // Talent.dbc - *data << uint8(talent.currentRank); // talentMaxRank (0-4) - - ++talentIdCount; - } - } - - data->put(pos, talentIdCount); // put real count - - *data << uint8(MAX_GLYPH_SLOT_INDEX); // glyphs count - - // GlyphProperties.dbc - for (uint8 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i) - *data << uint16(m_glyphs[specIdx][i].GetId()); - } - } -} - -void Player::BuildPetTalentsInfoData(WorldPacket* data) -{ - uint32 unspentTalentPoints = 0; - size_t pointsPos = data->wpos(); - *data << uint32(unspentTalentPoints); // [PH], unspentTalentPoints - - uint8 talentIdCount = 0; - size_t countPos = data->wpos(); - *data << uint8(talentIdCount); // [PH], talentIdCount - - Pet* pet = GetPet(); - if (!pet) - return; - - unspentTalentPoints = pet->GetFreeTalentPoints(); - - data->put(pointsPos, unspentTalentPoints); // put real points - - CreatureInfo const* ci = pet->GetCreatureInfo(); - if (!ci) - return; - - CreatureFamilyEntry const* pet_family = sCreatureFamilyStore.LookupEntry(ci->family); - if (!pet_family || pet_family->petTalentType < 0) - return; - - for (uint32 talentTabId = 1; talentTabId < sTalentTabStore.GetNumRows(); ++talentTabId) - { - TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentTabId); - if (!talentTabInfo) - continue; - - if (!((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask)) - continue; - - for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId) - { - TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId); - if (!talentInfo) - continue; - - // skip another tab talents - if (talentInfo->TalentTab != talentTabId) - continue; - - // find max talent rank - int32 curtalent_maxrank = -1; - for (int32 k = 4; k > -1; --k) - { - if (talentInfo->RankID[k] && pet->HasSpell(talentInfo->RankID[k])) - { - curtalent_maxrank = k; - break; - } - } - - // not learned talent - if (curtalent_maxrank < 0) - continue; - - *data << uint32(talentInfo->TalentID); // Talent.dbc - *data << uint8(curtalent_maxrank); // talentMaxRank (0-4) - - ++talentIdCount; - } - - data->put(countPos, talentIdCount); // put real count - - break; - } -} - -void Player::SendTalentsInfoData(bool pet) -{ - WorldPacket data(SMSG_TALENT_UPDATE, 50); - data << uint8(pet ? 1 : 0); - if (pet) - BuildPetTalentsInfoData(&data); - else - BuildPlayerTalentsInfoData(&data); - GetSession()->SendPacket(&data); -} - -void Player::BuildEnchantmentsInfoData(WorldPacket* data) -{ - uint32 slotUsedMask = 0; - size_t slotUsedMaskPos = data->wpos(); - *data << uint32(slotUsedMask); // slotUsedMask < 0x80000 - - for (uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i) - { - Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i); - - if (!item) - continue; - - slotUsedMask |= (1 << i); - - *data << uint32(item->GetEntry()); // item entry - - uint16 enchantmentMask = 0; - size_t enchantmentMaskPos = data->wpos(); - *data << uint16(enchantmentMask); // enchantmentMask < 0x1000 - - for (uint32 j = 0; j < MAX_ENCHANTMENT_SLOT; ++j) - { - uint32 enchId = item->GetEnchantmentId(EnchantmentSlot(j)); - - if (!enchId) - continue; - - enchantmentMask |= (1 << j); - - *data << uint16(enchId); // enchantmentId? - } - - data->put(enchantmentMaskPos, enchantmentMask); - - *data << uint16(item->GetItemRandomPropertyId()); - *data << item->GetGuidValue(ITEM_FIELD_CREATOR).WriteAsPacked(); - *data << uint32(item->GetItemSuffixFactor()); - } - - data->put(slotUsedMaskPos, slotUsedMask); -} - -void Player::SendEquipmentSetList() -{ - uint32 count = 0; - WorldPacket data(SMSG_LOAD_EQUIPMENT_SET, 4); - size_t count_pos = data.wpos(); - data << uint32(count); // count placeholder - for (EquipmentSets::iterator itr = m_EquipmentSets.begin(); itr != m_EquipmentSets.end(); ++itr) - { - if (itr->second.state == EQUIPMENT_SET_DELETED) - continue; - data.appendPackGUID(itr->second.Guid); - data << uint32(itr->first); - data << itr->second.Name; - data << itr->second.IconName; - for (uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i) - { - // ignored slots stored in IgnoreMask, client wants "1" as raw GUID, so no HIGHGUID_ITEM - if (itr->second.IgnoreMask & (1 << i)) - data << ObjectGuid(uint64(1)).WriteAsPacked(); - else - data << ObjectGuid(HIGHGUID_ITEM, itr->second.Items[i]).WriteAsPacked(); - } - - ++count; // client have limit but it checked at loading and set - } - data.put(count_pos, count); - GetSession()->SendPacket(&data); -} - -void Player::SetEquipmentSet(uint32 index, EquipmentSet eqset) -{ - if (eqset.Guid != 0) - { - bool found = false; - - for (EquipmentSets::iterator itr = m_EquipmentSets.begin(); itr != m_EquipmentSets.end(); ++itr) - { - if ((itr->second.Guid == eqset.Guid) && (itr->first == index)) - { - found = true; - break; - } - } - - if (!found) // something wrong... - { - sLog.outError("Player %s tried to save equipment set " UI64FMTD " (index %u), but that equipment set not found!", GetName(), eqset.Guid, index); - return; - } - } - - EquipmentSet& eqslot = m_EquipmentSets[index]; - - EquipmentSetUpdateState old_state = eqslot.state; - - eqslot = eqset; - - if (eqset.Guid == 0) - { - eqslot.Guid = sObjectMgr.GenerateEquipmentSetGuid(); - - WorldPacket data(SMSG_EQUIPMENT_SET_ID, 4 + 1); - data << uint32(index); - data.appendPackGUID(eqslot.Guid); - GetSession()->SendPacket(&data); - } - - eqslot.state = old_state == EQUIPMENT_SET_NEW ? EQUIPMENT_SET_NEW : EQUIPMENT_SET_CHANGED; -} - -void Player::_SaveEquipmentSets() -{ - static SqlStatementID updSets ; - static SqlStatementID insSets ; - static SqlStatementID delSets ; - - for (EquipmentSets::iterator itr = m_EquipmentSets.begin(); itr != m_EquipmentSets.end();) - { - uint32 index = itr->first; - EquipmentSet& eqset = itr->second; - switch (eqset.state) - { - case EQUIPMENT_SET_UNCHANGED: - ++itr; - break; // nothing do - case EQUIPMENT_SET_CHANGED: - { - SqlStatement stmt = CharacterDatabase.CreateStatement(updSets, "UPDATE character_equipmentsets SET name=?, iconname=?, ignore_mask=?, item0=?, item1=?, item2=?, item3=?, item4=?, " - "item5=?, item6=?, item7=?, item8=?, item9=?, item10=?, item11=?, item12=?, item13=?, item14=?, " - "item15=?, item16=?, item17=?, item18=? WHERE guid=? AND setguid=? AND setindex=?"); - - stmt.addString(eqset.Name); - stmt.addString(eqset.IconName); - stmt.addUInt32(eqset.IgnoreMask); - - for (int i = 0; i < EQUIPMENT_SLOT_END; ++i) - stmt.addUInt32(eqset.Items[i]); - - stmt.addUInt32(GetGUIDLow()); - stmt.addUInt64(eqset.Guid); - stmt.addUInt32(index); - - stmt.Execute(); - - eqset.state = EQUIPMENT_SET_UNCHANGED; - ++itr; - break; - } - case EQUIPMENT_SET_NEW: - { - SqlStatement stmt = CharacterDatabase.CreateStatement(insSets, "INSERT INTO character_equipmentsets VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); - stmt.addUInt32(GetGUIDLow()); - stmt.addUInt64(eqset.Guid); - stmt.addUInt32(index); - stmt.addString(eqset.Name); - stmt.addString(eqset.IconName); - stmt.addUInt32(eqset.IgnoreMask); - - for (int i = 0; i < EQUIPMENT_SLOT_END; ++i) - stmt.addUInt32(eqset.Items[i]); - - stmt.Execute(); - - eqset.state = EQUIPMENT_SET_UNCHANGED; - ++itr; - break; - } - case EQUIPMENT_SET_DELETED: - { - SqlStatement stmt = CharacterDatabase.CreateStatement(delSets, "DELETE FROM character_equipmentsets WHERE setguid = ?"); - stmt.PExecute(eqset.Guid); - m_EquipmentSets.erase(itr++); - break; - } - } - } -} - -void Player::_SaveBGData() -{ - // nothing save - if (!m_bgData.m_needSave) - return; - - static SqlStatementID delBGData ; - static SqlStatementID insBGData ; - - SqlStatement stmt = CharacterDatabase.CreateStatement(delBGData, "DELETE FROM character_battleground_data WHERE guid = ?"); - - stmt.PExecute(GetGUIDLow()); - - if (m_bgData.bgInstanceID) - { - stmt = CharacterDatabase.CreateStatement(insBGData, "INSERT INTO character_battleground_data VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); - /* guid, bgInstanceID, bgTeam, x, y, z, o, map, taxi[0], taxi[1], mountSpell */ - stmt.addUInt32(GetGUIDLow()); - stmt.addUInt32(m_bgData.bgInstanceID); - stmt.addUInt32(uint32(m_bgData.bgTeam)); - stmt.addFloat(m_bgData.joinPos.coord_x); - stmt.addFloat(m_bgData.joinPos.coord_y); - stmt.addFloat(m_bgData.joinPos.coord_z); - stmt.addFloat(m_bgData.joinPos.orientation); - stmt.addUInt32(m_bgData.joinPos.mapid); - stmt.addUInt32(m_bgData.taxiPath[0]); - stmt.addUInt32(m_bgData.taxiPath[1]); - stmt.addUInt32(m_bgData.mountSpell); - - stmt.Execute(); - } - - m_bgData.m_needSave = false; -} - -void Player::DeleteEquipmentSet(uint64 setGuid) -{ - for (EquipmentSets::iterator itr = m_EquipmentSets.begin(); itr != m_EquipmentSets.end(); ++itr) - { - if (itr->second.Guid == setGuid) - { - if (itr->second.state == EQUIPMENT_SET_NEW) - m_EquipmentSets.erase(itr); - else - itr->second.state = EQUIPMENT_SET_DELETED; - break; - } - } -} - -void Player::ActivateSpec(uint8 specNum) -{ - if (GetActiveSpec() == specNum) - return; - - if (specNum >= GetSpecsCount()) - return; - - UnsummonPetTemporaryIfAny(); - - // prevent deletion of action buttons by client at spell unlearn or by player while spec change in progress - SendLockActionButtons(); - - // Remove spec specific spells - for (uint32 i = 0; i < MAX_TALENT_TABS; ++i) - { - if (std::vector const* specSpells = GetTalentTreeMasterySpells(GetTalentTabPages(getClass())[i])) - for (size_t i = 0; i < specSpells->size(); ++i) - removeSpell(specSpells->at(i), true); - - if (std::vector const* specSpells = GetTalentTreePrimarySpells(GetTalentTabPages(getClass())[i])) - for (size_t i = 0; i < specSpells->size(); ++i) - removeSpell(specSpells->at(i), true); - } - - ApplyGlyphs(false); - - // copy of new talent spec (we will use it as model for converting current tlanet state to new) - PlayerTalentMap tempSpec = m_talents[specNum]; - - // copy old spec talents to new one, must be before spec switch to have previous spec num(as m_activeSpec) - m_talents[specNum] = m_talents[m_activeSpec]; - - SetActiveSpec(specNum); - - // remove all talent spells that don't exist in next spec but exist in old - for (PlayerTalentMap::iterator specIter = m_talents[m_activeSpec].begin(); specIter != m_talents[m_activeSpec].end();) - { - PlayerTalent& talent = specIter->second; - - if (talent.state == PLAYERSPELL_REMOVED) - { - ++specIter; - continue; - } - - PlayerTalentMap::iterator iterTempSpec = tempSpec.find(specIter->first); - - // remove any talent rank if talent not listed in temp spec - if (iterTempSpec == tempSpec.end() || iterTempSpec->second.state == PLAYERSPELL_REMOVED) - { - TalentEntry const* talentInfo = talent.talentEntry; - - for (int r = 0; r < MAX_TALENT_RANK; ++r) - if (talentInfo->RankID[r]) - removeSpell(talentInfo->RankID[r], !IsPassiveSpell(talentInfo->RankID[r]), false); - - specIter = m_talents[m_activeSpec].begin(); - } - else - ++specIter; - } - - // now new spec data have only talents (maybe different rank) as in temp spec data, sync ranks then. - for (PlayerTalentMap::const_iterator tempIter = tempSpec.begin(); tempIter != tempSpec.end(); ++tempIter) - { - PlayerTalent const& talent = tempIter->second; - - // removed state talent already unlearned in prev. loop - // but we need restore it if it deleted for finish removed-marked data in DB - if (talent.state == PLAYERSPELL_REMOVED) - { - m_talents[m_activeSpec][tempIter->first] = talent; - continue; - } - - uint32 talentSpellId = talent.talentEntry->RankID[talent.currentRank]; - - // learn talent spells if they not in new spec (old spec copy) - // and if they have different rank - if (PlayerTalent const* cur_talent = GetKnownTalentById(tempIter->first)) - { - if (cur_talent->currentRank != talent.currentRank) - learnSpell(talentSpellId, false); - } - else - learnSpell(talentSpellId, false); - - // sync states - original state is changed in addSpell that learnSpell calls - PlayerTalentMap::iterator specIter = m_talents[m_activeSpec].find(tempIter->first); - if (specIter != m_talents[m_activeSpec].end()) - specIter->second.state = talent.state; - else - { - sLog.outError("ActivateSpec: Talent spell %u expected to learned at spec switch but not listed in talents at final check!", talentSpellId); - - // attempt resync DB state (deleted lost spell from DB) - if (talent.state != PLAYERSPELL_NEW) - { - PlayerTalent& talentNew = m_talents[m_activeSpec][tempIter->first]; - talentNew = talent; - talentNew.state = PLAYERSPELL_REMOVED; - } - } - } - - InitTalentForLevel(); - - // recheck action buttons (not checked at loading/spec copy) - ActionButtonList const& currentActionButtonList = m_actionButtons[m_activeSpec]; - for (ActionButtonList::const_iterator itr = currentActionButtonList.begin(); itr != currentActionButtonList.end();) - { - if (itr->second.uState != ACTIONBUTTON_DELETED) - { - // remove broken without any output (it can be not correct because talents not copied at spec creating) - if (!IsActionButtonDataValid(itr->first, itr->second.GetAction(), itr->second.GetType(), this, false)) - { - removeActionButton(m_activeSpec, itr->first); - itr = currentActionButtonList.begin(); - continue; - } - } - ++itr; - } - - ResummonPetTemporaryUnSummonedIfAny(); - - if (std::vector const* specSpells = GetTalentTreeMasterySpells(m_talentsPrimaryTree[m_activeSpec])) - for (size_t i = 0; i < specSpells->size(); ++i) - learnSpell(specSpells->at(i), false); - - if (std::vector const* specSpells = GetTalentTreePrimarySpells(m_talentsPrimaryTree[m_activeSpec])) - for (size_t i = 0; i < specSpells->size(); ++i) - learnSpell(specSpells->at(i), false); - - ApplyGlyphs(true); - - SendInitialActionButtons(); - - Powers pw = getPowerType(); - if (pw != POWER_MANA) - SetPower(POWER_MANA, 0); - - SetPower(pw, 0); - - if (m_talentsPrimaryTree[m_activeSpec] && !sTalentTabStore.LookupEntry(m_talentsPrimaryTree[m_activeSpec])) - resetTalents(true); - - // Update talent tree role-dependent mana regen - UpdateManaRegen(); - - UpdateArmorSpecializations(); -} - -void Player::UpdateSpecCount(uint8 count) -{ - uint8 curCount = GetSpecsCount(); - if (curCount == count) - return; - - // maybe current spec data must be copied to 0 spec? - if (m_activeSpec >= count) - ActivateSpec(0); - - // copy spec data from new specs - if (count > curCount) - { - // copy action buttons from active spec (more easy in this case iterate first by button) - ActionButtonList const& currentActionButtonList = m_actionButtons[m_activeSpec]; - - for (ActionButtonList::const_iterator itr = currentActionButtonList.begin(); itr != currentActionButtonList.end(); ++itr) - { - if (itr->second.uState != ACTIONBUTTON_DELETED) - { - for (uint8 spec = curCount; spec < count; ++spec) - addActionButton(spec, itr->first, itr->second.GetAction(), itr->second.GetType()); - } - } - } - // delete spec data for removed specs - else if (count < curCount) - { - // delete action buttons for removed spec - for (uint8 spec = count; spec < curCount; ++spec) - { - // delete action buttons for removed spec - for (uint8 button = 0; button < MAX_ACTION_BUTTONS; ++button) - removeActionButton(spec, button); - } - } - - SetSpecsCount(count); - - SendTalentsInfoData(false); -} - -void Player::RemoveAtLoginFlag(AtLoginFlags f, bool in_db_also /*= false*/) -{ - m_atLoginFlags &= ~f; - - if (in_db_also) - CharacterDatabase.PExecute("UPDATE characters set at_login = at_login & ~ %u WHERE guid ='%u'", uint32(f), GetGUIDLow()); -} - -void Player::SendClearCooldown(uint32 spell_id, Unit* target) -{ - ObjectGuid guid = target->GetObjectGuid(); - - WorldPacket data(SMSG_CLEAR_COOLDOWNS, 1 + 8 + 4); - data.WriteGuidMask<1, 3, 6>(guid); - data.WriteBits(1, 24); // cooldown count - data.WriteGuidMask<7, 5, 2, 4, 0>(guid); - - data.WriteGuidBytes<7, 2, 4, 5, 1, 3>(guid); - data << uint32(spell_id); - data.WriteGuidBytes<0, 6>(guid); - - SendDirectMessage(&data); -} - -bool Player::HasMovementFlag(MovementFlags f) const -{ - return m_movementInfo.HasMovementFlag(f); -} - -void Player::ResetTimeSync() -{ - m_timeSyncCounter = 0; - m_timeSyncTimer = 0; - m_timeSyncClient = 0; - m_timeSyncServer = WorldTimer::getMSTime(); -} - -void Player::SendTimeSync() -{ - WorldPacket data(SMSG_TIME_SYNC_REQ, 4); - data << uint32(m_timeSyncCounter++); - GetSession()->SendPacket(&data); - - // Schedule next sync in 10 sec - m_timeSyncTimer = 10000; - m_timeSyncServer = WorldTimer::getMSTime(); -} - -void Player::SendDuelCountdown(uint32 counter) -{ - WorldPacket data(SMSG_DUEL_COUNTDOWN, 4); - data << uint32(counter); // seconds - GetSession()->SendPacket(&data); -} - -bool Player::IsImmuneToSpellEffect(SpellEntry const* spellInfo, SpellEffectIndex index, bool castOnSelf) const -{ - SpellEffectEntry const* spellEffect = spellInfo->GetSpellEffect(index); - if(spellEffect) - { - switch(spellEffect->Effect) - { - case SPELL_EFFECT_ATTACK_ME: - return true; - default: - break; - } - switch(spellEffect->EffectApplyAuraName) - { - case SPELL_AURA_MOD_TAUNT: - return true; - default: - break; - } - } - - return Unit::IsImmuneToSpellEffect(spellInfo, index, castOnSelf); -} - -void Player::SetHomebindToLocation(WorldLocation const& loc, uint32 area_id) -{ - m_homebindMapId = loc.mapid; - m_homebindAreaId = area_id; - m_homebindX = loc.coord_x; - m_homebindY = loc.coord_y; - m_homebindZ = loc.coord_z; - - // update sql homebind - CharacterDatabase.PExecute("UPDATE character_homebind SET map = '%u', zone = '%u', position_x = '%f', position_y = '%f', position_z = '%f' WHERE guid = '%u'", - m_homebindMapId, m_homebindAreaId, m_homebindX, m_homebindY, m_homebindZ, GetGUIDLow()); -} - -Object* Player::GetObjectByTypeMask(ObjectGuid guid, TypeMask typemask) -{ - switch (guid.GetHigh()) - { - case HIGHGUID_ITEM: - if (typemask & TYPEMASK_ITEM) - return GetItemByGuid(guid); - break; - case HIGHGUID_PLAYER: - if (GetObjectGuid() == guid) - return this; - if ((typemask & TYPEMASK_PLAYER) && IsInWorld()) - return ObjectAccessor::FindPlayer(guid); - break; - case HIGHGUID_GAMEOBJECT: - if ((typemask & TYPEMASK_GAMEOBJECT) && IsInWorld()) - return GetMap()->GetGameObject(guid); - break; - case HIGHGUID_UNIT: - case HIGHGUID_VEHICLE: - if ((typemask & TYPEMASK_UNIT) && IsInWorld()) - return GetMap()->GetCreature(guid); - break; - case HIGHGUID_PET: - if ((typemask & TYPEMASK_UNIT) && IsInWorld()) - return GetMap()->GetPet(guid); - break; - case HIGHGUID_DYNAMICOBJECT: - if ((typemask & TYPEMASK_DYNAMICOBJECT) && IsInWorld()) - return GetMap()->GetDynamicObject(guid); - break; - case HIGHGUID_TRANSPORT: - case HIGHGUID_CORPSE: - case HIGHGUID_MO_TRANSPORT: - case HIGHGUID_INSTANCE: - case HIGHGUID_GROUP: - default: - break; - } - - return NULL; -} - -void Player::SetRestType(RestType n_r_type, uint32 areaTriggerId /*= 0*/) -{ - rest_type = n_r_type; - - if (rest_type == REST_TYPE_NO) - { - RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING); - - // Set player to FFA PVP when not in rested environment. - if (sWorld.IsFFAPvPRealm()) - SetFFAPvP(true); - } - else - { - SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING); - - inn_trigger_id = areaTriggerId; - time_inn_enter = time(NULL); - - if (sWorld.IsFFAPvPRealm()) - SetFFAPvP(false); - } -} - -uint32 Player::GetEquipGearScore(bool withBags, bool withBank) -{ - if (withBags && withBank && m_cachedGS > 0) - return m_cachedGS; - - GearScoreVec gearScore(EQUIPMENT_SLOT_END); - uint32 twoHandScore = 0; - - for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i) - { - if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - _fillGearScoreData(item, &gearScore, twoHandScore); - } - - if (withBags) - { - // check inventory - for (int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i) - { - if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - _fillGearScoreData(item, &gearScore, twoHandScore); - } - - // check bags - for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) - { - if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - { - for (uint32 j = 0; j < pBag->GetBagSize(); ++j) - { - if (Item* item2 = pBag->GetItemByPos(j)) - _fillGearScoreData(item2, &gearScore, twoHandScore); - } - } - } - } - - if (withBank) - { - for (uint8 i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; ++i) - { - if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - _fillGearScoreData(item, &gearScore, twoHandScore); - } - - for (uint8 i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i) - { - if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - { - if (item->IsBag()) - { - Bag* bag = (Bag*)item; - for (uint8 j = 0; j < bag->GetBagSize(); ++j) - { - if (Item* item2 = bag->GetItemByPos(j)) - _fillGearScoreData(item2, &gearScore, twoHandScore); - } - } - } - } - } - - uint8 count = EQUIPMENT_SLOT_END - 2; // ignore body and tabard slots - uint32 sum = 0; - - // check if 2h hand is higher level than main hand + off hand - if (gearScore[EQUIPMENT_SLOT_MAINHAND] + gearScore[EQUIPMENT_SLOT_OFFHAND] < twoHandScore * 2) - { - gearScore[EQUIPMENT_SLOT_OFFHAND] = 0; // off hand is ignored in calculations if 2h weapon has higher score - --count; - gearScore[EQUIPMENT_SLOT_MAINHAND] = twoHandScore; - } - - for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i) - { - sum += gearScore[i]; - } - - if (count) - { - uint32 res = uint32(sum / count); - DEBUG_LOG("Player: calculating gear score for %u. Result is %u", GetObjectGuid().GetCounter(), res); - - if (withBags && withBank) - m_cachedGS = res; - - return res; - } - else - return 0; -} - -void Player::_fillGearScoreData(Item* item, GearScoreVec* gearScore, uint32& twoHandScore) -{ - if (!item) - return; - - if (CanUseItem(item->GetProto()) != EQUIP_ERR_OK) - return; - - uint8 type = item->GetProto()->InventoryType; - uint32 level = item->GetProto()->ItemLevel; - - switch (type) - { - case INVTYPE_2HWEAPON: - twoHandScore = std::max(twoHandScore, level); - break; - case INVTYPE_WEAPON: - case INVTYPE_WEAPONMAINHAND: - (*gearScore)[EQUIPMENT_SLOT_MAINHAND] = std::max((*gearScore)[EQUIPMENT_SLOT_MAINHAND], level); - break; - case INVTYPE_SHIELD: - case INVTYPE_WEAPONOFFHAND: - (*gearScore)[EQUIPMENT_SLOT_OFFHAND] = std::max((*gearScore)[EQUIPMENT_SLOT_OFFHAND], level); - break; - case INVTYPE_THROWN: - case INVTYPE_RANGEDRIGHT: - case INVTYPE_RANGED: - case INVTYPE_QUIVER: - case INVTYPE_RELIC: - (*gearScore)[EQUIPMENT_SLOT_RANGED] = std::max((*gearScore)[EQUIPMENT_SLOT_RANGED], level); - break; - case INVTYPE_HEAD: - (*gearScore)[EQUIPMENT_SLOT_HEAD] = std::max((*gearScore)[EQUIPMENT_SLOT_HEAD], level); - break; - case INVTYPE_NECK: - (*gearScore)[EQUIPMENT_SLOT_NECK] = std::max((*gearScore)[EQUIPMENT_SLOT_NECK], level); - break; - case INVTYPE_SHOULDERS: - (*gearScore)[EQUIPMENT_SLOT_SHOULDERS] = std::max((*gearScore)[EQUIPMENT_SLOT_SHOULDERS], level); - break; - case INVTYPE_BODY: - (*gearScore)[EQUIPMENT_SLOT_BODY] = std::max((*gearScore)[EQUIPMENT_SLOT_BODY], level); - break; - case INVTYPE_CHEST: - (*gearScore)[EQUIPMENT_SLOT_CHEST] = std::max((*gearScore)[EQUIPMENT_SLOT_CHEST], level); - break; - case INVTYPE_WAIST: - (*gearScore)[EQUIPMENT_SLOT_WAIST] = std::max((*gearScore)[EQUIPMENT_SLOT_WAIST], level); - break; - case INVTYPE_LEGS: - (*gearScore)[EQUIPMENT_SLOT_LEGS] = std::max((*gearScore)[EQUIPMENT_SLOT_LEGS], level); - break; - case INVTYPE_FEET: - (*gearScore)[EQUIPMENT_SLOT_FEET] = std::max((*gearScore)[EQUIPMENT_SLOT_FEET], level); - break; - case INVTYPE_WRISTS: - (*gearScore)[EQUIPMENT_SLOT_WRISTS] = std::max((*gearScore)[EQUIPMENT_SLOT_WRISTS], level); - break; - case INVTYPE_HANDS: - (*gearScore)[EQUIPMENT_SLOT_HEAD] = std::max((*gearScore)[EQUIPMENT_SLOT_HEAD], level); - break; - // equipped gear score check uses both rings and trinkets for calculation, assume that for bags/banks it is the same - // with keeping second highest score at second slot - case INVTYPE_FINGER: - { - if ((*gearScore)[EQUIPMENT_SLOT_FINGER1] < level) - { - (*gearScore)[EQUIPMENT_SLOT_FINGER2] = (*gearScore)[EQUIPMENT_SLOT_FINGER1]; - (*gearScore)[EQUIPMENT_SLOT_FINGER1] = level; - } - else if ((*gearScore)[EQUIPMENT_SLOT_FINGER2] < level) - (*gearScore)[EQUIPMENT_SLOT_FINGER2] = level; - break; - } - case INVTYPE_TRINKET: - { - if ((*gearScore)[EQUIPMENT_SLOT_TRINKET1] < level) - { - (*gearScore)[EQUIPMENT_SLOT_TRINKET2] = (*gearScore)[EQUIPMENT_SLOT_TRINKET1]; - (*gearScore)[EQUIPMENT_SLOT_TRINKET1] = level; - } - else if ((*gearScore)[EQUIPMENT_SLOT_TRINKET2] < level) - (*gearScore)[EQUIPMENT_SLOT_TRINKET2] = level; - break; - } - case INVTYPE_CLOAK: - (*gearScore)[EQUIPMENT_SLOT_BACK] = std::max((*gearScore)[EQUIPMENT_SLOT_BACK], level); - break; - default: - break; - } -} - -void Player::SendCurrencies() const -{ - WorldPacket data(SMSG_SEND_CURRENCIES, m_currencies.size() * 4); - data.WriteBits(m_currencies.size(), 23); - - for (PlayerCurrenciesMap::const_iterator itr = m_currencies.begin(); itr != m_currencies.end(); ++itr) - { - uint32 weekCap = GetCurrencyWeekCap(itr->second.currencyEntry); - data.WriteBit(weekCap && itr->second.weekCount); - data.WriteBits(itr->second.flags, 4); - data.WriteBit(weekCap); - data.WriteBit(itr->second.currencyEntry->HasSeasonCount()); - } - - for (PlayerCurrenciesMap::const_iterator itr = m_currencies.begin(); itr != m_currencies.end(); ++itr) - { - data << uint32(floor(itr->second.totalCount / itr->second.currencyEntry->GetPrecision())); - - uint32 weekCap = GetCurrencyWeekCap(itr->second.currencyEntry); - if (weekCap) - data << uint32(floor(weekCap / itr->second.currencyEntry->GetPrecision())); - if (itr->second.currencyEntry->HasSeasonCount()) - data << uint32(floor(itr->second.seasonCount / itr->second.currencyEntry->GetPrecision())); - data << uint32(itr->first); - if (weekCap && itr->second.weekCount) - data << uint32(floor(itr->second.weekCount / itr->second.currencyEntry->GetPrecision())); - } - - GetSession()->SendPacket(&data); -} - -uint32 Player::GetCurrencyWeekCap(CurrencyTypesEntry const * currency) const -{ - uint32 cap = currency->WeekCap; - switch (currency->ID) - { - case CURRENCY_CONQUEST_POINTS: - cap = sWorld.getConfig(CONFIG_UINT32_CURRENCY_CONQUEST_POINTS_DEFAULT_WEEK_CAP); - break; - } - - return cap; -} - -void Player::SendCurrencyWeekCap(uint32 id) const -{ - SendCurrencyWeekCap(sCurrencyTypesStore.LookupEntry(id)); -} - -void Player::SendCurrencyWeekCap(CurrencyTypesEntry const * currency) const -{ - if (!currency || !IsInWorld() || GetSession()->PlayerLoading()) - return; - - uint32 cap = GetCurrencyWeekCap(currency); - if (!cap) - return; - - WorldPacket packet(SMSG_SET_CURRENCY_WEEK_LIMIT, 8); - packet << uint32(floor(cap / currency->GetPrecision())); - packet << uint32(currency->ID); - GetSession()->SendPacket(&packet); -} - -uint32 Player::GetCurrencyTotalCap(CurrencyTypesEntry const* currency) const -{ - uint32 cap = currency->TotalCap; - - return cap; -} - -uint32 Player::GetCurrencyCount(uint32 id) const -{ - PlayerCurrenciesMap::const_iterator itr = m_currencies.find(id); - return itr != m_currencies.end() ? itr->second.totalCount : 0; -} - -uint32 Player::GetCurrencySeasonCount(uint32 id) const -{ - PlayerCurrenciesMap::const_iterator itr = m_currencies.find(id); - return itr != m_currencies.end() ? itr->second.seasonCount : 0; -} - -uint32 Player::GetCurrencyWeekCount(uint32 id) const -{ - PlayerCurrenciesMap::const_iterator itr = m_currencies.find(id); - return itr != m_currencies.end() ? itr->second.weekCount : 0; -} - -void Player::ModifyCurrencyCount(uint32 id, int32 count, bool modifyWeek, bool modifySeason, bool ignoreMultipliers) -{ - if (!count) - return; - - if (!ignoreMultipliers && count > 0) - count *= GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_CURRENCY_GAIN, id); - - CurrencyTypesEntry const * currency = NULL; - - int32 oldTotalCount = 0; - int32 oldWeekCount = 0; - PlayerCurrenciesMap::iterator itr = m_currencies.find(id); - - bool initWeek = false; - if (itr == m_currencies.end()) - { - currency = sCurrencyTypesStore.LookupEntry(id); - MANGOS_ASSERT(currency); - - PlayerCurrency cur; - cur.state = PLAYERCURRENCY_NEW; - cur.totalCount = 0; - cur.weekCount = 0; - cur.seasonCount = 0; - cur.flags = 0; - cur.currencyEntry = currency; - m_currencies[id] = cur; - initWeek = true; - itr = m_currencies.find(id); - } - else - { - oldTotalCount = itr->second.totalCount; - oldWeekCount = itr->second.weekCount; - currency = itr->second.currencyEntry; - } - - int32 newTotalCount = oldTotalCount + count; - if (newTotalCount < 0) - newTotalCount = 0; - - int32 newWeekCount = oldWeekCount + (modifyWeek && count > 0 ? count : 0); - if (newWeekCount < 0) - newWeekCount = 0; - - int32 totalCap = GetCurrencyTotalCap(currency); - if (totalCap && totalCap < newTotalCount) - { - int32 delta = newTotalCount - totalCap; - newTotalCount = totalCap; - newWeekCount -= delta; - } - - int32 weekCap = GetCurrencyWeekCap(currency); - if (modifyWeek && weekCap && newWeekCount > weekCap) - { - int32 delta = newWeekCount - weekCap; - newWeekCount = weekCap; - newTotalCount -= delta; - } - initWeek &= weekCap != currency->WeekCap; - - if (newTotalCount != oldTotalCount) - { - if (itr->second.state != PLAYERCURRENCY_NEW) - itr->second.state = PLAYERCURRENCY_CHANGED; - - itr->second.totalCount = newTotalCount; - itr->second.weekCount = newWeekCount; - - int32 diff = newTotalCount - oldTotalCount; - if (diff > 0 && modifySeason) - itr->second.seasonCount += diff; - - // probably excessive checks - if (IsInWorld() && !GetSession()->PlayerLoading()) - { - if (diff > 0) - UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CURRENCY_EARNED, id, newTotalCount); - - WorldPacket packet(SMSG_SET_CURRENCY, 13); - bool bit0 = modifyWeek && weekCap && diff > 0; - bool bit1 = currency->HasSeasonCount(); - bool bit2 = currency->Category == CURRENCY_CATEGORY_META; // hides message in client when set - packet.WriteBit(bit0); - packet.WriteBit(bit1); - packet.WriteBit(bit2); - - if (bit1) - packet << uint32(floor(itr->second.seasonCount / currency->GetPrecision())); - packet << uint32(floor(newTotalCount / currency->GetPrecision())); - packet << uint32(id); - if (bit0) - packet << uint32(floor(newWeekCount / currency->GetPrecision())); - GetSession()->SendPacket(&packet); - - // init currency week limit for new currencies - if (initWeek) - SendCurrencyWeekCap(currency); - - if (diff > 0) - CurrencyAddedQuestCheck(id); - else - CurrencyRemovedQuestCheck(id); - } - - if (itr->first == CURRENCY_CONQUEST_ARENA_META || itr->first == CURRENCY_CONQUEST_BG_META) - ModifyCurrencyCount(CURRENCY_CONQUEST_POINTS, diff, modifyWeek, modifySeason, ignoreMultipliers); - } -} - -void Player::SetCurrencyCount(uint32 id, uint32 count) -{ - ModifyCurrencyCount(id, int32(count) - GetCurrencyCount(id), false, false, true); -} - -void Player::_LoadCurrencies(QueryResult* result) -{ - // 0 1 2 4 5 - // "SELECT id, totalCount, weekCount, seasonCount, flags FROM character_currencies WHERE guid = '%u'" - - if (result) - { - do - { - Field* fields = result->Fetch(); - - uint32 currency_id = fields[0].GetUInt16(); - uint32 totalCount = fields[1].GetUInt32(); - uint32 weekCount = fields[2].GetUInt32(); - uint32 seasonCount = fields[3].GetUInt32(); - uint8 flags = fields[4].GetUInt8(); - - CurrencyTypesEntry const * entry = sCurrencyTypesStore.LookupEntry(currency_id); - if (!entry) - { - sLog.outError("Player::_LoadCurrencies: %s has not existing currency id %u, removing.", GetGuidStr().c_str(), currency_id); - CharacterDatabase.PExecute("DELETE FROM character_currencies WHERE id = '%u'", currency_id); - continue; - } - - uint32 weekCap = GetCurrencyWeekCap(entry); - uint32 totalCap = GetCurrencyTotalCap(entry); - - PlayerCurrency cur; - - cur.state = PLAYERCURRENCY_UNCHANGED; - - if (totalCap && totalCount > totalCap) - cur.totalCount = totalCap; - else - cur.totalCount = totalCount; - - if (weekCap && weekCount > weekCap) - cur.weekCount = weekCap; - else - cur.weekCount = weekCount; - - cur.seasonCount = seasonCount; - - cur.flags = flags & PLAYERCURRENCY_MASK_USED_BY_CLIENT; - cur.currencyEntry = entry; - - m_currencies[currency_id] = cur; - } - while (result->NextRow()); - } -} - -void Player::_SaveCurrencies() -{ - for (PlayerCurrenciesMap::iterator itr = m_currencies.begin(); itr != m_currencies.end();) - { - if (itr->second.state == PLAYERCURRENCY_CHANGED) - CharacterDatabase.PExecute("UPDATE `character_currencies` SET `totalCount` = '%u', `weekCount` = '%u', `seasonCount` = '%u', `flags` = '%u' WHERE `guid` = '%u' AND `id` = '%u'", itr->second.totalCount, itr->second.weekCount, itr->second.seasonCount, itr->second.flags, GetGUIDLow(), itr->first); - else if (itr->second.state == PLAYERCURRENCY_NEW) - CharacterDatabase.PExecute("INSERT INTO `character_currencies` (`guid`, `id`, `totalCount`, `weekCount`, `seasonCount`, `flags`) VALUES ('%u', '%u', '%u', '%u', '%u', '%u')", GetGUIDLow(), itr->first, itr->second.totalCount, itr->second.weekCount, itr->second.seasonCount, itr->second.flags); - - if (itr->second.state == PLAYERCURRENCY_REMOVED) - m_currencies.erase(itr++); - else - { - itr->second.state = PLAYERCURRENCY_UNCHANGED; - ++itr; - } - } -} - -void Player::SetCurrencyFlags(uint32 currencyId, uint8 flags) -{ - PlayerCurrenciesMap::iterator itr = m_currencies.find(currencyId); - if (itr == m_currencies.end()) - return; - - itr->second.flags = flags; - itr->second.state = PLAYERCURRENCY_CHANGED; -} - -void Player::ResetCurrencyWeekCounts() -{ - for (PlayerCurrenciesMap::iterator itr = m_currencies.begin(); itr != m_currencies.end(); ++itr) - { - itr->second.weekCount = 0; - itr->second.state = PLAYERCURRENCY_CHANGED; - } - - WorldPacket data(SMSG_WEEKLY_RESET_CURRENCIES, 0); - SendDirectMessage(&data); -} - -void Player::SendPvPRewards() -{ - // Placeholder - - WorldPacket data(SMSG_PVP_REWARDS, 6 * 4); - data << uint32(1650); // rbg conquest cap - data << uint32(0); // total conquest earned - data << uint32(1350); // arena conquest cap - data << uint32(0); // rbg conquest earned - data << uint32(0); // arena conquest earned - data << uint32(1650); // total conquest cap - - SendDirectMessage(&data); -} - -void Player::SendRatedBGStats() -{ - // Placeholder - - WorldPacket data(SMSG_RATED_BG_STATS, 18 * 4); - for (int i = 0; i < 18; ++i) - data << uint32(0); - - SendDirectMessage(&data); -} - -AreaLockStatus Player::GetAreaTriggerLockStatus(AreaTrigger const* at, Difficulty difficulty, uint32& miscRequirement) -{ - miscRequirement = 0; - - if (!at) - return AREA_LOCKSTATUS_UNKNOWN_ERROR; - - MapEntry const* mapEntry = sMapStore.LookupEntry(at->target_mapId); - if (!mapEntry) - return AREA_LOCKSTATUS_UNKNOWN_ERROR; - - bool isRegularTargetMap = !mapEntry->IsDungeon() || GetDifficulty(mapEntry->IsRaid()) == REGULAR_DIFFICULTY; - - MapDifficultyEntry const* mapDiff = GetMapDifficultyData(at->target_mapId, difficulty); - if (mapEntry->IsDungeon() && !mapDiff) - return AREA_LOCKSTATUS_MISSING_DIFFICULTY; - - // Expansion requirement - if (GetSession()->Expansion() < mapEntry->Expansion()) - { - miscRequirement = mapEntry->Expansion(); - return AREA_LOCKSTATUS_INSUFFICIENT_EXPANSION; - } - - // Gamemaster can always enter - if (isGameMaster()) - return AREA_LOCKSTATUS_OK; - - // Level Requirements - if (getLevel() < at->requiredLevel && !sWorld.getConfig(CONFIG_BOOL_INSTANCE_IGNORE_LEVEL)) - { - miscRequirement = at->requiredLevel; - return AREA_LOCKSTATUS_TOO_LOW_LEVEL; - } - if (!isRegularTargetMap && !sWorld.getConfig(CONFIG_BOOL_INSTANCE_IGNORE_LEVEL) && getLevel() < uint32(maxLevelForExpansion[mapEntry->Expansion()])) - { - miscRequirement = maxLevelForExpansion[mapEntry->Expansion()]; - return AREA_LOCKSTATUS_TOO_LOW_LEVEL; - } - - // Raid Requirements - if (mapEntry->IsRaid() && !sWorld.getConfig(CONFIG_BOOL_INSTANCE_IGNORE_RAID)) - if (!GetGroup() || !GetGroup()->isRaidGroup()) - return AREA_LOCKSTATUS_RAID_LOCKED; - - // Item Requirements: must have requiredItem OR requiredItem2, report the first one that's missing - if (at->requiredItem) - { - if (!HasItemCount(at->requiredItem, 1) && - (!at->requiredItem2 || !HasItemCount(at->requiredItem2, 1))) - { - miscRequirement = at->requiredItem; - return AREA_LOCKSTATUS_MISSING_ITEM; - } - } - else if (at->requiredItem2 && !HasItemCount(at->requiredItem2, 1)) - { - miscRequirement = at->requiredItem2; - return AREA_LOCKSTATUS_MISSING_ITEM; - } - // Heroic item requirements - if (!isRegularTargetMap && at->heroicKey) - { - if (!HasItemCount(at->heroicKey, 1) && (!at->heroicKey2 || !HasItemCount(at->heroicKey2, 1))) - { - miscRequirement = at->heroicKey; - return AREA_LOCKSTATUS_MISSING_ITEM; - } - } - else if (!isRegularTargetMap && at->heroicKey2 && !HasItemCount(at->heroicKey2, 1)) - { - miscRequirement = at->heroicKey2; - return AREA_LOCKSTATUS_MISSING_ITEM; - } - - // Quest Requirements - if (isRegularTargetMap && at->requiredQuest && !GetQuestRewardStatus(at->requiredQuest)) - { - miscRequirement = at->requiredQuest; - return AREA_LOCKSTATUS_QUEST_NOT_COMPLETED; - } - if (!isRegularTargetMap && at->requiredQuestHeroic && !GetQuestRewardStatus(at->requiredQuestHeroic)) - { - miscRequirement = at->requiredQuestHeroic; - return AREA_LOCKSTATUS_QUEST_NOT_COMPLETED; - } - - // If the map is not created, assume it is possible to enter it. - DungeonPersistentState* state = GetBoundInstanceSaveForSelfOrGroup(at->target_mapId); - Map* map = sMapMgr.FindMap(at->target_mapId, state ? state->GetInstanceId() : 0); - - // ToDo add achievement check - - // Map's state check - if (map && map->IsDungeon()) - { - // cannot enter if the instance is full (player cap), GMs don't count - if (((DungeonMap*)map)->GetPlayersCountExceptGMs() >= ((DungeonMap*)map)->GetMaxPlayers()) - return AREA_LOCKSTATUS_INSTANCE_IS_FULL; - - // In Combat check - if (map && map->GetInstanceData() && map->GetInstanceData()->IsEncounterInProgress()) - return AREA_LOCKSTATUS_ZONE_IN_COMBAT; - - // Bind Checks - InstancePlayerBind* pBind = GetBoundInstance(at->target_mapId, GetDifficulty(mapEntry->IsRaid())); - if (pBind && pBind->perm && pBind->state != state) - return AREA_LOCKSTATUS_HAS_BIND; - if (pBind && pBind->perm && pBind->state != map->GetPersistentState()) - return AREA_LOCKSTATUS_HAS_BIND; - } - - return AREA_LOCKSTATUS_OK; -} - -const uint32 armorSpecToClass[MAX_CLASSES] = -{ - 0, - 86526, // CLASS_WARRIOR - 86525, // CLASS_PALADIN - 86528, // CLASS_HUNTER - 86531, // CLASS_ROGUE - 0, // CLASS_PRIEST - 86524, // CLASS_DEATH_KNIGHT - 86529, // CLASS_SHAMAN - 0, // CLASS_MAGE - 0, // CLASS_WARLOCK - 0, // CLASS_UNK2 - 86530, // CLASS_DRUID -}; - -#define MAX_ARMOR_SPECIALIZATION_SPELLS 18 -struct armorSpecToTabInfo -{ - uint32 spellId; - uint8 Class; - uint16 tab; -}; - -const armorSpecToTabInfo armorSpecToTab[MAX_ARMOR_SPECIALIZATION_SPELLS] = -{ - { 86537, CLASS_DEATH_KNIGHT, 398 }, // blood - { 86113, CLASS_DEATH_KNIGHT, 399 }, // frost - { 86536, CLASS_DEATH_KNIGHT, 400 }, // unholy - { 86093, CLASS_DRUID, 752 }, // balance - { 86096, CLASS_DRUID, 750 }, // feral - { 86097, CLASS_DRUID, 750 }, // feral - { 86104, CLASS_DRUID, 748 }, // resto - { 86538, CLASS_HUNTER, 0 }, // - { 86103, CLASS_PALADIN, 831 }, // holy - { 86102, CLASS_PALADIN, 839 }, // prot - { 86539, CLASS_PALADIN, 855 }, // retro - { 86092, CLASS_ROGUE, 0 }, // - { 86100, CLASS_SHAMAN, 261 }, // elem - { 86099, CLASS_SHAMAN, 263 }, // ench - { 86108, CLASS_SHAMAN, 262 }, // restor - { 86101, CLASS_WARRIOR, 746 }, // arms - { 86110, CLASS_WARRIOR, 815 }, // fury - { 86535, CLASS_WARRIOR, 845 }, // prot -}; - -void Player::UpdateArmorSpecializations() -{ - uint32 specPassive = armorSpecToClass[getClass()]; - // return class has no armor specialization - if (!specPassive) - return; - - for (int i = 0; i < MAX_ARMOR_SPECIALIZATION_SPELLS; ++i) - { - if (armorSpecToTab[i].Class != getClass()) - continue; - - SpellEntry const * spellProto = sSpellStore.LookupEntry(armorSpecToTab[i].spellId); - if (!spellProto || !spellProto->HasAttribute(SPELL_ATTR_EX8_ARMOR_SPECIALIZATION)) - { - sLog.outError("Player::UpdateArmorSpecializations: unexistent or wrong spell %u for class %u", - armorSpecToTab[i].spellId, armorSpecToTab[i].Class); - continue; - } - - // remove if base passive is unlearned - if (!HasSpell(specPassive)) - { - RemoveAurasDueToSpell(spellProto->Id); - continue; - } - - SpellAuraHolder* holder = GetSpellAuraHolder(spellProto->Id); - if (!holder) - { - // cast absent spells that may be missing due to shapeshift form dependency - CastSpell(this, spellProto->Id, true); - continue; - } - - Aura* aura = holder->GetAuraByEffectIndex(EFFECT_INDEX_0); - if (!aura) - continue; - - // recalculate modifier depending on current tree - aura->ApplyModifier(false, false); - aura->GetModifier()->m_amount = CalculateSpellDamage(this, spellProto, EFFECT_INDEX_0); - aura->ApplyModifier(true, false); - } -} - -bool Player::FitArmorSpecializationRules(SpellEntry const * spellProto) const -{ - if (!spellProto || !spellProto->HasAttribute(SPELL_ATTR_EX8_ARMOR_SPECIALIZATION)) - return true; - - int i = 0; - for (; i < MAX_ARMOR_SPECIALIZATION_SPELLS; ++i) - { - if (spellProto->Id == armorSpecToTab[i].spellId) - { - if (!armorSpecToTab[i].tab && m_talentsPrimaryTree[m_activeSpec] == 0 || - armorSpecToTab[i].tab && armorSpecToTab[i].tab != m_talentsPrimaryTree[m_activeSpec]) - return false; - - break; - } - } - - if (i == MAX_ARMOR_SPECIALIZATION_SPELLS) - return false; - - if (!HasSpell(armorSpecToClass[getClass()])) - return false; - - if (SpellEquippedItemsEntry const * itemsEntry = spellProto->GetSpellEquippedItems()) - { - // there spells check items with inventory types which are in EquippedItemInventoryTypeMask - uint32 inventoryTypeMask = itemsEntry->EquippedItemInventoryTypeMask; - // get slots that should be check for item presence and SpellEquippedItemsEntry match - uint32 slotMask = 0; - uint8 slots[4]; - for (int i = 0; i < MAX_INVTYPE; ++i) - { - if (inventoryTypeMask & (1 << i)) - { - if (!GetSlotsForInventoryType(i, slots)) - continue; - for (int j = 0; j < 4; ++j) - if (slots[j] != NULL_SLOT) - slotMask |= 1 << slots[j]; - } - } - - for (int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i) - { - if (slotMask & (1 << i)) - { - Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i); - // item must be present for specialization to work - if (!item) - return false; - - if (item->GetProto()->Class != itemsEntry->EquippedItemClass) - return false; - - if (((1 << item->GetProto()->SubClass) & itemsEntry->EquippedItemSubClassMask) == 0) - return false; - } - } - } - - return true; -} - -void Player::SendPetitionSignResult(ObjectGuid petitionGuid, Player* player, uint32 result) -{ - WorldPacket data(SMSG_PETITION_SIGN_RESULTS, 8 + 8 + 4); - data << petitionGuid; - data << player->GetObjectGuid(); - data << uint32(result); - GetSession()->SendPacket(&data); -} - -void Player::SendPetitionTurnInResult(uint32 result) -{ - WorldPacket data(SMSG_TURN_IN_PETITION_RESULTS, 4); - data << uint32(result); - GetSession()->SendPacket(&data); -} diff --git a/src/game/Player.h b/src/game/Player.h deleted file mode 100644 index 2e59b2100..000000000 --- a/src/game/Player.h +++ /dev/null @@ -1,2724 +0,0 @@ -/** - * This code is part of MaNGOS. Contributor & Copyright details are in AUTHORS/THANKS. - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef _PLAYER_H -#define _PLAYER_H - -#include "Common.h" -#include "ItemPrototype.h" -#include "Unit.h" -#include "Item.h" - -#include "Database/DatabaseEnv.h" -#include "NPCHandler.h" -#include "QuestDef.h" -#include "Group.h" -#include "Bag.h" -#include "WorldSession.h" -#include "Pet.h" -#include "MapReference.h" -#include "Util.h" // for Tokens typedef -#include "AchievementMgr.h" -#include "ReputationMgr.h" -#include "BattleGround/BattleGround.h" -#include "SharedDefines.h" - -#include -#include - -struct Mail; -class Channel; -class DynamicObject; -class Creature; -class PlayerMenu; -class Transport; -class UpdateMask; -class SpellCastTargets; -class PlayerSocial; -class DungeonPersistentState; -class Spell; -class Item; -class PhaseMgr; - -struct AreaTrigger; - -typedef std::deque PlayerMails; - -#define PLAYER_MAX_SKILLS 128 -#define PLAYER_MAX_DAILY_QUESTS 25 -#define PLAYER_EXPLORED_ZONES_SIZE 156 - -// 2^n internal values, they are never sent to the client -enum PlayerUnderwaterState -{ - UNDERWATER_NONE = 0x00, - UNDERWATER_INWATER = 0x01, // terrain type is water and player is afflicted by it - UNDERWATER_INLAVA = 0x02, // terrain type is lava and player is afflicted by it - UNDERWATER_INSLIME = 0x04, // terrain type is lava and player is afflicted by it - UNDERWATER_INDARKWATER = 0x08, // terrain type is dark water and player is afflicted by it - - UNDERWATER_EXIST_TIMERS = 0x10 -}; - -enum BuyBankSlotResult -{ - ERR_BANKSLOT_FAILED_TOO_MANY = 0, - ERR_BANKSLOT_INSUFFICIENT_FUNDS = 1, - ERR_BANKSLOT_NOTBANKER = 2, - ERR_BANKSLOT_OK = 3 -}; - -enum PlayerCurrencyFlag -{ - PLAYERCURRENCY_FLAG_NONE = 0x0, - PLAYERCURRENCY_FLAG_UNK1 = 0x1, // unused? - PLAYERCURRENCY_FLAG_UNK2 = 0x2, // unused? - PLAYERCURRENCY_FLAG_SHOW_IN_BACKPACK = 0x4, - PLAYERCURRENCY_FLAG_UNUSED = 0x8, - - PLAYERCURRENCY_MASK_USED_BY_CLIENT = - PLAYERCURRENCY_FLAG_SHOW_IN_BACKPACK | - PLAYERCURRENCY_FLAG_UNUSED, -}; - -enum PlayerCurrencyState -{ - PLAYERCURRENCY_UNCHANGED = 0, - PLAYERCURRENCY_CHANGED = 1, - PLAYERCURRENCY_NEW = 2, - PLAYERCURRENCY_REMOVED = 3 -}; - -struct PlayerCurrency -{ - PlayerCurrencyState state; - uint32 totalCount; - uint32 weekCount; - uint32 seasonCount; - uint8 flags; - CurrencyTypesEntry const * currencyEntry; -}; - -enum PlayerSpellState -{ - PLAYERSPELL_UNCHANGED = 0, - PLAYERSPELL_CHANGED = 1, - PLAYERSPELL_NEW = 2, - PLAYERSPELL_REMOVED = 3 -}; - -struct PlayerSpell -{ - PlayerSpellState state : 8; - bool active : 1; // show in spellbook - bool dependent : 1; // learned as result another spell learn, skill grow, quest reward, etc - bool disabled : 1; // first rank has been learned in result talent learn but currently talent unlearned, save max learned ranks -}; - -struct PlayerTalent -{ - TalentEntry const* talentEntry; - uint32 currentRank; - PlayerSpellState state; -}; - -typedef UNORDERED_MAP PlayerCurrenciesMap; -typedef UNORDERED_MAP PlayerSpellMap; -typedef UNORDERED_MAP PlayerTalentMap; - -struct SpellCooldown -{ - time_t end; - uint16 itemid; -}; - -typedef std::map SpellCooldowns; - -enum TrainerSpellState -{ - TRAINER_SPELL_GRAY = 0, - TRAINER_SPELL_GREEN = 1, - TRAINER_SPELL_RED = 2, - TRAINER_SPELL_GREEN_DISABLED = 10 // custom value, not send to client: formally green but learn not allowed -}; - -enum ActionButtonUpdateState -{ - ACTIONBUTTON_UNCHANGED = 0, - ACTIONBUTTON_CHANGED = 1, - ACTIONBUTTON_NEW = 2, - ACTIONBUTTON_DELETED = 3 -}; - -enum ActionButtonType -{ - ACTION_BUTTON_SPELL = 0x00, - ACTION_BUTTON_C = 0x01, // click? - ACTION_BUTTON_EQSET = 0x20, - ACTION_BUTTON_EXPANDABLE = 0x30, - ACTION_BUTTON_MACRO = 0x40, - ACTION_BUTTON_CMACRO = ACTION_BUTTON_C | ACTION_BUTTON_MACRO, - ACTION_BUTTON_ITEM = 0x80 -}; - -#define ACTION_BUTTON_ACTION(X) (uint32(X) & 0x00FFFFFF) -#define ACTION_BUTTON_TYPE(X) ((uint32(X) & 0xFF000000) >> 24) -#define MAX_ACTION_BUTTON_ACTION_VALUE (0x00FFFFFF+1) - -struct ActionButton -{ - ActionButton() : packedData(0), uState(ACTIONBUTTON_NEW) {} - - uint32 packedData; - ActionButtonUpdateState uState; - - // helpers - ActionButtonType GetType() const { return ActionButtonType(ACTION_BUTTON_TYPE(packedData)); } - uint32 GetAction() const { return ACTION_BUTTON_ACTION(packedData); } - void SetActionAndType(uint32 action, ActionButtonType type) - { - uint32 newData = action | (uint32(type) << 24); - if (newData != packedData || uState == ACTIONBUTTON_DELETED) - { - packedData = newData; - if (uState != ACTIONBUTTON_NEW) - uState = ACTIONBUTTON_CHANGED; - } - } -}; - -// some action button indexes used in code or clarify structure -enum ActionButtonIndex -{ - ACTION_BUTTON_SHAMAN_TOTEMS_BAR = 132, -}; - -#define MAX_ACTION_BUTTONS 144 // checked in 3.2.0 - -typedef std::map ActionButtonList; - -enum GlyphUpdateState -{ - GLYPH_UNCHANGED = 0, - GLYPH_CHANGED = 1, - GLYPH_NEW = 2, - GLYPH_DELETED = 3 -}; - -struct Glyph -{ - uint32 id; - GlyphUpdateState uState; - - Glyph() : id(0), uState(GLYPH_UNCHANGED) { } - - uint32 GetId() { return id; } - - void SetId(uint32 newId) - { - if (newId == id) - return; - - if (id == 0 && uState == GLYPH_UNCHANGED) // not exist yet in db and already saved - { - uState = GLYPH_NEW; - } - else if (newId == 0) - { - if (uState == GLYPH_NEW) // delete before add new -> no change - uState = GLYPH_UNCHANGED; - else // delete existing data - uState = GLYPH_DELETED; - } - else if (uState != GLYPH_NEW) // if not new data, change current data - { - uState = GLYPH_CHANGED; - } - - id = newId; - } -}; - -struct PlayerCreateInfoItem -{ - PlayerCreateInfoItem(uint32 id, uint32 amount) : item_id(id), item_amount(amount) {} - - uint32 item_id; - uint32 item_amount; -}; - -typedef std::list PlayerCreateInfoItems; - -struct PlayerLevelInfo -{ - PlayerLevelInfo() { for (int i = 0; i < MAX_STATS; ++i) stats[i] = 0; } - - uint8 stats[MAX_STATS]; -}; - -typedef std::list PlayerCreateInfoSpells; - -struct PlayerCreateInfoAction -{ - PlayerCreateInfoAction() : button(0), type(0), action(0) {} - PlayerCreateInfoAction(uint8 _button, uint32 _action, uint8 _type) : button(_button), type(_type), action(_action) {} - - uint8 button; - uint8 type; - uint32 action; -}; - -typedef std::list PlayerCreateInfoActions; - -struct PlayerInfo -{ - // existence checked by displayId != 0 // existence checked by displayId != 0 - PlayerInfo() : displayId_m(0), displayId_f(0), levelInfo(NULL) - { - } - - uint32 mapId; - uint32 areaId; - float positionX; - float positionY; - float positionZ; - float orientation; - uint16 phaseMap; - uint16 displayId_m; - uint16 displayId_f; - PlayerCreateInfoItems item; - PlayerCreateInfoSpells spell; - PlayerCreateInfoActions action; - - PlayerLevelInfo* levelInfo; //[level-1] 0..MaxPlayerLevel-1 -}; - -struct PvPInfo -{ - PvPInfo() : inHostileArea(false), endTimer(0) {} - - bool inHostileArea; - time_t endTimer; -}; - -struct DuelInfo -{ - DuelInfo() : initiator(NULL), opponent(NULL), startTimer(0), startTime(0), outOfBound(0) {} - - Player* initiator; - Player* opponent; - time_t startTimer; - time_t startTime; - time_t outOfBound; -}; - -struct Areas -{ - uint32 areaID; - uint32 areaFlag; - float x1; - float x2; - float y1; - float y2; -}; - -#define MAX_RUNES 6 -enum RuneCooldowns -{ - RUNE_BASE_COOLDOWN = 10000, - RUNE_MISS_COOLDOWN = 1500 // cooldown applied on runes when the spell misses -}; - -enum RuneType -{ - RUNE_BLOOD = 0, - RUNE_UNHOLY = 1, - RUNE_FROST = 2, - RUNE_DEATH = 3, - NUM_RUNE_TYPES = 4 -}; - -static RuneType runeSlotTypes[MAX_RUNES] = -{ - RUNE_BLOOD, - RUNE_BLOOD, - RUNE_UNHOLY, - RUNE_UNHOLY, - RUNE_FROST, - RUNE_FROST -}; - -struct RuneInfo -{ - uint8 BaseRune; - uint8 CurrentRune; - uint16 BaseCooldown; - uint16 Cooldown; // msec - Aura const* ConvertAura; -}; - -struct Runes -{ - RuneInfo runes[MAX_RUNES]; - uint8 runeState; // mask of available runes - uint32 lastUsedRuneMask; - - void SetRuneState(uint8 index, bool set = true) - { - if (set) - runeState |= (1 << index); // usable - else - runeState &= ~(1 << index); // on cooldown - } -}; - -struct EnchantDuration -{ - EnchantDuration() : item(NULL), slot(MAX_ENCHANTMENT_SLOT), leftduration(0) {}; - EnchantDuration(Item* _item, EnchantmentSlot _slot, uint32 _leftduration) : item(_item), slot(_slot), leftduration(_leftduration) { MANGOS_ASSERT(item); }; - - Item* item; - EnchantmentSlot slot; - uint32 leftduration; -}; - -typedef std::list EnchantDurationList; -typedef std::list ItemDurationList; - -enum LfgRoles -{ - LEADER = 0x01, - TANK = 0x02, - HEALER = 0x04, - DAMAGE = 0x08 -}; - -enum RaidGroupError -{ - ERR_RAID_GROUP_NONE = 0, - ERR_RAID_GROUP_LOWLEVEL = 1, - ERR_RAID_GROUP_ONLY = 2, - ERR_RAID_GROUP_FULL = 3, - ERR_RAID_GROUP_REQUIREMENTS_UNMATCH = 4 -}; - -enum DrunkenState -{ - DRUNKEN_SOBER = 0, - DRUNKEN_TIPSY = 1, - DRUNKEN_DRUNK = 2, - DRUNKEN_SMASHED = 3 -}; - -#define MAX_DRUNKEN 4 - -enum PlayerFlags -{ - PLAYER_FLAGS_NONE = 0x00000000, - PLAYER_FLAGS_GROUP_LEADER = 0x00000001, - PLAYER_FLAGS_AFK = 0x00000002, - PLAYER_FLAGS_DND = 0x00000004, - PLAYER_FLAGS_GM = 0x00000008, - PLAYER_FLAGS_GHOST = 0x00000010, - PLAYER_FLAGS_RESTING = 0x00000020, - PLAYER_FLAGS_UNK7 = 0x00000040, // admin? - PLAYER_FLAGS_UNK8 = 0x00000080, // pre-3.0.3 PLAYER_FLAGS_FFA_PVP flag for FFA PVP state - PLAYER_FLAGS_CONTESTED_PVP = 0x00000100, // Player has been involved in a PvP combat and will be attacked by contested guards - PLAYER_FLAGS_IN_PVP = 0x00000200, - PLAYER_FLAGS_HIDE_HELM = 0x00000400, - PLAYER_FLAGS_HIDE_CLOAK = 0x00000800, - PLAYER_FLAGS_PARTIAL_PLAY_TIME = 0x00001000, // played long time - PLAYER_FLAGS_NO_PLAY_TIME = 0x00002000, // played too long time - PLAYER_FLAGS_IS_OUT_OF_BOUNDS = 0x00004000, // Lua_IsOutOfBounds - PLAYER_FLAGS_DEVELOPER = 0x00008000, // chat tag, name prefix - PLAYER_FLAGS_ENABLE_LOW_LEVEL_RAID = 0x00010000, // triggers lua event EVENT_ENABLE_LOW_LEVEL_RAID - PLAYER_FLAGS_TAXI_BENCHMARK = 0x00020000, // taxi benchmark mode (on/off) (2.0.1) - PLAYER_FLAGS_PVP_TIMER = 0x00040000, // 3.0.2, pvp timer active (after you disable pvp manually) - PLAYER_FLAGS_COMMENTATOR = 0x00080000, - PLAYER_FLAGS_UNK21 = 0x00100000, - PLAYER_FLAGS_UNK22 = 0x00200000, - PLAYER_FLAGS_COMMENTATOR_UBER = 0x00400000, // something like COMMENTATOR_CAN_USE_INSTANCE_COMMAND - PLAYER_FLAGS_UNK24 = 0x00800000, // EVENT_SPELL_UPDATE_USABLE and EVENT_UPDATE_SHAPESHIFT_USABLE, disabled all abilitys on tab except autoattack - PLAYER_FLAGS_UNK25 = 0x01000000, // EVENT_SPELL_UPDATE_USABLE and EVENT_UPDATE_SHAPESHIFT_USABLE, disabled all melee ability on tab include autoattack - PLAYER_FLAGS_XP_USER_DISABLED = 0x02000000, - PLAYER_FLAGS_UNK27 = 0x04000000, - PLAYER_FLAGS_AUTO_DECLINE_GUILDS = 0x08000000, // Automatically declines guild invites - PLAYER_FLAGS_GUILD_LEVELING_ENABLED = 0x10000000, // Lua_GetGuildLevelEnabled() - enables guild leveling related UI - PLAYER_FLAGS_VOID_STORAGE_UNLOCKED = 0x20000000, // unlocks void storage - PLAYER_FLAGS_UNK30 = 0x40000000, - PLAYER_FLAGS_UNK31 = 0x80000000, -}; - -// used for PLAYER__FIELD_KNOWN_TITLES field (uint64), (1< QuestStatusMap; - -enum QuestSlotOffsets -{ - QUEST_ID_OFFSET = 0, - QUEST_STATE_OFFSET = 1, - QUEST_COUNTS_OFFSET = 2, // 2 and 3 - QUEST_TIME_OFFSET = 4 -}; - -#define MAX_QUEST_OFFSET 5 - -enum QuestSlotStateMask -{ - QUEST_STATE_NONE = 0x0000, - QUEST_STATE_COMPLETE = 0x0001, - QUEST_STATE_FAIL = 0x0002 -}; - -enum SkillUpdateState -{ - SKILL_UNCHANGED = 0, - SKILL_CHANGED = 1, - SKILL_NEW = 2, - SKILL_DELETED = 3 -}; - -struct SkillStatusData -{ - SkillStatusData(uint8 _pos, SkillUpdateState _uState) : pos(_pos), uState(_uState) - { - } - uint8 pos; - SkillUpdateState uState; -}; - -typedef UNORDERED_MAP SkillStatusMap; - -enum PlayerSlots -{ - // first slot for item stored (in any way in player m_items data) - PLAYER_SLOT_START = 0, - // last+1 slot for item stored (in any way in player m_items data) - PLAYER_SLOT_END = 86, - PLAYER_SLOTS_COUNT = (PLAYER_SLOT_END - PLAYER_SLOT_START) -}; - -#define INVENTORY_SLOT_BAG_0 255 - -enum EquipmentSlots // 19 slots -{ - EQUIPMENT_SLOT_START = 0, - EQUIPMENT_SLOT_HEAD = 0, - EQUIPMENT_SLOT_NECK = 1, - EQUIPMENT_SLOT_SHOULDERS = 2, - EQUIPMENT_SLOT_BODY = 3, - EQUIPMENT_SLOT_CHEST = 4, - EQUIPMENT_SLOT_WAIST = 5, - EQUIPMENT_SLOT_LEGS = 6, - EQUIPMENT_SLOT_FEET = 7, - EQUIPMENT_SLOT_WRISTS = 8, - EQUIPMENT_SLOT_HANDS = 9, - EQUIPMENT_SLOT_FINGER1 = 10, - EQUIPMENT_SLOT_FINGER2 = 11, - EQUIPMENT_SLOT_TRINKET1 = 12, - EQUIPMENT_SLOT_TRINKET2 = 13, - EQUIPMENT_SLOT_BACK = 14, - EQUIPMENT_SLOT_MAINHAND = 15, - EQUIPMENT_SLOT_OFFHAND = 16, - EQUIPMENT_SLOT_RANGED = 17, - EQUIPMENT_SLOT_TABARD = 18, - EQUIPMENT_SLOT_END = 19 -}; - -enum InventorySlots // 4 slots -{ - INVENTORY_SLOT_BAG_START = 19, - INVENTORY_SLOT_BAG_END = 23 -}; - -enum InventoryPackSlots // 16 slots -{ - INVENTORY_SLOT_ITEM_START = 23, - INVENTORY_SLOT_ITEM_END = 39 -}; - -enum BankItemSlots // 28 slots -{ - BANK_SLOT_ITEM_START = 39, - BANK_SLOT_ITEM_END = 67 -}; - -enum BankBagSlots // 7 slots -{ - BANK_SLOT_BAG_START = 67, - BANK_SLOT_BAG_END = 74 -}; - -enum BuyBackSlots // 12 slots -{ - // stored in m_buybackitems - BUYBACK_SLOT_START = 74, - BUYBACK_SLOT_END = 86 -}; - -enum EquipmentSetUpdateState -{ - EQUIPMENT_SET_UNCHANGED = 0, - EQUIPMENT_SET_CHANGED = 1, - EQUIPMENT_SET_NEW = 2, - EQUIPMENT_SET_DELETED = 3 -}; - -struct EquipmentSet -{ - EquipmentSet() : Guid(0), IgnoreMask(0), state(EQUIPMENT_SET_NEW) - { - for (int i = 0; i < EQUIPMENT_SLOT_END; ++i) - Items[i] = 0; - } - - uint64 Guid; - std::string Name; - std::string IconName; - uint32 IgnoreMask; - uint32 Items[EQUIPMENT_SLOT_END]; - EquipmentSetUpdateState state; -}; - -#define MAX_EQUIPMENT_SET_INDEX 10 // client limit - -typedef std::map EquipmentSets; - -struct ItemPosCount -{ - ItemPosCount(uint16 _pos, uint32 _count) : pos(_pos), count(_count) {} - bool isContainedIn(std::vector const& vec) const; - uint16 pos; - uint32 count; -}; -typedef std::vector ItemPosCountVec; - -enum TradeSlots -{ - TRADE_SLOT_COUNT = 7, - TRADE_SLOT_TRADED_COUNT = 6, - TRADE_SLOT_NONTRADED = 6 -}; - -enum TransferAbortReason -{ - TRANSFER_ABORT_NONE = 0x00, - TRANSFER_ABORT_ERROR = 0x01, - TRANSFER_ABORT_MAX_PLAYERS = 0x02, // Transfer Aborted: instance is full - TRANSFER_ABORT_NOT_FOUND = 0x03, // Transfer Aborted: instance not found - TRANSFER_ABORT_TOO_MANY_INSTANCES = 0x04, // You have entered too many instances recently. - TRANSFER_ABORT_ZONE_IN_COMBAT = 0x06, // Unable to zone in while an encounter is in progress. - TRANSFER_ABORT_INSUF_EXPAN_LVL = 0x07, // You must have expansion installed to access this area. - TRANSFER_ABORT_DIFFICULTY = 0x08, // difficulty mode is not available for %s. - TRANSFER_ABORT_UNIQUE_MESSAGE = 0x09, // Until you've escaped TLK's grasp, you cannot leave this place! - TRANSFER_ABORT_TOO_MANY_REALM_INSTANCES = 0x0A, // Additional instances cannot be launched, please try again later. - TRANSFER_ABORT_NEED_GROUP = 0x0B, // 3.1 - TRANSFER_ABORT_NOT_FOUND2 = 0x0C, // 3.1 - TRANSFER_ABORT_NOT_FOUND3 = 0x0D, // 3.1 - TRANSFER_ABORT_NOT_FOUND4 = 0x0E, // 3.2 - TRANSFER_ABORT_REALM_ONLY = 0x0F, // All players on party must be from the same realm. - TRANSFER_ABORT_MAP_NOT_ALLOWED = 0x10, // Map can't be entered at this time. - // 0x11 not found - TRANSFER_ABORT_LOCKED_TO_DIFFERENT_INSTANCE = 0x12, // 4.0.1 - TRANSFER_ABORT_ALREADY_COMPLETED_ENCOUNTER = 0x13, // 4.0.1 -}; - -enum InstanceResetWarningType -{ - RAID_INSTANCE_WARNING_HOURS = 1, // WARNING! %s is scheduled to reset in %d hour(s). - RAID_INSTANCE_WARNING_MIN = 2, // WARNING! %s is scheduled to reset in %d minute(s)! - RAID_INSTANCE_WARNING_MIN_SOON = 3, // WARNING! %s is scheduled to reset in %d minute(s). Please exit the zone or you will be returned to your bind location! - RAID_INSTANCE_WELCOME = 4, // Welcome to %s. This raid instance is scheduled to reset in %s. - RAID_INSTANCE_EXPIRED = 5 -}; - -// PLAYER_FIELD_ARENA_TEAM_INFO_1_1 offsets -enum ArenaTeamInfoType -{ - ARENA_TEAM_ID = 0, - ARENA_TEAM_TYPE = 1, // new in 3.2 - team type? - ARENA_TEAM_MEMBER = 2, // 0 - captain, 1 - member - ARENA_TEAM_GAMES_WEEK = 3, - ARENA_TEAM_GAMES_SEASON = 4, - ARENA_TEAM_WINS_SEASON = 5, - ARENA_TEAM_PERSONAL_RATING = 6, - ARENA_TEAM_END = 7 -}; - -enum RestType -{ - REST_TYPE_NO = 0, - REST_TYPE_IN_TAVERN = 1, - REST_TYPE_IN_CITY = 2 -}; - -enum DuelCompleteType -{ - DUEL_INTERRUPTED = 0, - DUEL_WON = 1, - DUEL_FLED = 2 -}; - -enum TeleportToOptions -{ - TELE_TO_GM_MODE = 0x01, - TELE_TO_NOT_LEAVE_TRANSPORT = 0x02, - TELE_TO_NOT_LEAVE_COMBAT = 0x04, - TELE_TO_NOT_UNSUMMON_PET = 0x08, - TELE_TO_SPELL = 0x10, -}; - -/// Type of environmental damages -enum EnviromentalDamage -{ - DAMAGE_EXHAUSTED = 0, - DAMAGE_DROWNING = 1, - DAMAGE_FALL = 2, - DAMAGE_LAVA = 3, - DAMAGE_SLIME = 4, - DAMAGE_FIRE = 5, - DAMAGE_FALL_TO_VOID = 6 // custom case for fall without durability loss -}; - -enum PlayerChatTag -{ - CHAT_TAG_NONE = 0x00, - CHAT_TAG_AFK = 0x01, - CHAT_TAG_DND = 0x02, - CHAT_TAG_GM = 0x04, - CHAT_TAG_COM = 0x08, // Commentator - CHAT_TAG_DEV = 0x10, // Developer -}; - -enum PlayedTimeIndex -{ - PLAYED_TIME_TOTAL = 0, - PLAYED_TIME_LEVEL = 1 -}; - -#define MAX_PLAYED_TIME_INDEX 2 - -// used at player loading query list preparing, and later result selection -enum PlayerLoginQueryIndex -{ - PLAYER_LOGIN_QUERY_LOADFROM, - PLAYER_LOGIN_QUERY_LOADGROUP, - PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES, - PLAYER_LOGIN_QUERY_LOADAURAS, - PLAYER_LOGIN_QUERY_LOADSPELLS, - PLAYER_LOGIN_QUERY_LOADQUESTSTATUS, - PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS, - PLAYER_LOGIN_QUERY_LOADREPUTATION, - PLAYER_LOGIN_QUERY_LOADINVENTORY, - PLAYER_LOGIN_QUERY_LOADITEMLOOT, - PLAYER_LOGIN_QUERY_LOADACTIONS, - PLAYER_LOGIN_QUERY_LOADSOCIALLIST, - PLAYER_LOGIN_QUERY_LOADHOMEBIND, - PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS, - PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES, - PLAYER_LOGIN_QUERY_LOADGUILD, - PLAYER_LOGIN_QUERY_LOADARENAINFO, - PLAYER_LOGIN_QUERY_LOADACHIEVEMENTS, - PLAYER_LOGIN_QUERY_LOADCRITERIAPROGRESS, - PLAYER_LOGIN_QUERY_LOADEQUIPMENTSETS, - PLAYER_LOGIN_QUERY_LOADBGDATA, - PLAYER_LOGIN_QUERY_LOADACCOUNTDATA, - PLAYER_LOGIN_QUERY_LOADSKILLS, - PLAYER_LOGIN_QUERY_LOADGLYPHS, - PLAYER_LOGIN_QUERY_LOADMAILS, - PLAYER_LOGIN_QUERY_LOADMAILEDITEMS, - PLAYER_LOGIN_QUERY_LOADTALENTS, - PLAYER_LOGIN_QUERY_LOADWEEKLYQUESTSTATUS, - PLAYER_LOGIN_QUERY_LOADMONTHLYQUESTSTATUS, - PLAYER_LOGIN_QUERY_LOADCURRENCIES, - - MAX_PLAYER_LOGIN_QUERY -}; - -enum PlayerDelayedOperations -{ - DELAYED_SAVE_PLAYER = 0x01, - DELAYED_RESURRECT_PLAYER = 0x02, - DELAYED_SPELL_CAST_DESERTER = 0x04, - DELAYED_BG_MOUNT_RESTORE = 0x08, ///< Flag to restore mount state after teleport from BG - DELAYED_BG_TAXI_RESTORE = 0x10, ///< Flag to restore taxi state after teleport from BG - DELAYED_END -}; - -enum ReputationSource -{ - REPUTATION_SOURCE_KILL, - REPUTATION_SOURCE_QUEST, - REPUTATION_SOURCE_SPELL -}; - -// Player summoning auto-decline time (in secs) -#define MAX_PLAYER_SUMMON_DELAY (2*MINUTE) -#define MAX_MONEY_AMOUNT (UI64LIT(9999999999)) // from wowpedia - -struct InstancePlayerBind -{ - DungeonPersistentState* state; - bool perm; - /* permanent PlayerInstanceBinds are created in Raid/Heroic instances for players - that aren't already permanently bound when they are inside when a boss is killed - or when they enter an instance that the group leader is permanently bound to. */ - InstancePlayerBind() : state(NULL), perm(false) {} -}; - -enum PlayerRestState -{ - REST_STATE_RESTED = 0x01, - REST_STATE_NORMAL = 0x02, - REST_STATE_RAF_LINKED = 0x04 // Exact use unknown -}; - -class MANGOS_DLL_SPEC PlayerTaxi -{ - public: - PlayerTaxi(); - ~PlayerTaxi() {} - // Nodes - void InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint32 level); - void LoadTaxiMask(const char* data); - - bool IsTaximaskNodeKnown(uint32 nodeidx) const - { - uint8 field = uint8((nodeidx - 1) / 8); - uint8 submask = 1 << ((nodeidx - 1) % 8); - return (m_taximask[field] & submask) == submask; - } - bool SetTaximaskNode(uint32 nodeidx) - { - uint8 field = uint8((nodeidx - 1) / 8); - uint8 submask = 1 << ((nodeidx - 1) % 8); - if ((m_taximask[field] & submask) != submask) - { - m_taximask[field] |= submask; - return true; - } - else - return false; - } - void AppendTaximaskTo(ByteBuffer& data, bool all); - - // Destinations - bool LoadTaxiDestinationsFromString(const std::string& values, Team team); - std::string SaveTaxiDestinationsToString(); - - void ClearTaxiDestinations() { m_TaxiDestinations.clear(); } - void AddTaxiDestination(uint32 dest) { m_TaxiDestinations.push_back(dest); } - uint32 GetTaxiSource() const { return m_TaxiDestinations.empty() ? 0 : m_TaxiDestinations.front(); } - uint32 GetTaxiDestination() const { return m_TaxiDestinations.size() < 2 ? 0 : m_TaxiDestinations[1]; } - uint32 GetCurrentTaxiPath() const; - uint32 NextTaxiDestination() - { - m_TaxiDestinations.pop_front(); - return GetTaxiDestination(); - } - bool empty() const { return m_TaxiDestinations.empty(); } - - friend std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi); - private: - TaxiMask m_taximask; - std::deque m_TaxiDestinations; -}; - -std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi); - -/// Holder for BattleGround data -struct BGData -{ - BGData() : bgInstanceID(0), bgTypeID(BATTLEGROUND_TYPE_NONE), bgAfkReportedCount(0), bgAfkReportedTimer(0), - bgTeam(TEAM_NONE), mountSpell(0), m_needSave(false) { ClearTaxiPath(); } - - uint32 bgInstanceID; ///< This variable is set to bg->m_InstanceID, saved - /// when player is teleported to BG - (it is battleground's GUID) - BattleGroundTypeId bgTypeID; - - std::set bgAfkReporter; - uint8 bgAfkReportedCount; - time_t bgAfkReportedTimer; - - Team bgTeam; ///< What side the player will be added to, saved - - uint32 mountSpell; ///< Mount used before join to bg, saved - uint32 taxiPath[2]; ///< Current taxi active path start/end nodes, saved - - WorldLocation joinPos; ///< From where player entered BG, saved - - bool m_needSave; ///< true, if saved to DB fields modified after prev. save (marked as "saved" above) - - void ClearTaxiPath() { taxiPath[0] = taxiPath[1] = 0; } - bool HasTaxiPath() const { return taxiPath[0] && taxiPath[1]; } -}; - -class TradeData -{ - public: // constructors - TradeData(Player* player, Player* trader) : - m_player(player), m_trader(trader), m_accepted(false), m_acceptProccess(false), - m_money(0), m_spell(0) {} - - public: // access functions - - Player* GetTrader() const { return m_trader; } - TradeData* GetTraderData() const; - - Item* GetItem(TradeSlots slot) const; - bool HasItem(ObjectGuid item_guid) const; - - uint32 GetSpell() const { return m_spell; } - Item* GetSpellCastItem() const; - bool HasSpellCastItem() const { return !m_spellCastItem.IsEmpty(); } - - uint64 GetMoney() const { return m_money; } - - bool IsAccepted() const { return m_accepted; } - bool IsInAcceptProcess() const { return m_acceptProccess; } - public: // access functions - - void SetItem(TradeSlots slot, Item* item); - void SetSpell(uint32 spell_id, Item* castItem = NULL); - void SetMoney(uint64 money); - - void SetAccepted(bool state, bool crosssend = false); - - // must be called only from accept handler helper functions - void SetInAcceptProcess(bool state) { m_acceptProccess = state; } - - private: // internal functions - - void Update(bool for_trader = true); - - private: // fields - - Player* m_player; // Player who own of this TradeData - Player* m_trader; // Player who trade with m_player - - bool m_accepted; // m_player press accept for trade list - bool m_acceptProccess; // one from player/trader press accept and this processed - - uint64 m_money; // m_player place money to trade - - uint32 m_spell; // m_player apply spell to non-traded slot item - ObjectGuid m_spellCastItem; // applied spell casted by item use - - ObjectGuid m_items[TRADE_SLOT_COUNT]; // traded itmes from m_player side including non-traded slot -}; - -class MANGOS_DLL_SPEC Player : public Unit -{ - friend class WorldSession; - friend void Item::AddToUpdateQueueOf(Player* player); - friend void Item::RemoveFromUpdateQueueOf(Player* player); - public: - explicit Player(WorldSession* session); - ~Player(); - - void CleanupsBeforeDelete() override; - - static UpdateMask updateVisualBits; - static void InitVisibleBits(); - - void AddToWorld() override; - void RemoveFromWorld() override; - - void SendTeleportPacket(float oldX, float oldY, float oldZ, float oldO); - bool TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options = 0, AreaTrigger const* at = NULL); - - bool TeleportTo(WorldLocation const& loc, uint32 options = 0) - { - return TeleportTo(loc.mapid, loc.coord_x, loc.coord_y, loc.coord_z, loc.orientation, options); - } - - bool TeleportToBGEntryPoint(); - - void SetSummonPoint(uint32 mapid, float x, float y, float z) - { - m_summon_expire = time(NULL) + MAX_PLAYER_SUMMON_DELAY; - m_summon_mapid = mapid; - m_summon_x = x; - m_summon_y = y; - m_summon_z = z; - } - void SummonIfPossible(bool agree); - - bool Create(uint32 guidlow, const std::string& name, uint8 race, uint8 class_, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair, uint8 outfitId); - - void Update(uint32 update_diff, uint32 time) override; - - static bool BuildEnumData(QueryResult* result, ByteBuffer* data, ByteBuffer* buffer); - - void SetInWater(bool apply); - - bool IsInWater() const override { return m_isInWater; } - bool IsUnderWater() const override; - - void SendInitialPacketsBeforeAddToMap(); - void SendInitialPacketsAfterAddToMap(); - void SendInstanceResetWarning(uint32 mapid, Difficulty difficulty, uint32 time); - - Creature* GetNPCIfCanInteractWith(ObjectGuid guid, uint32 npcflagmask); - GameObject* GetGameObjectIfCanInteractWith(ObjectGuid guid, uint32 gameobject_type = MAX_GAMEOBJECT_TYPE) const; - - void ToggleAFK(); - void ToggleDND(); - bool isAFK() const { return HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK); } - bool isDND() const { return HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_DND); } - uint8 GetChatTag() const; - std::string autoReplyMsg; - - uint32 GetBarberShopCost(uint8 newhairstyle, uint8 newhaircolor, uint8 newfacialhair, uint32 newskintone); - - PlayerSocial* GetSocial() { return m_social; } - - PlayerTaxi m_taxi; - void InitTaxiNodesForLevel() { m_taxi.InitTaxiNodesForLevel(getRace(), getClass(), getLevel()); } - bool ActivateTaxiPathTo(std::vector const& nodes, Creature* npc = NULL, uint32 spellid = 0); - bool ActivateTaxiPathTo(uint32 taxi_path_id, uint32 spellid = 0); - // mount_id can be used in scripting calls - void ContinueTaxiFlight(); - bool isAcceptTickets() const { return GetSession()->GetSecurity() >= SEC_GAMEMASTER && (m_ExtraFlags & PLAYER_EXTRA_GM_ACCEPT_TICKETS); } - void SetAcceptTicket(bool on) { if (on) m_ExtraFlags |= PLAYER_EXTRA_GM_ACCEPT_TICKETS; else m_ExtraFlags &= ~PLAYER_EXTRA_GM_ACCEPT_TICKETS; } - bool isAcceptWhispers() const { return m_ExtraFlags & PLAYER_EXTRA_ACCEPT_WHISPERS; } - void SetAcceptWhispers(bool on) { if (on) m_ExtraFlags |= PLAYER_EXTRA_ACCEPT_WHISPERS; else m_ExtraFlags &= ~PLAYER_EXTRA_ACCEPT_WHISPERS; } - bool isGameMaster() const { return m_ExtraFlags & PLAYER_EXTRA_GM_ON; } - void SetGameMaster(bool on); - bool isGMChat() const { return GetSession()->GetSecurity() >= SEC_MODERATOR && (m_ExtraFlags & PLAYER_EXTRA_GM_CHAT); } - void SetGMChat(bool on) { if (on) m_ExtraFlags |= PLAYER_EXTRA_GM_CHAT; else m_ExtraFlags &= ~PLAYER_EXTRA_GM_CHAT; } - bool isTaxiCheater() const { return m_ExtraFlags & PLAYER_EXTRA_TAXICHEAT; } - void SetTaxiCheater(bool on) { if (on) m_ExtraFlags |= PLAYER_EXTRA_TAXICHEAT; else m_ExtraFlags &= ~PLAYER_EXTRA_TAXICHEAT; } - bool isGMVisible() const { return !(m_ExtraFlags & PLAYER_EXTRA_GM_INVISIBLE); } - void SetGMVisible(bool on); - void SetPvPDeath(bool on) { if (on) m_ExtraFlags |= PLAYER_EXTRA_PVP_DEATH; else m_ExtraFlags &= ~PLAYER_EXTRA_PVP_DEATH; } - - // 0 = own auction, -1 = enemy auction, 1 = goblin auction - int GetAuctionAccessMode() const { return m_ExtraFlags & PLAYER_EXTRA_AUCTION_ENEMY ? -1 : (m_ExtraFlags & PLAYER_EXTRA_AUCTION_NEUTRAL ? 1 : 0); } - void SetAuctionAccessMode(int state) - { - m_ExtraFlags &= ~(PLAYER_EXTRA_AUCTION_ENEMY | PLAYER_EXTRA_AUCTION_NEUTRAL); - - if (state < 0) - m_ExtraFlags |= PLAYER_EXTRA_AUCTION_ENEMY; - else if (state > 0) - m_ExtraFlags |= PLAYER_EXTRA_AUCTION_NEUTRAL; - } - - void GiveXP(uint32 xp, Unit* victim); - void GiveLevel(uint32 level); - - void InitStatsForLevel(bool reapplyMods = false); - - // Played Time Stuff - time_t m_logintime; - time_t m_Last_tick; - - uint32 m_Played_time[MAX_PLAYED_TIME_INDEX]; - uint32 GetTotalPlayedTime() { return m_Played_time[PLAYED_TIME_TOTAL]; } - uint32 GetLevelPlayedTime() { return m_Played_time[PLAYED_TIME_LEVEL]; } - - void ResetTimeSync(); - void SendTimeSync(); - - void SetDeathState(DeathState s) override; // overwrite Unit::SetDeathState - - float GetRestBonus() const { return m_rest_bonus; } - void SetRestBonus(float rest_bonus_new); - - RestType GetRestType() const { return rest_type; } - void SetRestType(RestType n_r_type, uint32 areaTriggerId = 0); - - time_t GetTimeInnEnter() const { return time_inn_enter; } - void UpdateInnerTime(time_t time) { time_inn_enter = time; } - - void RemovePet(PetSaveMode mode); - - PhaseMgr* GetPhaseMgr() { return phaseMgr; } - - void Say(const std::string& text, const uint32 language); - void Yell(const std::string& text, const uint32 language); - void TextEmote(const std::string& text); - void Whisper(const std::string& text, const uint32 language, ObjectGuid receiver); - void WhisperAddon(const std::string& text, const std::string& prefix, ObjectGuid receiver); - void BuildPlayerChat(WorldPacket* data, uint8 msgtype, const std::string& text, uint32 language, const char* addonPrefix = NULL) const; - - /*********************************************************/ - /*** STORAGE SYSTEM ***/ - /*********************************************************/ - - void SetVirtualItemSlot(uint8 i, Item* item); - void SetSheath(SheathState sheathed) override; // overwrite Unit version - bool GetSlotsForInventoryType(uint8 invType, uint8* slots, uint32 subClass = 0) const; - uint8 FindEquipSlot(ItemPrototype const* proto, uint32 slot, bool swap) const; - uint32 GetItemCount(uint32 item, bool inBankAlso = false, Item* skipItem = NULL) const; - uint32 GetItemCountWithLimitCategory(uint32 limitCategory, Item* skipItem = NULL) const; - Item* GetItemByGuid(ObjectGuid guid) const; - Item* GetItemByEntry(uint32 item) const; // only for special cases - Item* GetItemByLimitedCategory(uint32 limitedCategory) const; - Item* GetItemByPos(uint16 pos) const; - Item* GetItemByPos(uint8 bag, uint8 slot) const; - uint32 GetItemDisplayIdInSlot(uint8 bag, uint8 slot) const; - Item* GetWeaponForAttack(WeaponAttackType attackType) const { return GetWeaponForAttack(attackType, false, false); } - Item* GetWeaponForAttack(WeaponAttackType attackType, bool nonbroken, bool useable) const; - Item* GetShield(bool useable = false) const; - static uint32 GetAttackBySlot(uint8 slot); // MAX_ATTACK if not weapon slot - std::vector& GetItemUpdateQueue() { return m_itemUpdateQueue; } - static bool IsInventoryPos(uint16 pos) { return IsInventoryPos(pos >> 8, pos & 255); } - static bool IsInventoryPos(uint8 bag, uint8 slot); - static bool IsEquipmentPos(uint16 pos) { return IsEquipmentPos(pos >> 8, pos & 255); } - static bool IsEquipmentPos(uint8 bag, uint8 slot); - static bool IsBagPos(uint16 pos); - static bool IsBankPos(uint16 pos) { return IsBankPos(pos >> 8, pos & 255); } - static bool IsBankPos(uint8 bag, uint8 slot); - bool IsValidPos(uint16 pos, bool explicit_pos) const { return IsValidPos(pos >> 8, pos & 255, explicit_pos); } - bool IsValidPos(uint8 bag, uint8 slot, bool explicit_pos) const; - uint8 GetBankBagSlotCount() const { return GetByteValue(PLAYER_BYTES_2, 2); } - void SetBankBagSlotCount(uint8 count) { SetByteValue(PLAYER_BYTES_2, 2, count); } - bool HasItemCount(uint32 item, uint32 count, bool inBankAlso = false) const; - bool HasItemFitToSpellReqirements(SpellEntry const* spellInfo, Item const* ignoreItem = NULL); - bool CanNoReagentCast(SpellEntry const* spellInfo) const; - bool HasItemOrGemWithIdEquipped(uint32 item, uint32 count, uint8 except_slot = NULL_SLOT) const; - bool HasItemOrGemWithLimitCategoryEquipped(uint32 limitCategory, uint32 count, uint8 except_slot = NULL_SLOT) const; - InventoryResult CanTakeMoreSimilarItems(Item* pItem) const { return _CanTakeMoreSimilarItems(pItem->GetEntry(), pItem->GetCount(), pItem); } - InventoryResult CanTakeMoreSimilarItems(uint32 entry, uint32 count) const { return _CanTakeMoreSimilarItems(entry, count, NULL); } - InventoryResult CanStoreNewItem(uint8 bag, uint8 slot, ItemPosCountVec& dest, uint32 item, uint32 count, uint32* no_space_count = NULL) const - { - return _CanStoreItem(bag, slot, dest, item, count, NULL, false, no_space_count); - } - InventoryResult CanStoreItem(uint8 bag, uint8 slot, ItemPosCountVec& dest, Item* pItem, bool swap = false) const - { - if (!pItem) - return EQUIP_ERR_ITEM_NOT_FOUND; - uint32 count = pItem->GetCount(); - return _CanStoreItem(bag, slot, dest, pItem->GetEntry(), count, pItem, swap, NULL); - } - InventoryResult CanStoreItems(Item** pItem, int count) const; - InventoryResult CanEquipNewItem(uint8 slot, uint16& dest, uint32 item, bool swap) const; - InventoryResult CanEquipItem(uint8 slot, uint16& dest, Item* pItem, bool swap, bool direct_action = true) const; - - InventoryResult CanEquipUniqueItem(Item* pItem, uint8 except_slot = NULL_SLOT, uint32 limit_count = 1) const; - InventoryResult CanEquipUniqueItem(ItemPrototype const* itemProto, uint8 except_slot = NULL_SLOT, uint32 limit_count = 1) const; - InventoryResult CanUnequipItems(uint32 item, uint32 count) const; - InventoryResult CanUnequipItem(uint16 src, bool swap) const; - InventoryResult CanBankItem(uint8 bag, uint8 slot, ItemPosCountVec& dest, Item* pItem, bool swap, bool not_loading = true) const; - InventoryResult CanUseItem(Item* pItem, bool direct_action = true) const; - InventoryResult CanUseItem(ItemPrototype const* pItem) const; - InventoryResult CanUseAmmo(uint32 item) const; - Item* StoreNewItem(ItemPosCountVec const& pos, uint32 item, bool update, int32 randomPropertyId = 0); - Item* StoreItem(ItemPosCountVec const& pos, Item* pItem, bool update); - Item* EquipNewItem(uint16 pos, uint32 item, bool update); - Item* EquipItem(uint16 pos, Item* pItem, bool update); - void AutoUnequipOffhandIfNeed(); - bool StoreNewItemInBestSlots(uint32 item_id, uint32 item_count); - Item* StoreNewItemInInventorySlot(uint32 itemEntry, uint32 amount); - - void AutoStoreLoot(WorldObject const* lootTarget, uint32 loot_id, LootStore const& store, bool broadcast = false, uint8 bag = NULL_BAG, uint8 slot = NULL_SLOT); - void AutoStoreLoot(Loot& loot, bool broadcast = false, uint8 bag = NULL_BAG, uint8 slot = NULL_SLOT); - - Item* ConvertItem(Item* item, uint32 newItemId); - - InventoryResult _CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, uint32* no_space_count = NULL) const; - InventoryResult _CanStoreItem(uint8 bag, uint8 slot, ItemPosCountVec& dest, uint32 entry, uint32 count, Item* pItem = NULL, bool swap = false, uint32* no_space_count = NULL) const; - - void ApplyEquipCooldown(Item* pItem); - void SetAmmo(uint32 item); - void RemoveAmmo(); - float GetAmmoDPS() const { return m_ammoDPS; } - bool CheckAmmoCompatibility(const ItemPrototype* ammo_proto) const; - void QuickEquipItem(uint16 pos, Item* pItem); - void VisualizeItem(uint8 slot, Item* pItem); - void SetVisibleItemSlot(uint8 slot, Item* pItem); - Item* BankItem(ItemPosCountVec const& dest, Item* pItem, bool update) - { - return StoreItem(dest, pItem, update); - } - Item* BankItem(uint16 pos, Item* pItem, bool update); - void RemoveItem(uint8 bag, uint8 slot, bool update);// see ApplyItemOnStoreSpell notes - void MoveItemFromInventory(uint8 bag, uint8 slot, bool update); - // in trade, auction, guild bank, mail.... - void MoveItemToInventory(ItemPosCountVec const& dest, Item* pItem, bool update, bool in_characterInventoryDB = false); - // in trade, guild bank, mail.... - void RemoveItemDependentAurasAndCasts(Item* pItem); - void DestroyItem(uint8 bag, uint8 slot, bool update); - void DestroyItemCount(uint32 item, uint32 count, bool update, bool unequip_check = false, bool inBankAlso = false); - void DestroyItemCount(Item* item, uint32& count, bool update); - void DestroyConjuredItems(bool update); - void DestroyZoneLimitedItem(bool update, uint32 new_zone); - void SplitItem(uint16 src, uint16 dst, uint32 count); - void SwapItem(uint16 src, uint16 dst); - void AddItemToBuyBackSlot(Item* pItem); - Item* GetItemFromBuyBackSlot(uint32 slot); - void RemoveItemFromBuyBackSlot(uint32 slot, bool del); - - void TakeExtendedCost(uint32 extendedCostId); - - void SendEquipError(InventoryResult msg, Item* pItem, Item* pItem2 = NULL, uint32 itemid = 0) const; - void SendBuyError(BuyResult msg, Creature* pCreature, uint32 item, uint32 param); - void SendSellError(SellResult msg, Creature* pCreature, ObjectGuid itemGuid, uint32 param); - void AddWeaponProficiency(uint32 newflag) { m_WeaponProficiency |= newflag; } - void AddArmorProficiency(uint32 newflag) { m_ArmorProficiency |= newflag; } - uint32 GetWeaponProficiency() const { return m_WeaponProficiency; } - uint32 GetArmorProficiency() const { return m_ArmorProficiency; } - bool IsTwoHandUsed() const - { - Item* mainItem = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); - return mainItem && mainItem->GetProto()->InventoryType == INVTYPE_2HWEAPON && !CanTitanGrip(); - } - bool HasTwoHandWeaponInOneHand() const - { - Item* offItem = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); - Item* mainItem = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); - return offItem && ((mainItem && mainItem->GetProto()->InventoryType == INVTYPE_2HWEAPON) || offItem->GetProto()->InventoryType == INVTYPE_2HWEAPON); - } - void SendNewItem(Item* item, uint32 count, bool received, bool created, bool broadcast = false); - bool BuyItemFromVendorSlot(ObjectGuid vendorGuid, uint32 vendorslot, uint32 item, uint32 count, uint8 bag, uint8 slot); - bool BuyCurrencyFromVendorSlot(ObjectGuid vendorGuid, uint32 vendorslot, uint32 currencyId, uint32 count); - - float GetReputationPriceDiscount(Creature const* pCreature) const; - - Player* GetTrader() const { return m_trade ? m_trade->GetTrader() : NULL; } - TradeData* GetTradeData() const { return m_trade; } - void TradeCancel(bool sendback); - - void UpdateEnchantTime(uint32 time); - void UpdateItemDuration(uint32 time, bool realtimeonly = false); - void AddEnchantmentDurations(Item* item); - void RemoveEnchantmentDurations(Item* item); - void RemoveAllEnchantments(EnchantmentSlot slot); - void AddEnchantmentDuration(Item* item, EnchantmentSlot slot, uint32 duration); - void ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool apply_dur = true, bool ignore_condition = false); - void ApplyEnchantment(Item* item, bool apply); - void ApplyReforgeEnchantment(Item* item, bool apply); - void SendEnchantmentDurations(); - void BuildEnchantmentsInfoData(WorldPacket* data); - void AddItemDurations(Item* item); - void RemoveItemDurations(Item* item); - void SendItemDurations(); - void LoadCorpse(); - void LoadPet(); - - uint32 m_stableSlots; - - uint32 GetEquipGearScore(bool withBags = true, bool withBank = false); - void ResetCachedGearScore() { m_cachedGS = 0; } - typedef std::vector < uint32/*item level*/ > GearScoreVec; - - /*********************************************************/ - /*** GOSSIP SYSTEM ***/ - /*********************************************************/ - - void PrepareGossipMenu(WorldObject* pSource, uint32 menuId = 0); - void SendPreparedGossip(WorldObject* pSource); - void OnGossipSelect(WorldObject* pSource, uint32 gossipListId, uint32 menuId); - - uint32 GetGossipTextId(uint32 menuId, WorldObject* pSource); - uint32 GetGossipTextId(WorldObject* pSource); - uint32 GetDefaultGossipMenuForSource(WorldObject* pSource); - - /*********************************************************/ - /*** QUEST SYSTEM ***/ - /*********************************************************/ - - // Return player level when QuestLevel is dynamic (-1) - uint32 GetQuestLevelForPlayer(Quest const* pQuest) const { return pQuest && (pQuest->GetQuestLevel() > 0) ? (uint32)pQuest->GetQuestLevel() : getLevel(); } - - void PrepareQuestMenu(ObjectGuid guid); - void SendPreparedQuest(ObjectGuid guid); - bool IsActiveQuest(uint32 quest_id) const; // can be taken or taken - - // Quest is taken and not yet rewarded - // if completed_or_not = 0 (or any other value except 1 or 2) - returns true, if quest is taken and doesn't depend if quest is completed or not - // if completed_or_not = 1 - returns true, if quest is taken but not completed - // if completed_or_not = 2 - returns true, if quest is taken and already completed - bool IsCurrentQuest(uint32 quest_id, uint8 completed_or_not = 0) const; // taken and not yet rewarded - - Quest const* GetNextQuest(ObjectGuid guid, Quest const* pQuest); - bool CanSeeStartQuest(Quest const* pQuest) const; - bool CanTakeQuest(Quest const* pQuest, bool msg) const; - bool CanAddQuest(Quest const* pQuest, bool msg) const; - bool CanCompleteQuest(uint32 quest_id) const; - bool CanCompleteRepeatableQuest(Quest const* pQuest) const; - bool CanRewardQuest(Quest const* pQuest, bool msg) const; - bool CanRewardQuest(Quest const* pQuest, uint32 reward, bool msg) const; - void AddQuest(Quest const* pQuest, Object* questGiver); - void CompleteQuest(uint32 quest_id); - void IncompleteQuest(uint32 quest_id); - void RewardQuest(Quest const* pQuest, uint32 reward, Object* questGiver, bool announce = true); - - void FailQuest(uint32 quest_id); - bool SatisfyQuestSkill(Quest const* qInfo, bool msg) const; - bool SatisfyQuestSpell(Quest const* qInfo, bool msg) const; - bool SatisfyQuestLevel(Quest const* qInfo, bool msg) const; - bool SatisfyQuestLog(bool msg) const; - bool SatisfyQuestPreviousQuest(Quest const* qInfo, bool msg) const; - bool SatisfyQuestClass(Quest const* qInfo, bool msg) const; - bool SatisfyQuestRace(Quest const* qInfo, bool msg) const; - bool SatisfyQuestReputation(Quest const* qInfo, bool msg) const; - bool SatisfyQuestStatus(Quest const* qInfo, bool msg) const; - bool SatisfyQuestTimed(Quest const* qInfo, bool msg) const; - bool SatisfyQuestExclusiveGroup(Quest const* qInfo, bool msg) const; - bool SatisfyQuestNextChain(Quest const* qInfo, bool msg) const; - bool SatisfyQuestPrevChain(Quest const* qInfo, bool msg) const; - bool SatisfyQuestDay(Quest const* qInfo, bool msg) const; - bool SatisfyQuestWeek(Quest const* qInfo, bool msg) const; - bool SatisfyQuestMonth(Quest const* qInfo, bool msg) const; - bool CanGiveQuestSourceItemIfNeed(Quest const* pQuest, ItemPosCountVec* dest = NULL) const; - void GiveQuestSourceItemIfNeed(Quest const* pQuest); - bool TakeQuestSourceItem(uint32 quest_id, bool msg); - bool GetQuestRewardStatus(uint32 quest_id) const; - QuestStatus GetQuestStatus(uint32 quest_id) const; - void SetQuestStatus(uint32 quest_id, QuestStatus status); - - void SetDailyQuestStatus(uint32 quest_id); - void SetWeeklyQuestStatus(uint32 quest_id); - void SetMonthlyQuestStatus(uint32 quest_id); - void ResetDailyQuestStatus(); - void ResetWeeklyQuestStatus(); - void ResetMonthlyQuestStatus(); - - uint16 FindQuestSlot(uint32 quest_id) const; - uint32 GetQuestSlotQuestId(uint16 slot) const { return GetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot * MAX_QUEST_OFFSET + QUEST_ID_OFFSET); } - void SetQuestSlot(uint16 slot, uint32 quest_id, uint32 timer = 0) - { - SetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot * MAX_QUEST_OFFSET + QUEST_ID_OFFSET, quest_id); - SetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot * MAX_QUEST_OFFSET + QUEST_STATE_OFFSET, 0); - SetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot * MAX_QUEST_OFFSET + QUEST_COUNTS_OFFSET, 0); - SetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot * MAX_QUEST_OFFSET + QUEST_COUNTS_OFFSET + 1, 0); - SetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot * MAX_QUEST_OFFSET + QUEST_TIME_OFFSET, timer); - } - void SetQuestSlotCounter(uint16 slot, uint8 counter, uint16 count) - { - uint64 val = GetUInt64Value(PLAYER_QUEST_LOG_1_1 + slot * MAX_QUEST_OFFSET + QUEST_COUNTS_OFFSET); - val &= ~((uint64)0xFFFF << (counter * 16)); - val |= ((uint64)count << (counter * 16)); - SetUInt64Value(PLAYER_QUEST_LOG_1_1 + slot * MAX_QUEST_OFFSET + QUEST_COUNTS_OFFSET, val); - } - void SetQuestSlotState(uint16 slot, uint32 state) { SetFlag(PLAYER_QUEST_LOG_1_1 + slot * MAX_QUEST_OFFSET + QUEST_STATE_OFFSET, state); } - void RemoveQuestSlotState(uint16 slot, uint32 state) { RemoveFlag(PLAYER_QUEST_LOG_1_1 + slot * MAX_QUEST_OFFSET + QUEST_STATE_OFFSET, state); } - void SetQuestSlotTimer(uint16 slot, uint32 timer) { SetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot * MAX_QUEST_OFFSET + QUEST_TIME_OFFSET, timer); } - void SwapQuestSlot(uint16 slot1, uint16 slot2) - { - for (int i = 0; i < MAX_QUEST_OFFSET; ++i) - { - uint32 temp1 = GetUInt32Value(PLAYER_QUEST_LOG_1_1 + MAX_QUEST_OFFSET * slot1 + i); - uint32 temp2 = GetUInt32Value(PLAYER_QUEST_LOG_1_1 + MAX_QUEST_OFFSET * slot2 + i); - - SetUInt32Value(PLAYER_QUEST_LOG_1_1 + MAX_QUEST_OFFSET * slot1 + i, temp2); - SetUInt32Value(PLAYER_QUEST_LOG_1_1 + MAX_QUEST_OFFSET * slot2 + i, temp1); - } - } - uint32 GetReqKillOrCastCurrentCount(uint32 quest_id, int32 entry); - void AreaExploredOrEventHappens(uint32 questId); - void GroupEventHappens(uint32 questId, WorldObject const* pEventObject); - void CurrencyAddedQuestCheck(uint32 entry); - void CurrencyRemovedQuestCheck(uint32 entry); - void ItemAddedQuestCheck(uint32 entry, uint32 count); - void ItemRemovedQuestCheck(uint32 entry, uint32 count); - void SpellAddedQuestCheck(uint32 entry); - void SpellRemovedQuestCheck(uint32 entry); - void KilledMonster(CreatureInfo const* cInfo, ObjectGuid guid); - void KilledMonsterCredit(uint32 entry, ObjectGuid guid = ObjectGuid()); - void CastedCreatureOrGO(uint32 entry, ObjectGuid guid, uint32 spell_id, bool original_caster = true); - void TalkedToCreature(uint32 entry, ObjectGuid guid); - void MoneyChanged(uint32 value); - void ReputationChanged(FactionEntry const* factionEntry); - bool HasQuestForItem(uint32 itemid) const; - bool HasQuestForGO(int32 GOId) const; - void UpdateForQuestWorldObjects(); - bool CanShareQuest(uint32 quest_id) const; - - void SendQuestCompleteEvent(uint32 quest_id); - void SendQuestReward(Quest const* pQuest, uint32 XP, Object* questGiver); - void SendQuestFailed(uint32 quest_id, InventoryResult reason = EQUIP_ERR_OK); - void SendQuestTimerFailed(uint32 quest_id); - void SendCanTakeQuestResponse(uint32 msg) const; - void SendQuestConfirmAccept(Quest const* pQuest, Player* pReceiver); - void SendPushToPartyResponse(Player* pPlayer, uint32 msg); - void SendQuestUpdateAddCreatureOrGo(Quest const* pQuest, ObjectGuid guid, uint32 creatureOrGO_idx, uint32 count); - - ObjectGuid GetDividerGuid() const { return m_dividerGuid; } - void SetDividerGuid(ObjectGuid guid) { m_dividerGuid = guid; } - void ClearDividerGuid() { m_dividerGuid.Clear(); } - - uint32 GetInGameTime() { return m_ingametime; } - - void SetInGameTime(uint32 time) { m_ingametime = time; } - - void AddTimedQuest(uint32 quest_id) { m_timedquests.insert(quest_id); } - void RemoveTimedQuest(uint32 quest_id) { m_timedquests.erase(quest_id); } - - /*********************************************************/ - /*** LOAD SYSTEM ***/ - /*********************************************************/ - - bool LoadFromDB(ObjectGuid guid, SqlQueryHolder* holder); - - static uint32 GetZoneIdFromDB(ObjectGuid guid); - static uint32 GetLevelFromDB(ObjectGuid guid); - static bool LoadPositionFromDB(ObjectGuid guid, uint32& mapid, float& x, float& y, float& z, float& o, bool& in_flight); - - /*********************************************************/ - /*** SAVE SYSTEM ***/ - /*********************************************************/ - - void SaveToDB(); - void SaveInventoryAndGoldToDB(); // fast save function for item/money cheating preventing - void SaveGoldToDB(); - static void SetUInt32ValueInArray(Tokens& data, uint16 index, uint32 value); - static void SetFloatValueInArray(Tokens& data, uint16 index, float value); - static void Customize(ObjectGuid guid, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair); - static void SavePositionInDB(ObjectGuid guid, uint32 mapid, float x, float y, float z, float o, uint32 zone); - - static void DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRealmChars = true, bool deleteFinally = false); - static void DeleteOldCharacters(); - static void DeleteOldCharacters(uint32 keepDays); - - bool m_mailsUpdated; - - void SendPetTameFailure(PetTameFailureReason reason); - - void SetBindPoint(ObjectGuid guid); - void SendTalentWipeConfirm(ObjectGuid guid); - void RewardRage(uint32 damage, uint32 weaponSpeedHitFactor, bool attacker); - void SendPetSkillWipeConfirm(); - void CalcRage(uint32 damage, bool attacker); - void RegenerateAll(uint32 diff = REGEN_TIME_FULL); - void Regenerate(Powers power, uint32 diff); - void RegenerateHealth(uint32 diff); - void setRegenTimer(uint32 time) {m_regenTimer = time;} - void setWeaponChangeTimer(uint32 time) {m_weaponChangeTimer = time;} - - uint64 GetMoney() const { return GetUInt64Value(PLAYER_FIELD_COINAGE); } - void ModifyMoney(int64 d) - { - if (d < 0) - SetMoney(GetMoney() > uint64(-d) ? GetMoney() + d : 0); - else - SetMoney(GetMoney() < uint64(MAX_MONEY_AMOUNT - d) ? GetMoney() + d : MAX_MONEY_AMOUNT); - - // "At Gold Limit" - if (GetMoney() >= MAX_MONEY_AMOUNT) - SendEquipError(EQUIP_ERR_TOO_MUCH_GOLD, NULL, NULL); - } - void SetMoney(uint64 value) - { - SetUInt64Value(PLAYER_FIELD_COINAGE, value); - MoneyChanged(value); - UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED); - } - - QuestStatusMap& getQuestStatusMap() { return mQuestStatus; }; - - ObjectGuid const& GetSelectionGuid() const { return m_curSelectionGuid; } - void SetSelectionGuid(ObjectGuid guid) { m_curSelectionGuid = guid; SetTargetGuid(guid); } - - uint8 GetComboPoints() const { return m_comboPoints; } - ObjectGuid const& GetComboTargetGuid() const { return m_comboTargetGuid; } - - void AddComboPoints(Unit* target, int8 count); - void ClearComboPoints(); - void SendComboPoints(); - - void SendMailResult(uint32 mailId, MailResponseType mailAction, MailResponseResult mailError, uint32 equipError = 0, uint32 item_guid = 0, uint32 item_count = 0); - void SendNewMail(); - void UpdateNextMailTimeAndUnreads(); - void AddNewMailDeliverTime(time_t deliver_time); - - void RemoveMail(uint32 id); - - void AddMail(Mail* mail) { m_mail.push_front(mail);}// for call from WorldSession::SendMailTo - uint32 GetMailSize() { return m_mail.size(); } - Mail* GetMail(uint32 id); - - PlayerMails::iterator GetMailBegin() { return m_mail.begin();} - PlayerMails::iterator GetMailEnd() { return m_mail.end();} - - /*********************************************************/ - /*** MAILED ITEMS SYSTEM ***/ - /*********************************************************/ - - uint8 unReadMails; - time_t m_nextMailDelivereTime; - - typedef UNORDERED_MAP ItemMap; - - ItemMap mMitems; // template defined in objectmgr.cpp - - Item* GetMItem(uint32 id) - { - ItemMap::const_iterator itr = mMitems.find(id); - return itr != mMitems.end() ? itr->second : NULL; - } - - void AddMItem(Item* it) - { - MANGOS_ASSERT(it); - // ASSERT deleted, because items can be added before loading - mMitems[it->GetGUIDLow()] = it; - } - - bool RemoveMItem(uint32 id) - { - return mMitems.erase(id) ? true : false; - } - - void PetSpellInitialize(); - void SendPetGUIDs(); - void CharmSpellInitialize(); - void PossessSpellInitialize(); - void RemovePetActionBar(); - - bool HasSpell(uint32 spell) const override; - bool HasActiveSpell(uint32 spell) const; // show in spellbook - TrainerSpellState GetTrainerSpellState(TrainerSpell const* trainer_spell, uint32 reqLevel) const; - bool IsSpellFitByClassAndRace(uint32 spell_id, uint32* pReqlevel = NULL) const; - bool IsNeedCastPassiveLikeSpellAtLearn(SpellEntry const* spellInfo) const; - bool IsImmuneToSpellEffect(SpellEntry const* spellInfo, SpellEffectIndex index, bool castOnSelf) const override; - - void SendProficiency(ItemClass itemClass, uint32 itemSubclassMask); - void SendInitialSpells(); - bool addSpell(uint32 spell_id, bool active, bool learning, bool dependent, bool disabled); - void learnSpell(uint32 spell_id, bool dependent); - void removeSpell(uint32 spell_id, bool disabled = false, bool learn_low_rank = true, bool sendUpdate = true); - void resetSpells(); - void learnDefaultSpells(); - void learnQuestRewardedSpells(); - void learnQuestRewardedSpells(Quest const* quest); - void learnSpellHighRank(uint32 spellid); - - uint32 GetFreeTalentPoints() const { return m_freeTalentPoints; } - void SetFreeTalentPoints(uint32 points) { m_freeTalentPoints = points; } - void UpdateFreeTalentPoints(bool resetIfNeed = true); - uint32 GetPrimaryTalentTree(uint8 spec) const { return m_talentsPrimaryTree[spec]; } - void SetPrimaryTalentTree(uint8 spec, uint32 tree) { m_talentsPrimaryTree[spec] = tree; } - bool resetTalents(bool no_cost = false, bool all_specs = false); - uint32 resetTalentsCost() const; - void InitTalentForLevel(); - void BuildPlayerTalentsInfoData(WorldPacket* data); - void BuildPetTalentsInfoData(WorldPacket* data); - void SendTalentsInfoData(bool pet); - bool LearnTalent(uint32 talentId, uint32 talentRank); - void LearnPetTalent(ObjectGuid petGuid, uint32 talentId, uint32 talentRank); - - uint32 CalculateTalentsPoints() const; - - // Dual Spec - uint8 GetActiveSpec() { return m_activeSpec; } - void SetActiveSpec(uint8 spec) { m_activeSpec = spec; } - uint8 GetSpecsCount() { return m_specsCount; } - void SetSpecsCount(uint8 count) { m_specsCount = count; } - void ActivateSpec(uint8 specNum); - void UpdateSpecCount(uint8 count); - - void InitGlyphsForLevel(); - void SetGlyphSlot(uint8 slot, uint32 slottype) { SetUInt32Value(PLAYER_FIELD_GLYPH_SLOTS_1 + slot, slottype); } - uint32 GetGlyphSlot(uint8 slot) const { return GetUInt32Value(PLAYER_FIELD_GLYPH_SLOTS_1 + slot); } - void SetGlyph(uint8 slot, uint32 glyph) { m_glyphs[m_activeSpec][slot].SetId(glyph); } - uint32 GetGlyph(uint8 slot) { return m_glyphs[m_activeSpec][slot].GetId(); } - void ApplyGlyph(uint8 slot, bool apply); - void ApplyGlyphs(bool apply); - - uint32 GetFreePrimaryProfessionPoints() const { return GetUInt32Value(PLAYER_CHARACTER_POINTS); } - void SetFreePrimaryProfessions(uint16 profs) { SetUInt32Value(PLAYER_CHARACTER_POINTS, profs); } - void InitPrimaryProfessions(); - - PlayerSpellMap const& GetSpellMap() const { return m_spells; } - PlayerSpellMap& GetSpellMap() { return m_spells; } - - SpellCooldowns const& GetSpellCooldownMap() const { return m_spellCooldowns; } - - PlayerTalent const* GetKnownTalentById(int32 talentId) const; - SpellEntry const* GetKnownTalentRankById(int32 talentId) const; - - void AddSpellMod(Aura* aura, bool apply); - template T ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell const* spell = NULL); - - static uint32 const infinityCooldownDelay = MONTH; // used for set "infinity cooldowns" for spells and check - static uint32 const infinityCooldownDelayCheck = MONTH / 2; - bool HasSpellCooldown(uint32 spell_id) const - { - SpellCooldowns::const_iterator itr = m_spellCooldowns.find(spell_id); - return itr != m_spellCooldowns.end() && itr->second.end > time(NULL); - } - time_t GetSpellCooldownDelay(uint32 spell_id) const - { - SpellCooldowns::const_iterator itr = m_spellCooldowns.find(spell_id); - time_t t = time(NULL); - return itr != m_spellCooldowns.end() && itr->second.end > t ? itr->second.end - t : 0; - } - void AddSpellAndCategoryCooldowns(SpellEntry const* spellInfo, uint32 itemId, Spell* spell = NULL, bool infinityCooldown = false); - void AddSpellCooldown(uint32 spell_id, uint32 itemid, time_t end_time); - void SendCooldownEvent(SpellEntry const* spellInfo, uint32 itemId = 0, Spell* spell = NULL); - void ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs) override; - void RemoveSpellCooldown(uint32 spell_id, bool update = false); - void RemoveSpellCategoryCooldown(uint32 cat, bool update = false); - void SendClearCooldown(uint32 spell_id, Unit* target); - - GlobalCooldownMgr& GetGlobalCooldownMgr() { return m_GlobalCooldownMgr; } - - void RemoveArenaSpellCooldowns(); - void RemoveAllSpellCooldown(); - void _LoadSpellCooldowns(QueryResult* result); - void _SaveSpellCooldowns(); - void SetLastPotionId(uint32 item_id) { m_lastPotionId = item_id; } - uint32 GetLastPotionId() { return m_lastPotionId; } - void UpdatePotionCooldown(Spell* spell = NULL); - - void setResurrectRequestData(ObjectGuid guid, uint32 mapId, float X, float Y, float Z, uint32 health, uint32 mana) - { - m_resurrectGuid = guid; - m_resurrectMap = mapId; - m_resurrectX = X; - m_resurrectY = Y; - m_resurrectZ = Z; - m_resurrectHealth = health; - m_resurrectMana = mana; - } - void clearResurrectRequestData() { setResurrectRequestData(ObjectGuid(), 0, 0.0f, 0.0f, 0.0f, 0, 0); } - bool isRessurectRequestedBy(ObjectGuid guid) const { return m_resurrectGuid == guid; } - bool isRessurectRequested() const { return !m_resurrectGuid.IsEmpty(); } - void ResurectUsingRequestData(); - - uint32 getCinematic() { return m_cinematic; } - void setCinematic(uint32 cine) { m_cinematic = cine; } - - static bool IsActionButtonDataValid(uint8 button, uint32 action, uint8 type, Player* player, bool msg = true); - ActionButton* addActionButton(uint8 spec, uint8 button, uint32 action, uint8 type); - void removeActionButton(uint8 spec, uint8 button); - void SendInitialActionButtons() const; - void SendLockActionButtons() const; - ActionButton const* GetActionButton(uint8 button); - - PvPInfo pvpInfo; - void UpdatePvP(bool state, bool ovrride = false); - void UpdateZone(uint32 newZone, uint32 newArea); - void UpdateArea(uint32 newArea); - uint32 GetCachedZoneId() const { return m_zoneUpdateId; } - - void UpdateZoneDependentAuras(); - void UpdateAreaDependentAuras(); // subzones - void UpdateZoneDependentPets(); - - void UpdateAfkReport(time_t currTime); - void UpdatePvPFlag(time_t currTime); - void UpdateContestedPvP(uint32 currTime); - void SetContestedPvPTimer(uint32 newTime) {m_contestedPvPTimer = newTime;} - void ResetContestedPvP() - { - clearUnitState(UNIT_STAT_ATTACK_PLAYER); - RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP); - m_contestedPvPTimer = 0; - } - - /** todo: -maybe move UpdateDuelFlag+DuelComplete to independent DuelHandler.. **/ - DuelInfo* duel; - bool IsInDuelWith(Player const* player) const { return duel && duel->opponent == player && duel->startTime != 0; } - void UpdateDuelFlag(time_t currTime); - void CheckDuelDistance(time_t currTime); - void DuelComplete(DuelCompleteType type); - void SendDuelCountdown(uint32 counter); - - bool IsGroupVisibleFor(Player* p) const; - bool IsInSameGroupWith(Player const* p) const; - bool IsInSameRaidWith(Player const* p) const { return p == this || (GetGroup() != NULL && GetGroup() == p->GetGroup()); } - void UninviteFromGroup(); - static void RemoveFromGroup(Group* group, ObjectGuid guid); - void RemoveFromGroup() { RemoveFromGroup(GetGroup(), GetObjectGuid()); } - void SendUpdateToOutOfRangeGroupMembers(); - void SetAllowLowLevelRaid(bool allow) { ApplyModFlag(PLAYER_FLAGS, PLAYER_FLAGS_ENABLE_LOW_LEVEL_RAID, allow); } - bool GetAllowLowLevelRaid() const { return HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_ENABLE_LOW_LEVEL_RAID); } - - void SetInGuild(uint32 GuildId); - void SetGuildLevel(uint32 level) { SetUInt32Value(PLAYER_GUILDLEVEL, level); } - void SetRank(uint32 rankId){ SetUInt32Value(PLAYER_GUILDRANK, rankId); } - void SetGuildInvited(uint32 GuildId, ObjectGuid inviter = ObjectGuid()) { m_GuildIdInvited = GuildId; m_GuildInviterGuid = inviter; } - uint32 GetGuildId() const { return GetGuildGuid().GetCounter(); } - ObjectGuid GetGuildGuid() const { return GetGuidValue(OBJECT_FIELD_DATA); } - std::string GetGuildName() const; - static uint32 GetGuildIdFromDB(ObjectGuid guid); - static ObjectGuid GetGuildGuidFromDB(ObjectGuid guid); - void SendGuildDeclined(std::string name, bool autodecline); - uint32 GetRank() { return GetUInt32Value(PLAYER_GUILDRANK); } - static uint32 GetRankFromDB(ObjectGuid guid); - int GetGuildIdInvited() const { return m_GuildIdInvited; } - ObjectGuid GetGuildInviterGuid() const { return m_GuildInviterGuid; } - static void RemovePetitionsAndSigns(ObjectGuid guid); - void SendPetitionSignResult(ObjectGuid petitionGuid, Player* player, uint32 result); - void SendPetitionTurnInResult(uint32 result); - - // Arena Team - void SetInArenaTeam(uint32 ArenaTeamId, uint8 slot, ArenaType type) - { - SetArenaTeamInfoField(slot, ARENA_TEAM_ID, ArenaTeamId); - SetArenaTeamInfoField(slot, ARENA_TEAM_TYPE, type); - } - void SetArenaTeamInfoField(uint8 slot, ArenaTeamInfoType type, uint32 value) - { - SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (slot * ARENA_TEAM_END) + type, value); - } - uint32 GetArenaTeamId(uint8 slot) { return GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (slot * ARENA_TEAM_END) + ARENA_TEAM_ID); } - uint32 GetArenaPersonalRating(uint8 slot) { return GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (slot * ARENA_TEAM_END) + ARENA_TEAM_PERSONAL_RATING); } - static uint32 GetArenaTeamIdFromDB(ObjectGuid guid, ArenaType type); - void SetArenaTeamIdInvited(uint32 ArenaTeamId) { m_ArenaTeamIdInvited = ArenaTeamId; } - uint32 GetArenaTeamIdInvited() { return m_ArenaTeamIdInvited; } - static void LeaveAllArenaTeams(ObjectGuid guid); - - Difficulty GetDifficulty(bool isRaid) const { return isRaid ? m_raidDifficulty : m_dungeonDifficulty; } - Difficulty GetDungeonDifficulty() const { return m_dungeonDifficulty; } - Difficulty GetRaidDifficulty() const { return m_raidDifficulty; } - void SetDungeonDifficulty(Difficulty dungeon_difficulty) { m_dungeonDifficulty = dungeon_difficulty; } - void SetRaidDifficulty(Difficulty raid_difficulty) { m_raidDifficulty = raid_difficulty; } - - bool UpdateSkill(uint32 skill_id, uint32 step); - bool UpdateSkillPro(uint16 SkillId, int32 Chance, uint32 step); - - bool UpdateCraftSkill(uint32 spellid); - bool UpdateGatherSkill(uint32 SkillId, uint32 SkillValue, uint32 RedLevel, uint32 Multiplicator = 1); - bool UpdateFishingSkill(); - - float GetHealthBonusFromStamina(); - float GetManaBonusFromIntellect(); - - bool UpdateStats(Stats stat) override; - bool UpdateAllStats() override; - void UpdateResistances(uint32 school) override; - void UpdateArmor() override; - void UpdateMaxHealth() override; - void UpdateMaxPower(Powers power) override; - void UpdateAttackPowerAndDamage(bool ranged = false) override; - void UpdateShieldBlockDamageValue(); - void UpdateDamagePhysical(WeaponAttackType attType) override; - void ApplySpellPowerBonus(int32 amount, bool apply); - void UpdateSpellDamageAndHealingBonus(); - void ApplyRatingMod(CombatRating cr, int32 value, bool apply); - void UpdateRating(CombatRating cr); - void UpdateAllRatings(); - - void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, float& min_damage, float& max_damage); - - float GetMeleeCritFromAgility(); - void GetDodgeFromAgility(float& diminishing, float& nondiminishing); - void GetParryFromStrength(float& diminishing, float& nondiminishing); - float GetSpellCritFromIntellect(); - float OCTRegenMPPerSpirit(); - float GetRatingMultiplier(CombatRating cr) const; - float GetRatingBonusValue(CombatRating cr) const; - // Returns base spellpower bonus from items without intellect bonus - uint32 GetBaseSpellPowerBonus() const { return m_baseSpellPower; } - - float GetExpertiseDodgeOrParryReduction(WeaponAttackType attType) const; - void UpdateBlockPercentage(); - void UpdateCritPercentage(WeaponAttackType attType); - void UpdateAllCritPercentages(); - void UpdateParryPercentage(); - void UpdateDodgePercentage(); - void UpdateMeleeHitChances(); - void UpdateRangedHitChances(); - void UpdateSpellHitChances(); - - void UpdateAllSpellCritChances(); - void UpdateSpellCritChance(uint32 school); - void UpdateExpertise(WeaponAttackType attType); - void UpdateArmorPenetration(); - void ApplyManaRegenBonus(int32 amount, bool apply); - void ApplyHealthRegenBonus(int32 amount, bool apply); - void UpdateManaRegen(); - void UpdateMasteryAuras(); - void UpdateArmorSpecializations(); - bool FitArmorSpecializationRules(SpellEntry const * spellProto) const; - - ObjectGuid const& GetLootGuid() const { return m_lootGuid; } - void SetLootGuid(ObjectGuid const& guid) { m_lootGuid = guid; } - - void RemovedInsignia(Player* looterPlr); - - WorldSession* GetSession() const { return m_session; } - void SetSession(WorldSession* s) { m_session = s; } - - void BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const override; - void DestroyForPlayer(Player* target, bool anim = false) const override; - void SendLogXPGain(uint32 GivenXP, Unit* victim, uint32 RestXP); - - uint8 LastSwingErrorMsg() const { return m_swingErrorMsg; } - void SwingErrorMsg(uint8 val) { m_swingErrorMsg = val; } - - // notifiers - void SendAttackSwingCantAttack(); - void SendAttackSwingCancelAttack(); - void SendAttackSwingDeadTarget(); - void SendAttackSwingNotInRange(); - void SendAttackSwingBadFacingAttack(); - void SendAutoRepeatCancel(Unit* target); - void SendExplorationExperience(uint32 Area, uint32 Experience); - - void SendDungeonDifficulty(bool IsInGroup); - void SendRaidDifficulty(bool IsInGroup); - void ResetInstances(InstanceResetMethod method, bool isRaid); - void SendResetInstanceSuccess(uint32 MapId); - void SendResetInstanceFailed(uint32 reason, uint32 MapId); - void SendResetFailedNotify(uint32 mapid); - - bool SetPosition(float x, float y, float z, float orientation, bool teleport = false); - void UpdateUnderwaterState(Map* m, float x, float y, float z); - - void SendMessageToSet(WorldPacket* data, bool self) const override;// overwrite Object::SendMessageToSet - void SendMessageToSetInRange(WorldPacket* data, float fist, bool self) const override; - // overwrite Object::SendMessageToSetInRange - void SendMessageToSetInRange(WorldPacket* data, float dist, bool self, bool own_team_only) const; - - Corpse* GetCorpse() const; - void SpawnCorpseBones(); - Corpse* CreateCorpse(); - void KillPlayer(); - uint32 GetResurrectionSpellId(); - void ResurrectPlayer(float restore_percent, bool applySickness = false); - void BuildPlayerRepop(); - void RepopAtGraveyard(); - - void DurabilityLossAll(double percent, bool inventory); - void DurabilityLoss(Item* item, double percent); - void DurabilityPointsLossAll(int32 points, bool inventory); - void DurabilityPointsLoss(Item* item, int32 points); - void DurabilityPointLossForEquipSlot(EquipmentSlots slot); - uint32 DurabilityRepairAll(bool cost, float discountMod, bool guildBank); - uint32 DurabilityRepair(uint16 pos, bool cost, float discountMod, bool guildBank); - - void UpdateMirrorTimers(); - void StopMirrorTimers() - { - StopMirrorTimer(FATIGUE_TIMER); - StopMirrorTimer(BREATH_TIMER); - StopMirrorTimer(FIRE_TIMER); - } - - void SetRoot(bool enable) override; - void SetWaterWalk(bool enable) override; - - void JoinedChannel(Channel* c); - void LeftChannel(Channel* c); - void CleanupChannels(); - void UpdateLocalChannels(uint32 newZone); - void LeaveLFGChannel(); - - void SetSkill(uint16 id, uint16 currVal, uint16 maxVal, uint16 step = 0); - uint16 GetMaxSkillValue(uint32 skill) const; // max + perm. bonus + temp bonus - uint16 GetPureMaxSkillValue(uint32 skill) const; // max - uint16 GetSkillValue(uint32 skill) const; // skill value + perm. bonus + temp bonus - uint16 GetBaseSkillValue(uint32 skill) const; // skill value + perm. bonus - uint16 GetPureSkillValue(uint32 skill) const; // skill value - int16 GetSkillPermBonusValue(uint32 skill) const; - int16 GetSkillTempBonusValue(uint32 skill) const; - bool HasSkill(uint32 skill) const; - uint16 GetSkillStep(uint16 skill) const; - void learnSkillRewardedSpells(uint32 id, uint32 value); - - WorldLocation& GetTeleportDest() { return m_teleport_dest; } - bool IsBeingTeleported() const { return mSemaphoreTeleport_Near || mSemaphoreTeleport_Far; } - bool IsBeingTeleportedNear() const { return mSemaphoreTeleport_Near; } - bool IsBeingTeleportedFar() const { return mSemaphoreTeleport_Far; } - void SetSemaphoreTeleportNear(bool semphsetting) { mSemaphoreTeleport_Near = semphsetting; } - void SetSemaphoreTeleportFar(bool semphsetting) { mSemaphoreTeleport_Far = semphsetting; } - void ProcessDelayedOperations(); - - void CheckAreaExploreAndOutdoor(); - - static Team TeamForRace(uint8 race); - Team GetTeam() const { return m_team; } - static uint32 getFactionForRace(uint8 race); - void setFactionForRace(uint8 race); - - void InitDisplayIds(); - - bool IsAtGroupRewardDistance(WorldObject const* pRewardSource) const; - void RewardSinglePlayerAtKill(Unit* pVictim); - void RewardPlayerAndGroupAtEvent(uint32 creature_id, WorldObject* pRewardSource); - void RewardPlayerAndGroupAtCast(WorldObject* pRewardSource, uint32 spellid = 0); - bool isHonorOrXPTarget(Unit* pVictim) const; - - ReputationMgr& GetReputationMgr() { return m_reputationMgr; } - ReputationMgr const& GetReputationMgr() const { return m_reputationMgr; } - ReputationRank GetReputationRank(uint32 faction_id) const; - void RewardReputation(Unit* pVictim, float rate); - void RewardReputation(Quest const* pQuest); - int32 CalculateReputationGain(ReputationSource source, int32 rep, int32 faction, uint32 creatureOrQuestLevel = 0, bool noAuraBonus = false); - - void UpdateSkillsForLevel(); - void UpdateSkillsToMaxSkillsForLevel(); // for .levelup - void ModifySkillBonus(uint32 skillid, int32 val, bool talent); - - /*********************************************************/ - /*** CURRENCY SYSTEM ***/ - /*********************************************************/ - uint32 GetCurrencyCount(uint32 id) const; - uint32 GetCurrencySeasonCount(uint32 id) const; - uint32 GetCurrencyWeekCount(uint32 id) const; - void SendCurrencies() const; - void ModifyCurrencyCount(uint32 id, int32 count, bool modifyWeek = true, bool modifySeason = true, bool ignoreMultipliers = false); - bool HasCurrencyCount(uint32 id, uint32 count) const { return GetCurrencyCount(id) >= count; } - bool HasCurrencySeasonCount(uint32 id, uint32 count) const { return GetCurrencySeasonCount(id) >= count; } - void SetCurrencyCount(uint32 id, uint32 count); - void SendCurrencyWeekCap(uint32 id) const; - void SendCurrencyWeekCap(CurrencyTypesEntry const * currency) const; - void SetCurrencyFlags(uint32 currencyId, uint8 flags); - void ResetCurrencyWeekCounts(); - - /*********************************************************/ - /*** PVP SYSTEM ***/ - /*********************************************************/ - void UpdateHonorKills(); - bool RewardHonor(Unit *pVictim, uint32 groupsize, float honor = -1); - void SendPvPRewards(); - void SendRatedBGStats(); - - uint32 GetMaxPersonalArenaRatingRequirement(uint32 minarenaslot); - - // End of PvP System - - void SetDrunkValue(uint8 newDrunkValue, uint32 itemid = 0); - uint16 GetDrunkValue() const { return GetByteValue(PLAYER_BYTES_3, 1); } - static DrunkenState GetDrunkenstateByValue(uint8 value); - - uint32 GetDeathTimer() const { return m_deathTimer; } - uint32 GetCorpseReclaimDelay(bool pvp) const; - void UpdateCorpseReclaimDelay(); - void SendCorpseReclaimDelay(bool load = false); - - uint32 GetShieldBlockDamageValue() const override; // overwrite Unit version (virtual) - bool CanParry() const { return m_canParry; } - void SetCanParry(bool value); - bool CanBlock() const { return m_canBlock; } - void SetCanBlock(bool value); - bool CanDualWield() const { return m_canDualWield; } - void SetCanDualWield(bool value) { m_canDualWield = value; } - bool CanTitanGrip() const { return m_canTitanGrip; } - void SetCanTitanGrip(bool value) { m_canTitanGrip = value; } - bool CanTameExoticPets() const { return isGameMaster() || HasAuraType(SPELL_AURA_ALLOW_TAME_PET_TYPE); } - - void SetRegularAttackTime(); - void SetBaseModValue(BaseModGroup modGroup, BaseModType modType, float value) { m_auraBaseMod[modGroup][modType] = value; } - void HandleBaseModValue(BaseModGroup modGroup, BaseModType modType, float amount, bool apply); - float GetBaseModValue(BaseModGroup modGroup, BaseModType modType) const; - float GetTotalBaseModValue(BaseModGroup modGroup) const; - float GetTotalPercentageModValue(BaseModGroup modGroup) const { return m_auraBaseMod[modGroup][FLAT_MOD] + m_auraBaseMod[modGroup][PCT_MOD]; } - void _ApplyAllStatBonuses(); - void _RemoveAllStatBonuses(); - float GetArmorPenetrationPct() const { return m_armorPenetrationPct; } - int32 GetSpellPenetrationItemMod() const { return m_spellPenetrationItemMod; } - - void _ApplyWeaponDependentAuraMods(Item* item, WeaponAttackType attackType, bool apply); - void _ApplyWeaponDependentAuraCritMod(Item* item, WeaponAttackType attackType, Aura* aura, bool apply); - void _ApplyWeaponDependentAuraDamageMod(Item* item, WeaponAttackType attackType, Aura* aura, bool apply); - - void _ApplyItemMods(Item* item, uint8 slot, bool apply); - void _RemoveAllItemMods(); - void _ApplyAllItemMods(); - void _ApplyAllLevelScaleItemMods(bool apply); - void _ApplyItemBonuses(ItemPrototype const* proto, uint8 slot, bool apply, bool only_level_scale = false); - void _ApplyAmmoBonuses(); - bool EnchantmentFitsRequirements(uint32 enchantmentcondition, int8 slot); - void ToggleMetaGemsActive(uint8 exceptslot, bool apply); - void CorrectMetaGemEnchants(uint8 slot, bool apply); - void InitDataForForm(bool reapplyMods = false); - - void ApplyItemEquipSpell(Item* item, bool apply, bool form_change = false); - void ApplyEquipSpell(SpellEntry const* spellInfo, Item* item, bool apply, bool form_change = false); - void UpdateEquipSpellsAtFormChange(); - void CastItemCombatSpell(Unit* Target, WeaponAttackType attType); - void CastItemUseSpell(Item* item, SpellCastTargets const& targets, uint8 cast_count, uint32 glyphIndex); - - void ApplyItemOnStoreSpell(Item* item, bool apply); - void DestroyItemWithOnStoreSpell(Item* item, uint32 spellId); - - void SendEquipmentSetList(); - void SetEquipmentSet(uint32 index, EquipmentSet eqset); - void DeleteEquipmentSet(uint64 setGuid); - - void SendInitWorldStates(uint32 zone, uint32 area); - void SendUpdateWorldState(uint32 Field, uint32 Value); - void SendDirectMessage(WorldPacket* data) const; - void FillBGWeekendWorldStates(WorldPacket& data, uint32& count); - - void SendAurasForTarget(Unit* target); - - PlayerMenu* PlayerTalkClass; - std::vector ItemSetEff; - - void SendLoot(ObjectGuid guid, LootType loot_type); - void SendLootRelease(ObjectGuid guid); - void SendNotifyLootItemRemoved(uint8 lootSlot, bool currency = false); - void SendNotifyLootMoneyRemoved(); - - /*********************************************************/ - /*** BATTLEGROUND SYSTEM ***/ - /*********************************************************/ - - bool InBattleGround() const { return m_bgData.bgInstanceID != 0; } - bool InArena() const; - uint32 GetBattleGroundId() const { return m_bgData.bgInstanceID; } - BattleGroundTypeId GetBattleGroundTypeId() const { return m_bgData.bgTypeID; } - BattleGround* GetBattleGround() const; - - bool InBattleGroundQueue() const - { - for (int i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) - if (m_bgBattleGroundQueueID[i].bgQueueTypeId != BATTLEGROUND_QUEUE_NONE) - return true; - return false; - } - - BattleGroundQueueTypeId GetBattleGroundQueueTypeId(uint32 index) const { return m_bgBattleGroundQueueID[index].bgQueueTypeId; } - BattleGroundQueueTypeId GetBattleGroundQueueTypeIdByClientInstance(uint32 instanceId) const - { - for (int i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) - if (m_bgBattleGroundQueueID[i].invitedToInstance == instanceId) - return m_bgBattleGroundQueueID[i].bgQueueTypeId; - return BATTLEGROUND_QUEUE_NONE; - } - uint32 GetBattleGroundQueueIndex(BattleGroundQueueTypeId bgQueueTypeId) const - { - for (int i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) - if (m_bgBattleGroundQueueID[i].bgQueueTypeId == bgQueueTypeId) - return i; - return PLAYER_MAX_BATTLEGROUND_QUEUES; - } - bool IsInvitedForBattleGroundQueueType(BattleGroundQueueTypeId bgQueueTypeId) const - { - for (int i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) - if (m_bgBattleGroundQueueID[i].bgQueueTypeId == bgQueueTypeId) - return m_bgBattleGroundQueueID[i].invitedToInstance != 0; - return false; - } - bool InBattleGroundQueueForBattleGroundQueueType(BattleGroundQueueTypeId bgQueueTypeId) const - { - return GetBattleGroundQueueIndex(bgQueueTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES; - } - - void SetBattleGroundId(uint32 val, BattleGroundTypeId bgTypeId) - { - m_bgData.bgInstanceID = val; - m_bgData.bgTypeID = bgTypeId; - m_bgData.m_needSave = true; - } - uint32 AddBattleGroundQueueId(BattleGroundQueueTypeId val) - { - for (int i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) - { - if (m_bgBattleGroundQueueID[i].bgQueueTypeId == BATTLEGROUND_QUEUE_NONE || m_bgBattleGroundQueueID[i].bgQueueTypeId == val) - { - m_bgBattleGroundQueueID[i].bgQueueTypeId = val; - m_bgBattleGroundQueueID[i].invitedToInstance = 0; - return i; - } - } - return PLAYER_MAX_BATTLEGROUND_QUEUES; - } - bool HasFreeBattleGroundQueueId() - { - for (int i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) - if (m_bgBattleGroundQueueID[i].bgQueueTypeId == BATTLEGROUND_QUEUE_NONE) - return true; - return false; - } - void RemoveBattleGroundQueueId(BattleGroundQueueTypeId val) - { - for (int i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) - { - if (m_bgBattleGroundQueueID[i].bgQueueTypeId == val) - { - m_bgBattleGroundQueueID[i].bgQueueTypeId = BATTLEGROUND_QUEUE_NONE; - m_bgBattleGroundQueueID[i].invitedToInstance = 0; - return; - } - } - } - void SetInviteForBattleGroundQueueType(BattleGroundQueueTypeId bgQueueTypeId, uint32 instanceId) - { - for (int i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) - if (m_bgBattleGroundQueueID[i].bgQueueTypeId == bgQueueTypeId) - m_bgBattleGroundQueueID[i].invitedToInstance = instanceId; - } - bool IsInvitedForBattleGroundInstance(uint32 instanceId) const - { - for (int i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) - if (m_bgBattleGroundQueueID[i].invitedToInstance == instanceId) - return true; - return false; - } - WorldLocation const& GetBattleGroundEntryPoint() const { return m_bgData.joinPos; } - void SetBattleGroundEntryPoint(); - - void SetBGTeam(Team team) { m_bgData.bgTeam = team; m_bgData.m_needSave = true; } - Team GetBGTeam() const { return m_bgData.bgTeam ? m_bgData.bgTeam : GetTeam(); } - - void LeaveBattleground(bool teleportToEntryPoint = true); - bool CanJoinToBattleground() const; - bool CanReportAfkDueToLimit(); - void ReportedAfkBy(Player* reporter); - void ClearAfkReports() { m_bgData.bgAfkReporter.clear(); } - - bool GetBGAccessByLevel(BattleGroundTypeId bgTypeId) const; - bool CanUseBattleGroundObject(); - bool isTotalImmune(); - - // returns true if the player is in active state for capture point capturing - bool CanUseCapturePoint(); - - /*********************************************************/ - /*** REST SYSTEM ***/ - /*********************************************************/ - - bool isRested() const { return GetRestTime() >= 10 * IN_MILLISECONDS; } - uint32 GetXPRestBonus(uint32 xp); - uint32 GetRestTime() const { return m_restTime; } - void SetRestTime(uint32 v) { m_restTime = v; } - - /*********************************************************/ - /*** ENVIROMENTAL SYSTEM ***/ - /*********************************************************/ - - uint32 EnvironmentalDamage(EnviromentalDamage type, uint32 damage); - - /*********************************************************/ - /*** FLOOD FILTER SYSTEM ***/ - /*********************************************************/ - - void UpdateSpeakTime(); - bool CanSpeak() const; - void ChangeSpeakTime(int utime); - - /*********************************************************/ - /*** VARIOUS SYSTEMS ***/ - /*********************************************************/ - bool HasMovementFlag(MovementFlags f) const; // for script access to m_movementInfo.HasMovementFlag - void UpdateFallInformationIfNeed(MovementInfo const& minfo, uint16 opcode); - void SetFallInformation(uint32 time, float z) - { - m_lastFallTime = time; - m_lastFallZ = z; - } - void HandleFall(MovementInfo const& movementInfo); - - bool isMoving() const { return m_movementInfo.HasMovementFlag(movementFlagsMask); } - bool isMovingOrTurning() const { return m_movementInfo.HasMovementFlag(movementOrTurningFlagsMask); } - - bool CanFly() const { return m_movementInfo.HasMovementFlag(MOVEFLAG_CAN_FLY); } - bool IsFlying() const { return m_movementInfo.HasMovementFlag(MOVEFLAG_FLYING); } - bool IsFreeFlying() const { return HasAuraType(SPELL_AURA_MOD_FLIGHT_SPEED_MOUNTED) || HasAuraType(SPELL_AURA_FLY); } - bool CanStartFlyInArea(uint32 mapid, uint32 zone, uint32 area) const; - - void SetClientControl(Unit* target, uint8 allowMove); - void SetMover(Unit* target) { m_mover = target ? target : this; } - Unit* GetMover() const { return m_mover; } - bool IsSelfMover() const { return m_mover == this; }// normal case for player not controlling other unit - - ObjectGuid const& GetFarSightGuid() const { return GetGuidValue(PLAYER_FARSIGHT); } - - // Transports - Transport* GetTransport() const { return m_transport; } - void SetTransport(Transport* t) { m_transport = t; } - - float GetTransOffsetX() const { return m_movementInfo.GetTransportPos()->x; } - float GetTransOffsetY() const { return m_movementInfo.GetTransportPos()->y; } - float GetTransOffsetZ() const { return m_movementInfo.GetTransportPos()->z; } - float GetTransOffsetO() const { return m_movementInfo.GetTransportPos()->o; } - uint32 GetTransTime() const { return m_movementInfo.GetTransportTime(); } - int8 GetTransSeat() const { return m_movementInfo.GetTransportSeat(); } - - uint32 GetSaveTimer() const { return m_nextSave; } - void SetSaveTimer(uint32 timer) { m_nextSave = timer; } - - // Recall position - uint32 m_recallMap; - float m_recallX; - float m_recallY; - float m_recallZ; - float m_recallO; - void SaveRecallPosition(); - - void SetHomebindToLocation(WorldLocation const& loc, uint32 area_id); - void RelocateToHomebind() { SetLocationMapId(m_homebindMapId); Relocate(m_homebindX, m_homebindY, m_homebindZ); } - bool TeleportToHomebind(uint32 options = 0) { return TeleportTo(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ, GetOrientation(), options); } - - Object* GetObjectByTypeMask(ObjectGuid guid, TypeMask typemask); - - // currently visible objects at player client - GuidSet m_clientGUIDs; - - bool HaveAtClient(WorldObject const* u) { return u == this || m_clientGUIDs.find(u->GetObjectGuid()) != m_clientGUIDs.end(); } - - bool IsVisibleInGridForPlayer(Player* pl) const override; - bool IsVisibleGloballyFor(Player* pl) const; - - void UpdateVisibilityOf(WorldObject const* viewPoint, WorldObject* target); - - template - void UpdateVisibilityOf(WorldObject const* viewPoint, T* target, UpdateData& data, std::set& visibleNow); - - // Stealth detection system - void HandleStealthedUnitsDetection(); - - Camera& GetCamera() { return m_camera; } - - uint8 m_forced_speed_changes[MAX_MOVE_TYPE]; - - bool HasAtLoginFlag(AtLoginFlags f) const { return m_atLoginFlags & f; } - void SetAtLoginFlag(AtLoginFlags f) { m_atLoginFlags |= f; } - void RemoveAtLoginFlag(AtLoginFlags f, bool in_db_also = false); - - // Temporarily removed pet cache - uint32 GetTemporaryUnsummonedPetNumber() const { return m_temporaryUnsummonedPetNumber; } - void SetTemporaryUnsummonedPetNumber(uint32 petnumber) { m_temporaryUnsummonedPetNumber = petnumber; } - void UnsummonPetTemporaryIfAny(); - void ResummonPetTemporaryUnSummonedIfAny(); - bool IsPetNeedBeTemporaryUnsummoned() const { return !IsInWorld() || !isAlive() || IsMounted() /*+in flight*/; } - - void SendCinematicStart(uint32 CinematicSequenceId); - void SendMovieStart(uint32 MovieId); - - /*********************************************************/ - /*** INSTANCE SYSTEM ***/ - /*********************************************************/ - - typedef UNORDERED_MAP < uint32 /*mapId*/, InstancePlayerBind > BoundInstancesMap; - - void UpdateHomebindTime(uint32 time); - - uint32 m_HomebindTimer; - bool m_InstanceValid; - // permanent binds and solo binds by difficulty - BoundInstancesMap m_boundInstances[MAX_DIFFICULTY]; - InstancePlayerBind* GetBoundInstance(uint32 mapid, Difficulty difficulty); - BoundInstancesMap& GetBoundInstances(Difficulty difficulty) { return m_boundInstances[difficulty]; } - void UnbindInstance(uint32 mapid, Difficulty difficulty, bool unload = false); - void UnbindInstance(BoundInstancesMap::iterator& itr, Difficulty difficulty, bool unload = false); - InstancePlayerBind* BindToInstance(DungeonPersistentState* save, bool permanent, bool load = false); - void SendRaidInfo(); - void SendSavedInstances(); - static void ConvertInstancesToGroup(Player* player, Group* group = NULL, ObjectGuid player_guid = ObjectGuid()); - DungeonPersistentState* GetBoundInstanceSaveForSelfOrGroup(uint32 mapid); - - AreaLockStatus GetAreaTriggerLockStatus(AreaTrigger const* at, Difficulty difficulty, uint32& miscRequirement); - void SendTransferAbortedByLockStatus(MapEntry const* mapEntry, AreaLockStatus lockStatus, uint32 miscRequirement = 0); - - /*********************************************************/ - /*** GROUP SYSTEM ***/ - /*********************************************************/ - - Group* GetGroupInvite() { return m_groupInvite; } - void SetGroupInvite(Group* group) { m_groupInvite = group; } - Group* GetGroup() { return m_group.getTarget(); } - const Group* GetGroup() const { return (const Group*)m_group.getTarget(); } - GroupReference& GetGroupRef() { return m_group; } - void SetGroup(Group* group, int8 subgroup = -1); - uint8 GetSubGroup() const { return m_group.getSubGroup(); } - uint32 GetGroupUpdateFlag() const { return m_groupUpdateMask; } - void SetGroupUpdateFlag(uint32 flag) { m_groupUpdateMask |= flag; } - const uint64& GetAuraUpdateMask() const { return m_auraUpdateMask; } - void SetAuraUpdateMask(uint8 slot) { m_auraUpdateMask |= (uint64(1) << slot); } - Player* GetNextRandomRaidMember(float radius); - PartyResult CanUninviteFromGroup() const; - // BattleGround Group System - void SetBattleGroundRaid(Group* group, int8 subgroup = -1); - void RemoveFromBattleGroundRaid(); - Group* GetOriginalGroup() { return m_originalGroup.getTarget(); } - GroupReference& GetOriginalGroupRef() { return m_originalGroup; } - uint8 GetOriginalSubGroup() const { return m_originalGroup.getSubGroup(); } - void SetOriginalGroup(Group* group, int8 subgroup = -1); - - GridReference& GetGridRef() { return m_gridRef; } - MapReference& GetMapRef() { return m_mapRef; } - - bool isAllowedToLoot(Creature* creature); - - DeclinedName const* GetDeclinedNames() const { return m_declinedname; } - - // Rune functions, need check getClass() == CLASS_DEATH_KNIGHT before access - uint8 GetRunesState() const { return m_runes->runeState; } - RuneType GetBaseRune(uint8 index) const { return RuneType(m_runes->runes[index].BaseRune); } - RuneType GetCurrentRune(uint8 index) const { return RuneType(m_runes->runes[index].CurrentRune); } - uint16 GetRuneCooldown(uint8 index) const { return m_runes->runes[index].Cooldown; } - uint16 GetBaseRuneCooldown(uint8 index) const { return m_runes->runes[index].BaseCooldown; } - uint8 GetRuneCooldownFraction(uint8 index) const; - void UpdateRuneRegen(RuneType rune); - void UpdateRuneRegen(); - bool IsBaseRuneSlotsOnCooldown(RuneType runeType) const; - void ClearLastUsedRuneMask() { m_runes->lastUsedRuneMask = 0; } - uint32 GetLastUsedRuneMask() const { return m_runes->lastUsedRuneMask; } - bool IsLastUsedRune(uint8 index) const { return m_runes->lastUsedRuneMask & (1 << index); } - void SetLastUsedRune(RuneType type) { m_runes->lastUsedRuneMask |= 1 << uint32(type); } - void SetBaseRune(uint8 index, RuneType baseRune) { m_runes->runes[index].BaseRune = baseRune; } - void SetCurrentRune(uint8 index, RuneType currentRune) { m_runes->runes[index].CurrentRune = currentRune; } - void SetRuneCooldown(uint8 index, uint16 cooldown) { m_runes->runes[index].Cooldown = cooldown; m_runes->SetRuneState(index, (cooldown == 0) ? true : false); } - void SetBaseRuneCooldown(uint8 index, uint16 cooldown) { m_runes->runes[index].BaseCooldown = cooldown; } - void SetRuneConvertAura(uint8 index, Aura const* aura) { m_runes->runes[index].ConvertAura = aura; } - void AddRuneByAuraEffect(uint8 index, RuneType newType, Aura const* aura); - void RemoveRunesByAuraEffect(Aura const* aura); - void RestoreBaseRune(uint8 index); - void ConvertRune(uint8 index, RuneType newType); - void ResyncRunes(); - void AddRunePower(uint8 index); - void InitRunes(); - - AchievementMgr const& GetAchievementMgr() const { return m_achievementMgr; } - AchievementMgr& GetAchievementMgr() { return m_achievementMgr; } - void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1 = 0, uint32 miscvalue2 = 0, Unit* unit = NULL, uint32 time = 0); - void StartTimedAchievementCriteria(AchievementCriteriaTypes type, uint32 timedRequirementId, time_t startTime = 0); - - bool HasTitle(uint32 bitIndex) const; - bool HasTitle(CharTitlesEntry const* title) const { return HasTitle(title->bit_index); } - void SetTitle(CharTitlesEntry const* title, bool lost = false); - - bool canSeeSpellClickOn(Creature const* creature) const; - protected: - - uint32 m_contestedPvPTimer; - - /*********************************************************/ - /*** BATTLEGROUND SYSTEM ***/ - /*********************************************************/ - - /* - this is an array of BG queues (BgTypeIDs) in which is player - */ - struct BgBattleGroundQueueID_Rec - { - BattleGroundQueueTypeId bgQueueTypeId; - uint32 invitedToInstance; - }; - - BgBattleGroundQueueID_Rec m_bgBattleGroundQueueID[PLAYER_MAX_BATTLEGROUND_QUEUES]; - BGData m_bgData; - - /*********************************************************/ - /*** QUEST SYSTEM ***/ - /*********************************************************/ - - // We allow only one timed quest active at the same time. Below can then be simple value instead of set. - typedef std::set QuestSet; - QuestSet m_timedquests; - QuestSet m_weeklyquests; - QuestSet m_monthlyquests; - - ObjectGuid m_dividerGuid; - uint32 m_ingametime; - - /*********************************************************/ - /*** LOAD SYSTEM ***/ - /*********************************************************/ - - void _LoadActions(QueryResult* result); - void _LoadAuras(QueryResult* result, uint32 timediff); - void _LoadBoundInstances(QueryResult* result); - void _LoadInventory(QueryResult* result, uint32 timediff); - void _LoadItemLoot(QueryResult* result); - void _LoadMails(QueryResult* result); - void _LoadMailedItems(QueryResult* result); - void _LoadQuestStatus(QueryResult* result); - void _LoadDailyQuestStatus(QueryResult* result); - void _LoadWeeklyQuestStatus(QueryResult* result); - void _LoadMonthlyQuestStatus(QueryResult* result); - void _LoadGroup(QueryResult* result); - void _LoadSkills(QueryResult* result); - void _LoadSpells(QueryResult* result); - void _LoadTalents(QueryResult* result); - void _LoadFriendList(QueryResult* result); - bool _LoadHomeBind(QueryResult* result); - void _LoadDeclinedNames(QueryResult* result); - void _LoadArenaTeamInfo(QueryResult* result); - void _LoadEquipmentSets(QueryResult* result); - void _LoadBGData(QueryResult* result); - void _LoadGlyphs(QueryResult* result); - void _LoadIntoDataField(const char* data, uint32 startOffset, uint32 count); - - /*********************************************************/ - /*** SAVE SYSTEM ***/ - /*********************************************************/ - - void _SaveActions(); - void _SaveAuras(); - void _SaveInventory(); - void _SaveMail(); - void _SaveQuestStatus(); - void _SaveDailyQuestStatus(); - void _SaveWeeklyQuestStatus(); - void _SaveMonthlyQuestStatus(); - void _SaveSkills(); - void _SaveSpells(); - void _SaveEquipmentSets(); - void _SaveBGData(); - void _SaveGlyphs(); - void _SaveTalents(); - void _SaveStats(); - - void _SetCreateBits(UpdateMask* updateMask, Player* target) const override; - void _SetUpdateBits(UpdateMask* updateMask, Player* target) const override; - - /*********************************************************/ - /*** ENVIRONMENTAL SYSTEM ***/ - /*********************************************************/ - void HandleSobering(); - void SendMirrorTimer(MirrorTimerType Type, uint32 MaxValue, uint32 CurrentValue, int32 Regen); - void StopMirrorTimer(MirrorTimerType Type); - void HandleDrowning(uint32 time_diff); - int32 getMaxTimer(MirrorTimerType timer); - - /*********************************************************/ - /*** CURRENCY SYSTEM ***/ - /*********************************************************/ - PlayerCurrenciesMap m_currencies; - uint32 GetCurrencyWeekCap(CurrencyTypesEntry const * currency) const; - uint32 GetCurrencyTotalCap(CurrencyTypesEntry const * currency) const; - void _LoadCurrencies(QueryResult* result); - void _SaveCurrencies(); - - void outDebugStatsValues() const; - ObjectGuid m_lootGuid; - - Team m_team; - uint32 m_nextSave; - time_t m_speakTime; - uint32 m_speakCount; - Difficulty m_dungeonDifficulty; - Difficulty m_raidDifficulty; - time_t m_lastHonorKillsUpdateTime; - - uint32 m_atLoginFlags; - - Item* m_items[PLAYER_SLOTS_COUNT]; - uint32 m_currentBuybackSlot; - - std::vector m_itemUpdateQueue; - bool m_itemUpdateQueueBlocked; - - uint32 m_ExtraFlags; - ObjectGuid m_curSelectionGuid; - - ObjectGuid m_comboTargetGuid; - int8 m_comboPoints; - - QuestStatusMap mQuestStatus; - - SkillStatusMap mSkillStatus; - - uint32 m_GuildIdInvited; - ObjectGuid m_GuildInviterGuid; - uint32 m_ArenaTeamIdInvited; - - PlayerMails m_mail; - PlayerSpellMap m_spells; - PlayerTalentMap m_talents[MAX_TALENT_SPEC_COUNT]; - uint32 m_talentsPrimaryTree[MAX_TALENT_SPEC_COUNT]; - SpellCooldowns m_spellCooldowns; - uint32 m_lastPotionId; // last used health/mana potion in combat, that block next potion use - - GlobalCooldownMgr m_GlobalCooldownMgr; - - uint8 m_activeSpec; - uint8 m_specsCount; - - ActionButtonList m_actionButtons[MAX_TALENT_SPEC_COUNT]; - - Glyph m_glyphs[MAX_TALENT_SPEC_COUNT][MAX_GLYPH_SLOT_INDEX]; - - float m_auraBaseMod[BASEMOD_END][MOD_END]; - int16 m_baseRatingValue[MAX_COMBAT_RATING]; - uint16 m_baseSpellPower; - uint16 m_baseManaRegen; - uint32 m_baseHealthRegen; - float m_armorPenetrationPct; - int32 m_spellPenetrationItemMod; - - AuraList m_spellMods[MAX_SPELLMOD]; - EnchantDurationList m_enchantDuration; - ItemDurationList m_itemDuration; - - ObjectGuid m_resurrectGuid; - uint32 m_resurrectMap; - float m_resurrectX, m_resurrectY, m_resurrectZ; - uint32 m_resurrectHealth, m_resurrectMana; - - WorldSession* m_session; - - typedef std::list JoinedChannelsList; - JoinedChannelsList m_channels; - - uint32 m_cinematic; - - TradeData* m_trade; - - bool m_DailyQuestChanged; - bool m_WeeklyQuestChanged; - bool m_MonthlyQuestChanged; - - uint32 m_drunkTimer; - uint32 m_weaponChangeTimer; - - uint32 m_zoneUpdateId; - uint32 m_zoneUpdateTimer; - uint32 m_areaUpdateId; - uint32 m_positionStatusUpdateTimer; - - uint32 m_deathTimer; - time_t m_deathExpireTime; - - uint32 m_restTime; - - uint32 m_WeaponProficiency; - uint32 m_ArmorProficiency; - bool m_canParry; - bool m_canBlock; - bool m_canDualWield; - bool m_canTitanGrip; - uint8 m_swingErrorMsg; - float m_ammoDPS; - - //////////////////// Rest SystemPlayerSpell - time_t time_inn_enter; - uint32 inn_trigger_id; - float m_rest_bonus; - RestType rest_type; - //////////////////// Rest System///////////////////// - - // Transports - Transport* m_transport; - - uint32 m_freeTalentPoints; - uint32 m_resetTalentsCost; - time_t m_resetTalentsTime; - uint32 m_usedTalentCount; - uint32 m_questRewardTalentCount; - - // Social - PlayerSocial *m_social; - - // Groups - GroupReference m_group; - GroupReference m_originalGroup; - Group* m_groupInvite; - uint32 m_groupUpdateMask; - uint64 m_auraUpdateMask; - - // Player summoning - time_t m_summon_expire; - uint32 m_summon_mapid; - float m_summon_x; - float m_summon_y; - float m_summon_z; - - DeclinedName* m_declinedname; - Runes* m_runes; - EquipmentSets m_EquipmentSets; - uint8 m_slot; - - /// class dependent melee diminishing constant for dodge/parry/missed chances - static const float m_diminishing_k[MAX_CLASSES]; - - private: - void _HandleDeadlyPoison(Unit* Target, WeaponAttackType attType, SpellEntry const* spellInfo); - // internal common parts for CanStore/StoreItem functions - InventoryResult _CanStoreItem_InSpecificSlot(uint8 bag, uint8 slot, ItemPosCountVec& dest, ItemPrototype const* pProto, uint32& count, bool swap, Item* pSrcItem) const; - InventoryResult _CanStoreItem_InBag(uint8 bag, ItemPosCountVec& dest, ItemPrototype const* pProto, uint32& count, bool merge, bool non_specialized, Item* pSrcItem, uint8 skip_bag, uint8 skip_slot) const; - InventoryResult _CanStoreItem_InInventorySlots(uint8 slot_begin, uint8 slot_end, ItemPosCountVec& dest, ItemPrototype const* pProto, uint32& count, bool merge, Item* pSrcItem, uint8 skip_bag, uint8 skip_slot) const; - Item* _StoreItem(uint16 pos, Item* pItem, uint32 count, bool clone, bool update); - - void AdjustQuestReqItemCount(Quest const* pQuest, QuestStatusData& questStatusData); - - void SetCanDelayTeleport(bool setting) { m_bCanDelayTeleport = setting; } - bool IsHasDelayedTeleport() const - { - // we should not execute delayed teleports for now dead players but has been alive at teleport - // because we don't want player's ghost teleported from graveyard - return m_bHasDelayedTeleport && (isAlive() || !m_bHasBeenAliveAtDelayedTeleport); - } - - bool SetDelayedTeleportFlagIfCan() - { - m_bHasDelayedTeleport = m_bCanDelayTeleport; - m_bHasBeenAliveAtDelayedTeleport = isAlive(); - return m_bHasDelayedTeleport; - } - - void ScheduleDelayedOperation(uint32 operation) - { - if (operation < DELAYED_END) - m_DelayedOperations |= operation; - } - - void _fillGearScoreData(Item* item, GearScoreVec* gearScore, uint32& twoHandScore); - - Unit* m_mover; - Camera m_camera; - - GridReference m_gridRef; - MapReference m_mapRef; - - // Homebind coordinates - uint32 m_homebindMapId; - uint16 m_homebindAreaId; - float m_homebindX; - float m_homebindY; - float m_homebindZ; - - uint32 m_lastFallTime; - float m_lastFallZ; - - LiquidTypeEntry const* m_lastLiquid; - - int32 m_MirrorTimer[MAX_TIMERS]; - uint8 m_MirrorTimerFlags; - uint8 m_MirrorTimerFlagsLast; - bool m_isInWater; - - // Current teleport data - WorldLocation m_teleport_dest; - uint32 m_teleport_options; - bool mSemaphoreTeleport_Near; - bool mSemaphoreTeleport_Far; - - uint32 m_DelayedOperations; - bool m_bCanDelayTeleport; - bool m_bHasDelayedTeleport; - bool m_bHasBeenAliveAtDelayedTeleport; - - uint32 m_DetectInvTimer; - - // Temporary removed pet cache - uint32 m_temporaryUnsummonedPetNumber; - - AchievementMgr m_achievementMgr; - ReputationMgr m_reputationMgr; - - uint32 m_timeSyncCounter; - uint32 m_timeSyncTimer; - uint32 m_timeSyncClient; - uint32 m_timeSyncServer; - - uint32 m_cachedGS; - - PhaseMgr* phaseMgr; -}; - -void AddItemsSetItem(Player* player, Item* item); -void RemoveItemsSetItem(Player* player, ItemPrototype const* proto); - -#endif diff --git a/src/game/ScriptMgr.cpp b/src/game/ScriptMgr.cpp deleted file mode 100644 index 840d77256..000000000 --- a/src/game/ScriptMgr.cpp +++ /dev/null @@ -1,2365 +0,0 @@ -/** - * This code is part of MaNGOS. Contributor & Copyright details are in AUTHORS/THANKS. - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "ScriptMgr.h" -#include "Policies/Singleton.h" -#include "Log.h" -#include "ProgressBar.h" -#include "ObjectMgr.h" -#include "WaypointManager.h" -#include "World.h" -#include "GridNotifiers.h" -#include "GridNotifiersImpl.h" -#include "Cell.h" -#include "CellImpl.h" -#include "SQLStorages.h" -#include "BattleGround/BattleGround.h" -#include "OutdoorPvP/OutdoorPvP.h" -#include "WaypointMovementGenerator.h" - -#include "revision_nr.h" - -ScriptMapMapName sQuestEndScripts; -ScriptMapMapName sQuestStartScripts; -ScriptMapMapName sSpellScripts; -ScriptMapMapName sGameObjectScripts; -ScriptMapMapName sGameObjectTemplateScripts; -ScriptMapMapName sEventScripts; -ScriptMapMapName sGossipScripts; -ScriptMapMapName sCreatureDeathScripts; -ScriptMapMapName sCreatureMovementScripts; - -INSTANTIATE_SINGLETON_1(ScriptMgr); - -ScriptMgr::ScriptMgr() : - m_hScriptLib(NULL), - m_scheduledScripts(0), - - m_pOnInitScriptLibrary(NULL), - m_pOnFreeScriptLibrary(NULL), - m_pGetScriptLibraryVersion(NULL), - - m_pGetCreatureAI(NULL), - m_pCreateInstanceData(NULL), - - m_pOnGossipHello(NULL), - m_pOnGOGossipHello(NULL), - m_pOnGossipSelect(NULL), - m_pOnGOGossipSelect(NULL), - m_pOnGossipSelectWithCode(NULL), - m_pOnGOGossipSelectWithCode(NULL), - m_pOnQuestAccept(NULL), - m_pOnGOQuestAccept(NULL), - m_pOnItemQuestAccept(NULL), - m_pOnQuestRewarded(NULL), - m_pOnGOQuestRewarded(NULL), - m_pGetNPCDialogStatus(NULL), - m_pGetGODialogStatus(NULL), - m_pOnGOUse(NULL), - m_pOnItemUse(NULL), - m_pOnAreaTrigger(NULL), - m_pOnProcessEvent(NULL), - m_pOnEffectDummyCreature(NULL), - m_pOnEffectDummyGO(NULL), - m_pOnEffectDummyItem(NULL), - m_pOnEffectScriptEffectCreature(NULL), - m_pOnAuraDummy(NULL) -{ -} - -ScriptMgr::~ScriptMgr() -{ - UnloadScriptLibrary(); -} - -// ///////////////////////////////////////////////////////// -// DB SCRIPTS (loaders of static data) -// ///////////////////////////////////////////////////////// -// returns priority (0 == cannot start script) -uint8 GetSpellStartDBScriptPriority(SpellEntry const* spellinfo, SpellEffectIndex effIdx) -{ - SpellEffectEntry const* spellEffect = spellinfo->GetSpellEffect(effIdx); - if (!spellEffect) - return 0; - - if (spellEffect->Effect == SPELL_EFFECT_SCRIPT_EFFECT) - return 10; - - if (spellEffect->Effect == SPELL_EFFECT_DUMMY) - return 9; - - // NonExisting triggered spells can also start DB-Spell-Scripts - if (spellEffect->Effect == SPELL_EFFECT_TRIGGER_SPELL && !sSpellStore.LookupEntry(spellEffect->EffectTriggerSpell)) - return 5; - - // Can not start script - return 0; -} - -// Priorize: SCRIPT_EFFECT before DUMMY before Non-Existing triggered spell, for same priority the first effect with the priority triggers -bool ScriptMgr::CanSpellEffectStartDBScript(SpellEntry const* spellinfo, SpellEffectIndex effIdx) -{ - uint8 priority = GetSpellStartDBScriptPriority(spellinfo, effIdx); - if (!priority) - return false; - - for (int i = 0; i < MAX_EFFECT_INDEX; ++i) - { - uint8 currentPriority = GetSpellStartDBScriptPriority(spellinfo, SpellEffectIndex(i)); - if (currentPriority < priority) // lower priority, continue checking - continue; - if (currentPriority > priority) // take other index with higher priority - return false; - if (i < effIdx) // same priority at lower index - return false; - } - - return true; -} - -void ScriptMgr::LoadScripts(ScriptMapMapName& scripts, const char* tablename) -{ - if (IsScriptScheduled()) // function don't must be called in time scripts use. - return; - - sLog.outString("%s :", tablename); - - scripts.first = tablename; - scripts.second.clear(); // need for reload support - - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - QueryResult* result = WorldDatabase.PQuery("SELECT id, delay, command, datalong, datalong2, buddy_entry, search_radius, data_flags, dataint, dataint2, dataint3, dataint4, x, y, z, o FROM %s", tablename); - - uint32 count = 0; - - if (!result) - { - BarGoLink bar(1); - bar.step(); - - sLog.outString(); - sLog.outString(">> Loaded %u script definitions", count); - return; - } - - BarGoLink bar(result->GetRowCount()); - - do - { - bar.step(); - - Field* fields = result->Fetch(); - - ScriptInfo tmp; - tmp.id = fields[0].GetUInt32(); - tmp.delay = fields[1].GetUInt32(); - tmp.command = fields[2].GetUInt32(); - tmp.raw.data[0] = fields[3].GetUInt32(); - tmp.raw.data[1] = fields[4].GetUInt32(); - tmp.buddyEntry = fields[5].GetUInt32(); - tmp.searchRadiusOrGuid = fields[6].GetUInt32(); - tmp.data_flags = fields[7].GetUInt8(); - tmp.textId[0] = fields[8].GetInt32(); - tmp.textId[1] = fields[9].GetInt32(); - tmp.textId[2] = fields[10].GetInt32(); - tmp.textId[3] = fields[11].GetInt32(); - tmp.x = fields[12].GetFloat(); - tmp.y = fields[13].GetFloat(); - tmp.z = fields[14].GetFloat(); - tmp.o = fields[15].GetFloat(); - - // generic command args check - if (tmp.buddyEntry && !(tmp.data_flags & SCRIPT_FLAG_BUDDY_BY_GUID)) - { - if (tmp.IsCreatureBuddy() && !ObjectMgr::GetCreatureTemplate(tmp.buddyEntry)) - { - sLog.outErrorDb("Table `%s` has buddyEntry = %u in command %u for script id %u, but this creature_template does not exist, skipping.", tablename, tmp.buddyEntry, tmp.command, tmp.id); - continue; - } - else if (!tmp.IsCreatureBuddy() && !ObjectMgr::GetGameObjectInfo(tmp.buddyEntry)) - { - sLog.outErrorDb("Table `%s` has buddyEntry = %u in command %u for script id %u, but this gameobject_template does not exist, skipping.", tablename, tmp.buddyEntry, tmp.command, tmp.id); - continue; - } - if (!tmp.searchRadiusOrGuid) - { - sLog.outErrorDb("Table `%s` has searchRadius = 0 in command %u for script id %u for buddy %u, skipping.", tablename, tmp.command, tmp.id, tmp.buddyEntry); - continue; - } - } - - if (tmp.data_flags) // Check flags - { - if (tmp.data_flags & ~MAX_SCRIPT_FLAG_VALID) - { - sLog.outErrorDb("Table `%s` has invalid data_flags %u in command %u for script id %u, skipping.", tablename, tmp.data_flags, tmp.command, tmp.id); - continue; - } - if (!tmp.HasAdditionalScriptFlag() && tmp.data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) - { - sLog.outErrorDb("Table `%s` has invalid data_flags %u in command %u for script id %u, skipping.", tablename, tmp.data_flags, tmp.command, tmp.id); - continue; - } - if (tmp.data_flags & SCRIPT_FLAG_BUDDY_AS_TARGET && ! tmp.buddyEntry) - { - sLog.outErrorDb("Table `%s` has buddy required in data_flags %u in command %u for script id %u, but no buddy defined, skipping.", tablename, tmp.data_flags, tmp.command, tmp.id); - continue; - } - if (tmp.data_flags & SCRIPT_FLAG_BUDDY_BY_GUID) // Check guid - { - if (tmp.IsCreatureBuddy()) - { - CreatureData const* data = sObjectMgr.GetCreatureData(tmp.searchRadiusOrGuid); - if (!data) - { - sLog.outErrorDb("Table `%s` has buddy defined by guid (SCRIPT_FLAG_BUDDY_BY_GUID %u set) but no npc spawned with guid %u, skipping.", tablename, SCRIPT_FLAG_BUDDY_BY_GUID, tmp.searchRadiusOrGuid); - continue; - } - if (data->id != tmp.buddyEntry) - { - sLog.outErrorDb("Table `%s` has buddy defined by guid (SCRIPT_FLAG_BUDDY_BY_GUID %u set) but spawned npc with guid %u has entry %u, expected buddy_entry is %u, skipping.", tablename, SCRIPT_FLAG_BUDDY_BY_GUID, tmp.searchRadiusOrGuid, data->id, tmp.buddyEntry); - continue; - } - } - else - { - GameObjectData const* data = sObjectMgr.GetGOData(tmp.searchRadiusOrGuid); - if (!data) - { - sLog.outErrorDb("Table `%s` has go-buddy defined by guid (SCRIPT_FLAG_BUDDY_BY_GUID %u set) but no go spawned with guid %u, skipping.", tablename, SCRIPT_FLAG_BUDDY_BY_GUID, tmp.searchRadiusOrGuid); - continue; - } - if (data->id != tmp.buddyEntry) - { - sLog.outErrorDb("Table `%s` has go-buddy defined by guid (SCRIPT_FLAG_BUDDY_BY_GUID %u set) but spawned go with guid %u has entry %u, expected buddy_entry is %u, skipping.", tablename, SCRIPT_FLAG_BUDDY_BY_GUID, tmp.searchRadiusOrGuid, data->id, tmp.buddyEntry); - continue; - } - } - } - } - - switch (tmp.command) - { - case SCRIPT_COMMAND_TALK: // 0 - { - if (tmp.textId[0] == 0) - { - sLog.outErrorDb("Table `%s` has invalid talk text id (dataint = %i) in SCRIPT_COMMAND_TALK for script id %u", tablename, tmp.textId[0], tmp.id); - continue; - } - - for (int i = 0; i < MAX_TEXT_ID; ++i) - { - if (tmp.textId[i] && (tmp.textId[i] < MIN_DB_SCRIPT_STRING_ID || tmp.textId[i] >= MAX_DB_SCRIPT_STRING_ID)) - { - sLog.outErrorDb("Table `%s` has out of range text id (dataint = %i expected %u-%u) in SCRIPT_COMMAND_TALK for script id %u", tablename, tmp.textId[i], MIN_DB_SCRIPT_STRING_ID, MAX_DB_SCRIPT_STRING_ID, tmp.id); - continue; - } - } - - // if (!GetMangosStringLocale(tmp.dataint)) will be checked after db_script_string loading - break; - } - case SCRIPT_COMMAND_EMOTE: // 1 - { - if (!sEmotesStore.LookupEntry(tmp.emote.emoteId)) - { - sLog.outErrorDb("Table `%s` has invalid emote id (datalong = %u) in SCRIPT_COMMAND_EMOTE for script id %u", tablename, tmp.emote.emoteId, tmp.id); - continue; - } - break; - } - case SCRIPT_COMMAND_FIELD_SET: // 2 - case SCRIPT_COMMAND_MOVE_TO: // 3 - case SCRIPT_COMMAND_FLAG_SET: // 4 - case SCRIPT_COMMAND_FLAG_REMOVE: // 5 - break; - case SCRIPT_COMMAND_TELEPORT_TO: // 6 - { - if (!sMapStore.LookupEntry(tmp.teleportTo.mapId)) - { - sLog.outErrorDb("Table `%s` has invalid map (Id: %u) in SCRIPT_COMMAND_TELEPORT_TO for script id %u", tablename, tmp.teleportTo.mapId, tmp.id); - continue; - } - - if (!MaNGOS::IsValidMapCoord(tmp.x, tmp.y, tmp.z, tmp.o)) - { - sLog.outErrorDb("Table `%s` has invalid coordinates (X: %f Y: %f) in SCRIPT_COMMAND_TELEPORT_TO for script id %u", tablename, tmp.x, tmp.y, tmp.id); - continue; - } - break; - } - case SCRIPT_COMMAND_QUEST_EXPLORED: // 7 - { - Quest const* quest = sObjectMgr.GetQuestTemplate(tmp.questExplored.questId); - if (!quest) - { - sLog.outErrorDb("Table `%s` has invalid quest (ID: %u) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u", tablename, tmp.questExplored.questId, tmp.id); - continue; - } - - if (!quest->HasSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT)) - { - sLog.outErrorDb("Table `%s` has quest (ID: %u) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, but quest not have flag QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT in quest flags. Script command or quest flags wrong. Quest modified to require objective.", tablename, tmp.questExplored.questId, tmp.id); - - // this will prevent quest completing without objective - const_cast(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT); - - // continue; - quest objective requirement set and command can be allowed - } - - if (float(tmp.questExplored.distance) > DEFAULT_VISIBILITY_DISTANCE) - { - sLog.outErrorDb("Table `%s` has too large distance (%u) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u", - tablename, tmp.questExplored.distance, tmp.id); - continue; - } - - if (tmp.questExplored.distance && float(tmp.questExplored.distance) > DEFAULT_VISIBILITY_DISTANCE) - { - sLog.outErrorDb("Table `%s` has too large distance (%u) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, max distance is %f or 0 for disable distance check", - tablename, tmp.questExplored.distance, tmp.id, DEFAULT_VISIBILITY_DISTANCE); - continue; - } - - if (tmp.questExplored.distance && float(tmp.questExplored.distance) < INTERACTION_DISTANCE) - { - sLog.outErrorDb("Table `%s` has too small distance (%u) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, min distance is %f or 0 for disable distance check", - tablename, tmp.questExplored.distance, tmp.id, INTERACTION_DISTANCE); - continue; - } - - break; - } - case SCRIPT_COMMAND_KILL_CREDIT: // 8 - { - if (tmp.killCredit.creatureEntry && !ObjectMgr::GetCreatureTemplate(tmp.killCredit.creatureEntry)) - { - sLog.outErrorDb("Table `%s` has invalid creature (Entry: %u) in SCRIPT_COMMAND_KILL_CREDIT for script id %u", tablename, tmp.killCredit.creatureEntry, tmp.id); - continue; - } - break; - } - case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT: // 9 - { - uint32 goEntry = 0; - - if (!tmp.GetGOGuid()) - { - if (!tmp.buddyEntry) - { - sLog.outErrorDb("Table `%s` has no gameobject nor buddy defined in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u", tablename, tmp.id); - continue; - } - goEntry = tmp.buddyEntry; - } - else - { - GameObjectData const* data = sObjectMgr.GetGOData(tmp.GetGOGuid()); - if (!data) - { - sLog.outErrorDb("Table `%s` has invalid gameobject (GUID: %u) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u", tablename, tmp.GetGOGuid(), tmp.id); - continue; - } - goEntry = data->id; - } - - GameObjectInfo const* info = ObjectMgr::GetGameObjectInfo(goEntry); - if (!info) - { - sLog.outErrorDb("Table `%s` has gameobject with invalid entry (GUID: %u Entry: %u) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u", tablename, tmp.GetGOGuid(), goEntry, tmp.id); - continue; - } - - if (info->type == GAMEOBJECT_TYPE_FISHINGNODE || - info->type == GAMEOBJECT_TYPE_FISHINGHOLE || - info->type == GAMEOBJECT_TYPE_DOOR || - info->type == GAMEOBJECT_TYPE_BUTTON || - info->type == GAMEOBJECT_TYPE_TRAP) - { - sLog.outErrorDb("Table `%s` have gameobject type (%u) unsupported by command SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u", tablename, info->type, tmp.id); - continue; - } - break; - } - case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE: // 10 - { - if (!MaNGOS::IsValidMapCoord(tmp.x, tmp.y, tmp.z, tmp.o)) - { - sLog.outErrorDb("Table `%s` has invalid coordinates (X: %f Y: %f) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id %u", tablename, tmp.x, tmp.y, tmp.id); - continue; - } - - if (!ObjectMgr::GetCreatureTemplate(tmp.summonCreature.creatureEntry)) - { - sLog.outErrorDb("Table `%s` has invalid creature (Entry: %u) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id %u", tablename, tmp.summonCreature.creatureEntry, tmp.id); - continue; - } - break; - } - case SCRIPT_COMMAND_OPEN_DOOR: // 11 - case SCRIPT_COMMAND_CLOSE_DOOR: // 12 - { - uint32 goEntry = 0; - - if (!tmp.GetGOGuid()) - { - if (!tmp.buddyEntry) - { - sLog.outErrorDb("Table `%s` has no gameobject nor buddy defined in %s for script id %u", tablename, (tmp.command == SCRIPT_COMMAND_OPEN_DOOR ? "SCRIPT_COMMAND_OPEN_DOOR" : "SCRIPT_COMMAND_CLOSE_DOOR"), tmp.id); - continue; - } - goEntry = tmp.buddyEntry; - } - else - { - GameObjectData const* data = sObjectMgr.GetGOData(tmp.GetGOGuid()); - if (!data) - { - sLog.outErrorDb("Table `%s` has invalid gameobject (GUID: %u) in %s for script id %u", tablename, tmp.GetGOGuid(), (tmp.command == SCRIPT_COMMAND_OPEN_DOOR ? "SCRIPT_COMMAND_OPEN_DOOR" : "SCRIPT_COMMAND_CLOSE_DOOR"), tmp.id); - continue; - } - goEntry = data->id; - } - - GameObjectInfo const* info = ObjectMgr::GetGameObjectInfo(goEntry); - if (!info) - { - sLog.outErrorDb("Table `%s` has gameobject with invalid entry (GUID: %u Entry: %u) in %s for script id %u", tablename, tmp.GetGOGuid(), goEntry, (tmp.command == SCRIPT_COMMAND_OPEN_DOOR ? "SCRIPT_COMMAND_OPEN_DOOR" : "SCRIPT_COMMAND_CLOSE_DOOR"), tmp.id); - continue; - } - - if (info->type != GAMEOBJECT_TYPE_DOOR) - { - sLog.outErrorDb("Table `%s` has gameobject type (%u) non supported by command %s for script id %u", tablename, info->id, (tmp.command == SCRIPT_COMMAND_OPEN_DOOR ? "SCRIPT_COMMAND_OPEN_DOOR" : "SCRIPT_COMMAND_CLOSE_DOOR"), tmp.id); - continue; - } - - break; - } - case SCRIPT_COMMAND_ACTIVATE_OBJECT: // 13 - break; - case SCRIPT_COMMAND_REMOVE_AURA: // 14 - { - if (!sSpellStore.LookupEntry(tmp.removeAura.spellId)) - { - sLog.outErrorDb("Table `%s` using nonexistent spell (id: %u) in SCRIPT_COMMAND_REMOVE_AURA or SCRIPT_COMMAND_CAST_SPELL for script id %u", - tablename, tmp.removeAura.spellId, tmp.id); - continue; - } - break; - } - case SCRIPT_COMMAND_CAST_SPELL: // 15 - { - if (!sSpellStore.LookupEntry(tmp.castSpell.spellId)) - { - sLog.outErrorDb("Table `%s` using nonexistent spell (id: %u) in SCRIPT_COMMAND_REMOVE_AURA or SCRIPT_COMMAND_CAST_SPELL for script id %u", - tablename, tmp.castSpell.spellId, tmp.id); - continue; - } - break; - } - case SCRIPT_COMMAND_PLAY_SOUND: // 16 - { - if (!sSoundEntriesStore.LookupEntry(tmp.playSound.soundId)) - { - sLog.outErrorDb("Table `%s` using nonexistent sound (id: %u) in SCRIPT_COMMAND_PLAY_SOUND for script id %u", - tablename, tmp.playSound.soundId, tmp.id); - continue; - } - // bitmask: 0/1=target-player, 0/2=with distance dependent, 0/4=map wide, 0/8=zone wide - if (tmp.playSound.flags & ~(1 | 2 | 4 | 8)) - sLog.outErrorDb("Table `%s` using unsupported sound flags (datalong2: %u) in SCRIPT_COMMAND_PLAY_SOUND for script id %u, unsupported flags will be ignored", tablename, tmp.playSound.flags, tmp.id); - if ((tmp.playSound.flags & (1 | 2)) > 0 && (tmp.playSound.flags & (4 | 8)) > 0) - sLog.outErrorDb("Table `%s` uses sound flags (datalong2: %u) in SCRIPT_COMMAND_PLAY_SOUND for script id %u, combining (1|2) with (4|8) makes no sense", tablename, tmp.playSound.flags, tmp.id); - break; - } - case SCRIPT_COMMAND_CREATE_ITEM: // 17 - { - if (!ObjectMgr::GetItemPrototype(tmp.createItem.itemEntry)) - { - sLog.outErrorDb("Table `%s` has nonexistent item (entry: %u) in SCRIPT_COMMAND_CREATE_ITEM for script id %u", - tablename, tmp.createItem.itemEntry, tmp.id); - continue; - } - if (!tmp.createItem.amount) - { - sLog.outErrorDb("Table `%s` SCRIPT_COMMAND_CREATE_ITEM but amount is %u for script id %u", - tablename, tmp.createItem.amount, tmp.id); - continue; - } - break; - } - case SCRIPT_COMMAND_DESPAWN_SELF: // 18 - { - // for later, we might consider despawn by database guid, and define in datalong2 as option to despawn self. - break; - } - case SCRIPT_COMMAND_PLAY_MOVIE: // 19 - { - if (!sMovieStore.LookupEntry(tmp.playMovie.movieId)) - { - sLog.outErrorDb("Table `%s` use non-existing movie_id (id: %u) in SCRIPT_COMMAND_PLAY_MOVIE for script id %u", - tablename, tmp.playMovie.movieId, tmp.id); - continue; - } - break; - } - case SCRIPT_COMMAND_MOVEMENT: // 20 - { - if (tmp.movement.movementType >= MAX_DB_MOTION_TYPE) - { - sLog.outErrorDb("Table `%s` SCRIPT_COMMAND_MOVEMENT has invalid MovementType %u for script id %u", - tablename, tmp.movement.movementType, tmp.id); - continue; - } - - break; - } - case SCRIPT_COMMAND_SET_ACTIVEOBJECT: // 21 - break; - case SCRIPT_COMMAND_SET_FACTION: // 22 - { - if (tmp.faction.factionId && !sFactionTemplateStore.LookupEntry(tmp.faction.factionId)) - { - sLog.outErrorDb("Table `%s` has datalong = %u in SCRIPT_COMMAND_SET_FACTION for script id %u, but this faction-template does not exist.", tablename, tmp.faction.factionId, tmp.id); - continue; - } - - break; - } - case SCRIPT_COMMAND_MORPH_TO_ENTRY_OR_MODEL: // 23 - { - if (tmp.data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) - { - if (tmp.morph.creatureOrModelEntry && !sCreatureDisplayInfoStore.LookupEntry(tmp.morph.creatureOrModelEntry)) - { - sLog.outErrorDb("Table `%s` has datalong2 = %u in SCRIPT_COMMAND_MORPH_TO_ENTRY_OR_MODEL for script id %u, but this model does not exist.", tablename, tmp.morph.creatureOrModelEntry, tmp.id); - continue; - } - } - else - { - if (tmp.morph.creatureOrModelEntry && !ObjectMgr::GetCreatureTemplate(tmp.morph.creatureOrModelEntry)) - { - sLog.outErrorDb("Table `%s` has datalong2 = %u in SCRIPT_COMMAND_MORPH_TO_ENTRY_OR_MODEL for script id %u, but this creature_template does not exist.", tablename, tmp.morph.creatureOrModelEntry, tmp.id); - continue; - } - } - - break; - } - case SCRIPT_COMMAND_MOUNT_TO_ENTRY_OR_MODEL: // 24 - { - if (tmp.data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) - { - if (tmp.mount.creatureOrModelEntry && !sCreatureDisplayInfoStore.LookupEntry(tmp.mount.creatureOrModelEntry)) - { - sLog.outErrorDb("Table `%s` has datalong2 = %u in SCRIPT_COMMAND_MOUNT_TO_ENTRY_OR_MODEL for script id %u, but this model does not exist.", tablename, tmp.mount.creatureOrModelEntry, tmp.id); - continue; - } - } - else - { - if (tmp.mount.creatureOrModelEntry && !ObjectMgr::GetCreatureTemplate(tmp.mount.creatureOrModelEntry)) - { - sLog.outErrorDb("Table `%s` has datalong2 = %u in SCRIPT_COMMAND_MOUNT_TO_ENTRY_OR_MODEL for script id %u, but this creature_template does not exist.", tablename, tmp.mount.creatureOrModelEntry, tmp.id); - continue; - } - } - - break; - } - case SCRIPT_COMMAND_SET_RUN: // 25 - case SCRIPT_COMMAND_ATTACK_START: // 26 - break; - case SCRIPT_COMMAND_GO_LOCK_STATE: // 27 - { - if (// lock(0x01) and unlock(0x02) together - ((tmp.goLockState.lockState & 0x01) && (tmp.goLockState.lockState & 0x02)) || - // non-interact (0x4) and interact (0x08) together - ((tmp.goLockState.lockState & 0x04) && (tmp.goLockState.lockState & 0x08)) || - // no setting - !tmp.goLockState.lockState || - // invalid number - tmp.goLockState.lockState >= 0x10) - { - sLog.outErrorDb("Table `%s` has invalid lock state (datalong = %u) in SCRIPT_COMMAND_GO_LOCK_STATE for script id %u.", tablename, tmp.goLockState.lockState, tmp.id); - continue; - } - break; - } - case SCRIPT_COMMAND_STAND_STATE: // 28 - { - if (tmp.standState.stand_state >= MAX_UNIT_STAND_STATE) - { - sLog.outErrorDb("Table `%s` has invalid stand state (datalong = %u) in SCRIPT_COMMAND_STAND_STATE for script id %u", tablename, tmp.standState.stand_state, tmp.id); - continue; - } - break; - } - case SCRIPT_COMMAND_MODIFY_NPC_FLAGS: // 29 - break; - case SCRIPT_COMMAND_SEND_TAXI_PATH: // 30 - { - if (!sTaxiPathStore.LookupEntry(tmp.sendTaxiPath.taxiPathId)) - { - sLog.outErrorDb("Table `%s` has datalong = %u in SCRIPT_COMMAND_SEND_TAXI_PATH for script id %u, but this taxi path does not exist.", tablename, tmp.sendTaxiPath.taxiPathId, tmp.id); - continue; - } - // Check if this taxi path can be triggered with a spell - if (!sLog.HasLogFilter(LOG_FILTER_DB_STRICTED_CHECK)) - { - uint32 taxiSpell = 0; - for (uint32 i = 1; i < sSpellStore.GetNumRows() && taxiSpell == 0; ++i) - { - if (SpellEntry const* spell = sSpellStore.LookupEntry(i)) - for (int j = 0; j < MAX_EFFECT_INDEX; ++j) - { - SpellEffectEntry const* spellEffect = spell->GetSpellEffect(SpellEffectIndex(j)); - if (!spellEffect) - continue; - - if (spellEffect->Effect == SPELL_EFFECT_SEND_TAXI && spellEffect->EffectMiscValue == tmp.sendTaxiPath.taxiPathId) - { - taxiSpell = i; - break; - } - } - } - - if (taxiSpell) - { - sLog.outErrorDb("Table `%s` has datalong = %u in SCRIPT_COMMAND_SEND_TAXI_PATH for script id %u, but this taxi path can be triggered by spell %u.", tablename, tmp.sendTaxiPath.taxiPathId, tmp.id, taxiSpell); - continue; - } - } - break; - } - case SCRIPT_COMMAND_TERMINATE_SCRIPT: // 31 - { - if (tmp.terminateScript.npcEntry && !ObjectMgr::GetCreatureTemplate(tmp.terminateScript.npcEntry)) - { - sLog.outErrorDb("Table `%s` has datalong = %u in SCRIPT_COMMAND_TERMINATE_SCRIPT for script id %u, but this npc entry does not exist.", tablename, tmp.sendTaxiPath.taxiPathId, tmp.id); - continue; - } - break; - } - case SCRIPT_COMMAND_PAUSE_WAYPOINTS: // 32 - break; - case SCRIPT_COMMAND_XP_USER: // 33 - break; - case SCRIPT_COMMAND_TERMINATE_COND: // 34 - { - if (!sConditionStorage.LookupEntry(tmp.terminateCond.conditionId)) - { - sLog.outErrorDb("Table `%s` has datalong = %u in SCRIPT_COMMAND_TERMINATE_COND for script id %u, but this condition_id does not exist.", tablename, tmp.terminateCond.conditionId, tmp.id); - continue; - } - if (tmp.terminateCond.failQuest && !sObjectMgr.GetQuestTemplate(tmp.terminateCond.failQuest)) - { - sLog.outErrorDb("Table `%s` has datalong2 = %u in SCRIPT_COMMAND_TERMINATE_COND for script id %u, but this questId does not exist.", tablename, tmp.terminateCond.failQuest, tmp.id); - continue; - } - break; - } - default: - { - sLog.outErrorDb("Table `%s` unknown command %u, skipping.", tablename, tmp.command); - continue; - } - } - - if (scripts.second.find(tmp.id) == scripts.second.end()) - { - ScriptMap emptyMap; - scripts.second[tmp.id] = emptyMap; - } - scripts.second[tmp.id].insert(ScriptMap::value_type(tmp.delay, tmp)); - - ++count; - } - while (result->NextRow()); - - delete result; - - sLog.outString(); - sLog.outString(">> Loaded %u script definitions", count); -} - -void ScriptMgr::LoadGameObjectScripts() -{ - LoadScripts(sGameObjectScripts, "dbscripts_on_go_use"); - - // check ids - for (ScriptMapMap::const_iterator itr = sGameObjectScripts.second.begin(); itr != sGameObjectScripts.second.end(); ++itr) - { - if (!sObjectMgr.GetGOData(itr->first)) - sLog.outErrorDb("Table `dbscripts_on_go_use` has not existing gameobject (GUID: %u) as script id", itr->first); - } -} - -void ScriptMgr::LoadGameObjectTemplateScripts() -{ - LoadScripts(sGameObjectTemplateScripts, "dbscripts_on_go_template_use"); - - // check ids - for (ScriptMapMap::const_iterator itr = sGameObjectTemplateScripts.second.begin(); itr != sGameObjectTemplateScripts.second.end(); ++itr) - { - if (!sObjectMgr.GetGameObjectInfo(itr->first)) - sLog.outErrorDb("Table `dbscripts_on_go_template_use` has not existing gameobject (Entry: %u) as script id", itr->first); - } -} - -void ScriptMgr::LoadQuestEndScripts() -{ - LoadScripts(sQuestEndScripts, "dbscripts_on_quest_end"); - - // check ids - for (ScriptMapMap::const_iterator itr = sQuestEndScripts.second.begin(); itr != sQuestEndScripts.second.end(); ++itr) - { - if (!sObjectMgr.GetQuestTemplate(itr->first)) - sLog.outErrorDb("Table `dbscripts_on_quest_end` has not existing quest (Id: %u) as script id", itr->first); - } -} - -void ScriptMgr::LoadQuestStartScripts() -{ - LoadScripts(sQuestStartScripts, "dbscripts_on_quest_start"); - - // check ids - for (ScriptMapMap::const_iterator itr = sQuestStartScripts.second.begin(); itr != sQuestStartScripts.second.end(); ++itr) - { - if (!sObjectMgr.GetQuestTemplate(itr->first)) - sLog.outErrorDb("Table `dbscripts_on_quest_start` has not existing quest (Id: %u) as script id", itr->first); - } -} - -void ScriptMgr::LoadSpellScripts() -{ - LoadScripts(sSpellScripts, "dbscripts_on_spell"); - - // check ids - for (ScriptMapMap::const_iterator itr = sSpellScripts.second.begin(); itr != sSpellScripts.second.end(); ++itr) - { - SpellEntry const* spellInfo = sSpellStore.LookupEntry(itr->first); - if (!spellInfo) - { - sLog.outErrorDb("Table `dbscripts_on_spell` has not existing spell (Id: %u) as script id", itr->first); - continue; - } - - // check for correct spellEffect - bool found = false; - for (int i = 0; i < MAX_EFFECT_INDEX; ++i) - { - if (GetSpellStartDBScriptPriority(spellInfo, SpellEffectIndex(i))) - { - found = true; - break; - } - } - - if (!found) - sLog.outErrorDb("Table `dbscripts_on_spell` has unsupported spell (Id: %u)", itr->first); - } -} - -void ScriptMgr::LoadEventScripts() -{ - LoadScripts(sEventScripts, "dbscripts_on_event"); - - std::set eventIds; // Store possible event ids - CollectPossibleEventIds(eventIds); - - // Then check if all scripts are in above list of possible script entries - for (ScriptMapMap::const_iterator itr = sEventScripts.second.begin(); itr != sEventScripts.second.end(); ++itr) - { - std::set::const_iterator itr2 = eventIds.find(itr->first); - if (itr2 == eventIds.end()) - sLog.outErrorDb("Table `dbscripts_on_event` has script (Id: %u) not referring to any fitting gameobject_template or any spell effect %u or path taxi node data", - itr->first, SPELL_EFFECT_SEND_EVENT); - } -} - -void ScriptMgr::LoadGossipScripts() -{ - LoadScripts(sGossipScripts, "dbscripts_on_gossip"); - - // checks are done in LoadGossipMenuItems and LoadGossipMenu -} - -void ScriptMgr::LoadCreatureMovementScripts() -{ - LoadScripts(sCreatureMovementScripts, "dbscripts_on_creature_movement"); - - // checks are done in WaypointManager::Load -} - -void ScriptMgr::LoadCreatureDeathScripts() -{ - LoadScripts(sCreatureDeathScripts, "dbscripts_on_creature_death"); - - // check ids - for (ScriptMapMap::const_iterator itr = sCreatureDeathScripts.second.begin(); itr != sCreatureDeathScripts.second.end(); ++itr) - { - if (!sObjectMgr.GetCreatureTemplate(itr->first)) - sLog.outErrorDb("Table `dbscripts_on_creature_death` has not existing creature (Entry: %u) as script id", itr->first); - } -} - -void ScriptMgr::LoadDbScriptStrings() -{ - sObjectMgr.LoadMangosStrings(WorldDatabase, "db_script_string", MIN_DB_SCRIPT_STRING_ID, MAX_DB_SCRIPT_STRING_ID, true); - - std::set ids; - - for (int32 i = MIN_DB_SCRIPT_STRING_ID; i < MAX_DB_SCRIPT_STRING_ID; ++i) - if (sObjectMgr.GetMangosStringLocale(i)) - ids.insert(i); - - CheckScriptTexts(sQuestEndScripts, ids); - CheckScriptTexts(sQuestStartScripts, ids); - CheckScriptTexts(sSpellScripts, ids); - CheckScriptTexts(sGameObjectScripts, ids); - CheckScriptTexts(sGameObjectTemplateScripts, ids); - CheckScriptTexts(sEventScripts, ids); - CheckScriptTexts(sGossipScripts, ids); - CheckScriptTexts(sCreatureDeathScripts, ids); - CheckScriptTexts(sCreatureMovementScripts, ids); - - sWaypointMgr.CheckTextsExistance(ids); - - for (std::set::const_iterator itr = ids.begin(); itr != ids.end(); ++itr) - sLog.outErrorDb("Table `db_script_string` has unused string id %u", *itr); -} - -void ScriptMgr::CheckScriptTexts(ScriptMapMapName const& scripts, std::set& ids) -{ - for (ScriptMapMap::const_iterator itrMM = scripts.second.begin(); itrMM != scripts.second.end(); ++itrMM) - { - for (ScriptMap::const_iterator itrM = itrMM->second.begin(); itrM != itrMM->second.end(); ++itrM) - { - if (itrM->second.command == SCRIPT_COMMAND_TALK) - { - for (int i = 0; i < MAX_TEXT_ID; ++i) - { - if (itrM->second.textId[i] && !sObjectMgr.GetMangosStringLocale(itrM->second.textId[i])) - sLog.outErrorDb("Table `db_script_string` is missing string id %u, used in database script table %s id %u.", itrM->second.textId[i], scripts.first, itrMM->first); - - if (ids.find(itrM->second.textId[i]) != ids.end()) - ids.erase(itrM->second.textId[i]); - } - } - } - } -} - -// ///////////////////////////////////////////////////////// -// DB SCRIPT ENGINE -// ///////////////////////////////////////////////////////// - -/// Helper function to get Object source or target for Script-Command -/// returns false iff an error happened -bool ScriptAction::GetScriptCommandObject(const ObjectGuid guid, bool includeItem, Object*& resultObject) -{ - resultObject = NULL; - - if (!guid) - return true; - - switch (guid.GetHigh()) - { - case HIGHGUID_UNIT: - case HIGHGUID_VEHICLE: - resultObject = m_map->GetCreature(guid); - break; - case HIGHGUID_PET: - resultObject = m_map->GetPet(guid); - break; - case HIGHGUID_PLAYER: - resultObject = m_map->GetPlayer(guid); - break; - case HIGHGUID_GAMEOBJECT: - resultObject = m_map->GetGameObject(guid); - break; - case HIGHGUID_CORPSE: - resultObject = HashMapHolder::Find(guid); - break; - case HIGHGUID_ITEM: - // case HIGHGUID_CONTAINER: ==HIGHGUID_ITEM - { - if (includeItem) - { - if (Player* player = m_map->GetPlayer(m_ownerGuid)) - resultObject = player->GetItemByGuid(guid); - break; - } - // else no break, but display error message - } - default: - sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u with unsupported guid %s, skipping", m_table, m_script->id, m_script->command, guid.GetString().c_str()); - return false; - } - - if (resultObject && !resultObject->IsInWorld()) - resultObject = NULL; - - return true; -} - -/// Select source and target for a script command -/// Returns false iff an error happened -bool ScriptAction::GetScriptProcessTargets(WorldObject* pOrigSource, WorldObject* pOrigTarget, WorldObject*& pFinalSource, WorldObject*& pFinalTarget) -{ - WorldObject* pBuddy = NULL; - - if (m_script->buddyEntry) - { - if (m_script->data_flags & SCRIPT_FLAG_BUDDY_BY_GUID) - { - if (m_script->IsCreatureBuddy()) - { - CreatureInfo const* cinfo = ObjectMgr::GetCreatureTemplate(m_script->buddyEntry); - pBuddy = m_map->GetCreature(cinfo->GetObjectGuid(m_script->searchRadiusOrGuid)); - - if (pBuddy && !((Creature*)pBuddy)->isAlive()) - { - sLog.outError(" DB-SCRIPTS: Process table `%s` id %u, command %u has buddy %u by guid %u but buddy is dead, skipping.", m_table, m_script->id, m_script->command, m_script->buddyEntry, m_script->searchRadiusOrGuid); - return false; - } - } - else - { - // GameObjectInfo const* ginfo = ObjectMgr::GetGameObjectInfo(m_script->buddyEntry); - pBuddy = m_map->GetGameObject(ObjectGuid(HIGHGUID_GAMEOBJECT, m_script->buddyEntry, m_script->searchRadiusOrGuid)); - } - // TODO Maybe load related grid if not already done? How to handle multi-map case? - if (!pBuddy) - { - sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u has buddy %u by guid %u not loaded in map %u (data-flags %u), skipping.", m_table, m_script->id, m_script->command, m_script->buddyEntry, m_script->searchRadiusOrGuid, m_map->GetId(), m_script->data_flags); - return false; - } - } - else // Buddy by entry - { - if (!pOrigSource && !pOrigTarget) - { - sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u called without buddy %u, but no source for search available, skipping.", m_table, m_script->id, m_script->command, m_script->buddyEntry); - return false; - } - - // Prefer non-players as searcher - WorldObject* pSearcher = pOrigSource ? pOrigSource : pOrigTarget; - if (pSearcher->GetTypeId() == TYPEID_PLAYER && pOrigTarget && pOrigTarget->GetTypeId() != TYPEID_PLAYER) - pSearcher = pOrigTarget; - - - if (m_script->IsCreatureBuddy()) - { - Creature* pCreatureBuddy = NULL; - - MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck u_check(*pSearcher, m_script->buddyEntry, true, false, m_script->searchRadiusOrGuid, true); - MaNGOS::CreatureLastSearcher searcher(pCreatureBuddy, u_check); - - if (m_script->data_flags & SCRIPT_FLAG_BUDDY_IS_PET) - Cell::VisitWorldObjects(pSearcher, searcher, m_script->searchRadiusOrGuid); - else // Normal Creature - Cell::VisitGridObjects(pSearcher, searcher, m_script->searchRadiusOrGuid); - - pBuddy = pCreatureBuddy; - - // TODO: Remove this extra check output after a while - it might have false effects - if (!pBuddy && pSearcher->GetEntry() == m_script->buddyEntry) - { - sLog.outErrorDb(" DB-SCRIPTS: WARNING: Process table `%s` id %u, command %u has no OTHER buddy %u found - maybe you need to update the script?", m_table, m_script->id, m_script->command, m_script->buddyEntry); - pBuddy = pSearcher; - } - } - else - { - GameObject* pGOBuddy = NULL; - - MaNGOS::NearestGameObjectEntryInObjectRangeCheck u_check(*pSearcher, m_script->buddyEntry, m_script->searchRadiusOrGuid); - MaNGOS::GameObjectLastSearcher searcher(pGOBuddy, u_check); - - Cell::VisitGridObjects(pSearcher, searcher, m_script->searchRadiusOrGuid); - pBuddy = pGOBuddy; - } - - if (!pBuddy) - { - sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u has buddy %u not found in range %u of searcher %s (data-flags %u), skipping.", m_table, m_script->id, m_script->command, m_script->buddyEntry, m_script->searchRadiusOrGuid, pSearcher->GetGuidStr().c_str(), m_script->data_flags); - return false; - } - } - } - - if (m_script->data_flags & SCRIPT_FLAG_BUDDY_AS_TARGET) - { - pFinalSource = pOrigSource; - pFinalTarget = pBuddy; - } - else - { - pFinalSource = pBuddy ? pBuddy : pOrigSource; - pFinalTarget = pOrigTarget; - } - - if (m_script->data_flags & SCRIPT_FLAG_REVERSE_DIRECTION) - std::swap(pFinalSource, pFinalTarget); - - if (m_script->data_flags & SCRIPT_FLAG_SOURCE_TARGETS_SELF) - pFinalTarget = pFinalSource; - - return true; -} - -/// Helper to log error information -bool ScriptAction::LogIfNotCreature(WorldObject* pWorldObject) -{ - if (!pWorldObject || pWorldObject->GetTypeId() != TYPEID_UNIT) - { - sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u call for non-creature, skipping.", m_table, m_script->id, m_script->command); - return true; - } - return false; -} -bool ScriptAction::LogIfNotUnit(WorldObject* pWorldObject) -{ - if (!pWorldObject || !pWorldObject->isType(TYPEMASK_UNIT)) - { - sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u call for non-unit, skipping.", m_table, m_script->id, m_script->command); - return true; - } - return false; -} -bool ScriptAction::LogIfNotGameObject(WorldObject* pWorldObject) -{ - if (!pWorldObject || pWorldObject->GetTypeId() != TYPEID_GAMEOBJECT) - { - sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u call for non-gameobject, skipping.", m_table, m_script->id, m_script->command); - return true; - } - return false; -} - -/// Helper to get a player if possible (target preferred) -Player* ScriptAction::GetPlayerTargetOrSourceAndLog(WorldObject* pSource, WorldObject* pTarget) -{ - if ((!pTarget || pTarget->GetTypeId() != TYPEID_PLAYER) && (!pSource || pSource->GetTypeId() != TYPEID_PLAYER)) - { - sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u call for non player, skipping.", m_table, m_script->id, m_script->command); - return NULL; - } - - return pTarget && pTarget->GetTypeId() == TYPEID_PLAYER ? (Player*)pTarget : (Player*)pSource; -} - -/// Handle one Script Step -// Return true if and only if further parts of this script shall be skipped -bool ScriptAction::HandleScriptStep() -{ - WorldObject* pSource; - WorldObject* pTarget; - Object* pSourceOrItem; // Stores a provided pSource (if exists as WorldObject) or source-item - - { // Add scope for source & target variables so that they are not used below - Object* source = NULL; - Object* target = NULL; - if (!GetScriptCommandObject(m_sourceGuid, true, source)) - return false; - if (!GetScriptCommandObject(m_targetGuid, false, target)) - return false; - - // Give some debug log output for easier use - DEBUG_LOG("DB-SCRIPTS: Process table `%s` id %u, command %u for source %s (%sin world), target %s (%sin world)", m_table, m_script->id, m_script->command, m_sourceGuid.GetString().c_str(), source ? "" : "not ", m_targetGuid.GetString().c_str(), target ? "" : "not "); - - // Get expected source and target (if defined with buddy) - pSource = source && source->isType(TYPEMASK_WORLDOBJECT) ? (WorldObject*)source : NULL; - pTarget = target && target->isType(TYPEMASK_WORLDOBJECT) ? (WorldObject*)target : NULL; - if (!GetScriptProcessTargets(pSource, pTarget, pSource, pTarget)) - return false; - - pSourceOrItem = pSource ? pSource : (source && source->isType(TYPEMASK_ITEM) ? source : NULL); - } - - switch (m_script->command) - { - case SCRIPT_COMMAND_TALK: // 0 - { - if (!pSource) - { - sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u found no worldobject as source, skipping.", m_table, m_script->id, m_script->command); - break; - } - - Unit* unitTarget = pTarget && pTarget->isType(TYPEMASK_UNIT) ? static_cast(pTarget) : NULL; - int32 textId = m_script->textId[0]; - - // May have text for random - if (m_script->textId[1]) - { - int i = 2; - for (; i < MAX_TEXT_ID; ++i) - { - if (!m_script->textId[i]) - break; - } - - // Use one random - textId = m_script->textId[urand(0, i - 1)]; - } - - if (!DoDisplayText(pSource, textId, unitTarget)) - sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, could not display text %i properly", m_table, m_script->id, textId); - - break; - } - case SCRIPT_COMMAND_EMOTE: // 1 - { - if (LogIfNotUnit(pSource)) - break; - - ((Unit*)pSource)->HandleEmote(m_script->emote.emoteId); - break; - } - case SCRIPT_COMMAND_FIELD_SET: // 2 - if (!pSourceOrItem) - { - sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u call for NULL object.", m_table, m_script->id, m_script->command); - break; - } - if (m_script->setField.fieldId <= OBJECT_FIELD_ENTRY || m_script->setField.fieldId >= pSourceOrItem->GetValuesCount()) - { - sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u call for wrong field %u (max count: %u) in %s.", - m_table, m_script->id, m_script->command, m_script->setField.fieldId, pSourceOrItem->GetValuesCount(), pSourceOrItem->GetGuidStr().c_str()); - break; - } - pSourceOrItem->SetUInt32Value(m_script->setField.fieldId, m_script->setField.fieldValue); - break; - case SCRIPT_COMMAND_MOVE_TO: // 3 - { - if (LogIfNotUnit(pSource)) - break; - - // Just turn around - if ((m_script->x == 0.0f && m_script->y == 0.0f && m_script->z == 0.0f) || - // Check point-to-point distance, hence revert effect of bounding radius - ((Unit*)pSource)->IsWithinDist3d(m_script->x, m_script->y, m_script->z, 0.01f - ((Unit*)pSource)->GetObjectBoundingRadius())) - { - ((Unit*)pSource)->SetFacingTo(m_script->o); - break; - } - - // For command additional teleport the unit - if (m_script->data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) - { - ((Unit*)pSource)->NearTeleportTo(m_script->x, m_script->y, m_script->z, m_script->o != 0.0f ? m_script->o : ((Unit*)pSource)->GetOrientation()); - break; - } - - // Normal Movement - if (m_script->moveTo.travelSpeed) - ((Unit*)pSource)->MonsterMoveWithSpeed(m_script->x, m_script->y, m_script->z, m_script->moveTo.travelSpeed * 0.01f); - else - { - ((Unit*)pSource)->GetMotionMaster()->Clear(); - ((Unit*)pSource)->GetMotionMaster()->MovePoint(0, m_script->x, m_script->y, m_script->z); - } - break; - } - case SCRIPT_COMMAND_FLAG_SET: // 4 - if (!pSourceOrItem) - { - sLog.outErrorDb("SCRIPT_COMMAND_FLAG_SET (script id %u) call for NULL object.", m_script->id); - break; - } - if (m_script->setFlag.fieldId <= OBJECT_FIELD_ENTRY || m_script->setFlag.fieldId >= pSourceOrItem->GetValuesCount()) - { - sLog.outErrorDb("SCRIPT_COMMAND_FLAG_SET (script id %u) call for wrong field %u (max count: %u) in %s.", - m_script->id, m_script->setFlag.fieldId, pSourceOrItem->GetValuesCount(), pSourceOrItem->GetGuidStr().c_str()); - break; - } - pSourceOrItem->SetFlag(m_script->setFlag.fieldId, m_script->setFlag.fieldValue); - break; - case SCRIPT_COMMAND_FLAG_REMOVE: // 5 - if (!pSourceOrItem) - { - sLog.outErrorDb("SCRIPT_COMMAND_FLAG_REMOVE (script id %u) call for NULL object.", m_script->id); - break; - } - if (m_script->removeFlag.fieldId <= OBJECT_FIELD_ENTRY || m_script->removeFlag.fieldId >= pSourceOrItem->GetValuesCount()) - { - sLog.outErrorDb("SCRIPT_COMMAND_FLAG_REMOVE (script id %u) call for wrong field %u (max count: %u) in %s.", - m_script->id, m_script->removeFlag.fieldId, pSourceOrItem->GetValuesCount(), pSourceOrItem->GetGuidStr().c_str()); - break; - } - pSourceOrItem->RemoveFlag(m_script->removeFlag.fieldId, m_script->removeFlag.fieldValue); - break; - case SCRIPT_COMMAND_TELEPORT_TO: // 6 - { - Player* pPlayer = GetPlayerTargetOrSourceAndLog(pSource, pTarget); - if (!pPlayer) - break; - - pPlayer->TeleportTo(m_script->teleportTo.mapId, m_script->x, m_script->y, m_script->z, m_script->o); - break; - } - case SCRIPT_COMMAND_QUEST_EXPLORED: // 7 - { - Player* pPlayer = GetPlayerTargetOrSourceAndLog(pSource, pTarget); - if (!pPlayer) - break; - - WorldObject* pWorldObject = NULL; - if (pSource && pSource->isType(TYPEMASK_CREATURE_OR_GAMEOBJECT)) - pWorldObject = pSource; - else if (pTarget && pTarget->isType(TYPEMASK_CREATURE_OR_GAMEOBJECT)) - pWorldObject = pTarget; - - // if we have a distance, we must have a worldobject - if (m_script->questExplored.distance != 0 && !pWorldObject) - { - sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u called without source worldobject, skipping.", m_table, m_script->id, m_script->command); - break; - } - - bool failQuest = false; - // Creature must be alive for giving credit - if (pWorldObject && pWorldObject->GetTypeId() == TYPEID_UNIT && !((Creature*)pWorldObject)->isAlive()) - failQuest = true; - else if (m_script->questExplored.distance != 0 && !pWorldObject->IsWithinDistInMap(pPlayer, float(m_script->questExplored.distance))) - failQuest = true; - - // quest id and flags checked at script loading - if (!failQuest) - pPlayer->AreaExploredOrEventHappens(m_script->questExplored.questId); - else - pPlayer->FailQuest(m_script->questExplored.questId); - - break; - } - case SCRIPT_COMMAND_KILL_CREDIT: // 8 - { - Player* pPlayer = GetPlayerTargetOrSourceAndLog(pSource, pTarget); - if (!pPlayer) - break; - - uint32 creatureEntry = m_script->killCredit.creatureEntry; - WorldObject* pRewardSource = pSource && pSource->GetTypeId() == TYPEID_UNIT ? pSource : (pTarget && pTarget->GetTypeId() == TYPEID_UNIT ? pTarget : NULL); - - // dynamic effect, take entry of reward Source - if (!creatureEntry) - { - if (pRewardSource) - creatureEntry = pRewardSource->GetEntry(); - else - { - sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u called for dynamic killcredit without creature partner, skipping.", m_table, m_script->id, m_script->command); - break; - } - } - - if (m_script->killCredit.isGroupCredit) - { - WorldObject* pSearcher = pRewardSource ? pRewardSource : (pSource ? pSource : pTarget); - if (pSearcher != pRewardSource) - sLog.outDebug(" DB-SCRIPTS: Process table `%s` id %u, SCRIPT_COMMAND_KILL_CREDIT called for groupCredit without creature as searcher, script might need adjustment.", m_table, m_script->id); - pPlayer->RewardPlayerAndGroupAtEvent(creatureEntry, pSearcher); - } - else - pPlayer->KilledMonsterCredit(creatureEntry, pRewardSource ? pRewardSource->GetObjectGuid() : ObjectGuid()); - - break; - } - case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT: // 9 - { - GameObject* pGo = NULL; - uint32 time_to_despawn = m_script->respawnGo.despawnDelay < 5 ? 5 : m_script->respawnGo.despawnDelay; - - if (m_script->respawnGo.goGuid) - { - GameObjectData const* goData = sObjectMgr.GetGOData(m_script->respawnGo.goGuid); - if (!goData) - break; // checked at load - - // TODO - This was a change, was before current map of source - pGo = m_map->GetGameObject(ObjectGuid(HIGHGUID_GAMEOBJECT, goData->id, m_script->respawnGo.goGuid)); - } - else - { - if (LogIfNotGameObject(pSource)) - break; - - pGo = (GameObject*)pSource; - } - - if (!pGo) - { - sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u failed for gameobject(guid: %u, buddyEntry: %u).", m_table, m_script->id, m_script->command, m_script->respawnGo.goGuid, m_script->buddyEntry); - break; - } - - if (pGo->GetGoType() == GAMEOBJECT_TYPE_FISHINGNODE || - pGo->GetGoType() == GAMEOBJECT_TYPE_DOOR || - pGo->GetGoType() == GAMEOBJECT_TYPE_BUTTON || - pGo->GetGoType() == GAMEOBJECT_TYPE_TRAP) - { - sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u can not be used with gameobject of type %u (guid: %u, buddyEntry: %u).", m_table, m_script->id, m_script->command, uint32(pGo->GetGoType()), m_script->respawnGo.goGuid, m_script->buddyEntry); - break; - } - - if (pGo->isSpawned()) - break; // gameobject already spawned - - pGo->SetLootState(GO_READY); - pGo->SetRespawnTime(time_to_despawn); // despawn object in ? seconds - pGo->Refresh(); - break; - } - case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE: // 10 - { - if (!pSource) - { - sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u found no worldobject as source, skipping.", m_table, m_script->id, m_script->command); - break; - } - - float x = m_script->x; - float y = m_script->y; - float z = m_script->z; - float o = m_script->o; - - Creature* pCreature = pSource->SummonCreature(m_script->summonCreature.creatureEntry, x, y, z, o, m_script->summonCreature.despawnDelay ? TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN : TEMPSUMMON_DEAD_DESPAWN, m_script->summonCreature.despawnDelay, (m_script->data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) ? true : false); - if (!pCreature) - { - sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u failed for creature (entry: %u).", m_table, m_script->id, m_script->command, m_script->summonCreature.creatureEntry); - break; - } - - break; - } - case SCRIPT_COMMAND_OPEN_DOOR: // 11 - case SCRIPT_COMMAND_CLOSE_DOOR: // 12 - { - GameObject* pDoor; - uint32 time_to_reset = m_script->changeDoor.resetDelay < 15 ? 15 : m_script->changeDoor.resetDelay; - - if (m_script->changeDoor.goGuid) - { - GameObjectData const* goData = sObjectMgr.GetGOData(m_script->changeDoor.goGuid); - if (!goData) // checked at load - break; - - // TODO - Was a change, before random map - pDoor = m_map->GetGameObject(ObjectGuid(HIGHGUID_GAMEOBJECT, goData->id, m_script->changeDoor.goGuid)); - } - else - { - if (LogIfNotGameObject(pSource)) - break; - - pDoor = (GameObject*)pSource; - } - - if (!pDoor) - { - sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u failed for gameobject(guid: %u, buddyEntry: %u).", m_table, m_script->id, m_script->command, m_script->changeDoor.goGuid, m_script->buddyEntry); - break; - } - - if (pDoor->GetGoType() != GAMEOBJECT_TYPE_DOOR) - { - sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u failed for non-door(GoType: %u).", m_table, m_script->id, m_script->command, pDoor->GetGoType()); - break; - } - - if ((m_script->command == SCRIPT_COMMAND_OPEN_DOOR && pDoor->GetGoState() != GO_STATE_READY) || - (m_script->command == SCRIPT_COMMAND_CLOSE_DOOR && pDoor->GetGoState() == GO_STATE_READY)) - break; // to be opened door already open, or to be closed door already closed - - pDoor->UseDoorOrButton(time_to_reset); - - if (pTarget && pTarget->isType(TYPEMASK_GAMEOBJECT) && ((GameObject*)pTarget)->GetGoType() == GAMEOBJECT_TYPE_BUTTON) - ((GameObject*)pTarget)->UseDoorOrButton(time_to_reset); - - break; - } - case SCRIPT_COMMAND_ACTIVATE_OBJECT: // 13 - { - if (LogIfNotUnit(pSource)) - break; - if (LogIfNotGameObject(pTarget)) - break; - - ((GameObject*)pTarget)->Use((Unit*)pSource); - break; - } - case SCRIPT_COMMAND_REMOVE_AURA: // 14 - { - if (LogIfNotUnit(pSource)) - break; - - ((Unit*)pSource)->RemoveAurasDueToSpell(m_script->removeAura.spellId); - break; - } - case SCRIPT_COMMAND_CAST_SPELL: // 15 - { - if (LogIfNotUnit(pTarget)) // TODO - Change when support for casting without victim will be supported - break; - - // TODO: when GO cast implemented, code below must be updated accordingly to also allow GO spell cast - if (pSource && pSource->GetTypeId() == TYPEID_GAMEOBJECT) - { - ((Unit*)pTarget)->CastSpell(((Unit*)pTarget), m_script->castSpell.spellId, true, NULL, NULL, pSource->GetObjectGuid()); - break; - } - - if (LogIfNotUnit(pSource)) - break; - ((Unit*)pSource)->CastSpell(((Unit*)pTarget), m_script->castSpell.spellId, (m_script->data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) != 0); - - break; - } - case SCRIPT_COMMAND_PLAY_SOUND: // 16 - { - if (!pSource) - { - sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u could not find proper source", m_table, m_script->id, m_script->command); - break; - } - - // bitmask: 0/1=target-player, 0/2=with distance dependent, 0/4=map wide, 0/8=zone wide - Player* pSoundTarget = NULL; - if (m_script->playSound.flags & 1) - { - pSoundTarget = GetPlayerTargetOrSourceAndLog(pSource, pTarget); - if (!pSoundTarget) - break; - } - - if (m_script->playSound.flags & 2) - pSource->PlayDistanceSound(m_script->playSound.soundId, pSoundTarget); - else if (m_script->playSound.flags & (4 | 8)) - m_map->PlayDirectSoundToMap(m_script->playSound.soundId, m_script->playSound.flags & 8 ? pSource->GetZoneId() : 0); - else - pSource->PlayDirectSound(m_script->playSound.soundId, pSoundTarget); - - break; - } - case SCRIPT_COMMAND_CREATE_ITEM: // 17 - { - Player* pPlayer = GetPlayerTargetOrSourceAndLog(pSource, pTarget); - if (!pPlayer) - break; - - if (Item* pItem = pPlayer->StoreNewItemInInventorySlot(m_script->createItem.itemEntry, m_script->createItem.amount)) - pPlayer->SendNewItem(pItem, m_script->createItem.amount, true, false); - - break; - } - case SCRIPT_COMMAND_DESPAWN_SELF: // 18 - { - // TODO - Remove this check after a while - if (pTarget && pTarget->GetTypeId() != TYPEID_UNIT && pSource && pSource->GetTypeId() == TYPEID_UNIT) - { - sLog.outErrorDb("DB-SCRIPTS: Process table `%s` id %u, command %u target must be creature, but (only) source is, use data_flags to fix", m_table, m_script->id, m_script->command); - pTarget = pSource; - } - - if (LogIfNotCreature(pTarget)) - break; - - ((Creature*)pTarget)->ForcedDespawn(m_script->despawn.despawnDelay); - - break; - } - case SCRIPT_COMMAND_PLAY_MOVIE: // 19 - { - Player* pPlayer = GetPlayerTargetOrSourceAndLog(pSource, pTarget); - if (!pPlayer) - break; - - pPlayer->SendMovieStart(m_script->playMovie.movieId); - - break; - } - case SCRIPT_COMMAND_MOVEMENT: // 20 - { - if (LogIfNotCreature(pSource)) - break; - - // Consider add additional checks for cases where creature should not change movementType - // (pet? in combat? already using same MMgen as script try to apply?) - - switch (m_script->movement.movementType) - { - case IDLE_MOTION_TYPE: - ((Creature*)pSource)->GetMotionMaster()->MoveIdle(); - break; - case RANDOM_MOTION_TYPE: - if (m_script->data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) - ((Creature*)pSource)->GetMotionMaster()->MoveRandomAroundPoint(pSource->GetPositionX(), pSource->GetPositionY(), pSource->GetPositionZ(), float(m_script->movement.wanderDistance)); - else - { - float respX, respY, respZ, respO, wander_distance; - ((Creature*)pSource)->GetRespawnCoord(respX, respY, respZ, &respO, &wander_distance); - wander_distance = m_script->movement.wanderDistance ? m_script->movement.wanderDistance : wander_distance; - ((Creature*)pSource)->GetMotionMaster()->MoveRandomAroundPoint(respX, respY, respZ, wander_distance); - } - break; - case WAYPOINT_MOTION_TYPE: - ((Creature*)pSource)->GetMotionMaster()->MoveWaypoint(); - break; - } - - break; - } - case SCRIPT_COMMAND_SET_ACTIVEOBJECT: // 21 - { - if (LogIfNotCreature(pSource)) - break; - - ((Creature*)pSource)->SetActiveObjectState(m_script->activeObject.activate); - break; - } - case SCRIPT_COMMAND_SET_FACTION: // 22 - { - if (LogIfNotCreature(pSource)) - break; - - if (m_script->faction.factionId) - ((Creature*)pSource)->SetFactionTemporary(m_script->faction.factionId, m_script->faction.flags); - else - ((Creature*)pSource)->ClearTemporaryFaction(); - - break; - } - case SCRIPT_COMMAND_MORPH_TO_ENTRY_OR_MODEL: // 23 - { - if (LogIfNotCreature(pSource)) - break; - - if (!m_script->morph.creatureOrModelEntry) - ((Creature*)pSource)->DeMorph(); - else if (m_script->data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) - ((Creature*)pSource)->SetDisplayId(m_script->morph.creatureOrModelEntry); - else - { - CreatureInfo const* ci = ObjectMgr::GetCreatureTemplate(m_script->morph.creatureOrModelEntry); - uint32 display_id = Creature::ChooseDisplayId(ci); - - ((Creature*)pSource)->SetDisplayId(display_id); - } - - break; - } - case SCRIPT_COMMAND_MOUNT_TO_ENTRY_OR_MODEL: // 24 - { - if (LogIfNotCreature(pSource)) - break; - - if (!m_script->mount.creatureOrModelEntry) - ((Creature*)pSource)->Unmount(); - else if (m_script->data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) - ((Creature*)pSource)->Mount(m_script->mount.creatureOrModelEntry); - else - { - CreatureInfo const* ci = ObjectMgr::GetCreatureTemplate(m_script->mount.creatureOrModelEntry); - uint32 display_id = Creature::ChooseDisplayId(ci); - - ((Creature*)pSource)->Mount(display_id); - } - - break; - } - case SCRIPT_COMMAND_SET_RUN: // 25 - { - if (LogIfNotCreature(pSource)) - break; - - ((Creature*)pSource)->SetWalk(!m_script->run.run, true); - - break; - } - case SCRIPT_COMMAND_ATTACK_START: // 26 - { - if (LogIfNotCreature(pSource)) - break; - if (LogIfNotUnit(pTarget)) - break; - - Creature* pAttacker = static_cast(pSource); - Unit* unitTarget = static_cast(pTarget); - - if (pAttacker->IsFriendlyTo(unitTarget)) - { - sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u attacker is friendly to target, can not attack (Attacker: %s, Target: %s)", m_table, m_script->id, m_script->command, pAttacker->GetGuidStr().c_str(), unitTarget->GetGuidStr().c_str()); - break; - } - - pAttacker->AI()->AttackStart(unitTarget); - - break; - } - case SCRIPT_COMMAND_GO_LOCK_STATE: // 27 - { - if (LogIfNotGameObject(pSource)) - break; - - GameObject* pGo = static_cast(pSource); - - /* flag lockState - * go_lock 0x01 - * go_unlock 0x02 - * go_nonInteract 0x04 - * go_Interact 0x08 - */ - - // Lock or Unlock - if (m_script->goLockState.lockState & 0x01) - pGo->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED); - else if (m_script->goLockState.lockState & 0x02) - pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED); - // Set Non Interactable or Set Interactable - if (m_script->goLockState.lockState & 0x04) - pGo->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); - else if (m_script->goLockState.lockState & 0x08) - pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); - - break; - } - case SCRIPT_COMMAND_STAND_STATE: // 28 - { - if (LogIfNotCreature(pSource)) - break; - - // Must be safe cast to Unit* here - ((Unit*)pSource)->SetStandState(m_script->standState.stand_state); - break; - } - case SCRIPT_COMMAND_MODIFY_NPC_FLAGS: // 29 - { - if (LogIfNotCreature(pSource)) - break; - - // Add Flags - if (m_script->npcFlag.change_flag & 0x01) - pSource->SetFlag(UNIT_NPC_FLAGS, m_script->npcFlag.flag); - // Remove Flags - else if (m_script->npcFlag.change_flag & 0x02) - pSource->RemoveFlag(UNIT_NPC_FLAGS, m_script->npcFlag.flag); - // Toggle Flags - else - { - if (pSource->HasFlag(UNIT_NPC_FLAGS, m_script->npcFlag.flag)) - pSource->RemoveFlag(UNIT_NPC_FLAGS, m_script->npcFlag.flag); - else - pSource->SetFlag(UNIT_NPC_FLAGS, m_script->npcFlag.flag); - } - - break; - } - case SCRIPT_COMMAND_SEND_TAXI_PATH: // 30 - { - // only Player - Player* pPlayer = GetPlayerTargetOrSourceAndLog(pSource, pTarget); - if (!pPlayer) - break; - - pPlayer->ActivateTaxiPathTo(m_script->sendTaxiPath.taxiPathId); - break; - } - case SCRIPT_COMMAND_TERMINATE_SCRIPT: // 31 - { - bool result = false; - if (m_script->terminateScript.npcEntry) - { - WorldObject* pSearcher = pSource ? pSource : pTarget; - if (pSearcher->GetTypeId() == TYPEID_PLAYER && pTarget && pTarget->GetTypeId() != TYPEID_PLAYER) - pSearcher = pTarget; - - Creature* pCreatureBuddy = NULL; - MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck u_check(*pSearcher, m_script->terminateScript.npcEntry, true, false, m_script->terminateScript.searchDist, true); - MaNGOS::CreatureLastSearcher searcher(pCreatureBuddy, u_check); - Cell::VisitGridObjects(pSearcher, searcher, m_script->terminateScript.searchDist); - - if (!(m_script->data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) && !pCreatureBuddy) - { - DEBUG_LOG("DB-SCRIPTS: Process table `%s` id %u, terminate further steps of this script! (as searched other npc %u was not found alive)", m_table, m_script->id, m_script->terminateScript.npcEntry); - result = true; - } - else if (m_script->data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL && pCreatureBuddy) - { - DEBUG_LOG("DB-SCRIPTS: Process table `%s` id %u, terminate further steps of this script! (as searched other npc %u was found alive)", m_table, m_script->id, m_script->terminateScript.npcEntry); - result = true; - } - } - else - result = true; - - if (result) // Terminate further steps of this script - { - if (m_script->textId[0] && !LogIfNotCreature(pSource)) - { - Creature* cSource = static_cast(pSource); - if (cSource->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE) - (static_cast* >(cSource->GetMotionMaster()->top()))->AddToWaypointPauseTime(m_script->textId[0]); - } - - return true; - } - - break; - } - case SCRIPT_COMMAND_PAUSE_WAYPOINTS: // 32 - { - if (LogIfNotCreature(pSource)) - return false; - if (m_script->pauseWaypoint.doPause) - ((Creature*)pSource)->addUnitState(UNIT_STAT_WAYPOINT_PAUSED); - else - ((Creature*)pSource)->clearUnitState(UNIT_STAT_WAYPOINT_PAUSED); - break; - } - case SCRIPT_COMMAND_XP_USER: // 33 - { - Player* pPlayer = GetPlayerTargetOrSourceAndLog(pSource, pTarget); - if (!pPlayer) - break; - - if (m_script->xpDisabled.flags) - pPlayer->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_XP_USER_DISABLED); - else - pPlayer->RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_XP_USER_DISABLED); - break; - } - case SCRIPT_COMMAND_TERMINATE_COND: - { - Player* player = NULL; - WorldObject* second = pSource; - // First case: target is player - if (pTarget && pTarget->GetTypeId() == TYPEID_PLAYER) - player = static_cast(pTarget); - // Second case: source is player - else if (pSource && pSource->GetTypeId() == TYPEID_PLAYER) - { - player = static_cast(pSource); - second = pTarget; - } - - bool terminateResult; - if (m_script->data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) - terminateResult = !sObjectMgr.IsPlayerMeetToCondition(m_script->terminateCond.conditionId, player, m_map, second, CONDITION_FROM_DBSCRIPTS); - else - terminateResult = sObjectMgr.IsPlayerMeetToCondition(m_script->terminateCond.conditionId, player, m_map, second, CONDITION_FROM_DBSCRIPTS); - - if (terminateResult && m_script->terminateCond.failQuest && player) - { - if (Group* group = player->GetGroup()) - { - for (GroupReference* groupRef = group->GetFirstMember(); groupRef != NULL; groupRef = groupRef->next()) - { - Player* member = groupRef->getSource(); - if (member->GetQuestStatus(m_script->terminateCond.failQuest) == QUEST_STATUS_INCOMPLETE) - member->FailQuest(m_script->terminateCond.failQuest); - } - } - else - { - if (player->GetQuestStatus(m_script->terminateCond.failQuest) == QUEST_STATUS_INCOMPLETE) - player->FailQuest(m_script->terminateCond.failQuest); - } - } - return terminateResult; - } - default: - sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u unknown command used.", m_table, m_script->id, m_script->command); - break; - } - - return false; -} - -// ///////////////////////////////////////////////////////// -// Scripting Library Hooks -// ///////////////////////////////////////////////////////// - -void ScriptMgr::LoadAreaTriggerScripts() -{ - m_AreaTriggerScripts.clear(); // need for reload case - QueryResult* result = WorldDatabase.Query("SELECT entry, ScriptName FROM scripted_areatrigger"); - - uint32 count = 0; - - if (!result) - { - BarGoLink bar(1); - bar.step(); - - sLog.outString(); - sLog.outString(">> Loaded %u scripted areatrigger", count); - return; - } - - BarGoLink bar(result->GetRowCount()); - - do - { - ++count; - bar.step(); - - Field* fields = result->Fetch(); - - uint32 triggerId = fields[0].GetUInt32(); - const char* scriptName = fields[1].GetString(); - - if (!sAreaTriggerStore.LookupEntry(triggerId)) - { - sLog.outErrorDb("Table `scripted_areatrigger` has area trigger (ID: %u) not listed in `AreaTrigger.dbc`.", triggerId); - continue; - } - - m_AreaTriggerScripts[triggerId] = GetScriptId(scriptName); - } - while (result->NextRow()); - - delete result; - - sLog.outString(); - sLog.outString(">> Loaded %u areatrigger scripts", count); -} - -void ScriptMgr::LoadEventIdScripts() -{ - m_EventIdScripts.clear(); // need for reload case - QueryResult* result = WorldDatabase.Query("SELECT id, ScriptName FROM scripted_event"); - - uint32 count = 0; - - if (!result) - { - BarGoLink bar(1); - bar.step(); - - sLog.outString(); - sLog.outString(">> Loaded %u scripted event id", count); - return; - } - - BarGoLink bar(result->GetRowCount()); - - std::set eventIds; // Store possible event ids - CollectPossibleEventIds(eventIds); - - do - { - ++count; - bar.step(); - - Field* fields = result->Fetch(); - - uint32 eventId = fields[0].GetUInt32(); - const char* scriptName = fields[1].GetString(); - - std::set::const_iterator itr = eventIds.find(eventId); - if (itr == eventIds.end()) - sLog.outErrorDb("Table `scripted_event` has id %u not referring to any gameobject_template type 10 data2 field, type 3 data6 field, type 13 data 2 field, type 29 or any spell effect %u or path taxi node data", - eventId, SPELL_EFFECT_SEND_EVENT); - - m_EventIdScripts[eventId] = GetScriptId(scriptName); - } - while (result->NextRow()); - - delete result; - - sLog.outString(); - sLog.outString(">> Loaded %u scripted event id", count); -} - -void ScriptMgr::LoadScriptNames() -{ - m_scriptNames.push_back(""); - QueryResult* result = WorldDatabase.Query( - "SELECT DISTINCT(ScriptName) FROM creature_template WHERE ScriptName <> '' " - "UNION " - "SELECT DISTINCT(ScriptName) FROM gameobject_template WHERE ScriptName <> '' " - "UNION " - "SELECT DISTINCT(ScriptName) FROM item_template WHERE ScriptName <> '' " - "UNION " - "SELECT DISTINCT(ScriptName) FROM scripted_areatrigger WHERE ScriptName <> '' " - "UNION " - "SELECT DISTINCT(ScriptName) FROM scripted_event WHERE ScriptName <> '' " - "UNION " - "SELECT DISTINCT(ScriptName) FROM instance_template WHERE ScriptName <> '' " - "UNION " - "SELECT DISTINCT(ScriptName) FROM world_template WHERE ScriptName <> ''"); - - if (!result) - { - BarGoLink bar(1); - bar.step(); - sLog.outString(); - sLog.outErrorDb(">> Loaded empty set of Script Names!"); - return; - } - - BarGoLink bar(result->GetRowCount()); - uint32 count = 0; - - do - { - bar.step(); - m_scriptNames.push_back((*result)[0].GetString()); - ++count; - } - while (result->NextRow()); - delete result; - - std::sort(m_scriptNames.begin(), m_scriptNames.end()); - sLog.outString(); - sLog.outString(">> Loaded %d Script Names", count); -} - -uint32 ScriptMgr::GetScriptId(const char* name) const -{ - // use binary search to find the script name in the sorted vector - // assume "" is the first element - if (!name) - return 0; - - ScriptNameMap::const_iterator itr = - std::lower_bound(m_scriptNames.begin(), m_scriptNames.end(), name); - - if (itr == m_scriptNames.end() || *itr != name) - return 0; - - return uint32(itr - m_scriptNames.begin()); -} - -uint32 ScriptMgr::GetAreaTriggerScriptId(uint32 triggerId) const -{ - AreaTriggerScriptMap::const_iterator itr = m_AreaTriggerScripts.find(triggerId); - if (itr != m_AreaTriggerScripts.end()) - return itr->second; - - return 0; -} - -uint32 ScriptMgr::GetEventIdScriptId(uint32 eventId) const -{ - EventIdScriptMap::const_iterator itr = m_EventIdScripts.find(eventId); - if (itr != m_EventIdScripts.end()) - return itr->second; - - return 0; -} - -char const* ScriptMgr::GetScriptLibraryVersion() const -{ - if (!m_pGetScriptLibraryVersion) - return ""; - - return m_pGetScriptLibraryVersion(); -} - -CreatureAI* ScriptMgr::GetCreatureAI(Creature* pCreature) -{ - if (!m_pGetCreatureAI) - return NULL; - - return m_pGetCreatureAI(pCreature); -} - -InstanceData* ScriptMgr::CreateInstanceData(Map* pMap) -{ - if (!m_pCreateInstanceData) - return NULL; - - return m_pCreateInstanceData(pMap); -} - -bool ScriptMgr::OnGossipHello(Player* pPlayer, Creature* pCreature) -{ - return m_pOnGossipHello != NULL && m_pOnGossipHello(pPlayer, pCreature); -} - -bool ScriptMgr::OnGossipHello(Player* pPlayer, GameObject* pGameObject) -{ - return m_pOnGOGossipHello != NULL && m_pOnGOGossipHello(pPlayer, pGameObject); -} - -bool ScriptMgr::OnGossipSelect(Player* pPlayer, Creature* pCreature, uint32 sender, uint32 action, const char* code) -{ - if (code) - return m_pOnGossipSelectWithCode != NULL && m_pOnGossipSelectWithCode(pPlayer, pCreature, sender, action, code); - else - return m_pOnGossipSelect != NULL && m_pOnGossipSelect(pPlayer, pCreature, sender, action); -} - -bool ScriptMgr::OnGossipSelect(Player* pPlayer, GameObject* pGameObject, uint32 sender, uint32 action, const char* code) -{ - if (code) - return m_pOnGOGossipSelectWithCode != NULL && m_pOnGOGossipSelectWithCode(pPlayer, pGameObject, sender, action, code); - else - return m_pOnGOGossipSelect != NULL && m_pOnGOGossipSelect(pPlayer, pGameObject, sender, action); -} - -bool ScriptMgr::OnQuestAccept(Player* pPlayer, Creature* pCreature, Quest const* pQuest) -{ - return m_pOnQuestAccept != NULL && m_pOnQuestAccept(pPlayer, pCreature, pQuest); -} - -bool ScriptMgr::OnQuestAccept(Player* pPlayer, GameObject* pGameObject, Quest const* pQuest) -{ - return m_pOnGOQuestAccept != NULL && m_pOnGOQuestAccept(pPlayer, pGameObject, pQuest); -} - -bool ScriptMgr::OnQuestAccept(Player* pPlayer, Item* pItem, Quest const* pQuest) -{ - return m_pOnItemQuestAccept != NULL && m_pOnItemQuestAccept(pPlayer, pItem, pQuest); -} - -bool ScriptMgr::OnQuestRewarded(Player* pPlayer, Creature* pCreature, Quest const* pQuest) -{ - return m_pOnQuestRewarded != NULL && m_pOnQuestRewarded(pPlayer, pCreature, pQuest); -} - -bool ScriptMgr::OnQuestRewarded(Player* pPlayer, GameObject* pGameObject, Quest const* pQuest) -{ - return m_pOnGOQuestRewarded != NULL && m_pOnGOQuestRewarded(pPlayer, pGameObject, pQuest); -} - -uint32 ScriptMgr::GetDialogStatus(Player* pPlayer, Creature* pCreature) -{ - if (!m_pGetNPCDialogStatus) - return DIALOG_STATUS_UNDEFINED; - - return m_pGetNPCDialogStatus(pPlayer, pCreature); -} - -uint32 ScriptMgr::GetDialogStatus(Player* pPlayer, GameObject* pGameObject) -{ - if (!m_pGetGODialogStatus) - return DIALOG_STATUS_UNDEFINED; - - return m_pGetGODialogStatus(pPlayer, pGameObject); -} - -bool ScriptMgr::OnGameObjectUse(Player* pPlayer, GameObject* pGameObject) -{ - return m_pOnGOUse != NULL && m_pOnGOUse(pPlayer, pGameObject); -} - -bool ScriptMgr::OnItemUse(Player* pPlayer, Item* pItem, SpellCastTargets const& targets) -{ - return m_pOnItemUse != NULL && m_pOnItemUse(pPlayer, pItem, targets); -} - -bool ScriptMgr::OnAreaTrigger(Player* pPlayer, AreaTriggerEntry const* atEntry) -{ - return m_pOnAreaTrigger != NULL && m_pOnAreaTrigger(pPlayer, atEntry); -} - -bool ScriptMgr::OnProcessEvent(uint32 eventId, Object* pSource, Object* pTarget, bool isStart) -{ - return m_pOnProcessEvent != NULL && m_pOnProcessEvent(eventId, pSource, pTarget, isStart); -} - -bool ScriptMgr::OnEffectDummy(Unit* pCaster, uint32 spellId, SpellEffectIndex effIndex, Creature* pTarget, ObjectGuid originalCasterGuid) -{ - return m_pOnEffectDummyCreature != NULL && m_pOnEffectDummyCreature(pCaster, spellId, effIndex, pTarget, originalCasterGuid); -} - -bool ScriptMgr::OnEffectDummy(Unit* pCaster, uint32 spellId, SpellEffectIndex effIndex, GameObject* pTarget, ObjectGuid originalCasterGuid) -{ - return m_pOnEffectDummyGO != NULL && m_pOnEffectDummyGO(pCaster, spellId, effIndex, pTarget, originalCasterGuid); -} - -bool ScriptMgr::OnEffectDummy(Unit* pCaster, uint32 spellId, SpellEffectIndex effIndex, Item* pTarget, ObjectGuid originalCasterGuid) -{ - return m_pOnEffectDummyItem != NULL && m_pOnEffectDummyItem(pCaster, spellId, effIndex, pTarget, originalCasterGuid); -} - -bool ScriptMgr::OnEffectScriptEffect(Unit* pCaster, uint32 spellId, SpellEffectIndex effIndex, Creature* pTarget, ObjectGuid originalCasterGuid) -{ - return m_pOnEffectScriptEffectCreature != NULL && m_pOnEffectScriptEffectCreature(pCaster, spellId, effIndex, pTarget, originalCasterGuid); -} - -bool ScriptMgr::OnAuraDummy(Aura const* pAura, bool apply) -{ - return m_pOnAuraDummy != NULL && m_pOnAuraDummy(pAura, apply); -} - -ScriptLoadResult ScriptMgr::LoadScriptLibrary(const char* libName) -{ - UnloadScriptLibrary(); - - std::string name = libName; - name = MANGOS_SCRIPT_PREFIX + name + MANGOS_SCRIPT_SUFFIX; - - m_hScriptLib = MANGOS_LOAD_LIBRARY(name.c_str()); - - if (!m_hScriptLib) - return SCRIPT_LOAD_ERR_NOT_FOUND; - -# define GET_SCRIPT_HOOK_PTR(P,N) \ - GetScriptHookPtr((P), (N)); \ - if (!(P)) \ - { \ - /* prevent call before init */ \ - m_pOnFreeScriptLibrary = NULL; \ - UnloadScriptLibrary(); \ - return SCRIPT_LOAD_ERR_WRONG_API; \ - } - - // let check used mangosd revision for build library (unsafe use with different revision because changes in inline functions, define and etc) - char const* (MANGOS_IMPORT * pGetMangosRevStr)(); - - GET_SCRIPT_HOOK_PTR(pGetMangosRevStr, "GetMangosRevStr"); - - GET_SCRIPT_HOOK_PTR(m_pOnInitScriptLibrary, "InitScriptLibrary"); - GET_SCRIPT_HOOK_PTR(m_pOnFreeScriptLibrary, "FreeScriptLibrary"); - GET_SCRIPT_HOOK_PTR(m_pGetScriptLibraryVersion, "GetScriptLibraryVersion"); - - GET_SCRIPT_HOOK_PTR(m_pGetCreatureAI, "GetCreatureAI"); - GET_SCRIPT_HOOK_PTR(m_pCreateInstanceData, "CreateInstanceData"); - - GET_SCRIPT_HOOK_PTR(m_pOnGossipHello, "GossipHello"); - GET_SCRIPT_HOOK_PTR(m_pOnGOGossipHello, "GOGossipHello"); - GET_SCRIPT_HOOK_PTR(m_pOnGossipSelect, "GossipSelect"); - GET_SCRIPT_HOOK_PTR(m_pOnGOGossipSelect, "GOGossipSelect"); - GET_SCRIPT_HOOK_PTR(m_pOnGossipSelectWithCode, "GossipSelectWithCode"); - GET_SCRIPT_HOOK_PTR(m_pOnGOGossipSelectWithCode, "GOGossipSelectWithCode"); - GET_SCRIPT_HOOK_PTR(m_pOnQuestAccept, "QuestAccept"); - GET_SCRIPT_HOOK_PTR(m_pOnGOQuestAccept, "GOQuestAccept"); - GET_SCRIPT_HOOK_PTR(m_pOnItemQuestAccept, "ItemQuestAccept"); - GET_SCRIPT_HOOK_PTR(m_pOnQuestRewarded, "QuestRewarded"); - GET_SCRIPT_HOOK_PTR(m_pOnGOQuestRewarded, "GOQuestRewarded"); - GET_SCRIPT_HOOK_PTR(m_pGetNPCDialogStatus, "GetNPCDialogStatus"); - GET_SCRIPT_HOOK_PTR(m_pGetGODialogStatus, "GetGODialogStatus"); - GET_SCRIPT_HOOK_PTR(m_pOnGOUse, "GOUse"); - GET_SCRIPT_HOOK_PTR(m_pOnItemUse, "ItemUse"); - GET_SCRIPT_HOOK_PTR(m_pOnAreaTrigger, "AreaTrigger"); - GET_SCRIPT_HOOK_PTR(m_pOnProcessEvent, "ProcessEvent"); - GET_SCRIPT_HOOK_PTR(m_pOnEffectDummyCreature, "EffectDummyCreature"); - GET_SCRIPT_HOOK_PTR(m_pOnEffectDummyGO, "EffectDummyGameObject"); - GET_SCRIPT_HOOK_PTR(m_pOnEffectDummyItem, "EffectDummyItem"); - GET_SCRIPT_HOOK_PTR(m_pOnEffectScriptEffectCreature, "EffectScriptEffectCreature"); - GET_SCRIPT_HOOK_PTR(m_pOnAuraDummy, "AuraDummy"); - -# undef GET_SCRIPT_HOOK_PTR - - if (strcmp(pGetMangosRevStr(), REVISION_NR) != 0) - { - m_pOnFreeScriptLibrary = NULL; // prevent call before init - UnloadScriptLibrary(); - return SCRIPT_LOAD_ERR_OUTDATED; - } - - m_pOnInitScriptLibrary(); - return SCRIPT_LOAD_OK; -} - -void ScriptMgr::UnloadScriptLibrary() -{ - if (!m_hScriptLib) - return; - - if (m_pOnFreeScriptLibrary) - m_pOnFreeScriptLibrary(); - - MANGOS_CLOSE_LIBRARY(m_hScriptLib); - m_hScriptLib = NULL; - - m_pOnInitScriptLibrary = NULL; - m_pOnFreeScriptLibrary = NULL; - m_pGetScriptLibraryVersion = NULL; - - m_pGetCreatureAI = NULL; - m_pCreateInstanceData = NULL; - - m_pOnGossipHello = NULL; - m_pOnGOGossipHello = NULL; - m_pOnGossipSelect = NULL; - m_pOnGOGossipSelect = NULL; - m_pOnGossipSelectWithCode = NULL; - m_pOnGOGossipSelectWithCode = NULL; - m_pOnQuestAccept = NULL; - m_pOnGOQuestAccept = NULL; - m_pOnItemQuestAccept = NULL; - m_pOnQuestRewarded = NULL; - m_pOnGOQuestRewarded = NULL; - m_pGetNPCDialogStatus = NULL; - m_pGetGODialogStatus = NULL; - m_pOnGOUse = NULL; - m_pOnItemUse = NULL; - m_pOnAreaTrigger = NULL; - m_pOnProcessEvent = NULL; - m_pOnEffectDummyCreature = NULL; - m_pOnEffectDummyGO = NULL; - m_pOnEffectDummyItem = NULL; - m_pOnEffectScriptEffectCreature = NULL; - m_pOnAuraDummy = NULL; -} - -void ScriptMgr::CollectPossibleEventIds(std::set& eventIds) -{ - // Load all possible script entries from gameobjects - for (SQLStorageBase::SQLSIterator itr = sGOStorage.getDataBegin(); itr < sGOStorage.getDataEnd(); ++itr) - { - switch (itr->type) - { - case GAMEOBJECT_TYPE_GOOBER: - eventIds.insert(itr->goober.eventId); - break; - case GAMEOBJECT_TYPE_CHEST: - eventIds.insert(itr->chest.eventId); - break; - case GAMEOBJECT_TYPE_CAMERA: - eventIds.insert(itr->camera.eventID); - break; - case GAMEOBJECT_TYPE_CAPTURE_POINT: - eventIds.insert(itr->capturePoint.neutralEventID1); - eventIds.insert(itr->capturePoint.neutralEventID2); - eventIds.insert(itr->capturePoint.contestedEventID1); - eventIds.insert(itr->capturePoint.contestedEventID2); - eventIds.insert(itr->capturePoint.progressEventID1); - eventIds.insert(itr->capturePoint.progressEventID2); - eventIds.insert(itr->capturePoint.winEventID1); - eventIds.insert(itr->capturePoint.winEventID2); - break; - case GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING: - eventIds.insert(itr->destructibleBuilding.damagedEvent); - eventIds.insert(itr->destructibleBuilding.destroyedEvent); - eventIds.insert(itr->destructibleBuilding.intactEvent); - eventIds.insert(itr->destructibleBuilding.rebuildingEvent); - break; - default: - break; - } - } - - // Load all possible script entries from spells - for (uint32 i = 1; i < sSpellStore.GetNumRows(); ++i) - { - SpellEntry const* spell = sSpellStore.LookupEntry(i); - if (spell) - { - for (int j = 0; j < MAX_EFFECT_INDEX; ++j) - { - SpellEffectEntry const* spellEffect = spell->GetSpellEffect(SpellEffectIndex(j)); - if (!spellEffect) - continue; - - if (spellEffect->Effect == SPELL_EFFECT_SEND_EVENT) - { - if (spellEffect->EffectMiscValue) - eventIds.insert(spellEffect->EffectMiscValue); - } - } - } - } - - // Load all possible event entries from taxi path nodes - for (size_t path_idx = 0; path_idx < sTaxiPathNodesByPath.size(); ++path_idx) - { - for (size_t node_idx = 0; node_idx < sTaxiPathNodesByPath[path_idx].size(); ++node_idx) - { - TaxiPathNodeEntry const& node = sTaxiPathNodesByPath[path_idx][node_idx]; - - if (node.arrivalEventID) - eventIds.insert(node.arrivalEventID); - - if (node.departureEventID) - eventIds.insert(node.departureEventID); - } - } -} - -// Starters for events -bool StartEvents_Event(Map* map, uint32 id, Object* source, Object* target, bool isStart/*=true*/, Unit* forwardToPvp/*=NULL*/) -{ - MANGOS_ASSERT(source); - - // Handle SD2 script - if (sScriptMgr.OnProcessEvent(id, source, target, isStart)) - return true; - - // Handle PvP Calls - if (forwardToPvp && source->GetTypeId() == TYPEID_GAMEOBJECT) - { - BattleGround* bg = NULL; - OutdoorPvP* opvp = NULL; - if (forwardToPvp->GetTypeId() == TYPEID_PLAYER) - { - bg = ((Player*)forwardToPvp)->GetBattleGround(); - if (!bg) - opvp = sOutdoorPvPMgr.GetScript(((Player*)forwardToPvp)->GetCachedZoneId()); - } - else - { - if (map->IsBattleGroundOrArena()) - bg = ((BattleGroundMap*)map)->GetBG(); - else // Use the go, because GOs don't move - opvp = sOutdoorPvPMgr.GetScript(((GameObject*)source)->GetZoneId()); - } - - if (bg && bg->HandleEvent(id, static_cast(source))) - return true; - - if (opvp && opvp->HandleEvent(id, static_cast(source))) - return true; - } - - Map::ScriptExecutionParam execParam = Map::SCRIPT_EXEC_PARAM_UNIQUE_BY_SOURCE_TARGET; - if (source->isType(TYPEMASK_CREATURE_OR_GAMEOBJECT)) - execParam = Map::SCRIPT_EXEC_PARAM_UNIQUE_BY_SOURCE; - else if (target && target->isType(TYPEMASK_CREATURE_OR_GAMEOBJECT)) - execParam = Map::SCRIPT_EXEC_PARAM_UNIQUE_BY_TARGET; - - return map->ScriptsStart(sEventScripts, id, source, target, execParam); -} - -// Wrappers -uint32 GetAreaTriggerScriptId(uint32 triggerId) -{ - return sScriptMgr.GetAreaTriggerScriptId(triggerId); -} - -uint32 GetEventIdScriptId(uint32 eventId) -{ - return sScriptMgr.GetEventIdScriptId(eventId); -} - -uint32 GetScriptId(const char* name) -{ - return sScriptMgr.GetScriptId(name); -} - -char const* GetScriptName(uint32 id) -{ - return sScriptMgr.GetScriptName(id); -} - -uint32 GetScriptIdsCount() -{ - return sScriptMgr.GetScriptIdsCount(); -} - diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp deleted file mode 100644 index 7cd553f7c..000000000 --- a/src/game/Spell.cpp +++ /dev/null @@ -1,8284 +0,0 @@ -/** - * This code is part of MaNGOS. Contributor & Copyright details are in AUTHORS/THANKS. - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Spell.h" -#include "Database/DatabaseEnv.h" -#include "WorldPacket.h" -#include "WorldSession.h" -#include "GridNotifiers.h" -#include "GridNotifiersImpl.h" -#include "Opcodes.h" -#include "Log.h" -#include "UpdateMask.h" -#include "World.h" -#include "ObjectMgr.h" -#include "SpellMgr.h" -#include "Player.h" -#include "Pet.h" -#include "Unit.h" -#include "DynamicObject.h" -#include "Group.h" -#include "UpdateData.h" -#include "MapManager.h" -#include "ObjectAccessor.h" -#include "CellImpl.h" -#include "Policies/Singleton.h" -#include "SharedDefines.h" -#include "LootMgr.h" -#include "VMapFactory.h" -#include "BattleGround/BattleGround.h" -#include "Util.h" -#include "Chat.h" -#include "DB2Stores.h" -#include "SQLStorages.h" -#include "Vehicle.h" -#include "TemporarySummon.h" -#include "SQLStorages.h" - -extern pEffect SpellEffects[TOTAL_SPELL_EFFECTS]; - -class PrioritizeManaUnitWraper -{ - public: - explicit PrioritizeManaUnitWraper(Unit* unit) : i_unit(unit) - { - uint32 maxmana = unit->GetMaxPower(POWER_MANA); - i_percent = maxmana ? unit->GetPower(POWER_MANA) * 100 / maxmana : 101; - } - Unit* getUnit() const { return i_unit; } - uint32 getPercent() const { return i_percent; } - private: - Unit* i_unit; - uint32 i_percent; -}; - -struct PrioritizeMana -{ - int operator()(PrioritizeManaUnitWraper const& x, PrioritizeManaUnitWraper const& y) const - { - return x.getPercent() > y.getPercent(); - } -}; - -typedef std::priority_queue, PrioritizeMana> PrioritizeManaUnitQueue; - -class PrioritizeHealthUnitWraper -{ - public: - explicit PrioritizeHealthUnitWraper(Unit* unit) : i_unit(unit) - { - i_percent = unit->GetHealth() * 100 / unit->GetMaxHealth(); - } - Unit* getUnit() const { return i_unit; } - uint32 getPercent() const { return i_percent; } - private: - Unit* i_unit; - uint32 i_percent; -}; - -struct PrioritizeHealth -{ - int operator()(PrioritizeHealthUnitWraper const& x, PrioritizeHealthUnitWraper const& y) const - { - return x.getPercent() > y.getPercent(); - } -}; - -typedef std::priority_queue, PrioritizeHealth> PrioritizeHealthUnitQueue; - -bool IsQuestTameSpell(uint32 spellId) -{ - SpellEntry const* spellproto = sSpellStore.LookupEntry(spellId); - if (!spellproto) - return false; - - SpellEffectEntry const* spellEffect0 = spellproto->GetSpellEffect(EFFECT_INDEX_0); - SpellEffectEntry const* spellEffect1 = spellproto->GetSpellEffect(EFFECT_INDEX_1); - return spellEffect0 && spellEffect0->Effect == SPELL_EFFECT_THREAT && - spellEffect1 && spellEffect1->Effect == SPELL_EFFECT_APPLY_AURA && spellEffect1->EffectApplyAuraName == SPELL_AURA_DUMMY; -} - -SpellCastTargets::SpellCastTargets() -{ - m_unitTarget = NULL; - m_itemTarget = NULL; - m_GOTarget = NULL; - - m_itemTargetEntry = 0; - - m_srcX = m_srcY = m_srcZ = m_destX = m_destY = m_destZ = 0.0f; - m_strTarget = ""; - m_targetMask = 0; - - m_elevation = 0.0f; - m_speed = 0.0f; -} - -SpellCastTargets::~SpellCastTargets() -{ -} - -void SpellCastTargets::setUnitTarget(Unit* target) -{ - if (!target) - return; - - m_destX = target->GetPositionX(); - m_destY = target->GetPositionY(); - m_destZ = target->GetPositionZ(); - m_unitTarget = target; - m_unitTargetGUID = target->GetObjectGuid(); - m_targetMask |= TARGET_FLAG_UNIT; -} - -void SpellCastTargets::setDestination(float x, float y, float z) -{ - m_destX = x; - m_destY = y; - m_destZ = z; - m_targetMask |= TARGET_FLAG_DEST_LOCATION; -} - -void SpellCastTargets::setSource(float x, float y, float z) -{ - m_srcX = x; - m_srcY = y; - m_srcZ = z; - m_targetMask |= TARGET_FLAG_SOURCE_LOCATION; -} - -void SpellCastTargets::setGOTarget(GameObject* target) -{ - m_GOTarget = target; - m_GOTargetGUID = target->GetObjectGuid(); - // m_targetMask |= TARGET_FLAG_OBJECT; -} - -void SpellCastTargets::setItemTarget(Item* item) -{ - if (!item) - return; - - m_itemTarget = item; - m_itemTargetGUID = item->GetObjectGuid(); - m_itemTargetEntry = item->GetEntry(); - m_targetMask |= TARGET_FLAG_ITEM; -} - -void SpellCastTargets::setTradeItemTarget(Player* caster) -{ - m_itemTargetGUID = ObjectGuid(uint64(TRADE_SLOT_NONTRADED)); - m_itemTargetEntry = 0; - m_targetMask |= TARGET_FLAG_TRADE_ITEM; - - Update(caster); -} - -void SpellCastTargets::setCorpseTarget(Corpse* corpse) -{ - m_CorpseTargetGUID = corpse->GetObjectGuid(); -} - -void SpellCastTargets::Update(Unit* caster) -{ - m_GOTarget = m_GOTargetGUID ? caster->GetMap()->GetGameObject(m_GOTargetGUID) : NULL; - m_unitTarget = m_unitTargetGUID ? - (m_unitTargetGUID == caster->GetObjectGuid() ? caster : ObjectAccessor::GetUnit(*caster, m_unitTargetGUID)) : - NULL; - - m_itemTarget = NULL; - if (caster->GetTypeId() == TYPEID_PLAYER) -{ - Player* player = ((Player*)caster); - - if (m_targetMask & TARGET_FLAG_ITEM) - m_itemTarget = player->GetItemByGuid(m_itemTargetGUID); - else if (m_targetMask & TARGET_FLAG_TRADE_ITEM) - { - if (TradeData* pTrade = player->GetTradeData()) - if (m_itemTargetGUID.GetRawValue() < TRADE_SLOT_COUNT) - m_itemTarget = pTrade->GetTraderData()->GetItem(TradeSlots(m_itemTargetGUID.GetRawValue())); - } - - if (m_itemTarget) - m_itemTargetEntry = m_itemTarget->GetEntry(); - } -} - -void SpellCastTargets::read(ByteBuffer& data, Unit* caster) -{ - data >> m_targetMask; - - if (m_targetMask == TARGET_FLAG_SELF) - { - m_destX = caster->GetPositionX(); - m_destY = caster->GetPositionY(); - m_destZ = caster->GetPositionZ(); - m_unitTarget = caster; - m_unitTargetGUID = caster->GetObjectGuid(); - return; - } - - // TARGET_FLAG_UNK2 is used for non-combat pets, maybe other? - if (m_targetMask & (TARGET_FLAG_UNIT | TARGET_FLAG_UNK2)) - data >> m_unitTargetGUID.ReadAsPacked(); - - if (m_targetMask & (TARGET_FLAG_OBJECT)) - data >> m_GOTargetGUID.ReadAsPacked(); - - if ((m_targetMask & (TARGET_FLAG_ITEM | TARGET_FLAG_TRADE_ITEM)) && caster->GetTypeId() == TYPEID_PLAYER) - data >> m_itemTargetGUID.ReadAsPacked(); - - if (m_targetMask & (TARGET_FLAG_CORPSE | TARGET_FLAG_PVP_CORPSE)) - data >> m_CorpseTargetGUID.ReadAsPacked(); - - if (m_targetMask & TARGET_FLAG_SOURCE_LOCATION) - { - data >> m_srcTransportGUID.ReadAsPacked(); - data >> m_srcX >> m_srcY >> m_srcZ; - if (!MaNGOS::IsValidMapCoord(m_srcX, m_srcY, m_srcZ)) - throw ByteBufferException(false, data.rpos(), 0, data.size()); - } - - if (m_targetMask & TARGET_FLAG_DEST_LOCATION) - { - data >> m_destTransportGUID.ReadAsPacked(); - data >> m_destX >> m_destY >> m_destZ; - if (!MaNGOS::IsValidMapCoord(m_destX, m_destY, m_destZ)) - throw ByteBufferException(false, data.rpos(), 0, data.size()); - } - - if (m_targetMask & TARGET_FLAG_STRING) - data >> m_strTarget; - - // find real units/GOs - Update(caster); -} - -void SpellCastTargets::write(ByteBuffer& data) const -{ - data << uint32(m_targetMask); - - if (m_targetMask & (TARGET_FLAG_UNIT | TARGET_FLAG_PVP_CORPSE | TARGET_FLAG_OBJECT | TARGET_FLAG_CORPSE | TARGET_FLAG_UNK2)) - { - if (m_targetMask & TARGET_FLAG_UNIT) - { - if (m_unitTarget) - data << m_unitTarget->GetPackGUID(); - else - data << uint8(0); - } - else if (m_targetMask & TARGET_FLAG_OBJECT) - { - if (m_GOTarget) - data << m_GOTarget->GetPackGUID(); - else - data << uint8(0); - } - else if (m_targetMask & (TARGET_FLAG_CORPSE | TARGET_FLAG_PVP_CORPSE)) - data << m_CorpseTargetGUID.WriteAsPacked(); - else - data << uint8(0); - } - - if (m_targetMask & (TARGET_FLAG_ITEM | TARGET_FLAG_TRADE_ITEM)) - { - if (m_itemTarget) - data << m_itemTarget->GetPackGUID(); - else - data << uint8(0); - } - - if (m_targetMask & TARGET_FLAG_SOURCE_LOCATION) - { - data << m_srcTransportGUID.WriteAsPacked(); - data << m_srcX << m_srcY << m_srcZ; - } - - if (m_targetMask & TARGET_FLAG_DEST_LOCATION) - { - data << m_destTransportGUID.WriteAsPacked(); - data << m_destX << m_destY << m_destZ; - } - - if (m_targetMask & TARGET_FLAG_STRING) - data << m_strTarget; -} - -void SpellCastTargets::ReadAdditionalData(WorldPacket& data, uint8& cast_flags) -{ - if (cast_flags & 0x02) // has trajectory data - { - data >> m_elevation >> m_speed; - - bool hasMovementData; - data >> hasMovementData; - if (hasMovementData) - { - MovementInfo mi; - data >> mi; - setSource(mi.GetPos()->x, mi.GetPos()->y, mi.GetPos()->z); - } - } - else if (cast_flags & 0x08) // has archaeology weight - { - uint32 count; - uint8 type; - data >> count; - for (uint32 i = 0; i < count; ++i) - { - data >> type; - switch (type) - { - case 1: // Fragments - data >> Unused(); // Currency entry - data >> Unused(); // Currency count - break; - case 2: // Keystones - data >> Unused(); // Item entry - data >> Unused(); // Item count - break; - } - } - } -} - -Spell::Spell(Unit* caster, SpellEntry const* info, bool triggered, ObjectGuid originalCasterGUID, SpellEntry const* triggeredBy) -{ - MANGOS_ASSERT(caster != NULL && info != NULL); - MANGOS_ASSERT(info == sSpellStore.LookupEntry(info->Id) && "`info` must be pointer to sSpellStore element"); - - if (info->SpellDifficultyId && caster->IsInWorld() && caster->GetMap()->IsDungeon()) - { - if (SpellEntry const* spellEntry = GetSpellEntryByDifficulty(info->SpellDifficultyId, caster->GetMap()->GetDifficulty(), caster->GetMap()->IsRaid())) - m_spellInfo = spellEntry; - else - m_spellInfo = info; - } - else - m_spellInfo = info; - - m_triggeredBySpellInfo = triggeredBy; - - m_spellInterrupts = m_spellInfo->GetSpellInterrupts(); - - m_caster = caster; - m_selfContainer = NULL; - m_referencedFromCurrentSpell = false; - m_executedCurrently = false; - m_delayStart = 0; - m_delayAtDamageCount = 0; - - m_applyMultiplierMask = 0; - - // Get data for type of attack - m_attackType = GetWeaponAttackType(m_spellInfo); - - m_spellSchoolMask = GetSpellSchoolMask(info); // Can be override for some spell (wand shoot for example) - - if (m_attackType == RANGED_ATTACK) - { - // wand case - if ((m_caster->getClassMask() & CLASSMASK_WAND_USERS) != 0 && m_caster->GetTypeId() == TYPEID_PLAYER) - { - if (Item* pItem = ((Player*)m_caster)->GetWeaponForAttack(RANGED_ATTACK)) - m_spellSchoolMask = SpellSchoolMask(1 << pItem->GetProto()->DamageType); - } - } - // Set health leech amount to zero - m_healthLeech = 0; - - m_originalCasterGUID = originalCasterGUID ? originalCasterGUID : m_caster->GetObjectGuid(); - - UpdateOriginalCasterPointer(); - - for (int i = 0; i < MAX_EFFECT_INDEX; ++i) - m_currentBasePoints[i] = m_spellInfo->CalculateSimpleValue(SpellEffectIndex(i)); - - m_spellState = SPELL_STATE_PREPARING; - - m_castPositionX = m_castPositionY = m_castPositionZ = 0; - m_TriggerSpells.clear(); - m_preCastSpells.clear(); - m_IsTriggeredSpell = triggered; - // m_AreaAura = false; - m_CastItem = NULL; - - unitTarget = NULL; - itemTarget = NULL; - gameObjTarget = NULL; - focusObject = NULL; - m_cast_count = 0; - m_glyphIndex = 0; - m_triggeredByAuraSpell = NULL; - - // Auto Shot & Shoot (wand) - m_autoRepeat = IsAutoRepeatRangedSpell(m_spellInfo); - - m_runesState = 0; - m_powerCost = 0; // setup to correct value in Spell::prepare, don't must be used before. - m_usedHolyPower = 0; - m_casttime = 0; // setup to correct value in Spell::prepare, don't must be used before. - m_timer = 0; // will set to cast time in prepare - m_duration = 0; - - m_needAliveTargetMask = 0; - - // determine reflection - m_canReflect = false; - - m_spellFlags = SPELL_FLAG_NORMAL; - - if (m_spellInfo->GetDmgClass() == SPELL_DAMAGE_CLASS_MAGIC && !m_spellInfo->HasAttribute(SPELL_ATTR_EX_CANT_REFLECTED)) - { - for (int j = 0; j < MAX_EFFECT_INDEX; ++j) - { - SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(SpellEffectIndex(j)); - if(!spellEffect) - continue; - if (spellEffect->Effect == 0) - continue; - - if(!IsPositiveTarget(spellEffect->EffectImplicitTargetA, spellEffect->EffectImplicitTargetB)) - m_canReflect = true; - else - m_canReflect = m_spellInfo->HasAttribute(SPELL_ATTR_EX_NEGATIVE); - - if (m_canReflect) - continue; - else - break; - } - } - - CleanupTargetList(); -} - -Spell::~Spell() -{ -} - -template -WorldObject* Spell::FindCorpseUsing() -{ - // non-standard target selection - SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex); - float max_range = GetSpellMaxRange(srange); - - WorldObject* result = NULL; - - T u_check(m_caster, max_range); - MaNGOS::WorldObjectSearcher searcher(result, u_check); - - Cell::VisitGridObjects(m_caster, searcher, max_range); - - if (!result) - Cell::VisitWorldObjects(m_caster, searcher, max_range); - - return result; -} - -void Spell::FillTargetMap() -{ - // TODO: ADD the correct target FILLS!!!!!! - - UnitList tmpUnitLists[MAX_EFFECT_INDEX]; // Stores the temporary Target Lists for each effect - uint8 effToIndex[MAX_EFFECT_INDEX] = {0, 1, 2}; // Helper array, to link to another tmpUnitList, if the targets for both effects match - for (int i = 0; i < MAX_EFFECT_INDEX; ++i) - { - SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(SpellEffectIndex(i)); - if(!spellEffect) - continue; - - // not call for empty effect. - // Also some spells use not used effect targets for store targets for dummy effect in triggered spells - if(spellEffect->Effect == SPELL_EFFECT_NONE) - continue; - - // targets for TARGET_SCRIPT_COORDINATES (A) and TARGET_SCRIPT - // for TARGET_FOCUS_OR_SCRIPTED_GAMEOBJECT (A) all is checked in Spell::CheckCast and in Spell::CheckItem - // filled in Spell::CheckCast call - if(spellEffect->EffectImplicitTargetA == TARGET_SCRIPT_COORDINATES || - spellEffect->EffectImplicitTargetA == TARGET_SCRIPT || - spellEffect->EffectImplicitTargetA == TARGET_FOCUS_OR_SCRIPTED_GAMEOBJECT || - (spellEffect->EffectImplicitTargetB == TARGET_SCRIPT && spellEffect->EffectImplicitTargetA != TARGET_SELF)) - continue; - - // TODO: find a way so this is not needed? - // for area auras always add caster as target (needed for totems for example) - if (IsAreaAuraEffect(spellEffect->Effect)) - AddUnitTarget(m_caster, SpellEffectIndex(i)); - - // no double fill for same targets - for (int j = 0; j < i; ++j) - { - SpellEffectEntry const* spellEffect1 = m_spellInfo->GetSpellEffect(SpellEffectIndex(j)); - if (!spellEffect1) - continue; - - // Check if same target, but handle i.e. AreaAuras different - if (spellEffect->EffectImplicitTargetA == spellEffect1->EffectImplicitTargetA && spellEffect->EffectImplicitTargetB == spellEffect1->EffectImplicitTargetB - && spellEffect1->Effect != SPELL_EFFECT_NONE - && !IsAreaAuraEffect(spellEffect->Effect) && !IsAreaAuraEffect(spellEffect1->Effect)) - // Add further conditions here if required - { - effToIndex[i] = j; // effect i has same targeting list as effect j - break; - } - } - - if (effToIndex[i] == i) // New target combination - { - // TargetA/TargetB dependent from each other, we not switch to full support this dependences - // but need it support in some know cases - switch(spellEffect->EffectImplicitTargetA) - { - case TARGET_NONE: - switch(spellEffect->EffectImplicitTargetB) - { - case TARGET_NONE: - if (m_caster->GetObjectGuid().IsPet()) - SetTargetMap(SpellEffectIndex(i), TARGET_SELF, tmpUnitLists[i /*==effToIndex[i]*/]); - else - SetTargetMap(SpellEffectIndex(i), TARGET_EFFECT_SELECT, tmpUnitLists[i /*==effToIndex[i]*/]); - break; - default: - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetB, tmpUnitLists[i /*==effToIndex[i]*/]); - break; - } - break; - case TARGET_SELF: - switch(spellEffect->EffectImplicitTargetB) - { - case TARGET_NONE: - case TARGET_EFFECT_SELECT: - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetA, tmpUnitLists[i /*==effToIndex[i]*/]); - break; - case TARGET_AREAEFFECT_INSTANT: // use B case that not dependent from from A in fact - if((m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) == 0) - m_targets.setDestination(m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ()); - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetB, tmpUnitLists[i /*==effToIndex[i]*/]); - break; - case TARGET_BEHIND_VICTIM: // use B case that not dependent from from A in fact - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetB, tmpUnitLists[i /*==effToIndex[i]*/]); - break; - default: - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetA, tmpUnitLists[i /*==effToIndex[i]*/]); - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetB, tmpUnitLists[i /*==effToIndex[i]*/]); - break; - } - break; - case TARGET_EFFECT_SELECT: - switch(spellEffect->EffectImplicitTargetB) - { - case TARGET_NONE: - case TARGET_EFFECT_SELECT: - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetA, tmpUnitLists[i /*==effToIndex[i]*/]); - break; - // dest point setup required - case TARGET_AREAEFFECT_INSTANT: - case TARGET_AREAEFFECT_CUSTOM: - case TARGET_ALL_ENEMY_IN_AREA: - case TARGET_ALL_ENEMY_IN_AREA_INSTANT: - case TARGET_ALL_ENEMY_IN_AREA_CHANNELED: - case TARGET_ALL_FRIENDLY_UNITS_IN_AREA: - case TARGET_AREAEFFECT_GO_AROUND_DEST: - case TARGET_RANDOM_NEARBY_DEST: - // triggered spells get dest point from default target set, ignore it - if (!(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) || m_IsTriggeredSpell) - if (WorldObject* castObject = GetCastingObject()) - m_targets.setDestination(castObject->GetPositionX(), castObject->GetPositionY(), castObject->GetPositionZ()); - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetB, tmpUnitLists[i /*==effToIndex[i]*/]); - break; - // target pre-selection required - case TARGET_INNKEEPER_COORDINATES: - case TARGET_TABLE_X_Y_Z_COORDINATES: - case TARGET_CASTER_COORDINATES: - case TARGET_SCRIPT_COORDINATES: - case TARGET_CURRENT_ENEMY_COORDINATES: - case TARGET_DUELVSPLAYER_COORDINATES: - case TARGET_DYNAMIC_OBJECT_COORDINATES: - case TARGET_POINT_AT_NORTH: - case TARGET_POINT_AT_SOUTH: - case TARGET_POINT_AT_EAST: - case TARGET_POINT_AT_WEST: - case TARGET_POINT_AT_NE: - case TARGET_POINT_AT_NW: - case TARGET_POINT_AT_SE: - case TARGET_POINT_AT_SW: - // need some target for processing - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetA, tmpUnitLists[i /*==effToIndex[i]*/]); - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetB, tmpUnitLists[i /*==effToIndex[i]*/]); - break; - default: - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetB, tmpUnitLists[i /*==effToIndex[i]*/]); - break; - } - break; - case TARGET_CASTER_COORDINATES: - switch(spellEffect->EffectImplicitTargetB) - { - case TARGET_ALL_ENEMY_IN_AREA: - // Note: this hack with search required until GO casting not implemented - // environment damage spells already have around enemies targeting but this not help in case nonexistent GO casting support - // currently each enemy selected explicitly and self cast damage - if (spellEffect->Effect == SPELL_EFFECT_ENVIRONMENTAL_DAMAGE) - { - if(m_targets.getUnitTarget()) - tmpUnitLists[i /*==effToIndex[i]*/].push_back(m_targets.getUnitTarget()); - } - else - { - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetA, tmpUnitLists[i /*==effToIndex[i]*/]); - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetB, tmpUnitLists[i /*==effToIndex[i]*/]); - } - break; - case 0: - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetA, tmpUnitLists[i /*==effToIndex[i]*/]); - tmpUnitLists[i /*==effToIndex[i]*/].push_back(m_caster); - break; - default: - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetA, tmpUnitLists[i /*==effToIndex[i]*/]); - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetB, tmpUnitLists[i /*==effToIndex[i]*/]); - break; - } - break; - case TARGET_TABLE_X_Y_Z_COORDINATES: - switch(spellEffect->EffectImplicitTargetB) - { - case 0: - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetA, tmpUnitLists[i /*==effToIndex[i]*/]); - - - // need some target for processing - SetTargetMap(SpellEffectIndex(i), TARGET_EFFECT_SELECT, tmpUnitLists[i /*==effToIndex[i]*/]); - break; - case TARGET_AREAEFFECT_INSTANT: // All 17/7 pairs used for dest teleportation, A processed in effect code - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetB, tmpUnitLists[i /*==effToIndex[i]*/]); - break; - default: - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetA, tmpUnitLists[i /*==effToIndex[i]*/]); - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetB, tmpUnitLists[i /*==effToIndex[i]*/]); - } - case TARGET_SELF2: - switch(spellEffect->EffectImplicitTargetB) - { - case TARGET_NONE: - case TARGET_EFFECT_SELECT: - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetA, tmpUnitLists[i /*==effToIndex[i]*/]); - break; - case TARGET_AREAEFFECT_CUSTOM: - // triggered spells get dest point from default target set, ignore it - if (!(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) || m_IsTriggeredSpell) - if (WorldObject* castObject = GetCastingObject()) - m_targets.setDestination(castObject->GetPositionX(), castObject->GetPositionY(), castObject->GetPositionZ()); - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetB, tmpUnitLists[i /*==effToIndex[i]*/]); - break; - // most A/B target pairs is self->negative and not expect adding caster to target list - default: - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetB, tmpUnitLists[i /*==effToIndex[i]*/]); - break; - } - break; - case TARGET_DUELVSPLAYER_COORDINATES: - switch(spellEffect->EffectImplicitTargetB) - { - case TARGET_NONE: - case TARGET_EFFECT_SELECT: - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetA, tmpUnitLists[i /*==effToIndex[i]*/]); - if (Unit* currentTarget = m_targets.getUnitTarget()) - tmpUnitLists[i /*==effToIndex[i]*/].push_back(currentTarget); - break; - default: - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetA, tmpUnitLists[i /*==effToIndex[i]*/]); - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetB, tmpUnitLists[i /*==effToIndex[i]*/]); - break; - } - break; - default: - switch(spellEffect->EffectImplicitTargetB) - { - case 0: - case TARGET_EFFECT_SELECT: - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetA, tmpUnitLists[i /*==effToIndex[i]*/]); - break; - case TARGET_SCRIPT_COORDINATES: // B case filled in CheckCast but we need fill unit list base at A case - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetA, tmpUnitLists[i /*==effToIndex[i]*/]); - break; - default: - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetA, tmpUnitLists[i /*==effToIndex[i]*/]); - SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetB, tmpUnitLists[i /*==effToIndex[i]*/]); - break; - } - break; - } - } - - if (m_caster->GetTypeId() == TYPEID_PLAYER) - { - Player* me = (Player*)m_caster; - for (UnitList::const_iterator itr = tmpUnitLists[effToIndex[i]].begin(); itr != tmpUnitLists[effToIndex[i]].end(); ++itr) - { - Player* targetOwner = (*itr)->GetCharmerOrOwnerPlayerOrPlayerItself(); - if (targetOwner && targetOwner != me && targetOwner->IsPvP() && !me->IsInDuelWith(targetOwner)) - { - me->UpdatePvP(true); - me->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); - break; - } - } - } - - for (UnitList::iterator itr = tmpUnitLists[effToIndex[i]].begin(); itr != tmpUnitLists[effToIndex[i]].end();) - { - if (!CheckTarget(*itr, SpellEffectIndex(i))) - { - itr = tmpUnitLists[effToIndex[i]].erase(itr); - continue; - } - else - ++itr; - } - - for (UnitList::const_iterator iunit = tmpUnitLists[effToIndex[i]].begin(); iunit != tmpUnitLists[effToIndex[i]].end(); ++iunit) - AddUnitTarget((*iunit), SpellEffectIndex(i)); - } -} - -void Spell::prepareDataForTriggerSystem() -{ - //========================================================================================== - // Now fill data for trigger system, need know: - // an spell trigger another or not ( m_canTrigger ) - // Create base triggers flags for Attacker and Victim ( m_procAttacker and m_procVictim) - //========================================================================================== - // Fill flag can spell trigger or not - // TODO: possible exist spell attribute for this - m_canTrigger = false; - - if (m_CastItem) - m_canTrigger = false; // Do not trigger from item cast spell - else if (!m_IsTriggeredSpell) - m_canTrigger = true; // Normal cast - can trigger - else if (!m_triggeredByAuraSpell) - m_canTrigger = true; // Triggered from SPELL_EFFECT_TRIGGER_SPELL - can trigger - - if (!m_canTrigger) // Exceptions (some periodic triggers) - { - switch (m_spellInfo->GetSpellFamilyName()) - { - case SPELLFAMILY_MAGE: - // Arcane Missles / Blizzard triggers need do it - if (m_spellInfo->IsFitToFamilyMask(UI64LIT(0x0000000000200080))) - m_canTrigger = true; - // Clearcasting trigger need do it - else if (m_spellInfo->IsFitToFamilyMask(UI64LIT(0x0000000200000000), 0x00000008)) - m_canTrigger = true; - // Replenish Mana, item spell with triggered cases (Mana Agate, etc mana gems) - else if (m_spellInfo->IsFitToFamilyMask(UI64LIT(0x0000010000000000))) - m_canTrigger = true; - break; - case SPELLFAMILY_WARLOCK: - // For Hellfire Effect / Rain of Fire / Seed of Corruption triggers need do it - if (m_spellInfo->IsFitToFamilyMask(UI64LIT(0x0000800000000060))) - m_canTrigger = true; - break; - case SPELLFAMILY_PRIEST: - // For Penance,Mind Sear,Mind Flay heal/damage triggers need do it - if (m_spellInfo->IsFitToFamilyMask(UI64LIT(0x0001800000800000), 0x00000040)) - m_canTrigger = true; - break; - case SPELLFAMILY_ROGUE: - // For poisons need do it - if (m_spellInfo->IsFitToFamilyMask(UI64LIT(0x000000101001E000))) - m_canTrigger = true; - break; - case SPELLFAMILY_HUNTER: - // Hunter Rapid Killing/Explosive Trap Effect/Immolation Trap Effect/Frost Trap Aura/Snake Trap Effect/Explosive Shot - if (m_spellInfo->IsFitToFamilyMask(UI64LIT(0x0100200000000214), 0x200)) - m_canTrigger = true; - break; - case SPELLFAMILY_PALADIN: - // For Judgements (all) / Holy Shock triggers need do it - if (m_spellInfo->IsFitToFamilyMask(UI64LIT(0x0001000900B80400))) - m_canTrigger = true; - break; - default: - break; - } - } - - // Get data for type of attack and fill base info for trigger - switch (m_spellInfo->GetDmgClass()) - { - case SPELL_DAMAGE_CLASS_MELEE: - m_procAttacker = PROC_FLAG_SUCCESSFUL_MELEE_SPELL_HIT; - if (m_attackType == OFF_ATTACK) - m_procAttacker |= PROC_FLAG_SUCCESSFUL_OFFHAND_HIT; - m_procVictim = PROC_FLAG_TAKEN_MELEE_SPELL_HIT; - break; - case SPELL_DAMAGE_CLASS_RANGED: - // Auto attack - if (m_spellInfo->HasAttribute(SPELL_ATTR_EX2_AUTOREPEAT_FLAG)) - { - m_procAttacker = PROC_FLAG_SUCCESSFUL_RANGED_HIT; - m_procVictim = PROC_FLAG_TAKEN_RANGED_HIT; - } - else // Ranged spell attack - { - m_procAttacker = PROC_FLAG_SUCCESSFUL_RANGED_SPELL_HIT; - m_procVictim = PROC_FLAG_TAKEN_RANGED_SPELL_HIT; - } - break; - default: - if (IsPositiveSpell(m_spellInfo->Id)) // Check for positive spell - { - m_procAttacker = PROC_FLAG_SUCCESSFUL_POSITIVE_SPELL; - m_procVictim = PROC_FLAG_TAKEN_POSITIVE_SPELL; - } - else if (m_spellInfo->HasAttribute(SPELL_ATTR_EX2_AUTOREPEAT_FLAG)) // Wands auto attack - { - m_procAttacker = PROC_FLAG_SUCCESSFUL_RANGED_HIT; - m_procVictim = PROC_FLAG_TAKEN_RANGED_HIT; - } - else // Negative spell - { - m_procAttacker = PROC_FLAG_SUCCESSFUL_NEGATIVE_SPELL_HIT; - m_procVictim = PROC_FLAG_TAKEN_NEGATIVE_SPELL_HIT; - } - break; - } - - // some negative spells have positive effects to another or same targets - // avoid triggering negative hit for only positive targets - m_negativeEffectMask = 0x0; - for (int i = 0; i < MAX_EFFECT_INDEX; ++i) - if (!IsPositiveEffect(m_spellInfo, SpellEffectIndex(i))) - m_negativeEffectMask |= (1 << i); - - // Hunter traps spells (for Entrapment trigger) - // Gives your Immolation Trap, Frost Trap, Explosive Trap, and Snake Trap .... - if (m_spellInfo->IsFitToFamily(SPELLFAMILY_HUNTER, UI64LIT(0x000020000000001C))) - m_procAttacker |= PROC_FLAG_ON_TRAP_ACTIVATION; -} - -void Spell::CleanupTargetList() -{ - m_UniqueTargetInfo.clear(); - m_UniqueGOTargetInfo.clear(); - m_UniqueItemInfo.clear(); - m_delayMoment = 0; -} - -void Spell::AddUnitTarget(Unit* pVictim, SpellEffectIndex effIndex) -{ - SpellEffectEntry const *spellEffect = m_spellInfo->GetSpellEffect(effIndex); - if (!spellEffect || spellEffect->Effect == 0) - return; - - // Check for effect immune skip if immuned - bool immuned = pVictim->IsImmuneToSpellEffect(m_spellInfo, effIndex, pVictim == m_caster); - - if (pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->IsTotem() && (m_spellFlags & SPELL_FLAG_REDIRECTED)) - immuned = false; - - ObjectGuid targetGUID = pVictim->GetObjectGuid(); - - // Lookup target in already in list - for (TargetList::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - { - if (targetGUID == ihit->targetGUID) // Found in list - { - if (!immuned) - ihit->effectMask |= 1 << effIndex; // Add only effect mask if not immuned - return; - } - } - - // This is new target calculate data for him - - // Get spell hit result on target - TargetInfo target; - target.targetGUID = targetGUID; // Store target GUID - target.effectMask = immuned ? 0 : (1 << effIndex); // Store index of effect if not immuned - target.processed = false; // Effects not applied on target - - // Calculate hit result - target.missCondition = m_caster->SpellHitResult(pVictim, m_spellInfo, m_canReflect); - - // spell fly from visual cast object - WorldObject* affectiveObject = GetAffectiveCasterObject(); - - // Spell have speed (possible inherited from triggering spell) - need calculate incoming time - float speed = m_spellInfo->speed == 0.0f && m_triggeredBySpellInfo ? m_triggeredBySpellInfo->speed : m_spellInfo->speed; - if (speed > 0.0f && affectiveObject && (pVictim != affectiveObject || (m_targets.m_targetMask & (TARGET_FLAG_SOURCE_LOCATION | TARGET_FLAG_DEST_LOCATION)))) - { - // calculate spell incoming interval - float dist = 0.0f; // distance to impact - if (pVictim == affectiveObject) // Calculate dist to destination target also for self-cast spells - { - if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) - dist = affectiveObject->GetDistance(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ); - else // Must have Source Target - dist = affectiveObject->GetDistance(m_targets.m_srcX, m_targets.m_srcY, m_targets.m_srcZ); - } - else // normal unit target, take distance - dist = affectiveObject->GetDistance(pVictim->GetPositionX(), pVictim->GetPositionY(), pVictim->GetPositionZ()); - - if (dist < 5.0f) - dist = 5.0f; - target.timeDelay = (uint64) floor(dist / speed * 1000.0f); - - // Calculate minimum incoming time - if (m_delayMoment == 0 || m_delayMoment > target.timeDelay) - m_delayMoment = target.timeDelay; - } - // Spell casted on self - mostly TRIGGER_MISSILE code - else if (m_spellInfo->speed > 0.0f && affectiveObject && pVictim == affectiveObject) - { - float dist = 0.0f; - if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) - dist = affectiveObject->GetDistance(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ); - - target.timeDelay = (uint64) floor(dist / m_spellInfo->speed * 1000.0f); - } - else - target.timeDelay = UI64LIT(0); - - // If target reflect spell back to caster - if (target.missCondition == SPELL_MISS_REFLECT) - { - // Calculate reflected spell result on caster - target.reflectResult = m_caster->SpellHitResult(m_caster, m_spellInfo, m_canReflect); - - if (target.reflectResult == SPELL_MISS_REFLECT) // Impossible reflect again, so simply deflect spell - target.reflectResult = SPELL_MISS_PARRY; - - // Increase time interval for reflected spells by 1.5 - target.timeDelay += target.timeDelay >> 1; - - m_spellFlags |= SPELL_FLAG_REFLECTED; - } - else - target.reflectResult = SPELL_MISS_NONE; - - // Add target to list - m_UniqueTargetInfo.push_back(target); -} - -void Spell::AddUnitTarget(ObjectGuid unitGuid, SpellEffectIndex effIndex) -{ - if (Unit* unit = m_caster->GetObjectGuid() == unitGuid ? m_caster : ObjectAccessor::GetUnit(*m_caster, unitGuid)) - AddUnitTarget(unit, effIndex); -} - -void Spell::AddGOTarget(GameObject* pVictim, SpellEffectIndex effIndex) -{ - SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(effIndex); - if (!spellEffect || spellEffect->Effect == 0) - return; - - ObjectGuid targetGUID = pVictim->GetObjectGuid(); - - // Lookup target in already in list - for (GOTargetList::iterator ihit = m_UniqueGOTargetInfo.begin(); ihit != m_UniqueGOTargetInfo.end(); ++ihit) - { - if (targetGUID == ihit->targetGUID) // Found in list - { - ihit->effectMask |= (1 << effIndex); // Add only effect mask - return; - } - } - - // This is new target calculate data for him - - GOTargetInfo target; - target.targetGUID = targetGUID; - target.effectMask = (1 << effIndex); - target.processed = false; // Effects not apply on target - - // spell fly from visual cast object - WorldObject* affectiveObject = GetAffectiveCasterObject(); - - // Spell can have speed - need calculate incoming time - float speed = m_spellInfo->speed == 0.0f && m_triggeredBySpellInfo ? m_triggeredBySpellInfo->speed : m_spellInfo->speed; - if (speed > 0.0f && affectiveObject && pVictim != affectiveObject) - { - // calculate spell incoming interval - float dist = affectiveObject->GetDistance(pVictim->GetPositionX(), pVictim->GetPositionY(), pVictim->GetPositionZ()); - if (dist < 5.0f) - dist = 5.0f; - target.timeDelay = (uint64) floor(dist / speed * 1000.0f); - if (m_delayMoment == 0 || m_delayMoment > target.timeDelay) - m_delayMoment = target.timeDelay; - } - else - target.timeDelay = UI64LIT(0); - - // Add target to list - m_UniqueGOTargetInfo.push_back(target); -} - -void Spell::AddGOTarget(ObjectGuid goGuid, SpellEffectIndex effIndex) -{ - if (GameObject* go = m_caster->GetMap()->GetGameObject(goGuid)) - AddGOTarget(go, effIndex); -} - -void Spell::AddItemTarget(Item* pitem, SpellEffectIndex effIndex) -{ - SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(effIndex); - if (!spellEffect || spellEffect->Effect == 0) - return; - - // Lookup target in already in list - for (ItemTargetList::iterator ihit = m_UniqueItemInfo.begin(); ihit != m_UniqueItemInfo.end(); ++ihit) - { - if (pitem == ihit->item) // Found in list - { - ihit->effectMask |= (1 << effIndex); // Add only effect mask - return; - } - } - - // This is new target add data - - ItemTargetInfo target; - target.item = pitem; - target.effectMask = (1 << effIndex); - m_UniqueItemInfo.push_back(target); -} - -void Spell::DoAllEffectOnTarget(TargetInfo* target) -{ - if (target->processed) // Check target - return; - target->processed = true; // Target checked in apply effects procedure - - // Get mask of effects for target - uint32 mask = target->effectMask; - - Unit* unit = m_caster->GetObjectGuid() == target->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, target->targetGUID); - if (!unit) - return; - - // Get original caster (if exist) and calculate damage/healing from him data - Unit* real_caster = GetAffectiveCaster(); - // FIXME: in case wild GO heal/damage spells will be used target bonuses - Unit* caster = real_caster ? real_caster : m_caster; - - SpellMissInfo missInfo = target->missCondition; - // Need init unitTarget by default unit (can changed in code on reflect) - // Or on missInfo!=SPELL_MISS_NONE unitTarget undefined (but need in trigger subsystem) - unitTarget = unit; - - // Reset damage/healing counter - ResetEffectDamageAndHeal(); - - // Fill base trigger info - uint32 procAttacker = m_procAttacker; - uint32 procVictim = m_procVictim; - uint32 procEx = PROC_EX_NONE; - - // drop proc flags in case target not affected negative effects in negative spell - // for example caster bonus or animation, - // except miss case where will assigned PROC_EX_* flags later - if (((procAttacker | procVictim) & NEGATIVE_TRIGGER_MASK) && - !(target->effectMask & m_negativeEffectMask) && missInfo == SPELL_MISS_NONE) - { - procAttacker = PROC_FLAG_NONE; - procVictim = PROC_FLAG_NONE; - } - - float speed = m_spellInfo->speed == 0.0f && m_triggeredBySpellInfo ? m_triggeredBySpellInfo->speed : m_spellInfo->speed; - if (speed > 0.0f) - { - // mark effects that were already handled in Spell::HandleDelayedSpellLaunch on spell launch as processed - for (int32 i = 0; i < MAX_EFFECT_INDEX; ++i) - if (IsEffectHandledOnDelayedSpellLaunch(m_spellInfo, SpellEffectIndex(i))) - mask &= ~(1 << i); - - // maybe used in effects that are handled on hit - m_damage += target->damage; - } - - if (missInfo == SPELL_MISS_NONE) // In case spell hit target, do all effect on that target - DoSpellHitOnUnit(unit, mask); - else if (missInfo == SPELL_MISS_REFLECT) // In case spell reflect from target, do all effect on caster (if hit) - { - if (target->reflectResult == SPELL_MISS_NONE) // If reflected spell hit caster -> do all effect on him - { - DoSpellHitOnUnit(m_caster, mask); - unitTarget = m_caster; - } - } - else if (missInfo == SPELL_MISS_MISS || missInfo == SPELL_MISS_RESIST) - { - if (real_caster && real_caster != unit) - { - // can cause back attack (if detected) - if (!m_spellInfo->HasAttribute(SPELL_ATTR_EX3_NO_INITIAL_AGGRO) && !IsPositiveSpell(m_spellInfo->Id) && - m_caster->isVisibleForOrDetect(unit, unit, false)) - { - if (!unit->isInCombat() && unit->GetTypeId() != TYPEID_PLAYER && ((Creature*)unit)->AI()) - ((Creature*)unit)->AI()->AttackedBy(real_caster); - - unit->AddThreat(real_caster); - unit->SetInCombatWith(real_caster); - real_caster->SetInCombatWith(unit); - } - } - } - - // All calculated do it! - // Do healing and triggers - if (m_healing) - { - bool crit = real_caster && real_caster->IsSpellCrit(unitTarget, m_spellInfo, m_spellSchoolMask); - uint32 addhealth = m_healing; - if (crit) - { - procEx |= PROC_EX_CRITICAL_HIT; - addhealth = caster->SpellCriticalHealingBonus(m_spellInfo, addhealth, NULL); - } - else - procEx |= PROC_EX_NORMAL_HIT; - - uint32 absorb = 0; - unitTarget->CalculateHealAbsorb(addhealth, &absorb); - addhealth -= absorb; - - // Do triggers for unit (reflect triggers passed on hit phase for correct drop charge) - if (m_canTrigger && missInfo != SPELL_MISS_REFLECT) - { - caster->ProcDamageAndSpell(unitTarget, real_caster ? procAttacker : uint32(PROC_FLAG_NONE), procVictim, procEx, addhealth, m_attackType, m_spellInfo); - } - - int32 gain = caster->DealHeal(unitTarget, addhealth, m_spellInfo, crit, absorb); - - if (real_caster) - unitTarget->getHostileRefManager().threatAssist(real_caster, float(gain) * 0.5f * sSpellMgr.GetSpellThreatMultiplier(m_spellInfo), m_spellInfo); - } - // Do damage and triggers - else if (m_damage) - { - // Fill base damage struct (unitTarget - is real spell target) - SpellNonMeleeDamage damageInfo(caster, unitTarget, m_spellInfo->Id, m_spellSchoolMask); - - if (speed > 0.0f) - { - damageInfo.damage = m_damage; - damageInfo.HitInfo = target->HitInfo; - } - // Add bonuses and fill damageInfo struct - else - caster->CalculateSpellDamage(&damageInfo, m_damage, m_spellInfo, m_attackType); - - unitTarget->CalculateAbsorbResistBlock(caster, &damageInfo, m_spellInfo); - - caster->DealDamageMods(damageInfo.target, damageInfo.damage, &damageInfo.absorb); - - // Send log damage message to client - caster->SendSpellNonMeleeDamageLog(&damageInfo); - - procEx = createProcExtendMask(&damageInfo, missInfo); - procVictim |= PROC_FLAG_TAKEN_ANY_DAMAGE; - - // Do triggers for unit (reflect triggers passed on hit phase for correct drop charge) - if (m_canTrigger && missInfo != SPELL_MISS_REFLECT) - caster->ProcDamageAndSpell(unitTarget, real_caster ? procAttacker : uint32(PROC_FLAG_NONE), procVictim, procEx, damageInfo.damage, m_attackType, m_spellInfo); - - // trigger weapon enchants for weapon based spells; exclude spells that stop attack, because may break CC - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_spellInfo->GetEquippedItemClass() == ITEM_CLASS_WEAPON && - !m_spellInfo->HasAttribute(SPELL_ATTR_STOP_ATTACK_TARGET)) - ((Player*)m_caster)->CastItemCombatSpell(unitTarget, m_attackType); - - // Haunt (NOTE: for avoid use additional field damage stored in dummy value (replace unused 100%) - // apply before deal damage because aura can be removed at target kill - SpellClassOptionsEntry const *classOpt = m_spellInfo->GetSpellClassOptions(); - if (classOpt && classOpt->SpellFamilyName == SPELLFAMILY_WARLOCK && m_spellInfo->SpellIconID == 3172 && - (classOpt->SpellFamilyFlags & UI64LIT(0x0004000000000000))) - if(Aura* dummy = unitTarget->GetDummyAura(m_spellInfo->Id)) - dummy->GetModifier()->m_amount = damageInfo.damage; - - caster->DealSpellDamage(&damageInfo, true); - - // Scourge Strike, here because needs to use final damage in second part of the spell - if (classOpt && classOpt->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT && classOpt->SpellFamilyFlags & UI64LIT(0x0800000000000000)) - { - uint32 count = 0; - Unit::SpellAuraHolderMap const& auras = unitTarget->GetSpellAuraHolderMap(); - for (Unit::SpellAuraHolderMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) - { - if(itr->second->GetSpellProto()->GetDispel() == DISPEL_DISEASE && - itr->second->GetCasterGuid() == caster->GetObjectGuid()) - ++count; - } - - if (count) - { - int32 bp = count * CalculateDamage(EFFECT_INDEX_2, unitTarget) * damageInfo.damage / 100; - if (bp) - caster->CastCustomSpell(unitTarget, 70890, &bp, NULL, NULL, true); - } - } - } - // Passive spell hits/misses or active spells only misses (only triggers if proc flags set) - else if (procAttacker || procVictim) - { - // Fill base damage struct (unitTarget - is real spell target) - SpellNonMeleeDamage damageInfo(caster, unitTarget, m_spellInfo->Id, m_spellSchoolMask); - procEx = createProcExtendMask(&damageInfo, missInfo); - // Do triggers for unit (reflect triggers passed on hit phase for correct drop charge) - if (m_canTrigger && missInfo != SPELL_MISS_REFLECT) - caster->ProcDamageAndSpell(unit, real_caster ? procAttacker : uint32(PROC_FLAG_NONE), procVictim, procEx, 0, m_attackType, m_spellInfo); - } - - // Call scripted function for AI if this spell is casted upon a creature - if (unit->GetTypeId() == TYPEID_UNIT) - { - // cast at creature (or GO) quest objectives update at successful cast finished (+channel finished) - // ignore pets or autorepeat/melee casts for speed (not exist quest for spells (hm... ) - if (real_caster && !((Creature*)unit)->IsPet() && !IsAutoRepeat() && !IsNextMeleeSwingSpell() && !IsChannelActive()) - if (Player* p = real_caster->GetCharmerOrOwnerPlayerOrPlayerItself()) - p->RewardPlayerAndGroupAtCast(unit, m_spellInfo->Id); - - if (((Creature*)unit)->AI()) - ((Creature*)unit)->AI()->SpellHit(m_caster, m_spellInfo); - } - - // Call scripted function for AI if this spell is casted by a creature - if (m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->AI()) - ((Creature*)m_caster)->AI()->SpellHitTarget(unit, m_spellInfo); - if (real_caster && real_caster != m_caster && real_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)real_caster)->AI()) - ((Creature*)real_caster)->AI()->SpellHitTarget(unit, m_spellInfo); -} - -void Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask) -{ - if (!unit || !effectMask) - return; - - Unit* realCaster = GetAffectiveCaster(); - - // Recheck immune (only for delayed spells) - float speed = m_spellInfo->speed == 0.0f && m_triggeredBySpellInfo ? m_triggeredBySpellInfo->speed : m_spellInfo->speed; - if (speed && ( - unit->IsImmunedToDamage(GetSpellSchoolMask(m_spellInfo)) || - unit->IsImmuneToSpell(m_spellInfo, unit == realCaster))) - { - if (realCaster) - realCaster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_IMMUNE); - - ResetEffectDamageAndHeal(); - return; - } - - if (unit->GetTypeId() == TYPEID_PLAYER) - { - ((Player*)unit)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET, m_spellInfo->Id); - ((Player*)unit)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2, m_spellInfo->Id); - } - - if (realCaster && realCaster->GetTypeId() == TYPEID_PLAYER) - ((Player*)realCaster)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2, m_spellInfo->Id, 0, unit); - - if (realCaster && realCaster != unit) - { - // Recheck UNIT_FLAG_NON_ATTACKABLE for delayed spells - if (speed > 0.0f && - unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE) && - unit->GetCharmerOrOwnerGuid() != m_caster->GetObjectGuid()) - { - realCaster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE); - ResetEffectDamageAndHeal(); - return; - } - - if (!realCaster->IsFriendlyTo(unit)) - { - // for delayed spells ignore not visible explicit target - if (speed > 0.0f && unit == m_targets.getUnitTarget() && - !unit->isVisibleForOrDetect(m_caster, m_caster, false)) - { - realCaster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE); - ResetEffectDamageAndHeal(); - return; - } - - // not break stealth by cast targeting - if (!m_spellInfo->HasAttribute(SPELL_ATTR_EX_NOT_BREAK_STEALTH)) - unit->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); - - // can cause back attack (if detected), stealth removed at Spell::cast if spell break it - if (!m_spellInfo->HasAttribute(SPELL_ATTR_EX3_NO_INITIAL_AGGRO) && !IsPositiveSpell(m_spellInfo->Id) && - m_caster->isVisibleForOrDetect(unit, unit, false)) - { - // use speedup check to avoid re-remove after above lines - if (m_spellInfo->HasAttribute(SPELL_ATTR_EX_NOT_BREAK_STEALTH)) - unit->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); - - // caster can be detected but have stealth aura - m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); - - if (!unit->IsStandState() && !unit->hasUnitState(UNIT_STAT_STUNNED)) - unit->SetStandState(UNIT_STAND_STATE_STAND); - - if (!unit->isInCombat() && unit->GetTypeId() != TYPEID_PLAYER && ((Creature*)unit)->AI()) - unit->AttackedBy(realCaster); - - unit->AddThreat(realCaster); - unit->SetInCombatWith(realCaster); - realCaster->SetInCombatWith(unit); - - if (Player* attackedPlayer = unit->GetCharmerOrOwnerPlayerOrPlayerItself()) - realCaster->SetContestedPvP(attackedPlayer); - } - } - else - { - // for delayed spells ignore negative spells (after duel end) for friendly targets - if (speed > 0.0f && !IsPositiveSpell(m_spellInfo->Id)) - { - realCaster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE); - ResetEffectDamageAndHeal(); - return; - } - - // assisting case, healing and resurrection - if (unit->hasUnitState(UNIT_STAT_ATTACK_PLAYER)) - realCaster->SetContestedPvP(); - - if (unit->isInCombat() && !m_spellInfo->HasAttribute(SPELL_ATTR_EX3_NO_INITIAL_AGGRO)) - { - realCaster->SetInCombatState(unit->GetCombatTimer() > 0); - unit->getHostileRefManager().threatAssist(realCaster, 0.0f, m_spellInfo); - } - } - } - - // Get Data Needed for Diminishing Returns, some effects may have multiple auras, so this must be done on spell hit, not aura add - m_diminishGroup = GetDiminishingReturnsGroupForSpell(m_spellInfo, m_triggeredByAuraSpell); - m_diminishLevel = unit->GetDiminishing(m_diminishGroup); - // Increase Diminishing on unit, current informations for actually casts will use values above - if ((GetDiminishingReturnsGroupType(m_diminishGroup) == DRTYPE_PLAYER && unit->GetTypeId() == TYPEID_PLAYER) || - GetDiminishingReturnsGroupType(m_diminishGroup) == DRTYPE_ALL) - unit->IncrDiminishing(m_diminishGroup); - - // Apply additional spell effects to target - CastPreCastSpells(unit); - - if (IsSpellAppliesAura(m_spellInfo, effectMask)) - { - m_spellAuraHolder = CreateSpellAuraHolder(m_spellInfo, unit, realCaster, m_CastItem); - m_spellAuraHolder->setDiminishGroup(m_diminishGroup); - } - else - m_spellAuraHolder = NULL; - - for (int effectNumber = 0; effectNumber < MAX_EFFECT_INDEX; ++effectNumber) - { - if (effectMask & (1 << effectNumber)) - { - SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(SpellEffectIndex(effectNumber)); - HandleEffects(unit, NULL, NULL, SpellEffectIndex(effectNumber), m_damageMultipliers[effectNumber]); - if (m_applyMultiplierMask & (1 << effectNumber)) - { - // Get multiplier - float multiplier = spellEffect ? spellEffect->DmgMultiplier : 1.0f; - // Apply multiplier mods - if (realCaster) - if (Player* modOwner = realCaster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_EFFECT_PAST_FIRST, multiplier, this); - m_damageMultipliers[effectNumber] *= multiplier; - } - } - } - - // now apply all created auras - if (m_spellAuraHolder) - { - // normally shouldn't happen - if (!m_spellAuraHolder->IsEmptyHolder()) - { - int32 duration = m_spellAuraHolder->GetAuraMaxDuration(); - int32 originalDuration = duration; - - if (duration > 0) - { - int32 limitduration = GetDiminishingReturnsLimitDuration(m_diminishGroup, m_spellInfo); - unit->ApplyDiminishingToDuration(m_diminishGroup, duration, m_caster, m_diminishLevel, limitduration, m_spellFlags & SPELL_FLAG_REFLECTED); - - // Fully diminished - if (duration == 0) - { - delete m_spellAuraHolder; - return; - } - } - - duration = unit->CalculateAuraDuration(m_spellInfo, effectMask, duration, m_caster, this); - - if (duration != originalDuration) - { - m_spellAuraHolder->SetAuraMaxDuration(duration); - m_spellAuraHolder->SetAuraDuration(duration); - } - - unit->AddSpellAuraHolder(m_spellAuraHolder); - } - else - delete m_spellAuraHolder; - } -} - -void Spell::DoAllEffectOnTarget(GOTargetInfo* target) -{ - if (target->processed) // Check target - return; - target->processed = true; // Target checked in apply effects procedure - - uint32 effectMask = target->effectMask; - if (!effectMask) - return; - - GameObject* go = m_caster->GetMap()->GetGameObject(target->targetGUID); - if (!go) - return; - - for (int effectNumber = 0; effectNumber < MAX_EFFECT_INDEX; ++effectNumber) - if (effectMask & (1 << effectNumber)) - HandleEffects(NULL, NULL, go, SpellEffectIndex(effectNumber)); - - // cast at creature (or GO) quest objectives update at successful cast finished (+channel finished) - // ignore autorepeat/melee casts for speed (not exist quest for spells (hm... ) - if (!IsAutoRepeat() && !IsNextMeleeSwingSpell() && !IsChannelActive()) - { - if (Player* p = m_caster->GetCharmerOrOwnerPlayerOrPlayerItself()) - p->RewardPlayerAndGroupAtCast(go, m_spellInfo->Id); - } -} - -void Spell::DoAllEffectOnTarget(ItemTargetInfo* target) -{ - uint32 effectMask = target->effectMask; - if (!target->item || !effectMask) - return; - - for (int effectNumber = 0; effectNumber < MAX_EFFECT_INDEX; ++effectNumber) - if (effectMask & (1 << effectNumber)) - HandleEffects(NULL, target->item, NULL, SpellEffectIndex(effectNumber)); -} - -void Spell::HandleDelayedSpellLaunch(TargetInfo* target) -{ - // Get mask of effects for target - uint32 mask = target->effectMask; - - Unit* unit = m_caster->GetObjectGuid() == target->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, target->targetGUID); - if (!unit) - return; - - // Get original caster (if exist) and calculate damage/healing from him data - Unit* real_caster = GetAffectiveCaster(); - // FIXME: in case wild GO heal/damage spells will be used target bonuses - Unit* caster = real_caster ? real_caster : m_caster; - - SpellMissInfo missInfo = target->missCondition; - // Need init unitTarget by default unit (can changed in code on reflect) - // Or on missInfo!=SPELL_MISS_NONE unitTarget undefined (but need in trigger subsystem) - unitTarget = unit; - - // Reset damage/healing counter - m_damage = 0; - m_healing = 0; // healing maybe not needed at this point - - // Fill base damage struct (unitTarget - is real spell target) - SpellNonMeleeDamage damageInfo(caster, unitTarget, m_spellInfo->Id, m_spellSchoolMask); - - // keep damage amount for reflected spells - if (missInfo == SPELL_MISS_NONE || (missInfo == SPELL_MISS_REFLECT && target->reflectResult == SPELL_MISS_NONE)) - { - for (int32 effectNumber = 0; effectNumber < MAX_EFFECT_INDEX; ++effectNumber) - { - if (mask & (1 << effectNumber) && IsEffectHandledOnDelayedSpellLaunch(m_spellInfo, SpellEffectIndex(effectNumber))) - { - SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(SpellEffectIndex(effectNumber)); - HandleEffects(unit, NULL, NULL, SpellEffectIndex(effectNumber), m_damageMultipliers[effectNumber]); - if (m_applyMultiplierMask & (1 << effectNumber)) - { - // Get multiplier - float multiplier = spellEffect ? spellEffect->DmgMultiplier : 1.0f; - // Apply multiplier mods - if (real_caster) - if (Player* modOwner = real_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_EFFECT_PAST_FIRST, multiplier, this); - m_damageMultipliers[effectNumber] *= multiplier; - } - } - } - - if (m_damage > 0) - caster->CalculateSpellDamage(&damageInfo, m_damage, m_spellInfo, m_attackType); - } - - target->damage = damageInfo.damage; - target->HitInfo = damageInfo.HitInfo; -} - -void Spell::InitializeDamageMultipliers() -{ - for (int32 i = 0; i < MAX_EFFECT_INDEX; ++i) - { - SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(SpellEffectIndex(i)); - if(!spellEffect) - continue; - if (spellEffect->Effect == 0) - continue; - - uint32 EffectChainTarget = spellEffect->EffectChainTarget; - if (Unit* realCaster = GetAffectiveCaster()) - if (Player* modOwner = realCaster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_JUMP_TARGETS, EffectChainTarget, this); - - m_damageMultipliers[i] = 1.0f; - if( (spellEffect->EffectImplicitTargetA == TARGET_CHAIN_DAMAGE || spellEffect->EffectImplicitTargetA == TARGET_CHAIN_HEAL) && - (EffectChainTarget > 1) ) - m_applyMultiplierMask |= (1 << i); - } -} - -bool Spell::IsAliveUnitPresentInTargetList() -{ - // Not need check return true - if (m_needAliveTargetMask == 0) - return true; - - uint8 needAliveTargetMask = m_needAliveTargetMask; - - for (TargetList::const_iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - { - if (ihit->missCondition == SPELL_MISS_NONE && (needAliveTargetMask & ihit->effectMask)) - { - Unit* unit = m_caster->GetObjectGuid() == ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID); - - // either unit is alive and normal spell, or unit dead and deathonly-spell - if (unit && (unit->isAlive() != IsDeathOnlySpell(m_spellInfo))) - needAliveTargetMask &= ~ihit->effectMask; // remove from need alive mask effect that have alive target - } - } - - // is all effects from m_needAliveTargetMask have alive targets - return needAliveTargetMask == 0; -} - -// Helper for Chain Healing -// Spell target first -// Raidmates then descending by injury suffered (MaxHealth - Health) -// Other players/mobs then descending by injury suffered (MaxHealth - Health) -struct ChainHealingOrder : public std::binary_function -{ - const Unit* MainTarget; - ChainHealingOrder(Unit const* Target) : MainTarget(Target) {}; - // functor for operator ">" - bool operator()(Unit const* _Left, Unit const* _Right) const - { - return (ChainHealingHash(_Left) < ChainHealingHash(_Right)); - } - int32 ChainHealingHash(Unit const* Target) const - { - if (Target == MainTarget) - return 0; - else if (Target->GetTypeId() == TYPEID_PLAYER && MainTarget->GetTypeId() == TYPEID_PLAYER && - ((Player const*)Target)->IsInSameRaidWith((Player const*)MainTarget)) - { - if (Target->GetHealth() == Target->GetMaxHealth()) - return 40000; - else - return 20000 - Target->GetMaxHealth() + Target->GetHealth(); - } - else - return 40000 - Target->GetMaxHealth() + Target->GetHealth(); - } -}; - -class ChainHealingFullHealth: std::unary_function -{ - public: - const Unit* MainTarget; - ChainHealingFullHealth(const Unit* Target) : MainTarget(Target) {}; - - bool operator()(const Unit* Target) - { - return (Target != MainTarget && Target->GetHealth() == Target->GetMaxHealth()); - } -}; - -// Helper for targets nearest to the spell target -// The spell target is always first unless there is a target at _completely_ the same position (unbelievable case) -struct TargetDistanceOrderNear : public std::binary_function -{ - const Unit* MainTarget; - TargetDistanceOrderNear(const Unit* Target) : MainTarget(Target) {}; - // functor for operator ">" - bool operator()(const Unit* _Left, const Unit* _Right) const - { - return MainTarget->GetDistanceOrder(_Left, _Right); - } -}; - -// Helper for targets furthest away to the spell target -// The spell target is always first unless there is a target at _completely_ the same position (unbelievable case) -struct TargetDistanceOrderFarAway : public std::binary_function -{ - const Unit* MainTarget; - TargetDistanceOrderFarAway(const Unit* Target) : MainTarget(Target) {}; - // functor for operator "<" - bool operator()(const Unit* _Left, const Unit* _Right) const - { - return !MainTarget->GetDistanceOrder(_Left, _Right); - } -}; - -void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& targetUnitMap) -{ - SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(effIndex); - SpellClassOptionsEntry const* classOpt = m_spellInfo->GetSpellClassOptions(); - if (!spellEffect) - return; - - uint32 EffectChainTarget = spellEffect ? spellEffect->EffectChainTarget : 0; - uint32 unMaxTargets = m_spellInfo->GetMaxAffectedTargets(); - - float radius; - GetSpellRangeAndRadius(spellEffect, radius, EffectChainTarget, unMaxTargets); - - std::list tempTargetGOList; - - switch (targetMode) - { - case TARGET_RANDOM_NEARBY_LOC: - // special case for Fatal Attraction (BT, Mother Shahraz) - if (m_spellInfo->Id == 40869) - radius = 30.0f; - - // Get a random point in circle. Use sqrt(rand) to correct distribution when converting polar to Cartesian coordinates. - radius *= sqrtf(rand_norm_f()); - // no 'break' expected since we use code in case TARGET_RANDOM_CIRCUMFERENCE_POINT!!! - case TARGET_RANDOM_CIRCUMFERENCE_POINT: - { - // Get a random point AT the circumference - float angle = 2.0f * M_PI_F * rand_norm_f(); - float dest_x, dest_y, dest_z; - m_caster->GetClosePoint(dest_x, dest_y, dest_z, 0.0f, radius, angle); - m_targets.setDestination(dest_x, dest_y, dest_z); - - // This targetMode is often used as 'last' implicitTarget for positive spells, that just require coordinates - // and no unitTarget (e.g. summon effects). As MaNGOS always needs a unitTarget we add just the caster here. - // Logic: This is first target, and no second target => use m_caster -- This is second target: use m_caster if the spell is positive or a summon spell - if ((spellEffect->EffectImplicitTargetA == targetMode && spellEffect->EffectImplicitTargetB == TARGET_NONE) || - (spellEffect->EffectImplicitTargetB == targetMode && (IsPositiveSpell(m_spellInfo) || spellEffect->Effect == SPELL_EFFECT_SUMMON))) - targetUnitMap.push_back(m_caster); - break; - } - case TARGET_91: - case TARGET_RANDOM_NEARBY_DEST: - { - // Get a random point IN the CIRCEL around current M_TARGETS COORDINATES(!). - if (radius > 0.0f) - { - // Use sqrt(rand) to correct distribution when converting polar to Cartesian coordinates. - radius *= sqrtf(rand_norm_f()); - float angle = 2.0f * M_PI_F * rand_norm_f(); - float dest_x = m_targets.m_destX + cos(angle) * radius; - float dest_y = m_targets.m_destY + sin(angle) * radius; - float dest_z = m_caster->GetPositionZ(); - if (!MapManager::IsValidMapCoord(m_caster->GetMapId(), dest_x, dest_y, dest_z)) - { - sLog.outError("Spell::SetTargetMap: invalid map coordinates for spell %u eff_idx %u target mode %u: mapid %u x %f y %f z %f\n" - "spell radius: %f caster position: x %f y %f z %f\n" - "base dest position: x %f y %f z %f", - m_spellInfo->Id, effIndex, targetMode, m_caster->GetMapId(), dest_x, dest_y, dest_z, - radius, m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ(), - m_targets.m_destX, m_targets.m_destY, m_caster->GetPositionZ()); - m_targets.setDestination(m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ()); - } - else - { - m_caster->UpdateGroundPositionZ(dest_x, dest_y, dest_z); - m_targets.setDestination(dest_x, dest_y, dest_z); - } - } - - // This targetMode is often used as 'last' implicitTarget for positive spells, that just require coordinates - // and no unitTarget (e.g. summon effects). As MaNGOS always needs a unitTarget we add just the caster here. - // Logic: This is first target, and no second target => use m_caster -- This is second target: use m_caster if the spell is positive or a summon spell - if ((spellEffect->EffectImplicitTargetA == targetMode && spellEffect->EffectImplicitTargetB == TARGET_NONE) || - (spellEffect->EffectImplicitTargetB == targetMode && (IsPositiveSpell(m_spellInfo) || spellEffect->Effect == SPELL_EFFECT_SUMMON))) - targetUnitMap.push_back(m_caster); - - break; - } - case TARGET_TOTEM_EARTH: - case TARGET_TOTEM_WATER: - case TARGET_TOTEM_AIR: - case TARGET_TOTEM_FIRE: - { - float angle = m_caster->GetOrientation(); - switch (targetMode) - { - case TARGET_TOTEM_FIRE: angle += M_PI_F * 0.25f; break; // front - left - case TARGET_TOTEM_AIR: angle += M_PI_F * 0.75f; break; // back - left - case TARGET_TOTEM_WATER: angle += M_PI_F * 1.25f; break; // back - right - case TARGET_TOTEM_EARTH: angle += M_PI_F * 1.75f; break; // front - right - } - - float x, y; - float z = m_caster->GetPositionZ(); - // Do not search for a free spot. TODO: Should there be searched for a free spot. There was once a discussion that in case this space was impossible (LOS) m_caster's position should be used. - // TODO Bring this back to memory and search for it! - m_caster->GetNearPoint2D(x, y, radius, angle); - m_caster->UpdateAllowedPositionZ(x, y, z); - m_targets.setDestination(x, y, z); - - // Add Summoner - targetUnitMap.push_back(m_caster); - break; - } - case TARGET_SELF: - case TARGET_SELF2: - targetUnitMap.push_back(m_caster); - break; - case TARGET_RANDOM_ENEMY_CHAIN_IN_AREA: - { - m_targets.m_targetMask = 0; - unMaxTargets = EffectChainTarget; - float max_range = radius + unMaxTargets * CHAIN_SPELL_JUMP_RADIUS; - - UnitList tempTargetUnitMap; - - { - MaNGOS::AnyAoETargetUnitInObjectRangeCheck u_check(m_caster, max_range); - MaNGOS::UnitListSearcher searcher(tempTargetUnitMap, u_check); - Cell::VisitAllObjects(m_caster, searcher, max_range); - } - - if (tempTargetUnitMap.empty()) - break; - - tempTargetUnitMap.sort(TargetDistanceOrderNear(m_caster)); - - // Now to get us a random target that's in the initial range of the spell - uint32 t = 0; - UnitList::iterator itr = tempTargetUnitMap.begin(); - while (itr != tempTargetUnitMap.end() && (*itr)->IsWithinDist(m_caster, radius)) - ++t, ++itr; - - if (!t) - break; - - itr = tempTargetUnitMap.begin(); - std::advance(itr, rand() % t); - Unit* pUnitTarget = *itr; - targetUnitMap.push_back(pUnitTarget); - - tempTargetUnitMap.erase(itr); - - tempTargetUnitMap.sort(TargetDistanceOrderNear(pUnitTarget)); - - t = unMaxTargets - 1; - Unit* prev = pUnitTarget; - UnitList::iterator next = tempTargetUnitMap.begin(); - - while (t && next != tempTargetUnitMap.end()) - { - if (!prev->IsWithinDist(*next, CHAIN_SPELL_JUMP_RADIUS)) - break; - - if (!m_spellInfo->HasAttribute(SPELL_ATTR_EX2_IGNORE_LOS) && !prev->IsWithinLOSInMap(*next)) - { - ++next; - continue; - } - - prev = *next; - targetUnitMap.push_back(prev); - tempTargetUnitMap.erase(next); - tempTargetUnitMap.sort(TargetDistanceOrderNear(prev)); - next = tempTargetUnitMap.begin(); - - --t; - } - break; - } - case TARGET_RANDOM_FRIEND_CHAIN_IN_AREA: - { - m_targets.m_targetMask = 0; - unMaxTargets = EffectChainTarget; - float max_range = radius + unMaxTargets * CHAIN_SPELL_JUMP_RADIUS; - UnitList tempTargetUnitMap; - { - MaNGOS::AnyFriendlyUnitInObjectRangeCheck u_check(m_caster, max_range); - MaNGOS::UnitListSearcher searcher(tempTargetUnitMap, u_check); - Cell::VisitAllObjects(m_caster, searcher, max_range); - } - - if (tempTargetUnitMap.empty()) - break; - - tempTargetUnitMap.sort(TargetDistanceOrderNear(m_caster)); - - // Now to get us a random target that's in the initial range of the spell - uint32 t = 0; - UnitList::iterator itr = tempTargetUnitMap.begin(); - while (itr != tempTargetUnitMap.end() && (*itr)->IsWithinDist(m_caster, radius)) - ++t, ++itr; - - if (!t) - break; - - itr = tempTargetUnitMap.begin(); - std::advance(itr, rand() % t); - Unit* pUnitTarget = *itr; - targetUnitMap.push_back(pUnitTarget); - - tempTargetUnitMap.erase(itr); - - tempTargetUnitMap.sort(TargetDistanceOrderNear(pUnitTarget)); - - t = unMaxTargets - 1; - Unit* prev = pUnitTarget; - UnitList::iterator next = tempTargetUnitMap.begin(); - - while (t && next != tempTargetUnitMap.end()) - { - if (!prev->IsWithinDist(*next, CHAIN_SPELL_JUMP_RADIUS)) - break; - - if (!m_spellInfo->HasAttribute(SPELL_ATTR_EX2_IGNORE_LOS) && !prev->IsWithinLOSInMap(*next)) - { - ++next; - continue; - } - prev = *next; - targetUnitMap.push_back(prev); - tempTargetUnitMap.erase(next); - tempTargetUnitMap.sort(TargetDistanceOrderNear(prev)); - next = tempTargetUnitMap.begin(); - --t; - } - break; - } - case TARGET_PET: - { - Pet* tmpUnit = m_caster->GetPet(); - if (!tmpUnit) break; - targetUnitMap.push_back(tmpUnit); - break; - } - case TARGET_CHAIN_DAMAGE: - { - if (EffectChainTarget <= 1) - { - if (Unit* pUnitTarget = m_caster->SelectMagnetTarget(m_targets.getUnitTarget(), this, effIndex)) - { - if (m_targets.getUnitTarget() && m_targets.getUnitTarget() != pUnitTarget) - m_spellFlags |= SPELL_FLAG_REDIRECTED; - - m_targets.setUnitTarget(pUnitTarget); - targetUnitMap.push_back(pUnitTarget); - } - } - else - { - Unit* pUnitTarget = m_targets.getUnitTarget(); - WorldObject* originalCaster = GetAffectiveCasterObject(); - if (!pUnitTarget || !originalCaster) - break; - - unMaxTargets = EffectChainTarget; - - float max_range; - if(m_spellInfo->GetDmgClass() == SPELL_DAMAGE_CLASS_MELEE) - max_range = radius; - else - // FIXME: This very like horrible hack and wrong for most spells - max_range = radius + unMaxTargets * CHAIN_SPELL_JUMP_RADIUS; - - UnitList tempTargetUnitMap; - { - MaNGOS::AnyAoEVisibleTargetUnitInObjectRangeCheck u_check(pUnitTarget, originalCaster, max_range); - MaNGOS::UnitListSearcher searcher(tempTargetUnitMap, u_check); - Cell::VisitAllObjects(m_caster, searcher, max_range); - } - - if (tempTargetUnitMap.empty()) - break; - - tempTargetUnitMap.sort(TargetDistanceOrderNear(pUnitTarget)); - - if (*tempTargetUnitMap.begin() == pUnitTarget) - tempTargetUnitMap.erase(tempTargetUnitMap.begin()); - - targetUnitMap.push_back(pUnitTarget); - uint32 t = unMaxTargets - 1; - Unit* prev = pUnitTarget; - UnitList::iterator next = tempTargetUnitMap.begin(); - - while (t && next != tempTargetUnitMap.end()) - { - if (!prev->IsWithinDist(*next, CHAIN_SPELL_JUMP_RADIUS)) - break; - - if (!m_spellInfo->HasAttribute(SPELL_ATTR_EX2_IGNORE_LOS) && !prev->IsWithinLOSInMap(*next)) - { - ++next; - continue; - } - - prev = *next; - targetUnitMap.push_back(prev); - tempTargetUnitMap.erase(next); - tempTargetUnitMap.sort(TargetDistanceOrderNear(prev)); - next = tempTargetUnitMap.begin(); - - --t; - } - } - break; - } - case TARGET_ALL_ENEMY_IN_AREA: - FillAreaTargets(targetUnitMap, radius, PUSH_DEST_CENTER, SPELL_TARGETS_AOE_DAMAGE); - - switch (m_spellInfo->Id) - { - // Do not target current victim - case 30843: // Enfeeble - case 31347: // Doom - case 37676: // Insidious Whisper - case 38028: // Watery Grave - case 40618: // Insignificance - case 41376: // Spite - case 62166: // Stone Grip - case 63981: // Stone Grip (h) - { - if (Unit* pVictim = m_caster->getVictim()) - targetUnitMap.remove(pVictim); - break; - } - // Other special cases - case 42005: // Bloodboil (spell hits only the 5 furthest away targets) - { - if (targetUnitMap.size() > unMaxTargets) - { - targetUnitMap.sort(TargetDistanceOrderFarAway(m_caster)); - targetUnitMap.resize(unMaxTargets); - } - break; - } - default: - break; - } - break; - case TARGET_AREAEFFECT_INSTANT: - { - SpellTargets targetB = SPELL_TARGETS_AOE_DAMAGE; - switch (spellEffect->Effect) - { - case SPELL_EFFECT_QUEST_COMPLETE: - case SPELL_EFFECT_KILL_CREDIT_PERSONAL: - case SPELL_EFFECT_KILL_CREDIT_GROUP: - targetB = SPELL_TARGETS_ALL; - default: - // Select friendly targets for positive effect - if (IsPositiveEffect(m_spellInfo, effIndex)) - targetB = SPELL_TARGETS_FRIENDLY; - } - - UnitList tempTargetUnitMap; - SQLMultiStorage::SQLMSIteratorBounds bounds = sSpellScriptTargetStorage.getBounds(m_spellInfo->Id); - - // fill real target list if no spell script target defined - FillAreaTargets(bounds.first != bounds.second ? tempTargetUnitMap : targetUnitMap, - radius, PUSH_DEST_CENTER, bounds.first != bounds.second ? SPELL_TARGETS_ALL : targetB); - - if (!tempTargetUnitMap.empty()) - { - for (UnitList::const_iterator iter = tempTargetUnitMap.begin(); iter != tempTargetUnitMap.end(); ++iter) - { - if ((*iter)->GetTypeId() != TYPEID_UNIT) - continue; - - for (SQLMultiStorage::SQLMultiSIterator i_spellST = bounds.first; i_spellST != bounds.second; ++i_spellST) - { - if (i_spellST->CanNotHitWithSpellEffect(effIndex)) - continue; - - // only creature entries supported for this target type - if (i_spellST->type == SPELL_TARGET_TYPE_GAMEOBJECT) - continue; - - if ((*iter)->GetEntry() == i_spellST->targetEntry) - { - if (i_spellST->type == SPELL_TARGET_TYPE_DEAD && ((Creature*)(*iter))->IsCorpse()) - { - targetUnitMap.push_back((*iter)); - } - else if (i_spellST->type == SPELL_TARGET_TYPE_CREATURE && (*iter)->isAlive()) - { - targetUnitMap.push_back((*iter)); - } - - break; - } - } - } - } - - // exclude caster - targetUnitMap.remove(m_caster); - break; - } - case TARGET_AREAEFFECT_CUSTOM: - { - if (spellEffect && spellEffect->Effect == SPELL_EFFECT_PERSISTENT_AREA_AURA) - break; - else if (spellEffect && spellEffect->Effect == SPELL_EFFECT_SUMMON) - { - targetUnitMap.push_back(m_caster); - break; - } - - UnitList tempTargetUnitMap; - SQLMultiStorage::SQLMSIteratorBounds bounds = sSpellScriptTargetStorage.getBounds(m_spellInfo->Id); - // fill real target list if no spell script target defined - FillAreaTargets(bounds.first != bounds.second ? tempTargetUnitMap : targetUnitMap, radius, PUSH_DEST_CENTER, SPELL_TARGETS_ALL); - - if (!tempTargetUnitMap.empty()) - { - for (UnitList::const_iterator iter = tempTargetUnitMap.begin(); iter != tempTargetUnitMap.end(); ++iter) - { - if ((*iter)->GetTypeId() != TYPEID_UNIT) - continue; - - for (SQLMultiStorage::SQLMultiSIterator i_spellST = bounds.first; i_spellST != bounds.second; ++i_spellST) - { - if (i_spellST->CanNotHitWithSpellEffect(effIndex)) - continue; - - // only creature entries supported for this target type - if (i_spellST->type == SPELL_TARGET_TYPE_GAMEOBJECT) - continue; - - if ((*iter)->GetEntry() == i_spellST->targetEntry) - { - if (i_spellST->type == SPELL_TARGET_TYPE_DEAD && ((Creature*)(*iter))->IsCorpse()) - { - targetUnitMap.push_back((*iter)); - } - else if (i_spellST->type == SPELL_TARGET_TYPE_CREATURE && (*iter)->isAlive()) - { - targetUnitMap.push_back((*iter)); - } - - break; - } - } - } - } - else - { - // remove not targetable units if spell has no script targets - for (UnitList::iterator itr = targetUnitMap.begin(); itr != targetUnitMap.end();) - { - if (!(*itr)->isTargetableForAttack(m_spellInfo->HasAttribute(SPELL_ATTR_EX3_CAST_ON_DEAD))) - targetUnitMap.erase(itr++); - else - ++itr; - } - } - break; - } - case TARGET_AREAEFFECT_GO_AROUND_SOURCE: - case TARGET_AREAEFFECT_GO_AROUND_DEST: - case TARGET_GO_IN_FRONT_OF_CASTER_90: - { - float x, y, z; - - if (targetMode == TARGET_AREAEFFECT_GO_AROUND_SOURCE && (m_targets.m_targetMask & TARGET_FLAG_SOURCE_LOCATION)) - m_targets.getSource(x, y, z); - else if (targetMode == TARGET_AREAEFFECT_GO_AROUND_DEST) - m_targets.getDestination(x, y, z); - else // can also happen for GO_AROUND_SOURCE without SOURCE_LOCATION - m_caster->GetPosition(x, y, z); - - bool fixedTargetExist = false; - SQLMultiStorage::SQLMSIteratorBounds bounds = sSpellScriptTargetStorage.getBounds(m_spellInfo->Id); - for (SQLMultiStorage::SQLMultiSIterator i_spellST = bounds.first; i_spellST != bounds.second; ++i_spellST) - { - if (i_spellST->CanNotHitWithSpellEffect(effIndex)) - continue; - - if (i_spellST->type == SPELL_TARGET_TYPE_GAMEOBJECT) - { - fixedTargetExist = true; - // search all GO's with entry, within range of m_destN - MaNGOS::GameObjectEntryInPosRangeCheck go_check(*m_caster, i_spellST->targetEntry, x, y, z, radius); - MaNGOS::GameObjectListSearcher checker(tempTargetGOList, go_check); - Cell::VisitGridObjects(m_caster, checker, radius + GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex))); - } - } - - if (!fixedTargetExist) - { - // Generic handling for spells that require GO-type 33 - if (spellEffect->Effect == SPELL_EFFECT_WMO_DAMAGE || spellEffect->Effect == SPELL_EFFECT_WMO_REPAIR || spellEffect->Effect == SPELL_EFFECT_WMO_CHANGE) - { - MaNGOS::GameObjectTypeInPosRangeCheck go_check(*m_caster, GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING, x, y, z, radius, spellEffect->Effect == SPELL_EFFECT_WMO_DAMAGE, spellEffect->Effect == SPELL_EFFECT_WMO_REPAIR); - MaNGOS::GameObjectListSearcher checker(tempTargetGOList, go_check); - Cell::VisitGridObjects(m_caster, checker, radius + GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex))); - } - } - - // Filter some targets for special target-type - for (std::list::iterator itr = tempTargetGOList.begin(); itr != tempTargetGOList.end();) - { - switch (targetMode) - { - case TARGET_GO_IN_FRONT_OF_CASTER_90: - if (!m_caster->HasInArc(M_PI_F / 2, *itr)) - { - tempTargetGOList.erase(itr++); - continue; - } - // no break here - case TARGET_AREAEFFECT_GO_AROUND_SOURCE: - case TARGET_AREAEFFECT_GO_AROUND_DEST: - default: - ++itr; - } - } - break; - } - case TARGET_ALL_ENEMY_IN_AREA_INSTANT: - { - // targets the ground, not the units in the area - if(!spellEffect) - break; - - switch(spellEffect->Effect) - { - case SPELL_EFFECT_PERSISTENT_AREA_AURA: - break; - case SPELL_EFFECT_SUMMON: - targetUnitMap.push_back(m_caster); - break; - default: - FillAreaTargets(targetUnitMap, radius, PUSH_DEST_CENTER, SPELL_TARGETS_AOE_DAMAGE); - - // Mind Sear, triggered - if (m_spellInfo->IsFitToFamily(SPELLFAMILY_PRIEST, UI64LIT(0x0008000000000000))) - if (Unit* unitTarget = m_targets.getUnitTarget()) - targetUnitMap.remove(unitTarget); - - break; - } - break; - } - case TARGET_DUELVSPLAYER_COORDINATES: - { - if (Unit* currentTarget = m_targets.getUnitTarget()) - m_targets.setDestination(currentTarget->GetPositionX(), currentTarget->GetPositionY(), currentTarget->GetPositionZ()); - break; - } - case TARGET_ALL_PARTY_AROUND_CASTER: - { - if (m_caster->GetObjectGuid().IsPet()) - { - // only affect pet and owner - targetUnitMap.push_back(m_caster); - if (Unit* owner = m_caster->GetOwner()) - targetUnitMap.push_back(owner); - } - else - { - FillRaidOrPartyTargets(targetUnitMap, m_caster, m_caster, radius, false, true, true); - } - break; - } - case TARGET_ALL_PARTY_AROUND_CASTER_2: - case TARGET_ALL_PARTY: - { - FillRaidOrPartyTargets(targetUnitMap, m_caster, m_caster, radius, false, true, true); - break; - } - case TARGET_ALL_RAID_AROUND_CASTER: - { - if (m_spellInfo->Id == 57669) // Replenishment (special target selection) - { - // in arena, target should be only caster - if (m_caster->GetMap()->IsBattleArena()) - targetUnitMap.push_back(m_caster); - else - FillRaidOrPartyManaPriorityTargets(targetUnitMap, m_caster, m_caster, radius, 10, true, false, true); - } - else if (m_spellInfo->Id == 52759) // Ancestral Awakening (special target selection) - FillRaidOrPartyHealthPriorityTargets(targetUnitMap, m_caster, m_caster, radius, 1, true, false, true); - else - FillRaidOrPartyTargets(targetUnitMap, m_caster, m_caster, radius, true, true, IsPositiveSpell(m_spellInfo->Id)); - break; - } - case TARGET_SINGLE_FRIEND: - case TARGET_SINGLE_FRIEND_2: - if (m_targets.getUnitTarget()) - targetUnitMap.push_back(m_targets.getUnitTarget()); - break; - case TARGET_NONCOMBAT_PET: - if (Unit* target = m_targets.getUnitTarget()) - if (target->GetTypeId() == TYPEID_UNIT && ((Creature*)target)->IsPet() && ((Pet*)target)->getPetType() == MINI_PET) - targetUnitMap.push_back(target); - break; - case TARGET_SUMMONER: - { - WorldObject* caster = GetAffectiveCasterObject(); - if (!caster) - return; - - if (caster->GetTypeId() == TYPEID_UNIT && ((Creature*)caster)->IsTemporarySummon()) - targetUnitMap.push_back(((TemporarySummon*)(Creature*)caster)->GetSummoner()); - else if (caster->GetTypeId() == TYPEID_GAMEOBJECT && !((GameObject*)caster)->HasStaticDBSpawnData()) - targetUnitMap.push_back(((GameObject*)caster)->GetOwner()); - else - sLog.outError("SPELL: Spell ID %u with target ID %u was used by non temporary summon object %s.", m_spellInfo->Id, targetMode, caster->GetGuidStr().c_str()); - break; - } - case TARGET_CONTROLLED_VEHICLE: - if (m_caster->IsBoarded() && m_caster->GetTransportInfo()->IsOnVehicle()) - targetUnitMap.push_back((Unit*)m_caster->GetTransportInfo()->GetTransport()); - break; - case TARGET_VEHICLE_PASSENGER_0: - case TARGET_VEHICLE_PASSENGER_1: - case TARGET_VEHICLE_PASSENGER_2: - case TARGET_VEHICLE_PASSENGER_3: - case TARGET_VEHICLE_PASSENGER_4: - case TARGET_VEHICLE_PASSENGER_5: - case TARGET_VEHICLE_PASSENGER_6: - case TARGET_VEHICLE_PASSENGER_7: - if (m_caster->IsVehicle()) - if (Unit* passenger = m_caster->GetVehicleInfo()->GetPassenger(targetMode - TARGET_VEHICLE_PASSENGER_0)) - targetUnitMap.push_back(passenger); - break; - case TARGET_CASTER_COORDINATES: - { - // Check original caster is GO - set its coordinates as src cast - if (WorldObject* caster = GetCastingObject()) - m_targets.setSource(caster->GetPositionX(), caster->GetPositionY(), caster->GetPositionZ()); - break; - } - case TARGET_ALL_HOSTILE_UNITS_AROUND_CASTER: - FillAreaTargets(targetUnitMap, radius, PUSH_SELF_CENTER, SPELL_TARGETS_HOSTILE); - break; - case TARGET_ALL_FRIENDLY_UNITS_AROUND_CASTER: - switch (m_spellInfo->Id) - { - case 56153: // Guardian Aura - Ahn'Kahet - FillAreaTargets(targetUnitMap, radius, PUSH_SELF_CENTER, SPELL_TARGETS_FRIENDLY); - targetUnitMap.remove(m_caster); - break; - case 64844: // Divine Hymn - // target amount stored in parent spell dummy effect but hard to access - FillRaidOrPartyHealthPriorityTargets(targetUnitMap, m_caster, m_caster, radius, 3, true, false, true); - break; - case 64904: // Hymn of Hope - // target amount stored in parent spell dummy effect but hard to access - FillRaidOrPartyManaPriorityTargets(targetUnitMap, m_caster, m_caster, radius, 3, true, false, true); - break; - default: - // selected friendly units (for casting objects) around casting object - FillAreaTargets(targetUnitMap, radius, PUSH_SELF_CENTER, SPELL_TARGETS_FRIENDLY, GetCastingObject()); - break; - } - break; - case TARGET_ALL_FRIENDLY_UNITS_IN_AREA: - // Death Pact (in fact selection by player selection) - if (m_spellInfo->Id == 48743) - { - // checked in Spell::CheckCast - if (m_caster->GetTypeId() == TYPEID_PLAYER) - if (Unit* target = m_caster->GetMap()->GetPet(((Player*)m_caster)->GetSelectionGuid())) - targetUnitMap.push_back(target); - } - // Circle of Healing - else if (m_spellInfo->GetSpellFamilyName() == SPELLFAMILY_PRIEST && m_spellInfo->SpellVisual[0] == 8253) - { - Unit* target = m_targets.getUnitTarget(); - if (!target) - target = m_caster; - - uint32 count = 5; - // Glyph of Circle of Healing - if (Aura const* glyph = m_caster->GetDummyAura(55675)) - count += glyph->GetModifier()->m_amount; - - FillRaidOrPartyHealthPriorityTargets(targetUnitMap, m_caster, target, radius, count, true, false, true); - } - // Wild Growth - else if (m_spellInfo->GetSpellFamilyName() == SPELLFAMILY_DRUID && m_spellInfo->SpellIconID == 2864) - { - Unit* target = m_targets.getUnitTarget(); - if (!target) - target = m_caster; - uint32 count = CalculateDamage(EFFECT_INDEX_2, m_caster); // stored in dummy effect, affected by mods - - FillRaidOrPartyHealthPriorityTargets(targetUnitMap, m_caster, target, radius, count, true, false, true); - } - else - FillAreaTargets(targetUnitMap, radius, PUSH_DEST_CENTER, SPELL_TARGETS_FRIENDLY); - break; - // TARGET_SINGLE_PARTY means that the spells can only be casted on a party member and not on the caster (some seals, fire shield from imp, etc..) - case TARGET_SINGLE_PARTY: - { - Unit* target = m_targets.getUnitTarget(); - // Those spells apparently can't be casted on the caster. - if (target && target != m_caster) - { - // Can only be casted on group's members or its pets - Group* pGroup = NULL; - - Unit* owner = m_caster->GetCharmerOrOwner(); - Unit* targetOwner = target->GetCharmerOrOwner(); - if (owner) - { - if (owner->GetTypeId() == TYPEID_PLAYER) - { - if (target == owner) - { - targetUnitMap.push_back(target); - break; - } - pGroup = ((Player*)owner)->GetGroup(); - } - } - else if (m_caster->GetTypeId() == TYPEID_PLAYER) - { - if (targetOwner == m_caster && target->GetTypeId() == TYPEID_UNIT && ((Creature*)target)->IsPet()) - { - targetUnitMap.push_back(target); - break; - } - pGroup = ((Player*)m_caster)->GetGroup(); - } - - if (pGroup) - { - // Our target can also be a player's pet who's grouped with us or our pet. But can't be controlled player - if (targetOwner) - { - if (targetOwner->GetTypeId() == TYPEID_PLAYER && - target->GetTypeId() == TYPEID_UNIT && (((Creature*)target)->IsPet()) && - target->GetOwnerGuid() == targetOwner->GetObjectGuid() && - pGroup->IsMember(((Player*)targetOwner)->GetObjectGuid())) - { - targetUnitMap.push_back(target); - } - } - // 1Our target can be a player who is on our group - else if (target->GetTypeId() == TYPEID_PLAYER && pGroup->IsMember(((Player*)target)->GetObjectGuid())) - { - targetUnitMap.push_back(target); - } - } - } - break; - } - case TARGET_GAMEOBJECT: - if (m_targets.getGOTarget()) - AddGOTarget(m_targets.getGOTarget(), effIndex); - break; - case TARGET_IN_FRONT_OF_CASTER: - { - SpellNotifyPushType pushType = PUSH_IN_FRONT; - switch (m_spellInfo->SpellVisual[0]) // Some spell require a different target fill - { - case 3879: pushType = PUSH_IN_BACK; break; - case 7441: pushType = PUSH_IN_FRONT_15; break; - case 8669: pushType = PUSH_IN_FRONT_15; break; - } - FillAreaTargets(targetUnitMap, radius, pushType, SPELL_TARGETS_AOE_DAMAGE); - break; - } - case TARGET_LARGE_FRONTAL_CONE: - FillAreaTargets(targetUnitMap, radius, PUSH_IN_FRONT_90, SPELL_TARGETS_AOE_DAMAGE); - break; - case TARGET_NARROW_FRONTAL_CONE: - FillAreaTargets(targetUnitMap, radius, PUSH_IN_FRONT_15, SPELL_TARGETS_AOE_DAMAGE); - break; - case TARGET_IN_FRONT_OF_CASTER_30: - FillAreaTargets(targetUnitMap, radius, PUSH_IN_FRONT_30, SPELL_TARGETS_AOE_DAMAGE); - break; - case TARGET_DUELVSPLAYER: - { - if (Unit* target = m_targets.getUnitTarget()) - { - if (m_caster->IsFriendlyTo(target)) - { - targetUnitMap.push_back(target); - } - else - { - if (Unit* pUnitTarget = m_caster->SelectMagnetTarget(target, this, effIndex)) - { - if (target != pUnitTarget) - { - m_targets.setUnitTarget(pUnitTarget); - m_spellFlags |= SPELL_FLAG_REDIRECTED; - } - targetUnitMap.push_back(pUnitTarget); - } - } - } - break; - } - case TARGET_GAMEOBJECT_ITEM: - if (m_targets.getGOTargetGuid()) - AddGOTarget(m_targets.getGOTarget(), effIndex); - else if (m_targets.getItemTarget()) - AddItemTarget(m_targets.getItemTarget(), effIndex); - break; - case TARGET_MASTER: - if (Unit* owner = m_caster->GetCharmerOrOwner()) - targetUnitMap.push_back(owner); - break; - case TARGET_ALL_ENEMY_IN_AREA_CHANNELED: - // targets the ground, not the units in the area - if (spellEffect && spellEffect->Effect!=SPELL_EFFECT_PERSISTENT_AREA_AURA) - FillAreaTargets(targetUnitMap, radius, PUSH_DEST_CENTER, SPELL_TARGETS_AOE_DAMAGE); - break; - case TARGET_MINION: - if(spellEffect && spellEffect->Effect != SPELL_EFFECT_DUEL) - targetUnitMap.push_back(m_caster); - break; - case TARGET_SINGLE_ENEMY: - { - if (Unit* pUnitTarget = m_caster->SelectMagnetTarget(m_targets.getUnitTarget(), this, effIndex)) - { - if (m_targets.getUnitTarget() && m_targets.getUnitTarget() != pUnitTarget) - m_spellFlags |= SPELL_FLAG_REDIRECTED; - - targetUnitMap.push_back(pUnitTarget); - } - break; - } - case TARGET_AREAEFFECT_PARTY: - { - Unit* owner = m_caster->GetCharmerOrOwner(); - Player* pTarget = NULL; - - if (owner) - { - targetUnitMap.push_back(m_caster); - if (owner->GetTypeId() == TYPEID_PLAYER) - pTarget = (Player*)owner; - } - else if (m_caster->GetTypeId() == TYPEID_PLAYER) - { - if (Unit* target = m_targets.getUnitTarget()) - { - if (target->GetTypeId() != TYPEID_PLAYER) - { - if (((Creature*)target)->IsPet()) - { - Unit* targetOwner = target->GetOwner(); - if (targetOwner->GetTypeId() == TYPEID_PLAYER) - pTarget = (Player*)targetOwner; - } - } - else - pTarget = (Player*)target; - } - } - - Group* pGroup = pTarget ? pTarget->GetGroup() : NULL; - - if (pGroup) - { - uint8 subgroup = pTarget->GetSubGroup(); - - for (GroupReference* itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player* Target = itr->getSource(); - - // IsHostileTo check duel and controlled by enemy - if (Target && Target->GetSubGroup() == subgroup && !m_caster->IsHostileTo(Target)) - { - if (pTarget->IsWithinDistInMap(Target, radius)) - targetUnitMap.push_back(Target); - - if (Pet* pet = Target->GetPet()) - if (pTarget->IsWithinDistInMap(pet, radius)) - targetUnitMap.push_back(pet); - } - } - } - else if (owner) - { - if (m_caster->IsWithinDistInMap(owner, radius)) - targetUnitMap.push_back(owner); - } - else if (pTarget) - { - targetUnitMap.push_back(pTarget); - - if (Pet* pet = pTarget->GetPet()) - if (m_caster->IsWithinDistInMap(pet, radius)) - targetUnitMap.push_back(pet); - } - break; - } - case TARGET_SCRIPT: - { - if (m_targets.getUnitTarget()) - targetUnitMap.push_back(m_targets.getUnitTarget()); - if (m_targets.getItemTarget()) - AddItemTarget(m_targets.getItemTarget(), effIndex); - break; - } - case TARGET_SELF_FISHING: - targetUnitMap.push_back(m_caster); - break; - case TARGET_CHAIN_HEAL: - { - Unit* pUnitTarget = m_targets.getUnitTarget(); - if (!pUnitTarget) - break; - - if (EffectChainTarget <= 1) - targetUnitMap.push_back(pUnitTarget); - else - { - unMaxTargets = EffectChainTarget; - float max_range = radius + unMaxTargets * CHAIN_SPELL_JUMP_RADIUS; - - UnitList tempTargetUnitMap; - - FillAreaTargets(tempTargetUnitMap, max_range, PUSH_SELF_CENTER, SPELL_TARGETS_FRIENDLY); - - if (m_caster != pUnitTarget && std::find(tempTargetUnitMap.begin(), tempTargetUnitMap.end(), m_caster) == tempTargetUnitMap.end()) - tempTargetUnitMap.push_front(m_caster); - - tempTargetUnitMap.sort(TargetDistanceOrderNear(pUnitTarget)); - - if (tempTargetUnitMap.empty()) - break; - - if (*tempTargetUnitMap.begin() == pUnitTarget) - tempTargetUnitMap.erase(tempTargetUnitMap.begin()); - - targetUnitMap.push_back(pUnitTarget); - uint32 t = unMaxTargets - 1; - Unit* prev = pUnitTarget; - UnitList::iterator next = tempTargetUnitMap.begin(); - - while (t && next != tempTargetUnitMap.end()) - { - if (!prev->IsWithinDist(*next, CHAIN_SPELL_JUMP_RADIUS)) - break; - - if (!m_spellInfo->HasAttribute(SPELL_ATTR_EX2_IGNORE_LOS) && !prev->IsWithinLOSInMap(*next)) - { - ++next; - continue; - } - - if ((*next)->GetHealth() == (*next)->GetMaxHealth()) - { - next = tempTargetUnitMap.erase(next); - continue; - } - - prev = *next; - targetUnitMap.push_back(prev); - tempTargetUnitMap.erase(next); - tempTargetUnitMap.sort(TargetDistanceOrderNear(prev)); - next = tempTargetUnitMap.begin(); - - --t; - } - } - break; - } - case TARGET_CURRENT_ENEMY_COORDINATES: - { - Unit* currentTarget = m_targets.getUnitTarget(); - if (currentTarget) - { - targetUnitMap.push_back(currentTarget); - m_targets.setDestination(currentTarget->GetPositionX(), currentTarget->GetPositionY(), currentTarget->GetPositionZ()); - } - break; - } - case TARGET_AREAEFFECT_PARTY_AND_CLASS: - { - Player* targetPlayer = m_targets.getUnitTarget() && m_targets.getUnitTarget()->GetTypeId() == TYPEID_PLAYER - ? (Player*)m_targets.getUnitTarget() : NULL; - - Group* pGroup = targetPlayer ? targetPlayer->GetGroup() : NULL; - if (pGroup) - { - for (GroupReference* itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player* Target = itr->getSource(); - - // IsHostileTo check duel and controlled by enemy - if (Target && targetPlayer->IsWithinDistInMap(Target, radius) && - targetPlayer->getClass() == Target->getClass() && - !m_caster->IsHostileTo(Target)) - { - targetUnitMap.push_back(Target); - } - } - } - else if (m_targets.getUnitTarget()) - targetUnitMap.push_back(m_targets.getUnitTarget()); - break; - } - case TARGET_TABLE_X_Y_Z_COORDINATES: - { - if (SpellTargetPosition const* st = sSpellMgr.GetSpellTargetPosition(m_spellInfo->Id)) - { - m_targets.setDestination(st->target_X, st->target_Y, st->target_Z); - // TODO - maybe use an (internal) value for the map for neat far teleport handling - - // far-teleport spells are handled in SpellEffect, elsewise report an error about an unexpected map (spells are always locally) - if (st->target_mapId != m_caster->GetMapId() && spellEffect && spellEffect->Effect != SPELL_EFFECT_TELEPORT_UNITS && spellEffect->Effect != SPELL_EFFECT_BIND) - sLog.outError("SPELL: wrong map (%u instead %u) target coordinates for spell ID %u", st->target_mapId, m_caster->GetMapId(), m_spellInfo->Id); - } - else - sLog.outError("SPELL: unknown target coordinates for spell ID %u", m_spellInfo->Id); - break; - } - case TARGET_INFRONT_OF_VICTIM: - case TARGET_BEHIND_VICTIM: - case TARGET_RIGHT_FROM_VICTIM: - case TARGET_LEFT_FROM_VICTIM: - { - Unit* pTarget = NULL; - - // explicit cast data from client or server-side cast - // some spell at client send caster - if (m_targets.getUnitTarget() && m_targets.getUnitTarget() != m_caster) - pTarget = m_targets.getUnitTarget(); - else if (m_caster->getVictim()) - pTarget = m_caster->getVictim(); - else if (m_caster->GetTypeId() == TYPEID_PLAYER) - pTarget = ObjectAccessor::GetUnit(*m_caster, ((Player*)m_caster)->GetSelectionGuid()); - else if (m_targets.getUnitTarget()) - pTarget = m_caster; - - if (pTarget) - { - float angle = 0.0f; - - switch (targetMode) - { - case TARGET_INFRONT_OF_VICTIM: break; - case TARGET_BEHIND_VICTIM: angle = M_PI_F; break; - case TARGET_RIGHT_FROM_VICTIM: angle = -M_PI_F / 2; break; - case TARGET_LEFT_FROM_VICTIM: angle = M_PI_F / 2; break; - } - - float _target_x, _target_y, _target_z; - pTarget->GetClosePoint(_target_x, _target_y, _target_z, pTarget->GetObjectBoundingRadius(), radius, angle); - if (pTarget->IsWithinLOS(_target_x, _target_y, _target_z)) - { - targetUnitMap.push_back(m_caster); - m_targets.setDestination(_target_x, _target_y, _target_z); - } - } - break; - } - case TARGET_DYNAMIC_OBJECT_COORDINATES: - // if parent spell create dynamic object extract area from it - if (DynamicObject* dynObj = m_caster->GetDynObject(m_triggeredByAuraSpell ? m_triggeredByAuraSpell->Id : m_spellInfo->Id)) - m_targets.setDestination(dynObj->GetPositionX(), dynObj->GetPositionY(), dynObj->GetPositionZ()); - // else use destination of target if no destination set (ie for Mind Sear - 53022) - else if (!(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) && m_targets.m_targetMask & TARGET_FLAG_UNIT) - m_targets.setDestination(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ); - break; - case TARGET_DYNAMIC_OBJECT_FRONT: - case TARGET_DYNAMIC_OBJECT_BEHIND: - case TARGET_DYNAMIC_OBJECT_LEFT_SIDE: - case TARGET_DYNAMIC_OBJECT_RIGHT_SIDE: - { - if (!(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)) - { - // General override, we don't want to use max spell range here. - // Note: 0.0 radius is also for index 36. It is possible that 36 must be defined as - // "at the base of", in difference to 0 which appear to be "directly in front of". - // TODO: some summoned will make caster be half inside summoned object. Need to fix - // that in the below code (nearpoint vs closepoint, etc). - if (!spellEffect || spellEffect->GetRadiusIndex() == 0) - radius = 0.0f; - - if (m_spellInfo->Id == 50019) // Hawk Hunting, problematic 50K radius - radius = 10.0f; - - float angle = m_caster->GetOrientation(); - switch (targetMode) - { - case TARGET_DYNAMIC_OBJECT_FRONT: break; - case TARGET_DYNAMIC_OBJECT_BEHIND: angle += M_PI_F; break; - case TARGET_DYNAMIC_OBJECT_LEFT_SIDE: angle += M_PI_F / 2; break; - case TARGET_DYNAMIC_OBJECT_RIGHT_SIDE: angle -= M_PI_F / 2; break; - } - - float x, y; - m_caster->GetNearPoint2D(x, y, radius + m_caster->GetObjectBoundingRadius(), angle); - m_targets.setDestination(x, y, m_caster->GetPositionZ()); - } - - targetUnitMap.push_back(m_caster); - break; - } - case TARGET_POINT_AT_NORTH: - case TARGET_POINT_AT_SOUTH: - case TARGET_POINT_AT_EAST: - case TARGET_POINT_AT_WEST: - case TARGET_POINT_AT_NE: - case TARGET_POINT_AT_NW: - case TARGET_POINT_AT_SE: - case TARGET_POINT_AT_SW: - { - - if (!(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)) - { - Unit* currentTarget = m_targets.getUnitTarget() ? m_targets.getUnitTarget() : m_caster; - float angle = currentTarget != m_caster ? currentTarget->GetAngle(m_caster) : m_caster->GetOrientation(); - - switch (targetMode) - { - case TARGET_POINT_AT_NORTH: break; - case TARGET_POINT_AT_SOUTH: angle += M_PI_F; break; - case TARGET_POINT_AT_EAST: angle -= M_PI_F / 2; break; - case TARGET_POINT_AT_WEST: angle += M_PI_F / 2; break; - case TARGET_POINT_AT_NE: angle -= M_PI_F / 4; break; - case TARGET_POINT_AT_NW: angle += M_PI_F / 4; break; - case TARGET_POINT_AT_SE: angle -= 3 * M_PI_F / 4; break; - case TARGET_POINT_AT_SW: angle += 3 * M_PI_F / 4; break; - } - - float x, y; - currentTarget->GetNearPoint2D(x, y, radius + currentTarget->GetObjectBoundingRadius(), angle); - m_targets.setDestination(x, y, currentTarget->GetPositionZ()); - } - break; - } - case TARGET_DIRECTLY_FORWARD: - { - if (!(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)) - { - SpellRangeEntry const* rEntry = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex); - float minRange = GetSpellMinRange(rEntry); - float maxRange = GetSpellMaxRange(rEntry); - float dist = minRange + rand_norm_f() * (maxRange - minRange); - - float _target_x, _target_y, _target_z; - m_caster->GetClosePoint(_target_x, _target_y, _target_z, m_caster->GetObjectBoundingRadius(), dist); - m_targets.setDestination(_target_x, _target_y, _target_z); - } - - targetUnitMap.push_back(m_caster); - break; - } - case TARGET_EFFECT_SELECT: - { - // add here custom effects that need default target. - // FOR EVERY TARGET TYPE THERE IS A DIFFERENT FILL!! - if(!spellEffect) - break; - - switch(spellEffect->Effect) - { - case SPELL_EFFECT_DUMMY: - { - switch (m_spellInfo->Id) - { - case 20577: // Cannibalize - { - WorldObject* result = FindCorpseUsing (); - - if (result) - { - switch (result->GetTypeId()) - { - case TYPEID_UNIT: - case TYPEID_PLAYER: - targetUnitMap.push_back((Unit*)result); - break; - case TYPEID_CORPSE: - m_targets.setCorpseTarget((Corpse*)result); - if (Player* owner = ObjectAccessor::FindPlayer(((Corpse*)result)->GetOwnerGuid())) - targetUnitMap.push_back(owner); - break; - } - } - else - { - // clear cooldown at fail - if (m_caster->GetTypeId() == TYPEID_PLAYER) - ((Player*)m_caster)->RemoveSpellCooldown(m_spellInfo->Id, true); - SendCastResult(SPELL_FAILED_NO_EDIBLE_CORPSES); - finish(false); - } - break; - } - default: - if (m_targets.getUnitTarget()) - targetUnitMap.push_back(m_targets.getUnitTarget()); - break; - } - // Add AoE target-mask to self, if no target-dest provided already - if ((m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) == 0) - m_targets.setDestination(m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ()); - break; - } - case SPELL_EFFECT_BIND: - case SPELL_EFFECT_RESURRECT: - case SPELL_EFFECT_PARRY: - case SPELL_EFFECT_BLOCK: - case SPELL_EFFECT_CREATE_ITEM: - case SPELL_EFFECT_WEAPON: - case SPELL_EFFECT_TRIGGER_SPELL: - case SPELL_EFFECT_TRIGGER_MISSILE: - case SPELL_EFFECT_LEARN_SPELL: - case SPELL_EFFECT_SKILL_STEP: - case SPELL_EFFECT_PROFICIENCY: - case SPELL_EFFECT_SUMMON_OBJECT_WILD: - case SPELL_EFFECT_SELF_RESURRECT: - case SPELL_EFFECT_REPUTATION: - case SPELL_EFFECT_SEND_TAXI: - if (m_targets.getUnitTarget()) - targetUnitMap.push_back(m_targets.getUnitTarget()); - // Triggered spells have additional spell targets - cast them even if no explicit unit target is given (required for spell 50516 for example) - else if (spellEffect->Effect == SPELL_EFFECT_TRIGGER_SPELL) - targetUnitMap.push_back(m_caster); - break; - case SPELL_EFFECT_SUMMON_PLAYER: - if (m_caster->GetTypeId() == TYPEID_PLAYER && ((Player*)m_caster)->GetSelectionGuid()) - if (Player* target = sObjectMgr.GetPlayer(((Player*)m_caster)->GetSelectionGuid())) - targetUnitMap.push_back(target); - break; - case SPELL_EFFECT_RESURRECT_NEW: - if (m_targets.getUnitTarget()) - targetUnitMap.push_back(m_targets.getUnitTarget()); - if (m_targets.getCorpseTargetGuid()) - { - if (Corpse* corpse = m_caster->GetMap()->GetCorpse(m_targets.getCorpseTargetGuid())) - if (Player* owner = ObjectAccessor::FindPlayer(corpse->GetOwnerGuid())) - targetUnitMap.push_back(owner); - } - break; - case SPELL_EFFECT_TELEPORT_UNITS: - case SPELL_EFFECT_SUMMON: - case SPELL_EFFECT_SUMMON_CHANGE_ITEM: - case SPELL_EFFECT_TRANS_DOOR: - case SPELL_EFFECT_ADD_FARSIGHT: - case SPELL_EFFECT_APPLY_GLYPH: - case SPELL_EFFECT_STUCK: - case SPELL_EFFECT_BREAK_PLAYER_TARGETING: - case SPELL_EFFECT_SUMMON_ALL_TOTEMS: - case SPELL_EFFECT_FEED_PET: - case SPELL_EFFECT_DESTROY_ALL_TOTEMS: - case SPELL_EFFECT_SKILL: - targetUnitMap.push_back(m_caster); - break; - case SPELL_EFFECT_PERSISTENT_AREA_AURA: - if (Unit* currentTarget = m_targets.getUnitTarget()) - m_targets.setDestination(currentTarget->GetPositionX(), currentTarget->GetPositionY(), currentTarget->GetPositionZ()); - break; - case SPELL_EFFECT_LEARN_PET_SPELL: - if (Pet* pet = m_caster->GetPet()) - targetUnitMap.push_back(pet); - break; - case SPELL_EFFECT_ENCHANT_ITEM: - case SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY: - case SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC: - case SPELL_EFFECT_DISENCHANT: - case SPELL_EFFECT_PROSPECTING: - case SPELL_EFFECT_MILLING: - if (m_targets.getItemTarget()) - AddItemTarget(m_targets.getItemTarget(), effIndex); - break; - case SPELL_EFFECT_APPLY_AURA: - switch(spellEffect->EffectApplyAuraName) - { - case SPELL_AURA_ADD_FLAT_MODIFIER: // some spell mods auras have 0 target modes instead expected TARGET_SELF(1) (and present for other ranks for same spell for example) - case SPELL_AURA_ADD_PCT_MODIFIER: - targetUnitMap.push_back(m_caster); - break; - default: // apply to target in other case - if (m_targets.getUnitTarget()) - targetUnitMap.push_back(m_targets.getUnitTarget()); - break; - } - break; - case SPELL_EFFECT_APPLY_AREA_AURA_PARTY: - // AreaAura - if ((m_spellInfo->Attributes == (SPELL_ATTR_NOT_SHAPESHIFT | SPELL_ATTR_UNK18 | SPELL_ATTR_CASTABLE_WHILE_MOUNTED | SPELL_ATTR_CASTABLE_WHILE_SITTING)) || (m_spellInfo->Attributes == SPELL_ATTR_NOT_SHAPESHIFT)) - SetTargetMap(effIndex, TARGET_AREAEFFECT_PARTY, targetUnitMap); - break; - case SPELL_EFFECT_SKIN_PLAYER_CORPSE: - if (m_targets.getUnitTarget()) - targetUnitMap.push_back(m_targets.getUnitTarget()); - else if (m_targets.getCorpseTargetGuid()) - { - if (Corpse* corpse = m_caster->GetMap()->GetCorpse(m_targets.getCorpseTargetGuid())) - if (Player* owner = ObjectAccessor::FindPlayer(corpse->GetOwnerGuid())) - targetUnitMap.push_back(owner); - } - break; - default: - break; - } - break; - } - default: - // sLog.outError( "SPELL: Unknown implicit target (%u) for spell ID %u", targetMode, m_spellInfo->Id ); - break; - } - - if (unMaxTargets && targetUnitMap.size() > unMaxTargets) - { - // make sure one unit is always removed per iteration - uint32 removed_utarget = 0; - for (UnitList::iterator itr = targetUnitMap.begin(), next; itr != targetUnitMap.end(); itr = next) - { - next = itr; - ++next; - if (!*itr) continue; - if ((*itr) == m_targets.getUnitTarget()) - { - targetUnitMap.erase(itr); - removed_utarget = 1; - // break; - } - } - // remove random units from the map - while (targetUnitMap.size() > unMaxTargets - removed_utarget) - { - uint32 poz = urand(0, targetUnitMap.size() - 1); - for (UnitList::iterator itr = targetUnitMap.begin(); itr != targetUnitMap.end(); ++itr, --poz) - { - if (!*itr) continue; - - if (!poz) - { - targetUnitMap.erase(itr); - break; - } - } - } - // the player's target will always be added to the map - if (removed_utarget && m_targets.getUnitTarget()) - targetUnitMap.push_back(m_targets.getUnitTarget()); - } - if (!tempTargetGOList.empty()) // GO CASE - { - if (unMaxTargets && tempTargetGOList.size() > unMaxTargets) - { - // make sure one go is always removed per iteration - uint32 removed_utarget = 0; - for (std::list::iterator itr = tempTargetGOList.begin(), next; itr != tempTargetGOList.end(); itr = next) - { - next = itr; - ++next; - if (!*itr) continue; - if ((*itr) == m_targets.getGOTarget()) - { - tempTargetGOList.erase(itr); - removed_utarget = 1; - // break; - } - } - // remove random units from the map - while (tempTargetGOList.size() > unMaxTargets - removed_utarget) - { - uint32 poz = urand(0, tempTargetGOList.size() - 1); - for (std::list::iterator itr = tempTargetGOList.begin(); itr != tempTargetGOList.end(); ++itr, --poz) - { - if (!*itr) continue; - - if (!poz) - { - tempTargetGOList.erase(itr); - break; - } - } - } - } - // Add resulting GOs as GOTargets - for (std::list::iterator iter = tempTargetGOList.begin(); iter != tempTargetGOList.end(); ++iter) - AddGOTarget(*iter, effIndex); - } -} - -void Spell::prepare(SpellCastTargets const* targets, Aura* triggeredByAura) -{ - m_targets = *targets; - - m_spellState = SPELL_STATE_PREPARING; - - m_castPositionX = m_caster->GetPositionX(); - m_castPositionY = m_caster->GetPositionY(); - m_castPositionZ = m_caster->GetPositionZ(); - m_castOrientation = m_caster->GetOrientation(); - - if (triggeredByAura) - m_triggeredByAuraSpell = triggeredByAura->GetSpellProto(); - - // create and add update event for this spell - SpellEvent* Event = new SpellEvent(this); - m_caster->m_Events.AddEvent(Event, m_caster->m_Events.CalculateTime(1)); - - // Prevent casting at cast another spell (ServerSide check) - if (m_caster->IsNonMeleeSpellCasted(false, true, true) && m_cast_count) - { - SendCastResult(SPELL_FAILED_SPELL_IN_PROGRESS); - finish(false); - return; - } - - // Fill cost data - m_powerCost = CalculatePowerCost(m_spellInfo, m_caster, this, m_CastItem); - - SpellCastResult result = CheckCast(true); - if (result != SPELL_CAST_OK && !IsAutoRepeat()) // always cast autorepeat dummy for triggering - { - if (triggeredByAura) - { - SendChannelUpdate(0); - triggeredByAura->GetHolder()->SetAuraDuration(0); - } - SendCastResult(result); - finish(false); - return; - } - - // Prepare data for triggers - prepareDataForTriggerSystem(); - - // calculate cast time (calculated after first CheckCast check to prevent charge counting for first CheckCast fail) - m_casttime = GetSpellCastTime(m_spellInfo, this); - m_duration = CalculateSpellDuration(m_spellInfo, m_caster); - - // set timer base at cast time - ReSetTimer(); - - // stealth must be removed at cast starting (at show channel bar) - // skip triggered spell (item equip spell casting and other not explicit character casts/item uses) - if (!m_IsTriggeredSpell && isSpellBreakStealth(m_spellInfo)) - { - m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); - m_caster->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - } - - // add non-triggered (with cast time and without) - if (!m_IsTriggeredSpell) - { - // add to cast type slot - m_caster->SetCurrentCastedSpell(this); - - // will show cast bar - SendSpellStart(); - - TriggerGlobalCooldown(); - } - // execute triggered without cast time explicitly in call point - else if (m_timer == 0) - cast(true); - // else triggered with cast time will execute execute at next tick or later - // without adding to cast type slot - // will not show cast bar but will show effects at casting time etc -} - -void Spell::cancel() -{ - if (m_spellState == SPELL_STATE_FINISHED) - return; - - // channeled spells don't display interrupted message even if they are interrupted, possible other cases with no "Interrupted" message - bool sendInterrupt = IsChanneledSpell(m_spellInfo) ? false : true; - - m_autoRepeat = false; - switch (m_spellState) - { - case SPELL_STATE_PREPARING: - CancelGlobalCooldown(); - - //(no break) - case SPELL_STATE_DELAYED: - { - SendInterrupted(0); - - if (sendInterrupt) - SendCastResult(SPELL_FAILED_INTERRUPTED); - } break; - - case SPELL_STATE_CASTING: - { - for (TargetList::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - { - if (ihit->missCondition == SPELL_MISS_NONE) - { - Unit* unit = m_caster->GetObjectGuid() == (*ihit).targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID); - if (unit && unit->isAlive()) - unit->RemoveAurasByCasterSpell(m_spellInfo->Id, m_caster->GetObjectGuid()); - - // prevent other effects applying if spell is already interrupted - // i.e. if effects have different targets and it was interrupted on one of them when - // haven't yet applied to another - ihit->processed = true; - } - } - - SendChannelUpdate(0); - SendInterrupted(0); - - if (sendInterrupt) - SendCastResult(SPELL_FAILED_INTERRUPTED); - } break; - - default: - { - } break; - } - - finish(false); - m_caster->RemoveDynObject(m_spellInfo->Id); - m_caster->RemoveGameObject(m_spellInfo->Id, true); -} - -void Spell::cast(bool skipCheck) -{ - SetExecutedCurrently(true); - - if (!m_caster->CheckAndIncreaseCastCounter()) - { - if (m_triggeredByAuraSpell) - sLog.outError("Spell %u triggered by aura spell %u too deep in cast chain for cast. Cast not allowed for prevent overflow stack crash.", m_spellInfo->Id, m_triggeredByAuraSpell->Id); - else - sLog.outError("Spell %u too deep in cast chain for cast. Cast not allowed for prevent overflow stack crash.", m_spellInfo->Id); - - SendCastResult(SPELL_FAILED_ERROR); - finish(false); - SetExecutedCurrently(false); - return; - } - - // update pointers base at GUIDs to prevent access to already nonexistent object - UpdatePointers(); - - // cancel at lost main target unit - if (!m_targets.getUnitTarget() && m_targets.getUnitTargetGuid() && m_targets.getUnitTargetGuid() != m_caster->GetObjectGuid()) - { - cancel(); - m_caster->DecreaseCastCounter(); - SetExecutedCurrently(false); - return; - } - - if (m_caster->GetTypeId() != TYPEID_PLAYER && m_targets.getUnitTarget() && m_targets.getUnitTarget() != m_caster) - m_caster->SetInFront(m_targets.getUnitTarget()); - - SpellCastResult castResult = CheckPower(); - if (castResult != SPELL_CAST_OK) - { - SendCastResult(castResult); - finish(false); - m_caster->DecreaseCastCounter(); - SetExecutedCurrently(false); - return; - } - - // triggered cast called from Spell::prepare where it was already checked - if (!skipCheck) - { - castResult = CheckCast(false); - if (castResult != SPELL_CAST_OK) - { - SendCastResult(castResult); - finish(false); - m_caster->DecreaseCastCounter(); - SetExecutedCurrently(false); - return; - } - } - - SpellClassOptionsEntry const* classOpt = m_spellInfo->GetSpellClassOptions(); - - // different triggered (for caster) and precast (casted before apply effect to target) cases - switch(m_spellInfo->GetSpellFamilyName()) - { - case SPELLFAMILY_GENERIC: - { - // Bandages - if (m_spellInfo->GetMechanic() == MECHANIC_BANDAGE) - AddPrecastSpell(11196); // Recently Bandaged - // Stoneskin - else if (m_spellInfo->Id == 20594) - AddTriggeredSpell(65116); // Stoneskin - armor 10% for 8 sec - else if (m_spellInfo->Id == 68992) // Darkflight - { - AddPrecastSpell(96223); // Run Speed Marker - if (m_caster->HasWorgenForm()) - AddPrecastSpell(97709); // Altered Form - } - else if (m_spellInfo->Id == 68996) // Two Forms - { - if (m_caster->IsInWorgenForm()) - m_caster->RemoveSpellsCausingAura(SPELL_AURA_WORGEN_TRANSFORM); - else if (m_caster->HasWorgenForm()) - AddPrecastSpell(97709); // Altered Form - } - // Chaos Bane strength buff - else if (m_spellInfo->Id == 71904) - AddTriggeredSpell(73422); - break; - } - case SPELLFAMILY_MAGE: - { - // Ice Block - if (classOpt && classOpt->SpellFamilyFlags & UI64LIT(0x0000008000000000)) - AddPrecastSpell(41425); // Hypothermia - // Icy Veins - else if (m_spellInfo->Id == 12472) - { - if (m_caster->HasAura(56374)) // Glyph of Icy Veins - { - // not exist spell do it so apply directly - m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_DECREASE_SPEED); - m_caster->RemoveSpellsCausingAura(SPELL_AURA_HASTE_SPELLS); - } - } - // Fingers of Frost - else if (m_spellInfo->Id == 44544) - AddPrecastSpell(74396); // Fingers of Frost - break; - } - case SPELLFAMILY_WARRIOR: - { - // Shield Slam - if (classOpt && (classOpt->SpellFamilyFlags & UI64LIT(0x0000020000000000)) && m_spellInfo->GetCategory()==1209) - { - if (m_caster->HasAura(58375)) // Glyph of Blocking - AddTriggeredSpell(58374); // Glyph of Blocking - } - // Bloodrage - if (classOpt && (classOpt->SpellFamilyFlags & UI64LIT(0x0000000000000100))) - { - if (m_caster->HasAura(70844)) // Item - Warrior T10 Protection 4P Bonus - AddTriggeredSpell(70845); // Stoicism - } - // Bloodsurge (triggered), Sudden Death (triggered) - else if (m_spellInfo->Id == 46916 || m_spellInfo->Id == 52437) - // Item - Warrior T10 Melee 4P Bonus - if (Aura* aur = m_caster->GetAura(70847, EFFECT_INDEX_0)) - if (roll_chance_i(aur->GetModifier()->m_amount)) - AddTriggeredSpell(70849); // Extra Charge! - break; - } - case SPELLFAMILY_PRIEST: - { - // Power Word: Shield - if (m_spellInfo->GetMechanic() == MECHANIC_SHIELD && - (classOpt && classOpt->SpellFamilyFlags & UI64LIT(0x0000000000000001))) - AddPrecastSpell(6788); // Weakened Soul - // Prayer of Mending (jump animation), we need formal caster instead original for correct animation - else if (classOpt && classOpt->SpellFamilyFlags & UI64LIT(0x0000002000000000)) - AddTriggeredSpell(41637); - - switch (m_spellInfo->Id) - { - case 15237: AddTriggeredSpell(23455); break;// Holy Nova, rank 1 - case 15430: AddTriggeredSpell(23458); break;// Holy Nova, rank 2 - case 15431: AddTriggeredSpell(23459); break;// Holy Nova, rank 3 - case 27799: AddTriggeredSpell(27803); break;// Holy Nova, rank 4 - case 27800: AddTriggeredSpell(27804); break;// Holy Nova, rank 5 - case 27801: AddTriggeredSpell(27805); break;// Holy Nova, rank 6 - case 25331: AddTriggeredSpell(25329); break;// Holy Nova, rank 7 - case 48077: AddTriggeredSpell(48075); break;// Holy Nova, rank 8 - case 48078: AddTriggeredSpell(48076); break;// Holy Nova, rank 9 - default: break; - } - break; - } - case SPELLFAMILY_DRUID: - { - // Faerie Fire (Feral) - if (m_spellInfo->Id == 16857 && m_caster->GetShapeshiftForm() != FORM_CAT) - AddTriggeredSpell(60089); - // Clearcasting - else if (m_spellInfo->Id == 16870) - { - if (m_caster->HasAura(70718)) // Item - Druid T10 Balance 2P Bonus - AddPrecastSpell(70721); // Omen of Doom - } - // Berserk (Bear Mangle part) - else if (m_spellInfo->Id == 50334) - AddTriggeredSpell(58923); - break; - } - case SPELLFAMILY_ROGUE: - // Fan of Knives (main hand) - if (m_spellInfo->Id == 51723 && m_caster->GetTypeId() == TYPEID_PLAYER && - ((Player*)m_caster)->haveOffhandWeapon()) - { - AddTriggeredSpell(52874); // Fan of Knives (offhand) - } - break; - case SPELLFAMILY_HUNTER: - { - // Deterrence - if (m_spellInfo->Id == 19263) - AddPrecastSpell(67801); - // Kill Command - else if (m_spellInfo->Id == 34026) - { - if (m_caster->HasAura(37483)) // Improved Kill Command - Item set bonus - m_caster->CastSpell(m_caster, 37482, true);// Exploited Weakness - } - // Lock and Load - else if (m_spellInfo->Id == 56453) - AddPrecastSpell(67544); // Lock and Load Marker - break; - } - case SPELLFAMILY_PALADIN: - { - // Divine Illumination - if (m_spellInfo->Id == 31842) - { - if (m_caster->HasAura(70755)) // Item - Paladin T10 Holy 2P Bonus - AddPrecastSpell(71166); // Divine Illumination - } - // Hand of Reckoning - else if (m_spellInfo->Id == 62124) - { - if (m_targets.getUnitTarget() && m_targets.getUnitTarget()->getVictim() != m_caster) - AddPrecastSpell(67485); // Hand of Rekoning (no typos in name ;) ) - } - // Divine Shield, Divine Protection or Hand of Protection - else if (classOpt && classOpt->SpellFamilyFlags & UI64LIT(0x0000000000400080)) - { - AddPrecastSpell(25771); // Forbearance - AddPrecastSpell(61987); // Avenging Wrath Marker - } - // Lay on Hands - else if (classOpt && classOpt->SpellFamilyFlags & UI64LIT(0x0000000000008000)) - { - // only for self cast - if (m_caster == m_targets.getUnitTarget()) - AddPrecastSpell(25771); // Forbearance - } - // Avenging Wrath - else if (classOpt && classOpt->SpellFamilyFlags & UI64LIT(0x0000200000000000)) - AddPrecastSpell(61987); // Avenging Wrath Marker - break; - } - case SPELLFAMILY_SHAMAN: - { - SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(EFFECT_INDEX_0); - // Bloodlust - if (m_spellInfo->Id == 2825) - AddPrecastSpell(57724); // Sated - // Heroism - else if (m_spellInfo->Id == 32182) - AddPrecastSpell(57723); // Exhaustion - // Spirit Walk - else if (m_spellInfo->Id == 58875) - AddPrecastSpell(58876); - // Totem of Wrath - else if (spellEffect && spellEffect->Effect==SPELL_EFFECT_APPLY_AREA_AURA_RAID && classOpt && classOpt->SpellFamilyFlags & UI64LIT(0x0000000004000000)) - // only for main totem spell cast - AddTriggeredSpell(30708); // Totem of Wrath - break; - } - case SPELLFAMILY_DEATHKNIGHT: - { - // Chains of Ice - if (m_spellInfo->Id == 45524) - AddTriggeredSpell(55095); // Frost Fever - break; - } - default: - break; - } - - // traded items have trade slot instead of guid in m_itemTargetGUID - // set to real guid to be sent later to the client - m_targets.updateTradeSlotItem(); - - if (m_caster->GetTypeId() == TYPEID_PLAYER) - { - if (!m_IsTriggeredSpell && m_CastItem) - ((Player*)m_caster)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM, m_CastItem->GetEntry()); - - ((Player*)m_caster)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL, m_spellInfo->Id); - } - - FillTargetMap(); - - if (m_spellState == SPELL_STATE_FINISHED) // stop cast if spell marked as finish somewhere in FillTargetMap - { - m_caster->DecreaseCastCounter(); - SetExecutedCurrently(false); - return; - } - - // CAST SPELL - SendSpellCooldown(); - - TakePower(); - TakeReagents(); // we must remove reagents before HandleEffects to allow place crafted item in same slot - TakeAmmo(); - - SendCastResult(castResult); - SendSpellGo(); // we must send smsg_spell_go packet before m_castItem delete in TakeCastItem()... - - InitializeDamageMultipliers(); - - Unit* procTarget = m_targets.getUnitTarget(); - if (!procTarget) - procTarget = m_caster; - - // Okay, everything is prepared. Now we need to distinguish between immediate and evented delayed spells - float speed = m_spellInfo->speed == 0.0f && m_triggeredBySpellInfo ? m_triggeredBySpellInfo->speed : m_spellInfo->speed; - if (speed > 0.0f) - { - - // Remove used for cast item if need (it can be already NULL after TakeReagents call - // in case delayed spell remove item at cast delay start - TakeCastItem(); - - // fill initial spell damage from caster for delayed casted spells - for (TargetList::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - HandleDelayedSpellLaunch(&(*ihit)); - - // Okay, maps created, now prepare flags - m_immediateHandled = false; - m_spellState = SPELL_STATE_DELAYED; - SetDelayStart(0); - - // on spell cast end proc, - // critical hit related part is currently done on hit so proc there, - // 0 damage since any damage based procs should be on hit - // 0 victim proc since there is no victim proc dependent on successfull cast for caster - m_caster->ProcDamageAndSpell(procTarget, m_procAttacker, 0, PROC_EX_CAST_END, 0, m_attackType, m_spellInfo); - } - else - { - m_caster->ProcDamageAndSpell(procTarget, m_procAttacker, 0, PROC_EX_CAST_END, 0, m_attackType, m_spellInfo); - - // Immediate spell, no big deal - handle_immediate(); - } - - m_caster->DecreaseCastCounter(); - SetExecutedCurrently(false); -} - -void Spell::TakeAmmo() -{ - // take ammo - if (m_attackType == RANGED_ATTACK && m_caster->GetTypeId() == TYPEID_PLAYER) - { - Item* pItem = ((Player*)m_caster)->GetWeaponForAttack(RANGED_ATTACK, true, false); - - // wands don't have ammo - if (!pItem || pItem->GetProto()->SubClass == ITEM_SUBCLASS_WEAPON_WAND) - return; - - if (pItem->GetProto()->InventoryType == INVTYPE_THROWN) - { - if (pItem->GetMaxStackCount() == 1) - { - // decrease durability for non-stackable throw weapon - ((Player*)m_caster)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_RANGED); - } - else - { - // decrease items amount for stackable throw weapon - uint32 count = 1; - ((Player*)m_caster)->DestroyItemCount(pItem, count, true); - } - } - } -} - -void Spell::handle_immediate() -{ - // process immediate effects (items, ground, etc.) also initialize some variables - _handle_immediate_phase(); - - // start channeling if applicable (after _handle_immediate_phase for get persistent effect dynamic object for channel target - if (IsChanneledSpell(m_spellInfo) && m_duration) - { - m_spellState = SPELL_STATE_CASTING; - SendChannelStart(m_duration); - } - - for (TargetList::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - DoAllEffectOnTarget(&(*ihit)); - - for (GOTargetList::iterator ihit = m_UniqueGOTargetInfo.begin(); ihit != m_UniqueGOTargetInfo.end(); ++ihit) - DoAllEffectOnTarget(&(*ihit)); - - // spell is finished, perform some last features of the spell here - _handle_finish_phase(); - - // Remove used for cast item if need (it can be already NULL after TakeReagents call - TakeCastItem(); - - if (m_spellState != SPELL_STATE_CASTING) - finish(true); // successfully finish spell cast (not last in case autorepeat or channel spell) -} - -uint64 Spell::handle_delayed(uint64 t_offset) -{ - uint64 next_time = 0; - - if (!m_immediateHandled) - { - _handle_immediate_phase(); - m_immediateHandled = true; - } - - // now recheck units targeting correctness (need before any effects apply to prevent adding immunity at first effect not allow apply second spell effect and similar cases) - for (TargetList::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - { - if (!ihit->processed) - { - if (ihit->timeDelay <= t_offset) - DoAllEffectOnTarget(&(*ihit)); - else if (next_time == 0 || ihit->timeDelay < next_time) - next_time = ihit->timeDelay; - } - } - - // now recheck gameobject targeting correctness - for (GOTargetList::iterator ighit = m_UniqueGOTargetInfo.begin(); ighit != m_UniqueGOTargetInfo.end(); ++ighit) - { - if (!ighit->processed) - { - if (ighit->timeDelay <= t_offset) - DoAllEffectOnTarget(&(*ighit)); - else if (next_time == 0 || ighit->timeDelay < next_time) - next_time = ighit->timeDelay; - } - } - // All targets passed - need finish phase - if (next_time == 0) - { - // spell is finished, perform some last features of the spell here - _handle_finish_phase(); - - finish(true); // successfully finish spell cast - - // return zero, spell is finished now - return 0; - } - else - { - // spell is unfinished, return next execution time - return next_time; - } -} - -void Spell::_handle_immediate_phase() -{ - // handle some immediate features of the spell here - HandleThreatSpells(); - - m_needSpellLog = IsNeedSendToClient(); - for (int j = 0; j < MAX_EFFECT_INDEX; ++j) - { - SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(SpellEffectIndex(j)); - if(!spellEffect || spellEffect->Effect == 0) - continue; - - // apply Send Event effect to ground in case empty target lists - if( spellEffect->Effect == SPELL_EFFECT_SEND_EVENT && !HaveTargetsForEffect(SpellEffectIndex(j)) ) - { - HandleEffects(NULL, NULL, NULL, SpellEffectIndex(j)); - continue; - } - - // Don't do spell log, if is school damage spell - if(spellEffect->Effect == SPELL_EFFECT_SCHOOL_DAMAGE || spellEffect->Effect == 0) - m_needSpellLog = false; - } - - // initialize Diminishing Returns Data - m_diminishLevel = DIMINISHING_LEVEL_1; - m_diminishGroup = DIMINISHING_NONE; - - // process items - for (ItemTargetList::iterator ihit = m_UniqueItemInfo.begin(); ihit != m_UniqueItemInfo.end(); ++ihit) - DoAllEffectOnTarget(&(*ihit)); - - // process ground - for (int j = 0; j < MAX_EFFECT_INDEX; ++j) - { - SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(SpellEffectIndex(j)); - if(!spellEffect) - continue; - // persistent area auras target only the ground - if(spellEffect->Effect == SPELL_EFFECT_PERSISTENT_AREA_AURA) - HandleEffects(NULL, NULL, NULL, SpellEffectIndex(j)); - } -} - -void Spell::_handle_finish_phase() -{ - // spell log - if (m_needSpellLog) - SendLogExecute(); -} - -void Spell::SendSpellCooldown() -{ - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - Player* _player = (Player*)m_caster; - - // mana/health/etc potions, disabled by client (until combat out as declarate) - if (m_CastItem && m_CastItem->IsPotion()) - { - // need in some way provided data for Spell::finish SendCooldownEvent - _player->SetLastPotionId(m_CastItem->GetEntry()); - return; - } - - // (1) have infinity cooldown but set at aura apply, (2) passive cooldown at triggering - if (m_spellInfo->HasAttribute(SPELL_ATTR_DISABLED_WHILE_ACTIVE) || m_spellInfo->HasAttribute(SPELL_ATTR_PASSIVE)) - return; - - _player->AddSpellAndCategoryCooldowns(m_spellInfo, m_CastItem ? m_CastItem->GetEntry() : 0, this); -} - -void Spell::update(uint32 difftime) -{ - // update pointers based at it's GUIDs - UpdatePointers(); - - if (m_targets.getUnitTargetGuid() && !m_targets.getUnitTarget()) - { - cancel(); - return; - } - - SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(EFFECT_INDEX_0); - SpellInterruptsEntry const* spellInterrupts = m_spellInfo->GetSpellInterrupts(); - - // check if the player caster has moved before the spell finished - if ((m_caster->GetTypeId() == TYPEID_PLAYER && m_timer != 0) && - (m_castPositionX != m_caster->GetPositionX() || m_castPositionY != m_caster->GetPositionY() || m_castPositionZ != m_caster->GetPositionZ()) && - ((spellEffect && spellEffect->Effect != SPELL_EFFECT_STUCK) || !((Player*)m_caster)->m_movementInfo.HasMovementFlag(MOVEFLAG_FALLINGFAR)) && - !m_caster->HasAffectedAura(SPELL_AURA_ALLOW_CAST_WHILE_MOVING, m_spellInfo)) - { - // always cancel for channeled spells - if (m_spellState == SPELL_STATE_CASTING) - cancel(); - // don't cancel for melee, autorepeat, triggered and instant spells - else if(!IsNextMeleeSwingSpell() && !IsAutoRepeat() && !m_IsTriggeredSpell && (spellInterrupts && spellInterrupts->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT)) - cancel(); - } - - switch (m_spellState) - { - case SPELL_STATE_PREPARING: - { - if (m_timer) - { - if (difftime >= m_timer) - m_timer = 0; - else - m_timer -= difftime; - } - - if (m_timer == 0 && !IsNextMeleeSwingSpell() && !IsAutoRepeat()) - cast(); - } break; - case SPELL_STATE_CASTING: - { - if (m_timer > 0) - { - if (m_caster->GetTypeId() == TYPEID_PLAYER) - { - // check if player has jumped before the channeling finished - if (((Player*)m_caster)->m_movementInfo.HasMovementFlag(MOVEFLAG_FALLING) && - !m_caster->HasAffectedAura(SPELL_AURA_ALLOW_CAST_WHILE_MOVING, m_spellInfo)) - cancel(); - - // check for incapacitating player states - if (m_caster->hasUnitState(UNIT_STAT_CAN_NOT_REACT)) - cancel(); - - // check if player has turned if flag is set - if( spellInterrupts && (spellInterrupts->ChannelInterruptFlags & CHANNEL_FLAG_TURNING) && m_castOrientation != m_caster->GetOrientation() ) - cancel(); - } - - // check if there are alive targets left - if (!IsAliveUnitPresentInTargetList()) - { - SendChannelUpdate(0); - finish(); - } - - if (difftime >= m_timer) - m_timer = 0; - else - m_timer -= difftime; - } - - if (m_timer == 0) - { - SendChannelUpdate(0); - - // channeled spell processed independently for quest targeting - // cast at creature (or GO) quest objectives update at successful cast channel finished - // ignore autorepeat/melee casts for speed (not exist quest for spells (hm... ) - if (!IsAutoRepeat() && !IsNextMeleeSwingSpell()) - { - if (Player* p = m_caster->GetCharmerOrOwnerPlayerOrPlayerItself()) - { - for (TargetList::const_iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - { - TargetInfo const& target = *ihit; - if (!target.targetGUID.IsCreatureOrVehicle()) - continue; - - Unit* unit = m_caster->GetObjectGuid() == target.targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, target.targetGUID); - if (unit == NULL) - continue; - - p->RewardPlayerAndGroupAtCast(unit, m_spellInfo->Id); - } - - for (GOTargetList::const_iterator ihit = m_UniqueGOTargetInfo.begin(); ihit != m_UniqueGOTargetInfo.end(); ++ihit) - { - GOTargetInfo const& target = *ihit; - - GameObject* go = m_caster->GetMap()->GetGameObject(target.targetGUID); - if (!go) - continue; - - p->RewardPlayerAndGroupAtCast(go, m_spellInfo->Id); - } - } - } - - finish(); - } - } break; - default: - { - } break; - } -} - -void Spell::finish(bool ok) -{ - if (!m_caster) - return; - - if (m_spellState == SPELL_STATE_FINISHED) - return; - - m_spellState = SPELL_STATE_FINISHED; - - // other code related only to successfully finished spells - if (!ok) - return; - - // handle SPELL_AURA_ADD_TARGET_TRIGGER auras - Unit::AuraList const& targetTriggers = m_caster->GetAurasByType(SPELL_AURA_ADD_TARGET_TRIGGER); - for (Unit::AuraList::const_iterator i = targetTriggers.begin(); i != targetTriggers.end(); ++i) - { - if (!(*i)->isAffectedOnSpell(m_spellInfo)) - continue; - for (TargetList::const_iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - { - if (ihit->missCondition == SPELL_MISS_NONE) - { - // check m_caster->GetGUID() let load auras at login and speedup most often case - Unit* unit = m_caster->GetObjectGuid() == ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID); - if (unit && unit->isAlive()) - { - SpellEntry const* auraSpellInfo = (*i)->GetSpellProto(); - SpellEffectIndex auraSpellIdx = (*i)->GetEffIndex(); - // Calculate chance at that moment (can be depend for example from combo points) - int32 auraBasePoints = (*i)->GetBasePoints(); - int32 chance = m_caster->CalculateSpellDamage(unit, auraSpellInfo, auraSpellIdx, &auraBasePoints); - if(roll_chance_i(chance)) - if(SpellEffectEntry const* spellEffect = auraSpellInfo->GetSpellEffect(auraSpellIdx)) - m_caster->CastSpell(unit, spellEffect->EffectTriggerSpell, true, NULL, (*i)); - } - } - } - } - - // Heal caster for all health leech from all targets - if (m_healthLeech) - { - uint32 absorb = 0; - m_caster->CalculateHealAbsorb(uint32(m_healthLeech), &absorb); - m_caster->DealHeal(m_caster, uint32(m_healthLeech) - absorb, m_spellInfo, false, absorb); - } - - if (IsMeleeAttackResetSpell()) - { - m_caster->resetAttackTimer(BASE_ATTACK); - if (m_caster->haveOffhandWeapon()) - m_caster->resetAttackTimer(OFF_ATTACK); - } - - /*if (IsRangedAttackResetSpell()) - m_caster->resetAttackTimer(RANGED_ATTACK);*/ - - // Clear combo at finish state - if (m_caster->GetTypeId() == TYPEID_PLAYER && NeedsComboPoints(m_spellInfo)) - { - // Not drop combopoints if negative spell and if any miss on enemy exist - bool needDrop = true; - if (!IsPositiveSpell(m_spellInfo->Id)) - { - for (TargetList::const_iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - { - if (ihit->missCondition != SPELL_MISS_NONE && ihit->targetGUID != m_caster->GetObjectGuid()) - { - needDrop = false; - break; - } - } - } - if (needDrop) - ((Player*)m_caster)->ClearComboPoints(); - } - - // potions disabled by client, send event "not in combat" if need - if (m_caster->GetTypeId() == TYPEID_PLAYER) - ((Player*)m_caster)->UpdatePotionCooldown(this); - - // call triggered spell only at successful cast (after clear combo points -> for add some if need) - if (!m_TriggerSpells.empty()) - CastTriggerSpells(); - - // Stop Attack for some spells - if (m_spellInfo->HasAttribute(SPELL_ATTR_STOP_ATTACK_TARGET)) - m_caster->AttackStop(); - - // update encounter state if needed - Map* map = m_caster->GetMap(); - if (map->IsDungeon()) - ((DungeonMap*)map)->GetPersistanceState()->UpdateEncounterState(ENCOUNTER_CREDIT_CAST_SPELL, m_spellInfo->Id); -} - -void Spell::SendCastResult(SpellCastResult result) -{ - if (result == SPELL_CAST_OK) - return; - - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - if (((Player*)m_caster)->GetSession()->PlayerLoading()) // don't send cast results at loading time - return; - - SendCastResult((Player*)m_caster, m_spellInfo, m_cast_count, result); -} - -void Spell::SendCastResult(Player* caster, SpellEntry const* spellInfo, uint8 cast_count, SpellCastResult result, bool isPetCastResult /*=false*/) -{ - if (result == SPELL_CAST_OK) - return; - - WorldPacket data(isPetCastResult ? SMSG_PET_CAST_FAILED : SMSG_CAST_FAILED, (4 + 1 + 2)); - data << uint8(cast_count); // single cast or multi 2.3 (0/1) - data << uint32(spellInfo->Id); - data << uint8(!IsPassiveSpell(spellInfo) ? result : SPELL_FAILED_DONT_REPORT); // do not report failed passive spells - switch (result) - { - case SPELL_FAILED_NOT_READY: - data << uint32(0); // unknown, value 1 seen for 14177 (update cooldowns on client flag) - break; - case SPELL_FAILED_REQUIRES_SPELL_FOCUS: - data << uint32(spellInfo->GetRequiresSpellFocus()); - break; - case SPELL_FAILED_REQUIRES_AREA: // AreaTable.dbc id - // hardcode areas limitation case - switch (spellInfo->Id) - { - case 41617: // Cenarion Mana Salve - case 41619: // Cenarion Healing Salve - data << uint32(3905); - break; - case 41618: // Bottled Nethergon Energy - case 41620: // Bottled Nethergon Vapor - data << uint32(3842); - break; - case 45373: // Bloodberry Elixir - data << uint32(4075); - break; - default: // default case (don't must be) - data << uint32(0); - break; - } - break; - case SPELL_FAILED_TOTEMS: - { - SpellTotemsEntry const* totems = spellInfo->GetSpellTotems(); - for(int i = 0; i < MAX_SPELL_TOTEMS; ++i) - if(totems && totems->Totem[i]) - data << uint32(totems->Totem[i]); - } - break; - case SPELL_FAILED_TOTEM_CATEGORY: - { - SpellTotemsEntry const* totems = spellInfo->GetSpellTotems(); - for(int i = 0; i < MAX_SPELL_TOTEM_CATEGORIES; ++i) - if(totems && totems->TotemCategory[i]) - data << uint32(totems->TotemCategory[i]); - } - break; - case SPELL_FAILED_EQUIPPED_ITEM_CLASS: - { - SpellEquippedItemsEntry const* eqItems = spellInfo->GetSpellEquippedItems(); - data << uint32(eqItems ? eqItems->EquippedItemClass : -1); - data << uint32(eqItems ? eqItems->EquippedItemSubClassMask : 0); - //data << uint32(eqItems ? eqItems->EquippedItemInventoryTypeMask : 0); - } - break; - case SPELL_FAILED_EQUIPPED_ITEM_CLASS_MAINHAND: - case SPELL_FAILED_EQUIPPED_ITEM_CLASS_OFFHAND: - { - SpellEquippedItemsEntry const* eqItems = spellInfo->GetSpellEquippedItems(); - data << uint32(eqItems ? eqItems->EquippedItemClass : -1); - data << uint32(eqItems ? eqItems->EquippedItemSubClassMask : 0); - break; - } - case SPELL_FAILED_PREVENTED_BY_MECHANIC: - data << uint32(0); // SpellMechanic.dbc id - break; - case SPELL_FAILED_CUSTOM_ERROR: - data << uint32(0); // custom error id (see enum SpellCastResultCustom) - break; - case SPELL_FAILED_NEED_EXOTIC_AMMO: - { - SpellEquippedItemsEntry const* eqItems = spellInfo->GetSpellEquippedItems(); - data << uint32(eqItems ? eqItems->EquippedItemSubClassMask : 0);// seems correct... - break; - } - case SPELL_FAILED_REAGENTS: - data << uint32(0); // item id - break; - case SPELL_FAILED_NEED_MORE_ITEMS: - data << uint32(0); // item id - data << uint32(0); // item count? - break; - case SPELL_FAILED_MIN_SKILL: - data << uint32(0); // SkillLine.dbc id - data << uint32(0); // required skill value - break; - case SPELL_FAILED_TOO_MANY_OF_ITEM: - data << uint32(0); // ItemLimitCategory.dbc id - break; - case SPELL_FAILED_FISHING_TOO_LOW: - data << uint32(0); // required fishing skill - break; - default: - break; - } - caster->GetSession()->SendPacket(&data); -} - -void Spell::SendSpellStart() -{ - if (!IsNeedSendToClient()) - return; - - DEBUG_FILTER_LOG(LOG_FILTER_SPELL_CAST, "Sending SMSG_SPELL_START id=%u", m_spellInfo->Id); - - uint32 castFlags = CAST_FLAG_UNKNOWN2; - if (m_spellInfo->runeCostID) - castFlags |= CAST_FLAG_UNKNOWN19; - - if ((m_caster->GetTypeId() == TYPEID_PLAYER || - m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->IsPet()) && - m_spellInfo->powerType != POWER_HEALTH) - castFlags |= CAST_FLAG_PREDICTED_POWER; - - WorldPacket data(SMSG_SPELL_START, (8 + 8 + 4 + 4 + 2)); - if (m_CastItem) - data << m_CastItem->GetPackGUID(); - else - data << m_caster->GetPackGUID(); - - data << m_caster->GetPackGUID(); - data << uint8(m_cast_count); // pending spell cast - data << uint32(m_spellInfo->Id); // spellId - data << uint32(castFlags); // cast flags - data << uint32(m_timer); // delay? - data << uint32(m_casttime); // m_casttime - - data << m_targets; - - if (castFlags & CAST_FLAG_PREDICTED_POWER) // predicted power - data << uint32(m_caster->GetPower(Powers(m_spellInfo->powerType))); - - if (castFlags & CAST_FLAG_PREDICTED_RUNES) // predicted runes - { - if (m_caster->GetTypeId() == TYPEID_PLAYER) - { - Player* caster = (Player*)m_caster; - - data << uint8(m_runesState); - data << uint8(caster->GetRunesState()); - for (uint8 i = 0; i < MAX_RUNES; ++i) - data << uint8(255 - ((caster->GetRuneCooldown(i) / REGEN_TIME_FULL) * 51)); - } - else - { - data << uint8(0); - data << uint8(0); - for (uint8 i = 0; i < MAX_RUNES; ++i) - data << uint8(0); - } - } - - if (castFlags & CAST_FLAG_AMMO) // projectile info - WriteAmmoToPacket(&data); - - if (castFlags & CAST_FLAG_IMMUNITY) // cast immunity - { - data << uint32(0); // used for SetCastSchoolImmunities - data << uint32(0); // used for SetCastImmunities - } - - if (castFlags & CAST_FLAG_HEAL_PREDICTION) - { - uint8 unk = 0; - data << uint32(0); - data << uint8(unk); - if (unk == 2) - data << ObjectGuid().WriteAsPacked(); - } - - m_caster->SendMessageToSet(&data, true); -} - -void Spell::SendSpellGo() -{ - // not send invisible spell casting - if (!IsNeedSendToClient()) - return; - - DEBUG_FILTER_LOG(LOG_FILTER_SPELL_CAST, "Sending SMSG_SPELL_GO id=%u", m_spellInfo->Id); - - uint32 castFlags = CAST_FLAG_UNKNOWN9; - - if ((m_caster->GetTypeId() == TYPEID_PLAYER || - m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->IsPet()) && - m_spellInfo->powerType != POWER_HEALTH) - castFlags |= CAST_FLAG_PREDICTED_POWER; - - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->getClass() == CLASS_DEATH_KNIGHT && m_spellInfo->runeCostID) - { - castFlags |= CAST_FLAG_UNKNOWN19; // same as in SMSG_SPELL_START - castFlags |= CAST_FLAG_PREDICTED_RUNES; // rune cooldowns list - } - - WorldPacket data(SMSG_SPELL_GO, 50); // guess size - - if (m_CastItem) - data << m_CastItem->GetPackGUID(); - else - data << m_caster->GetPackGUID(); - - data << m_caster->GetPackGUID(); - data << uint8(m_cast_count); // pending spell cast? - data << uint32(m_spellInfo->Id); // spellId - data << uint32(castFlags); // cast flags - data << uint32(m_timer); - data << uint32(WorldTimer::getMSTime()); // timestamp - - WriteSpellGoTargets(&data); - - data << m_targets; - - if (castFlags & CAST_FLAG_PREDICTED_POWER) // predicted power - data << uint32(m_caster->GetPower(Powers(m_spellInfo->powerType))); - - if (castFlags & CAST_FLAG_PREDICTED_RUNES) // predicted runes - { - if (m_caster->GetTypeId() == TYPEID_PLAYER) - { - Player* caster = (Player*)m_caster; - - data << uint8(m_runesState); - data << uint8(caster->GetRunesState()); - for (uint8 i = 0; i < MAX_RUNES; ++i) - data << uint8(caster->GetRuneCooldownFraction(i)); - } - else - { - data << uint8(0); - data << uint8(0); - for (uint8 i = 0; i < MAX_RUNES; ++i) - data << uint8(0); - } - } - - if (castFlags & CAST_FLAG_ADJUST_MISSILE) // adjust missile trajectory duration - { - data << float(m_targets.GetElevation()); - data << uint32(m_delayMoment); - } - - if (castFlags & CAST_FLAG_AMMO) // projectile info - WriteAmmoToPacket(&data); - - if (castFlags & CAST_FLAG_VISUAL_CHAIN) // spell visual chain effect - { - data << uint32(0); // SpellVisual.dbc id? - data << uint32(0); // overrides previous field if > 0 and violencelevel client cvar < 2 - } - - if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) - { - data << uint8(0); // The value increase for each time, can remind of a cast count for the spell - } - - if (m_targets.m_targetMask & TARGET_FLAG_VISUAL_CHAIN) // probably used (or can be used) with CAST_FLAG_VISUAL_CHAIN flag - { - data << uint32(0); // count - - // for(int = 0; i < count; ++i) - //{ - // // position and guid? - // data << float(0) << float(0) << float(0) << uint64(0); - //} - } - - m_caster->SendMessageToSet(&data, true); -} - -void Spell::WriteAmmoToPacket(WorldPacket* data) -{ - uint32 ammoInventoryType = 0; - uint32 ammoDisplayID = 0; - - if (m_caster->GetTypeId() == TYPEID_PLAYER) - { - Item* pItem = ((Player*)m_caster)->GetWeaponForAttack(RANGED_ATTACK); - if (pItem) - { - ammoInventoryType = pItem->GetProto()->InventoryType; - if (ammoInventoryType == INVTYPE_THROWN) - ammoDisplayID = pItem->GetProto()->DisplayInfoID; - else - { - if(m_caster->GetDummyAura(46699)) // Requires No Ammo - { - ammoDisplayID = 5996; // normal arrow - ammoInventoryType = INVTYPE_AMMO; - } - } - } - } - else - { - for (uint8 i = 0; i < MAX_VIRTUAL_ITEM_SLOT; ++i) - { - if (uint32 item_id = m_caster->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + i)) - { - if(ItemPrototype const* itemEntry = sItemStorage.LookupEntry(item_id)) - //if(ItemEntry const * itemEntry = sItemStore.LookupEntry(item_id)) - { - if (itemEntry->Class == ITEM_CLASS_WEAPON) - { - switch (itemEntry->SubClass) - { - case ITEM_SUBCLASS_WEAPON_THROWN: - ammoDisplayID = itemEntry->DisplayInfoID; - ammoInventoryType = itemEntry->InventoryType; - break; - case ITEM_SUBCLASS_WEAPON_BOW: - case ITEM_SUBCLASS_WEAPON_CROSSBOW: - ammoDisplayID = 5996; // is this need fixing? - ammoInventoryType = INVTYPE_AMMO; - break; - case ITEM_SUBCLASS_WEAPON_GUN: - ammoDisplayID = 5998; // is this need fixing? - ammoInventoryType = INVTYPE_AMMO; - break; - } - - if (ammoDisplayID) - break; - } - } - } - } - } - - *data << uint32(ammoDisplayID); - *data << uint32(ammoInventoryType); -} - -void Spell::WriteSpellGoTargets(WorldPacket* data) -{ - size_t count_pos = data->wpos(); - *data << uint8(0); // placeholder - - // This function also fill data for channeled spells: - // m_needAliveTargetMask req for stop channeling if one target die - uint32 hit = m_UniqueGOTargetInfo.size(); // Always hits on GO - uint32 miss = 0; - - for (TargetList::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - { - if (ihit->effectMask == 0) // No effect apply - all immuned add state - { - // possibly SPELL_MISS_IMMUNE2 for this?? - ihit->missCondition = SPELL_MISS_IMMUNE2; - ++miss; - } - else if (ihit->missCondition == SPELL_MISS_NONE) // Add only hits - { - ++hit; - *data << ihit->targetGUID; - m_needAliveTargetMask |= ihit->effectMask; - } - else - ++miss; - } - - for (GOTargetList::const_iterator ighit = m_UniqueGOTargetInfo.begin(); ighit != m_UniqueGOTargetInfo.end(); ++ighit) - *data << ighit->targetGUID; // Always hits - - data->put(count_pos, hit); - - *data << (uint8)miss; - for (TargetList::const_iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - { - if (ihit->missCondition != SPELL_MISS_NONE) // Add only miss - { - *data << ihit->targetGUID; - *data << uint8(ihit->missCondition); - if (ihit->missCondition == SPELL_MISS_REFLECT) - *data << uint8(ihit->reflectResult); - } - } - // Reset m_needAliveTargetMask for non channeled spell - if (!IsChanneledSpell(m_spellInfo)) - m_needAliveTargetMask = 0; -} - -void Spell::SendLogExecute() -{ - Unit* target = m_targets.getUnitTarget() ? m_targets.getUnitTarget() : m_caster; - - WorldPacket data(SMSG_SPELLLOGEXECUTE, (8 + 4 + 4 + 4 + 4 + 8)); - - if (m_caster->GetTypeId() == TYPEID_PLAYER) - data << m_caster->GetPackGUID(); - else - data << target->GetPackGUID(); - - data << uint32(m_spellInfo->Id); - uint32 count1 = 1; - data << uint32(count1); // count1 (effect count?) - for (uint32 i = 0; i < count1; ++i) - { - SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(EFFECT_INDEX_0); - data << uint32(spellEffect ? spellEffect->Effect : 0);// spell effect - uint32 count2 = 1; - data << uint32(count2); // count2 (target count?) - for (uint32 j = 0; j < count2; ++j) - { - if(!spellEffect) - continue; - - switch(spellEffect->Effect) - { - case SPELL_EFFECT_POWER_DRAIN: - case SPELL_EFFECT_POWER_BURN: - if (Unit* unit = m_targets.getUnitTarget()) - data << unit->GetPackGUID(); - else - data << uint8(0); - data << uint32(0); - data << uint32(0); - data << float(0); - break; - case SPELL_EFFECT_ADD_EXTRA_ATTACKS: - if (Unit* unit = m_targets.getUnitTarget()) - data << unit->GetPackGUID(); - else - data << uint8(0); - data << uint32(0); // count? - break; - case SPELL_EFFECT_INTERRUPT_CAST: - if (Unit* unit = m_targets.getUnitTarget()) - data << unit->GetPackGUID(); - else - data << uint8(0); - data << uint32(0); // spellid - break; - case SPELL_EFFECT_DURABILITY_DAMAGE: - if (Unit* unit = m_targets.getUnitTarget()) - data << unit->GetPackGUID(); - else - data << uint8(0); - data << uint32(0); - data << uint32(0); - break; - case SPELL_EFFECT_OPEN_LOCK: - if (Item* item = m_targets.getItemTarget()) - data << item->GetPackGUID(); - else - data << uint8(0); - break; - case SPELL_EFFECT_CREATE_ITEM: - case SPELL_EFFECT_CREATE_RANDOM_ITEM: - case SPELL_EFFECT_CREATE_ITEM_2: - data << uint32(spellEffect->EffectItemType); - break; - case SPELL_EFFECT_SUMMON: - case SPELL_EFFECT_TRANS_DOOR: - case SPELL_EFFECT_SUMMON_PET: - case SPELL_EFFECT_SUMMON_OBJECT_WILD: - case SPELL_EFFECT_CREATE_HOUSE: - case SPELL_EFFECT_DUEL: - case SPELL_EFFECT_SUMMON_OBJECT_SLOT: - case SPELL_EFFECT_171: - if (Unit* unit = m_targets.getUnitTarget()) - data << unit->GetPackGUID(); - else if (m_targets.getItemTargetGuid()) - data << m_targets.getItemTargetGuid().WriteAsPacked(); - else if (GameObject* go = m_targets.getGOTarget()) - data << go->GetPackGUID(); - else - data << uint8(0); // guid - break; - case SPELL_EFFECT_FEED_PET: - data << uint32(m_targets.getItemTargetEntry()); - break; - case SPELL_EFFECT_DISMISS_PET: - if (Unit* unit = m_targets.getUnitTarget()) - data << unit->GetPackGUID(); - else - data << uint8(0); - break; - case SPELL_EFFECT_RESURRECT: - case SPELL_EFFECT_RESURRECT_NEW: - case SPELL_EFFECT_MASS_RESSURECTION: - if (Unit* unit = m_targets.getUnitTarget()) - data << unit->GetPackGUID(); - else - data << uint8(0); - break; - default: - return; - } - } - } - - m_caster->SendMessageToSet(&data, true); -} - -void Spell::SendInterrupted(uint8 result) -{ - WorldPacket data(SMSG_SPELL_FAILURE, (8 + 4 + 1)); - data << m_caster->GetPackGUID(); - data << uint8(m_cast_count); - data << uint32(m_spellInfo->Id); - data << uint8(result); - m_caster->SendMessageToSet(&data, true); - - data.Initialize(SMSG_SPELL_FAILED_OTHER, (8 + 4)); - data << m_caster->GetPackGUID(); - data << uint8(m_cast_count); - data << uint32(m_spellInfo->Id); - data << uint8(result); - m_caster->SendMessageToSet(&data, true); -} - -void Spell::SendChannelUpdate(uint32 time) -{ - if (time == 0) - { - // Reset farsight for some possessing auras of possessed summoned (as they might work with different aura types) - if (m_spellInfo->HasAttribute(SPELL_ATTR_EX_FARSIGHT) && m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->GetCharmGuid() - && !IsSpellHaveAura(m_spellInfo, SPELL_AURA_MOD_POSSESS) && !IsSpellHaveAura(m_spellInfo, SPELL_AURA_MOD_POSSESS_PET)) - { - Player* player = (Player*)m_caster; - // These Auras are applied to self, so get the possessed first - Unit* possessed = player->GetCharm(); - - player->SetCharm(NULL); - if (possessed) - player->SetClientControl(possessed, 0); - player->SetMover(NULL); - player->GetCamera().ResetView(); - player->RemovePetActionBar(); - - if (possessed) - { - possessed->clearUnitState(UNIT_STAT_CONTROLLED); - possessed->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); - possessed->SetCharmerGuid(ObjectGuid()); - // TODO - Requires more specials for target? - - // Some possessed might want to despawn? - if (possessed->GetUInt32Value(UNIT_CREATED_BY_SPELL) == m_spellInfo->Id && possessed->GetTypeId() == TYPEID_UNIT) - ((Creature*)possessed)->ForcedDespawn(); - } - } - - m_caster->RemoveAurasByCasterSpell(m_spellInfo->Id, m_caster->GetObjectGuid()); - - ObjectGuid target_guid = m_caster->GetChannelObjectGuid(); - if (target_guid != m_caster->GetObjectGuid() && target_guid.IsUnit()) - if (Unit* target = ObjectAccessor::GetUnit(*m_caster, target_guid)) - target->RemoveAurasByCasterSpell(m_spellInfo->Id, m_caster->GetObjectGuid()); - - // Only finish channeling when latest channeled spell finishes - if (m_caster->GetUInt32Value(UNIT_CHANNEL_SPELL) != m_spellInfo->Id) - return; - - m_caster->SetChannelObjectGuid(ObjectGuid()); - m_caster->SetUInt32Value(UNIT_CHANNEL_SPELL, 0); - } - - WorldPacket data(SMSG_CHANNEL_UPDATE, 8 + 4); - data << m_caster->GetPackGUID(); - data << uint32(time); - m_caster->SendMessageToSet(&data, true); -} - -void Spell::SendChannelStart(uint32 duration) -{ - WorldObject* target = NULL; - - // select dynobject created by first effect if any - if (m_spellInfo->GetSpellEffectIdByIndex(EFFECT_INDEX_0) == SPELL_EFFECT_PERSISTENT_AREA_AURA) - target = m_caster->GetDynObject(m_spellInfo->Id, EFFECT_INDEX_0); - // select first not resisted target from target list for _0_ effect - else if (!m_UniqueTargetInfo.empty()) - { - for (TargetList::const_iterator itr = m_UniqueTargetInfo.begin(); itr != m_UniqueTargetInfo.end(); ++itr) - { - if ((itr->effectMask & (1 << EFFECT_INDEX_0)) && itr->reflectResult == SPELL_MISS_NONE && - itr->targetGUID != m_caster->GetObjectGuid()) - { - target = ObjectAccessor::GetUnit(*m_caster, itr->targetGUID); - break; - } - } - } - else if (!m_UniqueGOTargetInfo.empty()) - { - for (GOTargetList::const_iterator itr = m_UniqueGOTargetInfo.begin(); itr != m_UniqueGOTargetInfo.end(); ++itr) - { - if (itr->effectMask & (1 << EFFECT_INDEX_0)) - { - target = m_caster->GetMap()->GetGameObject(itr->targetGUID); - break; - } - } - } - - WorldPacket data(SMSG_CHANNEL_START, (8 + 4 + 4)); - data << m_caster->GetPackGUID(); - data << uint32(m_spellInfo->Id); - data << uint32(duration); - data << uint8(0); // unk1 - //if (unk1) - //{ - // data << uint32(0); - // data << uint32(0); - //} - data << uint8(0); // unk2 - //if (unk2) - //{ - // data << ObjectGuid().WriteAsPacked(); - // data << uint32(0); - // data << uint8(0); // unk3 - // if (unk3 == 2) - // data << ObjectGuid().WriteAsPacked(); - //} - - m_caster->SendMessageToSet(&data, true); - - m_timer = duration; - - if (target) - m_caster->SetChannelObjectGuid(target->GetObjectGuid()); - - m_caster->SetUInt32Value(UNIT_CHANNEL_SPELL, m_spellInfo->Id); -} - -void Spell::SendResurrectRequest(Player* target) -{ - // Both players and NPCs can resurrect using spells - have a look at creature 28487 for example - // However, the packet structure differs slightly - - const char* sentName = m_caster->GetTypeId() == TYPEID_PLAYER ? "" : m_caster->GetNameForLocaleIdx(target->GetSession()->GetSessionDbLocaleIndex()); - - WorldPacket data(SMSG_RESURRECT_REQUEST, (8 + 4 + strlen(sentName) + 1 + 1 + 1)); - data << m_caster->GetObjectGuid(); - data << uint32(strlen(sentName) + 1); - - data << sentName; - data << uint8(0); - - data << uint8(m_caster->GetTypeId() == TYPEID_PLAYER ? 0 : 1); - data << uint32(m_spellInfo->Id); - - target->GetSession()->SendPacket(&data); -} - -void Spell::SendPlaySpellVisual(uint32 SpellID) -{ - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - WorldPacket data; - m_caster->BuildSendPlayVisualPacket(&data, SpellID, false); - - ((Player*)m_caster)->GetSession()->SendPacket(&data); -} - -void Spell::TakeCastItem() -{ - if (!m_CastItem || m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - // not remove cast item at triggered spell (equipping, weapon damage, etc) - if (m_IsTriggeredSpell && !(m_targets.m_targetMask & TARGET_FLAG_TRADE_ITEM)) - return; - - ItemPrototype const* proto = m_CastItem->GetProto(); - - if (!proto) - { - // This code is to avoid a crash - // I'm not sure, if this is really an error, but I guess every item needs a prototype - sLog.outError("Cast item (%s) has no item prototype", m_CastItem->GetGuidStr().c_str()); - return; - } - - bool expendable = false; - bool withoutCharges = false; - - for (int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) - { - if (proto->Spells[i].SpellId) - { - // item has limited charges - if (proto->Spells[i].SpellCharges) - { - if (proto->Spells[i].SpellCharges < 0 && !(proto->ExtraFlags & ITEM_EXTRA_NON_CONSUMABLE)) - expendable = true; - - int32 charges = m_CastItem->GetSpellCharges(i); - - // item has charges left - if (charges) - { - (charges > 0) ? --charges : ++charges; // abs(charges) less at 1 after use - if (proto->Stackable == 1) - m_CastItem->SetSpellCharges(i, charges); - m_CastItem->SetState(ITEM_CHANGED, (Player*)m_caster); - } - - // all charges used - withoutCharges = (charges == 0); - } - } - } - - if (expendable && withoutCharges) - { - uint32 count = 1; - ((Player*)m_caster)->DestroyItemCount(m_CastItem, count, true); - - // prevent crash at access to deleted m_targets.getItemTarget - ClearCastItem(); - } -} - -void Spell::TakePower() -{ - if (m_CastItem || m_triggeredByAuraSpell) - return; - - bool hit = true; - if (m_caster->GetTypeId() == TYPEID_PLAYER) - { - if (m_spellInfo->powerType == POWER_RAGE || m_spellInfo->powerType == POWER_ENERGY || m_spellInfo->powerType == POWER_HOLY_POWER) - { - ObjectGuid targetGuid = m_targets.getUnitTargetGuid(); - if (!targetGuid.IsEmpty()) - for (TargetList::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - if (ihit->targetGUID == targetGuid) - { - if (ihit->missCondition != SPELL_MISS_NONE && ihit->missCondition != SPELL_MISS_MISS) - hit = false; - if (ihit->missCondition != SPELL_MISS_NONE) - { - // lower spell cost on fail (by talent aura) - if (Player* modOwner = ((Player*)m_caster)->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_SPELL_COST_REFUND_ON_FAIL, m_powerCost); - } - break; - } - } - } - - // health as power used - if (m_spellInfo->powerType == POWER_HEALTH) - { - m_caster->ModifyHealth(-(int32)m_powerCost); - return; - } - - if (m_spellInfo->powerType >= MAX_POWERS) - { - sLog.outError("Spell::TakePower: Unknown power type '%d'", m_spellInfo->powerType); - return; - } - - Powers powerType = Powers(m_spellInfo->powerType); - - if (powerType == POWER_HOLY_POWER) - { - m_usedHolyPower = m_powerCost; - - // spells consume all holy power when successfully hit - if (hit) - { - // Divine Purpose - if (m_caster->HasAura(90174)) - { - m_usedHolyPower = m_caster->GetMaxPower(POWER_HOLY_POWER); - return; - } - else - m_usedHolyPower = m_caster->GetPower(POWER_HOLY_POWER); - } - - // Zealotry - does not take power - if (m_spellInfo->Id == 85696) - return; - - m_caster->ModifyPower(powerType, -(int32)m_usedHolyPower); - return; - } - - if (powerType == POWER_RUNE) - { - TakeRunePower(hit); - return; - } - - m_caster->ModifyPower(powerType, -(int32)m_powerCost); -} - -SpellCastResult Spell::CheckRunePower() -{ - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return SPELL_CAST_OK; - - Player* plr = (Player*)m_caster; - - if (plr->getClass() != CLASS_DEATH_KNIGHT) - return SPELL_CAST_OK; - - SpellRuneCostEntry const* src = sSpellRuneCostStore.LookupEntry(m_spellInfo->runeCostID); - if (!src || (src->NoRuneCost())) - return SPELL_CAST_OK; - - int32 runeCost[NUM_RUNE_TYPES]; // blood, frost, unholy, death - for (uint8 i = 0; i < RUNE_DEATH; ++i) - { - runeCost[i] = src->RuneCost[i]; - if (Player* modOwner = plr->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COST, runeCost[i], this); - } - - - runeCost[RUNE_DEATH] = MAX_RUNES; // calculated later - - // scan non-death runes (death rune not used explicitly in rune costs) - for (uint8 i = 0; i < MAX_RUNES; ++i) - { - RuneType rune = plr->GetCurrentRune(i); - if (!plr->GetRuneCooldown(i) && runeCost[rune] > 0) - --runeCost[rune]; - } - - // collect all not counted rune costs to death runes cost - for (uint8 i = 0; i < RUNE_DEATH; ++i) - if (runeCost[i] > 0) - runeCost[RUNE_DEATH] += runeCost[i]; - - // scan death runes - if (runeCost[RUNE_DEATH] > MAX_RUNES) - return SPELL_FAILED_NO_POWER; - - return SPELL_CAST_OK; -} - - -void Spell::TakeRunePower(bool hit) -{ - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - Player* plr = (Player*)m_caster; - - if (plr->getClass() != CLASS_DEATH_KNIGHT) - return; - - SpellRuneCostEntry const* src = sSpellRuneCostStore.LookupEntry(m_spellInfo->runeCostID); - - if (!src) - return; - - if (src->NoRuneCost() && src->NoRunicPowerGain()) - return; - - m_runesState = plr->GetRunesState(); // store previous state - - // at this moment for rune cost exist only no cost mods, and no percent mods - int32 runeCost[NUM_RUNE_TYPES]; // blood, frost, unholy, death - for (uint32 i = 0; i < RUNE_DEATH; ++i) - { - runeCost[i] = src->RuneCost[i]; - if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COST, runeCost[i], this); - } - - runeCost[RUNE_DEATH] = 0; // calculated later - - plr->ClearLastUsedRuneMask(); - - for (uint32 i = 0; i < MAX_RUNES; ++i) - { - RuneType rune = plr->GetCurrentRune(i); - if (!plr->GetRuneCooldown(i) && runeCost[rune] > 0) - { - uint16 baseCd = hit ? uint16(RUNE_BASE_COOLDOWN) : uint16(RUNE_MISS_COOLDOWN); - plr->SetBaseRuneCooldown(i, baseCd); - plr->SetRuneCooldown(i, baseCd); - plr->SetLastUsedRune(rune); - --runeCost[rune]; - } - } - - runeCost[RUNE_DEATH] = runeCost[RUNE_BLOOD] + runeCost[RUNE_UNHOLY] + runeCost[RUNE_FROST]; - - if (runeCost[RUNE_DEATH] > 0) - { - for (uint32 i = 0; i < MAX_RUNES; ++i) - { - RuneType rune = plr->GetCurrentRune(i); - if (!plr->GetRuneCooldown(i) && rune == RUNE_DEATH) - { - uint16 baseCd = hit ? uint16(RUNE_BASE_COOLDOWN) : uint16(RUNE_MISS_COOLDOWN); - plr->SetBaseRuneCooldown(i, baseCd); - plr->SetRuneCooldown(i, baseCd); - plr->SetLastUsedRune(rune); - --runeCost[rune]; - - // keep Death Rune type if missed - if (hit) - plr->RestoreBaseRune(i); - - if (runeCost[RUNE_DEATH] == 0) - break; - } - } - } - - if (hit) - { - // you can gain some runic power when use runes - int32 rp = int32(src->runePowerGain); - if (rp) - { - if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COST, rp, this); - - rp = int32(sWorld.getConfig(CONFIG_FLOAT_RATE_POWER_RUNICPOWER_INCOME) * rp); - rp += int32(m_caster->GetTotalAuraModifier(SPELL_AURA_MOD_RUNIC_POWER_REGEN) * rp / 100); - if (rp > 0) - plr->ModifyPower(POWER_RUNIC_POWER, rp); - } - } -} - -void Spell::TakeReagents() -{ - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - if (IgnoreItemRequirements()) // reagents used in triggered spell removed by original spell or don't must be removed. - return; - - Player* p_caster = (Player*)m_caster; - if (p_caster->CanNoReagentCast(m_spellInfo)) - return; - - SpellReagentsEntry const* spellReagents = m_spellInfo->GetSpellReagents(); - - for(uint32 x = 0; x < MAX_SPELL_REAGENTS; ++x) - { - if(!spellReagents) - continue; - if(spellReagents->Reagent[x] <= 0) - continue; - - uint32 itemid = spellReagents->Reagent[x]; - uint32 itemcount = spellReagents->ReagentCount[x]; - - // if CastItem is also spell reagent - if (m_CastItem) - { - ItemPrototype const* proto = m_CastItem->GetProto(); - if (proto && proto->ItemId == itemid) - { - for (int s = 0; s < MAX_ITEM_PROTO_SPELLS; ++s) - { - // CastItem will be used up and does not count as reagent - int32 charges = m_CastItem->GetSpellCharges(s); - if (proto->Spells[s].SpellCharges < 0 && abs(charges) < 2) - { - ++itemcount; - break; - } - } - - m_CastItem = NULL; - } - } - - // if getItemTarget is also spell reagent - if (m_targets.getItemTargetEntry() == itemid) - m_targets.setItemTarget(NULL); - - p_caster->DestroyItemCount(itemid, itemcount, true); - } -} - -void Spell::HandleThreatSpells() -{ - if (m_UniqueTargetInfo.empty()) - return; - - SpellThreatEntry const* threatEntry = sSpellMgr.GetSpellThreatEntry(m_spellInfo->Id); - - if (!threatEntry || (!threatEntry->threat && threatEntry->ap_bonus == 0.0f)) - return; - - float threat = threatEntry->threat; - if (threatEntry->ap_bonus != 0.0f) - threat += threatEntry->ap_bonus * m_caster->GetTotalAttackPowerValue(GetWeaponAttackType(m_spellInfo)); - - bool positive = true; - uint8 effectMask = 0; - for (int i = 0; i < MAX_EFFECT_INDEX; ++i) - if (SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(SpellEffectIndex(i))) - if (spellEffect->Effect) - effectMask |= (1<Id, sSpellMgr.GetSpellRank(m_spellInfo->Id)); - return; - } - positive = false; - } - - // since 2.0.1 threat from positive effects also is distributed among all targets, so the overall caused threat is at most the defined bonus - threat /= m_UniqueTargetInfo.size(); - - for (TargetList::const_iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - { - if (ihit->missCondition != SPELL_MISS_NONE) - continue; - - Unit* target = m_caster->GetObjectGuid() == ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID); - if (!target) - continue; - - // positive spells distribute threat among all units that are in combat with target, like healing - if (positive) - { - target->getHostileRefManager().threatAssist(m_caster /*real_caster ??*/, threat, m_spellInfo); - } - // for negative spells threat gets distributed among affected targets - else - { - if (!target->CanHaveThreatList()) - continue; - - target->AddThreat(m_caster, threat, false, GetSpellSchoolMask(m_spellInfo), m_spellInfo); - } - } - - DEBUG_FILTER_LOG(LOG_FILTER_SPELL_CAST, "Spell %u added an additional %f threat for %s " SIZEFMTD " target(s)", m_spellInfo->Id, threat, positive ? "assisting" : "harming", m_UniqueTargetInfo.size()); -} - -void Spell::HandleEffects(Unit* pUnitTarget, Item* pItemTarget, GameObject* pGOTarget, SpellEffectIndex i, float DamageMultiplier) -{ - unitTarget = pUnitTarget; - itemTarget = pItemTarget; - gameObjTarget = pGOTarget; - - SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(SpellEffectIndex(i)); - - damage = int32(CalculateDamage(i, unitTarget) * DamageMultiplier); - - if(spellEffect) - { - if(spellEffect->Effect < TOTAL_SPELL_EFFECTS) - { - DEBUG_FILTER_LOG(LOG_FILTER_SPELL_CAST, "Spell %u Effect%d : %u Targets: %s, %s, %s", - m_spellInfo->Id, i, spellEffect->Effect, - unitTarget ? unitTarget->GetGuidStr().c_str() : "-", - itemTarget ? itemTarget->GetGuidStr().c_str() : "-", - gameObjTarget ? gameObjTarget->GetGuidStr().c_str() : "-"); - - (*this.*SpellEffects[spellEffect->Effect])(spellEffect); - } - else - { - sLog.outError("WORLD: Spell %u Effect%d : %u > TOTAL_SPELL_EFFECTS", m_spellInfo->Id, i, spellEffect->Effect); - } - } - else - { - sLog.outError("WORLD: Spell %u has no effect at index %u", m_spellInfo->Id, i); - } -} - -void Spell::AddTriggeredSpell(uint32 spellId) -{ - SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellId); - - if (!spellInfo) - { - sLog.outError("Spell::AddTriggeredSpell: unknown spell id %u used as triggred spell for spell %u)", spellId, m_spellInfo->Id); - return; - } - - m_TriggerSpells.push_back(spellInfo); -} - -void Spell::AddPrecastSpell(uint32 spellId) -{ - SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellId); - - if (!spellInfo) - { - sLog.outError("Spell::AddPrecastSpell: unknown spell id %u used as pre-cast spell for spell %u)", spellId, m_spellInfo->Id); - return; - } - - m_preCastSpells.push_back(spellInfo); -} - -void Spell::CastTriggerSpells() -{ - for (SpellInfoList::const_iterator si = m_TriggerSpells.begin(); si != m_TriggerSpells.end(); ++si) - { - Spell* spell = new Spell(m_caster, (*si), true, m_originalCasterGUID); - spell->prepare(&m_targets); // use original spell original targets - } -} - -void Spell::CastPreCastSpells(Unit* target) -{ - for (SpellInfoList::const_iterator si = m_preCastSpells.begin(); si != m_preCastSpells.end(); ++si) - m_caster->CastSpell(target, (*si), true, m_CastItem); -} - -Unit* Spell::GetPrefilledUnitTargetOrUnitTarget(SpellEffectIndex effIndex) const -{ - for (TargetList::const_iterator itr = m_UniqueTargetInfo.begin(); itr != m_UniqueTargetInfo.end(); ++itr) - if (itr->effectMask & (1 << effIndex)) - return m_caster->GetMap()->GetUnit(itr->targetGUID); - - return m_targets.getUnitTarget(); -} - -SpellCastResult Spell::CheckCast(bool strict) -{ - // check cooldowns to prevent cheating (ignore passive spells, that client side visual only) - if (m_caster->GetTypeId() == TYPEID_PLAYER && !m_spellInfo->HasAttribute(SPELL_ATTR_PASSIVE) && - ((Player*)m_caster)->HasSpellCooldown(m_spellInfo->Id)) - { - if (m_triggeredByAuraSpell) - return SPELL_FAILED_DONT_REPORT; - else - return SPELL_FAILED_NOT_READY; - } - - // check global cooldown - if (strict && !m_IsTriggeredSpell && HasGlobalCooldown()) - return SPELL_FAILED_NOT_READY; - - // only allow triggered spells if at an ended battleground - if (!m_IsTriggeredSpell && m_caster->GetTypeId() == TYPEID_PLAYER) - if (BattleGround* bg = ((Player*)m_caster)->GetBattleGround()) - if (bg->GetStatus() == STATUS_WAIT_LEAVE) - return SPELL_FAILED_DONT_REPORT; - - if (!m_IsTriggeredSpell && IsNonCombatSpell(m_spellInfo) && - m_caster->isInCombat() && !m_caster->IsIgnoreUnitState(m_spellInfo, IGNORE_UNIT_COMBAT_STATE)) - return SPELL_FAILED_AFFECTING_COMBAT; - - if (m_caster->GetTypeId() == TYPEID_PLAYER && !((Player*)m_caster)->isGameMaster() && - sWorld.getConfig(CONFIG_BOOL_VMAP_INDOOR_CHECK) && - VMAP::VMapFactory::createOrGetVMapManager()->isLineOfSightCalcEnabled()) - { - if (m_spellInfo->HasAttribute(SPELL_ATTR_OUTDOORS_ONLY) && - !m_caster->GetTerrain()->IsOutdoors(m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ())) - return SPELL_FAILED_ONLY_OUTDOORS; - - if (m_spellInfo->HasAttribute(SPELL_ATTR_INDOORS_ONLY) && - m_caster->GetTerrain()->IsOutdoors(m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ())) - return SPELL_FAILED_ONLY_INDOORS; - } - // only check at first call, Stealth auras are already removed at second call - // for now, ignore triggered spells - if (strict && !m_IsTriggeredSpell) - { - // Ignore form req aura - if (!m_caster->HasAffectedAura(SPELL_AURA_MOD_IGNORE_SHAPESHIFT, m_spellInfo)) - { - // Cannot be used in this stance/form - SpellCastResult shapeError = GetErrorAtShapeshiftedCast(m_spellInfo, m_caster->GetShapeshiftForm()); - if (shapeError != SPELL_CAST_OK) - return shapeError; - - if (m_spellInfo->HasAttribute(SPELL_ATTR_ONLY_STEALTHED) && !(m_caster->HasStealthAura())) - return SPELL_FAILED_ONLY_STEALTHED; - } - } - - SpellAuraRestrictionsEntry const* auraRestrictions = m_spellInfo->GetSpellAuraRestrictions(); - - // caster state requirements - if(auraRestrictions && auraRestrictions->CasterAuraState && !m_caster->HasAuraState(AuraState(auraRestrictions->CasterAuraState))) - return SPELL_FAILED_CASTER_AURASTATE; - if(auraRestrictions && auraRestrictions->CasterAuraStateNot && m_caster->HasAuraState(AuraState(auraRestrictions->CasterAuraStateNot))) - return SPELL_FAILED_CASTER_AURASTATE; - - // Caster aura req check if need - if(auraRestrictions && auraRestrictions->casterAuraSpell && !m_caster->HasAura(auraRestrictions->casterAuraSpell)) - return SPELL_FAILED_CASTER_AURASTATE; - if(auraRestrictions && auraRestrictions->excludeCasterAuraSpell) - { - // Special cases of non existing auras handling - if(auraRestrictions->excludeCasterAuraSpell == 61988) - { - // Avenging Wrath Marker - if (m_caster->HasAura(61987)) - return SPELL_FAILED_CASTER_AURASTATE; - } - else if(m_caster->HasAura(auraRestrictions->excludeCasterAuraSpell)) - return SPELL_FAILED_CASTER_AURASTATE; - } - - if (m_caster->GetTypeId() == TYPEID_PLAYER) - { - // cancel autorepeat spells if cast start when moving - // (not wand currently autorepeat cast delayed to moving stop anyway in spell update code) - if (((Player*)m_caster)->isMoving() && !m_caster->HasAffectedAura(SPELL_AURA_ALLOW_CAST_WHILE_MOVING, m_spellInfo)) - { - // skip stuck spell to allow use it in falling case and apply spell limitations at movement - if ((!((Player*)m_caster)->m_movementInfo.HasMovementFlag(MOVEFLAG_FALLINGFAR) || m_spellInfo->GetSpellEffectIdByIndex(EFFECT_INDEX_0) != SPELL_EFFECT_STUCK) && - (IsAutoRepeat() || (m_spellInfo->GetAuraInterruptFlags() & AURA_INTERRUPT_FLAG_NOT_SEATED) != 0)) - return SPELL_FAILED_MOVING; - } - - if (!m_IsTriggeredSpell && NeedsComboPoints(m_spellInfo) && !m_caster->IsIgnoreUnitState(m_spellInfo, IGNORE_UNIT_TARGET_STATE) && - (!m_targets.getUnitTarget() || m_targets.getUnitTarget()->GetObjectGuid() != ((Player*)m_caster)->GetComboTargetGuid()) && - !m_spellInfo->HasAttribute(SPELL_ATTR_EX8_IGNORE_TARGET_FOR_COMBO_POINTS)) - // warrior not have real combo-points at client side but use this way for mark allow Overpower use - return m_caster->getClass() == CLASS_WARRIOR ? SPELL_FAILED_CASTER_AURASTATE : SPELL_FAILED_NO_COMBO_POINTS; - } - - // Spells like Disengage are allowed only in combat - if (!m_caster->isInCombat() && m_spellInfo->HasAttribute(SPELL_ATTR_STOP_ATTACK_TARGET) && m_spellInfo->HasAttribute(SPELL_ATTR_EX2_UNK26)) - return SPELL_FAILED_CASTER_AURASTATE; - - SpellClassOptionsEntry const* classOptions = m_spellInfo->GetSpellClassOptions(); - - if(Unit *target = m_targets.getUnitTarget()) - { - // target state requirements (not allowed state), apply to self also - if(auraRestrictions && auraRestrictions->TargetAuraStateNot && target->HasAuraState(AuraState(auraRestrictions->TargetAuraStateNot))) - return SPELL_FAILED_TARGET_AURASTATE; - - if (!m_IsTriggeredSpell && IsDeathOnlySpell(m_spellInfo) && target->isAlive()) - return SPELL_FAILED_TARGET_NOT_DEAD; - - // Target aura req check if need - if(auraRestrictions && auraRestrictions->targetAuraSpell && !target->HasAura(auraRestrictions->targetAuraSpell)) - return SPELL_FAILED_CASTER_AURASTATE; - - if(auraRestrictions && auraRestrictions->excludeTargetAuraSpell) - { - // Special cases of non existing auras handling - if (auraRestrictions->excludeTargetAuraSpell == 61988) - { - // Avenging Wrath Marker - if (target->HasAura(61987)) - return SPELL_FAILED_CASTER_AURASTATE; - } - else if (target->HasAura(auraRestrictions->excludeTargetAuraSpell)) - return SPELL_FAILED_CASTER_AURASTATE; - } - - // totem immunity for channeled spells(needs to be before spell cast) - // spell attribs for player channeled spells - if (m_spellInfo->HasAttribute(SPELL_ATTR_EX_UNK14) - && m_spellInfo->HasAttribute(SPELL_ATTR_EX5_UNK13) - && target->GetTypeId() == TYPEID_UNIT - && ((Creature*)target)->IsTotem()) - return SPELL_FAILED_IMMUNE; - - bool non_caster_target = target != m_caster && !IsSpellWithCasterSourceTargetsOnly(m_spellInfo); - - if (non_caster_target) - { - // target state requirements (apply to non-self only), to allow cast affects to self like Dirty Deeds - if (auraRestrictions && auraRestrictions->TargetAuraState && !target->HasAuraStateForCaster(AuraState(auraRestrictions->TargetAuraState), m_caster->GetObjectGuid()) && - !m_caster->IsIgnoreUnitState(m_spellInfo, auraRestrictions->TargetAuraState == AURA_STATE_FROZEN ? IGNORE_UNIT_TARGET_NON_FROZEN : IGNORE_UNIT_TARGET_STATE)) - return SPELL_FAILED_TARGET_AURASTATE; - - // Not allow casting on flying player - if (target->IsTaxiFlying()) - { - switch (m_spellInfo->Id) - { - // Except some spells from Taxi Flying cast - case 36573: // Vision Guide - case 42316: // Alcaz Survey Credit - case 42385: // Alcaz Survey Aura - break; - default: - return SPELL_FAILED_BAD_TARGETS; - } - } - - if (!m_IsTriggeredSpell && !m_spellInfo->HasAttribute(SPELL_ATTR_EX2_IGNORE_LOS) && VMAP::VMapFactory::checkSpellForLoS(m_spellInfo->Id) && !m_caster->IsWithinLOSInMap(target)) - return SPELL_FAILED_LINE_OF_SIGHT; - - // auto selection spell rank implemented in WorldSession::HandleCastSpellOpcode - // this case can be triggered if rank not found (too low-level target for first rank) - if (m_caster->GetTypeId() == TYPEID_PLAYER && !m_CastItem && !m_IsTriggeredSpell) - { - // spell expected to be auto-downranking in cast handle, so must be same - if (m_spellInfo != sSpellMgr.SelectAuraRankForLevel(m_spellInfo, target->getLevel())) - return SPELL_FAILED_LOWLEVEL; - } - - if (strict && m_spellInfo->HasAttribute(SPELL_ATTR_EX3_TARGET_ONLY_PLAYER) && target->GetTypeId() != TYPEID_PLAYER && !IsAreaOfEffectSpell(m_spellInfo)) - return SPELL_FAILED_BAD_TARGETS; - } - else if (m_caster == target) - { - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->IsInWorld()) - { - // Additional check for some spells - // If 0 spell effect empty - client not send target data (need use selection) - // TODO: check it on next client version - if (m_targets.m_targetMask == TARGET_FLAG_SELF && - m_spellInfo->GetEffectImplicitTargetAByIndex(EFFECT_INDEX_1) == TARGET_CHAIN_DAMAGE) - { - target = m_caster->GetMap()->GetUnit(((Player*)m_caster)->GetSelectionGuid()); - if (!target) - return SPELL_FAILED_BAD_TARGETS; - - m_targets.setUnitTarget(target); - } - } - - // Some special spells with non-caster only mode - - // Fire Shield - if (classOptions && classOptions->SpellFamilyName == SPELLFAMILY_WARLOCK && - m_spellInfo->SpellIconID == 16) - return SPELL_FAILED_BAD_TARGETS; - - // Focus Magic (main spell) - if (m_spellInfo->Id == 54646) - return SPELL_FAILED_BAD_TARGETS; - - // Lay on Hands (self cast) - if (classOptions && classOptions->SpellFamilyName == SPELLFAMILY_PALADIN && - classOptions->SpellFamilyFlags & UI64LIT(0x0000000000008000)) - { - if (target->HasAura(25771)) // Forbearance - return SPELL_FAILED_CASTER_AURASTATE; - if (target->HasAura(61987)) // Avenging Wrath Marker - return SPELL_FAILED_CASTER_AURASTATE; - } - } - - // check pet presents - for (int j = 0; j < MAX_EFFECT_INDEX; ++j) - { - SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(SpellEffectIndex(j)); - if(!spellEffect) - continue; - - if(spellEffect->EffectImplicitTargetA == TARGET_PET) - { - Pet* pet = m_caster->GetPet(); - if (!pet) - { - if (m_triggeredByAuraSpell) // not report pet not existence for triggered spells - return SPELL_FAILED_DONT_REPORT; - else - return SPELL_FAILED_NO_PET; - } - else if (!pet->isAlive()) - return SPELL_FAILED_TARGETS_DEAD; - break; - } - } - - // check creature type - // ignore self casts (including area casts when caster selected as target) - if (non_caster_target) - { - if (!CheckTargetCreatureType(target)) - { - if (target->GetTypeId() == TYPEID_PLAYER) - return SPELL_FAILED_TARGET_IS_PLAYER; - else - return SPELL_FAILED_BAD_TARGETS; - } - - // simple cases - bool explicit_target_mode = false; - bool target_hostile = false; - bool target_hostile_checked = false; - bool target_friendly = false; - bool target_friendly_checked = false; - for (int k = 0; k < MAX_EFFECT_INDEX; ++k) - { - SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(SpellEffectIndex(k)); - if(!spellEffect) - continue; - if (IsExplicitPositiveTarget(spellEffect->EffectImplicitTargetA)) - { - if (!target_hostile_checked) - { - target_hostile_checked = true; - target_hostile = m_caster->IsHostileTo(target); - } - - if (target_hostile) - return SPELL_FAILED_BAD_TARGETS; - - explicit_target_mode = true; - } - else if (IsExplicitNegativeTarget(spellEffect->EffectImplicitTargetA)) - { - if (!target_friendly_checked) - { - target_friendly_checked = true; - target_friendly = m_caster->IsFriendlyTo(target); - } - - if (target_friendly) - return SPELL_FAILED_BAD_TARGETS; - - explicit_target_mode = true; - } - } - // TODO: this check can be applied and for player to prevent cheating when IsPositiveSpell will return always correct result. - // check target for pet/charmed casts (not self targeted), self targeted cast used for area effects and etc - if (!explicit_target_mode && m_caster->GetTypeId() == TYPEID_UNIT && m_caster->GetCharmerOrOwnerGuid()) - { - // check correctness positive/negative cast target (pet cast real check and cheating check) - if (IsPositiveSpell(m_spellInfo->Id)) - { - if (!target_hostile_checked) - { - target_hostile_checked = true; - target_hostile = m_caster->IsHostileTo(target); - } - - if (target_hostile) - return SPELL_FAILED_BAD_TARGETS; - } - else - { - if (!target_friendly_checked) - { - target_friendly_checked = true; - target_friendly = m_caster->IsFriendlyTo(target); - } - - if (target_friendly) - return SPELL_FAILED_BAD_TARGETS; - } - } - } - - if (IsPositiveSpell(m_spellInfo->Id)) - if (target->IsImmuneToSpell(m_spellInfo, target == m_caster)) - return SPELL_FAILED_TARGET_AURASTATE; - - // Must be behind the target. - if (m_spellInfo->AttributesEx2 == SPELL_ATTR_EX2_UNK20 && m_spellInfo->HasAttribute(SPELL_ATTR_EX_UNK9) && target->HasInArc(M_PI_F, m_caster)) - { - // Exclusion for Pounce: Facing Limitation was removed in 2.0.1, but it still uses the same, old Ex-Flags - // Exclusion for Mutilate:Facing Limitation was removed in 2.0.1 and 3.0.3, but they still use the same, old Ex-Flags - // Exclusion for Throw: Facing limitation was added in 3.2.x, but that shouldn't be - if (!m_spellInfo->IsFitToFamily(SPELLFAMILY_DRUID, UI64LIT(0x0000000000020000)) && - !m_spellInfo->IsFitToFamily(SPELLFAMILY_ROGUE, UI64LIT(0x0020000000000000)) && - m_spellInfo->Id != 2764) - { - SendInterrupted(2); - return SPELL_FAILED_NOT_BEHIND; - } - } - - // Target must be facing you. - if ((m_spellInfo->Attributes == (SPELL_ATTR_UNK4 | SPELL_ATTR_NOT_SHAPESHIFT | SPELL_ATTR_UNK18 | SPELL_ATTR_STOP_ATTACK_TARGET)) && !target->HasInArc(M_PI_F, m_caster)) - { - SendInterrupted(2); - return SPELL_FAILED_NOT_INFRONT; - } - - // check if target is in combat - if (non_caster_target && m_spellInfo->HasAttribute(SPELL_ATTR_EX_NOT_IN_COMBAT_TARGET) && target->isInCombat()) - return SPELL_FAILED_TARGET_AFFECTING_COMBAT; - } - // zone check - uint32 zone, area; - m_caster->GetZoneAndAreaId(zone, area); - - SpellCastResult locRes = sSpellMgr.GetSpellAllowedInLocationError(m_spellInfo, m_caster->GetMapId(), zone, area, - m_caster->GetCharmerOrOwnerPlayerOrPlayerItself()); - if (locRes != SPELL_CAST_OK) - return locRes; - - // not let players cast spells at mount (and let do it to creatures) - if (m_caster->IsMounted() && m_caster->GetTypeId() == TYPEID_PLAYER && !m_IsTriggeredSpell && - !IsPassiveSpell(m_spellInfo) && !m_spellInfo->HasAttribute(SPELL_ATTR_CASTABLE_WHILE_MOUNTED)) - { - if (m_caster->IsTaxiFlying()) - return SPELL_FAILED_NOT_ON_TAXI; - else - return SPELL_FAILED_NOT_MOUNTED; - } - - // always (except passive spells) check items - if (!IsPassiveSpell(m_spellInfo)) - { - SpellCastResult castResult = CheckItems(); - if (castResult != SPELL_CAST_OK) - return castResult; - } - - // check spell focus object - if (m_spellInfo->GetRequiresSpellFocus()) - { - GameObject* ok = NULL; - MaNGOS::GameObjectFocusCheck go_check(m_caster, m_spellInfo->GetRequiresSpellFocus()); - MaNGOS::GameObjectSearcher checker(ok, go_check); - Cell::VisitGridObjects(m_caster, checker, m_caster->GetMap()->GetVisibilityDistance()); - - if (!ok) - return SPELL_FAILED_REQUIRES_SPELL_FOCUS; - - focusObject = ok; // game object found in range - } - - // Database based targets from spell_target_script - if (m_UniqueTargetInfo.empty()) // skip second CheckCast apply (for delayed spells for example) - { - for (int j = 0; j < MAX_EFFECT_INDEX; ++j) - { - SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(SpellEffectIndex(j)); - if(!spellEffect) - continue; - - if (spellEffect->EffectImplicitTargetA == TARGET_SCRIPT || - (spellEffect->EffectImplicitTargetB == TARGET_SCRIPT && spellEffect->EffectImplicitTargetA != TARGET_SELF) || - spellEffect->EffectImplicitTargetA == TARGET_SCRIPT_COORDINATES || - spellEffect->EffectImplicitTargetB == TARGET_SCRIPT_COORDINATES || - spellEffect->EffectImplicitTargetA == TARGET_FOCUS_OR_SCRIPTED_GAMEOBJECT) - { - - SQLMultiStorage::SQLMSIteratorBounds bounds = sSpellScriptTargetStorage.getBounds(m_spellInfo->Id); - - if (bounds.first == bounds.second) - { - if (spellEffect->EffectImplicitTargetA == TARGET_SCRIPT || spellEffect->EffectImplicitTargetB == TARGET_SCRIPT) - sLog.outErrorDb("Spell entry %u, effect %i has EffectImplicitTargetA/EffectImplicitTargetB = TARGET_SCRIPT, but creature are not defined in `spell_script_target`", m_spellInfo->Id, j); - - if (spellEffect->EffectImplicitTargetA == TARGET_SCRIPT_COORDINATES || spellEffect->EffectImplicitTargetB == TARGET_SCRIPT_COORDINATES) - sLog.outErrorDb("Spell entry %u, effect %i has EffectImplicitTargetA/EffectImplicitTargetB = TARGET_SCRIPT_COORDINATES, but gameobject or creature are not defined in `spell_script_target`", m_spellInfo->Id, j); - - if (spellEffect->EffectImplicitTargetA == TARGET_FOCUS_OR_SCRIPTED_GAMEOBJECT) - sLog.outErrorDb("Spell entry %u, effect %i has EffectImplicitTargetA/EffectImplicitTargetB = TARGET_FOCUS_OR_SCRIPTED_GAMEOBJECT, but gameobject are not defined in `spell_script_target`", m_spellInfo->Id, j); - } - - SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex); - float range = GetSpellMaxRange(srange); - - Creature* targetExplicit = NULL; // used for cases where a target is provided (by script for example) - Creature* creatureScriptTarget = NULL; - GameObject* goScriptTarget = NULL; - - for (SQLMultiStorage::SQLMultiSIterator i_spellST = bounds.first; i_spellST != bounds.second; ++i_spellST) - { - if (i_spellST->CanNotHitWithSpellEffect(SpellEffectIndex(j))) - continue; - - switch (i_spellST->type) - { - case SPELL_TARGET_TYPE_GAMEOBJECT: - { - GameObject* p_GameObject = NULL; - - if (i_spellST->targetEntry) - { - MaNGOS::NearestGameObjectEntryInObjectRangeCheck go_check(*m_caster, i_spellST->targetEntry, range); - MaNGOS::GameObjectLastSearcher checker(p_GameObject, go_check); - Cell::VisitGridObjects(m_caster, checker, range); - - if (p_GameObject) - { - // remember found target and range, next attempt will find more near target with another entry - creatureScriptTarget = NULL; - goScriptTarget = p_GameObject; - range = go_check.GetLastRange(); - } - } - else if (focusObject) // Focus Object - { - float frange = m_caster->GetDistance(focusObject); - if (range >= frange) - { - creatureScriptTarget = NULL; - goScriptTarget = focusObject; - range = frange; - } - } - break; - } - case SPELL_TARGET_TYPE_CREATURE: - case SPELL_TARGET_TYPE_DEAD: - default: - { - Creature* p_Creature = NULL; - - // check if explicit target is provided and check it up against database valid target entry/state - if (Unit* pTarget = m_targets.getUnitTarget()) - { - if (pTarget->GetTypeId() == TYPEID_UNIT && pTarget->GetEntry() == i_spellST->targetEntry) - { - if (i_spellST->type == SPELL_TARGET_TYPE_DEAD && ((Creature*)pTarget)->IsCorpse()) - { - // always use spellMaxRange, in case GetLastRange returned different in a previous pass - if (pTarget->IsWithinDistInMap(m_caster, GetSpellMaxRange(srange))) - targetExplicit = (Creature*)pTarget; - } - else if (i_spellST->type == SPELL_TARGET_TYPE_CREATURE && pTarget->isAlive()) - { - // always use spellMaxRange, in case GetLastRange returned different in a previous pass - if (pTarget->IsWithinDistInMap(m_caster, GetSpellMaxRange(srange))) - targetExplicit = (Creature*)pTarget; - } - } - } - - // no target provided or it was not valid, so use closest in range - if (!targetExplicit) - { - MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck u_check(*m_caster, i_spellST->targetEntry, i_spellST->type != SPELL_TARGET_TYPE_DEAD, i_spellST->type == SPELL_TARGET_TYPE_DEAD, range); - MaNGOS::CreatureLastSearcher searcher(p_Creature, u_check); - - // Visit all, need to find also Pet* objects - Cell::VisitAllObjects(m_caster, searcher, range); - - range = u_check.GetLastRange(); - } - - // always prefer provided target if it's valid - if (targetExplicit) - creatureScriptTarget = targetExplicit; - else if (p_Creature) - creatureScriptTarget = p_Creature; - - if (creatureScriptTarget) - goScriptTarget = NULL; - - break; - } - } - } - - if (creatureScriptTarget) - { - // store coordinates for TARGET_SCRIPT_COORDINATES - if (spellEffect->EffectImplicitTargetA == TARGET_SCRIPT_COORDINATES || - spellEffect->EffectImplicitTargetB == TARGET_SCRIPT_COORDINATES) - { - m_targets.setDestination(creatureScriptTarget->GetPositionX(), creatureScriptTarget->GetPositionY(), creatureScriptTarget->GetPositionZ()); - - if (spellEffect->EffectImplicitTargetA == TARGET_SCRIPT_COORDINATES && spellEffect->Effect != SPELL_EFFECT_PERSISTENT_AREA_AURA) - AddUnitTarget(creatureScriptTarget, SpellEffectIndex(j)); - } - // store explicit target for TARGET_SCRIPT - else - { - if (spellEffect->EffectImplicitTargetA == TARGET_SCRIPT || - spellEffect->EffectImplicitTargetB == TARGET_SCRIPT) - AddUnitTarget(creatureScriptTarget, SpellEffectIndex(j)); - } - } - else if (goScriptTarget) - { - // store coordinates for TARGET_SCRIPT_COORDINATES - if (spellEffect->EffectImplicitTargetA == TARGET_SCRIPT_COORDINATES || - spellEffect->EffectImplicitTargetB == TARGET_SCRIPT_COORDINATES) - { - m_targets.setDestination(goScriptTarget->GetPositionX(), goScriptTarget->GetPositionY(), goScriptTarget->GetPositionZ()); - - if (spellEffect->EffectImplicitTargetA == TARGET_SCRIPT_COORDINATES && spellEffect->Effect != SPELL_EFFECT_PERSISTENT_AREA_AURA) - AddGOTarget(goScriptTarget, SpellEffectIndex(j)); - } - // store explicit target for TARGET_FOCUS_OR_SCRIPTED_GAMEOBJECT - else - { - if (spellEffect->EffectImplicitTargetA == TARGET_FOCUS_OR_SCRIPTED_GAMEOBJECT || - spellEffect->EffectImplicitTargetB == TARGET_FOCUS_OR_SCRIPTED_GAMEOBJECT) - AddGOTarget(goScriptTarget, SpellEffectIndex(j)); - } - } - // Missing DB Entry or targets for this spellEffect. - else - { - /* For TARGET_FOCUS_OR_SCRIPTED_GAMEOBJECT makes DB targets optional not required for now - * TODO: Makes more research for this target type - */ - if (spellEffect->EffectImplicitTargetA != TARGET_FOCUS_OR_SCRIPTED_GAMEOBJECT) - { - // not report target not existence for triggered spells - if (m_triggeredByAuraSpell || m_IsTriggeredSpell) - return SPELL_FAILED_DONT_REPORT; - else - return SPELL_FAILED_BAD_TARGETS; - } - } - } - } - } - - if (!m_IsTriggeredSpell) - { - SpellCastResult castResult = CheckRange(strict); - if (castResult != SPELL_CAST_OK) - return castResult; - } - - { - SpellCastResult castResult = CheckPower(); - if (castResult != SPELL_CAST_OK) - return castResult; - } - - if (!m_IsTriggeredSpell) // triggered spell not affected by stun/etc - { - SpellCastResult castResult = CheckCasterAuras(); - if (castResult != SPELL_CAST_OK) - return castResult; - } - - for (int i = 0; i < MAX_EFFECT_INDEX; ++i) - { - SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(SpellEffectIndex(i)); - if(!spellEffect) - continue; - // for effects of spells that have only one target - switch(spellEffect->Effect) - { - case SPELL_EFFECT_INSTAKILL: - // Death Pact - if (m_spellInfo->Id == 48743) - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return SPELL_FAILED_ERROR; - - if (!((Player*)m_caster)->GetSelectionGuid()) - return SPELL_FAILED_BAD_IMPLICIT_TARGETS; - Pet* target = m_caster->GetMap()->GetPet(((Player*)m_caster)->GetSelectionGuid()); - - // alive - if (!target || target->isDead()) - return SPELL_FAILED_BAD_IMPLICIT_TARGETS; - // undead - if (target->GetCreatureType() != CREATURE_TYPE_UNDEAD) - return SPELL_FAILED_BAD_IMPLICIT_TARGETS; - // owned - if (target->GetOwnerGuid() != m_caster->GetObjectGuid()) - return SPELL_FAILED_BAD_IMPLICIT_TARGETS; - - float dist = GetSpellRadius(sSpellRadiusStore.LookupEntry(spellEffect->GetRadiusIndex())); - if (!target->IsWithinDistInMap(m_caster,dist)) - return SPELL_FAILED_OUT_OF_RANGE; - - // will set in target selection code - } - break; - case SPELL_EFFECT_DUMMY: - { - if (m_spellInfo->Id == 51582) // Rocket Boots Engaged - { - if (m_caster->IsInWater()) - return SPELL_FAILED_ONLY_ABOVEWATER; - } - else if (m_spellInfo->Id == 51690) // Killing Spree - { - UnitList targets; - - float radius = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)); - - MaNGOS::AnyUnfriendlyVisibleUnitInObjectRangeCheck unitCheck(m_caster, m_caster, radius); - MaNGOS::UnitListSearcher checker(targets, unitCheck); - Cell::VisitAllObjects(m_caster, checker, radius); - - if (targets.empty()) - return SPELL_FAILED_OUT_OF_RANGE; - } - else if (m_spellInfo->Id == 68996) // Two forms - { - if (m_caster->isInCombat()) - return SPELL_FAILED_AFFECTING_COMBAT; - } - else if (m_spellInfo->SpellIconID == 156) // Holy Shock - { - // spell different for friends and enemies - // hart version required facing - if (m_targets.getUnitTarget() && !m_caster->IsFriendlyTo(m_targets.getUnitTarget()) && !m_caster->HasInArc(M_PI_F, m_targets.getUnitTarget())) - return SPELL_FAILED_UNIT_NOT_INFRONT; - } - // Fire Nova - if (m_spellInfo->GetSpellFamilyName() == SPELLFAMILY_SHAMAN && m_spellInfo->SpellIconID == 33) - { - // fire totems slot - if (!m_caster->GetTotemGuid(TOTEM_SLOT_FIRE)) - return SPELL_FAILED_TOTEMS; - } - break; - } - case SPELL_EFFECT_DISTRACT: // All nearby enemies must not be in combat - { - if (m_targets.m_targetMask & (TARGET_FLAG_DEST_LOCATION | TARGET_FLAG_SOURCE_LOCATION)) - { - UnitList targetsCombat; - float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(spellEffect->GetRadiusIndex())); - - FillAreaTargets(targetsCombat, radius, PUSH_DEST_CENTER, SPELL_TARGETS_AOE_DAMAGE); - - if (targetsCombat.empty()) - break; - - for (UnitList::iterator itr = targetsCombat.begin(); itr != targetsCombat.end(); ++itr) - if ((*itr)->isInCombat()) - return SPELL_FAILED_TARGET_IN_COMBAT; - } - break; - } - case SPELL_EFFECT_SCHOOL_DAMAGE: - { - // Hammer of Wrath - if (m_spellInfo->SpellVisual[0] == 7250) - { - if (!m_targets.getUnitTarget()) - return SPELL_FAILED_BAD_IMPLICIT_TARGETS; - - if (m_targets.getUnitTarget()->GetHealth() > m_targets.getUnitTarget()->GetMaxHealth() * 0.2) - return SPELL_FAILED_BAD_TARGETS; - } - break; - } - case SPELL_EFFECT_TAMECREATURE: - { - // Spell can be triggered, we need to check original caster prior to caster - Unit* caster = GetAffectiveCaster(); - if (!caster || caster->GetTypeId() != TYPEID_PLAYER || - !m_targets.getUnitTarget() || - m_targets.getUnitTarget()->GetTypeId() == TYPEID_PLAYER) - return SPELL_FAILED_BAD_TARGETS; - - Player* plrCaster = (Player*)caster; - - bool gmmode = m_triggeredBySpellInfo == NULL; - - if (gmmode && !ChatHandler(plrCaster).FindCommand("npc tame")) - { - plrCaster->SendPetTameFailure(PETTAME_UNKNOWNERROR); - return SPELL_FAILED_DONT_REPORT; - } - - if (plrCaster->getClass() != CLASS_HUNTER && !gmmode) - { - plrCaster->SendPetTameFailure(PETTAME_UNITSCANTTAME); - return SPELL_FAILED_DONT_REPORT; - } - - Creature* target = (Creature*)m_targets.getUnitTarget(); - - if (target->IsPet() || target->isCharmed()) - { - plrCaster->SendPetTameFailure(PETTAME_CREATUREALREADYOWNED); - return SPELL_FAILED_DONT_REPORT; - } - - if (target->getLevel() > plrCaster->getLevel() && !gmmode) - { - plrCaster->SendPetTameFailure(PETTAME_TOOHIGHLEVEL); - return SPELL_FAILED_DONT_REPORT; - } - - if (target->GetCreatureInfo()->IsExotic() && !plrCaster->CanTameExoticPets() && !gmmode) - { - plrCaster->SendPetTameFailure(PETTAME_CANTCONTROLEXOTIC); - return SPELL_FAILED_DONT_REPORT; - } - - if (!target->GetCreatureInfo()->isTameable(plrCaster->CanTameExoticPets())) - { - plrCaster->SendPetTameFailure(PETTAME_NOTTAMEABLE); - return SPELL_FAILED_DONT_REPORT; - } - - if (plrCaster->GetPetGuid() || plrCaster->GetCharmGuid()) - { - plrCaster->SendPetTameFailure(PETTAME_ANOTHERSUMMONACTIVE); - return SPELL_FAILED_DONT_REPORT; - } - - break; - } - case SPELL_EFFECT_LEARN_SPELL: - { - if(spellEffect->EffectImplicitTargetA != TARGET_PET) - break; - - Pet* pet = m_caster->GetPet(); - - if (!pet) - return SPELL_FAILED_NO_PET; - - SpellEntry const *learn_spellproto = sSpellStore.LookupEntry(spellEffect->EffectTriggerSpell); - - if (!learn_spellproto) - return SPELL_FAILED_NOT_KNOWN; - - if(m_spellInfo->GetSpellLevel() > pet->getLevel()) - return SPELL_FAILED_LOWLEVEL; - - break; - } - case SPELL_EFFECT_LEARN_PET_SPELL: - { - Pet* pet = m_caster->GetPet(); - - if (!pet) - return SPELL_FAILED_NO_PET; - - SpellEntry const *learn_spellproto = sSpellStore.LookupEntry(spellEffect->EffectTriggerSpell); - if (!learn_spellproto) - return SPELL_FAILED_NOT_KNOWN; - - if(m_spellInfo->GetSpellLevel() > pet->getLevel()) - return SPELL_FAILED_LOWLEVEL; - - break; - } - case SPELL_EFFECT_APPLY_GLYPH: - { - uint32 glyphId = spellEffect->EffectMiscValue; - if(GlyphPropertiesEntry const *gp = sGlyphPropertiesStore.LookupEntry(glyphId)) - if(m_caster->HasAura(gp->SpellId)) - return SPELL_FAILED_UNIQUE_GLYPH; - break; - } - case SPELL_EFFECT_FEED_PET: - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return SPELL_FAILED_BAD_TARGETS; - - Item* foodItem = m_targets.getItemTarget(); - if (!foodItem) - return SPELL_FAILED_BAD_TARGETS; - - Pet* pet = m_caster->GetPet(); - - if (!pet) - return SPELL_FAILED_NO_PET; - - if (!pet->HaveInDiet(foodItem->GetProto())) - return SPELL_FAILED_WRONG_PET_FOOD; - - if (!pet->GetCurrentFoodBenefitLevel(foodItem->GetProto()->ItemLevel)) - return SPELL_FAILED_FOOD_LOWLEVEL; - - if (pet->isInCombat()) - return SPELL_FAILED_AFFECTING_COMBAT; - - break; - } - case SPELL_EFFECT_POWER_BURN: - case SPELL_EFFECT_POWER_DRAIN: - { - // Can be area effect, Check only for players and not check if target - caster (spell can have multiply drain/burn effects) - if (m_caster->GetTypeId() == TYPEID_PLAYER) - if (Unit* target = m_targets.getUnitTarget()) - if (target != m_caster && int32(target->getPowerType()) != spellEffect->EffectMiscValue) - return SPELL_FAILED_BAD_TARGETS; - break; - } - case SPELL_EFFECT_CHARGE: - { - if (m_caster->hasUnitState(UNIT_STAT_ROOT)) - return SPELL_FAILED_ROOTED; - - break; - } - case SPELL_EFFECT_SKINNING: - { - if (m_caster->GetTypeId() != TYPEID_PLAYER || !m_targets.getUnitTarget() || m_targets.getUnitTarget()->GetTypeId() != TYPEID_UNIT) - return SPELL_FAILED_BAD_TARGETS; - - if (!m_targets.getUnitTarget()->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE)) - return SPELL_FAILED_TARGET_UNSKINNABLE; - - Creature* creature = (Creature*)m_targets.getUnitTarget(); - if (creature->GetCreatureType() != CREATURE_TYPE_CRITTER && (!creature->lootForBody || creature->lootForSkin || !creature->loot.empty())) - { - return SPELL_FAILED_TARGET_NOT_LOOTED; - } - - uint32 skill = creature->GetCreatureInfo()->GetRequiredLootSkill(); - - int32 skillValue = ((Player*)m_caster)->GetSkillValue(skill); - int32 TargetLevel = m_targets.getUnitTarget()->getLevel(); - int32 ReqValue = (skillValue < 100 ? (TargetLevel - 10) * 10 : TargetLevel * 5); - if (ReqValue > skillValue) - return SPELL_FAILED_LOW_CASTLEVEL; - - // chance for fail at orange skinning attempt - if ((m_selfContainer && (*m_selfContainer) == this) && - skillValue < sWorld.GetConfigMaxSkillValue() && - (ReqValue < 0 ? 0 : ReqValue) > irand(skillValue - 25, skillValue + 37)) - return SPELL_FAILED_TRY_AGAIN; - - break; - } - case SPELL_EFFECT_OPEN_LOCK: - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) // only players can open locks, gather etc. - return SPELL_FAILED_BAD_TARGETS; - - // we need a go target in case of TARGET_GAMEOBJECT (for other targets acceptable GO and items) - if (spellEffect->EffectImplicitTargetA == TARGET_GAMEOBJECT) - { - if (!m_targets.getGOTarget()) - return SPELL_FAILED_BAD_TARGETS; - } - - // get the lock entry - uint32 lockId = 0; - if (GameObject* go = m_targets.getGOTarget()) - { - // In BattleGround players can use only flags and banners - if (((Player*)m_caster)->InBattleGround() && - !((Player*)m_caster)->CanUseBattleGroundObject()) - return SPELL_FAILED_TRY_AGAIN; - - lockId = go->GetGOInfo()->GetLockId(); - if (!lockId) - return SPELL_FAILED_ALREADY_OPEN; - } - else if (Item* item = m_targets.getItemTarget()) - { - // not own (trade?) - if (item->GetOwner() != m_caster) - return SPELL_FAILED_ITEM_GONE; - - lockId = item->GetProto()->LockID; - - // if already unlocked - if (!lockId || item->HasFlag(ITEM_FIELD_FLAGS, ITEM_DYNFLAG_UNLOCKED)) - return SPELL_FAILED_ALREADY_OPEN; - } - else - return SPELL_FAILED_BAD_TARGETS; - - SkillType skillId = SKILL_NONE; - int32 reqSkillValue = 0; - int32 skillValue = 0; - - // check lock compatibility - SpellCastResult res = CanOpenLock(SpellEffectIndex(i), lockId, skillId, reqSkillValue, skillValue); - if (res != SPELL_CAST_OK) - return res; - - // chance for fail at orange mining/herb/LockPicking gathering attempt - // second check prevent fail at rechecks - if (skillId != SKILL_NONE && (!m_selfContainer || ((*m_selfContainer) != this))) - { - bool canFailAtMax = skillId != SKILL_HERBALISM && skillId != SKILL_MINING; - - // chance for failure in orange gather / lockpick (gathering skill can't fail at maxskill) - if ((canFailAtMax || skillValue < sWorld.GetConfigMaxSkillValue()) && reqSkillValue > irand(skillValue - 25, skillValue + 37)) - return SPELL_FAILED_TRY_AGAIN; - } - break; - } - case SPELL_EFFECT_SUMMON_DEAD_PET: - { - Creature* pet = m_caster->GetPet(); - if (!pet) - return SPELL_FAILED_NO_PET; - - if (pet->isAlive()) - return SPELL_FAILED_ALREADY_HAVE_SUMMON; - - break; - } - // This is generic summon effect - case SPELL_EFFECT_SUMMON: - { - if (SummonPropertiesEntry const *summon_prop = sSummonPropertiesStore.LookupEntry(spellEffect->EffectMiscValueB)) - { - if (summon_prop->Group == SUMMON_PROP_GROUP_PETS) - { - if (m_caster->GetPetGuid()) - return SPELL_FAILED_ALREADY_HAVE_SUMMON; - - if (m_caster->GetCharmGuid()) - return SPELL_FAILED_ALREADY_HAVE_CHARM; - } - } - - break; - } - case SPELL_EFFECT_SUMMON_PET: - { - if (m_caster->GetPetGuid()) // let warlock do a replacement summon - { - - Pet* pet = ((Player*)m_caster)->GetPet(); - - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->getClass() == CLASS_WARLOCK) - { - if (strict) // Summoning Disorientation, trigger pet stun (cast by pet so it doesn't attack player) - pet->CastSpell(pet, 32752, true, NULL, NULL, pet->GetObjectGuid()); - } - else - return SPELL_FAILED_ALREADY_HAVE_SUMMON; - } - - if (m_caster->GetCharmGuid()) - return SPELL_FAILED_ALREADY_HAVE_CHARM; - - break; - } - case SPELL_EFFECT_SUMMON_PLAYER: - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return SPELL_FAILED_BAD_TARGETS; - if (!((Player*)m_caster)->GetSelectionGuid()) - return SPELL_FAILED_BAD_TARGETS; - - Player* target = sObjectMgr.GetPlayer(((Player*)m_caster)->GetSelectionGuid()); - if (!target || ((Player*)m_caster) == target || !target->IsInSameRaidWith((Player*)m_caster)) - return SPELL_FAILED_BAD_TARGETS; - - // check if our map is dungeon - if (sMapStore.LookupEntry(m_caster->GetMapId())->IsDungeon()) - { - InstanceTemplate const* instance = ObjectMgr::GetInstanceTemplate(m_caster->GetMapId()); - if (!instance) - return SPELL_FAILED_TARGET_NOT_IN_INSTANCE; - if (instance->levelMin > target->getLevel()) - return SPELL_FAILED_LOWLEVEL; - if (instance->levelMax && instance->levelMax < target->getLevel()) - return SPELL_FAILED_HIGHLEVEL; - } - break; - } - case SPELL_EFFECT_LEAP: - case SPELL_EFFECT_TELEPORT_UNITS_FACE_CASTER: - { - float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(spellEffect->GetRadiusIndex())); - float fx = m_caster->GetPositionX() + dis * cos(m_caster->GetOrientation()); - float fy = m_caster->GetPositionY() + dis * sin(m_caster->GetOrientation()); - // teleport a bit above terrain level to avoid falling below it - float fz = m_caster->GetMap()->GetHeight(m_caster->GetPhaseMask(), fx, fy, m_caster->GetPositionZ()); - if (fz <= INVALID_HEIGHT) // note: this also will prevent use effect in instances without vmaps height enabled - return SPELL_FAILED_TRY_AGAIN; - - float caster_pos_z = m_caster->GetPositionZ(); - // Control the caster to not climb or drop when +-fz > 8 - if (!(fz <= caster_pos_z + 8 && fz >= caster_pos_z - 8)) - return SPELL_FAILED_TRY_AGAIN; - - // not allow use this effect at battleground until battleground start - if (m_caster->GetTypeId() == TYPEID_PLAYER) - if (BattleGround const* bg = ((Player*)m_caster)->GetBattleGround()) - if (bg->GetStatus() != STATUS_IN_PROGRESS) - return SPELL_FAILED_TRY_AGAIN; - break; - } - case SPELL_EFFECT_STEAL_BENEFICIAL_BUFF: - { - if (m_targets.getUnitTarget() == m_caster) - return SPELL_FAILED_BAD_TARGETS; - break; - } - default: break; - } - } - - for (int i = 0; i < MAX_EFFECT_INDEX; ++i) - { - SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(SpellEffectIndex(i)); - if(!spellEffect) - continue; - - // Do not check in case of junk in DBC - if (!IsAuraApplyEffect(m_spellInfo, SpellEffectIndex(i))) - continue; - - // Possible Unit-target for the spell - Unit* expectedTarget = GetPrefilledUnitTargetOrUnitTarget(SpellEffectIndex(i)); - - switch (spellEffect->EffectApplyAuraName) - { - case SPELL_AURA_DUMMY: - { - // custom check - switch (m_spellInfo->Id) - { - case 34026: // Kill Command - if (!m_caster->GetPet()) - return SPELL_FAILED_NO_PET; - break; - case 61336: // Survival Instincts - if (m_caster->GetTypeId() != TYPEID_PLAYER || !((Player*)m_caster)->IsInFeralForm()) - return SPELL_FAILED_ONLY_SHAPESHIFT; - break; - default: - break; - } - break; - } - case SPELL_AURA_MOD_POSSESS: - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return SPELL_FAILED_UNKNOWN; - - if (expectedTarget == m_caster) - return SPELL_FAILED_BAD_TARGETS; - - if (m_caster->GetPetGuid()) - return SPELL_FAILED_ALREADY_HAVE_SUMMON; - - if (m_caster->GetCharmGuid()) - return SPELL_FAILED_ALREADY_HAVE_CHARM; - - if (m_caster->GetCharmerGuid()) - return SPELL_FAILED_CHARMED; - - if (!expectedTarget) - return SPELL_FAILED_BAD_IMPLICIT_TARGETS; - - if (expectedTarget->GetCharmerGuid()) - return SPELL_FAILED_CHARMED; - - if (int32(expectedTarget->getLevel()) > CalculateDamage(SpellEffectIndex(i), expectedTarget)) - return SPELL_FAILED_HIGHLEVEL; - - break; - } - case SPELL_AURA_MOD_CHARM: - { - if (expectedTarget == m_caster) - return SPELL_FAILED_BAD_TARGETS; - - if (m_caster->GetPetGuid()) - return SPELL_FAILED_ALREADY_HAVE_SUMMON; - - if (m_caster->GetCharmGuid()) - return SPELL_FAILED_ALREADY_HAVE_CHARM; - - if (m_caster->GetCharmerGuid()) - return SPELL_FAILED_CHARMED; - - if (!expectedTarget) - return SPELL_FAILED_BAD_IMPLICIT_TARGETS; - - if (expectedTarget->GetCharmerGuid()) - return SPELL_FAILED_CHARMED; - - if (int32(expectedTarget->getLevel()) > CalculateDamage(SpellEffectIndex(i), expectedTarget)) - return SPELL_FAILED_HIGHLEVEL; - - break; - } - case SPELL_AURA_MOD_POSSESS_PET: - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return SPELL_FAILED_UNKNOWN; - - if (m_caster->GetCharmGuid()) - return SPELL_FAILED_ALREADY_HAVE_CHARM; - - if (m_caster->GetCharmerGuid()) - return SPELL_FAILED_CHARMED; - - Pet* pet = m_caster->GetPet(); - if (!pet) - return SPELL_FAILED_NO_PET; - - if (pet->GetCharmerGuid()) - return SPELL_FAILED_CHARMED; - - break; - } - case SPELL_AURA_MOUNTED: - { - - if (m_caster->GetTypeId() == TYPEID_PLAYER && ((Player*)m_caster)->GetTransport()) - return SPELL_FAILED_NO_MOUNTS_ALLOWED; - - if (spellEffect->EffectMiscValueB && !m_caster->GetMountCapability(spellEffect->EffectMiscValueB)) - return SPELL_FAILED_NOT_HERE; - - // Ignore map check if spell have AreaId. AreaId already checked and this prevent special mount spells - if (m_caster->GetTypeId() == TYPEID_PLAYER && !sMapStore.LookupEntry(m_caster->GetMapId())->IsMountAllowed() && !m_IsTriggeredSpell && !m_spellInfo->GetAreaGroupId()) - return SPELL_FAILED_NO_MOUNTS_ALLOWED; - - if (m_caster->IsInDisallowedMountForm()) - return SPELL_FAILED_NOT_SHAPESHIFT; - - break; - } - case SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS: - { - if (!expectedTarget) - return SPELL_FAILED_BAD_IMPLICIT_TARGETS; - - // can be casted at non-friendly unit or own pet/charm - if (m_caster->IsFriendlyTo(expectedTarget)) - return SPELL_FAILED_TARGET_FRIENDLY; - - break; - } - case SPELL_AURA_FLY: - case SPELL_AURA_MOD_FLIGHT_SPEED_MOUNTED: - { - // not allow cast fly spells if not have req. skills (all spells is self target) - // allow always ghost flight spells - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->isAlive()) - { - if (!((Player*)m_caster)->CanStartFlyInArea(m_caster->GetMapId(), zone, area)) - return m_IsTriggeredSpell ? SPELL_FAILED_DONT_REPORT : SPELL_FAILED_NOT_HERE; - } - break; - } - case SPELL_AURA_PERIODIC_MANA_LEECH: - { - if (!expectedTarget) - return SPELL_FAILED_BAD_IMPLICIT_TARGETS; - - if (m_caster->GetTypeId() != TYPEID_PLAYER || m_CastItem) - break; - - if (expectedTarget->getPowerType() != POWER_MANA) - return SPELL_FAILED_BAD_TARGETS; - - break; - } - case SPELL_AURA_CONTROL_VEHICLE: - { - if (m_caster->HasAuraType(SPELL_AURA_MOUNTED)) - return SPELL_FAILED_NOT_MOUNTED; - - if (!expectedTarget || !expectedTarget->IsVehicle()) - return SPELL_FAILED_BAD_TARGETS; - - // It is possible to change between vehicles that are boarded on each other - if (m_caster->IsBoarded() && m_caster->GetTransportInfo()->IsOnVehicle()) - { - // Check if trying to board a vehicle that is boarded on current transport - bool boardedOnEachOther = m_caster->GetTransportInfo()->HasOnBoard(expectedTarget); - // Check if trying to board a vehicle that has the current transport on board - if (!boardedOnEachOther) - boardedOnEachOther = expectedTarget->GetVehicleInfo()->HasOnBoard(m_caster); - - if (!boardedOnEachOther) - return SPELL_FAILED_NOT_ON_TRANSPORT; - } - - if (!expectedTarget->GetVehicleInfo()->CanBoard(m_caster)) - return SPELL_FAILED_BAD_TARGETS; - - break; - } - case SPELL_AURA_MIRROR_IMAGE: - { - if (!expectedTarget) - return SPELL_FAILED_BAD_TARGETS; - - // Target must be creature. TODO: Check if target can also be player - if (expectedTarget->GetTypeId() != TYPEID_UNIT) - return SPELL_FAILED_BAD_TARGETS; - - if (expectedTarget == m_caster) // Clone self can't be accepted - return SPELL_FAILED_BAD_TARGETS; - - // It is assumed that target can not be cloned if already cloned by same or other clone auras - if (expectedTarget->HasAuraType(SPELL_AURA_MIRROR_IMAGE)) - return SPELL_FAILED_BAD_TARGETS; - - break; - } - case SPELL_AURA_WORGEN_TRANSFORM: - { - if (!m_caster->HasWorgenForm()) - return m_IsTriggeredSpell ? SPELL_FAILED_DONT_REPORT : SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW; - break; - } - default: - break; - } - } - - // check trade slot case (last, for allow catch any another cast problems) - if (m_targets.m_targetMask & TARGET_FLAG_TRADE_ITEM) - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return SPELL_FAILED_NOT_TRADING; - - Player* pCaster = ((Player*)m_caster); - TradeData* my_trade = pCaster->GetTradeData(); - - if (!my_trade) - return SPELL_FAILED_NOT_TRADING; - - TradeSlots slot = TradeSlots(m_targets.getItemTargetGuid().GetRawValue()); - if (slot != TRADE_SLOT_NONTRADED) - return SPELL_FAILED_ITEM_NOT_READY; - - // if trade not complete then remember it in trade data - if (!my_trade->IsInAcceptProcess()) - { - // Spell will be casted at completing the trade. Silently ignore at this place - my_trade->SetSpell(m_spellInfo->Id, m_CastItem); - return SPELL_FAILED_DONT_REPORT; - } - } - - // all ok - return SPELL_CAST_OK; -} - -SpellCastResult Spell::CheckPetCast(Unit* target) -{ - if (!m_caster->isAlive()) - return SPELL_FAILED_CASTER_DEAD; - - if (m_caster->IsNonMeleeSpellCasted(false)) // prevent spellcast interruption by another spellcast - return SPELL_FAILED_SPELL_IN_PROGRESS; - if (m_caster->isInCombat() && IsNonCombatSpell(m_spellInfo)) - return SPELL_FAILED_AFFECTING_COMBAT; - - if (m_caster->GetTypeId() == TYPEID_UNIT && (((Creature*)m_caster)->IsPet() || m_caster->isCharmed())) - { - // dead owner (pets still alive when owners ressed?) - if (m_caster->GetCharmerOrOwner() && !m_caster->GetCharmerOrOwner()->isAlive()) - return SPELL_FAILED_CASTER_DEAD; - - if (!target && m_targets.getUnitTarget()) - target = m_targets.getUnitTarget(); - - bool need = false; - for (int i = 0; i < MAX_EFFECT_INDEX; ++i) - { - SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(SpellEffectIndex(i)); - if(!spellEffect) - continue; - - if (spellEffect->EffectImplicitTargetA == TARGET_CHAIN_DAMAGE || - spellEffect->EffectImplicitTargetA == TARGET_SINGLE_FRIEND || - spellEffect->EffectImplicitTargetA == TARGET_SINGLE_FRIEND_2 || - spellEffect->EffectImplicitTargetA == TARGET_DUELVSPLAYER || - spellEffect->EffectImplicitTargetA == TARGET_SINGLE_PARTY || - spellEffect->EffectImplicitTargetA == TARGET_CURRENT_ENEMY_COORDINATES) - { - need = true; - if (!target) - return SPELL_FAILED_BAD_IMPLICIT_TARGETS; - break; - } - } - if (need) - m_targets.setUnitTarget(target); - - Unit* _target = m_targets.getUnitTarget(); - - // for target dead/target not valid - if (_target && m_targets.m_targetMask & TARGET_FLAG_UNIT) - { - if (!_target->isTargetableForAttack()) - return SPELL_FAILED_BAD_TARGETS; // guessed error - - if (IsPositiveSpell(m_spellInfo->Id)) - { - if (m_caster->IsHostileTo(_target)) - return SPELL_FAILED_BAD_TARGETS; - } - else - { - bool duelvsplayertar = false; - for (int j = 0; j < MAX_EFFECT_INDEX; ++j) - { - //TARGET_DUELVSPLAYER is positive AND negative - duelvsplayertar |= (m_spellInfo->GetEffectImplicitTargetAByIndex(SpellEffectIndex(j)) == TARGET_DUELVSPLAYER); - } - if (m_caster->IsFriendlyTo(target) && !duelvsplayertar) - { - return SPELL_FAILED_BAD_TARGETS; - } - } - } - // cooldown - if (((Creature*)m_caster)->HasSpellCooldown(m_spellInfo->Id)) - return SPELL_FAILED_NOT_READY; - } - - return CheckCast(true); -} - -SpellCastResult Spell::CheckCasterAuras() const -{ - // Flag drop spells totally immuned to caster auras - // FIXME: find more nice check for all totally immuned spells - // HasAttribute(SPELL_ATTR_EX3_UNK28) ? - if (m_spellInfo->Id == 23336 || // Alliance Flag Drop - m_spellInfo->Id == 23334 || // Horde Flag Drop - m_spellInfo->Id == 34991) // Summon Netherstorm Flag - return SPELL_CAST_OK; - - uint8 school_immune = 0; - uint32 mechanic_immune = 0; - uint32 dispel_immune = 0; - - // Check if the spell grants school or mechanic immunity. - // We use bitmasks so the loop is done only once and not on every aura check below. - if (m_spellInfo->HasAttribute(SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY)) - { - for (int i = 0; i < MAX_EFFECT_INDEX; ++i) - { - SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(SpellEffectIndex(i)); - if(!spellEffect) - continue; - if (spellEffect->EffectApplyAuraName == SPELL_AURA_SCHOOL_IMMUNITY) - school_immune |= uint32(spellEffect->EffectMiscValue); - else if (spellEffect->EffectApplyAuraName == SPELL_AURA_MECHANIC_IMMUNITY) - mechanic_immune |= 1 << uint32(spellEffect->EffectMiscValue-1); - else if (spellEffect->EffectApplyAuraName == SPELL_AURA_MECHANIC_IMMUNITY_MASK) - mechanic_immune |= uint32(spellEffect->EffectMiscValue); - else if (spellEffect->EffectApplyAuraName == SPELL_AURA_DISPEL_IMMUNITY) - dispel_immune |= GetDispellMask(DispelType(spellEffect->EffectMiscValue)); - } - - // immune movement impairment and loss of control (spell data have special structure for mark this case) - if (IsSpellRemoveAllMovementAndControlLossEffects(m_spellInfo)) - mechanic_immune = IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; - } - - // Check whether the cast should be prevented by any state you might have. - SpellCastResult prevented_reason = SPELL_CAST_OK; - bool spellUsableWhileStunned = m_spellInfo->HasAttribute(SPELL_ATTR_EX5_USABLE_WHILE_STUNNED); - - // Have to check if there is a stun aura. Otherwise will have problems with ghost aura apply while logging out - uint32 unitflag = m_caster->GetUInt32Value(UNIT_FIELD_FLAGS); // Get unit state - if (unitflag & UNIT_FLAG_STUNNED) - { - // Pain Suppression (have SPELL_ATTR_EX5_USABLE_WHILE_STUNNED that must be used only with glyph) - if (m_spellInfo->GetSpellFamilyName() == SPELLFAMILY_PRIEST && m_spellInfo->SpellIconID == 2178) - { - if (!m_caster->HasAura(63248)) // Glyph of Pain Suppression - spellUsableWhileStunned = false; - } - - // spell is usable while stunned, check if caster has only mechanic stun auras, another stun types must prevent cast spell - if (spellUsableWhileStunned) - { - bool is_stun_mechanic = true; - Unit::AuraList const& stunAuras = m_caster->GetAurasByType(SPELL_AURA_MOD_STUN); - for (Unit::AuraList::const_iterator itr = stunAuras.begin(); itr != stunAuras.end(); ++itr) - if (!(*itr)->HasMechanic(MECHANIC_STUN)) - { - is_stun_mechanic = false; - break; - } - if (!is_stun_mechanic) - prevented_reason = SPELL_FAILED_STUNNED; - } - else - prevented_reason = SPELL_FAILED_STUNNED; - } - else if (unitflag & UNIT_FLAG_CONFUSED && !m_spellInfo->HasAttribute(SPELL_ATTR_EX5_USABLE_WHILE_CONFUSED)) - prevented_reason = SPELL_FAILED_CONFUSED; - else if (unitflag & UNIT_FLAG_FLEEING && !m_spellInfo->HasAttribute(SPELL_ATTR_EX5_USABLE_WHILE_FEARED)) - prevented_reason = SPELL_FAILED_FLEEING; - else if (unitflag & UNIT_FLAG_SILENCED && m_spellInfo->GetPreventionType() == SPELL_PREVENTION_TYPE_SILENCE) - prevented_reason = SPELL_FAILED_SILENCED; - else if (unitflag & UNIT_FLAG_PACIFIED && m_spellInfo->GetPreventionType() == SPELL_PREVENTION_TYPE_PACIFY) - prevented_reason = SPELL_FAILED_PACIFIED; - else if (m_caster->HasAuraType(SPELL_AURA_ALLOW_ONLY_ABILITY)) - { - Unit::AuraList const& casingLimit = m_caster->GetAurasByType(SPELL_AURA_ALLOW_ONLY_ABILITY); - for (Unit::AuraList::const_iterator itr = casingLimit.begin(); itr != casingLimit.end(); ++itr) - { - if (!(*itr)->isAffectedOnSpell(m_spellInfo)) - { - prevented_reason = SPELL_FAILED_CASTER_AURASTATE; - break; - } - } - } - - // Attr must make flag drop spell totally immune from all effects - if (prevented_reason != SPELL_CAST_OK) - { - if (school_immune || mechanic_immune || dispel_immune) - { - // Checking auras is needed now, because you are prevented by some state but the spell grants immunity. - Unit::SpellAuraHolderMap const& auras = m_caster->GetSpellAuraHolderMap(); - for (Unit::SpellAuraHolderMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) - { - SpellAuraHolder* holder = itr->second; - SpellEntry const* pEntry = holder->GetSpellProto(); - - if ((GetSpellSchoolMask(pEntry) & school_immune) && !pEntry->HasAttribute(SPELL_ATTR_EX_UNAFFECTED_BY_SCHOOL_IMMUNE)) - continue; - if ((1<<(pEntry->GetDispel())) & dispel_immune) - continue; - - for (int32 i = 0; i < MAX_EFFECT_INDEX; ++i) - { - Aura* aura = holder->GetAuraByEffectIndex(SpellEffectIndex(i)); - if (!aura) - continue; - - if (GetSpellMechanicMask(pEntry, 1 << i) & mechanic_immune) - continue; - // Make a second check for spell failed so the right SPELL_FAILED message is returned. - // That is needed when your casting is prevented by multiple states and you are only immune to some of them. - switch (aura->GetModifier()->m_auraname) - { - case SPELL_AURA_MOD_STUN: - if (!spellUsableWhileStunned || !aura->HasMechanic(MECHANIC_STUN)) - return SPELL_FAILED_STUNNED; - break; - case SPELL_AURA_MOD_CONFUSE: - if (!m_spellInfo->HasAttribute(SPELL_ATTR_EX5_USABLE_WHILE_CONFUSED)) - return SPELL_FAILED_CONFUSED; - break; - case SPELL_AURA_MOD_FEAR: - if (!m_spellInfo->HasAttribute(SPELL_ATTR_EX5_USABLE_WHILE_FEARED)) - return SPELL_FAILED_FLEEING; - break; - case SPELL_AURA_MOD_SILENCE: - case SPELL_AURA_MOD_PACIFY: - case SPELL_AURA_MOD_PACIFY_SILENCE: - if( m_spellInfo->GetPreventionType() == SPELL_PREVENTION_TYPE_PACIFY) - return SPELL_FAILED_PACIFIED; - else if ( m_spellInfo->GetPreventionType() == SPELL_PREVENTION_TYPE_SILENCE) - return SPELL_FAILED_SILENCED; - break; - default: break; - } - } - } - } - // You are prevented from casting and the spell casted does not grant immunity. Return a failed error. - else - return prevented_reason; - } - return SPELL_CAST_OK; -} - -bool Spell::CanAutoCast(Unit* target) -{ - ObjectGuid targetguid = target->GetObjectGuid(); - - for (int j = 0; j < MAX_EFFECT_INDEX; ++j) - { - SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(SpellEffectIndex(j)); - if(!spellEffect) - continue; - if(spellEffect->Effect == SPELL_EFFECT_APPLY_AURA) - { - if( m_spellInfo->GetStackAmount() <= 1) - { - if (target->HasAura(m_spellInfo->Id, SpellEffectIndex(j))) - return false; - } - else - { - if(Aura* aura = target->GetAura(m_spellInfo->Id, SpellEffectIndex(j))) - if(aura->GetStackAmount() >= m_spellInfo->GetStackAmount()) - return false; - } - } - else if ( IsAreaAuraEffect( spellEffect->Effect )) - { - if (target->HasAura(m_spellInfo->Id, SpellEffectIndex(j))) - return false; - } - } - - SpellCastResult result = CheckPetCast(target); - - if (result == SPELL_CAST_OK || result == SPELL_FAILED_UNIT_NOT_INFRONT) - { - FillTargetMap(); - // check if among target units, our WANTED target is as well (->only self cast spells return false) - for (TargetList::const_iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - if (ihit->targetGUID == targetguid) - return true; - } - return false; // target invalid -} - -SpellCastResult Spell::CheckRange(bool strict) -{ - Unit* target = m_targets.getUnitTarget(); - - // special range cases - switch (m_spellInfo->rangeIndex) - { - // self cast doesn't need range checking -- also for Starshards fix - // spells that can be cast anywhere also need no check - case SPELL_RANGE_IDX_SELF_ONLY: - case SPELL_RANGE_IDX_ANYWHERE: - return SPELL_CAST_OK; - // combat range spells are treated differently - case SPELL_RANGE_IDX_COMBAT: - { - if (target) - { - if (target == m_caster) - return SPELL_CAST_OK; - - float range_mod = strict ? 0.0f : 5.0f; - float base = ATTACK_DISTANCE; - if (Player* modOwner = m_caster->GetSpellModOwner()) - range_mod += modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, base, this); - - // with additional 5 dist for non stricted case (some melee spells have delay in apply - return m_caster->CanReachWithMeleeAttack(target, range_mod) ? SPELL_CAST_OK : SPELL_FAILED_OUT_OF_RANGE; - } - break; // let continue in generic way for no target - } - } - - // add radius of caster and ~5 yds "give" for non stricred (landing) check - float range_mod = strict ? 1.25f : 6.25; - - SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex); - bool friendly = target ? target->IsFriendlyTo(m_caster) : false; - float max_range = GetSpellMaxRange(srange, friendly) + range_mod; - float min_range = GetSpellMinRange(srange, friendly); - - if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, max_range, this); - - if (target && target != m_caster) - { - // distance from target in checks - float dist = m_caster->GetCombatDistance(target, m_spellInfo->rangeIndex == SPELL_RANGE_IDX_COMBAT); - - if (dist > max_range) - return SPELL_FAILED_OUT_OF_RANGE; - if (min_range && dist < min_range) - return SPELL_FAILED_TOO_CLOSE; - if( m_caster->GetTypeId() == TYPEID_PLAYER && - (m_spellInfo->GetFacingCasterFlags() & SPELL_FACING_FLAG_INFRONT) && !m_caster->HasInArc( M_PI_F, target ) ) - return SPELL_FAILED_UNIT_NOT_INFRONT; - } - - // TODO verify that such spells really use bounding radius - if (m_targets.m_targetMask == TARGET_FLAG_DEST_LOCATION && m_targets.m_destX != 0 && m_targets.m_destY != 0 && m_targets.m_destZ != 0) - { - if (!m_caster->IsWithinDist3d(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, max_range)) - return SPELL_FAILED_OUT_OF_RANGE; - if (min_range && m_caster->IsWithinDist3d(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, min_range)) - return SPELL_FAILED_TOO_CLOSE; - } - - return SPELL_CAST_OK; -} - -uint32 Spell::CalculatePowerCost(SpellEntry const* spellInfo, Unit* caster, Spell const* spell, Item* castItem) -{ - // item cast not used power - if (castItem) - return 0; - - // Spell drain all exist power on cast (Only paladin lay of Hands) - if (spellInfo->HasAttribute(SPELL_ATTR_EX_DRAIN_ALL_POWER)) - { - // If power type - health drain all - if (spellInfo->powerType == POWER_HEALTH) - return caster->GetHealth(); - // Else drain all power - if (spellInfo->powerType < MAX_POWERS) - return caster->GetPower(Powers(spellInfo->powerType)); - sLog.outError("Spell::CalculateManaCost: Unknown power type '%d' in spell %d", spellInfo->powerType, spellInfo->Id); - return 0; - } - - // Base powerCost - int32 powerCost = spellInfo->GetManaCost(); - // PCT cost from total amount - if (uint32 manaCostPct = spellInfo->GetManaCostPercentage()) - { - switch (spellInfo->powerType) - { - // health as power used - case POWER_HEALTH: - powerCost += manaCostPct * caster->GetCreateHealth() / 100; - break; - case POWER_MANA: - powerCost += manaCostPct * caster->GetCreateMana() / 100; - break; - case POWER_RAGE: - case POWER_FOCUS: - case POWER_ENERGY: - powerCost += manaCostPct * caster->GetMaxPower(Powers(spellInfo->powerType)) / 100; - break; - case POWER_RUNE: - case POWER_RUNIC_POWER: - DEBUG_LOG("Spell::CalculateManaCost: Not implemented yet!"); - break; - default: - sLog.outError("Spell::CalculateManaCost: Unknown power type '%d' in spell %d", spellInfo->powerType, spellInfo->Id); - return 0; - } - } - - SpellSchoolMask schoolMask = spell ? spell->m_spellSchoolMask : GetSpellSchoolMask(spellInfo); - // Flat mod from caster auras by spell school - Unit::AuraList const& pwrCostAuras = caster->GetAurasByType(SPELL_AURA_MOD_POWER_COST_SCHOOL); - for (Unit::AuraList::const_iterator itr = pwrCostAuras.begin(); itr != pwrCostAuras.end(); ++itr) - { - if (((*itr)->GetModifier()->m_miscvalue & schoolMask) && - (!(*itr)->GetSpellEffect()->EffectMiscValueB || (*itr)->GetSpellEffect()->EffectMiscValueB & (1 << spellInfo->powerType))) - powerCost += (*itr)->GetModifier()->m_amount; - } - - // Shiv - costs 20 + weaponSpeed*10 energy (apply only to non-triggered spell with energy cost) - if (spellInfo->HasAttribute(SPELL_ATTR_EX4_SPELL_VS_EXTEND_COST)) - powerCost += caster->GetAttackTime(OFF_ATTACK) / 100; - - // Apply cost mod by spell - if (spell) - if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COST, powerCost, spell); - - if (spellInfo->HasAttribute(SPELL_ATTR_LEVEL_DAMAGE_CALCULATION)) - powerCost = int32(powerCost / (1.117f * spellInfo->GetSpellLevel() / caster->getLevel() - 0.1327f)); - - // PCT mod from user auras by school - float pctCostMultiplier = 1.0f; - Unit::AuraList const& pwrCostPctAuras = caster->GetAurasByType(SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT); - for (Unit::AuraList::const_iterator itr = pwrCostPctAuras.begin(); itr != pwrCostPctAuras.end(); ++itr) - { - if (((*itr)->GetModifier()->m_miscvalue & schoolMask) && - (!(*itr)->GetSpellEffect()->EffectMiscValueB || (*itr)->GetSpellEffect()->EffectMiscValueB & (1 << spellInfo->powerType))) - pctCostMultiplier += (*itr)->GetModifier()->m_amount / 100.0f; - } - - // PCT mod from user auras by school - powerCost = int32(powerCost * pctCostMultiplier); - if (powerCost < 0) - powerCost = 0; - return powerCost; -} - -SpellCastResult Spell::CheckPower() -{ - // item cast not used power - if (m_CastItem) - return SPELL_CAST_OK; - - // Do precise power regen on spell cast - if (m_powerCost > 0 && m_caster->GetTypeId() == TYPEID_PLAYER) - { - Player* playerCaster = (Player*)m_caster; - uint32 diff = REGEN_TIME_FULL - m_caster->GetRegenTimer(); - if (diff >= REGEN_TIME_PRECISE) - playerCaster->RegenerateAll(diff); - } - - // health as power used - need check health amount - if (m_spellInfo->powerType == POWER_HEALTH) - { - if (m_caster->GetHealth() <= m_powerCost) - return SPELL_FAILED_CASTER_AURASTATE; - return SPELL_CAST_OK; - } - - // Check valid power type - if (m_spellInfo->powerType >= MAX_POWERS) - { - sLog.outError("Spell::CheckMana: Unknown power type '%d'", m_spellInfo->powerType); - return SPELL_FAILED_UNKNOWN; - } - - // check rune cost only if a spell has PowerType == POWER_RUNE - if (m_spellInfo->powerType == POWER_RUNE) - { - SpellCastResult failReason = CheckRunePower(); - if (failReason != SPELL_CAST_OK) - return failReason; - } - - // Check power amount - Powers powerType = Powers(m_spellInfo->powerType); - if (m_caster->GetPower(powerType) < m_powerCost) - return SPELL_FAILED_NO_POWER; - - return SPELL_CAST_OK; -} - -bool Spell::IgnoreItemRequirements() const -{ - /// Check if it's an enchant scroll. These have no required reagents even though their spell does. - if (m_CastItem && (m_CastItem->GetProto()->Flags & ITEM_FLAG_ENCHANT_SCROLL)) - return true; - - if (m_IsTriggeredSpell) - { - /// Not own traded item (in trader trade slot) req. reagents including triggered spell case - if (Item* targetItem = m_targets.getItemTarget()) - if (targetItem->GetOwnerGuid() != m_caster->GetObjectGuid()) - return false; - - /// Some triggered spells have same reagents that have master spell - /// expected in test: master spell have reagents in first slot then triggered don't must use own - if (m_triggeredBySpellInfo) - { - SpellReagentsEntry const* spellReagents = m_triggeredBySpellInfo->GetSpellReagents(); - if (!spellReagents || !spellReagents->Reagent[0]) - return false; - } - - return true; - } - - return false; -} - -SpellCastResult Spell::CheckItems() -{ - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return SPELL_CAST_OK; - - Player* p_caster = (Player*)m_caster; - bool isScrollItem = false; - bool isVellumTarget = false; - - // cast item checks - if (m_CastItem) - { - if (m_CastItem->IsInTrade()) - return SPELL_FAILED_ITEM_NOT_FOUND; - - uint32 itemid = m_CastItem->GetEntry(); - if (!p_caster->HasItemCount(itemid, 1)) - return SPELL_FAILED_ITEM_NOT_FOUND; - - ItemPrototype const* proto = m_CastItem->GetProto(); - if (!proto) - return SPELL_FAILED_ITEM_NOT_FOUND; - - if (proto->Flags & ITEM_FLAG_ENCHANT_SCROLL) - isScrollItem = true; - - for (int i = 0; i < 5; ++i) - if (proto->Spells[i].SpellCharges) - if (m_CastItem->GetSpellCharges(i) == 0) - return SPELL_FAILED_NO_CHARGES_REMAIN; - - // consumable cast item checks - if (proto->Class == ITEM_CLASS_CONSUMABLE && m_targets.getUnitTarget()) - { - // such items should only fail if there is no suitable effect at all - see Rejuvenation Potions for example - SpellCastResult failReason = SPELL_CAST_OK; - for (int i = 0; i < MAX_EFFECT_INDEX; ++i) - { - SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(SpellEffectIndex(i)); - if(!spellEffect) - continue; - // skip check, pet not required like checks, and for TARGET_PET m_targets.getUnitTarget() is not the real target but the caster - if (spellEffect->EffectImplicitTargetA == TARGET_PET) - continue; - - if (spellEffect->Effect == SPELL_EFFECT_HEAL) - { - if (m_targets.getUnitTarget()->GetHealth() == m_targets.getUnitTarget()->GetMaxHealth()) - { - failReason = SPELL_FAILED_ALREADY_AT_FULL_HEALTH; - continue; - } - else - { - failReason = SPELL_CAST_OK; - break; - } - } - - // Mana Potion, Rage Potion, Thistle Tea(Rogue), ... - if (spellEffect->Effect == SPELL_EFFECT_ENERGIZE) - { - if(spellEffect->EffectMiscValue < 0 || spellEffect->EffectMiscValue >= MAX_POWERS) - { - failReason = SPELL_FAILED_ALREADY_AT_FULL_POWER; - continue; - } - - Powers power = Powers(spellEffect->EffectMiscValue); - if (m_targets.getUnitTarget()->GetPower(power) == m_targets.getUnitTarget()->GetMaxPower(power)) - { - failReason = SPELL_FAILED_ALREADY_AT_FULL_POWER; - continue; - } - else - { - failReason = SPELL_CAST_OK; - break; - } - } - } - if (failReason != SPELL_CAST_OK) - return failReason; - } - } - - // check target item (for triggered case not report error) - if (m_targets.getItemTargetGuid()) - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return m_IsTriggeredSpell && !(m_targets.m_targetMask & TARGET_FLAG_TRADE_ITEM) - ? SPELL_FAILED_DONT_REPORT : SPELL_FAILED_BAD_TARGETS; - - if (!m_targets.getItemTarget()) - return m_IsTriggeredSpell && !(m_targets.m_targetMask & TARGET_FLAG_TRADE_ITEM) - ? SPELL_FAILED_DONT_REPORT : SPELL_FAILED_ITEM_GONE; - - isVellumTarget = m_targets.getItemTarget()->GetProto()->IsVellum(); - if (!m_targets.getItemTarget()->IsFitToSpellRequirements(m_spellInfo)) - return m_IsTriggeredSpell && !(m_targets.m_targetMask & TARGET_FLAG_TRADE_ITEM) - ? SPELL_FAILED_DONT_REPORT : SPELL_FAILED_EQUIPPED_ITEM_CLASS; - - // Do not enchant vellum with scroll - if (isVellumTarget && isScrollItem) - return m_IsTriggeredSpell && !(m_targets.m_targetMask & TARGET_FLAG_TRADE_ITEM) - ? SPELL_FAILED_DONT_REPORT : SPELL_FAILED_BAD_TARGETS; - } - // if not item target then required item must be equipped (for triggered case not report error) - else - { - if (m_caster->GetTypeId() == TYPEID_PLAYER && !((Player*)m_caster)->HasItemFitToSpellReqirements(m_spellInfo)) - return m_IsTriggeredSpell ? SPELL_FAILED_DONT_REPORT : SPELL_FAILED_EQUIPPED_ITEM_CLASS; - } - - // check reagents (ignore triggered spells with reagents processed by original spell) and special reagent ignore case. - if (!IgnoreItemRequirements()) - { - if (!p_caster->CanNoReagentCast(m_spellInfo)) - { - SpellReagentsEntry const* spellReagents = m_spellInfo->GetSpellReagents(); - if(spellReagents) - { - for(uint32 i = 0; i < MAX_SPELL_REAGENTS; ++i) - { - if(spellReagents->Reagent[i] <= 0) - continue; - - uint32 itemid = spellReagents->Reagent[i]; - uint32 itemcount = spellReagents->ReagentCount[i]; - - // if CastItem is also spell reagent - if (m_CastItem && m_CastItem->GetEntry() == itemid) - { - ItemPrototype const *proto = m_CastItem->GetProto(); - if (!proto) - return SPELL_FAILED_REAGENTS; - for(int s = 0; s < MAX_ITEM_PROTO_SPELLS; ++s) - { - // CastItem will be used up and does not count as reagent - int32 charges = m_CastItem->GetSpellCharges(s); - if (proto->Spells[s].SpellCharges < 0 && !(proto->ExtraFlags & ITEM_EXTRA_NON_CONSUMABLE) && abs(charges) < 2) - { - ++itemcount; - break; - } - } - } - - if (!p_caster->HasItemCount(itemid, itemcount)) - return SPELL_FAILED_REAGENTS; - } - } - } - - // check totem-item requirements (items presence in inventory) - SpellTotemsEntry const* spellTotems = m_spellInfo->GetSpellTotems(); - if(spellTotems) - { - uint32 totems = MAX_SPELL_TOTEMS; - for(int i = 0; i < MAX_SPELL_TOTEMS ; ++i) - { - if (spellTotems->Totem[i] != 0) - { - if (p_caster->HasItemCount(spellTotems->Totem[i], 1)) - { - totems -= 1; - continue; - } - } - else - totems -= 1; - } - - if (totems != 0) - return SPELL_FAILED_TOTEMS; - } - } - - // special checks for spell effects - for (int i = 0; i < MAX_EFFECT_INDEX; ++i) - { - SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(SpellEffectIndex(i)); - if(!spellEffect) - continue; - switch (spellEffect->Effect) - { - case SPELL_EFFECT_CREATE_ITEM: - { - if (!m_IsTriggeredSpell && spellEffect->EffectItemType) - { - // Conjure Mana Gem (skip same or low level ranks for later recharge) - if (i == EFFECT_INDEX_0 && m_spellInfo->GetSpellEffectIdByIndex(EFFECT_INDEX_1) == SPELL_EFFECT_DUMMY) - { - if (ItemPrototype const* itemProto = ObjectMgr::GetItemPrototype(spellEffect->EffectItemType)) - { - if (Item* item = p_caster->GetItemByLimitedCategory(itemProto->ItemLimitCategory)) - { - if (item->GetProto()->ItemLevel <= itemProto->ItemLevel) - { - if (item->HasMaxCharges()) - return SPELL_FAILED_ITEM_AT_MAX_CHARGES; - - // will recharge in next effect - continue; - } - } - } - } - - ItemPosCountVec dest; - InventoryResult msg = p_caster->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, spellEffect->EffectItemType, 1 ); - if (msg != EQUIP_ERR_OK ) - { - p_caster->SendEquipError( msg, NULL, NULL, spellEffect->EffectItemType ); - return SPELL_FAILED_DONT_REPORT; - } - } - break; - } - case SPELL_EFFECT_RESTORE_ITEM_CHARGES: - { - if (Item* item = p_caster->GetItemByEntry(spellEffect->EffectItemType)) - if (item->HasMaxCharges()) - return SPELL_FAILED_ITEM_AT_MAX_CHARGES; - - break; - } - case SPELL_EFFECT_ENCHANT_ITEM: - case SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC: - { - Item* targetItem = m_targets.getItemTarget(); - if (!targetItem) - return SPELL_FAILED_ITEM_NOT_FOUND; - - if( targetItem->GetProto()->ItemLevel < m_spellInfo->GetBaseLevel() ) - return SPELL_FAILED_LOWLEVEL; - // Check if we can store a new scroll, enchanting vellum has implicit SPELL_EFFECT_CREATE_ITEM - if (isVellumTarget && spellEffect->EffectItemType) - { - ItemPosCountVec dest; - InventoryResult msg = p_caster->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, spellEffect->EffectItemType, 1 ); - if (msg != EQUIP_ERR_OK) - { - p_caster->SendEquipError(msg, NULL, NULL); - return SPELL_FAILED_DONT_REPORT; - } - } - // Not allow enchant in trade slot for some enchant type - if (targetItem->GetOwner() != m_caster) - { - uint32 enchant_id = spellEffect->EffectMiscValue; - SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); - if(!pEnchant) - return SPELL_FAILED_ERROR; - if (pEnchant->slot & ENCHANTMENT_CAN_SOULBOUND) - return SPELL_FAILED_NOT_TRADEABLE; - // cannot replace vellum with scroll in trade slot - if (isVellumTarget) - return SPELL_FAILED_ITEM_ENCHANT_TRADE_WINDOW; - } - break; - } - case SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY: - { - Item* item = m_targets.getItemTarget(); - if (!item) - return SPELL_FAILED_ITEM_NOT_FOUND; - // Not allow enchant in trade slot for some enchant type - if (item->GetOwner() != m_caster) - { - uint32 enchant_id = spellEffect->EffectMiscValue; - SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); - if(!pEnchant) - return SPELL_FAILED_ERROR; - if (pEnchant->slot & ENCHANTMENT_CAN_SOULBOUND) - return SPELL_FAILED_NOT_TRADEABLE; - } - break; - } - case SPELL_EFFECT_ENCHANT_HELD_ITEM: - // check item existence in effect code (not output errors at offhand hold item effect to main hand for example - break; - case SPELL_EFFECT_DISENCHANT: - { - if (!m_targets.getItemTarget()) - return SPELL_FAILED_CANT_BE_DISENCHANTED; - - // prevent disenchanting in trade slot - if (m_targets.getItemTarget()->GetOwnerGuid() != m_caster->GetObjectGuid()) - return SPELL_FAILED_CANT_BE_DISENCHANTED; - - ItemPrototype const* itemProto = m_targets.getItemTarget()->GetProto(); - if (!itemProto) - return SPELL_FAILED_CANT_BE_DISENCHANTED; - - // must have disenchant loot (other static req. checked at item prototype loading) - if (!itemProto->DisenchantID) - return SPELL_FAILED_CANT_BE_DISENCHANTED; - - // 2.0.x addon: Check player enchanting level against the item disenchanting requirements - int32 item_disenchantskilllevel = itemProto->RequiredDisenchantSkill; - if (item_disenchantskilllevel > int32(p_caster->GetSkillValue(SKILL_ENCHANTING))) - return SPELL_FAILED_LOW_CASTLEVEL; - break; - } - case SPELL_EFFECT_PROSPECTING: - { - if (!m_targets.getItemTarget()) - return SPELL_FAILED_CANT_BE_PROSPECTED; - // ensure item is a prospectable ore - if (!(m_targets.getItemTarget()->GetProto()->Flags & ITEM_FLAG_PROSPECTABLE)) - return SPELL_FAILED_CANT_BE_PROSPECTED; - // prevent prospecting in trade slot - if (m_targets.getItemTarget()->GetOwnerGuid() != m_caster->GetObjectGuid()) - return SPELL_FAILED_CANT_BE_PROSPECTED; - // Check for enough skill in jewelcrafting - uint32 item_prospectingskilllevel = m_targets.getItemTarget()->GetProto()->RequiredSkillRank; - if (item_prospectingskilllevel > p_caster->GetSkillValue(SKILL_JEWELCRAFTING)) - return SPELL_FAILED_LOW_CASTLEVEL; - // make sure the player has the required ores in inventory - if (int32(m_targets.getItemTarget()->GetCount()) < CalculateDamage(SpellEffectIndex(i), m_caster)) - return SPELL_FAILED_NEED_MORE_ITEMS; - - if (!LootTemplates_Prospecting.HaveLootFor(m_targets.getItemTargetEntry())) - return SPELL_FAILED_CANT_BE_PROSPECTED; - - break; - } - case SPELL_EFFECT_MILLING: - { - if (!m_targets.getItemTarget()) - return SPELL_FAILED_CANT_BE_MILLED; - // ensure item is a millable herb - if (!(m_targets.getItemTarget()->GetProto()->Flags & ITEM_FLAG_MILLABLE)) - return SPELL_FAILED_CANT_BE_MILLED; - // prevent milling in trade slot - if (m_targets.getItemTarget()->GetOwnerGuid() != m_caster->GetObjectGuid()) - return SPELL_FAILED_CANT_BE_MILLED; - // Check for enough skill in inscription - uint32 item_millingskilllevel = m_targets.getItemTarget()->GetProto()->RequiredSkillRank; - if (item_millingskilllevel > p_caster->GetSkillValue(SKILL_INSCRIPTION)) - return SPELL_FAILED_LOW_CASTLEVEL; - // make sure the player has the required herbs in inventory - if (int32(m_targets.getItemTarget()->GetCount()) < CalculateDamage(SpellEffectIndex(i), m_caster)) - return SPELL_FAILED_NEED_MORE_ITEMS; - - if (!LootTemplates_Milling.HaveLootFor(m_targets.getItemTargetEntry())) - return SPELL_FAILED_CANT_BE_MILLED; - - break; - } - case SPELL_EFFECT_WEAPON_DAMAGE: - case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL: - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) return SPELL_FAILED_TARGET_NOT_PLAYER; - if (m_attackType != RANGED_ATTACK) - break; - Item* pItem = ((Player*)m_caster)->GetWeaponForAttack(m_attackType, true, false); - if (!pItem) - return SPELL_FAILED_EQUIPPED_ITEM; - - switch (pItem->GetProto()->SubClass) - { - case ITEM_SUBCLASS_WEAPON_THROWN: - { - uint32 ammo = pItem->GetEntry(); - if (!((Player*)m_caster)->HasItemCount(ammo, 1)) - return SPELL_FAILED_NO_AMMO; - }; break; - case ITEM_SUBCLASS_WEAPON_WAND: - break; - default: - break; - } - break; - } - default: break; - } - } - - return SPELL_CAST_OK; -} - -void Spell::Delayed() -{ - if (!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - if (m_spellState == SPELL_STATE_DELAYED) - return; // spell is active and can't be time-backed - - if (isDelayableNoMore()) // Spells may only be delayed twice - return; - - // spells not loosing casting time ( slam, dynamites, bombs.. ) - if(!(m_spellInfo->GetInterruptFlags() & SPELL_INTERRUPT_FLAG_DAMAGE)) - return; - - // check pushback reduce - int32 delaytime = 500; // spellcasting delay is normally 500ms - int32 delayReduce = 100; // must be initialized to 100 for percent modifiers - ((Player*)m_caster)->ApplySpellMod(m_spellInfo->Id, SPELLMOD_NOT_LOSE_CASTING_TIME, delayReduce, this); - delayReduce += m_caster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; - if (delayReduce >= 100) - return; - - delaytime = delaytime * (100 - delayReduce) / 100; - - if (int32(m_timer) + delaytime > m_casttime) - { - delaytime = m_casttime - m_timer; - m_timer = m_casttime; - } - else - m_timer += delaytime; - - DETAIL_FILTER_LOG(LOG_FILTER_SPELL_CAST, "Spell %u partially interrupted for (%d) ms at damage", m_spellInfo->Id, delaytime); - - WorldPacket data(SMSG_SPELL_DELAYED, 8 + 4); - data << m_caster->GetPackGUID(); - data << uint32(delaytime); - - m_caster->SendMessageToSet(&data, true); -} - -void Spell::DelayedChannel() -{ - if (!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER || getState() != SPELL_STATE_CASTING) - return; - - if (isDelayableNoMore()) // Spells may only be delayed twice - return; - - // check pushback reduce - int32 delaytime = GetSpellDuration(m_spellInfo) * 25 / 100;// channeling delay is normally 25% of its time per hit - int32 delayReduce = 100; // must be initialized to 100 for percent modifiers - ((Player*)m_caster)->ApplySpellMod(m_spellInfo->Id, SPELLMOD_NOT_LOSE_CASTING_TIME, delayReduce, this); - delayReduce += m_caster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; - if (delayReduce >= 100) - return; - - delaytime = delaytime * (100 - delayReduce) / 100; - - if (int32(m_timer) < delaytime) - { - delaytime = m_timer; - m_timer = 0; - } - else - m_timer -= delaytime; - - DEBUG_FILTER_LOG(LOG_FILTER_SPELL_CAST, "Spell %u partially interrupted for %i ms, new duration: %u ms", m_spellInfo->Id, delaytime, m_timer); - - for (TargetList::const_iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - { - if ((*ihit).missCondition == SPELL_MISS_NONE) - { - if (Unit* unit = m_caster->GetObjectGuid() == ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID)) - unit->DelaySpellAuraHolder(m_spellInfo->Id, delaytime, unit->GetObjectGuid()); - } - } - - for (int j = 0; j < MAX_EFFECT_INDEX; ++j) - { - // partially interrupt persistent area auras - if (DynamicObject* dynObj = m_caster->GetDynObject(m_spellInfo->Id, SpellEffectIndex(j))) - dynObj->Delay(delaytime); - } - - SendChannelUpdate(m_timer); -} - -void Spell::UpdateOriginalCasterPointer() -{ - if (m_originalCasterGUID == m_caster->GetObjectGuid()) - m_originalCaster = m_caster; - else if (m_originalCasterGUID.IsGameObject()) - { - GameObject* go = m_caster->IsInWorld() ? m_caster->GetMap()->GetGameObject(m_originalCasterGUID) : NULL; - m_originalCaster = go ? go->GetOwner() : NULL; - } - else - { - Unit* unit = ObjectAccessor::GetUnit(*m_caster, m_originalCasterGUID); - m_originalCaster = unit && unit->IsInWorld() ? unit : NULL; - } -} - -void Spell::UpdatePointers() -{ - UpdateOriginalCasterPointer(); - - m_targets.Update(m_caster); -} - -bool Spell::CheckTargetCreatureType(Unit* target) const -{ - uint32 spellCreatureTargetMask = m_spellInfo->GetTargetCreatureType(); - - // Curse of Doom: not find another way to fix spell target check :/ - if (m_spellInfo->GetSpellFamilyName() == SPELLFAMILY_WARLOCK && m_spellInfo->GetCategory() == 1179) - { - // not allow cast at player - if (target->GetTypeId() == TYPEID_PLAYER) - return false; - - spellCreatureTargetMask = 0x7FF; - } - - // Dismiss Pet, Taming Lesson and Control Robot skipped - if (m_spellInfo->Id == 2641 || m_spellInfo->Id == 23356 || m_spellInfo->Id == 30009) - spellCreatureTargetMask = 0; - - if (spellCreatureTargetMask) - { - uint32 TargetCreatureType = target->GetCreatureTypeMask(); - - return !TargetCreatureType || (spellCreatureTargetMask & TargetCreatureType); - } - return true; -} - -CurrentSpellTypes Spell::GetCurrentContainer() -{ - if (IsNextMeleeSwingSpell()) - return (CURRENT_MELEE_SPELL); - else if (IsAutoRepeat()) - return (CURRENT_AUTOREPEAT_SPELL); - else if (IsChanneledSpell(m_spellInfo)) - return (CURRENT_CHANNELED_SPELL); - else - return (CURRENT_GENERIC_SPELL); -} - -bool Spell::CheckTarget(Unit* target, SpellEffectIndex eff) -{ - SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(eff); - if(!spellEffect) - return false; - - // Check targets for creature type mask and remove not appropriate (skip explicit self target case, maybe need other explicit targets) - if(spellEffect->EffectImplicitTargetA != TARGET_SELF ) - { - if (!CheckTargetCreatureType(target)) - return false; - } - - // Check Aura spell req (need for AoE spells) - SpellAuraRestrictionsEntry const* auraRestrictions = m_spellInfo->GetSpellAuraRestrictions(); - if(auraRestrictions) - { - if(auraRestrictions->targetAuraSpell && !target->HasAura(auraRestrictions->targetAuraSpell)) - return false; - if (auraRestrictions->excludeTargetAuraSpell && target->HasAura(auraRestrictions->excludeTargetAuraSpell)) - return false; - } - - // Check targets for not_selectable unit flag and remove - // A player can cast spells on his pet (or other controlled unit) though in any state - if (target != m_caster && target->GetCharmerOrOwnerGuid() != m_caster->GetObjectGuid()) - { - // any unattackable target skipped - if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) - return false; - - // unselectable targets skipped in all cases except TARGET_SCRIPT targeting - // in case TARGET_SCRIPT target selected by server always and can't be cheated - if ((!m_IsTriggeredSpell || target != m_targets.getUnitTarget()) && - target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE) && - spellEffect->EffectImplicitTargetA != TARGET_SCRIPT && - spellEffect->EffectImplicitTargetB != TARGET_SCRIPT && - spellEffect->EffectImplicitTargetA != TARGET_AREAEFFECT_INSTANT && - spellEffect->EffectImplicitTargetB != TARGET_AREAEFFECT_INSTANT && - spellEffect->EffectImplicitTargetA != TARGET_AREAEFFECT_CUSTOM && - spellEffect->EffectImplicitTargetB != TARGET_AREAEFFECT_CUSTOM ) - return false; - } - - // Check player targets and remove if in GM mode or GM invisibility (for not self casting case) - if (target != m_caster && target->GetTypeId() == TYPEID_PLAYER) - { - if (((Player*)target)->GetVisibility() == VISIBILITY_OFF) - return false; - - if (((Player*)target)->isGameMaster() && !IsPositiveSpell(m_spellInfo->Id)) - return false; - } - - // Check targets for LOS visibility (except spells without range limitations ) - switch(spellEffect->Effect) - { - case SPELL_EFFECT_SUMMON_PLAYER: // from anywhere - break; - case SPELL_EFFECT_DUMMY: - if (m_spellInfo->Id != 20577) // Cannibalize - break; - // fall through - case SPELL_EFFECT_RESURRECT_NEW: - // player far away, maybe his corpse near? - if (target != m_caster && !m_spellInfo->HasAttribute(SPELL_ATTR_EX2_IGNORE_LOS) && !target->IsWithinLOSInMap(m_caster)) - { - if (!m_targets.getCorpseTargetGuid()) - return false; - - Corpse* corpse = m_caster->GetMap()->GetCorpse(m_targets.getCorpseTargetGuid()); - if (!corpse) - return false; - - if (target->GetObjectGuid() != corpse->GetOwnerGuid()) - return false; - - if (!m_spellInfo->HasAttribute(SPELL_ATTR_EX2_IGNORE_LOS) && !corpse->IsWithinLOSInMap(m_caster)) - return false; - } - - // all ok by some way or another, skip normal check - break; - default: // normal case - // Get GO cast coordinates if original caster -> GO - if (target != m_caster) - if (WorldObject* caster = GetCastingObject()) - if (!m_spellInfo->HasAttribute(SPELL_ATTR_EX2_IGNORE_LOS) && !target->IsWithinLOSInMap(caster)) - return false; - break; - } - - if (target->GetTypeId() != TYPEID_PLAYER && m_spellInfo->HasAttribute(SPELL_ATTR_EX3_TARGET_ONLY_PLAYER) - && spellEffect->EffectImplicitTargetA != TARGET_SCRIPT && spellEffect->EffectImplicitTargetA != TARGET_SELF) - return false; - - switch (m_spellInfo->Id) - { - case 37433: // Spout (The Lurker Below), only players affected if its not in water - if (target->GetTypeId() != TYPEID_PLAYER || target->IsInWater()) - return false; - break; - case 68921: // Soulstorm (FoS), only targets farer than 10 away - case 69049: // Soulstorm - = - - if (m_caster->IsWithinDist(target, 10.0f, false)) - return false; - break; - default: - break; - } - - return true; -} - -bool Spell::IsNeedSendToClient() const -{ - return m_spellInfo->SpellVisual[0] || m_spellInfo->SpellVisual[1] || IsChanneledSpell(m_spellInfo) || - m_spellInfo->speed > 0.0f || (!m_triggeredByAuraSpell && !m_IsTriggeredSpell); -} - -bool Spell::IsTriggeredSpellWithRedundentCastTime() const -{ - return m_IsTriggeredSpell && (m_spellInfo->GetManaCost() || m_spellInfo->GetManaCostPercentage()); -} - -bool Spell::HaveTargetsForEffect(SpellEffectIndex effect) const -{ - for (TargetList::const_iterator itr = m_UniqueTargetInfo.begin(); itr != m_UniqueTargetInfo.end(); ++itr) - if (itr->effectMask & (1 << effect)) - return true; - - for (GOTargetList::const_iterator itr = m_UniqueGOTargetInfo.begin(); itr != m_UniqueGOTargetInfo.end(); ++itr) - if (itr->effectMask & (1 << effect)) - return true; - - for (ItemTargetList::const_iterator itr = m_UniqueItemInfo.begin(); itr != m_UniqueItemInfo.end(); ++itr) - if (itr->effectMask & (1 << effect)) - return true; - - return false; -} - -SpellEvent::SpellEvent(Spell* spell) : BasicEvent() -{ - m_Spell = spell; -} - -SpellEvent::~SpellEvent() -{ - if (m_Spell->getState() != SPELL_STATE_FINISHED) - m_Spell->cancel(); - - if (m_Spell->IsDeletable()) - { - delete m_Spell; - } - else - { - sLog.outError("~SpellEvent: %s %u tried to delete non-deletable spell %u. Was not deleted, causes memory leak.", - (m_Spell->GetCaster()->GetTypeId() == TYPEID_PLAYER ? "Player" : "Creature"), m_Spell->GetCaster()->GetGUIDLow(), m_Spell->m_spellInfo->Id); - } -} - -bool SpellEvent::Execute(uint64 e_time, uint32 p_time) -{ - // update spell if it is not finished - if (m_Spell->getState() != SPELL_STATE_FINISHED) - m_Spell->update(p_time); - - // check spell state to process - switch (m_Spell->getState()) - { - case SPELL_STATE_FINISHED: - { - // spell was finished, check deletable state - if (m_Spell->IsDeletable()) - { - // check, if we do have unfinished triggered spells - return true; // spell is deletable, finish event - } - // event will be re-added automatically at the end of routine) - } break; - - case SPELL_STATE_CASTING: - { - // this spell is in channeled state, process it on the next update - // event will be re-added automatically at the end of routine) - } break; - - case SPELL_STATE_DELAYED: - { - // first, check, if we have just started - if (m_Spell->GetDelayStart() != 0) - { - // no, we aren't, do the typical update - // check, if we have channeled spell on our hands - if (IsChanneledSpell(m_Spell->m_spellInfo)) - { - // evented channeled spell is processed separately, casted once after delay, and not destroyed till finish - // check, if we have casting anything else except this channeled spell and autorepeat - if (m_Spell->GetCaster()->IsNonMeleeSpellCasted(false, true, true)) - { - // another non-melee non-delayed spell is casted now, abort - m_Spell->cancel(); - } - else - { - // do the action (pass spell to channeling state) - m_Spell->handle_immediate(); - } - // event will be re-added automatically at the end of routine) - } - else - { - // run the spell handler and think about what we can do next - uint64 t_offset = e_time - m_Spell->GetDelayStart(); - uint64 n_offset = m_Spell->handle_delayed(t_offset); - if (n_offset) - { - // re-add us to the queue - m_Spell->GetCaster()->m_Events.AddEvent(this, m_Spell->GetDelayStart() + n_offset, false); - return false; // event not complete - } - // event complete - // finish update event will be re-added automatically at the end of routine) - } - } - else - { - // delaying had just started, record the moment - m_Spell->SetDelayStart(e_time); - // re-plan the event for the delay moment - m_Spell->GetCaster()->m_Events.AddEvent(this, e_time + m_Spell->GetDelayMoment(), false); - return false; // event not complete - } - } break; - - default: - { - // all other states - // event will be re-added automatically at the end of routine) - } break; - } - - // spell processing not complete, plan event on the next update interval - m_Spell->GetCaster()->m_Events.AddEvent(this, e_time + 1, false); - return false; // event not complete -} - -void SpellEvent::Abort(uint64 /*e_time*/) -{ - // oops, the spell we try to do is aborted - if (m_Spell->getState() != SPELL_STATE_FINISHED) - m_Spell->cancel(); -} - -bool SpellEvent::IsDeletable() const -{ - return m_Spell->IsDeletable(); -} - -SpellCastResult Spell::CanOpenLock(SpellEffectIndex effIndex, uint32 lockId, SkillType& skillId, int32& reqSkillValue, int32& skillValue) -{ - if (!lockId) // possible case for GO and maybe for items. - return SPELL_CAST_OK; - - // Get LockInfo - LockEntry const* lockInfo = sLockStore.LookupEntry(lockId); - - if (!lockInfo) - return SPELL_FAILED_BAD_TARGETS; - - bool reqKey = false; // some locks not have reqs - - for (int j = 0; j < 8; ++j) - { - switch (lockInfo->Type[j]) - { - // check key item (many fit cases can be) - case LOCK_KEY_ITEM: - if (lockInfo->Index[j] && m_CastItem && m_CastItem->GetEntry() == lockInfo->Index[j]) - return SPELL_CAST_OK; - reqKey = true; - break; - // check key skill (only single first fit case can be) - case LOCK_KEY_SKILL: - { - reqKey = true; - - // wrong locktype, skip - if(uint32(m_spellInfo->GetEffectMiscValue(effIndex)) != lockInfo->Index[j]) - continue; - - skillId = SkillByLockType(LockType(lockInfo->Index[j])); - - if (skillId != SKILL_NONE || skillId == MAX_SKILL_TYPE) - { - // skill bonus provided by casting spell (mostly item spells) - // add the damage modifier from the spell casted (cheat lock / skeleton key etc.) (use m_currentBasePoints, CalculateDamage returns wrong value) - uint32 spellSkillBonus = uint32(m_currentBasePoints[effIndex]); - reqSkillValue = lockInfo->Skill[j]; - - // castitem check: rogue using skeleton keys. the skill values should not be added in this case. - // MAX_SKILL_TYPE - skill value scales with caster level - if (skillId == MAX_SKILL_TYPE) - skillValue = m_CastItem || m_caster->GetTypeId() != TYPEID_PLAYER ? 0 : m_caster->getLevel() * 5; - else - skillValue = m_CastItem || m_caster->GetTypeId() != TYPEID_PLAYER ? 0 : ((Player*)m_caster)->GetSkillValue(skillId); - - skillValue += spellSkillBonus; - - if (skillValue < reqSkillValue) - return SPELL_FAILED_LOW_CASTLEVEL; - } - - return SPELL_CAST_OK; - } - } - } - - if (reqKey) - return SPELL_FAILED_BAD_TARGETS; - - return SPELL_CAST_OK; -} - -/* - * Fill target list by units around (x,y) points at radius distance - - * @param targetUnitMap Reference to target list that filled by function - * @param x X coordinates of center point for target search - * @param y Y coordinates of center point for target search - * @param radius Radius around (x,y) for target search - * @param pushType Additional rules for target area selection (in front, angle, etc) - * @param spellTargets Additional rules for target selection base at hostile/friendly state to original spell caster - * @param originalCaster If provided set alternative original caster, if =NULL then used Spell::GetAffectiveObject() return - */ -void Spell::FillAreaTargets(UnitList& targetUnitMap, float radius, SpellNotifyPushType pushType, SpellTargets spellTargets, WorldObject* originalCaster /*=NULL*/) -{ - MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, targetUnitMap, radius, pushType, spellTargets, originalCaster); - Cell::VisitAllObjects(notifier.GetCenterX(), notifier.GetCenterY(), m_caster->GetMap(), notifier, radius); -} - -void Spell::FillRaidOrPartyTargets(UnitList& targetUnitMap, Unit* member, Unit* center, float radius, bool raid, bool withPets, bool withcaster) -{ - Player* pMember = member->GetCharmerOrOwnerPlayerOrPlayerItself(); - Group* pGroup = pMember ? pMember->GetGroup() : NULL; - - if (pGroup) - { - uint8 subgroup = pMember->GetSubGroup(); - - for (GroupReference* itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player* Target = itr->getSource(); - - // IsHostileTo check duel and controlled by enemy - if (Target && (raid || subgroup == Target->GetSubGroup()) - && !m_caster->IsHostileTo(Target)) - { - if ((Target == center || center->IsWithinDistInMap(Target, radius)) && - (withcaster || Target != m_caster)) - targetUnitMap.push_back(Target); - - if (withPets) - if (Pet* pet = Target->GetPet()) - if ((pet == center || center->IsWithinDistInMap(pet, radius)) && - (withcaster || pet != m_caster)) - targetUnitMap.push_back(pet); - } - } - } - else - { - Unit* ownerOrSelf = pMember ? pMember : member->GetCharmerOrOwnerOrSelf(); - if ((ownerOrSelf == center || center->IsWithinDistInMap(ownerOrSelf, radius)) && - (withcaster || ownerOrSelf != m_caster)) - targetUnitMap.push_back(ownerOrSelf); - - if (withPets) - if (Pet* pet = ownerOrSelf->GetPet()) - if ((pet == center || center->IsWithinDistInMap(pet, radius)) && - (withcaster || pet != m_caster)) - targetUnitMap.push_back(pet); - } -} - -void Spell::FillRaidOrPartyManaPriorityTargets(UnitList& targetUnitMap, Unit* member, Unit* center, float radius, uint32 count, bool raid, bool withPets, bool withCaster) -{ - FillRaidOrPartyTargets(targetUnitMap, member, center, radius, raid, withPets, withCaster); - - PrioritizeManaUnitQueue manaUsers; - for (UnitList::const_iterator itr = targetUnitMap.begin(); itr != targetUnitMap.end(); ++itr) - if ((*itr)->getPowerType() == POWER_MANA && !(*itr)->isDead()) - manaUsers.push(PrioritizeManaUnitWraper(*itr)); - - targetUnitMap.clear(); - while (!manaUsers.empty() && targetUnitMap.size() < count) - { - targetUnitMap.push_back(manaUsers.top().getUnit()); - manaUsers.pop(); - } -} - -void Spell::FillRaidOrPartyHealthPriorityTargets(UnitList& targetUnitMap, Unit* member, Unit* center, float radius, uint32 count, bool raid, bool withPets, bool withCaster) -{ - FillRaidOrPartyTargets(targetUnitMap, member, center, radius, raid, withPets, withCaster); - - PrioritizeHealthUnitQueue healthQueue; - for (UnitList::const_iterator itr = targetUnitMap.begin(); itr != targetUnitMap.end(); ++itr) - if (!(*itr)->isDead()) - healthQueue.push(PrioritizeHealthUnitWraper(*itr)); - - targetUnitMap.clear(); - while (!healthQueue.empty() && targetUnitMap.size() < count) - { - targetUnitMap.push_back(healthQueue.top().getUnit()); - healthQueue.pop(); - } -} - -WorldObject* Spell::GetAffectiveCasterObject() const -{ - if (!m_originalCasterGUID) - return m_caster; - - if (m_originalCasterGUID.IsGameObject() && m_caster->IsInWorld()) - return m_caster->GetMap()->GetGameObject(m_originalCasterGUID); - return m_originalCaster; -} - -WorldObject* Spell::GetCastingObject() const -{ - if (m_originalCasterGUID.IsGameObject()) - return m_caster->IsInWorld() ? m_caster->GetMap()->GetGameObject(m_originalCasterGUID) : NULL; - else - return m_caster; -} - -void Spell::ResetEffectDamageAndHeal() -{ - m_damage = 0; - m_healing = 0; -} - -void Spell::SelectMountByAreaAndSkill(Unit* target, SpellEntry const* parentSpell, uint32 spellId75, uint32 spellId150, uint32 spellId225, uint32 spellId300, uint32 spellIdSpecial) -{ - if (!target || target->GetTypeId() != TYPEID_PLAYER) - return; - - // Prevent stacking of mounts - target->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); - uint16 skillval = ((Player*)target)->GetSkillValue(SKILL_RIDING); - if (!skillval) - return; - - if (skillval >= 225 && (spellId300 > 0 || spellId225 > 0)) - { - uint32 spellid = skillval >= 300 ? spellId300 : spellId225; - SpellEntry const* pSpell = sSpellStore.LookupEntry(spellid); - if (!pSpell) - { - sLog.outError("SelectMountByAreaAndSkill: unknown spell id %i by caster: %s", spellid, target->GetGuidStr().c_str()); - return; - } - - // zone check - uint32 zone, area; - target->GetZoneAndAreaId(zone, area); - - SpellCastResult locRes = sSpellMgr.GetSpellAllowedInLocationError(pSpell, target->GetMapId(), zone, area, target->GetCharmerOrOwnerPlayerOrPlayerItself()); - if (locRes != SPELL_CAST_OK || !((Player*)target)->CanStartFlyInArea(target->GetMapId(), zone, area)) - target->CastSpell(target, spellId150, true, NULL, NULL, ObjectGuid(), parentSpell); - else if (spellIdSpecial > 0) - { - for (PlayerSpellMap::const_iterator iter = ((Player*)target)->GetSpellMap().begin(); iter != ((Player*)target)->GetSpellMap().end(); ++iter) - { - if (iter->second.state != PLAYERSPELL_REMOVED) - { - SpellEntry const* spellInfo = sSpellStore.LookupEntry(iter->first); - for (int i = 0; i < MAX_EFFECT_INDEX; ++i) - { - SpellEffectEntry const* spellEffect = spellInfo->GetSpellEffect(SpellEffectIndex(i)); - if(!spellEffect) - continue; - - if(spellEffect->EffectApplyAuraName == SPELL_AURA_MOD_FLIGHT_SPEED_MOUNTED) - { - int32 mountSpeed = spellInfo->CalculateSimpleValue(SpellEffectIndex(i)); - - // speed higher than 280 replace it - if (mountSpeed > 280) - { - target->CastSpell(target, spellIdSpecial, true, NULL, NULL, ObjectGuid(), parentSpell); - return; - } - } - } - } - } - target->CastSpell(target, pSpell, true, NULL, NULL, ObjectGuid(), parentSpell); - } - else - target->CastSpell(target, pSpell, true, NULL, NULL, ObjectGuid(), parentSpell); - } - else if (skillval >= 150 && spellId150 > 0) - target->CastSpell(target, spellId150, true, NULL, NULL, ObjectGuid(), parentSpell); - else if (spellId75 > 0) - target->CastSpell(target, spellId75, true, NULL, NULL, ObjectGuid(), parentSpell); - - return; -} - -void Spell::ClearCastItem() -{ - if (m_CastItem == m_targets.getItemTarget()) - m_targets.setItemTarget(NULL); - - m_CastItem = NULL; -} - -bool Spell::HasGlobalCooldown() -{ - // global cooldown have only player or controlled units - if (m_caster->GetCharmInfo()) - return m_caster->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(m_spellInfo); - else if (m_caster->GetTypeId() == TYPEID_PLAYER) - return ((Player*)m_caster)->GetGlobalCooldownMgr().HasGlobalCooldown(m_spellInfo); - else - return false; -} - -void Spell::TriggerGlobalCooldown() -{ - int32 gcd = m_spellInfo->GetStartRecoveryTime(); - if (!gcd) - return; - - // global cooldown can't leave range 1..1.5 secs (if it it) - // exist some spells (mostly not player directly casted) that have < 1 sec and > 1.5 sec global cooldowns - // but its as test show not affected any spell mods. - if (gcd >= 1000 && gcd <= 1500) - { - // gcd modifier auras applied only to self spells and only player have mods for this - if (m_caster->GetTypeId() == TYPEID_PLAYER) - ((Player*)m_caster)->ApplySpellMod(m_spellInfo->Id, SPELLMOD_GLOBAL_COOLDOWN, gcd, this); - - // apply haste rating - gcd = int32(float(gcd) * m_caster->GetFloatValue(UNIT_MOD_CAST_SPEED)); - - if (gcd < 1000) - gcd = 1000; - else if (gcd > 1500) - gcd = 1500; - } - - // global cooldown have only player or controlled units - if (m_caster->GetCharmInfo()) - m_caster->GetCharmInfo()->GetGlobalCooldownMgr().AddGlobalCooldown(m_spellInfo, gcd); - else if (m_caster->GetTypeId() == TYPEID_PLAYER) - ((Player*)m_caster)->GetGlobalCooldownMgr().AddGlobalCooldown(m_spellInfo, gcd); -} - -void Spell::CancelGlobalCooldown() -{ - if (!m_spellInfo->GetStartRecoveryTime()) - return; - - // cancel global cooldown when interrupting current cast - if (m_caster->GetCurrentSpell(CURRENT_GENERIC_SPELL) != this) - return; - - // global cooldown have only player or controlled units - if (m_caster->GetCharmInfo()) - m_caster->GetCharmInfo()->GetGlobalCooldownMgr().CancelGlobalCooldown(m_spellInfo); - else if (m_caster->GetTypeId() == TYPEID_PLAYER) - ((Player*)m_caster)->GetGlobalCooldownMgr().CancelGlobalCooldown(m_spellInfo); -} - -void Spell::GetSpellRangeAndRadius(SpellEffectEntry const* spellEffect, float& radius, uint32& EffectChainTarget, uint32& unMaxTargets) const -{ - if (uint32 radiusIndex = spellEffect->GetRadiusIndex()) - radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(spellEffect->GetRadiusIndex())); - else - radius = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)); - if (Unit* realCaster = GetAffectiveCaster()) - { - if (Player* modOwner = realCaster->GetSpellModOwner()) - { - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, radius, this); - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_JUMP_TARGETS, EffectChainTarget, this); - } - } - - // custom target amount cases - switch(m_spellInfo->GetSpellFamilyName()) - { - case SPELLFAMILY_GENERIC: - { - switch (m_spellInfo->Id) - { - case 802: // Mutate Bug (AQ40, Emperor Vek'nilash) - case 804: // Explode Bug (AQ40, Emperor Vek'lor) - case 23138: // Gate of Shazzrah (MC, Shazzrah) - case 28560: // Summon Blizzard (Naxx, Sapphiron) - case 30541: // Blaze (Magtheridon) - case 30572: // Quake (Magtheridon) - case 30769: // Pick Red Riding Hood (Karazhan, Big Bad Wolf) - case 30835: // Infernal Relay (Karazhan, Prince Malchezaar) - case 31347: // Doom (Hyjal Summit, Azgalor) - case 32312: // Move 1 (Karazhan, Chess Event) - case 33711: // Murmur's Touch (Shadow Labyrinth, Murmur) - case 37388: // Move 2 (Karazhan, Chess Event) - case 38794: // Murmur's Touch (h) (Shadow Labyrinth, Murmur) - case 39338: // Karazhan - Chess, Medivh CHEAT: Hand of Medivh, Target Horde - case 39342: // Karazhan - Chess, Medivh CHEAT: Hand of Medivh, Target Alliance - case 40834: // Agonizing Flames (BT, Illidan Stormrage) - case 41537: // Summon Enslaved Soul (BT, Reliquary of Souls) - case 44869: // Spectral Blast (SWP, Kalecgos) - case 45391: // Summon Demonic Vapor (SWP, Felmyst) - case 45785: // Sinister Reflection Clone (SWP, Kil'jaeden) - case 45892: // Sinister Reflection (SWP, Kil'jaeden) - case 45976: // Open Portal (SWP, M'uru) - case 46372: // Ice Spear Target Picker (Slave Pens, Ahune) - case 47669: // Awaken Subboss (Utgarde Pinnacle) - case 48278: // Paralyze (Utgarde Pinnacle) - case 50742: // Ooze Combine (Halls of Stone) - case 50988: // Glare of the Tribunal (Halls of Stone) - case 51003: // Summon Dark Matter Target (Halls of Stone) - case 51146: // Summon Searing Gaze Target (Halls Of Stone) - case 52438: // Summon Skittering Swarmer (Azjol Nerub, Krik'thir the Gatewatcher) - case 52449: // Summon Skittering Infector (Azjol Nerub, Krik'thir the Gatewatcher) - case 53457: // Impale (Azjol Nerub, Anub'arak) - case 54148: // Ritual of the Sword (Utgarde Pinnacle, Svala) - case 55479: // Forced Obedience (Naxxramas, Razovius) - case 56140: // Summon Power Spark (Eye of Eternity, Malygos) - case 57578: // Lava Strike (Obsidian Sanctum, Sartharion) - case 59870: // Glare of the Tribunal (h) (Halls of Stone) - case 62016: // Charge Orb (Ulduar, Thorim) - case 62042: // Stormhammer (Ulduar, Thorim) - case 62166: // Stone Grip (Ulduar, Kologarn) - case 62301: // Cosmic Smash (Ulduar, Algalon) - case 62374: // Pursued (Ulduar, Flame Leviathan) - case 62488: // Activate Construct (Ulduar, Ignis) - case 62577: // Blizzard (Ulduar, Thorim) - case 62603: // Blizzard (h) (Ulduar, Thorim) - case 62797: // Storm Cloud (Ulduar, Hodir) - case 63018: // Searing Light (Ulduar, XT-002) - case 63024: // Gravity Bomb (Ulduar, XT-002) - case 63387: // Rapid Burst - case 63545: // Icicle (Ulduar, Hodir) - case 63795: // Psychosis (Ulduar, Yogg-Saron) - case 63820: // Summon Scrap Bot Trigger (Ulduar, Mimiron) use for Scrap Bots, hits npc 33856 - case 64218: // Overcharge (VoA, Emalon) - case 64234: // Gravity Bomb (h) (Ulduar, XT-002) - case 64425: // Summon Scrap Bot Trigger (Ulduar, Mimiron) use for Assault Bots, hits npc 33856 - case 64531: // Rapid Burst (h) - case 64543: // Melt Ice (Ulduar, Hodir) - case 65121: // Searing Light (h) (Ulduar, XT-002) - case 65301: // Psychosis (Ulduar, Yogg-Saron) - case 65872: // Pursuing Spikes (ToCrusader, Anub'arak) - case 65950: // Touch of Light (ToCrusader, Val'kyr Twins) - case 66001: // Touch of Darkness (ToCrusader, Val'kyr Twins) - case 66152: // Bullet Controller Summon Periodic Trigger Light (ToCrusader) - case 66153: // Bullet Controller Summon Periodic Trigger Dark (ToCrusader) - case 66332: // Nerubian Burrower (Mode 0) (ToCrusader, Anub'arak) - case 66336: // Mistress' Kiss (ToCrusader, Jaraxxus) - case 66339: // Summon Scarab (ToCrusader, Anub'arak) - case 67077: // Mistress' Kiss (Mode 2) (ToCrusader, Jaraxxus) - case 67281: // Touch of Darkness (Mode 1) - case 67282: // Touch of Darkness (Mode 2) - case 67283: // Touch of Darkness (Mode 3) - case 67296: // Touch of Light (Mode 1) - case 67297: // Touch of Light (Mode 2) - case 67298: // Touch of Light (Mode 3) - case 68912: // Wailing Souls (FoS) - case 68950: // Fear (FoS) - case 68987: // Pursuit (PoS) - case 69048: // Mirrored Soul (FoS) - case 69057: // Bone Spike Graveyard (Icecrown Citadel, Lord Marrowgar) 10 man - case 72088: - case 73142: - case 73144: - case 69140: // Coldflame (ICC, Marrowgar) - case 69674: // Mutated Infection (ICC, Rotface) - case 70450: // Blood Mirror - case 70837: // Blood Mirror - case 70882: // Slime Spray Summon Trigger (ICC, Rotface) - case 70920: // Unbound Plague Search Effect (ICC, Putricide) - case 71224: // Mutated Infection (Mode 1) - case 71445: // Twilight Bloodbolt - case 71471: // Twilight Bloodbolt - case 71837: // Vampiric Bite - case 71861: // Swarming Shadows - case 72091: // Frozen Orb (Vault of Archavon, Toravon) - case 72254: // Mark of Fallen Champion (target selection) (ICC, Deathbringer Saurfang) - case 73022: // Mutated Infection (Mode 2) - case 73023: // Mutated Infection (Mode 3) - unMaxTargets = 1; - break; - case 10258: // Awaken Vault Warder (Uldaman) - case 28542: // Life Drain (Naxx, Sapphiron) - case 62476: // Icicle (Ulduar, Hodir) - case 66013: // Penetrating Cold (10 man) (ToCrusader, Anub'arak) - case 67755: // Nerubian Burrower (Mode 1) (ToCrusader, Anub'arak) - case 67756: // Nerubian Burrower (Mode 2) (ToCrusader, Anub'arak) - case 68509: // Penetrating Cold (10 man heroic) - case 69055: // Bone Slice (ICC, Lord Marrowgar) - case 69278: // Gas spore (ICC, Festergut) - case 70341: // Slime Puddle (ICC, Putricide) - case 71336: // Pact of the Darkfallen - case 71390: // Pact of the Darkfallen - unMaxTargets = 2; - break; - case 28796: // Poison Bolt Volley (Naxx, Faerlina) - case 29213: // Curse of the Plaguebringer (Naxx, Noth the Plaguebringer) - case 30004: // Flame Wreath (Karazhan, Shade of Aran) - case 31298: // Sleep (Hyjal Summit, Anetheron) - case 39341: // Karazhan - Chess, Medivh CHEAT: Fury of Medivh, Target Horde - case 39344: // Karazhan - Chess, Medivh CHEAT: Fury of Medivh, Target Alliance - case 39992: // Needle Spine Targeting (BT, Warlord Najentus) - case 40869: // Fatal Attraction (BT, Mother Shahraz) - case 41303: // Soul Drain (BT, Reliquary of Souls) - case 41376: // Spite (BT, Reliquary of Souls) - case 51904: // Summon Ghouls On Scarlet Crusade - case 54522: // Summon Ghouls On Scarlet Crusade - case 60936: // Surge of Power (h) (Malygos) - case 61693: // Arcane Storm (Malygos) - case 62477: // Icicle (h) (Ulduar, Hodir) - case 63981: // StoneGrip (h) (Ulduar, Kologarn) - case 64598: // Cosmic Smash (h) (Ulduar, Algalon) - case 64620: // Summon Fire Bot Trigger (Ulduar, Mimiron) hits npc 33856 - case 70814: // Bone Slice (ICC, Lord Marrowgar, heroic) - case 72095: // Frozen Orb (h) (Vault of Archavon, Toravon) - case 72089: // Bone Spike Graveyard (Icecrown Citadel, Lord Marrowgar) 25 man - case 70826: - case 73143: - case 73145: - unMaxTargets = 3; - break; - case 37676: // Insidious Whisper (SSC, Leotheras the Blind) - case 38028: // Watery Grave (SSC, Morogrim Tidewalker) - case 46650: // Open Brutallus Back Door (SWP, Felmyst) - case 67757: // Nerubian Burrower (Mode 3) (ToCrusader, Anub'arak) - case 71221: // Gas spore (Mode 1) (ICC, Festergut) - unMaxTargets = 4; - break; - case 30843: // Enfeeble (Karazhan, Prince Malchezaar) - case 40243: // Crushing Shadows (BT, Teron Gorefiend) - case 42005: // Bloodboil (BT, Gurtogg Bloodboil) - case 45641: // Fire Bloom (SWP, Kil'jaeden) - case 55665: // Life Drain (h) (Naxx, Sapphiron) - case 58917: // Consume Minions - case 64604: // Nature Bomb (Ulduar, Freya) - case 67076: // Mistress' Kiss (Mode 1) (ToCrusader, Jaraxxus) - case 67078: // Mistress' Kiss (Mode 3) (ToCrusader, Jaraxxus) - case 67700: // Penetrating Cold (25 man) - case 68510: // Penetrating Cold (25 man, heroic) - unMaxTargets = 5; - break; - case 61694: // Arcane Storm (h) (Malygos) - unMaxTargets = 7; - break; - case 38054: // Random Rocket Missile - unMaxTargets = 8; - break; - case 54098: // Poison Bolt Volley (h) (Naxx, Faerlina) - case 54835: // Curse of the Plaguebringer (h) (Naxx, Noth the Plaguebringer) - unMaxTargets = 10; - break; - case 25991: // Poison Bolt Volley (AQ40, Pincess Huhuran) - unMaxTargets = 15; - break; - case 61916: // Lightning Whirl (Ulduar, Stormcaller Brundir) - unMaxTargets = urand(2, 3); - break; - case 46771: // Flame Sear (SWP, Grand Warlock Alythess) - unMaxTargets = urand(3, 5); - break; - case 63482: // Lightning Whirl (h) (Ulduar, Stormcaller Brundir) - unMaxTargets = urand(3, 6); - break; - case 74452: // Conflagration (Saviana, Ruby Sanctum) - { - if (m_caster) - { - switch (m_caster->GetMap()->GetDifficulty()) - { - case RAID_DIFFICULTY_10MAN_NORMAL: - case RAID_DIFFICULTY_10MAN_HEROIC: - unMaxTargets = 2; - break; - case RAID_DIFFICULTY_25MAN_NORMAL: - case RAID_DIFFICULTY_25MAN_HEROIC: - unMaxTargets = 5; - break; - } - } - break; - } - default: - break; - } - break; - } - case SPELLFAMILY_MAGE: - { - if (m_spellInfo->Id == 38194) // Blink - unMaxTargets = 1; - break; - } - case SPELLFAMILY_WARRIOR: - { - // Sunder Armor (main spell) - if (m_spellInfo->IsFitToFamilyMask(UI64LIT(0x0000000000004000), 0x00000000) && m_spellInfo->SpellVisual[0] == 406) - if (m_caster->HasAura(58387)) // Glyph of Sunder Armor - EffectChainTarget = 2; - break; - } - case SPELLFAMILY_DRUID: - { - // Starfall - if (m_spellInfo->IsFitToFamilyMask(UI64LIT(0x0000000000000000), 0x00000100)) - unMaxTargets = 2; - break; - } - case SPELLFAMILY_DEATHKNIGHT: - { - if (m_spellInfo->SpellIconID == 1737) // Corpse Explosion // TODO - spell 50445? - unMaxTargets = 1; - break; - } - case SPELLFAMILY_PALADIN: - if (m_spellInfo->Id == 20424) // Seal of Command (2 more target for single targeted spell) - { - // overwrite EffectChainTarget for non single target spell - if (Spell* currSpell = m_caster->GetCurrentSpell(CURRENT_GENERIC_SPELL)) - { - for(int i = 0; i < MAX_EFFECT_INDEX; ++i) - if(SpellEffectEntry const* spellEffect = currSpell->m_spellInfo->GetSpellEffect(SpellEffectIndex(i))) - if(spellEffect->EffectChainTarget > 0) - EffectChainTarget = 0; // no chain targets - if (currSpell->m_spellInfo->GetMaxAffectedTargets() > 0) - EffectChainTarget = 0; // no chain targets - } - } - break; - default: - break; - } - - // custom radius cases - switch (m_spellInfo->GetSpellFamilyName()) - { - case SPELLFAMILY_GENERIC: - { - switch (m_spellInfo->Id) - { - case 24811: // Draw Spirit (Lethon) - { - if (spellEffect->EffectIndex == EFFECT_INDEX_0) // Copy range from EFF_1 to 0 - radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(spellEffect->GetRadiusIndex())); - break; - } - case 28241: // Poison (Naxxramas, Grobbulus Cloud) - case 54363: // Poison (Naxxramas, Grobbulus Cloud) (H) - { - uint32 auraId = (m_spellInfo->Id == 28241 ? 28158 : 54362); - if (SpellAuraHolder* auraHolder = m_caster->GetSpellAuraHolder(auraId)) - radius = 0.5f * (60000 - auraHolder->GetAuraDuration()) * 0.001f; - break; - } - case 66881: // Slime Pool (ToCrusader, Acidmaw & Dreadscale) - case 67638: // Slime Pool (ToCrusader, Acidmaw & Dreadscale) (Mode 1) - case 67639: // Slime Pool (ToCrusader, Acidmaw & Dreadscale) (Mode 2) - case 67640: // Slime Pool (ToCrusader, Acidmaw & Dreadscale) (Mode 3) - if (SpellAuraHolder* auraHolder = m_caster->GetSpellAuraHolder(66882)) - radius = 0.5f * (60000 - auraHolder->GetAuraDuration()) * 0.001f; - break; - case 56438: // Arcane Overload - if (Unit* realCaster = GetAffectiveCaster()) - radius = radius * realCaster->GetObjectScale(); - break; - case 69057: // Bone Spike Graveyard (Icecrown Citadel, Lord Marrowgar encounter, 10N) - case 70826: // Bone Spike Graveyard (Icecrown Citadel, Lord Marrowgar encounter, 25N) - case 72088: // Bone Spike Graveyard (Icecrown Citadel, Lord Marrowgar encounter, 10H) - case 72089: // Bone Spike Graveyard (Icecrown Citadel, Lord Marrowgar encounter, 25H) - case 73142: // Bone Spike Graveyard (during Bone Storm) (Icecrown Citadel, Lord Marrowgar encounter, 10N) - case 73143: // Bone Spike Graveyard (during Bone Storm) (Icecrown Citadel, Lord Marrowgar encounter, 25N) - case 73144: // Bone Spike Graveyard (during Bone Storm) (Icecrown Citadel, Lord Marrowgar encounter, 10H) - case 73145: // Bone Spike Graveyard (during Bone Storm) (Icecrown Citadel, Lord Marrowgar encounter, 25H) - radius = DEFAULT_VISIBILITY_INSTANCE; - break; - default: - break; - } - break; - } - case SPELLFAMILY_DRUID: - { - switch (m_spellInfo->Id) - { - case 49376: // Feral Charge - Cat - // No default radius for this spell, so we need to use the contact distance - radius = CONTACT_DISTANCE; - break; - } - } - default: - break; - } -} diff --git a/src/game/Spell.h b/src/game/Spell.h deleted file mode 100644 index 31532b7ad..000000000 --- a/src/game/Spell.h +++ /dev/null @@ -1,937 +0,0 @@ -/** - * This code is part of MaNGOS. Contributor & Copyright details are in AUTHORS/THANKS. - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __SPELL_H -#define __SPELL_H - -#include "Common.h" -#include "GridDefines.h" -#include "SharedDefines.h" -#include "DBCEnums.h" -#include "ObjectGuid.h" -#include "LootMgr.h" -#include "Unit.h" -#include "Player.h" - -class WorldSession; -class WorldPacket; -class DynamicObj; -class Item; -class GameObject; -class Group; -class Aura; - -enum SpellCastFlags -{ - CAST_FLAG_NONE = 0x00000000, - CAST_FLAG_HIDDEN_COMBATLOG = 0x00000001, // hide in combat log? - CAST_FLAG_UNKNOWN2 = 0x00000002, - CAST_FLAG_UNKNOWN3 = 0x00000004, - CAST_FLAG_UNKNOWN4 = 0x00000008, - CAST_FLAG_UNKNOWN5 = 0x00000010, - CAST_FLAG_AMMO = 0x00000020, // Projectiles visual - CAST_FLAG_UNKNOWN7 = 0x00000040, // !0x41 mask used to call CGTradeSkillInfo::DoRecast - CAST_FLAG_UNKNOWN8 = 0x00000080, - CAST_FLAG_UNKNOWN9 = 0x00000100, - CAST_FLAG_UNKNOWN10 = 0x00000200, - CAST_FLAG_UNKNOWN11 = 0x00000400, - CAST_FLAG_PREDICTED_POWER = 0x00000800, // wotlk, trigger rune cooldown - CAST_FLAG_UNKNOWN13 = 0x00001000, - CAST_FLAG_UNKNOWN14 = 0x00002000, - CAST_FLAG_UNKNOWN15 = 0x00004000, - CAST_FLAG_UNKNOWN16 = 0x00008000, - CAST_FLAG_UNKNOWN17 = 0x00010000, - CAST_FLAG_ADJUST_MISSILE = 0x00020000, // wotlk - CAST_FLAG_UNKNOWN19 = 0x00040000, // spell cooldown related (may be category cooldown) - CAST_FLAG_VISUAL_CHAIN = 0x00080000, // wotlk - CAST_FLAG_UNKNOWN21 = 0x00100000, - CAST_FLAG_PREDICTED_RUNES = 0x00200000, // wotlk, rune cooldown list - CAST_FLAG_IMMUNITY = 0x04000000, // spell cast school imminity info - CAST_FLAG_UNKNOWN24 = 0x08000000, - CAST_FLAG_UNKNOWN25 = 0x10000000, - CAST_FLAG_UNKNOWN26 = 0x20000000, - CAST_FLAG_HEAL_PREDICTION = 0x40000000, // heal prediction - CAST_FLAG_UNKNOWN28 = 0x80000000, -}; - -enum SpellFlags -{ - SPELL_FLAG_NORMAL = 0x00, - SPELL_FLAG_REFLECTED = 0x01, // reflected spell - SPELL_FLAG_REDIRECTED = 0x02 // redirected spell -}; - -enum SpellNotifyPushType -{ - PUSH_IN_FRONT, - PUSH_IN_FRONT_90, - PUSH_IN_FRONT_30, - PUSH_IN_FRONT_15, - PUSH_IN_BACK, - PUSH_SELF_CENTER, - PUSH_DEST_CENTER, - PUSH_TARGET_CENTER -}; - -bool IsQuestTameSpell(uint32 spellId); - -namespace MaNGOS -{ - struct SpellNotifierPlayer; - struct SpellNotifierCreatureAndPlayer; -} - -class SpellCastTargets; - -struct SpellCastTargetsReader -{ - explicit SpellCastTargetsReader(SpellCastTargets& _targets, Unit* _caster) : targets(_targets), caster(_caster) {} - - SpellCastTargets& targets; - Unit* caster; -}; - -class SpellCastTargets -{ - public: - SpellCastTargets(); - ~SpellCastTargets(); - - void read(ByteBuffer& data, Unit* caster); - void write(ByteBuffer& data) const; - - SpellCastTargetsReader ReadForCaster(Unit* caster) { return SpellCastTargetsReader(*this, caster); } - void ReadAdditionalData(WorldPacket& data, uint8& cast_flags); - - SpellCastTargets& operator=(const SpellCastTargets& target) - { - m_unitTarget = target.m_unitTarget; - m_itemTarget = target.m_itemTarget; - m_GOTarget = target.m_GOTarget; - - m_unitTargetGUID = target.m_unitTargetGUID; - m_GOTargetGUID = target.m_GOTargetGUID; - m_CorpseTargetGUID = target.m_CorpseTargetGUID; - m_itemTargetGUID = target.m_itemTargetGUID; - m_srcTransportGUID = target.m_srcTransportGUID; - m_destTransportGUID = target.m_destTransportGUID; - - m_itemTargetEntry = target.m_itemTargetEntry; - - m_srcX = target.m_srcX; - m_srcY = target.m_srcY; - m_srcZ = target.m_srcZ; - - m_destX = target.m_destX; - m_destY = target.m_destY; - m_destZ = target.m_destZ; - - m_strTarget = target.m_strTarget; - - m_targetMask = target.m_targetMask; - - m_elevation = target.m_elevation; - m_speed = target.m_speed; - - return *this; - } - - void setUnitTarget(Unit* target); - ObjectGuid getUnitTargetGuid() const { return m_unitTargetGUID; } - Unit* getUnitTarget() const { return m_unitTarget; } - - void setDestination(float x, float y, float z); - void setSource(float x, float y, float z); - void getDestination(float& x, float& y, float& z) const { x = m_destX; y = m_destY; z = m_destZ; } - void getSource(float& x, float& y, float& z) const { x = m_srcX; y = m_srcY, z = m_srcZ; } - - void setGOTarget(GameObject* target); - ObjectGuid getGOTargetGuid() const { return m_GOTargetGUID; } - GameObject* getGOTarget() const { return m_GOTarget; } - - void setCorpseTarget(Corpse* corpse); - ObjectGuid getCorpseTargetGuid() const { return m_CorpseTargetGUID; } - - void setItemTarget(Item* item); - ObjectGuid getItemTargetGuid() const { return m_itemTargetGUID; } - Item* getItemTarget() const { return m_itemTarget; } - uint32 getItemTargetEntry() const { return m_itemTargetEntry; } - - void setTradeItemTarget(Player* caster); - - void updateTradeSlotItem() - { - if (m_itemTarget && (m_targetMask & TARGET_FLAG_TRADE_ITEM)) - { - m_itemTargetGUID = m_itemTarget->GetObjectGuid(); - m_itemTargetEntry = m_itemTarget->GetEntry(); - } - } - - bool IsEmpty() const { return !m_GOTargetGUID && !m_unitTargetGUID && !m_itemTarget && !m_CorpseTargetGUID; } - - void Update(Unit* caster); - - float m_srcX, m_srcY, m_srcZ; - float m_destX, m_destY, m_destZ; - std::string m_strTarget; - - float GetElevation() const { return m_elevation; } - float GetSpeed() const { return m_speed; } - - uint32 m_targetMask; - - private: - // objects (can be used at spell creating and after Update at casting - Unit* m_unitTarget; - GameObject* m_GOTarget; - Item* m_itemTarget; - - // object GUID/etc, can be used always - ObjectGuid m_unitTargetGUID; - ObjectGuid m_GOTargetGUID; - ObjectGuid m_CorpseTargetGUID; - ObjectGuid m_itemTargetGUID; - ObjectGuid m_srcTransportGUID; - ObjectGuid m_destTransportGUID; - uint32 m_itemTargetEntry; - - float m_elevation; - float m_speed; -}; - -inline ByteBuffer& operator<< (ByteBuffer& buf, SpellCastTargets const& targets) -{ - targets.write(buf); - return buf; -} - -inline ByteBuffer& operator>> (ByteBuffer& buf, SpellCastTargetsReader const& targets) -{ - targets.targets.read(buf, targets.caster); - return buf; -} - -enum SpellState -{ - SPELL_STATE_PREPARING = 0, // cast time delay period, non channeled spell - SPELL_STATE_CASTING = 1, // channeled time period spell casting state - SPELL_STATE_FINISHED = 2, // cast finished to success or fail - SPELL_STATE_DELAYED = 3 // spell casted but need time to hit target(s) -}; - -enum SpellTargets -{ - SPELL_TARGETS_HOSTILE, - SPELL_TARGETS_NOT_FRIENDLY, - SPELL_TARGETS_NOT_HOSTILE, - SPELL_TARGETS_FRIENDLY, - SPELL_TARGETS_AOE_DAMAGE, - SPELL_TARGETS_ALL -}; - -typedef std::multimap SpellTargetTimeMap; - -class Spell -{ - friend struct MaNGOS::SpellNotifierPlayer; - friend struct MaNGOS::SpellNotifierCreatureAndPlayer; - friend void Unit::SetCurrentCastedSpell(Spell* pSpell); - - public: - void EffectEmpty(SpellEffectEntry const* effect); - void EffectNULL(SpellEffectEntry const* effect); - void EffectUnused(SpellEffectEntry const* effect); - void EffectDistract(SpellEffectEntry const* effect); - void EffectPull(SpellEffectEntry const* effect); - void EffectSchoolDMG(SpellEffectEntry const* effect); - void EffectEnvironmentalDMG(SpellEffectEntry const* effect); - void EffectInstaKill(SpellEffectEntry const* effect); - void EffectDummy(SpellEffectEntry const* effect); - void EffectTeleportUnits(SpellEffectEntry const* effect); - void EffectApplyAura(SpellEffectEntry const* effect); - void EffectSendEvent(SpellEffectEntry const* effect); - void EffectPowerBurn(SpellEffectEntry const* effect); - void EffectPowerDrain(SpellEffectEntry const* effect); - void EffectHeal(SpellEffectEntry const* effect); - void EffectBind(SpellEffectEntry const* effect); - void EffectHealthLeech(SpellEffectEntry const* effect); - void EffectQuestComplete(SpellEffectEntry const* effect); - void EffectCreateItem(SpellEffectEntry const* effect); - void EffectCreateItem2(SpellEffectEntry const* effect); - void EffectCreateRandomItem(SpellEffectEntry const* effect); - void EffectPersistentAA(SpellEffectEntry const* effect); - void EffectEnergize(SpellEffectEntry const* effect); - void EffectOpenLock(SpellEffectEntry const* effect); - void EffectSummonChangeItem(SpellEffectEntry const* effect); - void EffectProficiency(SpellEffectEntry const* effect); - void EffectApplyAreaAura(SpellEffectEntry const* effect); - void EffectSummonType(SpellEffectEntry const* effect); - void EffectLearnSpell(SpellEffectEntry const* effect); - void EffectDispel(SpellEffectEntry const* effect); - void EffectDualWield(SpellEffectEntry const* effect); - void EffectPickPocket(SpellEffectEntry const* effect); - void EffectAddFarsight(SpellEffectEntry const* effect); - void EffectHealMechanical(SpellEffectEntry const* effect); - void EffectJump(SpellEffectEntry const* effect); - void EffectTeleUnitsFaceCaster(SpellEffectEntry const* effect); - void EffectLearnSkill(SpellEffectEntry const* effect); - void EffectTradeSkill(SpellEffectEntry const* effect); - void EffectEnchantItemPerm(SpellEffectEntry const* effect); - void EffectEnchantItemTmp(SpellEffectEntry const* effect); - void EffectTameCreature(SpellEffectEntry const* effect); - void EffectSummonPet(SpellEffectEntry const* effect); - void EffectLearnPetSpell(SpellEffectEntry const* effect); - void EffectWeaponDmg(SpellEffectEntry const* effect); - void EffectClearQuest(SpellEffectEntry const* effect); - void EffectForceCast(SpellEffectEntry const* effect); - void EffectTriggerSpell(SpellEffectEntry const* effect); - void EffectTriggerMissileSpell(SpellEffectEntry const* effect); - void EffectThreat(SpellEffectEntry const* effect); - void EffectRestoreItemCharges(SpellEffectEntry const* effect); - void EffectHealMaxHealth(SpellEffectEntry const* effect); - void EffectInterruptCast(SpellEffectEntry const* effect); - void EffectSummonObjectWild(SpellEffectEntry const* effect); - void EffectScriptEffect(SpellEffectEntry const* effect); - void EffectSanctuary(SpellEffectEntry const* effect); - void EffectAddComboPoints(SpellEffectEntry const* effect); - void EffectDuel(SpellEffectEntry const* effect); - void EffectStuck(SpellEffectEntry const* effect); - void EffectSummonPlayer(SpellEffectEntry const* effect); - void EffectActivateObject(SpellEffectEntry const* effect); - void EffectApplyGlyph(SpellEffectEntry const* effect); - void EffectEnchantHeldItem(SpellEffectEntry const* effect); - void EffectSummonObject(SpellEffectEntry const* effect); - void EffectResurrect(SpellEffectEntry const* effect); - void EffectParry(SpellEffectEntry const* effect); - void EffectBlock(SpellEffectEntry const* effect); - void EffectLeapForward(SpellEffectEntry const* effect); - void EffectLeapBack(SpellEffectEntry const* effect); - void EffectTransmitted(SpellEffectEntry const* effect); - void EffectDisEnchant(SpellEffectEntry const* effect); - void EffectInebriate(SpellEffectEntry const* effect); - void EffectFeedPet(SpellEffectEntry const* effect); - void EffectDismissPet(SpellEffectEntry const* effect); - void EffectReputation(SpellEffectEntry const* effect); - void EffectSelfResurrect(SpellEffectEntry const* effect); - void EffectSkinning(SpellEffectEntry const* effect); - void EffectCharge(SpellEffectEntry const* effect); - void EffectCharge2(SpellEffectEntry const* effect); - void EffectProspecting(SpellEffectEntry const* effect); - void EffectRedirectThreat(SpellEffectEntry const* effect); - void EffectMilling(SpellEffectEntry const* effect); - void EffectRenamePet(SpellEffectEntry const* effect); - void EffectSendTaxi(SpellEffectEntry const* effect); - void EffectKnockBack(SpellEffectEntry const* effect); - void EffectPlayerPull(SpellEffectEntry const* effect); - void EffectDispelMechanic(SpellEffectEntry const* effect); - void EffectSummonDeadPet(SpellEffectEntry const* effect); - void EffectSummonAllTotems(SpellEffectEntry const* effect); - void EffectBreakPlayerTargeting (SpellEffectEntry const* effect); - void EffectDestroyAllTotems(SpellEffectEntry const* effect); - void EffectDurabilityDamage(SpellEffectEntry const* effect); - void EffectSkill(SpellEffectEntry const* effect); - void EffectTaunt(SpellEffectEntry const* effect); - void EffectDurabilityDamagePCT(SpellEffectEntry const* effect); - void EffectModifyThreatPercent(SpellEffectEntry const* effect); - void EffectResurrectNew(SpellEffectEntry const* effect); - void EffectAddExtraAttacks(SpellEffectEntry const* effect); - void EffectSpiritHeal(SpellEffectEntry const* effect); - void EffectSkinPlayerCorpse(SpellEffectEntry const* effect); - void EffectStealBeneficialBuff(SpellEffectEntry const* effect); - void EffectUnlearnSpecialization(SpellEffectEntry const* effect); - void EffectHealPct(SpellEffectEntry const* effect); - void EffectEnergisePct(SpellEffectEntry const* effect); - void EffectTriggerSpellWithValue(SpellEffectEntry const* effect); - void EffectTriggerRitualOfSummoning(SpellEffectEntry const* effect); - void EffectKillCreditPersonal(SpellEffectEntry const* effect); - void EffectKillCreditGroup(SpellEffectEntry const* effect); - void EffectQuestFail(SpellEffectEntry const* effect); - void EffectQuestOffer(SpellEffectEntry const* effect); - void EffectActivateRune(SpellEffectEntry const* effect); - void EffectTeachTaxiNode(SpellEffectEntry const* effect); - void EffectWMODamage(SpellEffectEntry const* effect); - void EffectWMORepair(SpellEffectEntry const* effect); - void EffectWMOChange(SpellEffectEntry const* effect); - void EffectTitanGrip(SpellEffectEntry const* effect); - void EffectEnchantItemPrismatic(SpellEffectEntry const* effect); - void EffectPlaySound(SpellEffectEntry const* effect); - void EffectPlayMusic(SpellEffectEntry const* effect); - void EffectSpecCount(SpellEffectEntry const* effect); - void EffectActivateSpec(SpellEffectEntry const* effect); - void EffectCancelAura(SpellEffectEntry const* effect); - void EffectKnockBackFromPosition(SpellEffectEntry const* effect); - - Spell(Unit* caster, SpellEntry const* info, bool triggered, ObjectGuid originalCasterGUID = ObjectGuid(), SpellEntry const* triggeredBy = NULL); - ~Spell(); - - void prepare(SpellCastTargets const* targets, Aura* triggeredByAura = NULL); - - void cancel(); - - void update(uint32 difftime); - void cast(bool skipCheck = false); - void finish(bool ok = true); - void TakePower(); - void TakeRunePower(bool hit); - void TakeAmmo(); - void TakeReagents(); - void TakeCastItem(); - - SpellCastResult CheckCast(bool strict); - SpellCastResult CheckPetCast(Unit* target); - - // handlers - void handle_immediate(); - uint64 handle_delayed(uint64 t_offset); - // handler helpers - void _handle_immediate_phase(); - void _handle_finish_phase(); - - SpellCastResult CheckItems(); - SpellCastResult CheckRange(bool strict); - SpellCastResult CheckPower(); - SpellCastResult CheckRunePower(); - SpellCastResult CheckCasterAuras() const; - - int32 CalculateDamage(SpellEffectIndex i, Unit* target) { return m_caster->CalculateSpellDamage(target, m_spellInfo, i, &m_currentBasePoints[i]); } - static uint32 CalculatePowerCost(SpellEntry const* spellInfo, Unit* caster, Spell const* spell = NULL, Item* castItem = NULL); - - bool HaveTargetsForEffect(SpellEffectIndex effect) const; - void Delayed(); - void DelayedChannel(); - uint32 getState() const { return m_spellState; } - void setState(uint32 state) { m_spellState = state; } - - void DoCreateItem(SpellEffectEntry const* effect, uint32 itemtype); - - void WriteSpellGoTargets(WorldPacket* data); - void WriteAmmoToPacket(WorldPacket* data); - - template WorldObject* FindCorpseUsing(); - - bool CheckTarget(Unit* target, SpellEffectIndex eff); - bool CanAutoCast(Unit* target); - - static void MANGOS_DLL_SPEC SendCastResult(Player* caster, SpellEntry const* spellInfo, uint8 cast_count, SpellCastResult result, bool isPetCastResult = false); - void SendCastResult(SpellCastResult result); - void SendSpellStart(); - void SendSpellGo(); - void SendSpellCooldown(); - void SendLogExecute(); - void SendInterrupted(uint8 result); - void SendChannelUpdate(uint32 time); - void SendChannelStart(uint32 duration); - void SendResurrectRequest(Player* target); - void SendPlaySpellVisual(uint32 SpellID); - - void HandleEffects(Unit* pUnitTarget, Item* pItemTarget, GameObject* pGOTarget, SpellEffectIndex i, float DamageMultiplier = 1.0); - void HandleThreatSpells(); - // void HandleAddAura(Unit* Target); - - SpellEntry const* m_spellInfo; - SpellEntry const* m_triggeredBySpellInfo; - SpellInterruptsEntry const* m_spellInterrupts; - int32 m_currentBasePoints[MAX_EFFECT_INDEX]; // cache SpellEntry::CalculateSimpleValue and use for set custom base points - Item* m_CastItem; - uint8 m_cast_count; - uint32 m_glyphIndex; - SpellCastTargets m_targets; - - int32 GetCastTime() const { return m_casttime; } - uint32 GetCastedTime() { return m_timer; } - bool IsAutoRepeat() const { return m_autoRepeat; } - void SetAutoRepeat(bool rep) { m_autoRepeat = rep; } - void ReSetTimer() { m_timer = m_casttime > 0 ? m_casttime : 0; } - bool IsNextMeleeSwingSpell() const - { - return m_spellInfo->HasAttribute(SPELL_ATTR_ON_NEXT_SWING_1) || m_spellInfo->HasAttribute(SPELL_ATTR_ON_NEXT_SWING_2); - } - bool IsRangedSpell() const - { - return m_spellInfo->HasAttribute(SPELL_ATTR_RANGED); - } - bool IsChannelActive() const { return m_caster->GetUInt32Value(UNIT_CHANNEL_SPELL) != 0; } - bool IsMeleeAttackResetSpell() const { return !m_IsTriggeredSpell && m_spellInterrupts && (m_spellInterrupts->InterruptFlags & SPELL_INTERRUPT_FLAG_AUTOATTACK); } - bool IsRangedAttackResetSpell() const { return !m_IsTriggeredSpell && IsRangedSpell() && m_spellInterrupts && (m_spellInterrupts->InterruptFlags & SPELL_INTERRUPT_FLAG_AUTOATTACK); } - - bool IsDeletable() const { return !m_referencedFromCurrentSpell && !m_executedCurrently; } - void SetReferencedFromCurrent(bool yes) { m_referencedFromCurrentSpell = yes; } - void SetExecutedCurrently(bool yes) { m_executedCurrently = yes; } - uint64 GetDelayStart() const { return m_delayStart; } - void SetDelayStart(uint64 m_time) { m_delayStart = m_time; } - uint64 GetDelayMoment() const { return m_delayMoment; } - - bool IsNeedSendToClient() const; // use for hide spell cast for client in case when cast not have client side affect (animation or log entries) - bool IsTriggeredSpellWithRedundentCastTime() const; // use for ignore some spell data for triggered spells like cast time, some triggered spells have redundent copy data from main spell for client use purpose - - CurrentSpellTypes GetCurrentContainer(); - - // caster types: - // formal spell caster, in game source of spell affects cast - Unit* GetCaster() const { return m_caster; } - // real source of cast affects, explicit caster, or DoT/HoT applier, or GO owner, or wild GO itself. Can be NULL - WorldObject* GetAffectiveCasterObject() const; - // limited version returning NULL in cases wild gameobject caster object, need for Aura (auras currently not support non-Unit caster) - Unit* GetAffectiveCaster() const { return m_originalCasterGUID ? m_originalCaster : m_caster; } - // m_originalCasterGUID can store GO guid, and in this case this is visual caster - WorldObject* GetCastingObject() const; - - uint32 GetPowerCost() const { return m_powerCost; } - uint32 GetUsedHolyPower() const { return m_usedHolyPower; } - - void UpdatePointers(); // must be used at call Spell code after time delay (non triggered spell cast/update spell call/etc) - - bool CheckTargetCreatureType(Unit* target) const; - - void AddTriggeredSpell(SpellEntry const* spellInfo) { m_TriggerSpells.push_back(spellInfo); } - void AddPrecastSpell(SpellEntry const* spellInfo) { m_preCastSpells.push_back(spellInfo); } - void AddTriggeredSpell(uint32 spellId); - void AddPrecastSpell(uint32 spellId); - void CastPreCastSpells(Unit* target); - void CastTriggerSpells(); - - void CleanupTargetList(); - void ClearCastItem(); - - static void SelectMountByAreaAndSkill(Unit* target, SpellEntry const* parentSpell, uint32 spellId75, uint32 spellId150, uint32 spellId225, uint32 spellId300, uint32 spellIdSpecial); - - typedef std::list UnitList; - - protected: - bool HasGlobalCooldown(); - void TriggerGlobalCooldown(); - void CancelGlobalCooldown(); - - void SendLoot(ObjectGuid guid, LootType loottype, LockType lockType); - bool IgnoreItemRequirements() const; // some item use spells have unexpected reagent data - void UpdateOriginalCasterPointer(); - - Unit* m_caster; - - ObjectGuid m_originalCasterGUID; // real source of cast (aura caster/etc), used for spell targets selection - // e.g. damage around area spell trigered by victim aura and da,age emeies of aura caster - Unit* m_originalCaster; // cached pointer for m_originalCaster, updated at Spell::UpdatePointers() - - Spell** m_selfContainer; // pointer to our spell container (if applicable) - - // Spell data - SpellSchoolMask m_spellSchoolMask; // Spell school (can be overwrite for some spells (wand shoot for example) - WeaponAttackType m_attackType; // For weapon based attack - uint32 m_powerCost; // Calculated spell cost initialized only in Spell::prepare - uint32 m_usedHolyPower; - int32 m_casttime; // Calculated spell cast time initialized only in Spell::prepare - int32 m_duration; - bool m_canReflect; // can reflect this spell? - uint8 m_spellFlags; // for spells whose target was changed in cast i.e. due to reflect - bool m_autoRepeat; - uint8 m_runesState; - - uint8 m_delayAtDamageCount; - bool isDelayableNoMore() - { - if (m_delayAtDamageCount >= 2) - return true; - - ++m_delayAtDamageCount; - return false; - } - - // Delayed spells system - uint64 m_delayStart; // time of spell delay start, filled by event handler, zero = just started - uint64 m_delayMoment; // moment of next delay call, used internally - bool m_immediateHandled; // were immediate actions handled? (used by delayed spells only) - - // These vars are used in both delayed spell system and modified immediate spell system - bool m_referencedFromCurrentSpell; // mark as references to prevent deleted and access by dead pointers - bool m_executedCurrently; // mark as executed to prevent deleted and access by dead pointers - bool m_needSpellLog; // need to send spell log? - uint8 m_applyMultiplierMask; // by effect: damage multiplier needed? - float m_damageMultipliers[3]; // by effect: damage multiplier - - // Current targets, to be used in SpellEffects (MUST BE USED ONLY IN SPELL EFFECTS) - Unit* unitTarget; - Item* itemTarget; - GameObject* gameObjTarget; - SpellAuraHolder* m_spellAuraHolder; // spell aura holder for current target, created only if spell has aura applying effect - int32 damage; - - // this is set in Spell Hit, but used in Apply Aura handler - DiminishingLevels m_diminishLevel; - DiminishingGroup m_diminishGroup; - - // ------------------------------------------- - GameObject* focusObject; - - // Damage and healing in effects need just calculate - int32 m_damage; // Damage in effects count here - int32 m_healing; // Healing in effects count here - int32 m_healthLeech; // Health leech in effects for all targets count here - - //****************************************** - // Spell trigger system - //****************************************** - bool m_canTrigger; // Can start trigger (m_IsTriggeredSpell can`t use for this) - uint8 m_negativeEffectMask; // Use for avoid sent negative spell procs for additional positive effects only targets - uint32 m_procAttacker; // Attacker trigger flags - uint32 m_procVictim; // Victim trigger flags - void prepareDataForTriggerSystem(); - - //***************************************** - // Spell target filling - //***************************************** - - void FillTargetMap(); - void SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& targetUnitMap); - - void FillAreaTargets(UnitList& targetUnitMap, float radius, SpellNotifyPushType pushType, SpellTargets spellTargets, WorldObject* originalCaster = NULL); - void FillRaidOrPartyTargets(UnitList& targetUnitMap, Unit* member, Unit* center, float radius, bool raid, bool withPets, bool withcaster); - void FillRaidOrPartyManaPriorityTargets(UnitList& targetUnitMap, Unit* member, Unit* center, float radius, uint32 count, bool raid, bool withPets, bool withcaster); - void FillRaidOrPartyHealthPriorityTargets(UnitList& targetUnitMap, Unit* member, Unit* center, float radius, uint32 count, bool raid, bool withPets, bool withcaster); - - // Returns a target that was filled by SPELL_SCRIPT_TARGET (or selected victim) Can return NULL - Unit* GetPrefilledUnitTargetOrUnitTarget(SpellEffectIndex effIndex) const; - void GetSpellRangeAndRadius(SpellEffectEntry const* spellEffect, float& radius, uint32& EffectChainTarget, uint32& unMaxTargets) const; - - //***************************************** - // Spell target subsystem - //***************************************** - // Targets store structures and data - struct TargetInfo - { - ObjectGuid targetGUID; - uint64 timeDelay; - uint32 HitInfo; - uint32 damage; - SpellMissInfo missCondition: 8; - SpellMissInfo reflectResult: 8; - uint8 effectMask: 8; - bool processed: 1; - }; - uint8 m_needAliveTargetMask; // Mask req. alive targets - - struct GOTargetInfo - { - ObjectGuid targetGUID; - uint64 timeDelay; - uint8 effectMask: 8; - bool processed: 1; - }; - - struct ItemTargetInfo - { - Item* item; - uint8 effectMask; - }; - - typedef std::list TargetList; - typedef std::list GOTargetList; - typedef std::list ItemTargetList; - - TargetList m_UniqueTargetInfo; - GOTargetList m_UniqueGOTargetInfo; - ItemTargetList m_UniqueItemInfo; - - void AddUnitTarget(Unit* target, SpellEffectIndex effIndex); - void AddUnitTarget(ObjectGuid unitGuid, SpellEffectIndex effIndex); - void AddGOTarget(GameObject* target, SpellEffectIndex effIndex); - void AddGOTarget(ObjectGuid goGuid, SpellEffectIndex effIndex); - void AddItemTarget(Item* target, SpellEffectIndex effIndex); - void DoAllEffectOnTarget(TargetInfo* target); - void HandleDelayedSpellLaunch(TargetInfo* target); - void InitializeDamageMultipliers(); - void ResetEffectDamageAndHeal(); - void DoSpellHitOnUnit(Unit* unit, uint32 effectMask); - void DoAllEffectOnTarget(GOTargetInfo* target); - void DoAllEffectOnTarget(ItemTargetInfo* target); - bool IsAliveUnitPresentInTargetList(); - SpellCastResult CanOpenLock(SpellEffectIndex effIndex, uint32 lockid, SkillType& skillid, int32& reqSkillValue, int32& skillValue); - // ------------------------------------------- - - // List For Triggered Spells - typedef std::list SpellInfoList; - SpellInfoList m_TriggerSpells; // casted by caster to same targets settings in m_targets at success finish of current spell - SpellInfoList m_preCastSpells; // casted by caster to each target at spell hit before spell effects apply - - uint32 m_spellState; - uint32 m_timer; - - float m_castPositionX; - float m_castPositionY; - float m_castPositionZ; - float m_castOrientation; - bool m_IsTriggeredSpell; - - // if need this can be replaced by Aura copy - // we can't store original aura link to prevent access to deleted auras - // and in same time need aura data and after aura deleting. - SpellEntry const* m_triggeredByAuraSpell; - - private: - // NPC Summonings - struct CreaturePosition - { - CreaturePosition() : - x(0.0f), y(0.0f), z(0.0f), - creature(NULL) - {} - - float x, y, z; - Creature* creature; - }; - typedef std::vector CreatureSummonPositions; - - // return true IFF further processing required - bool DoSummonPet(SpellEffectEntry const* effect); - bool DoSummonTotem(SpellEffectEntry const* effect, uint8 slot_dbc = 0); - bool DoSummonWild(CreatureSummonPositions& list, SummonPropertiesEntry const* prop, SpellEffectEntry const* effect, uint32 level); - bool DoSummonCritter(CreatureSummonPositions& list, SummonPropertiesEntry const* prop, SpellEffectEntry const* effect, uint32 level); - bool DoSummonGuardian(CreatureSummonPositions& list, SummonPropertiesEntry const* prop, SpellEffectEntry const* effect, uint32 level); - bool DoSummonPossessed(CreatureSummonPositions& list, SummonPropertiesEntry const* prop, SpellEffectEntry const* effect, uint32 level); - bool DoSummonVehicle(CreatureSummonPositions& list, SummonPropertiesEntry const* prop, SpellEffectEntry const* effect, uint32 level); -}; - -enum ReplenishType -{ - REPLENISH_UNDEFINED = 0, - REPLENISH_HEALTH = 20, - REPLENISH_MANA = 21, - REPLENISH_RAGE = 22 -}; - -namespace MaNGOS -{ - struct MANGOS_DLL_DECL SpellNotifierPlayer // Currently unused. When put to use this one requires handling for source-location (smilar to below) - { - Spell::UnitList& i_data; - Spell& i_spell; - const uint32& i_index; - float i_radius; - WorldObject* i_originalCaster; - - SpellNotifierPlayer(Spell& spell, Spell::UnitList& data, const uint32& i, float radius) - : i_data(data), i_spell(spell), i_index(i), i_radius(radius) - { - i_originalCaster = i_spell.GetAffectiveCasterObject(); - } - - void Visit(PlayerMapType& m) - { - if (!i_originalCaster) - return; - - for (PlayerMapType::iterator itr = m.begin(); itr != m.end(); ++itr) - { - Player* pPlayer = itr->getSource(); - if (!pPlayer->isAlive() || pPlayer->IsTaxiFlying()) - continue; - - if (i_originalCaster->IsFriendlyTo(pPlayer)) - continue; - - if (pPlayer->IsWithinDist3d(i_spell.m_targets.m_destX, i_spell.m_targets.m_destY, i_spell.m_targets.m_destZ, i_radius)) - i_data.push_back(pPlayer); - } - } - template void Visit(GridRefManager&) {} - }; - - struct MANGOS_DLL_DECL SpellNotifierCreatureAndPlayer - { - Spell::UnitList* i_data; - Spell& i_spell; - SpellNotifyPushType i_push_type; - float i_radius; - SpellTargets i_TargetType; - WorldObject* i_originalCaster; - WorldObject* i_castingObject; - bool i_playerControlled; - float i_centerX; - float i_centerY; - float i_centerZ; - - float GetCenterX() const { return i_centerX; } - float GetCenterY() const { return i_centerY; } - - SpellNotifierCreatureAndPlayer(Spell& spell, Spell::UnitList& data, float radius, SpellNotifyPushType type, - SpellTargets TargetType = SPELL_TARGETS_NOT_FRIENDLY, WorldObject* originalCaster = NULL) - : i_data(&data), i_spell(spell), i_push_type(type), i_radius(radius), i_TargetType(TargetType), - i_originalCaster(originalCaster), i_castingObject(i_spell.GetCastingObject()) - { - if (!i_originalCaster) - i_originalCaster = i_spell.GetAffectiveCasterObject(); - i_playerControlled = i_originalCaster ? i_originalCaster->IsControlledByPlayer() : false; - - switch (i_push_type) - { - case PUSH_IN_FRONT: - case PUSH_IN_FRONT_90: - case PUSH_IN_FRONT_30: - case PUSH_IN_FRONT_15: - case PUSH_IN_BACK: - case PUSH_SELF_CENTER: - if (i_castingObject) - { - i_centerX = i_castingObject->GetPositionX(); - i_centerY = i_castingObject->GetPositionY(); - } - break; - case PUSH_DEST_CENTER: - if (i_spell.m_targets.m_targetMask & TARGET_FLAG_SOURCE_LOCATION) - i_spell.m_targets.getSource(i_centerX, i_centerY, i_centerZ); - else - i_spell.m_targets.getDestination(i_centerX, i_centerY, i_centerZ); - break; - case PUSH_TARGET_CENTER: - if (Unit* target = i_spell.m_targets.getUnitTarget()) - { - i_centerX = target->GetPositionX(); - i_centerY = target->GetPositionY(); - } - break; - default: - sLog.outError("SpellNotifierCreatureAndPlayer: unsupported PUSH_* case %u.", i_push_type); - } - } - - template inline void Visit(GridRefManager& m) - { - MANGOS_ASSERT(i_data); - - if (!i_originalCaster || !i_castingObject) - return; - - for (typename GridRefManager::iterator itr = m.begin(); itr != m.end(); ++itr) - { - // there are still more spells which can be casted on dead, but - // they are no AOE and don't have such a nice SPELL_ATTR flag - if ((i_TargetType != SPELL_TARGETS_ALL && !itr->getSource()->isTargetableForAttack(i_spell.m_spellInfo->HasAttribute(SPELL_ATTR_EX3_CAST_ON_DEAD))) - // mostly phase check - || !itr->getSource()->IsInMap(i_originalCaster)) - continue; - - switch (i_TargetType) - { - case SPELL_TARGETS_HOSTILE: - if (!i_originalCaster->IsHostileTo(itr->getSource())) - continue; - break; - case SPELL_TARGETS_NOT_FRIENDLY: - if (i_originalCaster->IsFriendlyTo(itr->getSource())) - continue; - break; - case SPELL_TARGETS_NOT_HOSTILE: - if (i_originalCaster->IsHostileTo(itr->getSource())) - continue; - break; - case SPELL_TARGETS_FRIENDLY: - if (!i_originalCaster->IsFriendlyTo(itr->getSource())) - continue; - break; - case SPELL_TARGETS_AOE_DAMAGE: - { - if (itr->getSource()->GetTypeId() == TYPEID_UNIT && ((Creature*)itr->getSource())->IsTotem()) - continue; - - if (i_playerControlled) - { - if (i_originalCaster->IsFriendlyTo(itr->getSource())) - continue; - } - else - { - if (!i_originalCaster->IsHostileTo(itr->getSource())) - continue; - } - } - break; - case SPELL_TARGETS_ALL: - break; - default: continue; - } - - // we don't need to check InMap here, it's already done some lines above - switch (i_push_type) - { - case PUSH_IN_FRONT: - if (i_castingObject->isInFront((Unit*)(itr->getSource()), i_radius, 2 * M_PI_F / 3)) - i_data->push_back(itr->getSource()); - break; - case PUSH_IN_FRONT_90: - if (i_castingObject->isInFront((Unit*)(itr->getSource()), i_radius, M_PI_F / 2)) - i_data->push_back(itr->getSource()); - break; - case PUSH_IN_FRONT_30: - if (i_castingObject->isInFront((Unit*)(itr->getSource()), i_radius, M_PI_F / 6)) - i_data->push_back(itr->getSource()); - break; - case PUSH_IN_FRONT_15: - if (i_castingObject->isInFront((Unit*)(itr->getSource()), i_radius, M_PI_F / 12)) - i_data->push_back(itr->getSource()); - break; - case PUSH_IN_BACK: - if (i_castingObject->isInBack((Unit*)(itr->getSource()), i_radius, 2 * M_PI_F / 3)) - i_data->push_back(itr->getSource()); - break; - case PUSH_SELF_CENTER: - if (i_castingObject->IsWithinDist((Unit*)(itr->getSource()), i_radius)) - i_data->push_back(itr->getSource()); - break; - case PUSH_DEST_CENTER: - if (itr->getSource()->IsWithinDist3d(i_centerX, i_centerY, i_centerZ, i_radius)) - i_data->push_back(itr->getSource()); - break; - case PUSH_TARGET_CENTER: - if (i_spell.m_targets.getUnitTarget() && i_spell.m_targets.getUnitTarget()->IsWithinDist((Unit*)(itr->getSource()), i_radius)) - i_data->push_back(itr->getSource()); - break; - } - } - } - -#ifdef WIN32 - template<> inline void Visit(CorpseMapType&) {} - template<> inline void Visit(GameObjectMapType&) {} - template<> inline void Visit(DynamicObjectMapType&) {} - template<> inline void Visit(CameraMapType&) {} -#endif - }; - -#ifndef WIN32 - template<> inline void SpellNotifierCreatureAndPlayer::Visit(CorpseMapType&) {} - template<> inline void SpellNotifierCreatureAndPlayer::Visit(GameObjectMapType&) {} - template<> inline void SpellNotifierCreatureAndPlayer::Visit(DynamicObjectMapType&) {} - template<> inline void SpellNotifierCreatureAndPlayer::Visit(CameraMapType&) {} -#endif -} - -typedef void(Spell::*pEffect)(SpellEffectEntry const* spellEffect); - -class SpellEvent : public BasicEvent -{ - public: - SpellEvent(Spell* spell); - virtual ~SpellEvent(); - - virtual bool Execute(uint64 e_time, uint32 p_time) override; - virtual void Abort(uint64 e_time) override; - virtual bool IsDeletable() const override; - protected: - Spell* m_Spell; -}; -#endif diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp deleted file mode 100644 index e3c677ea0..000000000 --- a/src/game/SpellEffects.cpp +++ /dev/null @@ -1,10902 +0,0 @@ -/** - * This code is part of MaNGOS. Contributor & Copyright details are in AUTHORS/THANKS. - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Common.h" -#include "Database/DatabaseEnv.h" -#include "WorldPacket.h" -#include "Opcodes.h" -#include "Log.h" -#include "UpdateMask.h" -#include "World.h" -#include "ObjectMgr.h" -#include "SpellMgr.h" -#include "Player.h" -#include "SkillExtraItems.h" -#include "Unit.h" -#include "Spell.h" -#include "DynamicObject.h" -#include "SpellAuras.h" -#include "Group.h" -#include "UpdateData.h" -#include "MapManager.h" -#include "ObjectAccessor.h" -#include "SharedDefines.h" -#include "Pet.h" -#include "GameObject.h" -#include "GossipDef.h" -#include "Creature.h" -#include "Totem.h" -#include "CreatureAI.h" -#include "BattleGround/BattleGroundMgr.h" -#include "BattleGround/BattleGround.h" -#include "BattleGround/BattleGroundEY.h" -#include "BattleGround/BattleGroundWS.h" -#include "Language.h" -#include "SocialMgr.h" -#include "VMapFactory.h" -#include "Util.h" -#include "TemporarySummon.h" -#include "ScriptMgr.h" -#include "SkillDiscovery.h" -#include "Formulas.h" -#include "GridNotifiers.h" -#include "GridNotifiersImpl.h" -#include "CellImpl.h" -#include "PhaseMgr.h" - -pEffect SpellEffects[TOTAL_SPELL_EFFECTS] = -{ - &Spell::EffectNULL, // 0 - &Spell::EffectInstaKill, // 1 SPELL_EFFECT_INSTAKILL - &Spell::EffectSchoolDMG, // 2 SPELL_EFFECT_SCHOOL_DAMAGE - &Spell::EffectDummy, // 3 SPELL_EFFECT_DUMMY - &Spell::EffectUnused, // 4 SPELL_EFFECT_PORTAL_TELEPORT unused from pre-1.2.1 - &Spell::EffectTeleportUnits, // 5 SPELL_EFFECT_TELEPORT_UNITS - &Spell::EffectApplyAura, // 6 SPELL_EFFECT_APPLY_AURA - &Spell::EffectEnvironmentalDMG, // 7 SPELL_EFFECT_ENVIRONMENTAL_DAMAGE - &Spell::EffectPowerDrain, // 8 SPELL_EFFECT_POWER_DRAIN - &Spell::EffectHealthLeech, // 9 SPELL_EFFECT_HEALTH_LEECH - &Spell::EffectHeal, // 10 SPELL_EFFECT_HEAL - &Spell::EffectBind, // 11 SPELL_EFFECT_BIND - &Spell::EffectUnused, // 12 SPELL_EFFECT_PORTAL unused from pre-1.2.1, exist 2 spell, but not exist any data about its real usage - &Spell::EffectUnused, // 13 SPELL_EFFECT_RITUAL_BASE unused from pre-1.2.1 - &Spell::EffectUnused, // 14 SPELL_EFFECT_RITUAL_SPECIALIZE unused from pre-1.2.1 - &Spell::EffectUnused, // 15 SPELL_EFFECT_RITUAL_ACTIVATE_PORTAL unused from pre-1.2.1 - &Spell::EffectQuestComplete, // 16 SPELL_EFFECT_QUEST_COMPLETE - &Spell::EffectWeaponDmg, // 17 SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL - &Spell::EffectResurrect, // 18 SPELL_EFFECT_RESURRECT - &Spell::EffectAddExtraAttacks, // 19 SPELL_EFFECT_ADD_EXTRA_ATTACKS - &Spell::EffectEmpty, // 20 SPELL_EFFECT_DODGE one spell: Dodge - &Spell::EffectEmpty, // 21 SPELL_EFFECT_EVADE one spell: Evade (DND) - &Spell::EffectParry, // 22 SPELL_EFFECT_PARRY - &Spell::EffectBlock, // 23 SPELL_EFFECT_BLOCK one spell: Block - &Spell::EffectCreateItem, // 24 SPELL_EFFECT_CREATE_ITEM - &Spell::EffectEmpty, // 25 SPELL_EFFECT_WEAPON spell per weapon type, in ItemSubclassmask store mask that can be used for usability check at equip, but current way using skill also work. - &Spell::EffectEmpty, // 26 SPELL_EFFECT_DEFENSE one spell: Defense - &Spell::EffectPersistentAA, // 27 SPELL_EFFECT_PERSISTENT_AREA_AURA - &Spell::EffectSummonType, // 28 SPELL_EFFECT_SUMMON - &Spell::EffectLeapForward, // 29 SPELL_EFFECT_LEAP - &Spell::EffectEnergize, // 30 SPELL_EFFECT_ENERGIZE - &Spell::EffectWeaponDmg, // 31 SPELL_EFFECT_WEAPON_PERCENT_DAMAGE - &Spell::EffectTriggerMissileSpell, // 32 SPELL_EFFECT_TRIGGER_MISSILE - &Spell::EffectOpenLock, // 33 SPELL_EFFECT_OPEN_LOCK - &Spell::EffectSummonChangeItem, // 34 SPELL_EFFECT_SUMMON_CHANGE_ITEM - &Spell::EffectApplyAreaAura, // 35 SPELL_EFFECT_APPLY_AREA_AURA_PARTY - &Spell::EffectLearnSpell, // 36 SPELL_EFFECT_LEARN_SPELL - &Spell::EffectEmpty, // 37 SPELL_EFFECT_SPELL_DEFENSE one spell: SPELLDEFENSE (DND) - &Spell::EffectDispel, // 38 SPELL_EFFECT_DISPEL - &Spell::EffectEmpty, // 39 SPELL_EFFECT_LANGUAGE misc store lang id - &Spell::EffectDualWield, // 40 SPELL_EFFECT_DUAL_WIELD - &Spell::EffectJump, // 41 SPELL_EFFECT_JUMP - &Spell::EffectJump, // 42 SPELL_EFFECT_JUMP2 - &Spell::EffectTeleUnitsFaceCaster, // 43 SPELL_EFFECT_TELEPORT_UNITS_FACE_CASTER - &Spell::EffectLearnSkill, // 44 SPELL_EFFECT_SKILL_STEP - &Spell::EffectNULL, // 45 SPELL_EFFECT_PLAY_MOVIE - &Spell::EffectNULL, // 46 SPELL_EFFECT_SPAWN spawn/login animation, expected by spawn unit cast, also base points store some dynflags - &Spell::EffectTradeSkill, // 47 SPELL_EFFECT_TRADE_SKILL - &Spell::EffectUnused, // 48 SPELL_EFFECT_STEALTH one spell: Base Stealth - &Spell::EffectUnused, // 49 SPELL_EFFECT_DETECT one spell: Detect - &Spell::EffectTransmitted, // 50 SPELL_EFFECT_TRANS_DOOR - &Spell::EffectUnused, // 51 SPELL_EFFECT_FORCE_CRITICAL_HIT unused from pre-1.2.1 - &Spell::EffectUnused, // 52 SPELL_EFFECT_GUARANTEE_HIT unused from pre-1.2.1 - &Spell::EffectEnchantItemPerm, // 53 SPELL_EFFECT_ENCHANT_ITEM - &Spell::EffectEnchantItemTmp, // 54 SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY - &Spell::EffectTameCreature, // 55 SPELL_EFFECT_TAMECREATURE - &Spell::EffectSummonPet, // 56 SPELL_EFFECT_SUMMON_PET - &Spell::EffectLearnPetSpell, // 57 SPELL_EFFECT_LEARN_PET_SPELL - &Spell::EffectWeaponDmg, // 58 SPELL_EFFECT_WEAPON_DAMAGE - &Spell::EffectCreateRandomItem, // 59 SPELL_EFFECT_CREATE_RANDOM_ITEM create item base at spell specific loot - &Spell::EffectProficiency, // 60 SPELL_EFFECT_PROFICIENCY - &Spell::EffectSendEvent, // 61 SPELL_EFFECT_SEND_EVENT - &Spell::EffectPowerBurn, // 62 SPELL_EFFECT_POWER_BURN - &Spell::EffectThreat, // 63 SPELL_EFFECT_THREAT - &Spell::EffectTriggerSpell, // 64 SPELL_EFFECT_TRIGGER_SPELL - &Spell::EffectApplyAreaAura, // 65 SPELL_EFFECT_APPLY_AREA_AURA_RAID - &Spell::EffectRestoreItemCharges, // 66 SPELL_EFFECT_RESTORE_ITEM_CHARGES itemtype - is affected item ID - &Spell::EffectHealMaxHealth, // 67 SPELL_EFFECT_HEAL_MAX_HEALTH - &Spell::EffectInterruptCast, // 68 SPELL_EFFECT_INTERRUPT_CAST - &Spell::EffectDistract, // 69 SPELL_EFFECT_DISTRACT - &Spell::EffectPull, // 70 SPELL_EFFECT_PULL one spell: Distract Move - &Spell::EffectPickPocket, // 71 SPELL_EFFECT_PICKPOCKET - &Spell::EffectAddFarsight, // 72 SPELL_EFFECT_ADD_FARSIGHT - &Spell::EffectNULL, // 73 SPELL_EFFECT_UNTRAIN_TALENTS one spell: Trainer: Untrain Talents - &Spell::EffectApplyGlyph, // 74 SPELL_EFFECT_APPLY_GLYPH - &Spell::EffectHealMechanical, // 75 SPELL_EFFECT_HEAL_MECHANICAL one spell: Mechanical Patch Kit - &Spell::EffectSummonObjectWild, // 76 SPELL_EFFECT_SUMMON_OBJECT_WILD - &Spell::EffectScriptEffect, // 77 SPELL_EFFECT_SCRIPT_EFFECT - &Spell::EffectUnused, // 78 SPELL_EFFECT_ATTACK - &Spell::EffectSanctuary, // 79 SPELL_EFFECT_SANCTUARY - &Spell::EffectAddComboPoints, // 80 SPELL_EFFECT_ADD_COMBO_POINTS - &Spell::EffectUnused, // 81 SPELL_EFFECT_CREATE_HOUSE one spell: Create House (TEST) - &Spell::EffectNULL, // 82 SPELL_EFFECT_BIND_SIGHT - &Spell::EffectDuel, // 83 SPELL_EFFECT_DUEL - &Spell::EffectStuck, // 84 SPELL_EFFECT_STUCK - &Spell::EffectSummonPlayer, // 85 SPELL_EFFECT_SUMMON_PLAYER - &Spell::EffectActivateObject, // 86 SPELL_EFFECT_ACTIVATE_OBJECT - &Spell::EffectWMODamage, // 87 SPELL_EFFECT_WMO_DAMAGE (57 spells in 3.3.2) - &Spell::EffectWMORepair, // 88 SPELL_EFFECT_WMO_REPAIR (2 spells in 3.3.2) - &Spell::EffectWMOChange, // 89 SPELL_EFFECT_WMO_CHANGE (7 spells in 3.3.2) - &Spell::EffectKillCreditPersonal, // 90 SPELL_EFFECT_KILL_CREDIT_PERSONAL Kill credit but only for single person - &Spell::EffectUnused, // 91 SPELL_EFFECT_THREAT_ALL one spell: zzOLDBrainwash - &Spell::EffectEnchantHeldItem, // 92 SPELL_EFFECT_ENCHANT_HELD_ITEM - &Spell::EffectBreakPlayerTargeting, // 93 SPELL_EFFECT_BREAK_PLAYER_TARGETING - &Spell::EffectSelfResurrect, // 94 SPELL_EFFECT_SELF_RESURRECT - &Spell::EffectSkinning, // 95 SPELL_EFFECT_SKINNING - &Spell::EffectCharge, // 96 SPELL_EFFECT_CHARGE - &Spell::EffectSummonAllTotems, // 97 SPELL_EFFECT_SUMMON_ALL_TOTEMS - &Spell::EffectKnockBack, // 98 SPELL_EFFECT_KNOCK_BACK - &Spell::EffectDisEnchant, // 99 SPELL_EFFECT_DISENCHANT - &Spell::EffectInebriate, //100 SPELL_EFFECT_INEBRIATE - &Spell::EffectFeedPet, //101 SPELL_EFFECT_FEED_PET - &Spell::EffectDismissPet, //102 SPELL_EFFECT_DISMISS_PET - &Spell::EffectReputation, //103 SPELL_EFFECT_REPUTATION - &Spell::EffectSummonObject, //104 SPELL_EFFECT_SUMMON_OBJECT_SLOT - &Spell::EffectNULL, //105 SPELL_EFFECT_SURVEY - &Spell::EffectNULL, //106 SPELL_EFFECT_SUMMON_RAID_MARKER - &Spell::EffectNULL, //107 SPELL_EFFECT_LOOT_CORPSE - &Spell::EffectDispelMechanic, //108 SPELL_EFFECT_DISPEL_MECHANIC - &Spell::EffectSummonDeadPet, //109 SPELL_EFFECT_SUMMON_DEAD_PET - &Spell::EffectDestroyAllTotems, //110 SPELL_EFFECT_DESTROY_ALL_TOTEMS - &Spell::EffectDurabilityDamage, //111 SPELL_EFFECT_DURABILITY_DAMAGE - &Spell::EffectUnused, //112 SPELL_EFFECT_112 (old SPELL_EFFECT_SUMMON_DEMON) - &Spell::EffectResurrectNew, //113 SPELL_EFFECT_RESURRECT_NEW - &Spell::EffectTaunt, //114 SPELL_EFFECT_ATTACK_ME - &Spell::EffectDurabilityDamagePCT, //115 SPELL_EFFECT_DURABILITY_DAMAGE_PCT - &Spell::EffectSkinPlayerCorpse, //116 SPELL_EFFECT_SKIN_PLAYER_CORPSE one spell: Remove Insignia, bg usage, required special corpse flags... - &Spell::EffectSpiritHeal, //117 SPELL_EFFECT_SPIRIT_HEAL one spell: Spirit Heal - &Spell::EffectSkill, //118 SPELL_EFFECT_SKILL professions and more - &Spell::EffectApplyAreaAura, //119 SPELL_EFFECT_APPLY_AREA_AURA_PET - &Spell::EffectUnused, //120 SPELL_EFFECT_TELEPORT_GRAVEYARD one spell: Graveyard Teleport Test - &Spell::EffectWeaponDmg, //121 SPELL_EFFECT_NORMALIZED_WEAPON_DMG - &Spell::EffectUnused, //122 SPELL_EFFECT_122 unused - &Spell::EffectSendTaxi, //123 SPELL_EFFECT_SEND_TAXI taxi/flight related (misc value is taxi path id) - &Spell::EffectPlayerPull, //124 SPELL_EFFECT_PLAYER_PULL opposite of knockback effect (pulls player twoard caster) - &Spell::EffectModifyThreatPercent, //125 SPELL_EFFECT_MODIFY_THREAT_PERCENT - &Spell::EffectStealBeneficialBuff, //126 SPELL_EFFECT_STEAL_BENEFICIAL_BUFF spell steal effect? - &Spell::EffectProspecting, //127 SPELL_EFFECT_PROSPECTING Prospecting spell - &Spell::EffectApplyAreaAura, //128 SPELL_EFFECT_APPLY_AREA_AURA_FRIEND - &Spell::EffectApplyAreaAura, //129 SPELL_EFFECT_APPLY_AREA_AURA_ENEMY - &Spell::EffectRedirectThreat, //130 SPELL_EFFECT_REDIRECT_THREAT - &Spell::EffectPlaySound, //131 SPELL_EFFECT_PLAY_SOUND sound id in misc value (SoundEntries.dbc) - &Spell::EffectPlayMusic, //132 SPELL_EFFECT_PLAY_MUSIC sound id in misc value (SoundEntries.dbc) - &Spell::EffectUnlearnSpecialization, //133 SPELL_EFFECT_UNLEARN_SPECIALIZATION unlearn profession specialization - &Spell::EffectKillCreditGroup, //134 SPELL_EFFECT_KILL_CREDIT_GROUP misc value is creature entry - &Spell::EffectNULL, //135 SPELL_EFFECT_CALL_PET - &Spell::EffectHealPct, //136 SPELL_EFFECT_HEAL_PCT - &Spell::EffectEnergisePct, //137 SPELL_EFFECT_ENERGIZE_PCT - &Spell::EffectLeapBack, //138 SPELL_EFFECT_LEAP_BACK Leap back - &Spell::EffectClearQuest, //139 SPELL_EFFECT_CLEAR_QUEST (misc - is quest ID) - &Spell::EffectForceCast, //140 SPELL_EFFECT_FORCE_CAST - &Spell::EffectNULL, //141 SPELL_EFFECT_141 damage and reduce speed? - &Spell::EffectTriggerSpellWithValue, //142 SPELL_EFFECT_TRIGGER_SPELL_WITH_VALUE - &Spell::EffectApplyAreaAura, //143 SPELL_EFFECT_APPLY_AREA_AURA_OWNER - &Spell::EffectKnockBackFromPosition, //144 SPELL_EFFECT_KNOCKBACK_FROM_POSITION - &Spell::EffectNULL, //145 SPELL_EFFECT_145 Black Hole Effect - &Spell::EffectActivateRune, //146 SPELL_EFFECT_ACTIVATE_RUNE - &Spell::EffectQuestFail, //147 SPELL_EFFECT_QUEST_FAIL quest fail - &Spell::EffectNULL, //148 SPELL_EFFECT_148 single spell: Inflicts Fire damage to an enemy. - &Spell::EffectCharge2, //149 SPELL_EFFECT_CHARGE2 swoop - &Spell::EffectQuestOffer, //150 SPELL_EFFECT_QUEST_OFFER - &Spell::EffectTriggerRitualOfSummoning, //151 SPELL_EFFECT_TRIGGER_SPELL_2 - &Spell::EffectNULL, //152 SPELL_EFFECT_152 summon Refer-a-Friend - &Spell::EffectNULL, //153 SPELL_EFFECT_CREATE_PET misc value is creature entry - &Spell::EffectTeachTaxiNode, //154 SPELL_EFFECT_TEACH_TAXI_NODE single spell: Teach River's Heart Taxi Path - &Spell::EffectTitanGrip, //155 SPELL_EFFECT_TITAN_GRIP Allows you to equip two-handed axes, maces and swords in one hand, but you attack $49152s1% slower than normal. - &Spell::EffectEnchantItemPrismatic, //156 SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC - &Spell::EffectCreateItem2, //157 SPELL_EFFECT_CREATE_ITEM_2 create item or create item template and replace by some randon spell loot item - &Spell::EffectMilling, //158 SPELL_EFFECT_MILLING milling - &Spell::EffectRenamePet, //159 SPELL_EFFECT_ALLOW_RENAME_PET allow rename pet once again - &Spell::EffectNULL, //160 SPELL_EFFECT_160 single spell: Nerub'ar Web Random Unit - &Spell::EffectSpecCount, //161 SPELL_EFFECT_TALENT_SPEC_COUNT second talent spec (learn/revert) - &Spell::EffectActivateSpec, //162 SPELL_EFFECT_TALENT_SPEC_SELECT activate primary/secondary spec - &Spell::EffectUnused, //163 unused in 3.3.5a - &Spell::EffectCancelAura, //164 SPELL_EFFECT_CANCEL_AURA - &Spell::EffectNULL, //165 SPELL_EFFECT_DAMAGE_FROM_MAX_HEALTH_PCT 82 spells in 4.3.4 - &Spell::EffectNULL, //166 SPELL_EFFECT_REWARD_CURRENCY 56 spells in 4.3.4 - &Spell::EffectNULL, //167 SPELL_EFFECT_167 42 spells in 4.3.4 - &Spell::EffectNULL, //168 SPELL_EFFECT_168 2 spells in 4.3.4 Allows give commands to controlled pet - &Spell::EffectNULL, //169 SPELL_EFFECT_DESTROY_ITEM 9 spells in 4.3.4 - &Spell::EffectNULL, //170 SPELL_EFFECT_170 70 spells in 4.3.4 - &Spell::EffectNULL, //171 SPELL_EFFECT_171 19 spells in 4.3.4 related to GO summon - &Spell::EffectNULL, //172 SPELL_EFFECT_MASS_RESSURECTION Mass Ressurection (Guild Perk) - &Spell::EffectNULL, //173 SPELL_EFFECT_BUY_GUILD_BANKSLOT 4 spells in 4.3.4 basepoints - slot - &Spell::EffectNULL, //174 SPELL_EFFECT_174 13 spells some sort of area aura apply effect - &Spell::EffectUnused, //175 SPELL_EFFECT_175 unused in 4.3.4 - &Spell::EffectNULL, //176 SPELL_EFFECT_SANCTUARY_2 4 spells in 4.3.4 - &Spell::EffectNULL, //177 SPELL_EFFECT_177 2 spells in 4.3.4 Deluge(100757) and test spell - &Spell::EffectUnused, //178 SPELL_EFFECT_178 unused in 4.3.4 - &Spell::EffectNULL, //179 SPELL_EFFECT_179 15 spells in 4.3.4 - &Spell::EffectUnused, //180 SPELL_EFFECT_180 unused in 4.3.4 - &Spell::EffectUnused, //181 SPELL_EFFECT_181 unused in 4.3.4 - &Spell::EffectNULL, //182 SPELL_EFFECT_182 3 spells 4.3.4 -}; - -void Spell::EffectEmpty(SpellEffectEntry const* /*effect*/) -{ - // NOT NEED ANY IMPLEMENTATION CODE, EFFECT POSISBLE USED AS MARKER OR CLIENT INFORM -} - -void Spell::EffectNULL(SpellEffectEntry const* /*effect*/) -{ - DEBUG_LOG("WORLD: Spell Effect DUMMY"); -} - -void Spell::EffectUnused(SpellEffectEntry const* /*effect*/) -{ - // NOT USED BY ANY SPELL OR USELESS OR IMPLEMENTED IN DIFFERENT WAY IN MANGOS -} - -void Spell::EffectResurrectNew(SpellEffectEntry const* effect) -{ - if (!unitTarget || unitTarget->isAlive()) - return; - - if (unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - if (!unitTarget->IsInWorld()) - return; - - Player* pTarget = ((Player*)unitTarget); - - if (pTarget->isRessurectRequested()) // already have one active request - return; - - uint32 health = damage; - uint32 mana = effect->EffectMiscValue; - pTarget->setResurrectRequestData(m_caster->GetObjectGuid(), m_caster->GetMapId(), m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ(), health, mana); - SendResurrectRequest(pTarget); -} - -void Spell::EffectInstaKill(SpellEffectEntry const* /*effect*/) -{ - if (!unitTarget || !unitTarget->isAlive()) - return; - - if (m_caster == unitTarget) // prevent interrupt message - finish(); - - WorldObject* caster = GetCastingObject(); // we need the original casting object - - WorldPacket data(SMSG_SPELLINSTAKILLLOG, (8 + 8 + 4)); - data << (caster && caster->GetTypeId() != TYPEID_GAMEOBJECT ? m_caster->GetObjectGuid() : ObjectGuid()); // Caster GUID - data << unitTarget->GetObjectGuid(); // Victim GUID - data << uint32(m_spellInfo->Id); - m_caster->SendMessageToSet(&data, true); - - m_caster->DealDamage(unitTarget, unitTarget->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); -} - -void Spell::EffectEnvironmentalDMG(SpellEffectEntry const* effect) -{ - uint32 absorb = 0; - uint32 resist = 0; - - // Note: this hack with damage replace required until GO casting not implemented - // environment damage spells already have around enemies targeting but this not help in case nonexistent GO casting support - // currently each enemy selected explicitly and self cast damage, we prevent apply self casted spell bonuses/etc - damage = effect->CalculateSimpleValue(); - - m_caster->CalculateDamageAbsorbAndResist(m_caster, GetSpellSchoolMask(m_spellInfo), SPELL_DIRECT_DAMAGE, damage, &absorb, &resist); - - m_caster->SendSpellNonMeleeDamageLog(m_caster, m_spellInfo->Id, damage, GetSpellSchoolMask(m_spellInfo), absorb, resist, false, 0, false); - if (m_caster->GetTypeId() == TYPEID_PLAYER) - ((Player*)m_caster)->EnvironmentalDamage(DAMAGE_FIRE, damage); -} - -void Spell::EffectSchoolDMG(SpellEffectEntry const* effect) -{ - if (unitTarget && unitTarget->isAlive()) - { - SpellClassOptionsEntry const* classOptions = m_spellInfo->GetSpellClassOptions(); - - switch(m_spellInfo->GetSpellFamilyName()) - { - case SPELLFAMILY_GENERIC: - { - switch (m_spellInfo->Id) // better way to check unknown - { - // Meteor like spells (divided damage to targets) - case 24340: case 26558: case 28884: // Meteor - case 36837: case 38903: case 41276: // Meteor - case 57467: // Meteor - case 26789: // Shard of the Fallen Star - case 31436: // Malevolent Cleave - case 35181: // Dive Bomb - case 40810: case 43267: case 43268: // Saber Lash - case 42384: // Brutal Swipe - case 45150: // Meteor Slash - case 64422: case 64688: // Sonic Screech - case 70492: case 72505: // Ooze Eruption - case 71904: // Chaos Bane - case 72624: case 72625: // Ooze Eruption - { - uint32 count = 0; - for(TargetList::const_iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - if(ihit->effectMask & (1<EffectIndex)) - ++count; - - damage /= count; // divide to all targets - break; - } - // percent from health with min - case 25599: // Thundercrash - { - damage = unitTarget->GetHealth() / 2; - if (damage < 200) - damage = 200; - break; - } - // Intercept (warrior spell trigger) - case 20253: - case 61491: - { - damage += uint32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * 0.12f); - break; - } - // percent max target health - case 29142: // Eyesore Blaster - case 35139: // Throw Boom's Doom - case 49882: // Leviroth Self-Impale - case 55269: // Deathly Stare - { - damage = damage * unitTarget->GetMaxHealth() / 100; - break; - } - // Cataclysmic Bolt - case 38441: - { - damage = unitTarget->GetMaxHealth() / 2; - break; - } - // Touch the Nightmare - case 50341: - { - if (SpellEffectIndex(effect->EffectIndex) == EFFECT_INDEX_2) - damage = int32(unitTarget->GetMaxHealth() * 0.3f); - break; - } - // Tympanic Tantrum - case 62775: - { - damage = unitTarget->GetMaxHealth() / 10; - break; - } - // Hand of Rekoning (name not have typos ;) ) - case 67485: - damage += uint32(0.5f * m_caster->GetTotalAttackPowerValue(BASE_ATTACK)); - break; - // Magic Bane normal (Forge of Souls - Bronjahm) - case 68793: - { - damage += uint32(unitTarget->GetMaxPower(POWER_MANA) / 2); - damage = std::min(damage, 10000); - break; - } - // Magic Bane heroic (Forge of Souls - Bronjahm) - case 69050: - { - damage += uint32(unitTarget->GetMaxPower(POWER_MANA) / 2); - damage = std::min(damage, 15000); - break; - } - } - break; - } - case SPELLFAMILY_MAGE: - // remove Arcane Blast buffs at any non-Arcane Blast arcane damage spell. - // NOTE: it removed at hit instead cast because currently spell done-damage calculated at hit instead cast - if ((m_spellInfo->SchoolMask & SPELL_SCHOOL_MASK_ARCANE) && !(classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x20000000))) - m_caster->RemoveAurasDueToSpell(36032); // Arcane Blast buff - break; - case SPELLFAMILY_WARRIOR: - { - // Bloodthirst - if (classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x40000000000)) - { - damage = uint32(damage * (m_caster->GetTotalAttackPowerValue(BASE_ATTACK)) / 100); - } - // Victory Rush - else if (classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x10000000000)) - { - damage = uint32(damage * m_caster->GetTotalAttackPowerValue(BASE_ATTACK) / 100); - m_caster->ModifyAuraState(AURA_STATE_WARRIOR_VICTORY_RUSH, false); - } - // Revenge ${$m1+$AP*0.310} to ${$M1+$AP*0.310} - else if (classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x0000000000000400)) - damage+= uint32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * 0.310f); - // Heroic Throw ${$m1+$AP*.50} - else if (classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x0000000100000000)) - damage+= uint32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * 0.5f); - // Shattering Throw ${$m1+$AP*.50} - else if (classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x0040000000000000)) - damage+= uint32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * 0.5f); - // Shockwave ${$m3/100*$AP} - else if (classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x0000800000000000)) - { - int32 pct = m_caster->CalculateSpellDamage(unitTarget, m_spellInfo, EFFECT_INDEX_2); - if (pct > 0) - damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * pct / 100); - break; - } - // Thunder Clap - else if (classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x0000000000000080)) - { - damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * 12 / 100); - } - break; - } - case SPELLFAMILY_WARLOCK: - { - // Incinerate Rank 1 & 2 - if ((classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x00004000000000)) && m_spellInfo->SpellIconID==2128) - { - // Incinerate does more dmg (dmg*0.25) if the target have Immolate debuff. - // Check aura state for speed but aura state set not only for Immolate spell - if (unitTarget->HasAuraState(AURA_STATE_CONFLAGRATE)) - { - Unit::AuraList const& RejorRegr = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE); - for (Unit::AuraList::const_iterator i = RejorRegr.begin(); i != RejorRegr.end(); ++i) - { - // Immolate - SpellClassOptionsEntry const* immSpellClassOpt = (*i)->GetSpellProto()->GetSpellClassOptions(); - if(!immSpellClassOpt) - continue; - if(immSpellClassOpt->SpellFamilyName == SPELLFAMILY_WARLOCK && - (immSpellClassOpt->SpellFamilyFlags & UI64LIT(0x00000000000004))) - { - damage += damage / 4; - break; - } - } - } - } - // Shadowflame - else if (classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x0001000000000000)) - { - // Apply DOT part - switch (m_spellInfo->Id) - { - case 47897: m_caster->CastSpell(unitTarget, 47960, true); break; - case 61290: m_caster->CastSpell(unitTarget, 61291, true); break; - default: - sLog.outError("Spell::EffectDummy: Unhandeled Shadowflame spell rank %u", m_spellInfo->Id); - break; - } - } - // Shadow Bite - else if (classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x0040000000000000)) - { - Unit* owner = m_caster->GetOwner(); - if (!owner) - break; - - uint32 counter = 0; - Unit::AuraList const& dotAuras = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE); - for (Unit::AuraList::const_iterator itr = dotAuras.begin(); itr != dotAuras.end(); ++itr) - if ((*itr)->GetCasterGuid() == owner->GetObjectGuid()) - ++counter; - - if (counter) - damage += (counter * owner->CalculateSpellDamage(unitTarget, m_spellInfo, EFFECT_INDEX_2) * damage) / 100.0f; - } - // Conflagrate - consumes Immolate or Shadowflame - else if (m_spellInfo->GetTargetAuraState() == AURA_STATE_CONFLAGRATE) - { - Aura const* aura = NULL; // found req. aura for damage calculation - - Unit::AuraList const& mPeriodic = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE); - for (Unit::AuraList::const_iterator i = mPeriodic.begin(); i != mPeriodic.end(); ++i) - { - // for caster applied auras only - if ((*i)->GetSpellProto()->GetSpellFamilyName() != SPELLFAMILY_WARLOCK || - (*i)->GetCasterGuid() != m_caster->GetObjectGuid()) - continue; - - // Immolate - if ((*i)->GetSpellProto()->IsFitToFamilyMask(UI64LIT(0x0000000000000004))) - { - aura = *i; // it selected always if exist - break; - } - - // Shadowflame - if ((*i)->GetSpellProto()->IsFitToFamilyMask(UI64LIT(0x0000000000000000), 0x00000002)) - aura = *i; // remember but wait possible Immolate as primary priority - } - - // found Immolate or Shadowflame - if (aura) - { - int32 damagetick = aura->GetModifier()->m_amount; - damage += damagetick * 4; - - // Glyph of Conflagrate - if (!m_caster->HasAura(56235)) - unitTarget->RemoveAurasByCasterSpell(aura->GetId(), m_caster->GetObjectGuid()); - break; - } - } - break; - } - case SPELLFAMILY_PRIEST: - { - // Shadow Word: Death - deals damage equal to damage done to caster - if (classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x0000000200000000)) - m_caster->CastCustomSpell(m_caster, 32409, &damage, 0, 0, true); - // Improved Mind Blast (Mind Blast in shadow form bonus) - else if (m_caster->GetShapeshiftForm() == FORM_SHADOW && (classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x00002000))) - { - Unit::AuraList const& ImprMindBlast = m_caster->GetAurasByType(SPELL_AURA_ADD_FLAT_MODIFIER); - for (Unit::AuraList::const_iterator i = ImprMindBlast.begin(); i != ImprMindBlast.end(); ++i) - { - if ((*i)->GetSpellProto()->GetSpellFamilyName() == SPELLFAMILY_PRIEST && - ((*i)->GetSpellProto()->SpellIconID == 95)) - { - int chance = (*i)->GetSpellProto()->CalculateSimpleValue(EFFECT_INDEX_1); - if (roll_chance_i(chance)) - // Mind Trauma - m_caster->CastSpell(unitTarget, 48301, true); - break; - } - } - } - break; - } - case SPELLFAMILY_DRUID: - { - SpellEffectEntry const* rakeSpellEffect = m_spellInfo->GetSpellEffect(EFFECT_INDEX_2); - // Ferocious Bite - if (m_caster->GetTypeId()==TYPEID_PLAYER && (classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x000800000)) && m_spellInfo->SpellVisual[0]==6587) - { - // converts up to 30 points of energy into ($f1+$AP/410) additional damage - float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK); - float multiple = ap / 410 + effect->DmgMultiplier; - damage += int32(((Player*)m_caster)->GetComboPoints() * ap * 7 / 100); - uint32 energy = m_caster->GetPower(POWER_ENERGY); - uint32 used_energy = energy > 30 ? 30 : energy; - damage += int32(used_energy * multiple); - m_caster->SetPower(POWER_ENERGY, energy - used_energy); - } - // Rake - else if (classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x0000000000001000) && rakeSpellEffect && rakeSpellEffect->Effect == SPELL_EFFECT_ADD_COMBO_POINTS) - { - // $AP*0.01 bonus - damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) / 100); - } - // Swipe - else if (classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x0010000000000000)) - { - damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * 0.08f); - } - break; - } - case SPELLFAMILY_ROGUE: - { - // Envenom - if (m_caster->GetTypeId()==TYPEID_PLAYER && (classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x800000000))) - { - // consume from stack dozes not more that have combo-points - if (uint32 combo = ((Player*)m_caster)->GetComboPoints()) - { - Aura* poison = 0; - // Lookup for Deadly poison (only attacker applied) - Unit::AuraList const& auras = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE); - for(Unit::AuraList::const_iterator itr = auras.begin(); itr!=auras.end(); ++itr) - { - SpellClassOptionsEntry const* poisonClassOptions = (*itr)->GetSpellProto()->GetSpellClassOptions(); - if(!poisonClassOptions) - continue; - if( poisonClassOptions->SpellFamilyName==SPELLFAMILY_ROGUE && - (poisonClassOptions->SpellFamilyFlags & UI64LIT(0x10000)) && - (*itr)->GetCasterGuid() == m_caster->GetObjectGuid()) - { - poison = *itr; - break; - } - } - // count consumed deadly poison doses at target - if (poison) - { - bool needConsume = true; - uint32 spellId = poison->GetId(); - uint32 doses = poison->GetStackAmount(); - if (doses > combo) - doses = combo; - - // Master Poisoner - Unit::AuraList const& auraList = ((Player*)m_caster)->GetAurasByType(SPELL_AURA_MOD_DURATION_OF_EFFECTS_BY_DISPEL); - for (Unit::AuraList::const_iterator iter = auraList.begin(); iter != auraList.end(); ++iter) - { - if ((*iter)->GetSpellProto()->GetSpellFamilyName() == SPELLFAMILY_ROGUE && (*iter)->GetSpellProto()->SpellIconID == 1960) - { - if (int32 chance = (*iter)->GetSpellProto()->CalculateSimpleValue(EFFECT_INDEX_2)) - if (roll_chance_i(chance)) - needConsume = false; - - break; - } - } - - if (needConsume) - unitTarget->RemoveAuraHolderFromStack(spellId, doses, m_caster->GetObjectGuid()); - - damage *= doses; - damage += int32(((Player*)m_caster)->GetTotalAttackPowerValue(BASE_ATTACK) * 0.09f * doses); - } - // Eviscerate and Envenom Bonus Damage (item set effect) - if (m_caster->GetDummyAura(37169)) - damage += ((Player*)m_caster)->GetComboPoints() * 40; - } - } - // Eviscerate - else if ((classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x00020000)) && m_caster->GetTypeId()==TYPEID_PLAYER) - { - if (uint32 combo = ((Player*)m_caster)->GetComboPoints()) - { - float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK); - damage += irand(int32(ap * combo * 0.03f), int32(ap * combo * 0.07f)); - - // Eviscerate and Envenom Bonus Damage (item set effect) - if (m_caster->GetDummyAura(37169)) - damage += combo * 40; - } - } - break; - } - case SPELLFAMILY_HUNTER: - { - // Gore - if (m_spellInfo->SpellIconID == 1578) - { - if (m_caster->HasAura(57627)) // Charge 6 sec post-affect - damage *= 2; - } - // Steady Shot - else if (classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x100000000)) - { - int32 base = irand((int32)m_caster->GetWeaponDamageRange(RANGED_ATTACK, MINDAMAGE), (int32)m_caster->GetWeaponDamageRange(RANGED_ATTACK, MAXDAMAGE)); - damage += int32(float(base) / m_caster->GetAttackTime(RANGED_ATTACK) * 2800 + m_caster->GetTotalAttackPowerValue(RANGED_ATTACK) * 0.1f); - } - break; - } - case SPELLFAMILY_PALADIN: - { - // Judgement of Righteousness - receive benefit from Spell Damage and Attack power - if (m_spellInfo->Id == 20187) - { - float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK); - int32 holy = m_caster->SpellBaseDamageBonusDone(GetSpellSchoolMask(m_spellInfo)); - if (holy < 0) - holy = 0; - damage += int32(ap * 0.2f) + int32(holy * 32 / 100); - } - // Judgement of Vengeance/Corruption ${1+0.22*$SPH+0.14*$AP} + 10% for each application of Holy Vengeance/Blood Corruption on the target - else if ((classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x800000000)) && m_spellInfo->SpellIconID==2292) - { - uint32 debuf_id; - switch (m_spellInfo->Id) - { - case 53733: debuf_id = 53742; break;// Judgement of Corruption -> Blood Corruption - case 31804: debuf_id = 31803; break;// Judgement of Vengeance -> Holy Vengeance - default: return; - } - - float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK); - int32 holy = m_caster->SpellBaseDamageBonusDone(GetSpellSchoolMask(m_spellInfo)); - if (holy < 0) - holy = 0; - damage += int32(ap * 0.14f) + int32(holy * 22 / 100); - // Get stack of Holy Vengeance on the target added by caster - uint32 stacks = 0; - Unit::AuraList const& auras = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE); - for (Unit::AuraList::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) - { - if (((*itr)->GetId() == debuf_id) && (*itr)->GetCasterGuid() == m_caster->GetObjectGuid()) - { - stacks = (*itr)->GetStackAmount(); - break; - } - } - // + 10% for each application of Holy Vengeance on the target - if (stacks) - damage += damage * stacks * 10 / 100; - } - // Avenger's Shield ($m1+0.07*$SPH+0.07*$AP) - ranged sdb for future - else if (classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x0000000000004000)) - { - float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK); - int32 holy = m_caster->SpellBaseDamageBonusDone(GetSpellSchoolMask(m_spellInfo)); - if (holy < 0) - holy = 0; - damage += int32(ap * 0.07f) + int32(holy * 7 / 100); - } - // Hammer of Wrath ($m1+0.15*$SPH+0.15*$AP) - ranged type sdb future fix - else if (classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x0000008000000000)) - { - float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK); - int32 holy = m_caster->SpellBaseDamageBonusDone(GetSpellSchoolMask(m_spellInfo)); - if (holy < 0) - holy = 0; - damage += int32(ap * 0.15f) + int32(holy * 15 / 100); - } - // Hammer of the Righteous - else if (classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x0004000000000000)) - { - // Add main hand dps * effect[2] amount - float average = (m_caster->GetFloatValue(UNIT_FIELD_MINDAMAGE) + m_caster->GetFloatValue(UNIT_FIELD_MAXDAMAGE)) / 2; - int32 count = m_caster->CalculateSpellDamage(unitTarget, m_spellInfo, EFFECT_INDEX_2); - damage += count * int32(average * IN_MILLISECONDS) / m_caster->GetAttackTime(BASE_ATTACK); - } - // Judgement - else if (m_spellInfo->Id == 54158) - { - // [1 + 0.25 * SPH + 0.16 * AP] - damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * 0.16f); - } - break; - } - } - - if (damage >= 0) - m_damage += damage; - } -} - -void Spell::EffectDummy(SpellEffectEntry const* effect) -{ - if (!unitTarget && !gameObjTarget && !itemTarget) - return; - - // selection by spell family - switch(m_spellInfo->GetSpellFamilyName()) - { - case SPELLFAMILY_GENERIC: - { - switch (m_spellInfo->Id) - { - case 3360: // Curse of the Eye - { - if (!unitTarget) - return; - - uint32 spell_id = (unitTarget->getGender() == GENDER_MALE) ? 10651 : 10653; - - m_caster->CastSpell(unitTarget, spell_id, true); - return; - } - case 7671: // Transformation (human<->worgen) - { - if (!unitTarget) - return; - - // Transform Visual - unitTarget->CastSpell(unitTarget, 24085, true); - return; - } - case 8063: // Deviate Fish - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - uint32 spell_id = 0; - switch (urand(1, 5)) - { - case 1: spell_id = 8064; break; // Sleepy - case 2: spell_id = 8065; break; // Invigorate - case 3: spell_id = 8066; break; // Shrink - case 4: spell_id = 8067; break; // Party Time! - case 5: spell_id = 8068; break; // Healthy Spirit - } - m_caster->CastSpell(m_caster, spell_id, true, NULL); - return; - } - case 8213: // Savory Deviate Delight - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - uint32 spell_id = 0; - switch (urand(1, 2)) - { - // Flip Out - ninja - case 1: spell_id = (m_caster->getGender() == GENDER_MALE ? 8219 : 8220); break; - // Yaaarrrr - pirate - case 2: spell_id = (m_caster->getGender() == GENDER_MALE ? 8221 : 8222); break; - } - - m_caster->CastSpell(m_caster, spell_id, true, NULL); - return; - } - case 9976: // Polly Eats the E.C.A.C. - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - // Summon Polly Jr. - unitTarget->CastSpell(unitTarget, 9998, true); - - ((Creature*)unitTarget)->ForcedDespawn(100); - return; - } - case 10254: // Stone Dwarf Awaken Visual - { - if (m_caster->GetTypeId() != TYPEID_UNIT) - return; - - // see spell 10255 (aura dummy) - m_caster->clearUnitState(UNIT_STAT_ROOT); - m_caster->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - return; - } - case 13120: // net-o-matic - { - if (!unitTarget) - return; - - uint32 spell_id = 0; - - uint32 roll = urand(0, 99); - - if (roll < 2) // 2% for 30 sec self root (off-like chance unknown) - spell_id = 16566; - else if (roll < 4) // 2% for 20 sec root, charge to target (off-like chance unknown) - spell_id = 13119; - else // normal root - spell_id = 13099; - - m_caster->CastSpell(unitTarget, spell_id, true, NULL); - return; - } - case 13567: // Dummy Trigger - { - // can be used for different aura triggering, so select by aura - if (!m_triggeredByAuraSpell || !unitTarget) - return; - - switch (m_triggeredByAuraSpell->Id) - { - case 26467: // Persistent Shield - m_caster->CastCustomSpell(unitTarget, 26470, &damage, NULL, NULL, true); - break; - default: - sLog.outError("EffectDummy: Non-handled case for spell 13567 for triggered aura %u", m_triggeredByAuraSpell->Id); - break; - } - return; - } - case 14537: // Six Demon Bag - { - if (!unitTarget) - return; - - Unit* newTarget = unitTarget; - uint32 spell_id = 0; - uint32 roll = urand(0, 99); - if (roll < 25) // Fireball (25% chance) - spell_id = 15662; - else if (roll < 50) // Frostbolt (25% chance) - spell_id = 11538; - else if (roll < 70) // Chain Lighting (20% chance) - spell_id = 21179; - else if (roll < 77) // Polymorph (10% chance, 7% to target) - spell_id = 14621; - else if (roll < 80) // Polymorph (10% chance, 3% to self, backfire) - { - spell_id = 14621; - newTarget = m_caster; - } - else if (roll < 95) // Enveloping Winds (15% chance) - spell_id = 25189; - else // Summon Felhund minion (5% chance) - { - spell_id = 14642; - newTarget = m_caster; - } - - m_caster->CastSpell(newTarget, spell_id, true, m_CastItem); - return; - } - case 15998: // Capture Worg Pup - case 29435: // Capture Female Kaliri Hatchling - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - Creature* creatureTarget = (Creature*)unitTarget; - - creatureTarget->ForcedDespawn(); - return; - } - case 16589: // Noggenfogger Elixir - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - uint32 spell_id = 0; - switch (urand(1, 3)) - { - case 1: spell_id = 16595; break; - case 2: spell_id = 16593; break; - default: spell_id = 16591; break; - } - - m_caster->CastSpell(m_caster, spell_id, true, NULL); - return; - } - case 17009: // Voodoo - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - uint32 spell_id = 0; - switch (urand(0, 6)) - { - case 0: spell_id = 16707; break; // Hex - case 1: spell_id = 16708; break; // Hex - case 2: spell_id = 16709; break; // Hex - case 3: spell_id = 16711; break; // Grow - case 4: spell_id = 16712; break; // Special Brew - case 5: spell_id = 16713; break; // Ghostly - case 6: spell_id = 16716; break; // Launch - } - - m_caster->CastSpell(unitTarget, spell_id, true, NULL, NULL, m_originalCasterGUID, m_spellInfo); - return; - } - case 17251: // Spirit Healer Res - { - if (!unitTarget) - return; - - Unit* caster = GetAffectiveCaster(); - - if (caster && caster->GetTypeId() == TYPEID_PLAYER) - { - WorldPacket data(SMSG_SPIRIT_HEALER_CONFIRM, 8); - data << unitTarget->GetObjectGuid(); - ((Player*)caster)->GetSession()->SendPacket(&data); - } - return; - } - case 17271: // Test Fetid Skull - { - if (!itemTarget && m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - uint32 spell_id = urand(0, 1) - ? 17269 // Create Resonating Skull - : 17270; // Create Bone Dust - - m_caster->CastSpell(m_caster, spell_id, true, NULL); - return; - } - case 17770: // Wolfshead Helm Energy - { - m_caster->CastSpell(m_caster, 29940, true, NULL); - return; - } - case 17950: // Shadow Portal - { - if (!unitTarget) - return; - - // Shadow Portal - const uint32 spell_list[6] = {17863, 17939, 17943, 17944, 17946, 17948}; - - m_caster->CastSpell(unitTarget, spell_list[urand(0, 5)], true); - return; - } - case 19411: // Lava Bomb - case 20474: // Lava Bomb - { - if (!unitTarget) - return; - - // Hack alert! - // This dummy are expected to cast spell 20494 to summon GO entry 177704 - // Spell does not exist client side, so we have to make a hack, creating the GO (SPELL_EFFECT_SUMMON_OBJECT_WILD) - // Spell should appear in both SMSG_SPELL_START/GO and SMSG_SPELLLOGEXECUTE - - // For later, creating custom spell - // _START: packguid: target, cast flags: 0xB, TARGET_FLAG_SELF - // _GO: packGuid: target, cast flags: 0x4309, TARGET_FLAG_DEST_LOCATION - // LOG: spell: 20494, effect, pguid: goguid - - GameObject* pGameObj = new GameObject; - - Map* map = unitTarget->GetMap(); - - if (!pGameObj->Create(map->GenerateLocalLowGuid(HIGHGUID_GAMEOBJECT), 177704, - map, m_caster->GetPhaseMask(), - unitTarget->GetPositionX(), unitTarget->GetPositionY(), unitTarget->GetPositionZ(), - unitTarget->GetOrientation())) - { - delete pGameObj; - return; - } - - DEBUG_LOG("Gameobject, create custom in SpellEffects.cpp EffectDummy"); - - // Expect created without owner, but with level from _template - pGameObj->SetRespawnTime(MINUTE / 2); - pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, pGameObj->GetGOInfo()->trap.level); - pGameObj->SetSpellId(m_spellInfo->Id); - - map->Add(pGameObj); - - return; - } - case 19869: // Dragon Orb - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER || unitTarget->HasAura(23958)) - return; - - unitTarget->CastSpell(unitTarget, 19832, true); - return; - } - case 20037: // Explode Orb Effect - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, 20038, true); - return; - } - case 20577: // Cannibalize - { - if (unitTarget) - m_caster->CastSpell(m_caster, 20578, false, NULL); - - return; - } - case 21147: // Arcane Vacuum - { - if (!unitTarget) - return; - - // Spell used by Azuregos to teleport all the players to him - // This also resets the target threat - if (m_caster->getThreatManager().getThreat(unitTarget)) - m_caster->getThreatManager().modifyThreatPercent(unitTarget, -100); - - // cast summon player - m_caster->CastSpell(unitTarget, 21150, true); - - return; - } - case 23019: // Crystal Prison Dummy DND - { - if (!unitTarget || !unitTarget->isAlive() || unitTarget->GetTypeId() != TYPEID_UNIT || ((Creature*)unitTarget)->IsPet()) - return; - - Creature* creatureTarget = (Creature*)unitTarget; - if (creatureTarget->IsPet()) - return; - - GameObject* pGameObj = new GameObject; - - Map* map = creatureTarget->GetMap(); - - // create before death for get proper coordinates - if (!pGameObj->Create(map->GenerateLocalLowGuid(HIGHGUID_GAMEOBJECT), 179644, map, m_caster->GetPhaseMask(), - creatureTarget->GetPositionX(), creatureTarget->GetPositionY(), creatureTarget->GetPositionZ(), - creatureTarget->GetOrientation())) - { - delete pGameObj; - return; - } - - pGameObj->SetRespawnTime(creatureTarget->GetRespawnTime() - time(NULL)); - pGameObj->SetOwnerGuid(m_caster->GetObjectGuid()); - pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel()); - pGameObj->SetSpellId(m_spellInfo->Id); - - creatureTarget->ForcedDespawn(); - - DEBUG_LOG("AddObject at SpellEfects.cpp EffectDummy"); - map->Add(pGameObj); - - return; - } - case 23074: // Arcanite Dragonling - { - if (!m_CastItem) - return; - - m_caster->CastSpell(m_caster, 19804, true, m_CastItem); - return; - } - case 23075: // Mithril Mechanical Dragonling - { - if (!m_CastItem) - return; - - m_caster->CastSpell(m_caster, 12749, true, m_CastItem); - return; - } - case 23076: // Mechanical Dragonling - { - if (!m_CastItem) - return; - - m_caster->CastSpell(m_caster, 4073, true, m_CastItem); - return; - } - case 23133: // Gnomish Battle Chicken - { - if (!m_CastItem) - return; - - m_caster->CastSpell(m_caster, 13166, true, m_CastItem); - return; - } - case 23138: // Gate of Shazzrah - { - if (!unitTarget) - return; - - // Effect probably include a threat change, but it is unclear if fully - // reset or just forced upon target for teleport (SMSG_HIGHEST_THREAT_UPDATE) - - // Gate of Shazzrah - m_caster->CastSpell(unitTarget, 23139, true); - return; - } - case 23448: // Transporter Arrival - Ultrasafe Transporter: Gadgetzan - backfires - { - int32 r = irand(0, 119); - if (r < 20) // Transporter Malfunction - 1/6 polymorph - m_caster->CastSpell(m_caster, 23444, true); - else if (r < 100) // Evil Twin - 4/6 evil twin - m_caster->CastSpell(m_caster, 23445, true); - else // Transporter Malfunction - 1/6 miss the target - m_caster->CastSpell(m_caster, 36902, true); - - return; - } - case 23453: // Gnomish Transporter - Ultrasafe Transporter: Gadgetzan - { - if (roll_chance_i(50)) // Gadgetzan Transporter - success - m_caster->CastSpell(m_caster, 23441, true); - else // Gadgetzan Transporter Failure - failure - m_caster->CastSpell(m_caster, 23446, true); - - return; - } - case 23645: // Hourglass Sand - m_caster->RemoveAurasDueToSpell(23170); // Brood Affliction: Bronze - return; - case 23725: // Gift of Life (warrior bwl trinket) - m_caster->CastSpell(m_caster, 23782, true); - m_caster->CastSpell(m_caster, 23783, true); - return; - case 24930: // Hallow's End Treat - { - uint32 spell_id = 0; - - switch (urand(1, 4)) - { - case 1: spell_id = 24924; break; // Larger and Orange - case 2: spell_id = 24925; break; // Skeleton - case 3: spell_id = 24926; break; // Pirate - case 4: spell_id = 24927; break; // Ghost - } - - m_caster->CastSpell(m_caster, spell_id, true); - return; - } - case 25860: // Reindeer Transformation - { - if (!m_caster->HasAuraType(SPELL_AURA_MOUNTED)) - return; - - float flyspeed = m_caster->GetSpeedRate(MOVE_FLIGHT); - float speed = m_caster->GetSpeedRate(MOVE_RUN); - - m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); - - // 5 different spells used depending on mounted speed and if mount can fly or not - if (flyspeed >= 4.1f) - // Flying Reindeer - m_caster->CastSpell(m_caster, 44827, true); // 310% flying Reindeer - else if (flyspeed >= 3.8f) - // Flying Reindeer - m_caster->CastSpell(m_caster, 44825, true); // 280% flying Reindeer - else if (flyspeed >= 1.6f) - // Flying Reindeer - m_caster->CastSpell(m_caster, 44824, true); // 60% flying Reindeer - else if (speed >= 2.0f) - // Reindeer - m_caster->CastSpell(m_caster, 25859, true); // 100% ground Reindeer - else - // Reindeer - m_caster->CastSpell(m_caster, 25858, true); // 60% ground Reindeer - - return; - } - case 26074: // Holiday Cheer - // implemented at client side - return; - case 28006: // Arcane Cloaking - { - if (unitTarget && unitTarget->GetTypeId() == TYPEID_PLAYER) - // Naxxramas Entry Flag Effect DND - m_caster->CastSpell(unitTarget, 29294, true); - - return; - } - case 29126: // Cleansing Flames (Darnassus) - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - unitTarget->CastSpell(unitTarget, 29099, true); - return; - } - case 29135: // Cleansing Flames (Ironforge) - case 29136: // Cleansing Flames (Orgrimmar) - case 29137: // Cleansing Flames (Stormwind) - case 29138: // Cleansing Flames (Thunder Bluff) - case 29139: // Cleansing Flames (Undercity) - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - uint32 spellIDs[] = {29102, 29130, 29101, 29132, 29133}; - unitTarget->CastSpell(unitTarget, spellIDs[m_spellInfo->Id - 29135], true); - return; - } - case 29200: // Purify Helboar Meat - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - uint32 spell_id = roll_chance_i(50) - ? 29277 // Summon Purified Helboar Meat - : 29278; // Summon Toxic Helboar Meat - - m_caster->CastSpell(m_caster, spell_id, true, NULL); - return; - } - case 29858: // Soulshatter - { - if (unitTarget && unitTarget->GetTypeId() == TYPEID_UNIT && unitTarget->IsHostileTo(m_caster)) - m_caster->CastSpell(unitTarget, 32835, true); - - return; - } - case 29969: // Summon Blizzard - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, 29952, true, NULL, NULL, m_caster->GetObjectGuid()); - return; - } - case 29979: // Massive Magnetic Pull - { - if (!unitTarget) - return; - - m_caster->CastSpell(unitTarget, 30010, true); - return; - } - case 30004: // Flame Wreath - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - m_caster->CastSpell(unitTarget, 29946, true); - return; - } - case 30458: // Nigh Invulnerability - { - if (!m_CastItem) - return; - - if (roll_chance_i(86)) // Nigh-Invulnerability - success - m_caster->CastSpell(m_caster, 30456, true, m_CastItem); - else // Complete Vulnerability - backfire in 14% casts - m_caster->CastSpell(m_caster, 30457, true, m_CastItem); - - return; - } - case 30507: // Poultryizer - { - if (!m_CastItem) - return; - - if (roll_chance_i(80)) // Poultryized! - success - m_caster->CastSpell(unitTarget, 30501, true, m_CastItem); - else // Poultryized! - backfire 20% - m_caster->CastSpell(unitTarget, 30504, true, m_CastItem); - - return; - } - case 32146: // Liquid Fire - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT || m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - ((Player*)m_caster)->KilledMonsterCredit(unitTarget->GetEntry(), unitTarget->GetObjectGuid()); - ((Creature*)unitTarget)->ForcedDespawn(); - return; - } - case 32300: // Focus Fire - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, unitTarget->GetMap()->IsRegularDifficulty() ? 32302 : 38382, true); - return; - } - case 32312: // Move 1 (Chess event AI short distance move) - case 37388: // Move 2 (Chess event AI long distance move) - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - // cast generic move spell - m_caster->CastSpell(unitTarget, 30012, true); - return; - } - case 33060: // Make a Wish - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - uint32 spell_id = 0; - - switch (urand(1, 5)) - { - case 1: spell_id = 33053; break; // Mr Pinchy's Blessing - case 2: spell_id = 33057; break; // Summon Mighty Mr. Pinchy - case 3: spell_id = 33059; break; // Summon Furious Mr. Pinchy - case 4: spell_id = 33062; break; // Tiny Magical Crawdad - case 5: spell_id = 33064; break; // Mr. Pinchy's Gift - } - - m_caster->CastSpell(m_caster, spell_id, true, NULL); - return; - } - case 34803: // Summon Reinforcements - { - m_caster->CastSpell(m_caster, 34810, true); // Summon 20083 behind of the caster - m_caster->CastSpell(m_caster, 34817, true); // Summon 20078 right of the caster - m_caster->CastSpell(m_caster, 34818, true); // Summon 20078 left of the caster - m_caster->CastSpell(m_caster, 34819, true); // Summon 20078 front of the caster - return; - } - case 36677: // Chaos Breath - { - if (!unitTarget) - return; - - uint32 possibleSpells[] = {36693, 36694, 36695, 36696, 36697, 36698, 36699, 36700} ; - std::vector spellPool(possibleSpells, possibleSpells + countof(possibleSpells)); - std::random_shuffle(spellPool.begin(), spellPool.end()); - - for (uint8 i = 0; i < (m_caster->GetMap()->IsRegularDifficulty() ? 2 : 4); ++i) - m_caster->CastSpell(m_caster, spellPool[i], true); - - return; - } - case 33923: // Sonic Boom - case 38796: // Sonic Boom (heroic) - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, m_spellInfo->Id == 33923 ? 33666 : 38795, true); - return; - } - case 35745: // Socrethar's Stone - { - uint32 spell_id; - switch (m_caster->GetAreaId()) - { - case 3900: spell_id = 35743; break; // Socrethar Portal - case 3742: spell_id = 35744; break; // Socrethar Portal - default: return; - } - - m_caster->CastSpell(m_caster, spell_id, true); - return; - } - case 37674: // Chaos Blast - { - if (!unitTarget) - return; - - int32 basepoints0 = 100; - m_caster->CastCustomSpell(unitTarget, 37675, &basepoints0, NULL, NULL, true); - return; - } - case 39189: // Sha'tari Torch - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT || m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - // Flames - if (unitTarget->HasAura(39199)) - return; - - unitTarget->CastSpell(unitTarget, 39199, true); - ((Player*)m_caster)->KilledMonsterCredit(unitTarget->GetEntry(), unitTarget->GetObjectGuid()); - ((Creature*)unitTarget)->ForcedDespawn(10000); - return; - } - case 39635: // Throw Glaive (first) - case 39849: // Throw Glaive (second) - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, 41466, true, NULL, NULL, m_caster->GetObjectGuid()); - return; - } - case 40802: // Mingo's Fortune Generator (Mingo's Fortune Giblets) - { - // selecting one from Bloodstained Fortune item - uint32 newitemid; - switch (urand(1, 20)) - { - case 1: newitemid = 32688; break; - case 2: newitemid = 32689; break; - case 3: newitemid = 32690; break; - case 4: newitemid = 32691; break; - case 5: newitemid = 32692; break; - case 6: newitemid = 32693; break; - case 7: newitemid = 32700; break; - case 8: newitemid = 32701; break; - case 9: newitemid = 32702; break; - case 10: newitemid = 32703; break; - case 11: newitemid = 32704; break; - case 12: newitemid = 32705; break; - case 13: newitemid = 32706; break; - case 14: newitemid = 32707; break; - case 15: newitemid = 32708; break; - case 16: newitemid = 32709; break; - case 17: newitemid = 32710; break; - case 18: newitemid = 32711; break; - case 19: newitemid = 32712; break; - case 20: newitemid = 32713; break; - default: - return; - } - - DoCreateItem(effect, newitemid); - return; - } - case 40834: // Agonizing Flames - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - m_caster->CastSpell(unitTarget, 40932, true); - return; - } - case 40869: // Fatal Attraction - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - m_caster->CastSpell(unitTarget, 41001, true); - return; - } - case 40962: // Blade's Edge Terrace Demon Boss Summon Branch - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - uint32 spell_id = 0; - switch (urand(1, 4)) - { - case 1: spell_id = 40957; break; // Blade's Edge Terrace Demon Boss Summon 1 - case 2: spell_id = 40959; break; // Blade's Edge Terrace Demon Boss Summon 2 - case 3: spell_id = 40960; break; // Blade's Edge Terrace Demon Boss Summon 3 - case 4: spell_id = 40961; break; // Blade's Edge Terrace Demon Boss Summon 4 - } - unitTarget->CastSpell(unitTarget, spell_id, true); - return; - } - case 41333: // Empyreal Equivalency - { - if (!unitTarget) - return; - - // Equilize the health of all targets based on the corresponding health percent - float health_diff = (float)unitTarget->GetMaxHealth() / (float)m_caster->GetMaxHealth(); - unitTarget->SetHealth(m_caster->GetHealth() * health_diff); - return; - } - case 42287: // Salvage Wreckage - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - if (roll_chance_i(66)) - m_caster->CastSpell(m_caster, 42289, true, m_CastItem); - else - m_caster->CastSpell(m_caster, 42288, true); - - return; - } - case 42628: // Fire Bomb (throw) - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, 42629, true); - return; - } - case 42631: // Fire Bomb (explode) - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - unitTarget->RemoveAurasDueToSpell(42629); - unitTarget->CastSpell(unitTarget, 42630, true); - - // despawn the bomb after exploding - ((Creature*)unitTarget)->ForcedDespawn(3000); - return; - } - case 42793: // Burn Body - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT || m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - Creature* pCreature = (Creature*)unitTarget; - - // Spell can be used in combat and may affect different target than the expected. - // If target is not the expected we need to prevent this effect. - if (pCreature->HasLootRecipient() || pCreature->isInCombat()) - return; - - // set loot recipient, prevent re-use same target - pCreature->SetLootRecipient(m_caster); - - pCreature->ForcedDespawn(m_duration); - - // EFFECT_INDEX_2 has 0 miscvalue for effect 134, doing the killcredit here instead (only one known case exist where 0) - ((Player*)m_caster)->KilledMonster(pCreature->GetCreatureInfo(), pCreature->GetObjectGuid()); - return; - } - case 43014: // Despawn Self - { - if (m_caster->GetTypeId() != TYPEID_UNIT) - return; - - ((Creature*)m_caster)->ForcedDespawn(); - return; - } - case 43036: // Dismembering Corpse - { - if (!unitTarget || m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - if (unitTarget->HasAura(43059, EFFECT_INDEX_0)) - return; - - unitTarget->CastSpell(m_caster, 43037, true); - unitTarget->CastSpell(unitTarget, 43059, true); - return; - } - case 43069: // Towers of Certain Doom: Skorn Cannonfire - { - // Towers of Certain Doom: Tower Caster Instakill - m_caster->CastSpell(m_caster, 43072, true); - return; - } - case 43096: // Summon All Players - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - m_caster->CastSpell(unitTarget, 43097, true); - return; - } - case 43144: // Hatch All Eggs - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, 42493, true, NULL, NULL, m_caster->GetObjectGuid()); - return; - } - case 43209: // Place Ram Meat - { - if (!unitTarget) - return; - - // Self Visual - Sleep Until Cancelled (DND) - unitTarget->RemoveAurasDueToSpell(6606); - return; - } - case 43498: // Siphon Soul - { - // This spell should cast the next spell only for one (player)target, however it should hit multiple targets, hence this kind of implementation - if (!unitTarget || m_UniqueTargetInfo.rbegin()->targetGUID != unitTarget->GetObjectGuid()) - return; - - std::vector possibleTargets; - possibleTargets.reserve(m_UniqueTargetInfo.size()); - for (TargetList::const_iterator itr = m_UniqueTargetInfo.begin(); itr != m_UniqueTargetInfo.end(); ++itr) - { - // Skip Non-Players - if (!itr->targetGUID.IsPlayer()) - continue; - - if (Unit* target = m_caster->GetMap()->GetPlayer(itr->targetGUID)) - possibleTargets.push_back(target); - } - - // Cast Siphon Soul channeling spell - if (!possibleTargets.empty()) - m_caster->CastSpell(possibleTargets[urand(0, possibleTargets.size() - 1)], 43501, false); - - return; - } - case 43572: // Send Them Packing: On /Raise Emote Dummy to Player - { - if (!unitTarget) - return; - - // m_caster (creature) should start walking back to it's "home" here, no clear way how to do that - - // Send Them Packing: On Successful Dummy Spell Kill Credit - m_caster->CastSpell(unitTarget, 42721, true); - return; - } - // Demon Broiled Surprise - case 43723: - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - ((Player*)m_caster)->CastSpell(unitTarget, 43753, true, m_CastItem, NULL, m_originalCasterGUID, m_spellInfo); - return; - } - case 43882: // Scourging Crystal Controller Dummy - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - // see spell dummy 50133 - unitTarget->RemoveAurasDueToSpell(43874); - return; - } - case 44454: // Tasty Reef Fish - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - m_caster->CastSpell(unitTarget, 44455, true, m_CastItem); - return; - } - case 44869: // Spectral Blast - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - // If target has spectral exhaustion or spectral realm aura return - if (unitTarget->HasAura(44867) || unitTarget->HasAura(46021)) - return; - - // Cast the spectral realm effect spell, visual spell and spectral blast rift summoning - unitTarget->CastSpell(unitTarget, 44866, true, NULL, NULL, m_caster->GetObjectGuid()); - unitTarget->CastSpell(unitTarget, 46648, true, NULL, NULL, m_caster->GetObjectGuid()); - unitTarget->CastSpell(unitTarget, 44811, true); - return; - } - case 44875: // Complete Raptor Capture - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - Creature* creatureTarget = (Creature*)unitTarget; - - creatureTarget->ForcedDespawn(); - - // cast spell Raptor Capture Credit - m_caster->CastSpell(m_caster, 42337, true, NULL); - return; - } - case 44997: // Converting Sentry - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - Creature* creatureTarget = (Creature*)unitTarget; - - creatureTarget->ForcedDespawn(); - - // Converted Sentry Credit - m_caster->CastSpell(m_caster, 45009, true); - return; - } - case 45030: // Impale Emissary - { - // Emissary of Hate Credit - m_caster->CastSpell(m_caster, 45088, true); - return; - } - case 45449: // Arcane Prisoner Rescue - { - uint32 spellId = 0; - switch (rand() % 2) - { - case 0: spellId = 45446; break; // Summon Arcane Prisoner - Male - case 1: spellId = 45448; break; // Summon Arcane Prisoner - Female - } - // Spawn - m_caster->CastSpell(m_caster, spellId, true); - - if (!unitTarget) return; - // Arcane Prisoner Kill Credit - unitTarget->CastSpell(m_caster, 45456, true); - - break; - } - case 45583: // Throw Gnomish Grenade - { - if (!unitTarget || m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - ((Player*)m_caster)->KilledMonsterCredit(unitTarget->GetEntry(), unitTarget->GetObjectGuid()); - - // look for gameobject within max spell range of unitTarget, and respawn if found - - // big fire - GameObject* pGo = NULL; - - float fMaxDist = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)); - - MaNGOS::NearestGameObjectEntryInPosRangeCheck go_check_big(*unitTarget, 187675, unitTarget->GetPositionX(), unitTarget->GetPositionY(), unitTarget->GetPositionZ(), fMaxDist); - MaNGOS::GameObjectSearcher checker1(pGo, go_check_big); - - Cell::VisitGridObjects(unitTarget, checker1, fMaxDist); - - if (pGo && !pGo->isSpawned()) - { - pGo->SetRespawnTime(MINUTE / 2); - pGo->Refresh(); - } - - // small fire - std::list lList; - - MaNGOS::GameObjectEntryInPosRangeCheck go_check_small(*unitTarget, 187676, unitTarget->GetPositionX(), unitTarget->GetPositionY(), unitTarget->GetPositionZ(), fMaxDist); - MaNGOS::GameObjectListSearcher checker2(lList, go_check_small); - - Cell::VisitGridObjects(unitTarget, checker2, fMaxDist); - - for (std::list::iterator iter = lList.begin(); iter != lList.end(); ++iter) - { - if (!(*iter)->isSpawned()) - { - (*iter)->SetRespawnTime(MINUTE / 2); - (*iter)->Refresh(); - } - } - - return; - } - case 45685: // Magnataur On Death 2 - { - m_caster->RemoveAurasDueToSpell(45673); - m_caster->RemoveAurasDueToSpell(45672); - m_caster->RemoveAurasDueToSpell(45677); - m_caster->RemoveAurasDueToSpell(45681); - m_caster->RemoveAurasDueToSpell(45683); - return; - } - case 45958: // Signal Alliance - { - m_caster->CastSpell(m_caster, effect->CalculateSimpleValue(), true); - return; - } - case 45976: // Open Portal - case 46177: // Open All Portals - { - if (!unitTarget) - return; - - // portal visual - unitTarget->CastSpell(unitTarget, 45977, true); - - // break in case additional procressing in scripting library required - break; - } - case 45980: // Re-Cursive Transmatter Injection - { - if (m_caster->GetTypeId() == TYPEID_PLAYER && unitTarget) - { - if (const SpellEntry* pSpell = sSpellStore.LookupEntry(46022)) - { - m_caster->CastSpell(unitTarget, pSpell, true); - SpellEffectEntry const* killSpellEffect = pSpell->GetSpellEffect(EFFECT_INDEX_0); - ((Player*)m_caster)->KilledMonsterCredit(killSpellEffect ? killSpellEffect->EffectMiscValue : 0); - } - - if (unitTarget->GetTypeId() == TYPEID_UNIT) - ((Creature*)unitTarget)->ForcedDespawn(); - } - - return; - } - case 45989: // Summon Void Sentinel Summoner Visual - { - if (!unitTarget) - return; - - // summon void sentinel - unitTarget->CastSpell(unitTarget, 45988, true); - - return; - } - case 45990: // Collect Oil - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - if (const SpellEntry* pSpell = sSpellStore.LookupEntry(45991)) - { - unitTarget->CastSpell(unitTarget, pSpell, true); - ((Creature*)unitTarget)->ForcedDespawn(m_duration); - } - - return; - } - case 46167: // Planning for the Future: Create Snowfall Glade Pup Cover - case 50918: // Gluttonous Lurkers: Create Basilisk Crystals Cover - case 50926: // Gluttonous Lurkers: Create Zul'Drak Rat Cover - case 51026: // Create Drakkari Medallion Cover - case 51592: // Pickup Primordial Hatchling - case 51961: // Captured Chicken Cover - case 55364: // Create Ghoul Drool Cover - case 61832: // Rifle the Bodies: Create Magehunter Personal Effects Cover - case 74904: // Pickup Sen'jin Frog - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT || m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - uint32 spellId = 0; - - switch (m_spellInfo->Id) - { - case 46167: spellId = 46773; break; - case 50918: spellId = 50919; break; - case 50926: spellId = 50927; break; - case 51026: spellId = 50737; break; - case 51592: spellId = 51593; break; - case 51961: spellId = 51037; break; - case 55364: spellId = 55363; break; - case 61832: spellId = 47096; break; - case 74904: spellId = 74905; break; - } - - if (const SpellEntry* pSpell = sSpellStore.LookupEntry(spellId)) - { - unitTarget->CastSpell(m_caster, spellId, true); - - Creature* creatureTarget = (Creature*)unitTarget; - - if (const SpellCastTimesEntry* pCastTime = sSpellCastTimesStore.LookupEntry(pSpell->CastingTimeIndex)) - creatureTarget->ForcedDespawn(pCastTime->CastTime + 1); - } - return; - } - case 46171: // Scuttle Wrecked Flying Machine - { - if (!unitTarget || m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - ((Player*)m_caster)->KilledMonsterCredit(unitTarget->GetEntry(), unitTarget->GetObjectGuid()); - - // look for gameobject within max spell range of unitTarget, and respawn if found - GameObject* pGo = NULL; - - float fMaxDist = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)); - - MaNGOS::NearestGameObjectEntryInPosRangeCheck go_check(*unitTarget, 187675, unitTarget->GetPositionX(), unitTarget->GetPositionY(), unitTarget->GetPositionZ(), fMaxDist); - MaNGOS::GameObjectSearcher checker(pGo, go_check); - - Cell::VisitGridObjects(unitTarget, checker, fMaxDist); - - if (pGo && !pGo->isSpawned()) - { - pGo->SetRespawnTime(MINUTE / 2); - pGo->Refresh(); - } - - return; - } - case 46372: // Ice Spear Target Picker - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - m_caster->CastSpell(unitTarget, 46359, true); - return; - } - case 46289: // Negative Energy - { - if (!unitTarget) - return; - - m_caster->CastSpell(unitTarget, 46285, true); - return; - } - case 46430: // Synch Health - { - if (!unitTarget) - return; - - unitTarget->SetHealth(m_caster->GetHealth()); - return; - } - case 46485: // Greatmother's Soulcatcher - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - if (const SpellEntry* pSpell = sSpellStore.LookupEntry(46486)) - { - m_caster->CastSpell(unitTarget, pSpell, true); - - if (SpellEffectEntry const* pSpellEffect = pSpell->GetSpellEffect(EFFECT_INDEX_0)) - if (const SpellEntry *pSpellCredit = sSpellStore.LookupEntry(pSpellEffect->EffectTriggerSpell)) - if(SpellEffectEntry const* pSpellCreditEffect = pSpellCredit->GetSpellEffect(EFFECT_INDEX_0)) - ((Player*)m_caster)->KilledMonsterCredit(pSpellCreditEffect->EffectMiscValue); - - ((Creature*)unitTarget)->ForcedDespawn(); - } - - return; - } - case 46606: // Plague Canister Dummy - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - unitTarget->CastSpell(m_caster, 43160, true); - unitTarget->SetDeathState(JUST_DIED); - unitTarget->SetHealth(0); - return; - } - case 46671: // Cleansing Flames (Exodar) - case 46672: // Cleansing Flames (Silvermoon) - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - unitTarget->CastSpell(unitTarget, m_spellInfo->Id == 46671 ? 46690 : 46689, true); - return; - } - case 46797: // Quest - Borean Tundra - Set Explosives Cart - { - if (!unitTarget || m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - ((Player*)m_caster)->KilledMonsterCredit(unitTarget->GetEntry(), unitTarget->GetObjectGuid()); - - // Quest - Borean Tundra - Summon Explosives Cart - unitTarget->CastSpell(unitTarget, 46798, true); - return; - } - case 47110: // Summon Drakuru's Image - { - uint32 spellId = 0; - - // Spell 47117,47149,47316,47405,50439 exist, are these used to check area/meet requirement - // and to cast correct spell in correct area? - - switch (m_caster->GetAreaId()) - { - case 4255: spellId = 47381; break; // Reagent Check (Frozen Mojo) - case 4209: spellId = 47386; break; // Reagent Check (Zim'Bo's Mojo) - case 4270: spellId = 47389; break; // Reagent Check (Desperate Mojo) - case 4216: spellId = 47408; break; // Reagent Check (Sacred Mojo) - case 4196: spellId = 50441; break; // Reagent Check (Survival Mojo) - } - - // The additional castspell arguments are needed here to remove reagents for triggered spells - if (spellId) - m_caster->CastSpell(m_caster, spellId, true, m_CastItem, NULL, m_caster->GetObjectGuid(), m_spellInfo); - - return; - } - case 47170: // Impale Leviroth - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - unitTarget->SetHealthPercent(8.0f); - - // Cosmetic - Underwater Blood (no sound) - unitTarget->CastSpell(unitTarget, 47172, true); - - ((Creature*)unitTarget)->AI()->AttackStart(m_caster); - return; - } - case 47176: // Infect Ice Troll - { - // Spell has wrong areaGroupid, so it can not be casted where expected. - // TODO: research if spells casted by NPC, having TARGET_SCRIPT, can have disabled area check - if (!unitTarget) - return; - - // Plague Effect Self - unitTarget->CastSpell(unitTarget, 47178, true); - return; - } - case 47305: // Potent Explosive Charge - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - // only if below 80% hp - if (unitTarget->GetHealthPercent() > 80.0f) - return; - - // Issues with explosion animation (remove insta kill spell resolves the issue) - - // Quest - Jormungar Explosion Spell Spawner - unitTarget->CastSpell(unitTarget, 47311, true); - - // Potent Explosive Charge - unitTarget->CastSpell(unitTarget, 47306, true); - - return; - } - case 47381: // Reagent Check (Frozen Mojo) - case 47386: // Reagent Check (Zim'Bo's Mojo) - case 47389: // Reagent Check (Desperate Mojo) - case 47408: // Reagent Check (Sacred Mojo) - case 50441: // Reagent Check (Survival Mojo) - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - switch (m_spellInfo->Id) - { - case 47381: - // Envision Drakuru - m_caster->CastSpell(m_caster, 47118, true); - break; - case 47386: - m_caster->CastSpell(m_caster, 47150, true); - break; - case 47389: - m_caster->CastSpell(m_caster, 47317, true); - break; - case 47408: - m_caster->CastSpell(m_caster, 47406, true); - break; - case 50441: - m_caster->CastSpell(m_caster, 50440, true); - break; - } - - return; - } - case 48046: // Use Camera - { - if (!unitTarget) - return; - - // No despawn expected, nor any change in dynamic flags/other flags. - // Need internal way to track if credit has been given for this object. - - // Iron Dwarf Snapshot Credit - m_caster->CastSpell(m_caster, 48047, true, m_CastItem, NULL, unitTarget->GetObjectGuid()); - return; - } - case 48790: // Neltharion's Flame - { - if (!unitTarget) - return; - - // Neltharion's Flame Fire Bunny: Periodic Fire Aura - unitTarget->CastSpell(unitTarget, 48786, false); - return; - } - case 49357: // Brewfest Mount Transformation - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - if (!m_caster->HasAuraType(SPELL_AURA_MOUNTED)) - return; - - m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); - - // Ram for Alliance, Kodo for Horde - if (((Player*)m_caster)->GetTeam() == ALLIANCE) - { - if (m_caster->GetSpeedRate(MOVE_RUN) >= 2.0f) - // 100% Ram - m_caster->CastSpell(m_caster, 43900, true); - else - // 60% Ram - m_caster->CastSpell(m_caster, 43899, true); - } - else - { - if (((Player*)m_caster)->GetSpeedRate(MOVE_RUN) >= 2.0f) - // 100% Kodo - m_caster->CastSpell(m_caster, 49379, true); - else - // 60% Kodo - m_caster->CastSpell(m_caster, 49378, true); - } - return; - } - case 49634: // Sergeant's Flare - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - // Towers of Certain Doom: Tower Bunny Smoke Flare Effect - // TODO: MaNGOS::DynamicObjectUpdater::VisitHelper prevent aura to be applied to dummy creature (see HandleAuraDummy for effect of aura) - m_caster->CastSpell(unitTarget, 56511, true); - - static uint32 const spellCredit[4] = - { - 43077, // E Kill Credit - 43067, // NW Kill Credit - 43087, // SE Kill Credit - 43086, // SW Kill Credit - }; - - // for sizeof(spellCredit) - for (int i = 0; i < 4; ++i) - { - const SpellEntry* pSpell = sSpellStore.LookupEntry(spellCredit[i]); - - if (pSpell->GetEffectMiscValue(EFFECT_INDEX_0) == unitTarget->GetEntry()) - { - m_caster->CastSpell(m_caster, spellCredit[i], true); - break; - } - } - - return; - } - case 49859: // Rune of Command - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - // Captive Stone Giant Kill Credit - unitTarget->CastSpell(m_caster, 43564, true); - // Is it supposed to despawn? - ((Creature*)unitTarget)->ForcedDespawn(); - return; - } - case 50133: // Scourging Crystal Controller - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - // Scourge Mur'gul Camp: Force Shield Arcane Purple x3 - if (unitTarget->HasAura(43874)) - { - // someone else is already channeling target - if (unitTarget->HasAura(43878)) - return; - - // Scourging Crystal Controller - m_caster->CastSpell(unitTarget, 43878, true, m_CastItem); - } - - return; - } - case 50243: // Teach Language - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - // spell has a 1/3 chance to trigger one of the below - if (roll_chance_i(66)) - return; - - if (((Player*)m_caster)->GetTeam() == ALLIANCE) - { - // 1000001 - gnomish binary - m_caster->CastSpell(m_caster, 50242, true); - } - else - { - // 01001000 - goblin binary - m_caster->CastSpell(m_caster, 50246, true); - } - - return; - } - case 50440: // Envision Drakuru - { - if (!unitTarget) - return; - - // Script Cast Summon Image of Drakuru 05 - unitTarget->CastSpell(unitTarget, 50439, true); - return; - } - case 50546: // Ley Line Focus Control Ring Effect - case 50547: // Ley Line Focus Control Amulet Effect - case 50548: // Ley Line Focus Control Talisman Effect - { - if (!m_originalCaster || !unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - switch (m_spellInfo->Id) - { - case 50546: unitTarget->CastSpell(m_originalCaster, 47390, true); break; - case 50547: unitTarget->CastSpell(m_originalCaster, 47472, true); break; - case 50548: unitTarget->CastSpell(m_originalCaster, 47635, true); break; - } - - return; - } - case 51276: // Incinerate Corpse - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - unitTarget->CastSpell(unitTarget, 51278, true); - unitTarget->CastSpell(m_caster, 51279, true); - - unitTarget->SetDeathState(JUST_DIED); - return; - } - case 51330: // Shoot RJR - { - if (!unitTarget) - return; - - // guessed chances - if (roll_chance_i(75)) - m_caster->CastSpell(unitTarget, roll_chance_i(50) ? 51332 : 51366, true, m_CastItem); - else - m_caster->CastSpell(unitTarget, 51331, true, m_CastItem); - - return; - } - case 51333: // Dig For Treasure - { - if (!unitTarget) - return; - - if (roll_chance_i(75)) - m_caster->CastSpell(unitTarget, 51370, true, m_CastItem); - else - m_caster->CastSpell(m_caster, 51345, true); - - return; - } - case 51336: // Magic Pull - { - if (!unitTarget) - return; - - m_caster->CastSpell(unitTarget, 50770, true); - return; - } - case 51420: // Digging for Treasure Ping - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - // only spell related protector pets exist currently - Pet* pPet = m_caster->GetProtectorPet(); - if (!pPet) - return; - - pPet->SetFacingToObject(unitTarget); - - // Digging for Treasure - pPet->CastSpell(unitTarget, 51405, true); - - ((Creature*)unitTarget)->ForcedDespawn(1); - return; - } - case 51582: // Rocket Boots Engaged (Rocket Boots Xtreme and Rocket Boots Xtreme Lite) - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - if (BattleGround* bg = ((Player*)m_caster)->GetBattleGround()) - bg->EventPlayerDroppedFlag((Player*)m_caster); - - m_caster->CastSpell(m_caster, 30452, true, NULL); - return; - } - case 51840: // Despawn Fruit Tosser - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - if (roll_chance_i(20)) - { - // summon NPC, or... - unitTarget->CastSpell(m_caster, 52070, true); - } - else - { - // ...drop banana, orange or papaya - switch (urand(0, 2)) - { - case 0: unitTarget->CastSpell(m_caster, 51836, true); break; - case 1: unitTarget->CastSpell(m_caster, 51837, true); break; - case 2: unitTarget->CastSpell(m_caster, 51839, true); break; - } - } - - ((Creature*)unitTarget)->ForcedDespawn(5000); - return; - } - case 51866: // Kick Nass - { - // It is possible that Nass Heartbeat (spell id 61438) is involved in this - // If so, unclear how it should work and using the below instead (even though it could be a bit hack-ish) - - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - // Only own guardian pet - if (m_caster != unitTarget->GetOwner()) - return; - - // This means we already set state (see below) and need to wait. - if (unitTarget->hasUnitState(UNIT_STAT_ROOT)) - return; - - // Expecting pTargetDummy to be summoned by AI at death of target creatures. - - Creature* pTargetDummy = NULL; - float fRange = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)); - - MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck u_check(*m_caster, 28523, true, false, fRange * 2); - MaNGOS::CreatureLastSearcher searcher(pTargetDummy, u_check); - - Cell::VisitGridObjects(m_caster, searcher, fRange * 2); - - if (pTargetDummy) - { - if (unitTarget->hasUnitState(UNIT_STAT_FOLLOW | UNIT_STAT_FOLLOW_MOVE)) - unitTarget->GetMotionMaster()->MovementExpired(); - - unitTarget->MonsterMoveWithSpeed(pTargetDummy->GetPositionX(), pTargetDummy->GetPositionY(), pTargetDummy->GetPositionZ(), 24.f); - - // Add state to temporarily prevent follow - unitTarget->addUnitState(UNIT_STAT_ROOT); - - // Collect Hair Sample - unitTarget->CastSpell(pTargetDummy, 51870, true); - } - - return; - } - case 51872: // Hair Sample Collected - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - // clear state to allow follow again - m_caster->clearUnitState(UNIT_STAT_ROOT); - - // Nass Kill Credit - m_caster->CastSpell(m_caster, 51871, true); - - // Despawn dummy creature - ((Creature*)unitTarget)->ForcedDespawn(); - - return; - } - case 51964: // Tormentor's Incense - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - // This might not be the best way, and effect may need some adjustment. Removal of any aura from surrounding dummy creatures? - if (((Creature*)unitTarget)->AI()) - ((Creature*)unitTarget)->AI()->AttackStart(m_caster); - - return; - } - case 51858: // Siphon of Acherus - { - if (!m_originalCaster || !unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - if (Player* pPlayer = m_originalCaster->GetCharmerOrOwnerPlayerOrPlayerItself()) - pPlayer->KilledMonsterCredit(unitTarget->GetEntry(), unitTarget->GetObjectGuid()); - - return; - } - case 52308: // Take Sputum Sample - { - switch(effect->EffectIndex) - { - case EFFECT_INDEX_0: - { - uint32 spellID = m_spellInfo->CalculateSimpleValue(EFFECT_INDEX_0); - uint32 reqAuraID = m_spellInfo->CalculateSimpleValue(EFFECT_INDEX_1); - - if (m_caster->HasAura(reqAuraID, EFFECT_INDEX_0)) - m_caster->CastSpell(m_caster, spellID, true, NULL); - return; - } - case EFFECT_INDEX_1: // additional data for dummy[0] - case EFFECT_INDEX_2: - return; - } - return; - } - case 52369: // Detonate Explosives - case 52371: // Detonate Explosives - { - if (!unitTarget) - return; - - // Cosmetic - Explosion - unitTarget->CastSpell(unitTarget, 46419, true); - - // look for gameobjects within max spell range of unitTarget, and respawn if found - std::list lList; - - float fMaxDist = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)); - - MaNGOS::GameObjectEntryInPosRangeCheck go_check(*unitTarget, 182071, unitTarget->GetPositionX(), unitTarget->GetPositionY(), unitTarget->GetPositionZ(), fMaxDist); - MaNGOS::GameObjectListSearcher checker(lList, go_check); - - Cell::VisitGridObjects(unitTarget, checker, fMaxDist); - - for (std::list::iterator iter = lList.begin(); iter != lList.end(); ++iter) - { - if (!(*iter)->isSpawned()) - { - (*iter)->SetRespawnTime(MINUTE / 2); - (*iter)->Refresh(); - } - } - - return; - } - case 52759: // Ancestral Awakening - { - if (!unitTarget) - return; - - m_caster->CastCustomSpell(unitTarget, 52752, &damage, NULL, NULL, true); - return; - } - case 52845: // Brewfest Mount Transformation (Faction Swap) - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - if (!m_caster->HasAuraType(SPELL_AURA_MOUNTED)) - return; - - m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); - - // Ram for Horde, Kodo for Alliance - if (((Player*)m_caster)->GetTeam() == HORDE) - { - if (m_caster->GetSpeedRate(MOVE_RUN) >= 2.0f) - // Swift Brewfest Ram, 100% Ram - m_caster->CastSpell(m_caster, 43900, true); - else - // Brewfest Ram, 60% Ram - m_caster->CastSpell(m_caster, 43899, true); - } - else - { - if (((Player*)m_caster)->GetSpeedRate(MOVE_RUN) >= 2.0f) - // Great Brewfest Kodo, 100% Kodo - m_caster->CastSpell(m_caster, 49379, true); - else - // Brewfest Riding Kodo, 60% Kodo - m_caster->CastSpell(m_caster, 49378, true); - } - return; - } - case 53341: // Rune of Cinderglacier - case 53343: // Rune of Razorice - { - // Runeforging Credit - m_caster->CastSpell(m_caster, 54586, true); - return; - } - case 53475: // Set Oracle Faction Friendly - case 53487: // Set Wolvar Faction Honored - case 54015: // Set Oracle Faction Honored - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - if (effect->EffectIndex == EFFECT_INDEX_0) - { - Player* pPlayer = (Player*)m_caster; - - uint32 faction_id = m_currentBasePoints[effect->EffectIndex]; - int32 rep_change = m_currentBasePoints[EFFECT_INDEX_1]; - - FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction_id); - - if (!factionEntry) - return; - - // Set rep to baserep + basepoints (expecting spillover for oposite faction -> become hated) - // Not when player already has equal or higher rep with this faction - if (pPlayer->GetReputationMgr().GetBaseReputation(factionEntry) < rep_change) - pPlayer->GetReputationMgr().SetReputation(factionEntry, rep_change); - - // EFFECT_INDEX_2 most likely update at war state, we already handle this in SetReputation - } - - return; - } - case 53808: // Pygmy Oil - { - const uint32 spellShrink = 53805; - const uint32 spellTransf = 53806; - - if (SpellAuraHolder* holder = m_caster->GetSpellAuraHolder(spellShrink)) - { - // chance to become pygmified (5, 10, 15 etc) - if (roll_chance_i(holder->GetStackAmount() * 5)) - { - m_caster->RemoveAurasDueToSpell(spellShrink); - m_caster->CastSpell(m_caster, spellTransf, true); - return; - } - } - - if (m_caster->HasAura(spellTransf)) - return; - - m_caster->CastSpell(m_caster, spellShrink, true); - return; - } - case 54577: // Throw U.D.E.D. - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - // Sometimes issues with explosion animation. Unclear why - // but possibly caused by the order of spells. - - // Permanent Feign Death - unitTarget->CastSpell(unitTarget, 29266, true); - - // need to despawn later - ((Creature*)unitTarget)->ForcedDespawn(2000); - - // Mammoth Explosion Spell Spawner - unitTarget->CastSpell(unitTarget, 54581, true, m_CastItem); - return; - } - case 54850: // Emerge - { - // Cast Emerge summon - m_caster->CastSpell(m_caster, 54851, true); - return; - } - case 54092: // Monster Slayer's Kit - { - if (!unitTarget) - return; - - uint32 spellIds[] = {51853, 54063, 54071, 54086}; - m_caster->CastSpell(unitTarget, spellIds[urand(0, 3)], true); - return; - } - case 55004: // Nitro Boosts - { - if (!m_CastItem) - return; - - if (roll_chance_i(95)) // Nitro Boosts - success - m_caster->CastSpell(m_caster, 54861, true, m_CastItem); - else // Knocked Up - backfire 5% - m_caster->CastSpell(m_caster, 46014, true, m_CastItem); - - return; - } - case 55818: // Hurl Boulder - { - // unclear how many summon min/max random, best guess below - uint32 random = urand(3, 5); - - for (uint32 i = 0; i < random; ++i) - m_caster->CastSpell(m_caster, 55528, true); - - return; - } - case 56430: // Arcane Bomb - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, 56431, true, NULL, NULL, m_caster->GetObjectGuid()); - unitTarget->CastSpell(unitTarget, 56432, true, NULL, NULL, m_caster->GetObjectGuid()); - return; - } - case 57578: // Lava Strike - { - if (!unitTarget) - return; - - m_caster->CastSpell(unitTarget, effect->CalculateSimpleValue(), true); - return; - } - case 57908: // Stain Cloth - { - // nothing do more - finish(); - - m_caster->CastSpell(m_caster, 57915, false, m_CastItem); - - // cast item deleted - ClearCastItem(); - break; - } - case 58418: // Portal to Orgrimmar - case 58420: // Portal to Stormwind - return; // implemented in EffectScript[0] - case 58601: // Remove Flight Auras - { - m_caster->RemoveSpellsCausingAura(SPELL_AURA_FLY); - m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_FLIGHT_SPEED_MOUNTED); - return; - } - case 59640: // Underbelly Elixir - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - uint32 spell_id = 0; - switch (urand(1, 3)) - { - case 1: spell_id = 59645; break; - case 2: spell_id = 59831; break; - case 3: spell_id = 59843; break; - } - - m_caster->CastSpell(m_caster, spell_id, true, NULL); - return; - } - case 60932: // Disengage (one from creature versions) - { - if (!unitTarget) - return; - - m_caster->CastSpell(unitTarget, 60934, true, NULL); - return; - } - case 62105: // To'kini's Blowgun - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - // Sleeping Sleep - unitTarget->CastSpell(unitTarget, 62248, true); - - // Although not really correct, it's needed to have access to m_caster later, - // to properly process spell 62110 (cast from gossip). - // Can possibly be replaced with a similar function that doesn't set any dynamic flags. - ((Creature*)unitTarget)->SetLootRecipient(m_caster); - - unitTarget->setFaction(190); // Ambient (neutral) - unitTarget->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); - return; - } - case 62278: // Lightning Orb Charger - { - if (!unitTarget) - return; - - unitTarget->CastSpell(m_caster, 62466, true); - unitTarget->CastSpell(unitTarget, 62279, true); - return; - } - case 62797: // Storm Cloud - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - m_caster->CastSpell(unitTarget, m_caster->GetMap()->IsRegularDifficulty() ? 65123 : 65133, true); - return; - } - case 62907: // Freya's Ward - { - if (!unitTarget) - return; - - for (uint8 i = 0; i < 5; ++i) - m_caster->CastSpell(unitTarget, effect->CalculateSimpleValue(), true); - return; - } - case 63499: // Dispel Magic - { - if (!unitTarget) - return; - - unitTarget->RemoveAurasDueToSpell(effect->CalculateSimpleValue()); - return; - } - case 63545: // Icicle - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - m_caster->CastSpell(unitTarget, effect->CalculateSimpleValue(), true); - } - case 63820: // Summon Scrap Bot Trigger (Ulduar - Mimiron) for Scrap Bots - case 64425: // Summon Scrap Bot Trigger (Ulduar - Mimiron) for Assault Bots - case 64620: // Summon Fire Bot Trigger (Ulduar - Mimiron) for Fire Bots - { - if (!unitTarget) - return; - - uint32 triggerSpell = 0; - switch (m_spellInfo->Id) - { - case 63820: triggerSpell = 64398; break; - case 64425: triggerSpell = 64426; break; - case 64620: triggerSpell = 64621; break; - } - unitTarget->CastSpell(unitTarget, triggerSpell, false); - return; - } - case 64385: // Spinning (from Unusual Compass) - { - m_caster->SetFacingTo(frand(0, M_PI_F * 2)); - return; - } - case 64489: // Feral Rush - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - m_caster->CastSpell(unitTarget, 64496, true); - return; - } - case 64543: // Melt Ice - { - if (!unitTarget) - return; - - m_caster->CastSpell(unitTarget, effect->CalculateSimpleValue(), true); - m_caster->CastSpell(m_caster, 64540, true); - return; - } - case 64673: // Feral Rush (h) - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - m_caster->CastSpell(unitTarget, 64674, true); - return; - } - case 64981: // Summon Random Vanquished Tentacle - { - uint32 spell_id = 0; - - switch (urand(0, 2)) - { - case 0: spell_id = 64982; break; // Summon Vanquished Crusher Tentacle - case 1: spell_id = 64983; break; // Summon Vanquished Constrictor Tentacle - case 2: spell_id = 64984; break; // Summon Vanquished Corruptor Tentacle - } - - m_caster->CastSpell(m_caster, spell_id, true); - return; - } - case 66390: // Read Last Rites - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT || m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - // Summon Tualiq Proxy - // Not known what purpose this has - unitTarget->CastSpell(unitTarget, 66411, true); - - // Summon Tualiq Spirit - // Offtopic note: the summoned has aura from spell 37119 and 66419. One of them should - // most likely make summoned "rise", hover up/sideways in the air (MOVEFLAG_LEVITATING + MOVEFLAG_HOVER) - unitTarget->CastSpell(unitTarget, 66412, true); - - ((Player*)m_caster)->KilledMonsterCredit(unitTarget->GetEntry(), unitTarget->GetObjectGuid()); - - // Must have a delay for proper spell animation - ((Creature*)unitTarget)->ForcedDespawn(1000); - return; - } - case 67019: // Flask of the North - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - uint32 spell_id = 0; - switch (m_caster->getClass()) - { - case CLASS_WARRIOR: - case CLASS_DEATH_KNIGHT: - spell_id = 67018; // STR for Warriors, Death Knights - break; - case CLASS_ROGUE: - case CLASS_HUNTER: - spell_id = 67017; // AP for Rogues, Hunters - break; - case CLASS_PRIEST: - case CLASS_MAGE: - case CLASS_WARLOCK: - spell_id = 67016; // SPD for Priests, Mages, Warlocks - break; - case CLASS_SHAMAN: - // random (SPD, AP) - spell_id = roll_chance_i(50) ? 67016 : 67017; - break; - case CLASS_PALADIN: - case CLASS_DRUID: - default: - // random (SPD, STR) - spell_id = roll_chance_i(50) ? 67016 : 67018; - break; - } - m_caster->CastSpell(m_caster, spell_id, true); - return; - } - case 69922: // Temper Quel'Delar - { - if (!unitTarget) - return; - - // Return Tempered Quel'Delar - unitTarget->CastSpell(m_caster, 69956, true); - return; - } - case 71445: // Twilight Bloodbolt - case 71471: // Twilight Bloodbolt - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - m_caster->CastSpell(unitTarget, 71818, true); - return; - } - case 71718: // Conjure Flame - case 72040: // Conjure Empowered Flame - { - if (!unitTarget) - return; - - m_caster->CastSpell(unitTarget, effect->CalculateSimpleValue(), true); - return; - } - case 71837: // Vampiric Bite - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - m_caster->CastSpell(unitTarget, 71726, true); - return; - } - case 71861: // Swarming Shadows - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - m_caster->CastSpell(unitTarget, 71264, true); - return; - } - case 72261: // Delirious Slash - { - if (!unitTarget) - return; - - m_caster->CastSpell(unitTarget, m_caster->CanReachWithMeleeAttack(unitTarget) ? 71623 : 72264, true); - return; - } - case 74452: // Conflagration - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - m_caster->CastSpell(unitTarget, 74453, true); - m_caster->CastSpell(unitTarget, 74454, true, NULL, NULL, m_caster->GetObjectGuid(), m_spellInfo); - return; - } - } - break; - } - case SPELLFAMILY_MAGE: - { - switch (m_spellInfo->Id) - { - case 11958: // Cold Snap - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - // immediately finishes the cooldown on Frost spells - const SpellCooldowns& cm = ((Player*)m_caster)->GetSpellCooldownMap(); - for (SpellCooldowns::const_iterator itr = cm.begin(); itr != cm.end();) - { - SpellEntry const* spellInfo = sSpellStore.LookupEntry(itr->first); - - if (spellInfo->GetSpellFamilyName() == SPELLFAMILY_MAGE && - (GetSpellSchoolMask(spellInfo) & SPELL_SCHOOL_MASK_FROST) && - spellInfo->Id != 11958 && GetSpellRecoveryTime(spellInfo) > 0) - { - ((Player*)m_caster)->RemoveSpellCooldown((itr++)->first, true); - } - else - ++itr; - } - return; - } - case 31687: // Summon Water Elemental - { - if (m_caster->HasAura(70937)) // Glyph of Eternal Water (permanent limited by known spells version) - m_caster->CastSpell(m_caster, 70908, true); - else // temporary version - m_caster->CastSpell(m_caster, 70907, true); - - return; - } - case 32826: // Polymorph Cast Visual - { - if (unitTarget && unitTarget->GetTypeId() == TYPEID_UNIT) - { - // Polymorph Cast Visual Rank 1 - const uint32 spell_list[6] = - { - 32813, // Squirrel Form - 32816, // Giraffe Form - 32817, // Serpent Form - 32818, // Dragonhawk Form - 32819, // Worgen Form - 32820 // Sheep Form - }; - unitTarget->CastSpell(unitTarget, spell_list[urand(0, 5)], true); - } - return; - } - case 38194: // Blink - { - // Blink - if (unitTarget) - m_caster->CastSpell(unitTarget, 38203, true); - - return; - } - } - - // Conjure Mana Gem - if (effect->EffectIndex == EFFECT_INDEX_1 && m_spellInfo->GetSpellEffectIdByIndex(EFFECT_INDEX_0) == SPELL_EFFECT_CREATE_ITEM) - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - // checked in create item check, avoid unexpected - if (Item* item = ((Player*)m_caster)->GetItemByLimitedCategory(ITEM_LIMIT_CATEGORY_MANA_GEM)) - if (item->HasMaxCharges()) - return; - - unitTarget->CastSpell( unitTarget, effect->CalculateSimpleValue(), true, m_CastItem); - return; - } - break; - } - case SPELLFAMILY_WARRIOR: - { - SpellClassOptionsEntry const* warClassOptions = m_spellInfo->GetSpellClassOptions(); - // Charge - if (warClassOptions && (warClassOptions->SpellFamilyFlags & UI64LIT(0x1)) && m_spellInfo->SpellVisual[0] == 867) - { - int32 chargeBasePoints0 = damage; - m_caster->CastCustomSpell(m_caster, 34846, &chargeBasePoints0, NULL, NULL, true); - return; - } - // Execute - if (warClassOptions && warClassOptions->SpellFamilyFlags & UI64LIT(0x20000000)) - { - if (!unitTarget) - return; - - uint32 rage = m_caster->GetPower(POWER_RAGE); - - // up to max 30 rage cost - if (rage > 300) - rage = 300; - - // Glyph of Execution bonus - uint32 rage_modified = rage; - - if (Aura* aura = m_caster->GetDummyAura(58367)) - rage_modified += aura->GetModifier()->m_amount * 10; - - int32 basePoints0 = damage+int32(rage_modified * effect->DmgMultiplier + - m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.2f); - - m_caster->CastCustomSpell(unitTarget, 20647, &basePoints0, NULL, NULL, true, 0); - - // Sudden Death - if (m_caster->HasAura(52437)) - { - Unit::AuraList const& auras = m_caster->GetAurasByType(SPELL_AURA_PROC_TRIGGER_SPELL); - for (Unit::AuraList::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) - { - // Only Sudden Death have this SpellIconID with SPELL_AURA_PROC_TRIGGER_SPELL - if ((*itr)->GetSpellProto()->SpellIconID == 1989) - { - // saved rage top stored in next affect - uint32 lastrage = (*itr)->GetSpellProto()->CalculateSimpleValue(EFFECT_INDEX_1) * 10; - if (lastrage < rage) - rage -= lastrage; - break; - } - } - } - - m_caster->SetPower(POWER_RAGE, m_caster->GetPower(POWER_RAGE) - rage); - return; - } - // Slam - if (warClassOptions && warClassOptions->SpellFamilyFlags & UI64LIT(0x0000000000200000)) - { - if (!unitTarget) - return; - - // dummy cast itself ignored by client in logs - m_caster->CastCustomSpell(unitTarget, 50782, &damage, NULL, NULL, true); - return; - } - // Concussion Blow - if (warClassOptions && warClassOptions->SpellFamilyFlags & UI64LIT(0x0000000004000000)) - { - m_damage += uint32(damage * m_caster->GetTotalAttackPowerValue(BASE_ATTACK) / 100); - return; - } - - switch (m_spellInfo->Id) - { - // Warrior's Wrath - case 21977: - { - if (!unitTarget) - return; - m_caster->CastSpell(unitTarget, 21887, true); // spell mod - return; - } - // Last Stand - case 12975: - { - int32 healthModSpellBasePoints0 = int32(m_caster->GetMaxHealth() * 0.3); - m_caster->CastCustomSpell(m_caster, 12976, &healthModSpellBasePoints0, NULL, NULL, true, NULL); - return; - } - // Bloodthirst - case 23881: - { - m_caster->CastCustomSpell(unitTarget, 23885, &damage, NULL, NULL, true, NULL); - return; - } - case 30012: // Move - { - if (!unitTarget || unitTarget->HasAura(39400)) - return; - - unitTarget->CastSpell(m_caster, 30253, true); - } - case 30284: // Change Facing - { - if (!unitTarget) - return; - - unitTarget->CastSpell(m_caster, 30270, true); - return; - } - case 37144: // Move (Chess event player knight move) - case 37146: // Move (Chess event player pawn move) - case 37148: // Move (Chess event player queen move) - case 37151: // Move (Chess event player rook move) - case 37152: // Move (Chess event player bishop move) - case 37153: // Move (Chess event player king move) - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - // cast generic move spell - m_caster->CastSpell(unitTarget, 30012, true); - return; - } - } - break; - } - case SPELLFAMILY_WARLOCK: - { - SpellClassOptionsEntry const* wrlClassOptions = m_spellInfo->GetSpellClassOptions(); - // Life Tap - if (wrlClassOptions && wrlClassOptions->SpellFamilyFlags & UI64LIT(0x0000000000040000)) - { - if (unitTarget && (int32(unitTarget->GetHealth()) > damage)) - { - // Shouldn't Appear in Combat Log - unitTarget->ModifyHealth(-damage); - - int32 spell_power = m_caster->SpellBaseDamageBonusDone(GetSpellSchoolMask(m_spellInfo)); - int32 mana = damage + spell_power / 2; - - // Improved Life Tap mod - Unit::AuraList const& auraDummy = m_caster->GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraList::const_iterator itr = auraDummy.begin(); itr != auraDummy.end(); ++itr) - if((*itr)->GetSpellProto()->GetSpellFamilyName()==SPELLFAMILY_WARLOCK && (*itr)->GetSpellProto()->SpellIconID == 208) - mana = ((*itr)->GetModifier()->m_amount + 100)* mana / 100; - - m_caster->CastCustomSpell(unitTarget, 31818, &mana, NULL, NULL, true); - - // Mana Feed - int32 manaFeedVal = 0; - Unit::AuraList const& mod = m_caster->GetAurasByType(SPELL_AURA_ADD_FLAT_MODIFIER); - for (Unit::AuraList::const_iterator itr = mod.begin(); itr != mod.end(); ++itr) - { - if((*itr)->GetSpellProto()->GetSpellFamilyName()==SPELLFAMILY_WARLOCK && (*itr)->GetSpellProto()->SpellIconID == 1982) - manaFeedVal+= (*itr)->GetModifier()->m_amount; - } - if (manaFeedVal > 0) - { - manaFeedVal = manaFeedVal * mana / 100; - m_caster->CastCustomSpell(m_caster, 32553, &manaFeedVal, NULL, NULL, true, NULL); - } - } - else - SendCastResult(SPELL_FAILED_FIZZLE); - - return; - } - break; - } - case SPELLFAMILY_PRIEST: - { - SpellClassOptionsEntry const* prtsClassOptions = m_spellInfo->GetSpellClassOptions(); - // Penance - if (prtsClassOptions && prtsClassOptions->SpellFamilyFlags & UI64LIT(0x0080000000000000)) - { - if (!unitTarget) - return; - - int hurt = 0; - int heal = 0; - switch (m_spellInfo->Id) - { - case 47540: hurt = 47758; heal = 47757; break; - case 53005: hurt = 53001; heal = 52986; break; - case 53006: hurt = 53002; heal = 52987; break; - case 53007: hurt = 53003; heal = 52988; break; - default: - sLog.outError("Spell::EffectDummy: Spell %u Penance need set correct heal/damage spell", m_spellInfo->Id); - return; - } - - // prevent interrupted message for main spell - finish(true); - - // replace cast by selected spell, this also make it interruptible including target death case - if (m_caster->IsFriendlyTo(unitTarget)) - m_caster->CastSpell(unitTarget, heal, false); - else - m_caster->CastSpell(unitTarget, hurt, false); - - return; - } - break; - - switch (m_spellInfo->Id) - { - case 21562: // Power Word: Fortitude - { - Unit* target = unitTarget; - if (!target) - target = m_caster; - - if (m_caster->GetTypeId() != TYPEID_PLAYER || target->GetTypeId() != TYPEID_PLAYER) - return; - - m_caster->CastSpell(target, ((Player*)m_caster)->GetGroup() != ((Player*)target)->GetGroup() ? 79104 : 79105, true, NULL, NULL, m_caster->GetObjectGuid()); - return; - } - case 27683: // Shadow Protection - { - Unit* target = unitTarget; - if (!target) - target = m_caster; - - if (m_caster->GetTypeId() != TYPEID_PLAYER || target->GetTypeId() != TYPEID_PLAYER) - return; - - m_caster->CastSpell(target, ((Player*)m_caster)->GetGroup() != ((Player*)target)->GetGroup() ? 79106 : 79107, true, NULL, NULL, m_caster->GetObjectGuid()); - return; - } - } - } - case SPELLFAMILY_DRUID: - { - // Starfall - if (m_spellInfo->IsFitToFamilyMask(UI64LIT(0x0000000000000000), 0x00000100)) - { - // Shapeshifting into an animal form or mounting cancels the effect. - if (m_caster->GetCreatureType() == CREATURE_TYPE_BEAST || m_caster->IsMounted()) - { - if (m_triggeredByAuraSpell) - m_caster->RemoveAurasDueToSpell(m_triggeredByAuraSpell->Id); - return; - } - - // Any effect which causes you to lose control of your character will supress the starfall effect. - if (m_caster->hasUnitState(UNIT_STAT_NO_FREE_MOVE)) - return; - - switch (m_spellInfo->Id) - { - case 50286: m_caster->CastSpell(unitTarget, 50288, true); return; - case 53196: m_caster->CastSpell(unitTarget, 53191, true); return; - case 53197: m_caster->CastSpell(unitTarget, 53194, true); return; - case 53198: m_caster->CastSpell(unitTarget, 53195, true); return; - default: - sLog.outError("Spell::EffectDummy: Unhandeled Starfall spell rank %u", m_spellInfo->Id); - return; - } - } - break; - } - case SPELLFAMILY_ROGUE: - { - switch (m_spellInfo->Id) - { - case 5938: // Shiv - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - Player* pCaster = ((Player*)m_caster); - - Item* item = pCaster->GetWeaponForAttack(OFF_ATTACK); - if (!item) - return; - - // all poison enchantments is temporary - uint32 enchant_id = item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT); - if (!enchant_id) - return; - - SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); - if (!pEnchant) - return; - - for (int s = 0; s < 3; ++s) - { - if (pEnchant->type[s] != ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL) - continue; - - SpellEntry const* combatEntry = sSpellStore.LookupEntry(pEnchant->spellid[s]); - if (!combatEntry || combatEntry->GetDispel() != DISPEL_POISON) - continue; - - m_caster->CastSpell(unitTarget, combatEntry, true, item); - } - - m_caster->CastSpell(unitTarget, 5940, true); - return; - } - case 14185: // Preparation - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - // immediately finishes the cooldown on certain Rogue abilities - const SpellCooldowns& cm = ((Player*)m_caster)->GetSpellCooldownMap(); - for (SpellCooldowns::const_iterator itr = cm.begin(); itr != cm.end();) - { - SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first); - SpellClassOptionsEntry const* prepClassOptions = spellInfo->GetSpellClassOptions(); - if (prepClassOptions && prepClassOptions->SpellFamilyName == SPELLFAMILY_ROGUE && (prepClassOptions->SpellFamilyFlags & UI64LIT(0x0000024000000860))) - ((Player*)m_caster)->RemoveSpellCooldown((itr++)->first,true); - else - ++itr; - } - return; - } - case 31231: // Cheat Death - { - // Cheating Death - m_caster->CastSpell(m_caster, 45182, true); - return; - } - case 51662: // Hunger for Blood - { - m_caster->CastSpell(m_caster, 63848, true); - return; - } - } - break; - } - case SPELLFAMILY_HUNTER: - { - SpellClassOptionsEntry const* huntClassOptions = m_spellInfo->GetSpellClassOptions(); - // Steady Shot - if (huntClassOptions && huntClassOptions->SpellFamilyFlags & UI64LIT(0x100000000)) - { - if (!unitTarget || !unitTarget->isAlive()) - return; - - bool found = false; - - // check dazed affect - Unit::AuraList const& decSpeedList = unitTarget->GetAurasByType(SPELL_AURA_MOD_DECREASE_SPEED); - for (Unit::AuraList::const_iterator iter = decSpeedList.begin(); iter != decSpeedList.end(); ++iter) - { - if ((*iter)->GetSpellProto()->SpellIconID==15 && (*iter)->GetSpellProto()->GetDispel()==0) - { - found = true; - break; - } - } - - if (found) - m_damage += damage; - return; - } - - // Disengage - if (huntClassOptions && huntClassOptions->SpellFamilyFlags & UI64LIT(0x0000400000000000)) - { - Unit* target = unitTarget; - uint32 spellid; - switch (m_spellInfo->Id) - { - case 57635: spellid = 57636; break; // one from creature cases - case 61507: spellid = 61508; break; // one from creature cases - default: - sLog.outError("Spell %u not handled propertly in EffectDummy(Disengage)", m_spellInfo->Id); - return; - } - if (!target || !target->isAlive()) - return; - m_caster->CastSpell(target, spellid, true, NULL); - } - - switch (m_spellInfo->Id) - { - case 23989: // Readiness talent - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - // immediately finishes the cooldown for hunter abilities - const SpellCooldowns& cm = ((Player*)m_caster)->GetSpellCooldownMap(); - for (SpellCooldowns::const_iterator itr = cm.begin(); itr != cm.end();) - { - SpellEntry const* spellInfo = sSpellStore.LookupEntry(itr->first); - - if (spellInfo->GetSpellFamilyName() == SPELLFAMILY_HUNTER && spellInfo->Id != 23989 && GetSpellRecoveryTime(spellInfo) > 0 ) - ((Player*)m_caster)->RemoveSpellCooldown((itr++)->first,true); - else - ++itr; - } - return; - } - case 37506: // Scatter Shot - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - // break Auto Shot and autohit - m_caster->InterruptSpell(CURRENT_AUTOREPEAT_SPELL); - m_caster->AttackStop(); - ((Player*)m_caster)->SendAttackSwingCancelAttack(); - return; - } - // Last Stand - case 53478: - { - if (!unitTarget) - return; - int32 healthModSpellBasePoints0 = int32(unitTarget->GetMaxHealth() * 0.3); - unitTarget->CastCustomSpell(unitTarget, 53479, &healthModSpellBasePoints0, NULL, NULL, true, NULL); - return; - } - // Master's Call - case 53271: - { - Pet* pet = m_caster->GetPet(); - if (!pet || !unitTarget) - return; - - pet->CastSpell(unitTarget, effect->CalculateSimpleValue(), true); - return; - } - } - break; - } - case SPELLFAMILY_PALADIN: - { - switch (m_spellInfo->SpellIconID) - { - case 156: // Holy Shock - { - if (!unitTarget) - return; - - int hurt = 0; - int heal = 0; - - switch (m_spellInfo->Id) - { - case 20473: hurt = 25912; heal = 25914; break; - case 20929: hurt = 25911; heal = 25913; break; - case 20930: hurt = 25902; heal = 25903; break; - case 27174: hurt = 27176; heal = 27175; break; - case 33072: hurt = 33073; heal = 33074; break; - case 48824: hurt = 48822; heal = 48820; break; - case 48825: hurt = 48823; heal = 48821; break; - default: - sLog.outError("Spell::EffectDummy: Spell %u not handled in HS", m_spellInfo->Id); - return; - } - - if (m_caster->IsFriendlyTo(unitTarget)) - m_caster->CastSpell(unitTarget, heal, true); - else - m_caster->CastSpell(unitTarget, hurt, true); - - return; - } - case 561: // Judgement of command - { - if (!unitTarget) - return; - - uint32 spell_id = m_currentBasePoints[effect->EffectIndex]; - SpellEntry const* spell_proto = sSpellStore.LookupEntry(spell_id); - if (!spell_proto) - return; - - m_caster->CastSpell(unitTarget, spell_proto, true, NULL); - return; - } - } - - switch (m_spellInfo->Id) - { - case 31789: // Righteous Defense (step 1) - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - { - SendCastResult(SPELL_FAILED_TARGET_AFFECTING_COMBAT); - return; - } - - // 31989 -> dummy effect (step 1) + dummy effect (step 2) -> 31709 (taunt like spell for each target) - Unit* friendTarget = !unitTarget || unitTarget->IsFriendlyTo(m_caster) ? unitTarget : unitTarget->getVictim(); - if (friendTarget) - { - Player* player = friendTarget->GetCharmerOrOwnerPlayerOrPlayerItself(); - if (!player || !player->IsInSameRaidWith((Player*)m_caster)) - friendTarget = NULL; - } - - // non-standard cast requirement check - if (!friendTarget || friendTarget->getAttackers().empty()) - { - ((Player*)m_caster)->RemoveSpellCooldown(m_spellInfo->Id, true); - SendCastResult(SPELL_FAILED_TARGET_AFFECTING_COMBAT); - return; - } - - // Righteous Defense (step 2) (in old version 31980 dummy effect) - // Clear targets for eff 1 - for (TargetList::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - ihit->effectMask &= ~(1 << 1); - - // not empty (checked), copy - Unit::AttackerSet attackers = friendTarget->getAttackers(); - - // selected from list 3 - for (uint32 i = 0; i < std::min(size_t(3), attackers.size()); ++i) - { - Unit::AttackerSet::iterator aItr = attackers.begin(); - std::advance(aItr, rand() % attackers.size()); - AddUnitTarget((*aItr), EFFECT_INDEX_1); - attackers.erase(aItr); - } - - // now let next effect cast spell at each target. - return; - } - case 37877: // Blessing of Faith - { - if (!unitTarget) - return; - - uint32 spell_id = 0; - switch (unitTarget->getClass()) - { - case CLASS_DRUID: spell_id = 37878; break; - case CLASS_PALADIN: spell_id = 37879; break; - case CLASS_PRIEST: spell_id = 37880; break; - case CLASS_SHAMAN: spell_id = 37881; break; - default: return; // ignore for not healing classes - } - - m_caster->CastSpell(m_caster, spell_id, true); - return; - } - } - break; - } - case SPELLFAMILY_SHAMAN: - { - SpellClassOptionsEntry const* shamClassOptions = m_spellInfo->GetSpellClassOptions(); - // Cleansing Totem - if (shamClassOptions && (shamClassOptions->SpellFamilyFlags & UI64LIT(0x0000000004000000)) && m_spellInfo->SpellIconID==1673) - { - if (unitTarget) - m_caster->CastSpell(unitTarget, 52025, true); - return; - } - // Healing Stream Totem - if (shamClassOptions && shamClassOptions->SpellFamilyFlags & UI64LIT(0x0000000000002000)) - { - if (unitTarget) - { - if (Unit* owner = m_caster->GetOwner()) - { - // spell have SPELL_DAMAGE_CLASS_NONE and not get bonuses from owner, use main spell for bonuses - if (m_triggeredBySpellInfo) - { - damage = int32(owner->SpellHealingBonusDone(unitTarget, m_triggeredBySpellInfo, damage, HEAL)); - damage = int32(unitTarget->SpellHealingBonusTaken(owner, m_triggeredBySpellInfo, damage, HEAL)); - } - - // Restorative Totems - Unit::AuraList const& mDummyAuras = owner->GetAurasByType(SPELL_AURA_DUMMY); - for (Unit::AuraList::const_iterator i = mDummyAuras.begin(); i != mDummyAuras.end(); ++i) - // only its have dummy with specific icon - if ((*i)->GetSpellProto()->GetSpellFamilyName() == SPELLFAMILY_SHAMAN && (*i)->GetSpellProto()->SpellIconID == 338) - damage += (*i)->GetModifier()->m_amount * damage / 100; - - // Glyph of Healing Stream Totem - if (Aura* dummy = owner->GetDummyAura(55456)) - damage += dummy->GetModifier()->m_amount * damage / 100; - } - m_caster->CastCustomSpell(unitTarget, 52042, &damage, NULL, NULL, true, 0, 0, m_originalCasterGUID); - } - return; - } - // Mana Spring Totem - if (shamClassOptions && shamClassOptions->SpellFamilyFlags & UI64LIT(0x0000000000004000)) - { - if (!unitTarget || unitTarget->getPowerType() != POWER_MANA) - return; - m_caster->CastCustomSpell(unitTarget, 52032, &damage, 0, 0, true, 0, 0, m_originalCasterGUID); - return; - } - // Flametongue Weapon Proc, Ranks - if (shamClassOptions && shamClassOptions->SpellFamilyFlags & UI64LIT(0x0000000000200000)) - { - if (!m_CastItem) - { - sLog.outError("Spell::EffectDummy: spell %i requires cast Item", m_spellInfo->Id); - return; - } - // found spelldamage coefficients of 0.381% per 0.1 speed and 15.244 per 4.0 speed - // but own calculation say 0.385 gives at most one point difference to published values - int32 spellDamage = m_caster->SpellBaseDamageBonusDone(GetSpellSchoolMask(m_spellInfo)); - float weaponSpeed = (1.0f / IN_MILLISECONDS) * m_CastItem->GetProto()->Delay; - int32 totalDamage = int32((damage + 3.85f * spellDamage) * 0.01 * weaponSpeed); - - m_caster->CastCustomSpell(unitTarget, 10444, &totalDamage, NULL, NULL, true, m_CastItem); - return; - } - if (m_spellInfo->Id == 39610) // Mana Tide Totem effect - { - if (!unitTarget || unitTarget->getPowerType() != POWER_MANA) - return; - - // Glyph of Mana Tide - if (Unit* owner = m_caster->GetOwner()) - if (Aura* dummy = owner->GetDummyAura(55441)) - damage += dummy->GetModifier()->m_amount; - // Regenerate 6% of Total Mana Every 3 secs - int32 EffectBasePoints0 = unitTarget->GetMaxPower(POWER_MANA) * damage / 100; - m_caster->CastCustomSpell(unitTarget, 39609, &EffectBasePoints0, NULL, NULL, true, NULL, NULL, m_originalCasterGUID); - return; - } - // Lava Lash - if (m_spellInfo->IsFitToFamilyMask(UI64LIT(0x0000000000000000), 0x00000004)) - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - Item* item = ((Player*)m_caster)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); - if (item) - { - // Damage is increased if your off-hand weapon is enchanted with Flametongue. - Unit::AuraList const& auraDummy = m_caster->GetAurasByType(SPELL_AURA_DUMMY); - for (Unit::AuraList::const_iterator itr = auraDummy.begin(); itr != auraDummy.end(); ++itr) - { - if ((*itr)->GetSpellProto()->IsFitToFamily(SPELLFAMILY_SHAMAN, UI64LIT(0x0000000000200000)) && - (*itr)->GetCastItemGuid() == item->GetObjectGuid()) - { - m_damage += m_damage * damage / 100; - return; - } - } - } - return; - } - // Fire Nova - if (m_spellInfo->SpellIconID == 33) - { - // fire totems slot - Totem* totem = m_caster->GetTotem(TOTEM_SLOT_FIRE); - if (!totem) - return; - - uint32 triggered_spell_id; - switch (m_spellInfo->Id) - { - case 1535: triggered_spell_id = 8349; break; - case 8498: triggered_spell_id = 8502; break; - case 8499: triggered_spell_id = 8503; break; - case 11314: triggered_spell_id = 11306; break; - case 11315: triggered_spell_id = 11307; break; - case 25546: triggered_spell_id = 25535; break; - case 25547: triggered_spell_id = 25537; break; - case 61649: triggered_spell_id = 61650; break; - case 61657: triggered_spell_id = 61654; break; - default: return; - } - - totem->CastSpell(totem, triggered_spell_id, true, NULL, NULL, m_caster->GetObjectGuid()); - - // Fire Nova Visual - totem->CastSpell(totem, 19823, true, NULL, NULL, m_caster->GetObjectGuid()); - return; - } - break; - } - case SPELLFAMILY_DEATHKNIGHT: - { - SpellClassOptionsEntry const* dkClassOptions = m_spellInfo->GetSpellClassOptions(); - // Death Coil - if (dkClassOptions && dkClassOptions->SpellFamilyFlags & UI64LIT(0x002000)) - { - if (m_caster->IsFriendlyTo(unitTarget)) - { - if (!unitTarget || unitTarget->GetCreatureType() != CREATURE_TYPE_UNDEAD) - return; - - int32 bp = int32(damage * 1.5f); - m_caster->CastCustomSpell(unitTarget, 47633, &bp, NULL, NULL, true); - } - else - { - int32 bp = damage; - m_caster->CastCustomSpell(unitTarget, 47632, &bp, NULL, NULL, true); - } - return; - } - // Hungering Cold - else if (dkClassOptions && dkClassOptions->SpellFamilyFlags & UI64LIT(0x0000100000000000)) - { - m_caster->CastSpell(m_caster, 51209, true); - return; - } - // Death Strike - else if (dkClassOptions && dkClassOptions->SpellFamilyFlags & UI64LIT(0x0000000000000010)) - { - uint32 count = 0; - Unit::SpellAuraHolderMap const& auras = unitTarget->GetSpellAuraHolderMap(); - for (Unit::SpellAuraHolderMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) - { - if (itr->second->GetSpellProto()->GetDispel() == DISPEL_DISEASE && - itr->second->GetCasterGuid() == m_caster->GetObjectGuid()) - { - ++count; - // max. 15% - if (count == 3) - break; - } - } - - SpellEffectEntry const* dsSpellEffect = m_spellInfo->GetSpellEffect(EFFECT_INDEX_0); - int32 bp = int32(count * m_caster->GetMaxHealth() * (dsSpellEffect ? dsSpellEffect->DmgMultiplier : 0.0f) / 100); - - // Improved Death Strike (percent stored in nonexistent EFFECT_INDEX_2 effect base points) - Unit::AuraList const& auraMod = m_caster->GetAurasByType(SPELL_AURA_ADD_FLAT_MODIFIER); - for (Unit::AuraList::const_iterator iter = auraMod.begin(); iter != auraMod.end(); ++iter) - { - // only required spell have spellicon for SPELL_AURA_ADD_FLAT_MODIFIER - if ((*iter)->GetSpellProto()->SpellIconID == 2751 && (*iter)->GetSpellProto()->GetSpellFamilyName() == SPELLFAMILY_DEATHKNIGHT) - { - bp += (*iter)->GetSpellProto()->CalculateSimpleValue(EFFECT_INDEX_2) * bp / 100; - break; - } - } - - m_caster->CastCustomSpell(m_caster, 45470, &bp, NULL, NULL, true); - return; - } - // Obliterate - else if (dkClassOptions && dkClassOptions->SpellFamilyFlags & UI64LIT(0x0002000000000000)) - { - // search for Annihilation - Unit::AuraList const& dummyList = m_caster->GetAurasByType(SPELL_AURA_DUMMY); - for (Unit::AuraList::const_iterator itr = dummyList.begin(); itr != dummyList.end(); ++itr) - { - if ((*itr)->GetSpellProto()->GetSpellFamilyName() == SPELLFAMILY_DEATHKNIGHT && (*itr)->GetSpellProto()->SpellIconID == 2710) - { - if (roll_chance_i((*itr)->GetModifier()->m_amount)) // don't consume if found - return; - else - break; - } - } - - // consume diseases - unitTarget->RemoveAurasWithDispelType(DISPEL_DISEASE, m_caster->GetObjectGuid()); - } - break; - } - } - - // pet auras - if (PetAura const* petSpell = sSpellMgr.GetPetAura(m_spellInfo->Id, SpellEffectIndex(effect->EffectIndex))) - { - m_caster->AddPetAura(petSpell); - return; - } - - // Script based implementation. Must be used only for not good for implementation in core spell effects - // So called only for not processed cases - bool libraryResult = false; - if (gameObjTarget) - libraryResult = sScriptMgr.OnEffectDummy(m_caster, m_spellInfo->Id, SpellEffectIndex(effect->EffectIndex), gameObjTarget, m_originalCasterGUID); - else if (unitTarget && unitTarget->GetTypeId() == TYPEID_UNIT) - libraryResult = sScriptMgr.OnEffectDummy(m_caster, m_spellInfo->Id, SpellEffectIndex(effect->EffectIndex), (Creature*)unitTarget, m_originalCasterGUID); - else if (itemTarget) - libraryResult = sScriptMgr.OnEffectDummy(m_caster, m_spellInfo->Id, SpellEffectIndex(effect->EffectIndex), itemTarget, m_originalCasterGUID); - - if (libraryResult || !unitTarget) - return; - - // Previous effect might have started script - if (!ScriptMgr::CanSpellEffectStartDBScript(m_spellInfo, SpellEffectIndex(effect->EffectIndex))) - return; - - DEBUG_FILTER_LOG(LOG_FILTER_SPELL_CAST, "Spell ScriptStart spellid %u in EffectDummy", m_spellInfo->Id); - m_caster->GetMap()->ScriptsStart(sSpellScripts, m_spellInfo->Id, m_caster, unitTarget); -} - -void Spell::EffectTriggerSpellWithValue(SpellEffectEntry const* effect) -{ - uint32 triggered_spell_id = effect->EffectTriggerSpell; - - // normal case - SpellEntry const* spellInfo = sSpellStore.LookupEntry(triggered_spell_id); - - if (!spellInfo) - { - sLog.outError("EffectTriggerSpellWithValue of spell %u: triggering unknown spell id %i", m_spellInfo->Id, triggered_spell_id); - return; - } - - int32 bp = damage; - m_caster->CastCustomSpell(unitTarget, triggered_spell_id, &bp, &bp, &bp, true, m_CastItem , NULL, m_originalCasterGUID, m_spellInfo); -} - -void Spell::EffectTriggerRitualOfSummoning(SpellEffectEntry const* effect) -{ - uint32 triggered_spell_id = effect->EffectTriggerSpell; - SpellEntry const *spellInfo = sSpellStore.LookupEntry( triggered_spell_id ); - - if (!spellInfo) - { - sLog.outError("EffectTriggerRitualOfSummoning of spell %u: triggering unknown spell id %i", m_spellInfo->Id, triggered_spell_id); - return; - } - - finish(); - - m_caster->CastSpell(unitTarget, spellInfo, false); -} - -void Spell::EffectClearQuest(SpellEffectEntry const* effect) -{ - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - Player* player = (Player*)m_caster; - - uint32 quest_id = effect->EffectMiscValue; - - if (!sObjectMgr.GetQuestTemplate(quest_id)) - { - sLog.outError("Spell::EffectClearQuest spell entry %u attempt clear quest entry %u but this quest does not exist.", m_spellInfo->Id, quest_id); - return; - } - - // remove quest possibly in quest log (is that expected?) - for (uint16 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot) - { - uint32 quest = player->GetQuestSlotQuestId(slot); - - if (quest == quest_id) - { - player->SetQuestSlot(slot, 0); - // ignore unequippable quest items in this case, it will still be equipped - player->TakeQuestSourceItem(quest_id, false); - } - } - - player->SetQuestStatus(quest_id, QUEST_STATUS_NONE); - player->getQuestStatusMap()[quest_id].m_rewarded = false; - - PhaseUpdateData phaseUdateData; - phaseUdateData.AddQuestUpdate(quest_id); - - player->GetPhaseMgr()->NotifyConditionChanged(phaseUdateData); -} - -void Spell::EffectForceCast(SpellEffectEntry const* effect) -{ - if (!unitTarget) - return; - - uint32 triggered_spell_id = effect->EffectTriggerSpell; - - // normal case - SpellEntry const* spellInfo = sSpellStore.LookupEntry(triggered_spell_id); - - if (!spellInfo) - { - sLog.outError("EffectForceCast of spell %u: triggering unknown spell id %i", m_spellInfo->Id, triggered_spell_id); - return; - } - - unitTarget->CastSpell(unitTarget, spellInfo, true, NULL, NULL, m_originalCasterGUID, m_spellInfo); -} - -void Spell::EffectTriggerSpell(SpellEffectEntry const* effect) -{ - // only unit case known - if (!unitTarget) - { - if (gameObjTarget || itemTarget) - sLog.outError("Spell::EffectTriggerSpell (Spell: %u): Unsupported non-unit case!", m_spellInfo->Id); - return; - } - - uint32 triggered_spell_id = effect->EffectTriggerSpell; - - // special cases - switch (triggered_spell_id) - { - case 18461: // Vanish (not exist) - { - unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOD_ROOT); - unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOD_DECREASE_SPEED); - unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOD_STALKED); - - // if this spell is given to NPC it must handle rest by it's own AI - if (unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - uint32 spellId = 1784; - // reset cooldown on it if needed - if (((Player*)unitTarget)->HasSpellCooldown(spellId)) - ((Player*)unitTarget)->RemoveSpellCooldown(spellId); - - m_caster->CastSpell(unitTarget, spellId, true); - return; - } - case 29284: // Brittle Armor - (need add max stack of 24575 Brittle Armor) - m_caster->CastSpell(unitTarget, 24575, true, m_CastItem, NULL, m_originalCasterGUID); - return; - case 29286: // Mercurial Shield - (need add max stack of 26464 Mercurial Shield) - m_caster->CastSpell(unitTarget, 26464, true, m_CastItem, NULL, m_originalCasterGUID); - return; - case 31980: // Righteous Defense - { - m_caster->CastSpell(unitTarget, 31790, true, m_CastItem, NULL, m_originalCasterGUID); - return; - } - case 35729: // Cloak of Shadows - { - Unit::SpellAuraHolderMap& Auras = unitTarget->GetSpellAuraHolderMap(); - for (Unit::SpellAuraHolderMap::iterator iter = Auras.begin(); iter != Auras.end(); ++iter) - { - // Remove all harmful spells on you except positive/passive/physical auras - if (!iter->second->IsPositive() && - !iter->second->IsPassive() && - !iter->second->IsDeathPersistent() && - (GetSpellSchoolMask(iter->second->GetSpellProto()) & SPELL_SCHOOL_MASK_NORMAL) == 0) - { - m_caster->RemoveAurasDueToSpell(iter->second->GetSpellProto()->Id); - iter = Auras.begin(); - } - } - return; - } - case 41967: // Priest Shadowfiend (34433) need apply mana gain trigger aura on pet - { - if (Unit* pet = unitTarget->GetPet()) - pet->CastSpell(pet, 28305, true); - return; - } - } - - // normal case - SpellEntry const* spellInfo = sSpellStore.LookupEntry(triggered_spell_id); - if (!spellInfo) - { - // No previous Effect might have started a script - bool startDBScript = unitTarget && ScriptMgr::CanSpellEffectStartDBScript(m_spellInfo, SpellEffectIndex(effect->EffectIndex)); - if (startDBScript) - { - DEBUG_FILTER_LOG(LOG_FILTER_SPELL_CAST, "Spell ScriptStart spellid %u in EffectTriggerSpell", m_spellInfo->Id); - startDBScript = m_caster->GetMap()->ScriptsStart(sSpellScripts, m_spellInfo->Id, m_caster, unitTarget); - } - - if (!startDBScript) - sLog.outError("EffectTriggerSpell of spell %u: triggering unknown spell id %i", m_spellInfo->Id, triggered_spell_id); - return; - } - - // select formal caster for triggered spell - Unit* caster = m_caster; - - // some triggered spells require specific equipment - if (spellInfo->GetEquippedItemClass() >=0 && m_caster->GetTypeId()==TYPEID_PLAYER) - { - // main hand weapon required - if (spellInfo->HasAttribute(SPELL_ATTR_EX3_MAIN_HAND)) - { - Item* item = ((Player*)m_caster)->GetWeaponForAttack(BASE_ATTACK, true, false); - - // skip spell if no weapon in slot or broken - if (!item) - return; - - // skip spell if weapon not fit to triggered spell - if (!item->IsFitToSpellRequirements(spellInfo)) - return; - } - - // offhand hand weapon required - if (spellInfo->HasAttribute(SPELL_ATTR_EX3_REQ_OFFHAND)) - { - Item* item = ((Player*)m_caster)->GetWeaponForAttack(OFF_ATTACK, true, false); - - // skip spell if no weapon in slot or broken - if (!item) - return; - - // skip spell if weapon not fit to triggered spell - if (!item->IsFitToSpellRequirements(spellInfo)) - return; - } - } - else - { - // Note: not exist spells with weapon req. and IsSpellHaveCasterSourceTargets == true - // so this just for speedup places in else - caster = IsSpellWithCasterSourceTargetsOnly(spellInfo) ? unitTarget : m_caster; - } - - caster->CastSpell(unitTarget, spellInfo, true, m_CastItem, NULL, m_originalCasterGUID, m_spellInfo); -} - -void Spell::EffectTriggerMissileSpell(SpellEffectEntry const* effect) -{ - uint32 triggered_spell_id = effect->EffectTriggerSpell; - - // normal case - SpellEntry const* spellInfo = sSpellStore.LookupEntry(triggered_spell_id); - - if (!spellInfo) - { - sLog.outError("EffectTriggerMissileSpell of spell %u (eff: %u): triggering unknown spell id %u", - m_spellInfo->Id,effect->EffectIndex,triggered_spell_id); - return; - } - - if (m_CastItem) - DEBUG_FILTER_LOG(LOG_FILTER_SPELL_CAST, "WORLD: cast Item spellId - %i", spellInfo->Id); - - m_caster->CastSpell(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, spellInfo, true, m_CastItem, NULL, m_originalCasterGUID, m_spellInfo); -} - -void Spell::EffectJump(SpellEffectEntry const* effect) -{ - if (m_caster->IsTaxiFlying()) - return; - - // Init dest coordinates - float x, y, z, o; - if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) - { - m_targets.getDestination(x, y, z); - - if(effect->EffectImplicitTargetA == TARGET_BEHIND_VICTIM) - { - // explicit cast data from client or server-side cast - // some spell at client send caster - Unit* pTarget = NULL; - if (m_targets.getUnitTarget() && m_targets.getUnitTarget() != m_caster) - pTarget = m_targets.getUnitTarget(); - else if (unitTarget->getVictim()) - pTarget = m_caster->getVictim(); - else if (m_caster->GetTypeId() == TYPEID_PLAYER) - pTarget = m_caster->GetMap()->GetUnit(((Player*)m_caster)->GetSelectionGuid()); - - o = pTarget ? pTarget->GetOrientation() : m_caster->GetOrientation(); - } - else - o = m_caster->GetOrientation(); - } - else if (unitTarget) - { - unitTarget->GetContactPoint(m_caster, x, y, z, CONTACT_DISTANCE); - o = m_caster->GetOrientation(); - } - else if (gameObjTarget) - { - gameObjTarget->GetContactPoint(m_caster, x, y, z, CONTACT_DISTANCE); - o = m_caster->GetOrientation(); - } - else - { - sLog.outError("Spell::EffectJump - unsupported target mode for spell ID %u", m_spellInfo->Id); - return; - } - - m_caster->NearTeleportTo(x, y, z, o, true); // TODO Implement this as jump movement? -} - -void Spell::EffectTeleportUnits(SpellEffectEntry const* effect) // TODO - Use target settings for this effect! -{ - if (!unitTarget || unitTarget->IsTaxiFlying()) - return; - - switch (m_spellInfo->Id) - { - case 48129: // Scroll of Recall - case 60320: // Scroll of Recall II - case 60321: // Scroll of Recall III - { - uint32 failAtLevel = 0; - switch (m_spellInfo->Id) - { - case 48129: failAtLevel = 40; break; - case 60320: failAtLevel = 70; break; - case 60321: failAtLevel = 80; break; - } - - if (unitTarget->getLevel() > failAtLevel && unitTarget->GetTypeId() == TYPEID_PLAYER) - { - unitTarget->CastSpell(unitTarget, 60444, true); - // TODO: Unclear use of probably related spell 60322 - uint32 spellId = (((Player*)unitTarget)->GetTeam() == ALLIANCE ? 60323 : 60328) + urand(0, 7); - unitTarget->CastSpell(unitTarget, spellId, true); - return; - } - break; - } - } - - // Target dependend on TargetB, if there is none provided, decide dependend on A - uint32 targetType = effect->EffectImplicitTargetB; - if (!targetType) - targetType = effect->EffectImplicitTargetA; - - switch (targetType) - { - case TARGET_INNKEEPER_COORDINATES: - { - // Only players can teleport to innkeeper - if (unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - ((Player*)unitTarget)->TeleportToHomebind(unitTarget == m_caster ? TELE_TO_SPELL : 0); - return; - } - case TARGET_AREAEFFECT_INSTANT: // in all cases first TARGET_TABLE_X_Y_Z_COORDINATES - case TARGET_TABLE_X_Y_Z_COORDINATES: - { - SpellTargetPosition const* st = sSpellMgr.GetSpellTargetPosition(m_spellInfo->Id); - if (!st) - { - sLog.outError("Spell::EffectTeleportUnits - unknown Teleport coordinates for spell ID %u", m_spellInfo->Id); - return; - } - - if (st->target_mapId == unitTarget->GetMapId()) - unitTarget->NearTeleportTo(st->target_X, st->target_Y, st->target_Z, st->target_Orientation, unitTarget == m_caster); - else if (unitTarget->GetTypeId() == TYPEID_PLAYER) - ((Player*)unitTarget)->TeleportTo(st->target_mapId, st->target_X, st->target_Y, st->target_Z, st->target_Orientation, unitTarget == m_caster ? TELE_TO_SPELL : 0); - break; - } - case TARGET_EFFECT_SELECT: - { - // m_destN filled, but sometimes for wrong dest and does not have TARGET_FLAG_DEST_LOCATION - - float x = unitTarget->GetPositionX(); - float y = unitTarget->GetPositionY(); - float z = unitTarget->GetPositionZ(); - float orientation = m_caster->GetOrientation(); - - m_caster->NearTeleportTo(x, y, z, orientation, unitTarget == m_caster); - return; - } - case TARGET_BEHIND_VICTIM: - { - Unit* pTarget = NULL; - - // explicit cast data from client or server-side cast - // some spell at client send caster - if (m_targets.getUnitTarget() && m_targets.getUnitTarget() != unitTarget) - pTarget = m_targets.getUnitTarget(); - else if (unitTarget->getVictim()) - pTarget = unitTarget->getVictim(); - else if (unitTarget->GetTypeId() == TYPEID_PLAYER) - pTarget = unitTarget->GetMap()->GetUnit(((Player*)unitTarget)->GetSelectionGuid()); - - // Init dest coordinates - float x = m_targets.m_destX; - float y = m_targets.m_destY; - float z = m_targets.m_destZ; - float orientation = pTarget ? pTarget->GetOrientation() : unitTarget->GetOrientation(); - unitTarget->NearTeleportTo(x, y, z, orientation, unitTarget == m_caster); - return; - } - default: - { - // If not exist data for dest location - return - if (!(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)) - { - sLog.outError( "Spell::EffectTeleportUnits - unknown EffectImplicitTargetB[%u] = %u for spell ID %u", effect->EffectIndex, effect->EffectImplicitTargetB, m_spellInfo->Id ); - return; - } - // Init dest coordinates - float x = m_targets.m_destX; - float y = m_targets.m_destY; - float z = m_targets.m_destZ; - float orientation = unitTarget->GetOrientation(); - // Teleport - unitTarget->NearTeleportTo(x, y, z, orientation, unitTarget == m_caster); - return; - } - } - - // post effects for TARGET_TABLE_X_Y_Z_COORDINATES - switch (m_spellInfo->Id) - { - // Dimensional Ripper - Everlook - case 23442: - { - int32 r = irand(0, 119); - if (r >= 70) // 7/12 success - { - if (r < 100) // 4/12 evil twin - m_caster->CastSpell(m_caster, 23445, true); - else // 1/12 fire - m_caster->CastSpell(m_caster, 23449, true); - } - return; - } - // Ultrasafe Transporter: Toshley's Station - case 36941: - { - if (roll_chance_i(50)) // 50% success - { - int32 rand_eff = urand(1, 7); - switch (rand_eff) - { - case 1: - // soul split - evil - m_caster->CastSpell(m_caster, 36900, true); - break; - case 2: - // soul split - good - m_caster->CastSpell(m_caster, 36901, true); - break; - case 3: - // Increase the size - m_caster->CastSpell(m_caster, 36895, true); - break; - case 4: - // Decrease the size - m_caster->CastSpell(m_caster, 36893, true); - break; - case 5: - // Transform - { - if (((Player*)m_caster)->GetTeam() == ALLIANCE) - m_caster->CastSpell(m_caster, 36897, true); - else - m_caster->CastSpell(m_caster, 36899, true); - break; - } - case 6: - // chicken - m_caster->CastSpell(m_caster, 36940, true); - break; - case 7: - // evil twin - m_caster->CastSpell(m_caster, 23445, true); - break; - } - } - return; - } - // Dimensional Ripper - Area 52 - case 36890: - { - if (roll_chance_i(50)) // 50% success - { - int32 rand_eff = urand(1, 4); - switch (rand_eff) - { - case 1: - // soul split - evil - m_caster->CastSpell(m_caster, 36900, true); - break; - case 2: - // soul split - good - m_caster->CastSpell(m_caster, 36901, true); - break; - case 3: - // Increase the size - m_caster->CastSpell(m_caster, 36895, true); - break; - case 4: - // Transform - { - if (((Player*)m_caster)->GetTeam() == ALLIANCE) - m_caster->CastSpell(m_caster, 36897, true); - else - m_caster->CastSpell(m_caster, 36899, true); - break; - } - } - } - return; - } - } -} - -void Spell::EffectApplyAura(SpellEffectEntry const* effect) -{ - if (!unitTarget) - return; - - // ghost spell check, allow apply any auras at player loading in ghost mode (will be cleanup after load) - if ((!unitTarget->isAlive() && !(IsDeathOnlySpell(m_spellInfo) || IsDeathPersistentSpell(m_spellInfo))) && - (unitTarget->GetTypeId() != TYPEID_PLAYER || !((Player*)unitTarget)->GetSession()->PlayerLoading())) - return; - - Unit* caster = GetAffectiveCaster(); - if (!caster) - { - // FIXME: currently we can't have auras applied explicitly by gameobjects - // so for auras from wild gameobjects (no owner) target used - if (m_originalCasterGUID.IsGameObject()) - caster = unitTarget; - else - return; - } - - DEBUG_FILTER_LOG(LOG_FILTER_SPELL_CAST, "Spell: Aura is: %u", effect->EffectApplyAuraName); - - Aura* aur = CreateAura(m_spellInfo, SpellEffectIndex(effect->EffectIndex), &m_currentBasePoints[effect->EffectIndex], m_spellAuraHolder, unitTarget, caster, m_CastItem); - m_spellAuraHolder->AddAura(aur, SpellEffectIndex(effect->EffectIndex)); -} - -void Spell::EffectUnlearnSpecialization(SpellEffectEntry const* effect) -{ - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - Player *_player = (Player*)unitTarget; - uint32 spellToUnlearn = effect->EffectTriggerSpell; - - _player->removeSpell(spellToUnlearn); - - if (WorldObject const* caster = GetCastingObject()) - DEBUG_LOG("Spell: %s has unlearned spell %u at %s", _player->GetGuidStr().c_str(), spellToUnlearn, caster->GetGuidStr().c_str()); -} - -void Spell::EffectPowerDrain(SpellEffectEntry const* effect) -{ - if(effect->EffectMiscValue < 0 || effect->EffectMiscValue >= MAX_POWERS) - return; - - Powers drain_power = Powers(effect->EffectMiscValue); - - if (!unitTarget) - return; - if (!unitTarget->isAlive()) - return; - if (unitTarget->getPowerType() != drain_power) - return; - if (damage < 0) - return; - - uint32 curPower = unitTarget->GetPower(drain_power); - - // add spell damage bonus - damage = m_caster->SpellDamageBonusDone(unitTarget, m_spellInfo, uint32(damage), SPELL_DIRECT_DAMAGE); - damage = unitTarget->SpellDamageBonusTaken(m_caster, m_spellInfo, uint32(damage), SPELL_DIRECT_DAMAGE); - - // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4) - uint32 power = damage; - if (drain_power == POWER_MANA) - power -= unitTarget->GetCritDamageReduction(power); - - int32 new_damage; - if (curPower < power) - new_damage = curPower; - else - new_damage = power; - - unitTarget->ModifyPower(drain_power, -new_damage); - - // Don`t restore from self drain - if (drain_power == POWER_MANA && m_caster != unitTarget) - { - float manaMultiplier = effect->EffectMultipleValue; - if(manaMultiplier==0) - manaMultiplier = 1; - - if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_MULTIPLE_VALUE, manaMultiplier); - - int32 gain = int32(new_damage * manaMultiplier); - - m_caster->EnergizeBySpell(m_caster, m_spellInfo->Id, gain, POWER_MANA); - } -} - -void Spell::EffectSendEvent(SpellEffectEntry const* effect) -{ - /* - we do not handle a flag dropping or clicking on flag in battleground by sendevent system - TODO: Actually, why not... - */ - DEBUG_FILTER_LOG(LOG_FILTER_SPELL_CAST, "Spell ScriptStart %u for spellid %u in EffectSendEvent ", effect->EffectMiscValue, m_spellInfo->Id); - StartEvents_Event(m_caster->GetMap(), effect->EffectMiscValue, m_caster, focusObject, true, m_caster); -} - -void Spell::EffectPowerBurn(SpellEffectEntry const* effect) -{ - if (effect->EffectMiscValue < 0 || effect->EffectMiscValue >= MAX_POWERS) - return; - - Powers powertype = Powers(effect->EffectMiscValue); - - if (!unitTarget) - return; - if (!unitTarget->isAlive()) - return; - if (unitTarget->getPowerType() != powertype) - return; - if (damage < 0) - return; - - // burn x% of target's mana, up to maximum of 2x% of caster's mana (Mana Burn) - if (m_spellInfo->GetManaCostPercentage()) - { - int32 maxdamage = m_caster->GetMaxPower(powertype) * damage * 2 / 100; - damage = unitTarget->GetMaxPower(powertype) * damage / 100; - if (damage > maxdamage) - damage = maxdamage; - } - - int32 curPower = int32(unitTarget->GetPower(powertype)); - - // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4) - int32 power = damage; - if (powertype == POWER_MANA) - power -= unitTarget->GetCritDamageReduction(power); - - int32 new_damage = (curPower < power) ? curPower : power; - - unitTarget->ModifyPower(powertype, -new_damage); - float multiplier = effect->EffectMultipleValue; - - if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_MULTIPLE_VALUE, multiplier); - - new_damage = int32(new_damage * multiplier); - m_damage += new_damage; -} - -void Spell::EffectHeal(SpellEffectEntry const* /*effect*/) -{ - if (unitTarget && unitTarget->isAlive() && damage >= 0) - { - // Try to get original caster - Unit* caster = GetAffectiveCaster(); - if (!caster) - return; - - int32 addhealth = damage; - - // Seal of Light proc - if (m_spellInfo->Id == 20167) - { - float ap = caster->GetTotalAttackPowerValue(BASE_ATTACK); - int32 holy = caster->SpellBaseHealingBonusDone(GetSpellSchoolMask(m_spellInfo)); - if (holy < 0) - holy = 0; - addhealth += int32(ap * 0.15) + int32(holy * 15 / 100); - } - // Vessel of the Naaru (Vial of the Sunwell trinket) - else if (m_spellInfo->Id == 45064) - { - // Amount of heal - depends from stacked Holy Energy - int damageAmount = 0; - Unit::AuraList const& mDummyAuras = m_caster->GetAurasByType(SPELL_AURA_DUMMY); - for (Unit::AuraList::const_iterator i = mDummyAuras.begin(); i != mDummyAuras.end(); ++i) - if ((*i)->GetId() == 45062) - damageAmount += (*i)->GetModifier()->m_amount; - if (damageAmount) - m_caster->RemoveAurasDueToSpell(45062); - - addhealth += damageAmount; - } - // Death Pact (percent heal) - else if (m_spellInfo->Id == 48743) - addhealth = addhealth * unitTarget->GetMaxHealth() / 100; - // Swiftmend - consumes Regrowth or Rejuvenation - else if (m_spellInfo->GetTargetAuraState() == AURA_STATE_SWIFTMEND && unitTarget->HasAuraState(AURA_STATE_SWIFTMEND)) - { - Unit::AuraList const& RejorRegr = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_HEAL); - // find most short by duration - Aura* targetAura = NULL; - for (Unit::AuraList::const_iterator i = RejorRegr.begin(); i != RejorRegr.end(); ++i) - { - SpellClassOptionsEntry const* smClassOptions = (*i)->GetSpellProto()->GetSpellClassOptions(); - if (smClassOptions && smClassOptions->SpellFamilyName == SPELLFAMILY_DRUID && - // Regrowth or Rejuvenation 0x40 | 0x10 - (smClassOptions->SpellFamilyFlags & UI64LIT(0x0000000000000050))) - { - if (!targetAura || (*i)->GetAuraDuration() < targetAura->GetAuraDuration()) - targetAura = *i; - } - } - - if (!targetAura) - { - sLog.outError("Target (GUID: %u TypeId: %u) has aurastate AURA_STATE_SWIFTMEND but no matching aura.", unitTarget->GetGUIDLow(), unitTarget->GetTypeId()); - return; - } - int idx = 0; - SpellEffectEntry const* targetSpellEffect = NULL; - while(idx < 3) - { - targetSpellEffect = targetAura->GetSpellProto()->GetSpellEffect(SpellEffectIndex(idx)); - if(targetSpellEffect && targetSpellEffect->EffectApplyAuraName == SPELL_AURA_PERIODIC_HEAL) - break; - ++idx; - } - - int32 tickheal = targetAura->GetModifier()->m_amount; - int32 tickcount = GetSpellDuration(targetAura->GetSpellProto()) / (targetSpellEffect ? targetSpellEffect->EffectAmplitude : 1) - 1; - - // Glyph of Swiftmend - if (!caster->HasAura(54824)) - unitTarget->RemoveAurasDueToSpell(targetAura->GetId()); - - addhealth += tickheal * tickcount; - } - // Runic Healing Injector & Healing Potion Injector effect increase for engineers - else if ((m_spellInfo->Id == 67486 || m_spellInfo->Id == 67489) && unitTarget->GetTypeId() == TYPEID_PLAYER) - { - Player* player = (Player*)unitTarget; - if (player->HasSkill(SKILL_ENGINEERING)) - addhealth += int32(addhealth * 0.25); - } - - // Chain Healing - SpellClassOptionsEntry const* chClassOptions = m_spellInfo->GetSpellClassOptions(); - if (chClassOptions && chClassOptions->SpellFamilyName == SPELLFAMILY_SHAMAN && chClassOptions->SpellFamilyFlags & UI64LIT(0x0000000000000100)) - { - if (unitTarget == m_targets.getUnitTarget()) - { - // check for Riptide - Aura* riptide = unitTarget->GetAura(SPELL_AURA_PERIODIC_HEAL, SPELLFAMILY_SHAMAN, UI64LIT(0x0), 0x00000010, caster->GetObjectGuid()); - if (riptide) - { - addhealth += addhealth / 4; - unitTarget->RemoveAurasDueToSpell(riptide->GetId()); - } - } - } - - addhealth = caster->SpellHealingBonusDone(unitTarget, m_spellInfo, addhealth, HEAL); - addhealth = unitTarget->SpellHealingBonusTaken(caster, m_spellInfo, addhealth, HEAL); - - m_healing += addhealth; - } -} - -void Spell::EffectHealPct(SpellEffectEntry const* /*effect*/) -{ - if (unitTarget && unitTarget->isAlive() && damage >= 0) - { - // Try to get original caster - Unit* caster = GetAffectiveCaster(); - if (!caster) - return; - - uint32 addhealth = unitTarget->GetMaxHealth() * damage / 100; - - addhealth = caster->SpellHealingBonusDone(unitTarget, m_spellInfo, addhealth, HEAL); - addhealth = unitTarget->SpellHealingBonusTaken(caster, m_spellInfo, addhealth, HEAL); - - uint32 absorb = 0; - unitTarget->CalculateHealAbsorb(addhealth, &absorb); - - int32 gain = caster->DealHeal(unitTarget, addhealth - absorb, m_spellInfo, false, absorb); - unitTarget->getHostileRefManager().threatAssist(caster, float(gain) * 0.5f * sSpellMgr.GetSpellThreatMultiplier(m_spellInfo), m_spellInfo); - } -} - -void Spell::EffectHealMechanical(SpellEffectEntry const* /*effect*/) -{ - // Mechanic creature type should be correctly checked by targetCreatureType field - if (unitTarget && unitTarget->isAlive() && damage >= 0) - { - // Try to get original caster - Unit* caster = GetAffectiveCaster(); - if (!caster) - return; - - uint32 addhealth = caster->SpellHealingBonusDone(unitTarget, m_spellInfo, damage, HEAL); - addhealth = unitTarget->SpellHealingBonusTaken(caster, m_spellInfo, addhealth, HEAL); - - uint32 absorb = 0; - unitTarget->CalculateHealAbsorb(addhealth, &absorb); - - caster->DealHeal(unitTarget, addhealth - absorb, m_spellInfo, false, absorb); - } -} - -void Spell::EffectHealthLeech(SpellEffectEntry const* effect) -{ - if (!unitTarget) - return; - if (!unitTarget->isAlive()) - return; - - if (damage < 0) - return; - - DEBUG_FILTER_LOG(LOG_FILTER_SPELL_CAST, "HealthLeech :%i", damage); - - uint32 curHealth = unitTarget->GetHealth(); - damage = m_caster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, damage); - if ((int32)curHealth < damage) - damage = curHealth; - - float multiplier = effect->EffectMultipleValue; - - if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_MULTIPLE_VALUE, multiplier); - - int32 heal = int32(damage * multiplier); - if (m_caster->isAlive()) - { - heal = m_caster->SpellHealingBonusTaken(m_caster, m_spellInfo, heal, HEAL); - - uint32 absorb = 0; - m_caster->CalculateHealAbsorb(heal, &absorb); - - m_caster->DealHeal(m_caster, heal - absorb, m_spellInfo, false, absorb); - } -} - -void Spell::DoCreateItem(SpellEffectEntry const* effect, uint32 itemtype) -{ - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - Player* player = (Player*)unitTarget; - - uint32 newitemid = itemtype; - ItemPrototype const* pProto = ObjectMgr::GetItemPrototype(newitemid); - if (!pProto) - { - player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL); - return; - } - - // bg reward have some special in code work - bool bg_mark = false; - switch (m_spellInfo->Id) - { - case SPELL_WG_MARK_VICTORY: - case SPELL_WG_MARK_DEFEAT: - bg_mark = true; - break; - default: - break; - } - - uint32 num_to_add = damage; - - if (num_to_add < 1) - num_to_add = 1; - if (num_to_add > pProto->GetMaxStackSize()) - num_to_add = pProto->GetMaxStackSize(); - - // init items_count to 1, since 1 item will be created regardless of specialization - int items_count = 1; - // the chance to create additional items - float additionalCreateChance = 0.0f; - // the maximum number of created additional items - uint8 additionalMaxNum = 0; - // get the chance and maximum number for creating extra items - if (canCreateExtraItems(player, m_spellInfo->Id, additionalCreateChance, additionalMaxNum)) - { - // roll with this chance till we roll not to create or we create the max num - while (roll_chance_f(additionalCreateChance) && items_count <= additionalMaxNum) - ++items_count; - } - - // really will be created more items - num_to_add *= items_count; - - // can the player store the new item? - ItemPosCountVec dest; - uint32 no_space = 0; - InventoryResult msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, newitemid, num_to_add, &no_space); - if (msg != EQUIP_ERR_OK) - { - // convert to possible store amount - if (msg == EQUIP_ERR_INVENTORY_FULL || msg == EQUIP_ERR_CANT_CARRY_MORE_OF_THIS) - num_to_add -= no_space; - else - { - // ignore mana gem case (next effect will recharge existing example) - if (effect->EffectIndex == EFFECT_INDEX_0 && m_spellInfo->GetSpellEffectIdByIndex(EFFECT_INDEX_1) == SPELL_EFFECT_DUMMY ) - return; - - // if not created by another reason from full inventory or unique items amount limitation - player->SendEquipError(msg, NULL, NULL, newitemid); - return; - } - } - - if (num_to_add) - { - // create the new item and store it - Item* pItem = player->StoreNewItem(dest, newitemid, true, Item::GenerateItemRandomPropertyId(newitemid)); - - // was it successful? return error if not - if (!pItem) - { - player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL); - return; - } - - // set the "Crafted by ..." property of the item - if (pItem->GetProto()->Class != ITEM_CLASS_CONSUMABLE && pItem->GetProto()->Class != ITEM_CLASS_QUEST) - pItem->SetGuidValue(ITEM_FIELD_CREATOR, player->GetObjectGuid()); - - // send info to the client - player->SendNewItem(pItem, num_to_add, true, !bg_mark); - - // we succeeded in creating at least one item, so a levelup is possible - if (!bg_mark) - player->UpdateCraftSkill(m_spellInfo->Id); - } -} - -void Spell::EffectCreateItem(SpellEffectEntry const* effect) -{ - DoCreateItem(effect, effect->EffectItemType); -} - -void Spell::EffectCreateItem2(SpellEffectEntry const* effect) -{ - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - Player* player = (Player*)m_caster; - - // explicit item (possible fake) - uint32 item_id = effect->EffectItemType; - - if (item_id) - DoCreateItem(effect, item_id); - - // not explicit loot (with fake item drop if need) - if (IsLootCraftingSpell(m_spellInfo)) - { - if (item_id) - { - if (!player->HasItemCount(item_id, 1)) - return; - - // remove reagent - uint32 count = 1; - player->DestroyItemCount(item_id, count, true); - } - - // create some random items - player->AutoStoreLoot(NULL, m_spellInfo->Id, LootTemplates_Spell); - } -} - -void Spell::EffectCreateRandomItem(SpellEffectEntry const* /*effect*/) -{ - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - Player* player = (Player*)m_caster; - - // create some random items - player->AutoStoreLoot(NULL, m_spellInfo->Id, LootTemplates_Spell); -} - -void Spell::EffectPersistentAA(SpellEffectEntry const* effect) -{ - Unit* pCaster = GetAffectiveCaster(); - // FIXME: in case wild GO will used wrong affective caster (target in fact) as dynobject owner - if (!pCaster) - pCaster = m_caster; - - float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(effect->GetRadiusIndex())); - - if (Player* modOwner = pCaster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, radius); - - DynamicObject* dynObj = new DynamicObject; - if (!dynObj->Create(pCaster->GetMap()->GenerateLocalLowGuid(HIGHGUID_DYNAMICOBJECT), pCaster, m_spellInfo->Id, SpellEffectIndex(effect->EffectIndex), m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, m_duration, radius, DYNAMIC_OBJECT_AREA_SPELL)) - { - delete dynObj; - return; - } - - pCaster->AddDynObject(dynObj); - pCaster->GetMap()->Add(dynObj); -} - -void Spell::EffectEnergize(SpellEffectEntry const* effect) -{ - if (!unitTarget) - return; - if (!unitTarget->isAlive()) - return; - - if(effect->EffectMiscValue < 0 || effect->EffectMiscValue >= MAX_POWERS) - return; - - Powers power = Powers(effect->EffectMiscValue); - - // Some level depends spells - int level_multiplier = 0; - int level_diff = 0; - switch (m_spellInfo->Id) - { - case 9512: // Restore Energy - level_diff = m_caster->getLevel() - 40; - level_multiplier = 2; - break; - case 24571: // Blood Fury - level_diff = m_caster->getLevel() - 60; - level_multiplier = 10; - break; - case 24532: // Burst of Energy - level_diff = m_caster->getLevel() - 60; - level_multiplier = 4; - break; - case 31930: // Judgements of the Wise - case 48542: // Revitalize (mana restore case) - case 63375: // Improved Stormstrike - case 68082: // Glyph of Seal of Command - damage = damage * unitTarget->GetCreateMana() / 100; - break; - case 67487: // Mana Potion Injector - case 67490: // Runic Mana Injector - { - if (unitTarget->GetTypeId() == TYPEID_PLAYER) - { - Player* player = (Player*)unitTarget; - if (player->HasSkill(SKILL_ENGINEERING)) - damage += int32(damage * 0.25); - } - break; - } - default: - break; - } - - if (level_diff > 0) - damage -= level_multiplier * level_diff; - - if (damage < 0) - return; - - if (unitTarget->GetMaxPower(power) == 0) - return; - - m_caster->EnergizeBySpell(unitTarget, m_spellInfo->Id, damage, power); - - // Mad Alchemist's Potion - if (m_spellInfo->Id == 45051) - { - // find elixirs on target - uint32 elixir_mask = 0; - Unit::SpellAuraHolderMap& Auras = unitTarget->GetSpellAuraHolderMap(); - for (Unit::SpellAuraHolderMap::iterator itr = Auras.begin(); itr != Auras.end(); ++itr) - { - uint32 spell_id = itr->second->GetId(); - if (uint32 mask = sSpellMgr.GetSpellElixirMask(spell_id)) - elixir_mask |= mask; - } - - // get available elixir mask any not active type from battle/guardian (and flask if no any) - elixir_mask = (elixir_mask & ELIXIR_FLASK_MASK) ^ ELIXIR_FLASK_MASK; - - // get all available elixirs by mask and spell level - std::vector elixirs; - SpellElixirMap const& m_spellElixirs = sSpellMgr.GetSpellElixirMap(); - for (SpellElixirMap::const_iterator itr = m_spellElixirs.begin(); itr != m_spellElixirs.end(); ++itr) - { - if (itr->second & elixir_mask) - { - if (itr->second & (ELIXIR_UNSTABLE_MASK | ELIXIR_SHATTRATH_MASK)) - continue; - - SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first); - if (spellInfo && (spellInfo->GetSpellLevel() < m_spellInfo->GetSpellLevel() || spellInfo->GetSpellLevel() > unitTarget->getLevel())) - continue; - - elixirs.push_back(itr->first); - } - } - - if (!elixirs.empty()) - { - // cast random elixir on target - uint32 rand_spell = urand(0, elixirs.size() - 1); - m_caster->CastSpell(unitTarget, elixirs[rand_spell], true, m_CastItem); - } - } -} - -void Spell::EffectEnergisePct(SpellEffectEntry const* effect) -{ - if (!unitTarget) - return; - if (!unitTarget->isAlive()) - return; - - if (effect->EffectMiscValue < 0 || effect->EffectMiscValue >= MAX_POWERS) - return; - - Powers power = Powers(effect->EffectMiscValue); - - uint32 maxPower = unitTarget->GetMaxPower(power); - if (maxPower == 0) - return; - - uint32 gain = damage * maxPower / 100; - m_caster->EnergizeBySpell(unitTarget, m_spellInfo->Id, gain, power); -} - -void Spell::SendLoot(ObjectGuid guid, LootType loottype, LockType lockType) -{ - if (gameObjTarget) - { - switch (gameObjTarget->GetGoType()) - { - case GAMEOBJECT_TYPE_DOOR: - case GAMEOBJECT_TYPE_BUTTON: - case GAMEOBJECT_TYPE_QUESTGIVER: - case GAMEOBJECT_TYPE_SPELL_FOCUS: - case GAMEOBJECT_TYPE_GOOBER: - gameObjTarget->Use(m_caster); - return; - - case GAMEOBJECT_TYPE_CHEST: - gameObjTarget->Use(m_caster); - // Don't return, let loots been taken - break; - - case GAMEOBJECT_TYPE_TRAP: - if (lockType == LOCKTYPE_DISARM_TRAP) - { - gameObjTarget->SetLootState(GO_JUST_DEACTIVATED); - return; - } - sLog.outError("Spell::SendLoot unhandled locktype %u for GameObject trap (entry %u) for spell %u.", lockType, gameObjTarget->GetEntry(), m_spellInfo->Id); - return; - default: - sLog.outError("Spell::SendLoot unhandled GameObject type %u (entry %u) for spell %u.", gameObjTarget->GetGoType(), gameObjTarget->GetEntry(), m_spellInfo->Id); - return; - } - } - - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - // Send loot - ((Player*)m_caster)->SendLoot(guid, loottype); -} - -void Spell::EffectOpenLock(SpellEffectEntry const* effect) -{ - if (!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER) - { - DEBUG_LOG("WORLD: Open Lock - No Player Caster!"); - return; - } - - Player* player = (Player*)m_caster; - - uint32 lockId = 0; - ObjectGuid guid; - - // Get lockId - if (gameObjTarget) - { - GameObjectInfo const* goInfo = gameObjTarget->GetGOInfo(); - // Arathi Basin banner opening ! - if ((goInfo->type == GAMEOBJECT_TYPE_BUTTON && goInfo->button.noDamageImmune) || - (goInfo->type == GAMEOBJECT_TYPE_GOOBER && goInfo->goober.losOK)) - { - // CanUseBattleGroundObject() already called in CheckCast() - // in battleground check - if (BattleGround* bg = player->GetBattleGround()) - { - // check if it's correct bg - if (bg->GetTypeID() == BATTLEGROUND_AB || bg->GetTypeID() == BATTLEGROUND_AV) - bg->EventPlayerClickedOnFlag(player, gameObjTarget); - return; - } - } - else if (goInfo->type == GAMEOBJECT_TYPE_FLAGSTAND) - { - // CanUseBattleGroundObject() already called in CheckCast() - // in battleground check - if (BattleGround* bg = player->GetBattleGround()) - { - if (bg->GetTypeID() == BATTLEGROUND_EY) - bg->EventPlayerClickedOnFlag(player, gameObjTarget); - return; - } - } - lockId = goInfo->GetLockId(); - guid = gameObjTarget->GetObjectGuid(); - } - else if (itemTarget) - { - lockId = itemTarget->GetProto()->LockID; - guid = itemTarget->GetObjectGuid(); - } - else - { - DEBUG_LOG("WORLD: Open Lock - No GameObject/Item Target!"); - return; - } - - SkillType skillId = SKILL_NONE; - int32 reqSkillValue = 0; - int32 skillValue; - - SpellCastResult res = CanOpenLock(SpellEffectIndex(effect->EffectIndex), lockId, skillId, reqSkillValue, skillValue); - if (res != SPELL_CAST_OK) - { - SendCastResult(res); - return; - } - - // mark item as unlocked - if (itemTarget) - itemTarget->SetFlag(ITEM_FIELD_FLAGS, ITEM_DYNFLAG_UNLOCKED); - - SendLoot(guid, LOOT_SKINNING, LockType(effect->EffectMiscValue)); - - // not allow use skill grow at item base open - if (!m_CastItem && skillId != SKILL_NONE) - { - // update skill if really known - if (uint32 pureSkillValue = player->GetPureSkillValue(skillId)) - { - if (gameObjTarget) - { - // Allow one skill-up until respawned - if (!gameObjTarget->IsInSkillupList(player) && - player->UpdateGatherSkill(skillId, pureSkillValue, reqSkillValue)) - gameObjTarget->AddToSkillupList(player); - } - else if (itemTarget) - { - // Do one skill-up - player->UpdateGatherSkill(skillId, pureSkillValue, reqSkillValue); - } - } - } -} - -void Spell::EffectSummonChangeItem(SpellEffectEntry const* effect) -{ - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - Player* player = (Player*)m_caster; - - // applied only to using item - if (!m_CastItem) - return; - - // ... only to item in own inventory/bank/equip_slot - if (m_CastItem->GetOwnerGuid() != player->GetObjectGuid()) - return; - - uint32 newitemid = effect->EffectItemType; - if (!newitemid) - return; - - Item* oldItem = m_CastItem; - - // prevent crash at access and unexpected charges counting with item update queue corrupt - ClearCastItem(); - - player->ConvertItem(oldItem, newitemid); -} - -void Spell::EffectProficiency(SpellEffectEntry const* /*effect*/) -{ - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - Player *p_target = (Player*)unitTarget; - - SpellEquippedItemsEntry const* eqItems = m_spellInfo->GetSpellEquippedItems(); - - if (!eqItems) - return; - - if (eqItems->EquippedItemClass == ITEM_CLASS_WEAPON && !(p_target->GetWeaponProficiency() & eqItems->EquippedItemSubClassMask)) - { - p_target->AddWeaponProficiency(eqItems->EquippedItemSubClassMask); - p_target->SendProficiency(ITEM_CLASS_WEAPON, p_target->GetWeaponProficiency()); - } - if (eqItems->EquippedItemClass == ITEM_CLASS_ARMOR && !(p_target->GetArmorProficiency() & eqItems->EquippedItemSubClassMask)) - { - p_target->AddArmorProficiency(eqItems->EquippedItemSubClassMask); - p_target->SendProficiency(ITEM_CLASS_ARMOR, p_target->GetArmorProficiency()); - } -} - -void Spell::EffectApplyAreaAura(SpellEffectEntry const* effect) -{ - if (!unitTarget) - return; - if (!unitTarget->isAlive()) - return; - - AreaAura* Aur = new AreaAura(m_spellInfo, SpellEffectIndex(effect->EffectIndex), &m_currentBasePoints[effect->EffectIndex], m_spellAuraHolder, unitTarget, m_caster, m_CastItem); - m_spellAuraHolder->AddAura(Aur, SpellEffectIndex(effect->EffectIndex)); -} - -void Spell::EffectSummonType(SpellEffectEntry const* effect) -{ - uint32 prop_id = effect->EffectMiscValueB; - SummonPropertiesEntry const *summon_prop = sSummonPropertiesStore.LookupEntry(prop_id); - if(!summon_prop) - { - sLog.outError("EffectSummonType: Unhandled summon type %u", prop_id); - return; - } - - // Pet's are atm handled differently - if (summon_prop->Group == SUMMON_PROP_GROUP_PETS && prop_id != 1562) - { - DoSummonPet(effect); - return; - } - - // Expected Amount: TODO - there are quite some exceptions (like totems, engineering dragonlings..) - uint32 amount = damage > 0 ? damage : 1; - - // basepoints of SUMMON_PROP_GROUP_VEHICLE is often a spellId, set amount to 1 - if (summon_prop->Group == SUMMON_PROP_GROUP_VEHICLE) - amount = 1; - - // Get casting object - WorldObject* realCaster = GetCastingObject(); - if (!realCaster) - { - sLog.outError("EffectSummonType: No Casting Object found for spell %u, (caster = %s)", m_spellInfo->Id, m_caster->GetGuidStr().c_str()); - return; - } - - Unit* responsibleCaster = m_originalCaster; - if (realCaster->GetTypeId() == TYPEID_GAMEOBJECT) - responsibleCaster = ((GameObject*)realCaster)->GetOwner(); - - // Expected Level (Totem, Pet and Critter may not use this) - uint32 level = responsibleCaster ? responsibleCaster->getLevel() : m_caster->getLevel(); - // level of creature summoned using engineering item based at engineering skill level - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_CastItem) - { - ItemPrototype const* proto = m_CastItem->GetProto(); - if (proto && proto->RequiredSkill == SKILL_ENGINEERING) - if (uint16 engineeringSkill = ((Player*)m_caster)->GetSkillValue(SKILL_ENGINEERING)) - { - level = engineeringSkill / 5; - amount = 1; // TODO HACK (needs a neat way of doing) - } - } - - CreatureSummonPositions summonPositions; - summonPositions.resize(amount, CreaturePosition()); - - // Set middle position - if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) - m_targets.getDestination(summonPositions[0].x, summonPositions[0].y, summonPositions[0].z); - else - { - realCaster->GetPosition(summonPositions[0].x, summonPositions[0].y, summonPositions[0].z); - - // TODO - Is this really an error? - sLog.outDebug("Spell Effect EFFECT_SUMMON (%u) - summon without destination (spell id %u, effIndex %u)", effect->Effect, m_spellInfo->Id, SpellEffectIndex(effect->EffectIndex)); - } - - // Set summon positions - float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(effect->GetRadiusIndex())); - CreatureSummonPositions::iterator itr = summonPositions.begin(); - for (++itr; itr != summonPositions.end(); ++itr) // In case of multiple summons around position for not-fist positions - { - if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION || radius > 1.0f) - { - realCaster->GetRandomPoint(summonPositions[0].x, summonPositions[0].y, summonPositions[0].z, radius, itr->x, itr->y, itr->z); - if (realCaster->GetMap()->GetHitPosition(summonPositions[0].x, summonPositions[0].y, summonPositions[0].z, itr->x, itr->y, itr->z, m_caster->GetPhaseMask(), -0.5f)) - realCaster->UpdateAllowedPositionZ(itr->x, itr->y, itr->z); - } - else // Get a point near the caster - { - realCaster->GetRandomPoint(summonPositions[0].x, summonPositions[0].y, summonPositions[0].z, radius, itr->x, itr->y, itr->z); - if (realCaster->GetMap()->GetHitPosition(summonPositions[0].x, summonPositions[0].y, summonPositions[0].z, itr->x, itr->y, itr->z, m_caster->GetPhaseMask(), -0.5f)) - realCaster->UpdateAllowedPositionZ(itr->x, itr->y, itr->z); - } - } - - bool summonResult = false; - switch (summon_prop->Group) - { - // faction handled later on, or loaded from template - case SUMMON_PROP_GROUP_WILD: - case SUMMON_PROP_GROUP_FRIENDLY: - { - switch (summon_prop->Title) // better from known way sorting summons by AI types - { - case UNITNAME_SUMMON_TITLE_NONE: - { - // those are classical totems - effectbasepoints is their hp and not summon ammount! - // 121: 23035, battlestands - // 647: 52893, Anti-Magic Zone (npc used) - if (prop_id == 121 || prop_id == 647) - summonResult = DoSummonTotem(effect); - else - summonResult = DoSummonWild(summonPositions, summon_prop, effect, level); - break; - } - case UNITNAME_SUMMON_TITLE_PET: - case UNITNAME_SUMMON_TITLE_MINION: - case UNITNAME_SUMMON_TITLE_RUNEBLADE: - summonResult = DoSummonGuardian(summonPositions, summon_prop, effect, level); - break; - case UNITNAME_SUMMON_TITLE_GUARDIAN: - { - if (prop_id == 61) // mixed guardians, totems, statues - { - // * Stone Statue, etc -- fits much better totem AI - if (m_spellInfo->SpellIconID == 2056) - summonResult = DoSummonTotem(effect); - else - { - // possible sort totems/guardians only by summon creature type - CreatureInfo const* cInfo = ObjectMgr::GetCreatureTemplate(effect->EffectMiscValue); - - if (!cInfo) - return; - - // FIXME: not all totems and similar cases selected by this check... - if (cInfo->type == CREATURE_TYPE_TOTEM) - summonResult = DoSummonTotem(effect); - else - summonResult = DoSummonGuardian(summonPositions, summon_prop, effect, level); - } - } - else - summonResult = DoSummonGuardian(summonPositions, summon_prop, effect, level); - break; - } - case UNITNAME_SUMMON_TITLE_CONSTRUCT: - { - if (prop_id == 2913) // Scrapbot - summonResult = DoSummonWild(summonPositions, summon_prop, effect, level); - else - summonResult = DoSummonGuardian(summonPositions, summon_prop, effect, level); - break; - } - case UNITNAME_SUMMON_TITLE_TOTEM: - summonResult = DoSummonTotem(effect, summon_prop->Slot); - break; - case UNITNAME_SUMMON_TITLE_COMPANION: - // slot 6 set for critters that can help to player in fighting - if (summon_prop->Slot == 6) - summonResult = DoSummonGuardian(summonPositions, summon_prop, effect, level); - else - summonResult = DoSummonCritter(summonPositions, summon_prop, effect, level); - break; - case UNITNAME_SUMMON_TITLE_OPPONENT: - case UNITNAME_SUMMON_TITLE_VEHICLE: - case UNITNAME_SUMMON_TITLE_MOUNT: - case UNITNAME_SUMMON_TITLE_LIGHTWELL: - case UNITNAME_SUMMON_TITLE_BUTLER: - summonResult = DoSummonWild(summonPositions, summon_prop, effect, level); - break; - default: - sLog.outError("EffectSummonType: Unhandled summon title %u", summon_prop->Title); - break; - } - break; - } - case SUMMON_PROP_GROUP_PETS: - { - // FIXME : multiple summons - not yet supported as pet - // 1562 - force of nature - sid 33831 - // 1161 - feral spirit - sid 51533 - if (prop_id == 1562) // 3 uncontrolable instead of one controllable :/ - summonResult = DoSummonGuardian(summonPositions, summon_prop, effect, level); - break; - } - case SUMMON_PROP_GROUP_CONTROLLABLE: - { - // TODO: Fix spell 46619 - if (m_spellInfo->Id != 46619) - summonResult = DoSummonPossessed(summonPositions, summon_prop, effect, level); - break; - } - case SUMMON_PROP_GROUP_VEHICLE: - { - summonResult = DoSummonVehicle(summonPositions, summon_prop, effect, level); - break; - } - default: - sLog.outError("EffectSummonType: Unhandled summon group type %u", summon_prop->Group); - break; - } - - if (!summonResult) - return; // No further handling required - - for (CreatureSummonPositions::iterator itr = summonPositions.begin(); itr != summonPositions.end(); ++itr) - { - MANGOS_ASSERT(itr->creature || itr != summonPositions.begin()); - if (!itr->creature) - { - sLog.outError("EffectSummonType: Expected to have %u NPCs summoned, but some failed (Spell id %u)", amount, m_spellInfo->Id); - continue; - } - - if (summon_prop->FactionId) - itr->creature->setFaction(summon_prop->FactionId); - // Else set faction to summoner's faction for pet-like summoned - else if ((summon_prop->Flags & SUMMON_PROP_FLAG_INHERIT_FACTION) || !itr->creature->IsTemporarySummon()) - itr->creature->setFaction(responsibleCaster ? responsibleCaster->getFaction() : m_caster->getFaction()); - - if (!itr->creature->IsTemporarySummon()) - { - itr->creature->AIM_Initialize(); - - m_caster->GetMap()->Add(itr->creature); - - // Notify Summoner - if (m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->AI()) - ((Creature*)m_caster)->AI()->JustSummoned(itr->creature); - if (m_originalCaster && m_originalCaster != m_caster && m_originalCaster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_originalCaster)->AI()) - ((Creature*)m_originalCaster)->AI()->JustSummoned(itr->creature); - } - } -} - -bool Spell::DoSummonWild(CreatureSummonPositions& list, SummonPropertiesEntry const* prop, SpellEffectEntry const* effect, uint32 level) -{ - MANGOS_ASSERT(!list.empty() && prop); - - uint32 creature_entry = effect->EffectMiscValue; - CreatureInfo const* cInfo = ObjectMgr::GetCreatureTemplate(creature_entry); - if (!cInfo) - { - sLog.outErrorDb("Spell::DoSummonWild: creature entry %u not found for spell %u.", creature_entry, m_spellInfo->Id); - return false; - } - - TempSummonType summonType = (m_duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN; - - for (CreatureSummonPositions::iterator itr = list.begin(); itr != list.end(); ++itr) - if (Creature* summon = m_caster->SummonCreature(creature_entry, itr->x, itr->y, itr->z, m_caster->GetOrientation(), summonType, m_duration)) - { - itr->creature = summon; - - summon->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); - - // UNIT_FIELD_CREATEDBY are not set for these kind of spells. - // Does exceptions exist? If so, what are they? - // summon->SetCreatorGuid(m_caster->GetObjectGuid()); - - // Notify original caster if not done already - if (m_originalCaster && m_originalCaster != m_caster && m_originalCaster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_originalCaster)->AI()) - ((Creature*)m_originalCaster)->AI()->JustSummoned(summon); - } - else - return false; - - return true; -} - -bool Spell::DoSummonCritter(CreatureSummonPositions& list, SummonPropertiesEntry const* prop, SpellEffectEntry const* effect, uint32 /*level*/) -{ - MANGOS_ASSERT(!list.empty() && prop); - - // ATM only first position is supported for summoning - uint32 pet_entry = effect->EffectMiscValue; - CreatureInfo const* cInfo = ObjectMgr::GetCreatureTemplate(pet_entry); - if (!cInfo) - { - sLog.outErrorDb("Spell::DoSummonCritter: creature entry %u not found for spell %u.", pet_entry, m_spellInfo->Id); - return false; - } - - Pet* old_critter = m_caster->GetMiniPet(); - - // for same pet just despawn (player unsummon command) - if (m_caster->GetTypeId() == TYPEID_PLAYER && old_critter && old_critter->GetEntry() == pet_entry) - { - m_caster->RemoveMiniPet(); - return false; - } - - // despawn old pet before summon new - if (old_critter) - m_caster->RemoveMiniPet(); - - // for (CreatureSummonPositions::iterator itr = list.begin(); itr != list.end(); ++itr) - CreatureCreatePos pos(m_caster->GetMap(), list[0].x, list[0].y, list[0].z, m_caster->GetOrientation(), m_caster->GetPhaseMask()); - - // summon new pet - Pet* critter = new Pet(MINI_PET); - - uint32 pet_number = sObjectMgr.GeneratePetNumber(); - if (!critter->Create(m_caster->GetMap()->GenerateLocalLowGuid(HIGHGUID_PET), pos, cInfo, pet_number)) - { - sLog.outError("Spell::EffectSummonCritter, spellid %u: no such creature entry %u", m_spellInfo->Id, pet_entry); - delete critter; - return false; - } - - // itr! - list[0].creature = critter; - - critter->SetRespawnCoord(pos); - - // critter->SetName(""); // generated by client - critter->SetOwnerGuid(m_caster->GetObjectGuid()); - critter->SetCreatorGuid(m_caster->GetObjectGuid()); - - critter->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); - - critter->InitPetCreateSpells(); // e.g. disgusting oozeling has a create spell as critter... - // critter->InitLevelupSpellsForLevel(); // none? - critter->SelectLevel(critter->GetCreatureInfo()); // some summoned creaters have different from 1 DB data for level/hp - critter->SetUInt32Value(UNIT_NPC_FLAGS, critter->GetCreatureInfo()->npcflag); - // some mini-pets have quests - // set timer for unsummon - if (m_duration > 0) - critter->SetDuration(m_duration); - - m_caster->SetMiniPet(critter); - - return true; -} - -bool Spell::DoSummonGuardian(CreatureSummonPositions& list, SummonPropertiesEntry const* prop, SpellEffectEntry const* effect, uint32 level) -{ - MANGOS_ASSERT(!list.empty() && prop); - - uint32 pet_entry = effect->EffectMiscValue; - CreatureInfo const* cInfo = ObjectMgr::GetCreatureTemplate(pet_entry); - if (!cInfo) - { - sLog.outErrorDb("Spell::DoSummonGuardian: creature entry %u not found for spell %u.", pet_entry, m_spellInfo->Id); - return false; - } - - PetType petType = prop->Title == UNITNAME_SUMMON_TITLE_COMPANION ? PROTECTOR_PET : GUARDIAN_PET; - - // second direct cast unsummon guardian(s) (guardians without like functionality have cooldown > spawn time) - if (!m_IsTriggeredSpell && m_caster->GetTypeId() == TYPEID_PLAYER) - { - bool found = false; - // including protector - while (Pet* old_summon = m_caster->FindGuardianWithEntry(pet_entry)) - { - old_summon->Unsummon(PET_SAVE_AS_DELETED, m_caster); - found = true; - } - - if (found) - return false; - } - - // protectors allowed only in single amount - if (petType == PROTECTOR_PET) - if (Pet* old_protector = m_caster->GetProtectorPet()) - old_protector->Unsummon(PET_SAVE_AS_DELETED, m_caster); - - // in another case summon new - for (CreatureSummonPositions::iterator itr = list.begin(); itr != list.end(); ++itr) - { - Pet* spawnCreature = new Pet(petType); - - CreatureCreatePos pos(m_caster->GetMap(), itr->x, itr->y, itr->z, -m_caster->GetOrientation(), m_caster->GetPhaseMask()); - - uint32 pet_number = sObjectMgr.GeneratePetNumber(); - if (!spawnCreature->Create(m_caster->GetMap()->GenerateLocalLowGuid(HIGHGUID_PET), pos, cInfo, pet_number)) - { - sLog.outError("Spell::DoSummonGuardian: can't create creature entry %u for spell %u.", pet_entry, m_spellInfo->Id); - delete spawnCreature; - return false; - } - - itr->creature = spawnCreature; - - spawnCreature->SetRespawnCoord(pos); - - if (m_duration > 0) - spawnCreature->SetDuration(m_duration); - - // spawnCreature->SetName(""); // generated by client - spawnCreature->SetOwnerGuid(m_caster->GetObjectGuid()); - spawnCreature->setPowerType(POWER_MANA); - spawnCreature->SetUInt32Value(UNIT_NPC_FLAGS, spawnCreature->GetCreatureInfo()->npcflag); - - spawnCreature->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, 0); - spawnCreature->SetCreatorGuid(m_caster->GetObjectGuid()); - spawnCreature->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); - - spawnCreature->InitStatsForLevel(level, m_caster); - spawnCreature->GetCharmInfo()->SetPetNumber(pet_number, false); - - m_caster->AddGuardian(spawnCreature); - } - - return true; -} - -bool Spell::DoSummonTotem(SpellEffectEntry const* effect, uint8 slot_dbc) -{ - // DBC store slots starting from 1, with no slot 0 value) - int slot = slot_dbc ? slot_dbc - 1 : TOTEM_SLOT_NONE; - - // unsummon old totem - if (slot < MAX_TOTEM_SLOT) - if (Totem* OldTotem = m_caster->GetTotem(TotemSlot(slot))) - OldTotem->UnSummon(); - - // FIXME: Setup near to finish point because GetObjectBoundingRadius set in Create but some Create calls can be dependent from proper position - // if totem have creature_template_addon.auras with persistent point for example or script call - float angle = slot < MAX_TOTEM_SLOT ? M_PI_F / MAX_TOTEM_SLOT - (slot * 2 * M_PI_F / MAX_TOTEM_SLOT) : 0; - - CreatureCreatePos pos(m_caster, m_caster->GetOrientation(), 2.0f, angle); - - CreatureInfo const* cinfo = ObjectMgr::GetCreatureTemplate(effect->EffectMiscValue); - if (!cinfo) - { - sLog.outErrorDb("Creature entry %u does not exist but used in spell %u totem summon.", m_spellInfo->Id, effect->EffectMiscValue); - return false; - } - - Totem* pTotem = new Totem; - - if (!pTotem->Create(m_caster->GetMap()->GenerateLocalLowGuid(HIGHGUID_UNIT), pos, cinfo, m_caster)) - { - delete pTotem; - return false; - } - - pTotem->SetRespawnCoord(pos); - - if (slot < MAX_TOTEM_SLOT) - m_caster->_AddTotem(TotemSlot(slot), pTotem); - - // pTotem->SetName(""); // generated by client - pTotem->SetOwner(m_caster); - pTotem->SetTypeBySummonSpell(m_spellInfo); // must be after Create call where m_spells initialized - - pTotem->SetDuration(m_duration); - - if (damage) // if not spell info, DB values used - { - pTotem->SetMaxHealth(damage); - pTotem->SetHealth(damage); - } - - pTotem->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); - - if (m_caster->GetTypeId() == TYPEID_PLAYER) - pTotem->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); - - if (m_caster->IsPvP()) - pTotem->SetPvP(true); - - if (m_caster->IsFFAPvP()) - pTotem->SetFFAPvP(true); - - // sending SMSG_TOTEM_CREATED before add to map (done in Summon) - if (slot < MAX_TOTEM_SLOT && m_caster->GetTypeId() == TYPEID_PLAYER) - { - WorldPacket data(SMSG_TOTEM_CREATED, 1 + 8 + 4 + 4); - data << uint8(slot); - data << pTotem->GetObjectGuid(); - data << uint32(m_duration); - data << uint32(m_spellInfo->Id); - ((Player*)m_caster)->SendDirectMessage(&data); - } - - pTotem->Summon(m_caster); - - return false; -} - -bool Spell::DoSummonPossessed(CreatureSummonPositions& list, SummonPropertiesEntry const* prop, SpellEffectEntry const* effect, uint32 level) -{ - MANGOS_ASSERT(!list.empty() && prop); - - uint32 creatureEntry = effect->EffectMiscValue; - CreatureInfo const* cInfo = ObjectMgr::GetCreatureTemplate(creatureEntry); - if (!cInfo) - { - sLog.outErrorDb("Spell::DoSummonPossessed: creature entry %u not found for spell %u.", creatureEntry, m_spellInfo->Id); - return false; - } - - Creature* spawnCreature = m_caster->SummonCreature(creatureEntry, list[0].x, list[0].y, list[0].z, m_caster->GetOrientation(), TEMPSUMMON_CORPSE_DESPAWN, 0); - if (!spawnCreature) - { - sLog.outError("Spell::DoSummonPossessed: creature entry %u for spell %u could not be summoned.", creatureEntry, m_spellInfo->Id); - return false; - } - - list[0].creature = spawnCreature; - - // Changes to be sent - spawnCreature->SetCharmerGuid(m_caster->GetObjectGuid()); - spawnCreature->SetCreatorGuid(m_caster->GetObjectGuid()); - spawnCreature->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); - spawnCreature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); - - spawnCreature->SetLevel(level); - - spawnCreature->SetWalk(m_caster->IsWalking(), true); - // TODO: Set Fly (ie glyph dependend) - - // Internal changes - spawnCreature->addUnitState(UNIT_STAT_CONTROLLED); - - // Changes to owner - if (m_caster->GetTypeId() == TYPEID_PLAYER) - { - Player* player = (Player*)m_caster; - - player->GetCamera().SetView(spawnCreature); - - player->SetCharm(spawnCreature); - player->SetClientControl(spawnCreature, 1); - player->SetMover(spawnCreature); - - if (CharmInfo* charmInfo = spawnCreature->InitCharmInfo(spawnCreature)) - charmInfo->InitPossessCreateSpells(); - player->PossessSpellInitialize(); - } - - // Notify Summoner - if (m_originalCaster && m_originalCaster != m_caster && m_originalCaster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_originalCaster)->AI()) - ((Creature*)m_originalCaster)->AI()->JustSummoned(spawnCreature); - - return true; -} - -bool Spell::DoSummonPet(SpellEffectEntry const* effect) -{ - if (m_caster->GetPetGuid()) - return false; - - if (!unitTarget) - return false; - - uint32 pet_entry = effect->EffectMiscValue; - CreatureInfo const* cInfo = ObjectMgr::GetCreatureTemplate(pet_entry); - if (!cInfo) - { - sLog.outErrorDb("Spell::DoSummonPet: creature entry %u not found for spell %u.", pet_entry, m_spellInfo->Id); - return false; - } - - uint32 level = m_caster->getLevel(); // TODO Engineering Pets have also caster-level? (if they exist) - Pet* spawnCreature = new Pet(SUMMON_PET); - - if (m_caster->GetTypeId() == TYPEID_PLAYER && spawnCreature->LoadPetFromDB((Player*)m_caster, pet_entry)) - { - // Summon in dest location - if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) - spawnCreature->Relocate(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, -m_caster->GetOrientation()); - - // set timer for unsummon - if (m_duration > 0) - spawnCreature->SetDuration(m_duration); - - return false; - } - - // Summon in dest location - CreatureCreatePos pos(m_caster->GetMap(), m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, -m_caster->GetOrientation(), m_caster->GetPhaseMask()); - - if (!(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)) - pos = CreatureCreatePos(m_caster, -m_caster->GetOrientation()); - - Map* map = m_caster->GetMap(); - uint32 pet_number = sObjectMgr.GeneratePetNumber(); - if (!spawnCreature->Create(map->GenerateLocalLowGuid(HIGHGUID_PET), pos, cInfo, pet_number)) - { - sLog.outErrorDb("Spell::EffectSummon: can't create creature with entry %u for spell %u", cInfo->Entry, m_spellInfo->Id); - delete spawnCreature; - return false; - } - - spawnCreature->SetRespawnCoord(pos); - - // set timer for unsummon - if (m_duration > 0) - spawnCreature->SetDuration(m_duration); - - spawnCreature->SetOwnerGuid(m_caster->GetObjectGuid()); - spawnCreature->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); - spawnCreature->setPowerType(POWER_MANA); - spawnCreature->setFaction(m_caster->getFaction()); - spawnCreature->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, 0); - spawnCreature->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0); - spawnCreature->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, 1000); - spawnCreature->SetCreatorGuid(m_caster->GetObjectGuid()); - spawnCreature->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); - - spawnCreature->InitStatsForLevel(level, m_caster); - - spawnCreature->GetCharmInfo()->SetPetNumber(pet_number, false); - - spawnCreature->AIM_Initialize(); - spawnCreature->InitPetCreateSpells(); - spawnCreature->InitLevelupSpellsForLevel(); - spawnCreature->SetHealth(spawnCreature->GetMaxHealth()); - spawnCreature->SetPower(POWER_MANA, spawnCreature->GetMaxPower(POWER_MANA)); - - // spawnCreature->SetName(""); // generated by client - - map->Add((Creature*)spawnCreature); - - m_caster->SetPet(spawnCreature); - - if (m_caster->GetTypeId() == TYPEID_PLAYER) - { - spawnCreature->GetCharmInfo()->SetReactState(REACT_DEFENSIVE); - spawnCreature->SavePetToDB(PET_SAVE_AS_CURRENT); - ((Player*)m_caster)->PetSpellInitialize(); - } - - if (m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->AI()) - ((Creature*)m_caster)->AI()->JustSummoned((Creature*)spawnCreature); - if (m_originalCaster && m_originalCaster != m_caster && m_originalCaster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_originalCaster)->AI()) - ((Creature*)m_originalCaster)->AI()->JustSummoned((Creature*)spawnCreature); - - return false; -} - -bool Spell::DoSummonVehicle(CreatureSummonPositions& list, SummonPropertiesEntry const* prop, SpellEffectEntry const * effect, uint32 /*level*/) -{ - MANGOS_ASSERT(!list.empty() && prop); - - uint32 creatureEntry = effect->EffectMiscValue; - CreatureInfo const* cInfo = ObjectMgr::GetCreatureTemplate(creatureEntry); - if (!cInfo) - { - sLog.outErrorDb("Spell::DoSummonVehicle: creature entry %u not found for spell %u.", creatureEntry, m_spellInfo->Id); - return false; - } - - Creature* spawnCreature = m_caster->SummonCreature(creatureEntry, list[0].x, list[0].y, list[0].z, m_caster->GetOrientation(), - (m_duration == 0) ? TEMPSUMMON_CORPSE_DESPAWN : TEMPSUMMON_TIMED_OOC_OR_CORPSE_DESPAWN, m_duration); - - if (!spawnCreature) - { - sLog.outError("Spell::DoSummonVehicle: creature entry %u for spell %u could not be summoned.", creatureEntry, m_spellInfo->Id); - return false; - } - - list[0].creature = spawnCreature; - - // Changes to be sent - spawnCreature->SetCreatorGuid(m_caster->GetObjectGuid()); - spawnCreature->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); - //spawnCreature->SetLevel(level); // Do we need to set level for vehicles? - - // Board the caster right after summoning - SpellEntry const* controlSpellEntry = sSpellStore.LookupEntry(effect->CalculateSimpleValue()); - if (controlSpellEntry && IsSpellHaveAura(controlSpellEntry, SPELL_AURA_CONTROL_VEHICLE)) - m_caster->CastSpell(spawnCreature, controlSpellEntry, true); - else - m_caster->CastSpell(spawnCreature, SPELL_RIDE_VEHICLE_HARDCODED, true); - - // If the boarding failed... - if (!spawnCreature->HasAuraType(SPELL_AURA_CONTROL_VEHICLE)) - { - spawnCreature->ForcedDespawn(); - return false; - } - - // Notify Summoner - if (m_originalCaster && m_originalCaster != m_caster && m_originalCaster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_originalCaster)->AI()) - ((Creature*)m_originalCaster)->AI()->JustSummoned(spawnCreature); - - return true; -} - -void Spell::EffectLearnSpell(SpellEffectEntry const* effect) -{ - if (!unitTarget) - return; - - if (unitTarget->GetTypeId() != TYPEID_PLAYER) - { - if (m_caster->GetTypeId() == TYPEID_PLAYER) - EffectLearnPetSpell(effect); - - return; - } - - Player* player = (Player*)unitTarget; - - uint32 spellToLearn = ((m_spellInfo->Id==SPELL_ID_GENERIC_LEARN) || (m_spellInfo->Id==SPELL_ID_GENERIC_LEARN_PET)) ? damage : effect->EffectTriggerSpell; - player->learnSpell(spellToLearn, false); - - if (WorldObject const* caster = GetCastingObject()) - DEBUG_LOG("Spell: %s has learned spell %u from %s", player->GetGuidStr().c_str(), spellToLearn, caster->GetGuidStr().c_str()); -} - -void Spell::EffectDispel(SpellEffectEntry const* effect) -{ - if (!unitTarget) - return; - - // Fill possible dispel list - std::list > dispel_list; - - // Create dispel mask by dispel type - uint32 dispel_type = effect->EffectMiscValue; - uint32 dispelMask = GetDispellMask( DispelType(dispel_type) ); - Unit::SpellAuraHolderMap const& auras = unitTarget->GetSpellAuraHolderMap(); - for (Unit::SpellAuraHolderMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) - { - SpellAuraHolder *holder = itr->second; - if ((1<GetSpellProto()->GetDispel()) & dispelMask) - { - if(holder->GetSpellProto()->GetDispel() == DISPEL_MAGIC) - { - bool positive = true; - if (!holder->IsPositive()) - positive = false; - else - positive = !holder->GetSpellProto()->HasAttribute(SPELL_ATTR_EX_NEGATIVE); - - // do not remove positive auras if friendly target - // negative auras if non-friendly target - if (positive == unitTarget->IsFriendlyTo(m_caster)) - continue; - } - // Unholy Blight prevents dispel of diseases from target - else if (holder->GetSpellProto()->GetDispel() == DISPEL_DISEASE) - if (unitTarget->HasAura(50536)) - continue; - - dispel_list.push_back(std::pair(holder, holder->GetStackAmount())); - } - } - // Ok if exist some buffs for dispel try dispel it - if (!dispel_list.empty()) - { - std::list > success_list; // (spell_id,casterGuid) - std::list < uint32 > fail_list; // spell_id - - // some spells have effect value = 0 and all from its by meaning expect 1 - if (!damage) - damage = 1; - - // Dispel N = damage buffs (or while exist buffs for dispel) - for (int32 count = 0; count < damage && !dispel_list.empty(); ++count) - { - // Random select buff for dispel - std::list >::iterator dispel_itr = dispel_list.begin(); - std::advance(dispel_itr, urand(0, dispel_list.size() - 1)); - - SpellAuraHolder* holder = dispel_itr->first; - - dispel_itr->second -= 1; - - // remove entry from dispel_list if nothing left in stack - if (dispel_itr->second == 0) - dispel_list.erase(dispel_itr); - - SpellEntry const* spellInfo = holder->GetSpellProto(); - // Base dispel chance - // TODO: possible chance depend from spell level?? - int32 miss_chance = 0; - // Apply dispel mod from aura caster - if (Unit* caster = holder->GetCaster()) - { - if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_RESIST_DISPEL_CHANCE, miss_chance, this); - } - // Try dispel - if (roll_chance_i(miss_chance)) - fail_list.push_back(spellInfo->Id); - else - { - bool foundDispelled = false; - for (std::list >::iterator success_iter = success_list.begin(); success_iter != success_list.end(); ++success_iter) - { - if (success_iter->first->GetId() == holder->GetId() && success_iter->first->GetCasterGuid() == holder->GetCasterGuid()) - { - success_iter->second += 1; - foundDispelled = true; - break; - } - } - if (!foundDispelled) - success_list.push_back(std::pair(holder, 1)); - } - } - // Send success log and really remove auras - if (!success_list.empty()) - { - int32 count = success_list.size(); - WorldPacket data(SMSG_SPELLDISPELLOG, 8 + 8 + 4 + 1 + 4 + count * 5); - data << unitTarget->GetPackGUID(); // Victim GUID - data << m_caster->GetPackGUID(); // Caster GUID - data << uint32(m_spellInfo->Id); // Dispel spell id - data << uint8(0); // not used - data << uint32(count); // count - for (std::list >::iterator j = success_list.begin(); j != success_list.end(); ++j) - { - SpellAuraHolder* dispelledHolder = j->first; - data << uint32(dispelledHolder->GetId()); // Spell Id - data << uint8(0); // 0 - dispelled !=0 cleansed - unitTarget->RemoveAuraHolderDueToSpellByDispel(dispelledHolder->GetId(), j->second, dispelledHolder->GetCasterGuid(), m_caster); - } - m_caster->SendMessageToSet(&data, true); - - // On success dispel - // Devour Magic - if (m_spellInfo->GetSpellFamilyName() == SPELLFAMILY_WARLOCK && m_spellInfo->GetCategory() == SPELLCATEGORY_DEVOUR_MAGIC) - { - int32 heal_amount = m_spellInfo->CalculateSimpleValue(EFFECT_INDEX_1); - m_caster->CastCustomSpell(m_caster, 19658, &heal_amount, NULL, NULL, true); - } - } - // Send fail log to client - if (!fail_list.empty()) - { - // Failed to dispel - WorldPacket data(SMSG_DISPEL_FAILED, 8 + 8 + 4 + 4 * fail_list.size()); - data << m_caster->GetObjectGuid(); // Caster GUID - data << unitTarget->GetObjectGuid(); // Victim GUID - data << uint32(m_spellInfo->Id); // Dispel spell id - for (std::list< uint32 >::iterator j = fail_list.begin(); j != fail_list.end(); ++j) - data << uint32(*j); // Spell Id - m_caster->SendMessageToSet(&data, true); - } - } -} - -void Spell::EffectDualWield(SpellEffectEntry const* /*effect*/) -{ - if (unitTarget && unitTarget->GetTypeId() == TYPEID_PLAYER) - ((Player*)unitTarget)->SetCanDualWield(true); -} - -void Spell::EffectPull(SpellEffectEntry const* /*effect*/) -{ - // TODO: create a proper pull towards distract spell center for distract - DEBUG_LOG("WORLD: Spell Effect DUMMY"); -} - -void Spell::EffectDistract(SpellEffectEntry const* /*effect*/) -{ - // Check for possible target - if (!unitTarget || unitTarget->isInCombat()) - return; - - // target must be OK to do this - if (unitTarget->hasUnitState(UNIT_STAT_CAN_NOT_REACT)) - return; - - unitTarget->SetFacingTo(unitTarget->GetAngle(m_targets.m_destX, m_targets.m_destY)); - unitTarget->clearUnitState(UNIT_STAT_MOVING); - - if (unitTarget->GetTypeId() == TYPEID_UNIT) - unitTarget->GetMotionMaster()->MoveDistract(damage * IN_MILLISECONDS); -} - -void Spell::EffectPickPocket(SpellEffectEntry const* /*effect*/) -{ - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - // victim must be creature and attackable - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT || m_caster->IsFriendlyTo(unitTarget)) - return; - - // victim have to be alive and humanoid or undead - if (unitTarget->isAlive() && (unitTarget->GetCreatureTypeMask() & CREATURE_TYPEMASK_HUMANOID_OR_UNDEAD) != 0) - { - int32 chance = 10 + int32(m_caster->getLevel()) - int32(unitTarget->getLevel()); - - if (chance > irand(0, 19)) - { - // Stealing successful - // DEBUG_LOG("Sending loot from pickpocket"); - ((Player*)m_caster)->SendLoot(unitTarget->GetObjectGuid(), LOOT_PICKPOCKETING); - } - else - { - // Reveal action + get attack - m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); - unitTarget->AttackedBy(m_caster); - } - } -} - -void Spell::EffectAddFarsight(SpellEffectEntry const* effect) -{ - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - int32 duration = GetSpellDuration(m_spellInfo); - DynamicObject* dynObj = new DynamicObject; - - // set radius to 0: spell not expected to work as persistent aura - if(!dynObj->Create(m_caster->GetMap()->GenerateLocalLowGuid(HIGHGUID_DYNAMICOBJECT), m_caster, m_spellInfo->Id, SpellEffectIndex(effect->EffectIndex), m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, duration, 0, DYNAMIC_OBJECT_FARSIGHT_FOCUS)) - { - delete dynObj; - return; - } - - m_caster->AddDynObject(dynObj); - m_caster->GetMap()->Add(dynObj); - - ((Player*)m_caster)->GetCamera().SetView(dynObj); -} - -void Spell::EffectTeleUnitsFaceCaster(SpellEffectEntry const* effect) -{ - if (!unitTarget) - return; - - if (unitTarget->IsTaxiFlying()) - return; - - float fx, fy, fz; - if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) - m_targets.getDestination(fx, fy, fz); - else - { - float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(effect->GetRadiusIndex())); - m_caster->GetClosePoint(fx, fy, fz, unitTarget->GetObjectBoundingRadius(), dis); - } - - unitTarget->NearTeleportTo(fx, fy, fz, -m_caster->GetOrientation(), unitTarget == m_caster); -} - -void Spell::EffectLearnSkill(SpellEffectEntry const* effect) -{ - if (unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - if (damage < 0) - return; - - uint32 skillid = effect->EffectMiscValue; - uint16 skillval = ((Player*)unitTarget)->GetPureSkillValue(skillid); - ((Player*)unitTarget)->SetSkill(skillid, skillval ? skillval : 1, damage * 75, damage); - - if (WorldObject const* caster = GetCastingObject()) - DEBUG_LOG("Spell: %s has learned skill %u (to maxlevel %u) from %s", unitTarget->GetGuidStr().c_str(), skillid, damage * 75, caster->GetGuidStr().c_str()); -} - -void Spell::EffectTradeSkill(SpellEffectEntry const* /*effect*/) -{ - if (unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - // uint32 skillid = m_spellInfo->EffectMiscValue[i]; - // uint16 skillmax = ((Player*)unitTarget)->(skillid); - // ((Player*)unitTarget)->SetSkill(skillid,skillval?skillval:1,skillmax+75); -} - -void Spell::EffectEnchantItemPerm(SpellEffectEntry const* effect) -{ - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - if (!itemTarget) - return; - - uint32 enchant_id = effect->EffectMiscValue; - if (!enchant_id) - return; - - SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); - if (!pEnchant) - return; - - // item can be in trade slot and have owner diff. from caster - Player* item_owner = itemTarget->GetOwner(); - if (!item_owner) - return; - - Player* p_caster = (Player*)m_caster; - - // Enchanting a vellum requires special handling, as it creates a new item - // instead of modifying an existing one. - ItemPrototype const* targetProto = itemTarget->GetProto(); - if (targetProto->IsVellum() && effect->EffectItemType) - { - unitTarget = m_caster; - DoCreateItem(effect, effect->EffectItemType); - // Vellum target case: Target becomes additional reagent, new scroll item created instead in Spell::EffectEnchantItemPerm() - // cannot already delete in TakeReagents() unfortunately - p_caster->DestroyItemCount(targetProto->ItemId, 1, true); - return; - } - - // Using enchant stored on scroll does not increase enchanting skill! (Already granted on scroll creation) - if (!(m_CastItem && m_CastItem->GetProto()->Flags & ITEM_FLAG_ENCHANT_SCROLL)) - p_caster->UpdateCraftSkill(m_spellInfo->Id); - - if (item_owner != p_caster && p_caster->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_BOOL_GM_LOG_TRADE)) - { - sLog.outCommand(p_caster->GetSession()->GetAccountId(), "GM %s (Account: %u) enchanting(perm): %s (Entry: %d) for player: %s (Account: %u)", - p_caster->GetName(), p_caster->GetSession()->GetAccountId(), - itemTarget->GetProto()->Name1, itemTarget->GetEntry(), - item_owner->GetName(), item_owner->GetSession()->GetAccountId()); - } - - // remove old enchanting before applying new if equipped - item_owner->ApplyEnchantment(itemTarget, PERM_ENCHANTMENT_SLOT, false); - - itemTarget->SetEnchantment(PERM_ENCHANTMENT_SLOT, enchant_id, 0, 0); - - // add new enchanting if equipped - item_owner->ApplyEnchantment(itemTarget, PERM_ENCHANTMENT_SLOT, true); -} - -void Spell::EffectEnchantItemPrismatic(SpellEffectEntry const* effect) -{ - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - if (!itemTarget) - return; - - Player* p_caster = (Player*)m_caster; - - uint32 enchant_id = effect->EffectMiscValue; - if (!enchant_id) - return; - - SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); - if (!pEnchant) - return; - - // support only enchantings with add socket in this slot - { - bool add_socket = false; - for (int i = 0; i < 3; ++i) - { - if (pEnchant->type[i] == ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET) - { - add_socket = true; - break; - } - } - if (!add_socket) - { - sLog.outError("Spell::EffectEnchantItemPrismatic: attempt apply enchant spell %u with SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC (%u) but without ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET (%u), not suppoted yet.", - m_spellInfo->Id, SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC, ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET); - return; - } - } - - // item can be in trade slot and have owner diff. from caster - Player* item_owner = itemTarget->GetOwner(); - if (!item_owner) - return; - - if (item_owner != p_caster && p_caster->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_BOOL_GM_LOG_TRADE)) - { - sLog.outCommand(p_caster->GetSession()->GetAccountId(), "GM %s (Account: %u) enchanting(perm): %s (Entry: %d) for player: %s (Account: %u)", - p_caster->GetName(), p_caster->GetSession()->GetAccountId(), - itemTarget->GetProto()->Name1, itemTarget->GetEntry(), - item_owner->GetName(), item_owner->GetSession()->GetAccountId()); - } - - // remove old enchanting before applying new if equipped - item_owner->ApplyEnchantment(itemTarget, PRISMATIC_ENCHANTMENT_SLOT, false); - - itemTarget->SetEnchantment(PRISMATIC_ENCHANTMENT_SLOT, enchant_id, 0, 0); - - // add new enchanting if equipped - item_owner->ApplyEnchantment(itemTarget, PRISMATIC_ENCHANTMENT_SLOT, true); -} - -void Spell::EffectEnchantItemTmp(SpellEffectEntry const* effect) -{ - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - Player* p_caster = (Player*)m_caster; - - // Rockbiter Weapon - SpellClassOptionsEntry const* classOptions = m_spellInfo->GetSpellClassOptions(); - if (classOptions && classOptions->SpellFamilyName == SPELLFAMILY_SHAMAN && classOptions->SpellFamilyFlags & UI64LIT(0x0000000000400000)) - { - uint32 spell_id = 0; - - // enchanting spell selected by calculated damage-per-sec stored in Effect[1] base value - // Note: damage calculated (correctly) with rounding int32(float(v)) but - // RW enchantments applied damage int32(float(v)+0.5), this create 0..1 difference sometime - switch (damage) - { - // Rank 1 - case 2: spell_id = 36744; break; // 0% [ 7% == 2, 14% == 2, 20% == 2] - // Rank 2 - case 4: spell_id = 36753; break; // 0% [ 7% == 4, 14% == 4] - case 5: spell_id = 36751; break; // 20% - // Rank 3 - case 6: spell_id = 36754; break; // 0% [ 7% == 6, 14% == 6] - case 7: spell_id = 36755; break; // 20% - // Rank 4 - case 9: spell_id = 36761; break; // 0% [ 7% == 6] - case 10: spell_id = 36758; break; // 14% - case 11: spell_id = 36760; break; // 20% - default: - sLog.outError("Spell::EffectEnchantItemTmp: Damage %u not handled in S'RW", damage); - return; - } - - SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell_id); - if (!spellInfo) - { - sLog.outError("Spell::EffectEnchantItemTmp: unknown spell id %i", spell_id); - return; - } - - Spell* spell = new Spell(m_caster, spellInfo, true); - SpellCastTargets targets; - targets.setItemTarget(itemTarget); - spell->prepare(&targets); - return; - } - - if (!itemTarget) - return; - - uint32 enchant_id = effect->EffectMiscValue; - - if (!enchant_id) - { - sLog.outError("Spell %u Effect %u (SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY) have 0 as enchanting id",m_spellInfo->Id,effect->EffectIndex); - return; - } - - SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); - if (!pEnchant) - { - sLog.outError("Spell %u Effect %u (SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY) have nonexistent enchanting id %u ",m_spellInfo->Id,effect->EffectIndex,enchant_id); - return; - } - - // select enchantment duration - uint32 duration; - - // rogue family enchantments exception by duration - if (m_spellInfo->Id == 38615) - duration = 1800; // 30 mins - // other rogue family enchantments always 1 hour (some have spell damage=0, but some have wrong data in EffBasePoints) - else if(classOptions && classOptions->SpellFamilyName == SPELLFAMILY_ROGUE) - duration = 3600; // 1 hour - // shaman family enchantments - else if(classOptions && classOptions->SpellFamilyName == SPELLFAMILY_SHAMAN) - duration = 1800; // 30 mins - // other cases with this SpellVisual already selected - else if (m_spellInfo->SpellVisual[0] == 215) - duration = 1800; // 30 mins - // some fishing pole bonuses - else if (m_spellInfo->SpellVisual[0] == 563) - duration = 600; // 10 mins - // shaman rockbiter enchantments - else if (m_spellInfo->SpellVisual[0] == 0) - duration = 1800; // 30 mins - else if (m_spellInfo->Id == 29702) - duration = 300; // 5 mins - else if (m_spellInfo->Id == 37360) - duration = 300; // 5 mins - // default case - else - duration = 3600; // 1 hour - - // item can be in trade slot and have owner diff. from caster - Player* item_owner = itemTarget->GetOwner(); - if (!item_owner) - return; - - if (item_owner != p_caster && p_caster->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_BOOL_GM_LOG_TRADE)) - { - sLog.outCommand(p_caster->GetSession()->GetAccountId(), "GM %s (Account: %u) enchanting(temp): %s (Entry: %d) for player: %s (Account: %u)", - p_caster->GetName(), p_caster->GetSession()->GetAccountId(), - itemTarget->GetProto()->Name1, itemTarget->GetEntry(), - item_owner->GetName(), item_owner->GetSession()->GetAccountId()); - } - - // remove old enchanting before applying new if equipped - item_owner->ApplyEnchantment(itemTarget, TEMP_ENCHANTMENT_SLOT, false); - - itemTarget->SetEnchantment(TEMP_ENCHANTMENT_SLOT, enchant_id, duration * 1000, 0); - - // add new enchanting if equipped - item_owner->ApplyEnchantment(itemTarget, TEMP_ENCHANTMENT_SLOT, true); -} - -void Spell::EffectTameCreature(SpellEffectEntry const* /*effect*/) -{ - // Caster must be player, checked in Spell::CheckCast - // Spell can be triggered, we need to check original caster prior to caster - Player* plr = (Player*)GetAffectiveCaster(); - - Creature* creatureTarget = (Creature*)unitTarget; - - // cast finish successfully - // SendChannelUpdate(0); - finish(); - - Pet* pet = new Pet(HUNTER_PET); - - if (!pet->CreateBaseAtCreature(creatureTarget)) - { - delete pet; - return; - } - - pet->SetOwnerGuid(plr->GetObjectGuid()); - pet->SetCreatorGuid(plr->GetObjectGuid()); - pet->setFaction(plr->getFaction()); - pet->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); - - if (plr->IsPvP()) - pet->SetPvP(true); - - if (plr->IsFFAPvP()) - pet->SetFFAPvP(true); - - // level of hunter pet can't be less owner level at 5 levels - uint32 level = creatureTarget->getLevel() + 5 < plr->getLevel() ? (plr->getLevel() - 5) : creatureTarget->getLevel(); - - if (!pet->InitStatsForLevel(level)) - { - sLog.outError("Pet::InitStatsForLevel() failed for creature (Entry: %u)!", creatureTarget->GetEntry()); - delete pet; - return; - } - - pet->GetCharmInfo()->SetPetNumber(sObjectMgr.GeneratePetNumber(), true); - // this enables pet details window (Shift+P) - pet->AIM_Initialize(); - pet->InitPetCreateSpells(); - pet->InitLevelupSpellsForLevel(); - pet->InitTalentForLevel(); - pet->SetHealth(pet->GetMaxHealth()); - - // "kill" original creature - creatureTarget->ForcedDespawn(); - - // prepare visual effect for levelup - pet->SetUInt32Value(UNIT_FIELD_LEVEL, level - 1); - - // add to world - pet->GetMap()->Add((Creature*)pet); - - // visual effect for levelup - pet->SetUInt32Value(UNIT_FIELD_LEVEL, level); - - // caster have pet now - plr->SetPet(pet); - - pet->SavePetToDB(PET_SAVE_AS_CURRENT); - plr->PetSpellInitialize(); -} - -void Spell::EffectSummonPet(SpellEffectEntry const* effect) -{ - uint32 petentry = effect->EffectMiscValue; - - Pet* OldSummon = m_caster->GetPet(); - - // if pet requested type already exist - if (OldSummon) - { - if ((petentry == 0 || OldSummon->GetEntry() == petentry) && OldSummon->getPetType() != SUMMON_PET) - { - // pet in corpse state can't be summoned - if (OldSummon->isDead()) - return; - - OldSummon->GetMap()->Remove((Creature*)OldSummon, false); - - float px, py, pz; - m_caster->GetClosePoint(px, py, pz, OldSummon->GetObjectBoundingRadius()); - - OldSummon->Relocate(px, py, pz, OldSummon->GetOrientation()); - m_caster->GetMap()->Add((Creature*)OldSummon); - - if (m_caster->GetTypeId() == TYPEID_PLAYER && OldSummon->isControlled()) - { - ((Player*)m_caster)->PetSpellInitialize(); - } - return; - } - - if (m_caster->GetTypeId() == TYPEID_PLAYER) - OldSummon->Unsummon(OldSummon->getPetType() == HUNTER_PET ? PET_SAVE_AS_DELETED : PET_SAVE_NOT_IN_SLOT, m_caster); - else - return; - } - - CreatureInfo const* cInfo = ObjectMgr::GetCreatureTemplate(petentry); - - // == 0 in case call current pet, check only real summon case - if (petentry && !cInfo) - { - sLog.outErrorDb("EffectSummonPet: creature entry %u not found for spell %u.", petentry, m_spellInfo->Id); - return; - } - - Pet* NewSummon = new Pet; - - // petentry==0 for hunter "call pet" (current pet summoned if any) - if (m_caster->GetTypeId() == TYPEID_PLAYER && NewSummon->LoadPetFromDB((Player*)m_caster, petentry)) - return; - - // not error in case fail hunter call pet - if (!petentry) - { - delete NewSummon; - return; - } - - CreatureCreatePos pos(m_caster, m_caster->GetOrientation()); - - Map* map = m_caster->GetMap(); - uint32 pet_number = sObjectMgr.GeneratePetNumber(); - if (!NewSummon->Create(map->GenerateLocalLowGuid(HIGHGUID_PET), pos, cInfo, pet_number)) - { - delete NewSummon; - return; - } - - NewSummon->SetRespawnCoord(pos); - - uint32 petlevel = m_caster->getLevel(); - NewSummon->setPetType(SUMMON_PET); - - uint32 faction = m_caster->getFaction(); - if (m_caster->GetTypeId() == TYPEID_UNIT) - { - if (((Creature*)m_caster)->IsTotem()) - NewSummon->GetCharmInfo()->SetReactState(REACT_AGGRESSIVE); - else - NewSummon->GetCharmInfo()->SetReactState(REACT_DEFENSIVE); - } - - NewSummon->SetOwnerGuid(m_caster->GetObjectGuid()); - NewSummon->SetCreatorGuid(m_caster->GetObjectGuid()); - NewSummon->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); - NewSummon->setFaction(faction); - NewSummon->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, uint32(time(NULL))); - NewSummon->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0); - NewSummon->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, 1000); - NewSummon->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); - - NewSummon->GetCharmInfo()->SetPetNumber(pet_number, true); - // this enables pet details window (Shift+P) - - if (m_caster->IsPvP()) - NewSummon->SetPvP(true); - - if (m_caster->IsFFAPvP()) - NewSummon->SetFFAPvP(true); - - NewSummon->InitStatsForLevel(petlevel, m_caster); - NewSummon->InitPetCreateSpells(); - NewSummon->InitLevelupSpellsForLevel(); - NewSummon->InitTalentForLevel(); - - if (m_caster->GetTypeId() == TYPEID_PLAYER && NewSummon->getPetType() == SUMMON_PET) - { - // generate new name for summon pet - std::string new_name = sObjectMgr.GeneratePetName(petentry); - if (!new_name.empty()) - NewSummon->SetName(new_name); - } - - if (NewSummon->getPetType() == HUNTER_PET) - { - NewSummon->RemoveByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED); - NewSummon->SetByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_ABANDONED); - } - - NewSummon->AIM_Initialize(); - NewSummon->SetHealth(NewSummon->GetMaxHealth()); - NewSummon->SetPower(POWER_MANA, NewSummon->GetMaxPower(POWER_MANA)); - - map->Add((Creature*)NewSummon); - - m_caster->SetPet(NewSummon); - DEBUG_LOG("New Pet has guid %u", NewSummon->GetGUIDLow()); - - if (m_caster->GetTypeId() == TYPEID_PLAYER) - { - NewSummon->SavePetToDB(PET_SAVE_AS_CURRENT); - ((Player*)m_caster)->PetSpellInitialize(); - } -} - -void Spell::EffectLearnPetSpell(SpellEffectEntry const* effect) -{ - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - Player* _player = (Player*)m_caster; - - Pet* pet = _player->GetPet(); - if (!pet) - return; - if (!pet->isAlive()) - return; - - SpellEntry const *learn_spellproto = sSpellStore.LookupEntry(effect->EffectTriggerSpell); - if(!learn_spellproto) - return; - - pet->learnSpell(learn_spellproto->Id); - - pet->SavePetToDB(PET_SAVE_AS_CURRENT); - _player->PetSpellInitialize(); - - if (WorldObject const* caster = GetCastingObject()) - DEBUG_LOG("Spell: %s has learned spell %u from %s", pet->GetGuidStr().c_str(), learn_spellproto->Id, caster->GetGuidStr().c_str()); -} - -void Spell::EffectTaunt(SpellEffectEntry const* /*effect*/) -{ - if (!unitTarget) - return; - - // this effect use before aura Taunt apply for prevent taunt already attacking target - // for spell as marked "non effective at already attacking target" - if (unitTarget->GetTypeId() != TYPEID_PLAYER) - { - if (unitTarget->getVictim() == m_caster) - { - SendCastResult(SPELL_FAILED_DONT_REPORT); - return; - } - } - - // Also use this effect to set the taunter's threat to the taunted creature's highest value - if (unitTarget->CanHaveThreatList() && unitTarget->getThreatManager().getCurrentVictim()) - unitTarget->getThreatManager().addThreat(m_caster, unitTarget->getThreatManager().getCurrentVictim()->getThreat()); -} - -void Spell::EffectWeaponDmg(SpellEffectEntry const* effect) -{ - if (!unitTarget) - return; - if (!unitTarget->isAlive()) - return; - - // multiple weapon dmg effect workaround - // execute only the last weapon damage - // and handle all effects at once - for (int j = 0; j < MAX_EFFECT_INDEX; ++j) - { - switch(m_spellInfo->GetSpellEffectIdByIndex(SpellEffectIndex(j))) - { - case SPELL_EFFECT_WEAPON_DAMAGE: - case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL: - case SPELL_EFFECT_NORMALIZED_WEAPON_DMG: - case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE: - if (j < int(effect->EffectIndex)) // we must calculate only at last weapon effect - return; - break; - } - } - - // some spell specific modifiers - bool spellBonusNeedWeaponDamagePercentMod = false; // if set applied weapon damage percent mode to spell bonus - - float weaponDamagePercentMod = 1.0f; // applied to weapon damage and to fixed effect damage bonus - float totalDamagePercentMod = 1.0f; // applied to final bonus+weapon damage - bool normalized = false; - - int32 spell_bonus = 0; // bonus specific for spell - - SpellClassOptionsEntry const* classOptions = m_spellInfo->GetSpellClassOptions(); - - switch(m_spellInfo->GetSpellFamilyName()) - { - case SPELLFAMILY_GENERIC: - { - switch (m_spellInfo->Id) - { - // for spells with divided damage to targets - case 66765: case 66809: case 67331: // Meteor Fists - case 67333: // Meteor Fists - case 69055: // Bone Slice - case 71021: // Saber Lash - { - uint32 count = 0; - for(TargetList::const_iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - if(ihit->effectMask & (1<EffectIndex)) - ++count; - - totalDamagePercentMod /= float(count); // divide to all targets - break; - } - } - break; - } - case SPELLFAMILY_WARRIOR: - { - // Devastate - if (m_spellInfo->SpellVisual[0] == 12295 && m_spellInfo->SpellIconID == 1508) - { - // Sunder Armor - Aura* sunder = unitTarget->GetAura(SPELL_AURA_MOD_RESISTANCE_PCT, SPELLFAMILY_WARRIOR, UI64LIT(0x0000000000004000), 0x00000000, m_caster->GetObjectGuid()); - - // Devastate bonus and sunder armor refresh - if (sunder) - { - sunder->GetHolder()->RefreshHolder(); - spell_bonus += sunder->GetStackAmount() * CalculateDamage(EFFECT_INDEX_2, unitTarget); - } - - // Devastate causing Sunder Armor Effect - // and no need to cast over max stack amount - if (!sunder || sunder->GetStackAmount() < sunder->GetSpellProto()->GetStackAmount()) - m_caster->CastSpell(unitTarget, 58567, true); - } - break; - } - case SPELLFAMILY_ROGUE: - { - // Mutilate (for each hand) - if(classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x600000000)) - { - bool found = false; - // fast check - if (unitTarget->HasAuraState(AURA_STATE_DEADLY_POISON)) - found = true; - // full aura scan - else - { - Unit::SpellAuraHolderMap const& auras = unitTarget->GetSpellAuraHolderMap(); - for (Unit::SpellAuraHolderMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) - { - if(itr->second->GetSpellProto()->GetDispel() == DISPEL_POISON) - { - found = true; - break; - } - } - } - - if (found) - totalDamagePercentMod *= 1.2f; // 120% if poisoned - } - // Fan of Knives - else if (m_caster->GetTypeId()==TYPEID_PLAYER && classOptions && (classOptions->SpellFamilyFlags & UI64LIT(0x0004000000000000))) - { - Item* weapon = ((Player*)m_caster)->GetWeaponForAttack(m_attackType, true, true); - if (weapon && weapon->GetProto()->SubClass == ITEM_SUBCLASS_WEAPON_DAGGER) - totalDamagePercentMod *= 1.5f; // 150% to daggers - } - // Ghostly Strike - else if (m_caster->GetTypeId() == TYPEID_PLAYER && m_spellInfo->Id == 14278) - { - Item* weapon = ((Player*)m_caster)->GetWeaponForAttack(m_attackType, true, true); - if (weapon && weapon->GetProto()->SubClass == ITEM_SUBCLASS_WEAPON_DAGGER) - totalDamagePercentMod *= 1.44f; // 144% to daggers - } - // Hemorrhage - else if (m_caster->GetTypeId() == TYPEID_PLAYER && classOptions && (classOptions->SpellFamilyFlags & UI64LIT(0x2000000))) - { - Item* weapon = ((Player*)m_caster)->GetWeaponForAttack(m_attackType, true, true); - if (weapon && weapon->GetProto()->SubClass == ITEM_SUBCLASS_WEAPON_DAGGER) - totalDamagePercentMod *= 1.45f; // 145% to daggers - } - break; - } - case SPELLFAMILY_PALADIN: - { - // Judgement of Command - receive benefit from Spell Damage and Attack Power - if(classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x00020000000000)) - { - float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK); - int32 holy = m_caster->SpellBaseDamageBonusDone(GetSpellSchoolMask(m_spellInfo)); - if (holy < 0) - holy = 0; - spell_bonus += int32(ap * 0.08f) + int32(holy * 13 / 100); - } - break; - } - case SPELLFAMILY_HUNTER: - { - // Kill Shot - if (classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x80000000000000)) - { - // 0.4*RAP added to damage (that is 0.2 if we apply PercentMod (200%) to spell_bonus, too) - spellBonusNeedWeaponDamagePercentMod = true; - spell_bonus += int32(0.2f * m_caster->GetTotalAttackPowerValue(RANGED_ATTACK)); - } - break; - } - case SPELLFAMILY_SHAMAN: - { - // Skyshatter Harness item set bonus - // Stormstrike - if(classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x001000000000)) - { - Unit::AuraList const& m_OverrideClassScript = m_caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); - for (Unit::AuraList::const_iterator citr = m_OverrideClassScript.begin(); citr != m_OverrideClassScript.end(); ++citr) - { - // Stormstrike AP Buff - if ((*citr)->GetModifier()->m_miscvalue == 5634) - { - m_caster->CastSpell(m_caster, 38430, true, NULL, *citr); - break; - } - } - } - break; - } - case SPELLFAMILY_DEATHKNIGHT: - { - // Blood Strike, Heart Strike, Obliterate - // Blood-Caked Strike - if (m_spellInfo->SpellIconID == 1736) - { - uint32 count = 0; - Unit::SpellAuraHolderMap const& auras = unitTarget->GetSpellAuraHolderMap(); - for (Unit::SpellAuraHolderMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) - { - if(itr->second->GetSpellProto()->GetDispel() == DISPEL_DISEASE && - itr->second->GetCasterGuid() == m_caster->GetObjectGuid()) - ++count; - } - - if (count) - { - // Effect 1(for Blood-Caked Strike)/3(other) damage is bonus - float bonus = count * CalculateDamage(m_spellInfo->SpellIconID == 1736 ? EFFECT_INDEX_0 : EFFECT_INDEX_2, unitTarget) / 100.0f; - // Blood Strike, Blood-Caked Strike and Obliterate store bonus*2 - if ((classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x0002000000400000)) || - m_spellInfo->SpellIconID == 1736) - bonus /= 2.0f; - - totalDamagePercentMod *= 1.0f + bonus; - } - - // Heart Strike secondary target - if (m_spellInfo->SpellIconID == 3145) - if (m_targets.getUnitTarget() != unitTarget) - weaponDamagePercentMod /= 2.0f; - } - // Glyph of Blood Strike - if( classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x0000000000400000) && - m_caster->HasAura(59332) && - unitTarget->HasAuraType(SPELL_AURA_MOD_DECREASE_SPEED)) - { - totalDamagePercentMod *= 1.2f; // 120% if snared - } - // Glyph of Death Strike - if( classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x0000000000000010) && - m_caster->HasAura(59336)) - { - int32 rp = m_caster->GetPower(POWER_RUNIC_POWER) / 10; - if (rp > 25) - rp = 25; - totalDamagePercentMod *= 1.0f + rp / 100.0f; - } - // Glyph of Plague Strike - if( classOptions && classOptions->SpellFamilyFlags & UI64LIT(0x0000000000000001) && - m_caster->HasAura(58657) ) - { - totalDamagePercentMod *= 1.2f; - } - break; - } - } - - int32 fixed_bonus = 0; - for (int j = 0; j < MAX_EFFECT_INDEX; ++j) - { - SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(SpellEffectIndex(j)); - if(!spellEffect) - continue; - - switch(spellEffect->Effect) - { - case SPELL_EFFECT_WEAPON_DAMAGE: - case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL: - fixed_bonus += CalculateDamage(SpellEffectIndex(j), unitTarget); - break; - case SPELL_EFFECT_NORMALIZED_WEAPON_DMG: - fixed_bonus += CalculateDamage(SpellEffectIndex(j), unitTarget); - normalized = true; - break; - case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE: - weaponDamagePercentMod *= float(CalculateDamage(SpellEffectIndex(j), unitTarget)) / 100.0f; - - // applied only to prev.effects fixed damage - fixed_bonus = int32(fixed_bonus * weaponDamagePercentMod); - break; - default: - break; // not weapon damage effect, just skip - } - } - - // apply weaponDamagePercentMod to spell bonus also - if (spellBonusNeedWeaponDamagePercentMod) - spell_bonus = int32(spell_bonus * weaponDamagePercentMod); - - // non-weapon damage - int32 bonus = spell_bonus + fixed_bonus; - - // apply to non-weapon bonus weapon total pct effect, weapon total flat effect included in weapon damage - if (bonus) - { - UnitMods unitMod; - switch (m_attackType) - { - default: - case BASE_ATTACK: unitMod = UNIT_MOD_DAMAGE_MAINHAND; break; - case OFF_ATTACK: unitMod = UNIT_MOD_DAMAGE_OFFHAND; break; - case RANGED_ATTACK: unitMod = UNIT_MOD_DAMAGE_RANGED; break; - } - - float weapon_total_pct = m_caster->GetModifierValue(unitMod, TOTAL_PCT); - bonus = int32(bonus * weapon_total_pct); - } - - // + weapon damage with applied weapon% dmg to base weapon damage in call - bonus += int32(m_caster->CalculateDamage(m_attackType, normalized) * weaponDamagePercentMod); - - // total damage - bonus = int32(bonus * totalDamagePercentMod); - - // prevent negative damage - m_damage += uint32(bonus > 0 ? bonus : 0); - - // Hemorrhage - if (m_spellInfo->IsFitToFamily(SPELLFAMILY_ROGUE, UI64LIT(0x0000000002000000))) - { - if (m_caster->GetTypeId() == TYPEID_PLAYER) - ((Player*)m_caster)->AddComboPoints(unitTarget, 1); - } - // Mangle (Cat): CP - else if (m_spellInfo->IsFitToFamily(SPELLFAMILY_DRUID, UI64LIT(0x0000040000000000))) - { - if (m_caster->GetTypeId() == TYPEID_PLAYER) - ((Player*)m_caster)->AddComboPoints(unitTarget, 1); - } -} - -void Spell::EffectThreat(SpellEffectEntry const* /*effect*/) -{ - if (!unitTarget || !unitTarget->isAlive() || !m_caster->isAlive()) - return; - - if (!unitTarget->CanHaveThreatList()) - return; - - unitTarget->AddThreat(m_caster, float(damage), false, GetSpellSchoolMask(m_spellInfo), m_spellInfo); -} - -void Spell::EffectHealMaxHealth(SpellEffectEntry const* /*effect*/) -{ - if (!unitTarget) - return; - if (!unitTarget->isAlive()) - return; - - uint32 heal = m_caster->GetMaxHealth(); - - m_healing += heal; -} - -void Spell::EffectInterruptCast(SpellEffectEntry const* /*effect*/) -{ - if (!unitTarget) - return; - if (!unitTarget->isAlive()) - return; - - // TODO: not all spells that used this effect apply cooldown at school spells - // also exist case: apply cooldown to interrupted cast only and to all spells - for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; ++i) - { - if (Spell* spell = unitTarget->GetCurrentSpell(CurrentSpellTypes(i))) - { - SpellEntry const* curSpellInfo = spell->m_spellInfo; - // check if we can interrupt spell - if ((curSpellInfo->GetInterruptFlags() & SPELL_INTERRUPT_FLAG_INTERRUPT) && curSpellInfo->GetPreventionType() == SPELL_PREVENTION_TYPE_SILENCE ) - { - unitTarget->ProhibitSpellSchool(GetSpellSchoolMask(curSpellInfo), GetSpellDuration(m_spellInfo)); - unitTarget->InterruptSpell(CurrentSpellTypes(i), false); - } - } - } -} - -void Spell::EffectSummonObjectWild(SpellEffectEntry const* effect) -{ - uint32 gameobject_id = effect->EffectMiscValue; - - GameObject* pGameObj = new GameObject; - - WorldObject* target = focusObject; - if (!target) - target = m_caster; - - float x, y, z; - if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) - m_targets.getDestination(x, y, z); - else - m_caster->GetClosePoint(x, y, z, DEFAULT_WORLD_OBJECT_SIZE); - - Map* map = target->GetMap(); - - if (!pGameObj->Create(map->GenerateLocalLowGuid(HIGHGUID_GAMEOBJECT), gameobject_id, map, - m_caster->GetPhaseMask(), x, y, z, target->GetOrientation())) - { - delete pGameObj; - return; - } - - pGameObj->SetRespawnTime(m_duration > 0 ? m_duration / IN_MILLISECONDS : 0); - pGameObj->SetSpellId(m_spellInfo->Id); - - // Wild object not have owner and check clickable by players - map->Add(pGameObj); - - // Store the GO to the caster - m_caster->AddWildGameObject(pGameObj); - - if (pGameObj->GetGoType() == GAMEOBJECT_TYPE_FLAGDROP && m_caster->GetTypeId() == TYPEID_PLAYER) - { - Player* pl = (Player*)m_caster; - BattleGround* bg = ((Player*)m_caster)->GetBattleGround(); - - switch (pGameObj->GetMapId()) - { - case 489: // WS - { - if (bg && bg->GetTypeID() == BATTLEGROUND_WS && bg->GetStatus() == STATUS_IN_PROGRESS) - { - Team team = pl->GetTeam() == ALLIANCE ? HORDE : ALLIANCE; - - ((BattleGroundWS*)bg)->SetDroppedFlagGuid(pGameObj->GetObjectGuid(), team); - } - break; - } - case 566: // EY - { - if (bg && bg->GetTypeID() == BATTLEGROUND_EY && bg->GetStatus() == STATUS_IN_PROGRESS) - { - ((BattleGroundEY*)bg)->SetDroppedFlagGuid(pGameObj->GetObjectGuid()); - } - break; - } - } - } - - pGameObj->SummonLinkedTrapIfAny(); - - if (m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->AI()) - ((Creature*)m_caster)->AI()->JustSummoned(pGameObj); - if (m_originalCaster && m_originalCaster != m_caster && m_originalCaster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_originalCaster)->AI()) - ((Creature*)m_originalCaster)->AI()->JustSummoned(pGameObj); -} - -void Spell::EffectScriptEffect(SpellEffectEntry const* effect) -{ - // TODO: we must implement hunter pet summon at login there (spell 6962) - switch(m_spellInfo->GetSpellFamilyName()) - { - case SPELLFAMILY_GENERIC: - { - switch (m_spellInfo->Id) - { - case 8856: // Bending Shinbone - { - if (!itemTarget && m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - uint32 spell_id = 0; - switch (urand(1, 5)) - { - case 1: spell_id = 8854; break; - default: spell_id = 8855; break; - } - - m_caster->CastSpell(m_caster, spell_id, true, NULL); - return; - } - case 17512: // Piccolo of the Flaming Fire - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - unitTarget->HandleEmoteCommand(EMOTE_STATE_DANCE); - return; - } - case 20589: // Escape artist - { - if (!unitTarget) - return; - - unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOD_ROOT); - unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOD_DECREASE_SPEED); - return; - } - case 22539: // Shadow Flame (All script effects, not just end ones to - case 22972: // prevent player from dodging the last triggered spell) - case 22975: - case 22976: - case 22977: - case 22978: - case 22979: - case 22980: - case 22981: - case 22982: - case 22983: - case 22984: - case 22985: - { - if (!unitTarget || !unitTarget->isAlive()) - return; - - // Onyxia Scale Cloak - if (unitTarget->GetDummyAura(22683)) - return; - - // Shadow Flame - m_caster->CastSpell(unitTarget, 22682, true); - return; - } - case 24194: // Uther's Tribute - case 24195: // Grom's Tribute - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - uint8 race = m_caster->getRace(); - uint32 spellId = 0; - - switch (m_spellInfo->Id) - { - case 24194: - switch (race) - { - case RACE_HUMAN: spellId = 24105; break; - case RACE_DWARF: spellId = 24107; break; - case RACE_NIGHTELF: spellId = 24108; break; - case RACE_GNOME: spellId = 24106; break; - case RACE_DRAENEI: spellId = 69533; break; - } - break; - case 24195: - switch (race) - { - case RACE_ORC: spellId = 24104; break; - case RACE_UNDEAD: spellId = 24103; break; - case RACE_TAUREN: spellId = 24102; break; - case RACE_TROLL: spellId = 24101; break; - case RACE_BLOODELF: spellId = 69530; break; - } - break; - } - - if (spellId) - m_caster->CastSpell(m_caster, spellId, true); - - return; - } - case 24320: // Poisonous Blood - { - unitTarget->CastSpell(unitTarget, 24321, true, NULL, NULL, m_caster->GetObjectGuid()); - return; - } - case 24324: // Blood Siphon - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - unitTarget->CastSpell(m_caster, unitTarget->HasAura(24321) ? 24323 : 24322, true); - return; - } - case 24590: // Brittle Armor - need remove one 24575 Brittle Armor aura - unitTarget->RemoveAuraHolderFromStack(24575); - return; - case 24714: // Trick - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - if (roll_chance_i(14)) // Trick (can be different critter models). 14% since below can have 1 of 6 - m_caster->CastSpell(m_caster, 24753, true); - else // Random Costume, 6 different (plus add. for gender) - m_caster->CastSpell(m_caster, 24720, true); - - return; - } - case 24717: // Pirate Costume - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - // Pirate Costume (male or female) - m_caster->CastSpell(unitTarget, unitTarget->getGender() == GENDER_MALE ? 24708 : 24709, true); - return; - } - case 24718: // Ninja Costume - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - // Ninja Costume (male or female) - m_caster->CastSpell(unitTarget, unitTarget->getGender() == GENDER_MALE ? 24711 : 24710, true); - return; - } - case 24719: // Leper Gnome Costume - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - // Leper Gnome Costume (male or female) - m_caster->CastSpell(unitTarget, unitTarget->getGender() == GENDER_MALE ? 24712 : 24713, true); - return; - } - case 24720: // Random Costume - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - uint32 spellId = 0; - - switch (urand(0, 6)) - { - case 0: - spellId = unitTarget->getGender() == GENDER_MALE ? 24708 : 24709; - break; - case 1: - spellId = unitTarget->getGender() == GENDER_MALE ? 24711 : 24710; - break; - case 2: - spellId = unitTarget->getGender() == GENDER_MALE ? 24712 : 24713; - break; - case 3: - spellId = 24723; - break; - case 4: - spellId = 24732; - break; - case 5: - spellId = unitTarget->getGender() == GENDER_MALE ? 24735 : 24736; - break; - case 6: - spellId = 24740; - break; - } - - m_caster->CastSpell(unitTarget, spellId, true); - return; - } - case 24737: // Ghost Costume - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - // Ghost Costume (male or female) - m_caster->CastSpell(unitTarget, unitTarget->getGender() == GENDER_MALE ? 24735 : 24736, true); - return; - } - case 24751: // Trick or Treat - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - // Tricked or Treated - unitTarget->CastSpell(unitTarget, 24755, true); - - // Treat / Trick - unitTarget->CastSpell(unitTarget, roll_chance_i(50) ? 24714 : 24715, true); - return; - } - case 25140: // Orb teleport spells - case 25143: - case 25650: - case 25652: - case 29128: - case 29129: - case 35376: - case 35727: - { - if (!unitTarget) - return; - - uint32 spellid; - switch (m_spellInfo->Id) - { - case 25140: spellid = 32568; break; - case 25143: spellid = 32572; break; - case 25650: spellid = 30140; break; - case 25652: spellid = 30141; break; - case 29128: spellid = 32571; break; - case 29129: spellid = 32569; break; - case 35376: spellid = 25649; break; - case 35727: spellid = 35730; break; - default: - return; - } - - unitTarget->CastSpell(unitTarget, spellid, false); - return; - } - case 26004: // Mistletoe - { - if (!unitTarget) - return; - - unitTarget->HandleEmote(EMOTE_ONESHOT_CHEER); - return; - } - case 26137: // Rotate Trigger - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, urand(0, 1) ? 26009 : 26136, true); - return; - } - case 26218: // Mistletoe - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - uint32 spells[3] = {26206, 26207, 45036}; - - m_caster->CastSpell(unitTarget, spells[urand(0, 2)], true); - return; - } - case 26275: // PX-238 Winter Wondervolt TRAP - { - uint32 spells[4] = {26272, 26157, 26273, 26274}; - - // check presence - for (int j = 0; j < 4; ++j) - if (unitTarget->HasAura(spells[j], EFFECT_INDEX_0)) - return; - - // cast - unitTarget->CastSpell(unitTarget, spells[urand(0, 3)], true); - return; - } - case 26465: // Mercurial Shield - need remove one 26464 Mercurial Shield aura - unitTarget->RemoveAuraHolderFromStack(26464); - return; - case 26656: // Summon Black Qiraji Battle Tank - { - if (!unitTarget) - return; - - // Prevent stacking of mounts - unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); - - // Two separate mounts depending on area id (allows use both in and out of specific instance) - if (unitTarget->GetAreaId() == 3428) - unitTarget->CastSpell(unitTarget, 25863, false); - else - unitTarget->CastSpell(unitTarget, 26655, false); - - return; - } - case 27687: // Summon Bone Minions - { - if (!unitTarget) - return; - - // Spells 27690, 27691, 27692, 27693 are missing from DBC - // So we need to summon creature 16119 manually - float x, y, z; - float angle = unitTarget->GetOrientation(); - for (uint8 i = 0; i < 4; ++i) - { - unitTarget->GetNearPoint(unitTarget, x, y, z, unitTarget->GetObjectBoundingRadius(), INTERACTION_DISTANCE, angle + i * M_PI_F / 2); - unitTarget->SummonCreature(16119, x, y, z, angle, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 10 * MINUTE * IN_MILLISECONDS); - } - return; - } - case 27695: // Summon Bone Mages - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, 27696, true); - unitTarget->CastSpell(unitTarget, 27697, true); - unitTarget->CastSpell(unitTarget, 27698, true); - unitTarget->CastSpell(unitTarget, 27699, true); - return; - } - case 28374: // Decimate (Naxxramas: Gluth) - case 54426: // Decimate (Naxxramas: Gluth (spells are identical)) - case 71123: // Decimate (ICC: Precious / Stinky) - { - if (!unitTarget) - return; - - float downToHealthPercent = (m_spellInfo->Id != 71123 ? 5 : effect->CalculateSimpleValue()) * 0.01f; - - int32 damage = unitTarget->GetHealth() - unitTarget->GetMaxHealth() * downToHealthPercent; - if (damage > 0) - m_caster->CastCustomSpell(unitTarget, 28375, &damage, NULL, NULL, true); - return; - } - case 28560: // Summon Blizzard - { - if (!unitTarget) - return; - - m_caster->SummonCreature(16474, unitTarget->GetPositionX(), unitTarget->GetPositionY(), unitTarget->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_DESPAWN, 30000); - return; - } - case 28859: // Cleansing Flames - case 29126: // Cleansing Flames (Darnassus) - case 29135: // Cleansing Flames (Ironforge) - case 29136: // Cleansing Flames (Orgrimmar) - case 29137: // Cleansing Flames (Stormwind) - case 29138: // Cleansing Flames (Thunder Bluff) - case 29139: // Cleansing Flames (Undercity) - case 46671: // Cleansing Flames (Exodar) - case 46672: // Cleansing Flames (Silvermoon) - { - // Cleanse all magic, curse, disease and poison - if (!unitTarget) - return; - - unitTarget->RemoveAurasWithDispelType(DISPEL_MAGIC); - unitTarget->RemoveAurasWithDispelType(DISPEL_CURSE); - unitTarget->RemoveAurasWithDispelType(DISPEL_DISEASE); - unitTarget->RemoveAurasWithDispelType(DISPEL_POISON); - - return; - } - case 29395: // Break Kaliri Egg - { - uint32 creature_id = 0; - uint32 rand = urand(0, 99); - - if (rand < 10) - creature_id = 17034; - else if (rand < 60) - creature_id = 17035; - else - creature_id = 17039; - - if (WorldObject* pSource = GetAffectiveCasterObject()) - pSource->SummonCreature(creature_id, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 120 * IN_MILLISECONDS); - return; - } - case 29830: // Mirren's Drinking Hat - { - uint32 item = 0; - switch (urand(1, 6)) - { - case 1: - case 2: - case 3: - item = 23584; break; // Loch Modan Lager - case 4: - case 5: - item = 23585; break; // Stouthammer Lite - case 6: - item = 23586; break; // Aerie Peak Pale Ale - } - - if (item) - DoCreateItem(effect,item); - - break; - } - case 30541: // Blaze - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, 30542, true); - break; - } - case 30769: // Pick Red Riding Hood - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - // cast Little Red Riding Hood - m_caster->CastSpell(unitTarget, 30768, true); - break; - } - case 30835: // Infernal Relay - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, 30836, true, NULL, NULL, m_caster->GetObjectGuid()); - break; - } - case 30918: // Improved Sprint - { - if (!unitTarget) - return; - - // Removes snares and roots. - unitTarget->RemoveAurasAtMechanicImmunity(IMMUNE_TO_ROOT_AND_SNARE_MASK, 30918, true); - break; - } - case 37142: // Karazhan - Chess NPC Action: Melee Attack: Conjured Water Elemental - case 37143: // Karazhan - Chess NPC Action: Melee Attack: Charger - case 37147: // Karazhan - Chess NPC Action: Melee Attack: Human Cleric - case 37149: // Karazhan - Chess NPC Action: Melee Attack: Human Conjurer - case 37150: // Karazhan - Chess NPC Action: Melee Attack: King Llane - case 37220: // Karazhan - Chess NPC Action: Melee Attack: Summoned Daemon - case 32227: // Karazhan - Chess NPC Action: Melee Attack: Footman - case 32228: // Karazhan - Chess NPC Action: Melee Attack: Grunt - case 37337: // Karazhan - Chess NPC Action: Melee Attack: Orc Necrolyte - case 37339: // Karazhan - Chess NPC Action: Melee Attack: Orc Wolf - case 37345: // Karazhan - Chess NPC Action: Melee Attack: Orc Warlock - case 37348: // Karazhan - Chess NPC Action: Melee Attack: Warchief Blackhand - { - if (!unitTarget) - return; - - m_caster->CastSpell(unitTarget, 32247, true); - return; - } - case 32301: // Ping Shirrak - { - if (!unitTarget) - return; - - // Cast Focus fire on caster - unitTarget->CastSpell(m_caster, 32300, true); - return; - } - case 33676: // Incite Chaos - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - m_caster->CastSpell(unitTarget, 33684, true); - return; - } - case 35865: // Summon Nether Vapor - { - if (!unitTarget) - return; - - float x, y, z; - for (uint8 i = 0; i < 4; ++i) - { - m_caster->GetNearPoint(m_caster, x, y, z, 0.0f, INTERACTION_DISTANCE, M_PI_F * .5f * i + M_PI_F * .25f); - m_caster->SummonCreature(21002, x, y, z, 0, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 30000); - } - return; - } - case 37431: // Spout - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, urand(0, 1) ? 37429 : 37430, true); - return; - } - case 37775: // Karazhan - Chess NPC Action - Poison Cloud - { - if (!unitTarget) - return; - - m_caster->CastSpell(unitTarget, 37469, true); - return; - } - case 37824: // Karazhan - Chess NPC Action - Shadow Mend - { - if (!unitTarget) - return; - - m_caster->CastSpell(unitTarget, 37456, true); - return; - } - case 38358: // Tidal Surge - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, 38353, true, NULL, NULL, m_caster->GetObjectGuid()); - return; - } - case 39338: // Karazhan - Chess, Medivh CHEAT: Hand of Medivh, Target Horde - case 39342: // Karazhan - Chess, Medivh CHEAT: Hand of Medivh, Target Alliance - { - if (!unitTarget) - return; - - m_caster->CastSpell(unitTarget, 39339, true); - return; - } - case 39341: // Karazhan - Chess, Medivh CHEAT: Fury of Medivh, Target Horde - case 39344: // Karazhan - Chess, Medivh CHEAT: Fury of Medivh, Target Alliance - { - if (!unitTarget) - return; - - m_caster->CastSpell(unitTarget, effect->CalculateSimpleValue(), true); - return; - } - case 41055: // Copy Weapon - { - if (m_caster->GetTypeId() != TYPEID_UNIT || !unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - if (Item* pItem = ((Player*)unitTarget)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND)) - { - ((Creature*)m_caster)->SetVirtualItem(VIRTUAL_ITEM_SLOT_0, pItem->GetEntry()); - - // Unclear what this spell should do - unitTarget->CastSpell(m_caster, effect->CalculateSimpleValue(), true); - } - - return; - } - case 41126: // Flame Crash - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, 41131, true); - break; - } - case 43365: // The Cleansing: Shrine Cast - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - // Script Effect Player Cast Mirror Image - m_caster->CastSpell(m_caster, 50217, true); - return; - } - case 43375: // Mixing Vrykul Blood - { - if (!unitTarget) - return; - - uint32 triggeredSpell[] = {43376, 43378, 43970, 43377}; - - unitTarget->CastSpell(unitTarget, triggeredSpell[urand(0, 3)], true); - return; - } - case 44364: // Rock Falcon Primer - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - // Are there anything special with this, a random chance or condition? - // Feeding Rock Falcon - unitTarget->CastSpell(unitTarget, effect->CalculateSimpleValue(), true, NULL, NULL, unitTarget->GetObjectGuid(), m_spellInfo); - return; - } - case 44455: // Character Script Effect Reverse Cast - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - Creature* pTarget = (Creature*)unitTarget; - - if (const SpellEntry *pSpell = sSpellStore.LookupEntry(effect->CalculateSimpleValue())) - { - // if we used item at least once... - if (pTarget->IsTemporarySummon() && int32(pTarget->GetEntry()) == pSpell->GetEffectMiscValue(SpellEffectIndex(effect->EffectIndex))) - { - TemporarySummon* pSummon = (TemporarySummon*)pTarget; - - // can only affect "own" summoned - if (pSummon->GetSummonerGuid() == m_caster->GetObjectGuid()) - { - if (pTarget->hasUnitState(UNIT_STAT_ROAMING | UNIT_STAT_ROAMING_MOVE)) - pTarget->GetMotionMaster()->MovementExpired(); - - // trigger cast of quest complete script (see code for this spell below) - pTarget->CastSpell(pTarget, 44462, true); - - pTarget->GetMotionMaster()->MovePoint(0, m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ()); - } - - return; - } - - // or if it is first time used item, cast summon and despawn the target - m_caster->CastSpell(pTarget, pSpell, true); - pTarget->ForcedDespawn(); - - // TODO: here we should get pointer to the just summoned and make it move. - // without, it will be one extra use of quest item - } - - return; - } - case 44462: // Cast Quest Complete on Master - { - if (m_caster->GetTypeId() != TYPEID_UNIT) - return; - - Creature* pQuestCow = NULL; - - float range = 20.0f; - - // search for a reef cow nearby - MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck u_check(*m_caster, 24797, true, false, range); - MaNGOS::CreatureLastSearcher searcher(pQuestCow, u_check); - - Cell::VisitGridObjects(m_caster, searcher, range); - - // no cows found, so return - if (!pQuestCow) - return; - - if (!((Creature*)m_caster)->IsTemporarySummon()) - return; - - if (const SpellEntry *pSpell = sSpellStore.LookupEntry(effect->CalculateSimpleValue())) - { - TemporarySummon* pSummon = (TemporarySummon*)m_caster; - - // all ok, so make summoner cast the quest complete - if (Unit* pSummoner = pSummon->GetSummoner()) - pSummoner->CastSpell(pSummoner, pSpell, true); - } - - return; - } - case 44876: // Force Cast - Portal Effect: Sunwell Isle - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, 44870, true); - break; - } - case 44811: // Spectral Realm - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - // If the player can't be teleported, send him a notification - if (unitTarget->HasAura(44867)) - { - ((Player*)unitTarget)->GetSession()->SendNotification(LANG_FAIL_ENTER_SPECTRAL_REALM); - return; - } - - // Teleport target to the spectral realm, add debuff and force faction - unitTarget->CastSpell(unitTarget, 46019, true); - unitTarget->CastSpell(unitTarget, 46021, true); - unitTarget->CastSpell(unitTarget, 44845, true); - unitTarget->CastSpell(unitTarget, 44852, true); - return; - } - case 45141: // Burn - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, 46394, true, NULL, NULL, m_caster->GetObjectGuid()); - return; - } - case 45151: // Burn - { - if (!unitTarget || unitTarget->HasAura(46394)) - return; - - // Make the burn effect jump to another friendly target - unitTarget->CastSpell(unitTarget, 46394, true); - return; - } - case 45185: // Stomp - { - if (!unitTarget) - return; - - // Remove the burn effect - unitTarget->RemoveAurasDueToSpell(46394); - return; - } - case 45204: // Clone Me! - { - if (!unitTarget) - return; - - unitTarget->CastSpell(m_caster, effect->CalculateSimpleValue(), true); - return; - } - case 45206: // Copy Off-hand Weapon - { - if (m_caster->GetTypeId() != TYPEID_UNIT || !unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - if (Item* pItem = ((Player*)unitTarget)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND)) - { - ((Creature*)m_caster)->SetVirtualItem(VIRTUAL_ITEM_SLOT_1, pItem->GetEntry()); - - // Unclear what this spell should do - unitTarget->CastSpell(m_caster, effect->CalculateSimpleValue(), true); - } - - return; - } - case 45235: // Blaze - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, 45236, true, NULL, NULL, m_caster->GetObjectGuid()); - return; - } - case 45260: // Karazhan - Chess - Force Player to Kill Bunny - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - unitTarget->CastSpell(unitTarget, 45259, true); - return; - } - case 45668: // Ultra-Advanced Proto-Typical Shortening Blaster - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - if (roll_chance_i(25)) // chance unknown, using 25 - return; - - static uint32 const spellPlayer[5] = - { - 45674, // Bigger! - 45675, // Shrunk - 45678, // Yellow - 45682, // Ghost - 45684 // Polymorph - }; - - static uint32 const spellTarget[5] = - { - 45673, // Bigger! - 45672, // Shrunk - 45677, // Yellow - 45681, // Ghost - 45683 // Polymorph - }; - - m_caster->CastSpell(m_caster, spellPlayer[urand(0, 4)], true); - unitTarget->CastSpell(unitTarget, spellTarget[urand(0, 4)], true); - - return; - } - case 45691: // Magnataur On Death 1 - { - // assuming caster is creature, if not, then return - if (m_caster->GetTypeId() != TYPEID_UNIT) - return; - - Player* pPlayer = ((Creature*)m_caster)->GetOriginalLootRecipient(); - - if (!pPlayer) - return; - - if (pPlayer->HasAura(45674) || pPlayer->HasAura(45675) || pPlayer->HasAura(45678) || pPlayer->HasAura(45682) || pPlayer->HasAura(45684)) - pPlayer->CastSpell(pPlayer, 45686, true); - - m_caster->CastSpell(m_caster, 45685, true); - - return; - } - case 45713: // Naked Caravan Guard - Master Transform - { - if (m_caster->GetTypeId() != TYPEID_UNIT) - return; - - const CreatureInfo* cTemplate = NULL; - - switch (m_caster->GetEntry()) - { - case 25342: cTemplate = ObjectMgr::GetCreatureTemplate(25340); break; - case 25343: cTemplate = ObjectMgr::GetCreatureTemplate(25341); break; - } - - if (!cTemplate) - return; - - uint32 display_id = 0; - - // Spell is designed to be used in creature addon. - // This makes it possible to set proper model before adding to map. - // For later, spell is used in gossip (with following despawn, - // so addon can reload the default model and data again). - - // It should be noted that additional spell id's have been seen in relation to this spell, but - // those does not exist in client (45701 (regular spell), 45705-45712 (auras), 45459-45460 (auras)). - // We can assume 45705-45712 are transform auras, used instead of hard coded models in the below code. - - // not in map yet OR no npc flags yet (restored after LoadCreatureAddon for respawn cases) - if (!m_caster->IsInMap(m_caster) || m_caster->GetUInt32Value(UNIT_NPC_FLAGS) == UNIT_NPC_FLAG_NONE) - { - display_id = Creature::ChooseDisplayId(cTemplate); - ((Creature*)m_caster)->LoadEquipment(((Creature*)m_caster)->GetEquipmentId()); - } - else - { - m_caster->SetUInt32Value(UNIT_NPC_FLAGS, cTemplate->npcflag); - ((Creature*)m_caster)->SetVirtualItem(VIRTUAL_ITEM_SLOT_0, 0); - ((Creature*)m_caster)->SetVirtualItem(VIRTUAL_ITEM_SLOT_1, 0); - - switch (m_caster->GetDisplayId()) - { - case 23246: display_id = 23245; break; - case 23247: display_id = 23250; break; - case 23248: display_id = 23251; break; - case 23249: display_id = 23252; break; - case 23124: display_id = 23253; break; - case 23125: display_id = 23254; break; - case 23126: display_id = 23255; break; - case 23127: display_id = 23256; break; - } - } - - m_caster->SetDisplayId(display_id); - return; - } - case 45714: // Fog of Corruption (caster inform) - { - if (!unitTarget || m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - unitTarget->CastSpell(m_caster, effect->CalculateSimpleValue(), true); - return; - } - case 45717: // Fog of Corruption (player buff) - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - unitTarget->CastSpell(unitTarget, 45726, true); - return; - } - case 45785: // Sinister Reflection Clone - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, effect->CalculateSimpleValue(), true); - return; - } - case 45833: // Power of the Blue Flight - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, 45836, true); - return; - } - case 45892: // Sinister Reflection - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - // Summon 4 clones of the same player - for (uint8 i = 0; i < 4; ++i) - unitTarget->CastSpell(unitTarget, 45891, true, NULL, NULL, m_caster->GetObjectGuid()); - return; - } - case 45918: // Soul Sever - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER || !unitTarget->HasAura(45717)) - return; - - // kill all charmed targets - unitTarget->CastSpell(unitTarget, 45917, true); - return; - } - case 45958: // Signal Alliance - { - // "escort" aura not present, so let nothing happen - if (!m_caster->HasAura(effect->CalculateSimpleValue())) - return; - // "escort" aura is present so break; and let DB table dbscripts_on_spell be used and process further. - else - break; - } - case 46203: // Goblin Weather Machine - { - if (!unitTarget) - return; - - uint32 spellId = 0; - switch (rand() % 4) - { - case 0: spellId = 46740; break; - case 1: spellId = 46739; break; - case 2: spellId = 46738; break; - case 3: spellId = 46736; break; - } - unitTarget->CastSpell(unitTarget, spellId, true); - break; - } - case 46642: // 5,000 Gold - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - ((Player*)unitTarget)->ModifyMoney(5000 * GOLD); - break; - } - case 47097: // Surge Needle Teleporter - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - if (unitTarget->GetAreaId() == 4157) - unitTarget->CastSpell(unitTarget, 47324, true); - else if (unitTarget->GetAreaId() == 4156) - unitTarget->CastSpell(unitTarget, 47325, true); - - break; - } - case 47311: // Quest - Jormungar Explosion Spell Spawner - { - // Summons npc's. They are expected to summon GO from 47315 - // but there is no way to get the summoned, to trigger a spell - // cast (workaround can be done with ai script). - - // Quest - Jormungar Explosion Summon Object - for (int i = 0; i < 2; ++i) - m_caster->CastSpell(m_caster, 47309, true); - - for (int i = 0; i < 2; ++i) - m_caster->CastSpell(m_caster, 47924, true); - - for (int i = 0; i < 2; ++i) - m_caster->CastSpell(m_caster, 47925, true); - - return; - } - case 47393: // The Focus on the Beach: Quest Completion Script - { - if (!unitTarget) - return; - - // Ley Line Information - unitTarget->RemoveAurasDueToSpell(47391); - return; - } - case 47615: // Atop the Woodlands: Quest Completion Script - { - if (!unitTarget) - return; - - // Ley Line Information - unitTarget->RemoveAurasDueToSpell(47473); - return; - } - case 47638: // The End of the Line: Quest Completion Script - { - if (!unitTarget) - return; - - // Ley Line Information - unitTarget->RemoveAurasDueToSpell(47636); - return; - } - case 47703: // Unholy Union - { - m_caster->CastSpell(m_caster, 50254, true); - return; - } - case 47724: // Frost Draw - { - m_caster->CastSpell(m_caster, 50239, true); - return; - } - case 47958: // Crystal Spikes - case 57083: // Crystal Spikes (h2) - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, 47954, true); - unitTarget->CastSpell(unitTarget, 47955, true); - unitTarget->CastSpell(unitTarget, 47956, true); - unitTarget->CastSpell(unitTarget, 47957, true); - return; - } - case 48590: // Avenging Spirits - { - if (!unitTarget) - return; - - // Summon 4 spirits summoners - unitTarget->CastSpell(unitTarget, 48586, true); - unitTarget->CastSpell(unitTarget, 48587, true); - unitTarget->CastSpell(unitTarget, 48588, true); - unitTarget->CastSpell(unitTarget, 48589, true); - return; - } - case 48603: // High Executor's Branding Iron - // Torture the Torturer: High Executor's Branding Iron Impact - unitTarget->CastSpell(unitTarget, 48614, true); - return; - case 48724: // The Denouncement: Commander Jordan On Death - case 48726: // The Denouncement: Lead Cannoneer Zierhut On Death - case 48728: // The Denouncement: Blacksmith Goodman On Death - case 48730: // The Denouncement: Stable Master Mercer On Death - { - // Compelled - if (!unitTarget || !m_caster->HasAura(48714)) - return; - - unitTarget->CastSpell(unitTarget, effect->CalculateSimpleValue(), true); - return; - } - // Gender spells - case 48762: // A Fall from Grace: Scarlet Raven Priest Image - Master - case 45759: // Warsong Orc Disguise - case 69672: // Sunreaver Disguise - case 69673: // Silver Covenant Disguise - { - if (!unitTarget) - return; - - uint8 gender = unitTarget->getGender(); - uint32 spellId; - switch (m_spellInfo->Id) - { - case 48762: spellId = (gender == GENDER_MALE ? 48763 : 48761); break; - case 45759: spellId = (gender == GENDER_MALE ? 45760 : 45762); break; - case 69672: spellId = (gender == GENDER_MALE ? 70974 : 70973); break; - case 69673: spellId = (gender == GENDER_MALE ? 70972 : 70971); break; - default: return; - } - unitTarget->CastSpell(unitTarget, spellId, true); - return; - } - case 48810: // Death's Door - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - // Spell effect order will summon creature first and then apply invisibility to caster. - // This result in that summoner/summoned can not see each other and that is not expected. - // Aura from 48814 can be used as a hack from creature_addon, but we can not get the - // summoned to cast this from this spell effect since we have no way to get pointer to creature. - // Most proper would be to summon to same visibility mask as summoner, and not use spell at all. - - // Binding Life - m_caster->CastSpell(m_caster, 48809, true); - - // After (after: meaning creature does not have auras at creation) - // creature is summoned and visible for player in map, it is expected to - // gain two auras. First from 29266(aura slot0) and then from 48808(aura slot1). - // We have no pointer to summoned, so only 48808 is possible from this spell effect. - - // Binding Death - m_caster->CastSpell(m_caster, 48808, true); - return; - } - case 48811: // Despawn Forgotten Soul - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - if (!((Creature*)unitTarget)->IsTemporarySummon()) - return; - - TemporarySummon* pSummon = (TemporarySummon*)unitTarget; - - Unit::AuraList const& images = unitTarget->GetAurasByType(SPELL_AURA_MIRROR_IMAGE); - - if (images.empty()) - return; - - Unit* pCaster = images.front()->GetCaster(); - Unit* pSummoner = unitTarget->GetMap()->GetUnit(pSummon->GetSummonerGuid()); - - if (pSummoner && pSummoner == pCaster) - pSummon->UnSummon(); - - return; - } - case 48917: // Who Are They: Cast from Questgiver - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - // Male Shadowy Disguise / Female Shadowy Disguise - unitTarget->CastSpell(unitTarget, unitTarget->getGender() == GENDER_MALE ? 38080 : 38081, true); - // Shadowy Disguise - unitTarget->CastSpell(unitTarget, 32756, true); - return; - } - case 49380: // Consume - case 59803: // Consume (heroic) - { - if (!unitTarget) - return; - - // Each target hit buffs the caster - unitTarget->CastSpell(m_caster, m_spellInfo->Id == 49380 ? 49381 : 59805, true, NULL, NULL, m_caster->GetObjectGuid()); - return; - } - case 49405: // Invader Taunt Trigger - { - if (!unitTarget) - return; - - unitTarget->CastSpell(m_caster, effect->CalculateSimpleValue(), true); - return; - } - case 50217: // The Cleansing: Script Effect Player Cast Mirror Image - { - // Summon Your Inner Turmoil - m_caster->CastSpell(m_caster, 50167, true); - - // Spell 50218 has TARGET_SCRIPT, but other wild summons near may exist, and then target can become wrong - // Only way to make this safe is to get the actual summoned by m_caster - - // Your Inner Turmoil's Mirror Image Aura - m_caster->CastSpell(m_caster, 50218, true); - - return; - } - case 50218: // The Cleansing: Your Inner Turmoil's Mirror Image Aura - { - if (!m_originalCaster || m_originalCaster->GetTypeId() != TYPEID_PLAYER || !unitTarget) - return; - - // determine if and what weapons can be copied - switch(effect->EffectIndex) - { - case EFFECT_INDEX_1: - if (((Player*)m_originalCaster)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND)) - unitTarget->CastSpell(m_originalCaster, effect->CalculateSimpleValue(), true); - - return; - case EFFECT_INDEX_2: - if (((Player*)m_originalCaster)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND)) - unitTarget->CastSpell(m_originalCaster, effect->CalculateSimpleValue(), true); - - return; - default: - return; - } - return; - } - case 50238: // The Cleansing: Your Inner Turmoil's On Death Cast on Master - { - if (m_caster->GetTypeId() != TYPEID_UNIT) - return; - - if (((Creature*)m_caster)->IsTemporarySummon()) - { - TemporarySummon* pSummon = (TemporarySummon*)m_caster; - - if (pSummon->GetSummonerGuid().IsPlayer()) - { - if (Player* pSummoner = sObjectMgr.GetPlayer(pSummon->GetSummonerGuid())) - pSummoner->CastSpell(pSummoner, effect->CalculateSimpleValue(), true); - } - } - - return; - } - case 50252: // Blood Draw - { - m_caster->CastSpell(m_caster, 50250, true); - return; - } - case 50255: // Poisoned Spear - case 59331: // Poisoned Spear (heroic) - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, effect->CalculateSimpleValue(), true, NULL, NULL, m_originalCasterGUID); - return; - } - case 50439: // Script Cast Summon Image of Drakuru 05 - { - // TODO: check if summon already exist, if it does in this instance, return. - - // Summon Drakuru - m_caster->CastSpell(m_caster, 50446, true); - return; - } - case 50630: // Eject All Passengers - { - m_caster->RemoveSpellsCausingAura(SPELL_AURA_CONTROL_VEHICLE); - return; - } - case 50725: // Vigilance - remove cooldown on Taunt - { - Unit* caster = GetAffectiveCaster(); - if (!caster || caster->GetTypeId() != TYPEID_PLAYER) - return; - - ((Player*)caster)->RemoveSpellCategoryCooldown(82, true); - return; - } - case 50742: // Ooze Combine - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - m_caster->CastSpell(unitTarget, 50747, true); - ((Creature*)m_caster)->ForcedDespawn(); - return; - } - case 50810: // Shatter - case 61546: // Shatter (h) - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - if (!unitTarget->HasAura(50812)) - return; - - unitTarget->RemoveAurasDueToSpell(50812); - unitTarget->CastSpell(unitTarget, m_spellInfo->Id == 50810 ? 50811 : 61547 , true, NULL, NULL, m_caster->GetObjectGuid()); - return; - } - case 50894: // Zul'Drak Rat - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - if (SpellAuraHolder* pHolder = unitTarget->GetSpellAuraHolder(m_spellInfo->Id)) - { - if (pHolder->GetStackAmount() + 1 >= m_spellInfo->GetStackAmount()) - { - // Gluttonous Lurkers: Summon Gorged Lurking Basilisk - unitTarget->CastSpell(m_caster, 50928, true); - ((Creature*)unitTarget)->ForcedDespawn(1); - } - } - - return; - } - case 51519: // Death Knight Initiate Visual - { - if (!unitTarget) - return; - - uint32 spellId = 0; - - bool isMale = unitTarget->getGender() == GENDER_MALE; - switch (unitTarget->getRace()) - { - case RACE_HUMAN: spellId = isMale ? 51520 : 51534; break; - case RACE_DWARF: spellId = isMale ? 51538 : 51537; break; - case RACE_NIGHTELF: spellId = isMale ? 51535 : 51536; break; - case RACE_GNOME: spellId = isMale ? 51539 : 51540; break; - case RACE_DRAENEI: spellId = isMale ? 51541 : 51542; break; - case RACE_ORC: spellId = isMale ? 51543 : 51544; break; - case RACE_UNDEAD: spellId = isMale ? 51549 : 51550; break; - case RACE_TAUREN: spellId = isMale ? 51547 : 51548; break; - case RACE_TROLL: spellId = isMale ? 51546 : 51545; break; - case RACE_BLOODELF: spellId = isMale ? 51551 : 51552; break; - default: - return; - } - - unitTarget->CastSpell(unitTarget, spellId, true); - return; - } - case 51770: // Emblazon Runeblade - { - Unit* caster = GetAffectiveCaster(); - if (!caster) - return; - - caster->CastSpell(caster, damage, false); - break; - } - case 51864: // Player Summon Nass - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - // Summon Nass - if (const SpellEntry* pSpell = sSpellStore.LookupEntry(51865)) - { - // Only if he is not already there - if (!m_caster->FindGuardianWithEntry(pSpell->GetEffectMiscValue(EFFECT_INDEX_0))) - { - m_caster->CastSpell(m_caster, pSpell, true); - - if (Pet* pPet = m_caster->FindGuardianWithEntry(pSpell->GetEffectMiscValue(EFFECT_INDEX_0))) - { - // Nass Periodic Say aura - pPet->CastSpell(pPet, 51868, true); - } - } - } - return; - } - case 51889: // Quest Accept Summon Nass - { - // This is clearly for quest accept, is spell 51864 then for gossip and does pretty much the same thing? - // Just "jumping" to what may be the "gossip-spell" for now, doing the same thing - m_caster->CastSpell(m_caster, 51864, true); - return; - } - case 51904: // Summon Ghouls On Scarlet Crusade - { - if (!unitTarget) - return; - - // cast Summon Ghouls On Scarlet Crusade - float x, y, z; - m_targets.getDestination(x, y, z); - unitTarget->CastSpell(x, y, z, 54522, true, NULL, NULL, m_originalCasterGUID); - return; - } - case 51910: // Kickin' Nass: Quest Completion - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - if (const SpellEntry* pSpell = sSpellStore.LookupEntry(51865)) - { - // Is this all to be done at completion? - if (Pet* pPet = m_caster->FindGuardianWithEntry(pSpell->GetEffectMiscValue(EFFECT_INDEX_0))) - pPet->Unsummon(PET_SAVE_AS_DELETED, m_caster); - } - return; - } - case 52479: // Gift of the Harvester - { - if (m_caster->GetTypeId() != TYPEID_PLAYER || !unitTarget) - return; - - // Each ghoul casts 52500 onto player, so use number of auras as check - Unit::SpellAuraHolderConstBounds bounds = m_caster->GetSpellAuraHolderBounds(52500); - uint32 summonedGhouls = std::distance(bounds.first, bounds.second); - - m_caster->CastSpell(unitTarget->GetPositionX(), unitTarget->GetPositionY(), unitTarget->GetPositionZ(), urand(0, 2) || summonedGhouls >= 5 ? 52505 : 52490, true); - return; - } - case 52555: // Dispel Scarlet Ghoul Credit Counter - { - if (!unitTarget) - return; - - unitTarget->RemoveAurasByCasterSpell(effect->CalculateSimpleValue(), m_caster->GetObjectGuid()); - return; - } - case 52694: // Recall Eye of Acherus - { - if (!m_caster || m_caster->GetTypeId() != TYPEID_UNIT) - return; - - Unit* charmer = m_caster->GetCharmer(); - if (!charmer || charmer->GetTypeId() != TYPEID_PLAYER) - return; - - charmer->RemoveAurasDueToSpell(51923); - - // HACK ALERT - // Replace with Spell Interrupting, when casting spells properly is possible in mangos - //charmer->InterruptNonMeleeSpells(true); - - Player* player = (Player*)charmer; - Creature* possessed = (Creature*)m_caster; - player->RemoveAurasDueToSpell(51852); - - player->SetCharm(NULL); - player->SetClientControl(possessed, 0); - player->SetMover(NULL); - player->GetCamera().ResetView(); - player->RemovePetActionBar(); - - possessed->clearUnitState(UNIT_STAT_CONTROLLED); - possessed->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); - possessed->SetCharmerGuid(ObjectGuid()); - possessed->ForcedDespawn(); - - return; - } - case 52751: // Death Gate - { - if (!unitTarget || unitTarget->getClass() != CLASS_DEATH_KNIGHT) - return; - - // triggered spell is stored in m_spellInfo->EffectBasePoints[0] - unitTarget->CastSpell(unitTarget, damage, false); - break; - } - case 52941: // Song of Cleansing - { - uint32 spellId = 0; - - switch (m_caster->GetAreaId()) - { - case 4385: spellId = 52954; break; // Bittertide Lake - case 4290: spellId = 52958; break; // River's Heart - case 4388: spellId = 52959; break; // Wintergrasp River - } - - if (spellId) - m_caster->CastSpell(m_caster, spellId, true); - - break; - } - case 54182: // An End to the Suffering: Quest Completion Script - { - if (!unitTarget) - return; - - // Remove aura (Mojo of Rhunok) given at quest accept / gossip - unitTarget->RemoveAurasDueToSpell(51967); - return; - } - case 54581: // Mammoth Explosion Spell Spawner - { - if (m_caster->GetTypeId() != TYPEID_UNIT) - return; - - // Summons misc npc's. They are expected to summon GO from 54625 - // but there is no way to get the summoned, to trigger a spell - // cast (workaround can be done with ai script). - - // Quest - Mammoth Explosion Summon Object - for (int i = 0; i < 2; ++i) - m_caster->CastSpell(m_caster, 54623, true); - - for (int i = 0; i < 2; ++i) - m_caster->CastSpell(m_caster, 54627, true); - - for (int i = 0; i < 2; ++i) - m_caster->CastSpell(m_caster, 54628, true); - - // Summon Main Mammoth Meat - m_caster->CastSpell(m_caster, 57444, true); - return; - } - case 54436: // Demonic Empowerment (succubus Vanish effect) - { - if (!unitTarget) - return; - - unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOD_ROOT); - unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOD_DECREASE_SPEED); - unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOD_STALKED); - unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOD_STUN); - return; - } - case 55693: // Remove Collapsing Cave Aura - { - if (!unitTarget) - return; - - unitTarget->RemoveAurasDueToSpell(effect->CalculateSimpleValue()); - break; - } - case 56072: // Ride Red Dragon Buddy - { - if (!unitTarget) - return; - - m_caster->CastSpell(unitTarget, effect->CalculateSimpleValue(), true); - break; - } - case 57082: // Crystal Spikes (h1) - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, 57077, true); - unitTarget->CastSpell(unitTarget, 57078, true); - unitTarget->CastSpell(unitTarget, 57080, true); - unitTarget->CastSpell(unitTarget, 57081, true); - return; - } - case 57337: // Great Feast - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, 58067, true); - break; - } - case 57397: // Fish Feast - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, 45548, true); - unitTarget->CastSpell(unitTarget, 57073, true); - unitTarget->CastSpell(unitTarget, 57398, true); - break; - } - case 58466: // Gigantic Feast - case 58475: // Small Feast - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, 57085, true); - break; - } - case 58418: // Portal to Orgrimmar - case 58420: // Portal to Stormwind - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER || effect->EffectIndex != EFFECT_INDEX_0) - return; - - uint32 spellID = m_spellInfo->CalculateSimpleValue(EFFECT_INDEX_0); - uint32 questID = m_spellInfo->CalculateSimpleValue(EFFECT_INDEX_1); - - if (((Player*)unitTarget)->GetQuestStatus(questID) == QUEST_STATUS_COMPLETE && !((Player*)unitTarget)->GetQuestRewardStatus(questID)) - unitTarget->CastSpell(unitTarget, spellID, true); - - return; - } - case 59317: // Teleporting - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - // return from top - if (((Player*)unitTarget)->GetAreaId() == 4637) - unitTarget->CastSpell(unitTarget, 59316, true); - // teleport atop - else - unitTarget->CastSpell(unitTarget, 59314, true); - - return; - } // random spell learn instead placeholder - case 59789: // Oracle Ablutions - { - if (!unitTarget) - return; - - switch (unitTarget->getPowerType()) - { - case POWER_RUNIC_POWER: - { - unitTarget->CastSpell(unitTarget, 59812, true); - break; - } - case POWER_MANA: - { - int32 manapool = unitTarget->GetMaxPower(POWER_MANA) * 0.05; - unitTarget->CastCustomSpell(unitTarget, 59813, &manapool, NULL, NULL, true); - break; - } - case POWER_RAGE: - { - unitTarget->CastSpell(unitTarget, 59814, true); - break; - } - case POWER_ENERGY: - { - unitTarget->CastSpell(unitTarget, 59815, true); - break; - } - // These are not restored - case POWER_FOCUS: - case POWER_RUNE: - case POWER_HEALTH: - break; - } - return; - } - case 60893: // Northrend Alchemy Research - case 61177: // Northrend Inscription Research - case 61288: // Minor Inscription Research - case 61756: // Northrend Inscription Research (FAST QA VERSION) - case 64323: // Book of Glyph Mastery - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - // learn random explicit discovery recipe (if any) - if (uint32 discoveredSpell = GetExplicitDiscoverySpell(m_spellInfo->Id, (Player*)m_caster)) - ((Player*)m_caster)->learnSpell(discoveredSpell, false); - - return; - } - case 62042: // Stormhammer - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - unitTarget->CastSpell(unitTarget, 62470, true); - unitTarget->CastSpell(m_caster, 64909, true); - return; - } - case 62381: // Chill - { - if (!unitTarget) - return; - - unitTarget->RemoveAurasDueToSpell(62373); - unitTarget->CastSpell(unitTarget, 62382, true); - return; - } - case 62488: // Activate Construct - { - if (!unitTarget || !unitTarget->HasAura(62468)) - return; - - unitTarget->RemoveAurasDueToSpell(62468); - unitTarget->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - unitTarget->CastSpell(unitTarget, 64474, true); - - if (m_caster->getVictim()) - ((Creature*)unitTarget)->AI()->AttackStart(m_caster->getVictim()); - return; - } - case 62524: // Attuned to Nature 2 Dose Reduction - case 62525: // Attuned to Nature 10 Dose Reduction - case 62521: // Attuned to Nature 25 Dose Reduction - { - if (!unitTarget) - return; - - uint32 numStacks = 0; - - switch (m_spellInfo->Id) - { - case 62524: numStacks = 2; break; - case 62525: numStacks = 10; break; - case 62521: numStacks = 25; break; - }; - - uint32 spellId = effect->CalculateSimpleValue(); - unitTarget->RemoveAuraHolderFromStack(spellId, numStacks); - return; - } - case 62678: // Summon Allies of Nature - { - const uint32 randSpells[] = - { - 62685, // Summon Wave - 1 Mob - 62686, // Summon Wave - 3 Mob - 62688, // Summon Wave - 10 Mob - }; - - m_caster->CastSpell(m_caster, randSpells[urand(0, countof(randSpells) - 1)], true); - return; - } - case 62688: // Summon Wave - 10 Mob - { - uint32 spellId = effect->CalculateSimpleValue(); - - for (uint32 i = 0; i < 10; ++i) - m_caster->CastSpell(m_caster, spellId, true); - - return; - } - case 62707: // Grab - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - unitTarget->CastSpell(unitTarget, 62708, true); - return; - } - case 63633: // Summon Rubble - { - if (!unitTarget) - return; - - for (uint8 i = 0; i < 5; ++i) - unitTarget->CastSpell(unitTarget, effect->CalculateSimpleValue(), true); - return; - } - case 64456: // Feral Essence Application Removal - { - if (!unitTarget) - return; - - uint32 spellId = effect->CalculateSimpleValue(); - unitTarget->RemoveAuraHolderFromStack(spellId); - return; - } - case 64475: // Strength of the Creator - { - if (!unitTarget) - return; - - unitTarget->RemoveAuraHolderFromStack(64473); - return; - } - case 64767: // Stormhammer - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) - return; - - if (Creature* target = (Creature*)unitTarget) - { - target->AI()->EnterEvadeMode(); - target->CastSpell(target, 62470, true); - target->CastSpell(m_caster, 64909, true); - target->CastSpell(target, 64778, true); - target->ForcedDespawn(10000); - } - return; - } - case 66477: // Bountiful Feast - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, 65422, true); - unitTarget->CastSpell(unitTarget, 66622, true); - break; - } - case 66741: // Chum the Water - { - // maybe this check should be done sooner? - if (!m_caster->IsInWater()) - return; - - uint32 spellId = 0; - - // too low/high? - if (roll_chance_i(33)) - spellId = 66737; // angry - else - { - switch (rand() % 3) - { - case 0: spellId = 66740; break; // blue - case 1: spellId = 66739; break; // tresher - case 2: spellId = 66738; break; // mako - } - } - - if (spellId) - m_caster->CastSpell(m_caster, spellId, true); - - return; - } - case 66744: // Make Player Destroy Totems - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - // Totem of the Earthen Ring does not really require or take reagents. - // Expecting RewardQuest() to already destroy them or we need additional code here to destroy. - unitTarget->CastSpell(unitTarget, 66747, true); - return; - } - case 67009: // Nether Power (ToC25: Lord Jaraxxus) - { - if (!unitTarget) - return; - - for (uint8 i = 0; i < 11; ++i) - unitTarget->CastSpell(unitTarget, effect->CalculateSimpleValue(), true); - - return; - } - case 68861: // Consume Soul (ICC FoS: Bronjahm) - if (unitTarget) - unitTarget->CastSpell(unitTarget, effect->CalculateSimpleValue(), true); - return; - case 68871: // Wailing Souls - // Left or Right direction? - m_caster->CastSpell(m_caster, urand(0, 1) ? 68875 : 68876, false); - // Clear TargetGuid for sweeping - m_caster->SetTargetGuid(ObjectGuid()); - return; - case 69048: // Mirrored Soul - { - if (!unitTarget) - return; - - // This is extremely strange! - // The spell should send SMSG_CHANNEL_START, SMSG_SPELL_START - // However it has cast time 2s, but should send SMSG_SPELL_GO instantly. - m_caster->CastSpell(unitTarget, 69051, true); - return; - } - case 69051: // Mirrored Soul - { - if (!unitTarget) - return; - - // Actually this spell should be sent with SMSG_SPELL_START - unitTarget->CastSpell(m_caster, 69023, true); - return; - } - case 69140: // Coldflame (random target selection) - { - if (!unitTarget) - return; - - m_caster->CastSpell(unitTarget, effect->CalculateSimpleValue(), true); - return; - } - case 69147: // Coldflame - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, effect->CalculateSimpleValue(), true); - return; - } - case 69377: // Fortitude - { - if (!unitTarget) - return; - - m_caster->CastSpell(unitTarget, 72590, true); - return; - } - case 69378: // Blessing of Forgotten Kings - { - if (!unitTarget) - return; - - m_caster->CastSpell(unitTarget, 72586, true); - return; - } - case 69381: // Gift of the Wild - { - if (!unitTarget) - return; - - m_caster->CastSpell(unitTarget, 72588, true); - return; - } - case 71806: // Glittering Sparks - { - if (!unitTarget) - return; - - m_caster->CastSpell(unitTarget, effect->CalculateSimpleValue(), true); - return; - } - case 72034: // Whiteout - case 72096: // Whiteout (heroic) - { - // cast Whiteout visual - m_caster->CastSpell(unitTarget, 72036, true); - return; - } - case 72705: // Coldflame (summon around the caster) - { - if (!unitTarget) - return; - - // Cast summon spells 72701, 72702, 72703, 72704 - for (uint32 triggeredSpell = effect->CalculateSimpleValue(); triggeredSpell < m_spellInfo->Id; ++triggeredSpell) - unitTarget->CastSpell(unitTarget, triggeredSpell, true); - - return; - } - case 74455: // Conflagration - { - if (!unitTarget) - return; - - unitTarget->CastSpell(m_caster, effect->CalculateSimpleValue(), true); - return; - } - } - break; - } - case SPELLFAMILY_WARLOCK: - { - switch (m_spellInfo->Id) - { - case 6201: // Healthstone creating spells - case 6202: - case 5699: - case 11729: - case 11730: - case 27230: - case 47871: - case 47878: - { - if (!unitTarget) - return; - - uint32 itemtype; - uint32 rank = 0; - Unit::AuraList const& mDummyAuras = unitTarget->GetAurasByType(SPELL_AURA_DUMMY); - for (Unit::AuraList::const_iterator i = mDummyAuras.begin(); i != mDummyAuras.end(); ++i) - { - if ((*i)->GetId() == 18692) - { - rank = 1; - break; - } - else if ((*i)->GetId() == 18693) - { - rank = 2; - break; - } - } - - static uint32 const itypes[8][3] = - { - { 5512, 19004, 19005}, // Minor Healthstone - { 5511, 19006, 19007}, // Lesser Healthstone - { 5509, 19008, 19009}, // Healthstone - { 5510, 19010, 19011}, // Greater Healthstone - { 9421, 19012, 19013}, // Major Healthstone - {22103, 22104, 22105}, // Master Healthstone - {36889, 36890, 36891}, // Demonic Healthstone - {36892, 36893, 36894} // Fel Healthstone - }; - - switch (m_spellInfo->Id) - { - case 6201: - itemtype = itypes[0][rank]; break; // Minor Healthstone - case 6202: - itemtype = itypes[1][rank]; break; // Lesser Healthstone - case 5699: - itemtype = itypes[2][rank]; break; // Healthstone - case 11729: - itemtype = itypes[3][rank]; break; // Greater Healthstone - case 11730: - itemtype = itypes[4][rank]; break; // Major Healthstone - case 27230: - itemtype = itypes[5][rank]; break; // Master Healthstone - case 47871: - itemtype = itypes[6][rank]; break; // Demonic Healthstone - case 47878: - itemtype = itypes[7][rank]; break; // Fel Healthstone - default: - return; - } - DoCreateItem( effect, itemtype ); - return; - } - case 47193: // Demonic Empowerment - { - if (!unitTarget) - return; - - uint32 entry = unitTarget->GetEntry(); - uint32 spellID; - switch (entry) - { - case 416: spellID = 54444; break; // imp - case 417: spellID = 54509; break; // fellhunter - case 1860: spellID = 54443; break; // void - case 1863: spellID = 54435; break; // succubus - case 17252: spellID = 54508; break; // fellguard - default: - return; - } - unitTarget->CastSpell(unitTarget, spellID, true); - return; - } - case 47422: // Everlasting Affliction - { - // Need refresh caster corruption auras on target - Unit::SpellAuraHolderMap& suAuras = unitTarget->GetSpellAuraHolderMap(); - for (Unit::SpellAuraHolderMap::iterator itr = suAuras.begin(); itr != suAuras.end(); ++itr) - { - SpellEntry const *spellInfo = (*itr).second->GetSpellProto(); - SpellClassOptionsEntry const* eaClassOptions = spellInfo->GetSpellClassOptions(); - if(eaClassOptions && eaClassOptions->SpellFamilyName == SPELLFAMILY_WARLOCK && - (eaClassOptions->SpellFamilyFlags & UI64LIT(0x0000000000000002)) && - (*itr).second->GetCasterGuid() == m_caster->GetObjectGuid()) - (*itr).second->RefreshHolder(); - } - return; - } - case 63521: // Guarded by The Light (Paladin spell with SPELLFAMILY_WARLOCK) - { - // Divine Plea, refresh on target (3 aura slots) - if (SpellAuraHolder* holder = unitTarget->GetSpellAuraHolder(54428)) - holder->RefreshHolder(); - - return; - } - } - break; - } - case SPELLFAMILY_PRIEST: - { - switch (m_spellInfo->Id) - { - case 47948: // Pain and Suffering - { - if (!unitTarget) - return; - - // Refresh Shadow Word: Pain on target - Unit::SpellAuraHolderMap& auras = unitTarget->GetSpellAuraHolderMap(); - for (Unit::SpellAuraHolderMap::iterator itr = auras.begin(); itr != auras.end(); ++itr) - { - SpellEntry const *spellInfo = (*itr).second->GetSpellProto(); - SpellClassOptionsEntry const* swpClassOptions = spellInfo->GetSpellClassOptions(); - if (swpClassOptions && swpClassOptions->SpellFamilyName == SPELLFAMILY_PRIEST && - (swpClassOptions->SpellFamilyFlags & UI64LIT(0x0000000000008000)) && - (*itr).second->GetCasterGuid() == m_caster->GetObjectGuid()) - { - (*itr).second->RefreshHolder(); - return; - } - } - return; - } - default: - break; - } - break; - } - case SPELLFAMILY_HUNTER: - { - switch (m_spellInfo->Id) - { - case 53209: // Chimera Shot - { - if (!unitTarget) - return; - - uint32 spellId = 0; - int32 basePoint = 0; - Unit* target = unitTarget; - Unit::SpellAuraHolderMap& Auras = unitTarget->GetSpellAuraHolderMap(); - for (Unit::SpellAuraHolderMap::iterator i = Auras.begin(); i != Auras.end(); ++i) - { - SpellAuraHolder* holder = i->second; - if (holder->GetCasterGuid() != m_caster->GetObjectGuid()) - continue; - - // Search only Serpent Sting, Viper Sting, Scorpid Sting auras - SpellClassOptionsEntry const* stingClassOptions = holder->GetSpellProto()->GetSpellClassOptions(); - if (!stingClassOptions || !stingClassOptions->SpellFamilyFlags.IsFitToFamilyMask(UI64LIT(0x000000800000C000))) - continue; - - // Refresh aura duration - holder->RefreshHolder(); - - Aura* aura = holder->GetAuraByEffectIndex(EFFECT_INDEX_0); - - if (!aura) - continue; - - // Serpent Sting - Instantly deals 40% of the damage done by your Serpent Sting. - if (stingClassOptions->IsFitToFamilyMask(UI64LIT(0x0000000000004000))) - { - // m_amount already include RAP bonus - basePoint = aura->GetModifier()->m_amount * aura->GetAuraMaxTicks() * 40 / 100; - spellId = 53353; // Chimera Shot - Serpent - } - - // Viper Sting - Instantly restores mana to you equal to 60% of the total amount drained by your Viper Sting. - if (stingClassOptions->IsFitToFamilyMask(UI64LIT(0x0000008000000000))) - { - uint32 target_max_mana = unitTarget->GetMaxPower(POWER_MANA); - if (!target_max_mana) - continue; - - // ignore non positive values (can be result apply spellmods to aura damage - uint32 pdamage = aura->GetModifier()->m_amount > 0 ? aura->GetModifier()->m_amount : 0; - - // Special case: draining x% of mana (up to a maximum of 2*x% of the caster's maximum mana) - uint32 maxmana = m_caster->GetMaxPower(POWER_MANA) * pdamage * 2 / 100; - - pdamage = target_max_mana * pdamage / 100; - if (pdamage > maxmana) - pdamage = maxmana; - - pdamage *= 4; // total aura damage - basePoint = pdamage * 60 / 100; - spellId = 53358; // Chimera Shot - Viper - target = m_caster; - } - - // Scorpid Sting - Attempts to Disarm the target for 10 sec. This effect cannot occur more than once per 1 minute. - if (stingClassOptions->IsFitToFamilyMask(UI64LIT(0x0000000000008000))) - spellId = 53359; // Chimera Shot - Scorpid - // ?? nothing say in spell desc (possibly need addition check) - // if ((familyFlag & UI64LIT(0x0000010000000000)) || // dot - // (familyFlag & UI64LIT(0x0000100000000000))) // stun - //{ - // spellId = 53366; // 53366 Chimera Shot - Wyvern - //} - } - - if (spellId) - m_caster->CastCustomSpell(target, spellId, &basePoint, 0, 0, false); - - return; - } - case 53412: // Invigoration (pet triggered script, master targeted) - { - if (!unitTarget) - return; - - Unit::AuraList const& auras = unitTarget->GetAurasByType(SPELL_AURA_DUMMY); - for (Unit::AuraList::const_iterator i = auras.begin(); i != auras.end(); ++i) - { - // Invigoration (master talent) - if ((*i)->GetModifier()->m_miscvalue == 8 && (*i)->GetSpellProto()->SpellIconID == 3487) - { - if (roll_chance_i((*i)->GetModifier()->m_amount)) - { - unitTarget->CastSpell(unitTarget, 53398, true, NULL, (*i), m_caster->GetObjectGuid()); - break; - } - } - } - return; - } - case 53271: // Master's Call - { - if (!unitTarget) - return; - - // script effect have in value, but this outdated removed part - unitTarget->CastSpell(unitTarget, 62305, true); - return; - } - default: - break; - } - break; - } - case SPELLFAMILY_PALADIN: - { - // Judgement (seal trigger) - if (m_spellInfo->GetCategory() == SPELLCATEGORY_JUDGEMENT) - { - if (!unitTarget || !unitTarget->isAlive()) - return; - - uint32 spellId1 = 0; - uint32 spellId2 = 0; - - // Judgement self add switch - switch (m_spellInfo->Id) - { - case 53407: spellId1 = 20184; break; // Judgement of Justice - case 20271: // Judgement of Light - case 57774: spellId1 = 20185; break; // Judgement of Light - case 53408: spellId1 = 20186; break; // Judgement of Wisdom - default: - sLog.outError("Unsupported Judgement (seal trigger) spell (Id: %u) in Spell::EffectScriptEffect", m_spellInfo->Id); - return; - } - - // offensive seals have aura dummy in 2 effect - Unit::AuraList const& m_dummyAuras = m_caster->GetAurasByType(SPELL_AURA_DUMMY); - for (Unit::AuraList::const_iterator itr = m_dummyAuras.begin(); itr != m_dummyAuras.end(); ++itr) - { - // search seal (offensive seals have judgement's aura dummy spell id in 2 effect - if ((*itr)->GetEffIndex() != EFFECT_INDEX_2 || !IsSealSpell((*itr)->GetSpellProto())) - continue; - spellId2 = (*itr)->GetModifier()->m_amount; - SpellEntry const* judge = sSpellStore.LookupEntry(spellId2); - if (!judge) - continue; - break; - } - - // if there were no offensive seals than there is seal with proc trigger aura - if (!spellId2) - { - Unit::AuraList const& procTriggerAuras = m_caster->GetAurasByType(SPELL_AURA_PROC_TRIGGER_SPELL); - for (Unit::AuraList::const_iterator itr = procTriggerAuras.begin(); itr != procTriggerAuras.end(); ++itr) - { - if ((*itr)->GetEffIndex() != EFFECT_INDEX_0 || !IsSealSpell((*itr)->GetSpellProto())) - continue; - spellId2 = 54158; - break; - } - } - - if (spellId1) - m_caster->CastSpell(unitTarget, spellId1, true); - - if (spellId2) - m_caster->CastSpell(unitTarget, spellId2, true); - - return; - } - break; - } - case SPELLFAMILY_POTION: - { - switch (m_spellInfo->Id) - { - case 28698: // Dreaming Glory - { - if (!unitTarget) - return; - - unitTarget->CastSpell(unitTarget, 28694, true); - break; - } - case 28702: // Netherbloom - { - if (!unitTarget) - return; - - // 25% chance of casting a random buff - if (roll_chance_i(75)) - return; - - // triggered spells are 28703 to 28707 - // Note: some sources say, that there was the possibility of - // receiving a debuff. However, this seems to be removed by a patch. - const uint32 spellid = 28703; - - // don't overwrite an existing aura - for (uint8 i = 0; i < 5; ++i) - if (unitTarget->HasAura(spellid + i, EFFECT_INDEX_0)) - return; - - unitTarget->CastSpell(unitTarget, spellid + urand(0, 4), true); - break; - } - case 28720: // Nightmare Vine - { - if (!unitTarget) - return; - - // 25% chance of casting Nightmare Pollen - if (roll_chance_i(75)) - return; - - unitTarget->CastSpell(unitTarget, 28721, true); - break; - } - } - break; - } - case SPELLFAMILY_DEATHKNIGHT: - { - switch (m_spellInfo->Id) - { - case 50842: // Pestilence - { - if (!unitTarget) - return; - - Unit* mainTarget = m_targets.getUnitTarget(); - if (!mainTarget) - return; - - // do only refresh diseases on main target if caster has Glyph of Disease - if (mainTarget == unitTarget && !m_caster->HasAura(63334)) - return; - - // Blood Plague - if (mainTarget->HasAura(55078)) - m_caster->CastSpell(unitTarget, 55078, true); - - // Frost Fever - if (mainTarget->HasAura(55095)) - m_caster->CastSpell(unitTarget, 55095, true); - - break; - } - } - break; - } - } - - // normal DB scripted effect - if (!unitTarget) - return; - - // Script based implementation. Must be used only for not good for implementation in core spell effects - // So called only for not processed cases - if (unitTarget->GetTypeId() == TYPEID_UNIT) - { - if (sScriptMgr.OnEffectScriptEffect(m_caster, m_spellInfo->Id, SpellEffectIndex(effect->EffectIndex), (Creature*)unitTarget, m_originalCasterGUID)) - return; - } - - // Previous effect might have started script - if (!ScriptMgr::CanSpellEffectStartDBScript(m_spellInfo, SpellEffectIndex(effect->EffectIndex))) - return; - - DEBUG_FILTER_LOG(LOG_FILTER_SPELL_CAST, "Spell ScriptStart spellid %u in EffectScriptEffect", m_spellInfo->Id); - m_caster->GetMap()->ScriptsStart(sSpellScripts, m_spellInfo->Id, m_caster, unitTarget); -} - -void Spell::EffectSanctuary(SpellEffectEntry const* /*effect*/) -{ - if (!unitTarget) - return; - // unitTarget->CombatStop(); - - unitTarget->CombatStop(); - unitTarget->getHostileRefManager().deleteReferences(); // stop all fighting - - // Vanish allows to remove all threat and cast regular stealth so other spells can be used - if (m_spellInfo->IsFitToFamily(SPELLFAMILY_ROGUE, UI64LIT(0x0000000000000800))) - ((Player*)m_caster)->RemoveSpellsCausingAura(SPELL_AURA_MOD_ROOT); -} - -void Spell::EffectAddComboPoints(SpellEffectEntry const* effect /*effect*/) -{ - if (!unitTarget) - return; - - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - if (damage <= 0) - return; - - ((Player*)m_caster)->AddComboPoints(unitTarget, damage); -} - -void Spell::EffectDuel(SpellEffectEntry const* effect) -{ - if (!m_caster || !unitTarget || m_caster->GetTypeId() != TYPEID_PLAYER || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - Player* caster = (Player*)m_caster; - Player* target = (Player*)unitTarget; - - // caster or target already have requested duel - if (caster->duel || target->duel || !target->GetSocial() || target->GetSocial()->HasIgnore(caster->GetObjectGuid())) - return; - - // Players can only fight a duel with each other outside (=not inside dungeons and not in capital cities) - AreaTableEntry const* casterAreaEntry = GetAreaEntryByAreaID(caster->GetAreaId()); - if (casterAreaEntry && !(casterAreaEntry->flags & AREA_FLAG_DUEL)) - { - SendCastResult(SPELL_FAILED_NO_DUELING); // Dueling isn't allowed here - return; - } - - AreaTableEntry const* targetAreaEntry = GetAreaEntryByAreaID(target->GetAreaId()); - if (targetAreaEntry && !(targetAreaEntry->flags & AREA_FLAG_DUEL)) - { - SendCastResult(SPELL_FAILED_NO_DUELING); // Dueling isn't allowed here - return; - } - - // CREATE DUEL FLAG OBJECT - GameObject* pGameObj = new GameObject; - - uint32 gameobject_id = effect->EffectMiscValue; - - Map* map = m_caster->GetMap(); - float x = (m_caster->GetPositionX() + unitTarget->GetPositionX()) * 0.5f; - float y = (m_caster->GetPositionY() + unitTarget->GetPositionY()) * 0.5f; - float z = m_caster->GetPositionZ(); - m_caster->UpdateAllowedPositionZ(x, y, z); - if (!pGameObj->Create(map->GenerateLocalLowGuid(HIGHGUID_GAMEOBJECT), gameobject_id, map, m_caster->GetPhaseMask(), x, y, z, m_caster->GetOrientation())) - { - delete pGameObj; - return; - } - - pGameObj->SetUInt32Value(GAMEOBJECT_FACTION, m_caster->getFaction()); - pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel() + 1); - - pGameObj->SetRespawnTime(m_duration > 0 ? m_duration / IN_MILLISECONDS : 0); - pGameObj->SetSpellId(m_spellInfo->Id); - - m_caster->AddGameObject(pGameObj); - map->Add(pGameObj); - // END - - // Send request - WorldPacket data(SMSG_DUEL_REQUESTED, 8 + 8); - data << pGameObj->GetObjectGuid(); - data << caster->GetObjectGuid(); - caster->GetSession()->SendPacket(&data); - target->GetSession()->SendPacket(&data); - - // create duel-info - DuelInfo* duel = new DuelInfo; - duel->initiator = caster; - duel->opponent = target; - duel->startTime = 0; - duel->startTimer = 0; - caster->duel = duel; - - DuelInfo* duel2 = new DuelInfo; - duel2->initiator = caster; - duel2->opponent = caster; - duel2->startTime = 0; - duel2->startTimer = 0; - target->duel = duel2; - - caster->SetGuidValue(PLAYER_DUEL_ARBITER, pGameObj->GetObjectGuid()); - target->SetGuidValue(PLAYER_DUEL_ARBITER, pGameObj->GetObjectGuid()); -} - -void Spell::EffectStuck(SpellEffectEntry const* effect /*effect*/) -{ - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - if (!sWorld.getConfig(CONFIG_BOOL_CAST_UNSTUCK)) - return; - - Player* pTarget = (Player*)unitTarget; - - DEBUG_LOG("Spell Effect: Stuck"); - DETAIL_LOG("Player %s (guid %u) used auto-unstuck future at map %u (%f, %f, %f)", pTarget->GetName(), pTarget->GetGUIDLow(), m_caster->GetMapId(), m_caster->GetPositionX(), pTarget->GetPositionY(), pTarget->GetPositionZ()); - - if (pTarget->IsTaxiFlying()) - return; - - // homebind location is loaded always - pTarget->TeleportToHomebind(unitTarget == m_caster ? TELE_TO_SPELL : 0); - - // Stuck spell trigger Hearthstone cooldown - SpellEntry const* spellInfo = sSpellStore.LookupEntry(8690); - if (!spellInfo) - return; - Spell spell(pTarget, spellInfo, true); - spell.SendSpellCooldown(); -} - -void Spell::EffectSummonPlayer(SpellEffectEntry const* /*effect*/) -{ - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - // Evil Twin (ignore player summon, but hide this for summoner) - if (unitTarget->GetDummyAura(23445)) - return; - - float x, y, z; - m_caster->GetClosePoint(x, y, z, unitTarget->GetObjectBoundingRadius()); - - ((Player*)unitTarget)->SetSummonPoint(m_caster->GetMapId(), x, y, z); - - WorldPacket data(SMSG_SUMMON_REQUEST, 8 + 4 + 4); - data << m_caster->GetObjectGuid(); // summoner guid - data << uint32(m_caster->GetZoneId()); // summoner zone - data << uint32(MAX_PLAYER_SUMMON_DELAY * IN_MILLISECONDS); // auto decline after msecs - ((Player*)unitTarget)->GetSession()->SendPacket(&data); -} - -static ScriptInfo generateActivateCommand() -{ - ScriptInfo si; - si.command = SCRIPT_COMMAND_ACTIVATE_OBJECT; - si.id = 0; - si.buddyEntry = 0; - si.searchRadiusOrGuid = 0; - si.data_flags = 0x00; - return si; -} - -void Spell::EffectActivateObject(SpellEffectEntry const* effect) -{ - if (!gameObjTarget) - return; - - uint32 misc_value = uint32(effect->EffectMiscValue); - - switch (misc_value) - { - case 1: // GO simple use - case 2: // unk - 2 spells - case 4: // unk - 1 spell - case 5: // GO trap usage - case 7: // unk - 2 spells - case 8: // GO usage with TargetB = none or random - case 10: // GO explosions - case 11: // unk - 1 spell - case 19: // unk - 1 spell - case 20: // unk - 2 spells - { - static ScriptInfo activateCommand = generateActivateCommand(); - - int32 delay_secs = effect->CalculateSimpleValue(); - gameObjTarget->GetMap()->ScriptCommandStart(activateCommand, delay_secs, m_caster, gameObjTarget); - break; - } - case 3: // GO custom anim - found mostly in Lunar Fireworks spells - gameObjTarget->SendGameObjectCustomAnim(gameObjTarget->GetObjectGuid()); - break; - case 12: // GO state active alternative - found mostly in Simon Game spells - gameObjTarget->UseDoorOrButton(0, true); - break; - case 13: // GO state ready - found only in Simon Game spells - gameObjTarget->ResetDoorOrButton(); - break; - case 15: // GO destroy - gameObjTarget->SetLootState(GO_JUST_DEACTIVATED); - break; - case 16: // GO custom use - found mostly in Wind Stones spells, Simon Game spells and other GO target summoning spells - { - switch (m_spellInfo->Id) - { - case 24734: // Summon Templar Random - case 24744: // Summon Templar (fire) - case 24756: // Summon Templar (air) - case 24758: // Summon Templar (earth) - case 24760: // Summon Templar (water) - case 24763: // Summon Duke Random - case 24765: // Summon Duke (fire) - case 24768: // Summon Duke (air) - case 24770: // Summon Duke (earth) - case 24772: // Summon Duke (water) - case 24784: // Summon Royal Random - case 24786: // Summon Royal (fire) - case 24788: // Summon Royal (air) - case 24789: // Summon Royal (earth) - case 24790: // Summon Royal (water) - { - uint32 npcEntry = 0; - uint32 templars[] = {15209, 15211, 15212, 15307}; - uint32 dukes[] = {15206, 15207, 15208, 15220}; - uint32 royals[] = {15203, 15204, 15205, 15305}; - - switch (m_spellInfo->Id) - { - case 24734: npcEntry = templars[urand(0, 3)]; break; - case 24763: npcEntry = dukes[urand(0, 3)]; break; - case 24784: npcEntry = royals[urand(0, 3)]; break; - case 24744: npcEntry = 15209; break; - case 24756: npcEntry = 15212; break; - case 24758: npcEntry = 15307; break; - case 24760: npcEntry = 15211; break; - case 24765: npcEntry = 15206; break; - case 24768: npcEntry = 15220; break; - case 24770: npcEntry = 15208; break; - case 24772: npcEntry = 15207; break; - case 24786: npcEntry = 15203; break; - case 24788: npcEntry = 15204; break; - case 24789: npcEntry = 15205; break; - case 24790: npcEntry = 15305; break; - } - - gameObjTarget->SummonCreature(npcEntry, gameObjTarget->GetPositionX(), gameObjTarget->GetPositionY(), gameObjTarget->GetPositionZ(), gameObjTarget->GetAngle(m_caster), TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, MINUTE * IN_MILLISECONDS); - gameObjTarget->SetLootState(GO_JUST_DEACTIVATED); - break; - } - case 40176: // Simon Game pre-game Begin, blue - case 40177: // Simon Game pre-game Begin, green - case 40178: // Simon Game pre-game Begin, red - case 40179: // Simon Game pre-game Begin, yellow - case 40283: // Simon Game END, blue - case 40284: // Simon Game END, green - case 40285: // Simon Game END, red - case 40286: // Simon Game END, yellow - case 40494: // Simon Game, switched ON - case 40495: // Simon Game, switched OFF - case 40512: // Simon Game, switch...disable Off switch - gameObjTarget->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); - break; - case 40632: // Summon Gezzarak the Huntress - case 40640: // Summon Karrog - case 40642: // Summon Darkscreecher Akkarai - case 40644: // Summon Vakkiz the Windrager - case 41004: // Summon Terokk - gameObjTarget->SetLootState(GO_JUST_DEACTIVATED); - break; - case 46085: // Place Fake Fur - { - float x, y, z; - gameObjTarget->GetClosePoint(x, y, z, gameObjTarget->GetObjectBoundingRadius(), 2 * INTERACTION_DISTANCE, frand(0, M_PI_F * 2)); - - // Note: event script is implemented in script library - gameObjTarget->SummonCreature(25835, x, y, z, gameObjTarget->GetOrientation(), TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 15000); - gameObjTarget->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE); - break; - } - case 46592: // Summon Ahune Lieutenant - { - uint32 npcEntry = 0; - - switch (gameObjTarget->GetEntry()) - { - case 188049: npcEntry = 26116; break; // Frostwave Lieutenant (Ashenvale) - case 188137: npcEntry = 26178; break; // Hailstone Lieutenant (Desolace) - case 188138: npcEntry = 26204; break; // Chillwind Lieutenant (Stranglethorn) - case 188148: npcEntry = 26214; break; // Frigid Lieutenant (Searing Gorge) - case 188149: npcEntry = 26215; break; // Glacial Lieutenant (Silithus) - case 188150: npcEntry = 26216; break; // Glacial Templar (Hellfire Peninsula) - } - - gameObjTarget->SummonCreature(npcEntry, gameObjTarget->GetPositionX(), gameObjTarget->GetPositionY(), gameObjTarget->GetPositionZ(), gameObjTarget->GetAngle(m_caster), TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, MINUTE * IN_MILLISECONDS); - gameObjTarget->SetLootState(GO_JUST_DEACTIVATED); - break; - } - } - break; - } - case 17: // GO unlock - found mostly in Simon Game spells - gameObjTarget->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); - break; - default: - sLog.outError("Spell::EffectActivateObject called with unknown misc value. Spell Id %u", m_spellInfo->Id); - break; - } -} - -void Spell::EffectApplyGlyph(SpellEffectEntry const* effect) -{ - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - Player* player = (Player*)m_caster; - - // glyph sockets level requirement - uint8 minLevel = 0; - switch (m_glyphIndex) - { - case 0: - case 1: - case 6: minLevel = 25; break; - case 2: - case 3: - case 7: minLevel = 50; break; - case 4: - case 5: - case 8: minLevel = 75; break; - } - - if (minLevel && m_caster->getLevel() < minLevel) - { - SendCastResult(SPELL_FAILED_GLYPH_SOCKET_LOCKED); - return; - } - - // apply new one - if(uint32 glyph = effect->EffectMiscValue) - { - if (GlyphPropertiesEntry const* gp = sGlyphPropertiesStore.LookupEntry(glyph)) - { - if (GlyphSlotEntry const* gs = sGlyphSlotStore.LookupEntry(player->GetGlyphSlot(m_glyphIndex))) - { - if (gp->TypeFlags != gs->TypeFlags) - { - SendCastResult(SPELL_FAILED_INVALID_GLYPH); - return; // glyph slot mismatch - } - } - - // remove old glyph - player->ApplyGlyph(m_glyphIndex, false); - player->SetGlyph(m_glyphIndex, glyph); - player->ApplyGlyph(m_glyphIndex, true); - player->SendTalentsInfoData(false); - } - } -} - -void Spell::EffectEnchantHeldItem(SpellEffectEntry const* effect) -{ - // this is only item spell effect applied to main-hand weapon of target player (players in area) - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - Player* item_owner = (Player*)unitTarget; - Item* item = item_owner->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); - - if (!item) - return; - - // must be equipped - if (!item ->IsEquipped()) - return; - - if (effect->EffectMiscValue) - { - uint32 enchant_id = effect->EffectMiscValue; - int32 duration = m_duration; // Try duration index first... - if (!duration) - duration = m_currentBasePoints[SpellEffectIndex(effect->EffectIndex)]; // Base points after... - if (!duration) - duration = 10; // 10 seconds for enchants which don't have listed duration - - SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); - if (!pEnchant) - return; - - // Always go to temp enchantment slot - EnchantmentSlot slot = TEMP_ENCHANTMENT_SLOT; - - // Enchantment will not be applied if a different one already exists - if (item->GetEnchantmentId(slot) && item->GetEnchantmentId(slot) != enchant_id) - return; - - // Apply the temporary enchantment - item->SetEnchantment(slot, enchant_id, duration * IN_MILLISECONDS, 0); - item_owner->ApplyEnchantment(item, slot, true); - } -} - -void Spell::EffectDisEnchant(SpellEffectEntry const* /*effect*/) -{ - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - Player* p_caster = (Player*)m_caster; - if (!itemTarget || !itemTarget->GetProto()->DisenchantID) - return; - - p_caster->UpdateCraftSkill(m_spellInfo->Id); - - ((Player*)m_caster)->SendLoot(itemTarget->GetObjectGuid(), LOOT_DISENCHANTING); - - // item will be removed at disenchanting end -} - -void Spell::EffectInebriate(SpellEffectEntry const* /*effect*/) -{ - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - Player* player = (Player*)unitTarget; - - uint8 drunkValue = player->GetDrunkValue() + (uint8)damage; - if (drunkValue > 100) - { - drunkValue = 100; - if (roll_chance_i(25)) - player->CastSpell(player, 67468, false); // Drunken Vomit - } - player->SetDrunkValue(drunkValue, m_CastItem ? m_CastItem->GetEntry() : 0); -} - -void Spell::EffectFeedPet(SpellEffectEntry const* effect) -{ - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - Player* _player = (Player*)m_caster; - - Item* foodItem = m_targets.getItemTarget(); - if (!foodItem) - return; - - Pet* pet = _player->GetPet(); - if (!pet) - return; - - if (!pet->isAlive()) - return; - - int32 benefit = pet->GetCurrentFoodBenefitLevel(foodItem->GetProto()->ItemLevel); - if (benefit <= 0) - return; - - uint32 count = 1; - _player->DestroyItemCount(foodItem, count, true); - // TODO: fix crash when a spell has two effects, both pointed at the same item target - - m_caster->CastCustomSpell(pet, effect->EffectTriggerSpell, &benefit, NULL, NULL, true); -} - -void Spell::EffectDismissPet(SpellEffectEntry const* /*effect*/) -{ - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - Pet* pet = m_caster->GetPet(); - - // not let dismiss dead pet - if (!pet || !pet->isAlive()) - return; - - pet->Unsummon(PET_SAVE_NOT_IN_SLOT, m_caster); -} - -void Spell::EffectSummonObject(SpellEffectEntry const* effect) -{ - uint32 go_id = effect->EffectMiscValue; - uint8 slot = effect->EffectMiscValueB; - if (slot >= MAX_OBJECT_SLOT) - return; - - if (ObjectGuid guid = m_caster->m_ObjectSlotGuid[slot]) - { - if (GameObject* obj = m_caster ? m_caster->GetMap()->GetGameObject(guid) : NULL) - obj->SetLootState(GO_JUST_DEACTIVATED); - m_caster->m_ObjectSlotGuid[slot].Clear(); - } - - GameObject* pGameObj = new GameObject; - - float x, y, z; - // If dest location if present - if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) - m_targets.getDestination(x, y, z); - // Summon in random point all other units if location present - else - m_caster->GetClosePoint(x, y, z, DEFAULT_WORLD_OBJECT_SIZE); - - Map* map = m_caster->GetMap(); - if (!pGameObj->Create(map->GenerateLocalLowGuid(HIGHGUID_GAMEOBJECT), go_id, map, - m_caster->GetPhaseMask(), x, y, z, m_caster->GetOrientation())) - { - delete pGameObj; - return; - } - - pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel()); - pGameObj->SetRespawnTime(m_duration > 0 ? m_duration / IN_MILLISECONDS : 0); - pGameObj->SetSpellId(m_spellInfo->Id); - m_caster->AddGameObject(pGameObj); - - map->Add(pGameObj); - - m_caster->m_ObjectSlotGuid[slot] = pGameObj->GetObjectGuid(); - - pGameObj->SummonLinkedTrapIfAny(); - - if (m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->AI()) - ((Creature*)m_caster)->AI()->JustSummoned(pGameObj); - if (m_originalCaster && m_originalCaster != m_caster && m_originalCaster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_originalCaster)->AI()) - ((Creature*)m_originalCaster)->AI()->JustSummoned(pGameObj); -} - -void Spell::EffectResurrect(SpellEffectEntry const* /*effect*/) -{ - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - if (unitTarget->isAlive() || !unitTarget->IsInWorld()) - return; - - switch (m_spellInfo->Id) - { - case 8342: // Defibrillate (Goblin Jumper Cables) has 33% chance on success - case 22999: // Defibrillate (Goblin Jumper Cables XL) has 50% chance on success - case 54732: // Defibrillate (Gnomish Army Knife) has 67% chance on success - { - uint32 failChance = 0; - uint32 failSpellId = 0; - switch (m_spellInfo->Id) - { - case 8342: failChance = 67; failSpellId = 8338; break; - case 22999: failChance = 50; failSpellId = 23055; break; - case 54732: failChance = 33; failSpellId = 0; break; - } - - if (roll_chance_i(failChance)) - { - if (failSpellId) - m_caster->CastSpell(m_caster, failSpellId, true, m_CastItem); - return; - } - break; - } - default: - break; - } - - Player* pTarget = ((Player*)unitTarget); - - if (pTarget->isRessurectRequested()) // already have one active request - return; - - uint32 health = pTarget->GetMaxHealth() * damage / 100; - uint32 mana = pTarget->GetMaxPower(POWER_MANA) * damage / 100; - - pTarget->setResurrectRequestData(m_caster->GetObjectGuid(), m_caster->GetMapId(), m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ(), health, mana); - SendResurrectRequest(pTarget); -} - -void Spell::EffectAddExtraAttacks(SpellEffectEntry const* /*effect*/) -{ - if (!unitTarget || !unitTarget->isAlive()) - return; - - if (unitTarget->m_extraAttacks) - return; - - unitTarget->m_extraAttacks = damage; -} - -void Spell::EffectParry(SpellEffectEntry const* /*effect*/) -{ - if (unitTarget && unitTarget->GetTypeId() == TYPEID_PLAYER) - ((Player*)unitTarget)->SetCanParry(true); -} - -void Spell::EffectBlock(SpellEffectEntry const* /*effect*/) -{ - if (unitTarget && unitTarget->GetTypeId() == TYPEID_PLAYER) - ((Player*)unitTarget)->SetCanBlock(true); -} - -void Spell::EffectLeapForward(SpellEffectEntry const* effect) -{ - if (unitTarget->IsTaxiFlying()) - return; - - if (m_spellInfo->rangeIndex == SPELL_RANGE_IDX_SELF_ONLY) - { - float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(effect->GetRadiusIndex())); - - // before caster - float fx, fy, fz; - unitTarget->GetClosePoint(fx, fy, fz, unitTarget->GetObjectBoundingRadius(), dis); - float ox, oy, oz; - unitTarget->GetPosition(ox, oy, oz); - - if (unitTarget->GetMap()->GetHitPosition(ox, oy, oz + 0.5f, fx, fy, fz, unitTarget->GetPhaseMask(), -0.5f)) - unitTarget->UpdateAllowedPositionZ(fx, fy, fz); - - unitTarget->NearTeleportTo(fx, fy, fz, unitTarget->GetOrientation(), unitTarget == m_caster); - } -} - -void Spell::EffectLeapBack(SpellEffectEntry const* effect) -{ - if (unitTarget->IsTaxiFlying()) - return; - - m_caster->KnockBackFrom(unitTarget, float(effect->EffectMiscValue) / 10, float(damage) / 10); -} - -void Spell::EffectReputation(SpellEffectEntry const* effect) -{ - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - Player* _player = (Player*)unitTarget; - - int32 rep_change = m_currentBasePoints[effect->EffectIndex]; - uint32 faction_id = effect->EffectMiscValue; - - FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction_id); - - if (!factionEntry) - return; - - rep_change = _player->CalculateReputationGain(REPUTATION_SOURCE_SPELL, rep_change, faction_id); - - _player->GetReputationMgr().ModifyReputation(factionEntry, rep_change); -} - -void Spell::EffectQuestComplete(SpellEffectEntry const* effect) -{ - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - // A few spells has additional value from basepoints, check condition here. - switch (m_spellInfo->Id) - { - case 43458: // Secrets of Nifflevar - { - if (!unitTarget->HasAura(effect->CalculateSimpleValue())) - return; - - break; - } - // TODO: implement these! - // "this spell awards credit for the entire raid (all spell targets as this is area target) if just ONE member has both auras (yes, both effect's basepoints)" - // case 72155: // Harvest Blight Specimen - // case 72162: // Harvest Blight Specimen - // break; - default: - break; - } - - uint32 quest_id = effect->EffectMiscValue; - ((Player*)unitTarget)->AreaExploredOrEventHappens(quest_id); -} - -void Spell::EffectSelfResurrect(SpellEffectEntry const* effect) -{ - if (!unitTarget || unitTarget->isAlive()) - return; - if (unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - if (!unitTarget->IsInWorld()) - return; - - uint32 health = 0; - uint32 mana = 0; - - // flat case - if (damage < 0) - { - health = uint32(-damage); - mana = effect->EffectMiscValue; - } - // percent case - else - { - health = uint32(damage / 100.0f * unitTarget->GetMaxHealth()); - if (unitTarget->GetMaxPower(POWER_MANA) > 0) - mana = uint32(damage / 100.0f * unitTarget->GetMaxPower(POWER_MANA)); - } - - Player* plr = ((Player*)unitTarget); - plr->ResurrectPlayer(0.0f); - - plr->SetHealth(health); - plr->SetPower(POWER_MANA, mana); - plr->SetPower(POWER_RAGE, 0); - plr->SetPower(POWER_ENERGY, plr->GetMaxPower(POWER_ENERGY)); - - plr->SpawnCorpseBones(); -} - -void Spell::EffectSkinning(SpellEffectEntry const* /*effect*/) -{ - if (unitTarget->GetTypeId() != TYPEID_UNIT) - return; - if (!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - Creature* creature = (Creature*) unitTarget; - int32 targetLevel = creature->getLevel(); - - uint32 skill = creature->GetCreatureInfo()->GetRequiredLootSkill(); - - ((Player*)m_caster)->SendLoot(creature->GetObjectGuid(), LOOT_SKINNING); - creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); - - int32 reqValue = targetLevel < 10 ? 0 : targetLevel < 20 ? (targetLevel - 10) * 10 : targetLevel * 5; - - int32 skillValue = ((Player*)m_caster)->GetPureSkillValue(skill); - - // Double chances for elites - ((Player*)m_caster)->UpdateGatherSkill(skill, skillValue, reqValue, creature->IsElite() ? 2 : 1); -} - -void Spell::EffectCharge(SpellEffectEntry const* /*effect*/) -{ - if (!unitTarget) - return; - - // TODO: research more ContactPoint/attack distance. - // 3.666666 instead of ATTACK_DISTANCE(5.0f) in below seem to give more accurate result. - float x, y, z; - unitTarget->GetContactPoint(m_caster, x, y, z, 3.666666f); - - if (unitTarget->GetTypeId() != TYPEID_PLAYER) - ((Creature*)unitTarget)->StopMoving(); - - // Only send MOVEMENTFLAG_WALK_MODE, client has strange issues with other move flags - m_caster->MonsterMoveWithSpeed(x, y, z, 24.f, true, true); - - // not all charge effects used in negative spells - if (unitTarget != m_caster && !IsPositiveSpell(m_spellInfo->Id)) - m_caster->Attack(unitTarget, true); -} - -void Spell::EffectCharge2(SpellEffectEntry const* /*effect*/) -{ - float x, y, z; - if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) - { - m_targets.getDestination(x, y, z); - - if (unitTarget->GetTypeId() != TYPEID_PLAYER) - ((Creature*)unitTarget)->StopMoving(); - } - else if (unitTarget && unitTarget != m_caster) - unitTarget->GetContactPoint(m_caster, x, y, z, 3.666666f); - else - return; - - // Only send MOVEMENTFLAG_WALK_MODE, client has strange issues with other move flags - m_caster->MonsterMoveWithSpeed(x, y, z, 24.f, true, true); - - // not all charge effects used in negative spells - if (unitTarget && unitTarget != m_caster && !IsPositiveSpell(m_spellInfo->Id)) - m_caster->Attack(unitTarget, true); -} - -void Spell::EffectKnockBack(SpellEffectEntry const* effect) -{ - if (!unitTarget) - return; - - unitTarget->KnockBackFrom(m_caster, float(effect->EffectMiscValue) / 10, float(damage) / 10); -} - -void Spell::EffectSendTaxi(SpellEffectEntry const* effect) -{ - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - ((Player*)unitTarget)->ActivateTaxiPathTo(effect->EffectMiscValue, m_spellInfo->Id); -} - -void Spell::EffectPlayerPull(SpellEffectEntry const* effect) -{ - if (!unitTarget) - return; - - float dist = unitTarget->GetDistance2d(m_caster); - if (damage && dist > damage) - dist = float(damage); - - unitTarget->KnockBackFrom(m_caster, -dist, float(effect->EffectMiscValue) / 10); -} - -void Spell::EffectDispelMechanic(SpellEffectEntry const* effect) -{ - if (!unitTarget) - return; - - uint32 mechanic = effect->EffectMiscValue; - - Unit::SpellAuraHolderMap& Auras = unitTarget->GetSpellAuraHolderMap(); - for (Unit::SpellAuraHolderMap::iterator iter = Auras.begin(), next; iter != Auras.end(); iter = next) - { - next = iter; - ++next; - SpellEntry const* spell = iter->second->GetSpellProto(); - if (iter->second->HasMechanic(mechanic)) - { - unitTarget->RemoveAurasDueToSpell(spell->Id); - if (Auras.empty()) - break; - else - next = Auras.begin(); - } - } -} - -void Spell::EffectSummonDeadPet(SpellEffectEntry const* /*effect*/) -{ - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - Player* _player = (Player*)m_caster; - Pet* pet = _player->GetPet(); - if (!pet) - return; - if (pet->isAlive()) - return; - if (damage < 0) - return; - - pet->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_NONE); - pet->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); - pet->SetDeathState(ALIVE); - pet->clearUnitState(UNIT_STAT_ALL_STATE); - pet->SetHealth(uint32(pet->GetMaxHealth() * (float(damage) / 100))); - - pet->AIM_Initialize(); - - // _player->PetSpellInitialize(); -- action bar not removed at death and not required send at revive - pet->SavePetToDB(PET_SAVE_AS_CURRENT); -} - -void Spell::EffectSummonAllTotems(SpellEffectEntry const* effect) -{ - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - int32 start_button = ACTION_BUTTON_SHAMAN_TOTEMS_BAR + effect->EffectMiscValue; - int32 amount_buttons = effect->EffectMiscValueB; - - for (int32 slot = 0; slot < amount_buttons; ++slot) - if (ActionButton const* actionButton = ((Player*)m_caster)->GetActionButton(start_button + slot)) - if (actionButton->GetType() == ACTION_BUTTON_SPELL) - if (uint32 spell_id = actionButton->GetAction()) - m_caster->CastSpell(unitTarget, spell_id, true); -} - -void Spell::EffectDestroyAllTotems(SpellEffectEntry const* /*effect*/) -{ - int32 mana = 0; - for (int slot = 0; slot < MAX_TOTEM_SLOT; ++slot) - { - if (Totem* totem = m_caster->GetTotem(TotemSlot(slot))) - { - if (damage) - { - uint32 spell_id = totem->GetUInt32Value(UNIT_CREATED_BY_SPELL); - if (SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell_id)) - { - uint32 manacost = m_caster->GetCreateMana() * spellInfo->GetManaCostPercentage() / 100; - mana += manacost * damage / 100; - } - } - totem->UnSummon(); - } - } - - if (mana) - m_caster->CastCustomSpell(m_caster, 39104, &mana, NULL, NULL, true); -} - -void Spell::EffectBreakPlayerTargeting (SpellEffectEntry const* /*effect*/) -{ - if (!unitTarget) - return; - - WorldPacket data(SMSG_CLEAR_TARGET, 8); - data << unitTarget->GetObjectGuid(); - unitTarget->SendMessageToSet(&data, false); -} - -void Spell::EffectDurabilityDamage(SpellEffectEntry const* effect) -{ - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - int32 slot = effect->EffectMiscValue; - - // FIXME: some spells effects have value -1/-2 - // Possibly its mean -1 all player equipped items and -2 all items - if (slot < 0) - { - ((Player*)unitTarget)->DurabilityPointsLossAll(damage, (slot < -1)); - return; - } - - // invalid slot value - if (slot >= INVENTORY_SLOT_BAG_END) - return; - - if (Item* item = ((Player*)unitTarget)->GetItemByPos(INVENTORY_SLOT_BAG_0, slot)) - ((Player*)unitTarget)->DurabilityPointsLoss(item, damage); -} - -void Spell::EffectDurabilityDamagePCT(SpellEffectEntry const* effect) -{ - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - int32 slot = effect->EffectMiscValue; - - // FIXME: some spells effects have value -1/-2 - // Possibly its mean -1 all player equipped items and -2 all items - if (slot < 0) - { - ((Player*)unitTarget)->DurabilityLossAll(double(damage) / 100.0f, (slot < -1)); - return; - } - - // invalid slot value - if (slot >= INVENTORY_SLOT_BAG_END) - return; - - if (damage <= 0) - return; - - if (Item* item = ((Player*)unitTarget)->GetItemByPos(INVENTORY_SLOT_BAG_0, slot)) - ((Player*)unitTarget)->DurabilityLoss(item, double(damage) / 100.0f); -} - -void Spell::EffectModifyThreatPercent(SpellEffectEntry const* /*effect*/) -{ - if (!unitTarget) - return; - - unitTarget->getThreatManager().modifyThreatPercent(m_caster, damage); -} - -void Spell::EffectTransmitted(SpellEffectEntry const* effect) -{ - uint32 name_id = effect->EffectMiscValue; - - GameObjectInfo const* goinfo = ObjectMgr::GetGameObjectInfo(name_id); - - if (!goinfo) - { - sLog.outErrorDb("Gameobject (Entry: %u) not exist and not created at spell (ID: %u) cast", name_id, m_spellInfo->Id); - return; - } - - float fx, fy, fz; - - if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) - m_targets.getDestination(fx, fy, fz); - // FIXME: this can be better check for most objects but still hack - else if (effect->GetRadiusIndex() && m_spellInfo->speed == 0) - { - float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(effect->GetRadiusIndex())); - m_caster->GetClosePoint(fx, fy, fz, DEFAULT_WORLD_OBJECT_SIZE, dis); - } - else - { - float min_dis = GetSpellMinRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)); - float max_dis = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)); - float dis = rand_norm_f() * (max_dis - min_dis) + min_dis; - - // special code for fishing bobber (TARGET_SELF_FISHING), should not try to avoid objects - // nor try to find ground level, but randomly vary in angle - if (goinfo->type == GAMEOBJECT_TYPE_FISHINGNODE) - { - // calculate angle variation for roughly equal dimensions of target area - float max_angle = (max_dis - min_dis) / (max_dis + m_caster->GetObjectBoundingRadius()); - float angle_offset = max_angle * (rand_norm_f() - 0.5f); - m_caster->GetNearPoint2D(fx, fy, dis + m_caster->GetObjectBoundingRadius(), m_caster->GetOrientation() + angle_offset); - - GridMapLiquidData liqData; - if (!m_caster->GetTerrain()->IsInWater(fx, fy, m_caster->GetPositionZ() + 1.f, &liqData)) - { - SendCastResult(SPELL_FAILED_NOT_FISHABLE); - SendChannelUpdate(0); - return; - } - - fz = liqData.level; - // finally, check LoS - if (!m_caster->IsWithinLOS(fx, fy, fz)) - { - SendCastResult(SPELL_FAILED_LINE_OF_SIGHT); - SendChannelUpdate(0); - return; - } - } - else - m_caster->GetClosePoint(fx, fy, fz, DEFAULT_WORLD_OBJECT_SIZE, dis); - } - - Map* cMap = m_caster->GetMap(); - - // if gameobject is summoning object, it should be spawned right on caster's position - if (goinfo->type == GAMEOBJECT_TYPE_SUMMONING_RITUAL) - { - m_caster->GetPosition(fx, fy, fz); - } - - GameObject* pGameObj = new GameObject; - - if (!pGameObj->Create(cMap->GenerateLocalLowGuid(HIGHGUID_GAMEOBJECT), name_id, cMap, - m_caster->GetPhaseMask(), fx, fy, fz, m_caster->GetOrientation())) - { - delete pGameObj; - return; - } - - int32 duration = m_duration; - - switch (goinfo->type) - { - case GAMEOBJECT_TYPE_FISHINGNODE: - { - m_caster->SetChannelObjectGuid(pGameObj->GetObjectGuid()); - m_caster->AddGameObject(pGameObj); // will removed at spell cancel - - // end time of range when possible catch fish (FISHING_BOBBER_READY_TIME..GetDuration(m_spellInfo)) - // start time == fish-FISHING_BOBBER_READY_TIME (0..GetDuration(m_spellInfo)-FISHING_BOBBER_READY_TIME) - int32 lastSec = 0; - switch (urand(0, 3)) - { - case 0: lastSec = 3; break; - case 1: lastSec = 7; break; - case 2: lastSec = 13; break; - case 3: lastSec = 17; break; - } - - duration = duration - lastSec * IN_MILLISECONDS + FISHING_BOBBER_READY_TIME * IN_MILLISECONDS; - break; - } - case GAMEOBJECT_TYPE_SUMMONING_RITUAL: - { - if (m_caster->GetTypeId() == TYPEID_PLAYER) - { - pGameObj->AddUniqueUse((Player*)m_caster); - m_caster->AddGameObject(pGameObj); // will removed at spell cancel - } - break; - } - case GAMEOBJECT_TYPE_FISHINGHOLE: - case GAMEOBJECT_TYPE_CHEST: - default: - break; - } - - pGameObj->SetRespawnTime(duration > 0 ? duration / IN_MILLISECONDS : 0); - - pGameObj->SetOwnerGuid(m_caster->GetObjectGuid()); - - pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel()); - pGameObj->SetSpellId(m_spellInfo->Id); - - DEBUG_LOG("AddObject at SpellEfects.cpp EffectTransmitted"); - // m_caster->AddGameObject(pGameObj); - // m_ObjToDel.push_back(pGameObj); - - cMap->Add(pGameObj); - - pGameObj->SummonLinkedTrapIfAny(); - - if (m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->AI()) - ((Creature*)m_caster)->AI()->JustSummoned(pGameObj); - if (m_originalCaster && m_originalCaster != m_caster && m_originalCaster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_originalCaster)->AI()) - ((Creature*)m_originalCaster)->AI()->JustSummoned(pGameObj); -} - -void Spell::EffectProspecting(SpellEffectEntry const* /*effect*/) -{ - if (m_caster->GetTypeId() != TYPEID_PLAYER || !itemTarget) - return; - - Player* p_caster = (Player*)m_caster; - - if (sWorld.getConfig(CONFIG_BOOL_SKILL_PROSPECTING)) - { - uint32 SkillValue = p_caster->GetPureSkillValue(SKILL_JEWELCRAFTING); - uint32 reqSkillValue = itemTarget->GetProto()->RequiredSkillRank; - p_caster->UpdateGatherSkill(SKILL_JEWELCRAFTING, SkillValue, reqSkillValue); - } - - ((Player*)m_caster)->SendLoot(itemTarget->GetObjectGuid(), LOOT_PROSPECTING); -} - -void Spell::EffectMilling(SpellEffectEntry const* /*effect*/) -{ - if (m_caster->GetTypeId() != TYPEID_PLAYER || !itemTarget) - return; - - Player* p_caster = (Player*)m_caster; - - if (sWorld.getConfig(CONFIG_BOOL_SKILL_MILLING)) - { - uint32 SkillValue = p_caster->GetPureSkillValue(SKILL_INSCRIPTION); - uint32 reqSkillValue = itemTarget->GetProto()->RequiredSkillRank; - p_caster->UpdateGatherSkill(SKILL_INSCRIPTION, SkillValue, reqSkillValue); - } - - ((Player*)m_caster)->SendLoot(itemTarget->GetObjectGuid(), LOOT_MILLING); -} - -void Spell::EffectSkill(SpellEffectEntry const* /*effect*/) -{ - DEBUG_LOG("WORLD: SkillEFFECT"); -} - -void Spell::EffectSpiritHeal(SpellEffectEntry const* /*effect*/) -{ - // TODO player can't see the heal-animation - he should respawn some ticks later - if (!unitTarget || unitTarget->isAlive()) - return; - if (unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - if (!unitTarget->IsInWorld()) - return; - if (m_spellInfo->Id == 22012 && !unitTarget->HasAura(2584)) - return; - - ((Player*)unitTarget)->ResurrectPlayer(1.0f); - ((Player*)unitTarget)->SpawnCorpseBones(); -} - -// remove insignia spell effect -void Spell::EffectSkinPlayerCorpse(SpellEffectEntry const* /*effect*/) -{ - DEBUG_LOG("Effect: SkinPlayerCorpse"); - if ((m_caster->GetTypeId() != TYPEID_PLAYER) || (unitTarget->GetTypeId() != TYPEID_PLAYER) || (unitTarget->isAlive())) - return; - - ((Player*)unitTarget)->RemovedInsignia((Player*)m_caster); -} - -void Spell::EffectStealBeneficialBuff(SpellEffectEntry const* effect) -{ - DEBUG_LOG("Effect: StealBeneficialBuff"); - - if (!unitTarget || unitTarget == m_caster) // can't steal from self - return; - - typedef std::vector StealList; - StealList steal_list; - // Create dispel mask by dispel type - uint32 dispelMask = GetDispellMask( DispelType(effect->EffectMiscValue) ); - Unit::SpellAuraHolderMap const& auras = unitTarget->GetSpellAuraHolderMap(); - for (Unit::SpellAuraHolderMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) - { - SpellAuraHolder *holder = itr->second; - if (holder && (1<GetSpellProto()->GetDispel()) & dispelMask) - { - // Need check for passive? this - if (holder->IsPositive() && !holder->IsPassive() && !holder->GetSpellProto()->HasAttribute(SPELL_ATTR_EX4_NOT_STEALABLE)) - steal_list.push_back(holder); - } - } - // Ok if exist some buffs for dispel try dispel it - if (!steal_list.empty()) - { - typedef std::list < std::pair > SuccessList; - SuccessList success_list; - int32 list_size = steal_list.size(); - // Dispell N = damage buffs (or while exist buffs for dispel) - for (int32 count = 0; count < damage && list_size > 0; ++count) - { - // Random select buff for dispel - SpellAuraHolder* holder = steal_list[urand(0, list_size - 1)]; - // Not use chance for steal - // TODO possible need do it - success_list.push_back(SuccessList::value_type(holder->GetId(), holder->GetCasterGuid())); - - // Remove buff from list for prevent doubles - for (StealList::iterator j = steal_list.begin(); j != steal_list.end();) - { - SpellAuraHolder* stealed = *j; - if (stealed->GetId() == holder->GetId() && stealed->GetCasterGuid() == holder->GetCasterGuid()) - { - j = steal_list.erase(j); - --list_size; - } - else - ++j; - } - } - // Really try steal and send log - if (!success_list.empty()) - { - int32 count = success_list.size(); - WorldPacket data(SMSG_SPELLSTEALLOG, 8 + 8 + 4 + 1 + 4 + count * 5); - data << unitTarget->GetPackGUID(); // Victim GUID - data << m_caster->GetPackGUID(); // Caster GUID - data << uint32(m_spellInfo->Id); // Dispell spell id - data << uint8(0); // not used - data << uint32(count); // count - for (SuccessList::iterator j = success_list.begin(); j != success_list.end(); ++j) - { - SpellEntry const* spellInfo = sSpellStore.LookupEntry(j->first); - data << uint32(spellInfo->Id); // Spell Id - data << uint8(0); // 0 - steals !=0 transfers - unitTarget->RemoveAurasDueToSpellBySteal(spellInfo->Id, j->second, m_caster); - } - m_caster->SendMessageToSet(&data, true); - } - } -} - -void Spell::EffectWMODamage(SpellEffectEntry const* effect) -{ - DEBUG_LOG("Effect: WMODamage"); - - if (!gameObjTarget || gameObjTarget->GetGoType() != GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING) - { - sLog.outError("Spell::EffectWMODamage called without valid targets. Spell Id %u", m_spellInfo->Id); - return; - } - - if (!gameObjTarget->GetHealth()) - return; - - Unit* caster = GetAffectiveCaster(); - if (!caster) - return; - - DEBUG_LOG("Spell::EffectWMODamage, spell Id %u, go entry %u, damage %u", m_spellInfo->Id, gameObjTarget->GetEntry(), uint32(damage)); - gameObjTarget->DealGameObjectDamage(uint32(damage), m_spellInfo->Id, caster); -} - -void Spell::EffectWMORepair(SpellEffectEntry const* effect) -{ - DEBUG_LOG("Effect: WMORepair"); - - if (!gameObjTarget || gameObjTarget->GetGoType() != GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING) - { - sLog.outError("Spell::EffectWMORepair called without valid targets. Spell Id %u", m_spellInfo->Id); - return; - } - - Unit* caster = GetAffectiveCaster(); - if (!caster) - return; - - DEBUG_LOG("Spell::EffectWMORepair, spell Id %u, go entry %u", m_spellInfo->Id, gameObjTarget->GetEntry()); - gameObjTarget->RebuildGameObject(m_spellInfo->Id, caster); -} - -void Spell::EffectWMOChange(SpellEffectEntry const* effect) -{ - DEBUG_LOG("Effect: WMOChange"); - - if (!gameObjTarget || gameObjTarget->GetGoType() != GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING) - { - sLog.outError("Spell::EffectWMOChange called without valid targets. Spell Id %u", m_spellInfo->Id); - return; - } - - DEBUG_LOG("Spell::EffectWMOChange, spell Id %u, object %u, misc-value %u", m_spellInfo->Id, gameObjTarget->GetEntry(), effect->EffectMiscValue); - - Unit* caster = GetAffectiveCaster(); - if (!caster) - return; - - switch (effect->EffectMiscValue) - { - case 0: // Set to full health - gameObjTarget->ForceGameObjectHealth(gameObjTarget->GetMaxHealth(), caster); - break; - case 1: // Set to damaged - gameObjTarget->ForceGameObjectHealth(gameObjTarget->GetGOInfo()->destructibleBuilding.damagedNumHits, caster); - break; - case 2: // Set to destroyed - gameObjTarget->ForceGameObjectHealth(-int32(gameObjTarget->GetHealth()), caster); - break; - case 3: // Set to rebuilding - gameObjTarget->ForceGameObjectHealth(0, caster); - break; - default: - sLog.outError("Spell::EffectWMOChange, spell Id %u with undefined change value %u", m_spellInfo->Id, effect->EffectMiscValue); - break; - } -} - -void Spell::EffectKillCreditPersonal(SpellEffectEntry const* effect) -{ - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - ((Player*)unitTarget)->KilledMonsterCredit(effect->EffectMiscValue); -} - -void Spell::EffectKillCreditGroup(SpellEffectEntry const* effect) -{ - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - ((Player*)unitTarget)->RewardPlayerAndGroupAtEvent(effect->EffectMiscValue, unitTarget); -} - -void Spell::EffectQuestFail(SpellEffectEntry const* effect) -{ - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - ((Player*)unitTarget)->FailQuest(effect->EffectMiscValue); -} - -void Spell::EffectActivateRune(SpellEffectEntry const* effect) -{ - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - Player* plr = (Player*)m_caster; - - if (plr->getClass() != CLASS_DEATH_KNIGHT) - return; - - int32 count = damage; // max amount of reset runes - plr->ResyncRunes(); -} - -void Spell::EffectTitanGrip(SpellEffectEntry const* effect) -{ - // Make sure "Titan's Grip" (49152) penalty spell does not silently change - if (effect->EffectMiscValue != 49152) - sLog.outError("Spell::EffectTitanGrip: Spell %u has unexpected EffectMiscValue '%u'", m_spellInfo->Id, effect->EffectMiscValue); - if (unitTarget && unitTarget->GetTypeId() == TYPEID_PLAYER) - { - Player* plr = (Player*)m_caster; - plr->SetCanTitanGrip(true); - if (plr->HasTwoHandWeaponInOneHand() && !plr->HasAura(49152)) - plr->CastSpell(plr, 49152, true); - } -} - -void Spell::EffectRenamePet(SpellEffectEntry const* /*effect*/) -{ - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT || - !((Creature*)unitTarget)->IsPet() || ((Pet*)unitTarget)->getPetType() != HUNTER_PET) - return; - - unitTarget->RemoveByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED); -} - -void Spell::EffectPlaySound(SpellEffectEntry const* effect) -{ - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - uint32 soundId = effect->EffectMiscValue; - if (!sSoundEntriesStore.LookupEntry(soundId)) - { - sLog.outError("EffectPlaySound: Sound (Id: %u) in spell %u does not exist.", soundId, m_spellInfo->Id); - return; - } - - unitTarget->PlayDirectSound(soundId, (Player*)unitTarget); -} - -void Spell::EffectPlayMusic(SpellEffectEntry const* effect) -{ - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - uint32 soundId = effect->EffectMiscValue; - if (!sSoundEntriesStore.LookupEntry(soundId)) - { - sLog.outError("EffectPlayMusic: Sound (Id: %u) in spell %u does not exist.", soundId, m_spellInfo->Id); - return; - } - - WorldPacket data(SMSG_PLAY_MUSIC, 4); - data << uint32(soundId); - data << ObjectGuid(); - ((Player*)unitTarget)->GetSession()->SendPacket(&data); -} - -void Spell::EffectSpecCount(SpellEffectEntry const* /*effect*/) -{ - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - ((Player*)unitTarget)->UpdateSpecCount(damage); -} - -void Spell::EffectActivateSpec(SpellEffectEntry const* /*effect*/) -{ - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - uint32 spec = damage - 1; - - ((Player*)unitTarget)->ActivateSpec(spec); -} - -void Spell::EffectBind(SpellEffectEntry const* effect) -{ - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - Player* player = (Player*)unitTarget; - - uint32 area_id = uint32(effect->EffectMiscValue); - WorldLocation loc; - if (effect->EffectImplicitTargetA == TARGET_TABLE_X_Y_Z_COORDINATES || - effect->EffectImplicitTargetB == TARGET_TABLE_X_Y_Z_COORDINATES) - { - SpellTargetPosition const* st = sSpellMgr.GetSpellTargetPosition(m_spellInfo->Id); - if (!st) - { - sLog.outError("Spell::EffectBind - unknown Teleport coordinates for spell ID %u", m_spellInfo->Id); - return; - } - - loc.mapid = st->target_mapId; - loc.coord_x = st->target_X; - loc.coord_y = st->target_Y; - loc.coord_z = st->target_Z; - loc.orientation = st->target_Orientation; - if (!area_id) - area_id = sTerrainMgr.GetAreaId(loc.mapid, loc.coord_x, loc.coord_y, loc.coord_z); - } - else - { - player->GetPosition(loc); - if (!area_id) - area_id = player->GetAreaId(); - } - - player->SetHomebindToLocation(loc, area_id); - - // binding - WorldPacket data(SMSG_BINDPOINTUPDATE, (4 + 4 + 4 + 4 + 4)); - data << float(loc.coord_x); - data << float(loc.coord_y); - data << float(loc.coord_z); - data << uint32(loc.mapid); - data << uint32(area_id); - player->SendDirectMessage(&data); - - DEBUG_LOG("New Home Position for %s: XYZ: %f %f %f on Map %u", player->GetGuidStr().c_str(), loc.coord_x, loc.coord_y, loc.coord_z, loc.mapid); - - // zone update - data.Initialize(SMSG_PLAYERBOUND, 8 + 4); - data << m_caster->GetObjectGuid(); - data << uint32(area_id); - player->SendDirectMessage(&data); -} - -void Spell::EffectRestoreItemCharges(SpellEffectEntry const* effect) -{ - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - Player* player = (Player*)unitTarget; - - ItemPrototype const* itemProto = ObjectMgr::GetItemPrototype(effect->EffectItemType); - if (!itemProto) - return; - - // In case item from limited category recharge any from category, is this valid checked early in spell checks - Item* item; - if (itemProto->ItemLimitCategory) - item = ((Player*)unitTarget)->GetItemByLimitedCategory(itemProto->ItemLimitCategory); - else - item = player->GetItemByEntry(effect->EffectItemType); - - if (!item) - return; - - item->RestoreCharges(); -} - -void Spell::EffectRedirectThreat(SpellEffectEntry const* effect) -{ - if (!unitTarget) - return; - - if (m_spellInfo->Id == 59665) // Vigilance - if (Aura* glyph = unitTarget->GetDummyAura(63326)) // Glyph of Vigilance - damage += glyph->GetModifier()->m_amount; - - m_caster->getHostileRefManager().SetThreatRedirection(unitTarget->GetObjectGuid(), uint32(damage)); -} - -void Spell::EffectTeachTaxiNode(SpellEffectEntry const* effect) -{ - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - Player* player = (Player*)unitTarget; - - uint32 taxiNodeId = effect->EffectMiscValue; - if (!sTaxiNodesStore.LookupEntry(taxiNodeId)) - return; - - if (player->m_taxi.SetTaximaskNode(taxiNodeId)) - { - WorldPacket data(SMSG_NEW_TAXI_PATH, 0); - player->SendDirectMessage(&data); - - data.Initialize(SMSG_TAXINODE_STATUS, 9); - data << m_caster->GetObjectGuid(); - data << uint8(1); - player->SendDirectMessage(&data); - } -} - -void Spell::EffectQuestOffer(SpellEffectEntry const* effect) -{ - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - if (Quest const* quest = sObjectMgr.GetQuestTemplate(effect->EffectMiscValue)) - { - Player* player = (Player*)unitTarget; - - if (player->CanTakeQuest(quest, false)) - player->PlayerTalkClass->SendQuestGiverQuestDetails(quest, player->GetObjectGuid(), true); - } -} - -void Spell::EffectCancelAura(SpellEffectEntry const* effect) -{ - if (!unitTarget) - return; - - uint32 spellId = effect->EffectTriggerSpell; - - if (!sSpellStore.LookupEntry(spellId)) - { - sLog.outError("Spell::EffectCancelAura: spell %u doesn't exist", spellId); - return; - } - - unitTarget->RemoveAurasDueToSpell(spellId); -} - -void Spell::EffectKnockBackFromPosition(SpellEffectEntry const* effect) -{ - if (!unitTarget) - return; - - float x, y, z; - if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) - m_targets.getDestination(x, y, z); - else - m_caster->GetPosition(x, y, z); - - float angle = unitTarget->GetAngle(x, y) + M_PI_F; - float horizontalSpeed = effect->EffectMiscValue * 0.1f; - float verticalSpeed = damage * 0.1f; - unitTarget->KnockBackWithAngle(angle, horizontalSpeed, verticalSpeed); -} diff --git a/src/game/pchdef.h b/src/game/pchdef.h index 34e0fc856..743d097cf 100644 --- a/src/game/pchdef.h +++ b/src/game/pchdef.h @@ -1,3 +1,27 @@ +/** + * MaNGOS is a full featured server for World of Warcraft, supporting + * the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8 + * + * Copyright (C) 2005-2015 MaNGOS project + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + // add here most rarely modified headers to speed up debug build compilation #include "WorldSocket.h" // must be first to make ACE happy with ACE includes in it #include "Common.h"