mirror of
https://github.com/mangosfour/server.git
synced 2025-12-14 07:37:01 +00:00
Not store dependent spells in character_spell
* Mark spells learned in result character creating, another spell learning, skill grow, quest reward as dependent and not store its in `character_spell`. * Prevent re-learning known spell in expected state * Prevent re-learning low rank spell as active if higher rank known. * New type of non-stacked ranked spells check: skill dependent spell bonuses. * Activate (show propetly and cast if need) lesser spell rank for non-stackable in spellbooks spells at unlearn high rank
This commit is contained in:
parent
4b5aba18f8
commit
bbf8fd0742
10 changed files with 222 additions and 85 deletions
|
|
@ -2541,7 +2541,7 @@ void Player::AddNewMailDeliverTime(time_t deliver_time)
|
|||
}
|
||||
}
|
||||
|
||||
bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool disabled)
|
||||
bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependent, bool disabled)
|
||||
{
|
||||
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
|
||||
if (!spellInfo)
|
||||
|
|
@ -2574,29 +2574,78 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool disabled
|
|||
|
||||
PlayerSpellState state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;
|
||||
|
||||
bool dependent_set = false;
|
||||
bool disabled_case = false;
|
||||
bool superceded_old = false;
|
||||
|
||||
PlayerSpellMap::iterator itr = m_spells.find(spell_id);
|
||||
if (itr != m_spells.end())
|
||||
{
|
||||
uint32 next_active_spell_id = 0;
|
||||
// fix activate state for non-stackable low rank (and find next spell for !active case)
|
||||
if(!SpellMgr::canStackSpellRanks(spellInfo) && spellmgr.GetSpellRank(spellInfo->Id) != 0)
|
||||
{
|
||||
SpellChainMapNext const& nextMap = spellmgr.GetSpellChainNext();
|
||||
for(SpellChainMapNext::const_iterator next_itr = nextMap.lower_bound(spell_id); next_itr != nextMap.upper_bound(spell_id); ++next_itr)
|
||||
{
|
||||
if(HasSpell(next_itr->second))
|
||||
{
|
||||
// high rank already known so this must !active
|
||||
active = false;
|
||||
next_active_spell_id = next_itr->second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// not do anything if already known in expected state
|
||||
if(itr->second->state != PLAYERSPELL_REMOVED && itr->second->active == active &&
|
||||
itr->second->dependent == dependent && itr->second->disabled == disabled)
|
||||
return false;
|
||||
|
||||
// dependent spell known as not dependent, overwrite state
|
||||
if (itr->second->state != PLAYERSPELL_REMOVED && !itr->second->dependent && dependent)
|
||||
{
|
||||
itr->second->dependent = dependent;
|
||||
if (itr->second->state != PLAYERSPELL_NEW)
|
||||
itr->second->state = PLAYERSPELL_CHANGED;
|
||||
dependent_set = true;
|
||||
}
|
||||
|
||||
// update active state for known spell
|
||||
if(itr->second->active != active && itr->second->state != PLAYERSPELL_REMOVED && !itr->second->disabled)
|
||||
{
|
||||
itr->second->active = active;
|
||||
|
||||
// !IsInWorld() && !learning == explicitly load from DB and then exist in it already and set correctly
|
||||
if(!IsInWorld() && !learning)
|
||||
if(!IsInWorld() && !learning && !dependent_set)
|
||||
itr->second->state = PLAYERSPELL_UNCHANGED;
|
||||
else if(itr->second->state != PLAYERSPELL_NEW)
|
||||
itr->second->state = PLAYERSPELL_CHANGED;
|
||||
|
||||
if(!active)
|
||||
if(active)
|
||||
{
|
||||
WorldPacket data(SMSG_REMOVED_SPELL, 4);
|
||||
data << uint16(spell_id);
|
||||
GetSession()->SendPacket(&data);
|
||||
if (IsPassiveSpell(spell_id) && IsNeedCastPassiveSpellAtLearn(spellInfo))
|
||||
CastSpell (this,spell_id,true);
|
||||
}
|
||||
else if(IsInWorld())
|
||||
{
|
||||
if(next_active_spell_id)
|
||||
{
|
||||
// update spell ranks in spellbook and action bar
|
||||
WorldPacket data(SMSG_SUPERCEDED_SPELL, (4));
|
||||
data << uint16(spell_id);
|
||||
data << uint16(next_active_spell_id);
|
||||
GetSession()->SendPacket( &data );
|
||||
}
|
||||
else
|
||||
{
|
||||
WorldPacket data(SMSG_REMOVED_SPELL, 4);
|
||||
data << uint16(spell_id);
|
||||
GetSession()->SendPacket(&data);
|
||||
}
|
||||
}
|
||||
|
||||
return active; // learn (show in spell book if active now)
|
||||
}
|
||||
|
||||
|
|
@ -2625,7 +2674,7 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool disabled
|
|||
default: // known not saved yet spell (new or modified)
|
||||
{
|
||||
// can be in case spell loading but learned at some previous spell loading
|
||||
if(!IsInWorld() && !learning)
|
||||
if(!IsInWorld() && !learning && !dependent_set)
|
||||
itr->second->state = PLAYERSPELL_UNCHANGED;
|
||||
|
||||
return false;
|
||||
|
|
@ -2658,16 +2707,17 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool disabled
|
|||
// non talent spell: learn low ranks (recursive call)
|
||||
else if(uint32 prev_spell = spellmgr.GetPrevSpellInChain(spell_id))
|
||||
{
|
||||
if(!IsInWorld()) // at spells loading, no output, but allow save
|
||||
addSpell(prev_spell,active,true,disabled);
|
||||
if(!IsInWorld() || disabled) // at spells loading, no output, but allow save
|
||||
addSpell(prev_spell,active,true,true,disabled);
|
||||
else // at normal learning
|
||||
learnSpell(prev_spell);
|
||||
learnSpell(prev_spell,true);
|
||||
}
|
||||
|
||||
PlayerSpell *newspell = new PlayerSpell;
|
||||
newspell->active = active;
|
||||
newspell->state = state;
|
||||
newspell->disabled = disabled;
|
||||
newspell->state = state;
|
||||
newspell->active = active;
|
||||
newspell->dependent = dependent;
|
||||
newspell->disabled = disabled;
|
||||
|
||||
// replace spells in action bars and spellbook to bigger rank if only one spell rank must be accessible
|
||||
if(newspell->active && !newspell->disabled && !SpellMgr::canStackSpellRanks(spellInfo) && spellmgr.GetSpellRank(spellInfo->Id) != 0)
|
||||
|
|
@ -2694,7 +2744,8 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool disabled
|
|||
|
||||
// mark old spell as disable (SMSG_SUPERCEDED_SPELL replace it in client by new)
|
||||
itr->second->active = false;
|
||||
itr->second->state = PLAYERSPELL_CHANGED;
|
||||
if(itr->second->state != PLAYERSPELL_NEW)
|
||||
itr->second->state = PLAYERSPELL_CHANGED;
|
||||
superceded_old = true; // new spell replace old in action bars and spell book.
|
||||
}
|
||||
else if(spellmgr.IsHighRankOfSpell(itr->first,spell_id))
|
||||
|
|
@ -2736,26 +2787,7 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool disabled
|
|||
// also cast passive spells (including all talents without SPELL_EFFECT_LEARN_SPELL) with additional checks
|
||||
else if (IsPassiveSpell(spell_id))
|
||||
{
|
||||
bool need_cast = false;
|
||||
|
||||
switch(spell_id)
|
||||
{
|
||||
// some spells not have stance data expacted cast at form change or present
|
||||
case 5420: need_cast = (m_form == FORM_TREE); break;
|
||||
case 5419: need_cast = (m_form == FORM_TRAVEL); break;
|
||||
case 7376: need_cast = (m_form == FORM_DEFENSIVESTANCE); break;
|
||||
case 7381: need_cast = (m_form == FORM_BERSERKERSTANCE); break;
|
||||
case 21156: need_cast = (m_form == FORM_BATTLESTANCE); break;
|
||||
case 21178: need_cast = (m_form == FORM_BEAR || m_form == FORM_DIREBEAR); break;
|
||||
case 33948: need_cast = (m_form == FORM_FLIGHT); break;
|
||||
case 34764: need_cast = (m_form == FORM_FLIGHT); break;
|
||||
case 40121: need_cast = (m_form == FORM_FLIGHT_EPIC); break;
|
||||
case 40122: need_cast = (m_form == FORM_FLIGHT_EPIC); break;
|
||||
// another spells have proper stance data
|
||||
default: need_cast = !spellInfo->Stances || m_form != 0 && (spellInfo->Stances & (1<<(m_form-1))); break;
|
||||
}
|
||||
//Check CasterAuraStates
|
||||
if (need_cast && (!spellInfo->CasterAuraState || HasAuraState(AuraState(spellInfo->CasterAuraState))))
|
||||
if(IsNeedCastPassiveSpellAtLearn(spellInfo))
|
||||
CastSpell(this, spell_id, true);
|
||||
}
|
||||
else if( IsSpellHaveEffect(spellInfo,SPELL_EFFECT_SKILL_STEP) )
|
||||
|
|
@ -2840,9 +2872,9 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool disabled
|
|||
if(!itr->second.autoLearned)
|
||||
{
|
||||
if(!IsInWorld() || !itr->second.active) // at spells loading, no output, but allow save
|
||||
addSpell(itr->second.spell,itr->second.active,true,false);
|
||||
addSpell(itr->second.spell,itr->second.active,true,true,false);
|
||||
else // at normal learning
|
||||
learnSpell(itr->second.spell);
|
||||
learnSpell(itr->second.spell,true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2856,14 +2888,39 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool disabled
|
|||
return active && !disabled && !superceded_old;
|
||||
}
|
||||
|
||||
void Player::learnSpell(uint32 spell_id)
|
||||
bool Player::IsNeedCastPassiveSpellAtLearn(SpellEntry const* spellInfo) const
|
||||
{
|
||||
bool need_cast = false;
|
||||
|
||||
switch(spellInfo->Id)
|
||||
{
|
||||
// some spells not have stance data expacted cast at form change or present
|
||||
case 5420: need_cast = (m_form == FORM_TREE); break;
|
||||
case 5419: need_cast = (m_form == FORM_TRAVEL); break;
|
||||
case 7376: need_cast = (m_form == FORM_DEFENSIVESTANCE); break;
|
||||
case 7381: need_cast = (m_form == FORM_BERSERKERSTANCE); break;
|
||||
case 21156: need_cast = (m_form == FORM_BATTLESTANCE); break;
|
||||
case 21178: need_cast = (m_form == FORM_BEAR || m_form == FORM_DIREBEAR); break;
|
||||
case 33948: need_cast = (m_form == FORM_FLIGHT); break;
|
||||
case 34764: need_cast = (m_form == FORM_FLIGHT); break;
|
||||
case 40121: need_cast = (m_form == FORM_FLIGHT_EPIC); break;
|
||||
case 40122: need_cast = (m_form == FORM_FLIGHT_EPIC); break;
|
||||
// another spells have proper stance data
|
||||
default: need_cast = !spellInfo->Stances || m_form != 0 && (spellInfo->Stances & (1<<(m_form-1))); break;
|
||||
}
|
||||
|
||||
//Check CasterAuraStates
|
||||
return need_cast && (!spellInfo->CasterAuraState || HasAuraState(AuraState(spellInfo->CasterAuraState)));
|
||||
}
|
||||
|
||||
void Player::learnSpell(uint32 spell_id, bool dependent)
|
||||
{
|
||||
PlayerSpellMap::iterator itr = m_spells.find(spell_id);
|
||||
|
||||
bool disabled = (itr != m_spells.end()) ? itr->second->disabled : false;
|
||||
bool active = disabled ? itr->second->active : true;
|
||||
|
||||
bool learning = addSpell(spell_id,active,true,false);
|
||||
bool learning = addSpell(spell_id,active,true,dependent,false);
|
||||
|
||||
// learn all disabled higher ranks (recursive)
|
||||
if(disabled)
|
||||
|
|
@ -2873,7 +2930,7 @@ void Player::learnSpell(uint32 spell_id)
|
|||
{
|
||||
PlayerSpellMap::iterator iter = m_spells.find(i->second);
|
||||
if (iter != m_spells.end() && iter->second->disabled)
|
||||
learnSpell(i->second);
|
||||
learnSpell(i->second,false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2901,10 +2958,8 @@ void Player::removeSpell(uint32 spell_id, bool disabled)
|
|||
if(HasSpell(itr2->second) && !GetTalentSpellPos(itr2->second))
|
||||
removeSpell(itr2->second,disabled);
|
||||
|
||||
// removing
|
||||
WorldPacket data(SMSG_REMOVED_SPELL, 4);
|
||||
data << uint16(spell_id);
|
||||
GetSession()->SendPacket(&data);
|
||||
bool cur_active = itr->second->active;
|
||||
bool cur_dependent = itr->second->dependent;
|
||||
|
||||
if (disabled)
|
||||
{
|
||||
|
|
@ -3017,7 +3072,54 @@ void Player::removeSpell(uint32 spell_id, bool disabled)
|
|||
for(SpellLearnSpellMap::const_iterator itr2 = spell_begin; itr2 != spell_end; ++itr2)
|
||||
removeSpell(itr2->second.spell, disabled);
|
||||
|
||||
// TODO: recast if need lesser ranks spell for passive with IsPassiveSpellStackableWithRanks
|
||||
// activate lesser rank in spellbook/action bar, and cast it if need
|
||||
bool prev_activate = false;
|
||||
|
||||
if(uint32 prev_id = spellmgr.GetPrevSpellInChain (spell_id))
|
||||
{
|
||||
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
|
||||
|
||||
// if talent then lesser rank also talen and need learn
|
||||
if(talentCosts)
|
||||
learnSpell (prev_id,false);
|
||||
// if ranked non-stackable spell: need activate lesser rank and update dendence state
|
||||
else if(cur_active && !SpellMgr::canStackSpellRanks(spellInfo) && spellmgr.GetSpellRank(spellInfo->Id) != 0)
|
||||
{
|
||||
// need manually update dependence state (learn spell ignore like attempts)
|
||||
PlayerSpellMap::iterator prev_itr = m_spells.find(prev_id);
|
||||
if (prev_itr != m_spells.end())
|
||||
{
|
||||
if(prev_itr->second->dependent != cur_dependent)
|
||||
{
|
||||
prev_itr->second->dependent = cur_dependent;
|
||||
if(prev_itr->second->state != PLAYERSPELL_NEW)
|
||||
prev_itr->second->state = PLAYERSPELL_CHANGED;
|
||||
}
|
||||
|
||||
// now re-learn if need re-activate
|
||||
if(cur_active && !prev_itr->second->active)
|
||||
{
|
||||
if(addSpell(prev_id,true,false,prev_itr->second->dependent,prev_itr->second->disabled))
|
||||
{
|
||||
// downgrade spell ranks in spellbook and action bar
|
||||
WorldPacket data(SMSG_SUPERCEDED_SPELL, (4));
|
||||
data << uint16(spell_id);
|
||||
data << uint16(prev_id);
|
||||
GetSession()->SendPacket( &data );
|
||||
prev_activate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove from spell book if not replaced by lesser rank
|
||||
if(!prev_activate)
|
||||
{
|
||||
WorldPacket data(SMSG_REMOVED_SPELL, 4);
|
||||
data << uint16(spell_id);
|
||||
GetSession()->SendPacket(&data);
|
||||
}
|
||||
}
|
||||
|
||||
void Player::RemoveArenaSpellCooldowns()
|
||||
|
|
@ -4708,7 +4810,7 @@ bool Player::UpdateCraftSkill(uint32 spellid)
|
|||
if(spellEntry && spellEntry->Mechanic==MECHANIC_DISCOVERY)
|
||||
{
|
||||
if(uint32 discoveredSpell = GetSkillDiscoverySpell(_spell_idx->second->skillId, spellid, this))
|
||||
learnSpell(discoveredSpell);
|
||||
learnSpell(discoveredSpell,false);
|
||||
}
|
||||
|
||||
uint32 craft_skill_gain = sWorld.getConfig(CONFIG_SKILL_GAIN_CRAFTING);
|
||||
|
|
@ -15148,7 +15250,7 @@ void Player::_LoadSpells(QueryResult *result)
|
|||
{
|
||||
Field *fields = result->Fetch();
|
||||
|
||||
addSpell(fields[0].GetUInt16(), fields[1].GetBool(), false, fields[2].GetBool());
|
||||
addSpell(fields[0].GetUInt16(), fields[1].GetBool(), false, false, fields[2].GetBool());
|
||||
}
|
||||
while( result->NextRow() );
|
||||
|
||||
|
|
@ -15910,7 +16012,9 @@ void Player::_SaveSpells()
|
|||
++next;
|
||||
if (itr->second->state == PLAYERSPELL_REMOVED || itr->second->state == PLAYERSPELL_CHANGED)
|
||||
CharacterDatabase.PExecute("DELETE FROM character_spell WHERE guid = '%u' and spell = '%u'", GetGUIDLow(), itr->first);
|
||||
if (itr->second->state == PLAYERSPELL_NEW || itr->second->state == PLAYERSPELL_CHANGED)
|
||||
|
||||
// add only changed/new not dependent spells
|
||||
if (!itr->second->dependent && (itr->second->state == PLAYERSPELL_NEW || itr->second->state == PLAYERSPELL_CHANGED))
|
||||
CharacterDatabase.PExecute("INSERT INTO character_spell (guid,spell,active,disabled) VALUES ('%u', '%u', '%u', '%u')", GetGUIDLow(), itr->first, itr->second->active ? 1 : 0,itr->second->disabled ? 1 : 0);
|
||||
|
||||
if (itr->second->state == PLAYERSPELL_REMOVED)
|
||||
|
|
@ -18129,9 +18233,9 @@ void Player::learnDefaultSpells()
|
|||
uint32 tspell = *itr;
|
||||
sLog.outDebug("PLAYER (Class: %u Race: %u): Adding initial spell, id = %u",uint32(getClass()),uint32(getRace()), tspell);
|
||||
if(!IsInWorld()) // will send in INITIAL_SPELLS in list anyway at map add
|
||||
addSpell(tspell,true,true,false);
|
||||
addSpell(tspell,true,true,true,false);
|
||||
else // but send in normal spell in game learn case
|
||||
learnSpell(tspell);
|
||||
learnSpell(tspell,true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -18241,8 +18345,8 @@ void Player::learnSkillRewardedSpells(uint32 skill_id )
|
|||
|
||||
if (sSpellStore.LookupEntry(pAbility->spellId))
|
||||
{
|
||||
// Ok need learn spell
|
||||
learnSpell(pAbility->spellId);
|
||||
// Ok need learn dependent spell
|
||||
learnSpell(pAbility->spellId,true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue