mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-22 05:06:24 +00:00
refactor(Core/ObjectMgr): Implement display probabilities. (#19068)
* Init. Cherry-picked from TC commits9d210476e5andc488fb219aCo-Authored-By: Traesh <9392905+traesh@users.noreply.github.com> Co-Authored-By: Shauren <shauren.trinity@gmail.com> * Add brute data. Needs validation against what existed before, i.e. Classic change prevention. * Add validation info for brute data. * Remove incomplete data queries. * Requested changes. * Whitespace. * Requested change. Table name. Co-authored-by: Kitzunu <24550914+Kitzunu@users.noreply.github.com> * Requested change. Table name. Co-authored-by: Kitzunu <24550914+Kitzunu@users.noreply.github.com> * Resolve the funny merge conflicts. I wonder why git blame doesn't work on a file with 20k lines in it that's odd huh champ. * Remove unused parameter. * Remove uses of unused parameter. * Use unused parameter. Hopefully? * I will cry. * Sobbing endlessly. * Remove comment. * Adjust table structure query. Remove length parameters and allow null for build value. Co-authored-by: Kitzunu <24550914+Kitzunu@users.noreply.github.com> * Adjust column datatype and add check constraint. --------- Co-authored-by: Traesh <9392905+traesh@users.noreply.github.com> Co-authored-by: Shauren <shauren.trinity@gmail.com> Co-authored-by: Kitzunu <24550914+Kitzunu@users.noreply.github.com>
This commit is contained in:
@@ -77,7 +77,7 @@ void WorldDatabaseConnection::DoPrepareStatements()
|
||||
PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID, "SELECT id FROM waypoint_scripts WHERE guid = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_DEL_CREATURE, "DELETE FROM creature WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(WORLD_SEL_COMMANDS, "SELECT name, security, help FROM command", CONNECTION_SYNCH);
|
||||
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_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, 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 id1 = ? OR id2 = ? OR id3 = ?", CONNECTION_SYNCH);
|
||||
|
||||
@@ -424,10 +424,10 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
{
|
||||
if (CreatureTemplate const* ci = sObjectMgr->GetCreatureTemplate(e.action.morphOrMount.creature))
|
||||
{
|
||||
uint32 displayId = ObjectMgr::ChooseDisplayId(ci);
|
||||
target->ToCreature()->SetDisplayId(displayId);
|
||||
CreatureModel const* model = ObjectMgr::ChooseDisplayId(ci);
|
||||
target->ToCreature()->SetDisplayId(model->CreatureDisplayID, model->DisplayScale);
|
||||
LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_MORPH_TO_ENTRY_OR_MODEL: Creature entry {}, GuidLow {} set displayid to {}",
|
||||
target->GetEntry(), target->GetGUID().ToString(), displayId);
|
||||
target->GetEntry(), target->GetGUID().ToString(), model->CreatureDisplayID);
|
||||
}
|
||||
}
|
||||
//if no param1, then use value from param2 (modelId)
|
||||
@@ -1316,7 +1316,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
if (e.action.morphOrMount.creature > 0)
|
||||
{
|
||||
if (CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(e.action.morphOrMount.creature))
|
||||
target->ToUnit()->Mount(ObjectMgr::ChooseDisplayId(cInfo));
|
||||
target->ToUnit()->Mount(ObjectMgr::ChooseDisplayId(cInfo)->CreatureDisplayID);
|
||||
}
|
||||
else
|
||||
target->ToUnit()->Mount(e.action.morphOrMount.model);
|
||||
|
||||
@@ -116,51 +116,104 @@ VendorItem const* VendorItemData::FindItemCostPair(uint32 item_id, uint32 extend
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32 CreatureTemplate::GetRandomValidModelId() const
|
||||
CreatureModel const CreatureModel::DefaultInvisibleModel(11686, 1.0f, 1.0f);
|
||||
CreatureModel const CreatureModel::DefaultVisibleModel(17519, 1.0f, 1.0f);
|
||||
|
||||
CreatureModel const* CreatureTemplate::GetModelByIdx(uint32 idx) const
|
||||
{
|
||||
uint8 c = 0;
|
||||
uint32 modelIDs[4];
|
||||
|
||||
if (Modelid1) modelIDs[c++] = Modelid1;
|
||||
if (Modelid2) modelIDs[c++] = Modelid2;
|
||||
if (Modelid3) modelIDs[c++] = Modelid3;
|
||||
if (Modelid4) modelIDs[c++] = Modelid4;
|
||||
|
||||
return ((c > 0) ? modelIDs[urand(0, c - 1)] : 0);
|
||||
return idx < Models.size() ? &Models[idx] : nullptr;
|
||||
}
|
||||
|
||||
uint32 CreatureTemplate::GetFirstValidModelId() const
|
||||
CreatureModel const* CreatureTemplate::GetRandomValidModel() const
|
||||
{
|
||||
if (Modelid1) return Modelid1;
|
||||
if (Modelid2) return Modelid2;
|
||||
if (Modelid3) return Modelid3;
|
||||
if (Modelid4) return Modelid4;
|
||||
return 0;
|
||||
if (!Models.size())
|
||||
return nullptr;
|
||||
|
||||
// If only one element, ignore the Probability (even if 0)
|
||||
if (Models.size() == 1)
|
||||
return &Models[0];
|
||||
|
||||
auto selectedItr = Acore::Containers::SelectRandomWeightedContainerElement(Models, [](CreatureModel const& model)
|
||||
{
|
||||
return model.Probability;
|
||||
});
|
||||
|
||||
return &(*selectedItr);
|
||||
}
|
||||
|
||||
CreatureModel const* CreatureTemplate::GetFirstValidModel() const
|
||||
{
|
||||
for (CreatureModel const& model : Models)
|
||||
if (model.CreatureDisplayID)
|
||||
return &model;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CreatureModel const* CreatureTemplate::GetModelWithDisplayId(uint32 displayId) const
|
||||
{
|
||||
for (CreatureModel const& model : Models)
|
||||
if (displayId == model.CreatureDisplayID)
|
||||
return &model;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CreatureModel const* CreatureTemplate::GetFirstInvisibleModel() const
|
||||
{
|
||||
for (CreatureModel const& model : Models)
|
||||
if (CreatureModelInfo const* modelInfo = sObjectMgr->GetCreatureModelInfo(model.CreatureDisplayID))
|
||||
if (modelInfo && modelInfo->is_trigger)
|
||||
return &model;
|
||||
|
||||
return &CreatureModel::DefaultInvisibleModel;
|
||||
}
|
||||
|
||||
CreatureModel const* CreatureTemplate::GetFirstVisibleModel() const
|
||||
{
|
||||
for (CreatureModel const& model : Models)
|
||||
if (CreatureModelInfo const* modelInfo = sObjectMgr->GetCreatureModelInfo(model.CreatureDisplayID))
|
||||
if (modelInfo && !modelInfo->is_trigger)
|
||||
return &model;
|
||||
|
||||
return &CreatureModel::DefaultVisibleModel;
|
||||
}
|
||||
|
||||
void CreatureTemplate::InitializeQueryData()
|
||||
{
|
||||
queryData.Initialize(SMSG_CREATURE_QUERY_RESPONSE, 1);
|
||||
|
||||
queryData << uint32(Entry); // creature entry
|
||||
queryData << uint32(Entry); // creature entry
|
||||
queryData << Name;
|
||||
queryData << uint8(0) << uint8(0) << uint8(0); // name2, name3, name4, always empty
|
||||
queryData << uint8(0) << uint8(0) << uint8(0); // name2, name3, name4, always empty
|
||||
queryData << SubName;
|
||||
queryData << IconName; // "Directions" for guard, string for Icons 2.3.0
|
||||
queryData << uint32(type_flags); // flags
|
||||
queryData << uint32(type); // CreatureType.dbc
|
||||
queryData << uint32(family); // CreatureFamily.dbc
|
||||
queryData << uint32(rank); // Creature Rank (elite, boss, etc)
|
||||
queryData << uint32(KillCredit[0]); // new in 3.1, kill credit
|
||||
queryData << uint32(KillCredit[1]); // new in 3.1, kill credit
|
||||
queryData << uint32(Modelid1); // Modelid1
|
||||
queryData << uint32(Modelid2); // Modelid2
|
||||
queryData << uint32(Modelid3); // Modelid3
|
||||
queryData << uint32(Modelid4); // Modelid4
|
||||
queryData << float(ModHealth); // dmg/hp modifier
|
||||
queryData << float(ModMana); // dmg/mana modifier
|
||||
queryData << IconName; // "Directions" for guard, string for Icons 2.3.0
|
||||
queryData << uint32(type_flags); // flags
|
||||
queryData << uint32(type); // CreatureType.dbc
|
||||
queryData << uint32(family); // CreatureFamily.dbc
|
||||
queryData << uint32(rank); // Creature Rank (elite, boss, etc)
|
||||
queryData << uint32(KillCredit[0]); // new in 3.1, kill credit
|
||||
queryData << uint32(KillCredit[1]); // new in 3.1, kill credit
|
||||
if (GetModelByIdx(0))
|
||||
queryData << uint32(GetModelByIdx(0)->CreatureDisplayID); // Modelid1
|
||||
else
|
||||
queryData << uint32(0); // Modelid1
|
||||
if (GetModelByIdx(1))
|
||||
queryData << uint32(GetModelByIdx(1)->CreatureDisplayID); // Modelid2
|
||||
else
|
||||
queryData << uint32(0); // Modelid2
|
||||
if (GetModelByIdx(2))
|
||||
queryData << uint32(GetModelByIdx(2)->CreatureDisplayID); // Modelid3
|
||||
else
|
||||
queryData << uint32(0); // Modelid3
|
||||
if (GetModelByIdx(3))
|
||||
queryData << uint32(GetModelByIdx(3)->CreatureDisplayID); // Modelid4
|
||||
else
|
||||
queryData << uint32(0); // Modelid4
|
||||
queryData << float(ModHealth); // dmg/hp modifier
|
||||
queryData << float(ModMana); // dmg/mana modifier
|
||||
queryData << uint8(RacialLeader);
|
||||
queryData << uint32(movementId); // CreatureMovementInfo.dbc
|
||||
queryData << uint32(movementId); // CreatureMovementInfo.dbc
|
||||
}
|
||||
|
||||
bool AssistDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
|
||||
@@ -423,21 +476,22 @@ bool Creature::InitEntry(uint32 Entry, const CreatureData* data)
|
||||
SetByteValue(UNIT_FIELD_BYTES_0, 1, uint8(cinfo->unit_class));
|
||||
|
||||
// Cancel load if no model defined
|
||||
if (!(cinfo->GetFirstValidModelId()))
|
||||
if (!(cinfo->GetFirstValidModel()))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Creature (Entry: {}) has no model defined in table `creature_template`, can't load. ", Entry);
|
||||
LOG_ERROR("sql.sql", "Creature (Entry: {}) has no model defined in table `creature_template_model`, can't load. ", Entry);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 displayID = ObjectMgr::ChooseDisplayId(GetCreatureTemplate(), data);
|
||||
if (!sObjectMgr->GetCreatureModelRandomGender(&displayID)) // Cancel load if no model defined
|
||||
CreatureModel model = *ObjectMgr::ChooseDisplayId(cinfo, data);
|
||||
CreatureModelInfo const* mInfo = sObjectMgr->GetCreatureModelRandomGender(&model, cinfo);
|
||||
if (!mInfo) // Cancel load if no model defined
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Creature (Entry: {}) has no model defined in table `creature_template`, can't load. ", Entry);
|
||||
LOG_ERROR("sql.sql", "Creature (Entry: {}) has no model {} defined in table `creature_template_model`, can't load. ", Entry, model.CreatureDisplayID);
|
||||
return false;
|
||||
}
|
||||
|
||||
SetDisplayId(displayID);
|
||||
SetNativeDisplayId(displayID);
|
||||
SetDisplayId(model.CreatureDisplayID, model.DisplayScale);
|
||||
SetNativeDisplayId(model.CreatureDisplayID);
|
||||
|
||||
// Load creature equipment
|
||||
if (!data)
|
||||
@@ -1121,11 +1175,12 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, u
|
||||
break;
|
||||
}
|
||||
|
||||
uint32 displayID = GetNativeDisplayId();
|
||||
if (sObjectMgr->GetCreatureModelRandomGender(&displayID) && !IsTotem()) // Cancel load if no model defined or if totem
|
||||
CreatureModel display(GetNativeDisplayId(), GetNativeObjectScale(), 1.0f);
|
||||
CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelRandomGender(&display, cinfo);
|
||||
if (minfo && !IsTotem()) // Cancel load if no model defined or if totem
|
||||
{
|
||||
SetDisplayId(displayID);
|
||||
SetNativeDisplayId(displayID);
|
||||
SetDisplayId(display.CreatureDisplayID, display.DisplayScale);
|
||||
SetNativeDisplayId(display.CreatureDisplayID);
|
||||
}
|
||||
|
||||
LoadCreaturesAddon();
|
||||
@@ -1360,9 +1415,9 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
|
||||
CreatureTemplate const* cinfo = GetCreatureTemplate();
|
||||
if (cinfo)
|
||||
{
|
||||
if (displayId == cinfo->Modelid1 || displayId == cinfo->Modelid2 ||
|
||||
displayId == cinfo->Modelid3 || displayId == cinfo->Modelid4)
|
||||
displayId = 0;
|
||||
for (CreatureModel model : cinfo->Models)
|
||||
if (displayId && displayId == model.CreatureDisplayID)
|
||||
displayId = 0;
|
||||
|
||||
if (npcflag == cinfo->npcflag)
|
||||
npcflag = 0;
|
||||
@@ -2031,11 +2086,12 @@ void Creature::Respawn(bool force)
|
||||
// Do not override transform auras
|
||||
if (GetAuraEffectsByType(SPELL_AURA_TRANSFORM).empty())
|
||||
{
|
||||
uint32 displayID = GetNativeDisplayId();
|
||||
if (sObjectMgr->GetCreatureModelRandomGender(&displayID)) // Cancel load if no model defined
|
||||
CreatureModel display(GetNativeDisplayId(), GetNativeObjectScale(), 1.0f);
|
||||
CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelRandomGender(&display, GetCreatureTemplate());
|
||||
if (minfo) // Cancel load if no model defined
|
||||
{
|
||||
SetDisplayId(displayID);
|
||||
SetNativeDisplayId(displayID);
|
||||
SetDisplayId(display.CreatureDisplayID, display.DisplayScale);
|
||||
SetNativeDisplayId(display.CreatureDisplayID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3414,9 +3470,9 @@ void Creature::SetObjectScale(float scale)
|
||||
SetFloatValue(UNIT_FIELD_COMBATREACH, combatReach * scale);
|
||||
}
|
||||
|
||||
void Creature::SetDisplayId(uint32 modelId)
|
||||
void Creature::SetDisplayId(uint32 modelId, float displayScale /*= 1.f*/)
|
||||
{
|
||||
Unit::SetDisplayId(modelId);
|
||||
Unit::SetDisplayId(modelId, displayScale);
|
||||
|
||||
float combatReach = DEFAULT_WORLD_OBJECT_SIZE;
|
||||
|
||||
@@ -3430,9 +3486,17 @@ void Creature::SetDisplayId(uint32 modelId)
|
||||
if (IsPet())
|
||||
combatReach = DEFAULT_COMBAT_REACH;
|
||||
|
||||
SetObjectScale(displayScale);
|
||||
|
||||
SetFloatValue(UNIT_FIELD_COMBATREACH, combatReach * GetObjectScale());
|
||||
}
|
||||
|
||||
void Creature::SetDisplayFromModel(uint32 modelIdx)
|
||||
{
|
||||
if (CreatureModel const* model = GetCreatureTemplate()->GetModelByIdx(modelIdx))
|
||||
SetDisplayId(model->CreatureDisplayID, model->DisplayScale);
|
||||
}
|
||||
|
||||
void Creature::SetTarget(ObjectGuid guid)
|
||||
{
|
||||
if (!_focusSpell)
|
||||
|
||||
@@ -53,7 +53,8 @@ public:
|
||||
|
||||
float GetNativeObjectScale() const override;
|
||||
void SetObjectScale(float scale) override;
|
||||
void SetDisplayId(uint32 modelId) override;
|
||||
void SetDisplayId(uint32 displayId, float displayScale = 1.f) override;
|
||||
void SetDisplayFromModel(uint32 modelIdx);
|
||||
|
||||
void DisappearAndDie();
|
||||
|
||||
|
||||
@@ -171,16 +171,29 @@ struct CreatureMovementData
|
||||
std::string ToString() const;
|
||||
};
|
||||
|
||||
struct CreatureModel
|
||||
{
|
||||
static CreatureModel const DefaultInvisibleModel;
|
||||
static CreatureModel const DefaultVisibleModel;
|
||||
|
||||
CreatureModel() :
|
||||
CreatureDisplayID(0), DisplayScale(0.0f), Probability(0.0f) { }
|
||||
|
||||
CreatureModel(uint32 creatureDisplayID, float displayScale, float probability) :
|
||||
CreatureDisplayID(creatureDisplayID), DisplayScale(displayScale), Probability(probability) { }
|
||||
|
||||
uint32 CreatureDisplayID;
|
||||
float DisplayScale;
|
||||
float Probability;
|
||||
};
|
||||
|
||||
// from `creature_template` table
|
||||
struct CreatureTemplate
|
||||
{
|
||||
uint32 Entry;
|
||||
uint32 DifficultyEntry[MAX_DIFFICULTY - 1];
|
||||
uint32 KillCredit[MAX_KILL_CREDIT];
|
||||
uint32 Modelid1;
|
||||
uint32 Modelid2;
|
||||
uint32 Modelid3;
|
||||
uint32 Modelid4;
|
||||
std::vector<CreatureModel> Models;
|
||||
std::string Name;
|
||||
std::string SubName;
|
||||
std::string IconName;
|
||||
@@ -239,8 +252,12 @@ struct CreatureTemplate
|
||||
uint32 flags_extra;
|
||||
uint32 ScriptID;
|
||||
WorldPacket queryData; // pussywizard
|
||||
[[nodiscard]] uint32 GetRandomValidModelId() const;
|
||||
[[nodiscard]] uint32 GetFirstValidModelId() const;
|
||||
CreatureModel const* GetModelByIdx(uint32 idx) const;
|
||||
CreatureModel const* GetRandomValidModel() const;
|
||||
CreatureModel const* GetFirstValidModel() const;
|
||||
CreatureModel const* GetModelWithDisplayId(uint32 displayId) const;
|
||||
CreatureModel const* GetFirstInvisibleModel() const;
|
||||
CreatureModel const* GetFirstVisibleModel() const;
|
||||
|
||||
// helpers
|
||||
[[nodiscard]] SkillType GetRequiredLootSkill() const
|
||||
@@ -389,6 +406,7 @@ struct CreatureModelInfo
|
||||
float combat_reach;
|
||||
uint8 gender;
|
||||
uint32 modelid_other_gender;
|
||||
float is_trigger;
|
||||
};
|
||||
|
||||
// Benchmarked: Faster than std::map (insert/find)
|
||||
|
||||
@@ -2418,9 +2418,9 @@ void Pet::SynchronizeLevelWithOwner()
|
||||
}
|
||||
}
|
||||
|
||||
void Pet::SetDisplayId(uint32 modelId)
|
||||
void Pet::SetDisplayId(uint32 modelId, float displayScale /*= 1.f*/)
|
||||
{
|
||||
Guardian::SetDisplayId(modelId);
|
||||
Guardian::SetDisplayId(modelId, displayScale);
|
||||
|
||||
if (!isControlled())
|
||||
return;
|
||||
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
void RemoveFromWorld() override;
|
||||
|
||||
float GetNativeObjectScale() const override;
|
||||
void SetDisplayId(uint32 modelId) override;
|
||||
void SetDisplayId(uint32 modelId, float displayScale = 1.f) override;
|
||||
|
||||
PetType getPetType() const { return m_petType; }
|
||||
void setPetType(PetType type) { m_petType = type; }
|
||||
|
||||
@@ -17034,13 +17034,16 @@ void Unit::RecalculateObjectScale()
|
||||
SetObjectScale(std::max(scale, scaleMin));
|
||||
}
|
||||
|
||||
void Unit::SetDisplayId(uint32 modelId)
|
||||
void Unit::SetDisplayId(uint32 modelId, float displayScale /*=1.f*/)
|
||||
{
|
||||
SetUInt32Value(UNIT_FIELD_DISPLAYID, modelId);
|
||||
|
||||
// Set Gender by modelId
|
||||
if (CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelInfo(modelId))
|
||||
SetByteValue(UNIT_FIELD_BYTES_0, 2, minfo->gender);
|
||||
|
||||
SetObjectScale(displayScale);
|
||||
|
||||
sScriptMgr->OnDisplayIdChange(this, modelId);
|
||||
}
|
||||
|
||||
@@ -21233,17 +21236,11 @@ void Unit::PatchValuesUpdate(ByteBuffer& valuesUpdateBuf, BuildValuesCachePosPoi
|
||||
{
|
||||
if (target->IsGameMaster() && target->GetSession()->IsGMAccount())
|
||||
{
|
||||
if (cinfo->Modelid1)
|
||||
displayId = cinfo->Modelid1; // Modelid1 is a visible model for gms
|
||||
else
|
||||
displayId = 17519; // world visible trigger's model
|
||||
displayId = cinfo->GetFirstVisibleModel()->CreatureDisplayID;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cinfo->Modelid2)
|
||||
displayId = cinfo->Modelid2; // Modelid2 is an invisible model for players
|
||||
else
|
||||
displayId = 11686; // world invisible trigger's model
|
||||
displayId = cinfo->GetFirstInvisibleModel()->CreatureDisplayID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2261,10 +2261,10 @@ public:
|
||||
virtual float GetNativeObjectScale() const { return 1.0f; }
|
||||
virtual void RecalculateObjectScale();
|
||||
[[nodiscard]] uint32 GetDisplayId() const { return GetUInt32Value(UNIT_FIELD_DISPLAYID); }
|
||||
virtual void SetDisplayId(uint32 modelId);
|
||||
virtual void SetDisplayId(uint32 modelId, float displayScale = 1.f);
|
||||
[[nodiscard]] uint32 GetNativeDisplayId() const { return GetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID); }
|
||||
void RestoreDisplayId();
|
||||
void SetNativeDisplayId(uint32 modelId) { SetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID, modelId); }
|
||||
void SetNativeDisplayId(uint32 displayId) { SetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID, displayId); }
|
||||
void setTransForm(uint32 spellid) { m_transform = spellid;}
|
||||
[[nodiscard]] uint32 getTransForm() const { return m_transform;}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "LFGMgr.h"
|
||||
#include "Log.h"
|
||||
#include "MapMgr.h"
|
||||
#include <numeric>
|
||||
#include "Pet.h"
|
||||
#include "PoolMgr.h"
|
||||
#include "ReputationMgr.h"
|
||||
@@ -576,20 +577,20 @@ void ObjectMgr::LoadCreatureTemplates()
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
// 0 1 2 3 4 5 6 7 8
|
||||
QueryResult result = WorldDatabase.Query("SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, modelid1, modelid2, modelid3, "
|
||||
// 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
||||
"modelid4, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, faction, npcflag, speed_walk, speed_run, speed_swim, speed_flight, "
|
||||
// 23 24 25 26 27 28 29 30 31 32 33 34
|
||||
"detection_range, scale, `rank`, dmgschool, DamageModifier, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, "
|
||||
// 35 36 37 38 39 40 41
|
||||
"dynamicflags, family, trainer_type, trainer_spell, trainer_class, trainer_race, type, "
|
||||
// 42 43 44 45 46 47 48 49 50 51
|
||||
"type_flags, lootid, pickpocketloot, skinloot, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, "
|
||||
// 52 53 54 55 56 57 58 59 60 61 62 63
|
||||
"ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, ExperienceModifier, "
|
||||
// 64 65 66 67 68 69 70
|
||||
"RacialLeader, movementId, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, ScriptName "
|
||||
// 0 1 2 3 4 5 6 7 8
|
||||
QueryResult result = WorldDatabase.Query("SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, name, subname, IconName, "
|
||||
// 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
||||
"gossip_menu_id, minlevel, maxlevel, exp, faction, npcflag, speed_walk, speed_run, speed_swim, speed_flight, detection_range, scale, `rank`, dmgschool, "
|
||||
// 23 24 25 26 27 28 29 30 31 32 33 34
|
||||
"DamageModifier, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, dynamicflags, family, trainer_type, trainer_spell, "
|
||||
// 35 36 37 38 39 40 41
|
||||
"trainer_class, trainer_race, type, type_flags, lootid, pickpocketloot, skinloot, "
|
||||
// 42 43 44 45 46 47 48 49 50 51
|
||||
"PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, "
|
||||
// 52 53 54 55 56 57 58 59 60 61 62 63
|
||||
"ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, mechanic_immune_mask, "
|
||||
// 64 65 66
|
||||
"spell_school_immune_mask, flags_extra, ScriptName "
|
||||
"FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId ORDER BY entry DESC;");
|
||||
|
||||
if (!result)
|
||||
@@ -609,6 +610,9 @@ void ObjectMgr::LoadCreatureTemplates()
|
||||
++count;
|
||||
} while (result->NextRow());
|
||||
|
||||
// We load the creature models after loading but before checking
|
||||
LoadCreatureTemplateModels();
|
||||
|
||||
sScriptMgr->OnAfterDatabaseLoadCreatureTemplates(_creatureTemplateStoreFast);
|
||||
|
||||
LoadCreatureTemplateResistances();
|
||||
@@ -658,47 +662,42 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields, bool triggerHook)
|
||||
{
|
||||
creatureTemplate.KillCredit[i] = fields[4 + i].Get<uint32>();
|
||||
}
|
||||
|
||||
creatureTemplate.Modelid1 = fields[6].Get<uint32>();
|
||||
creatureTemplate.Modelid2 = fields[7].Get<uint32>();
|
||||
creatureTemplate.Modelid3 = fields[8].Get<uint32>();
|
||||
creatureTemplate.Modelid4 = fields[9].Get<uint32>();
|
||||
creatureTemplate.Name = fields[10].Get<std::string>();
|
||||
creatureTemplate.SubName = fields[11].Get<std::string>();
|
||||
creatureTemplate.IconName = fields[12].Get<std::string>();
|
||||
creatureTemplate.GossipMenuId = fields[13].Get<uint32>();
|
||||
creatureTemplate.minlevel = fields[14].Get<uint8>();
|
||||
creatureTemplate.maxlevel = fields[15].Get<uint8>();
|
||||
creatureTemplate.expansion = uint32(fields[16].Get<int16>());
|
||||
creatureTemplate.faction = uint32(fields[17].Get<uint16>());
|
||||
creatureTemplate.npcflag = fields[18].Get<uint32>();
|
||||
creatureTemplate.speed_walk = fields[19].Get<float>();
|
||||
creatureTemplate.speed_run = fields[20].Get<float>();
|
||||
creatureTemplate.speed_swim = fields[21].Get<float>();
|
||||
creatureTemplate.speed_flight = fields[22].Get<float>();
|
||||
creatureTemplate.detection_range = fields[23].Get<float>();
|
||||
creatureTemplate.scale = fields[24].Get<float>();
|
||||
creatureTemplate.rank = uint32(fields[25].Get<uint8>());
|
||||
creatureTemplate.dmgschool = uint32(fields[26].Get<int8>());
|
||||
creatureTemplate.DamageModifier = fields[27].Get<float>();
|
||||
creatureTemplate.BaseAttackTime = fields[28].Get<uint32>();
|
||||
creatureTemplate.RangeAttackTime = fields[29].Get<uint32>();
|
||||
creatureTemplate.BaseVariance = fields[30].Get<float>();
|
||||
creatureTemplate.RangeVariance = fields[31].Get<float>();
|
||||
creatureTemplate.unit_class = uint32(fields[32].Get<uint8>());
|
||||
creatureTemplate.unit_flags = fields[33].Get<uint32>();
|
||||
creatureTemplate.unit_flags2 = fields[34].Get<uint32>();
|
||||
creatureTemplate.dynamicflags = fields[35].Get<uint32>();
|
||||
creatureTemplate.family = uint32(fields[36].Get<uint8>());
|
||||
creatureTemplate.trainer_type = uint32(fields[37].Get<uint8>());
|
||||
creatureTemplate.trainer_spell = fields[38].Get<uint32>();
|
||||
creatureTemplate.trainer_class = uint32(fields[39].Get<uint8>());
|
||||
creatureTemplate.trainer_race = uint32(fields[40].Get<uint8>());
|
||||
creatureTemplate.type = uint32(fields[41].Get<uint8>());
|
||||
creatureTemplate.type_flags = fields[42].Get<uint32>();
|
||||
creatureTemplate.lootid = fields[43].Get<uint32>();
|
||||
creatureTemplate.pickpocketLootId = fields[44].Get<uint32>();
|
||||
creatureTemplate.SkinLootId = fields[45].Get<uint32>();
|
||||
creatureTemplate.Name = fields[6].Get<std::string>();
|
||||
creatureTemplate.SubName = fields[7].Get<std::string>();
|
||||
creatureTemplate.IconName = fields[8].Get<std::string>();
|
||||
creatureTemplate.GossipMenuId = fields[9].Get<uint32>();
|
||||
creatureTemplate.minlevel = fields[10].Get<uint8>();
|
||||
creatureTemplate.maxlevel = fields[11].Get<uint8>();
|
||||
creatureTemplate.expansion = uint32(fields[12].Get<int16>());
|
||||
creatureTemplate.faction = uint32(fields[13].Get<uint16>());
|
||||
creatureTemplate.npcflag = fields[14].Get<uint32>();
|
||||
creatureTemplate.speed_walk = fields[15].Get<float>();
|
||||
creatureTemplate.speed_run = fields[16].Get<float>();
|
||||
creatureTemplate.speed_swim = fields[17].Get<float>();
|
||||
creatureTemplate.speed_flight = fields[18].Get<float>();
|
||||
creatureTemplate.detection_range = fields[19].Get<float>();
|
||||
creatureTemplate.scale = fields[20].Get<float>();
|
||||
creatureTemplate.rank = uint32(fields[21].Get<uint8>());
|
||||
creatureTemplate.dmgschool = uint32(fields[22].Get<int8>());
|
||||
creatureTemplate.DamageModifier = fields[23].Get<float>();
|
||||
creatureTemplate.BaseAttackTime = fields[24].Get<uint32>();
|
||||
creatureTemplate.RangeAttackTime = fields[25].Get<uint32>();
|
||||
creatureTemplate.BaseVariance = fields[26].Get<float>();
|
||||
creatureTemplate.RangeVariance = fields[27].Get<float>();
|
||||
creatureTemplate.unit_class = uint32(fields[28].Get<uint8>());
|
||||
creatureTemplate.unit_flags = fields[29].Get<uint32>();
|
||||
creatureTemplate.unit_flags2 = fields[30].Get<uint32>();
|
||||
creatureTemplate.dynamicflags = fields[31].Get<uint32>();
|
||||
creatureTemplate.family = uint32(fields[32].Get<uint8>());
|
||||
creatureTemplate.trainer_type = uint32(fields[33].Get<uint8>());
|
||||
creatureTemplate.trainer_spell = fields[34].Get<uint32>();
|
||||
creatureTemplate.trainer_class = uint32(fields[35].Get<uint8>());
|
||||
creatureTemplate.trainer_race = uint32(fields[36].Get<uint8>());
|
||||
creatureTemplate.type = uint32(fields[37].Get<uint8>());
|
||||
creatureTemplate.type_flags = fields[38].Get<uint32>();
|
||||
creatureTemplate.lootid = fields[39].Get<uint32>();
|
||||
creatureTemplate.pickpocketLootId = fields[40].Get<uint32>();
|
||||
creatureTemplate.SkinLootId = fields[41].Get<uint32>();
|
||||
|
||||
for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
|
||||
{
|
||||
@@ -710,49 +709,49 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields, bool triggerHook)
|
||||
creatureTemplate.spells[i] = 0;
|
||||
}
|
||||
|
||||
creatureTemplate.PetSpellDataId = fields[46].Get<uint32>();
|
||||
creatureTemplate.VehicleId = fields[47].Get<uint32>();
|
||||
creatureTemplate.mingold = fields[48].Get<uint32>();
|
||||
creatureTemplate.maxgold = fields[49].Get<uint32>();
|
||||
creatureTemplate.AIName = fields[50].Get<std::string>();
|
||||
creatureTemplate.MovementType = uint32(fields[51].Get<uint8>());
|
||||
creatureTemplate.PetSpellDataId = fields[42].Get<uint32>();
|
||||
creatureTemplate.VehicleId = fields[43].Get<uint32>();
|
||||
creatureTemplate.mingold = fields[44].Get<uint32>();
|
||||
creatureTemplate.maxgold = fields[45].Get<uint32>();
|
||||
creatureTemplate.AIName = fields[46].Get<std::string>(); // stopped here, fix it
|
||||
creatureTemplate.MovementType = uint32(fields[47].Get<uint8>());
|
||||
if (!fields[48].IsNull())
|
||||
{
|
||||
creatureTemplate.Movement.Ground = static_cast<CreatureGroundMovementType>(fields[48].Get<uint8>());
|
||||
}
|
||||
|
||||
creatureTemplate.Movement.Swim = fields[49].Get<bool>();
|
||||
if (!fields[50].IsNull())
|
||||
{
|
||||
creatureTemplate.Movement.Flight = static_cast<CreatureFlightMovementType>(fields[50].Get<uint8>());
|
||||
}
|
||||
|
||||
creatureTemplate.Movement.Rooted = fields[51].Get<bool>();
|
||||
if (!fields[52].IsNull())
|
||||
{
|
||||
creatureTemplate.Movement.Ground = static_cast<CreatureGroundMovementType>(fields[52].Get<uint8>());
|
||||
creatureTemplate.Movement.Chase = static_cast<CreatureChaseMovementType>(fields[52].Get<uint8>());
|
||||
}
|
||||
if (!fields[53].IsNull())
|
||||
{
|
||||
creatureTemplate.Movement.Random = static_cast<CreatureRandomMovementType>(fields[53].Get<uint8>());
|
||||
}
|
||||
|
||||
creatureTemplate.Movement.Swim = fields[53].Get<bool>();
|
||||
if (!fields[54].IsNull())
|
||||
{
|
||||
creatureTemplate.Movement.Flight = static_cast<CreatureFlightMovementType>(fields[54].Get<uint8>());
|
||||
creatureTemplate.Movement.InteractionPauseTimer = fields[54].Get<uint32>();
|
||||
}
|
||||
|
||||
creatureTemplate.Movement.Rooted = fields[55].Get<bool>();
|
||||
if (!fields[56].IsNull())
|
||||
{
|
||||
creatureTemplate.Movement.Chase = static_cast<CreatureChaseMovementType>(fields[56].Get<uint8>());
|
||||
}
|
||||
if (!fields[57].IsNull())
|
||||
{
|
||||
creatureTemplate.Movement.Random = static_cast<CreatureRandomMovementType>(fields[57].Get<uint8>());
|
||||
}
|
||||
if (!fields[58].IsNull())
|
||||
{
|
||||
creatureTemplate.Movement.InteractionPauseTimer = fields[58].Get<uint32>();
|
||||
}
|
||||
|
||||
creatureTemplate.HoverHeight = fields[59].Get<float>();
|
||||
creatureTemplate.ModHealth = fields[60].Get<float>();
|
||||
creatureTemplate.ModMana = fields[61].Get<float>();
|
||||
creatureTemplate.ModArmor = fields[62].Get<float>();
|
||||
creatureTemplate.ModExperience = fields[63].Get<float>();
|
||||
creatureTemplate.RacialLeader = fields[64].Get<bool>();
|
||||
creatureTemplate.movementId = fields[65].Get<uint32>();
|
||||
creatureTemplate.RegenHealth = fields[66].Get<bool>();
|
||||
creatureTemplate.MechanicImmuneMask = fields[67].Get<uint32>();
|
||||
creatureTemplate.SpellSchoolImmuneMask = fields[68].Get<uint8>();
|
||||
creatureTemplate.flags_extra = fields[69].Get<uint32>();
|
||||
creatureTemplate.ScriptID = GetScriptId(fields[70].Get<std::string>());
|
||||
creatureTemplate.HoverHeight = fields[55].Get<float>();
|
||||
creatureTemplate.ModHealth = fields[56].Get<float>();
|
||||
creatureTemplate.ModMana = fields[57].Get<float>();
|
||||
creatureTemplate.ModArmor = fields[58].Get<float>();
|
||||
creatureTemplate.ModExperience = fields[59].Get<float>();
|
||||
creatureTemplate.RacialLeader = fields[60].Get<bool>();
|
||||
creatureTemplate.movementId = fields[61].Get<uint32>();
|
||||
creatureTemplate.RegenHealth = fields[62].Get<bool>();
|
||||
creatureTemplate.MechanicImmuneMask = fields[63].Get<uint32>();
|
||||
creatureTemplate.SpellSchoolImmuneMask = fields[64].Get<uint8>();
|
||||
creatureTemplate.flags_extra = fields[65].Get<uint32>();
|
||||
creatureTemplate.ScriptID = GetScriptId(fields[66].Get<std::string>());
|
||||
|
||||
// useful if the creature template load is being triggered from outside this class
|
||||
if (triggerHook)
|
||||
@@ -762,6 +761,58 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields, bool triggerHook)
|
||||
|
||||
}
|
||||
|
||||
void ObjectMgr::LoadCreatureTemplateModels()
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
// 0 1 2 3
|
||||
QueryResult result = WorldDatabase.Query("SELECT CreatureID, CreatureDisplayID, DisplayScale, Probability FROM creature_template_model ORDER BY Idx ASC");
|
||||
|
||||
if (!result)
|
||||
{
|
||||
LOG_INFO("server.loading", ">> Loaded 0 creature template model definitions. DB table `creature_template_model` is empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 count = 0;
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
|
||||
uint32 creatureId = fields[0].Get<uint32>();
|
||||
uint32 creatureDisplayId = fields[1].Get<uint32>();
|
||||
float displayScale = fields[2].Get<float>();
|
||||
float probability = fields[3].Get<float>();
|
||||
|
||||
CreatureTemplate const* cInfo = GetCreatureTemplate(creatureId);
|
||||
if (!cInfo)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Creature template (Entry: {}) does not exist but has a record in `creature_template_model`", creatureId);
|
||||
continue;
|
||||
}
|
||||
|
||||
CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(creatureDisplayId);
|
||||
if (!displayEntry)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Creature (Entry: {}) lists non-existing CreatureDisplayID id ({}), this can crash the client.", creatureId, creatureDisplayId);
|
||||
continue;
|
||||
}
|
||||
|
||||
CreatureModelInfo const* modelInfo = GetCreatureModelInfo(creatureDisplayId);
|
||||
if (!modelInfo)
|
||||
LOG_ERROR("sql.sql", "No model data exist for `CreatureDisplayID` = {} listed by creature (Entry: {%u}}).", creatureDisplayId, creatureId);
|
||||
|
||||
if (displayScale <= 0.0f)
|
||||
displayScale = 1.0f;
|
||||
|
||||
const_cast<CreatureTemplate*>(cInfo)->Models.emplace_back(creatureDisplayId, displayScale, probability);
|
||||
|
||||
++count;
|
||||
} while (result->NextRow());
|
||||
|
||||
LOG_INFO("server.loading", ">> Loaded {} creature template models in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
|
||||
}
|
||||
|
||||
void ObjectMgr::LoadCreatureTemplateResistances()
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
@@ -1112,76 +1163,6 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo)
|
||||
if (!factionTemplate)
|
||||
LOG_ERROR("sql.sql", "Creature (Entry: {}) has non-existing faction template ({}).", cInfo->Entry, cInfo->faction);
|
||||
|
||||
// used later for scale
|
||||
CreatureDisplayInfoEntry const* displayScaleEntry = nullptr;
|
||||
|
||||
if (cInfo->Modelid1)
|
||||
{
|
||||
CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid1);
|
||||
if (!displayEntry)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Creature (Entry: {}) lists non-existing Modelid1 id ({}), this can crash the client.", cInfo->Entry, cInfo->Modelid1);
|
||||
const_cast<CreatureTemplate*>(cInfo)->Modelid1 = 0;
|
||||
}
|
||||
else if (!displayScaleEntry)
|
||||
displayScaleEntry = displayEntry;
|
||||
|
||||
CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid1);
|
||||
if (!modelInfo)
|
||||
LOG_ERROR("sql.sql", "No model data exist for `Modelid1` = {} listed by creature (Entry: {}).", cInfo->Modelid1, cInfo->Entry);
|
||||
}
|
||||
|
||||
if (cInfo->Modelid2)
|
||||
{
|
||||
CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid2);
|
||||
if (!displayEntry)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Creature (Entry: {}) lists non-existing Modelid2 id ({}), this can crash the client.", cInfo->Entry, cInfo->Modelid2);
|
||||
const_cast<CreatureTemplate*>(cInfo)->Modelid2 = 0;
|
||||
}
|
||||
else if (!displayScaleEntry)
|
||||
displayScaleEntry = displayEntry;
|
||||
|
||||
CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid2);
|
||||
if (!modelInfo)
|
||||
LOG_ERROR("sql.sql", "No model data exist for `Modelid2` = {} listed by creature (Entry: {}).", cInfo->Modelid2, cInfo->Entry);
|
||||
}
|
||||
|
||||
if (cInfo->Modelid3)
|
||||
{
|
||||
CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid3);
|
||||
if (!displayEntry)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Creature (Entry: {}) lists non-existing Modelid3 id ({}), this can crash the client.", cInfo->Entry, cInfo->Modelid3);
|
||||
const_cast<CreatureTemplate*>(cInfo)->Modelid3 = 0;
|
||||
}
|
||||
else if (!displayScaleEntry)
|
||||
displayScaleEntry = displayEntry;
|
||||
|
||||
CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid3);
|
||||
if (!modelInfo)
|
||||
LOG_ERROR("sql.sql", "No model data exist for `Modelid3` = {} listed by creature (Entry: {}).", cInfo->Modelid3, cInfo->Entry);
|
||||
}
|
||||
|
||||
if (cInfo->Modelid4)
|
||||
{
|
||||
CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid4);
|
||||
if (!displayEntry)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Creature (Entry: {}) lists non-existing Modelid4 id ({}), this can crash the client.", cInfo->Entry, cInfo->Modelid4);
|
||||
const_cast<CreatureTemplate*>(cInfo)->Modelid4 = 0;
|
||||
}
|
||||
else if (!displayScaleEntry)
|
||||
displayScaleEntry = displayEntry;
|
||||
|
||||
CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid4);
|
||||
if (!modelInfo)
|
||||
LOG_ERROR("sql.sql", "No model data exist for `Modelid4` = {} listed by creature (Entry: {}).", cInfo->Modelid4, cInfo->Entry);
|
||||
}
|
||||
|
||||
if (!displayScaleEntry)
|
||||
LOG_ERROR("sql.sql", "Creature (Entry: {}) does not have any existing display id in Modelid1/Modelid2/Modelid3/Modelid4.", cInfo->Entry);
|
||||
|
||||
for (int k = 0; k < MAX_KILL_CREDIT; ++k)
|
||||
{
|
||||
if (cInfo->KillCredit[k])
|
||||
@@ -1194,6 +1175,11 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo)
|
||||
}
|
||||
}
|
||||
|
||||
if (!cInfo->Models.size())
|
||||
LOG_ERROR("sql.sql", "Creature (Entry: {}) does not have any existing display id in creature_template_model.", cInfo->Entry);
|
||||
else if (std::accumulate(cInfo->Models.begin(), cInfo->Models.end(), 0.0f, [](float sum, CreatureModel const& model) { return sum + model.Probability; }) <= 0.0f)
|
||||
LOG_ERROR("sql.sql", "Creature (Entry: {}) has zero total chance for all models in creature_template_model.", cInfo->Entry);
|
||||
|
||||
if (!cInfo->unit_class || ((1 << (cInfo->unit_class - 1)) & CLASSMASK_ALL_CREATURES) == 0)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid unit_class ({}) in creature_template. Set to 1 (UNIT_CLASS_WARRIOR).", cInfo->Entry, cInfo->unit_class);
|
||||
@@ -1280,15 +1266,6 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo)
|
||||
const_cast<CreatureTemplate*>(cInfo)->MovementType = IDLE_MOTION_TYPE;
|
||||
}
|
||||
|
||||
/// if not set custom creature scale then load scale from CreatureDisplayInfo.dbc
|
||||
if (cInfo->scale <= 0.0f)
|
||||
{
|
||||
if (displayScaleEntry)
|
||||
const_cast<CreatureTemplate*>(cInfo)->scale = displayScaleEntry->scale;
|
||||
else
|
||||
const_cast<CreatureTemplate*>(cInfo)->scale = 1.0f;
|
||||
}
|
||||
|
||||
if (cInfo->expansion > (MAX_EXPANSIONS - 1))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: {}) with expansion {}. Ignored and set to 0.", cInfo->Entry, cInfo->expansion);
|
||||
@@ -1722,13 +1699,19 @@ CreatureModelInfo const* ObjectMgr::GetCreatureModelInfo(uint32 modelId) const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32 ObjectMgr::ChooseDisplayId(CreatureTemplate const* cinfo, CreatureData const* data /*= nullptr*/)
|
||||
CreatureModel const* ObjectMgr::ChooseDisplayId(CreatureTemplate const* cinfo, CreatureData const* data /*= nullptr*/)
|
||||
{
|
||||
// Load creature model (display id)
|
||||
if (data && data->displayid)
|
||||
return data->displayid;
|
||||
if (CreatureModel const* model = cinfo->GetModelWithDisplayId(data->displayid))
|
||||
return model;
|
||||
|
||||
return cinfo->GetRandomValidModelId();
|
||||
if (!(cinfo->flags_extra & CREATURE_FLAG_EXTRA_TRIGGER))
|
||||
if (CreatureModel const* model = cinfo->GetRandomValidModel())
|
||||
return model;
|
||||
|
||||
// Triggers by default receive the invisible model
|
||||
return cinfo->GetFirstInvisibleModel();
|
||||
}
|
||||
|
||||
void ObjectMgr::ChooseCreatureFlags(const CreatureTemplate* cinfo, uint32& npcflag, uint32& unit_flags, uint32& dynamicflags, const CreatureData* data /*= nullptr*/)
|
||||
@@ -1750,9 +1733,9 @@ void ObjectMgr::ChooseCreatureFlags(const CreatureTemplate* cinfo, uint32& npcfl
|
||||
}
|
||||
}
|
||||
|
||||
CreatureModelInfo const* ObjectMgr::GetCreatureModelRandomGender(uint32* displayID)
|
||||
CreatureModelInfo const* ObjectMgr::GetCreatureModelRandomGender(CreatureModel* model, CreatureTemplate const* creatureTemplate) const
|
||||
{
|
||||
CreatureModelInfo const* modelInfo = GetCreatureModelInfo(*displayID);
|
||||
CreatureModelInfo const* modelInfo = GetCreatureModelInfo(model->CreatureDisplayID);
|
||||
if (!modelInfo)
|
||||
return nullptr;
|
||||
|
||||
@@ -1761,11 +1744,20 @@ CreatureModelInfo const* ObjectMgr::GetCreatureModelRandomGender(uint32* display
|
||||
{
|
||||
CreatureModelInfo const* minfo_tmp = GetCreatureModelInfo(modelInfo->modelid_other_gender);
|
||||
if (!minfo_tmp)
|
||||
LOG_ERROR("sql.sql", "Model (Entry: {}) has modelid_other_gender {} not found in table `creature_model_info`. ", *displayID, modelInfo->modelid_other_gender);
|
||||
LOG_ERROR("sql.sql", "Model (Entry: {}) has modelid_other_gender {} not found in table `creature_model_info`. ", model->CreatureDisplayID, modelInfo->modelid_other_gender);
|
||||
else
|
||||
{
|
||||
// Model ID changed
|
||||
*displayID = modelInfo->modelid_other_gender;
|
||||
model->CreatureDisplayID = modelInfo->modelid_other_gender;
|
||||
if (creatureTemplate)
|
||||
{
|
||||
auto itr = std::find_if(creatureTemplate->Models.begin(), creatureTemplate->Models.end(), [&](CreatureModel const& templateModel)
|
||||
{
|
||||
return templateModel.CreatureDisplayID == modelInfo->modelid_other_gender;
|
||||
});
|
||||
if (itr != creatureTemplate->Models.end())
|
||||
*model = *itr;
|
||||
}
|
||||
return minfo_tmp;
|
||||
}
|
||||
}
|
||||
@@ -1790,10 +1782,15 @@ void ObjectMgr::LoadCreatureModelInfo()
|
||||
_creatureModelStore.rehash(result->GetRowCount());
|
||||
uint32 count = 0;
|
||||
|
||||
// List of ModelDataIDs that use Invisible models
|
||||
uint32 triggerCreatureModelDataID[14] = { 1731, 1752, 2206, 2296, 2372, 2382, 2481, 2512, 2513, 2611, 2636, 2790, 3230, 3274 };
|
||||
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
|
||||
uint32 displayId = fields[0].Get<uint32>();
|
||||
CreatureDisplayInfoEntry const* creatureDisplay = sCreatureDisplayInfoStore.LookupEntry(displayId);
|
||||
uint32 modelId = fields[0].Get<uint32>();
|
||||
|
||||
CreatureModelInfo& modelInfo = _creatureModelStore[modelId];
|
||||
@@ -1802,6 +1799,7 @@ void ObjectMgr::LoadCreatureModelInfo()
|
||||
modelInfo.combat_reach = fields[2].Get<float>();
|
||||
modelInfo.gender = fields[3].Get<uint8>();
|
||||
modelInfo.modelid_other_gender = fields[4].Get<uint32>();
|
||||
modelInfo.is_trigger = false;
|
||||
|
||||
// Checks
|
||||
|
||||
@@ -1823,6 +1821,18 @@ void ObjectMgr::LoadCreatureModelInfo()
|
||||
if (modelInfo.combat_reach < 0.1f)
|
||||
modelInfo.combat_reach = DEFAULT_COMBAT_REACH;
|
||||
|
||||
if (CreatureModelDataEntry const* modelData = sCreatureModelDataStore.LookupEntry(creatureDisplay->ModelId))
|
||||
{
|
||||
for (uint32 i = 0; i < 14; i++)
|
||||
{
|
||||
if (modelData->Id == triggerCreatureModelDataID[i])
|
||||
{
|
||||
modelInfo.is_trigger = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
++count;
|
||||
} while (result->NextRow());
|
||||
|
||||
@@ -6586,7 +6596,8 @@ void ObjectMgr::GetTaxiPath(uint32 source, uint32 destination, uint32& path, uin
|
||||
|
||||
uint32 ObjectMgr::GetTaxiMountDisplayId(uint32 id, TeamId teamId, bool allowed_alt_team /* = false */)
|
||||
{
|
||||
uint32 mount_id = 0;
|
||||
CreatureModel mountModel;
|
||||
CreatureTemplate const* mount_info = nullptr;
|
||||
|
||||
// select mount creature id
|
||||
TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(id);
|
||||
@@ -6602,22 +6613,23 @@ uint32 ObjectMgr::GetTaxiMountDisplayId(uint32 id, TeamId teamId, bool allowed_a
|
||||
mount_entry = node->MountCreatureID[teamId];
|
||||
}
|
||||
|
||||
CreatureTemplate const* mount_info = GetCreatureTemplate(mount_entry);
|
||||
mount_info = GetCreatureTemplate(mount_entry);
|
||||
if (mount_info)
|
||||
{
|
||||
mount_id = mount_info->GetRandomValidModelId();
|
||||
if (!mount_id)
|
||||
CreatureModel const* model = mount_info->GetRandomValidModel();
|
||||
if (!model)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "No displayid found for the taxi mount with the entry {}! Can't load it!", mount_entry);
|
||||
return 0;
|
||||
}
|
||||
mountModel = *model;
|
||||
}
|
||||
}
|
||||
|
||||
// minfo is not actually used but the mount_id was updated
|
||||
GetCreatureModelRandomGender(&mount_id);
|
||||
GetCreatureModelRandomGender(&mountModel, mount_info);
|
||||
|
||||
return mount_id;
|
||||
return mountModel.CreatureDisplayID;
|
||||
}
|
||||
|
||||
void ObjectMgr::LoadAreaTriggers()
|
||||
|
||||
@@ -770,8 +770,8 @@ public:
|
||||
CreatureTemplate const* GetCreatureTemplate(uint32 entry);
|
||||
[[nodiscard]] CreatureTemplateContainer const* GetCreatureTemplates() const { return &_creatureTemplateStore; }
|
||||
CreatureModelInfo const* GetCreatureModelInfo(uint32 modelId) const;
|
||||
CreatureModelInfo const* GetCreatureModelRandomGender(uint32* displayID);
|
||||
static uint32 ChooseDisplayId(CreatureTemplate const* cinfo, CreatureData const* data = nullptr);
|
||||
CreatureModelInfo const* GetCreatureModelRandomGender(CreatureModel* model, CreatureTemplate const* creatureTemplate) const;
|
||||
static CreatureModel const* ChooseDisplayId(CreatureTemplate const* cinfo, CreatureData const* data = nullptr);
|
||||
static void ChooseCreatureFlags(CreatureTemplate const* cinfo, uint32& npcflag, uint32& unit_flags, uint32& dynamicflags, CreatureData const* data = nullptr);
|
||||
EquipmentInfo const* GetEquipmentInfo(uint32 entry, int8& id);
|
||||
CreatureAddon const* GetCreatureAddon(ObjectGuid::LowType lowguid);
|
||||
@@ -1021,6 +1021,7 @@ public:
|
||||
void LoadCreatureLocales();
|
||||
void LoadCreatureTemplates();
|
||||
void LoadCreatureTemplate(Field* fields, bool triggerHook = false);
|
||||
void LoadCreatureTemplateModels();
|
||||
void LoadCreatureTemplateAddons();
|
||||
void LoadCreatureTemplateResistances();
|
||||
void LoadCreatureTemplateSpells();
|
||||
|
||||
@@ -116,23 +116,35 @@ void WorldSession::HandleCreatureQueryOpcode(WorldPacket& recvData)
|
||||
}
|
||||
// guess size
|
||||
WorldPacket data(SMSG_CREATURE_QUERY_RESPONSE, 100);
|
||||
data << uint32(entry); // creature entry
|
||||
data << uint32(entry); // creature entry
|
||||
data << Name;
|
||||
data << uint8(0) << uint8(0) << uint8(0); // name2, name3, name4, always empty
|
||||
data << uint8(0) << uint8(0) << uint8(0); // name2, name3, name4, always empty
|
||||
data << Title;
|
||||
data << ci->IconName; // "Directions" for guard, string for Icons 2.3.0
|
||||
data << uint32(ci->type_flags); // flags
|
||||
data << uint32(ci->type); // CreatureType.dbc
|
||||
data << uint32(ci->family); // CreatureFamily.dbc
|
||||
data << uint32(ci->rank); // Creature Rank (elite, boss, etc)
|
||||
data << uint32(ci->KillCredit[0]); // new in 3.1, kill credit
|
||||
data << uint32(ci->KillCredit[1]); // new in 3.1, kill credit
|
||||
data << uint32(ci->Modelid1); // Modelid1
|
||||
data << uint32(ci->Modelid2); // Modelid2
|
||||
data << uint32(ci->Modelid3); // Modelid3
|
||||
data << uint32(ci->Modelid4); // Modelid4
|
||||
data << float(ci->ModHealth); // dmg/hp modifier
|
||||
data << float(ci->ModMana); // dmg/mana modifier
|
||||
data << ci->IconName; // "Directions" for guard, string for Icons 2.3.0
|
||||
data << uint32(ci->type_flags); // flags
|
||||
data << uint32(ci->type); // CreatureType.dbc
|
||||
data << uint32(ci->family); // CreatureFamily.dbc
|
||||
data << uint32(ci->rank); // Creature Rank (elite, boss, etc)
|
||||
data << uint32(ci->KillCredit[0]); // new in 3.1, kill credit
|
||||
data << uint32(ci->KillCredit[1]); // new in 3.1, kill credit
|
||||
if (ci->GetModelByIdx(0))
|
||||
data << uint32(ci->GetModelByIdx(0)->CreatureDisplayID); // Modelid1
|
||||
else
|
||||
data << uint32(0); // Modelid1
|
||||
if (ci->GetModelByIdx(1))
|
||||
data << uint32(ci->GetModelByIdx(1)->CreatureDisplayID); // Modelid2
|
||||
else
|
||||
data << uint32(0); // Modelid2
|
||||
if (ci->GetModelByIdx(2))
|
||||
data << uint32(ci->GetModelByIdx(2)->CreatureDisplayID); // Modelid3
|
||||
else
|
||||
data << uint32(0); // Modelid3
|
||||
if (ci->GetModelByIdx(3))
|
||||
data << uint32(ci->GetModelByIdx(3)->CreatureDisplayID); // Modelid4
|
||||
else
|
||||
data << uint32(0); // Modelid4
|
||||
data << float(ci->ModHealth); // dmg/hp modifier
|
||||
data << float(ci->ModMana); // dmg/mana modifier
|
||||
data << uint8(ci->RacialLeader);
|
||||
|
||||
CreatureQuestItemList const* items = sObjectMgr->GetCreatureQuestItemList(entry);
|
||||
@@ -143,7 +155,7 @@ void WorldSession::HandleCreatureQueryOpcode(WorldPacket& recvData)
|
||||
for (size_t i = 0; i < MAX_CREATURE_QUEST_ITEMS; ++i)
|
||||
data << uint32(0);
|
||||
|
||||
data << uint32(ci->movementId); // CreatureMovementInfo.dbc
|
||||
data << uint32(ci->movementId); // CreatureMovementInfo.dbc
|
||||
SendPacket(&data);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -2705,7 +2705,7 @@ void AuraEffect::HandleAuraTransform(AuraApplication const* aurApp, uint8 mode,
|
||||
{
|
||||
uint32 model_id = 0;
|
||||
|
||||
if (uint32 modelid = ci->GetRandomValidModelId())
|
||||
if (uint32 modelid = ObjectMgr::ChooseDisplayId(ci)->CreatureDisplayID)
|
||||
model_id = modelid; // Will use the default model here
|
||||
|
||||
// Polymorph (sheep)
|
||||
@@ -2757,10 +2757,10 @@ void AuraEffect::HandleAuraTransform(AuraApplication const* aurApp, uint8 mode,
|
||||
uint32 cr_id = target->GetAuraEffectsByType(SPELL_AURA_MOUNTED).front()->GetMiscValue();
|
||||
if (CreatureTemplate const* ci = sObjectMgr->GetCreatureTemplate(cr_id))
|
||||
{
|
||||
uint32 displayID = ObjectMgr::ChooseDisplayId(ci);
|
||||
sObjectMgr->GetCreatureModelRandomGender(&displayID);
|
||||
CreatureModel model = *ObjectMgr::ChooseDisplayId(ci);
|
||||
sObjectMgr->GetCreatureModelRandomGender(&model, ci);
|
||||
|
||||
target->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, displayID);
|
||||
target->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, model.CreatureDisplayID);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3272,15 +3272,9 @@ void AuraEffect::HandleAuraMounted(AuraApplication const* aurApp, uint8 mode, bo
|
||||
|
||||
if (CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(creatureEntry))
|
||||
{
|
||||
if (GetMiscValueB() > 0) // Choose proper modelid
|
||||
{
|
||||
displayId = GetMiscValueB() == 2 && creatureInfo->Modelid2 > 0 ? creatureInfo->Modelid2 : creatureInfo->Modelid1;
|
||||
}
|
||||
else // Should we choose random modelid in this case?
|
||||
{
|
||||
displayId = ObjectMgr::ChooseDisplayId(creatureInfo);
|
||||
}
|
||||
sObjectMgr->GetCreatureModelRandomGender(&displayId);
|
||||
CreatureModel model = *ObjectMgr::ChooseDisplayId(creatureInfo);
|
||||
sObjectMgr->GetCreatureModelRandomGender(&model, creatureInfo);
|
||||
displayId = model.CreatureDisplayID;
|
||||
|
||||
vehicleId = creatureInfo->VehicleId;
|
||||
|
||||
@@ -5793,10 +5787,10 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool
|
||||
|
||||
if (CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(creatureEntry))
|
||||
{
|
||||
uint32 displayID = ObjectMgr::ChooseDisplayId(creatureInfo);
|
||||
sObjectMgr->GetCreatureModelRandomGender(&displayID);
|
||||
CreatureModel model = *ObjectMgr::ChooseDisplayId(creatureInfo);
|
||||
sObjectMgr->GetCreatureModelRandomGender(&model, creatureInfo);
|
||||
|
||||
target->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, displayID);
|
||||
target->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, model.CreatureDisplayID);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -5823,10 +5817,10 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool
|
||||
|
||||
if (CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(creatureEntry))
|
||||
{
|
||||
uint32 displayID = ObjectMgr::ChooseDisplayId(creatureInfo);
|
||||
sObjectMgr->GetCreatureModelRandomGender(&displayID);
|
||||
CreatureModel model = *ObjectMgr::ChooseDisplayId(creatureInfo);
|
||||
sObjectMgr->GetCreatureModelRandomGender(&model, creatureInfo);
|
||||
|
||||
target->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, displayID);
|
||||
target->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, model.CreatureDisplayID);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -63,7 +63,7 @@ struct npc_eye_of_acherus : public ScriptedAI
|
||||
{
|
||||
npc_eye_of_acherus(Creature* creature) : ScriptedAI(creature)
|
||||
{
|
||||
creature->SetDisplayId(creature->GetCreatureTemplate()->Modelid1);
|
||||
creature->SetDisplayFromModel(0);
|
||||
creature->SetReactState(REACT_PASSIVE);
|
||||
}
|
||||
|
||||
@@ -968,7 +968,7 @@ public:
|
||||
{
|
||||
me->SetImmuneToAll(true);
|
||||
me->SetFaction(FACTION_FRIENDLY);
|
||||
me->SetDisplayId(me->GetCreatureTemplate()->Modelid1); // Modelid2 is a horse.
|
||||
me->SetDisplayFromModel(0); // Modelid2 is a horse.
|
||||
}
|
||||
|
||||
ObjectGuid minerGUID;
|
||||
|
||||
Reference in New Issue
Block a user