aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/database/Database/Implementation/WorldDatabase.cpp2
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp141
-rw-r--r--src/server/game/Entities/Creature/Creature.h11
-rw-r--r--src/server/game/Entities/Creature/CreatureData.h31
-rw-r--r--src/server/game/Entities/Pet/Pet.cpp9
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp161
-rw-r--r--src/server/game/Miscellaneous/SharedDefines.h3
-rw-r--r--src/server/game/Server/Packets/CombatLogPacketsCommon.cpp25
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;
}