Core/Spells: Reimplemented automatic spell learning

* Fixed learning/unlearning talents
This commit is contained in:
Shauren
2015-01-30 23:58:16 +01:00
parent beb1a06ea5
commit 3f28fd304d
30 changed files with 724 additions and 674 deletions
+19
View File
@@ -0,0 +1,19 @@
DELETE FROM `playercreateinfo_skills` WHERE `skill` IN (795,796,798,800,804,829,840,849,904,921,924);
INSERT INTO `playercreateinfo_skills` (`raceMask`,`classMask`,`skill`,`rank`,`comment`) VALUES
(0, 4, 795, 0, "Hunter skills."),
(0, 32, 796, 0, "Death Knight skills."),
(0, 1024, 798, 0, "Druid skills."),
(0, 2, 800, 0, "Paladin skills."),
(0, 16, 804, 0, "Priest skills."),
(0, 512, 829, 0, "Monk skills."),
(0, 1, 840, 0, "Warrior skills."),
(0, 256, 849, 0, "Warlock skills."),
(0, 128, 904, 0, "Mage skills."),
(0, 8, 921, 0, "Rogue skills."),
(0, 64, 924, 0, "Shaman skills.");
DELETE FROM `spell_ranks` WHERE `first_spell_id`=127650;
INSERT INTO `spell_ranks` (`first_spell_id`,`spell_id`,`rank`) VALUES
(127650,127650,1), -- Remove Talent: Vanishing Powder
(127650,127649,2), -- Remove Talent: Dust of Disappearance
(127650,113873,3); -- Remove Talent: Tome of the Clear Mind
@@ -1078,7 +1078,7 @@ void Battleground::AddPlayer(Player* player)
BattlegroundPlayer bp;
bp.OfflineRemoveTime = 0;
bp.Team = team;
bp.ActiveSpec = player->GetActiveTalentSpec();
bp.ActiveSpec = player->GetSpecId(player->GetActiveTalentGroup());
// Add to list/maps
m_Players[player->GetGUID()] = bp;
+24 -32
View File
@@ -76,7 +76,6 @@ DBCStorage <ChrRacesEntry> sChrRacesStore(ChrRacesEntryfmt);
DBCStorage <ChrPowerTypesEntry> sChrPowerTypesStore(ChrClassesXPowerTypesfmt);
DBCStorage <ChrSpecializationEntry> sChrSpecializationStore(ChrSpecializationEntryfmt);
ChrSpecializationByIndexArray sChrSpecializationByIndexStore;
SpecializationSpellsBySpecStore sSpecializationSpellsBySpecStore;
DBCStorage <CinematicSequencesEntry> sCinematicSequencesStore(CinematicSequencesEntryfmt);
DBCStorage <CreatureDisplayInfoEntry> sCreatureDisplayInfoStore(CreatureDisplayInfofmt);
DBCStorage <CreatureDisplayInfoExtraEntry> sCreatureDisplayInfoExtraStore(CreatureDisplayInfoExtrafmt);
@@ -199,6 +198,7 @@ DBCStorage <SkillTiersEntry> sSkillTiersStore(SkillTiersfmt);
DBCStorage <SoundEntriesEntry> sSoundEntriesStore(SoundEntriesfmt);
DBCStorage <SpecializationSpellsEntry> sSpecializationSpellsStore(SpecializationSpellsEntryfmt);
std::unordered_map<uint32, std::vector<SpecializationSpellsEntry const*>> sSpecializationSpellsBySpecStore;
DBCStorage <SpellItemEnchantmentEntry> sSpellItemEnchantmentStore(SpellItemEnchantmentfmt);
DBCStorage <SpellItemEnchantmentConditionEntry> sSpellItemEnchantmentConditionStore(SpellItemEnchantmentConditionfmt);
DBCStorage <SpellEntry> sSpellStore(SpellEntryfmt);
@@ -229,9 +229,7 @@ DBCStorage <SpellShapeshiftFormEntry> sSpellShapeshiftFormStore(SpellShapeshiftF
DBCStorage <StableSlotPricesEntry> sStableSlotPricesStore(StableSlotPricesfmt);
DBCStorage <SummonPropertiesEntry> sSummonPropertiesStore(SummonPropertiesfmt);
DBCStorage <TalentEntry> sTalentStore(TalentEntryfmt);
TalentBySpellIDMap sTalentBySpellIDMap;
SpecializationSpellsMap sSpecializationSpellsMap;
SpecializationOverrideSpellsMap sSpecializationOverrideSpellMap;
TalentsByPosition sTalentByPos;
DBCStorage <TotemCategoryEntry> sTotemCategoryStore(TotemCategoryEntryfmt);
DBCStorage <TransportAnimationEntry> sTransportAnimationStore(TransportAnimationfmt);
@@ -546,16 +544,15 @@ void LoadDBCStores(const std::string& dataPath)
LoadDBC(availableDbcLocales, bad_dbc_files, sSkillTiersStore, dbcPath, "SkillTiers.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sSoundEntriesStore, dbcPath, "SoundEntries.dbc");//15595
LoadDBC(availableDbcLocales, bad_dbc_files, sSpecializationSpellsStore, dbcPath, "SpecializationSpells.dbc");
for (uint32 i = 1; i < sSpecializationSpellsStore.GetNumRows(); ++i)
for (uint32 i = 0; i < sSpecializationSpellsStore.GetNumRows(); ++i)
{
SpecializationSpellsEntry const* specSpells = sSpecializationSpellsStore.LookupEntry(i);
if (!specSpells)
continue;
sSpecializationSpellsBySpecStore[specSpells->SpecID].push_back(specSpells);
if (specSpells->OverridesSpellID)
sSpecializationOverrideSpellMap[specSpells->SpecID][specSpells->OverridesSpellID] = specSpells->SpellID;
sSpecializationSpellsBySpecStore[specSpells->SpecID].push_back(specSpells);
}
LoadDBC(availableDbcLocales, bad_dbc_files, sSpellStore, dbcPath, "Spell.dbc"/*, &CustomSpellEntryfmt, &CustomSpellEntryIndex*/);
LoadDBC(availableDbcLocales, bad_dbc_files, sSpellCategoriesStore, dbcPath, "SpellCategories.dbc");//15595
LoadDBC(availableDbcLocales, bad_dbc_files, sSpellCategoryStore, dbcPath, "SpellCategory.dbc");
@@ -599,15 +596,17 @@ void LoadDBCStores(const std::string& dataPath)
sSpellEffectScallingByEffectId.insert(std::make_pair(spellEffectScaling->SpellEffectID, j));
}
LoadDBC(availableDbcLocales, bad_dbc_files, sTalentStore, dbcPath, "Talent.dbc");//15595
for (unsigned int i = 0; i < sTalentStore.GetNumRows(); ++i)
LoadDBC(availableDbcLocales, bad_dbc_files, sTalentStore, dbcPath, "Talent.dbc");//19342
for (uint32 i = 0; i < sTalentStore.GetNumRows(); ++i)
{
TalentEntry const* talentInfo = sTalentStore.LookupEntry(i);
if (!talentInfo)
continue;
sTalentBySpellIDMap[talentInfo->SpellID] = talentInfo;
if (TalentEntry const* talentInfo = sTalentStore.LookupEntry(i))
{
if (talentInfo->ClassID < MAX_CLASSES && talentInfo->TierID < MAX_TALENT_TIERS && talentInfo->ColumnIndex < MAX_TALENT_COLUMNS)
sTalentByPos[talentInfo->ClassID][talentInfo->TierID][talentInfo->ColumnIndex].push_back(talentInfo);
else
TC_LOG_ERROR("server.loading", "Value of class (found: %u, max allowed %u) or (found: %u, max allowed %u) tier or column (found: %u, max allowed %u) is invalid.",
talentInfo->ClassID, MAX_CLASSES, talentInfo->TierID, MAX_TALENT_TIERS, talentInfo->ColumnIndex, MAX_TALENT_COLUMNS);
}
}
//LoadDBC(availableDbcLocales, bad_dbc_files, sTeamContributionPointsStore, dbcPath, "TeamContributionPoints.dbc");
@@ -967,14 +966,6 @@ PvPDifficultyEntry const* GetBattlegroundBracketById(uint32 mapid, BattlegroundB
return NULL;
}
TalentEntry const* GetTalentBySpellID(uint32 spellID)
{
auto itr = sTalentBySpellIDMap.find(spellID);
if (itr != sTalentBySpellIDMap.end())
return itr->second;
return nullptr;
}
uint32 GetLiquidFlags(uint32 liquidType)
{
if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(liquidType))
@@ -1044,14 +1035,6 @@ SkillRaceClassInfoEntry const* GetSkillRaceClassInfo(uint32 skill, uint8 race, u
return NULL;
}
uint32 GetTalentSpellCost(uint32 spellId)
{
TalentBySpellIDMap::const_iterator itr = sTalentBySpellIDMap.find(spellId);
if (itr == sTalentBySpellIDMap.end())
return 0;
return 1;
}
uint32 GetQuestUniqueBitFlag(uint32 questId)
{
QuestV2Entry const* v2 = sQuestV2Store.LookupEntry(questId);
@@ -1060,3 +1043,12 @@ uint32 GetQuestUniqueBitFlag(uint32 questId)
return v2->UniqueBitFlag;
}
std::vector<SpecializationSpellsEntry const*> const* GetSpecializationSpells(uint32 specId)
{
auto itr = sSpecializationSpellsBySpecStore.find(specId);
if (itr != sSpecializationSpellsBySpecStore.end())
return &itr->second;
return nullptr;
}
+3 -12
View File
@@ -30,8 +30,6 @@ typedef std::list<uint32> SimpleFactionsList;
SimpleFactionsList const* GetFactionTeamList(uint32 faction);
char const* GetPetName(uint32 petfamily, uint32 dbclang);
uint32 GetTalentSpellCost(uint32 spellId);
TalentEntry const* GetTalentBySpellID(uint32 spellID);
int32 GetAreaFlagByAreaID(uint32 area_id); // -1 if not found
AreaTableEntry const* GetAreaEntryByAreaID(uint32 area_id);
@@ -91,14 +89,10 @@ typedef std::unordered_multimap<uint32, SkillRaceClassInfoEntry const*> SkillRac
typedef std::pair<SkillRaceClassInfoMap::iterator, SkillRaceClassInfoMap::iterator> SkillRaceClassInfoBounds;
SkillRaceClassInfoEntry const* GetSkillRaceClassInfo(uint32 skill, uint8 race, uint8 class_);
typedef std::vector<SpecializationSpellsEntry const*> SpecializationSpellsBySpecEntry;
typedef std::unordered_map<uint32, SpecializationSpellsBySpecEntry> SpecializationSpellsBySpecStore;
typedef ChrSpecializationEntry const* ChrSpecializationByIndexArray[MAX_CLASSES][MAX_SPECIALIZATIONS];
typedef std::unordered_map<uint32, TalentEntry const*> TalentBySpellIDMap;
typedef std::map<uint32, std::vector<uint32> > SpecializationSpellsMap;
extern SpecializationSpellsMap sSpecializationSpellsMap;
extern SpecializationOverrideSpellsMap sSpecializationOverrideSpellMap;
std::vector<SpecializationSpellsEntry const*> const* GetSpecializationSpells(uint32 specId);
typedef std::vector<TalentEntry const*> TalentsByPosition[MAX_CLASSES][MAX_TALENT_TIERS][MAX_TALENT_COLUMNS];
extern TalentsByPosition sTalentByPos;
template<class T>
class GameTable
@@ -243,8 +237,6 @@ extern DBCStorage <SkillLineAbilityEntry> sSkillLineAbilityStore;
extern DBCStorage <SkillTiersEntry> sSkillTiersStore;
extern DBCStorage <SoundEntriesEntry> sSoundEntriesStore;
extern SpellEffectScallingByEffectId sSpellEffectScallingByEffectId;
extern DBCStorage <SpecializationSpellsEntry> sSpecializationSpellsStore;
extern SpecializationSpellsBySpecStore sSpecializationSpellsBySpecStore;
extern DBCStorage <SpellCastTimesEntry> sSpellCastTimesStore;
extern DBCStorage <SpellCategoryEntry> sSpellCategoryStore;
extern DBCStorage <SpellDurationEntry> sSpellDurationStore;
@@ -271,7 +263,6 @@ extern DBCStorage <SpellTargetRestrictionsEntry> sSpellTargetRestrictionsStore;
//extern DBCStorage <StableSlotPricesEntry> sStableSlotPricesStore;
extern DBCStorage <SummonPropertiesEntry> sSummonPropertiesStore;
extern DBCStorage <TalentEntry> sTalentStore;
extern TalentBySpellIDMap sTalentBySpellIDMap;
extern DBCStorage <TotemCategoryEntry> sTotemCategoryStore;
extern DBCStorage <UnitPowerBarEntry> sUnitPowerBarStore;
extern DBCStorage <VehicleEntry> sVehicleStore;
@@ -1788,6 +1788,7 @@ struct SummonPropertiesEntry
};
#define MAX_TALENT_TIERS 7
#define MAX_TALENT_COLUMNS 3
struct TalentEntry
{
+318 -264
View File
@@ -2998,13 +2998,14 @@ void Player::InitTalentForLevel()
{
uint8 level = getLevel();
// talents base at level diff (talents = level - 9 but some can be used already)
if (level < MIN_SPECIALIZATION_LEVEL)
ResetTalentSpecialization();
uint32 talentTiers = CalculateTalentsTiers();
if (level < 15)
{
// Remove all talent points
if (GetUsedTalentCount() > 0) // Free any used talents
{
ResetTalents(true);
}
ResetTalents(true);
}
else
{
@@ -3013,9 +3014,15 @@ void Player::InitTalentForLevel()
SetTalentGroupsCount(1);
SetActiveTalentGroup(0);
}
if (!GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_MORE_TALENTS_THAN_ALLOWED))
for (uint32 t = talentTiers; t < MAX_TALENT_TIERS; ++t)
for (uint32 c = 0; c < MAX_TALENT_COLUMNS; ++c)
for (TalentEntry const* talent : sTalentByPos[getClass()][t][c])
RemoveTalent(talent);
}
SetUInt32Value(PLAYER_FIELD_MAX_TALENT_TIERS, CalculateTalentsPoints());
SetUInt32Value(PLAYER_FIELD_MAX_TALENT_TIERS, talentTiers);
if (!GetSession()->PlayerLoading())
SendTalentsInfoData(); // update at client
@@ -3294,29 +3301,20 @@ void DeleteSpellFromAllPlayers(uint32 spellId)
}
}
bool Player::AddTalent(uint32 talentId, uint8 spec, bool learning)
bool Player::AddTalent(TalentEntry const* talent, uint8 spec, bool learning)
{
TalentEntry const* talentEntry = sTalentStore.LookupEntry(talentId);
// Check if talent exists in Talent.dbc
if (!talentEntry)
{
TC_LOG_ERROR("spells", "Player::addTalent: Talent %u not found", talentId);
return false;
}
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(talentEntry->SpellID);
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(talent->SpellID);
if (!spellInfo)
{
// do character spell book cleanup (all characters)
if (!IsInWorld()) // spell load case
if (!IsInWorld() && !learning) // spell load case
{
TC_LOG_ERROR("spells", "Player::addSpell: Non-existed in SpellStore spell #%u request, deleting for all characters in `character_spell`.", talentEntry->SpellID);
TC_LOG_ERROR("spells", "Player::AddTalent: Non-existed in SpellStore spell #%u request, deleting for all characters in `character_spell`.", talent->SpellID);
DeleteSpellFromAllPlayers(talentEntry->SpellID);
DeleteSpellFromAllPlayers(talent->SpellID);
}
else
TC_LOG_ERROR("spells", "Player::addSpell: Non-existed in SpellStore spell #%u request.", talentEntry->SpellID);
TC_LOG_ERROR("spells", "Player::AddTalent: Non-existed in SpellStore spell #%u request.", talent->SpellID);
return false;
}
@@ -3324,40 +3322,58 @@ bool Player::AddTalent(uint32 talentId, uint8 spec, bool learning)
if (!SpellMgr::IsSpellValid(spellInfo, this, false))
{
// do character spell book cleanup (all characters)
if (!IsInWorld()) // spell load case
if (!IsInWorld() && !learning) // spell load case
{
TC_LOG_ERROR("spells", "Player::addTalent: Broken spell #%u learning not allowed, deleting for all characters in `character_talent`.", talentEntry->SpellID);
TC_LOG_ERROR("spells", "Player::AddTalent: Broken spell #%u learning not allowed, deleting for all characters in `character_talent`.", talent->SpellID);
DeleteSpellFromAllPlayers(talentEntry->SpellID);
DeleteSpellFromAllPlayers(talent->SpellID);
}
else
TC_LOG_ERROR("spells", "Player::addTalent: Broken spell #%u learning not allowed.", talentEntry->SpellID);
TC_LOG_ERROR("spells", "Player::AddTalent: Broken spell #%u learning not allowed.", talent->SpellID);
return false;
}
PlayerTalentMap::iterator itr = GetTalentMap(spec)->find(talentId);
if (itr == GetTalentMap(spec)->end())
{
//if (GetTalentBySpellID(talentEntry->SpellID))
{
PlayerSpellState state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;
PlayerTalent* newtalent = new PlayerTalent();
if (talent->OverridesSpellID)
AddOverrideSpell(talent->OverridesSpellID, talent->SpellID);
newtalent->state = state;
newtalent->spec = spec;
(*GetTalentMap(spec))[talentId] = newtalent;
return true;
}
//else
// TC_LOG_ERROR("spells", "Player::addTalent: Talent %u not found in talent store.", talentId);
}
else
PlayerTalentMap::iterator itr = GetTalentMap(spec)->find(talent->ID);
if (itr != GetTalentMap(spec)->end())
itr->second->state = PLAYERSPELL_UNCHANGED;
else
{
PlayerSpellState state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;
PlayerTalent* newtalent = new PlayerTalent();
return false;
newtalent->state = state;
newtalent->spec = spec;
(*GetTalentMap(spec))[talent->ID] = newtalent;
}
return true;
}
void Player::RemoveTalent(TalentEntry const* talent)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(talent->SpellID);
if (!spellInfo)
return;
RemoveSpell(talent->SpellID, true);
// search for spells that the talent teaches and unlearn them
for (SpellEffectInfo const* effect : spellInfo->GetEffectsForDifficulty(DIFFICULTY_NONE))
if (effect && effect->TriggerSpell > 0 && effect->Effect == SPELL_EFFECT_LEARN_SPELL)
RemoveSpell(effect->TriggerSpell, true);
if (talent->OverridesSpellID)
RemoveOverrideSpell(talent->OverridesSpellID, talent->SpellID);
// if this talent rank can be found in the PlayerTalentMap, mark the talent as removed so it gets deleted
PlayerTalentMap::iterator plrTalent = GetTalentMap(GetActiveTalentGroup())->find(talent->ID);
if (plrTalent != GetTalentMap(GetActiveTalentGroup())->end())
plrTalent->second->state = PLAYERSPELL_REMOVED;
}
bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent, bool disabled, bool loading /*= false*/, bool fromSkill /*= false*/)
@@ -3453,7 +3469,7 @@ bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent
if (active)
{
if (spellInfo->IsPassive() && IsNeedCastPassiveSpellAtLearn(spellInfo))
CastSpell (this, spellId, true);
CastSpell(this, spellId, true);
}
else if (IsInWorld())
{
@@ -3569,11 +3585,9 @@ bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent
return false;
}
uint32 talentCost = GetTalentSpellCost(spellId);
// cast talents with SPELL_EFFECT_LEARN_SPELL (other dependent spells will learned later as not auto-learned)
// note: all spells with SPELL_EFFECT_LEARN_SPELL isn't passive
if (!loading && talentCost > 0 && spellInfo->HasEffect(SPELL_EFFECT_LEARN_SPELL))
if (!loading && spellInfo->HasAttribute(SPELL_ATTR0_CU_IS_TALENT) && spellInfo->HasEffect(SPELL_EFFECT_LEARN_SPELL))
{
// ignore stance requirement for talent learn spell (stance set for spell only for client spell description show)
CastSpell(this, spellId, true);
@@ -3590,72 +3604,45 @@ bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent
return false;
}
// update used talent points count
SetUsedTalentCount(GetUsedTalentCount() + talentCost);
// update free primary prof.points (if any, can be none in case GM .learn prof. learning)
if (uint32 freeProfs = GetFreePrimaryProfessionPoints())
{
if (spellInfo->IsPrimaryProfessionFirstRank())
SetFreePrimaryProfessions(freeProfs-1);
SetFreePrimaryProfessions(freeProfs - 1);
}
// add dependent skills
uint16 maxskill = GetMaxSkillValueForLevel();
SpellLearnSkillNode const* spellLearnSkill = sSpellMgr->GetSpellLearnSkill(spellId);
SkillLineAbilityMapBounds skill_bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId);
if (spellLearnSkill)
// add dependent skills if this spell is not learned from adding skill already
if (!fromSkill)
{
uint32 skill_value = GetPureSkillValue(spellLearnSkill->skill);
uint32 skill_max_value = GetPureMaxSkillValue(spellLearnSkill->skill);
if (skill_value < spellLearnSkill->value)
skill_value = spellLearnSkill->value;
uint32 new_skill_max_value = spellLearnSkill->maxvalue == 0 ? maxskill : spellLearnSkill->maxvalue;
if (skill_max_value < new_skill_max_value)
skill_max_value = new_skill_max_value;
SetSkill(spellLearnSkill->skill, spellLearnSkill->step, skill_value, skill_max_value);
}
else
{
// not ranked skills
for (SkillLineAbilityMap::const_iterator _spell_idx = skill_bounds.first; _spell_idx != skill_bounds.second; ++_spell_idx)
if (SpellLearnSkillNode const* spellLearnSkill = sSpellMgr->GetSpellLearnSkill(spellId))
{
SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->SkillLine);
if (!pSkill)
continue;
uint32 skill_value = GetPureSkillValue(spellLearnSkill->skill);
uint32 skill_max_value = GetPureMaxSkillValue(spellLearnSkill->skill);
if (HasSkill(pSkill->ID))
continue;
if (skill_value < spellLearnSkill->value)
skill_value = spellLearnSkill->value;
SkillRaceClassInfoEntry const* rcEntry = GetSkillRaceClassInfo(pSkill->ID, getRace(), getClass());
if (!rcEntry)
continue;
uint32 new_skill_max_value = spellLearnSkill->maxvalue == 0 ? GetMaxSkillValueForLevel() : spellLearnSkill->maxvalue;
if (_spell_idx->second->AquireMethod == SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN ||
// lockpicking/runeforging special case, not have SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN
((pSkill->ID == SKILL_LOCKPICKING || pSkill->ID == SKILL_RUNEFORGING) && (_spell_idx->second->TrivialSkillLineRankHigh == 0 || _spell_idx->second->TrivialSkillLineRankHigh == 1)))
if (skill_max_value < new_skill_max_value)
skill_max_value = new_skill_max_value;
SetSkill(spellLearnSkill->skill, spellLearnSkill->step, skill_value, skill_max_value);
}
else
{
// not ranked skills
for (SkillLineAbilityMap::const_iterator _spell_idx = skill_bounds.first; _spell_idx != skill_bounds.second; ++_spell_idx)
{
switch (GetSkillRangeType(rcEntry))
{
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;
}
SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->SkillLine);
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);
}
}
}
@@ -3734,8 +3721,7 @@ bool Player::IsNeedCastPassiveSpellAtLearn(SpellInfo const* spellInfo) const
bool Player::IsCurrentSpecMasterySpell(SpellInfo const* spellInfo) const
{
if (ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(GetActiveTalentSpec()))
if (ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(GetSpecId(GetActiveTalentGroup())))
return spellInfo->Id == chrSpec->MasterySpellID[0] || spellInfo->Id == chrSpec->MasterySpellID[1];
return false;
@@ -3790,7 +3776,8 @@ void Player::RemoveSpell(uint32 spell_id, bool disabled, bool learn_low_rank)
// unlearn non talent higher ranks (recursive)
if (uint32 nextSpell = sSpellMgr->GetNextSpellInChain(spell_id))
{
if (HasSpell(nextSpell) && !GetTalentBySpellID(nextSpell))
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(nextSpell);
if (HasSpell(nextSpell) && !spellInfo->HasAttribute(SPELL_ATTR0_CU_IS_TALENT))
RemoveSpell(nextSpell, disabled, false);
}
//unlearn spells dependent from recently removed spells
@@ -3916,6 +3903,8 @@ void Player::RemoveSpell(uint32 spell_id, bool disabled, bool learn_low_rank)
}
}
m_overrideSpells.erase(spell_id);
if (spell_id == 46917 && m_canTitanGrip)
SetCanTitanGrip(false);
@@ -4118,11 +4107,8 @@ uint32 Player::GetNextResetTalentsCost() const
}
}
bool Player::ResetTalents(bool noCost, bool resetTalents, bool resetSpecialization)
bool Player::ResetTalents(bool noCost)
{
if (!resetTalents && !resetSpecialization)
return false;
sScriptMgr->OnPlayerTalentsReset(this, noCost);
// not need after this call
@@ -4144,41 +4130,30 @@ bool Player::ResetTalents(bool noCost, bool resetTalents, bool resetSpecializati
RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true);
if (resetTalents)
for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
{
for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
{
TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
if (!talentInfo)
continue;
if (!talentInfo)
continue;
// unlearn only talents for character class
// some spell learned by one class as normal spells or know at creation but another class learn it as talent,
// to prevent unexpected lost normal learned spell skip another class talents
if (talentInfo->ClassID != getClass())
continue;
// unlearn only talents for character class
// some spell learned by one class as normal spells or know at creation but another class learn it as talent,
// to prevent unexpected lost normal learned spell skip another class talents
if (talentInfo->ClassID != getClass())
continue;
// skip non-existant talent ranks
if (talentInfo->SpellID == 0)
continue;
SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(talentInfo->SpellID);
if (!spellEntry)
continue;
RemoveSpell(spellEntry->Id, false);
// search for spells that the talent teaches and unlearn them, 6.x remove?
for (SpellEffectInfo const* effect : spellEntry->GetEffectsForDifficulty(DIFFICULTY_NONE))
if (effect && effect->TriggerSpell > 0 && effect->Effect == SPELL_EFFECT_LEARN_SPELL)
RemoveSpell(effect->TriggerSpell, true);
GetTalentMap(GetActiveTalentGroup())->erase(talentId);
}
RemoveTalent(talentInfo);
}
if (resetSpecialization)
{
SetTalentSpec(GetActiveTalentGroup(), 0);
SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, 0);
}
// Remove spec specific spells
RemoveSpecializationSpells();
SetSpecId(GetActiveTalentGroup(), 0);
SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, 0);
SQLTransaction trans = CharacterDatabase.BeginTransaction();
_SaveTalents(trans);
@@ -4206,35 +4181,6 @@ bool Player::ResetTalents(bool noCost, bool resetTalents, bool resetSpecializati
return true;
}
bool Player::RemoveTalent(uint32 talentId)
{
TalentEntry const* talent = sTalentStore.LookupEntry(talentId);
if (!talent)
return false;
uint32 spellId = talent->SpellID;
SpellInfo const* unlearnSpellProto = sSpellMgr->GetSpellInfo(spellId);
RemoveSpell(spellId, false);
// 6.x remove?
for (SpellEffectInfo const* effect : unlearnSpellProto->GetEffectsForDifficulty(DIFFICULTY_NONE))
if (effect && effect->TriggerSpell > 0 && effect->Effect == SPELL_EFFECT_LEARN_SPELL)
RemoveSpell(effect->TriggerSpell, false);
GetTalentMap(GetActiveTalentSpec())->erase(talentId);
// Needs to be executed orthewise the talents will be screwedsx
SQLTransaction trans = CharacterDatabase.BeginTransaction();
_SaveTalents(trans);
_SaveSpells(trans);
CharacterDatabase.CommitTransaction(trans);
SendTalentsInfoData();
return true;
}
Mail* Player::GetMail(uint32 id)
{
for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
@@ -4299,10 +4245,10 @@ bool Player::HasSpell(uint32 spell) const
!itr->second->disabled);
}
bool Player::HasTalent(uint32 talentId, uint8 group)
bool Player::HasTalent(uint32 talentId, uint8 group) const
{
PlayerTalentMap::const_iterator itr = GetTalentMap(group)->find(talentId);
return (itr != GetTalentMap(group)->end());
return (itr != GetTalentMap(group)->end() && itr->second->state != PLAYERSPELL_REMOVED);
}
bool Player::HasActiveSpell(uint32 spell) const
@@ -5951,26 +5897,28 @@ void Player::UpdateSkillsForLevel()
if (!rcEntry)
continue;
if (GetSkillRangeType(rcEntry) != SKILL_RANGE_LEVEL)
continue;
if (IsWeaponSkill(rcEntry->SkillID))
continue;
uint16 field = itr->second.pos / 2;
uint8 offset = itr->second.pos & 1; // itr->second.pos % 2
//uint16 val = GetUInt16Value(PLAYER_SKILL_LINEID + SKILL_RANK_OFFSET + field, offset);
uint16 max = GetUInt16Value(PLAYER_SKILL_LINEID + SKILL_MAX_RANK_OFFSET + field, offset);
/// update only level dependent max skill values
if (max != 1)
if (GetSkillRangeType(rcEntry) == SKILL_RANGE_LEVEL)
{
SetUInt16Value(PLAYER_SKILL_LINEID + SKILL_RANK_OFFSET + field, offset, maxSkill);
SetUInt16Value(PLAYER_SKILL_LINEID + SKILL_MAX_RANK_OFFSET + field, offset, maxSkill);
if (itr->second.uState != SKILL_NEW)
itr->second.uState = SKILL_CHANGED;
if (!IsWeaponSkill(rcEntry->SkillID))
{
uint16 max = GetUInt16Value(PLAYER_SKILL_LINEID + SKILL_MAX_RANK_OFFSET + field, offset);
/// update only level dependent max skill values
if (max != 1)
{
SetUInt16Value(PLAYER_SKILL_LINEID + SKILL_RANK_OFFSET + field, offset, maxSkill);
SetUInt16Value(PLAYER_SKILL_LINEID + SKILL_MAX_RANK_OFFSET + field, offset, maxSkill);
if (itr->second.uState != SKILL_NEW)
itr->second.uState = SKILL_CHANGED;
}
}
}
// Update level dependent skillline spells
LearnSkillRewardedSpells(rcEntry->SkillID, GetUInt16Value(PLAYER_SKILL_LINEID + SKILL_RANK_OFFSET + field, offset));
}
}
@@ -8415,7 +8363,7 @@ void Player::CastItemUseSpell(Item* item, SpellCastTargets const& targets, uint8
Spell* spell = new Spell(this, spellInfo, (count > 0) ? TRIGGERED_FULL_MASK : TRIGGERED_NONE);
spell->m_CastItem = item;
spell->m_cast_count = cast_count; // set count of casts
spell->m_glyphIndex = glyphIndex; // glyph index
spell->m_misc.Data = glyphIndex; // glyph index
spell->prepare(&targets);
++count;
@@ -8443,7 +8391,7 @@ void Player::CastItemUseSpell(Item* item, SpellCastTargets const& targets, uint8
Spell* spell = new Spell(this, spellInfo, (count > 0) ? TRIGGERED_FULL_MASK : TRIGGERED_NONE);
spell->m_CastItem = item;
spell->m_cast_count = cast_count; // set count of casts
spell->m_glyphIndex = glyphIndex; // glyph index
spell->m_misc.Data = glyphIndex; // glyph index
spell->prepare(&targets);
++count;
@@ -17193,17 +17141,19 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
if (talentSpec)
{
if (sChrSpecializationStore.LookupEntry(talentSpec))
SetTalentSpec(i, talentSpec);
SetSpecId(i, talentSpec);
else
SetAtLoginFlag(AT_LOGIN_RESET_TALENTS);
}
}
SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, GetActiveTalentSpec());
SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, GetSpecId(GetActiveTalentGroup()));
_LoadTalents(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_TALENTS));
_LoadSpells(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELLS));
LearnSpecializationSpells();
_LoadGlyphs(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_GLYPHS));
_LoadAuras(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_AURAS), holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_AURA_EFFECTS), time_diff);
_LoadGlyphAuras();
@@ -18912,7 +18862,7 @@ void Player::SaveToDB(bool create /*=false*/)
ss.str("");
for (uint8 i = 0; i < MAX_TALENT_GROUPS; ++i)
ss << GetTalentSpec(i) << " ";
ss << GetSpecId(i) << " ";
stmt->setString(index++, ss.str());
stmt->setUInt16(index++, (uint16)m_ExtraFlags);
stmt->setUInt8(index++, m_stableSlots);
@@ -19049,7 +18999,7 @@ void Player::SaveToDB(bool create /*=false*/)
ss.str("");
for (uint8 i = 0; i < MAX_TALENT_GROUPS; ++i)
ss << GetTalentSpec(i) << " ";
ss << GetSpecId(i) << " ";
stmt->setString(index++, ss.str());
stmt->setUInt16(index++, (uint16)m_ExtraFlags);
stmt->setUInt8(index++, m_stableSlots);
@@ -25404,43 +25354,60 @@ void Player::CompletedAchievement(AchievementEntry const* entry)
bool Player::LearnTalent(uint32 talentId)
{
TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
if (!talentInfo)
return false;
uint32 maxTalentTier = GetUInt32Value(PLAYER_FIELD_MAX_TALENT_TIERS);
if (talentInfo->SpecID && talentInfo->SpecID != GetSpecId(GetActiveTalentGroup()))
return false;
// prevent learn talent for different class (cheating)
if (talentInfo->ClassID != getClass())
return false;
// check if we have enough talent points
if (talentInfo->TierID > maxTalentTier)
if (talentInfo->TierID >= GetUInt32Value(PLAYER_FIELD_MAX_TALENT_TIERS))
return false;
// Check if player doesnt have any spell in selected collumn
for (uint32 i = 0; i < sTalentStore.GetNumRows(); i++)
// Check if there is a different talent for us to learn in selected slot
// Example situation:
// Warrior talent row 2 slot 0
// Talent.dbc has an entry for each specialization
// but only 2 out of 3 have SpecID != 0
// We need to make sure that if player is in one of these defined specs he will not learn the other choice
TalentEntry const* bestSlotMatch = nullptr;
for (TalentEntry const* talent : sTalentByPos[getClass()][talentInfo->TierID][talentInfo->ColumnIndex])
{
if (TalentEntry const* talent = sTalentStore.LookupEntry(i))
if (!talent->SpecID)
bestSlotMatch = talent;
else if (talent->SpecID == GetSpecId(GetActiveTalentGroup()))
{
if (talentInfo->TierID == talent->TierID && HasSpell(talent->SpellID))
return false;
bestSlotMatch = talent;
break;
}
}
if (talentInfo != bestSlotMatch)
return false;
// Check if player doesnt have any talent in current tier
for (uint32 c = 0; c < MAX_TALENT_COLUMNS; ++c)
for (TalentEntry const* talent : sTalentByPos[getClass()][talentInfo->TierID][c])
if (HasTalent(talent->ID, GetActiveTalentGroup()))
return false;
// spell not set in talent.dbc
uint32 spellid = talentInfo->SpellID;
if (spellid == 0)
if (!spellid)
{
TC_LOG_ERROR("entities.player", "Talent.dbc has no spellInfo for talent: %u (spell id = 0)", talentId);
return false;
}
// already known
if (HasSpell(spellid))
if (HasTalent(talentId, GetActiveTalentGroup()) || HasSpell(spellid))
return false;
if (!AddTalent(talentId, GetActiveTalentGroup(), true))
if (!AddTalent(talentInfo, GetActiveTalentGroup(), true))
return false;
LearnSpell(spellid, false);
@@ -25452,49 +25419,42 @@ bool Player::LearnTalent(uint32 talentId)
void Player::LearnTalentSpecialization(uint32 talentSpec)
{
if (GetActiveTalentSpec())
if (GetSpecId(GetActiveTalentGroup()))
return;
SetTalentSpec(GetActiveTalentGroup(), talentSpec);
SetSpecId(GetActiveTalentGroup(), talentSpec);
SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, talentSpec);
PlayerTalentMap* talents = GetTalentMap(GetActiveTalentGroup());
for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
{
TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
if (!talentInfo || talentInfo->ClassID != getClass() || talentInfo->SpecID != talentSpec)
continue;
for (PlayerTalentMap::iterator itr = talents->begin(); itr != talents->end();)
{
TalentEntry const* talent = sTalentStore.LookupEntry(itr->first);
if (!talent || talent->TierID != talentInfo->TierID)
{
++itr;
continue;
}
RemoveSpell(talent->SpellID, false);
itr = talents->erase(itr);
TC_LOG_DEBUG("spells", "Player %s unlearning talent id: %u tier: %u due to specialization change", GetName().c_str(), talent->ID, talent->TierID);
}
}
SendTalentsInfoData();
SaveToDB();
// Reset only talents that have different spells for each spec
uint32 class_ = getClass();
for (uint32 t = 0; t < MAX_TALENT_TIERS; ++t)
for (uint32 c = 0; c < MAX_TALENT_COLUMNS; ++c)
if (sTalentByPos[class_][t][c].size() > 1)
for (TalentEntry const* talent : sTalentByPos[class_][t][c])
RemoveTalent(talent);
LearnSpecializationSpells();
SendTalentsInfoData();
}
void Player::AddKnownCurrency(uint32 itemId)
void Player::ResetTalentSpecialization()
{
if (CurrencyTypesEntry const* ctEntry = sCurrencyTypesStore.LookupEntry(itemId))
SetFlag64(0, (1LL << (ctEntry->ID-1)));
if (!GetSpecId(GetActiveTalentGroup()))
return;
SetSpecId(GetActiveTalentGroup(), 0);
SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, 0);
// Reset only talents that have different spells for each spec
uint32 class_ = getClass();
for (uint32 t = 0; t < MAX_TALENT_TIERS; ++t)
for (uint32 c = 0; c < MAX_TALENT_COLUMNS; ++c)
if (sTalentByPos[class_][t][c].size() > 1)
for (TalentEntry const* talent : sTalentByPos[class_][t][c])
RemoveTalent(talent);
RemoveSpecializationSpells();
SendTalentsInfoData();
}
void Player::UpdateFallInformationIfNeed(MovementInfo const& minfo, uint16 opcode)
@@ -25577,12 +25537,14 @@ void Player::SendTalentsInfoData()
{
WorldPackets::Talent::TalentGroupInfo groupInfoPkt;
groupInfoPkt.SpecID = GetTalentSpec(i);
groupInfoPkt.SpecID = GetSpecId(i);
groupInfoPkt.TalentIDs.reserve(GetTalentMap(i)->size());
for (PlayerTalentMap::const_iterator itr = GetTalentMap(i)->begin(); itr != GetTalentMap(i)->end(); ++itr)
{
if (itr->second->state == PLAYERSPELL_REMOVED)
continue;
TalentEntry const* talentInfo = sTalentStore.LookupEntry(itr->first);
if (!talentInfo)
{
@@ -25600,9 +25562,6 @@ void Player::SendTalentsInfoData()
continue;
}
if (!HasTalent(itr->first, i))
continue;
groupInfoPkt.TalentIDs.push_back(uint16(itr->first));
}
@@ -25919,7 +25878,8 @@ void Player::_LoadTalents(PreparedQueryResult result)
if (result)
{
do
AddTalent((*result)[0].GetUInt32(), (*result)[1].GetUInt8(), false);
if (TalentEntry const* talent = sTalentStore.LookupEntry((*result)[0].GetUInt32()))
AddTalent(talent, (*result)[1].GetUInt8(), false);
while (result->NextRow());
}
}
@@ -25930,15 +25890,25 @@ void Player::_SaveTalents(SQLTransaction& trans)
stmt->setUInt64(0, GetGUID().GetCounter());
trans->Append(stmt);
PlayerTalentMap* talents;
for (uint8 group = 0; group < MAX_TALENT_GROUPS; ++group)
{
for (PlayerTalentMap::iterator itr = GetTalentMap(group)->begin(); itr != GetTalentMap(group)->end(); ++itr)
talents = GetTalentMap(group);
for (PlayerTalentMap::iterator itr = talents->begin(); itr != talents->end();)
{
if (itr->second->state == PLAYERSPELL_REMOVED)
{
delete itr->second;
itr = talents->erase(itr);
continue;
}
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_TALENT);
stmt->setUInt64(0, GetGUID().GetCounter());
stmt->setUInt32(1, itr->first);
stmt->setUInt8(2, itr->second->spec);
trans->Append(stmt);
++itr;
}
}
}
@@ -25989,12 +25959,12 @@ void Player::UpdateTalentGroupCount(uint8 count)
SendTalentsInfoData();
}
void Player::ActivateTalentGroup(uint8 group)
void Player::ActivateTalentGroup(uint8 spec)
{
if (GetActiveTalentGroup() == group)
if (GetActiveTalentGroup() == spec)
return;
if (group > GetTalentGroupsCount())
if (spec > GetTalentGroupsCount())
return;
if (IsNonMeleeSpellCast(false))
@@ -26026,57 +25996,76 @@ void Player::ActivateTalentGroup(uint8 group)
for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
{
TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
if (!talentInfo)
continue;
// unlearn only talents for character class
// some spell learned by one class as normal spells or know at creation but another class learn it as talent,
// to prevent unexpected lost normal learned spell skip another class talents
if (getClass() != talentInfo->ClassID)
if (talentInfo->ClassID != getClass())
continue;
SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(talentInfo->SpellID);
if (!spellEntry)
if (talentInfo->SpellID == 0)
continue;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(talentInfo->SpellID);
if (!spellInfo)
continue;
RemoveSpell(talentInfo->SpellID, true);
// search for spells that the talent teaches and unlearn them
for (SpellEffectInfo const* effect : spellEntry->GetEffectsForDifficulty(DIFFICULTY_NONE))
for (SpellEffectInfo const* effect : spellInfo->GetEffectsForDifficulty(DIFFICULTY_NONE))
if (effect && effect->TriggerSpell > 0 && effect->Effect == SPELL_EFFECT_LEARN_SPELL)
RemoveSpell(effect->TriggerSpell, true);
if (talentInfo->OverridesSpellID)
RemoveOverrideSpell(talentInfo->OverridesSpellID, talentInfo->SpellID);
}
// remove glyphs
// Remove spec specific spells
RemoveSpecializationSpells();
// set glyphs
for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot)
// remove secondary glyph
if (uint32 oldglyph = GetGlyph(GetActiveTalentGroup(), slot))
if (GlyphPropertiesEntry const* old_gp = sGlyphPropertiesStore.LookupEntry(oldglyph))
RemoveAurasDueToSpell(old_gp->SpellID);
// Activate new group
SetActiveTalentGroup(group);
uint32 spentTalents = 0;
SetActiveTalentGroup(spec);
SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, GetSpecId(spec));
for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
{
TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
if (!talentInfo)
continue;
// learn only talents for character class
if (getClass() != talentInfo->ClassID)
if (talentInfo->ClassID != getClass())
continue;
++spentTalents;
if (!talentInfo->SpellID)
continue;
if (HasTalent(talentInfo->SpellID, group))
LearnSpell(talentInfo->SpellID, false);
// if the talent can be found in the newly activated PlayerTalentMap
if (HasTalent(talentInfo->ID, GetActiveTalentGroup()))
{
LearnSpell(talentInfo->SpellID, false); // add the talent to the PlayerSpellMap
if (talentInfo->OverridesSpellID)
AddOverrideSpell(talentInfo->OverridesSpellID, talentInfo->SpellID);
}
}
LearnSpecializationSpells();
if (CanUseMastery())
if (ChrSpecializationEntry const* specialization = sChrSpecializationStore.LookupEntry(GetSpecId(GetActiveTalentGroup())))
for (uint32 i = 0; i < MAX_MASTERY_SPELLS; ++i)
if (uint32 mastery = specialization->MasterySpellID[i])
LearnSpell(mastery, false);
// set glyphs
for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot)
{
@@ -26090,7 +26079,6 @@ void Player::ActivateTalentGroup(uint8 group)
SetGlyph(slot, glyph);
}
SetUsedTalentCount(spentTalents);
InitTalentForLevel();
{
@@ -26109,10 +26097,8 @@ void Player::ActivateTalentGroup(uint8 group)
SetPower(pw, 0);
if (!sChrSpecializationStore.LookupEntry(GetActiveTalentSpec()))
{
if (!sChrSpecializationStore.LookupEntry(GetSpecId(GetActiveTalentGroup())))
ResetTalents(true);
}
}
void Player::ResetTimeSync()
@@ -27153,10 +27139,17 @@ void Player::SendSupercededSpell(uint32 oldSpell, uint32 newSpell)
GetSession()->SendPacket(&data);
}
uint32 Player::CalculateTalentsPoints() const
uint32 Player::CalculateTalentsTiers() const
{
// 1 talent point for every 15 levels
return getLevel() >= 100 ? 7 : uint32(floor(getLevel() / 15.f));
static uint32 const DefaultTalentRowLevels[MAX_TALENT_TIERS] = { 15, 30, 45, 60, 75, 90, 100 };
static uint32 const DKTalentRowLevels[MAX_TALENT_TIERS] = { 57, 58, 59, 60, 75, 90, 100 };
uint32 const* rowLevels = (getClass() != CLASS_DEATH_KNIGHT) ? DefaultTalentRowLevels : DKTalentRowLevels;
for (uint32 i = MAX_TALENT_TIERS; i; --i)
if (getLevel() >= rowLevels[i - 1])
return i;
return 0;
}
Difficulty Player::GetDifficultyID(MapEntry const* mapEntry) const
@@ -27219,3 +27212,64 @@ Difficulty Player::CheckLoadedLegacyRaidDifficultyID(Difficulty difficulty)
return difficulty;
}
SpellInfo const* Player::GetCastSpellInfo(SpellInfo const* spellInfo) const
{
auto range = m_overrideSpells.equal_range(spellInfo->Id);
for (auto itr = range.first; itr != range.second; ++itr)
if (SpellInfo const* newInfo = sSpellMgr->GetSpellInfo(itr->second))
return Unit::GetCastSpellInfo(newInfo);
return Unit::GetCastSpellInfo(spellInfo);
}
void Player::RemoveOverrideSpell(uint32 overridenSpellId, uint32 newSpellId)
{
auto range = m_overrideSpells.equal_range(overridenSpellId);
for (auto itr = range.first; itr != range.second; ++itr)
{
if (itr->second == newSpellId)
{
m_overrideSpells.erase(itr);
break;
}
}
}
void Player::LearnSpecializationSpells()
{
if (std::vector<SpecializationSpellsEntry const*> const* specSpells = GetSpecializationSpells(GetSpecId(GetActiveTalentGroup())))
{
for (size_t j = 0; j < specSpells->size(); ++j)
{
SpecializationSpellsEntry const* specSpell = specSpells->at(j);
LearnSpell(specSpell->SpellID, false);
if (specSpell->OverridesSpellID)
AddOverrideSpell(specSpell->OverridesSpellID, specSpell->SpellID);
}
}
}
void Player::RemoveSpecializationSpells()
{
for (uint32 i = 0; i < MAX_SPECIALIZATIONS; ++i)
{
if (ChrSpecializationEntry const* specialization = sChrSpecializationByIndexStore[getClass()][i])
{
if (std::vector<SpecializationSpellsEntry const*> const* specSpells = GetSpecializationSpells(specialization->ID))
{
for (size_t j = 0; j < specSpells->size(); ++j)
{
SpecializationSpellsEntry const* specSpell = specSpells->at(j);
RemoveSpell(specSpell->SpellID, true);
if (specSpell->OverridesSpellID)
RemoveOverrideSpell(specSpell->OverridesSpellID, specSpell->SpellID);
}
}
for (uint32 j = 0; j < MAX_MASTERY_SPELLS; ++j)
if (uint32 mastery = specialization->MasterySpellID[j])
RemoveAurasDueToSpell(mastery);
}
}
}
+22 -25
View File
@@ -1247,13 +1247,15 @@ private:
struct PlayerTalentInfo
{
PlayerTalentInfo() : UsedTalentCount(0), ResetTalentsCost(0), ResetTalentsTime(0), ActiveGroup(0), GroupsCount(1)
PlayerTalentInfo() :
ResetTalentsCost(0), ResetTalentsTime(0),
ActiveGroup(0), GroupsCount(1)
{
for (uint8 i = 0; i < MAX_TALENT_GROUPS; ++i)
{
GroupInfo[i].Talents = new PlayerTalentMap();
memset(GroupInfo[i].Glyphs, 0, MAX_GLYPH_SLOT_INDEX * sizeof(uint32));
GroupInfo[i].TalentTree = 0;
GroupInfo[i].SpecId = 0;
}
}
@@ -1271,10 +1273,9 @@ struct PlayerTalentInfo
{
PlayerTalentMap* Talents;
uint32 Glyphs[MAX_GLYPH_SLOT_INDEX];
uint32 TalentTree;
uint32 SpecId;
} GroupInfo[MAX_TALENT_GROUPS];
uint32 UsedTalentCount;
uint32 ResetTalentsCost;
time_t ResetTalentsTime;
uint8 ActiveGroup;
@@ -1284,6 +1285,7 @@ private:
PlayerTalentInfo(PlayerTalentInfo const&);
};
class Player : public Unit, public GridObject<Player>
{
friend class WorldSession;
@@ -1820,6 +1822,7 @@ class Player : public Unit, public GridObject<Player>
void SendRemoveControlBar();
bool HasSpell(uint32 spell) const override;
bool HasActiveSpell(uint32 spell) const; // show in spellbook
SpellInfo const* GetCastSpellInfo(SpellInfo const* spellInfo) const override;
TrainerSpellState GetTrainerSpellState(TrainerSpell const* trainer_spell) const;
bool IsSpellFitByClassAndRace(uint32 spell_id) const;
bool IsNeedCastPassiveSpellAtLearn(SpellInfo const* spellInfo) const;
@@ -1839,41 +1842,37 @@ class Player : public Unit, public GridObject<Player>
void LearnSpellHighestRank(uint32 spellid);
void AddTemporarySpell(uint32 spellId);
void RemoveTemporarySpell(uint32 spellId);
void AddOverrideSpell(uint32 overridenSpellId, uint32 newSpellId) { m_overrideSpells.emplace(overridenSpellId, newSpellId); }
void RemoveOverrideSpell(uint32 overridenSpellId, uint32 newSpellId);
void LearnSpecializationSpells();
void RemoveSpecializationSpells();
void SetReputation(uint32 factionentry, uint32 value);
uint32 GetReputation(uint32 factionentry) const;
std::string GetGuildName();
// Talents
uint32 GetUsedTalentCount() const { return _talentMgr->UsedTalentCount; }
void SetUsedTalentCount(uint32 talents) { _talentMgr->UsedTalentCount = talents; }
uint32 GetTalentResetCost() const { return _talentMgr->ResetTalentsCost; }
void SetTalentResetCost(uint32 cost) { _talentMgr->ResetTalentsCost = cost; }
uint32 GetTalentResetTime() const { return _talentMgr->ResetTalentsTime; }
void SetTalentResetTime(time_t time_) { _talentMgr->ResetTalentsTime = time_; }
uint8 GetTalentGroupsCount() const { return _talentMgr->GroupsCount; }
void SetTalentGroupsCount(uint8 count) { _talentMgr->GroupsCount = count; }
uint32 GetSpecId(uint8 group) const { return _talentMgr->GroupInfo[group].SpecId; }
void SetSpecId(uint8 group, uint32 tree) { _talentMgr->GroupInfo[group].SpecId = tree; }
uint8 GetActiveTalentGroup() const { return _talentMgr->ActiveGroup; }
void SetActiveTalentGroup(uint8 group){ _talentMgr->ActiveGroup = group; }
uint8 GetTalentGroupsCount() const { return _talentMgr->GroupsCount; }
void SetTalentGroupsCount(uint8 count) { _talentMgr->GroupsCount = count; }
uint32 GetTalentSpec(uint8 group) const { return _talentMgr->GroupInfo[group].TalentTree; }
void SetTalentSpec(uint8 group, uint32 talentSpec) const { _talentMgr->GroupInfo[group].TalentTree = talentSpec; }
uint32 GetActiveTalentSpec() const { return _talentMgr->GroupInfo[_talentMgr->ActiveGroup].TalentTree; }
bool ResetTalents(bool noCost = false, bool resetTalents = true, bool resetSpecialization = true);
bool RemoveTalent(uint32 talentId);
bool ResetTalents(bool noCost = false);
uint32 GetNextResetTalentsCost() const;
void InitTalentForLevel();
void SendTalentsInfoData();
bool LearnTalent(uint32 talentId);
bool AddTalent(uint32 talentId, uint8 spec, bool learning);
bool HasTalent(uint32 talentId, uint8 spec);
uint32 CalculateTalentsPoints() const;
bool AddTalent(TalentEntry const* talent, uint8 spec, bool learning);
bool HasTalent(uint32 spell_id, uint8 spec) const;
void RemoveTalent(TalentEntry const* talent);
uint32 CalculateTalentsTiers() const;
void LearnTalentSpecialization(uint32 talentSpec);
void ResetTalentSpecialization();
// Dual Spec
void UpdateTalentGroupCount(uint8 count);
@@ -2801,6 +2800,7 @@ class Player : public Unit, public GridObject<Player>
PlayerMails m_mail;
PlayerSpellMap m_spells;
std::unordered_multimap<uint32 /*overridenSpellId*/, uint32 /*newSpellId*/> m_overrideSpells;
uint32 m_lastPotionId; // last used health/mana potion in combat, that block next potion use
GlobalCooldownMgr m_GlobalCooldownMgr;
@@ -2919,9 +2919,6 @@ class Player : public Unit, public GridObject<Player>
void RefundItem(Item* item);
void SendItemRefundResult(Item* item, ItemExtendedCostEntry const* iece, uint8 error);
// know currencies are not removed at any point (0 displayed)
void AddKnownCurrency(uint32 itemId);
void AdjustQuestReqItemCount(Quest const* quest);
bool IsCanDelayTeleport() const { return m_bCanDelayTeleport; }
+1 -1
View File
@@ -526,7 +526,7 @@ void Player::UpdateMastery()
value += GetRatingBonusValue(CR_MASTERY);
SetFloatValue(PLAYER_MASTERY, value);
ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(GetActiveTalentSpec());
ChrSpecializationEntry const* chrSpec = sChrSpecializationStore.LookupEntry(GetSpecId(GetActiveTalentGroup()));
if (!chrSpec)
return;
+15
View File
@@ -16569,3 +16569,18 @@ void Unit::Whisper(uint32 textId, Player* target, bool isBossWhisper /*= false*/
packet.Initalize(isBossWhisper ? CHAT_MSG_RAID_BOSS_WHISPER : CHAT_MSG_MONSTER_WHISPER, LANG_UNIVERSAL, this, target, DB2Manager::GetBroadcastTextValue(bct, locale, getGender()), 0, "", locale);
target->SendDirectMessage(packet.Write());
}
SpellInfo const* Unit::GetCastSpellInfo(SpellInfo const* spellInfo) const
{
Unit::AuraEffectList swaps = GetAuraEffectsByType(SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS);
Unit::AuraEffectList const& swaps2 = GetAuraEffectsByType(SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS_2);
if (!swaps2.empty())
swaps.insert(swaps.end(), swaps2.begin(), swaps2.end());
for (AuraEffect const* auraEffect : swaps)
if (auraEffect->IsAffectingSpell(spellInfo))
if (SpellInfo const* newInfo = sSpellMgr->GetSpellInfo(auraEffect->GetAmount()))
return newInfo;
return spellInfo;
}
+1
View File
@@ -1936,6 +1936,7 @@ class Unit : public WorldObject
Spell* GetCurrentSpell(uint32 spellType) const { return m_currentSpells[spellType]; }
Spell* FindCurrentSpellBySpellId(uint32 spell_id) const;
int32 GetCurrentSpellCastTime(uint32 spell_id) const;
virtual SpellInfo const* GetCastSpellInfo(SpellInfo const* spellInfo) const;
ObjectGuid m_SummonSlot[MAX_SUMMON_SLOT];
ObjectGuid m_ObjectSlot[MAX_GAMEOBJECT_SLOT];
+1 -1
View File
@@ -752,7 +752,7 @@ void WorldSession::HandlePetSpellAutocastOpcode(WorldPacket& recvPacket)
charmInfo->SetSpellAutocast(spellInfo, state != 0);
}
void WorldSession::HandlePetCastSpellOpcode(WorldPackets::Spells::SpellCastRequest& castRequest)
void WorldSession::HandlePetCastSpellOpcode(WorldPackets::Spells::PetCastSpell& castRequest)
{
TC_LOG_DEBUG("network", "WORLD: CMSG_PET_CAST_SPELL");
/*
@@ -32,10 +32,8 @@ void WorldSession::HandleLearnTalentsOpcode(WorldPackets::Talent::LearnTalents&
{
bool anythingLearned = false;
for (uint32 talentId : packet.Talents)
{
if (_player->LearnTalent(talentId))
anythingLearned = true;
}
if (anythingLearned)
_player->SendTalentsInfoData();
+11 -67
View File
@@ -320,109 +320,53 @@ void WorldSession::HandleGameobjectReportUse(WorldPackets::GameObject::GameObjec
_player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT, go->GetEntry());
}
void WorldSession::HandleCastSpellOpcode(WorldPackets::Spells::SpellCastRequest& castRequest)
void WorldSession::HandleCastSpellOpcode(WorldPackets::Spells::CastSpell& cast)
{
// ignore for remote control state (for player case)
Unit* mover = _player->m_mover;
if (mover != _player && mover->GetTypeId() == TYPEID_PLAYER)
{
return;
}
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(castRequest.SpellID);
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(cast.Cast.SpellID);
if (!spellInfo)
{
TC_LOG_ERROR("network", "WORLD: unknown spell id %u", castRequest.SpellID);
TC_LOG_ERROR("network", "WORLD: unknown spell id %u", cast.Cast.SpellID);
return;
}
if (spellInfo->IsPassive())
{
return;
}
Unit* caster = mover;
if (caster->GetTypeId() == TYPEID_UNIT && !caster->ToCreature()->HasSpell(castRequest.SpellID))
if (caster->GetTypeId() == TYPEID_UNIT && !caster->ToCreature()->HasSpell(spellInfo->Id))
{
// If the vehicle creature does not have the spell but it allows the passenger to cast own spells
// change caster to player and let him cast
if (!_player->IsOnVehicle(caster) || spellInfo->CheckVehicle(_player) != SPELL_CAST_OK)
{
return;
}
caster = _player;
}
if (caster->GetTypeId() == TYPEID_PLAYER && !caster->ToPlayer()->HasActiveSpell(castRequest.SpellID))
{
// not have spell in spellbook
// check known spell
if (caster->GetTypeId() == TYPEID_PLAYER && !caster->ToPlayer()->HasActiveSpell(spellInfo->Id))
return;
}
if (Player* plr = caster->ToPlayer())
{
uint32 specId = plr->GetActiveTalentSpec();
if (specId)
{
if (sSpecializationOverrideSpellMap.find(specId) != sSpecializationOverrideSpellMap.end())
{
if (sSpecializationOverrideSpellMap[specId].find(castRequest.SpellID) != sSpecializationOverrideSpellMap[specId].end())
{
SpellInfo const* newSpellInfo = sSpellMgr->GetSpellInfo(sSpecializationOverrideSpellMap[specId][castRequest.SpellID]);
if (newSpellInfo)
{
if (newSpellInfo->SpellLevel <= caster->getLevel())
{
spellInfo = newSpellInfo;
castRequest.SpellID = newSpellInfo->Id;
}
}
}
}
}
}
Unit::AuraEffectList swaps = mover->GetAuraEffectsByType(SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS);
Unit::AuraEffectList const& swaps2 = mover->GetAuraEffectsByType(SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS_2);
if (!swaps2.empty())
swaps.insert(swaps.end(), swaps2.begin(), swaps2.end());
if (!swaps.empty())
{
for (Unit::AuraEffectList::const_iterator itr = swaps.begin(); itr != swaps.end(); ++itr)
{
if ((*itr)->IsAffectingSpell(spellInfo))
{
if (SpellInfo const* newInfo = sSpellMgr->GetSpellInfo((*itr)->GetAmount()))
{
spellInfo = newInfo;
castRequest.SpellID = newInfo->Id;
}
break;
}
}
}
// Check possible spell cast overrides
spellInfo = caster->GetCastSpellInfo(spellInfo);
// Client is resending autoshot cast opcode when other spell is cast during shoot rotation
// Skip it to prevent "interrupt" message
if (spellInfo->IsAutoRepeatRangedSpell() && caster->GetCurrentSpell(CURRENT_AUTOREPEAT_SPELL)
&& caster->GetCurrentSpell(CURRENT_AUTOREPEAT_SPELL)->m_spellInfo == spellInfo)
{
return;
}
// can't use our own spells when we're in possession of another unit,
if (_player->isPossessing())
{
return;
}
// client provided targets
SpellCastTargets targets(caster, castRequest.TargetFlags, castRequest.UnitGuid, castRequest.ItemGuid, castRequest.SrcTransportGuid, castRequest.DstTransportGuid, castRequest.SrcPos, castRequest.DstPos, castRequest.Pitch, castRequest.Speed, castRequest.Name);
//HandleClientCastFlags(recvPacket, castFlags, targets);
SpellCastTargets targets(caster, cast.Cast.Target);
// auto-selection buff level base at target level (in spellInfo)
if (targets.GetUnitTarget())
@@ -435,8 +379,8 @@ void WorldSession::HandleCastSpellOpcode(WorldPackets::Spells::SpellCastRequest&
}
Spell* spell = new Spell(caster, spellInfo, TRIGGERED_NONE, ObjectGuid::Empty, false);
spell->m_cast_count = castRequest.CastID; // set count of casts
spell->m_glyphIndex = castRequest.Misc; // 6.x Misc is just a guess
spell->m_cast_count = cast.Cast.CastID; // set count of casts
spell->m_misc.Data = cast.Cast.Misc; // 6.x Misc is just a guess
spell->prepare(&targets);
}
@@ -1188,7 +1188,7 @@ enum SpellEffectName
SPELL_EFFECT_178 = 178, // Unused (4.3.4)
SPELL_EFFECT_CREATE_AREATRIGGER = 179,
SPELL_EFFECT_UPDATE_AREATRIGGER = 180, // NYI
SPELL_EFFECT_REMOVE_TALENT = 181, // NYI
SPELL_EFFECT_REMOVE_TALENT = 181,
SPELL_EFFECT_182 = 182,
SPELL_EFFECT_183 = 183,
SPELL_EFFECT_REPUTATION_2 = 184, // NYI
@@ -1517,7 +1517,7 @@ enum SpellCastResult // 19116
SPELL_FAILED_BUILDING_ACTIVATE_NOT_READY = 257,
SPELL_FAILED_NOT_SOULBOUND = 258,
SPELL_FAILED_RIDING_VEHICLE = 259,
SPELL_FAILED_UNKNOWN = 260, // custom value, default case
SPELL_FAILED_UNKNOWN = 260,
SPELL_CAST_OK = 0xFFFF // custom value, must not be sent to client
};
+69 -56
View File
@@ -141,78 +141,91 @@ WorldPacket const* WorldPackets::Spells::AuraUpdate::Write()
return &_worldPacket;
}
void WorldPackets::Spells::SpellCastRequest::Read()
ByteBuffer& operator>>(ByteBuffer& buffer, WorldPackets::Spells::TargetLocation& location)
{
if (_worldPacket.GetOpcode() == CMSG_PET_CAST_SPELL)
_worldPacket >> PetGuid;
buffer >> location.Transport;
buffer >> location.Location.m_positionX;
buffer >> location.Location.m_positionY;
buffer >> location.Location.m_positionZ;
return buffer;
}
_worldPacket >> CastID;
_worldPacket >> SpellID;
_worldPacket >> Misc;
ByteBuffer& operator>>(ByteBuffer& buffer, WorldPackets::Spells::SpellTargetData& targetData)
{
buffer.ResetBitPos();
_worldPacket.ResetBitPos();
targetData.Flags = buffer.ReadBits(21);
targetData.SrcLocation.HasValue = buffer.ReadBit();
targetData.DstLocation.HasValue = buffer.ReadBit();
targetData.Orientation.HasValue = buffer.ReadBit();
uint32 nameLength = buffer.ReadBits(7);
TargetFlags = _worldPacket.ReadBits(21);
bool HasSrcLocation = _worldPacket.ReadBit();
bool HasDstLocation = _worldPacket.ReadBit();
bool HasOrientation = _worldPacket.ReadBit();
uint32 NameLen = _worldPacket.ReadBits(7);
buffer >> targetData.Unit;
buffer >> targetData.Item;
_worldPacket >> UnitGuid;
_worldPacket >> ItemGuid;
if (targetData.SrcLocation.HasValue)
buffer >> targetData.SrcLocation.Value;
if (HasSrcLocation)
if (targetData.DstLocation.HasValue)
buffer >> targetData.DstLocation.Value;
if (targetData.Orientation.HasValue)
buffer >> targetData.Orientation.Value;
targetData.Name = buffer.ReadString(nameLength);
return buffer;
}
ByteBuffer& operator>>(ByteBuffer& buffer, WorldPackets::Spells::MissileTrajectoryRequest& trajectory)
{
buffer >> trajectory.Pitch;
buffer >> trajectory.Speed;
return buffer;
}
ByteBuffer& operator>>(ByteBuffer& buffer, WorldPackets::Spells::SpellCastRequest& request)
{
buffer >> request.CastID;
buffer >> request.SpellID;
buffer >> request.Misc;
buffer >> request.Target;
buffer >> request.MissileTrajectory;
buffer >> request.Charmer;
buffer.ResetBitPos();
request.SendCastFlags = buffer.ReadBits(5);
request.MoveUpdate.HasValue = buffer.ReadBit();
request.Weight.resize(buffer.ReadBits(2));
if (request.MoveUpdate.HasValue)
buffer >> request.MoveUpdate.Value;
for (WorldPackets::Spells::SpellWeight& weight : request.Weight)
{
_worldPacket >> SrcTransportGuid;
_worldPacket >> SrcPos.m_positionX;
_worldPacket >> SrcPos.m_positionY;
_worldPacket >> SrcPos.m_positionZ;
buffer.ResetBitPos();
weight.Type = buffer.ReadBits(2);
buffer >> weight.ID;
buffer >> weight.Quantity;
}
if (HasDstLocation)
{
_worldPacket >> DstTransportGuid;
_worldPacket >> DstPos.m_positionX;
_worldPacket >> DstPos.m_positionY;
_worldPacket >> DstPos.m_positionZ;
}
return buffer;
}
if (HasOrientation)
_worldPacket >> Orientation;
void WorldPackets::Spells::CastSpell::Read()
{
_worldPacket >> Cast;
}
Name = _worldPacket.ReadString(NameLen);
_worldPacket >> Pitch;
_worldPacket >> Speed;
_worldPacket >> Guid;
_worldPacket.ResetBitPos();
SendCastFlags = _worldPacket.ReadBits(5);
bool HasMoveUpdate = _worldPacket.ReadBit();
uint32 SpellWeightCount = _worldPacket.ReadBits(2);
if (HasMoveUpdate)
{
_worldPacket >> movementInfo;
}
for (uint32 i = 0; i < SpellWeightCount; ++i)
{
_worldPacket.ResetBitPos();
SpellWeight unused;
unused.Type = _worldPacket.ReadBits(2);
_worldPacket >> unused.ID;
_worldPacket >> unused.Quantity;
}
void WorldPackets::Spells::PetCastSpell::Read()
{
_worldPacket >> PetGUID;
_worldPacket >> Cast;
}
ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::TargetLocation const& targetLocation)
{
data << targetLocation.Transport;
// data << targetLocation.Location.PositionXYZStream();
data << targetLocation.Location.m_positionX;
data << targetLocation.Location.m_positionY;
data << targetLocation.Location.m_positionZ;
+50 -54
View File
@@ -155,46 +155,6 @@ namespace WorldPackets
std::vector<AuraInfo> Auras;
};
class SpellCastRequest final : public ClientPacket
{
public:
struct SpellWeight
{
uint32 Type = 0;
int32 ID = 0;
uint32 Quantity = 0;
};
SpellCastRequest(WorldPacket&& packet) : ClientPacket(std::move(packet))
{
ASSERT(packet.GetOpcode() == CMSG_CAST_SPELL || packet.GetOpcode() == CMSG_PET_CAST_SPELL);
}
void Read() override;
ObjectGuid PetGuid;
uint8 CastID = 0;
uint32 SpellID = 0;
uint32 Misc = 0;
uint32 TargetFlags = 0;
ObjectGuid UnitGuid;
ObjectGuid ItemGuid;
ObjectGuid SrcTransportGuid;
ObjectGuid DstTransportGuid;
Position SrcPos;
Position DstPos;
float Orientation = 0.0f;
std::string Name;
float Pitch = 0.0f;
float Speed = 0.0f;
ObjectGuid Guid;
uint32 SendCastFlags = 0;
MovementInfo movementInfo;
};
struct TargetLocation
{
ObjectGuid Transport;
@@ -208,10 +168,59 @@ namespace WorldPackets
ObjectGuid Item;
Optional<TargetLocation> SrcLocation;
Optional<TargetLocation> DstLocation;
Optional<float> Orientation; // Not found in JAM structures
Optional<float> Orientation;
std::string Name;
};
struct MissileTrajectoryRequest
{
float Pitch = 0.0f;
float Speed = 0.0f;
};
struct SpellWeight
{
uint32 Type = 0;
int32 ID = 0;
uint32 Quantity = 0;
};
struct SpellCastRequest
{
uint8 CastID = 0;
uint32 SpellID = 0;
uint32 Misc = 0;
uint8 SendCastFlags = 0;
SpellTargetData Target;
MissileTrajectoryRequest MissileTrajectory;
Optional<MovementInfo> MoveUpdate;
std::vector<SpellWeight> Weight;
ObjectGuid Charmer;
};
class CastSpell final : public ClientPacket
{
public:
CastSpell(WorldPacket&& packet) : ClientPacket(CMSG_CAST_SPELL, std::move(packet)) { }
void Read() override;
SpellCastRequest Cast;
};
class PetCastSpell final : public ClientPacket
{
public:
PetCastSpell(WorldPacket&& packet) : ClientPacket(CMSG_PET_CAST_SPELL, std::move(packet)) { }
void Read() override;
ObjectGuid PetGUID;
SpellCastRequest Cast;
};
struct SpellMissStatus
{
uint8 Reason = 0;
@@ -392,18 +401,5 @@ namespace WorldPackets
}
ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::SpellCastLogData const& spellCastLogData);
ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::TargetLocation const& targetLocation);
ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::SpellTargetData const& spellTargetData);
ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::SpellMissStatus const& spellMissStatus);
ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::SpellPowerData const& spellPowerData);
ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::RuneData const& runeData);
ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::MissileTrajectoryResult const& missileTrajectory);
ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::SpellAmmo const& spellAmmo);
ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::ProjectileVisualData const& projectileVisual);
ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::CreatureImmunities const& immunities);
ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::SpellHealPrediction const& spellPred);
ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::SpellCastData const& spellCastData);
ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::SpellModifierData const& spellModifierData);
ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::SpellModifier const& spellModifier);
#endif // SpellPackets_h__
+2 -2
View File
@@ -255,7 +255,7 @@ void OpcodeTable::Initialize()
DEFINE_OPCODE_HANDLER_OLD(CMSG_CANCEL_TEMP_ENCHANTMENT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleCancelTempEnchantmentOpcode);
DEFINE_HANDLER(CMSG_CANCEL_TRADE, STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT, PROCESS_THREADUNSAFE, WorldPackets::Trade::CancelTrade, &WorldSession::HandleCancelTradeOpcode);
DEFINE_OPCODE_HANDLER_OLD(CMSG_CAN_DUEL, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
DEFINE_HANDLER(CMSG_CAST_SPELL, STATUS_LOGGEDIN, PROCESS_THREADSAFE, WorldPackets::Spells::SpellCastRequest, &WorldSession::HandleCastSpellOpcode);
DEFINE_HANDLER(CMSG_CAST_SPELL, STATUS_LOGGEDIN, PROCESS_THREADSAFE, WorldPackets::Spells::CastSpell, &WorldSession::HandleCastSpellOpcode);
DEFINE_OPCODE_HANDLER_OLD(CMSG_CHALLENGE_MODE_REQUEST_LEADERS, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
DEFINE_OPCODE_HANDLER_OLD(CMSG_CHALLENGE_MODE_REQUEST_MAP_STATS, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
DEFINE_OPCODE_HANDLER_OLD(CMSG_CHANGE_BAG_SLOT_FLAG, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
@@ -650,7 +650,7 @@ void OpcodeTable::Initialize()
DEFINE_OPCODE_HANDLER_OLD(CMSG_PET_BATTLE_REQUEST_WILD, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
DEFINE_OPCODE_HANDLER_OLD(CMSG_PET_BATTLE_SCRIPT_ERROR_NOTIFY, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
DEFINE_OPCODE_HANDLER_OLD(CMSG_PET_CANCEL_AURA, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandlePetCancelAuraOpcode );
DEFINE_HANDLER(CMSG_PET_CAST_SPELL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Spells::SpellCastRequest, &WorldSession::HandlePetCastSpellOpcode);
DEFINE_HANDLER(CMSG_PET_CAST_SPELL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Spells::PetCastSpell, &WorldSession::HandlePetCastSpellOpcode);
DEFINE_OPCODE_HANDLER_OLD(CMSG_PET_LEARN_TALENT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandlePetLearnTalent );
DEFINE_OPCODE_HANDLER_OLD(CMSG_PET_NAME_CACHE, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
DEFINE_OPCODE_HANDLER_OLD(CMSG_PET_NAME_QUERY, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandlePetNameQuery );
+4 -3
View File
@@ -247,7 +247,8 @@ namespace WorldPackets
namespace Spells
{
class CancelAura;
class SpellCastRequest;
class CastSpell;
class PetCastSpell;
class SetActionButton;
}
@@ -947,7 +948,7 @@ class WorldSession
void HandleUseItemOpcode(WorldPacket& recvPacket);
void HandleOpenItemOpcode(WorldPacket& recvPacket);
void HandleCastSpellOpcode(WorldPackets::Spells::SpellCastRequest& castRequest);
void HandleCastSpellOpcode(WorldPackets::Spells::CastSpell& castRequest);
void HandleCancelCastOpcode(WorldPacket& recvPacket);
void HandleCancelAuraOpcode(WorldPackets::Spells::CancelAura& cancelAura);
void HandleCancelGrowthAuraOpcode(WorldPacket& recvPacket);
@@ -1030,7 +1031,7 @@ class WorldSession
void HandlePetRename(WorldPacket& recvData);
void HandlePetCancelAuraOpcode(WorldPacket& recvPacket);
void HandlePetSpellAutocastOpcode(WorldPacket& recvPacket);
void HandlePetCastSpellOpcode(WorldPackets::Spells::SpellCastRequest& castRequest);
void HandlePetCastSpellOpcode(WorldPackets::Spells::PetCastSpell& castRequest);
void HandlePetLearnTalent(WorldPacket& recvPacket);
void HandleSetActionBarToggles(WorldPacket& recvData);
+54 -59
View File
@@ -109,34 +109,43 @@ void SpellDestination::RelocateOffset(Position const& offset)
_position.RelocateOffset(offset);
}
SpellCastTargets::SpellCastTargets() : m_elevation(0), m_speed(0), m_strTarget()
SpellCastTargets::SpellCastTargets() : m_targetMask(0), m_objectTarget(nullptr), m_itemTarget(nullptr),
m_itemTargetEntry(0), m_elevation(0.0f), m_speed(0.0f)
{
m_objectTarget = NULL;
m_itemTarget = NULL;
m_itemTargetEntry = 0;
m_targetMask = 0;
}
SpellCastTargets::SpellCastTargets(Unit* caster, uint32 targetMask, ObjectGuid targetGuid, ObjectGuid itemTargetGuid, ObjectGuid srcTransportGuid, ObjectGuid destTransportGuid, Position srcPos, Position destPos, float elevation, float missileSpeed, std::string targetString) :
m_targetMask(targetMask), m_objectTargetGUID(targetGuid), m_itemTargetGUID(itemTargetGuid), m_elevation(elevation), m_speed(missileSpeed), m_strTarget(targetString)
SpellCastTargets::SpellCastTargets(Unit* caster, WorldPackets::Spells::SpellTargetData const& spellTargetData) :
m_targetMask(spellTargetData.Flags), m_objectTarget(nullptr), m_itemTarget(nullptr),
m_objectTargetGUID(spellTargetData.Unit), m_itemTargetGUID(spellTargetData.Item),
m_itemTargetEntry(0), m_elevation(0.0f), m_speed(0.0f), m_strTarget(spellTargetData.Name)
{
m_objectTarget = NULL;
m_itemTarget = NULL;
m_itemTargetEntry = 0;
if (spellTargetData.SrcLocation.HasValue)
{
m_src._transportGUID = spellTargetData.SrcLocation.Value.Transport;
Position* pos;
if (!m_src._transportGUID.IsEmpty())
pos = &m_src._transportOffset;
else
pos = &m_src._position;
m_src._transportGUID = srcTransportGuid;
if (m_src._transportGUID != ObjectGuid::Empty)
m_src._transportOffset.Relocate(srcPos);
else
m_src._position.Relocate(srcPos);
pos->Relocate(spellTargetData.SrcLocation.Value.Location);
if (spellTargetData.Orientation.HasValue)
pos->SetOrientation(spellTargetData.Orientation.Value);
}
m_dst._transportGUID = destTransportGuid;
if (m_dst._transportGUID != ObjectGuid::Empty)
m_dst._transportOffset.Relocate(destPos);
else
m_dst._position.Relocate(destPos);
if (spellTargetData.DstLocation.HasValue)
{
m_dst._transportGUID = spellTargetData.DstLocation.Value.Transport;
Position* pos;
if (!m_dst._transportGUID.IsEmpty())
pos = &m_dst._transportOffset;
else
pos = &m_dst._position;
pos->Relocate(spellTargetData.DstLocation.Value.Location);
if (spellTargetData.Orientation.HasValue)
pos->SetOrientation(spellTargetData.Orientation.Value);
}
Update(caster);
}
@@ -230,39 +239,6 @@ void SpellCastTargets::Write(WorldPackets::Spells::SpellTargetData& data)
if (m_targetMask & TARGET_FLAG_STRING)
data.Name = m_strTarget;
/*data << uint32(m_targetMask);
if (m_targetMask & (TARGET_FLAG_UNIT | TARGET_FLAG_CORPSE_ALLY | TARGET_FLAG_GAMEOBJECT | TARGET_FLAG_CORPSE_ENEMY | TARGET_FLAG_UNIT_MINIPET))
data << m_objectTargetGUID.WriteAsPacked();
if (m_targetMask & (TARGET_FLAG_ITEM | TARGET_FLAG_TRADE_ITEM))
{
if (m_itemTarget)
data << m_itemTarget->GetPackGUID();
else
data << uint8(0);
}
if (m_targetMask & TARGET_FLAG_SOURCE_LOCATION)
{
data << m_src._transportGUID.WriteAsPacked(); // relative position guid here - transport for example
if (!m_src._transportGUID.IsEmpty())
data << m_src._transportOffset.PositionXYZStream();
else
data << m_src._position.PositionXYZStream();
}
if (m_targetMask & TARGET_FLAG_DEST_LOCATION)
{
data << m_dst._transportGUID.WriteAsPacked(); // relative position guid here - transport for example
if (!m_dst._transportGUID.IsEmpty())
data << m_dst._transportOffset.PositionXYZStream();
else
data << m_dst._position.PositionXYZStream();
}
if (m_targetMask & TARGET_FLAG_STRING)
data << m_strTarget;*/
}
ObjectGuid SpellCastTargets::GetOrigUnitTargetGUID() const
@@ -665,7 +641,7 @@ m_spellValue(new SpellValue(caster->GetMap()->GetDifficultyID(), m_spellInfo)),
m_procEx = 0;
focusObject = NULL;
m_cast_count = 0;
m_glyphIndex = 0;
m_misc.Data = 0;
m_preCastSpell = 0;
m_triggeredByAuraSpell = NULL;
m_spellAura = NULL;
@@ -3744,7 +3720,7 @@ void Spell::SendCastResult(SpellCastResult result)
if (m_caster->ToPlayer()->GetSession()->PlayerLoading()) // don't send cast results at loading time
return;
SendCastResult(m_caster->ToPlayer(), m_spellInfo, m_cast_count, result, m_customError);
SendCastResult(m_caster->ToPlayer(), m_spellInfo, m_cast_count, result, m_customError, SMSG_CAST_FAILED, m_misc.Data);
}
void Spell::SendPetCastResult(SpellCastResult result)
@@ -3756,10 +3732,10 @@ void Spell::SendPetCastResult(SpellCastResult result)
if (!owner || owner->GetTypeId() != TYPEID_PLAYER)
return;
SendCastResult(owner->ToPlayer(), m_spellInfo, m_cast_count, result, SPELL_CUSTOM_ERROR_NONE, SMSG_PET_CAST_FAILED);
SendCastResult(owner->ToPlayer(), m_spellInfo, m_cast_count, result, SPELL_CUSTOM_ERROR_NONE, SMSG_PET_CAST_FAILED, m_misc.Data);
}
void Spell::SendCastResult(Player* caster, SpellInfo const* spellInfo, uint8 cast_count, SpellCastResult result, SpellCustomErrors customError /*= SPELL_CUSTOM_ERROR_NONE*/, OpcodeServer opcode /*= SMSG_CAST_FAILED*/)
void Spell::SendCastResult(Player* caster, SpellInfo const* spellInfo, uint8 cast_count, SpellCastResult result, SpellCustomErrors customError /*= SPELL_CUSTOM_ERROR_NONE*/, OpcodeServer opcode /*= SMSG_CAST_FAILED*/, uint32 misc /*= 0*/)
{
if (result == SPELL_CAST_OK)
return;
@@ -3870,6 +3846,12 @@ void Spell::SendCastResult(Player* caster, SpellInfo const* spellInfo, uint8 cas
packet.FailedArg1 = missingItem; // first missing item
break;
}
case SPELL_FAILED_CANT_UNTALENT:
{
if (TalentEntry const* talent = sTalentStore.LookupEntry(misc))
packet.FailedArg1 = talent->SpellID;
break;
}
// TODO: SPELL_FAILED_NOT_STANDING
default:
break;
@@ -5606,12 +5588,25 @@ SpellCastResult Spell::CheckCast(bool strict)
break;
}
case SPELL_EFFECT_TALENT_SPEC_SELECT:
{
// can't change during already started arena/battleground
if (m_caster->GetTypeId() == TYPEID_PLAYER)
if (Battleground const* bg = m_caster->ToPlayer()->GetBattleground())
if (bg->GetStatus() == STATUS_IN_PROGRESS)
return SPELL_FAILED_NOT_IN_BATTLEGROUND;
break;
}
case SPELL_EFFECT_REMOVE_TALENT:
{
if (m_caster->GetTypeId() != TYPEID_PLAYER)
return SPELL_FAILED_BAD_TARGETS;
TalentEntry const* talent = sTalentStore.LookupEntry(m_misc.TalentId);
if (!talent)
return SPELL_FAILED_DONT_REPORT;
if (m_caster->ToPlayer()->HasSpellCooldown(talent->SpellID))
return SPELL_FAILED_CANT_UNTALENT;
break;
}
default:
break;
}
+11 -3
View File
@@ -107,7 +107,7 @@ class SpellCastTargets
{
public:
SpellCastTargets();
SpellCastTargets(Unit* caster, uint32 targetMask, ObjectGuid targetGuid, ObjectGuid itemTargetGuid, ObjectGuid srcTransportGuid, ObjectGuid destTransportGuid, Position srcPos, Position destPos, float elevation, float missileSpeed, std::string targetString);
SpellCastTargets(Unit* caster, WorldPackets::Spells::SpellTargetData const& spellTargetData);
~SpellCastTargets();
void Read(ByteBuffer& data, Unit* caster);
@@ -364,6 +364,7 @@ class Spell
void EffectGiveCurrency(SpellEffIndex effIndex);
void EffectResurrectWithAura(SpellEffIndex effIndex);
void EffectCreateAreaTrigger(SpellEffIndex effIndex);
void EffectRemoveTalent(SpellEffIndex effIndex);
typedef std::set<Aura*> UsedSpellMods;
@@ -444,7 +445,7 @@ class Spell
void CheckSrc() { if (!m_targets.HasSrc()) m_targets.SetSrc(*m_caster); }
void CheckDst() { if (!m_targets.HasDst()) m_targets.SetDst(*m_caster); }
static void SendCastResult(Player* caster, SpellInfo const* spellInfo, uint8 cast_count, SpellCastResult result, SpellCustomErrors customError = SPELL_CUSTOM_ERROR_NONE, OpcodeServer opcode = SMSG_CAST_FAILED);
static void SendCastResult(Player* caster, SpellInfo const* spellInfo, uint8 cast_count, SpellCastResult result, SpellCustomErrors customError = SPELL_CUSTOM_ERROR_NONE, OpcodeServer opcode = SMSG_CAST_FAILED, uint32 misc = 0);
void SendCastResult(SpellCastResult result);
void SendPetCastResult(SpellCastResult result);
void SendSpellStart();
@@ -475,7 +476,14 @@ class Spell
ObjectGuid m_castItemGUID;
uint32 m_castItemEntry;
uint8 m_cast_count;
uint32 m_glyphIndex;
union
{
// Alternate names for this value
uint32 TalentId;
uint32 GlyphSlot;
uint32 Data;
} m_misc;
uint32 m_preCastSpell;
SpellCastTargets m_targets;
int8 m_comboPointGain;
+28 -11
View File
@@ -250,7 +250,7 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]=
&Spell::EffectUnused, //178 SPELL_EFFECT_178 unused
&Spell::EffectCreateAreaTrigger, //179 SPELL_EFFECT_CREATE_AREATRIGGER
&Spell::EffectNULL, //180 SPELL_EFFECT_UPDATE_AREATRIGGER
&Spell::EffectNULL, //181 SPELL_EFFECT_REMOVE_TALENT
&Spell::EffectRemoveTalent, //181 SPELL_EFFECT_REMOVE_TALENT
&Spell::EffectNULL, //182 SPELL_EFFECT_182
&Spell::EffectNULL, //183 SPELL_EFFECT_183
&Spell::EffectNULL, //184 SPELL_EFFECT_REPUTATION
@@ -3938,7 +3938,7 @@ void Spell::EffectStuck(SpellEffIndex /*effIndex*/)
{
if (!player->GetDeathTimer())
player->RepopAtGraveyard();
return;
}
@@ -3948,7 +3948,7 @@ void Spell::EffectStuck(SpellEffIndex /*effIndex*/)
player->Kill(player);
return;
}
player->TeleportTo(player->m_homebindMapId, player->m_homebindX, player->m_homebindY, player->m_homebindZ, player->GetOrientation(), TELE_TO_SPELL);
// Stuck spell trigger Hearthstone cooldown
@@ -4005,7 +4005,7 @@ void Spell::EffectApplyGlyph(SpellEffIndex /*effIndex*/)
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
if (m_glyphIndex >= MAX_GLYPH_SLOT_INDEX)
if (m_misc.GlyphSlot >= MAX_GLYPH_SLOT_INDEX)
return;
Player* player = m_caster->ToPlayer();
@@ -4014,7 +4014,7 @@ void Spell::EffectApplyGlyph(SpellEffIndex /*effIndex*/)
// glyph sockets level requirement
uint8 minLevel = 0;
switch (m_glyphIndex)
switch (m_misc.GlyphSlot)
{
case 0:
case 1:
@@ -4038,7 +4038,7 @@ void Spell::EffectApplyGlyph(SpellEffIndex /*effIndex*/)
{
if (GlyphPropertiesEntry const* newGlyphProperties = sGlyphPropertiesStore.LookupEntry(newGlyph))
{
if (GlyphSlotEntry const* newGlyphSlot = sGlyphSlotStore.LookupEntry(player->GetGlyphSlot(m_glyphIndex)))
if (GlyphSlotEntry const* newGlyphSlot = sGlyphSlotStore.LookupEntry(player->GetGlyphSlot(m_misc.GlyphSlot)))
{
if (newGlyphProperties->Type != newGlyphSlot->Type)
{
@@ -4048,26 +4048,26 @@ void Spell::EffectApplyGlyph(SpellEffIndex /*effIndex*/)
}
// remove old glyph
if (uint32 oldGlyph = player->GetGlyph(player->GetActiveTalentGroup(), m_glyphIndex))
if (uint32 oldGlyph = player->GetGlyph(player->GetActiveTalentGroup(), m_misc.GlyphSlot))
{
if (GlyphPropertiesEntry const* oldGlyphProperties = sGlyphPropertiesStore.LookupEntry(oldGlyph))
{
player->RemoveAurasDueToSpell(oldGlyphProperties->SpellID);
player->SetGlyph(m_glyphIndex, 0);
player->SetGlyph(m_misc.GlyphSlot, 0);
}
}
player->CastSpell(m_caster, newGlyphProperties->SpellID, true);
player->SetGlyph(m_glyphIndex, newGlyph);
player->SetGlyph(m_misc.GlyphSlot, newGlyph);
player->SendTalentsInfoData();
}
}
else if (uint32 oldGlyph = player->GetGlyph(player->GetActiveTalentGroup(), m_glyphIndex)) // Removing the glyph, get the old one
else if (uint32 oldGlyph = player->GetGlyph(player->GetActiveTalentGroup(), m_misc.GlyphSlot)) // Removing the glyph, get the old one
{
if (GlyphPropertiesEntry const* oldGlyphProperties = sGlyphPropertiesStore.LookupEntry(oldGlyph))
{
player->RemoveAurasDueToSpell(oldGlyphProperties->SpellID);
player->SetGlyph(m_glyphIndex, 0);
player->SetGlyph(m_misc.GlyphSlot, 0);
player->SendTalentsInfoData();
}
}
@@ -5768,3 +5768,20 @@ void Spell::EffectCreateAreaTrigger(SpellEffIndex /*effIndex*/)
if (!areaTrigger->CreateAreaTrigger(sObjectMgr->GetGenerator<HighGuid::AreaTrigger>()->Generate(), triggerEntry, GetCaster(), GetSpellInfo(), pos))
delete areaTrigger;
}
void Spell::EffectRemoveTalent(SpellEffIndex effIndex)
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
TalentEntry const* talent = sTalentStore.LookupEntry(m_misc.TalentId);
if (!talent)
return;
Player* player = unitTarget ? unitTarget->ToPlayer() : nullptr;
if (!player)
return;
player->RemoveTalent(talent);
player->SendTalentsInfoData();
}
+65 -3
View File
@@ -875,9 +875,71 @@ SpellEffectInfo::StaticData SpellEffectInfo::_data[TOTAL_SPELL_EFFECTS] =
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 177 SPELL_EFFECT_177
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 178 SPELL_EFFECT_178
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_DEST}, // 179 SPELL_EFFECT_CREATE_AREATRIGGER
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 180 SPELL_EFFECT_180
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 181 SPELL_EFFECT_181
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 182 SPELL_EFFECT_182
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 180 SPELL_EFFECT_UPDATE_AREATRIGGER
{EFFECT_IMPLICIT_TARGET_CASTER, TARGET_OBJECT_TYPE_UNIT}, // 181 SPELL_EFFECT_REMOVE_TALENT
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 182 SPELL_EFFECT_182
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 183 SPELL_EFFECT_183
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 184 SPELL_EFFECT_REPUTATION_2
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 185 SPELL_EFFECT_185
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 186 SPELL_EFFECT_186
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 187 SPELL_EFFECT_RANDOMIZE_ARCHAEOLOGY_DIGSITES
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 188 SPELL_EFFECT_188
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 189 SPELL_EFFECT_LOOT
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 190 SPELL_EFFECT_190
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 191 SPELL_EFFECT_TELEPORT_TO_DIGSITE
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 192 SPELL_EFFECT_192
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 193 SPELL_EFFECT_193
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 194 SPELL_EFFECT_194
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 195 SPELL_EFFECT_195
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 196 SPELL_EFFECT_196
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 197 SPELL_EFFECT_197
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 198 SPELL_EFFECT_198
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 199 SPELL_EFFECT_199
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 200 SPELL_EFFECT_HEAL_BATTLEPET_PCT
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 201 SPELL_EFFECT_ENABLE_BATTLE_PETS
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 202 SPELL_EFFECT_202
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 203 SPELL_EFFECT_203
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 204 SPELL_EFFECT_204
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 205 SPELL_EFFECT_205
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 206 SPELL_EFFECT_206
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 207 SPELL_EFFECT_207
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 208 SPELL_EFFECT_208
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 209 SPELL_EFFECT_209
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 210 SPELL_EFFECT_210
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 211 SPELL_EFFECT_211
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 212 SPELL_EFFECT_212
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 213 SPELL_EFFECT_213
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 214 SPELL_EFFECT_214
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 215 SPELL_EFFECT_215
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 216 SPELL_EFFECT_216
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 217 SPELL_EFFECT_217
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 218 SPELL_EFFECT_218
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 219 SPELL_EFFECT_219
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 220 SPELL_EFFECT_220
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 221 SPELL_EFFECT_221
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 222 SPELL_EFFECT_222
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 223 SPELL_EFFECT_223
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 224 SPELL_EFFECT_224
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 225 SPELL_EFFECT_225
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 226 SPELL_EFFECT_226
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 227 SPELL_EFFECT_227
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 228 SPELL_EFFECT_228
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 229 SPELL_EFFECT_229
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 230 SPELL_EFFECT_230
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 231 SPELL_EFFECT_231
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 232 SPELL_EFFECT_232
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 233 SPELL_EFFECT_233
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 234 SPELL_EFFECT_234
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 235 SPELL_EFFECT_235
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 236 SPELL_EFFECT_236
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 237 SPELL_EFFECT_237
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 238 SPELL_EFFECT_238
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 239 SPELL_EFFECT_239
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 240 SPELL_EFFECT_240
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 241 SPELL_EFFECT_241
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 242 SPELL_EFFECT_242
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 243 SPELL_EFFECT_243
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 244 SPELL_EFFECT_244
};
SpellInfo::SpellInfo(SpellEntry const* spellEntry, SpellEffectEntryMap effects)
+1
View File
@@ -186,6 +186,7 @@ enum SpellCustomAttributes
SPELL_ATTR0_CU_CONE_LINE = 0x00000004,
SPELL_ATTR0_CU_SHARE_DAMAGE = 0x00000008,
SPELL_ATTR0_CU_NO_INITIAL_THREAT = 0x00000010,
SPELL_ATTR0_CU_IS_TALENT = 0x00000020,
SPELL_ATTR0_CU_AURA_CC = 0x00000040,
SPELL_ATTR0_CU_DIRECT_DAMAGE = 0x00000100,
SPELL_ATTR0_CU_CHARGE = 0x00000200,
+13 -69
View File
@@ -1251,73 +1251,8 @@ void SpellMgr::UnloadSpellInfoChains()
mSpellChains.clear();
}
void SpellMgr::LoadSpellTalentRanks()
{
/* TODO: 6.x remove this
// cleanup core data before reload - remove reference to ChainNode from SpellInfo
UnloadSpellInfoChains();
for (uint32 i = 0; i < sTalentStore.GetNumRows(); ++i)
{
TalentEntry const* talentInfo = sTalentStore.LookupEntry(i);
if (!talentInfo)
continue;
SpellInfo const* lastSpell = NULL;
for (uint8 rank = MAX_TALENT_RANK - 1; rank > 0; --rank)
{
if (talentInfo->RankID[rank])
{
lastSpell = GetSpellInfo(talentInfo->RankID[rank]);
break;
}
}
if (!lastSpell)
continue;
SpellInfo const* firstSpell = GetSpellInfo(talentInfo->RankID[0]);
if (!firstSpell)
{
TC_LOG_ERROR("spells", "SpellMgr::LoadSpellTalentRanks: First Rank Spell %u for TalentEntry %u does not exist.", talentInfo->RankID[0], i);
continue;
}
SpellInfo const* prevSpell = NULL;
for (uint8 rank = 0; rank < MAX_TALENT_RANK; ++rank)
{
uint32 spellId = talentInfo->RankID[rank];
if (!spellId)
break;
SpellInfo const* currentSpell = GetSpellInfo(spellId);
if (!currentSpell)
{
TC_LOG_ERROR("spells", "SpellMgr::LoadSpellTalentRanks: Spell %u (Rank: %u) for TalentEntry %u does not exist.", spellId, rank + 1, i);
break;
}
SpellChainNode node;
node.first = firstSpell;
node.last = lastSpell;
node.rank = rank + 1;
node.prev = prevSpell;
node.next = node.rank < MAX_TALENT_RANK ? GetSpellInfo(talentInfo->RankID[node.rank]) : NULL;
mSpellChains[spellId] = node;
mSpellInfoMap[spellId]->ChainEntry = &mSpellChains[spellId];
prevSpell = currentSpell;
}
}*/
}
void SpellMgr::LoadSpellRanks()
{
// cleanup data and load spell ranks for talents from dbc
LoadSpellTalentRanks();
uint32 oldMSTime = getMSTime();
// 0 1 2
@@ -1553,7 +1488,8 @@ void SpellMgr::LoadSpellLearnSpells()
node.active = fields[2].GetBool();
node.autoLearned = false;
if (!GetSpellInfo(spell_id))
SpellInfo const* spellInfo = GetSpellInfo(spell_id);
if (!spellInfo)
{
TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_learn_spell` does not exist", spell_id);
continue;
@@ -1565,7 +1501,7 @@ void SpellMgr::LoadSpellLearnSpells()
continue;
}
if (GetTalentBySpellID(node.spell))
if (spellInfo->HasAttribute(SPELL_ATTR0_CU_IS_TALENT))
{
TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_learn_spell` attempt learning talent spell %u, skipped", spell_id, node.spell);
continue;
@@ -1603,7 +1539,7 @@ void SpellMgr::LoadSpellLearnSpells()
// talent or passive spells or skill-step spells auto-cast and not need dependent learning,
// pet teaching spells must not be dependent learning (cast)
// other required explicit dependent learning
dbc_node.autoLearned = effect->TargetA.GetTarget() == TARGET_UNIT_PET || GetTalentBySpellID(spell) || entry->IsPassive() || entry->HasEffect(SPELL_EFFECT_SKILL_STEP);
dbc_node.autoLearned = effect->TargetA.GetTarget() == TARGET_UNIT_PET || entry->HasAttribute(SPELL_ATTR0_CU_IS_TALENT) || entry->IsPassive() || entry->HasEffect(SPELL_EFFECT_SKILL_STEP);
SpellLearnSpellMapBounds db_node_bounds = dbSpellLearnSpells.equal_range(spell);
@@ -1654,7 +1590,7 @@ void SpellMgr::LoadSpellLearnSpells()
{
if (itr->second.spell == mastery)
{
TC_LOG_ERROR("sql.sql", "Found redundant record (entry: %u, SpellID: %u) in `spell_learn_spell`, spell added automatically as mastery learned spell from TalentTab.dbc", masteryMainSpell, mastery);
TC_LOG_ERROR("sql.sql", "Found redundant record (entry: %u, SpellID: %u) in `spell_learn_spell`, spell added automatically as mastery learned spell from ChrSpecialization.dbc", masteryMainSpell, mastery);
found = true;
break;
}
@@ -2873,6 +2809,11 @@ void SpellMgr::LoadSpellInfoCustomAttributes()
TC_LOG_INFO("server.loading", ">> Loaded %u spell custom attributes from DB in %u ms", count, GetMSTimeDiffToNow(oldMSTime2));
}
std::set<uint32> talentSpells;
for (uint32 i = 0; i < sTalentStore.GetNumRows(); ++i)
if (TalentEntry const* talentInfo = sTalentStore.LookupEntry(i))
talentSpells.insert(talentInfo->SpellID);
for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i)
{
spellInfo = mSpellInfoMap[i];
@@ -2986,6 +2927,9 @@ void SpellMgr::LoadSpellInfoCustomAttributes()
if (spellInfo->SpellVisual[0] == 3879)
spellInfo->AttributesCu |= SPELL_ATTR0_CU_CONE_BACK;
if (talentSpells.count(spellInfo->Id))
spellInfo->AttributesCu |= SPELL_ATTR0_CU_IS_TALENT;
switch (spellInfo->SpellFamilyName)
{
case SPELLFAMILY_WARRIOR:
-1
View File
@@ -695,7 +695,6 @@ class SpellMgr
// Loading data at server startup
void UnloadSpellInfoChains();
void LoadSpellTalentRanks();
void LoadSpellRanks();
void LoadSpellRequired();
void LoadSpellLearnSkills();
@@ -22,6 +22,7 @@
#include "World.h"
#include "Database/DatabaseEnv.h"
#include "SpellMgr.h"
#include "SpellInfo.h"
#include "DBCStores.h"
void CharacterDatabaseCleaner::CleanDatabase()
@@ -128,7 +129,8 @@ void CharacterDatabaseCleaner::CleanCharacterSkills()
bool CharacterDatabaseCleaner::SpellCheck(uint32 spell_id)
{
return sSpellMgr->GetSpellInfo(spell_id) && GetTalentBySpellID(spell_id) != nullptr;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell_id);
return spellInfo && !spellInfo->HasAttribute(SPELL_ATTR0_CU_IS_TALENT);
}
void CharacterDatabaseCleaner::CleanCharacterSpell()
+1 -1
View File
@@ -204,7 +204,7 @@ public:
// learn highest rank of talent and learn all non-talent spell ranks (recursive by tree)
player->LearnSpellHighestRank(talentInfo->SpellID);
player->AddTalent(talentInfo->SpellID, player->GetActiveTalentGroup(), true);
player->AddTalent(talentInfo, player->GetActiveTalentGroup(), true);
}
handler->SendSysMessage(LANG_COMMAND_LEARN_CLASS_TALENTS);
+1 -1
View File
@@ -435,11 +435,11 @@ public:
handler->PSendSysMessage(LANG_COMMAND_TARGET_LISTAURAS, auras.size());
for (Unit::AuraApplicationMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
{
bool talent = (GetTalentBySpellID(itr->second->GetBase()->GetId()) != nullptr);
AuraApplication const* aurApp = itr->second;
Aura const* aura = aurApp->GetBase();
char const* name = aura->GetSpellInfo()->SpellName;
bool talent = aura->GetSpellInfo()->HasAttribute(SPELL_ATTR0_CU_IS_TALENT);
std::ostringstream ss_name;
ss_name << "|cffffffff|Hspell:" << aura->GetId() << "|h[" << name << "]|h|r";
+2 -2
View File
@@ -814,7 +814,7 @@ public:
SpellInfo const* learnSpellInfo = effect ? sSpellMgr->GetSpellInfo(effect->TriggerSpell) : NULL;
bool talent = (GetTalentBySpellID(id) != nullptr);
bool talent = spellInfo->HasAttribute(SPELL_ATTR0_CU_IS_TALENT);
bool passive = spellInfo->IsPassive();
bool active = target && target->HasAura(id);
@@ -886,7 +886,7 @@ public:
SpellInfo const* learnSpellInfo = effect ? sSpellMgr->GetSpellInfo(effect->TriggerSpell) : NULL;
bool talent = (GetTalentBySpellID(id) != nullptr);
bool talent = spellInfo->HasAttribute(SPELL_ATTR0_CU_IS_TALENT);
bool passive = spellInfo->IsPassive();
bool active = target && target->HasAura(id);
+1 -1
View File
@@ -164,7 +164,7 @@ class spell_dru_eclipse_energize : public SpellScriptLoader
Player* caster = GetCaster()->ToPlayer();
// No boomy, no deal.
if (caster->GetActiveTalentSpec() != TALENT_SPEC_DRUID_BALANCE)
if (caster->GetSpecId(caster->GetActiveTalentGroup()) != TALENT_SPEC_DRUID_BALANCE)
return;
switch (GetSpellInfo()->Id)