Dynamic Creature/Go spawning:

- True blizzlike creature spawn/respawn behavior - new creature = new object
 - Toggleable spawn groups (with C++/SAI/command options to use them)
 - Custom feature: dynamic spawn rate scaling. Accelerates respawn rate based on players in the zone.
 - Backward compatibility mode (set via group and for summons)
   to support creatures/gos that currently don't work well with this
   (this should be removed once the exceptions are fixed)

Fixes and closes #2858
Tags #8661 as fixable.
Fixes and closes #13787
Fixes #15222.
This commit is contained in:
r00ty-tc
2017-05-07 21:48:41 +01:00
committed by Treeston
parent d24ce1739a
commit 59db2eeea0
59 changed files with 2709 additions and 771 deletions
+1 -1
View File
@@ -972,7 +972,7 @@ public:
Map* map = handler->GetSession()->GetPlayer()->GetMap();
if (!v->Create(map->GenerateLowGuid<HighGuid::Vehicle>(), map, handler->GetSession()->GetPlayer()->GetPhaseMask(), entry, x, y, z, o, nullptr, id))
if (!v->Create(map->GenerateLowGuid<HighGuid::Vehicle>(), map, handler->GetSession()->GetPlayer()->GetPhaseMask(), entry, { x, y, z, o }, nullptr, id))
{
delete v;
return false;
+7 -23
View File
@@ -138,8 +138,6 @@ public:
float o = fields[3].GetFloat();
uint32 mapId = fields[4].GetUInt16();
Transport* transport = nullptr;
if (!MapManager::IsValidMapCoord(mapId, x, y, z, o) || sObjectMgr->IsTransportMap(mapId))
{
handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, x, y, mapId);
@@ -157,11 +155,7 @@ public:
else
player->SaveRecallPosition();
if (player->TeleportTo(mapId, x, y, z, o))
{
if (transport)
transport->AddPassenger(player);
}
player->TeleportTo(mapId, x, y, z, o);
return true;
}
@@ -272,28 +266,18 @@ public:
if (!guidLow)
return false;
float x, y, z, o;
uint32 mapId;
// by DB guid
if (GameObjectData const* goData = sObjectMgr->GetGOData(guidLow))
{
x = goData->posX;
y = goData->posY;
z = goData->posZ;
o = goData->orientation;
mapId = goData->mapid;
}
else
GameObjectData const* goData = sObjectMgr->GetGameObjectData(guidLow);
if (!goData)
{
handler->SendSysMessage(LANG_COMMAND_GOOBJNOTFOUND);
handler->SetSentErrorMessage(true);
return false;
}
if (!MapManager::IsValidMapCoord(mapId, x, y, z, o) || sObjectMgr->IsTransportMap(mapId))
if (!MapManager::IsValidMapCoord(goData->spawnPoint) || sObjectMgr->IsTransportMap(goData->spawnPoint.GetMapId()))
{
handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, x, y, mapId);
handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, goData->spawnPoint.GetPositionX(), goData->spawnPoint.GetPositionY(), goData->spawnPoint.GetMapId());
handler->SetSentErrorMessage(true);
return false;
}
@@ -308,7 +292,7 @@ public:
else
player->SaveRecallPosition();
player->TeleportTo(mapId, x, y, z, o);
player->TeleportTo(goData->spawnPoint);
return true;
}
+50 -15
View File
@@ -38,6 +38,10 @@ EndScriptData */
#include "RBAC.h"
#include "WorldSession.h"
// definitions are over in cs_npc.cpp
bool HandleNpcSpawnGroup(ChatHandler* handler, char const* args);
bool HandleNpcDespawnGroup(ChatHandler* handler, char const* args);
class gobject_commandscript : public CommandScript
{
public:
@@ -57,15 +61,17 @@ public:
};
static std::vector<ChatCommand> gobjectCommandTable =
{
{ "activate", rbac::RBAC_PERM_COMMAND_GOBJECT_ACTIVATE, false, &HandleGameObjectActivateCommand, "" },
{ "delete", rbac::RBAC_PERM_COMMAND_GOBJECT_DELETE, false, &HandleGameObjectDeleteCommand, "" },
{ "info", rbac::RBAC_PERM_COMMAND_GOBJECT_INFO, false, &HandleGameObjectInfoCommand, "" },
{ "move", rbac::RBAC_PERM_COMMAND_GOBJECT_MOVE, false, &HandleGameObjectMoveCommand, "" },
{ "near", rbac::RBAC_PERM_COMMAND_GOBJECT_NEAR, false, &HandleGameObjectNearCommand, "" },
{ "target", rbac::RBAC_PERM_COMMAND_GOBJECT_TARGET, false, &HandleGameObjectTargetCommand, "" },
{ "turn", rbac::RBAC_PERM_COMMAND_GOBJECT_TURN, false, &HandleGameObjectTurnCommand, "" },
{ "add", rbac::RBAC_PERM_COMMAND_GOBJECT_ADD, false, nullptr, "", gobjectAddCommandTable },
{ "set", rbac::RBAC_PERM_COMMAND_GOBJECT_SET, false, nullptr, "", gobjectSetCommandTable },
{ "activate", rbac::RBAC_PERM_COMMAND_GOBJECT_ACTIVATE, false, &HandleGameObjectActivateCommand, "" },
{ "delete", rbac::RBAC_PERM_COMMAND_GOBJECT_DELETE, false, &HandleGameObjectDeleteCommand, "" },
{ "info", rbac::RBAC_PERM_COMMAND_GOBJECT_INFO, false, &HandleGameObjectInfoCommand, "" },
{ "move", rbac::RBAC_PERM_COMMAND_GOBJECT_MOVE, false, &HandleGameObjectMoveCommand, "" },
{ "near", rbac::RBAC_PERM_COMMAND_GOBJECT_NEAR, false, &HandleGameObjectNearCommand, "" },
{ "target", rbac::RBAC_PERM_COMMAND_GOBJECT_TARGET, false, &HandleGameObjectTargetCommand, "" },
{ "turn", rbac::RBAC_PERM_COMMAND_GOBJECT_TURN, false, &HandleGameObjectTurnCommand, "" },
{ "spawngroup", rbac::RBAC_PERM_COMMAND_GOBJECT_SPAWNGROUP, false, &HandleNpcSpawnGroup, "" },
{ "despawngroup", rbac::RBAC_PERM_COMMAND_GOBJECT_DESPAWNGROUP, false, &HandleNpcDespawnGroup,""},
{ "add", rbac::RBAC_PERM_COMMAND_GOBJECT_ADD, false, nullptr, "", gobjectAddCommandTable },
{ "set", rbac::RBAC_PERM_COMMAND_GOBJECT_SET, false, nullptr, "", gobjectSetCommandTable },
};
static std::vector<ChatCommand> commandTable =
{
@@ -169,14 +175,14 @@ public:
object = new GameObject();
// this will generate a new guid if the object is in an instance
if (!object->LoadGameObjectFromDB(guidLow, map))
if (!object->LoadFromDB(guidLow, map, true))
{
delete object;
return false;
}
/// @todo is it really necessary to add both the real and DB table guid here ?
sObjectMgr->AddGameobjectToGrid(guidLow, sObjectMgr->GetGOData(guidLow));
sObjectMgr->AddGameobjectToGrid(guidLow, sObjectMgr->GetGameObjectData(guidLow));
handler->PSendSysMessage(LANG_GAMEOBJECT_ADD, objectId, objectInfo->name.c_str(), guidLow, player->GetPositionX(), player->GetPositionY(), player->GetPositionZ());
return true;
@@ -430,7 +436,7 @@ public:
object->Delete();
object = new GameObject();
if (!object->LoadGameObjectFromDB(guidLow, map))
if (!object->LoadFromDB(guidLow, map, true))
{
delete object;
return false;
@@ -499,7 +505,7 @@ public:
object->Delete();
object = new GameObject();
if (!object->LoadGameObjectFromDB(guidLow, map))
if (!object->LoadFromDB(guidLow, map, true))
{
delete object;
return false;
@@ -578,7 +584,7 @@ public:
if (!gameObjectInfo)
continue;
handler->PSendSysMessage(LANG_GO_LIST_CHAT, guid, entry, guid, gameObjectInfo->name.c_str(), x, y, z, mapId);
handler->PSendSysMessage(LANG_GO_LIST_CHAT, guid, entry, guid, gameObjectInfo->name.c_str(), x, y, z, mapId, "", "");
++count;
} while (result->NextRow());
@@ -611,7 +617,7 @@ public:
if (!cValue)
return false;
ObjectGuid::LowType guidLow = atoul(cValue);
GameObjectData const* data = sObjectMgr->GetGOData(guidLow);
GameObjectData const* data = sObjectMgr->GetGameObjectData(guidLow);
if (!data)
return false;
entry = data->id;
@@ -623,9 +629,16 @@ public:
GameObjectTemplate const* gameObjectInfo = sObjectMgr->GetGameObjectTemplate(entry);
GameObject* thisGO = nullptr;
if (!gameObjectInfo)
return false;
if (*args && handler->GetSession()->GetPlayer())
thisGO = handler->GetSession()->GetPlayer()->FindNearestGameObject(entry, 30);
else if (handler->getSelectedObject() && handler->getSelectedObject()->GetTypeId() == TYPEID_GAMEOBJECT)
thisGO = handler->getSelectedObject()->ToGameObject();
type = gameObjectInfo->type;
displayId = gameObjectInfo->displayId;
name = gameObjectInfo->name;
@@ -634,10 +647,32 @@ public:
else if (type == GAMEOBJECT_TYPE_FISHINGHOLE)
lootId = gameObjectInfo->fishinghole.lootId;
// If we have a real object, send some info about it
if (thisGO)
{
handler->PSendSysMessage(LANG_SPAWNINFO_GUIDINFO, thisGO->GetGUID().ToString().c_str());
handler->PSendSysMessage(LANG_SPAWNINFO_SPAWNID_LOCATION, thisGO->GetSpawnId(), thisGO->GetPositionX(), thisGO->GetPositionY(), thisGO->GetPositionZ());
if (Player* player = handler->GetSession()->GetPlayer())
{
Position playerPos = player->GetPosition();
float dist = thisGO->GetExactDist(&playerPos);
handler->PSendSysMessage(LANG_SPAWNINFO_DISTANCEFROMPLAYER, dist);
}
}
handler->PSendSysMessage(LANG_GOINFO_ENTRY, entry);
handler->PSendSysMessage(LANG_GOINFO_TYPE, type);
handler->PSendSysMessage(LANG_GOINFO_LOOTID, lootId);
handler->PSendSysMessage(LANG_GOINFO_DISPLAYID, displayId);
if (WorldObject* object = handler->getSelectedObject())
{
if (object->ToGameObject() && object->ToGameObject()->GetGameObjectData() && object->ToGameObject()->GetGameObjectData()->spawnGroupData->groupId)
{
SpawnGroupTemplateData const* groupData = object->ToGameObject()->GetGameObjectData()->spawnGroupData;
handler->PSendSysMessage(LANG_SPAWNINFO_GROUP_ID, groupData->name.c_str(), groupData->groupId, groupData->flags, groupData->isActive);
}
if (object->ToGameObject())
handler->PSendSysMessage(LANG_SPAWNINFO_COMPATIBILITY_MODE, object->ToGameObject()->GetRespawnCompatibilityMode());
}
handler->PSendSysMessage(LANG_GOINFO_NAME, name.c_str());
handler->PSendSysMessage(LANG_GOINFO_SIZE, gameObjectInfo->size);
+136 -4
View File
@@ -27,7 +27,9 @@ EndScriptData */
#include "Chat.h"
#include "DatabaseEnv.h"
#include "DBCStores.h"
#include "GameObject.h"
#include "Language.h"
#include "MapManager.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "Player.h"
@@ -50,6 +52,7 @@ public:
{ "object", rbac::RBAC_PERM_COMMAND_LIST_OBJECT, true, &HandleListObjectCommand, "" },
{ "auras", rbac::RBAC_PERM_COMMAND_LIST_AURAS, false, &HandleListAurasCommand, "" },
{ "mail", rbac::RBAC_PERM_COMMAND_LIST_MAIL, true, &HandleListMailCommand, "" },
{ "respawns", rbac::RBAC_PERM_COMMAND_LIST_MAIL, false, &HandleListRespawnsCommand, "" },
};
static std::vector<ChatCommand> commandTable =
{
@@ -117,11 +120,40 @@ public:
float y = fields[2].GetFloat();
float z = fields[3].GetFloat();
uint16 mapId = fields[4].GetUInt16();
bool liveFound = false;
// Get map (only support base map from console)
Map* thisMap;
if (handler->GetSession())
handler->PSendSysMessage(LANG_CREATURE_LIST_CHAT, guid, guid, cInfo->Name.c_str(), x, y, z, mapId);
thisMap = handler->GetSession()->GetPlayer()->GetMap();
else
handler->PSendSysMessage(LANG_CREATURE_LIST_CONSOLE, guid, cInfo->Name.c_str(), x, y, z, mapId);
thisMap = sMapMgr->FindBaseNonInstanceMap(mapId);
// If map found, try to find active version of this creature
if (thisMap)
{
auto const creBounds = thisMap->GetCreatureBySpawnIdStore().equal_range(guid);
if (creBounds.first != creBounds.second)
{
for (std::unordered_multimap<uint32, Creature*>::const_iterator itr = creBounds.first; itr != creBounds.second;)
{
if (handler->GetSession())
handler->PSendSysMessage(LANG_CREATURE_LIST_CHAT, guid, guid, cInfo->Name.c_str(), x, y, z, mapId, itr->second->GetGUID().ToString().c_str(), itr->second->IsAlive() ? "*" : " ");
else
handler->PSendSysMessage(LANG_CREATURE_LIST_CONSOLE, guid, cInfo->Name.c_str(), x, y, z, mapId, itr->second->GetGUID().ToString().c_str(), itr->second->IsAlive() ? "*" : " ");
++itr;
}
liveFound = true;
}
}
if (!liveFound)
{
if (handler->GetSession())
handler->PSendSysMessage(LANG_CREATURE_LIST_CHAT, guid, guid, cInfo->Name.c_str(), x, y, z, mapId, "", "");
else
handler->PSendSysMessage(LANG_CREATURE_LIST_CONSOLE, guid, cInfo->Name.c_str(), x, y, z, mapId, "", "");
}
}
while (result->NextRow());
}
@@ -407,11 +439,40 @@ public:
float z = fields[3].GetFloat();
uint16 mapId = fields[4].GetUInt16();
uint32 entry = fields[5].GetUInt32();
bool liveFound = false;
// Get map (only support base map from console)
Map* thisMap;
if (handler->GetSession())
handler->PSendSysMessage(LANG_GO_LIST_CHAT, guid, entry, guid, gInfo->name.c_str(), x, y, z, mapId);
thisMap = handler->GetSession()->GetPlayer()->GetMap();
else
handler->PSendSysMessage(LANG_GO_LIST_CONSOLE, guid, gInfo->name.c_str(), x, y, z, mapId);
thisMap = sMapMgr->FindBaseNonInstanceMap(mapId);
// If map found, try to find active version of this object
if (thisMap)
{
auto const goBounds = thisMap->GetGameObjectBySpawnIdStore().equal_range(guid);
if (goBounds.first != goBounds.second)
{
for (std::unordered_multimap<uint32, GameObject*>::const_iterator itr = goBounds.first; itr != goBounds.second;)
{
if (handler->GetSession())
handler->PSendSysMessage(LANG_GO_LIST_CHAT, guid, entry, guid, gInfo->name.c_str(), x, y, z, mapId, itr->second->GetGUID().ToString().c_str(), itr->second->isSpawned() ? "*" : " ");
else
handler->PSendSysMessage(LANG_GO_LIST_CONSOLE, guid, gInfo->name.c_str(), x, y, z, mapId, itr->second->GetGUID().ToString().c_str(), itr->second->isSpawned() ? "*" : " ");
++itr;
}
liveFound = true;
}
}
if (!liveFound)
{
if (handler->GetSession())
handler->PSendSysMessage(LANG_GO_LIST_CHAT, guid, entry, guid, gInfo->name.c_str(), x, y, z, mapId, "", "");
else
handler->PSendSysMessage(LANG_GO_LIST_CONSOLE, guid, gInfo->name.c_str(), x, y, z, mapId, "", "");
}
}
while (result->NextRow());
}
@@ -581,8 +642,79 @@ public:
handler->PSendSysMessage(LANG_LIST_MAIL_NOT_FOUND);
return true;
}
static char const* GetZoneName(uint32 zoneId, LocaleConstant locale)
{
AreaTableEntry const* zoneEntry = sAreaTableStore.LookupEntry(zoneId);
return zoneEntry ? zoneEntry->area_name[locale] : "<unknown zone>";
}
static bool HandleListRespawnsCommand(ChatHandler* handler, char const* args)
{
// We need a player
Player const* player = handler->GetSession()->GetPlayer();
if (!player)
return false;
// And we need a map
Map const* map = player->GetMap();
if (!map)
return false;
uint32 range = 0;
if (*args)
range = atoi((char*)args);
RespawnVector respawns;
LocaleConstant locale = handler->GetSession()->GetSessionDbcLocale();
char const* stringOverdue = sObjectMgr->GetTrinityString(LANG_LIST_RESPAWNS_OVERDUE, locale);
char const* stringCreature = sObjectMgr->GetTrinityString(LANG_LIST_RESPAWNS_CREATURES, locale);
char const* stringGameobject = sObjectMgr->GetTrinityString(LANG_LIST_RESPAWNS_GAMEOBJECTS, locale);
uint32 zoneId = player->GetZoneId();
if (range)
handler->PSendSysMessage(LANG_LIST_RESPAWNS_RANGE, stringCreature, range);
else
handler->PSendSysMessage(LANG_LIST_RESPAWNS_ZONE, stringCreature, GetZoneName(zoneId, handler->GetSessionDbcLocale()), zoneId);
handler->PSendSysMessage(LANG_LIST_RESPAWNS_LISTHEADER);
map->GetRespawnInfo(respawns, SPAWN_TYPEMASK_CREATURE, range ? 0 : zoneId);
for (RespawnInfo* ri : respawns)
{
CreatureData const* data = sObjectMgr->GetCreatureData(ri->spawnId);
if (!data)
continue;
if (range && !player->IsInDist(data->spawnPoint, range))
continue;
uint32 gridY = ri->gridId / MAX_NUMBER_OF_GRIDS;
uint32 gridX = ri->gridId % MAX_NUMBER_OF_GRIDS;
std::string respawnTime = ri->respawnTime > time(NULL) ? secsToTimeString(uint64(ri->respawnTime - time(NULL)), true) : stringOverdue;
handler->PSendSysMessage("%u | %u | [%02u,%02u] | %s (%u) | %s", ri->spawnId, ri->entry, gridX, gridY, GetZoneName(ri->zoneId, handler->GetSessionDbcLocale()), ri->zoneId, data->spawnGroupData->isActive ? respawnTime.c_str() : "inactive");
}
respawns.clear();
if (range)
handler->PSendSysMessage(LANG_LIST_RESPAWNS_RANGE, stringGameobject, range);
else
handler->PSendSysMessage(LANG_LIST_RESPAWNS_ZONE, stringGameobject, GetZoneName(zoneId, handler->GetSessionDbcLocale()), zoneId);
handler->PSendSysMessage(LANG_LIST_RESPAWNS_LISTHEADER);
map->GetRespawnInfo(respawns, SPAWN_TYPEMASK_GAMEOBJECT, range ? 0 : zoneId);
for (RespawnInfo* ri : respawns)
{
GameObjectData const* data = sObjectMgr->GetGameObjectData(ri->spawnId);
if (!data)
continue;
if (range && !player->IsInDist(data->spawnPoint, range))
continue;
uint32 gridY = ri->gridId / MAX_NUMBER_OF_GRIDS;
uint32 gridX = ri->gridId % MAX_NUMBER_OF_GRIDS;
std::string respawnTime = ri->respawnTime > time(NULL) ? secsToTimeString(uint64(ri->respawnTime - time(NULL)), true) : stringOverdue;
handler->PSendSysMessage("%u | %u | [% 02u, % 02u] | %s (%u) | %s", ri->spawnId, ri->entry, gridX, gridY, GetZoneName(ri->zoneId, handler->GetSessionDbcLocale()), ri->zoneId, data->spawnGroupData->isActive ? respawnTime.c_str() : "inactive");
}
return true;
}
};
void AddSC_list_commandscript()
{
new list_commandscript();
+12
View File
@@ -1911,10 +1911,22 @@ public:
return true;
}
// First handle any creatures that still have a corpse around
Trinity::RespawnDo u_do;
Trinity::WorldObjectWorker<Trinity::RespawnDo> worker(player, u_do);
Cell::VisitGridObjects(player, worker, player->GetGridActivationRange());
// Now handle any that had despawned, but had respawn time logged.
RespawnVector data;
player->GetMap()->GetRespawnInfo(data, SPAWN_TYPEMASK_ALL, 0);
if (!data.empty())
{
uint32 const gridId = Trinity::ComputeGridCoord(player->GetPositionX(), player->GetPositionY()).GetId();
for (RespawnInfo* info : data)
if (info->gridId == gridId)
player->GetMap()->RemoveRespawnTime(info, true);
}
return true;
}
+135 -59
View File
@@ -176,6 +176,86 @@ EnumName<CreatureFlagsExtra> const flagsExtra[FLAGS_EXTRA_COUNT] =
CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK)
};
bool HandleNpcSpawnGroup(ChatHandler* handler, char const* args)
{
if (!*args)
return false;
bool ignoreRespawn = false;
bool force = false;
uint32 groupId = 0;
// Decode arguments
char* arg = strtok((char*)args, " ");
while (arg)
{
std::string thisArg = arg;
std::transform(thisArg.begin(), thisArg.end(), thisArg.begin(), ::tolower);
if (thisArg == "ignorerespawn")
ignoreRespawn = true;
else if (thisArg == "force")
force = true;
else if (thisArg.empty() || !(std::count_if(thisArg.begin(), thisArg.end(), ::isdigit) == (int)thisArg.size()))
return false;
else
groupId = atoi(thisArg.c_str());
arg = strtok(NULL, " ");
}
Player* player = handler->GetSession()->GetPlayer();
std::vector <WorldObject*> creatureList;
if (!sObjectMgr->SpawnGroupSpawn(groupId, player->GetMap(), ignoreRespawn, force, &creatureList))
{
handler->PSendSysMessage(LANG_SPAWNGROUP_BADGROUP, groupId);
handler->SetSentErrorMessage(true);
return false;
}
handler->PSendSysMessage(LANG_SPAWNGROUP_SPAWNCOUNT, creatureList.size());
for (WorldObject* obj : creatureList)
handler->PSendSysMessage("%s (%s)", obj->GetName(), obj->GetGUID().ToString().c_str());
return true;
}
bool HandleNpcDespawnGroup(ChatHandler* handler, char const* args)
{
if (!*args)
return false;
bool deleteRespawnTimes = false;
uint32 groupId = 0;
// Decode arguments
char* arg = strtok((char*)args, " ");
while (arg)
{
std::string thisArg = arg;
std::transform(thisArg.begin(), thisArg.end(), thisArg.begin(), ::tolower);
if (thisArg == "removerespawntime")
deleteRespawnTimes = true;
else if (thisArg.empty() || !(std::count_if(thisArg.begin(), thisArg.end(), ::isdigit) == (int)thisArg.size()))
return false;
else
groupId = atoi(thisArg.c_str());
arg = strtok(nullptr, " ");
}
Player* player = handler->GetSession()->GetPlayer();
if (!sObjectMgr->SpawnGroupDespawn(groupId, player->GetMap(), deleteRespawnTimes))
{
handler->PSendSysMessage(LANG_SPAWNGROUP_BADGROUP, groupId);
handler->SetSentErrorMessage(true);
return false;
}
return true;
}
class npc_commandscript : public CommandScript
{
public:
@@ -219,21 +299,23 @@ public:
};
static std::vector<ChatCommand> npcCommandTable =
{
{ "info", rbac::RBAC_PERM_COMMAND_NPC_INFO, false, &HandleNpcInfoCommand, "" },
{ "near", rbac::RBAC_PERM_COMMAND_NPC_NEAR, false, &HandleNpcNearCommand, "" },
{ "move", rbac::RBAC_PERM_COMMAND_NPC_MOVE, false, &HandleNpcMoveCommand, "" },
{ "playemote", rbac::RBAC_PERM_COMMAND_NPC_PLAYEMOTE, false, &HandleNpcPlayEmoteCommand, "" },
{ "say", rbac::RBAC_PERM_COMMAND_NPC_SAY, false, &HandleNpcSayCommand, "" },
{ "textemote", rbac::RBAC_PERM_COMMAND_NPC_TEXTEMOTE, false, &HandleNpcTextEmoteCommand, "" },
{ "whisper", rbac::RBAC_PERM_COMMAND_NPC_WHISPER, false, &HandleNpcWhisperCommand, "" },
{ "yell", rbac::RBAC_PERM_COMMAND_NPC_YELL, false, &HandleNpcYellCommand, "" },
{ "tame", rbac::RBAC_PERM_COMMAND_NPC_TAME, false, &HandleNpcTameCommand, "" },
{ "add", rbac::RBAC_PERM_COMMAND_NPC_ADD, false, nullptr, "", npcAddCommandTable },
{ "delete", rbac::RBAC_PERM_COMMAND_NPC_DELETE, false, nullptr, "", npcDeleteCommandTable },
{ "follow", rbac::RBAC_PERM_COMMAND_NPC_FOLLOW, false, nullptr, "", npcFollowCommandTable },
{ "set", rbac::RBAC_PERM_COMMAND_NPC_SET, false, nullptr, "", npcSetCommandTable },
{ "evade", rbac::RBAC_PERM_COMMAND_NPC_EVADE, false, &HandleNpcEvadeCommand, "" },
{ "showloot", rbac::RBAC_PERM_COMMAND_NPC_SHOWLOOT, false, &HandleNpcShowLootCommand, "" },
{ "info", rbac::RBAC_PERM_COMMAND_NPC_INFO, false, &HandleNpcInfoCommand, "" },
{ "near", rbac::RBAC_PERM_COMMAND_NPC_NEAR, false, &HandleNpcNearCommand, "" },
{ "move", rbac::RBAC_PERM_COMMAND_NPC_MOVE, false, &HandleNpcMoveCommand, "" },
{ "playemote", rbac::RBAC_PERM_COMMAND_NPC_PLAYEMOTE, false, &HandleNpcPlayEmoteCommand, "" },
{ "say", rbac::RBAC_PERM_COMMAND_NPC_SAY, false, &HandleNpcSayCommand, "" },
{ "textemote", rbac::RBAC_PERM_COMMAND_NPC_TEXTEMOTE, false, &HandleNpcTextEmoteCommand, "" },
{ "whisper", rbac::RBAC_PERM_COMMAND_NPC_WHISPER, false, &HandleNpcWhisperCommand, "" },
{ "yell", rbac::RBAC_PERM_COMMAND_NPC_YELL, false, &HandleNpcYellCommand, "" },
{ "tame", rbac::RBAC_PERM_COMMAND_NPC_TAME, false, &HandleNpcTameCommand, "" },
{ "spawngroup", rbac::RBAC_PERM_COMMAND_NPC_SPAWNGROUP, false, &HandleNpcSpawnGroup, "" },
{ "despawngroup", rbac::RBAC_PERM_COMMAND_NPC_DESPAWNGROUP, false, &HandleNpcDespawnGroup, "" },
{ "add", rbac::RBAC_PERM_COMMAND_NPC_ADD, false, nullptr, "", npcAddCommandTable },
{ "delete", rbac::RBAC_PERM_COMMAND_NPC_DELETE, false, nullptr, "", npcDeleteCommandTable },
{ "follow", rbac::RBAC_PERM_COMMAND_NPC_FOLLOW, false, nullptr, "", npcFollowCommandTable },
{ "set", rbac::RBAC_PERM_COMMAND_NPC_SET, false, nullptr, "", npcSetCommandTable },
{ "evade", rbac::RBAC_PERM_COMMAND_NPC_EVADE, false, &HandleNpcEvadeCommand, "" },
{ "showloot", rbac::RBAC_PERM_COMMAND_NPC_SHOWLOOT, false, &HandleNpcShowLootCommand, "" },
};
static std::vector<ChatCommand> commandTable =
{
@@ -257,22 +339,16 @@ public:
return false;
Player* chr = handler->GetSession()->GetPlayer();
float x = chr->GetPositionX();
float y = chr->GetPositionY();
float z = chr->GetPositionZ();
float o = chr->GetOrientation();
Map* map = chr->GetMap();
if (Transport* trans = chr->GetTransport())
{
ObjectGuid::LowType guid = map->GenerateLowGuid<HighGuid::Unit>();
CreatureData& data = sObjectMgr->NewOrExistCreatureData(guid);
data.spawnId = guid;
data.id = id;
data.phaseMask = chr->GetPhaseMaskForSpawn();
data.posX = chr->GetTransOffsetX();
data.posY = chr->GetTransOffsetY();
data.posZ = chr->GetTransOffsetZ();
data.orientation = chr->GetTransOffsetO();
data.spawnPoint.Relocate(chr->GetTransOffsetX(), chr->GetTransOffsetY(), chr->GetTransOffsetZ(), chr->GetTransOffsetO());
Creature* creature = trans->CreateNPCPassenger(guid, &data);
@@ -283,7 +359,7 @@ public:
}
Creature* creature = new Creature();
if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), id, x, y, z, o))
if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), id, *chr))
{
delete creature;
return false;
@@ -298,7 +374,7 @@ public:
creature->CleanupsBeforeDelete();
delete creature;
creature = new Creature();
if (!creature->LoadCreatureFromDB(db_guid, map))
if (!creature->LoadFromDB(db_guid, map, true, true))
{
delete creature;
return false;
@@ -473,7 +549,7 @@ public:
static bool HandleNpcDeleteCommand(ChatHandler* handler, char const* args)
{
Creature* unit = nullptr;
Creature* creature = nullptr;
if (*args)
{
@@ -485,22 +561,27 @@ public:
ObjectGuid::LowType lowguid = atoul(cId);
if (!lowguid)
return false;
unit = handler->GetCreatureFromPlayerMapByDbGuid(lowguid);
creature = handler->GetCreatureFromPlayerMapByDbGuid(lowguid);
}
else
unit = handler->getSelectedCreature();
creature = handler->getSelectedCreature();
if (!unit || unit->IsPet() || unit->IsTotem())
if (!creature || creature->IsPet() || creature->IsTotem())
{
handler->SendSysMessage(LANG_SELECT_CREATURE);
handler->SetSentErrorMessage(true);
return false;
}
// Delete the creature
unit->CombatStop();
unit->DeleteFromDB();
unit->AddObjectToRemoveList();
if (TempSummon* summon = creature->ToTempSummon())
summon->UnSummon();
else
{
// Delete the creature
creature->CombatStop();
creature->DeleteFromDB();
creature->AddObjectToRemoveList();
}
handler->SendSysMessage(LANG_COMMAND_DELCREATMESSAGE);
@@ -690,13 +771,20 @@ public:
uint32 nativeid = target->GetNativeDisplayId();
uint32 Entry = target->GetEntry();
int64 curRespawnDelay = target->GetRespawnTimeEx()-time(nullptr);
int64 curRespawnDelay = target->GetRespawnCompatibilityMode() ? target->GetRespawnTimeEx() - time(nullptr) : target->GetMap()->GetCreatureRespawnTime(target->GetSpawnId()) - time(nullptr);
if (curRespawnDelay < 0)
curRespawnDelay = 0;
std::string curRespawnDelayStr = secsToTimeString(uint64(curRespawnDelay), true);
std::string defRespawnDelayStr = secsToTimeString(target->GetRespawnDelay(), true);
handler->PSendSysMessage(LANG_NPCINFO_CHAR, target->GetSpawnId(), target->GetGUID().GetCounter(), faction, npcflags, Entry, displayid, nativeid);
if (target->GetCreatureData() && target->GetCreatureData()->spawnGroupData->groupId)
{
if (SpawnGroupTemplateData const* groupData = target->GetCreatureData()->spawnGroupData)
handler->PSendSysMessage(LANG_SPAWNINFO_GROUP_ID, groupData->name.c_str(), groupData->groupId, groupData->flags, groupData->isActive);
}
handler->PSendSysMessage(LANG_SPAWNINFO_COMPATIBILITY_MODE, target->GetRespawnCompatibilityMode());
handler->PSendSysMessage(LANG_NPCINFO_LEVEL, target->getLevel());
handler->PSendSysMessage(LANG_NPCINFO_EQUIPMENT, target->GetCurrentEquipmentId(), target->GetOriginalEquipmentId());
handler->PSendSysMessage(LANG_NPCINFO_HEALTH, target->GetCreateHealth(), target->GetMaxHealth(), target->GetHealth());
@@ -766,7 +854,7 @@ public:
if (!creatureTemplate)
continue;
handler->PSendSysMessage(LANG_CREATURE_LIST_CHAT, guid, guid, creatureTemplate->Name.c_str(), x, y, z, mapId);
handler->PSendSysMessage(LANG_CREATURE_LIST_CHAT, guid, guid, creatureTemplate->Name.c_str(), x, y, z, mapId, "", "");
++count;
}
@@ -784,6 +872,9 @@ public:
ObjectGuid::LowType lowguid = 0;
Creature* creature = handler->getSelectedCreature();
Player const* player = handler->GetSession()->GetPlayer();
if (!player)
return false;
if (!creature)
{
@@ -803,9 +894,7 @@ public:
return false;
}
uint32 map_id = data->mapid;
if (handler->GetSession()->GetPlayer()->GetMapId() != map_id)
if (player->GetMapId() != data->spawnPoint.GetMapId())
{
handler->PSendSysMessage(LANG_COMMAND_CREATUREATSAMEMAP, lowguid);
handler->SetSentErrorMessage(true);
@@ -813,25 +902,12 @@ public:
}
}
else
{
lowguid = creature->GetSpawnId();
}
float x = handler->GetSession()->GetPlayer()->GetPositionX();
float y = handler->GetSession()->GetPlayer()->GetPositionY();
float z = handler->GetSession()->GetPlayer()->GetPositionZ();
float o = handler->GetSession()->GetPlayer()->GetOrientation();
if (creature)
{
if (CreatureData const* data = sObjectMgr->GetCreatureData(creature->GetSpawnId()))
{
const_cast<CreatureData*>(data)->posX = x;
const_cast<CreatureData*>(data)->posY = y;
const_cast<CreatureData*>(data)->posZ = z;
const_cast<CreatureData*>(data)->orientation = o;
}
creature->UpdatePosition(x, y, z, o);
sObjectMgr->NewOrExistCreatureData(creature->GetSpawnId()).spawnPoint.Relocate(*player);
creature->UpdatePosition(*player);
creature->GetMotionMaster()->Initialize();
if (creature->IsAlive()) // dead creature will reset movement generator at respawn
{
@@ -842,10 +918,10 @@ public:
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_POSITION);
stmt->setFloat(0, x);
stmt->setFloat(1, y);
stmt->setFloat(2, z);
stmt->setFloat(3, o);
stmt->setFloat(0, player->GetPositionX());
stmt->setFloat(1, player->GetPositionY());
stmt->setFloat(2, player->GetPositionZ());
stmt->setFloat(3, player->GetOrientation());
stmt->setUInt32(4, lowguid);
WorldDatabase.Execute(stmt);
@@ -1529,13 +1605,13 @@ public:
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL_2, "Per-player quest items");
_IterateNotNormalLootMap(handler, loot.GetPlayerQuestItems(), loot.quest_items);
}
if (!loot.GetPlayerFFAItems().empty())
{
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL_2, "FFA items per allowed player");
_IterateNotNormalLootMap(handler, loot.GetPlayerFFAItems(), loot.items);
}
if (!loot.GetPlayerNonQuestNonFFAConditionalItems().empty())
{
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL_2, "Per-player conditional items");
+8 -9
View File
@@ -662,7 +662,7 @@ public:
// re-create
Creature* wpCreature = new Creature();
if (!wpCreature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), VISUAL_WAYPOINT, chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), chr->GetOrientation()))
if (!wpCreature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), VISUAL_WAYPOINT, *chr))
{
handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, VISUAL_WAYPOINT);
delete wpCreature;
@@ -671,8 +671,7 @@ public:
wpCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn());
// To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells();
/// @todo Should we first use "Create" then use "LoadFromDB"?
if (!wpCreature->LoadCreatureFromDB(wpCreature->GetSpawnId(), map))
if (!wpCreature->LoadFromDB(wpCreature->GetSpawnId(), map, true, true))
{
handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, VISUAL_WAYPOINT);
delete wpCreature;
@@ -874,7 +873,7 @@ public:
float o = chr->GetOrientation();
Creature* wpCreature = new Creature();
if (!wpCreature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), id, x, y, z, o))
if (!wpCreature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), id, { x, y, z, o }))
{
handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id);
delete wpCreature;
@@ -891,7 +890,7 @@ public:
WorldDatabase.Execute(stmt);
// To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells();
if (!wpCreature->LoadCreatureFromDB(wpCreature->GetSpawnId(), map))
if (!wpCreature->LoadFromDB(wpCreature->GetSpawnId(), map, true, true))
{
handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id);
delete wpCreature;
@@ -937,7 +936,7 @@ public:
Map* map = chr->GetMap();
Creature* creature = new Creature();
if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), id, x, y, z, o))
if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), id, { x, y, z, o }))
{
handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id);
delete creature;
@@ -945,7 +944,7 @@ public:
}
creature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn());
if (!creature->LoadCreatureFromDB(creature->GetSpawnId(), map))
if (!creature->LoadFromDB(creature->GetSpawnId(), map, true, true))
{
handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id);
delete creature;
@@ -986,7 +985,7 @@ public:
Map* map = chr->GetMap();
Creature* creature = new Creature();
if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), id, x, y, z, o))
if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), id, { x, y, z, o }))
{
handler->PSendSysMessage(LANG_WAYPOINT_NOTCREATED, id);
delete creature;
@@ -994,7 +993,7 @@ public:
}
creature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn());
if (!creature->LoadCreatureFromDB(creature->GetSpawnId(), map))
if (!creature->LoadFromDB(creature->GetSpawnId(), map, true, true))
{
handler->PSendSysMessage(LANG_WAYPOINT_NOTCREATED, id);
delete creature;
@@ -264,7 +264,7 @@ class ValithriaDespawner : public BasicEvent
creature->SetRespawnDelay(10);
if (CreatureData const* data = creature->GetCreatureData())
creature->UpdatePosition(data->posX, data->posY, data->posZ, data->orientation);
creature->UpdatePosition(data->spawnPoint);
creature->DespawnOrUnsummon();
creature->SetCorpseDelay(corpseDelay);
@@ -354,7 +354,7 @@ class FrostwingGauntletRespawner
creature->SetRespawnDelay(2);
if (CreatureData const* data = creature->GetCreatureData())
creature->UpdatePosition(data->posX, data->posY, data->posZ, data->orientation);
creature->UpdatePosition(data->spawnPoint);
creature->DespawnOrUnsummon();
creature->SetCorpseDelay(corpseDelay);
@@ -392,13 +392,13 @@ class instance_icecrown_citadel : public InstanceMapScript
break;
case NPC_ZAFOD_BOOMBOX:
if (GameObjectTemplate const* go = sObjectMgr->GetGameObjectTemplate(GO_THE_SKYBREAKER_A))
if ((TeamInInstance == ALLIANCE && data->mapid == go->moTransport.mapID) ||
(TeamInInstance == HORDE && data->mapid != go->moTransport.mapID))
if ((TeamInInstance == ALLIANCE && data->spawnPoint.GetMapId() == go->moTransport.mapID) ||
(TeamInInstance == HORDE && data->spawnPoint.GetMapId() != go->moTransport.mapID))
return entry;
return 0;
case NPC_IGB_MURADIN_BRONZEBEARD:
if ((TeamInInstance == ALLIANCE && data->posX > 10.0f) ||
(TeamInInstance == HORDE && data->posX < 10.0f))
if ((TeamInInstance == ALLIANCE && data->spawnPoint.GetPositionX() > 10.0f) ||
(TeamInInstance == HORDE && data->spawnPoint.GetPositionX() < 10.0f))
return entry;
return 0;
default:
@@ -96,7 +96,7 @@ public:
void InitializeAI() override
{
if (!me->isDead())
if (!me->isDead() && instance->GetBossState(BOSS_ANUBREKHAN) != DONE)
{
Reset();
SummonGuards();
@@ -83,7 +83,7 @@ class boss_faerlina : public CreatureScript
void InitializeAI() override
{
if (!me->isDead())
if (!me->isDead() && instance->GetBossState(BOSS_FAERLINA) != DONE)
{
Reset();
SummonAdds();
@@ -265,14 +265,9 @@ struct boss_four_horsemen_baseAI : public BossAI
for (Horseman boss : horsemen)
{
if (Creature* cBoss = getHorsemanHandle(boss))
{
cBoss->DespawnOrUnsummon();
cBoss->SetRespawnTime(15);
}
cBoss->DespawnOrUnsummon(0, Seconds(15));
else
{
TC_LOG_WARN("scripts", "FourHorsemenAI: Encounter resetting but horseman with id %u is not present", uint32(boss));
}
}
}
@@ -79,7 +79,7 @@ public:
void InitializeAI() override
{
if (!me->isDead())
if (!me->isDead() && instance->GetBossState(BOSS_RAZUVIOUS) != DONE)
{
Reset();
SummonAdds();