Core/Player: All spells and skills are now automatically learned without any need for external data
* Fixes learning mail/plate at level 40 for shamans/warriors/paladins * Fixes learning riding for worgen/paladins/warlocks at level 20
This commit is contained in:
@@ -0,0 +1 @@
|
||||
DROP TABLE IF EXISTS `playercreateinfo_skills`;
|
||||
@@ -231,6 +231,7 @@ extern DBCStorage <RandomPropertiesPointsEntry> sRandomPropertiesPointsStore;
|
||||
extern DBCStorage <ScalingStatDistributionEntry> sScalingStatDistributionStore;
|
||||
extern DBCStorage <SkillLineEntry> sSkillLineStore;
|
||||
extern DBCStorage <SkillLineAbilityEntry> sSkillLineAbilityStore;
|
||||
extern DBCStorage <SkillRaceClassInfoEntry> sSkillRaceClassInfoStore;
|
||||
extern DBCStorage <SkillTiersEntry> sSkillTiersStore;
|
||||
extern SpellEffectScallingByEffectId sSpellEffectScallingByEffectId;
|
||||
extern DBCStorage <SpellCastTimesEntry> sSpellCastTimesStore;
|
||||
|
||||
@@ -1400,11 +1400,11 @@ struct SkillRaceClassInfoEntry
|
||||
{
|
||||
//uint32 ID; // 0
|
||||
uint32 SkillID; // 1
|
||||
uint32 RaceMask; // 2
|
||||
uint32 ClassMask; // 3
|
||||
int32 RaceMask; // 2
|
||||
int32 ClassMask; // 3
|
||||
uint32 Flags; // 4
|
||||
//uint32 Availability; // 5
|
||||
//uint32 MinLevel; // 6
|
||||
uint32 Availability; // 5
|
||||
uint32 MinLevel; // 6
|
||||
uint32 SkillTierID; // 7
|
||||
};
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ char const RandomPropertiesPointsfmt[] = "niiiiiiiiiiiiiii";
|
||||
char const ScalingStatDistributionfmt[] = "niii";
|
||||
char const SkillLinefmt[] = "nisxixixx";
|
||||
char const SkillLineAbilityfmt[] = "niiiiiiiiiiii";
|
||||
char const SkillRaceClassInfofmt[] = "diiiixxi";
|
||||
char const SkillRaceClassInfofmt[] = "diiiiiii";
|
||||
char const SkillTiersfmt[] = "niiiiiiiiiiiiiiii";
|
||||
char const SpecializationSpellsEntryfmt[] = "niiix";
|
||||
char const SpellCastTimefmt[] = "nixx";
|
||||
|
||||
@@ -2909,7 +2909,7 @@ void Player::GiveLevel(uint8 level)
|
||||
packet.HealthDelta = int32(basehp) - int32(GetCreateHealth());
|
||||
|
||||
/// @todo find some better solution
|
||||
// for (int i = 0; i < MAX_STORED_POWERS; ++i)
|
||||
// for (int i = 0; i < MAX_STORED_POWERS; ++i)
|
||||
packet.PowerDelta[0] = int32(basemana) - int32(GetCreateMana());
|
||||
packet.PowerDelta[1] = 0;
|
||||
packet.PowerDelta[2] = 0;
|
||||
@@ -2936,6 +2936,7 @@ void Player::GiveLevel(uint8 level)
|
||||
SetLevel(level);
|
||||
|
||||
UpdateSkillsForLevel();
|
||||
LearnDefaultSkills();
|
||||
LearnSpecializationSpells();
|
||||
|
||||
// save base values (bonuses already included in stored stats
|
||||
@@ -3048,7 +3049,6 @@ void Player::InitStatsForLevel(bool reapplyMods)
|
||||
SetUInt32Value(UNIT_FIELD_AURASTATE, 0);
|
||||
|
||||
UpdateSkillsForLevel();
|
||||
LearnSpecializationSpells();
|
||||
|
||||
// set default cast time multiplier
|
||||
SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f);
|
||||
@@ -3645,9 +3645,9 @@ bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent
|
||||
if (!pSkill)
|
||||
continue;
|
||||
|
||||
///@todo: confirm if rogues start with lockpicking skill at level 1 but only receive the spell to use it at level 16
|
||||
if ((_spell_idx->second->AquireMethod == SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN && !HasSkill(pSkill->ID)) || (pSkill->ID == SKILL_LOCKPICKING && _spell_idx->second->TrivialSkillLineRankHigh == 0))
|
||||
LearnDefaultSkill(pSkill->ID, 0);
|
||||
if ((_spell_idx->second->AquireMethod == SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN && !HasSkill(pSkill->ID)))
|
||||
if (SkillRaceClassInfoEntry const* rcInfo = GetSkillRaceClassInfo(pSkill->ID, getRace(), getClass()))
|
||||
LearnDefaultSkill(rcInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22675,20 +22675,20 @@ void Player::LearnDefaultSkills()
|
||||
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))
|
||||
SkillRaceClassInfoEntry const* rcInfo = *itr;
|
||||
if (HasSkill(rcInfo->SkillID))
|
||||
continue;
|
||||
|
||||
LearnDefaultSkill(skillId, itr->Rank);
|
||||
if (rcInfo->MinLevel > getLevel())
|
||||
continue;
|
||||
|
||||
LearnDefaultSkill(rcInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void Player::LearnDefaultSkill(uint32 skillId, uint16 rank)
|
||||
void Player::LearnDefaultSkill(SkillRaceClassInfoEntry const* rcInfo)
|
||||
{
|
||||
SkillRaceClassInfoEntry const* rcInfo = GetSkillRaceClassInfo(skillId, getRace(), getClass());
|
||||
if (!rcInfo)
|
||||
return;
|
||||
|
||||
uint16 skillId = rcInfo->SkillID;
|
||||
switch (GetSkillRangeType(rcInfo))
|
||||
{
|
||||
case SKILL_RANGE_LANGUAGE:
|
||||
@@ -22715,8 +22715,9 @@ void Player::LearnDefaultSkill(uint32 skillId, uint16 rank)
|
||||
break;
|
||||
case SKILL_RANGE_RANK:
|
||||
{
|
||||
if (!rank)
|
||||
break;
|
||||
uint16 rank = 1;
|
||||
if (getClass() == CLASS_DEATH_KNIGHT && skillId == SKILL_FIRST_AID)
|
||||
rank = 4;
|
||||
|
||||
SkillTiersEntry const* tier = sSkillTiersStore.LookupEntry(rcInfo->SkillTierID);
|
||||
uint16 maxValue = tier->Value[std::max<int32>(rank - 1, 0)];
|
||||
@@ -22724,7 +22725,7 @@ void Player::LearnDefaultSkill(uint32 skillId, uint16 rank)
|
||||
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);
|
||||
skillValue = std::min(std::max(uint16(1), uint16((getLevel() - 1) * 5)), maxValue);
|
||||
|
||||
SetSkill(skillId, rank, skillValue, maxValue);
|
||||
break;
|
||||
|
||||
@@ -386,13 +386,7 @@ struct PlayerCreateInfoAction
|
||||
|
||||
typedef std::list<PlayerCreateInfoAction> PlayerCreateInfoActions;
|
||||
|
||||
struct PlayerCreateInfoSkill
|
||||
{
|
||||
uint16 SkillId;
|
||||
uint16 Rank;
|
||||
};
|
||||
|
||||
typedef std::list<PlayerCreateInfoSkill> PlayerCreateInfoSkills;
|
||||
typedef std::list<SkillRaceClassInfoEntry const*> PlayerCreateInfoSkills;
|
||||
|
||||
struct PlayerInfo
|
||||
{
|
||||
@@ -1874,7 +1868,7 @@ class Player : public Unit, public GridObject<Player>
|
||||
void ResetSpells(bool myClassOnly = false);
|
||||
void LearnCustomSpells();
|
||||
void LearnDefaultSkills();
|
||||
void LearnDefaultSkill(uint32 skillId, uint16 rank);
|
||||
void LearnDefaultSkill(SkillRaceClassInfoEntry const* rcInfo);
|
||||
void LearnQuestRewardedSpells();
|
||||
void LearnQuestRewardedSpells(Quest const* quest);
|
||||
void LearnSpellHighestRank(uint32 spellid);
|
||||
|
||||
@@ -2899,73 +2899,16 @@ void ObjectMgr::LoadPlayerInfo()
|
||||
{
|
||||
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 (SkillRaceClassInfoEntry const* rcInfo : sSkillRaceClassInfoStore)
|
||||
if (rcInfo->Availability == 1)
|
||||
for (uint32 raceIndex = RACE_HUMAN; raceIndex < MAX_RACES; ++raceIndex)
|
||||
{
|
||||
if (raceMask == 0 || ((1 << (raceIndex - 1)) & raceMask))
|
||||
{
|
||||
if (rcInfo->RaceMask == -1 || ((1 << (raceIndex - 1)) & rcInfo->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 (rcInfo->ClassMask == -1 || ((1 << (classIndex - 1)) & rcInfo->ClassMask))
|
||||
if (PlayerInfo* info = _playerInfo[raceIndex][classIndex])
|
||||
{
|
||||
info->skills.push_back(skill);
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (result->NextRow());
|
||||
info->skills.push_back(rcInfo);
|
||||
|
||||
TC_LOG_INFO("server.loading", ">> Loaded %u player create skills in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
|
||||
}
|
||||
TC_LOG_INFO("server.loading", ">> Loaded player create skills in %u ms", GetMSTimeDiffToNow(oldMSTime));
|
||||
}
|
||||
|
||||
// Load playercreate custom spells
|
||||
|
||||
Reference in New Issue
Block a user