[11023] Implement support for threat multiplier and AP based threat bonus for spells.

* Higher ranks are now automatically filled when not listed in spell_threat
* Added some loading checks to detect inconsistent data

Signed-off-by: Lynx3d <lynx3d@some-imaginary-isp.org>
This commit is contained in:
x3n 2011-01-17 11:54:13 +01:00 committed by Lynx3d
parent 77d8b41cc4
commit d35be7f4c1
12 changed files with 238 additions and 177 deletions

View file

@ -1023,7 +1023,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target)
int32 gain = caster->DealHeal(unitTarget, addhealth, m_spellInfo, crit, absorb);
if (real_caster)
unitTarget->getHostileRefManager().threatAssist(real_caster, float(gain) * 0.5f, m_spellInfo);
unitTarget->getHostileRefManager().threatAssist(real_caster, float(gain) * 0.5f * sSpellMgr.GetSpellThreatMultiplier(m_spellInfo), m_spellInfo);
}
// Do damage and triggers
else if (m_damage)
@ -4225,14 +4225,18 @@ void Spell::HandleThreatSpells(uint32 spellId)
if(!m_targets.getUnitTarget()->CanHaveThreatList())
return;
uint16 threat = sSpellMgr.GetSpellThreat(spellId);
SpellThreatEntry const* threatEntry = sSpellMgr.GetSpellThreatEntry(spellId);
if(!threat)
if(!threatEntry || (!threatEntry->threat && threatEntry->ap_bonus == 0.0f))
return;
m_targets.getUnitTarget()->AddThreat(m_caster, float(threat), false, GetSpellSchoolMask(m_spellInfo), m_spellInfo);
float threat = threatEntry->threat;
if (threatEntry->ap_bonus != 0.0f)
threat += threatEntry->ap_bonus * m_caster->GetTotalAttackPowerValue(GetWeaponAttackType(m_spellInfo));
DEBUG_FILTER_LOG(LOG_FILTER_SPELL_CAST, "Spell %u, rank %u, added an additional %i threat", spellId, sSpellMgr.GetSpellRank(spellId), threat);
m_targets.getUnitTarget()->AddThreat(m_caster, threat, false, GetSpellSchoolMask(m_spellInfo), m_spellInfo);
DEBUG_FILTER_LOG(LOG_FILTER_SPELL_CAST, "Spell %u, rank %u, added an additional %f threat", spellId, sSpellMgr.GetSpellRank(spellId), threat);
}
void Spell::HandleEffects(Unit *pUnitTarget,Item *pItemTarget,GameObject *pGOTarget,SpellEffectIndex i, float DamageMultiplier)

View file

@ -6879,7 +6879,7 @@ void Aura::PeriodicTick()
pCaster->CalculateHealAbsorb(heal, &absorbHeal);
int32 gain = pCaster->DealHeal(pCaster, heal - absorbHeal, spellProto, false, absorbHeal);
pCaster->getHostileRefManager().threatAssist(pCaster, gain * 0.5f, spellProto);
pCaster->getHostileRefManager().threatAssist(pCaster, gain * 0.5f * sSpellMgr.GetSpellThreatMultiplier(spellProto), spellProto);
break;
}
case SPELL_AURA_PERIODIC_HEAL:
@ -6951,7 +6951,7 @@ void Aura::PeriodicTick()
if( BattleGround *bg = ((Player*)pCaster)->GetBattleGround() )
bg->UpdatePlayerScore(((Player*)pCaster), SCORE_HEALING_DONE, gain);
target->getHostileRefManager().threatAssist(pCaster, float(gain) * 0.5f, spellProto);
target->getHostileRefManager().threatAssist(pCaster, float(gain) * 0.5f * sSpellMgr.GetSpellThreatMultiplier(spellProto), spellProto);
// heal for caster damage
if(target != pCaster && spellProto->SpellVisual[0] == 163)
@ -7091,7 +7091,7 @@ void Aura::PeriodicTick()
int32 gain = target->ModifyPower(power,pdamage);
if(Unit* pCaster = GetCaster())
target->getHostileRefManager().threatAssist(pCaster, float(gain) * 0.5f, spellProto);
target->getHostileRefManager().threatAssist(pCaster, float(gain) * 0.5f * sSpellMgr.GetSpellThreatMultiplier(spellProto), spellProto);
break;
}
case SPELL_AURA_OBS_MOD_MANA:
@ -7117,7 +7117,7 @@ void Aura::PeriodicTick()
int32 gain = target->ModifyPower(POWER_MANA, pdamage);
if(Unit* pCaster = GetCaster())
target->getHostileRefManager().threatAssist(pCaster, float(gain) * 0.5f, spellProto);
target->getHostileRefManager().threatAssist(pCaster, float(gain) * 0.5f * sSpellMgr.GetSpellThreatMultiplier(spellProto), spellProto);
break;
}
case SPELL_AURA_POWER_BURN_MANA:
@ -7179,7 +7179,7 @@ void Aura::PeriodicTick()
int32 gain = target->ModifyHealth(m_modifier.m_amount);
if (Unit *caster = GetCaster())
target->getHostileRefManager().threatAssist(caster, float(gain) * 0.5f, spellProto);
target->getHostileRefManager().threatAssist(caster, float(gain) * 0.5f * sSpellMgr.GetSpellThreatMultiplier(spellProto), spellProto);
break;
}
case SPELL_AURA_MOD_POWER_REGEN:

View file

@ -3538,7 +3538,7 @@ void Spell::EffectHealPct(SpellEffectIndex /*eff_idx*/)
unitTarget->CalculateHealAbsorb(addhealth, &absorb);
int32 gain = caster->DealHeal(unitTarget, addhealth - absorb, m_spellInfo, false, absorb);
unitTarget->getHostileRefManager().threatAssist(caster, float(gain) * 0.5f, m_spellInfo);
unitTarget->getHostileRefManager().threatAssist(caster, float(gain) * 0.5f * sSpellMgr.GetSpellThreatMultiplier(m_spellInfo), m_spellInfo);
}
}

View file

