From 98d4a57db10e358fdf2adf87696a332934747c5d Mon Sep 17 00:00:00 2001 From: Ovahlord <18347559+Ovahlord@users.noreply.github.com> Date: Fri, 22 Aug 2025 20:26:10 +0200 Subject: [PATCH] Core/Creatures: moved pre/post summon actions into SummonInfo API and disabled most old TemporarySummon functionalities --- .../game/Entities/Creature/Creature.cpp | 50 +---- src/server/game/Entities/Creature/Creature.h | 3 - .../Creature/SummonInfo/SummonInfo.cpp | 101 ++++++++- .../Entities/Creature/SummonInfo/SummonInfo.h | 9 + .../Entities/Creature/TemporarySummon.cpp | 31 +++ .../game/Entities/Creature/TemporarySummon.h | 9 - src/server/game/Entities/Object/Object.cpp | 74 +------ src/server/game/Entities/Player/Player.cpp | 63 +++++- src/server/game/Entities/Player/Player.h | 1 + src/server/game/Entities/Unit/StatSystem.cpp | 208 ------------------ src/server/game/Entities/Unit/Unit.cpp | 3 + src/server/game/Server/Packets/PetPackets.cpp | 31 +++ src/server/game/Server/Packets/PetPackets.h | 28 +++ 13 files changed, 272 insertions(+), 339 deletions(-) diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index dd5261cb54..64011dd6ed 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -336,9 +336,20 @@ void Creature::RemoveFromWorld() { // If the creature about to despawn, unregister it for the summoner if (SummonInfo* summonInfo = GetSummonInfo()) + { if (Unit* summoner = summonInfo->GetUnitSummoner()) + { summoner->UnregisterSummon(summonInfo); + if (summonInfo->GetControl() == SummonPropertiesControl::Pet) + { + summoner->SetPetGUID(ObjectGuid::Empty); + //if (Player* playerSummoner = summoner->ToPlayer()) + // playerSummoner->SendRemoveControlBar(); + } + } + } + if (GetZoneScript()) GetZoneScript()->OnCreatureRemove(this); @@ -2814,45 +2825,6 @@ bool Creature::IsSummon() const return _summonInfo != nullptr; } -void Creature::HandlePreSummonActions() -{ - SummonInfo* summonInfo = ASSERT_NOTNULL(GetSummonInfo()); - - if (Unit* summoner = summonInfo->GetUnitSummoner()) - { - SummonPropertiesSlot slot = summonInfo->GetSummonSlot(); - - // Controlled summons always set their creator guid, which is being used to display summoner names in their title - if (summonInfo->IsControlledBySummoner()) - SetCreatorGUID(summoner->GetGUID()); - - // Totem slot summons always send the TotemCreated packet. Some non-Shaman classes use this - // to display summon icons that can be canceled (Consecration, DK ghouls, Wild Mushrooms) - switch (slot) - { - case SummonPropertiesSlot::Totem1: - case SummonPropertiesSlot::Totem2: - case SummonPropertiesSlot::Totem3: - case SummonPropertiesSlot::Totem4: - { - if (Player* playerSummoner = Object::ToPlayer(summoner)) - { - WorldPackets::Totem::TotemCreated totemCreated; - totemCreated.Totem = GetGUID(); - totemCreated.SpellID = GetUInt32Value(UNIT_CREATED_BY_SPELL); - totemCreated.Duration = summonInfo->GetRemainingDuration().value_or(0ms).count(); - totemCreated.Slot = AsUnderlyingType(summonInfo->GetSummonSlot()) - 1; - - playerSummoner->SendDirectMessage(totemCreated.Write()); - } - break; - } - default: - break; - } - } -} - void Creature::AllLootRemovedFromCorpse() { if (loot.loot_type != LOOT_SKINNING && !IsPet() && GetCreatureTemplate()->SkinLootId && hasLootRecipient()) diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 5a69107e32..527bbcb3e1 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -430,9 +430,6 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma bool IsSummon() const override; - // Handles all summon based actions which happen before the creature is being added to the world - void HandlePreSummonActions(); - protected: bool CreateFromProto(ObjectGuid::LowType guidlow, uint32 entry, CreatureData const* data = nullptr, uint32 vehId = 0); bool InitEntry(uint32 entry, CreatureData const* data = nullptr); diff --git a/src/server/game/Entities/Creature/SummonInfo/SummonInfo.cpp b/src/server/game/Entities/Creature/SummonInfo/SummonInfo.cpp index d3305f0f33..c195992b07 100644 --- a/src/server/game/Entities/Creature/SummonInfo/SummonInfo.cpp +++ b/src/server/game/Entities/Creature/SummonInfo/SummonInfo.cpp @@ -17,12 +17,15 @@ #include "SummonInfo.h" +#include "CharmInfo.h" #include "Creature.h" -#include "DBCStores.h" #include "DBCEnums.h" +#include "DBCStores.h" #include "Log.h" #include "ObjectAccessor.h" +#include "Player.h" #include "SummonInfoArgs.h" +#include "TotemPackets.h" SummonInfo::SummonInfo(Creature* summonedCreature, SummonInfoArgs const& args) : _summonedCreature(ASSERT_NOTNULL(summonedCreature)), _summonerGUID(args.Summoner ? args.Summoner->GetGUID() : ObjectGuid::Empty), @@ -64,6 +67,13 @@ void SummonInfo::InitializeSummonProperties(uint32 summonPropertiesId, Unit cons // Controlled summons inherit their summoner's faction if not overridden by DBC data if (!_factionId.has_value()) _factionId = summoner->GetFaction(); + + // The summon is going to be treated as a pet. Prepare spell list. + if (_control == SummonPropertiesControl::Pet) + { + if (CharmInfo* charmInfo = _summonedCreature->InitCharmInfo()) + charmInfo->InitCharmCreateSpells(); + } } } } @@ -103,6 +113,86 @@ Optional SummonInfo::GetCreatureLevel() const return _creatureLevel; } +void SummonInfo::HandlePreSummonActions() +{ + if (Unit* summoner = GetUnitSummoner()) + { + SummonPropertiesSlot slot = GetSummonSlot(); + + // Controlled summons always set their creator guid, which is being used to display summoner names in their title + if (IsControlledBySummoner()) + _summonedCreature->SetCreatorGUID(summoner->GetGUID()); + + // Pets are set to Assist by default (this does not apply for class pets which save their states) + if (_control == SummonPropertiesControl::Pet) + _summonedCreature->SetReactState(REACT_ASSIST); + + // Totem slot summons always send the TotemCreated packet. Some non-Shaman classes use this + // to display summon icons that can be canceled (Consecration, DK ghouls, Wild Mushrooms) + switch (slot) + { + case SummonPropertiesSlot::Totem1: + case SummonPropertiesSlot::Totem2: + case SummonPropertiesSlot::Totem3: + case SummonPropertiesSlot::Totem4: + { + if (Player* playerSummoner = Object::ToPlayer(summoner)) + { + WorldPackets::Totem::TotemCreated totemCreated; + totemCreated.Totem = _summonedCreature->GetGUID(); + totemCreated.SpellID = _summonedCreature->GetUInt32Value(UNIT_CREATED_BY_SPELL); + totemCreated.Duration = _remainingDuration.value_or(0ms).count(); + totemCreated.Slot = AsUnderlyingType(_summonSlot) - 1; + + playerSummoner->SendDirectMessage(totemCreated.Write()); + } + break; + } + default: + break; + } + } +} + +void SummonInfo::HandlePostSummonActions() +{ + // Register Pet and enable its control + if (_control == SummonPropertiesControl::Pet) + { + if (Unit* summoner = GetUnitSummoner()) + { + _summonedCreature->SetOwnerGUID(summoner->GetGUID()); + + // Only set the Summon GUID when it's empty (prevent multiple applications on multi-summons like Feral Spirit) + if (summoner->GetMinionGUID() != _summonedCreature->GetGUID()) + { + summoner->SetMinionGUID(_summonedCreature->GetGUID()); + + if (Player* playerSummoner = summoner->ToPlayer()) + { + _summonedCreature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); + playerSummoner->SendPetSpells(this); + } + } + } + } +} + +void SummonInfo::HandlePreUnsummonActions() +{ + Unit* summoner = GetUnitSummoner(); + if (!summoner) + return; + + // Clear the current pet action bar and reset the Summon GUID + if (_control == SummonPropertiesControl::Pet && summoner->GetMinionGUID() == _summonedCreature->GetGUID()) + { + summoner->SetMinionGUID(ObjectGuid::Empty); + if (Player* player = summoner->ToPlayer()) + player->SendRemoveControlBar(); + } +} + void SummonInfo::UpdateRemainingDuration(Milliseconds deltaTime) { if (!_remainingDuration.has_value() || _remainingDuration <= 0ms) @@ -112,9 +202,9 @@ void SummonInfo::UpdateRemainingDuration(Milliseconds deltaTime) if (_remainingDuration <= 0ms) { if (DespawnsWhenExpired()) - GetSummonedCreature()->DespawnOrUnsummon(); + _summonedCreature->DespawnOrUnsummon(); else - GetSummonedCreature()->KillSelf(); + _summonedCreature->KillSelf(); } } @@ -179,3 +269,8 @@ SummonPropertiesSlot SummonInfo::GetSummonSlot() const { return _summonSlot; } + +SummonPropertiesControl SummonInfo::GetControl() const +{ + return _control; +} diff --git a/src/server/game/Entities/Creature/SummonInfo/SummonInfo.h b/src/server/game/Entities/Creature/SummonInfo/SummonInfo.h index bb3fe1e19b..c262fde577 100644 --- a/src/server/game/Entities/Creature/SummonInfo/SummonInfo.h +++ b/src/server/game/Entities/Creature/SummonInfo/SummonInfo.h @@ -59,6 +59,13 @@ public: // Returns the level of the creature that will override the default level calculation. Nullopt when the creature uses its default values. Optional GetCreatureLevel() const; + // Handles all summon actions which must be performed before the summoned creature is being added to the world + void HandlePreSummonActions(); + // Handles all summon actions which must be performed after the summoned creature has been added to the world + void HandlePostSummonActions(); + // Handles all summon actions which much be performend before the summoned creature is being removed from the world + void HandlePreUnsummonActions(); + // Updates the remaining duration of a summon and triggers the expiration void UpdateRemainingDuration(Milliseconds deltaTime); @@ -82,6 +89,8 @@ public: bool IsControlledBySummoner() const; // Returns the summon slot that the summon is going to be stored in SummonPropertiesSlot GetSummonSlot() const; + // Returns the summon control type which determines how the summon can be controlled by the summoner + SummonPropertiesControl GetControl() const; private: Creature* _summonedCreature; diff --git a/src/server/game/Entities/Creature/TemporarySummon.cpp b/src/server/game/Entities/Creature/TemporarySummon.cpp index e1f4563490..63181a98d3 100644 --- a/src/server/game/Entities/Creature/TemporarySummon.cpp +++ b/src/server/game/Entities/Creature/TemporarySummon.cpp @@ -50,6 +50,7 @@ void TempSummon::Update(uint32 diff) { Creature::Update(diff); + /* if (m_deathState == DEAD) { UnSummon(); @@ -147,10 +148,12 @@ void TempSummon::Update(uint32 diff) TC_LOG_ERROR("entities.unit", "Temporary summoned creature (entry: %u) have unknown type %u of ", GetEntry(), m_type); break; } + */ } void TempSummon::InitStats(uint32 duration) { + /* ASSERT(!IsPet()); m_timer = duration; @@ -186,10 +189,12 @@ void TempSummon::InitStats(uint32 duration) } } } + */ } void TempSummon::InitSummon() { + /* if (Unit* owner = GetSummoner()) { if (owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->IsAIEnabled()) @@ -197,6 +202,7 @@ void TempSummon::InitSummon() if (IsAIEnabled()) AI()->IsSummonedBy(owner); } + */ } void TempSummon::UpdateObjectVisibilityOnCreate() @@ -206,8 +212,10 @@ void TempSummon::UpdateObjectVisibilityOnCreate() void TempSummon::SetTempSummonType(TempSummonType type) { + /* m_type = type; GetSummonInfo()->SetDespawnWhenExpired(true); // To maintain legacy behavior in which all summons just despawn when expired + */ } void TempSummon::UnSummon(uint32 msTime) @@ -220,6 +228,7 @@ void TempSummon::UnSummon(uint32 msTime) return; } + /* //ASSERT(!IsPet()); if (IsPet()) { @@ -234,6 +243,7 @@ void TempSummon::UnSummon(uint32 msTime) if (owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->IsAIEnabled()) owner->ToCreature()->AI()->SummonedCreatureDespawn(this); } + */ AddObjectToRemoveList(); } @@ -264,6 +274,7 @@ Minion::Minion(SummonPropertiesEntry const* properties, Unit* owner, bool isWorl void Minion::InitStats(uint32 duration) { + /* TempSummon::InitStats(duration); SetReactState(REACT_PASSIVE); @@ -286,6 +297,7 @@ void Minion::InitStats(uint32 duration) SetUInt32Value(UNIT_NPC_FLAGS, GetCreatureTemplate()->npcflag); SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); } + */ } void Minion::RemoveFromWorld() @@ -293,6 +305,7 @@ void Minion::RemoveFromWorld() if (!IsInWorld()) return; + /* Unit* owner = GetOwner(); if ((IsMinion() || IsControlableGuardian()) && !IsTotem() && !IsVehicle()) @@ -309,6 +322,7 @@ void Minion::RemoveFromWorld() if (totemOwner->m_Controlled.find(this) != totemOwner->m_Controlled.end()) totemOwner->m_Controlled.erase(this); } + */ TempSummon::RemoveFromWorld(); } @@ -338,15 +352,19 @@ Guardian::Guardian(SummonPropertiesEntry const* properties, Unit* owner, bool is { memset(m_statFromOwner, 0, sizeof(float)*MAX_STATS); m_unitTypeMask |= UNIT_MASK_GUARDIAN; + + /* if (properties && SummonTitle(properties->Title) == SummonTitle::Pet) { m_unitTypeMask |= UNIT_MASK_CONTROLABLE_GUARDIAN; InitCharmInfo(); } + */ } void Guardian::InitStats(uint32 duration) { + /* Minion::InitStats(duration); InitStatsForLevel(getLevel()); @@ -355,18 +373,21 @@ void Guardian::InitStats(uint32 duration) m_charmInfo->InitCharmCreateSpells(); SetReactState(REACT_AGGRESSIVE); + */ } void Guardian::InitSummon() { TempSummon::InitSummon(); + /* if (GetOwner()->GetTypeId() == TYPEID_PLAYER && GetOwner()->GetMinionGUID() == GetGUID() && !GetOwner()->GetCharmedGUID()) { GetOwner()->ToPlayer()->CharmSpellInitialize(); } + */ } Puppet::Puppet(SummonPropertiesEntry const* properties, Unit* owner) @@ -379,19 +400,26 @@ Puppet::Puppet(SummonPropertiesEntry const* properties, Unit* owner) void Puppet::InitStats(uint32 duration) { Minion::InitStats(duration); + + /* SetReactState(REACT_PASSIVE); + */ } void Puppet::InitSummon() { + /* Minion::InitSummon(); if (!SetCharmedBy(GetOwner(), CHARM_TYPE_POSSESS)) ABORT(); + */ } void Puppet::Update(uint32 time) { Minion::Update(time); + + /* //check if caster is channelling? if (IsInWorld()) { @@ -401,6 +429,7 @@ void Puppet::Update(uint32 time) /// @todo why long distance .die does not remove it } } + */ } void Puppet::RemoveFromWorld() @@ -408,6 +437,8 @@ void Puppet::RemoveFromWorld() if (!IsInWorld()) return; + /* RemoveCharmedBy(nullptr); + */ Minion::RemoveFromWorld(); } diff --git a/src/server/game/Entities/Creature/TemporarySummon.h b/src/server/game/Entities/Creature/TemporarySummon.h index 39de736d89..e679b19782 100644 --- a/src/server/game/Entities/Creature/TemporarySummon.h +++ b/src/server/game/Entities/Creature/TemporarySummon.h @@ -119,15 +119,6 @@ class TC_GAME_API Guardian : public Minion bool InitStatsForLevel(uint8 level); void InitSummon() override; - bool UpdateStats(Stats stat) override; - bool UpdateAllStats() override; - void UpdateResistances(uint32 school) override; - void UpdateArmor() override; - void UpdateMaxHealth() override; - void UpdateMaxPower(Powers power) override; - void UpdateAttackPowerAndDamage(bool ranged = false) override; - void UpdateDamagePhysical(WeaponAttackType attType) override; - int32 GetBonusDamage() const { return m_bonusSpellDamage; } float GetBonusStatFromOwner(Stats stat) const { return m_statFromOwner[stat]; } void SetBonusDamage(int32 damage); diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index a2e39089d2..af02e159a0 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -2008,74 +2008,7 @@ void WorldObject::AddObjectToRemoveList() TempSummon* Map::SummonCreature(uint32 entry, Position const& pos, SummonCreatureExtraArgs const& summonArgs /*= { }*/) { - uint32 mask = UNIT_MASK_SUMMON; - - if (summonArgs.SummonProperties) - { - switch (summonArgs.SummonProperties->Control) - { - case SUMMON_CATEGORY_PET: - mask = UNIT_MASK_GUARDIAN; - break; - case SUMMON_CATEGORY_PUPPET: - mask = UNIT_MASK_PUPPET; - break; - case SUMMON_CATEGORY_VEHICLE: - mask = UNIT_MASK_MINION; - break; - case SUMMON_CATEGORY_WILD: - case SUMMON_CATEGORY_ALLY: - case SUMMON_CATEGORY_UNK: - { - switch (SummonTitle(summonArgs.SummonProperties->Title)) - { - case SummonTitle::Minion: - case SummonTitle::Guardian: - case SummonTitle::Runeblade: - mask = UNIT_MASK_GUARDIAN; - break; - case SummonTitle::Totem: - case SummonTitle::Lightwell: - mask = UNIT_MASK_TOTEM; - break; - case SummonTitle::Vehicle: - case SummonTitle::Mount: - mask = UNIT_MASK_SUMMON; - break; - case SummonTitle::Companion: - mask = UNIT_MASK_MINION; - break; - default: - if (summonArgs.SummonProperties->Flags & 512) // Mirror Image, Summon Gargoyle - mask = UNIT_MASK_GUARDIAN; - break; - } - break; - } - default: - return nullptr; - } - } - - TempSummon* summon = nullptr; - switch (mask) - { - case UNIT_MASK_SUMMON: - summon = new TempSummon(summonArgs.SummonProperties, summonArgs.Summoner, false); - break; - case UNIT_MASK_GUARDIAN: - summon = new Guardian(summonArgs.SummonProperties, summonArgs.Summoner, false); - break; - case UNIT_MASK_PUPPET: - summon = new Puppet(summonArgs.SummonProperties, summonArgs.Summoner); - break; - case UNIT_MASK_TOTEM: - summon = new Totem(summonArgs.SummonProperties, summonArgs.Summoner); - break; - case UNIT_MASK_MINION: - summon = new Minion(summonArgs.SummonProperties, summonArgs.Summoner, false); - break; - } + TempSummon* summon = new TempSummon(summonArgs.SummonProperties, summonArgs.Summoner, false); // Store special settings which have been extracted from spell effects/scripts SummonInfoArgs args; @@ -2118,8 +2051,10 @@ TempSummon* Map::SummonCreature(uint32 entry, Position const& pos, SummonCreatur transport->AddPassenger(summon); } + SummonInfo* summonInfo = ASSERT_NOTNULL(summon->GetSummonInfo()); + summon->SetHomePosition(pos); - summon->HandlePreSummonActions(); + summonInfo->HandlePreSummonActions(); summon->InitStats(summonArgs.SummonDuration); summon->SetPrivateObjectOwner(summonArgs.PrivateObjectOwner); @@ -2134,6 +2069,7 @@ TempSummon* Map::SummonCreature(uint32 entry, Position const& pos, SummonCreatur } summon->InitSummon(); + summonInfo->HandlePostSummonActions(); // call MoveInLineOfSight for nearby creatures Trinity::AIRelocationNotifier notifier(*summon); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 1dd10d141c..dcf5df0bcf 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -16,6 +16,11 @@ */ #include "Player.h" + +#include + +#include + #include "AccountMgr.h" #include "AchievementMgr.h" #include "Archaeology.h" @@ -36,13 +41,13 @@ #include "CharmInfo.h" #include "Chat.h" #include "CinematicMgr.h" -#include "CombatPackets.h" #include "CombatLogPackets.h" +#include "CombatPackets.h" #include "Common.h" #include "ConditionMgr.h" #include "CreatureAI.h" -#include "DatabaseEnv.h" #include "DB2Stores.h" +#include "DatabaseEnv.h" #include "DisableMgr.h" #include "Formulas.h" #include "GameClient.h" @@ -57,13 +62,13 @@ #include "GroupMgr.h" #include "Guild.h" #include "GuildMgr.h" +#include "InstancePackets.h" #include "InstanceSaveMgr.h" #include "InstanceScript.h" -#include "InstancePackets.h" #include "ItemPackets.h" #include "KillRewarder.h" -#include "Language.h" #include "LFGMgr.h" +#include "Language.h" #include "Log.h" #include "LootItemStorage.h" #include "LootMgr.h" @@ -72,9 +77,9 @@ #include "MapManager.h" #include "MiscPackets.h" #include "MotionMaster.h" -#include "MovementStructures.h" -#include "MovementPackets.h" #include "MovementPacketSender.h" +#include "MovementPackets.h" +#include "MovementStructures.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "Opcodes.h" @@ -102,6 +107,7 @@ #include "SpellMgr.h" #include "SpellPackets.h" #include "StringConvert.h" +#include "SummonInfo.h" #include "TerrainMgr.h" #include "TicketMgr.h" #include "TradeData.h" @@ -119,8 +125,6 @@ #include "WorldSession.h" #include "WorldStateMgr.h" #include "WorldStatePackets.h" -#include -#include // corpse reclaim times static constexpr uint32 DEATH_EXPIRE_STEP = 5 * MINUTE; @@ -20756,6 +20760,49 @@ void Player::VehicleSpellInitialize() SendDirectMessage(&data); } +void Player::SendPetSpells(SummonInfo* summonInfo) +{ + if (!summonInfo) + return; + + Creature* summon = summonInfo->GetSummonedCreature(); + CharmInfo* charmInfo = summon->GetCharmInfo(); + if (!charmInfo) + return; + + uint8 maxCharmInfoSpells = 0; + for (uint32 i = 0; i < MAX_SPELL_CHARM; ++i) + if (charmInfo->GetCharmSpell(i)->GetAction()) + ++maxCharmInfoSpells; + + WorldPacket data(SMSG_PET_SPELLS, 8 + 2 + 4 + 4 + 4 * MAX_UNIT_ACTION_BAR_INDEX + 1 + 4 * maxCharmInfoSpells + 1); + data << summon->GetGUID(); + data << uint16(summon->GetCreatureTemplate()->family); + data << uint32(summonInfo->GetRemainingDuration().value_or(0ms).count()); + data << uint8(summon->GetReactState()); + data << uint8(charmInfo->GetCommandState()); + data << uint16(0); + + // action bar loop + charmInfo->BuildActionBar(&data); + + data << uint8(maxCharmInfoSpells); + + if (maxCharmInfoSpells) + { + for (uint32 i = 0; i < MAX_SPELL_CHARM; ++i) + { + CharmSpellInfo* cspell = charmInfo->GetCharmSpell(i); + if (cspell->GetAction()) + data << uint32(cspell->packedData); + } + } + + data << uint8(0); // cooldowns count + + SendDirectMessage(&data); +} + void Player::CharmSpellInitialize() { Unit* charm = GetFirstControlled(); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 1c09fca661..ec51d3f9f3 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1532,6 +1532,7 @@ class TC_GAME_API Player : public Unit, public GridObject void CharmSpellInitialize(); void PossessSpellInitialize(); void VehicleSpellInitialize(); + void SendPetSpells(SummonInfo* summonInfo); void SendRemoveControlBar() const; bool HasSpell(uint32 spell) const override; bool HasActiveSpell(uint32 spell) const; // show in spellbook diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp index a982ba99b4..43193985d6 100644 --- a/src/server/game/Entities/Unit/StatSystem.cpp +++ b/src/server/game/Entities/Unit/StatSystem.cpp @@ -1096,214 +1096,6 @@ void Creature::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, ######## ######## #######################################*/ -bool Guardian::UpdateStats(Stats stat) -{ - if (stat >= MAX_STATS) - return false; - - // value = ((base_value * base_pct) + total_value) * total_pct - float value = GetTotalStatValue(stat); - //ApplyStatBuffMod(stat, m_statFromOwner[stat], false); - float ownersBonus = 0.0f; - - SetStat(stat, int32(value)); - m_statFromOwner[stat] = ownersBonus; - UpdateStatBuffMod(stat); - - switch (stat) - { - case STAT_STRENGTH: - UpdateAttackPowerAndDamage(); - break; - case STAT_AGILITY: - UpdateArmor(); - break; - case STAT_STAMINA: - UpdateMaxHealth(); - break; - case STAT_INTELLECT: - UpdateMaxPower(POWER_MANA); - break; - case STAT_SPIRIT: - break; - default: - break; - } - - return true; -} - -bool Guardian::UpdateAllStats() -{ - UpdateMaxHealth(); - - for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i) - UpdateStats(Stats(i)); - - for (uint8 i = POWER_MANA; i < MAX_POWERS; ++i) - UpdateMaxPower(Powers(i)); - - UpdateAllResistances(); - - return true; -} - -void Guardian::UpdateResistances(uint32 school) -{ - if (school > SPELL_SCHOOL_NORMAL) - { - float value = GetTotalAuraModValue(UnitMods(UNIT_MOD_RESISTANCE_START + school)); - SetResistance(SpellSchools(school), int32(value)); - } - else - UpdateArmor(); -} - -void Guardian::UpdateArmor() -{ - float value = 0.0f; - UnitMods unitMod = UNIT_MOD_ARMOR; - - value = GetFlatModifierValue(unitMod, BASE_VALUE); - value *= GetPctModifierValue(unitMod, BASE_PCT); - //value += GetStat(STAT_AGILITY) * 2.0f; (deprecated since 4.x) - value += GetFlatModifierValue(unitMod, TOTAL_VALUE); - value *= GetPctModifierValue(unitMod, TOTAL_PCT); - - - SetArmor(int32(value)); -} - -void Guardian::UpdateMaxHealth() -{ - UnitMods unitMod = UNIT_MOD_HEALTH; - float stamina = GetStat(STAT_STAMINA) - GetCreateStat(STAT_STAMINA); - float multiplicator = 10.0f; - - float value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetCreateHealth(); - value *= GetPctModifierValue(unitMod, BASE_PCT); - value += GetFlatModifierValue(unitMod, TOTAL_VALUE) + stamina * multiplicator; - value *= GetPctModifierValue(unitMod, TOTAL_PCT); - - SetMaxHealth((uint32)value); -} - -void Guardian::UpdateMaxPower(Powers power) -{ - if (GetPowerIndex(power) == MAX_POWERS) - return; - - UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + AsUnderlyingType(power)); - - float addValue = (power == POWER_MANA) ? GetStat(STAT_INTELLECT) - GetCreateStat(STAT_INTELLECT) : 0.0f; - float multiplicator = 15.0f; - - float value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetCreatePowerValue(power); - value *= GetPctModifierValue(unitMod, BASE_PCT); - value += GetFlatModifierValue(unitMod, TOTAL_VALUE) + addValue * multiplicator; - value *= GetPctModifierValue(unitMod, TOTAL_PCT); - - SetMaxPower(power, int32(value)); -} - -void Guardian::UpdateAttackPowerAndDamage(bool ranged) -{ - if (ranged) - return; - - float ap_per_strength = 2.0f; - float val = GetStat(STAT_STRENGTH) - 20.0f; - - val *= ap_per_strength; - - UnitMods unitMod = UNIT_MOD_ATTACK_POWER; - - SetStatFlatModifier(UNIT_MOD_ATTACK_POWER, BASE_VALUE, val); - - // in BASE_VALUE of UNIT_MOD_ATTACK_POWER for creatures we store data of meleeattackpower field in DB - float base_attPower = GetFlatModifierValue(unitMod, BASE_VALUE) * GetPctModifierValue(unitMod, BASE_PCT); - float attPowerMod = GetFlatModifierValue(unitMod, TOTAL_VALUE); - float attPowerMultiplier = GetPctModifierValue(unitMod, TOTAL_PCT) - 1.0f; - - // UNIT_FIELD_(RANGED)_ATTACK_POWER field - SetInt32Value(UNIT_FIELD_ATTACK_POWER, int32(base_attPower)); - // UNIT_FIELD_(RANGED)_ATTACK_POWER_MOD_POS field - SetInt32Value(UNIT_FIELD_ATTACK_POWER_MOD_POS, int32(attPowerMod > 0.f ? attPowerMod : 0.f)); - // UNIT_FIELD_(RANGED)_ATTACK_POWER_MOD_NEG field - SetInt32Value(UNIT_FIELD_ATTACK_POWER_MOD_NEG, int32(attPowerMod < 0.f ? -attPowerMod : 0.f)); - // UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field - SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER, attPowerMultiplier); - - // automatically update weapon damage after attack power modification - UpdateDamagePhysical(BASE_ATTACK); - - // update pet spell power - int32 spellDamage = 0; - for (auto itr : GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_DONE)) - spellDamage += itr->GetAmount(); - - SetBonusDamage(spellDamage); -} - -void Guardian::UpdateDamagePhysical(WeaponAttackType attType) -{ - if (attType > BASE_ATTACK) - return; - - float bonusDamage = 0.0f; - if (m_owner->GetTypeId() == TYPEID_PLAYER) - { - //force of nature - if (GetEntry() == ENTRY_TREANT) - { - int32 spellDmg = int32(m_owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + AsUnderlyingType(SPELL_SCHOOL_NATURE))) + m_owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG + AsUnderlyingType(SPELL_SCHOOL_NATURE)); - if (spellDmg > 0) - bonusDamage = spellDmg * 0.09f; - } - //greater fire elemental - else if (GetEntry() == ENTRY_FIRE_ELEMENTAL) - { - int32 spellDmg = int32(m_owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + AsUnderlyingType(SPELL_SCHOOL_FIRE))) + m_owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG + AsUnderlyingType(SPELL_SCHOOL_FIRE)); - if (spellDmg > 0) - bonusDamage = spellDmg * 0.4f; - } - } - - UnitMods unitMod = UNIT_MOD_DAMAGE_MAINHAND; - - float att_speed = float(GetBaseAttackTime(BASE_ATTACK))/1000.0f; - - float base_value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType) / 14.0f * att_speed + bonusDamage; - float base_pct = GetPctModifierValue(unitMod, BASE_PCT); - float total_value = GetFlatModifierValue(unitMod, TOTAL_VALUE); - float total_pct = GetPctModifierValue(unitMod, TOTAL_PCT); - - float weapon_mindamage = GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE); - float weapon_maxdamage = GetWeaponDamageRange(BASE_ATTACK, MAXDAMAGE); - - float mindamage = ((base_value + weapon_mindamage) * base_pct + total_value) * total_pct; - float maxdamage = ((base_value + weapon_maxdamage) * base_pct + total_value) * total_pct; - - /// @todo: remove this - Unit::AuraEffectList const& mDummy = GetAuraEffectsByType(SPELL_AURA_MOD_ATTACKSPEED); - for (Unit::AuraEffectList::const_iterator itr = mDummy.begin(); itr != mDummy.end(); ++itr) - { - switch ((*itr)->GetSpellInfo()->Id) - { - case 61682: - case 61683: - AddPct(mindamage, -(*itr)->GetAmount()); - AddPct(maxdamage, -(*itr)->GetAmount()); - break; - default: - break; - } - } - - SetStatFloatValue(UNIT_FIELD_MINDAMAGE, mindamage); - SetStatFloatValue(UNIT_FIELD_MAXDAMAGE, maxdamage); -} - void Guardian::SetBonusDamage(int32 damage) { //m_bonusSpellDamage = damage; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 161cefd452..61f4457058 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -8863,6 +8863,9 @@ void Unit::UnregisterSummon(SummonInfo* summon) if (!summon) return; + // Perform cleanup actions if needed + summon->HandlePreUnsummonActions(); + SummonPropertiesSlot slot = summon->GetSummonSlot(); if (slot == SummonPropertiesSlot::None) { diff --git a/src/server/game/Server/Packets/PetPackets.cpp b/src/server/game/Server/Packets/PetPackets.cpp index 3469659fcb..689f692f2c 100644 --- a/src/server/game/Server/Packets/PetPackets.cpp +++ b/src/server/game/Server/Packets/PetPackets.cpp @@ -16,7 +16,9 @@ */ #include "PetPackets.h" + #include "PetDefines.h" +#include "Spell.h" WorldPacket const* WorldPackets::Pet::PetDismissSound::Write() { @@ -80,3 +82,32 @@ WorldPacket const* WorldPackets::Pet::PetAdded::Write() return &_worldPacket; } + +WorldPacket const* WorldPackets::Pet::PetSpells::Write() +{ + _worldPacket << PetGUID; + if (PetGUID.IsEmpty()) + return &_worldPacket; + + _worldPacket << uint16(_CreatureFamily); + _worldPacket << uint32(TimeLimit); + _worldPacket << uint8(ReactState); + _worldPacket << uint8(CommandState); + _worldPacket << uint16(Flag); + + _worldPacket.append(ActionButtons.data(), ActionButtons.size()); + + _worldPacket << uint8(Actions.size()); + _worldPacket.append(Actions.data(), Actions.size()); + + _worldPacket << uint8(Cooldowns.size()); + for (PetSpellCooldown const& cooldown : Cooldowns) + { + _worldPacket << int32(cooldown.SpellID); + _worldPacket << uint16(cooldown.Category); + _worldPacket << int32(cooldown.Duration); + _worldPacket << int32(cooldown.CategoryDuration); + } + + return &_worldPacket; +} diff --git a/src/server/game/Server/Packets/PetPackets.h b/src/server/game/Server/Packets/PetPackets.h index bd9f9989ef..171854113d 100644 --- a/src/server/game/Server/Packets/PetPackets.h +++ b/src/server/game/Server/Packets/PetPackets.h @@ -94,6 +94,34 @@ namespace WorldPackets uint32 PetNumber = 0; uint8 Flags = 0; }; + + struct PetSpellCooldown + { + int32 SpellID = 0; + int32 Duration = 0; + int32 CategoryDuration = 0; + uint16 Category = 0; + }; + + class PetSpells final : public ServerPacket + { + public: + PetSpells() : ServerPacket(SMSG_PET_SPELLS, 100) { } + + WorldPacket const* Write() override; + + ObjectGuid PetGUID; + uint16 _CreatureFamily = 0; ///< @see enum CreatureFamily + uint32 TimeLimit = 0; + uint8 ReactState = 0; + uint8 CommandState = 0; + uint16 Flag = 0; + + std::array ActionButtons = { }; + + std::vector Actions; + std::vector Cooldowns; + }; } }