mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-13 01:08:35 +00:00
feat(Core/Creature): Dual id Spawning WIP (#10115)
* feat(Core/Creature): Multi id Spawning WIP * Update Creature.cpp * Update PR * Add Sql * Update rev_1641837958335217980.sql * Update src/server/game/Globals/ObjectMgr.cpp Co-authored-by: Kitzunu <24550914+Kitzunu@users.noreply.github.com> * Update src/server/game/Globals/ObjectMgr.cpp Co-authored-by: Kitzunu <24550914+Kitzunu@users.noreply.github.com> * Update rev_1641837958335217980.sql * Update cs_npc.cpp * Create changes_1641842959398297300.md * Fixed issue added with random model PR * Update GameEventMgr.cpp Co-authored-by: Kitzunu <24550914+Kitzunu@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
INSERT INTO `version_db_world` (`sql_rev`) VALUES ('1641837958335217980');
|
||||
|
||||
ALTER TABLE `creature`
|
||||
CHANGE COLUMN `id` `creature_id1` MEDIUMINT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Creature Identifier' AFTER `guid`,
|
||||
ADD COLUMN `creature_id2` MEDIUMINT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Creature Identifier' AFTER `creature_id1`,
|
||||
ADD COLUMN `chance_id1` FLOAT UNSIGNED NOT NULL DEFAULT 100 COMMENT 'Chance id1 spawns' AFTER `creature_id2`;
|
||||
16
doc/changelog/pendings/changes_1641842959398297300.md
Normal file
16
doc/changelog/pendings/changes_1641842959398297300.md
Normal file
@@ -0,0 +1,16 @@
|
||||
We suggest that you always use the latest version of our master branch.
|
||||
https://github.com/azerothcore/azerothcore-wotlk/tree/master
|
||||
|
||||
### How to upgrade
|
||||
|
||||
For server administrators: instructions about how to upgrade existing servers are available [here](http://www.azerothcore.org/wiki/Upgrade-from-pre-2.0.0-to-latest-master).
|
||||
|
||||
## Release notes
|
||||
|
||||
This PR removes the modelId column from creature table to allow us to move to a dual entry spawn system.
|
||||
|
||||
If this causes an issue for in game or custom spawns the following line of SAI can update the modelId.
|
||||
|
||||
(#entryorguid,0,0,0,11,0,100,0,0,0,0,0,0,3,0,#modelId,0,0,0,0,1,0,0,0,0,0,0,0,0,"Creature Name - On Spawn - Change Model to #modelId"),
|
||||
|
||||
Special thanks to @Shin @Kitzunu @M'Dic for assistance.
|
||||
@@ -80,10 +80,10 @@ void WorldDatabaseConnection::DoPrepareStatements()
|
||||
PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, modelid1, modelid2, modelid3, modelid4, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, faction, npcflag, speed_walk, speed_run, speed_swim, speed_flight, detection_range, scale, `rank`, dmgschool, DamageModifier, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, dynamicflags, family, trainer_type, trainer_spell, trainer_class, trainer_race, type, type_flags, lootid, pickpocketloot, skinloot, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, ScriptName FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId WHERE entry = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_BY_ID, "SELECT guid, delay, command, datalong, datalong2, dataint, x, y, z, o FROM waypoint_scripts WHERE id = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_ITEM_TEMPLATE_BY_NAME, "SELECT entry FROM item_template WHERE name = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_CREATURE_BY_ID, "SELECT guid FROM creature WHERE id = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_CREATURE_BY_ID, "SELECT guid FROM creature WHERE creature_id1 = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_GAMEOBJECT_NEAREST, "SELECT guid, id, position_x, position_y, position_z, map, (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) AS order_ FROM gameobject WHERE map = ? AND (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) <= ? AND (phaseMask & ?) <> 0 ORDER BY order_", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_CREATURE_NEAREST, "SELECT guid, id, position_x, position_y, position_z, map, (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) AS order_ FROM creature WHERE map = ? AND (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) <= ? AND (phaseMask & ?) <> 0 ORDER BY order_", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_INS_CREATURE, "INSERT INTO creature (guid, id , map, spawnMask, phaseMask, equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, wander_distance, currentwaypoint, curhealth, curmana, MovementType, npcflag, unit_flags, dynamicflags) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(WORLD_SEL_CREATURE_NEAREST, "SELECT guid, creature_id1,creature_id2, position_x, position_y, position_z, map, (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) AS order_ FROM creature WHERE map = ? AND (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) <= ? AND (phaseMask & ?) <> 0 ORDER BY order_", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_INS_CREATURE, "INSERT INTO creature (guid, creature_id1, creature_id2, chance_id1, map, spawnMask, phaseMask, equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, wander_distance, currentwaypoint, curhealth, curmana, MovementType, npcflag, unit_flags, dynamicflags) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(WORLD_DEL_GAME_EVENT_CREATURE, "DELETE FROM game_event_creature WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(WORLD_DEL_GAME_EVENT_MODEL_EQUIP, "DELETE FROM game_event_model_equip WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(WORLD_INS_GAMEOBJECT, "INSERT INTO gameobject (guid, id, map, spawnMask, phaseMask, position_x, position_y, position_z, orientation, rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
|
||||
|
||||
@@ -1610,7 +1610,12 @@ bool Creature::LoadCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool ad
|
||||
m_creatureData = data;
|
||||
m_spawnId = spawnId;
|
||||
|
||||
if (!Create(map->GenerateLowGuid<HighGuid::Unit>(), map, data->phaseMask, data->id, 0, data->posX, data->posY, data->posZ, data->orientation, data))
|
||||
// Add to world
|
||||
uint32 entry = data->id;
|
||||
if(data->id2)
|
||||
entry = (rand() % 100 <= data->chance_id1) ? data->id : data->id2;
|
||||
|
||||
if (!Create(map->GenerateLowGuid<HighGuid::Unit>(), map, data->phaseMask, entry, 0, data->posX, data->posY, data->posZ, data->orientation, data))
|
||||
return false;
|
||||
|
||||
//We should set first home position, because then AI calls home movement
|
||||
@@ -1900,26 +1905,34 @@ void Creature::Respawn(bool force)
|
||||
if (getDeathState() == DEAD)
|
||||
{
|
||||
if (m_spawnId)
|
||||
{
|
||||
GetMap()->RemoveCreatureRespawnTime(m_spawnId);
|
||||
CreatureData const* data = sObjectMgr->GetCreatureData(m_spawnId);
|
||||
// Respawn check if spawn has 2 entries
|
||||
if (data->id2)
|
||||
{
|
||||
uint32 entry = (rand() % 100 <= data->chance_id1) ? data->id : data->id2;
|
||||
UpdateEntry(entry, data, true); // Select Random Entry
|
||||
m_defaultMovementType = MovementGeneratorType(data->movementType); // Reload Movement Type
|
||||
LoadEquipment(data->equipmentId); // Reload Equipment
|
||||
AIM_Initialize(); // Reload AI
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_originalEntry != GetEntry())
|
||||
UpdateEntry(m_originalEntry);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DEBUG("entities.unit", "Respawning creature %s (SpawnId: %u, %s)", GetName().c_str(), GetSpawnId(), GetGUID().ToString().c_str());
|
||||
m_respawnTime = 0;
|
||||
ResetPickPocketLootTime();
|
||||
loot.clear();
|
||||
|
||||
if (m_originalEntry != GetEntry())
|
||||
UpdateEntry(m_originalEntry);
|
||||
|
||||
SelectLevel();
|
||||
|
||||
setDeathState(JUST_RESPAWNED);
|
||||
// MDic - Acidmanifesto
|
||||
// If creature has genders it will consider gender changing on respawn.
|
||||
if (sObjectMgr->GetCreatureTemplate(m_originalEntry))
|
||||
{
|
||||
InitEntry(m_originalEntry);
|
||||
}
|
||||
|
||||
// MDic - Acidmanifesto
|
||||
uint32 displayID = GetNativeDisplayId();
|
||||
if (sObjectMgr->GetCreatureModelRandomGender(&displayID)) // Cancel load if no model defined
|
||||
{
|
||||
@@ -2549,10 +2562,7 @@ bool Creature::LoadCreaturesAddon(bool reload)
|
||||
SetVisibilityDistanceOverride(cainfo->visibilityDistanceType);
|
||||
}
|
||||
|
||||
if (cainfo->emote != 0)
|
||||
{
|
||||
SetUInt32Value(UNIT_NPC_EMOTESTATE, cainfo->emote);
|
||||
}
|
||||
SetUInt32Value(UNIT_NPC_EMOTESTATE, cainfo->emote);
|
||||
|
||||
// Check if visibility distance different
|
||||
if (cainfo->visibilityDistanceType != VisibilityDistanceType::Normal)
|
||||
|
||||
@@ -360,6 +360,8 @@ struct CreatureData
|
||||
{
|
||||
CreatureData() { }
|
||||
uint32 id{0}; // entry in creature_template
|
||||
uint32 id2{0}; // entry in creature_template
|
||||
float chance_id1{0}; // Chance for first id to spawn
|
||||
uint16 mapid{0};
|
||||
uint32 phaseMask{0};
|
||||
uint32 displayid{0};
|
||||
|
||||
@@ -490,8 +490,8 @@ void GameEventMgr::LoadFromDB()
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
// 0 1 2 3 4
|
||||
QueryResult result = WorldDatabase.Query("SELECT creature.guid, creature.id, game_event_model_equip.eventEntry, game_event_model_equip.modelid, game_event_model_equip.equipment_id "
|
||||
// 0 1 2 3 4 5
|
||||
QueryResult result = WorldDatabase.Query("SELECT creature.guid, creature.creature_id1, creature.creature_id2, game_event_model_equip.eventEntry, game_event_model_equip.modelid, game_event_model_equip.equipment_id "
|
||||
"FROM creature JOIN game_event_model_equip ON creature.guid=game_event_model_equip.guid");
|
||||
|
||||
if (!result)
|
||||
@@ -508,7 +508,8 @@ void GameEventMgr::LoadFromDB()
|
||||
|
||||
ObjectGuid::LowType guid = fields[0].GetUInt32();
|
||||
uint32 entry = fields[1].GetUInt32();
|
||||
uint16 event_id = fields[2].GetUInt8();
|
||||
//uint32 entry2 = fields[2].GetUInt32();
|
||||
uint16 event_id = fields[3].GetUInt8();
|
||||
|
||||
if (event_id >= mGameEventModelEquip.size())
|
||||
{
|
||||
@@ -518,8 +519,8 @@ void GameEventMgr::LoadFromDB()
|
||||
|
||||
ModelEquipList& equiplist = mGameEventModelEquip[event_id];
|
||||
ModelEquip newModelEquipSet;
|
||||
newModelEquipSet.modelid = fields[3].GetUInt32();
|
||||
newModelEquipSet.equipment_id = fields[4].GetUInt8();
|
||||
newModelEquipSet.modelid = fields[4].GetUInt32();
|
||||
newModelEquipSet.equipment_id = fields[5].GetUInt8();
|
||||
newModelEquipSet.equipement_id_prev = 0;
|
||||
newModelEquipSet.modelid_prev = 0;
|
||||
|
||||
|
||||
@@ -1486,7 +1486,7 @@ void ObjectMgr::LoadCreatureMovementOverrides()
|
||||
"COALESCE(cmo.InteractionPauseTimer, ctm.InteractionPauseTimer) "
|
||||
"FROM creature_movement_override AS cmo "
|
||||
"LEFT JOIN creature AS c ON c.guid = cmo.SpawnId "
|
||||
"LEFT JOIN creature_template_movement AS ctm ON ctm.CreatureId = c.id");
|
||||
"LEFT JOIN creature_template_movement AS ctm ON ctm.CreatureId = c.creature_id1");
|
||||
if (!result)
|
||||
{
|
||||
LOG_INFO("server.loading", ">> Loaded 0 creature movement overrides. DB table `creature_movement_override` is empty!");
|
||||
@@ -1984,11 +1984,11 @@ void ObjectMgr::LoadCreatures()
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
// 0 1 2 3 4 5 6 7 8 9 10
|
||||
QueryResult result = WorldDatabase.Query("SELECT creature.guid, id, map, equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, wander_distance, "
|
||||
// 11 12 13 14 15 16 17 18 19 20 21
|
||||
// 0 1 2 3 4 5 6 7 8 9 10 11
|
||||
QueryResult result = WorldDatabase.Query("SELECT creature.guid, creature_id1, creature_id2, chance_id1, map, equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, wander_distance, "
|
||||
// 12 13 14 15 16 17 18 19 20 21 22
|
||||
"currentwaypoint, curhealth, curmana, MovementType, spawnMask, phaseMask, eventEntry, pool_entry, creature.npcflag, creature.unit_flags, creature.dynamicflags, "
|
||||
// 22
|
||||
// 23
|
||||
"creature.ScriptName "
|
||||
"FROM creature "
|
||||
"LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid "
|
||||
@@ -2016,37 +2016,56 @@ void ObjectMgr::LoadCreatures()
|
||||
Field* fields = result->Fetch();
|
||||
|
||||
ObjectGuid::LowType spawnId = fields[0].GetUInt32();
|
||||
uint32 entry = fields[1].GetUInt32();
|
||||
uint32 id = fields[1].GetUInt32();
|
||||
uint32 id2 = fields[2].GetUInt32();
|
||||
uint32 chance = fields[3].GetUInt32();
|
||||
|
||||
CreatureTemplate const* cInfo = GetCreatureTemplate(entry);
|
||||
CreatureTemplate const* cInfo = GetCreatureTemplate(id);
|
||||
if (!cInfo)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: %u) with non existing creature entry %u, skipped.", spawnId, entry);
|
||||
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: %u) with non existing creature entry %u in creature_id1 field, skipped.", spawnId, id);
|
||||
continue;
|
||||
}
|
||||
|
||||
CreatureTemplate const* cInfo2 = GetCreatureTemplate(id2);
|
||||
if (!cInfo2 && id2)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: %u) with non existing creature entry %u in creature_id2 field, skipped.", spawnId, id2);
|
||||
continue;
|
||||
}
|
||||
if (!chance || chance > 100)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `creature` chance_id1 (Value: %u) must be greater than 0 and less than or equal to 100, skipped.", chance);
|
||||
continue;
|
||||
}
|
||||
if (chance == 100 && id2)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `creature` has spawnid = %u chance = 100 even though creature_id2 has an entry. changed to 50.", spawnId);
|
||||
chance = 50;
|
||||
}
|
||||
CreatureData& data = _creatureDataStore[spawnId];
|
||||
data.id = entry;
|
||||
data.mapid = fields[2].GetUInt16();
|
||||
data.equipmentId = fields[3].GetInt8();
|
||||
data.posX = fields[4].GetFloat();
|
||||
data.posY = fields[5].GetFloat();
|
||||
data.posZ = fields[6].GetFloat();
|
||||
data.orientation = fields[7].GetFloat();
|
||||
data.spawntimesecs = fields[8].GetUInt32();
|
||||
data.wander_distance = fields[9].GetFloat();
|
||||
data.currentwaypoint = fields[10].GetUInt32();
|
||||
data.curhealth = fields[11].GetUInt32();
|
||||
data.curmana = fields[12].GetUInt32();
|
||||
data.movementType = fields[13].GetUInt8();
|
||||
data.spawnMask = fields[14].GetUInt8();
|
||||
data.phaseMask = fields[15].GetUInt32();
|
||||
int16 gameEvent = fields[16].GetInt8();
|
||||
uint32 PoolId = fields[17].GetUInt32();
|
||||
data.npcflag = fields[18].GetUInt32();
|
||||
data.unit_flags = fields[19].GetUInt32();
|
||||
data.dynamicflags = fields[20].GetUInt32();
|
||||
data.ScriptId = GetScriptId(fields[21].GetString());
|
||||
data.id = id;
|
||||
data.id2 = id2;
|
||||
data.chance_id1 = chance;
|
||||
data.mapid = fields[4].GetUInt16();
|
||||
data.equipmentId = fields[5].GetInt8();
|
||||
data.posX = fields[6].GetFloat();
|
||||
data.posY = fields[7].GetFloat();
|
||||
data.posZ = fields[8].GetFloat();
|
||||
data.orientation = fields[9].GetFloat();
|
||||
data.spawntimesecs = fields[10].GetUInt32();
|
||||
data.wander_distance = fields[11].GetFloat();
|
||||
data.currentwaypoint = fields[12].GetUInt32();
|
||||
data.curhealth = fields[13].GetUInt32();
|
||||
data.curmana = fields[14].GetUInt32();
|
||||
data.movementType = fields[15].GetUInt8();
|
||||
data.spawnMask = fields[16].GetUInt8();
|
||||
data.phaseMask = fields[17].GetUInt32();
|
||||
int16 gameEvent = fields[18].GetInt8();
|
||||
uint32 PoolId = fields[19].GetUInt32();
|
||||
data.npcflag = fields[20].GetUInt32();
|
||||
data.unit_flags = fields[21].GetUInt32();
|
||||
data.dynamicflags = fields[22].GetUInt32();
|
||||
data.ScriptId = GetScriptId(fields[23].GetString());
|
||||
|
||||
if (!data.ScriptId)
|
||||
data.ScriptId = cInfo->ScriptID;
|
||||
|
||||
@@ -670,10 +670,11 @@ public:
|
||||
Field* fields = result->Fetch();
|
||||
ObjectGuid::LowType guid = fields[0].GetUInt32();
|
||||
uint32 entry = fields[1].GetUInt32();
|
||||
float x = fields[2].GetFloat();
|
||||
float y = fields[3].GetFloat();
|
||||
float z = fields[4].GetFloat();
|
||||
uint16 mapId = fields[5].GetUInt16();
|
||||
//uint32 entry2 = fields[2].GetUInt32();
|
||||
float x = fields[3].GetFloat();
|
||||
float y = fields[4].GetFloat();
|
||||
float z = fields[5].GetFloat();
|
||||
uint16 mapId = fields[6].GetUInt16();
|
||||
|
||||
CreatureTemplate const* creatureTemplate = sObjectMgr->GetCreatureTemplate(entry);
|
||||
if (!creatureTemplate)
|
||||
|
||||
@@ -372,7 +372,7 @@ public:
|
||||
std::string normalizedName(name);
|
||||
WorldDatabase.EscapeString(normalizedName);
|
||||
|
||||
QueryResult result = WorldDatabase.PQuery("SELECT c.position_x, c.position_y, c.position_z, c.orientation, c.map, ct.name FROM creature c INNER JOIN creature_template ct ON c.id = ct.entry WHERE ct.name LIKE '%s'", normalizedName.c_str());
|
||||
QueryResult result = WorldDatabase.PQuery("SELECT c.position_x, c.position_y, c.position_z, c.orientation, c.map, ct.name FROM creature c INNER JOIN creature_template ct ON c.creature_id1 = ct.entry WHERE ct.name LIKE '%s'", normalizedName.c_str());
|
||||
if (!result)
|
||||
{
|
||||
handler->SendSysMessage(LANG_COMMAND_GOCREATNOTFOUND);
|
||||
|
||||
Reference in New Issue
Block a user