@ -1636,26 +1636,77 @@ void SpellMgr::LoadSpellElixirs()
sLog.outString( ">> Loaded %u spell elixir definitions", count );
}
struct DoSpellThreat
{
DoSpellThreat(SpellThreatMap& _threatMap) : threatMap(_threatMap), count(0) {}
void operator() (uint32 spell_id)
{
SpellThreatEntry const &ste = state->second;
// add ranks only for not filled data (spells adding flat threat are usually different for ranks)
SpellThreatMap::const_iterator spellItr = threatMap.find(spell_id);
if (spellItr == threatMap.end())
threatMap[spell_id] = ste;
// just assert that entry is not redundant
else
{
SpellThreatEntry const& r_ste = spellItr->second;
if (ste.threat == r_ste.threat && ste.multiplier == r_ste.multiplier && ste.ap_bonus == r_ste.ap_bonus)
sLog.outErrorDb("Spell %u listed in `spell_threat` as custom rank has same data as Rank 1, so redundant", spell_id);
}
}
const char* TableName() { return "spell_threat"; }
bool IsValidCustomRank(SpellThreatEntry const &ste, uint32 entry, uint32 first_id)
{
if (!ste.threat)
{
sLog.outErrorDb("Spell %u listed in `spell_threat` is not first rank (%u) in chain and has no threat", entry, first_id);
// prevent loading unexpected data
return false;
}
return true;
}
void AddEntry(SpellThreatEntry const &ste, SpellEntry const *spell)
{
threatMap[spell->Id] = ste;
// flat threat bonus and attack power bonus currently only work properly when all
// effects have same targets, otherwise, we'd need to seperate it by effect index
if (ste.threat || ste.ap_bonus != 0.f)
{
const uint32 *targetA = spell->EffectImplicitTargetA;
const uint32 *targetB = spell->EffectImplicitTargetB;
if ((targetA[EFFECT_INDEX_1] && targetA[EFFECT_INDEX_1] != targetA[EFFECT_INDEX_0]) ||
(targetA[EFFECT_INDEX_2] && targetA[EFFECT_INDEX_2] != targetA[EFFECT_INDEX_0]))
sLog.outErrorDb("Spell %u listed in `spell_threat` has effects with different targets, threat may be assigned incorrectly", spell->Id);
}
++count;
}
bool HasEntry(uint32 spellId) { return threatMap.count(spellId) > 0; }
bool SetStateToEntry(uint32 spellId) { return (state = threatMap.find(spellId)) != threatMap.end(); }
SpellThreatMap& threatMap;
SpellThreatMap::const_iterator state;
uint32 count;
};
void SpellMgr::LoadSpellThreats()
{
mSpellThreatMap.clear(); // need for reload case
uint32 count = 0;
// 0 1
QueryResult *result = WorldDatabase.Query("SELECT entry, Threat FROM spell_threat");
// 0 1 2 3
QueryResult *result = WorldDatabase.Query("SELECT entry, Threat, multiplier, ap_bonus FROM spell_threat");
if( !result )
{
barGoLink bar( 1 );
bar.step();
sLog.outString();
sLog.outString( ">> Loaded %u aggro generating spells", count );
sLog.outString( ">> No spell threat entries loaded.");
return;
}
SpellRankHelper<SpellThreatEntry, DoSpellThreat, SpellThreatMap> rankHelper(*this, mSpellThreatMap);
barGoLink bar( (int)result->GetRowCount() );
do
@ -1665,23 +1716,22 @@ void SpellMgr::LoadSpellThreats()
bar.step();
uint32 entry = fields[0].GetUInt32();
uint16 Threat = fields[1].GetUInt16();
if (!sSpellStore.LookupEntry(entry))
{
sLog.outErrorDb("Spell %u listed in `spell_threat` does not exist", entry);
continue;
}
SpellThreatEntry ste;
ste.threat = fields[1].GetUInt16();
ste.multiplier = fields[2].GetFloat();
ste.ap_bonus = fields[3].GetFloat();
mSpellThreatMap[entry] = Threat;
rankHelper.RecordRank(ste, entry);
++count;
} while( result->NextRow() );
rankHelper.FillHigherRanks();
delete result;
sLog.outString();
sLog.outString( ">> Loaded %u aggro generating spells", count );
sLog.outString( ">> Loaded %u spell threat entries", rankHelper.worker.count );
}
bool SpellMgr::IsRankSpellDueToSpell(SpellEntry const *spellInfo_1,uint32 spellId_2) const

View file

@ -622,9 +622,16 @@ typedef UNORDERED_MAP<uint32, SpellBonusEntry> SpellBonusMap;
#define ELIXIR_SHATTRATH_MASK 0x08
#define ELIXIR_WELL_FED 0x10 // Some foods have SPELLFAMILY_POTION
struct SpellThreatEntry
{
uint16 threat;
float multiplier;
float ap_bonus;
};
typedef std::map<uint32, uint8> SpellElixirMap;
typedef std::map<uint32, float> SpellProcItemEnchantMap;
typedef std::map<uint32, uint16> SpellThreatMap;
typedef std::map<uint32, SpellThreatEntry> SpellThreatMap;
// Spell script target related declarations (accessed using SpellMgr functions)
enum SpellTargetType
@ -846,13 +853,24 @@ class SpellMgr
return SPELL_NORMAL;
}
uint16 GetSpellThreat(uint32 spellid) const
SpellThreatEntry const* GetSpellThreatEntry(uint32 spellid) const
{
SpellThreatMap::const_iterator itr = mSpellThreatMap.find(spellid);
if(itr==mSpellThreatMap.end())
return 0;
if (itr != mSpellThreatMap.end())
return &itr->second;
return itr->second;
return NULL;
}
float GetSpellThreatMultiplier(SpellEntry const *spellInfo) const
{
if (!spellInfo)
return 1.0f;
if (SpellThreatEntry const *entry = GetSpellThreatEntry(spellInfo->Id))
return entry->multiplier;
return 1.0f;
}
// Spell proc events

