aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Entities/Creature
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/game/Entities/Creature')
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp148
-rw-r--r--src/server/game/Entities/Creature/Creature.h4
-rw-r--r--src/server/game/Entities/Creature/CreatureData.h31
-rw-r--r--src/server/game/Entities/Creature/GossipDef.cpp8
-rw-r--r--src/server/game/Entities/Creature/Trainer.cpp31
-rw-r--r--src/server/game/Entities/Creature/Trainer.h3
6 files changed, 139 insertions, 86 deletions
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index e0f03a7717e..fde7d49a943 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -77,68 +77,67 @@ VendorItem const* VendorItemData::FindItemCostPair(uint32 item_id, uint32 extend
return nullptr;
}
-uint32 CreatureTemplate::GetRandomValidModelId() 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);
-}
+CreatureModel const CreatureModel::DefaultInvisibleModel(11686, 1.0f, 1.0f);
+CreatureModel const CreatureModel::DefaultVisibleModel(17519, 1.0f, 1.0f);
-uint32 CreatureTemplate::GetFirstValidModelId() const
+CreatureModel const* CreatureTemplate::GetModelByIdx(uint32 idx) const
{
- if (Modelid1) return Modelid1;
- if (Modelid2) return Modelid2;
- if (Modelid3) return Modelid3;
- if (Modelid4) return Modelid4;
- return 0;
+ return idx < Models.size() ? &Models[idx] : nullptr;
}
-uint32 CreatureTemplate::GetFirstInvisibleModel() const
+CreatureModel const* CreatureTemplate::GetRandomValidModel() const
{
- CreatureModelInfo const* modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid1);
- if (modelInfo && modelInfo->is_trigger)
- return Modelid1;
+ if (!Models.size())
+ return nullptr;
- modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid2);
- if (modelInfo && modelInfo->is_trigger)
- return Modelid2;
+ // If only one element, ignore the Probability (even if 0)
+ if (Models.size() == 1)
+ return &Models[0];
- modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid3);
- if (modelInfo && modelInfo->is_trigger)
- return Modelid3;
+ auto selectedItr = Trinity::Containers::SelectRandomWeightedContainerElement(Models, [](CreatureModel const& model)
+ {
+ return model.Probability;
+ });
- modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid4);
- if (modelInfo && modelInfo->is_trigger)
- return Modelid4;
+ return &(*selectedItr);
+}
- return 11686;
+CreatureModel const* CreatureTemplate::GetFirstValidModel() const
+{
+ for (CreatureModel const& model : Models)
+ if (model.CreatureDisplayID)
+ return &model;
+
+ return nullptr;
}
-uint32 CreatureTemplate::GetFirstVisibleModel() const
+CreatureModel const* CreatureTemplate::GetModelWithDisplayId(uint32 displayId) const
{
- CreatureModelInfo const* modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid1);
- if (modelInfo && !modelInfo->is_trigger)
- return Modelid1;
+ for (CreatureModel const& model : Models)
+ if (displayId == model.CreatureDisplayID)
+ return &model;
- modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid2);
- if (modelInfo && !modelInfo->is_trigger)
- return Modelid2;
+ return nullptr;
+}
- modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid3);
- if (modelInfo && !modelInfo->is_trigger)
- return Modelid3;
+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;
+}
- modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid4);
- if (modelInfo && !modelInfo->is_trigger)
- return Modelid4;
+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 17519;
+ return &CreatureModel::DefaultVisibleModel;
}
bool AssistDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
@@ -347,22 +346,22 @@ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/)
SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, uint8(cinfo->unit_class));
// Cancel load if no model defined
- if (!(cinfo->GetFirstValidModelId()))
+ if (!(cinfo->GetFirstValidModel()))
{
TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has no model defined in table `creature_template`, can't load. ", entry);
return false;
}
- uint32 displayID = ObjectMgr::ChooseDisplayId(GetCreatureTemplate(), data);
- CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelRandomGender(&displayID);
+ CreatureModel model = *ObjectMgr::ChooseDisplayId(cinfo, data);
+ CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelRandomGender(&model, cinfo);
if (!minfo) // Cancel load if no model defined
{
- TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has invalid model %u defined in table `creature_template`, can't load.", entry, displayID);
+ TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has invalid model %u defined in table `creature_template`, can't load.", entry, model.CreatureDisplayID);
return false;
}
- SetDisplayId(displayID);
- SetNativeDisplayId(displayID);
+ SetDisplayId(model.CreatureDisplayID, model.DisplayScale);
+ SetNativeDisplayId(model.CreatureDisplayID, model.DisplayScale);
SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, minfo->gender);
// Load creature equipment
@@ -388,7 +387,7 @@ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/)
SetSpeedRate(MOVE_SWIM, 1.0f); // using 1.0 rate
SetSpeedRate(MOVE_FLIGHT, 1.0f); // using 1.0 rate
- // Will set UNIT_FIELD_BOUNDINGRADIUS and UNIT_FIELD_COMBATREACH
+ // Will set UNIT_FIELD_BOUNDINGRADIUS, UNIT_FIELD_COMBATREACH and UNIT_FIELD_DISPLAYSCALE
SetObjectScale(cinfo->scale);
SetFloatValue(UNIT_FIELD_HOVERHEIGHT, cinfo->HoverHeight);
@@ -434,6 +433,8 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/,
SetUInt32Value(OBJECT_DYNAMIC_FLAGS, dynamicFlags);
+ SetUInt32Value(UNIT_FIELD_STATE_ANIM_ID, sAnimationDataStore.GetNumRows());
+
RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT);
SetBaseAttackTime(BASE_ATTACK, cInfo->BaseAttackTime);
@@ -894,7 +895,8 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 entry, float
if (!CreateFromProto(guidlow, entry, data, vehId))
return false;
- switch (GetCreatureTemplate()->rank)
+ cinfo = GetCreatureTemplate(); // might be different than initially requested
+ switch (cinfo->rank)
{
case CREATURE_ELITE_RARE:
m_corpseDelay = sWorld->getIntConfig(CONFIG_CORPSE_DECAY_RARE);
@@ -924,12 +926,12 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 entry, float
Relocate(x, y, z, ang);
}
- uint32 displayID = GetNativeDisplayId();
- CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelRandomGender(&displayID);
+ CreatureModel display(GetNativeDisplayId(), GetNativeDisplayScale(), 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, display.DisplayScale);
SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, minfo->gender);
}
@@ -942,10 +944,10 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 entry, float
m_serverSideVisibilityDetect.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_GHOST);
}
- if (GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING)
+ if (cinfo->flags_extra & CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING)
AddUnitState(UNIT_STATE_IGNORE_PATHFINDING);
- if (GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK)
+ if (cinfo->flags_extra & CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK)
{
ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK, true);
ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK_DEST, true);
@@ -1129,9 +1131,9 @@ void Creature::SaveToDB(uint32 mapid, std::vector<Difficulty> const& spawnDiffic
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;
@@ -1844,12 +1846,12 @@ void Creature::Respawn(bool force)
setDeathState(JUST_RESPAWNED);
- uint32 displayID = GetNativeDisplayId();
- CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelRandomGender(&displayID);
+ CreatureModel display(GetNativeDisplayId(), GetNativeDisplayScale(), 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, display.DisplayScale);
SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, minfo->gender);
}
@@ -2575,7 +2577,7 @@ uint8 Creature::GetLevelForTarget(WorldObject const* target) const
uint8 targetLevelWithDelta = unitTarget->getLevel() + GetInt32Value(UNIT_FIELD_SCALING_LEVEL_DELTA);
if (target->IsPlayer())
- targetLevelWithDelta += target->GetUInt32Value(PLAYER_FIELD_SCALING_PLAYER_LEVEL_DELTA);
+ targetLevelWithDelta += target->GetUInt32Value(ACTIVE_PLAYER_FIELD_SCALING_PLAYER_LEVEL_DELTA);
return RoundToInterval<uint8>(targetLevelWithDelta, GetUInt32Value(UNIT_FIELD_SCALING_LEVEL_MIN), GetUInt32Value(UNIT_FIELD_SCALING_LEVEL_MAX));
}
@@ -2853,9 +2855,9 @@ void Creature::SetObjectScale(float scale)
}
}
-void Creature::SetDisplayId(uint32 modelId)
+void Creature::SetDisplayId(uint32 modelId, float displayScale /*= 1.f*/)
{
- Unit::SetDisplayId(modelId);
+ Unit::SetDisplayId(modelId, displayScale);
if (CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelInfo(modelId))
{
@@ -2864,6 +2866,12 @@ void Creature::SetDisplayId(uint32 modelId)
}
}
+void Creature::SetDisplayFromModel(uint32 modelIdx)
+{
+ if (CreatureModel const* model = GetCreatureTemplate()->GetModelByIdx(modelIdx))
+ SetDisplayId(model->CreatureDisplayID, model->DisplayScale);
+}
+
void Creature::SetTarget(ObjectGuid const& guid)
{
if (IsFocusing(nullptr, true))
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index d87db9be2ca..b56fe8c9137 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -69,7 +69,8 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
void RemoveFromWorld() 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();
@@ -149,7 +150,6 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
bool UpdateStats(Stats stat) override;
bool UpdateAllStats() override;
- void UpdateResistances(uint32 school) override;
void UpdateArmor() override;
void UpdateMaxHealth() override;
void UpdateMaxPower(Powers power) override;
diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h
index d0d29d842e7..02fdcfbff16 100644
--- a/src/server/game/Entities/Creature/CreatureData.h
+++ b/src/server/game/Entities/Creature/CreatureData.h
@@ -301,16 +301,29 @@ struct CreatureLevelScaling
int16 DeltaLevelMax;
};
+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 TC_GAME_API CreatureTemplate
{
uint32 Entry;
uint32 DifficultyEntry[MAX_CREATURE_DIFFICULTIES];
uint32 KillCredit[MAX_KILL_CREDIT];
- uint32 Modelid1;
- uint32 Modelid2;
- uint32 Modelid3;
- uint32 Modelid4;
+ std::vector<CreatureModel> Models;
std::string Name;
std::string FemaleName;
std::string SubName;
@@ -369,10 +382,12 @@ struct TC_GAME_API CreatureTemplate
uint32 MechanicImmuneMask;
uint32 flags_extra;
uint32 ScriptID;
- uint32 GetRandomValidModelId() const;
- uint32 GetFirstValidModelId() const;
- uint32 GetFirstInvisibleModel() const;
- uint32 GetFirstVisibleModel() 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
SkillType GetRequiredLootSkill() const
diff --git a/src/server/game/Entities/Creature/GossipDef.cpp b/src/server/game/Entities/Creature/GossipDef.cpp
index 98a253fe870..9f5b461ef8d 100644
--- a/src/server/game/Entities/Creature/GossipDef.cpp
+++ b/src/server/game/Entities/Creature/GossipDef.cpp
@@ -286,6 +286,7 @@ void PlayerMenu::SendPointOfInterest(uint32 id) const
}
WorldPackets::NPC::GossipPOI packet;
+ packet.ID = pointOfInterest->ID;
packet.Name = pointOfInterest->Name;
LocaleConstant localeConstant = _session->GetSessionDbLocaleIndex();
@@ -438,6 +439,7 @@ void PlayerMenu::SendQuestGiverQuestDetails(Quest const* quest, ObjectGuid npcGU
packet.InformUnit = _session->GetPlayer()->GetDivider();
packet.QuestID = quest->GetQuestId();
packet.PortraitGiver = quest->GetQuestGiverPortrait();
+ packet.PortraitGiverMount = quest->GetQuestGiverPortraitMount();
packet.PortraitTurnIn = quest->GetQuestTurnInPortrait();
packet.AutoLaunched = autoLaunched;
packet.DisplayPopup = displayPopup;
@@ -512,6 +514,7 @@ void PlayerMenu::SendQuestQueryResponse(Quest const* quest) const
packet.Info.QuestID = quest->GetQuestId();
packet.Info.QuestType = quest->GetQuestType();
packet.Info.QuestLevel = quest->GetQuestLevel();
+ packet.Info.QuestScalingFactionGroup = quest->GetQuestScalingFactionGroup();
packet.Info.QuestMaxScalingLevel = quest->GetQuestMaxScalingLevel();
packet.Info.QuestPackageID = quest->GetQuestPackageID();
packet.Info.QuestMinLevel = quest->GetMinLevel();
@@ -543,12 +546,14 @@ void PlayerMenu::SendQuestQueryResponse(Quest const* quest) const
packet.Info.StartItem = quest->GetSrcItemId();
packet.Info.Flags = quest->GetFlags();
packet.Info.FlagsEx = quest->GetFlagsEx();
+ packet.Info.FlagsEx2 = quest->GetFlagsEx2();
packet.Info.RewardTitle = quest->GetRewTitle();
packet.Info.RewardArenaPoints = quest->GetRewArenaPoints();
packet.Info.RewardSkillLineID = quest->GetRewardSkillId();
packet.Info.RewardNumSkillUps = quest->GetRewardSkillPoints();
packet.Info.RewardFactionFlags = quest->GetRewardReputationMask();
packet.Info.PortraitGiver = quest->GetQuestGiverPortrait();
+ packet.Info.PortraitGiverMount = quest->GetQuestGiverPortraitMount();
packet.Info.PortraitTurnIn = quest->GetQuestTurnInPortrait();
for (uint8 i = 0; i < QUEST_ITEM_DROP_COUNT; ++i)
@@ -585,7 +590,7 @@ void PlayerMenu::SendQuestQueryResponse(Quest const* quest) const
packet.Info.POIPriority = quest->GetPOIPriority();
packet.Info.AllowableRaces = quest->GetAllowableRaces();
- packet.Info.QuestRewardID = quest->GetRewardId();
+ packet.Info.TreasurePickerID = quest->GetTreasurePickerId();
packet.Info.Expansion = quest->GetExpansion();
for (QuestObjective const& questObjective : quest->GetObjectives())
@@ -666,6 +671,7 @@ void PlayerMenu::SendQuestGiverOfferReward(Quest const* quest, ObjectGuid npcGUI
packet.PortraitTurnIn = quest->GetQuestTurnInPortrait();
packet.PortraitGiver = quest->GetQuestGiverPortrait();
+ packet.PortraitGiverMount = quest->GetQuestGiverPortraitMount();
packet.QuestPackageID = quest->GetQuestPackageID();
_session->SendPacket(packet.Write());
diff --git a/src/server/game/Entities/Creature/Trainer.cpp b/src/server/game/Entities/Creature/Trainer.cpp
index fb9f0af7f19..af59acb7496 100644
--- a/src/server/game/Entities/Creature/Trainer.cpp
+++ b/src/server/game/Entities/Creature/Trainer.cpp
@@ -24,6 +24,10 @@
namespace Trainer
{
+ bool Spell::IsCastable() const
+ {
+ return sSpellMgr->AssertSpellInfo(SpellId)->HasEffect(SPELL_EFFECT_LEARN_SPELL);
+ }
Trainer::Trainer(uint32 id, Type type, std::string greeting, std::vector<Spell> spells) : _id(id), _type(type), _spells(std::move(spells))
{
@@ -136,9 +140,30 @@ namespace Trainer
return SpellState::Unavailable;
// check ranks
- if (uint32 previousRankSpellId = sSpellMgr->GetPrevSpellInChain(trainerSpell->LearnedSpellId))
- if (!player->HasSpell(previousRankSpellId))
- return SpellState::Unavailable;
+ bool hasLearnSpellEffect = false;
+ bool knowsAllLearnedSpells = true;
+ for (SpellEffectInfo const* spellEffect : sSpellMgr->AssertSpellInfo(trainerSpell->SpellId)->GetEffectsForDifficulty(DIFFICULTY_NONE))
+ {
+ if (!spellEffect || !spellEffect->IsEffect(SPELL_EFFECT_LEARN_SPELL))
+ continue;
+
+ hasLearnSpellEffect = true;
+ if (!player->HasSpell(spellEffect->TriggerSpell))
+ knowsAllLearnedSpells = false;
+
+ if (uint32 previousRankSpellId = sSpellMgr->GetPrevSpellInChain(spellEffect->TriggerSpell))
+ if (!player->HasSpell(previousRankSpellId))
+ return SpellState::Unavailable;
+ }
+
+ if (!hasLearnSpellEffect)
+ {
+ if (uint32 previousRankSpellId = sSpellMgr->GetPrevSpellInChain(trainerSpell->SpellId))
+ if (!player->HasSpell(previousRankSpellId))
+ return SpellState::Unavailable;
+ }
+ else if (knowsAllLearnedSpells)
+ return SpellState::Known;
// check additional spell requirement
for (auto const& requirePair : sSpellMgr->GetSpellsRequiredForSpellBounds(trainerSpell->SpellId))
diff --git a/src/server/game/Entities/Creature/Trainer.h b/src/server/game/Entities/Creature/Trainer.h
index f7e9c51dba1..20ee7deb3f5 100644
--- a/src/server/game/Entities/Creature/Trainer.h
+++ b/src/server/game/Entities/Creature/Trainer.h
@@ -59,8 +59,7 @@ namespace Trainer
std::array<uint32, 3> ReqAbility = { };
uint8 ReqLevel = 0;
- uint32 LearnedSpellId = 0;
- bool IsCastable() const { return LearnedSpellId != SpellId; }
+ bool IsCastable() const;
};
class Trainer