diff options
Diffstat (limited to 'src')
29 files changed, 382 insertions, 557 deletions
diff --git a/src/server/database/Database/Implementation/WorldDatabase.cpp b/src/server/database/Database/Implementation/WorldDatabase.cpp index f5af5860cc4..ec9f91a548c 100644 --- a/src/server/database/Database/Implementation/WorldDatabase.cpp +++ b/src/server/database/Database/Implementation/WorldDatabase.cpp @@ -76,7 +76,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, help FROM command", CONNECTION_SYNCH); - PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, name, femaleName, subname, TitleAlt, IconName, HealthScalingExpansion, RequiredExpansion, VignetteID, faction, npcflag, speed_walk, speed_run, scale, `rank`, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, unit_flags3, dynamicflags, family, trainer_class, type, type_flags, type_flags2, lootid, pickpocketloot, skinloot, VehicleId, mingold, maxgold, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HealthModifier, HealthModifierExtra, ManaModifier, ManaModifierExtra, ArmorModifier, DamageModifier, ExperienceModifier, RacialLeader, movementId, CreatureDifficultyID, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, ScriptName, StringId FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId WHERE entry = ? OR 1 = ?", CONNECTION_SYNCH); + PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, KillCredit1, KillCredit2, name, femaleName, subname, TitleAlt, IconName, RequiredExpansion, VignetteID, faction, npcflag, speed_walk, speed_run, scale, `rank`, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, unit_flags3, dynamicflags, family, trainer_class, type, VehicleId, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, ExperienceModifier, RacialLeader, movementId, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, ScriptName, StringId FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId WHERE entry = ? OR 1 = ?", 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_CREATURE_BY_ID, "SELECT guid FROM creature WHERE id = ?", 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)) <= ? ORDER BY order_", CONNECTION_SYNCH); diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp index 572ab846ef2..c12c1b8c1f2 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp @@ -389,7 +389,7 @@ bool EscortAI::AssistPlayerInCombatAgainst(Unit* who) return false; // experimental (unknown) flag not present - if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST)) + if (!(me->GetCreatureDifficulty()->TypeFlags & CREATURE_TYPE_FLAG_CAN_ASSIST)) return false; // not a player diff --git a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp index 04057317e97..f12b7aaf9b6 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp @@ -268,7 +268,7 @@ bool FollowerAI::ShouldAssistPlayerInCombatAgainst(Unit* who) const return false; // experimental (unknown) flag not present - if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST)) + if (!(me->GetCreatureDifficulty()->TypeFlags & CREATURE_TYPE_FLAG_CAN_ASSIST)) return false; if (!who->isInAccessiblePlaceFor(me)) diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index 36917a73ea3..0b83f83643d 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -464,7 +464,7 @@ bool SmartAI::AssistPlayerInCombatAgainst(Unit* who) return false; // experimental (unknown) flag not present - if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST)) + if (!(me->GetCreatureDifficulty()->TypeFlags & CREATURE_TYPE_FLAG_CAN_ASSIST)) return false; // not a player diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 60d0319b9ce..522314d8687 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -164,19 +164,16 @@ CreatureModel const* CreatureTemplate::GetFirstVisibleModel() const return &CreatureModel::DefaultVisibleModel; } -int32 CreatureTemplate::GetHealthScalingExpansion() const -{ - return HealthScalingExpansion == EXPANSION_LEVEL_CURRENT ? CURRENT_EXPANSION : HealthScalingExpansion; -} - void CreatureTemplate::InitializeQueryData() { for (uint8 loc = LOCALE_enUS; loc < TOTAL_LOCALES; ++loc) - QueryData[loc] = BuildQueryData(static_cast<LocaleConstant>(loc)); + QueryData[loc] = BuildQueryData(static_cast<LocaleConstant>(loc), DIFFICULTY_NONE); } -WorldPacket CreatureTemplate::BuildQueryData(LocaleConstant loc) const +WorldPacket CreatureTemplate::BuildQueryData(LocaleConstant loc, Difficulty difficulty) const { + CreatureDifficulty const* creatureDifficulty = GetDifficulty(difficulty); + WorldPackets::Query::QueryCreatureResponse queryTemp; queryTemp.CreatureID = Entry; @@ -190,8 +187,8 @@ WorldPacket CreatureTemplate::BuildQueryData(LocaleConstant loc) const stats.Name[0] = Name; stats.NameAlt[0] = FemaleName; - stats.Flags[0] = type_flags; - stats.Flags[1] = type_flags2; + stats.Flags[0] = creatureDifficulty->TypeFlags; + stats.Flags[1] = creatureDifficulty->TypeFlags2; stats.CreatureType = type; stats.CreatureFamily = family; @@ -207,15 +204,15 @@ WorldPacket CreatureTemplate::BuildQueryData(LocaleConstant loc) const return { model.CreatureDisplayID, model.DisplayScale, model.Probability }; }); - stats.HpMulti = ModHealth; - stats.EnergyMulti = ModMana; + stats.HpMulti = creatureDifficulty->HealthModifier; + stats.EnergyMulti = creatureDifficulty->ManaModifier; stats.CreatureMovementInfoID = movementId; stats.RequiredExpansion = RequiredExpansion; - stats.HealthScalingExpansion = HealthScalingExpansion; + stats.HealthScalingExpansion = creatureDifficulty->HealthScalingExpansion; stats.VignetteID = VignetteID; stats.Class = unit_class; - stats.CreatureDifficultyID = CreatureDifficultyID; + stats.CreatureDifficultyID = creatureDifficulty->CreatureDifficultyID; stats.WidgetSetID = WidgetSetID; stats.WidgetSetUnitConditionID = WidgetSetUnitConditionID; @@ -223,7 +220,7 @@ WorldPacket CreatureTemplate::BuildQueryData(LocaleConstant loc) const stats.TitleAlt = TitleAlt; stats.CursorName = IconName; - if (std::vector<uint32> const* items = sObjectMgr->GetCreatureQuestItemList(Entry)) + if (std::vector<uint32> const* items = sObjectMgr->GetCreatureQuestItemList(Entry, difficulty)) stats.QuestItems.insert(stats.QuestItems.begin(), items->begin(), items->end()); if (loc != LOCALE_enUS) @@ -240,23 +237,42 @@ WorldPacket CreatureTemplate::BuildQueryData(LocaleConstant loc) const return queryTemp.Move(); } -CreatureLevelScaling const* CreatureTemplate::GetLevelScaling(Difficulty difficulty) const +CreatureDifficulty const* CreatureTemplate::GetDifficulty(Difficulty difficulty) const { - auto it = scalingStore.find(difficulty); - if (it != scalingStore.end()) + auto it = difficultyStore.find(difficulty); + if (it != difficultyStore.end()) return &it->second; - struct DefaultCreatureLevelScaling : public CreatureLevelScaling + // If there is no data for the difficulty, try to get data for the fallback difficulty + DifficultyEntry const* difficultyEntry = sDifficultyStore.LookupEntry(difficulty); + if (difficultyEntry) + return GetDifficulty(Difficulty(difficultyEntry->FallbackDifficultyID)); + + // No data for DIFFICULTY_NONE (0) + struct DefaultCreatureDifficulty : public CreatureDifficulty { - DefaultCreatureLevelScaling() + DefaultCreatureDifficulty() { DeltaLevelMin = 0; DeltaLevelMax = 0; ContentTuningID = 0; + HealthScalingExpansion = 0; + HealthModifier = 1.f; + ManaModifier = 1.f; + ArmorModifier = 1.f; + DamageModifier = 1.f; + CreatureDifficultyID = 0; + TypeFlags = 0; + TypeFlags2 = 0; + LootID = 0; + PickPocketLootID = 0; + SkinLootID = 0; + GoldMin = 0; + GoldMax = 0; } }; - static const DefaultCreatureLevelScaling defScaling; - return &defScaling; + static const DefaultCreatureDifficulty defDifficulty; + return &defDifficulty; } bool AssistDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) @@ -292,7 +308,7 @@ bool ForcedDespawnDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) Creature::Creature(bool isWorldObject) : Unit(isWorldObject), MapObject(), m_PlayerDamageReq(0), m_dontClearTapListOnEvade(false), _pickpocketLootRestore(0), m_corpseRemoveTime(0), m_respawnTime(0), m_respawnDelay(300), m_corpseDelay(60), m_ignoreCorpseDecayRatio(false), m_wanderDistance(0.0f), m_boundaryCheckTime(2500), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE), m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(UI64LIT(0)), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false), m_cannotReachTarget(false), m_cannotReachTimer(0), - m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), _waypointPathId(0), _currentWaypointNodeInfo(0, 0), + m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_creatureDifficulty(nullptr), _waypointPathId(0), _currentWaypointNodeInfo(0, 0), m_formation(nullptr), m_triggerJustAppeared(true), m_respawnCompatibilityMode(false), _lastDamagedTime(0), _regenerateHealth(true), _isMissingCanSwimFlagOutOfCombat(false), _gossipMenuId(0), _sparringHealthPct(0) { @@ -472,55 +488,32 @@ void Creature::RemoveCorpse(bool setSpawnTime, bool destroyForNearbyPlayers) */ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/) { - CreatureTemplate const* normalInfo = sObjectMgr->GetCreatureTemplate(entry); - if (!normalInfo) + CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(entry); + if (!creatureInfo) { TC_LOG_ERROR("sql.sql", "Creature::InitEntry creature entry {} does not exist.", entry); return false; } - // get difficulty 1 mode entry - CreatureTemplate const* cinfo = nullptr; - DifficultyEntry const* difficultyEntry = sDifficultyStore.LookupEntry(GetMap()->GetDifficultyID()); - while (!cinfo && difficultyEntry) - { - int32 idx = CreatureTemplate::DifficultyIDToDifficultyEntryIndex(difficultyEntry->ID); - if (idx == -1) - break; - - if (normalInfo->DifficultyEntry[idx]) - { - cinfo = sObjectMgr->GetCreatureTemplate(normalInfo->DifficultyEntry[idx]); - break; - } - - if (!difficultyEntry->FallbackDifficultyID) - break; - - difficultyEntry = sDifficultyStore.LookupEntry(difficultyEntry->FallbackDifficultyID); - } - - if (!cinfo) - cinfo = normalInfo; - - SetEntry(entry); // normal entry always - m_creatureInfo = cinfo; // map mode related always + m_creatureInfo = creatureInfo; + SetEntry(entry); + m_creatureDifficulty = creatureInfo->GetDifficulty(GetMap()->GetDifficultyID()); // equal to player Race field, but creature does not have race SetRace(RACE_NONE); // known valid are: CLASS_WARRIOR, CLASS_PALADIN, CLASS_ROGUE, CLASS_MAGE - SetClass(uint8(cinfo->unit_class)); + SetClass(uint8(creatureInfo->unit_class)); // Cancel load if no model defined - if (!(cinfo->GetFirstValidModel())) + if (!(creatureInfo->GetFirstValidModel())) { TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has no model defined in table `creature_template`, can't load. ", entry); return false; } - CreatureModel model = *ObjectMgr::ChooseDisplayId(cinfo, data); - CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelRandomGender(&model, cinfo); + CreatureModel model = *ObjectMgr::ChooseDisplayId(creatureInfo, data); + CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelRandomGender(&model, creatureInfo); if (!minfo) // Cancel load if no model defined { TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid model {} defined in table `creature_template`, can't load.", entry, model.CreatureDisplayID); @@ -540,7 +533,7 @@ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/) LoadEquipment(data->equipmentId); } - SetName(normalInfo->Name); // at normal entry always + SetName(creatureInfo->Name); // at normal entry always SetModCastingSpeed(1.0f); SetModSpellHaste(1.0f); @@ -549,30 +542,30 @@ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/) SetModHasteRegen(1.0f); SetModTimeRate(1.0f); - SetSpeedRate(MOVE_WALK, cinfo->speed_walk); - SetSpeedRate(MOVE_RUN, cinfo->speed_run); + SetSpeedRate(MOVE_WALK, creatureInfo->speed_walk); + SetSpeedRate(MOVE_RUN, creatureInfo->speed_run); SetSpeedRate(MOVE_SWIM, 1.0f); // using 1.0 rate SetSpeedRate(MOVE_FLIGHT, 1.0f); // using 1.0 rate // Will set UNIT_FIELD_BOUNDINGRADIUS, UNIT_FIELD_COMBATREACH and UNIT_FIELD_DISPLAYSCALE SetObjectScale(GetNativeObjectScale()); - SetCanDualWield(cinfo->flags_extra & CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK); + SetCanDualWield(creatureInfo->flags_extra & CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK); // checked at loading - m_defaultMovementType = MovementGeneratorType(data ? data->movementType : cinfo->MovementType); + m_defaultMovementType = MovementGeneratorType(data ? data->movementType : creatureInfo->MovementType); if (!m_wanderDistance && m_defaultMovementType == RANDOM_MOTION_TYPE) m_defaultMovementType = IDLE_MOTION_TYPE; for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i) m_spells[i] = GetCreatureTemplate()->spells[i]; - _staticFlags.ApplyFlag(CREATURE_STATIC_FLAG_NO_XP, cinfo->type == CREATURE_TYPE_CRITTER + _staticFlags.ApplyFlag(CREATURE_STATIC_FLAG_NO_XP, creatureInfo->type == CREATURE_TYPE_CRITTER || IsPet() || IsTotem() - || cinfo->flags_extra & CREATURE_FLAG_EXTRA_NO_XP); + || creatureInfo->flags_extra & CREATURE_FLAG_EXTRA_NO_XP); - _staticFlags.ApplyFlag(CREATURE_STATIC_FLAG_4_TREAT_AS_RAID_UNIT_FOR_HELPFUL_SPELLS, (cinfo->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT) != 0); + _staticFlags.ApplyFlag(CREATURE_STATIC_FLAG_4_TREAT_AS_RAID_UNIT_FOR_HELPFUL_SPELLS, (GetCreatureDifficulty()->TypeFlags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT) != 0); return true; } @@ -1072,6 +1065,8 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 entry, Posit return false; } + CreatureDifficulty const* creatureDifficulty = cinfo->GetDifficulty(GetMap()->GetDifficultyID()); + //! Relocate before CreateFromProto, to initialize coords and allow //! returning correct zone id for selecting OutdoorPvP/Battlefield script Relocate(pos); @@ -1091,7 +1086,7 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 entry, Posit } // Allow players to see those units while dead, do it here (mayby altered by addon auras) - if (cinfo->type_flags & CREATURE_TYPE_FLAG_VISIBLE_TO_GHOSTS) + if (creatureDifficulty->TypeFlags & CREATURE_TYPE_FLAG_VISIBLE_TO_GHOSTS) m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_ALIVE | GHOST_VISIBILITY_GHOST); if (!CreateFromProto(guidlow, entry, data, vehId)) @@ -1302,7 +1297,7 @@ uint32 Creature::GetLootId() const if (m_lootId) return *m_lootId; - return GetCreatureTemplate()->lootid; + return GetCreatureDifficulty()->LootID; } void Creature::SetLootId(Optional<uint32> lootId) @@ -1568,7 +1563,7 @@ void Creature::UpdateLevelDependantStats() // mana Powers powerType = CalculateDisplayPowerType(); SetCreateMana(stats->BaseMana); - SetStatPctModifier(UnitMods(UNIT_MOD_POWER_START + AsUnderlyingType(powerType)), BASE_PCT, cInfo->ModMana * cInfo->ModManaExtra); + SetStatPctModifier(UnitMods(UNIT_MOD_POWER_START + AsUnderlyingType(powerType)), BASE_PCT, GetCreatureDifficulty()->ManaModifier); SetPowerType(powerType); if (PowerTypeEntry const* powerTypeEntry = sDB2Manager.GetPowerTypeEntry(powerType)) @@ -2432,7 +2427,7 @@ bool Creature::isWorldBoss() const if (IsPet()) return false; - return (GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_BOSS_MOB) != 0; + return (GetCreatureDifficulty()->TypeFlags & CREATURE_TYPE_FLAG_BOSS_MOB) != 0; } // select nearest hostile unit within the given distance (regardless of threat list). @@ -2688,8 +2683,7 @@ CreatureAddon const* Creature::GetCreatureAddon() const return addon; } - // dependent from difficulty mode entry - return sObjectMgr->GetCreatureTemplateAddon(GetCreatureTemplate()->Entry); + return sObjectMgr->GetCreatureTemplateAddon(GetEntry()); } //creature_addon table @@ -2939,28 +2933,28 @@ bool Creature::HasScalableLevels() const void Creature::ApplyLevelScaling() { - CreatureLevelScaling const* scaling = GetCreatureTemplate()->GetLevelScaling(GetMap()->GetDifficultyID()); + CreatureDifficulty const* creatureDifficulty = GetCreatureDifficulty(); - if (Optional<ContentTuningLevels> levels = sDB2Manager.GetContentTuningData(scaling->ContentTuningID, 0)) + if (Optional<ContentTuningLevels> levels = sDB2Manager.GetContentTuningData(creatureDifficulty->ContentTuningID, 0)) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::ScalingLevelMin), levels->MinLevel); SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::ScalingLevelMax), levels->MaxLevel); } - int32 mindelta = std::min(scaling->DeltaLevelMax, scaling->DeltaLevelMin); - int32 maxdelta = std::max(scaling->DeltaLevelMax, scaling->DeltaLevelMin); + int32 mindelta = std::min(creatureDifficulty->DeltaLevelMax, creatureDifficulty->DeltaLevelMin); + int32 maxdelta = std::max(creatureDifficulty->DeltaLevelMax, creatureDifficulty->DeltaLevelMin); int32 delta = mindelta == maxdelta ? mindelta : irand(mindelta, maxdelta); SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::ScalingLevelDelta), delta); - SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::ContentTuningID), scaling->ContentTuningID); + SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::ContentTuningID), creatureDifficulty->ContentTuningID); } uint64 Creature::GetMaxHealthByLevel(uint8 level) const { CreatureTemplate const* cInfo = GetCreatureTemplate(); - CreatureLevelScaling const* scaling = cInfo->GetLevelScaling(GetMap()->GetDifficultyID()); - float baseHealth = sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureHealth, level, cInfo->GetHealthScalingExpansion(), scaling->ContentTuningID, Classes(cInfo->unit_class)); - return std::max(baseHealth * cInfo->ModHealth * cInfo->ModHealthExtra, 1.0f); + CreatureDifficulty const* creatureDifficulty = GetCreatureDifficulty(); + float baseHealth = sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureHealth, level, creatureDifficulty->GetHealthScalingExpansion(), creatureDifficulty->ContentTuningID, Classes(cInfo->unit_class)); + return std::max(baseHealth * creatureDifficulty->HealthModifier, 1.0f); } float Creature::GetHealthMultiplierForTarget(WorldObject const* target) const @@ -2978,8 +2972,8 @@ float Creature::GetHealthMultiplierForTarget(WorldObject const* target) const float Creature::GetBaseDamageForLevel(uint8 level) const { CreatureTemplate const* cInfo = GetCreatureTemplate(); - CreatureLevelScaling const* scaling = cInfo->GetLevelScaling(GetMap()->GetDifficultyID()); - return sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureAutoAttackDps, level, cInfo->GetHealthScalingExpansion(), scaling->ContentTuningID, Classes(cInfo->unit_class)); + CreatureDifficulty const* creatureDifficulty = GetCreatureDifficulty(); + return sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureAutoAttackDps, level, creatureDifficulty->GetHealthScalingExpansion(), creatureDifficulty->ContentTuningID, Classes(cInfo->unit_class)); } float Creature::GetDamageMultiplierForTarget(WorldObject const* target) const @@ -2995,9 +2989,9 @@ float Creature::GetDamageMultiplierForTarget(WorldObject const* target) const float Creature::GetBaseArmorForLevel(uint8 level) const { CreatureTemplate const* cInfo = GetCreatureTemplate(); - CreatureLevelScaling const* scaling = cInfo->GetLevelScaling(GetMap()->GetDifficultyID()); - float baseArmor = sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureArmor, level, cInfo->GetHealthScalingExpansion(), scaling->ContentTuningID, Classes(cInfo->unit_class)); - return baseArmor * cInfo->ModArmor; + CreatureDifficulty const* creatureDifficulty = GetCreatureDifficulty(); + float baseArmor = sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureArmor, level, creatureDifficulty->GetHealthScalingExpansion(), creatureDifficulty->ContentTuningID, Classes(cInfo->unit_class)); + return baseArmor * creatureDifficulty->ArmorModifier; } float Creature::GetArmorMultiplierForTarget(WorldObject const* target) const @@ -3505,7 +3499,7 @@ void Creature::AtEngage(Unit* target) { Unit::AtEngage(target); - if (!(GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_ALLOW_MOUNTED_COMBAT)) + if (!(GetCreatureDifficulty()->TypeFlags & CREATURE_TYPE_FLAG_ALLOW_MOUNTED_COMBAT)) Dismount(); RefreshCanSwimFlag(); diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index a60701e8f26..1876b77f664 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -215,6 +215,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma CreatureTemplate const* GetCreatureTemplate() const { return m_creatureInfo; } CreatureData const* GetCreatureData() const { return m_creatureData; } + CreatureDifficulty const* GetCreatureDifficulty() const { return m_creatureDifficulty; } CreatureAddon const* GetCreatureAddon() const; std::string const& GetAIName() const; @@ -472,8 +473,9 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma bool DisableReputationGain; - CreatureTemplate const* m_creatureInfo; // Can differ from sObjectMgr->GetCreatureTemplate(GetEntry()) in difficulty mode > 0 + CreatureTemplate const* m_creatureInfo; CreatureData const* m_creatureData; + CreatureDifficulty const* m_creatureDifficulty; std::array<std::string_view, 3> m_stringIds; Optional<std::string> m_scriptStringId; diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h index 010afc75434..e84c1541749 100644 --- a/src/server/game/Entities/Creature/CreatureData.h +++ b/src/server/game/Entities/Creature/CreatureData.h @@ -441,7 +441,6 @@ const uint8 MAX_KILL_CREDIT = 2; const uint32 MAX_CREATURE_MODELS = 4; const uint32 MAX_CREATURE_NAMES = 4; const uint32 MAX_CREATURE_SPELLS = 8; -const uint32 MAX_CREATURE_DIFFICULTIES = 3; struct CreatureModel { @@ -459,18 +458,48 @@ struct CreatureModel float Probability; }; -struct CreatureLevelScaling +struct CreatureDifficulty { int16 DeltaLevelMin; int16 DeltaLevelMax; int32 ContentTuningID; + int32 HealthScalingExpansion; + float HealthModifier; + float ManaModifier; + float ArmorModifier; + float DamageModifier; + int32 CreatureDifficultyID; + uint32 TypeFlags; + uint32 TypeFlags2; + uint32 LootID; + uint32 PickPocketLootID; + uint32 SkinLootID; + uint32 GoldMin; + uint32 GoldMax; + + // Helpers + int32 GetHealthScalingExpansion() const + { + return HealthScalingExpansion == EXPANSION_LEVEL_CURRENT ? CURRENT_EXPANSION : HealthScalingExpansion; + } + + SkillType GetRequiredLootSkill() const + { + if (TypeFlags & CREATURE_TYPE_FLAG_SKIN_WITH_HERBALISM) + return SKILL_HERBALISM; + else if (TypeFlags & CREATURE_TYPE_FLAG_SKIN_WITH_MINING) + return SKILL_MINING; + else if (TypeFlags & CREATURE_TYPE_FLAG_SKIN_WITH_ENGINEERING) + return SKILL_ENGINEERING; + else + return SKILL_SKINNING; // Default case + } }; // from `creature_template` table struct TC_GAME_API CreatureTemplate { uint32 Entry; - uint32 DifficultyEntry[MAX_CREATURE_DIFFICULTIES]; uint32 KillCredit[MAX_KILL_CREDIT]; std::vector<CreatureModel> Models; std::string Name; @@ -479,8 +508,7 @@ struct TC_GAME_API CreatureTemplate std::string TitleAlt; std::string IconName; std::vector<uint32> GossipMenuIds; - std::unordered_map<Difficulty, CreatureLevelScaling> scalingStore; - int32 HealthScalingExpansion; + std::unordered_map<Difficulty, CreatureDifficulty> difficultyStore; uint32 RequiredExpansion; uint32 VignetteID; /// @todo Read Vignette.db2 uint32 faction; @@ -502,29 +530,15 @@ struct TC_GAME_API CreatureTemplate CreatureFamily family; // enum CreatureFamily values (optional) uint32 trainer_class; uint32 type; // enum CreatureType values - uint32 type_flags; // enum CreatureTypeFlags mask values - uint32 type_flags2; // unknown enum, only set for 4 creatures (with value 1) - uint32 lootid; - uint32 pickpocketLootId; - uint32 SkinLootId; int32 resistance[MAX_SPELL_SCHOOL]; uint32 spells[MAX_CREATURE_SPELLS]; uint32 VehicleId; - uint32 mingold; - uint32 maxgold; std::string AIName; uint32 MovementType; CreatureMovementData Movement; - float ModHealth; - float ModHealthExtra; - float ModMana; - float ModManaExtra; // Added in 4.x, this value is usually 2 for a small group of creatures with double mana - float ModArmor; - float ModDamage; float ModExperience; bool RacialLeader; uint32 movementId; - int32 CreatureDifficultyID; int32 WidgetSetID; int32 WidgetSetUnitConditionID; bool RegenHealth; @@ -540,70 +554,25 @@ struct TC_GAME_API CreatureTemplate CreatureModel const* GetModelWithDisplayId(uint32 displayId) const; CreatureModel const* GetFirstInvisibleModel() const; CreatureModel const* GetFirstVisibleModel() const; - int32 GetHealthScalingExpansion() const; - CreatureLevelScaling const* GetLevelScaling(Difficulty difficulty) const; + CreatureDifficulty const* GetDifficulty(Difficulty difficulty) const; - // helpers - SkillType GetRequiredLootSkill() const - { - if (type_flags & CREATURE_TYPE_FLAG_SKIN_WITH_HERBALISM) - return SKILL_HERBALISM; - else if (type_flags & CREATURE_TYPE_FLAG_SKIN_WITH_MINING) - return SKILL_MINING; - else if (type_flags & CREATURE_TYPE_FLAG_SKIN_WITH_ENGINEERING) - return SKILL_ENGINEERING; - else - return SKILL_SKINNING; // normal case - } - - bool IsExotic() const + // Helpers + bool IsExotic(CreatureDifficulty const* creatureDifficulty) const { - return (type_flags & CREATURE_TYPE_FLAG_TAMEABLE_EXOTIC) != 0; + return (creatureDifficulty->TypeFlags & CREATURE_TYPE_FLAG_TAMEABLE_EXOTIC) != 0; } - bool IsTameable(bool canTameExotic) const + bool IsTameable(bool canTameExotic, CreatureDifficulty const* creatureDifficulty) const { - if (type != CREATURE_TYPE_BEAST || family == CREATURE_FAMILY_NONE || (type_flags & CREATURE_TYPE_FLAG_TAMEABLE) == 0) + if (type != CREATURE_TYPE_BEAST || family == CREATURE_FAMILY_NONE || (creatureDifficulty->TypeFlags & CREATURE_TYPE_FLAG_TAMEABLE) == 0) return false; // if can tame exotic then can tame any tameable - return canTameExotic || !IsExotic(); + return canTameExotic || !IsExotic(creatureDifficulty); } void InitializeQueryData(); - WorldPacket BuildQueryData(LocaleConstant loc) const; - - static int32 DifficultyIDToDifficultyEntryIndex(uint32 difficulty) - { - switch (difficulty) - { - case DIFFICULTY_NONE: - case DIFFICULTY_NORMAL: - case DIFFICULTY_10_N: - case DIFFICULTY_40: - case DIFFICULTY_3_MAN_SCENARIO_N: - case DIFFICULTY_NORMAL_RAID: - return -1; - case DIFFICULTY_HEROIC: - case DIFFICULTY_25_N: - case DIFFICULTY_3_MAN_SCENARIO_HC: - case DIFFICULTY_HEROIC_RAID: - return 0; - case DIFFICULTY_10_HC: - case DIFFICULTY_MYTHIC_KEYSTONE: - case DIFFICULTY_MYTHIC_RAID: - return 1; - case DIFFICULTY_25_HC: - return 2; - case DIFFICULTY_LFR: - case DIFFICULTY_LFR_NEW: - case DIFFICULTY_EVENT_RAID: - case DIFFICULTY_EVENT_DUNGEON: - case DIFFICULTY_EVENT_SCENARIO: - default: - return -1; - } - } + WorldPacket BuildQueryData(LocaleConstant loc, Difficulty difficulty) const; }; #pragma pack(push, 1) diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 96bbbd0c0a9..7b80617b528 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -3191,7 +3191,7 @@ bool WorldObject::IsValidAssistTarget(WorldObject const* target, SpellInfo const if (!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_ASSIST_IMMUNE_PC)) if (unitTarget && !unitTarget->IsPvP()) if (Creature const* creatureTarget = target->ToCreature()) - return creatureTarget->HasFlag(CREATURE_STATIC_FLAG_4_TREAT_AS_RAID_UNIT_FOR_HELPFUL_SPELLS) || (creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST); + return creatureTarget->HasFlag(CREATURE_STATIC_FLAG_4_TREAT_AS_RAID_UNIT_FOR_HELPFUL_SPELLS) || (creatureTarget->GetCreatureDifficulty()->TypeFlags & CREATURE_TYPE_FLAG_CAN_ASSIST); } return true; diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 27102506008..7b7ad04704b 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -230,7 +230,7 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c if (petInfo->Type == HUNTER_PET) { CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(petInfo->CreatureId); - if (!creatureInfo || !creatureInfo->IsTameable(owner->CanTameExoticPets())) + if (!creatureInfo || !creatureInfo->IsTameable(owner->CanTameExoticPets(), GetCreatureDifficulty())) return false; } @@ -919,7 +919,8 @@ bool Guardian::InitStatsForLevel(uint8 petlevel) CreatureBaseStats const* stats = sObjectMgr->GetCreatureBaseStats(petlevel, cinfo->unit_class); ApplyLevelScaling(); - SetCreateHealth(std::max(sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureHealth, petlevel, cinfo->GetHealthScalingExpansion(), m_unitData->ContentTuningID, Classes(cinfo->unit_class)) * cinfo->ModHealth * cinfo->ModHealthExtra * _GetHealthMod(cinfo->rank), 1.0f)); + CreatureDifficulty const* creatureDifficulty = GetCreatureDifficulty(); + SetCreateHealth(std::max(sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureHealth, petlevel, creatureDifficulty->GetHealthScalingExpansion(), m_unitData->ContentTuningID, Classes(cinfo->unit_class)) * creatureDifficulty->HealthModifier * _GetHealthMod(cinfo->rank), 1.0f)); SetCreateMana(stats->BaseMana); SetCreateStat(STAT_STRENGTH, 22); SetCreateStat(STAT_AGILITY, 22); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index a2f4245b5bf..6d28af5cd21 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -2030,11 +2030,11 @@ Creature* Player::GetNPCIfCanInteractWith(ObjectGuid const& guid, NPCFlags npcFl return nullptr; // Deathstate checks - if (!IsAlive() && !(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_VISIBLE_TO_GHOSTS)) + if (!IsAlive() && !(creature->GetCreatureDifficulty()->TypeFlags & CREATURE_TYPE_FLAG_VISIBLE_TO_GHOSTS)) return nullptr; // alive or spirit healer - if (!creature->IsAlive() && !(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_INTERACT_WHILE_DEAD)) + if (!creature->IsAlive() && !(creature->GetCreatureDifficulty()->TypeFlags & CREATURE_TYPE_FLAG_INTERACT_WHILE_DEAD)) return nullptr; // appropriate npc type diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp index 2e1f248ecc7..b408ebe5403 100644 --- a/src/server/game/Entities/Unit/StatSystem.cpp +++ b/src/server/game/Entities/Unit/StatSystem.cpp @@ -1034,7 +1034,7 @@ void Creature::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, float basePct = GetPctModifierValue(unitMod, BASE_PCT) * attackSpeedMulti; float totalValue = GetFlatModifierValue(unitMod, TOTAL_VALUE); float totalPct = addTotalPct ? GetPctModifierValue(unitMod, TOTAL_PCT) : 1.0f; - float dmgMultiplier = GetCreatureTemplate()->ModDamage; // = ModDamage * _GetDamageMod(rank); + float dmgMultiplier = GetCreatureDifficulty()->DamageModifier; // = DamageModifier * _GetDamageMod(rank); minDamage = ((weaponMinDamage + baseValue) * dmgMultiplier * basePct + totalValue) * totalPct; maxDamage = ((weaponMaxDamage + baseValue) * dmgMultiplier * basePct + totalValue) * totalPct; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index f6acfa09d7b..7de61fa8ed8 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -10747,7 +10747,7 @@ void Unit::SetMeleeAnimKitId(uint16 animKitId) if (dungeonEncounter) { creature->m_personalLoot = GenerateDungeonEncounterPersonalLoot(dungeonEncounter->ID, creature->GetLootId(), - LootTemplates_Creature, LOOT_CORPSE, creature, creature->GetCreatureTemplate()->mingold, creature->GetCreatureTemplate()->maxgold, + LootTemplates_Creature, LOOT_CORPSE, creature, creature->GetCreatureDifficulty()->GoldMin, creature->GetCreatureDifficulty()->GoldMax, creature->GetLootMode(), creature->GetMap()->GetDifficultyLootItemContext(), tappers); } else if (!tappers.empty()) @@ -10761,7 +10761,7 @@ void Unit::SetMeleeAnimKitId(uint16 animKitId) loot->FillLoot(lootid, LootTemplates_Creature, looter, dungeonEncounter != nullptr, false, creature->GetLootMode(), creature->GetMap()->GetDifficultyLootItemContext()); if (creature->GetLootMode() > 0) - loot->generateMoneyLoot(creature->GetCreatureTemplate()->mingold, creature->GetCreatureTemplate()->maxgold); + loot->generateMoneyLoot(creature->GetCreatureDifficulty()->GoldMin, creature->GetCreatureDifficulty()->GoldMax); if (group) loot->NotifyLootList(creature->GetMap()); @@ -10787,7 +10787,7 @@ void Unit::SetMeleeAnimKitId(uint16 animKitId) loot->FillLoot(lootid, LootTemplates_Creature, tapper, true, false, creature->GetLootMode(), creature->GetMap()->GetDifficultyLootItemContext()); if (creature->GetLootMode() > 0) - loot->generateMoneyLoot(creature->GetCreatureTemplate()->mingold, creature->GetCreatureTemplate()->maxgold); + loot->generateMoneyLoot(creature->GetCreatureDifficulty()->GoldMin, creature->GetCreatureDifficulty()->GoldMax); creature->m_personalLoot[tapper->GetGUID()].reset(loot); } @@ -10887,7 +10887,7 @@ void Unit::SetMeleeAnimKitId(uint16 animKitId) else creature->AllLootRemovedFromCorpse(); - if (creature->CanHaveLoot() && LootTemplates_Skinning.HaveLootFor(creature->GetCreatureTemplate()->SkinLootId)) + if (creature->CanHaveLoot() && LootTemplates_Skinning.HaveLootFor(creature->GetCreatureDifficulty()->SkinLootID)) { creature->SetDynamicFlag(UNIT_DYNFLAG_CAN_SKIN); creature->SetUnitFlag(UNIT_FLAG_SKINNABLE); diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index a0ec6d3c411..78179a5534c 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -353,23 +353,23 @@ void ObjectMgr::LoadCreatureTemplates() { uint32 oldMSTime = getMSTime(); - // 0 1 2 3 4 5 6 7 8 - // "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, name, femaleName, subname, " - // 9 10 11 12 13 - // "TitleAlt, IconName, HealthScalingExpansion, RequiredExpansion, VignetteID, " - // 14 15 16 17 18 19 20 21 22 23 24 + // 0 1 2 3 4 5 + // "SELECT entry, KillCredit1, KillCredit2, name, femaleName, subname, " + // 6 7 8 9 + // "TitleAlt, IconName, RequiredExpansion, VignetteID, " + // 10 11 12 13 14 15 16 17 18 19 20 // "faction, npcflag, speed_walk, speed_run, scale, `rank`, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, " - // 25 26 27 28 29 30 31 32 + // 21 22 23 24 25 26 27 28 // "unit_class, unit_flags, unit_flags2, unit_flags3, dynamicflags, family, trainer_class, type, " - // 33 34 35 36 37 38 39 40 41 42 - // "type_flags, type_flags2, lootid, pickpocketloot, skinloot, VehicleId, mingold, maxgold, AIName, MovementType, " - // 43 44 45 46 47 48 49 50 51 52 53 54 55 56 - // "ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HealthModifier, HealthModifierExtra, ManaModifier, ManaModifierExtra, ArmorModifier, DamageModifier, ExperienceModifier, " - // 57 58 59 60 61 62 - // "RacialLeader, movementId, CreatureDifficultyID, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, " - // 63 64 65 + // 29 30 31 + // "VehicleId, AIName, MovementType, " + // 32 33 34 35 36 38 38 39 + // "ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, ExperienceModifier, " + // 40 41 42 43 44 + // "RacialLeader, movementId, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, " + // 45 46 47 // "mechanic_immune_mask, spell_school_immune_mask, flags_extra, " - // 66 67 + // 48 49 // "ScriptName, StringId FROM creature_template WHERE entry = ? OR 1 = ?"); WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_CREATURE_TEMPLATE); @@ -412,44 +412,35 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields) creatureTemplate.Entry = entry; - for (uint32 i = 0; i < MAX_CREATURE_DIFFICULTIES; ++i) - creatureTemplate.DifficultyEntry[i] = fields[1 + i].GetUInt32(); - for (uint8 i = 0; i < MAX_KILL_CREDIT; ++i) - creatureTemplate.KillCredit[i] = fields[4 + i].GetUInt32(); - - creatureTemplate.Name = fields[6].GetString(); - creatureTemplate.FemaleName = fields[7].GetString(); - creatureTemplate.SubName = fields[8].GetString(); - creatureTemplate.TitleAlt = fields[9].GetString(); - creatureTemplate.IconName = fields[10].GetString(); - creatureTemplate.HealthScalingExpansion = fields[11].GetInt32(); - creatureTemplate.RequiredExpansion = fields[12].GetUInt32(); - creatureTemplate.VignetteID = fields[13].GetUInt32(); - creatureTemplate.faction = fields[14].GetUInt16(); - creatureTemplate.npcflag = fields[15].GetUInt64(); - creatureTemplate.speed_walk = fields[16].GetFloat(); - creatureTemplate.speed_run = fields[17].GetFloat(); - creatureTemplate.scale = fields[18].GetFloat(); - creatureTemplate.rank = uint32(fields[19].GetUInt8()); - creatureTemplate.dmgschool = uint32(fields[20].GetInt8()); - creatureTemplate.BaseAttackTime = fields[21].GetUInt32(); - creatureTemplate.RangeAttackTime = fields[22].GetUInt32(); - creatureTemplate.BaseVariance = fields[23].GetFloat(); - creatureTemplate.RangeVariance = fields[24].GetFloat(); - creatureTemplate.unit_class = uint32(fields[25].GetUInt8()); - creatureTemplate.unit_flags = fields[26].GetUInt32(); - creatureTemplate.unit_flags2 = fields[27].GetUInt32(); - creatureTemplate.unit_flags3 = fields[28].GetUInt32(); - creatureTemplate.dynamicflags = fields[29].GetUInt32(); - creatureTemplate.family = CreatureFamily(fields[30].GetInt32()); - creatureTemplate.trainer_class = uint32(fields[31].GetUInt8()); - creatureTemplate.type = uint32(fields[32].GetUInt8()); - creatureTemplate.type_flags = fields[33].GetUInt32(); - creatureTemplate.type_flags2 = fields[34].GetUInt32(); - creatureTemplate.lootid = fields[35].GetUInt32(); - creatureTemplate.pickpocketLootId = fields[36].GetUInt32(); - creatureTemplate.SkinLootId = fields[37].GetUInt32(); + creatureTemplate.KillCredit[i] = fields[1 + i].GetUInt32(); + + creatureTemplate.Name = fields[3].GetString(); + creatureTemplate.FemaleName = fields[4].GetString(); + creatureTemplate.SubName = fields[5].GetString(); + creatureTemplate.TitleAlt = fields[6].GetString(); + creatureTemplate.IconName = fields[7].GetString(); + creatureTemplate.RequiredExpansion = fields[8].GetUInt32(); + creatureTemplate.VignetteID = fields[9].GetUInt32(); + creatureTemplate.faction = fields[10].GetUInt16(); + creatureTemplate.npcflag = fields[11].GetUInt64(); + creatureTemplate.speed_walk = fields[12].GetFloat(); + creatureTemplate.speed_run = fields[13].GetFloat(); + creatureTemplate.scale = fields[14].GetFloat(); + creatureTemplate.rank = uint32(fields[15].GetUInt8()); + creatureTemplate.dmgschool = uint32(fields[16].GetInt8()); + creatureTemplate.BaseAttackTime = fields[17].GetUInt32(); + creatureTemplate.RangeAttackTime = fields[18].GetUInt32(); + creatureTemplate.BaseVariance = fields[19].GetFloat(); + creatureTemplate.RangeVariance = fields[20].GetFloat(); + creatureTemplate.unit_class = uint32(fields[21].GetUInt8()); + creatureTemplate.unit_flags = fields[22].GetUInt32(); + creatureTemplate.unit_flags2 = fields[23].GetUInt32(); + creatureTemplate.unit_flags3 = fields[24].GetUInt32(); + creatureTemplate.dynamicflags = fields[25].GetUInt32(); + creatureTemplate.family = CreatureFamily(fields[26].GetInt32()); + creatureTemplate.trainer_class = uint32(fields[27].GetUInt8()); + creatureTemplate.type = uint32(fields[28].GetUInt8()); for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i) creatureTemplate.resistance[i] = 0; @@ -457,50 +448,41 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields) for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i) creatureTemplate.spells[i] = 0; - creatureTemplate.VehicleId = fields[38].GetUInt32(); - creatureTemplate.mingold = fields[39].GetUInt32(); - creatureTemplate.maxgold = fields[40].GetUInt32(); - creatureTemplate.AIName = fields[41].GetString(); - creatureTemplate.MovementType = uint32(fields[42].GetUInt8()); - if (!fields[43].IsNull()) - creatureTemplate.Movement.Ground = static_cast<CreatureGroundMovementType>(fields[43].GetUInt8()); - - if (!fields[44].IsNull()) - creatureTemplate.Movement.Swim = fields[44].GetBool(); - - if (!fields[45].IsNull()) - creatureTemplate.Movement.Flight = static_cast<CreatureFlightMovementType>(fields[45].GetUInt8()); - - if (!fields[46].IsNull()) - creatureTemplate.Movement.Rooted = fields[46].GetBool(); - - if (!fields[47].IsNull()) - creatureTemplate.Movement.Chase = static_cast<CreatureChaseMovementType>(fields[47].GetUInt8()); - - if (!fields[48].IsNull()) - creatureTemplate.Movement.Random = static_cast<CreatureRandomMovementType>(fields[48].GetUInt8()); - - if (!fields[49].IsNull()) - creatureTemplate.Movement.InteractionPauseTimer = fields[49].GetUInt32(); - - creatureTemplate.ModHealth = fields[50].GetFloat(); - creatureTemplate.ModHealthExtra = fields[51].GetFloat(); - creatureTemplate.ModMana = fields[52].GetFloat(); - creatureTemplate.ModManaExtra = fields[53].GetFloat(); - creatureTemplate.ModArmor = fields[54].GetFloat(); - creatureTemplate.ModDamage = fields[55].GetFloat(); - creatureTemplate.ModExperience = fields[56].GetFloat(); - creatureTemplate.RacialLeader = fields[57].GetBool(); - creatureTemplate.movementId = fields[58].GetUInt32(); - creatureTemplate.CreatureDifficultyID = fields[59].GetInt32(); - creatureTemplate.WidgetSetID = fields[60].GetInt32(); - creatureTemplate.WidgetSetUnitConditionID = fields[61].GetInt32(); - creatureTemplate.RegenHealth = fields[62].GetBool(); - creatureTemplate.MechanicImmuneMask = fields[63].GetUInt64(); - creatureTemplate.SpellSchoolImmuneMask = fields[64].GetUInt32(); - creatureTemplate.flags_extra = fields[65].GetUInt32(); - creatureTemplate.ScriptID = GetScriptId(fields[66].GetString()); - creatureTemplate.StringId = fields[67].GetString(); + creatureTemplate.VehicleId = fields[29].GetUInt32(); + creatureTemplate.AIName = fields[30].GetString(); + creatureTemplate.MovementType = uint32(fields[31].GetUInt8()); + if (!fields[32].IsNull()) + creatureTemplate.Movement.Ground = static_cast<CreatureGroundMovementType>(fields[32].GetUInt8()); + + if (!fields[33].IsNull()) + creatureTemplate.Movement.Swim = fields[33].GetBool(); + + if (!fields[34].IsNull()) + creatureTemplate.Movement.Flight = static_cast<CreatureFlightMovementType>(fields[34].GetUInt8()); + + if (!fields[35].IsNull()) + creatureTemplate.Movement.Rooted = fields[35].GetBool(); + + if (!fields[36].IsNull()) + creatureTemplate.Movement.Chase = static_cast<CreatureChaseMovementType>(fields[36].GetUInt8()); + + if (!fields[37].IsNull()) + creatureTemplate.Movement.Random = static_cast<CreatureRandomMovementType>(fields[37].GetUInt8()); + + if (!fields[38].IsNull()) + creatureTemplate.Movement.InteractionPauseTimer = fields[38].GetUInt32(); + + creatureTemplate.ModExperience = fields[39].GetFloat(); + creatureTemplate.RacialLeader = fields[40].GetBool(); + creatureTemplate.movementId = fields[41].GetUInt32(); + creatureTemplate.WidgetSetID = fields[42].GetInt32(); + creatureTemplate.WidgetSetUnitConditionID = fields[43].GetInt32(); + creatureTemplate.RegenHealth = fields[44].GetBool(); + creatureTemplate.MechanicImmuneMask = fields[45].GetUInt64(); + creatureTemplate.SpellSchoolImmuneMask = fields[46].GetUInt32(); + creatureTemplate.flags_extra = fields[47].GetUInt32(); + creatureTemplate.ScriptID = GetScriptId(fields[48].GetString()); + creatureTemplate.StringId = fields[49].GetString(); } void ObjectMgr::LoadCreatureTemplateGossip() @@ -935,16 +917,16 @@ void ObjectMgr::LoadCreatureTemplateSparring() TC_LOG_INFO("server.loading", ">> Loaded %u creature template sparring rows in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } -void ObjectMgr::LoadCreatureScalingData() +void ObjectMgr::LoadCreatureTemplateDifficulty() { uint32 oldMSTime = getMSTime(); - // 0 1 2 3 4 - QueryResult result = WorldDatabase.Query("SELECT Entry, DifficultyID, LevelScalingDeltaMin, LevelScalingDeltaMax, ContentTuningID FROM creature_template_scaling ORDER BY Entry"); + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + QueryResult result = WorldDatabase.Query("SELECT Entry, DifficultyID, LevelScalingDeltaMin, LevelScalingDeltaMax, ContentTuningID, HealthScalingExpansion, HealthModifier, ManaModifier, ArmorModifier, DamageModifier, CreatureDifficultyID, TypeFlags, TypeFlags2, LootID, PickPocketLootID, SkinLootID, GoldMin, GoldMax FROM creature_template_difficulty ORDER BY Entry"); if (!result) { - TC_LOG_INFO("server.loading", ">> Loaded 0 creature template scaling definitions. DB table `creature_template_scaling` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 creature template difficulty definitions. DB table `creature_template_difficulty` is empty."); return; } @@ -959,34 +941,51 @@ void ObjectMgr::LoadCreatureScalingData() auto itr = _creatureTemplateStore.find(entry); if (itr == _creatureTemplateStore.end()) { - TC_LOG_ERROR("sql.sql", "Creature template (Entry: {}) does not exist but has a record in `creature_template_scaling`", entry); + TC_LOG_ERROR("sql.sql", "Creature template (Entry: {}) does not exist but has a record in `creature_template_difficulty`", entry); continue; } - CreatureLevelScaling creatureLevelScaling; - creatureLevelScaling.DeltaLevelMin = fields[2].GetInt16(); - creatureLevelScaling.DeltaLevelMax = fields[3].GetInt16(); - creatureLevelScaling.ContentTuningID = fields[4].GetInt32(); + CreatureDifficulty creatureDifficulty; + creatureDifficulty.DeltaLevelMin = fields[2].GetInt16(); + creatureDifficulty.DeltaLevelMax = fields[3].GetInt16(); + creatureDifficulty.ContentTuningID = fields[4].GetInt32(); + creatureDifficulty.HealthScalingExpansion = fields[5].GetInt32(); + creatureDifficulty.HealthModifier = fields[6].GetFloat(); + creatureDifficulty.ManaModifier = fields[7].GetFloat(); + creatureDifficulty.ArmorModifier = fields[8].GetFloat(); + creatureDifficulty.DamageModifier = fields[9].GetFloat(); + creatureDifficulty.CreatureDifficultyID = fields[10].GetInt32(); + creatureDifficulty.TypeFlags = fields[11].GetUInt32(); + creatureDifficulty.TypeFlags2 = fields[12].GetUInt32(); + creatureDifficulty.LootID = fields[13].GetUInt32(); + creatureDifficulty.PickPocketLootID = fields[14].GetUInt32(); + creatureDifficulty.SkinLootID = fields[15].GetUInt32(); + creatureDifficulty.GoldMin = fields[16].GetUInt32(); + creatureDifficulty.GoldMax = fields[17].GetUInt32(); - itr->second.scalingStore[difficulty] = creatureLevelScaling; + // TODO: Check if this still applies + creatureDifficulty.DamageModifier *= Creature::_GetDamageMod(itr->second.rank); - // Assign creature level scaling to creature difficulty entry (if any) - // TODO: Drop the use of creature difficulties - int32 difficultyIndex = CreatureTemplate::DifficultyIDToDifficultyEntryIndex(difficulty); - if (difficultyIndex != -1) + if (creatureDifficulty.HealthScalingExpansion < EXPANSION_LEVEL_CURRENT || creatureDifficulty.HealthScalingExpansion >= MAX_EXPANSIONS) { - if (uint32 difficultyEntry = itr->second.DifficultyEntry[difficultyIndex]) - { - auto itr2 = _creatureTemplateStore.find(difficultyEntry); - if (itr2 != _creatureTemplateStore.end()) - itr2->second.scalingStore[difficulty] = creatureLevelScaling; - } + TC_LOG_ERROR("sql.sql", "Table `creature_template_difficulty` lists creature (ID: {}) with invalid `HealthScalingExpansion` {}. Ignored and set to 0.", + entry, creatureDifficulty.HealthScalingExpansion); + creatureDifficulty.HealthScalingExpansion = 0; } + if (creatureDifficulty.GoldMin > creatureDifficulty.GoldMax) + { + TC_LOG_ERROR("sql.sql", "Table `creature_template_difficulty` lists creature (ID: {}) with `GoldMin` {} greater than `GoldMax` {}, setting `GoldMax` to {}.", + entry, creatureDifficulty.GoldMin, creatureDifficulty.GoldMax, creatureDifficulty.GoldMin); + creatureDifficulty.GoldMax = creatureDifficulty.GoldMin; + } + + itr->second.difficultyStore[difficulty] = creatureDifficulty; + ++count; } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded {} creature template scaling data in {} ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded {} creature template difficulty data in {} ms", count, GetMSTimeDiffToNow(oldMSTime)); } void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo) @@ -994,183 +993,6 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo) if (!cInfo) return; - bool ok = true; // bool to allow continue outside this loop - for (uint32 diff = 0; diff < MAX_CREATURE_DIFFICULTIES && ok; ++diff) - { - if (!cInfo->DifficultyEntry[diff]) - continue; - ok = false; // will be set to true at the end of this loop again - - CreatureTemplate const* difficultyInfo = GetCreatureTemplate(cInfo->DifficultyEntry[diff]); - if (!difficultyInfo) - { - TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has `difficulty_entry_{}`={} but creature entry {} does not exist.", - cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff], cInfo->DifficultyEntry[diff]); - continue; - } - - bool ok2 = true; - for (uint32 diff2 = 0; diff2 < MAX_CREATURE_DIFFICULTIES && ok2; ++diff2) - { - ok2 = false; - if (_difficultyEntries[diff2].find(cInfo->Entry) != _difficultyEntries[diff2].end()) - { - TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) is listed as `difficulty_entry_{}` of another creature, but itself lists {} in `difficulty_entry_{}`.", - cInfo->Entry, diff2 + 1, cInfo->DifficultyEntry[diff], diff + 1); - continue; - } - - if (_difficultyEntries[diff2].find(cInfo->DifficultyEntry[diff]) != _difficultyEntries[diff2].end()) - { - TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) already listed as `difficulty_entry_{}` for another entry.", cInfo->DifficultyEntry[diff], diff2 + 1); - continue; - } - - if (_hasDifficultyEntries[diff2].find(cInfo->DifficultyEntry[diff]) != _hasDifficultyEntries[diff2].end()) - { - TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has `difficulty_entry_{}`={} but creature entry {} has itself a value in `difficulty_entry_{}`.", - cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff], cInfo->DifficultyEntry[diff], diff2 + 1); - continue; - } - ok2 = true; - } - - if (!ok2) - continue; - - if (cInfo->HealthScalingExpansion > difficultyInfo->HealthScalingExpansion) - { - TC_LOG_ERROR("sql.sql", "Creature (ID: {}, Expansion: {}) has different `HealthScalingExpansion` in difficulty {} mode (ID: {}, Expansion: {}).", - cInfo->Entry, cInfo->HealthScalingExpansion, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->HealthScalingExpansion); - } - - if (cInfo->faction != difficultyInfo->faction) - { - TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, faction: {}) has different `faction` in difficulty {} mode (Entry: {}, faction: {}).", - cInfo->Entry, cInfo->faction, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->faction); - TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `faction`={} WHERE `entry`={};", - cInfo->faction, cInfo->DifficultyEntry[diff]); - } - - if (cInfo->unit_class != difficultyInfo->unit_class) - { - TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, class: {}) has different `unit_class` in difficulty {} mode (Entry: {}, class: {}).", - cInfo->Entry, cInfo->unit_class, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->unit_class); - TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `unit_class`={} WHERE `entry`={};", - cInfo->unit_class, cInfo->DifficultyEntry[diff]); - continue; - } - - uint32 differenceMask = cInfo->npcflag ^ difficultyInfo->npcflag; - if (cInfo->npcflag != difficultyInfo->npcflag) - { - TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, `npcflag`: {}) has different `npcflag` in difficulty {} mode (Entry: {}, `npcflag`: {}).", - cInfo->Entry, cInfo->npcflag, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->npcflag); - TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `npcflag`=`npcflag`^{} WHERE `entry`={};", - differenceMask, cInfo->DifficultyEntry[diff]); - continue; - } - - if (cInfo->dmgschool != difficultyInfo->dmgschool) - { - TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, `dmgschool`: {}) has different `dmgschool` in difficulty {} mode (Entry: {}, `dmgschool`: {}).", - cInfo->Entry, cInfo->dmgschool, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->dmgschool); - TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `dmgschool`={} WHERE `entry`={};", - cInfo->dmgschool, cInfo->DifficultyEntry[diff]); - } - - differenceMask = cInfo->unit_flags2 ^ difficultyInfo->unit_flags2; - if (cInfo->unit_flags2 != difficultyInfo->unit_flags2) - { - TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, `unit_flags2`: {}) has different `unit_flags2` in difficulty {} mode (Entry: {}, `unit_flags2`: {}).", - cInfo->Entry, cInfo->unit_flags2, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->unit_flags2); - TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `unit_flags2`=`unit_flags2`^{} WHERE `entry`={};", - differenceMask, cInfo->DifficultyEntry[diff]); - } - - if (cInfo->family != difficultyInfo->family) - { - TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, family: {}) has different `family` in difficulty {} mode (Entry: {}, family: {}).", - cInfo->Entry, cInfo->family, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->family); - TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `family`={} WHERE `entry`={};", - cInfo->family, cInfo->DifficultyEntry[diff]); - } - - if (cInfo->trainer_class != difficultyInfo->trainer_class) - { - TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, trainer_class: {}) has different `trainer_class` in difficulty {} mode (Entry: {}, trainer_class: {}).", - cInfo->Entry, cInfo->trainer_class, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->trainer_class); - TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `trainer_class`={} WHERE `entry`={};", - cInfo->trainer_class, cInfo->DifficultyEntry[diff]); - continue; - } - - if (cInfo->type != difficultyInfo->type) - { - TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, type: {}) has different `type` in difficulty {} mode (Entry: {}, type: {}).", - cInfo->Entry, cInfo->type, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->type); - TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `type`={} WHERE `entry`={};", - cInfo->type, cInfo->DifficultyEntry[diff]); - } - - if (!cInfo->VehicleId && difficultyInfo->VehicleId) - { - TC_LOG_ERROR("sql.sql", "Non-vehicle Creature (Entry: {}, VehicleId: {}) has `VehicleId` set in difficulty {} mode (Entry: {}, VehicleId: {}).", - cInfo->Entry, cInfo->VehicleId, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->VehicleId); - } - - if (cInfo->RegenHealth != difficultyInfo->RegenHealth) - { - TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, RegenHealth: {}) has different `RegenHealth` in difficulty {} mode (Entry: {}, RegenHealth: {}).", - cInfo->Entry, cInfo->RegenHealth, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->RegenHealth); - TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `RegenHealth`={} WHERE `entry`={};", - cInfo->RegenHealth, cInfo->DifficultyEntry[diff]); - } - - differenceMask = cInfo->MechanicImmuneMask & (~difficultyInfo->MechanicImmuneMask); - if (differenceMask) - { - TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, mechanic_immune_mask: {}) has weaker immunities in difficulty {} mode (Entry: {}, mechanic_immune_mask: {}).", - cInfo->Entry, cInfo->MechanicImmuneMask, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->MechanicImmuneMask); - TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `mechanic_immune_mask`=`mechanic_immune_mask`|{} WHERE `entry`={};", - differenceMask, cInfo->DifficultyEntry[diff]); - } - - differenceMask = (cInfo->flags_extra ^ difficultyInfo->flags_extra) & (~CREATURE_FLAG_EXTRA_INSTANCE_BIND); - if (differenceMask) - { - TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, flags_extra: {}) has different `flags_extra` in difficulty {} mode (Entry: {}, flags_extra: {}).", - cInfo->Entry, cInfo->flags_extra, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->flags_extra); - TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `flags_extra`=`flags_extra`^{} WHERE `entry`={};", - differenceMask, cInfo->DifficultyEntry[diff]); - } - - if (!difficultyInfo->AIName.empty()) - { - TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) lists difficulty {} mode entry {} with `AIName` filled in. `AIName` of difficulty 0 mode creature is always used instead.", - cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff]); - continue; - } - - if (difficultyInfo->ScriptID) - { - TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) lists difficulty {} mode entry {} with `ScriptName` filled in. `ScriptName` of difficulty 0 mode creature is always used instead.", - cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff]); - continue; - } - - _hasDifficultyEntries[diff].insert(cInfo->Entry); - _difficultyEntries[diff].insert(cInfo->DifficultyEntry[diff]); - ok = true; - } - - if (cInfo->mingold > cInfo->maxgold) - { - TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has `mingold` {} which is greater than `maxgold` {}, setting `maxgold` to {}.", - cInfo->Entry, cInfo->mingold, cInfo->maxgold, cInfo->mingold); - const_cast<CreatureTemplate*>(cInfo)->maxgold = cInfo->mingold; - } - if (!cInfo->AIName.empty()) { auto registryItem = sCreatureAIRegistry->GetRegistryItem(cInfo->AIName); @@ -1281,12 +1103,6 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo) const_cast<CreatureTemplate*>(cInfo)->MovementType = IDLE_MOTION_TYPE; } - if (cInfo->HealthScalingExpansion < EXPANSION_LEVEL_CURRENT || cInfo->HealthScalingExpansion >= MAX_EXPANSIONS) - { - TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (ID: {}) with invalid `HealthScalingExpansion` {}. Ignored and set to 0.", cInfo->Entry, cInfo->HealthScalingExpansion); - const_cast<CreatureTemplate*>(cInfo)->HealthScalingExpansion = 0; - } - if (cInfo->RequiredExpansion >= MAX_EXPANSIONS) { TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: {}) with `RequiredExpansion` {}. Ignored and set to 0.", cInfo->Entry, cInfo->RequiredExpansion); @@ -1323,8 +1139,6 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo) const_cast<CreatureTemplate*>(cInfo)->dynamicflags = 0; } - const_cast<CreatureTemplate*>(cInfo)->ModDamage *= Creature::_GetDamageMod(cInfo->rank); - if (!cInfo->GossipMenuIds.empty() && !(cInfo->npcflag & UNIT_NPC_FLAG_GOSSIP)) TC_LOG_INFO("sql.sql", "Creature (Entry: {}) has assigned gossip menu, but npcflag does not include UNIT_NPC_FLAG_GOSSIP.", cInfo->Entry); else if (cInfo->GossipMenuIds.empty() && cInfo->npcflag & UNIT_NPC_FLAG_GOSSIP) @@ -2412,19 +2226,6 @@ void ObjectMgr::LoadCreatures() continue; } - bool ok = true; - for (uint32 diff = 0; diff < MAX_CREATURE_DIFFICULTIES && ok; ++diff) - { - if (_difficultyEntries[diff].find(data.id) != _difficultyEntries[diff].end()) - { - TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {}) that is listed as difficulty {} template (entry: {}) in `creature_template`, skipped.", - guid, diff + 1, data.id); - ok = false; - } - } - if (!ok) - continue; - // -1 random, 0 no equipment if (data.equipmentId != 0) { @@ -4635,6 +4436,20 @@ void ObjectMgr::BuildPlayerLevelInfo(uint8 race, uint8 _class, uint8 level, Play } } +std::vector<uint32> const* ObjectMgr::GetCreatureQuestItemList(uint32 creatureEntry, Difficulty difficulty) const +{ + CreatureQuestItemMap::const_iterator itr = _creatureQuestItemStore.find(std::make_pair(creatureEntry, difficulty)); + if (itr != _creatureQuestItemStore.end()) + return &itr->second; + + // If there is no data for the difficulty, try to get data for the fallback difficulty + DifficultyEntry const* difficultyEntry = sDifficultyStore.LookupEntry(difficulty); + if (difficultyEntry) + return GetCreatureQuestItemList(creatureEntry, Difficulty(difficultyEntry->FallbackDifficultyID)); + + return nullptr; +} + void ObjectMgr::LoadQuests() { uint32 oldMSTime = getMSTime(); @@ -6442,14 +6257,6 @@ void ObjectMgr::LoadInstanceEncounters() continue; } const_cast<CreatureTemplate*>(creatureInfo)->flags_extra |= CREATURE_FLAG_EXTRA_DUNGEON_BOSS; - for (uint8 diff = 0; diff < MAX_CREATURE_DIFFICULTIES; ++diff) - { - if (uint32 diffEntry = creatureInfo->DifficultyEntry[diff]) - { - if (CreatureTemplate const* diffInfo = GetCreatureTemplate(diffEntry)) - const_cast<CreatureTemplate*>(diffInfo)->flags_extra |= CREATURE_FLAG_EXTRA_DUNGEON_BOSS; - } - } break; } case ENCOUNTER_CREDIT_CAST_SPELL: @@ -10993,8 +10800,8 @@ void ObjectMgr::LoadCreatureQuestItems() { uint32 oldMSTime = getMSTime(); - // 0 1 2 - QueryResult result = WorldDatabase.Query("SELECT CreatureEntry, ItemId, Idx FROM creature_questitem ORDER BY Idx ASC"); + // 0 1 2 3 + QueryResult result = WorldDatabase.Query("SELECT CreatureEntry, DifficultyID, ItemId, Idx FROM creature_questitem ORDER BY Idx ASC"); if (!result) { @@ -11008,24 +10815,25 @@ void ObjectMgr::LoadCreatureQuestItems() Field* fields = result->Fetch(); uint32 entry = fields[0].GetUInt32(); - uint32 item = fields[1].GetUInt32(); - uint32 idx = fields[2].GetUInt32(); + Difficulty difficulty = Difficulty(fields[1].GetUInt8()); + uint32 item = fields[2].GetUInt32(); + uint32 idx = fields[3].GetUInt32(); CreatureTemplate const* creatureInfo = GetCreatureTemplate(entry); if (!creatureInfo) { - TC_LOG_ERROR("sql.sql", "Table `creature_questitem` has data for nonexistent creature (entry: {}, idx: {}), skipped", entry, idx); + TC_LOG_ERROR("sql.sql", "Table `creature_questitem` has data for nonexistent creature (entry: {}, difficulty: {}, idx: {}), skipped", entry, difficulty, idx); continue; }; ItemEntry const* db2Data = sItemStore.LookupEntry(item); if (!db2Data) { - TC_LOG_ERROR("sql.sql", "Table `creature_questitem` has nonexistent item (ID: {}) in creature (entry: {}, idx: {}), skipped", item, entry, idx); + TC_LOG_ERROR("sql.sql", "Table `creature_questitem` has nonexistent item (ID: {}) in creature (entry: {}, difficulty: {}, idx: {}), skipped", item, entry, difficulty, idx); continue; }; - _creatureQuestItemStore[entry].push_back(item); + _creatureQuestItemStore[std::make_pair(entry, difficulty)].push_back(item); ++count; } diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index c0a2f5b228c..939f5bcc217 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -502,7 +502,7 @@ typedef std::unordered_map<uint16, CreatureBaseStats> CreatureBaseStatsContainer typedef std::unordered_map<uint8, EquipmentInfo> EquipmentInfoContainerInternal; typedef std::unordered_map<uint32, EquipmentInfoContainerInternal> EquipmentInfoContainer; typedef std::unordered_map<uint32, CreatureModelInfo> CreatureModelContainer; -typedef std::unordered_map<uint32, std::vector<uint32>> CreatureQuestItemMap; +typedef std::unordered_map<std::pair<uint32, Difficulty>, std::vector<uint32>> CreatureQuestItemMap; typedef std::unordered_map<uint32, GameObjectTemplate> GameObjectTemplateContainer; typedef std::unordered_map<uint32, GameObjectTemplateAddon> GameObjectTemplateAddonContainer; typedef std::unordered_map<ObjectGuid::LowType, GameObjectOverride> GameObjectOverrideContainer; @@ -1181,13 +1181,7 @@ class TC_GAME_API ObjectMgr } GameObjectQuestItemMap const* GetGameObjectQuestItemMap() const { return &_gameObjectQuestItemStore; } - std::vector<uint32> const* GetCreatureQuestItemList(uint32 id) const - { - CreatureQuestItemMap::const_iterator itr = _creatureQuestItemStore.find(id); - if (itr != _creatureQuestItemStore.end()) - return &itr->second; - return nullptr; - } + std::vector<uint32> const* GetCreatureQuestItemList(uint32 creatureEntry, Difficulty difficulty) const; CreatureQuestItemMap const* GetCreatureQuestItemMap() const { return &_creatureQuestItemStore; } uint32 GetNearestTaxiNode(float x, float y, float z, uint32 mapid, uint32 team); @@ -1326,12 +1320,12 @@ class TC_GAME_API ObjectMgr void LoadCreatureTemplateAddons(); void LoadCreatureTemplateSparring(); void LoadCreatureTemplate(Field* fields); + void LoadCreatureTemplateDifficulty(); void LoadCreatureTemplateGossip(); void LoadCreatureTemplateResistances(); void LoadCreatureTemplateSpells(); void LoadCreatureTemplateModels(); void LoadCreatureSummonedData(); - void LoadCreatureScalingData(); void CheckCreatureTemplate(CreatureTemplate const* cInfo); void CheckCreatureMovement(char const* table, uint64 id, CreatureMovementData& creatureMovement); void LoadGameObjectQuestItems(); @@ -1953,9 +1947,6 @@ class TC_GAME_API ObjectMgr std::unordered_map<uint32, Trainer::Trainer> _trainers; std::map<std::tuple<uint32, uint32, uint32>, uint32> _creatureDefaultTrainers; - std::set<uint32> _difficultyEntries[MAX_CREATURE_DIFFICULTIES]; // already loaded difficulty 1 value in creatures, used in CheckCreatureTemplate - std::set<uint32> _hasDifficultyEntries[MAX_CREATURE_DIFFICULTIES]; // already loaded creatures with difficulty 1 values, used in CheckCreatureTemplate - std::unordered_map<uint8, RaceUnlockRequirement> _raceUnlockRequirementStore; std::vector<RaceClassAvailability> _classExpansionRequirementStore; RealmNameContainer _realmNameStore; diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp index c8a94c3f206..372816cc758 100644 --- a/src/server/game/Handlers/NPCHandler.cpp +++ b/src/server/game/Handlers/NPCHandler.cpp @@ -481,7 +481,7 @@ void WorldSession::HandleSetPetSlot(WorldPackets::NPC::SetPetSlot& setPetSlot) if (dstPet) { CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(dstPet->CreatureId); - if (!creatureInfo || !creatureInfo->IsTameable(_player->CanTameExoticPets())) + if (!creatureInfo || !creatureInfo->IsTameable(_player->CanTameExoticPets(), creatureInfo->GetDifficulty(DIFFICULTY_NONE))) { SendPetStableResult(StableResult::CantControlExotic); return; @@ -507,7 +507,7 @@ void WorldSession::HandleSetPetSlot(WorldPackets::NPC::SetPetSlot& setPetSlot) } CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(srcPet->CreatureId); - if (!creatureInfo || !creatureInfo->IsTameable(_player->CanTameExoticPets())) + if (!creatureInfo || !creatureInfo->IsTameable(_player->CanTameExoticPets(), creatureInfo->GetDifficulty(DIFFICULTY_NONE))) { SendPetStableResult(StableResult::CantControlExotic); return; diff --git a/src/server/game/Handlers/QueryHandler.cpp b/src/server/game/Handlers/QueryHandler.cpp index 1dddefe38b7..74548ba34e2 100644 --- a/src/server/game/Handlers/QueryHandler.cpp +++ b/src/server/game/Handlers/QueryHandler.cpp @@ -23,6 +23,7 @@ #include "GameTime.h" #include "Item.h" #include "Log.h" +#include "Map.h" #include "NPCHandler.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" @@ -72,11 +73,15 @@ void WorldSession::HandleCreatureQuery(WorldPackets::Query::QueryCreature& packe if (CreatureTemplate const* ci = sObjectMgr->GetCreatureTemplate(packet.CreatureID)) { TC_LOG_DEBUG("network", "WORLD: CMSG_QUERY_CREATURE '{}' - Entry: {}.", ci->Name, packet.CreatureID); - if (sWorld->getBoolConfig(CONFIG_CACHE_DATA_QUERIES)) + + Difficulty difficulty = _player->GetMap()->GetDifficultyID(); + + // Cache only exists for difficulty base + if (sWorld->getBoolConfig(CONFIG_CACHE_DATA_QUERIES) && difficulty == DIFFICULTY_NONE) SendPacket(&ci->QueryData[static_cast<uint32>(GetSessionDbLocaleIndex())]); else { - WorldPacket response = ci->BuildQueryData(GetSessionDbLocaleIndex()); + WorldPacket response = ci->BuildQueryData(GetSessionDbLocaleIndex(), difficulty); SendPacket(&response); } TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUERY_CREATURE_RESPONSE"); diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp index 9475a5c906b..00959ce3a0b 100644 --- a/src/server/game/Loot/LootMgr.cpp +++ b/src/server/game/Loot/LootMgr.cpp @@ -993,12 +993,15 @@ void LoadLootTemplates_Creature() CreatureTemplateContainer const& ctc = sObjectMgr->GetCreatureTemplates(); for (auto const& creatureTemplatePair : ctc) { - if (uint32 lootid = creatureTemplatePair.second.lootid) + for (auto const& [difficulty, creatureDifficulty] : creatureTemplatePair.second.difficultyStore) { - if (!lootIdSet.count(lootid)) - LootTemplates_Creature.ReportNonExistingId(lootid, "Creature", creatureTemplatePair.first); - else - lootIdSetUsed.insert(lootid); + if (uint32 lootid = creatureDifficulty.LootID) + { + if (!lootIdSet.count(lootid)) + LootTemplates_Creature.ReportNonExistingId(lootid, "Creature", creatureTemplatePair.first); + else + lootIdSetUsed.insert(lootid); + } } } @@ -1182,12 +1185,15 @@ void LoadLootTemplates_Pickpocketing() CreatureTemplateContainer const& ctc = sObjectMgr->GetCreatureTemplates(); for (auto const& creatureTemplatePair : ctc) { - if (uint32 lootid = creatureTemplatePair.second.pickpocketLootId) + for (auto const& [difficulty, creatureDifficulty] : creatureTemplatePair.second.difficultyStore) { - if (!lootIdSet.count(lootid)) - LootTemplates_Pickpocketing.ReportNonExistingId(lootid, "Creature", creatureTemplatePair.first); - else - lootIdSetUsed.insert(lootid); + if (uint32 lootid = creatureDifficulty.PickPocketLootID) + { + if (!lootIdSet.count(lootid)) + LootTemplates_Pickpocketing.ReportNonExistingId(lootid, "Creature", creatureTemplatePair.first); + else + lootIdSetUsed.insert(lootid); + } } } @@ -1269,12 +1275,15 @@ void LoadLootTemplates_Skinning() CreatureTemplateContainer const& ctc = sObjectMgr->GetCreatureTemplates(); for (auto const& creatureTemplatePair : ctc) { - if (uint32 lootid = creatureTemplatePair.second.SkinLootId) + for (auto const& [difficulty, creatureDifficulty] : creatureTemplatePair.second.difficultyStore) { - if (!lootIdSet.count(lootid)) - LootTemplates_Skinning.ReportNonExistingId(lootid, "Creature", creatureTemplatePair.first); - else - lootIdSetUsed.insert(lootid); + if (uint32 lootid = creatureDifficulty.SkinLootID) + { + if (!lootIdSet.count(lootid)) + LootTemplates_Skinning.ReportNonExistingId(lootid, "Creature", creatureTemplatePair.first); + else + lootIdSetUsed.insert(lootid); + } } } diff --git a/src/server/game/Miscellaneous/Formulas.h b/src/server/game/Miscellaneous/Formulas.h index 77f0c64f93a..6630602b026 100644 --- a/src/server/game/Miscellaneous/Formulas.h +++ b/src/server/game/Miscellaneous/Formulas.h @@ -188,7 +188,7 @@ namespace Trinity if (gain && creature) { // Players get only 10% xp for killing creatures of lower expansion levels than himself - if ((uint32(creature->GetCreatureTemplate()->GetHealthScalingExpansion()) < GetExpansionForLevel(player->GetLevel()))) + if ((uint32(creature->GetCreatureDifficulty()->GetHealthScalingExpansion()) < GetExpansionForLevel(player->GetLevel()))) gain = uint32(round(gain / 10.0f)); if (creature->isElite()) diff --git a/src/server/game/Server/Packets/CombatLogPacketsCommon.cpp b/src/server/game/Server/Packets/CombatLogPacketsCommon.cpp index acaeacb9b9f..962fe622201 100644 --- a/src/server/game/Server/Packets/CombatLogPacketsCommon.cpp +++ b/src/server/game/Server/Packets/CombatLogPacketsCommon.cpp @@ -68,7 +68,7 @@ template<> bool ContentTuningParams::GenerateDataForUnits<Creature, Player>(Creature* attacker, Player* target) { CreatureTemplate const* creatureTemplate = attacker->GetCreatureTemplate(); - CreatureLevelScaling const* creatureScaling = creatureTemplate->GetLevelScaling(attacker->GetMap()->GetDifficultyID()); + CreatureDifficulty const* creatureDifficulty = creatureTemplate->GetDifficulty(attacker->GetMap()->GetDifficultyID()); Type = TYPE_CREATURE_TO_PLAYER_DAMAGE; PlayerLevelDelta = target->m_activePlayerData->ScalingPlayerLevelDelta; @@ -76,9 +76,9 @@ bool ContentTuningParams::GenerateDataForUnits<Creature, Player>(Creature* attac TargetItemLevel = 0; ScalingHealthItemLevelCurveID = target->m_unitData->ScalingHealthItemLevelCurveID; TargetLevel = target->GetLevel(); - Expansion = creatureTemplate->HealthScalingExpansion; + Expansion = creatureDifficulty->HealthScalingExpansion; TargetScalingLevelDelta = int8(attacker->m_unitData->ScalingLevelDelta); - TargetContentTuningID = creatureScaling->ContentTuningID; + TargetContentTuningID = creatureDifficulty->ContentTuningID; return true; } @@ -86,7 +86,7 @@ template<> bool ContentTuningParams::GenerateDataForUnits<Player, Creature>(Player* attacker, Creature* target) { CreatureTemplate const* creatureTemplate = target->GetCreatureTemplate(); - CreatureLevelScaling const* creatureScaling = creatureTemplate->GetLevelScaling(target->GetMap()->GetDifficultyID()); + CreatureDifficulty const* creatureDifficulty = creatureTemplate->GetDifficulty(target->GetMap()->GetDifficultyID()); Type = TYPE_PLAYER_TO_CREATURE_DAMAGE; PlayerLevelDelta = attacker->m_activePlayerData->ScalingPlayerLevelDelta; @@ -94,9 +94,9 @@ bool ContentTuningParams::GenerateDataForUnits<Player, Creature>(Player* attacke TargetItemLevel = 0; ScalingHealthItemLevelCurveID = target->m_unitData->ScalingHealthItemLevelCurveID; TargetLevel = target->GetLevel(); - Expansion = creatureTemplate->HealthScalingExpansion; + Expansion = creatureDifficulty->HealthScalingExpansion; TargetScalingLevelDelta = int8(target->m_unitData->ScalingLevelDelta); - TargetContentTuningID = creatureScaling->ContentTuningID; + TargetContentTuningID = creatureDifficulty->ContentTuningID; return true; } @@ -105,15 +105,15 @@ bool ContentTuningParams::GenerateDataForUnits<Creature, Creature>(Creature* att { Creature* accessor = target->HasScalableLevels() ? target : attacker; CreatureTemplate const* creatureTemplate = accessor->GetCreatureTemplate(); - CreatureLevelScaling const* creatureScaling = creatureTemplate->GetLevelScaling(accessor->GetMap()->GetDifficultyID()); + CreatureDifficulty const* creatureDifficulty = creatureTemplate->GetDifficulty(accessor->GetMap()->GetDifficultyID()); Type = TYPE_CREATURE_TO_CREATURE_DAMAGE; PlayerLevelDelta = 0; PlayerItemLevel = 0; TargetLevel = target->GetLevel(); - Expansion = creatureTemplate->HealthScalingExpansion; + Expansion = creatureDifficulty->HealthScalingExpansion; TargetScalingLevelDelta = int8(accessor->m_unitData->ScalingLevelDelta); - TargetContentTuningID = creatureScaling->ContentTuningID; + TargetContentTuningID = creatureDifficulty->ContentTuningID; return true; } diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 2b58cdca83f..e46565887ac 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -1875,7 +1875,7 @@ void Spell::SelectImplicitTrajTargets(SpellEffectInfo const& spellEffectInfo, Sp if (Creature* creatureTarget = unit->ToCreature()) { - if (!(creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_COLLIDE_WITH_MISSILES)) + if (!(creatureTarget->GetCreatureDifficulty()->TypeFlags & CREATURE_TYPE_FLAG_COLLIDE_WITH_MISSILES)) continue; } } @@ -6252,10 +6252,11 @@ SpellCastResult Spell::CheckCast(bool strict, int32* param1 /*= nullptr*/, int32 if (info.first->Type == HUNTER_PET) { CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(info.first->CreatureId); - if (!creatureInfo || !creatureInfo->IsTameable(playerCaster->CanTameExoticPets())) + CreatureDifficulty const* creatureDifficulty = creatureInfo->GetDifficulty(DIFFICULTY_NONE); + if (!creatureInfo || !creatureInfo->IsTameable(playerCaster->CanTameExoticPets(), creatureDifficulty)) { // if problem in exotic pet - if (creatureInfo && creatureInfo->IsTameable(true)) + if (creatureInfo && creatureInfo->IsTameable(true, creatureDifficulty)) playerCaster->SendTameFailure(PetTameResult::CantControlExotic); else playerCaster->SendTameFailure(PetTameResult::NoPetAvailable); diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index d90f7cd7342..1e548610341 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -2177,7 +2177,7 @@ void Spell::EffectPickPocket() creature->StartPickPocketRefillTimer(); creature->m_loot.reset(new Loot(creature->GetMap(), creature->GetGUID(), LOOT_PICKPOCKETING, nullptr)); - if (uint32 lootid = creature->GetCreatureTemplate()->pickpocketLootId) + if (uint32 lootid = creature->GetCreatureDifficulty()->PickPocketLootID) creature->m_loot->FillLoot(lootid, LootTemplates_Pickpocketing, player, true); // Generate extra money for pick pocket loot @@ -3725,13 +3725,13 @@ void Spell::EffectSkinning() Creature* creature = unitTarget->ToCreature(); int32 targetLevel = creature->GetLevelForTarget(m_caster); - uint32 skill = creature->GetCreatureTemplate()->GetRequiredLootSkill(); + uint32 skill = creature->GetCreatureDifficulty()->GetRequiredLootSkill(); creature->SetUnitFlag3(UNIT_FLAG3_ALREADY_SKINNED); creature->SetDynamicFlag(UNIT_DYNFLAG_LOOTABLE); Loot* loot = new Loot(creature->GetMap(), creature->GetGUID(), LOOT_SKINNING, nullptr); creature->m_personalLoot[player->GetGUID()].reset(loot); - loot->FillLoot(creature->GetCreatureTemplate()->SkinLootId, LootTemplates_Skinning, player, true); + loot->FillLoot(creature->GetCreatureDifficulty()->SkinLootID, LootTemplates_Skinning, player, true); player->SendLoot(*loot); if (!IsPartOfSkillLine(skill, m_spellInfo->Id)) diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index cab8349095b..f835b85de2e 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -2184,7 +2184,7 @@ SpellCastResult SpellInfo::CheckTarget(WorldObject const* caster, WorldObject co if (!targetCreature) return SPELL_FAILED_BAD_TARGETS; - if (!targetCreature->CanHaveLoot() || !LootTemplates_Pickpocketing.HaveLootFor(targetCreature->GetCreatureTemplate()->pickpocketLootId)) + if (!targetCreature->CanHaveLoot() || !LootTemplates_Pickpocketing.HaveLootFor(targetCreature->GetCreatureDifficulty()->PickPocketLootID)) return SPELL_FAILED_TARGET_NO_POCKETS; } diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 3bf7c0a6c58..67d8cc0d280 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1981,12 +1981,12 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Creature template addons..."); sObjectMgr->LoadCreatureTemplateAddons(); + TC_LOG_INFO("server.loading", "Loading Creature template difficulty..."); + sObjectMgr->LoadCreatureTemplateDifficulty(); + TC_LOG_INFO("server.loading", "Loading Creature template sparring..."); sObjectMgr->LoadCreatureTemplateSparring(); - TC_LOG_INFO("server.loading", "Loading Creature template scaling..."); - sObjectMgr->LoadCreatureScalingData(); - TC_LOG_INFO("server.loading", "Loading Reputation Reward Rates..."); sObjectMgr->LoadReputationRewardRate(); diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index 75e622d8d88..96de137197b 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -523,7 +523,10 @@ public: handler->PSendSysMessage(LANG_NPCINFO_DYNAMIC_FLAGS, target->GetDynamicFlags()); handler->PSendSysMessage(LANG_COMMAND_RAWPAWNTIMES, defRespawnDelayStr.c_str(), curRespawnDelayStr.c_str()); - handler->PSendSysMessage(LANG_NPCINFO_LOOT, cInfo->lootid, cInfo->pickpocketLootId, cInfo->SkinLootId); + + CreatureDifficulty const* creatureDifficulty = target->GetCreatureDifficulty(); + handler->PSendSysMessage(LANG_NPCINFO_LOOT, creatureDifficulty->LootID, creatureDifficulty->PickPocketLootID, creatureDifficulty->SkinLootID); + handler->PSendSysMessage(LANG_NPCINFO_DUNGEON_ID, target->GetInstanceId()); if (CreatureData const* data = sObjectMgr->GetCreatureData(target->GetSpawnId())) @@ -1092,7 +1095,7 @@ public: CreatureTemplate const* cInfo = creatureTarget->GetCreatureTemplate(); - if (!cInfo->IsTameable (player->CanTameExoticPets())) + if (!cInfo->IsTameable (player->CanTameExoticPets(), creatureTarget->GetCreatureDifficulty())) { handler->PSendSysMessage (LANG_CREATURE_NON_TAMEABLE, cInfo->Entry); handler->SetSentErrorMessage (true); diff --git a/src/server/scripts/Northrend/Gundrak/boss_drakkari_colossus.cpp b/src/server/scripts/Northrend/Gundrak/boss_drakkari_colossus.cpp index 17c6645df02..12b9ac830dc 100644 --- a/src/server/scripts/Northrend/Gundrak/boss_drakkari_colossus.cpp +++ b/src/server/scripts/Northrend/Gundrak/boss_drakkari_colossus.cpp @@ -45,8 +45,12 @@ enum Spells SPELL_FREEZE_ANIM = 16245, SPELL_MOJO_PUDDLE = 55627, SPELL_MOJO_WAVE = 55626, + SPELL_MORTAL_STRIKES_NORMAL = 54715, + SPELL_MORTAL_STRIKES_HEROIC = 59454 }; +#define SPELL_MORTAL_STRIKES DUNGEON_MODE<uint32>(SPELL_MORTAL_STRIKES_NORMAL, SPELL_MORTAL_STRIKES_HEROIC) + enum ColossusEvents { EVENT_MIGHTY_BLOW = 1, @@ -110,6 +114,8 @@ struct boss_drakkari_colossus : public BossAI events.ScheduleEvent(EVENT_MIGHTY_BLOW, 10s, 30s); Initialize(); + + DoCastSelf(SPELL_MORTAL_STRIKES, true); } void JustEngagedWith(Unit* who) override diff --git a/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp b/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp index 68e8279c164..0d2cd019ef4 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp @@ -76,6 +76,7 @@ enum Spells SPELL_TELEPORT_LIVE = 28026 }; +#define SPELL_UNHOLY_AURA RAID_MODE<uint32>(55606, 55608) #define SPELL_DEATH_PLAGUE RAID_MODE<uint32>(55604, 55645) #define SPELL_SHADOW_BOLT_VOLLEY RAID_MODE<uint32>(27831, 55638) #define SPELL_ARCANE_EXPLOSION RAID_MODE<uint32>(27989, 56407) @@ -663,7 +664,19 @@ struct npc_gothik_minion_livingrider : public npc_gothik_minion_baseAI { npc_gothik_minion_livingrider(Creature* creature) : npc_gothik_minion_baseAI(creature, SPELL_ANCHOR_1_RIDER), _boltVolleyTimer(urandms(5,10)) { } - void _UpdateAI(uint32 diff) + void JustAppeared() override + { + npc_gothik_minion_baseAI::JustAppeared(); + DoCastSelf(SPELL_UNHOLY_AURA, true); + } + + void JustReachedHome() override + { + npc_gothik_minion_baseAI::JustReachedHome(); + DoCastSelf(SPELL_UNHOLY_AURA, true); + } + + void _UpdateAI(uint32 diff) override { if (diff < _boltVolleyTimer) _boltVolleyTimer -= diff; @@ -674,6 +687,8 @@ struct npc_gothik_minion_livingrider : public npc_gothik_minion_baseAI } if (!me->HasUnitState(UNIT_STATE_CASTING)) DoMeleeAttackIfReady(); + + npc_gothik_minion_baseAI::_UpdateAI(diff); } uint32 _boltVolleyTimer; }; @@ -718,7 +733,19 @@ struct npc_gothik_minion_spectralrider : public npc_gothik_minion_baseAI { npc_gothik_minion_spectralrider(Creature* creature) : npc_gothik_minion_baseAI(creature), _frenzyTimer(urandms(2,5)), _drainTimer(urandms(8,12)) { } - void _UpdateAI(uint32 diff) + void JustAppeared() override + { + npc_gothik_minion_baseAI::JustAppeared(); + DoCastSelf(SPELL_UNHOLY_AURA, true); + } + + void JustReachedHome() override + { + npc_gothik_minion_baseAI::JustReachedHome(); + DoCastSelf(SPELL_UNHOLY_AURA, true); + } + + void _UpdateAI(uint32 diff) override { if (diff < _frenzyTimer) _frenzyTimer -= diff; @@ -764,6 +791,8 @@ struct npc_gothik_minion_spectralrider : public npc_gothik_minion_baseAI if (!me->HasUnitState(UNIT_STATE_CASTING)) DoMeleeAttackIfReady(); + + npc_gothik_minion_baseAI::_UpdateAI(diff); } uint32 _frenzyTimer, _drainTimer; }; diff --git a/src/server/scripts/Outland/TempestKeep/arcatraz/boss_wrath_scryer_soccothrates.cpp b/src/server/scripts/Outland/TempestKeep/arcatraz/boss_wrath_scryer_soccothrates.cpp index 0af123ae289..ab0c7473639 100644 --- a/src/server/scripts/Outland/TempestKeep/arcatraz/boss_wrath_scryer_soccothrates.cpp +++ b/src/server/scripts/Outland/TempestKeep/arcatraz/boss_wrath_scryer_soccothrates.cpp @@ -56,9 +56,13 @@ enum Spells SPELL_KNOCK_AWAY = 36512, SPELL_FELFIRE_LINE_UP = 35770, SPELL_CHARGE_TARGETING = 36038, - SPELL_CHARGE = 35754 + SPELL_CHARGE = 35754, + SPELL_FEL_IMMOLATION_NORMAL = 36051, + SPELL_FEL_IMMOLATION_HEROIC = 39007 }; +#define SPELL_FEL_IMMOLATION DUNGEON_MODE<uint32>(SPELL_FEL_IMMOLATION_NORMAL, SPELL_FEL_IMMOLATION_HEROIC) + enum Events { EVENT_FELFIRE_SHOCK = 1, @@ -92,6 +96,8 @@ struct boss_wrath_scryer_soccothrates : public BossAI preFight = false; dalliahTaunt = false; dalliahDeath = false; + + DoCastSelf(SPELL_FEL_IMMOLATION, true); } void JustDied(Unit* /*killer*/) override diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp index 2da3be2e4b0..bd24a8fbf5c 100644 --- a/src/server/scripts/Spells/spell_hunter.cpp +++ b/src/server/scripts/Spells/spell_hunter.cpp @@ -548,7 +548,7 @@ class spell_hun_tame_beast : public SpellScript return SPELL_FAILED_HIGHLEVEL; // use SMSG_PET_TAME_FAILURE? - if (!target->GetCreatureTemplate()->IsTameable(caster->CanTameExoticPets())) + if (!target->GetCreatureTemplate()->IsTameable(caster->CanTameExoticPets(), target->GetCreatureDifficulty())) return SPELL_FAILED_BAD_TARGETS; if (PetStable const* petStable = caster->GetPetStable()) diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp index 6c16d72b086..5a305ef0102 100644 --- a/src/server/scripts/Spells/spell_item.cpp +++ b/src/server/scripts/Spells/spell_item.cpp @@ -1074,11 +1074,12 @@ class spell_item_extract_gas : public AuraScript { Player* player = GetCaster()->ToPlayer(); Creature* creature = GetTarget()->ToCreature(); + CreatureDifficulty const* creatureDifficulty = creature->GetCreatureDifficulty(); // missing lootid has been reported on startup - just return - if (!creature->GetCreatureTemplate()->SkinLootId) + if (!creatureDifficulty->SkinLootID) return; - player->AutoStoreLoot(creature->GetCreatureTemplate()->SkinLootId, LootTemplates_Skinning, ItemContext::NONE, true); + player->AutoStoreLoot(creatureDifficulty->SkinLootID, LootTemplates_Skinning, ItemContext::NONE, true); creature->DespawnOrUnsummon(); } } |