mirror of
https://github.com/mangosfour/server.git
synced 2025-12-15 19:37:02 +00:00
[c12562] Cleanup Style
This commit is contained in:
parent
d988eb4038
commit
bf5c6b1ddd
19 changed files with 3432 additions and 1071 deletions
|
|
@ -15,3 +15,710 @@
|
|||
* 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();
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -39,7 +39,7 @@ bool CreatureEventAIHolder::UpdateRepeatTimer(Creature* creature, uint32 repeatM
|
|||
Time = urand(repeatMin, repeatMax);
|
||||
else
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", creature->GetEntry(), Event.event_id, Event.event_type);
|
||||
sLog.outErrorEventAI("Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", creature->GetEntry(), Event.event_id, Event.event_type);
|
||||
Enabled = false;
|
||||
return false;
|
||||
}
|
||||
|
|
@ -57,10 +57,17 @@ int CreatureEventAI::Permissible(const Creature* creature)
|
|||
void CreatureEventAI::GetAIInformation(ChatHandler& reader)
|
||||
{
|
||||
reader.PSendSysMessage(LANG_NPC_EVENTAI_PHASE, (uint32)m_Phase);
|
||||
reader.PSendSysMessage(LANG_NPC_EVENTAI_MOVE, reader.GetOnOffStr(m_CombatMovementEnabled));
|
||||
reader.PSendSysMessage(LANG_NPC_EVENTAI_MOVE, reader.GetOnOffStr(m_isCombatMovement));
|
||||
reader.PSendSysMessage(LANG_NPC_EVENTAI_COMBAT, reader.GetOnOffStr(m_MeleeEnabled));
|
||||
}
|
||||
|
||||
// For Non Dungeon map only allow non-difficulty flags or EFLAG_DIFFICULTY_0 mode
|
||||
inline bool IsEventFlagsFitForNormalMap(uint8 eFlags)
|
||||
{
|
||||
return !(eFlags & (EFLAG_DIFFICULTY_0 | EFLAG_DIFFICULTY_1 | EFLAG_DIFFICULTY_2 | EFLAG_DIFFICULTY_3)) ||
|
||||
(eFlags & EFLAG_DIFFICULTY_0);
|
||||
}
|
||||
|
||||
CreatureEventAI::CreatureEventAI(Creature* c) : CreatureAI(c)
|
||||
{
|
||||
// Need make copy for filter unneeded steps and safe in case table reload
|
||||
|
|
@ -68,21 +75,21 @@ CreatureEventAI::CreatureEventAI(Creature* c) : CreatureAI(c)
|
|||
if (creatureEventsItr != sEventAIMgr.GetCreatureEventAIMap().end())
|
||||
{
|
||||
uint32 events_count = 0;
|
||||
for (CreatureEventAI_Event_Vec::const_iterator i = (*creatureEventsItr).second.begin(); i != (*creatureEventsItr).second.end(); ++i)
|
||||
for (CreatureEventAI_Event_Vec::const_iterator i = creatureEventsItr->second.begin(); i != creatureEventsItr->second.end(); ++i)
|
||||
{
|
||||
// Debug check
|
||||
#ifndef MANGOS_DEBUG
|
||||
if ((*i).event_flags & EFLAG_DEBUG_ONLY)
|
||||
if (i->event_flags & EFLAG_DEBUG_ONLY)
|
||||
continue;
|
||||
#endif
|
||||
if (m_creature->GetMap()->IsDungeon())
|
||||
{
|
||||
if ((1 << (m_creature->GetMap()->GetSpawnMode() + 1)) & (*i).event_flags)
|
||||
if ((1 << (m_creature->GetMap()->GetSpawnMode() + 1)) & i->event_flags)
|
||||
{
|
||||
++events_count;
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (IsEventFlagsFitForNormalMap(i->event_flags))
|
||||
++events_count;
|
||||
}
|
||||
// EventMap had events but they were not added because they must be for instance
|
||||
|
|
@ -91,47 +98,65 @@ CreatureEventAI::CreatureEventAI(Creature* c) : CreatureAI(c)
|
|||
else
|
||||
{
|
||||
m_CreatureEventAIList.reserve(events_count);
|
||||
for (CreatureEventAI_Event_Vec::const_iterator i = (*creatureEventsItr).second.begin(); i != (*creatureEventsItr).second.end(); ++i)
|
||||
for (CreatureEventAI_Event_Vec::const_iterator i = creatureEventsItr->second.begin(); i != creatureEventsItr->second.end(); ++i)
|
||||
{
|
||||
|
||||
// Debug check
|
||||
#ifndef MANGOS_DEBUG
|
||||
if ((*i).event_flags & EFLAG_DEBUG_ONLY)
|
||||
if (i->event_flags & EFLAG_DEBUG_ONLY)
|
||||
continue;
|
||||
#endif
|
||||
if (m_creature->GetMap()->IsDungeon())
|
||||
{
|
||||
if ((1 << (m_creature->GetMap()->GetSpawnMode() + 1)) & (*i).event_flags)
|
||||
if ((1 << (m_creature->GetMap()->GetSpawnMode() + 1)) & i->event_flags)
|
||||
{
|
||||
// event flagged for instance mode
|
||||
m_CreatureEventAIList.push_back(CreatureEventAIHolder(*i));
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (IsEventFlagsFitForNormalMap(i->event_flags))
|
||||
m_CreatureEventAIList.push_back(CreatureEventAIHolder(*i));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
sLog.outError("CreatureEventAI: EventMap for Creature %u is empty but creature is using CreatureEventAI.", m_creature->GetEntry());
|
||||
sLog.outErrorEventAI("EventMap for Creature %u is empty but creature is using CreatureEventAI.", m_creature->GetEntry());
|
||||
|
||||
m_bEmptyList = m_CreatureEventAIList.empty();
|
||||
m_Phase = 0;
|
||||
m_CombatMovementEnabled = true;
|
||||
m_MeleeEnabled = true;
|
||||
m_AttackDistance = 0.0f;
|
||||
m_AttackAngle = 0.0f;
|
||||
|
||||
m_InvinceabilityHpLevel = 0;
|
||||
|
||||
// Handle Spawned Events
|
||||
if (!m_bEmptyList)
|
||||
{
|
||||
for (CreatureEventAIList::iterator i = m_CreatureEventAIList.begin(); i != m_CreatureEventAIList.end(); ++i)
|
||||
if (SpawnedEventConditionsCheck((*i).Event))
|
||||
ProcessEvent(*i);
|
||||
// Handle Spawned Events, also calls Reset()
|
||||
JustRespawned();
|
||||
}
|
||||
|
||||
#define LOG_PROCESS_EVENT \
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_EVENT_AI_DEV, "CreatureEventAI: Event type %u (script %u) triggered for %s (invoked by %s)", \
|
||||
pHolder.Event.event_type, pHolder.Event.event_id, m_creature->GetGuidStr().c_str(), pActionInvoker ? pActionInvoker->GetGuidStr().c_str() : "<no invoker>")
|
||||
|
||||
inline bool IsTimerBasedEvent(EventAI_Type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case EVENT_T_TIMER_IN_COMBAT:
|
||||
case EVENT_T_TIMER_OOC:
|
||||
case EVENT_T_TIMER_GENERIC:
|
||||
case EVENT_T_MANA:
|
||||
case EVENT_T_HP:
|
||||
case EVENT_T_TARGET_HP:
|
||||
case EVENT_T_TARGET_CASTING:
|
||||
case EVENT_T_FRIENDLY_HP:
|
||||
case EVENT_T_AURA:
|
||||
case EVENT_T_TARGET_AURA:
|
||||
case EVENT_T_MISSING_AURA:
|
||||
case EVENT_T_TARGET_MISSING_AURA:
|
||||
case EVENT_T_RANGE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
Reset();
|
||||
}
|
||||
|
||||
bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pActionInvoker)
|
||||
|
|
@ -141,17 +166,25 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
|
|||
|
||||
// Check the inverse phase mask (event doesn't trigger if current phase bit is set in mask)
|
||||
if (pHolder.Event.event_inverse_phase_mask & (1 << m_Phase))
|
||||
{
|
||||
if (!IsTimerBasedEvent(pHolder.Event.event_type))
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_EVENT_AI_DEV, "CreatureEventAI: Event %u skipped because of phasemask %u. Current phase %u", pHolder.Event.event_id, pHolder.Event.event_inverse_phase_mask, m_Phase);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsTimerBasedEvent(pHolder.Event.event_type))
|
||||
LOG_PROCESS_EVENT;
|
||||
|
||||
CreatureEventAI_Event const& event = pHolder.Event;
|
||||
|
||||
// Check event conditions based on the event type, also reset events
|
||||
switch (event.event_type)
|
||||
{
|
||||
case EVENT_T_TIMER:
|
||||
case EVENT_T_TIMER_IN_COMBAT:
|
||||
if (!m_creature->isInCombat())
|
||||
return false;
|
||||
|
||||
LOG_PROCESS_EVENT;
|
||||
// Repeat Timers
|
||||
pHolder.UpdateRepeatTimer(m_creature, event.timer.repeatMin, event.timer.repeatMax);
|
||||
break;
|
||||
|
|
@ -159,6 +192,12 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
|
|||
if (m_creature->isInCombat() || m_creature->IsInEvadeMode())
|
||||
return false;
|
||||
|
||||
LOG_PROCESS_EVENT;
|
||||
// Repeat Timers
|
||||
pHolder.UpdateRepeatTimer(m_creature, event.timer.repeatMin, event.timer.repeatMax);
|
||||
break;
|
||||
case EVENT_T_TIMER_GENERIC:
|
||||
LOG_PROCESS_EVENT;
|
||||
// Repeat Timers
|
||||
pHolder.UpdateRepeatTimer(m_creature, event.timer.repeatMin, event.timer.repeatMax);
|
||||
break;
|
||||
|
|
@ -172,6 +211,7 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
|
|||
if (perc > event.percent_range.percentMax || perc < event.percent_range.percentMin)
|
||||
return false;
|
||||
|
||||
LOG_PROCESS_EVENT;
|
||||
// Repeat Timers
|
||||
pHolder.UpdateRepeatTimer(m_creature, event.percent_range.repeatMin, event.percent_range.repeatMax);
|
||||
break;
|
||||
|
|
@ -186,6 +226,7 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
|
|||
if (perc > event.percent_range.percentMax || perc < event.percent_range.percentMin)
|
||||
return false;
|
||||
|
||||
LOG_PROCESS_EVENT;
|
||||
// Repeat Timers
|
||||
pHolder.UpdateRepeatTimer(m_creature, event.percent_range.repeatMin, event.percent_range.repeatMax);
|
||||
break;
|
||||
|
|
@ -225,6 +266,7 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
|
|||
if (perc > event.percent_range.percentMax || perc < event.percent_range.percentMin)
|
||||
return false;
|
||||
|
||||
LOG_PROCESS_EVENT;
|
||||
// Repeat Timers
|
||||
pHolder.UpdateRepeatTimer(m_creature, event.percent_range.repeatMin, event.percent_range.repeatMax);
|
||||
break;
|
||||
|
|
@ -233,6 +275,7 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
|
|||
if (!m_creature->isInCombat() || !m_creature->getVictim() || !m_creature->getVictim()->IsNonMeleeSpellCasted(false, false, true))
|
||||
return false;
|
||||
|
||||
LOG_PROCESS_EVENT;
|
||||
// Repeat Timers
|
||||
pHolder.UpdateRepeatTimer(m_creature, event.target_casting.repeatMin, event.target_casting.repeatMax);
|
||||
break;
|
||||
|
|
@ -247,6 +290,7 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
|
|||
|
||||
pActionInvoker = pUnit;
|
||||
|
||||
LOG_PROCESS_EVENT;
|
||||
// Repeat Timers
|
||||
pHolder.UpdateRepeatTimer(m_creature, event.friendly_hp.repeatMin, event.friendly_hp.repeatMax);
|
||||
break;
|
||||
|
|
@ -325,6 +369,7 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
|
|||
if (!holder || holder->GetStackAmount() < event.buffed.amount)
|
||||
return false;
|
||||
|
||||
LOG_PROCESS_EVENT;
|
||||
// Repeat Timers
|
||||
pHolder.UpdateRepeatTimer(m_creature, event.buffed.repeatMin, event.buffed.repeatMax);
|
||||
break;
|
||||
|
|
@ -338,6 +383,7 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
|
|||
if (!holder || holder->GetStackAmount() < event.buffed.amount)
|
||||
return false;
|
||||
|
||||
LOG_PROCESS_EVENT;
|
||||
// Repeat Timers
|
||||
pHolder.UpdateRepeatTimer(m_creature, event.buffed.repeatMin, event.buffed.repeatMax);
|
||||
break;
|
||||
|
|
@ -348,6 +394,7 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
|
|||
if (holder && holder->GetStackAmount() >= event.buffed.amount)
|
||||
return false;
|
||||
|
||||
LOG_PROCESS_EVENT;
|
||||
// Repeat Timers
|
||||
pHolder.UpdateRepeatTimer(m_creature, event.buffed.repeatMin, event.buffed.repeatMax);
|
||||
break;
|
||||
|
|
@ -361,12 +408,13 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
|
|||
if (holder && holder->GetStackAmount() >= event.buffed.amount)
|
||||
return false;
|
||||
|
||||
LOG_PROCESS_EVENT;
|
||||
// Repeat Timers
|
||||
pHolder.UpdateRepeatTimer(m_creature, event.buffed.repeatMin, event.buffed.repeatMax);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u has invalid Event Type(%u), missing from ProcessEvent() Switch.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
|
||||
sLog.outErrorEventAI("Creature %u using Event %u has invalid Event Type(%u), missing from ProcessEvent() Switch.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -421,6 +469,12 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
|
|||
|
||||
void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32 rnd, uint32 EventId, Unit* pActionInvoker)
|
||||
{
|
||||
if (action.type == ACTION_T_NONE)
|
||||
return;
|
||||
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_EVENT_AI_DEV, "CreatureEventAI: Process action %u (script %u) triggered for %s (invoked by %s)",
|
||||
action.type, EventId, m_creature->GetGuidStr().c_str(), pActionInvoker ? pActionInvoker->GetGuidStr().c_str() : "<no invoker>");
|
||||
|
||||
switch (action.type)
|
||||
{
|
||||
case ACTION_T_TEXT:
|
||||
|
|
@ -527,8 +581,15 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
|
|||
}
|
||||
case ACTION_T_CAST:
|
||||
{
|
||||
Unit* target = GetTargetByType(action.cast.target, pActionInvoker);
|
||||
uint32 selectFlags = 0;
|
||||
uint32 spellId = 0;
|
||||
if (!(action.cast.castFlags & (CAST_TRIGGERED | CAST_FORCE_CAST | CAST_FORCE_TARGET_SELF)))
|
||||
{
|
||||
spellId = action.cast.spellId;
|
||||
selectFlags = SELECT_FLAG_IN_LOS;
|
||||
}
|
||||
|
||||
Unit* target = GetTargetByType(action.cast.target, pActionInvoker, spellId, selectFlags);
|
||||
if (!target)
|
||||
{
|
||||
sLog.outDebug("CreatureEventAI: NULL target for ACTION_T_CAST creature entry %u casting spell id %u", m_creature->GetEntry(), action.cast.spellId);
|
||||
|
|
@ -549,11 +610,11 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
|
|||
{
|
||||
case CHASE_MOTION_TYPE:
|
||||
case FOLLOW_MOTION_TYPE:
|
||||
m_AttackDistance = 0.0f;
|
||||
m_AttackAngle = 0.0f;
|
||||
m_attackDistance = 0.0f;
|
||||
m_attackAngle = 0.0f;
|
||||
|
||||
m_creature->GetMotionMaster()->Clear(false);
|
||||
m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim(), m_AttackDistance, m_AttackAngle);
|
||||
m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim(), m_attackDistance, m_attackAngle);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -574,12 +635,12 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
|
|||
Creature* pCreature = NULL;
|
||||
|
||||
if (action.summon.duration)
|
||||
pCreature = m_creature->SummonCreature(action.summon.creatureId, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, action.summon.duration);
|
||||
pCreature = m_creature->SummonCreature(action.summon.creatureId, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, action.summon.duration);
|
||||
else
|
||||
pCreature = m_creature->SummonCreature(action.summon.creatureId, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 0);
|
||||
pCreature = m_creature->SummonCreature(action.summon.creatureId, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 0);
|
||||
|
||||
if (!pCreature)
|
||||
sLog.outErrorDb("CreatureEventAI: failed to spawn creature %u. Spawn event %d is on creature %d", action.summon.creatureId, EventId, m_creature->GetEntry());
|
||||
sLog.outErrorEventAI("failed to spawn creature %u. Spawn event %d is on creature %d", action.summon.creatureId, EventId, m_creature->GetEntry());
|
||||
else if (action.summon.target != TARGET_T_SELF && target)
|
||||
pCreature->AI()->AttackStart(target);
|
||||
break;
|
||||
|
|
@ -602,7 +663,7 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
|
|||
((Player*)target)->AreaExploredOrEventHappens(action.quest_event.questId);
|
||||
break;
|
||||
case ACTION_T_CAST_EVENT:
|
||||
if (Unit* target = GetTargetByType(action.cast_event.target, pActionInvoker))
|
||||
if (Unit* target = GetTargetByType(action.cast_event.target, pActionInvoker, 0, SELECT_FLAG_PLAYER))
|
||||
if (target->GetTypeId() == TYPEID_PLAYER)
|
||||
((Player*)target)->CastedCreatureOrGO(action.cast_event.creatureId, m_creature->GetObjectGuid(), action.cast_event.spellId);
|
||||
break;
|
||||
|
|
@ -632,57 +693,37 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
|
|||
break;
|
||||
case ACTION_T_COMBAT_MOVEMENT:
|
||||
// ignore no affect case
|
||||
if (m_CombatMovementEnabled == (action.combat_movement.state != 0))
|
||||
if (m_isCombatMovement == (action.combat_movement.state != 0))
|
||||
return;
|
||||
|
||||
m_CombatMovementEnabled = action.combat_movement.state != 0;
|
||||
SetCombatMovement(action.combat_movement.state != 0, true);
|
||||
|
||||
// Allow movement (create new targeted movement gen only if idle)
|
||||
if (m_CombatMovementEnabled)
|
||||
{
|
||||
if (action.combat_movement.melee && m_creature->isInCombat())
|
||||
if (Unit* victim = m_creature->getVictim())
|
||||
m_creature->SendMeleeAttackStart(victim);
|
||||
|
||||
if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == IDLE_MOTION_TYPE)
|
||||
{
|
||||
m_creature->GetMotionMaster()->Clear(false);
|
||||
m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim(), m_AttackDistance, m_AttackAngle);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (action.combat_movement.melee && m_creature->isInCombat())
|
||||
if (Unit* victim = m_creature->getVictim())
|
||||
m_creature->SendMeleeAttackStop(victim);
|
||||
|
||||
if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
|
||||
{
|
||||
m_creature->GetMotionMaster()->Clear(false);
|
||||
m_creature->GetMotionMaster()->MoveIdle();
|
||||
m_creature->StopMoving();
|
||||
}
|
||||
}
|
||||
if (m_isCombatMovement && action.combat_movement.melee && m_creature->isInCombat() && m_creature->getVictim())
|
||||
m_creature->SendMeleeAttackStart(m_creature->getVictim());
|
||||
else if (action.combat_movement.melee && m_creature->isInCombat() && m_creature->getVictim())
|
||||
m_creature->SendMeleeAttackStop(m_creature->getVictim());
|
||||
break;
|
||||
case ACTION_T_SET_PHASE:
|
||||
m_Phase = action.set_phase.phase;
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_EVENT_AI_DEV, "CreatureEventAI: ACTION_T_SET_PHASE - script %u for %s, phase is now %u", EventId, m_creature->GetGuidStr().c_str(), m_Phase);
|
||||
break;
|
||||
case ACTION_T_INC_PHASE:
|
||||
{
|
||||
int32 new_phase = int32(m_Phase) + action.set_inc_phase.step;
|
||||
if (new_phase < 0)
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Event %d decrease Phase under 0. CreatureEntry = %d", EventId, m_creature->GetEntry());
|
||||
sLog.outErrorEventAI("Event %d decrease Phase under 0. CreatureEntry = %d", EventId, m_creature->GetEntry());
|
||||
m_Phase = 0;
|
||||
}
|
||||
else if (new_phase >= MAX_PHASE)
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Event %d incremented Phase above %u. Phase mask cannot be used with phases past %u. CreatureEntry = %d", EventId, MAX_PHASE - 1, MAX_PHASE - 1, m_creature->GetEntry());
|
||||
sLog.outErrorEventAI("Event %d incremented Phase above %u. Phase mask cannot be used with phases past %u. CreatureEntry = %d", EventId, MAX_PHASE - 1, MAX_PHASE - 1, m_creature->GetEntry());
|
||||
m_Phase = MAX_PHASE - 1;
|
||||
}
|
||||
else
|
||||
m_Phase = new_phase;
|
||||
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_EVENT_AI_DEV, "CreatureEventAI: ACTION_T_INC_PHASE - script %u for %s, phase is now %u", EventId, m_creature->GetGuidStr().c_str(), m_Phase);
|
||||
break;
|
||||
}
|
||||
case ACTION_T_EVADE:
|
||||
|
|
@ -708,27 +749,28 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
|
|||
target->RemoveAurasDueToSpell(action.remove_aura.spellId);
|
||||
break;
|
||||
case ACTION_T_RANGED_MOVEMENT:
|
||||
m_AttackDistance = (float)action.ranged_movement.distance;
|
||||
m_AttackAngle = action.ranged_movement.angle / 180.0f * M_PI_F;
|
||||
m_attackDistance = (float)action.ranged_movement.distance;
|
||||
m_attackAngle = action.ranged_movement.angle / 180.0f * M_PI_F;
|
||||
|
||||
if (m_CombatMovementEnabled)
|
||||
if (m_isCombatMovement)
|
||||
{
|
||||
if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
|
||||
{
|
||||
// Drop current movement gen
|
||||
m_creature->GetMotionMaster()->Clear(false);
|
||||
m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim(), m_AttackDistance, m_AttackAngle);
|
||||
m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim(), m_attackDistance, m_attackAngle);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ACTION_T_RANDOM_PHASE:
|
||||
m_Phase = GetRandActionParam(rnd, action.random_phase.phase1, action.random_phase.phase2, action.random_phase.phase3);
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_EVENT_AI_DEV, "CreatureEventAI: ACTION_T_RANDOM_PHASE - script %u for %s, phase is now %u", EventId, m_creature->GetGuidStr().c_str(), m_Phase);
|
||||
break;
|
||||
case ACTION_T_RANDOM_PHASE_RANGE:
|
||||
if (action.random_phase_range.phaseMax > action.random_phase_range.phaseMin)
|
||||
m_Phase = action.random_phase_range.phaseMin + (rnd % (action.random_phase_range.phaseMax - action.random_phase_range.phaseMin));
|
||||
else
|
||||
sLog.outErrorDb("CreatureEventAI: ACTION_T_RANDOM_PHASE_RANGE cannot have Param2 <= Param1. Divide by Zero. Event = %d. CreatureEntry = %d", EventId, m_creature->GetEntry());
|
||||
sLog.outErrorEventAI("ACTION_T_RANDOM_PHASE_RANGE cannot have Param2 <= Param1. Divide by Zero. Event = %d. CreatureEntry = %d", EventId, m_creature->GetEntry());
|
||||
break;
|
||||
case ACTION_T_SUMMON_ID:
|
||||
{
|
||||
|
|
@ -737,18 +779,18 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
|
|||
CreatureEventAI_Summon_Map::const_iterator i = sEventAIMgr.GetCreatureEventAISummonMap().find(action.summon_id.spawnId);
|
||||
if (i == sEventAIMgr.GetCreatureEventAISummonMap().end())
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: failed to spawn creature %u. Summon map index %u does not exist. EventID %d. CreatureID %d", action.summon_id.creatureId, action.summon_id.spawnId, EventId, m_creature->GetEntry());
|
||||
sLog.outErrorEventAI("failed to spawn creature %u. Summon map index %u does not exist. EventID %d. CreatureID %d", action.summon_id.creatureId, action.summon_id.spawnId, EventId, m_creature->GetEntry());
|
||||
return;
|
||||
}
|
||||
|
||||
Creature* pCreature = NULL;
|
||||
if ((*i).second.SpawnTimeSecs)
|
||||
pCreature = m_creature->SummonCreature(action.summon_id.creatureId, (*i).second.position_x, (*i).second.position_y, (*i).second.position_z, (*i).second.orientation, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, (*i).second.SpawnTimeSecs);
|
||||
pCreature = m_creature->SummonCreature(action.summon_id.creatureId, (*i).second.position_x, (*i).second.position_y, (*i).second.position_z, (*i).second.orientation, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, (*i).second.SpawnTimeSecs);
|
||||
else
|
||||
pCreature = m_creature->SummonCreature(action.summon_id.creatureId, (*i).second.position_x, (*i).second.position_y, (*i).second.position_z, (*i).second.orientation, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 0);
|
||||
pCreature = m_creature->SummonCreature(action.summon_id.creatureId, (*i).second.position_x, (*i).second.position_y, (*i).second.position_z, (*i).second.orientation, TEMPSUMMON_TIMED_OOC_DESPAWN, 0);
|
||||
|
||||
if (!pCreature)
|
||||
sLog.outErrorDb("CreatureEventAI: failed to spawn creature %u. EventId %d.Creature %d", action.summon_id.creatureId, EventId, m_creature->GetEntry());
|
||||
sLog.outErrorEventAI("failed to spawn creature %u. EventId %d.Creature %d", action.summon_id.creatureId, EventId, m_creature->GetEntry());
|
||||
else if (action.summon_id.target != TARGET_T_SELF && target)
|
||||
pCreature->AI()->AttackStart(target);
|
||||
|
||||
|
|
@ -761,7 +803,7 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
|
|||
else
|
||||
{
|
||||
// if not available, use pActionInvoker
|
||||
if (Unit* pTarget = GetTargetByType(action.killed_monster.target, pActionInvoker))
|
||||
if (Unit* pTarget = GetTargetByType(action.killed_monster.target, pActionInvoker, 0, SELECT_FLAG_PLAYER))
|
||||
if (Player* pPlayer2 = pTarget->GetCharmerOrOwnerPlayerOrPlayerItself())
|
||||
pPlayer2->RewardPlayerAndGroupAtEvent(action.killed_monster.creatureId, m_creature);
|
||||
}
|
||||
|
|
@ -771,7 +813,7 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
|
|||
InstanceData* pInst = m_creature->GetInstanceData();
|
||||
if (!pInst)
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Event %d attempt to set instance data without instance script. Creature %d", EventId, m_creature->GetEntry());
|
||||
sLog.outErrorEventAI("Event %d attempt to set instance data without instance script. Creature %d", EventId, m_creature->GetEntry());
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -783,14 +825,14 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
|
|||
Unit* target = GetTargetByType(action.set_inst_data64.target, pActionInvoker);
|
||||
if (!target)
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Event %d attempt to set instance data64 but Target == NULL. Creature %d", EventId, m_creature->GetEntry());
|
||||
sLog.outErrorEventAI("Event %d attempt to set instance data64 but Target == NULL. Creature %d", EventId, m_creature->GetEntry());
|
||||
return;
|
||||
}
|
||||
|
||||
InstanceData* pInst = m_creature->GetInstanceData();
|
||||
if (!pInst)
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Event %d attempt to set instance data64 without instance script. Creature %d", EventId, m_creature->GetEntry());
|
||||
sLog.outErrorEventAI("Event %d attempt to set instance data64 without instance script. Creature %d", EventId, m_creature->GetEntry());
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -801,7 +843,7 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
|
|||
if (m_creature->GetEntry() == action.update_template.creatureId)
|
||||
{
|
||||
|
||||
sLog.outErrorDb("CreatureEventAI: Event %d ACTION_T_UPDATE_TEMPLATE call with param1 == current entry. Creature %d", EventId, m_creature->GetEntry());
|
||||
sLog.outErrorEventAI("Event %d ACTION_T_UPDATE_TEMPLATE call with param1 == current entry. Creature %d", EventId, m_creature->GetEntry());
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -811,7 +853,7 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
|
|||
if (m_creature->isDead())
|
||||
{
|
||||
|
||||
sLog.outErrorDb("CreatureEventAI: Event %d ACTION_T_DIE on dead creature. Creature %d", EventId, m_creature->GetEntry());
|
||||
sLog.outErrorEventAI("Event %d ACTION_T_DIE on dead creature. Creature %d", EventId, m_creature->GetEntry());
|
||||
return;
|
||||
}
|
||||
m_creature->DealDamage(m_creature, m_creature->GetMaxHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
|
||||
|
|
@ -869,18 +911,26 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
|
|||
}
|
||||
}
|
||||
|
||||
void CreatureEventAI::JustRespawned()
|
||||
void CreatureEventAI::JustRespawned() // NOTE that this is called from the AI's constructor as well
|
||||
{
|
||||
Reset();
|
||||
|
||||
if (m_bEmptyList)
|
||||
return;
|
||||
|
||||
// Handle Spawned Events
|
||||
for (CreatureEventAIList::iterator i = m_CreatureEventAIList.begin(); i != m_CreatureEventAIList.end(); ++i)
|
||||
if (SpawnedEventConditionsCheck((*i).Event))
|
||||
{
|
||||
// Reset generic timer
|
||||
if (i->Event.event_type == EVENT_T_TIMER_GENERIC)
|
||||
{
|
||||
if (i->UpdateRepeatTimer(m_creature, i->Event.timer.initialMin, i->Event.timer.initialMax))
|
||||
i->Enabled = true;
|
||||
}
|
||||
// Handle Spawned Events
|
||||
else if (SpawnedEventConditionsCheck((*i).Event))
|
||||
ProcessEvent(*i);
|
||||
}
|
||||
}
|
||||
|
||||
void CreatureEventAI::Reset()
|
||||
{
|
||||
|
|
@ -928,7 +978,7 @@ void CreatureEventAI::JustReachedHome()
|
|||
|
||||
void CreatureEventAI::EnterEvadeMode()
|
||||
{
|
||||
m_creature->RemoveAllAuras();
|
||||
m_creature->RemoveAllAurasOnEvade();
|
||||
m_creature->DeleteThreatList();
|
||||
m_creature->CombatStop(true);
|
||||
|
||||
|
|
@ -1036,7 +1086,7 @@ void CreatureEventAI::EnterCombat(Unit* enemy)
|
|||
ProcessEvent(*i, enemy);
|
||||
break;
|
||||
// Reset all in combat timers
|
||||
case EVENT_T_TIMER:
|
||||
case EVENT_T_TIMER_IN_COMBAT:
|
||||
if ((*i).UpdateRepeatTimer(m_creature, event.timer.initialMin, event.timer.initialMax))
|
||||
(*i).Enabled = true;
|
||||
break;
|
||||
|
|
@ -1064,15 +1114,7 @@ void CreatureEventAI::AttackStart(Unit* who)
|
|||
m_creature->SetInCombatWith(who);
|
||||
who->SetInCombatWith(m_creature);
|
||||
|
||||
if (m_CombatMovementEnabled)
|
||||
{
|
||||
m_creature->GetMotionMaster()->MoveChase(who, m_AttackDistance, m_AttackAngle);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_creature->GetMotionMaster()->MoveIdle();
|
||||
m_creature->StopMoving();
|
||||
}
|
||||
HandleMovementOnAttackStart(who);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1177,9 +1219,10 @@ void CreatureEventAI::UpdateAI(const uint32 diff)
|
|||
switch ((*i).Event.event_type)
|
||||
{
|
||||
case EVENT_T_TIMER_OOC:
|
||||
case EVENT_T_TIMER_GENERIC:
|
||||
ProcessEvent(*i);
|
||||
break;
|
||||
case EVENT_T_TIMER:
|
||||
case EVENT_T_TIMER_IN_COMBAT:
|
||||
case EVENT_T_MANA:
|
||||
case EVENT_T_HP:
|
||||
case EVENT_T_TARGET_HP:
|
||||
|
|
@ -1246,7 +1289,7 @@ inline int32 CreatureEventAI::GetRandActionParam(uint32 rnd, int32 param1, int32
|
|||
return 0;
|
||||
}
|
||||
|
||||
inline Unit* CreatureEventAI::GetTargetByType(uint32 Target, Unit* pActionInvoker)
|
||||
inline Unit* CreatureEventAI::GetTargetByType(uint32 Target, Unit* pActionInvoker, uint32 forSpellId, uint32 selectFlags)
|
||||
{
|
||||
switch (Target)
|
||||
{
|
||||
|
|
@ -1255,15 +1298,21 @@ inline Unit* CreatureEventAI::GetTargetByType(uint32 Target, Unit* pActionInvoke
|
|||
case TARGET_T_HOSTILE:
|
||||
return m_creature->getVictim();
|
||||
case TARGET_T_HOSTILE_SECOND_AGGRO:
|
||||
return m_creature->SelectAttackingTarget(ATTACKING_TARGET_TOPAGGRO, 1);
|
||||
return m_creature->SelectAttackingTarget(ATTACKING_TARGET_TOPAGGRO, 1, forSpellId, selectFlags);
|
||||
case TARGET_T_HOSTILE_LAST_AGGRO:
|
||||
return m_creature->SelectAttackingTarget(ATTACKING_TARGET_BOTTOMAGGRO, 0);
|
||||
return m_creature->SelectAttackingTarget(ATTACKING_TARGET_BOTTOMAGGRO, 0, forSpellId, selectFlags);
|
||||
case TARGET_T_HOSTILE_RANDOM:
|
||||
return m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0);
|
||||
return m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, forSpellId, selectFlags);
|
||||
case TARGET_T_HOSTILE_RANDOM_NOT_TOP:
|
||||
return m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1);
|
||||
return m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, forSpellId, selectFlags);
|
||||
case TARGET_T_HOSTILE_RANDOM_PLAYER:
|
||||
return m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, forSpellId, SELECT_FLAG_PLAYER | selectFlags);
|
||||
case TARGET_T_HOSTILE_RANDOM_NOT_TOP_PLAYER:
|
||||
return m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, forSpellId, SELECT_FLAG_PLAYER | selectFlags);
|
||||
case TARGET_T_ACTION_INVOKER:
|
||||
return pActionInvoker;
|
||||
case TARGET_T_ACTION_INVOKER_OWNER:
|
||||
return pActionInvoker ? pActionInvoker->GetCharmerOrOwnerOrSelf() : NULL;
|
||||
default:
|
||||
return NULL;
|
||||
};
|
||||
|
|
@ -1305,13 +1354,13 @@ void CreatureEventAI::DoScriptText(int32 textEntry, WorldObject* pSource, Unit*
|
|||
{
|
||||
if (!pSource)
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i, invalid Source pointer.", textEntry);
|
||||
sLog.outErrorEventAI("DoScriptText entry %i, invalid Source pointer.", textEntry);
|
||||
return;
|
||||
}
|
||||
|
||||
if (textEntry >= 0)
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: DoScriptText with source entry %u (TypeId=%u, guid=%u) attempts to process text entry %i, but text entry must be negative.", pSource->GetEntry(), pSource->GetTypeId(), pSource->GetGUIDLow(), textEntry);
|
||||
sLog.outErrorEventAI("DoScriptText with source entry %u (TypeId=%u, guid=%u) attempts to process text entry %i, but text entry must be negative.", pSource->GetEntry(), pSource->GetTypeId(), pSource->GetGUIDLow(), textEntry);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1319,7 +1368,7 @@ void CreatureEventAI::DoScriptText(int32 textEntry, WorldObject* pSource, Unit*
|
|||
|
||||
if (i == sEventAIMgr.GetCreatureEventAITextMap().end())
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: DoScriptText with source entry %u (TypeId=%u, guid=%u) could not find text entry %i.", pSource->GetEntry(), pSource->GetTypeId(), pSource->GetGUIDLow(), textEntry);
|
||||
sLog.outErrorEventAI("DoScriptText with source entry %u (TypeId=%u, guid=%u) could not find text entry %i.", pSource->GetEntry(), pSource->GetTypeId(), pSource->GetGUIDLow(), textEntry);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1330,7 +1379,7 @@ void CreatureEventAI::DoScriptText(int32 textEntry, WorldObject* pSource, Unit*
|
|||
if (GetSoundEntriesStore()->LookupEntry((*i).second.SoundId))
|
||||
pSource->PlayDirectSound((*i).second.SoundId);
|
||||
else
|
||||
sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i tried to process invalid sound id %u.", textEntry, (*i).second.SoundId);
|
||||
sLog.outErrorEventAI("DoScriptText entry %i tried to process invalid sound id %u.", textEntry, (*i).second.SoundId);
|
||||
}
|
||||
|
||||
if ((*i).second.Emote)
|
||||
|
|
@ -1340,7 +1389,7 @@ void CreatureEventAI::DoScriptText(int32 textEntry, WorldObject* pSource, Unit*
|
|||
((Unit*)pSource)->HandleEmote((*i).second.Emote);
|
||||
}
|
||||
else
|
||||
sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i tried to process emote for invalid TypeId (%u).", textEntry, pSource->GetTypeId());
|
||||
sLog.outErrorEventAI("DoScriptText entry %i tried to process emote for invalid TypeId (%u).", textEntry, pSource->GetTypeId());
|
||||
}
|
||||
|
||||
switch ((*i).second.Type)
|
||||
|
|
@ -1361,13 +1410,13 @@ void CreatureEventAI::DoScriptText(int32 textEntry, WorldObject* pSource, Unit*
|
|||
{
|
||||
if (target && target->GetTypeId() == TYPEID_PLAYER)
|
||||
pSource->MonsterWhisper(textEntry, target);
|
||||
else sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i cannot whisper without target unit (TYPEID_PLAYER).", textEntry);
|
||||
else sLog.outErrorEventAI("DoScriptText entry %i cannot whisper without target unit (TYPEID_PLAYER).", textEntry);
|
||||
} break;
|
||||
case CHAT_TYPE_BOSS_WHISPER:
|
||||
{
|
||||
if (target && target->GetTypeId() == TYPEID_PLAYER)
|
||||
pSource->MonsterWhisper(textEntry, target, true);
|
||||
else sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i cannot whisper without target unit (TYPEID_PLAYER).", textEntry);
|
||||
else sLog.outErrorEventAI("DoScriptText entry %i cannot whisper without target unit (TYPEID_PLAYER).", textEntry);
|
||||
} break;
|
||||
case CHAT_TYPE_ZONE_YELL:
|
||||
pSource->MonsterYellToZone(textEntry, (*i).second.Language, target);
|
||||
|
|
@ -1375,36 +1424,6 @@ void CreatureEventAI::DoScriptText(int32 textEntry, WorldObject* pSource, Unit*
|
|||
}
|
||||
}
|
||||
|
||||
bool CreatureEventAI::CanCast(Unit* Target, SpellEntry const* Spell, bool Triggered)
|
||||
{
|
||||
// No target so we can't cast
|
||||
if (!Target || !Spell)
|
||||
return false;
|
||||
|
||||
// Silenced so we can't cast
|
||||
if (!Triggered && (m_creature->hasUnitState(UNIT_STAT_CAN_NOT_REACT_OR_LOST_CONTROL) ||
|
||||
m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED)))
|
||||
return false;
|
||||
|
||||
// Check for power
|
||||
if (!Triggered && m_creature->GetPower((Powers)Spell->powerType) < Spell::CalculatePowerCost(Spell, m_creature))
|
||||
return false;
|
||||
|
||||
SpellRangeEntry const* TempRange = NULL;
|
||||
|
||||
TempRange = GetSpellRangeStore()->LookupEntry(Spell->rangeIndex);
|
||||
|
||||
// Spell has invalid range store so we can't use it
|
||||
if (!TempRange)
|
||||
return false;
|
||||
|
||||
// Unit is out of range of this spell
|
||||
if (!m_creature->IsInRange(Target, TempRange->minRange, TempRange->maxRange))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CreatureEventAI::ReceiveEmote(Player* pPlayer, uint32 text_emote)
|
||||
{
|
||||
if (m_bEmptyList)
|
||||
|
|
@ -1418,7 +1437,7 @@ void CreatureEventAI::ReceiveEmote(Player* pPlayer, uint32 text_emote)
|
|||
return;
|
||||
|
||||
PlayerCondition pcon(0, (*itr).Event.receive_emote.condition, (*itr).Event.receive_emote.conditionValue1, (*itr).Event.receive_emote.conditionValue2);
|
||||
if (pcon.Meets(pPlayer))
|
||||
if (pcon.Meets(pPlayer, m_creature->GetMap(), m_creature, CONDITION_FROM_EVENTAI))
|
||||
{
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "CreatureEventAI: ReceiveEmote CreatureEventAI: Condition ok, processing");
|
||||
ProcessEvent(*itr, pPlayer);
|
||||
|
|
|
|||
|
|
@ -23,9 +23,10 @@
|
|||
#include "CreatureEventAIMgr.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "ProgressBar.h"
|
||||
#include "Policies/SingletonImp.h"
|
||||
#include "Policies/Singleton.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "GridDefines.h"
|
||||
#include "SpellMgr.h"
|
||||
|
||||
INSTANTIATE_SINGLETON_1(CreatureEventAIMgr);
|
||||
|
||||
|
|
@ -62,33 +63,33 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Texts(bool check_entry_use)
|
|||
// range negative
|
||||
if (i > MIN_CREATURE_AI_TEXT_STRING_ID || i <= MAX_CREATURE_AI_TEXT_STRING_ID)
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` is not in valid range(%d-%d)", i, MIN_CREATURE_AI_TEXT_STRING_ID, MAX_CREATURE_AI_TEXT_STRING_ID);
|
||||
sLog.outErrorEventAI("CreatureEventAI: Entry %i in table `creature_ai_texts` is not in valid range(%d-%d)", i, MIN_CREATURE_AI_TEXT_STRING_ID, MAX_CREATURE_AI_TEXT_STRING_ID);
|
||||
continue;
|
||||
}
|
||||
|
||||
// range negative (don't must be happen, loaded from same table)
|
||||
if (!sObjectMgr.GetMangosStringLocale(i))
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` not found", i);
|
||||
sLog.outErrorEventAI("Entry %i in table `creature_ai_texts` not found", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (temp.SoundId)
|
||||
{
|
||||
if (!sSoundEntriesStore.LookupEntry(temp.SoundId))
|
||||
sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` has Sound %u but sound does not exist.", i, temp.SoundId);
|
||||
sLog.outErrorEventAI("Entry %i in table `creature_ai_texts` has Sound %u but sound does not exist.", i, temp.SoundId);
|
||||
}
|
||||
|
||||
if (!GetLanguageDescByID(temp.Language))
|
||||
sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` using Language %u but Language does not exist.", i, temp.Language);
|
||||
sLog.outErrorEventAI("Entry %i in table `creature_ai_texts` using Language %u but Language does not exist.", i, temp.Language);
|
||||
|
||||
if (temp.Type > CHAT_TYPE_ZONE_YELL)
|
||||
sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` has Type %u but this Chat Type does not exist.", i, temp.Type);
|
||||
sLog.outErrorEventAI("Entry %i in table `creature_ai_texts` has Type %u but this Chat Type does not exist.", i, temp.Type);
|
||||
|
||||
if (temp.Emote)
|
||||
{
|
||||
if (!sEmotesStore.LookupEntry(temp.Emote))
|
||||
sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` has Emote %u but emote does not exist.", i, temp.Emote);
|
||||
sLog.outErrorEventAI("Entry %i in table `creature_ai_texts` has Emote %u but emote does not exist.", i, temp.Emote);
|
||||
}
|
||||
|
||||
m_CreatureEventAI_TextMap[i] = temp;
|
||||
|
|
@ -143,13 +144,12 @@ void CreatureEventAIMgr::CheckUnusedAITexts()
|
|||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (std::set<int32>::const_iterator itr = idx_set.begin(); itr != idx_set.end(); ++itr)
|
||||
sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` but not used in EventAI scripts.", *itr);
|
||||
sLog.outErrorEventAI("Entry %i in table `creature_ai_texts` but not used in EventAI scripts.", *itr);
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
|
@ -182,7 +182,7 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Summons(bool check_entry_use)
|
|||
|
||||
if (!MaNGOS::IsValidMapCoord(temp.position_x, temp.position_y, temp.position_z, temp.orientation))
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Summon id %u have wrong coordinates (%f, %f, %f, %f), skipping.", temp.id, temp.position_x, temp.position_y, temp.position_z, temp.orientation);
|
||||
sLog.outErrorEventAI("Summon id %u have wrong coordinates (%f, %f, %f, %f), skipping.", temp.id, temp.position_x, temp.position_y, temp.position_z, temp.orientation);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -235,13 +235,12 @@ void CreatureEventAIMgr::CheckUnusedAISummons()
|
|||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (std::set<int32>::const_iterator itr = idx_set.begin(); itr != idx_set.end(); ++itr)
|
||||
sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_summons` but not used in EventAI scripts.", *itr);
|
||||
sLog.outErrorEventAI("Entry %i in table `creature_ai_summons` but not used in EventAI scripts.", *itr);
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
|
@ -278,7 +277,7 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
|
|||
// Report any errors in event
|
||||
if (e_type >= EVENT_T_END)
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u have wrong type (%u), skipping.", i, e_type);
|
||||
sLog.outErrorEventAI("Event %u have wrong type (%u), skipping.", i, e_type);
|
||||
continue;
|
||||
}
|
||||
temp.event_type = EventAI_Type(e_type);
|
||||
|
|
@ -294,43 +293,44 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
|
|||
// Creature does not exist in database
|
||||
if (!sCreatureStorage.LookupEntry<CreatureInfo>(temp.creature_id))
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u has script for non-existing creature entry (%u), skipping.", i, temp.creature_id);
|
||||
sLog.outErrorEventAI("Event %u has script for non-existing creature entry (%u), skipping.", i, temp.creature_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
// No chance of this event occuring
|
||||
if (temp.event_chance == 0)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u has 0 percent chance. Event will never trigger!", i);
|
||||
sLog.outErrorEventAI("Event %u has 0 percent chance. Event will never trigger!", i);
|
||||
// Chance above 100, force it to be 100
|
||||
else if (temp.event_chance > 100)
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u are using event %u with more than 100 percent chance. Adjusting to 100 percent.", temp.creature_id, i);
|
||||
sLog.outErrorEventAI("Creature %u are using event %u with more than 100 percent chance. Adjusting to 100 percent.", temp.creature_id, i);
|
||||
temp.event_chance = 100;
|
||||
}
|
||||
|
||||
// Individual event checks
|
||||
switch (temp.event_type)
|
||||
{
|
||||
case EVENT_T_TIMER:
|
||||
case EVENT_T_TIMER_IN_COMBAT:
|
||||
case EVENT_T_TIMER_OOC:
|
||||
case EVENT_T_TIMER_GENERIC:
|
||||
if (temp.timer.initialMax < temp.timer.initialMin)
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u are using timed event(%u) with param2 < param1 (InitialMax < InitialMin). Event will never repeat.", temp.creature_id, i);
|
||||
sLog.outErrorEventAI("Creature %u are using timed event(%u) with param2 < param1 (InitialMax < InitialMin). Event will never repeat.", temp.creature_id, i);
|
||||
if (temp.timer.repeatMax < temp.timer.repeatMin)
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
sLog.outErrorEventAI("Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
break;
|
||||
case EVENT_T_HP:
|
||||
case EVENT_T_MANA:
|
||||
case EVENT_T_TARGET_HP:
|
||||
case EVENT_T_TARGET_MANA:
|
||||
if (temp.percent_range.percentMax > 100)
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u are using percentage event(%u) with param2 (MinPercent) > 100. Event will never trigger! ", temp.creature_id, i);
|
||||
sLog.outErrorEventAI("Creature %u are using percentage event(%u) with param2 (MinPercent) > 100. Event will never trigger! ", temp.creature_id, i);
|
||||
|
||||
if (temp.percent_range.percentMax <= temp.percent_range.percentMin)
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u are using percentage event(%u) with param1 <= param2 (MaxPercent <= MinPercent). Event will never trigger! ", temp.creature_id, i);
|
||||
sLog.outErrorEventAI("Creature %u are using percentage event(%u) with param1 <= param2 (MaxPercent <= MinPercent). Event will never trigger! ", temp.creature_id, i);
|
||||
|
||||
if (temp.event_flags & EFLAG_REPEATABLE && !temp.percent_range.repeatMin && !temp.percent_range.repeatMax)
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u has param3 and param4=0 (RepeatMin/RepeatMax) but cannot be repeatable without timers. Removing EFLAG_REPEATABLE for event %u.", temp.creature_id, i);
|
||||
sLog.outErrorEventAI("Creature %u has param3 and param4=0 (RepeatMin/RepeatMax) but cannot be repeatable without timers. Removing EFLAG_REPEATABLE for event %u.", temp.creature_id, i);
|
||||
temp.event_flags &= ~EFLAG_REPEATABLE;
|
||||
}
|
||||
break;
|
||||
|
|
@ -340,29 +340,29 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
|
|||
SpellEntry const* pSpell = sSpellStore.LookupEntry(temp.spell_hit.spellId);
|
||||
if (!pSpell)
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u has nonexistent SpellID(%u) defined in event %u.", temp.creature_id, temp.spell_hit.spellId, i);
|
||||
sLog.outErrorEventAI("Creature %u has nonexistent SpellID(%u) defined in event %u.", temp.creature_id, temp.spell_hit.spellId, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((temp.spell_hit.schoolMask & pSpell->SchoolMask) != pSpell->SchoolMask)
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u has param1(spellId %u) but param2 is not -1 and not equal to spell's school mask. Event %u can never trigger.", temp.creature_id, temp.spell_hit.schoolMask, i);
|
||||
sLog.outErrorEventAI("Creature %u has param1(spellId %u) but param2 is not -1 and not equal to spell's school mask. Event %u can never trigger.", temp.creature_id, temp.spell_hit.schoolMask, i);
|
||||
}
|
||||
|
||||
if (!temp.spell_hit.schoolMask)
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u is using invalid SpellSchoolMask(%u) defined in event %u.", temp.creature_id, temp.spell_hit.schoolMask, i);
|
||||
sLog.outErrorEventAI("Creature %u is using invalid SpellSchoolMask(%u) defined in event %u.", temp.creature_id, temp.spell_hit.schoolMask, i);
|
||||
|
||||
if (temp.spell_hit.repeatMax < temp.spell_hit.repeatMin)
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
sLog.outErrorEventAI("Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
break;
|
||||
case EVENT_T_RANGE:
|
||||
if (temp.range.maxDist < temp.range.minDist)
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (MaxDist < MinDist). Event will never repeat.", temp.creature_id, i);
|
||||
sLog.outErrorEventAI("Creature %u are using event(%u) with param2 < param1 (MaxDist < MinDist). Event will never repeat.", temp.creature_id, i);
|
||||
if (temp.range.repeatMax < temp.range.repeatMin)
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
sLog.outErrorEventAI("Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
break;
|
||||
case EVENT_T_OOC_LOS:
|
||||
if (temp.ooc_los.repeatMax < temp.ooc_los.repeatMin)
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
sLog.outErrorEventAI("Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
break;
|
||||
case EVENT_T_SPAWNED:
|
||||
switch (temp.spawned.condition)
|
||||
|
|
@ -371,63 +371,63 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
|
|||
break;
|
||||
case SPAWNED_EVENT_MAP:
|
||||
if (!sMapStore.LookupEntry(temp.spawned.conditionValue1))
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u are using spawned event(%u) with param1 = %u 'map specific' but map (param2: %u) does not exist. Event will never repeat.", temp.creature_id, i, temp.spawned.condition, temp.spawned.conditionValue1);
|
||||
sLog.outErrorEventAI("Creature %u are using spawned event(%u) with param1 = %u 'map specific' but map (param2: %u) does not exist. Event will never repeat.", temp.creature_id, i, temp.spawned.condition, temp.spawned.conditionValue1);
|
||||
break;
|
||||
case SPAWNED_EVENT_ZONE:
|
||||
if (!GetAreaEntryByAreaID(temp.spawned.conditionValue1))
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u are using spawned event(%u) with param1 = %u 'area specific' but area (param2: %u) does not exist. Event will never repeat.", temp.creature_id, i, temp.spawned.condition, temp.spawned.conditionValue1);
|
||||
sLog.outErrorEventAI("Creature %u are using spawned event(%u) with param1 = %u 'area specific' but area (param2: %u) does not exist. Event will never repeat.", temp.creature_id, i, temp.spawned.condition, temp.spawned.conditionValue1);
|
||||
break;
|
||||
default:
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u are using invalid spawned event %u mode (%u) in param1", temp.creature_id, i, temp.spawned.condition);
|
||||
sLog.outErrorEventAI("Creature %u are using invalid spawned event %u mode (%u) in param1", temp.creature_id, i, temp.spawned.condition);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case EVENT_T_FRIENDLY_HP:
|
||||
if (temp.friendly_hp.repeatMax < temp.friendly_hp.repeatMin)
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
sLog.outErrorEventAI("Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
break;
|
||||
case EVENT_T_FRIENDLY_IS_CC:
|
||||
if (temp.friendly_is_cc.repeatMax < temp.friendly_is_cc.repeatMin)
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
sLog.outErrorEventAI("Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
break;
|
||||
case EVENT_T_FRIENDLY_MISSING_BUFF:
|
||||
{
|
||||
SpellEntry const* pSpell = sSpellStore.LookupEntry(temp.friendly_buff.spellId);
|
||||
if (!pSpell)
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u has nonexistent SpellID(%u) defined in event %u.", temp.creature_id, temp.friendly_buff.spellId, i);
|
||||
sLog.outErrorEventAI("Creature %u has nonexistent SpellID(%u) defined in event %u.", temp.creature_id, temp.friendly_buff.spellId, i);
|
||||
continue;
|
||||
}
|
||||
if (temp.friendly_buff.radius <= 0)
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u has wrong radius (%u) for EVENT_T_FRIENDLY_MISSING_BUFF defined in event %u.", temp.creature_id, temp.friendly_buff.radius, i);
|
||||
sLog.outErrorEventAI("Creature %u has wrong radius (%u) for EVENT_T_FRIENDLY_MISSING_BUFF defined in event %u.", temp.creature_id, temp.friendly_buff.radius, i);
|
||||
continue;
|
||||
}
|
||||
if (temp.friendly_buff.repeatMax < temp.friendly_buff.repeatMin)
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
sLog.outErrorEventAI("Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
break;
|
||||
}
|
||||
case EVENT_T_KILL:
|
||||
if (temp.kill.repeatMax < temp.kill.repeatMin)
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
sLog.outErrorEventAI("Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
break;
|
||||
case EVENT_T_TARGET_CASTING:
|
||||
if (temp.target_casting.repeatMax < temp.target_casting.repeatMin)
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
sLog.outErrorEventAI("Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
break;
|
||||
case EVENT_T_SUMMONED_UNIT:
|
||||
case EVENT_T_SUMMONED_JUST_DIED:
|
||||
case EVENT_T_SUMMONED_JUST_DESPAWN:
|
||||
if (!sCreatureStorage.LookupEntry<CreatureInfo>(temp.summoned.creatureId))
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with nonexistent creature template id (%u) in param1, skipped.", temp.creature_id, i, temp.summoned.creatureId);
|
||||
sLog.outErrorEventAI("Creature %u are using event(%u) with nonexistent creature template id (%u) in param1, skipped.", temp.creature_id, i, temp.summoned.creatureId);
|
||||
if (temp.summoned.repeatMax < temp.summoned.repeatMin)
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
sLog.outErrorEventAI("Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
break;
|
||||
case EVENT_T_QUEST_ACCEPT:
|
||||
case EVENT_T_QUEST_COMPLETE:
|
||||
if (!sObjectMgr.GetQuestTemplate(temp.quest.questId))
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with nonexistent quest id (%u) in param1, skipped.", temp.creature_id, i, temp.quest.questId);
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u using not implemented event (%u) in event %u.", temp.creature_id, temp.event_id, i);
|
||||
sLog.outErrorEventAI("Creature %u are using event(%u) with nonexistent quest id (%u) in param1, skipped.", temp.creature_id, i, temp.quest.questId);
|
||||
sLog.outErrorEventAI("Creature %u using not implemented event (%u) in event %u.", temp.creature_id, temp.event_id, i);
|
||||
continue;
|
||||
|
||||
case EVENT_T_AGGRO:
|
||||
|
|
@ -437,7 +437,7 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
|
|||
{
|
||||
if (temp.event_flags & EFLAG_REPEATABLE)
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u has EFLAG_REPEATABLE set. Event can never be repeatable. Removing flag for event %u.", temp.creature_id, i);
|
||||
sLog.outErrorEventAI("Creature %u has EFLAG_REPEATABLE set. Event can never be repeatable. Removing flag for event %u.", temp.creature_id, i);
|
||||
temp.event_flags &= ~EFLAG_REPEATABLE;
|
||||
}
|
||||
|
||||
|
|
@ -448,19 +448,19 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
|
|||
{
|
||||
if (!sEmotesTextStore.LookupEntry(temp.receive_emote.emoteId))
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u using event %u: param1 (EmoteTextId: %u) are not valid.", temp.creature_id, i, temp.receive_emote.emoteId);
|
||||
sLog.outErrorEventAI("Creature %u using event %u: param1 (EmoteTextId: %u) are not valid.", temp.creature_id, i, temp.receive_emote.emoteId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!PlayerCondition::IsValid(0, ConditionType(temp.receive_emote.condition), temp.receive_emote.conditionValue1, temp.receive_emote.conditionValue2))
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u using event %u: param2 (Condition: %u) are not valid.", temp.creature_id, i, temp.receive_emote.condition);
|
||||
sLog.outErrorEventAI("Creature %u using event %u: param2 (Condition: %u) are not valid.", temp.creature_id, i, temp.receive_emote.condition);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(temp.event_flags & EFLAG_REPEATABLE))
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u using event %u: EFLAG_REPEATABLE not set. Event must always be repeatable. Flag applied.", temp.creature_id, i);
|
||||
sLog.outErrorEventAI("Creature %u using event %u: EFLAG_REPEATABLE not set. Event must always be repeatable. Flag applied.", temp.creature_id, i);
|
||||
temp.event_flags |= EFLAG_REPEATABLE;
|
||||
}
|
||||
|
||||
|
|
@ -475,21 +475,21 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
|
|||
SpellEntry const* pSpell = sSpellStore.LookupEntry(temp.buffed.spellId);
|
||||
if (!pSpell)
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u has nonexistent SpellID(%u) defined in event %u.", temp.creature_id, temp.buffed.spellId, i);
|
||||
sLog.outErrorEventAI("Creature %u has nonexistent SpellID(%u) defined in event %u.", temp.creature_id, temp.buffed.spellId, i);
|
||||
continue;
|
||||
}
|
||||
if (temp.buffed.amount < 1)
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u has wrong spell stack size (%u) defined in event %u.", temp.creature_id, temp.buffed.amount, i);
|
||||
sLog.outErrorEventAI("Creature %u has wrong spell stack size (%u) defined in event %u.", temp.creature_id, temp.buffed.amount, i);
|
||||
continue;
|
||||
}
|
||||
if (temp.buffed.repeatMax < temp.buffed.repeatMin)
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
sLog.outErrorEventAI("Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
sLog.outErrorDb("CreatureEventAI: Creature %u using not checked at load event (%u) in event %u. Need check code update?", temp.creature_id, temp.event_id, i);
|
||||
sLog.outErrorEventAI("Creature %u using not checked at load event (%u) in event %u. Need check code update?", temp.creature_id, temp.event_id, i);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -498,7 +498,7 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
|
|||
uint16 action_type = fields[10 + (j * 4)].GetUInt16();
|
||||
if (action_type >= ACTION_T_END)
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u has incorrect action type (%u), replace by ACTION_T_NONE.", i, j + 1, action_type);
|
||||
sLog.outErrorEventAI("Event %u Action %u has incorrect action type (%u), replace by ACTION_T_NONE.", i, j + 1, action_type);
|
||||
temp.action[j].type = ACTION_T_NONE;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -518,9 +518,9 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
|
|||
case ACTION_T_CHANCED_TEXT:
|
||||
// Check first param as chance
|
||||
if (!action.chanced_text.chance)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u has not set chance param1. Text will not be displayed", i, j + 1);
|
||||
sLog.outErrorEventAI("Event %u Action %u has not set chance param1. Text will not be displayed", i, j + 1);
|
||||
else if (action.chanced_text.chance >= 100)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u has set chance param1 >= 100. Text will always be displayed", i, j + 1);
|
||||
sLog.outErrorEventAI("Event %u Action %u has set chance param1 >= 100. Text will always be displayed", i, j + 1);
|
||||
// no break here to check texts
|
||||
case ACTION_T_TEXT:
|
||||
{
|
||||
|
|
@ -531,19 +531,19 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
|
|||
if (action.text.TextId[k])
|
||||
{
|
||||
if (k > firstTextParam && not_set)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u has param%d, but it follow after not set param. Required for randomized text.", i, j + 1, k + 1);
|
||||
sLog.outErrorEventAI("Event %u Action %u has param%d, but it follow after not set param. Required for randomized text.", i, j + 1, k + 1);
|
||||
|
||||
if (!action.text.TextId[k])
|
||||
not_set = true;
|
||||
// range negative
|
||||
else if (action.text.TextId[k] > MIN_CREATURE_AI_TEXT_STRING_ID || action.text.TextId[k] <= MAX_CREATURE_AI_TEXT_STRING_ID)
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param%d references out-of-range entry (%i) in texts table.", i, j + 1, k + 1, action.text.TextId[k]);
|
||||
sLog.outErrorEventAI("Event %u Action %u param%d references out-of-range entry (%i) in texts table.", i, j + 1, k + 1, action.text.TextId[k]);
|
||||
action.text.TextId[k] = 0;
|
||||
}
|
||||
else if (m_CreatureEventAI_TextMap.find(action.text.TextId[k]) == m_CreatureEventAI_TextMap.end())
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param%d references non-existing entry (%i) in texts table.", i, j + 1, k + 1, action.text.TextId[k]);
|
||||
sLog.outErrorEventAI("Event %u Action %u param%d references non-existing entry (%i) in texts table.", i, j + 1, k + 1, action.text.TextId[k]);
|
||||
action.text.TextId[k] = 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -553,7 +553,7 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
|
|||
case ACTION_T_SET_FACTION:
|
||||
if (action.set_faction.factionId != 0 && !sFactionTemplateStore.LookupEntry(action.set_faction.factionId))
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses nonexistent FactionId %u.", i, j + 1, action.set_faction.factionId);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent FactionId %u.", i, j + 1, action.set_faction.factionId);
|
||||
action.set_faction.factionId = 0;
|
||||
}
|
||||
break;
|
||||
|
|
@ -562,7 +562,7 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
|
|||
{
|
||||
if (action.morph.creatureId && !sCreatureStorage.LookupEntry<CreatureInfo>(action.morph.creatureId))
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses nonexistent Creature entry %u.", i, j + 1, action.morph.creatureId);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent Creature entry %u.", i, j + 1, action.morph.creatureId);
|
||||
action.morph.creatureId = 0;
|
||||
}
|
||||
|
||||
|
|
@ -570,12 +570,12 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
|
|||
{
|
||||
if (action.morph.creatureId)
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u have unused ModelId %u with also set creature id %u.", i, j + 1, action.morph.modelId, action.morph.creatureId);
|
||||
sLog.outErrorEventAI("Event %u Action %u have unused ModelId %u with also set creature id %u.", i, j + 1, action.morph.modelId, action.morph.creatureId);
|
||||
action.morph.modelId = 0;
|
||||
}
|
||||
else if (!sCreatureDisplayInfoStore.LookupEntry(action.morph.modelId))
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses nonexistent ModelId %u.", i, j + 1, action.morph.modelId);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent ModelId %u.", i, j + 1, action.morph.modelId);
|
||||
action.morph.modelId = 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -583,33 +583,33 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
|
|||
break;
|
||||
case ACTION_T_SOUND:
|
||||
if (!sSoundEntriesStore.LookupEntry(action.sound.soundId))
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses nonexistent SoundID %u.", i, j + 1, action.sound.soundId);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent SoundID %u.", i, j + 1, action.sound.soundId);
|
||||
break;
|
||||
case ACTION_T_EMOTE:
|
||||
if (!sEmotesStore.LookupEntry(action.emote.emoteId))
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 (EmoteId: %u) are not valid.", i, j + 1, action.emote.emoteId);
|
||||
sLog.outErrorEventAI("Event %u Action %u param1 (EmoteId: %u) are not valid.", i, j + 1, action.emote.emoteId);
|
||||
break;
|
||||
case ACTION_T_RANDOM_SOUND:
|
||||
if (!sSoundEntriesStore.LookupEntry(action.random_sound.soundId1))
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 uses nonexistent SoundID %u.", i, j + 1, action.random_sound.soundId1);
|
||||
sLog.outErrorEventAI("Event %u Action %u param1 uses nonexistent SoundID %u.", i, j + 1, action.random_sound.soundId1);
|
||||
if (action.random_sound.soundId2 >= 0 && !sSoundEntriesStore.LookupEntry(action.random_sound.soundId2))
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param2 uses nonexistent SoundID %u.", i, j + 1, action.random_sound.soundId2);
|
||||
sLog.outErrorEventAI("Event %u Action %u param2 uses nonexistent SoundID %u.", i, j + 1, action.random_sound.soundId2);
|
||||
if (action.random_sound.soundId3 >= 0 && !sSoundEntriesStore.LookupEntry(action.random_sound.soundId3))
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param3 uses nonexistent SoundID %u.", i, j + 1, action.random_sound.soundId3);
|
||||
sLog.outErrorEventAI("Event %u Action %u param3 uses nonexistent SoundID %u.", i, j + 1, action.random_sound.soundId3);
|
||||
break;
|
||||
case ACTION_T_RANDOM_EMOTE:
|
||||
if (!sEmotesStore.LookupEntry(action.random_emote.emoteId1))
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 (EmoteId: %u) are not valid.", i, j + 1, action.random_emote.emoteId1);
|
||||
sLog.outErrorEventAI("Event %u Action %u param1 (EmoteId: %u) are not valid.", i, j + 1, action.random_emote.emoteId1);
|
||||
if (action.random_emote.emoteId2 >= 0 && !sEmotesStore.LookupEntry(action.random_emote.emoteId2))
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param2 (EmoteId: %u) are not valid.", i, j + 1, action.random_emote.emoteId2);
|
||||
sLog.outErrorEventAI("Event %u Action %u param2 (EmoteId: %u) are not valid.", i, j + 1, action.random_emote.emoteId2);
|
||||
if (action.random_emote.emoteId3 >= 0 && !sEmotesStore.LookupEntry(action.random_emote.emoteId3))
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param3 (EmoteId: %u) are not valid.", i, j + 1, action.random_emote.emoteId3);
|
||||
sLog.outErrorEventAI("Event %u Action %u param3 (EmoteId: %u) are not valid.", i, j + 1, action.random_emote.emoteId3);
|
||||
break;
|
||||
case ACTION_T_CAST:
|
||||
{
|
||||
const SpellEntry* spell = sSpellStore.LookupEntry(action.cast.spellId);
|
||||
if (!spell)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses nonexistent SpellID %u.", i, j + 1, action.cast.spellId);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent SpellID %u.", i, j + 1, action.cast.spellId);
|
||||
/* FIXME: temp.raw.param3 not have event tipes with recovery time in it....
|
||||
else
|
||||
{
|
||||
|
|
@ -627,143 +627,158 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
|
|||
action.cast.castFlags |= CAST_TRIGGERED;
|
||||
|
||||
if (action.cast.target >= TARGET_T_END)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j + 1);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses incorrect Target type", i, j + 1);
|
||||
|
||||
// Some Advanced target type checks - Can have false positives
|
||||
if (!sLog.HasLogFilter(LOG_FILTER_EVENT_AI_DEV) && spell)
|
||||
{
|
||||
// used TARGET_T_ACTION_INVOKER, but likely should be _INVOKER_OWNER instead
|
||||
if (action.cast.target == TARGET_T_ACTION_INVOKER &&
|
||||
(IsSpellHaveEffect(spell, SPELL_EFFECT_QUEST_COMPLETE) || IsSpellHaveEffect(spell, SPELL_EFFECT_CREATE_RANDOM_ITEM) || IsSpellHaveEffect(spell, SPELL_EFFECT_DUMMY)
|
||||
|| IsSpellHaveEffect(spell, SPELL_EFFECT_KILL_CREDIT_PERSONAL) || IsSpellHaveEffect(spell, SPELL_EFFECT_KILL_CREDIT_GROUP)))
|
||||
sLog.outErrorEventAI("Event %u Action %u has TARGET_T_ACTION_INVOKER(%u) target type, but should have TARGET_T_ACTION_INVOKER_OWNER(%u).", i, j + 1, TARGET_T_ACTION_INVOKER, TARGET_T_ACTION_INVOKER_OWNER);
|
||||
|
||||
// Spell that should only target players, but could get any
|
||||
if (spell->HasAttribute(SPELL_ATTR_EX3_TARGET_ONLY_PLAYER) &&
|
||||
(action.cast.target == TARGET_T_ACTION_INVOKER || action.cast.target == TARGET_T_HOSTILE_RANDOM || action.cast.target == TARGET_T_HOSTILE_RANDOM_NOT_TOP))
|
||||
sLog.outErrorEventAI("Event %u Action %u uses Target type %u for a spell (%u) that should only target players. This could be wrong.", i, j + 1, action.cast.target, action.cast.spellId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ACTION_T_SUMMON:
|
||||
if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.summon.creatureId))
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses nonexistent creature entry %u.", i, j + 1, action.summon.creatureId);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent creature entry %u.", i, j + 1, action.summon.creatureId);
|
||||
|
||||
if (action.summon.target >= TARGET_T_END)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j + 1);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses incorrect Target type", i, j + 1);
|
||||
break;
|
||||
case ACTION_T_THREAT_SINGLE_PCT:
|
||||
if (std::abs(action.threat_single_pct.percent) > 100)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses invalid percent value %u.", i, j + 1, action.threat_single_pct.percent);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses invalid percent value %u.", i, j + 1, action.threat_single_pct.percent);
|
||||
if (action.threat_single_pct.target >= TARGET_T_END)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j + 1);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses incorrect Target type", i, j + 1);
|
||||
break;
|
||||
case ACTION_T_THREAT_ALL_PCT:
|
||||
if (std::abs(action.threat_all_pct.percent) > 100)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses invalid percent value %u.", i, j + 1, action.threat_all_pct.percent);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses invalid percent value %u.", i, j + 1, action.threat_all_pct.percent);
|
||||
break;
|
||||
case ACTION_T_QUEST_EVENT:
|
||||
if (Quest const* qid = sObjectMgr.GetQuestTemplate(action.quest_event.questId))
|
||||
{
|
||||
if (!qid->HasSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT))
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u. SpecialFlags for quest entry %u does not include |2, Action will not have any effect.", i, j + 1, action.quest_event.questId);
|
||||
sLog.outErrorEventAI("Event %u Action %u. SpecialFlags for quest entry %u does not include |2, Action will not have any effect.", i, j + 1, action.quest_event.questId);
|
||||
}
|
||||
else
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses nonexistent Quest entry %u.", i, j + 1, action.quest_event.questId);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent Quest entry %u.", i, j + 1, action.quest_event.questId);
|
||||
|
||||
if (action.quest_event.target >= TARGET_T_END)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j + 1);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses incorrect Target type", i, j + 1);
|
||||
|
||||
break;
|
||||
case ACTION_T_CAST_EVENT:
|
||||
if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.cast_event.creatureId))
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses nonexistent creature entry %u.", i, j + 1, action.cast_event.creatureId);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent creature entry %u.", i, j + 1, action.cast_event.creatureId);
|
||||
if (!sSpellStore.LookupEntry(action.cast_event.spellId))
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses nonexistent SpellID %u.", i, j + 1, action.cast_event.spellId);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent SpellID %u.", i, j + 1, action.cast_event.spellId);
|
||||
if (action.cast_event.target >= TARGET_T_END)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j + 1);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses incorrect Target type", i, j + 1);
|
||||
break;
|
||||
case ACTION_T_SET_UNIT_FIELD:
|
||||
if (action.set_unit_field.field < OBJECT_END || action.set_unit_field.field >= UNIT_END)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 (UNIT_FIELD*). Index out of range for intended use.", i, j + 1);
|
||||
sLog.outErrorEventAI("Event %u Action %u param1 (UNIT_FIELD*). Index out of range for intended use.", i, j + 1);
|
||||
if (action.set_unit_field.target >= TARGET_T_END)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j + 1);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses incorrect Target type", i, j + 1);
|
||||
break;
|
||||
case ACTION_T_SET_UNIT_FLAG:
|
||||
case ACTION_T_REMOVE_UNIT_FLAG:
|
||||
if (action.unit_flag.target >= TARGET_T_END)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j + 1);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses incorrect Target type", i, j + 1);
|
||||
break;
|
||||
case ACTION_T_SET_PHASE:
|
||||
if (action.set_phase.phase >= MAX_PHASE)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phase >= %u. Phase mask cannot be used past phase %u.", i, j + 1, MAX_PHASE, MAX_PHASE - 1);
|
||||
sLog.outErrorEventAI("Event %u Action %u attempts to set phase >= %u. Phase mask cannot be used past phase %u.", i, j + 1, MAX_PHASE, MAX_PHASE - 1);
|
||||
break;
|
||||
case ACTION_T_INC_PHASE:
|
||||
if (action.set_inc_phase.step == 0)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u is incrementing phase by 0. Was this intended?", i, j + 1);
|
||||
sLog.outErrorEventAI("Event %u Action %u is incrementing phase by 0. Was this intended?", i, j + 1);
|
||||
else if (std::abs(action.set_inc_phase.step) > MAX_PHASE - 1)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u is change phase by too large for any use %i.", i, j + 1, action.set_inc_phase.step);
|
||||
sLog.outErrorEventAI("Event %u Action %u is change phase by too large for any use %i.", i, j + 1, action.set_inc_phase.step);
|
||||
break;
|
||||
case ACTION_T_QUEST_EVENT_ALL:
|
||||
if (Quest const* qid = sObjectMgr.GetQuestTemplate(action.quest_event_all.questId))
|
||||
{
|
||||
if (!qid->HasSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT))
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u. SpecialFlags for quest entry %u does not include |2, Action will not have any effect.", i, j + 1, action.quest_event_all.questId);
|
||||
sLog.outErrorEventAI("Event %u Action %u. SpecialFlags for quest entry %u does not include |2, Action will not have any effect.", i, j + 1, action.quest_event_all.questId);
|
||||
}
|
||||
else
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses nonexistent Quest entry %u.", i, j + 1, action.quest_event_all.questId);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent Quest entry %u.", i, j + 1, action.quest_event_all.questId);
|
||||
break;
|
||||
case ACTION_T_CAST_EVENT_ALL:
|
||||
if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.cast_event_all.creatureId))
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses nonexistent creature entry %u.", i, j + 1, action.cast_event_all.creatureId);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent creature entry %u.", i, j + 1, action.cast_event_all.creatureId);
|
||||
if (!sSpellStore.LookupEntry(action.cast_event_all.spellId))
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses nonexistent SpellID %u.", i, j + 1, action.cast_event_all.spellId);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent SpellID %u.", i, j + 1, action.cast_event_all.spellId);
|
||||
break;
|
||||
case ACTION_T_REMOVEAURASFROMSPELL:
|
||||
if (!sSpellStore.LookupEntry(action.remove_aura.spellId))
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses nonexistent SpellID %u.", i, j + 1, action.remove_aura.spellId);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent SpellID %u.", i, j + 1, action.remove_aura.spellId);
|
||||
if (action.remove_aura.target >= TARGET_T_END)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j + 1);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses incorrect Target type", i, j + 1);
|
||||
break;
|
||||
case ACTION_T_RANDOM_PHASE: // PhaseId1, PhaseId2, PhaseId3
|
||||
if (action.random_phase.phase1 >= MAX_PHASE)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phase1 >= %u. Phase mask cannot be used past phase %u.", i, j + 1, MAX_PHASE, MAX_PHASE - 1);
|
||||
sLog.outErrorEventAI("Event %u Action %u attempts to set phase1 >= %u. Phase mask cannot be used past phase %u.", i, j + 1, MAX_PHASE, MAX_PHASE - 1);
|
||||
if (action.random_phase.phase2 >= MAX_PHASE)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phase2 >= %u. Phase mask cannot be used past phase %u.", i, j + 1, MAX_PHASE, MAX_PHASE - 1);
|
||||
sLog.outErrorEventAI("Event %u Action %u attempts to set phase2 >= %u. Phase mask cannot be used past phase %u.", i, j + 1, MAX_PHASE, MAX_PHASE - 1);
|
||||
if (action.random_phase.phase3 >= MAX_PHASE)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phase3 >= %u. Phase mask cannot be used past phase %u.", i, j + 1, MAX_PHASE, MAX_PHASE - 1);
|
||||
sLog.outErrorEventAI("Event %u Action %u attempts to set phase3 >= %u. Phase mask cannot be used past phase %u.", i, j + 1, MAX_PHASE, MAX_PHASE - 1);
|
||||
break;
|
||||
case ACTION_T_RANDOM_PHASE_RANGE: // PhaseMin, PhaseMax
|
||||
if (action.random_phase_range.phaseMin >= MAX_PHASE)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phaseMin >= %u. Phase mask cannot be used past phase %u.", i, j + 1, MAX_PHASE, MAX_PHASE - 1);
|
||||
sLog.outErrorEventAI("Event %u Action %u attempts to set phaseMin >= %u. Phase mask cannot be used past phase %u.", i, j + 1, MAX_PHASE, MAX_PHASE - 1);
|
||||
if (action.random_phase_range.phaseMin >= MAX_PHASE)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phaseMax >= %u. Phase mask cannot be used past phase %u.", i, j + 1, MAX_PHASE, MAX_PHASE - 1);
|
||||
sLog.outErrorEventAI("Event %u Action %u attempts to set phaseMax >= %u. Phase mask cannot be used past phase %u.", i, j + 1, MAX_PHASE, MAX_PHASE - 1);
|
||||
if (action.random_phase_range.phaseMin >= action.random_phase_range.phaseMax)
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phaseMax <= phaseMin.", i, j + 1);
|
||||
sLog.outErrorEventAI("Event %u Action %u attempts to set phaseMax <= phaseMin.", i, j + 1);
|
||||
std::swap(action.random_phase_range.phaseMin, action.random_phase_range.phaseMax);
|
||||
// equal case processed at call
|
||||
}
|
||||
break;
|
||||
case ACTION_T_SUMMON_ID:
|
||||
if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.summon_id.creatureId))
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses nonexistent creature entry %u.", i, j + 1, action.summon_id.creatureId);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent creature entry %u.", i, j + 1, action.summon_id.creatureId);
|
||||
if (action.summon_id.target >= TARGET_T_END)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j + 1);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses incorrect Target type", i, j + 1);
|
||||
if (m_CreatureEventAI_Summon_Map.find(action.summon_id.spawnId) == m_CreatureEventAI_Summon_Map.end())
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u summons missing CreatureEventAI_Summon %u", i, j + 1, action.summon_id.spawnId);
|
||||
sLog.outErrorEventAI("Event %u Action %u summons missing CreatureEventAI_Summon %u", i, j + 1, action.summon_id.spawnId);
|
||||
break;
|
||||
case ACTION_T_KILLED_MONSTER:
|
||||
if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.killed_monster.creatureId))
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses nonexistent creature entry %u.", i, j + 1, action.killed_monster.creatureId);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent creature entry %u.", i, j + 1, action.killed_monster.creatureId);
|
||||
if (action.killed_monster.target >= TARGET_T_END)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j + 1);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses incorrect Target type", i, j + 1);
|
||||
break;
|
||||
case ACTION_T_SET_INST_DATA:
|
||||
if (!(temp.event_flags & EFLAG_DIFFICULTY_ALL))
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u. Cannot set instance data without difficulty event flags.", i, j + 1);
|
||||
sLog.outErrorEventAI("Event %u Action %u. Cannot set instance data without difficulty event flags.", i, j + 1);
|
||||
if (action.set_inst_data.value > 4/*SPECIAL*/)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set instance data above encounter state 4. Custom case?", i, j + 1);
|
||||
sLog.outErrorEventAI("Event %u Action %u attempts to set instance data above encounter state 4. Custom case?", i, j + 1);
|
||||
break;
|
||||
case ACTION_T_SET_INST_DATA64:
|
||||
if (!(temp.event_flags & EFLAG_DIFFICULTY_ALL))
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u. Cannot set instance data without difficulty event flags.", i, j + 1);
|
||||
sLog.outErrorEventAI("Event %u Action %u. Cannot set instance data without difficulty event flags.", i, j + 1);
|
||||
if (action.set_inst_data64.target >= TARGET_T_END)
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j + 1);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses incorrect Target type", i, j + 1);
|
||||
break;
|
||||
case ACTION_T_UPDATE_TEMPLATE:
|
||||
if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.update_template.creatureId))
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses nonexistent creature entry %u.", i, j + 1, action.update_template.creatureId);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent creature entry %u.", i, j + 1, action.update_template.creatureId);
|
||||
break;
|
||||
case ACTION_T_SET_SHEATH:
|
||||
if (action.set_sheath.sheath >= MAX_SHEATH_STATE)
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses wrong sheath state %u.", i, j + 1, action.set_sheath.sheath);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses wrong sheath state %u.", i, j + 1, action.set_sheath.sheath);
|
||||
action.set_sheath.sheath = SHEATH_STATE_UNARMED;
|
||||
}
|
||||
break;
|
||||
|
|
@ -772,7 +787,7 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
|
|||
{
|
||||
if (action.invincibility_hp_level.hp_level > 100)
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses wrong percent value %u.", i, j + 1, action.invincibility_hp_level.hp_level);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses wrong percent value %u.", i, j + 1, action.invincibility_hp_level.hp_level);
|
||||
action.invincibility_hp_level.hp_level = 100;
|
||||
}
|
||||
}
|
||||
|
|
@ -782,7 +797,7 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
|
|||
{
|
||||
if (action.mount.creatureId && !sCreatureStorage.LookupEntry<CreatureInfo>(action.mount.creatureId))
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses nonexistent Creature entry %u.", i, j + 1, action.mount.creatureId);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent Creature entry %u.", i, j + 1, action.mount.creatureId);
|
||||
action.morph.creatureId = 0;
|
||||
}
|
||||
|
||||
|
|
@ -790,12 +805,12 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
|
|||
{
|
||||
if (action.mount.creatureId)
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u have unused ModelId %u with also set creature id %u.", i, j + 1, action.mount.modelId, action.mount.creatureId);
|
||||
sLog.outErrorEventAI("Event %u Action %u have unused ModelId %u with also set creature id %u.", i, j + 1, action.mount.modelId, action.mount.creatureId);
|
||||
action.mount.modelId = 0;
|
||||
}
|
||||
else if (!sCreatureDisplayInfoStore.LookupEntry(action.mount.modelId))
|
||||
{
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses nonexistent ModelId %u.", i, j + 1, action.mount.modelId);
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent ModelId %u.", i, j + 1, action.mount.modelId);
|
||||
action.mount.modelId = 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -815,10 +830,10 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
|
|||
case ACTION_T_RANDOM_SAY:
|
||||
case ACTION_T_RANDOM_YELL:
|
||||
case ACTION_T_RANDOM_TEXTEMOTE:
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u currently unused ACTION type. Did you forget to update database?", i, j + 1);
|
||||
sLog.outErrorEventAI("Event %u Action %u currently unused ACTION type. Did you forget to update database?", i, j + 1);
|
||||
break;
|
||||
default:
|
||||
sLog.outErrorDb("CreatureEventAI: Event %u Action %u have currently not checked at load action type (%u). Need check code update?", i, j + 1, temp.action[j].type);
|
||||
sLog.outErrorEventAI("Event %u Action %u have currently not checked at load action type (%u). Need check code update?", i, j + 1, temp.action[j].type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -832,16 +847,16 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
|
|||
delete result;
|
||||
|
||||
// post check
|
||||
for (uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i)
|
||||
for (uint32 i = 1; i < sCreatureStorage.GetMaxEntry(); ++i)
|
||||
{
|
||||
if (CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
|
||||
{
|
||||
bool ainame = strcmp(cInfo->AIName, "EventAI") == 0;
|
||||
bool hasevent = m_CreatureEventAI_Event_Map.find(i) != m_CreatureEventAI_Event_Map.end();
|
||||
if (ainame && !hasevent)
|
||||
sLog.outErrorDb("CreatureEventAI: EventAI not has script for creature entry (%u), but AIName = '%s'.", i, cInfo->AIName);
|
||||
sLog.outErrorEventAI("EventAI not has script for creature entry (%u), but AIName = '%s'.", i, cInfo->AIName);
|
||||
else if (!ainame && hasevent)
|
||||
sLog.outErrorDb("CreatureEventAI: EventAI has script for creature entry (%u), but AIName = '%s' instead 'EventAI'.", i, cInfo->AIName);
|
||||
sLog.outErrorEventAI("EventAI has script for creature entry (%u), but AIName = '%s' instead 'EventAI'.", i, cInfo->AIName);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -788,7 +788,7 @@ void LoadDBCStores(const std::string& dataPath)
|
|||
|
||||
// fix DK node at Ebon Hold
|
||||
if (i == 315)
|
||||
((TaxiNodesEntry*)node)->MountCreatureID[1] = 32981;
|
||||
(const_cast<TaxiNodesEntry*>(node))->MountCreatureID[1] = node->MountCreatureID[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -302,9 +302,9 @@ bool ChatHandler::HandleGPSCommand(char* args)
|
|||
zone_y = 0;
|
||||
}
|
||||
|
||||
TerrainInfo const* map = obj->GetTerrain();
|
||||
float ground_z = map->GetHeight(obj->GetPositionX(), obj->GetPositionY(), MAX_HEIGHT);
|
||||
float floor_z = map->GetHeight(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ());
|
||||
Map const* map = obj->GetMap();
|
||||
float ground_z = map->GetHeight(obj->GetPhaseMask(), obj->GetPositionX(), obj->GetPositionY(), MAX_HEIGHT);
|
||||
float floor_z = map->GetHeight(obj->GetPhaseMask(), obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ());
|
||||
|
||||
GridPair p = MaNGOS::ComputeGridPair(obj->GetPositionX(), obj->GetPositionY());
|
||||
|
||||
|
|
@ -314,9 +314,11 @@ bool ChatHandler::HandleGPSCommand(char* args)
|
|||
uint32 have_map = GridMap::ExistMap(obj->GetMapId(), gx, gy) ? 1 : 0;
|
||||
uint32 have_vmap = GridMap::ExistVMap(obj->GetMapId(), gx, gy) ? 1 : 0;
|
||||
|
||||
TerrainInfo const* terrain = obj->GetTerrain();
|
||||
|
||||
if (have_vmap)
|
||||
{
|
||||
if (map->IsOutdoors(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ()))
|
||||
if (terrain->IsOutdoors(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ()))
|
||||
PSendSysMessage("You are OUTdoor");
|
||||
else
|
||||
PSendSysMessage("You are INdoor");
|
||||
|
|
@ -347,11 +349,22 @@ bool ChatHandler::HandleGPSCommand(char* args)
|
|||
zone_x, zone_y, ground_z, floor_z, have_map, have_vmap);
|
||||
|
||||
GridMapLiquidData liquid_status;
|
||||
GridMapLiquidStatus res = map->getLiquidStatus(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), MAP_ALL_LIQUIDS, &liquid_status);
|
||||
GridMapLiquidStatus res = terrain->getLiquidStatus(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), MAP_ALL_LIQUIDS, &liquid_status);
|
||||
if (res)
|
||||
{
|
||||
PSendSysMessage(LANG_LIQUID_STATUS, liquid_status.level, liquid_status.depth_level, liquid_status.type, res);
|
||||
PSendSysMessage(LANG_LIQUID_STATUS, liquid_status.level, liquid_status.depth_level, liquid_status.type_flags, res);
|
||||
}
|
||||
|
||||
// Additional vmap debugging help
|
||||
#ifdef _DEBUG_VMAPS
|
||||
PSendSysMessage("Static terrain height (maps only): %f", obj->GetTerrain()->GetHeightStatic(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), false));
|
||||
|
||||
if (VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager())
|
||||
PSendSysMessage("Vmap Terrain Height %f", vmgr->getHeight(obj->GetMapId(), obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ() + 2.0f, 10000.0f));
|
||||
|
||||
PSendSysMessage("Static map height (maps and vmaps): %f", obj->GetTerrain()->GetHeightStatic(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ()));
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -492,7 +505,6 @@ bool ChatHandler::HandleGonameCommand(char* args)
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (target)
|
||||
{
|
||||
// check online security
|
||||
|
|
@ -642,6 +654,49 @@ bool ChatHandler::HandleRecallCommand(char* args)
|
|||
return HandleGoHelper(target, target->m_recallMap, target->m_recallX, target->m_recallY, &target->m_recallZ, &target->m_recallO);
|
||||
}
|
||||
|
||||
bool ChatHandler::HandleModifyHolyPowerCommand(char* args)
|
||||
{
|
||||
if (!*args)
|
||||
return false;
|
||||
|
||||
int32 power = atoi(args);
|
||||
|
||||
if (power < 0)
|
||||
{
|
||||
SendSysMessage(LANG_BAD_VALUE);
|
||||
SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
Player* chr = getSelectedPlayer();
|
||||
if (!chr)
|
||||
{
|
||||
SendSysMessage(LANG_NO_CHAR_SELECTED);
|
||||
SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// check online security
|
||||
if (HasLowerSecurity(chr))
|
||||
return false;
|
||||
|
||||
int32 maxPower = int32(chr->GetMaxPower(POWER_HOLY_POWER));
|
||||
if (power > maxPower)
|
||||
{
|
||||
SendSysMessage(LANG_BAD_VALUE);
|
||||
SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
PSendSysMessage(LANG_YOU_CHANGE_HOLY_POWER, GetNameLink(chr).c_str(), power, maxPower);
|
||||
if (needReportToTarget(chr))
|
||||
ChatHandler(chr).PSendSysMessage(LANG_YOURS_HOLY_POWER_CHANGED, GetNameLink().c_str(), power, maxPower);
|
||||
|
||||
chr->SetPower(POWER_HOLY_POWER, power);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Edit Player HP
|
||||
bool ChatHandler::HandleModifyHPCommand(char* args)
|
||||
{
|
||||
|
|
@ -1519,15 +1574,20 @@ bool ChatHandler::HandleModifyMoneyCommand(char* args)
|
|||
if (HasLowerSecurity(chr))
|
||||
return false;
|
||||
|
||||
int32 addmoney = atoi(args);
|
||||
int64 addmoney;
|
||||
if (!ExtractInt64(&args, addmoney))
|
||||
return false;
|
||||
|
||||
uint32 moneyuser = chr->GetMoney();
|
||||
uint64 moneyuser = chr->GetMoney();
|
||||
|
||||
std::stringstream absadd; absadd << abs(addmoney);
|
||||
std::stringstream add; add << addmoney;
|
||||
|
||||
if (addmoney < 0)
|
||||
{
|
||||
int32 newmoney = int32(moneyuser) + addmoney;
|
||||
|
||||
DETAIL_LOG(GetMangosString(LANG_CURRENT_MONEY), moneyuser, addmoney, newmoney);
|
||||
int64 newmoney = int64(moneyuser) + addmoney;
|
||||
DETAIL_LOG("USER1: %s, ADD: %s, DIF: %s",
|
||||
MoneyToString(moneyuser).c_str(), MoneyToString(addmoney).c_str(), MoneyToString(newmoney).c_str());
|
||||
if (newmoney <= 0)
|
||||
{
|
||||
PSendSysMessage(LANG_YOU_TAKE_ALL_MONEY, GetNameLink(chr).c_str());
|
||||
|
|
@ -1541,17 +1601,17 @@ bool ChatHandler::HandleModifyMoneyCommand(char* args)
|
|||
if (newmoney > MAX_MONEY_AMOUNT)
|
||||
newmoney = MAX_MONEY_AMOUNT;
|
||||
|
||||
PSendSysMessage(LANG_YOU_TAKE_MONEY, abs(addmoney), GetNameLink(chr).c_str());
|
||||
PSendSysMessage(LANG_YOU_TAKE_MONEY, MoneyToString(abs(addmoney)).c_str(), GetNameLink(chr).c_str());
|
||||
if (needReportToTarget(chr))
|
||||
ChatHandler(chr).PSendSysMessage(LANG_YOURS_MONEY_TAKEN, GetNameLink().c_str(), abs(addmoney));
|
||||
ChatHandler(chr).PSendSysMessage(LANG_YOURS_MONEY_TAKEN, GetNameLink().c_str(), MoneyToString(abs(addmoney)).c_str());
|
||||
chr->SetMoney(newmoney);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PSendSysMessage(LANG_YOU_GIVE_MONEY, addmoney, GetNameLink(chr).c_str());
|
||||
PSendSysMessage(LANG_YOU_GIVE_MONEY, MoneyToString(addmoney).c_str(), GetNameLink(chr).c_str());
|
||||
if (needReportToTarget(chr))
|
||||
ChatHandler(chr).PSendSysMessage(LANG_YOURS_MONEY_GIVEN, GetNameLink().c_str(), addmoney);
|
||||
ChatHandler(chr).PSendSysMessage(LANG_YOURS_MONEY_GIVEN, GetNameLink().c_str(), MoneyToString(addmoney).c_str());
|
||||
|
||||
if (addmoney >= MAX_MONEY_AMOUNT)
|
||||
chr->SetMoney(MAX_MONEY_AMOUNT);
|
||||
|
|
@ -1559,7 +1619,8 @@ bool ChatHandler::HandleModifyMoneyCommand(char* args)
|
|||
chr->ModifyMoney(addmoney);
|
||||
}
|
||||
|
||||
DETAIL_LOG(GetMangosString(LANG_NEW_MONEY), moneyuser, addmoney, chr->GetMoney());
|
||||
DETAIL_LOG("USER2: %s, ADD: %s, RESULT: %s\n",
|
||||
MoneyToString(moneyuser).c_str(), MoneyToString(addmoney).c_str(), MoneyToString(chr->GetMoney()).c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -2090,8 +2151,6 @@ bool ChatHandler::HandleGoCommand(char* args)
|
|||
return HandleGoHelper(_player, mapid, x, y, &z);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// teleport at coordinates
|
||||
bool ChatHandler::HandleGoXYCommand(char* args)
|
||||
{
|
||||
|
|
|
|||
108
src/game/Map.cpp
108
src/game/Map.cpp
|
|
@ -37,6 +37,7 @@
|
|||
#include "VMapFactory.h"
|
||||
#include "MoveMap.h"
|
||||
#include "BattleGround/BattleGroundMgr.h"
|
||||
#include "Calendar.h"
|
||||
|
||||
Map::~Map()
|
||||
{
|
||||
|
|
@ -434,6 +435,8 @@ bool Map::loaded(const GridPair& p) const
|
|||
|
||||
void Map::Update(const uint32& t_diff)
|
||||
{
|
||||
m_dyn_tree.update(t_diff);
|
||||
|
||||
/// update worldsessions for existing players
|
||||
for (m_mapRefIter = m_mapRefManager.begin(); m_mapRefIter != m_mapRefManager.end(); ++m_mapRefIter)
|
||||
{
|
||||
|
|
@ -1338,6 +1341,7 @@ bool DungeonMap::Add(Player* player)
|
|||
data << uint32(0);
|
||||
player->GetSession()->SendPacket(&data);
|
||||
player->BindToInstance(GetPersistanceState(), true);
|
||||
sCalendarMgr.SendCalendarRaidLockoutAdd(player, GetPersistanceState());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1444,6 +1448,7 @@ void DungeonMap::PermBindAllPlayers(Player* player)
|
|||
WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4);
|
||||
data << uint32(0);
|
||||
plr->GetSession()->SendPacket(&data);
|
||||
sCalendarMgr.SendCalendarRaidLockoutAdd(plr, GetPersistanceState());
|
||||
}
|
||||
|
||||
// if the leader is not in the instance the group will not get a perm bind
|
||||
|
|
@ -1490,7 +1495,6 @@ DungeonPersistentState* DungeonMap::GetPersistanceState() const
|
|||
return (DungeonPersistentState*)Map::GetPersistentState();
|
||||
}
|
||||
|
||||
|
||||
/* ******* Battleground Instance Maps ******* */
|
||||
|
||||
BattleGroundMap::BattleGroundMap(uint32 id, time_t expiry, uint32 InstanceId, uint8 spawnMode)
|
||||
|
|
@ -1516,7 +1520,6 @@ BattleGroundPersistentState* BattleGroundMap::GetPersistanceState() const
|
|||
return (BattleGroundPersistentState*)Map::GetPersistentState();
|
||||
}
|
||||
|
||||
|
||||
void BattleGroundMap::InitVisibilityDistance()
|
||||
{
|
||||
// init visibility distance for BG/Arenas
|
||||
|
|
@ -1587,8 +1590,10 @@ bool Map::CanEnter(Player* player)
|
|||
}
|
||||
|
||||
/// Put scripts in the execution queue
|
||||
bool Map::ScriptsStart(ScriptMapMapName const& scripts, uint32 id, Object* source, Object* target)
|
||||
bool Map::ScriptsStart(ScriptMapMapName const& scripts, uint32 id, Object* source, Object* target, ScriptExecutionParam execParams /*=SCRIPT_EXEC_PARAM_UNIQUE_BY_SOURCE_TARGET*/)
|
||||
{
|
||||
MANGOS_ASSERT(source);
|
||||
|
||||
///- Find the script map
|
||||
ScriptMapMap::const_iterator s = scripts.second.find(id);
|
||||
if (s == scripts.second.end())
|
||||
|
|
@ -1599,6 +1604,20 @@ bool Map::ScriptsStart(ScriptMapMapName const& scripts, uint32 id, Object* sourc
|
|||
ObjectGuid targetGuid = target ? target->GetObjectGuid() : ObjectGuid();
|
||||
ObjectGuid ownerGuid = source->isType(TYPEMASK_ITEM) ? ((Item*)source)->GetOwnerGuid() : ObjectGuid();
|
||||
|
||||
if (execParams) // Check if the execution should be uniquely
|
||||
{
|
||||
for (ScriptScheduleMap::const_iterator searchItr = m_scriptSchedule.begin(); searchItr != m_scriptSchedule.end(); ++searchItr)
|
||||
{
|
||||
if (searchItr->second.IsSameScript(scripts.first, id,
|
||||
execParams & SCRIPT_EXEC_PARAM_UNIQUE_BY_SOURCE ? sourceGuid : ObjectGuid(),
|
||||
execParams & SCRIPT_EXEC_PARAM_UNIQUE_BY_TARGET ? targetGuid : ObjectGuid(), ownerGuid))
|
||||
{
|
||||
DEBUG_LOG("DB-SCRIPTS: Process table `%s` id %u. Skip script as script already started for source %s, target %s - ScriptsStartParams %u", scripts.first, id, sourceGuid.GetString().c_str(), targetGuid.GetString().c_str(), execParams);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///- Schedule script execution for all scripts in the script map
|
||||
ScriptMap const* s2 = &(s->second);
|
||||
for (ScriptMap::const_iterator iter = s2->begin(); iter != s2->end(); ++iter)
|
||||
|
|
@ -1640,13 +1659,34 @@ void Map::ScriptsProcess()
|
|||
// ok as multimap is a *sorted* associative container
|
||||
while (!m_scriptSchedule.empty() && (iter->first <= sWorld.GetGameTime()))
|
||||
{
|
||||
iter->second.HandleScriptStep();
|
||||
if (iter->second.HandleScriptStep())
|
||||
{
|
||||
// Terminate following script steps of this script
|
||||
const char* tableName = iter->second.GetTableName();
|
||||
uint32 id = iter->second.GetId();
|
||||
ObjectGuid sourceGuid = iter->second.GetSourceGuid();
|
||||
ObjectGuid targetGuid = iter->second.GetTargetGuid();
|
||||
ObjectGuid ownerGuid = iter->second.GetOwnerGuid();
|
||||
|
||||
for (ScriptScheduleMap::iterator rmItr = m_scriptSchedule.begin(); rmItr != m_scriptSchedule.end();)
|
||||
{
|
||||
if (rmItr->second.IsSameScript(tableName, id, sourceGuid, targetGuid, ownerGuid))
|
||||
{
|
||||
m_scriptSchedule.erase(rmItr++);
|
||||
sScriptMgr.DecreaseScheduledScriptCount();
|
||||
}
|
||||
else
|
||||
++rmItr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_scriptSchedule.erase(iter);
|
||||
iter = m_scriptSchedule.begin();
|
||||
|
||||
sScriptMgr.DecreaseScheduledScriptCount();
|
||||
}
|
||||
iter = m_scriptSchedule.begin();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1850,7 +1890,6 @@ class StaticMonsterChatBuilder
|
|||
Unit* i_target;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Function simulates yell of creature
|
||||
*
|
||||
|
|
@ -1879,7 +1918,6 @@ void Map::MonsterYellToMap(ObjectGuid guid, int32 textId, uint32 language, Unit*
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function simulates yell of creature
|
||||
*
|
||||
|
|
@ -1921,18 +1959,60 @@ void Map::PlayDirectSoundToMap(uint32 soundId, uint32 zoneId /*=0*/)
|
|||
/**
|
||||
* Function to check if a point is in line of sight from an other point
|
||||
*/
|
||||
bool Map::IsInLineOfSight(float srcX, float srcY, float srcZ, float destX, float destY, float destZ)
|
||||
bool Map::IsInLineOfSight(float srcX, float srcY, float srcZ, float destX, float destY, float destZ, uint32 phasemask) const
|
||||
{
|
||||
VMAP::IVMapManager* vMapManager = VMAP::VMapFactory::createOrGetVMapManager();
|
||||
return vMapManager->isInLineOfSight(GetId(), srcX, srcY, srcZ, destX, destY, destZ);
|
||||
return VMAP::VMapFactory::createOrGetVMapManager()->isInLineOfSight(GetId(), srcX, srcY, srcZ, destX, destY, destZ)
|
||||
&& m_dyn_tree.isInLineOfSight(srcX, srcY, srcZ, destX, destY, destZ, phasemask);
|
||||
}
|
||||
|
||||
/**
|
||||
* get the hit position and return true if we hit something
|
||||
* get the hit position and return true if we hit something (in this case the dest position will hold the hit-position)
|
||||
* otherwise the result pos will be the dest pos
|
||||
*/
|
||||
bool Map::GetObjectHitPos(float srcX, float srcY, float srcZ, float destX, float destY, float destZ, float& resX, float& resY, float& resZ, float pModifyDist)
|
||||
bool Map::GetHitPosition(float srcX, float srcY, float srcZ, float& destX, float& destY, float& destZ, uint32 phasemask, float modifyDist) const
|
||||
{
|
||||
VMAP::IVMapManager* vMapManager = VMAP::VMapFactory::createOrGetVMapManager();
|
||||
return vMapManager->getObjectHitPos(GetId(), srcX, srcY, srcZ, destX, destY, destZ, resX, resY, resZ, pModifyDist);
|
||||
// at first check all static objects
|
||||
float tempX, tempY, tempZ = 0.0f;
|
||||
bool result0 = VMAP::VMapFactory::createOrGetVMapManager()->getObjectHitPos(GetId(), srcX, srcY, srcZ, destX, destY, destZ, tempX, tempY, tempZ, modifyDist);
|
||||
if (result0)
|
||||
{
|
||||
DEBUG_LOG("Map::GetHitPosition vmaps corrects gained with static objects! new dest coords are X:%f Y:%f Z:%f", destX, destY, destZ);
|
||||
destX = tempX;
|
||||
destY = tempY;
|
||||
destZ = tempZ;
|
||||
}
|
||||
// at second all dynamic objects, if static check has an hit, then we can calculate only to this closer point
|
||||
bool result1 = m_dyn_tree.getObjectHitPos(phasemask, srcX, srcY, srcZ, destX, destY, destZ, tempX, tempY, tempZ, modifyDist);
|
||||
if (result1)
|
||||
{
|
||||
DEBUG_LOG("Map::GetHitPosition vmaps corrects gained with dynamic objects! new dest coords are X:%f Y:%f Z:%f", destX, destY, destZ);
|
||||
destX = tempX;
|
||||
destY = tempY;
|
||||
destZ = tempZ;
|
||||
}
|
||||
return result0 || result1;
|
||||
}
|
||||
|
||||
float Map::GetHeight(uint32 phasemask, float x, float y, float z) const
|
||||
{
|
||||
float staticHeight = m_TerrainData->GetHeightStatic(x, y, z);
|
||||
|
||||
// Get Dynamic Height around static Height (if valid)
|
||||
float dynSearchHeight = 2.0f + (z < staticHeight ? staticHeight : z);
|
||||
return std::max<float>(staticHeight, m_dyn_tree.getHeight(x, y, dynSearchHeight, dynSearchHeight - staticHeight, phasemask));
|
||||
}
|
||||
|
||||
void Map::InsertGameObjectModel(const GameObjectModel& mdl)
|
||||
{
|
||||
m_dyn_tree.insert(mdl);
|
||||
}
|
||||
|
||||
void Map::RemoveGameObjectModel(const GameObjectModel& mdl)
|
||||
{
|
||||
m_dyn_tree.remove(mdl);
|
||||
}
|
||||
|
||||
bool Map::ContainsGameObjectModel(const GameObjectModel& mdl) const
|
||||
{
|
||||
return m_dyn_tree.contains(mdl);
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -23,39 +23,77 @@
|
|||
* @file Vehicle.cpp
|
||||
* This file contains the code needed for CMaNGOS to support vehicles
|
||||
* Currently implemented
|
||||
* - TODO Board
|
||||
* - TODO Unboard
|
||||
* - TODO Switch
|
||||
* - Board to board a passenger onto a vehicle (includes checks)
|
||||
* - Unboard to unboard a passenger from the vehicle
|
||||
* - SwitchSeat to switch to another seat of the same vehicle
|
||||
* - CanBoard to check if a passenger can board a vehicle
|
||||
* - Internal helper to set the controlling and spells for a vehicle's seat
|
||||
* - Internal helper to control the available seats of a vehicle
|
||||
*/
|
||||
|
||||
#include "Vehicle.h"
|
||||
#include "Common.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Log.h"
|
||||
#include "Unit.h"
|
||||
#include "Creature.h"
|
||||
#include "CreatureAI.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Vehicle.h"
|
||||
#include "SQLStorages.h"
|
||||
#include "Util.h"
|
||||
#include "movement/MoveSplineInit.h"
|
||||
#include "movement/MoveSpline.h"
|
||||
#include "MapManager.h"
|
||||
#include "TemporarySummon.h"
|
||||
|
||||
void ObjectMgr::LoadVehicleAccessory()
|
||||
{
|
||||
sVehicleAccessoryStorage.Load();
|
||||
|
||||
sLog.outString(">> Loaded %u vehicle accessories", sVehicleAccessoryStorage.GetRecordCount());
|
||||
sLog.outString();
|
||||
|
||||
// Check content
|
||||
for (SQLMultiStorage::SQLSIterator<VehicleAccessory> itr = sVehicleAccessoryStorage.getDataBegin<VehicleAccessory>(); itr < sVehicleAccessoryStorage.getDataEnd<VehicleAccessory>(); ++itr)
|
||||
{
|
||||
if (!sCreatureStorage.LookupEntry<CreatureInfo>(itr->vehicleEntry))
|
||||
{
|
||||
sLog.outErrorDb("Table `vehicle_accessory` has entry (vehicle entry: %u, seat %u, passenger %u) where vehicle_entry is invalid, skip vehicle.", itr->vehicleEntry, itr->seatId, itr->passengerEntry);
|
||||
sVehicleAccessoryStorage.EraseEntry(itr->vehicleEntry);
|
||||
continue;
|
||||
}
|
||||
if (!sCreatureStorage.LookupEntry<CreatureInfo>(itr->passengerEntry))
|
||||
{
|
||||
sLog.outErrorDb("Table `vehicle_accessory` has entry (vehicle entry: %u, seat %u, passenger %u) where accessory_entry is invalid, skip vehicle.", itr->vehicleEntry, itr->seatId, itr->passengerEntry);
|
||||
sVehicleAccessoryStorage.EraseEntry(itr->vehicleEntry);
|
||||
continue;
|
||||
}
|
||||
if (itr->seatId >= MAX_VEHICLE_SEAT)
|
||||
{
|
||||
sLog.outErrorDb("Table `vehicle_accessory` has entry (vehicle entry: %u, seat %u, passenger %u) where seat is invalid (must be between 0 and %u), skip vehicle.", itr->vehicleEntry, itr->seatId, itr->passengerEntry, MAX_VEHICLE_SEAT - 1);
|
||||
sVehicleAccessoryStorage.EraseEntry(itr->vehicleEntry);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor of VehicleInfo
|
||||
*
|
||||
* @param owner MUST be provided owner of the vehicle (type Unit)
|
||||
* @param vehicleEntry MUST be provided dbc-entry of the vehicle
|
||||
* @param overwriteNpcEntry Use to overwrite the GetEntry() result for selecting associated passengers
|
||||
*
|
||||
* This function will initialise the VehicleInfo of the vehicle owner
|
||||
* Also the seat-map is created here
|
||||
*/
|
||||
VehicleInfo::VehicleInfo(Unit* owner, VehicleEntry const* vehicleEntry) : TransportBase(owner),
|
||||
VehicleInfo::VehicleInfo(Unit* owner, VehicleEntry const* vehicleEntry, uint32 overwriteNpcEntry) : TransportBase(owner),
|
||||
m_vehicleEntry(vehicleEntry),
|
||||
m_creatureSeats(0),
|
||||
m_playerSeats(0)
|
||||
m_playerSeats(0),
|
||||
m_overwriteNpcEntry(overwriteNpcEntry),
|
||||
m_isInitialized(false)
|
||||
{
|
||||
MANGOS_ASSERT(vehicleEntry);
|
||||
|
||||
|
|
@ -78,6 +116,33 @@ VehicleInfo::VehicleInfo(Unit* owner, VehicleEntry const* vehicleEntry) : Transp
|
|||
}
|
||||
}
|
||||
|
||||
VehicleInfo::~VehicleInfo()
|
||||
{
|
||||
((Unit*)m_owner)->RemoveSpellsCausingAura(SPELL_AURA_CONTROL_VEHICLE);
|
||||
|
||||
RemoveAccessoriesFromMap(); // Remove accessories (for example required with player vehicles)
|
||||
}
|
||||
|
||||
void VehicleInfo::Initialize()
|
||||
{
|
||||
if (!m_overwriteNpcEntry)
|
||||
m_overwriteNpcEntry = m_owner->GetEntry();
|
||||
|
||||
// Loading passengers (rough version only!)
|
||||
SQLMultiStorage::SQLMSIteratorBounds<VehicleAccessory> bounds = sVehicleAccessoryStorage.getBounds<VehicleAccessory>(m_overwriteNpcEntry);
|
||||
for (SQLMultiStorage::SQLMultiSIterator<VehicleAccessory> itr = bounds.first; itr != bounds.second; ++itr)
|
||||
{
|
||||
if (Creature* summoned = m_owner->SummonCreature(itr->passengerEntry, m_owner->GetPositionX(), m_owner->GetPositionY(), m_owner->GetPositionZ(), 2 * m_owner->GetOrientation(), TEMPSUMMON_DEAD_DESPAWN, 0))
|
||||
{
|
||||
DEBUG_LOG("VehicleInfo(of %s)::Initialize: Load vehicle accessory %s onto seat %u", m_owner->GetGuidStr().c_str(), summoned->GetGuidStr().c_str(), itr->seatId);
|
||||
m_accessoryGuids.insert(summoned->GetObjectGuid());
|
||||
int32 basepoint0 = itr->seatId + 1;
|
||||
summoned->CastCustomSpell((Unit*)m_owner, SPELL_RIDE_VEHICLE_HARDCODED, &basepoint0, NULL, NULL, true);
|
||||
}
|
||||
}
|
||||
m_isInitialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will board a passenger onto a vehicle
|
||||
*
|
||||
|
|
@ -88,11 +153,68 @@ void VehicleInfo::Board(Unit* passenger, uint8 seat)
|
|||
{
|
||||
MANGOS_ASSERT(passenger);
|
||||
|
||||
DEBUG_LOG("VehicleInfo::Board: Try to board passenger %s to seat %u", passenger->GetObjectGuid().GetString().c_str(), seat);
|
||||
DEBUG_LOG("VehicleInfo(of %s)::Board: Try to board passenger %s to seat %u", m_owner->GetGuidStr().c_str(), passenger->GetGuidStr().c_str(), seat);
|
||||
|
||||
// This check is also called in Spell::CheckCast()
|
||||
if (!CanBoard(passenger))
|
||||
return;
|
||||
|
||||
// Use the planned seat only if the seat is valid, possible to choose and empty
|
||||
if (!IsSeatAvailableFor(passenger, seat))
|
||||
if (!GetUsableSeatFor(passenger, seat))
|
||||
return;
|
||||
|
||||
VehicleSeatEntry const* seatEntry = GetSeatEntry(seat);
|
||||
MANGOS_ASSERT(seatEntry);
|
||||
|
||||
// ToDo: Unboard passenger from a MOTransport when they are properly implemented
|
||||
/*if (TransportInfo* transportInfo = passenger->GetTransportInfo())
|
||||
{
|
||||
WorldObject* transporter = transportInfo->GetTransport();
|
||||
|
||||
// Must be a MO transporter
|
||||
MANGOS_ASSERT(transporter->GetObjectGuid().IsMOTransport());
|
||||
|
||||
((Transport*)transporter)->UnBoardPassenger(passenger);
|
||||
}*/
|
||||
|
||||
DEBUG_LOG("VehicleInfo::Board: Board passenger: %s to seat %u", passenger->GetGuidStr().c_str(), seat);
|
||||
|
||||
// Calculate passengers local position
|
||||
float lx, ly, lz, lo;
|
||||
CalculateBoardingPositionOf(passenger->GetPositionX(), passenger->GetPositionY(), passenger->GetPositionZ(), passenger->GetOrientation(), lx, ly, lz, lo);
|
||||
|
||||
BoardPassenger(passenger, lx, ly, lz, lo, seat); // Use TransportBase to store the passenger
|
||||
|
||||
// Set data for createobject packets
|
||||
passenger->m_movementInfo.SetTransportData(m_owner->GetObjectGuid(), lx, ly, lz, lo, 0, seat);
|
||||
|
||||
if (passenger->GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
Player* pPlayer = (Player*)passenger;
|
||||
pPlayer->RemovePet(PET_SAVE_AS_CURRENT);
|
||||
|
||||
WorldPacket data(SMSG_ON_CANCEL_EXPECTED_RIDE_VEHICLE_AURA);
|
||||
pPlayer->GetSession()->SendPacket(&data);
|
||||
|
||||
// SMSG_BREAK_TARGET (?)
|
||||
}
|
||||
|
||||
if (!passenger->IsRooted())
|
||||
passenger->SetRoot(true);
|
||||
|
||||
Movement::MoveSplineInit init(*passenger);
|
||||
init.MoveTo(0.0f, 0.0f, 0.0f); // ToDo: Set correct local coords
|
||||
init.SetFacing(0.0f); // local orientation ? ToDo: Set proper orientation!
|
||||
init.SetBoardVehicle();
|
||||
init.Launch();
|
||||
|
||||
// Apply passenger modifications
|
||||
ApplySeatMods(passenger, seatEntry->m_flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will switch the seat of a passenger
|
||||
* This function will switch the seat of a passenger on the same vehicle
|
||||
*
|
||||
* @param passenger MUST be provided. This Unit will change its seat on the vehicle
|
||||
* @param seat Seat to which the passenger will be switched
|
||||
|
|
@ -100,6 +222,50 @@ void VehicleInfo::Board(Unit* passenger, uint8 seat)
|
|||
void VehicleInfo::SwitchSeat(Unit* passenger, uint8 seat)
|
||||
{
|
||||
MANGOS_ASSERT(passenger);
|
||||
|
||||
DEBUG_LOG("VehicleInfo::SwitchSeat: passenger: %s try to switch to seat %u", passenger->GetGuidStr().c_str(), seat);
|
||||
|
||||
// Switching seats is not possible
|
||||
if (m_vehicleEntry->m_flags & VEHICLE_FLAG_DISABLE_SWITCH)
|
||||
return;
|
||||
|
||||
PassengerMap::const_iterator itr = m_passengers.find(passenger);
|
||||
MANGOS_ASSERT(itr != m_passengers.end());
|
||||
|
||||
// We are already boarded to this seat
|
||||
if (itr->second->GetTransportSeat() == seat)
|
||||
return;
|
||||
|
||||
// Check if it's a valid seat
|
||||
if (!IsSeatAvailableFor(passenger, seat))
|
||||
return;
|
||||
|
||||
VehicleSeatEntry const* seatEntry = GetSeatEntry(itr->second->GetTransportSeat());
|
||||
MANGOS_ASSERT(seatEntry);
|
||||
|
||||
// Switching seats is only allowed if this flag is set
|
||||
if (~seatEntry->m_flags & SEAT_FLAG_CAN_SWITCH)
|
||||
return;
|
||||
|
||||
// Remove passenger modifications of the old seat
|
||||
RemoveSeatMods(passenger, seatEntry->m_flags);
|
||||
|
||||
// Set to new seat
|
||||
itr->second->SetTransportSeat(seat);
|
||||
|
||||
Movement::MoveSplineInit init(*passenger);
|
||||
init.MoveTo(0.0f, 0.0f, 0.0f); // ToDo: Set correct local coords
|
||||
//if (oldorientation != neworientation) (?)
|
||||
//init.SetFacing(0.0f); // local orientation ? ToDo: Set proper orientation!
|
||||
// It seems that Seat switching is sent without SplineFlag BoardVehicle
|
||||
init.Launch();
|
||||
|
||||
// Get seatEntry of new seat
|
||||
seatEntry = GetSeatEntry(seat);
|
||||
MANGOS_ASSERT(seatEntry);
|
||||
|
||||
// Apply passenger modifications of the new seat
|
||||
ApplySeatMods(passenger, seatEntry->m_flags);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -113,7 +279,63 @@ void VehicleInfo::UnBoard(Unit* passenger, bool changeVehicle)
|
|||
{
|
||||
MANGOS_ASSERT(passenger);
|
||||
|
||||
DEBUG_LOG("VehicleInfo::Unboard: passenger: %s", passenger->GetObjectGuid().GetString().c_str());
|
||||
DEBUG_LOG("VehicleInfo::Unboard: passenger: %s", passenger->GetGuidStr().c_str());
|
||||
|
||||
PassengerMap::const_iterator itr = m_passengers.find(passenger);
|
||||
MANGOS_ASSERT(itr != m_passengers.end());
|
||||
|
||||
VehicleSeatEntry const* seatEntry = GetSeatEntry(itr->second->GetTransportSeat());
|
||||
MANGOS_ASSERT(seatEntry);
|
||||
|
||||
UnBoardPassenger(passenger); // Use TransportBase to remove the passenger from storage list
|
||||
|
||||
if (!changeVehicle) // Send expected unboarding packages
|
||||
{
|
||||
// Update movementInfo
|
||||
passenger->m_movementInfo.ClearTransportData();
|
||||
|
||||
if (passenger->GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
Player* pPlayer = (Player*)passenger;
|
||||
pPlayer->ResummonPetTemporaryUnSummonedIfAny();
|
||||
|
||||
// SMSG_PET_DISMISS_SOUND (?)
|
||||
}
|
||||
|
||||
if (passenger->IsRooted())
|
||||
passenger->SetRoot(false);
|
||||
|
||||
Movement::MoveSplineInit init(*passenger);
|
||||
// ToDo: Set proper unboard coordinates
|
||||
init.MoveTo(m_owner->GetPositionX(), m_owner->GetPositionY(), m_owner->GetPositionZ());
|
||||
init.SetExitVehicle();
|
||||
init.Launch();
|
||||
|
||||
// Despawn if passenger was accessory
|
||||
if (passenger->GetTypeId() == TYPEID_UNIT && m_accessoryGuids.find(passenger->GetObjectGuid()) != m_accessoryGuids.end())
|
||||
{
|
||||
Creature* cPassenger = static_cast<Creature*>(passenger);
|
||||
// TODO Same TODO as in VehicleInfo::RemoveAccessoriesFromMap
|
||||
cPassenger->ForcedDespawn(5000);
|
||||
m_accessoryGuids.erase(passenger->GetObjectGuid());
|
||||
}
|
||||
}
|
||||
|
||||
// Remove passenger modifications
|
||||
RemoveSeatMods(passenger, seatEntry->m_flags);
|
||||
|
||||
// Some creature vehicles get despawned after passenger unboarding
|
||||
if (m_owner->GetTypeId() == TYPEID_UNIT)
|
||||
{
|
||||
// TODO: Guesswork, but seems to be fairly near correct
|
||||
// Only if the passenger was on control seat? Also depending on some flags
|
||||
if ((seatEntry->m_flags & SEAT_FLAG_CAN_CONTROL) &&
|
||||
!(m_vehicleEntry->m_flags & (VEHICLE_FLAG_UNK4 | VEHICLE_FLAG_UNK20)))
|
||||
{
|
||||
if (((Creature*)m_owner)->IsTemporarySummon())
|
||||
((Creature*)m_owner)->ForcedDespawn(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -130,6 +352,14 @@ bool VehicleInfo::CanBoard(Unit* passenger) const
|
|||
if (passenger == m_owner)
|
||||
return false;
|
||||
|
||||
// Passenger is already on this vehicle (in this case switching seats is required)
|
||||
if (passenger->IsBoarded() && passenger->GetTransportInfo()->GetTransport() == m_owner)
|
||||
return false;
|
||||
|
||||
// Prevent circular boarding: passenger (could only be vehicle) must not have m_owner on board
|
||||
if (passenger->IsVehicle() && passenger->GetVehicleInfo()->HasOnBoard(m_owner))
|
||||
return false;
|
||||
|
||||
// Check if we have at least one empty seat
|
||||
if (!GetEmptySeats())
|
||||
return false;
|
||||
|
|
@ -146,8 +376,17 @@ bool VehicleInfo::CanBoard(Unit* passenger) const
|
|||
return GetEmptySeatsMask() & m_creatureSeats;
|
||||
}
|
||||
|
||||
Unit* VehicleInfo::GetPassenger(uint8 seat) const
|
||||
{
|
||||
for (PassengerMap::const_iterator itr = m_passengers.begin(); itr != m_passengers.end(); ++itr)
|
||||
if (itr->second->GetTransportSeat() == seat)
|
||||
return (Unit*)itr->first;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Helper function to undo the turning of the vehicle to calculate a relative position of the passenger when boarding
|
||||
void VehicleInfo::CalculateBoardingPositionOf(float gx, float gy, float gz, float go, float &lx, float &ly, float &lz, float &lo)
|
||||
void VehicleInfo::CalculateBoardingPositionOf(float gx, float gy, float gz, float go, float& lx, float& ly, float& lz, float& lo) const
|
||||
{
|
||||
NormalizeRotatedPosition(gx - m_owner->GetPositionX(), gy - m_owner->GetPositionY(), lx, ly);
|
||||
|
||||
|
|
@ -155,6 +394,21 @@ void VehicleInfo::CalculateBoardingPositionOf(float gx, float gy, float gz, floa
|
|||
lo = NormalizeOrientation(go - m_owner->GetOrientation());
|
||||
}
|
||||
|
||||
void VehicleInfo::RemoveAccessoriesFromMap()
|
||||
{
|
||||
// Remove all accessories
|
||||
for (GuidSet::const_iterator itr = m_accessoryGuids.begin(); itr != m_accessoryGuids.end(); ++itr)
|
||||
{
|
||||
if (Creature* pAccessory = m_owner->GetMap()->GetCreature(*itr))
|
||||
{
|
||||
// TODO - unclear how long to despawn, also maybe some flag etc depending
|
||||
pAccessory->ForcedDespawn(5000);
|
||||
}
|
||||
}
|
||||
m_accessoryGuids.clear();
|
||||
m_isInitialized = false;
|
||||
}
|
||||
|
||||
/* ************************************************************************************************
|
||||
* Helper function for seat control
|
||||
* ***********************************************************************************************/
|
||||
|
|
@ -221,11 +475,102 @@ bool VehicleInfo:: IsUsableSeatForPlayer(uint32 seatFlags) const
|
|||
/// Add control and such modifiers to a passenger if required
|
||||
void VehicleInfo::ApplySeatMods(Unit* passenger, uint32 seatFlags)
|
||||
{
|
||||
Unit* pVehicle = (Unit*)m_owner; // Vehicles are alawys Unit
|
||||
|
||||
if (passenger->GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
Player* pPlayer = (Player*)passenger;
|
||||
|
||||
if (seatFlags & SEAT_FLAG_CAN_CONTROL)
|
||||
{
|
||||
pPlayer->GetCamera().SetView(pVehicle);
|
||||
|
||||
pPlayer->SetCharm(pVehicle);
|
||||
pVehicle->SetCharmerGuid(pPlayer->GetObjectGuid());
|
||||
|
||||
pVehicle->addUnitState(UNIT_STAT_CONTROLLED);
|
||||
pVehicle->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED);
|
||||
|
||||
pPlayer->SetClientControl(pVehicle, 1);
|
||||
pPlayer->SetMover(pVehicle);
|
||||
|
||||
// Unconfirmed - default speed handling
|
||||
if (pVehicle->GetTypeId() == TYPEID_UNIT)
|
||||
{
|
||||
if (!pPlayer->IsWalking() && pVehicle->IsWalking())
|
||||
{
|
||||
((Creature*)pVehicle)->SetWalk(false, true);
|
||||
}
|
||||
else if (pPlayer->IsWalking() && !pVehicle->IsWalking())
|
||||
{
|
||||
((Creature*)pVehicle)->SetWalk(true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (seatFlags & (SEAT_FLAG_USABLE | SEAT_FLAG_CAN_CAST))
|
||||
{
|
||||
CharmInfo* charmInfo = pVehicle->InitCharmInfo(pVehicle);
|
||||
charmInfo->InitVehicleCreateSpells();
|
||||
|
||||
pPlayer->PossessSpellInitialize();
|
||||
}
|
||||
}
|
||||
else if (passenger->GetTypeId() == TYPEID_UNIT)
|
||||
{
|
||||
if (seatFlags & SEAT_FLAG_CAN_CONTROL)
|
||||
{
|
||||
passenger->SetCharm(pVehicle);
|
||||
pVehicle->SetCharmerGuid(passenger->GetObjectGuid());
|
||||
}
|
||||
|
||||
((Creature*)passenger)->AI()->SetCombatMovement(false);
|
||||
// Not entirely sure how this must be handled in relation to CONTROL
|
||||
// But in any way this at least would require some changes in the movement system most likely
|
||||
passenger->GetMotionMaster()->Clear(false, true);
|
||||
passenger->GetMotionMaster()->MoveIdle();
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove control and such modifiers to a passenger if they were added
|
||||
void VehicleInfo::RemoveSeatMods(Unit* passenger, uint32 seatFlags)
|
||||
{
|
||||
Unit* pVehicle = (Unit*)m_owner;
|
||||
|
||||
if (passenger->GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
Player* pPlayer = (Player*)passenger;
|
||||
|
||||
if (seatFlags & SEAT_FLAG_CAN_CONTROL)
|
||||
{
|
||||
pPlayer->SetCharm(NULL);
|
||||
pVehicle->SetCharmerGuid(ObjectGuid());
|
||||
|
||||
pPlayer->SetClientControl(pVehicle, 0);
|
||||
pPlayer->SetMover(NULL);
|
||||
|
||||
pVehicle->clearUnitState(UNIT_STAT_CONTROLLED);
|
||||
pVehicle->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED);
|
||||
|
||||
// must be called after movement control unapplying
|
||||
pPlayer->GetCamera().ResetView();
|
||||
}
|
||||
|
||||
if (seatFlags & (SEAT_FLAG_USABLE | SEAT_FLAG_CAN_CAST))
|
||||
pPlayer->RemovePetActionBar();
|
||||
}
|
||||
else if (passenger->GetTypeId() == TYPEID_UNIT)
|
||||
{
|
||||
if (seatFlags & SEAT_FLAG_CAN_CONTROL)
|
||||
{
|
||||
passenger->SetCharm(NULL);
|
||||
pVehicle->SetCharmerGuid(ObjectGuid());
|
||||
}
|
||||
// Reinitialize movement
|
||||
((Creature*)passenger)->AI()->SetCombatMovement(true, true);
|
||||
if (!passenger->getVictim())
|
||||
passenger->GetMotionMaster()->Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
/*! @} */
|
||||
|
|
|
|||
|
|
@ -260,6 +260,36 @@ std::string TimeToTimestampStr(time_t t)
|
|||
return std::string(buf);
|
||||
}
|
||||
|
||||
time_t timeBitFieldsToSecs(uint32 packedDate)
|
||||
{
|
||||
tm lt;
|
||||
memset(<, 0, sizeof(lt));
|
||||
|
||||
lt.tm_min = packedDate & 0x3F;
|
||||
lt.tm_hour = (packedDate >> 6) & 0x1F;
|
||||
lt.tm_wday = (packedDate >> 11) & 7;
|
||||
lt.tm_mday = ((packedDate >> 14) & 0x3F) + 1;
|
||||
lt.tm_mon = (packedDate >> 20) & 0xF;
|
||||
lt.tm_year = ((packedDate >> 24) & 0x1F) + 100;
|
||||
|
||||
return time_t(mktime(<));
|
||||
}
|
||||
|
||||
std::string MoneyToString(uint64 money)
|
||||
{
|
||||
uint32 gold = money / 10000;
|
||||
uint32 silv = (money % 10000) / 100;
|
||||
uint32 copp = (money % 10000) % 100;
|
||||
std::stringstream ss;
|
||||
if (gold)
|
||||
ss << gold << "g";
|
||||
if (silv || gold)
|
||||
ss << silv << "s";
|
||||
ss << copp << "c";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
/// Check if the string is a valid ip address representation
|
||||
bool IsIPAddress(char const* ipaddress)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#ifndef __REVISION_NR_H__
|
||||
#define __REVISION_NR_H__
|
||||
#define REVISION_NR "12562"
|
||||
#define REVISION_NR "12563"
|
||||
#endif // __REVISION_NR_H__
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue