diff options
23 files changed, 384 insertions, 85 deletions
diff --git a/sql/updates/world/master/2017_07_29_00_world.sql b/sql/updates/world/master/2017_07_29_00_world.sql new file mode 100644 index 00000000000..9307960390f --- /dev/null +++ b/sql/updates/world/master/2017_07_29_00_world.sql @@ -0,0 +1,9 @@ +DROP TABLE IF EXISTS `creature_template_scaling`; +CREATE TABLE `creature_template_scaling` ( + `Entry` MEDIUMINT (8) UNSIGNED NOT NULL, + `LevelScalingMin` SMALLINT (5) UNSIGNED NOT NULL DEFAULT 0, + `LevelScalingMax` SMALLINT (5) UNSIGNED NOT NULL DEFAULT 0, + `LevelScalingDelta` SMALLINT (5) NOT NULL DEFAULT 0, + `VerifiedBuild` SMALLINT (5) DEFAULT NULL, + PRIMARY KEY (`Entry`) +); diff --git a/src/server/game/Achievements/CriteriaHandler.cpp b/src/server/game/Achievements/CriteriaHandler.cpp index 466fcbf8880..cbf296ddb7e 100644 --- a/src/server/game/Achievements/CriteriaHandler.cpp +++ b/src/server/game/Achievements/CriteriaHandler.cpp @@ -320,7 +320,7 @@ bool CriteriaData::Meets(uint32 criteriaId, Player const* source, Unit const* ta case CRITERIA_DATA_TYPE_T_LEVEL: if (!target) return false; - return target->getLevel() >= Level.Min; + return target->GetLevelForTarget(source) >= Level.Min; case CRITERIA_DATA_TYPE_T_GENDER: if (!target) return false; @@ -1683,7 +1683,7 @@ bool CriteriaHandler::AdditionalRequirementsSatisfied(ModifierTreeNode const* tr return false; break; case CRITERIA_ADDITIONAL_CONDITION_TARGET_LEVEL: // 40 - if (!unit || unit->getLevel() != reqValue) + if (!unit || unit->GetLevelForTarget(referencePlayer) != reqValue) return false; break; case CRITERIA_ADDITIONAL_CONDITION_TARGET_ZONE: // 41 diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 80f775e410a..c7698b706c3 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -437,8 +437,8 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/, if (updateLevel) SelectLevel(); - - UpdateLevelDependantStats(); + else + UpdateLevelDependantStats(); // We still re-initialize level dependant stats on entry update SetMeleeDamageSchool(SpellSchools(cInfo->dmgschool)); SetModifierValue(UNIT_MOD_RESISTANCE_HOLY, BASE_VALUE, float(cInfo->resistance[SPELL_SCHOOL_HOLY])); @@ -1186,11 +1186,23 @@ void Creature::SelectLevel() { CreatureTemplate const* cInfo = GetCreatureTemplate(); - // level - uint8 minlevel = std::min(cInfo->maxlevel, cInfo->minlevel); - uint8 maxlevel = std::max(cInfo->maxlevel, cInfo->minlevel); - uint8 level = minlevel == maxlevel ? minlevel : urand(minlevel, maxlevel); - SetLevel(level); + if (!HasScalableLevels()) + { + // level + uint8 minlevel = std::min(cInfo->maxlevel, cInfo->minlevel); + uint8 maxlevel = std::max(cInfo->maxlevel, cInfo->minlevel); + uint8 level = minlevel == maxlevel ? minlevel : urand(minlevel, maxlevel); + SetLevel(level); + } + else + { + SetLevel(cInfo->levelScaling->MaxLevel); + SetUInt32Value(UNIT_FIELD_SCALING_LEVEL_MIN, cInfo->levelScaling->MinLevel); + SetUInt32Value(UNIT_FIELD_SCALING_LEVEL_MAX, cInfo->levelScaling->MaxLevel); + SetUInt32Value(UNIT_FIELD_SCALING_LEVEL_DELTA, cInfo->levelScaling->DeltaLevel); + } + + UpdateLevelDependantStats(); } void Creature::UpdateLevelDependantStats() @@ -1612,7 +1624,7 @@ bool Creature::CanStartAttack(Unit const* who, bool force) const return false; // No aggro from gray creatures - if (CheckNoGrayAggroConfig(who->getLevelForTarget(this), getLevelForTarget(who))) + if (CheckNoGrayAggroConfig(who->GetLevelForTarget(this), GetLevelForTarget(who))) return false; return IsWithinLOSInMap(who); @@ -1640,8 +1652,8 @@ float Creature::GetAttackDistance(Unit const* player) const if (aggroRate == 0) return 0.0f; - uint32 playerlevel = player->getLevelForTarget(this); - uint32 creaturelevel = getLevelForTarget(player); + uint32 playerlevel = player->GetLevelForTarget(this); + uint32 creaturelevel = GetLevelForTarget(player); int32 leveldif = int32(playerlevel) - int32(creaturelevel); @@ -1779,8 +1791,8 @@ void Creature::Respawn(bool force) loot.clear(); if (m_originalEntry != GetEntry()) UpdateEntry(m_originalEntry); - - SelectLevel(); + else + SelectLevel(); setDeathState(JUST_RESPAWNED); @@ -2440,17 +2452,89 @@ void Creature::AllLootRemovedFromCorpse() m_respawnTime = m_corpseRemoveTime + m_respawnDelay; } -uint8 Creature::getLevelForTarget(WorldObject const* target) const +bool Creature::HasScalableLevels() const { - if (!isWorldBoss() || !target->ToUnit()) - return Unit::getLevelForTarget(target); + CreatureTemplate const* cinfo = GetCreatureTemplate(); + return cinfo->levelScaling.is_initialized(); +} + +uint64 Creature::GetMaxHealthByLevel(uint8 level) const +{ + CreatureTemplate const* cInfo = GetCreatureTemplate(); + CreatureBaseStats const* stats = sObjectMgr->GetCreatureBaseStats(level, cInfo->unit_class); + return stats->GenerateHealth(cInfo); +} + +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(); + CreatureBaseStats const* stats = sObjectMgr->GetCreatureBaseStats(level, cInfo->unit_class); + return stats->GenerateBaseDamage(cInfo); +} + +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(); + CreatureBaseStats const* stats = sObjectMgr->GetCreatureBaseStats(level, cInfo->unit_class); + return stats->GenerateArmor(cInfo); +} + +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()) + { + if (isWorldBoss()) + { + 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()) + { + uint8 targetLevelWithDelta = unitTarget->getLevel() + GetCreatureTemplate()->levelScaling->DeltaLevel; + + if (target->IsPlayer()) + targetLevelWithDelta += target->GetUInt32Value(PLAYER_FIELD_SCALING_PLAYER_LEVEL_DELTA); + + return RoundToInterval<uint8>(targetLevelWithDelta, GetUInt32Value(UNIT_FIELD_SCALING_LEVEL_MIN), GetUInt32Value(UNIT_FIELD_SCALING_LEVEL_MAX)); + } + } - uint16 level = target->ToUnit()->getLevel() + sWorld->getIntConfig(CONFIG_WORLD_BOSS_LEVEL_DIFF); - if (level < 1) - return 1; - if (level > 255) - return 255; - return uint8(level); + return Unit::GetLevelForTarget(target); } std::string Creature::GetAIName() const @@ -2601,11 +2685,11 @@ float Creature::GetAggroRange(Unit const* target) const uint32 targetLevel = 0; if (target->GetTypeId() == TYPEID_PLAYER) - targetLevel = target->getLevelForTarget(this); + targetLevel = target->GetLevelForTarget(this); else if (target->GetTypeId() == TYPEID_UNIT) - targetLevel = target->ToCreature()->getLevelForTarget(this); + targetLevel = target->ToCreature()->GetLevelForTarget(this); - uint32 myLevel = getLevelForTarget(target); + uint32 myLevel = GetLevelForTarget(target); int32 levelDiff = int32(targetLevel) - int32(myLevel); // The maximum Aggro Radius is capped at 45 yards (25 level difference) diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 320cb16ad12..a1344752b0d 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -112,7 +112,17 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma bool IsDungeonBoss() const; - uint8 getLevelForTarget(WorldObject const* target) const override; // overwrite Unit::getLevelForTarget for boss level support + bool HasScalableLevels() const; + 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 4b62e3ba138..876148a1c1f 100644 --- a/src/server/game/Entities/Creature/CreatureData.h +++ b/src/server/game/Entities/Creature/CreatureData.h @@ -19,6 +19,7 @@ #define CreatureData_h__ #include "DBCEnums.h" +#include "Optional.h" #include "SharedDefines.h" #include "UnitDefines.h" #include <string> @@ -291,6 +292,13 @@ const uint32 MAX_CREATURE_NAMES = 4; const uint32 MAX_CREATURE_SPELLS = 8; const uint32 MAX_CREATURE_DIFFICULTIES = 3; +struct CreatureLevelScaling +{ + uint16 MinLevel; + uint16 MaxLevel; + int16 DeltaLevel; +}; + // from `creature_template` table struct TC_GAME_API CreatureTemplate { @@ -308,6 +316,7 @@ struct TC_GAME_API CreatureTemplate uint32 GossipMenuId; int16 minlevel; int16 maxlevel; + Optional<CreatureLevelScaling> levelScaling; int32 HealthScalingExpansion; uint32 RequiredExpansion; uint32 VignetteID; /// @todo Read Vignette.db2 diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index c3d5c15e0e6..e5bcf6623c4 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -1113,10 +1113,10 @@ bool GameObject::IsInvisibleDueToDespawn() const return false; } -uint8 GameObject::getLevelForTarget(WorldObject const* target) const +uint8 GameObject::GetLevelForTarget(WorldObject const* target) const { if (Unit* owner = GetOwner()) - return owner->getLevelForTarget(target); + return owner->GetLevelForTarget(target); return 1; } diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index e55f74c4b2b..f91aa3d4672 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -234,7 +234,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> bool IsAlwaysVisibleFor(WorldObject const* seer) const override; bool IsInvisibleDueToDespawn() const override; - uint8 getLevelForTarget(WorldObject const* target) const override; + uint8 GetLevelForTarget(WorldObject const* target) const override; GameObject* LookupFishingHoleAround(float range); diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 1510ac718d9..8746121ecbc 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -2231,13 +2231,13 @@ bool WorldObject::CanDetectStealthOf(WorldObject const* obj, bool checkAlert) co // Level difference: 5 point / level, starting from level 1. // There may be spells for this and the starting points too, but // not in the DBCs of the client. - detectionValue += int32(getLevelForTarget(obj) - 1) * 5; + detectionValue += int32(GetLevelForTarget(obj) - 1) * 5; // Apply modifiers detectionValue += m_stealthDetect.GetValue(StealthType(i)); if (go) if (Unit* owner = go->GetOwner()) - detectionValue -= int32(owner->getLevelForTarget(this) - 1) * 5; + detectionValue -= int32(owner->GetLevelForTarget(this) - 1) * 5; detectionValue -= obj->m_stealth.GetValue(StealthType(i)); diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 7b21be71a39..942c1792367 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -255,29 +255,37 @@ class TC_GAME_API Object // FG: some hacky helpers void ForceValuesUpdateAtIndex(uint32); - Player* ToPlayer() { if (GetTypeId() == TYPEID_PLAYER) return reinterpret_cast<Player*>(this); else return NULL; } - Player const* ToPlayer() const { if (GetTypeId() == TYPEID_PLAYER) return reinterpret_cast<Player const*>(this); else return NULL; } + inline bool IsPlayer() const { return GetTypeId() == TYPEID_PLAYER; } + Player* ToPlayer() { if (IsPlayer()) return reinterpret_cast<Player*>(this); else return nullptr; } + Player const* ToPlayer() const { if (IsPlayer()) return reinterpret_cast<Player const*>(this); else return nullptr; } - Creature* ToCreature() { if (GetTypeId() == TYPEID_UNIT) return reinterpret_cast<Creature*>(this); else return NULL; } - Creature const* ToCreature() const { if (GetTypeId() == TYPEID_UNIT) return reinterpret_cast<Creature const*>(this); else return NULL; } + inline bool IsCreature() const { return GetTypeId() == TYPEID_UNIT; } + Creature* ToCreature() { if (IsCreature()) return reinterpret_cast<Creature*>(this); else return nullptr; } + Creature const* ToCreature() const { if (IsCreature()) return reinterpret_cast<Creature const*>(this); else return nullptr; } - Unit* ToUnit() { if (isType(TYPEMASK_UNIT)) return reinterpret_cast<Unit*>(this); else return NULL; } - Unit const* ToUnit() const { if (isType(TYPEMASK_UNIT)) return reinterpret_cast<Unit const*>(this); else return NULL; } + inline bool IsUnit() const { return isType(TYPEMASK_UNIT); } + Unit* ToUnit() { if (IsUnit()) return reinterpret_cast<Unit*>(this); else return nullptr; } + Unit const* ToUnit() const { if (IsUnit()) return reinterpret_cast<Unit const*>(this); else return nullptr; } - GameObject* ToGameObject() { if (GetTypeId() == TYPEID_GAMEOBJECT) return reinterpret_cast<GameObject*>(this); else return NULL; } - GameObject const* ToGameObject() const { if (GetTypeId() == TYPEID_GAMEOBJECT) return reinterpret_cast<GameObject const*>(this); else return NULL; } + inline bool IsGameObject() const { return GetTypeId() == TYPEID_GAMEOBJECT; } + GameObject* ToGameObject() { if (IsGameObject()) return reinterpret_cast<GameObject*>(this); else return nullptr; } + GameObject const* ToGameObject() const { if (IsGameObject()) return reinterpret_cast<GameObject const*>(this); else return nullptr; } - Corpse* ToCorpse() { if (GetTypeId() == TYPEID_CORPSE) return reinterpret_cast<Corpse*>(this); else return NULL; } - Corpse const* ToCorpse() const { if (GetTypeId() == TYPEID_CORPSE) return reinterpret_cast<Corpse const*>(this); else return NULL; } + inline bool IsCorpse() const { return GetTypeId() == TYPEID_CORPSE; } + Corpse* ToCorpse() { if (IsCorpse()) return reinterpret_cast<Corpse*>(this); else return nullptr; } + Corpse const* ToCorpse() const { if (IsCorpse()) return reinterpret_cast<Corpse const*>(this); else return nullptr; } - DynamicObject* ToDynObject() { if (GetTypeId() == TYPEID_DYNAMICOBJECT) return reinterpret_cast<DynamicObject*>(this); else return NULL; } - DynamicObject const* ToDynObject() const { if (GetTypeId() == TYPEID_DYNAMICOBJECT) return reinterpret_cast<DynamicObject const*>(this); else return NULL; } + inline bool IsDynObject() const { return GetTypeId() == TYPEID_DYNAMICOBJECT; } + DynamicObject* ToDynObject() { if (IsDynObject()) return reinterpret_cast<DynamicObject*>(this); else return nullptr; } + DynamicObject const* ToDynObject() const { if (IsDynObject()) return reinterpret_cast<DynamicObject const*>(this); else return nullptr; } - AreaTrigger* ToAreaTrigger() { if (GetTypeId() == TYPEID_AREATRIGGER) return reinterpret_cast<AreaTrigger*>(this); else return NULL; } - AreaTrigger const* ToAreaTrigger() const { if (GetTypeId() == TYPEID_AREATRIGGER) return reinterpret_cast<AreaTrigger const*>(this); else return NULL; } + inline bool IsAreaTrigger() const { return GetTypeId() == TYPEID_AREATRIGGER; } + AreaTrigger* ToAreaTrigger() { if (IsAreaTrigger()) return reinterpret_cast<AreaTrigger*>(this); else return nullptr; } + AreaTrigger const* ToAreaTrigger() const { if (IsAreaTrigger()) return reinterpret_cast<AreaTrigger const*>(this); else return nullptr; } - Conversation* ToConversation() { if (GetTypeId() == TYPEID_CONVERSATION) return reinterpret_cast<Conversation*>(this); else return NULL; } - Conversation const* ToConversation() const { if (GetTypeId() == TYPEID_CONVERSATION) return reinterpret_cast<Conversation const*>(this); else return NULL; } + inline bool IsConversation() const { return GetTypeId() == TYPEID_CONVERSATION; } + Conversation* ToConversation() { if (GetTypeId() == TYPEID_CONVERSATION) return reinterpret_cast<Conversation*>(this); else return nullptr; } + Conversation const* ToConversation() const { if (GetTypeId() == TYPEID_CONVERSATION) return reinterpret_cast<Conversation const*>(this); else return nullptr; } protected: Object(); @@ -468,7 +476,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation virtual void SendMessageToSetInRange(WorldPacket const* data, float dist, bool self) const; virtual void SendMessageToSet(WorldPacket const* data, Player const* skipped_rcvr) const; - virtual uint8 getLevelForTarget(WorldObject const* /*target*/) const { return 1; } + virtual uint8 GetLevelForTarget(WorldObject const* /*target*/) const { return 1; } void PlayDistanceSound(uint32 soundId, Player* target = nullptr); void PlayDirectSound(uint32 soundId, Player* target = nullptr); diff --git a/src/server/game/Entities/Player/KillRewarder.cpp b/src/server/game/Entities/Player/KillRewarder.cpp index 1e89d90cec6..096dcadf61f 100644 --- a/src/server/game/Entities/Player/KillRewarder.cpp +++ b/src/server/game/Entities/Player/KillRewarder.cpp @@ -107,7 +107,7 @@ inline void KillRewarder::_InitGroupData() // 2.4. _maxNotGrayMember - maximum level of alive group member within reward distance, // for whom victim is not gray; uint32 grayLevel = Trinity::XP::GetGrayLevel(lvl); - if (_victim->getLevel() > grayLevel && (!_maxNotGrayMember || _maxNotGrayMember->getLevel() < lvl)) + if (_victim->GetLevelForTarget(member) > grayLevel && (!_maxNotGrayMember || _maxNotGrayMember->getLevel() < lvl)) _maxNotGrayMember = member; } // 2.5. _isFullXP - flag identifying that for all group members victim is not gray, diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index b0938712855..4da32995924 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -2334,6 +2334,19 @@ void Player::RemoveFromGroup(Group* group, ObjectGuid guid, RemoveMethod method group->RemoveMember(guid, method, kicker, reason); } +void Player::SetXP(uint32 xp) +{ + SetUInt32Value(PLAYER_XP, xp); + + int32 playerLevelDelta = 0; + + // If XP < 50%, player should see scaling creature with -1 level except for level max + if (getLevel() < MAX_LEVEL && xp < (GetUInt32Value(PLAYER_NEXT_LEVEL_XP) / 2)) + playerLevelDelta = -1; + + SetInt32Value(PLAYER_FIELD_SCALING_PLAYER_LEVEL_DELTA, playerLevelDelta); +} + void Player::GiveXP(uint32 xp, Unit* victim, float group_rate) { if (xp < 1) @@ -2389,7 +2402,7 @@ void Player::GiveXP(uint32 xp, Unit* victim, float group_rate) nextLvlXP = GetUInt32Value(PLAYER_NEXT_LEVEL_XP); } - SetUInt32Value(PLAYER_XP, newXP); + SetXP(newXP); } // Update player to next level @@ -6123,7 +6136,7 @@ void Player::RewardReputation(Unit* victim, float rate) if (Rep->RepFaction1 && (!Rep->TeamDependent || team == ALLIANCE)) { - int32 donerep1 = CalculateReputationGain(REPUTATION_SOURCE_KILL, victim->getLevel(), Rep->RepValue1, ChampioningFaction ? ChampioningFaction : Rep->RepFaction1); + int32 donerep1 = CalculateReputationGain(REPUTATION_SOURCE_KILL, victim->GetLevelForTarget(this), Rep->RepValue1, ChampioningFaction ? ChampioningFaction : Rep->RepFaction1); donerep1 = int32(donerep1 * rate); FactionEntry const* factionEntry1 = sFactionStore.LookupEntry(ChampioningFaction ? ChampioningFaction : Rep->RepFaction1); @@ -6134,7 +6147,7 @@ void Player::RewardReputation(Unit* victim, float rate) if (Rep->RepFaction2 && (!Rep->TeamDependent || team == HORDE)) { - int32 donerep2 = CalculateReputationGain(REPUTATION_SOURCE_KILL, victim->getLevel(), Rep->RepValue2, ChampioningFaction ? ChampioningFaction : Rep->RepFaction2); + int32 donerep2 = CalculateReputationGain(REPUTATION_SOURCE_KILL, victim->GetLevelForTarget(this), Rep->RepValue2, ChampioningFaction ? ChampioningFaction : Rep->RepFaction2); donerep2 = int32(donerep2 * rate); FactionEntry const* factionEntry2 = sFactionStore.LookupEntry(ChampioningFaction ? ChampioningFaction : Rep->RepFaction2); @@ -6272,7 +6285,7 @@ bool Player::RewardHonor(Unit* victim, uint32 groupsize, int32 honor, bool pvpto uint8 k_level = getLevel(); uint8 k_grey = Trinity::XP::GetGrayLevel(k_level); - uint8 v_level = victim->getLevel(); + uint8 v_level = victim->GetLevelForTarget(this); if (v_level <= k_grey) return false; @@ -17352,7 +17365,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) } SetUInt32Value(UNIT_FIELD_LEVEL, fields[6].GetUInt8()); - SetUInt32Value(PLAYER_XP, fields[7].GetUInt32()); + SetXP(fields[7].GetUInt32()); _LoadIntoDataField(fields[65].GetString(), PLAYER_EXPLORED_ZONES_1, PLAYER_EXPLORED_ZONES_SIZE); _LoadIntoDataField(fields[66].GetString(), PLAYER__FIELD_KNOWN_TITLES, KNOWN_TITLES_SIZE * 2); @@ -24571,7 +24584,7 @@ uint32 Player::GetResurrectionSpellId() const // Used in triggers for check "Only to targets that grant experience or honor" req bool Player::isHonorOrXPTarget(Unit const* victim) const { - uint8 v_level = victim->getLevel(); + uint8 v_level = victim->GetLevelForTarget(this); uint8 k_grey = Trinity::XP::GetGrayLevel(getLevel()); // Victim level less gray level diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index b96a44eb310..385b2ba6917 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1106,6 +1106,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> void SetGMVisible(bool on); void SetPvPDeath(bool on) { if (on) m_ExtraFlags |= PLAYER_EXTRA_PVP_DEATH; else m_ExtraFlags &= ~PLAYER_EXTRA_PVP_DEATH; } + void SetXP(uint32 xp); void GiveXP(uint32 xp, Unit* victim, float group_rate=1.0f); void GiveLevel(uint8 level); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 4d1c88b2a8b..17a51e9d5b2 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -691,7 +691,10 @@ void Unit::DealDamageMods(Unit* victim, uint32 &damage, uint32* absorb) if (absorb) *absorb += damage; damage = 0; + return; } + + damage *= GetDamageMultiplierForTarget(victim); } uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellInfo const* spellProto, bool durabilityLoss) @@ -839,6 +842,8 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam victim->ToCreature()->LowerPlayerDamageReq(health < damage ? health : damage); } + damage /= victim->GetHealthMultiplierForTarget(this); + if (health <= damage) { TC_LOG_DEBUG("entities.unit", "DealDamage: victim just died"); @@ -1443,7 +1448,7 @@ void Unit::DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss) // there is a newbie protection, at level 10 just 7% base chance; assuming linear function if (victim->getLevel() < 30) - Probability = 0.65f * victim->getLevel() + 0.5f; + Probability = 0.65f * victim->GetLevelForTarget(this) + 0.5f; uint32 VictimDefense = victim->GetMaxSkillValueForLevel(this); uint32 AttackerMeleeSkill = GetMaxSkillValueForLevel(); @@ -1554,6 +1559,8 @@ uint32 Unit::CalcArmorReducedDamage(Unit* attacker, Unit* victim, const uint32 d { float armor = float(victim->GetArmor()); + armor *= victim->GetArmorMultiplierForTarget(attacker); + // bypass enemy armor by SPELL_AURA_BYPASS_ARMOR_FOR_CASTER int32 armorBypassPct = 0; AuraEffectList const & reductionAuras = victim->GetAuraEffectsByType(SPELL_AURA_BYPASS_ARMOR_FOR_CASTER); @@ -1580,10 +1587,10 @@ uint32 Unit::CalcArmorReducedDamage(Unit* attacker, Unit* victim, const uint32 d if (GetTypeId() == TYPEID_PLAYER) { float maxArmorPen = 0; - if (victim->getLevel() < 60) - maxArmorPen = float(400 + 85 * victim->getLevel()); + if (victim->GetLevelForTarget(attacker) < 60) + maxArmorPen = float(400 + 85 * victim->GetLevelForTarget(attacker)); else - maxArmorPen = 400 + 85 * victim->getLevel() + 4.5f * 85 * (victim->getLevel() - 59); + maxArmorPen = 400 + 85 * victim->GetLevelForTarget(attacker) + 4.5f * 85 * (victim->GetLevelForTarget(attacker) - 59); // Cap armor penetration to this number maxArmorPen = std::min((armor + maxArmorPen) / 3, armor); @@ -1621,7 +1628,7 @@ uint32 Unit::CalcSpellResistance(Unit* victim, SpellSchoolMask schoolMask, Spell uint8 const bossLevel = 83; uint32 const bossResistanceConstant = 510; uint32 resistanceConstant = 0; - uint8 level = victim->getLevel(); + uint8 level = victim->GetLevelForTarget(this); if (level == bossLevel) resistanceConstant = bossResistanceConstant; @@ -2066,8 +2073,8 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst(Unit const* victim, WeaponAttackTy int32 sum = 0, tmp = 0; int32 roll = urand(0, 9999); - int32 attackerLevel = getLevelForTarget(victim); - int32 victimLevel = getLevelForTarget(this); + int32 attackerLevel = GetLevelForTarget(victim); + int32 victimLevel = GetLevelForTarget(this); // check if attack comes from behind, nobody can parry or block if attacker is behind bool canParryOrBlock = victim->HasInArc(float(M_PI), this) || victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION); @@ -2470,10 +2477,10 @@ SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo SpellSchoolMask schoolMask = spellInfo->GetSchoolMask(); // PvP - PvE spell misschances per leveldif > 2 int32 lchance = victim->GetTypeId() == TYPEID_PLAYER ? 7 : 11; - int32 thisLevel = getLevelForTarget(victim); + int32 thisLevel = GetLevelForTarget(victim); if (GetTypeId() == TYPEID_UNIT && ToCreature()->IsTrigger()) thisLevel = std::max<int32>(thisLevel, spellInfo->SpellLevel); - int32 leveldif = int32(victim->getLevelForTarget(this)) - thisLevel; + int32 leveldif = int32(victim->GetLevelForTarget(this)) - thisLevel; int32 levelBasedHitDiff = leveldif; // Base hit chance from attacker and victim levels @@ -2601,7 +2608,7 @@ SpellMissInfo Unit::SpellHitResult(Unit* victim, SpellInfo const* spellInfo, boo float Unit::GetUnitDodgeChance(WeaponAttackType attType, Unit const* victim) const { - int32 const levelDiff = victim->getLevelForTarget(this) - getLevelForTarget(victim); + int32 const levelDiff = victim->GetLevelForTarget(this) - GetLevelForTarget(victim); float chance = 0.0f; float levelBonus = 0.0f; @@ -2637,7 +2644,7 @@ float Unit::GetUnitDodgeChance(WeaponAttackType attType, Unit const* victim) con float Unit::GetUnitParryChance(WeaponAttackType attType, Unit const* victim) const { - int32 const levelDiff = victim->getLevelForTarget(this) - getLevelForTarget(victim); + int32 const levelDiff = victim->GetLevelForTarget(this) - GetLevelForTarget(victim); float chance = 0.0f; float levelBonus = 0.0f; @@ -2686,7 +2693,7 @@ float Unit::GetUnitMissChance(WeaponAttackType attType) const float Unit::GetUnitBlockChance(WeaponAttackType /*attType*/, Unit const* victim) const { - int32 const levelDiff = victim->getLevelForTarget(this) - getLevelForTarget(victim); + int32 const levelDiff = victim->GetLevelForTarget(this) - GetLevelForTarget(victim); float chance = 0.0f; float levelBonus = 0.0f; @@ -5052,6 +5059,11 @@ void Unit::SendSpellNonMeleeDamageLog(SpellNonMeleeDamage const* log) packet.Absorbed = log->absorb; packet.Periodic = log->periodicLog; packet.Flags = log->HitInfo; + + WorldPackets::Spells::SandboxScalingData sandboxScalingData; + if (sandboxScalingData.GenerateDataForUnits(log->attacker, log->target)) + packet.SandboxScaling = sandboxScalingData; + SendCombatLogMessage(&packet); } @@ -5086,6 +5098,11 @@ void Unit::SendPeriodicAuraLog(SpellPeriodicAuraLogInfo* info) spellLogEffect.Crit = info->critical; /// @todo: implement debug info + WorldPackets::Spells::SandboxScalingData sandboxScalingData; + if (Unit* caster = ObjectAccessor::GetUnit(*this, aura->GetCasterGUID())) + if (sandboxScalingData.GenerateDataForUnits(caster, this)) + spellLogEffect.SandboxScaling = sandboxScalingData; + data.Effects.push_back(spellLogEffect); SendCombatLogMessage(&data); @@ -5141,6 +5158,10 @@ void Unit::SendAttackStateUpdate(CalcDamageInfo* damageInfo) packet.LogData.Initialize(damageInfo->attacker); + WorldPackets::Spells::SandboxScalingData sandboxScalingData; + if (sandboxScalingData.GenerateDataForUnits(damageInfo->attacker, damageInfo->target)) + packet.SandboxScaling = sandboxScalingData; + SendCombatLogMessage(&packet); } @@ -11327,7 +11348,7 @@ Pet* Unit::CreateTamedPetFrom(Creature* creatureTarget, uint32 spell_id) return NULL; } - uint8 level = creatureTarget->getLevel() + 5 < getLevel() ? (getLevel() - 5) : creatureTarget->getLevel(); + uint8 level = creatureTarget->GetLevelForTarget(this) + 5 < getLevel() ? (getLevel() - 5) : creatureTarget->GetLevelForTarget(this); InitTamedPet(pet, level, spell_id); diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index cd56f883e7a..0bfc51f5c12 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1086,7 +1086,7 @@ class TC_GAME_API Unit : public WorldObject bool IsVehicle() const { return (m_unitTypeMask & UNIT_MASK_VEHICLE) != 0; } uint8 getLevel() const { return uint8(GetUInt32Value(UNIT_FIELD_LEVEL)); } - uint8 getLevelForTarget(WorldObject const* /*target*/) const override { return getLevel(); } + uint8 GetLevelForTarget(WorldObject const* /*target*/) const override { return getLevel(); } void SetLevel(uint8 lvl); uint8 getRace() const { return GetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_RACE); } uint32 getRaceMask() const { return 1 << (getRace()-1); } @@ -1121,6 +1121,10 @@ class TC_GAME_API Unit : public WorldObject int64 ModifyHealth(int64 val); int64 GetHealthGain(int64 dVal); + virtual float GetHealthMultiplierForTarget(WorldObject const* /*target*/) const { return 1.0f; } + virtual float GetDamageMultiplierForTarget(WorldObject const* /*target*/) const { return 1.0f; } + virtual float GetArmorMultiplierForTarget(WorldObject const* /*target*/) const { return 1.0f; } + Powers getPowerType() const { return Powers(GetUInt32Value(UNIT_FIELD_DISPLAY_POWER)); } void setPowerType(Powers power); void UpdateDisplayPower(); @@ -1190,7 +1194,7 @@ class TC_GAME_API Unit : public WorldObject void SetMeleeAnimKitId(uint16 animKitId); uint16 GetMeleeAnimKitId() const override { return _meleeAnimKitId; } - uint16 GetMaxSkillValueForLevel(Unit const* target = NULL) const { return (target ? getLevelForTarget(target) : getLevel()) * 5; } + uint16 GetMaxSkillValueForLevel(Unit const* target = NULL) const { return (target ? GetLevelForTarget(target) : getLevel()) * 5; } void DealDamageMods(Unit* victim, uint32 &damage, uint32* absorb); uint32 DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDamage = NULL, DamageEffectType damagetype = DIRECT_DAMAGE, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* spellProto = NULL, bool durabilityLoss = true); void Kill(Unit* victim, bool durabilityLoss = true); diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 2efdbfb015b..a506a7676cd 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -641,6 +641,45 @@ void ObjectMgr::LoadCreatureTemplateAddons() TC_LOG_INFO("server.loading", ">> Loaded %u creature template addons in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } +void ObjectMgr::LoadCreatureScalingData() +{ + uint32 oldMSTime = getMSTime(); + + // 0 1 + QueryResult result = WorldDatabase.Query("SELECT Entry, LevelScalingMin, LevelScalingMax, LevelScalingDelta FROM creature_template_scaling"); + + if (!result) + { + TC_LOG_INFO("server.loading", ">> Loaded 0 creature template scaling definitions. DB table `creature_template_scaling` is empty."); + return; + } + + uint32 count = 0; + do + { + Field* fields = result->Fetch(); + + uint32 entry = fields[0].GetUInt32(); + + CreatureTemplateContainer::iterator itr = _creatureTemplateStore.find(entry); + if (itr == _creatureTemplateStore.end()) + { + TC_LOG_ERROR("sql.sql", "Creature template (Entry: %u) does not exist but has a record in `creature_template_scaling`", entry); + continue; + } + + CreatureLevelScaling creatureLevelScaling; + creatureLevelScaling.MinLevel = fields[1].GetUInt16(); + creatureLevelScaling.MaxLevel = fields[2].GetUInt16(); + creatureLevelScaling.DeltaLevel = fields[3].GetInt16(); + itr->second.levelScaling = creatureLevelScaling; + + ++count; + } while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u creature template scaling data in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); +} + void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo) { if (!cInfo) diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 678d0df64e6..aad57743e82 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -1099,6 +1099,7 @@ class TC_GAME_API ObjectMgr void LoadCreatureTemplates(); void LoadCreatureTemplateAddons(); void LoadCreatureTemplate(Field* fields); + void LoadCreatureScalingData(); void CheckCreatureTemplate(CreatureTemplate const* cInfo); void LoadGameObjectQuestItems(); void LoadCreatureQuestItems(); diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index 5a98941d0d6..f010d60f291 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -298,7 +298,7 @@ void WorldSession::HandleCastSpellOpcode(WorldPackets::Spells::CastSpell& cast) // auto-selection buff level base at target level (in spellInfo) if (targets.GetUnitTarget()) { - SpellInfo const* actualSpellInfo = spellInfo->GetAuraRankForLevel(targets.GetUnitTarget()->getLevel()); + SpellInfo const* actualSpellInfo = spellInfo->GetAuraRankForLevel(targets.GetUnitTarget()->GetLevelForTarget(caster)); // if rank not found then function return NULL but in explicit cast case original spell can be cast and later failed with appropriate error message if (actualSpellInfo) diff --git a/src/server/game/Miscellaneous/Formulas.h b/src/server/game/Miscellaneous/Formulas.h index af750a5a3a6..70149995cd5 100644 --- a/src/server/game/Miscellaneous/Formulas.h +++ b/src/server/game/Miscellaneous/Formulas.h @@ -177,7 +177,7 @@ namespace Trinity { float xpMod = 1.0f; - gain = BaseGain(player->getLevel(), u->getLevel()); + gain = BaseGain(player->getLevel(), u->GetLevelForTarget(player)); if (gain && creature) { diff --git a/src/server/game/Server/Packets/CombatLogPacketsCommon.h b/src/server/game/Server/Packets/CombatLogPacketsCommon.h index 7f8d77dd73f..232b570c85e 100644 --- a/src/server/game/Server/Packets/CombatLogPacketsCommon.h +++ b/src/server/game/Server/Packets/CombatLogPacketsCommon.h @@ -49,15 +49,26 @@ namespace WorldPackets struct SandboxScalingData { + enum SandboxScalingDataType : uint32 + { + TYPE_PLAYER_TO_PLAYER = 1, // NYI + TYPE_CREATURE_TO_PLAYER_DAMAGE = 2, + TYPE_PLAYER_TO_CREATURE_DAMAGE = 3, + TYPE_CREATURE_TO_CREATURE_DAMAGE = 4 + }; + uint32 Type = 0; int16 PlayerLevelDelta = 0; uint16 PlayerItemLevel = 0; uint8 TargetLevel = 0; uint8 Expansion = 0; - uint8 Class = 1; - uint8 TargetMinScalingLevel = 1; - uint8 TargetMaxScalingLevel = 1; - int8 TargetScalingLevelDelta = 1; + uint8 Class = 0; + uint8 TargetMinScalingLevel = 0; + uint8 TargetMaxScalingLevel = 0; + int8 TargetScalingLevelDelta = 0; + + template<class T, class U> + bool GenerateDataForUnits(T* attacker, U* target); }; } diff --git a/src/server/game/Server/Packets/SpellPackets.cpp b/src/server/game/Server/Packets/SpellPackets.cpp index ffe17bfdffd..20f41f13e95 100644 --- a/src/server/game/Server/Packets/SpellPackets.cpp +++ b/src/server/game/Server/Packets/SpellPackets.cpp @@ -15,8 +15,10 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "SpellPackets.h" +#include "Creature.h" #include "MovementPackets.h" +#include "Player.h" +#include "SpellPackets.h" void WorldPackets::Spells::CancelAura::Read() { @@ -90,6 +92,90 @@ WorldPacket const* WorldPackets::Spells::SendUnlearnSpells::Write() return &_worldPacket; } +template<class T, class U> +bool WorldPackets::Spells::SandboxScalingData::GenerateDataForUnits(T* /*attacker*/, U* /*target*/) +{ + return false; +} + +template<> +bool WorldPackets::Spells::SandboxScalingData::GenerateDataForUnits<Creature, Player>(Creature* attacker, Player* target) +{ + CreatureTemplate const* creatureTemplate = attacker->GetCreatureTemplate(); + + Type = TYPE_CREATURE_TO_PLAYER_DAMAGE; + PlayerLevelDelta = target->GetInt32Value(PLAYER_FIELD_SCALING_PLAYER_LEVEL_DELTA); + PlayerItemLevel = target->GetAverageItemLevel(); + TargetLevel = target->getLevel(); + Expansion = creatureTemplate->RequiredExpansion; + Class = creatureTemplate->unit_class; + TargetMinScalingLevel = uint8(creatureTemplate->levelScaling->MinLevel); + TargetMaxScalingLevel = uint8(creatureTemplate->levelScaling->MaxLevel); + TargetScalingLevelDelta = int8(creatureTemplate->levelScaling->DeltaLevel); + return true; +} + +template<> +bool WorldPackets::Spells::SandboxScalingData::GenerateDataForUnits<Player, Creature>(Player* attacker, Creature* target) +{ + CreatureTemplate const* creatureTemplate = target->GetCreatureTemplate(); + + Type = TYPE_PLAYER_TO_CREATURE_DAMAGE; + PlayerLevelDelta = attacker->GetInt32Value(PLAYER_FIELD_SCALING_PLAYER_LEVEL_DELTA); + PlayerItemLevel = attacker->GetAverageItemLevel(); + TargetLevel = target->getLevel(); + Expansion = creatureTemplate->RequiredExpansion; + Class = creatureTemplate->unit_class; + TargetMinScalingLevel = uint8(creatureTemplate->levelScaling->MinLevel); + TargetMaxScalingLevel = uint8(creatureTemplate->levelScaling->MaxLevel); + TargetScalingLevelDelta = int8(creatureTemplate->levelScaling->DeltaLevel); + return true; +} + +template<> +bool WorldPackets::Spells::SandboxScalingData::GenerateDataForUnits<Creature, Creature>(Creature* attacker, Creature* target) +{ + CreatureTemplate const* creatureTemplate = target->HasScalableLevels() ? target->GetCreatureTemplate() : attacker->GetCreatureTemplate(); + + Type = TYPE_CREATURE_TO_CREATURE_DAMAGE; + PlayerLevelDelta = 0; + PlayerItemLevel = 0; + TargetLevel = target->getLevel(); + Expansion = creatureTemplate->RequiredExpansion; + Class = creatureTemplate->unit_class; + TargetMinScalingLevel = uint8(creatureTemplate->levelScaling->MinLevel); + TargetMaxScalingLevel = uint8(creatureTemplate->levelScaling->MaxLevel); + TargetScalingLevelDelta = int8(creatureTemplate->levelScaling->DeltaLevel); + return true; +} + +template<> +bool WorldPackets::Spells::SandboxScalingData::GenerateDataForUnits<Unit, Unit>(Unit* attacker, Unit* target) +{ + if (Player* playerAttacker = attacker->ToPlayer()) + { + if (Player* playerTarget = target->ToPlayer()) + return GenerateDataForUnits(playerAttacker, playerTarget); + else if (Creature* creatureTarget = target->ToCreature()) + { + if (creatureTarget->HasScalableLevels()) + return GenerateDataForUnits(playerAttacker, creatureTarget); + } + } + else if (Creature* creatureAttacker = attacker->ToCreature()) + { + if (Player* playerTarget = target->ToPlayer()) + return GenerateDataForUnits(creatureAttacker, playerTarget); + else if (Creature* creatureTarget = target->ToCreature()) + { + if (creatureAttacker->HasScalableLevels() || creatureTarget->HasScalableLevels()) + return GenerateDataForUnits(creatureAttacker, creatureTarget); + } + } + + return false; +} + ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::AuraDataInfo const& auraData) { data << auraData.CastID; diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index bb5bdc602da..44085cc5025 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -2122,7 +2122,7 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= if (m_auraScaleMask && ihit->effectMask == m_auraScaleMask && m_caster != target) { SpellInfo const* auraSpell = m_spellInfo->GetFirstRankSpell(); - if (uint32(target->getLevel() + 10) >= auraSpell->SpellLevel) + if (uint32(target->GetLevelForTarget(m_caster) + 10) >= auraSpell->SpellLevel) ihit->scaleAura = true; } return; @@ -2143,7 +2143,7 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= if (m_auraScaleMask && targetInfo.effectMask == m_auraScaleMask && m_caster != target) { SpellInfo const* auraSpell = m_spellInfo->GetFirstRankSpell(); - if (uint32(target->getLevel() + 10) >= auraSpell->SpellLevel) + if (uint32(target->GetLevelForTarget(m_caster) + 10) >= auraSpell->SpellLevel) targetInfo.scaleAura = true; } @@ -5302,7 +5302,7 @@ SpellCastResult Spell::CheckCast(bool strict) uint32 skill = creature->GetCreatureTemplate()->GetRequiredLootSkill(); int32 skillValue = m_caster->ToPlayer()->GetSkillValue(skill); - int32 TargetLevel = m_targets.GetUnitTarget()->getLevel(); + int32 TargetLevel = m_targets.GetUnitTarget()->GetLevelForTarget(m_caster); int32 ReqValue = (skillValue < 100 ? (TargetLevel-10) * 10 : TargetLevel * 5); if (ReqValue > skillValue) return SPELL_FAILED_LOW_CASTLEVEL; @@ -5614,7 +5614,7 @@ SpellCastResult Spell::CheckCast(bool strict) return SPELL_FAILED_TARGET_IS_PLAYER_CONTROLLED; int32 value = CalculateDamage(effect->EffectIndex, target); - if (value && int32(target->getLevel()) > value) + if (value && int32(target->GetLevelForTarget(m_caster)) > value) return SPELL_FAILED_HIGHLEVEL; } @@ -6773,7 +6773,7 @@ bool Spell::CheckEffectTarget(Unit const* target, SpellEffectInfo const* effect, if (!target->GetCharmerGUID().IsEmpty()) return false; if (int32 value = CalculateDamage(effect->EffectIndex, target)) - if ((int32)target->getLevel() > value) + if ((int32)target->GetLevelForTarget(m_caster) > value) return false; break; default: diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 3b1297d36c4..57202975230 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -2719,7 +2719,7 @@ void Spell::EffectTameCreature(SpellEffIndex /*effIndex*/) // "kill" original creature creatureTarget->DespawnOrUnsummon(); - uint8 level = (creatureTarget->getLevel() < (m_caster->getLevel() - 5)) ? (m_caster->getLevel() - 5) : creatureTarget->getLevel(); + uint8 level = (creatureTarget->GetLevelForTarget(m_caster) < (m_caster->GetLevelForTarget(creatureTarget) - 5)) ? (m_caster->GetLevelForTarget(creatureTarget) - 5) : creatureTarget->GetLevelForTarget(m_caster); // prepare visual effect for levelup pet->SetUInt32Value(UNIT_FIELD_LEVEL, level - 1); @@ -4279,7 +4279,7 @@ void Spell::EffectSkinning(SpellEffIndex /*effIndex*/) return; Creature* creature = unitTarget->ToCreature(); - int32 targetLevel = creature->getLevel(); + int32 targetLevel = creature->GetLevelForTarget(m_caster); uint32 skill = creature->GetCreatureTemplate()->GetRequiredLootSkill(); diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 78212696331..2692e0bbcff 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1713,6 +1713,9 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Creature template addons..."); sObjectMgr->LoadCreatureTemplateAddons(); + TC_LOG_INFO("server.loading", "Loading Creature template scaling..."); + sObjectMgr->LoadCreatureScalingData(); + TC_LOG_INFO("server.loading", "Loading Reputation Reward Rates..."); sObjectMgr->LoadReputationRewardRate(); |