View file

@ -293,7 +293,7 @@ void Unit::Update( uint32 update_diff, uint32 p_time )
{
if(!IsInWorld())
return;
/*if(p_time > m_AurasCheck)
{
m_AurasCheck = 2000;
@ -953,10 +953,8 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa
}
if (pVictim->GetTypeId() != TYPEID_PLAYER)
{
if(spellProto && IsDamageToThreatSpell(spellProto))
pVictim->AddThreat(this, float(damage*2), (cleanDamage && cleanDamage->hitOutCome == MELEE_HIT_CRIT), damageSchoolMask, spellProto);
else
pVictim->AddThreat(this, float(damage), (cleanDamage && cleanDamage->hitOutCome == MELEE_HIT_CRIT), damageSchoolMask, spellProto);
float threat = damage * sSpellMgr.GetSpellThreatMultiplier(spellProto);
pVictim->AddThreat(this, threat, (cleanDamage && cleanDamage->hitOutCome == MELEE_HIT_CRIT), damageSchoolMask, spellProto);
}
else // victim is a player
{
@ -7185,22 +7183,6 @@ bool Unit::IsImmuneToSpellEffect(SpellEntry const* spellInfo, SpellEffectIndex i
return false;
}
bool Unit::IsDamageToThreatSpell(SpellEntry const * spellInfo) const
{
if (!spellInfo)
return false;
uint32 family = spellInfo->SpellFamilyName;
uint64 flags = spellInfo->SpellFamilyFlags;
if ((family == 5 && flags == 256) || //Searing Pain
(family == 6 && flags == 8192) || //Mind Blast
(family == 11 && flags == 1048576)) //Earth Shock
return true;
return false;
}
/**
* Calculates caster part of melee damage bonuses,
* also includes different bonuses dependent from target auras

View file

@ -1450,8 +1450,6 @@ class MANGOS_DLL_SPEC Unit : public WorldObject
void CastSpell(float x, float y, float z, uint32 spellId, bool triggered, Item *castItem = NULL, Aura* triggeredByAura = NULL, ObjectGuid originalCaster = ObjectGuid(), SpellEntry const* triggeredBy = NULL);
void CastSpell(float x, float y, float z, SpellEntry const *spellInfo, bool triggered, Item *castItem = NULL, Aura* triggeredByAura = NULL, ObjectGuid originalCaster = ObjectGuid(), SpellEntry const* triggeredBy = NULL);
bool IsDamageToThreatSpell(SpellEntry const * spellInfo) const;
void DeMorph();
void SendAttackStateUpdate(CalcDamageInfo *damageInfo);
@ -2151,4 +2149,4 @@ inline void Unit::SendMonsterMoveByPath(Path<Elem,Node> const& path, uint32 star
SendMessageToSet(&data, true);
}
#endif
#endif

View file

@ -1,4 +1,4 @@
#ifndef __REVISION_NR_H__
#define __REVISION_NR_H__
#define REVISION_NR "11022"
#define REVISION_NR "11023"
#endif // __REVISION_NR_H__

View file

@ -1,6 +1,6 @@
#ifndef __REVISION_SQL_H__
#define __REVISION_SQL_H__
#define REVISION_DB_CHARACTERS "required_10973_01_characters_game_event_status"
#define REVISION_DB_MANGOS "required_11018_01_mangos_command"
#define REVISION_DB_MANGOS "required_11023_01_mangos_spell_threat"
#define REVISION_DB_REALMD "required_10008_01_realmd_realmd_db_version"
#endif // __REVISION_SQL_H__