*Move spell rank extractor out of core, use sql table instead for easier maintaining of dependant database data.

--HG--
branch : trunk
This commit is contained in:
QAston
2010-01-29 17:54:29 +01:00
parent 39a260d10d
commit 9e31e08358
6 changed files with 13954 additions and 309 deletions
File diff suppressed because it is too large Load Diff
+6920
View File
File diff suppressed because it is too large Load Diff
+35 -35
View File
@@ -1421,41 +1421,41 @@ struct SpellEntry
int32 EffectMiscValueB[MAX_SPELL_EFFECTS]; // 119-121 m_effectMiscValueB
uint32 EffectTriggerSpell[MAX_SPELL_EFFECTS]; // 122-124 m_effectTriggerSpell
float EffectPointsPerComboPoint[MAX_SPELL_EFFECTS]; // 125-127 m_effectPointsPerCombo
flag96 EffectSpellClassMask[MAX_SPELL_EFFECTS]; // 127-133
uint32 SpellVisual[2]; // 134-135 m_spellVisualID
uint32 SpellIconID; // 136 m_spellIconID
uint32 activeIconID; // 137 m_activeIconID
//uint32 spellPriority; // 138 not used
char* SpellName[16]; // 139-154 m_name_lang
//uint32 SpellNameFlag; // 155 not used
char* Rank[16]; // 156-171 m_nameSubtext_lang
//uint32 RankFlags; // 172 not used
//char* Description[16]; // 173-188 m_description_lang not used
//uint32 DescriptionFlags; // 189 not used
//char* ToolTip[16]; // 190-205 m_auraDescription_lang not used
//uint32 ToolTipFlags; // 206 not used
uint32 ManaCostPercentage; // 207 m_manaCostPct
uint32 StartRecoveryCategory; // 208 m_startRecoveryCategory
uint32 StartRecoveryTime; // 209 m_startRecoveryTime
uint32 MaxTargetLevel; // 210 m_maxTargetLevel
uint32 SpellFamilyName; // 211 m_spellClassSet
flag96 SpellFamilyFlags; // 212-214
uint32 MaxAffectedTargets; // 215 m_maxTargets
uint32 DmgClass; // 216 m_defenseType
uint32 PreventionType; // 217 m_preventionType
//uint32 StanceBarOrder; // 218 m_stanceBarOrder not used
float DmgMultiplier[3]; // 219-221 m_effectChainAmplitude
//uint32 MinFactionId; // 222 m_minFactionID not used
//uint32 MinReputation; // 223 m_minReputation not used
//uint32 RequiredAuraVision; // 224 m_requiredAuraVision not used
uint32 TotemCategory[2]; // 225-226 m_requiredTotemCategoryID
int32 AreaGroupId; // 227 m_requiredAreaGroupId
uint32 SchoolMask; // 228 m_schoolMask
uint32 runeCostID; // 229 m_runeCostID
//uint32 spellMissileID; // 230 m_spellMissileID not used
//uint32 PowerDisplayId; // 231 PowerDisplay.dbc, new in 3.1
//float unk_320_4[3]; // 232-234 3.2.0
//uint32 spellDescriptionVariableID; // 235 3.2.0
flag96 EffectSpellClassMask[MAX_SPELL_EFFECTS]; // 127-136
uint32 SpellVisual[2]; // 137-138 m_spellVisualID
uint32 SpellIconID; // 139 m_spellIconID
uint32 activeIconID; // 140 m_activeIconID
//uint32 spellPriority; // 141 not used
char* SpellName[16]; // 142-157 m_name_lang
//uint32 SpellNameFlag; // 158 not used
char* Rank[16]; // 159-174 m_nameSubtext_lang
//uint32 RankFlags; // 175 not used
//char* Description[16]; // 176-191 m_description_lang not used
//uint32 DescriptionFlags; // 192 not used
//char* ToolTip[16]; // 193-208 m_auraDescription_lang not used
//uint32 ToolTipFlags; // 209 not used
uint32 ManaCostPercentage; // 210 m_manaCostPct
uint32 StartRecoveryCategory; // 211 m_startRecoveryCategory
uint32 StartRecoveryTime; // 212 m_startRecoveryTime
uint32 MaxTargetLevel; // 213 m_maxTargetLevel
uint32 SpellFamilyName; // 214 m_spellClassSet
flag96 SpellFamilyFlags; // 215-217
uint32 MaxAffectedTargets; // 218 m_maxTargets
uint32 DmgClass; // 219 m_defenseType
uint32 PreventionType; // 220 m_preventionType
//uint32 StanceBarOrder; // 221 m_stanceBarOrder not used
float DmgMultiplier[3]; // 222-224 m_effectChainAmplitude
//uint32 MinFactionId; // 225 m_minFactionID not used
//uint32 MinReputation; // 226 m_minReputation not used
//uint32 RequiredAuraVision; // 227 m_requiredAuraVision not used
uint32 TotemCategory[2]; // 228-229 m_requiredTotemCategoryID
int32 AreaGroupId; // 230 m_requiredAreaGroupId
uint32 SchoolMask; // 231 m_schoolMask
uint32 runeCostID; // 232 m_runeCostID
//uint32 spellMissileID; // 233 m_spellMissileID not used
//uint32 PowerDisplayId; // 234 PowerDisplay.dbc, new in 3.1
//float unk_320_4[3]; // 235-237 3.2.0
//uint32 spellDescriptionVariableID; // 238 3.2.0
// helpers
int32 CalculateSimpleValue(uint8 eff) const { return EffectBasePoints[eff]+int32(EffectBaseDice[eff]); }
+88 -270
View File
@@ -3231,302 +3231,120 @@ void SpellMgr::LoadSpellRequired()
sLog.outString( ">> Loaded %u spell required records", rows );
}
struct SpellRankEntry
{
uint32 SkillId;
char const *SpellName;
uint32 DurationIndex;
uint32 RangeIndex;
uint32 SpellVisual;
uint32 ProcFlags;
flag96 SpellFamilyFlags;
uint32 TargetAuraState;
uint32 ManaCost;
uint32 CastingTimeIndex;
flag96 Effect;
flag96 Aura;
uint16 TalentID;
bool operator < (const SpellRankEntry & _Right) const
{
return (SkillId != _Right.SkillId ? SkillId < _Right.SkillId
: SpellName != _Right.SpellName ? SpellName < _Right.SpellName
: ProcFlags != _Right.ProcFlags ? ProcFlags < _Right.ProcFlags
: Effect != _Right.Effect ? Effect < _Right.Effect
: Aura != _Right.Aura ? Aura < _Right.Aura
: TalentID != _Right.TalentID ? TalentID < _Right.TalentID
: (CastingTimeIndex != _Right.CastingTimeIndex) && (!CastingTimeIndex || !_Right.CastingTimeIndex || CastingTimeIndex == 1 || !_Right.CastingTimeIndex == 1) ? CastingTimeIndex < _Right.CastingTimeIndex
: SpellFamilyFlags != _Right.SpellFamilyFlags ? SpellFamilyFlags < _Right.SpellFamilyFlags
: (SpellVisual != _Right.SpellVisual) && (!SpellVisual || !_Right.SpellVisual) ? SpellVisual < _Right.SpellVisual
: (ManaCost != _Right.ManaCost) && (!ManaCost || !_Right.ManaCost) ? ManaCost < _Right.ManaCost
: (DurationIndex != _Right.DurationIndex) && (!DurationIndex || !_Right.DurationIndex)? DurationIndex < _Right.DurationIndex
: (RangeIndex != _Right.RangeIndex) && (!RangeIndex || !_Right.RangeIndex || RangeIndex == 1 || !_Right.RangeIndex == 1) ? RangeIndex < _Right.RangeIndex
: TargetAuraState < _Right.TargetAuraState
);
}
};
struct SpellRankValue
{
uint32 Id;
char const *Rank;
bool strict;
};
void SpellMgr::LoadSpellChains()
void SpellMgr::LoadSpellRanks()
{
mSpellChains.clear(); // need for reload case
std::vector<uint32> ChainedSpells;
for (uint32 ability_id = 0; ability_id < sSkillLineAbilityStore.GetNumRows(); ++ability_id)
QueryResult_AutoPtr result = WorldDatabase.Query("SELECT first_spell_id, spell_id, rank from spell_ranks ORDER BY first_spell_id , rank");
if (!result)
{
SkillLineAbilityEntry const *AbilityInfo=sSkillLineAbilityStore.LookupEntry(ability_id);
if (!AbilityInfo)
continue;
if (!AbilityInfo->forward_spellid)
continue;
ChainedSpells.push_back(AbilityInfo->forward_spellid);
barGoLink bar(1);
bar.step();
sLog.outString();
sLog.outString(">> Loaded 0 spell rank records");
sLog.outErrorDb("`spell_ranks` table is empty!");
return;
}
std::multimap<SpellRankEntry, SpellRankValue> RankMap;
barGoLink bar(result->GetRowCount());
for (uint32 ability_id = 0; ability_id < sSkillLineAbilityStore.GetNumRows(); ++ability_id)
uint32 rows = 0;
bool finished = false;
do
{
SkillLineAbilityEntry const *AbilityInfo=sSkillLineAbilityStore.LookupEntry(ability_id);
if (!AbilityInfo)
continue;
//get only spell with lowest ability_id to prevent doubles
uint32 spell_id = AbilityInfo->spellId;
bool found = false;
for (uint32 i = 0; i < ChainedSpells.size(); ++i)
{
if (ChainedSpells.at(i) == spell_id)
found = true;
}
if (found)
continue;
if(mSkillLineAbilityMap.lower_bound(spell_id)->second->id != ability_id)
continue;
SpellEntry const *SpellInfo=sSpellStore.LookupEntry(spell_id);
if (!SpellInfo)
continue;
std::string sRank = SpellInfo->Rank[sWorld.GetDefaultDbcLocale()];
if(sRank.empty())
continue;
//exception to polymorph spells-make pig and turtle other chain than sheep
if (SpellInfo->SpellFamilyName == SPELLFAMILY_MAGE && (SpellInfo->SpellFamilyFlags[0] & 0x1000000) && SpellInfo->SpellIconID != 82)
continue;
SpellRankEntry entry;
SpellRankValue value;
entry.SkillId = AbilityInfo->skillId;
entry.SpellName = SpellInfo->SpellName[sWorld.GetDefaultDbcLocale()];
entry.DurationIndex = SpellInfo->DurationIndex;
entry.RangeIndex = SpellInfo->rangeIndex;
entry.ProcFlags = SpellInfo->procFlags;
entry.SpellFamilyFlags = SpellInfo->SpellFamilyFlags;
entry.TargetAuraState = SpellInfo->TargetAuraState;
entry.SpellVisual = SpellInfo->SpellVisual[0];
entry.ManaCost = SpellInfo->manaCost;
entry.CastingTimeIndex = 0;
entry.TalentID = 0;
for (;;)
{
AbilityInfo = mSkillLineAbilityMap.lower_bound(spell_id)->second;
value.Id = spell_id;
value.Rank = SpellInfo->Rank[sWorld.GetDefaultDbcLocale()];
value.strict = false;
RankMap.insert(std::pair<SpellRankEntry, SpellRankValue>(entry,value));
spell_id = AbilityInfo->forward_spellid;
SpellInfo = sSpellStore.LookupEntry(spell_id);
if (!SpellInfo)
break;
}
}
barGoLink bar(RankMap.size());
uint32 count = 0;
for (std::multimap<SpellRankEntry, SpellRankValue>::iterator itr = RankMap.begin(); itr!=RankMap.end();)
{
SpellRankEntry entry = itr->first;
//trac errors in extracted data
std::multimap<char const *, std::multimap<SpellRankEntry, SpellRankValue>::iterator> RankErrorMap;
for (std::multimap<SpellRankEntry, SpellRankValue>::iterator itr2 = RankMap.lower_bound(entry); itr2 != RankMap.upper_bound(entry); ++itr2)
// spellid, rank
std::list<std::pair<int32, int32>> rankChain;
int32 currentSpell = -1;
int32 lastSpell = -1;
// fill one chain
while(currentSpell == lastSpell && !finished)
{
bar.step();
RankErrorMap.insert(std::pair<char const *, std::multimap<SpellRankEntry, SpellRankValue>::iterator>(itr2->second.Rank,itr2));
}
bool error = false;
//if strict == true strict check is not needed
if (!itr->second.strict)
//check for rank duplicates, if there are any do strict check
for (std::multimap<char const *, std::multimap<SpellRankEntry, SpellRankValue>::iterator>::iterator itr2 = RankErrorMap.begin(); itr2!=RankErrorMap.end();)
{
char const *err_entry = itr2->first;
uint32 rank_count=RankErrorMap.count(itr2->first);
if (rank_count > 1)
{
error = true;
break;
}
else
++itr2;
}
bool allHaveTalents = true;
if (error)
{
std::list<uint32> ConflictedSpells;
for (std::multimap<SpellRankEntry, SpellRankValue>::iterator itr2 = RankMap.lower_bound(entry); itr2 != RankMap.upper_bound(entry); itr2 = RankMap.lower_bound(entry))
{
ConflictedSpells.push_back(itr2->second.Id);
if (!GetTalentSpellPos(itr2->second.Id))
allHaveTalents = false;
RankMap.erase(itr2);
}
SpellRankEntry nextEntry, currEntry;
for (; !ConflictedSpells.empty(); ConflictedSpells.pop_front())
{
SpellEntry const *SpellInfo = sSpellStore.LookupEntry(ConflictedSpells.front());
currEntry.SkillId = entry.SkillId;
currEntry.SpellName = SpellInfo->SpellName[sWorld.GetDefaultDbcLocale()];
currEntry.DurationIndex = SpellInfo->DurationIndex;
currEntry.RangeIndex = SpellInfo->rangeIndex;
currEntry.ProcFlags = SpellInfo->procFlags;
currEntry.SpellFamilyFlags = SpellInfo->SpellFamilyFlags;
//compare talents only when all spells from chain have entry
//to prevent wrong results with spells which have first rank talented and other not
if (allHaveTalents)
currEntry.TalentID=GetTalentSpellPos(ConflictedSpells.front())->talent_id;
else
currEntry.TalentID = 0;
currEntry.TargetAuraState = SpellInfo->TargetAuraState;
currEntry.SpellVisual = SpellInfo->SpellVisual[0];
currEntry.ManaCost = SpellInfo->manaCost;
Field *fields = result->Fetch();
//compare effects and casting time
currEntry.CastingTimeIndex = SpellInfo->CastingTimeIndex;
currEntry.Effect[0] = SpellInfo->Effect[0];
currEntry.Effect[1] = SpellInfo->Effect[1];
currEntry.Effect[2] = SpellInfo->Effect[2];
currentSpell = fields[0].GetUInt32();
if (lastSpell == -1)
lastSpell = currentSpell;
uint32 spell_id = fields[1].GetUInt32();
uint32 rank = fields[2].GetUInt32();
currEntry.Aura[0] = SpellInfo->EffectApplyAuraName[0];
currEntry.Aura[1] = SpellInfo->EffectApplyAuraName[1];
currEntry.Aura[2] = SpellInfo->EffectApplyAuraName[2];
SpellRankValue currValue;
currValue.Id = ConflictedSpells.front();
currValue.Rank = SpellInfo->Rank[sWorld.GetDefaultDbcLocale()];
currValue.strict = true;
RankMap.insert(std::pair<SpellRankEntry, SpellRankValue>(currEntry,currValue));
}
itr = RankMap.begin();
continue;
}
else
for (std::multimap<char const *, std::multimap<SpellRankEntry, SpellRankValue>::iterator>::iterator itr2 = RankErrorMap.begin(); itr2!=RankErrorMap.end();)
// don't drop the row if we're moving to the next rank
if (currentSpell == lastSpell)
{
char const *err_entry = itr2->first;
uint32 rank_count = RankErrorMap.count(itr2->first);
if (rank_count > 1)
for (itr2 = RankErrorMap.lower_bound(err_entry); itr2 != RankErrorMap.upper_bound(err_entry); ++itr2)
{
// I removed this pointless error message since there is NOTHING that can be
// done about it. Spell chain data is read from DBC...
// If you *REALLY* need to see this, uncomment this line and the one below
// sLog.outDebug("There is a duplicate rank entry (%s) for spell: %u",itr2->first,itr2->second->second.Id);
if (itr2->second->second.Id != 47541 && itr2->second->second.Id != 45902 && itr2->second->second.Id != 7620)
{
// Part two of pointless error messages...
// sLog.outDebug("Spell %u removed from chain data.",itr2->second->second.Id);
RankMap.erase(itr2->second);
}
}
else
++itr2;
}
//order spells by spellLevel
std::list<uint32> RankedSpells;
uint32 min_spell_lvl = 0;
std::multimap<SpellRankEntry, SpellRankValue>::iterator min_itr;
for (; RankMap.count(entry);)
{
for (std::multimap<SpellRankEntry, SpellRankValue>::iterator itr2 = RankMap.lower_bound(entry); itr2!=RankMap.upper_bound(entry); ++itr2)
{
SpellEntry const *SpellInfo=sSpellStore.LookupEntry(itr2->second.Id);
if (SpellInfo->spellLevel<min_spell_lvl || itr2 == RankMap.lower_bound(entry))
{
min_spell_lvl = SpellInfo->spellLevel;
min_itr = itr2;
}
}
RankedSpells.push_back(min_itr->second.Id);
RankMap.erase(min_itr);
}
//use data from talent.dbc
uint16 talent_id = 0;
for (std::list<uint32>::iterator itr2 = RankedSpells.begin(); itr2 != RankedSpells.end();)
{
if (TalentSpellPos const *TalentPos = GetTalentSpellPos(*itr2))
{
talent_id = TalentPos->talent_id;
RankedSpells.erase(itr2);
itr2 = RankedSpells.begin();
rankChain.push_back(std::make_pair(spell_id, rank));
if (!result->NextRow())
finished = true;
}
else
++itr2;
break;
}
if (talent_id)
// check if chain is made with valid first spell
SpellEntry const * first = sSpellStore.LookupEntry(lastSpell);
if (!first)
{
TalentEntry const *TalentInfo = sTalentStore.LookupEntry(talent_id);
for (uint8 rank = MAX_TALENT_RANK; rank > 0; --rank)
if (TalentInfo->RankID[rank-1])
RankedSpells.push_front(TalentInfo->RankID[rank-1]);
}
//do not proceed for spells with less than 2 ranks
itr = RankMap.begin();
if (RankedSpells.size() < 2)
sLog.outErrorDb("Spell rank identifier(first_spell_id) %u listed in `spell_ranks` does not exist!", lastSpell);
continue;
++count;
uint32 spell_rank = 1;
for (std::list<uint32>::iterator itr2 = RankedSpells.begin(); itr2 != RankedSpells.end(); ++spell_rank)
}
// check if chain is long enough
if (rankChain.size() < 2)
{
uint32 spell_id = *itr2;
mSpellChains[spell_id].rank = spell_rank;
mSpellChains[spell_id].first = RankedSpells.front();
mSpellChains[spell_id].last = RankedSpells.back();
++itr2;
if (spell_rank<2)
mSpellChains[spell_id].prev = 0;
if (spell_id == RankedSpells.back())
mSpellChains[spell_id].next = 0;
else
sLog.outErrorDb("There is only 1 spell rank for identifier(first_spell_id) %u in `spell_ranks`, entry is not needed!", lastSpell);
continue;
}
int32 curRank = 0;
bool valid = true;
// check spells in chain
for (std::list<std::pair<int32, int32>>::iterator itr = rankChain.begin() ; itr!= rankChain.end(); ++itr)
{
SpellEntry const * spell = sSpellStore.LookupEntry(itr->first);
if (!spell)
{
mSpellChains[*itr2].prev = spell_id;
mSpellChains[spell_id].next = *itr2;
sLog.outErrorDb("Spell %u (rank %u) listed in `spell_ranks` for chain %u does not exist!", itr->first, itr->second, lastSpell);
valid = false;
break;
}
++curRank;
if (itr->second != curRank)
{
sLog.outErrorDb("Spell %u (rank %u) listed in `spell_ranks` for chain %u does not have proper rank value(should be %u)!", itr->first, itr->second, lastSpell, curRank);
valid = false;
continue;
}
}
}
if (!valid)
continue;
int32 prevRank = 0;
// insert the chain
std::list<std::pair<int32, int32>>::iterator itr = rankChain.begin();
do
{
int32 addedSpell = itr->first;
mSpellChains[addedSpell].first = lastSpell;
mSpellChains[addedSpell].last = rankChain.back().first;
mSpellChains[addedSpell].rank = itr->second;
mSpellChains[addedSpell].prev = prevRank;
prevRank = addedSpell;
++itr;
if (itr == rankChain.end())
{
mSpellChains[addedSpell].next = 0;
break;
}
else
mSpellChains[addedSpell].next = itr->first;
}
while (true);
//uncomment these two lines to print yourself list of spell_chains on startup
//for(UNORDERED_MAP<uint32, SpellChainNode>::iterator itr = mSpellChains.begin(); itr != mSpellChains.end(); ++itr)
//sLog.outString("Id: %u, Rank: %d , %s, %u, %u, %u, %u",itr->first,itr->second.rank, sSpellStore.LookupEntry(itr->first)->Rank[sWorld.GetDefaultDbcLocale()], itr->second.first, itr->second.last,itr->second.next, itr->second.prev);
++rows;
} while (!finished);
sLog.outString();
sLog.outString(">> Loaded %u spell chains",count);
sLog.outString( ">> Loaded %u spell rank records", rows );
}
// set data in core for now
+1 -1
View File
@@ -1207,7 +1207,7 @@ class SpellMgr
static SpellMgr& Instance();
// Loading data at server startup
void LoadSpellChains();
void LoadSpellRanks();
void LoadSpellRequired();
void LoadSpellLearnSkills();
void LoadSpellLearnSpells();
+3 -3
View File
@@ -1305,8 +1305,8 @@ void World::SetInitialWorldSettings()
sLog.outString("Loading Game Object Templates..."); // must be after LoadPageTexts
objmgr.LoadGameobjectInfo();
sLog.outString("Loading Spell Chain Data...");
spellmgr.LoadSpellChains();
sLog.outString("Loading Spell Rank Data...");
spellmgr.LoadSpellRanks();
sLog.outString("Loading Spell Required Data...");
spellmgr.LoadSpellRequired();
@@ -1315,7 +1315,7 @@ void World::SetInitialWorldSettings()
spellmgr.LoadSpellElixirs();
sLog.outString("Loading Spell Learn Skills...");
spellmgr.LoadSpellLearnSkills(); // must be after LoadSpellChains
spellmgr.LoadSpellLearnSkills(); // must be after LoadSpellRanks
sLog.outString("Loading Spell Learn Spells...");
spellmgr.LoadSpellLearnSpells();