[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

@ -24,7 +24,7 @@ CREATE TABLE `db_version` (
`version` varchar(120) default NULL,
`creature_ai_version` varchar(120) default NULL,
`cache_id` int(10) default '0',
`required_11018_01_mangos_command` bit(1) default NULL
`required_11023_01_mangos_spell_threat` bit(1) default NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes';
--
@ -17629,6 +17629,8 @@ DROP TABLE IF EXISTS `spell_threat`;
CREATE TABLE `spell_threat` (
`entry` mediumint(8) unsigned NOT NULL,
`Threat` smallint(6) NOT NULL,
`multiplier` float NOT NULL default '1' COMMENT 'threat multiplier for damage/healing',
`ap_bonus` float NOT NULL default '0' COMMENT 'additional threat bonus from attack power',
PRIMARY KEY (`entry`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED;
@ -17639,123 +17641,123 @@ CREATE TABLE `spell_threat` (
LOCK TABLES `spell_threat` WRITE;
/*!40000 ALTER TABLE `spell_threat` DISABLE KEYS */;
INSERT INTO `spell_threat` VALUES
(72,293),
(78,20),
(99,42),
(284,39),
(285,59),
(469,40),
(676,104),
(770,108),
(845,10),
(1160,16),
(1608,78),
(1672,180),
(1715,61),
(1735,42),
(2048,70),
(2139,300),
(5242,26),
(6190,26),
(6192,32),
(6343,17),
(6572,155),
(6673,18),
(6574,195),
(6807,322),
(6808,322),
(6809,322),
(7369,40),
(7372,101),
(7373,141),
(7379,235),
(7386,100),
(8198,40),
(8204,64),
(8205,96),
(8972,322),
(9490,42),
(9745,322),
(9747,42),
(9880,322),
(9881,322),
(9898,42),
(11549,40),
(11550,48),
(11551,56),
(11554,30),
(11555,37),
(11556,43),
(11564,98),
(11565,118),
(11566,137),
(11567,145),
(11580,143),
(11581,180),
(11600,275),
(11601,315),
(11608,60),
(11609,70),
(11775,395),
(14921,415),
(16857,108),
(17735,200),
(17750,300),
(17751,450),
(17752,600),
(20569,100),
(20736,100),
(20925,20),
(20927,30),
(20928,40),
(23922,160),
(23923,190),
(23924,220),
(23925,250),
(24394,580),
(24583,5),
(25202,50),
(25203,55),
(25231,130),
(25258,286),
(25264,215),
(25269,400),
(25286,175),
(25288,355),
(25289,62),
(26996,322),
(26998,42),
(27179,54),
(29704,230),
(29707,196),
(30324,220),
(30356,323),
(30357,483),
(33745,285),
(33878,129),
(33986,180),
(33987,232),
(47436,78),
(47437,63),
(47439,60),
(47440,80),
(47449,236),
(47450,259),
(47487,520),
(47488,770),
(47497,101),
(47498,101),
(47501,235),
(47502,260),
(47519,180),
(47520,225),
(48479,322),
(48480,322),
(48559,42),
(48560,42),
(48567,285),
(48568,285),
(57823,500);
( 72, 293, 1, 0),
( 78, 20, 1, 0),
( 99, 42, 1, 0),
( 284, 39, 1, 0),
( 285, 59, 1, 0),
( 469, 40, 1, 0),
( 676, 104, 1, 0),
( 770, 108, 1, 0),
( 845, 10, 1, 0),
( 1160, 16, 1, 0),
( 1608, 78, 1, 0),
( 1672, 180, 1, 0),
( 1715, 61, 1, 0),
( 1735, 42, 1, 0),
( 2048, 70, 1, 0),
( 2139, 300, 1, 0),
( 5242, 26, 1, 0),
( 6190, 26, 1, 0),
( 6192, 32, 1, 0),
( 6343, 17, 1, 0),
( 6572, 155, 1, 0),
( 6673, 18, 1, 0),
( 6574, 195, 1, 0),
( 6807, 322, 1, 0),
( 6808, 322, 1, 0),
( 6809, 322, 1, 0),
( 7369, 40, 1, 0),
( 7372, 101, 1, 0),
( 7373, 141, 1, 0),
( 7379, 235, 1, 0),
( 7386, 100, 1, 0),
( 8198, 40, 1, 0),
( 8204, 64, 1, 0),
( 8205, 96, 1, 0),
( 8972, 322, 1, 0),
( 9490, 42, 1, 0),
( 9745, 322, 1, 0),
( 9747, 42, 1, 0),
( 9880, 322, 1, 0),
( 9881, 322, 1, 0),
( 9898, 42, 1, 0),
(11549, 40, 1, 0),
(11550, 48, 1, 0),
(11551, 56, 1, 0),
(11554, 30, 1, 0),
(11555, 37, 1, 0),
(11556, 43, 1, 0),
(11564, 98, 1, 0),
(11565, 118, 1, 0),
(11566, 137, 1, 0),
(11567, 145, 1, 0),
(11580, 143, 1, 0),
(11581, 180, 1, 0),
(11600, 275, 1, 0),
(11601, 315, 1, 0),
(11608, 60, 1, 0),
(11609, 70, 1, 0),
(11775, 395, 1, 0),
(14921, 415, 1, 0),
(16857, 108, 1, 0),
(17735, 200, 1, 0),
(17750, 300, 1, 0),
(17751, 450, 1, 0),
(17752, 600, 1, 0),
(20569, 100, 1, 0),
(20736, 100, 1, 0),
(20925, 20, 1, 0),
(20927, 30, 1, 0),
(20928, 40, 1, 0),
(23922, 160, 1, 0),
(23923, 190, 1, 0),
(23924, 220, 1, 0),
(23925, 250, 1, 0),
(24394, 580, 1, 0),
(24583, 5, 1, 0),
(25202, 50, 1, 0),
(25203, 55, 1, 0),
(25231, 130, 1, 0),
(25258, 286, 1, 0),
(25264, 215, 1, 0),
(25269, 400, 1, 0),
(25286, 175, 1, 0),
(25288, 355, 1, 0),
(25289, 62, 1, 0),
(26996, 322, 1, 0),
(26998, 42, 1, 0),
(27179, 54, 1, 0),
(29704, 230, 1, 0),
(29707, 196, 1, 0),
(30324, 220, 1, 0),
(30356, 323, 1, 0),
(30357, 483, 1, 0),
(33745, 285, 1, 0),
(33878, 129, 1, 0),
(33986, 180, 1, 0),
(33987, 232, 1, 0),
(47436, 78, 1, 0),
(47437, 63, 1, 0),
(47439, 60, 1, 0),
(47440, 80, 1, 0),
(47449, 236, 1, 0),
(47450, 259, 1, 0),
(47487, 520, 1, 0),
(47488, 770, 1, 0),
(47497, 101, 1, 0),
(47498, 101, 1, 0),
(47501, 235, 1, 0),
(47502, 260, 1, 0),
(47519, 180, 1, 0),
(47520, 225, 1, 0),
(48479, 322, 1, 0),
(48480, 322, 1, 0),
(48559, 42, 1, 0),
(48560, 42, 1, 0),
(48567, 285, 1, 0),
(48568, 285, 1, 0),
(57823, 500, 1, 0);
/*!40000 ALTER TABLE `spell_threat` ENABLE KEYS */;
UNLOCK TABLES;

View file

@ -0,0 +1,5 @@
ALTER TABLE db_version CHANGE COLUMN required_11018_01_mangos_command required_11023_01_mangos_spell_threat bit;
ALTER TABLE spell_threat ADD COLUMN multiplier FLOAT NOT NULL DEFAULT 1.0 COMMENT 'threat multiplier for damage/healing' AFTER Threat;
ALTER TABLE spell_threat ADD COLUMN ap_bonus FLOAT NOT NULL DEFAULT 0.0 COMMENT 'additional threat bonus from attack power' AFTER multiplier;

View file

@ -153,6 +153,7 @@ pkgdata_DATA = \
10998_01_mangos_spell_proc_event.sql \
11002_01_mangos_spell_proc_event.sql \
11018_01_mangos_command.sql \
11023_01_mangos_spell_threat.sql \
README
## Additional files to include when running 'make dist'
@ -286,4 +287,5 @@ EXTRA_DIST = \
10998_01_mangos_spell_proc_event.sql \
11002_01_mangos_spell_proc_event.sql \
11018_01_mangos_command.sql \
11023_01_mangos_spell_threat.sql \
README

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

@ -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);

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__