fix(Core/Player): Use SkillLineAbility.dbc to determine player initia… (#6015)

* fix(Core/Player): Use SkillLineAbility.dbc to determine player initial spells - skill assignment done in a new table `playercreateinfo_skills`

* Cherry-pick 2a3546ca36

* Cherry-Pick d28b66bca8

* Cherry-Pick 193408f335

- Closes https://github.com/azerothcore/azerothcore-wotlk/issues/1659
- Closes https://github.com/azerothcore/azerothcore-wotlk/issues/6036
- Closes https://github.com/chromiecraft/chromiecraft/issues/693

Co-Authored-By: Shauren shauren.trinity@gmail.com
Co-Authored-By: Rothend 67004168+Rothend@users.noreply.github.com
Co-Authored-By: claudiodfc claudio.daniel.f.c@gmail.com
This commit is contained in:
Kitzunu
2021-06-21 21:23:18 +02:00
committed by GitHub
parent eeab4f5de6
commit 1be561e03b
17 changed files with 579 additions and 251 deletions

View File

@@ -20,6 +20,7 @@
#include "ChannelMgr.h"
#include "CharacterDatabaseCleaner.h"
#include "Chat.h"
#include "Config.h"
#include "Common.h"
#include "ConditionMgr.h"
#include "CreatureAI.h"
@@ -1186,7 +1187,8 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo
}
// original spells
learnDefaultSpells();
LearnDefaultSkills();
LearnCustomSpells();
// original action bar
for (PlayerCreateInfoActions::const_iterator action_itr = info->action.begin(); action_itr != info->action.end(); ++action_itr)
@@ -4077,12 +4079,6 @@ bool Player::_addSpell(uint32 spellId, uint8 addSpecMask, bool temporary, bool l
SetFreePrimaryProfessions(freeProfs - 1);
}
// pussywizard: update 310 flyer speed
if (!Has310Flyer(false))
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED && spellInfo->Effects[i].CalcValue() == 310)
SetHas310Flyer(true);
uint16 maxskill = GetMaxSkillValueForLevel();
SpellLearnSkillNode const* spellLearnSkill = sSpellMgr->GetSpellLearnSkill(spellId);
SkillLineAbilityMapBounds skill_bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId);
@@ -4102,29 +4098,28 @@ bool Player::_addSpell(uint32 spellId, uint8 addSpecMask, bool temporary, bool l
}
else
{
// pussywizard: checked, ok
// not ranked skills
for (SkillLineAbilityMap::const_iterator _spell_idx = skill_bounds.first; _spell_idx != skill_bounds.second; ++_spell_idx)
{
SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->skillId);
if (!pSkill || HasSkill(pSkill->id))
continue;
if (_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL || // pussywizard: learning autolearned spell from skill ensures having the skill
((pSkill->id == SKILL_LOCKPICKING || pSkill->id == SKILL_RUNEFORGING) && _spell_idx->second->max_value == 0)) // pussywizard: learning any spell from lockpicking or runeforging ensures having the skill
SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->SkillLine);
if (!pSkill)
{
switch (GetSkillRangeType(pSkill, _spell_idx->second->racemask != 0))
continue;
}
if (_spell_idx->second->AcquireMethod == SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN && !HasSkill(pSkill->id))
{
LearnDefaultSkill(pSkill->id, 0);
}
if (pSkill->id == SKILL_MOUNTS && !Has310Flyer(false))
{
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
case SKILL_RANGE_LANGUAGE:
SetSkill(pSkill->id, GetSkillStep(pSkill->id), 300, 300);
break;
case SKILL_RANGE_LEVEL:
SetSkill(pSkill->id, GetSkillStep(pSkill->id), 1, GetMaxSkillValueForLevel());
break;
case SKILL_RANGE_MONO:
SetSkill(pSkill->id, GetSkillStep(pSkill->id), 1, 1);
break;
default:
break;
if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED && spellInfo->Effects[i].CalcValue() == 310)
{
SetHas310Flyer(true);
}
}
}
}
@@ -4135,8 +4130,8 @@ bool Player::_addSpell(uint32 spellId, uint8 addSpecMask, bool temporary, bool l
{
for (SkillLineAbilityMap::const_iterator _spell_idx = skill_bounds.first; _spell_idx != skill_bounds.second; ++_spell_idx)
{
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE, _spell_idx->second->skillId);
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS, _spell_idx->second->skillId);
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE, _spell_idx->second->SkillLine);
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS, _spell_idx->second->SkillLine);
}
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL, spellId);
}
@@ -4194,9 +4189,9 @@ void Player::learnSpell(uint32 spellId)
}
}
void Player::removeSpell(uint32 spellId, uint8 removeSpecMask, bool onlyTemporary)
void Player::removeSpell(uint32 spell_id, uint8 removeSpecMask, bool onlyTemporary)
{
PlayerSpellMap::iterator itr = m_spells.find(spellId);
PlayerSpellMap::iterator itr = m_spells.find(spell_id);
if (itr == m_spells.end())
return;
@@ -4210,15 +4205,15 @@ void Player::removeSpell(uint32 spellId, uint8 removeSpecMask, bool onlyTemporar
// pussywizard: remove non-talent higher ranks (recursive)
// pussywizard: do this at the beginning, not in the middle of removing!
if (uint32 nextSpell = sSpellMgr->GetNextSpellInChain(spellId))
if (uint32 nextSpell = sSpellMgr->GetNextSpellInChain(spell_id))
if (!GetTalentSpellPos(nextSpell))
removeSpell(nextSpell, removeSpecMask, onlyTemporary);
// xinef: if current spell has talentcost, remove spells requiring this spell
uint32 firstRankSpellId = sSpellMgr->GetFirstSpellInChain(spellId);
uint32 firstRankSpellId = sSpellMgr->GetFirstSpellInChain(spell_id);
if (GetTalentSpellCost(firstRankSpellId))
{
SpellsRequiringSpellMapBounds spellsRequiringSpell = sSpellMgr->GetSpellsRequiringSpellBounds(spellId);
SpellsRequiringSpellMapBounds spellsRequiringSpell = sSpellMgr->GetSpellsRequiringSpellBounds(firstRankSpellId);
for (auto spellsItr = spellsRequiringSpell.first; spellsItr != spellsRequiringSpell.second; ++spellsItr)
{
removeSpell(spellsItr->second, removeSpecMask, onlyTemporary);
@@ -4226,7 +4221,7 @@ void Player::removeSpell(uint32 spellId, uint8 removeSpecMask, bool onlyTemporar
}
// pussywizard: re-search, it can be corrupted in prev loop
itr = m_spells.find(spellId);
itr = m_spells.find(spell_id);
if (itr == m_spells.end())
return;
@@ -4249,13 +4244,13 @@ void Player::removeSpell(uint32 spellId, uint8 removeSpecMask, bool onlyTemporar
// xinef: this is used for talents and they are not removed in removeSpell function...
// xinef: however ill leave this here just in case
// pussywizard: remove owned aura obtained from currently removed spell
RemoveOwnedAura(spellId);
RemoveOwnedAura(spell_id);
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell_id);
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
// pussywizard: remove pet auras
if (PetAura const* petSpell = sSpellMgr->GetPetAura(spellId, i))
if (PetAura const* petSpell = sSpellMgr->GetPetAura(spell_id, i))
RemovePetAura(petSpell);
// pussywizard: remove all triggered auras
@@ -4275,13 +4270,13 @@ void Player::removeSpell(uint32 spellId, uint8 removeSpecMask, bool onlyTemporar
if (Has310Flyer(false))
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED && spellInfo->Effects[i].CalcValue() == 310)
Has310Flyer(true, spellId);
Has310Flyer(true, spell_id);
// pussywizard: remove dependent skill
SpellLearnSkillNode const* spellLearnSkill = sSpellMgr->GetSpellLearnSkill(spellId);
SpellLearnSkillNode const* spellLearnSkill = sSpellMgr->GetSpellLearnSkill(spell_id);
if (spellLearnSkill)
{
uint32 prev_spell = sSpellMgr->GetPrevSpellInChain(spellId);
uint32 prev_spell = sSpellMgr->GetPrevSpellInChain(spell_id);
if (!prev_spell) // pussywizard: first rank, remove skill
SetSkill(spellLearnSkill->skill, 0, 0, 0);
@@ -4313,24 +4308,28 @@ void Player::removeSpell(uint32 spellId, uint8 removeSpecMask, bool onlyTemporar
}
else
{
// pussywizard: checked, ok
SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId);
for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spell_id);
// most likely will never be used, haven't heard of cases where players unlearn a mount
if (Has310Flyer(false) && spellInfo)
{
SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->skillId);
if (!pSkill)
continue;
// pussywizard: don't understand why whole skill is removed when just single spell from it is removed
if ((_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL && pSkill->categoryId != SKILL_CATEGORY_CLASS) || // pussywizard: don't unlearn class skills
((pSkill->id == SKILL_LOCKPICKING || pSkill->id == SKILL_RUNEFORGING) && _spell_idx->second->max_value == 0))
for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
{
// not reset skills for professions and racial abilities
if ((pSkill->categoryId == SKILL_CATEGORY_SECONDARY || pSkill->categoryId == SKILL_CATEGORY_PROFESSION) && (IsProfessionSkill(pSkill->id) || _spell_idx->second->racemask != 0))
SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->SkillLine);
if (!pSkill)
continue;
// pussywizard: this is needed for weapon/armor/language skills to remove them when loosing spell
SetSkill(pSkill->id, GetSkillStep(pSkill->id), 0, 0);
if (_spell_idx->second->SkillLine == SKILL_MOUNTS)
{
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED &&
spellInfo->Effects[i].CalcValue() == 310)
{
Has310Flyer(true, spell_id); // with true as first argument its also used to set/remove the flag
break;
}
}
}
}
}
}
@@ -4338,8 +4337,8 @@ void Player::removeSpell(uint32 spellId, uint8 removeSpecMask, bool onlyTemporar
// pussywizard: remove from spell book (can't be replaced by previous rank, because such spells can't be unlearnt)
if (!onlyTemporary || ((!spellInfo->HasAttribute(SpellAttr0(SPELL_ATTR0_PASSIVE | SPELL_ATTR0_DO_NOT_DISPLAY)) || !spellInfo->HasAnyAura()) && !spellInfo->HasEffect(SPELL_EFFECT_LEARN_SPELL)))
{
sScriptMgr->OnPlayerForgotSpell(this, spellId);
SendLearnPacket(spellId, false);
sScriptMgr->OnPlayerForgotSpell(this, spell_id);
SendLearnPacket(spell_id, false);
}
}
@@ -4363,7 +4362,7 @@ bool Player::Has310Flyer(bool checkAllSpells, uint32 excludeSpellId)
SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(itr->first);
for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
{
if (_spell_idx->second->skillId != SKILL_MOUNTS)
if (_spell_idx->second->SkillLine != SKILL_MOUNTS)
break; // We can break because mount spells belong only to one skillline (at least 310 flyers do)
spellInfo = sSpellMgr->AssertSpellInfo(itr->first);
@@ -6304,9 +6303,6 @@ bool Player::UpdateSkill(uint32 skill_id, uint32 step)
if (!skill_id)
return false;
if (skill_id == SKILL_FIST_WEAPONS)
skill_id = SKILL_UNARMED;
SkillStatusMap::iterator itr = mSkillStatus.find(skill_id);
if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
return false;
@@ -6328,6 +6324,7 @@ bool Player::UpdateSkill(uint32 skill_id, uint32 step)
SetUInt32Value(valueIndex, MAKE_SKILL_VALUE(new_value, max));
if (itr->second.uState != SKILL_NEW)
itr->second.uState = SKILL_CHANGED;
UpdateSkillEnchantments(skill_id, value, new_value);
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL, skill_id);
return true;
@@ -6355,25 +6352,25 @@ bool Player::UpdateCraftSkill(uint32 spellid)
for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
{
if (_spell_idx->second->skillId)
if (_spell_idx->second->SkillLine)
{
uint32 SkillValue = GetPureSkillValue(_spell_idx->second->skillId);
uint32 SkillValue = GetPureSkillValue(_spell_idx->second->SkillLine);
// Alchemy Discoveries here
SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(spellid);
if (spellEntry && spellEntry->Mechanic == MECHANIC_DISCOVERY)
{
if (uint32 discoveredSpell = GetSkillDiscoverySpell(_spell_idx->second->skillId, spellid, this))
if (uint32 discoveredSpell = GetSkillDiscoverySpell(_spell_idx->second->SkillLine, spellid, this))
learnSpell(discoveredSpell);
}
uint32 craft_skill_gain = sWorld->getIntConfig(CONFIG_SKILL_GAIN_CRAFTING);
return UpdateSkillPro(_spell_idx->second->skillId, SkillGainChance(SkillValue,
_spell_idx->second->max_value,
(_spell_idx->second->max_value + _spell_idx->second->min_value) / 2,
_spell_idx->second->min_value),
craft_skill_gain);
return UpdateSkillPro(_spell_idx->second->SkillLine, SkillGainChance(SkillValue,
_spell_idx->second->TrivialSkillLineRankHigh,
(_spell_idx->second->TrivialSkillLineRankHigh + _spell_idx->second->TrivialSkillLineRankLow) / 2,
_spell_idx->second->TrivialSkillLineRankLow),
craft_skill_gain);
}
}
return false;
@@ -6524,7 +6521,7 @@ void Player::UpdateWeaponSkill(Unit* victim, WeaponAttackType attType)
UpdateSkill(SKILL_UNARMED, weapon_skill_gain);
UpdateSkill(SKILL_FIST_WEAPONS, weapon_skill_gain);
}
else if (tmpitem)
else if (tmpitem && tmpitem->GetTemplate()->SubClass != ITEM_SUBCLASS_WEAPON_FISHING_POLE)
{
switch (tmpitem->GetTemplate()->SubClass)
{
@@ -6610,11 +6607,11 @@ void Player::UpdateSkillsForLevel()
continue;
uint32 pskill = itr->first;
SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(pskill);
if (!pSkill)
SkillRaceClassInfoEntry const* rcEntry = GetSkillRaceClassInfo(pskill, getRace(), getClass());
if (!rcEntry)
continue;
if (GetSkillRangeType(pSkill, false) != SKILL_RANGE_LEVEL)
if (GetSkillRangeType(rcEntry) != SKILL_RANGE_LEVEL)
continue;
uint32 valueIndex = PLAYER_SKILL_VALUE_INDEX(itr->second.pos);
@@ -6626,7 +6623,7 @@ void Player::UpdateSkillsForLevel()
if (max != 1)
{
/// maximize skill always
if (alwaysMaxSkill)
if (alwaysMaxSkill || (rcEntry->Flags & SKILL_FLAG_ALWAYS_MAX_VALUE))
{
SetUInt32Value(valueIndex, MAKE_SKILL_VALUE(maxSkill, maxSkill));
if (itr->second.uState != SKILL_NEW)
@@ -6717,8 +6714,8 @@ void Player::SetSkill(uint16 id, uint16 step, uint16 newVal, uint16 maxVal)
// remove all spells that related to this skill
for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
if (SkillLineAbilityEntry const* pAbility = sSkillLineAbilityStore.LookupEntry(j))
if (pAbility->skillId == id)
removeSpell(sSpellMgr->GetFirstSpellInChain(pAbility->spellId), SPEC_MASK_ALL, false);
if (pAbility->SkillLine == id)
removeSpell(sSpellMgr->GetFirstSpellInChain(pAbility->Spell), SPEC_MASK_ALL, false);
}
}
else if (newVal) //add
@@ -18395,7 +18392,9 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, SQLQueryHolder* holder)
m_specsCount = fields[64].GetUInt8();
m_activeSpec = fields[65].GetUInt8();
learnDefaultSpells(); // pussywizard: move before loading spells and talents
LearnDefaultSkills();
LearnCustomSpells();
_LoadSpells(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELLS));
_LoadTalents(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_TALENTS));
@@ -23868,16 +23867,109 @@ void Player::resetSpells()
for (PlayerSpellMap::const_iterator iter = spellMap.begin(); iter != spellMap.end(); ++iter)
removeSpell(iter->first, SPEC_MASK_ALL, false);
learnDefaultSpells();
LearnDefaultSkills();
LearnCustomSpells();
learnQuestRewardedSpells();
}
void Player::learnDefaultSpells()
void Player::LearnCustomSpells()
{
// xinef: learn default race/class spells
PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(true), getClass());
for (PlayerCreateInfoSpells::const_iterator itr = info->spell.begin(); itr != info->spell.end(); ++itr)
_addSpell(*itr, SPEC_MASK_ALL, true);
if (!sWorld->getBoolConfig(CONFIG_START_ALL_SPELLS))
{
return;
}
// learn default race/class spells
PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass());
for (PlayerCreateInfoSpells::const_iterator itr = info->customSpells.begin(); itr != info->customSpells.end(); ++itr)
{
uint32 tspell = *itr;
LOG_DEBUG("entities.player.loading", "PLAYER (Class: %u Race: %u): Adding initial spell, id = %u", uint32(getClass()), uint32(getRace()), tspell);
}
}
void Player::LearnDefaultSkills()
{
// learn default race/class skills
PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass());
for (PlayerCreateInfoSkills::const_iterator itr = info->skills.begin(); itr != info->skills.end(); ++itr)
{
uint32 skillId = itr->SkillId;
if (HasSkill(skillId))
continue;
LearnDefaultSkill(skillId, itr->Rank);
}
}
void Player::LearnDefaultSkill(uint32 skillId, uint16 rank)
{
SkillRaceClassInfoEntry const* rcInfo = GetSkillRaceClassInfo(skillId, getRace(), getClass());
if (!rcInfo)
return;
LOG_DEBUG("entities.player.loading", "PLAYER (Class: %u Race: %u): Adding initial skill, id = %u", uint32(getClass()), uint32(getRace()), skillId);
switch (GetSkillRangeType(rcInfo))
{
case SKILL_RANGE_LANGUAGE:
SetSkill(skillId, 0, 300, 300);
break;
case SKILL_RANGE_LEVEL:
{
uint16 skillValue = 1;
uint16 maxValue = GetMaxSkillValueForLevel();
if (sWorld->getBoolConfig(CONFIG_ALWAYS_MAXSKILL) && !IsProfessionOrRidingSkill(skillId))
{
skillValue = maxValue;
}
else if (rcInfo->Flags & SKILL_FLAG_ALWAYS_MAX_VALUE)
{
skillValue = maxValue;
}
else if (getClass() == CLASS_DEATH_KNIGHT)
{
skillValue = std::min(std::max<uint16>({ 1, uint16((getLevel() - 1) * 5) }), maxValue);
}
else if (skillId == SKILL_FIST_WEAPONS)
{
skillValue = std::max<uint16>(1, GetSkillValue(SKILL_UNARMED));
}
else if (skillId == SKILL_LOCKPICKING)
{
skillValue = std::max<uint16>(1, GetSkillValue(SKILL_LOCKPICKING));
}
SetSkill(skillId, 0, skillValue, maxValue);
break;
}
case SKILL_RANGE_MONO:
SetSkill(skillId, 0, 1, 1);
break;
case SKILL_RANGE_RANK:
{
if (!rank)
{
break;
}
SkillTiersEntry const* tier = sSkillTiersStore.LookupEntry(rcInfo->SkillTierID);
uint16 maxValue = tier->Value[std::max<int32>(rank - 1, 0)];
uint16 skillValue = 1;
if (rcInfo->Flags & SKILL_FLAG_ALWAYS_MAX_VALUE)
{
skillValue = maxValue;
}
else if (getClass() == CLASS_DEATH_KNIGHT)
{
skillValue = std::min(std::max<uint16>({ uint16(1), uint16((getLevel() - 1) * 5) }), maxValue);
}
SetSkill(skillId, rank, skillValue, maxValue);
break;
}
default:
break;
}
}
void Player::learnQuestRewardedSpells(Quest const* quest)
@@ -23932,40 +24024,47 @@ void Player::learnSkillRewardedSpells(uint32 skill_id, uint32 skill_value)
for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
{
SkillLineAbilityEntry const* pAbility = sSkillLineAbilityStore.LookupEntry(j);
if (!pAbility || pAbility->skillId != skill_id || pAbility->learnOnGetSkill != ABILITY_LEARNED_ON_GET_PROFESSION_SKILL)
continue;
// Check race if set
if (pAbility->racemask && !(pAbility->racemask & raceMask))
continue;
// Check class if set
if (pAbility->classmask && !(pAbility->classmask & classMask))
continue;
if (sSpellMgr->GetSpellInfo(pAbility->spellId))
if (!pAbility || pAbility->SkillLine != skill_id)
{
// need unlearn spell
if (skill_value < pAbility->req_skill_value)
removeSpell(pAbility->spellId, SPEC_MASK_ALL, true);
// need learn
else
{
// Xinef: there is next spell and we have enough skill value to obtain it - skip current spell
if (pAbility->req_skill_value > 1 && pAbility->forward_spellid)
{
bool hasForwardSpell = false;
SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(pAbility->forward_spellid);
for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
if (skill_value >= _spell_idx->second->req_skill_value)
{
hasForwardSpell = true;
break;
}
if (hasForwardSpell)
continue;
}
continue;
}
addSpell(pAbility->spellId, SPEC_MASK_ALL, true, true, true);
}
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(pAbility->Spell);
if (!spellInfo)
{
continue;
}
if (pAbility->AcquireMethod != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE && pAbility->AcquireMethod != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN)
{
continue;
}
// Check race if set
if (pAbility->RaceMask && !(pAbility->RaceMask & raceMask))
{
continue;
}
// Check class if set
if (pAbility->ClassMask && !(pAbility->ClassMask & classMask))
{
continue;
}
// need unlearn spell
if (skill_value < pAbility->MinSkillLineRank && pAbility->AcquireMethod == SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE)
{
removeSpell(pAbility->Spell, GetActiveSpec(), true);
}
// need learn
else if (!IsInWorld())
{
addSpell(pAbility->Spell, true, true, true, false);
}
else
{
learnSpell(pAbility->Spell);
}
}
}
@@ -24189,11 +24288,11 @@ bool Player::IsSpellFitByClassAndRace(uint32 spell_id) const
for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
{
// skip wrong race skills
if (_spell_idx->second->racemask && (_spell_idx->second->racemask & racemask) == 0)
if (_spell_idx->second->RaceMask && (_spell_idx->second->RaceMask & racemask) == 0)
continue;
// skip wrong class skills
if (_spell_idx->second->classmask && (_spell_idx->second->classmask & classmask) == 0)
if (_spell_idx->second->ClassMask && (_spell_idx->second->ClassMask & classmask) == 0)
continue;
return true;
@@ -24658,7 +24757,7 @@ bool Player::IsAtRecruitAFriendDistance(WorldObject const* pOther) const
return pOther->GetDistance(player) <= sWorld->getFloatConfig(CONFIG_MAX_RECRUIT_A_FRIEND_DISTANCE);
}
uint32 Player::GetBaseWeaponSkillValue (WeaponAttackType attType) const
uint32 Player::GetBaseWeaponSkillValue(WeaponAttackType attType) const
{
Item* item = GetWeaponForAttack(attType, true);
@@ -24666,8 +24765,8 @@ uint32 Player::GetBaseWeaponSkillValue (WeaponAttackType attType) const
if (attType != BASE_ATTACK && !item)
return 0;
// weapon skill or (unarmed for base attack and for fist weapons)
uint32 skill = (item && item->GetSkill() != SKILL_FIST_WEAPONS) ? item->GetSkill() : uint32(SKILL_UNARMED);
// weapon skill or (unarmed for base attack)
uint32 skill = item ? item->GetSkill() : uint32(SKILL_UNARMED);
return GetBaseSkillValue(skill);
}
@@ -25830,15 +25929,15 @@ void Player::_LoadSkills(PreparedQueryResult result)
uint16 value = fields[1].GetUInt16();
uint16 max = fields[2].GetUInt16();
SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(skill);
if (!pSkill)
SkillRaceClassInfoEntry const* rcEntry = GetSkillRaceClassInfo(skill, getRace(), getClass());
if (!rcEntry)
{
LOG_ERROR("entities.player", "Character %s has skill %u that does not exist.", GetGUID().ToString().c_str(), skill);
continue;
}
// set fixed skill ranges
switch (GetSkillRangeType(pSkill, false))
switch (GetSkillRangeType(rcEntry))
{
case SKILL_RANGE_LANGUAGE: // 300..300
value = max = 300;
@@ -25846,9 +25945,12 @@ void Player::_LoadSkills(PreparedQueryResult result)
case SKILL_RANGE_MONO: // 1..1, grey monolite bar
value = max = 1;
break;
case SKILL_RANGE_LEVEL:
max = GetMaxSkillValueForLevel();
default:
break;
}
if (value == 0)
{
LOG_ERROR("entities.player", "Character %s has skill %u with value 0. Will be deleted.", GetGUID().ToString().c_str(), skill);
@@ -25863,11 +25965,20 @@ void Player::_LoadSkills(PreparedQueryResult result)
continue;
}
// enable unlearn button for primary professions only
if (pSkill->categoryId == SKILL_CATEGORY_PROFESSION)
SetUInt32Value(PLAYER_SKILL_INDEX(count), MAKE_PAIR32(skill, 1));
else
SetUInt32Value(PLAYER_SKILL_INDEX(count), MAKE_PAIR32(skill, 0));
uint16 skillStep = 0;
if (SkillTiersEntry const* skillTier = sSkillTiersStore.LookupEntry(rcEntry->SkillTierID))
{
for (uint32 i = 0; i < MAX_SKILL_STEP; ++i)
{
if (skillTier->Value[skillStep] == max)
{
skillStep = i + 1;
break;
}
}
}
SetUInt32Value(PLAYER_SKILL_INDEX(count), MAKE_PAIR32(skill, skillStep));
SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(count), MAKE_SKILL_VALUE(value, max));
SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(count), 0);
@@ -25892,34 +26003,6 @@ void Player::_LoadSkills(PreparedQueryResult result)
SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(count), 0);
SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(count), 0);
}
// special settings
if (getClass() == CLASS_DEATH_KNIGHT)
{
uint8 base_level = std::min(getLevel(), uint8(sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL)));
if (base_level < 1)
base_level = 1;
uint16 base_skill = (base_level - 1) * 5; // 270 at starting level 55
if (base_skill < 1)
base_skill = 1; // skill mast be known and then > 0 in any case
if (GetPureSkillValue(SKILL_FIRST_AID) < base_skill)
SetSkill(SKILL_FIRST_AID, 4 /*artisan*/, base_skill, 300);
if (GetPureSkillValue(SKILL_AXES) < base_skill)
SetSkill(SKILL_AXES, 0, base_skill, base_skill);
if (GetPureSkillValue(SKILL_DEFENSE) < base_skill)
SetSkill(SKILL_DEFENSE, 0, base_skill, base_skill);
if (GetPureSkillValue(SKILL_POLEARMS) < base_skill)
SetSkill(SKILL_POLEARMS, 0, base_skill, base_skill);
if (GetPureSkillValue(SKILL_SWORDS) < base_skill)
SetSkill(SKILL_SWORDS, 0, base_skill, base_skill);
if (GetPureSkillValue(SKILL_2H_AXES) < base_skill)
SetSkill(SKILL_2H_AXES, 0, base_skill, base_skill);
if (GetPureSkillValue(SKILL_2H_SWORDS) < base_skill)
SetSkill(SKILL_2H_SWORDS, 0, base_skill, base_skill);
if (GetPureSkillValue(SKILL_UNARMED) < base_skill)
SetSkill(SKILL_UNARMED, 0, base_skill, base_skill);
}
}
uint32 Player::GetPhaseMaskForSpawn() const