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;
|
||||
|
||||
Reference in New Issue
Block a user