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:
VladimirMangos 2009-01-24 07:18:02 +03:00
parent 4b5aba18f8
commit bbf8fd0742
10 changed files with 222 additions and 85 deletions

View file

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