aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/game/Battlegrounds/Battleground.cpp2
-rw-r--r--src/server/game/DataStores/DBCStores.cpp56
-rw-r--r--src/server/game/DataStores/DBCStores.h15
-rw-r--r--src/server/game/DataStores/DBCStructure.h1
-rw-r--r--src/server/game/Entities/Player/Player.cpp574
-rw-r--r--src/server/game/Entities/Player/Player.h47
-rw-r--r--src/server/game/Entities/Unit/StatSystem.cpp2
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp15
-rw-r--r--src/server/game/Entities/Unit/Unit.h1
-rw-r--r--src/server/game/Handlers/PetHandler.cpp2
-rw-r--r--src/server/game/Handlers/SkillHandler.cpp2
-rw-r--r--src/server/game/Handlers/SpellHandler.cpp78
-rw-r--r--src/server/game/Miscellaneous/SharedDefines.h4
-rw-r--r--src/server/game/Server/Packets/SpellPackets.cpp119
-rw-r--r--src/server/game/Server/Packets/SpellPackets.h104
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp4
-rw-r--r--src/server/game/Server/WorldSession.h7
-rw-r--r--src/server/game/Spells/Spell.cpp113
-rw-r--r--src/server/game/Spells/Spell.h14
-rw-r--r--src/server/game/Spells/SpellEffects.cpp39
-rw-r--r--src/server/game/Spells/SpellInfo.cpp68
-rw-r--r--src/server/game/Spells/SpellInfo.h1
-rw-r--r--src/server/game/Spells/SpellMgr.cpp82
-rw-r--r--src/server/game/Spells/SpellMgr.h1
-rw-r--r--src/server/game/Tools/CharacterDatabaseCleaner.cpp4
-rw-r--r--src/server/scripts/Commands/cs_learn.cpp2
-rw-r--r--src/server/scripts/Commands/cs_list.cpp2
-rw-r--r--src/server/scripts/Commands/cs_lookup.cpp4
-rw-r--r--src/server/scripts/Spells/spell_druid.cpp2
29 files changed, 698 insertions, 667 deletions
diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp
index 0bba2c84827..e870eadec1e 100644
--- a/src/server/game/Battlegrounds/Battleground.cpp
+++ b/src/server/game/Battlegrounds/Battleground.cpp
@@ -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;
diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp
index 7532a6f8c75..bd087be84dd 100644
--- a/src/server/game/DataStores/DBCStores.cpp
+++ b/src/server/game/DataStores/DBCStores.cpp
@@ -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;
+}
diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h
index 8a411300a79..edbb5aa55bf 100644
--- a/src/server/game/DataStores/DBCStores.h
+++ b/src/server/game/DataStores/DBCStores.h
@@ -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;
diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h
index 69739a32854..0bad082801d 100644
--- a/src/server/game/DataStores/DBCStructure.h
+++ b/src/server/game/DataStores/DBCStructure.h
@@ -1788,6 +1788,7 @@ struct SummonPropertiesEntry
};
#define MAX_TALENT_TIERS 7
+#define MAX_TALENT_COLUMNS 3
struct TalentEntry
{
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 0c3d928f055..d373e46fc41 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -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;
+ 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();
- (*GetTalentMap(spec))[talentId] = newtalent;
+ newtalent->state = state;
+ newtalent->spec = spec;
- return true;
- }
- //else
- // TC_LOG_ERROR("spells", "Player::addTalent: Talent %u not found in talent store.", talentId);
+ (*GetTalentMap(spec))[talent->ID] = newtalent;
}
- else
- itr->second->state = PLAYERSPELL_UNCHANGED;
- return false;
+ 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 (SpellLearnSkillNode const* spellLearnSkill = sSpellMgr->GetSpellLearnSkill(spellId))
+ {
+ uint32 skill_value = GetPureSkillValue(spellLearnSkill->skill);
+ uint32 skill_max_value = GetPureMaxSkillValue(spellLearnSkill->skill);
- if (skill_value < spellLearnSkill->value)
- skill_value = spellLearnSkill->value;
+ if (skill_value < spellLearnSkill->value)
+ skill_value = spellLearnSkill->value;
- uint32 new_skill_max_value = spellLearnSkill->maxvalue == 0 ? maxskill : spellLearnSkill->maxvalue;
+ uint32 new_skill_max_value = spellLearnSkill->maxvalue == 0 ? GetMaxSkillValueForLevel() : spellLearnSkill->maxvalue;
- if (skill_max_value < new_skill_max_value)
- skill_max_value = new_skill_max_value;
+ 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)
+ SetSkill(spellLearnSkill->skill, spellLearnSkill->step, skill_value, skill_max_value);
+ }
+ else
{
- SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->SkillLine);
- if (!pSkill)
- continue;
-
- if (HasSkill(pSkill->ID))
- continue;
-
- SkillRaceClassInfoEntry const* rcEntry = GetSkillRaceClassInfo(pSkill->ID, getRace(), getClass());
- if (!rcEntry)
- continue;
-
- 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)))
+ // 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);
-
- 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;
-
- SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(talentInfo->SpellID);
- if (!spellEntry)
- continue;
+ TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
+ if (!talentInfo)
+ continue;
- RemoveSpell(spellEntry->Id, false);
+ // 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;
- // 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);
+ // skip non-existant talent ranks
+ if (talentInfo->SpellID == 0)
+ continue;
- 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);
- }
- }
+ // 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();
+}
- SaveToDB();
+void Player::ResetTalentSpecialization()
+{
+ if (!GetSpecId(GetActiveTalentGroup()))
+ return;
- SendTalentsInfoData();
-}
+ 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);
-void Player::AddKnownCurrency(uint32 itemId)
-{
- if (CurrencyTypesEntry const* ctEntry = sCurrencyTypesStore.LookupEntry(itemId))
- SetFlag64(0, (1LL << (ctEntry->ID-1)));
+ 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;
+
+ if (talentInfo->SpellID == 0)
continue;
- SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(talentInfo->SpellID);
- if (!spellEntry)
+ 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);
+ }
+ }
+}
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index e72a22fefbb..a22cd007ee0 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -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; }
diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp
index cbb62fcad73..237f963be2b 100644
--- a/src/server/game/Entities/Unit/StatSystem.cpp
+++ b/src/server/game/Entities/Unit/StatSystem.cpp
@@ -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;
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 1a67ac2609b..59822acb7ad 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -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;
+}
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index a42d9de5a63..0311b67befd 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -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];
diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp
index 9a132bb1ae2..c1ac3cb18b2 100644
--- a/src/server/game/Handlers/PetHandler.cpp
+++ b/src/server/game/Handlers/PetHandler.cpp
@@ -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");
/*
diff --git a/src/server/game/Handlers/SkillHandler.cpp b/src/server/game/Handlers/SkillHandler.cpp
index d7cf1226088..a83ce9f996d 100644
--- a/src/server/game/Handlers/SkillHandler.cpp
+++ b/src/server/game/Handlers/SkillHandler.cpp
@@ -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();
diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp
index e1459f696f9..55574585246 100644
--- a/src/server/game/Handlers/SpellHandler.cpp
+++ b/src/server/game/Handlers/SpellHandler.cpp
@@ -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);
}
diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h
index 122181fb313..3f8184a7d85 100644
--- a/src/server/game/Miscellaneous/SharedDefines.h
+++ b/src/server/game/Miscellaneous/SharedDefines.h
@@ -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
};
diff --git a/src/server/game/Server/Packets/SpellPackets.cpp b/src/server/game/Server/Packets/SpellPackets.cpp
index 053bb6c504f..6f93fec73f2 100644
--- a/src/server/game/Server/Packets/SpellPackets.cpp
+++ b/src/server/game/Server/Packets/SpellPackets.cpp
@@ -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;
-
- _worldPacket >> CastID;
- _worldPacket >> SpellID;
- _worldPacket >> Misc;
-
- _worldPacket.ResetBitPos();
-
- TargetFlags = _worldPacket.ReadBits(21);
- bool HasSrcLocation = _worldPacket.ReadBit();
- bool HasDstLocation = _worldPacket.ReadBit();
- bool HasOrientation = _worldPacket.ReadBit();
- uint32 NameLen = _worldPacket.ReadBits(7);
-
- _worldPacket >> UnitGuid;
- _worldPacket >> ItemGuid;
+ buffer >> location.Transport;
+ buffer >> location.Location.m_positionX;
+ buffer >> location.Location.m_positionY;
+ buffer >> location.Location.m_positionZ;
+ return buffer;
+}
- if (HasSrcLocation)
- {
- _worldPacket >> SrcTransportGuid;
- _worldPacket >> SrcPos.m_positionX;
- _worldPacket >> SrcPos.m_positionY;
- _worldPacket >> SrcPos.m_positionZ;
- }
+ByteBuffer& operator>>(ByteBuffer& buffer, WorldPackets::Spells::SpellTargetData& targetData)
+{
+ buffer.ResetBitPos();
- if (HasDstLocation)
- {
- _worldPacket >> DstTransportGuid;
- _worldPacket >> DstPos.m_positionX;
- _worldPacket >> DstPos.m_positionY;
- _worldPacket >> DstPos.m_positionZ;
- }
+ 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);
- if (HasOrientation)
- _worldPacket >> Orientation;
+ buffer >> targetData.Unit;
+ buffer >> targetData.Item;
- Name = _worldPacket.ReadString(NameLen);
+ if (targetData.SrcLocation.HasValue)
+ buffer >> targetData.SrcLocation.Value;
- _worldPacket >> Pitch;
- _worldPacket >> Speed;
+ if (targetData.DstLocation.HasValue)
+ buffer >> targetData.DstLocation.Value;
- _worldPacket >> Guid;
+ if (targetData.Orientation.HasValue)
+ buffer >> targetData.Orientation.Value;
- _worldPacket.ResetBitPos();
+ targetData.Name = buffer.ReadString(nameLength);
- SendCastFlags = _worldPacket.ReadBits(5);
+ return buffer;
+}
- bool HasMoveUpdate = _worldPacket.ReadBit();
- uint32 SpellWeightCount = _worldPacket.ReadBits(2);
+ByteBuffer& operator>>(ByteBuffer& buffer, WorldPackets::Spells::MissileTrajectoryRequest& trajectory)
+{
+ buffer >> trajectory.Pitch;
+ buffer >> trajectory.Speed;
+ return buffer;
+}
- if (HasMoveUpdate)
+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 >> movementInfo;
+ buffer.ResetBitPos();
+ weight.Type = buffer.ReadBits(2);
+ buffer >> weight.ID;
+ buffer >> weight.Quantity;
}
- for (uint32 i = 0; i < SpellWeightCount; ++i)
- {
- _worldPacket.ResetBitPos();
- SpellWeight unused;
- unused.Type = _worldPacket.ReadBits(2);
- _worldPacket >> unused.ID;
- _worldPacket >> unused.Quantity;
- }
+ return buffer;
+}
+
+void WorldPackets::Spells::CastSpell::Read()
+{
+ _worldPacket >> Cast;
+}
+
+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;
diff --git a/src/server/game/Server/Packets/SpellPackets.h b/src/server/game/Server/Packets/SpellPackets.h
index fa7b8e0b04a..fa5af7b2882 100644
--- a/src/server/game/Server/Packets/SpellPackets.h
+++ b/src/server/game/Server/Packets/SpellPackets.h
@@ -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__
diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp
index 49649c2df0c..988a858e83b 100644
--- a/src/server/game/Server/Protocol/Opcodes.cpp
+++ b/src/server/game/Server/Protocol/Opcodes.cpp
@@ -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 );
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index 5c9c05bbe92..291ba0a9f66 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -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);
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index d729d76a1d5..173c9a027b2 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -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;
}
diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h
index a8ff53b5cb1..48a62ab0e10 100644
--- a/src/server/game/Spells/Spell.h
+++ b/src/server/game/Spells/Spell.h
@@ -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;
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index 64e74424f70..7c5ab810ce4 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -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();
+}
diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp
index 1f6b5238137..a70c73de6df 100644
--- a/src/server/game/Spells/SpellInfo.cpp
+++ b/src/server/game/Spells/SpellInfo.cpp
@@ -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)
diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h
index 5b9cfacb56b..b585588726e 100644
--- a/src/server/game/Spells/SpellInfo.h
+++ b/src/server/game/Spells/SpellInfo.h
@@ -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,
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp
index d8bfed43c2b..85a04c6bf4e 100644
--- a/src/server/game/Spells/SpellMgr.cpp
+++ b/src/server/game/Spells/SpellMgr.cpp
@@ -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:
diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h
index 74423f6e4f3..36ffe105c54 100644
--- a/src/server/game/Spells/SpellMgr.h
+++ b/src/server/game/Spells/SpellMgr.h
@@ -695,7 +695,6 @@ class SpellMgr
// Loading data at server startup
void UnloadSpellInfoChains();
- void LoadSpellTalentRanks();
void LoadSpellRanks();
void LoadSpellRequired();
void LoadSpellLearnSkills();
diff --git a/src/server/game/Tools/CharacterDatabaseCleaner.cpp b/src/server/game/Tools/CharacterDatabaseCleaner.cpp
index bd1d1897d5b..e065439b5ab 100644
--- a/src/server/game/Tools/CharacterDatabaseCleaner.cpp
+++ b/src/server/game/Tools/CharacterDatabaseCleaner.cpp
@@ -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()
diff --git a/src/server/scripts/Commands/cs_learn.cpp b/src/server/scripts/Commands/cs_learn.cpp
index e607bd3f082..7a34ba55110 100644
--- a/src/server/scripts/Commands/cs_learn.cpp
+++ b/src/server/scripts/Commands/cs_learn.cpp
@@ -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);
diff --git a/src/server/scripts/Commands/cs_list.cpp b/src/server/scripts/Commands/cs_list.cpp
index c06dba9dc4e..0ee987aadf6 100644
--- a/src/server/scripts/Commands/cs_list.cpp
+++ b/src/server/scripts/Commands/cs_list.cpp
@@ -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";
diff --git a/src/server/scripts/Commands/cs_lookup.cpp b/src/server/scripts/Commands/cs_lookup.cpp
index 626a0cbb086..41bd04c5dca 100644
--- a/src/server/scripts/Commands/cs_lookup.cpp
+++ b/src/server/scripts/Commands/cs_lookup.cpp
@@ -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);
diff --git a/src/server/scripts/Spells/spell_druid.cpp b/src/server/scripts/Spells/spell_druid.cpp
index 669fb8838fb..dd002dedee5 100644
--- a/src/server/scripts/Spells/spell_druid.cpp
+++ b/src/server/scripts/Spells/spell_druid.cpp
@@ -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)