diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/server/database/Database/Implementation/WorldDatabase.cpp | 2 | ||||
-rw-r--r-- | src/server/game/Entities/Creature/Creature.cpp | 141 | ||||
-rw-r--r-- | src/server/game/Entities/Creature/Creature.h | 11 | ||||
-rw-r--r-- | src/server/game/Entities/Creature/CreatureData.h | 31 | ||||
-rw-r--r-- | src/server/game/Entities/Pet/Pet.cpp | 9 | ||||
-rw-r--r-- | src/server/game/Globals/ObjectMgr.cpp | 161 | ||||
-rw-r--r-- | src/server/game/Miscellaneous/SharedDefines.h | 3 | ||||
-rw-r--r-- | src/server/game/Server/Packets/CombatLogPacketsCommon.cpp | 25 |
8 files changed, 154 insertions, 229 deletions
diff --git a/src/server/database/Database/Implementation/WorldDatabase.cpp b/src/server/database/Database/Implementation/WorldDatabase.cpp index 2438bc1c1e1..39235e7cb45 100644 --- a/src/server/database/Database/Implementation/WorldDatabase.cpp +++ b/src/server/database/Database/Implementation/WorldDatabase.cpp @@ -62,7 +62,7 @@ void WorldDatabaseConnection::DoPrepareStatements() PrepareStatement(WORLD_SEL_CREATURE_ADDON_BY_GUID, "SELECT guid FROM creature_addon 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, KillCredit1, KillCredit2, name, femaleName, subname, TitleAlt, IconName, RequiredExpansion, VignetteID, faction, npcflag, speed_walk, speed_run, scale, Classification, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, unit_flags3, family, trainer_class, type, VehicleId, AIName, MovementType, ctm.HoverInitiallyEnabled, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, ExperienceModifier, RacialLeader, movementId, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, CreatureImmunitiesId, 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, Classification, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, unit_flags3, family, trainer_class, type, PetSpellDataId, VehicleId, AIName, MovementType, ctm.HoverInitiallyEnabled, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, ExperienceModifier, Civilian, RacialLeader, movementId, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, CreatureImmunitiesId, 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_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); PrepareStatement(WORLD_SEL_CREATURE_NEAREST, "SELECT guid, id, position_x, position_y, position_z, map, (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) AS order_ FROM creature WHERE map = ? AND (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) <= ? ORDER BY order_", CONNECTION_SYNCH); diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index cc8e3fc7757..60075659717 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -177,6 +177,7 @@ WorldPacket CreatureTemplate::BuildQueryData(LocaleConstant loc, Difficulty diff WorldPackets::Query::CreatureStats& stats = queryTemp.Stats; + stats.Civilian = Civilian; stats.Leader = RacialLeader; stats.Name[0] = Name; @@ -188,6 +189,7 @@ WorldPacket CreatureTemplate::BuildQueryData(LocaleConstant loc, Difficulty diff stats.CreatureType = type; stats.CreatureFamily = family; stats.Classification = uint32(Classification); + stats.PetSpellDataID = PetSpellDataID; for (uint32 i = 0; i < MAX_KILL_CREDIT; ++i) stats.ProxyCreatureID[i] = KillCredit[i]; @@ -251,9 +253,8 @@ CreatureDifficulty const* CreatureTemplate::GetDifficulty(Difficulty difficulty) { DefaultCreatureDifficulty() { - DeltaLevelMin = 0; - DeltaLevelMax = 0; - ContentTuningID = 0; + MinLevel = 1; + MaxLevel = 1; HealthScalingExpansion = 0; HealthModifier = 1.f; ManaModifier = 1.f; @@ -620,17 +621,15 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/, if (updateLevel) SelectLevel(); - else if (!IsGuardian()) + + // Do not update guardian stats here - they are handled in Guardian::InitStatsForLevel() + if (!IsGuardian()) { uint32 previousHealth = GetHealth(); UpdateLevelDependantStats(); // We still re-initialize level dependant stats on entry update if (previousHealth > 0) SetHealth(previousHealth); - } - // Do not update guardian stats here - they are handled in Guardian::InitStatsForLevel() - if (!IsGuardian()) - { SetMeleeDamageSchool(SpellSchools(cInfo->dmgschool)); SetStatFlatModifier(UNIT_MOD_RESISTANCE_HOLY, BASE_VALUE, float(cInfo->resistance[SPELL_SCHOOL_HOLY])); SetStatFlatModifier(UNIT_MOD_RESISTANCE_FIRE, BASE_VALUE, float(cInfo->resistance[SPELL_SCHOOL_FIRE])); @@ -1568,25 +1567,23 @@ void Creature::SaveToDB(uint32 mapid, std::vector<Difficulty> const& spawnDiffic void Creature::SelectLevel() { // Level - ApplyLevelScaling(); - int32 levelWithDelta = m_unitData->ScalingLevelMax + m_unitData->ScalingLevelDelta; - uint8 level = RoundToInterval<int32>(levelWithDelta, 1, STRONG_MAX_LEVEL); - SetLevel(level); - - UpdateLevelDependantStats(); + CreatureDifficulty const* difficulty = GetCreatureDifficulty(); + if (difficulty->MinLevel != difficulty->MaxLevel) + SetLevel(urand(difficulty->MinLevel, difficulty->MaxLevel)); + else + SetLevel(difficulty->MinLevel); } void Creature::UpdateLevelDependantStats() { CreatureTemplate const* cInfo = GetCreatureTemplate(); CreatureClassifications classification = IsPet() ? CreatureClassifications::Normal : cInfo->Classification; - uint8 level = GetLevel(); - CreatureBaseStats const* stats = sObjectMgr->GetCreatureBaseStats(level, cInfo->unit_class); + CreatureBaseStats const* stats = sObjectMgr->GetCreatureBaseStats(GetLevel(), cInfo->unit_class); // health float healthmod = GetHealthMod(classification); - uint32 basehp = GetMaxHealthByLevel(level); + uint32 basehp = stats->GenerateHealth(m_creatureDifficulty); uint32 health = uint32(basehp * healthmod); SetCreateHealth(health); @@ -1611,7 +1608,7 @@ void Creature::UpdateLevelDependantStats() } // damage - float basedamage = GetBaseDamageForLevel(level); + float basedamage = stats->GenerateBaseDamage(m_creatureDifficulty); float weaponBaseMinDamage = basedamage; float weaponBaseMaxDamage = basedamage * 1.5f; @@ -1628,7 +1625,7 @@ void Creature::UpdateLevelDependantStats() SetStatFlatModifier(UNIT_MOD_ATTACK_POWER, BASE_VALUE, stats->AttackPower); SetStatFlatModifier(UNIT_MOD_ATTACK_POWER_RANGED, BASE_VALUE, stats->RangedAttackPower); - float armor = GetBaseArmorForLevel(level); + float armor = (float)stats->GenerateArmor(m_creatureDifficulty); /// @todo Why is this treated as uint32 when it's a float? SetStatFlatModifier(UNIT_MOD_ARMOR, BASE_VALUE, armor); } @@ -3008,84 +3005,6 @@ void Creature::UpdateNearbyPlayersInteractions() } } -bool Creature::HasScalableLevels() const -{ - return m_unitData->ContentTuningID != 0; -} - -void Creature::ApplyLevelScaling() -{ - CreatureDifficulty const* creatureDifficulty = GetCreatureDifficulty(); - - 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(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), creatureDifficulty->ContentTuningID); -} - -uint64 Creature::GetMaxHealthByLevel(uint8 level) const -{ - CreatureTemplate const* cInfo = GetCreatureTemplate(); - CreatureDifficulty const* creatureDifficulty = GetCreatureDifficulty(); - float baseHealth = sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureHealth, level, creatureDifficulty->GetHealthScalingExpansion(), creatureDifficulty->ContentTuningID, Classes(cInfo->unit_class), 0); - return std::max(baseHealth * creatureDifficulty->HealthModifier, 1.0f); -} - -float Creature::GetHealthMultiplierForTarget(WorldObject const* target) const -{ - if (!HasScalableLevels()) - return 1.0f; - - uint8 levelForTarget = GetLevelForTarget(target); - if (GetLevel() < levelForTarget) - return 1.0f; - - return double(GetMaxHealthByLevel(levelForTarget)) / double(GetCreateHealth()); -} - -float Creature::GetBaseDamageForLevel(uint8 level) const -{ - CreatureTemplate const* cInfo = GetCreatureTemplate(); - CreatureDifficulty const* creatureDifficulty = GetCreatureDifficulty(); - return sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureAutoAttackDps, level, creatureDifficulty->GetHealthScalingExpansion(), creatureDifficulty->ContentTuningID, Classes(cInfo->unit_class), 0); -} - -float Creature::GetDamageMultiplierForTarget(WorldObject const* target) const -{ - if (!HasScalableLevels()) - return 1.0f; - - uint8 levelForTarget = GetLevelForTarget(target); - - return GetBaseDamageForLevel(levelForTarget) / GetBaseDamageForLevel(GetLevel()); -} - -float Creature::GetBaseArmorForLevel(uint8 level) const -{ - CreatureTemplate const* cInfo = GetCreatureTemplate(); - CreatureDifficulty const* creatureDifficulty = GetCreatureDifficulty(); - float baseArmor = sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureArmor, level, creatureDifficulty->GetHealthScalingExpansion(), creatureDifficulty->ContentTuningID, Classes(cInfo->unit_class), 0); - return baseArmor * creatureDifficulty->ArmorModifier; -} - -float Creature::GetArmorMultiplierForTarget(WorldObject const* target) const -{ - if (!HasScalableLevels()) - return 1.0f; - - uint8 levelForTarget = GetLevelForTarget(target); - - return GetBaseArmorForLevel(levelForTarget) / GetBaseArmorForLevel(GetLevel()); -} - uint8 Creature::GetLevelForTarget(WorldObject const* target) const { if (Unit const* unitTarget = target->ToUnit()) @@ -3095,34 +3014,6 @@ uint8 Creature::GetLevelForTarget(WorldObject const* target) const uint8 level = unitTarget->GetLevel() + sWorld->getIntConfig(CONFIG_WORLD_BOSS_LEVEL_DIFF); return RoundToInterval<uint8>(level, 1u, 255u); } - - // If this creature should scale level, adapt level depending of target level - // between UNIT_FIELD_SCALING_LEVEL_MIN and UNIT_FIELD_SCALING_LEVEL_MAX - if (HasScalableLevels()) - { - int32 scalingLevelMin = m_unitData->ScalingLevelMin; - int32 scalingLevelMax = m_unitData->ScalingLevelMax; - int32 scalingLevelDelta = m_unitData->ScalingLevelDelta; - int32 scalingFactionGroup = m_unitData->ScalingFactionGroup; - int32 targetLevel = unitTarget->m_unitData->EffectiveLevel; - if (!targetLevel) - targetLevel = unitTarget->GetLevel(); - - int32 targetLevelDelta = 0; - - if (Player const* playerTarget = target->ToPlayer()) - { - if (scalingFactionGroup && sFactionTemplateStore.AssertEntry(sChrRacesStore.AssertEntry(playerTarget->GetRace())->FactionID)->FactionGroup != scalingFactionGroup) - scalingLevelMin = scalingLevelMax; - - int32 maxCreatureScalingLevel = playerTarget->m_activePlayerData->MaxCreatureScalingLevel; - targetLevelDelta = std::min(maxCreatureScalingLevel > 0 ? maxCreatureScalingLevel - targetLevel : 0, *playerTarget->m_activePlayerData->ScalingPlayerLevelDelta); - } - - int32 levelWithDelta = targetLevel + targetLevelDelta; - int32 level = RoundToInterval(levelWithDelta, scalingLevelMin, scalingLevelMax) + scalingLevelDelta; - return RoundToInterval(level, 1, MAX_LEVEL + 3); - } } return Unit::GetLevelForTarget(target); diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 839abfdd725..a64ba0c02e7 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -187,19 +187,8 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma void SetInteractionAllowedInCombat(bool interactionAllowed) override; void UpdateNearbyPlayersInteractions() override; - bool HasScalableLevels() const; - void ApplyLevelScaling(); uint8 GetLevelForTarget(WorldObject const* target) const override; - uint64 GetMaxHealthByLevel(uint8 level) const; - float GetHealthMultiplierForTarget(WorldObject const* target) const override; - - float GetBaseDamageForLevel(uint8 level) const; - float GetDamageMultiplierForTarget(WorldObject const* target) const override; - - float GetBaseArmorForLevel(uint8 level) const; - float GetArmorMultiplierForTarget(WorldObject const* target) const override; - bool IsInEvadeMode() const { return HasUnitState(UNIT_STATE_EVADE); } bool IsEvadingAttacks() const { return IsInEvadeMode() || CanNotReachTarget(); } diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h index 1e386ff2da5..31cf963dc69 100644 --- a/src/server/game/Entities/Creature/CreatureData.h +++ b/src/server/game/Entities/Creature/CreatureData.h @@ -436,9 +436,8 @@ struct CreatureModel struct CreatureDifficulty { - int16 DeltaLevelMin; - int16 DeltaLevelMax; - int32 ContentTuningID; + uint8 MinLevel; + uint8 MaxLevel; int32 HealthScalingExpansion; float HealthModifier; float ManaModifier; @@ -507,6 +506,7 @@ struct TC_GAME_API CreatureTemplate CreatureFamily family; // enum CreatureFamily values (optional) uint32 trainer_class; uint32 type; // enum CreatureType values + uint32 PetSpellDataID; int32 resistance[MAX_SPELL_SCHOOL]; uint32 spells[MAX_CREATURE_SPELLS]; uint32 VehicleId; @@ -514,6 +514,7 @@ struct TC_GAME_API CreatureTemplate uint32 MovementType; CreatureMovementData Movement; float ModExperience; + bool Civilian; bool RacialLeader; uint32 movementId; int32 WidgetSetID; @@ -556,11 +557,29 @@ struct TC_GAME_API CreatureTemplate // Defines base stats for creatures (used to calculate HP/mana/armor/attackpower/rangedattackpower/all damage). struct TC_GAME_API CreatureBaseStats { - uint32 BaseMana; - uint32 AttackPower; - uint32 RangedAttackPower; + std::array<uint32, CURRENT_EXPANSION + 1> BaseHealth; + uint32 BaseMana = 0; + uint32 BaseArmor = 0; + uint32 AttackPower = 0; + uint32 RangedAttackPower = 0; + std::array<float, CURRENT_EXPANSION + 1> BaseDamage; // Helpers + + uint32 GenerateHealth(CreatureDifficulty const* difficulty) const { return uint32(ceil(BaseHealth[difficulty->GetHealthScalingExpansion()] * difficulty->HealthModifier)); } + uint32 GenerateMana(CreatureDifficulty const* difficulty) const + { + // Mana can be 0. + if (!BaseMana) + return 0; + + return uint32(ceil(BaseMana * difficulty->ManaModifier)); + } + + uint32 GenerateArmor(CreatureDifficulty const* difficulty) const { return uint32(ceil(BaseArmor * difficulty->ArmorModifier)); } + + float GenerateBaseDamage(CreatureDifficulty const* difficulty) const { return BaseDamage[difficulty->GetHealthScalingExpansion()]; } + static CreatureBaseStats const* GetBaseStats(uint8 level, uint8 unitClass); }; diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index c6966763d9d..2d2cac13da0 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -906,7 +906,10 @@ bool Guardian::InitStatsForLevel(uint8 petlevel) { // remove elite bonuses included in DB values CreatureBaseStats const* stats = sObjectMgr->GetCreatureBaseStats(petlevel, cinfo->unit_class); - ApplyLevelScaling(); + float healthmod = GetHealthMod(cinfo->Classification); + uint32 basehp = stats->GenerateHealth(m_creatureDifficulty); + uint32 health = uint32(basehp * healthmod); + uint32 mana = stats->GenerateMana(m_creatureDifficulty); CreatureDifficulty const* creatureDifficulty = GetCreatureDifficulty(); SetCreateHealth(std::max(sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureHealth, petlevel, creatureDifficulty->GetHealthScalingExpansion(), m_unitData->ContentTuningID, Classes(cinfo->unit_class), 0) * creatureDifficulty->HealthModifier * GetHealthMod(cinfo->Classification), 1.0f)); @@ -915,6 +918,7 @@ bool Guardian::InitStatsForLevel(uint8 petlevel) SetCreateStat(STAT_AGILITY, 22); SetCreateStat(STAT_STAMINA, 25); SetCreateStat(STAT_INTELLECT, 28); + SetCreateStat(STAT_SPIRIT, 27); } // Power @@ -1069,7 +1073,8 @@ bool Guardian::InitStatsForLevel(uint8 petlevel) * should be copied here (or moved to another method or if that function should be called here * or not just for this default case) */ - float basedamage = GetBaseDamageForLevel(petlevel); + CreatureBaseStats const* stats = sObjectMgr->GetCreatureBaseStats(petlevel, cinfo->unit_class); + float basedamage = stats->GenerateBaseDamage(m_creatureDifficulty); float weaponBaseMinDamage = basedamage; float weaponBaseMaxDamage = basedamage * 1.5f; diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 5aaa3e6e080..eb9c3a9b7c1 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -352,15 +352,15 @@ void ObjectMgr::LoadCreatureTemplates() // "faction, npcflag, speed_walk, speed_run, scale, `rank`, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, " // 21 22 23 24 // "unit_class, unit_flags, unit_flags2, unit_flags3, " - // 25 26 27 28 29 30 - // "family, trainer_class, type, VehicleId, AIName, MovementType, " - // 31 32 33 34 35 + // 25 26 27 28 29 30 31 + // "family, trainer_class, type, PetSpellDataId, VehicleId, AIName, MovementType, " + // 32 33 34 35 36 // "ctm.HoverInitiallyEnabled, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, ExperienceModifier, " - // 36 37 38 39 40 - // "RacialLeader, movementId, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, " - // 41 42 + // 37 38 39 40 41 42 + // "Civilian, RacialLeader, movementId, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, " + // 43 44 // "CreatureImmunitiesId, flags_extra, " - // 43 44 + // 45 46 // "ScriptName, StringId FROM creature_template WHERE entry = ? OR 1 = ?"); WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_CREATURE_TEMPLATE); @@ -429,6 +429,7 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields) creatureTemplate.family = CreatureFamily(fields[25].GetInt32()); creatureTemplate.trainer_class = uint32(fields[26].GetUInt8()); creatureTemplate.type = uint32(fields[27].GetUInt8()); + creatureTemplate.PetSpellDataID = uint32(fields[28].GetUInt32()); for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i) creatureTemplate.resistance[i] = 0; @@ -436,32 +437,33 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields) for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i) creatureTemplate.spells[i] = 0; - creatureTemplate.VehicleId = fields[28].GetUInt32(); - creatureTemplate.AIName = fields[29].GetString(); - creatureTemplate.MovementType = uint32(fields[30].GetUInt8()); - - if (!fields[31].IsNull()) - creatureTemplate.Movement.HoverInitiallyEnabled = fields[31].GetBool(); + creatureTemplate.VehicleId = fields[29].GetUInt32(); + creatureTemplate.AIName = fields[30].GetString(); + creatureTemplate.MovementType = uint32(fields[31].GetUInt8()); if (!fields[32].IsNull()) - creatureTemplate.Movement.Chase = static_cast<CreatureChaseMovementType>(fields[32].GetUInt8()); + creatureTemplate.Movement.HoverInitiallyEnabled = fields[32].GetBool(); if (!fields[33].IsNull()) - creatureTemplate.Movement.Random = static_cast<CreatureRandomMovementType>(fields[33].GetUInt8()); + creatureTemplate.Movement.Chase = static_cast<CreatureChaseMovementType>(fields[33].GetUInt8()); if (!fields[34].IsNull()) - creatureTemplate.Movement.InteractionPauseTimer = fields[34].GetUInt32(); + creatureTemplate.Movement.Random = static_cast<CreatureRandomMovementType>(fields[34].GetUInt8()); + + if (!fields[35].IsNull()) + creatureTemplate.Movement.InteractionPauseTimer = fields[35].GetUInt32(); - creatureTemplate.ModExperience = fields[35].GetFloat(); - creatureTemplate.RacialLeader = fields[36].GetBool(); - creatureTemplate.movementId = fields[37].GetUInt32(); - creatureTemplate.WidgetSetID = fields[38].GetInt32(); - creatureTemplate.WidgetSetUnitConditionID = fields[39].GetInt32(); - creatureTemplate.RegenHealth = fields[40].GetBool(); - creatureTemplate.CreatureImmunitiesId = fields[41].GetInt32(); - creatureTemplate.flags_extra = fields[42].GetUInt32(); - creatureTemplate.ScriptID = GetScriptId(fields[43].GetString()); - creatureTemplate.StringId = fields[44].GetString(); + creatureTemplate.ModExperience = fields[36].GetFloat(); + creatureTemplate.Civilian = fields[37].GetBool(); + creatureTemplate.RacialLeader = fields[38].GetBool(); + creatureTemplate.movementId = fields[39].GetUInt32(); + creatureTemplate.WidgetSetID = fields[40].GetInt32(); + creatureTemplate.WidgetSetUnitConditionID = fields[41].GetInt32(); + creatureTemplate.RegenHealth = fields[42].GetBool(); + creatureTemplate.CreatureImmunitiesId = fields[43].GetInt32(); + creatureTemplate.flags_extra = fields[44].GetUInt32(); + creatureTemplate.ScriptID = GetScriptId(fields[45].GetString()); + creatureTemplate.StringId = fields[46].GetString(); } void ObjectMgr::LoadCreatureTemplateGossip() @@ -924,13 +926,13 @@ void ObjectMgr::LoadCreatureTemplateDifficulty() { uint32 oldMSTime = getMSTime(); - // 0 1 2 3 4 5 - QueryResult result = WorldDatabase.Query("SELECT Entry, DifficultyID, LevelScalingDeltaMin, LevelScalingDeltaMax, ContentTuningID, HealthScalingExpansion, " - // 6 7 8 9 10 11 12 + // 0 1 2 3 4 + QueryResult result = WorldDatabase.Query("SELECT Entry, DifficultyID, MinLevel, MaxLevel, HealthScalingExpansion, " + // 5 6 7 8 9 10 11 "HealthModifier, ManaModifier, ArmorModifier, DamageModifier, CreatureDifficultyID, TypeFlags, TypeFlags2, " - // 13 14 15 16 17 + // 12 13 14 15 16 "LootID, PickPocketLootID, SkinLootID, GoldMin, GoldMax," - // 18 19 20 21 22 23 24 25 + // 17 18 19 20 21 22 23 24 "StaticFlags1, StaticFlags2, StaticFlags3, StaticFlags4, StaticFlags5, StaticFlags6, StaticFlags7, StaticFlags8 " "FROM creature_template_difficulty ORDER BY Entry"); @@ -956,30 +958,51 @@ void ObjectMgr::LoadCreatureTemplateDifficulty() } 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(); - creatureDifficulty.StaticFlags = CreatureStaticFlagsHolder(CreatureStaticFlags(fields[18].GetUInt32()), CreatureStaticFlags2(fields[19].GetUInt32()), - CreatureStaticFlags3(fields[20].GetUInt32()), CreatureStaticFlags4(fields[21].GetUInt32()), CreatureStaticFlags5(fields[22].GetUInt32()), - CreatureStaticFlags6(fields[23].GetUInt32()), CreatureStaticFlags7(fields[24].GetUInt32()), CreatureStaticFlags8(fields[25].GetUInt32())); + creatureDifficulty.MinLevel = fields[2].GetUInt8(); + creatureDifficulty.MaxLevel = fields[3].GetUInt8(); + creatureDifficulty.HealthScalingExpansion = fields[4].GetInt32(); + creatureDifficulty.HealthModifier = fields[5].GetFloat(); + creatureDifficulty.ManaModifier = fields[6].GetFloat(); + creatureDifficulty.ArmorModifier = fields[7].GetFloat(); + creatureDifficulty.DamageModifier = fields[8].GetFloat(); + creatureDifficulty.CreatureDifficultyID = fields[9].GetInt32(); + creatureDifficulty.TypeFlags = fields[10].GetUInt32(); + creatureDifficulty.TypeFlags2 = fields[11].GetUInt32(); + creatureDifficulty.LootID = fields[12].GetUInt32(); + creatureDifficulty.PickPocketLootID = fields[13].GetUInt32(); + creatureDifficulty.SkinLootID = fields[14].GetUInt32(); + creatureDifficulty.GoldMin = fields[15].GetUInt32(); + creatureDifficulty.GoldMax = fields[16].GetUInt32(); + creatureDifficulty.StaticFlags = CreatureStaticFlagsHolder(CreatureStaticFlags(fields[17].GetUInt32()), CreatureStaticFlags2(fields[18].GetUInt32()), + CreatureStaticFlags3(fields[19].GetUInt32()), CreatureStaticFlags4(fields[20].GetUInt32()), CreatureStaticFlags5(fields[21].GetUInt32()), + CreatureStaticFlags6(fields[22].GetUInt32()), CreatureStaticFlags7(fields[23].GetUInt32()), CreatureStaticFlags8(fields[24].GetUInt32())); // TODO: Check if this still applies creatureDifficulty.DamageModifier *= Creature::GetDamageMod(itr->second.Classification); - if (creatureDifficulty.HealthScalingExpansion < EXPANSION_LEVEL_CURRENT || creatureDifficulty.HealthScalingExpansion >= MAX_EXPANSIONS) + if (creatureDifficulty.MinLevel == 0 || creatureDifficulty.MaxLevel == 0) + { + if (creatureDifficulty.MinLevel == 0) + { + TC_LOG_ERROR("sql.sql", "Table `creature_template_difficulty` lists creature (ID: {}) has MinLevel set to 0 but the allowed minimum is 1. Ignored and set to 1.", entry); + creatureDifficulty.MinLevel = 1; + } + + if (creatureDifficulty.MaxLevel == 0) + { + TC_LOG_ERROR("sql.sql", "Table `creature_template_difficulty` lists creature (ID: {}) has MaxLevel set to 0 but the allowed minimum is 1. Ignored and set to 1.", entry); + creatureDifficulty.MaxLevel = 1; + } + } + + if (creatureDifficulty.MinLevel > creatureDifficulty.MaxLevel) + { + TC_LOG_ERROR("sql.sql", "Table `creature_template_difficulty` lists creature (ID: {}) with a higher MinLevel ({}) than MaxLevel ({}). MaxLevel will be set to MinLevel value.", + entry, creatureDifficulty.MinLevel, creatureDifficulty.MaxLevel); + creatureDifficulty.MinLevel = creatureDifficulty.MaxLevel; + } + + if (creatureDifficulty.HealthScalingExpansion < EXPANSION_LEVEL_CURRENT || creatureDifficulty.HealthScalingExpansion > CURRENT_EXPANSION) { TC_LOG_ERROR("sql.sql", "Table `creature_template_difficulty` lists creature (ID: {}) with invalid `HealthScalingExpansion` {}. Ignored and set to 0.", entry, creatureDifficulty.HealthScalingExpansion); @@ -4169,7 +4192,8 @@ void ObjectMgr::LoadPlayerInfo() uint32 oldMSTime = getMSTime(); - QueryResult raceStatsResult = WorldDatabase.Query("SELECT race, str, agi, sta, inte FROM player_racestats"); + // 0 1 2 3 4 5 6 + QueryResult raceStatsResult = WorldDatabase.Query("SELECT race, str, agi, sta, inte, spi FROM player_racestats"); if (!raceStatsResult) { @@ -4193,8 +4217,8 @@ void ObjectMgr::LoadPlayerInfo() } while (raceStatsResult->NextRow()); - // 0 1 2 3 4 5 - QueryResult result = WorldDatabase.Query("SELECT class, level, str, agi, sta, inte FROM player_classlevelstats"); + // 0 1 2 3 4 5 6 + QueryResult result = WorldDatabase.Query("SELECT class, level, str, agi, sta, inte, spi FROM player_classlevelstats"); if (!result) { @@ -9962,8 +9986,8 @@ CreatureBaseStats const* ObjectMgr::GetCreatureBaseStats(uint8 level, uint8 unit void ObjectMgr::LoadCreatureClassLevelStats() { uint32 oldMSTime = getMSTime(); - // 0 1 2 3 4 - QueryResult result = WorldDatabase.Query("SELECT level, class, basemana, attackpower, rangedattackpower FROM creature_classlevelstats"); + // 0 1 2 3 4 5 6 7 8 9 10 12 13 14 + QueryResult result = WorldDatabase.Query("SELECT level, class, basehp0, basehp1, basehp2, basehp3, basemana, basearmor, attackpower, rangedattackpower, damage_base, damage_exp1, damage_exp2, damage_exp3 FROM creature_classlevelstats"); if (!result) { @@ -9984,10 +10008,29 @@ void ObjectMgr::LoadCreatureClassLevelStats() CreatureBaseStats stats; - stats.BaseMana = fields[2].GetUInt32(); + for (uint8 i = 0; i <= CURRENT_EXPANSION; ++i) + { + stats.BaseHealth[i] = fields[2 + i].GetUInt32(); + + if (stats.BaseHealth[i] == 0) + { + TC_LOG_ERROR("sql.sql", "Creature base stats for class {}, level {} has invalid zero base HP[{}] - set to 1", Class, Level, i); + stats.BaseHealth[i] = 1; + } + + stats.BaseDamage[i] = fields[10 + i].GetFloat(); + if (stats.BaseDamage[i] < 0.0f) + { + TC_LOG_ERROR("sql.sql", "Creature base stats for class {}, level {} has invalid negative base damage[{}] - set to 0.0", Class, Level, i); + stats.BaseDamage[i] = 0.0f; + } + } + + stats.BaseMana = fields[6].GetUInt32(); + stats.BaseArmor = fields[7].GetUInt32(); - stats.AttackPower = fields[3].GetUInt16(); - stats.RangedAttackPower = fields[4].GetUInt16(); + stats.AttackPower = fields[8].GetUInt16(); + stats.RangedAttackPower = fields[9].GetUInt16(); _creatureBaseStatsStore[MAKE_PAIR16(Level, Class)] = stats; diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 08954b5c251..3766d4109e2 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -276,9 +276,10 @@ enum Stats : uint16 STAT_AGILITY = 1, STAT_STAMINA = 2, STAT_INTELLECT = 3, + STAT_SPIRIT = 4 }; -#define MAX_STATS 4 +#define MAX_STATS 5 // EnumUtils: DESCRIBE THIS enum Powers : int8 diff --git a/src/server/game/Server/Packets/CombatLogPacketsCommon.cpp b/src/server/game/Server/Packets/CombatLogPacketsCommon.cpp index e2435ae0660..cb817e72762 100644 --- a/src/server/game/Server/Packets/CombatLogPacketsCommon.cpp +++ b/src/server/game/Server/Packets/CombatLogPacketsCommon.cpp @@ -78,7 +78,6 @@ bool ContentTuningParams::GenerateDataForUnits<Creature, Player>(Creature* attac TargetLevel = target->GetLevel(); Expansion = creatureDifficulty->HealthScalingExpansion; TargetScalingLevelDelta = int8(attacker->m_unitData->ScalingLevelDelta); - TargetContentTuningID = creatureDifficulty->ContentTuningID; return true; } @@ -96,14 +95,13 @@ bool ContentTuningParams::GenerateDataForUnits<Player, Creature>(Player* attacke TargetLevel = target->GetLevel(); Expansion = creatureDifficulty->HealthScalingExpansion; TargetScalingLevelDelta = int8(target->m_unitData->ScalingLevelDelta); - TargetContentTuningID = creatureDifficulty->ContentTuningID; return true; } template<> bool ContentTuningParams::GenerateDataForUnits<Creature, Creature>(Creature* attacker, Creature* target) { - Creature* accessor = target->HasScalableLevels() ? target : attacker; + Creature* accessor = attacker; CreatureTemplate const* creatureTemplate = accessor->GetCreatureTemplate(); CreatureDifficulty const* creatureDifficulty = creatureTemplate->GetDifficulty(accessor->GetMap()->GetDifficultyID()); @@ -113,7 +111,6 @@ bool ContentTuningParams::GenerateDataForUnits<Creature, Creature>(Creature* att TargetLevel = target->GetLevel(); Expansion = creatureDifficulty->HealthScalingExpansion; TargetScalingLevelDelta = int8(accessor->m_unitData->ScalingLevelDelta); - TargetContentTuningID = creatureDifficulty->ContentTuningID; return true; } @@ -121,28 +118,8 @@ template<> bool ContentTuningParams::GenerateDataForUnits<Unit, Unit>(Unit* attacker, Unit* target) { if (Player* playerAttacker = Object::ToPlayer(attacker)) - { if (Player* playerTarget = Object::ToPlayer(target)) return GenerateDataForUnits(playerAttacker, playerTarget); - else if (Creature* creatureTarget = Object::ToCreature(target)) - { - if (creatureTarget->HasScalableLevels()) - return GenerateDataForUnits(playerAttacker, creatureTarget); - } - } - else if (Creature* creatureAttacker = Object::ToCreature(attacker)) - { - if (Player* playerTarget = Object::ToPlayer(target)) - { - if (creatureAttacker->HasScalableLevels()) - return GenerateDataForUnits(creatureAttacker, playerTarget); - } - else if (Creature* creatureTarget = Object::ToCreature(target)) - { - if (creatureAttacker->HasScalableLevels() || creatureTarget->HasScalableLevels()) - return GenerateDataForUnits(creatureAttacker, creatureTarget); - } - } return false; } |