/*
* Copyright (C) 2008-2010 TrinityCore
* Copyright (C) 2005-2009 MaNGOS
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*/
#include "Map.h"
#include "World.h"
#include "CellImpl.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "Transport.h"
#include "ScriptedCreature.h"
#include "WaypointManager.h"
#include "GossipDef.h"
#include "MapManager.h"
#include "ObjectMgr.h"
#include "MapRefManager.h"
/// Put scripts in the execution queue
void Map::ScriptsStart(ScriptMapMap const& scripts, uint32 id, Object* source, Object* target)
{
///- Find the script map
ScriptMapMap::const_iterator s = scripts.find(id);
if (s == scripts.end())
return;
// prepare static data
uint64 sourceGUID = source ? source->GetGUID() : (uint64)0; //some script commands doesn't have source
uint64 targetGUID = target ? target->GetGUID() : (uint64)0;
uint64 ownerGUID = (source->GetTypeId() == TYPEID_ITEM) ? ((Item*)source)->GetOwnerGUID() : (uint64)0;
///- Schedule script execution for all scripts in the script map
ScriptMap const *s2 = &(s->second);
bool immedScript = false;
for (ScriptMap::const_iterator iter = s2->begin(); iter != s2->end(); ++iter)
{
ScriptAction sa;
sa.sourceGUID = sourceGUID;
sa.targetGUID = targetGUID;
sa.ownerGUID = ownerGUID;
sa.script = &iter->second;
m_scriptSchedule.insert(std::pair(time_t(sWorld.GetGameTime() + iter->first), sa));
if (iter->first == 0)
immedScript = true;
sWorld.IncreaseScheduledScriptsCount();
}
///- If one of the effects should be immediate, launch the script execution
if (/*start &&*/ immedScript && !i_scriptLock)
{
i_scriptLock = true;
ScriptsProcess();
i_scriptLock = false;
}
}
void Map::ScriptCommandStart(ScriptInfo const& script, uint32 delay, Object* source, Object* target)
{
// NOTE: script record _must_ exist until command executed
// prepare static data
uint64 sourceGUID = source ? source->GetGUID() : (uint64)0;
uint64 targetGUID = target ? target->GetGUID() : (uint64)0;
uint64 ownerGUID = (source->GetTypeId() == TYPEID_ITEM) ? ((Item*)source)->GetOwnerGUID() : (uint64)0;
ScriptAction sa;
sa.sourceGUID = sourceGUID;
sa.targetGUID = targetGUID;
sa.ownerGUID = ownerGUID;
sa.script = &script;
m_scriptSchedule.insert(std::pair(time_t(sWorld.GetGameTime() + delay), sa));
sWorld.IncreaseScheduledScriptsCount();
///- If effects should be immediate, launch the script execution
if (delay == 0 && !i_scriptLock)
{
i_scriptLock = true;
ScriptsProcess();
i_scriptLock = false;
}
}
// Helpers for ScriptProcess method.
inline Player* Map::_GetScriptPlayerSourceOrTarget(Object* source, Object* target, const ScriptInfo* scriptInfo) const
{
Player *pPlayer = NULL;
if (!source && !target)
sLog.outError("%s source and target objects are NULL.", scriptInfo->GetDebugInfo().c_str());
else
{
// Check target first, then source.
if (target)
pPlayer = target->ToPlayer();
if (!pPlayer && source)
pPlayer = source->ToPlayer();
if (!pPlayer)
sLog.outError("%s neither source nor target object is player (source: TypeId: %u, Entry: %u, GUID: %u; target: TypeId: %u, Entry: %u, GUID: %u), skipping.",
scriptInfo->GetDebugInfo().c_str(),
source ? source->GetTypeId() : 0, source ? source->GetEntry() : 0, source ? source->GetGUIDLow() : 0,
target ? target->GetTypeId() : 0, target ? target->GetEntry() : 0, target ? target->GetGUIDLow() : 0);
}
return pPlayer;
}
inline Creature* Map::_GetScriptCreatureSourceOrTarget(Object* source, Object* target, const ScriptInfo* scriptInfo, bool bReverse) const
{
Creature *pCreature = NULL;
if (!source && !target)
sLog.outError("%s source and target objects are NULL.", scriptInfo->GetDebugInfo().c_str());
else
{
if (bReverse)
{
// Check target first, then source.
if (target)
pCreature = target->ToCreature();
if (!pCreature && source)
pCreature = source->ToCreature();
}
else
{
// Check source first, then target.
if (source)
pCreature = source->ToCreature();
if (!pCreature && target)
pCreature = target->ToCreature();
}
if (!pCreature)
sLog.outError("%s neither source nor target are creatures (source: TypeId: %u, Entry: %u, GUID: %u; target: TypeId: %u, Entry: %u, GUID: %u), skipping.",
scriptInfo->GetDebugInfo().c_str(),
source ? source->GetTypeId() : 0, source ? source->GetEntry() : 0, source ? source->GetGUIDLow() : 0,
target ? target->GetTypeId() : 0, target ? target->GetEntry() : 0, target ? target->GetGUIDLow() : 0);
}
return pCreature;
}
inline Unit* Map::_GetScriptUnit(Object* obj, bool isSource, const ScriptInfo* scriptInfo) const
{
Unit* pUnit = NULL;
if (!obj)
sLog.outError("%s %s object is NULL.", scriptInfo->GetDebugInfo().c_str(), isSource ? "source" : "target");
else if (!obj->isType(TYPEMASK_UNIT))
sLog.outError("%s %s object is not unit (TypeId: %u, Entry: %u, GUID: %u), skipping.",
scriptInfo->GetDebugInfo().c_str(), isSource ? "source" : "target", obj->GetTypeId(), obj->GetEntry(), obj->GetGUIDLow());
else
{
pUnit = dynamic_cast(obj);
if (!pUnit)
sLog.outError("%s %s object could not be casted to unit.",
scriptInfo->GetDebugInfo().c_str(), isSource ? "source" : "target");
}
return pUnit;
}
inline Player* Map::_GetScriptPlayer(Object* obj, bool isSource, const ScriptInfo* scriptInfo) const
{
Player* pPlayer = NULL;
if (!obj)
sLog.outError("%s %s object is NULL.", scriptInfo->GetDebugInfo().c_str(), isSource ? "source" : "target");
else
{
pPlayer = obj->ToPlayer();
if (!pPlayer)
sLog.outError("%s %s object is not a player (TypeId: %u, Entry: %u, GUID: %u).",
scriptInfo->GetDebugInfo().c_str(), isSource ? "source" : "target", obj->GetTypeId(), obj->GetEntry(), obj->GetGUIDLow());
}
return pPlayer;
}
inline Creature* Map::_GetScriptCreature(Object* obj, bool isSource, const ScriptInfo* scriptInfo) const
{
Creature* pCreature = NULL;
if (!obj)
sLog.outError("%s %s object is NULL.", scriptInfo->GetDebugInfo().c_str(), isSource ? "source" : "target");
else
{
pCreature = obj->ToCreature();
if (!pCreature)
sLog.outError("%s %s object is not a creature (TypeId: %u, Entry: %u, GUID: %u).", scriptInfo->GetDebugInfo().c_str(),
isSource ? "source" : "target", obj->GetTypeId(), obj->GetEntry(), obj->GetGUIDLow());
}
return pCreature;
}
inline WorldObject* Map::_GetScriptWorldObject(Object* obj, bool isSource, const ScriptInfo* scriptInfo) const
{
WorldObject* pWorldObject = NULL;
if (!obj)
sLog.outError("%s %s object is NULL.",
scriptInfo->GetDebugInfo().c_str(), isSource ? "source" : "target");
else
{
pWorldObject = dynamic_cast(obj);
if (!pWorldObject)
sLog.outError("%s %s object is not a world object (TypeId: %u, Entry: %u, GUID: %u).",
scriptInfo->GetDebugInfo().c_str(), isSource ? "source" : "target", obj->GetTypeId(), obj->GetEntry(), obj->GetGUIDLow());
}
return pWorldObject;
}
inline void Map::_ScriptProcessDoor(Object* source, Object* target, const ScriptInfo* scriptInfo) const
{
bool bOpen = false;
uint32 guid = scriptInfo->ToggleDoor.GOGuid;
int32 nTimeToToggle = std::max(15, int32(scriptInfo->ToggleDoor.ResetDelay));
switch (scriptInfo->command)
{
case SCRIPT_COMMAND_OPEN_DOOR: bOpen = true; break;
case SCRIPT_COMMAND_CLOSE_DOOR: break;
default:
sLog.outError("%s unknown command for _ScriptProcessDoor.", scriptInfo->GetDebugInfo().c_str());
return;
}
if (!guid)
sLog.outError("%s door guid is not specified.", scriptInfo->GetDebugInfo().c_str());
else if (!source)
sLog.outError("%s source object is NULL.", scriptInfo->GetDebugInfo().c_str());
else if (!source->isType(TYPEMASK_UNIT))
sLog.outError("%s source object is not unit (TypeId: %u, Entry: %u, GUID: %u), skipping.", scriptInfo->GetDebugInfo().c_str(),
source->GetTypeId(), source->GetEntry(), source->GetGUIDLow());
else
{
WorldObject* wSource = dynamic_cast (source);
if (!wSource)
sLog.outError("%s source object could not be casted to world object (TypeId: %u, Entry: %u, GUID: %u), skipping.",
scriptInfo->GetDebugInfo().c_str(), source->GetTypeId(), source->GetEntry(), source->GetGUIDLow());
else
{
GameObject *pDoor = _FindGameObject(wSource, guid);
if (!pDoor)
sLog.outError("%s gameobject was not found (guid: %u).", scriptInfo->GetDebugInfo().c_str(), guid);
else if (pDoor->GetGoType() != GAMEOBJECT_TYPE_DOOR)
sLog.outError("%s gameobject is not a door (GoType: %u, Entry: %u, GUID: %u).",
scriptInfo->GetDebugInfo().c_str(), pDoor->GetGoType(), pDoor->GetEntry(), pDoor->GetGUIDLow());
else if (bOpen == (pDoor->GetGoState() == GO_STATE_READY))
{
pDoor->UseDoorOrButton(nTimeToToggle);
if (target && target->isType(TYPEMASK_GAMEOBJECT))
{
GameObject* goTarget = dynamic_cast(target);
if (goTarget && goTarget->GetGoType() == GAMEOBJECT_TYPE_BUTTON)
goTarget->UseDoorOrButton(nTimeToToggle);
}
}
}
}
}
inline GameObject* Map::_FindGameObject(WorldObject* pSearchObject, uint32 guid) const
{
GameObject *pGameObject = NULL;
CellPair p(Trinity::ComputeCellPair(pSearchObject->GetPositionX(), pSearchObject->GetPositionY()));
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
Trinity::GameObjectWithDbGUIDCheck goCheck(*pSearchObject, guid);
Trinity::GameObjectSearcher checker(pSearchObject, pGameObject, goCheck);
TypeContainerVisitor, GridTypeMapContainer > objectChecker(checker);
cell.Visit(p, objectChecker, *pSearchObject->GetMap());
return pGameObject;
}
/// Process queued scripts
void Map::ScriptsProcess()
{
if (m_scriptSchedule.empty())
return;
///- Process overdue queued scripts
std::multimap::iterator iter = m_scriptSchedule.begin();
// ok as multimap is a *sorted* associative container
while (!m_scriptSchedule.empty() && (iter->first <= sWorld.GetGameTime()))
{
ScriptAction const& step = iter->second;
Object* source = NULL;
if (step.sourceGUID)
{
switch (GUID_HIPART(step.sourceGUID))
{
case HIGHGUID_ITEM:
// case HIGHGUID_CONTAINER: == HIGHGUID_ITEM
{
Player* player = HashMapHolder::Find(step.ownerGUID);
if (player)
source = player->GetItemByGuid(step.sourceGUID);
break;
}
case HIGHGUID_UNIT:
source = HashMapHolder::Find(step.sourceGUID);
break;
case HIGHGUID_PET:
source = HashMapHolder::Find(step.sourceGUID);
break;
case HIGHGUID_PLAYER:
source = HashMapHolder::Find(step.sourceGUID);
break;
case HIGHGUID_GAMEOBJECT:
source = HashMapHolder::Find(step.sourceGUID);
break;
case HIGHGUID_CORPSE:
source = HashMapHolder::Find(step.sourceGUID);
break;
case HIGHGUID_MO_TRANSPORT:
for (MapManager::TransportSet::iterator iter = sMapMgr.m_Transports.begin(); iter != sMapMgr.m_Transports.end(); ++iter)
{
if ((*iter)->GetGUID() == step.sourceGUID)
{
source = reinterpret_cast