Core/Skills: Use SkillLineAbility.dbc to determine player initial spells - skill assignment done in a new table playercreateinfo_skills
* Removed DK start skill hacks * Resolved missing skills in character skill tab Closes #12311
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
-- Set fist weapon skill equal to current unarmed skill value
|
||||
UPDATE character_skills cs_unarmed INNER JOIN character_skills cs_fist ON cs_unarmed.guid = cs_fist.guid
|
||||
SET cs_fist.value = cs_unarmed.value, cs_fist.max = cs_unarmed.max
|
||||
WHERE cs_unarmed.skill = 162 AND cs_fist.skill = 473;
|
||||
@@ -0,0 +1,90 @@
|
||||
DROP TABLE IF EXISTS `playercreateinfo_spells`;
|
||||
|
||||
DROP TABLE IF EXISTS `playercreateinfo_skills`;
|
||||
CREATE TABLE `playercreateinfo_skills` (
|
||||
`raceMask` int(10) unsigned NOT NULL,
|
||||
`classMask` int(10) unsigned NOT NULL,
|
||||
`skill` smallint(5) unsigned NOT NULL,
|
||||
`rank` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`comment` varchar(255) DEFAULT NULL,
|
||||
PRIMARY KEY (`raceMask`,`classMask`,`skill`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
INSERT INTO `playercreateinfo_skills` VALUES
|
||||
(0,0,95,0,'Defense'),
|
||||
(0,0,162,0,'Unarmed'),
|
||||
(0,0,183,0,'GENERIC (DND)'),
|
||||
(0,0,415,0,'Cloth'),
|
||||
(0,0,777,0,'Mounts'),
|
||||
(0,0,778,0,'Companion Pets'),
|
||||
(0,1,26,0,'Warrior - Arms'),
|
||||
(0,1,256,0,'Warrior - Fury'),
|
||||
(0,1,257,0,'Warrior - Protection'),
|
||||
(0,2,184,0,'Paladin - Retribution'),
|
||||
(0,2,267,0,'Paladin - Protection'),
|
||||
(0,2,594,0,'Paladin - Holy'),
|
||||
(0,4,50,0,'Hunter - Beast Mastery'),
|
||||
(0,4,51,0,'Hunter - Survival'),
|
||||
(0,4,163,0,'Hunter - Marksmanship'),
|
||||
(0,8,38,0,'Rogue - Combat'),
|
||||
(0,8,39,0,'Rogue - Subtlety'),
|
||||
(0,8,176,0,'Thrown'),
|
||||
(0,8,253,0,'Rogue - Assassination'),
|
||||
(0,16,56,0,'Priest - Holy'),
|
||||
(0,16,78,0,'Priest - Shadow'),
|
||||
(0,16,613,0,'Priest - Discipline'),
|
||||
(0,32,129,4,'Death Knight - First Aid'),
|
||||
(0,32,229,0,'Polearms'),
|
||||
(0,32,293,0,'Plate'),
|
||||
(0,32,762,0,'Death Knight - Riding'),
|
||||
(0,32,770,0,'Death Knight - Blood'),
|
||||
(0,32,771,0,'Death Knight - Frost'),
|
||||
(0,32,772,0,'Death Knight - Unholy'),
|
||||
(0,35,55,0,'Two-Handed Swords'),
|
||||
(0,35,413,0,'Mail'),
|
||||
(0,37,44,0,'Axes'),
|
||||
(0,37,172,0,'Two-Handed Axes'),
|
||||
(0,39,43,0,'Swords'),
|
||||
(0,40,118,0,'Dual Wield'),
|
||||
(0,64,373,0,'Shaman - Enhancement'),
|
||||
(0,64,374,0,'Shaman - Restoration'),
|
||||
(0,64,375,0,'Shaman - Elemental'),
|
||||
(0,67,433,0,'Shield'),
|
||||
(0,128,6,0,'Mage - Frost'),
|
||||
(0,128,8,0,'Mage - Fire'),
|
||||
(0,128,237,0,'Mage - Arcane'),
|
||||
(0,256,354,0,'Warlock - Demonology'),
|
||||
(0,256,355,0,'Warlock - Affliction'),
|
||||
(0,256,593,0,'Warlock - Destruction'),
|
||||
(0,400,228,0,'Wands'),
|
||||
(0,1024,134,0,'Druid - Feral'),
|
||||
(0,1024,573,0,'Druid - Restoration'),
|
||||
(0,1024,574,0,'Druid - Balance'),
|
||||
(0,1107,54,0,'Maces'),
|
||||
(0,1135,414,0,'Leather'),
|
||||
(0,1488,136,0,'Staves'),
|
||||
(1,0,754,0,'Human - Racial'),
|
||||
(2,0,125,0,'Orc - Racial'),
|
||||
(4,0,101,0,'Dwarf - Racial'),
|
||||
(4,0,111,0,'Language: Dwarven'),
|
||||
(8,0,126,0,'Night Elf - Racial'),
|
||||
(8,0,137,0,'Language: Thalassian'),
|
||||
(16,0,220,0,'Undead - Racial'),
|
||||
(16,0,673,0,'Language: Forsaken'),
|
||||
(32,0,113,0,'Language: Darnassian'),
|
||||
(32,0,124,0,'Tauren - Racial'),
|
||||
(36,4,46,0,'Guns'),
|
||||
(64,0,313,0,'Language: Gnomish'),
|
||||
(64,0,753,0,'Gnome - Racial'),
|
||||
(128,0,315,0,'Language: Troll'),
|
||||
(128,0,733,0,'Troll - Racial'),
|
||||
(512,0,115,0,'Language: Taurahe'),
|
||||
(512,0,756,0,'Blood Elf - Racial'),
|
||||
(650,4,45,0,'Bows'),
|
||||
(690,0,109,0,'Language: Orcish'),
|
||||
(735,1293,173,0,'Daggers'),
|
||||
(1024,0,759,0,'Language: Draenei'),
|
||||
(1024,0,760,0,'Draenei - Racial'),
|
||||
(1024,4,226,0,'Crossbows'),
|
||||
(1061,3,160,0,'Two-Handed Maces'),
|
||||
(1101,0,98,0,'Language: Common');
|
||||
@@ -326,8 +326,8 @@ enum MapFlags
|
||||
|
||||
enum AbilytyLearnType
|
||||
{
|
||||
ABILITY_LEARNED_ON_GET_PROFESSION_SKILL = 1,
|
||||
ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL = 2
|
||||
SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE = 1, // Spell state will update depending on skill value
|
||||
SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN = 2 // Spell will be learned/removed together with entire skill
|
||||
};
|
||||
|
||||
enum ItemEnchantmentType
|
||||
@@ -356,7 +356,7 @@ enum SkillRaceClassInfoFlags
|
||||
SKILL_FLAG_UNLEARNABLE = 0x20, // Skill can be unlearned
|
||||
SKILL_FLAG_INCLUDE_IN_SORT = 0x80, // Spells belonging to a skill with this flag will additionally compare skill ids when sorting spellbook in client
|
||||
SKILL_FLAG_NOT_TRAINABLE = 0x100,
|
||||
SKILL_FLAG_MONO_VALUE = 0x400 // Skill always has value 1
|
||||
SKILL_FLAG_MONO_VALUE = 0x400 // Skill always has value 1 - clientside display flag, real value can be different
|
||||
};
|
||||
|
||||
enum SpellCategoryFlags
|
||||
|
||||
@@ -450,7 +450,7 @@ void LoadDBCStores(const std::string& dataPath)
|
||||
if (spellInfo->spellLevel)
|
||||
continue;
|
||||
|
||||
if (skillLine->learnOnGetSkill != ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL)
|
||||
if (skillLine->AutolearnType != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN)
|
||||
continue;
|
||||
|
||||
sPetFamilySpellsStore[i].insert(spellInfo->Id);
|
||||
|
||||
@@ -1535,23 +1535,6 @@ struct ScalingStatValuesEntry
|
||||
// uint32 displayOrder; // 19 m_sortIndex
|
||||
//};
|
||||
|
||||
//struct SkillRaceClassInfoEntry{
|
||||
// uint32 id; // 0 m_ID
|
||||
// uint32 skillId; // 1 m_skillID
|
||||
// uint32 raceMask; // 2 m_raceMask
|
||||
// uint32 classMask; // 3 m_classMask
|
||||
// uint32 flags; // 4 m_flags
|
||||
// uint32 reqLevel; // 5 m_minLevel
|
||||
// uint32 skillTierId; // 6 m_skillTierID
|
||||
// uint32 skillCostID; // 7 m_skillCostIndex
|
||||
//};
|
||||
|
||||
//struct SkillTiersEntry{
|
||||
// uint32 id; // 0 m_ID
|
||||
// uint32 skillValue[16]; // 1-17 m_cost
|
||||
// uint32 maxSkillValue[16]; // 18-32 m_valueMax
|
||||
//};
|
||||
|
||||
struct SkillLineEntry
|
||||
{
|
||||
uint32 id; // 0 m_ID
|
||||
@@ -1578,7 +1561,7 @@ struct SkillLineAbilityEntry
|
||||
//uint32 classmaskNot; // 6 m_excludeClass
|
||||
uint32 req_skill_value; // 7 m_minSkillLineRank
|
||||
uint32 forward_spellid; // 8 m_supercededBySpell
|
||||
uint32 learnOnGetSkill; // 9 m_acquireMethod
|
||||
uint32 AutolearnType; // 9 m_acquireMethod
|
||||
uint32 max_value; // 10 m_trivialSkillLineRankHigh
|
||||
uint32 min_value; // 11 m_trivialSkillLineRankLow
|
||||
//uint32 characterPoints[2]; // 12-13 m_characterPoints[2]
|
||||
|
||||
@@ -1134,7 +1134,8 @@ bool Player::Create(uint32 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)
|
||||
@@ -3827,34 +3828,14 @@ bool Player::addSpell(uint32 spellId, bool active, bool learning, bool dependent
|
||||
if (!pSkill)
|
||||
continue;
|
||||
|
||||
if (!Has310Flyer(false) && pSkill->id == SKILL_MOUNTS)
|
||||
if (!HasSkill(pSkill->id))
|
||||
LearnDefaultSkill(pSkill->id, 0);
|
||||
|
||||
if (pSkill->id == SKILL_MOUNTS && !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);
|
||||
|
||||
if (HasSkill(pSkill->id))
|
||||
continue;
|
||||
|
||||
if (_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL ||
|
||||
// lockpicking/runeforging special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
|
||||
((pSkill->id == SKILL_LOCKPICKING || pSkill->id == SKILL_RUNEFORGING) && _spell_idx->second->max_value == 0))
|
||||
{
|
||||
switch (GetSkillRangeType(pSkill, _spell_idx->second->racemask != 0))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4079,33 +4060,27 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank)
|
||||
// not ranked skills
|
||||
SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spell_id);
|
||||
|
||||
for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
|
||||
// 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;
|
||||
|
||||
if ((_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL &&
|
||||
pSkill->categoryId != SKILL_CATEGORY_CLASS) ||// not unlearn class skills (spellbook/talent pages)
|
||||
// lockpicking/runeforging special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
|
||||
((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->skillId);
|
||||
if (!pSkill)
|
||||
continue;
|
||||
|
||||
SetSkill(pSkill->id, GetSkillStep(pSkill->id), 0, 0);
|
||||
}
|
||||
|
||||
// most likely will never be used, haven't heard of cases where players unlearn a mount
|
||||
if (Has310Flyer(false) && _spell_idx->second->skillId == SKILL_MOUNTS)
|
||||
{
|
||||
if (spellInfo)
|
||||
if (_spell_idx->second->skillId == 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6080,9 +6055,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;
|
||||
@@ -6097,13 +6069,14 @@ bool Player::UpdateSkill(uint32 skill_id, uint32 step)
|
||||
|
||||
if (value < max)
|
||||
{
|
||||
uint32 new_value = value+step;
|
||||
uint32 new_value = value + step;
|
||||
if (new_value > max)
|
||||
new_value = max;
|
||||
|
||||
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;
|
||||
@@ -6277,9 +6250,25 @@ void Player::UpdateWeaponSkill(WeaponAttackType attType)
|
||||
|
||||
Item* tmpitem = GetWeaponForAttack(attType, true);
|
||||
if (!tmpitem && attType == BASE_ATTACK)
|
||||
{
|
||||
// Keep unarmed & fist weapon skills in sync
|
||||
UpdateSkill(SKILL_UNARMED, weapon_skill_gain);
|
||||
UpdateSkill(SKILL_FIST_WEAPONS, weapon_skill_gain);
|
||||
}
|
||||
else if (tmpitem && tmpitem->GetTemplate()->SubClass != ITEM_SUBCLASS_WEAPON_FISHING_POLE)
|
||||
UpdateSkill(tmpitem->GetSkill(), weapon_skill_gain);
|
||||
{
|
||||
switch (tmpitem->GetTemplate()->SubClass)
|
||||
{
|
||||
case ITEM_SUBCLASS_WEAPON_FISHING_POLE:
|
||||
break;
|
||||
case ITEM_SUBCLASS_WEAPON_FIST:
|
||||
UpdateSkill(SKILL_UNARMED, weapon_skill_gain);
|
||||
// no break intended
|
||||
default:
|
||||
UpdateSkill(tmpitem->GetSkill(), weapon_skill_gain);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateAllCritPercentages();
|
||||
}
|
||||
@@ -6352,11 +6341,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);
|
||||
@@ -17704,7 +17693,8 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder)
|
||||
|
||||
// after spell and quest load
|
||||
InitTalentForLevel();
|
||||
learnDefaultSpells();
|
||||
LearnDefaultSkills();
|
||||
LearnCustomSpells();
|
||||
|
||||
// must be before inventory (some items required reputation check)
|
||||
m_reputationMgr->LoadFromDB(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_REPUTATION));
|
||||
@@ -23078,15 +23068,19 @@ void Player::resetSpells(bool myClassOnly)
|
||||
for (PlayerSpellMap::const_iterator iter = smap.begin(); iter != smap.end(); ++iter)
|
||||
removeSpell(iter->first, false, false); // only iter->first can be accessed, object by iter->second can be deleted already
|
||||
|
||||
learnDefaultSpells();
|
||||
LearnDefaultSkills();
|
||||
LearnCustomSpells();
|
||||
learnQuestRewardedSpells();
|
||||
}
|
||||
|
||||
void Player::learnDefaultSpells()
|
||||
void Player::LearnCustomSpells()
|
||||
{
|
||||
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->spell.begin(); itr != info->spell.end(); ++itr)
|
||||
for (PlayerCreateInfoSpells::const_iterator itr = info->customSpells.begin(); itr != info->customSpells.end(); ++itr)
|
||||
{
|
||||
uint32 tspell = *itr;
|
||||
TC_LOG_DEBUG("entities.player.loading", "PLAYER (Class: %u Race: %u): Adding initial spell, id = %u", uint32(getClass()), uint32(getRace()), tspell);
|
||||
@@ -23097,6 +23091,63 @@ void Player::learnDefaultSpells()
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
TC_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 = 0;
|
||||
uint16 maxValue = GetMaxSkillValueForLevel();
|
||||
if (rcInfo->Flags & SKILL_FLAG_ALWAYS_MAX_VALUE)
|
||||
skillValue = maxValue;
|
||||
else
|
||||
skillValue = std::min(std::max<uint16>({ 1, uint16((getLevel() - 1) * 5) }), maxValue);
|
||||
|
||||
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->SkillTier);
|
||||
uint16 maxValue = std::max<uint16>(GetMaxSkillValue(skillId), tier->MaxSkill[std::max<int32>(rank - 1, 0)]);
|
||||
uint16 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)
|
||||
{
|
||||
int32 spell_id = quest->GetRewSpellCast();
|
||||
@@ -23194,29 +23245,35 @@ void Player::learnSkillRewardedSpells(uint32 skill_id, uint32 skill_value)
|
||||
{
|
||||
uint32 raceMask = getRaceMask();
|
||||
uint32 classMask = getClassMask();
|
||||
for (uint32 j=0; j<sSkillLineAbilityStore.GetNumRows(); ++j)
|
||||
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)
|
||||
if (!pAbility || pAbility->skillId != skill_id)
|
||||
continue;
|
||||
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(pAbility->spellId);
|
||||
if (!spellInfo)
|
||||
continue;
|
||||
|
||||
if (pAbility->AutolearnType != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE && pAbility->AutolearnType != 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;
|
||||
|
||||
if (sSpellMgr->GetSpellInfo(pAbility->spellId))
|
||||
{
|
||||
// need unlearn spell
|
||||
if (skill_value < pAbility->req_skill_value)
|
||||
removeSpell(pAbility->spellId);
|
||||
// need learn
|
||||
else if (!IsInWorld())
|
||||
addSpell(pAbility->spellId, true, true, true, false);
|
||||
else
|
||||
learnSpell(pAbility->spellId, true);
|
||||
}
|
||||
// need unlearn spell
|
||||
if (skill_value < pAbility->req_skill_value && pAbility->AutolearnType == SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE)
|
||||
removeSpell(pAbility->spellId);
|
||||
// need learn
|
||||
else if (!IsInWorld())
|
||||
addSpell(pAbility->spellId, true, true, true, false);
|
||||
else
|
||||
learnSpell(pAbility->spellId, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23944,7 +24001,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);
|
||||
|
||||
@@ -23952,8 +24009,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);
|
||||
}
|
||||
|
||||
@@ -24907,15 +24964,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)
|
||||
{
|
||||
TC_LOG_ERROR("entities.player", "Character %u has skill %u that does not exist.", GetGUIDLow(), skill);
|
||||
continue;
|
||||
}
|
||||
|
||||
// set fixed skill ranges
|
||||
switch (GetSkillRangeType(pSkill, false))
|
||||
switch (GetSkillRangeType(rcEntry))
|
||||
{
|
||||
case SKILL_RANGE_LANGUAGE: // 300..300
|
||||
value = max = 300;
|
||||
@@ -24923,9 +24980,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)
|
||||
{
|
||||
TC_LOG_ERROR("entities.player", "Character %u has skill %u with value 0. Will be deleted.", GetGUIDLow(), skill);
|
||||
@@ -24940,11 +25000,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->SkillTier))
|
||||
{
|
||||
for (uint32 i = 0; i < MAX_SKILL_STEP; ++i)
|
||||
{
|
||||
if (skillTier->MaxSkill[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);
|
||||
@@ -24970,34 +25039,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
|
||||
|
||||
@@ -249,6 +249,14 @@ struct PlayerCreateInfoAction
|
||||
|
||||
typedef std::list<PlayerCreateInfoAction> PlayerCreateInfoActions;
|
||||
|
||||
struct PlayerCreateInfoSkill
|
||||
{
|
||||
uint16 SkillId;
|
||||
uint16 Rank;
|
||||
};
|
||||
|
||||
typedef std::list<PlayerCreateInfoSkill> PlayerCreateInfoSkills;
|
||||
|
||||
struct PlayerInfo
|
||||
{
|
||||
// existence checked by displayId != 0
|
||||
@@ -263,8 +271,9 @@ struct PlayerInfo
|
||||
uint16 displayId_m;
|
||||
uint16 displayId_f;
|
||||
PlayerCreateInfoItems item;
|
||||
PlayerCreateInfoSpells spell;
|
||||
PlayerCreateInfoSpells customSpells;
|
||||
PlayerCreateInfoActions action;
|
||||
PlayerCreateInfoSkills skills;
|
||||
|
||||
PlayerLevelInfo* levelInfo; //[level-1] 0..MaxPlayerLevel-1
|
||||
};
|
||||
@@ -1573,7 +1582,9 @@ class Player : public Unit, public GridObject<Player>
|
||||
void learnSpell(uint32 spell_id, bool dependent);
|
||||
void removeSpell(uint32 spell_id, bool disabled = false, bool learn_low_rank = true);
|
||||
void resetSpells(bool myClassOnly = false);
|
||||
void learnDefaultSpells();
|
||||
void LearnCustomSpells();
|
||||
void LearnDefaultSkills();
|
||||
void LearnDefaultSkill(uint32 skillId, uint16 rank);
|
||||
void learnQuestRewardedSpells();
|
||||
void learnQuestRewardedSpells(Quest const* quest);
|
||||
void learnSpellHighRank(uint32 spellid);
|
||||
|
||||
@@ -2834,12 +2834,10 @@ uint32 Unit::GetWeaponSkillValue (WeaponAttackType attType, Unit const* target)
|
||||
if (IsInFeralForm())
|
||||
return GetMaxSkillValueForLevel(); // always maximized SKILL_FERAL_COMBAT in fact
|
||||
|
||||
// weapon skill or (unarmed for base attack and fist weapons)
|
||||
uint32 skill;
|
||||
if (item && item->GetSkill() != SKILL_FIST_WEAPONS)
|
||||
// weapon skill or (unarmed for base attack)
|
||||
uint32 skill = SKILL_UNARMED;
|
||||
if (item)
|
||||
skill = item->GetSkill();
|
||||
else
|
||||
skill = SKILL_UNARMED;
|
||||
|
||||
// in PvP use full skill instead current skill value
|
||||
value = (target && target->IsControlledByPlayer())
|
||||
@@ -15288,7 +15286,7 @@ void Unit::Kill(Unit* victim, bool durabilityLoss)
|
||||
group->SendLooter(creature, NULL);
|
||||
|
||||
// Update round robin looter only if the creature had loot
|
||||
if (!creature->loot.empty())
|
||||
if (!loot->empty())
|
||||
group->UpdateLooterGuid(creature);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3322,17 +3322,91 @@ void ObjectMgr::LoadPlayerInfo()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Load playercreate skills
|
||||
TC_LOG_INFO("server.loading", "Loading Player Create Skill Data...");
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
QueryResult result = WorldDatabase.PQuery("SELECT raceMask, classMask, skill, rank FROM playercreateinfo_skills");
|
||||
|
||||
if (!result)
|
||||
{
|
||||
TC_LOG_ERROR("server.loading", ">> Loaded 0 player create skills. DB table `playercreateinfo_skills` is empty.");
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32 count = 0;
|
||||
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
uint32 raceMask = fields[0].GetUInt32();
|
||||
uint32 classMask = fields[1].GetUInt32();
|
||||
PlayerCreateInfoSkill skill;
|
||||
skill.SkillId = fields[2].GetUInt16();
|
||||
skill.Rank = fields[3].GetUInt16();
|
||||
|
||||
if (skill.Rank >= MAX_SKILL_STEP)
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "Skill rank value %hu set for skill %hu raceMask %u classMask %u is too high, max allowed value is %d", skill.Rank, skill.SkillId, raceMask, classMask, MAX_SKILL_STEP);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE))
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "Wrong race mask %u in `playercreateinfo_skills` table, ignoring.", raceMask);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE))
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "Wrong class mask %u in `playercreateinfo_skills` table, ignoring.", classMask);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!sSkillLineStore.LookupEntry(skill.SkillId))
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "Wrong skill id %u in `playercreateinfo_skills` table, ignoring.", skill.SkillId);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint32 raceIndex = RACE_HUMAN; raceIndex < MAX_RACES; ++raceIndex)
|
||||
{
|
||||
if (raceMask == 0 || ((1 << (raceIndex - 1)) & raceMask))
|
||||
{
|
||||
for (uint32 classIndex = CLASS_WARRIOR; classIndex < MAX_CLASSES; ++classIndex)
|
||||
{
|
||||
if (classMask == 0 || ((1 << (classIndex - 1)) & classMask))
|
||||
{
|
||||
if (!GetSkillRaceClassInfo(skill.SkillId, raceIndex, classIndex))
|
||||
continue;
|
||||
|
||||
if (PlayerInfo* info = _playerInfo[raceIndex][classIndex])
|
||||
{
|
||||
info->skills.push_back(skill);
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (result->NextRow());
|
||||
|
||||
TC_LOG_INFO("server.loading", ">> Loaded %u player create skills in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
|
||||
}
|
||||
}
|
||||
|
||||
// Load playercreate spells
|
||||
TC_LOG_INFO("server.loading", "Loading Player Create Spell Data...");
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
std::string tableName = sWorld->getBoolConfig(CONFIG_START_ALL_SPELLS) ? "playercreateinfo_spell_custom" : "playercreateinfo_spell";
|
||||
QueryResult result = WorldDatabase.PQuery("SELECT racemask, classmask, Spell FROM %s", tableName.c_str());
|
||||
QueryResult result = WorldDatabase.PQuery("SELECT racemask, classmask, Spell FROM playercreateinfo_spell_custom");
|
||||
|
||||
if (!result)
|
||||
{
|
||||
TC_LOG_ERROR("server.loading", ">> Loaded 0 player create spells. DB table `%s` is empty.", tableName.c_str());
|
||||
TC_LOG_ERROR("server.loading", ">> Loaded 0 player create spells. DB table `playercreateinfo_spell_custom` is empty.");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3347,13 +3421,13 @@ void ObjectMgr::LoadPlayerInfo()
|
||||
|
||||
if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE))
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "Wrong race mask %u in `%s` table, ignoring.", raceMask, tableName.c_str());
|
||||
TC_LOG_ERROR("sql.sql", "Wrong race mask %u in `playercreateinfo_spell_custom` table, ignoring.", raceMask);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE))
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "Wrong class mask %u in `%s` table, ignoring.", classMask, tableName.c_str());
|
||||
TC_LOG_ERROR("sql.sql", "Wrong class mask %u in `playercreateinfo_spell_custom` table, ignoring.", classMask);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -3367,7 +3441,7 @@ void ObjectMgr::LoadPlayerInfo()
|
||||
{
|
||||
if (PlayerInfo* info = _playerInfo[raceIndex][classIndex])
|
||||
{
|
||||
info->spell.push_back(spellId);
|
||||
info->customSpells.push_back(spellId);
|
||||
++count;
|
||||
}
|
||||
// We need something better here, the check is not accounting for spells used by multiple races/classes but not all of them.
|
||||
@@ -3381,7 +3455,7 @@ void ObjectMgr::LoadPlayerInfo()
|
||||
}
|
||||
while (result->NextRow());
|
||||
|
||||
TC_LOG_INFO("server.loading", ">> Loaded %u player create spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
|
||||
TC_LOG_INFO("server.loading", ">> Loaded %u custom player create spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7808,36 +7882,27 @@ int32 ObjectMgr::GetBaseReputationOf(FactionEntry const* factionEntry, uint8 rac
|
||||
return 0;
|
||||
}
|
||||
|
||||
SkillRangeType GetSkillRangeType(SkillLineEntry const* pSkill, bool racial)
|
||||
SkillRangeType GetSkillRangeType(SkillRaceClassInfoEntry const* rcEntry)
|
||||
{
|
||||
switch (pSkill->categoryId)
|
||||
SkillLineEntry const* skill = sSkillLineStore.LookupEntry(rcEntry->SkillId);
|
||||
if (!skill)
|
||||
return SKILL_RANGE_NONE;
|
||||
|
||||
if (sSkillTiersStore.LookupEntry(rcEntry->SkillTier))
|
||||
return SKILL_RANGE_RANK;
|
||||
|
||||
if (rcEntry->SkillId == SKILL_RUNEFORGING)
|
||||
return SKILL_RANGE_MONO;
|
||||
|
||||
switch (skill->categoryId)
|
||||
{
|
||||
case SKILL_CATEGORY_LANGUAGES: return SKILL_RANGE_LANGUAGE;
|
||||
case SKILL_CATEGORY_WEAPON:
|
||||
if (pSkill->id != SKILL_FIST_WEAPONS)
|
||||
return SKILL_RANGE_LEVEL;
|
||||
else
|
||||
return SKILL_RANGE_MONO;
|
||||
case SKILL_CATEGORY_ARMOR:
|
||||
case SKILL_CATEGORY_CLASS:
|
||||
if (pSkill->id != SKILL_LOCKPICKING)
|
||||
return SKILL_RANGE_MONO;
|
||||
else
|
||||
return SKILL_RANGE_LEVEL;
|
||||
case SKILL_CATEGORY_SECONDARY:
|
||||
case SKILL_CATEGORY_PROFESSION:
|
||||
// not set skills for professions and racial abilities
|
||||
if (IsProfessionSkill(pSkill->id))
|
||||
return SKILL_RANGE_RANK;
|
||||
else if (racial)
|
||||
return SKILL_RANGE_NONE;
|
||||
else
|
||||
return SKILL_RANGE_MONO;
|
||||
default:
|
||||
case SKILL_CATEGORY_ATTRIBUTES: //not found in dbc
|
||||
case SKILL_CATEGORY_GENERIC: //only GENERIC(DND)
|
||||
return SKILL_RANGE_NONE;
|
||||
return SKILL_RANGE_MONO;
|
||||
case SKILL_CATEGORY_LANGUAGES:
|
||||
return SKILL_RANGE_LANGUAGE;
|
||||
}
|
||||
|
||||
return SKILL_RANGE_LEVEL;
|
||||
}
|
||||
|
||||
void ObjectMgr::LoadGameTele()
|
||||
|
||||
@@ -643,7 +643,7 @@ enum SkillRangeType
|
||||
SKILL_RANGE_NONE // 0..0 always
|
||||
};
|
||||
|
||||
SkillRangeType GetSkillRangeType(SkillLineEntry const* pSkill, bool racial);
|
||||
SkillRangeType GetSkillRangeType(SkillRaceClassInfoEntry const* rcEntry);
|
||||
|
||||
#define MAX_PLAYER_NAME 12 // max allowed by client name length
|
||||
#define MAX_INTERNAL_PLAYER_NAME 15 // max server internal player name length (> MAX_PLAYER_NAME for support declined names)
|
||||
|
||||
@@ -967,7 +967,7 @@ bool SpellInfo::IsAbilityLearnedWithProfession() const
|
||||
for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
|
||||
{
|
||||
SkillLineAbilityEntry const* pAbility = _spell_idx->second;
|
||||
if (!pAbility || pAbility->learnOnGetSkill != ABILITY_LEARNED_ON_GET_PROFESSION_SKILL)
|
||||
if (!pAbility || pAbility->AutolearnType != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE)
|
||||
continue;
|
||||
|
||||
if (pAbility->req_skill_value > 0)
|
||||
|
||||
@@ -2382,7 +2382,7 @@ void SpellMgr::LoadPetLevelupSpellMap()
|
||||
if (skillLine->skillId != creatureFamily->skillLine[j])
|
||||
continue;
|
||||
|
||||
if (skillLine->learnOnGetSkill != ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL)
|
||||
if (skillLine->AutolearnType != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN)
|
||||
continue;
|
||||
|
||||
SpellInfo const* spell = GetSpellInfo(skillLine->spellId);
|
||||
|
||||
@@ -332,7 +332,8 @@ public:
|
||||
if (!handler->extractPlayerTarget((char*)args, &target))
|
||||
return false;
|
||||
|
||||
target->learnDefaultSpells();
|
||||
target->LearnDefaultSkills();
|
||||
target->LearnCustomSpells();
|
||||
target->learnQuestRewardedSpells();
|
||||
|
||||
handler->PSendSysMessage(LANG_COMMAND_LEARN_ALL_DEFAULT_AND_QUEST, handler->GetNameLink(target).c_str());
|
||||
|
||||
Reference in New Issue
Block a user