[c12562] Cleanup Style

This commit is contained in:
Dramacydal 2013-05-31 09:27:56 +01:00 committed by Antz
parent d988eb4038
commit bf5c6b1ddd
19 changed files with 3432 additions and 1071 deletions

View file

@ -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

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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];
}
}

View file

@ -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)
{

View file

@ -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

View file

@ -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();
}
}
/*! @} */

View file

@ -260,6 +260,36 @@ std::string TimeToTimestampStr(time_t t)
return std::string(buf);
}
time_t timeBitFieldsToSecs(uint32 packedDate)
{
tm lt;
memset(&lt, 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(&lt));
}
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)
{

View file

@ -1,4 +1,4 @@
#ifndef __REVISION_NR_H__
#define __REVISION_NR_H__
#define REVISION_NR "12562"
#define REVISION_NR "12563"
#endif // __REVISION_NR_H__