Advertisement
Guest User

Player::AddSpell

a guest
Apr 6th, 2020
250
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 13.91 KB | None | 0 0
  1. bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent, bool disabled, bool loading /*= false*/, int32 fromSkill /*= 0*/)
  2. {
  3.     SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
  4.     if (!spellInfo)
  5.     {
  6.         // do character spell book cleanup (all characters)
  7.         if (!IsInWorld() && !learning)                       // spell load case
  8.         {
  9.             TC_LOG_ERROR("spells", "Player::AddSpell: Spell (ID: %u) does not exist. deleting for all characters in `character_spell`.", spellId);
  10.  
  11.             DeleteSpellFromAllPlayers(spellId);
  12.         }
  13.         else
  14.             TC_LOG_ERROR("spells", "Player::AddSpell: Spell (ID: %u) does not exist", spellId);
  15.  
  16.         return false;
  17.     }
  18.  
  19.     if (!SpellMgr::IsSpellValid(spellInfo, this, false))
  20.     {
  21.         // do character spell book cleanup (all characters)
  22.         if (!IsInWorld() && !learning)                       // spell load case
  23.         {
  24.             TC_LOG_ERROR("spells", "Player::AddSpell: Spell (ID: %u) is invalid. deleting for all characters in `character_spell`.", spellId);
  25.  
  26.             DeleteSpellFromAllPlayers(spellId);
  27.         }
  28.         else
  29.             TC_LOG_ERROR("spells", "Player::AddSpell: Spell (ID: %u) is invalid", spellId);
  30.  
  31.         return false;
  32.     }
  33.  
  34.     PlayerSpellState state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;
  35.  
  36.     bool dependent_set = false;
  37.     bool disabled_case = false;
  38.     bool superceded_old = false;
  39.  
  40.     PlayerSpellMap::iterator itr = m_spells.find(spellId);
  41.  
  42.     // Remove temporary spell if found to prevent conflicts
  43.     if (itr != m_spells.end() && itr->second->state == PLAYERSPELL_TEMPORARY)
  44.         RemoveTemporarySpell(spellId);
  45.     else if (itr != m_spells.end())
  46.     {
  47.         uint32 next_active_spell_id = 0;
  48.         // fix activate state for non-stackable low rank (and find next spell for !active case)
  49.         if (spellInfo->IsRanked())
  50.         {
  51.             if (uint32 next = sSpellMgr->GetNextSpellInChain(spellId))
  52.             {
  53.                 if (HasSpell(next))
  54.                 {
  55.                     // high rank already known so this must !active
  56.                     active = false;
  57.                     next_active_spell_id = next;
  58.                 }
  59.             }
  60.         }
  61.  
  62.         // not do anything if already known in expected state
  63.         if (itr->second->state != PLAYERSPELL_REMOVED && itr->second->active == active &&
  64.             itr->second->dependent == dependent && itr->second->disabled == disabled)
  65.         {
  66.             if (!IsInWorld() && !learning)                   // explicitly load from DB and then exist in it already and set correctly
  67.                 itr->second->state = PLAYERSPELL_UNCHANGED;
  68.  
  69.             return false;
  70.         }
  71.  
  72.         // dependent spell known as not dependent, overwrite state
  73.         if (itr->second->state != PLAYERSPELL_REMOVED && !itr->second->dependent && dependent)
  74.         {
  75.             itr->second->dependent = dependent;
  76.             if (itr->second->state != PLAYERSPELL_NEW)
  77.                 itr->second->state = PLAYERSPELL_CHANGED;
  78.             dependent_set = true;
  79.         }
  80.  
  81.         // update active state for known spell
  82.         if (itr->second->active != active && itr->second->state != PLAYERSPELL_REMOVED && !itr->second->disabled)
  83.         {
  84.             itr->second->active = active;
  85.  
  86.             if (!IsInWorld() && !learning && !dependent_set) // explicitly load from DB and then exist in it already and set correctly
  87.                 itr->second->state = PLAYERSPELL_UNCHANGED;
  88.             else if (itr->second->state != PLAYERSPELL_NEW)
  89.                 itr->second->state = PLAYERSPELL_CHANGED;
  90.  
  91.             if (active)
  92.             {
  93.                 if (spellInfo->IsPassive() && IsNeedCastPassiveSpellAtLearn(spellInfo))
  94.                     CastSpell(this, spellId, true);
  95.             }
  96.             else if (IsInWorld())
  97.             {
  98.                 if (next_active_spell_id)
  99.                     SendSupercededSpell(spellId, next_active_spell_id);
  100.                 else
  101.                 {
  102.                     WorldPackets::Spells::UnlearnedSpells unlearnedSpells;
  103.                     unlearnedSpells.SpellID.push_back(spellId);
  104.                     SendDirectMessage(unlearnedSpells.Write());
  105.                 }
  106.             }
  107.  
  108.             return active;                                  // learn (show in spell book if active now)
  109.         }
  110.  
  111.         if (itr->second->disabled != disabled && itr->second->state != PLAYERSPELL_REMOVED)
  112.         {
  113.             if (itr->second->state != PLAYERSPELL_NEW)
  114.                 itr->second->state = PLAYERSPELL_CHANGED;
  115.             itr->second->disabled = disabled;
  116.  
  117.             if (disabled)
  118.                 return false;
  119.  
  120.             disabled_case = true;
  121.         }
  122.         else switch (itr->second->state)
  123.         {
  124.             case PLAYERSPELL_UNCHANGED:                     // known saved spell
  125.                 return false;
  126.             case PLAYERSPELL_REMOVED:                       // re-learning removed not saved spell
  127.             {
  128.                 delete itr->second;
  129.                 m_spells.erase(itr);
  130.                 state = PLAYERSPELL_CHANGED;
  131.                 break;                                      // need re-add
  132.             }
  133.             default:                                        // known not saved yet spell (new or modified)
  134.             {
  135.                 // can be in case spell loading but learned at some previous spell loading
  136.                 if (!IsInWorld() && !learning && !dependent_set)
  137.                     itr->second->state = PLAYERSPELL_UNCHANGED;
  138.  
  139.                 return false;
  140.             }
  141.         }
  142.     }
  143.  
  144.     if (!disabled_case) // skip new spell adding if spell already known (disabled spells case)
  145.     {
  146.         // non talent spell: learn low ranks (recursive call)
  147.         if (uint32 prev_spell = sSpellMgr->GetPrevSpellInChain(spellId))
  148.         {
  149.             if (!IsInWorld() || disabled)                    // at spells loading, no output, but allow save
  150.                 AddSpell(prev_spell, active, true, true, disabled, false, fromSkill);
  151.             else                                            // at normal learning
  152.                 LearnSpell(prev_spell, true, fromSkill);
  153.         }
  154.  
  155.         PlayerSpell* newspell = new PlayerSpell;
  156.         newspell->state     = state;
  157.         newspell->active    = active;
  158.         newspell->dependent = dependent;
  159.         newspell->disabled  = disabled;
  160.  
  161.         // replace spells in action bars and spellbook to bigger rank if only one spell rank must be accessible
  162.         if (newspell->active && !newspell->disabled && spellInfo->IsRanked())
  163.         {
  164.             for (PlayerSpellMap::iterator itr2 = m_spells.begin(); itr2 != m_spells.end(); ++itr2)
  165.             {
  166.                 if (itr2->second->state == PLAYERSPELL_REMOVED)
  167.                     continue;
  168.  
  169.                 SpellInfo const* i_spellInfo = sSpellMgr->GetSpellInfo(itr2->first);
  170.                 if (!i_spellInfo)
  171.                     continue;
  172.  
  173.                 if (spellInfo->IsDifferentRankOf(i_spellInfo))
  174.                 {
  175.                     if (itr2->second->active)
  176.                     {
  177.                         if (spellInfo->IsHighRankOf(i_spellInfo))
  178.                         {
  179.                             if (IsInWorld())                 // not send spell (re-/over-)learn packets at loading
  180.                                 SendSupercededSpell(itr2->first, spellId);
  181.  
  182.                             // mark old spell as disable (SMSG_SUPERCEDED_SPELL replace it in client by new)
  183.                             itr2->second->active = false;
  184.                             if (itr2->second->state != PLAYERSPELL_NEW)
  185.                                 itr2->second->state = PLAYERSPELL_CHANGED;
  186.                             superceded_old = true;          // new spell replace old in action bars and spell book.
  187.                         }
  188.                         else
  189.                         {
  190.                             if (IsInWorld())                 // not send spell (re-/over-)learn packets at loading
  191.                                 SendSupercededSpell(spellId, itr2->first);
  192.  
  193.                             // mark new spell as disable (not learned yet for client and will not learned)
  194.                             newspell->active = false;
  195.                             if (newspell->state != PLAYERSPELL_NEW)
  196.                                 newspell->state = PLAYERSPELL_CHANGED;
  197.                         }
  198.                     }
  199.                 }
  200.             }
  201.         }
  202.  
  203.         m_spells[spellId] = newspell;
  204.  
  205.         // return false if spell disabled
  206.         if (newspell->disabled)
  207.             return false;
  208.     }
  209.  
  210.     // cast talents with SPELL_EFFECT_LEARN_SPELL (other dependent spells will learned later as not auto-learned)
  211.     // note: all spells with SPELL_EFFECT_LEARN_SPELL isn't passive
  212.     if (!loading && spellInfo->HasAttribute(SPELL_ATTR0_CU_IS_TALENT) && spellInfo->HasEffect(SPELL_EFFECT_LEARN_SPELL))
  213.     {
  214.         // ignore stance requirement for talent learn spell (stance set for spell only for client spell description show)
  215.         CastSpell(this, spellId, true);
  216.     }
  217.     // also cast passive spells (including all talents without SPELL_EFFECT_LEARN_SPELL) with additional checks
  218.     else if (spellInfo->IsPassive())
  219.     {
  220.         if (IsNeedCastPassiveSpellAtLearn(spellInfo))
  221.             CastSpell(this, spellId, true);
  222.     }
  223.     else if (spellInfo->HasEffect(SPELL_EFFECT_SKILL_STEP))
  224.     {
  225.         CastSpell(this, spellId, true);
  226.         return false;
  227.     }
  228.  
  229.     // update free primary prof.points (if any, can be none in case GM .learn prof. learning)
  230.     if (uint32 freeProfs = GetFreePrimaryProfessionPoints())
  231.     {
  232.         if (spellInfo->IsPrimaryProfessionFirstRank())
  233.             SetFreePrimaryProfessions(freeProfs - 1);
  234.     }
  235.  
  236.     SkillLineAbilityMapBounds skill_bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId);
  237.  
  238.     if (SpellLearnSkillNode const* spellLearnSkill = sSpellMgr->GetSpellLearnSkill(spellId))
  239.     {
  240.         // add dependent skills if this spell is not learned from adding skill already
  241.         if (spellLearnSkill->skill != fromSkill)
  242.         {
  243.             uint32 skill_value = GetPureSkillValue(spellLearnSkill->skill);
  244.             uint32 skill_max_value = GetPureMaxSkillValue(spellLearnSkill->skill);
  245.  
  246.             if (skill_value < spellLearnSkill->value)
  247.                 skill_value = spellLearnSkill->value;
  248.  
  249.             uint32 new_skill_max_value = spellLearnSkill->maxvalue == 0 ? GetMaxSkillValueForLevel() : spellLearnSkill->maxvalue;
  250.  
  251.             if (skill_max_value < new_skill_max_value)
  252.                 skill_max_value = new_skill_max_value;
  253.  
  254.             SetSkill(spellLearnSkill->skill, spellLearnSkill->step, skill_value, skill_max_value);
  255.         }
  256.     }
  257.     else
  258.     {
  259.         // not ranked skills
  260.         for (SkillLineAbilityMap::const_iterator _spell_idx = skill_bounds.first; _spell_idx != skill_bounds.second; ++_spell_idx)
  261.         {
  262.             SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->SkillLine);
  263.             if (!pSkill)
  264.                 continue;
  265.  
  266.             if (_spell_idx->second->SkillLine == fromSkill)
  267.                 continue;
  268.  
  269.             // Runeforging special case
  270.             if ((_spell_idx->second->AcquireMethod == SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN && !HasSkill(_spell_idx->second->SkillLine)) || ((_spell_idx->second->SkillLine == SKILL_RUNEFORGING) && _spell_idx->second->TrivialSkillLineRankHigh == 0))
  271.                 if (SkillRaceClassInfoEntry const* rcInfo = sDB2Manager.GetSkillRaceClassInfo(_spell_idx->second->SkillLine, getRace(), getClass()))
  272.                     LearnDefaultSkill(rcInfo);
  273.         }
  274.     }
  275.  
  276.     // learn dependent spells
  277.     SpellLearnSpellMapBounds spell_bounds = sSpellMgr->GetSpellLearnSpellMapBounds(spellId);
  278.  
  279.     for (SpellLearnSpellMap::const_iterator itr2 = spell_bounds.first; itr2 != spell_bounds.second; ++itr2)
  280.     {
  281.         if (!itr2->second.AutoLearned)
  282.         {
  283.             if (!IsInWorld() || !itr2->second.Active)       // at spells loading, no output, but allow save
  284.                 AddSpell(itr2->second.Spell, itr2->second.Active, true, true, false);
  285.             else                                            // at normal learning
  286.                 LearnSpell(itr2->second.Spell, true);
  287.         }
  288.  
  289.         if (itr2->second.OverridesSpell && itr2->second.Active)
  290.             AddOverrideSpell(itr2->second.OverridesSpell, itr2->second.Spell);
  291.     }
  292.  
  293.     if (!GetSession()->PlayerLoading())
  294.     {
  295.         // not ranked skills
  296.         for (SkillLineAbilityMap::const_iterator _spell_idx = skill_bounds.first; _spell_idx != skill_bounds.second; ++_spell_idx)
  297.         {
  298.             UpdateCriteria(CRITERIA_TYPE_LEARN_SKILL_LINE, _spell_idx->second->SkillLine);
  299.             UpdateCriteria(CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS, _spell_idx->second->SkillLine);
  300.         }
  301.  
  302.         UpdateCriteria(CRITERIA_TYPE_LEARN_SPELL, spellId);
  303.     }
  304.  
  305.     // needs to be when spell is already learned, to prevent infinite recursion crashes
  306.     if (sDB2Manager.GetMount(spellId))
  307.         GetSession()->GetCollectionMgr()->AddMount(spellId, MOUNT_STATUS_NONE, false, IsInWorld() ? false : true);
  308.  
  309.     // need to add Battle pets automatically into pet journal
  310.     for (BattlePetSpeciesEntry const* entry : sBattlePetSpeciesStore)
  311.     {
  312.         if (entry->SummonSpellID == int32(spellId) && GetSession()->GetBattlePetMgr()->GetPetCount(entry->ID) == 0)
  313.         {
  314.             GetSession()->GetBattlePetMgr()->AddPet(entry->ID, entry->CreatureID, BattlePetMgr::RollPetBreed(entry->ID), BattlePetMgr::GetDefaultPetQuality(entry->ID));
  315.             UpdateCriteria(CRITERIA_TYPE_OWN_BATTLE_PET_COUNT);
  316.             break;
  317.         }
  318.     }
  319.  
  320.     // return true (for send learn packet) only if spell active (in case ranked spells) and not replace old spell
  321.     return active && !disabled && !superceded_old;
  322. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement