*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:
+7
-7
@@ -370,21 +370,21 @@ typedef std::list<VendorItemCount> VendorItemCounts;
|
|||||||
|
|
||||||
struct TrainerSpell
|
struct TrainerSpell
|
||||||
{
|
{
|
||||||
TrainerSpell() : spell(0), spellCost(0), reqSkill(0), reqSkillValue(0), reqLevel(0), learnedSpell(0) {}
|
TrainerSpell() : spell(0), spellCost(0), reqSkill(0), reqSkillValue(0), reqLevel(0)
|
||||||
|
{
|
||||||
TrainerSpell(uint32 _spell, uint32 _spellCost, uint32 _reqSkill, uint32 _reqSkillValue, uint32 _reqLevel, uint32 _learnedspell)
|
for (uint8 i = 0; i < MAX_SPELL_EFFECTS ; ++i)
|
||||||
: spell(_spell), spellCost(_spellCost), reqSkill(_reqSkill), reqSkillValue(_reqSkillValue), reqLevel(_reqLevel), learnedSpell(_learnedspell)
|
learnedSpell[i] = 0;
|
||||||
{}
|
}
|
||||||
|
|
||||||
uint32 spell;
|
uint32 spell;
|
||||||
uint32 spellCost;
|
uint32 spellCost;
|
||||||
uint32 reqSkill;
|
uint32 reqSkill;
|
||||||
uint32 reqSkillValue;
|
uint32 reqSkillValue;
|
||||||
uint32 reqLevel;
|
uint32 reqLevel;
|
||||||
uint32 learnedSpell;
|
uint32 learnedSpell[3];
|
||||||
|
|
||||||
// helpers
|
// helpers
|
||||||
bool IsCastable() const { return learnedSpell != spell; }
|
bool IsCastable() const { return learnedSpell[0] != spell; }
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef UNORDERED_MAP<uint32 /*spellid*/, TrainerSpell> TrainerSpellMap;
|
typedef UNORDERED_MAP<uint32 /*spellid*/, TrainerSpell> TrainerSpellMap;
|
||||||
|
|||||||
+37
-13
@@ -159,11 +159,23 @@ void WorldSession::SendTrainerList( uint64 guid, const std::string& strTitle )
|
|||||||
{
|
{
|
||||||
TrainerSpell const* tSpell = &itr->second;
|
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;
|
continue;
|
||||||
|
|
||||||
bool primary_prof_first_rank = spellmgr.IsPrimaryProfessionFirstRankSpell(tSpell->learnedSpell);
|
|
||||||
SpellChainNode const* chain_node = spellmgr.GetSpellChainNode(tSpell->learnedSpell);
|
|
||||||
TrainerSpellState state = _player->GetTrainerSpellState(tSpell);
|
TrainerSpellState state = _player->GetTrainerSpellState(tSpell);
|
||||||
|
|
||||||
data << uint32(tSpell->spell); // learned spell (or cast-spell in profession case)
|
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);
|
data << uint32(tSpell->reqSkillValue);
|
||||||
//prev + req or req + 0
|
//prev + req or req + 0
|
||||||
uint8 maxReq = 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);
|
if (!tSpell->learnedSpell[i])
|
||||||
++maxReq;
|
continue;
|
||||||
}
|
if (SpellChainNode const* chain_node = spellmgr.GetSpellChainNode(tSpell->learnedSpell[i]))
|
||||||
SpellsRequiringSpellMapBounds spellsRequired = spellmgr.GetSpellsRequiredForSpellBounds(tSpell->spell);
|
{
|
||||||
for (SpellsRequiringSpellMap::const_iterator itr2 = spellsRequired.first; itr2 != spellsRequired.second && maxReq < 3; ++itr2)
|
if (chain_node->prev)
|
||||||
{
|
{
|
||||||
data << uint32(itr2->second);
|
data << uint32(chain_node->prev);
|
||||||
++maxReq;
|
++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)
|
while (maxReq < 3)
|
||||||
{
|
{
|
||||||
@@ -258,7 +282,7 @@ void WorldSession::HandleTrainerBuySpellOpcode( WorldPacket & recv_data )
|
|||||||
SendPacket(&data);
|
SendPacket(&data);
|
||||||
|
|
||||||
// learn explicitly or cast explicitly
|
// 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.
|
//FIXME: prof. spell entry in trainer list not marked gray until list re-open.
|
||||||
_player->CastSpell(_player,trainer_spell->spell,true);
|
_player->CastSpell(_player,trainer_spell->spell,true);
|
||||||
else
|
else
|
||||||
|
|||||||
+20
-7
@@ -8015,21 +8015,34 @@ void ObjectMgr::LoadTrainerSpell()
|
|||||||
trainerSpell.reqLevel = spellinfo->spellLevel;
|
trainerSpell.reqLevel = spellinfo->spellLevel;
|
||||||
|
|
||||||
// calculate learned spell for profession case when stored cast-spell
|
// calculate learned spell for profession case when stored cast-spell
|
||||||
trainerSpell.learnedSpell = spell;
|
trainerSpell.learnedSpell[0] = spell;
|
||||||
for (uint8 i = 0; i <3; ++i)
|
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
||||||
{
|
{
|
||||||
if(spellinfo->Effect[i] != SPELL_EFFECT_LEARN_SPELL)
|
if(spellinfo->Effect[i] != SPELL_EFFECT_LEARN_SPELL)
|
||||||
continue;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(SpellMgr::IsProfessionSpell(trainerSpell.learnedSpell))
|
|
||||||
data.trainerType = 2;
|
|
||||||
|
|
||||||
++count;
|
++count;
|
||||||
|
|
||||||
} while (result->NextRow());
|
} while (result->NextRow());
|
||||||
|
|||||||
+43
-31
@@ -4093,52 +4093,64 @@ TrainerSpellState Player::GetTrainerSpellState(TrainerSpell const* trainer_spell
|
|||||||
if (!trainer_spell)
|
if (!trainer_spell)
|
||||||
return TRAINER_SPELL_RED;
|
return TRAINER_SPELL_RED;
|
||||||
|
|
||||||
if (!trainer_spell->learnedSpell)
|
bool hasSpell = true;
|
||||||
return TRAINER_SPELL_RED;
|
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
|
// known spell
|
||||||
if(HasSpell(trainer_spell->learnedSpell))
|
if (hasSpell)
|
||||||
return TRAINER_SPELL_GRAY;
|
return TRAINER_SPELL_GRAY;
|
||||||
|
|
||||||
// check race/class requirement
|
// check skill requirement
|
||||||
if(!IsSpellFitByClassAndRace(trainer_spell->learnedSpell))
|
if(trainer_spell->reqSkill && GetBaseSkillValue(trainer_spell->reqSkill) < trainer_spell->reqSkillValue)
|
||||||
return TRAINER_SPELL_RED;
|
return TRAINER_SPELL_RED;
|
||||||
|
|
||||||
// check level requirement
|
// check level requirement
|
||||||
if(getLevel() < trainer_spell->reqLevel)
|
if(getLevel() < trainer_spell->reqLevel)
|
||||||
return TRAINER_SPELL_RED;
|
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 (!trainer_spell->learnedSpell[i])
|
||||||
if(spell_chain->prev && !HasSpell(spell_chain->prev))
|
continue;
|
||||||
|
|
||||||
|
// check race/class requirement
|
||||||
|
if(!IsSpellFitByClassAndRace(trainer_spell->learnedSpell[i]))
|
||||||
return TRAINER_SPELL_RED;
|
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
|
// check primary prof. limit
|
||||||
if(spellmgr.IsPrimaryProfessionFirstRankSpell(spell->Id) && GetFreePrimaryProfessionPoints() == 0)
|
// first rank of primary profession spell when there are no proffesions avalible is disabled
|
||||||
return TRAINER_SPELL_GREEN_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;
|
return TRAINER_SPELL_GREEN;
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-17
@@ -1660,12 +1660,18 @@ bool SpellMgr::IsProfessionOrRidingSpell(uint32 spellId)
|
|||||||
if(!spellInfo)
|
if(!spellInfo)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(spellInfo->Effect[1] != SPELL_EFFECT_SKILL)
|
for (uint8 i = 0 ; i < MAX_SPELL_EFFECTS ; ++i)
|
||||||
return false;
|
{
|
||||||
|
if(spellInfo->Effect[i] == SPELL_EFFECT_SKILL)
|
||||||
|
{
|
||||||
|
uint32 skill = spellInfo->EffectMiscValue[i];
|
||||||
|
|
||||||
uint32 skill = spellInfo->EffectMiscValue[1];
|
bool found = IsProfessionOrRidingSkill(skill);
|
||||||
|
if (found)
|
||||||
return IsProfessionOrRidingSkill(skill);
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SpellMgr::IsProfessionSpell(uint32 spellId)
|
bool SpellMgr::IsProfessionSpell(uint32 spellId)
|
||||||
@@ -1674,12 +1680,18 @@ bool SpellMgr::IsProfessionSpell(uint32 spellId)
|
|||||||
if(!spellInfo)
|
if(!spellInfo)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(spellInfo->Effect[1] != SPELL_EFFECT_SKILL)
|
for (uint8 i = 0 ; i < MAX_SPELL_EFFECTS ; ++i)
|
||||||
return false;
|
{
|
||||||
|
if(spellInfo->Effect[i] == SPELL_EFFECT_SKILL)
|
||||||
|
{
|
||||||
|
uint32 skill = spellInfo->EffectMiscValue[i];
|
||||||
|
|
||||||
uint32 skill = spellInfo->EffectMiscValue[1];
|
bool found = IsProfessionSkill(skill);
|
||||||
|
if (found)
|
||||||
return IsProfessionSkill(skill);
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SpellMgr::IsPrimaryProfessionSpell(uint32 spellId)
|
bool SpellMgr::IsPrimaryProfessionSpell(uint32 spellId)
|
||||||
@@ -1688,12 +1700,18 @@ bool SpellMgr::IsPrimaryProfessionSpell(uint32 spellId)
|
|||||||
if(!spellInfo)
|
if(!spellInfo)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(spellInfo->Effect[1] != SPELL_EFFECT_SKILL)
|
for (uint8 i = 0 ; i < MAX_SPELL_EFFECTS ; ++i)
|
||||||
return false;
|
{
|
||||||
|
if(spellInfo->Effect[i] == SPELL_EFFECT_SKILL)
|
||||||
|
{
|
||||||
|
uint32 skill = spellInfo->EffectMiscValue[i];
|
||||||
|
|
||||||
uint32 skill = spellInfo->EffectMiscValue[1];
|
bool found = IsPrimaryProfessionSkill(skill);
|
||||||
|
if (found)
|
||||||
return IsPrimaryProfessionSkill(skill);
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SpellMgr::IsPrimaryProfessionFirstRankSpell(uint32 spellId) const
|
bool SpellMgr::IsPrimaryProfessionFirstRankSpell(uint32 spellId) const
|
||||||
@@ -1965,7 +1983,8 @@ void SpellMgr::LoadSpellScriptTarget()
|
|||||||
}
|
}
|
||||||
if (!targetfound)
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3227,7 +3246,7 @@ void SpellMgr::LoadSpellRequired()
|
|||||||
|
|
||||||
uint32 spell_id = fields[0].GetUInt32();
|
uint32 spell_id = fields[0].GetUInt32();
|
||||||
uint32 spell_req = fields[1].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);
|
SpellEntry const * spell = sSpellStore.LookupEntry(spell_id);
|
||||||
if (!spell)
|
if (!spell)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user