mirror of
https://github.com/mangosfour/server.git
synced 2025-12-18 01:37:01 +00:00
[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:
parent
77d8b41cc4
commit
d35be7f4c1
12 changed files with 238 additions and 177 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#ifndef __REVISION_NR_H__
|
||||
#define __REVISION_NR_H__
|
||||
#define REVISION_NR "11022"
|
||||
#define REVISION_NR "11023"
|
||||
#endif // __REVISION_NR_H__
|
||||
|
|
|
|||
|
|
@ -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__
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue