diff options
Diffstat (limited to 'src')
64 files changed, 1536 insertions, 899 deletions
diff --git a/src/server/game/Battlefield/Battlefield.cpp b/src/server/game/Battlefield/Battlefield.cpp index 54c43673411..c9f2492617a 100644 --- a/src/server/game/Battlefield/Battlefield.cpp +++ b/src/server/game/Battlefield/Battlefield.cpp @@ -450,6 +450,25 @@ void Battlefield::SendUpdateWorldState(uint32 variable, uint32 value, bool hidde BroadcastPacketToZone(worldstate.Write()); } +void Battlefield::AddCapturePoint(BfCapturePoint* cp) +{ + Battlefield::BfCapturePointMap::iterator i = m_capturePoints.find(cp->GetCapturePointEntry()); + if (i != m_capturePoints.end()) + { + TC_LOG_ERROR("bg.battlefield", "Battlefield::AddCapturePoint: CapturePoint %s already exists!", cp->GetCapturePointEntry()); + delete i->second; + } + m_capturePoints[cp->GetCapturePointEntry()] = cp; +} + +BfCapturePoint* Battlefield::GetCapturePoint(uint32 entry) const +{ + Battlefield::BfCapturePointMap::const_iterator itr = m_capturePoints.find(entry); + if (itr != m_capturePoints.end()) + return itr->second; + return nullptr; +} + void Battlefield::RegisterZone(uint32 zoneId) { sBattlefieldMgr->AddZone(zoneId, this); @@ -913,10 +932,11 @@ bool BfCapturePoint::SetCapturePointData(GameObject* capturePoint) TC_LOG_DEBUG("bg.battlefield", "Creating capture point %u", capturePoint->GetEntry()); m_capturePointGUID = capturePoint->GetGUID(); + m_capturePointEntry = capturePoint->GetEntry(); // check info existence GameObjectTemplate const* goinfo = capturePoint->GetGOInfo(); - if (goinfo->type != GAMEOBJECT_TYPE_CAPTURE_POINT) + if (goinfo->type != GAMEOBJECT_TYPE_CONTROL_ZONE) { TC_LOG_ERROR("misc", "OutdoorPvP: GO %u is not capture point!", capturePoint->GetEntry()); return false; @@ -927,7 +947,7 @@ bool BfCapturePoint::SetCapturePointData(GameObject* capturePoint) m_maxSpeed = m_maxValue / (goinfo->controlZone.minTime ? goinfo->controlZone.minTime : 60); m_neutralValuePct = goinfo->controlZone.neutralPercent; m_minValue = m_maxValue * goinfo->controlZone.neutralPercent / 100; - m_capturePointEntry = capturePoint->GetEntry(); + if (m_team == TEAM_ALLIANCE) { m_value = m_maxValue; diff --git a/src/server/game/Battlefield/Battlefield.h b/src/server/game/Battlefield/Battlefield.h index 2ea1f100ee2..3fcfc22b941 100644 --- a/src/server/game/Battlefield/Battlefield.h +++ b/src/server/game/Battlefield/Battlefield.h @@ -402,15 +402,8 @@ class Battlefield : public ZoneScript void BroadcastPacketToWar(WorldPacket const* data) const; // CapturePoint system - void AddCapturePoint(BfCapturePoint* cp) { m_capturePoints[cp->GetCapturePointEntry()] = cp; } - - BfCapturePoint* GetCapturePoint(uint32 lowguid) const - { - Battlefield::BfCapturePointMap::const_iterator itr = m_capturePoints.find(lowguid); - if (itr != m_capturePoints.end()) - return itr->second; - return NULL; - } + void AddCapturePoint(BfCapturePoint* cp); + BfCapturePoint* GetCapturePoint(uint32 entry) const; void RegisterZone(uint32 zoneid); bool HasPlayer(Player* player) const; 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/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 78efc590566..bb4bed9f79a 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -1315,8 +1315,8 @@ void GameObject::Use(Unit* user) { if (Player* ChairUser = ObjectAccessor::FindPlayer(itr->second)) { - if (ChairUser->IsSitState() && ChairUser->getStandState() != UNIT_STAND_STATE_SIT && ChairUser->GetExactDist2d(x_i, y_i) < 0.1f) - continue; // This seat is already occupied by ChairUser. NOTE: Not sure if the ChairUser->getStandState() != UNIT_STAND_STATE_SIT check is required. + if (ChairUser->IsSitState() && ChairUser->GetStandState() != UNIT_STAND_STATE_SIT && ChairUser->GetExactDist2d(x_i, y_i) < 0.1f) + continue; // This seat is already occupied by ChairUser. NOTE: Not sure if the ChairUser->GetStandState() != UNIT_STAND_STATE_SIT check is required. else itr->second.Clear(); // This seat is unoccupied. } @@ -1345,7 +1345,7 @@ void GameObject::Use(Unit* user) { itr->second = player->GetGUID(); //this slot in now used by player player->TeleportTo(GetMapId(), x_lowest, y_lowest, GetPositionZ(), GetOrientation(), TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET); - player->SetStandState(UNIT_STAND_STATE_SIT_LOW_CHAIR + info->chair.chairheight); + player->SetStandState(UnitStandStateType(UNIT_STAND_STATE_SIT_LOW_CHAIR + info->chair.chairheight)); return; } } @@ -1773,7 +1773,7 @@ void GameObject::Use(Unit* user) WorldPacket data(SMSG_ENABLE_BARBER_SHOP, 0); player->SendDirectMessage(&data); - player->SetStandState(UNIT_STAND_STATE_SIT_LOW_CHAIR+info->barberChair.chairheight); + player->SetStandState(UnitStandStateType(UNIT_STAND_STATE_SIT_LOW_CHAIR + info->barberChair.chairheight)); return; } default: diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index 239324c572d..ff89e3f7cc0 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -776,11 +776,11 @@ union GameObjectValue { uint32 MaxOpens; } FishingHole; - //29 GAMEOBJECT_TYPE_CAPTURE_POINT + //29 GAMEOBJECT_TYPE_CONTROL_ZONE struct { OPvPCapturePoint *OPvPObj; - } CapturePoint; + } ControlZone; //33 GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING struct { diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index bd084491dee..d373e46fc41 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -33,6 +33,7 @@ #include "CharacterPackets.h" #include "TalentPackets.h" #include "Chat.h" +#include "CombatPackets.h" #include "Common.h" #include "ConditionMgr.h" #include "CreatureAI.h" @@ -2997,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 { @@ -3012,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 @@ -3293,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; } @@ -3323,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*/) @@ -3452,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()) { @@ -3568,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); @@ -3589,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); } } } @@ -3733,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; @@ -3789,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 @@ -3915,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); @@ -4117,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 @@ -4143,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); @@ -4205,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) @@ -4298,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 @@ -5950,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)); } } @@ -8414,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; @@ -8442,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; @@ -17192,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(); @@ -18724,7 +18675,7 @@ bool Player::Satisfy(AccessRequirement const* ar, uint32 target_map, bool report else if (mapDiff->HasMessage()) // if (missingAchievement) covered by this case SendTransferAborted(target_map, TRANSFER_ABORT_DIFFICULTY, target_difficulty); else if (missingItem) - GetSession()->SendAreaTriggerMessage(GetSession()->GetTrinityString(LANG_LEVEL_MINREQUIRED_AND_ITEM), LevelMin, sObjectMgr->GetItemTemplate(missingItem)->GetName(GetSession()->GetSessionDbcLocale())); + GetSession()->SendAreaTriggerMessage(GetSession()->GetTrinityString(LANG_LEVEL_MINREQUIRED_AND_ITEM), LevelMin, ASSERT_NOTNULL(sObjectMgr->GetItemTemplate(missingItem))->GetName(GetSession()->GetSessionDbcLocale())); else if (LevelMin) GetSession()->SendAreaTriggerMessage(GetSession()->GetTrinityString(LANG_LEVEL_MINREQUIRED), LevelMin); } @@ -18911,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); @@ -19048,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); @@ -19937,12 +19888,6 @@ bool Player::CanSpeak() const /*** LOW LEVEL FUNCTIONS:Notifiers ***/ /*********************************************************/ -void Player::SendAttackSwingNotInRange() -{ - WorldPacket data(SMSG_ATTACKSWING_NOTINRANGE, 0); - GetSession()->SendPacket(&data); -} - void Player::SavePositionInDB(WorldLocation const& loc, uint16 zoneId, ObjectGuid guid, SQLTransaction& trans) { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHARACTER_POSITION); @@ -19971,26 +19916,32 @@ void Player::SetUInt32ValueInArray(Tokenizer& Tokenizer, uint16 index, uint32 va void Player::SendAttackSwingDeadTarget() { - WorldPacket data(SMSG_ATTACKSWING_DEADTARGET, 0); - GetSession()->SendPacket(&data); + WorldPackets::Combat::AttackSwingError packet(ATTACKSWINGERR_DEADTARGET); + GetSession()->SendPacket(packet.Write()); } void Player::SendAttackSwingCantAttack() { - WorldPacket data(SMSG_ATTACKSWING_CANT_ATTACK, 0); - GetSession()->SendPacket(&data); + WorldPackets::Combat::AttackSwingError packet(ATTACKSWINGERR_CANT_ATTACK); + GetSession()->SendPacket(packet.Write()); } -void Player::SendAttackSwingCancelAttack() +void Player::SendAttackSwingNotInRange() { - WorldPacket data(SMSG_CANCEL_COMBAT, 0); - GetSession()->SendPacket(&data); + WorldPackets::Combat::AttackSwingError packet(ATTACKSWINGERR_NOTINRANGE); + GetSession()->SendPacket(packet.Write()); } void Player::SendAttackSwingBadFacingAttack() { - WorldPacket data(SMSG_ATTACKSWING_BADFACING, 0); - GetSession()->SendPacket(&data); + WorldPackets::Combat::AttackSwingError packet(ATTACKSWINGERR_BADFACING); + GetSession()->SendPacket(packet.Write()); +} + +void Player::SendAttackSwingCancelAttack() +{ + WorldPackets::Combat::CancelCombat packet; + GetSession()->SendPacket(packet.Write()); } void Player::SendAutoRepeatCancel(Unit* target) @@ -25403,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); @@ -25451,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) @@ -25576,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) { @@ -25599,9 +25562,6 @@ void Player::SendTalentsInfoData() continue; } - if (!HasTalent(itr->first, i)) - continue; - groupInfoPkt.TalentIDs.push_back(uint16(itr->first)); } @@ -25918,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()); } } @@ -25929,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; } } } @@ -25988,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)) @@ -26025,57 +25996,76 @@ void Player::ActivateTalentGroup(uint8 group) for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId) { TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId); - if (!talentInfo) continue; // unlearn only talents for character class // some spell learned by one class as normal spells or know at creation but another class learn it as talent, // to prevent unexpected lost normal learned spell skip another class talents - if (getClass() != talentInfo->ClassID) + if (talentInfo->ClassID != getClass()) continue; - SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(talentInfo->SpellID); - if (!spellEntry) + if (talentInfo->SpellID == 0) + continue; + + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(talentInfo->SpellID); + if (!spellInfo) continue; RemoveSpell(talentInfo->SpellID, true); // search for spells that the talent teaches and unlearn them - for (SpellEffectInfo const* effect : spellEntry->GetEffectsForDifficulty(DIFFICULTY_NONE)) + for (SpellEffectInfo const* effect : spellInfo->GetEffectsForDifficulty(DIFFICULTY_NONE)) if (effect && effect->TriggerSpell > 0 && effect->Effect == SPELL_EFFECT_LEARN_SPELL) RemoveSpell(effect->TriggerSpell, true); + + if (talentInfo->OverridesSpellID) + RemoveOverrideSpell(talentInfo->OverridesSpellID, talentInfo->SpellID); } - // remove glyphs + // Remove spec specific spells + RemoveSpecializationSpells(); + + // set glyphs for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot) // remove secondary glyph if (uint32 oldglyph = GetGlyph(GetActiveTalentGroup(), slot)) if (GlyphPropertiesEntry const* old_gp = sGlyphPropertiesStore.LookupEntry(oldglyph)) RemoveAurasDueToSpell(old_gp->SpellID); - // Activate new group - SetActiveTalentGroup(group); - - uint32 spentTalents = 0; + SetActiveTalentGroup(spec); + SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, GetSpecId(spec)); for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId) { TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId); - if (!talentInfo) continue; // learn only talents for character class - if (getClass() != talentInfo->ClassID) + if (talentInfo->ClassID != getClass()) continue; - ++spentTalents; + if (!talentInfo->SpellID) + continue; - if (HasTalent(talentInfo->SpellID, group)) - LearnSpell(talentInfo->SpellID, false); + // if the talent can be found in the newly activated PlayerTalentMap + if (HasTalent(talentInfo->ID, GetActiveTalentGroup())) + { + LearnSpell(talentInfo->SpellID, false); // add the talent to the PlayerSpellMap + if (talentInfo->OverridesSpellID) + AddOverrideSpell(talentInfo->OverridesSpellID, talentInfo->SpellID); + } } + LearnSpecializationSpells(); + + if (CanUseMastery()) + if (ChrSpecializationEntry const* specialization = sChrSpecializationStore.LookupEntry(GetSpecId(GetActiveTalentGroup()))) + for (uint32 i = 0; i < MAX_MASTERY_SPELLS; ++i) + if (uint32 mastery = specialization->MasterySpellID[i]) + LearnSpell(mastery, false); + // set glyphs for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot) { @@ -26089,7 +26079,6 @@ void Player::ActivateTalentGroup(uint8 group) SetGlyph(slot, glyph); } - SetUsedTalentCount(spentTalents); InitTalentForLevel(); { @@ -26108,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() @@ -27152,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 @@ -27218,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 e895e696bb4..a22cd007ee0 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -670,6 +670,14 @@ struct SkillStatusData typedef std::unordered_map<uint32, SkillStatusData> SkillStatusMap; +enum AttackSwingErr +{ + ATTACKSWINGERR_CANT_ATTACK = 0, + ATTACKSWINGERR_NOTINRANGE = 1, + ATTACKSWINGERR_BADFACING = 2, + ATTACKSWINGERR_DEADTARGET = 3 +}; + class Quest; class Spell; class Item; @@ -1239,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; } } @@ -1263,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; @@ -1276,6 +1285,7 @@ private: PlayerTalentInfo(PlayerTalentInfo const&); }; + class Player : public Unit, public GridObject<Player> { friend class WorldSession; @@ -1812,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; @@ -1831,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); @@ -2793,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; @@ -2911,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 eea0040404e..d9d6cd9d2e5 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 a2adb6a320c..59822acb7ad 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -63,6 +63,7 @@ #include "MovementStructures.h" #include "WorldSession.h" #include "ChatPackets.h" +#include "MiscPackets.h" #include "MovementPackets.h" #include "CombatPackets.h" #include "CombatLogPackets.h" @@ -161,6 +162,30 @@ ProcEventInfo::ProcEventInfo(Unit* actor, Unit* actionTarget, Unit* procTarget, _damageInfo(damageInfo), _healInfo(healInfo) { } +SpellInfo const* ProcEventInfo::GetSpellInfo() const +{ + /// WORKAROUND: unfinished new proc system + if (_spell) + return _spell->GetSpellInfo(); + if (_damageInfo) + return _damageInfo->GetSpellInfo(); + /*if (_healInfo) + return _healInfo->GetSpellInfo();*/ + return nullptr; +} + +SpellSchoolMask ProcEventInfo::GetSchoolMask() const +{ + /// WORKAROUND: unfinished new proc system + if (_spell) + return _spell->GetSpellInfo()->GetSchoolMask(); + if (_damageInfo) + return _damageInfo->GetSchoolMask(); + /*if (_healInfo) + return _healInfo->GetSchoolMask();*/ + return SPELL_SCHOOL_MASK_NONE; +} + Unit::Unit(bool isWorldObject) : WorldObject(isWorldObject), m_movedPlayer(NULL), m_lastSanctuaryTime(0), IsAIEnabled(false), NeedChangeAI(false), LastCharmerGUID(), @@ -12575,7 +12600,7 @@ void Unit::StopMoving() bool Unit::IsSitState() const { - uint8 s = getStandState(); + UnitStandStateType s = GetStandState(); return s == UNIT_STAND_STATE_SIT_CHAIR || s == UNIT_STAND_STATE_SIT_LOW_CHAIR || s == UNIT_STAND_STATE_SIT_MEDIUM_CHAIR || s == UNIT_STAND_STATE_SIT_HIGH_CHAIR || @@ -12584,22 +12609,21 @@ bool Unit::IsSitState() const bool Unit::IsStandState() const { - uint8 s = getStandState(); + UnitStandStateType s = GetStandState(); return !IsSitState() && s != UNIT_STAND_STATE_SLEEP && s != UNIT_STAND_STATE_KNEEL; } -void Unit::SetStandState(uint8 state) +void Unit::SetStandState(UnitStandStateType state) { - SetByteValue(UNIT_FIELD_BYTES_1, 0, state); + SetByteValue(UNIT_FIELD_BYTES_1, 0, uint8(state)); if (IsStandState()) RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_SEATED); if (GetTypeId() == TYPEID_PLAYER) { - WorldPacket data(SMSG_STANDSTATE_UPDATE, 1); - data << (uint8)state; - ToPlayer()->GetSession()->SendPacket(&data); + WorldPackets::Misc::StandStateUpdate packet(state); + ToPlayer()->GetSession()->SendPacket(packet.Write()); } } @@ -16545,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 1b8c188afb4..0311b67befd 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -97,7 +97,7 @@ enum SpellModOp SPELLMOD_COOLDOWN = 11, SPELLMOD_EFFECT2 = 12, SPELLMOD_IGNORE_ARMOR = 13, - SPELLMOD_COST = 14, + SPELLMOD_COST = 14, // Used when SpellPowerEntry::PowerIndex == 0 SPELLMOD_CRIT_DAMAGE_BONUS = 15, SPELLMOD_RESIST_MISS_CHANCE = 16, SPELLMOD_JUMP_TARGETS = 17, @@ -113,10 +113,16 @@ enum SpellModOp SPELLMOD_VALUE_MULTIPLIER = 27, SPELLMOD_RESIST_DISPEL_CHANCE = 28, SPELLMOD_CRIT_DAMAGE_BONUS_2 = 29, //one not used spell - SPELLMOD_SPELL_COST_REFUND_ON_FAIL = 30 + SPELLMOD_SPELL_COST_REFUND_ON_FAIL = 30, + SPELLMOD_STACK_AMOUNT = 31, // has no effect on tooltip parsing + SPELLMOD_EFFECT4 = 32, + SPELLMOD_EFFECT5 = 33, + SPELLMOD_SPELL_COST2 = 34, // Used when SpellPowerEntry::PowerIndex == 1 + SPELLMOD_JUMP_DISTANCE = 35, + SPELLMOD_STACK_AMOUNT2 = 37 // same as SPELLMOD_STACK_AMOUNT but affects tooltips }; -#define MAX_SPELLMOD 32 +#define MAX_SPELLMOD 38 enum SpellValueMod { @@ -978,8 +984,8 @@ public: uint32 GetSpellPhaseMask() const { return _spellPhaseMask; } uint32 GetHitMask() const { return _hitMask; } - SpellInfo const* GetSpellInfo() const { return NULL; } - SpellSchoolMask GetSchoolMask() const { return SPELL_SCHOOL_MASK_NONE; } + SpellInfo const* GetSpellInfo() const; + SpellSchoolMask GetSchoolMask() const; DamageInfo* GetDamageInfo() const { return _damageInfo; } HealInfo* GetHealInfo() const { return _healInfo; } @@ -1500,10 +1506,10 @@ class Unit : public WorldObject uint32 GetCreatureType() const; uint32 GetCreatureTypeMask() const; - uint8 getStandState() const { return GetByteValue(UNIT_FIELD_BYTES_1, 0); } + UnitStandStateType GetStandState() const { return UnitStandStateType(GetByteValue(UNIT_FIELD_BYTES_1, 0)); } bool IsSitState() const; bool IsStandState() const; - void SetStandState(uint8 state); + void SetStandState(UnitStandStateType state); void SetStandFlags(uint8 flags) { SetByteFlag(UNIT_FIELD_BYTES_1, 2, flags); } void RemoveStandFlags(uint8 flags) { RemoveByteFlag(UNIT_FIELD_BYTES_1, 2, flags); } @@ -1930,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/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index d588d048d3d..202165ffd57 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -156,11 +156,11 @@ bool normalizePlayerName(std::string& name) } // Extracts player and realm names delimited by - -ExtendedPlayerName ExtractExtendedPlayerName(std::string& name) +ExtendedPlayerName ExtractExtendedPlayerName(std::string const& name) { size_t pos = name.find('-'); if (pos != std::string::npos) - return ExtendedPlayerName(name.substr(0, pos), name.substr(pos+1)); + return ExtendedPlayerName(name.substr(0, pos), name.substr(pos + 1)); else return ExtendedPlayerName(name, ""); } @@ -1869,7 +1869,7 @@ ObjectGuid::LowType ObjectMgr::AddGOData(uint32 entry, uint32 mapId, float x, fl data.spawnMask = 1; data.go_state = GO_STATE_READY; data.phaseMask = PHASEMASK_NORMAL; - data.artKit = goinfo->type == GAMEOBJECT_TYPE_CAPTURE_POINT ? 21 : 0; + data.artKit = goinfo->type == GAMEOBJECT_TYPE_CONTROL_ZONE ? 21 : 0; data.dbData = false; AddGameobjectToGrid(guid, &data); diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 579cc7a5d4e..428d234fdb6 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -636,12 +636,12 @@ bool normalizePlayerName(std::string& name); struct ExtendedPlayerName { - ExtendedPlayerName(std::string const& name, std::string const& realm) : Name(name), Realm(realm) {} + ExtendedPlayerName(std::string const& name, std::string const& realm) : Name(name), Realm(realm) { } std::string Name; std::string Realm; }; -ExtendedPlayerName ExtractExtendedPlayerName(std::string& name); +ExtendedPlayerName ExtractExtendedPlayerName(std::string const& name); struct LanguageDesc { diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index c255d786ce3..c7a7bcbf62c 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -1369,7 +1369,7 @@ void WorldSession::HandleAlterAppearance(WorldPacket& recvData) return; } - if (_player->getStandState() != UNIT_STAND_STATE_SIT_LOW_CHAIR + go->GetGOInfo()->barberChair.chairheight) + if (_player->GetStandState() != UNIT_STAND_STATE_SIT_LOW_CHAIR + go->GetGOInfo()->barberChair.chairheight) { SendBarberShopResult(BARBER_SHOP_RESULT_NOT_ON_CHAIR); return; @@ -1399,7 +1399,7 @@ void WorldSession::HandleAlterAppearance(WorldPacket& recvData) _player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP, 1); - _player->SetStandState(0); // stand up + _player->SetStandState(UNIT_STAND_STATE_STAND); } void WorldSession::HandleRemoveGlyph(WorldPacket& recvData) diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index aed2da5c534..1860904d617 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -419,7 +419,7 @@ void WorldSession::HandleLogoutRequestOpcode(WorldPackets::Character::LogoutRequ // not set flags if player can't free move to prevent lost state at logout cancel if (GetPlayer()->CanFreeMove()) { - if (GetPlayer()->getStandState() == UNIT_STAND_STATE_STAND) + if (GetPlayer()->GetStandState() == UNIT_STAND_STATE_STAND) GetPlayer()->SetStandState(UNIT_STAND_STATE_SIT); GetPlayer()->SetRooted(true); GetPlayer()->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); @@ -543,13 +543,9 @@ void WorldSession::HandleSetSelectionOpcode(WorldPackets::Misc::SetSelection& pa _player->SetSelection(packet.Selection); } -void WorldSession::HandleStandStateChangeOpcode(WorldPacket& recvData) +void WorldSession::HandleStandStateChangeOpcode(WorldPackets::Misc::StandStateChange& packet) { - // TC_LOG_DEBUG("network", "WORLD: Received CMSG_STANDSTATECHANGE"); -- too many spam in log at lags/debug stop - uint32 animstate; - recvData >> animstate; - - _player->SetStandState(animstate); + _player->SetStandState(packet.StandState); } void WorldSession::HandleContactListOpcode(WorldPacket& recvData) 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/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 41594ad7d94..724d2a7eed4 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -29,6 +29,7 @@ #include "InstanceScript.h" #include "MapInstanced.h" #include "MapManager.h" +#include "MiscPackets.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "Pet.h" @@ -36,6 +37,7 @@ #include "Transport.h" #include "Vehicle.h" #include "VMapFactory.h" +#include "Weather.h" u_map_magic MapMagic = { {'M','A','P','S'} }; u_map_magic MapVersionMagic = { {'v','1','.','4'} }; @@ -49,6 +51,10 @@ u_map_magic MapLiquidMagic = { {'M','L','I','Q'} }; GridState* si_GridStates[MAX_GRID_STATE]; + +ZoneDynamicInfo::ZoneDynamicInfo() : MusicId(0), WeatherId(WEATHER_STATE_FINE), + WeatherGrade(0.0f), OverrideLightId(0), LightFadeInTime(0) { } + Map::~Map() { sScriptMgr->OnDestroyMap(this); @@ -2706,7 +2712,7 @@ uint32 Map::GetPlayersCountExceptGMs() const return count; } -void Map::SendToPlayers(WorldPacket* data) const +void Map::SendToPlayers(WorldPacket const* data) const { for (MapRefManager::const_iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr) itr->GetSource()->GetSession()->SendPacket(data); @@ -3491,13 +3497,10 @@ void Map::SendZoneDynamicInfo(Player* player) player->SendDirectMessage(&data); } - if (uint32 weather = itr->second.WeatherId) + if (WeatherState weatherId = itr->second.WeatherId) { - WorldPacket data(SMSG_WEATHER, 4 + 4 + 1); - data << uint32(weather); - data << float(itr->second.WeatherGrade); - data << uint8(0); - player->SendDirectMessage(&data); + WorldPackets::Misc::Weather weather(weatherId, itr->second.WeatherGrade); + player->SendDirectMessage(weather.Write()); } if (uint32 overrideLight = itr->second.OverrideLightId) @@ -3532,7 +3535,7 @@ void Map::SetZoneMusic(uint32 zoneId, uint32 musicId) } } -void Map::SetZoneWeather(uint32 zoneId, uint32 weatherId, float weatherGrade) +void Map::SetZoneWeather(uint32 zoneId, WeatherState weatherId, float weatherGrade) { if (_zoneDynamicInfo.find(zoneId) == _zoneDynamicInfo.end()) _zoneDynamicInfo.insert(ZoneDynamicInfoMap::value_type(zoneId, ZoneDynamicInfo())); @@ -3544,15 +3547,12 @@ void Map::SetZoneWeather(uint32 zoneId, uint32 weatherId, float weatherGrade) if (!players.isEmpty()) { - WorldPacket data(SMSG_WEATHER, 4 + 4 + 1); - data << uint32(weatherId); - data << float(weatherGrade); - data << uint8(0); + WorldPackets::Misc::Weather weather(weatherId, weatherGrade); for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) if (Player* player = itr->GetSource()) if (player->GetZoneId() == zoneId) - player->SendDirectMessage(&data); + player->SendDirectMessage(weather.Write()); } } diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index d38852e50c9..752953091f8 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -53,6 +53,8 @@ class MapInstanced; class BattlegroundMap; class InstanceMap; class Transport; +enum WeatherState : uint32; + namespace Trinity { struct ObjectUpdater; } struct ScriptAction @@ -231,11 +233,10 @@ enum LevelRequirementVsMode struct ZoneDynamicInfo { - ZoneDynamicInfo() : MusicId(0), WeatherId(0), WeatherGrade(0.0f), - OverrideLightId(0), LightFadeInTime(0) { } + ZoneDynamicInfo(); uint32 MusicId; - uint32 WeatherId; + WeatherState WeatherId; float WeatherGrade; uint32 OverrideLightId; uint32 LightFadeInTime; @@ -424,7 +425,7 @@ class Map : public GridRefManager<NGridType> void AddWorldObject(WorldObject* obj) { i_worldObjects.insert(obj); } void RemoveWorldObject(WorldObject* obj) { i_worldObjects.erase(obj); } - void SendToPlayers(WorldPacket* data) const; + void SendToPlayers(WorldPacket const* data) const; typedef MapRefManager PlayerList; PlayerList const& GetPlayers() const { return m_mapRefManager; } @@ -513,7 +514,7 @@ class Map : public GridRefManager<NGridType> void SendZoneDynamicInfo(Player* player); void SetZoneMusic(uint32 zoneId, uint32 musicId); - void SetZoneWeather(uint32 zoneId, uint32 weatherId, float weatherGrade); + void SetZoneWeather(uint32 zoneId, WeatherState weatherId, float weatherGrade); void SetZoneOverrideLight(uint32 zoneId, uint32 lightId, uint32 fadeInTime); void UpdateAreaDependentAuras(); diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 122181fb313..7a6b20696fd 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -504,7 +504,7 @@ enum SpellAttr4 SPELL_ATTR4_UNK4 = 0x00000010, // 4 This will no longer cause guards to attack on use?? SPELL_ATTR4_UNK5 = 0x00000020, // 5 SPELL_ATTR4_NOT_STEALABLE = 0x00000040, // 6 although such auras might be dispellable, they cannot be stolen - SPELL_ATTR4_TRIGGERED = 0x00000080, // 7 spells forced to be triggered + SPELL_ATTR4_CAN_CAST_WHILE_CASTING = 0x00000080, // 7 Can be cast while another cast is in progress - see CanCastWhileCasting(SpellRec const*,CGUnit_C *,int &) SPELL_ATTR4_FIXED_DAMAGE = 0x00000100, // 8 Ignores resilience and any (except mechanic related) damage or % damage taken auras on target. SPELL_ATTR4_TRIGGER_ACTIVATE = 0x00000200, // 9 initially disabled / trigger activate from event (Execute, Riposte, Deep Freeze end other) SPELL_ATTR4_SPELL_VS_EXTEND_COST = 0x00000400, // 10 Rogue Shiv have this flag @@ -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/OutdoorPvP/OutdoorPvP.cpp b/src/server/game/OutdoorPvP/OutdoorPvP.cpp index a23d71e0fe8..089bba92c38 100644 --- a/src/server/game/OutdoorPvP/OutdoorPvP.cpp +++ b/src/server/game/OutdoorPvP/OutdoorPvP.cpp @@ -141,7 +141,7 @@ bool OPvPCapturePoint::SetCapturePointData(uint32 entry, uint32 map, float x, fl // check info existence GameObjectTemplate const* goinfo = sObjectMgr->GetGameObjectTemplate(entry); - if (!goinfo || goinfo->type != GAMEOBJECT_TYPE_CAPTURE_POINT) + if (!goinfo || goinfo->type != GAMEOBJECT_TYPE_CONTROL_ZONE) { TC_LOG_ERROR("outdoorpvp", "OutdoorPvP: GO %u is not capture point!", entry); return false; @@ -548,21 +548,6 @@ bool OutdoorPvP::HandleDropFlag(Player* player, uint32 id) return false; } -bool OPvPCapturePoint::HandleGossipOption(Player* /*player*/, ObjectGuid /*guid*/, uint32 /*id*/) -{ - return false; -} - -bool OPvPCapturePoint::CanTalkTo(Player* /*player*/, Creature* /*c*/, GossipMenuItems const& /*gso*/) -{ - return false; -} - -bool OPvPCapturePoint::HandleDropFlag(Player* /*player*/, uint32 /*id*/) -{ - return false; -} - int32 OPvPCapturePoint::HandleOpenGo(Player* /*player*/, ObjectGuid guid) { std::map<ObjectGuid, uint32>::iterator itr = m_ObjectTypes.find(guid); @@ -573,18 +558,32 @@ int32 OPvPCapturePoint::HandleOpenGo(Player* /*player*/, ObjectGuid guid) return -1; } -bool OutdoorPvP::HandleAreaTrigger(Player* /*player*/, uint32 /*trigger*/) -{ - return false; -} - -void OutdoorPvP::BroadcastPacket(WorldPacket &data) const +void OutdoorPvP::BroadcastPacket(WorldPacket const* data) const { // This is faster than sWorld->SendZoneMessage for (uint32 team = 0; team < 2; ++team) for (GuidSet::const_iterator itr = m_players[team].begin(); itr != m_players[team].end(); ++itr) if (Player* const player = ObjectAccessor::FindPlayer(*itr)) - player->GetSession()->SendPacket(&data); + player->SendDirectMessage(data); +} + +void OutdoorPvP::AddCapturePoint(OPvPCapturePoint* cp) +{ + OPvPCapturePointMap::iterator i = m_capturePoints.find(cp->m_capturePointGUID); + if (i != m_capturePoints.end()) + { + TC_LOG_ERROR("outdoorpvp", "OutdoorPvP::AddCapturePoint: CapturePoint %s already exists!", cp->m_capturePointGUID); + delete i->second; + } + m_capturePoints[cp->m_capturePointGUID] = cp; +} + +OPvPCapturePoint* OutdoorPvP::GetCapturePoint(ObjectGuid guid) const +{ + OutdoorPvP::OPvPCapturePointMap::const_iterator itr = m_capturePoints.find(guid); + if (itr != m_capturePoints.end()) + return itr->second; + return nullptr; } void OutdoorPvP::RegisterZone(uint32 zoneId) @@ -622,7 +621,7 @@ void OutdoorPvP::TeamApplyBuff(TeamId team, uint32 spellId, uint32 spellId2) void OutdoorPvP::OnGameObjectCreate(GameObject* go) { - if (go->GetGoType() != GAMEOBJECT_TYPE_CAPTURE_POINT) + if (go->GetGoType() != GAMEOBJECT_TYPE_CONTROL_ZONE) return; if (OPvPCapturePoint *cp = GetCapturePoint(go->GetGUID())) @@ -631,7 +630,7 @@ void OutdoorPvP::OnGameObjectCreate(GameObject* go) void OutdoorPvP::OnGameObjectRemove(GameObject* go) { - if (go->GetGoType() != GAMEOBJECT_TYPE_CAPTURE_POINT) + if (go->GetGoType() != GAMEOBJECT_TYPE_CONTROL_ZONE) return; if (OPvPCapturePoint *cp = GetCapturePoint(go->GetGUID())) diff --git a/src/server/game/OutdoorPvP/OutdoorPvP.h b/src/server/game/OutdoorPvP/OutdoorPvP.h index 8f4475bce13..f550a35aff0 100644 --- a/src/server/game/OutdoorPvP/OutdoorPvP.h +++ b/src/server/game/OutdoorPvP/OutdoorPvP.h @@ -119,11 +119,11 @@ class OPvPCapturePoint virtual void SendChangePhase(); - virtual bool HandleGossipOption(Player* player, ObjectGuid guid, uint32 gossipid); + virtual bool HandleGossipOption(Player* /*player*/, ObjectGuid /*guid*/, uint32 /*gossipId*/) { return false; } - virtual bool CanTalkTo(Player* player, Creature* c, GossipMenuItems const& gso); + virtual bool CanTalkTo(Player* /*player*/, Creature* /*creature*/, GossipMenuItems const& /*gso*/) { return false; } - virtual bool HandleDropFlag(Player* player, uint32 spellId); + virtual bool HandleDropFlag(Player* /*player*/, uint32 /*spellId*/) { return false; } virtual void DeleteSpawns(); @@ -204,7 +204,7 @@ class OutdoorPvP : public ZoneScript virtual void FillInitialWorldStates(WorldPacket & /*data*/) { } // called when a player triggers an areatrigger - virtual bool HandleAreaTrigger(Player* player, uint32 trigger); + virtual bool HandleAreaTrigger(Player* /*player*/, uint32 /*trigger*/) { return false; } // called on custom spell virtual bool HandleCustomSpell(Player* player, uint32 spellId, GameObject* go); @@ -274,25 +274,16 @@ class OutdoorPvP : public ZoneScript // world state stuff virtual void SendRemoveWorldStates(Player* /*player*/) { } - void BroadcastPacket(WorldPacket & data) const; + void BroadcastPacket(WorldPacket const* data) const; virtual void HandlePlayerEnterZone(Player* player, uint32 zone); virtual void HandlePlayerLeaveZone(Player* player, uint32 zone); virtual void HandlePlayerResurrects(Player* player, uint32 zone); - void AddCapturePoint(OPvPCapturePoint* cp) - { - m_capturePoints[cp->m_capturePointGUID] = cp; - } + void AddCapturePoint(OPvPCapturePoint* cp); - OPvPCapturePoint * GetCapturePoint(ObjectGuid guid) const - { - OutdoorPvP::OPvPCapturePointMap::const_iterator itr = m_capturePoints.find(guid); - if (itr != m_capturePoints.end()) - return itr->second; - return NULL; - } + OPvPCapturePoint * GetCapturePoint(ObjectGuid guid) const; void RegisterZone(uint32 zoneid); diff --git a/src/server/game/Server/Packets/CombatPackets.cpp b/src/server/game/Server/Packets/CombatPackets.cpp index a23cbc7515b..53a489ebc21 100644 --- a/src/server/game/Server/Packets/CombatPackets.cpp +++ b/src/server/game/Server/Packets/CombatPackets.cpp @@ -140,3 +140,10 @@ WorldPacket const* WorldPackets::Combat::AttackerStateUpdate::Write() return &_worldPacket; } + +WorldPacket const* WorldPackets::Combat::AttackSwingError::Write() +{ + _worldPacket.WriteBits(Reason, 2); + _worldPacket.FlushBits(); + return &_worldPacket; +} diff --git a/src/server/game/Server/Packets/CombatPackets.h b/src/server/game/Server/Packets/CombatPackets.h index 012859ffc65..54d97e5774c 100644 --- a/src/server/game/Server/Packets/CombatPackets.h +++ b/src/server/game/Server/Packets/CombatPackets.h @@ -36,6 +36,17 @@ namespace WorldPackets ObjectGuid Victim; }; + class AttackSwingError final : public ServerPacket + { + public: + AttackSwingError() : ServerPacket(SMSG_ATTACKSWING_ERROR, 4) { } + AttackSwingError(AttackSwingErr reason) : ServerPacket(SMSG_ATTACKSWING_ERROR, 4), Reason(reason) { } + + WorldPacket const* Write() override; + + AttackSwingErr Reason = ATTACKSWINGERR_CANT_ATTACK; + }; + class AttackStop final : public ClientPacket { public: @@ -165,6 +176,14 @@ namespace WorldPackets UnkAttackerState UnkState; float Unk = 0.0f; }; + + class CancelCombat final : public ServerPacket + { + public: + CancelCombat() : ServerPacket(SMSG_CANCEL_COMBAT, 0) { } + + WorldPacket const* Write() override { return &_worldPacket; } + }; } } diff --git a/src/server/game/Server/Packets/MiscPackets.cpp b/src/server/game/Server/Packets/MiscPackets.cpp index da92f65020a..699d504554e 100644 --- a/src/server/game/Server/Packets/MiscPackets.cpp +++ b/src/server/game/Server/Packets/MiscPackets.cpp @@ -259,3 +259,33 @@ void WorldPackets::Misc::ResurrectResponse::Read() _worldPacket >> Resurrecter; _worldPacket >> Response; } + +WorldPackets::Misc::Weather::Weather() : ServerPacket(SMSG_WEATHER, 4 + 4 + 1) { } + +WorldPackets::Misc::Weather::Weather(WeatherState weatherID, float intensity /*= 0.0f*/, bool abrupt /*= false*/) + : ServerPacket(SMSG_WEATHER, 4 + 4 + 1), WeatherID(weatherID), Intensity(intensity), Abrupt(abrupt) { } + +WorldPacket const* WorldPackets::Misc::Weather::Write() +{ + _worldPacket << uint32(WeatherID); + _worldPacket << float(Intensity); + _worldPacket.WriteBit(Abrupt); + + _worldPacket.FlushBits(); + return &_worldPacket; +} + +void WorldPackets::Misc::StandStateChange::Read() +{ + uint32 state; + _worldPacket >> state; + + StandState = UnitStandStateType(state); +} + +WorldPacket const* WorldPackets::Misc::StandStateUpdate::Write() +{ + _worldPacket << uint8(State); + + return &_worldPacket; +} diff --git a/src/server/game/Server/Packets/MiscPackets.h b/src/server/game/Server/Packets/MiscPackets.h index b46b0aed60a..4a0d0bfbd12 100644 --- a/src/server/game/Server/Packets/MiscPackets.h +++ b/src/server/game/Server/Packets/MiscPackets.h @@ -23,6 +23,8 @@ #include "WorldSession.h" #include "G3D/Vector3.h" #include "Object.h" +#include "Unit.h" +#include "Weather.h" namespace WorldPackets { @@ -339,7 +341,7 @@ namespace WorldPackets void Read() override { } }; - class RequestCemeteryListResponse : public ServerPacket + class RequestCemeteryListResponse final : public ServerPacket { public: RequestCemeteryListResponse() : ServerPacket(SMSG_REQUEST_CEMETERY_LIST_RESPONSE, 1) { } @@ -361,13 +363,47 @@ namespace WorldPackets uint32 Response = 0; }; - class AreaTriggerNoCorpse : public ServerPacket + class AreaTriggerNoCorpse final : public ServerPacket { public: AreaTriggerNoCorpse() : ServerPacket(SMSG_AREA_TRIGGER_NO_CORPSE, 0) { } WorldPacket const* Write() override { return &_worldPacket; } }; + + class Weather final : public ServerPacket + { + public: + Weather(); + Weather(WeatherState weatherID, float intensity = 0.0f, bool abrupt = false); + + WorldPacket const* Write() override; + + bool Abrupt = false; + float Intensity = 0.0f; + WeatherState WeatherID = WEATHER_STATE_FINE; + }; + + class StandStateChange final : public ClientPacket + { + public: + StandStateChange(WorldPacket&& packet) : ClientPacket(CMSG_STAND_STATE_CHANGE, std::move(packet)) { } + + void Read() override; + + UnitStandStateType StandState = UNIT_STAND_STATE_STAND; + }; + + class StandStateUpdate final : public ServerPacket + { + public: + StandStateUpdate() : ServerPacket(SMSG_STAND_STATE_UPDATE, 1) { } + StandStateUpdate(UnitStandStateType state) : ServerPacket(SMSG_STAND_STATE_UPDATE, 1), State(state) { } + + WorldPacket const* Write() override; + + UnitStandStateType State = UNIT_STAND_STATE_STAND; + }; } } diff --git a/src/server/game/Server/Packets/QueryPackets.h b/src/server/game/Server/Packets/QueryPackets.h index 9b131ae82be..5a48db610ca 100644 --- a/src/server/game/Server/Packets/QueryPackets.h +++ b/src/server/game/Server/Packets/QueryPackets.h @@ -314,7 +314,7 @@ namespace WorldPackets WorldPacket const* Write() override; time_t CurrentTime = time_t(0); - int32 TimeOutRequest; + int32 TimeOutRequest = 0; }; } } 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 cb8cd23482f..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 ); @@ -809,7 +809,7 @@ void OpcodeTable::Initialize() DEFINE_OPCODE_HANDLER_OLD(CMSG_SPELLCLICK, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleSpellClick ); DEFINE_OPCODE_HANDLER_OLD(CMSG_SPIRIT_HEALER_ACTIVATE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleSpiritHealerActivateOpcode); DEFINE_HANDLER(CMSG_SPLIT_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Item::SplitItem, &WorldSession::HandleSplitItemOpcode); - DEFINE_OPCODE_HANDLER_OLD(CMSG_STANDSTATECHANGE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleStandStateChangeOpcode ); + DEFINE_HANDLER(CMSG_STAND_STATE_CHANGE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Misc::StandStateChange, &WorldSession::HandleStandStateChangeOpcode); DEFINE_OPCODE_HANDLER_OLD(CMSG_START_QUEST, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); DEFINE_OPCODE_HANDLER_OLD(CMSG_STOP_DANCE, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); DEFINE_OPCODE_HANDLER_OLD(CMSG_STORE_LOOT_IN_SLOT, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); @@ -938,11 +938,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_ATTACKERSTATEUPDATE, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ATTACKSTART, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ATTACKSTOP, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_ATTACKSWING_BADFACING, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_ATTACKSWING_CANT_ATTACK, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_ATTACKSWING_DEADTARGET, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_ATTACKSWING_ERROR, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_ATTACKSWING_NOTINRANGE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_ATTACKSWING_ERROR, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_AUCTION_BIDDER_NOTIFICATION, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_AUCTION_CLOSED_NOTIFICATION, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_AUCTION_COMMAND_RESULT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); @@ -1056,7 +1052,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_CALENDAR_UPDATE_INVITE_LIST, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CAMERA_SHAKE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CANCEL_AUTO_REPEAT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_CANCEL_COMBAT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_CANCEL_COMBAT, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CANCEL_ORPHAN_SPELL_VISUAL, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CANCEL_SCENE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CANCEL_SPELL_VISUAL, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); @@ -1803,7 +1799,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_SPELL_START, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SPELL_UPDATE_CHAIN_TARGETS, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SPIRIT_HEALER_CONFIRM, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_STANDSTATE_UPDATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_STAND_STATE_UPDATE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_START_ELAPSED_TIMER, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_START_ELAPSED_TIMERS, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_START_LOOT_ROLL, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); @@ -1885,7 +1881,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_WARDEN_DATA, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_WARGAME_CHECK_ENTRY, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_WARGAME_REQUEST_SENT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_WEATHER, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_WEATHER, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_WEEKLY_LAST_RESET, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_WEEKLY_RESET_CURRENCY, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_WEEKLY_SPELL_USAGE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); diff --git a/src/server/game/Server/Protocol/Opcodes.h b/src/server/game/Server/Protocol/Opcodes.h index 971c69a2151..fa0ffde42cb 100644 --- a/src/server/game/Server/Protocol/Opcodes.h +++ b/src/server/game/Server/Protocol/Opcodes.h @@ -719,7 +719,7 @@ enum OpcodeClient : uint32 CMSG_SPELLCLICK = 0x0BC2, CMSG_SPIRIT_HEALER_ACTIVATE = 0x03EC, CMSG_SPLIT_ITEM = 0x0795, - CMSG_STANDSTATECHANGE = 0x0ABD, + CMSG_STAND_STATE_CHANGE = 0x0ABD, CMSG_START_QUEST = 0xBADD, CMSG_STOP_DANCE = 0xBADD, CMSG_STORE_LOOT_IN_SLOT = 0xBADD, @@ -846,11 +846,7 @@ enum OpcodeServer : uint32 SMSG_ATTACKERSTATEUPDATE = 0x1204, SMSG_ATTACKSTART = 0x1D3E, SMSG_ATTACKSTOP = 0x1382, - SMSG_ATTACKSWING_BADFACING = 0xBADD, - SMSG_ATTACKSWING_CANT_ATTACK = 0xBADD, - SMSG_ATTACKSWING_DEADTARGET = 0xBADD, SMSG_ATTACKSWING_ERROR = 0x0509, - SMSG_ATTACKSWING_NOTINRANGE = 0xBADD, SMSG_AUCTION_BIDDER_NOTIFICATION = 0xBADD, SMSG_AUCTION_CLOSED_NOTIFICATION = 0x058E, SMSG_AUCTION_COMMAND_RESULT = 0x0B2D, @@ -1712,7 +1708,7 @@ enum OpcodeServer : uint32 SMSG_SPELL_START = 0x0803, SMSG_SPELL_UPDATE_CHAIN_TARGETS = 0x0374, SMSG_SPIRIT_HEALER_CONFIRM = 0x1331, - SMSG_STANDSTATE_UPDATE = 0x1311, + SMSG_STAND_STATE_UPDATE = 0x1311, SMSG_START_ELAPSED_TIMER = 0x0D2A, SMSG_START_ELAPSED_TIMERS = 0x093F, SMSG_START_LOOT_ROLL = 0x1B84, diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 17ca3e3c58c..166a432077c 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -1279,7 +1279,7 @@ uint32 WorldSession::DosProtection::GetMaxPacketCounterAllowed(uint16 opcode) co case CMSG_MESSAGECHAT_YELL: // 0 3.5 case CMSG_INSPECT: // 0 3.5 //case CMSG_AREA_SPIRIT_HEALER_QUERY: // not profiled - case CMSG_STANDSTATECHANGE: // not profiled + case CMSG_STAND_STATE_CHANGE: // not profiled case CMSG_RANDOM_ROLL: // not profiled case CMSG_TIME_SYNC_RESPONSE: // not profiled case CMSG_TRAINER_BUY_SPELL: // not profiled diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 0707ffbb6fc..291ba0a9f66 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -203,6 +203,7 @@ namespace WorldPackets class RepopRequest; class RequestCemeteryList; class ResurrectResponse; + class StandStateChange; class UITimeRequest; } @@ -246,7 +247,8 @@ namespace WorldPackets namespace Spells { class CancelAura; - class SpellCastRequest; + class CastSpell; + class PetCastSpell; class SetActionButton; } @@ -735,7 +737,7 @@ class WorldSession void HandleZoneUpdateOpcode(WorldPacket& recvPacket); void HandleSetSelectionOpcode(WorldPackets::Misc::SetSelection& packet); - void HandleStandStateChangeOpcode(WorldPacket& recvPacket); + void HandleStandStateChangeOpcode(WorldPackets::Misc::StandStateChange& packet); void HandleEmoteOpcode(WorldPacket& recvPacket); void HandleContactListOpcode(WorldPacket& recvPacket); void HandleAddFriendOpcode(WorldPacket& recvPacket); @@ -946,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); @@ -1029,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/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 819942901cb..9cf748ff912 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -41,6 +41,7 @@ #include "WeatherMgr.h" #include "Pet.h" #include "ReputationMgr.h" +#include "MiscPackets.h" class Aura; // @@ -6633,10 +6634,8 @@ void AuraEffect::HandleAuraForceWeather(AuraApplication const* aurApp, uint8 mod if (apply) { - WorldPacket data(SMSG_WEATHER, (4 + 4 + 1)); - - data << uint32(GetMiscValue()) << 1.0f << uint8(0); - target->GetSession()->SendPacket(&data); + WorldPackets::Misc::Weather weather(WeatherState(GetMiscValue()), 1.0f); + target->GetSession()->SendPacket(weather.Write()); } else { diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index d729d76a1d5..387a01dbf0a 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 @@ -641,8 +617,8 @@ m_spellValue(new SpellValue(caster->GetMap()->GetDifficultyID(), m_spellInfo)), m_spellState = SPELL_STATE_NULL; _triggeredCastFlags = triggerFlags; - if (info->HasAttribute(SPELL_ATTR4_TRIGGERED)) - _triggeredCastFlags = TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_IGNORE_EQUIPPED_ITEM_REQUIREMENT); + if (info->HasAttribute(SPELL_ATTR4_CAN_CAST_WHILE_CASTING)) + _triggeredCastFlags = TriggerCastFlags(uint32(_triggeredCastFlags) | TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_CAST_DIRECTLY); m_CastItem = NULL; m_castItemGUID.Clear(); @@ -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 3eeb50a2d39..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,11 +3938,17 @@ void Spell::EffectStuck(SpellEffIndex /*effIndex*/) { if (!player->GetDeathTimer()) player->RepopAtGraveyard(); - + + return; + } + + // the player dies if hearthstone is in cooldown, else the player is teleported to home + if (player->HasSpellCooldown(8690)) + { + player->Kill(player); return; } - // the player is teleported to home player->TeleportTo(player->m_homebindMapId, player->m_homebindX, player->m_homebindY, player->m_homebindZ, player->GetOrientation(), TELE_TO_SPELL); // Stuck spell trigger Hearthstone cooldown @@ -3999,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(); @@ -4008,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: @@ -4032,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) { @@ -4042,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(); } } @@ -5762,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 02d3e2e2ea1..d0eff5fff00 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; } @@ -1711,7 +1647,7 @@ void SpellMgr::LoadSpellTargetPositions() { Field* fields = result->Fetch(); - uint32 Spell_ID = fields[0].GetUInt32(); + uint32 spellId = fields[0].GetUInt32(); SpellEffIndex effIndex = SpellEffIndex(fields[1].GetUInt8()); SpellTargetPosition st; @@ -1724,27 +1660,27 @@ void SpellMgr::LoadSpellTargetPositions() MapEntry const* mapEntry = sMapStore.LookupEntry(st.target_mapId); if (!mapEntry) { - TC_LOG_ERROR("sql.sql", "Spell (ID: %u, EffectIndex: %u) is using a non-existant MapID (ID: %u).", Spell_ID, effIndex, st.target_mapId); + TC_LOG_ERROR("sql.sql", "Spell (Id: %u, EffectIndex: %u) is using a non-existant MapID (ID: %u).", spellId, effIndex, st.target_mapId); continue; } if (st.target_X == 0 && st.target_Y == 0 && st.target_Z == 0) { - TC_LOG_ERROR("sql.sql", "Spell (ID: %u, EffectIndex: %u): target coordinates not provided.", Spell_ID, effIndex); + TC_LOG_ERROR("sql.sql", "Spell (Id: %u, EffectIndex: %u): target coordinates not provided.", spellId, effIndex); continue; } - SpellInfo const* spellInfo = GetSpellInfo(Spell_ID); + SpellInfo const* spellInfo = GetSpellInfo(spellId); if (!spellInfo) { - TC_LOG_ERROR("sql.sql", "Spell (ID: %u) listed in `spell_target_position` does not exist.", Spell_ID); + TC_LOG_ERROR("sql.sql", "Spell (Id: %u) listed in `spell_target_position` does not exist.", spellId); continue; } SpellEffectInfo const* effect = spellInfo->GetEffect(effIndex); if (!effect) { - TC_LOG_ERROR("sql.sql", "Spell (Id: %u, effIndex: %u) listed in `spell_target_position` does not have an effect at index %u.", Spell_ID, effIndex, effIndex); + TC_LOG_ERROR("sql.sql", "Spell (Id: %u, EffectIndex: %u) listed in `spell_target_position` does not have an effect at index %u.", spellId, effIndex, effIndex); continue; } @@ -1756,19 +1692,13 @@ void SpellMgr::LoadSpellTargetPositions() if (effect->TargetA.GetTarget() == TARGET_DEST_DB || effect->TargetB.GetTarget() == TARGET_DEST_DB) { - TC_LOG_ERROR("sql.sql", "Spell (Id: %u, effIndex: %u) listed in `spell_target_position` does not have TARGET_DEST_DB as target at index %u.", Spell_ID, effIndex, effIndex); - continue; - } - - if (effect->TargetA.GetTarget() == TARGET_DEST_DB || effect->TargetB.GetTarget() == TARGET_DEST_DB) - { - std::pair<uint32, SpellEffIndex> key = std::make_pair(Spell_ID, effIndex); + std::pair<uint32, SpellEffIndex> key = std::make_pair(spellId, effIndex); mSpellTargetPositions[key] = st; ++count; } else { - TC_LOG_ERROR("sql.sql", "Spell (Id: %u, effIndex: %u) listed in `spell_target_position` does not have target TARGET_DEST_DB (17).", Spell_ID, effIndex); + TC_LOG_ERROR("sql.sql", "Spell (Id: %u, EffectIndex: %u) listed in `spell_target_position` does not have target TARGET_DEST_DB (17).", spellId, effIndex); continue; } @@ -1776,38 +1706,26 @@ void SpellMgr::LoadSpellTargetPositions() /* // Check all spells - for (uint32 i = 1; i < GetSpellInfoStoreSize; ++i) + for (uint32 i = 1; i < GetSpellInfoStoreSize(); ++i) { SpellInfo const* spellInfo = GetSpellInfo(i); if (!spellInfo) continue; - bool found = false; - for (int j = 0; j < MAX_SPELL_EFFECTS; ++j) + for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j) { - switch (spellInfo->Effects[j].TargetA) - { - case TARGET_DEST_DB: - found = true; - break; - } - if (found) - break; - switch (spellInfo->Effects[j].TargetB) - { - case TARGET_DEST_DB: - found = true; - break; - } - if (found) - break; - } - if (found) - { - if (!sSpellMgr->GetSpellTargetPosition(i)) - TC_LOG_DEBUG("spells", "Spell (ID: %u) does not have record in `spell_target_position`", i); + SpellEffectInfo const* effect = spellInfo->GetEffect(j); + if (!effect) + continue; + + if (effect->TargetA.GetTarget() != TARGET_DEST_DB && effect->TargetB.GetTarget() != TARGET_DEST_DB) + continue; + + if (!GetSpellTargetPosition(i, SpellEffIndex(j))) + TC_LOG_DEBUG("spells", "Spell (Id: %u, EffectIndex: %u) does not have record in `spell_target_position`.", i, j); } - }*/ + } + */ TC_LOG_INFO("server.loading", ">> Loaded %u spell teleport coordinates in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } @@ -2891,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]; @@ -3004,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: @@ -3761,7 +3687,7 @@ void SpellMgr::LoadSpellInfoCorrections() case 45440: // Steam Tonk Controller case 60256: // Collect Sample // Crashes client on pressing ESC - spellInfo->AttributesEx4 &= ~SPELL_ATTR4_TRIGGERED; + spellInfo->AttributesEx4 &= ~SPELL_ATTR4_CAN_CAST_WHILE_CASTING; break; case 96942: // Gaze of Occu'thar case 101009: // Gaze of Occu'thar 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/game/Weather/Weather.cpp b/src/server/game/Weather/Weather.cpp index 9ad77761123..ecceb0b102f 100644 --- a/src/server/game/Weather/Weather.cpp +++ b/src/server/game/Weather/Weather.cpp @@ -30,6 +30,7 @@ #include "ScriptMgr.h" #include "Opcodes.h" #include "WorldSession.h" +#include "MiscPackets.h" /// Create the Weather object Weather::Weather(uint32 zone, WeatherData const* weatherChances) @@ -194,9 +195,8 @@ bool Weather::ReGenerate() void Weather::SendWeatherUpdateToPlayer(Player* player) { - WorldPacket data(SMSG_WEATHER, (4+4+4)); - data << uint32(GetWeatherState()) << (float)m_grade << uint8(0); - player->GetSession()->SendPacket(&data); + WorldPackets::Misc::Weather weather(GetWeatherState(), m_grade); + player->GetSession()->SendPacket(weather.Write()); } /// Send the new weather to all players in the zone @@ -210,13 +210,10 @@ bool Weather::UpdateWeather() WeatherState state = GetWeatherState(); - WorldPacket data(SMSG_WEATHER, (4+4+4)); - data << uint32(state); - data << (float)m_grade; - data << uint8(0); + WorldPackets::Misc::Weather weather(state, m_grade); //- Returns false if there were no players found to update - if (!sWorld->SendZoneMessage(m_zone, &data)) + if (!sWorld->SendZoneMessage(m_zone, weather.Write())) return false; ///- Log the event diff --git a/src/server/game/Weather/Weather.h b/src/server/game/Weather/Weather.h index 04d38b19c73..93a5ecd448f 100644 --- a/src/server/game/Weather/Weather.h +++ b/src/server/game/Weather/Weather.h @@ -43,7 +43,7 @@ struct WeatherData uint32 ScriptId; }; -enum WeatherState +enum WeatherState : uint32 { WEATHER_STATE_FINE = 0, WEATHER_STATE_FOG = 1, // Used in some instance encounters. diff --git a/src/server/game/Weather/WeatherMgr.cpp b/src/server/game/Weather/WeatherMgr.cpp index 8f599b8514b..883e622bf50 100644 --- a/src/server/game/Weather/WeatherMgr.cpp +++ b/src/server/game/Weather/WeatherMgr.cpp @@ -28,6 +28,7 @@ #include "WorldPacket.h" #include "Opcodes.h" #include "WorldSession.h" +#include "MiscPackets.h" namespace WeatherMgr { @@ -144,9 +145,8 @@ void LoadWeatherData() void SendFineWeatherUpdateToPlayer(Player* player) { - WorldPacket data(SMSG_WEATHER, (4+4+4)); - data << (uint32)WEATHER_STATE_FINE << (float)0.0f << uint8(0); - player->GetSession()->SendPacket(&data); + WorldPackets::Misc::Weather weather(WEATHER_STATE_FINE); + player->GetSession()->SendPacket(weather.Write()); } void Update(uint32 diff) 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/EasternKingdoms/zone_eastern_plaguelands.cpp b/src/server/scripts/EasternKingdoms/zone_eastern_plaguelands.cpp index 2f10aa858f9..7550faeaec1 100644 --- a/src/server/scripts/EasternKingdoms/zone_eastern_plaguelands.cpp +++ b/src/server/scripts/EasternKingdoms/zone_eastern_plaguelands.cpp @@ -177,7 +177,7 @@ public: if (creature->IsQuestGiver()) player->PrepareQuestMenu(creature->GetGUID()); - if (player->GetQuestStatus(5742) == QUEST_STATUS_INCOMPLETE && player->getStandState() == UNIT_STAND_STATE_SIT) + if (player->GetQuestStatus(5742) == QUEST_STATUS_INCOMPLETE && player->GetStandState() == UNIT_STAND_STATE_SIT) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); diff --git a/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_ossirian.cpp b/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_ossirian.cpp index 463d1ee7e0b..3e9852ae909 100644 --- a/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_ossirian.cpp +++ b/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_ossirian.cpp @@ -22,6 +22,7 @@ #include "SpellInfo.h" #include "WorldPacket.h" #include "Opcodes.h" +#include "Packets/MiscPackets.h" enum Texts { @@ -146,9 +147,8 @@ class boss_ossirian : public CreatureScript if (!map->IsDungeon()) return; - WorldPacket data(SMSG_WEATHER, (4+4+4)); - data << uint32(WEATHER_STATE_HEAVY_SANDSTORM) << float(1) << uint8(0); - map->SendToPlayers(&data); + WorldPackets::Misc::Weather weather(WEATHER_STATE_HEAVY_SANDSTORM, 1.0f); + map->SendToPlayers(weather.Write()); for (uint8 i = 0; i < NUM_TORNADOS; ++i) { diff --git a/src/server/scripts/Kalimdor/zone_the_barrens.cpp b/src/server/scripts/Kalimdor/zone_the_barrens.cpp index 4268cafa1ea..232261df951 100644 --- a/src/server/scripts/Kalimdor/zone_the_barrens.cpp +++ b/src/server/scripts/Kalimdor/zone_the_barrens.cpp @@ -547,7 +547,7 @@ public: { if (!HasEscortState(STATE_ESCORT_ESCORTING)) { - if (me->getStandState() == UNIT_STAND_STATE_DEAD) + if (me->GetStandState() == UNIT_STAND_STATE_DEAD) me->SetStandState(UNIT_STAND_STATE_STAND); IsPostEvent = false; diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp index d15b8a53a9a..6a76d4c688e 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp @@ -21,6 +21,7 @@ #include "SpellScript.h" #include "Transport.h" #include "Player.h" +#include "MoveSplineInit.h" #include "halls_of_reflection.h" enum Text @@ -342,6 +343,20 @@ class npc_jaina_or_sylvanas_intro_hor : public CreatureScript public: npc_jaina_or_sylvanas_intro_hor() : CreatureScript("npc_jaina_or_sylvanas_intro_hor") { } + bool OnGossipHello(Player* player, Creature* creature) override + { + // override default gossip + if (InstanceScript* instance = creature->GetInstanceScript()) + if (instance->GetData(DATA_QUEL_DELAR_EVENT) == IN_PROGRESS || instance->GetData(DATA_QUEL_DELAR_EVENT) == SPECIAL) + { + player->PlayerTalkClass->ClearMenus(); + return true; + } + + // load default gossip + return false; + } + struct npc_jaina_or_sylvanas_intro_horAI : public ScriptedAI { npc_jaina_or_sylvanas_intro_horAI(Creature* creature) : ScriptedAI(creature) @@ -1998,6 +2013,13 @@ class at_hor_intro_start : public AreaTriggerScript if (_instance->GetData(DATA_INTRO_EVENT) == NOT_STARTED) _instance->SetData(DATA_INTRO_EVENT, IN_PROGRESS); + if (player->HasAura(SPELL_QUEL_DELAR_COMPULSION) && (player->GetQuestStatus(QUEST_HALLS_OF_REFLECTION_ALLIANCE) == QUEST_STATUS_INCOMPLETE || + player->GetQuestStatus(QUEST_HALLS_OF_REFLECTION_HORDE) == QUEST_STATUS_INCOMPLETE) && _instance->GetData(DATA_QUEL_DELAR_EVENT) == NOT_STARTED) + { + _instance->SetData(DATA_QUEL_DELAR_EVENT, IN_PROGRESS); + _instance->SetGuidData(DATA_QUEL_DELAR_INVOKER, player->GetGUID()); + } + return true; } }; @@ -2330,6 +2352,395 @@ class npc_lumbering_abomination : public CreatureScript } }; +enum QuelDelarUther +{ + ACTION_UTHER_START_SCREAM = 1, + ACTION_UTHER_OUTRO = 2, + + EVENT_UTHER_1 = 1, + EVENT_UTHER_2 = 2, + EVENT_UTHER_3 = 3, + EVENT_UTHER_4 = 4, + EVENT_UTHER_5 = 5, + EVENT_UTHER_6 = 6, + EVENT_UTHER_7 = 7, + EVENT_UTHER_8 = 8, + EVENT_UTHER_9 = 9, + EVENT_UTHER_10 = 10, + EVENT_UTHER_11 = 11, + EVENT_UTHER_FACING = 12, + EVENT_UTHER_KNEEL = 13, + + SAY_UTHER_QUEL_DELAR_1 = 16, + SAY_UTHER_QUEL_DELAR_2 = 17, + SAY_UTHER_QUEL_DELAR_3 = 18, + SAY_UTHER_QUEL_DELAR_4 = 19, + SAY_UTHER_QUEL_DELAR_5 = 20, + SAY_UTHER_QUEL_DELAR_6 = 21, + + SPELL_ESSENCE_OF_CAPTURED_1 = 73036 +}; + +enum QuelDelarSword +{ + SPELL_WHIRLWIND_VISUAL = 70300, + SPELL_HEROIC_STRIKE = 29426, + SPELL_WHIRLWIND = 67716, + SPELL_BLADESTORM = 67541, + + NPC_QUEL_DELAR = 37158, + POINT_TAKE_OFF = 1, + + EVENT_QUEL_DELAR_INIT = 1, + EVENT_QUEL_DELAR_FLIGHT_INIT = 2, + EVENT_QUEL_DELAR_FLIGHT = 3, + EVENT_QUEL_DELAR_LAND = 4, + EVENT_QUEL_DELAR_FIGHT = 5, + EVENT_QUEL_DELAR_BLADESTORM = 6, + EVENT_QUEL_DELAR_HEROIC_STRIKE = 7, + EVENT_QUEL_DELAR_WHIRLWIND = 8, + + SAY_QUEL_DELAR_SWORD = 0 +}; + +enum QuelDelarMisc +{ + SAY_FROSTMOURNE_BUNNY = 0, + SPELL_QUEL_DELAR_WILL = 70698 +}; + +Position const QuelDelarCenterPos = { 5309.259f, 2006.390f, 718.046f, 0.0f }; +Position const QuelDelarSummonPos = { 5298.473f, 1994.852f, 709.424f, 3.979351f }; +Position const QuelDelarMovement[] = +{ + { 5292.870f, 1998.950f, 718.046f, 0.0f }, + { 5295.819f, 1991.912f, 707.707f, 0.0f }, + { 5295.301f, 1989.782f, 708.696f, 0.0f } +}; + +Position const UtherQuelDelarMovement[] = +{ + { 5336.830f, 1981.700f, 709.319f, 0.0f }, + { 5314.350f, 1993.440f, 707.726f, 0.0f } +}; + +class npc_uther_quel_delar : public CreatureScript +{ + public: + npc_uther_quel_delar() : CreatureScript("npc_uther_quel_delar") { } + + struct npc_uther_quel_delarAI : public ScriptedAI + { + npc_uther_quel_delarAI(Creature* creature) : ScriptedAI(creature) + { + _instance = me->GetInstanceScript(); + } + + void Reset() override + { + // Prevent to break Uther in intro event during instance encounter + if (_instance->GetData(DATA_QUEL_DELAR_EVENT) != IN_PROGRESS && _instance->GetData(DATA_QUEL_DELAR_EVENT) != SPECIAL) + return; + + _events.Reset(); + _events.ScheduleEvent(EVENT_UTHER_1, 1); + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage) override + { + if (damage >= me->GetHealth()) + damage = me->GetHealth() - 1; + } + + void DoAction(int32 action) override + { + switch (action) + { + case ACTION_UTHER_START_SCREAM: + _instance->SetData(DATA_QUEL_DELAR_EVENT, SPECIAL); + _events.ScheduleEvent(EVENT_UTHER_2, 0); + break; + case ACTION_UTHER_OUTRO: + _events.ScheduleEvent(EVENT_UTHER_6, 0); + break; + default: + break; + } + } + + void MovementInform(uint32 /*type*/, uint32 pointId) override + { + switch (pointId) + { + case 1: + _events.ScheduleEvent(EVENT_UTHER_FACING, 1000); + break; + default: + break; + } + } + + void UpdateAI(uint32 diff) override + { + // Prevent to break Uther in intro event during instance encounter + if (_instance->GetData(DATA_QUEL_DELAR_EVENT) != IN_PROGRESS && _instance->GetData(DATA_QUEL_DELAR_EVENT) != SPECIAL) + return; + + _events.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_UTHER_1: + Talk(SAY_UTHER_QUEL_DELAR_1); + break; + case EVENT_UTHER_2: + if (Creature* bunny = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_FROSTMOURNE_ALTAR_BUNNY))) + if (Unit* target = ObjectAccessor::GetPlayer(*me, _instance->GetGuidData(DATA_QUEL_DELAR_INVOKER))) + bunny->CastSpell(target, SPELL_QUEL_DELAR_WILL, true); + _events.ScheduleEvent(EVENT_UTHER_3, 2000); + break; + case EVENT_UTHER_3: + me->SummonCreature(NPC_QUEL_DELAR, QuelDelarSummonPos); + _events.ScheduleEvent(EVENT_UTHER_4, 2000); + break; + case EVENT_UTHER_4: + Talk(SAY_UTHER_QUEL_DELAR_2); + _events.ScheduleEvent(EVENT_UTHER_5, 8000); + break; + case EVENT_UTHER_5: + me->GetMotionMaster()->MovePoint(1, UtherQuelDelarMovement[0]); + break; + case EVENT_UTHER_6: + me->SetWalk(true); + me->GetMotionMaster()->MovePoint(0, UtherQuelDelarMovement[1]); + _events.ScheduleEvent(EVENT_UTHER_7, 5000); + break; + case EVENT_UTHER_7: + Talk(SAY_UTHER_QUEL_DELAR_3); + _events.ScheduleEvent(EVENT_UTHER_8, 12000); + break; + case EVENT_UTHER_8: + Talk(SAY_UTHER_QUEL_DELAR_4); + _events.ScheduleEvent(EVENT_UTHER_9, 7000); + break; + case EVENT_UTHER_9: + Talk(SAY_UTHER_QUEL_DELAR_5); + _events.ScheduleEvent(EVENT_UTHER_10, 10000); + break; + case EVENT_UTHER_10: + Talk(SAY_UTHER_QUEL_DELAR_6); + _events.ScheduleEvent(EVENT_UTHER_11, 5000); + break; + case EVENT_UTHER_11: + DoCast(me, SPELL_ESSENCE_OF_CAPTURED_1, true); + me->DespawnOrUnsummon(3000); + _instance->SetData(DATA_QUEL_DELAR_EVENT, DONE); + break; + case EVENT_UTHER_FACING: + if (Creature* bunny = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_FROSTMOURNE_ALTAR_BUNNY))) + me->SetFacingToObject(bunny); + _events.ScheduleEvent(EVENT_UTHER_KNEEL, 1000); + break; + case EVENT_UTHER_KNEEL: + me->HandleEmoteCommand(EMOTE_STATE_KNEEL); + break; + default: + break; + } + } + } + + private: + EventMap _events; + InstanceScript* _instance; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetHallsOfReflectionAI<npc_uther_quel_delarAI>(creature); + } +}; + +class npc_quel_delar_sword : public CreatureScript +{ + public: + npc_quel_delar_sword() : CreatureScript("npc_quel_delar_sword") { } + + struct npc_quel_delar_swordAI : public ScriptedAI + { + npc_quel_delar_swordAI(Creature* creature) : ScriptedAI(creature) + { + _instance = me->GetInstanceScript(); + me->SetDisplayId(me->GetCreatureTemplate()->Modelid2); + _intro = true; + } + + void Reset() override + { + _events.Reset(); + me->SetSpeed(MOVE_FLIGHT, 4.5f, true); + DoCast(SPELL_WHIRLWIND_VISUAL); + if (_intro) + _events.ScheduleEvent(EVENT_QUEL_DELAR_INIT, 0); + else + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + } + + void EnterCombat(Unit* /*victim*/) override + { + _events.ScheduleEvent(EVENT_QUEL_DELAR_HEROIC_STRIKE, 4000); + _events.ScheduleEvent(EVENT_QUEL_DELAR_BLADESTORM, 6000); + _events.ScheduleEvent(EVENT_QUEL_DELAR_WHIRLWIND, 6000); + } + + void JustDied(Unit* /*killer*/) override + { + if (Creature* uther = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_UTHER_QUEL_DELAR))) + uther->AI()->DoAction(ACTION_UTHER_OUTRO); + } + + void MovementInform(uint32 type, uint32 pointId) override + { + if (type != EFFECT_MOTION_TYPE) + return; + + switch (pointId) + { + case POINT_TAKE_OFF: + _events.ScheduleEvent(EVENT_QUEL_DELAR_FLIGHT, 0); + break; + default: + break; + } + } + + void UpdateAI(uint32 diff) override + { + _events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (!UpdateVictim()) + { + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_QUEL_DELAR_INIT: + if (Creature* bunny = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_FROSTMOURNE_ALTAR_BUNNY))) + bunny->AI()->Talk(SAY_FROSTMOURNE_BUNNY); + _intro = false; + _events.ScheduleEvent(EVENT_QUEL_DELAR_FLIGHT_INIT, 2500); + break; + case EVENT_QUEL_DELAR_FLIGHT_INIT: + me->GetMotionMaster()->MoveTakeoff(POINT_TAKE_OFF, QuelDelarMovement[0]); + break; + case EVENT_QUEL_DELAR_FLIGHT: + { + Movement::MoveSplineInit init(me); + FillCirclePath(QuelDelarCenterPos, 18.0f, 718.046f, init.Path(), true); + init.SetFly(); + init.SetCyclic(); + init.SetAnimation(Movement::ToFly); + init.Launch(); + + _events.ScheduleEvent(EVENT_QUEL_DELAR_LAND, 15000); + break; + } + case EVENT_QUEL_DELAR_LAND: + me->StopMoving(); + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MoveLand(0, QuelDelarMovement[1]); + _events.ScheduleEvent(EVENT_QUEL_DELAR_FIGHT, 6000); + break; + case EVENT_QUEL_DELAR_FIGHT: + Talk(SAY_QUEL_DELAR_SWORD); + me->GetMotionMaster()->MovePoint(0, QuelDelarMovement[2]); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + break; + default: + break; + } + } + } + else + { + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_QUEL_DELAR_BLADESTORM: + DoCast(me, SPELL_BLADESTORM); + _events.ScheduleEvent(EVENT_QUEL_DELAR_BLADESTORM, 10000); + break; + case EVENT_QUEL_DELAR_HEROIC_STRIKE: + DoCastVictim(SPELL_HEROIC_STRIKE); + _events.ScheduleEvent(EVENT_QUEL_DELAR_HEROIC_STRIKE, 6000); + break; + case EVENT_QUEL_DELAR_WHIRLWIND: + DoCastAOE(SPELL_WHIRLWIND); + _events.ScheduleEvent(EVENT_QUEL_DELAR_WHIRLWIND, 1000); + break; + default: + break; + } + } + + DoMeleeAttackIfReady(); + } + } + + private: + void FillCirclePath(Position const& centerPos, float radius, float z, Movement::PointsArray& path, bool clockwise) + { + float step = clockwise ? -M_PI / 8.0f : M_PI / 8.0f; + float angle = centerPos.GetAngle(me->GetPositionX(), me->GetPositionY()); + + for (uint8 i = 0; i < 16; angle += step, ++i) + { + G3D::Vector3 point; + point.x = centerPos.GetPositionX() + radius * cosf(angle); + point.y = centerPos.GetPositionY() + radius * sinf(angle); + point.z = z; + path.push_back(point); + } + } + + EventMap _events; + InstanceScript* _instance; + bool _intro; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetHallsOfReflectionAI<npc_quel_delar_swordAI>(creature); + } +}; + +// 5660 +class at_hor_uther_quel_delar_start : public AreaTriggerScript +{ + public: + at_hor_uther_quel_delar_start() : AreaTriggerScript("at_hor_uther_quel_delar_start") { } + + bool OnTrigger(Player* player, AreaTriggerEntry const* /*trigger*/) override + { + if (player->IsGameMaster()) + return true; + + InstanceScript* _instance = player->GetInstanceScript(); + + if (_instance->GetData(DATA_QUEL_DELAR_EVENT) == IN_PROGRESS) + if (Creature* uther = ObjectAccessor::GetCreature(*player, _instance->GetGuidData(DATA_UTHER_QUEL_DELAR))) + uther->AI()->DoAction(ACTION_UTHER_START_SCREAM); + + return true; + } +}; + // 72900 - Start Halls of Reflection Quest AE class spell_hor_start_halls_of_reflection_quest_ae : public SpellScriptLoader { @@ -2447,6 +2858,7 @@ void AddSC_halls_of_reflection() new at_hor_waves_restarter(); new at_hor_impenetrable_door(); new at_hor_shadow_throne(); + new at_hor_uther_quel_delar_start(); new npc_jaina_or_sylvanas_intro_hor(); new npc_jaina_or_sylvanas_escape_hor(); new npc_the_lich_king_escape_hor(); @@ -2461,6 +2873,8 @@ void AddSC_halls_of_reflection() new npc_raging_ghoul(); new npc_risen_witch_doctor(); new npc_lumbering_abomination(); + new npc_uther_quel_delar(); + new npc_quel_delar_sword(); new spell_hor_start_halls_of_reflection_quest_ae(); new spell_hor_evasion(); new spell_hor_gunship_cannon_fire(); diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.h b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.h index 9594617fa9a..d2f9ab5d262 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.h +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.h @@ -46,7 +46,13 @@ enum DataTypes DATA_ESCAPE_LEADER = 10, DATA_ICEWALL = 11, DATA_ICEWALL_TARGET = 12, - DATA_GUNSHIP = 13 + DATA_GUNSHIP = 13, + + // Quest stuff + DATA_QUEL_DELAR_EVENT = 14, + DATA_FROSTMOURNE_ALTAR_BUNNY = 15, + DATA_UTHER_QUEL_DELAR = 16, + DATA_QUEL_DELAR_INVOKER = 17 }; enum CreatureIds @@ -131,7 +137,8 @@ enum InstanceEvents EVENT_NEXT_WAVE = 2, EVENT_DO_WIPE = 3, EVENT_ADD_WAVE = 4, - EVENT_SPAWN_ESCAPE_EVENT = 5 + EVENT_SPAWN_ESCAPE_EVENT = 5, + EVENT_QUEL_DELAR_SUMMON_UTHER = 6 }; enum InstanceEventIds @@ -160,7 +167,17 @@ enum InstanceSpells // Gunship SPELL_GUNSHIP_CANNON_FIRE = 70017, SPELL_GUNSHIP_CANNON_FIRE_MISSILE_ALLIANCE = 70021, - SPELL_GUNSHIP_CANNON_FIRE_MISSILE_HORDE = 70246 + SPELL_GUNSHIP_CANNON_FIRE_MISSILE_HORDE = 70246, + + // Halls of Reflection quest + SPELL_QUEL_DELAR_COMPULSION = 70013, + SPELL_ESSENCE_OF_CAPTURED = 70720 +}; + +enum InstanceQuests +{ + QUEST_HALLS_OF_REFLECTION_ALLIANCE = 24480, + QUEST_HALLS_OF_REFLECTION_HORDE = 24561 }; enum InstanceWorldStates diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp index ae2023fd255..820821e4151 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp @@ -73,6 +73,8 @@ Position const SpawnPos[] = { 5299.250f, 2035.998f, 707.7781f, 5.026548f } }; +Position const UtherQuelDalarPos = { 5302.001f, 1988.698f, 707.7781f, 3.700098f }; + class instance_halls_of_reflection : public InstanceMapScript { public: @@ -89,6 +91,7 @@ class instance_halls_of_reflection : public InstanceMapScript _waveCount = 0; _introState = NOT_STARTED; _frostswornGeneralState = NOT_STARTED; + _quelDelarState = NOT_STARTED; events.Reset(); } @@ -159,6 +162,9 @@ class instance_halls_of_reflection : public InstanceMapScript case NPC_ICE_WALL_TARGET: IcewallTargetGUID = creature->GetGUID(); break; + case NPC_UTHER: + UtherGUID = creature->GetGUID(); + break; default: break; } @@ -439,6 +445,18 @@ class instance_halls_of_reflection : public InstanceMapScript HandleGameObject(ShadowThroneDoorGUID, true); _frostswornGeneralState = data; break; + case DATA_QUEL_DELAR_EVENT: + if (data == IN_PROGRESS) + { + if (_quelDelarState == NOT_STARTED) + { + if (Creature* bunny = instance->GetCreature(FrostmourneAltarBunnyGUID)) + bunny->CastSpell((Unit*)NULL, SPELL_ESSENCE_OF_CAPTURED); + events.ScheduleEvent(EVENT_QUEL_DELAR_SUMMON_UTHER, 2000); + } + } + _quelDelarState = data; + break; default: break; } @@ -446,6 +464,18 @@ class instance_halls_of_reflection : public InstanceMapScript SaveToDB(); } + void SetGuidData(uint32 type, ObjectGuid data) override + { + switch (type) + { + case DATA_QUEL_DELAR_INVOKER: + QuelDelarInvokerGUID = data; + break; + default: + break; + } + } + // wave scheduling, checked when wave npcs die void OnUnitDeath(Unit* unit) override { @@ -491,6 +521,9 @@ class instance_halls_of_reflection : public InstanceMapScript case EVENT_SPAWN_ESCAPE_EVENT: SpawnEscapeEvent(); break; + case EVENT_QUEL_DELAR_SUMMON_UTHER: + instance->SummonCreature(NPC_UTHER, UtherQuelDalarPos); + break; } } @@ -649,6 +682,8 @@ class instance_halls_of_reflection : public InstanceMapScript return _introState; case DATA_FROSTSWORN_GENERAL: return _frostswornGeneralState; + case DATA_QUEL_DELAR_EVENT: + return _quelDelarState; default: break; } @@ -682,6 +717,12 @@ class instance_halls_of_reflection : public InstanceMapScript return IcewallGUID; case DATA_ICEWALL_TARGET: return IcewallTargetGUID; + case DATA_FROSTMOURNE_ALTAR_BUNNY: + return FrostmourneAltarBunnyGUID; + case DATA_UTHER_QUEL_DELAR: + return UtherGUID; + case DATA_QUEL_DELAR_INVOKER: + return QuelDelarInvokerGUID; default: break; } @@ -691,7 +732,7 @@ class instance_halls_of_reflection : public InstanceMapScript void WriteSaveDataMore(std::ostringstream& data) override { - data << _introState << ' ' << _frostswornGeneralState; + data << _introState << ' ' << _frostswornGeneralState << ' ' << _quelDelarState; } void ReadSaveDataMore(std::istringstream& data) override @@ -708,6 +749,12 @@ class instance_halls_of_reflection : public InstanceMapScript SetData(DATA_FROSTSWORN_GENERAL, DONE); else SetData(DATA_FROSTSWORN_GENERAL, NOT_STARTED); + + data >> temp; + if (temp == DONE) + SetData(DATA_QUEL_DELAR_EVENT, DONE); + else + SetData(DATA_QUEL_DELAR_EVENT, NOT_STARTED); } private: @@ -731,6 +778,7 @@ class instance_halls_of_reflection : public InstanceMapScript uint32 _waveCount; uint32 _introState; uint32 _frostswornGeneralState; + uint32 _quelDelarState; EventMap events; GuidSet waveGuidList[8]; @@ -740,6 +788,8 @@ class instance_halls_of_reflection : public InstanceMapScript ObjectGuid CaptainGUID; ObjectGuid IcewallGUID; ObjectGuid IcewallTargetGUID; + ObjectGuid QuelDelarInvokerGUID; + ObjectGuid UtherGUID; GuidSet GunshipCannonGUIDs; GuidSet GunshipStairGUIDs; diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp index 3c068915585..5dcf4854943 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp @@ -353,9 +353,9 @@ static bool IsEncounterFinished(Unit* who) if (!mkii || !vx001 || !aerial) return false; - if (mkii->getStandState() == UNIT_STAND_STATE_DEAD && - vx001->getStandState() == UNIT_STAND_STATE_DEAD && - aerial->getStandState() == UNIT_STAND_STATE_DEAD) + if (mkii->GetStandState() == UNIT_STAND_STATE_DEAD && + vx001->GetStandState() == UNIT_STAND_STATE_DEAD && + aerial->GetStandState() == UNIT_STAND_STATE_DEAD) { who->Kill(mkii); who->Kill(vx001); diff --git a/src/server/scripts/Northrend/zone_zuldrak.cpp b/src/server/scripts/Northrend/zone_zuldrak.cpp index f73930181f0..91c796a6e69 100644 --- a/src/server/scripts/Northrend/zone_zuldrak.cpp +++ b/src/server/scripts/Northrend/zone_zuldrak.cpp @@ -313,7 +313,7 @@ public: { player->KilledMonsterCredit(gymerDummy->GetEntry(), gymerDummy->GetGUID()); gymerDummy->CastSpell(gymerDummy, SPELL_GYMER_LOCK_EXPLOSION, true); - gymerDummy->DespawnOrUnsummon(); + gymerDummy->DespawnOrUnsummon(4 * IN_MILLISECONDS); } } return true; diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp index 9aa1981eabb..38ccf33a612 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp @@ -535,7 +535,7 @@ class boss_kaelthas : public CreatureScript case 2: Advisor = (ObjectAccessor::GetCreature(*me, m_auiAdvisorGuid[0])); - if (Advisor && (Advisor->getStandState() == UNIT_STAND_STATE_DEAD)) + if (Advisor && (Advisor->GetStandState() == UNIT_STAND_STATE_DEAD)) { Talk(SAY_INTRO_SANGUINAR); @@ -569,7 +569,7 @@ class boss_kaelthas : public CreatureScript case 4: Advisor = (ObjectAccessor::GetCreature(*me, m_auiAdvisorGuid[1])); - if (Advisor && (Advisor->getStandState() == UNIT_STAND_STATE_DEAD)) + if (Advisor && (Advisor->GetStandState() == UNIT_STAND_STATE_DEAD)) { Talk(SAY_INTRO_CAPERNIAN); @@ -603,7 +603,7 @@ class boss_kaelthas : public CreatureScript case 6: Advisor = (ObjectAccessor::GetCreature(*me, m_auiAdvisorGuid[2])); - if (Advisor && (Advisor->getStandState() == UNIT_STAND_STATE_DEAD)) + if (Advisor && (Advisor->GetStandState() == UNIT_STAND_STATE_DEAD)) { Talk(SAY_INTRO_TELONICUS); @@ -638,7 +638,7 @@ class boss_kaelthas : public CreatureScript case 8: Advisor = (ObjectAccessor::GetCreature(*me, m_auiAdvisorGuid[3])); - if (Advisor && (Advisor->getStandState() == UNIT_STAND_STATE_DEAD)) + if (Advisor && (Advisor->GetStandState() == UNIT_STAND_STATE_DEAD)) { Phase = 2; instance->SetData(DATA_KAELTHASEVENT, 2); diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp index 00071c4804e..65b25564401 100644 --- a/src/server/scripts/Spells/spell_dk.cpp +++ b/src/server/scripts/Spells/spell_dk.cpp @@ -452,7 +452,7 @@ class spell_dk_death_coil : public SpellScriptLoader { SpellInfo const* DCD = sSpellMgr->EnsureSpellInfo(SPELL_DK_DEATH_COIL_DAMAGE); SpellEffectInfo const* eff = DCD->GetEffect(EFFECT_0); - int32 bp = caster->SpellDamageBonusDone(target, DCD, eff->CalcValue(caster), DIRECT_DAMAGE, eff); + int32 bp = caster->SpellDamageBonusDone(target, DCD, eff->CalcValue(caster), SPELL_DIRECT_DAMAGE, eff); caster->CastCustomSpell(target, SPELL_DK_DEATH_COIL_BARRIER, &bp, nullptr, nullptr, true); } @@ -658,11 +658,7 @@ class spell_dk_festering_strike : public SpellScriptLoader { PrepareSpellScript(spell_dk_festering_strike_SpellScript); - public: - spell_dk_festering_strike_SpellScript() { } - - private: - bool Validate(SpellInfo const* spellInfo) override + bool Validate(SpellInfo const* /*spellInfo*/) override { if (!sSpellMgr->GetSpellInfo(SPELL_DK_FROST_FEVER) || !sSpellMgr->GetSpellInfo(SPELL_DK_BLOOD_PLAGUE) || !sSpellMgr->GetSpellInfo(SPELL_DK_CHAINS_OF_ICE)) return false; @@ -671,7 +667,7 @@ class spell_dk_festering_strike : public SpellScriptLoader void HandleScriptEffect(SpellEffIndex /*effIndex*/) { - int32 extraDuration = GetSpellInfo()->GetEffect(EFFECT_2)->CalcValue(); + int32 extraDuration = GetEffectValue(); Unit* target = GetHitUnit(); ObjectGuid casterGUID = GetCaster()->GetGUID(); @@ -726,6 +722,11 @@ class spell_dk_ghoul_explode : public SpellScriptLoader return true; } + void HandleDamage(SpellEffIndex /*effIndex*/) + { + SetHitDamage(GetCaster()->CountPctFromMaxHealth(GetEffectInfo(EFFECT_2)->CalcValue(GetCaster()))); + } + void Suicide(SpellEffIndex /*effIndex*/) { if (Unit* unitTarget = GetHitUnit()) @@ -737,6 +738,7 @@ class spell_dk_ghoul_explode : public SpellScriptLoader void Register() override { + OnEffectHitTarget += SpellEffectFn(spell_dk_ghoul_explode_SpellScript::HandleDamage, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); OnEffectHitTarget += SpellEffectFn(spell_dk_ghoul_explode_SpellScript::Suicide, EFFECT_1, SPELL_EFFECT_SCHOOL_DAMAGE); } }; @@ -760,7 +762,8 @@ class spell_dk_glyph_of_deaths_embrace : public SpellScriptLoader bool CheckProc(ProcEventInfo& eventInfo) { - return (GetTarget()->GetCreatureType() == CREATURE_TYPE_UNDEAD && GetTarget()->GetOwner()); + Unit* actionTarget = eventInfo.GetActionTarget(); + return actionTarget && actionTarget->GetCreatureType() == CREATURE_TYPE_UNDEAD && actionTarget->GetOwner(); } void Register() override @@ -793,6 +796,11 @@ class spell_dk_glyph_of_runic_power : public SpellScriptLoader return true; } + bool Load() override + { + return GetUnitOwner()->getClass() == CLASS_DEATH_KNIGHT; + } + bool CheckProc(ProcEventInfo& eventInfo) { return eventInfo.GetSpellInfo() && (eventInfo.GetSpellInfo()->GetAllEffectsMechanicMask() & (1 << MECHANIC_SNARE | 1 << MECHANIC_ROOT | 1 << MECHANIC_FREEZE)); @@ -801,8 +809,7 @@ class spell_dk_glyph_of_runic_power : public SpellScriptLoader void HandleProc(ProcEventInfo& eventInfo) { if (Unit* target = eventInfo.GetProcTarget()) - if (target->getClass() == CLASS_DEATH_KNIGHT) - target->CastSpell(target, SPELL_DK_GLYPH_OF_RUNIC_POWER_TRIGGERED, true); + target->CastSpell(target, SPELL_DK_GLYPH_OF_RUNIC_POWER_TRIGGERED, true); } void Register() override @@ -945,7 +952,7 @@ class spell_dk_raise_dead : public SpellScriptLoader void HandleDummy(SpellEffIndex /*effIndex*/) { - GetCaster()->CastSpell(((Unit*)nullptr), GetSpellInfo()->GetEffect(EFFECT_0)->CalcValue(), true); + GetCaster()->CastSpell(((Unit*)nullptr), GetEffectValue(), true); } void Register() override @@ -1075,19 +1082,23 @@ class spell_dk_will_of_the_necropolis : public SpellScriptLoader { PrepareAuraScript(spell_dk_will_of_the_necropolis_AuraScript); - bool Validate(SpellInfo const* /*spellInfo*/) override + bool Validate(SpellInfo const* spellInfo) override { if (!sSpellMgr->GetSpellInfo(SPELL_DK_WILL_OF_THE_NECROPOLIS)) return false; + if (!spellInfo->GetEffect(EFFECT_0)) + return false; return true; } bool CheckProc(ProcEventInfo& eventInfo) { - if (GetTarget()->HasAura(SPELL_DK_WILL_OF_THE_NECROPOLIS)) + Unit* target = GetTarget(); + + if (target->HasAura(SPELL_DK_WILL_OF_THE_NECROPOLIS)) return false; - return GetTarget()->HealthBelowPctDamaged(30, eventInfo.GetDamageInfo()->GetDamage()); + return target->HealthBelowPctDamaged(GetSpellInfo()->GetEffect(EFFECT_0)->CalcValue(target), eventInfo.GetDamageInfo()->GetDamage()); } void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) 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) diff --git a/src/server/scripts/Spells/spell_holiday.cpp b/src/server/scripts/Spells/spell_holiday.cpp index 44dd17e5624..e6e97a9d04a 100644 --- a/src/server/scripts/Spells/spell_holiday.cpp +++ b/src/server/scripts/Spells/spell_holiday.cpp @@ -64,7 +64,7 @@ class spell_love_is_in_the_air_romantic_picnic : public SpellScriptLoader Unit* caster = GetCaster(); // If our player is no longer sit, remove all auras - if (target->getStandState() != UNIT_STAND_STATE_SIT) + if (target->GetStandState() != UNIT_STAND_STATE_SIT) { target->RemoveAura(SPELL_ROMANTIC_PICNIC_ACHIEV); target->RemoveAura(GetAura()); @@ -83,7 +83,7 @@ class spell_love_is_in_the_air_romantic_picnic : public SpellScriptLoader target->VisitNearbyWorldObject(INTERACTION_DISTANCE*2, searcher); for (std::list<Player*>::const_iterator itr = playerList.begin(); itr != playerList.end(); ++itr) { - if ((*itr) != target && (*itr)->HasAura(GetId())) // && (*itr)->getStandState() == UNIT_STAND_STATE_SIT) + if ((*itr) != target && (*itr)->HasAura(GetId())) // && (*itr)->GetStandState() == UNIT_STAND_STATE_SIT) { if (caster) { diff --git a/src/server/shared/Debugging/Errors.h b/src/server/shared/Debugging/Errors.h index df770c2aa47..4d4624b63dd 100644 --- a/src/server/shared/Debugging/Errors.h +++ b/src/server/shared/Debugging/Errors.h @@ -49,4 +49,10 @@ namespace Trinity #define ASSERT WPAssert +template <typename T> inline T* ASSERT_NOTNULL(T* pointer) +{ + ASSERT(pointer); + return pointer; +} + #endif |