*Add support for spells in npc_trainer table with multiple SPELL_EFFECT_LEARN_SPELL, this fixes problems with learning such spells from trainers.

--HG--
branch : trunk
This commit is contained in:
QAston
2010-01-31 17:24:13 +01:00
parent d435366bce
commit ac388e6f21
5 changed files with 144 additions and 76 deletions
+7 -7
View File
@@ -370,21 +370,21 @@ typedef std::list<VendorItemCount> VendorItemCounts;
struct TrainerSpell
{
TrainerSpell() : spell(0), spellCost(0), reqSkill(0), reqSkillValue(0), reqLevel(0), learnedSpell(0) {}
TrainerSpell(uint32 _spell, uint32 _spellCost, uint32 _reqSkill, uint32 _reqSkillValue, uint32 _reqLevel, uint32 _learnedspell)
: spell(_spell), spellCost(_spellCost), reqSkill(_reqSkill), reqSkillValue(_reqSkillValue), reqLevel(_reqLevel), learnedSpell(_learnedspell)
{}
TrainerSpell() : spell(0), spellCost(0), reqSkill(0), reqSkillValue(0), reqLevel(0)
{
for (uint8 i = 0; i < MAX_SPELL_EFFECTS ; ++i)
learnedSpell[i] = 0;
}
uint32 spell;
uint32 spellCost;
uint32 reqSkill;
uint32 reqSkillValue;
uint32 reqLevel;
uint32 learnedSpell;
uint32 learnedSpell[3];
// helpers
bool IsCastable() const { return learnedSpell != spell; }
bool IsCastable() const { return learnedSpell[0] != spell; }
};
typedef UNORDERED_MAP<uint32 /*spellid*/, TrainerSpell> TrainerSpellMap;
+37 -13
View File
@@ -159,11 +159,23 @@ void WorldSession::SendTrainerList( uint64 guid, const std::string& strTitle )
{
TrainerSpell const* tSpell = &itr->second;
if(!_player->IsSpellFitByClassAndRace(tSpell->learnedSpell))
bool valid = true;
bool primary_prof_first_rank = false;
for (uint8 i = 0; i < MAX_SPELL_EFFECTS ; ++i)
{
if (!tSpell->learnedSpell[i])
continue;
if(!_player->IsSpellFitByClassAndRace(tSpell->learnedSpell[i]))
{
valid = false;
break;
}
if (spellmgr.IsPrimaryProfessionFirstRankSpell(tSpell->learnedSpell[i]))
primary_prof_first_rank = true;
}
if (!valid)
continue;
bool primary_prof_first_rank = spellmgr.IsPrimaryProfessionFirstRankSpell(tSpell->learnedSpell);
SpellChainNode const* chain_node = spellmgr.GetSpellChainNode(tSpell->learnedSpell);
TrainerSpellState state = _player->GetTrainerSpellState(tSpell);
data << uint32(tSpell->spell); // learned spell (or cast-spell in profession case)
@@ -178,16 +190,28 @@ void WorldSession::SendTrainerList( uint64 guid, const std::string& strTitle )
data << uint32(tSpell->reqSkillValue);
//prev + req or req + 0
uint8 maxReq = 0;
if (!tSpell->IsCastable() && chain_node && chain_node->prev)
for (uint8 i = 0; i < MAX_SPELL_EFFECTS ; ++i)
{
data << uint32(chain_node->prev);
++maxReq;
}
SpellsRequiringSpellMapBounds spellsRequired = spellmgr.GetSpellsRequiredForSpellBounds(tSpell->spell);
for (SpellsRequiringSpellMap::const_iterator itr2 = spellsRequired.first; itr2 != spellsRequired.second && maxReq < 3; ++itr2)
{
data << uint32(itr2->second);
++maxReq;
if (!tSpell->learnedSpell[i])
continue;
if (SpellChainNode const* chain_node = spellmgr.GetSpellChainNode(tSpell->learnedSpell[i]))
{
if (chain_node->prev)
{
data << uint32(chain_node->prev);
++maxReq;
}
}
if (maxReq == 3)
break;
SpellsRequiringSpellMapBounds spellsRequired = spellmgr.GetSpellsRequiredForSpellBounds(tSpell->learnedSpell[i]);
for (SpellsRequiringSpellMap::const_iterator itr2 = spellsRequired.first; itr2 != spellsRequired.second && maxReq < 3; ++itr2)
{
data << uint32(itr2->second);
++maxReq;
}
if (maxReq == 3)
break;
}
while (maxReq < 3)
{
@@ -258,7 +282,7 @@ void WorldSession::HandleTrainerBuySpellOpcode( WorldPacket & recv_data )
SendPacket(&data);
// learn explicitly or cast explicitly
if(trainer_spell->IsCastable ())
if(trainer_spell->IsCastable())
//FIXME: prof. spell entry in trainer list not marked gray until list re-open.
_player->CastSpell(_player,trainer_spell->spell,true);
else
+20 -7
View File
@@ -8015,21 +8015,34 @@ void ObjectMgr::LoadTrainerSpell()
trainerSpell.reqLevel = spellinfo->spellLevel;
// calculate learned spell for profession case when stored cast-spell
trainerSpell.learnedSpell = spell;
for (uint8 i = 0; i <3; ++i)
trainerSpell.learnedSpell[0] = spell;
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
if(spellinfo->Effect[i] != SPELL_EFFECT_LEARN_SPELL)
continue;
if(SpellMgr::IsProfessionOrRidingSpell(spellinfo->EffectTriggerSpell[i]))
// player must be able to cast spell on himself
if (spellinfo->EffectImplicitTargetA[i] != 0 && spellinfo->EffectImplicitTargetA[i] != TARGET_UNIT_TARGET_ALLY && spellinfo->EffectImplicitTargetA[i] != TARGET_UNIT_TARGET_ANY)
{
trainerSpell.learnedSpell = spellinfo->EffectTriggerSpell[i];
sLog.outErrorDb("Table `npc_trainer` has spell %u for trainer entry %u with learn effect which has incorrect target type, ignoring learn effect!", spell, entry);
continue;
}
if (trainerSpell.learnedSpell[0] == spell)
trainerSpell.learnedSpell[0] = 0;
trainerSpell.learnedSpell[i] = spellinfo->EffectTriggerSpell[i];
}
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
if (!trainerSpell.learnedSpell[i])
continue;
if(SpellMgr::IsProfessionSpell(trainerSpell.learnedSpell[i]))
{
data.trainerType = 2;
break;
}
}
if(SpellMgr::IsProfessionSpell(trainerSpell.learnedSpell))
data.trainerType = 2;
++count;
} while (result->NextRow());
+43 -31
View File
@@ -4093,52 +4093,64 @@ TrainerSpellState Player::GetTrainerSpellState(TrainerSpell const* trainer_spell
if (!trainer_spell)
return TRAINER_SPELL_RED;
if (!trainer_spell->learnedSpell)
return TRAINER_SPELL_RED;
bool hasSpell = true;
for (uint8 i = 0; i < MAX_SPELL_EFFECTS ; ++i)
{
if (!trainer_spell->learnedSpell[i])
continue;
if(!HasSpell(trainer_spell->learnedSpell[i]))
{
hasSpell = false;
break;
}
}
// known spell
if(HasSpell(trainer_spell->learnedSpell))
if (hasSpell)
return TRAINER_SPELL_GRAY;
// check race/class requirement
if(!IsSpellFitByClassAndRace(trainer_spell->learnedSpell))
// check skill requirement
if(trainer_spell->reqSkill && GetBaseSkillValue(trainer_spell->reqSkill) < trainer_spell->reqSkillValue)
return TRAINER_SPELL_RED;
// check level requirement
if(getLevel() < trainer_spell->reqLevel)
return TRAINER_SPELL_RED;
if(SpellChainNode const* spell_chain = spellmgr.GetSpellChainNode(trainer_spell->learnedSpell))
for (uint8 i = 0; i < MAX_SPELL_EFFECTS ; ++i)
{
// check prev.rank requirement
if(spell_chain->prev && !HasSpell(spell_chain->prev))
if (!trainer_spell->learnedSpell[i])
continue;
// check race/class requirement
if(!IsSpellFitByClassAndRace(trainer_spell->learnedSpell[i]))
return TRAINER_SPELL_RED;
if(SpellChainNode const* spell_chain = spellmgr.GetSpellChainNode(trainer_spell->learnedSpell[i]))
{
// check prev.rank requirement
if(spell_chain->prev && !HasSpell(spell_chain->prev))
return TRAINER_SPELL_RED;
}
SpellsRequiringSpellMapBounds spellsRequired = spellmgr.GetSpellsRequiredForSpellBounds(trainer_spell->learnedSpell[i]);
for (SpellsRequiringSpellMap::const_iterator itr = spellsRequired.first; itr != spellsRequired.second; ++itr)
{
// check additional spell requirement
if(!HasSpell(itr->second))
return TRAINER_SPELL_RED;
}
}
SpellsRequiringSpellMapBounds spellsRequired = spellmgr.GetSpellsRequiredForSpellBounds(trainer_spell->spell);
for (SpellsRequiringSpellMap::const_iterator itr = spellsRequired.first; itr != spellsRequired.second; ++itr)
{
// check additional spell requirement
if(!HasSpell(itr->second))
return TRAINER_SPELL_RED;
}
// check skill requirement
if(trainer_spell->reqSkill && GetBaseSkillValue(trainer_spell->reqSkill) < trainer_spell->reqSkillValue)
return TRAINER_SPELL_RED;
// exist, already checked at loading
SpellEntry const* spell = sSpellStore.LookupEntry(trainer_spell->learnedSpell);
// secondary prof. or not prof. spell
uint32 skill = spell->EffectMiscValue[1];
if(spell->Effect[1] != SPELL_EFFECT_SKILL || !IsPrimaryProfessionSkill(skill))
return TRAINER_SPELL_GREEN;
// check primary prof. limit
if(spellmgr.IsPrimaryProfessionFirstRankSpell(spell->Id) && GetFreePrimaryProfessionPoints() == 0)
return TRAINER_SPELL_GREEN_DISABLED;
// first rank of primary profession spell when there are no proffesions avalible is disabled
for (uint8 i = 0; i < MAX_SPELL_EFFECTS ; ++i)
{
if (!trainer_spell->learnedSpell[i])
continue;
if((spellmgr.IsPrimaryProfessionFirstRankSpell(trainer_spell->learnedSpell[i])) && (GetFreePrimaryProfessionPoints() == 0))
return TRAINER_SPELL_GREEN_DISABLED;
}
return TRAINER_SPELL_GREEN;
}
+36 -17
View File
@@ -1660,12 +1660,18 @@ bool SpellMgr::IsProfessionOrRidingSpell(uint32 spellId)
if(!spellInfo)
return false;
if(spellInfo->Effect[1] != SPELL_EFFECT_SKILL)
return false;
for (uint8 i = 0 ; i < MAX_SPELL_EFFECTS ; ++i)
{
if(spellInfo->Effect[i] == SPELL_EFFECT_SKILL)
{
uint32 skill = spellInfo->EffectMiscValue[i];
uint32 skill = spellInfo->EffectMiscValue[1];
return IsProfessionOrRidingSkill(skill);
bool found = IsProfessionOrRidingSkill(skill);
if (found)
return true;
}
}
return false;
}
bool SpellMgr::IsProfessionSpell(uint32 spellId)
@@ -1674,12 +1680,18 @@ bool SpellMgr::IsProfessionSpell(uint32 spellId)
if(!spellInfo)
return false;
if(spellInfo->Effect[1] != SPELL_EFFECT_SKILL)
return false;
for (uint8 i = 0 ; i < MAX_SPELL_EFFECTS ; ++i)
{
if(spellInfo->Effect[i] == SPELL_EFFECT_SKILL)
{
uint32 skill = spellInfo->EffectMiscValue[i];
uint32 skill = spellInfo->EffectMiscValue[1];
return IsProfessionSkill(skill);
bool found = IsProfessionSkill(skill);
if (found)
return true;
}
}
return false;
}
bool SpellMgr::IsPrimaryProfessionSpell(uint32 spellId)
@@ -1688,12 +1700,18 @@ bool SpellMgr::IsPrimaryProfessionSpell(uint32 spellId)
if(!spellInfo)
return false;
if(spellInfo->Effect[1] != SPELL_EFFECT_SKILL)
return false;
for (uint8 i = 0 ; i < MAX_SPELL_EFFECTS ; ++i)
{
if(spellInfo->Effect[i] == SPELL_EFFECT_SKILL)
{
uint32 skill = spellInfo->EffectMiscValue[i];
uint32 skill = spellInfo->EffectMiscValue[1];
return IsPrimaryProfessionSkill(skill);
bool found = IsPrimaryProfessionSkill(skill);
if (found)
return true;
}
}
return false;
}
bool SpellMgr::IsPrimaryProfessionFirstRankSpell(uint32 spellId) const
@@ -1965,7 +1983,8 @@ void SpellMgr::LoadSpellScriptTarget()
}
if (!targetfound)
{
sLog.outErrorDb("Table `spell_script_target`: spellId %u listed for TargetEntry %u does not have any implicit target TARGET_UNIT_NEARBY_ENTRY(38) or TARGET_DST_NEARBY_ENTRY (46).",spellId,targetEntry);
sLog.outErrorDb("Table `spell_script_target`: spellId %u listed for TargetEntry %u does not have any implicit target TARGET_UNIT_NEARBY_ENTRY(38) or TARGET_DST_NEARBY_ENTRY (46)\
,TARGET_UNIT_AREA_ENTRY_SRC(7), TARGET_UNIT_AREA_ENTRY_DST(8), TARGET_UNIT_CONE_ENTRY(60)",spellId,targetEntry);
continue;
}
@@ -3227,7 +3246,7 @@ void SpellMgr::LoadSpellRequired()
uint32 spell_id = fields[0].GetUInt32();
uint32 spell_req = fields[1].GetUInt32();
// check if chain is made with valid first spell
// validate table
SpellEntry const * spell = sSpellStore.LookupEntry(spell_id);
if (!spell)
{