mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-02-28 06:25:55 +00:00
feat(Scripts/Commands): Implement npc/gameobject load commands (#24644)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -2422,6 +2422,164 @@ void ObjectMgr::LoadCreatures()
|
||||
LOG_INFO("server.loading", " ");
|
||||
}
|
||||
|
||||
// Loads a single creature spawn from DB into the cache.
|
||||
// Creature::LoadCreatureFromDB() reads from cache (GetCreatureData()), not from DB directly,
|
||||
// so this must be called first for spawns not loaded at startup.
|
||||
CreatureData const* ObjectMgr::LoadCreatureDataFromDB(ObjectGuid::LowType spawnId)
|
||||
{
|
||||
CreatureData const* data = GetCreatureData(spawnId);
|
||||
if (data)
|
||||
return data;
|
||||
|
||||
QueryResult result = WorldDatabase.Query("SELECT creature.guid, id1, id2, id3, map, equipment_id, "
|
||||
"position_x, position_y, position_z, orientation, spawntimesecs, wander_distance, "
|
||||
"currentwaypoint, curhealth, curmana, MovementType, spawnMask, phaseMask, "
|
||||
"creature.npcflag, creature.unit_flags, creature.dynamicflags, creature.ScriptName "
|
||||
"FROM creature WHERE creature.guid = {}", spawnId);
|
||||
|
||||
if (!result)
|
||||
return nullptr;
|
||||
|
||||
Field* fields = result->Fetch();
|
||||
uint32 id1 = fields[1].Get<uint32>();
|
||||
uint32 id2 = fields[2].Get<uint32>();
|
||||
uint32 id3 = fields[3].Get<uint32>();
|
||||
|
||||
CreatureTemplate const* cInfo = GetCreatureTemplate(id1);
|
||||
if (!cInfo)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {}) with non-existing creature entry {} in id1 field, skipped.", spawnId, id1);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (id2 && !GetCreatureTemplate(id2))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {}) with non-existing creature entry {} in id2 field, skipped.", spawnId, id2);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (id3 && !GetCreatureTemplate(id3))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {}) with non-existing creature entry {} in id3 field, skipped.", spawnId, id3);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!id2 && id3)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {}) with creature entry {} in id3 field but no entry in id2 field, skipped.", spawnId, id3);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CreatureData& creatureData = _creatureDataStore[spawnId];
|
||||
creatureData.id1 = id1;
|
||||
creatureData.id2 = id2;
|
||||
creatureData.id3 = id3;
|
||||
creatureData.mapid = fields[4].Get<uint16>();
|
||||
creatureData.equipmentId = fields[5].Get<int8>();
|
||||
creatureData.posX = fields[6].Get<float>();
|
||||
creatureData.posY = fields[7].Get<float>();
|
||||
creatureData.posZ = fields[8].Get<float>();
|
||||
creatureData.orientation = fields[9].Get<float>();
|
||||
creatureData.spawntimesecs = fields[10].Get<uint32>();
|
||||
creatureData.wander_distance = fields[11].Get<float>();
|
||||
creatureData.currentwaypoint = fields[12].Get<uint32>();
|
||||
creatureData.curhealth = fields[13].Get<uint32>();
|
||||
creatureData.curmana = fields[14].Get<uint32>();
|
||||
creatureData.movementType = fields[15].Get<uint8>();
|
||||
creatureData.spawnMask = fields[16].Get<uint8>();
|
||||
creatureData.phaseMask = fields[17].Get<uint32>();
|
||||
creatureData.npcflag = fields[18].Get<uint32>();
|
||||
creatureData.unit_flags = fields[19].Get<uint32>();
|
||||
creatureData.dynamicflags = fields[20].Get<uint32>();
|
||||
creatureData.ScriptId = GetScriptId(fields[21].Get<std::string>());
|
||||
|
||||
if (!creatureData.ScriptId)
|
||||
creatureData.ScriptId = cInfo->ScriptID;
|
||||
|
||||
MapEntry const* mapEntry = sMapStore.LookupEntry(creatureData.mapid);
|
||||
if (!mapEntry)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {}) that spawned at non-existing map (Id: {}), skipped.", spawnId, creatureData.mapid);
|
||||
_creatureDataStore.erase(spawnId);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (mapEntry->IsRaid() && creatureData.spawntimesecs >= 7 * DAY && creatureData.spawntimesecs < 14 * DAY)
|
||||
creatureData.spawntimesecs = 14 * DAY;
|
||||
|
||||
bool ok = true;
|
||||
for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1 && ok; ++diff)
|
||||
{
|
||||
if (_difficultyEntries[diff].find(id1) != _difficultyEntries[diff].end() ||
|
||||
_difficultyEntries[diff].find(id2) != _difficultyEntries[diff].end() ||
|
||||
_difficultyEntries[diff].find(id3) != _difficultyEntries[diff].end())
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {}) that is listed as difficulty {} template (Entries: {}, {}, {}) in `creature_template`, skipped.",
|
||||
spawnId, diff + 1, id1, id2, id3);
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
_creatureDataStore.erase(spawnId);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (creatureData.equipmentId != 0)
|
||||
{
|
||||
if (!GetEquipmentInfo(id1, creatureData.equipmentId) ||
|
||||
(id2 && !GetEquipmentInfo(id2, creatureData.equipmentId)) ||
|
||||
(id3 && !GetEquipmentInfo(id3, creatureData.equipmentId)))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `creature` has creature (Entries: {}, {}, {}) with equipment_id {} not found in table `creature_equip_template`, set to no equipment.",
|
||||
id1, id2, id3, creatureData.equipmentId);
|
||||
creatureData.equipmentId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (creatureData.movementType >= MAX_DB_MOTION_TYPE)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {} Entries: {}, {}, {}) with wrong movement generator type ({}), set to IDLE.",
|
||||
spawnId, id1, id2, id3, creatureData.movementType);
|
||||
creatureData.movementType = IDLE_MOTION_TYPE;
|
||||
}
|
||||
|
||||
if (creatureData.wander_distance < 0.0f)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {} Entries: {}, {}, {}) with `wander_distance`< 0, set to 0.",
|
||||
spawnId, id1, id2, id3);
|
||||
creatureData.wander_distance = 0.0f;
|
||||
}
|
||||
else if (creatureData.movementType == RANDOM_MOTION_TYPE)
|
||||
{
|
||||
if (creatureData.wander_distance == 0.0f)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {} Entries: {}, {}, {}) with `MovementType`=1 (random movement) but with `wander_distance`=0, replace by idle movement type (0).",
|
||||
spawnId, id1, id2, id3);
|
||||
creatureData.movementType = IDLE_MOTION_TYPE;
|
||||
}
|
||||
}
|
||||
else if (creatureData.movementType == IDLE_MOTION_TYPE)
|
||||
{
|
||||
if (creatureData.wander_distance != 0.0f)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {} Entries: {}, {}, {}) with `MovementType`=0 (idle) have `wander_distance`<>0, set to 0.",
|
||||
spawnId, id1, id2, id3);
|
||||
creatureData.wander_distance = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if (creatureData.phaseMask == 0)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {} Entries: {}, {}, {}) with `phaseMask`=0 (not visible for anyone), set to 1.",
|
||||
spawnId, id1, id2, id3);
|
||||
creatureData.phaseMask = 1;
|
||||
}
|
||||
|
||||
return &creatureData;
|
||||
}
|
||||
|
||||
void ObjectMgr::LoadCreatureSparring()
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
@@ -2764,6 +2922,128 @@ void ObjectMgr::LoadGameobjects()
|
||||
LOG_INFO("server.loading", " ");
|
||||
}
|
||||
|
||||
// Loads a single gameobject spawn from DB into the cache.
|
||||
// GameObject::LoadGameObjectFromDB() reads from cache (GetGameObjectData()), not from DB directly,
|
||||
// so this must be called first for spawns not loaded at startup.
|
||||
GameObjectData const* ObjectMgr::LoadGameObjectDataFromDB(ObjectGuid::LowType spawnId)
|
||||
{
|
||||
GameObjectData const* data = GetGameObjectData(spawnId);
|
||||
if (data)
|
||||
return data;
|
||||
|
||||
QueryResult result = WorldDatabase.Query("SELECT gameobject.guid, id, map, position_x, position_y, position_z, orientation, "
|
||||
"rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state, spawnMask, phaseMask, "
|
||||
"ScriptName "
|
||||
"FROM gameobject WHERE gameobject.guid = {}", spawnId);
|
||||
|
||||
if (!result)
|
||||
return nullptr;
|
||||
|
||||
Field* fields = result->Fetch();
|
||||
uint32 entry = fields[1].Get<uint32>();
|
||||
|
||||
GameObjectTemplate const* gInfo = GetGameObjectTemplate(entry);
|
||||
if (!gInfo)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {}) with non-existing gameobject entry {}, skipped.", spawnId, entry);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (gInfo->displayId && !sGameObjectDisplayInfoStore.LookupEntry(gInfo->displayId))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry {} GoType: {}) with an invalid displayId ({}), not loaded.",
|
||||
spawnId, entry, gInfo->type, gInfo->displayId);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GameObjectData& goData = _gameObjectDataStore[spawnId];
|
||||
goData.id = entry;
|
||||
goData.mapid = fields[2].Get<uint16>();
|
||||
goData.posX = fields[3].Get<float>();
|
||||
goData.posY = fields[4].Get<float>();
|
||||
goData.posZ = fields[5].Get<float>();
|
||||
goData.orientation = fields[6].Get<float>();
|
||||
goData.rotation.x = fields[7].Get<float>();
|
||||
goData.rotation.y = fields[8].Get<float>();
|
||||
goData.rotation.z = fields[9].Get<float>();
|
||||
goData.rotation.w = fields[10].Get<float>();
|
||||
goData.spawntimesecs = fields[11].Get<int32>();
|
||||
goData.animprogress = fields[12].Get<uint8>();
|
||||
goData.artKit = 0;
|
||||
goData.ScriptId = GetScriptId(fields[16].Get<std::string>());
|
||||
|
||||
if (!goData.ScriptId)
|
||||
goData.ScriptId = gInfo->ScriptId;
|
||||
|
||||
MapEntry const* mapEntry = sMapStore.LookupEntry(goData.mapid);
|
||||
if (!mapEntry)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) spawned on a non-existing map (Id: {}), skipped.", spawnId, entry, goData.mapid);
|
||||
_gameObjectDataStore.erase(spawnId);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (goData.spawntimesecs == 0 && gInfo->IsDespawnAtAction())
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with `spawntimesecs` (0) value, but the gameobject is marked as despawnable at action.", spawnId, entry);
|
||||
}
|
||||
|
||||
uint32 go_state = fields[13].Get<uint8>();
|
||||
if (go_state >= MAX_GO_STATE)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid `state` ({}) value, skipped.", spawnId, entry, go_state);
|
||||
_gameObjectDataStore.erase(spawnId);
|
||||
return nullptr;
|
||||
}
|
||||
goData.go_state = GOState(go_state);
|
||||
|
||||
goData.spawnMask = fields[14].Get<uint8>();
|
||||
goData.phaseMask = fields[15].Get<uint32>();
|
||||
|
||||
if (goData.rotation.x < -1.0f || goData.rotation.x > 1.0f)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid rotationX ({}) value, skipped.", spawnId, entry, goData.rotation.x);
|
||||
_gameObjectDataStore.erase(spawnId);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (goData.rotation.y < -1.0f || goData.rotation.y > 1.0f)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid rotationY ({}) value, skipped.", spawnId, entry, goData.rotation.y);
|
||||
_gameObjectDataStore.erase(spawnId);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (goData.rotation.z < -1.0f || goData.rotation.z > 1.0f)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid rotationZ ({}) value, skipped.", spawnId, entry, goData.rotation.z);
|
||||
_gameObjectDataStore.erase(spawnId);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (goData.rotation.w < -1.0f || goData.rotation.w > 1.0f)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid rotationW ({}) value, skipped.", spawnId, entry, goData.rotation.w);
|
||||
_gameObjectDataStore.erase(spawnId);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!MapMgr::IsValidMapCoord(goData.mapid, goData.posX, goData.posY, goData.posZ, goData.orientation))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid coordinates, skipped.", spawnId, entry);
|
||||
_gameObjectDataStore.erase(spawnId);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (goData.phaseMask == 0)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with `phaseMask`=0 (not visible for anyone), set to 1.", spawnId, entry);
|
||||
goData.phaseMask = 1;
|
||||
}
|
||||
|
||||
return &goData;
|
||||
}
|
||||
|
||||
void ObjectMgr::AddGameobjectToGrid(ObjectGuid::LowType guid, GameObjectData const* data)
|
||||
{
|
||||
uint8 mask = data->spawnMask;
|
||||
|
||||
@@ -1226,6 +1226,21 @@ public:
|
||||
[[nodiscard]] CreatureSparringContainer const& GetSparringData() const { return _creatureSparringStore; }
|
||||
|
||||
CreatureData& NewOrExistCreatureData(ObjectGuid::LowType spawnId) { return _creatureDataStore[spawnId]; }
|
||||
/**
|
||||
* @brief Loads a single creature spawn entry from the database into the data store cache.
|
||||
*
|
||||
* This is needed as a prerequisite for Creature::LoadCreatureFromDB(), which reads
|
||||
* from the in-memory cache (via GetCreatureData()) rather than querying the DB itself.
|
||||
* For spawns not loaded during server startup, this method populates the cache so that
|
||||
* Creature::LoadCreatureFromDB() can then create the live entity.
|
||||
*
|
||||
* Returns the cached data if already loaded, or nullptr if the spawn doesn't exist
|
||||
* or fails validation.
|
||||
*
|
||||
* @param spawnId The creature spawn GUID to load.
|
||||
* @return Pointer to the cached CreatureData, or nullptr on failure.
|
||||
*/
|
||||
CreatureData const* LoadCreatureDataFromDB(ObjectGuid::LowType spawnId);
|
||||
void DeleteCreatureData(ObjectGuid::LowType spawnId);
|
||||
[[nodiscard]] ObjectGuid GetLinkedRespawnGuid(ObjectGuid guid) const
|
||||
{
|
||||
@@ -1311,6 +1326,21 @@ public:
|
||||
[[nodiscard]] QuestGreeting const* GetQuestGreeting(TypeID type, uint32 id) const;
|
||||
|
||||
GameObjectData& NewGOData(ObjectGuid::LowType guid) { return _gameObjectDataStore[guid]; }
|
||||
/**
|
||||
* @brief Loads a single gameobject spawn entry from the database into the data store cache.
|
||||
*
|
||||
* This is needed as a prerequisite for GameObject::LoadGameObjectFromDB(), which reads
|
||||
* from the in-memory cache (via GetGameObjectData()) rather than querying the DB itself.
|
||||
* For spawns not loaded during server startup, this method populates the cache so that
|
||||
* GameObject::LoadGameObjectFromDB() can then create the live entity.
|
||||
*
|
||||
* Returns the cached data if already loaded, or nullptr if the spawn doesn't exist
|
||||
* or fails validation.
|
||||
*
|
||||
* @param spawnId The gameobject spawn GUID to load.
|
||||
* @return Pointer to the cached GameObjectData, or nullptr on failure.
|
||||
*/
|
||||
GameObjectData const* LoadGameObjectDataFromDB(ObjectGuid::LowType spawnId);
|
||||
void DeleteGOData(ObjectGuid::LowType guid);
|
||||
|
||||
[[nodiscard]] ModuleString const* GetModuleString(std::string module, uint32 id) const
|
||||
|
||||
@@ -50,6 +50,7 @@ public:
|
||||
{ "turn", HandleGameObjectTurnCommand, SEC_ADMINISTRATOR, Console::No },
|
||||
{ "add temp", HandleGameObjectAddTempCommand, SEC_GAMEMASTER, Console::No },
|
||||
{ "add", HandleGameObjectAddCommand, SEC_ADMINISTRATOR, Console::No },
|
||||
{ "load", HandleGameObjectLoadCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||
{ "set phase", HandleGameObjectSetPhaseCommand, SEC_ADMINISTRATOR, Console::No },
|
||||
{ "set state", HandleGameObjectSetStateCommand, SEC_ADMINISTRATOR, Console::No },
|
||||
{ "respawn", HandleGameObjectRespawn, SEC_GAMEMASTER, Console::No }
|
||||
@@ -144,6 +145,64 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleGameObjectLoadCommand(ChatHandler* handler, GameObjectSpawnId spawnId)
|
||||
{
|
||||
if (!spawnId)
|
||||
return false;
|
||||
|
||||
if (sObjectMgr->GetGameObjectData(spawnId))
|
||||
{
|
||||
handler->SendErrorMessage("Gameobject spawn {} is already loaded.", uint32(spawnId));
|
||||
return false;
|
||||
}
|
||||
|
||||
GameObjectData const* data = sObjectMgr->LoadGameObjectDataFromDB(spawnId);
|
||||
if (!data)
|
||||
{
|
||||
handler->SendErrorMessage("Gameobject spawn {} not found in the database.", uint32(spawnId));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sPoolMgr->IsPartOfAPool<GameObject>(spawnId))
|
||||
{
|
||||
handler->SendErrorMessage("Gameobject spawn {} is part of a pool and cannot be manually loaded.", uint32(spawnId));
|
||||
return false;
|
||||
}
|
||||
|
||||
QueryResult eventResult = WorldDatabase.Query("SELECT guid FROM game_event_gameobject WHERE guid = {}", uint32(spawnId));
|
||||
if (eventResult)
|
||||
{
|
||||
handler->SendErrorMessage("Gameobject spawn {} is managed by the game event system and cannot be manually loaded.", uint32(spawnId));
|
||||
return false;
|
||||
}
|
||||
|
||||
Map* map = sMapMgr->FindBaseNonInstanceMap(data->mapid);
|
||||
if (!map)
|
||||
{
|
||||
handler->SendErrorMessage("Gameobject spawn {} is on a non-continent map (ID: {}). Only continent maps are supported.", uint32(spawnId), data->mapid);
|
||||
return false;
|
||||
}
|
||||
|
||||
GameObjectTemplate const* objectInfo = sObjectMgr->GetGameObjectTemplate(data->id);
|
||||
if (!objectInfo)
|
||||
{
|
||||
handler->SendErrorMessage("Gameobject template not found for entry {}.", data->id);
|
||||
return false;
|
||||
}
|
||||
|
||||
GameObject* object = sObjectMgr->IsGameObjectStaticTransport(objectInfo->entry) ? new StaticTransport() : new GameObject();
|
||||
if (!object->LoadGameObjectFromDB(spawnId, map, true))
|
||||
{
|
||||
delete object;
|
||||
handler->SendErrorMessage("Failed to load gameobject spawn {}.", uint32(spawnId));
|
||||
return false;
|
||||
}
|
||||
|
||||
sObjectMgr->AddGameobjectToGrid(spawnId, data);
|
||||
handler->PSendSysMessage("Gameobject spawn {} loaded successfully.", uint32(spawnId));
|
||||
return true;
|
||||
}
|
||||
|
||||
// add go, temp only
|
||||
static bool HandleGameObjectAddTempCommand(ChatHandler* handler, GameObjectEntry objectId, Optional<uint64> spawntime)
|
||||
{
|
||||
|
||||
@@ -21,9 +21,11 @@
|
||||
#include "CreatureGroups.h"
|
||||
#include "GameTime.h"
|
||||
#include "Language.h"
|
||||
#include "MapMgr.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Pet.h"
|
||||
#include "Player.h"
|
||||
#include "PoolMgr.h"
|
||||
#include "TargetedMovementGenerator.h" // for HandleNpcUnFollowCommand
|
||||
#include "Transport.h"
|
||||
#include <string>
|
||||
@@ -192,6 +194,7 @@ public:
|
||||
{ "add", npcAddCommandTable },
|
||||
{ "delete", npcDeleteCommandTable },
|
||||
{ "follow", npcFollowCommandTable },
|
||||
{ "load", HandleNpcLoadCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||
{ "set", npcSetCommandTable }
|
||||
};
|
||||
static ChatCommandTable commandTable =
|
||||
@@ -260,6 +263,57 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleNpcLoadCommand(ChatHandler* handler, CreatureSpawnId spawnId)
|
||||
{
|
||||
if (!spawnId)
|
||||
return false;
|
||||
|
||||
if (sObjectMgr->GetCreatureData(spawnId))
|
||||
{
|
||||
handler->SendErrorMessage("Creature spawn {} is already loaded.", uint32(spawnId));
|
||||
return false;
|
||||
}
|
||||
|
||||
CreatureData const* data = sObjectMgr->LoadCreatureDataFromDB(spawnId);
|
||||
if (!data)
|
||||
{
|
||||
handler->SendErrorMessage("Creature spawn {} not found in the database.", uint32(spawnId));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sPoolMgr->IsPartOfAPool<Creature>(spawnId))
|
||||
{
|
||||
handler->SendErrorMessage("Creature spawn {} is part of a pool and cannot be manually loaded.", uint32(spawnId));
|
||||
return false;
|
||||
}
|
||||
|
||||
QueryResult eventResult = WorldDatabase.Query("SELECT guid FROM game_event_creature WHERE guid = {}", uint32(spawnId));
|
||||
if (eventResult)
|
||||
{
|
||||
handler->SendErrorMessage("Creature spawn {} is managed by the game event system and cannot be manually loaded.", uint32(spawnId));
|
||||
return false;
|
||||
}
|
||||
|
||||
Map* map = sMapMgr->FindBaseNonInstanceMap(data->mapid);
|
||||
if (!map)
|
||||
{
|
||||
handler->SendErrorMessage("Creature spawn {} is on a non-continent map (ID: {}). Only continent maps are supported.", uint32(spawnId), data->mapid);
|
||||
return false;
|
||||
}
|
||||
|
||||
Creature* creature = new Creature();
|
||||
if (!creature->LoadCreatureFromDB(spawnId, map, true, true))
|
||||
{
|
||||
delete creature;
|
||||
handler->SendErrorMessage("Failed to load creature spawn {}.", uint32(spawnId));
|
||||
return false;
|
||||
}
|
||||
|
||||
sObjectMgr->AddCreatureToGrid(spawnId, data);
|
||||
handler->PSendSysMessage("Creature spawn {} loaded successfully.", uint32(spawnId));
|
||||
return true;
|
||||
}
|
||||
|
||||
//add item in vendorlist
|
||||
static bool HandleNpcAddVendorItemCommand(ChatHandler* handler, ItemTemplate const* item, Optional<uint32> mc, Optional<uint32> it, Optional<uint32> ec, Optional<bool> addMulti)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user